aboutsummaryrefslogtreecommitdiff
path: root/design/vga.v
blob: e652ea62aa1b8725882fd0147082ab5d36066fb9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
`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