|
---------------------------------------------------------------------------
|
|
-- (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;
|
|
|
|
-- Generic unsigned-input to signed-output DC blocker.
|
|
-- AUDIO_IN is treated as offset-binary unsigned audio:
|
|
-- 0 -> most negative signed value
|
|
-- 2**(BITS-1) -> zero
|
|
-- 2**BITS - 1 -> most positive signed value
|
|
--
|
|
-- Filter:
|
|
-- y = x - dc_old
|
|
-- dc = dc_old + (y / 2**K)
|
|
--
|
|
-- AUDIO_OUT is registered and saturated to BITS bits.
|
|
ENTITY dc_blocker IS
|
|
GENERIC
|
|
(
|
|
BITS : positive := 16;
|
|
EXTRA_BITS : positive := 4;
|
|
K : natural := 10
|
|
);
|
|
PORT
|
|
(
|
|
CLK : IN std_logic;
|
|
RESET_N : IN std_logic;
|
|
ENABLE_CYCLE : IN std_logic;
|
|
|
|
AUDIO_IN : IN unsigned(BITS-1 downto 0);
|
|
AUDIO_OUT : OUT signed(BITS-1 downto 0)
|
|
);
|
|
END dc_blocker;
|
|
|
|
ARCHITECTURE vhdl OF dc_blocker IS
|
|
constant ACC_WIDTH : positive := BITS + EXTRA_BITS;
|
|
|
|
subtype acc_t is signed(ACC_WIDTH-1 downto 0);
|
|
|
|
function midpoint return acc_t is
|
|
variable r : acc_t := (others => '0');
|
|
begin
|
|
r(BITS-1) := '1';
|
|
return r;
|
|
end function;
|
|
|
|
function saturate_to_bits(v : acc_t) return signed is
|
|
variable r : signed(BITS-1 downto 0);
|
|
variable overflow : boolean := false;
|
|
variable max_val : signed(BITS-1 downto 0) := (others => '1');
|
|
variable min_val : signed(BITS-1 downto 0) := (others => '0');
|
|
begin
|
|
max_val(BITS-1) := '0';
|
|
min_val(BITS-1) := '1';
|
|
|
|
-- A value fits into BITS signed bits when all bits above BITS-1
|
|
-- match the sign bit that will remain after truncation.
|
|
for i in BITS to ACC_WIDTH-1 loop
|
|
if v(i) /= v(BITS-1) then
|
|
overflow := true;
|
|
end if;
|
|
end loop;
|
|
|
|
if overflow then
|
|
if v(ACC_WIDTH-1) = '0' then
|
|
r := max_val;
|
|
else
|
|
r := min_val;
|
|
end if;
|
|
else
|
|
r := v(BITS-1 downto 0);
|
|
end if;
|
|
|
|
return r;
|
|
end function;
|
|
|
|
constant MIDPOINT_VALUE : acc_t := midpoint;
|
|
|
|
signal dc_reg : acc_t;
|
|
signal dc_next : acc_t;
|
|
signal audio_out_reg : signed(BITS-1 downto 0);
|
|
signal audio_out_next : signed(BITS-1 downto 0);
|
|
BEGIN
|
|
process(AUDIO_IN, ENABLE_CYCLE, dc_reg)
|
|
variable x_ext : acc_t;
|
|
variable err : acc_t;
|
|
variable adj : acc_t;
|
|
begin
|
|
x_ext := signed(resize(AUDIO_IN, ACC_WIDTH)) - MIDPOINT_VALUE;
|
|
err := x_ext - dc_reg;
|
|
adj := shift_right(err, K);
|
|
|
|
dc_next <= dc_reg;
|
|
audio_out_next <= saturate_to_bits(err);
|
|
|
|
if ENABLE_CYCLE = '1' then
|
|
dc_next <= dc_reg + adj;
|
|
end if;
|
|
end process;
|
|
|
|
process(CLK, RESET_N)
|
|
begin
|
|
if RESET_N = '0' then
|
|
dc_reg <= (others => '0');
|
|
audio_out_reg <= (others => '0');
|
|
elsif CLK'event and CLK = '1' then
|
|
dc_reg <= dc_next;
|
|
audio_out_reg <= audio_out_next;
|
|
end if;
|
|
end process;
|
|
|
|
AUDIO_OUT <= audio_out_reg;
|
|
END vhdl;
|