`resetall
`timescale 1ns / 1ps
`default_nettype none

module digital_rx_chain #
(
    parameter CTRL_REG_ADDR = 32'h00000000,
    parameter NUM_SAMPLES_REG_ADDR = 32'h00000004,
    parameter START_SAMPLE_REG_ADDR = 32'h00000008,
    
    parameter integer AXI_ADDR_WIDTH	= 32,
    parameter integer AXI_DATA_WIDTH	= 32,
    parameter integer JESD_DATA_WIDTH	= 64
//    parameter integer JESD_DATA_WIDTH	= 32       
)
(
    input  wire       clk,

    // AXI4L Config Interface
    axi4l_intf.slave ctrl_if,
    
    input  wire        start_of_pulse,
    
    // Input Data
    input  wire        in_tvalid,
    input  wire [JESD_DATA_WIDTH-1:0] in_tdata_i,
    input  wire [JESD_DATA_WIDTH-1:0] in_tdata_q,
    
    // Output Data
    axi4s_intf.master    rx_out

);

reg        out_tvalid_r;
wire       out_tlast_r;
wire       out_tstart_r;

// ------------------------------
// Register Inputs for timing
// ------------------------------
reg        in_tstart_reg;
reg        in_tvalid_reg;
reg [JESD_DATA_WIDTH-1:0] in_tdata_i_reg;
reg [JESD_DATA_WIDTH-1:0] in_tdata_q_reg;
always @ (posedge clk) begin
    in_tstart_reg <= start_of_pulse;
    in_tvalid_reg <= in_tvalid;
    in_tdata_i_reg <= in_tdata_i;
    in_tdata_q_reg <= in_tdata_q;
end


// ------------------------------
// AXIL Decode
// ------------------------------
wire [AXI_ADDR_WIDTH-1 : 0]		raddr;
wire [AXI_ADDR_WIDTH-1 : 0]		waddr;
wire 						    rden;
wire 						    wren;
wire [AXI_DATA_WIDTH-1 : 0]		wdata;
reg [AXI_DATA_WIDTH-1 : 0]		rdata;


axil_slave 
# (
	.DATA_WIDTH(AXI_DATA_WIDTH),
	.ADDR_WIDTH(AXI_ADDR_WIDTH)
) axil_slave_i 
(
	// AXIL Slave
	.S_AXI_ACLK(ctrl_if.clk),
	.S_AXI_ARESETN(ctrl_if.resetn),
	.S_AXI_AWADDR(ctrl_if.awaddr),
	.S_AXI_AWPROT(ctrl_if.awprot),
	.S_AXI_AWVALID(ctrl_if.awvalid),
	.S_AXI_AWREADY(ctrl_if.awready),
	.S_AXI_WDATA(ctrl_if.wdata),
	.S_AXI_WSTRB(ctrl_if.wstrb),
	.S_AXI_WVALID(ctrl_if.wvalid),
	.S_AXI_WREADY(ctrl_if.wready),
	.S_AXI_BRESP(ctrl_if.bresp),
	.S_AXI_BVALID(ctrl_if.bvalid),
	.S_AXI_BREADY(ctrl_if.bready),
	.S_AXI_ARADDR(ctrl_if.araddr),
	.S_AXI_ARPROT(ctrl_if.arprot),
	.S_AXI_ARVALID(ctrl_if.arvalid),
	.S_AXI_ARREADY(ctrl_if.arready),
	.S_AXI_RDATA(ctrl_if.rdata),
	.S_AXI_RRESP(ctrl_if.rresp),
	.S_AXI_RVALID(ctrl_if.rvalid),
	.S_AXI_RREADY(ctrl_if.rready),	
	
	.raddr(raddr),
	.waddr(waddr),
	.wren(wren),
    .rden(rden),
	.wdata(wdata),
	.rdata(rdata)
);

// ------------------------------
// Config Registers
// ------------------------------
wire reset;
reg [31:0]  reg_ctrl;
reg [15:0]  reg_num_samples;
reg [27:0]  reg_start_sample;

always @ (posedge ctrl_if.clk) begin
    if (~ctrl_if.resetn) begin
        reg_ctrl <= 0;
    end else if (wren && waddr[11:0] == CTRL_REG_ADDR) begin
        reg_ctrl <= wdata[15:0];
    end
end

always @ (posedge ctrl_if.clk) begin
    if (~ctrl_if.resetn) begin
        reg_num_samples <= 0;
    end else if (wren && waddr[11:0] == NUM_SAMPLES_REG_ADDR) begin
        reg_num_samples <= wdata[15:0];
    end
end

always @ (posedge ctrl_if.clk) begin
    if (~ctrl_if.resetn) begin
        reg_start_sample <= 0;
    end else if (wren && waddr[11:0] == START_SAMPLE_REG_ADDR) begin
        reg_start_sample <= wdata;
    end
end

always @ (posedge ctrl_if.clk) begin
    if (rden) begin
        if ( raddr[11:0] == CTRL_REG_ADDR ) 
            rdata <= reg_ctrl;       
        if ( raddr[11:0] == NUM_SAMPLES_REG_ADDR ) 
            rdata <= reg_num_samples;   
        if ( raddr[11:0] == START_SAMPLE_REG_ADDR ) 
            rdata <= reg_start_sample;   
    end      
end

assign reset = reg_ctrl[0];


// ------------------------------
// Event Counter
// ------------------------------
reg [15:0] event_cnt;

always @ (posedge clk) begin
   if (reset == 1'b1) begin
       event_cnt <= 0;
   end else begin
      if (in_tstart_reg) begin
         event_cnt <= event_cnt + 1;
      end
   end
end

// ------------------------------
// Sample gating
// ------------------------------
reg [16:0] sample_cnt;
reg pulse_active;
reg pulse_active_q;
reg pulse_active_fed;
reg pulse_active_fed_q;

// Delay event pulse by start sample
reg [27:0] start_sample_cnt;
reg [27:0] start_sample_this_pulse;
reg delay_active;
reg delay_active_q;
reg delay_active_fed;

always @ (posedge clk) begin
    if (reset == 1'b1) begin
        start_sample_cnt <= 0;
        delay_active <= 0;
    end else begin

        delay_active_q <= delay_active;
        delay_active_fed <= ~delay_active && delay_active_q;

        if (in_tstart_reg && in_tvalid_reg) begin
            start_sample_cnt <= reg_start_sample;
            start_sample_this_pulse <= reg_start_sample;
            delay_active <= 1;
        end

        if (delay_active) begin
            start_sample_cnt <= start_sample_cnt - 1;
            if (start_sample_cnt == 0) begin
                delay_active <= 0;
            end
        end

    end
end

always @ (posedge clk) begin
    if (reset == 1'b1) begin
        sample_cnt <= 0;
        pulse_active <= 0;
        pulse_active_q <= 0;
        pulse_active_fed <= 1;
        pulse_active_fed_q <= 1;
        
    end else begin
       
        pulse_active_q <= pulse_active;
        pulse_active_fed <= ~pulse_active && pulse_active_q;
        pulse_active_fed_q <= pulse_active_fed;

        if (delay_active_fed && in_tvalid_reg) begin
            sample_cnt <= 0;
            pulse_active <= 1;
        end;
            
        if (out_tvalid_r) begin
            sample_cnt <= sample_cnt + 1;
            if (sample_cnt == reg_num_samples-1) begin
                sample_cnt <= 0;
                pulse_active <= 0;
            end
        end
    end
end

assign out_tlast_r = ((sample_cnt == reg_num_samples-1) && out_tvalid_r) ? 1'b1 : 1'b0;
assign out_tstart_r = ((sample_cnt == 0) && out_tvalid_r) ? 1'b1 : 1'b0;
assign out_tvalid_r = in_tvalid_reg && (pulse_active || delay_active_fed);

// ------------------------------
// Buffer
// ------------------------------
axi4s_intf # (
    .AXI_DATA_WIDTH(JESD_DATA_WIDTH * 2),
    .AXI_USER_WIDTH(1)     
 ) 
 axis_odec_out (
    .clk(clk),
    .resetn(~reset)
 ); 

 axi4s_intf # (
    .AXI_DATA_WIDTH(JESD_DATA_WIDTH * 2),
    .AXI_USER_WIDTH(1)     
 ) 
 axis_pulse_buffer_out (
    .clk(clk),
    .resetn(~reset)
 );   

 axi4s_intf # (
    .AXI_DATA_WIDTH(64),
    .AXI_USER_WIDTH(8)     
 ) 
 axis_dwidth_conv_out (
    .clk(clk),
    .resetn(~reset)
 );      
   
assign axis_odec_out.tvalid = out_tvalid_r && (pulse_active || delay_active_fed);
assign axis_odec_out.tlast = out_tlast_r;
assign axis_odec_out.tuser = out_tstart_r;

//204C
assign axis_odec_out.tdata[15:0]     = in_tdata_i_reg[63:48];
assign axis_odec_out.tdata[31:16]    = in_tdata_q_reg[63:48];
assign axis_odec_out.tdata[47:32]    = in_tdata_i_reg[47:32];
assign axis_odec_out.tdata[63:48]    = in_tdata_q_reg[47:32];
assign axis_odec_out.tdata[79:64]    = in_tdata_i_reg[31:16];
assign axis_odec_out.tdata[95:80]    = in_tdata_q_reg[31:16];
assign axis_odec_out.tdata[111:96]   = in_tdata_i_reg[15:0];
assign axis_odec_out.tdata[127:112]  = in_tdata_q_reg[15:0];     

pulse_buffer_fifo pulse_buffer_fifo_i (
   .s_axis_aresetn(~reset), 
   .s_axis_aclk(clk),     

   .s_axis_tvalid(axis_odec_out.tvalid),
   .s_axis_tready(),
   .s_axis_tdata(axis_odec_out.tdata),  
   .s_axis_tlast(axis_odec_out.tlast),
   .s_axis_tuser(axis_odec_out.tuser),

   .m_axis_tvalid(axis_pulse_buffer_out.tvalid),
   .m_axis_tready(axis_pulse_buffer_out.tready),
   .m_axis_tdata(axis_pulse_buffer_out.tdata), 
   .m_axis_tlast(axis_pulse_buffer_out.tlast),
   .m_axis_tuser(axis_pulse_buffer_out.tuser) 
);

wire [15:0] dwidth_conv_tuser_in;
assign dwidth_conv_tuser_in[15:1] = '0;
assign dwidth_conv_tuser_in[0] = axis_pulse_buffer_out.tuser;

// This is going from 128 bits to 64 bits
dig_rx_dwidth_converter dig_rx_dwidth_converter_i (
    .aresetn(~reset),
    .aclk(clk),      
   
   .s_axis_tvalid(axis_pulse_buffer_out.tvalid),
   .s_axis_tready(axis_pulse_buffer_out.tready),
   .s_axis_tdata(axis_pulse_buffer_out.tdata),
   .s_axis_tlast(axis_pulse_buffer_out.tlast),
   .s_axis_tuser(dwidth_conv_tuser_in),

   .m_axis_tvalid(axis_dwidth_conv_out.tvalid),
   .m_axis_tready(axis_dwidth_conv_out.tready),
   .m_axis_tdata(axis_dwidth_conv_out.tdata),
   .m_axis_tlast(axis_dwidth_conv_out.tlast),
   .m_axis_tuser(axis_dwidth_conv_out.tuser)
);

// Now need a clock converter to go to output which is connected directly to eth udp
dig_rx_clock_converter dig_rx_clock_converter_i (
  .s_axis_aresetn(axis_dwidth_conv_out.resetn),
  .s_axis_aclk(axis_dwidth_conv_out.clk),      
  .s_axis_tvalid(axis_dwidth_conv_out.tvalid),  
  .s_axis_tready(axis_dwidth_conv_out.tready),  
  .s_axis_tdata(axis_dwidth_conv_out.tdata),    
  .s_axis_tlast(axis_dwidth_conv_out.tlast),    
  .s_axis_tuser(axis_dwidth_conv_out.tuser),    
  
  .m_axis_aresetn(rx_out.resetn),
  .m_axis_aclk(rx_out.clk),      
  .m_axis_tvalid(rx_out.tvalid),  
  .m_axis_tready(rx_out.tready),  
  .m_axis_tdata(rx_out.tdata),    
  .m_axis_tlast(rx_out.tlast),    
  .m_axis_tuser(rx_out.tuser)     
);


//204B
//assign axis_odec_out.tdata[15:0]     = in_tdata_i_reg[31:16];
//assign axis_odec_out.tdata[31:16]    = in_tdata_q_reg[31:16];
//assign axis_odec_out.tdata[47:32]    = in_tdata_i_reg[15:0];
//assign axis_odec_out.tdata[63:48]    = in_tdata_q_reg[15:0];


//pulse_buffer_204B_fifo pulse_buffer_fifo_i (
//    .s_axis_aresetn(~reset), 
//    .s_axis_aclk(clk),     
    
//    .s_axis_tvalid(axis_odec_out.tvalid),
//    .s_axis_tready(),
//    .s_axis_tdata(axis_odec_out.tdata),  
//    .s_axis_tlast(axis_odec_out.tlast),
//    .s_axis_tuser(axis_odec_out.tuser),
    
//    .m_axis_aclk(rx_out.clk),      
//    .m_axis_tvalid(rx_out.tvalid),  
//    .m_axis_tready(rx_out.tready),  
//    .m_axis_tdata(rx_out.tdata),    
//    .m_axis_tlast(rx_out.tlast),    
//    .m_axis_tuser(rx_out.tuser)     
//);

assign rx_out.tkeep = '1;
assign rx_out.tdest = 1;

endmodule


`resetall
