//
// Synthesizable test pattern generators and checkers
// for CHDR that can be used to test transparent blocks
// (FIFOs, switches, etc)
//

//`define MTU 8192
`define MTU 1536

module axi_chdr_test_pattern
  (
   input clk,
   input reset,
  
   //
   // CHDR friendly AXI stream input
   //
   output reg [63:0] i_tdata,
   output reg i_tlast,
   output reg i_tvalid,
   input  wire i_tready,
   //
   // CHDR friendly AXI Stream output
   //
   input wire [63:0] o_tdata,
   input wire o_tlast,
   input wire o_tvalid,
   output reg o_tready,
   //
   // Test flags
   //
   input start,
   input [15:0] control,
   output reg fail,
   output reg done
   );

   wire [7:0]      bist_rx_delay = control[7:0];
   wire [7:0]      bist_tx_delay = control[15:8];
   

   reg [15:0] tx_count, rx_count;
   reg [15:0] tx_data, rx_data;
   reg [7:0]  tx_delay, rx_delay;
   

   localparam TX_IDLE = 0;
   localparam TX_START = 1;
   localparam TX_ACTIVE = 2;
   localparam TX_GAP = 3;
   localparam TX_DONE = 4;
   localparam TX_WAIT = 5;

   localparam RX_IDLE = 0;
   localparam RX_ACTIVE = 1;
   localparam RX_FAIL = 2;
   localparam RX_DONE = 3;
   localparam RX_WAIT = 4;
      
   reg [2:0]  tx_state, rx_state;
 
   //
   // Transmitter
   //
   always @(posedge clk)
     if (reset)
       begin
	  tx_delay <= 0; 
	  tx_count <= 8;
	  tx_data <= 0;
	  i_tdata <= 64'h0;
	  i_tlast <= 1'b0;
	  i_tvalid <= 1'b0;
	  tx_state <= TX_IDLE;
       end
     else
       begin
	  case(tx_state)
	    TX_IDLE: begin
	       tx_delay <= 0; 
	       i_tdata <= 64'h0;
	       i_tlast <= 1'b0;
	       i_tvalid <= 1'b0;
	       tx_data <= 0;
	       tx_count <= 4;
	       // Run whilst start asserted.
	       if (start) begin
		  tx_state <= TX_START;		  
		  // ....Go back to initialized state if start deasserted.
	       end else begin		 		  
		  tx_state <= TX_IDLE;
	       end		  
	    end // case: TX_IDLE

	    //
	    // START signal is asserted.
	    // Now need to start transmiting a packet.
	    //
	    TX_START: begin
	       // At the next clock edge drive first beat of new packet onto HDR bus.
	       i_tlast <= 1'b0;
	       i_tvalid <= 1'b1;
	       tx_data <= tx_data + 4;
	       // i_tdata <= {tx_data,tx_data+16'd1,tx_data+16'd2,tx_data+16'd3};
	       i_tdata <= {4{(tx_data[2]?16'hffff:16'h0000)^tx_data[15:0]}};
	       tx_state <= TX_ACTIVE;
	       
	    end
	    
	    //
	    // Valid data is (already) being driven onto the CHDR bus.
	    // i_tlast may also be driven asserted if current data count has reached EOP.
	    // Watch i_tready to see when it's consumed.
	    // When packets are consumed increment data counter or transition state if
	    // EOP has sucsesfully concluded.
	    //
	    TX_ACTIVE: begin
	       i_tvalid <= 1'b1; // Always assert tvalid
	       if (i_tready) begin
		  
//		  i_tdata <= {tx_data,tx_data+16'd1,tx_data+16'd2,tx_data+16'd3};
		  i_tdata <= {4{(tx_data[2]?16'hffff:16'h0000)^tx_data[15:0]}};
		  // Will this next beat be the last in a packet?
		  if (tx_data == tx_count) begin
		     tx_data <= 0;
		     i_tlast <= 1'b1;
		     tx_state <= TX_GAP;
		  end else begin
		     tx_data <= tx_data + 4;
		     i_tlast <= 1'b0;
		     tx_state <= TX_ACTIVE;
		  end		  
	       end else begin
		  // Keep driving all CHDR bus signals as-is until i_tready is asserted.
		  tx_state <= TX_ACTIVE;
	       end
	    end // case: TX_ACTIVE
	    //
	    // Force an inter-packet gap between packets in a BIST sequence where tvalid is driven low.
	    // As we leave this state check if all packets in BIST sequence have been generated yet,
	    // and if so go to done state.
	    //
	    TX_GAP: begin
	       if (i_tready) begin
		  i_tvalid <= 1'b0;
		  i_tdata <= 64'h0;
		  i_tlast <= 1'b0;
		  tx_count <= tx_count + 4;
		  
		  if (tx_count < `MTU) begin
		     tx_state <= TX_WAIT;
		     tx_delay <= bist_tx_delay;		  
		  end else
		    tx_state <= TX_DONE;
	       end else begin // if (i_tready)
		  tx_state <= TX_GAP;
	       end
	    end // case: TX_GAP
	    //
	    // Simulate inter packet gap in real UHD system
	    TX_WAIT: begin
	       if (tx_delay == 0)
		 tx_state <= TX_START;
	       else begin
		  tx_delay <= tx_delay - 1;
		  tx_state <= TX_WAIT;
	       end
	    end
	    
	    //
	    // Complete test pattern BIST sequence has been transmitted. Sit in this
	    // state indefinately if START is taken low, which re-inits the whole BIST solution.
	    // 
	    TX_DONE: begin
	       if (!start) begin
		  tx_state <= TX_DONE;
	       end else begin
		  tx_state <= TX_IDLE;
	       end
	       i_tvalid <= 1'b0;
	       i_tdata <= 64'd0;
	       i_tlast <= 1'b0;
	       
	    end
	  endcase // case (tx_state)
       end

   //
   // Receiver
   //
   always @(posedge clk)
     if (reset)
       begin
	  rx_delay <= 0;
	  rx_count <= 0;
	  rx_data <= 0;
	  o_tready <= 1'b0;
	  rx_state <= RX_IDLE;
	  fail <= 1'b0;
	  done <= 1'b0;
	  
       end
     else begin
	case (rx_state)
	  RX_IDLE: begin
	     rx_delay <= 0;
	     o_tready <= 1'b0;
	     rx_data <= 0;
	     rx_count <= 4;
             fail <= 1'b0;
	     done <= 1'b0;
	     // Not accepting data whilst Idle,
	     // switch to active when packet arrives
	     if (o_tvalid) begin		
	        o_tready <= 1'b1;
		rx_state <= RX_ACTIVE;
	     end else
	       rx_state <= RX_IDLE;	       	    
	  end
	  
	  RX_ACTIVE: begin
	     o_tready <= 1'b1;
	     if (o_tvalid)
//	       if (o_tdata != {rx_data,rx_data+16'd1,rx_data+16'd2,rx_data+16'd3})
	       if (o_tdata != {4{(rx_data[2]?16'hffff:16'h0000)^rx_data[15:0]}})
		 begin
		    $display("o_tdata: %x  !=  expected:  %x @ time: %d",o_tdata,
//			     {rx_data,rx_data+16'd1,rx_data+16'd2,rx_data+16'd3},
			     {4{(rx_data[2]?16'hffff:16'h0000)^rx_data[15:0]}},
			     $time);		    
		    rx_state <= RX_FAIL;
		 end
	       else
		 // Should last be asserted?
		 if (rx_data == rx_count)
		   // ...last not asserted when it should be!
		   if (~(o_tlast===1)) begin
		      $display("o_tlast not asserted when it should be @ time: %d",$time);		     
		      rx_state <= RX_FAIL;
		   end else begin
		      // End of packet, set up to RX next 
		      rx_data <= 0;
		      rx_count <= rx_count + 4;
		      rx_delay <= bist_rx_delay;
		      if (rx_count == `MTU) begin
			 rx_state <= RX_DONE;
		      end else begin
			 rx_state <= RX_WAIT;
		      end
		      o_tready <= 1'b0;
		   end	       
		 else
		   // ...last asserted when it should not be!
		   if (~(o_tlast===0)) begin
		      $display("o_tlast asserted when it should not be @ time: %d",$time);		     
		      rx_state <= RX_FAIL;
		   end else begin
		      // Still in packet body
		      rx_data <= rx_data + 4;
		      rx_delay <= bist_rx_delay;
		      rx_state <= RX_WAIT;
		      o_tready <= 1'b0;
		   end
	     else
	       // Nothing to do this cycle
	       rx_state <= RX_ACTIVE;	       
	  end // case: RX_ACTIVE

	  // To simulate the radio consuming samples at a steady rate set by the decimation
	  // have a programable delay here
	  RX_WAIT: begin
	     if (rx_delay == 0) begin
		rx_state <= RX_ACTIVE;
		o_tready <= 1'b1;
	     end else begin
		rx_delay <= rx_delay - 1;
		rx_state <= RX_WAIT;
	     end	     
	  end
	  
	  
	  RX_FAIL: begin
	     o_tready <= 1'b0;
	     done <= 1'b1;
	     fail <= 1'b1;
	     // If start is deasserted allow BIST logic to reset and rearm
	     if (start)
	       rx_state <= RX_FAIL;
	     else
	       rx_state <= RX_IDLE;
	     
	  end
	  
	  RX_DONE: begin
	     o_tready <= 1'b0;
	     done <= 1'b1;
	     fail <= 1'b0;
	     // If start is asserted allow BIST logic to reset, rearm & restart
	     if (!start)
	       rx_state <= RX_DONE;
	     else
	       rx_state <= RX_IDLE;
	     
	  end
	  
	endcase // case (rx_state)
     end
 
   

endmodule