Restored UART model to address stride 4, for GFE

// Copyright (c) 2016-2020 Bluespec, Inc. All Rights Reserved

package UART_Model;

// ================================================================
// This package implements a slave IP, a UART model.
//
// This is a basic (and somewhat incomplete) model of a classic 16550
// UART, enough to do basic character reads and writes, interrupts,
// etc.  Just sends/receives the chars into Get/Put interfaces,
// leaving it to external logic to manage actual physical
// tranmit/receive.  In particular, this module does nothing about
// clock speed, baud rates, etc.
//
// Bus interface width: This slave IP can be attached to fabrics with
// 32b- or 64b-wide data channels.  The type parameter 'Wd_Data' in
// Fabric_Defs.bsv specifies this.
//
// Address stride: the 16550 UART's registers are just 1-byte wide.
// As a slave IP in a system, this IP places them at aligned addresses
// with a gap of 4 or 8 bytes.  This is controlled by the Integer
// parameter 'address_stride' in this file.

// Some of the 'truncate()'s and 'zeroExtend()'s below are no-ops but
// necessary to satisfy type-checking, to manage these width
// variations.
// ================================================================

export UART_IFC (..), mkUART;

// ================================================================
// BSV library imports

import  Vector        :: *;
import  FIFOF         :: *;
import  GetPut        :: *;
import  ClientServer  :: *;
import  ConfigReg     :: *;

// ----------------
// BSV additional libs

import Cur_Cycle  :: *;
import GetPut_Aux :: *;
import Semi_FIFOF :: *;

// ================================================================
// Project imports

import AXI4_Types  :: *;
import Fabric_Defs :: *;

// ================================================================
// UART registers and their address offsets

Bit #(3)  addr_UART_rbr  = 3'h_0;    // receiver buffer register (read only)
Bit #(3)  addr_UART_thr  = 3'h_0;    // transmitter holding register (write only)
Bit #(3)  addr_UART_ier  = 3'h_1;    // interrupt enable register
Bit #(3)  addr_UART_iir  = 3'h_2;    // interrupt id register    (read-only)
Bit #(3)  addr_UART_lcr  = 3'h_3;    // line control reg
Bit #(3)  addr_UART_mcr  = 3'h_4;    // modem control reg
Bit #(3)  addr_UART_lsr  = 3'h_5;    // line status reg     (read-only)
Bit #(3)  addr_UART_msr  = 3'h_6;    // modem status reg    (read-only)
Bit #(3)  addr_UART_scr  = 3'h_7;    // scratch pad reg

// Aliased registers, depending on control bits
Bit #(3)  addr_UART_dll  = 3'h_0;    // divisor latch low
Bit #(3)  addr_UART_dlm  = 3'h_1;    // divisor latch high
Bit #(3)  addr_UART_fcr  = 3'h_2;    // fifo control reg    (write-only)

// Bit fields of ier (Interrupt Enable Register)
Bit #(8)  uart_ier_erbfi = 8'h_01;     // Enable Received Data Available Interrupt
Bit #(8)  uart_ier_etbei = 8'h_02;     // Enable Transmitter Holding Register Empty Interrupt
Bit #(8)  uart_ier_elsi  = 8'h_04;     // Enable Receiver Line Status Interrupt
Bit #(8)  uart_ier_edssi = 8'h_08;     // Enable Modem Status Interrupt

// iir values (Interrupt Identification Register) in decreasing priority of interrupts
Bit #(8)  uart_iir_none  = 8'h_01;     // None (no interrupts pending)
Bit #(8)  uart_iir_rls   = 8'h_06;     // Receiver Line Status
Bit #(8)  uart_iir_rda   = 8'h_04;     // Received Data Available
Bit #(8)  uart_iir_cti   = 8'h_0C;     // Character Timeout Indication
Bit #(8)  uart_iir_thre  = 8'h_02;     // Transmitter Holding Register Empty
Bit #(8)  uart_iir_ms    = 8'h_00;     // Modem Status

// Bit fields of LCR
Bit #(8)  uart_lcr_dlab  = 8'h_80;     // Divisor latch access bit
Bit #(8)  uart_lcr_bc    = 8'h_40;     // Break control
Bit #(8)  uart_lcr_sp    = 8'h_20;     // Stick parity
Bit #(8)  uart_lcr_eps   = 8'h_10;     // Even parity
Bit #(8)  uart_lcr_pen   = 8'h_08;     // Parity enable
Bit #(8)  uart_lcr_stb   = 8'h_04;     // # of stop bits (0=1b,1=2b)
Bit #(8)  uart_lcr_wls   = 8'h_03;     // word len (0:5b,1:6b,2:7b,3:8b)

// Bit fields of LSR
Bit #(8)  uart_lsr_rxfe  = 8'h_80;    // Receiver FIFO error
Bit #(8)  uart_lsr_temt  = 8'h_40;    // Transmitter empty
Bit #(8)  uart_lsr_thre  = 8'h_20;    // THR empty
Bit #(8)  uart_lsr_bi    = 8'h_10;    // Break interrupt
Bit #(8)  uart_lsr_fe    = 8'h_08;    // Framing Error
Bit #(8)  uart_lsr_pe    = 8'h_04;    // Parity Error
Bit #(8)  uart_lsr_oe    = 8'h_02;    // Overrun Error
Bit #(8)  uart_lsr_dr    = 8'h_01;    // Data Ready

Bit #(8)  uart_lsr_reset_value = (uart_lsr_temt | uart_lsr_thre);

// ================================================================
// THIS MODULE's INTERFACE

interface UART_IFC;
   // Reset
   interface Server #(Bit #(0), Bit #(0))  server_reset;

   // set_addr_map should be called after this module's reset
   method Action set_addr_map (Fabric_Addr addr_base, Fabric_Addr addr_lim);

   // Main Fabric Reqs/Rsps
   interface AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave;

   // To external console
   interface Get #(Bit #(8))  get_to_console;
   interface Put #(Bit #(8))  put_from_console;

   // Interrupt pending
   (* always_ready *)
   method Bool  intr;
endinterface

// ================================================================
// Local types and constants

// Module state
typedef enum {STATE_START,
	      STATE_READY
   } Module_State
deriving (Bits, Eq, FShow);

// ================================================================
// Addressing of UART registers

// ----------------------------------------------------------------
// UART reg addresses should be at stride 4 or 8.

Integer address_stride = 4;
// Integer address_stride = 8;

// ----------------------------------------------------------------
// Split a bus address into (offset, lsbs), based on the address
// stride.

function Tuple2 #(Bit #(64), Bit #(3)) split_addr (Bit #(64) addr);
   Bit #(64) offset = ((address_stride == 4) ? (addr >> 2)           : (addr >> 3));
   Bit #(3)  lsbs   = ((address_stride == 4) ? { 1'b0, addr [1:0] }  : addr [2:0]);

   return tuple2 (offset, lsbs);
endfunction

// ----------------------------------------------------------------
// Extract data from AXI4 byte lanes, based on the AXI4 'strobe'
// (byte-enable) bits.

function Bit #(64) fn_extract_AXI4_data (Bit #(64) data, Bit #(8) strb);
   Bit #(64) result = 0;
   case (strb)
      8'b_0000_0001: result = zeroExtend (data [ 7:0]);
      8'b_0000_0010: result = zeroExtend (data [15:8]);
      8'b_0000_0100: result = zeroExtend (data [23:16]);
      8'b_0000_1000: result = zeroExtend (data [31:24]);
      8'b_0001_0000: result = zeroExtend (data [39:32]);
      8'b_0010_0000: result = zeroExtend (data [47:40]);
      8'b_0100_0000: result = zeroExtend (data [55:48]);
      8'b_1000_0000: result = zeroExtend (data [63:56]);

      8'b_0000_0011: result = zeroExtend (data [15:0]);
      8'b_0000_1100: result = zeroExtend (data [31:16]);
      8'b_0011_0000: result = zeroExtend (data [47:32]);
      8'b_1100_0000: result = zeroExtend (data [63:48]);

      8'b_0000_1111: result = zeroExtend (data [31:0]);
      8'b_1111_0000: result = zeroExtend (data [63:32]);

      8'b_1111_1111: result = zeroExtend (data [63:0]);
   endcase
   return result;
endfunction

// ================================================================
// THIS MODULE's IMPLEMENTATION

(* synthesize *)
module mkUART (UART_IFC);

   // 0: quiet; 1: reset and char input/output; 2: + detail
   Reg #(Bit #(8)) cfg_verbosity <- mkConfigReg (0);

   Reg #(Module_State) rg_state     <- mkReg (STATE_START);

   // These regs represent where this UART is placed in the address space.
   Reg #(Fabric_Addr)  rg_addr_base <- mkRegU;
   Reg #(Fabric_Addr)  rg_addr_lim  <- mkRegU;

   FIFOF #(Bit #(0)) f_reset_reqs <- mkFIFOF;
   FIFOF #(Bit #(0)) f_reset_rsps <- mkFIFOF;

   // ----------------
   // Connector to AXI4 fabric

   AXI4_Slave_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave_xactor <- mkAXI4_Slave_Xactor;

   // ----------------
   // character queues to and from external circuitry for the console

   FIFOF #(Bit #(8)) f_from_console <- mkFIFOF;
   FIFOF #(Bit #(8)) f_to_console   <- mkFIFOF;

   // ----------------
   // These are the 16550 UART registers
   // See fn_addr_offset() above for meaning of 'addr offset'

   Reg #(Bit #(8))  rg_rbr <- mkRegU;       // addr offset 0
   Reg #(Bit #(8))  rg_thr <- mkRegU;       // addr offset 0
   Reg #(Bit #(8))  rg_dll <- mkReg (0);    // addr offset 0

   Reg #(Bit #(8))  rg_ier <- mkReg (0);    // addr offset 1
   Reg #(Bit #(8))  rg_dlm <- mkReg (0);    // addr offset 1

   // IIR is a virtual read-only register computed from other regs
   Reg #(Bit #(8))  rg_fcr <- mkReg (0);    // addr offset 2

   Reg #(Bit #(8))  rg_lcr <- mkReg (0);    // addr offset 3
   Reg #(Bit #(8))  rg_mcr <- mkReg (0);    // addr offset 4
   Reg #(Bit #(8))  rg_lsr <- mkReg (uart_lsr_reset_value);    // addr offset 5
   Reg #(Bit #(8))  rg_msr <- mkReg (0);    // addr offset 6
   Reg #(Bit #(8))  rg_scr <- mkReg (0);    // addr offset 7

   // ----------------
   // Virtual read-only register IIR

   function Bit #(8) fn_iir ();
      Bit #(8) iir = uart_iir_none;

      if (   ((rg_ier & uart_ier_erbfi) != 0)    // Rx interrupt enabled
	  && ((rg_lsr & uart_lsr_dr)    != 0))   // data ready
	 iir = uart_iir_rda;

      else if ((rg_ier & uart_ier_etbei) != 0)   // Tx Holding Reg Empty intr enabled
	 iir = uart_iir_thre;

      return iir;
   endfunction

   // ----------------
   // Test if an interrupt is pending

   function Bool fn_intr ();
      let iir = fn_iir ();
      Bool eip = ((iir & uart_iir_none) == 0);
      return eip;
   endfunction

   // ================================================================
   // BEHAVIOR

   // ----------------------------------------------------------------
   // Soft reset (on token in f_reset_reqs)

   rule rl_reset;
      f_reset_reqs.deq;

      rg_dll <= 0;
      rg_ier <= 0;
      rg_dlm <= 0;
      rg_fcr <= 0;
      rg_lcr <= 0;
      rg_mcr <= 0;
      rg_lsr <= uart_lsr_reset_value;
      rg_msr <= 0;
      rg_scr <= 0;

      slave_xactor.reset;
      rg_state <= STATE_READY;

      f_reset_rsps.enq (?);

      if (cfg_verbosity != 0)
	 $display ("%0d: UART.rl_reset", cur_cycle);
   endrule

   // ----------------------------------------------------------------
   // Handle fabric read requests

   rule rl_process_rd_req (rg_state == STATE_READY);
      let rda <- pop_o (slave_xactor.o_rd_addr);

      let byte_addr = rda.araddr - rg_addr_base;
      match { .offset, .lsbs } = split_addr (zeroExtend (byte_addr));

      Bit #(8)  rdata_byte = 0;
      AXI4_Resp rresp      = axi4_resp_okay;

      if ((rda.araddr < rg_addr_base) || (rda.araddr >= rg_addr_lim)) begin
	 $display ("%0d: %m.rl_process_rd_req: ERROR: UART addr out of bounds", cur_cycle);
	 $display ("    UART base addr 0x%0h  limit addr 0x%0h", rg_addr_base, rg_addr_lim);
	 $display ("    AXI4 request: ", fshow (rda));
	 rresp = axi4_resp_decerr;
      end
      else if (lsbs != 0) begin
	 $display ("%0d: %m.rl_process_rd_req: ERROR: UART misaligned addr", cur_cycle);
	 $display ("            ", fshow (rda));
	 rresp = axi4_resp_slverr;
      end
      else if (offset [63:3] != 0) begin
	 $display ("%0d: %m.rl_process_rd_req: ERROR: UART unsupported addr", cur_cycle);
	 $display ("    Register offset 0x%0h", offset);
	 $display ("    ", fshow (rda));
	 rresp = axi4_resp_decerr;
      end

      // offset 0: RBR
      else if ((offset [2:0] == addr_UART_rbr) && ((rg_lcr & uart_lcr_dlab) == 0)) begin
	 // Read an input char
	 rg_lsr <= (rg_lsr & (~ uart_lsr_dr));    // Reset data-ready
	 rdata_byte = rg_rbr;
      end
      // offset 0: DLL
      else if ((offset [2:0] == addr_UART_dll) && ((rg_lcr & uart_lcr_dlab) != 0))
	 rdata_byte = rg_dll;

      // offset 1: IER
      else if ((offset [2:0] == addr_UART_ier) && ((rg_lcr & uart_lcr_dlab) == 0))
	 rdata_byte = rg_ier;
      // offset 1: DLM
      else if ((offset [2:0] == addr_UART_dlm) && ((rg_lcr & uart_lcr_dlab) != 0))
	 rdata_byte = rg_dlm;

      // offset 2: IIR (read-only)
      else if (offset [2:0] == addr_UART_iir) rdata_byte  = fn_iir();

      // offset 3: LCR
      else if (offset [2:0] == addr_UART_lcr) rdata_byte  = { 0, rg_lcr };
      // offset 4: MCR
      else if (offset [2:0] == addr_UART_mcr) rdata_byte  = { 0, rg_mcr };
      // offset 5: LSR
      else if (offset [2:0] == addr_UART_lsr) rdata_byte  = { 0, rg_lsr };
      // offset 6: MSR
      else if (offset [2:0] == addr_UART_msr) rdata_byte  = { 0, rg_msr };
      // offset 7: SCR
      else if (offset [2:0] == addr_UART_scr) rdata_byte  = { 0, rg_scr };

      else begin
	 $display ("%0d: %m.rl_process_rd_req: ERROR: UART unsupported addr", cur_cycle);
	 $display ("    ", fshow (rda));
	 rresp = axi4_resp_decerr;
      end

      // Align data byte for AXI4 data bus based on fabric-width
      Fabric_Data rdata = zeroExtend (rdata_byte);
      if ((valueOf (Wd_Data) == 64) && (byte_addr [2:0] == 3'b100))
	 rdata = rdata << 32;

      // Send read-response to bus
      let rdr = AXI4_Rd_Data {rid:   rda.arid,
			      rdata: rdata,
			      rresp: rresp,
			      rlast: True,
			      ruser: rda.aruser};
      slave_xactor.i_rd_data.enq (rdr);

      if (cfg_verbosity > 1) begin
	 $display ("%0d: %m.rl_process_rd_req", cur_cycle);
	 $display ("            ", fshow (rda));
	 $display ("            ", fshow (rdr));
      end
   endrule

   // ----------------------------------------------------------------
   // Handle fabric write requests

   rule rl_process_wr_req (rg_state == STATE_READY);
      let wra <- pop_o (slave_xactor.o_wr_addr);
      let wrd <- pop_o (slave_xactor.o_wr_data);

      Bit #(64) wdata     = zeroExtend (wrd.wdata);
      Bit #(8)  wstrb     = zeroExtend (wrd.wstrb);
      Bit #(8)  data_byte = truncate (fn_extract_AXI4_data (wdata, wstrb));

      let byte_addr = wra.awaddr - rg_addr_base;
      match { .offset, .lsbs } = split_addr (zeroExtend (byte_addr));

      AXI4_Resp bresp = axi4_resp_okay;

      if ((wra.awaddr < rg_addr_base) || (wra.awaddr >= rg_addr_lim)) begin
	 $display ("%0d: %m.rl_process_rd_req: ERROR: UART addr out of bounds", cur_cycle);
	 $display ("    UART base addr 0x%0h  limit addr 0x%0h", rg_addr_base, rg_addr_lim);
	 $display ("    AXI4 request: ", fshow (wra));
	 bresp = axi4_resp_decerr;
      end
      else if (lsbs != 0) begin
	 $display ("%0d: %m.rl_process_wr_req: ERROR: UART misaligned addr", cur_cycle);
	 $display ("            ", fshow (wra));
	 bresp = axi4_resp_slverr;
      end
      else if (offset [63:3] != 0) begin
	 $display ("%0d: %m.rl_process_wr_req: ERROR: UART unsupported addr", cur_cycle);
	 $display ("    Register offset 0x%0h", offset);
	 $display ("    ", fshow (wra));
	 $display ("    ", fshow (wrd));
	 bresp = axi4_resp_decerr;
      end

      // offset 0: THR
      else if ((offset [2:0] == addr_UART_thr) && ((rg_lcr & uart_lcr_dlab) == 0)) begin
	 // Write a char to the serial line
	 rg_thr <= data_byte;
	 f_to_console.enq (data_byte);
	 if (cfg_verbosity != 0)
	    $display ("%0d: %m.rl_process_wr_req: ASCII %0d (0x%0h)", cur_cycle, data_byte, data_byte);
      end
      // offset 0: DLL
      else if ((offset [2:0] == addr_UART_dll) && ((rg_lcr & uart_lcr_dlab) != 0))
	 rg_dll <= data_byte;

      // offset 1: IER
      else if ((offset [2:0] == addr_UART_ier) && ((rg_lcr & uart_lcr_dlab) == 0))
	 rg_ier <= data_byte;
      // offset 1: DLM
      else if ((offset [2:0] == addr_UART_dlm) && ((rg_lcr & uart_lcr_dlab) != 0))
	 rg_dlm <= data_byte;

      // offset 2: FCR (write-only)
      else if (offset [2:0] == addr_UART_fcr) rg_fcr <= data_byte;

      // offset 3: LCR
      else if (offset [2:0] == addr_UART_lcr) rg_lcr <= data_byte;
      // offset 4: MCR
      else if (offset [2:0] == addr_UART_mcr) rg_mcr <= data_byte;
      // offset 5: LSR
      else if (offset [2:0] == addr_UART_lsr) noAction;    // LSR is read-only
      // offset 6: MSR
      else if (offset [2:0] == addr_UART_msr) noAction;    // MSR is read-only
      // offset 7: SCR
      else if (offset [2:0] == addr_UART_scr) rg_scr <= data_byte;

      else begin
	 $display ("%0d: %m.rl_process_wr_req: ERROR: UART unsupported addr", cur_cycle);
	 $display ("    ", fshow (wra));
	 $display ("    ", fshow (wrd));
	 bresp = axi4_resp_decerr;
      end

      // Send write-response to bus
      let wrr = AXI4_Wr_Resp {bid:   wra.awid,
			      bresp: bresp,
			      buser: wra.awuser};
      slave_xactor.i_wr_resp.enq (wrr);

      if (cfg_verbosity > 1) begin
	 $display ("%0d: %m.rl_process_wr_req", cur_cycle);
	 $display ("            ", fshow (wra));
	 $display ("            ", fshow (wrd));
	 $display ("            ", fshow (wrr));
      end
   endrule

   // ----------------------------------------------------------------
   // Receive a char from the serial line when RBR is empty (i.e., LSR.DR is 0),
   // and deposit it into RBR
   // and set it full (LSR.DR = 1)

   (* descending_urgency = "rl_receive, rl_process_rd_req" *)

   rule rl_receive ((rg_lsr & uart_lsr_dr) == 0);
      let ch <- pop (f_from_console);
      rg_rbr <= ch;

      let new_lsr = (rg_lsr | uart_lsr_dr);    // Set data-ready
      rg_lsr <= new_lsr;

      if (cfg_verbosity > 1)
	 $display ("UART_Model.rl_receive: received char 0x%0h; new_lsr = 0x%0h",
		   ch, new_lsr);
   endrule

   // ================================================================
   // INTERFACE

   // Reset
   interface server_reset   = toGPServer (f_reset_reqs, f_reset_rsps);

   // set_addr_map should be called after this module's reset
   method Action  set_addr_map (Fabric_Addr addr_base, Fabric_Addr addr_lim);
      if (addr_base [2:0] != 0)
	 $display ("%0d: WARNING: UART.set_addr_map: addr_base 0x%0h is not 8-Byte-aligned",
		   cur_cycle, addr_base);

      if (addr_lim [2:0] != 0)
	 $display ("%0d: WARNING: UART.set_addr_map: addr_lim 0x%0h is not 8-Byte-aligned",
		   cur_cycle, addr_lim);

      rg_addr_base <= addr_base;
      rg_addr_lim  <= addr_lim;
   endmethod

   // Main Fabric Reqs/Rsps
   interface  slave = slave_xactor.axi_side;

   // To external console
   interface  put_from_console = toPut (f_from_console);
   interface  get_to_console   = toGet (f_to_console);

   // Interrupt pending
   method Bool  intr;
      return fn_intr ();
   endmethod
endmodule

// ================================================================

endpackage

// Copyright (c) 2016-2020 Bluespec, Inc. All Rights Reserved

package UART_Model;

// ================================================================
// This package implements a slave IP, a UART model.
//
// This is a basic (and somewhat incomplete) model of a classic 16550
// UART, enough to do basic character reads and writes, interrupts,
// etc.  Just sends/receives the chars into Get/Put interfaces,
// leaving it to external logic to manage actual physical
// tranmit/receive.  In particular, this module does nothing about
// clock speed, baud rates, etc.
//
// Bus interface width: This slave IP can be attached to fabrics with
// 32b- or 64b-wide data channels.  The type parameter 'Wd_Data' in
// Fabric_Defs.bsv specifies this.
//
// Address stride: the 16550 UART's registers are just 1-byte wide.
// As a slave IP in a system, this IP places them at aligned addresses
// with a gap of 4 or 8 bytes.  This is controlled by the Integer
// parameter 'address_stride' in this file.

// Some of the 'truncate()'s and 'zeroExtend()'s below are no-ops but
// necessary to satisfy type-checking, to manage these width
// variations.
// ================================================================

export UART_IFC (..), mkUART;

// ================================================================
// BSV library imports

import  Vector        :: *;
import  FIFOF         :: *;
import  GetPut        :: *;
import  ClientServer  :: *;
import  ConfigReg     :: *;

// ----------------
// BSV additional libs

import Cur_Cycle  :: *;
import GetPut_Aux :: *;
import Semi_FIFOF :: *;

// ================================================================
// Project imports

import AXI4_Types  :: *;
import Fabric_Defs :: *;

// ================================================================
// UART registers and their address offsets

Bit #(3)  addr_UART_rbr  = 3'h_0;    // receiver buffer register (read only)
Bit #(3)  addr_UART_thr  = 3'h_0;    // transmitter holding register (write only)
Bit #(3)  addr_UART_ier  = 3'h_1;    // interrupt enable register
Bit #(3)  addr_UART_iir  = 3'h_2;    // interrupt id register    (read-only)
Bit #(3)  addr_UART_lcr  = 3'h_3;    // line control reg
Bit #(3)  addr_UART_mcr  = 3'h_4;    // modem control reg
Bit #(3)  addr_UART_lsr  = 3'h_5;    // line status reg     (read-only)
Bit #(3)  addr_UART_msr  = 3'h_6;    // modem status reg    (read-only)
Bit #(3)  addr_UART_scr  = 3'h_7;    // scratch pad reg

// Aliased registers, depending on control bits
Bit #(3)  addr_UART_dll  = 3'h_0;    // divisor latch low
Bit #(3)  addr_UART_dlm  = 3'h_1;    // divisor latch high
Bit #(3)  addr_UART_fcr  = 3'h_2;    // fifo control reg    (write-only)

// Bit fields of ier (Interrupt Enable Register)
Bit #(8)  uart_ier_erbfi = 8'h_01;     // Enable Received Data Available Interrupt
Bit #(8)  uart_ier_etbei = 8'h_02;     // Enable Transmitter Holding Register Empty Interrupt
Bit #(8)  uart_ier_elsi  = 8'h_04;     // Enable Receiver Line Status Interrupt
Bit #(8)  uart_ier_edssi = 8'h_08;     // Enable Modem Status Interrupt

// iir values (Interrupt Identification Register) in decreasing priority of interrupts
Bit #(8)  uart_iir_none  = 8'h_01;     // None (no interrupts pending)
Bit #(8)  uart_iir_rls   = 8'h_06;     // Receiver Line Status
Bit #(8)  uart_iir_rda   = 8'h_04;     // Received Data Available
Bit #(8)  uart_iir_cti   = 8'h_0C;     // Character Timeout Indication
Bit #(8)  uart_iir_thre  = 8'h_02;     // Transmitter Holding Register Empty
Bit #(8)  uart_iir_ms    = 8'h_00;     // Modem Status

// Bit fields of LCR
Bit #(8)  uart_lcr_dlab  = 8'h_80;     // Divisor latch access bit
Bit #(8)  uart_lcr_bc    = 8'h_40;     // Break control
Bit #(8)  uart_lcr_sp    = 8'h_20;     // Stick parity
Bit #(8)  uart_lcr_eps   = 8'h_10;     // Even parity
Bit #(8)  uart_lcr_pen   = 8'h_08;     // Parity enable
Bit #(8)  uart_lcr_stb   = 8'h_04;     // # of stop bits (0=1b,1=2b)
Bit #(8)  uart_lcr_wls   = 8'h_03;     // word len (0:5b,1:6b,2:7b,3:8b)

// Bit fields of LSR
Bit #(8)  uart_lsr_rxfe  = 8'h_80;    // Receiver FIFO error
Bit #(8)  uart_lsr_temt  = 8'h_40;    // Transmitter empty
Bit #(8)  uart_lsr_thre  = 8'h_20;    // THR empty
Bit #(8)  uart_lsr_bi    = 8'h_10;    // Break interrupt
Bit #(8)  uart_lsr_fe    = 8'h_08;    // Framing Error
Bit #(8)  uart_lsr_pe    = 8'h_04;    // Parity Error
Bit #(8)  uart_lsr_oe    = 8'h_02;    // Overrun Error
Bit #(8)  uart_lsr_dr    = 8'h_01;    // Data Ready

Bit #(8)  uart_lsr_reset_value = (uart_lsr_temt | uart_lsr_thre);

// ================================================================
// THIS MODULE's INTERFACE

interface UART_IFC;
   // Reset
   interface Server #(Bit #(0), Bit #(0))  server_reset;

   // set_addr_map should be called after this module's reset
   method Action set_addr_map (Fabric_Addr addr_base, Fabric_Addr addr_lim);

   // Main Fabric Reqs/Rsps
   interface AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave;

   // To external console
   interface Get #(Bit #(8))  get_to_console;
   interface Put #(Bit #(8))  put_from_console;

   // Interrupt pending
   (* always_ready *)
   method Bool  intr;
endinterface

// ================================================================
// Local types and constants

// Module state
typedef enum {STATE_START,
	      STATE_READY
   } Module_State
deriving (Bits, Eq, FShow);

// ================================================================
// Addressing of UART registers

// ----------------------------------------------------------------
// UART reg addresses should be at stride 4 or 8.

// Integer address_stride = 4;
Integer address_stride = 8;

// ----------------------------------------------------------------
// Split a bus address into (offset, lsbs), based on the address
// stride.

function Tuple2 #(Bit #(64), Bit #(3)) split_addr (Bit #(64) addr);
   Bit #(64) offset = ((address_stride == 4) ? (addr >> 2)           : (addr >> 3));
   Bit #(3)  lsbs   = ((address_stride == 4) ? { 1'b0, addr [1:0] }  : addr [2:0]);

   return tuple2 (offset, lsbs);
endfunction

// ----------------------------------------------------------------
// Extract data from AXI4 byte lanes, based on the AXI4 'strobe'
// (byte-enable) bits.

function Bit #(64) fn_extract_AXI4_data (Bit #(64) data, Bit #(8) strb);
   Bit #(64) result = 0;
   case (strb)
      8'b_0000_0001: result = zeroExtend (data [ 7:0]);
      8'b_0000_0010: result = zeroExtend (data [15:8]);
      8'b_0000_0100: result = zeroExtend (data [23:16]);
      8'b_0000_1000: result = zeroExtend (data [31:24]);
      8'b_0001_0000: result = zeroExtend (data [39:32]);
      8'b_0010_0000: result = zeroExtend (data [47:40]);
      8'b_0100_0000: result = zeroExtend (data [55:48]);
      8'b_1000_0000: result = zeroExtend (data [63:56]);

      8'b_0000_0011: result = zeroExtend (data [15:0]);
      8'b_0000_1100: result = zeroExtend (data [31:16]);
      8'b_0011_0000: result = zeroExtend (data [47:32]);
      8'b_1100_0000: result = zeroExtend (data [63:48]);

      8'b_0000_1111: result = zeroExtend (data [31:0]);
      8'b_1111_0000: result = zeroExtend (data [63:32]);

      8'b_1111_1111: result = zeroExtend (data [63:0]);
   endcase
   return result;
endfunction

// ================================================================
// THIS MODULE's IMPLEMENTATION

(* synthesize *)
module mkUART (UART_IFC);

   // 0: quiet; 1: reset and char input/output; 2: + detail
   Reg #(Bit #(8)) cfg_verbosity <- mkConfigReg (0);

   Reg #(Module_State) rg_state     <- mkReg (STATE_START);

   // These regs represent where this UART is placed in the address space.
   Reg #(Fabric_Addr)  rg_addr_base <- mkRegU;
   Reg #(Fabric_Addr)  rg_addr_lim  <- mkRegU;

   FIFOF #(Bit #(0)) f_reset_reqs <- mkFIFOF;
   FIFOF #(Bit #(0)) f_reset_rsps <- mkFIFOF;

   // ----------------
   // Connector to AXI4 fabric

   AXI4_Slave_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave_xactor <- mkAXI4_Slave_Xactor;

   // ----------------
   // character queues to and from external circuitry for the console

   FIFOF #(Bit #(8)) f_from_console <- mkFIFOF;
   FIFOF #(Bit #(8)) f_to_console   <- mkFIFOF;

   // ----------------
   // These are the 16550 UART registers
   // See fn_addr_offset() above for meaning of 'addr offset'

   Reg #(Bit #(8))  rg_rbr <- mkRegU;       // addr offset 0
   Reg #(Bit #(8))  rg_thr <- mkRegU;       // addr offset 0
   Reg #(Bit #(8))  rg_dll <- mkReg (0);    // addr offset 0

   Reg #(Bit #(8))  rg_ier <- mkReg (0);    // addr offset 1
   Reg #(Bit #(8))  rg_dlm <- mkReg (0);    // addr offset 1

   // IIR is a virtual read-only register computed from other regs
   Reg #(Bit #(8))  rg_fcr <- mkReg (0);    // addr offset 2

   Reg #(Bit #(8))  rg_lcr <- mkReg (0);    // addr offset 3
   Reg #(Bit #(8))  rg_mcr <- mkReg (0);    // addr offset 4
   Reg #(Bit #(8))  rg_lsr <- mkReg (uart_lsr_reset_value);    // addr offset 5
   Reg #(Bit #(8))  rg_msr <- mkReg (0);    // addr offset 6
   Reg #(Bit #(8))  rg_scr <- mkReg (0);    // addr offset 7

   // ----------------
   // Virtual read-only register IIR

   function Bit #(8) fn_iir ();
      Bit #(8) iir = uart_iir_none;

      if (   ((rg_ier & uart_ier_erbfi) != 0)    // Rx interrupt enabled
	  && ((rg_lsr & uart_lsr_dr)    != 0))   // data ready
	 iir = uart_iir_rda;

      else if ((rg_ier & uart_ier_etbei) != 0)   // Tx Holding Reg Empty intr enabled
	 iir = uart_iir_thre;

      return iir;
   endfunction

   // ----------------
   // Test if an interrupt is pending

   function Bool fn_intr ();
      let iir = fn_iir ();
      Bool eip = ((iir & uart_iir_none) == 0);
      return eip;
   endfunction

   // ================================================================
   // BEHAVIOR

   // ----------------------------------------------------------------
   // Soft reset (on token in f_reset_reqs)

   rule rl_reset;
      f_reset_reqs.deq;

      rg_dll <= 0;
      rg_ier <= 0;
      rg_dlm <= 0;
      rg_fcr <= 0;
      rg_lcr <= 0;
      rg_mcr <= 0;
      rg_lsr <= uart_lsr_reset_value;
      rg_msr <= 0;
      rg_scr <= 0;

      slave_xactor.reset;
      rg_state <= STATE_READY;

      f_reset_rsps.enq (?);

      if (cfg_verbosity != 0)
	 $display ("%0d: UART.rl_reset", cur_cycle);
   endrule

   // ----------------------------------------------------------------
   // Handle fabric read requests

   rule rl_process_rd_req (rg_state == STATE_READY);
      let rda <- pop_o (slave_xactor.o_rd_addr);

      let byte_addr = rda.araddr - rg_addr_base;
      match { .offset, .lsbs } = split_addr (zeroExtend (byte_addr));

      Bit #(8)  rdata_byte = 0;
      AXI4_Resp rresp      = axi4_resp_okay;

      if ((rda.araddr < rg_addr_base) || (rda.araddr >= rg_addr_lim)) begin
	 $display ("%0d: %m.rl_process_rd_req: ERROR: UART addr out of bounds", cur_cycle);
	 $display ("    UART base addr 0x%0h  limit addr 0x%0h", rg_addr_base, rg_addr_lim);
	 $display ("    AXI4 request: ", fshow (rda));
	 rresp = axi4_resp_decerr;
      end
      else if (lsbs != 0) begin
	 $display ("%0d: %m.rl_process_rd_req: ERROR: UART misaligned addr", cur_cycle);
	 $display ("            ", fshow (rda));
	 rresp = axi4_resp_slverr;
      end
      else if (offset [63:3] != 0) begin
	 $display ("%0d: %m.rl_process_rd_req: ERROR: UART unsupported addr", cur_cycle);
	 $display ("    Register offset 0x%0h", offset);
	 $display ("    ", fshow (rda));
	 rresp = axi4_resp_decerr;
      end

      // offset 0: RBR
      else if ((offset [2:0] == addr_UART_rbr) && ((rg_lcr & uart_lcr_dlab) == 0)) begin
	 // Read an input char
	 rg_lsr <= (rg_lsr & (~ uart_lsr_dr));    // Reset data-ready
	 rdata_byte = rg_rbr;
      end
      // offset 0: DLL
      else if ((offset [2:0] == addr_UART_dll) && ((rg_lcr & uart_lcr_dlab) != 0))
	 rdata_byte = rg_dll;

      // offset 1: IER
      else if ((offset [2:0] == addr_UART_ier) && ((rg_lcr & uart_lcr_dlab) == 0))
	 rdata_byte = rg_ier;
      // offset 1: DLM
      else if ((offset [2:0] == addr_UART_dlm) && ((rg_lcr & uart_lcr_dlab) != 0))
	 rdata_byte = rg_dlm;

      // offset 2: IIR (read-only)
      else if (offset [2:0] == addr_UART_iir) rdata_byte  = fn_iir();

      // offset 3: LCR
      else if (offset [2:0] == addr_UART_lcr) rdata_byte  = { 0, rg_lcr };
      // offset 4: MCR
      else if (offset [2:0] == addr_UART_mcr) rdata_byte  = { 0, rg_mcr };
      // offset 5: LSR
      else if (offset [2:0] == addr_UART_lsr) rdata_byte  = { 0, rg_lsr };
      // offset 6: MSR
      else if (offset [2:0] == addr_UART_msr) rdata_byte  = { 0, rg_msr };
      // offset 7: SCR
      else if (offset [2:0] == addr_UART_scr) rdata_byte  = { 0, rg_scr };

      else begin
	 $display ("%0d: %m.rl_process_rd_req: ERROR: UART unsupported addr", cur_cycle);
	 $display ("    ", fshow (rda));
	 rresp = axi4_resp_decerr;
      end

      // Align data byte for AXI4 data bus based on fabric-width
      Fabric_Data rdata = zeroExtend (rdata_byte);
      if ((valueOf (Wd_Data) == 64) && (byte_addr [2:0] == 3'b100))
	 rdata = rdata << 32;

      // Send read-response to bus
      let rdr = AXI4_Rd_Data {rid:   rda.arid,
			      rdata: rdata,
			      rresp: rresp,
			      rlast: True,
			      ruser: rda.aruser};
      slave_xactor.i_rd_data.enq (rdr);

      if (cfg_verbosity > 1) begin
	 $display ("%0d: %m.rl_process_rd_req", cur_cycle);
	 $display ("            ", fshow (rda));
	 $display ("            ", fshow (rdr));
      end
   endrule

   // ----------------------------------------------------------------
   // Handle fabric write requests

   rule rl_process_wr_req (rg_state == STATE_READY);
      let wra <- pop_o (slave_xactor.o_wr_addr);
      let wrd <- pop_o (slave_xactor.o_wr_data);

      Bit #(64) wdata     = zeroExtend (wrd.wdata);
      Bit #(8)  wstrb     = zeroExtend (wrd.wstrb);
      Bit #(8)  data_byte = truncate (fn_extract_AXI4_data (wdata, wstrb));

      let byte_addr = wra.awaddr - rg_addr_base;
      match { .offset, .lsbs } = split_addr (zeroExtend (byte_addr));

      AXI4_Resp bresp = axi4_resp_okay;

      if ((wra.awaddr < rg_addr_base) || (wra.awaddr >= rg_addr_lim)) begin
	 $display ("%0d: %m.rl_process_rd_req: ERROR: UART addr out of bounds", cur_cycle);
	 $display ("    UART base addr 0x%0h  limit addr 0x%0h", rg_addr_base, rg_addr_lim);
	 $display ("    AXI4 request: ", fshow (wra));
	 bresp = axi4_resp_decerr;
      end
      else if (lsbs != 0) begin
	 $display ("%0d: %m.rl_process_wr_req: ERROR: UART misaligned addr", cur_cycle);
	 $display ("            ", fshow (wra));
	 bresp = axi4_resp_slverr;
      end
      else if (offset [63:3] != 0) begin
	 $display ("%0d: %m.rl_process_wr_req: ERROR: UART unsupported addr", cur_cycle);
	 $display ("    Register offset 0x%0h", offset);
	 $display ("    ", fshow (wra));
	 $display ("    ", fshow (wrd));
	 bresp = axi4_resp_decerr;
      end

      // offset 0: THR
      else if ((offset [2:0] == addr_UART_thr) && ((rg_lcr & uart_lcr_dlab) == 0)) begin
	 // Write a char to the serial line
	 rg_thr <= data_byte;
	 f_to_console.enq (data_byte);
	 if (cfg_verbosity != 0)
	    $display ("%0d: %m.rl_process_wr_req: ASCII %0d (0x%0h)", cur_cycle, data_byte, data_byte);
      end
      // offset 0: DLL
      else if ((offset [2:0] == addr_UART_dll) && ((rg_lcr & uart_lcr_dlab) != 0))
	 rg_dll <= data_byte;

      // offset 1: IER
      else if ((offset [2:0] == addr_UART_ier) && ((rg_lcr & uart_lcr_dlab) == 0))
	 rg_ier <= data_byte;
      // offset 1: DLM
      else if ((offset [2:0] == addr_UART_dlm) && ((rg_lcr & uart_lcr_dlab) != 0))
	 rg_dlm <= data_byte;

      // offset 2: FCR (write-only)
      else if (offset [2:0] == addr_UART_fcr) rg_fcr <= data_byte;

      // offset 3: LCR
      else if (offset [2:0] == addr_UART_lcr) rg_lcr <= data_byte;
      // offset 4: MCR
      else if (offset [2:0] == addr_UART_mcr) rg_mcr <= data_byte;
      // offset 5: LSR
      else if (offset [2:0] == addr_UART_lsr) noAction;    // LSR is read-only
      // offset 6: MSR
      else if (offset [2:0] == addr_UART_msr) noAction;    // MSR is read-only
      // offset 7: SCR
      else if (offset [2:0] == addr_UART_scr) rg_scr <= data_byte;

      else begin
	 $display ("%0d: %m.rl_process_wr_req: ERROR: UART unsupported addr", cur_cycle);
	 $display ("    ", fshow (wra));
	 $display ("    ", fshow (wrd));
	 bresp = axi4_resp_decerr;
      end

      // Send write-response to bus
      let wrr = AXI4_Wr_Resp {bid:   wra.awid,
			      bresp: bresp,
			      buser: wra.awuser};
      slave_xactor.i_wr_resp.enq (wrr);

      if (cfg_verbosity > 1) begin
	 $display ("%0d: %m.rl_process_wr_req", cur_cycle);
	 $display ("            ", fshow (wra));
	 $display ("            ", fshow (wrd));
	 $display ("            ", fshow (wrr));
      end
   endrule

   // ----------------------------------------------------------------
   // Receive a char from the serial line when RBR is empty (i.e., LSR.DR is 0),
   // and deposit it into RBR
   // and set it full (LSR.DR = 1)

   (* descending_urgency = "rl_receive, rl_process_rd_req" *)

   rule rl_receive ((rg_lsr & uart_lsr_dr) == 0);
      let ch <- pop (f_from_console);
      rg_rbr <= ch;

      let new_lsr = (rg_lsr | uart_lsr_dr);    // Set data-ready
      rg_lsr <= new_lsr;

      if (cfg_verbosity > 1)
	 $display ("UART_Model.rl_receive: received char 0x%0h; new_lsr = 0x%0h",
		   ch, new_lsr);
   endrule

   // ================================================================
   // INTERFACE

   // Reset
   interface server_reset   = toGPServer (f_reset_reqs, f_reset_rsps);

   // set_addr_map should be called after this module's reset
   method Action  set_addr_map (Fabric_Addr addr_base, Fabric_Addr addr_lim);
      if (addr_base [2:0] != 0)
	 $display ("%0d: WARNING: UART.set_addr_map: addr_base 0x%0h is not 8-Byte-aligned",
		   cur_cycle, addr_base);

      if (addr_lim [2:0] != 0)
	 $display ("%0d: WARNING: UART.set_addr_map: addr_lim 0x%0h is not 8-Byte-aligned",
		   cur_cycle, addr_lim);

      rg_addr_base <= addr_base;
      rg_addr_lim  <= addr_lim;
   endmethod

   // Main Fabric Reqs/Rsps
   interface  slave = slave_xactor.axi_side;

   // To external console
   interface  put_from_console = toPut (f_from_console);
   interface  get_to_console   = toGet (f_to_console);

   // Interrupt pending
   method Bool  intr;
      return fn_intr ();
   endmethod
endmodule

// ================================================================

endpackage


combined rules to simplify

combined rules to simplify

// Copyright (c) 2013 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import BuildVector::*;
import Cntrs::*;
import FIFOF::*;
import FIFO::*;
import GetPut::*;
import Assert::*;
import ClientServer::*;
import BRAM::*;
import BRAMFIFO::*;
import ConfigCounter::*;
import Connectable::*;
import ConnectalMemory::*;
import ConnectalBramFifo::*;
import MemTypes::*;
import Pipe::*;
import MemUtils::*;
import ConnectalConfig::*;

`include "ConnectalProjectConfig.bsv"

module mkMemReadEngine(MemReadEngine#(busWidth, userWidth, cmdQDepth, numServers))
   provisos( Mul#(TDiv#(busWidth, 8), 8, busWidth)
	    ,Add#(1, a__, numServers)
	    ,Add#(busWidth, 0, userWidth)
	    ,FunnelPipesPipelined#(1, numServers, MemTypes::MemData#(userWidth), 2)
	    ,Pipe::FunnelPipesPipelined#(1, numServers, MemTypes::MemRequest, 2)
	    ,Add#(b__, TLog#(numServers), 6)
      	    );
   let rv <- mkMemReadEngineBuff(valueOf(cmdQDepth) * valueOf(TExp#(BurstLenSize)));
   return rv;
endmodule

typedef struct {
    Bit#(BurstLenSize) len;
    Bool               last;
} NextReq deriving (Bits, Eq);

function NextReq getNext(Bit#(32) len, Bit#(BurstLenSize) burst);
   NextReq v;
   v.last = (len <= extend(burst));
   v.len = v.last ? truncate(len) : burst;
   return v;
endfunction

typedef struct {
   Bit#(indexSize)    idx;
   SGLId              sglId;
   Bit#(32)           base;
   Bit#(MemTagSize)   tag;
   Bool               last;
   Bit#(BurstLenSize) burstLen;
} ServerRequest#(numeric type indexSize) deriving (Bits, Eq);

interface MemReadChannel#(numeric type busWidth, numeric type userWidth, numeric type cmdQDepth);
   interface PipeOut#(MemRequest)        readReq;
   interface PipeIn#(MemData#(busWidth)) readData;
   interface MemReadEngineServer#(userWidth) readServer;
endinterface

module mkMemReadChannel#(Integer bufferSizeBytes, Integer channelNumber) (MemReadChannel#(busWidth, userWidth, cmdQDepth))
   provisos (Div#(busWidth,8,busWidthBytes),
	     Mul#(busWidthBytes,8,busWidth),
	     Log#(busWidthBytes,beatShift),
	     Log#(cmdQDepth,logCmdQDepth),
	     Log#(cmdQDepth, cmdBuffAddrSz),
	     Log#(1, serverIdxSz),
	     Add#(busWidth, 0, userWidth),
	     Add#(1,logCmdQDepth, outCntSz),
	     Add#(1, c__, 1),
	     Add#(b__, TLog#(1), cmdBuffAddrSz),
	     Add#(e__, TLog#(1), TAdd#(1, cmdBuffAddrSz)),
	     Add#(a__, serverIdxSz, cmdBuffAddrSz),
	     Min#(2,TLog#(1),bpc),
	     Add#(d__, TLog#(1), TAdd#(1, serverIdxSz)),
	     Add#(f__, serverIdxSz, MemTagSize));
   let verbose = False;
   let beatShift = fromInteger(valueOf(beatShift));

   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();

   Integer bufferSizeBeats = bufferSizeBytes/valueOf(busWidthBytes);
   Reg#(Bool)          clientInFlight <- mkReg(False);
   ConfigCounter#(16)  clientAvail <- mkConfigCounter(fromInteger(bufferSizeBeats));
   Reg#(MemengineCmd)  clientCommand <- mkReg(unpack(0));
`ifdef USE_DUAL_CLOCK_FIFOF
   FIFOF#(MemDataF#(userWidth)) clientDataFifo <- mkDualClockBramFIFOF(clock, reset, clock, reset);
`else
   FIFOF#(MemDataF#(userWidth)) clientDataFifo <- mkSizedBRAMFIFOF(bufferSizeBeats);
`endif
   FIFO#(MemengineCmd) clientRequest <- mkFIFO();
   Reg#(Bit#(32))      clientLen <- mkReg(unpack(0));
   Reg#(Bit#(32))      clientBase <- mkReg(unpack(0));
   Reg#(NextReq)       clientNext <- mkReg(unpack(0));
   Count#(Bit#(32))    clientCycles <- mkCount(0);
   FIFOF#(MemRequestCycles) clientCyclesFifo <- mkFIFOF();

   FIFO#(Tuple3#(Bool,Bool,Bit#(BurstLenSize))) serverCheckAvail <- mkSizedFIFO(1);
   FIFOF#(ServerRequest#(serverIdxSz))      serverRequest <- mkFIFOF();
   FIFOF#(MemRequest)                          dmaRequest <- mkSizedFIFOF(valueOf(cmdQDepth));
   FIFO#(Tuple3#(Bit#(8),Bit#(serverIdxSz),Bool)) serverProcessing <- mkSizedFIFO(valueOf(cmdQDepth));
   FIFOF#(MemData#(busWidth))                       serverDataFifo <- mkFIFOF;

   Reg#(Bit#(8))                    respCnt <- mkReg(0);
   Reg#(Bit#(32)) counter <- mkReg(0);

   rule incCounter;
      counter <= counter + 1;
   endrule

   rule rule_cycles;
      clientCycles.incr(1);
   endrule
   rule rule_startNew if (!clientInFlight);
      let cmd <- toGet(clientRequest).get();
      clientInFlight <= True;
      clientCommand <= cmd;
      clientLen <= cmd.len - extend(cmd.burstLen);
      clientBase <= cmd.base;
      clientNext <= getNext(cmd.len, cmd.burstLen);
      clientCycles <= 0;
      if (verbose) $display("mkMemReadEngineBuff::%d rule_startNew %d", counter, clientAvail.read);
   endrule
   rule rule_checkAvail if (clientInFlight);
      let cmd_len = clientNext.len;
      let last_burst = clientNext.last;
      let cond0 <- clientAvail.maybeDecrement(unpack(extend(cmd_len>>beatShift)));
      serverCheckAvail.enq(tuple3(cond0,last_burst,cmd_len));
      if (verbose) $display("mkMemReadEngineBuff::%d rule_checkAvail avail %d burstLen %d cond0 %d last_burst %d", counter, clientAvail.read(), cmd_len>>beatShift, cond0, last_burst);
   endrule

   rule rule_requestServer if (clientInFlight);
      match {.cond0,.last_burst,.cmd_len} <- toGet(serverCheckAvail).get;
      if  (cond0) begin
	 if (verbose) $display("mkMemReadEngineBuff::%d rule_requestServer clientLen %d cond0 %d last_burst %d", counter, clientLen, cond0, last_burst);
	 serverProcessing.enq(tuple3(truncate(cmd_len>>beatShift), 0, last_burst));
	 if (verbose) $display("MemReadEngine::%d readReq idx %d offset %h burstLenBytes %h last %d", counter, 0, clientBase, cmd_len, last_burst);

	 dmaRequest.enq(MemRequest { sglId: clientCommand.sglId, offset: extend(clientBase),
	    burstLen:cmd_len, tag: fromInteger(channelNumber)
	    `ifdef BYTE_ENABLES
				    , firstbe: maxBound, lastbe: maxBound
	    `endif
	    });
         clientBase <= clientBase + extend(cmd_len);
         clientLen <= clientLen - extend(cmd_len);
         clientNext <= getNext(clientLen, clientCommand.burstLen);
	 if (last_burst) begin
	    if (verbose) $display("mkMemReadEngineBuff::%d rule_requestServer last_burst %d", counter, last_burst);
	    clientInFlight <= False;
	    `ifdef MEMENGINE_REQUEST_CYCLES
	    $display("clientCycles = %d", clientCycles);
	    clientCyclesFifo.enq(MemRequestCycles { tag:clientCommand.tag, cycles: clientCycles });
	    `endif
	 end
      end
   endrule

   rule read_data_rule;
      let d <- toGet(serverDataFifo).get();
      match {.rc, .idx, .last_burst} = serverProcessing.first;
      let new_respCnt = respCnt+1;
      let l = False;
      if (verbose) $display("mkMemReadEngineBuff::%d data %h new_respCnt %d rc %d last_burst %d idx %d clientInFlight %d eob %d", counter, d.data, new_respCnt, rc, last_burst, idx, clientInFlight, d.last);
      if (new_respCnt == rc) begin
	 respCnt <= 0;
	 serverProcessing.deq;
	 //$display("eob %d", idx);
	 l = last_burst;
      end
      else begin
	 respCnt <= new_respCnt;
      end
      clientDataFifo.enq(MemDataF { data: d.data, tag: d.tag >> valueOf(serverIdxSz), first: (respCnt == 0), last: l});
   endrule

   MemReadEngineServer#(userWidth) rs = (interface MemReadEngineServer#(userWidth);
		  interface Put request;
		     method Action put(MemengineCmd cmd);
`ifdef SIMULATION
			Bit#(32) bsb = fromInteger(bufferSizeBytes);
			Bit#(32) dw = fromInteger(valueOf(busWidthBytes));
			let mdw = ((cmd.len)/dw)*dw != cmd.len;
			let bbl = extend(cmd.burstLen) > bsb;
			if(bbl || mdw) begin
			   if (bbl)
			      $display("XXXXXXXXXX mkMemReadEngineBuff::unsupported burstLen %d %d", bsb, cmd.burstLen);
			   if (mdw)
			      $display("XXXXXXXXXX mkMemReadEngineBuff::unsupported len %d", cmd.len);
			end
			else
`endif
                           begin
			   clientRequest.enq(cmd);
			   end
 		     endmethod
		  endinterface
                  interface data = interface PipeOut;
	             method MemDataF#(userWidth) first;
	                return clientDataFifo.first;
	             endmethod
	             method Action deq;
	                if (verbose) $display("mkMemReadEngineBuff::check_out: data %h clientAvail %d eob %d", clientDataFifo.first.data, clientAvail.read(), clientDataFifo.first.last);
	                clientDataFifo.deq;
	                clientAvail.increment(1);
	             endmethod
	             method Bool notEmpty = clientDataFifo.notEmpty;
                  endinterface;
	       interface requestCycles = toPipeOut(clientCyclesFifo);
               endinterface);
   interface readServer = rs;
   interface PipeOut readReq = toPipeOut(dmaRequest);
   interface PipeIn readData = toPipeIn(serverDataFifo);
endmodule

module mkMemReadEngineBuff#(Integer bufferSizeBytes) (MemReadEngine#(busWidth, userWidth, cmdQDepth, numServers))
   provisos (Div#(busWidth,8,busWidthBytes),
	     Mul#(busWidthBytes,8,busWidth),
	     Add#(busWidth, 0, userWidth),
	     FunnelPipesPipelined#(1, numServers, MemTypes::MemData#(userWidth), 2),
	     FunnelPipesPipelined#(1, numServers, MemTypes::MemRequest, 2),
	     Add#(a__, TLog#(numServers), 6)
	     );
   let verbose = False;

   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();

   Integer bufferSizeBeats = bufferSizeBytes/valueOf(busWidthBytes);

   Vector#(numServers, MemReadChannel#(busWidth,userWidth,cmdQDepth)) readChannels <- genWithM(mkMemReadChannel(bufferSizeBytes));
   Vector#(numServers, FIFOF#(Bit#(MemTagSize))) readtagFifos <- replicateM(mkSizedFIFOF(valueOf(cmdQDepth)));
   function PipeOut#(MemRequest) readChannelDmaReadReq(Integer i);
      return readChannels[i].readReq;
   endfunction
   function MemReadEngineServer#(userWidth) readChannelServer(Integer i);
      return readChannels[i].readServer;
   endfunction

   FIFOF#(MemRequest) reqFifo <- mkFIFOF();
   FunnelPipe#(1,numServers,MemRequest,2) reqFunnel <- mkFunnelPipesPipelined(genWith(readChannelDmaReadReq));
   FIFOF#(MemData#(busWidth)) readDataFifo <- mkFIFOF();
   function Tuple2#(Bit#(TLog#(numServers)),MemData#(userWidth)) tagData(MemData#(userWidth) md);
      return tuple2(truncate(md.tag), md);
   endfunction
   UnFunnelPipe#(1,numServers,MemData#(userWidth),2) dataPipes <- mkUnFunnelPipesPipelined(vec(mapPipe(tagData, toPipeOut(readDataFifo))));
   // FIXME: pass this a parameter to mkMemReadChannel
   for (Integer channel = 0; channel < valueOf(numServers); channel = channel + 1)
      mkConnection(dataPipes[channel], readChannels[channel].readData);

   interface Vector  readServers = genWith(readChannelServer);
   interface MemReadClient dmaClient;
      interface Get    readReq = toGet(reqFunnel[0]);
      interface Put   readData = toPut(readDataFifo);
   endinterface

endmodule

// Copyright (c) 2013 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Vector::*;
import BuildVector::*;
import Cntrs::*;
import FIFOF::*;
import FIFO::*;
import GetPut::*;
import Assert::*;
import ClientServer::*;
import BRAM::*;
import BRAMFIFO::*;
import ConfigCounter::*;
import Connectable::*;
import ConnectalMemory::*;
import ConnectalBramFifo::*;
import MemTypes::*;
import Pipe::*;
import MemUtils::*;
import ConnectalConfig::*;

`include "ConnectalProjectConfig.bsv"

module mkMemReadEngine(MemReadEngine#(busWidth, userWidth, cmdQDepth, numServers))
   provisos( Mul#(TDiv#(busWidth, 8), 8, busWidth)
	    ,Add#(1, a__, numServers)
	    ,Add#(busWidth, 0, userWidth)
	    ,FunnelPipesPipelined#(1, numServers, MemTypes::MemData#(userWidth), 2)
	    ,Pipe::FunnelPipesPipelined#(1, numServers, MemTypes::MemRequest, 2)
	    ,Add#(b__, TLog#(numServers), 6)
      	    );
   let rv <- mkMemReadEngineBuff(valueOf(cmdQDepth) * valueOf(TExp#(BurstLenSize)));
   return rv;
endmodule

typedef struct {
    Bit#(BurstLenSize) len;
    Bool               last;
} NextReq deriving (Bits, Eq);

function NextReq getNext(Bit#(32) len, Bit#(BurstLenSize) burst);
   NextReq v;
   v.last = (len <= extend(burst));
   v.len = v.last ? truncate(len) : burst;
   return v;
endfunction

typedef struct {
   Bit#(indexSize)    idx;
   SGLId              sglId;
   Bit#(32)           base;
   Bit#(MemTagSize)   tag;
   Bool               last;
   Bit#(BurstLenSize) burstLen;
} ServerRequest#(numeric type indexSize) deriving (Bits, Eq);

interface MemReadChannel#(numeric type busWidth, numeric type userWidth, numeric type cmdQDepth);
   interface PipeOut#(MemRequest)        readReq;
   interface PipeIn#(MemData#(busWidth)) readData;
   interface MemReadEngineServer#(userWidth) readServer;
endinterface

module mkMemReadChannel#(Integer bufferSizeBytes, Integer channelNumber) (MemReadChannel#(busWidth, userWidth, cmdQDepth))
   provisos (Div#(busWidth,8,busWidthBytes),
	     Mul#(busWidthBytes,8,busWidth),
	     Log#(busWidthBytes,beatShift),
	     Log#(cmdQDepth,logCmdQDepth),
	     Log#(cmdQDepth, cmdBuffAddrSz),
	     Log#(1, serverIdxSz),
	     Add#(busWidth, 0, userWidth),
	     Add#(1,logCmdQDepth, outCntSz),
	     Add#(1, c__, 1),
	     Add#(b__, TLog#(1), cmdBuffAddrSz),
	     Add#(e__, TLog#(1), TAdd#(1, cmdBuffAddrSz)),
	     Add#(a__, serverIdxSz, cmdBuffAddrSz),
	     Min#(2,TLog#(1),bpc),
	     Add#(d__, TLog#(1), TAdd#(1, serverIdxSz)),
	     Add#(f__, serverIdxSz, MemTagSize));
   let verbose = False;
   let beatShift = fromInteger(valueOf(beatShift));

   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();

   Integer bufferSizeBeats = bufferSizeBytes/valueOf(busWidthBytes);
   Reg#(Bool)          clientInFlight <- mkReg(False);
   ConfigCounter#(16)  clientAvail <- mkConfigCounter(fromInteger(bufferSizeBeats));
   Reg#(MemengineCmd)  clientCommand <- mkReg(unpack(0));
`ifdef USE_DUAL_CLOCK_FIFOF
   FIFOF#(MemDataF#(userWidth)) clientDataFifo <- mkDualClockBramFIFOF(clock, reset, clock, reset);
`else
   FIFOF#(MemDataF#(userWidth)) clientDataFifo <- mkSizedBRAMFIFOF(bufferSizeBeats);
`endif
   FIFO#(MemengineCmd) clientRequest <- mkFIFO();
   Reg#(Bit#(32))      clientLen <- mkReg(unpack(0));
   Reg#(Bit#(32))      clientBase <- mkReg(unpack(0));
   Reg#(NextReq)       clientNext <- mkReg(unpack(0));
   Count#(Bit#(32))    clientCycles <- mkCount(0);
   FIFOF#(MemRequestCycles) clientCyclesFifo <- mkFIFOF();

   FIFO#(Tuple3#(Bool,Bool,Bit#(BurstLenSize))) serverCheckAvail <- mkSizedFIFO(1);
   FIFOF#(ServerRequest#(serverIdxSz))      serverRequest <- mkFIFOF();
   FIFOF#(MemRequest)                          dmaRequest <- mkSizedFIFOF(valueOf(cmdQDepth));
   FIFO#(Tuple3#(Bit#(8),Bit#(serverIdxSz),Bool)) serverProcessing <- mkSizedFIFO(valueOf(cmdQDepth));
   FIFOF#(MemData#(busWidth))                       serverDataFifo <- mkFIFOF;

   Reg#(Bit#(8))                    respCnt <- mkReg(0);
   Reg#(Bit#(32)) counter <- mkReg(0);

   rule incCounter;
      counter <= counter + 1;
   endrule

   rule rule_cycles;
      clientCycles.incr(1);
   endrule
   rule rule_startNew if (!clientInFlight);
      let cmd <- toGet(clientRequest).get();
      clientInFlight <= True;
      clientCommand <= cmd;
      clientLen <= cmd.len - extend(cmd.burstLen);
      clientBase <= cmd.base;
      clientNext <= getNext(cmd.len, cmd.burstLen);
      clientCycles <= 0;
      if (verbose) $display("mkMemReadEngineBuff::%d rule_startNew %d", counter, clientAvail.read);
   endrule
   rule rule_checkAvail if (clientInFlight);
      let cmd_len = clientNext.len;
      let last_burst = clientNext.last;
      let cond0 <- clientAvail.maybeDecrement(unpack(extend(cmd_len>>beatShift)));
      serverCheckAvail.enq(tuple3(cond0,last_burst,cmd_len));
      if (verbose) $display("mkMemReadEngineBuff::%d rule_checkAvail avail %d burstLen %d cond0 %d last_burst %d", counter, clientAvail.read(), cmd_len>>beatShift, cond0, last_burst);
   endrule

   rule rule_requestServer if (clientInFlight);
      match {.cond0,.last_burst,.cmd_len} <- toGet(serverCheckAvail).get;
      if  (cond0) begin
	 if (verbose) $display("mkMemReadEngineBuff::%d rule_requestServer clientLen %d cond0 %d last_burst %d", counter, clientLen, cond0, last_burst);
	 serverRequest.enq(ServerRequest{
	    idx:fromInteger(0),sglId:clientCommand.sglId,base:clientBase,
	    tag:clientCommand.tag,last:last_burst,burstLen:cmd_len});
         clientBase <= clientBase + extend(cmd_len);
         clientLen <= clientLen - extend(cmd_len);
         clientNext <= getNext(clientLen, clientCommand.burstLen);
	 if (last_burst) begin
	    if (verbose) $display("mkMemReadEngineBuff::%d rule_requestServer last_burst %d", counter, last_burst);
	    clientInFlight <= False;
	    `ifdef MEMENGINE_REQUEST_CYCLES
	    $display("clientCycles = %d", clientCycles);
	    clientCyclesFifo.enq(MemRequestCycles { tag:clientCommand.tag, cycles: clientCycles });
	    `endif
	 end
      end
   endrule

   rule rl_serverRequest;
      let sr <- toGet(serverRequest).get;
      serverProcessing.enq(tuple3(truncate(sr.burstLen>>beatShift), sr.idx, sr.last));
      if (verbose) $display("MemReadEngine::%d readReq idx %d offset %h burstLenBytes %h last %d", counter, sr.idx, sr.base, sr.burstLen, sr.last);

      dmaRequest.enq(MemRequest { sglId: sr.sglId, offset: extend(sr.base),
	 burstLen:sr.burstLen, tag: fromInteger(channelNumber)
`ifdef BYTE_ENABLES
				 , firstbe: maxBound, lastbe: maxBound
`endif
	       });
   endrule

   rule read_data_rule;
      let d <- toGet(serverDataFifo).get();
      match {.rc, .idx, .last_burst} = serverProcessing.first;
      let new_respCnt = respCnt+1;
      let l = False;
      if (verbose) $display("mkMemReadEngineBuff::%d data %h new_respCnt %d rc %d last_burst %d idx %d clientInFlight %d eob %d", counter, d.data, new_respCnt, rc, last_burst, idx, clientInFlight, d.last);
      if (new_respCnt == rc) begin
	 respCnt <= 0;
	 serverProcessing.deq;
	 //$display("eob %d", idx);
	 l = last_burst;
      end
      else begin
	 respCnt <= new_respCnt;
      end
      clientDataFifo.enq(MemDataF { data: d.data, tag: d.tag >> valueOf(serverIdxSz), first: (respCnt == 0), last: l});
   endrule

   MemReadEngineServer#(userWidth) rs = (interface MemReadEngineServer#(userWidth);
		  interface Put request;
		     method Action put(MemengineCmd cmd);
`ifdef SIMULATION
			Bit#(32) bsb = fromInteger(bufferSizeBytes);
			Bit#(32) dw = fromInteger(valueOf(busWidthBytes));
			let mdw = ((cmd.len)/dw)*dw != cmd.len;
			let bbl = extend(cmd.burstLen) > bsb;
			if(bbl || mdw) begin
			   if (bbl)
			      $display("XXXXXXXXXX mkMemReadEngineBuff::unsupported burstLen %d %d", bsb, cmd.burstLen);
			   if (mdw)
			      $display("XXXXXXXXXX mkMemReadEngineBuff::unsupported len %d", cmd.len);
			end
			else
`endif
                           begin
			   clientRequest.enq(cmd);
			   end
 		     endmethod
		  endinterface
                  interface data = interface PipeOut;
	             method MemDataF#(userWidth) first;
	                return clientDataFifo.first;
	             endmethod
	             method Action deq;
	                if (verbose) $display("mkMemReadEngineBuff::check_out: data %h clientAvail %d eob %d", clientDataFifo.first.data, clientAvail.read(), clientDataFifo.first.last);
	                clientDataFifo.deq;
	                clientAvail.increment(1);
	             endmethod
	             method Bool notEmpty = clientDataFifo.notEmpty;
                  endinterface;
	       interface requestCycles = toPipeOut(clientCyclesFifo);
               endinterface);
   interface readServer = rs;
   interface PipeOut readReq = toPipeOut(dmaRequest);
   interface PipeIn readData = toPipeIn(serverDataFifo);
endmodule

module mkMemReadEngineBuff#(Integer bufferSizeBytes) (MemReadEngine#(busWidth, userWidth, cmdQDepth, numServers))
   provisos (Div#(busWidth,8,busWidthBytes),
	     Mul#(busWidthBytes,8,busWidth),
	     Add#(busWidth, 0, userWidth),
	     FunnelPipesPipelined#(1, numServers, MemTypes::MemData#(userWidth), 2),
	     FunnelPipesPipelined#(1, numServers, MemTypes::MemRequest, 2),
	     Add#(a__, TLog#(numServers), 6)
	     );
   let verbose = False;

   let clock <- exposeCurrentClock();
   let reset <- exposeCurrentReset();

   Integer bufferSizeBeats = bufferSizeBytes/valueOf(busWidthBytes);

   Vector#(numServers, MemReadChannel#(busWidth,userWidth,cmdQDepth)) readChannels <- genWithM(mkMemReadChannel(bufferSizeBytes));
   Vector#(numServers, FIFOF#(Bit#(MemTagSize))) readtagFifos <- replicateM(mkSizedFIFOF(valueOf(cmdQDepth)));
   function PipeOut#(MemRequest) readChannelDmaReadReq(Integer i);
      return readChannels[i].readReq;
   endfunction
   function MemReadEngineServer#(userWidth) readChannelServer(Integer i);
      return readChannels[i].readServer;
   endfunction

   FIFOF#(MemRequest) reqFifo <- mkFIFOF();
   FunnelPipe#(1,numServers,MemRequest,2) reqFunnel <- mkFunnelPipesPipelined(genWith(readChannelDmaReadReq));
   FIFOF#(MemData#(busWidth)) readDataFifo <- mkFIFOF();
   function Tuple2#(Bit#(TLog#(numServers)),MemData#(userWidth)) tagData(MemData#(userWidth) md);
      return tuple2(truncate(md.tag), md);
   endfunction
   UnFunnelPipe#(1,numServers,MemData#(userWidth),2) dataPipes <- mkUnFunnelPipesPipelined(vec(mapPipe(tagData, toPipeOut(readDataFifo))));
   // FIXME: pass this a parameter to mkMemReadChannel
   for (Integer channel = 0; channel < valueOf(numServers); channel = channel + 1)
      mkConnection(dataPipes[channel], readChannels[channel].readData);

   interface Vector  readServers = genWith(readChannelServer);
   interface MemReadClient dmaClient;
      interface Get    readReq = toGet(reqFunnel[0]);
      interface Put   readData = toPut(readDataFifo);
   endinterface

endmodule


add provisos

add provisos

// Copyright (c) 2013 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Gearbox::*;
import ChannelSelect::*;
import Complex::*;
import FPCMult::*;
import FixedPoint::*;
import Pipe::*;

/* rfreqDataWrite data is FixedPoint(2,14) */
/* setCoeff data is FixedPoint(2, 23) */
interface ChannelSelectTestRequest;
   method Action rfreqDataWrite(Bit#(16) dataRe, Bit#(16) dataIm);
   method Action setCoeff(Bit#(10) addr, Bit#(32) valueRe, Bit#(32) valueIm);
endinterface

/* rfreqDataWrite data is FixedPoint(2,14) */
interface ChannelSelectTestIndication;
   method Action ifreqData(Bit#(16) dataRe, Bit#(16) dataIm);
endinterface

module mkChannelSelectTestRequest#(ChannelSelectTestIndication indication) (ChannelSelectTestRequest)
   provisos(Bits#(CoeffData, a__),
	    Bits#(ProductData, b__),
            Bits#(MulData, c__));

   Clock clk <- exposeCurrentClock;
   Reset rst <- exposeCurrentReset;
   Gearbox#(1, 2, Complex#(Signal)) gb <- mk1toNGearbox(clk, rst, clk, rst);
   ChannelSelect cs <- mkChannelSelect(64);

   rule processRF;
      let data = gb.first();
      gb.deq();
      cs.rfreq.enq(data);
   endrule

   rule processIF;
      let data = cs.ifreq.first();
      cs.ifreq.deq();
      indication.ifreqData(data.rel, data.img);
   endrule

   method Action rfreqDataBit(Bit#(16) dataRe, Bit#(16) dataIm);
      FixedPoint#(2,14) re = unpack(pack(dataRe));
      FixedPoint#(2,14) im = unpack(pack(dataIm));
      gb.enq(Complex{rel: re, img: im});
   endmethod

   method Action setCoeff(Bit#(10) addr, Bit#(32) valueRe, Bit#(32) valueIm);
    FixedPoint#(2, 23) re = unpack(pack(truncate(valueRe)));
    FixedPoint#(2, 23) im = unpack(pack(truncate(valueIm)));
      cs.setCoeff(addr, Complex{rel: re, img:im});
   endmethod
endmodule

// Copyright (c) 2013 Quanta Research Cambridge, Inc.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Gearbox::*;
import ChannelSelect::*;
import Complex::*;
import FPCMult::*;
import FixedPoint::*;

/* rfreqDataWrite data is FixedPoint(2,14) */
/* setCoeff data is FixedPoint(2, 23) */
interface ChannelSelectTestRequest;
   method Action rfreqDataWrite(Bit#(16) dataRe, Bit#(16) dataIm);
   method Action setCoeff(Bit#(10) addr, Bit#(32) valueRe, Bit#(32) valueIm);
endinterface

/* rfreqDataWrite data is FixedPoint(2,14) */
interface ChannelSelectTestIndication;
   method Action ifreqData(Bit#(16) dataRe, Bit#(16) dataIm);
endinterface

module mkChannelSelectTestRequest#(ChannelSelectTestIndication indication) (ChannelSelectTestRequest);
   Clock clk <- exposeCurrentClock;
   Reset rst <- exposeCurrentReset;
   Gearbox#(1, 2, Complex#(Signal)) gb <- mk1toNGearbox(clk, rst, clk, rst);
   ChannelSelect cs <- mkChannelSelect(64);

   rule processRF;
      let data = gb.first();
      gb.deq();
      cs.rfreq.enq(data);
   endrule

   rule processIF;
      let data = cs.ifreq.first();
      cs.ifreq.deq();
      indication.ifreqData(data);
   endrule

   method Action rfreqDataBit(Bit#(16) dataRe, Bit#(16) dataIm);
      FixedPoint#(2,14) re = unpack(pack(dataRe));
      FixedPoint#(2,14) im = unpack(pack(dataIm));
      gb.enq(Complex{rel: re, img: im});
   endmethod

   method Action setCoeff(Bit#(10) addr, Bit#(32) valueRe, Bit#(32) valueIm);
    FixedPoint#(2, 23) re = unpack(pack(truncate(valueRe)));
    FixedPoint#(2, 23) im = unpack(pack(truncate(valueIm)));
      cs.setCoeff(addr, Complex{rel: re, img:im});
   endmethod
endmodule


clear lread_data_timestamp on reset

clear lread_data_timestamp on reset

// Copyright (c) 2015 Cornell University.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Clocks::*;
import FIFO::*;
import FIFOF::*;
import Vector::*;
import Pipe::*;
import GetPut::*;
import Probe::*;

typedef struct {
   Bit#(8) port_no;
   Bit#(64) data;
} BufData deriving (Bits, Eq);

interface DtpIfc;
   interface PipeIn#(Bit#(128)) timestamp; // streaming time counter from NetTop.
   interface Vector#(4, PipeOut#(Bit#(53))) fromHost;
   interface Vector#(4, PipeIn#(Bit#(53)))  toHost;
   interface Vector#(4, PipeIn#(Bit#(32)))  delay;
   interface Vector#(4, PipeIn#(Bit#(32)))  state;
   interface Vector#(4, PipeIn#(Bit#(64)))  jumpCount;
   interface Vector#(4, PipeIn#(Bit#(53)))  cLocal;
   interface PipeIn#(Bit#(53)) globalOut;
   interface Vector#(4, PipeOut#(Bit#(32))) interval;
   interface Reset rst;
   interface PipeIn#(Bit#(1)) isSwitch;
endinterface

interface SonicUserRequest;
   method Action dtp_read_version();
   method Action dtp_reset(Bit#(32) len);
   method Action dtp_set_cnt(Bit#(8) port_no, Bit#(64) c);
   method Action dtp_read_delay(Bit#(8) port_no);
   method Action dtp_read_state(Bit#(8) port_no);
   method Action dtp_read_error(Bit#(8) port_no);
   method Action dtp_read_cnt(Bit#(8) cmd);
   method Action dtp_logger_write_cnt(Bit#(8) port_no, Bit#(64) local_cnt);
   method Action dtp_logger_read_cnt(Bit#(8) port_no);
   method Action dtp_read_local_cnt(Bit#(8) port_no);
   method Action dtp_read_global_cnt();
   method Action dtp_set_beacon_interval(Bit#(8) port_no, Bit#(32) interval);
   method Action dtp_read_beacon_interval(Bit#(8) port_no);
/* NOT IMPLEMENTED YET.
   method Action dtp_ctrl_disable();
   method Action dtp_ctrl_enable();
*/
endinterface

interface SonicUserIndication;
   method Action dtp_read_version_resp(Bit#(32) version);
   method Action dtp_read_delay_resp(Bit#(8) port_no, Bit#(32) delay);
   method Action dtp_read_state_resp(Bit#(8) port_no, Bit#(32) state);
   method Action dtp_read_error_resp(Bit#(8) port_no, Bit#(64) jumpc);
   method Action dtp_read_cnt_resp(Bit#(64) val);
   method Action dtp_logger_read_cnt_resp(Bit#(8) port_no, Bit#(64) localc, Bit#(64) msg1, Bit#(64) msg2);
   method Action dtp_read_local_cnt_resp(Bit#(8) port_no, Bit#(64) val);
   method Action dtp_read_global_cnt_resp(Bit#(64) val);
   method Action dtp_read_beacon_interval_resp(Bit#(8) port_no, Bit#(32) interval);
endinterface

interface SonicUser;
   interface SonicUserRequest request;
   interface DtpIfc           dtp;
endinterface

module mkSonicUser#(SonicUserIndication indication)(SonicUser);
   let verbose = False;
   Clock defaultClock <- exposeCurrentClock();

   Reg#(Bit#(64))  cycle_count <- mkReg(0);
   Reg#(Bit#(64))  last_count  <- mkReg(0);

   FIFOF#(Bit#(128)) cntFifo <- mkFIFOF();
   Reg#(Bit#(128))   cycle_reg <- mkReg(0);
   Reg#(Bit#(1))     isSwitch_reg <- mkReg(0);
   Vector#(4, Reg#(Bit#(32))) beacon_interval <- replicateM(mkReg(1000)); //default beacon interval is 1000.

   Vector#(4, FIFOF#(Bit#(53))) fromHostFifo <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(53))) toHostFifo   <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(32))) delayFifo    <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(32))) stateFifo    <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(64))) jumpCountFifo <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(53))) cLocalFifo   <- replicateM(mkSizedFIFOF(4));
   FIFOF#(Bit#(53)) cGlobalFifo <- mkSizedFIFOF(4);
   Vector#(4, FIFOF#(Bit#(32))) intervalFifo <- replicateM(mkSizedFIFOF(4));
   FIFOF#(Bit#(1)) isSwitchFifo <- mkFIFOF();

   Reg#(Bit#(8))  lwrite_port <- mkReg(0);
   FIFOF#(BufData) lwrite_data_cycle1 <- mkSizedFIFOF(8);
   FIFOF#(BufData) lwrite_data_cycle2 <- mkSizedFIFOF(8);
   FIFOF#(void) log_write_cf <- mkFIFOF;

   Vector#(4, FIFOF#(BufData)) lread_data_cycle1 <- replicateM(mkSizedFIFOF(8));
   Vector#(4, FIFOF#(BufData)) lread_data_cycle2 <- replicateM(mkSizedFIFOF(8));
   Vector#(4, FIFOF#(Bit#(53))) lread_data_timestamp <- replicateM(mkSizedFIFOF(8));

   Reg#(Bit#(28)) dtp_rst_cntr <- mkReg(0);
   MakeResetIfc dtpResetOut <- mkResetSync(0, False, defaultClock);

   rule count;
      cycle_count <= cycle_count + 1;
   endrule

   // clear all fifo on reset
   rule clearOnReset(dtpResetOut.isAsserted);
      for (Integer i=0; i<4; i=i+1) begin
         fromHostFifo[i].clear();
         toHostFifo[i].clear();
         delayFifo[i].clear();
         stateFifo[i].clear();
         jumpCountFifo[i].clear();
         cLocalFifo[i].clear();
         intervalFifo[i].clear();
         lread_data_cycle1[i].clear();
         lread_data_cycle2[i].clear();
         lread_data_timestamp[i].clear();
      end
      cGlobalFifo.clear();
      cntFifo.clear();
      lwrite_data_cycle1.clear();
      lwrite_data_cycle2.clear();
      log_write_cf.clear();
   endrule

   // dtp_read_cnt
   rule snapshot_dtp_timestamp;
      let v <- toGet(cntFifo).get;
      cycle_reg <= v;
   endrule

   // dtp_read_delay
   Vector#(4, Reg#(Bit#(32))) delay_reg <- replicateM(mkReg(0));
   for (Integer i=0; i<4; i=i+1) begin
      rule snapshot_delay;
         let v <- toGet(delayFifo[i]).get;
         delay_reg[i] <= v;
      endrule
   end

   // dtp_read_state
   Vector#(4, Reg#(Bit#(32))) state_reg <- replicateM(mkReg(0));
   for (Integer i=0; i<4; i=i+1) begin
      rule snapshot_state;
         let v <- toGet(stateFifo[i]).get;
         state_reg[i] <= v;
      endrule
   end

   // dtp_read_error
   Vector#(4, Reg#(Bit#(64))) jumpc_reg <- replicateM(mkReg(0));
   for (Integer i=0; i<4; i=i+1) begin
      rule snapshot_jumpc;
         let v <- toGet(jumpCountFifo[i]).get;
         jumpc_reg[i] <= v;
      endrule
   end

   // dtp_read_local_cnt
   Vector#(4, Reg#(Bit#(53))) clocal_reg <- replicateM(mkReg(0));
   for (Integer i=0; i<4; i=i+1) begin
      rule snapshot_clocal;
         let v <- toGet(cLocalFifo[i]).get;
         clocal_reg[i] <= v;
      endrule
   end

   // dtp_read_global_cnt
   Reg#(Bit#(53)) cglobal_reg <- mkReg(0);
   rule snapshot_cglobal;
      let v <- toGet(cGlobalFifo).get;
      cglobal_reg <= v;
   endrule

   // dtp_set_interval
   for (Integer i=0; i<4; i=i+1) begin
      rule set_interval;
         let v = beacon_interval[i];
         intervalFifo[i].enq(v);
      endrule
   end

   // dtp_logger_write_cnt
   rule log_from_host_cycle1;
      let v = lwrite_data_cycle1.first;
      if (fromHostFifo[v.port_no].notFull) begin
         fromHostFifo[v.port_no].enq({1'b0, truncate(v.data)});
         log_write_cf.enq(?);
         lwrite_data_cycle1.deq;
      end
   endrule
   rule log_from_host_cycle2 (log_write_cf.notEmpty);
      let v = lwrite_data_cycle2.first;
      if (fromHostFifo[v.port_no].notFull) begin
         fromHostFifo[v.port_no].enq({1'b1, truncate(v.data)});
         lwrite_data_cycle2.deq;
         log_write_cf.deq;
      end
   endrule

   for (Integer i=0; i<4; i=i+1) begin
      rule save_host_data (toHostFifo[i].notEmpty && lread_data_cycle1[i].notFull &&
                           lread_data_cycle2[i].notFull && lread_data_timestamp[i].notFull);
         Bit#(53) v = toHostFifo[i].first;
         case (v[52]) matches
            0: begin
               lread_data_cycle1[i].enq(BufData{port_no:fromInteger(i), data:zeroExtend(v[51:0])});
            end
            1: begin
               lread_data_cycle2[i].enq(BufData{port_no:fromInteger(i), data:zeroExtend(v[51:0])});
               Bit#(53) timestamp = 0;
               if (isSwitch_reg == 0) begin // 0 for NIC, 1 for Switch
                  timestamp = clocal_reg[i];
               end
               else begin
                  timestamp = cglobal_reg;
               end
               lread_data_timestamp[i].enq(timestamp);
            end
         endcase
         toHostFifo[i].deq;
      endrule
   end

   rule assert_reset (dtp_rst_cntr != 0);
      dtp_rst_cntr <= dtp_rst_cntr - 1;
      dtpResetOut.assertReset;
   endrule

   rule switch_mode;
      let v <- toGet(isSwitchFifo).get;
      isSwitch_reg <= v;
   endrule

   // Interface to external modules.
   interface dtp = (interface DtpIfc;
      interface timestamp = toPipeIn(cntFifo);
      interface delay     = map(toPipeIn, delayFifo);
      interface state     = map(toPipeIn, stateFifo);
      interface jumpCount = map(toPipeIn, jumpCountFifo);
      interface toHost    = map(toPipeIn, toHostFifo);
      interface fromHost  = map(toPipeOut, fromHostFifo);
      interface cLocal    = map(toPipeIn, cLocalFifo);
      interface globalOut = toPipeIn(cGlobalFifo);
      interface interval  = map(toPipeOut, intervalFifo);
      interface rst       = dtpResetOut.new_rst;
      interface isSwitch  = toPipeIn(isSwitchFifo);
   endinterface);

   // API implementation
   interface SonicUserRequest request;
   method Action dtp_read_version();
      let v = `DtpVersion; //Defined in Makefile as time of compilation.
      indication.dtp_read_version_resp(v);
   endmethod
   method Action dtp_reset(Bit#(32) len);
      Bit#(28) limit = truncate(len) & 28'hFFFFFFF;
      if (limit == 0) limit = 32;
      dtp_rst_cntr <= limit;
   endmethod
   method Action dtp_set_cnt(Bit#(8) port_no, Bit#(64) c);
      //
   endmethod
   method Action dtp_read_delay(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_delay_resp(port_no, truncate(delay_reg[port_no]));
      end
   endmethod
   method Action dtp_read_state(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_state_resp(port_no, state_reg[port_no]);
      end
   endmethod
   method Action dtp_read_error(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_error_resp(port_no, jumpc_reg[port_no]);
      end
   endmethod
   method Action dtp_read_cnt(Bit#(8) port_no);
      indication.dtp_read_cnt_resp(truncate(cycle_reg));
   endmethod
   method Action dtp_logger_write_cnt(Bit#(8) port_no, Bit#(64) host_timestamp);
      // Check valid port No.
      if (port_no < 4) begin
         lwrite_data_cycle1.enq(BufData{port_no: port_no, data: host_timestamp});
         lwrite_data_cycle2.enq(BufData{port_no: port_no, data: zeroExtend(clocal_reg[port_no])});
      end
   endmethod
   method Action dtp_logger_read_cnt(Bit#(8) port_no);
      if (port_no < 4) begin
         if (lread_data_cycle1[port_no].notEmpty && lread_data_cycle2[port_no].notEmpty) begin
            Bit#(64) remote_message1 = lread_data_cycle1[port_no].first.data;
            Bit#(64) remote_message2 = lread_data_cycle2[port_no].first.data;
            Bit#(53) timestamp = lread_data_timestamp[port_no].first;
            indication.dtp_logger_read_cnt_resp(port_no,
                                                zeroExtend(timestamp),
                                                zeroExtend(remote_message1),
                                                zeroExtend(remote_message2));
            lread_data_cycle1[port_no].deq;
            lread_data_cycle2[port_no].deq;
            lread_data_timestamp[port_no].deq;
         end
      end
   endmethod
   method Action dtp_read_local_cnt(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_local_cnt_resp(port_no, zeroExtend(clocal_reg[port_no]));
      end
   endmethod
   method Action dtp_read_global_cnt();
      indication.dtp_read_global_cnt_resp(zeroExtend(cglobal_reg));
   endmethod
   method Action dtp_set_beacon_interval(Bit#(8) port_no, Bit#(32) interval);
      if (port_no < 4) begin
         beacon_interval[port_no] <= interval;
      end
   endmethod
   method Action dtp_read_beacon_interval(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_beacon_interval_resp(port_no, beacon_interval[port_no]);
      end
   endmethod
   endinterface
endmodule

// Copyright (c) 2015 Cornell University.

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy,
// modify, merge, publish, distribute, sublicense, and/or sell copies
// of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

import Clocks::*;
import FIFO::*;
import FIFOF::*;
import Vector::*;
import Pipe::*;
import GetPut::*;
import Probe::*;

typedef struct {
   Bit#(8) port_no;
   Bit#(64) data;
} BufData deriving (Bits, Eq);

interface DtpIfc;
   interface PipeIn#(Bit#(128)) timestamp; // streaming time counter from NetTop.
   interface Vector#(4, PipeOut#(Bit#(53))) fromHost;
   interface Vector#(4, PipeIn#(Bit#(53)))  toHost;
   interface Vector#(4, PipeIn#(Bit#(32)))  delay;
   interface Vector#(4, PipeIn#(Bit#(32)))  state;
   interface Vector#(4, PipeIn#(Bit#(64)))  jumpCount;
   interface Vector#(4, PipeIn#(Bit#(53)))  cLocal;
   interface PipeIn#(Bit#(53)) globalOut;
   interface Vector#(4, PipeOut#(Bit#(32))) interval;
   interface Reset rst;
   interface PipeIn#(Bit#(1)) isSwitch;
endinterface

interface SonicUserRequest;
   method Action dtp_read_version();
   method Action dtp_reset(Bit#(32) len);
   method Action dtp_set_cnt(Bit#(8) port_no, Bit#(64) c);
   method Action dtp_read_delay(Bit#(8) port_no);
   method Action dtp_read_state(Bit#(8) port_no);
   method Action dtp_read_error(Bit#(8) port_no);
   method Action dtp_read_cnt(Bit#(8) cmd);
   method Action dtp_logger_write_cnt(Bit#(8) port_no, Bit#(64) local_cnt);
   method Action dtp_logger_read_cnt(Bit#(8) port_no);
   method Action dtp_read_local_cnt(Bit#(8) port_no);
   method Action dtp_read_global_cnt();
   method Action dtp_set_beacon_interval(Bit#(8) port_no, Bit#(32) interval);
   method Action dtp_read_beacon_interval(Bit#(8) port_no);
/* NOT IMPLEMENTED YET.
   method Action dtp_ctrl_disable();
   method Action dtp_ctrl_enable();
*/
endinterface

interface SonicUserIndication;
   method Action dtp_read_version_resp(Bit#(32) version);
   method Action dtp_read_delay_resp(Bit#(8) port_no, Bit#(32) delay);
   method Action dtp_read_state_resp(Bit#(8) port_no, Bit#(32) state);
   method Action dtp_read_error_resp(Bit#(8) port_no, Bit#(64) jumpc);
   method Action dtp_read_cnt_resp(Bit#(64) val);
   method Action dtp_logger_read_cnt_resp(Bit#(8) port_no, Bit#(64) localc, Bit#(64) msg1, Bit#(64) msg2);
   method Action dtp_read_local_cnt_resp(Bit#(8) port_no, Bit#(64) val);
   method Action dtp_read_global_cnt_resp(Bit#(64) val);
   method Action dtp_read_beacon_interval_resp(Bit#(8) port_no, Bit#(32) interval);
endinterface

interface SonicUser;
   interface SonicUserRequest request;
   interface DtpIfc           dtp;
endinterface

module mkSonicUser#(SonicUserIndication indication)(SonicUser);
   let verbose = False;
   Clock defaultClock <- exposeCurrentClock();

   Reg#(Bit#(64))  cycle_count <- mkReg(0);
   Reg#(Bit#(64))  last_count  <- mkReg(0);

   FIFOF#(Bit#(128)) cntFifo <- mkFIFOF();
   Reg#(Bit#(128))   cycle_reg <- mkReg(0);
   Reg#(Bit#(1))     isSwitch_reg <- mkReg(0);
   Vector#(4, Reg#(Bit#(32))) beacon_interval <- replicateM(mkReg(1000)); //default beacon interval is 1000.

   Vector#(4, FIFOF#(Bit#(53))) fromHostFifo <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(53))) toHostFifo   <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(32))) delayFifo    <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(32))) stateFifo    <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(64))) jumpCountFifo <- replicateM(mkSizedFIFOF(4));
   Vector#(4, FIFOF#(Bit#(53))) cLocalFifo   <- replicateM(mkSizedFIFOF(4));
   FIFOF#(Bit#(53)) cGlobalFifo <- mkSizedFIFOF(4);
   Vector#(4, FIFOF#(Bit#(32))) intervalFifo <- replicateM(mkSizedFIFOF(4));
   FIFOF#(Bit#(1)) isSwitchFifo <- mkFIFOF();

   Reg#(Bit#(8))  lwrite_port <- mkReg(0);
   FIFOF#(BufData) lwrite_data_cycle1 <- mkSizedFIFOF(8);
   FIFOF#(BufData) lwrite_data_cycle2 <- mkSizedFIFOF(8);
   FIFOF#(void) log_write_cf <- mkFIFOF;

   Vector#(4, FIFOF#(BufData)) lread_data_cycle1 <- replicateM(mkSizedFIFOF(8));
   Vector#(4, FIFOF#(BufData)) lread_data_cycle2 <- replicateM(mkSizedFIFOF(8));
   Vector#(4, FIFOF#(Bit#(53))) lread_data_timestamp <- replicateM(mkSizedFIFOF(8));

   Reg#(Bit#(28)) dtp_rst_cntr <- mkReg(0);
   MakeResetIfc dtpResetOut <- mkResetSync(0, False, defaultClock);

   rule count;
      cycle_count <= cycle_count + 1;
   endrule

   // clear all fifo on reset
   rule clearOnReset(dtpResetOut.isAsserted);
      for (Integer i=0; i<4; i=i+1) begin
         fromHostFifo[i].clear();
         toHostFifo[i].clear();
         delayFifo[i].clear();
         stateFifo[i].clear();
         jumpCountFifo[i].clear();
         cLocalFifo[i].clear();
         intervalFifo[i].clear();
         lread_data_cycle1[i].clear();
         lread_data_cycle2[i].clear();
      end
      cGlobalFifo.clear();
      cntFifo.clear();
      lwrite_data_cycle1.clear();
      lwrite_data_cycle2.clear();
      log_write_cf.clear();
   endrule

   // dtp_read_cnt
   rule snapshot_dtp_timestamp;
      let v <- toGet(cntFifo).get;
      cycle_reg <= v;
   endrule

   // dtp_read_delay
   Vector#(4, Reg#(Bit#(32))) delay_reg <- replicateM(mkReg(0));
   for (Integer i=0; i<4; i=i+1) begin
      rule snapshot_delay;
         let v <- toGet(delayFifo[i]).get;
         delay_reg[i] <= v;
      endrule
   end

   // dtp_read_state
   Vector#(4, Reg#(Bit#(32))) state_reg <- replicateM(mkReg(0));
   for (Integer i=0; i<4; i=i+1) begin
      rule snapshot_state;
         let v <- toGet(stateFifo[i]).get;
         state_reg[i] <= v;
      endrule
   end

   // dtp_read_error
   Vector#(4, Reg#(Bit#(64))) jumpc_reg <- replicateM(mkReg(0));
   for (Integer i=0; i<4; i=i+1) begin
      rule snapshot_jumpc;
         let v <- toGet(jumpCountFifo[i]).get;
         jumpc_reg[i] <= v;
      endrule
   end

   // dtp_read_local_cnt
   Vector#(4, Reg#(Bit#(53))) clocal_reg <- replicateM(mkReg(0));
   for (Integer i=0; i<4; i=i+1) begin
      rule snapshot_clocal;
         let v <- toGet(cLocalFifo[i]).get;
         clocal_reg[i] <= v;
      endrule
   end

   // dtp_read_global_cnt
   Reg#(Bit#(53)) cglobal_reg <- mkReg(0);
   rule snapshot_cglobal;
      let v <- toGet(cGlobalFifo).get;
      cglobal_reg <= v;
   endrule

   // dtp_set_interval
   for (Integer i=0; i<4; i=i+1) begin
      rule set_interval;
         let v = beacon_interval[i];
         intervalFifo[i].enq(v);
      endrule
   end

   // dtp_logger_write_cnt
   rule log_from_host_cycle1;
      let v = lwrite_data_cycle1.first;
      if (fromHostFifo[v.port_no].notFull) begin
         fromHostFifo[v.port_no].enq({1'b0, truncate(v.data)});
         log_write_cf.enq(?);
         lwrite_data_cycle1.deq;
      end
   endrule
   rule log_from_host_cycle2 (log_write_cf.notEmpty);
      let v = lwrite_data_cycle2.first;
      if (fromHostFifo[v.port_no].notFull) begin
         fromHostFifo[v.port_no].enq({1'b1, truncate(v.data)});
         lwrite_data_cycle2.deq;
         log_write_cf.deq;
      end
   endrule

   for (Integer i=0; i<4; i=i+1) begin
      rule save_host_data (toHostFifo[i].notEmpty && lread_data_cycle1[i].notFull &&
                           lread_data_cycle2[i].notFull && lread_data_timestamp[i].notFull);
         Bit#(53) v = toHostFifo[i].first;
         case (v[52]) matches
            0: begin
               lread_data_cycle1[i].enq(BufData{port_no:fromInteger(i), data:zeroExtend(v[51:0])});
            end
            1: begin
               lread_data_cycle2[i].enq(BufData{port_no:fromInteger(i), data:zeroExtend(v[51:0])});
               Bit#(53) timestamp = 0;
               if (isSwitch_reg == 0) begin // 0 for NIC, 1 for Switch
                  timestamp = clocal_reg[i];
               end
               else begin
                  timestamp = cglobal_reg;
               end
               lread_data_timestamp[i].enq(timestamp);
            end
         endcase
         toHostFifo[i].deq;
      endrule
   end

   rule assert_reset (dtp_rst_cntr != 0);
      dtp_rst_cntr <= dtp_rst_cntr - 1;
      dtpResetOut.assertReset;
   endrule

   rule switch_mode;
      let v <- toGet(isSwitchFifo).get;
      isSwitch_reg <= v;
   endrule

   // Interface to external modules.
   interface dtp = (interface DtpIfc;
      interface timestamp = toPipeIn(cntFifo);
      interface delay     = map(toPipeIn, delayFifo);
      interface state     = map(toPipeIn, stateFifo);
      interface jumpCount = map(toPipeIn, jumpCountFifo);
      interface toHost    = map(toPipeIn, toHostFifo);
      interface fromHost  = map(toPipeOut, fromHostFifo);
      interface cLocal    = map(toPipeIn, cLocalFifo);
      interface globalOut = toPipeIn(cGlobalFifo);
      interface interval  = map(toPipeOut, intervalFifo);
      interface rst       = dtpResetOut.new_rst;
      interface isSwitch  = toPipeIn(isSwitchFifo);
   endinterface);

   // API implementation
   interface SonicUserRequest request;
   method Action dtp_read_version();
      let v = `DtpVersion; //Defined in Makefile as time of compilation.
      indication.dtp_read_version_resp(v);
   endmethod
   method Action dtp_reset(Bit#(32) len);
      Bit#(28) limit = truncate(len) & 28'hFFFFFFF;
      if (limit == 0) limit = 32;
      dtp_rst_cntr <= limit;
   endmethod
   method Action dtp_set_cnt(Bit#(8) port_no, Bit#(64) c);
      //
   endmethod
   method Action dtp_read_delay(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_delay_resp(port_no, truncate(delay_reg[port_no]));
      end
   endmethod
   method Action dtp_read_state(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_state_resp(port_no, state_reg[port_no]);
      end
   endmethod
   method Action dtp_read_error(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_error_resp(port_no, jumpc_reg[port_no]);
      end
   endmethod
   method Action dtp_read_cnt(Bit#(8) port_no);
      indication.dtp_read_cnt_resp(truncate(cycle_reg));
   endmethod
   method Action dtp_logger_write_cnt(Bit#(8) port_no, Bit#(64) host_timestamp);
      // Check valid port No.
      if (port_no < 4) begin
         lwrite_data_cycle1.enq(BufData{port_no: port_no, data: host_timestamp});
         lwrite_data_cycle2.enq(BufData{port_no: port_no, data: zeroExtend(clocal_reg[port_no])});
      end
   endmethod
   method Action dtp_logger_read_cnt(Bit#(8) port_no);
      if (port_no < 4) begin
         if (lread_data_cycle1[port_no].notEmpty && lread_data_cycle2[port_no].notEmpty) begin
            Bit#(64) remote_message1 = lread_data_cycle1[port_no].first.data;
            Bit#(64) remote_message2 = lread_data_cycle2[port_no].first.data;
            Bit#(53) timestamp = lread_data_timestamp[port_no].first;
            indication.dtp_logger_read_cnt_resp(port_no,
                                                zeroExtend(timestamp),
                                                zeroExtend(remote_message1),
                                                zeroExtend(remote_message2));
            lread_data_cycle1[port_no].deq;
            lread_data_cycle2[port_no].deq;
            lread_data_timestamp[port_no].deq;
         end
      end
   endmethod
   method Action dtp_read_local_cnt(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_local_cnt_resp(port_no, zeroExtend(clocal_reg[port_no]));
      end
   endmethod
   method Action dtp_read_global_cnt();
      indication.dtp_read_global_cnt_resp(zeroExtend(cglobal_reg));
   endmethod
   method Action dtp_set_beacon_interval(Bit#(8) port_no, Bit#(32) interval);
      if (port_no < 4) begin
         beacon_interval[port_no] <= interval;
      end
   endmethod
   method Action dtp_read_beacon_interval(Bit#(8) port_no);
      if (port_no < 4) begin
         indication.dtp_read_beacon_interval_resp(port_no, beacon_interval[port_no]);
      end
   endmethod
   endinterface
endmodule