diff --git a/src/CalcPipeline.vhd b/src/CalcPipeline.vhd deleted file mode 100644 index 948cfef..0000000 --- a/src/CalcPipeline.vhd +++ /dev/null @@ -1,207 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; -use ieee.math_real.all; - -entity CalcPipeline is - generic ( - --@ Width of the sprite index (Base address) register - G_Index_Width : integer := 5; - --@ Width of the sprite offset (Line address) register - G_Offset_Width : integer := 8; - --@ Width of the X position (Row) register - G_X_Width : integer := 10; - --@ Width of the pixel data from rom - G_Rom_Width : integer := 8; - --@ Width of the pixel data output - G_Pixel_Width : integer := 8 - ); - port ( - --@ Clock; (**Rising edge** triggered) - I_CLK : in std_logic; - --@ Clock Enable; (**Synchronous**, **Active high**) - I_CE : in std_logic; - --@ Reset; (**Synchronous**, **Active high**) - I_RST : in std_logic; - - --@ @virtualbus Start-OP @dir In Start calculation bus - --@ AXI like valid; (**Synchronous**, **Active high**) - I_OP_Valid : in std_logic; - --@ AXI like ready; (**Synchronous**, **Active high**) - O_OP_Ready : out std_logic; - --@ Index address - I_OP_Index : in std_logic_vector(G_Index_Width - 1 downto 0); - --@ Offset address - I_OP_Offset : in std_logic_vector(G_Offset_Width - 1 downto 0); - --@ X position of the request - I_OP_X_Request : in std_logic_vector(G_X_Width - 1 downto 0); - --@ X position of the sprite - I_OP_X_Sprite : in std_logic_vector(G_X_Width - 1 downto 0); - --@ @end - - --@ @virtualbus Rom-Address @dir Out Request rom data bus - --@ AXI like valid; (**Synchronous**, **Active high**) - O_Rom_Valid : out std_logic; - --@ AXI like ready; (**Synchronous**, **Active high**) - I_Rom_Ready : in std_logic; - --@ Rom address - O_Rom_Address : out std_logic_vector(G_Index_Width + G_Offset_Width - 1 downto 0); - --@ @end - - --@ @virtualbus Rom-Data @dir In Rom data bus - --@ AXI like valid; (**Synchronous**, **Active high**) - I_Rom_Valid : in std_logic; - --@ AXI like ready; (**Synchronous**, **Active high**) - O_Rom_Ready : out std_logic; - --@ Rom data - I_Rom_Data : in std_logic_vector(G_Rom_Width - 1 downto 0); - --@ @end - - --@ @virtualbus Pixel-Data @dir Out Pixel data output bus - --@ AXI like valid; (**Synchronous**, **Active high**) - O_Pixel_Valid : out std_logic; - --@ AXI like ready; (**Synchronous**, **Active high**) - I_Pixel_Ready : in std_logic; - --@ Pixel data - O_Pixel_Data : out std_logic_vector(G_Pixel_Width - 1 downto 0) - --@ @end - ); -end entity CalcPipeline; - -architecture RTL of CalcPipeline is - signal R_Index : std_logic_vector(G_Index_Width - 1 downto 0) := (others => '0'); - signal R_Offset : std_logic_vector(G_Offset_Width - 1 downto 0) := (others => '0'); - signal R_X_Request : std_logic_vector(G_X_Width - 1 downto 0) := (others => '0'); - signal R_X_Sprite : std_logic_vector(G_X_Width - 1 downto 0) := (others => '0'); - - signal C_Address : std_logic_vector(G_Index_Width + G_Offset_Width - 1 downto 0) := (others => '0'); - signal R_Address : std_logic_vector(G_Index_Width + G_Offset_Width - 1 downto 0) := (others => '0'); - - signal I_I_Calculating_Ready : std_logic := '0'; - signal O_I_Calculating_Valid : std_logic := '0'; - - signal I_O_Calculating_Ready : std_logic := '0'; - signal O_O_Calculating_Valid : std_logic := '0'; - - signal C_X_Visible : std_logic := '0'; - signal R_X_Visible : std_logic := '0'; - - signal I_RomRequest_Valid : std_logic := '0'; - signal O_RomRequest_Ready : std_logic := '0'; -begin - - INST_I_Calculating : entity work.PipelineStage - generic map( - G_PipelineStages => 1, - G_D0_Width => G_Index_Width, - G_D1_Width => G_Offset_Width, - G_D2_Width => G_X_Width, - G_D3_Width => G_X_Width - ) - port map( - I_CLK => I_CLK, - I_CE => I_CE, - I_RST => I_RST, - I_Valid => I_OP_Valid, - O_Ready => O_OP_Ready, - I_Data_0 => I_OP_Index, - I_Data_1 => I_OP_Offset, - I_Data_2 => I_OP_X_Request, - I_Data_3 => I_OP_X_Sprite, - O_Valid => O_I_Calculating_Valid, - I_Ready => I_I_Calculating_Ready, - O_Data_0 => R_Index, - O_Data_1 => R_Offset, - O_Data_2 => R_X_Request, - O_Data_3 => R_X_Sprite - ); - - process (R_Offset, R_Index, R_X_Request, R_X_Sprite) - variable V_Offset : unsigned(G_Offset_Width - 1 downto 0); - variable V_X_Request : unsigned(G_X_Width - 1 downto 0); - variable V_X_Sprite : unsigned(G_X_Width - 1 downto 0); - variable V_RelativeOffset : unsigned(G_X_Width - 1 downto 0); - variable V_CalculateOffset : unsigned(G_Offset_Width - 1 downto 0); - begin - V_Offset := unsigned(R_Offset); - V_X_Request := unsigned(R_X_Request); - V_X_Sprite := unsigned(R_X_Sprite); - - V_RelativeOffset := V_X_Request - V_X_Sprite; - V_CalculateOffset := V_Offset + V_RelativeOffset(G_Offset_Width - 1 downto 0); - - if V_CalculateOffset > 15 then - C_X_Visible <= '0'; - else - C_X_Visible <= '1'; - end if; - - C_Address <= R_Index & std_logic_vector(V_CalculateOffset); - end process; - - INST_O_Calculating : entity work.PipelineStage - generic map( - G_PipelineStages => 1, - G_D0_Width => G_Index_Width + G_Offset_Width, - G_D1_Width => 1 - ) - port map( - I_CLK => I_CLK, - I_CE => I_CE, - I_RST => I_RST, - I_Valid => O_I_Calculating_Valid, - O_Ready => I_I_Calculating_Ready, - I_Data_0 => C_Address, - I_Data_1(0) => C_X_Visible, - O_Valid => O_O_Calculating_Valid, - I_Ready => I_O_Calculating_Ready, - O_Data_0 => R_Address, - O_Data_1(0) => R_X_Visible - ); - - process (R_X_Visible, O_O_Calculating_Valid, O_RomRequest_Ready) - begin - if R_X_Visible = '0' then - I_RomRequest_Valid <= '0'; - I_O_Calculating_Ready <= '1'; - else - I_RomRequest_Valid <= O_O_Calculating_Valid; - I_O_Calculating_Ready <= O_RomRequest_Ready; - end if; - end process; - - INST_RomRequest : entity work.PipelineStage - generic map( - G_PipelineStages => 1, - G_D0_Width => G_Index_Width + G_Offset_Width - ) - port map( - I_CLK => I_CLK, - I_CE => I_CE, - I_RST => I_RST, - I_Valid => I_RomRequest_Valid, - O_Ready => O_RomRequest_Ready, - I_Data_0 => R_Address, - O_Valid => O_Rom_Valid, - I_Ready => I_Rom_Ready, - O_Data_0 => O_Rom_Address - ); - - I_ForwardPixelData : entity work.PipelineStage - generic map( - G_PipelineStages => 1, - G_D0_Width => G_Pixel_Width - ) - port map( - I_CLK => I_CLK, - I_CE => I_CE, - I_RST => I_RST, - I_Valid => I_Rom_Valid, - O_Ready => O_Rom_Ready, - I_Data_0 => I_Rom_Data, - O_Valid => O_Pixel_Valid, - I_Ready => I_Pixel_Ready, - O_Data_0 => O_Pixel_Data - ); - -end architecture; diff --git a/src/HorizontalSpritePipeline.vhd b/src/HorizontalSpritePipeline.vhd new file mode 100644 index 0000000..c836c89 --- /dev/null +++ b/src/HorizontalSpritePipeline.vhd @@ -0,0 +1,301 @@ +---------------------------------------------------------------------------------- +--@ - Name: **HorizontalSpritePipeline** +--@ - Version: 0.0.1 +--@ - Author: _0xMax42 ([Blog](0xMax42.io))_ +--@ - License: [MIT](LICENSE) +--@ +--@ ## Description +--@ This pipeline calculates whether a sprite is visible at a given horizontal +--@ scanline position (X coordinate), and if so, outputs the corresponding pixel +--@ address and color data. +--@ +--@ The operation is fully pipelined and includes the following steps: +--@ - Latching of sprite index, offset, and X positions +--@ - Calculation of the horizontal offset between request and sprite position +--@ - Visibility check against the maximum sprite width +--@ - ROM address generation for the sprite pixel +--@ - Forwarding of pixel data to the output pipeline +--@ +--@ Non-visible pixels (outside the horizontal sprite bounds) are filtered out +--@ before any ROM access occurs. The pipeline uses fully synchronous logic +--@ and AXI-like handshake signals across all bus interfaces. +--@ +--@ ## History +--@ - 0.0.1 (2025-04-20) Initial version +---------------------------------------------------------------------------------- +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.math_real.all; + +entity HorizontalSpritePipeline is + generic ( + --@ Width of the sprite index (Base address) register + G_Index_Width : integer := 5; + --@ Width of the sprite offset (Line address) register + G_Offset_Width : integer := 8; + --@ Width of the X position (Row) register + G_X_Width : integer := 10; + --@ Width of the pixel data from rom + G_Rom_Width : integer := 8; + --@ Width of the pixel data output + G_Pixel_Width : integer := 8; + --@ Horizontal width of a sprite in pixels + G_SpriteMaxWidth : integer := 16 + ); + port ( + --@ Clock; (**Rising edge** triggered) + I_CLK : in std_logic; + --@ Clock Enable; (**Synchronous**, **Active high**) + I_CE : in std_logic; + --@ Reset; (**Synchronous**, **Active high**) + I_RST : in std_logic; + + --@ @virtualbus Operation @dir In Operation input bus + --@ AXI like valid; (**Synchronous**, **Active high**) + I_OP_Valid : in std_logic; + --@ AXI like ready; (**Synchronous**, **Active high**) + O_OP_Ready : out std_logic; + --@ Index address + I_OP_Index : in std_logic_vector(G_Index_Width - 1 downto 0); + --@ Offset address + I_OP_Offset : in std_logic_vector(G_Offset_Width - 1 downto 0); + --@ X position of the request + I_OP_X_Request : in std_logic_vector(G_X_Width - 1 downto 0); + --@ X position of the sprite + I_OP_X_Sprite : in std_logic_vector(G_X_Width - 1 downto 0); + --@ @end + + --@ @virtualbus Rom-Address @dir Out Request rom data bus + --@ AXI like valid; (**Synchronous**, **Active high**) + O_Rom_Valid : out std_logic; + --@ AXI like ready; (**Synchronous**, **Active high**) + I_Rom_Ready : in std_logic; + --@ Rom address + O_Rom_Address : out std_logic_vector(G_Index_Width + G_Offset_Width - 1 downto 0); + --@ @end + + --@ @virtualbus Rom-Data @dir In Rom data bus + --@ AXI like valid; (**Synchronous**, **Active high**) + I_Rom_Valid : in std_logic; + --@ AXI like ready; (**Synchronous**, **Active high**) + O_Rom_Ready : out std_logic; + --@ Rom data + I_Rom_Data : in std_logic_vector(G_Rom_Width - 1 downto 0); + --@ @end + + --@ @virtualbus Pixel-Data @dir Out Pixel data output bus + --@ AXI like valid; (**Synchronous**, **Active high**) + O_Pixel_Valid : out std_logic; + --@ AXI like ready; (**Synchronous**, **Active high**) + I_Pixel_Ready : in std_logic; + --@ Pixel data + O_Pixel_Data : out std_logic_vector(G_Pixel_Width - 1 downto 0) + --@ @end + ); +end entity HorizontalSpritePipeline; + +architecture RTL of HorizontalSpritePipeline is + --@ Register enable for the horizontal sprite pipeline + signal O_HSpritePipelineCtrl_Enable : std_logic := '0'; + --@ Ready in signal for the **output** of the horizontal sprite pipeline + signal I_HSpritePipeline_Ready : std_logic := '0'; + --@ Valid out signal for the **output** of the horizontal sprite pipeline + signal O_HSpritePipeline_Valid : std_logic := '0'; + + --@ Register for the `I_OP_Index` signal (Pipeline stage 1/2) + signal R_Index : std_logic_vector(G_Index_Width - 1 downto 0) := (others => '0'); + --@ Register for the `I_OP_Offset` signal (Pipeline stage 1/2) + signal R_Offset : std_logic_vector(G_Offset_Width - 1 downto 0) := (others => '0'); + --@ Register for the `I_OP_X_Request` signal (Pipeline stage 1) + signal R_X_Request : std_logic_vector(G_X_Width - 1 downto 0) := (others => '0'); + --@ Register for the `I_OP_X_Sprite` signal (Pipeline stage 1) + signal R_X_Sprite : std_logic_vector(G_X_Width - 1 downto 0) := (others => '0'); + + --@ Calculated sprite X offset (Between pipeline stage 1 and 2) + signal C_Sprite_X_Offset : std_logic_vector(G_X_Width - 1 downto 0) := (others => '0'); + --@ Register for the calculated sprite X offset (Pipeline stage 2) + signal R_Sprite_X_Offset : std_logic_vector(G_X_Width - 1 downto 0) := (others => '0'); + + --@ Calculated address for the ROM (Between pipeline stage 2 and 3) + signal C_Address : std_logic_vector(G_Index_Width + G_Offset_Width - 1 downto 0) := (others => '0'); + --@ Register for the calculated address for the ROM (Pipeline stage 3) + signal R_Address : std_logic_vector(G_Index_Width + G_Offset_Width - 1 downto 0) := (others => '0'); + + --@ Calculated visibility signal (Between pipeline stage 2 and 3) + signal C_X_Visible : std_logic := '0'; + --@ Register for the calculated visibility signal (Pipeline stage 3) + signal R_X_Visible : std_logic := '0'; + + --@ Valid signal for the ROM request (From pipeline stage 3 to 4; after filtering) + signal I_RomRequest_Valid : std_logic := '0'; + --@ Ready signal for the ROM request (From pipeline stage 3 to 4; after filtering) + signal O_RomRequest_Ready : std_logic := '0'; + +begin + + INST_HSpritePipelineCtrl : entity work.PipelineController + generic map( + G_PipelineStages => 3 + ) + port map( + I_CLK => I_CLK, + I_CE => I_CE, + O_Enable => O_HSpritePipelineCtrl_Enable, + I_Valid => I_OP_Valid, + O_Ready => O_OP_Ready, + O_Valid => O_HSpritePipeline_Valid, + I_Ready => I_HSpritePipeline_Ready + ); + + INST_HSpritePipeline_Index : entity work.PipelineRegister + generic map( + G_PipelineStages => 2, + G_Width => G_Index_Width, + G_RegisterBalancing => "forward" + ) + port map( + I_CLK => I_CLK, + I_Enable => O_HSpritePipelineCtrl_Enable, + I_Data => I_OP_Index, + O_Data => R_Index + ); + + INST_HSpritePipeline_Offset : entity work.PipelineRegister + generic map( + G_PipelineStages => 2, + G_Width => G_Offset_Width, + G_RegisterBalancing => "forward" + ) + port map( + I_CLK => I_CLK, + I_Enable => O_HSpritePipelineCtrl_Enable, + I_Data => I_OP_Offset, + O_Data => R_Offset + ); + + INST_HSpritePipeline_Request : entity work.PipelineRegister + generic map( + G_PipelineStages => 1, + G_Width => G_X_Width, + G_RegisterBalancing => "forward" + ) + port map( + I_CLK => I_CLK, + I_Enable => O_HSpritePipelineCtrl_Enable, + I_Data => I_OP_X_Request, + O_Data => R_X_Request + ); + + INST_HSpritePipeline_X_Sprite : entity work.PipelineRegister + generic map( + G_PipelineStages => 1, + G_Width => G_X_Width, + G_RegisterBalancing => "forward" + ) + port map( + I_CLK => I_CLK, + I_Enable => O_HSpritePipelineCtrl_Enable, + I_Data => I_OP_X_Sprite, + O_Data => R_X_Sprite + ); + + C_Sprite_X_Offset <= std_logic_vector( + unsigned(R_X_Request) - unsigned(R_X_Sprite) + ); + + INST_HSpritePipeline_X_Offset : entity work.PipelineRegister + generic map( + G_PipelineStages => 1, + G_Width => G_X_Width, + G_RegisterBalancing => "yes" + ) + port map( + I_CLK => I_CLK, + I_Enable => O_HSpritePipelineCtrl_Enable, + I_Data => C_Sprite_X_Offset, + O_Data => R_Sprite_X_Offset + ); + + C_Address <= R_Index & std_logic_vector( + unsigned(R_Offset) + unsigned(R_Sprite_X_Offset(G_Offset_Width - 1 downto 0)) + ); + + C_X_Visible <= '1' when unsigned(R_Sprite_X_Offset) < G_SpriteMaxWidth else + '0'; + + INST_HSpritePipeline_Address : entity work.PipelineRegister + generic map( + G_PipelineStages => 1, + G_Width => G_Index_Width + G_Offset_Width, + G_RegisterBalancing => "backward" + ) + port map( + I_CLK => I_CLK, + I_Enable => O_HSpritePipelineCtrl_Enable, + I_Data => C_Address, + O_Data => R_Address + ); + + INST_HSpritePipeline_X_Visible : entity work.PipelineRegister + generic map( + G_PipelineStages => 1, + G_Width => 1, + G_RegisterBalancing => "backward" + ) + port map( + I_CLK => I_CLK, + I_Enable => O_HSpritePipelineCtrl_Enable, + I_Data(0) => C_X_Visible, + O_Data(0) => R_X_Visible + ); + + INST_DropUnvisibleRequests : entity work.PipelineFilter + generic map( + G_MaskWidth => 1, + G_Mask => "1", + G_MaskMode => "not_equal" + ) + port map( + I_Match(0) => R_X_Visible, + I_Valid => O_HSpritePipeline_Valid, + O_Ready => I_HSpritePipeline_Ready, + O_Valid => I_RomRequest_Valid, + I_Ready => O_RomRequest_Ready + ); + + INST_RomRequestBuffer : entity work.PipelineStage + generic map( + G_PipelineStages => 1, + G_D0_Width => G_Index_Width + G_Offset_Width + ) + port map( + I_CLK => I_CLK, + I_CE => I_CE, + I_RST => I_RST, + I_Valid => I_RomRequest_Valid, + O_Ready => O_RomRequest_Ready, + I_Data_0 => R_Address, + O_Valid => O_Rom_Valid, + I_Ready => I_Rom_Ready, + O_Data_0 => O_Rom_Address + ); + + INST_PixelDataBuffer : entity work.PipelineStage + generic map( + G_PipelineStages => 1, + G_D0_Width => G_Pixel_Width + ) + port map( + I_CLK => I_CLK, + I_CE => I_CE, + I_RST => I_RST, + I_Valid => I_Rom_Valid, + O_Ready => O_Rom_Ready, + I_Data_0 => I_Rom_Data, + O_Valid => O_Pixel_Valid, + I_Ready => I_Pixel_Ready, + O_Data_0 => O_Pixel_Data + ); + +end architecture;