Forum Discussion
Altera_Forum
Honored Contributor
17 years agoThis is where VHDL is significantly more flexible than Verilog. Since VHDL supports all the floating point operations naturally and doesn't complain when a "real" type is being manipulated during synthesis, you can actually code this up extremely generically.
library ieee ;
use ieee.std_logic_1164.all ;
use ieee.numeric_std.all ;
use ieee.math_real.all ;
entity complex_tone_ram is
generic (
FREQ_SAMPLE : real := 50.0e6 ;
FREQ_TONE : real := 4.096e6 ;
Q : positive := 12 ;
WIDTH : positive := 15
) ;
port (
clock : in std_logic ;
sreset : in std_logic ;
enable : in std_logic ;
i_out : out signed(WIDTH-1 downto 0) ;
q_out : out signed(WIDTH-1 downto 0)
) ;
end entity ; -- complex_tone_ram
architecture arch of complex_tone_ram is
-- Naive search for LCM between sample rate and tone to be generated
function calculate_num_points( sample_rate, tone : real ) return integer is
variable retval : integer ;
begin
retval := 1 ;
while( (tone*real(retval)/sample_rate) mod 1.0 /= 0.0 ) loop
retval := retval + 1 ;
end loop ;
return retval ;
end function ;
-- A constant to hold the number of points
constant NUM_POINTS : integer := calculate_num_points( FREQ_SAMPLE, FREQ_TONE ) ;
-- The RAM type that will be inferred
type ram_t is array(NUM_POINTS-1 downto 0) of signed(WIDTH-1 downto 0) ;
-- A function to populate the RAM
function populate_ram( phase_increment, scale : real ) return ram_t is
variable retval : ram_t := (others =>(others =>'0')) ;
begin
for i in retval'range loop
retval(i) := to_signed(integer(round(scale*cos(phase_increment*real(i)))), retval(i)'length) ;
end loop ;
return retval ;
end function ;
-- The RAM signal itself
signal ram : ram_t := populate_ram( FREQ_TONE/FREQ_SAMPLE * MATH_2_PI, real(2**Q) ) ;
-- Find the location which is 90 degrees out of phase with 1.0
function find_q_start_address( ram : ram_t ) return integer is
variable retval : integer ;
begin
retval := 0 ;
for i in ram'range loop
if( ram(i) = 0 and ram(i+1) > 0 ) then
retval := i ;
end if ;
end loop ;
return retval ;
end function ;
constant I_START_ADDRESS : integer := 0 ;
constant Q_START_ADDRESS : integer := find_q_start_address( populate_ram( FREQ_TONE/FREQ_SAMPLE * MATH_2_PI, real(2**Q) ) ) ;
-- I and Q address signals
signal i_address : integer range ram_t'range := I_START_ADDRESS ;
signal q_address : integer range ram_t'range := Q_START_ADDRESS ;
begin
generate_addresses : process( clock )
begin
if( rising_edge( clock ) ) then
if( sreset = '1' ) then
i_address <= I_START_ADDRESS ;
q_address <= Q_START_ADDRESS ;
else
if( enable = '1' ) then
if( i_address = ram'high ) then
i_address <= 0 ;
else
i_address <= i_address + 1 ;
end if ;
if( q_address = ram'high ) then
q_address <= 0 ;
else
q_address <= q_address + 1 ;
end if ;
end if ;
end if ;
end if ;
end process ;
create_tone_output : process( clock )
begin
if( rising_edge( clock ) ) then
i_out <= ram(i_address) ;
q_out <= ram(q_address) ;
end if ;
end process ;
end architecture ; -- arch
All will be done at compile time for any particular complex frequency. Obviously this can be adapted to be a real-only tone generator as well. NOTE: Quartus II has an issue with performing a loop more than 10,000 times, but I believe that check can be disabled if you require. EDIT: Also note the Q starting address with be in-phase with the I starting address if the number of sample points is not divisible by 4.