Replaces CalcPipeline with HorizontalSpritePipeline for improved readability and maintainability. Introduces a structured pipeline architecture with enhanced visibility checks, ROM access efficiency, and modular design principles. Provides better handling of non-visible pixels and adheres to AXI-like handshake standards.
302 lines
12 KiB
VHDL
302 lines
12 KiB
VHDL
----------------------------------------------------------------------------------
|
|
--@ - 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;
|