// 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_cdr.v,v 1.2 2005/07/27 15:15:53 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 22-Jul-05 version is the first attempt to track varying clocks, * actually doing so independently on each lane of serial data. * For each lane, in a 80 bit sample time, we will report on how * many times we moved the clock by "delta" and whether or not that * resulted in one extra or one less sample of data. * 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_cdr (tdata, rdata) ; input [3:0] tdata ; output [3:0] rdata ; reg [3:0] rdata ; parameter bit_period = 0.40 ; parameter delta = 0.01 ; 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, rclk0, rclk1, rclk2, rclk3 ; reg [half_link_delay:0] tx_data, tx_data_sent ; reg [half_link_delay:0] tx_dat[0:3] ; reg [half_link_delay:0] rx_data ; reg [half_link_delay:0] rx_dat1[0:3] ; reg [half_link_delay:0] rx_dat2[0:3] ; reg [7:0] tx_cntr, tx_cntr0, tx_cntr1, tx_cntr2, tx_cntr3 ; reg [1:0] rx_ptr, rx_flag, tx_flag ; reg [3:0] rxdatflags ; reg [9:0] rx_adj1 [0:3] ; reg [9:0] rx_adj2_0, rx_adj2_1, rx_adj2_2, rx_adj2_3 ; reg [9:0] rx_cntr0, rx_cntr1, rx_cntr2, rx_cntr3 ; reg [9:0] rx_cntr_lim1 [0:3] ; reg [9:0] rx_cntr_lim2 [0:3] ; reg [7:0] rx_interval_adj [0:3] ; integer i, i1 ; reg [7:0] tmpdata ; reg [15:0] tmpdata2 ; integer rx_block ; reg [31:0] expect ; reg eof_ifile = 0 ; wire tclk0, tclk1, tclk2, tclk3 ; wire [3:0] tdata_cdr, spedup, slowed ; reg [3:0] en_adj = 0 ; reg [9:0] num_adj0, num_adj1, num_adj2, num_adj3; reg rclk0_adj = 0 ; sit_cdr tcdr0 (.din(tdata[0]), .rdata(tdata_cdr[0]), .rclk(tclk0), .spedup(spedup[0]), .slowed(slowed[0]), .en_adj(en_adj[0])) ; sit_cdr tcdr1 (.din(tdata[1]), .rdata(tdata_cdr[1]), .rclk(tclk1), .spedup(spedup[1]), .slowed(slowed[1]), .en_adj(en_adj[1])) ; sit_cdr tcdr2 (.din(tdata[2]), .rdata(tdata_cdr[2]), .rclk(tclk2), .spedup(spedup[2]), .slowed(slowed[2]), .en_adj(en_adj[2])) ; sit_cdr tcdr3 (.din(tdata[3]), .rdata(tdata_cdr[3]), .rclk(tclk3), .spedup(spedup[3]), .slowed(slowed[3]), .en_adj(en_adj[3])) ; defparam tcdr0.half_clk = bit_period / 2 ; defparam tcdr1.half_clk = bit_period / 2 ; defparam tcdr2.half_clk = bit_period / 2 ; defparam tcdr3.half_clk = bit_period / 2 ; defparam tcdr0.delta = delta ; defparam tcdr1.delta = delta ; defparam tcdr2.delta = delta ; defparam tcdr3.delta = delta ; /* Format for data, by bytes: * 1 - header flag (rotates through values of C/B/A/F) * 2 - type: 7 for this data packet * 3 - length in DW of data, / 4 (includes real data only, not time info) * 4 - rsvd * 5-12 - 2 bytes per link (0-3) for time adjustments, bytes 5/6 for link 0: * bits 11:10: * 00 - N samples * 01 - N-1 samples * 10 - N+1 samples, extra is 0 * 11 - N+1 samples, extra is 1 * bits 9:0 - signed # of clock adjustments, -512 to 511 * (but actual # of slips will be limited to just under 1 bit time) * 13-32 - 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). * 33-52 - link1 data * 53-72 - link2 data * 73-92 - 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::::.... */ /* * 1. Accumulate serial data based on tclk0-tclk3 sampling. * 2. count number of adjustments. If we reach (bit_period-3*delta)/delta, * then don't allow any more adjustments in this "packet interval" * 3. When we reach nominal "half_link_delay" (based on ideal clock), take data * and send data. Some links may (eventually) be ahead/behind by 1 bit time. * * 4. receive data when any link thinks it needs more. * 5. spread out needed adjustments by interval = (half_link_delay) / num_adj * (interval being a truncated integer), with 1st adjustment immediately. */ task send_data ; begin case (tx_flag) 0: $fwrite (fd_o, "C7") ; 1: $fwrite (fd_o, "B7") ; 2: $fwrite (fd_o, "A7") ; 3: $fwrite (fd_o, "F7") ; endcase tx_flag = tx_flag + 1 ; tmpdata = half_link_delay/16 + 8'h30 ; $fwrite (fd_o, "%s ", tmpdata) ; /* calculate clock adj bytes... */ if (tx_cntr0 == half_link_delay) tmpdata2 = num_adj0 ; else if (tx_cntr0 == (half_link_delay-1)) tmpdata2 = num_adj0 | 12'h400 ; else if (tx_cntr0 == (half_link_delay+1)) tmpdata2 = (num_adj0 | 12'h800 | ((tx_dat[0] >> (half_link_delay - 10)) & 12'h400)) ; else $display ($stime, " link0 something is wrong: report to developer"); tmpdata2[13:6] = tmpdata2[11:6] << 2 ; tmpdata2 = tmpdata2 + 16'h3030 ; $fwrite (fd_o, "%s", tmpdata2) ; if (tx_cntr1 == half_link_delay) tmpdata2 = num_adj1 ; else if (tx_cntr1 == (half_link_delay-1)) tmpdata2 = num_adj1 | 12'h400 ; else if (tx_cntr1 == (half_link_delay+1)) tmpdata2 = (num_adj1 | 12'h800 | ((tx_dat[1] >> (half_link_delay - 10)) & 12'h400)) ; else $display ($stime, " link1 something is wrong: report to developer"); tmpdata2[13:6] = tmpdata2[11:6] << 2 ; tmpdata2 = tmpdata2 + 16'h3030 ; $fwrite (fd_o, "%s", tmpdata2) ; if (tx_cntr2 == half_link_delay) tmpdata2 = num_adj2 ; else if (tx_cntr2 == (half_link_delay-1)) tmpdata2 = num_adj2 | 12'h400 ; else if (tx_cntr2 == (half_link_delay+1)) tmpdata2 = (num_adj2 | 12'h800 | ((tx_dat[2] >> (half_link_delay - 10)) & 12'h400)) ; else $display ($stime, " link2 something is wrong: report to developer"); tmpdata2[13:6] = tmpdata2[11:6] << 2 ; tmpdata2 = tmpdata2 + 16'h3030 ; $fwrite (fd_o, "%s", tmpdata2) ; if (tx_cntr3 == half_link_delay) tmpdata2 = num_adj3 ; else if (tx_cntr3 == (half_link_delay-1)) tmpdata2 = num_adj3 | 12'h400 ; else if (tx_cntr3 == (half_link_delay+1)) tmpdata2 = (num_adj3 | 12'h800 | ((tx_dat[3] >> (half_link_delay - 10)) & 12'h400)) ; else $display ($stime, " link3 something is wrong: report to developer"); tmpdata2[13:6] = tmpdata2[11:6] << 2 ; tmpdata2 = tmpdata2 + 16'h3030 ; $fwrite (fd_o, "%s", tmpdata2) ; num_adj0 = 0 ; num_adj1 = 0 ; num_adj2 = 0 ; num_adj3 = 0 ; tx_cntr0 = 0 ; tx_cntr1 = 0 ; tx_cntr2 = 0 ; tx_cntr3 = 0 ; 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 1) && ~eof_ifile) 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 if (rcvbuf[(8*(100-1)-1):(8*(100-2))] == "5") begin /* text string message from remote side, display it */ tmpdata = (100 - 4 - (4 * rcvbuf[(8*(100-2)-1):(8*(100-3))]) ; $display ("%m: %s", rcvbuf[8*(100-4)-1:8*tmpdata] ); end else while (rcvbuf[(8*(100-1)-1):(8*(100-2))] != "7") 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 = "C7 " ; 1: expect = "B7 " ; 2: expect = "A7 " ; 3: expect = "F7 " ; 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 92 is in bits 9*8-1 to 8*8 (link3 last nibble) * byte 32 is in bits 69*8-1 to 68*8 (link0 last nibble) * byte 13 is in bits 88*8-1 to 87*8 (link0 first nibble) * * bytes 5/6 are in bits 96*8-1 to 94*8 */ tmpdata = (rcvbuf >> (8*(88-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 tmpdata2 = (rcvbuf >> (8*(94-i1*2)) & 16'hffff) - 16'h3030 ; rx_dat1[i1] = {tmpdata2[12], rx_data[half_link_delay-1:0]} ; /* rx_adj bit [9] is the sign, change bits [8:0] to absolute value... */ if (tmpdata2[11]) begin rx_adj1[i1] = {tmpdata2[11], ~tmpdata2[10:8], ~tmpdata2[5:0]} ; rx_adj1[i1] = rx_adj1[i1] + 1 ; end else rx_adj1[i1] = {tmpdata2[11:8], tmpdata2[5:0]} ; rx_cntr_lim1[i1] = (tmpdata2[13:12] == 1) ? (half_link_delay - 1) : (half_link_delay + tmpdata2[13]) ; end // $display ("rcv line %s", rcvbuf); // $display ($stime, " rcv data for link0: adj %d, cntrlim %d, data:\n%b", // rx_adj1[0], rx_cntr_lim1[0], rx_dat1[0]); end end endtask initial begin tclk = 1 ; rclk0 = 0 ; rclk1 = 0 ; rclk2 = 0 ; rclk3 = 0 ; tx_cntr = 0 ; num_adj0 = 0 ; num_adj1 = 0 ; num_adj2 = 0 ; num_adj3 = 0 ; tx_cntr0 = 0 ; tx_cntr1 = 0 ; tx_cntr2 = 0 ; tx_cntr3 = 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 /* 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 rxdatflags = 0 ; for (i1=0;i1<4;i1=i1+1) begin rx_adj1[i1] = 0 ; rx_cntr_lim1[i1] = half_link_delay ; rx_cntr_lim2[i1] = half_link_delay ; tx_dat[i1] = 0 ; end rx_cntr0 = 0 ; rx_cntr1 = 0 ; rx_cntr2 = 0 ; rx_cntr3 = 0 ; rx_adj2_0 = 0 ; rx_adj2_1 = 0 ; rx_adj2_2 = 0 ; rx_adj2_3 = 0 ; end always #bit_period tclk = ~tclk ; always @ (tclk) begin tx_cntr = tx_cntr + 1 ; if (tx_cntr == half_link_delay) begin tx_cntr = 0 ; send_data ; end end always @ (posedge tclk0) begin #(2*delta) ; tx_data = tx_dat[0] ; tx_data[tx_cntr0] = tdata[0] ; tx_dat[0] = tx_data ; tx_cntr0 = tx_cntr0 + 1 ; if (spedup[0]) num_adj0 = num_adj0 + 1 ; else if (slowed[0]) num_adj0 = num_adj0 - 1 ; en_adj[0] = (num_adj0 <= ((bit_period/delta)-3)) || (num_adj0 >= (1024-(bit_period/delta))) ; end always @ (posedge tclk1) begin #(2*delta) ; tx_data = tx_dat[1] ; tx_data[tx_cntr1] = tdata[1] ; tx_dat[1] = tx_data ; tx_cntr1 = tx_cntr1 + 1 ; if (spedup[1]) num_adj1 = num_adj1 + 1 ; else if (slowed[1]) num_adj1 = num_adj1 - 1 ; en_adj[1] = (num_adj1 <= ((bit_period/delta)-3)) || (num_adj1 >= (1024-(bit_period/delta))) ; end always @ (posedge tclk2) begin #(2*delta) ; tx_data = tx_dat[2] ; tx_data[tx_cntr2] = tdata[2] ; tx_dat[2] = tx_data ; tx_cntr2 = tx_cntr2 + 1 ; if (spedup[2]) num_adj2 = num_adj2 + 1 ; else if (slowed[2]) num_adj2 = num_adj2 - 1 ; en_adj[2] = (num_adj2 <= ((bit_period/delta)-3)) || (num_adj2 >= (1024-(bit_period/delta))) ; end always @ (posedge tclk3) begin #(2*delta) ; tx_data = tx_dat[3] ; tx_data[tx_cntr3] = tdata[3] ; tx_dat[3] = tx_data ; tx_cntr3 = tx_cntr3 + 1 ; if (spedup[3]) num_adj3 = num_adj3 + 1 ; else if (slowed[3]) num_adj3 = num_adj3 - 1 ; en_adj[3] = (num_adj3 <= ((bit_period/delta)-3)) || (num_adj3 >= (1024-(bit_period/delta))) ; end always begin #(bit_period / 2) rclk0 <= 1 ; if (rx_adj2_0[8:0] != 0) begin if ((rx_cntr0 % rx_interval_adj[0]) != 1) #(bit_period / 2) rclk0 <= 0 ; else begin rx_adj2_0[8:0] = rx_adj2_0[8:0] - 1 ; rclk0_adj <= ~rclk0_adj ; if (rx_adj2_0[9]) // slowed #((bit_period / 2) + delta) rclk0 <= 0 ; else // spedup #((bit_period / 2) - delta) rclk0 <= 0 ; end end else #(bit_period / 2) rclk0 <= 0 ; end always begin #(bit_period / 2) rclk1 <= 1 ; if (rx_adj2_1[8:0] != 0) begin if ((rx_cntr1 % rx_interval_adj[1]) != 1) #(bit_period / 2) rclk1 <= 0 ; else begin rx_adj2_1[8:0] = rx_adj2_1[8:0] - 1 ; if (rx_adj2_1[9]) // slowed #((bit_period / 2) + delta) rclk1 <= 0 ; else // spedup #((bit_period / 2) - delta) rclk1 <= 0 ; end end else #(bit_period / 2) rclk1 <= 0 ; end always begin #(bit_period / 2) rclk2 <= 1 ; if (rx_adj2_2[8:0] != 0) begin if ((rx_cntr2 % rx_interval_adj[2]) != 1) #(bit_period / 2) rclk2 <= 0 ; else begin rx_adj2_2[8:0] = rx_adj2_2[8:0] - 1 ; if (rx_adj2_2[9]) // slowed #((bit_period / 2) + delta) rclk2 <= 0 ; else // spedup #((bit_period / 2) - delta) rclk2 <= 0 ; end end else #(bit_period / 2) rclk2 <= 0 ; end always begin #(bit_period / 2) rclk3 <= 1 ; if (rx_adj2_3[8:0] != 0) begin if ((rx_cntr3 % rx_interval_adj[3]) != 1) #(bit_period / 2) rclk3 <= 0 ; else begin rx_adj2_3[8:0] = rx_adj2_3[8:0] - 1 ; if (rx_adj2_3[9]) // slowed #((bit_period / 2) + delta) rclk3 <= 0 ; else // spedup #((bit_period / 2) - delta) rclk3 <= 0 ; end end else #(bit_period / 2) rclk3 <= 0 ; end always @ (posedge rclk0) begin if (rx_cntr0 == rx_cntr_lim2[0]) begin if ((rxdatflags[0] == 0) && (rxdatflags != 0)) $display (" link0 wants new data, but not all links took theirs: ", "report to developer"); else if (rxdatflags[0] == 0) rcv_data ; rxdatflags[0] = 0 ; rx_dat2[0] = rx_dat1[0] ; rx_adj2_0 = rx_adj1[0] ; rx_cntr_lim2[0] = rx_cntr_lim1[0] ; rx_cntr0 = 0 ; if (rx_adj2_0[8:0] != 0) rx_interval_adj[0] = rx_cntr_lim2[0] / rx_adj2_0[8:0] ; end rx_data = rx_dat2[0] ; rdata[0] = rx_data[rx_cntr0] ; rx_cntr0 = rx_cntr0 + 1 ; end always @ (posedge rclk1) begin if (rx_cntr1 == rx_cntr_lim2[1]) begin if ((rxdatflags[1] == 0) && (rxdatflags != 0)) $display (" link1 wants new data, but not all links took theirs: ", "report to developer"); else if (rxdatflags[1] == 0) rcv_data ; rxdatflags[1] = 0 ; rx_dat2[1] = rx_dat1[1] ; rx_adj2_1 = rx_adj1[1] ; rx_cntr_lim2[1] = rx_cntr_lim1[1] ; rx_cntr1 = 0 ; if (rx_adj2_1[8:0] != 0) rx_interval_adj[1] = rx_cntr_lim2[1] / rx_adj2_1[8:0] ; end rx_data = rx_dat2[1] ; rdata[1] = rx_data[rx_cntr1] ; rx_cntr1 = rx_cntr1 + 1 ; end always @ (posedge rclk2) begin if (rx_cntr2 == rx_cntr_lim2[2]) begin if ((rxdatflags[2] == 0) && (rxdatflags != 0)) $display (" link2 wants new data, but not all links took theirs: ", "report to developer"); else if (rxdatflags[2] == 0) rcv_data ; rxdatflags[2] = 0 ; rx_dat2[2] = rx_dat1[2] ; rx_adj2_2 = rx_adj1[2] ; rx_cntr_lim2[2] = rx_cntr_lim1[2] ; rx_cntr2 = 0 ; if (rx_adj2_2[8:0] != 0) rx_interval_adj[2] = rx_cntr_lim2[2] / rx_adj2_2[8:0] ; end rx_data = rx_dat2[2] ; rdata[2] = rx_data[rx_cntr2] ; rx_cntr2 = rx_cntr2 + 1 ; end always @ (posedge rclk3) begin if (rx_cntr3 == rx_cntr_lim2[3]) begin if ((rxdatflags[3] == 0) && (rxdatflags != 0)) $display (" link3 wants new data, but not all links took theirs: ", "report to developer"); else if (rxdatflags[3] == 0) rcv_data ; rxdatflags[3] = 0 ; rx_dat2[3] = rx_dat1[3] ; rx_adj2_3 = rx_adj1[3] ; rx_cntr_lim2[3] = rx_cntr_lim1[3] ; rx_cntr3 = 0 ; if (rx_adj2_3[8:0] != 0) rx_interval_adj[3] = rx_cntr_lim2[3] / rx_adj2_3[8:0] ; end rx_data = rx_dat2[3] ; rdata[3] = rx_data[rx_cntr3] ; rx_cntr3 = rx_cntr3 + 1 ; end wire #bit_period rclk0_del = rclk0 ; endmodule /* * $Log: sit_ver_x4_cdr.v,v $ * Revision 1.2 2005/07/27 15:15:53 cbenz * Revision 1.1 2005/07/25 12:56:02 cbenz */