|
---------------------------------------------------------------------------
|
|
-- (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 sigmadelta_2ndorder_dither IS
|
|
GENERIC (
|
|
DITHER_ENABLE : integer := 1; -- 0/1
|
|
DITHER_BITS : integer := 2; -- 1..4; start small
|
|
LFSR_SEED : unsigned(15 downto 0) := x"ACE1"
|
|
);
|
|
PORT
|
|
(
|
|
CLK : IN STD_LOGIC;
|
|
RESET_N : IN STD_LOGIC;
|
|
|
|
ENABLE : IN STD_LOGIC := '1';
|
|
|
|
AUDIN : IN UNSIGNED(15 downto 0);
|
|
AUDOUT : OUT std_logic
|
|
);
|
|
END sigmadelta_2ndorder_dither;
|
|
|
|
architecture vhdl of sigmadelta_2ndorder_dither is
|
|
signal ttl1_reg : signed(21 downto 1) := (others => '0');
|
|
signal ttl2_reg : signed(23 downto 1) := (others => '0');
|
|
signal out_reg : std_logic := '0';
|
|
|
|
signal lfsr : unsigned(15 downto 0) := LFSR_SEED;
|
|
|
|
function sat_add(a, b : signed) return signed is
|
|
variable aw : integer := a'length;
|
|
variable ext : signed(aw downto 0);
|
|
variable r : signed(aw-1 downto 0);
|
|
begin
|
|
ext := resize(a, aw+1) + resize(b, aw+1);
|
|
if ext(aw) /= ext(aw-1) then
|
|
if ext(aw) = '0' then
|
|
r := (others => '1'); r(aw-1) := '0'; -- +max
|
|
else
|
|
r := (others => '0'); r(aw-1) := '1'; -- -min
|
|
end if;
|
|
else
|
|
r := ext(aw-1 downto 0);
|
|
end if;
|
|
return r;
|
|
end function;
|
|
|
|
begin
|
|
|
|
process(CLK, RESET_N)
|
|
variable audinadj : unsigned(16 downto 0);
|
|
variable fb : signed(21 downto 0);
|
|
variable ttl1_tmp : signed(21 downto 1);
|
|
variable ttl2_inc : signed(23 downto 1);
|
|
|
|
-- threshold dither (TPDF-ish): (u1 - u2)
|
|
variable u1, u2 : signed(DITHER_BITS downto 0);
|
|
variable dith23 : signed(23 downto 1);
|
|
|
|
variable q_in : signed(23 downto 1);
|
|
variable out_next : std_logic;
|
|
begin
|
|
if RESET_N = '0' then
|
|
ttl1_reg <= (others => '0');
|
|
ttl2_reg <= (others => '0');
|
|
out_reg <= '0';
|
|
lfsr <= LFSR_SEED;
|
|
elsif rising_edge(CLK) then
|
|
if ENABLE = '1' then
|
|
|
|
-- 16-bit Galois LFSR taps: 16,14,13,11
|
|
lfsr <= lfsr(14 downto 0) & (lfsr(15) xor lfsr(13) xor lfsr(12) xor lfsr(10));
|
|
|
|
-- Keep your original input shaping
|
|
audinadj := resize(AUDIN, 17) + to_unsigned(4096, 17) - resize(AUDIN(15 downto 3), 17);
|
|
|
|
-- Build tiny threshold dither (default ~±1..3 LSB at the *threshold* domain)
|
|
if DITHER_ENABLE = 1 then
|
|
u1 := resize(signed('0' & lfsr(DITHER_BITS-1 downto 0)), DITHER_BITS+1)
|
|
- to_signed(2**(DITHER_BITS-1), DITHER_BITS+1);
|
|
u2 := resize(signed('0' & lfsr(2*DITHER_BITS-1 downto DITHER_BITS)), DITHER_BITS+1)
|
|
- to_signed(2**(DITHER_BITS-1), DITHER_BITS+1);
|
|
dith23 := resize(resize(u1 - u2, 23), 23);
|
|
else
|
|
dith23 := (others => '0');
|
|
end if;
|
|
|
|
-- Quantizer: use full-precision sign, with dither to kill limit cycles
|
|
q_in := sat_add(ttl2_reg, dith23);
|
|
if q_in(q_in'left) = '0' then
|
|
out_next := '1';
|
|
else
|
|
out_next := '0';
|
|
end if;
|
|
|
|
-- Feedback (same style as your original: unipolar bit at 2^16)
|
|
fb := (others => '0');
|
|
fb(16) := out_next;
|
|
|
|
-- Saturating integrators
|
|
ttl1_tmp := sat_add(ttl1_reg, resize(signed("0" & audinadj), 21) - resize(fb(20 downto 0), 21));
|
|
ttl2_inc := resize(
|
|
(ttl1_tmp(20 downto 1) & '0') -
|
|
(resize(fb(20 downto 0), 22) + resize(fb(18 downto 0) & "00", 22)),
|
|
23
|
|
);
|
|
|
|
ttl1_reg <= ttl1_tmp;
|
|
ttl2_reg <= sat_add(ttl2_reg, ttl2_inc);
|
|
out_reg <= out_next;
|
|
|
|
end if;
|
|
end if;
|
|
end process;
|
|
|
|
AUDOUT <= out_reg;
|
|
|
|
end vhdl;
|
|
|