VGA controller
Standard VGA has a resolution of 640*480 pixels. The horizontal synchronization (hs) initiates drawing a new line (row) and the vertical synchronization (vs) initiates drawing a new frame. The horizontal and vertical synchronization signals are defined as below. Note that there are areas in which the pixels are not displayed (black part in the figure).
The frequency of VGA is 50 - 60 Hz. That is, in one second, 50 - 60 frames must be refreshed. Because the number of total pixels is (96 $+$ 48 $+$ 640 $+$ 16) $\times$ (2 $+$ 33 $+$ 480 $+$ 10), or 800 $\times$ 525, or 420000, the clock frequency of the VGA controller must be in range of 420000 $\times$ 50 $=$ 21,000,000 Hz and 420000 $\times$ 60 $=$ 25,200,000 Hz. Then we use 50 MHz clock to generate a 25 MHz clock. Thus, the refresh rate is 25000000 / 420000 $=$ 58.5238, i.e., the frequency is 58.5238 Hz, in range of 50 and 60 Hz. The following Verilog HDL code implements the VGA controller.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
// VGA signal generator, 640 x 480, by Li Yamin, yamin@ieee.org module vgac (vga_clk,clrn,d_in,row_addr,col_addr,rdn,r,g,b,hs,vs); // vgac input [11:0] d_in; // rrrr_gggg_bbbb, pixel input vga_clk; // 25MHz input clrn; output reg [8:0] row_addr; // pixel ram row address, 480 (512) lines output reg [9:0] col_addr; // pixel ram col address, 640 (1024) pixels output reg [3:0] r,g,b; // red, green, blue colors, 4-bit for each output reg rdn; // read pixel RAM (active low) output reg hs,vs; // horizontal and vertical synchronization // h_count: vga horizontal counter (0-799 pixels) reg [9:0] h_count; always @ (posedge vga_clk or negedge clrn) begin if (!clrn) begin h_count <= 10'h0; end else if (h_count == 10'd799) begin h_count <= 10'h0; end else begin h_count <= h_count + 10'h1; end end // v_count: vga vertical counter (0-524 lines) reg [9:0] v_count; always @ (posedge vga_clk or negedge clrn) begin if (!clrn) begin v_count <= 10'h0; end else if (h_count == 10'd799) begin if (v_count == 10'd524) begin v_count <= 10'h0; end else begin v_count <= v_count + 10'h1; end end end // signals, will be latched for outputs wire [9:0] row = v_count - 10'd35; // ( 2+33= 35) pixel ram row address wire [9:0] col = h_count - 10'd143; // (96+48=144) pixel ram col address wire h_sync = (h_count > 10'd95); // 96 -> 799 wire v_sync = (v_count > 10'd1); // 2 -> 524 wire read = (h_count > 10'd142) && // 143 -> 782 = (h_count < 10'd783) && // 640 pixels (v_count > 10'd34) && // 35 -> 514 = (v_count < 10'd515); // 480 lines // vga signals always @ (posedge vga_clk) begin // posedge orginal row_addr <= row[8:0]; // pixel ram row address col_addr <= col; // pixel ram col address rdn <= ~read; // read pixel (active low) hs <= h_sync; // horizontal synch vs <= v_sync; // vertical synch r <= rdn ? 4'h0 : d_in[11:08]; // 4-bit red g <= rdn ? 4'h0 : d_in[07:04]; // 4-bit green b <= rdn ? 4'h0 : d_in[03:00]; // 4-bit blue end endmodule |
hs starts:
hs ends:
First pixel:
One frame ends and next frame starts:
VGA graphics mode and text mode
Graphics mode: Video RAM stores bit-map image. For true-color VGA, it needs a 640 $\times$ 480 $\times$ 24 Video RAM (12 bits for color in the figure below).
Text mode: Character RAM stores ASCII. For a 8 $\times$ 8 character font, it needs a 80 $\times$ 60 $\times$ 7 Character RAM.
Addresses in text mode:
Font table
A font table defines the shapes of the ASCII characters. Below shows an 8*8 font table written in C.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
/* * 8*8 ASCII fonts (font8.c) */ const unsigned char Font[][8] = { {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}, // <SPACE> 20 {0x18,0x18,0x18,0x18,0x00,0x18,0x18,0x00}, // ! 21 {0x6c,0x6c,0x48,0x00,0x00,0x00,0x00,0x00}, // " 22 {0x6c,0x6c,0xfe,0x6c,0xfe,0x6c,0x6c,0x00}, // # 23 {0x18,0x7e,0xd8,0x7e,0x1b,0x7e,0x18,0x00}, // $ 24 {0x62,0x66,0x0c,0x18,0x30,0x66,0x46,0x00}, // % 25 {0x38,0x6c,0x68,0x76,0xdc,0xcc,0x76,0x00}, // & 26 {0x18,0x18,0x30,0x00,0x00,0x00,0x00,0x00}, // ' 27 {0x0c,0x18,0x30,0x30,0x30,0x18,0x0c,0x00}, // ( 28 {0x30,0x18,0x0c,0x0c,0x0c,0x18,0x30,0x00}, // ) 29 {0x00,0x6c,0x38,0xfe,0x38,0x6c,0x00,0x00}, // * 2a {0x00,0x18,0x18,0x7e,0x18,0x18,0x00,0x00}, // + 2b {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x10}, // , 2c {0x00,0x00,0x00,0x7e,0x00,0x00,0x00,0x00}, // - 2d {0x00,0x00,0x00,0x00,0x00,0x18,0x18,0x00}, // . 2e {0x02,0x06,0x0c,0x18,0x30,0x60,0x40,0x00}, // / 2f {0x3c,0x66,0x6e,0x76,0x66,0x66,0x3c,0x00}, // 0 30 {0x18,0x18,0x38,0x18,0x18,0x18,0x3c,0x00}, // 1 31 {0x7c,0x06,0x06,0x3c,0x60,0x60,0x7c,0x00}, // 2 32 {0x7c,0x06,0x06,0x3c,0x06,0x06,0x7c,0x00}, // 3 33 {0x66,0x66,0x66,0x7e,0x06,0x06,0x06,0x00}, // 4 34 {0x7e,0x60,0x60,0x7c,0x06,0x06,0x7c,0x00}, // 5 35 {0x3c,0x60,0x60,0x7c,0x66,0x66,0x3c,0x00}, // 6 36 {0x7e,0x06,0x0c,0x18,0x18,0x18,0x18,0x00}, // 7 37 {0x3c,0x66,0x66,0x3c,0x66,0x66,0x3c,0x00}, // 8 38 {0x3c,0x66,0x66,0x3e,0x06,0x06,0x3c,0x00}, // 9 39 {0x00,0x18,0x18,0x00,0x18,0x18,0x00,0x00}, // : 3a {0x00,0x00,0x18,0x18,0x00,0x18,0x18,0x10}, // ; 3b {0x0c,0x18,0x30,0x60,0x30,0x18,0x0c,0x00}, // < 3c {0x00,0x00,0x7e,0x00,0x7e,0x00,0x00,0x00}, // = 3d {0x30,0x18,0x0c,0x06,0x0c,0x18,0x30,0x00}, // > 3e {0x3c,0x66,0x06,0x1c,0x18,0x00,0x18,0x00}, // ? 3f {0x3c,0x66,0x6e,0x6a,0x6e,0x60,0x3e,0x00}, // @ 40 {0x3c,0x66,0x66,0x7e,0x66,0x66,0x66,0x00}, // A 41 {0x7c,0x66,0x66,0x7c,0x66,0x66,0x7c,0x00}, // B 42 {0x3c,0x66,0x60,0x60,0x60,0x66,0x3c,0x00}, // C 43 {0x7c,0x66,0x66,0x66,0x66,0x66,0x7c,0x00}, // D 44 {0x7e,0x60,0x60,0x7c,0x60,0x60,0x7e,0x00}, // E 45 {0x7e,0x60,0x60,0x7c,0x60,0x60,0x60,0x00}, // F 46 {0x3c,0x66,0x60,0x6e,0x66,0x66,0x3c,0x00}, // G 47 {0x66,0x66,0x66,0x7e,0x66,0x66,0x66,0x00}, // H 48 {0x3c,0x18,0x18,0x18,0x18,0x18,0x3c,0x00}, // I 49 {0x3e,0x0c,0x0c,0x0c,0x0c,0x6c,0x38,0x00}, // J 4a {0x66,0x6c,0x78,0x70,0x78,0x6c,0x66,0x00}, // K 4b {0x60,0x60,0x60,0x60,0x60,0x60,0x7e,0x00}, // L 4c {0xc6,0xee,0xfe,0xd6,0xc6,0xc6,0xc6,0x00}, // M 4d {0x66,0x66,0x76,0x7e,0x6e,0x66,0x66,0x00}, // N 4e {0x3c,0x66,0x66,0x66,0x66,0x66,0x3c,0x00}, // O 4f {0x7c,0x66,0x66,0x7c,0x60,0x60,0x60,0x00}, // P 50 {0x3c,0x66,0x66,0x66,0x6e,0x66,0x3e,0x00}, // Q 51 {0x7c,0x66,0x66,0x7c,0x66,0x66,0x66,0x00}, // R 52 {0x3e,0x60,0x60,0x3c,0x06,0x06,0x7c,0x00}, // S 53 {0x7e,0x18,0x18,0x18,0x18,0x18,0x18,0x00}, // T 54 {0x66,0x66,0x66,0x66,0x66,0x66,0x3c,0x00}, // U 55 {0x66,0x66,0x66,0x66,0x3c,0x3c,0x18,0x00}, // V 56 {0xc6,0xc6,0xd6,0xd6,0xfe,0xee,0x44,0x00}, // W 57 {0x66,0x66,0x3c,0x18,0x3c,0x66,0x66,0x00}, // X 58 {0x66,0x66,0x66,0x3c,0x18,0x18,0x18,0x00}, // Y 59 {0x7e,0x06,0x0c,0x18,0x30,0x60,0x7e,0x00}, // Z 5a {0x3c,0x30,0x30,0x30,0x30,0x30,0x3c,0x00}, // [ 5b {0x40,0x60,0x30,0x18,0x0c,0x06,0x02,0x00}, // \ 5c {0x3c,0x0c,0x0c,0x0c,0x0c,0x0c,0x3c,0x00}, // ] 5d {0x10,0x38,0x6c,0x00,0x00,0x00,0x00,0x00}, // ^ 5e {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff}, // _ 5f {0x18,0x18,0x0c,0x00,0x00,0x00,0x00,0x00}, // ` 60 {0x00,0x00,0x3c,0x06,0x3e,0x66,0x3a,0x00}, // a 61 {0x60,0x60,0x7c,0x66,0x66,0x66,0x7c,0x00}, // b 62 {0x00,0x00,0x3c,0x66,0x60,0x66,0x3c,0x00}, // c 63 {0x06,0x06,0x3e,0x66,0x66,0x66,0x3e,0x00}, // d 64 {0x00,0x00,0x3c,0x66,0x7c,0x60,0x3c,0x00}, // e 65 {0x0e,0x18,0x18,0x3e,0x18,0x18,0x18,0x00}, // f 66 {0x00,0x00,0x3e,0x66,0x66,0x3e,0x06,0x3c}, // g 67 {0x60,0x60,0x7c,0x66,0x66,0x66,0x66,0x00}, // h 68 {0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x00}, // i 69 {0x18,0x00,0x18,0x18,0x18,0x18,0x18,0x70}, // j 6a {0x60,0x60,0x66,0x6c,0x78,0x6c,0x66,0x00}, // k 6b {0x30,0x30,0x30,0x30,0x30,0x30,0x1c,0x00}, // l 6c {0x00,0x00,0xcc,0xfe,0xd6,0xc6,0xc6,0x00}, // m 6d {0x00,0x00,0x7c,0x66,0x66,0x66,0x66,0x00}, // n 6e {0x00,0x00,0x3c,0x66,0x66,0x66,0x3c,0x00}, // o 6f {0x00,0x00,0x7c,0x66,0x66,0x7c,0x60,0x60}, // p 70 {0x00,0x00,0x3e,0x66,0x66,0x3e,0x06,0x06}, // q 71 {0x00,0x00,0x36,0x38,0x30,0x30,0x30,0x00}, // r 72 {0x00,0x00,0x3e,0x60,0x3c,0x06,0x7c,0x00}, // s 73 {0x18,0x18,0x3c,0x18,0x18,0x18,0x0c,0x00}, // t 74 {0x00,0x00,0x66,0x66,0x66,0x66,0x3c,0x00}, // u 75 {0x00,0x00,0x66,0x66,0x66,0x3c,0x18,0x00}, // v 76 {0x00,0x00,0xc6,0xd6,0xd6,0x7c,0x28,0x00}, // w 77 {0x00,0x00,0x66,0x3c,0x18,0x3c,0x66,0x00}, // x 78 {0x00,0x00,0x66,0x66,0x66,0x3e,0x06,0x7c}, // y 79 {0x00,0x00,0x7e,0x0c,0x18,0x30,0x7e,0x00}, // z 7a {0x1c,0x30,0x30,0x60,0x30,0x30,0x1c,0x00}, // { 7b {0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00}, // | 7c {0x38,0x0c,0x0c,0x06,0x0c,0x0c,0x38,0x00}, // } 7d {0x00,0x32,0x4c,0x00,0x00,0x00,0x00,0x00}, // ~ 7e {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff} // <DEL> 7f }; |
The shapes of the fonts are shown below by font_show.c.
Compile and execute on Cygwin:
$ gcc font_show.c font8.c -o font_show $ ./font_show |
OO OO OO OO OO OO OO O OOO OO OO OO O OO OO OO OO OO OOOOOO OO OO OO OO OO OO OO OO OO OO OO OO O O OOOOOOO OO OO OO OO O OO OO OO OOO OO OO OO OO OO OOOOOO OO OOO OO OO OO OOOOOOO OOOOOO OOOOOO OO OOOOOOO OO OO OO OO OOO OO OO OOO OO OO OO OO OO OOOOOO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO O OO OOO OO OO OO OO OO O O OOOO OO OOOOO OOOOO OO OO OOOOOO OOOO OOOOOO OOOO OOOO OO OO OOOO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OOO OOO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OOOOOO OO OO OOO OO OO OOOO OOOO OOOOOO OOOOO OOOOO OO OOOO OOOOO OO OO OO OOO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OOOOOO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OOOO OOOO OOOOO OOOOO OO OOOOO OOOO OO OOOO OOOO OO OO OO OO O OOOO OOOO OOOOO OOOO OOOOO OOOOOO OOOOOO OOOO OO OO OOOO OOOOO OO OO OO OO OO OO OO OOOO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OOO OOO OO OO OO OO OO OOO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OOOO OO OOOOOOO OOO OO OO OO OO O O OOOOOO OOOOO OO OO OO OOOOO OOOOO OO OOO OOOOOO OO OO OOO OO OO O OO OOOOOO OO OO OO OOO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OOOO OO OO OO OO OOO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OOOOO OO OO OOOOO OOOO OOOOO OOOOOO OO OOOO OO OO OOOO OOO OO OO OOOOOO OO OO OO OO OOOO OOOOO OOOO OOOOO OOOOO OOOOOO OO OO OO OO OO OO OO OO OO OO OOOOOO OOOO O OOOO O OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OOO OO OO OO OO OO OO OO OO OO OO OO OO OO O OO OOOO OO OO OO OO OO OO OO OO OOOOO OO OO OOOOO OOOO OO OO OO OO OO OO O OO OO OOOO OO OO OO OO OO OO OOO OO OO OO OO OO OO OOOO OOOOOOO OOOO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OOOO OOO OOO OO OO OO OO OO OO OO OO OOOOO OO OO OOOOO OO OOOO OO O O OO OO OO OOOOOO OOOO O OOOO OOOOOOOO OO OO OO OOO OO OO OO OO OO OO OO OO OO OO OO OO OO OOOO OOOOO OOOO OOOOO OOOO OO OOOOO OOOOO OO OO OO OO OO OO OO OOOOO OOOO OO OO OO OO OO OO OO OO OO OOOOO OO OO OO OO OO OO OO OO OO OOOOOOO OO OO OO OO OOOOO OO OO OO OO OO OOOOO OO OO OO OO OO OO OO OOOO OO OO O OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OOOOO OO OO OO OO OO OO OO OO OO OO OO OO OO OOO O OOOOO OOOO OOOOO OOOO OO OO OO OO OO OO OO OO OOO OO OO OO OO OOOO OOOO OOO OO OOO OO OOO OOOOOOOO OO OO OO OO OO O OOOOOOOO OOOOO OOOOO OO OO OOOOO OOOO OO OO OO OO OO OO OO OO OO OO OOOOOO OO OO OO O OO OOOOOOOO OO OO OO OO OOO OO OO OO OO OO OO OO O OO OOOO OO OO OO OO OO OO OOOOOOOO OO OO OO OO OO OOOO OO OO OO OO OO OO O OO OO OO OO OO OO OO OO OOOOOOOO OOOOO OOOOO OO OO OO OO OO OOOO OOOOO OOOO OOOOO OO OO OO OO OOOOOOOO OO OO OO OOOOO OO OOOO OO O O OO OO OO OOOOOO OOO OO OOO OOOOOOOO OO OO OOOOO OOOOOOOO |
The following C program generates the Verilog HDL file for font table module.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// gen_font_table_v.c #include <stdio.h> // gen_font_table_v.c, generate font table (Verilog HDL) extern unsigned char Font[][8]; int main() { unsigned char char_row_bitmap; int addr = 0; int char_no, i, j; printf ("module font_table (a,d);\n"); printf (" input [12:0] a; // 8*8*128=2^3*2^3*2^7\n"); printf (" output d; // font dot\n"); printf (" wire rom [0:8191];\n"); printf (" assign d = rom[a];\n\n"); for (char_no = 0; char_no < 128; char_no++) { for (i = 0; i < 8; i++) { // 8 rows per char if (char_no >= 0x20) { // |
Compile and execute on Cygwin:
$ gcc gen_font_table_v.c font8.c -o gen_font_table_v $ ./gen_font_table_v > font_table.v
Character RAM
Character RAM stores ASCII codes of characters which will be displayed on VGA. Character RAM is implemented with synchronous memory, see the Verilog HDL below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
// Single port synchronous RAM, by Li Yamin, yamin@ieee.org module char_ram (clk,addr,dout,din,we); // single-port ram input clk,we; input [12:0] addr; input [6:0] din; output [6:0] dout; reg [12:0] addr_reg; reg [6:0] char_ram [0:4799]; // 80 * 60 = 4800 always @(posedge clk) begin // posedge read and write if (we) begin char_ram[addr] <= din; // write char ram end addr_reg <= addr; // register read address end assign dout = char_ram[addr_reg]; // read char ram initial begin char_ram[13'd2354] = 7'h48; // H char_ram[13'd2355] = 7'h65; // e char_ram[13'd2356] = 7'h6c; // l char_ram[13'd2357] = 7'h6c; // l char_ram[13'd2358] = 7'h6f; // o char_ram[13'd2359] = 7'h2c; // , char_ram[13'd2360] = 7'h20; // char_ram[13'd2361] = 7'h46; // F char_ram[13'd2362] = 7'h50; // P char_ram[13'd2363] = 7'h47; // G char_ram[13'd2364] = 7'h41; // A char_ram[13'd2365] = 7'h21; // ! end endmodule |
Display "Hello, FPGA!"
The following Verilog HDL code shows "Hello, FPGA!" on the center of display.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
// Use font table to display "Hello, FPGA!", by Li Yamin // // Single-port char ram +-posedge stores VGA address for read // | // _____ _____ |_____ _____ _____ _____ // sys_clk |_____| |_____| |_____| |_____| |_____| |_____| | // (50mhz) +-negedge | | VGAC | | | | // | generates | | reads | | | | // | clk25m | | char ram | | | | // |___________| |___________| |___________| | // clk25m | |___________| |___________| |___________| // +-posedge +-negedge | // | VGAC | registers | // | outputs | ascii | // clk25m is used as the selection | address | for VGA | // signal of multiplexer for ram |------ one cycle ------| module font_table_hello_fpga (sys_clk,clrn,r,g,b,hs,vs); input sys_clk,clrn; output [3:0] r,g,b; // red, green, blue colors, 4-bit for each output hs,vs; // horizontal and vertical synchronization // output of char ram wire [6:0] ascii; // output of char ram reg [6:0] ascii_reg; // registered ascii // signals for vgac and font table wire [8:0] row_addr; // vga pixel row address, 480 (512) lines wire [9:0] col_addr; // vga pixel col address, 640 (1024) pixels wire rdn; // read pixel ram (active low), not used wire ft_dot; // font dot wire [11:0] vgac_din = (ft_dot) ? 12'hfff : 12'h00f; // rrrr_gggg_bbbb, pixel wire [5:0] char_row = row_addr[8:3]; // char row wire [6:0] char_col = col_addr[9:3]; // char col wire [2:0] font_row = row_addr[2:0]; // font row wire [2:0] font_col = col_addr[2:0]; // font col wire [12:0] ft_addr = {ascii_reg,font_row,font_col}; // font_table address wire [12:0] vga_addr = {char_row,6'h0} + {char_row,4'h0} + char_col; // row*(64+16)+col // clock 25mhz reg clk25m = 1; // clk25m: 25MHz always @(negedge sys_clk) begin clk25m <= ~clk25m; // clk25m: 25MHz end // register ascii for access to font table always @(negedge clk25m) begin ascii_reg <= ascii; // registered ascii end // vgac vgac vc (clk25m,clrn,vgac_din,row_addr,col_addr,rdn,r,g,b,hs,vs); // vgac // char ram, 80 * 60 = 4800, posedge writes data and stores address for read wire [12:0] ram_addr = vga_addr; // ram read only wire ram_we = 1'b0; // do not write ram wire [6:0] ram_din; // do not write ram char_ram cr (sys_clk,ram_addr,ascii,ram_din,ram_we); // single-port ram // font_table 128 x 8 x 8 x 1 font_table ft (ft_addr,ft_dot); endmodule |
Exercise
- Draw a circuit block diagram for font_table_hello_fpga.v.
- Write a test bench font_table_hello_fpga_tb.v to simulate font_table_hello_fpga.v with ModelSim.
OO OO OO OO OOOOOO OOOOO OOOO OOOO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OOOO OO OO OOOO OO OO OO OO OO OO OO OOOOOO OO OO OO OO OO OO OOOOO OOOOO OO OOO OOOOOO OO OO OO OOOOO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OO OOOO OOO OOO OOOO OO OO OO OOOO OO OO OO O
(1) First row (font_row 0) of characters 'H' (0x48) and 'e' (0x65), in the center of screen (char_row = 29, char_col = 34, 35):
(2) Last row (font_row 7) of characters 'A' (0x41) and '!' (0x21), in the center of screen (char_row = 29, char_col = 44, 45):
You can use add wave -r /* to show the waveforms of internal signals. Revise char_ram.v to remove 'X' of the waveforms.
- Referring to DE0-CV FPGA Board User Manual, implement font_table_hello_fpga.v on DE0-CV FPGA board.
Pin assignments:
set_location_assignment PIN_M9 -to sys_clk set_location_assignment PIN_P22 -to clrn set_location_assignment PIN_B6 -to b[0] set_location_assignment PIN_B7 -to b[1] set_location_assignment PIN_A8 -to b[2] set_location_assignment PIN_A7 -to b[3] set_location_assignment PIN_L7 -to g[0] set_location_assignment PIN_K7 -to g[1] set_location_assignment PIN_J7 -to g[2] set_location_assignment PIN_J8 -to g[3] set_location_assignment PIN_A9 -to r[0] set_location_assignment PIN_B10 -to r[1] set_location_assignment PIN_C9 -to r[2] set_location_assignment PIN_A5 -to r[3] set_location_assignment PIN_H8 -to hs set_location_assignment PIN_G8 -to vs
- Display your Kanji (16 $\times$ 16 per Kanji) name on VGA.
0 char_row 0 1 char_col O O O 0 font_row O O O 1 O O OOOOOOOO O 2 OOOOOOOOO O OOOOOO 3 O O O O O 4 O O O O O 5 O O O O OO O 6 OOOOOOOOOOO O OOO O O 7 O O O O O O 8 O O O O O O 9 O O O O O O 10 O O O O O OO O 11 O O OOO O OO O O 12 O OOOOOO O OO O O 13 O O OO OO O 14 O O OO O 15 111111 111111 01234567890123450123456789012345 font_col
- Display an image on display (Graphics mode). You may use a python program to extract RGB from an image file.
For example, to display a photo (width 75, height 100 pixels), we ignore the least significant two bits of VGA row and column addresses, as shown as below.
Below is the VRAM module generated by the python program.
module vram_w75h100 (a,d); input [12:0] a; // 100 * 75 = 7500 pixels -> 8k -> 13-bit address output [11:0] d; // rrrr_gggg_bbbb wire [11:0] rom [0:7499]; assign d = rom[a]; assign rom[13'd0] = 12'hba7; assign rom[13'd1] = 12'hba7; assign rom[13'd2] = 12'hba7; assign rom[13'd3] = 12'hba7; assign rom[13'd4] = 12'hba7; assign rom[13'd5] = 12'hba7; assign rom[13'd6] = 12'hba8; assign rom[13'd7] = 12'hba8; assign rom[13'd8] = 12'hba8; assign rom[13'd9] = 12'hbb8; assign rom[13'd10] = 12'hbb8; assign rom[13'd11] = 12'hbb8; assign rom[13'd12] = 12'hcb8; assign rom[13'd13] = 12'hcb8; assign rom[13'd14] = 12'hcb9; assign rom[13'd15] = 12'hcb9; assign rom[13'd16] = 12'hcb9; assign rom[13'd17] = 12'hcb9; assign rom[13'd18] = 12'hcb9; assign rom[13'd19] = 12'hcc9; assign rom[13'd20] = 12'hcca; assign rom[13'd21] = 12'hcca; assign rom[13'd22] = 12'hcca; assign rom[13'd23] = 12'hcca; assign rom[13'd24] = 12'hcca; assign rom[13'd25] = 12'hcca; assign rom[13'd26] = 12'hcca; assign rom[13'd27] = 12'hdca; // ... endmodule
Below is the simulation waveform. You can refer to vgac_tb.v to prepare the testbench.
Implemented on FPGA:
1:1