// Chuck Benz, Hollis, NH   Copyright (c)2005
// http://asics.chuckbenz.com
//
// The information and description contained herein is the
// property of Chuck Benz.
//
// This information may only be used under license from
// Chuck Benz Asic & FPGA Design, or it's assignees.  Any 
// other use or modification is prohibited
//
// $Id: sit_ver_x4.v,v 1.3 2005/07/25 12:54:59 cbenz Exp cbenz $
//

/* sit_ver_x4.v - this is the verilog module encapsulating the
 * simulation interconnection technology - connecting to another
 * simulation on another system. This verilog module uses Verilog-2001
 * file IO with named pipes to communicate with a separate process
 * running on this system. That process opens a socket to the other
 * simulation, either directly or through a sit server.

 * This 27-Jun-05 initial version is simple, assuming that we'll just
 * sample the data at the bit rate and ship it over.  Later versions
 * will implement a more proper clock recovery and will send over the
 * timestamp so that the far side can track the clock variation. 

 * An alternative version would send each signal change as a separate
 * timestamped event, still sending a sync packet every 1/2 link delay
 * so that the far side can advance it's time clock even when the signal
 * is not changing.

 * For x4 module, we'll use a different data type (6), and do all 
 * nibbles from link 0, then all from link 1, then all from link 2...
 * instead of 20 nibbles of data, we'll have 80, so we'll use 100 byte packets.

 */

`timescale 1ns / 10ps

module sit_ver_x4 (tdata, rdata) ;

  input  [3:0] tdata ;
  output [3:0] rdata ;
  reg    [3:0] rdata ;

  parameter bit_period = 0.4 ;
  parameter half_link_delay = 80 ; // how many bits we'll pack into a packet.
				// which is also half the link latency...
  parameter ifile="sit_pipei" ;
  parameter ofile="sit_pipeo" ;
  parameter filename_from_param_only = 0 ;

  integer  fd_i, fd_o ;
  integer  len_in ;
  reg [8*100-1:0] rcvbuf ;
  reg [8*64:1] filename ;
  reg [8*65:1] ifilename ;
  reg [8*65:1] ofilename ;

  reg    tclk, rclk ;
  reg [half_link_delay-1:0] tx_data, tx_data_sent ;
  reg [half_link_delay-1:0] tx_dat[0:3] ;
  reg [half_link_delay-1:0] rx_data ;
  reg [half_link_delay-1:0] rx_dat[0:3] ;
  reg [7:0] tx_cntr, rx_cntr ;
  reg [1:0] rx_ptr, rx_flag, tx_flag ;
  integer i, i1 ;
  reg [7:0] tmpdata ;
  integer rx_block ;
  reg [31:0] expect ;
  reg       eof_ifile = 0 ;


  /* Format for data, by bytes:
   * 1 - header flag (rotates through values of C/B/A/F)
   * 2 - type: 6 for this data packet
   * 3 - length in DW of data, / 4 (includes real data only, not time)
   * 4 - rsvd
   * 5-8 - rsvd for time info, to be used at later date
   * 9-28 - link0 data - each byte represents a nibble
   *    of serial data encoded as values 0x30-0x41 - 0x40 being Z, 0x41 as X.
   *    (X and Z get spread to full nibble).
   * 29-48 - link1 data
   * 49-68 - link2 data
   * 69-88 - link3 data
   * 100 - a \n to finish the packet.
   *
   * we're using 80 bits per packet, so it will look like:
   *
   *  C65     @@?5=4751:3256<5:::::::::::::::::::::::::>0;=4751:3256<5::::::::::::::::::::::::           
   *  B65     ::::::::::::::::::::@@?5=4751:3256<5::::@@?5=4751:3256<5::::@@?5=4751:3256<5::::           
   *  A65     :>0;=4751:3256<5::::....           
   */

  task send_data ;
  begin
    case (tx_flag)
	0: $fwrite (fd_o, "C6") ;
	1: $fwrite (fd_o, "B6") ;
	2: $fwrite (fd_o, "A6") ;
	3: $fwrite (fd_o, "F6") ;
    endcase
    tmpdata = half_link_delay/16 + 8'h30 ;
    /* in below, there are 4 reserved bytes that will get time info later.*/
    $fwrite (fd_o, "%s     ", tmpdata) ;
    tx_flag = tx_flag + 1 ;
    tmpdata = 0 ;
    tx_data_sent = tx_dat[0] ;
    for (i1=0; i1<4 ; i1=i1+1) begin
      tx_data = tx_dat[i1] ;
      for (i=0; i<half_link_delay; i=i+1) begin
	if ((tmpdata < 16) && (tx_data[i] === 0))
	  tmpdata = tmpdata << 1 ;
	else if ((tmpdata < 16) && (tx_data[i] === 1))
	  tmpdata = (tmpdata << 1) | 1 ;
	else if ((tmpdata != 16) && (tx_data[i] === 1'bz))
	  tmpdata = 17 ;
	else if (tx_data[i] === 1'bx)
	  tmpdata = 16 ;
	if (3 == (i & 3)) begin
	  tmpdata = tmpdata + 48 ;
	  $fwrite (fd_o, "%s", tmpdata) ;
	  tmpdata = 0 ;
	end
      end
    end  // end of for loop
    /* we've written 8+ half_link_delay) bytes, fill in the remainder */
    $fwrite (fd_o, "    ");
    $fwrite (fd_o, "    ");
    $fwrite (fd_o, "   \n");
    $fflush (fd_o) ;
  end
  endtask

  task rcv_data ;
  begin
    len_in = $fgets (rcvbuf, fd_i) ;
    if ((len_in < 100) && (len_in > 0))
	rcvbuf = rcvbuf << ((100 - len_in)*8) ;
    if (len_in == 0) begin
      $display ($stime, " EOF on file/pipe %s.", ifilename) ;
      eof_ifile = 1 ;
      end
    else if (rcvbuf[(8*(100-1)-1):(8*(100-2))] == "3") begin
      $display ($stime, " end record on file/pipe %s.", ifilename) ;
      eof_ifile = 1 ;
      end      
    else while (rcvbuf[(8*(100-1)-1):(8*(100-2))] != "6") begin
	$display ("sit_link non-x4-data record: %s in %s", 
		  (rcvbuf[(8*(100-1)-1):(8*(100-2))]), rcvbuf) ;
	case (rcvbuf[(8*100)-1:8*99])
	  8'h43: rx_flag = 1 ;
	  8'h42: rx_flag = 2 ;
	  8'h41: rx_flag = 3 ;
	  8'h46: rx_flag = 0 ;
	endcase
	len_in = $fgets (rcvbuf, fd_i) ;
	if ((len_in < 100) && (len_in > 0))
          rcvbuf = rcvbuf << ((100 - len_in)*8) ;
    end
    case (rx_flag)
      0: expect = "C6  " ;
      1: expect = "B6  " ;
      2: expect = "A6  " ;
      3: expect = "F6  " ;
    endcase
    expect[15:8] = half_link_delay/16 + 8'h30 ;
    if (~eof_ifile && (len_in != 100))
      $display ($stime, " error reading from file %s, non-100 char record",
	ifilename) ;
    else if (~eof_ifile && (rcvbuf[(8*100)-1:8*96] != expect))
       $display ($stime, " surprising rx pkt hdr, %s,\n expected %s", 
		 rcvbuf[(8*100)-1:8*96], expect) ;
    case (rcvbuf[(8*100)-1:8*99])
	8'h43: rx_flag = 1 ;
	8'h42: rx_flag = 2 ;
	8'h41: rx_flag = 3 ;
	8'h46: rx_flag = 0 ;
    endcase
    rx_data = 0 ;
    if (~eof_ifile)
      for (i1=0;i1<4;i1=i1+1) begin
	for (i=half_link_delay; i>0; i=i-4) begin
	/* start at end of data and shift it left so [0] is first on wire.
      	 *   with 8 prefix bytes, 20 data bytes, 12 unused bytes,
      	 * first bits on wire in byte 9, last byte 28.
	 * byte 100 is in bits 7:0
     	 * byte 88 is in bits 13*8-1 to 12*8 (link3 last nibble)
	 * byte 28 is in bits 73*8-1 to 72*8 (link0 last nibble)
	 * byte  9 is in bits 92*8-1 to 91*8 (link0 first nibble)
	 *  i will be 80, 76, ..., 4 (never 0), i1 will be 0, 1, 2, 3
      	 */
	tmpdata = (rcvbuf >> (8*(92-i1*20-i/4)) & 8'hff) - 8'h30 ;
	if (tmpdata == 16)
	  rx_data =  {rx_data, 4'bxxxx} ;
	else if (tmpdata == 17)
	  rx_data =  {rx_data, 4'bzzzz} ;
	else
	  rx_data = {rx_data, tmpdata[0], tmpdata[1], tmpdata[2], tmpdata[3]} ;
	end
	rx_dat[i1] = rx_data ;
    end
  end
  endtask

  initial begin
    tclk = 1 ;
    rclk = 1 ;
    tx_cntr = 0 ;
    rx_cntr = 0 ;
    rx_ptr = 0 ;
    tx_flag = 0 ;
    rx_flag = 0 ;
    rx_block = 0 ;
    if (filename_from_param_only ||
	! $value$plusargs("SIT_FILENAME=%s", filename)) begin
      $display ("taking file name from parameter");
	/* order is important here - open o before i. */
      fd_o = $fopen (ofile, "w") ;
      fd_i = $fopen (ifile, "r") ;
      ifilename = ifile ;
      ofilename = ofile ;
    end
    else begin
      $display ("taking file name from command line: %s", filename) ;
      ifilename = (filename << 8) | "i" ;
      ofilename = (filename << 8) | "o" ;
	/* order is important here - open o before i. */
      fd_o = $fopen (ofilename, "w") ;
      fd_i = $fopen (ifilename, "r") ;
    end
  end

  always #bit_period tclk = ~tclk ;
  always #bit_period rclk = ~ rclk ;

  always @ (tclk) begin
    for (i=0; i<4; i=i+1) begin
      tx_data = tx_dat[i] ;
      tx_data[tx_cntr] = tdata[i] ;
      tx_dat[i] = tx_data ;
    end
    tx_cntr = tx_cntr + 1 ;
    if (tx_cntr == half_link_delay) begin
      tx_cntr = 0 ;
      send_data ;
    end
  end

  always @ ( rclk) begin
    for (i=0; i<4; i=i+1) begin
      rx_data = rx_dat[i] ;
      rdata[i] = rx_data[rx_cntr] ;
    end
    rx_cntr = rx_cntr + 1 ;
    if (rx_cntr == half_link_delay) begin
      rx_cntr = 0 ;
      if ((rx_block > 0) && ~eof_ifile)
	rcv_data ;
      rx_block = rx_block + 1 ;
    end
  end

endmodule
/* 
 * $Log: sit_ver_x4.v,v $
 * Revision 1.3  2005/07/25 12:54:59  cbenz
 * 1.2 was broken, compared to 4 instead of "6". some more cleanup as well.
 *
 * Revision 1.2  2005/07/22 00:00:32  cbenz
 * *** empty log message ***
 *
 * Revision 1.1  2005/07/12 13:16:39  cbenz
 * Initial revision
 *
 * Revision 1.1  2005/07/07 16:44:04  cbenz
 * Initial revision
 */
