bladeRF-wiphy/fpga/vhdl/wlan_channel_inverter.vhd
2020-12-30 23:19:51 -08:00

159 lines
5.5 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 work ;
use work.wlan_p.all ;
entity wlan_channel_inverter is
port (
clock : in std_logic ;
reset : in std_logic ;
first : in std_logic ;
last : in std_logic ;
in_channel : in wlan_sample_t ;
in_reference : in wlan_sample_t ;
out_inverted : out wlan_sample_t ;
done : out std_logic
) ;
end entity ;
architecture arch of wlan_channel_inverter is
-- Sequence is either -1, 0 or 1, so no need to store extra precision
type seq_lut_t is array(natural range <>) of integer range -1 to 1 ;
-- Convert from real type to LUT type
function create_long_seq_lut return seq_lut_t is
variable rv : seq_lut_t(LONG_SEQ_FREQ'range) ;
begin
for i in rv'range loop
rv(i) := integer(LONG_SEQ_FREQ(i).re) ;
end loop ;
return rv ;
end function ;
constant LONG_SEQ_LUT : seq_lut_t := create_long_seq_lut ;
-- TODO: Make this a feedback signal
signal snr_bias : unsigned(15 downto 0) := to_unsigned( 1, 16 ) ;
signal ref_x_conjh_i : signed(31 downto 0) ;
signal ref_x_conjh_q : signed(31 downto 0) ;
signal ref_x_conjh_valid : std_logic ;
signal magsq : unsigned(31 downto 0) ;
signal magsq_valid : std_logic ;
signal last_reg : std_logic ;
signal div_i : signed(31 downto 0) ;
signal div_q : signed(31 downto 0) ;
signal div_valid : std_logic ;
signal div_done : std_logic ;
begin
-- Incoming channel is in the frequency domain, H(t),
-- with the reference signal, T2(t). The inversion of this
-- is to then take T2(t) * H*(t) / ( |H(t)|^2 + NSR )
calc_ref_x_conjh : process(clock, reset)
variable count : natural range 0 to LONG_SEQ_FREQ'high := 0 ;
begin
if( reset = '1' ) then
count := 0 ;
ref_x_conjh_i <= (others =>'0') ;
ref_x_conjh_q <= (others =>'0') ;
ref_x_conjh_valid <= '0' ;
last_reg <= '0' ;
elsif( rising_edge(clock) ) then
last_reg <= last ;
ref_x_conjh_valid <= in_channel.valid ;
ref_x_conjh_i <= in_channel.i*in_reference.i + in_channel.q*in_reference.q ;
ref_x_conjh_q <= in_channel.i*in_reference.q - in_channel.q*in_reference.i ;
if( in_channel.valid = '1' ) then
if( first = '1' ) then
count := 1 ;
else
if( count < LONG_SEQ_LUT'high ) then
count := count + 1 ;
else
assert last = '1'
report "Last not high when expected for channel estimate"
severity error ;
count := 0 ;
end if ;
end if ;
end if ;
end if ;
end process ;
calc_magsq : process(clock, reset)
variable sample : wlan_sample_t := ( (others =>'0'), (others =>'0'), '0' );
begin
if( reset = '1' ) then
magsq <= (others =>'0') ;
magsq_valid <= '0' ;
sample := ( (others =>'0'), (others =>'0'), '0' ) ;
elsif( rising_edge(clock) ) then
magsq_valid <= in_channel.valid ;
if( in_channel.valid = '1' ) then
sample.i := resize(in_channel.i,sample.i'length) ;
sample.q := resize(in_channel.q,sample.q'length) ;
magsq <= resize(shift_right(unsigned(std_logic_vector(in_channel.i*in_channel.i + in_channel.q*in_channel.q)),12),magsq'length)+snr_bias ;
end if ;
end if ;
end process ;
-- Divide each value
U_div : entity work.wlan_divide
generic map (
SAMPLE_WIDTH => ref_x_conjh_i'length,
DENOM_WIDTH => magsq'length,
NUM_PIPELINE => magsq'length
) port map (
clock => clock,
reset => reset,
in_i => ref_x_conjh_i,
in_q => ref_x_conjh_q,
in_denom => magsq,
in_valid => magsq_valid,
in_done => last_reg,
out_i => div_i,
out_q => div_q,
out_valid => div_valid,
out_done => div_done
) ;
-- Outputs
out_inverted.i <= resize(div_i,out_inverted.i'length) ;
out_inverted.q <= resize(div_q,out_inverted.q'length) ;
out_inverted.valid <= div_valid ;
done <= div_done ;
end architecture ;