Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CIC Decimation core re-written in VHDL #48

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion hdl/modules/cic/Manifest.py
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
files = [ "cic_dyn.vhd", "cic_decim.v", "log2.v", "decimation_strober.vhd", "cic_dual.vhd" ]
files = [ "cic_dyn.vhd", "log2.v", "decimation_strober.vhd", "cic_dual.vhd" ]

if use_cic_decim_vhdl:
files.append("cic_decim.vhd")
else:
files.append("cic_decim.v")
185 changes: 185 additions & 0 deletions hdl/modules/cic/cic_decim.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
-------------------------------------------------------------------------------
-- Title : CIC decimator with dynamically-adjustable decimator
-------------------------------------------------------------------------------
-- Author : Augusto Fraga Giachero <[email protected]>
-- Company : CNPEM LNLS-GIE
-- Platform : Generic
-- Standard : VHDL'93
-------------------------------------------------------------------------------
-- Description: CIC decimator with dinamically adjustable decimation rate
--
-- The CIC has a valid signal (act) pipeline that signals when the data is
-- filling integrator and comb pipelines. When the decimation strobe
-- comes (act_out_i), the data in the last integrator register is sampled
-- to the comb stage. However, to allow the decimation strobe to happen in
-- a different clock period from the valid input signal, the valid signal
-- in the last register is marked as invalid during the decimation. The
-- sampling only happens when this register is valid, avoiding data corruption
-- from occasional spurious decimation strobes.
--
-- Design based on Daniel Tavare's verilog implementation
-------------------------------------------------------------------------------
-- Copyright (c) 2023 CNPEM
-- Licensed under GNU Lesser General Public License (LGPL) v3.0
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2023-01-20 1.0 augusto.fraga Created
-------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity cic_decim is
generic (
DATAIN_WIDTH : integer := 16;
DATAOUT_WIDTH : integer := 16;
M : integer := 2;
N : integer := 5;
MAXRATE : integer := 64;
BITGROWTH : integer := 35;
ROUND_CONVERGENT : integer := 0
);
port (
clk_i : in std_logic;
rst_i : in std_logic;
en_i : in std_logic;
data_i : in std_logic_vector(DATAIN_WIDTH-1 downto 0);
data_o : out std_logic_vector(DATAOUT_WIDTH-1 downto 0);
act_i : in std_logic;
act_out_i : in std_logic;
val_o : out std_logic
);
end entity;

architecture cic_decim_arch of cic_decim is
constant c_dataout_full_width : natural := DATAIN_WIDTH + BITGROWTH;
constant c_dataout_extra_bits : integer := c_dataout_full_width - DATAOUT_WIDTH;
type t_signed_array is array (positive range <>) of signed(c_dataout_full_width downto 0);
type t_signed_matrix is array (positive range <>) of t_signed_array(N-1 downto 0);

function f_replicate(x : std_logic; len : natural)
return std_logic_vector
is
variable v_ret : std_logic_vector(len-1 downto 0) := (others => x);
begin
return v_ret;
end f_replicate;

-- round using "convergent rounding" method
function f_convergent_round(x : std_logic_vector; x_new_msb : natural)
return std_logic_vector
is
constant x_old_msb : natural := x'left;
constant x_extra_msb : natural := x_old_msb - x_new_msb - 1;
variable v_x_conv : std_logic_vector(x_new_msb downto 0);
begin
-- if it's midscale (tie), converge to even integers
if (unsigned(x(x_extra_msb downto 0)) =
unsigned('1' & f_replicate('0', x_extra_msb))) then
v_x_conv := std_logic_vector(unsigned(x(x_old_msb downto x_extra_msb+1)) +
unsigned'("" & x(x_extra_msb+1)));
-- otherwise, round to nearest integer
else
v_x_conv := std_logic_vector(unsigned(x(x_old_msb downto x_extra_msb+1)) +
unsigned'("" & x(x_extra_msb)));
end if;

-- overflow, saturate
if v_x_conv(v_x_conv'left) /= x(x_old_msb) then
return x(x_old_msb) & f_replicate(not x(x_old_msb), x_new_msb);
end if;

return v_x_conv;
end f_convergent_round;

signal integrator : t_signed_array(N-1 downto 0);
signal pipe : t_signed_array(N-1 downto 0);
signal diff_delay : t_signed_matrix(M-1 downto 0);
signal act_integ : std_logic_vector(N-1 downto 0);
signal act_comb : std_logic_vector(N-1 downto 0);
signal sampler : signed(c_dataout_full_width downto 0);
signal act_samp : std_logic;
signal val_int : std_logic;
begin
process(clk_i)
begin
if rising_edge(clk_i) then
if rst_i = '1' then
diff_delay <= (others => (others => (others => '0')));
integrator <= (others => (others => '0'));
pipe <= (others => (others => '0'));
act_integ <= (others => '0');
act_comb <= (others => '0');
sampler <= (others => '0');
data_o <= (others => '0');
act_samp <= '0';
val_int <= '0';
val_o <= '0';
elsif en_i = '1' then
if act_i = '1' then
integrator(0) <= integrator(0) + resize(signed(data_i), N);
act_integ(0) <= '1';
for i in 1 to N-1 loop
integrator(i) <= integrator(i) + integrator(i-1);
act_integ(i) <= act_integ(i);
end loop;
else
-- Clear the act_integ flag only when the COMB section acknowledges it
if act_out_i = '1' then
act_integ(N-1) <= '0';
end if;

if act_out_i = '1' and act_integ(N-1) = '1' then
sampler <= integrator(N-1);
act_samp <= '1';
diff_delay(0)(0) <= sampler;

for i in 1 to M-1 loop
diff_delay(0)(i) <= diff_delay(0)(i-1);
end loop;

pipe(0) <= sampler - diff_delay(0)(M-1);
act_comb(0) <= act_samp;

for i in 1 to N-1 loop
diff_delay(i)(0) <= pipe(i-1);
for j in 1 to M-1 loop
diff_delay(i)(j) <= diff_delay(i)(j-1);
end loop;
pipe(i) <= pipe(i-1) - diff_delay(i)(M-1);
act_comb(i) <= act_comb(i-1);
end loop;

if N = 1 then
val_int <= act_samp;
else
val_int <= act_comb(N-2);
end if;

else
val_int <= '0';
end if;

end if;
val_o <= val_int;
if c_dataout_extra_bits = 0 then
data_o <= std_logic_vector(pipe(N-1));
elsif c_dataout_extra_bits > 0 then
if ROUND_CONVERGENT = 1 then
-- Convergent round
data_o <= f_convergent_round(std_logic_vector(pipe(N-1)), DATAOUT_WIDTH);
else
-- Truncate least significant bits
data_o <= std_logic_vector(pipe(N-1)(c_dataout_full_width-1 downto c_dataout_extra_bits));
end if;
else
-- Sign-extend bits as selected data output width > computed data output
-- width
data_o <= std_logic_vector(resize(pipe(N-1), DATAOUT_WIDTH));
end if;
end if;
end if;
end process;
end architecture;
6 changes: 6 additions & 0 deletions hdl/testbench/cic_decim/Manifest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
files = ["cic_decim_tb.vhd"]
modules = {"local" : [
"../../ip_cores/general-cores",
"../../modules",
]
}
146 changes: 146 additions & 0 deletions hdl/testbench/cic_decim/cic_decim_tb.vhd
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
-------------------------------------------------------------------------------
-- Title : CIC decimator testbench
-------------------------------------------------------------------------------
-- Author : Augusto Fraga Giachero
-- Company : CNPEM LNLS-GIE
-- Platform : Simulation
-- Standard : VHDL 2008
-------------------------------------------------------------------------------
-- Description:
-------------------------------------------------------------------------------
-- Copyright (c) 2023-01-23 CNPEM
-- Licensed under GNU Lesser General Public License (LGPL) v3.0
-------------------------------------------------------------------------------
-- Revisions :
-- Date Version Author Description
-- 2023-01-23 1.0 augusto.fraga Created
-------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use ieee.math_real.all;

library work;
use work.dsp_cores_pkg.all;

entity cic_decim_tb is
generic (
g_DATAIN_WIDTH : integer := 16;
g_DATAOUT_WIDTH : integer := 32;
g_CIC_DELAY : integer := 1;
g_CIC_STAGES : integer := 1;
g_MAX_RATE : integer := 2048;
g_BITGROWTH : integer := 11;
g_ROUND_CONVERGENT : boolean := false
);
end cic_decim_tb;

architecture rtl of cic_decim_tb is
function bool_to_int(x : boolean) return integer is
begin
if x then
return 1;
else
return 0;
end if;
end function;

procedure f_gen_clk(constant freq : in natural;
signal clk : inout std_logic) is
begin
loop
wait for (0.5 / real(freq)) * 1 sec;
clk <= not clk;
end loop;
end procedure f_gen_clk;

procedure f_wait_cycles(signal clk : in std_logic;
constant cycles : natural) is
begin
for i in 1 to cycles loop
wait until rising_edge(clk);
end loop;
end procedure f_wait_cycles;

type t_cic_decim_iface is record
en_i : std_logic;
decim_i : std_logic;
valid_i : std_logic;
valid_o : std_logic;
data_i : std_logic_vector(g_DATAIN_WIDTH-1 downto 0);
data_o : std_logic_vector(g_DATAOUT_WIDTH-1 downto 0);
end record;

procedure f_cic_decim_write(signal cic_if : inout t_cic_decim_iface;
data : signed(g_DATAIN_WIDTH-1 downto 0);
signal clk : in std_logic) is
begin
cic_if.en_i <= '1';
cic_if.data_i <= std_logic_vector(data);
cic_if.valid_i <= '1';
wait until rising_edge(clk);
cic_if.valid_i <= '0';
end procedure;

procedure f_cic_decim_read(signal cic_if : inout t_cic_decim_iface;
signal data : out signed(g_DATAOUT_WIDTH-1 downto 0);
signal clk : in std_logic) is
begin
cic_if.en_i <= '1';
cic_if.decim_i <= '1';
wait until rising_edge(clk);
wait until rising_edge(clk);
cic_if.decim_i <= '0';
wait until cic_if.valid_o = '1';
data <= signed(cic_if.data_o);
end procedure;

signal cic_decim_iface : t_cic_decim_iface := (
en_i => '0',
decim_i => '0',
valid_i => '0',
valid_o => '0',
data_i => (others => '0'),
data_o => (others => '0')
);
signal result : signed(g_DATAOUT_WIDTH-1 downto 0) := (others => '0');
signal clk : std_logic := '0';
signal rst : std_logic := '1';
begin

cmp_cic_decim: cic_decim
generic map (
DATAIN_WIDTH => g_DATAIN_WIDTH,
DATAOUT_WIDTH => g_DATAOUT_WIDTH,
M => g_CIC_DELAY,
N => g_CIC_STAGES,
MAXRATE => g_MAX_RATE,
BITGROWTH => g_BITGROWTH,
ROUND_CONVERGENT => bool_to_int(g_ROUND_CONVERGENT)
)
port map (
clk_i => clk,
rst_i => rst,
en_i => cic_decim_iface.en_i,
data_i => cic_decim_iface.data_i,
data_o => cic_decim_iface.data_o,
act_i => cic_decim_iface.valid_i,
act_out_i => cic_decim_iface.decim_i,
val_o => cic_decim_iface.valid_o
);

f_gen_clk(100e6, clk);
process
begin
f_wait_cycles(clk, 5);
rst <= '0';
f_wait_cycles(clk, 1);
for i in 1 to 20 loop
f_cic_decim_write(cic_decim_iface, to_signed(20, g_DATAIN_WIDTH), clk);
end loop;
f_cic_decim_read(cic_decim_iface, result, clk);
std.env.finish;
end process;

end architecture rtl;
4 changes: 4 additions & 0 deletions hdl/testbench/cic_decim/ghdl/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
cic_decim_tb
cic_decim_tb.ghw
*.cf
*.o
13 changes: 13 additions & 0 deletions hdl/testbench/cic_decim/ghdl/Manifest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
action = "simulation"
target = "xilinx"
syn_device = "xc7a200t"
sim_tool = "ghdl"
top_module = "cic_decim_tb"

modules = {"local" : ["../"]}

use_cic_decim_vhdl = True

ghdl_opt = "--std=08"

sim_post_cmd = "ghdl -r --std=08 cic_decim_tb --wave=cic_decim_tb.ghw --assert-level=error --stop-time=50us"