From: rs <> Date: Sat, 31 Jan 2026 21:59:04 +0000 (-0600) Subject: Update a lot of things at once for mp3tape project X-Git-Url: https://git.the-white-hart.net/?a=commitdiff_plain;h=15f326127144c39b61435485570ee407eb1ad1d6;p=vhdl Update a lot of things at once for mp3tape project Doing all of this in one commit was not a good move. I'm sorry, but it had to be done. --- diff --git a/libraries/dsp/deserialize.vhd b/libraries/dsp/deserialize.vhd index d723160..09086c6 100644 --- a/libraries/dsp/deserialize.vhd +++ b/libraries/dsp/deserialize.vhd @@ -1,5 +1,5 @@ -------------------------------------------------------------------------------- --- deserialize - Collect N words into a single, wide word +-- deserialize - Collect N words into a single, wide word (little-endian) -- -- 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. diff --git a/libraries/dsp/i2s_ctrl.vhd b/libraries/dsp/i2s_ctrl.vhd deleted file mode 100644 index 13cf406..0000000 --- a/libraries/dsp/i2s_ctrl.vhd +++ /dev/null @@ -1,163 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library unisim; -use unisim.vcomponents.all; - - -entity i2s_ctrl is - port ( - rst_i: in std_logic; - clk_50: in std_logic; -- 50 MHz - - -- For external use - mclk: out std_logic; -- MCLK, 256*LRCLK(WS) - ws: out std_logic; -- WS/LRCLK, 44.1028 kHz (good enough for government work) - sck: out std_logic; - - -- For internal use, rising and falling edges of SCK in MCLK domain - rise: out std_logic; - fall: out std_logic - ); -end i2s_ctrl; - - -architecture behavioral of i2s_ctrl is - - signal mclk_orig: std_logic; -- Output from DCM - signal mclk_int: std_logic; -- DCM output after clock buffer - signal sck_int: std_logic; - signal sck_reg: std_logic; - - signal ws_tick: std_logic; -- For WS generation - signal ws_carry: std_logic; - signal ws_reg: std_logic := '0'; - - signal rise_int: std_logic; - signal fall_int: std_logic; - signal rise_reg: std_logic; - signal fall_reg: std_logic; - -begin - - e_dcm: dcm_sp - generic map ( - CLK_FEEDBACK => "NONE", - --CLKDV_DIVIDE => 1, - CLKFX_DIVIDE => 31, - CLKFX_MULTIPLY => 7, - CLKIN_DIVIDE_BY_2 => false, - CLKIN_PERIOD => 20.0, - --CLKOUT_PHASE_SHIFT => "NONE", - --DESKEW_ADJUST => "SYSTEM_SYNCHRONOUS", - --FACTORY_JF => - --PHASE_SHIFT => 0, - DFS_FREQUENCY_MODE => "LOW", - --DLL_FREQUENCY_MODE => "LOW", - --DSS_MODE => "NONE", - --DUTY_CYCLE_CORRECTION => false, - STARTUP_WAIT => true - ) - port map ( - CLKIN => clk_50, - CLKFB => '0', - DSSEN => '0', - RST => rst_i, - PSINCDEC => '0', - PSEN => '0', - PSCLK => '0', - CLK0 => open, - CLK90 => open, - CLK180 => open, - CLK270 => open, - CLK2X => open, - CLK2X180 => open, - CLKDV => open, - CLKFX => mclk_orig, - CLKFX180 => open, - LOCKED => open, - STATUS => open, - PSDONE => open - ); - - e_mclkbuf: bufg - port map ( - I => mclk_orig, - O => mclk_int - ); - - e_sck_gen: srl16 - generic map (INIT => x"0003") - port map ( - D => sck_int, - CLK => mclk_int, - A0 => '1', - A1 => '1', - A2 => '0', - A3 => '0', - Q => sck_int - ); - - process (mclk_int, sck_int, sck_reg) - begin - if rising_edge(mclk_int) then - sck_reg <= sck_int; - end if; - end process; - rise_int <= '1' when sck_reg = '0' and sck_int = '1' else '0'; - fall_int <= '1' when sck_reg = '1' and sck_int = '0' else '0'; - - e_ws_count_0: srlc16e - generic map (INIT => x"0000") - port map ( - D => ws_tick, - CE => fall_int, - CLK => mclk_int, - A0 => '0', - A1 => '0', - A2 => '0', - A3 => '0', - Q => open, - Q15 => ws_carry - ); - - e_ws_count_1: srlc16e - generic map (INIT => x"0001") - port map ( - D => ws_carry, - CE => fall_int, - CLK => mclk_int, - A0 => '0', - A1 => '0', - A2 => '0', - A3 => '0', - Q => open, - Q15 => ws_tick - ); - - process (mclk_int, ws_tick, fall_int, ws_reg) - begin - if rising_edge(mclk_int) then - if ws_tick = '1' and fall_int = '1' then - ws_reg <= not ws_reg; - end if; - end if; - end process; - - mclk <= mclk_int; - sck <= sck_reg; - ws <= ws_reg; - - process (mclk_int, rise_int, fall_int) - begin - if rising_edge(mclk_int) then - rise_reg <= rise_int; - fall_reg <= fall_int; - end if; - end process; - - rise <= rise_reg; - fall <= fall_reg; - -end behavioral; diff --git a/libraries/dsp/i2s_input.vhd b/libraries/dsp/i2s_input.vhd deleted file mode 100644 index 43a0c4c..0000000 --- a/libraries/dsp/i2s_input.vhd +++ /dev/null @@ -1,93 +0,0 @@ --------------------------------------------------------------------------------- --- i2s_input - receives a stream of samples from an I2S interface --------------------------------------------------------------------------------- - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library unisim; -use unisim.vcomponents.all; - - -entity i2s_input is - generic (WIDTH: positive := 32); - port ( - mclk: in std_logic; - sck: in std_logic; - ws: in std_logic; - sd: in std_logic; - - rise: in std_logic; - fall: in std_logic; - - stb_o: out std_logic; - rdy_i: in std_logic; - l_dat_o: out std_logic_vector(WIDTH-1 downto 0); - r_dat_o: out std_logic_vector(WIDTH-1 downto 0) - ); -end i2s_input; - - -architecture behavioral of i2s_input is - - signal shift_reg: std_logic_vector(WIDTH-1 downto 0); - signal ws_reg: std_logic; - signal ws2_reg: std_logic; - - signal latch_l: std_logic; - signal latch_r: std_logic; - - signal samp_l_reg: std_logic_vector(WIDTH-1 downto 0); - signal samp_r_reg: std_logic_vector(WIDTH-1 downto 0); - - signal stb_reg: std_logic; - -begin - - process (mclk, rise, ws) - begin - if rising_edge(mclk) then - if rise = '1' then - shift_reg <= shift_reg(WIDTH-2 downto 0) & sd; - ws2_reg <= ws_reg; - ws_reg <= ws; - end if; - end if; - end process; - - latch_l <= '1' when ws_reg = '1' and ws2_reg = '0' else '0'; - latch_r <= '1' when ws_reg = '0' and ws2_reg = '1' else '0'; - - process (mclk, rise, latch_l, latch_r, shift_reg) - begin - if rising_edge(mclk) then - if rise = '1' then - if latch_l = '1' then - samp_l_reg <= shift_reg; - end if; - if latch_r = '1' then - samp_r_reg <= shift_reg; - end if; - end if; - end if; - end process; - - process (mclk, rise, latch_r, rdy_i) - begin - if rising_edge(mclk) then - if rise = '1' then - if latch_r = '1' then - stb_reg <= '1'; - elsif rdy_i = '1' then - stb_reg <= '0'; - end if; - end if; - end if; - end process; - - stb_o <= stb_reg; - l_dat_o <= samp_l_reg; - r_dat_o <= samp_r_reg; - -end behavioral; diff --git a/libraries/dsp/i2s_output.vhd b/libraries/dsp/i2s_output.vhd deleted file mode 100644 index 11fda1b..0000000 --- a/libraries/dsp/i2s_output.vhd +++ /dev/null @@ -1,75 +0,0 @@ --------------------------------------------------------------------------------- --- i2s_output - transmits a stream of samples to an I2S interface --------------------------------------------------------------------------------- - -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library unisim; -use unisim.vcomponents.all; - - -entity i2s_output is - generic (WIDTH: positive := 32); - port ( - mclk: in std_logic; - sck: in std_logic; - ws: in std_logic; - sd: out std_logic; - - rise: in std_logic; - fall: in std_logic; - - stb_i: in std_logic; - rdy_o: out std_logic; - l_dat_i: in std_logic_vector(WIDTH-1 downto 0); - r_dat_i: in std_logic_vector(WIDTH-1 downto 0) - ); -end i2s_output; - - -architecture behavioral of i2s_output is - - signal ws_reg: std_logic; - signal ws2_reg: std_logic; - signal shift_reg: std_logic_vector(WIDTH-1 downto 0); - - signal latch_l: std_logic; - signal latch_r: std_logic; - -begin - - sd <= shift_reg(WIDTH-1); - - process (mclk, fall, ws, ws_reg) - begin - if rising_edge(mclk) then - if fall = '1' then - ws2_reg <= ws_reg; - ws_reg <= ws; - end if; - end if; - end process; - - latch_l <= '1' when ws_reg = '0' and ws2_reg = '1' else '0'; - latch_r <= '1' when ws_reg = '1' and ws2_reg = '0' else '0'; - - process (mclk, fall, latch_l, latch_r, l_dat_i, r_dat_i, shift_reg) - begin - if rising_edge(mclk) then - if fall = '1' then - if latch_l = '1' then - shift_reg <= l_dat_i; - elsif latch_r = '1' then - shift_reg <= r_dat_i; - else - shift_reg <= shift_reg(WIDTH-2 downto 0) & '0'; - end if; - end if; - end if; - end process; - - rdy_o <= latch_r and fall; - -end behavioral; diff --git a/libraries/dsp/i2s_pmod.vhd b/libraries/dsp/i2s_pmod.vhd deleted file mode 100644 index 984cfaf..0000000 --- a/libraries/dsp/i2s_pmod.vhd +++ /dev/null @@ -1,174 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; - -library utility; -library work; - - -entity i2s_pmod is - port ( - rst_50: in std_logic; - clk_50: in std_logic; - - stb_i: in std_logic; - rdy_o: out std_logic; - l_dat_i: in std_logic_vector(23 downto 0); - r_dat_i: in std_logic_vector(23 downto 0); - - stb_o: out std_logic; - rdy_i: in std_logic; - l_dat_o: out std_logic_vector(23 downto 0); - r_dat_o: out std_logic_vector(23 downto 0); - - pmod: inout std_logic_vector(7 downto 0) - ); -end i2s_pmod; - - -architecture behavioral of i2s_pmod is - - signal rst_i2s: std_logic; - signal mclk: std_logic; - signal sck: std_logic; - signal ws: std_logic; - signal txsd: std_logic; - signal rxsd: std_logic; - - signal rise: std_logic; - signal fall: std_logic; - - signal rx_stb: std_logic; - signal rx_rdy: std_logic; - signal rx_l_dat: std_logic_vector(31 downto 0); - signal rx_r_dat: std_logic_vector(31 downto 0); - - signal tx_stb: std_logic; - signal tx_rdy: std_logic; - signal tx_l_dat: std_logic_vector(31 downto 0); - signal tx_r_dat: std_logic_vector(31 downto 0); - - signal rx_head_dat: std_logic_vector(47 downto 0); - signal rx_tail_dat: std_logic_vector(47 downto 0); - - signal tx_head_dat: std_logic_vector(47 downto 0); - signal tx_tail_dat: std_logic_vector(47 downto 0); - -begin - - -- The ADC and DAC decide choose their modes based on the values of their - -- pins at powerup, so the rst_i2s signal is here to gate the pins and - -- leave them in an high-Z state after powerup for a couple MCLK cycles - -- to let them sense their settings. This is probably unnecessary, as the - -- FPGA will leave all its IO blocks disabled until configuration is - -- complete, but it doesn't hurt to be sure. - e_rst: entity utility.sync_sig - generic map (INIT => '1') - port map ( - rst_i => rst_50, - clk_i => mclk, - sig_i => '0', - sig_o => rst_i2s - ); - - e_ctrl: entity work.i2s_ctrl - port map ( - rst_i => rst_50, - clk_50 => clk_50, - - mclk => mclk, - sck => sck, - ws => ws, - - rise => rise, - fall => fall - ); - - e_rx: entity work.i2s_input - generic map (WIDTH => 32) - port map ( - mclk => mclk, - sck => sck, - ws => ws, - sd => rxsd, - - rise => rise, - fall => fall, - - stb_o => rx_stb, - rdy_i => rx_rdy, - l_dat_o => rx_l_dat, - r_dat_o => rx_r_dat - ); - - e_rx_fifo: entity utility.sync_fifo_16 - generic map (WIDTH => 48) - port map ( - head_rst_i => rst_i2s, - head_clk_i => mclk, - head_stb_i => rx_stb, - head_rdy_o => rx_rdy, - head_dat_i => rx_head_dat, - - tail_rst_i => rst_50, - tail_clk_i => clk_50, - tail_stb_o => stb_o, - tail_rdy_i => rdy_i, - tail_dat_o => rx_tail_dat - ); - rx_head_dat <= rx_r_dat(31 downto 8) & rx_l_dat(31 downto 8); - r_dat_o <= rx_tail_dat(47 downto 24); - l_dat_o <= rx_tail_dat(23 downto 0); - - e_tx: entity work.i2s_output - generic map (WIDTH => 32) - port map ( - mclk => mclk, - sck => sck, - ws => ws, - sd => txsd, - - rise => rise, - fall => fall, - - stb_i => tx_stb, - rdy_o => tx_rdy, - l_dat_i => tx_l_dat, - r_dat_i => tx_r_dat - ); - - e_tx_fifo: entity utility.sync_fifo_16 - generic map (WIDTH => 48) - port map ( - head_rst_i => rst_50, - head_clk_i => clk_50, - head_stb_i => stb_i, - head_rdy_o => rdy_o, - head_dat_i => tx_head_dat, - - tail_rst_i => rst_i2s, - tail_clk_i => mclk, - tail_stb_o => tx_stb, - tail_rdy_i => tx_rdy, - tail_dat_o => tx_tail_dat - ); - tx_head_dat <= r_dat_i & l_dat_i; - tx_r_dat <= tx_tail_dat(47 downto 24) & x"00"; - tx_l_dat <= tx_tail_dat(23 downto 0) & x"00"; - - -- pmod(3 downto 0) <= JA4 & JA3 & JA2 & JA1 -- TOP - -- pmod(7 downto 4) <= JA10 & JA9 & JA8 & JA7 -- BOTTOM - - -- Top row - pmod(0) <= 'Z' when rst_i2s = '1' else mclk; - pmod(1) <= 'Z' when rst_i2s = '1' else ws; - pmod(2) <= 'Z' when rst_i2s = '1' else sck; - pmod(3) <= 'Z' when rst_i2s = '1' else txsd; - - -- Bottom row - pmod(4) <= 'Z' when rst_i2s = '1' else mclk; - pmod(5) <= 'Z' when rst_i2s = '1' else ws; - pmod(6) <= 'Z' when rst_i2s = '1' else sck; - pmod(7) <= 'Z'; - rxsd <= pmod(7); - -end behavioral; diff --git a/libraries/dsp/pcm16_2ch_gain.vhd b/libraries/dsp/pcm16_2ch_gain.vhd deleted file mode 100644 index 9938214..0000000 --- a/libraries/dsp/pcm16_2ch_gain.vhd +++ /dev/null @@ -1,76 +0,0 @@ -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 <= dat_i(15) & dat_i(15) & dat_i(15 downto 0); - samp_r <= dat_i(31) & dat_i(31) & 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)); - - 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); - - result <= result_r & result_l; - - 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 deleted file mode 100644 index 2f4c89d..0000000 --- a/libraries/dsp/pcm16_2ch_sum.vhd +++ /dev/null @@ -1,96 +0,0 @@ -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_o: 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 <= signed(a_dat_i(15 downto 0)); - samp_a_r <= signed(a_dat_i(31 downto 16)); - samp_b_l <= signed(b_dat_i(15 downto 0)); - samp_b_r <= signed(b_dat_i(31 downto 16)); - - result_l <= (samp_a_l(15) & samp_a_l) + ((samp_b_l(15)) & samp_b_l); - result_r <= (samp_a_r(15) & samp_a_r) + ((samp_b_r(15)) & samp_b_r); - - e_sat_l: entity work.saturate - generic map (WIDTH_IN => 17, WIDTH_OUT => 16) - port map (dat_i => std_logic_vector(result_l), dat_o => sat_l); - - e_sat_r: entity work.saturate - generic map (WIDTH_IN => 17, WIDTH_OUT => 16) - port map (dat_i => std_logic_vector(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/pcm16_2ch_tapeeffect.vhd b/libraries/dsp/pcm16_2ch_tapeeffect.vhd deleted file mode 100644 index 1769458..0000000 --- a/libraries/dsp/pcm16_2ch_tapeeffect.vhd +++ /dev/null @@ -1,228 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library work; - - -entity pcm16_2ch_tapeeffect is - port ( - rst_i: in std_logic; - clk_i: in std_logic; - - en_rolloff: in std_logic; - en_flutter: in std_logic; - en_wow: in std_logic; - en_noise: in std_logic; - - 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_tapeeffect; - - -architecture behavioral of pcm16_2ch_tapeeffect is - - -- Stage 1 - High frequency rolloff - - signal split_a_stb: std_logic; - signal split_a_rdy: std_logic; - signal split_b_stb: std_logic; - signal split_b_rdy: std_logic; - signal split_dat: std_logic_vector(31 downto 0); - - signal filter_stb: std_logic; - signal filter_rdy: std_logic; - signal filter_dat: std_logic_vector(31 downto 0); - - signal bypass_stb: std_logic; - signal bypass_rdy: std_logic; - signal bypass_dat: std_logic_vector(31 downto 0); - - signal merge_stb: std_logic; - signal merge_rdy: std_logic; - signal merge_dat: std_logic_vector(31 downto 0); - - signal stage1_stb: std_logic; - signal stage1_rdy: std_logic; - signal stage1_dat: std_logic_vector(31 downto 0); - - -- Stage 2 - Wow and flutter - - constant FLUTTER_WIDTH: integer := 9; - constant FLUTTER_INC: std_logic_vector(15 downto 0) := "00000000"&"00100000"; - signal flutter_stb: std_logic; - signal flutter_rdy: std_logic; - signal flutter_dat: std_logic_vector(FLUTTER_WIDTH-1 downto 0); - - signal delay_dat: std_logic_vector(9 downto 0); - - signal stage2_stb: std_logic; - signal stage2_rdy: std_logic; - signal stage2_dat: std_logic_vector(31 downto 0); - - -- Stage 3 - Hiss - - signal noise_stb: std_logic; - signal noise_rdy: std_logic; - signal noise_raw: std_logic_vector( 9 downto 0); - signal noise_dat: std_logic_vector(31 downto 0); - -begin - - ---------------------------------------------------------------------------- - -- Low pass filter the audio stream - - e_split: entity work.pipectrl_split - generic map (WIDTH => 32) - port map ( - rst_i => rst_i, - clk_i => clk_i, - - stb_i => stb_i, - rdy_o => rdy_o, - dat_i => dat_i, - - a_stb_o => split_a_stb, - a_rdy_i => split_a_rdy, - - b_stb_o => split_b_stb, - b_rdy_i => split_b_rdy, - - dat_o => split_dat - ); - - e_filter: entity work.pcm16_2ch_windowsum - generic map (WINDOW => 16) - port map ( - rst_i => rst_i, - clk_i => clk_i, - - stb_i => split_a_stb, - rdy_o => split_a_rdy, - dat_i => split_dat, - - stb_o => filter_stb, - rdy_i => filter_rdy, - dat_o => filter_dat - ); - - e_bypass: entity work.pipectrl - generic map (WIDTH => 32) - port map ( - rst_i => rst_i, - clk_i => clk_i, - - stb_i => split_b_stb, - rdy_o => split_b_rdy, - dat_i => split_dat, - - stb_o => bypass_stb, - rdy_i => bypass_rdy, - dat_o => bypass_dat - ); - - e_filter_merge: entity work.merge - port map ( - a_stb_i => filter_stb, - a_rdy_o => filter_rdy, - - b_stb_i => bypass_stb, - b_rdy_o => bypass_rdy, - - stb_o => merge_stb, - rdy_i => merge_rdy - ); - merge_dat <= filter_dat when en_rolloff = '1' else bypass_dat; - - e_filter_ctrl: entity work.pipectrl - generic map (WIDTH => 32) - port map ( - rst_i => rst_i, - clk_i => clk_i, - - stb_i => merge_stb, - rdy_o => merge_rdy, - dat_i => merge_dat, - - stb_o => stage1_stb, - rdy_i => stage1_rdy, - dat_o => stage1_dat - ); - - ---------------------------------------------------------------------------- - -- Variable delay for wow and flutter - - e_flutter: entity work.src_fracstep - generic map (WIDTH_OUT => FLUTTER_WIDTH) - port map ( - rst_i => rst_i, - clk_i => clk_i, - - inc_i => std_logic_vector(unsigned(FLUTTER_INC)), - - stb_o => flutter_stb, - rdy_i => flutter_rdy, - dat_o => flutter_dat - ); - - delay_dat <= std_logic_vector(resize(unsigned(flutter_dat), 10)) when en_flutter = '1' else (others => '0'); - - e_vardelay: entity work.pcm16_2ch_vardelay - port map ( - rst_i => rst_i, - clk_i => clk_i, - - delay_stb_i => flutter_stb, - delay_rdy_o => flutter_rdy, - delay_dat_i => delay_dat, - - audio_stb_i => stage1_stb, - audio_rdy_o => stage1_rdy, - audio_dat_i => stage1_dat, - - stb_o => stage2_stb, - rdy_i => stage2_rdy, - dat_o => stage2_dat - ); - - ---------------------------------------------------------------------------- - -- Add noise stream to audio stream - - e_noise: entity work.src_noise - generic map (WIDTH => 10) - port map ( - clk_i => clk_i, - - stb_o => noise_stb, - rdy_i => noise_rdy, - dat_o => noise_raw - ); - - noise_dat <= std_logic_vector(resize(signed(noise_raw), 16)) & - std_logic_vector(resize(signed(noise_raw), 16)) when en_noise = '1' else (others => '0'); - - e_sum: entity work.pcm16_2ch_sum - port map ( - rst_i => rst_i, - clk_i => clk_i, - - a_stb_i => stage2_stb, - a_rdy_o => stage2_rdy, - a_dat_i => stage2_dat, - - b_stb_i => noise_stb, - b_rdy_o => noise_rdy, - b_dat_i => noise_dat, - - stb_o => stb_o, - rdy_i => rdy_i, - dat_o => dat_o - ); - -end behavioral; diff --git a/libraries/dsp/pcm16_2ch_vardelay.vhd b/libraries/dsp/pcm16_2ch_vardelay.vhd deleted file mode 100644 index d79d347..0000000 --- a/libraries/dsp/pcm16_2ch_vardelay.vhd +++ /dev/null @@ -1,146 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; -use ieee.numeric_std.all; - -library unisim; -use unisim.vcomponents.all; - -library work; - - -entity pcm16_2ch_vardelay is - port ( - rst_i: in std_logic; - clk_i: in std_logic; - - delay_stb_i: in std_logic; - delay_rdy_o: out std_logic; - delay_dat_i: in std_logic_vector(9 downto 0); - - audio_stb_i: in std_logic; - audio_rdy_o: out std_logic; - audio_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_vardelay; - - -architecture behavioral of pcm16_2ch_vardelay is - - constant COUNT_WIDTH: integer := 10; - - signal en: std_logic; - signal stb: std_logic; - signal rdy: std_logic; - - signal samp_l: std_logic_vector(15 downto 0); - signal samp_r: std_logic_vector(15 downto 0); - - signal result_l: std_logic_vector(15 downto 0); - signal result_r: std_logic_vector(15 downto 0); - - signal ptr_head_reg: unsigned(COUNT_WIDTH-1 downto 0) := (others => '0'); - signal ptr_tail: unsigned(COUNT_WIDTH-1 downto 0); - -begin - - e_merge: entity work.merge - port map ( - a_stb_i => delay_stb_i, - a_rdy_o => delay_rdy_o, - b_stb_i => audio_stb_i, - b_rdy_o => audio_rdy_o, - stb_o => stb, - rdy_i => rdy - ); - - e_ctrl: entity work.pipectrl - generic map (WIDTH => 0) - port map ( - rst_i => rst_i, - clk_i => clk_i, - en_o => en, - stb_i => stb, - rdy_o => rdy, - dat_i => open, - stb_o => stb_o, - rdy_i => rdy_i, - dat_o => open - ); - - process (rst_i, clk_i, en, ptr_head_reg) - begin - if rising_edge(clk_i) then - if rst_i = '1' then - -- Probably don't need reset logic - elsif en = '1' then - ptr_head_reg <= ptr_head_reg + 1; - end if; - end if; - end process; - - samp_l <= audio_dat_i(15 downto 0); - samp_r <= audio_dat_i(31 downto 16); - - ptr_tail <= (ptr_head_reg - unsigned(delay_dat_i)) - 1; - - e_fifo_l: ramb16_s18_s18 - port map ( - -- Head port (insertion) - WEA => '1', - ENA => en, - SSRA => '0', - CLKA => clk_i, - ADDRA => std_logic_vector(ptr_head_reg), - DIA => samp_l, - DIPA => (others => '0'), - - DOA => open, - DOPA => open, - - -- Tail port (removal) - WEB => '0', - ENB => en, - SSRB => '0', - CLKB => clk_i, - ADDRB => std_logic_vector(ptr_tail), - DIB => (others => '0'), - DIPB => (others => '0'), - - DOB => result_l, - DOPB => open - ); - - e_fifo_r: ramb16_s18_s18 - port map ( - -- Head port (insertion) - WEA => '1', - ENA => en, - SSRA => '0', - CLKA => clk_i, - ADDRA => std_logic_vector(ptr_head_reg), - DIA => samp_r, - DIPA => (others => '0'), - - DOA => open, - DOPA => open, - - -- Tail port (removal) - WEB => '0', - ENB => en, - SSRB => '0', - CLKB => clk_i, - ADDRB => std_logic_vector(ptr_tail), - DIB => (others => '0'), - DIPB => (others => '0'), - - DOB => result_r, - DOPB => open - ); - - dat_o <= result_r & result_l; - -end behavioral; diff --git a/libraries/dsp/pcm16_2ch_windowsum.vhd b/libraries/dsp/pcm16_2ch_windowsum.vhd deleted file mode 100644 index 83bff1c..0000000 --- a/libraries/dsp/pcm16_2ch_windowsum.vhd +++ /dev/null @@ -1,96 +0,0 @@ -library ieee; -use ieee.std_logic_1164.all; -use ieee.math_real.all; - -library work; - - -entity pcm16_2ch_windowsum is - generic ( - WINDOW: 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(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_windowsum; - - -architecture behavioral of pcm16_2ch_windowsum is - - constant FILT_WIDTH: positive := 16+integer(ceil(log2(real(WINDOW)))); - - signal en: std_logic; - - signal samp_l: std_logic_vector(15 downto 0); - signal samp_r: std_logic_vector(15 downto 0); - - signal filt_l: std_logic_vector(FILT_WIDTH-1 downto 0); - signal filt_r: std_logic_vector(FILT_WIDTH-1 downto 0); - - signal result_l: std_logic_vector(15 downto 0); - signal result_r: std_logic_vector(15 downto 0); - -begin - - e_ctrl: entity work.pipectrl - generic map (WIDTH => 0) - port map ( - rst_i => rst_i, - clk_i => clk_i, - en_o => en, - stb_i => stb_i, - rdy_o => rdy_o, - dat_i => open, - stb_o => stb_o, - rdy_i => rdy_i, - dat_o => open - ); - - samp_l <= dat_i(15 downto 0); - samp_r <= dat_i(31 downto 16); - - e_filter_l: entity work.filter_windowsum - generic map ( - WIDTH => 16, - WINDOW => WINDOW - ) - port map ( - rst_i => rst_i, - clk_i => clk_i, - - en_i => en, - - dat_i => samp_l, - dat_o => filt_l - ); - - e_filter_r: entity work.filter_windowsum - generic map ( - WIDTH => 16, - WINDOW => WINDOW - ) - port map ( - rst_i => rst_i, - clk_i => clk_i, - - en_i => en, - - dat_i => samp_r, - dat_o => filt_r - ); - - result_l <= filt_l(FILT_WIDTH-1 downto FILT_WIDTH-16); - result_r <= filt_r(FILT_WIDTH-1 downto FILT_WIDTH-16); - - dat_o <= result_r & result_l; - -end behavioral; diff --git a/libraries/i2s/i2s_ctrl.vhd b/libraries/i2s/i2s_ctrl.vhd new file mode 100644 index 0000000..13cf406 --- /dev/null +++ b/libraries/i2s/i2s_ctrl.vhd @@ -0,0 +1,163 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + + +entity i2s_ctrl is + port ( + rst_i: in std_logic; + clk_50: in std_logic; -- 50 MHz + + -- For external use + mclk: out std_logic; -- MCLK, 256*LRCLK(WS) + ws: out std_logic; -- WS/LRCLK, 44.1028 kHz (good enough for government work) + sck: out std_logic; + + -- For internal use, rising and falling edges of SCK in MCLK domain + rise: out std_logic; + fall: out std_logic + ); +end i2s_ctrl; + + +architecture behavioral of i2s_ctrl is + + signal mclk_orig: std_logic; -- Output from DCM + signal mclk_int: std_logic; -- DCM output after clock buffer + signal sck_int: std_logic; + signal sck_reg: std_logic; + + signal ws_tick: std_logic; -- For WS generation + signal ws_carry: std_logic; + signal ws_reg: std_logic := '0'; + + signal rise_int: std_logic; + signal fall_int: std_logic; + signal rise_reg: std_logic; + signal fall_reg: std_logic; + +begin + + e_dcm: dcm_sp + generic map ( + CLK_FEEDBACK => "NONE", + --CLKDV_DIVIDE => 1, + CLKFX_DIVIDE => 31, + CLKFX_MULTIPLY => 7, + CLKIN_DIVIDE_BY_2 => false, + CLKIN_PERIOD => 20.0, + --CLKOUT_PHASE_SHIFT => "NONE", + --DESKEW_ADJUST => "SYSTEM_SYNCHRONOUS", + --FACTORY_JF => + --PHASE_SHIFT => 0, + DFS_FREQUENCY_MODE => "LOW", + --DLL_FREQUENCY_MODE => "LOW", + --DSS_MODE => "NONE", + --DUTY_CYCLE_CORRECTION => false, + STARTUP_WAIT => true + ) + port map ( + CLKIN => clk_50, + CLKFB => '0', + DSSEN => '0', + RST => rst_i, + PSINCDEC => '0', + PSEN => '0', + PSCLK => '0', + CLK0 => open, + CLK90 => open, + CLK180 => open, + CLK270 => open, + CLK2X => open, + CLK2X180 => open, + CLKDV => open, + CLKFX => mclk_orig, + CLKFX180 => open, + LOCKED => open, + STATUS => open, + PSDONE => open + ); + + e_mclkbuf: bufg + port map ( + I => mclk_orig, + O => mclk_int + ); + + e_sck_gen: srl16 + generic map (INIT => x"0003") + port map ( + D => sck_int, + CLK => mclk_int, + A0 => '1', + A1 => '1', + A2 => '0', + A3 => '0', + Q => sck_int + ); + + process (mclk_int, sck_int, sck_reg) + begin + if rising_edge(mclk_int) then + sck_reg <= sck_int; + end if; + end process; + rise_int <= '1' when sck_reg = '0' and sck_int = '1' else '0'; + fall_int <= '1' when sck_reg = '1' and sck_int = '0' else '0'; + + e_ws_count_0: srlc16e + generic map (INIT => x"0000") + port map ( + D => ws_tick, + CE => fall_int, + CLK => mclk_int, + A0 => '0', + A1 => '0', + A2 => '0', + A3 => '0', + Q => open, + Q15 => ws_carry + ); + + e_ws_count_1: srlc16e + generic map (INIT => x"0001") + port map ( + D => ws_carry, + CE => fall_int, + CLK => mclk_int, + A0 => '0', + A1 => '0', + A2 => '0', + A3 => '0', + Q => open, + Q15 => ws_tick + ); + + process (mclk_int, ws_tick, fall_int, ws_reg) + begin + if rising_edge(mclk_int) then + if ws_tick = '1' and fall_int = '1' then + ws_reg <= not ws_reg; + end if; + end if; + end process; + + mclk <= mclk_int; + sck <= sck_reg; + ws <= ws_reg; + + process (mclk_int, rise_int, fall_int) + begin + if rising_edge(mclk_int) then + rise_reg <= rise_int; + fall_reg <= fall_int; + end if; + end process; + + rise <= rise_reg; + fall <= fall_reg; + +end behavioral; diff --git a/libraries/i2s/i2s_input.vhd b/libraries/i2s/i2s_input.vhd new file mode 100644 index 0000000..43a0c4c --- /dev/null +++ b/libraries/i2s/i2s_input.vhd @@ -0,0 +1,93 @@ +-------------------------------------------------------------------------------- +-- i2s_input - receives a stream of samples from an I2S interface +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + + +entity i2s_input is + generic (WIDTH: positive := 32); + port ( + mclk: in std_logic; + sck: in std_logic; + ws: in std_logic; + sd: in std_logic; + + rise: in std_logic; + fall: in std_logic; + + stb_o: out std_logic; + rdy_i: in std_logic; + l_dat_o: out std_logic_vector(WIDTH-1 downto 0); + r_dat_o: out std_logic_vector(WIDTH-1 downto 0) + ); +end i2s_input; + + +architecture behavioral of i2s_input is + + signal shift_reg: std_logic_vector(WIDTH-1 downto 0); + signal ws_reg: std_logic; + signal ws2_reg: std_logic; + + signal latch_l: std_logic; + signal latch_r: std_logic; + + signal samp_l_reg: std_logic_vector(WIDTH-1 downto 0); + signal samp_r_reg: std_logic_vector(WIDTH-1 downto 0); + + signal stb_reg: std_logic; + +begin + + process (mclk, rise, ws) + begin + if rising_edge(mclk) then + if rise = '1' then + shift_reg <= shift_reg(WIDTH-2 downto 0) & sd; + ws2_reg <= ws_reg; + ws_reg <= ws; + end if; + end if; + end process; + + latch_l <= '1' when ws_reg = '1' and ws2_reg = '0' else '0'; + latch_r <= '1' when ws_reg = '0' and ws2_reg = '1' else '0'; + + process (mclk, rise, latch_l, latch_r, shift_reg) + begin + if rising_edge(mclk) then + if rise = '1' then + if latch_l = '1' then + samp_l_reg <= shift_reg; + end if; + if latch_r = '1' then + samp_r_reg <= shift_reg; + end if; + end if; + end if; + end process; + + process (mclk, rise, latch_r, rdy_i) + begin + if rising_edge(mclk) then + if rise = '1' then + if latch_r = '1' then + stb_reg <= '1'; + elsif rdy_i = '1' then + stb_reg <= '0'; + end if; + end if; + end if; + end process; + + stb_o <= stb_reg; + l_dat_o <= samp_l_reg; + r_dat_o <= samp_r_reg; + +end behavioral; diff --git a/libraries/i2s/i2s_output.vhd b/libraries/i2s/i2s_output.vhd new file mode 100644 index 0000000..11fda1b --- /dev/null +++ b/libraries/i2s/i2s_output.vhd @@ -0,0 +1,75 @@ +-------------------------------------------------------------------------------- +-- i2s_output - transmits a stream of samples to an I2S interface +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + + +entity i2s_output is + generic (WIDTH: positive := 32); + port ( + mclk: in std_logic; + sck: in std_logic; + ws: in std_logic; + sd: out std_logic; + + rise: in std_logic; + fall: in std_logic; + + stb_i: in std_logic; + rdy_o: out std_logic; + l_dat_i: in std_logic_vector(WIDTH-1 downto 0); + r_dat_i: in std_logic_vector(WIDTH-1 downto 0) + ); +end i2s_output; + + +architecture behavioral of i2s_output is + + signal ws_reg: std_logic; + signal ws2_reg: std_logic; + signal shift_reg: std_logic_vector(WIDTH-1 downto 0); + + signal latch_l: std_logic; + signal latch_r: std_logic; + +begin + + sd <= shift_reg(WIDTH-1); + + process (mclk, fall, ws, ws_reg) + begin + if rising_edge(mclk) then + if fall = '1' then + ws2_reg <= ws_reg; + ws_reg <= ws; + end if; + end if; + end process; + + latch_l <= '1' when ws_reg = '0' and ws2_reg = '1' else '0'; + latch_r <= '1' when ws_reg = '1' and ws2_reg = '0' else '0'; + + process (mclk, fall, latch_l, latch_r, l_dat_i, r_dat_i, shift_reg) + begin + if rising_edge(mclk) then + if fall = '1' then + if latch_l = '1' then + shift_reg <= l_dat_i; + elsif latch_r = '1' then + shift_reg <= r_dat_i; + else + shift_reg <= shift_reg(WIDTH-2 downto 0) & '0'; + end if; + end if; + end if; + end process; + + rdy_o <= latch_r and fall; + +end behavioral; diff --git a/libraries/i2s/i2s_pmod.vhd b/libraries/i2s/i2s_pmod.vhd new file mode 100644 index 0000000..984cfaf --- /dev/null +++ b/libraries/i2s/i2s_pmod.vhd @@ -0,0 +1,174 @@ +library ieee; +use ieee.std_logic_1164.all; + +library utility; +library work; + + +entity i2s_pmod is + port ( + rst_50: in std_logic; + clk_50: in std_logic; + + stb_i: in std_logic; + rdy_o: out std_logic; + l_dat_i: in std_logic_vector(23 downto 0); + r_dat_i: in std_logic_vector(23 downto 0); + + stb_o: out std_logic; + rdy_i: in std_logic; + l_dat_o: out std_logic_vector(23 downto 0); + r_dat_o: out std_logic_vector(23 downto 0); + + pmod: inout std_logic_vector(7 downto 0) + ); +end i2s_pmod; + + +architecture behavioral of i2s_pmod is + + signal rst_i2s: std_logic; + signal mclk: std_logic; + signal sck: std_logic; + signal ws: std_logic; + signal txsd: std_logic; + signal rxsd: std_logic; + + signal rise: std_logic; + signal fall: std_logic; + + signal rx_stb: std_logic; + signal rx_rdy: std_logic; + signal rx_l_dat: std_logic_vector(31 downto 0); + signal rx_r_dat: std_logic_vector(31 downto 0); + + signal tx_stb: std_logic; + signal tx_rdy: std_logic; + signal tx_l_dat: std_logic_vector(31 downto 0); + signal tx_r_dat: std_logic_vector(31 downto 0); + + signal rx_head_dat: std_logic_vector(47 downto 0); + signal rx_tail_dat: std_logic_vector(47 downto 0); + + signal tx_head_dat: std_logic_vector(47 downto 0); + signal tx_tail_dat: std_logic_vector(47 downto 0); + +begin + + -- The ADC and DAC decide choose their modes based on the values of their + -- pins at powerup, so the rst_i2s signal is here to gate the pins and + -- leave them in an high-Z state after powerup for a couple MCLK cycles + -- to let them sense their settings. This is probably unnecessary, as the + -- FPGA will leave all its IO blocks disabled until configuration is + -- complete, but it doesn't hurt to be sure. + e_rst: entity utility.sync_sig + generic map (INIT => '1') + port map ( + rst_i => rst_50, + clk_i => mclk, + sig_i => '0', + sig_o => rst_i2s + ); + + e_ctrl: entity work.i2s_ctrl + port map ( + rst_i => rst_50, + clk_50 => clk_50, + + mclk => mclk, + sck => sck, + ws => ws, + + rise => rise, + fall => fall + ); + + e_rx: entity work.i2s_input + generic map (WIDTH => 32) + port map ( + mclk => mclk, + sck => sck, + ws => ws, + sd => rxsd, + + rise => rise, + fall => fall, + + stb_o => rx_stb, + rdy_i => rx_rdy, + l_dat_o => rx_l_dat, + r_dat_o => rx_r_dat + ); + + e_rx_fifo: entity utility.sync_fifo_16 + generic map (WIDTH => 48) + port map ( + head_rst_i => rst_i2s, + head_clk_i => mclk, + head_stb_i => rx_stb, + head_rdy_o => rx_rdy, + head_dat_i => rx_head_dat, + + tail_rst_i => rst_50, + tail_clk_i => clk_50, + tail_stb_o => stb_o, + tail_rdy_i => rdy_i, + tail_dat_o => rx_tail_dat + ); + rx_head_dat <= rx_r_dat(31 downto 8) & rx_l_dat(31 downto 8); + r_dat_o <= rx_tail_dat(47 downto 24); + l_dat_o <= rx_tail_dat(23 downto 0); + + e_tx: entity work.i2s_output + generic map (WIDTH => 32) + port map ( + mclk => mclk, + sck => sck, + ws => ws, + sd => txsd, + + rise => rise, + fall => fall, + + stb_i => tx_stb, + rdy_o => tx_rdy, + l_dat_i => tx_l_dat, + r_dat_i => tx_r_dat + ); + + e_tx_fifo: entity utility.sync_fifo_16 + generic map (WIDTH => 48) + port map ( + head_rst_i => rst_50, + head_clk_i => clk_50, + head_stb_i => stb_i, + head_rdy_o => rdy_o, + head_dat_i => tx_head_dat, + + tail_rst_i => rst_i2s, + tail_clk_i => mclk, + tail_stb_o => tx_stb, + tail_rdy_i => tx_rdy, + tail_dat_o => tx_tail_dat + ); + tx_head_dat <= r_dat_i & l_dat_i; + tx_r_dat <= tx_tail_dat(47 downto 24) & x"00"; + tx_l_dat <= tx_tail_dat(23 downto 0) & x"00"; + + -- pmod(3 downto 0) <= JA4 & JA3 & JA2 & JA1 -- TOP + -- pmod(7 downto 4) <= JA10 & JA9 & JA8 & JA7 -- BOTTOM + + -- Top row + pmod(0) <= 'Z' when rst_i2s = '1' else mclk; + pmod(1) <= 'Z' when rst_i2s = '1' else ws; + pmod(2) <= 'Z' when rst_i2s = '1' else sck; + pmod(3) <= 'Z' when rst_i2s = '1' else txsd; + + -- Bottom row + pmod(4) <= 'Z' when rst_i2s = '1' else mclk; + pmod(5) <= 'Z' when rst_i2s = '1' else ws; + pmod(6) <= 'Z' when rst_i2s = '1' else sck; + pmod(7) <= 'Z'; + rxsd <= pmod(7); + +end behavioral; diff --git a/libraries/nexys2/mem_wb16_0_opt.vhd b/libraries/nexys2/mem_wb16_0_opt.vhd new file mode 100644 index 0000000..d953f59 --- /dev/null +++ b/libraries/nexys2/mem_wb16_0_opt.vhd @@ -0,0 +1,178 @@ +-------------------------------------------------------------------------------- +-- mem_wb16_0 - Simple, non-caching 16-bit interface to Nexys2 onboard memory +-------------------------------------------------------------------------------- +-- WISHBONE DATASHEET +-- +-- Wishbone specification used: Rev B.3 +-- Interface type: device +-- Port size: 8-bit +-- Operand sizes: 8-bit +-- Endianness: undefined (port size same as granularity) +-- Data transfer sequence: undefined +-- Clock constraints: max 50 MHz +-- Signals: +-- * rst_i +-- * clk_i +-- * fls_cyc_i (CYC_I for flash) +-- * ram_cyc_i (CYC_I for RAM) +-- * stb_i +-- * we_i +-- * ack_o +-- * adr_i (24-bit) +-- * dat_i (8-bit) +-- * dat_o (8-bit) +-------------------------------------------------------------------------------- + +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 mem_wb16_0_opt is + generic ( + CYCLES_ACTIVE: std_logic_vector(3 downto 0) := "0110"; + CYCLES_TOTAL: std_logic_vector(3 downto 0) := "1000" + ); + port ( + -- Wishbone SYSCON + rst_i: in std_logic; + clk_i: in std_logic; + + -- Wishbone system interface + fls_cyc_i: in std_logic; + ram_cyc_i: in std_logic; + stb_i: in std_logic; + we_i: in std_logic; + ack_o: out std_logic; + adr_i: in std_logic_vector(23 downto 0); + dat_i: in std_logic_vector(15 downto 0); + dat_o: out std_logic_vector(15 downto 0); + + -- Memory interface + MemOE: out std_logic; + MemWR: out std_logic; + RamAdv: out std_logic; + RamCS: out std_logic; + RamClk: out std_logic; + RamCRE: out std_logic; + RamUB: out std_logic; + RamLB: out std_logic; + RamWait: in std_logic; + FlashRp: out std_logic; + FlashCS: out std_logic; + FlashStSts: in std_logic; + MemAdr: out std_logic_vector(23 downto 1); + MemDB_i: in std_logic_vector(15 downto 0); -- Inbound: from memory to device + MemDB_o: out std_logic_vector(15 downto 0) -- Outbound: from device to memory + ); +end mem_wb16_0_opt; + + +architecture behavioral of mem_wb16_0_opt is + + signal state_idle: std_logic; + signal state_total: std_logic; + + signal state_idle_next: std_logic; + signal state_total_next: std_logic; + + signal count_cycles: std_logic_vector(3 downto 0); + signal count_start: std_logic; + signal count_done: std_logic; + + signal mem_enable: std_logic; + + -- Replacement for original cyc_i when separating cyc for ram and flash + signal cyc_i: std_logic; + +begin + + cyc_i <= fls_cyc_i or ram_cyc_i; + + process (rst_i, clk_i, state_idle_next, state_total_next) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + state_idle <= '1'; + state_total <= '0'; + else + state_idle <= state_idle_next; + state_total <= state_total_next; + end if; + end if; + end process; + + process (state_idle, state_total, cyc_i, stb_i, we_i, count_done) + begin + state_idle_next <= state_idle; + state_total_next <= state_total; + count_start <= '0'; + + ack_o <= '0'; + + mem_enable <= '0'; + MemOE <= '1'; + MemWR <= '1'; + + if state_idle = '1' then + -- Idle, waiting for transaction request + if cyc_i = '1' and stb_i = '1' then + state_idle_next <= '0'; + count_start <= '1'; + end if; + elsif state_total = '0' then + -- Memory active + mem_enable <= '1'; + MemOE <= we_i; + MemWR <= not we_i; + + if count_done = '1' then + ack_o <= '1'; + state_total_next <= '1'; + end if; + else + -- Memory inactive + if count_done = '1' then + state_idle_next <= '1'; + state_total_next <= '0'; + end if; + end if; + end process; + + -- Little-endian memory interface + RamCS <= not (mem_enable and ram_cyc_i); + RamAdv <= '0'; + RamClk <= '0'; + RamCRE <= '0'; + RamUB <= '1'; + RamLB <= '1'; + FlashCS <= not (mem_enable and fls_cyc_i); + FlashRp <= '1'; + MemAdr <= adr_i(23 downto 1); + MemDB_o <= dat_i; + dat_o <= MemDB_i; + + + -- Cycle counter + count_cycles <= CYCLES_TOTAL when state_total = '1' else CYCLES_ACTIVE; + + e_count: srl16 + generic map (INIT => x"0000") + port map ( + clk => clk_i, + + a0 => count_cycles(0), + a1 => count_cycles(1), + a2 => count_cycles(2), + a3 => count_cycles(3), + + d => count_start, + + q => count_done + ); + +end behavioral; diff --git a/libraries/pipe/ctrl.vhd b/libraries/pipe/ctrl.vhd new file mode 100644 index 0000000..cb8200c --- /dev/null +++ b/libraries/pipe/ctrl.vhd @@ -0,0 +1,85 @@ +-------------------------------------------------------------------------------- +-- ctrl - Pipeline stage control logic +-- +-- Keeps track of the full/empty status of a pipeline stage and propagates stall +-- signals upstream. +-- +-- There is a combinational logic chain between the rdy_i and rdy_o signals. It +-- may be be necessary to include skid buffers periodically to break the chain +-- for timing closure purposes. +-- +-- The interstage register is assumed to be external because some components +-- like BRAMs have built-in output registers that a design may want to use as +-- the interstage register. +-- +-- Also, some pipeline stages may have internal state that needs the "enable" +-- signal to know when to perform their actions (such as delay lines or +-- accumulators). +-------------------------------------------------------------------------------- +-- Ports: +-- +-- rst_i - Active high synchronous reset +-- clk_i - Clock +-- +-- stb_i - Input strobe, high when upstream stage has a value to accept +-- rdy_o - Ready output, high when the upstream stage can release its value +-- +-- stb_o - Output strobe, high when this stage has a value to pass downstream +-- rdy_i - Ready input, high when this stage can release its value +-- +-- en_o - Enable signal to external interstage register +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + + +entity ctrl is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + stb_i: in std_logic; + rdy_o: out std_logic; + + stb_o: out std_logic; + rdy_i: in std_logic; + + en_o: out std_logic + ); +end ctrl; + + +architecture behavioral of ctrl is + + signal enable: std_logic; + signal rdy: std_logic; + signal stb_reg: std_logic; + +begin + + -- Enable the (external) stage register to latch if: + -- * it is currently empty + -- * it is currently full, but the downstream stage is accepting the current data + -- + -- But not if we're in reset - if the upstream source is in a different reset + -- domain and comes out of reset before this, we don't want to respond as ready + enable <= ((not stb_reg) or rdy_i) and stb_i and (not rst_i); + en_o <= enable; + + stb_o <= stb_reg; + rdy <= ((not stb_reg) or rdy_i) and (not rst_i); + rdy_o <= rdy; + + process (rst_i, clk_i, enable, stb_i) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + stb_reg <= '0'; + elsif rdy = '1' then + stb_reg <= stb_i; + end if; + end if; + end process; + +end behavioral; diff --git a/libraries/pipe/ctrl_merge.vhd b/libraries/pipe/ctrl_merge.vhd new file mode 100644 index 0000000..b014194 --- /dev/null +++ b/libraries/pipe/ctrl_merge.vhd @@ -0,0 +1,72 @@ +-------------------------------------------------------------------------------- +-- ctrl_merge - Merge two upstream pipelines into a single pipeline +-- +-- Both upstream stages must strobe before either are given a ready signal, and +-- both are given a ready signal at the same time. This entity is for pipeline +-- stages that require and consume information from two upstream pipes, not for +-- stages that take one *or* another or interleave streams. +-------------------------------------------------------------------------------- +-- Ports: +-- +-- rst_i - Active high synchronous reset +-- clk_i - Clock +-- +-- a_stb_i - Upstream source A strobe +-- b_rdy_o - Ready signal to source A +-- +-- b_stb_i - Upstream source B strobe +-- b_rdy_o - Ready signal to source B +-- +-- stb_o - Combined strobe for both sources +-- rdy_i - Ready signal from downstream +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + + +entity ctrl_merge is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + a_stb_i: in std_logic; + a_rdy_o: out std_logic; + + b_stb_i: in std_logic; + b_rdy_o: out std_logic; + + stb_o: out std_logic; + rdy_i: in std_logic; + + en_o: out std_logic + ); +end ctrl_merge; + + +architecture behavioral of ctrl_merge is + + signal costrobe: std_logic; + signal rdy: std_logic; + +begin + + costrobe <= a_stb_i and b_stb_i; + a_rdy_o <= costrobe and rdy; + b_rdy_o <= costrobe and rdy; + + e_ctrl: entity work.ctrl + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => costrobe, + rdy_o => rdy, + + stb_o => stb_o, + rdy_i => rdy_i, + + en_o => en_o + ); + +end behavioral; diff --git a/libraries/pipe/ctrl_split.vhd b/libraries/pipe/ctrl_split.vhd new file mode 100644 index 0000000..350b6f9 --- /dev/null +++ b/libraries/pipe/ctrl_split.vhd @@ -0,0 +1,91 @@ +-------------------------------------------------------------------------------- +-- ctrl_split - Split a pipeline into two downstream pipelines +-- +-- Both downstream stages must respond with ready signals before an enable +-- signal will be passed to the stage and a ready signal is passed upstream. +-- This entity is for situations where both downstream stages consume values +-- from the current stage, not where one *or* the other may, or for +-- demultiplexing data streams. +-------------------------------------------------------------------------------- +-- Ports: +-- +-- rst_i - Active high synchronous reset +-- clk_i - Clock +-- +-- stb_i - Input strobe, high when upstream stage has a value to accept +-- rdy_o - Ready output, high when the upstream stage can release its value +-- +-- a_stb_o - Strobe to sink A +-- a_rdy_i - Ready from sink A +-- +-- b_stb_o - Strobe to sink B +-- b_rdy_i - Ready from sink B +-- +-- en_o - Enable signal to current stage's logic and interstage register +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + + +entity ctrl_split is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + stb_i: in std_logic; + rdy_o: out std_logic; + + a_stb_o: out std_logic; + a_rdy_i: in std_logic; + + b_stb_o: out std_logic; + b_rdy_i: in std_logic; + + en_o: out std_logic + ); +end ctrl_split; + + +architecture behavioral of ctrl_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 rdy: std_logic; + +begin + + a_rdy <= (not a_stb_reg) or a_rdy_i; + b_rdy <= (not b_stb_reg) or b_rdy_i; + rdy <= a_rdy and b_rdy and (not rst_i); + rdy_o <= rdy; + en_o <= rdy and stb_i; + + -- Full registers + process (rst_i, clk_i, rdy, stb_i, a_rdy_i, b_rdy_i) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + a_stb_reg <= '0'; + b_stb_reg <= '0'; + elsif rdy = '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; + +end behavioral; diff --git a/libraries/pipe/stage_delay.vhd b/libraries/pipe/stage_delay.vhd new file mode 100644 index 0000000..e30bc45 --- /dev/null +++ b/libraries/pipe/stage_delay.vhd @@ -0,0 +1,60 @@ +-------------------------------------------------------------------------------- +-- stage_delay - Ready-made pipeline stage that delays a data stream +-- +-- The first DELAY samples coming out will be all zeroes. +-- +-- rst_i should be held active for DELAY or 16 clock cycles, whichever is lower. +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + +library dsp; +library work; + + +entity stage_delay is + generic ( + WIDTH: positive := 16; + DELAY: 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 stage_delay; + + +architecture behavioral of stage_delay is + + signal en: std_logic; + +begin + + stb_o <= stb_i; + rdy_o <= rdy_i; + + en <= stb_i and rdy_i; + + e_delay: entity dap.delay_srl + generic map ( + WIDTH => WIDTH, + DELAY => DELAY + ) + port map ( + rst_i => rst_i, + clk_i => clk_i, + en_i => en, + dat_i => dat_i, + dat_o => dat_o + ); + +end behavioral; diff --git a/libraries/pipe/stage_merge.vhd b/libraries/pipe/stage_merge.vhd new file mode 100644 index 0000000..5a9a28a --- /dev/null +++ b/libraries/pipe/stage_merge.vhd @@ -0,0 +1,76 @@ +-------------------------------------------------------------------------------- +-- stage_merge - Ready-made empty pipeline stage that merges a pipeline +-- +-- Can be used as a generic interstage register at the end of a pipeline stage +-- that needs to merge two data streams. +-- +-- There is only one data input dat_i, which will be registered to the data +-- output dat_o. It is up to the rest of the logic to decide what that value +-- should be as a combination of the two data values from the upstream stage. +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + +library work; + + +entity stage_merge is + generic ( + WIDTH: positive := 16 + ); + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + a_stb_i: in std_logic; + a_rdy_o: out std_logic; + + b_stb_i: in std_logic; + b_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 stage_merge; + + +architecture behavioral of stage_merge is + + signal en: std_logic; + signal dat_reg: std_logic_vector(WIDTH-1 downto 0); + +begin + + e_ctrl: entity work.ctrl_merge + port map ( + rst_i => rst_i, + clk_i => clk_i, + + 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_o, + rdy_i => rdy_i, + + en_o => en + ); + + process (clk_i, en, dat_i) + begin + if rising_edge(clk_i) then + if en = '1' then + dat_reg <= dat_i; + end if; + end if; + end process; + + dat_o <= dat_reg; + +end behavioral; diff --git a/libraries/pipe/stage_mux2.vhd b/libraries/pipe/stage_mux2.vhd new file mode 100644 index 0000000..23a9174 --- /dev/null +++ b/libraries/pipe/stage_mux2.vhd @@ -0,0 +1,63 @@ +-------------------------------------------------------------------------------- +-- stage_mux2 - Ready-made pipeline stage that multiplexes two pipelines to one +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + +library work; + + +entity stage_mux2 is + generic ( + WIDTH: positive := 16 + ); + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + sel: in std_logic; + + stb_0_i: in std_logic; + rdy_0_o: out std_logic; + dat_0_i: in std_logic_vector(WIDTH-1 downto 0); + + stb_1_i: in std_logic; + rdy_1_o: out std_logic; + dat_1_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 stage_mux2; + + +architecture behavioral of stage_mux2 is + + signal dat: std_logic_vector(WIDTH-1 downto 0); + +begin + + dat <= dat_0_i when sel = '0' else dat_1_i; + + e_ctrl: entity work.stage_merge + generic map (WIDTH => WIDTH) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + a_stb_i => stb_0_i, + a_rdy_o => rdy_0_o, + + b_stb_i => stb_1_i, + b_rdy_o => rdy_1_o, + + dat_i => dat, + + stb_o => stb_o, + rdy_i => rdy_i, + dat_o => dat_o + ); + +end behavioral; diff --git a/libraries/pipe/stage_reg.vhd b/libraries/pipe/stage_reg.vhd new file mode 100644 index 0000000..3b67681 --- /dev/null +++ b/libraries/pipe/stage_reg.vhd @@ -0,0 +1,64 @@ +-------------------------------------------------------------------------------- +-- stage_empty - Ready-made empty pipeline stage +-- +-- Can be used as a generic interstage register at the end of a pipeline stage. +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + +library work; + + +entity stage_reg 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 stage_reg; + + +architecture behavioral of stage_reg is + + signal en: std_logic; + signal dat_reg: std_logic_vector(WIDTH-1 downto 0); + +begin + + e_ctrl: entity work.ctrl + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => stb_i, + rdy_o => rdy_o, + + stb_o => stb_o, + rdy_i => rdy_i, + + en_o => en + ); + + process (clk_i, en, dat_i) + begin + if rising_edge(clk_i) then + if en = '1' then + dat_reg <= dat_i; + end if; + end if; + end process; + + dat_o <= dat_reg; + +end behavioral; diff --git a/libraries/pipe/stage_skid.vhd b/libraries/pipe/stage_skid.vhd new file mode 100644 index 0000000..c1d6354 --- /dev/null +++ b/libraries/pipe/stage_skid.vhd @@ -0,0 +1,93 @@ +-------------------------------------------------------------------------------- +-- stage_skid - Skid buffer to break combinational ready (stall) signal chains +-- +-- Can be placed inline between any two pipeline stages to break all +-- combinational logic chains in the control signalling between the two. +-- +-- This does add a multiplexer delay to the data signal, so it may adversely +-- affect timing if placed before the pipeline stage that is already the +-- critical path for clock cycle timing. +-------------------------------------------------------------------------------- +-- Ports: +-- +-- rst_i - Active high synchronous reset +-- clk_i - Clock +-- +-- stb_i - Input strobe, high when upstream stage has a value to accept +-- rdy_o - Ready output, high when the upstream stage can release its value +-- dat_i - Value being passed downstream +-- +-- stb_o - Output strobe, high when a value can pass downstream +-- rdy_i - Ready input, high when downstream can accept a value +-- dat_o - Value from upstream +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + + +entity stage_skid 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 stage_skid; + + +architecture behavioral of stage_skid is + + -- Registered ready signal + signal rdy_reg: std_logic := '1'; + + -- Skid buffer + signal skid_en: std_logic; + signal skid_clr: std_logic; + signal skid_stb_reg: std_logic := '0'; + signal skid_dat_reg: std_logic_vector(WIDTH-1 downto 0); + +begin + + stb_o <= stb_reg or skid_stb_reg; + rdy_o <= rdy_reg; + dat_o <= skid_dat_reg when skid_stb_reg = '1' else dat_i; + + skid_en <= rdy_reg and (not rdy_i); -- Upstream thinks downstream is ready, but it's not + skid_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; + + -- Skid buffer + process (rst_i, clk_i, skid_en, skid_clr, stb_reg, dat_i) + begin + if rising_edge(clk_i) then + if rst_i = '1' or skid_clr = '1' then + skid_stb_reg <= '0'; + elsif skid_en = '1' then + skid_stb_reg <= stb_i; + skid_dat_reg <= dat_i; + end if; + end if; + end process; + +end behavioral; diff --git a/libraries/pipe/stage_split.vhd b/libraries/pipe/stage_split.vhd new file mode 100644 index 0000000..f92259f --- /dev/null +++ b/libraries/pipe/stage_split.vhd @@ -0,0 +1,68 @@ +-------------------------------------------------------------------------------- +-- stage_split - Ready-made empty pipeline stage that splits a pipeline +-- +-- Can be used as a generic interstage register at the end of a pipeline stage +-- that needs to split in twain. +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + +library work; + + +entity stage_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 stage_split; + + +architecture behavioral of stage_split is + + signal en: std_logic; + signal dat_reg: std_logic_vector(WIDTH-1 downto 0); + +begin + + e_ctrl: entity work.ctrl_split + port map ( + rst_i => rst_i, + clk_i => clk_i, + stb_i => stb_i, + rdy_o => rdy_o, + a_stb_o => a_stb_o, + a_rdy_i => a_rdy_i, + b_stb_o => b_stb_o, + b_rdy_i => b_rdy_i, + en_o => en + ); + + process (clk_i, en, dat_i) + begin + if rising_edge(clk_i) then + if en = '1' then + dat_reg <= dat_i; + end if; + end if; + end process; + + dat_o <= dat_reg; + +end behavioral; diff --git a/libraries/pipe/tests/test_ctrl.vhd b/libraries/pipe/tests/test_ctrl.vhd new file mode 100644 index 0000000..8ace096 --- /dev/null +++ b/libraries/pipe/tests/test_ctrl.vhd @@ -0,0 +1,176 @@ +library ieee; +use ieee.std_logic_1164.all; + +library work; + + +entity test_ctrl is +end test_ctrl; + + +architecture behavior of test_ctrl is + + type test_t is ( + T_RESET, + T_0_NO_OP, + T_1_NO_OP, + T_4_EMPTY_WITH_STALL, + T_6_FULL_STALL, + T_7_FULL_NO_STALL, + T_2_FULL_STALL_2, + T_3_DRAIN, + T_5_NORMAL, + T_DONE + ); + constant CLK_I_PERIOD: time := 20 ns; + + signal test: test_t; + + signal rst_i: std_logic; + signal clk_i: std_logic; + + signal stb_i: std_logic; + signal rdy_o: std_logic; + + signal stb_o: std_logic; + signal rdy_i: std_logic; + + signal en_o: std_logic; + +begin + + p_test: process + begin + -- Initial values + stb_i <= '0'; + rdy_i <= '0'; + + -- Reset + test <= T_RESET; + rst_i <= '1'; + wait for CLK_I_PERIOD*2; + rst_i <= '0'; + wait until falling_edge(clk_i); + + -- Test + + -- stb_i stb_o rdy_i | rdy_o stb_o' en_o + -- 0 0 0 0 | + -- 1 0 0 1 | + -- 2 0 1 0 | + -- 3 0 1 1 | + -- 4 1 0 0 | + -- 5 1 0 1 | + -- 6 1 1 0 | + -- 7 1 1 1 | + + test <= T_0_NO_OP; + stb_i <= '0'; -- Nothing from upstream + rdy_i <= '0'; -- Downstream stalled + wait for 1 ns; + assert stb_o = '0'; -- Currently empty + assert rdy_o = '1'; -- Report ready to upstream + assert en_o = '0'; -- Don't load + wait until falling_edge(clk_i); + assert stb_o = '0'; -- Still empty + + test <= T_1_NO_OP; + stb_i <= '0'; -- Nothing from upstream + rdy_i <= '1'; -- Downstream ready + wait for 1 ns; + assert stb_o = '0'; -- Currently empty + assert rdy_o = '1'; -- Report ready to upstream + assert en_o = '0'; -- Don't load + wait until falling_edge(clk_i); + assert stb_o = '0'; -- Still empty + + test <= T_4_EMPTY_WITH_STALL; + stb_i <= '1'; -- Upstream pushing + rdy_i <= '0'; -- Downstream stalled + wait for 1 ns; + assert stb_o = '0' report "a"; -- Currently empty + assert rdy_o = '1' report "b"; -- Report ready to upstream + assert en_o = '1' report "c"; -- Perform load + wait until falling_edge(clk_i); + assert stb_o = '1' report "d"; -- Ends up full + + test <= T_6_FULL_STALL; + stb_i <= '1'; -- Upstream pushing + rdy_i <= '0'; -- Downstream stalled + wait for 1 ns; + assert stb_o = '1'; -- Currently full + assert rdy_o = '0'; -- Propagate stall + assert en_o = '0'; -- Don't load + wait until falling_edge(clk_i); + assert stb_o = '1'; -- Still full + + test <= T_7_FULL_NO_STALL; + stb_i <= '1'; -- Upstream pushing + rdy_i <= '1'; -- Downstream ready + wait for 1 ns; + assert stb_o = '1'; -- Currently full + assert rdy_o = '1'; -- Report ready to upstream + assert en_o = '1'; -- Perform load + wait until falling_edge(clk_i); + assert stb_o = '1'; -- Ends up full + + test <= T_2_FULL_STALL_2; + stb_i <= '0'; -- Upstream not pushing + rdy_i <= '0'; -- Downstream stalled + wait for 1 ns; + assert stb_o = '1'; -- Currently full + assert rdy_o = '0'; -- Propagate stall + assert en_o = '0'; -- Don't load + wait until falling_edge(clk_i); + assert stb_o = '1'; -- Still full + + test <= T_3_DRAIN; + stb_i <= '0'; -- Upstream not pushing + rdy_i <= '1'; -- Downstream ready + wait for 1 ns; + assert stb_o = '1'; -- Currently full + assert rdy_o = '1'; -- Report ready to upstream + assert en_o = '0'; -- Don't load + wait until falling_edge(clk_i); + assert stb_o = '0'; -- Ends up empty + + test <= T_5_NORMAL; + stb_i <= '1'; -- Upstream pushing + rdy_i <= '1'; -- Downstream ready + wait for 1 ns; + assert stb_o = '0'; -- Currently empty + assert rdy_o = '1'; -- Report ready to upstream + assert en_o = '1'; -- Perform load + wait until falling_edge(clk_i); + assert stb_o = '1'; -- Ends up full + + -- Done + test <= T_DONE; + wait; + end process; + + + e_uut: entity work.ctrl + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => stb_i, + rdy_o => rdy_o, + + stb_o => stb_o, + rdy_i => rdy_i, + + en_o => en_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/pipe/tests/test_ctrl_split.vhd b/libraries/pipe/tests/test_ctrl_split.vhd new file mode 100644 index 0000000..b368bf1 --- /dev/null +++ b/libraries/pipe/tests/test_ctrl_split.vhd @@ -0,0 +1,434 @@ +library ieee; +use ieee.std_logic_1164.all; + +library work; + + +entity test_ctrl_split is +end test_ctrl_split; + + +architecture behavior of test_ctrl_split is + + constant CLK_I_PERIOD: time := 20 ns; + + -- Some tests are redundant due to symmetry and skipped, see table below + type test_t is ( + T_RESET, + T_00, T_01, T_02, T_03, T_04, T_05, T_06, T_07, + T_08, T_09, T_0A, T_0B, T_0C, T_0D, T_0E, T_0F, + T_10, T_11, T_12, T_13, T_14, T_15, T_16, T_17, + T_18, T_19, T_1A, T_1B, T_1C, T_1D, T_1E, T_1F, + T_DONE + ); + + signal test: test_t; + + signal rst_i: std_logic; + signal clk_i: std_logic; + + signal stb_i: std_logic; + signal rdy_o: std_logic; + + signal a_stb_o: std_logic; + signal a_rdy_i: std_logic; + + signal b_stb_o: std_logic; + signal b_rdy_i: std_logic; + + signal en_o: std_logic; + +begin + + p_test: process + begin + -- Inital values + + -- Reset + rst_i <= '1'; + wait for CLK_I_PERIOD*2; + rst_i <= '0'; + wait until falling_edge(clk_i); + + -- Tests + + -- Full test enumeration + -- stb_i a_rdy_i b_rdy_i a_stb_o b_stb_o | rdy_o a_stb_o' b_stb_o' en_o test (symmetric-test) + -- -------------------------------------------+-------------------------------------------------------- + -- 0 0 0 0 0 | 1 0 0 0 T_00 + -- 0 0 0 0 1 | 0 0 1 0 T_01 (T_02) + -- 0 0 0 1 0 | 0 1 0 0 T_02 (T_01) + -- 0 0 0 1 1 | 0 1 1 0 T_03 + -- 0 0 1 0 0 | 1 0 0 0 T_04 (T_08) + -- 0 0 1 0 1 | 1 0 0 0 T_05 (T_0A) + -- 0 0 1 1 0 | 0 1 0 0 T_06 (T_09) + -- 0 0 1 1 1 | 0 1 0 0 T_07 (T_0B) + -- 0 1 0 0 0 | 1 0 0 0 T_08 (T_04) + -- 0 1 0 0 1 | 0 0 1 0 T_09 (T_06) + -- 0 1 0 1 0 | 1 0 0 0 T_0A (T_05) + -- 0 1 0 1 1 | 0 0 1 0 T_0B (T_07) + -- 0 1 1 0 0 | 1 0 0 0 T_0C + -- 0 1 1 0 1 | 1 0 0 0 T_0D (T_0E) + -- 0 1 1 1 0 | 1 0 0 0 T_0E (T_0D) + -- 0 1 1 1 1 | 1 0 0 0 T_0F + -- 1 0 0 0 0 | 1 1 1 1 T_10 + -- 1 0 0 0 1 | 0 0 1 0 T_11 (T_12) + -- 1 0 0 1 0 | 0 1 0 0 T_12 (T_11) + -- 1 0 0 1 1 | 0 1 1 0 T_13 + -- 1 0 1 0 0 | 1 1 1 1 T_14 (T_18) + -- 1 0 1 0 1 | 1 1 1 1 T_15 (T_1A) + -- 1 0 1 1 0 | 0 1 0 0 T_16 (T_19) + -- 1 0 1 1 1 | 0 1 0 0 T_17 (T_1B) + -- 1 1 0 0 0 | 1 1 1 1 T_18 (T_14) + -- 1 1 0 0 1 | 0 0 1 0 T_19 (T_16) + -- 1 1 0 1 0 | 1 1 1 1 T_1A (T_15) + -- 1 1 0 1 1 | 0 0 1 0 T_1B (T_17) + -- 1 1 1 0 0 | 1 1 1 1 T_1C + -- 1 1 1 0 1 | 1 1 1 1 T_1D (T_1E) + -- 1 1 1 1 0 | 1 1 1 1 T_1E (T_1D) + -- 1 1 1 1 1 | 1 1 1 1 T_1F + + -- Test sequence with reduncancies removed (T_0B run three times to traverse state graph) + -- stb_i a_rdy_i b_rdy_i a_stb_o b_stb_o | rdy_o a_stb_o' b_stb_o' en_o test (symmetric-test) + -- -------------------------------------------+-------------------------------------------------------- + -- 0 0 0 0 0 | 1 0 0 0 T_00 + -- 0 0 1 0 0 | 1 0 0 0 T_04 (T_08) + -- 0 1 1 0 0 | 1 0 0 0 T_0C + -- 1 0 0 0 0 | 1 1 1 1 T_10 + -- 0 0 0 1 1 | 0 1 1 0 T_03 + -- 1 0 0 1 1 | 0 1 1 0 T_13 + -- 1 1 1 1 1 | 1 1 1 1 T_1F + -- 0 1 1 1 1 | 1 0 0 0 T_0F + -- 1 0 1 0 0 | 1 1 1 1 T_14 (T_18) + -- 0 1 0 1 1 | 0 0 1 0 T_0B (T_07) + -- 0 0 0 0 1 | 0 0 1 0 T_01 (T_02) + -- 0 1 0 0 1 | 0 0 1 0 T_09 (T_06) + -- 1 0 0 0 1 | 0 0 1 0 T_11 (T_12) + -- 1 1 0 0 1 | 0 0 1 0 T_19 (T_16) + -- 1 0 1 0 1 | 1 1 1 1 T_15 (T_1A) + -- 1 1 0 1 1 | 0 0 1 0 T_1B (T_17) + -- 1 1 1 0 1 | 1 1 1 1 T_1D (T_1E) + -- 0 1 0 1 1 | 0 0 1 0 T_0B (T_07)* + -- 0 0 1 0 1 | 1 0 0 0 T_05 (T_0A) + -- 1 1 1 0 0 | 1 1 1 1 T_1C + -- 0 1 0 1 1 | 0 0 1 0 T_0B (T_07)* + -- 0 1 1 0 1 | 1 0 0 0 T_0D (T_0E) + -- stb_i a_rdy_i b_rdy_i a_stb_o b_stb_o | rdy_o a_stb_o' b_stb_o' en_o test (symmetric-test) + + test <= T_00; + stb_i <= '0'; + a_rdy_i <= '0'; + b_rdy_i <= '0'; + wait for 1 ns; + assert a_stb_o = '0' report "a_stb_o"; + assert b_stb_o = '0' report "b_stb_o"; + assert rdy_o = '1' report "rdy_o"; + assert en_o = '0' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '0' report "a_stb_o'"; + assert b_stb_o = '0' report "b_stb_o'"; + + test <= T_04; + stb_i <= '0'; + a_rdy_i <= '0'; + b_rdy_i <= '1'; + wait for 1 ns; + assert a_stb_o = '0' report "a_stb_o"; + assert b_stb_o = '0' report "b_stb_o"; + assert rdy_o = '1' report "rdy_o"; + assert en_o = '0' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '0' report "a_stb_o'"; + assert b_stb_o = '0' report "b_stb_o'"; + + test <= T_0C; + stb_i <= '0'; + a_rdy_i <= '1'; + b_rdy_i <= '1'; + wait for 1 ns; + assert a_stb_o = '0' report "a_stb_o"; + assert b_stb_o = '0' report "b_stb_o"; + assert rdy_o = '1' report "rdy_o"; + assert en_o = '0' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '0' report "a_stb_o'"; + assert b_stb_o = '0' report "b_stb_o'"; + + test <= T_10; + stb_i <= '1'; + a_rdy_i <= '0'; + b_rdy_i <= '0'; + wait for 1 ns; + assert a_stb_o = '0' report "a_stb_o"; + assert b_stb_o = '0' report "b_stb_o"; + assert rdy_o = '1' report "rdy_o"; + assert en_o = '1' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '1' report "a_stb_o'"; + assert b_stb_o = '1' report "b_stb_o'"; + + test <= T_03; + stb_i <= '0'; + a_rdy_i <= '0'; + b_rdy_i <= '0'; + wait for 1 ns; + assert a_stb_o = '1' report "a_stb_o"; + assert b_stb_o = '1' report "b_stb_o"; + assert rdy_o = '0' report "rdy_o"; + assert en_o = '0' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '1' report "a_stb_o'"; + assert b_stb_o = '1' report "b_stb_o'"; + + test <= T_13; + stb_i <= '1'; + a_rdy_i <= '0'; + b_rdy_i <= '0'; + wait for 1 ns; + assert a_stb_o = '1' report "a_stb_o"; + assert b_stb_o = '1' report "b_stb_o"; + assert rdy_o = '0' report "rdy_o"; + assert en_o = '0' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '1' report "a_stb_o'"; + assert b_stb_o = '1' report "b_stb_o'"; + + test <= T_1F; + stb_i <= '1'; + a_rdy_i <= '1'; + b_rdy_i <= '1'; + wait for 1 ns; + assert a_stb_o = '1' report "a_stb_o"; + assert b_stb_o = '1' report "b_stb_o"; + assert rdy_o = '1' report "rdy_o"; + assert en_o = '1' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '1' report "a_stb_o'"; + assert b_stb_o = '1' report "b_stb_o'"; + + test <= T_0F; + stb_i <= '0'; + a_rdy_i <= '1'; + b_rdy_i <= '1'; + wait for 1 ns; + assert a_stb_o = '1' report "a_stb_o"; + assert b_stb_o = '1' report "b_stb_o"; + assert rdy_o = '1' report "rdy_o"; + assert en_o = '0' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '0' report "a_stb_o'"; + assert b_stb_o = '0' report "b_stb_o'"; + + test <= T_14; + stb_i <= '1'; + a_rdy_i <= '0'; + b_rdy_i <= '1'; + wait for 1 ns; + assert a_stb_o = '0' report "a_stb_o"; + assert b_stb_o = '0' report "b_stb_o"; + assert rdy_o = '1' report "rdy_o"; + assert en_o = '1' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '1' report "a_stb_o'"; + assert b_stb_o = '1' report "b_stb_o'"; + + test <= T_0B; + stb_i <= '0'; + a_rdy_i <= '1'; + b_rdy_i <= '0'; + wait for 1 ns; + assert a_stb_o = '1' report "a_stb_o"; + assert b_stb_o = '1' report "b_stb_o"; + assert rdy_o = '0' report "rdy_o"; + assert en_o = '0' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '0' report "a_stb_o'"; + assert b_stb_o = '1' report "b_stb_o'"; + + test <= T_01; + stb_i <= '0'; + a_rdy_i <= '0'; + b_rdy_i <= '0'; + wait for 1 ns; + assert a_stb_o = '0' report "a_stb_o"; + assert b_stb_o = '1' report "b_stb_o"; + assert rdy_o = '0' report "rdy_o"; + assert en_o = '0' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '0' report "a_stb_o'"; + assert b_stb_o = '1' report "b_stb_o'"; + + test <= T_09; + stb_i <= '0'; + a_rdy_i <= '1'; + b_rdy_i <= '0'; + wait for 1 ns; + assert a_stb_o = '0' report "a_stb_o"; + assert b_stb_o = '1' report "b_stb_o"; + assert rdy_o = '0' report "rdy_o"; + assert en_o = '0' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '0' report "a_stb_o'"; + assert b_stb_o = '1' report "b_stb_o'"; + + test <= T_11; + stb_i <= '1'; + a_rdy_i <= '0'; + b_rdy_i <= '0'; + wait for 1 ns; + assert a_stb_o = '0' report "a_stb_o"; + assert b_stb_o = '1' report "b_stb_o"; + assert rdy_o = '0' report "rdy_o"; + assert en_o = '0' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '0' report "a_stb_o'"; + assert b_stb_o = '1' report "b_stb_o'"; + + test <= T_19; + stb_i <= '1'; + a_rdy_i <= '1'; + b_rdy_i <= '0'; + wait for 1 ns; + assert a_stb_o = '0' report "a_stb_o"; + assert b_stb_o = '1' report "b_stb_o"; + assert rdy_o = '0' report "rdy_o"; + assert en_o = '0' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '0' report "a_stb_o'"; + assert b_stb_o = '1' report "b_stb_o'"; + + test <= T_15; + stb_i <= '1'; + a_rdy_i <= '0'; + b_rdy_i <= '1'; + wait for 1 ns; + assert a_stb_o = '0' report "a_stb_o"; + assert b_stb_o = '1' report "b_stb_o"; + assert rdy_o = '1' report "rdy_o"; + assert en_o = '1' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '1' report "a_stb_o'"; + assert b_stb_o = '1' report "b_stb_o'"; + + test <= T_1B; + stb_i <= '1'; + a_rdy_i <= '1'; + b_rdy_i <= '0'; + wait for 1 ns; + assert a_stb_o = '1' report "a_stb_o"; + assert b_stb_o = '1' report "b_stb_o"; + assert rdy_o = '0' report "rdy_o"; + assert en_o = '0' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '0' report "a_stb_o'"; + assert b_stb_o = '1' report "b_stb_o'"; + + test <= T_1D; + stb_i <= '1'; + a_rdy_i <= '1'; + b_rdy_i <= '1'; + wait for 1 ns; + assert a_stb_o = '0' report "a_stb_o"; + assert b_stb_o = '1' report "b_stb_o"; + assert rdy_o = '1' report "rdy_o"; + assert en_o = '1' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '1' report "a_stb_o'"; + assert b_stb_o = '1' report "b_stb_o'"; + + test <= T_0B; + stb_i <= '0'; + a_rdy_i <= '1'; + b_rdy_i <= '0'; + wait for 1 ns; + assert a_stb_o = '1' report "a_stb_o"; + assert b_stb_o = '1' report "b_stb_o"; + assert rdy_o = '0' report "rdy_o"; + assert en_o = '0' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '0' report "a_stb_o'"; + assert b_stb_o = '1' report "b_stb_o'"; + + test <= T_05; + stb_i <= '0'; + a_rdy_i <= '0'; + b_rdy_i <= '1'; + wait for 1 ns; + assert a_stb_o = '0' report "a_stb_o"; + assert b_stb_o = '1' report "b_stb_o"; + assert rdy_o = '1' report "rdy_o"; + assert en_o = '0' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '0' report "a_stb_o'"; + assert b_stb_o = '0' report "b_stb_o'"; + + test <= T_1C; + stb_i <= '1'; + a_rdy_i <= '1'; + b_rdy_i <= '1'; + wait for 1 ns; + assert a_stb_o = '0' report "a_stb_o"; + assert b_stb_o = '0' report "b_stb_o"; + assert rdy_o = '1' report "rdy_o"; + assert en_o = '1' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '1' report "a_stb_o'"; + assert b_stb_o = '1' report "b_stb_o'"; + + test <= T_0B; + stb_i <= '0'; + a_rdy_i <= '1'; + b_rdy_i <= '0'; + wait for 1 ns; + assert a_stb_o = '1' report "a_stb_o"; + assert b_stb_o = '1' report "b_stb_o"; + assert rdy_o = '0' report "rdy_o"; + assert en_o = '0' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '0' report "a_stb_o'"; + assert b_stb_o = '1' report "b_stb_o'"; + + test <= T_0D; + stb_i <= '0'; + a_rdy_i <= '1'; + b_rdy_i <= '1'; + wait for 1 ns; + assert a_stb_o = '0' report "a_stb_o"; + assert b_stb_o = '1' report "b_stb_o"; + assert rdy_o = '1' report "rdy_o"; + assert en_o = '0' report "en_o"; + wait until falling_edge(clk_i); + assert a_stb_o = '0' report "a_stb_o'"; + assert b_stb_o = '0' report "b_stb_o'"; + + -- Done + test <= T_DONE; + wait; + end process; + + e_uut: entity work.ctrl_split + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => stb_i, + rdy_o => rdy_o, + + a_stb_o => a_stb_o, + a_rdy_i => a_rdy_i, + + b_stb_o => b_stb_o, + b_rdy_i => b_rdy_i, + + en_o => en_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/pipe/tests/test_splitmux.vhd b/libraries/pipe/tests/test_splitmux.vhd new file mode 100644 index 0000000..05973bb --- /dev/null +++ b/libraries/pipe/tests/test_splitmux.vhd @@ -0,0 +1,151 @@ +library ieee; +use ieee.std_logic_1164.all; + +library work; + + +entity test_splitmux is +end test_splitmux; + + +architecture behavior of test_splitmux is + + constant CLK_I_PERIOD: time := 20 ns; + + signal rst_i: std_logic; + signal clk_i: std_logic; + + signal in_stb_i: std_logic; + signal in_rdy_o: std_logic; + signal in_dat_i: std_logic_vector(7 downto 0); + + signal split_a_stb: std_logic; + signal split_a_rdy: std_logic; + signal split_b_stb: std_logic; + signal split_b_rdy: std_logic; + signal split_dat: std_logic_vector(7 downto 0); + + signal path_a_stb: std_logic; + signal path_a_rdy: std_logic; + signal path_a_dat: std_logic_vector(7 downto 0); + + signal path_b_stb: std_logic; + signal path_b_rdy: std_logic; + signal path_b_dat: std_logic_vector(7 downto 0); + + signal sel: std_logic; + signal out_stb_o: std_logic; + signal out_rdy_i: std_logic; + signal out_dat_o: std_logic_vector(7 downto 0); + +begin + + p_test: process + begin + -- Initial values + in_stb_i <= '0'; + out_rdy_i <= '0'; + sel <= '0'; + in_dat_i <= x"a5"; + + -- Reset + rst_i <= '1'; + wait for CLK_I_PERIOD*2; + rst_i <= '0'; + wait until falling_edge(clk_i); + + -- Test + wait for CLK_I_PERIOD*3; + in_stb_i <= '1'; + wait for CLK_I_PERIOD; + in_stb_i <= '0'; + + wait for CLK_I_PERIOD*3; + out_rdy_i <= '1'; + + -- Done + wait; + end process; + + + e_split: entity work.stage_split + generic map (WIDTH => 8) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => in_stb_i, + rdy_o => in_rdy_o, + dat_i => in_dat_i, + + a_stb_o => split_a_stb, + a_rdy_i => split_a_rdy, + + b_stb_o => split_b_stb, + b_rdy_i => split_b_rdy, + + dat_o => split_dat + ); + + + e_path_a: entity work.stage_reg + generic map (WIDTH => 8) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => split_a_stb, + rdy_o => split_a_rdy, + dat_i => split_dat, + + stb_o => path_a_stb, + rdy_i => path_a_rdy, + dat_o => path_a_dat + ); + e_path_b: entity work.stage_reg + generic map (WIDTH => 8) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => split_b_stb, + rdy_o => split_b_rdy, + dat_i => split_dat, + + stb_o => path_b_stb, + rdy_i => path_b_rdy, + dat_o => path_b_dat + ); + + + e_mux: entity work.stage_mux2 + generic map (WIDTH => 8) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + sel => sel, + + stb_0_i => path_a_stb, + rdy_0_o => path_a_rdy, + dat_0_i => path_a_dat, + + stb_1_i => path_b_stb, + rdy_1_o => path_b_rdy, + dat_1_i => path_b_dat, + + stb_o => out_stb_o, + rdy_i => out_rdy_i, + dat_o => out_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/utility/sync_fifo_1k_16.vhd b/libraries/utility/sync_fifo_1k_16.vhd new file mode 100644 index 0000000..086f01c --- /dev/null +++ b/libraries/utility/sync_fifo_1k_16.vhd @@ -0,0 +1,181 @@ +-------------------------------------------------------------------------------- +-- sync_fifo_1k_16 - cross clock domain FIFO, 1024x16-bit +-- +-- Generics: +-- SYNC_STAGES - number of shift register stages to use when synchronizing +-- +-- Ports: +-- head_rst_i - synchronous reset in head clock domain +-- head_clk_i - clock domain for data insertion +-- head_stb_i - high to trigger insertion of a byte +-- head_rdy_o - high when head can accept bytes +-- head_dat_i - byte to insert into the head +-- tail_rst_i - synchronous reset in tail clock domain +-- tail_clk_i - clock domain for data removal +-- tail_stb_o - high when a byte is available for removal +-- tail_ack_i - high to trigger acknowledgement of current tail byte +-- tail_dat_o - current tail byte when tail_stb_o is asserted +-- +-- The head and tail reset are only separate to prevent duplication of reset +-- synchronization logic. Both ends should always be reset together, although +-- it is acceptable for one end to come out of reset before the other due to +-- differences in clock rates. +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + +library utility; + + +entity sync_fifo_1k_16 is + generic (SYNC_STAGES: positive := 2); + port ( + head_rst_i: in std_logic; + head_clk_i: in std_logic; + head_stb_i: in std_logic; + head_rdy_o: out std_logic; + head_dat_i: in std_logic_vector(15 downto 0); + + tail_rst_i: in std_logic; + tail_clk_i: in std_logic; + tail_stb_o: out std_logic; + tail_ack_i: in std_logic; + tail_dat_o: out std_logic_vector(15 downto 0) + ); +end sync_fifo_1k_16; + + +architecture behavioral of sync_fifo_1k_16 is + + signal head_rst_wait: std_logic; + signal tail_adr_sync: std_logic_vector(9 downto 0); + signal head_adr_reg: std_logic_vector(9 downto 0) := (others => '0'); + signal head_adr_next: std_logic_vector(9 downto 0); + signal head_rdy: std_logic; + signal head_step: std_logic; + signal is_full: std_logic; + + signal tail_rst_wait: std_logic; + signal head_adr_sync: std_logic_vector(9 downto 0); + signal tail_adr_reg: std_logic_vector(9 downto 0) := (others => '0'); + signal tail_adr_inc: std_logic_vector(9 downto 0); + signal tail_adr_next: std_logic_vector(9 downto 0); + signal tail_stb: std_logic; + signal tail_step: std_logic; + signal is_empty: std_logic; + +begin + + -- Head logic + + e_sync_tail_rst: entity utility.sync_sig + generic map (SYNC_STAGES => SYNC_STAGES) + port map ( + clk_i => head_clk_i, + sig_i => tail_rst_i, + sig_o => head_rst_wait + ); + + e_sync_tail: entity utility.sync_vec + generic map (SYNC_STAGES => SYNC_STAGES) + port map ( + clk_i => head_clk_i, + sig_i => std_logic_vector(tail_adr_reg), + sig_o => tail_adr_sync + ); + + is_full <= '1' when std_logic_vector(head_adr_next) = tail_adr_sync or + head_rst_i = '1' or head_rst_wait = '1' + else '0'; + + head_rdy <= not is_full; + head_rdy_o <= head_rdy; + + head_step <= head_stb_i and head_rdy; + + e_head_adr: entity utility.gray_counter + generic map (N => 10) + port map ( + rst_i => head_rst_i, + clk_i => head_clk_i, + ena_i => head_step, + gray => head_adr_reg, + inc => head_adr_next + ); + + + -- Tail logic + + e_sync_head_rst: entity utility.sync_sig + generic map (SYNC_STAGES => SYNC_STAGES) + port map ( + clk_i => tail_clk_i, + sig_i => head_rst_i, + sig_o => tail_rst_wait + ); + + e_sync_head: entity utility.sync_vec + generic map (SYNC_STAGES => SYNC_STAGES) + port map ( + clk_i => tail_clk_i, + sig_i => std_logic_vector(head_adr_reg), + sig_o => head_adr_sync + ); + + is_empty <= '1' when std_logic_vector(tail_adr_reg) = head_adr_sync or + tail_rst_i = '1' or tail_rst_wait = '1' + else '0'; + + tail_stb <= not is_empty; + tail_stb_o <= tail_stb; + + tail_step <= tail_stb and tail_ack_i; + tail_adr_next <= tail_adr_inc when tail_step = '1' else tail_adr_reg; + + e_tail_adr: entity utility.gray_counter + generic map (N => 10) + port map ( + rst_i => tail_rst_i, + clk_i => tail_clk_i, + ena_i => tail_step, + gray => tail_adr_reg, + inc => tail_adr_inc + ); + + + -- FIFO memory + + e_fifo: ramb16_s18_s18 + generic map ( + SIM_COLLISION_CHECK => "GENERATE_X_ONLY" + ) + port map ( + -- Port A is the FIFO head + wea => head_step, + ena => '1', + ssra => '0', + clka => head_clk_i, + addra => std_logic_vector(head_adr_reg), + dia => head_dat_i, + dipa => "00", + doa => open, + dopa => open, + + -- Port B is the FIFO tail + web => '0', + enb => '1', + ssrb => '0', + clkb => tail_clk_i, + addrb => std_logic_vector(tail_adr_next), + dib => x"0000", + dipb => "00", + dob => tail_dat_o, + dopb => open + ); + +end behavioral; diff --git a/libraries/utility/sync_sig.vhd b/libraries/utility/sync_sig.vhd index ae761f0..f43130a 100644 --- a/libraries/utility/sync_sig.vhd +++ b/libraries/utility/sync_sig.vhd @@ -50,7 +50,7 @@ architecture behavioral of sync_sig is begin - process (clk_i, sig_i, shift_reg) + process (rst_i, clk_i, sig_i, shift_reg) begin if rst_i = '1' then shift_reg <= (others => INIT); diff --git a/projects/mp3tape/ifft_control.vhd b/projects/mp3tape/ifft_control.vhd new file mode 100644 index 0000000..0cd2d35 --- /dev/null +++ b/projects/mp3tape/ifft_control.vhd @@ -0,0 +1,172 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + +library work; + + +entity ifft_control is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + -- Loading + stb_i: in std_logic; + rdy_o: out std_logic; + fin_i: in std_logic; + + -- Unloading + stb_o: out std_logic; + rdy_i: in std_logic; + + -- Datapath control connections + load: out std_logic; + unload: out std_logic; + adr_unload: out std_logic_vector(9 downto 0); + + count_en: out std_logic; + count_done: in std_logic; + + use_j: out std_logic; + s_en: out std_logic; + s_wr: out std_logic; + w_read: out std_logic; + + mult_en: out std_logic + ); +end ifft_control; + + +architecture behavioral of ifft_control is + + type state_t is ( + S_LOAD, + S_FETCH_W_J, + S_FETCH_I_MUL, + S_STORE_I, + S_STORE_J, + S_PRE_UNLOAD, + S_UNLOAD + ); + + signal state_reg: state_t; + signal state_next: state_t; + + signal unload_reg: unsigned( 9 downto 0); + signal unload_next: unsigned(10 downto 0); + signal unload_en: std_logic; + signal unload_done: std_logic; + +begin + + process (rst_i, clk_i, state_next) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + state_reg <= S_LOAD; + else + state_reg <= state_next; + end if; + end if; + end process; + + process (rst_i, clk_i, unload_en, unload_next) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + unload_reg <= (others => '0'); + elsif unload_en = '1' then + unload_reg <= unload_next(9 downto 0); + end if; + end if; + end process; + unload_next <= ('0' & unload_reg) + 1; + unload_done <= unload_next(unload_next'high); + adr_unload <= std_logic_vector(unload_reg); + + process (state_reg, count_done, stb_i, rdy_i, fin_i, unload_done, count_done) + begin + state_next <= state_reg; + load <= '0'; + unload <= '0'; + count_en <= '0'; + use_j <= '0'; + s_en <= '0'; + s_wr <= '0'; + w_read <= '0'; + mult_en <= '0'; + + unload_en <= '0'; + + rdy_o <= '0'; + stb_o <= '0'; + + case state_reg is + when S_LOAD => + if stb_i = '1' and fin_i = '1' then + state_next <= S_FETCH_W_J; + end if; + + load <= stb_i; + rdy_o <= '1'; + + when S_PRE_UNLOAD => + state_next <= S_UNLOAD; + + unload <= '1'; -- Signal to datapath + unload_en <= '1'; -- Unload address counter + + when S_UNLOAD => + if rdy_i = '1' then + if unload_done = '1' then + state_next <= S_LOAD; + end if; + + -- Sample data is cleared while unloading, so we can't hold unload active + -- or else we'll load our own zero out if the downstream isn't ready + unload <= '1'; + unload_en <= '1'; + end if; + + stb_o <= '1'; + + when S_FETCH_W_J => + state_next <= S_FETCH_I_MUL; + + use_j <= '1'; + s_en <= '1'; + w_read <= '1'; + + when S_FETCH_I_MUL => + state_next <= S_STORE_I; + + mult_en <= '1'; + s_en <= '1'; + + when S_STORE_I => + state_next <= S_STORE_J; + + s_en <= '1'; + s_wr <= '1'; + + when S_STORE_J => + if count_done = '1' then + state_next <= S_PRE_UNLOAD; + else + state_next <= S_FETCH_W_J; + end if; + + use_j <= '1'; + s_en <= '1'; + s_wr <= '1'; + count_en <= '1'; + + when others => + state_next <= S_LOAD; + end case; + end process; + +end behavioral; diff --git a/projects/mp3tape/ifft_counter.vhd b/projects/mp3tape/ifft_counter.vhd new file mode 100644 index 0000000..86d20fc --- /dev/null +++ b/projects/mp3tape/ifft_counter.vhd @@ -0,0 +1,94 @@ +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 ifft_counter is + generic ( + IDX_WIDTH: positive := 10 + ); + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + en_i: in std_logic; + done_o: out std_logic; + + i_idx: out std_logic_vector(IDX_WIDTH-1 downto 0); + j_idx: out std_logic_vector(IDX_WIDTH-1 downto 0); + w_idx: out std_logic_vector(IDX_WIDTH-2 downto 0) + ); +end ifft_counter; + + +architecture behavioral of ifft_counter is + + -- Indicates stage number + signal group_size_reg: unsigned(IDX_WIDTH-1 downto 0); + signal group_size_next: unsigned(IDX_WIDTH-1 downto 0); + + -- Indicates which group within the stage + signal group_base_reg: unsigned(IDX_WIDTH-1 downto 0); + signal group_base_next: unsigned(IDX_WIDTH downto 0); + signal inc_stage: std_logic; + + -- Indicates which sample within the group + signal n_reg: unsigned(IDX_WIDTH-2 downto 0); + signal n_next: unsigned(IDX_WIDTH-1 downto 0); + signal inc_group: std_logic; + + -- Twiddle factor index + signal w_reg: unsigned(IDX_WIDTH-2 downto 0); + signal w_inc: unsigned(IDX_WIDTH-1 downto 0); + +begin + + n_next <= ('0' & n_reg) + 1; + inc_group <= or_reduce(std_logic_vector(n_next) and std_logic_vector(group_size_reg)); + + group_base_next <= ('0' & group_base_reg) + (group_size_reg & '0'); + inc_stage <= group_base_next(IDX_WIDTH); + + group_size_next <= group_size_reg(IDX_WIDTH-2 downto 0) & group_size_reg(IDX_WIDTH-1); + + done_o <= group_size_next(0) and inc_stage and inc_group; + + g_w_inc: for i in IDX_WIDTH-1 downto 0 generate + w_inc(i) <= group_size_reg(IDX_WIDTH-1 - i); + end generate; + + process (rst_i, clk_i, en_i, inc_group, inc_stage, group_base_next, n_next, group_size_next, w_reg, w_inc) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + group_base_reg <= (others => '0'); + group_size_reg(IDX_WIDTH-1 downto 1) <= (others => '0'); + group_size_reg(0) <= '1'; + n_reg <= (others => '0'); + w_reg <= (others => '0'); + elsif en_i = '1' then + if inc_group = '1' then + -- Will naturally overflow to 0 when done, no need for reset logic + group_base_reg <= group_base_next(IDX_WIDTH-1 downto 0); + n_reg <= (others => '0'); + + if inc_stage = '1' then + group_size_reg <= group_size_next; + end if; + else + n_reg <= n_next(IDX_WIDTH-2 downto 0); + end if; + w_reg <= w_reg + w_inc(IDX_WIDTH-2 downto 0); + end if; + end if; + end process; + + i_idx <= std_logic_vector(group_base_reg + n_reg); + j_idx <= std_logic_vector(group_base_reg + n_reg + group_size_reg); + w_idx <= std_logic_vector(w_reg); + +end behavioral; diff --git a/projects/mp3tape/ifft_datapath.vhd b/projects/mp3tape/ifft_datapath.vhd new file mode 100644 index 0000000..c8d814d --- /dev/null +++ b/projects/mp3tape/ifft_datapath.vhd @@ -0,0 +1,297 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + + +entity ifft_datapath is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + -- Control signals for loading and unloading + load: in std_logic; + adr_load: in std_logic_vector( 9 downto 0); + dat_load: in std_logic_vector(15 downto 0); + + unload: in std_logic; + adr_unload: in std_logic_vector( 9 downto 0); + dat_unload: out std_logic_vector(15 downto 0); + + -- Control signals for managing scratch state + use_j: in std_logic; -- Use the J sample index instead of I + s_en: in std_logic; -- Load sample from scratch, also write if s_wr asserted + s_wr: in std_logic; -- Write sample value to scratch memory + w_read: in std_logic; -- Load twiddle factor + i_idx: in std_logic_vector(9 downto 0); -- Even-group sample index + j_idx: in std_logic_vector(9 downto 0); -- Odd-group sample index + w_idx: in std_logic_vector(8 downto 0); -- Twiddle factor index + + -- Control signals for computation + mult_en: in std_logic + ); +end ifft_datapath; + + +architecture behavioral of ifft_datapath is + + -- Currently looked-up sample, could be I or J + signal s_addr: std_logic_vector( 9 downto 0); + signal s_real: std_logic_vector(15 downto 0); + signal s_imag: std_logic_vector(15 downto 0); + signal s_real_x: std_logic_vector(17 downto 0); + signal s_imag_x: std_logic_vector(17 downto 0); + signal s_real_new: std_logic_vector(15 downto 0); + signal s_imag_new: std_logic_vector(15 downto 0); + + -- Omega root-of-unity twiddle factor + signal w_comp: std_logic_vector(31 downto 0); + signal w_real: std_logic_vector(15 downto 0); + signal w_imag: std_logic_vector(15 downto 0); + signal w_real_x: std_logic_vector(17 downto 0); + signal w_imag_x: std_logic_vector(17 downto 0); + + -- Complex multiplication partial products and result + signal pp_wr_sr: std_logic_vector(35 downto 0); + signal pp_wi_si: std_logic_vector(35 downto 0); + signal pp_wr_si: std_logic_vector(35 downto 0); + signal pp_wi_sr: std_logic_vector(35 downto 0); + signal wj_real: signed(15 downto 0); + signal wj_imag: signed(15 downto 0); + + -- Updated I and J samples + signal i_real_new: std_logic_vector(16 downto 0); + signal i_imag_new: std_logic_vector(16 downto 0); + signal j_real_new: std_logic_vector(16 downto 0); + signal j_imag_new: std_logic_vector(16 downto 0); + + -- Sideloading samples into and out of scratch space + signal side_en: std_logic; + signal side_addr: std_logic_vector( 9 downto 0); + signal addr_rev: std_logic_vector( 9 downto 0); + signal side_samp: std_logic_vector(15 downto 0); + +begin + + -- Add and subtract I and wJ samples to create new samples + i_real_new <= std_logic_vector(signed(s_real(15) & s_real) + wj_real); + i_imag_new <= std_logic_vector(signed(s_imag(15) & s_imag) + wj_imag); + j_real_new <= std_logic_vector(signed(s_real(15) & s_real) - wj_real); + j_imag_new <= std_logic_vector(signed(s_imag(15) & s_imag) - wj_imag); + + + -- Complex multiplication of sample with twiddle factor + -- Samples are integers, twiddle factors are purely fractional (0.16 fixed-point) + s_real_x <= s_real & "00"; + s_imag_x <= s_imag & "00"; + w_real_x <= w_real & "00"; + w_imag_x <= w_imag & "00"; + e_mul_w_real_s_real: mult18x18s + port map ( + A => w_real_x, + B => s_real_x, + C => clk_i, + CE => mult_en, + R => '0', + P => pp_wr_sr + ); + e_mul_w_imag_s_imag: mult18x18s + port map ( + A => w_imag_x, + B => s_imag_x, + C => clk_i, + CE => mult_en, + R => '0', + P => pp_wi_si + ); + e_mul_w_real_s_imag: mult18x18s + port map ( + A => w_real_x, + B => s_imag_x, + C => clk_i, + CE => mult_en, + R => '0', + P => pp_wr_si + ); + e_mul_w_imag_s_real: mult18x18s + port map ( + A => w_imag_x, + B => s_real_x, + C => clk_i, + CE => mult_en, + R => '0', + P => pp_wi_sr + ); + -- Lose some precision to save logic, but no big deal (I hope) + wj_real <= signed(pp_wr_sr(35 downto 20)) - signed(pp_wi_si(35 downto 20)); + wj_imag <= signed(pp_wr_si(35 downto 20)) + signed(pp_wi_sr(35 downto 20)); + + + -- Select sample I or J in scratch space + s_addr <= j_idx when use_j = '1' else i_idx; + --s_real_new <= j_real_new(16 downto 1) when use_j = '1' else i_real_new(16 downto 1); + --s_imag_new <= j_imag_new(16 downto 1) when use_j = '1' else i_imag_new(16 downto 1); + s_real_new <= j_real_new(15 downto 0) when use_j = '1' else i_real_new(15 downto 0); + s_imag_new <= j_imag_new(15 downto 0) when use_j = '1' else i_imag_new(15 downto 0); + + + -- Select data to sideload into port B + -- When loading samples, address bits get reversed to perform even/odd reordering + g_reverse_addr: for i in 0 to 9 generate + addr_rev(i) <= adr_load(9-i); + end generate; + side_en <= load or unload; + side_samp <= dat_load when load = '1' else (others => '0'); + side_addr <= addr_rev when load = '1' else adr_unload; + + + -- Scratch buffer within which to perform iFFT, separate real and imaginary components + e_scratch_real: ramb16_s18_s18 + generic map ( + -- When writing, don't change the previously read value (allows writing xj without clobbering previous xi read) + WRITE_MODE_A => "NO_CHANGE", + + -- When reading and writing, read the old value (allows resetting to zero while unloading transformed values) + WRITE_MODE_B => "READ_FIRST" + ) + port map ( + -- Internal port for algorithm sample access + WEA => s_wr, + ENA => s_en, + SSRA => '0', + CLKA => clk_i, + ADDRA => s_addr, + DIA => s_real_new, + DIPA => (others => '0'), + DOPA => open, + DOA => s_real, + + -- Secondary port for loading, unloading, and clearing samples + WEB => side_en, -- Always writing when sideloading, either loading samples or clearing while unloading + ENB => side_en, + SSRB => '0', + CLKB => clk_i, + ADDRB => side_addr, + DIB => side_samp, + DIPB => (others => '0'), + DOPB => open, + DOB => dat_unload + ); + e_scratch_imag: ramb16_s18_s18 + generic map ( + -- When writing, don't change the previously read value (allows writing xj without clobbering previous xi read) + WRITE_MODE_A => "NO_CHANGE", + + -- When reading and writing, read the old value (allows resetting to zero while unloading transformed values) + WRITE_MODE_B => "READ_FIRST" + ) + port map ( + -- Internal port for algorithm sample access + WEA => s_wr, + ENA => s_en, + SSRA => '0', + CLKA => clk_i, + ADDRA => s_addr, + DIA => s_imag_new, + DIPA => (others => '0'), + DOPA => open, + DOA => s_imag, + + -- Secondary port for loading, unloading, and clearing samples + WEB => side_en, -- Always writing when sideloading, either loading samples or clearing while unloading + ENB => side_en, + SSRB => '0', + CLKB => clk_i, + ADDRB => side_addr, + DIB => (others => '0'), -- Compressed audio is real-valued (cosines) only + DIPB => (others => '0'), + DOPB => open, + DOB => open + ); + + + -- Twiddle factor lookup table + e_twiddle_lut: ramb16_s36 + generic map ( + INIT_00 => x"7fe0057e7fe804b67fef03ed7ff503247ff9025b7ffc01927ffe00c97fff0000", + INIT_01 => x"7f740bc37f860afb7f960a327fa6096a7fb408a17fc107d97fcd07107fd70647", + INIT_02 => x"7eb912007ed411397eef10727f080fab7f200ee37f370e1b7f4c0d537f610c8b", + INIT_03 => x"7db018337dd5176d7df916a77e1c15e17e3e151b7e5e14557e7e138e7e9c12c7", + INIT_04 => x"7c591e567c881d937cb61ccf7ce21c0b7d0e1b467d381a827d6119bd7d8918f8", + INIT_05 => x"7ab524677aee23a67b2522e47b5c22237b9121617bc4209f7bf71fdc7c291f19", + INIT_06 => x"78c62a61790829a3794928e57989282679c727677a0426a77a4125e77a7c2527", + INIT_07 => x"768d304176d82f8677222ecc776b2e1077b32d5477f92c98783f2bdb78832b1e", + INIT_08 => x"740a3603745e354d74b13496750333de7554332675a4326d75f331b4764030fb", + INIT_09 => x"71403ba4719d3af271f93a3f7254398c72ae38d873063824735e376f73b536b9", + INIT_0A => x"6e3041206e9540736efa3fc56f5e3f166fc03e6770223db770823d0770e13c56", + INIT_0B => x"6adb46746b4a45cc6bb745236c23447a6c8e43d06cf843256d6142796dc941cd", + INIT_0C => x"67454b9d67bc4afa68314a5768a549b36919490e698b486969fc47c36a6c471c", + INIT_0D => x"6370509763ee4ffa646b4f5d64e74ebf65624e2065dd4d8066564ce066ce4c3f", + INIT_0E => x"5f5d555f5fe254c96067543260eb539a616e530161f05268627151ce62f15133", + INIT_0F => x"5b0f59f35b9c59635c2858d35cb358425d3d57b05dc6571d5e4f56895ed655f4", + INIT_10 => x"56895e4f571d5dc657b05d3d58425cb358d35c2859635b9c59f35b0f5a815a81", + INIT_11 => x"51ce6271526861f05301616e539a60eb5432606754c95fe2555f5f5d55f45ed6", + INIT_12 => x"4ce066564d8065dd4e2065624ebf64e74f5d646b4ffa63ee50976370513362f1", + INIT_13 => x"47c369fc4869698b490e691949b368a54a5768314afa67bc4b9d67454c3f66ce", + INIT_14 => x"42796d6143256cf843d06c8e447a6c2345236bb745cc6b4a46746adb471c6a6c", + INIT_15 => x"3d0770823db770223e676fc03f166f5e3fc56efa40736e9541206e3041cd6dc9", + INIT_16 => x"376f735e3824730638d872ae398c72543a3f71f93af2719d3ba471403c5670e1", + INIT_17 => x"31b475f3326d75a43326755433de7503349674b1354d745e3603740a36b973b5", + INIT_18 => x"2bdb783f2c9877f92d5477b32e10776b2ecc77222f8676d83041768d30fb7640", + INIT_19 => x"25e77a4126a77a04276779c72826798928e5794929a379082a6178c62b1e7883", + INIT_1A => x"1fdc7bf7209f7bc421617b9122237b5c22e47b2523a67aee24677ab525277a7c", + INIT_1B => x"19bd7d611a827d381b467d0e1c0b7ce21ccf7cb61d937c881e567c591f197c29", + INIT_1C => x"138e7e7e14557e5e151b7e3e15e17e1c16a77df9176d7dd518337db018f87d89", + INIT_1D => x"0d537f4c0e1b7f370ee37f200fab7f0810727eef11397ed412007eb912c77e9c", + INIT_1E => x"07107fcd07d97fc108a17fb4096a7fa60a327f960afb7f860bc37f740c8b7f61", + INIT_1F => x"00c97ffe01927ffc025b7ff903247ff503ed7fef04b67fe8057e7fe006477fd7", + INIT_20 => x"fa827fe0fb4a7fe8fc137feffcdc7ff5fda57ff9fe6e7ffcff377ffe00007fff", + INIT_21 => x"f43d7f74f5057f86f5ce7f96f6967fa6f75f7fb4f8277fc1f8f07fcdf9b97fd7", + INIT_22 => x"ee007eb9eec77ed4ef8e7eeff0557f08f11d7f20f1e57f37f2ad7f4cf3757f61", + INIT_23 => x"e7cd7db0e8937dd5e9597df9ea1f7e1ceae57e3eebab7e5eec727e7eed397e9c", + INIT_24 => x"e1aa7c59e26d7c88e3317cb6e3f57ce2e4ba7d0ee57e7d38e6437d61e7087d89", + INIT_25 => x"db997ab5dc5a7aeedd1c7b25dddd7b5cde9f7b91df617bc4e0247bf7e0e77c29", + INIT_26 => x"d59f78c6d65d7908d71b7949d7da7989d89979c7d9597a04da197a41dad97a7c", + INIT_27 => x"cfbf768dd07a76d8d1347722d1f0776bd2ac77b3d36877f9d425783fd4e27883", + INIT_28 => x"c9fd740acab3745ecb6a74b1cc227503ccda7554cd9375a4ce4c75f3cf057640", + INIT_29 => x"c45c7140c50e719dc5c171f9c6747254c72872aec7dc7306c891735ec94773b5", + INIT_2A => x"bee06e30bf8d6e95c03b6efac0ea6f5ec1996fc0c2497022c2f97082c3aa70e1", + INIT_2B => x"b98c6adbba346b4abadd6bb7bb866c23bc306c8ebcdb6cf8bd876d61be336dc9", + INIT_2C => x"b4636745b50667bcb5a96831b64d68a5b6f26919b797698bb83d69fcb8e46a6c", + INIT_2D => x"af696370b00663eeb0a3646bb14164e7b1e06562b28065ddb3206656b3c166ce", + INIT_2E => x"aaa15f5dab375fe2abce6067ac6660ebacff616ead9861f0ae326271aecd62f1", + INIT_2F => x"a60d5b0fa69d5b9ca72d5c28a7be5cb3a8505d3da8e35dc6a9775e4faa0c5ed6", + INIT_30 => x"a1b15689a23a571da2c357b0a34d5842a3d858d3a4645963a4f159f3a57f5a81", + INIT_31 => x"9d8f51ce9e1052689e9253019f15539a9f995432a01e54c9a0a3555fa12a55f4", + INIT_32 => x"99aa4ce09a234d809a9e4e209b194ebf9b954f5d9c124ffa9c9050979d0f5133", + INIT_33 => x"960447c39675486996e7490e975b49b397cf4a5798444afa98bb4b9d99324c3f", + INIT_34 => x"929f427993084325937243d093dd447a9449452394b645cc952546749594471c", + INIT_35 => x"8f7e3d078fde3db790403e6790a23f1691063fc5916b407391d04120923741cd", + INIT_36 => x"8ca2376f8cfa38248d5238d88dac398c8e073a3f8e633af28ec03ba48f1f3c56", + INIT_37 => x"8a0d31b48a5c326d8aac33268afd33de8b4f34968ba2354d8bf636038c4b36b9", + INIT_38 => x"87c12bdb88072c98884d2d5488952e1088de2ecc89282f868973304189c030fb", + INIT_39 => x"85bf25e785fc26a7863927678677282686b728e586f829a3873a2a61877d2b1e", + INIT_3A => x"84091fdc843c209f846f216184a4222384db22e4851223a6854b246785842527", + INIT_3B => x"829f19bd82c81a8282f21b46831e1c0b834a1ccf83781d9383a71e5683d71f19", + INIT_3C => x"8182138e81a2145581c2151b81e415e1820716a7822b176d82501833827718f8", + INIT_3D => x"80b40d5380c90e1b80e00ee380f80fab81111072812c113981471200816412c7", + INIT_3E => x"80330710803f07d9804c08a1805a096a806a0a32807a0afb808c0bc3809f0c8b", + INIT_3F => x"800200c9800401928007025b800b0324801103ed801804b68020057e80290647" + ) + port map ( + WE => '0', + EN => w_read, + SSR => '0', + CLK => clk_i, + ADDR => w_idx, + DI => (others => '0'), + DIP => (others => '0'), + DOP => open, + DO => w_comp + ); + w_real <= w_comp(31 downto 16); + w_imag <= w_comp(15 downto 0); + +end behavioral; diff --git a/projects/mp3tape/nexys2.vhd b/projects/mp3tape/nexys2.vhd index 09eb77f..a03f122 100644 --- a/projects/mp3tape/nexys2.vhd +++ b/projects/mp3tape/nexys2.vhd @@ -4,6 +4,8 @@ use ieee.std_logic_1164.all; library utility; library dsp; library nexys2_lib; +library i2s; +library work; entity nexys2 is @@ -51,6 +53,7 @@ architecture behavioral of nexys2 is signal dl_stb: std_logic; signal dl_rdy: std_logic; signal dl_dat: std_logic_vector(7 downto 0); + signal ul_stb: std_logic; signal ul_rdy: std_logic; signal ul_dat: std_logic_vector(7 downto 0); @@ -65,34 +68,125 @@ architecture behavioral of nexys2 is signal debug_to_host: std_logic_vector(63 downto 0); signal debug_from_host: std_logic_vector(63 downto 0); + -------------------------------------------------------- -- Audio pipelines - signal audio_dl_stb: std_logic; - signal audio_dl_rdy: std_logic; - signal audio_dl_dat: std_logic_vector(31 downto 0); - signal audio_ul_stb: std_logic; - signal audio_ul_rdy: std_logic; - signal audio_ul_dat: std_logic_vector(31 downto 0); + -- Data streamed from host computer, little-endian 32-bit words + signal host_dl_stb: std_logic; + signal host_dl_rdy: std_logic; + signal host_dl_dat: std_logic_vector(31 downto 0); + + -- Compressed audio data stream + signal comp_stb: std_logic; + signal comp_rdy: std_logic; + signal comp_bin_dat: std_logic_vector(9 downto 0); + signal comp_amp_dat: std_logic_vector(15 downto 0); + signal comp_fin_dat: std_logic; + + -- Decompressed audio + signal decomp_stb: std_logic; + signal decomp_rdy: std_logic; + signal decomp_dat: std_logic_vector(15 downto 0); + + -- Buffered decompressed audio + signal uneffected_stb: std_logic; + signal uneffected_rdy: std_logic; + signal uneffected_dat: std_logic_vector(15 downto 0); + + -- Samples output to DAC + signal dac_stb: std_logic; + signal dac_rdy: std_logic; + signal dac_dat: std_logic_vector(15 downto 0); + + + -------------------------------------------------------- - signal audio_i_stb: std_logic; - signal audio_i_rdy: std_logic; - signal audio_i_dat: std_logic_vector(31 downto 0); + -- Samples input from ADC + signal adc_stb: std_logic; + signal adc_rdy: std_logic; + signal adc_dat: std_logic_vector(31 downto 0); - signal audio_o_stb: std_logic; - signal audio_o_rdy: std_logic; - signal audio_o_dat: std_logic_vector(31 downto 0); + -- Data streamed to host computer + signal host_ul_stb: std_logic; + signal host_ul_rdy: std_logic; + signal host_ul_dat: std_logic_vector(31 downto 0); + + + signal debug: std_logic_vector(7 downto 0); -- Debug only begin seg <= (others => '1'); dp <= '1'; an <= (others => '1'); - Led <= (others => '0'); + Led <= debug; --(others => '0'); + + ---------------------------------------------------------------------------- + -- Deserialization + + e_deser: entity dsp.deserialize + generic map (WIDTH => 8, N => 4) + port map ( + rst_i => rst_50, + clk_i => clk_50, + + stb_i => dl_stb, + rdy_o => dl_rdy, + dat_i => dl_dat, + + stb_o => host_dl_stb, + rdy_i => host_dl_rdy, + dat_o => host_dl_dat + ); + + comp_stb <= host_dl_stb; + host_dl_rdy <= comp_rdy; + comp_bin_dat <= host_dl_dat( 9 downto 0); + comp_amp_dat <= host_dl_dat(31 downto 16); + comp_fin_dat <= host_dl_dat(15); + + ---------------------------------------------------------------------------- + -- Decompression + + e_decomp: entity work.ifft + port map ( + rst_i => rst_50, + clk_i => clk_50, + + stb_i => comp_stb, + rdy_o => comp_rdy, + bin_dat_i => comp_bin_dat, + amp_dat_i => comp_amp_dat, + fin_dat_i => comp_fin_dat, + + stb_o => decomp_stb, + rdy_i => decomp_rdy, + samp_dat_o => decomp_dat + ); + + ---------------------------------------------------------------------------- + -- Drainage FIFO + -- Doesn't cross a clock domain, but it's here so I'm using it + + e_drain: entity utility.sync_fifo_1k_16 + port map ( + head_rst_i => rst_50, + head_clk_i => clk_50, + head_stb_i => decomp_stb, + head_rdy_o => decomp_rdy, + head_dat_i => decomp_dat, + + tail_rst_i => rst_50, + tail_clk_i => clk_50, + tail_stb_o => uneffected_stb, + tail_ack_i => uneffected_rdy, + tail_dat_o => uneffected_dat + ); ---------------------------------------------------------------------------- -- Processing pipeline - e_effect: entity dsp.pcm16_2ch_tapeeffect + e_effect: entity work.pcm16_1ch_tapeeffect port map ( rst_i => rst_50, clk_i => clk_50, @@ -102,18 +196,21 @@ begin en_wow => sw(2), en_noise => sw(3), - stb_i => audio_dl_stb, - rdy_o => audio_dl_rdy, - dat_i => audio_dl_dat, + stb_i => uneffected_stb, + rdy_o => uneffected_rdy, + dat_i => uneffected_dat, + + stb_o => dac_stb, + rdy_i => dac_rdy, + dat_o => dac_dat, - stb_o => audio_o_stb, - rdy_i => audio_o_rdy, - dat_o => audio_o_dat + debug_o => debug ); - audio_ul_stb <= audio_i_stb; - audio_ul_rdy <= audio_i_rdy; - audio_ul_dat <= audio_i_dat; + -- Input to the host from the ADC, just to have a place to put it + host_ul_stb <= adc_stb; + host_ul_dat <= adc_dat; + adc_rdy <= host_ul_rdy; ---------------------------------------------------------------------------- @@ -179,30 +276,15 @@ begin debug_o => debug_from_host ); - e_deser: entity dsp.deserialize - generic map (WIDTH => 8, N => 4) - port map ( - rst_i => rst_50, - clk_i => clk_50, - - stb_i => dl_stb, - rdy_o => dl_rdy, - dat_i => dl_dat, - - stb_o => audio_dl_stb, - rdy_i => audio_dl_rdy, - dat_o => audio_dl_dat - ); - e_ser: entity dsp.serialize generic map (WIDTH => 8, N => 4) port map ( rst_i => rst_50, clk_i => clk_50, - stb_i => audio_ul_stb, - rdy_o => audio_ul_rdy, - dat_i => audio_ul_dat, + stb_i => host_ul_stb, + rdy_o => host_ul_rdy, + dat_i => host_ul_dat, stb_o => ul_stb, rdy_i => ul_rdy, @@ -213,25 +295,27 @@ begin ---------------------------------------------------------------------------- -- I2S interface - e_i2s_pmod: entity dsp.i2s_pmod + e_i2s_pmod: entity i2s.i2s_pmod port map ( rst_50 => rst_50, clk_50 => clk_50, - stb_i => audio_o_stb, - rdy_o => audio_o_rdy, + stb_i => dac_stb, + rdy_o => dac_rdy, l_dat_i => i2s_tx_l_dat, r_dat_i => i2s_tx_r_dat, - stb_o => audio_i_stb, - rdy_i => audio_i_rdy, + stb_o => adc_stb, + rdy_i => adc_rdy, l_dat_o => i2s_rx_l_dat, r_dat_o => i2s_rx_r_dat, pmod => JA ); - i2s_tx_r_dat <= audio_o_dat(31 downto 16) & x"00"; - i2s_tx_l_dat <= audio_o_dat(15 downto 0) & x"00"; - audio_i_dat <= i2s_rx_r_dat(23 downto 8) & i2s_rx_l_dat(23 downto 8); + + i2s_tx_r_dat <= dac_dat & x"00"; + i2s_tx_l_dat <= dac_dat & x"00"; + + adc_dat <= i2s_rx_r_dat(23 downto 8) & i2s_rx_l_dat(23 downto 8); end behavioral; diff --git a/projects/mp3tape/pcm16_1ch_gain.vhd b/projects/mp3tape/pcm16_1ch_gain.vhd new file mode 100644 index 0000000..927ba72 --- /dev/null +++ b/projects/mp3tape/pcm16_1ch_gain.vhd @@ -0,0 +1,69 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + +library dsp; +library pipe; +library work; + + +entity pcm16_1ch_gain is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + gain: 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(15 downto 0); + + stb_o: out std_logic; + rdy_i: in std_logic; + dat_o: out std_logic_vector(15 downto 0) + ); +end pcm16_1ch_gain; + + +architecture behavioral of pcm16_1ch_gain is + + signal samp_in: std_logic_vector(17 downto 0); + signal samp_out: std_logic_vector(35 downto 0); + + signal out_r: std_logic_vector(35 downto 0); + + signal result: std_logic_vector(15 downto 0); + +begin + + -- Sign extend + samp_in <= dat_i(15) & dat_i(15) & dat_i; + + -- Multiply + result <= std_logic_vector(signed(samp_in) * signed(gain)); + + -- Saturate + e_saturate: entity dsp.saturate + generic map (WIDTH_IN => 27, WIDTH_OUT => 16) + port map (dat_i => result(35 downto 9), dat_o => samp_out); + + -- Interstage register and control logic + e_interstage: entity pipe.stage_reg + generic map (WIDTH => 16) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => stb_i, + rdy_o => rdy_o, + dat_i => samp_out, + + stb_o => stb_o, + rdy_i => rdy_i, + dat_o => dat_o + ); + +end behavioral; diff --git a/projects/mp3tape/pcm16_1ch_noise.vhd b/projects/mp3tape/pcm16_1ch_noise.vhd new file mode 100644 index 0000000..95e0b5f --- /dev/null +++ b/projects/mp3tape/pcm16_1ch_noise.vhd @@ -0,0 +1,51 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library pipe; +library dsp; + + +entity pcm16_1ch_noise is + generic ( + SIGFIGS: positive := 10 + ); + port ( + clk_i: in std_logic; + + en_i: in std_logic; + + stb_o: out std_logic; + rdy_i: in std_logic; + dat_o: out std_logic_vector(15 downto 0) + ); +end pcm16_1ch_noise; + + +architecture behavioral of pcm16_1ch_noise is + + constant X: std_logic_vector(15 downto 0) := x"0001"; + + signal noise_dat: std_logic_vector(SIGFIGS-1 downto 0); + +begin + + g_bits: for i in SIGFIGS-1 downto 0 generate + e_lfsr: entity dsp.lfsr + generic map ( + -- First part guarantees at least one bit set to prevent stuck-at-zero bits + -- Second part is an attempt at pseudorandom initial state per-bit + INIT => std_logic_vector(rotate_left(unsigned(X), i)) or std_logic_vector(to_unsigned(i*13, 16) xor x"a5c3") + ) + port map ( + clk_i => clk_i, + en_i => rdy_i, + dat_o => noise_dat(i) + ); + end generate; + + stb_o <= '1'; + + dat_o <= std_logic_vector(resize(signed(noise_dat), 16)) when en_i = '1' else (others => '0'); + +end behavioral; diff --git a/projects/mp3tape/pcm16_1ch_sum.vhd b/projects/mp3tape/pcm16_1ch_sum.vhd new file mode 100644 index 0000000..6464ac6 --- /dev/null +++ b/projects/mp3tape/pcm16_1ch_sum.vhd @@ -0,0 +1,65 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library dsp; +library pipe; +library work; + + +entity pcm16_1ch_sum is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + a_stb_i: in std_logic; + a_rdy_o: out std_logic; + a_dat_i: in std_logic_vector(15 downto 0); + + b_stb_i: in std_logic; + b_rdy_o: out std_logic; + b_dat_i: in std_logic_vector(15 downto 0); + + stb_o: out std_logic; + rdy_i: in std_logic; + dat_o: out std_logic_vector(15 downto 0) + ); +end pcm16_1ch_sum; + + +architecture behavioral of pcm16_1ch_sum is + + -- Extra bit for carry + signal result: signed(16 downto 0); + + -- Saturated results + signal sat: std_logic_vector(15 downto 0); + +begin + + result <= signed(a_dat_i(15) & a_dat_i) + signed((b_dat_i(15)) & b_dat_i); + + e_sat: entity dsp.saturate + generic map (WIDTH_IN => 17, WIDTH_OUT => 16) + port map (dat_i => std_logic_vector(result), dat_o => sat); + + e_ctrl: entity pipe.stage_merge + generic map (WIDTH => 16) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + a_stb_i => a_stb_i, + a_rdy_o => a_rdy_o, + + b_stb_i => b_stb_i, + b_rdy_o => b_rdy_o, + + dat_i => sat, + + stb_o => stb_o, + rdy_i => rdy_i, + dat_o => dat_o + ); + +end behavioral; diff --git a/projects/mp3tape/pcm16_1ch_tapeeffect.vhd b/projects/mp3tape/pcm16_1ch_tapeeffect.vhd new file mode 100644 index 0000000..6dce38e --- /dev/null +++ b/projects/mp3tape/pcm16_1ch_tapeeffect.vhd @@ -0,0 +1,236 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library pipe; +library work; + + +entity pcm16_1ch_tapeeffect is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + en_rolloff: in std_logic; + en_flutter: in std_logic; + en_wow: in std_logic; + en_noise: in std_logic; + + stb_i: in std_logic; + rdy_o: out std_logic; + dat_i: in std_logic_vector(15 downto 0); + + stb_o: out std_logic; + rdy_i: in std_logic; + dat_o: out std_logic_vector(15 downto 0); + + debug_o: out std_logic_vector(7 downto 0) + ); +end pcm16_1ch_tapeeffect; + + +architecture behavioral of pcm16_1ch_tapeeffect is + + -- Stage 1 - High frequency rolloff + + signal split_a_stb: std_logic; + signal split_a_rdy: std_logic; + signal split_b_stb: std_logic; + signal split_b_rdy: std_logic; + signal split_dat: std_logic_vector(15 downto 0); + signal split_dat_reg: std_logic_vector(15 downto 0); + + signal filter_stb: std_logic; + signal filter_rdy: std_logic; + signal filter_dat: std_logic_vector(15 downto 0); + + signal bypass_stb: std_logic; + signal bypass_rdy: std_logic; + signal bypass_dat: std_logic_vector(15 downto 0); + + signal merge_stb: std_logic; + signal merge_rdy: std_logic; + signal merge_dat: std_logic_vector(15 downto 0); + + signal stage1_stb: std_logic; + signal stage1_rdy: std_logic; + signal stage1_dat: std_logic_vector(15 downto 0); + + -- Stage 2 - Wow and flutter + + constant WOW_WIDTH: integer := 9; + + constant FLUTTER_WIDTH: integer := 9; + constant FLUTTER_INC: std_logic_vector(15 downto 0) := "00000000"&"00100000"; + signal flutter_stb: std_logic; + signal flutter_rdy: std_logic; + signal flutter_dat: std_logic_vector(FLUTTER_WIDTH-1 downto 0); + + signal delay_dat: std_logic_vector(9 downto 0); + + signal stage2_stb: std_logic; + signal stage2_rdy: std_logic; + signal stage2_dat: std_logic_vector(15 downto 0); + + -- Stage 3 - Hiss + + signal noise_stb: std_logic; + signal noise_rdy: std_logic; + signal noise_dat: std_logic_vector(15 downto 0); + +begin + + ---------------------------------------------------------------------------- + -- Low pass filter the audio stream + + e_split: entity pipe.stage_split + generic map (WIDTH => 16) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => stb_i, + rdy_o => rdy_o, + dat_i => dat_i, + + a_stb_o => split_a_stb, + a_rdy_i => split_a_rdy, + + b_stb_o => split_b_stb, + b_rdy_i => split_b_rdy, + + dat_o => split_dat + ); + + e_filter: entity work.pcm16_1ch_windowsum + generic map (WINDOW => 16) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => split_a_stb, + rdy_o => split_a_rdy, + dat_i => split_dat, + + stb_o => filter_stb, + rdy_i => filter_rdy, + dat_o => filter_dat + ); + e_bypass: entity pipe.stage_reg + generic map (WIDTH => 16) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => split_b_stb, + rdy_o => split_b_rdy, + dat_i => split_dat, + + stb_o => bypass_stb, + rdy_i => bypass_rdy, + dat_o => bypass_dat + ); + + e_filter_mux: entity pipe.stage_mux2 + generic map (WIDTH => 16) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + sel => en_rolloff, + + stb_0_i => bypass_stb, + rdy_0_o => bypass_rdy, + dat_0_i => bypass_dat, + + stb_1_i => filter_stb, + rdy_1_o => filter_rdy, + dat_1_i => filter_dat, + + stb_o => stage1_stb, + rdy_i => stage1_rdy, + dat_o => stage1_dat + ); + + ---------------------------------------------------------------------------- + -- Variable delay for wow and flutter + + stage2_stb <= stage1_stb; + stage1_rdy <= stage2_rdy; + stage2_dat <= stage1_dat; + + --e_wowgen: entity dsp.src_fracstep + -- generic map (WIDTH_OUT => 10) + -- port map ( + -- rst_i => rst_i, + -- clk_i => clk_i, + + -- inc_i => std_logic_vector(unsigned(FLUTTER_INC)), + + -- stb_o => flutter_stb, + -- rdy_i => flutter_rdy, + -- dat_o => flutter_dat + -- ); + + --e_sintab: entity dsp.table_sine_1k_16 + -- port map ( + -- en => + + -- adr_i => flutter_dat, + -- dat_o => + -- ); + + --delay_dat <= std_logic_vector(resize(unsigned(flutter_dat), 10)) when en_flutter = '1' else (others => '0'); + + --e_vardelay: entity work.pcm16_1ch_vardelay + -- port map ( + -- rst_i => rst_i, + -- clk_i => clk_i, + + -- delay_stb_i => '1', + -- delay_rdy_o => open, + -- delay_dat_i => (others => '0'), + + -- data_stb_i => stage1_stb, + -- data_rdy_o => stage1_rdy, + -- data_dat_i => stage1_dat, + + -- stb_o => stage2_stb, + -- rdy_i => stage2_rdy, + -- dat_o => stage2_dat + -- ); + + ---------------------------------------------------------------------------- + -- Add noise stream to audio stream + + e_noise: entity work.pcm16_1ch_noise + generic map (SIGFIGS => 10) + port map ( + clk_i => clk_i, + + en_i => en_noise, + + stb_o => noise_stb, + rdy_i => noise_rdy, + dat_o => noise_dat + ); + + e_sum: entity work.pcm16_1ch_sum + port map ( + rst_i => rst_i, + clk_i => clk_i, + + a_stb_i => stage2_stb, + a_rdy_o => stage2_rdy, + a_dat_i => stage2_dat, + + b_stb_i => noise_stb, + b_rdy_o => noise_rdy, + b_dat_i => noise_dat, + + stb_o => stb_o, + rdy_i => rdy_i, + dat_o => dat_o + ); + +end behavioral; diff --git a/projects/mp3tape/pcm16_1ch_vardelay.vhd b/projects/mp3tape/pcm16_1ch_vardelay.vhd new file mode 100644 index 0000000..8ded750 --- /dev/null +++ b/projects/mp3tape/pcm16_1ch_vardelay.vhd @@ -0,0 +1,101 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + +library dsp; +library pipe; +library work; + + +entity pcm16_1ch_vardelay is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + delay_stb_i: in std_logic; + delay_rdy_o: out std_logic; + delay_dat_i: in std_logic_vector(9 downto 0); + + data_stb_i: in std_logic; + data_rdy_o: out std_logic; + data_dat_i: in std_logic_vector(15 downto 0); + + stb_o: out std_logic; + rdy_i: in std_logic; + dat_o: out std_logic_vector(15 downto 0) + ); +end pcm16_1ch_vardelay; + + +architecture behavioral of pcm16_1ch_vardelay is + + constant COUNT_WIDTH: integer := 10; + + signal en: std_logic; + + signal ptr_head_reg: unsigned(COUNT_WIDTH-1 downto 0) := (others => '0'); + signal ptr_tail: unsigned(COUNT_WIDTH-1 downto 0); + +begin + + e_ctrl: entity pipe.ctrl_merge + port map ( + rst_i => rst_i, + clk_i => clk_i, + + a_stb_i => delay_stb_i, + a_rdy_o => delay_rdy_o, + + b_stb_i => data_stb_i, + b_rdy_o => data_rdy_o, + + stb_o => stb_o, + rdy_i => rdy_i, + + en_o => en + ); + + process (rst_i, clk_i, en, ptr_head_reg) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + -- Probably don't need reset logic + elsif en = '1' then + ptr_head_reg <= ptr_head_reg + 1; + end if; + end if; + end process; + + ptr_tail <= (ptr_head_reg - unsigned(delay_dat_i)) - 1; + + e_fifo: ramb16_s18_s18 + port map ( + -- Head port (insertion) + WEA => '1', + ENA => en, + SSRA => '0', + CLKA => clk_i, + ADDRA => std_logic_vector(ptr_head_reg), + DIA => data_dat_i, + DIPA => (others => '0'), + + DOA => open, + DOPA => open, + + -- Tail port (removal) + WEB => '0', + ENB => en, + SSRB => '0', + CLKB => clk_i, + ADDRB => std_logic_vector(ptr_tail), + DIB => (others => '0'), + DIPB => (others => '0'), + + DOB => dat_o, + DOPB => open + ); + +end behavioral; diff --git a/projects/mp3tape/pcm16_1ch_windowsum.vhd b/projects/mp3tape/pcm16_1ch_windowsum.vhd new file mode 100644 index 0000000..bbef579 --- /dev/null +++ b/projects/mp3tape/pcm16_1ch_windowsum.vhd @@ -0,0 +1,69 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.math_real.all; + +library dsp; +library pipe; +library work; + + +entity pcm16_1ch_windowsum is + generic ( + WINDOW: 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(15 downto 0); + + stb_o: out std_logic; + rdy_i: in std_logic; + dat_o: out std_logic_vector(15 downto 0) + ); +end pcm16_1ch_windowsum; + + +architecture behavioral of pcm16_1ch_windowsum is + + constant FILT_WIDTH: positive := 16+integer(ceil(log2(real(WINDOW)))); + + signal en: std_logic; + signal filt: std_logic_vector(FILT_WIDTH-1 downto 0); + +begin + + e_ctrl: entity pipe.ctrl + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => stb_i, + rdy_o => rdy_o, + + stb_o => stb_o, + rdy_i => rdy_i, + + en_o => en + ); + + e_filter: entity dsp.filter_windowsum + generic map ( + WIDTH => 16, + WINDOW => WINDOW + ) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + en_i => en, + + dat_i => dat_i, + dat_o => filt + ); + + dat_o <= filt(FILT_WIDTH-1 downto FILT_WIDTH-16); + +end behavioral; diff --git a/projects/mp3tape/pcm16_2ch_gain.vhd b/projects/mp3tape/pcm16_2ch_gain.vhd new file mode 100644 index 0000000..f25faca --- /dev/null +++ b/projects/mp3tape/pcm16_2ch_gain.vhd @@ -0,0 +1,77 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + +library dsp; +library work; + + +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 <= dat_i(15) & dat_i(15) & dat_i(15 downto 0); + samp_r <= dat_i(31) & dat_i(31) & 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)); + + e_sat_l: entity dsp.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 dsp.saturate + generic map (WIDTH_IN => 27, WIDTH_OUT => 16) + port map (dat_i => out_r(35 downto 9), dat_o => result_r); + + result <= result_r & result_l; + + 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/projects/mp3tape/pcm16_2ch_noise.vhd b/projects/mp3tape/pcm16_2ch_noise.vhd new file mode 100644 index 0000000..0fde6cb --- /dev/null +++ b/projects/mp3tape/pcm16_2ch_noise.vhd @@ -0,0 +1,51 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library dsp; + + +entity pcm16_2ch_noise is + generic ( + SIGFIGS: positive := 10 + ); + port ( + clk_i: in std_logic; + + en_i: in std_logic; + + stb_o: out std_logic; + rdy_i: in std_logic; + dat_o: out std_logic_vector(31 downto 0) + ); +end pcm16_2ch_noise; + + +architecture behavioral of pcm16_2ch_noise is + + constant X: std_logic_vector(15 downto 0) := x"0001"; + + signal noise_dat: std_logic_vector(SIGFIGS-1 downto 0); + +begin + + g_bits: for i in SIGFIGS-1 downto 0 generate + e_lfsr: entity dsp.lfsr + generic map ( + -- First part guarantees at least one bit set + -- Second part is an attempt at pseudorandomness + INIT => std_logic_vector(rotate_left(unsigned(X), i)) or std_logic_vector(to_unsigned(i*13, 16) xor x"a5c3") + ) + port map ( + clk_i => clk_i, + en_i => rdy_i, + dat_o => noise_dat(i) + ); + end generate; + + stb_o <= '1'; + + dat_o <= std_logic_vector(resize(signed(noise_dat), 16)) & + std_logic_vector(resize(signed(noise_dat), 16)) when en_i = '1' else (others => '0'); + +end behavioral; diff --git a/projects/mp3tape/pcm16_2ch_sum.vhd b/projects/mp3tape/pcm16_2ch_sum.vhd new file mode 100644 index 0000000..031b697 --- /dev/null +++ b/projects/mp3tape/pcm16_2ch_sum.vhd @@ -0,0 +1,90 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library dsp; +library pipe; +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_o: 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 <= signed(a_dat_i(15 downto 0)); + samp_a_r <= signed(a_dat_i(31 downto 16)); + samp_b_l <= signed(b_dat_i(15 downto 0)); + samp_b_r <= signed(b_dat_i(31 downto 16)); + + result_l <= (samp_a_l(15) & samp_a_l) + ((samp_b_l(15)) & samp_b_l); + result_r <= (samp_a_r(15) & samp_a_r) + ((samp_b_r(15)) & samp_b_r); + + e_sat_l: entity dsp.saturate + generic map (WIDTH_IN => 17, WIDTH_OUT => 16) + port map (dat_i => std_logic_vector(result_l), dat_o => sat_l); + + e_sat_r: entity dsp.saturate + generic map (WIDTH_IN => 17, WIDTH_OUT => 16) + port map (dat_i => std_logic_vector(result_r), dat_o => sat_r); + + result <= sat_r & sat_l; + + e_ctrl: entity pipe.stage_merge + generic map (WIDTH => 32) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + a_stb_i => a_stb_i, + a_rdy_o => a_rdy_o, + + b_stb_i => b_stb_i, + b_rdy_o => b_rdy_o, + + dat_i => result, + + stb_o => stb_o, + rdy_i => rdy_i, + dat_o => dat_o + ); + +end behavioral; diff --git a/projects/mp3tape/pcm16_2ch_tapeeffect.vhd b/projects/mp3tape/pcm16_2ch_tapeeffect.vhd new file mode 100644 index 0000000..033d9b3 --- /dev/null +++ b/projects/mp3tape/pcm16_2ch_tapeeffect.vhd @@ -0,0 +1,227 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library pipe; +library work; + + +entity pcm16_2ch_tapeeffect is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + en_rolloff: in std_logic; + en_flutter: in std_logic; + en_wow: in std_logic; + en_noise: in std_logic; + + 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); + + debug_o: out std_logic_vector(7 downto 0) + ); +end pcm16_2ch_tapeeffect; + + +architecture behavioral of pcm16_2ch_tapeeffect is + + -- Stage 1 - High frequency rolloff + + signal split_a_stb: std_logic; + signal split_a_rdy: std_logic; + signal split_b_stb: std_logic; + signal split_b_rdy: std_logic; + signal split_dat: std_logic_vector(31 downto 0); + signal split_dat_reg: std_logic_vector(31 downto 0); + + signal filter_stb: std_logic; + signal filter_rdy: std_logic; + signal filter_dat: std_logic_vector(31 downto 0); + + signal bypass_stb: std_logic; + signal bypass_rdy: std_logic; + signal bypass_dat: std_logic_vector(31 downto 0); + + signal merge_stb: std_logic; + signal merge_rdy: std_logic; + signal merge_dat: std_logic_vector(31 downto 0); + + signal stage1_stb: std_logic; + signal stage1_rdy: std_logic; + signal stage1_dat: std_logic_vector(31 downto 0); + + -- Stage 2 - Wow and flutter + + constant FLUTTER_WIDTH: integer := 9; + constant FLUTTER_INC: std_logic_vector(15 downto 0) := "00000000"&"00100000"; + signal flutter_stb: std_logic; + signal flutter_rdy: std_logic; + signal flutter_dat: std_logic_vector(FLUTTER_WIDTH-1 downto 0); + + signal delay_dat: std_logic_vector(9 downto 0); + + signal stage2_stb: std_logic; + signal stage2_rdy: std_logic; + signal stage2_dat: std_logic_vector(31 downto 0); + + -- Stage 3 - Hiss + + signal noise_stb: std_logic; + signal noise_rdy: std_logic; + signal noise_raw: std_logic_vector( 9 downto 0); + signal noise_dat: std_logic_vector(31 downto 0); + +begin + + ---------------------------------------------------------------------------- + -- Low pass filter the audio stream + + e_split: entity pipe.stage_split + generic map (WIDTH => 32) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => stb_i, + rdy_o => rdy_o, + dat_i => dat_i, + + a_stb_o => split_a_stb, + a_rdy_i => split_a_rdy, + + b_stb_o => split_b_stb, + b_rdy_i => split_b_rdy, + + dat_o => split_dat + ); + + e_filter: entity work.pcm16_2ch_windowsum + generic map (WINDOW => 16) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => split_a_stb, + rdy_o => split_a_rdy, + dat_i => split_dat, + + stb_o => filter_stb, + rdy_i => filter_rdy, + dat_o => filter_dat + ); + e_bypass: entity pipe.stage_reg + generic map (WIDTH => 32) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => split_b_stb, + rdy_o => split_b_rdy, + dat_i => split_dat, + + stb_o => bypass_stb, + rdy_i => bypass_rdy, + dat_o => bypass_dat + ); + + e_filter_mux: entity pipe.stage_mux2 + generic map (WIDTH => 32) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + sel => en_rolloff, + + stb_0_i => bypass_stb, + rdy_0_o => bypass_rdy, + dat_0_i => bypass_dat, + + stb_1_i => filter_stb, + rdy_1_o => filter_rdy, + dat_1_i => filter_dat, + + stb_o => stage1_stb, + rdy_i => stage1_rdy, + dat_o => stage1_dat + ); + + ---------------------------------------------------------------------------- + -- Variable delay for wow and flutter + + stage2_stb <= stage1_stb; + stage1_rdy <= stage2_rdy; + stage2_dat <= stage1_dat; + + --e_flutter: entity dsp.src_fracstep + -- generic map (WIDTH_OUT => FLUTTER_WIDTH) + -- port map ( + -- rst_i => rst_i, + -- clk_i => clk_i, + + -- inc_i => std_logic_vector(unsigned(FLUTTER_INC)), + + -- stb_o => flutter_stb, + -- rdy_i => flutter_rdy, + -- dat_o => flutter_dat + -- ); + + --delay_dat <= std_logic_vector(resize(unsigned(flutter_dat), 10)) when en_flutter = '1' else (others => '0'); + + --e_vardelay: entity work.pcm16_2ch_vardelay + -- port map ( + -- rst_i => rst_i, + -- clk_i => clk_i, + + -- delay_stb_i => flutter_stb, + -- delay_rdy_o => flutter_rdy, + -- delay_dat_i => delay_dat, + + -- audio_stb_i => stage1_stb, + -- audio_rdy_o => stage1_rdy, + -- audio_dat_i => stage1_dat, + + -- stb_o => stage2_stb, + -- rdy_i => stage2_rdy, + -- dat_o => stage2_dat + -- ); + + ---------------------------------------------------------------------------- + -- Add noise stream to audio stream + + e_noise: entity work.pcm16_2ch_noise + generic map (SIGFIGS => 10) + port map ( + clk_i => clk_i, + + en_i => en_noise, + + stb_o => noise_stb, + rdy_i => noise_rdy, + dat_o => noise_dat + ); + + e_sum: entity work.pcm16_2ch_sum + port map ( + rst_i => rst_i, + clk_i => clk_i, + + a_stb_i => stage2_stb, + a_rdy_o => stage2_rdy, + a_dat_i => stage2_dat, + + b_stb_i => noise_stb, + b_rdy_o => noise_rdy, + b_dat_i => noise_dat, + + stb_o => stb_o, + rdy_i => rdy_i, + dat_o => dat_o + ); + +end behavioral; diff --git a/projects/mp3tape/pcm16_2ch_vardelay.vhd b/projects/mp3tape/pcm16_2ch_vardelay.vhd new file mode 100644 index 0000000..0f4c2d9 --- /dev/null +++ b/projects/mp3tape/pcm16_2ch_vardelay.vhd @@ -0,0 +1,147 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library unisim; +use unisim.vcomponents.all; + +library dsp; +library work; + + +entity pcm16_2ch_vardelay is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + delay_stb_i: in std_logic; + delay_rdy_o: out std_logic; + delay_dat_i: in std_logic_vector(9 downto 0); + + audio_stb_i: in std_logic; + audio_rdy_o: out std_logic; + audio_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_vardelay; + + +architecture behavioral of pcm16_2ch_vardelay is + + constant COUNT_WIDTH: integer := 10; + + signal en: std_logic; + signal stb: std_logic; + signal rdy: std_logic; + + signal samp_l: std_logic_vector(15 downto 0); + signal samp_r: std_logic_vector(15 downto 0); + + signal result_l: std_logic_vector(15 downto 0); + signal result_r: std_logic_vector(15 downto 0); + + signal ptr_head_reg: unsigned(COUNT_WIDTH-1 downto 0) := (others => '0'); + signal ptr_tail: unsigned(COUNT_WIDTH-1 downto 0); + +begin + + e_merge: entity dsp.merge + port map ( + a_stb_i => delay_stb_i, + a_rdy_o => delay_rdy_o, + b_stb_i => audio_stb_i, + b_rdy_o => audio_rdy_o, + stb_o => stb, + rdy_i => rdy + ); + + e_ctrl: entity dsp.pipectrl + generic map (WIDTH => 0) + port map ( + rst_i => rst_i, + clk_i => clk_i, + en_o => en, + stb_i => stb, + rdy_o => rdy, + dat_i => open, + stb_o => stb_o, + rdy_i => rdy_i, + dat_o => open + ); + + process (rst_i, clk_i, en, ptr_head_reg) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + -- Probably don't need reset logic + elsif en = '1' then + ptr_head_reg <= ptr_head_reg + 1; + end if; + end if; + end process; + + samp_l <= audio_dat_i(15 downto 0); + samp_r <= audio_dat_i(31 downto 16); + + ptr_tail <= (ptr_head_reg - unsigned(delay_dat_i)) - 1; + + e_fifo_l: ramb16_s18_s18 + port map ( + -- Head port (insertion) + WEA => '1', + ENA => en, + SSRA => '0', + CLKA => clk_i, + ADDRA => std_logic_vector(ptr_head_reg), + DIA => samp_l, + DIPA => (others => '0'), + + DOA => open, + DOPA => open, + + -- Tail port (removal) + WEB => '0', + ENB => en, + SSRB => '0', + CLKB => clk_i, + ADDRB => std_logic_vector(ptr_tail), + DIB => (others => '0'), + DIPB => (others => '0'), + + DOB => result_l, + DOPB => open + ); + + e_fifo_r: ramb16_s18_s18 + port map ( + -- Head port (insertion) + WEA => '1', + ENA => en, + SSRA => '0', + CLKA => clk_i, + ADDRA => std_logic_vector(ptr_head_reg), + DIA => samp_r, + DIPA => (others => '0'), + + DOA => open, + DOPA => open, + + -- Tail port (removal) + WEB => '0', + ENB => en, + SSRB => '0', + CLKB => clk_i, + ADDRB => std_logic_vector(ptr_tail), + DIB => (others => '0'), + DIPB => (others => '0'), + + DOB => result_r, + DOPB => open + ); + + dat_o <= result_r & result_l; + +end behavioral; diff --git a/projects/mp3tape/pcm16_2ch_windowsum.vhd b/projects/mp3tape/pcm16_2ch_windowsum.vhd new file mode 100644 index 0000000..59f3b9e --- /dev/null +++ b/projects/mp3tape/pcm16_2ch_windowsum.vhd @@ -0,0 +1,98 @@ +library ieee; +use ieee.std_logic_1164.all; +use ieee.math_real.all; + +library dsp; +library pipe; +library work; + + +entity pcm16_2ch_windowsum is + generic ( + WINDOW: 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(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_windowsum; + + +architecture behavioral of pcm16_2ch_windowsum is + + constant FILT_WIDTH: positive := 16+integer(ceil(log2(real(WINDOW)))); + + signal en: std_logic; + + signal samp_l: std_logic_vector(15 downto 0); + signal samp_r: std_logic_vector(15 downto 0); + + signal filt_l: std_logic_vector(FILT_WIDTH-1 downto 0); + signal filt_r: std_logic_vector(FILT_WIDTH-1 downto 0); + + signal result_l: std_logic_vector(15 downto 0); + signal result_r: std_logic_vector(15 downto 0); + +begin + + e_ctrl: entity pipe.ctrl + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => stb_i, + rdy_o => rdy_o, + + stb_o => stb_o, + rdy_i => rdy_i, + + en_o => en + ); + + samp_l <= dat_i(15 downto 0); + samp_r <= dat_i(31 downto 16); + + e_filter_l: entity dsp.filter_windowsum + generic map ( + WIDTH => 16, + WINDOW => WINDOW + ) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + en_i => en, + + dat_i => samp_l, + dat_o => filt_l + ); + + e_filter_r: entity dsp.filter_windowsum + generic map ( + WIDTH => 16, + WINDOW => WINDOW + ) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + en_i => en, + + dat_i => samp_r, + dat_o => filt_r + ); + + result_l <= filt_l(FILT_WIDTH-1 downto FILT_WIDTH-16); + result_r <= filt_r(FILT_WIDTH-1 downto FILT_WIDTH-16); + + dat_o <= result_r & result_l; + +end behavioral; diff --git a/projects/mp3tape/test_tapeeffect.vhd b/projects/mp3tape/test_tapeeffect.vhd index 072d662..1a59142 100644 --- a/projects/mp3tape/test_tapeeffect.vhd +++ b/projects/mp3tape/test_tapeeffect.vhd @@ -2,6 +2,7 @@ library ieee; use ieee.std_logic_1164.all; library dsp; +library work; entity test_tapeeffect is @@ -42,7 +43,7 @@ begin wait; end process; - e_uut: entity dsp.pcm16_2ch_tapeeffect + e_uut: entity work.pcm16_2ch_tapeeffect port map ( rst_i => rst_i, clk_i => clk_i, diff --git a/projects/mp3tape/tests/test_ifft.vhd b/projects/mp3tape/tests/test_ifft.vhd new file mode 100644 index 0000000..5e04c90 --- /dev/null +++ b/projects/mp3tape/tests/test_ifft.vhd @@ -0,0 +1,106 @@ +library ieee; +use ieee.std_logic_1164.all; + + +entity test_ifft is +end test_ifft; + + +architecture behavior of test_ifft is + + constant CLK_I_PERIOD: time := 20 ns; + + signal rst_i: std_logic; + signal clk_i: std_logic; + + signal stb_i: std_logic; + signal rdy_o: std_logic; + signal bin_dat_i: std_logic_vector( 9 downto 0); + signal amp_dat_i: std_logic_vector(15 downto 0); + signal fin_dat_i: std_logic; + + signal stb_o: std_logic; + signal rdy_i: std_logic; + signal samp_dat_o: std_logic_vector(15 downto 0); + + type test_t is ( + T_RESET, + T_BIN_1, + T_BIN_5, + T_COMPUTE, + T_UNLOAD, + T_DONE + ); + + signal test: test_t; + +begin + + p_test: process + begin + -- Initial values + stb_i <= '0'; + rdy_i <= '1'; + + -- Reset + test <= T_RESET; + rst_i <= '1'; + wait for CLK_I_PERIOD*2; + rst_i <= '0'; + wait until falling_edge(clk_i); + + -- Test + test <= T_BIN_1; + bin_dat_i <= "0000000001"; + amp_dat_i <= x"7fff"; + fin_dat_i <= '0'; + stb_i <= '1'; + wait until falling_edge(clk_i); + stb_i <= '0'; + + test <= T_BIN_5; + bin_dat_i <= "0000000101"; + amp_dat_i <= x"07ff"; + fin_dat_i <= '1'; + stb_i <= '1'; + wait until falling_edge(clk_i); + stb_i <= '0'; + + test <= T_COMPUTE; + wait until stb_o = '1'; + + test <= T_UNLOAD; + wait until stb_o = '0'; + + -- Done + test <= T_DONE; + wait; + end process; + + + e_uut: entity work.ifft + port map ( + rst_i => rst_i, + clk_i => clk_i, + + stb_i => stb_i, + rdy_o => rdy_o, + bin_dat_i => bin_dat_i, + amp_dat_i => amp_dat_i, + fin_dat_i => fin_dat_i, + + stb_o => stb_o, + rdy_i => rdy_i, + samp_dat_o => samp_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; \ No newline at end of file diff --git a/projects/mp3tape/tests/test_ifft_counter.vhd b/projects/mp3tape/tests/test_ifft_counter.vhd new file mode 100644 index 0000000..23068f9 --- /dev/null +++ b/projects/mp3tape/tests/test_ifft_counter.vhd @@ -0,0 +1,80 @@ +library ieee; +use ieee.std_logic_1164.all; + +library work; + + +entity test_ifft_counter is +end test_ifft_counter; + + +architecture behavior of test_ifft_counter is + + constant CLK_I_PERIOD: time := 20 ns; + constant IDX_WIDTH: positive := 3; + + signal rst_i: std_logic; + signal clk_i: std_logic; + + signal en_i: std_logic; + signal done_o: std_logic; + + signal i_idx: std_logic_vector(IDX_WIDTH-1 downto 0); + signal j_idx: std_logic_vector(IDX_WIDTH-1 downto 0); + signal w_idx: std_logic_vector(IDX_WIDTH-2 downto 0); + +begin + + p_test: process + begin + -- Initial values + en_i <= '0'; + + -- Reset + rst_i <= '1'; + wait for CLK_I_PERIOD*2; + rst_i <= '0'; + wait until falling_edge(clk_i); + + -- Test + en_i <= '1'; + + wait until done_o = '1'; + wait until rising_edge(clk_i); + en_i <= '0'; + wait for CLK_I_PERIOD*3; + wait until falling_edge(clk_i); + en_i <= '1'; + wait until done_o = '1'; + wait until rising_edge(clk_i); + en_i <= '0'; + + -- Done + wait; + end process; + + + e_uut: entity work.ifft_counter + generic map (IDX_WIDTH => IDX_WIDTH) + port map ( + rst_i => rst_i, + clk_i => clk_i, + + en_i => en_i, + done_o => done_o, + + i_idx => i_idx, + j_idx => j_idx, + w_idx => w_idx + ); + + + 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/projects/mp3tape/tests/test_ifft_datapath.vhd b/projects/mp3tape/tests/test_ifft_datapath.vhd new file mode 100644 index 0000000..b691d0f --- /dev/null +++ b/projects/mp3tape/tests/test_ifft_datapath.vhd @@ -0,0 +1,160 @@ +library ieee; +use ieee.std_logic_1164.all; + +library work; + + +entity test_ifft_datapath is +end test_ifft_datapath; + + +architecture behavior of test_ifft_datapath is + + constant CLK_I_PERIOD: time := 20 ns; + + signal rst_i: std_logic; + signal clk_i: std_logic; + + signal load: std_logic; + signal unload: std_logic; + signal addr: std_logic_vector(9 downto 0); + signal dat_load: std_logic_vector(15 downto 0); + signal dat_unload: std_logic_vector(15 downto 0); + + signal use_j: std_logic; + signal s_en: std_logic; + signal s_wr: std_logic; + signal w_read: std_logic; + signal i_idx: std_logic_vector(9 downto 0); + signal j_idx: std_logic_vector(9 downto 0); + signal w_idx: std_logic_vector(8 downto 0); + signal mult_en: std_logic; + + type test_t is ( + T_RESET, + T_LOAD, + T_LOOKUP, + T_MULT, + T_STORE_XI, + T_STORE_XJ, + T_UNLOAD_XI, + T_UNLOAD_XJ, + T_VERIFY_CLEAR, + T_DONE + ); + + signal test: test_t; + +begin + + p_test: process + begin + -- Initial values + load <= '0'; + unload <= '0'; + use_j <= '0'; + s_en <= '0'; + s_wr <= '0'; + w_read <= '0'; + mult_en <= '0'; + + -- Reset + test <= T_RESET; + rst_i <= '1'; + wait for CLK_I_PERIOD*2; + rst_i <= '0'; + wait until falling_edge(clk_i); + + -- Test + test <= T_LOAD; + load <= '1'; + addr <= "0000110101"; + dat_load <= x"1234"; + wait until falling_edge(clk_i); + addr <= "0000110100"; + dat_load <= x"4321"; + wait until falling_edge(clk_i); + load <= '0'; + dat_load <= (others => '0'); + addr <= (others => '0'); + + test <= T_LOOKUP; + i_idx <= "1010110000"; + j_idx <= "0010110000"; + w_idx <= "000000001"; + use_j <= '1'; + s_en <= '1'; + w_read <= '1'; + wait until falling_edge(clk_i); + use_j <= '0'; + s_en <= '0'; + w_read <= '0'; + + test <= T_MULT; + mult_en <= '1'; + s_en <= '1'; + wait until falling_edge(clk_i); + mult_en <= '0'; + s_en <= '0'; + + test <= T_STORE_XI; + s_en <= '1'; + s_wr <= '1'; + wait until falling_edge(clk_i); + test <= T_STORE_XJ; + use_j <= '1'; + wait until falling_edge(clk_i); + use_j <= '0'; + s_en <= '0'; + s_wr <= '0'; + + test <= T_UNLOAD_XI; + unload <= '1'; + addr <= "1010110000"; + wait until falling_edge(clk_i); + test <= T_UNLOAD_XJ; + addr <= "0010110000"; + wait until falling_edge(clk_i); + + test <= T_VERIFY_CLEAR; + unload <= '1'; + wait until falling_edge(clk_i); + unload <= '0'; + + -- Done + test <= T_DONE; + wait; + end process; + + + e_uut: entity work.ifft_datapath + port map ( + rst_i => rst_i, + clk_i => clk_i, + + load => load, + unload => unload, + addr => addr, + dat_load => dat_load, + dat_unload => dat_unload, + + use_j => use_j, + s_en => s_en, + s_wr => s_wr, + w_read => w_read, + i_idx => i_idx, + j_idx => j_idx, + w_idx => w_idx, + mult_en => mult_en + ); + + + 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/projects/mp3tape/tests/test_nexys2.vhd b/projects/mp3tape/tests/test_nexys2.vhd index 6e28142..8faa000 100644 --- a/projects/mp3tape/tests/test_nexys2.vhd +++ b/projects/mp3tape/tests/test_nexys2.vhd @@ -32,6 +32,10 @@ architecture behavior of test_nexys2 is signal sw: std_logic_vector(7 downto 0); signal JA: std_logic_vector(7 downto 0); + signal file_read: string(1 to 100); + signal is_empty: boolean; + signal file_write: string(1 to 100); + begin p_test: process @@ -85,11 +89,12 @@ begin ); - e_i2s: entity simulated.proto_i2s_tx + e_i2s_pmod: entity simulated.dev_pmod_i2s port map ( - sck => JA(6), - ws => JA(5), - sd => JA(7) + J => JA, + file_read => file_read, + is_empty => is_empty, + file_write => file_write ); diff --git a/projects/mp3tape/tools/jankman.py b/projects/mp3tape/tools/jankman.py index c1129cc..71f3766 100755 --- a/projects/mp3tape/tools/jankman.py +++ b/projects/mp3tape/tools/jankman.py @@ -112,6 +112,8 @@ def do_jankify(f: BinaryIO, g: BinaryIO): if len(block) < 1024: block += [0.0]*(1024-len(block)) x = compress_chunk(block, 0) + last_i, last_p = x[-1] + x[-1] = (last_i | 0x8000, last_p) # Set the end-of-block marker for i, p in x: f_write_u16_le(g, i) f_write_u16_le(g, p) @@ -125,8 +127,11 @@ def do_unjank(f: BinaryIO, g: BinaryIO): for i in range(num_blocks): print(i, num_blocks) # Obnoxious, but less obnoxious than not knowing block = [fft.C(0,0)]*1024 - for i in range(20): + for x in range(20): i = f_chomp_u16_le(f) + if x == 19: + assert i & 0x8000 != 0 # Check for the end of block flag + i &= 0x7fff p = f_chomp_u16_le(f) block[i] = fft.C(p*scale,0) c0 = fft.ifft_iter(block) diff --git a/projects/nexys2_host_controller/host/build.sh b/projects/nexys2_host_controller/host/build.sh index 3709245..0ccdd83 100755 --- a/projects/nexys2_host_controller/host/build.sh +++ b/projects/nexys2_host_controller/host/build.sh @@ -12,3 +12,4 @@ gcc -o digdude digdude.c fileformat.c memctrl.c ec.c $CFLAGS -I $INC -L $LIBDIR gcc -o jtag jtag.c ec.c $CFLAGS -I $INC -L $LIBDIR -ldmgr -ldjtg gcc -o stm stm.c ec.c $CFLAGS -I $INC -L $LIBDIR -ldmgr -ldstm +gcc -o stm_test stm_test.c ec.c $CFLAGS -I $INC -L $LIBDIR -ldmgr -ldstm diff --git a/projects/nexys2_host_controller/host/stm_test.c b/projects/nexys2_host_controller/host/stm_test.c index 74cc75c..25ec2ca 100644 --- a/projects/nexys2_host_controller/host/stm_test.c +++ b/projects/nexys2_host_controller/host/stm_test.c @@ -376,6 +376,16 @@ int main(int argc, char **argv) EC_FALSE(file_emit_wav_header(out_file, &info)); in_len = info.data_block_size; // Make sure we don't download metadata } + else if (!strcmp(format, "jank")) + { + // Jankfile + uint32_t num_blocks; + + EC_FALSE(file_read_u32(in_file, &num_blocks)); + printf("Block count: %" PRIi32 "\n", num_blocks); + in_len = num_blocks * 20 * 4; + out_len = num_blocks * 1024; + } else if (!strcmp(format, "bin")) { // Binary format, nothing to do