OSDN Git Service

- vram read bug fixed
[motonesfpga/motonesfpga.git] / simulation / ppu / ppu.vhd
1 library ieee;
2 use ieee.std_logic_1164.all;
3
4 entity ppu is 
5     port (  clk         : in std_logic;
6             ce_n        : in std_logic;
7             rst_n       : in std_logic;
8             r_nw        : 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;
12             rd_n        : out std_logic;
13             wr_n        : out std_logic;
14             ale         : 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)
23     );
24 end ppu;
25
26 architecture rtl of ppu is
27
28 component ppu_render
29     port (  clk         : in std_logic;
30             rst_n       : in std_logic;
31             rd_n        : out std_logic;
32             wr_n        : out std_logic;
33             ale         : out 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);
47             r_nw            : in std_logic;
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
53     );
54 end component;
55
56 component vga_ctl
57     port (  ppu_clk     : in std_logic;
58             vga_clk     : in std_logic;
59             rst_n       : 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)
70     );
71 end component;
72
73 component d_flip_flop
74     generic (
75             dsize : integer := 8
76             );
77     port (
78             clk     : in std_logic;
79             res_n   : in std_logic;
80             set_n   : in std_logic;
81             we_n    : in std_logic;
82             d       : in std_logic_vector (dsize - 1 downto 0);
83             q       : out std_logic_vector (dsize - 1 downto 0)
84         );
85 end component;
86
87 component counter_register
88     generic (
89         dsize       : integer := 8;
90         inc         : integer := 1
91     );
92     port (  clk         : in std_logic;
93             rst_n       : in std_logic;
94             ce_n        : in std_logic;
95             we_n        : in std_logic;
96             d           : in std_logic_vector(dsize - 1 downto 0);
97             q           : out std_logic_vector(dsize - 1 downto 0)
98     );
99 end component;
100
101 procedure d_print(msg : string) is
102 use std.textio.all;
103 use ieee.std_logic_textio.all;
104 variable out_l : line;
105 begin
106     write(out_l, msg);
107     writeline(output, out_l);
108 end  procedure;
109
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);
115
116 constant dsize     : integer := 8;
117
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";
126
127 constant PPUNEN     : integer := 7;  --nmi enable
128 constant ST_VBL     : integer := 7;  --vblank
129
130 signal clk_n            : std_logic;
131
132 signal ppu_clk_cnt_res_n    : std_logic;
133 signal ppu_clk_cnt          : std_logic_vector(1 downto 0);
134
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;
146
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;
165
166 signal oam_bus_ce_n     : std_logic;
167 signal plt_bus_ce_n     : std_logic;
168
169 signal oam_plt_addr     : std_logic_vector (7 downto 0);
170 signal oam_plt_data     : std_logic_vector (7 downto 0);
171
172 begin
173
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);
180
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);
184
185     --PPU registers.
186     clk_n <= not clk;
187
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); 
190
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);
193
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);
196
197     ppu_status_inst : d_flip_flop generic map(dsize)
198             port map (read_status, rst_n, '1', '0', ppu_status, ppu_stat_out);
199
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);
204
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);
212
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);
220
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);
223
224
225     reg_set_p : process (rst_n, ce_n, r_nw, cpu_addr, cpu_d, 
226                         ppu_status(ST_VBL), ppu_ctrl(PPUNEN))
227     begin
228         if (rst_n = '0') then
229             ppu_latch_rst_n <= '0';
230             vblank_n <= '1';
231         end if;
232
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
235                 --start vblank.
236                 vblank_n <= '0';
237             else
238                 --clear flag.
239                 vblank_n <= '1';
240             end if;
241         end if;
242
243         if (rst_n = '1' and ce_n = '0') then
244
245             --register set.
246             if(cpu_addr = PPUCTRL) then
247                 ppu_ctrl_we_n <= '0';
248             else
249                 ppu_ctrl_we_n <= '1';
250             end if;
251
252             if(cpu_addr = PPUMASK) then
253                 ppu_mask_we_n <= '0';
254             else
255                 ppu_mask_we_n <= '1';
256             end if;
257
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
262                 read_status <= '1';
263             else
264                 ppu_latch_rst_n <= '1';
265                 read_status <= '0';
266             end if;
267
268             if(cpu_addr = OAMADDR) then
269                 oam_addr_we_n <= '0';
270             else
271                 oam_addr_we_n <= '1';
272             end if;
273
274             if(cpu_addr = OAMDATA) then
275                 oam_data_we_n <= '0';
276             else
277                 oam_data_we_n <= '1';
278             end if;
279
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';
285                 else
286                     ppu_scroll_y_we_n <= '0';
287                     ppu_scroll_x_we_n <= '1';
288                 end if;
289             else
290                 ppu_scroll_x_we_n <= '1';
291                 ppu_scroll_y_we_n <= '1';
292                 ppu_scroll_cnt_ce_n <= '1';
293             end if;
294
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);
300                 else
301                     ppu_addr_in <= ppu_addr(13 downto 8) & cpu_d;
302                 end if;
303             else
304                 ppu_addr_cnt_ce_n <= '1';
305                 ppu_addr_we_n <= '1';
306             end if;
307
308             if (cpu_addr = PPUDATA and r_nw = '0') then
309                 read_data_n <= '0';
310             else
311                 read_data_n <= '1';
312             end if;
313         else
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';
323             read_status <= '0';
324             read_data_n <= '1';
325         end if; --if (rst_n = '1' and ce_n = '0') 
326
327     end process;
328
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)
332     begin
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");
338             end if;
339
340             --start counter.
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';
346                 end if;
347                 --d_print("clk event");
348             end if;
349
350             --oam data set
351             if (cpu_addr = OAMDATA and ppu_clk_cnt = "00") then
352                 oam_bus_ce_n <= '0';
353                 oam_plt_addr <= oam_addr;
354                 if (r_nw = '1') then
355                     oam_plt_data <= (others => 'Z');
356                     cpu_d <= oam_plt_data;
357                 else
358                     oam_plt_data <= cpu_d;
359                 end if;
360                 --address increment for burst write. 
361                 oam_addr_ce_n <= '0';
362             else
363                 cpu_d <= (others => 'Z');
364                 oam_addr_ce_n <= '1';
365                 oam_bus_ce_n <= '1';
366             end if;
367
368             --vram address access.
369             if (cpu_addr = PPUADDR and ppu_clk_cnt = "00") then
370                 if (ppu_addr_cnt(0) = '0') then
371                     --load addr high
372                     ale <= '0';
373                 else
374                     --load addr low and output vram/plt bus.
375
376                     --if address is 3fxx, set palette table.
377                     if (ppu_addr(13 downto 8) = "111111") then
378                         oam_plt_addr <= cpu_d;
379                         ale <= '0';
380                     else
381                         vram_ad <= cpu_d;
382                         vram_a <= ppu_addr(13 downto 8);
383                         ale <= '1';
384                     end if;
385                 end if;
386             elsif (cpu_addr = PPUDATA and ppu_clk_cnt = "01") then
387                 --for burst write.
388                 if (ppu_addr(13 downto 8) = "111111") then
389                     oam_plt_addr <= ppu_addr(7 downto 0);
390                     ale <= '0';
391                 else
392                     vram_a <= ppu_addr(13 downto 8);
393                     vram_ad <= ppu_addr(7 downto 0);
394                     ale <= '1';
395                 end if;
396             else
397                 ale <= '0';
398             end if;
399
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
404                     --case palette tbl.
405                     plt_bus_ce_n <= '0';
406                     if (r_nw = '0') then
407                         oam_plt_data <= cpu_d;
408                     else
409                         oam_plt_data <= (others => 'Z');
410                         cpu_d <= oam_plt_data;
411                     end if;
412                     rd_n <= '1';
413                     wr_n <= '1';
414                 else
415                     rd_n <= not r_nw;
416                     wr_n <= r_nw;
417                     plt_bus_ce_n <= '1';
418                     if (r_nw = '0') then
419                         vram_ad <= cpu_d;
420                     else
421                         vram_ad <= (others => 'Z');
422                         cpu_d <= vram_ad;
423                     end if;
424                 end if;
425             else
426                 plt_bus_ce_n <= '1';
427                 ppu_data_we_n <= '1';
428                 rd_n <= '1';
429                 wr_n <= '1';
430             end if;
431
432             --sustain cpu output data when reading.
433             if (cpu_addr = PPUDATA and r_nw = '1' and ppu_clk_cnt /= "00") then
434                 cpu_d <= ppu_data;
435             end if;
436             if (cpu_addr = OAMDATA and r_nw = '1' and ppu_clk_cnt /= "00") then
437                 cpu_d <= oam_data;
438             end if;
439
440             if(cpu_addr = PPUSTATUS and r_nw = '1') then
441                 cpu_d <= ppu_stat_out;
442             end if;
443
444         else
445             ppu_data_we_n    <= '1';
446             plt_bus_ce_n <= '1';
447             ppu_clk_cnt_res_n <= '0';
448             oam_bus_ce_n     <= '1';
449             oam_addr_ce_n <= '1';
450
451             rd_n <= 'Z';
452             wr_n <= 'Z';
453             ale <= 'Z';
454             oam_plt_data <= (others => 'Z');
455             vram_ad <= (others => 'Z');
456             vram_a <= (others => 'Z');
457             cpu_d <= (others => 'Z');
458         end if;
459     end process;
460
461 end rtl;
462