📜 ⬆️ ⬇️

VGA adapter on FPGA Altera Cyclone III

Hi Habr - in this article I am going to share my progress in the development of Altera Cyclone III FPGAs . After blinking lights and games with counters - I decided to do something more serious. I made the simplest VGA adapter. About its main parts and will be discussed. The article is more focused on beginners, because for experienced this task is not difficult, but for development, in my opinion - a good training task. I spend my experiments on the Altera DE0 debug board. I will describe the scheme on Verilog , Wednesday - Quartus II v 12.0. So - welcome under the cat:

Block diagram of the VGA adapter
Let's start with the fact that we will understand what VGA consists of and what it is eaten with.
Initially, VGA was created for monitors with a cathode ray tube; therefore, we control here not with a matrix of pixels, as in LCD displays, but with an electron beam that runs across the screen and we need to strengthen / weaken it or turn it off at the right moments. When I started working with VGA - I was surprised - how little detailed information describing this standard I could find - which, incidentally, is understandable - it is already outdated and the manufacturers refuse it. I hope my article will help someone in mastering the standard.
A clipping from a VGA article about VGA:
VGA (as well as EGA) consists of the following main subsystems (popularly, the word “sequencer” was called a set of access control registers to the planes of video memory):

In the role of a synchronizer, we will have a 50 MHz generator, which is present on the debug board.
We are not going to implement video memory and data exchange with the processor - instead, we ourselves will generate a stream of bits for the attribute controller.
So - the task is divided into the implementation of the following blocks:

  1. Synchronizer - 50 megahertz frequency is not very convenient for us, so it will have to be slightly changed
  2. CRT controller , which will issue synchronization signals in accordance with the requirements of the VGA standard
  3. The picture generator will be used for debugging - the block will draw the simplest picture and give it to the CRT controller

Synchronizer


Let's start with the fact that we climb the English Wiki and watch the Signal timings for VGA, where we learn that the clock frequency should be 25.175 MHz.

So we have the first task - to make 50 MHz - 25.175 MHz. PLL or PLL will help us in this matter.
With the help of Mega Wizard, we create our PLL with the necessary settings. To do this, go Tools / MegaWizard Plug-InManager. There we select the type of output file Verilog HDL I / O ALTPLL and tune it - set the input frequency and then convert it. We need to get a frequency of 25.175 MHz, while we can only multiply and divide by an integer, that is, we need to convert the number 0.5035 into a fraction. You can multiply by 5035 and divide by 10,000, but note that you can reduce both numbers by 5. We get a fraction of 1007/2000. At the same time, the wizard itself will bring the required multiplier to the fraction available to him. We include the received module in the project, we plug our shred from the board to its input, and to create an output we create a wire, which we will get somewhere later, the main goal of this item is to achieve a frequency of 25.175 MHz.

CRT controller


Clock Generators

Moving on. Now we need to create modules that provide sync signals that satisfy the horizontal and vertical sync timings, that is, for horizontal synchronization:

That is, we must hold the synchronization line in high state time A before issuing data, then press to earth for time B, then transfer to high state, wait for time C and only then the data flow goes - the ray begins to run through the line in time D and we This time should have time to install all the pixels with a frequency of 25.175 megahertz and so on for each row. Vertical synchronization works much the same way, but instead of pixels, we transfer lines and in one sync pulse period we draw the entire frame.
The task has been defined - to create a block that implements the sequence described above, taking the VGA_CLK frequency as an input, let us add, perhaps, a reset signal from the inputs. The implementation of such a block is not simple, but very simple:
With a period of 1 / VGA_CLK = 0.03972194637537239324726911618669 microseconds, we need 24 cycles per A, 95 per B, 48 per C, and 640 per D and on the rising edges of the horizontal synchronization signal, then all the delays can be considered in the periods of drawing the line. In addition, we have an “idle” area on the screen, really - the screen is a bit larger in tact, and we need to draw only in the visible area, therefore it is desirable that the module should inform us the current coordinates of the ray, and if we are going to assign each pixel some kind of color - without knowing the current position of the beam is not enough.
Sync module
module VGA_SYNC( input CLK, input SYNC_RST_N, output reg H_SYNC_CLK, output reg V_SYNC_CLK, output wire [10:0]oCurrent_X, output wire [10:0]oCurrent_Y, output reg oSYNC_COLOR); parameter A_TIME_H = 24; parameter B_TIME_H = 95; parameter C_TIME_H = 48; parameter D_TIME_H = 640; parameter TOTAL_TIME_H = A_TIME_H + B_TIME_H + C_TIME_H + D_TIME_H; parameter BLANK_H = A_TIME_H + B_TIME_H + C_TIME_H; parameter A_TIME_V = 10; parameter B_TIME_V = 2; parameter C_TIME_V = 33; parameter D_TIME_V = 480; parameter TOTAL_TIME_V = A_TIME_V + B_TIME_V + C_TIME_V + D_TIME_V; parameter BLANK_V = A_TIME_V + B_TIME_V + C_TIME_V; reg [10:0]H_Counter; reg [10:0]V_Counter; assign oCurrent_X = (H_Counter >= BLANK_H) ? H_Counter-BLANK_H : 11'h0 ; assign oCurrent_Y = (V_Counter >= BLANK_V) ? V_Counter-BLANK_V : 11'h0 ; always@(posedge(CLK) or negedge(SYNC_RST_N)) begin if(!SYNC_RST_N) begin H_Counter <= 1'b0; H_SYNC_CLK <= 1'b1; end else begin if(H_Counter < (TOTAL_TIME_H-1)) H_Counter <= H_Counter + 1'b1; else begin H_Counter <= 1'b0; oSYNC_COLOR <= 1'b0; end if(H_Counter == A_TIME_H-1) H_SYNC_CLK <= 1'b0; if(H_Counter == A_TIME_H + B_TIME_H-1) H_SYNC_CLK <= 1'b1; if(H_Counter == BLANK_H) oSYNC_COLOR <= 1'b1; end end always@(posedge(H_SYNC_CLK) or negedge(SYNC_RST_N)) begin if(!SYNC_RST_N) begin V_Counter <= 1'b0; V_SYNC_CLK <= 1'b1; end else begin if(V_Counter < (TOTAL_TIME_V-1)) V_Counter <= V_Counter + 1'b1; else V_Counter <= 1'b0; if(V_Counter == A_TIME_V-1) V_SYNC_CLK <= 1'b0; if(V_Counter == A_TIME_V + B_TIME_V-1) V_SYNC_CLK <= 1'b1; end end endmodule 

Output module

The actual pulses are generated, the coordinates of the point are known, it remains only to turn on the desired color in time, while the beam runs through the line - to do this, create a module that receives synchronization signals, a frequency of 25.175 MHz, 3 color buses and coordinates, and an output - 4-bit R-2R DAC for each color, well, further - VGA connector
Output Module
 module VGA_OUT(RESET, SYNC_COLOR, VGA_CLK, oVGA_R, oVGA_G, oVGA_B, iVGA_R, iVGA_G, iVGA_B, Current_X, Current_Y); input wire VGA_CLK; input wire RESET; input wire SYNC_COLOR; input wire [10:0]Current_X; input wire [10:0]Current_Y; input wire [3:0]iVGA_R; input wire [3:0]iVGA_G; input wire [3:0]iVGA_B; output reg [3:0]oVGA_R; output reg [3:0]oVGA_G; output reg [3:0]oVGA_B; always@(posedge VGA_CLK or negedge RESET) begin if(!RESET) begin oVGA_R <= 0; oVGA_G <= 0; oVGA_B <= 0; end else begin oVGA_R <= ((SYNC_COLOR == 1)&&(Current_X > 0)&&(Current_Y > 0)) ? iVGA_R : 0; oVGA_G <= ((SYNC_COLOR == 1)&&(Current_X > 0)&&(Current_Y > 0)) ? iVGA_G : 0; oVGA_B <= ((SYNC_COLOR == 1)&&(Current_X > 0)&&(Current_Y > 0)) ? iVGA_B : 0; end end endmodule 

To check the system - we will generate some kind of picture - this block is not described by me, I took it from Alter’s exams, well, there’s nothing to deal with - we only need a picture from this module
Bistrim Generation Module
 module VGA_BITSTREAM ( // Read Out Side oRed, oGreen, oBlue, iVGA_X, iVGA_Y, iVGA_CLK, // Control Signals iRST_n, iColor_SW ); // Read Out Side output reg [9:0] oRed; output reg [9:0] oGreen; output reg [9:0] oBlue; input [9:0] iVGA_X; input [9:0] iVGA_Y; input iVGA_CLK; // Control Signals input iRST_n; input iColor_SW; always@(posedge iVGA_CLK or negedge iRST_n) begin if(!iRST_n) begin oRed <= 0; oGreen <= 0; oBlue <= 0; end else begin if(iColor_SW == 1) begin if (iVGA_Y<120) begin oRed <= (iVGA_X<40) ? 0 : (iVGA_X>=40 && iVGA_X<80) ? 1 : (iVGA_X>=80 && iVGA_X<120) ? 2 : (iVGA_X>=120 && iVGA_X<160) ? 3 : (iVGA_X>=160 && iVGA_X<200) ? 4 : (iVGA_X>=200 && iVGA_X<240) ? 5 : (iVGA_X>=240 && iVGA_X<280) ? 6 : (iVGA_X>=280 && iVGA_X<320) ? 7 : (iVGA_X>=320 && iVGA_X<360) ? 8 : (iVGA_X>=360 && iVGA_X<400) ? 9 : (iVGA_X>=400 && iVGA_X<440) ? 10 : (iVGA_X>=440 && iVGA_X<480 ) ? 11 : (iVGA_X>=480 && iVGA_X<520 ) ? 12 : (iVGA_X>=520 && iVGA_X<560 ) ? 13 : (iVGA_X>=560 && iVGA_X<600 ) ? 14 : 15 ; oGreen <= 0; oBlue <= 0; end else if (iVGA_Y>=120 && iVGA_Y<240) begin oRed <= 0; oGreen <= (iVGA_X<40) ? 15 : (iVGA_X>=40 && iVGA_X<80) ? 14 : (iVGA_X>=80 && iVGA_X<120) ? 13 : (iVGA_X>=120 && iVGA_X<160) ? 12 : (iVGA_X>=160 && iVGA_X<200) ? 11 : (iVGA_X>=200 && iVGA_X<240) ? 10 : (iVGA_X>=240 && iVGA_X<280) ? 9 : (iVGA_X>=280 && iVGA_X<320) ? 8 : (iVGA_X>=320 && iVGA_X<360) ? 7 : (iVGA_X>=360 && iVGA_X<400) ? 6 : (iVGA_X>=400 && iVGA_X<440) ? 5 : (iVGA_X>=440 && iVGA_X<480 ) ? 4 : (iVGA_X>=480 && iVGA_X<520 ) ? 3 : (iVGA_X>=520 && iVGA_X<560 ) ? 2 : (iVGA_X>=560 && iVGA_X<600 ) ? 1 : 0 ; oBlue <= 0; end else if (iVGA_Y>=240 && iVGA_Y<360) begin oRed <= 0; oGreen <= 0; oBlue <= (iVGA_X<40) ? 0 : (iVGA_X>=40 && iVGA_X<80) ? 1 : (iVGA_X>=80 && iVGA_X<120) ? 2 : (iVGA_X>=120 && iVGA_X<160) ? 3 : (iVGA_X>=160 && iVGA_X<200) ? 4 : (iVGA_X>=200 && iVGA_X<240) ? 5 : (iVGA_X>=240 && iVGA_X<280) ? 6 : (iVGA_X>=280 && iVGA_X<320) ? 7 : (iVGA_X>=320 && iVGA_X<360) ? 8 : (iVGA_X>=360 && iVGA_X<400) ? 9 : (iVGA_X>=400 && iVGA_X<440) ? 10 : (iVGA_X>=440 && iVGA_X<480 ) ? 11 : (iVGA_X>=480 && iVGA_X<520 ) ? 12 : (iVGA_X>=520 && iVGA_X<560 ) ? 13 : (iVGA_X>=560 && iVGA_X<600 ) ? 14 : 15 ; end else begin oRed <= (iVGA_X<40) ? 15 : (iVGA_X>=40 && iVGA_X<80) ? 14 : (iVGA_X>=80 && iVGA_X<120) ? 13 : (iVGA_X>=120 && iVGA_X<160) ? 12 : (iVGA_X>=160 && iVGA_X<200) ? 11 : (iVGA_X>=200 && iVGA_X<240) ? 10 : (iVGA_X>=240 && iVGA_X<280) ? 9 : (iVGA_X>=280 && iVGA_X<320) ? 8 : (iVGA_X>=320 && iVGA_X<360) ? 7 : (iVGA_X>=360 && iVGA_X<400) ? 6 : (iVGA_X>=400 && iVGA_X<440) ? 5 : (iVGA_X>=440 && iVGA_X<480 ) ? 4 : (iVGA_X>=480 && iVGA_X<520 ) ? 3 : (iVGA_X>=520 && iVGA_X<560 ) ? 2 : (iVGA_X>=560 && iVGA_X<600 ) ? 1 : 0 ; oGreen <= (iVGA_X<40) ? 15 : (iVGA_X>=40 && iVGA_X<80) ? 14 : (iVGA_X>=80 && iVGA_X<120) ? 13 : (iVGA_X>=120 && iVGA_X<160) ? 12 : (iVGA_X>=160 && iVGA_X<200) ? 11 : (iVGA_X>=200 && iVGA_X<240) ? 10 : (iVGA_X>=240 && iVGA_X<280) ? 9 : (iVGA_X>=280 && iVGA_X<320) ? 8 : (iVGA_X>=320 && iVGA_X<360) ? 7 : (iVGA_X>=360 && iVGA_X<400) ? 6 : (iVGA_X>=400 && iVGA_X<440) ? 5 : (iVGA_X>=440 && iVGA_X<480 ) ? 4 : (iVGA_X>=480 && iVGA_X<520 ) ? 3 : (iVGA_X>=520 && iVGA_X<560 ) ? 2 : (iVGA_X>=560 && iVGA_X<600 ) ? 1 : 0 ; oBlue <= (iVGA_X<40) ? 15 : (iVGA_X>=40 && iVGA_X<80) ? 14 : (iVGA_X>=80 && iVGA_X<120) ? 13 : (iVGA_X>=120 && iVGA_X<160) ? 12 : (iVGA_X>=160 && iVGA_X<200) ? 11 : (iVGA_X>=200 && iVGA_X<240) ? 10 : (iVGA_X>=240 && iVGA_X<280) ? 9 : (iVGA_X>=280 && iVGA_X<320) ? 8 : (iVGA_X>=320 && iVGA_X<360) ? 7 : (iVGA_X>=360 && iVGA_X<400) ? 6 : (iVGA_X>=400 && iVGA_X<440) ? 5 : (iVGA_X>=440 && iVGA_X<480 ) ? 4 : (iVGA_X>=480 && iVGA_X<520 ) ? 3 : (iVGA_X>=520 && iVGA_X<560 ) ? 2 : (iVGA_X>=560 && iVGA_X<600 ) ? 1 : 0 ; end end else begin oRed <= (iVGA_Y<120) ? 3 : (iVGA_Y>=120 && iVGA_Y<240) ? 7 : (iVGA_Y>=240 && iVGA_Y<360) ? 11 : 15 ; oGreen <= (iVGA_X<80) ? 1 : (iVGA_X>=80 && iVGA_X<160) ? 3 : (iVGA_X>=160 && iVGA_X<240) ? 5 : (iVGA_X>=240 && iVGA_X<320) ? 7 : (iVGA_X>=320 && iVGA_X<400) ? 9 : (iVGA_X>=400 && iVGA_X<480) ? 11 : (iVGA_X>=480 && iVGA_X<560) ? 13 : 15 ; oBlue <= (iVGA_Y<60) ? 15 : (iVGA_Y>=60 && iVGA_Y<120) ? 13 : (iVGA_Y>=120 && iVGA_Y<180) ? 11 : (iVGA_Y>=180 && iVGA_Y<240) ? 9 : (iVGA_Y>=240 && iVGA_Y<300) ? 7 : (iVGA_Y>=300 && iVGA_Y<360) ? 5 : (iVGA_Y>=360 && iVGA_Y<420) ? 3 : 1 ; end end end endmodule 

Main module
 module VGA_MAIN(CLOCK_50, KEY, LEDG, VGA_HS, VGA_VS, VGA_R, VGA_G, VGA_B, SW ); input CLOCK_50; input [2:0]KEY; input [9:0]SW; output [9:0]LEDG; output VGA_HS; // VGA H_SYNC output VGA_VS; // VGA V_SYNC output [3:0] VGA_R; // VGA Red[3:0] output [3:0] VGA_G; // VGA Green[3:0] output [3:0] VGA_B; // VGA Blue[3:0] wire VGA_CLK; wire H_SYNC_CLK; wire V_SYNC_CLK; wire RESET; wire [10:0]Current_X; wire [10:0]Current_Y; wire SYNC_COLOR; reg [3:0] iVGA_R; reg [3:0] iVGA_G; reg [3:0] iVGA_B; wire [3:0] irVGA_R; wire [3:0] irVGA_G; wire [3:0] irVGA_B; assign irVGA_R[3:0] = iVGA_R[3:0]; assign irVGA_G[3:0] = iVGA_G[3:0]; assign irVGA_B[3:0] = iVGA_B[3:0]; assign RESET = KEY[0]; assign VGA_HS = H_SYNC_CLK; assign VGA_VS = V_SYNC_CLK; VGA_PLL u1 ( .inclk0(CLOCK_50), .c0(VGA_CLK) ); VGA_SYNC u2 ( .CLK(VGA_CLK), .H_SYNC_CLK(H_SYNC_CLK), .V_SYNC_CLK(V_SYNC_CLK), .SYNC_RST_N(KEY[0]), .oCurrent_X(Current_X), .oCurrent_Y(Current_Y), .oSYNC_COLOR(SYNC_COLOR)); VGA_OUT u3 ( .oVGA_R(VGA_R[3:0]), .oVGA_G(VGA_G[3:0]), .oVGA_B(VGA_B[3:0]), .iVGA_R(iVGA_R[3:0]), .iVGA_G(iVGA_G[3:0]), .iVGA_B(iVGA_B[3:0]), .VGA_CLK(VGA_CLK), .Current_X(Current_X), .Current_Y(Current_Y), .SYNC_COLOR(SYNC_COLOR), .RESET(RESET) ); VGA_BITSTREAM u4(.oRed(irVGA_R), .oGreen(irVGA_G), .oBlue(irVGA_B), .iVGA_X(Current_X), .iVGA_Y(Current_Y), .iVGA_CLK(VGA_CLK), .iRST_n(RESET), .iColor_SW(SW[0])); endmodule 

The result is such a structural scheme:


Well, the video work:

')
In general, the first step in creating your video card is done - it remains to attach the video memory and connect it all with an external controller - for example, STM32. In the future, I want to make the controller generate a video stream and send it to the video adapter, and that one would display it on the screen. Of course, it’s not worth talking about the practical sense of this all - things are already outdated and rather primitive, everything was done only for the purpose of teaching and reinforcing skills, somewhere there is a fast block code, so if you have any questions, tips and comments, write in the comments

Source: https://habr.com/ru/post/157863/


All Articles