-------------------------------------------------------------------------------
-- Company:
-- Engineer:       Jason M. Blevins
-- 
-- Create Date:    19:38:12 11/10/2016 
-- Design Name: 
-- Module Name:    dds_pulse_2x_top - behavioral
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description:
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments:
--  * All information is proprietary/confidential *
--
--  Supports single channel mode or dual channel (summed) mode.
--  When using dual channel mode, the module hangs after the shortest of the
--  two pulse streams completes. Ideally, both streams will be equal length.
--
--  For each channel:
--  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
--
-------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
--Library UNIMACRO;
--use UNIMACRO.vcomponents.all;
USE IEEE.NUMERIC_STD.ALL;

entity dds_pulse_2x_top is
  port(
    clk_in          : in  std_logic;
    rst_in          : in  std_logic;
    mode_in         : in  std_logic;    --  0=single, 1=dual
    scale_in        : in  std_logic_vector(15 downto 0);
    fifo1_data_in   : in  std_logic_vector(31 downto 0);
    fifo1_dval_in   : in  std_logic;
    fifo1_empty_in  : in  std_logic;
    fifo1_rden_out  : out std_logic;
    fifo2_data_in   : in  std_logic_vector(31 downto 0);
    fifo2_dval_in   : in  std_logic;
    fifo2_empty_in  : in  std_logic;
    fifo2_rden_out  : out std_logic;
    holdoff_in      : in  std_logic;
    overflow_out    : out std_logic_vector(1 downto 0);
    underflow_out   : out std_logic_vector(1 downto 0);
    i_max_abs_out   : out std_logic_vector(15 downto 0);
    q_max_abs_out   : out std_logic_vector(15 downto 0);
    data_out        : out std_logic_vector(31 downto 0);
    dval_out        : out std_logic    
    );
end entity;
  
architecture mixed of dds_pulse_2x_top 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_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 component;
  
  component sfifo_32b_1024_pf992_latency1
    port(
      clk       : in  std_logic;
      srst       : in  std_logic;
      din       : in  std_logic_vector(31 downto 0);
      wr_en     : in  std_logic;
      rd_en     : in  std_logic;
      dout      : out std_logic_vector(31 downto 0);
      full      : out std_logic;
      overflow  : out std_logic;
      empty     : out std_logic;
      underflow : out std_logic;
      prog_full : out std_logic
      );
  end component;
  
  component adder_16signed_16signed_latency2
    port(
      a       : in  std_logic_vector(15 downto 0);
      b       : in  std_logic_vector(15 downto 0);
      clk     : in  std_logic;
      ce      : in  std_logic;
      bypass  : in  std_logic;
      s       : out std_logic_vector(16 downto 0)
      );
  end component;
   
  signal  rst_r                 : std_logic :=  '1';
  signal  mode_n_r              : std_logic :=  '1';  --  1=single, 0=dual
  signal  scale_r               : std_logic_vector(15 downto 0) :=  x"8000";
  signal  pulse_fifo_dval_r     : std_logic_vector(1 downto 0)  :=  "00";
  signal  pulse_adder_dval_r    : std_logic :=  '0';
  signal  pulse_adder_ce        : std_logic;
  signal  pulse_data_r          : std_logic_vector(31 downto 0) := (others => '0');
  signal  pulse_dval_r          : std_logic :=  '0';
  signal  adder_dval_r          : std_logic :=  '0';
  signal  holdoff_r             : std_logic;
  
  signal  pulse1_mult_dval_r    : std_logic :=  '0';
  signal  pulse1_mult_ce_pipe_r : std_logic_vector(1 downto 0)  :=  "00";
  signal  pulse1_mult_ce        : std_logic;  
  signal  pulse1_data           : std_logic_vector(31 downto 0);
  signal  pulse1_dval           : std_logic;
  signal  pulse1_data_scaled    : std_logic_vector(31 downto 0);
  signal  pulse1_fifo_overflow  : std_logic;
  signal  pulse1_fifo_empty     : std_logic;
  signal  pulse1_fifo_underflow : std_logic;
  signal  pulse1_fifo_progfull  : std_logic;
  signal  pulse1_fifo_rden      : std_logic;
  signal  pulse1_fifo_rden_r    : std_logic := '0';
  signal  pulse1_fifo_dout      : std_logic_vector(31 downto 0);
  signal  pulse1_fifo_dout_r    : std_logic_vector(31 downto 0) := (others => '0');
  signal  adder1_s              : std_logic_vector(16 downto 0);
  signal  adder1_s_r            : std_logic_vector(16 downto 0);
  signal  adder1_s_r1           : std_logic_vector(15 downto 0);
  signal  i_abs_max_r           : unsigned(15 downto 0);
  signal  fifo1_underflow_r     : std_logic :=  '0';
  signal  fifo1_overflow_r      : std_logic :=  '0';
  
  signal  pulse2_mult_dval_r    : std_logic :=  '0';
  signal  pulse2_mult_ce_pipe_r : std_logic_vector(1 downto 0)  :=  "00";
  signal  pulse2_mult_ce        : std_logic;  
  signal  pulse2_data           : std_logic_vector(31 downto 0);
  signal  pulse2_dval           : std_logic;
  signal  pulse2_data_scaled    : std_logic_vector(31 downto 0);
  signal  pulse2_fifo_overflow  : std_logic;
  signal  pulse2_fifo_empty     : std_logic;
  signal  pulse2_fifo_underflow : std_logic;
  signal  pulse2_fifo_progfull  : std_logic;
  signal  pulse2_fifo_rden      : std_logic;
  signal  pulse2_fifo_dout      : std_logic_vector(31 downto 0);
  signal  pulse2_fifo_dout_r    : std_logic_vector(31 downto 0);
  signal  adder2_s              : std_logic_vector(16 downto 0);
  signal  adder2_s_r            : std_logic_vector(16 downto 0);
  signal  adder2_s_r1           : std_logic_vector(15 downto 0);
  signal  q_abs_max_r           : unsigned(15 downto 0);
  signal  fifo2_underflow_r     : std_logic :=  '0';
  signal  fifo2_overflow_r      : std_logic :=  '0';

begin

  data_out      <=  pulse_data_r;
  dval_out      <=  pulse_dval_r;
  i_max_abs_out <=  std_logic_vector(i_abs_max_r);
  q_max_abs_out <=  std_logic_vector(q_abs_max_r);
  overflow_out  <=  fifo2_overflow_r & fifo1_overflow_r;
  underflow_out <=  fifo2_underflow_r & fifo1_underflow_r;

  process(clk_in)
  begin 
    if(rising_edge(clk_in))then
      rst_r     <=  rst_in;
      scale_r   <=  scale_in;
      mode_n_r  <=  not(mode_in);
      holdoff_r <=  holdoff_in;
    end if;
  end process;

  process(clk_in)
  begin 
    if(rising_edge(clk_in))then
      pulse1_mult_dval_r                <=  pulse1_mult_ce_pipe_r(1);
      pulse1_mult_ce_pipe_r(1 downto 0) <=  pulse1_mult_ce_pipe_r(0) & pulse1_dval;
      pulse2_mult_dval_r                <=  pulse2_mult_ce_pipe_r(1);
      pulse2_mult_ce_pipe_r(1 downto 0) <=  pulse2_mult_ce_pipe_r(0) & pulse2_dval;
      pulse_fifo_dval_r(1 downto 0)     <=  pulse_fifo_dval_r(0) & pulse1_fifo_rden_r;
      pulse_adder_dval_r                <=  pulse_fifo_dval_r(1);
      pulse1_fifo_rden_r                <=  pulse1_fifo_rden;
      pulse1_fifo_dout_r                <=  pulse1_fifo_dout;
      pulse2_fifo_dout_r                <=  pulse2_fifo_dout;
    end if;
  end process;
     
  pulse1_mult_ce  <=  pulse1_mult_ce_pipe_r(1) or pulse1_mult_ce_pipe_r(0) or pulse1_dval;
  pulse2_mult_ce  <=  pulse2_mult_ce_pipe_r(1) or pulse2_mult_ce_pipe_r(0) or pulse2_dval;

  pulse1_fifo_rden  <=  not(pulse1_fifo_empty) and not(pulse2_fifo_empty) and not(holdoff_r) when mode_n_r = '0' else
                        not(pulse1_fifo_empty) and not(holdoff_r);
                        
  pulse2_fifo_rden  <=  not(pulse1_fifo_empty) and not(pulse2_fifo_empty) and not(holdoff_r) when mode_n_r = '0' else
                        '0';
                        
  pulse_adder_ce    <=  pulse_fifo_dval_r(1) or pulse_fifo_dval_r(0);

  i_dds_pulse1_gen : dds_pulse_gen
    port map(
      clk_in        =>  clk_in,
      rst_in        =>  rst_r,
      fifo_data_in  =>  fifo1_data_in,
      fifo_dval_in  =>  fifo1_dval_in,
      fifo_empty_in =>  fifo1_empty_in,
      fifo_rden_out =>  fifo1_rden_out,
      holdoff_in    =>  pulse1_fifo_progfull,
      data_out      =>  pulse1_data,  
      dval_out      =>  pulse1_dval   
      );
  
  i_pulse1_mult1 : mult_16signed_x_16unsigned_latency3
    port map(
      clk   =>  clk_in,
      a     =>  pulse1_data(15 downto 0),
      b     =>  scale_r,
      ce    =>  pulse1_mult_ce,
      sclr  =>  '0',
      p     =>  pulse1_data_scaled(15 downto 0)
      );
      
  i_pulse1_mult2 : mult_16signed_x_16unsigned_latency3
    port map(
      clk   =>  clk_in,
      a     =>  pulse1_data(31 downto 16),
      b     =>  scale_r,
      ce    =>  pulse1_mult_ce,
      sclr  =>  '0',
      p     =>  pulse1_data_scaled(31 downto 16)
      );
      
  i_pulse1_fifo : sfifo_32b_1024_pf992_latency1
    port map(
      clk       =>  clk_in,
      srst       =>  rst_r,
      din       =>  pulse1_data_scaled,
      wr_en     =>  pulse1_mult_dval_r,
      rd_en     =>  pulse1_fifo_rden,
      dout      =>  pulse1_fifo_dout,
      full      =>  open,
      overflow  =>  pulse1_fifo_overflow,
      empty     =>  pulse1_fifo_empty,
      underflow =>  pulse1_fifo_underflow,
      prog_full =>  pulse1_fifo_progfull
      );

  i_dds_pulse2_gen : dds_pulse_gen
    port map(
      clk_in        =>  clk_in,
      rst_in        =>  rst_r,
      fifo_data_in  =>  fifo2_data_in,
      fifo_dval_in  =>  fifo2_dval_in,
      fifo_empty_in =>  fifo2_empty_in,
      fifo_rden_out =>  fifo2_rden_out,
      holdoff_in    =>  pulse2_fifo_progfull,
      data_out      =>  pulse2_data,  
      dval_out      =>  pulse2_dval   
      );
  
  i_pulse2_mult1 : mult_16signed_x_16unsigned_latency3
    port map(
      clk   =>  clk_in,
      a     =>  pulse2_data(15 downto 0),
      b     =>  scale_r,
      ce    =>  pulse2_mult_ce,
      sclr  =>  '0',
      p     =>  pulse2_data_scaled(15 downto 0)
      );
      
  i_pulse2_mult2 : mult_16signed_x_16unsigned_latency3
    port map(
      clk   =>  clk_in,
      a     =>  pulse2_data(31 downto 16),
      b     =>  scale_r,
      ce    =>  pulse2_mult_ce,
      sclr  =>  '0',
      p     =>  pulse2_data_scaled(31 downto 16)
      );
      
  i_pulse2_fifo : sfifo_32b_1024_pf992_latency1
    port map(
      clk       =>  clk_in,
      srst       =>  rst_r,
      din       =>  pulse2_data_scaled,
      wr_en     =>  pulse2_mult_dval_r,
      rd_en     =>  pulse2_fifo_rden,
      dout      =>  pulse2_fifo_dout,
      full      =>  open,
      overflow  =>  pulse2_fifo_overflow,
      empty     =>  pulse2_fifo_empty,
      underflow =>  pulse2_fifo_underflow,
      prog_full =>  pulse2_fifo_progfull
      );

  i_pulse_adder1 : adder_16signed_16signed_latency2
    port map(
      a       =>  pulse2_fifo_dout_r(15 downto 0),
      b       =>  pulse1_fifo_dout_r(15 downto 0),
      clk     =>  clk_in,
      ce      =>  pulse_adder_ce,
      bypass  =>  mode_n_r, --  when set to '1', b input proceeds to s output
      s       =>  adder1_s
      );

  i_pulse_adder2 : adder_16signed_16signed_latency2
    port map(
      a       =>  pulse2_fifo_dout_r(31 downto 16),
      b       =>  pulse1_fifo_dout_r(31 downto 16),
      clk     =>  clk_in,
      ce      =>  pulse_adder_ce,
      bypass  =>  mode_n_r, --  when set to '1', b input proceeds to s output
      s       =>  adder2_s
      ); 

  process(clk_in)
  begin 
    if(rising_edge(clk_in))then 
      adder1_s_r    <=  adder1_s;
      adder2_s_r    <=  adder2_s;
      adder_dval_r  <=  pulse_adder_dval_r;
      pulse_dval_r  <=  adder_dval_r;
      if(adder_dval_r = '1')then
        case adder1_s_r(16 downto 15) is
          when "01" =>  --positive overflow
            pulse_data_r(15 downto 0)  <=  x"7FFF";
          when "10" =>  --negative overflow
            pulse_data_r(15 downto 0)  <=  x"8000";
          when others =>
            pulse_data_r(15 downto 0)  <=  adder1_s_r(16) & adder1_s_r(14 downto 0);
        end case;
        case adder2_s_r(16 downto 15) is
          when "01" =>  --positive overflow
            pulse_data_r(31 downto 16)  <=  x"7FFF";
          when "10" =>  --negative overflow
            pulse_data_r(31 downto 16)  <=  x"8000";
          when others =>
            pulse_data_r(31 downto 16)  <=  adder2_s_r(16) & adder2_s_r(14 downto 0);
        end case;
      end if;   
      if(rst_r = '1')then
        --adder_dval_r  <=  '0';
        --pulse_dval_r  <=  '0';
        i_abs_max_r       <=  (others => '0');
        q_abs_max_r       <=  (others => '0');
        fifo1_overflow_r  <=  '0';
        fifo1_underflow_r <=  '0';
        fifo2_overflow_r  <=  '0';
        fifo2_underflow_r <=  '0';        
      else    
        --adder_dval_r  <=  pulse_adder_dval_r;
        --pulse_dval_r  <=  adder_dval_r;
        if(pulse1_fifo_overflow = '1')then
          fifo1_overflow_r <= '1';
        end if;
        if(pulse1_fifo_underflow = '1')then
          fifo1_underflow_r <= '1';
        end if;
        if(pulse2_fifo_overflow = '1')then
          fifo2_overflow_r <= '1';
        end if;
        if(pulse2_fifo_underflow = '1')then
          fifo2_underflow_r <= '1';
        end if;
        if(adder_dval_r = '1')then
          -- if(abs(signed(adder1_s_r)) > i_abs_max_r)then
            -- i_abs_max_r <= abs(signed(adder1_s_r));
          -- end if;
          if(adder1_s_r(16) = '0')then
            adder1_s_r1 <=  adder1_s_r(15 downto 0);
          else
            adder1_s_r1 <=  not(adder1_s_r(15 downto 0));
          end if;
            
          -- if(abs(signed(adder2_s_r)) > q_abs_max_r)then
            -- q_abs_max_r <= abs(signed(adder2_s_r));
          -- end if;
          if(adder2_s_r(16) = '0')then
            adder2_s_r1 <=  adder2_s_r(15 downto 0);
          else
            adder2_s_r1 <=  not(adder2_s_r(15 downto 0));
          end if;
        end if;
        if(pulse_dval_r = '1')then
          if(unsigned(adder1_s_r1) > i_abs_max_r)then
            i_abs_max_r <= unsigned(adder1_s_r1);
          end if;
          if(unsigned(adder2_s_r1) > q_abs_max_r)then
            q_abs_max_r <= unsigned(adder2_s_r1);
          end if;
        end if;
      end if;
    end if;
  end process;      
    
end mixed;
    