component ppu_render
port ( clk : in std_logic;
rst_n : in std_logic;
- vblank_n : out std_logic;
rd_n : out std_logic;
wr_n : out std_logic;
ale : out std_logic;
vram_ad : inout std_logic_vector (7 downto 0);
vram_a : out std_logic_vector (13 downto 8);
- plt_bus_ce_n : in std_logic;
- plt_r_nw : in std_logic;
- plt_addr : in std_logic_vector (4 downto 0);
- plt_data : inout std_logic_vector (7 downto 0);
pos_x : out std_logic_vector (8 downto 0);
pos_y : out std_logic_vector (8 downto 0);
r : out std_logic_vector (3 downto 0);
g : out std_logic_vector (3 downto 0);
- b : out std_logic_vector (3 downto 0)
+ b : out std_logic_vector (3 downto 0);
+ ppu_ctrl : in std_logic_vector (7 downto 0);
+ ppu_mask : in std_logic_vector (7 downto 0);
+ read_status : in std_logic;
+ ppu_status : out std_logic_vector (7 downto 0);
+ ppu_scroll_x : in std_logic_vector (7 downto 0);
+ ppu_scroll_y : in std_logic_vector (7 downto 0);
+ r_nw : in std_logic;
+ oam_bus_ce_n : in std_logic;
+ plt_bus_ce_n : in std_logic;
+ oam_plt_addr : in std_logic_vector (7 downto 0);
+ oam_plt_data : inout std_logic_vector (7 downto 0);
+ v_bus_busy_n : out std_logic
);
end component;
component vga_ctl
- port ( vga_clk : in std_logic;
+ port ( ppu_clk : in std_logic;
+ vga_clk : in std_logic;
rst_n : in std_logic;
pos_x : in std_logic_vector (8 downto 0);
pos_y : in std_logic_vector (8 downto 0);
);
end component;
+component d_flip_flop
+ generic (
+ dsize : integer := 8
+ );
+ port (
+ clk : in std_logic;
+ res_n : in std_logic;
+ set_n : in std_logic;
+ we_n : in std_logic;
+ d : in std_logic_vector (dsize - 1 downto 0);
+ q : out std_logic_vector (dsize - 1 downto 0)
+ );
+end component;
+
+component counter_register
+ generic (
+ dsize : integer := 8;
+ inc : integer := 1
+ );
+ port ( clk : in std_logic;
+ rst_n : in std_logic;
+ ce_n : in std_logic;
+ we_n : in std_logic;
+ d : in std_logic_vector(dsize - 1 downto 0);
+ q : out std_logic_vector(dsize - 1 downto 0)
+ );
+end component;
+
+procedure d_print(msg : string) is
+use std.textio.all;
+use ieee.std_logic_textio.all;
+variable out_l : line;
+begin
+ write(out_l, msg);
+ writeline(output, out_l);
+end procedure;
+
signal pos_x : std_logic_vector (8 downto 0);
signal pos_y : std_logic_vector (8 downto 0);
signal nes_r : std_logic_vector (3 downto 0);
signal nes_g : std_logic_vector (3 downto 0);
signal nes_b : std_logic_vector (3 downto 0);
-signal plt_bus_ce_n : std_logic;
-signal plt_r_nw : std_logic;
-signal plt_addr : std_logic_vector (4 downto 0);
-signal plt_data : std_logic_vector (7 downto 0);
+constant dsize : integer := 8;
-begin
+constant PPUCTRL : std_logic_vector(2 downto 0) := "000";
+constant PPUMASK : std_logic_vector(2 downto 0) := "001";
+constant PPUSTATUS : std_logic_vector(2 downto 0) := "010";
+constant OAMADDR : std_logic_vector(2 downto 0) := "011";
+constant OAMDATA : std_logic_vector(2 downto 0) := "100";
+constant PPUSCROLL : std_logic_vector(2 downto 0) := "101";
+constant PPUADDR : std_logic_vector(2 downto 0) := "110";
+constant PPUDATA : std_logic_vector(2 downto 0) := "111";
+
+constant PPUVAI : integer := 2; --vram address increment
+constant PPUNEN : integer := 7; --nmi enable
+constant ST_VBL : integer := 7; --vblank
+signal clk_n : std_logic;
- ---TODO: for the time being...
- plt_bus_ce_n <= 'Z';
- plt_r_nw <= 'Z';
- plt_addr <= (others => 'Z');
- plt_data <= (others => 'Z');
+signal ppu_clk_cnt_res_n : std_logic;
+signal ppu_clk_cnt : std_logic_vector(1 downto 0);
- render_inst : ppu_render port map (clk, rst_n, vblank_n,
+signal ppu_ctrl_we_n : std_logic;
+signal ppu_mask_we_n : std_logic;
+signal oam_addr_ce_n : std_logic;
+signal oam_addr_we_n : std_logic;
+signal oam_data_we_n : std_logic;
+signal ppu_scroll_x_we_n : std_logic;
+signal ppu_scroll_y_we_n : std_logic;
+signal ppu_scroll_cnt_ce_n : std_logic;
+signal ppu_addr_we_n : std_logic;
+signal ppu_addr_cnt_ce_n : std_logic;
+signal ppu_data_we_n : std_logic;
+
+signal ppu_ctrl : std_logic_vector (dsize - 1 downto 0);
+signal ppu_mask : std_logic_vector (dsize - 1 downto 0);
+signal read_status : std_logic;
+signal ppu_status : std_logic_vector (dsize - 1 downto 0);
+signal ppu_stat_out : std_logic_vector (dsize - 1 downto 0);
+signal oam_addr : std_logic_vector (dsize - 1 downto 0);
+signal oam_data : std_logic_vector (dsize - 1 downto 0);
+signal ppu_scroll_x : std_logic_vector (dsize - 1 downto 0);
+signal ppu_scroll_y : std_logic_vector (dsize - 1 downto 0);
+signal ppu_scroll_cnt : std_logic_vector (0 downto 0);
+signal ppu_addr : std_logic_vector (13 downto 0);
+signal ppu_addr_inc1 : std_logic_vector (13 downto 0);
+signal ppu_addr_inc32 : std_logic_vector (13 downto 0);
+signal ppu_addr_in : std_logic_vector (13 downto 0);
+signal ppu_addr_cnt : std_logic_vector (0 downto 0);
+signal ppu_data : std_logic_vector (dsize - 1 downto 0);
+signal ppu_data_in : std_logic_vector (dsize - 1 downto 0);
+signal ppu_data_out : std_logic_vector (dsize - 1 downto 0);
+signal read_data_n : std_logic;
+signal ppu_latch_rst_n : std_logic;
+signal v_bus_busy_n : std_logic;
+
+signal oam_bus_ce_n : std_logic;
+signal plt_bus_ce_n : std_logic;
+
+signal oam_plt_addr : std_logic_vector (dsize - 1 downto 0);
+signal oam_plt_data : std_logic_vector (dsize - 1 downto 0);
+signal plt_data_out : std_logic_vector (dsize - 1 downto 0);
+
+begin
+
+ render_inst : ppu_render port map (clk, rst_n,
rd_n, wr_n, ale, vram_ad, vram_a,
- plt_bus_ce_n, plt_r_nw, plt_addr, plt_data,
- pos_x, pos_y, nes_r, nes_g, nes_b);
+ pos_x, pos_y, nes_r, nes_g, nes_b,
+ ppu_ctrl, ppu_mask, read_status, ppu_status, ppu_scroll_x, ppu_scroll_y,
+ r_nw, oam_bus_ce_n, plt_bus_ce_n,
+ oam_plt_addr, oam_plt_data, v_bus_busy_n);
- vga_inst : vga_ctl port map (vga_clk, rst_n,
+ vga_inst : vga_ctl port map (clk, vga_clk, rst_n,
pos_x, pos_y, nes_r, nes_g, nes_b,
h_sync_n, v_sync_n, r, g, b);
+
+ --PPU registers.
+ clk_n <= not clk;
+
+ ppu_clk_cnt_inst : counter_register generic map (2, 1)
+ port map (clk_n, ppu_clk_cnt_res_n, '0', '1', (others => '0'), ppu_clk_cnt);
+
+ ppu_ctrl_inst : d_flip_flop generic map(dsize)
+ port map (clk_n, rst_n, '1', ppu_ctrl_we_n, cpu_d, ppu_ctrl);
+
+ ppu_mask_inst : d_flip_flop generic map(dsize)
+ port map (clk_n, rst_n, '1', ppu_mask_we_n, cpu_d, ppu_mask);
+
+ ppu_status_inst : d_flip_flop generic map(dsize)
+ port map (read_status, rst_n, '1', '0', ppu_status, ppu_stat_out);
+
+ oma_addr_inst : counter_register generic map(dsize, 1)
+ port map (clk_n, rst_n, oam_addr_ce_n, oam_addr_we_n, cpu_d, oam_addr);
+ oma_data_inst : d_flip_flop generic map(dsize)
+ port map (clk_n, rst_n, '1', oam_data_we_n, cpu_d, oam_data);
+
+ ppu_scroll_x_inst : d_flip_flop generic map(dsize)
+ port map (clk_n, rst_n, '1', ppu_scroll_x_we_n, cpu_d, ppu_scroll_x);
+ ppu_scroll_y_inst : d_flip_flop generic map(dsize)
+ port map (clk_n, rst_n, '1', ppu_scroll_y_we_n, cpu_d, ppu_scroll_y);
+ ppu_scroll_cnt_inst : counter_register generic map (1, 1)
+ port map (clk_n, ppu_latch_rst_n, ppu_scroll_cnt_ce_n,
+ '1', (others => '0'), ppu_scroll_cnt);
+
+ ppu_addr_inst_inc1 : counter_register generic map(14, 1)
+ port map (clk_n, rst_n, ppu_data_we_n, ppu_addr_we_n, ppu_addr_in, ppu_addr_inc1);
+ ppu_addr_inst_inc32 : counter_register generic map(14, 32)
+ port map (clk_n, rst_n, ppu_data_we_n, ppu_addr_we_n, ppu_addr_in, ppu_addr_inc32);
+
+ ppu_addr <= ppu_addr_inc32 when ppu_ctrl(PPUVAI) = '1' else
+ ppu_addr_inc1;
+
+ ppu_addr_cnt_inst : counter_register generic map (1, 1)
+ port map (clk_n, ppu_latch_rst_n, ppu_addr_cnt_ce_n,
+ '1', (others => '0'), ppu_addr_cnt);
+ ppu_data_inst : d_flip_flop generic map(dsize)
+ port map (clk_n, rst_n, '1', ppu_data_we_n, cpu_d, ppu_data);
+
+ ppu_data_in_inst : d_flip_flop generic map(dsize)
+ port map (clk_n, rst_n, '1', ppu_data_we_n, vram_ad, ppu_data_in);
+
+ ppu_data_out_inst : d_flip_flop generic map(dsize)
+ port map (read_data_n, rst_n, '1', '0', ppu_data_in, ppu_data_out);
+
+ plt_data_out_inst : d_flip_flop generic map(dsize)
+ port map (clk_n, rst_n, '1', ppu_data_we_n, oam_plt_data, plt_data_out);
+
+ reg_set_p : process (rst_n, ce_n, r_nw, cpu_addr, cpu_d,
+ ppu_status(ST_VBL), ppu_ctrl(PPUNEN))
+ begin
+
+ if (ppu_status(ST_VBL)'event or ppu_ctrl(PPUNEN)'event) then
+ if (ppu_status(ST_VBL) = '1' and ppu_ctrl(PPUNEN) = '1') then
+ --start vblank.
+ vblank_n <= '0';
+ else
+ --clear flag.
+ vblank_n <= '1';
+ end if;
+ end if;
+
+ if (rst_n = '0') then
+ vblank_n <= '1';
+ elsif (rst_n = '1' and ce_n = '0') then
+
+ --register set.
+ if(cpu_addr = PPUCTRL) then
+ ppu_ctrl_we_n <= '0';
+ else
+ ppu_ctrl_we_n <= '1';
+ end if;
+
+ if(cpu_addr = PPUMASK) then
+ ppu_mask_we_n <= '0';
+ else
+ ppu_mask_we_n <= '1';
+ end if;
+
+ if(cpu_addr = PPUSTATUS and r_nw = '1') then
+ --notify reading status
+ read_status <= '1';
+ else
+ read_status <= '0';
+ end if;
+
+ if(cpu_addr = OAMADDR) then
+ oam_addr_we_n <= '0';
+ else
+ oam_addr_we_n <= '1';
+ end if;
+
+ if(cpu_addr = OAMDATA) then
+ oam_data_we_n <= '0';
+ else
+ oam_data_we_n <= '1';
+ end if;
+
+ if(cpu_addr = PPUSCROLL) then
+ ppu_scroll_cnt_ce_n <= '0';
+ if (ppu_scroll_cnt(0) = '0') then
+ ppu_scroll_x_we_n <= '0';
+ ppu_scroll_y_we_n <= '1';
+ else
+ ppu_scroll_y_we_n <= '0';
+ ppu_scroll_x_we_n <= '1';
+ end if;
+ else
+ ppu_scroll_x_we_n <= '1';
+ ppu_scroll_y_we_n <= '1';
+ ppu_scroll_cnt_ce_n <= '1';
+ end if;
+
+ if(cpu_addr = PPUADDR) then
+ ppu_addr_we_n <= '0';
+ if (ppu_addr_cnt(0) = '0') then
+ ppu_addr_in <= cpu_d(5 downto 0) & ppu_addr(7 downto 0);
+ else
+ ppu_addr_in <= ppu_addr(13 downto 8) & cpu_d;
+ end if;
+ else
+ ppu_addr_we_n <= '1';
+ end if;
+
+ if (cpu_addr = PPUDATA and r_nw = '1') then
+ read_data_n <= '0';
+ else
+ read_data_n <= '1';
+ end if;
+ else
+ ppu_ctrl_we_n <= '1';
+ ppu_mask_we_n <= '1';
+ oam_addr_we_n <= '1';
+ oam_data_we_n <= '1';
+ ppu_scroll_x_we_n <= '1';
+ ppu_scroll_y_we_n <= '1';
+ ppu_scroll_cnt_ce_n <= '1';
+ ppu_addr_we_n <= '1';
+ read_status <= '0';
+ read_data_n <= '1';
+ end if; --if (rst_n = '1' and ce_n = '0')
+
+ end process;
+
+ --cpu and ppu clock timing adjustment...
+ clk_cnt_set_p : process (rst_n, ce_n, r_nw, cpu_addr, cpu_d, clk,
+ oam_plt_data, vram_ad, ppu_stat_out)
+ begin
+ if (rst_n = '0') then
+ ppu_latch_rst_n <= '0';
+ elsif (rst_n = '1' and ce_n = '0') then
+ --set counter=0 on register write.
+ if (ce_n'event or r_nw'event or cpu_addr'event or (cpu_d'event and r_nw = '0')) then
+ ppu_clk_cnt_res_n <= '0';
+ --d_print("write event");
+ end if;
+
+ --start counter.
+ if (clk'event and clk = '0') then
+ if (ppu_clk_cnt = "10") then
+ ppu_clk_cnt_res_n <= '0';
+ elsif (ppu_clk_cnt = "00") then
+ ppu_clk_cnt_res_n <= '1';
+ end if;
+
+ if (read_status = '1') then
+ --reading status resets ppu_addr/scroll cnt.
+ ppu_latch_rst_n <= '0';
+ else
+ ppu_latch_rst_n <= '1';
+ end if;
+ --d_print("clk event");
+ end if;
+
+ --oam data set
+ if (cpu_addr = OAMDATA and ppu_clk_cnt = "00") then
+ oam_bus_ce_n <= '0';
+ oam_plt_addr <= oam_addr;
+ if (r_nw = '1') then
+ oam_plt_data <= (others => 'Z');
+ cpu_d <= oam_plt_data;
+ else
+ oam_plt_data <= cpu_d;
+ end if;
+ --address increment for burst write.
+ oam_addr_ce_n <= '0';
+ else
+ cpu_d <= (others => 'Z');
+ oam_addr_ce_n <= '1';
+ oam_bus_ce_n <= '1';
+ end if; --if (cpu_addr = OAMDATA and ppu_clk_cnt = "00") then
+
+ --vram address access.
+ if (cpu_addr = PPUADDR and ppu_clk_cnt = "00") then
+ ppu_addr_cnt_ce_n <= '0';
+ if (ppu_addr_cnt(0) = '0') then
+ --load addr high
+ ale <= '0';
+ else
+ --load addr low and output vram/plt bus.
+
+ --if address is 3fxx, set palette table.
+ if (ppu_addr(13 downto 8) = "111111") then
+ oam_plt_addr <= cpu_d;
+ ale <= '0';
+ else
+ vram_ad <= cpu_d;
+ vram_a <= ppu_addr(13 downto 8);
+ ale <= '1';
+ end if;
+ end if;
+ elsif (cpu_addr = PPUDATA and ppu_clk_cnt = "01") then
+ ppu_addr_cnt_ce_n <= '1';
+ --for burst write.
+ if (ppu_addr(13 downto 8) = "111111") then
+ oam_plt_addr <= ppu_addr(7 downto 0);
+ ale <= '0';
+ else
+ vram_a <= ppu_addr(13 downto 8);
+ vram_ad <= ppu_addr(7 downto 0);
+ ale <= '1';
+ end if;
+ else
+ ppu_addr_cnt_ce_n <= '1';
+ ale <= '0';
+ end if; --if (cpu_addr = PPUADDR and ppu_clk_cnt = "00") then
+
+ if (cpu_addr = PPUDATA and ppu_clk_cnt = "00") then
+ ppu_data_we_n <= '0';
+ vram_a <= ppu_addr(13 downto 8);
+ if (ppu_addr(13 downto 8) = "111111") then
+ --case palette tbl.
+ plt_bus_ce_n <= '0';
+ if (r_nw = '0') then
+ oam_plt_data <= cpu_d;
+ else
+ oam_plt_data <= (others => 'Z');
+ cpu_d <= oam_plt_data;
+ end if;
+ rd_n <= '1';
+ wr_n <= '1';
+ else
+ rd_n <= not r_nw;
+ wr_n <= r_nw;
+ plt_bus_ce_n <= '1';
+ if (r_nw = '0') then
+ vram_ad <= cpu_d;
+ else
+ cpu_d <= ppu_data_out;
+ end if;
+ end if;
+ else
+ plt_bus_ce_n <= '1';
+ ppu_data_we_n <= '1';
+ rd_n <= 'Z';
+ wr_n <= 'Z';
+ end if; --if (cpu_addr = PPUDATA and ppu_clk_cnt = "00") then
+
+ --sustain cpu output data when reading.
+ if (cpu_addr = PPUDATA and r_nw = '1' and ppu_clk_cnt /= "00") then
+ if (ppu_addr(13 downto 8) = "111111") then
+ cpu_d <= plt_data_out;
+ else
+ cpu_d <= ppu_data_out;
+ end if;
+ end if;
+ if (cpu_addr = OAMDATA and r_nw = '1' and ppu_clk_cnt /= "00") then
+ cpu_d <= oam_data;
+ end if;
+
+ if(cpu_addr = PPUSTATUS and r_nw = '1') then
+ cpu_d <= ppu_stat_out;
+ end if;
+
+ else
+ ppu_data_we_n <= '1';
+ plt_bus_ce_n <= '1';
+ ppu_clk_cnt_res_n <= '0';
+ oam_bus_ce_n <= '1';
+ oam_addr_ce_n <= '1';
+ ppu_addr_cnt_ce_n <= '1';
+ ppu_latch_rst_n <= '1';
+
+ rd_n <= 'Z';
+ wr_n <= 'Z';
+ ale <= 'Z';
+ oam_plt_data <= (others => 'Z');
+ vram_ad <= (others => 'Z');
+ vram_a <= (others => 'Z');
+ cpu_d <= (others => 'Z');
+ end if; --if (rst_n = '0') then
+ end process;
+
end rtl;