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
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
|
`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 wire h_sync,
output wire v_sync,
output wire [2:0] red,
output wire [2:0] green,
output wire [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
parameter FG_COLOR = {3'b010, 3'b111, 3'b101};
parameter BG_COLOR = {3'b000, 3'b000, 3'b111};
/* display this for non-ascii characters */
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 [6:0] char_x;
wire [4:0] char_y;
wire [12:0] char_flat_idx;
assign char_x = h_counter / 8;
assign char_y = v_counter / 16;
assign char_flat_idx = char_x + char_y * LINE_LENGTH;
/*
* hs[0], vs[0], fetched_memory_field, is_high_byte, char, char_pixel_x[0],
* char_pixel_y and display_on[0] get loaded first, then, one tick later,
* hs[1], vs[1], char_pixel_x[1], display_on[1], char_pixel_row,
* replacement_pixel_row and is_ascii_char get loaded and finally, another
* tick later, color gets loaded
*/
reg [2:0] hs;
reg [2:0] vs;
reg [15:0] fetched_memory_field;
reg is_high_byte;
reg [2:0] char_pixel_x [1:0];
reg [3:0] char_pixel_y;
reg [1:0] display_on;
reg [0:7] char_pixel_row;
reg [0:7] replacement_pixel_row;
reg is_ascii_char;
reg [8:0] color;
wire [7:0] char;
wire [0:7] pixel_row_to_use;
wire pixel_on;
assign char = is_high_byte ? fetched_memory_field[15:8] :
fetched_memory_field[7:0];
assign pixel_row_to_use = is_ascii_char ? char_pixel_row :
replacement_pixel_row;
assign pixel_on = pixel_row_to_use[char_pixel_x[1]];
/* Assign module's outputs */
assign h_sync = hs[2];
assign v_sync = vs[2];
assign {red, green, blue} = color;
always @ (posedge clock_25mhz) begin
/* Stuff, that gets loaded first */
if (h_stage == H_STAGE_SYNC)
hs[0] <= H_POLARITY;
else
hs[0] <= ~H_POLARITY;
if (v_stage == V_STAGE_SYNC)
vs[0] <= V_POLARITY;
else
vs[0] <= ~V_POLARITY;
fetched_memory_field <= text_memory[char_flat_idx[12:1]];
is_high_byte <= char_flat_idx[0];
char_pixel_x[0] <= h_counter % 8;
char_pixel_y <= v_counter % 16;
display_on[0] <= h_stage == H_STAGE_ACTIVE_VIDEO &&
v_stage == V_STAGE_ACTIVE_VIDEO;
/* Stuff, that gets loaded one tick later */
hs[1] <= hs[0];
vs[1] <= vs[0];
char_pixel_x[1] <= char_pixel_x[0];
display_on[1] <= display_on[0];
char_pixel_row <= font[char[6:0] * 16 + char_pixel_y];
replacement_pixel_row <= replacement_char[char_pixel_y];
is_ascii_char <= ~char[7];
/* Stuff, that gets loaded another tick later */
hs[2] <= hs[1];
vs[2] <= vs[1];
if (!display_on)
color <= 9'b0;
else if (pixel_on)
color <= FG_COLOR;
else
color <= BG_COLOR;
end // always @ (posedge clock_25mhz)
`ifdef SIMULATION
/* avoid undefined values */
initial begin
powered_on <= 0;
powered_on_latched <= 0;
ack <= 0;
hs <= {3{~H_POLARITY}};
vs <= {3{~V_POLARITY}};
fetched_memory_field <= 16'b0;
is_high_byte <= 0;
char_pixel_x[0] <= 0;
char_pixel_x[1] <= 0;
char_pixel_y <= 0;
display_on <= 2'b0;
char_pixel_row <= 8'b0;
replacement_pixel_row <= 8'b0;
is_ascii_char <= 0;
color <= 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
|