aboutsummaryrefslogtreecommitdiff
path: root/vga-core/vga_controller.sv
diff options
context:
space:
mode:
Diffstat (limited to 'vga-core/vga_controller.sv')
-rw-r--r--vga-core/vga_controller.sv216
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