aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--basys3/basys3.srcs/ppu_consts.vhd4
-rw-r--r--basys3/basys3.srcs/ppu_sprite_bg.vhd150
-rw-r--r--basys3/basys3.srcs/ppu_sprite_bg_tb.vhd86
-rw-r--r--basys3/basys3.srcs/ppu_sprite_transform.vhd24
-rw-r--r--basys3/basys3.xpr16
5 files changed, 277 insertions, 3 deletions
diff --git a/basys3/basys3.srcs/ppu_consts.vhd b/basys3/basys3.srcs/ppu_consts.vhd
index f4d428e..89b7a7a 100644
--- a/basys3/basys3.srcs/ppu_consts.vhd
+++ b/basys3/basys3.srcs/ppu_consts.vhd
@@ -21,12 +21,14 @@ package ppu_consts is
constant PPU_POS_V_WIDTH : natural := 8; -- amount of 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_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; -- amount of tiles (horizontally) on background canvas
constant PPU_BG_CANVAS_TILES_V : natural := 30; -- amount of tiles (vertically) on background canvas
constant PPU_TILE_INDEX_WIDTH : natural := 10; -- amount of bits needed to index a tile from TMM memory
constant PPU_PIXELS_PER_TILE_WORD : natural := 5; -- amount of pixels defined in one word in TMM memory
- constant PPU_TILE_SIZE : natural := 48;
+ constant PPU_SPRITE_PIXELS_PER_WORD : natural := 52; -- amount of words needed for a single sprite
end package ppu_consts;
diff --git a/basys3/basys3.srcs/ppu_sprite_bg.vhd b/basys3/basys3.srcs/ppu_sprite_bg.vhd
new file mode 100644
index 0000000..a92c401
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_sprite_bg.vhd
@@ -0,0 +1,150 @@
+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;
+
+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 std_logic_vector(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- pixel position relative to tile
+ YI : in std_logic_vector(PPU_SPRITE_POS_V_WIDTH-1 downto 0); -- pixel position relative to tile
+ FLIP_H, FLIP_V : in std_logic; -- flip sprite
+ XO : out std_logic_vector(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- new pixel position relative to tile
+ YO : out std_logic_vector(PPU_SPRITE_POS_V_WIDTH-1 downto 0)); -- new pixel position relative to tile
+ end component;
+
+ signal O_BAM_ADDR : std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0) := (others => '0');
+ signal I_BAM_DATA : std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0);
+ signal O_TMM_ADDR : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0) := (others => '0');
+ signal I_TMM_DATA : std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0);
+
+ signal FLIP_H, FLIP_V : std_logic := '0';
+ signal TMM_IDX : std_logic_vector(PPU_TILE_INDEX_WIDTH-1 downto 0) := (others => '0');
+
+ alias BAM_DATA_COL_IDX is I_BAM_DATA(12 downto 10);
+ signal TMM_DATA_PAL_IDX : std_logic_vector(PPU_PALETTE_COLOR_WIDTH-1 downto 0);
+ signal O_CIDX : std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0) := (others => '0');
+
+ type states is (PL_BAM_ADDR, PL_BAM_DATA, PL_TMM_ADDR, PL_TMM_DATA);
+ signal state, next_state : states := PL_BAM_ADDR;
+
+ -- docs/architecture.md#background-attribute-memory
+ alias BAM_DATA_FLIP_H is I_BAM_DATA(14);
+ alias BAM_DATA_FLIP_V is I_BAM_DATA(13);
+ alias BAM_DATA_TILE_IDX is I_BAM_DATA(9 downto 0);
+
+ 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_PIXEL_IDX_X, TILE_PIXEL_IDX_Y : integer := 0; -- xy position of pixel within tile
+ signal TILE_PIXEL_IDX : integer := 0; -- index of pixel within tile
+ signal TRANS_TILE_PIDX_X, TRANS_TILE_PIDX_Y : integer := 0; -- transformed xy position of pixel within tile
+ signal TILEMAP_WORD_OFFSET : integer := 0;
+ signal TRANSFORM_XI, TRANSFORM_XO : std_logic_vector(PPU_SPRITE_POS_H_WIDTH-1 downto 0);
+ signal TRANSFORM_YI, TRANSFORM_YO : std_logic_vector(PPU_SPRITE_POS_V_WIDTH-1 downto 0);
+ signal PIXEL_BIT_OFFSET : integer := 0;
+begin
+ -- CIDX tri-state driver
+ CIDX <= O_CIDX when OE = '1' else (others => 'Z');
+
+ -- internal line separators
+ FLIP_H <= BAM_DATA_FLIP_H;
+ FLIP_V <= BAM_DATA_FLIP_V;
+ O_CIDX <= BAM_DATA_COL_IDX & TMM_DATA_PAL_IDX;
+
+ -- BAM ADDR
+ PIXEL_ABS_X <= to_integer(unsigned(X)) + to_integer(unsigned(BG_SHIFT_X));
+ PIXEL_ABS_Y <= to_integer(unsigned(Y)) + to_integer(unsigned(BG_SHIFT_Y));
+ TILE_IDX_X <= PIXEL_ABS_X / 16;
+ TILE_IDX_Y <= PIXEL_ABS_Y / 16;
+ TILE_PIXEL_IDX_X <= PIXEL_ABS_X - TILE_IDX_X * 16;
+ TILE_PIXEL_IDX_Y <= PIXEL_ABS_Y - TILE_IDX_Y * 16;
+ O_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 + FAM ADDR
+ TRANSFORM_XI <= std_logic_vector(to_unsigned(TILE_PIXEL_IDX_X, PPU_SPRITE_POS_H_WIDTH));
+ TRANSFORM_YI <= std_logic_vector(to_unsigned(TILE_PIXEL_IDX_Y, PPU_SPRITE_POS_V_WIDTH));
+ transform: component ppu_sprite_transform port map(
+ XI => TRANSFORM_XI,
+ YI => TRANSFORM_YI,
+ FLIP_H => FLIP_H,
+ FLIP_V => FLIP_V,
+ XO => TRANSFORM_XO,
+ YO => TRANSFORM_YO);
+ TRANS_TILE_PIDX_X <= to_integer(unsigned(TRANSFORM_XO));
+ TRANS_TILE_PIDX_Y <= to_integer(unsigned(TRANSFORM_YO));
+
+ TILE_PIXEL_IDX <= integer(PPU_SPRITE_WIDTH) * TRANS_TILE_PIDX_Y + TRANS_TILE_PIDX_X;
+ TILEMAP_WORD_OFFSET <= TILE_PIXEL_IDX / PPU_PIXELS_PER_TILE_WORD;
+ PIXEL_BIT_OFFSET <= TILE_PIXEL_IDX mod PPU_PIXELS_PER_TILE_WORD;
+
+ O_TMM_ADDR <= std_logic_vector(to_unsigned(PPU_SPRITE_PIXELS_PER_WORD * to_integer(unsigned(BAM_DATA_TILE_IDX)) + TILEMAP_WORD_OFFSET, PPU_TMM_ADDR_WIDTH));
+
+ -- TMM DATA
+ with PIXEL_BIT_OFFSET select
+ TMM_DATA_PAL_IDX <= TMM_DATA(2 downto 0) when 0,
+ TMM_DATA(5 downto 3) when 1,
+ TMM_DATA(8 downto 6) when 2,
+ TMM_DATA(11 downto 9) when 3,
+ TMM_DATA(14 downto 12) when 4,
+ (others => '0') when others;
+
+ -- state machine (pipeline stage counter)
+ fsm: process(CLK, RESET)
+ begin
+ if RESET = '1' then
+ state <= PL_BAM_ADDR;
+ elsif rising_edge(CLK) then
+ state <= next_state;
+ end if;
+ end process;
+
+ -- sync read/write
+ process(state)
+ begin
+ next_state <= state;
+ case state is
+ when PL_BAM_ADDR =>
+ next_state <= PL_BAM_DATA;
+ BAM_ADDR <= O_BAM_ADDR;
+
+ when PL_BAM_DATA =>
+ next_state <= PL_TMM_ADDR;
+ I_BAM_DATA <= BAM_DATA;
+
+ when PL_TMM_ADDR =>
+ next_state <= PL_TMM_DATA;
+ TMM_ADDR <= O_TMM_ADDR;
+
+ when PL_TMM_DATA =>
+ next_state <= PL_BAM_ADDR;
+ I_TMM_DATA <= TMM_DATA;
+
+
+ end case;
+ 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..2dee2fe
--- /dev/null
+++ b/basys3/basys3.srcs/ppu_sprite_bg_tb.vhd
@@ -0,0 +1,86 @@
+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);
+
+ BAM_DATA <= std_logic_vector(to_unsigned(16#4814#, PPU_BAM_DATA_WIDTH)); -- hex((1 << 14) | (0 << 13) | (2 << 10) | (20 << 0))
+ TMM_DATA <= std_logic_vector(to_unsigned(16#ffff#, PPU_TMM_DATA_WIDTH));
+ 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));
+
+ 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_transform.vhd b/basys3/basys3.srcs/ppu_sprite_transform.vhd
new file mode 100644
index 0000000..d1b1a23
--- /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 std_logic_vector(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- pixel position relative to tile
+ YI : in std_logic_vector(PPU_SPRITE_POS_V_WIDTH-1 downto 0); -- pixel position relative to tile
+ FLIP_H, FLIP_V : in std_logic; -- flip sprite
+ XO : out std_logic_vector(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- new pixel position relative to tile
+ YO : out std_logic_vector(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 : std_logic_vector(PPU_SPRITE_POS_H_WIDTH-1 downto 0);
+ signal FLIPPED_Y : std_logic_vector(PPU_SPRITE_POS_V_WIDTH-1 downto 0);
+begin
+ FLIPPED_X <= std_logic_vector(to_unsigned(PPU_SPRITE_WIDTH-1 - to_integer(unsigned(XI)), PPU_SPRITE_POS_H_WIDTH));
+ FLIPPED_Y <= std_logic_vector(to_unsigned(PPU_SPRITE_HEIGHT-1 - to_integer(unsigned(YI)), PPU_SPRITE_POS_V_WIDTH));
+
+ XO <= FLIPPED_X when FLIP_H = '1' else XI;
+ YO <= FLIPPED_Y when FLIP_V = '1' else YI;
+end Behavioral;
diff --git a/basys3/basys3.xpr b/basys3/basys3.xpr
index 8dcc698..556c73e 100644
--- a/basys3/basys3.xpr
+++ b/basys3/basys3.xpr
@@ -61,7 +61,7 @@
<Option Name="IPStaticSourceDir" Val="$PIPUSERFILESDIR/ipstatic"/>
<Option Name="EnableBDX" Val="FALSE"/>
<Option Name="DSABoardId" Val="basys3"/>
- <Option Name="WTXSimLaunchSim" Val="27"/>
+ <Option Name="WTXSimLaunchSim" Val="31"/>
<Option Name="WTModelSimLaunchSim" Val="0"/>
<Option Name="WTQuestaLaunchSim" Val="0"/>
<Option Name="WTIesLaunchSim" Val="0"/>
@@ -154,6 +154,12 @@
<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>
<Config>
<Option Name="DesignMode" Val="RTL"/>
<Option Name="TopModule" Val="ppu_sprite_bg"/>
@@ -205,9 +211,15 @@
<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>
<Config>
<Option Name="DesignMode" Val="RTL"/>
- <Option Name="TopModule" Val="ppu_addr_dec_tb"/>
+ <Option Name="TopModule" Val="ppu_sprite_bg_tb"/>
<Option Name="TopLib" Val="xil_defaultlib"/>
<Option Name="TransportPathDelay" Val="0"/>
<Option Name="TransportIntDelay" Val="0"/>