---------------------------------------------------------------------------------- -- Company: -- Engineer: -- -- Create Date: 10:36:54 04/05/2009 -- Design Name: -- Module Name: dft_core - Behavioral -- Project Name: -- Target Devices: -- Tool versions: -- Description: -- -- Dependencies: -- -- Revision: -- Revision 0.01 - File Created -- Additional Comments: -- ---------------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; use IEEE.NUMERIC_STD.ALL; ---- Uncomment the following library declaration if instantiating ---- any Xilinx primitives in this code. --library UNISIM; --use UNISIM.VComponents.all; entity DFT_CORE is generic ( DFT_SLV_DWIDTH: integer := 32; DFT_MST_AWIDTH: integer := 32; DFT_MST_DWIDTH: integer := 32 ); port ( Clk: in std_logic; Reset: in std_logic; dft_busy : in std_logic; dft_go : out std_logic; dft_intern_req : out std_logic; dft_time_addr : in std_logic_vector(0 to DFT_SLV_DWIDTH-1); dft_real_addr : in std_logic_vector(0 to DFT_SLV_DWIDTH-1); dft_imag_addr : in std_logic_vector(0 to DFT_SLV_DWIDTH-1); dft_read_cmd : out std_logic; dft_write_cmd : out std_logic; dft_out_addr : out std_logic_vector(0 to DFT_MST_AWIDTH-1); dft_read_data : in std_logic_vector(0 to DFT_MST_DWIDTH-1); dft_write_data : out std_logic_vector(0 to DFT_MST_DWIDTH-1); dft_read_data_xfer : in std_logic; dft_write_data_xfer : in std_logic; dft_interrupt_done : out std_logic ); end DFT_CORE; architecture Behavioral of DFT_CORE is component sin_cos_core port ( THETA: IN std_logic_VECTOR(8 downto 0); SINE: OUT std_logic_VECTOR(13 downto 0); COSINE: OUT std_logic_VECTOR(13 downto 0)); end component; type dft_states is (start_state, out_loop_state, in_loop_state, sin_cos_state, accum_state, inc_i_state, write_state, done_state, final_state); signal current_state, next_state : dft_states; signal out_data_cur_reg, out_data_next_reg : std_logic_vector(0 to DFT_MST_DWIDTH-1); signal real_data_cur_reg, real_data_next_reg : std_logic_vector(0 to DFT_MST_DWIDTH-1); signal imag_data_cur_reg, imag_data_next_reg : std_logic_vector(0 to DFT_MST_DWIDTH-1); signal theta_cur_reg, theta_next_reg : std_logic_vector(8 downto 0); signal sine_data, cosine_data : std_logic_vector(13 downto 0); signal cos_cur_reg, cos_next_reg : std_logic_vector(13 downto 0); signal sin_cur_reg, sin_next_reg : std_logic_vector(13 downto 0); signal i_cur_reg, i_next_reg, j_cur_reg, j_next_reg : std_logic_vector(15 downto 0); signal write_xfer_cur_reg, write_xfer_next_reg : std_logic; signal ri_cnter_cur_reg, ri_cnter_next_reg : std_logic_vector(0 to 1); signal read_data_cur_reg, read_data_next_reg : std_logic_vector(0 to DFT_MST_DWIDTH-1); signal num_pts_cur_reg, num_pts_next_reg : std_logic_vector(15 downto 0); signal read_num_pts_cur_reg, read_num_pts_next_reg : std_logic; signal half_num_pts : std_logic_vector(15 downto 0); signal interrupt_cur_reg, interrupt_next_reg : std_logic; signal read_data_signed : signed(15 downto 0); signal cos_signed : signed(13 downto 0); signal sin_signed : signed(13 downto 0); signal real_data_signed : signed(0 to DFT_MST_DWIDTH-1); signal imag_data_signed : signed(0 to DFT_MST_DWIDTH-1); begin sin_cos_core_inst : sin_cos_core port map ( THETA => theta_cur_reg, SINE => sine_data, COSINE => cosine_data); -- ============================================================================================ -- DFT STATE MACHINE: Sequential Logic -- ============================================================================================ DFT_SEQ : process( Clk, Reset ) is begin if ( Clk'event and Clk = '1' ) then if ( Reset = '1' ) then current_state <= start_state; out_data_cur_reg <= (others => '0'); real_data_cur_reg <= (others => '0'); imag_data_cur_reg <= (others => '0'); i_cur_reg <= (others => '0'); j_cur_reg <= (others => '0'); write_xfer_cur_reg <= '0'; ri_cnter_cur_reg <= "00"; theta_cur_reg <= (others => '0'); cos_cur_reg <= (others => '0'); sin_cur_reg <= (others => '0'); read_data_cur_reg <= (others => '0'); read_num_pts_cur_reg <= '0'; num_pts_cur_reg <= (others => '0'); interrupt_cur_reg <= '0'; else current_state <= next_state; out_data_cur_reg <= out_data_next_reg; real_data_cur_reg <= real_data_next_reg; imag_data_cur_reg <= imag_data_next_reg; i_cur_reg <= i_next_reg; j_cur_reg <= j_next_reg; write_xfer_cur_reg <= write_xfer_next_reg; ri_cnter_cur_reg <= ri_cnter_next_reg; theta_cur_reg <= theta_next_reg; cos_cur_reg <= cos_next_reg; sin_cur_reg <= sin_next_reg; read_data_cur_reg <= read_data_next_reg; read_num_pts_cur_reg <= read_num_pts_next_reg; num_pts_cur_reg <= num_pts_next_reg; interrupt_cur_reg <= interrupt_next_reg; end if; end if; end process; -- Connect the output data bus to the register holding the value to be written to -- memory. dft_write_data <= out_data_cur_reg; -- Divide the number of points read from the slave register by 2. half_num_pts <= '0' & num_pts_cur_reg(15 downto 1); dft_interrupt_done <= interrupt_cur_reg; -- ============================================================================================ -- DFT STATE MACHINE: Combinational Logic -- ============================================================================================ DFT_PROCESS : process( current_state, dft_busy, dft_time_addr, dft_real_addr, dft_imag_addr, out_data_cur_reg, real_data_cur_reg, imag_data_cur_reg, dft_read_data_xfer, dft_write_data_xfer, write_xfer_cur_reg, i_cur_reg, j_cur_reg, ri_cnter_cur_reg, dft_read_data, read_data_cur_reg, read_num_pts_cur_reg, num_pts_cur_reg, interrupt_cur_reg, read_data_signed, cos_signed, sin_signed, real_data_signed, imag_data_signed ) is begin -- Default assignments. dft_go <= '0'; dft_intern_req <= '0'; dft_read_cmd <= '0'; dft_write_cmd <= '0'; dft_out_addr <= (others => '0'); out_data_next_reg <= out_data_cur_reg; real_data_next_reg <= real_data_cur_reg; imag_data_next_reg <= imag_data_cur_reg; i_next_reg <= i_cur_reg; j_next_reg <= j_cur_reg; write_xfer_next_reg <= write_xfer_cur_reg; ri_cnter_next_reg <= ri_cnter_cur_reg; theta_next_reg <= theta_cur_reg; cos_next_reg <= cos_cur_reg; sin_next_reg <= sin_cur_reg; read_data_next_reg <= read_data_cur_reg; read_num_pts_next_reg <= read_num_pts_cur_reg; num_pts_next_reg <= num_pts_cur_reg; interrupt_next_reg <= interrupt_cur_reg; read_data_signed <= (others => '0'); cos_signed <= (others => '0'); sin_signed <= (others => '0'); real_data_signed <= (others => '0'); imag_data_signed <= (others => '0'); case current_state is -- Wait for data address to become non-zero -- once both are set, go. when start_state => -- I write the number of points to the dft_time_addr software register first -- and then re-write it with the base address of the time domain data. if ( dft_time_addr /= X"00000000" and read_num_pts_cur_reg = '0' ) then read_num_pts_next_reg <= '1'; num_pts_next_reg <= dft_time_addr(16 to 31); end if; if ( dft_time_addr = X"00000000" or dft_real_addr = X"00000000" or dft_imag_addr = X"00000000" ) then next_state <= start_state; else next_state <= out_loop_state; end if; -- OUTTER LOOP: Move through j values. when out_loop_state => -- If after incrementing j, leaves us pointing the the last position + 1, we're done. if ( j_cur_reg = half_num_pts ) then next_state <= done_state; -- Zero out the accumulator registers. else real_data_next_reg <= (others => '0'); imag_data_next_reg <= (others => '0'); next_state <= in_loop_state; end if; -- INNER LOOP: Read a y_value and set the argument for theta in theta reg. when in_loop_state => theta_next_reg <= i_cur_reg*j_cur_reg; if ( dft_busy = '1' ) then next_state <= in_loop_state; else dft_go <= '1'; dft_read_cmd <= '1'; -- dft_out_addr is latched into the master register on next rising edge, so we don't need -- to hold it there. dft_out_addr <= dft_time_addr + (i_cur_reg(13 downto 0) & "00"); next_state <= sin_cos_state; end if; -- Wait for the y data read to complete. Latch the sine/cosine outputs -- note theta is registered above. when sin_cos_state => if ( dft_read_data_xfer = '0' ) then next_state <= sin_cos_state; else -- Remember, the dft_read_data is ONLY valid as long as dft_read_data_xfer is '1' -- MUST be saved -- here. read_data_next_reg <= dft_read_data; cos_next_reg <= cosine_data; sin_next_reg <= sine_data; next_state <= accum_state; end if; -- Accumulate real and imag components. when accum_state => read_data_signed <= signed(read_data_cur_reg(16 to 31)); cos_signed <= signed(cos_cur_reg); sin_signed <= signed(sin_cur_reg); real_data_signed <= signed(real_data_cur_reg); imag_data_signed <= signed(imag_data_cur_reg); real_data_next_reg <= real_data_signed + read_data_signed * cos_signed; imag_data_next_reg <= imag_data_signed + read_data_signed * sin_signed; next_state <= inc_i_state; -- Increment i and loop or break. when inc_i_state => -- If last i, clear i and move to write state. if ( i_cur_reg + 1 = num_pts_cur_reg ) then i_next_reg <= (others => '0'); next_state <= write_state; else i_next_reg <= i_cur_reg + 1; next_state <= in_loop_state; end if; -- Write real/imag values to memory. Always wait for the dft_busy to become '0' -- If dft_busy AND dft_write_data_xfer are BOTH 1, then remember the latter was 1 so we know -- it's okay to move on to the next write. Not sure if this is necessary because I think the -- sequence will always be that dft_busy is 1 until the write request is issued. The -- dft_write_data_xfer will not become 1 until sometime later, after busy is set back to 0 -- because of the time it takes the memory operation to complete. when write_state => dft_intern_req <= '1'; -- wait for busy to be clear -- if the write is being acknowledged at the same time, -- remember this so we can proceed once busy is 0. if ( dft_busy = '1' ) then if ( dft_write_data_xfer = '1' ) then write_xfer_next_reg <= '1'; end if; next_state <= write_state; -- If the write is acknowledged, or was acknowledged in a previous cycle when busy was set, -- or if this is the first write, start write command. elsif ( dft_write_data_xfer = '1' or write_xfer_cur_reg = '1' or ri_cnter_cur_reg = "00" ) then write_xfer_next_reg <= '0'; dft_go <= '1'; dft_write_cmd <= '1'; -- We need to do this twice, once for the real value and once for the imag value. if ( ri_cnter_cur_reg = "00" ) then ri_cnter_next_reg <= "01"; -- dft_out_addr is latched into the master register on next rising edge, so we don't need -- to hold it there. dft_out_addr <= dft_real_addr + (j_cur_reg(13 downto 0) & "00"); -- Latch data to be written on rising edge. out_data_next_reg <= real_data_cur_reg; next_state <= write_state; -- imag write. elsif ( ri_cnter_cur_reg = "01" ) then ri_cnter_next_reg <= "10"; dft_out_addr <= dft_imag_addr + (j_cur_reg(13 downto 0) & "00"); out_data_next_reg <= imag_data_cur_reg; next_state <= write_state; -- Both writes completed, inc j and start again. else ri_cnter_next_reg <= "00"; j_next_reg <= j_cur_reg + 1; next_state <= out_loop_state; end if; else next_state <= write_state; end if; -- Completed. when done_state => interrupt_next_reg <= '1'; next_state <= final_state; when final_state => interrupt_next_reg <= '0'; next_state <= final_state; when others => next_state <= start_state; end case; end process; end Behavioral;