-
Notifications
You must be signed in to change notification settings - Fork 0
/
vgacon.vhd
307 lines (277 loc) · 11.4 KB
/
vgacon.vhd
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
295
296
297
298
299
300
301
302
303
304
305
306
307
-------------------------------------------------------------------------------
-- Title : VGA Controller for DE1 boards
-- Project :
-------------------------------------------------------------------------------
-- File : vgacontop.vhd
-- Author : Rafael Auler
-- Company :
-- Created : 2010-03-21
-- Last update: 2010-03-26
-- Platform :
-- Standard : VHDL'2008
-------------------------------------------------------------------------------
-- Description:
-------------------------------------------------------------------------------
-- Copyright (c) 2010
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2010-03-21 1.0 Rafael Auler Created
-- 2010-03-26 1.1 Rafael Auler Working 64x60 display w/ internal mem.
-- 2010-03-26 1.2 Rafael Auler Working with arbitrary res. (up to
-- 640x480, tied to on-chip memory
-- availability). Defaults to 128x96.
-- 2018-03-17 1.3 IBFelzmann Modified to work with the new DE1-SoC board
-------------------------------------------------------------------------------
-- How sync signals are generated for 640x480
-- Note: sync signals are active low
-------------------------------------------------------------------------------
-- Horizontal sync:
-- -------------------__--------
-- | | | |
-- <----------->
-- 640
-- <---------------->
-- 660
-- <------------------->
-- 756
-- <-------------------------->
-- 800
-------------------------------------------------------------------------------
-- Vertical sync:
-- -----------------__-------
--
-- | | | |
-- <--------->
-- 480
-- <-------------->
-- 494
-- <----------------->
-- 495
-- <----------------------->
-- 525
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- Notes:
-- write_clk, write_addr, write_enable and data_in are input signals used to
-- write to this controller memory and thus altering the displayed image on VGA.
--
-- "data_in" has 3 bits and represents a single image pixel.
-- (high bit for RED, middle for GREEN and lower for BLUE - total of 8 colors).
--
-- These signals follow simple memory write protocol (we=1 writes
-- data_in to address (pixel number) write_addr. This last signal may assume
-- NUM_HORZ_PIXELS * NUM_VERT_PIXELS different values, corresponding to each
-- one of the displayable pixels.
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
entity vgacon is
generic (
-- When changing this, remember to keep 4:3 aspect ratio
-- Must also keep in mind that our native resolution is 640x480, and
-- you can't cross these bounds (although you will seldom have enough
-- on-chip memory to instantiate this module with higher res).
NUM_HORZ_PIXELS : natural := 128; -- Number of horizontal pixels
NUM_VERT_PIXELS : natural := 96); -- Number of vertical pixels
port (
clk50M, rstn : in std_logic;
write_clk, write_enable : in std_logic;
write_addr : in integer range 0 to
NUM_HORZ_PIXELS * NUM_VERT_PIXELS - 1;
data_in : in std_logic_vector(2 downto 0);
vga_clk : out std_logic; -- Ideally 25.175 MHz
red, green, blue : out std_logic_vector(7 downto 0);
hsync, vsync : out std_logic;
sync, blank : out std_logic);
end vgacon;
architecture behav of vgacon is
-- Two signals: one is delayed by one clock cycle. The monitor control uses
-- the delayed one. We need a counter 1 clock cycle earlier, relative
-- to the monitor signal, in order to index the memory contents
-- for the next cycle, when the pixel is in fact sent to the monitor.
signal h_count, h_count_d : integer range 0 to 799; -- horizontal counter
signal v_count, v_count_d : integer range 0 to 524; -- vertical counter
-- We only want to address HORZ*VERT pixels in memory
signal read_addr : integer range 0 to NUM_HORZ_PIXELS * NUM_VERT_PIXELS - 1;
signal h_drawarea, v_drawarea, drawarea : std_logic;
signal data_out : std_logic_vector(2 downto 0);
signal vga_clk_int : std_logic;
begin -- behav
sync <= '1';
blank <= '0';
-------------------------------------------------------------------------------
-- The following component is automatically generated by Quartus (a megafunction).
-- As Altera DE1 board does not have a 25.175 MHz, but a 27 Mhz, we
-- instantiate a PLL (Phase Locked Loop) to divide out 27 MHz clock
-- and reach a satisfiable 25.2MHz clock for our VGA controller (14/15 ratio)
------------------------------------------------------------------------------
divider: work.vga_pll port map (clk50M, NOT rstn, vga_clk_int);
vga_clk <= vga_clk_int;
-- This is our dual clock RAM. We use our VGA clock to read contents from
-- memory (pixel color value). The user of this module may use any clock
-- to write contents to this memory, modifying pixels individually.
vgamem : work.dual_clock_ram
generic map (
MEMSIZE => NUM_HORZ_PIXELS * NUM_VERT_PIXELS)
port map (
read_clk => vga_clk_int,
write_clk => write_clk,
read_address => read_addr,
write_address => write_addr,
data_in => data_in,
data_out => data_out,
we => write_enable);
-- purpose: Increments the current horizontal position counter
-- type : sequential
-- inputs : vga_clk_int, rstn
-- outputs: h_count, h_count_d
horz_counter: process (vga_clk_int, rstn)
begin -- process horz_counter
if rstn = '0' then -- asynchronous reset (active low)
h_count <= 0;
h_count_d <= 0;
elsif vga_clk_int'event and vga_clk_int = '1' then -- rising clock edge
h_count_d <= h_count; -- 1 clock cycle delayed counter
if h_count = 799 then
h_count <= 0;
else
h_count <= h_count + 1;
end if;
end if;
end process horz_counter;
-- purpose: Determines if we are in the horizontal "drawable" area
-- type : combinational
-- inputs : h_count_d
-- outputs: h_drawarea
horz_sync: process (h_count_d)
begin -- process horz_sync
if h_count_d < 640 then
h_drawarea <= '1';
else
h_drawarea <= '0';
end if;
end process horz_sync;
-- purpose: Increments the current vertical counter position
-- type : sequential
-- inputs : vga_clk_int, rstn
-- outputs: v_count, v_count_d
vert_counter: process (vga_clk_int, rstn)
begin -- process vert_counter
if rstn = '0' then -- asynchronous reset (active low)
v_count <= 0;
v_count_d <= 0;
elsif vga_clk_int'event and vga_clk_int = '1' then -- rising clock edge
v_count_d <= v_count; -- 1 clock cycle delayed counter
if h_count = 699 then
if v_count = 524 then
v_count <= 0;
else
v_count <= v_count + 1;
end if;
end if;
end if;
end process vert_counter;
-- purpose: Updates information based on vertical position
-- type : combinational
-- inputs : v_count_d
-- outputs: v_drawarea
vert_sync: process (v_count_d)
begin -- process vert_sync
if v_count_d < 480 then
v_drawarea <= '1';
else
v_drawarea <= '0';
end if;
end process vert_sync;
-- purpose: Generates synchronization signals
-- type : combinational
-- inputs : v_count_d, h_count_d
-- outputs: hsync, vsync
synchronization: process (v_count_d, h_count_d)
begin -- process sync
if (h_count_d >= 659) and (h_count_d <= 755) then
hsync <= '0';
else
hsync <= '1';
end if;
if (v_count_d >= 493) and (v_count_d <= 494) then
vsync <= '0';
else
vsync <= '1';
end if;
end process synchronization;
-- determines whether we are in drawable area on screen a.t.m.
drawarea <= v_drawarea and h_drawarea;
-- purpose: calculates the controller memory address to read pixel data
-- type : combinational
-- inputs : h_count, v_count
-- outputs: read_addr
gen_r_addr: process (h_count, v_count)
begin -- process gen_r_addr
read_addr <= h_count / (640 / NUM_HORZ_PIXELS)
+ ((v_count/(480 / NUM_VERT_PIXELS))
* NUM_HORZ_PIXELS);
end process gen_r_addr;
-- Build color signals based on memory output and "drawarea" signal
-- (if we are not in the drawable area of 640x480, must deassert all
-- color signals).
red <= (others => data_out(2) and drawarea);
green <= (others => data_out(1) and drawarea);
blue <= (others => data_out(0) and drawarea);
end behav;
-------------------------------------------------------------------------------
-- The following entity is a dual clock RAM (read operates at different
-- clock from write). This is used to isolate two clock domains. The first
-- is the 25.2 MHz clock domain in which our VGA controller needs to operate.
-- This is the read clock, because we read from this memory to determine
-- the color of a pixel. The second is the clock domain of the user of this
-- module, writing in the memory the contents it wants to display in the VGA.
-------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
entity dual_clock_ram is
generic (
MEMSIZE : natural);
port (
read_clk, write_clk : in std_logic; -- support different clocks
data_in : in std_logic_vector(2 downto 0);
write_address, read_address : in integer range 0 to MEMSIZE - 1;
we : in std_logic; -- write enable
data_out : out std_logic_vector(2 downto 0));
end dual_clock_ram;
architecture behav of dual_clock_ram is
-- we only want to address (store) MEMSIZE elements
subtype addr is integer range 0 to MEMSIZE - 1;
type mem is array (addr) of std_logic_vector(2 downto 0);
signal ram_block : mem;
-- we don't care with read after write behavior (whether ram reads
-- old or new data in the same cycle).
attribute ramstyle : string;
attribute ramstyle of dual_clock_ram : entity is "no_rw_check";
attribute ram_init_file : string;
attribute ram_init_file of ram_block : signal is "vga_mem.mif";
begin -- behav
-- purpose: Reads data from RAM
-- type : sequential
-- inputs : read_clk, read_address
-- outputs: data_out
read: process (read_clk)
begin -- process read
if read_clk'event and read_clk = '1' then -- rising clock edge
data_out <= ram_block(read_address);
end if;
end process read;
-- purpose: Writes data to RAM
-- type : sequential
-- inputs : write_clk, write_address
-- outputs: ram_block
write: process (write_clk)
begin -- process write
if write_clk'event and write_clk = '1' then -- rising clock edge
if we = '1' then
ram_block(write_address) <= data_in;
end if;
end if;
end process write;
end behav;