-------------------------------------------------------------------------------
-- Company:
-- Engineer:       Jason M. Blevins
-- 
-- Create Date:    19:38:12 11/10/2016 
-- Design Name: 
-- Module Name:    dds_pulse_gen - behavioral
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description:
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Revision 0.02 - Added Sweep Functionality 10-17-2017
-- Additional Comments:
--  * All information is proprietary/confidential *
--
--  A 256-bit control word (32x8 right shifted in) is read from an external FIFO.
--  The control word contains all information needed to create a pulse
--
--    RESERVED                                = fifo_data_r(255 downto 241)   --  15-bits
--    SWAP IQ CHANELS                         = fifo_data_r(240)              --  1-bit    set to '1' for negative frequencies
--    SCALE FACTOR TO SCALE OUTPUT AMPLITUDE  = fifo_data_r(239 downto 224)   --  16-bits, (0,1], full-scale = 1.000000000000000
--    DDS PHASE OFFSET                        = fifo_data_r(223 downto 192)   --  32-bits, reserved, set to 0x0000_0000
--    DDS PHASE INCREMENT                     = fifo_data_r(191 downto 160)   --  32-bits
--    # DDS SAMPLES                           = fifo_data_r(159 downto 128)   --  32-bits
--    # MIDPOINT SAMPLES PRIOR TO PULSE       = fifo_data_r(127 downto 96)    --  32-bits
--    RESERVED                                = fifo_data_r(95 downto 88)     --  8-bit
--    DDS PHASE INCREMENT STEP                = fifo_data_r(87 downto 64)     --  24-bits (2's complement SIGNED !!)
--    RESERVED                                = fifo_data_r(63 downto 48)     --  16-bits
--    DDS PHASE INCREMENT DWELL               = fifo_data_r(47 downto 32)     --  16-bits
--    RESERVED                                = fifo_data_r(31 downto 0)      --  32-bits
--
-------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
--Library UNIMACRO;
--use UNIMACRO.vcomponents.all;
USE IEEE.NUMERIC_STD.ALL;

entity dds_pulse_gen is
  port(
    clk_in        : in  std_logic;
    rst_in        : in  std_logic;
    fifo_data_in  : in  std_logic_vector(31 downto 0);
    fifo_dval_in  : in  std_logic;
    fifo_empty_in : in  std_logic;
    fifo_rden_out : out std_logic;
    holdoff_in    : in  std_logic;
    data_out      : out std_logic_vector(31 downto 0);
    dval_out      : out std_logic    
    );
end entity;
  
architecture mixed of dds_pulse_gen is
  
  component mult_16signed_x_16unsigned_latency3
    port(
      clk   : in  std_logic;
      a     : in  std_logic_vector(15 downto 0);
      b     : in  std_logic_vector(15 downto 0);
      ce    : in  std_logic;
      sclr  : in  std_logic;
      p     : out std_logic_vector(15 downto 0)
      );
  end component;

  component dds_latency10
    port(
      -- ce      : in  std_logic;
      -- clk     : in  std_logic;
      -- sclr    : in  std_logic;
      -- pinc_in : in  std_logic_vector(31 downto 0);
      -- poff_in : in  std_logic_vector(31 downto 0);
      -- rdy     : out std_logic;
      -- cosine  : out std_logic_vector(15 downto 0);
      -- sine    : out std_logic_vector(15 downto 0)
      aclk                : IN STD_LOGIC;
      aclken              : IN STD_LOGIC;
      aresetn             : IN STD_LOGIC;
      s_axis_phase_tvalid : IN STD_LOGIC;
      s_axis_phase_tdata  : IN STD_LOGIC_VECTOR(31 DOWNTO 0);
      m_axis_data_tvalid  : OUT STD_LOGIC;
      m_axis_data_tdata   : OUT STD_LOGIC_VECTOR(31 DOWNTO 0) 
      );
  end component;

  component addsub
    port (
      a   : in std_logic_vector(31 downto 0); -- unsigned
      b   : in std_logic_vector(23 downto 0); -- signed
      --clk : in std_logic;
      s   : out std_logic_vector(31 downto 0) -- latency=0 ?? Not practical in HW, design should be updated to allow for latency.
      );
  end component;  
  
  constant  MIDPOINT        : std_logic_vector(31 downto 0) :=  x"00000000";
  
  type state_type is (s0, s0a, s0b, s0c, s1, s2, s3);
  signal  state             : state_type;
  signal  state_r           : state_type;
  signal  rst_r             : std_logic :=  '1';
  signal  rstn_r            : std_logic :=  '0';

  signal  cnt1_r            : unsigned(3 downto 0) :=  "0000";
  signal  cnt1              : unsigned(3 downto 0);
  signal  cnt2_r            : unsigned(2 downto 0) :=  "000";
  signal  cnt2              : unsigned(2 downto 0);
  signal  cnt3_r            : unsigned(31 downto 0) :=  x"00000000";
  signal  cnt3              : unsigned(31 downto 0);
  signal  cnt4_r            : unsigned(31 downto 0) :=  x"00000000";
  signal  cnt4              : unsigned(31 downto 0);
  signal  cnt5_r            : unsigned(3 downto 0) :=  "0000";
  signal  cnt5              : unsigned(3 downto 0);
  signal  fifo_data_ce      : std_logic;
  signal  fifo_data_r       : std_logic_vector(255 downto 0);
  signal  fifo_rden         : std_logic;
  --signal  fifo_rden_r       : std_logic :=  '0';
  signal  dval_r            : std_logic :=  '0';
  signal  dval              : std_logic;
  signal  dds_data          : std_logic_vector(31 downto 0);
  signal  data              : std_logic_vector(31 downto 0);
  signal  data_r            : std_logic_vector(31 downto 0);
  signal  idle_sample_cnt_r : unsigned(31 downto 0);
  signal  idle_sample_cnt_r1 : unsigned(31 downto 0);
  signal  dds_sample_cnt_r  : unsigned(31 downto 0);
  signal  dds_sample_cnt_r1  : unsigned(31 downto 0);
  signal  phase_inc_init_r  : std_logic_vector(31 downto 0);
  signal  phase_offset_r    : std_logic_vector(31 downto 0);
  signal  swap_r            : std_logic :=  '0';
  signal  scale_r           : std_logic_vector(15 downto 0);
  signal  mult_dval_r       : std_logic :=  '0';
  signal  data_swap_scaled  : std_logic_vector(31 downto 0);
  signal  data_scaled       : std_logic_vector(31 downto 0);
  signal  dds_ce            : std_logic;
  signal  dds_rst           : std_logic;
  signal  dds_rdy           : std_logic;
  signal  mult_ce           : std_logic;
  signal  mult_ce_pipe_r    : std_logic_vector(1 downto 0)  :=  "00";
  signal  holdoff_r         : std_logic;
  
  --signal  phase_inc_mux_sel : std_logic;
  signal  phase_inc_update_en      : std_logic;
  --signal  phase_inc_mux     : std_logic_vector(31 downto 0);
  signal  phase_inc         : std_logic_vector(31 downto 0);
  signal  phase_inc_r       : std_logic_vector(31 downto 0);
  signal  phase_inc_r1      : std_logic_vector(31 downto 0);
  signal  phase_inc_step_r  : std_logic_vector(23 downto 0) :=  (others => '0');
  signal  phase_inc_dwell_r : unsigned(15 downto 0) :=  x"0000";
  signal  phase_inc_dwell_cnt_r : unsigned(15 downto 0) := x"0000";
  signal  phase_inc_dwell_cnt   : unsigned(15 downto 0);
  signal  phase_inc_addsub  : std_logic_vector(31 downto 0);
  signal  rstn              : std_logic;

begin

  

  i_addsub : addsub
    port map(
      a   =>  phase_inc_r,
      b   =>  phase_inc_step_r,
      --clk =>  clk_in,
      s   =>  phase_inc_addsub
      );

  fifo_rden_out <=  fifo_rden;--fifo_rden_r;
  data_out      <=  data_r;
  dval_out      <=  dval_r;
  
  process(clk_in)
  begin 
    if(rising_edge(clk_in))then
      if(rst_r = '1')then
        state_r <=  s0;
        cnt1_r  <=  (others => '0');
        cnt2_r  <=  (others => '0');
        cnt3_r  <=  (others => '0');
        cnt4_r  <=  (others => '0');
        cnt5_r  <=  (others => '0');
        phase_inc_dwell_cnt_r <= (others => '0');
      else
        state_r <=  state;
        cnt1_r  <=  cnt1;
        cnt2_r  <=  cnt2;
        cnt3_r  <=  cnt3;
        cnt4_r  <=  cnt4;
        cnt5_r  <=  cnt5;
        phase_inc_dwell_cnt_r <=  phase_inc_dwell_cnt;
      end if;
      if(fifo_data_ce = '1')then
        fifo_data_r <=  fifo_data_in & fifo_data_r(255 downto 32);--fifo_data_r(223 downto 0) & fifo_data_in;
      end if;
      rst_r               <=  rst_in;
      rstn_r              <= not(rst_in);
      dval_r              <=  dval;
      phase_offset_r      <=  fifo_data_r(223 downto 192);
      -- phase_inc_init_r    <=  fifo_data_r(191 downto 160);
      dds_sample_cnt_r    <=  unsigned(fifo_data_r(159 downto 128));
      dds_sample_cnt_r1   <=  dds_sample_cnt_r;
      swap_r              <=  fifo_data_r(240);
      scale_r             <=  fifo_data_r(239 downto 224);
      idle_sample_cnt_r   <=  unsigned(fifo_data_r(127 downto 96));
      idle_sample_cnt_r1  <=  idle_sample_cnt_r;
      phase_inc_step_r    <=  fifo_data_r(87 downto 64);
      phase_inc_dwell_r   <=  unsigned(fifo_data_r(47 downto 32));
      data_r              <=  data;
      holdoff_r           <=  holdoff_in;
      phase_inc_r         <=  phase_inc;
      
      if(phase_inc_update_en = '1')then
        phase_inc_r1 <= phase_inc_r;
      end if;
      
    end if;
  end process;
  
 
  phase_inc_init_r    <=  fifo_data_r(191 downto 160);
  
  --  FSM next-state & output process
  process(state_r, cnt1_r, cnt2_r, cnt3_r, cnt4_r, cnt5_r, holdoff_r, fifo_empty_in, fifo_dval_in, phase_inc_init_r, phase_inc_r,
          mult_dval_r, data_swap_scaled, idle_sample_cnt_r1, dds_sample_cnt_r1, phase_inc_dwell_r, phase_inc_dwell_cnt_r, phase_inc_addsub)
  begin
    --defaults
    fifo_rden           <=  '0';
    cnt1                <=  cnt1_r;
    cnt2                <=  cnt2_r;
    cnt3                <=  cnt3_r;
    cnt4                <=  cnt4_r;
    cnt5                <=  cnt5_r;
    state               <=  state_r;
    dds_ce              <=  '0';
    dds_rst             <=  '1';
    dval                <=  mult_dval_r;
    data                <=  data_swap_scaled;
    fifo_data_ce        <=  '0';
    phase_inc_dwell_cnt <=  phase_inc_dwell_cnt_r;
    phase_inc           <=  phase_inc_r;
    phase_inc_update_en <=  '0';   
   
    case state_r is
    
      when s0 =>
        phase_inc_dwell_cnt <=  (others => '0');
        if(fifo_empty_in = '0' and cnt1_r < 8)then
          fifo_rden <=  '1';
          cnt1      <=  cnt1_r +1;
        end if;
        if(fifo_dval_in = '1')then
          fifo_data_ce  <=  '1';
          if(cnt2_r < 7)then
            cnt2  <=  cnt2_r +1;
          else
            cnt2  <=  (others => '0');
            cnt1  <=  (others => '0');
            state <=  s0a;
          end if;
        end if;
        
      -- This state is needed to clock the input shift reg data into next set of regs.
      -- Possibly can be removed to decrease pulse param processing cycles.
      when s0a =>
        state <=  s0b;--s1;
        phase_inc <=  phase_inc_init_r;
        
      -- This state is needed to clock the input shift reg data into next set of regs.
      -- Possibly can be removed to decrease pulse param processing cycles.
      when s0b =>
        state               <=  s1;
        phase_inc_update_en <=  '1';
        phase_inc           <=  phase_inc_addsub;--phase_inc_r + phase_inc_step_r;
        
      -- This state is needed to clock the input shift reg data into next set of regs.
      -- Possibly can be removed to decrease pulse param processing cycles.
      -- when s0c =>
        -- state               <=  s1;
        -- phase_inc_update_en <=  '1';
        -- phase_inc           <=  phase_inc_addsub;--phase_inc_r + phase_inc_step_r;
        
      
      -- Insert midpoint (idle) samples that preceed the pulse.
      when s1 =>
        data  <=  MIDPOINT;
        if(cnt3_r < idle_sample_cnt_r1)then
          if(holdoff_r = '0')then
            cnt3  <=  cnt3_r +1;
            dval  <=  '1';
          end if;
        else
          cnt3  <=  (others => '0');
          if(dds_sample_cnt_r1 > 0)then
            state <=  s2;
          else
            state <=  s0;
          end if;
        end if;
		
      -- Turn on DDS for requested number of samples.
      when s2 =>
        dds_rst <= '0';
        
        if(holdoff_r = '0')then
          if(phase_inc_dwell_cnt_r < phase_inc_dwell_r)then
            phase_inc_dwell_cnt <=  phase_inc_dwell_cnt_r +1;
          else
            phase_inc_dwell_cnt <=  (others => '0');
            phase_inc_update_en <=  '1';
            phase_inc           <=  phase_inc_addsub;--phase_inc_r + phase_inc_step_r;
          end if;
        end if;
        
        if(holdoff_r = '0')then
          dds_ce  <=  '1';
          if(cnt4_r < dds_sample_cnt_r1)then
            cnt4  <=  cnt4_r +1;            
          else
            cnt4  <=  (others => '0');
            state <=  s3;
          end if;
        end if;
        
        -- phase_inc_mux_sel <=  '1';
        -- --phase_inc_en      <=  not(holdoff_r);        
        -- dds_rst           <=  '0';       
        -- if(cnt4_r < dds_sample_cnt_r1)then
          -- if(holdoff_r = '0')then
            -- dds_ce  <=  '1';
            -- cnt4    <=  cnt4_r +1;
            -- if(phase_inc_dwell_cnt_r < phase_inc_dwell_r)then
              -- phase_inc_dwell_cnt <= phase_inc_dwell_cnt_r +1;
            -- else
              -- phase_inc_dwell_cnt <=  x"00000";
              -- phase_inc_en          <=  '1';
            -- end if;
          -- end if;
        -- else
          -- if(holdoff_r = '0')then
            -- dds_ce <=  '1';
            -- cnt4   <=  (others => '0');
            -- state  <=  s3;
          -- end if;
        -- end if;
      
      -- Keep DDS enabled to overcome the latency of the core (i.e. wait for our samples to pop out).
      when s3 =>
        dds_rst <=  '0';
        if(cnt5_r < 9)then
          if(holdoff_r = '0')then
            dds_ce  <=  '1';
            cnt5    <=  cnt5_r +1;
          end if;
        else
          cnt5  <= (others => '0');
          state <=  s0;
        end if;
      when others =>
    end case;
  end process;  
  
  process(clk_in)
  begin 
    if(rising_edge(clk_in))then
      mult_dval_r                 <=  mult_ce_pipe_r(1);
      mult_ce_pipe_r(1 downto 0)  <=  mult_ce_pipe_r(0) & (dds_rdy and dds_ce);
    end if;
  end process;
  
  data_swap_scaled  <=  data_scaled when swap_r = '0' else data_scaled(15 downto 0) & data_scaled(31 downto 16); 
  mult_ce           <=  mult_ce_pipe_r(1) or mult_ce_pipe_r(0) or (dds_rdy and dds_ce);    

  i_dds : dds_latency10
    port map(
      -- ce      =>  dds_ce,
      -- clk     =>  clk_in,
      -- sclr    =>  dds_rst,
      -- pinc_in =>  phase_inc_r1,
      -- poff_in =>  phase_offset_r,
      -- rdy     =>  dds_rdy,
      -- cosine  =>  dds_data(15 downto 0),
      -- sine    =>  dds_data(31 downto 16)
      aclk                =>  clk_in,
      aclken              =>  dds_ce,
      aresetn             =>  rstn_r,
      s_axis_phase_tvalid =>  dds_ce,
      s_axis_phase_tdata  =>  phase_inc_r1,
      m_axis_data_tvalid  =>  dds_rdy,
      m_axis_data_tdata   =>  dds_data(31 downto 0)
      );
      
  i_mult1 : mult_16signed_x_16unsigned_latency3
    port map(
      clk   =>  clk_in,
      a     =>  dds_data(15 downto 0),
      b     =>  scale_r,
      ce    =>  mult_ce,
      sclr  =>  '0',
      p     =>  data_scaled(15 downto 0)
      );
      
  i_mult2 : mult_16signed_x_16unsigned_latency3
    port map(
      clk   =>  clk_in,
      a     =>  dds_data(31 downto 16),
      b     =>  scale_r,
      ce    =>  mult_ce,
      sclr  =>  '0',
      p     =>  data_scaled(31 downto 16)
      );
    
end mixed;
    