aboutsummaryrefslogtreecommitdiff
path: root/design/vga.v
diff options
context:
space:
mode:
authorWojciech Kosior <kwojtus@protonmail.com>2020-09-01 10:54:59 +0200
committerWojciech Kosior <kwojtus@protonmail.com>2020-09-01 11:04:22 +0200
commitee1f6c47e1eff920068f4bceaf604f9535a2e8a9 (patch)
tree580eb001a72601d254bb29cc348a529490f84808 /design/vga.v
parentcd02ddff8886aa1db29f80d3cc5cf99a349d8258 (diff)
downloadAGH-engineering-thesis-ee1f6c47e1eff920068f4bceaf604f9535a2e8a9.tar.gz
AGH-engineering-thesis-ee1f6c47e1eff920068f4bceaf604f9535a2e8a9.zip
start anew
Diffstat (limited to 'design/vga.v')
-rw-r--r--design/vga.v241
1 files changed, 241 insertions, 0 deletions
diff --git a/design/vga.v b/design/vga.v
new file mode 100644
index 0000000..4b71b28
--- /dev/null
+++ b/design/vga.v
@@ -0,0 +1,241 @@
+`default_nettype none
+
+ module vga
+ #(
+ parameter FONT_FILE = "font.mem"
+ )
+ (
+ output wire ACK_O,
+ input wire CLK_I,
+ input wire [10:0] ADR_I,
+ input wire [15:0] DAT_I,
+ output wire [15:0] DAT_O,
+ input wire RST_I,
+ input wire STB_I,
+ input wire WE_I,
+ output wire STALL_O,
+
+ /* Non-wishbone */
+ input wire clock_25mhz,
+ output reg h_sync,
+ output reg v_sync,
+ output reg [2:0] red,
+ output reg [2:0] green,
+ output reg [2:0] blue
+ );
+
+ reg powered_on;
+
+ parameter LINES = V_ACTIVE_VIDEO / 16; /* 30 */
+ parameter LINE_LENGTH = H_ACTIVE_VIDEO / 8; /* 80 */
+ parameter CHARACTERS_ON_SCREEN = LINE_LENGTH * LINES; /* 2400 */
+
+ /*
+ * We want to store 2400 characters in memory. 2 chars go into one 16-bit
+ * word, so wee need a 1200x16 array. One embedded RAM block in iCE40 FPGAs
+ * is able to store 256x16 bits. This means, instead of choosing 1200 as
+ * array size, we can choose 1280, which is a multiple of 256.
+ */
+ reg [15:0] text_memory [1279 : 0];
+
+ /*
+ * Enable writes to text_memory using wishbone interface.
+ * (no reads for now)
+ */
+
+ reg ack;
+
+ assign DAT_O = {16{powered_on}};
+ assign ACK_O = ack;
+ assign STALL_O = 1'b0;
+
+ always @ (posedge CLK_I) begin
+ ack <= STB_I && !RST_I;
+
+ if (STB_I && WE_I) begin
+ if (ADR_I < 1280)
+ text_memory[ADR_I] <= DAT_I;
+ else
+ powered_on <= DAT_I != 16'b0;
+ end
+ end
+
+ /* Non-wishbone part - generate 640x480 60Hz VGA output */
+
+ reg powered_on_latched;
+
+ always @ (posedge clock_25mhz)
+ powered_on_latched <= powered_on;
+
+ parameter H_POLARITY = 1'b0;
+ parameter H_FRONT_PORCH = 8;
+ parameter H_SYNC = 96;
+ parameter H_BACK_PORCH = 40;
+ parameter H_LEFT_BORDER = 8;
+ parameter H_ACTIVE_VIDEO = 640;
+ parameter H_RIGHT_BORDER = 8;
+
+ reg [9:0] h_counter;
+
+ parameter H_STAGE_RB_OR_FP = 0; /* right border of front porch */
+ parameter H_STAGE_SYNC = 1;
+ parameter H_STAGE_BP_OR_LB = 2; /* back porch or left border */
+ parameter H_STAGE_ACTIVE_VIDEO = 3;
+
+ reg [1:0] h_stage;
+
+ always @ (posedge clock_25mhz) begin
+ if (powered_on_latched) begin
+ if ((h_stage == H_STAGE_RB_OR_FP &&
+ h_counter + 1 == H_RIGHT_BORDER + H_FRONT_PORCH) ||
+ (h_stage == H_STAGE_SYNC &&
+ h_counter + 1 == H_SYNC) ||
+ (h_stage == H_STAGE_BP_OR_LB &&
+ h_counter + 1 == H_BACK_PORCH + H_LEFT_BORDER) ||
+ (h_stage == H_STAGE_ACTIVE_VIDEO &&
+ h_counter + 1 == H_ACTIVE_VIDEO)) begin
+ h_stage <= h_stage + 1;
+ h_counter <= 0;
+ end else begin // if ((h_stage == H_STAGE_RB_OR_FP &&...
+ h_counter <= h_counter + 1;
+ end
+ end else begin // if (powered_on_latched)
+ h_stage <= H_STAGE_RB_OR_FP;
+ h_counter <= H_RIGHT_BORDER;
+ end // else: !if(powered_on_latched)
+ end // always @ (posedge clock_25mhz)
+
+ wire end_of_line;
+ assign end_of_line = h_stage == H_STAGE_RB_OR_FP &&
+ h_counter + 1 == H_RIGHT_BORDER;
+
+ parameter V_POLARITY = 1'b1;
+ parameter V_FRONT_PORCH = 2;
+ parameter V_SYNC = 2;
+ parameter V_BACK_PORCH = 25;
+ parameter V_TOP_BORDER = 8;
+ parameter V_ACTIVE_VIDEO = 480;
+ parameter V_BOTTOM_BORDER = 8;
+
+ reg [8:0] v_counter;
+
+ parameter V_STAGE_BB_OR_FP = 0; /* bottom border of front porch */
+ parameter V_STAGE_SYNC = 1;
+ parameter V_STAGE_BP_OR_TB = 2; /* back porch or top border */
+ parameter V_STAGE_ACTIVE_VIDEO = 3;
+
+ reg [1:0] v_stage;
+
+ always @ (posedge clock_25mhz) begin
+ if (powered_on_latched) begin
+ if (end_of_line) begin
+ if ((v_stage == V_STAGE_BB_OR_FP &&
+ v_counter + 1 == V_BOTTOM_BORDER + V_FRONT_PORCH) ||
+ (v_stage == V_STAGE_SYNC &&
+ v_counter + 1 == V_SYNC) ||
+ (v_stage == V_STAGE_BP_OR_TB &&
+ v_counter + 1 == V_BACK_PORCH + V_TOP_BORDER) ||
+ (v_stage == V_STAGE_ACTIVE_VIDEO &&
+ v_counter + 1 == V_ACTIVE_VIDEO)) begin
+ v_stage <= v_stage + 1;
+ v_counter <= 0;
+ end else begin // if ((v_stage == V_STAGE_BB_OR_FP &&...
+ v_counter <= v_counter + 1;
+ end
+ end // if (end_of_line)
+ end else begin // if (powered_on_latched)
+ v_stage <= V_STAGE_BB_OR_FP;
+ v_counter <= V_BOTTOM_BORDER;
+ end // else: !if(powered_on_latched)
+ end // always @ (posedge clock_25mhz)
+
+ reg [0:7] font [128 * 16 - 1 : 0];
+
+ initial begin
+ $readmemb(FONT_FILE, font, 0, 128 * 16 - 1);
+ end
+
+ wire [6:0] char_x;
+ wire [4:0] char_y;
+ wire [12:0] char_flat_idx;
+ wire [15:0] text_memory_field;
+ wire [7:0] char;
+ assign char_x = h_counter / 8;
+ assign char_y = v_counter / 16;
+ assign char_flat_idx = char_x + char_y * LINE_LENGTH;
+ assign text_memory_field = text_memory[char_flat_idx[12:1]];
+ assign char = char_flat_idx[0] ?
+ text_memory_field[15:8] : text_memory_field[7:0];
+
+ wire [0:7] replacement_char [0:16];
+ assign replacement_char[0] = 8'b00000000;
+ assign replacement_char[1] = 8'b00011000;
+ assign replacement_char[2] = 8'b00111100;
+ assign replacement_char[3] = 8'b01100110;
+ assign replacement_char[4] = 8'b01011010;
+ assign replacement_char[5] = 8'b01111010;
+ assign replacement_char[6] = 8'b01111010;
+ assign replacement_char[7] = 8'b01111010;
+ assign replacement_char[8] = 8'b01110110;
+ assign replacement_char[9] = 8'b01101110;
+ assign replacement_char[10] = 8'b01101110;
+ assign replacement_char[11] = 8'b01111110;
+ assign replacement_char[12] = 8'b01101110;
+ assign replacement_char[13] = 8'b00111100;
+ assign replacement_char[14] = 8'b00011000;
+ assign replacement_char[15] = 8'b00000000;
+
+ wire [2:0] char_pixel_x;
+ wire [3:0] char_pixel_y;
+ wire pixel_on;
+ wire display_on;
+ assign char_pixel_x = h_counter % 8;
+ assign char_pixel_y = v_counter % 16;
+ /* Replace non-ASCII characters (i.e. chars with values above 127) */
+ assign pixel_on = char[7] ? replacement_char[char_pixel_y][char_pixel_x] :
+ font[char[6:0] * 16 + char_pixel_y][char_pixel_x];
+ assign display_on = h_stage == H_STAGE_ACTIVE_VIDEO &&
+ v_stage == V_STAGE_ACTIVE_VIDEO;
+
+ parameter FG_COLOR = {3'b010, 3'b111, 3'b101};
+ parameter BG_COLOR = {3'b000, 3'b000, 3'b111};
+
+ always @ (posedge clock_25mhz) begin
+ if (h_stage == H_STAGE_SYNC)
+ h_sync <= H_POLARITY;
+ else
+ h_sync <= ~H_POLARITY;
+
+ if (v_stage == V_STAGE_SYNC)
+ v_sync <= V_POLARITY;
+ else
+ v_sync <= ~V_POLARITY;
+
+ if (!display_on)
+ {red, green, blue} <= 9'b0;
+ else if (pixel_on)
+ {red, green, blue} <= FG_COLOR;
+ else
+ {red, green, blue} <= BG_COLOR;
+ end // always @ (posedge clock_25mhz)
+
+`ifdef SIMULATION
+ /* avoid undefined values */
+ initial begin
+ powered_on <= 0;
+ powered_on_latched <= 0;
+ ack <= 0;
+
+ h_sync <= ~H_POLARITY;
+ v_sync <= ~V_POLARITY;
+ {red, green, blue} <= 9'b0;
+
+ h_counter <= 0;
+ h_stage <= H_STAGE_RB_OR_FP;
+
+ v_counter <= 0;
+ v_stage <= V_STAGE_BB_OR_FP;
+ end
+`endif
+endmodule // vga
+