///////////////////////////////////////////////////////////////////////
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// ====================================================================
// Copyright 2002 (c) Lattice Semiconductor Corporation
// ALL RIGHTS RESERVED
//
// This confidential and proprietary software may be used only as
// authorised by a licensing agreement from Lattice Semiconductor
// Corporation.
// The entire notice above must be reproduced on all authorized
// copies and copies may only be made to the extent permitted
// by a licensing agreement from Lattice Semiconductor Corporation.
//

// ====================================================================
// File Details
// ====================================================================
// Project              :       ORSPII
// Filename             :       latspi4_rx.v
// Author               :       Ben_Kerr,bkerr@latticesemi.com
// Created on           :       Thursday June 27 2002 (WW25)
// Last Modified By     :       $Author: bkerr $
// Last Modified On     :       $Date: Wed Sep 25 07:38:17 2002 $ 
// Revision             :       $Revision: 1.12 $
// Status               :       Development
// Dependencies         :       latspi4_inc.v
//=====================================================================

//=====================================================================
// Description        :
//=====================================================================
// This module implements a bus functional model of the receive (ingress)
// SPI4 interface. The following tasks perform protocol and compliance
// verification of a SPI4 compliant interface.
//
// Task list
// ---------
// rx          : controls a process to receive payload data
// cal_tx      : controls a process to send FIFO status
//
// FIFO status
// -----------
// This module does not emulate a receive FIFO, and therefore does not
// provide any feedback on the RSTAT interface. As no buffering of data
// is performed, the DUT is able to send data at full line-rate without the
// possibilty of overflowing a storage element. The cal_rx task only
// provides the ability to alter the FIFO status for all ports in the
// calendar sequence, therefore providing basic flow-control of the DUT.
//
// Credit checking
// ---------------
// This module records the amount of credit granted on the RSTAT interface
// to a particular port. As words are received on a port, the amount of
// credit is reduced. If the credit count ever goes negative, (the DUT has
// transferred more data than it was allowed), an error is generated.
//
//=====================================================================

//=====================================================================
// Revision History
//=====================================================================
// $Log: latspi4_rx.v.rca $
// 
//  Revision: 1.12 Wed Sep 25 07:38:17 2002 bkerr
//  Added check_credit control to allow credit checking to be disabled
//  when LatSPI4 is used for monitoring only in HS loopback mode
// 
//  Revision: 1.11 Tue Sep 24 09:11:01 2002 bkerr
//  Corrected bug to set cal_m and cal_len variables when CALENDAR_SW_EN_TX
//  was 0
// 
//  Revision: 1.10 Tue Sep 24 05:24:11 2002 bkerr
//  Altered cal_tx task to pick-up correct CAL_M and CAL_LEN values
//  depending on which calendar sequence is being sent
// 
//  Revision: 1.9 Fri Sep  6 08:13:45 2002 bkerr
//  Implemented packet length counting to allow for more detailed fault
//  investigation
// 
//  Revision: 1.8 Fri Aug 23 08:22:56 2002 bkerr
//  Added initial to drive out 2'b11 on the STAT interface to force
//  the DUT to output training during start-up
// 
//  Revision: 1.7 Mon Aug  5 10:32:35 2002 bkerr
//  Corrected bug in invalid burst detection logic
// 
//  Revision: 1.6 Mon Aug  5 09:52:38 2002 bkerr
//  Added invalid burst length checker
// 
//  Revision: 1.5 Tue Jul 16 12:03:35 2002 bkerr
//  Added rx & tx sop and eop counters per port
// 
//  Revision: 1.4 Fri Jul 12 09:48:13 2002 bkerr
//  Fixed bug in expected EOPS checker
//  Incremented toptest.error_count when reporting errors
// 
//  Revision: 1.3 Wed Jul 10 12:12:55 2002 bkerr
//  Fixed bug in bad EOPS detection
// 
//  Revision: 1.2 Thu Jul  4 06:25:27 2002 bkerr
//  Added credit checking functionality
// 
//  Revision: 1.1 Tue Jul  2 03:53:52 2002 bkerr
//  Initial creation
//=====================================================================

`timescale 1ps/1ps

module latspi4_rx(
   rdclk,               // INPUT  - data bus clock
   rdat,                // INPUT  - data bus
   rctl,                //.INPUT  - control word indication
   rsclk,               // OUTPUT - FIFO status clock
   rstat                // OUTPUT - FIFO status
   );

// ---------------------------------------------------------------------------
// includes
// ---------------------------------------------------------------------------
`include "latspi4_inc.v"

// ---------------------------------------------------------------------------
// parameters
// ---------------------------------------------------------------------------
parameter OP_DELAY    =   1000;      // clk->out delay in ps
parameter DEBUG       =   0;         // control display of debug messages

// ---------------------------------------------------------------------------
// I/O declarations
// ---------------------------------------------------------------------------
input          rdclk;
input   [15:0] rdat;
input          rctl;
output         rsclk;
output  [ 1:0] rstat;

wire           rdclk;
wire    [15:0] rdat;
wire           rctl;
wire           rsclk;
wire    [ 1:0] rstat;

// ---------------------------------------------------------------------------
// local variables
// ---------------------------------------------------------------------------
reg    [ 15:0]  rdat_in;               // RDAT with DIP-4 replaced with calculated
                                       // value

reg    [  1:0]  rstat_out;             // clock edge aligned RSTAT
reg    [  1:0]  rsclk_div;             // RSCLK divider

reg    [ 15:0]  prev_dip16;            // Storage for previously calculated DIP-16 value
reg    [  1:0]  prev_dip2;             // Storage for previously calculated DIP-2 value 
reg    [  7:0]  prev_port;             // Storage for previous port number

reg    [  7:0]  port_count  [255:0];   // Array of counters for each port
reg    [255:0]  port_open;             // Flags indicating if an SOP has opened a port
integer         credit      [255:0];   // Granted credit count per port
integer         num_words;             // Number of 16-bit words received in burst
integer         num_bytes   [255:0];   // Number of bytes recieved per port
reg             check_credit;          // Enable credit checking

// training sequence counters
integer         num_tcw;
integer         num_tdw;

// extracted control word fields
reg    [  1:0]  eops;
reg    [  7:0]  port;
reg             sop;

// expected control word fields
reg    [  1:0]  expected_eops;         // Expected EOPS based on last word in burst
reg    [  1:0]  expected_eops_1q;      // 1 cycle delayed to align with control word

// legal RDAT state machine next-state transitions
reg             expect_pcw;
reg             expect_pdw;
reg             expect_ext;
reg             expect_eoe;
reg             expect_tcw;
reg             expect_tdw;
reg             expect_idle;

// ---------------------------------------------------------------------------
// Events
// ---------------------------------------------------------------------------
// error conditions
event          dip4_error;
event          incorrect_eops;
event          invalid_cw;
event          invalid_port_in_pdw;
event          invalid_count_in_pdw;
event          invalid_tdw;
event          invalid_pdw;
event          invalid_sop;
event          invalid_eops;
event          credit_exceeded;

// ---------------------------------------------------------------------------
// continuous assignments
// ---------------------------------------------------------------------------
// Model clk->out delay
assign  #(OP_DELAY) rstat   = rstat_out;

initial
   rstat_out = 2'b11;

// ---------------------------------------------------------------------------
// rsclk generation - 1/4 frequency of rdclk
// ---------------------------------------------------------------------------
initial
   rsclk_div   = 2'b00;

always @(posedge rdclk)
   rsclk_div = rsclk_div + 2'b01;

assign rsclk = rsclk_div[1];

// ---------------------------------------------------------------------------
// Perform DIP-4 calculation on all incomming words
// ---------------------------------------------------------------------------
always @(posedge rdclk or negedge rdclk)
   rdat_in = calc_dip4(rctl,rdat);

// ---------------------------------------------------------------------------
// Configure local variables
// ---------------------------------------------------------------------------
initial
begin : config_main

   integer i;

   prev_dip16  = 16'h0000;
   prev_dip2   = 2'b00;
   for(i=0;i<256;i=i+1)
   begin
      port_count[i]  = 8'h00;
      credit[i]      = 0;
      num_bytes[i]   = 0;
   end

   port_open = {256{1'b0}};
   // possible RDAT state machine states out of reset
   expect_idle = 1'b1;
   expect_tcw  = 1'b1;
   expect_pcw  = 1'b1;
   expect_pdw  = 1'b0;
   expect_tdw  = 1'b0;
   expect_ext  = 1'b0;
   expect_eoe  = 1'b0;

   // reset training burst counters
   num_tcw     = 0;
   num_tdw     = 0;
   
   // reset burst word counter
   num_words   = 0;
   
   // Check credit by default
   check_credit = 1'b1;
end

// ---------------------------------------------------------------------------
// rx task
// ---------------------------------------------------------------------------
// Description
// -----------
// Starts and stops a process to receive control and payload data provided
// on the RDAT interface
//
// Parameters
// ----------
// start    1 - starts the calendar receive process
// stop     1 - stops the calendar receive process
// ---------------------------------------------------------------------------
task rx;
input      start;
input      stop;
begin
   if(start)
      rx_main.go = 1'b1;

   if(stop)
   begin
      rx_main.go = 1'b0;
      disable rx_main;
   end
end
endtask

// ---------------------------------------------------------------------------
// rx_main process
// ---------------------------------------------------------------------------
// Description
// -----------
// Implements a continuous process to receive control and payload data
// provided on the RDAT interface. Once started, the process runs continuously
// ---------------------------------------------------------------------------
always
begin : rx_main
   // current RDAT state machine state
   reg      idle;
   reg      pcw;
   reg      pdw;
   reg      tcw;
   reg      tdw;
   reg      ext;
   reg      eoe;      
   reg      go;
   
   // Non-blocking control
   wait(go === 1'b1)

   // RDAT state machine
   forever
   begin
      @(posedge rdclk or negedge rdclk)

      idle      = 1'b0;
      pcw       = 1'b0;
      pdw       = 1'b0;
      tcw       = 1'b0;
      tdw       = 1'b0;
      ext       = 1'b0;
      eoe       = 1'b0;

      if(rctl === 1'b1)
      begin
      
         // Check for DIP-4 errors
         if(rdat_in[3:0] !== 4'b1111)
            -> dip4_error;
            
         // Decode control words
         if(rdat_in[15] === 1'b1 &&     // Payload control word
            expect_pcw
            )          
         begin
            pcw               = 1'b1;
            expected_eops_1q  = expected_eops;
            expected_eops     = 2'bxx;
            num_tcw           = 0;
            num_tdw           = 0;

            // extract sop & port details from control word
            prev_port         = port;
            port              = rdat_in[11:4];
            sop               = rdat_in[12];
            eops              = rdat_in[14:13];
            if(sop === 1'b1)
            begin
               port_count[port] = 8'h00;
               latspi4.rx_sop_count[port] = latspi4.rx_sop_count[port] + 1;
            end
               
            // Valid next states
            expect_idle = 1'b0;
            expect_tcw  = 1'b0;
            expect_pcw  = 1'b0;
            expect_pdw  = 1'b1;
            expect_tdw  = 1'b0;
            expect_ext  = 1'b1;
            expect_eoe  = 1'b1;
         end
         else if( rdat_in[15]   === 1'b0     &&  // IDLE control word
                  rdat_in[12:0] === 13'h000f  &&
                  expect_idle
                )
         begin
            idle              = 1'b1;
            sop               = 1'b0;
            eops              = rdat_in[14:13];
            prev_port         = port;
            expected_eops_1q  = expected_eops;
            expected_eops     = 2'bxx;
            num_tcw           = 0;
            num_tdw           = 0;

            // Valid next states
            expect_idle = 1'b1;
            expect_tcw  = 1'b1;
            expect_pcw  = 1'b1;
            expect_pdw  = 1'b0;
            expect_tdw  = 1'b0;
            expect_ext  = 1'b0;
            expect_eoe  = 1'b0;
         end
         else if( rdat_in === `LATSPI4_TCW &&    // Training control word
                expect_tcw
               )       
         begin
            tcw               = 1'b1;
            sop               = 1'b0;
            eops              = 2'bxx;
            expected_eops_1q  = expected_eops;
            expected_eops     = 2'bxx;

            num_tcw           = num_tcw + 1;

            // Check number of training control words
            if(num_tcw >= 10)
            begin
               expect_tcw  = 1'b0;
               expect_tdw  = 1'b1;
               num_tcw     = 0;
            end
            else
            begin
               expect_tcw  = 1'b1;
               expect_tdw  = 1'b0;
            end

            // Valid next states
            expect_idle = 1'b0;
            expect_pcw  = 1'b0;
            expect_pdw  = 1'b0;
            expect_ext  = 1'b0;
            expect_eoe  = 1'b0;
         end
         else if( rdat_in[15:12] === 4'b0001 &&     // Extended control word
                expect_ext
               )
         begin
            ext               = 1'b1;
            sop               = 1'b0;
            eops              = 2'bxx;
            expected_eops_1q  = expected_eops;
            expected_eops     = 2'bxx;

            // Valid next states
            expect_idle = 1'b0;
            expect_tcw  = 1'b0;
            expect_pcw  = 1'b0;
            expect_pdw  = 1'b1;
            expect_tdw  = 1'b0;
            expect_ext  = 1'b1;
            expect_eoe  = 1'b1;
         end
         else if( rdat_in[15:12] === 4'b0111 &&     // End-of-extended control word
                expect_eoe
               )  
         begin
            eoe               = 1'b1;
            sop               = 1'b0;
            eops              = 2'bxx;
            expected_eops_1q  = expected_eops;
            expected_eops     = 2'bxx;
            
            // Valid next states
            expect_idle = 1'b0;
            expect_tcw  = 1'b0;
            expect_pcw  = 1'b0;
            expect_pdw  = 1'b1;
            expect_tdw  = 1'b0;
            expect_ext  = 1'b0;
            expect_eoe  = 1'b1;
         end
         else
         begin
            -> invalid_cw;

            expect_idle = 1'b1;
            expect_tcw  = 1'b1;
            expect_pcw  = 1'b1;
            expect_pdw  = 1'b0;
            expect_tdw  = 1'b0;
            expect_ext  = 1'b0;
            expect_eoe  = 1'b0;
         end

         // Check EOPS bits for the previous burst
         if(eops != expected_eops_1q)
            -> incorrect_eops;
            
         // Check for bad EOPS
         if(eops === `LATSPI4_EOPS_2VAL ||
            eops === `LATSPI4_EOPS_1VAL ||
            eops === `LATSPI4_EOPS_ABRT)
         begin
            if(!port_open[prev_port])
               -> invalid_eops;
            else
               port_open[prev_port] = 1'b0;
               
            if(eops === `LATSPI4_EOPS_1VAL)
               num_bytes[prev_port] = num_bytes[prev_port] - 1;
            
            if(DEBUG)
            begin
               $display("%t: Received eops on port %0d. Length=%0d",
                        $time,
                        prev_port,
                        num_bytes[prev_port]);
               num_bytes[prev_port] = 0;
            end
         end
         
         // Check for bad SOP
         if(sop === `LATSPI4_SOP)
         begin
            if(port_open[port])
               -> invalid_sop;
            else
               port_open[port] = 1'b1;
         end
         
         // Check for incorrect burst length
         if( expected_eops_1q  === `LATSPI4_EOPS_CONT &&
            (num_words % 8) != 0)
            invalid_burst_length(num_words);
            
         num_words = 0;
                              
      end // if(tctrl === 1'b1)
      else if(rctl === 1'b0)     // Decode payload data words
      begin
         if(expect_pdw)  // Payload data
         begin
            pdw   = 1'b1;
            sop   = 1'b0;
            eops  = 2'bxx;
                                    
            // Determine expected EOP
            expected_eops_1q = expected_eops;
            if(rdat_in === 16'hee00)
               expected_eops = `LATSPI4_EOPS_1VAL;
            else if(rdat_in === 16'heeee)
               expected_eops = `LATSPI4_EOPS_2VAL;
            else if(rdat_in === 16'haaaa ||
                    rdat_in === 16'haa00)
               expected_eops = `LATSPI4_EOPS_ABRT;
            else
            begin
               // check TDAT format
               // check port number is correct
               if(rdat_in[15:8] !== port)
                  -> invalid_port_in_pdw;
               // check count in data word is correct
               if(rdat_in[ 7:0] !== port_count[port])
                  -> invalid_count_in_pdw;
               expected_eops = `LATSPI4_EOPS_CONT;
            end
            port_count[port] = port_count[port] + 8'h01;
            num_bytes[port]  = num_bytes[port] + 2;
            
            // Subtract credit & check for credit violation
            num_words = num_words + 1;            
               
            if(
               (
                (num_words % 8) ==  0 ||
                 expected_eops != `LATSPI4_EOPS_CONT
               )
               && check_credit
              )
            begin
               credit[port] = credit[port] - 1;
               if(credit[port] < 0)
               begin
                  -> credit_exceeded;
                  credit[port] = 0;
               end
            end
            
            if(expected_eops !== `LATSPI4_EOPS_CONT)
               latspi4.rx_eop_count[port] = latspi4.rx_eop_count[port] + 1;
            
            // Valid next states
            expect_idle = 1'b1;
            expect_tcw  = 1'b0;
            expect_pcw  = 1'b1;
            expect_pdw  = 1'b1;
            expect_tdw  = 1'b0;
            expect_ext  = 1'b0;
            expect_eoe  = 1'b0;
         end
         else if(expect_tdw) // Training data word
         begin
            if(rdat_in !== `LATSPI4_TDW)
               -> invalid_tdw;
            else
            begin
               tdw               = 1'b1;
               num_tdw           = num_tdw + 1;
               sop               = 1'b0;
               eops              = 2'bxx;
               expected_eops_1q  = expected_eops;
               expected_eops     = 2'bxx;

               if(num_tdw >= 10)
               begin
                  expect_idle = 1'b1;
                  expect_tcw  = 1'b1;
                  expect_pcw  = 1'b1;
                  expect_tdw  = 1'b0;
                  num_tdw     = 0;
               end
               else
               begin
                  expect_idle = 1'b0;
                  expect_tcw  = 1'b0;
                  expect_pcw  = 1'b0;
                  expect_tdw  = 1'b1;
               end

               expect_pdw     = 1'b0;
               expect_ext     = 1'b0;
               expect_eoe     = 1'b0;
            end
         end
         else
         begin
            -> invalid_pdw;
            expect_idle = 1'b1;
            expect_tcw  = 1'b1;
            expect_pcw  = 1'b1;
            expect_pdw  = 1'b0;
            expect_tdw  = 1'b0;
            expect_ext  = 1'b0;
            expect_eoe  = 1'b0;
         end
      end // else if(tctl === 1'b0)
   end // forever
   go = 1'b0;
end // always

// ---------------------------------------------------------------------------
// cal_tx task
// ---------------------------------------------------------------------------
// Description
// -----------
// Starts and stops a process to send FIFO status information on the RSTAT 
// interface
//
// Parameters
// ----------
// start       1 - starts the calendar receive process
// stop        1 - stops the calendar receive process
// cal_sel     0 - calendar select word = 01
//             1 - calendar select word = 10
// status      the FIFO status to send for all ports
// ---------------------------------------------------------------------------
task cal_tx;
input         start;
input         stop;
input         cal_sel;
input [ 1:0]  status;
begin
   if(start)
   begin
      cal_tx_main.cal_sel     = cal_sel;
      cal_tx_main.status      = status;
      
      cal_tx_main.go          = 1'b1;
   end
   
   if(stop)
   begin
      cal_tx_main.go          = 1'b0;
      disable cal_tx_main;
   end
end
endtask

// ---------------------------------------------------------------------------
// cal_tx_main process
// ---------------------------------------------------------------------------
// Description
// -----------
// Implements a continuous process to send FIFO status information on the
// RSTAT interface.  Once started, the process runs continuously.
// The process must be disabled before being restarted with a new cal_len
// & cal_m configuration.
// ---------------------------------------------------------------------------      
always
begin : cal_tx_main
   reg            cal_sel;
   reg    [ 1:0]  status;
   integer        i,j;           // Loop variables
   reg    [ 7:0]  cal_port;      // Current port extracted from calendar
   reg            go;
   integer        cal_m;
   integer        cal_len;

   // Non-blocking control  
   wait(go === 1'b1);
   
   forever
   begin
      if(cal_sel)
      begin
         cal_m        = toptest.CALENDAR_M_TX_SHDW;
         cal_len      = toptest.CALENDAR_LEN_TX_SHDW;
      end
      else
      begin
         cal_m        = toptest.CALENDAR_M_TX;
         cal_len      = toptest.CALENDAR_LEN_TX;
      end
      
      // SYNC
      @(posedge rsclk)
      rstat_out <= calc_dip2(1'b1,1'b0,2'b11);

      // CAL_SEL
      if(toptest.CALENDAR_SW_EN_TX)
      begin
         @(posedge rsclk)
         if(cal_sel)
            rstat_out   <= calc_dip2(1'b0,1'b0,2'b10);
         else
            rstat_out   <= calc_dip2(1'b0,1'b0,2'b01);
      end

      // CAL_M loop
      for(i=0;i<cal_m;i=i+1)
      begin

         // CAL_LEN loop
         for(j=0;j<cal_len;j=j+1)
         begin
            @(posedge rsclk)
            rstat_out   <=  calc_dip2(1'b0,1'b0,status);
            
            // Update credit issued for port
            if(toptest.CALENDAR_SW_EN_TX)
            begin 
               if(cal_sel)
                  cal_port = toptest.TX_SHADOW_CAL_SEQ[j];
               else
                  cal_port = toptest.TX_MAIN_CAL_SEQ[j];
            end
            else
               cal_port    = toptest.TX_MAIN_CAL_SEQ[j];

            case(status)
               `LATSPI4_STARVING  : credit[cal_port]    =  toptest.MAX_BURST1;
               `LATSPI4_HUNGRY    : if(credit[cal_port] >= toptest.MAX_BURST2)
                                       credit[cal_port] =  credit[cal_port];
                                    else
                                       credit[cal_port] =  toptest.MAX_BURST2;
               `LATSPI4_SATISFIED : credit[cal_port]    =  credit[cal_port];
            endcase            
         end
      end

      // DIP2
      @(posedge rsclk)
      rstat_out   <=  calc_dip2(1'b0,1'b1,2'b11);
   end // forever
   go = 1'b0;
end // always

// ---------------------------------------------------------------------------
// Error reporting
// ---------------------------------------------------------------------------
always @(dip4_error)
begin
   if(latspi4.error_enable)
   begin
      $display("%t: %m: ERROR: DIP-4 error",$time);
      toptest.error_count = toptest.error_count + 1;
   end
end

always @(incorrect_eops)
begin
   if(latspi4.error_enable)
   begin
      $display("%t: %m: ERROR: Incorrect EOPS. Got:%x Expected:%x",
               $time,
               eops,
               expected_eops_1q
            );
      toptest.error_count = toptest.error_count + 1;
   end   
end

always @(invalid_cw)
begin
   if(latspi4.error_enable)
   begin
      $display("%t: %m: ERROR: Invalid Control Word. Got:%x",
               $time,
               rdat_in
            );
      toptest.error_count = toptest.error_count + 1;
   end            
end

always @(invalid_port_in_pdw)
begin
   if(latspi4.error_enable)
   begin
      $display("%t: %m: ERROR: Incorrect port in payload data word. Got:%x Expected:%x",
               $time,
               rdat_in[15:8],
               port
            );
      toptest.error_count = toptest.error_count + 1;
   end
end

always @(invalid_count_in_pdw)
begin
   if(latspi4.error_enable)
   begin
      $display("%t: %m: ERROR: Incorrect count in payload data word. Got:%x Expected:%x",
               $time,
               rdat_in[7:0],
               port_count[port]
            );
      toptest.error_count = toptest.error_count + 1;
   end
end

always @(invalid_tdw)
begin
   if(latspi4.error_enable)
   begin
      $display("%t: %m: ERROR: Incorrect training data word. Got:%x Expected:%x",
               $time,
               rdat_in,
               `LATSPI4_TDW
            );
      toptest.error_count = toptest.error_count + 1;
   end
end

always @(invalid_pdw)
begin
   if(latspi4.error_enable)
   begin
      $display("%t: %m: ERROR: Invalid payload data word Got:%x",
               $time,
               rdat_in
            );
      toptest.error_count = toptest.error_count + 1;
   end
end

always @(invalid_sop)
begin
   if(latspi4.error_enable)
   begin
      $display("%t: %m: ERROR: Unexpected SOP. Port %0d already open",
               $time,
               port
            );
      toptest.error_count = toptest.error_count + 1;
   end
end

always @(invalid_eops)
begin
   if(latspi4.error_enable)
   begin
      $display("%t: %m: ERROR: Unexpected EOPS. Port %0d not open",
               $time,
               port
            );
      toptest.error_count = toptest.error_count + 1;
   end
end

task invalid_burst_length;
input [31:0]   num_words;
begin
   if(latspi4.error_enable)
   begin
      $display("%t: %m: ERROR: Invalid burst length received. Got:%0d Expected:multiple of 8",
               $time,
               num_words
            );
      toptest.error_count = toptest.error_count + 1;
   end
end
endtask

always @(credit_exceeded)
begin
   if(latspi4.error_enable)
   begin
      $display("%t: %m: ERROR: Port %0d exceeded credit granted",
               $time,
               port
            );
      toptest.error_count = toptest.error_count + 1;
   end
end

// ---------------------------------------------------------------------------
// Functions
// ---------------------------------------------------------------------------
function [15:0] calc_dip4;
input           ctl;
input    [15:0] d;
reg      [ 7:0] dip8;
reg      [ 3:0] dip4;
begin
   prev_dip16  = {prev_dip16[0],prev_dip16[15:1]}  ^ d;
   dip8        =  prev_dip16[15:8]                 ^ prev_dip16[7:0];
   dip4        =  dip8      [ 7:4]                 ^ dip8      [3:0];

   if(ctl)
   begin
      calc_dip4   =   {d[15:4],dip4};
      prev_dip16  =   16'h0000;
   end
   else
      calc_dip4   =   d;
end
endfunction

function [1:0]  calc_dip2;
input         sync;
input         dip2;
input   [1:0]   d;
begin
   prev_dip2      = {prev_dip2[0],prev_dip2[1]}   ^   d;

   if(sync)      // SYNC word not included in DIP-2 calculation
      prev_dip2   = 2'b00;

   if(dip2)
      calc_dip2   = latspi4.rx_dip2_valid ? prev_dip2 : ~(prev_dip2);
   else
      calc_dip2   = d;
end
endfunction

`ifdef SHOW_IDENTITY
   initial begin
     $display("%M $Id: latspi4_rx.v.rca 1.12 Wed Sep 25 07:38:17 2002 bkerr Experimental $");
   end
   
`endif

endmodule
