----------------------------------------------------------------------------------
--@ - Name: **Pipeline Controller**
--@ - Version: 0.0.2
--@ - Author: _Maximilian Passarello ([Blog](mpassarello.de))_
--@ - License: [MIT](LICENSE)
--@
--@ The Pipeline Controller provides an easy way to construct a pipeline
--@ with AXI-Like handshaking at the input and output of the pipeline.
--@
--@ ### Core functions
--@
--@ - **Data flow control**: Data flow control is implemented via handshaking at the input and output ports.
--@ - **Validity control**: The controller keeps the validity of the data in the individual pipeline stages under control.
--@ - **Adjustability**: The pipeline controller can be customized via the generics.
--@
--@ ### Generics
--@
--@ Use the generic `G_PipelineStages` to set how deep the pipeline is.
--@ This depth contains all the registers associated with the pipeline.
--@ For example, for an _I_FF ⇨ Combinatorics ⇨ O_FF_ construction, the generic must be set to **2**.
--@
--@ The active level of the reset input can also be set.
--@
--@ ### Clock Enable
--@
--@ The `I_CE` port is active high and, when deactivated,
--@ effectively switches on the acceptance or output of data via handshaking in addition to the pipeline.
--@
--@ ### Reset
--@
--@ A reset is explicitly **not** necessary on the pipeline registers.
--@ The validity of the data is kept under control via the pipeline controller
--@ and only this requires a dedicated reset if necessary.
--@
--@ ### Pipeline control
--@
--@ You must connect the `O_Enable` port to the CE input of the corresponding pipeline registers.
--@ This is used to activate or deactivate the pipeline in full or via CE deactivated state.
--@
--@ ### AXI like Handshaking
--@
--@ - **Input**: The `O_Ready` (active high) port is used to signal to the data-supplying component that data should be accepted.
--@ If it switches on `I_Valid` (active high), this in turn signals that data is ready to be accepted at its output.
--@ If both ports are active at the same time, the transfer is executed.
--@ - **Output**: The process runs analogously at the pipeline output.
--@
--@ ## History
--@ - 0.0.1 (2024-03-24) Initial version
--@ - 0.0.2 (2024-04-13) Enhanced the validity update logic to correctly handle configurations with a single pipeline stage
----------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;
entity PipelineController is
generic (
--@ Number of pipeline stages (FFs in the pipeline including I/O FFs)
G_PipelineStages : integer := 3;
--@ Reset active at this level
G_ResetActiveAt : std_logic := '1'
);
port (
--@ Clock signal; **Rising edge** triggered
I_CLK : in std_logic := '0';
--@ Reset signal; Active at `G_ResetActiveAt`
I_RST : in std_logic := '0';
--@ Chip enable; Active high
I_CE : in std_logic := '1';
--@ Pipeline enable; Active high when pipeline can accept data and `I_CE` is high.
--@ **Note:** Connect `CE` of the registers to be controlled by this controller to `O_Enable`.
O_Enable : out std_logic := '0';
--@ @virtualbus Input-AXI-Handshake @dir in Input AXI like Handshake
--@ Valid data flag; indicates that the data on `I_Data` of the connected registers is valid.
I_Valid : in std_logic := '0';
--@ Ready flag; indicates that the connected registers is ready to accept data.
O_Ready : out std_logic := '0';
--@ @end
--@ @virtualbus Output-AXI-Handshake @dir out Output AXI like Handshake
--@ Valid data flag; indicates that the data on `O_Data` of the connected registers is valid.
O_Valid : out std_logic := '0';
--@ Ready flag; indicates that the external component is ready to accept data.
I_Ready : in std_logic := '0'
--@ @end
);
end entity PipelineController;
architecture RTL of PipelineController is
--@ Pipeline ready signal for each stage of the pipeline to indicate that the data in pipeline is valid
signal R_Valid : std_logic_vector(G_PipelineStages - 1 downto 0) := (others => '0');
--@ Ready signal for the pipeline controller to indicate that the pipeline can accept data;
--@ mapped to `O_Enable` and `O_Ready` ports.
signal C_Ready : std_logic := '1';
begin
GEN_ForwardExternalFlags : if G_PipelineStages = 0 generate
--@ If no pipeline stages are defined, the flags are directly connected to the input and output ports.
P_ExternalFlags : process (I_CE, I_Valid, I_Ready)
begin
O_Valid <= I_Valid;
O_Enable <= I_Ready and I_CE;
O_Ready <= I_Ready and I_CE;
end process;
end generate;
GEN_ExternalFlags : if G_PipelineStages > 0 generate
--@ Produce the `O_Valid`, `O_Enable`, and `O_Ready` signals for the pipeline controller.
--@ - `O_Enable`, and `O_Ready` are **and** combined from the `C_Ready` and `I_CE` signals.
--@ - `O_Valid` is the last bit of the `R_Valid` signal
--@ and represents the validity of the data in the last stage of the pipeline.
P_ExternalFlags : process (R_Valid, C_Ready, I_CE)
begin
O_Valid <= R_Valid(R_Valid'high);
O_Enable <= C_Ready and I_CE;
O_Ready <= C_Ready and I_CE;
end process;
end generate;
GEN_InternalFlags : if G_PipelineStages > 0 generate
--@ Produce the `C_Ready` signal for the pipeline controller,
--@ controlling the data flow in the pipeline.
--@ `C_Ready` is asserted when the data is available in the last stage of the pipeline
--@ **and** the external component is ready to accept data
--@ **or** when no data is available in the last stage of the pipeline.
P_InternalFlags : process (R_Valid, I_Ready)
begin
if R_Valid(R_Valid'high) = '1' then
-- Data is available in the last stage of the pipeline.
if I_Ready = '1' then
-- O_Data is accepted from the external component.
C_Ready <= '1';
else
-- O_Data is not accepted from the external component.
C_Ready <= '0';
end if;
else
-- No data available in the last stage of the pipeline.
C_Ready <= '1';
end if;
end process;
end generate;
GEN_ValidPipe : if G_PipelineStages > 0 generate
--@ Shift the pipeline stages with `R_Valid` signal as placeholder to control
--@ the validity of the data in the individual pipeline stages.
P_ValidPipeline : process (I_CLK)
begin
if rising_edge(I_CLK) then
if I_RST = G_ResetActiveAt then
R_Valid <= (others => '0');
elsif I_CE = '1' then
if C_Ready = '1' then
if G_PipelineStages = 1 then
R_Valid(0) <= I_Valid;
else
R_Valid <= R_Valid(R_Valid'high - 1 downto R_Valid'low) & I_Valid;
end if;
end if;
end if;
end if;
end process;
end generate;
end architecture RTL;