diff options
| author | github-classroom[bot] <66690702+github-classroom[bot]@users.noreply.github.com> | 2026-02-05 19:36:36 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-02-05 19:36:36 +0000 |
| commit | ead28dd6fed440ccf4667c459778012bb0d95733 (patch) | |
| tree | bbc326fa1b487efc0fe163ef733a76c8a241fbb0 /vga-core/vga_controller.sv | |
Initial commit
Diffstat (limited to 'vga-core/vga_controller.sv')
| -rw-r--r-- | vga-core/vga_controller.sv | 216 |
1 files changed, 216 insertions, 0 deletions
diff --git a/vga-core/vga_controller.sv b/vga-core/vga_controller.sv new file mode 100644 index 0000000..4217c13 --- /dev/null +++ b/vga-core/vga_controller.sv @@ -0,0 +1,216 @@ +// synopsys translate_off +`timescale 1 ps / 1 ps +// synopsys translate_on + +/* This module implements the VGA controller. It assumes a 25MHz clock is supplied as input. + * + * General approach: + * Go through each line of the screen and read the colour each pixel on that line should have from + * the Video memory. To do that for each (x,y) pixel on the screen convert (x,y) coordinate to + * a memory_address at which the pixel colour is stored in Video memory. Once the pixel colour is + * read from video memory its brightness is first increased before it is forwarded to the VGA DAC. + */ +module vga_controller( vga_clock, resetn, pixel_colour, memory_address, + VGA_R, VGA_G, VGA_B, + VGA_HS, VGA_VS, VGA_BLANK, + VGA_SYNC, VGA_CLK); + + /* Screen resolution and colour depth parameters. */ + + parameter BITS_PER_COLOUR_CHANNEL = 1; + /* The number of bits per colour channel used to represent the colour of each pixel. A value + * of 1 means that Red, Green and Blue colour channels will use 1 bit each to represent the intensity + * of the respective colour channel. For BITS_PER_COLOUR_CHANNEL=1, the adapter can display 8 colours. + * In general, the adapter is able to use 2^(3*BITS_PER_COLOUR_CHANNEL) colours. The number of colours is + * limited by the screen resolution and the amount of on-chip memory available on the target device. + */ + + parameter MONOCHROME = "FALSE"; + /* Set this parameter to "TRUE" if you only wish to use black and white colours. Doing so will reduce + * the amount of memory you will use by a factor of 3. */ + + parameter RESOLUTION = "320x240"; + /* Set this parameter to "160x120" or "320x240". It will cause the VGA adapter to draw each dot on + * the screen by using a block of 4x4 pixels ("160x120" resolution) or 2x2 pixels ("320x240" resolution). + * It effectively reduces the screen resolution to an integer fraction of 640x480. It was necessary + * to reduce the resolution for the Video Memory to fit within the on-chip memory limits. + */ + + parameter USING_DE1 = "FALSE"; + /* If set to "TRUE" it adjust the offset of the drawing mechanism to account for the differences + * between the DE2 and DE1 VGA digital to analogue converters. Set to "TRUE" if and only if + * you are running your circuit on a DE1 board. */ + + //--- Timing parameters. + /* Recall that the VGA specification requires a few more rows and columns are drawn + * when refreshing the screen than are actually present on the screen. This is necessary to + * generate the vertical and the horizontal syncronization signals. If you wish to use a + * display mode other than 640x480 you will need to modify the parameters below as well + * as change the frequency of the clock driving the monitor (VGA_CLK). + */ + parameter C_VERT_NUM_PIXELS = 11'd480; + parameter C_VERT_SYNC_START = 11'd493; + parameter C_VERT_SYNC_END = 11'd494; //(C_VERT_SYNC_START + 2 - 1); + parameter C_VERT_TOTAL_COUNT = 11'd525; + + parameter C_HORZ_NUM_PIXELS = 11'd640; + parameter C_HORZ_SYNC_START = 11'd659; + parameter C_HORZ_SYNC_END = 11'd754; //(C_HORZ_SYNC_START + 96 - 1); + parameter C_HORZ_TOTAL_COUNT = 11'd800; + + /*****************************************************************************/ + /* Declare inputs and outputs. */ + /*****************************************************************************/ + + input vga_clock, resetn; + input [((MONOCHROME == "TRUE") ? (0) : (BITS_PER_COLOUR_CHANNEL*3-1)):0] pixel_colour; + output [((RESOLUTION == "320x240") ? (16) : (14)):0] memory_address; + output reg [9:0] VGA_R; + output reg [9:0] VGA_G; + output reg [9:0] VGA_B; + output reg VGA_HS; + output reg VGA_VS; + output reg VGA_BLANK; + output VGA_SYNC, VGA_CLK; + + /*****************************************************************************/ + /* Local Signals. */ + /*****************************************************************************/ + + reg VGA_HS1; + reg VGA_VS1; + reg VGA_BLANK1; + reg [9:0] xCounter, yCounter; + wire xCounter_clear; + wire yCounter_clear; + wire vcc; + + reg [((RESOLUTION == "320x240") ? (8) : (7)):0] x; + reg [((RESOLUTION == "320x240") ? (7) : (6)):0] y; + /* Inputs to the converter. */ + + /*****************************************************************************/ + /* Controller implementation. */ + /*****************************************************************************/ + + assign vcc =1'b1; + + /* A counter to scan through a horizontal line. */ + always @(posedge vga_clock or negedge resetn) + begin + if (!resetn) + xCounter <= 10'd0; + else if (xCounter_clear) + xCounter <= 10'd0; + else + begin + xCounter <= xCounter + 1'b1; + end + end + assign xCounter_clear = (xCounter == (C_HORZ_TOTAL_COUNT-1)); + + /* A counter to scan vertically, indicating the row currently being drawn. */ + always @(posedge vga_clock or negedge resetn) + begin + if (!resetn) + yCounter <= 10'd0; + else if (xCounter_clear && yCounter_clear) + yCounter <= 10'd0; + else if (xCounter_clear) //Increment when x counter resets + yCounter <= yCounter + 1'b1; + end + assign yCounter_clear = (yCounter == (C_VERT_TOTAL_COUNT-1)); + + /* Convert the xCounter/yCounter location from screen pixels (640x480) to our + * local dots (320x240 or 160x120). Here we effectively divide x/y coordinate by 2 or 4, + * depending on the resolution. */ + always @(*) + begin + if (RESOLUTION == "320x240") + begin + x = xCounter[9:1]; + y = yCounter[8:1]; + end + else + begin + x = xCounter[9:2]; + y = yCounter[8:2]; + end + end + + /* Change the (x,y) coordinate into a memory address. */ + vga_address_translator controller_translator( + .x(x), .y(y), .mem_address(memory_address) ); + defparam controller_translator.RESOLUTION = RESOLUTION; + + + /* Generate the vertical and horizontal synchronization pulses. */ + always @(posedge vga_clock) + begin + //- Sync Generator (ACTIVE LOW) + if (USING_DE1 == "TRUE") + VGA_HS1 <= ~((xCounter >= C_HORZ_SYNC_START-2) && (xCounter <= C_HORZ_SYNC_END-2)); + else + VGA_HS1 <= ~((xCounter >= C_HORZ_SYNC_START) && (xCounter <= C_HORZ_SYNC_END)); + VGA_VS1 <= ~((yCounter >= C_VERT_SYNC_START) && (yCounter <= C_VERT_SYNC_END)); + + //- Current X and Y is valid pixel range + VGA_BLANK1 <= ((xCounter < C_HORZ_NUM_PIXELS) && (yCounter < C_VERT_NUM_PIXELS)); + + //- Add 1 cycle delay + VGA_HS <= VGA_HS1; + VGA_VS <= VGA_VS1; + VGA_BLANK <= VGA_BLANK1; + end + + /* VGA sync should be 1 at all times. */ + assign VGA_SYNC = vcc; + + /* Generate the VGA clock signal. */ + assign VGA_CLK = vga_clock; + + /* Brighten the colour output. */ + // The colour input is first processed to brighten the image a little. Setting the top + // bits to correspond to the R,G,B colour makes the image a bit dull. To brighten the image, + // each bit of the colour is replicated through the 10 DAC colour input bits. For example, + // when BITS_PER_COLOUR_CHANNEL is 2 and the red component is set to 2'b10, then the + // VGA_R input to the DAC will be set to 10'b1010101010. + + integer index; + integer sub_index; + + wire on_screen; + + assign on_screen = (USING_DE1 == "TRUE") ? + (({1'b0, xCounter} >= 2) & ({1'b0, xCounter} < C_HORZ_NUM_PIXELS+2) & ({1'b0, yCounter} < C_VERT_NUM_PIXELS)) : + (({1'b0, xCounter} >= 0) & ({1'b0, xCounter} < C_HORZ_NUM_PIXELS+2) & ({1'b0, yCounter} < C_VERT_NUM_PIXELS)); + + always @(pixel_colour or on_screen) + begin + VGA_R <= 'b0; + VGA_G <= 'b0; + VGA_B <= 'b0; + if (MONOCHROME == "FALSE") + begin + for (index = 10-BITS_PER_COLOUR_CHANNEL; index >= 0; index = index - BITS_PER_COLOUR_CHANNEL) + begin + for (sub_index = BITS_PER_COLOUR_CHANNEL - 1; sub_index >= 0; sub_index = sub_index - 1) + begin + VGA_R[sub_index+index] <= on_screen & pixel_colour[sub_index + BITS_PER_COLOUR_CHANNEL*2]; + VGA_G[sub_index+index] <= on_screen & pixel_colour[sub_index + BITS_PER_COLOUR_CHANNEL]; + VGA_B[sub_index+index] <= on_screen & pixel_colour[sub_index]; + end + end + end + else + begin + for (index = 0; index < 10; index = index + 1) + begin + VGA_R[index] <= on_screen & pixel_colour[0:0]; + VGA_G[index] <= on_screen & pixel_colour[0:0]; + VGA_B[index] <= on_screen & pixel_colour[0:0]; + end + end + end + +endmodule |