From: rs <> Date: Wed, 10 Dec 2025 02:56:05 +0000 (-0600) Subject: Add some pipeline/DSP entities X-Git-Url: https://git.the-white-hart.net/?a=commitdiff_plain;h=67be3e37ce1be31728f9b6302ed914c9543c8164;p=vhdl Add some pipeline/DSP entities --- diff --git a/libraries/dsp/deserialize.vhd b/libraries/dsp/deserialize.vhd new file mode 100644 index 0000000..d723160 --- /dev/null +++ b/libraries/dsp/deserialize.vhd @@ -0,0 +1,149 @@ +-------------------------------------------------------------------------------- +-- deserialize - Collect N words into a single, wide word +-- +-- Useful for collecting bytes streamed over the USB FIFO interface into +-- strucutred chunks, like two-channel 16-bit PCM streamed in from a WAV file. +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + + +entity deserialize is + generic ( + WIDTH: positive := 8; + N: positive := 4 + ); + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + stb_i: in std_logic; + rdy_o: out std_logic; + dat_i: in std_logic_vector(WIDTH-1 downto 0); + + stb_o: out std_logic; + rdy_i: in std_logic; + dat_o: out std_logic_vector((WIDTH*N)-1 downto 0) + ); +end deserialize; + + +architecture behavioral of deserialize is + + type state_t is (S_IDLE, S_COUNT, S_HOLD); + type word_array_t is array(natural range <>) of std_logic_vector(WIDTH-1 downto 0); + + signal state_reg: state_t; + signal state_next: state_t; + + signal count_en: std_logic; + signal count_start: std_logic; + signal count_done: std_logic; + + signal shift_en: std_logic; + signal shift_reg: word_array_t(3 downto 0); + +begin + + process (rst_i, clk_i, state_next) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + state_reg <= S_IDLE; + else + state_reg <= state_next; + end if; + end if; + end process; + + process (state_reg, count_done, stb_i, rdy_i) + begin + state_next <= state_reg; + count_en <= '0'; + count_start <= '0'; + shift_en <= '0'; + rdy_o <= '0'; + + case state_reg is + when S_IDLE => + rdy_o <= '1'; + if stb_i = '1' then + count_en <= '1'; + count_start <= '1'; + shift_en <= '1'; + state_next <= S_COUNT; + end if; + + when S_COUNT => + if count_done = '1' then + -- Done with this group + if stb_i = '1' and rdy_i = '1' then + -- Source has a byte available and sink is accepting group + -- Acknowledge byte, shift it in, drop counter bit and start new count + rdy_o <= '1'; + shift_en <= '1'; + count_en <= '1'; + count_start <= '1'; + elsif stb_i = '0' and rdy_i = '1' then + -- Sink is accepting group + -- Drop counter bit and return to idle + count_en <= '1'; + state_next <= S_IDLE; + end if; + else + -- Not done with this group yet, source has a byte available + -- Acknowledge byte, shift it in, and advance counter + if stb_i = '1' then + rdy_o <= '1'; + count_en <= '1'; + shift_en <= '1'; + end if; + end if; + + when others => + state_next <= S_IDLE; + end case; + end process; + + -- Word counter SRL + e_counter: srl16e + port map ( + CLK => clk_i, + CE => count_en, + A0 => '1', + A1 => '1', + A2 => '0', + A3 => '0', + D => count_start, + Q => count_done + ); + + stb_o <= count_done; + + -- Shift register to collect bytes in a group + process (clk_i, shift_en, dat_i) + begin + if rising_edge(clk_i) then + if shift_en = '1' then + for i in shift_reg'high-1 downto 0 loop + shift_reg(i) <= shift_reg(i+1); + end loop; + shift_reg(shift_reg'high) <= dat_i; + end if; + end if; + end process; + + -- Assemble bytes from the holding register into concatenated output + process (shift_reg) + begin + for i in N-1 downto 0 loop + dat_o(((i+1)*WIDTH)-1 downto i*WIDTH) <= shift_reg(i); + end loop; + end process; + +end behavioral; diff --git a/libraries/dsp/merge.vhd b/libraries/dsp/merge.vhd new file mode 100644 index 0000000..9fc6dd0 --- /dev/null +++ b/libraries/dsp/merge.vhd @@ -0,0 +1,44 @@ +-------------------------------------------------------------------------------- +-- merge - merge two data streams into a single pipeline +-- +-- This contains a combinational path between the input and output - a runout +-- buffer may be needed to break a long combinational chain to improve clock +-- rate. +-- +-- All streams must be in the same clock domain. +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + + +entity merge is + port ( + a_stb_i: in std_logic; + a_ack_o: out std_logic; + + b_stb_i: in std_logic; + b_ack_o: out std_logic; + + stb_o: out std_logic; + ack_i: in std_logic + ); +end merge; + + +architecture behavioral of merge is + + signal costrobe: std_logic; + +begin + + costrobe <= a_stb_i and b_stb_i; + stb_o <= costrobe; + a_ack_o <= costrobe and ack_i; + b_ack_o <= costrobe and ack_i; + +end behavioral; diff --git a/libraries/dsp/pcm16_2ch_gain.vhd b/libraries/dsp/pcm16_2ch_gain.vhd new file mode 100644 index 0000000..2bdbac2 --- /dev/null +++ b/libraries/dsp/pcm16_2ch_gain.vhd @@ -0,0 +1,76 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + +library dsp; + + +entity pcm16_2ch_gain is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + gain_l: in std_logic_vector(17 downto 0); -- 9.9 fixed point + gain_r: in std_logic_vector(17 downto 0); -- 9.9 fixed point + + stb_i: in std_logic; + rdy_o: out std_logic; + dat_i: in std_logic_vector(31 downto 0); + + stb_o: out std_logic; + rdy_i: in std_logic; + dat_o: out std_logic_vector(31 downto 0) + ); +end pcm16_2ch_gain; + + +architecture behavioral of pcm16_2ch_gain is + + signal samp_l: std_logic_vector(17 downto 0); + signal samp_r: std_logic_vector(17 downto 0); + + signal out_l: std_logic_vector(35 downto 0); + signal out_r: std_logic_vector(35 downto 0); + + signal result_l: std_logic_vector(15 downto 0); + signal result_r: std_logic_vector(15 downto 0); + + signal result: std_logic_vector(31 downto 0); + +begin + + samp_l <= "00" & dat_i(15 downto 0); + samp_r <= "00" & dat_i(31 downto 16); + + out_l <= std_logic_vector(signed(samp_l) * signed(gain_l)); + out_r <= std_logic_vector(signed(samp_r) * signed(gain_r)); + + result <= (out_r(24 downto 9)) & (out_l(24 downto 9)); + + e_sat_l: entity work.saturate + generic map (WIDTH_IN => 27, WIDTH_OUT => 16) + port map (dat_i => out_l(35 downto 9), dat_o => result_l); + + e_sat_r: entity work.saturate + generic map (WIDTH_IN => 27, WIDTH_OUT => 16) + port map (dat_i => out_r(35 downto 9), dat_o => result_r); + + e_interstage: entity dsp.pipectrl + generic map (WIDTH => 32) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => stb_i, + rdy_o => rdy_o, + dat_i => result, + + stb_o => stb_o, + rdy_i => rdy_i, + dat_o => dat_o + ); + +end behavioral; diff --git a/libraries/dsp/pcm16_2ch_sum.vhd b/libraries/dsp/pcm16_2ch_sum.vhd new file mode 100644 index 0000000..9213801 --- /dev/null +++ b/libraries/dsp/pcm16_2ch_sum.vhd @@ -0,0 +1,96 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library work; + + +entity pcm16_2ch_sum is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + a_stb_i: in std_logic; + a_rdy_i: out std_logic; + a_dat_i: in std_logic_vector(31 downto 0); + + b_stb_i: in std_logic; + b_rdy_o: out std_logic; + b_dat_i: in std_logic_vector(31 downto 0); + + stb_o: out std_logic; + rdy_i: in std_logic; + dat_o: out std_logic_vector(31 downto 0) + ); +end pcm16_2ch_sum; + + +architecture behavioral of pcm16_2ch_sum is + + signal samp_a_l: signed(15 downto 0); + signal samp_a_r: signed(15 downto 0); + signal samp_b_l: signed(15 downto 0); + signal samp_b_r: signed(15 downto 0); + + -- Extra bit on each for carry + signal result_l: signed(16 downto 0); + signal result_r: signed(16 downto 0); + + -- Saturated results + signal sat_l: std_logic_vector(15 downto 0); + signal sat_r: std_logic_vector(15 downto 0); + + -- Packaged result + signal result: std_logic_vector(31 downto 0); + + signal stb: std_logic; + signal rdy: std_logic; + +begin + + samp_a_l <= a_dat_i(15 downto 0); + samp_a_r <= a_dat_i(31 downto 16); + samp_b_l <= b_dat_i(15 downto 0); + samp_b_r <= b_dat_i(31 downto 16); + + result_l <= samp_a_l + samp_b_l; + result_r <= samp_a_r + samp_b_r; + + e_sat_l: entity work.saturate + generic map (WIDTH_IN => 17, WIDTH_OUT => 16) + port map (dat_i => result_l, dat_o => sat_l); + + e_sat_r: entity work.saturate + generic map (WIDTH_IN => 17, WIDTH_OUT => 16) + port map (dat_i => result_r, dat_o => sat_r); + + result <= sat_r & sat_l; + + e_merge: entity work.merge + port map ( + a_stb_i => a_stb_i, + a_rdy_o => a_rdy_o, + + b_stb_i => b_stb_i, + b_rdy_o => b_rdy_o, + + stb_o => stb, + rdy_i => rdy + ); + + e_pipectrl: entity work.pipectrl + generic map (WIDTH => 32) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => stb, + rdy_o => rdy, + dat_i => result, + + stb_o => stb_o, + rdy_i => rdy_i, + dat_o => dat_o + ); + +end behavioral; diff --git a/libraries/dsp/pipectrl.vhd b/libraries/dsp/pipectrl.vhd new file mode 100644 index 0000000..b07b22f --- /dev/null +++ b/libraries/dsp/pipectrl.vhd @@ -0,0 +1,61 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity pipectrl is + generic ( + WIDTH: positive := 16 + ); + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + stb_i: in std_logic; + rdy_o: out std_logic; + dat_i: in std_logic_vector(WIDTH-1 downto 0); + + stb_o: out std_logic; + rdy_i: in std_logic; + dat_o: out std_logic_vector(WIDTH-1 downto 0) + ); +end pipectrl; + + +architecture behavioral of pipectrl is + + signal enable: std_logic; + signal stb_reg: std_logic; + signal dat_reg: std_logic_vector(WIDTH-1 downto 0); + +begin + + -- Enable the stage register to latch if: + -- * it is currently empty + -- * if the downstream stage is accepting the current data + enable <= (not stb_reg) or rdy_i; + + stb_o <= stb_reg; + rdy_o <= enable; + dat_o <= dat_reg; + + process (rst_i, clk_i, enable, stb_i) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + stb_reg <= '0'; + elsif enable = '1' then + stb_reg <= stb_i; + end if; + end if; + end process; + + process (clk_i, enable, dat_i) + begin + if rising_edge(clk_i) then + if enable = '1' then + dat_reg <= dat_i; + end if; + end if; + end process; + +end behavioral; diff --git a/libraries/dsp/pipectrl_runout.vhd b/libraries/dsp/pipectrl_runout.vhd new file mode 100644 index 0000000..097f847 --- /dev/null +++ b/libraries/dsp/pipectrl_runout.vhd @@ -0,0 +1,89 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity pipectrl_runout is + generic ( + WIDTH: positive := 16 + ); + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + stb_i: in std_logic; + rdy_o: out std_logic; + dat_i: in std_logic_vector(WIDTH-1 downto 0); + + stb_o: out std_logic; + rdy_i: in std_logic; + dat_o: out std_logic_vector(WIDTH-1 downto 0) + ); +end pipectrl_runout; + + +architecture behavioral of pipectrl_runout is + + -- Registered ready signal + signal rdy_reg: std_logic := '1'; + + -- Normal interstage register + signal en: std_logic; + signal stb_reg: std_logic := '0'; + signal dat_reg: std_logic_vector(WIDTH-1 downto 0); + + -- Run-out-buffer + signal rob_en: std_logic; + signal rob_clr: std_logic; + signal rob_stb_reg: std_logic := '0'; + signal rob_dat_reg: std_logic_vector(WIDTH-1 downto 0); + +begin + + stb_o <= stb_reg or rob_stb_reg; + rdy_o <= rdy_reg; + dat_o <= rob_dat_reg when rob_stb_reg = '1' else dat_reg; + + en <= rdy_reg; + rob_en <= rdy_reg and (not rdy_i); + rob_clr <= rdy_i; + + -- Registered ready signal + process (rst_i, clk_i, rdy_i) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + rdy_reg <= '1'; + else + rdy_reg <= rdy_i; + end if; + end if; + end process; + + -- Normal interstage + process (rst_i, clk_i, en, stb_i, dat_i) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + stb_reg <= '0'; + elsif en = '1' then + stb_reg <= stb_i; + dat_reg <= dat_i; + rob_dat_reg <= dat_reg; -- + end if; + end if; + end process; + + -- Runout buffer + process (rst_i, clk_i, rob_en, rob_clr, stb_reg, dat_reg) + begin + if rising_edge(clk_i) then + if rst_i = '1' or rob_clr = '1' then + rob_stb_reg <= '0'; + elsif rob_en = '1' then + rob_stb_reg <= stb_reg; + --rob_dat_reg <= dat_reg; + end if; + end if; + end process; + +end behavioral; diff --git a/libraries/dsp/pipectrl_runout_srl.vhd b/libraries/dsp/pipectrl_runout_srl.vhd new file mode 100644 index 0000000..2213c7d --- /dev/null +++ b/libraries/dsp/pipectrl_runout_srl.vhd @@ -0,0 +1,106 @@ +library ieee; +use ieee.std_logic_1164.all; + +library unisim; +use unisim.vcomponents.all; + + +entity pipectrl_runout_srl is + generic ( + WIDTH: positive := 16 + ); + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + stb_i: in std_logic; + rdy_o: out std_logic; + dat_i: in std_logic_vector(WIDTH-1 downto 0); + + stb_o: out std_logic; + rdy_i: in std_logic; + dat_o: out std_logic_vector(WIDTH-1 downto 0) + ); +end pipectrl_runout_srl; + + +architecture behavioral of pipectrl_runout_srl is + + -- Registered ready signal + signal rdy_reg: std_logic := '1'; + + -- Normal interstage register + signal en: std_logic; + signal stb_reg: std_logic := '0'; + --signal dat_reg: std_logic_vector(WIDTH-1 downto 0); + + -- Run-out-buffer + signal rob_en: std_logic; + signal rob_clr: std_logic; + signal rob_stb_reg: std_logic := '0'; + --signal rob_dat_reg: std_logic_vector(WIDTH-1 downto 0); + +begin + + stb_o <= stb_reg or rob_stb_reg; + rdy_o <= rdy_reg; + --dat_o <= rob_dat_reg when rob_stb_reg = '1' else dat_reg; + + en <= rdy_reg; + rob_en <= rdy_reg and (not rdy_i); + rob_clr <= rdy_i; + + -- Registered ready signal + process (rst_i, clk_i, rdy_i) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + rdy_reg <= '1'; + else + rdy_reg <= rdy_i; + end if; + end if; + end process; + + g_srl: for i in WIDTH-1 downto 0 generate + e_srl: srl16e + port map ( + d => dat_i(i), + ce => en, + clk => clk_i, + a0 => rob_stb_reg, + a1 => '0', + a2 => '0', + a3 => '0', + q => dat_o(i) + ); + end generate; + + -- Normal interstage + process (rst_i, clk_i, en, stb_i) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + stb_reg <= '0'; + elsif en = '1' then + stb_reg <= stb_i; + --dat_reg <= dat_i; + --rob_dat_reg <= dat_reg; -- + end if; + end if; + end process; + + -- Runout buffer + process (rst_i, clk_i, rob_en, rob_clr, stb_reg) + begin + if rising_edge(clk_i) then + if rst_i = '1' or rob_clr = '1' then + rob_stb_reg <= '0'; + elsif rob_en = '1' then + rob_stb_reg <= stb_reg; + --rob_dat_reg <= dat_reg; + end if; + end if; + end process; + +end behavioral; diff --git a/libraries/dsp/pipectrl_split.vhd b/libraries/dsp/pipectrl_split.vhd new file mode 100644 index 0000000..c3a0dd4 --- /dev/null +++ b/libraries/dsp/pipectrl_split.vhd @@ -0,0 +1,78 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity pipectrl_split is + generic ( + WIDTH: positive := 16 + ); + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + stb_i: in std_logic; + rdy_o: out std_logic; + dat_i: in std_logic_vector(WIDTH-1 downto 0); + + a_stb_o: out std_logic; + a_rdy_i: in std_logic; + + b_stb_o: out std_logic; + b_rdy_i: in std_logic; + + dat_o: out std_logic_vector(WIDTH-1 downto 0) + ); +end pipectrl_split; + + +architecture behavioral of pipectrl_split is + + signal a_stb_reg: std_logic := '0'; + signal a_rdy: std_logic; + + signal b_stb_reg: std_logic := '0'; + signal b_rdy: std_logic; + + signal load: std_logic; + + signal dat_reg: std_logic_vector(WIDTH-1 downto 0); + +begin + + a_rdy <= (not a_stb_reg) or a_rdy_i; + b_rdy <= (not b_stb_reg) or b_rdy_i; + load <= a_rdy and b_rdy; + rdy_o <= load; + + -- Full registers + process (rst_i, clk_i, load, a_rdy_i, b_rdy_i) + begin + if rising_edge(clk_i) then + if load = '1' then + a_stb_reg <= stb_i; + b_stb_reg <= stb_i; + else + if a_rdy_i = '1' then + a_stb_reg <= '0'; + end if; + if b_rdy_i = '1' then + b_stb_reg <= '0'; + end if; + end if; + end if; + end process; + a_stb_o <= a_stb_reg; + b_stb_o <= b_stb_reg; + + -- Interstage register + process (clk_i, load, dat_i) + begin + if rising_edge(clk_i) then + if load = '1' then + dat_reg <= dat_i; + end if; + end if; + end process; + dat_o <= dat_reg; + +end behavioral; diff --git a/libraries/dsp/saturate.vhd b/libraries/dsp/saturate.vhd new file mode 100644 index 0000000..58744fc --- /dev/null +++ b/libraries/dsp/saturate.vhd @@ -0,0 +1,51 @@ +-------------------------------------------------------------------------------- +-- saturate - reduce the precision of a data stream, saturating at max and min +-- +-- This is combinational to allow for composition into more complex pipeline +-- stages. A FIFO or run out buffer can be added to the output to break the +-- combinational chain when used on its own. +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.std_logic_misc.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + + +entity saturate is + port ( + dat_i: in std_logic_vector; + dat_o: out std_logic_vector + ); +end saturate; + + +architecture behavioral of saturate is + + constant WIDTH_IN: positive := dat_i'length; + constant WIDTH_OUT: positive := dat_o'length; + + constant MAX: std_logic_vector(WIDTH_OUT-1 downto 0) := (WIDTH_OUT-1 => '0', others => '1'); + constant MIN: std_logic_vector(WIDTH_OUT-1 downto 0) := (WIDTH_OUT-1 => '1', others => '0'); + + signal upper: std_logic_vector(WIDTH_IN downto WIDTH_OUT); + + signal is_negative: std_logic; + signal is_saturated: std_logic; + +begin + + upper <= dat_i(WIDTH_IN-1 downto WIDTH_OUT-1); + + is_negative <= dat_i(WIDTH_IN-1); + is_saturated <= '0' when and_reduce(upper) = '1' or + nor_reduce(upper) = '1' else '1'; + + dat_o <= dat_i(WIDTH_OUT-1 downto 0) when is_saturated = '0' else + MAX when is_negative = '0' else + MIN; + +end behavioral; diff --git a/libraries/dsp/serialize.vhd b/libraries/dsp/serialize.vhd new file mode 100644 index 0000000..be6efa8 --- /dev/null +++ b/libraries/dsp/serialize.vhd @@ -0,0 +1,149 @@ +-------------------------------------------------------------------------------- +-- serialize - split a wide word into N smaller words +-- +-- Useful for taking structured chunks of data, like two-channel 16-bit PCM +-- samples, and serializing into bytes for transfer over the USB FIFO interface. +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + + +entity serialize is + generic ( + WIDTH: positive := 8; + N: positive := 4 + ); + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + stb_i: in std_logic; + rdy_o: out std_logic; + dat_i: in std_logic_vector((WIDTH*N)-1 downto 0); + + stb_o: out std_logic; + rdy_i: in std_logic; + dat_o: out std_logic_vector(WIDTH-1 downto 0) + ); +end serialize; + + +architecture behavioral of serialize is + + constant SRL_TAP: std_logic_vector(3 downto 0) := std_logic_vector(to_unsigned(N-1, 4)); + + type state_t is ( + S_IDLE, + S_COUNT + ); + type word_array_t is array(natural range <>) of std_logic_vector(WIDTH-1 downto 0); + + signal state_reg: state_t; + signal state_next: state_t; + + signal count_en: std_logic; + signal count_start: std_logic; + signal count_done: std_logic; + + signal shift_ld: std_logic; + signal shift_en: std_logic; + signal shift_reg: word_array_t(N-1 downto 0); + +begin + + -- State machine + process (rst_i, clk_i) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + state_reg <= S_IDLE; + else + state_reg <= state_next; + end if; + end if; + end process; + + process (state_reg, stb_i, rdy_i, count_done) + begin + state_next <= state_reg; + count_start <= '0'; + count_en <= '0'; + shift_ld <= '0'; + shift_en <= '0'; + stb_o <= '0'; + rdy_o <= '0'; + + case state_reg is + when S_IDLE => + rdy_o <= '1'; + if stb_i = '1' then + count_start <= '1'; + count_en <= '1'; + shift_ld <= '1'; + state_next <= S_COUNT; + end if; + + when S_COUNT => + stb_o <= '1'; + if count_done = '1' then + if stb_i = '1' and rdy_i = '1' then + -- Source has a new group available and sink is accepting the last word + rdy_o <= '1'; + count_start <= '1'; + count_en <= '1'; + shift_ld <= '1'; + elsif stb_i = '0' and rdy_i = '1' then + -- Sink is accepting the last word, return to idle + count_en <= '1'; + state_next <= S_IDLE; + end if; + else + -- Not done with this group yet, sink has a byte available + -- Acknowledge byte, shift it in, and advance counter + if rdy_i = '1' then + shift_en <= '1'; + count_en <= '1'; + end if; + end if; + + when others => + state_next <= S_IDLE; + end case; + end process; + + -- Word counter SRL + e_counter: srl16e + port map ( + CLK => clk_i, + CE => count_en, + A0 => SRL_TAP(0), + A1 => SRL_TAP(1), + A2 => SRL_TAP(2), + A3 => SRL_TAP(3), + D => count_start, + Q => count_done + ); + + -- Shift register + process (clk_i, shift_ld, shift_en, shift_reg) + begin + if rising_edge(clk_i) then + if shift_ld = '1' then + for i in N-1 downto 0 loop + shift_reg(i) <= dat_i(((i+1)*WIDTH)-1 downto i*WIDTH); + end loop; + elsif shift_en = '1' then + for i in N-2 downto 0 loop + shift_reg(i) <= shift_reg(i+1); + end loop; + end if; + end if; + end process; + dat_o <= shift_reg(0); + +end behavioral; diff --git a/libraries/dsp/tests/test_deserialize.vhd b/libraries/dsp/tests/test_deserialize.vhd new file mode 100644 index 0000000..b58bed5 --- /dev/null +++ b/libraries/dsp/tests/test_deserialize.vhd @@ -0,0 +1,111 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity test_deserialize is +end test_deserialize; + + +architecture behavior of test_deserialize is + + constant CLK_I_PERIOD: time := 20 ns; + + signal rst_i: std_logic; + signal clk_i: std_logic; + + signal stb_i: std_logic; + signal ack_o: std_logic; + signal dat_i: std_logic_vector(7 downto 0); + + signal stb_o: std_logic; + signal ack_i: std_logic; + signal dat_o: std_logic_vector(31 downto 0); + +begin + + p_test: process + begin + -- Initial values + stb_i <= '0'; + ack_i <= '0'; + + -- Reset + rst_i <= '1'; + wait for CLK_I_PERIOD*17; + rst_i <= '0'; + + -- Test + ack_i <= '1'; + + wait until falling_edge(clk_i); + stb_i <= '1'; + dat_i <= x"11"; + wait until rising_edge(clk_i) and ack_o = '1'; + + wait until falling_edge(clk_i); + stb_i <= '1'; + dat_i <= x"22"; + wait until rising_edge(clk_i) and ack_o = '1'; + + wait until falling_edge(clk_i); + stb_i <= '1'; + dat_i <= x"33"; + wait until rising_edge(clk_i) and ack_o = '1'; + + wait until falling_edge(clk_i); + stb_i <= '1'; + dat_i <= x"44"; + wait until rising_edge(clk_i) and ack_o = '1'; + + wait until falling_edge(clk_i); + stb_i <= '1'; + dat_i <= x"55"; + wait until rising_edge(clk_i) and ack_o = '1'; + + wait until falling_edge(clk_i); + stb_i <= '1'; + dat_i <= x"66"; + wait until rising_edge(clk_i) and ack_o = '1'; + + wait until falling_edge(clk_i); + stb_i <= '1'; + dat_i <= x"77"; + wait until rising_edge(clk_i) and ack_o = '1'; + + wait until falling_edge(clk_i); + stb_i <= '1'; + dat_i <= x"88"; + wait until rising_edge(clk_i) and ack_o = '1'; + + stb_i <= '0'; + + -- Done + wait; + end process; + + e_uut: entity work.deserialize + generic map ( + WIDTH => 8, + N => 4 + ) + port map ( + rst_i => rst_i, + clk_i => clk_i, + stb_i => stb_i, + ack_o => ack_o, + dat_i => dat_i, + stb_o => stb_o, + ack_i => ack_i, + dat_o => dat_o + ); + + p_clk: process + begin + clk_i <= '0'; + wait for CLK_I_PERIOD/2; + clk_i <= '1'; + wait for CLK_I_PERIOD/2; + end process; + +end; diff --git a/libraries/dsp/tests/test_pipectrl.vhd b/libraries/dsp/tests/test_pipectrl.vhd new file mode 100644 index 0000000..fa8b817 --- /dev/null +++ b/libraries/dsp/tests/test_pipectrl.vhd @@ -0,0 +1,168 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library work; + + +entity test_pipectrl is +end test_pipectrl; + + +architecture behavior of test_pipectrl is + + constant CLK_I_PERIOD: time := 20 ns; + + signal rst_i: std_logic; + signal clk_i: std_logic; + + signal src_stb: std_logic; + signal src_rdy: std_logic; + signal src_dat: std_logic_vector(7 downto 0); + + signal s01_stb: std_logic; + signal s01_rdy: std_logic; + signal s01_dat: std_logic_vector(7 downto 0); + + signal s12_stb: std_logic; + signal s12_rdy: std_logic; + signal s12_dat: std_logic_vector(7 downto 0); + + signal snk_stb: std_logic; + signal snk_rdy: std_logic; + signal snk_dat: std_logic_vector(7 downto 0); + +begin + + p_source: process + begin + -- Initial values + src_stb <= '0'; + + -- Reset + rst_i <= '1'; + wait for CLK_I_PERIOD*3; + rst_i <= '0'; + + -- Test + wait until falling_edge(clk_i); + src_stb <= '1'; + src_dat <= x"00"; + wait until rising_edge(clk_i) and src_rdy = '1'; + + wait until falling_edge(clk_i); + src_stb <= '1'; + src_dat <= x"01"; + wait until rising_edge(clk_i) and src_rdy = '1'; + + wait until falling_edge(clk_i); + src_stb <= '1'; + src_dat <= x"02"; + wait until rising_edge(clk_i) and src_rdy = '1'; + + wait until falling_edge(clk_i); + src_stb <= '1'; + src_dat <= x"03"; + wait until rising_edge(clk_i) and src_rdy = '1'; + + wait until falling_edge(clk_i); + src_stb <= '1'; + src_dat <= x"04"; + wait until rising_edge(clk_i) and src_rdy = '1'; + + wait until falling_edge(clk_i); + src_stb <= '0'; + + -- Done + wait; + end process; + + p_sink: process + begin + -- Initial values + snk_rdy <= '0'; + + -- Reset + wait for CLK_I_PERIOD*3; + + -- Test + wait for CLK_I_PERIOD*5; + + wait until falling_edge(clk_i) and snk_stb = '1'; + snk_rdy <= '1'; + wait until rising_edge(clk_i); + + wait until falling_edge(clk_i) and snk_stb = '1'; + snk_rdy <= '1'; + wait until rising_edge(clk_i); + + wait until falling_edge(clk_i) and snk_stb = '1'; + snk_rdy <= '0'; + wait until rising_edge(clk_i); + + wait until falling_edge(clk_i) and snk_stb = '1'; + snk_rdy <= '1'; + wait until rising_edge(clk_i); + + wait until falling_edge(clk_i) and snk_stb = '1'; + snk_rdy <= '1'; + wait until rising_edge(clk_i); + + wait until falling_edge(clk_i) and snk_stb = '1'; + snk_rdy <= '1'; + wait until rising_edge(clk_i); + + snk_rdy <= '0'; + + -- Done + wait; + end process; + + e_stage0: entity work.pipectrl + generic map (WIDTH => 8) + port map ( + rst_i => rst_i, + clk_i => clk_i, + stb_i => src_stb, + rdy_o => src_rdy, + dat_i => src_dat, + stb_o => s01_stb, + rdy_i => s01_rdy, + dat_o => s01_dat + ); + + e_stage1: entity work.pipectrl_runout_srl + generic map (WIDTH => 8) + port map ( + rst_i => rst_i, + clk_i => clk_i, + stb_i => s01_stb, + rdy_o => s01_rdy, + dat_i => s01_dat, + stb_o => s12_stb, + rdy_i => s12_rdy, + dat_o => s12_dat + ); + + e_stage2: entity work.pipectrl + generic map (WIDTH => 8) + port map ( + rst_i => rst_i, + clk_i => clk_i, + stb_i => s12_stb, + rdy_o => s12_rdy, + dat_i => s12_dat, + stb_o => snk_stb, + rdy_i => snk_rdy, + dat_o => snk_dat + ); + + p_clk: process + begin + clk_i <= '0'; + wait for CLK_I_PERIOD/2; + clk_i <= '1'; + wait for CLK_I_PERIOD/2; + end process; + +end; diff --git a/libraries/dsp/tests/test_pipectrl_split.vhd b/libraries/dsp/tests/test_pipectrl_split.vhd new file mode 100644 index 0000000..0e06166 --- /dev/null +++ b/libraries/dsp/tests/test_pipectrl_split.vhd @@ -0,0 +1,190 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library work; + + +entity test_pipectrl_split is +end test_pipectrl_split; + + +architecture behavior of test_pipectrl_split is + + constant CLK_I_PERIOD: time := 20 ns; + + signal rst_i: std_logic; + signal clk_i: std_logic; + + -- Source + signal src_stb: std_logic; + signal src_rdy: std_logic; + signal src_dat: std_logic_vector(7 downto 0); + + -- Stage 0 output + signal s0_stb: std_logic; + signal s0_rdy: std_logic; + signal s0_dat: std_logic_vector(7 downto 0); + + -- Stage 1 (splitter) output + signal s1a_stb: std_logic; + signal s1a_rdy: std_logic; + signal s1b_stb: std_logic; + signal s1b_rdy: std_logic; + signal s1_dat: std_logic_vector(7 downto 0); + + -- Stage 2a + signal s2a_stb: std_logic; + signal s2a_rdy: std_logic; + signal s2a_dat: std_logic_vector(7 downto 0); + + -- Stage 2b + signal s2b_stb: std_logic; + signal s2b_rdy: std_logic; + signal s2b_dat: std_logic_vector(7 downto 0); + +begin + + ---------------------------------------------------------------------------- + -- Data stream source + p_source: process + begin + -- Initial values + src_stb <= '0'; + + -- Reset + rst_i <= '1'; + wait for CLK_I_PERIOD*3; + rst_i <= '0'; + + -- Test + + -- Insert 0x00 + src_dat <= x"00"; + src_stb <= '1'; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + -- Insert 0x01 + src_dat <= x"01"; + src_stb <= '1'; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + -- Insert 0x02 + src_dat <= x"02"; + src_stb <= '1'; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + -- Insert 0x03 + src_dat <= x"03"; + src_stb <= '1'; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + -- Insert 0x04 + src_dat <= x"04"; + src_stb <= '1'; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + -- Done + wait; + end process; + + ---------------------------------------------------------------------------- + -- Data sink A + p_sink_a: process + begin + -- Initial values + s2a_rdy <= '0'; + + -- Test + s2a_rdy <= '1'; -- Remove all values + + -- Done + wait; + end process; + + ---------------------------------------------------------------------------- + -- Data sink B + p_sink_b: process + begin + -- Initial values + s2b_rdy <= '0'; + + -- Test + wait until s2b_stb = '1'; -- Wait for values to enter + wait for CLK_I_PERIOD*4; -- Wait four more clocks + s2b_rdy <= '1'; -- remove all values + + -- Done + wait; + end process; + + ---------------------------------------------------------------------------- + + e_s0: entity work.pipectrl + generic map (WIDTH => 8) + port map ( + rst_i => rst_i, + clk_i => clk_i, + stb_i => src_stb, + rdy_o => src_rdy, + dat_i => src_dat, + stb_o => s0_stb, + rdy_i => s0_rdy, + dat_o => s0_dat + ); + + e_s1: entity work.pipectrl_split + generic map (WIDTH => 8) + port map ( + rst_i => rst_i, + clk_i => clk_i, + stb_i => s0_stb, + rdy_o => s0_rdy, + dat_i => s0_dat, + a_stb_o => s1a_stb, + a_rdy_i => s1a_rdy, + b_stb_o => s1b_stb, + b_rdy_i => s1b_rdy, + dat_o => s1_dat + ); + + e_s2a: entity work.pipectrl + generic map (WIDTH => 8) + port map ( + rst_i => rst_i, + clk_i => clk_i, + stb_i => s1a_stb, + rdy_o => s1a_rdy, + dat_i => s1_dat, + stb_o => s2a_stb, + rdy_i => s2a_rdy, + dat_o => s2a_dat + ); + + e_s2b: entity work.pipectrl + generic map (WIDTH => 8) + port map ( + rst_i => rst_i, + clk_i => clk_i, + stb_i => s1b_stb, + rdy_o => s1b_rdy, + dat_i => s1_dat, + stb_o => s2b_stb, + rdy_i => s2b_rdy, + dat_o => s2b_dat + ); + + p_clk: process + begin + clk_i <= '0'; + wait for CLK_I_PERIOD/2; + clk_i <= '1'; + wait for CLK_I_PERIOD/2; + end process; + +end; diff --git a/libraries/dsp/tests/test_saturate.vhd b/libraries/dsp/tests/test_saturate.vhd new file mode 100644 index 0000000..c71d40d --- /dev/null +++ b/libraries/dsp/tests/test_saturate.vhd @@ -0,0 +1,80 @@ +library ieee; +use ieee.std_logic_1164.all; + +library work; + + +entity test_saturate is +end test_saturate; + + +architecture behavior of test_saturate is + + signal dat_i: std_logic_vector(19 downto 0); + signal dat_o: std_logic_vector(15 downto 0); + +begin + + p_test: process + begin + dat_i <= x"00000"; + wait for 5 ns; + assert dat_o = x"0000"; + wait for 5 ns; + + dat_i <= x"fffff"; + wait for 5 ns; + assert dat_o = x"ffff"; + wait for 5 ns; + + dat_i <= x"0005a"; + wait for 5 ns; + assert dat_o = x"005a"; + wait for 5 ns; + + dat_i <= x"fffc3"; + wait for 5 ns; + assert dat_o = x"ffc3"; + wait for 5 ns; + + dat_i <= x"10045"; + wait for 5 ns; + assert dat_o = x"7fff"; + wait for 5 ns; + + dat_i <= x"7ff23"; + wait for 5 ns; + assert dat_o = x"7fff"; + wait for 5 ns; + + dat_i <= x"80056"; + wait for 5 ns; + assert dat_o = x"8000"; + wait for 5 ns; + + dat_i <= x"f0021"; + wait for 5 ns; + assert dat_o = x"8000"; + wait for 5 ns; + + dat_i <= x"08045"; + wait for 5 ns; + assert dat_o = x"7fff"; + wait for 5 ns; + + dat_i <= x"f7009"; + wait for 5 ns; + assert dat_o = x"8000"; + wait for 5 ns; + + report "done"; + wait; + end process; + + e_uut: entity work.saturate + port map ( + dat_i => dat_i, + dat_o => dat_o + ); + +end; diff --git a/libraries/dsp/tests/test_serdes.vhd b/libraries/dsp/tests/test_serdes.vhd new file mode 100644 index 0000000..e9dda07 --- /dev/null +++ b/libraries/dsp/tests/test_serdes.vhd @@ -0,0 +1,174 @@ +library ieee; +use ieee.std_logic_1164.all; + +library work; + + +entity test_serdes is +end test_serdes; + + +architecture behavior of test_serdes is + + constant CLK_I_PERIOD: time := 20 ns; + + signal rst_i: std_logic; + signal clk_i: std_logic; + + signal src_stb: std_logic; + signal src_rdy: std_logic; + signal src_dat: std_logic_vector(7 downto 0); + + signal x_stb: std_logic; + signal x_rdy: std_logic; + signal x_dat: std_logic_vector(31 downto 0); + + signal snk_stb: std_logic; + signal snk_rdy: std_logic; + signal snk_dat: std_logic_vector(7 downto 0); + +begin + + p_source: process + begin + -- Initial values + src_stb <= '0'; + + -- Reset + rst_i <= '1'; + wait for CLK_I_PERIOD*3; + rst_i <= '0'; + + -- Test + + src_stb <= '1'; + src_dat <= x"00"; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + src_stb <= '1'; + src_dat <= x"01"; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + src_stb <= '1'; + src_dat <= x"02"; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + src_stb <= '1'; + src_dat <= x"03"; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + src_stb <= '1'; + src_dat <= x"04"; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + src_stb <= '1'; + src_dat <= x"05"; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + src_stb <= '1'; + src_dat <= x"06"; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + src_stb <= '1'; + src_dat <= x"07"; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + src_stb <= '1'; + src_dat <= x"08"; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + src_stb <= '1'; + src_dat <= x"09"; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + src_stb <= '1'; + src_dat <= x"0a"; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + src_stb <= '1'; + src_dat <= x"0b"; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + src_stb <= '1'; + src_dat <= x"0c"; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + src_stb <= '1'; + src_dat <= x"0d"; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + src_stb <= '1'; + src_dat <= x"0e"; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + src_stb <= '1'; + src_dat <= x"0f"; + wait until rising_edge(clk_i) and src_rdy = '1'; + src_stb <= '0'; + + -- Done + wait; + end process; + + p_sink: process + begin + -- Initial values + snk_rdy <= '0'; + + -- Test + snk_rdy <= '1'; + + -- Done + wait; + end process; + + e_deserialize: entity work.deserialize + generic map (WIDTH => 8, N => 4) + port map ( + rst_i => rst_i, + clk_i => clk_i, + stb_i => src_stb, + rdy_o => src_rdy, + dat_i => src_dat, + stb_o => x_stb, + rdy_i => x_rdy, + dat_o => x_dat + ); + + e_serialize: entity work.serialize + generic map (WIDTH => 8, N => 4) + port map ( + rst_i => rst_i, + clk_i => clk_i, + stb_i => x_stb, + rdy_o => x_rdy, + dat_i => x_dat, + stb_o => snk_stb, + rdy_i => snk_rdy, + dat_o => snk_dat + ); + + p_clk: process + begin + clk_i <= '0'; + wait for CLK_I_PERIOD/2; + clk_i <= '1'; + wait for CLK_I_PERIOD/2; + end process; + +end; diff --git a/libraries/dsp/tests/test_serialize.vhd b/libraries/dsp/tests/test_serialize.vhd new file mode 100644 index 0000000..3ee8002 --- /dev/null +++ b/libraries/dsp/tests/test_serialize.vhd @@ -0,0 +1,81 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + + +entity test_serialize is +end test_serialize; + + +architecture behavior of test_serialize is + + constant CLK_I_PERIOD: time := 20 ns; + + signal rst_i: std_logic; + signal clk_i: std_logic; + + signal stb_i: std_logic; + signal ack_o: std_logic; + signal dat_i: std_logic_vector(31 downto 0); + + signal stb_o: std_logic; + signal ack_i: std_logic; + signal dat_o: std_logic_vector(7 downto 0); + +begin + + p_test: process + begin + -- Initial values + stb_i <= '0'; + ack_i <= '0'; + + -- Reset + rst_i <= '1'; + wait for CLK_I_PERIOD*17; + rst_i <= '0'; + + -- Test + ack_i <= '1'; + + wait until falling_edge(clk_i); + stb_i <= '1'; + dat_i <= x"44332211"; + wait until rising_edge(clk_i) and ack_o = '1'; + + wait until falling_edge(clk_i); + stb_i <= '1'; + dat_i <= x"88776655"; + wait until rising_edge(clk_i) and ack_o = '1'; + + stb_i <= '0'; + + -- Done + wait; + end process; + + e_uut: entity work.serialize + generic map ( + WIDTH => 8, + N => 4 + ) + port map ( + rst_i => rst_i, + clk_i => clk_i, + stb_i => stb_i, + ack_o => ack_o, + dat_i => dat_i, + stb_o => stb_o, + ack_i => ack_i, + dat_o => dat_o + ); + + p_clk: process + begin + clk_i <= '0'; + wait for CLK_I_PERIOD/2; + clk_i <= '1'; + wait for CLK_I_PERIOD/2; + end process; + +end;