`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 - 1; 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]; /* Should result in initialization of embedded RAM */ 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