mirror of
https://github.com/Nuand/bladeRF-wiphy.git
synced 2024-12-19 05:38:09 +00:00
333 lines
12 KiB
VHDL
333 lines
12 KiB
VHDL
-- This file is part of bladeRF-wiphy.
|
|
--
|
|
-- Copyright (C) 2020 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 wlan ;
|
|
use wlan.wlan_p.all ;
|
|
use wlan.wlan_rx_p.all ;
|
|
|
|
entity wlan_equalizer is
|
|
port (
|
|
clock : in std_logic ;
|
|
reset : in std_logic ;
|
|
|
|
init : in std_logic ;
|
|
|
|
dfe_sample : in wlan_sample_t ;
|
|
|
|
in_sample : in wlan_sample_t ;
|
|
in_done : in std_logic ;
|
|
out_sample : out wlan_sample_t ;
|
|
out_done : in std_logic
|
|
) ;
|
|
end entity;
|
|
|
|
architecture arch of wlan_equalizer is
|
|
|
|
type equalizer_coefficient is record
|
|
i : signed( 15 downto 0 ) ;
|
|
q : signed( 15 downto 0 ) ;
|
|
end record ;
|
|
|
|
type equalizer_coefficients_t is array (integer range <>) of equalizer_coefficient ;
|
|
|
|
constant T2 : integer_array_t( 0 to 63 ) := (
|
|
0, 1, -1, -1, 1, 1, -1, 1,
|
|
-1, 1, -1, -1, -1, -1, -1, 1,
|
|
1, -1, -1, 1, -1, 1, -1, 1,
|
|
1, 1, 1, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 1, 1,
|
|
-1, -1, 1, 1, -1, 1, -1, 1,
|
|
1, 1, 1, 1, 1, -1, -1, 1,
|
|
1, -1, 1, -1, 1, 1, 1, 1
|
|
);
|
|
|
|
type fsm_t is (IDLE, INITIAL_ESTIMATE, UPDATE_ESTIMATE) ;
|
|
|
|
type state_t is record
|
|
rfsm, wfsm : fsm_t ;
|
|
eq : equalizer_coefficients_t( 63 downto 0 ) ;
|
|
update_index : unsigned( 5 downto 0 ) ;
|
|
rupdate_index : unsigned( 5 downto 0 ) ;
|
|
eq_chan, eq_ref : wlan_sample_t ;
|
|
eq_first, eq_last : std_logic;
|
|
|
|
rcupdate_index : unsigned( 5 downto 0 ) ;
|
|
rcupdate_cur_eq_sample : wlan_sample_t ;
|
|
rcupdate_new_eq_sample : wlan_sample_t ;
|
|
rcupdate_result : wlan_sample_t ;
|
|
rcupdate_result_index : unsigned( 5 downto 0 ) ;
|
|
end record ;
|
|
|
|
function NULL_STATE return state_t is
|
|
variable rv : state_t ;
|
|
begin
|
|
for x in rv.eq'range loop
|
|
rv.eq(x) := (others => (others => '0') ) ;
|
|
end loop ;
|
|
rv.eq_chan := NULL_SAMPLE ;
|
|
rv.eq_ref := NULL_SAMPLE ;
|
|
rv.eq_first := '0' ;
|
|
rv.eq_last := '0' ;
|
|
rv.update_index := ( others => '0' ) ;
|
|
rv.rfsm := IDLE ;
|
|
rv.wfsm := IDLE ;
|
|
rv.rupdate_index := ( others => '0' ) ;
|
|
rv.rcupdate_index := ( others => '0' ) ;
|
|
rv.rcupdate_cur_eq_sample := NULL_SAMPLE ;
|
|
rv.rcupdate_new_eq_sample := NULL_SAMPLE ;
|
|
rv.rcupdate_result := NULL_SAMPLE ;
|
|
rv.rcupdate_result_index := ( others => '0' ) ;
|
|
return rv ;
|
|
end function ;
|
|
|
|
|
|
signal equalizer : wlan_sample_t ;
|
|
|
|
signal last_sample : wlan_sample_t ;
|
|
signal last_sample_r : wlan_sample_t ;
|
|
signal last_sample_rr : wlan_sample_t ;
|
|
signal last_sample_rrr : wlan_sample_t ;
|
|
signal eq_sample : wlan_sample_t ;
|
|
|
|
signal bin_index : unsigned( 5 downto 0 ) ;
|
|
signal in_sample_r : wlan_sample_t ;
|
|
signal result_sample : wlan_sample_t ;
|
|
signal equalizer_coeff_sample : equalizer_coefficient ;
|
|
|
|
signal current, future : state_t := NULL_STATE ;
|
|
signal eq_chan, eq_ref, eq_out, eq_invd: wlan_sample_t ;
|
|
signal eq_first, eq_last, eq_inv_done : std_logic;
|
|
begin
|
|
|
|
eq_ref <= current.eq_ref;
|
|
eq_chan <= current.eq_chan;
|
|
|
|
eq_first <= current.eq_first;
|
|
eq_last <= current.eq_last;
|
|
|
|
eq_out.i <= shift_right(eq_invd.i, 2);
|
|
eq_out.q <= shift_right(eq_invd.q, 2);
|
|
|
|
u_chan_inverter: entity work.wlan_channel_inverter
|
|
port map (
|
|
clock => clock,
|
|
reset => reset,
|
|
|
|
first => eq_first,
|
|
last => eq_last,
|
|
|
|
in_channel => eq_chan,
|
|
in_reference => eq_ref,
|
|
|
|
out_inverted => eq_invd,
|
|
done => eq_inv_done
|
|
) ;
|
|
|
|
|
|
out_sample <= eq_sample ;
|
|
|
|
comb : process(all)
|
|
variable idx, ridx : integer ;
|
|
begin
|
|
future <= current ;
|
|
case current.wfsm is
|
|
when IDLE =>
|
|
future.wfsm <= INITIAL_ESTIMATE ;
|
|
|
|
when INITIAL_ESTIMATE =>
|
|
future.eq_first <= '0';
|
|
future.eq_last <= '0';
|
|
|
|
future.eq_chan.valid <= '0';
|
|
future.eq_ref.valid <= '0';
|
|
if( in_done = '1' ) then
|
|
future.wfsm <= UPDATE_ESTIMATE ;
|
|
future.update_index <= ( others => '0' ) ;
|
|
end if ;
|
|
if( in_sample.valid = '1' ) then
|
|
idx := to_integer( current.update_index ) ;
|
|
future.update_index <= current.update_index + 1 ;
|
|
future.eq_chan.i <= in_sample.i;
|
|
future.eq_chan.q <= in_sample.q;
|
|
future.eq_chan.valid <= in_sample.valid;
|
|
|
|
future.eq_ref.i <= to_signed(T2(idx) * 4096, 16);
|
|
future.eq_ref.q <= to_signed(0, 16);
|
|
|
|
if( idx = 0 ) then
|
|
future.eq_first <= '1' ;
|
|
else
|
|
future.eq_first <= '0' ;
|
|
end if ;
|
|
if( idx = 63 ) then
|
|
future.eq_last <= '1' ;
|
|
else
|
|
future.eq_last <= '0' ;
|
|
end if ;
|
|
end if ;
|
|
|
|
when UPDATE_ESTIMATE =>
|
|
future.eq_first <= '0';
|
|
future.eq_last <= '0';
|
|
future.eq_chan.valid <= '0';
|
|
future.eq_ref.valid <= '0';
|
|
if( dfe_sample.valid = '0' ) then
|
|
future.update_index <= ( others => '0' ) ;
|
|
end if ;
|
|
if( dfe_sample.valid = '1' ) then
|
|
idx := to_integer( current.update_index ) ;
|
|
future.update_index <= current.update_index + 1 ;
|
|
future.eq_chan.i <= last_sample.i;
|
|
future.eq_chan.q <= last_sample.q;
|
|
future.eq_chan.valid <= last_sample.valid;
|
|
|
|
future.eq_ref.i <= dfe_sample.i;
|
|
future.eq_ref.q <= dfe_sample.q;
|
|
future.eq_ref.valid <= dfe_sample.valid;
|
|
|
|
if( idx = 0 ) then
|
|
future.eq_first <= '1' ;
|
|
else
|
|
future.eq_first <= '0' ;
|
|
end if ;
|
|
if( idx = 63 ) then
|
|
future.eq_last <= '1' ;
|
|
else
|
|
future.eq_last <= '0' ;
|
|
end if ;
|
|
|
|
end if ;
|
|
|
|
if( init = '1' ) then
|
|
future.wfsm <= IDLE ;
|
|
end if;
|
|
end case ;
|
|
|
|
future.rcupdate_new_eq_sample.valid <= '0' ;
|
|
case current.rfsm is
|
|
when IDLE =>
|
|
future.rfsm <= INITIAL_ESTIMATE ;
|
|
|
|
when INITIAL_ESTIMATE =>
|
|
if( eq_inv_done = '1' ) then
|
|
future.rfsm <= UPDATE_ESTIMATE ;
|
|
end if ;
|
|
if( eq_invd.valid = '1' ) then
|
|
ridx := to_integer( current.rupdate_index ) ;
|
|
future.rupdate_index <= current.rupdate_index + 1 ;
|
|
|
|
future.eq(ridx).i <= resize(shift_right(eq_invd.i, 0), 16 ) ;
|
|
future.eq(ridx).q <= resize(shift_right(eq_invd.q, 0), 16 ) ;
|
|
end if;
|
|
|
|
when UPDATE_ESTIMATE =>
|
|
if( eq_invd.valid = '0' ) then
|
|
future.rupdate_index <= ( others => '0' ) ;
|
|
end if ;
|
|
|
|
if( eq_invd.valid = '1' ) then
|
|
ridx := to_integer( current.rupdate_index ) ;
|
|
future.rupdate_index <= current.rupdate_index + 1 ;
|
|
|
|
future.rcupdate_index <= to_unsigned(ridx, future.rcupdate_index'length);
|
|
future.rcupdate_cur_eq_sample.i <= current.eq(ridx).i ;
|
|
future.rcupdate_cur_eq_sample.q <= current.eq(ridx).q ;
|
|
future.rcupdate_new_eq_sample <= eq_invd ;
|
|
--future.eq(ridx).i <= current.eq(ridx).i / 2 + eq_invd.i / 2 ;
|
|
--future.eq(ridx).q <= current.eq(ridx).q / 2 + eq_invd.q / 2 ;
|
|
--future.eq(ridx).i <= eq_invd.i / 2 ;
|
|
--future.eq(ridx).q <= eq_invd.q / 2 ;
|
|
end if ;
|
|
|
|
if( init = '1' ) then
|
|
future.rfsm <= IDLE ;
|
|
end if;
|
|
end case ;
|
|
|
|
if( current.rcupdate_new_eq_sample.valid = '1' ) then
|
|
future.rcupdate_result_index <= current.rcupdate_index ;
|
|
future.rcupdate_result.i <= current.rcupdate_cur_eq_sample.i / 2 + current.rcupdate_new_eq_sample.i / 2 ;
|
|
future.rcupdate_result.q <= current.rcupdate_cur_eq_sample.q / 2 + current.rcupdate_new_eq_sample.q / 2 ;
|
|
future.rcupdate_result.valid <= '1' ;
|
|
else
|
|
future.rcupdate_result.valid <= '0' ;
|
|
end if ;
|
|
|
|
if( current.rcupdate_result.valid = '1' ) then
|
|
if( current.rcupdate_result_index /= 7 and current.rcupdate_result_index /= 21 and
|
|
current.rcupdate_result_index /= 43 and current.rcupdate_result_index /= 57 ) then
|
|
future.eq(to_integer(current.rcupdate_result_index)).i <= current.rcupdate_result.i ;
|
|
future.eq(to_integer(current.rcupdate_result_index)).q <= current.rcupdate_result.q ;
|
|
end if ;
|
|
end if ;
|
|
|
|
end process ;
|
|
|
|
process( clock )
|
|
variable idx : integer ;
|
|
begin
|
|
if( reset = '1' ) then
|
|
current <= NULL_STATE ;
|
|
in_sample_r <= NULL_SAMPLE ;
|
|
result_sample <= NULL_SAMPLE ;
|
|
elsif( rising_edge( clock ) ) then
|
|
last_sample_r <= in_sample ;
|
|
last_sample_rr <= last_sample_r ;
|
|
last_sample_rrr <= last_sample_rr ;
|
|
last_sample <= last_sample_rrr ;
|
|
in_sample_r.valid <= '0' ;
|
|
|
|
if( init = '1' ) then
|
|
current <= NULL_STATE ;
|
|
bin_index <= ( others => '0' ) ;
|
|
else
|
|
current <= future ;
|
|
end if ;
|
|
|
|
if( in_sample.valid = '1' ) then
|
|
if( current.rfsm = UPDATE_ESTIMATE ) then
|
|
--eq_sample.valid <= in_sample.valid ;
|
|
in_sample_r <= in_sample ;
|
|
idx := to_integer( bin_index ) ;
|
|
bin_index <= bin_index + 1 ;
|
|
equalizer_coeff_sample <= current.eq(idx);
|
|
--eq_sample.i <= resize(shift_right(in_sample.i * current.eq(idx).i - in_sample.q * current.eq(idx).q, 12), 16 ) ;
|
|
--eq_sample.q <= resize(shift_right(in_sample.i * current.eq(idx).q + in_sample.q * current.eq(idx).i, 12), 16 ) ;
|
|
--eq_sample.i <= in_sample.i;--resize(shift_right(in_sample.i * current.eq(idx).i - in_sample.q * current.eq(idx).q, 12), 16 ) ;
|
|
--eq_sample.q <= in_sample.q;--resize(shift_right(in_sample.i * current.eq(idx).q + in_sample.q * current.eq(idx).i, 12), 16 ) ;
|
|
end if ;
|
|
end if ;
|
|
|
|
if( in_sample_r.valid = '1' ) then
|
|
result_sample.i <= resize(shift_right(in_sample_r.i * equalizer_coeff_sample.i - in_sample_r.q * equalizer_coeff_sample.q, 12), 16 ) ;
|
|
result_sample.q <= resize(shift_right(in_sample_r.i * equalizer_coeff_sample.q + in_sample_r.q * equalizer_coeff_sample.i, 12), 16 ) ;
|
|
result_sample.valid <= '1' ;
|
|
else
|
|
result_sample.valid <= '0' ;
|
|
end if ;
|
|
|
|
eq_sample <= result_sample ;
|
|
end if ;
|
|
end process ;
|
|
|
|
end architecture ;
|