Reputation: 21
I want to create a ROM (just like a look up table) for storing ASCII characters in a way that when I extract the character from the ROM, I can display it on a video screen.
Character is supposed to take 8 pixels column wise and 16 pixels row wise (16*8) and each pixel gets a 10 bit value- 10'b 1111111111 for maximum luminance and 10'b 0000000000 for minimum.
Since, I cannot create a 2D array in verilog how am I supposed to create a 1D array and implement this?
Upvotes: 2
Views: 10443
Reputation: 4381
Two dimensional arrays can not be at port input/output in Verilog. But they can be declared and used within inside module.
Here, the memory is 80 bit wide and with a depth of 16. So, the address width must be log2(16) = 4
, 4 bits wide.
You can implement something as follows (broad view pseudo-code). Here, mem[0]
is least address block, and each block has 0th bit as LSB with 79th bit as MSB:
module memory(address,read_en,clk,reset,out);
// clk,reset declarations
// address to be accessed in memory
input reg [3:0] address;
// read/write signal
input read_en;
//output signal, for data read from memory
output [79:0] out;
// internal memory, accessed through address only
reg [79:0] mem [16];
// inside always block
// clock synchronous block
always @(posedge clk, negedge reset)
begin
// reset logic, followed by:
if(read_en)
begin
out <= mem[address];
end
end
endmodule
Without using internal memory, it would be a bit difficult to get through addresses. Memory is to be declared as packed array this time, so its 1280(16*80) bits wide.Following is method for packed array:
// rest all declarations are same.
reg [1279:0] mem;
out<=mem[(((address+1)*80)-1) -: 80];
The -:
operator is used for bit slicing. As follows, refer to Verilog slicing link for further information.
x -: Y, the start position is x and count down from x by Y. Y is necessarily a constant.
There is no difference (or perhaps, a little difference) between using packed and unpacked arrays. So, prefer using mixed block array.
For loading data into ROM from a file, refer to this link. Though $readmemh
shall make it non-synthesizable.
For more information, on accessing memory using refer to this link. This site shows synthesizable module.
For two dimensional arrays, refer to this question.
Upvotes: 0
Reputation: 11428
You can create such memory using a reg, like this:
module charmem (
input wire clk,
input wire [7:0] charaddr,
input wire [3:0] scanaddr,
input wire [2:0] pixeladdr,
output reg [9:0] pixel
);
reg [9:0] chars[0:32767]; // 256 chars, 16 scans, 8 pixels
initial begin
$readmemh ("chardef.hex", chars, 0); // this IS synthesizable on Xilinx
end
always @(posedge clk) begin
scan <= chars[{charaddr,scanaddr,pixeladdr}];
end
endmodule
chardef.hex
would be a text file with a 10 bit hex number per line. The first 8 hex numbers would be the pixels of the first scan of the first character. Next, the following 8 pixels for the second scan of the first character, and so until the 8 pixels of the 16-th scan of the first character. Then, the 8 pixels of the first scan of the second character, and so on.
Note that even you cannot use n-dimensional matrices in Verilog (with n>=3), you can take advantage of your dimensions being powers of two (256x16x8), so a 1D vector can be used to implement it, by just concatenating the indexes to form a unique memory address (well, as yo usee, it's actually a 2D vector if you see it as a bit element matrix instead of a 10-bit element vector).
Note too that, despite you have asked for a non block-RAM solution, you will want to use that, as a distributed memory solution for a reg like this will surely eat many of your precious logic resources and will take ages to synthesize, and I don't see a reason why this ROM cannot be implemented in block-RAM.
Let assume that your video controller will have two counters: x
, y
for instance. Assuming your active region is 640x480, and the character ASCII code you want to draw is stored in character
(a 8 bit reg), you may do as this:
wire [9:0] pixel;
charmem chartable (
.clk(clk),
.charaddr(character),
.scanaddr(y[3:0]),
.pixeladdr(x[2:0]),
.pixel(pixel)
);
To make charmem to output a black pixel when the video controller is not updating the active region, you can add a videoenable
signal to it, so if it is '1', the pixel is the one retrieved from the ROM, otherwise is a black.
always @(posedge clk) begin
if (videoenable == 1'b1)
scan <= chars[{charaddr,scanaddr,pixeladdr}];
else
scan <= 10'h000;
end
videoenable
would be updated as this:
reg videoenable;
always @* begin
if (x >= 10'd0 && x <= 10'd639 &&
y >= 10'd0 && y <= 10'd479)
videoenable = 1'b1;
else
videoenable = 1'b0;
end
Upvotes: 1