📄 demod.vhd
字号:
-- demod.vhd QAM demodulator design, intended to illustrate Tcl testbench
--
-- rev 1.0, 03 April 2002, Jonathan Bromley
-- first attempt
--
-- rev 1.1, 17 April 2002, Jonathan Bromley
-- added Costas loop for phase lock, improved resolution of LP filter
-- scaling (although I don't think it's really necessary as the I&D
-- filter does all that's needed).
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity iq_demod is
generic (
if_bits : natural := 4; -- bit width of IF, digitised at Clk rate
baseband_bits : natural := 8; -- bit width of I/Q outputs
-- Numerically controlled oscillator for carrier:
-- f(carrier) = f(Clk) * carrier_incr / (2**NCO_bits)
NCO_bits : positive := 8; -- bit width of NCO phase accumulator
carrier_incr : positive := 32; -- NCO phase increment
-- Output lowpass filter is a crude single-pole thing, no good!
filter_shift : natural := 4; -- left shift to scale output filter
filter_rate : natural := 8 -- multiplier, filter A = rate/2**shift
);
port (
Clk, Rst : in std_logic;
if_signal : in signed(if_bits-1 downto 0);
I, Q : out signed(baseband_bits-1 downto 0)
);
end;
-----------------------------------------------------------------------------
architecture RTL of iq_demod is
signal refI, refQ, dump : std_logic;
signal prodI, prodQ : signed(if_bits-1 downto 0);
signal filterI, filterQ : signed(baseband_bits-1 downto 0);
signal carrier_delta : unsigned(NCO_bits-1 downto 0);
signal idumpI, idumpQ : signed(baseband_bits-1 downto 0);
begin
------------------------------------------------------------------- NCO ---
-- Numerically controlled oscillator
--
NCO: process (Clk, Rst)
variable PhaseAcc: unsigned(NCO_bits-1 downto 0);
begin
if Rst = '1' then
PhaseAcc := (others => '0');
refI <= '0';
refQ <= '0';
dump <= '0';
elsif rising_edge(Clk) then
refI <= PhaseAcc(NCO_bits-1); -- cosine
refQ <= PhaseAcc(NCO_bits-1) xnor PhaseAcc(NCO_bits-2); -- sine
-- generate a pulse once per carrier cycle for I&D filter
if (refI = '1') and (PhaseAcc(NCO_bits-1) = '0') then
dump <= '1';
else
dump <= '0';
end if;
PhaseAcc := PhaseAcc + carrier_delta;
end if;
end process;
----------------------------------------------------------------- DEMOD ---
-- Synchronous quadrature demodulator (binary multiplier)
--
demod: process (refI, refQ, if_signal)
begin
if refI = '0' then
prodI <= -if_signal;
else
prodI <= if_signal;
end if;
if refQ = '0' then
prodQ <= -if_signal;
else
prodQ <= if_signal;
end if;
end process;
------------------------------------------------------------------- PLL ---
-- Costas loop for phase lock
--
PLL: process (Clk, Rst)
variable absI : signed(idumpI'RANGE);
variable absQ : signed(idumpQ'RANGE);
variable error, reverse: BOOLEAN;
begin
if Rst = '1' then
-- reset actions
carrier_delta <= to_unsigned(carrier_incr, NCO_bits);
elsif rising_edge(Clk) then
if dump = '1' then -- Sample time
-- Calculate absolute values of I, q
if idumpI < 0 then
absI := not idumpI; -- cut price negation, nearly right!
else
absI := idumpI;
end if;
if idumpQ < 0 then
absQ := not idumpQ; -- cut price negation, nearly right!
else
absQ := idumpQ;
end if;
-- Determine quadrant
reverse := (idumpI < 0) xor (idumpQ < 0);
-- Determine direction of error
error := reverse xor (absI > absQ);
-- Determine whether in deadband?
-- Make correction
if error then
carrier_delta <= to_unsigned(carrier_incr+1, NCO_bits);
else
carrier_delta <= to_unsigned(carrier_incr-1, NCO_bits);
end if;
end if;
end if;
end process;
------------------------------------------------------------------- LPF ---
-- Output lowpass filter
--
LPF: process (Clk, Rst)
variable sumI, sumQ: signed(baseband_bits-1 downto 0);
function "sra"(x: signed; n: natural) return signed is
variable xx: signed(x'length-1 downto 0) := x;
begin
for i in 1 to n loop
xx := xx(xx'left) & xx(xx'left downto 1);
end loop;
return xx;
end;
begin
if Rst = '1' then
filterI <= (others => '0');
filterq <= (others => '0');
sumI := (others => '0');
sumQ := (others => '0');
idumpI <= (others => '0');
idumpQ <= (others => '0');
elsif rising_edge(Clk) then
if dump = '1' then -- Dump integrator, generate output
-- Output lowpass
filterI <= resize(
filterI + ((signed'((sumI - filterI) * filter_rate)) sra filter_shift),
filterI'length);
filterQ <= resize(
filterQ + ((signed'((sumQ - filterQ) * filter_rate)) sra filter_shift),
filterQ'length);
-- dump the integrator
sumI := (others => '0');
sumQ := (others => '0');
end if;
-- Integrate (or initialise, if it's the dump cycle)
sumI := sumI + prodI;
sumQ := sumQ + prodQ;
idumpI <= sumI;
idumpQ <= sumQ;
end if;
end process;
-- Produce the output
--
I <= filterI;
Q <= filterQ;
end;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -