diff options
author | lonkaars <loek@pipeframe.xyz> | 2023-02-24 13:20:02 +0100 |
---|---|---|
committer | lonkaars <loek@pipeframe.xyz> | 2023-02-24 13:20:02 +0100 |
commit | f3a47bde9bfaaa716de835c0c1499a685b4ac4f7 (patch) | |
tree | 90abe28726ea7484184179129256022472eb2e24 | |
parent | 7da7908989686daa2ac9fd2f3f79cad2f03c0828 (diff) | |
parent | 14a1c464c27206bff847fd46d3d5594b30f53af9 (diff) |
Merge branch 'dev' into ppu-interface
40 files changed, 2223 insertions, 427 deletions
diff --git a/basys3/basys3.srcs/apu.vhd b/basys3/basys3.srcs/apu.vhd index 4a594ab..1fff1e8 100644 --- a/basys3/basys3.srcs/apu.vhd +++ b/basys3/basys3.srcs/apu.vhd @@ -3,29 +3,29 @@ use ieee.std_logic_1164.all; --use ieee.numeric_std.all; entity apu is port( - CLK100: in std_logic; -- system clock - RESET: in std_logic; -- global (async) system reset - DATA: in std_logic_vector(15 downto 0); - SOUND: out std_logic); + CLK100 : in std_logic; -- system clock + RESET : in std_logic; -- global (async) system reset + DATA : in std_logic_vector(15 downto 0); + SOUND : out std_logic); - -- EN: in std_logic; -- PPU VRAM enable (enable ADDR and DATA tri-state drivers) - -- WEN: in std_logic; -- PPU VRAM write enable - -- ADDR: in std_logic_vector(15 downto 0); -- PPU VRAM ADDR - -- R,G,B: out std_logic_vector(3 downto 0); - -- NVSYNC, NHSYNC: out std_logic; -- native VGA out - -- TVSYNC, TVBLANK, THSYNC, THBLANK: out std_logic); -- tiny VGA out + -- EN : in std_logic; -- PPU VRAM enable (enable ADDR and DATA tri-state drivers) + -- WEN : in std_logic; -- PPU VRAM write enable + -- ADDR : in std_logic_vector(15 downto 0); -- PPU VRAM ADDR + -- R,G,B : out std_logic_vector(3 downto 0); + -- NVSYNC, NHSYNC : out std_logic; -- native VGA out + -- TVSYNC, TVBLANK, THSYNC, THBLANK : out std_logic); -- tiny VGA out end apu; architecture Behavioral of apu is component apu_note_to_frequency port( - data: in std_logic_vector(7 downto 0); - freq: out std_logic_vector(7 downto 0)); --frequency + data : in std_logic_vector(7 downto 0); + freq : out std_logic_vector(7 downto 0)); --frequency end component; component apu_LUT_reader port( - clk: in std_logic; - rst: in std_logic; - wave: in std_logic_vector(1 downto 0); - level: out std_logic_vector(7 downto 0)); + clk : in std_logic; + rst : in std_logic; + wave : in std_logic_vector(1 downto 0); + level : out std_logic_vector(7 downto 0)); end component; begin diff --git a/basys3/basys3.srcs/apu_LUT_reader.vhd b/basys3/basys3.srcs/apu_lut_reader.vhd index 6039798..2f92eca 100644 --- a/basys3/basys3.srcs/apu_LUT_reader.vhd +++ b/basys3/basys3.srcs/apu_lut_reader.vhd @@ -2,7 +2,7 @@ library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; -entity apu_LUT_reader is +entity apu_lut_reader is port ( clk : in std_logic; rst : in std_logic; @@ -12,7 +12,7 @@ entity apu_LUT_reader is ); end entity; -architecture Behavioral of apu_LUT_reader is +architecture behavioral of apu_lut_reader is constant AMPLITUDE : natural := 0; constant SAMPLE_SIZE : natural := 256; diff --git a/basys3/basys3.srcs/apu_tb_LUT_reader.vhd b/basys3/basys3.srcs/apu_lut_reader_tb.vhd index 5a38d39..1b425bf 100644 --- a/basys3/basys3.srcs/apu_tb_LUT_reader.vhd +++ b/basys3/basys3.srcs/apu_lut_reader_tb.vhd @@ -2,14 +2,14 @@ library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; -library UNISIM; -use UNISIM.VComponents.all; +library unisim; +use unisim.vcomponents.all; -entity apu_tb_LUT_reader is +entity apu_lut_reader_tb is end entity; -architecture Behavioral of apu_tb_LUT_reader is - component apu_LUT_reader is +architecture Behavioral of apu_lut_reader_tb is + component apu_lut_reader is port ( clk : in std_logic; rst : in std_logic; diff --git a/basys3/basys3.srcs/apu_note_to_frequency.vhd b/basys3/basys3.srcs/apu_note_to_frequency.vhd index 8a7b3d6..810cef9 100644 --- a/basys3/basys3.srcs/apu_note_to_frequency.vhd +++ b/basys3/basys3.srcs/apu_note_to_frequency.vhd @@ -10,9 +10,9 @@ entity apu_note_to_frequency is port ( end entity; architecture Behavioral of apu_note_to_frequency is - signal buff_small: std_logic_vector(7 downto 0) := (others => '0'); - signal buff: std_logic_vector(11 downto 0) := (others => '0'); - signal shift: integer; + signal buff_small : std_logic_vector(7 downto 0) := (others => '0'); + signal buff : std_logic_vector(15 downto 0) := (others => '0'); + signal shift : integer; begin shift <= to_integer(unsigned( data(2 downto 0) )); diff --git a/basys3/basys3.srcs/apu_note_to_frequency_tb.vhd b/basys3/basys3.srcs/apu_note_to_frequency_tb.vhd index 6814c1f..f48a40c 100644 --- a/basys3/basys3.srcs/apu_note_to_frequency_tb.vhd +++ b/basys3/basys3.srcs/apu_note_to_frequency_tb.vhd @@ -10,20 +10,20 @@ end entity; architecture Behavioral of apu_note_to_frequency_tb is component apu_note_to_frequency is port( - data: in std_logic_vector(7 downto 0); - freq: out std_logic_vector(11 downto 0)); -- frequency + data : in std_logic_vector(7 downto 0); + freq : out std_logic_vector(11 downto 0)); -- frequency end component; - signal data: std_logic_vector(7 downto 0) := (others => '0'); - signal freq: std_logic_vector(11 downto 0) := (others => '0'); + signal data : std_logic_vector(7 downto 0) := (others => '0'); + signal freq : std_logic_vector(11 downto 0) := (others => '0'); - signal ok: boolean := false; + signal ok : boolean := false; begin - uut: apu_note_to_frequency port map( + uut : apu_note_to_frequency port map( data => data, freq => freq); - tb: process + tb : process begin for i in 0 to 255 loop data <= std_logic_vector(to_unsigned(i, 8)); diff --git a/basys3/basys3.srcs/er_ram.vhd b/basys3/basys3.srcs/er_ram.vhd new file mode 100644 index 0000000..66f905b --- /dev/null +++ b/basys3/basys3.srcs/er_ram.vhd @@ -0,0 +1,53 @@ +library ieee; +library work; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +entity er_ram is -- exposed register RAM + generic( + ADDR_W : natural := 2; -- ADDR line width + DATA_W : natural := 2; -- DATA line width + ADDR_LOW : natural := 16#0000#; -- starting address + ADDR_RANGE : natural := 16#0002#); -- amount of valid addresses after ADDR_LOW + port( + CLK : in std_logic; -- clock + RST : in std_logic; -- async memory clear + WEN : in std_logic; -- write enable + ADDR : in std_logic_vector(ADDR_W-1 downto 0); -- address line + DATA : in std_logic_vector(DATA_W-1 downto 0); -- data input + REG : out std_logic_vector((ADDR_RANGE*DATA_W)-1 downto 0)); -- exposed register output +end er_ram; + +architecture Behavioral of er_ram is + component er_ram_mod + generic( + W : natural := DATA_W; -- module data width + ADDR_W : natural := ADDR_W; -- address width + ADDR_M : std_logic_vector(ADDR_W-1 downto 0) := (others => '0')); -- address match + port( + CLK : in std_logic; -- clock + RST : in std_logic; -- async memory clear + WEN : in std_logic; -- write enable + ADDR : in std_logic_vector(ADDR_W-1 downto 0); + DATA : in std_logic_vector(W-1 downto 0); -- data + REG : out std_logic_vector(W-1 downto 0)); -- direct register out + end component; + signal INT_REG : std_logic_vector((ADDR_RANGE*DATA_W)-1 downto 0); +begin + REG <= INT_REG; + + registers : for idx in 0 to ADDR_RANGE - 1 generate + reg : component er_ram_mod + generic map( + W => DATA_W, + ADDR_W => ADDR_W, + ADDR_M => std_logic_vector(to_unsigned(ADDR_LOW + idx, ADDR_W))) + port map( + CLK => CLK, + RST => RST, + WEN => WEN, + ADDR => ADDR, + DATA => DATA, + REG => INT_REG(idx*DATA_W+DATA_W-1 downto idx*DATA_W)); + end generate; +end Behavioral; diff --git a/basys3/basys3.srcs/er_ram_mod.vhd b/basys3/basys3.srcs/er_ram_mod.vhd new file mode 100644 index 0000000..ae680b1 --- /dev/null +++ b/basys3/basys3.srcs/er_ram_mod.vhd @@ -0,0 +1,34 @@ +library ieee; +library work; +use ieee.std_logic_1164.all; + +entity er_ram_mod is -- exposed register RAM module (single register) + generic( + W : natural := 1; -- module data width + ADDR_W : natural := 1; -- address width + ADDR_M : std_logic_vector(ADDR_W-1 downto 0) := (others => '0')); -- address match + port( + CLK : in std_logic; -- clock + RST : in std_logic; -- async memory clear + WEN : in std_logic; -- write enable + ADDR : in std_logic_vector(ADDR_W-1 downto 0); -- RAM address line + DATA : in std_logic_vector(W-1 downto 0); -- RAM input data line + REG : out std_logic_vector(W-1 downto 0)); -- direct register output lines +end er_ram_mod; + +architecture Behavioral of er_ram_mod is + signal DATA_REG : std_logic_vector(W-1 downto 0); +begin + REG <= DATA_REG; + + process(CLK, RST) + begin + if RST = '1' then + DATA_REG <= (others => '0'); + elsif rising_edge(CLK) then + if WEN = '1' and ADDR = ADDR_M then + DATA_REG <= DATA; + end if; + end if; + end process; +end Behavioral; diff --git a/basys3/basys3.srcs/er_ram_mod_tb.vhd b/basys3/basys3.srcs/er_ram_mod_tb.vhd new file mode 100644 index 0000000..aa77a56 --- /dev/null +++ b/basys3/basys3.srcs/er_ram_mod_tb.vhd @@ -0,0 +1,81 @@ +library ieee; +library unisim; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use unisim.vcomponents.all; + +entity er_ram_mod_tb is +end er_ram_mod_tb; + +architecture behavioral of er_ram_mod_tb is + component er_ram_mod + generic( + W : natural := 1; -- module data width + ADDR_W : natural := 1; -- address width + ADDR_M : std_logic_vector(ADDR_W-1 downto 0) := (others => '0')); -- address match + port( + CLK : in std_logic; -- clock + RST : in std_logic; -- async memory clear + WEN : in std_logic; -- write enable + ADDR : in std_logic_vector(ADDR_W-1 downto 0); + DATA : in std_logic_vector(W-1 downto 0); -- data + REG : out std_logic_vector(W-1 downto 0)); -- direct register out + end component; + + signal CLK, RST, WEN : std_logic := '0'; + signal ADDR : std_logic_vector(3 downto 0); + signal DATA : std_logic_vector(7 downto 0); + signal REG : std_logic_vector(7 downto 0); +begin + uut : component er_ram_mod + generic map( + W => 8, + ADDR_W => 4, + ADDR_M => x"5") + port map( + CLK => CLK, + RST => RST, + WEN => WEN, + ADDR => ADDR, + DATA => DATA, + REG => REG); + + tb : process + begin + wait for 5 ns; + + -- async reset (safety) + RST <= '1'; + wait for 5 ns; + RST <= '0'; + wait for 5 ns; + + -- set 0xef at address 0x5 (exists) + DATA <= x"ef"; + ADDR <= x"5"; + WEN <= '1'; + + CLK <= '1'; + wait for 5 ns; + CLK <= '0'; + wait for 5 ns; + + -- set 0x34 at address 0x4 (doesn't exist) + ADDR <= x"4"; + DATA <= x"34"; + + CLK <= '1'; + wait for 5 ns; + CLK <= '0'; + wait for 5 ns; + + -- reset + RST <= '1'; + wait for 5 ns; + RST <= '0'; + wait for 5 ns; + + wait; -- stop for simulator + end process; +end; diff --git a/basys3/basys3.srcs/er_ram_tb.vhd b/basys3/basys3.srcs/er_ram_tb.vhd new file mode 100644 index 0000000..d360442 --- /dev/null +++ b/basys3/basys3.srcs/er_ram_tb.vhd @@ -0,0 +1,116 @@ +library ieee; +library unisim; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use unisim.vcomponents.all; + +entity er_ram_tb is +end er_ram_tb; + +architecture behavioral of er_ram_tb is + component er_ram + generic( + ADDR_W : natural := 2; -- ADDR line width + DATA_W : natural := 2; -- DATA line width + ADDR_LOW : natural := 16#0000#; -- starting address + ADDR_RANGE : natural := 16#0002#); -- amount of valid addresses after ADDR_LOW + port( + CLK : in std_logic; -- clock + RST : in std_logic; -- async memory clear + WEN : in std_logic; -- write enable + ADDR : in std_logic_vector(ADDR_W-1 downto 0); + DATA : in std_logic_vector(DATA_W-1 downto 0); + REG : out std_logic_vector((ADDR_W*DATA_W)-1 downto 0)); + end component; + + signal CLK, RST, WEN : std_logic := '0'; + signal ADDR : std_logic_vector(3 downto 0); + signal DATA : std_logic_vector(7 downto 0); + signal REG : std_logic_vector(31 downto 0); +begin + uut : component er_ram + generic map( + ADDR_W => 4, + DATA_W => 8, + ADDR_LOW => 0, + ADDR_RANGE => 4) + port map( + CLK => CLK, + RST => RST, + WEN => WEN, + ADDR => ADDR, + DATA => DATA, + REG => REG); + + tb : process + begin + wait for 5 ns; + + -- async reset (safety) + RST <= '1'; + wait for 5 ns; + RST <= '0'; + wait for 5 ns; + + -- set 0xef at address 0x1 (exists) + DATA <= x"ef"; + ADDR <= x"1"; + WEN <= '1'; + + CLK <= '1'; + wait for 5 ns; + CLK <= '0'; + wait for 5 ns; + + -- set 0x34 at address 0x4 (doesn't exist) + ADDR <= x"4"; + DATA <= x"34"; + + CLK <= '1'; + wait for 5 ns; + CLK <= '0'; + wait for 5 ns; + + -- reset + RST <= '1'; + wait for 5 ns; + RST <= '0'; + wait for 5 ns; + + -- set REG to 0x12345678 + ADDR <= x"0"; + DATA <= x"12"; + + CLK <= '1'; + wait for 1 ns; + CLK <= '0'; + wait for 1 ns; + + ADDR <= x"1"; + DATA <= x"34"; + + CLK <= '1'; + wait for 1 ns; + CLK <= '0'; + wait for 1 ns; + + ADDR <= x"2"; + DATA <= x"56"; + + CLK <= '1'; + wait for 1 ns; + CLK <= '0'; + wait for 1 ns; + + ADDR <= x"3"; + DATA <= x"78"; + + CLK <= '1'; + wait for 1 ns; + CLK <= '0'; + wait for 1 ns; + + wait; -- stop for simulator + end process; +end; diff --git a/basys3/basys3.srcs/ppu.vhd b/basys3/basys3.srcs/ppu.vhd index 663f3ab..9cf1bc0 100644 --- a/basys3/basys3.srcs/ppu.vhd +++ b/basys3/basys3.srcs/ppu.vhd @@ -6,184 +6,191 @@ use ieee.std_logic_1164.all; use work.ppu_consts.all; entity ppu is port( - CLK100: in std_logic; -- system clock - RESET: in std_logic; -- global (async) system reset - EN: in std_logic; -- PPU VRAM enable (enable ADDR and DATA tri-state drivers) - WEN: in std_logic; -- PPU VRAM write enable - ADDR: in std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0); -- PPU VRAM ADDR - DATA: in std_logic_vector(PPU_RAM_BUS_DATA_WIDTH-1 downto 0); - R,G,B: out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); - NVSYNC, NHSYNC: out std_logic; -- native VGA out - TVSYNC, TVBLANK, THSYNC, THBLANK: out std_logic); -- tiny VGA out + CLK100 : in std_logic; -- system clock + RESET : in std_logic; -- global (async) system reset + EN : in std_logic; -- PPU VRAM enable (enable ADDR and DATA tri-state drivers) + WEN : in std_logic; -- PPU VRAM write enable + ADDR : in std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0); -- PPU VRAM ADDR + DATA : in std_logic_vector(PPU_RAM_BUS_DATA_WIDTH-1 downto 0); + R,G,B : out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); + NVSYNC, NHSYNC : out std_logic; -- native VGA out + TVSYNC, TVBLANK, THSYNC, THBLANK : out std_logic); -- tiny VGA out end ppu; architecture Behavioral of ppu is + -- TODO: separate SPRITE_BG and SPRITE_FG lines (foreground_sprite only needs 2 clock cycles) component ppu_pceg port( -- pipeline clock edge generator - CLK: in std_logic; -- system clock - RESET: in std_logic; -- async reset - SPRITE: out std_logic; -- sprite info fetch + sprite pixel fetch - COMP_PAL: out std_logic; -- compositor + palette lookup - DONE: out std_logic); -- last pipeline stage done + CLK : in std_logic; -- system clock + RESET : in std_logic; -- async reset + SPRITE : out std_logic; -- sprite info fetch + sprite pixel fetch + COMP_PAL : out std_logic; -- compositor + palette lookup + DONE : out std_logic); -- last pipeline stage done end component; component ppu_addr_dec port( -- pipeline clock edge generator - WEN: in std_logic; -- EXT write enable + WEN : in std_logic; -- EXT write enable TMM_WEN, BAM_WEN, FAM_WEN, PAL_WEN, - AUX_WEN: out std_logic; -- write enable MUX - EN: in std_logic; -- EXT *ADDR enable (switch *AO to ADDR instead of *AI) - ADDR: in std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0); -- address in - TMM_AI: in std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); - BAM_AI: in std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); - FAM_AI: in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); - PAL_AI: in std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); - AUX_AI: in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); - TMM_AO: out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); - BAM_AO: out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); - FAM_AO: out std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); - PAL_AO: out std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); - AUX_AO: out std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0)); + AUX_WEN : out std_logic; -- write enable MUX + EN : in std_logic; -- EXT *ADDR enable (switch *AO to ADDR instead of *AI) + ADDR : in std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0); -- address in + TMM_AI : in std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); + BAM_AI : in std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); + FAM_AI : in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); + PAL_AI : in std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); + AUX_AI : in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); + TMM_AO : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); + BAM_AO : out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); + FAM_AO : out std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); + PAL_AO : out std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); + AUX_AO : out std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0)); end component; component ppu_bam port( -- BAM block memory - clka: in std_logic; - rsta: in std_logic; - wea: in std_logic_vector(0 downto 0); - addra: in std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); - dina: in std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0); - douta: out std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0); - rsta_busy: out std_logic); + clka : in std_logic; + rsta : in std_logic; + wea : in std_logic_vector(0 downto 0); + addra : in std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); + dina : in std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0); + douta : out std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0); + rsta_busy : out std_logic); end component; component ppu_tmm port( -- TMM block memory - clka: in std_logic; - rsta: in std_logic; - wea: in std_logic_vector(0 downto 0); - addra: in std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); - dina: in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0); - douta: out std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0); - rsta_busy: out std_logic); + clka : in std_logic; + rsta : in std_logic; + wea : in std_logic_vector(0 downto 0); + addra : in std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); + dina : in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0); + douta : out std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0); + rsta_busy : out std_logic); end component; component ppu_aux port( - CLK: in std_logic; -- system clock - RESET: in std_logic; -- reset memory + CLK : in std_logic; -- system clock + RESET : in std_logic; -- reset memory -- internal memory block (AUX) - AUX_WEN: in std_logic; -- VRAM AUX write enable - AUX_ADDR: in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); -- VRAM AUX address - AUX_DATA: in std_logic_vector(PPU_AUX_DATA_WIDTH-1 downto 0); -- VRAM AUX data + AUX_WEN : in std_logic; -- VRAM AUX write enable + AUX_ADDR : in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); -- VRAM AUX address + AUX_DATA : in std_logic_vector(PPU_AUX_DATA_WIDTH-1 downto 0); -- VRAM AUX data -- aux outputs - BG_SHIFT_X: out std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); - BG_SHIFT_Y: out std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); - FG_FETCH: out std_logic); + BG_SHIFT_X : out std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); + BG_SHIFT_Y : out std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); + FG_FETCH : out std_logic); end component; component ppu_sprite_bg port( -- background sprite -- inputs - CLK: in std_logic; -- system clock - RESET: in std_logic; -- reset clock counter - OE: in std_logic; -- output enable (of CIDX) - X: in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x - Y: in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y + CLK : in std_logic; -- pipeline clock + RESET : in std_logic; -- reset clock counter + OE : in std_logic; -- output enable (of CIDX) + X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x + Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y -- aux inputs - BG_SHIFT_X: in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); - BG_SHIFT_Y: in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); + BG_SHIFT_X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); + BG_SHIFT_Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- used memory blocks - BAM_ADDR: out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); - BAM_DATA: in std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0); - TMM_ADDR: out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); - TMM_DATA: in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0); + BAM_ADDR : out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); + BAM_DATA : in std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0); + TMM_ADDR : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); + TMM_DATA : in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0); -- outputs - CIDX: out std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0)); -- output color + CIDX : out std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0)); -- output color end component; - component ppu_sprite_fg port( -- foreground sprite - -- inputs - CLK: in std_logic; -- system clock - RESET: in std_logic; -- reset internal memory and clock counters - OE: in std_logic; -- output enable (of CIDX) - X: in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x - Y: in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y - FETCH: in std_logic; -- fetch sprite data from TMM (TODO: generic map, set foreground sprite component index) + component ppu_sprite_fg -- foreground sprite + generic ( + IDX : natural := 0); + port( + -- inputs + CLK : in std_logic; -- system clock + RESET : in std_logic; -- reset internal memory and clock counters + OE : in std_logic; -- output enable (of CIDX) + X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x + Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y + FETCH : in std_logic; -- fetch sprite data from TMM + VBLANK : in std_logic; -- fetch during vblank - -- internal memory block (FAM) - FAM_WEN: in std_logic; -- VRAM FAM write enable - FAM_ADDR: in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); -- VRAM fam address - FAM_DATA: in std_logic_vector(PPU_FAM_DATA_WIDTH-1 downto 0); -- VRAM fam data + -- internal memory block (FAM) + FAM_WEN : in std_logic; -- VRAM FAM write enable + FAM_ADDR : in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); -- VRAM fam address + FAM_DATA : in std_logic_vector(PPU_FAM_DATA_WIDTH-1 downto 0); -- VRAM fam data - -- used memory blocks - TMM_ADDR: out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); - TMM_DATA: in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0); + -- used memory blocks + TMM_ADDR : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); + TMM_DATA : in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0); - -- outputs - CIDX: out std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0); -- output color - HIT: out std_logic); -- current pixel is not transparent + -- outputs + CIDX : out std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0); -- output color + HIT : out std_logic); -- current pixel is not transparent end component; component ppu_comp port( -- compositor - FG_HIT: in std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0); - BG_EN: out std_logic; - FG_EN: out std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0)); + FG_HIT : in std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0); + BG_EN : out std_logic; + FG_EN : out std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0)); end component; component ppu_plut port( -- palette lookup table - CLK: in std_logic; -- system clock - CIDX: in std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0); -- color in - RESET: in std_logic; + CLK : in std_logic; -- system clock + CIDX : in std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0); -- color in + RESET : in std_logic; -- internal memory block (AUX) - PAL_WEN: in std_logic; -- VRAM PAL write enable - PAL_ADDR: in std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); -- VRAM PAL address - PAL_DATA: in std_logic_vector(PPU_PAL_DATA_WIDTH-1 downto 0); -- VRAM PAL data + PAL_WEN : in std_logic; -- VRAM PAL write enable + PAL_ADDR : in std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); -- VRAM PAL address + PAL_DATA : in std_logic_vector(PPU_PAL_DATA_WIDTH-1 downto 0); -- VRAM PAL data - R,G,B: out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0)); -- VGA color out + R,G,B : out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0)); -- VGA color out end component; component ppu_vga_tiny port( -- tiny vga signal generator - CLK: in std_logic; -- system clock - RESET: in std_logic; + CLK : in std_logic; -- system clock + RESET : in std_logic; - X: out std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x - Y: out std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y + X : out std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x + Y : out std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y VSYNC, VBLANK, - HSYNC, HBLANK: out std_logic); -- VGA sync outputs + HSYNC, HBLANK : out std_logic); -- VGA sync outputs end component; component ppu_vga_native port( -- native vga signal generator (upscaler) - CLK: in std_logic; -- system clock - RESET: in std_logic; + CLK : in std_logic; -- system clock + RESET : in std_logic; - X: in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x - Y: in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y - PREADY: in std_logic; -- current pixel ready (pixel color is stable) - RI,GI,BI: in std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- VGA color in + X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x + Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y + PREADY : in std_logic; -- current pixel ready (pixel color is stable) + RI,GI,BI : in std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- VGA color in - RO,GO,BO: out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- VGA color out - VSYNC, HSYNC: out std_logic); -- VGA sync outputs + RO,GO,BO : out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- VGA color out + VSYNC, HSYNC : out std_logic); -- VGA sync outputs end component; -- signals - signal SYSCLK, SYSRST: std_logic; -- system clock and reset - signal PL_SPRITE, PL_COMP_PAL, PL_DONE: std_logic; -- pipeline stages - signal TMM_WEN, BAM_WEN, FAM_WEN, PAL_WEN, AUX_WEN: std_logic; - signal TMM_AI, TMM_AO: std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); - signal BAM_AI, BAM_AO: std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); - signal FAM_AI, FAM_AO: std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); - signal PAL_AI, PAL_AO: std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); - signal AUX_AI, AUX_AO: std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); - signal TMM_DO: std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0); - signal BAM_DO: std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0); - signal FAM_DO: std_logic_vector(PPU_FAM_DATA_WIDTH-1 downto 0); - signal PAL_DO: std_logic_vector(PPU_PAL_DATA_WIDTH-1 downto 0); - signal AUX_DO: std_logic_vector(PPU_AUX_DATA_WIDTH-1 downto 0); - signal CIDX: std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0); - signal BG_EN: std_logic; - signal FG_EN, FG_HIT: std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0); - signal X: std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x - signal Y: std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y - signal UR,UG,UB: std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- unstable RGB (to be buffered) - signal SR,SG,SB: std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- stable RGB (buffered until PL_COMP_PAL) - signal BG_SHIFT_X: std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); - signal BG_SHIFT_Y: std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); - signal FG_FETCH: std_logic; + signal SYSCLK, SYSRST : std_logic; -- system clock and reset + signal PL_SPRITE, PL_COMP_PAL, PL_DONE : std_logic; -- pipeline stages + signal TMM_WEN, BAM_WEN, FAM_WEN, PAL_WEN, AUX_WEN : std_logic; + signal TMM_AI, TMM_AO : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); + signal BAM_AI, BAM_AO : std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); + signal FAM_AI, FAM_AO : std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); + signal PAL_AI, PAL_AO : std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); + signal AUX_AI, AUX_AO : std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); + signal TMM_DO : std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0); + signal BAM_DO : std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0); + signal FAM_DO : std_logic_vector(PPU_FAM_DATA_WIDTH-1 downto 0); + signal PAL_DO : std_logic_vector(PPU_PAL_DATA_WIDTH-1 downto 0); + signal AUX_DO : std_logic_vector(PPU_AUX_DATA_WIDTH-1 downto 0); + signal CIDX : std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0); + signal BG_EN : std_logic; + signal FG_EN, FG_HIT : std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0); + signal X : std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x + signal Y : std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y + signal UR,UG,UB : std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- unstable RGB (to be buffered) + signal SR,SG,SB : std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- stable RGB (buffered until PL_COMP_PAL) + signal BG_SHIFT_X : std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); + signal BG_SHIFT_Y : std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); + signal FG_FETCH : std_logic; + signal TINY_VBLANK, TINY_VSYNC, TINY_HBLANK, TINY_HSYNC, + NATIVE_VSYNC, NATIVE_HSYNC : std_logic; begin SYSCLK <= CLK100; SYSRST <= RESET; @@ -197,14 +204,21 @@ begin FAM_AI <= (others => '0'); PAL_AI <= (others => '0'); - pipeline_clock_edge_generator: component ppu_pceg port map( + TVBLANK <= TINY_VBLANK; + TVSYNC <= TINY_VSYNC; + THBLANK <= TINY_HBLANK; + THSYNC <= TINY_HSYNC; + NVSYNC <= NATIVE_VSYNC; + NHSYNC <= NATIVE_HSYNC; + + pipeline_clock_edge_generator : component ppu_pceg port map( CLK => SYSCLK, RESET => SYSRST, SPRITE => PL_SPRITE, COMP_PAL => PL_COMP_PAL, DONE => PL_DONE); - address_decoder: component ppu_addr_dec port map( + address_decoder : component ppu_addr_dec port map( EN => EN, WEN => WEN, ADDR => ADDR, @@ -224,7 +238,7 @@ begin PAL_WEN => PAL_WEN, AUX_WEN => AUX_WEN); - background_attribute_memory: component ppu_bam port map( + background_attribute_memory : component ppu_bam port map( clka => SYSCLK, rsta => SYSRST, wea => (others => BAM_WEN), @@ -232,7 +246,7 @@ begin dina => DATA(PPU_BAM_DATA_WIDTH-1 downto 0), douta => BAM_DO, rsta_busy => open); - tilemap_memory: component ppu_tmm port map( + tilemap_memory : component ppu_tmm port map( clka => SYSCLK, rsta => SYSRST, wea => (others => TMM_WEN), @@ -241,7 +255,7 @@ begin douta => TMM_DO, rsta_busy => open); - aux: component ppu_aux port map( + aux : component ppu_aux port map( CLK => SYSCLK, RESET => SYSRST, AUX_WEN => AUX_WEN, @@ -251,7 +265,7 @@ begin BG_SHIFT_Y => BG_SHIFT_Y, FG_FETCH => FG_FETCH); - background_sprite: component ppu_sprite_bg port map( + background_sprite : component ppu_sprite_bg port map( CLK => PL_SPRITE, RESET => SYSRST, OE => BG_EN, @@ -265,29 +279,32 @@ begin TMM_DATA => TMM_DO, CIDX => CIDX); - foreground_sprites: for FG_IDX in 0 to PPU_FG_SPRITE_COUNT-1 generate - foreground_sprite: component ppu_sprite_fg port map( - CLK => PL_SPRITE, - RESET => SYSRST, - OE => FG_EN(FG_IDX), - X => X, - Y => Y, - FETCH => FG_FETCH, - FAM_WEN => FAM_WEN, - FAM_ADDR => FAM_AO, - FAM_DATA => DATA(PPU_FAM_DATA_WIDTH-1 downto 0), - TMM_ADDR => TMM_AI, - TMM_DATA => TMM_DO, - CIDX => CIDX, - HIT => FG_HIT(FG_IDX)); + foreground_sprites : for FG_IDX in 0 to PPU_FG_SPRITE_COUNT-1 generate + foreground_sprite : component ppu_sprite_fg + generic map( IDX => FG_IDX ) + port map( + CLK => SYSCLK, + RESET => SYSRST, + OE => FG_EN(FG_IDX), + X => X, + Y => Y, + FETCH => FG_FETCH, + VBLANK => TINY_VBLANK, + FAM_WEN => FAM_WEN, + FAM_ADDR => FAM_AO, + FAM_DATA => DATA(PPU_FAM_DATA_WIDTH-1 downto 0), + TMM_ADDR => TMM_AI, + TMM_DATA => TMM_DO, + CIDX => CIDX, + HIT => FG_HIT(FG_IDX)); end generate; - compositor: component ppu_comp port map( -- compositor + compositor : component ppu_comp port map( -- compositor FG_HIT => FG_HIT, BG_EN => BG_EN, FG_EN => FG_EN); - palette_lookup: component ppu_plut port map( -- palette lookup table + palette_lookup : component ppu_plut port map( -- palette lookup table CLK => SYSCLK, CIDX => CIDX, RESET => SYSRST, @@ -312,17 +329,17 @@ begin end if; end process; - tiny_vga_signal_generator: component ppu_vga_tiny port map( -- tiny vga signal generator + tiny_vga_signal_generator : component ppu_vga_tiny port map( -- tiny vga signal generator CLK => SYSCLK, RESET => SYSRST, X => X, Y => Y, - VSYNC => TVSYNC, - VBLANK => TVBLANK, - HSYNC => THSYNC, - HBLANK => THBLANK); + VSYNC => TINY_VSYNC, + VBLANK => TINY_VBLANK, + HSYNC => TINY_HSYNC, + HBLANK => TINY_HBLANK); - native_vga_signal_generator: component ppu_vga_native port map( -- native vga signal generator (upscaler) + native_vga_signal_generator : component ppu_vga_native port map( -- native vga signal generator (upscaler) CLK => SYSCLK, RESET => SYSRST, X => X, @@ -334,6 +351,6 @@ begin RO => R, GO => G, BO => B, - VSYNC => NVSYNC, - HSYNC => NHSYNC); + VSYNC => NATIVE_VSYNC, + HSYNC => NATIVE_HSYNC); end Behavioral; diff --git a/basys3/basys3.srcs/ppu_addr_dec.vhd b/basys3/basys3.srcs/ppu_addr_dec.vhd new file mode 100644 index 0000000..e0c374f --- /dev/null +++ b/basys3/basys3.srcs/ppu_addr_dec.vhd @@ -0,0 +1,51 @@ +library ieee; +library work; +use ieee.std_logic_1164.all; +--use ieee.numeric_std.all; +use work.ppu_consts.all; + +entity ppu_addr_dec is port( + EN : in std_logic; -- EXT *ADDR enable (switch *AO to ADDR instead of *AI) + WEN : in std_logic; -- EXT write enable + TMM_WEN, + BAM_WEN, + FAM_WEN, + PAL_WEN, + AUX_WEN : out std_logic; -- write enable MUX + ADDR : in std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0); -- address in + TMM_AI : in std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); + BAM_AI : in std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); + FAM_AI : in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); + PAL_AI : in std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); + AUX_AI : in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); + TMM_AO : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); + BAM_AO : out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); + FAM_AO : out std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); + PAL_AO : out std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); + AUX_AO : out std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0)); +end ppu_addr_dec; + +architecture Behavioral of ppu_addr_dec is + signal TMM_RANGE, BAM_RANGE, FAM_RANGE, PAL_RANGE, AUX_RANGE : std_logic := '0'; -- ADDR in range of memory area +begin + -- address MUX + TMM_AO <= ADDR(PPU_TMM_ADDR_WIDTH-1 downto 0) when EN = '1' else TMM_AI; + BAM_AO <= ADDR(PPU_BAM_ADDR_WIDTH-1 downto 0) when EN = '1' else BAM_AI; + FAM_AO <= ADDR(PPU_FAM_ADDR_WIDTH-1 downto 0) when EN = '1' else FAM_AI; + PAL_AO <= ADDR(PPU_PAL_ADDR_WIDTH-1 downto 0) when EN = '1' else PAL_AI; + AUX_AO <= ADDR(PPU_AUX_ADDR_WIDTH-1 downto 0) when EN = '1' else AUX_AI; + + -- WEN MUX + TMM_WEN <= TMM_RANGE and WEN; + BAM_WEN <= BAM_RANGE and WEN; + FAM_WEN <= FAM_RANGE and WEN; + PAL_WEN <= PAL_RANGE and WEN; + AUX_WEN <= AUX_RANGE and WEN; + + -- address ranges + TMM_RANGE <= '1' when not ((ADDR(15) and ADDR(14) and ADDR(13)) or (ADDR(15) and ADDR(14) and ADDR(12))) else '0'; + BAM_RANGE <= '1' when TMM_RANGE = '0' and (ADDR(11) = '0') else '0'; + FAM_RANGE <= '1' when TMM_RANGE = '0' and (ADDR(11) = '1' and ADDR(10) = '0') else '0'; + PAL_RANGE <= '1' when TMM_RANGE = '0' and (ADDR(11) = '1' and ADDR(10) = '1' and ADDR(9) = '0') else '0'; + AUX_RANGE <= '1' when TMM_RANGE = '0' and (ADDR(11) = '1' and ADDR(10) = '1' and ADDR(9) = '1') else '0'; +end Behavioral; diff --git a/basys3/basys3.srcs/ppu_addr_dec.vhdl b/basys3/basys3.srcs/ppu_addr_dec.vhdl deleted file mode 100644 index 28c22fc..0000000 --- a/basys3/basys3.srcs/ppu_addr_dec.vhdl +++ /dev/null @@ -1,51 +0,0 @@ -library ieee; -library work; -use ieee.std_logic_1164.all; ---use ieee.numeric_std.all; -use work.ppu_consts.all; - -entity ppu_addr_dec is port( - EN: in std_logic; -- EXT *ADDR enable (switch *AO to ADDR instead of *AI) - WEN: in std_logic; -- EXT write enable - TMM_WEN, - BAM_WEN, - FAM_WEN, - PAL_WEN, - AUX_WEN: out std_logic; -- write enable MUX - ADDR: in std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0); -- address in - TMM_AI: in std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); - BAM_AI: in std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); - FAM_AI: in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); - PAL_AI: in std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); - AUX_AI: in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); - TMM_AO: out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); - BAM_AO: out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); - FAM_AO: out std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); - PAL_AO: out std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); - AUX_AO: out std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0)); -end ppu_addr_dec; - -architecture Behavioral of ppu_addr_dec is - signal TMM_RANGE, BAM_RANGE, FAM_RANGE, PAL_RANGE, AUX_RANGE: std_logic := '0'; -- ADDR in range of memory area -begin - -- address MUX - TMM_AO <= ADDR(PPU_TMM_ADDR_WIDTH-1 downto 0) when EN = '1' else TMM_AI; - BAM_AO <= ADDR(PPU_BAM_ADDR_WIDTH-1 downto 0) when EN = '1' else BAM_AI; - FAM_AO <= ADDR(PPU_FAM_ADDR_WIDTH-1 downto 0) when EN = '1' else FAM_AI; - PAL_AO <= ADDR(PPU_PAL_ADDR_WIDTH-1 downto 0) when EN = '1' else PAL_AI; - AUX_AO <= ADDR(PPU_AUX_ADDR_WIDTH-1 downto 0) when EN = '1' else AUX_AI; - - -- WEN MUX - TMM_WEN <= TMM_RANGE and WEN; - BAM_WEN <= BAM_RANGE and WEN; - FAM_WEN <= FAM_RANGE and WEN; - PAL_WEN <= PAL_RANGE and WEN; - AUX_WEN <= AUX_RANGE and WEN; - - -- address ranges - TMM_RANGE <= '1' when not (ADDR(15) = '1' and ADDR(14) = '1') else '0'; - BAM_RANGE <= '1' when (ADDR(15) = '1' and ADDR(14) = '1') and (ADDR(11) = '0') else '0'; - FAM_RANGE <= '1' when (ADDR(15) = '1' and ADDR(14) = '1') and (ADDR(11) = '1' and ADDR(10) = '0') else '0'; - PAL_RANGE <= '1' when (ADDR(15) = '1' and ADDR(14) = '1') and (ADDR(11) = '1' and ADDR(10) = '1' and ADDR(9) = '0') else '0'; - AUX_RANGE <= '1' when (ADDR(15) = '1' and ADDR(14) = '1') and (ADDR(11) = '1' and ADDR(10) = '1' and ADDR(9) = '1') else '0'; -end Behavioral; diff --git a/basys3/basys3.srcs/ppu_addr_dec_tb.vhd b/basys3/basys3.srcs/ppu_addr_dec_tb.vhd new file mode 100644 index 0000000..f31ee67 --- /dev/null +++ b/basys3/basys3.srcs/ppu_addr_dec_tb.vhd @@ -0,0 +1,86 @@ +library ieee; +library unisim; +library work; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use unisim.vcomponents.all; +use work.ppu_consts.all; + +entity ppu_addr_dec_tb is +end ppu_addr_dec_tb; + +architecture behavioral of ppu_addr_dec_tb is + component ppu_addr_dec port( + EN : in std_logic; -- EXT *ADDR enable (switch *AO to ADDR instead of *AI) + WEN : in std_logic; -- EXT write enable + TMM_WEN, + BAM_WEN, + FAM_WEN, + PAL_WEN, + AUX_WEN : out std_logic; -- write enable MUX + ADDR : in std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0); -- address in + TMM_AI : in std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); + BAM_AI : in std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); + FAM_AI : in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); + PAL_AI : in std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); + AUX_AI : in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); + TMM_AO : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); + BAM_AO : out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); + FAM_AO : out std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); + PAL_AO : out std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); + AUX_AO : out std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0)); + end component; + signal EN : std_logic; + signal WEN : std_logic; + signal TMM_WEN, BAM_WEN, FAM_WEN, PAL_WEN, AUX_WEN : std_logic; + signal ADDR : std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0); + signal TMM_AI : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); + signal BAM_AI : std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); + signal FAM_AI : std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); + signal PAL_AI : std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); + signal AUX_AI : std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); + signal TMM_AO : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); + signal BAM_AO : std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); + signal FAM_AO : std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); + signal PAL_AO : std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); + signal AUX_AO : std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); +begin + uut : ppu_addr_dec port map( + EN => EN, + WEN => WEN, + TMM_WEN => TMM_WEN, + BAM_WEN => BAM_WEN, + FAM_WEN => FAM_WEN, + PAL_WEN => PAL_WEN, + AUX_WEN => AUX_WEN, + ADDR => ADDR, + TMM_AI => TMM_AI, + BAM_AI => BAM_AI, + FAM_AI => FAM_AI, + PAL_AI => PAL_AI, + AUX_AI => AUX_AI, + TMM_AO => TMM_AO, + BAM_AO => BAM_AO, + FAM_AO => FAM_AO, + PAL_AO => PAL_AO, + AUX_AO => AUX_AO); + + EN <= '1'; + WEN <= '1'; + + TMM_AI <= (others => '1'); + BAM_AI <= (others => '0'); + FAM_AI <= (others => '1'); + PAL_AI <= (others => '0'); + AUX_AI <= (others => '0'); + + tb : process + begin + for i in 0 to 65535 loop + ADDR <= std_logic_vector(to_unsigned(i,16)); + wait for 10 ps; + end loop; + wait; -- stop for simulator + end process; +end; diff --git a/basys3/basys3.srcs/ppu_addr_dec_tb.vhdl b/basys3/basys3.srcs/ppu_addr_dec_tb.vhdl deleted file mode 100644 index 5c7119d..0000000 --- a/basys3/basys3.srcs/ppu_addr_dec_tb.vhdl +++ /dev/null @@ -1,86 +0,0 @@ -library ieee; -library unisim; -library work; - -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use unisim.vcomponents.all; -use work.ppu_consts.all; - -entity ppu_addr_dec_tb is -end ppu_addr_dec_tb; - -architecture behavioral of ppu_addr_dec_tb is - component ppu_addr_dec port( - EN: in std_logic; -- EXT *ADDR enable (switch *AO to ADDR instead of *AI) - WEN: in std_logic; -- EXT write enable - TMM_WEN, - BAM_WEN, - FAM_WEN, - PAL_WEN, - AUX_WEN: out std_logic; -- write enable MUX - ADDR: in std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0); -- address in - TMM_AI: in std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); - BAM_AI: in std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); - FAM_AI: in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); - PAL_AI: in std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); - AUX_AI: in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); - TMM_AO: out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); - BAM_AO: out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); - FAM_AO: out std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); - PAL_AO: out std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); - AUX_AO: out std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0)); - end component; - signal EN: std_logic; - signal WEN: std_logic; - signal TMM_WEN, BAM_WEN, FAM_WEN, PAL_WEN, AUX_WEN: std_logic; - signal ADDR: std_logic_vector(PPU_RAM_BUS_ADDR_WIDTH-1 downto 0); - signal TMM_AI: std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); - signal BAM_AI: std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); - signal FAM_AI: std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); - signal PAL_AI: std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); - signal AUX_AI: std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); - signal TMM_AO: std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); - signal BAM_AO: std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); - signal FAM_AO: std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); - signal PAL_AO: std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); - signal AUX_AO: std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); -begin - uut: ppu_addr_dec port map( - EN => EN, - WEN => WEN, - TMM_WEN => TMM_WEN, - BAM_WEN => BAM_WEN, - FAM_WEN => FAM_WEN, - PAL_WEN => PAL_WEN, - AUX_WEN => AUX_WEN, - ADDR => ADDR, - TMM_AI => TMM_AI, - BAM_AI => BAM_AI, - FAM_AI => FAM_AI, - PAL_AI => PAL_AI, - AUX_AI => AUX_AI, - TMM_AO => TMM_AO, - BAM_AO => BAM_AO, - FAM_AO => FAM_AO, - PAL_AO => PAL_AO, - AUX_AO => AUX_AO); - - EN <= '1'; - WEN <= '1'; - - TMM_AI <= (others => '1'); - BAM_AI <= (others => '0'); - FAM_AI <= (others => '1'); - PAL_AI <= (others => '0'); - AUX_AI <= (others => '0'); - - tb: process - begin - for i in 0 to 65535 loop - ADDR <= std_logic_vector(to_unsigned(i,16)); - wait for 10 ps; - end loop; - wait; -- stop for simulator - end process; -end; diff --git a/basys3/basys3.srcs/ppu_aux.vhd b/basys3/basys3.srcs/ppu_aux.vhd new file mode 100644 index 0000000..9062bc4 --- /dev/null +++ b/basys3/basys3.srcs/ppu_aux.vhd @@ -0,0 +1,52 @@ +library ieee; +library work; +use ieee.std_logic_1164.all; +--use ieee.numeric_std.all; +use work.ppu_consts.all; + +entity ppu_aux is port( + CLK : in std_logic; -- system clock + RESET : in std_logic; -- reset memory + + -- internal memory block (AUX) + AUX_WEN : in std_logic; -- VRAM AUX write enable + AUX_ADDR : in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); -- VRAM AUX address + AUX_DATA : in std_logic_vector(PPU_AUX_DATA_WIDTH-1 downto 0); -- VRAM AUX data + + -- aux outputs + BG_SHIFT_X : out std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); + BG_SHIFT_Y : out std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); + FG_FETCH : out std_logic); +end ppu_aux; + +architecture Behavioral of ppu_aux is + component er_ram + generic( + ADDR_W : natural := PPU_AUX_ADDR_WIDTH; -- ADDR line width + DATA_W : natural := PPU_AUX_DATA_WIDTH; -- DATA line width + ADDR_LOW : natural := 16#0000#; -- starting address + ADDR_RANGE : natural := 16#0002#); -- amount of valid addresses after ADDR_LOW + port( + CLK : in std_logic; -- clock + RST : in std_logic; -- async memory clear + WEN : in std_logic; -- write enable + ADDR : in std_logic_vector(ADDR_W-1 downto 0); + DATA : in std_logic_vector(DATA_W-1 downto 0); + REG : out std_logic_vector((ADDR_RANGE*DATA_W)-1 downto 0)); -- exposed register output + end component; + + signal INT_REG : std_logic_vector(2 * PPU_AUX_DATA_WIDTH - 1 downto 0); +begin + -- docs/architecture.md#auxiliary-memory + FG_FETCH <= INT_REG(17); + BG_SHIFT_X <= INT_REG(16 downto 8); + BG_SHIFT_Y <= INT_REG(7 downto 0); + + RAM : component er_ram port map( + CLK => CLK, + RST => RESET, + WEN => AUX_WEN, + ADDR => AUX_ADDR, + DATA => AUX_DATA, + REG => INT_REG); +end Behavioral; diff --git a/basys3/basys3.srcs/ppu_aux_tb.vhd b/basys3/basys3.srcs/ppu_aux_tb.vhd new file mode 100644 index 0000000..ab3db62 --- /dev/null +++ b/basys3/basys3.srcs/ppu_aux_tb.vhd @@ -0,0 +1,100 @@ +library ieee; +library unisim; +library work; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use unisim.vcomponents.all; +use work.ppu_consts.all; + +entity ppu_aux_tb is +end ppu_aux_tb; + +architecture behavioral of ppu_aux_tb is + component ppu_aux port( + CLK : in std_logic; -- system clock + RESET : in std_logic; -- reset memory + + -- internal memory block (AUX) + AUX_WEN : in std_logic; -- VRAM AUX write enable + AUX_ADDR : in std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0); -- VRAM AUX address + AUX_DATA : in std_logic_vector(PPU_AUX_DATA_WIDTH-1 downto 0); -- VRAM AUX data + + -- aux outputs + BG_SHIFT_X : out std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); + BG_SHIFT_Y : out std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); + FG_FETCH : out std_logic); + end component; + + signal CLK, RST, WEN : std_logic := '0'; + signal ADDR : std_logic_vector(PPU_AUX_ADDR_WIDTH-1 downto 0) := (others => '0'); + signal DATA : std_logic_vector(PPU_AUX_DATA_WIDTH-1 downto 0) := (others => '0'); + + signal BG_SHIFT_X : std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); + signal BG_SHIFT_Y : std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); + signal FG_FETCH : std_logic; +begin + uut : component ppu_aux + port map( + CLK => CLK, + RESET => RST, + AUX_WEN => WEN, + AUX_ADDR => ADDR, + AUX_DATA => DATA, + BG_SHIFT_X => BG_SHIFT_X, + BG_SHIFT_Y => BG_SHIFT_Y, + FG_FETCH => FG_FETCH); + + tb : process + begin + -- reset + RST <= '1'; + wait for 5 ns; + RST <= '0'; + wait for 5 ns; + + WEN <= '1'; + -- set BG_SHIFT_X to 178 + -- set BG_SHIFT_Y to 39 + -- set FG_FETCH to 1 + ADDR(0) <= '0'; + DATA <= x"b327"; + + CLK <= '1'; + wait for 1 ns; + CLK <= '0'; + wait for 1 ns; + + ADDR(0) <= '1'; + DATA <= x"0002"; + + CLK <= '1'; + wait for 1 ns; + CLK <= '0'; + wait for 1 ns; + + wait for 5 ns; + + -- set BG_SHIFT_X to 30 + -- set BG_SHIFT_Y to 69 + -- set FG_FETCH to 0 + ADDR(0) <= '0'; + DATA <= x"1e45"; + + CLK <= '1'; + wait for 1 ns; + CLK <= '0'; + wait for 1 ns; + + ADDR(0) <= '1'; + DATA <= x"0000"; + + CLK <= '1'; + wait for 1 ns; + CLK <= '0'; + wait for 1 ns; + + + wait; -- stop for simulator + end process; +end; diff --git a/basys3/basys3.srcs/ppu_comp.vhd b/basys3/basys3.srcs/ppu_comp.vhd new file mode 100644 index 0000000..e79738f --- /dev/null +++ b/basys3/basys3.srcs/ppu_comp.vhd @@ -0,0 +1,36 @@ +library ieee; +use ieee.std_logic_1164.all; +use work.ppu_consts.all; + +entity ppu_comp is port ( + FG_HIT : in std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0); + BG_EN : out std_logic; + FG_EN : out std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0)); +end ppu_comp; + +architecture Behavioral of ppu_comp is + signal FG_HIT_EMPTY : std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0) := (others => '0'); +begin + process (FG_HIT) + variable HIT : boolean := false; + begin + -- check if FG_HIT is not empty + if FG_HIT /= FG_HIT_EMPTY then + BG_EN <= '0'; + for i in 0 to PPU_FG_SPRITE_COUNT-1 loop + -- if FG_HIT is the first one then enable it + if(FG_HIT(i) = '1' and HIT = false) then + FG_EN(i) <= '1'; + HIT := true; + else + -- make rest low + FG_EN(i) <= '0'; + end if; + end loop; + HIT := false; + else + BG_EN <= '1'; + FG_EN <= (others => '0'); + end if; + end process; +end Behavioral; diff --git a/basys3/basys3.srcs/ppu_comp_tb.vhd b/basys3/basys3.srcs/ppu_comp_tb.vhd new file mode 100644 index 0000000..be4c2e3 --- /dev/null +++ b/basys3/basys3.srcs/ppu_comp_tb.vhd @@ -0,0 +1,40 @@ +library ieee; +library unisim; +use ieee.std_logic_1164.all; +use work.ppu_consts.all; +use unisim.vcomponents.all; + +entity ppu_comp_tb is +end ppu_comp_tb; + +architecture behavioral of ppu_comp_tb is + component ppu_comp port ( + FG_HIT : in std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0); + BG_EN : out std_logic; + FG_EN : out std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0)); + end component; + signal FG_HIT : std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0) := (others => '0'); + signal BG_EN : std_logic := '0'; + signal FG_EN : std_logic_vector(PPU_FG_SPRITE_COUNT-1 downto 0) := (others => '0'); +begin + uut : ppu_comp port map ( + FG_HIT => FG_HIT, + BG_EN => BG_EN, + FG_EN => FG_EN + ); + tb : process + begin + FG_HIT <= (others => '0'); + wait for 1 ps; + FG_HIT(6) <= '1'; + FG_HIT(5) <= '1'; + FG_HIT(100) <= '1'; + wait for 1 ps; + + FG_HIT(0) <= '1'; + wait for 1 ps; + FG_HIT <= (others => '0'); + wait for 1 ps; + wait; + end process; +end Behavioral; diff --git a/basys3/basys3.srcs/ppu_consts.vhd b/basys3/basys3.srcs/ppu_consts.vhd index c063586..75b6168 100644 --- a/basys3/basys3.srcs/ppu_consts.vhd +++ b/basys3/basys3.srcs/ppu_consts.vhd @@ -1,22 +1,55 @@ +library ieee; +use ieee.math_real.all; + +-- https://docs.google.com/spreadsheets/d/1MU6K4c4PtMR_JXIpc3I0ZJdLZNnoFO7G2P3olCz6LSc package ppu_consts is - constant PPU_RAM_BUS_ADDR_WIDTH: natural := 16; -- RAM bus address width - constant PPU_RAM_BUS_DATA_WIDTH: natural := 16; -- RAM bus data width - constant PPU_FG_SPRITE_COUNT: natural := 128; -- amount of foreground sprites - constant PPU_COLOR_OUTPUT_DEPTH: natural := 4; -- VGA output channel depth - constant PPU_PALETTE_IDX_WIDTH: natural := 3; -- palette index width (within sprite) - constant PPU_PALETTE_WIDTH: natural := 3; -- palette index width (palette table) - constant PPU_PALETTE_CIDX_WIDTH: natural := PPU_PALETTE_IDX_WIDTH + PPU_PALETTE_WIDTH; -- global palette index width - constant PPU_TMM_ADDR_WIDTH: natural := 16; - constant PPU_TMM_DATA_WIDTH: natural := 16; - constant PPU_BAM_ADDR_WIDTH: natural := 11; - constant PPU_BAM_DATA_WIDTH: natural := 15; - constant PPU_FAM_ADDR_WIDTH: natural := 8; - constant PPU_FAM_DATA_WIDTH: natural := 16; - constant PPU_PAL_ADDR_WIDTH: natural := 6; - constant PPU_PAL_DATA_WIDTH: natural := 12; - constant PPU_AUX_ADDR_WIDTH: natural := 2; - constant PPU_AUX_DATA_WIDTH: natural := 16; - constant PPU_POS_H_WIDTH: natural := 9; -- amount of bits for horizontal screen offset - constant PPU_POS_V_WIDTH: natural := 8; -- amount of bits for vertical screen offset -end package ppu_consts; + -- utility functions + function ceil_log2(n : natural) return natural; + constant PPU_RAM_BUS_ADDR_WIDTH : natural := 16; -- RAM bus address width + constant PPU_RAM_BUS_DATA_WIDTH : natural := 16; -- RAM bus data width + constant PPU_FG_SPRITE_COUNT : natural := 128; -- foreground sprites + constant PPU_COLOR_OUTPUT_DEPTH : natural := 4; -- VGA output channel depth + constant PPU_PALETTE_COLOR_WIDTH : natural := 3; -- palette index width (within sprite) + constant PPU_PALETTE_INDEX_WIDTH : natural := 3; -- palette index width (palette table) + constant PPU_PALETTE_CIDX_WIDTH : natural := (PPU_PALETTE_COLOR_WIDTH + PPU_PALETTE_INDEX_WIDTH); -- global palette index width + constant PPU_TMM_ADDR_WIDTH : natural := 16; -- tilemap memory ram bus address width + constant PPU_TMM_DATA_WIDTH : natural := 15; -- tilemap memory ram bus data width + constant PPU_BAM_ADDR_WIDTH : natural := 11; -- background attribute memory ram bus address width + constant PPU_BAM_DATA_WIDTH : natural := 15; -- background attribute memory ram bus data width + constant PPU_FAM_ADDR_WIDTH : natural := 8; -- foreground attribute memory ram bus address width + constant PPU_FAM_DATA_WIDTH : natural := 16; -- foreground attribute memory ram bus data width + constant PPU_PAL_ADDR_WIDTH : natural := 6; -- palette memory ram bus address width + constant PPU_PAL_DATA_WIDTH : natural := 12; -- palette memory ram bus data width + constant PPU_AUX_ADDR_WIDTH : natural := 1; -- auxiliary memory ram bus address width + constant PPU_AUX_DATA_WIDTH : natural := 16; -- auxiliary memory ram bus data width + constant PPU_POS_H_WIDTH : natural := 9; -- bits for horizontal screen offset + constant PPU_POS_V_WIDTH : natural := 8; -- bits for vertical screen offset + constant PPU_SPRITE_WIDTH : natural := 16; -- sprite width (pixels) + constant PPU_SPRITE_HEIGHT : natural := 16; -- sprite height (pixels) + constant PPU_SPRITE_PIDX_WIDTH : natural := 8; -- bits needed to identify horizontal and vertical pixel within sprite + constant PPU_SPRITE_POS_H_WIDTH: natural := 4; -- bits needed to identify horizontal pixel within sprite + constant PPU_SPRITE_POS_V_WIDTH: natural := 4; -- bits needed to identify vertical pixel within sprite + constant PPU_SCREEN_WIDTH : natural := 320; -- absolute screen width (pixels) + constant PPU_SCREEN_HEIGHT : natural := 240; -- absolute screen height (pixels) + constant PPU_BG_CANVAS_TILES_H : natural := 40; -- tiles (horizontally) on background canvas + constant PPU_BG_CANVAS_TILES_V : natural := 30; -- tiles (vertically) on background canvas + constant PPU_BG_CANVAS_TILE_H_WIDTH : natural := 6; -- bits needed to describe horizontal bg tile index (grid coordinates) + constant PPU_BG_CANVAS_TILE_V_WIDTH : natural := 5; -- bits needed to describe vertical bg tile index (grid coordinates) + constant PPU_TILE_INDEX_WIDTH : natural := 10; -- bits needed to index a tile from TMM memory + constant PPU_PIXELS_PER_TILE_WORD : natural := 5; -- pixels defined in one word in TMM memory + constant PPU_SPRITE_WORD_COUNT : natural := 52; -- words needed for a single sprite + constant PPU_PIXEL_BIT_WIDTH : natural := 3; -- bits needed to identify pixel in TMM word + constant PPU_TILE_BIT_WIDTH : natural := (PPU_SPRITE_WIDTH * PPU_SPRITE_HEIGHT * PPU_PALETTE_COLOR_WIDTH); -- bits in single tile + constant PPU_TMM_CACHE_FETCH_C_COUNT : natural := PPU_SPRITE_WORD_COUNT + 1; + constant PPU_TMM_CACHE_FETCH_A_COUNT : natural := PPU_TMM_CACHE_FETCH_C_COUNT * PPU_FG_SPRITE_COUNT; -- amount of clocks to fetch new TMM cache + constant PPU_TMM_CACHE_FETCH_A_WIDTH : natural := ceil_log2(PPU_TMM_CACHE_FETCH_A_COUNT); +end package ppu_consts; +package body ppu_consts is + -- https://stackoverflow.com/questions/21783280/number-of-bits-to-represent-an-integer-in-vhdl + -- Returns number of bits required to represent val in binary vector + function ceil_log2(n : natural) return natural is + begin + return natural(integer(ceil(log2(real(n - 1))))); + end function; +end package body ppu_consts; diff --git a/basys3/basys3.srcs/ppu_pceg.vhdl b/basys3/basys3.srcs/ppu_pceg.vhd index 9675e5b..1aaeee4 100644 --- a/basys3/basys3.srcs/ppu_pceg.vhdl +++ b/basys3/basys3.srcs/ppu_pceg.vhd @@ -3,18 +3,18 @@ use ieee.std_logic_1164.all; --use ieee.numeric_std.all; entity ppu_pceg is port( - CLK: in std_logic; -- system clock - RESET: in std_logic; -- async reset - SPRITE: out std_logic; -- sprite info fetch + sprite pixel fetch - COMP_PAL: out std_logic; -- compositor + palette lookup - DONE: out std_logic); -- last pipeline stage done + CLK : in std_logic; -- system clock + RESET : in std_logic; -- async reset + SPRITE : out std_logic; -- sprite info fetch + sprite pixel fetch + COMP_PAL : out std_logic; -- compositor + palette lookup + DONE : out std_logic); -- last pipeline stage done end ppu_pceg; architecture Behavioral of ppu_pceg is - constant PPU_PL_TOTAL_STAGES: natural := 14; + constant PPU_PL_TOTAL_STAGES : natural := 14; type states is (PL_SPRITE, PL_COMP_PAL, PL_DONE); - signal state: states := PL_SPRITE; + signal state : states := PL_SPRITE; begin -- output drivers SPRITE <= CLK when RESET = '0' and state = PL_SPRITE else '0'; @@ -22,7 +22,7 @@ begin DONE <= '1' when RESET = '0' and state = PL_DONE else '0'; process(CLK, RESET) - variable CLK_IDX: natural range 0 to PPU_PL_TOTAL_STAGES+1 := 0; + variable CLK_IDX : natural range 0 to PPU_PL_TOTAL_STAGES+1 := 0; begin if RESET = '1' then state <= PL_SPRITE; diff --git a/basys3/basys3.srcs/ppu_pceg_tb.vhdl b/basys3/basys3.srcs/ppu_pceg_tb.vhd index 137d4b4..719ec06 100644 --- a/basys3/basys3.srcs/ppu_pceg_tb.vhdl +++ b/basys3/basys3.srcs/ppu_pceg_tb.vhd @@ -10,27 +10,27 @@ end ppu_pceg_tb; architecture behavioral of ppu_pceg_tb is component ppu_pceg port( - CLK: in std_logic; -- system clock - RESET: in std_logic; -- async reset - SPRITE: out std_logic; -- sprite info fetch + sprite pixel fetch - COMP_PAL: out std_logic; -- compositor + palette lookup - DONE: out std_logic); -- last pipeline stage done + CLK : in std_logic; -- system clock + RESET : in std_logic; -- async reset + SPRITE : out std_logic; -- sprite info fetch + sprite pixel fetch + COMP_PAL : out std_logic; -- compositor + palette lookup + DONE : out std_logic); -- last pipeline stage done end component; - signal CLK: std_logic := '0'; - signal RESET: std_logic := '0'; - signal SPRITE: std_logic; - signal COMP_PAL: std_logic; - signal DONE: std_logic; + signal CLK : std_logic := '0'; + signal RESET : std_logic := '0'; + signal SPRITE : std_logic; + signal COMP_PAL : std_logic; + signal DONE : std_logic; begin - uut: ppu_pceg port map( + uut : ppu_pceg port map( CLK => CLK, RESET => RESET, SPRITE => SPRITE, COMP_PAL => COMP_PAL, DONE => DONE); - tb: process + tb : process begin for i in 0 to 32 loop if i > 20 then diff --git a/basys3/basys3.srcs/ppu_plut.vhd b/basys3/basys3.srcs/ppu_plut.vhd new file mode 100644 index 0000000..d2e132e --- /dev/null +++ b/basys3/basys3.srcs/ppu_plut.vhd @@ -0,0 +1,69 @@ +library ieee; +use ieee.std_logic_1164.all; +use work.ppu_consts.all; +use ieee.numeric_std.all; +use ieee.std_logic_unsigned.all; + +entity ppu_plut is port ( + CLK : in std_logic; -- system clock + CIDX : in std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0); -- color in + RESET : in std_logic; + + -- internal memory block (AUX) + PAL_WEN : in std_logic; -- VRAM PAL write enable + PAL_ADDR : in std_logic_vector(PPU_PAL_ADDR_WIDTH-1 downto 0); -- VRAM PAL address + PAL_DATA : in std_logic_vector(PPU_PAL_DATA_WIDTH-1 downto 0); -- VRAM PAL data + + R,G,B : out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0)); -- VGA color out +end ppu_plut; + +architecture Behavioral of ppu_plut is + component er_ram + generic( + ADDR_W : natural := PPU_PAL_ADDR_WIDTH; -- ADDR line width + DATA_W : natural := PPU_PAL_DATA_WIDTH; -- DATA line width + ADDR_LOW : natural := 16#0000#; -- starting address + ADDR_RANGE : natural := 16#0040#); -- amount of valid addresses after ADDR_LOW + port( + CLK : in std_logic; -- clock + RST : in std_logic; -- async memory clear + WEN : in std_logic; -- write enable + ADDR : in std_logic_vector(ADDR_W-1 downto 0); + DATA : in std_logic_vector(DATA_W-1 downto 0); + REG : out std_logic_vector((ADDR_RANGE*DATA_W)-1 downto 0)); -- exposed register output + end component; + + signal PLUT : std_logic_vector((64 * PPU_PAL_DATA_WIDTH)-1 downto 0) := (others => '0'); + signal CHECK_ZERO_CIDX : std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0) := (others => '0'); -- color in +begin + RAM : component er_ram port map( + CLK => CLK, + RST => RESET, + WEN => PAL_WEN, + ADDR => PAL_ADDR, + DATA => PAL_DATA, + REG => PLUT); + + process(CLK, RESET) + variable COLOR : std_logic_vector(PPU_PAL_DATA_WIDTH-1 downto 0) := (others => '0'); -- COLORS RGB IN + variable CIDX_INT : integer := 0; + begin + if RESET = '1' then + PLUT <= (others => '0'); + else + if rising_edge (CLK) then + if (CIDX /= CHECK_ZERO_CIDX) then + CIDX_INT := to_integer(unsigned(CIDX)); + COLOR := PLUT((12 * CIDX_INT) + 11 downto (12*CIDX_INT)); + R <= COLOR(11 downto 8); + G <= COLOR(7 downto 4); + B <= COLOR(3 downto 0); + else + R <= x"0"; + G <= x"0"; + B <= x"0"; + end if; + end if; + end if; + end process; +end Behavioral; diff --git a/basys3/basys3.srcs/ppu_sprite_bg.vhd b/basys3/basys3.srcs/ppu_sprite_bg.vhd new file mode 100644 index 0000000..dba5b8e --- /dev/null +++ b/basys3/basys3.srcs/ppu_sprite_bg.vhd @@ -0,0 +1,138 @@ +library ieee; +library work; + +use ieee.std_logic_1164.all; +use ieee.std_logic_unsigned.all; +use ieee.numeric_std.all; +use work.ppu_consts.all; + +-- TODO: add input stable / output stable pipeline stages if this doesn't work with propagation delays +entity ppu_sprite_bg is port( + -- inputs + CLK : in std_logic; -- pipeline clock + RESET : in std_logic; -- reset clock counter + OE : in std_logic; -- output enable (of CIDX) + X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x + Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y + + -- aux inputs + BG_SHIFT_X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); + BG_SHIFT_Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); + + -- used memory blocks + BAM_ADDR : out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); + BAM_DATA : in std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0); + TMM_ADDR : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); + TMM_DATA : in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0); + + -- outputs + CIDX : out std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0)); -- output color +end ppu_sprite_bg; + +architecture Behavioral of ppu_sprite_bg is + component ppu_sprite_transform port( + XI : in unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- pixel position relative to tile + YI : in unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0); -- pixel position relative to tile + FLIP_H, FLIP_V : in std_logic; -- flip sprite + XO : out unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- new pixel position relative to tile + YO : out unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0)); -- new pixel position relative to tile + end component; + + -- BAM and TMM in/out temp + registers + signal T_BAM_ADDR, R_BAM_ADDR : std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0) := (others => '0'); + signal T_BAM_DATA, R_BAM_DATA : std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0) := (others => '0'); + signal T_TMM_ADDR, R_TMM_ADDR : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0) := (others => '0'); + signal T_TMM_DATA, R_TMM_DATA : std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0) := (others => '0'); + + -- state machine for synchronizing pipeline stages + type states is (PL_BAM_ADDR, PL_BAM_DATA, PL_TMM_ADDR, PL_TMM_DATA); + signal state : states := PL_BAM_ADDR; + + -- docs/architecture.md#background-attribute-memory + alias BAM_DATA_FLIP_H is R_BAM_DATA(14); -- flip horizontally + alias BAM_DATA_FLIP_V is R_BAM_DATA(13); -- flip vertically + alias BAM_DATA_TILE_IDX is R_BAM_DATA(9 downto 0); -- tilemap tile index + alias BAM_DATA_COL_IDX is R_BAM_DATA(12 downto 10); -- palette for sprite + + -- auxiliary signals (temp variables) + signal PIXEL_ABS_X, PIXEL_ABS_Y : integer := 0; -- absolute pixel position (relative to BG canvas instead of viewport) + signal TILE_IDX_X, TILE_IDX_Y : integer := 0; -- background canvas tile grid xy + signal TILE_PIDX_X, TRANS_TILE_PIDX_X : unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0) := (others => '0'); -- x position of pixel within tile (local tile coords) + signal TILE_PIDX_Y, TRANS_TILE_PIDX_Y : unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0) := (others => '0'); -- y position of pixel within tile (local tile coords) + signal TRANS_TILE_PIDX : integer := 0; -- index of pixel within tile (reading order) + signal TILEMAP_WORD_OFFSET : integer := 0; -- word offset from tile start address in TMM + signal PIXEL_BIT_OFFSET : integer := 0; -- pixel index within word of TMM + signal TMM_DATA_PAL_IDX : std_logic_vector(PPU_PALETTE_COLOR_WIDTH-1 downto 0); -- color of palette + signal T_CIDX : std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0) := (others => '0'); -- output color buffer/register +begin + -- output drivers + CIDX <= T_CIDX when OE = '1' else (others => 'Z'); + BAM_ADDR <= R_BAM_ADDR; + TMM_ADDR <= R_TMM_ADDR; + T_BAM_DATA <= BAM_DATA; + T_TMM_DATA <= TMM_DATA; + -- CIDX combination + T_CIDX <= BAM_DATA_COL_IDX & TMM_DATA_PAL_IDX; + + -- BAM address calculations + PIXEL_ABS_X <= to_integer(unsigned(X)) + to_integer(unsigned(BG_SHIFT_X)); -- pixel position relative to background canvas + PIXEL_ABS_Y <= to_integer(unsigned(Y)) + to_integer(unsigned(BG_SHIFT_Y)); -- pixel position relative to background canvas + TILE_IDX_X <= PIXEL_ABS_X / 16; -- tile grid index + TILE_IDX_Y <= PIXEL_ABS_Y / 16; -- tile grid index + TILE_PIDX_X <= to_unsigned(PIXEL_ABS_X - TILE_IDX_X * 16, TILE_PIDX_X'length); -- (sprite local) pixel coords + TILE_PIDX_Y <= to_unsigned(PIXEL_ABS_Y - TILE_IDX_Y * 16, TILE_PIDX_Y'length); -- (sprite local) pixel coords + T_BAM_ADDR <= std_logic_vector(to_unsigned((TILE_IDX_Y * integer(PPU_BG_CANVAS_TILES_H)) + TILE_IDX_X, PPU_BAM_ADDR_WIDTH)); + + -- BAM data dependant calculations + transform: component ppu_sprite_transform port map( + XI => TILE_PIDX_X, + YI => TILE_PIDX_Y, + FLIP_H => BAM_DATA_FLIP_H, + FLIP_V => BAM_DATA_FLIP_V, + XO => TRANS_TILE_PIDX_X, + YO => TRANS_TILE_PIDX_Y); + + -- TMM address calculations + TRANS_TILE_PIDX <= integer(PPU_SPRITE_WIDTH) * to_integer(TRANS_TILE_PIDX_Y) + to_integer(TRANS_TILE_PIDX_X); -- pixel index of sprite + TILEMAP_WORD_OFFSET <= TRANS_TILE_PIDX / PPU_PIXELS_PER_TILE_WORD; -- word offset from starting word of sprite + PIXEL_BIT_OFFSET <= TRANS_TILE_PIDX mod PPU_PIXELS_PER_TILE_WORD; -- pixel bit offset + T_TMM_ADDR <= std_logic_vector(to_unsigned(PPU_SPRITE_WORD_COUNT * to_integer(unsigned(BAM_DATA_TILE_IDX)) + TILEMAP_WORD_OFFSET, PPU_TMM_ADDR_WIDTH)); -- TMM address + + -- TMM DATA + with PIXEL_BIT_OFFSET select + TMM_DATA_PAL_IDX <= R_TMM_DATA(2 downto 0) when 0, + R_TMM_DATA(5 downto 3) when 1, + R_TMM_DATA(8 downto 6) when 2, + R_TMM_DATA(11 downto 9) when 3, + R_TMM_DATA(14 downto 12) when 4, + (others => '0') when others; + + -- state machine (pipeline stage counter) + sync r/w + process(CLK, RESET) + begin + if RESET = '1' then + -- reset state + state <= PL_BAM_ADDR; + -- reset internal pipeline registers + R_BAM_ADDR <= (others => '0'); + R_BAM_DATA <= (others => '0'); + R_TMM_ADDR <= (others => '0'); + R_TMM_DATA <= (others => '0'); + elsif rising_edge(CLK) then + case state is + when PL_BAM_ADDR => + state <= PL_BAM_DATA; + R_BAM_ADDR <= T_BAM_ADDR; + when PL_BAM_DATA => + state <= PL_TMM_ADDR; + R_BAM_DATA <= T_BAM_DATA; + when PL_TMM_ADDR => + state <= PL_TMM_DATA; + R_TMM_ADDR <= T_TMM_ADDR; + when PL_TMM_DATA => + state <= PL_BAM_ADDR; + R_TMM_DATA <= T_TMM_DATA; + end case; + end if; + end process; +end Behavioral; diff --git a/basys3/basys3.srcs/ppu_sprite_bg_tb.vhd b/basys3/basys3.srcs/ppu_sprite_bg_tb.vhd new file mode 100644 index 0000000..65da15c --- /dev/null +++ b/basys3/basys3.srcs/ppu_sprite_bg_tb.vhd @@ -0,0 +1,107 @@ +library ieee; +library work; +library unisim; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use unisim.vcomponents.all; +use work.ppu_consts.all; + +entity ppu_sprite_bg_tb is +end ppu_sprite_bg_tb; + +architecture Behavioral of ppu_sprite_bg_tb is + component ppu_sprite_bg port( + -- inputs + CLK : in std_logic; -- pipeline clock + RESET : in std_logic; -- reset clock counter + OE : in std_logic; -- output enable (of CIDX) + X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x + Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y + + -- aux inputs + BG_SHIFT_X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); + BG_SHIFT_Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); + + -- used memory blocks + BAM_ADDR : out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); + BAM_DATA : in std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0); + TMM_ADDR : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); + TMM_DATA : in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0); + + -- outputs + CIDX : out std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0)); -- output color + end component; + signal CLK : std_logic := '0'; -- pipeline clock + signal RESET : std_logic := '0'; -- reset clock counter + signal OE : std_logic := '0'; -- output enable (of CIDX) + signal X : std_logic_vector(PPU_POS_H_WIDTH-1 downto 0) := (others => '0'); -- current screen pixel x + signal Y : std_logic_vector(PPU_POS_V_WIDTH-1 downto 0) := (others => '0'); -- current screen pixel y + signal BG_SHIFT_X : std_logic_vector(PPU_POS_H_WIDTH-1 downto 0) := (others => '0'); + signal BG_SHIFT_Y : std_logic_vector(PPU_POS_V_WIDTH-1 downto 0) := (others => '0'); + signal BAM_ADDR : std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0); + signal BAM_DATA : std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0) := (others => '0'); + signal TMM_ADDR : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); + signal TMM_DATA : std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0) := (others => '0'); + signal CIDX : std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0); -- output color +begin + uut : ppu_sprite_bg port map( + CLK => CLK, + RESET => RESET, + OE => OE, + X => X, + Y => Y, + BG_SHIFT_X => BG_SHIFT_X, + BG_SHIFT_Y => BG_SHIFT_Y, + BAM_ADDR => BAM_ADDR, + BAM_DATA => BAM_DATA, + TMM_ADDR => TMM_ADDR, + TMM_DATA => TMM_DATA, + CIDX => CIDX); + + -- inputs + -- BAM_DATA -> FLIP_H + palette index 2 + tilemap index 10 + -- TMM_DATA -> all pixels color 7 + -- X -> 25 + -- Y -> 60 + -- BG_SHIFT_X -> 3 + -- BG_SHIFT_Y -> 3 + BAM_DATA <= 15x"4814"; -- hex((1 << 14) | (0 << 13) | (2 << 10) | (20 << 0)) + TMM_DATA <= 15x"7e3f"; -- hex(0x7fff & ~(0b111 << 6)) + X <= std_logic_vector(to_unsigned(25, PPU_POS_H_WIDTH)); + Y <= std_logic_vector(to_unsigned(60, PPU_POS_V_WIDTH)); + BG_SHIFT_X <= std_logic_vector(to_unsigned(3, PPU_POS_H_WIDTH)); + BG_SHIFT_Y <= std_logic_vector(to_unsigned(3, PPU_POS_V_WIDTH)); + + -- expected outputs: + -- absolute pixel coordinates -> (28, 63) + -- absolute background canvas tile index coordinates -> (1, 3) + -- tile index -> 3 * 40 + 1 = 121 + -- BAM_ADDR => 121 (OK) + -- sprite local coordinates -> (12, 15) + -- transformed local coordinates -> (12, 0) (apply FLIP_H) + -- pixel index -> 0 * 16 + 12 = 12 + -- pixel word address -> 52 * 20 + 12//5 -> 1042 + -- TMM_ADDR => 1042 (OK) + -- pixel bits -> [pixel n+] = 12 % 5 = 2 -> 8 downto 6 + -- cidx should be -- (2 << 3) | (0 << 0) = 16 + -- CIDX => 16 (OK) + + tb : process + begin + for i in 0 to 32 loop + if i > 10 then + OE <= '1'; + end if; + if i > 20 then + RESET <= '1'; + end if; + + wait for 5 ns; + CLK <= '1'; + wait for 5 ns; + CLK <= '0'; + end loop; + wait; -- stop for simulator + end process; +end Behavioral; diff --git a/basys3/basys3.srcs/ppu_sprite_fg.vhd b/basys3/basys3.srcs/ppu_sprite_fg.vhd new file mode 100644 index 0000000..af7cfa3 --- /dev/null +++ b/basys3/basys3.srcs/ppu_sprite_fg.vhd @@ -0,0 +1,192 @@ +library ieee; +library work; + +use ieee.std_logic_1164.all; +use ieee.std_logic_unsigned.all; +use ieee.numeric_std.all; +use work.ppu_consts.all; + +-- TODO: add input stable / output stable pipeline stages if this doesn't work with propagation delays +entity ppu_sprite_fg is -- foreground sprite + generic ( + IDX : natural := 0); -- sprite index number + port( + -- inputs + CLK : in std_logic; -- system clock + RESET : in std_logic; -- reset internal memory and clock counters + OE : in std_logic; -- output enable (of CIDX) + X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x + Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y + FETCH : in std_logic; -- fetch sprite data from TMM + VBLANK : in std_logic; -- fetch during vblank + + -- internal memory block (FAM) + FAM_WEN : in std_logic; -- VRAM FAM write enable + FAM_ADDR : in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); -- VRAM fam address + FAM_DATA : in std_logic_vector(PPU_FAM_DATA_WIDTH-1 downto 0); -- VRAM fam data + + -- used memory blocks + TMM_ADDR : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); + TMM_DATA : in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0); + + -- outputs + CIDX : out std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0); -- output color + HIT : out std_logic); -- current pixel is not transparent +end ppu_sprite_fg; + +architecture Behavioral of ppu_sprite_fg is + component ppu_sprite_transform port( + XI : in unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- pixel position relative to tile + YI : in unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0); -- pixel position relative to tile + FLIP_H, FLIP_V : in std_logic; -- flip sprite + XO : out unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- new pixel position relative to tile + YO : out unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0)); -- new pixel position relative to tile + end component; + component er_ram -- exposed register RAM + generic( + ADDR_W : natural := 2; -- ADDR line width + DATA_W : natural := 2; -- DATA line width + ADDR_LOW : natural := 16#0000#; -- starting address + ADDR_RANGE : natural := 16#0002#); -- amount of valid addresses after ADDR_LOW + port( + CLK : in std_logic; -- clock + RST : in std_logic; -- async memory clear + WEN : in std_logic; -- write enable + ADDR : in std_logic_vector(ADDR_W-1 downto 0); -- address line + DATA : in std_logic_vector(DATA_W-1 downto 0); -- data input + REG : out std_logic_vector((ADDR_RANGE*DATA_W)-1 downto 0)); -- exposed register output + end component; + + -- FAM and TMM in/out lines + signal T_TMM_ADDR : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0) := (others => '0'); + signal T_TMM_DATA : std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0) := (others => '0'); + + -- auxiliary signals (temp variables) + signal T_CIDX : std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0) := (others => '0'); -- output color buffer/register + signal INT_FAM : std_logic_vector(31 downto 0); + + -- FAM aliases (docs/architecture.md#foreground-attribute-memory) + alias FAM_REG_FLIP_H is INT_FAM(31); -- Flip horizontally + alias FAM_REG_FLIP_V is INT_FAM(30); -- Flip vertically + alias FAM_REG_POS_H is INT_FAM(29 downto 21); -- horizontal position (offset by -16) + alias FAM_REG_POS_V is INT_FAM(20 downto 13); -- vertical position (offset by -16) + alias FAM_REG_COL_IDX is INT_FAM(12 downto 10); -- Palette index for tile + alias FAM_REG_TILE_IDX is INT_FAM(9 downto 0); -- Tilemap index + + signal SPRITE_ACTIVE : std_logic := '0'; -- is pixel in bounding box of sprite + signal PIXEL_ABS_X, PIXEL_ABS_Y : integer := 0; -- absolute pixel position (relative to FG canvas instead of viewport) + signal TILE_PIDX_X, TRANS_TILE_PIDX_X : unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0) := (others => '0'); -- xy position of pixel within tile (local tile coords) + signal TILE_PIDX_Y, TRANS_TILE_PIDX_Y : unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0) := (others => '0'); -- xy position of pixel within tile (local tile coords) + signal TRANS_TILE_PIXEL_IDX : integer := 0; -- index of pixel within tile (reading order) + signal TILEMAP_WORD_OFFSET : integer := 0; -- word offset from tile start address in TMM + signal TMM_DATA_PAL_IDX : std_logic_vector(PPU_PALETTE_COLOR_WIDTH-1 downto 0); -- color of palette + + -- TMM cache lines + signal TMM_CACHE_WEN, TMM_CACHE_UPDATE_TURN : std_logic := '0'; + signal TMM_CACHE_DATA : std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0) := (others => '0'); + signal TMM_CACHE_ADDR : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0) := (others => '0'); + signal TMM_CACHE : std_logic_vector((PPU_SPRITE_WORD_COUNT * PPU_TMM_DATA_WIDTH)-1 downto 0); +begin + -- output drivers + CIDX <= T_CIDX when OE = '1' else (others => 'Z'); + -- CIDX combination + T_CIDX <= FAM_REG_COL_IDX & TMM_DATA_PAL_IDX; + + -- FAM memory + FAM : component er_ram + generic map( + ADDR_W => PPU_FAM_ADDR_WIDTH, + DATA_W => PPU_FAM_DATA_WIDTH, + ADDR_LOW => IDX*2, + ADDR_RANGE => 2) + port map( + CLK => CLK, + RST => RESET, + WEN => FAM_WEN, + ADDR => FAM_ADDR, + DATA => FAM_DATA, + REG => INT_FAM); + + -- pixel position within bounding box of sprite + SPRITE_ACTIVE <= '1' when ((unsigned(X) + 16) >= unsigned(FAM_REG_POS_H)) and + ((unsigned(X) + 16) < (unsigned(FAM_REG_POS_H) + to_unsigned(PPU_SPRITE_WIDTH, PPU_POS_H_WIDTH))) and + ((unsigned(Y) + 16) >= unsigned(FAM_REG_POS_V)) and + ((unsigned(Y) + 16) < (unsigned(FAM_REG_POS_V) + to_unsigned(PPU_SPRITE_HEIGHT, PPU_POS_V_WIDTH))) else '0'; + + -- (sprite local) pixel coords + TILE_PIDX_X <= resize(unsigned(X) + 16 - resize(unsigned(FAM_REG_POS_H), TILE_PIDX_X'length), TILE_PIDX_X'length); + TILE_PIDX_Y <= resize(unsigned(Y) + 16 - resize(unsigned(FAM_REG_POS_V), TILE_PIDX_Y'length), TILE_PIDX_Y'length); + + -- transform local coords + transform: component ppu_sprite_transform port map( + XI => TILE_PIDX_X, + YI => TILE_PIDX_Y, + FLIP_H => FAM_REG_FLIP_H, + FLIP_V => FAM_REG_FLIP_V, + XO => TRANS_TILE_PIDX_X, + YO => TRANS_TILE_PIDX_Y); + + -- pixel index + TRANS_TILE_PIXEL_IDX <= integer(PPU_SPRITE_WIDTH) * to_integer(TRANS_TILE_PIDX_Y) + to_integer(TRANS_TILE_PIDX_X); + -- palette color at pixel + TMM_DATA_PAL_IDX <= TMM_CACHE(TRANS_TILE_PIXEL_IDX * integer(PPU_PALETTE_COLOR_WIDTH) + integer(PPU_PALETTE_COLOR_WIDTH)-1 downto TRANS_TILE_PIXEL_IDX * integer(PPU_PALETTE_COLOR_WIDTH)); + -- if pixel in sprite hitbox and TMM_DATA_PAL_IDX > 0 + HIT <= SPRITE_ACTIVE and (nor TMM_DATA_PAL_IDX); + + -- FETCH LOGIC BELOW + TMM_ADDR <= T_TMM_ADDR when TMM_CACHE_UPDATE_TURN else (others => 'Z'); + T_TMM_DATA <= TMM_DATA; + + -- TTM cache + ttm_cache : component er_ram + generic map( + ADDR_W => PPU_TMM_ADDR_WIDTH, + DATA_W => PPU_TMM_DATA_WIDTH, + ADDR_LOW => 0, + ADDR_RANGE => PPU_SPRITE_WORD_COUNT) + port map( + CLK => CLK, + RST => RESET, + WEN => TMM_CACHE_WEN, + ADDR => TMM_CACHE_ADDR, + DATA => TMM_CACHE_DATA, + REG => TMM_CACHE); + + -- fetch machine, should do the following (offset data read by one clock -> propagation/lookup delay): + -- CLK[53 * IDX + 0] (addr = 0) + -- CLK[53 * IDX + 1] (addr = 1, read data[0]) + -- CLK[53 * IDX + 2] (addr = 2, read data[1]), etc + -- a full tile is 52 words, but since the offset is 1 clock, a total copy takes 53 clock cycles + process(CLK, RESET, FETCH) + constant TMM_FETCH_CLK_RANGE_BEGIN : natural := PPU_TMM_CACHE_FETCH_C_COUNT * IDX; -- fetch CLK count for copying this module's sprite from TMM + variable TMM_FETCH_CTR : unsigned(PPU_TMM_CACHE_FETCH_A_WIDTH-1 downto 0) := (others => '0'); -- CLK counter while FETCH=1 + variable TMM_FETCH_CTR_REL : unsigned(PPU_TMM_CACHE_FETCH_A_WIDTH-1 downto 0) := (others => '0'); -- CLK counter relative for sprite[IDX] + begin + if RESET = '1' or FETCH = '0' then + TMM_FETCH_CTR := (others => '0'); + TMM_FETCH_CTR_REL := (others => '0'); + TMM_CACHE_WEN <= '0'; + TMM_CACHE_UPDATE_TURN <= '0'; + elsif rising_edge(CLK) then + TMM_FETCH_CTR := TMM_FETCH_CTR + 1; + TMM_FETCH_CTR_REL := TMM_FETCH_CTR - TMM_FETCH_CLK_RANGE_BEGIN; + + if TMM_FETCH_CTR >= TMM_FETCH_CLK_RANGE_BEGIN and + TMM_FETCH_CTR < (TMM_FETCH_CLK_RANGE_BEGIN + PPU_TMM_CACHE_FETCH_C_COUNT) then + TMM_CACHE_UPDATE_TURN <= '1'; + if TMM_FETCH_CTR_REL < PPU_TMM_CACHE_FETCH_C_COUNT - 1 then -- calculate address until second to last clock + T_TMM_ADDR <= std_logic_vector(resize(TMM_FETCH_CTR - IDX, T_TMM_ADDR'length)); + TMM_CACHE_ADDR <= std_logic_vector(resize(TMM_FETCH_CTR_REL - 1, TMM_CACHE_ADDR'length)); + end if; + + if TMM_FETCH_CTR_REL > 0 then -- read offset + TMM_CACHE_DATA <= T_TMM_DATA; + TMM_CACHE_WEN <= '1'; + end if; + else + TMM_CACHE_WEN <= '0'; + TMM_CACHE_UPDATE_TURN <= '0'; + end if; + end if; + end process; +end Behavioral; diff --git a/basys3/basys3.srcs/ppu_sprite_fg_tb.vhd b/basys3/basys3.srcs/ppu_sprite_fg_tb.vhd new file mode 100644 index 0000000..19b9f35 --- /dev/null +++ b/basys3/basys3.srcs/ppu_sprite_fg_tb.vhd @@ -0,0 +1,140 @@ +library ieee; +library work; +library unisim; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use unisim.vcomponents.all; +use work.ppu_consts.all; + +entity ppu_sprite_fg_tb is +end ppu_sprite_fg_tb; + +architecture Behavioral of ppu_sprite_fg_tb is + component ppu_sprite_fg -- foreground sprite + generic ( + IDX : natural := 0); -- sprite index number + port( + -- inputs + CLK : in std_logic; -- system clock + RESET : in std_logic; -- reset internal memory and clock counters + OE : in std_logic; -- output enable (of CIDX) + X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x + Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y + FETCH : in std_logic; -- fetch sprite data from TMM + VBLANK : in std_logic; -- fetch during vblank + + -- internal memory block (FAM) + FAM_WEN : in std_logic; -- VRAM FAM write enable + FAM_ADDR : in std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0); -- VRAM fam address + FAM_DATA : in std_logic_vector(PPU_FAM_DATA_WIDTH-1 downto 0); -- VRAM fam data + + -- used memory blocks + TMM_ADDR : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); + TMM_DATA : in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0); + + -- outputs + CIDX : out std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0); -- output color + HIT : out std_logic); -- current pixel is not transparent + end component; + signal CLK : std_logic := '0'; + signal RESET : std_logic := '0'; + signal OE : std_logic := '0'; + signal X : std_logic_vector(PPU_POS_H_WIDTH-1 downto 0) := (others => '0'); + signal Y : std_logic_vector(PPU_POS_V_WIDTH-1 downto 0) := (others => '0'); + signal FETCH : std_logic := '0'; + signal VBLANK : std_logic := '0'; + signal FAM_WEN : std_logic := '0'; + signal FAM_ADDR : std_logic_vector(PPU_FAM_ADDR_WIDTH-1 downto 0) := (others => '0'); + signal FAM_DATA : std_logic_vector(PPU_FAM_DATA_WIDTH-1 downto 0) := (others => '0'); + signal TMM_ADDR : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0); + signal TMM_DATA : std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0) := (others => '0'); + signal CIDX : std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0) := (others => '0'); + signal HIT : std_logic; + + signal CLK_I : unsigned(31 downto 0) := (others => '0'); +begin + uut: component ppu_sprite_fg + generic map( IDX => 2 ) + port map( + CLK => CLK, + RESET => RESET, + OE => OE, + X => X, + Y => Y, + FETCH => FETCH, + VBLANK => VBLANK, + FAM_WEN => FAM_WEN, + FAM_ADDR => FAM_ADDR, + FAM_DATA => FAM_DATA, + TMM_ADDR => TMM_ADDR, + TMM_DATA => TMM_DATA, + CIDX => CIDX, + HIT => HIT); + + tb : process + begin + -- initialize TMM cache + RESET <= '1'; wait for 1 ns; RESET <= '0'; wait for 1 ns; + + OE <= '1'; + VBLANK <= '0'; + FETCH <= '0'; + + -- FAM contents: + -- flip horizontally + -- xy -> (42-16, 8-16) = (26, -8) + -- palette index 2 + -- tilemap index 460 (0x1cc) + -- = hex((1 << 31) | (0 << 30) | (42 << 21) | (8 << 13) | (2 << 10) | (460 << 0)) + FAM_WEN <= '1'; + FAM_ADDR <= std_logic_vector(to_unsigned(16#0005#, FAM_ADDR'length)); + FAM_DATA <= std_logic_vector(to_unsigned(16#8541#, FAM_DATA'length)); + CLK <= '1'; wait for 1 ns; CLK <= '0'; wait for 1 ns; + FAM_ADDR <= std_logic_vector(to_unsigned(16#0004#, FAM_ADDR'length)); + FAM_DATA <= std_logic_vector(to_unsigned(16#09cc#, FAM_DATA'length)); + CLK <= '1'; wait for 1 ns; CLK <= '0'; wait for 1 ns; + FAM_WEN <= '0'; + + for i in 0 to 32 loop + if i = 0 then + X <= std_logic_vector(to_unsigned(25, X'length)); + Y <= std_logic_vector(to_unsigned(60, Y'length)); + end if; + + if i = 3 then + X <= std_logic_vector(to_unsigned(29, X'length)); + Y <= std_logic_vector(to_unsigned(4, Y'length)); + end if; + + wait for 5 ns; CLK <= '1'; wait for 5 ns; CLK <= '0'; + end loop; + + wait for 1 ns; + RESET <= '1'; + wait for 10 ns; + RESET <= '0'; + VBLANK <= '1'; + FETCH <= '1'; + wait for 1 ns; + + -- FETCH check + for i in 0 to 500 loop + CLK_I <= to_unsigned(i, 32); + -- if i > 10 then + -- OE <= '1'; + -- end if; + -- if i > 20 then + -- RESET <= '1'; + -- end if; + + TMM_DATA <= std_logic_vector(to_unsigned(i - 106, TMM_DATA'length)); + + CLK <= '1'; + wait for 1 ns; + CLK <= '0'; + wait for 1 ns; + end loop; + wait; -- stop for simulator + end process; +end Behavioral; diff --git a/basys3/basys3.srcs/ppu_sprite_transform.vhd b/basys3/basys3.srcs/ppu_sprite_transform.vhd new file mode 100644 index 0000000..f94616c --- /dev/null +++ b/basys3/basys3.srcs/ppu_sprite_transform.vhd @@ -0,0 +1,24 @@ +library ieee; +library work; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use work.ppu_consts.all; + +entity ppu_sprite_transform is port( -- flip sprites + XI : in unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- pixel position relative to tile + YI : in unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0); -- pixel position relative to tile + FLIP_H, FLIP_V : in std_logic; -- flip sprite + XO : out unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- new pixel position relative to tile + YO : out unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0)); -- new pixel position relative to tile +end ppu_sprite_transform; + +architecture Behavioral of ppu_sprite_transform is + signal FLIPPED_X : unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); + signal FLIPPED_Y : unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0); +begin + FLIPPED_X <= to_unsigned(PPU_SPRITE_WIDTH-1, PPU_SPRITE_POS_H_WIDTH) - XI; + FLIPPED_Y <= to_unsigned(PPU_SPRITE_HEIGHT-1, PPU_SPRITE_POS_V_WIDTH) - YI; + + XO <= FLIPPED_X when FLIP_V = '1' else XI; + YO <= FLIPPED_Y when FLIP_H = '1' else YI; +end Behavioral; diff --git a/basys3/basys3.srcs/ppu_sprite_transform_tb.vhd b/basys3/basys3.srcs/ppu_sprite_transform_tb.vhd new file mode 100644 index 0000000..516a653 --- /dev/null +++ b/basys3/basys3.srcs/ppu_sprite_transform_tb.vhd @@ -0,0 +1,66 @@ +library ieee; +library unisim; +library work; + +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use unisim.vcomponents.all; +use work.ppu_consts.all; + +entity ppu_sprite_transform_tb is +end ppu_sprite_transform_tb; + +architecture behavioral of ppu_sprite_transform_tb is + component ppu_sprite_transform port( -- flip sprites + XI : in unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- pixel position relative to tile + YI : in unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0); -- pixel position relative to tile + FLIP_H, FLIP_V : in std_logic; -- flip sprite + XO : out unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- new pixel position relative to tile + YO : out unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0)); -- new pixel position relative to tile + end component; + + signal XI : unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); + signal YI : unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0); + signal FLIP_H, FLIP_V : std_logic := '0'; + signal XO : unsigned(PPU_SPRITE_POS_H_WIDTH-1 downto 0); + signal YO : unsigned(PPU_SPRITE_POS_V_WIDTH-1 downto 0); +begin + uut : component ppu_sprite_transform + port map( + XI => XI, + YI => YI, + FLIP_H => FLIP_H, + FLIP_V => FLIP_V, + XO => XO, + YO => YO); + + tb : process + begin + XI <= to_unsigned(4, PPU_SPRITE_POS_H_WIDTH); + YI <= to_unsigned(6, PPU_SPRITE_POS_V_WIDTH); + wait for 5 ns; + + XI <= to_unsigned(2, PPU_SPRITE_POS_H_WIDTH); + YI <= to_unsigned(14, PPU_SPRITE_POS_V_WIDTH); + wait for 5 ns; + + FLIP_H <= '1'; + wait for 5 ns; + + FLIP_V <= '1'; + wait for 5 ns; + + XI <= to_unsigned(6, PPU_SPRITE_POS_H_WIDTH); + YI <= to_unsigned(8, PPU_SPRITE_POS_V_WIDTH); + wait for 5 ns; + + FLIP_H <= '0'; + wait for 5 ns; + + XI <= to_unsigned(2, PPU_SPRITE_POS_H_WIDTH); + YI <= to_unsigned(14, PPU_SPRITE_POS_V_WIDTH); + wait for 5 ns; + + wait; -- stop for simulator + end process; +end; diff --git a/basys3/basys3.srcs/ppu_vga_native.vhd b/basys3/basys3.srcs/ppu_vga_native.vhd new file mode 100644 index 0000000..47288e9 --- /dev/null +++ b/basys3/basys3.srcs/ppu_vga_native.vhd @@ -0,0 +1,95 @@ +library ieee; +use ieee.std_logic_1164.all; +use work.ppu_consts.all; +use ieee.numeric_std.all; +use ieee.std_logic_unsigned.all; + +entity ppu_vga_native is port ( + CLK: in std_logic; -- system clock + RESET: in std_logic; + + X: in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x + Y: in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y + PREADY: in std_logic; -- current pixel ready (pixel color is stable) + RI,GI,BI: in std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- VGA color in + + RO,GO,BO: out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- VGA color out + VSYNC, HSYNC: out std_logic); -- VGA sync outputs +end ppu_vga_native; + +architecture Behavioral of ppu_vga_native is + type line_buffer is array(319 downto 0) of std_logic_vector(11 downto 0); + signal ram_x0 : line_buffer; -- buffer 0 + signal ram_x1: line_buffer; -- buffer 1 + signal hcount: std_logic_vector(9 downto 0):= (others => '0'); + signal vcount: std_logic_vector(9 downto 0):= (others => '0'); + signal clk_counter: std_logic_vector(1 downto 0):= (others => '0'); + signal rgb_out : std_logic_vector(11 downto 0):= (others => '0'); -- output colors + signal px : integer; -- conversion for hcount + signal py :integer; -- conversion for vcount + signal buffer_filled_on_buffer0 : integer; + signal buffer_filled_on_buffer1 : integer; +begin + process (clk, x, y) + variable v_x : integer; -- integer to hold vector X + begin + if rising_edge(clk) then + clk_counter <= clk_counter + 1; + if clk_counter = "11" then + v_x := to_integer(unsigned(x) - 72); + if v_x >= 0 and v_x < 320 and PREADY = '1' then + if y(0) = '0' then + ram_x0(v_x) <= RI & GI & BI; + if v_x = 319 then + buffer_filled_on_buffer0 <= to_integer(unsigned(y) - 14); + end if; + else + ram_x1(v_x) <= RI & GI & BI; + if v_x = 319 then + buffer_filled_on_buffer1 <= to_integer(unsigned(y) - 14); + end if; + end if; + end if; + -- T display(display data) + if (hcount >= 144) and (hcount < 784) and (vcount >= 31) and (vcount < 511) then + px <= to_integer(unsigned(hcount) - 144); + py <= to_integer(unsigned(vcount) - 31); + if buffer_filled_on_buffer0 = (py/2) then + rgb_out <= ram_x0(px/2); + elsif buffer_filled_on_buffer1 = (py/2) then + rgb_out <= ram_x1(px/2); + else + rgb_out <= (others => '0'); + end if; + end if; + -- pulse width + hsync <= '1'; + if hcount < 97 then + hsync <= '0'; + end if; + + vsync <= '1'; + if vcount < 3 then + vsync <= '0'; + end if; + + -- sync pulse time + hcount <= hcount + 1; + + if hcount = 800 then + vcount <= vcount + 1; + hcount <= (others => '0'); + end if; + + if vcount = 521 then + vcount <= (others => '0'); + end if; + end if; + + -- output colors + RO <= rgb_out(11 downto 8); + GO <= rgb_out(7 downto 4); + BO <= rgb_out(3 downto 0); + end if; + end process; +end Behavioral; diff --git a/basys3/basys3.srcs/ppu_vga_native_tb.vhd b/basys3/basys3.srcs/ppu_vga_native_tb.vhd new file mode 100644 index 0000000..06061a0 --- /dev/null +++ b/basys3/basys3.srcs/ppu_vga_native_tb.vhd @@ -0,0 +1,89 @@ +library ieee; +library unisim; +use ieee.std_logic_1164.all; +use work.ppu_consts.all; +use ieee.numeric_std.all; +use ieee.std_logic_unsigned.all; +use unisim.vcomponents.all; + +entity ppu_vga_native_tb is +end ppu_vga_native_tb; + +architecture Behavioral of ppu_vga_native_tb is + component ppu_vga_native port ( + CLK : in std_logic; -- system clock + RESET : in std_logic; + + X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x + Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y + PREADY : in std_logic; -- current pixel ready (pixel color is stable) + RI,GI,BI : in std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- VGA color in + + RO,GO,BO : out std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0); -- VGA color out + VSYNC, HSYNC : out std_logic); -- VGA sync outputs + end component; + signal CLK : std_logic := '0'; + signal RST : std_logic := '0'; + signal PREADY : std_logic := '0'; + + signal X : std_logic_vector(PPU_POS_H_WIDTH-1 downto 0) := (others => '0'); + signal Y : std_logic_vector(PPU_POS_V_WIDTH-1 downto 0) := (others => '0'); + + signal RI,GI,BI : std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0) := (others => '0'); -- VGA color in + signal RO,GO,BO : std_logic_vector(PPU_COLOR_OUTPUT_DEPTH-1 downto 0) := (others => '0'); -- VGA color out + signal VSYNC, HSYNC : std_logic := '0'; + + signal Xas : integer := 72; + signal Yas : integer := 14; + signal counter : std_logic_vector(1 downto 0) := (others => '0'); +begin + uut : ppu_vga_native port map( + CLK => CLK, + RESET => RST, + X => X, + Y => Y, + PREADY => PREADY, + RI => RI, + GI => GI, + BI => BI, + RO => RO, + GO => GO, + BO => BO, + VSYNC => VSYNC, + HSYNC => HSYNC + ); + + tb : process + begin + CLK <= '1'; + wait for 1 ps; + CLK <= '0'; + wait for 1 ps; + end process; + + process(CLK) + begin + if rising_edge(CLK) then + counter <= counter + 1; + end if; + + if(counter = "11") then + pready <= '1'; + ri <= x"d"; + gi <= x"a"; + bi <= x"d"; + x <= std_logic_vector(to_unsigned(Xas, x'length)); + if (Xas = 391) then + Xas <= 72; + y <= std_logic_vector(to_unsigned(Yas, y'length)); + if (Yas = 255) then + Yas <= 14; + else + Yas <= Yas + 1; + end if; + else + Xas <= Xas + 1; + end if; + end if; + end process; +end Behavioral; diff --git a/basys3/basys3.srcs/ppu_vga_tiny.vhd b/basys3/basys3.srcs/ppu_vga_tiny.vhd new file mode 100644 index 0000000..0e496f6 --- /dev/null +++ b/basys3/basys3.srcs/ppu_vga_tiny.vhd @@ -0,0 +1,73 @@ +library ieee; +use ieee.std_logic_1164.all; +use work.ppu_consts.all; +use ieee.numeric_std.all; +use ieee.std_logic_unsigned.all; + +entity ppu_vga_tiny is port ( + CLK : in std_logic; -- system clock + RESET : in std_logic; + + X : out std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x + Y : out std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y + + VSYNC, VBLANK, + HSYNC, HBLANK : out std_logic); -- VGA sync outputs +end ppu_vga_tiny; + +architecture Behavioral of ppu_vga_tiny is + signal hcount : std_logic_vector(PPU_POS_H_WIDTH-1 downto 0) := (others => '0'); + signal vcount : std_logic_vector(PPU_POS_V_WIDTH-1 downto 0) := (others => '0'); + signal clk_counter : std_logic_vector(4 downto 0) := (others => '0'); +begin + process (CLK) + begin + if rising_edge(CLK) then + clk_counter <= clk_counter + 1; + if(clk_counter > 15) then + clk_counter <= (others => '0'); + -- x,y data out + X <= hcount; + Y <= vcount; + + --pulse width + if hcount < 32 or hcount >= 320-80 then + hsync <= '0'; + else + hsync <= '1'; + end if; + + if vcount < 8 or vcount >= 240-15 then + vsync <= '0'; + else + vsync <= '1'; + end if; + + -- Hblank and Vblank outputs + if hcount >= 320-80 then + hblank <= '1'; + else + hblank <= '0'; + end if; + + if vcount >= 240-15 then + vblank <= '1'; + else + vblank <= '0'; + end if; + + -- sync pulse time + hcount <= hcount + 1; + + if hcount = 400 then + vcount <= vcount + 1; + hcount <= (others => '0'); + end if; + + if vcount = 255 then + vcount <= (others => '0'); + end if; + end if; + end if; + end process; +end Behavioral; diff --git a/basys3/basys3.srcs/sources_1/ip/ppu_bam/ppu_bam.xci b/basys3/basys3.srcs/sources_1/ip/ppu_bam/ppu_bam.xci index f5e1696..e299ea1 100644 --- a/basys3/basys3.srcs/sources_1/ip/ppu_bam/ppu_bam.xci +++ b/basys3/basys3.srcs/sources_1/ip/ppu_bam/ppu_bam.xci @@ -163,7 +163,7 @@ "BOARD_CONNECTIONS": [ { "value": "" } ], "DEVICE": [ { "value": "xc7a35t" } ], "PACKAGE": [ { "value": "cpg236" } ], - "PREFHDL": [ { "value": "VERILOG" } ], + "PREFHDL": [ { "value": "VHDL" } ], "SILICON_REVISION": [ { "value": "" } ], "SIMULATOR_LANGUAGE": [ { "value": "MIXED" } ], "SPEEDGRADE": [ { "value": "-1" } ], diff --git a/basys3/basys3.srcs/sources_1/ip/ppu_tmm/ppu_tmm.xci b/basys3/basys3.srcs/sources_1/ip/ppu_tmm/ppu_tmm.xci index 51914b1..9663635 100644 --- a/basys3/basys3.srcs/sources_1/ip/ppu_tmm/ppu_tmm.xci +++ b/basys3/basys3.srcs/sources_1/ip/ppu_tmm/ppu_tmm.xci @@ -14,7 +14,7 @@ "Use_AXI_ID": [ { "value": "false", "resolve_type": "user", "format": "bool", "enabled": false, "usage": "all" } ], "AXI_ID_Width": [ { "value": "4", "resolve_type": "user", "format": "long", "enabled": false, "usage": "all" } ], "Memory_Type": [ { "value": "Single_Port_RAM", "resolve_type": "user", "usage": "all" } ], - "PRIM_type_to_Implement": [ { "value": "BRAM", "resolve_type": "user", "usage": "all" } ], + "PRIM_type_to_Implement": [ { "value": "BRAM", "resolve_type": "user", "enabled": false, "usage": "all" } ], "Enable_32bit_Address": [ { "value": "false", "resolve_type": "user", "format": "bool", "usage": "all" } ], "ecctype": [ { "value": "No_ECC", "resolve_type": "user", "enabled": false, "usage": "all" } ], "ECC": [ { "value": "false", "resolve_type": "user", "format": "bool", "enabled": false, "usage": "all" } ], @@ -32,13 +32,13 @@ "Algorithm": [ { "value": "Minimum_Area", "resolve_type": "user", "usage": "all" } ], "Primitive": [ { "value": "8kx2", "resolve_type": "user", "enabled": false, "usage": "all" } ], "Assume_Synchronous_Clk": [ { "value": "false", "resolve_type": "user", "format": "bool", "enabled": false, "usage": "all" } ], - "Write_Width_A": [ { "value": "16", "resolve_type": "user", "format": "long", "usage": "all" } ], - "Write_Depth_A": [ { "value": "49152", "value_src": "user", "resolve_type": "user", "format": "long", "usage": "all" } ], - "Read_Width_A": [ { "value": "16", "resolve_type": "user", "usage": "all" } ], + "Write_Width_A": [ { "value": "15", "value_src": "user", "resolve_type": "user", "format": "long", "usage": "all" } ], + "Write_Depth_A": [ { "value": "53248", "value_src": "user", "resolve_type": "user", "format": "long", "usage": "all" } ], + "Read_Width_A": [ { "value": "15", "resolve_type": "user", "usage": "all" } ], "Operating_Mode_A": [ { "value": "WRITE_FIRST", "resolve_type": "user", "usage": "all" } ], "Enable_A": [ { "value": "Always_Enabled", "value_src": "user", "resolve_type": "user", "usage": "all" } ], - "Write_Width_B": [ { "value": "16", "resolve_type": "user", "enabled": false, "usage": "all" } ], - "Read_Width_B": [ { "value": "16", "resolve_type": "user", "enabled": false, "usage": "all" } ], + "Write_Width_B": [ { "value": "15", "resolve_type": "user", "enabled": false, "usage": "all" } ], + "Read_Width_B": [ { "value": "15", "resolve_type": "user", "enabled": false, "usage": "all" } ], "Operating_Mode_B": [ { "value": "WRITE_FIRST", "resolve_type": "user", "enabled": false, "usage": "all" } ], "Enable_B": [ { "value": "Always_Enabled", "resolve_type": "user", "enabled": false, "usage": "all" } ], "Register_PortA_Output_of_Memory_Primitives": [ { "value": "true", "resolve_type": "user", "format": "bool", "usage": "all" } ], @@ -110,10 +110,10 @@ "C_USE_BYTE_WEA": [ { "value": "0", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_WEA_WIDTH": [ { "value": "1", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_WRITE_MODE_A": [ { "value": "WRITE_FIRST", "resolve_type": "generated", "usage": "all" } ], - "C_WRITE_WIDTH_A": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ], - "C_READ_WIDTH_A": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ], - "C_WRITE_DEPTH_A": [ { "value": "49152", "resolve_type": "generated", "format": "long", "usage": "all" } ], - "C_READ_DEPTH_A": [ { "value": "49152", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_WRITE_WIDTH_A": [ { "value": "15", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_READ_WIDTH_A": [ { "value": "15", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_WRITE_DEPTH_A": [ { "value": "53248", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_READ_DEPTH_A": [ { "value": "53248", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_ADDRA_WIDTH": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_HAS_RSTB": [ { "value": "0", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_RST_PRIORITY_B": [ { "value": "CE", "resolve_type": "generated", "usage": "all" } ], @@ -124,10 +124,10 @@ "C_USE_BYTE_WEB": [ { "value": "0", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_WEB_WIDTH": [ { "value": "1", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_WRITE_MODE_B": [ { "value": "WRITE_FIRST", "resolve_type": "generated", "usage": "all" } ], - "C_WRITE_WIDTH_B": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ], - "C_READ_WIDTH_B": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ], - "C_WRITE_DEPTH_B": [ { "value": "49152", "resolve_type": "generated", "format": "long", "usage": "all" } ], - "C_READ_DEPTH_B": [ { "value": "49152", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_WRITE_WIDTH_B": [ { "value": "15", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_READ_WIDTH_B": [ { "value": "15", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_WRITE_DEPTH_B": [ { "value": "53248", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_READ_DEPTH_B": [ { "value": "53248", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_ADDRB_WIDTH": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_HAS_MEM_OUTPUT_REGS_A": [ { "value": "1", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_HAS_MEM_OUTPUT_REGS_B": [ { "value": "0", "resolve_type": "generated", "format": "long", "usage": "all" } ], @@ -153,9 +153,9 @@ "C_EN_SHUTDOWN_PIN": [ { "value": "0", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_EN_SAFETY_CKT": [ { "value": "1", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_DISABLE_WARN_BHV_RANGE": [ { "value": "0", "resolve_type": "generated", "format": "long", "usage": "all" } ], - "C_COUNT_36K_BRAM": [ { "value": "22", "resolve_type": "generated", "usage": "all" } ], - "C_COUNT_18K_BRAM": [ { "value": "1", "resolve_type": "generated", "usage": "all" } ], - "C_EST_POWER_SUMMARY": [ { "value": "Estimated Power for IP : 16.534446 mW", "resolve_type": "generated", "usage": "all" } ] + "C_COUNT_36K_BRAM": [ { "value": "19", "resolve_type": "generated", "usage": "all" } ], + "C_COUNT_18K_BRAM": [ { "value": "8", "resolve_type": "generated", "usage": "all" } ], + "C_EST_POWER_SUMMARY": [ { "value": "Estimated Power for IP : 13.861152 mW", "resolve_type": "generated", "usage": "all" } ] }, "project_parameters": { "ARCHITECTURE": [ { "value": "artix7" } ], @@ -163,7 +163,7 @@ "BOARD_CONNECTIONS": [ { "value": "" } ], "DEVICE": [ { "value": "xc7a35t" } ], "PACKAGE": [ { "value": "cpg236" } ], - "PREFHDL": [ { "value": "VERILOG" } ], + "PREFHDL": [ { "value": "VHDL" } ], "SILICON_REVISION": [ { "value": "" } ], "SIMULATOR_LANGUAGE": [ { "value": "MIXED" } ], "SPEEDGRADE": [ { "value": "-1" } ], @@ -189,8 +189,8 @@ "rsta": [ { "direction": "in", "driver_value": "0" } ], "wea": [ { "direction": "in", "size_left": "0", "size_right": "0", "driver_value": "0" } ], "addra": [ { "direction": "in", "size_left": "15", "size_right": "0", "driver_value": "0" } ], - "dina": [ { "direction": "in", "size_left": "15", "size_right": "0", "driver_value": "0" } ], - "douta": [ { "direction": "out", "size_left": "15", "size_right": "0" } ], + "dina": [ { "direction": "in", "size_left": "14", "size_right": "0", "driver_value": "0" } ], + "douta": [ { "direction": "out", "size_left": "14", "size_right": "0" } ], "rsta_busy": [ { "direction": "out" } ] }, "interfaces": { diff --git a/basys3/basys3.xpr b/basys3/basys3.xpr index 466cc1f..4060a21 100644 --- a/basys3/basys3.xpr +++ b/basys3/basys3.xpr @@ -42,8 +42,10 @@ <Option Name="SimulatorGccVersionVCS" Val="9.2.0"/> <Option Name="SimulatorGccVersionRiviera" Val="9.3.0"/> <Option Name="SimulatorGccVersionActiveHdl" Val="9.3.0"/> + <Option Name="TargetLanguage" Val="VHDL"/> <Option Name="BoardPart" Val="digilentinc.com:basys3:part0:1.2"/> <Option Name="BoardPartRepoPaths" Val="$PPRDIR/../../../../.Xilinx/Vivado/2022.2/xhub/board_store/xilinx_board_store"/> + <Option Name="SourceMgmtMode" Val="DisplayOnly"/> <Option Name="ActiveSimSet" Val="sim_1"/> <Option Name="DefaultLib" Val="xil_defaultlib"/> <Option Name="ProjectType" Val="Default"/> @@ -59,20 +61,20 @@ <Option Name="IPStaticSourceDir" Val="$PIPUSERFILESDIR/ipstatic"/> <Option Name="EnableBDX" Val="FALSE"/> <Option Name="DSABoardId" Val="basys3"/> - <Option Name="WTXSimLaunchSim" Val="12"/> + <Option Name="WTXSimLaunchSim" Val="113"/> <Option Name="WTModelSimLaunchSim" Val="0"/> <Option Name="WTQuestaLaunchSim" Val="0"/> <Option Name="WTIesLaunchSim" Val="0"/> <Option Name="WTVcsLaunchSim" Val="0"/> <Option Name="WTRivieraLaunchSim" Val="0"/> <Option Name="WTActivehdlLaunchSim" Val="0"/> - <Option Name="WTXSimExportSim" Val="3"/> - <Option Name="WTModelSimExportSim" Val="3"/> - <Option Name="WTQuestaExportSim" Val="3"/> + <Option Name="WTXSimExportSim" Val="4"/> + <Option Name="WTModelSimExportSim" Val="4"/> + <Option Name="WTQuestaExportSim" Val="4"/> <Option Name="WTIesExportSim" Val="0"/> - <Option Name="WTVcsExportSim" Val="3"/> - <Option Name="WTRivieraExportSim" Val="3"/> - <Option Name="WTActivehdlExportSim" Val="3"/> + <Option Name="WTVcsExportSim" Val="4"/> + <Option Name="WTRivieraExportSim" Val="4"/> + <Option Name="WTActivehdlExportSim" Val="4"/> <Option Name="GenerateIPUpgradeLog" Val="TRUE"/> <Option Name="XSimRadix" Val="hex"/> <Option Name="XSimTimeUnit" Val="ns"/> @@ -96,20 +98,20 @@ <Attr Name="UsedIn" Val="simulation"/> </FileInfo> </File> - <File Path="$PSRCDIR/ppu_addr_dec.vhdl"> - <FileInfo> + <File Path="$PSRCDIR/ppu_pceg.vhd"> + <FileInfo SFType="VHDL2008"> <Attr Name="UsedIn" Val="synthesis"/> <Attr Name="UsedIn" Val="simulation"/> </FileInfo> </File> - <File Path="$PSRCDIR/ppu_pceg.vhdl"> - <FileInfo> + <File Path="$PSRCDIR/ppu.vhd"> + <FileInfo SFType="VHDL2008"> <Attr Name="UsedIn" Val="synthesis"/> <Attr Name="UsedIn" Val="simulation"/> </FileInfo> </File> - <File Path="$PSRCDIR/ppu.vhd"> - <FileInfo> + <File Path="$PSRCDIR/ppu_addr_dec.vhd"> + <FileInfo SFType="VHDL2008"> <Attr Name="UsedIn" Val="synthesis"/> <Attr Name="UsedIn" Val="simulation"/> </FileInfo> @@ -117,7 +119,6 @@ <File Path="$PSRCDIR/apu.vhd"> <FileInfo> <Attr Name="UserDisabled" Val="1"/> - <Attr Name="AutoDisabled" Val="1"/> <Attr Name="UsedIn" Val="synthesis"/> <Attr Name="UsedIn" Val="simulation"/> </FileInfo> @@ -125,7 +126,73 @@ <File Path="$PSRCDIR/apu_note_to_frequency.vhd"> <FileInfo> <Attr Name="UserDisabled" Val="1"/> - <Attr Name="AutoDisabled" Val="1"/> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="simulation"/> + </FileInfo> + </File> + <File Path="$PSRCDIR/er_ram.vhd"> + <FileInfo SFType="VHDL2008"> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="simulation"/> + </FileInfo> + </File> + <File Path="$PSRCDIR/er_ram_mod.vhd"> + <FileInfo SFType="VHDL2008"> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="simulation"/> + </FileInfo> + </File> + <File Path="$PSRCDIR/ppu_aux.vhd"> + <FileInfo SFType="VHDL2008"> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="simulation"/> + </FileInfo> + </File> + <File Path="$PSRCDIR/ppu_sprite_bg.vhd"> + <FileInfo> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="simulation"/> + </FileInfo> + </File> + <File Path="$PSRCDIR/ppu_sprite_transform.vhd"> + <FileInfo> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="simulation"/> + </FileInfo> + </File> + <File Path="$PSRCDIR/ppu_sprite_fg.vhd"> + <FileInfo SFType="VHDL2008"> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="simulation"/> + </FileInfo> + </File> + <File Path="$PSRCDIR/ppu_vga_tiny.vhd"> + <FileInfo> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="simulation"/> + </FileInfo> + </File> + <File Path="$PSRCDIR/ppu_comp.vhd"> + <FileInfo> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="simulation"/> + </FileInfo> + </File> + <File Path="$PSRCDIR/ppu_plut.vhd"> + <FileInfo> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="simulation"/> + </FileInfo> + </File> + <File Path="$PSRCDIR/ppu_vga_native.vhd"> + <FileInfo> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="simulation"/> + </FileInfo> + </File> + <File Path="$PSRCDIR/apu_lut_reader.vhd"> + <FileInfo> + <Attr Name="UserDisabled" Val="1"/> <Attr Name="UsedIn" Val="synthesis"/> <Attr Name="UsedIn" Val="simulation"/> </FileInfo> @@ -144,15 +211,14 @@ </FileSet> <FileSet Name="sim_1" Type="SimulationSrcs" RelSrcDir="$PSRCDIR/sim_1" RelGenDir="$PGENDIR/sim_1"> <Filter Type="Srcs"/> - <File Path="$PSRCDIR/ppu_addr_dec_tb.vhdl"> + <File Path="$PSRCDIR/ppu_addr_dec_tb.vhd"> <FileInfo> <Attr Name="UsedIn" Val="synthesis"/> <Attr Name="UsedIn" Val="simulation"/> </FileInfo> </File> - <File Path="$PSRCDIR/ppu_pceg_tb.vhdl"> - <FileInfo> - <Attr Name="AutoDisabled" Val="1"/> + <File Path="$PSRCDIR/ppu_pceg_tb.vhd"> + <FileInfo SFType="VHDL2008"> <Attr Name="UsedIn" Val="synthesis"/> <Attr Name="UsedIn" Val="simulation"/> </FileInfo> @@ -160,14 +226,49 @@ <File Path="$PSRCDIR/apu_note_to_frequency_tb.vhd"> <FileInfo> <Attr Name="UserDisabled" Val="1"/> - <Attr Name="AutoDisabled" Val="1"/> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="simulation"/> + </FileInfo> + </File> + <File Path="$PSRCDIR/er_ram_mod_tb.vhd"> + <FileInfo SFType="VHDL2008"> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="simulation"/> + </FileInfo> + </File> + <File Path="$PSRCDIR/er_ram_tb.vhd"> + <FileInfo SFType="VHDL2008"> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="simulation"/> + </FileInfo> + </File> + <File Path="$PSRCDIR/ppu_aux_tb.vhd"> + <FileInfo> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="simulation"/> + </FileInfo> + </File> + <File Path="$PSRCDIR/ppu_sprite_bg_tb.vhd"> + <FileInfo SFType="VHDL2008"> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="simulation"/> + </FileInfo> + </File> + <File Path="$PSRCDIR/ppu_sprite_transform_tb.vhd"> + <FileInfo> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="simulation"/> + </FileInfo> + </File> + <File Path="$PSRCDIR/ppu_sprite_fg_tb.vhd"> + <FileInfo SFType="VHDL2008"> <Attr Name="UsedIn" Val="synthesis"/> <Attr Name="UsedIn" Val="simulation"/> </FileInfo> </File> <Config> <Option Name="DesignMode" Val="RTL"/> - <Option Name="TopModule" Val="ppu_addr_dec_tb"/> + <Option Name="TopModule" Val="ppu_sprite_fg_tb"/> <Option Name="TopLib" Val="xil_defaultlib"/> <Option Name="TransportPathDelay" Val="0"/> <Option Name="TransportIntDelay" Val="0"/> @@ -177,10 +278,19 @@ <Option Name="PamSignalDriverFile" Val="xil_bypass_driver"/> <Option Name="PamPseudoTop" Val="pseudo_tb"/> <Option Name="SrcSet" Val="sources_1"/> + <Option Name="NLNetlistMode" Val="funcsim"/> </Config> </FileSet> <FileSet Name="utils_1" Type="Utils" RelSrcDir="$PSRCDIR/utils_1" RelGenDir="$PGENDIR/utils_1"> <Filter Type="Utils"/> + <File Path="$PSRCDIR/utils_1/imports/synth_1/ppu.dcp"> + <FileInfo> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="implementation"/> + <Attr Name="UsedInSteps" Val="synth_1"/> + <Attr Name="AutoDcp" Val="1"/> + </FileInfo> + </File> <Config> <Option Name="TopAutoSet" Val="TRUE"/> </Config> @@ -234,11 +344,12 @@ </Simulator> </Simulators> <Runs Version="1" Minor="19"> - <Run Id="synth_1" Type="Ft3:Synth" SrcSet="sources_1" Part="xc7a35tcpg236-1" ConstrsSet="constrs_1" Description="Vivado Synthesis Defaults" AutoIncrementalCheckpoint="true" WriteIncrSynthDcp="false" State="current" IncludeInArchive="true" IsChild="false" AutoIncrementalDir="$PSRCDIR/utils_1/imports/synth_1" AutoRQSDir="$PSRCDIR/utils_1/imports/synth_1"> + <Run Id="synth_1" Type="Ft3:Synth" SrcSet="sources_1" Part="xc7a35tcpg236-1" ConstrsSet="constrs_1" Description="Vivado Synthesis Defaults" AutoIncrementalCheckpoint="true" IncrementalCheckpoint="$PSRCDIR/utils_1/imports/synth_1/ppu.dcp" WriteIncrSynthDcp="false" State="current" Dir="$PRUNDIR/synth_1" IncludeInArchive="true" IsChild="false" AutoIncrementalDir="$PSRCDIR/utils_1/imports/synth_1" AutoRQSDir="$PSRCDIR/utils_1/imports/synth_1"> <Strategy Version="1" Minor="2"> <StratHandle Name="Vivado Synthesis Defaults" Flow="Vivado Synthesis 2022"/> <Step Id="synth_design"/> </Strategy> + <GeneratedRun Dir="$PRUNDIR" File="gen_run.xml"/> <ReportStrategy Name="Vivado Synthesis Default Reports" Flow="Vivado Synthesis 2022"/> <Report Name="ROUTE_DESIGN.REPORT_METHODOLOGY" Enabled="1"/> <RQSFiles/> diff --git a/docs/architecture.md b/docs/architecture.md index a60e0da..0c6fea7 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -134,6 +134,12 @@ Notable differences: Our game doesn't need this capability for any visual effects. Leaving this feature out will lead to a simpler hardware design +- Sprites are positioned relative to the viewport, not the background layer + + This leads to a simpler hardware architecture for the foreground sprite + rendering component. Since the CPU is already likely to reposition all + foreground sprites on every frame, the position calculation is moved to + hardware to software. ## Hardware design schematics @@ -227,16 +233,17 @@ Important notes: - The PPU's memory bus has 16-bit addresses and 16-bit words. - Some memory regions use physical word sizes smaller than 16-bits, so "unneeded" bits will be discarded by the PPU. -- All "fields" or words containing multiple bit strings representing different - pieces of information are ordered from MSB to LSB. +- Apparent size means the amount of addresses in a given memory region. As + mentioned earlier, the exact word sizes of a memory area can vary, though + this is not visible to the CPU as all data is presented as 16-bit words. -|Address offset|Size (16-bit words)|Alias|Description| +|Address offset|Apparent size|Alias|Description| |-|-|-|-| -|`0x0000`|`0xc000`|TMM |[tilemap memory][TMM]| -|`0xc000`|`0x04b0`|BAM |[background attribute memory][BAM]| -|`0xc800`|`0x0100`|FAM |[foreground attribute memory][FAM]| -|`0xcc00`|`0x0040`|PAL |[palettes][PAL]| -|`0xce00`|`0x0002`|AUX |[auxiliary memory][AUX]| +|`0x0000`|`0xd000`|TMM |[tilemap memory][TMM]| +|`0xd000`|`0x04b0`|BAM |[background attribute memory][BAM]| +|`0xd800`|`0x0100`|FAM |[foreground attribute memory][FAM]| +|`0xdc00`|`0x0040`|PAL |[palettes][PAL]| +|`0xde00`|`0x0002`|AUX |[auxiliary memory][AUX]| This table contains the "official" PPU register offsets and sizes. Due to the way the address decoder works, some of these memory regions might be duplicated @@ -247,24 +254,44 @@ there is no address validity checking. [TMM]: #tilemap-memory ### Tilemap memory -- Each sprite takes up 768 bits spread across 48 16-bit words -- Sprites and pixels are stored adjacently in memory without padding -- Pixel order is from top-left to bottom-right in (English) reading order. +- Each sprite takes up 768 bits spread across 52 15-bit words (with one + discarded padding bit per word) +- Pixel index order is from top-left to bottom-right in (English) reading + order. +- Bits `14 downto 3` of the byte with the highest address for a given tile are + not used +- To calculate TMM address $a$ for any given pixel $p$ of tile with index $t$, + compute $a=52*t+\left\lfloor\frac{p}{5}\right\rfloor$ + +Word format: + +|Range (VHDL)|Description| +|-|-| +|`15`|(discarded)| +|`14 downto 12`|pixel $n+4$| +|`11 downto 9`|pixel $n+3$| +|`8 downto 6`|pixel $n+2$| +|`5 downto 3`|pixel $n+1$| +|`2 downto 0`|pixel $n+0$| + [BAM]: #background-attribute-memory ### Background attribute memory - 15-bit words (MSB discarded in hardware) -- Address indicates which background sprite is currently targeted in reading order - e.g. $\textrm{addr} = c000_{\textrm{hex}} + x + y*w$ where $x$ and $y$ are the background tile, and $w$ is the amount of horizontal tiles fit on the background layer (40) +- Address indicates which background sprite is currently targeted in reading + order + e.g. $\textrm{addr} = c000_{\textrm{hex}} + x + y*w$ where $x$ and $y$ + are the background tile, and $w$ is the amount of horizontal tiles fit on the + background layer (40) Word format: |Range (VHDL)|Description| |-|-| -|`15`|Flip horizontally| -|`14`|Flip vertically| -|`13`|(unused)| +|`15`|(discarded)| +|`14`|Flip horizontally| +|`13`|Flip vertically| |`12 downto 10`|Palette index for tile| |`9 downto 0`|Tilemap index| @@ -30,12 +30,14 @@ before formatting as a failsafe. - custom typedefs are prefixed with `hh_` and suffixed with `_t` (e.g. `hh_bam_tile_t`) - library hooks that need specific symbol names are exempt from the naming conventions (e.g. `main` or `HAL_UART_MspInit`) +- names are always in English ## others - document **how to use** code using doxygen-style comments in headers - document **what code is doing** using inline comments (where applicable) - don't write redundant comments (e.g. `int c = a + b; // add a and b`) +- comments are always in English ### markdown @@ -50,4 +52,31 @@ before formatting as a failsafe. - use lower case keywords - testbench name is the component name with `_tb` as suffix - vhdl filename is the same as the component name - +- vhdl files should end in the `.vhd` file extension, not `.vhdl` +- use spaces around the colon used for setting the type of signal definitions +- do not mix uppercase and lowercase in entity/component/port/signal names +- remove the default vivado file headers (large comment blocks with file info) +- add a comment to the entity definition describing what the entity does +- library import names should be lowercase +- with the exception of off by one shifts, absolutely NO magic numbers are + allowed. use existing constants if possible, or create new ones in a consts + file instead of using magic numbers. +- entities/components without a generic map should put the opening `port (` on + the same line as the `entity ... is ` line +- entities/components with a generic map should be formatted like this: + ``` + entity ... is + generic map( + NAME : type := default; + NAME : type := default) + port map( + NAME : in type; + NAME : out type); + end ...; + ``` +- port/generic maps with only one signal should be condensed into a single line +- all library declarations should be grouped together at the top of the file +- all `use` directives should be grouped together below the library + declarations +- use a space before and after the comment syntax, e.g. `variable x : integer + := 0; -- comment here` diff --git a/test/sdl2/.gitignore b/test/sdl2/.gitignore new file mode 100644 index 0000000..f0c9b81 --- /dev/null +++ b/test/sdl2/.gitignore @@ -0,0 +1,2 @@ +*.o +main diff --git a/test/sdl2/main.c b/test/sdl2/main.c new file mode 100644 index 0000000..beddc3f --- /dev/null +++ b/test/sdl2/main.c @@ -0,0 +1,48 @@ +#include <stdlib.h> +#include <stdbool.h> +#include <math.h> +#include <SDL2/SDL.h> + +#define UPSCALE_FACTOR 3 +#define WIN_HEIGHT 240 +#define WIN_WIDTH 320 +#define FPS 60 + +static void pixel(SDL_Renderer* r, unsigned px, unsigned py, unsigned cr, unsigned cg, unsigned cb) { + SDL_SetRenderDrawColor(r, cr, cg, cb, 255); + SDL_RenderFillRect(r, &(SDL_Rect) {.x = px * UPSCALE_FACTOR, .y = py * UPSCALE_FACTOR, .w = UPSCALE_FACTOR, .h = UPSCALE_FACTOR}); +} + +int main() { + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); + SDL_Window *window = SDL_CreateWindow("sdl2 test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, WIN_WIDTH * UPSCALE_FACTOR, WIN_HEIGHT * UPSCALE_FACTOR, SDL_WINDOW_SHOWN); + SDL_Renderer *renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); + + SDL_Event e; + unsigned long frame = 0; + while (true) { + uint32_t start = SDL_GetTicks(); + frame++; + while (SDL_PollEvent(&e)) if (e.type == SDL_QUIT) exit(0); + + SDL_RenderClear(renderer); + + for (unsigned i = 0; i < WIN_WIDTH; i++) { + for (unsigned j = 0; j < WIN_HEIGHT; j++) { + pixel(renderer, i, j, (unsigned)(sqrt(i * 20) + frame) % 255, (i * j + frame) % 255, (j * 20) % 255); + } + } + + SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); + SDL_RenderPresent(renderer); + + uint32_t end = SDL_GetTicks(); + int wait_for = 1000 / FPS - (end - start); + if (wait_for > 0) SDL_Delay(wait_for); + } + + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); + return 0; +} diff --git a/test/sdl2/makefile b/test/sdl2/makefile new file mode 100644 index 0000000..4c26dc3 --- /dev/null +++ b/test/sdl2/makefile @@ -0,0 +1,18 @@ +CC = gcc +LD = gcc +RM = rm -f +TARGET = main +LFLAGS += -lSDL2 -lm +OBJS += main.o + +all: $(TARGET) + +%.o: %.c + $(CC) -c $(CFLAGS) $< -o $@ + +$(TARGET): $(OBJS) + $(LD) $^ $(LFLAGS) -o $@ + +clean: + $(RM) $(TARGET) $(OBJS) + diff --git a/test/sdl2/readme.md b/test/sdl2/readme.md new file mode 100644 index 0000000..940e315 --- /dev/null +++ b/test/sdl2/readme.md @@ -0,0 +1,6 @@ +# sdl2 test + +this is a simple POC that makes a window to draw PPU graphics to using SDL2. +this code will be moved to the stm32/ppu directory, and the stm32's makefile +will be edited to include a sim target for building the game for desktop +debugging. |