|
---------------------------------------------------------------------------
|
|
-- (c) 2020 mark watson
|
|
-- I am happy for anyone to use this for non-commercial use.
|
|
-- If my vhdl files are used commercially or otherwise sold,
|
|
-- please contact me for explicit permission at scrameta (gmail).
|
|
-- This applies for source and binary form and derived works.
|
|
---------------------------------------------------------------------------
|
|
LIBRARY ieee;
|
|
USE ieee.std_logic_1164.all;
|
|
use ieee.numeric_std.all;
|
|
use IEEE.STD_LOGIC_MISC.all;
|
|
|
|
ENTITY sample_adpcm IS
|
|
PORT
|
|
(
|
|
CLK : IN STD_LOGIC;
|
|
RESET_N : IN STD_LOGIC;
|
|
|
|
SYNCRESET : IN STD_LOGIC_VECTOR(3 downto 0); -- reset accumulator/step for next update
|
|
|
|
select_channel : out std_logic_vector(1 downto 0); -- ask for current data for this channel
|
|
|
|
store : OUT std_logic;
|
|
data_out : OUT std_logic_vector(15 downto 0); -- current output
|
|
|
|
dirty : IN STD_LOGIC_VECTOR(3 downto 0); -- channel needs updating
|
|
|
|
data_request : out std_logic;
|
|
data_ready : in std_logic;
|
|
data_in : in std_logic_vector(3 downto 0);
|
|
|
|
STEP_ADDR : out std_logic_vector(6 downto 0); -- ask for step value
|
|
STEP_REQUEST : out std_logic;
|
|
STEP_READY : in std_logic;
|
|
STEP_VALUE : in std_logic_vector(14 downto 0)
|
|
);
|
|
END sample_adpcm;
|
|
|
|
ARCHITECTURE vhdl OF sample_adpcm IS
|
|
|
|
function stepadj_fn(x: std_logic_vector(2 downto 0)) return signed is
|
|
begin
|
|
case x is
|
|
when "000" => return to_signed(-1,5);
|
|
when "001" => return to_signed(-1,5);
|
|
when "010" => return to_signed(-1,5);
|
|
when "011" => return to_signed(-1,5);
|
|
when "100" => return to_signed(2,5);
|
|
when "101" => return to_signed(4,5);
|
|
when "110" => return to_signed(6,5);
|
|
when "111" => return to_signed(8,5);
|
|
end case;
|
|
end stepadj_fn;
|
|
|
|
signal acc0_reg : signed(15 downto 0);
|
|
signal acc0_next : signed(15 downto 0);
|
|
|
|
signal acc1_reg : signed(15 downto 0);
|
|
signal acc1_next : signed(15 downto 0);
|
|
|
|
signal acc2_reg : signed(15 downto 0);
|
|
signal acc2_next : signed(15 downto 0);
|
|
|
|
signal acc3_reg : signed(15 downto 0);
|
|
signal acc3_next : signed(15 downto 0);
|
|
|
|
signal acc_next : signed(15 downto 0);
|
|
signal acc_mux : signed(15 downto 0);
|
|
|
|
signal decstep0_reg : unsigned(6 downto 0);
|
|
signal decstep0_next : unsigned(6 downto 0);
|
|
|
|
signal decstep1_reg : unsigned(6 downto 0);
|
|
signal decstep1_next : unsigned(6 downto 0);
|
|
|
|
signal decstep2_reg : unsigned(6 downto 0);
|
|
signal decstep2_next : unsigned(6 downto 0);
|
|
|
|
signal decstep3_reg : unsigned(6 downto 0);
|
|
signal decstep3_next : unsigned(6 downto 0);
|
|
|
|
signal decstep_next : unsigned(6 downto 0);
|
|
signal decstep_mux : unsigned(6 downto 0);
|
|
|
|
signal write_ch0 : std_logic;
|
|
signal write_ch1 : std_logic;
|
|
signal write_ch2 : std_logic;
|
|
signal write_ch3 : std_logic;
|
|
|
|
signal sel : std_logic_vector(1 downto 0);
|
|
|
|
signal syncreset_next : std_logic_vector(3 downto 0);
|
|
signal syncreset_reg : std_logic_vector(3 downto 0);
|
|
|
|
signal dirty_reg : std_logic_vector(3 downto 0);
|
|
signal dirty_next : std_logic_vector(3 downto 0);
|
|
|
|
signal code_reg : std_logic_vector(3 downto 0);
|
|
signal code_next : std_logic_vector(3 downto 0);
|
|
|
|
signal state_reg : std_logic_vector(2 downto 0);
|
|
signal state_next: std_logic_vector(2 downto 0);
|
|
constant state_ch0_mem_req : std_logic_vector(2 downto 0) := "000";
|
|
constant state_ch0_step_req : std_logic_vector(2 downto 0) := "001";
|
|
constant state_ch1_mem_req : std_logic_vector(2 downto 0) := "010";
|
|
constant state_ch1_step_req : std_logic_vector(2 downto 0) := "011";
|
|
constant state_ch2_mem_req : std_logic_vector(2 downto 0) := "100";
|
|
constant state_ch2_step_req : std_logic_vector(2 downto 0) := "101";
|
|
constant state_ch3_mem_req : std_logic_vector(2 downto 0) := "110";
|
|
constant state_ch3_step_req : std_logic_vector(2 downto 0) := "111";
|
|
BEGIN
|
|
-- register
|
|
process(clk,reset_n)
|
|
begin
|
|
if (reset_n='0') then
|
|
acc0_reg <= (others=>'0');
|
|
acc1_reg <= (others=>'0');
|
|
acc2_reg <= (others=>'0');
|
|
acc3_reg <= (others=>'0');
|
|
decstep0_reg <= (others=>'0');
|
|
decstep1_reg <= (others=>'0');
|
|
decstep2_reg <= (others=>'0');
|
|
decstep3_reg <= (others=>'0');
|
|
syncreset_reg <= (others=>'0');
|
|
dirty_reg <= (others=>'0');
|
|
code_reg <= (others=>'0');
|
|
state_reg <= state_ch0_mem_req;
|
|
elsif (clk'event and clk='1') then
|
|
acc0_reg <= acc0_next;
|
|
acc1_reg <= acc1_next;
|
|
acc2_reg <= acc2_next;
|
|
acc3_reg <= acc3_next;
|
|
decstep0_reg <= decstep0_next;
|
|
decstep1_reg <= decstep1_next;
|
|
decstep2_reg <= decstep2_next;
|
|
decstep3_reg <= decstep3_next;
|
|
syncreset_reg <= syncreset_next;
|
|
dirty_reg <= dirty_next;
|
|
code_reg <= code_next;
|
|
state_reg <= state_next;
|
|
end if;
|
|
end process;
|
|
|
|
process(state_reg, dirty, dirty_reg, code_reg, data_in, data_ready, step_ready)
|
|
begin
|
|
code_next <= code_reg;
|
|
dirty_next <= dirty_reg or dirty;
|
|
state_next <= state_reg;
|
|
|
|
data_request <= '0';
|
|
step_request <= '0';
|
|
sel <= (others=>'0');
|
|
write_ch0 <= '0';
|
|
write_ch1 <= '0';
|
|
write_ch2 <= '0';
|
|
write_ch3 <= '0';
|
|
|
|
store <= step_ready;
|
|
if (data_ready='1') then
|
|
code_next <= data_in;
|
|
end if;
|
|
|
|
case state_reg is
|
|
when state_ch0_mem_req =>
|
|
sel <= "00";
|
|
data_request <= dirty_reg(0);
|
|
if (data_ready='1') then
|
|
code_next <= data_in;
|
|
state_next <= state_ch0_step_req;
|
|
end if;
|
|
if (dirty_reg(0)='0') then
|
|
state_next <= state_ch1_mem_req;
|
|
end if;
|
|
when state_ch0_step_req =>
|
|
step_request <= '1';
|
|
sel <= "00";
|
|
write_ch0 <= step_ready;
|
|
if (step_ready='1') then
|
|
state_next <= state_ch1_mem_req;
|
|
dirty_next(0) <= '0';
|
|
end if;
|
|
|
|
when state_ch1_mem_req =>
|
|
sel <= "01";
|
|
data_request <= dirty_reg(1);
|
|
if (data_ready='1') then
|
|
code_next <= data_in;
|
|
state_next <= state_ch1_step_req;
|
|
end if;
|
|
if (dirty_reg(1)='0') then
|
|
state_next <= state_ch2_mem_req;
|
|
end if;
|
|
when state_ch1_step_req =>
|
|
step_request <= '1';
|
|
sel <= "01";
|
|
write_ch1 <= step_ready;
|
|
if (step_ready='1') then
|
|
state_next <= state_ch2_mem_req;
|
|
dirty_next(1) <= '0';
|
|
end if;
|
|
|
|
when state_ch2_mem_req =>
|
|
sel <= "10";
|
|
data_request <= dirty_reg(2);
|
|
if (data_ready='1') then
|
|
code_next <= data_in;
|
|
state_next <= state_ch2_step_req;
|
|
end if;
|
|
if (dirty_reg(2)='0') then
|
|
state_next <= state_ch3_mem_req;
|
|
end if;
|
|
when state_ch2_step_req =>
|
|
step_request <= '1';
|
|
sel <= "10";
|
|
write_ch2 <= step_ready;
|
|
if (step_ready='1') then
|
|
state_next <= state_ch3_mem_req;
|
|
dirty_next(2) <= '0';
|
|
end if;
|
|
|
|
when state_ch3_mem_req =>
|
|
sel <= "11";
|
|
data_request <= dirty_reg(3);
|
|
if (data_ready='1') then
|
|
code_next <= data_in;
|
|
state_next <= state_ch3_step_req;
|
|
end if;
|
|
if (dirty_reg(3)='0') then
|
|
state_next <= state_ch0_mem_req;
|
|
end if;
|
|
when state_ch3_step_req =>
|
|
step_request <= '1';
|
|
sel <= "11";
|
|
write_ch3 <= step_ready;
|
|
if (step_ready='1') then
|
|
state_next <= state_ch0_mem_req;
|
|
dirty_next(3) <= '0';
|
|
end if;
|
|
when others =>
|
|
state_next <= state_ch0_mem_req;
|
|
end case;
|
|
end process;
|
|
|
|
process(acc0_reg, acc1_reg, acc2_reg, acc3_reg,
|
|
decstep0_reg, decstep1_reg, decstep2_reg, decstep3_reg,
|
|
acc_next, decstep_next,
|
|
write_ch0,write_ch1,write_ch2,write_ch3)
|
|
begin
|
|
acc0_next <= acc0_reg;
|
|
acc1_next <= acc1_reg;
|
|
acc2_next <= acc2_reg;
|
|
acc3_next <= acc3_reg;
|
|
decstep0_next <= decstep0_reg;
|
|
decstep1_next <= decstep1_reg;
|
|
decstep2_next <= decstep2_reg;
|
|
decstep3_next <= decstep3_reg;
|
|
|
|
if (write_ch0='1') then
|
|
acc0_next <= acc_next;
|
|
decstep0_next <= decstep_next;
|
|
end if;
|
|
|
|
if (write_ch1='1') then
|
|
acc1_next <= acc_next;
|
|
decstep1_next <= decstep_next;
|
|
end if;
|
|
|
|
if (write_ch2='1') then
|
|
acc2_next <= acc_next;
|
|
decstep2_next <= decstep_next;
|
|
end if;
|
|
|
|
if (write_ch3='1') then
|
|
acc3_next <= acc_next;
|
|
decstep3_next <= decstep_next;
|
|
end if;
|
|
end process;
|
|
|
|
process(sel,syncreset_reg, syncreset, step_ready,
|
|
acc0_reg, acc1_reg, acc2_reg, acc3_reg,
|
|
decstep0_reg, decstep1_reg, decstep2_reg, decstep3_reg
|
|
)
|
|
variable rst : std_logic;
|
|
begin
|
|
acc_mux <= (others=>'0');
|
|
decstep_mux <= (others=>'0');
|
|
|
|
syncreset_next <= (syncreset or syncreset_reg);
|
|
|
|
rst := '0';
|
|
|
|
case sel is
|
|
when "00" =>
|
|
acc_mux <= acc0_reg;
|
|
decstep_mux <= decstep0_reg;
|
|
rst := syncreset_reg(0) or syncreset(0);
|
|
syncreset_next(0) <= (syncreset_reg(0) or syncreset(0)) and not(step_ready);
|
|
when "01" =>
|
|
acc_mux <= acc1_reg;
|
|
decstep_mux <= decstep1_reg;
|
|
rst := syncreset_reg(1) or syncreset(1);
|
|
syncreset_next(1) <= (syncreset_reg(1) or syncreset(1)) and not(step_ready);
|
|
when "10" =>
|
|
acc_mux <= acc2_reg;
|
|
decstep_mux <= decstep2_reg;
|
|
rst := syncreset_reg(2) or syncreset(2);
|
|
syncreset_next(2) <= (syncreset_reg(2) or syncreset(2)) and not(step_ready);
|
|
when "11" =>
|
|
acc_mux <= acc3_reg;
|
|
decstep_mux <= decstep3_reg;
|
|
rst := syncreset_reg(3) or syncreset(3);
|
|
syncreset_next(3) <= (syncreset_reg(3) or syncreset(3)) and not(step_ready);
|
|
when others =>
|
|
end case;
|
|
|
|
if (rst='1') then
|
|
acc_mux <= (others=>'0');
|
|
decstep_mux <= (others=>'0');
|
|
end if;
|
|
end process;
|
|
|
|
process(acc_mux,decstep_mux,
|
|
code_reg, step_value)
|
|
|
|
variable code : std_logic_vector(3 downto 0);
|
|
variable codeadj : signed(8 downto 0);
|
|
variable stepsize : signed(17 downto 0);
|
|
variable vlue : signed(26 downto 0);
|
|
variable vlue8 : signed(16 downto 0);
|
|
variable decstepnext : signed(7 downto 0);
|
|
variable acc_sum : signed(16 downto 0);
|
|
variable oflow : boolean;
|
|
begin
|
|
acc_next <= acc_mux;
|
|
decstep_next <= decstep_mux;
|
|
|
|
codeadj:= (others=>'0');
|
|
|
|
codeadj := resize(signed('0'&code_reg(2 downto 0)),8)&"1";
|
|
|
|
stepsize := resize(signed('0'&step_value),18);
|
|
|
|
vlue :=codeadj*stepsize;
|
|
|
|
if (code_reg(3)='0') then
|
|
vlue8 := vlue(19 downto 3);
|
|
else
|
|
vlue8 := -vlue(19 downto 3);
|
|
end if;
|
|
|
|
acc_sum := resize(acc_mux,17) + vlue8;
|
|
oflow := acc_sum(16)/=acc_sum(15);
|
|
if (oflow) then
|
|
acc_next <= resize(acc_sum(16 downto 16),16);
|
|
else
|
|
acc_next <= acc_sum(15 downto 0);
|
|
end if;
|
|
|
|
decstepnext := resize(stepadj_fn(code_reg(2 downto 0)),8) + signed(resize(decstep_mux,8));
|
|
if (decstepnext>88) then
|
|
decstepnext := to_signed(88,8);
|
|
elsif (decstepnext<0) then
|
|
decstepnext := to_signed(0,8);
|
|
end if;
|
|
decstep_next <= unsigned(decstepnext(6 downto 0));
|
|
end process;
|
|
|
|
data_out(15) <= not(acc_mux(15));
|
|
data_out(14 downto 0) <= std_logic_vector(acc_mux(14 downto 0));
|
|
|
|
select_channel <= sel;
|
|
step_addr <= std_logic_vector(decstep_mux);
|
|
|
|
end vhdl;
|
|
|