From 286ae5a12c0f212733f8903fab931de3c37f3501 Mon Sep 17 00:00:00 2001 From: MaxP Date: Sat, 19 Apr 2025 20:39:13 +0000 Subject: [PATCH] Adds pipeline buffer and controller with testbench Implements a pipeline buffer component supporting passthrough and register modes, controlled via a dedicated controller. Adds AXI-like handshake signals for data flow management. Includes a testbench to validate functionality with randomized delays. Addresses robust data buffering and flow control. --- src/PipelineBuffer.vhd | 52 ++++++++++ src/PipelineBufferController.vhd | 73 +++++++++++++ tests/PipelineBuffer_tb.vhd | 169 +++++++++++++++++++++++++++++++ 3 files changed, 294 insertions(+) create mode 100644 src/PipelineBuffer.vhd create mode 100644 src/PipelineBufferController.vhd create mode 100644 tests/PipelineBuffer_tb.vhd diff --git a/src/PipelineBuffer.vhd b/src/PipelineBuffer.vhd new file mode 100644 index 0000000..b438fdc --- /dev/null +++ b/src/PipelineBuffer.vhd @@ -0,0 +1,52 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.math_real.all; + +entity PipelineBuffer is + generic ( + --@ Data width + G_Width : integer := 32 + ); + port ( + --@ Clock signal; (**Rising edge** triggered) + I_CLK : in std_logic := '0'; + --@ Enable input from **Pipeline Buffer Controller** + --@ [1]: If low, data is passed through, else data is registered + --@ [0]: Enable for register + I_Enable : in std_logic_vector(1 downto 0) := (others => '0'); + --@ Data input + I_Data : in std_logic_vector(G_Width - 1 downto 0) := (others => '0'); + --@ Data output + O_Data : out std_logic_vector(G_Width - 1 downto 0) := (others => '0') + ); +end entity PipelineBuffer; + +architecture RTL of PipelineBuffer is + signal C_MUX : std_logic := '0'; + signal C_Enable : std_logic := '0'; + + signal R_Data : std_logic_vector(G_Width - 1 downto 0) := (others => '0'); +begin + + C_MUX <= I_Enable(1); + C_Enable <= I_Enable(0); + + P_MUX : process (C_MUX, I_Data, R_Data) + begin + if C_MUX = '0' then -- Passthrough mode + O_Data <= I_Data; + else -- Register mode + O_Data <= R_Data; + end if; + end process; + + P_Register : process (I_CLK) + begin + if rising_edge(I_CLK) then + if C_Enable = '1' then + R_Data <= I_Data; + end if; + end if; + end process; +end architecture; diff --git a/src/PipelineBufferController.vhd b/src/PipelineBufferController.vhd new file mode 100644 index 0000000..4f4af77 --- /dev/null +++ b/src/PipelineBufferController.vhd @@ -0,0 +1,73 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.math_real.all; + +entity PipelineBufferController is + generic ( + --@ Reset active at this level + G_ResetActiveAt : std_logic := '1' + ); + port ( + --@ Clock signal; (**Rising edge** triggered) + I_CLK : in std_logic := '0'; + --@ Reset; (**Synchronous**, **Active at `G_ResetActiveAt`**) + I_RST : in std_logic := '0'; + --@ Chip enable; (**Synchronous**, **Active high**) + I_CE : in std_logic := '1'; + + --@ [1]: If low, data is passed through, else data is registered + --@ [0]: Enable for register + O_Enable : out std_logic_vector(1 downto 0) := (others => '0'); + + --@ @virtualbus AXI-Flags-In @dir In Input interface for AXI-like handshake + --@ AXI like valid; (**Synchronous**, **Active high**) + I_Valid : in std_logic := '0'; + --@ AXI like ready; (**Synchronous**, **Active high**) + O_Ready : out std_logic := '0'; + --@ @end + + --@ @virtualbus AXI-Flags-Out @dir Out Output interface for AXI-like handshake + --@ AXI like valid; (**Synchronous**, **Active high**) + O_Valid : out std_logic := '0'; + --@ AXI like ready; (**Synchronous**, **Active high**) + I_Ready : in std_logic := '0' + --@ @end + ); +end entity PipelineBufferController; + +architecture RTL of PipelineBufferController is + signal C_MUX : std_logic := '0'; + signal C_Enable : std_logic := '0'; + + signal R_IsBuffered : std_logic := '0'; +begin + + --@ Set mux to buffered mode if data is available in the buffer. + C_MUX <= R_IsBuffered; + --@ Enable the buffer register if not buffered and chip enable is high. + C_Enable <= I_CE and not R_IsBuffered; + --@ Set the ready signal to high if not buffered. + O_Ready <= not R_IsBuffered; + --@ Set the valid signal to high if data is available in the buffer or if data is valid. + O_Valid <= R_IsBuffered or I_Valid; + + process (I_CLK) + begin + if rising_edge(I_CLK) then + if I_RST = G_ResetActiveAt then + R_IsBuffered <= '0'; + elsif I_CE = '1' then + if R_IsBuffered = '0' and I_Valid = '1' then + R_IsBuffered <= '1'; + elsif I_Ready = '1' and (R_IsBuffered or I_Valid) = '1' then + R_IsBuffered <= '0'; + end if; + end if; + end if; + end process; + + O_Enable(1) <= C_MUX; + O_Enable(0) <= C_Enable; + +end architecture; diff --git a/tests/PipelineBuffer_tb.vhd b/tests/PipelineBuffer_tb.vhd new file mode 100644 index 0000000..efde32f --- /dev/null +++ b/tests/PipelineBuffer_tb.vhd @@ -0,0 +1,169 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; +use ieee.math_real.all; +use std.env.stop; + +entity PipelineBuffer_tb is +end entity PipelineBuffer_tb; + +architecture RTL of PipelineBuffer_tb is + -- Clock signal period + constant K_Period : time := 20 ns; + + -- Zufallsverzögerungen + constant K_WriteDelay : natural := 40; + constant K_ReadDelay : natural := 60; + + -- Konstanten + constant G_ResetActiveAt : std_logic := '1'; + constant G_Width : integer := 32; + + -- Zufalls-Seed + shared variable seed1 : integer := 42; + shared variable seed2 : integer := 1337; + + -- Randomfunktion + impure function rand_int(min_val, max_val : integer) return integer is + variable r : real; + begin + uniform(seed1, seed2, r); + return integer(round(r * real(max_val - min_val + 1) + real(min_val) - 0.5)); + end function; + + -- Signale + signal I_CLK : std_logic := '0'; + signal I_RST : std_logic := '1'; + signal I_CE : std_logic := '1'; + signal O_Enable : std_logic_vector(1 downto 0) := (others => '0'); + signal I_Valid : std_logic := '0'; + signal O_Ready : std_logic := '0'; + signal O_Valid : std_logic := '0'; + signal I_Ready : std_logic := '0'; + + signal I_Data : std_logic_vector(G_Width - 1 downto 0) := (others => 'U'); + signal O_Data : std_logic_vector(G_Width - 1 downto 0) := (others => 'U'); + +begin + -- Clock + Clocking : process + begin + while true loop + I_CLK <= '0'; + wait for (K_Period / 2); + I_CLK <= '1'; + wait for (K_Period / 2); + end loop; + end process; + + -- Reset + I_RST <= G_ResetActiveAt, not G_ResetActiveAt after 100 ns; + + -- DUT: Controller + i_PipelineBufferController : entity work.PipelineBufferController + generic map( + G_ResetActiveAt => G_ResetActiveAt + ) + port map( + I_CLK => I_CLK, + I_RST => I_RST, + I_CE => I_CE, + O_Enable => O_Enable, + I_Valid => I_Valid, + O_Ready => O_Ready, + O_Valid => O_Valid, + I_Ready => I_Ready + ); + + -- DUT: Register + i_PipelineBuffer : entity work.PipelineBuffer + generic map( + G_Width => G_Width + ) + port map( + I_CLK => I_CLK, + I_Enable => O_Enable, + I_Data => I_Data, + O_Data => O_Data + ); + + -- Write Stimulus + Stim_Write : process + variable delay : integer := 0; + variable i : integer := 1; + variable pending : boolean := false; + begin + I_Valid <= '0'; + I_Data <= (others => 'U'); + wait until I_RST = '0'; + + while true loop + wait until rising_edge(I_CLK); + + -- Neues Paket vorbereiten + if not pending and delay = 0 then + I_Data <= std_logic_vector(to_unsigned(i, G_Width)); + I_Valid <= '1'; + pending := true; + report "Sende Paket #" & integer'image(i) severity note; + end if; + + -- Handshake erfolgt + if O_Ready = '1' and I_Valid = '1' then + I_Valid <= '0'; + i := i + 1; + delay := rand_int(1, K_WriteDelay); + pending := false; + end if; + + -- Verzögerung herunterzählen + if delay > 0 and not pending then + delay := delay - 1; + end if; + end loop; + end process; + + -- Read Stimulus (robust) + Stim_Read : process + variable delay : integer := 0; + variable expected : integer := 1; + variable received : integer; + variable consume_now : boolean := false; + begin + I_Ready <= '0'; + wait until I_RST = '0'; + + while true loop + wait until rising_edge(I_CLK); + + -- Wenn O_Valid vorhanden und kein Delay mehr: jetzt lesen + if O_Valid = '1' and delay = 0 and not consume_now then + I_Ready <= '1'; + consume_now := true; -- Warte auf nächste Gültigkeit + end if; + + -- Sobald Handshake erfolgt (O_Valid & I_Ready), auswerten + if O_Valid = '1' and I_Ready = '1' and consume_now then + received := to_integer(unsigned(O_Data)); + if received = expected then + report "Empfange Paket #" & integer'image(expected) severity note; + else + report "FEHLER bei Paket #" & integer'image(expected) & + ": erwartet " & integer'image(expected) & + ", empfangen " & integer'image(received) severity error; + end if; + + expected := expected + 1; + delay := rand_int(1, K_ReadDelay); + consume_now := false; + I_Ready <= '0'; + end if; + + -- Wartezeit herunterzählen + if delay > 0 and not consume_now then + delay := delay - 1; + end if; + end loop; + end process; + +end architecture RTL;