]> git.the-white-hart.net Git - vhdl/commitdiff
Add some pipeline/DSP entities main
authorrs <>
Wed, 10 Dec 2025 02:56:05 +0000 (20:56 -0600)
committerrs <>
Wed, 10 Dec 2025 02:56:05 +0000 (20:56 -0600)
16 files changed:
libraries/dsp/deserialize.vhd [new file with mode: 0644]
libraries/dsp/merge.vhd [new file with mode: 0644]
libraries/dsp/pcm16_2ch_gain.vhd [new file with mode: 0644]
libraries/dsp/pcm16_2ch_sum.vhd [new file with mode: 0644]
libraries/dsp/pipectrl.vhd [new file with mode: 0644]
libraries/dsp/pipectrl_runout.vhd [new file with mode: 0644]
libraries/dsp/pipectrl_runout_srl.vhd [new file with mode: 0644]
libraries/dsp/pipectrl_split.vhd [new file with mode: 0644]
libraries/dsp/saturate.vhd [new file with mode: 0644]
libraries/dsp/serialize.vhd [new file with mode: 0644]
libraries/dsp/tests/test_deserialize.vhd [new file with mode: 0644]
libraries/dsp/tests/test_pipectrl.vhd [new file with mode: 0644]
libraries/dsp/tests/test_pipectrl_split.vhd [new file with mode: 0644]
libraries/dsp/tests/test_saturate.vhd [new file with mode: 0644]
libraries/dsp/tests/test_serdes.vhd [new file with mode: 0644]
libraries/dsp/tests/test_serialize.vhd [new file with mode: 0644]

diff --git a/libraries/dsp/deserialize.vhd b/libraries/dsp/deserialize.vhd
new file mode 100644 (file)
index 0000000..d723160
--- /dev/null
@@ -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 (file)
index 0000000..9fc6dd0
--- /dev/null
@@ -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 (file)
index 0000000..2bdbac2
--- /dev/null
@@ -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 (file)
index 0000000..9213801
--- /dev/null
@@ -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 (file)
index 0000000..b07b22f
--- /dev/null
@@ -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 (file)
index 0000000..097f847
--- /dev/null
@@ -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 (file)
index 0000000..2213c7d
--- /dev/null
@@ -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 (file)
index 0000000..c3a0dd4
--- /dev/null
@@ -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 (file)
index 0000000..58744fc
--- /dev/null
@@ -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 (file)
index 0000000..be6efa8
--- /dev/null
@@ -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 (file)
index 0000000..b58bed5
--- /dev/null
@@ -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 (file)
index 0000000..fa8b817
--- /dev/null
@@ -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 (file)
index 0000000..0e06166
--- /dev/null
@@ -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 (file)
index 0000000..c71d40d
--- /dev/null
@@ -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 (file)
index 0000000..e9dda07
--- /dev/null
@@ -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 (file)
index 0000000..3ee8002
--- /dev/null
@@ -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;