当前位置:网站首页>Design of vga/lcd display controller system based on FPGA (Part 2)

Design of vga/lcd display controller system based on FPGA (Part 2)

2022-06-24 15:11:00 FPGA technology Jianghu

be based on FPGA Of VGA/LCD Display controller system design ( in )

Today, I bring you FPGA Of VGA/LCD Display controller design , Because of the long space , It is divided into three parts . Today brings the second , medium-length ,VGA Display principle and VGA/LCD Display the basic framework of the controller , Don't talk much , Loading .

There were also image processing and VGA Show related articles , Great Xia, you can search by yourself .

Source series : be based on FPGA Of VGA Drive design ( Source attached works )

be based on FPGA Real time image edge detection system design ( On )

be based on FPGA Real time image edge detection system design ( in )

be based on FPGA Real time image edge detection system design ( Next )

Reading guide

VGA (Video Graphics Array) Video graphics array , yes IBM On 1987 Annual follow PS/2 machine (PersonalSystem 2) A video transmission standard using analog signals . This standard is very outdated for today's PC market . But at that time, it had high resolution 、 The display speed is fast 、 Rich colors, etc , It has been widely used in the field of color display , It is a low standard supported by many manufacturers .

LCD ( Liquid Crystal Display For short ) Liquid crystal display .LCD The structure is to place a liquid crystal cell between two parallel glass substrates , The lower base plate is arranged on the glass TFT( Thin film transistor ), A color filter is arranged on the upper substrate glass , adopt TFT The rotation direction of liquid crystal molecules is controlled by changing the signal and voltage on the liquid crystal , So as to control whether the polarized light of each pixel is emitted or not and achieve the purpose of display . According to the different backlights ,LCD Can be divided into CCFL A display and a LED There are two types of monitors .LCD Has replaced CRT The mainstream , Prices have also fallen a lot , And has been fully popularized .

In the previous article, I introduced how to get 、 Process the video signal provided by the camera , In practical application, the processed signals need to be displayed on the display . This process is opposite to that in signal processing , The composition of digital signals according to the system of TV signals conforms to the time sequence 、 Signal with required format , And add various synchronization signals for control . This article will pass FPGA Achieve one VGA/LCD Displays an instance of the controller , And the implementation process is introduced in detail .

Part II summary : Introduction VGA/LCD Implementation of display controller program , Including top-level programs 、 Color lookup table 、 Color processor 、 Cursor processor 、 Video timing generator and output FIFO And so on .

3、 ... and 、VGA/LCD Implementation of display controller program

3.1 Top level program

The top-level program needs to connect and control each sub module , top floor vga_top The module code is as follows :

module vga_enh_top (…);
  // Input and output 
  input wb_clk_i; // External clock input 
  input wb_rst_i; //  Synchronous high effective restart signal 
  input rst_i; //  Asynchronous restart signal 
  output wb_inta_o; //  Interrupt request signal 
  ….
  // Connect the sub modules 
  vga_wb_slave wbs (
                    .clk_i ( wb_clk_i ),
                    ….
                   );
                ….
    // Read output  FIFO  An interrupt signal is generated when the content 
    always @(posedge clk_p_i)
    luint_pclk <= #1 line_fifo_rreq & line_fifo_empty_rd;
    
    always @(posedge wb_clk_i or negedge arst)
    //ctrl_ven  Is in the control register  VEN  position , It is the enabling bit of the display 
    // When the monitor is not working , Clear interrupt 
        if (!ctrl_ven)
            begin
                sluint <= #1 1'b0;
                luint <= #1 1'b0;
            end
        else
            begin
                sluint <= #1 luint_pclk;
                luint <= #1 sluint;
            end
endmodule  

3.2 Color lookup table --Color Lookup Table

The color lookup table saves 256 Color resolution R、G、B All possible colors , So it's a piece 256×24 Bit static RAM District , Each pixel consists of R、G、B Each color 8 Bit data composition , The program includes two such color lookup tables , altogether 512×24 position .

The main codes of the color lookup table are as follows :

module vga_csm_pb (clk_i, req0_i, ack0_o, adr0_i, dat0_i, dat0_o, we0_i, req1_i, ack1_o, adr1_i, dat1_i, dat1_o, we1_i);
    //  Parameter setting 
    // Set the data bus width 
    parameter DWIDTH = 32;
    
    // Address bus width 
    parameter AWIDTH = 8;
    
    // Input and output 
    // Clock input 
    input clk_i;
    
    // Interface to host 
    input [ AWIDTH -1:0] adr0_i; // Address input signal 
    input [ DWIDTH -1:0] dat0_i; // Data input signal 
    output [ DWIDTH -1:0] dat0_o; // Data output signal 
    input we0_i; // Write enable input signal 
    input req0_i; // Access request input signal 
    output ack0_o; // Access reply output signal 
    input [ AWIDTH -1:0] adr1_i; // Address input signal 
    input [ DWIDTH -1:0] dat1_i; // Data input signal 
    output [ DWIDTH -1:0] dat1_o; // Data output signal 
    input we1_i; // Write enable input signal 
    input req1_i; // Access request input signal 
    output ack1_o; // Access reply output signal 
    
    // Variable declarations 
    // Multiple selection signals 
    wire acc0, acc1;
    reg dacc0, dacc1;
    wire sel0, sel1;
    reg ack0, ack1;
    
    // Memory data output 
    wire [DWIDTH -1:0] mem_q;
    
    // Module body 
    // Generate multiple selection signals 
    assign acc0 = req0_i;
    assign acc1 = req1_i && !sel0;
    
    [email protected](posedge clk_i)
        begin
            dacc0 <= #1 acc0 & !ack0_o;
            dacc1 <= #1 acc1 & !ack1_o;
        end
    
    assign sel0 = acc0 && !dacc0;
    assign sel1 = acc1 && !dacc1;
    
    [email protected](posedge clk_i)
        begin
            ack0 <= #1 sel0 && !ack0_o;
            ack1 <= #1 sel1 && !ack1_o;
        end
    
    // Select input address signal 、 Data signals 、 Control signal, etc 
    wire [AWIDTH -1:0] mem_adr = sel0 ? adr0_i : adr1_i;
    wire [DWIDTH -1:0] mem_d = sel0 ? dat0_i : dat1_i;
    wire mem_we = sel0 ? req0_i && we0_i : req1_i && we1_i;
    
    //  Connect to pre-defined single channel memory 
    generic_spram #(AWIDTH, DWIDTH) clut_mem(
                                              .clk(clk_i),
                                              .rst(1'b0), // No restart 
                                              .ce(1'b1), // All the time 
                                              .we(mem_we),
                                              .oe(1'b1), // Always output data 
                                              .addr(mem_adr),
                                              .di(mem_d),
                                              .do(mem_q)
                                             );
    
    // Specify data output 
    assign dat0_o = mem_q;
    assign dat1_o = mem_q;
    
    // Generate reply output 
    assign ack0_o = ( (sel0 && we0_i) || ack0 );
    assign ack1_o = ( (sel1 && we1_i) || ack1 );
    
endmodule

A common memory is defined in the code , Apply to Altera、Xilinx Of FPGA product . The main codes are as follows :

module generic_spram(
    // Universal  SRAM  Interface 
    clk, rst, ce, we, oe, addr, di, do
    )
    
    // Default address and data bus line width 
    parameter aw = 6; // Address bus line width 
    parameter dw = 8; // Data bus line width 
    
    // Universal  SRAM  Interface 
    input clk; // The clock , The rising edge is effective 
    input rst; // Reset signal , Highly effective 
    input ce; // Piece of optional signal , Highly effective 
    input we; // Write enable signal , Highly effective 
    input oe; // Output enable signal , Highly effective 
    input [aw-1:0] addr; // Address bus input 
    input [dw-1:0] di; // Input data bus 
    output [dw-1:0] do; // Output data bus 
    
    // Module body 
    // If it is universal  FPGA
    `ifdef VENDOR_FPGA
        reg [dw-1:0] mem [(1<<aw) -1:0]
        reg [aw-1:0] ra;
    
        // Read operations 
        always @(posedge clk)
            if (ce)
                ra <= #1 addr;
                
        assign #1 do = mem[ra];
    
        // Write operations 
        always @(posedge clk)
            if (we && ce)
                mem[addr] <= #1 di;
    `else
    // If the  XILINX  The company's  FPGA
    `ifdef VENDOR_XILINX
    
        wire [dw-1:0] q; // Output 
        
        //FPGA  Memory instantiation 
        // Virtex/Spartan2 BlockRAMs
        xilinx_ram_sp xilinx_ram(
                                .clk(clk),
                                .rst(rst),
                                .addr(addr),
                                .di(di),
                                .en(ce),
                                .we(we),
                                .do(do)
                              );
    
        defparam
        xilinx_ram.dwidth = dw,
        xilinx_ram.awidth = aw;
    `else
    // If it is  Altera  The company's  FPGA
    `ifdef VENDOR_ALTERA
        //  use  Altera FLEX  Series of  EABs
        altera_ram_sp altera_ram(
                                  .inclock(clk),
                                  .address(addr),
                                  .data(di),
                                  .we(we && ce),
                                  .q(do)
                              );
                              
        defparam
            altera_ram.dwidth = dw,
            altera_ram.awidth = aw;
            
    `else
    `else
        // General mode 
        reg [dw-1:0] mem [(1<<aw)-1:0]; // RAM  Content 
        wire [dw-1:0] q; // RAM  Output 
        reg [aw-1:0] raddr; // RAM  Read the address 
        // Data output 
        assign do = (oe) ? q : {dw{1'bz}};
        
        // RAM  Reading and writing 
        // Read operations 
        [email protected](posedge clk)
        if (ce) // && !we)
            raddr <= #1 addr;
            
        assign #1 q = rst ? {dw{1'b0}} : mem[raddr];
        
        // Write operations 
        [email protected](posedge clk)
            if (ce && we)
                mem[addr] <= #1 di;
                
    `endif // !VENDOR_ALTERA
    `endif // !VENDOR_XILINX
    `endif // !VENDOR_FPGA
        // use  Altera  The company's  FPGA
        `ifdef VENDOR_ALTERA
        module altera_ram_sp (
                                address,
                                inclock,
                                we,
                                data,
                                q
                              );
            
            parameter awidth = 7;
            parameter dwidth = 8;
            
            input [awidth -1:0] address;
            input inclock;
            input we;
            input [dwidth -1:0] data;
            output [dwidth -1:0] q;
            syn_ram_irou #(
                            "UNUSED",
                            dwidth,
                            awidth,
                            1 << awidth
                          );
            
            altera_spram_model (
                                .Inclock(inclock),
                                .Address(address),
                                .Data(data),
                                .WE(we),
                                .Q(q)
                            );
                            
        endmodule
        
    `endif // VENDOR_ALTERA
    // use  XILINX  The company's  FPGA
    `ifdef VENDOR_XILINX
        module xilinx_ram_sp (
            clk,
            rst,
            addr,
            di,
            en,
            we,
            do)
            
            parameter awidth = 7;
            parameter dwidth = 8;
            
            input clk;
            input rst;
            input [awidth -1:0] addr;
            input [dwidth -1:0] di;
            input en;
            input we;
            output [dwidth -1:0] do;
            
            C_MEM_SP_BLOCK_V1_0 #(
                awidth,
                1,
                "0",
                1 << awidth,
                1,
                1,
                1,
                1,
                1,
                1,
                1,
                "",
                16,
                0,
                0,
                1,
                1,
                dwidth
              );
                
            xilinx_spram_model (
                                .CLK(clk),
                                .RST(rst),
                                .ADDR(addr),
                                .DI(di),
                                .EN(en),
                                .WE(we),
                                .DO(do)
                               );
        endmodule
        
    `endif // VENDOR_XILINX

3.3 Color processor --Color Processor

The color processor is responsible for generating the color of each pixel . This function consists of a color processor and an output FIFO Joint completion , The internal structure of the color processor is shown in the figure 5 Shown .

chart 5 Internal structure of color processor

The color processor includes an address generator 、 Data buffer and colorization module :

  • Address generator While generating the address of the video memory , The address generator operates the switching of memory blocks and records the number of pixels to be read . When all pixels are read , Switch the block position of the memory .
  • Data buffering Temporarily save the data read from the video memory , Access to data can be made according to consecutive addresses . All data is stored in consecutive addresses .8 In bit mode , One 32 Bit word save 4 Pixel data ;16 In bit mode , One 32 Bit word save 2 Pixel ;24 In bit mode , One 32 Bit word save 1 1/3 Pixel ;32 In bit mode , One 32 Bit word save 1 Pixel .
  • Colorization module Convert the data stored in the data buffer into color data , And the output .

The main code of the color processor is as follows :

module vga_colproc(clk, srst, vdat_buffer_di, ColorDepth, PseudoColor,
    vdat_buffer_empty, vdat_buffer_rreq, rgb_fifo_full,
    rgb_fifo_wreq, r, g, b,
    clut_req, clut_ack, clut_offs, clut_q
  );
  
    // Input 、 Output 
    input clk; // Input clock 
    input srst; // Synchronous reset signal 
    input [31:0] vdat_buffer_di; // Video memory data input 
    input [1:0] ColorDepth; // Color depth (8  position 、16  position 、24  Bit mode )
    input PseudoColor; // False color enable 
    input vdat_buffer_empty;
    output vdat_buffer_rreq; // Read buffer request 
    reg vdat_buffer_rreq;
    input rgb_fifo_full;
    output rgb_fifo_wreq;
    reg rgb_fifo_wreq;
    output [7:0] r, g, b; // Pixel color information 
    reg [7:0] r, g, b;
    output clut_req; // Color lookup table request 
    reg clut_req;
    input clut_ack; // Color lookup table answer 
    output [ 7:0] clut_offs; // Color lookup table offset 
    reg [7:0] clut_offs;
    input [23:0] clut_q; // Color lookup table data input 
    
    // variable declaration 
    reg [31:0] DataBuffer;
    reg [7:0] Ra, Ga, Ba;
    reg [1:0] colcnt;
    reg RGBbuf_wreq;
    
    // Module content 
    always @(posedge clk)
        if (vdat_buffer_rreq)
            DataBuffer <= #1 vdat_buffer_di;
    
    // State machine 
    // Expand the data read from the data buffer 
    parameter idle = 7'b000_0000,
          fill_buf = 7'b000_0001,
          bw_8bpp = 7'b000_0010,
          col_8bpp = 7'b000_0100,
          col_16bpp_a = 7'b000_1000,
          col_16bpp_b = 7'b001_0000,
          col_24bpp = 7'b010_0000,
          col_32bpp = 7'b100_0000;
          
      reg [6:0] c_state; // synopsys enum_state
      reg [6:0] nxt_state; // synopsys enum_state
      
    // State machine 
    always @(c_state or vdat_buffer_empty or ColorDepth or PseudoColor or rgb_fifo_full or colcnt or clut_ack)
    begin : nxt_state_decoder
        // initialization 
        nxt_state = c_state;
        case (c_state)
            // Idle state 
            idle:
                // If the data buffer is not empty , data  FIFO  In case of dissatisfaction , Start filling in the data 
                if (!vdat_buffer_empty && !rgb_fifo_full)
                    nxt_state = fill_buf;
            //  Fill the data buffer with data  FIFO  in 
            fill_buf:
                // Color mode judgment 
                case (ColorDepth)
                    2'b00:
                        // Pseudo color 
                        if (PseudoColor)
                            nxt_state = col_8bpp;
                        else
                            nxt_state = bw_8bpp;
                            
                    //16  Bit mode 
                    2'b01:
                        nxt_state = col_16bpp_a;
                        
                    //24  Bit mode 
                    2'b10:
                        nxt_state = col_24bpp;
                    //32  Bit mode 
                    2'b11:
                        nxt_state = col_32bpp;
                endcase
                
            //8  Bit black and white mode 
            bw_8bpp:
            if (!rgb_fifo_full && !(|colcnt) )
                if (!vdat_buffer_empty)
                    nxt_state = fill_buf;
                else
                    nxt_state = idle;
            
            //8  Bit color mode 
            col_8bpp:
                if (!(|colcnt))
                    if (!vdat_buffer_empty && !rgb_fifo_full)
                        nxt_state = fill_buf;
                    else
                        nxt_state = idle;
                        
            // 16  Bit color mode 
            col_16bpp_a:
                if (!rgb_fifo_full)
                    nxt_state = col_16bpp_b;
            col_16bpp_b:
                if (!rgb_fifo_full)
                    if (!vdat_buffer_empty)
                        nxt_state = fill_buf;
                    else
                        nxt_state = idle;
                        
            // 24  Bit color mode 
            col_24bpp:
                if (!rgb_fifo_full)
                    if (colcnt == 2'h1) // (colcnt == 1)
                        nxt_state = col_24bpp; //  Stay in the current state 
                    else if (!vdat_buffer_empty)
                        nxt_state = fill_buf;
                    else
                        nxt_state = idle;
                        
            // 32  Bit color mode 
            col_32bpp:
            if (!rgb_fifo_full)
                if (!vdat_buffer_empty)
                    nxt_state = fill_buf;
                else
                    nxt_state = idle;
        endcase
    end
    
    //  Generate status register 
    always @(posedge clk)
        if (srst)
            c_state <= #1 idle;
        else
            c_state <= #1 nxt_state;
            
        reg iclut_req;
        reg ivdat_buf_rreq;
        reg [7:0] iR, iG, iB, iRa, iGa, iBa;
    
    // Output decoding 
    always @(c_state or vdat_buffer_empty or colcnt or DataBuffer or rgb_fifo_full or clut_ack or clut_q or Ba or Ga or Ra)
    begin : output_decoder
    
        // Initialization value 
        ivdat_buf_rreq = 1'b0;
        RGBbuf_wreq = 1'b0;
        iclut_req = 1'b0;
        
        iR = 'h0;
        iG = 'h0;
        iB = 'h0;
        iRa = 'h0;
        iGa = 'h0;
        iBa = '
        
            case (c_state)
                // Idle state 
                idle:
                    begin
                    // preservation  RGB  Data  FIFO  Non empty 
                        if (!rgb_fifo_full)
                            if (!vdat_buffer_empty)
                                ivdat_buf_rreq = 1'b1;
                    //  Enter into  8  Bit pseudo color mode 
                    RGBbuf_wreq = clut_ack;
                    iR = clut_q[23:16];
                    iG = clut_q[15: 8];
                    iB = clut_q[ 7: 0];
                end
                
            // Fill the cache with data 
            fill_buf:
                begin
                    // Get into  8  Bit pseudo color mode 
                    RGBbuf_wreq = clut_ack;
                    iR = clut_q[23:16];
                    iG = clut_q[15: 8];
                    iB = clut_q[ 7: 0];
                end
                
            // 8  Bit black North mode 
            bw_8bpp:
                begin
                    if (!rgb_fifo_full)
                        begin
                            RGBbuf_wreq 
                            
                            if ( (!vdat_buffer_empty) && !(|colcnt) )
                                ivdat_buf_rreq = 1'b1;
                        end
                        
                    case (colcnt)
                        2'b11:
                            begin
                                iR = DataBuffer[31:24];
                                iG = DataBuffer[31:24];
                                iB = DataBuffer[31:24];
                            end
                            
                        2'b10:
                            begin
                                iR = DataBuffer[23:16];
                                iG = DataBuffer[23:16];
                                iB = DataBuffer[23:16];
                            end
                            
                            2'b01:
                                begin
                                    iR = DataBuffer[15:8];
                                    iG = DataBuffer[15:8];
                                    iB = DataBuffer[15:8];
                                end
                                
                            default:
                                begin
                                    iR = DataBuffer[7:0];
                                    iG = DataBuffer[7:0];
                                    iB = DataBuffer[7:0];
                                end
                        endcase
                    end
                    
                    //8  Bit mode 
                    col_8bpp:
                    begin
                        if (!(|colcnt))
                            if (!vdat_buffer_empty && !rgb_fifo_full)
                                ivdat_buf_rreq 
                                
                        RGBbuf_wreq = clut_ack;
                        
                        iR = clut_q[23:16];
                        iG = clut_q[15: 8];
                        iB = clut_q[ 7: 0];
                        iclut_req = !rgb_fifo_full || (colcnt[1] ^ colcnt[0]);
                    end
                    
                    //16  Bit color mode 
                    col_16bpp_a:
                        begin
                        if (!rgb_fifo_full)
                            RGBbuf_wreq = 1'b1;
                            
                        iR[7:3] = DataBuffer[31:27];
                        iG[7:2] = DataBuffer[26:21];
                        iB[7:3] = DataBuffer[20:16];
                        end
                        
                    col_16bpp_b:
                        begin
                            if (!rgb_fifo_full)
                            begin
                                RGBbuf_wreq = 1'b1;
                                
                                if (!vdat_buffer_empty)
                                    ivdat_buf_rreq = 1'b1;
                            end
                            
                                iR[7:3] = DataBuffer[15:11];
                                iG[7:2] = DataBuffer[10: 5];
                                iB[7:3] = DataBuffer[ 4: 0];
                        end
                        
                    // 24  Bit color mode 
                    col_24bpp:
                    begin
                        if (!rgb_fifo_full)
                            begin
                                RGBbuf_wreq 
                                
                                if ( (colcnt != 2'h1) && !vdat_buffer_empty)
                                    ivdat_buf_rreq = 1'b1;
                            end
                            
                        case (colcnt) // synopsis full_case parallel_case
                            2'b11:
                            begin
                                iR = DataBuffer[31:24];
                                iG = DataBuffer[23:16];
                                iB = DataBuffer[15: 8];
                                iRa = DataBuffer[ 7: 0];
                            end
                            
                           2'b10:
                            begin
                                iR = Ra;
                                iG = DataBuffer[31:24];
                                iB = DataBuffer[23:16];
                                iRa = DataBuffer[15: 8];
                                iGa = DataBuffer[ 7: 0];
                            end

                            2'b01:
                            begin
                                iR = Ra;
                                iG = Ga;
                                iB = DataBuffer[31:24];
                                iRa = DataBuffer[23:16];
                                iGa = DataBuffer[15: 8];
                                iBa = DataBuffer[ 7: 0];
                            end
                            
                        default:
                            begin
                                iR = Ra;
                                iG = Ga;
                                iB = Ba;
                            end
                    endcase
                end                            
                    
                    // 32  Bit color mode 
                    col_32bpp:
                    begin
                        if (!rgb_fifo_full)
                            begin
                                RGBbuf_wreq = 1'b1;
                                
                                if (!vdat_buffer_empty)
                                    ivdat_buf_rreq = 1'b1;
                            end
                                iR[7:0] = DataBuffer[23:16];
                                iG[7:0] = DataBuffer[15:8];
                                iB[7:0] = DataBuffer[7:0];
                    end
                endcase
            end
            
            // Generate output register 
            always @(posedge clk)
                begin
                    r <= #1 iR;
                    g <= #1 iG;
                    b <= #1 iB;
                    
                    if (RGBbuf_wreq)
                        begin
                            Ra <= #1 iRa;
                            Ba <= #1 iBa;
                            Ga <= #1 iGa;
                        end
                        
                    if (srst)
                        begin
                            vdat_buffer_rreq <= #1 1'b0;
                            rgb_fifo_wreq <= #1 1'b0;
                            clut_req <= #1 1'b0;
                        end
                    else
                        begin
                            vdat_buffer_rreq <= #1 ivdat_buf_rreq;
                            rgb_fifo_wreq <= #1 RGBbuf_wreq;
                            clut_req <= #1 iclut_req;
                        end
                end
            
            // Color lookup table offset 
            always @(colcnt or DataBuffer)
                case (colcnt) // synopsis full_case parallel_case
                    2'b11: clut_offs = DataBuffer[31:24];
                    2'b10: clut_offs = DataBuffer[23:16];
                    2'b01: clut_offs = DataBuffer[15: 8];
                    2'b00: clut_offs = DataBuffer[ 7: 0];
                endcase
                
          // Color counter 
          always @(posedge clk)
              if (srst)
                  colcnt <= #1 2'b11;
              else if (RGBbuf_wreq)
                  colcnt <= #1 colcnt -2'h1;
                  
endmodule                                                           

3.4 Cursor processor --Cursor Processor

VGA/LCD The display controller also provides a hardware cursor , It can be for GUI( Graphical user interface ,Graphics UserInterface) Provide an arrow like cursor , Pictured 6 Shown .

chart 6 Cursor provided by cursor processor

The cursor is formed by a cursor processor (Cursor Processor) complete . The program provides... For each cursor template 16kbit Space , The resolution of the cursor can be selected , There are two types of templates :

32×32 Pixel mode In this template , Each pixel data is saved in 16 Bit byte . 64×64 Pixel mode In this template , Each pixel data is saved in 4 Bit byte .

The program structure of cursor processor is shown in Figure 7 Shown .

chart 7 Program structure of cursor processor

When copying cursor to cursor data buffer , The address generator generates the memory address needed for the write operation . The cursor data buffer provides a block 512×32 Bit SRAM, Data used to save the cursor . The cursor processor is responsible for tracking the position of the cursor , Decide whether the cursor template needs to be updated 、 Whether the cursor needs to be displayed, etc .

The main code of cursor processor is as follows :

module vga_curproc (clk, rst_i, Thgate, Tvgate, idat, idat_wreq,
    cursor_xy, cursor_en, cursor_res,
    cursor_wadr, cursor_wdat, cursor_we,
    cc_adr_o, cc_dat_i,
    rgb_fifo_wreq, rgb);
    
    // Input and output 
    input clk; // Clock input 
    input rst_i; // Synchronous high effective reset signal 
    
    // Image size 
    input [15:0] Thgate, Tvgate; // Horizontal and vertical dimensions 
    
    // Image data 
    input [23:0] idat; // Input image data 
    input idat_wreq; //  Image data write request 
    
    // Cursor data 
    input [31:0] cursor_xy; // Cursor coordinates 
    input cursor_en; // Cursor valid flag 
    input cursor_res; // Cursor resolution 
    input [ 8:0] cursor_wadr; //  Cursor buffer write address 
    input [31:0] cursor_wdat; //  Cursor buffer writes data 
    input cursor_we; // Cursor buffer write valid 
    
    //  Color register interface 
    output [ 3:0] cc_adr_o; // Cursor color register address 
    reg [ 3:0] cc_adr_o;
    input [15:0] cc_dat_i; //  Cursor color register data 
    
    // And  FIFO  The record of 
    output rgb_fifo_wreq; // RGB  Data output request 
    reg rgb_fifo_wreq;
    output [23:0] rgb; // RGB  Data output 
    reg [23:0] rgb;
    
    // variable declaration 
    reg dcursor_en, ddcursor_en, dddcursor_en;
    reg [15:0] xcnt, ycnt;
    wire xdone, ydone;
    wire [15:0] cursor_x, cursor_y;
    wire cursor_isalpha;
    reg [15:0] cdat, dcdat;
    wire [ 7:0] cursor_r, cursor_g, cursor_b, cursor_alpha;
    reg inbox_x, inbox_y;
    wire inbox;
    reg dinbox, ddinbox, dddinbox;
    reg [23:0] didat, ddidat, dddidat;
    reg didat_wreq, ddidat_wreq;
    wire [31:0] cbuf_q;
    reg [11:0] cbuf_ra;
    reg [ 2:0] dcbuf_ra;
    wire [ 8:0] cbuf_a;
    reg store1, store2;
    
    //  Procedural subject 
    //  produce  x、y  The counter of 
    [email protected](posedge clk)
        if(rst_i || xdone)
            xcnt <= #1 16'h0;
        else if (idat_wreq)
            xcnt <= #1 xcnt + 16'h1;
        
    assign xdone = (xcnt == Thgate) && idat_wreq;
    
    [email protected](posedge clk)
        if(rst_i || ydone)
            ycnt <= #1 16'h0;
        else if (xdone)
            ycnt <= #1 ycnt + 16'h1;
        
    assign ydone = (ycnt == Tvgate) && xdone;
    
    //  Decode cursor position , Decompose into two coordinates 
    assign cursor_x = cursor_xy[15: 0];
    assign cursor_y = cursor_xy[31:16];
    
    //  produce  inbox  The signal 
    [email protected](posedge clk)
        begin
            inbox_x <= #1 (xcnt >= cursor_x) && (xcnt < (cursor_x + (cursor_res ? 16'h7f : 16'h1f) ));
            inbox_y <= #1 (ycnt >= cursor_y) && (ycnt < (cursor_y + (cursor_res ? 16'h7f : 16'h1f) ));
        end
        
    assign inbox = inbox_x && inbox_y;
    
    [email protected](posedge clk)
        dinbox <= #1 inbox;
   
    [email protected](posedge clk)
        if (didat_wreq)
            ddinbox <= #1 dinbox;
    
    [email protected](posedge clk)
        dddinbox <= #1 ddinbox;
    
    //  Generate cursor buffer address counter 
    [email protected](posedge clk)
        if (!cursor_en || ydone)
            cbuf_ra <= #1 12'h0;
        else if (inbox && idat_wreq)
            cbuf_ra <= #1 cbuf_ra +12'h1;
   
    [email protected](posedge clk)
        dcbuf_ra <= #1 cbuf_ra[2:0];
    
    assign cbuf_a = cursor_we ? cursor_wadr : cursor_res ? cbuf_ra[11:3] : cbuf_ra[9:1];
   
    //  Connect to cursor memory 
    generic_spram #(9, 32) cbuf(
                                .clk(clk),
                                .rst(1'b0), //  No reset 
                                .ce(1'b1), //  Always working 
                                .we(cursor_we),
                                .oe(1'b1), //  Always output data 
                                .addr(cbuf_a),
                                .di(cursor_wdat),
                                .do(cbuf_q)
                            );
                            
     //  stay  32×32  Decode cursor data in pixel mode 
    [email protected](posedge clk)
        if (didat_wreq)
            cdat <= #1 dcbuf_ra[0] ? cbuf_q[31:16] : cbuf_q[15:0];
    
    [email protected](posedge clk)
        dcdat <= #1 cdat;
    
    // stay  64×64  Decode cursor data in pixel mode 
    //  Generate cursor color address 
    [email protected](posedge clk)
        if (didat_wreq)
            case (dcbuf_ra)
                3'b000: cc_adr_o <= cbuf_q[ 3: 0];
                3'b001: cc_adr_o <= cbuf_q[ 7: 4];
                3'b010: cc_adr_o <= cbuf_q[11: 8];
                3'b011: cc_adr_o <= cbuf_q[15:12];
                3'b100: cc_adr_o <= cbuf_q[19:16];
                3'b101: cc_adr_o <= cbuf_q[23:20];
                3'b110: cc_adr_o <= cbuf_q[27:24];
                3'b111: cc_adr_o <= cbuf_q[31:28];
            endcase
   
    //  Generate cursor color 
    assign cursor_isalpha = cursor_res ? cc_dat_i[15] : dcdat[15];
    assign cursor_alpha = cursor_res ? cc_dat_i[7:0] : dcdat[7:0];
    assign cursor_r = {cursor_res ? cc_dat_i[14:10] : dcdat[14:10], 3'h0};
    assign cursor_g = {cursor_res ? cc_dat_i[ 9: 5] : dcdat[ 9: 5], 3'h0};
    assign cursor_b = {cursor_res ? cc_dat_i[ 4: 0] : dcdat[ 4: 0], 3'h0};
   
    //  Delay image data 
    [email protected](posedge clk)
        didat <= #1 idat;
    
    [email protected](posedge clk)
        if (didat_wreq)
            ddidat <= #1 didat;
    
    [email protected](posedge clk)
        dddidat <= #1 ddidat;
   
    [email protected](posedge clk)
        begin
            didat_wreq <= #1 idat_wreq;
            ddidat_wreq <= #1 didat_wreq;
        end
        
    // Generate the selected unit 
    [email protected](posedge clk)
        dcursor_en <= #1 cursor_en;
        
    [email protected](posedge clk)
        if (didat_wreq)
            ddcursor_en <= #1 dcursor_en;
            
    [email protected](posedge clk)
        dddcursor_en <= #1 ddcursor_en;
    
    [email protected](posedge clk)
        if (ddidat_wreq)
            if (!dddcursor_en || !dddinbox)
                rgb <= #1 dddidat;
            else if (cursor_isalpha)
                `ifdef VGA_HWC_3D
                    rgb <= #1 dddidat * cursor_alpha;
                `else
                    rgb <= #1 dddidat;
                `endif
            else
                rgb <= #1 {cursor_r, cursor_g, cursor_b};
                
    //  Generate a write request signal 
    [email protected](posedge clk)
        if (rst_i)
            begin
                store1 <= #1 1'b0;
                store2 <= #1 1'b0;
            end
        else
            begin
                store1 <= #1 didat_wreq | store1;
                store2 <= #1 (didat_wreq & store1) | store2;
            end
            
    [email protected](posedge clk)
        rgb_fifo_wreq <= #1 ddidat_wreq & store2;
        
endmodule

3.5 Video timing generator --Video Timing Generator

The video timing generator generates the synchronization signal necessary for the correct display of the image — Horizontal synchronization signal 、 Vertical sync signal .

1) Horizontal synchronization signal of video signal

The horizontal synchronization signal is shown in the figure 8 Shown .

chart 8 Horizontal synchronization signal

Thsync Indicates the time of the horizontal synchronization process , Measure in pixel beats .Thgdel Is the horizontal gate delay time , Indicates the time from the end of synchronization to the start of horizontal gate signal .Thgate Represents the time in the visible area of a video line .Thlen Indicates the length of time for the entire horizontal synchronization .

2) Vertical synchronization signal of video signal

The vertical synchronization signal is shown in the figure 9 Shown .

chart 9 Vertical sync signal

Tvsync Indicates the time of the vertical synchronization process , Measure in line beat .Tvgdel Is the vertical door delay time , Indicates the time from the end of synchronization to the start of vertical door signal .Tvgate Represents the time in the visible area of a video signal .Tvlen Indicates the length of time for the entire horizontal synchronization .

The main code of the video timing generator is as follows :

module vga_tgen(
    clk, clk_ena, rst,
    Thsync, Thgdel, Thgate, Thlen, Tvsync, Tvgdel, Tvgate, Tvlen,
    eol, eof, gate, hsync, vsync, csync, blank
    )
    
    // Input and output 
    input clk;
    input clk_ena;
    input rst;
    
    // Horizontal timing setting input signal 
    input [ 7:0] Thsync; //  Horizontal sync signal width 
    input [ 7:0] Thgdel; //  Horizontal synchronizing gate delay 
    input [15:0] Thgate; //  Horizontal door ( The number of visible pixels per line of video signal )
    input [15:0] Thlen; //  The length of the horizontal synchronization signal  ( Number of pixels per line of video signal )
    
    //  Vertical timing setting input signal 
    input [ 7:0] Tvsync; //  Vertical sync signal width 
    input [ 7:0] Tvgdel; //  Development of vertical synchronous door 
    input [15:0] Tvgate; //  Vertical door ( The number of visible pixels per video signal )
    input [15:0] Tvlen; // The length of the vertical synchronization signal  ( Number of pixel lines per video signal )
   
    // Output 
    output eol; //  The end of a line of signals 
    output eof; //  The end of an image 
    output gate; //  Vertical and horizontal door signals 
    output hsync; //  Horizontal synchronization signal 
    output vsync; //  Vertical sync signal 
    output csync; //  Composite synchronization signal 
    output blank; //  Blank signal 
   
    //  variable declaration 
    wire Hgate, Vgate;
    wire Hdone;
    
    // Procedural subject 
    //  Connect the horizontal timing generator 
    vga_vtim hor_gen(
        .clk(clk),
        .ena(clk_ena),
        .rst(rst),
        .Tsync(Thsync),
        .Tgdel(Thgdel),
        .Tgate(Thgate),
        .Tlen(Thlen),
        .Sync(hsync),
        .Gate(Hgate),
        .Done(Hdone)
    );
    
    //  Connect the vertical timing generator 
    wire vclk_ena = Hdone & clk_ena;
        vga_vtim ver_gen(
        .clk(clk),
        .ena(vclk_ena),
        .rst(rst),
        .Tsync(Tvsync),
        .Tgdel(Tvgdel),
        .Tgate(Tvgate),
        .Tlen(Tvlen),
        .Sync(vsync),
        .Gate(Vgate),
        .Done(eof)
    );
    
    //  Specify the output 
    assign eol = Hdone;
    assign gate = Hgate & Vgate;
    assign csync = hsync | vsync;
    assign blank = ~gate;
    
endmodule

The main codes generated by line synchronization and field synchronization signals are as follows :

module vga_vtim(clk, ena, rst, Tsync, Tgdel, Tgate, Tlen, Sync, Gate, Done);
    //  Input and output 
    input clk; // Clock signal 
    input ena; //  Count enable 
    input rst; //  Synchronous reset signal , Highly effective 
    input [ 7:0] Tsync; //  Synchronization time 
    input [ 7:0] Tgdel; //  Gate delay 
    input [15:0] Tgate; //  Time of door signal 
    input [15:0] Tlen; //  Line time / Field time 
    output Sync; //  Output synchronization signal 
    output Gate; //  Door signal 
    output Done; //  That's ok / The end of the field 
    reg Sync;
    reg Gate;
    reg Done;
    
    //  Procedural subject 
    // Generate a timed state machine 
    reg [15:0] cnt, cnt_len;
    wire [16:0] cnt_nxt, cnt_len_nxt;
    wire cnt_done, cnt_len_done;
    assign cnt_nxt = {1'b0, cnt} -17'h1;
    assign cnt_done = cnt_nxt[16];
    assign cnt_len_nxt = {1'b0, cnt_len} -17'h1;
    assign cnt_len_done = cnt_len_nxt[16];
    reg [4:0] state;
    
    parameter [4:0] idle_state = 5'b00001;
    parameter [4:0] sync_state = 5'b00010;
    parameter [4:0] gdel_state = 5'b00100;
    parameter [4:0] gate_state = 5'b01000;
    parameter [4:0] len_state = 5'b10000;
    
    always @(posedge clk)
        // Reset 
        if (rst)
            begin
                state <= #1 idle_state;
                cnt <= #1 16'h0;
                cnt_len <= #1 16'b0;
                Sync <= #1 1'b0;
                Gate <= #1 1'b0;
                Done <= #1 1'b0;
            end
        else if (ena)
            begin
                cnt <= #1 cnt_nxt[15:0];
                cnt_len <= #1 cnt_len_nxt[15:0];
                Done <= #1 1'b0;
                case (state)
                    // Idle state 
                    idle_state:
                        begin
                            state <= #1 sync_state;
                            cnt <= #1 Tsync;
                            cnt_len <= #1 Tlen;
                            Sync <= #1 1'b1;
                        end
                        
                    // Sync 
                    sync_state:
                        if (cnt_done)
                            begin
                                state <= #1 gdel_state;
                                cnt <= #1 Tgdel;
                                Sync <= #1 1'b0;
                            end
                            
                    // Gate delay 
                    gdel_state:
                        if (cnt_done)
                            begin
                                state <= #1 gate_state;
                                cnt <= #1 Tgate;
                                Gate <= #1 1'b1;
                            end
                            
                    // door 
                    gate_state:
                        if (cnt_done)
                            begin
                                state <= #1 len_state;
                                Gate <= #1 1'b0;
                            end
                    
                    // Total length 
                    len_state:
                        if (cnt_len_done)
                            begin
                                state <= #1 sync_state;
                                cnt <= #1 Tsync;
                                cnt_len <= #1 Tlen;
                                Sync <= #1 1'b1;
                                Done <= #1 1'b1;
                            end
                endcase
            end
endmoduld

3.6 Output FIFO

Output FIFO Used to ensure continuous data flow output to VGA perhaps LCD On the monitor .

Output FIFO The main code of is as follows :

module vga_fifo_dc (rclk, wclk, aclr, wreq, d, rreq, q, rd_empty, rd_full, wr_empty, wr_full);
    
    //  Parameter setting 
    parameter AWIDTH = 7; //128  An entrance 
    parameter DWIDTH = 16; //16bit  Data bus width 
    
    //  Input and output 
    input rclk; //  Read the clock 
    input wclk; //  Write clock 
    input aclr; //  Asynchronous clear signal , Low efficiency 
    input wreq; //  Write request signal 
    input [DWIDTH -1:0] d; //  data input 
    input rreq; //  Read request 
    output [DWIDTH -1:0] q; //  Data output 
    output rd_empty; // FIFO  Empty flag , Synchronize with the read clock 
    reg rd_empty;
    output rd_full; // FIFO  Full sign ,  Synchronize with the read clock 
    reg rd_full;
    output wr_empty; // FIFO  Empty flag , Synchronize with the write clock 
    reg wr_empty;
    output wr_full; // FIFO  Full sign , Synchronize with the write clock 
    reg wr_full;
    
    //  variable declaration 
    reg [AWIDTH -1:0] rptr, wptr;
    wire ifull, iempty;
    reg rempty, rfull, wempty, wfull;
    
    //  Procedural subject 
    //  Read pointer 
    [email protected](posedge rclk or negedge aclr)
        if (~aclr)
            rptr <= #1 0;
        else if (rreq)
            rptr <= #1 rptr + 1;
    
    //  The write pointer 
    [email protected](posedge wclk or negedge aclr)
        if (~aclr)
            wptr <= #1 0;
        else if (wreq)
            wptr <= #1 wptr +1;
    
    //  Status flag 
    wire [AWIDTH -1:0] tmp;
    wire [AWIDTH -1:0] tmp2;
    
    assign tmp = wptr - rptr;
    assign iempty = (rptr == wptr) ? 1'b1 : 1'b0;
    assign tmp2 = (1 << AWIDTH) -3;
    assign ifull = ( tmp >= tmp2 ) ? 1'b1 : 1'b0;
    
    // Read the clock sign 
    [email protected](posedge rclk or negedge aclr)
        if (~aclr)
            begin
                rempty <= #1 1'b1;
                rfull <= #1 1'b0;
                rd_empty <= #1 1'b1;
                rd_full <= #1 1'b0;
            end
        else
            begin
                rempty <= #1 iempty;
                rfull <= #1 ifull;
                rd_empty <= #1 rempty;
                rd_full <= #1 rfull;
            end
    
    //  Write clock flag 
    [email protected](posedge wclk or negedge aclr)
        if (~aclr)
            begin
                wempty <= #1 1'b1;
                wfull <= #1 1'b0;
                wr_empty <= #1 1'b1;
                wr_full <= #1 1'b0;
            end
        else
            begin
                wempty <= #1 iempty;
                wfull <= #1 ifull;
                wr_empty <= #1 wempty;
                wr_full <= #1 wfull;
            end
            
    //  Connect to dual port memory 
    generic_dpram #(AWIDTH, DWIDTH) fifo_dc_mem(
          .rclk(rclk),
          .rrst(1'b0),
          .rce(1'b1),
          .oe(1'b1),
          .raddr(rptr),
          .do(q),
          .wclk(wclk),
          .wrst(1'b0),
          .wce(1'b1),
          .we(wreq),
          .waddr(wptr),
          .di(d)
      );
      
endmodule

This is the end of this article , The next one is based on FPGA Of VGA/LCD Display controller design ( Next ), Program simulation, test and summary .

原网站

版权声明
本文为[FPGA technology Jianghu]所创,转载请带上原文链接,感谢
https://yzsam.com/2021/05/20210521184407159a.html