2 * | *WISHBONE DATASHEET* |
3 * |---------------------------------------------------------------------------|
4 * | *Description* | *Specification* |
5 * |---------------------------------+-----------------------------------------|
6 * | General description | VGA signal generator (640x480@60Hz) |
7 * |---------------------------------+-----------------------------------------|
8 * | Supported cycles | SLAVE, pipelined READ/WRITE |
9 * |---------------------------------+-----------------------------------------|
10 * | Data port, size | 16-bit |
11 * | Data port, granularity | 16-bit |
12 * | Data port, maximum operand size | 16-bit |
13 * | Data transfer ordering | Big endian and/or little endian |
14 * | Data transfer ordering | Undefined |
15 * | Address port, size | 11-bit |
16 * |---------------------------------+-----------------------------------------|
17 * | Clock frequency constraints | NONE (determined by memory primitive, |
18 * | | about 100 MHz in case of iCE40HX8K) |
19 * |---------------------------------+-----------------------------------------|
20 * | | *Signal name* | *WISHBONE Equiv.* |
21 * | |------------------+----------------------|
23 * | | ADR_I | ADR_I() |
24 * | Supported signal list and cross | CLK_I | CLK_I |
25 * | reference to equivalent | DAT_I | DAT_I() |
26 * | WISHBONE signals | DAT_O | DAT_O() |
30 * | | STALL_O | STALL_O |
31 * |---------------------------------+-----------------------------------------|
32 * | | Circuit assumes the use of synchronous |
33 * | Special requirements | RAM with asynchronour read |
34 * | | inreffable by synthesis software. |
35 * |---------------------------------+-----------------------------------------|
36 * | | Module provides a simple text mode, |
37 * | | that can be used to display 30 lines of |
38 * | | 80 characters each. Module can be used |
40 * | Additional information | * ASCII values of characters to |
41 * | | display should be written to text |
43 * | | * A non-zero value should be |
44 * | | written to the "power-on" |
45 * | | register to start generating VGA |
47 * | | * Zero should be written to the |
48 * | | "power-on" register to stop |
49 * | | generating VGA output. |
50 * | | Also see the memory map below. |
51 * | | The "power-on" register can be |
52 * | | read at any time to check if VGA |
53 * | | output is being generated. It reads |
54 * | | a non-zero value if module is |
55 * | | operating and zero otherwise. |
56 * | | Each byte of the text mode memory |
57 * | | corresponds to one character on |
58 * | | video display. They are arranged by |
59 * | | rows, from up to down. I.e. writing |
60 * | | value 65 to the first 2 bytes of |
61 * | | text video memory shall result in |
62 * | | character "A" being printed in 2 |
63 * | | leftmost fields of the topmost line |
64 * | | of the display. Writing a byte |
65 * | | value outside ASCII range shall |
66 * | | result in a replacement character |
67 * | | being written. Text mode memory is |
68 * | | 2560 bytes big. The last 160 bytes |
69 * | | are not used by video display and |
70 * | | don't serve any special purpose. |
71 * | | The font used for ASCII characters |
72 * | | is defined in an external file and |
73 * | | can be substituted. |
77 * The memory map is as follows:
78 * h000 - h4FF - VGA text memory
79 * h500 - VGA power-on reg
81 * Accessing higher addresses than specified results in UNDEFINED behavior.
88 parameter FONT_FILE
= "font.mem"
93 input wire [10:0] ADR_I
,
94 input wire [15:0] DAT_I
,
95 output wire [15:0] DAT_O
,
102 input wire clock_25mhz
,
105 output wire [2:0] red
,
106 output wire [2:0] green
,
107 output wire [2:0] blue
112 parameter LINES
= V_ACTIVE_VIDEO
/ 16; /* 30 */
113 parameter LINE_LENGTH
= H_ACTIVE_VIDEO
/ 8; /* 80 */
114 parameter CHARACTERS_ON_SCREEN
= LINE_LENGTH
* LINES
; /* 2400 */
117 * We want to store 2400 characters in memory. 2 chars go into one 16-bit
118 * word, so wee need a 1200x16 array. One embedded RAM block in iCE40 FPGAs
119 * is able to store 256x16 bits. This means, instead of choosing 1200 as
120 * array size, we can choose 1280, which is a multiple of 256.
122 reg [15:0] text_memory
[1279 : 0];
124 /* Enable writes and reads of text_memory using wishbone interface. */
126 reg [15:0] data_at_adr
;
130 assign DAT_O
= outputting_data ? data_at_adr
: {16{powered_on
}};
132 assign STALL_O
= 1'b0;
134 always @ (posedge CLK_I
) begin
135 ack
<= STB_I
&& !RST_I
;
137 if (STB_I
&& WE_I
) begin
139 text_memory
[ADR_I
] <= DAT_I
;
141 powered_on
<= DAT_I
!= 16'b0;
144 outputting_data
<= ADR_I
< 1280;
146 data_at_adr
<= text_memory
[ADR_I
];
149 /* Non-wishbone part - generate 640x480 60Hz VGA output */
151 reg powered_on_latched
;
153 always @ (posedge clock_25mhz
)
154 powered_on_latched
<= powered_on
;
156 parameter H_POLARITY
= 1'b0;
157 parameter H_FRONT_PORCH
= 8;
158 parameter H_SYNC
= 96;
159 parameter H_BACK_PORCH
= 40;
160 parameter H_LEFT_BORDER
= 8;
161 parameter H_ACTIVE_VIDEO
= 640;
162 parameter H_RIGHT_BORDER
= 8;
166 parameter H_STAGE_RB_OR_FP
= 0; /* right border of front porch */
167 parameter H_STAGE_SYNC
= 1;
168 parameter H_STAGE_BP_OR_LB
= 2; /* back porch or left border */
169 parameter H_STAGE_ACTIVE_VIDEO
= 3;
173 always @ (posedge clock_25mhz
) begin
174 if (powered_on_latched
) begin
175 if ((h_stage
== H_STAGE_RB_OR_FP
&&
176 h_counter
+ 1 == H_RIGHT_BORDER
+ H_FRONT_PORCH
) ||
177 (h_stage
== H_STAGE_SYNC
&&
178 h_counter
+ 1 == H_SYNC
) ||
179 (h_stage
== H_STAGE_BP_OR_LB
&&
180 h_counter
+ 1 == H_BACK_PORCH
+ H_LEFT_BORDER
) ||
181 (h_stage
== H_STAGE_ACTIVE_VIDEO
&&
182 h_counter
+ 1 == H_ACTIVE_VIDEO
)) begin
183 h_stage
<= h_stage
+ 1;
185 end else begin // if ((h_stage == H_STAGE_RB_OR_FP &&...
186 h_counter
<= h_counter
+ 1;
188 end else begin // if (powered_on_latched)
189 h_stage
<= H_STAGE_RB_OR_FP
;
190 h_counter
<= H_RIGHT_BORDER
- 1;
191 end // else: !if(powered_on_latched)
192 end // always @ (posedge clock_25mhz)
195 assign end_of_line
= h_stage
== H_STAGE_RB_OR_FP
&&
196 h_counter
+ 1 == H_RIGHT_BORDER
;
198 parameter V_POLARITY
= 1'b1;
199 parameter V_FRONT_PORCH
= 2;
200 parameter V_SYNC
= 2;
201 parameter V_BACK_PORCH
= 25;
202 parameter V_TOP_BORDER
= 8;
203 parameter V_ACTIVE_VIDEO
= 480;
204 parameter V_BOTTOM_BORDER
= 8;
208 parameter V_STAGE_BB_OR_FP
= 0; /* bottom border of front porch */
209 parameter V_STAGE_SYNC
= 1;
210 parameter V_STAGE_BP_OR_TB
= 2; /* back porch or top border */
211 parameter V_STAGE_ACTIVE_VIDEO
= 3;
215 always @ (posedge clock_25mhz
) begin
216 if (powered_on_latched
) begin
217 if (end_of_line
) begin
218 if ((v_stage
== V_STAGE_BB_OR_FP
&&
219 v_counter
+ 1 == V_BOTTOM_BORDER
+ V_FRONT_PORCH
) ||
220 (v_stage
== V_STAGE_SYNC
&&
221 v_counter
+ 1 == V_SYNC
) ||
222 (v_stage
== V_STAGE_BP_OR_TB
&&
223 v_counter
+ 1 == V_BACK_PORCH
+ V_TOP_BORDER
) ||
224 (v_stage
== V_STAGE_ACTIVE_VIDEO
&&
225 v_counter
+ 1 == V_ACTIVE_VIDEO
)) begin
226 v_stage
<= v_stage
+ 1;
228 end else begin // if ((v_stage == V_STAGE_BB_OR_FP &&...
229 v_counter
<= v_counter
+ 1;
231 end // if (end_of_line)
232 end else begin // if (powered_on_latched)
233 v_stage
<= V_STAGE_BB_OR_FP
;
234 v_counter
<= V_BOTTOM_BORDER
;
235 end // else: !if(powered_on_latched)
236 end // always @ (posedge clock_25mhz)
238 reg [0:7] font
[128 * 16 - 1 : 0];
240 /* Should result in initialization of embedded RAM */
242 $readmemb(FONT_FILE
, font
, 0, 128 * 16 - 1);
245 parameter FG_COLOR
= {3'b010, 3'b111, 3'b101};
246 parameter BG_COLOR
= {3'b000, 3'b000, 3'b111};
248 /* display this for non-ascii characters */
249 wire [0:7] replacement_char
[0:16];
250 assign replacement_char
[0] = 8'b00000000;
251 assign replacement_char
[1] = 8'b00011000;
252 assign replacement_char
[2] = 8'b00111100;
253 assign replacement_char
[3] = 8'b01100110;
254 assign replacement_char
[4] = 8'b01011010;
255 assign replacement_char
[5] = 8'b01111010;
256 assign replacement_char
[6] = 8'b01111010;
257 assign replacement_char
[7] = 8'b01111010;
258 assign replacement_char
[8] = 8'b01110110;
259 assign replacement_char
[9] = 8'b01101110;
260 assign replacement_char
[10] = 8'b01101110;
261 assign replacement_char
[11] = 8'b01111110;
262 assign replacement_char
[12] = 8'b01101110;
263 assign replacement_char
[13] = 8'b00111100;
264 assign replacement_char
[14] = 8'b00011000;
265 assign replacement_char
[15] = 8'b00000000;
269 wire [12:0] char_flat_idx
;
270 assign char_x
= h_counter
/ 8;
271 assign char_y
= v_counter
/ 16;
272 assign char_flat_idx
= char_x
+ char_y
* LINE_LENGTH
;
275 * hs[0], vs[0], fetched_memory_field, is_high_byte, char, char_pixel_x[0],
276 * char_pixel_y and display_on[0] get loaded first, then, one tick later,
277 * hs[1], vs[1], char_pixel_x[1], display_on[1], char_pixel_row,
278 * replacement_pixel_row and is_ascii_char get loaded and finally, another
279 * tick later, color gets loaded
283 reg [15:0] fetched_memory_field
;
285 reg [2:0] char_pixel_x
[1:0];
286 reg [3:0] char_pixel_y
;
287 reg [1:0] display_on
;
288 reg [0:7] char_pixel_row
;
289 reg [0:7] replacement_pixel_row
;
294 wire [0:7] pixel_row_to_use
;
296 assign char
= is_high_byte ? fetched_memory_field
[15:8] :
297 fetched_memory_field
[7:0];
298 assign pixel_row_to_use
= is_ascii_char ? char_pixel_row
:
299 replacement_pixel_row
;
300 assign pixel_on
= pixel_row_to_use
[char_pixel_x
[1]];
302 /* Assign module's outputs */
303 assign h_sync
= hs
[2];
304 assign v_sync
= vs
[2];
305 assign {red
, green
, blue
} = color
;
307 always @ (posedge clock_25mhz
) begin
308 /* Stuff, that gets loaded first */
309 if (h_stage
== H_STAGE_SYNC
)
312 hs
[0] <= ~H_POLARITY
;
314 if (v_stage
== V_STAGE_SYNC
)
317 vs
[0] <= ~V_POLARITY
;
319 fetched_memory_field
<= text_memory
[char_flat_idx
[12:1]];
320 is_high_byte
<= char_flat_idx
[0];
322 char_pixel_x
[0] <= h_counter
% 8;
323 char_pixel_y
<= v_counter
% 16;
325 display_on
[0] <= h_stage
== H_STAGE_ACTIVE_VIDEO
&&
326 v_stage
== V_STAGE_ACTIVE_VIDEO
;
328 /* Stuff, that gets loaded one tick later */
332 char_pixel_x
[1] <= char_pixel_x
[0];
334 display_on
[1] <= display_on
[0];
336 char_pixel_row
<= font
[char
[6:0] * 16 + char_pixel_y
];
337 replacement_pixel_row
<= replacement_char
[char_pixel_y
];
339 is_ascii_char
<= ~char
[7];
341 /* Stuff, that gets loaded another tick later */
351 end // always @ (posedge clock_25mhz)
354 /* avoid undefined values */
357 powered_on_latched
<= 0;
360 hs
<= {3{~H_POLARITY
}};
361 vs
<= {3{~V_POLARITY
}};
362 fetched_memory_field
<= 16'b0;
364 char_pixel_x
[0] <= 0;
365 char_pixel_x
[1] <= 0;
368 char_pixel_row
<= 8'b0;
369 replacement_pixel_row
<= 8'b0;
374 h_stage
<= H_STAGE_RB_OR_FP
;
377 v_stage
<= V_STAGE_BB_OR_FP
;