TABLE OF CONTENTS (HIDE)

VGA Controler and Font Table

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 (hs) 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).

vga640x480.svg
vga_timing.svg

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

Testbench vgac_tb.v.

hs starts:

vgac_wave1.bmp

hs ends:

vgac_wave2.bmp

First pixel:

vgac_wave3.bmp

One frame ends and next frame starts:

vgac_wave4.bmp

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).

display_graphics_mode.svg

Text mode: Character RAM stores ASCII. For a 8 $\times$ 8 character font, it needs a 80 $\times$ 60 $\times$ 7 Character RAM.

display_ascii8x8.svg

Addresses in text mode:

vga_addresses.svg

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) {                       // 
                char_row_bitmap = Font[char_no - 0x20][i];
            } else {
                char_row_bitmap = 0;
            }
            for (j = 7; j >= 0; j--) {                   // 8 pixels per row
                printf ("    assign rom[13'h%04x] = ", addr++);
                if (((char_row_bitmap >> j) & 1) == 1) {
                    printf ("1;\n");                     // a dot
                } else {
                    printf ("0;\n");                     // blank
                } 
            }
        }
    }
    printf ("endmodule\n");
    return 0;
}

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

  1. Draw a circuit block diagram for font_table_hello_fpga.v.
  2. 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                                                    
    
    Show and explain the waveforms for 8 rows of characters 'H' (0x48). Two waveforms are shown below.

    (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):

    font_table_hello_fpga_wave1.png
    
    hello-fpga-blue-white.svg
    

    (2) Last row (font_row 7) of characters 'A' (0x41) and '!' (0x21), in the center of screen (char_row = 29, char_col = 44, 45):

    font_table_hello_fpga_wave2.png
    
    hello-fpga-blue-white-A.svg
    

    You can use add wave -r /* to show the waveforms of internal signals. Revise char_ram.v to remove 'X' of the waveforms.

  3. 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
    
    font_table_hello_fpga.png
    
  4. 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_kanji_wave_00_.png
    display_kanji_wave_01_.png
    display_kanji_wave_02_.png
    display_kanji_wave_03_.png
    display_kanji_wave_04_.png
    display_kanji_wave_05_.png
    display_kanji_wave_06_.png
    display_kanji_wave_07_.png
    display_kanji_wave_08_.png
    display_kanji_wave_09_.png
    display_kanji_wave_10_.png
    display_kanji_wave_11_.png
    display_kanji_wave_12_.png
    display_kanji_wave_13_.png
    display_kanji_wave_14_.png
    display_kanji_wave_15_.png
    
    display_kanji.png
    
  5. 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.

    dispaly_image_w75_h100.svg
    

    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.

    display_photo_wave.png
    

    Implemented on FPGA:

    2021-05-19_085709.png
    

    1:1

    dispaly_image_w75_h100_1x1.svg
    
    2023-06-02_110440.png