Add traceback based soft decision Viterbi decoder implementation

This commit is contained in:
Robert Ghilduta 2021-01-10 21:23:06 -08:00
parent 45fda6959a
commit cbcc2c1951
11 changed files with 1219 additions and 0 deletions

View File

@ -0,0 +1,85 @@
-- This file is part of bladeRF-wiphy.
--
-- Copyright (C) 2021 Nuand, LLC.
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along
-- with this program; if not, write to the Free Software Foundation, Inc.,
-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.viterbi_p.all;
entity branch_compare is
generic(
RESET_ACTIVE : boolean := false;
REG_UNCODED : boolean := false
);
port(
clock : in std_logic;
reset : in std_logic;
bm_a : in unsigned(15 downto 0);
bm_b : in unsigned(15 downto 0);
bm_valid : in std_logic;
path_in_a : in path_t;
path_in_b : in path_t;
branch : in branch_inputs_t;
path_out : out path_t;
win : out std_logic
);
end entity;
architecture arch of branch_compare is
begin
process(reset, clock)
variable cost_a, cost_b : unsigned(path_in_a.cost'range);
begin
if (reset = '1') then
path_out <= NULL_PATH_RST(RESET_ACTIVE);
win <= '0';
elsif (rising_edge(clock)) then
if (bm_valid = '1') then
cost_a := path_in_a.cost + bm_a;
cost_b := path_in_b.cost + bm_b;
if ((path_in_a.cost = (path_in_a.cost'range => '1')) and
(path_in_b.cost = (path_in_b.cost'range => '1'))) then
win <= '0';
path_out.cost <= ( others => '1' );
elsif (path_in_a.cost = (path_in_a.cost'range => '1')) then
win <= '1';
path_out.cost <= cost_b;
elsif (path_in_b.cost = (path_in_b.cost'range => '1')) then
win <= '0';
path_out.cost <= cost_a;
else
if (cost_b < cost_a) then
win <= '1';
path_out.cost <= cost_b;
else
win <= '0';
path_out.cost <= cost_a;
end if;
end if;
end if;
end if;
end process;
end architecture;

View File

@ -0,0 +1,56 @@
-- This file is part of bladeRF-wiphy.
--
-- Copyright (C) 2021 Nuand, LLC.
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along
-- with this program; if not, write to the Free Software Foundation, Inc.,
-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.viterbi_p.all;
entity comp2 is
port(
clock : in std_logic;
reset : in std_logic;
path_name_a : in path_name_t;
path_name_b : in path_name_t;
path_valid : in std_logic;
path_name_out : out path_name_t
);
end entity;
architecture arch of comp2 is
begin
process(clock, reset)
begin
if (reset = '1') then
path_name_out.name <= (others => '0');
path_name_out.path <= NULL_PATH_T;
elsif (rising_edge(clock)) then
if (path_valid = '1') then
if (path_name_b.path.cost < path_name_a.path.cost) then
path_name_out <= path_name_b;
else
path_name_out <= path_name_a;
end if;
end if;
end if;
end process;
end architecture;

View File

@ -0,0 +1,87 @@
-- This file is part of bladeRF-wiphy.
--
-- Copyright (C) 2021 Nuand, LLC.
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along
-- with this program; if not, write to the Free Software Foundation, Inc.,
-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.viterbi_p.all;
entity r2_comparator is
generic(
NUM_PATHS : in natural := 64;
STATE_BITS : in natural := 6
);
port(
clock : in std_logic;
reset : in std_logic;
paths : in path_arr_t(NUM_PATHS-1 downto 0);
path_valid : in std_logic;
label_out : out unsigned(STATE_BITS-1 downto 0);
valid_out : out std_logic
);
end entity;
architecture arch of r2_comparator is
type path_name_arr_arr_t is array(natural range <>) of path_name_arr_t(NUM_PATHS-1 downto 0);
signal r2_matrix : path_name_arr_arr_t(STATE_BITS downto 0);
signal valid_out_r : std_logic_vector(STATE_BITS - 1 downto 1);
begin
gen_init_path: for i in paths'range generate
r2_matrix(0)(i).path <= paths(i);
r2_matrix(0)(i).name <= to_unsigned(i, 6);
end generate;
process(clock, reset)
begin
if (reset = '1') then
valid_out_r <= ( others => '0' );
valid_out <= '0';
elsif (rising_edge(clock)) then
if (path_valid = '1') then
valid_out_r <= path_valid & valid_out_r(valid_out_r'high downto 2);
valid_out <= valid_out_r(1);
else
valid_out <= '0';
end if;
end if;
end process;
gen_cs: for cs_i in 1 to STATE_BITS generate
gen_rs: for rs_i in 0 to NUM_PATHS/(2**cs_i)-1 generate
U_comp2 : entity work.comp2
port map(
clock => clock,
reset => reset,
path_name_a => r2_matrix(cs_i-1)(rs_i * 2),
path_name_b => r2_matrix(cs_i-1)(rs_i * 2 + 1),
path_valid => path_valid,
path_name_out => r2_matrix(cs_i)(rs_i)
);
end generate;
end generate;
label_out <= r2_matrix(r2_matrix'high)(0).name;
end architecture;

View File

@ -0,0 +1,9 @@
vcom -work work -2008 ../viterbi_p.vhd
vcom -work work -2008 ../tracer.vhd
vcom -work work -2008 ../comp2.vhd
vcom -work work -2008 ../r2_comparator.vhd
vcom -work work -2008 ../branch_compare.vhd
vcom -work work -2008 ../traceback.vhd
vcom -work work -2008 ../viterbi_decoder.vhd
vcom -work work -2008 viterbi_decoder_tb.vhd
vcom -work work -2008 r2_comparator_tb.vhd

View File

@ -0,0 +1,69 @@
-- This file is part of bladeRF-wiphy.
--
-- Copyright (C) 2021 Nuand, LLC.
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along
-- with this program; if not, write to the Free Software Foundation, Inc.,
-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.viterbi_p.all;
entity r2_comparator_tb is
end entity;
architecture behv of r2_comparator_tb is
signal clock : std_logic := '0';
signal reset : std_logic;
signal valid : std_logic;
signal paths : path_arr_t(63 downto 0);
begin
clock <= not clock after 10 ns;
reset <= '1', '0' after 100 ns;
valid <= '0', '1' after 198 ns, '0' after 548 ns;
process
variable i : integer;
variable t_cost : integer;
begin
for i in paths'range loop
if (i = 51) then
t_cost := 10;
else
t_cost := 100 + i;
end if;
paths(i).cost <= to_unsigned(t_cost, paths(i).cost'high + 1);
paths(i).active <= '0';
end loop;
wait;
end process;
U_uut: entity work.r2_comparator
port map(
clock => clock,
reset => reset,
paths => paths,
path_valid => valid,
label_out => open,
valid_out => open
);
end architecture;

View File

@ -0,0 +1,189 @@
-- This file is part of bladeRF-wiphy.
--
-- Copyright (C) 2021 Nuand, LLC.
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along
-- with this program; if not, write to the Free Software Foundation, Inc.,
-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity viterbi_decoder_tb is
end entity;
architecture behv of viterbi_decoder_tb is
signal clock : std_logic := '0';
signal reset : std_logic;
type int_arr_t is array(natural range <>) of integer;
constant recv : int_arr_t(0 to 47) := (
-125, -122, -126, 127, 120, -127,
-126, -128, -126, 127, 124, -127,
118, -127, 122, -127, -120, -125,
-123, 127, -117, -125, -124, -127,
-127, -127, 122, -127, 127, 119,
-127, 119, 127, -122, 128, -120,
127, 127, 127, 124, 122, 127,
127, 119, 122, 125, 126, 121);
type LUT_sl is array(natural range <>) of std_logic;
constant LUT : LUT_sl := ( '1', '1', '1', '0',
'0', '1', '0', '1',
'0', '1', '1', '0',
'0', '1', '1', '1',
'0', '1', '1', '0',
'0', '0', '1', '0',
'0', '0', '0', '0',
'0', '1', '0', '1',
'0', '1', '1', '0',
'0', '1', '1', '1',
'0', '1', '1', '0',
'0', '0', '1', '0',
'0', '0', '0', '0',
'0', '1', '0', '1',
'0', '1', '1', '0',
'0', '1', '1', '1',
'0', '1', '1', '0',
'0', '0', '1', '0',
'0', '0', '0', '0',
'1', '1', '1', '1',
'1', '1', '1', '1',
'1', '1', '1', '1',
'1', '1', '1', '1'
);
--signal t_state : std_logic_vector(6 downto 0);
signal t_valid : std_logic := '0';
signal t_bit_a, t_bit_b : std_logic_vector(7 downto 0) := ( others => '0' );
signal t_erasure : std_logic_vector(1 downto 0) := ( others => '0' );
function generate_out_bit(code : std_logic_vector(6 downto 0); state : std_logic_vector(6 downto 0); in_bit : std_logic)
return std_logic
is
variable ret : std_logic;
variable i : natural;
begin
ret := '0';
for i in 0 to code'high loop
if(code(i) = '1') then
if(i = code'high) then
ret := ret xor in_bit;
else
ret := ret xor state(code'high-1-i);
end if;
end if;
end loop;
return ret;
end function;
constant G_A : integer := 91;
constant G_B : integer := 121;
constant K : integer := 7;
function G_A_VEC_GEN return std_logic_vector is
begin
return std_logic_vector(to_unsigned(G_A, K));
end function;
function G_B_VEC_GEN return std_logic_vector is
begin
return std_logic_vector(to_unsigned(G_B, K));
end function;
constant G_A_VEC : std_logic_vector(K-1 downto 0) := G_A_VEC_GEN;
constant G_B_VEC : std_logic_vector(K-1 downto 0) := G_B_VEC_GEN;
begin
clock <= not clock after 10 ns;
reset <= '1', '0' after 100 ns, '1' after 5 us, '0' after 6 us;
process
variable state : std_logic_vector(6 downto 0) := ( others => '0' );
variable b_a, b_b : std_logic;
variable idx : integer := 0;
begin
wait for 500 ns;
for iz in 0 to 23 loop
for i in 0 to 23 loop
wait until rising_edge(clock);
t_valid <= '1';
t_bit_a <= not(std_logic_vector(127 + to_signed(recv(2*i), t_bit_a'high +1)));
t_bit_b <= not(std_logic_vector(127 + to_signed(recv(2*i+1), t_bit_b'high +1)));
--if (recv(i*2) < 0) then
-- t_bit_a <= ( others => '1' );
--else
-- t_bit_a <= ( others => '0' );
--end if;
--if (recv(i*2+1) < 0) then
-- t_bit_b <= ( others => '1' );
--else
-- t_bit_b <= ( others => '0' );
--end if;
end loop;
t_erasure <= ( others => '1' );
wait until reset = '1';
t_valid <= '0';
wait for 30 us;
end loop;
--for i in LUT'range loop
-- wait until rising_edge(clock);
-- b_a := generate_out_bit(G_A_VEC, state, LUT(idx));
-- b_b := generate_out_bit(G_B_VEC, state, LUT(idx));
-- t_bit_a <= ( others => b_a );
-- t_bit_b <= ( others => b_b );
-- t_valid <= '1';
-- state := state(5 downto 0) & LUT(idx);
-- wait until rising_edge(clock);
-- t_valid <= '0';
-- idx := idx + 1;
-- if (idx > 90) then
-- t_bit_a <= ( others => '0' );
-- t_bit_b <= ( others => '0' );
-- for i in 0 to 900 loop
-- t_valid <= '1';
-- wait until rising_edge(clock);
-- t_valid <= '0';
-- if (i = 10 or i = 50) then
-- wait for 10 us;
-- end if;
-- wait until rising_edge(clock);
-- end loop;
-- t_valid <= '0';
-- wait;
-- end if;
--end loop;
end process;
U_uut: entity work.viterbi_decoder
port map(
clock => clock,
reset => reset,
in_a => t_bit_a,
in_b => t_bit_b,
erasure => (others => '0'),
bsd_valid => t_valid,
out_bit => open,
out_valid => open
);
end architecture;

View File

@ -0,0 +1,112 @@
onerror {resume}
quietly WaveActivateNextPane {} 0
add wave -noupdate /viterbi_decoder_tb/U_uut/G_A
add wave -noupdate /viterbi_decoder_tb/U_uut/G_A_VEC
add wave -noupdate /viterbi_decoder_tb/U_uut/G_B
add wave -noupdate /viterbi_decoder_tb/U_uut/G_B_VEC
add wave -noupdate /viterbi_decoder_tb/U_uut/K
add wave -noupdate /viterbi_decoder_tb/U_uut/TB_LEN
add wave -noupdate /viterbi_decoder_tb/U_uut/acs_reg
add wave -noupdate /viterbi_decoder_tb/U_uut/acs_valid
add wave -noupdate /viterbi_decoder_tb/U_uut/best_idx
add wave -noupdate /viterbi_decoder_tb/U_uut/bm
add wave -noupdate /viterbi_decoder_tb/U_uut/bm_valid
add wave -noupdate /viterbi_decoder_tb/U_uut/bsd_in
add wave -noupdate /viterbi_decoder_tb/U_uut/bsd_valid
add wave -noupdate /viterbi_decoder_tb/U_uut/clock
add wave -noupdate /viterbi_decoder_tb/U_uut/erasure
add wave -noupdate /viterbi_decoder_tb/U_uut/in_a
add wave -noupdate /viterbi_decoder_tb/U_uut/in_b
add wave -noupdate /viterbi_decoder_tb/U_uut/lut
add wave -noupdate /viterbi_decoder_tb/U_uut/out_bit
add wave -noupdate /viterbi_decoder_tb/U_uut/out_valid
add wave -noupdate /viterbi_decoder_tb/U_uut/paths
add wave -noupdate /viterbi_decoder_tb/U_uut/reset
add wave -noupdate /viterbi_decoder_tb/U_uut/U_r2_comp/NUM_PATHS
add wave -noupdate /viterbi_decoder_tb/U_uut/U_r2_comp/STATE_BITS
add wave -noupdate /viterbi_decoder_tb/U_uut/U_r2_comp/clock
add wave -noupdate /viterbi_decoder_tb/U_uut/U_r2_comp/label_out
add wave -noupdate /viterbi_decoder_tb/U_uut/U_r2_comp/path_valid
add wave -noupdate /viterbi_decoder_tb/U_uut/U_r2_comp/paths
add wave -noupdate /viterbi_decoder_tb/U_uut/U_r2_comp/r2_matrix
add wave -noupdate /viterbi_decoder_tb/U_uut/U_r2_comp/reset
add wave -noupdate /viterbi_decoder_tb/U_uut/U_r2_comp/valid_out
add wave -noupdate /viterbi_decoder_tb/U_uut/U_r2_comp/valid_out_r
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/NUM_STATES
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/STATE_BITS
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/TB_LEN
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/acs_reg
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/acs_valid
add wave -noupdate -radix unsigned /viterbi_decoder_tb/U_uut/U_traceback/best_idx
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/best_valid
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/bit_out
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/clock
add wave -noupdate -expand /viterbi_decoder_tb/U_uut/U_traceback/current
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/future
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/reset
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/trace_state
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/trace_state_valid
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(0)/U_tracer/NUM_STATES
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(0)/U_tracer/STATE_BITS
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(0)/U_tracer/acs_reg
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(0)/U_tracer/acs_valid
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(0)/U_tracer/clock
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(0)/U_tracer/reset
add wave -noupdate -radix unsigned /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(0)/U_tracer/state_in
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(0)/U_tracer/state_valid
add wave -noupdate -radix unsigned /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(0)/U_tracer/state_out
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(0)/U_tracer/valid_out
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/NUM_STATES
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/STATE_BITS
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/TB_LEN
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/acs_reg
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/acs_valid
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/best_idx
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/best_valid
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/bit_out
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/valid_out
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/clock
add wave -noupdate -expand /viterbi_decoder_tb/U_uut/U_traceback/current
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/future
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/reset
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/trace_state
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/trace_state_valid
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/valid_out
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(1)/U_tracer/acs_reg
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(1)/U_tracer/acs_valid
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(1)/U_tracer/clock
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(1)/U_tracer/NUM_STATES
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(1)/U_tracer/reset
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(1)/U_tracer/STATE_BITS
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(1)/U_tracer/state_in
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(1)/U_tracer/state_out
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(1)/U_tracer/state_valid
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(1)/U_tracer/valid_out
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(19)/U_tracer/acs_reg
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(19)/U_tracer/acs_valid
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(19)/U_tracer/clock
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(19)/U_tracer/NUM_STATES
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(19)/U_tracer/reset
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(19)/U_tracer/STATE_BITS
add wave -noupdate -radix unsigned /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(19)/U_tracer/state_in
add wave -noupdate -radix unsigned /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(19)/U_tracer/state_out
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(19)/U_tracer/state_valid
add wave -noupdate /viterbi_decoder_tb/U_uut/U_traceback/gen_tracers(19)/U_tracer/valid_out
TreeUpdate [SetDefaultTree]
WaveRestoreCursors {{Cursor 1} {3754327 ps} 0}
quietly wave cursor active 1
configure wave -namecolwidth 518
configure wave -valuecolwidth 100
configure wave -justifyvalue left
configure wave -signalnamewidth 0
configure wave -snapdistance 10
configure wave -datasetprefix 0
configure wave -rowmargin 4
configure wave -childrowmargin 2
configure wave -gridoffset 0
configure wave -gridperiod 1
configure wave -griddelta 40
configure wave -timeline 0
configure wave -timelineunits ps
update
WaveRestoreZoom {0 ps} {52500 ns}

View File

@ -0,0 +1,155 @@
-- This file is part of bladeRF-wiphy.
--
-- Copyright (C) 2021 Nuand, LLC.
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along
-- with this program; if not, write to the Free Software Foundation, Inc.,
-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.viterbi_p.all;
entity traceback is
generic(
STATE_BITS : in integer;
NUM_STATES : in integer;
TB_LEN : in integer
);
port(
clock : in std_logic;
reset : in std_logic;
acs_reg : in std_logic_vector(NUM_STATES-1 downto 0);
acs_valid : in std_logic;
best_idx : in unsigned(STATE_BITS-1 downto 0);
best_valid : in std_logic;
bit_out : out std_logic;
valid_out : out std_logic
);
end entity;
architecture arch of traceback is
type history_t is array(7 + TB_LEN*2 downto 0) of std_logic_vector(NUM_STATES-1 downto 0);
type fsm_t is (IDLE, START_TRACER);
type state_t is record
fsm : fsm_t;
loaded : std_logic;
history : history_t;
acs_count : natural range 0 to TB_LEN+STATE_BITS*2;
acs_enough : std_logic;
bit_out : std_logic;
valid_out : std_logic;
end record;
function NULL_STATE_T return state_t is
variable ret : state_t;
begin
ret.fsm := IDLE;
ret.loaded := '0';
for i in ret.history'range loop
ret.history(i) := ( others => '0' );
end loop;
ret.acs_count := 0;
ret.acs_enough := '0';
ret.bit_out := '0';
ret.valid_out := '0';
return(ret);
end function;
signal current, future : state_t := NULL_STATE_T;
type trace_state_t is array(TB_LEN downto 0) of unsigned(STATE_BITS-1 downto 0);
signal trace_state : trace_state_t;
signal trace_state_valid : std_logic_vector(TB_LEN downto 0);
begin
sync : process(clock, reset)
begin
if (reset = '1') then
current <= NULL_STATE_T;
elsif (rising_edge(clock)) then
current <= future;
end if;
end process;
trace_state(0) <= best_idx;
trace_state_valid(0) <= current.acs_enough;
gen_tracers: for i in 0 to TB_LEN-1 generate
U_tracer: entity work.tracer
generic map(
STATE_BITS => STATE_BITS,
NUM_STATES => NUM_STATES
)
port map(
clock => clock,
reset => reset,
state_in => trace_state(i),
state_valid => trace_state_valid(i),
acs_reg => current.history(6 + 2 * i),
acs_valid => acs_valid,
state_out => trace_state(i+1),
valid_out => trace_state_valid(i+1)
);
end generate;
bit_out <= current.bit_out;
valid_out <= current.valid_out;
comb : process(all)
begin
future <= current;
future.bit_out <= '0';
future.valid_out <= '0';
if (acs_valid = '1') then
if (current.acs_enough = '0') then
if (current.acs_count = TB_LEN + 6 + 5) then
future.acs_enough <= '1';
else
future.acs_count <= current.acs_count + 1;
end if;
end if;
future.history <= current.history(current.history'high-1 downto 0) & acs_reg;
end if;
case current.fsm is
when IDLE =>
if (current.acs_enough = '1' and acs_valid = '1') then
future.fsm <= START_TRACER;
end if;
when START_TRACER =>
if (acs_valid = '1') then
-- the bit that is about to be shifted out is actually the uncoded bit
-- the new bit that is shifted in is from the current branch, the historical ACS reg basically determines the LSB of the new state
future.bit_out <= trace_state(TB_LEN)(5);
future.valid_out <= trace_state_valid(TB_LEN);
end if;
when others =>
future <= NULL_STATE_T;
end case;
end process;
end architecture;

View File

@ -0,0 +1,61 @@
-- This file is part of bladeRF-wiphy.
--
-- Copyright (C) 2021 Nuand, LLC.
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along
-- with this program; if not, write to the Free Software Foundation, Inc.,
-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.viterbi_p.all;
entity tracer is
generic(
STATE_BITS : in integer;
NUM_STATES : in integer
);
port(
clock : in std_logic;
reset : in std_logic;
state_in : in unsigned(STATE_BITS-1 downto 0);
state_valid : in std_logic;
acs_reg : in std_logic_vector(NUM_STATES-1 downto 0);
acs_valid : in std_logic;
state_out : out unsigned(STATE_BITS-1 downto 0);
valid_out : out std_logic
);
end entity;
architecture arch of tracer is
begin
process(clock, reset)
variable s_bit : std_logic;
begin
if (reset='1') then
state_out <= ( others => '0' );
valid_out <= '0';
elsif (rising_edge(clock)) then
s_bit := acs_reg(to_integer(state_in));
if (acs_valid = '1') then
state_out <= s_bit & state_in(state_in'high downto 1);
valid_out <= state_valid;
end if;
end if;
end process;
end architecture;

View File

@ -0,0 +1,311 @@
-- This file is part of bladeRF-wiphy.
--
-- Copyright (C) 2021 Nuand, LLC.
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along
-- with this program; if not, write to the Free Software Foundation, Inc.,
-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
library work;
use work.viterbi_p.all;
entity viterbi_decoder is
generic(
K : in natural := 7;
G_A : in natural := 91;
G_B : in natural := 121;
TB_LEN : in natural := 42
);
port(
clock : in std_logic;
reset : in std_logic;
in_a : in std_logic_vector(7 downto 0);
in_b : in std_logic_vector(7 downto 0);
erasure : in std_logic_vector(1 downto 0);
bsd_valid : in std_logic;
out_bit : out std_logic;
out_valid : out std_logic
);
end entity;
architecture arch of viterbi_decoder is
function STATE_BITS return natural is
begin
return K-1;
end function;
function NUM_STATES return natural is
begin
return (2**STATE_BITS);
end function;
function G_A_VEC_GEN return std_logic_vector is
begin
return std_logic_vector(to_unsigned(G_A, K));
end function;
function G_B_VEC_GEN return std_logic_vector is
begin
return std_logic_vector(to_unsigned(G_B, K));
end function;
constant G_A_VEC : std_logic_vector(K-1 downto 0) := G_A_VEC_GEN;
constant G_B_VEC : std_logic_vector(K-1 downto 0) := G_B_VEC_GEN;
type branch_lut_t is array(NUM_STATES-1 downto 0) of branch_inputs_t;
function generate_out_bit(code : std_logic_vector(K-1 downto 0); state : std_logic_vector(STATE_BITS-1 downto 0); in_bit : std_logic)
return std_logic
is
variable ret : std_logic;
variable i : natural;
begin
ret := '0';
for i in 0 to code'high loop
if(code(i) = '1') then
if(i = code'high) then
ret := ret xor in_bit;
else
ret := ret xor state(code'high-1-i);
end if;
end if;
end loop;
return ret;
end function;
function generate_next_state(state : std_logic_vector(STATE_BITS-1 downto 0); in_bit : std_logic)
return std_logic_vector is
begin
return state(state'high - 1 downto 0) & in_bit;
end function;
-- if the convolution encoder's initial state is 0, some states are
-- "impossible" at least for K-1 bits
function is_state_active( t_idx : integer ; state : integer ) return boolean is
begin
if (t_idx < 0) then
return true;
end if;
return ( (state) <= (2**t_idx - 1) );
end function;
-- cast std_logic vector to integer
function sl_to_integer( x : std_logic ) return integer is
begin
if (x = '1') then
return 1;
end if;
return 0;
end function;
function gen_branch_t( i : integer ; in_bit : std_logic ) return branch_t is
variable t_state_slv : std_logic_vector(STATE_BITS-1 downto 0);
variable next_state_slv : std_logic_vector(STATE_BITS-1 downto 0);
variable t_bit_a : std_logic;
variable t_bit_b : std_logic;
variable t_bit_slv : std_logic_vector(1 downto 0);
variable ret : branch_t;
begin
t_state_slv := std_logic_vector(to_unsigned(i, STATE_BITS));
t_bit_a := generate_out_bit(G_A_VEC, t_state_slv, in_bit);
t_bit_b := generate_out_bit(G_B_VEC, t_state_slv, in_bit);
next_state_slv := t_state_slv(t_state_slv'high-1 downto 0) & in_bit;
ret.set := true;
ret.start_state := i;
ret.u_bit := in_bit;
ret.bit_a := t_bit_a;
ret.bit_b := t_bit_b;
ret.bit_s := t_state_slv(t_state_slv'high);
t_bit_slv := t_bit_b & t_bit_a;
ret.bm_idx := to_integer(unsigned(t_bit_slv));
ret.prev_state := i;
ret.next_state := to_integer(unsigned(next_state_slv));
--report "S[" & integer'image(i) & "] -- bit=" & to_string(in_bit) &
-- " coded=" & to_string(t_bit_a) & "," & to_string(t_bit_b) &
-- " -- D[" & integer'image(to_integer(unsigned(next_state_slv))) & "]";
return ret;
end function;
function generate_branch_table return branch_lut_t is
variable tmp_branch : branch_t;
variable ret : branch_lut_t;
begin
for i in 0 to NUM_STATES-1 loop
ret(i)(0).set := false;
ret(i)(1).set := false;
end loop;
for i in 0 to NUM_STATES-1 loop
-- uncoded bit is 0?
tmp_branch := gen_branch_t(i, '0');
if (ret(tmp_branch.next_state)(sl_to_integer(tmp_branch.bit_s)).set) then
report "ERROR BUILDING BRANCH TABLE, INPUTS MUST BE UNIQUE" severity error;
end if;
ret(tmp_branch.next_state)(sl_to_integer(tmp_branch.bit_s)) := tmp_branch;
-- uncoded bit is 1?
tmp_branch := gen_branch_t(i, '1');
if (ret(tmp_branch.next_state)(sl_to_integer(tmp_branch.bit_s)).set) then
report "ERROR BUILDING BRANCH TABLE, INPUTS MUST BE UNIQUE" severity error;
end if;
ret(tmp_branch.next_state)(sl_to_integer(tmp_branch.bit_s)) := tmp_branch;
end loop;
return ret;
end function;
--function NULL_EDGE is return edge_t
constant lut : branch_lut_t := generate_branch_table;
signal bsd_in : bsd_t(1 downto 0);
signal paths : path_arr_t(NUM_STATES-1 downto 0);
signal bm : bm_t(3 downto 0);
signal bm_valid : std_logic;
function single_bit_loss_function(received : unsigned; expected : std_logic) return unsigned is
variable expanded : unsigned(received'range);
begin
if (expected = '0') then
return(received);
end if;
expanded := (others => '1');
return expanded - received;
end function;
function loss_function(received_bsd : bsd_t ; expected : unsigned ; erasure : std_logic_vector) return unsigned is
variable loss : unsigned(15 downto 0);
begin
loss := ( others => '0' );
for i in received_bsd'range loop
if (erasure(i) = '0') then
loss := loss + single_bit_loss_function(received_bsd(i), expected(i));
end if;
end loop;
return loss;
end function;
signal acs_reg : std_logic_vector(NUM_STATES-1 downto 0);
signal acs_valid : std_logic;
signal best_idx : unsigned(STATE_BITS-1 downto 0);
begin
bsd_in(0) <= unsigned(in_a);
bsd_in(1) <= unsigned(in_b);
process(clock, reset)
begin
if (reset = '1') then
for i in bm'range loop
bm(i) <= ( others => '0' );
end loop;
bm_valid <= '0';
elsif (rising_edge(clock)) then
if (bsd_valid = '1') then
bm_valid <= '1';
for i in bm'range loop
bm(i) <= loss_function(bsd_in, to_unsigned(i,2), erasure);
end loop;
else
bm_valid <= '0';
end if;
end if;
end process;
gen_bm_loop: for t_state in 0 to (NUM_STATES-1) generate
gen_bc: entity work.branch_compare
generic map(
RESET_ACTIVE => (t_state = 0)
)
port map(
clock => clock,
reset => reset,
bm_a => bm(lut(t_state)(0).bm_idx),
bm_b => bm(lut(t_state)(1).bm_idx),
bm_valid => bm_valid,
path_in_a => paths(lut(t_state)(0).prev_state),
path_in_b => paths(lut(t_state)(1).prev_state),
branch => lut(t_state),
path_out => paths(t_state),
win => acs_reg(t_state)
);
end generate;
process(clock, reset)
begin
if (reset = '1') then
acs_valid <= '0';
elsif (rising_edge(clock)) then
acs_valid <= bm_valid;
end if;
end process;
U_r2_comp: entity work.r2_comparator
port map(
clock => clock,
reset => reset,
paths => paths,
path_valid => acs_valid,
label_out => best_idx
);
U_traceback: entity work.traceback
generic map(
STATE_BITS => STATE_BITS,
NUM_STATES => NUM_STATES,
TB_LEN => TB_LEN
)
port map(
clock => clock,
reset => reset,
acs_reg => acs_reg,
acs_valid => acs_valid,
best_idx => best_idx,
best_valid => '0',
bit_out => out_bit,
valid_out => out_valid
);
end architecture;

View File

@ -0,0 +1,85 @@
-- This file is part of bladeRF-wiphy.
--
-- Copyright (C) 2021 Nuand, LLC.
--
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along
-- with this program; if not, write to the Free Software Foundation, Inc.,
-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package viterbi_p is
type path_t is record
--- path metric stuff
cost : unsigned(23 downto 0);
active : std_logic;
end record;
type path_arr_t is array(natural range <>) of path_t;
type path_name_t is record
path : path_t;
name : unsigned(5 downto 0);
end record;
type path_name_arr_t is array(natural range <>) of path_name_t;
type branch_t is record
-- starting conditions
start_state : natural;
u_bit : std_logic;
-- internal state keeping
bit_s : std_logic;
set : boolean;
-- output
bit_a : std_logic;
bit_b : std_logic;
bm_idx : integer;
-- final state
prev_state : integer;
next_state : integer;
end record;
type branch_inputs_t is array(1 downto 0) of branch_t;
type bsd_t is array(natural range <>) of unsigned(7 downto 0);
type bm_t is array(natural range <>) of unsigned(15 downto 0);
function NULL_PATH_RST(init_state : boolean) return path_t;
function NULL_PATH_T return path_t;
end package;
package body viterbi_p is
function NULL_PATH_RST(init_state : boolean) return path_t is
variable ret : path_t;
begin
if (init_state) then
ret.cost := ( others => '0' );
else
ret.cost := ( others => '1' );
end if;
ret.active := '0';
return ret;
end function;
function NULL_PATH_T return path_t is
begin
return NULL_PATH_RST(false);
end function;
end package body;