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