Introduces sprite channel processing pipeline

Adds modules for sprite operations, including opcode decoding, register handling, and vertical pipeline calculations. Replaces legacy scheduler with a more modular and efficient design. Updates constraints for clock timing.

Enhances sprite rendering pipeline with improved modularity and scalability.
This commit is contained in:
2025-04-21 15:25:12 +00:00
parent 032960103c
commit 3c641355fc
7 changed files with 9108 additions and 144 deletions

265
src/OPDecoder.vhd Normal file
View File

@@ -0,0 +1,265 @@
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
use work.OPCodes.all;
entity OPDecoder is
generic (
--@ Data width of the operation data.
G_OPCodeData_Width : integer := 10;
--@ 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 Y position (Line) register
G_Y_Width : integer := 10
);
port (
--@ Clock signal; (**Rising edge** triggered)
I_CLK : in std_logic;
--@ Clock enable signal (**Active high**)
I_CE : in std_logic;
--@ Synchronous reset signal (**Active high**)
I_RST : in std_logic;
--@ @virtualbus Operation-Write @dir in Operation Write Interface
--@ Indicates if the `OPCode` and `OPData` are valid. (**Active high**)
I_OP_Valid : in std_logic := '0';
--@ Indicates if the decoder is ready to accept data. (**Active high**)
O_OP_Ready : out std_logic := '0';
--@ Operation code for the sprite channel
I_OP_Code : in std_logic_vector(3 downto 0) := (others => '0');
--@ Data to be used with the operation code.
I_OP_Data : in std_logic_vector(G_OPCodeData_Width - 1 downto 0) := (others => '0');
--@ @end
--@ @virtualbus Register-Write @dir out Bus to write to the register file
--@ Write enable for the sprite index register; (**Active high**)
O_Register_Index_WE : out std_logic := '0';
--@ Data to write to the sprite index (Base address) register.
O_Register_Index : out std_logic_vector(G_Index_Width - 1 downto 0) := (others => '0');
--@ Write enable for the sprite offset (line) register; (**Active high**)
O_Register_Offset_WE : out std_logic := '0';
--@ Data to write to the sprite offset (Line address) register.
O_Register_Offset : out std_logic_vector(G_Offset_Width - 1 downto 0) := (others => '0');
--@ Write enable for the X position register. (**Active high**)
O_Register_X_We : out std_logic := '0';
--@ Data to write to the X position register (Row) of the sprite.
O_Register_X : out std_logic_vector(G_X_Width - 1 downto 0) := (others => '0');
--@ Write enable for the Y position register. (**Active high**)
O_Register_Y_WE : out std_logic := '0';
--@ Data to write to the Y position register (Line) of the sprite.
O_Register_Y : out std_logic_vector(G_Y_Width - 1 downto 0) := (others => '0');
--@ Write enable for the `IsVisible` flag. (**Active high**)
O_IsVisible_WE : out std_logic := '0';
--@ Flag to write to the `IsVisible` flag.
O_IsVisible : out std_logic := '0';
--@ @end
--@ @virtualbus Register-Read @dir In Parallel read interface to the register file
--@ Indicates if the sprite is in the line visible.
I_IsVisible : in std_logic := '0';
--@ @end
--@ @virtualbus YHitCheck-Input-Interface @dir in YHitCheck Input Interface
--@ Indicates if the pipeline is ready to accept data. **Active high**
I_YHitCheck_Ready : in std_logic := '0';
--@ Indicates if the pipeline is valid. **Active high**
O_YHitCheck_Valid : out std_logic := '0';
--@ The line to check if the sprite is in the line visible.
O_YHitCheck_YToCheck : out std_logic_vector(G_Y_Width - 1 downto 0) := (others => '0');
--@ @end
--@ @virtualbus YHitCheck-Output-Interface @dir out YHitCheck Output Interface
--@ Indicates if the pipeline is ready to deliver data. **Active high**
O_YHitCheck_Ready : out std_logic := '0';
--@ Indicates if `O_IsVisible` is valid. **Active high**
I_YHitCheck_Valid : in std_logic := '0';
--@ Indicates if the sprite is visible in the line.
I_YHitCheck_IsVisible : in std_logic := '0';
--@ The calculated offset address of the sprite.
I_YHitCheck_Offset : in std_logic_vector(G_Offset_Width - 1 downto 0) := (others => '0');
--@ @end
--@ @virtualbus HorizontalSpritePipeline-Interface @dir Out Interface to the horizontal sprite pipeline
--@ AXI like valid; (**Synchronous**, **Active high**)
O_HSpritePipeline_Valid : out std_logic := '0';
--@ AXI like ready; (**Synchronous**, **Active high**)
I_HSpritePipeline_Ready : in std_logic := '0';
--@ Requested X position of the sprite in the line.
O_HSpritePipeline_X_Request : out std_logic_vector(G_X_Width - 1 downto 0) := (others => '0')
--@ @end
);
end OPDecoder;
architecture RTL of OPDecoder is
signal C_OP_Ready : std_logic := '0';
type T_State is (
S_Ready,
S_Dispatch_YHitCheck,
S_WaitResponse_YHitCheck,
S_Dispatch_CalcPipeline
);
signal R_State : T_State := S_Ready;
signal C_NextState : T_State := S_Ready;
signal C_OP_Data_WE : std_logic := '0';
signal R_OP_Data : std_logic_vector(G_OPCodeData_Width - 1 downto 0) := (others => '0');
begin
O_OP_Ready <= C_OP_Ready;
P_RegisterNewLineReq : process (I_CLK)
begin
if rising_edge(I_CLK) then
if I_CE = '1' then
if I_RST = '1' then
R_OP_Data <= (others => '0');
elsif C_OP_Data_WE = '1' then
R_OP_Data <= I_OP_Data;
end if;
end if;
end if;
end process;
P_StateForwarding : process (I_CLK)
begin
if rising_edge(I_CLK) then
if I_CE = '1' then
if I_RST = '1' then
R_State <= S_Ready;
else
R_State <= C_NextState;
end if;
end if;
end if;
end process;
P_StateMachine : process (
R_State, I_OP_Code, R_OP_Data, I_OP_Valid,
I_YHitCheck_Ready, I_YHitCheck_Valid, I_YHitCheck_IsVisible, I_YHitCheck_Offset,
I_HSpritePipeline_Ready, I_OP_Data, I_IsVisible
)
begin
-- Default the next state to the current state
C_NextState <= R_State;
-- Default the output signals to prevent latches
O_Register_Index_WE <= '0';
O_Register_Index <= (others => '0');
O_Register_X_We <= '0';
O_Register_X <= (others => '0');
O_Register_Y_WE <= '0';
O_Register_Y <= (others => '0');
O_Register_Offset_WE <= '0';
O_Register_Offset <= (others => '0');
O_IsVisible_WE <= '0';
O_IsVisible <= '0';
O_YHitCheck_Valid <= '0';
O_YHitCheck_Ready <= '0';
O_YHitCheck_YToCheck <= (others => '0');
C_OP_Ready <= '0';
O_HSpritePipeline_Valid <= '0';
O_HSpritePipeline_X_Request <= (others => '0');
C_OP_Data_WE <= '0';
-- State Machine
case R_State is
when S_Ready =>
C_OP_Ready <= '1';
C_OP_Data_WE <= '1';
if I_OP_Valid = '1' then
case I_OP_Code is
when K_OP_NOP =>
C_NextState <= S_Ready;
when K_OP_NEWLINE =>
O_IsVisible_WE <= '0';
O_IsVisible <= '0';
C_NextState <= S_Dispatch_YHitCheck;
when K_OP_SET_ID =>
O_Register_Index_WE <= '1';
O_Register_Index <= I_OP_Data(G_Index_Width - 1 downto 0);
C_NextState <= S_Ready;
when K_OP_SET_X =>
O_Register_X_We <= '1';
O_Register_X <= I_OP_Data(G_X_Width - 1 downto 0);
C_NextState <= S_Ready;
when K_OP_SET_Y =>
O_Register_Y_WE <= '1';
O_Register_Y <= I_OP_Data(G_Y_Width - 1 downto 0);
C_NextState <= S_Ready;
when K_OP_REQ_ROW_DATA =>
if I_IsVisible = '1' then
C_NextState <= S_Dispatch_CalcPipeline;
end if;
when K_OP_CLEAR_ALL =>
O_Register_Index_WE <= '1';
O_Register_Index <= (others => '0');
--
O_Register_X_We <= '1';
O_Register_X <= (others => '0');
--
O_Register_Y_WE <= '1';
O_Register_Y <= (others => '0');
--
O_Register_Offset_WE <= '1';
O_Register_Offset <= (others => '0');
--
O_IsVisible_WE <= '1';
O_IsVisible <= '0';
C_NextState <= S_Ready;
when others =>
C_NextState <= S_Ready;
end case;
end if;
when S_Dispatch_YHitCheck =>
O_YHitCheck_Valid <= '1';
O_YHitCheck_YToCheck <= R_OP_Data(G_Y_Width - 1 downto 0);
if I_YHitCheck_Ready = '1' then
C_NextState <= S_WaitResponse_YHitCheck;
end if;
when S_WaitResponse_YHitCheck =>
O_YHitCheck_Ready <= '1';
if I_YHitCheck_Valid = '1' then
O_IsVisible_WE <= '1';
O_IsVisible <= I_YHitCheck_IsVisible;
O_Register_Offset_WE <= '1';
O_Register_Offset <= I_YHitCheck_Offset;
C_NextState <= S_Ready;
end if;
when S_Dispatch_CalcPipeline =>
O_HSpritePipeline_Valid <= '1';
O_HSpritePipeline_X_Request <= R_OP_Data(G_X_Width - 1 downto 0);
if I_HSpritePipeline_Ready = '1' then
C_NextState <= S_Ready;
end if;
end case;
end process;
end architecture;

122
src/RegisterFile.vhd Normal file
View File

@@ -0,0 +1,122 @@
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
entity RegisterFile 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 Y position (Line) register
G_Y_Width : integer := 10
);
port (
--@ Clock signal; (**Rising edge** triggered)
I_CLK : in std_logic;
--@ Clock enable signal (**Active high**)
I_CE : in std_logic := '1';
--@ Synchronous reset signal (**Active high**)
I_RST : in std_logic := '0';
--@ @virtualbus Register-Write @dir in Bus to write to the register file
--@ Write enable for the sprite index register; (**Active high**)
I_Index_WE : in std_logic := '0';
--@ Data to write to the sprite index (Base address) register.
I_Index : in std_logic_vector(G_Index_Width - 1 downto 0) := (others => '0');
--@ Write enable for the sprite offset (line) register; (**Active high**)
I_Offset_WE : in std_logic := '0';
--@ Data to write to the sprite offset (Line address) register.
I_Offset : in std_logic_vector(G_Offset_Width - 1 downto 0) := (others => '0');
--@ Write enable for the X position register. (**Active high**)
I_X_We : in std_logic := '0';
--@ Data to write to the X position register (Row) of the sprite.
I_X : in std_logic_vector(G_X_Width - 1 downto 0) := (others => '0');
--@ Write enable for the Y position register. (**Active high**)
I_Y_WE : in std_logic := '0';
--@ Data to write to the Y position register (Line) of the sprite.
I_Y : in std_logic_vector(G_Y_Width - 1 downto 0) := (others => '0');
--@ Write enable for the `IsVisible` flag. (**Active high**)
I_IsVisible_WE : in std_logic := '0';
--@ Flag to write to the `IsVisible` flag.
I_IsVisible : in std_logic := '0';
--@ @virtualbus Register-Read @dir out Bus to read from the register file
--@ Sprite index (Base address) of the sprite.
O_Index : out std_logic_vector(G_Index_Width - 1 downto 0) := (others => '0');
--@ Sprite offset (Line address) of the sprite.
O_Offset : out std_logic_vector(G_Offset_Width - 1 downto 0) := (others => '0');
--@ X position of the sprite.
O_X : out std_logic_vector(G_X_Width - 1 downto 0) := (others => '0');
--@ Y position of the sprite.
O_Y : out std_logic_vector(G_Y_Width - 1 downto 0) := (others => '0');
--@ Flag to indicate if the sprite line is valid; (**Active high**)
O_IsVisible : out std_logic := '0'
--@ @end
);
end entity;
architecture RTL of RegisterFile is
--@ Register for the sprite index (Base address).
signal R_Index : std_logic_vector(G_Index_Width - 1 downto 0) := (others => '0');
--@ Register for the sprite offset (Line address).
signal R_Offset : std_logic_vector(G_Offset_Width - 1 downto 0) := (others => '0');
--@ Register for the X position (Row) of the sprite.
signal R_X : std_logic_vector(G_X_Width - 1 downto 0) := (others => '0');
--@ Register for the Y position (Line) of the sprite.
signal R_Y : std_logic_vector(G_Y_Width - 1 downto 0) := (others => '0');
--@ Register for the `IsVisible` flag.
signal R_IsVisible : std_logic := '0';
begin
--@ Register file process
P_RegisterFile : process (I_CLK)
begin
if rising_edge(I_CLK) then
if I_RST = '1' then
R_Index <= (others => '0');
R_Offset <= (others => '0');
R_X <= (others => '0');
R_Y <= (others => '0');
R_IsVisible <= '0';
elsif I_CE = '1' then
if I_Index_WE = '1' then
R_Index <= I_Index;
end if;
if I_Offset_WE = '1' then
R_Offset <= I_Offset;
end if;
if I_X_We = '1' then
R_X <= I_X;
end if;
if I_Y_WE = '1' then
R_Y <= I_Y;
end if;
if I_IsVisible_WE = '1' then
R_IsVisible <= I_IsVisible;
end if;
end if;
end if;
end process;
--@ Forwarding the register values to the output
P_Forwarding : process (
R_Index, R_Offset, R_X, R_Y,
R_IsVisible
)
begin
O_Index <= R_Index;
O_Offset <= R_Offset;
O_X <= R_X;
O_Y <= R_Y;
O_IsVisible <= R_IsVisible;
end process;
end architecture;

8322
src/RomPackage.vhd Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,144 +0,0 @@
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
entity AXI_Handshaking_Scheduler_4 is
generic (
G_DataWidth : integer := 32
);
port (
--@ Clock signal; (**Rising edge** triggered)
I_CLK : in std_logic;
--@ Clock enable signal (**Active high**)
I_CE : in std_logic;
--@ Synchronous reset signal (**Active high**)
I_RST : in std_logic;
--@ @virtualbus P0 @dir in P0 interface
I_P0_Valid : in std_logic := '0';
O_P0_Ready : out std_logic := '0';
I_P0_Data : in std_logic_vector(G_DataWidth - 1 downto 0) := (others => '0');
--@ @end
--@ @virtualbus P1 @dir in P1 interface
I_P1_Valid : in std_logic := '0';
O_P1_Ready : out std_logic := '0';
I_P1_Data : in std_logic_vector(G_DataWidth - 1 downto 0) := (others => '0');
--@ @end
--@ @virtualbus P2 @dir in P2 interface
I_P2_Valid : in std_logic := '0';
O_P2_Ready : out std_logic := '0';
I_P2_Data : in std_logic_vector(G_DataWidth - 1 downto 0) := (others => '0');
--@ @end
--@ @virtualbus P3 @dir in P3 interface
I_P3_Valid : in std_logic := '0';
O_P3_Ready : out std_logic := '0';
I_P3_Data : in std_logic_vector(G_DataWidth - 1 downto 0) := (others => '0');
--@ @end
--@ @virtualbus Out @dir out Output interface
O_Out_Valid : out std_logic := '0';
I_Out_Ready : in std_logic := '0';
O_Out_Data : out std_logic_vector(G_DataWidth - 1 downto 0) := (others => '0');
O_Out_Address : out std_logic_vector(1 downto 0) := (others => '0')
--@ @end
);
end entity AXI_Handshaking_Scheduler_4;
architecture Rtl of AXI_Handshaking_Scheduler_4 is
signal R_Counter : unsigned(1 downto 0) := (others => '0');
signal C_Select : std_logic_vector(3 downto 0) := (others => '0');
signal C_Code : std_logic_vector(1 downto 0) := (others => '0');
signal C_CodeReverse : std_logic_vector(1 downto 0) := (others => '0');
begin
i_PriorityEncoder_4 : entity work.PriorityEncoder_4
port map(
I_Select => C_Select,
O_Code => C_Code
);
P_SelectMux : process (R_Counter, I_P0_Valid, I_P1_Valid, I_P2_Valid, I_P3_Valid)
begin
if R_Counter = "00" then
C_Select <= I_P0_Valid & I_P1_Valid & I_P2_Valid & I_P3_Valid;
elsif R_Counter = "01" then
C_Select <= I_P1_Valid & I_P2_Valid & I_P3_Valid & I_P0_Valid;
elsif R_Counter = "10" then
C_Select <= I_P2_Valid & I_P3_Valid & I_P0_Valid & I_P1_Valid;
elsif R_Counter = "11" then
C_Select <= I_P3_Valid & I_P0_Valid & I_P1_Valid & I_P2_Valid;
else
C_Select <= (others => '-');
end if;
end process;
P_CodeReverse : process (C_Code, R_Counter)
begin
C_CodeReverse <= std_logic_vector(unsigned(C_Code) + R_Counter);
end process;
P_OutMux : process (
C_CodeReverse, I_P0_Data, I_P1_Data, I_P2_Data, I_P3_Data,
I_P0_Valid, I_P1_Valid, I_P2_Valid, I_P3_Valid,
I_Out_Ready)
begin
O_Out_Valid <= '0';
O_P0_Ready <= '0';
O_P1_Ready <= '0';
O_P2_Ready <= '0';
O_P3_Ready <= '0';
O_Out_Data <= (others => '0');
O_Out_Address <= C_CodeReverse;
case C_CodeReverse is
when "00" =>
O_Out_Valid <= I_P0_Valid;
O_P0_Ready <= I_Out_Ready;
O_Out_Data <= I_P0_Data;
when "01" =>
O_Out_Valid <= I_P1_Valid;
O_P1_Ready <= I_Out_Ready;
O_Out_Data <= I_P1_Data;
when "10" =>
O_Out_Valid <= I_P2_Valid;
O_P2_Ready <= I_Out_Ready;
O_Out_Data <= I_P2_Data;
when "11" =>
O_Out_Valid <= I_P3_Valid;
O_P3_Ready <= I_Out_Ready;
O_Out_Data <= I_P3_Data;
when others =>
null;
end case;
end process;
P_Counter : process (I_CLK)
begin
if rising_edge(I_CLK) then
if I_CE = '1' then
if I_RST = '1' then
R_Counter <= (others => '0');
else
if I_Out_Ready = '1' then
case C_CodeReverse is
when "00" =>
R_Counter <= "01";
when "01" =>
R_Counter <= "10";
when "10" =>
R_Counter <= "11";
when "11" =>
R_Counter <= "00";
when others =>
R_Counter <= "00";
end case;
end if;
end if;
end if;
end if;
end process P_Counter;
end architecture;

5
src/SpriteChannel.ucf Normal file
View File

@@ -0,0 +1,5 @@
# TEMPERATURE = 50 C;
NET I_CLK LOC = B8;
NET I_CLK TNM_NET = CLOCK;
TIMESPEC TS_CLOCK = PERIOD CLOCK 230 MHz HIGH 50 %;

232
src/SpriteChannel.vhd Normal file
View File

@@ -0,0 +1,232 @@
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
entity SpriteChannel is
generic (
--@ Data width of the operation data.
G_OPCodeData_Width : integer := 10;
--@ 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 Y position (Line) register
G_Y_Width : integer := 10;
--@ The height of the sprite in pixels
G_Sprite_Height : integer := 16;
--@ The width of the sprite in pixels
G_Sprite_Width : integer := 16
);
port (
--@ Clock signal; **Rising edge** triggered
I_CLK : in std_logic;
--@ Clock Enable signal
I_CE : in std_logic;
--@ Synchronous reset signal
I_RST : in std_logic;
--@ @virtualbus Operation-Write @dir in Operation Write Interface
--@ Indicates if the `OPCode` and `OPData` are valid. (**Active high**)
I_OP_Valid : in std_logic := '0';
--@ Indicates if the decoder is ready to accept data. (**Active high**)
O_OP_Ready : out std_logic := '0';
--@ Operation code for the sprite channel
I_OP_Code : in std_logic_vector(3 downto 0) := (others => '0');
--@ Data to be used with the operation code.
I_OP_Data : in std_logic_vector(G_OPCodeData_Width - 1 downto 0) := (others => '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(7 downto 0)
--@ @end
);
end entity;
architecture RTL of SpriteChannel 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 : std_logic_vector(G_X_Width - 1 downto 0) := (others => '0');
signal R_Y : std_logic_vector(G_Y_Width - 1 downto 0) := (others => '0');
signal R_IsVisible : std_logic := '0';
signal OI_P0_Address_Valid : std_logic := '0';
signal IO_P0_Address_Ready : std_logic := '0';
signal OI_P0_Address : std_logic_vector(G_Index_Width + G_Offset_Width - 1 downto 0) := (others => '0');
signal IO_P0_Data_Valid : std_logic := '0';
signal OI_P0_Data_Ready : std_logic := '0';
signal IO_P0_Data : std_logic_vector(7 downto 0) := (others => '0');
signal OI_Register_Index_WE : std_logic := '0';
signal OI_Register_Index : std_logic_vector(G_Index_Width - 1 downto 0) := (others => '0');
signal OI_Register_Offset_WE : std_logic := '0';
signal OI_Register_Offset : std_logic_vector(G_Offset_Width - 1 downto 0) := (others => '0');
signal OI_Register_X_We : std_logic := '0';
signal OI_Register_X : std_logic_vector(G_X_Width - 1 downto 0) := (others => '0');
signal OI_Register_Y_WE : std_logic := '0';
signal OI_Register_Y : std_logic_vector(G_Y_Width - 1 downto 0) := (others => '0');
signal OI_IsVisible_WE : std_logic := '0';
signal OI_IsVisible : std_logic := '0';
signal I_YHitCheck_Ready : std_logic := '0';
signal I_YHitCheck_Valid : std_logic := '0';
signal I_YHitCheck_IsVisible : std_logic := '0';
signal I_YHitCheck_Offset : std_logic_vector(G_Offset_Width - 1 downto 0) := (others => '0');
signal O_YHitCheck_Valid : std_logic := '0';
signal O_YHitCheck_YToCheck : std_logic_vector(G_Y_Width - 1 downto 0) := (others => '0');
signal O_YHitCheck_Ready : std_logic := '0';
signal OI_CalcPipeline_Valid : std_logic := '0';
signal IO_CalcPipeline_Ready : std_logic := '0';
signal OI_CalcPipeline_X_Request : std_logic_vector(G_X_Width - 1 downto 0) := (others => '0');
begin
i_RegisterFile : entity work.RegisterFile
generic map(
G_Index_Width => G_Index_Width,
G_Offset_Width => G_Offset_Width,
G_X_Width => G_X_Width,
G_Y_Width => G_Y_Width
)
port map(
I_CLK => I_CLK,
I_CE => I_CE,
I_RST => I_RST,
I_Index_WE => OI_Register_Index_WE,
I_Index => OI_Register_Index,
I_Offset_WE => OI_Register_Offset_WE,
I_Offset => OI_Register_Offset,
I_X_We => OI_Register_X_We,
I_X => OI_Register_X,
I_Y_WE => OI_Register_Y_WE,
I_Y => OI_Register_Y,
I_IsVisible_WE => OI_IsVisible_WE,
I_IsVisible => OI_IsVisible,
O_Index => R_Index,
O_Offset => R_Offset,
O_X => R_X,
O_Y => R_Y,
O_IsVisible => R_IsVisible
);
i_OPDecoder : entity work.OPDecoder
generic map(
G_OPCodeData_Width => G_OPCodeData_Width,
G_Index_Width => G_Index_Width,
G_Offset_Width => G_Offset_Width,
G_X_Width => G_X_Width,
G_Y_Width => G_Y_Width
)
port map(
I_CLK => I_CLK,
I_CE => I_CE,
I_RST => I_RST,
I_OP_Valid => I_OP_Valid,
O_OP_Ready => O_OP_Ready,
I_OP_Code => I_OP_Code,
I_OP_Data => I_OP_Data,
O_Register_Index_WE => OI_Register_Index_WE,
O_Register_Index => OI_Register_Index,
O_Register_Offset_WE => OI_Register_Offset_WE,
O_Register_Offset => OI_Register_Offset,
O_Register_X_We => OI_Register_X_We,
O_Register_X => OI_Register_X,
O_Register_Y_WE => OI_Register_Y_WE,
O_Register_Y => OI_Register_Y,
O_IsVisible_WE => OI_IsVisible_WE,
O_IsVisible => OI_IsVisible,
I_IsVisible => R_IsVisible,
I_YHitCheck_Ready => I_YHitCheck_Ready,
O_YHitCheck_Valid => O_YHitCheck_Valid,
O_YHitCheck_YToCheck => O_YHitCheck_YToCheck,
O_YHitCheck_Ready => O_YHitCheck_Ready,
I_YHitCheck_Valid => I_YHitCheck_Valid,
I_YHitCheck_IsVisible => I_YHitCheck_IsVisible,
I_YHitCheck_Offset => I_YHitCheck_Offset,
O_HSpritePipeline_Valid => OI_CalcPipeline_Valid,
I_HSpritePipeline_Ready => IO_CalcPipeline_Ready,
O_HSpritePipeline_X_Request => OI_CalcPipeline_X_Request
);
i_YHitCheck : entity work.VerticalSpritePipeline
generic map(
G_Y_Width => G_Y_Width,
G_Sprite_Height => G_Sprite_Height,
G_Offset_Width => G_Offset_Width,
G_PipelineStages => 2
)
port map(
I_CLK => I_CLK,
I_CE => I_CE,
O_Ready => I_YHitCheck_Ready,
I_Valid => O_YHitCheck_Valid,
I_YToCheck => O_YHitCheck_YToCheck,
I_Y => R_Y,
I_Ready => O_YHitCheck_Ready,
O_Valid => I_YHitCheck_Valid,
O_IsVisible => I_YHitCheck_IsVisible,
O_Offset => I_YHitCheck_Offset
);
i_Rom : entity work.Rom
generic map(
G_Address_Width => 13,
G_Data_Width => 8,
G_P0_BufferStages => 1,
G_P0_ID_Width => 0,
G_P1_BufferStages => 0,
G_P1_ID_Width => 0,
G_RomType => "Block"
)
port map(
I_CLK => I_CLK,
I_CE => I_CE,
I_P0_Address_Valid => OI_P0_Address_Valid,
O_P0_Address_Ready => IO_P0_Address_Ready,
I_P0_Address => OI_P0_Address,
O_P0_Data_Valid => IO_P0_Data_Valid,
I_P0_Data_Ready => OI_P0_Data_Ready,
O_P0_Data => IO_P0_Data
);
i_CalcPipeline : entity work.HorizontalSpritePipeline
generic map(
G_Index_Width => G_Index_Width,
G_Offset_Width => G_Offset_Width,
G_X_Width => G_X_Width,
G_Rom_Width => 8,
G_Pixel_Width => 8
)
port map(
I_CLK => I_CLK,
I_CE => I_CE,
I_RST => I_RST,
I_OP_Valid => OI_CalcPipeline_Valid,
O_OP_Ready => IO_CalcPipeline_Ready,
I_OP_X_Request => OI_CalcPipeline_X_Request,
I_OP_Index => R_Index,
I_OP_Offset => R_Offset,
I_OP_X_Sprite => R_X,
O_Rom_Valid => OI_P0_Address_Valid,
I_Rom_Ready => IO_P0_Address_Ready,
O_Rom_Address => OI_P0_Address,
I_Rom_Valid => IO_P0_Data_Valid,
O_Rom_Ready => OI_P0_Data_Ready,
I_Rom_Data => IO_P0_Data,
O_Pixel_Valid => O_Pixel_Valid,
I_Pixel_Ready => I_Pixel_Ready,
O_Pixel_Data => O_Pixel_Data
);
end architecture RTL;

View File

@@ -0,0 +1,162 @@
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
use work.SpriteRom.all;
entity VerticalSpritePipeline is
generic (
--@ Width of the Y position (Line) register
G_Y_Width : integer := 10;
--@ The height of the sprite in pixels
G_Sprite_Height : integer := 16;
--@ Width of the sprite offset (Line address) register
G_Offset_Width : integer := 8;
--@ The pipeline stages for the calculating pipeline (multiply by 2 for the the latency of the pipeline)
G_PipelineStages : integer := 2
);
port (
--@ Clock signal; (**Rising edge** triggered)
I_CLK : in std_logic := '0';
--@ Clock enable signal (**Active high**)
I_CE : in std_logic := '1';
--@ @virtualbus YHitCheck-Input-Interface @dir in YHitCheck Input Interface
--@ Indicates if the pipeline is ready to accept data. (**Active high**)
O_Ready : out std_logic := '0';
--@ Indicates if the pipeline is valid. (**Active high**)
I_Valid : in std_logic := '0';
--@ The line to check if the sprite is in the line visible.
I_YToCheck : in std_logic_vector(G_Y_Width - 1 downto 0) := (others => '0');
--@ The sprite Y position to check if the sprite is in the line visible.
I_Y : in std_logic_vector(G_Y_Width - 1 downto 0) := (others => '0');
--@ @end
--@ @virtualbus YHitCheck-Output-Interface @dir out YHitCheck Output Interface
--@ Indicates if the pipeline is ready to deliver data. (**Active high**)
I_Ready : in std_logic := '0';
--@ Indicates if `O_IsVisible` is valid. (**Active high**)
O_Valid : out std_logic := '0';
--@ Indicates if the sprite is visible in the line.
O_IsVisible : out std_logic := '0';
--@ The calculated offset address of the sprite.
O_Offset : out std_logic_vector(G_Offset_Width - 1 downto 0) := (others => '0')
--@ @end
);
end entity VerticalSpritePipeline;
architecture Rtl of VerticalSpritePipeline is
--@ Line to check if the sprite is in the line visible
signal R_YToCheck : std_logic_vector(G_Y_Width - 1 downto 0) := (others => '0');
--@ The sprite Y position to check if the sprite is in the line visible
signal R_SpriteY : std_logic_vector(G_Y_Width - 1 downto 0) := (others => '0');
--@ Calculated visibility signal
signal C_IsVisible : std_logic := '0';
--@ The calculated offset address of the sprite
signal C_Offset : std_logic_vector(G_Offset_Width - 1 downto 0) := (others => '0');
--@ Pipeline enable signal
signal S_CalculatingPipeline_Enable : std_logic := '0';
begin
--@ Pipeline controller for the calculating pipeline
I_CalculatingPipelineCtrl : entity work.PipelineController
generic map(
G_PipelineStages => G_PipelineStages * 2
)
port map(
I_CLK => I_CLK,
I_CE => I_CE,
O_Enable => S_CalculatingPipeline_Enable,
I_Valid => I_Valid,
O_Ready => O_Ready,
O_Valid => O_Valid,
I_Ready => I_Ready
);
--@ Input register for the Y position of the sprite
I_Y_InputRegister : entity work.PipelineRegister
generic map(
G_PipelineStages => G_PipelineStages,
G_Width => G_Y_Width,
G_RegisterBalancing => "forward"
)
port map(
I_CLK => I_CLK,
I_Enable => S_CalculatingPipeline_Enable,
I_Data => I_Y,
O_Data => R_SpriteY
);
--@ Input register for the line to check if the sprite is in the line visible
I_YToCheck_InputRegister : entity work.PipelineRegister
generic map(
G_PipelineStages => G_PipelineStages,
G_Width => G_Y_Width,
G_RegisterBalancing => "forward"
)
port map(
I_CLK => I_CLK,
I_Enable => S_CalculatingPipeline_Enable,
I_Data => I_YToCheck,
O_Data => R_YToCheck
);
--@ Combinatory process to calculate the visibility and offset of the sprite.
P_CalculateVisibility : process (R_SpriteY, R_YToCheck)
variable V_SpriteY : unsigned(R_SpriteY'range);
variable V_YToCheck : unsigned(R_YToCheck'range);
variable V_SpriteYBottom : unsigned(R_SpriteY'range);
variable V_OffsetLine : integer;
variable V_Offset : unsigned(C_Offset'range);
begin
V_SpriteY := unsigned(R_SpriteY);
V_YToCheck := unsigned(R_YToCheck);
V_SpriteYBottom := V_SpriteY + to_unsigned(G_Sprite_Height - 1, R_SpriteY'length);
if V_YToCheck >= V_SpriteY and V_YToCheck <= V_SpriteYBottom then
C_IsVisible <= '1';
else
C_IsVisible <= '0';
end if;
V_OffsetLine := to_integer(V_YToCheck - V_SpriteY);
-- pragma translate_off
if V_OffsetLine < 0 or V_OffsetLine >= K_SPRITE_ROW_OFFSETS'length then
V_OffsetLine := 0;
end if;
-- pragma translate_on
V_Offset := to_unsigned(K_SPRITE_ROW_OFFSETS(V_OffsetLine), C_Offset'length);
C_Offset <= std_logic_vector(V_Offset);
end process;
--@ Output register for the visibility of the sprite
I_IsVisible_OutputRegister : entity work.PipelineRegister
generic map(
G_PipelineStages => G_PipelineStages,
G_Width => 1,
G_RegisterBalancing => "backward"
)
port map(
I_CLK => I_CLK,
I_Enable => S_CalculatingPipeline_Enable,
I_Data(0) => C_IsVisible,
O_Data(0) => O_IsVisible
);
--@ Output register for the offset of the sprite
I_Offset_OutputRegister : entity work.PipelineRegister
generic map(
G_PipelineStages => G_PipelineStages,
G_Width => G_Offset_Width,
G_RegisterBalancing => "backward"
)
port map(
I_CLK => I_CLK,
I_Enable => S_CalculatingPipeline_Enable,
I_Data => C_Offset,
O_Data => O_Offset
);
end architecture;