//
// Copyright 2014-2016 Ettus Research
// Copyright 2018 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//


module new_tx_control
  #(parameter BASE=0)
   (input clk, input reset, input clear,
    input set_stb, input [7:0] set_addr, input [31:0] set_data,

    input [63:0] vita_time,
    output reg ack_or_error,
    output packet_consumed,
    output [11:0] seqnum,
    output reg [63:0] error_code,
    output [31:0] sid,

    // From tx_deframer
    input [175:0] sample_tdata,
    input sample_tvalid,
    output sample_tready,

    // To DSP Core
    output [31:0] sample,
    output run,    input strobe,

    output [31:0] debug
);

   wire [31:0] sample1 = sample_tdata[31:0];
   wire [31:0] sample0 = sample_tdata[63:32];
   wire [63:0] send_time = sample_tdata[127:64];

   assign sid = sample_tdata[159:128];
   assign seqnum = sample_tdata[171:160];

   wire eop = sample_tdata[172];
   wire eob = sample_tdata[173];
   wire send_at = sample_tdata[174];
   wire odd = sample_tdata[175];

   wire now, early, late, too_early;
   wire policy_next_burst, policy_next_packet, policy_wait;
   wire clear_seqnum_int;

   setting_reg #(.my_addr(BASE), .width(3)) sr_error_policy
     (.clk(clk),.rst(reset),.strobe(set_stb),.addr(set_addr),
      .in(set_data),.out({policy_next_burst,policy_next_packet,policy_wait}),.changed(clear_seqnum_int));

   time_compare
     time_compare (.clk(clk), .reset(reset), .time_now(vita_time), .trigger_time(send_time),
                   .now(now), .early(early), .late(late), .too_early(too_early));

   assign run = (state == ST_SAMP0) | (state == ST_SAMP1);
   assign sample = (state == ST_SAMP0) ? sample0 : sample1;

   reg [2:0] state;

   localparam ST_IDLE  = 0;
   localparam ST_SAMP0 = 1;
   localparam ST_SAMP1 = 2;
   localparam ST_ERROR = 3;
   localparam ST_WAIT  = 4;

   reg [11:0] expected_seqnum;

   wire [63:0] CODE_EOB_ACK            = {32'd1,20'd0,seqnum};
   wire [63:0] CODE_UNDERRUN           = {32'd2,20'd0,seqnum};
   wire [63:0] CODE_SEQ_ERROR          = {32'd4,4'd0,expected_seqnum,4'd0,seqnum};
   wire [63:0] CODE_TIME_ERROR         = {32'd8,20'd0,seqnum};
   wire [63:0] CODE_UNDERRUN_MIDPKT    = {32'd16,20'd0,seqnum};
   wire [63:0] CODE_SEQ_ERROR_MIDBURST = {32'd32,4'd0,expected_seqnum,4'd0,seqnum};

   reg clear_seqnum_latch;

   wire burst_start = sample_tvalid & (~send_at | now);
   wire last_sample = sample_tvalid & sample_tready & eop;
   wire time_to_clear = clear_seqnum_latch && (
                        (last_sample && eob) ||
                        (state == ST_ERROR) ||
                        (state == ST_IDLE && ~burst_start));

   always @(posedge clk) begin
     if(reset | clear) begin
       expected_seqnum <= 12'd0;
       clear_seqnum_latch <= 0;
     end else begin
       if(clear_seqnum_int) begin
         clear_seqnum_latch <= 1;
       end
       if(time_to_clear) begin
         expected_seqnum <= 12'd0;
         clear_seqnum_latch <= 0;
       end else if(last_sample) begin
         expected_seqnum <= seqnum + 12'd1;
       end
     end
   end

   always @(posedge clk)
     if(reset | clear) begin
       state <= ST_IDLE;
       ack_or_error <= 1'b0;
       error_code <= 64'd0;
     end else begin
       case(state)
       ST_IDLE :
         begin
            ack_or_error <= 1'b0;
            if(sample_tvalid)
          if(~send_at | now)
            if(expected_seqnum != seqnum)
              begin
                state <= ST_ERROR;
                ack_or_error <= 1'b1;
                error_code <= CODE_SEQ_ERROR;
              end
            else
              state <= ST_SAMP0;
          else if(late)
            begin
              state <= ST_ERROR;
              ack_or_error <= 1'b1;
              error_code <= CODE_TIME_ERROR;
            end
         end // case: ST_IDLE
       ST_SAMP0 :
         if(strobe)
           if(~sample_tvalid)
             begin
               state <= ST_ERROR;
               ack_or_error <= 1'b1;
               error_code <= CODE_UNDERRUN;
             end
           else if(eop & odd & eob)
             begin
               state <= ST_IDLE;
               ack_or_error <= 1'b1;
               error_code <= CODE_EOB_ACK;
             end
           else if(eop & odd)
             state <= ST_SAMP0;
           else if(expected_seqnum != seqnum)
             begin
               state <= ST_ERROR;
               ack_or_error <= 1'b1;
               error_code <= CODE_SEQ_ERROR_MIDBURST;
             end
           else
             state <= ST_SAMP1;
       ST_SAMP1 :
         if(strobe)
           if(eop & eob)
             begin
               state <= ST_IDLE;
               ack_or_error <= 1'b1;
               error_code <= CODE_EOB_ACK;
             end
           else
             state <= ST_SAMP0;
       ST_ERROR :
         begin
           ack_or_error <= 1'b0;
             if(sample_tvalid & eop)
               if(policy_next_packet | (policy_next_burst & eob)) begin
                 state <= ST_IDLE;
               end
          // FIXME: Implement a wait state or remove wait policy
          // else if(policy_wait)
          //   state <= ST_WAIT;
         end
       endcase // case (state)
     end

   assign sample_tready = (state == ST_ERROR) | (strobe & ( (state == ST_SAMP1) | ((state == ST_SAMP0) & eop & odd) ) );

   assign packet_consumed = eop & sample_tvalid & sample_tready;

   assign debug = {
     error_code[37:32], // [30:25]
     error_code[11:0], // [24:13]
     sample_tvalid, //[12]
     now, // [11]
     early, // [10]
     late, // [9]
     too_early, // [8]
     strobe,  // [7]
     eop, // [6]
     eob, // [5]
     send_at, // [4]
     odd,  // [3]
     state[2:0] //  [2:0]
   };

endmodule // new_tx_control