2 use ieee.std_logic_1164.all;
5 port ( clk : in std_logic;
9 cpu_addr : in std_logic_vector (2 downto 0);
10 cpu_d : inout std_logic_vector (7 downto 0);
11 vblank_n : out std_logic;
15 vram_ad : inout std_logic_vector (7 downto 0);
16 vram_a : out std_logic_vector (13 downto 8);
17 vga_clk : in std_logic;
18 h_sync_n : out std_logic;
19 v_sync_n : out std_logic;
20 r : out std_logic_vector(3 downto 0);
21 g : out std_logic_vector(3 downto 0);
22 b : out std_logic_vector(3 downto 0)
26 architecture rtl of ppu is
29 port ( clk : in std_logic;
34 vram_ad : inout std_logic_vector (7 downto 0);
35 vram_a : out std_logic_vector (13 downto 8);
36 pos_x : out std_logic_vector (8 downto 0);
37 pos_y : out std_logic_vector (8 downto 0);
38 r : out std_logic_vector (3 downto 0);
39 g : out std_logic_vector (3 downto 0);
40 b : out std_logic_vector (3 downto 0);
41 ppu_ctrl : in std_logic_vector (7 downto 0);
42 ppu_mask : in std_logic_vector (7 downto 0);
43 read_status : in std_logic;
44 ppu_status : out std_logic_vector (7 downto 0);
45 ppu_scroll_x : in std_logic_vector (7 downto 0);
46 ppu_scroll_y : in std_logic_vector (7 downto 0);
48 oam_bus_ce_n : in std_logic;
49 plt_bus_ce_n : in std_logic;
50 oam_plt_addr : in std_logic_vector (7 downto 0);
51 oam_plt_data : inout std_logic_vector (7 downto 0);
52 v_bus_busy_n : out std_logic
57 port ( ppu_clk : in std_logic;
58 vga_clk : in std_logic;
60 pos_x : in std_logic_vector (8 downto 0);
61 pos_y : in std_logic_vector (8 downto 0);
62 nes_r : in std_logic_vector (3 downto 0);
63 nes_g : in std_logic_vector (3 downto 0);
64 nes_b : in std_logic_vector (3 downto 0);
65 h_sync_n : out std_logic;
66 v_sync_n : out std_logic;
67 r : out std_logic_vector(3 downto 0);
68 g : out std_logic_vector(3 downto 0);
69 b : out std_logic_vector(3 downto 0)
82 d : in std_logic_vector (dsize - 1 downto 0);
83 q : out std_logic_vector (dsize - 1 downto 0)
87 component counter_register
92 port ( clk : in std_logic;
96 d : in std_logic_vector(dsize - 1 downto 0);
97 q : out std_logic_vector(dsize - 1 downto 0)
101 procedure d_print(msg : string) is
103 use ieee.std_logic_textio.all;
104 variable out_l : line;
107 writeline(output, out_l);
110 signal pos_x : std_logic_vector (8 downto 0);
111 signal pos_y : std_logic_vector (8 downto 0);
112 signal nes_r : std_logic_vector (3 downto 0);
113 signal nes_g : std_logic_vector (3 downto 0);
114 signal nes_b : std_logic_vector (3 downto 0);
116 constant dsize : integer := 8;
118 constant PPUCTRL : std_logic_vector(2 downto 0) := "000";
119 constant PPUMASK : std_logic_vector(2 downto 0) := "001";
120 constant PPUSTATUS : std_logic_vector(2 downto 0) := "010";
121 constant OAMADDR : std_logic_vector(2 downto 0) := "011";
122 constant OAMDATA : std_logic_vector(2 downto 0) := "100";
123 constant PPUSCROLL : std_logic_vector(2 downto 0) := "101";
124 constant PPUADDR : std_logic_vector(2 downto 0) := "110";
125 constant PPUDATA : std_logic_vector(2 downto 0) := "111";
127 constant PPUNEN : integer := 7; --nmi enable
128 constant ST_VBL : integer := 7; --vblank
130 signal clk_n : std_logic;
132 signal ppu_clk_cnt_res_n : std_logic;
133 signal ppu_clk_cnt : std_logic_vector(1 downto 0);
135 signal ppu_ctrl_we_n : std_logic;
136 signal ppu_mask_we_n : std_logic;
137 signal oam_addr_ce_n : std_logic;
138 signal oam_addr_we_n : std_logic;
139 signal oam_data_we_n : std_logic;
140 signal ppu_scroll_x_we_n : std_logic;
141 signal ppu_scroll_y_we_n : std_logic;
142 signal ppu_scroll_cnt_ce_n : std_logic;
143 signal ppu_addr_we_n : std_logic;
144 signal ppu_addr_cnt_ce_n : std_logic;
145 signal ppu_data_we_n : std_logic;
147 signal ppu_ctrl : std_logic_vector (dsize - 1 downto 0);
148 signal ppu_mask : std_logic_vector (dsize - 1 downto 0);
149 signal read_status : std_logic;
150 signal ppu_status : std_logic_vector (dsize - 1 downto 0);
151 signal ppu_stat_out : std_logic_vector (dsize - 1 downto 0);
152 signal oam_addr : std_logic_vector (dsize - 1 downto 0);
153 signal oam_data : std_logic_vector (dsize - 1 downto 0);
154 signal ppu_scroll_x : std_logic_vector (dsize - 1 downto 0);
155 signal ppu_scroll_y : std_logic_vector (dsize - 1 downto 0);
156 signal ppu_scroll_cnt : std_logic_vector (0 downto 0);
157 signal ppu_addr : std_logic_vector (13 downto 0);
158 signal ppu_addr_in : std_logic_vector (13 downto 0);
159 signal ppu_addr_cnt : std_logic_vector (0 downto 0);
160 signal ppu_data : std_logic_vector (dsize - 1 downto 0);
161 signal ppu_data_out : std_logic_vector (dsize - 1 downto 0);
162 signal read_data_n : std_logic;
163 signal ppu_latch_rst_n : std_logic;
164 signal v_bus_busy_n : std_logic;
166 signal oam_bus_ce_n : std_logic;
167 signal plt_bus_ce_n : std_logic;
169 signal oam_plt_addr : std_logic_vector (7 downto 0);
170 signal oam_plt_data : std_logic_vector (7 downto 0);
174 render_inst : ppu_render port map (clk, rst_n,
175 rd_n, wr_n, ale, vram_ad, vram_a,
176 pos_x, pos_y, nes_r, nes_g, nes_b,
177 ppu_ctrl, ppu_mask, read_status, ppu_status, ppu_scroll_x, ppu_scroll_y,
178 r_nw, oam_bus_ce_n, plt_bus_ce_n,
179 oam_plt_addr, oam_plt_data, v_bus_busy_n);
181 vga_inst : vga_ctl port map (clk, vga_clk, rst_n,
182 pos_x, pos_y, nes_r, nes_g, nes_b,
183 h_sync_n, v_sync_n, r, g, b);
188 ppu_clk_cnt_inst : counter_register generic map (2, 1)
189 port map (clk_n, ppu_clk_cnt_res_n, '0', '1', (others => '0'), ppu_clk_cnt);
191 ppu_ctrl_inst : d_flip_flop generic map(dsize)
192 port map (clk_n, rst_n, '1', ppu_ctrl_we_n, cpu_d, ppu_ctrl);
194 ppu_mask_inst : d_flip_flop generic map(dsize)
195 port map (clk_n, rst_n, '1', ppu_mask_we_n, cpu_d, ppu_mask);
197 ppu_status_inst : d_flip_flop generic map(dsize)
198 port map (read_status, rst_n, '1', '0', ppu_status, ppu_stat_out);
200 oma_addr_inst : counter_register generic map(dsize, 1)
201 port map (clk_n, rst_n, oam_addr_ce_n, oam_addr_we_n, cpu_d, oam_addr);
202 oma_data_inst : d_flip_flop generic map(dsize)
203 port map (clk_n, rst_n, '1', oam_data_we_n, cpu_d, oam_data);
205 ppu_scroll_x_inst : d_flip_flop generic map(dsize)
206 port map (clk_n, rst_n, '1', ppu_scroll_x_we_n, cpu_d, ppu_scroll_x);
207 ppu_scroll_y_inst : d_flip_flop generic map(dsize)
208 port map (clk_n, rst_n, '1', ppu_scroll_y_we_n, cpu_d, ppu_scroll_y);
209 ppu_scroll_cnt_inst : counter_register generic map (1, 1)
210 port map (clk_n, ppu_latch_rst_n, ppu_scroll_cnt_ce_n,
211 '1', (others => '0'), ppu_scroll_cnt);
213 ppu_addr_inst : counter_register generic map(14, 1)
214 port map (clk_n, rst_n, ppu_data_we_n, ppu_addr_we_n, ppu_addr_in, ppu_addr);
215 ppu_addr_cnt_inst : counter_register generic map (1, 1)
216 port map (clk_n, ppu_latch_rst_n, ppu_addr_cnt_ce_n,
217 '1', (others => '0'), ppu_addr_cnt);
218 ppu_data_inst : d_flip_flop generic map(dsize)
219 port map (clk_n, rst_n, '1', ppu_data_we_n, cpu_d, ppu_data);
221 ppu_data_out_inst : d_flip_flop generic map(dsize)
222 port map (clk_n, rst_n, '1', read_data_n, ppu_data, ppu_data_out);
225 reg_set_p : process (rst_n, ce_n, r_nw, cpu_addr, cpu_d,
226 ppu_status(ST_VBL), ppu_ctrl(PPUNEN))
228 if (rst_n = '0') then
229 ppu_latch_rst_n <= '0';
233 if (ppu_status(ST_VBL)'event or ppu_ctrl(PPUNEN)'event) then
234 if (ppu_status(ST_VBL) = '1' and ppu_ctrl(PPUNEN) = '1') then
243 if (rst_n = '1' and ce_n = '0') then
246 if(cpu_addr = PPUCTRL) then
247 ppu_ctrl_we_n <= '0';
249 ppu_ctrl_we_n <= '1';
252 if(cpu_addr = PPUMASK) then
253 ppu_mask_we_n <= '0';
255 ppu_mask_we_n <= '1';
258 if(cpu_addr = PPUSTATUS and r_nw = '1') then
259 --reading status resets ppu_addr/scroll cnt.
260 ppu_latch_rst_n <= '0';
261 --notify reading status
264 ppu_latch_rst_n <= '1';
268 if(cpu_addr = OAMADDR) then
269 oam_addr_we_n <= '0';
271 oam_addr_we_n <= '1';
274 if(cpu_addr = OAMDATA) then
275 oam_data_we_n <= '0';
277 oam_data_we_n <= '1';
280 if(cpu_addr = PPUSCROLL) then
281 ppu_scroll_cnt_ce_n <= '0';
282 if (ppu_scroll_cnt(0) = '0') then
283 ppu_scroll_x_we_n <= '0';
284 ppu_scroll_y_we_n <= '1';
286 ppu_scroll_y_we_n <= '0';
287 ppu_scroll_x_we_n <= '1';
290 ppu_scroll_x_we_n <= '1';
291 ppu_scroll_y_we_n <= '1';
292 ppu_scroll_cnt_ce_n <= '1';
295 if(cpu_addr = PPUADDR) then
296 ppu_addr_cnt_ce_n <= '0';
297 ppu_addr_we_n <= '0';
298 if (ppu_addr_cnt(0) = '0') then
299 ppu_addr_in <= cpu_d(5 downto 0) & ppu_addr(7 downto 0);
301 ppu_addr_in <= ppu_addr(13 downto 8) & cpu_d;
304 ppu_addr_cnt_ce_n <= '1';
305 ppu_addr_we_n <= '1';
308 if (cpu_addr = PPUDATA and r_nw = '0') then
314 ppu_ctrl_we_n <= '1';
315 ppu_mask_we_n <= '1';
316 oam_addr_we_n <= '1';
317 oam_data_we_n <= '1';
318 ppu_scroll_x_we_n <= '1';
319 ppu_scroll_y_we_n <= '1';
320 ppu_scroll_cnt_ce_n <= '1';
321 ppu_addr_we_n <= '1';
322 ppu_addr_cnt_ce_n <= '1';
325 end if; --if (rst_n = '1' and ce_n = '0')
329 --cpu and ppu clock timing adjustment...
330 clk_cnt_set_p : process (rst_n, ce_n, r_nw, cpu_addr, cpu_d, clk,
331 oam_plt_data, vram_ad, ppu_stat_out)
333 if (rst_n = '1' and ce_n = '0') then
334 --set counter=0 on register write.
335 if (ce_n'event or r_nw'event or cpu_addr'event or (cpu_d'event and r_nw = '0')) then
336 ppu_clk_cnt_res_n <= '0';
337 --d_print("write event");
341 if (clk'event and clk = '0') then
342 if (ppu_clk_cnt = "10") then
343 ppu_clk_cnt_res_n <= '0';
344 elsif (ppu_clk_cnt = "00") then
345 ppu_clk_cnt_res_n <= '1';
347 --d_print("clk event");
351 if (cpu_addr = OAMDATA and ppu_clk_cnt = "00") then
353 oam_plt_addr <= oam_addr;
355 oam_plt_data <= (others => 'Z');
356 cpu_d <= oam_plt_data;
358 oam_plt_data <= cpu_d;
360 --address increment for burst write.
361 oam_addr_ce_n <= '0';
363 cpu_d <= (others => 'Z');
364 oam_addr_ce_n <= '1';
368 --vram address access.
369 if (cpu_addr = PPUADDR and ppu_clk_cnt = "00") then
370 if (ppu_addr_cnt(0) = '0') then
374 --load addr low and output vram/plt bus.
376 --if address is 3fxx, set palette table.
377 if (ppu_addr(13 downto 8) = "111111") then
378 oam_plt_addr <= cpu_d;
382 vram_a <= ppu_addr(13 downto 8);
386 elsif (cpu_addr = PPUDATA and ppu_clk_cnt = "01") then
388 if (ppu_addr(13 downto 8) = "111111") then
389 oam_plt_addr <= ppu_addr(7 downto 0);
392 vram_a <= ppu_addr(13 downto 8);
393 vram_ad <= ppu_addr(7 downto 0);
400 if (cpu_addr = PPUDATA and ppu_clk_cnt = "00") then
401 ppu_data_we_n <= '0';
402 vram_a <= ppu_addr(13 downto 8);
403 if (ppu_addr(13 downto 8) = "111111") then
407 oam_plt_data <= cpu_d;
409 oam_plt_data <= (others => 'Z');
410 cpu_d <= oam_plt_data;
421 vram_ad <= (others => 'Z');
427 ppu_data_we_n <= '1';
432 --sustain cpu output data when reading.
433 if (cpu_addr = PPUDATA and r_nw = '1' and ppu_clk_cnt /= "00") then
436 if (cpu_addr = OAMDATA and r_nw = '1' and ppu_clk_cnt /= "00") then
440 if(cpu_addr = PPUSTATUS and r_nw = '1') then
441 cpu_d <= ppu_stat_out;
445 ppu_data_we_n <= '1';
447 ppu_clk_cnt_res_n <= '0';
449 oam_addr_ce_n <= '1';
454 oam_plt_data <= (others => 'Z');
455 vram_ad <= (others => 'Z');
456 vram_a <= (others => 'Z');
457 cpu_d <= (others => 'Z');