bladeRF-wiphy/fpga/ip/nuand/cordic.vhd
2020-12-30 23:19:51 -08:00

202 lines
8.0 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 ;
package cordic_p is
-- Vectoring mode forces outputs.y to 0
-- Rotation mode forces outputs.z to 0
type cordic_mode_t is (CORDIC_ROTATION, CORDIC_VECTORING) ;
-- TODO: Make this a generic package
type cordic_xyz_t is record
x : signed(15 downto 0) ;
y : signed(15 downto 0) ;
z : signed(15 downto 0) ;
valid : std_logic ;
end record ;
end package ; -- cordic_p
library ieee ;
use ieee.std_logic_1164.all ;
use ieee.numeric_std.all ;
use ieee.math_real.all ;
library work ;
use work.cordic_p.all ;
entity cordic is
port (
clock : in std_logic ;
reset : in std_logic ;
mode : in cordic_mode_t ;
inputs : in cordic_xyz_t ;
normalized : out cordic_xyz_t ;
outputs : out cordic_xyz_t
) ;
end entity ; -- cordic
architecture arch of cordic is
-- TODO: Make this generic
constant NUM_STAGES : natural := 14 ;
-- TODO: Use the VHDL-2008 integer_vector
type integer_array_t is array(natural range <>) of integer ;
-- Each stage of the CORDIC is atan(2^(-i))
function calculate_cordic_table return integer_array_t is
variable rv : integer_array_t(0 to NUM_STAGES-1) := (others => 0) ;
begin
for i in 0 to rv'high loop
rv(i) := integer(round((2.0**NUM_STAGES)*arctan(2.0**(-i))/MATH_PI)) ;
end loop ;
return rv ;
end function ;
-- Constant array
constant K : integer_array_t := calculate_cordic_table ;
-- The array for CORDIC stages
type xyzs_t is array(0 to NUM_STAGES-1) of cordic_xyz_t ;
-- ALl the XYZ's for the CORDIC stages
signal xyzs : xyzs_t ;
signal nxyzs : xyzs_t ;
signal nflip : std_logic_vector(0 to NUM_STAGES-1) ;
begin
rotate : process(clock, reset)
begin
if( reset = '1' ) then
xyzs <= (others =>(x => (others =>'0'), y => (others =>'0'), z => (others =>'0'), valid => '0')) ;
nxyzs <= (others =>(x => (others =>'0'), y => (others =>'0'), z => (others =>'0'), valid => '0')) ;
nflip <= (others => '0') ;
elsif( rising_edge( clock ) ) then
-- First stage will rotate the vector to be within -pi/2 to pi/2
xyzs(0).valid <= inputs.valid ;
nxyzs(0).valid <= inputs.valid ;
if( inputs.valid = '1' ) then
case mode is
when CORDIC_ROTATION =>
-- Make sure we're only rotating -pi/2 to pi/2 and adjust accordingly
if( inputs.z > 2**(NUM_STAGES-1) ) then
xyzs(0).x <= -inputs.x ;
xyzs(0).y <= -inputs.y ;
xyzs(0).z <= inputs.z - 2**NUM_STAGES ;
elsif( inputs.z < -(2**(NUM_STAGES-1)) ) then
xyzs(0).x <= -inputs.x ;
xyzs(0).y <= -inputs.y ;
xyzs(0).z <= inputs.z + 2**NUM_STAGES ;
else
xyzs(0).x <= inputs.x ;
xyzs(0).y <= inputs.y ;
xyzs(0).z <= inputs.z ;
end if ;
when CORDIC_VECTORING =>
nxyzs(0).x <= to_signed(1243, nxyzs(0).x'length) ;
nxyzs(0).y <= (others => '0') ;
nflip(0) <= '1' ;
-- Make sure we're in the 1st or 4th quadrant only
if( inputs.x < 0 and inputs.y < 0 ) then
xyzs(0).x <= -inputs.x ;
xyzs(0).y <= -inputs.y ;
xyzs(0).z <= inputs.z - 2**(NUM_STAGES) ;
elsif( inputs.x < 0 ) then
xyzs(0).x <= -inputs.x ;
xyzs(0).y <= -inputs.y ;
xyzs(0).z <= inputs.z + 2**(NUM_STAGES) ;
else
xyzs(0).x <= inputs.x ;
xyzs(0).y <= inputs.y ;
xyzs(0).z <= inputs.z ;
nflip(0) <= '0' ;
end if ;
end case ;
end if ;
-- Run through all the other stages
for i in 0 to xyzs'high-1 loop
xyzs(i+1).valid <= xyzs(i).valid ;
nxyzs(i+1).valid <= nxyzs(i).valid ;
nflip(i+1) <= nflip(i) ;
if( xyzs(i).valid = '1' ) then
case mode is
when CORDIC_ROTATION =>
if( xyzs(i).z < 0 ) then
xyzs(i+1).x <= xyzs(i).x + shift_right(xyzs(i).y, i) ;
xyzs(i+1).y <= xyzs(i).y - shift_right(xyzs(i).x, i) ;
xyzs(i+1).z <= xyzs(i).z + K(i) ;
else
xyzs(i+1).x <= xyzs(i).x - shift_right(xyzs(i).y, i) ;
xyzs(i+1).y <= xyzs(i).y + shift_right(xyzs(i).x, i) ;
xyzs(i+1).z <= xyzs(i).z - K(i) ;
end if ;
when CORDIC_VECTORING =>
if( xyzs(i).y < 0 ) then
xyzs(i+1).x <= xyzs(i).x - shift_right(xyzs(i).y, i) ;
xyzs(i+1).y <= xyzs(i).y + shift_right(xyzs(i).x, i) ;
xyzs(i+1).z <= xyzs(i).z - K(i) ;
nxyzs(i+1).x <= nxyzs(i).x + shift_right(nxyzs(i).y, i) ;
nxyzs(i+1).y <= nxyzs(i).y - shift_right(nxyzs(i).x, i) ;
else
xyzs(i+1).x <= xyzs(i).x + shift_right(xyzs(i).y, i) ;
xyzs(i+1).y <= xyzs(i).y - shift_right(xyzs(i).x, i) ;
xyzs(i+1).z <= xyzs(i).z + K(i) ;
nxyzs(i+1).x <= nxyzs(i).x - shift_right(nxyzs(i).y, i) ;
nxyzs(i+1).y <= nxyzs(i).y + shift_right(nxyzs(i).x, i) ;
end if ;
end case ;
end if ;
end loop ;
-- Output stage
outputs <= xyzs(xyzs'high) ;
end if ;
end process ;
normalized_output : process(clock, reset)
begin
if( reset = '1' ) then
normalized <= (x => (others =>'0'), y => (others =>'0'), z => (others =>'0'), valid => '0') ;
elsif( rising_edge( clock ) ) then
normalized.valid <= '0' ;
if( nxyzs(nxyzs'high).valid = '1' ) then
if( nflip(nflip'high) = '1' ) then
normalized.x <= - nxyzs(nxyzs'high).x ;
normalized.y <= - nxyzs(nxyzs'high).y ;
else
normalized.x <= nxyzs(nxyzs'high).x ;
normalized.y <= nxyzs(nxyzs'high).y ;
end if ;
normalized.valid <= '1' ;
end if ;
end if ;
end process ;
end architecture ; -- arch