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 PPUVAI : integer := 2; --vram address increment
128 constant PPUNEN : integer := 7; --nmi enable
129 constant ST_VBL : integer := 7; --vblank
131 signal clk_n : std_logic;
133 signal ppu_clk_cnt_res_n : std_logic;
134 signal ppu_clk_cnt : std_logic_vector(1 downto 0);
136 signal ppu_ctrl_we_n : std_logic;
137 signal ppu_mask_we_n : std_logic;
138 signal oam_addr_ce_n : std_logic;
139 signal oam_addr_we_n : std_logic;
140 signal oam_data_we_n : std_logic;
141 signal ppu_scroll_x_we_n : std_logic;
142 signal ppu_scroll_y_we_n : std_logic;
143 signal ppu_scroll_cnt_ce_n : std_logic;
144 signal ppu_addr_we_n : std_logic;
145 signal ppu_addr_cnt_ce_n : std_logic;
146 signal ppu_data_we_n : std_logic;
148 signal ppu_ctrl : std_logic_vector (dsize - 1 downto 0);
149 signal ppu_mask : std_logic_vector (dsize - 1 downto 0);
150 signal read_status : std_logic;
151 signal ppu_status : std_logic_vector (dsize - 1 downto 0);
152 signal ppu_stat_out : std_logic_vector (dsize - 1 downto 0);
153 signal oam_addr : std_logic_vector (dsize - 1 downto 0);
154 signal oam_data : std_logic_vector (dsize - 1 downto 0);
155 signal ppu_scroll_x : std_logic_vector (dsize - 1 downto 0);
156 signal ppu_scroll_y : std_logic_vector (dsize - 1 downto 0);
157 signal ppu_scroll_cnt : std_logic_vector (0 downto 0);
158 signal ppu_addr : std_logic_vector (13 downto 0);
159 signal ppu_addr_inc1 : std_logic_vector (13 downto 0);
160 signal ppu_addr_inc32 : std_logic_vector (13 downto 0);
161 signal ppu_addr_in : std_logic_vector (13 downto 0);
162 signal ppu_addr_cnt : std_logic_vector (0 downto 0);
163 signal ppu_data : std_logic_vector (dsize - 1 downto 0);
164 signal ppu_data_in : std_logic_vector (dsize - 1 downto 0);
165 signal ppu_data_out : std_logic_vector (dsize - 1 downto 0);
166 signal read_data_n : std_logic;
167 signal ppu_latch_rst_n : std_logic;
168 signal v_bus_busy_n : std_logic;
170 signal oam_bus_ce_n : std_logic;
171 signal plt_bus_ce_n : std_logic;
173 signal oam_plt_addr : std_logic_vector (dsize - 1 downto 0);
174 signal oam_plt_data : std_logic_vector (dsize - 1 downto 0);
175 signal plt_data_out : std_logic_vector (dsize - 1 downto 0);
179 render_inst : ppu_render port map (clk, rst_n,
180 rd_n, wr_n, ale, vram_ad, vram_a,
181 pos_x, pos_y, nes_r, nes_g, nes_b,
182 ppu_ctrl, ppu_mask, read_status, ppu_status, ppu_scroll_x, ppu_scroll_y,
183 r_nw, oam_bus_ce_n, plt_bus_ce_n,
184 oam_plt_addr, oam_plt_data, v_bus_busy_n);
186 vga_inst : vga_ctl port map (clk, vga_clk, rst_n,
187 pos_x, pos_y, nes_r, nes_g, nes_b,
188 h_sync_n, v_sync_n, r, g, b);
193 ppu_clk_cnt_inst : counter_register generic map (2, 1)
194 port map (clk_n, ppu_clk_cnt_res_n, '0', '1', (others => '0'), ppu_clk_cnt);
196 ppu_ctrl_inst : d_flip_flop generic map(dsize)
197 port map (clk_n, rst_n, '1', ppu_ctrl_we_n, cpu_d, ppu_ctrl);
199 ppu_mask_inst : d_flip_flop generic map(dsize)
200 port map (clk_n, rst_n, '1', ppu_mask_we_n, cpu_d, ppu_mask);
202 ppu_status_inst : d_flip_flop generic map(dsize)
203 port map (read_status, rst_n, '1', '0', ppu_status, ppu_stat_out);
205 oma_addr_inst : counter_register generic map(dsize, 1)
206 port map (clk_n, rst_n, oam_addr_ce_n, oam_addr_we_n, cpu_d, oam_addr);
207 oma_data_inst : d_flip_flop generic map(dsize)
208 port map (clk_n, rst_n, '1', oam_data_we_n, cpu_d, oam_data);
210 ppu_scroll_x_inst : d_flip_flop generic map(dsize)
211 port map (clk_n, rst_n, '1', ppu_scroll_x_we_n, cpu_d, ppu_scroll_x);
212 ppu_scroll_y_inst : d_flip_flop generic map(dsize)
213 port map (clk_n, rst_n, '1', ppu_scroll_y_we_n, cpu_d, ppu_scroll_y);
214 ppu_scroll_cnt_inst : counter_register generic map (1, 1)
215 port map (clk_n, ppu_latch_rst_n, ppu_scroll_cnt_ce_n,
216 '1', (others => '0'), ppu_scroll_cnt);
218 ppu_addr_inst_inc1 : counter_register generic map(14, 1)
219 port map (clk_n, rst_n, ppu_data_we_n, ppu_addr_we_n, ppu_addr_in, ppu_addr_inc1);
220 ppu_addr_inst_inc32 : counter_register generic map(14, 32)
221 port map (clk_n, rst_n, ppu_data_we_n, ppu_addr_we_n, ppu_addr_in, ppu_addr_inc32);
223 ppu_addr <= ppu_addr_inc32 when ppu_ctrl(PPUVAI) = '1' else
226 ppu_addr_cnt_inst : counter_register generic map (1, 1)
227 port map (clk_n, ppu_latch_rst_n, ppu_addr_cnt_ce_n,
228 '1', (others => '0'), ppu_addr_cnt);
229 ppu_data_inst : d_flip_flop generic map(dsize)
230 port map (clk_n, rst_n, '1', ppu_data_we_n, cpu_d, ppu_data);
232 ppu_data_in_inst : d_flip_flop generic map(dsize)
233 port map (clk_n, rst_n, '1', ppu_data_we_n, vram_ad, ppu_data_in);
235 ppu_data_out_inst : d_flip_flop generic map(dsize)
236 port map (read_data_n, rst_n, '1', '0', ppu_data_in, ppu_data_out);
238 plt_data_out_inst : d_flip_flop generic map(dsize)
239 port map (clk_n, rst_n, '1', ppu_data_we_n, oam_plt_data, plt_data_out);
241 reg_set_p : process (rst_n, ce_n, r_nw, cpu_addr, cpu_d,
242 ppu_status(ST_VBL), ppu_ctrl(PPUNEN))
245 if (ppu_status(ST_VBL)'event or ppu_ctrl(PPUNEN)'event) then
246 if (ppu_status(ST_VBL) = '1' and ppu_ctrl(PPUNEN) = '1') then
255 if (rst_n = '0') then
257 elsif (rst_n = '1' and ce_n = '0') then
260 if(cpu_addr = PPUCTRL) then
261 ppu_ctrl_we_n <= '0';
263 ppu_ctrl_we_n <= '1';
266 if(cpu_addr = PPUMASK) then
267 ppu_mask_we_n <= '0';
269 ppu_mask_we_n <= '1';
272 if(cpu_addr = PPUSTATUS and r_nw = '1') then
273 --notify reading status
279 if(cpu_addr = OAMADDR) then
280 oam_addr_we_n <= '0';
282 oam_addr_we_n <= '1';
285 if(cpu_addr = OAMDATA) then
286 oam_data_we_n <= '0';
288 oam_data_we_n <= '1';
291 if(cpu_addr = PPUSCROLL) then
292 ppu_scroll_cnt_ce_n <= '0';
293 if (ppu_scroll_cnt(0) = '0') then
294 ppu_scroll_x_we_n <= '0';
295 ppu_scroll_y_we_n <= '1';
297 ppu_scroll_y_we_n <= '0';
298 ppu_scroll_x_we_n <= '1';
301 ppu_scroll_x_we_n <= '1';
302 ppu_scroll_y_we_n <= '1';
303 ppu_scroll_cnt_ce_n <= '1';
306 if(cpu_addr = PPUADDR) then
307 ppu_addr_we_n <= '0';
308 if (ppu_addr_cnt(0) = '0') then
309 ppu_addr_in <= cpu_d(5 downto 0) & ppu_addr(7 downto 0);
311 ppu_addr_in <= ppu_addr(13 downto 8) & cpu_d;
314 ppu_addr_we_n <= '1';
317 if (cpu_addr = PPUDATA and r_nw = '1') then
323 ppu_ctrl_we_n <= '1';
324 ppu_mask_we_n <= '1';
325 oam_addr_we_n <= '1';
326 oam_data_we_n <= '1';
327 ppu_scroll_x_we_n <= '1';
328 ppu_scroll_y_we_n <= '1';
329 ppu_scroll_cnt_ce_n <= '1';
330 ppu_addr_we_n <= '1';
333 end if; --if (rst_n = '1' and ce_n = '0')
337 --cpu and ppu clock timing adjustment...
338 clk_cnt_set_p : process (rst_n, ce_n, r_nw, cpu_addr, cpu_d, clk,
339 oam_plt_data, vram_ad, ppu_stat_out)
341 if (rst_n = '0') then
342 ppu_latch_rst_n <= '0';
343 elsif (rst_n = '1' and ce_n = '0') then
344 --set counter=0 on register write.
345 if (ce_n'event or r_nw'event or cpu_addr'event or (cpu_d'event and r_nw = '0')) then
346 ppu_clk_cnt_res_n <= '0';
347 --d_print("write event");
351 if (clk'event and clk = '0') then
352 if (ppu_clk_cnt = "10") then
353 ppu_clk_cnt_res_n <= '0';
354 elsif (ppu_clk_cnt = "00") then
355 ppu_clk_cnt_res_n <= '1';
358 if (read_status = '1') then
359 --reading status resets ppu_addr/scroll cnt.
360 ppu_latch_rst_n <= '0';
362 ppu_latch_rst_n <= '1';
364 --d_print("clk event");
368 if (cpu_addr = OAMDATA and ppu_clk_cnt = "00") then
370 oam_plt_addr <= oam_addr;
372 oam_plt_data <= (others => 'Z');
373 cpu_d <= oam_plt_data;
375 oam_plt_data <= cpu_d;
377 --address increment for burst write.
378 oam_addr_ce_n <= '0';
380 cpu_d <= (others => 'Z');
381 oam_addr_ce_n <= '1';
383 end if; --if (cpu_addr = OAMDATA and ppu_clk_cnt = "00") then
385 --vram address access.
386 if (cpu_addr = PPUADDR and ppu_clk_cnt = "00") then
387 ppu_addr_cnt_ce_n <= '0';
388 if (ppu_addr_cnt(0) = '0') then
392 --load addr low and output vram/plt bus.
394 --if address is 3fxx, set palette table.
395 if (ppu_addr(13 downto 8) = "111111") then
396 oam_plt_addr <= cpu_d;
400 vram_a <= ppu_addr(13 downto 8);
404 elsif (cpu_addr = PPUDATA and ppu_clk_cnt = "01") then
405 ppu_addr_cnt_ce_n <= '1';
407 if (ppu_addr(13 downto 8) = "111111") then
408 oam_plt_addr <= ppu_addr(7 downto 0);
411 vram_a <= ppu_addr(13 downto 8);
412 vram_ad <= ppu_addr(7 downto 0);
416 ppu_addr_cnt_ce_n <= '1';
418 end if; --if (cpu_addr = PPUADDR and ppu_clk_cnt = "00") then
420 if (cpu_addr = PPUDATA and ppu_clk_cnt = "00") then
421 ppu_data_we_n <= '0';
422 vram_a <= ppu_addr(13 downto 8);
423 if (ppu_addr(13 downto 8) = "111111") then
427 oam_plt_data <= cpu_d;
429 oam_plt_data <= (others => 'Z');
430 cpu_d <= oam_plt_data;
441 cpu_d <= ppu_data_out;
446 ppu_data_we_n <= '1';
449 end if; --if (cpu_addr = PPUDATA and ppu_clk_cnt = "00") then
451 --sustain cpu output data when reading.
452 if (cpu_addr = PPUDATA and r_nw = '1' and ppu_clk_cnt /= "00") then
453 if (ppu_addr(13 downto 8) = "111111") then
454 cpu_d <= plt_data_out;
456 cpu_d <= ppu_data_out;
459 if (cpu_addr = OAMDATA and r_nw = '1' and ppu_clk_cnt /= "00") then
463 if(cpu_addr = PPUSTATUS and r_nw = '1') then
464 cpu_d <= ppu_stat_out;
468 ppu_data_we_n <= '1';
470 ppu_clk_cnt_res_n <= '0';
472 oam_addr_ce_n <= '1';
473 ppu_addr_cnt_ce_n <= '1';
474 ppu_latch_rst_n <= '1';
479 oam_plt_data <= (others => 'Z');
480 vram_ad <= (others => 'Z');
481 vram_a <= (others => 'Z');
482 cpu_d <= (others => 'Z');
483 end if; --if (rst_n = '0') then