Tuesday, February 22, 2011

IQ Phase and Gain Correction - Testbench

Below is the testbench for the IQ Phase and Gain Correction.

As you can see, there's a lot more going on in the testbench than in the block
for the algorithm. This is because the testbench has to generate the test
signals and provide the clock and establish the various settings for the test,
and that all adds up in this case to more lines of code.


On the list of improvements is to move the I and Q signal creation into a
procedure. This encapsulates code and will make the testbench a lot more
readable.


Another obvious improvement is to figure out exactly why the gain correction
isn't working yet.


More soon,
-Michelle W5NYV


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

entity IQGainPhaseCorrection_testbench is
end entity;

architecture IQGainPhaseCorrection_testbench_arch of
IQGainPhaseCorrection_testbench is
--declare the DUT as a component.
component IQGainPhaseCorrection is
generic(width :natural);
port(
clk :in std_logic;
x1 :in signed(width downto 0);
y1 :in signed(width downto 0);
gain_error :out signed(width downto 0);
gain_lock :out bit;
phase_error :out signed(width downto 0);
phase_lock :out bit;
corrected_x1 :out signed(width downto 0);
corrected_y1 :out signed(width downto 0)
);
end component;

--provide signals to run the DUT.
signal clk_tb : std_logic := '0';
signal x1_tb : signed(31 downto 0);
signal y1_tb : signed(31 downto 0);
signal gain_error_tb : signed(31 downto 0);
signal gain_lock_tb : bit;
signal phase_error_tb : signed(31 downto 0);
signal phase_lock_tb : bit;
signal corrected_x1_tb : signed(31 downto 0);
signal corrected_y1_tb : signed(31 downto 0);

begin

--connect the testbench signal to the component
DUT:IQGainPhaseCorrection
generic map(
width => 31
)
port map(
clk => clk_tb,
x1 => x1_tb,
y1 => y1_tb,
gain_error => gain_error_tb,
gain_lock => gain_lock_tb,
phase_error => phase_error_tb,
phase_lock => phase_lock_tb,
corrected_x1 => corrected_x1_tb,
corrected_y1 => corrected_y1_tb
);


--create x1 and y1. MTreseler says, "sin in vhdl I use use ieee.math_real.all
and cast to integer."


CREATE_X1_I: process
variable angle : real;
variable local_x1 : real;
variable sgma : real :=0.01; --sigma of noise
variable amplitude : real := 1.0; --amplitude
variable freq : real := 0.03; --relative frequency
variable u_noise: real; --uniform distribution noise
variable n_noise: real := 0.0; --normal distribution noise

variable seed1 : positive := 10;
variable seed2 : positive := 200;

--loop controls
variable make_normal_count : integer := 12;
variable n_dat : integer := 4;

variable int_x1: integer;

begin
for n_dat_count in 0 to n_dat loop
--make a random number
uniform(seed1, seed2, u_noise);
report "Random uniform noise in I creation is " & real'image(u_noise) &
".";

for normal_count in 0 to make_normal_count loop
--turn the uniform distributed number
--into a normally distributed number
--by using the central limit theorem.
n_noise := n_noise + u_noise;
end loop;
n_noise := n_noise - (0.5)*(real(make_normal_count)); --normal
distribution with a mean of zero
report "Random normal noise in I creation is " & real'image(n_noise) &
".";
n_noise := n_noise/(real(make_normal_count)); --max values reduced?
report "Random normal noise (normalized?) in I creation is " &
real'image(n_noise) & ".";

local_x1 := amplitude*cos(2.0*math_pi*(real(n_dat_count))*freq) +
sgma*(n_noise);
--local_x1 := cos(2.0*math_pi*(real(n_dat_count))*freq); --simpler
version

--AGC scaling
local_x1 := local_x1/(1.01);

report "local_x1 is " & real'image(local_x1) & ".";
--x1_tb <= local_x1; --somehow get real turned into signed.
-- 1. rescale to 0..(nearly)4096, find integer part
--int_x1 := INTEGER(TRUNC(local_x1*4294967296.0)); -- from random_vector


int_x1 := integer(trunc(local_x1*(2.0**31.0))); --scaled

report "integer version of x1 is " & integer'image(int_x1) & ".";
-- 2. convert to signed
x1_tb <= (to_signed(int_x1, x1_tb'LENGTH));


end loop;
wait;

end process CREATE_X1_I;


CREATE_Y1_Q: process
variable angle : real;
variable local_y1 : real;
variable e1 : real := 0.1; --gain error
variable a1 : real := (10.0*math_pi)/180.0; --phase error of 10 degrees
variable sgma : real := 0.01; --sigma of noise
variable amplitude : real := 1.0; --amplitude
variable freq : real := 0.03; --relative frequency
variable u_noise: real; --uniformly distributed noise
variable n_noise: real := 0.0; --normally distributed noise
variable seed1 : positive := 1;
variable seed2 : positive := 2;

--loop controls
variable make_normal_count : integer := 12;
variable n_dat : integer := 4;

variable int_y1: integer;
begin

for n_dat_count in 0 to n_dat loop
--make a random number
uniform(seed1, seed2, u_noise);
report "Random uniform noise in I creation is " & real'image(u_noise) &
".";

for normal_count in 0 to make_normal_count loop
--turn the uniform distributed number
--into a normally distributed number
--by using the central limit theorem.
n_noise := n_noise + u_noise;
end loop;
n_noise := n_noise - (0.5)*(real(make_normal_count));
n_noise := n_noise/(real(make_normal_count)); --reduce size of noise
report "Random normal noise in I creation is " & real'image(n_noise) &
".";
n_noise := n_noise/(real(make_normal_count)); --max values reduced?
report "Random normal noise (normalized?) in I creation is " &
real'image(n_noise) & ".";

local_y1 := amplitude*(1.0 + e1)*cos(2.0*math_pi*(real(n_dat_count))*freq +
a1) + sgma*(n_noise);

--AGC scaling
local_y1 := local_y1/(1.01);

--int_y1 := INTEGER(TRUNC(local_y1*4294967296.0)); -- from random_vector
int_y1 := integer(trunc(local_y1*(2.0**31.0))); --scaled
report "integer version of y1 is " & integer'image(int_y1) & ".";

-- 2. convert to signed
y1_tb <= (to_signed(int_y1, y1_tb'LENGTH));

end loop;
wait;

end process CREATE_Y1_Q;


DRIVE_CLOCK:process
begin
clk_tb <= not clk_tb;
wait for 50 ns;
end process;

end IQGainPhaseCorrection_testbench_arch;

No comments: