Let's investigate 3 ways to generate graphical video data.
The classical (and easy) way to display graphics on an LCD is to hold rasterized bitmap data into a RAM.
We are going to use a blockram here (see here to learn about blockrams).
We display a small 128x32 pixels bitmap here (fits nicey into a 4Kbit blockram):
// Use a blockram to hold the graphical data wire [7:0] BitmapData; blockram_8x512 RAM_bitmap(.clk(clk), .rd_adr({CounterY[4:0],CounterX[4:1]}), .data_out(BitmapData)); // Let's say we need 4 bits at a time wire [3:0] LCD_Bitmap4 = CounterX[0] ? BitmapData[3:0] : BitmapData[7:4]; // Display the data into a chessboard pattern wire [3:0] LCD_BitmapChessboard = (CounterY[5] ^ CounterX[5]) ? 4'b000 : LCD_Bitmap4 ^ {4{CounterY[5]}};
Not shown above is how the RAM is written. The easiest is to treat it as a ROM (the RAM content is part of the FPGA configuration, and doesn't change while the FPGA is running).
Here's a macro shot:
The drawback of rasterized bitmaps is that you require a large enough RAM to hold the state of each pixel of the bitmap. That's expensive using internal FPGA RAM, so external RAM is often used instead.
Now, let's explore more original ways to create graphics.
// We assume CounterX and CounterY are available: // CounterX is the pixel number of the current line // CounterY is the line number // We use a RAM to hold the "Y" values // Y=F(CounterX) wire [7:0] RAM_Y_value; blockram_8x512 RAM_FXY(.clk(clk), .rd_adr(CounterX), .data_out(RAM_Y_value)); // check for equality between the "Y" values and "CounterY" reg grcpeq1; always @(posedge clk) grcpeq1 <= (RAM_Y_value==CounterY); reg grcpeq2; always @(posedge clk) grcpeq2 <= grcpeq1; // check for "greater-than" between the "Y" values and "CounterY" reg grcp1; always @(posedge clk) grcp1 <= (RAM_Y_value>CounterY); reg grcp2; always @(posedge clk) grcp2 <= grcp1; // display a pixel if equality, or if "CounterY" is between 2 successive "Y" values wire FXpix= grcpeq2 | (grcp1 ^ grcp2);
Here's the result using F(x)=cos(x*2*pi/480)*sin(x*2*pi/480*4):
reg [15:0] X0, Y0, X1, Y1; always @(posedge clk) if(Vsync) begin X0 <= 0; Y0 <= 0; X1 <= 0; Y1 <= 0; end else if(Hsync) begin X0 <= X1 - 100; Y0 <= Y1 + 400; X1 <= X1 - 100; Y1 <= Y1 + 400; end else begin X0 <= X0 + 400; Y0 <= Y0 + 100; end // Display a chessboard pattern by XOR'ing the MSB of X and Y counters // You could also display a rotozoomed bitmap by feeding X and Y to a bitmap in a RAM wire rotozoom_pix = X0[15] ^ Y0[15];
This is simplified because the increment values are fixed (400 and 100 above). You'd want to vary them in a real implementation. By varying the coefficients, you rotate and zoom the chessboard.
Here's the result, mixed with the previous waveform:
You can then mix that with some text...