Bug-fixes and address-fixups in PLIC

Bug-fixes and address-fixups in PLIC

// Copyright (c) 2019 Bluespec, Inc.  All Rights Reserved

package PLIC;

// ================================================================
// This package implements a PLIC (Platform-Level Interrupt Controller)
// conforming to the RISC-V PLIC standard.
// It is parameterized for:
//   - # of sources
//   - # of targets
//   - # of priorities
//
// ================================================================
// Bluespec lib imports

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

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

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

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

import AXI4_Types   :: *;
import AXI4_Fabric  :: *;
import Fabric_Defs  :: *;    // for Wd_Id, Wd_Addr, Wd_Data, Wd_User

// ================================================================
// Change bitwidth without requiring < or > constraints.

function Bit #(m) changeWidth (Bit #(n) x);
   Bit #(TAdd #(m, n)) y = zeroExtend (x);
   Bit #(m)            z = y [valueOf (m)-1 : 0];
   return z;
endfunction

// ================================================================
// Maximum supported sources, targets, ...

typedef  10  T_wd_source_id;    // Max 1024 sources (source 0 is reserved for 'no interrupt')
typedef   5  T_wd_target_id;    // Max 32 targets

// ================================================================
// Interfaces

// ----------------
// Individual source interface

interface PLIC_Source_IFC;
   (* always_ready, always_enabled *)
   method Action  m_interrupt_req (Bool set_not_clear);
endinterface

// ----------------
// Individual target interface

interface PLIC_Target_IFC;
   (* always_ready *)
   method Bool  m_eip;    // external interrupt pending
endinterface

// ----------------
// PLIC interface

interface PLIC_IFC #(numeric type  t_n_sources,
		     numeric type  t_n_targets,
		     numeric type  t_max_priority);
   // 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 (Bit #(64)  addr_base, Bit #(64)  addr_lim);

   // Memory-mapped access
   interface AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) axi4_slave;

   // sources
   interface Vector #(t_n_sources, PLIC_Source_IFC)  v_sources;

   // targets EIPs (External Interrupt Pending)
   interface Vector #(t_n_targets, PLIC_Target_IFC) v_targets;
endinterface

// ================================================================
// PLIC module implementation

module mkPLIC (PLIC_IFC #(t_n_sources, t_n_targets, t_max_priority))
   provisos (Add #(_any_0, TLog #(t_n_sources), T_wd_source_id),
	     Add #(_any_1, TLog #(t_n_targets), T_wd_target_id),
	     Log #(TAdd #(t_max_priority, 1), t_wd_priority));

   Reg #(Bit #(8)) cfg_verbosity <- mkConfigReg (0);

   // Source_Ids and Priorities are read and written over the memory interface
   // and should fit within the data bus width, currently 64 bits.
   staticAssert ((valueOf (TLog #(t_n_sources))               <= 64), "PLIC: t_n_sources parameter too large");
   staticAssert ((valueOf (TLog #(TAdd #(t_max_priority, 1))) <= 64), "PLIC: t_max_priority parameter too large");

   Integer  n_sources = valueOf (t_n_sources);
   Integer  n_targets = valueOf (t_n_targets);

   // ----------------
   // Soft reset requests and responses
   FIFOF #(Bit #(0))  f_reset_reqs <- mkFIFOF;
   FIFOF #(Bit #(0))  f_reset_rsps <- mkFIFOF;

   // ----------------
   // Memory-mapped access

   // Base and limit addrs for this memory-mapped block.
   Reg #(Bit #(64))  rg_addr_base <- mkRegU;
   Reg #(Bit #(64))  rg_addr_lim  <- mkRegU;

   // Connector to AXI4 fabric
   AXI4_Slave_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave_xactor <- mkAXI4_Slave_Xactor;

   // ----------------
   // Per-interrupt source state

   // Interrupt pending from source
   Vector #(t_n_sources, Reg #(Bool))                  vrg_source_ip   <- replicateM (mkReg (False));
   // Interrupt claimed and being serviced by a hart
   Vector #(t_n_sources, Reg #(Bool))                  vrg_source_busy <- replicateM (mkReg (False));
   // Priority for this source
   Vector #(t_n_sources, Reg #(Bit #(t_wd_priority)))  vrg_source_prio <- replicateM (mkReg (0));

   // ----------------
   // Per-target hart context state

   // Threshold: interrupts at or below threshold should be masked out for target
   Vector #(t_n_targets, Reg #(Bit #(t_wd_priority)))   vrg_target_threshold <- replicateM (mkReg ('1));
   // Target has claimed interrupt for source and is servicing it
   Vector #(t_n_targets, Reg #(Bit #(TLog #(t_n_sources))))  vrg_servicing_source <- replicateM (mkReg (0));

   // ----------------
   // Per-target, per-source state

   // Interrupt enables from source to target
   Vector #(t_n_targets,
	    Vector #(t_n_sources, Reg #(Bool)))  vvrg_ie <- replicateM (replicateM (mkReg (False)));

   // ================================================================
   // Compute outputs for each target (combinational)

   function Tuple2 #(Bool, Bit #(TLog #(t_n_sources))) fn_target_output (Integer target_id);
      Bit #(TLog #(t_n_sources)) max_id = 0;
      Bit #(t_wd_priority)  prio   = 0;
      // Note: source_ids begin at 1, not 0.
      for (Integer source_id = 1; source_id < n_sources; source_id = source_id + 1)
	 if (   vrg_source_ip [source_id]
	     && (vrg_source_prio [source_id] > prio)) begin
	    max_id = fromInteger (source_id);
	    prio   = vrg_source_prio [source_id];
	 end
      // Assertion: if any interrupt is pending, prio > 0
      Bool eip = (prio > vrg_target_threshold [target_id]);
      return tuple2 (eip, max_id);
   endfunction

   // For each target: (interrupt pending, source)
   Vector #(t_n_targets,
	    Tuple2 #(Bool, Bit #(TLog #(t_n_sources))))  v_target_outputs = genWith (fn_target_output);

   // ================================================================
   // Soft reset

   rule rl_reset;
      let x <- pop (f_reset_reqs);

      for (Integer source_id = 0; source_id < n_sources; source_id = source_id + 1) begin
	 vrg_source_ip   [source_id] <= False;
	 vrg_source_busy [source_id]  <= False;
	 vrg_source_prio [source_id] <= 0;
      end

      for (Integer target_id = 0; target_id < n_targets; target_id = target_id + 1) begin
	 // Mask all interrupts with highest threshold
	 vrg_target_threshold [target_id] <= '1;
	 vrg_servicing_source [target_id] <=  0;
      end

      for (Integer target_id = 0; target_id < n_targets; target_id = target_id + 1)
	 for (Integer source_id = 0; source_id < n_sources; source_id = source_id + 1)
	    vvrg_ie [target_id][source_id] <= False;

      slave_xactor.reset;

      f_reset_rsps.enq (?);
   endrule

   // ================================================================
   // Bus interface for reading/writing control/status regs
   // Relative-address map is same as 'SiFive U54-MC Core Complex Manual v1p0'.
   // Accesses are 4-bytes wide, even though bus is 64b wide.

   // ----------------------------------------------------------------
   // Handle memory-mapped read requests

   rule rl_process_rd_req  (! f_reset_reqs.notEmpty);

      let rda <- pop_o (slave_xactor.o_rd_addr);
      if (cfg_verbosity > 1) begin
	 $display ("%0d: PLIC.rl_process_rd_req:", cur_cycle);
	 $display ("    ", fshow (rda));
      end

      let        addr_offset = rda.araddr - rg_addr_base;
      Bit #(64)  rdata       = 0;
      AXI4_Resp  rresp       = axi4_resp_okay;

      if (rda.araddr < rg_addr_base) begin
	 $display ("%0d: ERROR: PLIC.rl_process_rd_req: unrecognized addr", cur_cycle);
	 $display ("            ", fshow (rda));
	 rresp = axi4_resp_decerr;
      end

      // Source priority
      else if (addr_offset < 'h1000) begin
	 Bit #(T_wd_source_id)  source_id = truncate (addr_offset [11:2]);
	 if ((0 < source_id) && (source_id <= fromInteger (n_sources - 1)))
	    rdata = changeWidth (vrg_source_prio [source_id]);
	 else
	    rresp = axi4_resp_slverr;
      end

      // Source IPs (interrupt pending).
      // Return 32 consecutive IP bits starting with addr.
      else if (('h1000 <= addr_offset) && (addr_offset < 'h2000)) begin
	 Bit #(T_wd_source_id)  source_id_base = truncate ({ addr_offset [11:0], 5'h0 });

	 function Bool fn_ip_source_id (Integer source_id_offset);
	    let source_id = source_id_base + fromInteger (source_id_offset);
	    Bool ip_source_id = (  (source_id <= fromInteger (n_sources - 1))
				 ? vrg_source_ip [source_id]
				 : False);
	    return ip_source_id;
	 endfunction

	 if (source_id_base <= fromInteger (n_sources - 1)) begin
	    Bit #(32) v_ip = pack (genWith  (fn_ip_source_id));
	    rdata = changeWidth (v_ip);
	 end
	 else
	    rresp = axi4_resp_slverr;
      end

      // Source IEs (interrupt enables) for a target
      // Return 32 consecutive IE bits starting with addr.
      // Target 0 addrs: 2000-207F, Target 1 addrs: 2080-20FF, ...
      else if (('h2000 <= addr_offset) && (addr_offset < 'h3000)) begin
	 Bit #(T_wd_target_id)  target_id      = truncate (addr_offset [11:7]);
	 Bit #(T_wd_source_id)  source_id_base = truncate ({ addr_offset [6:0], 5'h0 });

	 function Bool fn_ie_source_id (Integer source_id_offset);
	    let source_id = fromInteger (source_id_offset) + source_id_base;
	    return (  (source_id <= fromInteger (n_sources - 1))
		    ? vvrg_ie [target_id][source_id]
		    : False);
	 endfunction

	 if (   (source_id_base <= fromInteger (n_sources - 1))
	     && (target_id      <= fromInteger (n_targets - 1))) begin
	    Bit #(32) v_ie = pack (genWith  (fn_ie_source_id));
	    rdata = changeWidth (v_ie);
	 end
	 else
	    rresp = axi4_resp_slverr;
      end

      // Target threshold
      else if ((addr_offset [31:0] & 32'hFFFF_0FFF) == 32'h0020_0000) begin
	 Bit #(T_wd_target_id)  target_id = truncate (addr_offset [20:12]);
	 if (target_id <= fromInteger (n_targets - 1))
	    rdata = changeWidth (vrg_target_threshold [target_id]);
	 else
	    rresp = axi4_resp_slverr;
      end

      // Interrupt service claim by target
      else if ((addr_offset [31:0] & 32'hFFFF_0FFF) == 32'h0020_0004) begin
	 Bit #(T_wd_target_id)  target_id  = truncate (addr_offset [20:12]);
	 match { .eip, .max_id } = v_target_outputs [target_id];
	 if (target_id <= fromInteger (n_targets - 1)) begin
	    if (vrg_servicing_source [target_id] != 0) begin
	       $display ("%0d: ERROR: PLIC: target %0d claiming without prior completion",
			 cur_cycle, target_id);
	       $display ("    Still servicing interrupt from source %0d", vrg_servicing_source [target_id]);
	       $display ("    Trying to claim service   for  source %0d", max_id);
	       $display ("    Ignoring.");
	       rresp = axi4_resp_slverr;
	    end
	    else begin
	       if (max_id != 0) begin
		  vrg_source_ip   [max_id]         <= False;
		  vrg_source_busy [max_id]         <= True;
		  vrg_servicing_source [target_id] <= truncate (max_id);
	       end
	       rdata = changeWidth (max_id);
	    end
	 end
	 else
	    rresp = axi4_resp_slverr;
      end

      else
	 rresp = axi4_resp_decerr;

      if (rresp != axi4_resp_okay) begin
	 $display ("%0d: ERROR: PLIC.rl_process_rd_req: unrecognized addr", cur_cycle);
	 $display ("            ", fshow (rda));
      end

      if (addr_offset [2:0] != 0)
	 rdata = { rdata [31:0], 32'h0 };

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

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

   // ----------------------------------------------------------------
   // Handle memory-mapped write requests

   (* descending_urgency = "rl_process_wr_req, rl_process_rd_req" *)    // ad hoc choice

   rule rl_process_wr_req (! f_reset_reqs.notEmpty);

      let wra <- pop_o (slave_xactor.o_wr_addr);
      let wrd <- pop_o (slave_xactor.o_wr_data);
      if (cfg_verbosity > 1) begin
	 $display ("%0d: PLIC.rl_process_wr_req", cur_cycle);
	 $display ("    ", fshow (wra));
	 $display ("    ", fshow (wrd));
      end

      let addr_offset  = wra.awaddr - rg_addr_base;
      let wdata32      = (((Wd_Data == 64) && ((addr_offset & 0x7 == 'h100)))
			  ? wrd_data [63:32]
			  : wrd_data [31:0])

      if (wra.awaddr < rg_addr_base) begin
	 $display ("%0d: ERROR: PLIC.rl_process_wr_req: unrecognized addr", cur_cycle);
	 $display ("            ", fshow (wra));
	 $display ("            ", fshow (wrd));
	 bresp = axi4_resp_decerr;
      end

      // Source priority
      else if (addr_offset < 'h1000) begin
	 Bit #(T_wd_source_id)  source_id = truncate (addr_offset [11:2]);
	 if ((0 < source_id) && (source_id <= fromInteger (n_sources - 1)))
	    vrg_source_prio [source_id] <= changeWidth (wdata32);
      end

      // Source IPs (interrupt pending).
      // Read-only, so ignore write; just check that addr ok.
      else if (('h1000 <= addr_offset) && (addr_offset < 'h2000)) begin
	 Bit #(T_wd_source_id)  source_id_base = truncate ({ addr_offset [11:0], 5'h0 });

	 if (! (source_id_base <= fromInteger (n_sources - 1)))
	    bresp = axi4_resp_decerr;
      end

      // Source IEs (interrupt enables) for a target
      // Write 32 consecutive IE bits starting with addr.
      // Target 0 addrs: 2000-207F, Target 1 addrs: 2080-20FF, ...
      else if (('h2000 <= addr_offset) && (addr_offset < 'h3000)) begin
	 Bit #(T_wd_target_id)  target_id      = truncate (addr_offset [11:7]);
	 Bit #(T_wd_source_id)  source_id_base = truncate ({ addr_offset [6:0], 5'h0 });

	 if (   (source_id_base <= fromInteger (n_sources - 1))
	     && (target_id      <= fromInteger (n_targets - 1))) begin
	    for (Bit #(T_wd_source_id)  k = 0; k < 32; k = k + 1) begin
	       Bit #(T_wd_source_id)  source_id = source_id_base + k;
	       if (source_id <= fromInteger (n_sources - 1))
		  vvrg_ie [target_id][source_id] <= unpack (wdata32 [k]);
	    end
	 end
	 else
	    bresp = axi4_resp_decerr;
      end

      // Target threshold
      else if ((addr_offset [31:0] & 32'hFFFF_0FFF) == 32'h0020_0000) begin
	 Bit #(T_wd_target_id)  target_id = truncate (addr_offset [20:12]);
	 if (target_id <= fromInteger (n_targets - 1))
	    vrg_target_threshold [target_id] <= changeWidth (wdata32);
	 else
	    bresp = axi4_resp_decerr;
      end

      // Interrupt service completion by target
      // Actual memory-write-data is irrelevant.
      else if ((addr_offset [31:0] & 32'hFFFF_0FFF) == 32'h0020_0004) begin
	 Bit #(T_wd_target_id)  target_id = truncate (addr_offset [20:12]);
	 Bit #(T_wd_source_id)  source_id = zeroExtend (vrg_servicing_source [target_id]);

	 if (target_id <= fromInteger (n_targets - 1)) begin
	    if (vrg_source_busy [source_id]) begin
	       vrg_source_busy [source_id] <= False;
	       vrg_servicing_source [target_id] <= 0;
	    end
	    else begin
	       $display ("%0d: ERROR: PLIC: interrupt completion to source that is not being serviced",
			 cur_cycle);
	       $display ("    Completion message from target %0d to source %0d", target_id, source_id);
	       $display ("    Ignoring");
	       bresp = axi4_resp_slverr;
	    end
	 end
	 else
	    bresp = axi4_resp_decerr;
      end

      else
	 bresp = axi4_resp_decerr;

      if (bresp != axi4_resp_okay) begin
	 $display ("%0d: ERROR: PLIC.rl_process_wr_req: unrecognized addr", cur_cycle);
	 $display ("            ", fshow (wra));
	 $display ("            ", fshow (wrd));
      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: PLIC.AXI4.rl_process_wr_req", cur_cycle);
	 $display ("            ", fshow (wra));
	 $display ("            ", fshow (wrd));
	 $display ("            ", fshow (wrr));
      end
   endrule: rl_process_wr_req

   // ================================================================
   // Creator of each source interface

   function PLIC_Source_IFC  fn_mk_PLIC_Source_IFC (Integer source_id);
      return interface PLIC_Source_IFC;
		method Action  m_interrupt_req (Bool set_not_clear);
		   action
		      if (! vrg_source_busy [source_id])
			 vrg_source_ip [source_id] <= set_not_clear;
		   endaction
		endmethod
	     endinterface;
   endfunction

   // ================================================================
   // Creator of each target interface

   function PLIC_Target_IFC  fn_mk_PLIC_Target_IFC (Integer target_id);
      return interface PLIC_Target_IFC;
		method Bool m_eip;    // external interrupt pending
		   match { .eip, .max_id } = v_target_outputs [target_id];
		   return eip;
		endmethod
	     endinterface;
   endfunction

   // ================================================================
   // 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 (Bit #(64)  addr_base, Bit #(64)  addr_lim);
      if (addr_base [1:0] != 0)
	 $display ("%0d: WARNING: PLIC.set_addr_map: addr_base 0x%0h is not 4-Byte-aligned",
		   cur_cycle, addr_base);

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

      rg_addr_base <= addr_base;
      rg_addr_lim  <= addr_lim;
   endmethod

   // Memory-mapped access
   interface  axi4_slave = slave_xactor.axi_side;

   // sources
   interface  v_sources = genWith  (fn_mk_PLIC_Source_IFC);

   // targets
   interface  v_targets = genWith  (fn_mk_PLIC_Target_IFC);
endmodule

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

endpackage

// Copyright (c) 2019 Bluespec, Inc.  All Rights Reserved

package PLIC;

// ================================================================
// This package implements a PLIC (Platform-Level Interrupt Controller)
// conforming to the RISC-V PLIC standard.
// It is parameterized for:
//   - # of sources
//   - # of targets
//   - # of priorities
//
// ================================================================
// Bluespec lib imports

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

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

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

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

import AXI4_Types   :: *;
import AXI4_Fabric  :: *;
import Fabric_Defs  :: *;    // for Wd_Id, Wd_Addr, Wd_Data, Wd_User

// ================================================================
// Change bitwidth without requiring < or > constraints.

function Bit #(m) changeWidth (Bit #(n) x);
   Bit #(TAdd #(m, n)) y = zeroExtend (x);
   Bit #(m)            z = y [valueOf (m)-1 : 0];
   return z;
endfunction

// ================================================================
// Interfaces

// ----------------
// Individual source interface

interface PLIC_Source_IFC;
   (* always_ready, always_enabled *)
   method Action  m_interrupt_req (Bool set_not_clear);
endinterface

// ----------------
// Individual target interface

interface PLIC_Target_IFC;
   (* always_ready *)
   method Bool  m_eip;    // external interrupt pending
endinterface

// ----------------
// PLIC interface

interface PLIC_IFC #(numeric type  t_n_sources,
		     numeric type  t_n_targets,
		     numeric type  t_max_priority);
   // 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 (Bit #(64)  addr_base, Bit #(64)  addr_lim);

   // Memory-mapped access
   interface AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) axi4_slave;

   // sources
   interface Vector #(t_n_sources, PLIC_Source_IFC)  v_sources;

   // targets EIPs (External Interrupt Pending)
   interface Vector #(t_n_targets, PLIC_Target_IFC) v_targets;
endinterface

// ================================================================
// PLIC module implementation

module mkPLIC (PLIC_IFC #(t_n_sources, t_n_targets, t_max_priority))
   provisos (Log #(t_n_sources,    t_wd_source_id),
	     Log #(t_max_priority, t_wd_priority));

   Reg #(Bit #(8)) cfg_verbosity <- mkConfigReg (0);

   // Source_Ids and Priorities are read and written over the memory interface
   // and should fit within the data bus width, currently 64 bits.
   staticAssert ((valueOf (t_wd_source_id) <= valueOf (64)), "PLIC: t_n_sources parameter too large");
   staticAssert ((valueOf (t_wd_priority)  <= valueOf (64)), "PLIC: t_max_priority parameter too large");

   Integer  n_sources = valueOf (t_n_sources);
   Integer  n_targets = valueOf (t_n_targets);

   // ----------------
   // Soft reset requests and responses
   FIFOF #(Bit #(0))  f_reset_reqs <- mkFIFOF;
   FIFOF #(Bit #(0))  f_reset_rsps <- mkFIFOF;

   // ----------------
   // Memory-mapped access

   // Base and limit addrs for this memory-mapped block.
   Reg #(Bit #(64))  rg_addr_base <- mkRegU;
   Reg #(Bit #(64))  rg_addr_lim  <- mkRegU;

   // Connector to AXI4 fabric
   AXI4_Slave_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave_xactor <- mkAXI4_Slave_Xactor;

   // ----------------
   // Per-interrupt source state

   // Interrupt pending from source
   Vector #(t_n_sources, Reg #(Bool))                  vrg_source_ip   <- replicateM (mkReg (False));
   // Interrupt claimed and being serviced by a hart
   Vector #(t_n_sources, Reg #(Bool))                  vrg_source_busy <- replicateM (mkReg (False));
   // Priority for this source
   Vector #(t_n_sources, Reg #(Bit #(t_wd_priority)))  vrg_source_prio <- replicateM (mkReg (0));

   // ----------------
   // Per-target hart context state

   // Threshold: interrupts at or below threshold should be masked out for target
   Vector #(t_n_targets, Reg #(Bit #(t_wd_priority)))  vrg_target_threshold <- replicateM (mkReg ('1));
   // Target has claimed interrupt for source and is servicing it
   Vector #(t_n_targets, Reg #(Bit #(t_wd_source_id))) vrg_servicing_source <- replicateM (mkReg (0));

   // ----------------
   // Per-target, per-source state

   // Interrupt enables from source to target
   Vector #(t_n_targets,
	    Vector #(t_n_sources, Reg #(Bool)))  vvrg_ie <- replicateM (replicateM (mkReg (False)));

   // ================================================================
   // Compute outputs for each target (combinational)

   function Tuple2 #(Bool, Bit #(t_wd_source_id)) fn_target_output (Integer target_id);
      Bit #(t_wd_source_id) max_id = 0;
      Bit #(t_wd_priority)  prio   = 0;
      // Note: source_ids begin at 1, not 0.
      for (Integer source_id = 1; source_id < n_sources; source_id = source_id + 1)
	 if (   vrg_source_ip [source_id]
	     && (vrg_source_prio [source_id] > prio)) begin
	    max_id = fromInteger (source_id);
	    prio   = vrg_source_prio [source_id];
	 end
      // Assertion: if any interrupt is pending, prio > 0
      Bool eip = (prio > vrg_target_threshold [target_id]);
      return tuple2 (eip, max_id);
   endfunction

   // For each target: (interrupt pending, source)
   Vector #(t_n_targets,
	    Tuple2 #(Bool, Bit #(t_wd_source_id)))  v_target_outputs = genWith (fn_target_output);

   // ================================================================
   // Soft reset

   rule rl_reset;
      let x <- pop (f_reset_reqs);

      for (Integer source_id = 0; source_id < n_sources; source_id = source_id + 1) begin
	 vrg_source_ip   [source_id] <= False;
	 vrg_source_busy [source_id]  <= False;
	 vrg_source_prio [source_id] <= 0;
      end

      for (Integer target_id = 0; target_id < n_targets; target_id = target_id + 1) begin
	 // Mask all interrupts with highest threshold
	 vrg_target_threshold [target_id] <= '1;
	 vrg_servicing_source [target_id] <=  0;
      end

      for (Integer target_id = 0; target_id < n_targets; target_id = target_id + 1)
	 for (Integer source_id = 0; source_id < n_sources; source_id = source_id + 1)
	    vvrg_ie [target_id][source_id] <= False;

      slave_xactor.reset;

      f_reset_rsps.enq (?);
   endrule

   // ================================================================
   // Bus interface for reading/writing control/status regs
   // Relative-address map is same as 'SiFive U54-MC Core Complex Manual v1p0'.
   // Accesses are 4-bytes wide, even though bus is 64b wide.

   // ----------------------------------------------------------------
   // Handle memory-mapped read requests

   rule rl_process_rd_req  (! f_reset_reqs.notEmpty);

      let rda <- pop_o (slave_xactor.o_rd_addr);
      if (cfg_verbosity > 1) begin
	 $display ("%0d: PLIC.rl_process_rd_req:", cur_cycle);
	 $display ("    ", fshow (rda));
      end

      let        addr_offset = rda.araddr - rg_addr_base;
      Bit #(64)  rdata       = 0;
      AXI4_Resp  rresp       = axi4_resp_okay;

      if (rda.araddr < rg_addr_base) begin
	 $display ("%0d: ERROR: PLIC.rl_process_rd_req: unrecognized addr", cur_cycle);
	 $display ("            ", fshow (rda));
	 rresp = axi4_resp_decerr;
      end

      // Source priority
      else if (addr_offset < 'h1000) begin
	 Bit #(10) source_id = addr_offset [11:2];
	 if ((0 < source_id) && (source_id < fromInteger (n_sources)))
	    rdata = changeWidth (vrg_source_prio [source_id]);
      end

      // Source IPs (interrupt pending).
      // Return 32 consecutive IP bits starting with addr.
      else if (('h1000 <= addr_offset) && (addr_offset < 'h2000)) begin
	 Bit #(15) source_id_base = { addr_offset [11:0], 3'h0 };

	 function Bool fn_ip_source_id (Integer source_id_offset);
	    let source_id = source_id_base + fromInteger (source_id_offset);
	    Bool ip_source_id = False;
	    if (source_id < fromInteger (n_sources))
	       ip_source_id = vrg_source_ip [source_id];
	    return ip_source_id;
	 endfunction

	 if (source_id_base < fromInteger (n_sources)) begin
	    Bit #(32) v_ip = pack (genWith  (fn_ip_source_id));
	    rdata = changeWidth (v_ip);
	 end
      end

      // Source IEs (interrupt enables) for a target
      // Return 32 consecutive IE bits starting with addr.
      // Target 0 addrs: 2000-207F, Target 1 addrs: 2080-20FF, ...
      else if (('h2000 <= addr_offset) && (addr_offset < 'h3000)) begin
	 Bit #(5)  target_id      = addr_offset [11:7];
	 Bit #(10) source_id_base = { addr_offset [6:0], 3'h0 };

	 function Bool fn_ie_source_id (Integer source_id_offset);
	    let source_id = fromInteger (source_id_offset) + source_id_base;
	    return (  (source_id < fromInteger (n_sources))
		    ? vvrg_ie [target_id][source_id]
		    : False);
	 endfunction

	 if (   (source_id_base < fromInteger (n_sources))
	     && (target_id      < fromInteger (n_targets))) begin
	    Bit #(32) v_ie = pack (genWith  (fn_ie_source_id));
	    rdata = changeWidth (v_ie);
	 end
      end

      // Target threshold
      else if ((addr_offset [31:0] & 32'hFFFF_0FFF) == 32'h0020_0000) begin
	 Bit #(4) target_id = addr_offset [15:12];
	 if (target_id < fromInteger (n_targets))
	    rdata = changeWidth (vrg_target_threshold [target_id]);
      end

      // Interrupt service claim by target
      else if ((addr_offset [31:0] & 32'hFFFF_0FFF) == 32'h0020_0004) begin
	 Bit #(4) target_id  = addr_offset [15:12];
	 match { .eip, .max_id } = v_target_outputs [target_id];
	 if (target_id < fromInteger (n_targets)) begin
	    if (vrg_servicing_source [target_id] != 0) begin
	       $display ("%0d: ERROR: PLIC: target %0d claiming without prior completion",
			 cur_cycle, target_id);
	       $display ("    Still servicing interrupt from source %0d", vrg_servicing_source [target_id]);
	       $display ("    Trying to claim service   for  source %0d", max_id);
	       $display ("    Ignoring.");
	       rresp = axi4_resp_slverr;
	    end
	    else begin
	       if (max_id != 0) begin
		  vrg_source_ip   [max_id]         <= False;
		  vrg_source_busy [max_id]         <= True;
		  vrg_servicing_source [target_id] <= max_id;
	       end
	       rdata = changeWidth (max_id);
	    end
	 end
      end

      else
	 rresp = axi4_resp_decerr;

      if (rresp != axi4_resp_okay) begin
	 $display ("%0d: ERROR: PLIC.rl_process_rd_req: unrecognized addr", cur_cycle);
	 $display ("            ", fshow (rda));
      end

      if (addr_offset [2:0] != 0)
	 rdata = { rdata [31:0], 32'h0 };

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

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

   // ----------------------------------------------------------------
   // Handle memory-mapped write requests

   (* descending_urgency = "rl_process_wr_req, rl_process_rd_req" *)    // ad hoc choice

   rule rl_process_wr_req (! f_reset_reqs.notEmpty);

      let wra <- pop_o (slave_xactor.o_wr_addr);
      let wrd <- pop_o (slave_xactor.o_wr_data);
      if (cfg_verbosity > 1) begin
	 $display ("%0d: PLIC.rl_process_wr_req", cur_cycle);
	 $display ("    ", fshow (wra));
	 $display ("    ", fshow (wrd));
      end

      let addr_offset  = wra.awaddr - rg_addr_base;
      let wdata32      = (((addr_offset & 'h7) == 0) ? wrd.wdata [31:0] : wrd.wdata [63:32]);
      AXI4_Resp  bresp = axi4_resp_okay;

      if (wra.awaddr < rg_addr_base) begin
	 $display ("%0d: ERROR: PLIC.rl_process_wr_req: unrecognized addr", cur_cycle);
	 $display ("            ", fshow (wra));
	 $display ("            ", fshow (wrd));
	 bresp = axi4_resp_decerr;
      end

      // Source priority
      else if (addr_offset < 'h1000) begin
	 Bit #(10) source_id = addr_offset [11:2];
	 if ((0 < source_id) && (source_id < fromInteger (n_sources)))
	    vrg_source_prio [source_id] <= changeWidth (wdata32);
      end

      // Source IPs (interrupt pending).
      // Read-only, so ignore write; just check that addr ok.
      else if (('h1000 <= addr_offset) && (addr_offset < 'h2000)) begin
	 Bit #(15) source_id_base = { addr_offset [11:0], 3'h0 };

	 if (source_id_base >= fromInteger (n_sources))
	    bresp = axi4_resp_decerr;
      end

      // Source IEs (interrupt enables) for a target
      // Write 32 consecutive IE bits starting with addr.
      // Target 0 addrs: 2000-207F, Target 1 addrs: 2080-20FF, ...
      else if (('h2000 <= addr_offset) && (addr_offset < 'h3000)) begin
	 Bit #(5)  target_id      = addr_offset [11:7];
	 Bit #(10) source_id_base = { addr_offset [6:0], 3'h0 };

	 if (   (source_id_base < fromInteger (n_sources))
	     && (target_id      < fromInteger (n_targets))) begin
	    for (Bit #(10) k = 0; k < 32; k = k + 1) begin
	       Bit #(10) source_id = source_id_base + k;
	       if (source_id < fromInteger (n_sources))
		  vvrg_ie [target_id][source_id] <= unpack (wdata32 [k]);
	    end
	 end
      end

      // Target threshold
      else if ((addr_offset [31:0] & 32'hFFFF_0FFF) == 32'h0020_0000) begin
	 Bit #(4) target_id = addr_offset [15:12];
	 if (target_id < fromInteger (n_targets))
	    vrg_target_threshold [target_id] <= changeWidth (wdata32);
      end

      // Interrupt service completion by target
      // Actual memory-write-data is irrelevant.
      else if ((addr_offset [31:0] & 32'hFFFF_0FFF) == 32'h0020_0004) begin
	 Bit #(4)               target_id = addr_offset [15:12];
	 Bit #(t_wd_source_id)  source_id = vrg_servicing_source [target_id];

	 if (target_id < fromInteger (n_targets)) begin
	    if (vrg_source_busy [source_id]) begin
	       vrg_source_busy [source_id] <= False;
	       vrg_servicing_source [target_id] <= 0;
	    end
	    else begin
	       $display ("%0d: ERROR: PLIC: interrupt completion to source that is not being serviced",
			 cur_cycle);
	       $display ("    Completion message from target %0d to source %0d", target_id, source_id);
	       $display ("    Ignoring");
	       bresp = axi4_resp_slverr;
	    end
	 end
      end

      else
	 bresp = axi4_resp_decerr;

      if (bresp != axi4_resp_okay) begin
	 $display ("%0d: ERROR: PLIC.rl_process_wr_req: unrecognized addr", cur_cycle);
	 $display ("            ", fshow (wra));
	 $display ("            ", fshow (wrd));
      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: PLIC.AXI4.rl_process_wr_req", cur_cycle);
	 $display ("            ", fshow (wra));
	 $display ("            ", fshow (wrd));
	 $display ("            ", fshow (wrr));
      end
   endrule: rl_process_wr_req

   // ================================================================
   // Creator of each source interface

   function PLIC_Source_IFC  fn_mk_PLIC_Source_IFC (Integer source_id);
      return interface PLIC_Source_IFC;
		method Action  m_interrupt_req (Bool set_not_clear);
		   action
		      if (! vrg_source_busy [source_id])
			 vrg_source_ip [source_id] <= set_not_clear;
		   endaction
		endmethod
	     endinterface;
   endfunction

   // ================================================================
   // Creator of each target interface

   function PLIC_Target_IFC  fn_mk_PLIC_Target_IFC (Integer target_id);
      return interface PLIC_Target_IFC;
		method Bool m_eip;    // external interrupt pending
		   match { .eip, .max_id } = v_target_outputs [target_id];
		   return eip;
		endmethod
	     endinterface;
   endfunction

   // ================================================================
   // 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 (Bit #(64)  addr_base, Bit #(64)  addr_lim);
      if (addr_base [1:0] != 0)
	 $display ("%0d: WARNING: PLIC.set_addr_map: addr_base 0x%0h is not 4-Byte-aligned",
		   cur_cycle, addr_base);

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

      rg_addr_base <= addr_base;
      rg_addr_lim  <= addr_lim;
   endmethod

   // Memory-mapped access
   interface  axi4_slave = slave_xactor.axi_side;

   // sources
   interface  v_sources = genWith  (fn_mk_PLIC_Source_IFC);

   // targets
   interface  v_targets = genWith  (fn_mk_PLIC_Target_IFC);
endmodule

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

endpackage


fixed ifdef to ifndef

fixed ifdef to ifndef

// 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.

package AlteraEthPhy;

import Clocks::*;
import Vector::*;
import FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import Connectable::*;
import GetPut::*;
import Pipe::*;
`ifdef NUMBER_OF_ALTERA_PORTS
import Altera10GPhy3::*;
`else
import ALTERA_ETH_10GBASER_WRAPPER::*;
`endif

`ifdef NUMBER_OF_ALTERA_PORTS
typedef `NUMBER_OF_ALTERA_PORTS NumPorts;
`else
typedef 4 NumPorts;
`endif

interface EthPhyIfc;
   (*always_ready, always_enabled*)
   interface Vector#(NumPorts, Put#(Bit#(72)))  tx;
   (*always_ready, always_enabled*)
   interface Vector#(NumPorts, Get#(Bit#(72))) rx;
   (*always_ready, always_enabled*)
   method Vector#(NumPorts, Bit#(1)) serial_tx;
   (*always_ready, always_enabled*)
   method Action serial_rx(Vector#(NumPorts, Bit#(1)) v);
   interface Clock rx_clkout;
endinterface

(* synthesize *)
module mkAlteraEthPhy#(Clock clk_50, Clock clk_644, Clock clk_xgmii, Reset rst_50)(EthPhyIfc);
   Vector#(NumPorts, FIFOF#(Bit#(72))) txFifo = newVector;
   Vector#(NumPorts, FIFOF#(Bit#(72))) rxFifo = newVector;
   Clock defaultClock <- exposeCurrentClock;
   Reset defaultReset <- exposeCurrentReset;
   Reset invertedReset <- mkResetInverter(rst_50, clocked_by defaultClock);

   Eth10GPhyWrap phy <- mkEth10GPhyWrap(clk_50, clk_644, clk_xgmii, invertedReset);
   Reset xgmii_reset <- mkAsyncReset(2, defaultReset, phy.xgmii_rx_clk);

   for (Integer i=0; i<valueOf(NumPorts); i=i+1) begin
      txFifo[i] <- mkUGFIFOF(clocked_by clk_xgmii, reset_by noReset);
      rule tx_mac;
         let v <- toGet(txFifo[i]).get;
         case (i)
           0: phy.xgmii_tx.dc_0(v);
           1: phy.xgmii_tx.dc_1(v);
           2: phy.xgmii_tx.dc_2(v);
`ifndef NUMBER_OF_ALTERA_PORTS
           3: phy.xgmii_tx.dc_3(v);
`endif
         endcase
      endrule
   end

   for (Integer i=0; i<valueOf(NumPorts); i=i+1) begin
      rxFifo[i] <- mkUGFIFOF(clocked_by phy.xgmii_rx_clk, reset_by noReset);
      rule rx_mac;
         case(i)
            0: rxFifo[0].enq(phy.xgmii_rx.dc_0);
            1: rxFifo[1].enq(phy.xgmii_rx.dc_1);
            2: rxFifo[2].enq(phy.xgmii_rx.dc_2);
`ifndef NUMBER_OF_ALTERA_PORTS
            3: rxFifo[3].enq(phy.xgmii_rx.dc_3);
`endif
         endcase
      endrule
   end

   Vector#(NumPorts, Wire#(Bit#(1))) tx_serial <- replicateM(mkDWire(0));
   rule tx_serial0;
      tx_serial[0] <= phy.tx_serial.data_0;
   endrule
   rule tx_serial1;
      tx_serial[1] <= phy.tx_serial.data_1;
   endrule
   rule tx_serial2;
      tx_serial[2] <= phy.tx_serial.data_2;
   endrule
`ifndef NUMBER_OF_ALTERA_PORTS
   rule tx_serial3;
      tx_serial[3] <= phy.tx_serial.data_3;
   endrule
`endif

   Vector#(NumPorts, Wire#(Bit#(1))) rx_serial_wire <- replicateM(mkDWire(0));

   rule rx_serial0;
      phy.rx_serial.data_0(rx_serial_wire[0]);
   endrule
   rule rx_serial1;
      phy.rx_serial.data_1(rx_serial_wire[1]);
   endrule
   rule rx_serial2;
      phy.rx_serial.data_2(rx_serial_wire[2]);
   endrule
`ifndef NUMBER_OF_ALTERA_PORTS
   rule rx_serial3;
      phy.rx_serial.data_3(rx_serial_wire[3]);
   endrule
`endif

   interface tx = map(toPut, txFifo);
   interface rx = map(toGet, rxFifo);
   method serial_tx = readVReg(tx_serial);
   method serial_rx = writeVReg(rx_serial_wire);
   interface rx_clkout = phy.xgmii_rx_clk;
endmodule

endpackage

// 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.

package AlteraEthPhy;

import Clocks::*;
import Vector::*;
import FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import Connectable::*;
import GetPut::*;
import Pipe::*;
`ifdef NUMBER_OF_ALTERA_PORTS
import Altera10GPhy3::*;
`else
import ALTERA_ETH_10GBASER_WRAPPER::*;
`endif

`ifdef NUMBER_OF_ALTERA_PORTS
typedef `NUMBER_OF_ALTERA_PORTS NumPorts;
`else
typedef 4 NumPorts;
`endif

interface EthPhyIfc;
   (*always_ready, always_enabled*)
   interface Vector#(NumPorts, Put#(Bit#(72)))  tx;
   (*always_ready, always_enabled*)
   interface Vector#(NumPorts, Get#(Bit#(72))) rx;
   (*always_ready, always_enabled*)
   method Vector#(NumPorts, Bit#(1)) serial_tx;
   (*always_ready, always_enabled*)
   method Action serial_rx(Vector#(NumPorts, Bit#(1)) v);
   interface Clock rx_clkout;
endinterface

(* synthesize *)
module mkAlteraEthPhy#(Clock clk_50, Clock clk_644, Clock clk_xgmii, Reset rst_50)(EthPhyIfc);
   Vector#(NumPorts, FIFOF#(Bit#(72))) txFifo = newVector;
   Vector#(NumPorts, FIFOF#(Bit#(72))) rxFifo = newVector;
   Clock defaultClock <- exposeCurrentClock;
   Reset defaultReset <- exposeCurrentReset;
   Reset invertedReset <- mkResetInverter(rst_50, clocked_by defaultClock);

   Eth10GPhyWrap phy <- mkEth10GPhyWrap(clk_50, clk_644, clk_xgmii, invertedReset);
   Reset xgmii_reset <- mkAsyncReset(2, defaultReset, phy.xgmii_rx_clk);

   for (Integer i=0; i<valueOf(NumPorts); i=i+1) begin
      txFifo[i] <- mkUGFIFOF(clocked_by clk_xgmii, reset_by noReset);
      rule tx_mac;
         let v <- toGet(txFifo[i]).get;
         case (i)
           0: phy.xgmii_tx.dc_0(v);
           1: phy.xgmii_tx.dc_1(v);
           2: phy.xgmii_tx.dc_2(v);
`ifdef NUMBER_OF_ALTERA_PORTS
           3: phy.xgmii_tx.dc_3(v);
`endif
         endcase
      endrule
   end

   for (Integer i=0; i<valueOf(NumPorts); i=i+1) begin
      rxFifo[i] <- mkUGFIFOF(clocked_by phy.xgmii_rx_clk, reset_by noReset);
      rule rx_mac;
         case(i)
            0: rxFifo[0].enq(phy.xgmii_rx.dc_0);
            1: rxFifo[1].enq(phy.xgmii_rx.dc_1);
            2: rxFifo[2].enq(phy.xgmii_rx.dc_2);
`ifdef NUMBER_OF_ALTERA_PORTS
            3: rxFifo[3].enq(phy.xgmii_rx.dc_3);
`endif
         endcase
      endrule
   end

   Vector#(NumPorts, Wire#(Bit#(1))) tx_serial <- replicateM(mkDWire(0));
   rule tx_serial0;
      tx_serial[0] <= phy.tx_serial.data_0;
   endrule
   rule tx_serial1;
      tx_serial[1] <= phy.tx_serial.data_1;
   endrule
   rule tx_serial2;
      tx_serial[2] <= phy.tx_serial.data_2;
   endrule
`ifdef NUMBER_OF_ALTERA_PORTS
   rule tx_serial3;
      tx_serial[3] <= phy.tx_serial.data_3;
   endrule
`endif

   Vector#(NumPorts, Wire#(Bit#(1))) rx_serial_wire <- replicateM(mkDWire(0));

   rule rx_serial0;
      phy.rx_serial.data_0(rx_serial_wire[0]);
   endrule
   rule rx_serial1;
      phy.rx_serial.data_1(rx_serial_wire[1]);
   endrule
   rule rx_serial2;
      phy.rx_serial.data_2(rx_serial_wire[2]);
   endrule
`ifdef NUMBER_OF_ALTERA_PORTS
   rule rx_serial3;
      phy.rx_serial.data_3(rx_serial_wire[3]);
   endrule
`endif

   interface tx = map(toPut, txFifo);
   interface rx = map(toGet, rxFifo);
   method serial_tx = readVReg(tx_serial);
   method serial_rx = writeVReg(rx_serial_wire);
   interface rx_clkout = phy.xgmii_rx_clk;
endmodule

endpackage


some debug code left in by accident

some debug code left in by accident

// 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.

// BSV Libraries
import BRAMFIFO::*;
import FIFO::*;
import FIFOF::*;
import Vector::*;
import GetPut::*;
import ClientServer::*;
import Assert::*;

// XBSV Libraries
import MemTypes::*;
import PortalMemory::*;
import SGList::*;

typedef 9 SGL_PIPELINE_DEPTH;
typedef 32 TAG_DEPTH;

interface MemWriteInternal#(numeric type addrWidth, numeric type dataWidth);
   interface DmaDbg dbg;
   interface MemWriteClient#(addrWidth,dataWidth) write_client;
endinterface

interface MemReadInternal#(numeric type addrWidth, numeric type dataWidth);
   interface DmaDbg dbg;
   interface MemReadClient#(addrWidth,dataWidth) read_client;
endinterface

function Bool bad_pointer(ObjectPointer p);
   return (p > fromInteger(valueOf(MaxNumSGLists)) || p == 0);
endfunction

interface TagGen#(numeric type numClients, numeric type numTags);
   method Action tag_request(Bit#(TLog#(numClients)) client, Bit#(6) orig_tag);
   method ActionValue#(Bit#(TLog#(numTags)))  tag_response;
   method Action return_tag(Bit#(TLog#(numTags)) tag);
endinterface

module mkTagGenOO(TagGen#(numClients,numTags))
   provisos(Log#(numTags,tagWidth),
	    Log#(numClients,clientWidth));

   let request_fifo0 <- mkSizedFIFOF(valueOf(SGL_PIPELINE_DEPTH));
   let request_fifo1 <- mkSizedFIFOF(valueOf(SGL_PIPELINE_DEPTH));
   let return_fifo <- mkFIFO;
   Vector#(numTags, Reg#(Bit#(TLog#(TAG_DEPTH)))) tag_regs <- replicateM(mkReg(0));
   Vector#(numTags, Reg#(Maybe#(Tuple2#(Bit#(clientWidth),Bit#(6))))) client_map <- replicateM(mkReg(tagged Invalid));
   Maybe#(UInt#(tagWidth)) next_free = findElem(0, readVReg(tag_regs));

   rule return_tag_rule;
      return_fifo.deq;
      let tag = return_fifo.first;
      tag_regs[tag] <= tag_regs[tag]-1;
      if (tag_regs[tag]==1)
	 client_map[tag] <= tagged Invalid;
   endrule

   rule tag_request_rule;
      request_fifo0.deq;
      let client = tpl_2(request_fifo0.first);
      let orig_tag = tpl_3(request_fifo0.first);
      let rv = case  (tpl_1(request_fifo0.first)) matches
		  tagged Valid .tag: return (_when_(tag_regs[tag] < maxBound) (tag));
		  tagged Invalid: return (_when_(isValid(next_free)) (fromMaybe(?, next_free)));
	       endcase;
      request_fifo1.enq(tuple3(rv,client,orig_tag));
   endrule

   method Action tag_request(Bit#(clientWidth) client, Bit#(6) orig_tag) if (request_fifo0.notFull);
      let rv = findElem(tagged Valid tuple2(client,orig_tag), readVReg(client_map));
      request_fifo0.enq(tuple3(rv,client,orig_tag));
   endmethod

   method ActionValue#(Bit#(tagWidth)) tag_response;
      request_fifo1.deq;
      let rv = tpl_1(request_fifo1.first);
      let client = tpl_2(request_fifo1.first);
      let orig_tag = tpl_3(request_fifo1.first);
      client_map[rv] <= tagged Valid tuple2(client,orig_tag);
      tag_regs[rv] <= tag_regs[rv]+1;
      return extend(pack(rv));
   endmethod

   method Action return_tag(Bit#(tagWidth) tag);
      return_fifo.enq(tag);
   endmethod

endmodule

module mkTagGenIO(TagGen#(numClients,numTags))
   provisos(Log#(numTags,tagWidth),
	    Log#(numClients,clientWidth),
	    Bits#(Bit#(clientWidth), tagWidth));

   let request_fifo <- mkSizedFIFO(valueOf(SGL_PIPELINE_DEPTH));

   method Action tag_request(Bit#(clientWidth) client, Bit#(6) client_tag);
      request_fifo.enq(client);
   endmethod

   method ActionValue#(Bit#(tagWidth)) tag_response;
      request_fifo.deq;
      let client = request_fifo.first;
      return pack(client);
   endmethod

   method Action return_tag(Bit#(tagWidth) tag);
      noAction;
   endmethod

endmodule

typedef struct {ObjectRequest req;
		Bit#(TLog#(numClients)) client; } LRec#(numeric type numClients, numeric type numTags, numeric type addrWidth) deriving(Bits);

typedef struct {ObjectRequest req;
		Bit#(addrWidth) pa;
		Bit#(TLog#(numTags)) rename_tag;
		Bit#(TLog#(numClients)) client; } RRec#(numeric type numClients, numeric type numTags, numeric type addrWidth) deriving(Bits);

typedef struct {ObjectRequest req;
		Bit#(TLog#(numTags)) rename_tag;
		Bit#(TLog#(numClients)) client; } DRec#(numeric type numClients, numeric type numTags, numeric type addrWidth) deriving(Bits);

typedef struct {Bit#(6) orig_tag;
		Bit#(TLog#(numClients)) client; } RResp#(numeric type numClients, numeric type numTags, numeric type addrWidth) deriving(Bits);

module mkMemReadInternal#(Integer id,
			  Vector#(numClients, ObjectReadClient#(dataWidth)) readClients,
			  DmaIndication dmaIndication,
			  Server#(Tuple2#(SGListId,Bit#(ObjectOffsetSize)),Bit#(addrWidth)) sgl,
			  TagGen#(numClients, numTags) tag_gen)
   (MemReadInternal#(addrWidth, dataWidth))

   provisos(Add#(b__, addrWidth, 64),
	    Add#(c__, 12, addrWidth),
	    Add#(1, c__, d__),
	    Div#(dataWidth,8,dataWidthBytes),
	    Mul#(dataWidthBytes,8,dataWidth),
	    Log#(dataWidthBytes,beatShift),
	    Add#(a__, TLog#(numTags), 6));

   // stage 0: address translation (latency = SGL_PIPELINE_DEPTH)
   FIFO#(LRec#(numClients,numTags,addrWidth)) lreqFifo <- mkSizedFIFO(valueOf(SGL_PIPELINE_DEPTH));
   // stage 1: address validation (latency = 1)
   FIFO#(RRec#(numClients,numTags,addrWidth))  reqFifo <- mkFIFO;
   // stage 2: read commands (maximum buffering to handle high latency read response times)
   Vector#(numTags, FIFOF#(DRec#(numClients,numTags,addrWidth))) dreqFifos <- replicateM(mkSizedFIFOF(valueOf(TAG_DEPTH)));
   // stage 3: read data (minimal buffering required)
   FIFO#(Tuple2#(DRec#(numClients,numTags,addrWidth),MemData#(dataWidth))) readDataPipelineFifo <- mkFIFO;
   FIFO#(MemData#(dataWidth)) responseFifo <- mkFIFO;
   Vector#(numTags, Reg#(Bit#(8)))           burstRegs <- replicateM(mkReg(0));
   Reg#(Bit#(64))  beatCount <- mkReg(0);
   let beat_shift = fromInteger(valueOf(beatShift));

   // performance analytics
   Reg#(Bit#(64)) cycle_cnt <- mkReg(0);
   Reg#(Bit#(64)) last_loadClient <- mkReg(0);
   Reg#(Bit#(64)) last_sglResp <- mkReg(0);
   Reg#(Bit#(64)) last_eob <- mkReg(0);
   (* fire_when_enabled *)
   rule cycle;
      cycle_cnt <= cycle_cnt+1;
   endrule

   for (Integer selectReg = 0; selectReg < valueOf(numClients); selectReg = selectReg + 1)
      rule loadClient;
      	 //$display("mkMemReadInternal::loadClient %d %d", selectReg, cycle_cnt-last_loadClient);
	 //last_loadClient <= cycle_cnt;
   	 ObjectRequest req <- readClients[selectReg].readReq.get();
   	 if (bad_pointer(req.pointer))
   	    dmaIndication.badPointer(req.pointer);
   	 else begin
	    tag_gen.tag_request(fromInteger(selectReg), req.tag);
   	    lreqFifo.enq(LRec{req:req, client:fromInteger(selectReg)});
   	    sgl.request.put(tuple2(truncate(req.pointer),req.offset));
   	 end
      endrule

   rule checkSglResp;
      let physAddr <- sgl.response.get;
      let req = lreqFifo.first.req;
      let client = lreqFifo.first.client;
      let rename_tag <- tag_gen.tag_response;
      lreqFifo.deq();
      if (physAddr <= (1 << valueOf(SGListPageShift0))) begin
	 // squash request
	 $display("dmaRead: badAddr pointer=%d offset=%h physAddr=%h", req.pointer, req.offset, physAddr);
	 dmaIndication.badAddr(req.pointer, extend(req.offset), extend(physAddr));
	 tag_gen.return_tag(rename_tag);
      end
      else begin
	 if (False && physAddr[31:24] != 0)
	    $display("checkSglResp: funny physAddr req.pointer=%d req.offset=%h physAddr=%h", req.pointer, req.offset, physAddr);
	 reqFifo.enq(RRec{req:req, pa:physAddr, client:client, rename_tag:extend(rename_tag)});
	 //$display("checkSglResp: client=%d, rename_tag=%d", client,rename_tag);
      end
      //$display("mkMemReadInternal::sglResp %d %d", client, cycle_cnt-last_sglResp);
      //last_sglResp <= cycle_cnt;
   endrule

   rule readDataComp;
      readDataPipelineFifo.deq;
      let drq = tpl_1(readDataPipelineFifo.first);
      let response = tpl_2(readDataPipelineFifo.first);
      let client = drq.client;
      let req = drq.req;
      readClients[client].readData.put(ObjectData { data: response.data, tag: req.tag, last: False});
      //$display("readDataComp: %d %h", client, response.data);
      beatCount <= beatCount+1;
   endrule

   rule read_client_response;
      let response <- toGet(responseFifo).get();
      Bit#(6) response_tag = response.tag;
      let dreqFifo = dreqFifos[response_tag];
      dynamicAssert(truncate(response_tag) == dreqFifo.first.rename_tag, "mkMemReadInternal");
      readDataPipelineFifo.enq(tuple2(dreqFifo.first, response));
      let burstLen = burstRegs[response_tag];
      if (burstLen == 0)
	 burstLen = dreqFifo.first.req.burstLen >> beat_shift;
      if (burstLen == 1) begin
	 //$display("mkMemReadInternal::eob %d", cycle_cnt-last_eob);
	 last_eob <= cycle_cnt;
	 dreqFifo.deq();
	 tag_gen.return_tag(truncate(response_tag));
      end
      burstRegs[response_tag] <= burstLen-1;
   endrule

   interface MemReadClient read_client;
      interface Get readReq;
	 method ActionValue#(MemRequest#(addrWidth)) get();
	    reqFifo.deq;
	    let req = reqFifo.first.req;
	    let physAddr = reqFifo.first.pa;
	    let client = reqFifo.first.client;
	    let rename_tag = reqFifo.first.rename_tag;
	    if (False && physAddr[31:24] != 0)
	       $display("req_ar: funny physAddr req.pointer=%d req.offset=%h physAddr=%h", req.pointer, req.offset, physAddr);
	    dreqFifos[rename_tag].enq(DRec{req:req, client:client, rename_tag:rename_tag});
	    //$display("readReq: client=%d, rename_tag=%d, physAddr=%h", client,rename_tag,physAddr);
	    return MemRequest{addr:physAddr, burstLen:req.burstLen, tag:extend(rename_tag)};
	 endmethod
      endinterface
      interface Put readData = toPut(responseFifo);
   endinterface
   interface DmaDbg dbg;
      method ActionValue#(DmaDbgRec) dbg();
	 // Bit#(1) ne0 = pack(dreqFifos[0].notEmpty);
	 // Bit#(1) ne1 = pack(dreqFifos[1].notEmpty);
	 // return DmaDbgRec{x:extend(burstRegs[0]), y:extend(burstRegs[1]), z:extend(ne0), w:extend(ne1)};
	 return DmaDbgRec{x:0, y:0, z:0, w:0};
      endmethod
      method ActionValue#(Bit#(64)) getMemoryTraffic();
	 return beatCount;
      endmethod
   endinterface
endmodule

module mkMemWriteInternal#(Integer iid,
			   Vector#(numClients, ObjectWriteClient#(dataWidth)) writeClients,
			   DmaIndication dmaIndication,
			   Server#(Tuple2#(SGListId,Bit#(ObjectOffsetSize)),Bit#(addrWidth)) sgl,
			   TagGen#(numClients, numTags) tag_gen)
   (MemWriteInternal#(addrWidth, dataWidth))

   provisos(Add#(b__, addrWidth, 64),
	    Add#(c__, 12, addrWidth),
	    Add#(1, c__, d__),
	    Div#(dataWidth,8,dataWidthBytes),
	    Mul#(dataWidthBytes,8,dataWidth),
	    Log#(dataWidthBytes,beatShift),
	    Add#(a__, TLog#(numTags), 6));

   // stage 0: address translation (latency = SGL_PIPELINE_DEPTH)
   FIFO#(LRec#(numClients,numTags,addrWidth)) lreqFifo <- mkSizedFIFO(valueOf(SGL_PIPELINE_DEPTH));
   // stage 1: address validation (latency = 1)
   FIFO#(RRec#(numClients,numTags,addrWidth))  reqFifo <- mkFIFO;
   // stage 2: write commands (maximum buffering to handle high latency writes)
   FIFO#(DRec#(numClients,numTags,addrWidth)) dreqFifo <- mkSizedBRAMFIFO(valueOf(TMul#(TAG_DEPTH,numTags)));
   // stage 3: write data (maximum buffering, though I have no idea if any hosts will begin the next data transfer before sending the write ack)
   Vector#(numTags, FIFO#(RResp#(numClients,numTags,addrWidth))) respFifos <- replicateM(mkSizedFIFO(valueOf(TAG_DEPTH)));
   // stage 4: write done (minimal buffering required)
   FIFO#(Tuple2#(RResp#(numClients,numTags,addrWidth),Bit#(6))) writeDonePipelineFifo <- mkFIFO;

   Reg#(Bit#(8)) burstReg <- mkReg(0);
   Reg#(Bit#(64)) beatCount <- mkReg(0);
   let beat_shift = fromInteger(valueOf(beatShift));

   Reg#(Bit#(64)) cycle_cnt <- mkReg(0);
   Reg#(Bit#(64)) last_loadClient <- mkReg(0);
   Reg#(Bit#(64)) last_sglResp <- mkReg(0);
   Reg#(Bit#(64)) last_eob <- mkReg(0);
   (* fire_when_enabled *)
   rule cycle;
      cycle_cnt <= cycle_cnt+1;
   endrule

   for (Integer selectReg = 0; selectReg < valueOf(numClients); selectReg = selectReg + 1)
       rule loadClient;
      	  //$display("mkMemWriteInternal::loadClient %d %d", selectReg, cycle_cnt-last_loadClient);
	  //last_loadClient <= cycle_cnt;
   	  ObjectRequest req <- writeClients[selectReg].writeReq.get();
   	  if (bad_pointer(req.pointer))
   	     dmaIndication.badPointer(req.pointer);
   	  else begin
	     tag_gen.tag_request(fromInteger(selectReg), req.tag);
   	     lreqFifo.enq(LRec{req:req, client:fromInteger(selectReg)});
   	     sgl.request.put(tuple2(truncate(req.pointer),req.offset));
   	  end
       endrule

   rule checkSglResp;
      let physAddr <- sgl.response.get;
      let req = lreqFifo.first.req;
      let client = lreqFifo.first.client;
      let rename_tag <- tag_gen.tag_response;
      lreqFifo.deq();
      if (physAddr <= (1 << valueOf(SGListPageShift0))) begin
	 // squash request
	 $display("dmaWrite: badAddr handle=%d addr=%h physAddr=%h", req.pointer, req.offset, physAddr);
	 dmaIndication.badAddr(req.pointer, extend(req.offset), extend(physAddr));
	 tag_gen.return_tag(rename_tag);
      end
      else begin
	 reqFifo.enq(RRec{req:req, pa:physAddr, client:client, rename_tag:extend(rename_tag)});
	 //$display("checkSglResp: client=%d, rename_tag=%d", client,rename_tag);
      end
      //$display("mkMemWriteInternal::sglResp %d %d", client, cycle_cnt-last_sglResp);
      //last_sglResp <= cycle_cnt;
   endrule

   rule writeDoneComp;
      writeDonePipelineFifo.deq;
      let client = tpl_1(writeDonePipelineFifo.first).client;
      let orig_tag = tpl_1(writeDonePipelineFifo.first).orig_tag;
      let response_tag = tpl_2(writeDonePipelineFifo.first);
      writeClients[client].writeDone.put(orig_tag);
      tag_gen.return_tag(truncate(response_tag));
   endrule

   interface MemWriteClient write_client;
      interface Get writeReq;
	 method ActionValue#(MemRequest#(addrWidth)) get();
	    let req = reqFifo.first.req;
	    let physAddr = reqFifo.first.pa;
	    let client = reqFifo.first.client;
	    let rename_tag = reqFifo.first.rename_tag;
	    reqFifo.deq;
	    dreqFifo.enq(DRec{req:req, client:client, rename_tag:rename_tag});
	    //$display("writeReq: client=%d, rename_tag=%d", client,rename_tag);
	    return MemRequest{addr:physAddr, burstLen:req.burstLen, tag:extend(rename_tag)};
	 endmethod
      endinterface
      interface Get writeData;
	 method ActionValue#(MemData#(dataWidth)) get();
	    let client = dreqFifo.first.client;
	    let req = dreqFifo.first.req;
	    let rename_tag =dreqFifo.first.rename_tag;
	    ObjectData#(dataWidth) tagdata <- writeClients[client].writeData.get();
	    let burstLen = burstReg;
	    if (burstLen == 0) begin
	       burstLen = req.burstLen >> beat_shift;
	       respFifos[rename_tag].enq(RResp{orig_tag:req.tag, client:client});
	    end
	    burstReg <= burstLen-1;
	    beatCount <= beatCount+1;
	    if (burstLen == 1)
	       dreqFifo.deq();
	    //$display("writeData: client=%d, rename_tag=%d", client, rename_tag);
	    return MemData { data: tagdata.data,  tag:extend(rename_tag), last: False };
	 endmethod
      endinterface
      interface Put writeDone;
	 method Action put(Bit#(6) resp);
	    let response_tag = resp;
	    let respFifo = respFifos[response_tag];
	    writeDonePipelineFifo.enq(tuple2(respFifo.first,resp));
	    respFifo.deq;
	 endmethod
      endinterface
   endinterface
   interface DmaDbg dbg;
      method ActionValue#(DmaDbgRec) dbg();
	 return DmaDbgRec{x:fromInteger(valueOf(numClients)), y:?, z:?, w:?};
      endmethod
      method ActionValue#(Bit#(64)) getMemoryTraffic();
	 return beatCount;
      endmethod
   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.

// BSV Libraries
import BRAMFIFO::*;
import FIFO::*;
import FIFOF::*;
import Vector::*;
import GetPut::*;
import ClientServer::*;
import Assert::*;

// XBSV Libraries
import MemTypes::*;
import PortalMemory::*;
import SGList::*;

typedef 9 SGL_PIPELINE_DEPTH;
typedef 32 TAG_DEPTH;

interface MemWriteInternal#(numeric type addrWidth, numeric type dataWidth);
   interface DmaDbg dbg;
   interface MemWriteClient#(addrWidth,dataWidth) write_client;
endinterface

interface MemReadInternal#(numeric type addrWidth, numeric type dataWidth);
   interface DmaDbg dbg;
   interface MemReadClient#(addrWidth,dataWidth) read_client;
endinterface

function Bool bad_pointer(ObjectPointer p);
   return (p > fromInteger(valueOf(MaxNumSGLists)) || p == 0);
endfunction

interface TagGen#(numeric type numClients, numeric type numTags);
   method Action tag_request(Bit#(TLog#(numClients)) client, Bit#(6) orig_tag);
   method ActionValue#(Bit#(TLog#(numTags)))  tag_response;
   method Action return_tag(Bit#(TLog#(numTags)) tag);
endinterface

module mkTagGenOO(TagGen#(numClients,numTags))
   provisos(Log#(numTags,tagWidth),
	    Log#(numClients,clientWidth));

   let request_fifo0 <- mkSizedFIFOF(valueOf(SGL_PIPELINE_DEPTH));
   let request_fifo1 <- mkSizedFIFOF(valueOf(SGL_PIPELINE_DEPTH));
   let return_fifo <- mkFIFO;
   Vector#(numTags, Reg#(Bit#(TLog#(TAG_DEPTH)))) tag_regs <- replicateM(mkReg(0));
   Vector#(numTags, Reg#(Maybe#(Tuple2#(Bit#(clientWidth),Bit#(6))))) client_map <- replicateM(mkReg(tagged Invalid));
   Maybe#(UInt#(tagWidth)) next_free = findElem(0, readVReg(tag_regs));

   rule return_tag_rule;
      return_fifo.deq;
      let tag = return_fifo.first;
      tag_regs[tag] <= tag_regs[tag]-1;
      if (tag_regs[tag]==1)
	 client_map[tag] <= tagged Invalid;
   endrule

   rule tag_request_rule;
      request_fifo0.deq;
      let client = tpl_2(request_fifo0.first);
      let orig_tag = tpl_3(request_fifo0.first);
      let rv = case  (tpl_1(request_fifo0.first)) matches
		  tagged Valid .tag: return (_when_(tag_regs[tag] < maxBound) (tag));
		  tagged Invalid: return (_when_(isValid(next_free)) (fromMaybe(?, next_free)));
	       endcase;
      request_fifo1.enq(tuple3(rv,client,orig_tag));
   endrule

   method Action tag_request(Bit#(clientWidth) client, Bit#(6) orig_tag) if (request_fifo0.notFull);
      let rv = findElem(tagged Valid tuple2(client,orig_tag), readVReg(client_map));
      request_fifo0.enq(tuple3(rv,client,orig_tag));
   endmethod

   method ActionValue#(Bit#(tagWidth)) tag_response;
      request_fifo1.deq;
      let rv = tpl_1(request_fifo1.first);
      let client = tpl_2(request_fifo1.first);
      let orig_tag = tpl_3(request_fifo1.first);
      client_map[rv] <= tagged Valid tuple2(client,orig_tag);
      tag_regs[rv] <= tag_regs[rv]+1;
      return extend(pack(rv));
   endmethod

   method Action return_tag(Bit#(tagWidth) tag);
      return_fifo.enq(tag);
   endmethod

endmodule

module mkTagGenIO(TagGen#(numClients,numTags))
   provisos(Log#(numTags,tagWidth),
	    Log#(numClients,clientWidth),
	    Bits#(Bit#(clientWidth), tagWidth));

   let request_fifo <- mkSizedFIFO(valueOf(SGL_PIPELINE_DEPTH));

   method Action tag_request(Bit#(clientWidth) client, Bit#(6) client_tag);
      request_fifo.enq(client);
   endmethod

   method ActionValue#(Bit#(tagWidth)) tag_response;
      request_fifo.deq;
      let client = request_fifo.first;
      return pack(client);
   endmethod

   method Action return_tag(Bit#(tagWidth) tag);
      noAction;
   endmethod

endmodule

typedef struct {ObjectRequest req;
		Bit#(TLog#(numClients)) client; } LRec#(numeric type numClients, numeric type numTags, numeric type addrWidth) deriving(Bits);

typedef struct {ObjectRequest req;
		Bit#(addrWidth) pa;
		Bit#(TLog#(numTags)) rename_tag;
		Bit#(TLog#(numClients)) client; } RRec#(numeric type numClients, numeric type numTags, numeric type addrWidth) deriving(Bits);

typedef struct {ObjectRequest req;
		Bit#(TLog#(numTags)) rename_tag;
		Bit#(TLog#(numClients)) client; } DRec#(numeric type numClients, numeric type numTags, numeric type addrWidth) deriving(Bits);

typedef struct {Bit#(6) orig_tag;
		Bit#(TLog#(numClients)) client; } RResp#(numeric type numClients, numeric type numTags, numeric type addrWidth) deriving(Bits);

module mkMemReadInternal#(Integer id,
			  Vector#(numClients, ObjectReadClient#(dataWidth)) readClients,
			  DmaIndication dmaIndication,
			  Server#(Tuple2#(SGListId,Bit#(ObjectOffsetSize)),Bit#(addrWidth)) sgl,
			  TagGen#(numClients, numTags) tag_gen)
   (MemReadInternal#(addrWidth, dataWidth))

   provisos(Add#(b__, addrWidth, 64),
	    Add#(c__, 12, addrWidth),
	    Add#(1, c__, d__),
	    Div#(dataWidth,8,dataWidthBytes),
	    Mul#(dataWidthBytes,8,dataWidth),
	    Log#(dataWidthBytes,beatShift),
	    Add#(a__, TLog#(numTags), 6));

   // stage 0: address translation (latency = SGL_PIPELINE_DEPTH)
   FIFO#(LRec#(numClients,numTags,addrWidth)) lreqFifo <- mkSizedFIFO(valueOf(SGL_PIPELINE_DEPTH));
   // stage 1: address validation (latency = 1)
   FIFO#(RRec#(numClients,numTags,addrWidth))  reqFifo <- mkFIFO;
   // stage 2: read commands (maximum buffering to handle high latency read response times)
   Vector#(numTags, FIFOF#(DRec#(numClients,numTags,addrWidth))) dreqFifos <- replicateM(mkSizedFIFOF(valueOf(TAG_DEPTH)));
   // stage 3: read data (minimal buffering required)
   FIFO#(Tuple2#(DRec#(numClients,numTags,addrWidth),MemData#(dataWidth))) readDataPipelineFifo <- mkFIFO;
   FIFO#(MemData#(dataWidth)) responseFifo <- mkFIFO;
   Vector#(numTags, Reg#(Bit#(8)))           burstRegs <- replicateM(mkReg(0));
   Reg#(Bit#(64))  beatCount <- mkReg(0);
   let beat_shift = fromInteger(valueOf(beatShift));

   // performance analytics
   Reg#(Bit#(64)) cycle_cnt <- mkReg(0);
   Reg#(Bit#(64)) last_loadClient <- mkReg(0);
   Reg#(Bit#(64)) last_sglResp <- mkReg(0);
   Reg#(Bit#(64)) last_eob <- mkReg(0);
   (* fire_when_enabled *)
   rule cycle;
      cycle_cnt <= cycle_cnt+1;
   endrule

   for (Integer selectReg = 0; selectReg < valueOf(numClients); selectReg = selectReg + 1)
      rule loadClient;
      	 //$display("mkMemReadInternal::loadClient %d %d", selectReg, cycle_cnt-last_loadClient);
	 //last_loadClient <= cycle_cnt;
   	 ObjectRequest req <- readClients[selectReg].readReq.get();
   	 if (bad_pointer(req.pointer))
   	    dmaIndication.badPointer(req.pointer);
   	 else begin
	    tag_gen.tag_request(fromInteger(selectReg), req.tag);
   	    lreqFifo.enq(LRec{req:req, client:fromInteger(selectReg)});
   	    sgl.request.put(tuple2(truncate(req.pointer),req.offset));
   	 end
      endrule

   rule checkSglResp;
      let physAddr <- sgl.response.get;
      let req = lreqFifo.first.req;
      let client = lreqFifo.first.client;
      let rename_tag <- tag_gen.tag_response;
      lreqFifo.deq();
      if (physAddr <= (1 << valueOf(SGListPageShift0))) begin
	 // squash request
	 $display("dmaRead: badAddr pointer=%d offset=%h physAddr=%h", req.pointer, req.offset, physAddr);
	 dmaIndication.badAddr(req.pointer, extend(req.offset), extend(physAddr));
	 tag_gen.return_tag(rename_tag);
      end
      else begin
	 if (False && physAddr[31:24] != 0)
	    $display("checkSglResp: funny physAddr req.pointer=%d req.offset=%h physAddr=%h", req.pointer, req.offset, physAddr);
	 reqFifo.enq(RRec{req:req, pa:physAddr, client:client, rename_tag:extend(rename_tag)});
	 //$display("checkSglResp: client=%d, rename_tag=%d", client,rename_tag);
      end
      //$display("mkMemReadInternal::sglResp %d %d", client, cycle_cnt-last_sglResp);
      //last_sglResp <= cycle_cnt;
   endrule

   rule readDataComp;
      readDataPipelineFifo.deq;
      let drq = tpl_1(readDataPipelineFifo.first);
      let response = tpl_2(readDataPipelineFifo.first);
      let client = drq.client;
      let req = drq.req;
      readClients[client].readData.put(ObjectData { data: response.data, tag: req.tag, last: False});
      //$display("readDataComp: %d %h", client, response.data);
      beatCount <= beatCount+1;
   endrule

   rule read_client_response;
      let response <- toGet(responseFifo).get();
      Bit#(6) response_tag = response.tag;
      let dreqFifo = dreqFifos[response_tag];
      dynamicAssert(truncate(response_tag) == dreqFifo.first.rename_tag, "mkMemReadInternal");
      readDataPipelineFifo.enq(tuple2(dreqFifo.first, response));
      let burstLen = burstRegs[response_tag];
      if (burstLen == 0)
	 burstLen = dreqFifo.first.req.burstLen >> beat_shift;
      if (burstLen == 1) begin
	 //$display("mkMemReadInternal::eob %d", cycle_cnt-last_eob);
	 last_eob <= cycle_cnt;
	 dreqFifo.deq();
	 tag_gen.return_tag(truncate(response_tag));
      end
      burstRegs[response_tag] <= burstLen-1;
   endrule

   interface MemReadClient read_client;
      interface Get readReq;
	 method ActionValue#(MemRequest#(addrWidth)) get();
	    reqFifo.deq;
	    let req = reqFifo.first.req;
	    let physAddr = reqFifo.first.pa;
	    let client = reqFifo.first.client;
	    let rename_tag = reqFifo.first.rename_tag;
	    if (False && physAddr[31:24] != 0)
	       $display("req_ar: funny physAddr req.pointer=%d req.offset=%h physAddr=%h", req.pointer, req.offset, physAddr);
	    dreqFifos[rename_tag].enq(DRec{req:req, client:client, rename_tag:rename_tag});
	    //$display("readReq: client=%d, rename_tag=%d, physAddr=%h", client,rename_tag,physAddr);
	    return MemRequest{addr:physAddr, burstLen:req.burstLen, tag:extend(rename_tag)};
	 endmethod
      endinterface
      interface Put readData = toPut(responseFifo);
   endinterface
   interface DmaDbg dbg;
      method ActionValue#(DmaDbgRec) dbg();
	 Bit#(1) ne0 = pack(dreqFifos[0].notEmpty);
	 Bit#(1) ne1 = pack(dreqFifos[1].notEmpty);
	 return DmaDbgRec{x:extend(burstRegs[0]), y:extend(burstRegs[1]), z:extend(ne0), w:extend(ne1)};
      endmethod
      method ActionValue#(Bit#(64)) getMemoryTraffic();
	 return beatCount;
      endmethod
   endinterface
endmodule

module mkMemWriteInternal#(Integer iid,
			   Vector#(numClients, ObjectWriteClient#(dataWidth)) writeClients,
			   DmaIndication dmaIndication,
			   Server#(Tuple2#(SGListId,Bit#(ObjectOffsetSize)),Bit#(addrWidth)) sgl,
			   TagGen#(numClients, numTags) tag_gen)
   (MemWriteInternal#(addrWidth, dataWidth))

   provisos(Add#(b__, addrWidth, 64),
	    Add#(c__, 12, addrWidth),
	    Add#(1, c__, d__),
	    Div#(dataWidth,8,dataWidthBytes),
	    Mul#(dataWidthBytes,8,dataWidth),
	    Log#(dataWidthBytes,beatShift),
	    Add#(a__, TLog#(numTags), 6));

   // stage 0: address translation (latency = SGL_PIPELINE_DEPTH)
   FIFO#(LRec#(numClients,numTags,addrWidth)) lreqFifo <- mkSizedFIFO(valueOf(SGL_PIPELINE_DEPTH));
   // stage 1: address validation (latency = 1)
   FIFO#(RRec#(numClients,numTags,addrWidth))  reqFifo <- mkFIFO;
   // stage 2: write commands (maximum buffering to handle high latency writes)
   FIFO#(DRec#(numClients,numTags,addrWidth)) dreqFifo <- mkSizedBRAMFIFO(valueOf(TMul#(TAG_DEPTH,numTags)));
   // stage 3: write data (maximum buffering, though I have no idea if any hosts will begin the next data transfer before sending the write ack)
   Vector#(numTags, FIFO#(RResp#(numClients,numTags,addrWidth))) respFifos <- replicateM(mkSizedFIFO(valueOf(TAG_DEPTH)));
   // stage 4: write done (minimal buffering required)
   FIFO#(Tuple2#(RResp#(numClients,numTags,addrWidth),Bit#(6))) writeDonePipelineFifo <- mkFIFO;

   Reg#(Bit#(8)) burstReg <- mkReg(0);
   Reg#(Bit#(64)) beatCount <- mkReg(0);
   let beat_shift = fromInteger(valueOf(beatShift));

   Reg#(Bit#(64)) cycle_cnt <- mkReg(0);
   Reg#(Bit#(64)) last_loadClient <- mkReg(0);
   Reg#(Bit#(64)) last_sglResp <- mkReg(0);
   Reg#(Bit#(64)) last_eob <- mkReg(0);
   (* fire_when_enabled *)
   rule cycle;
      cycle_cnt <= cycle_cnt+1;
   endrule

   for (Integer selectReg = 0; selectReg < valueOf(numClients); selectReg = selectReg + 1)
       rule loadClient;
      	  //$display("mkMemWriteInternal::loadClient %d %d", selectReg, cycle_cnt-last_loadClient);
	  //last_loadClient <= cycle_cnt;
   	  ObjectRequest req <- writeClients[selectReg].writeReq.get();
   	  if (bad_pointer(req.pointer))
   	     dmaIndication.badPointer(req.pointer);
   	  else begin
	     tag_gen.tag_request(fromInteger(selectReg), req.tag);
   	     lreqFifo.enq(LRec{req:req, client:fromInteger(selectReg)});
   	     sgl.request.put(tuple2(truncate(req.pointer),req.offset));
   	  end
       endrule

   rule checkSglResp;
      let physAddr <- sgl.response.get;
      let req = lreqFifo.first.req;
      let client = lreqFifo.first.client;
      let rename_tag <- tag_gen.tag_response;
      lreqFifo.deq();
      if (physAddr <= (1 << valueOf(SGListPageShift0))) begin
	 // squash request
	 $display("dmaWrite: badAddr handle=%d addr=%h physAddr=%h", req.pointer, req.offset, physAddr);
	 dmaIndication.badAddr(req.pointer, extend(req.offset), extend(physAddr));
	 tag_gen.return_tag(rename_tag);
      end
      else begin
	 reqFifo.enq(RRec{req:req, pa:physAddr, client:client, rename_tag:extend(rename_tag)});
	 //$display("checkSglResp: client=%d, rename_tag=%d", client,rename_tag);
      end
      //$display("mkMemWriteInternal::sglResp %d %d", client, cycle_cnt-last_sglResp);
      //last_sglResp <= cycle_cnt;
   endrule

   rule writeDoneComp;
      writeDonePipelineFifo.deq;
      let client = tpl_1(writeDonePipelineFifo.first).client;
      let orig_tag = tpl_1(writeDonePipelineFifo.first).orig_tag;
      let response_tag = tpl_2(writeDonePipelineFifo.first);
      writeClients[client].writeDone.put(orig_tag);
      tag_gen.return_tag(truncate(response_tag));
   endrule

   interface MemWriteClient write_client;
      interface Get writeReq;
	 method ActionValue#(MemRequest#(addrWidth)) get();
	    let req = reqFifo.first.req;
	    let physAddr = reqFifo.first.pa;
	    let client = reqFifo.first.client;
	    let rename_tag = reqFifo.first.rename_tag;
	    reqFifo.deq;
	    dreqFifo.enq(DRec{req:req, client:client, rename_tag:rename_tag});
	    //$display("writeReq: client=%d, rename_tag=%d", client,rename_tag);
	    return MemRequest{addr:physAddr, burstLen:req.burstLen, tag:extend(rename_tag)};
	 endmethod
      endinterface
      interface Get writeData;
	 method ActionValue#(MemData#(dataWidth)) get();
	    let client = dreqFifo.first.client;
	    let req = dreqFifo.first.req;
	    let rename_tag =dreqFifo.first.rename_tag;
	    ObjectData#(dataWidth) tagdata <- writeClients[client].writeData.get();
	    let burstLen = burstReg;
	    if (burstLen == 0) begin
	       burstLen = req.burstLen >> beat_shift;
	       respFifos[rename_tag].enq(RResp{orig_tag:req.tag, client:client});
	    end
	    burstReg <= burstLen-1;
	    beatCount <= beatCount+1;
	    if (burstLen == 1)
	       dreqFifo.deq();
	    //$display("writeData: client=%d, rename_tag=%d", client, rename_tag);
	    return MemData { data: tagdata.data,  tag:extend(rename_tag), last: False };
	 endmethod
      endinterface
      interface Put writeDone;
	 method Action put(Bit#(6) resp);
	    let response_tag = resp;
	    let respFifo = respFifos[response_tag];
	    writeDonePipelineFifo.enq(tuple2(respFifo.first,resp));
	    respFifo.deq;
	 endmethod
      endinterface
   endinterface
   interface DmaDbg dbg;
      method ActionValue#(DmaDbgRec) dbg();
	 return DmaDbgRec{x:fromInteger(valueOf(numClients)), y:?, z:?, w:?};
      endmethod
      method ActionValue#(Bit#(64)) getMemoryTraffic();
	 return beatCount;
      endmethod
   endinterface
endmodule


fix fence for in order core

fix fence for in order core


update provisos

update provisos

// bsv libraries
import SpecialFIFOs::*;
import Vector::*;
import StmtFSM::*;
import FIFO::*;

// portz libraries
import AxiMasterSlave::*;
import Directory::*;
import CtrlMux::*;
import Portal::*;
import PortalMemory::*;
import Dma::*;
import DmaUtils::*;
import AxiDma::*;

// generated by tool
import RingIndicationProxy::*;
import RingRequestWrapper::*;
import DmaConfigWrapper::*;
import DmaIndicationProxy::*;

// defined by user
import Ring::*;

typedef enum {RingIndication, RingRequest, DmaIndication, DmaRequest} IfcNames deriving (Eq,Bits);

module mkPortalTop(StdPortalTop#(addrWidth))

   provisos(Add#(addrWidth, a__, 52),
	    Add#(b__, addrWidth, 64),
	    Add#(c__, 12, addrWidth),
	    Add#(addrWidth, d__, 44),
	    Add#(e__, c__, 40),
	    Add#(f__, addrWidth, 40));

   // instantiate Dma infrastructure
   DmaIndicationProxy dmaIndicationProxy <- mkDmaIndicationProxy(DmaIndication);
   DmaReadBuffer#(64,8) dma_read_chan <- mkDmaReadBuffer();
   DmaWriteBuffer#(64,8) dma_write_chan <- mkDmaWriteBuffer();
   DmaReadBuffer#(64,8) cmd_read_chan <- mkDmaReadBuffer();
   DmaWriteBuffer#(64,8) cmd_write_chan <- mkDmaWriteBuffer();

   Vector#(2, DmaReadClient#(64)) readClients = newVector();
   readClients[0] = dma_read_chan.dmaClient;
   readClients[1] = cmd_read_chan.dmaClient;

   Vector#(2, DmaWriteClient#(64)) writeClients = newVector();
   writeClients[0] = dma_write_chan.dmaClient;
   writeClients[1] = cmd_write_chan.dmaClient;

   Integer numRequests = 8;
   AxiDmaServer#(addrWidth,64) dma <- mkAxiDmaServer(dmaIndicationProxy.ifc, numRequests, readClients, writeClients);
   DmaConfigWrapper dmaRequestWrapper <- mkDmaConfigWrapper(DmaRequest,dma.request);

   // instantiate user portals
   RingIndicationProxy ringIndicationProxy <- mkRingIndicationProxy(RingIndication);
   RingRequest ringRequest <- mkRingRequest(ringIndicationProxy.ifc, dma_read_chan.dmaServer, dma_write_chan.dmaServer, cmd_read_chan.dmaServer, cmd_write_chan.dmaServer);
   RingRequestWrapper ringRequestWrapper <- mkRingRequestWrapper(RingRequest, ringRequest);

   Vector#(4,StdPortal) portals;
   portals[0] = ringIndicationProxy.portalIfc;
   portals[1] = ringRequestWrapper.portalIfc;
   portals[2] = dmaIndicationProxy.portalIfc;
   portals[3] = dmaRequestWrapper.portalIfc;

   let interrupt_mux <- mkInterruptMux(portals);

   // instantiate system directory
   Directory dir <- mkDirectory(portals);
   Vector#(1,StdPortal) directories;
   directories[0] = dir.portalIfc;

   // when constructing the ctrl mux, directories must be the first argument
   let ctrl_mux <- mkAxiSlaveMux(directories,portals);

   interface interrupt = interrupt_mux;
   interface ctrl = ctrl_mux;
   interface m_axi = dma.m_axi;
   interface leds = ?;
endmodule : mkPortalTop

// bsv libraries
import SpecialFIFOs::*;
import Vector::*;
import StmtFSM::*;
import FIFO::*;

// portz libraries
import AxiMasterSlave::*;
import Directory::*;
import CtrlMux::*;
import Portal::*;
import PortalMemory::*;
import Dma::*;
import DmaUtils::*;
import AxiDma::*;

// generated by tool
import RingIndicationProxy::*;
import RingRequestWrapper::*;
import DmaConfigWrapper::*;
import DmaIndicationProxy::*;

// defined by user
import Ring::*;

typedef enum {RingIndication, RingRequest, DmaIndication, DmaRequest} IfcNames deriving (Eq,Bits);

module mkPortalTop(StdPortalTop#(addrWidth)) provisos(

   Add#(addrWidth, a__, 52),
   Add#(b__, addrWidth, 64),
   Add#(c__, 12, addrWidth),
   Add#(addrWidth, d__, 44));

   // instantiate Dma infrastructure
   DmaIndicationProxy dmaIndicationProxy <- mkDmaIndicationProxy(DmaIndication);
   DmaReadBuffer#(64,8) dma_read_chan <- mkDmaReadBuffer();
   DmaWriteBuffer#(64,8) dma_write_chan <- mkDmaWriteBuffer();
   DmaReadBuffer#(64,8) cmd_read_chan <- mkDmaReadBuffer();
   DmaWriteBuffer#(64,8) cmd_write_chan <- mkDmaWriteBuffer();

   Vector#(2, DmaReadClient#(64)) readClients = newVector();
   readClients[0] = dma_read_chan.dmaClient;
   readClients[1] = cmd_read_chan.dmaClient;

   Vector#(2, DmaWriteClient#(64)) writeClients = newVector();
   writeClients[0] = dma_write_chan.dmaClient;
   writeClients[1] = cmd_write_chan.dmaClient;

   Integer numRequests = 8;
   AxiDmaServer#(addrWidth,64) dma <- mkAxiDmaServer(dmaIndicationProxy.ifc, numRequests, readClients, writeClients);
   DmaConfigWrapper dmaRequestWrapper <- mkDmaConfigWrapper(DmaRequest,dma.request);

   // instantiate user portals
   RingIndicationProxy ringIndicationProxy <- mkRingIndicationProxy(RingIndication);
   RingRequest ringRequest <- mkRingRequest(ringIndicationProxy.ifc, dma_read_chan.dmaServer, dma_write_chan.dmaServer, cmd_read_chan.dmaServer, cmd_write_chan.dmaServer);
   RingRequestWrapper ringRequestWrapper <- mkRingRequestWrapper(RingRequest, ringRequest);

   Vector#(4,StdPortal) portals;
   portals[0] = ringIndicationProxy.portalIfc;
   portals[1] = ringRequestWrapper.portalIfc;
   portals[2] = dmaIndicationProxy.portalIfc;
   portals[3] = dmaRequestWrapper.portalIfc;

   let interrupt_mux <- mkInterruptMux(portals);

   // instantiate system directory
   Directory dir <- mkDirectory(portals);
   Vector#(1,StdPortal) directories;
   directories[0] = dir.portalIfc;

   // when constructing the ctrl mux, directories must be the first argument
   let ctrl_mux <- mkAxiSlaveMux(directories,portals);

   interface interrupt = interrupt_mux;
   interface ctrl = ctrl_mux;
   interface m_axi = dma.m_axi;
   interface leds = ?;
endmodule : mkPortalTop


added some documentation to DmaController

// Copyright (c) 2015 Connectal Project

// 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 FIFO::*;
import FIFOF::*;
import GetPut::*;
import Connectable::*;
import Pipe::*;
import MemTypes::*;
import MemReadEngine::*;
import MemWriteEngine::*;
import HostInterface::*;

interface DmaRequest;
   //
   // Configures burstLen used by DMA transfers. Only needed for performance tuning if default value does not perform well.
   //
   method Action burstLen(Bit#(8) burstLenBytes);
   //
   // Requests a read of system memory, streaming the data to the readData PipeOut
   // @param objId the reference to the memory object allocated by portalAlloc
   // @param base  offset, in bytes, from which to start reading
   // @param bytes number of bytes to read, must be a multiple of the buswidth in bytes
   // @param tag   identifier for the request
   method Action read(Bit#(32) objId, Bit#(32) base, Bit#(32) bytes, Bit#(8) tag);
   //
   // Requests a write of system memory, streaming the data from writeData PipeIn
   // @param objId the reference to the memory object allocated by portalAlloc
   // @param base  offset, in bytes, to which to start writing
   // @param bytes number of bytes to write, must be a multiple of the buswidth in bytes
   // @param tag   identifier for the request
   method Action write(Bit#(32) objId, Bit#(32) base, Bit#(32) bytes, Bit#(8) tag);
endinterface

interface DmaIndication;
   // Indicates completion of read request, identified by tag, from offset base of objId
   method Action readDone(Bit#(32) objId, Bit#(32) base, Bit#(8) tag);
   // Indicates completion of write request, identified by tag, to offset base of objId
   method Action writeDone(Bit#(32) objId, Bit#(32) base, Bit#(8) tag);
endinterface

//
// DmaController controls multiple channels of DMA to/from system memory
// @param numChannels: the maximum number of simultaneous read and write streams
interface DmaController#(numeric type numChannels);
   // request from software
   interface Vector#(numChannels,DmaRequest) request;
   // data out to application logic
   interface Vector#(numChannels,PipeOut#(MemDataF#(DataBusWidth))) readData;
   // data in from application logic
   interface Vector#(numChannels,PipeIn#(MemDataF#(DataBusWidth)))  writeData;
   // memory interfaces connected to MemServer
   interface Vector#(1,MemReadClient#(DataBusWidth))      readClient;
   interface Vector#(1,MemWriteClient#(DataBusWidth))     writeClient;
endinterface

typedef 14 NumOutstandingRequests;
typedef TMul#(NumOutstandingRequests,TMul#(32,4)) BufferSizeBytes;

function Bit#(dsz) memdatafToData(MemDataF#(dsz) mdf); return mdf.data; endfunction

module mkDmaController#(Vector#(numChannels,DmaIndication) indication)(DmaController#(numChannels))
   provisos (Add#(1, a__, numChannels),
	     Add#(b__, TLog#(numChannels), TAdd#(1, TLog#(TMul#(NumOutstandingRequests, numChannels)))),
	     Add#(c__, TLog#(numChannels), 6), // why is this?
	     Add#(d__, TLog#(numChannels), TLog#(TMul#(NumOutstandingRequests, numChannels)))
	     );
   MemReadEngine#(DataBusWidth,DataBusWidth,NumOutstandingRequests,numChannels)  re <- mkMemReadEngineBuff(valueOf(BufferSizeBytes));
   MemWriteEngine#(DataBusWidth,DataBusWidth,NumOutstandingRequests,numChannels) we <- mkMemWriteEngineBuff(valueOf(BufferSizeBytes));

   Vector#(numChannels, FIFO#(Tuple2#(Bit#(32),Bit#(32)))) readReqs <- replicateM(mkSizedFIFO(valueOf(NumOutstandingRequests)));
   Vector#(numChannels, FIFO#(Tuple2#(Bit#(32),Bit#(32)))) writeReqs <- replicateM(mkSizedFIFO(valueOf(NumOutstandingRequests)));
   Vector#(numChannels, FIFOF#(MemDataF#(DataBusWidth))) readFifo <- replicateM(mkFIFOF());
   Vector#(numChannels, FIFO#(Bit#(8))) writeTags <- replicateM(mkSizedFIFO(valueOf(NumOutstandingRequests)));
   Reg#(Bit#(BurstLenSize)) burstLenReg <- mkReg(64);

   for (Integer channel = 0; channel < valueOf(numChannels); channel = channel + 1) begin
       FIFO#(Bit#(8)) readTags <- mkSizedFIFO(valueOf(NumOutstandingRequests));

       rule readDataRule;
	  let mdf <- toGet(re.readServers[channel].data).get();
	  if (mdf.last)
	     readTags.enq(extend(mdf.tag));
	  readFifo[channel].enq(mdf);
       endrule
       rule readDoneRule;
	  match { .objId, .base } <- toGet(readReqs[channel]).get();
	  let tag <- toGet(readTags).get();
	  indication[channel].readDone(objId, base, tag);
       endrule
       rule writeDoneRule;
	  match { .objId, .base } <- toGet(writeReqs[channel]).get();
	  let done <- we.writeServers[channel].done.get();
	  let tag <- toGet(writeTags[channel]).get();
	  indication[channel].writeDone(objId, base, tag);
       endrule
   end

   function DmaRequest dmaRequestInterface(Integer channel);
      return (interface DmaRequest;
	 method Action burstLen(Bit#(8) burstLenBytes);
	      burstLenReg <= burstLenBytes;
	 endmethod
	 method Action read(Bit#(32) objId, Bit#(32) base, Bit#(32) bytes, Bit#(8) tag);
	      readReqs[channel].enq(tuple2(objId, base));
	      re.readServers[channel].request.put(MemengineCmd {sglId: truncate(objId),
								base: extend(base),
								burstLen: extend(burstLenReg),
								len: bytes,
								tag: truncate(tag)
								});
	 endmethod
	 method Action write(Bit#(32) objId, Bit#(32) base, Bit#(32) bytes, Bit#(8) tag);
	      writeReqs[channel].enq(tuple2(objId, base));
	      we.writeServers[channel].request.put(MemengineCmd {sglId: truncate(objId),
								 base: extend(base),
								 burstLen: extend(burstLenReg),
								 len: bytes,
								 tag: truncate(tag)
								 });
	      writeTags[channel].enq(tag);
	 endmethod
	 endinterface);
   endfunction
   function PipeIn#(Bit#(dsz)) writeServerData(MemWriteEngineServer#(dsz) s); return s.data; endfunction

   interface Vector request = genWith(dmaRequestInterface);
   interface readData = map(toPipeOut,readFifo);
   interface writeData = map(mapPipeIn(memdatafToData), map(writeServerData, we.writeServers));
   interface readClient = vec(re.dmaClient);
   interface writeClient = vec(we.dmaClient);
endmodule

// Copyright (c) 2015 Connectal Project

// 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 FIFO::*;
import FIFOF::*;
import GetPut::*;
import Connectable::*;
import Pipe::*;
import MemTypes::*;
import MemReadEngine::*;
import MemWriteEngine::*;
import HostInterface::*;

interface DmaRequest;
   method Action burstLen(Bit#(8) burstLenBytes);
   method Action read(Bit#(32) objId, Bit#(32) base, Bit#(32) bytes, Bit#(8) tag);
   method Action write(Bit#(32) objId, Bit#(32) base, Bit#(32) bytes, Bit#(8) tag);
endinterface

interface DmaIndication;
   method Action readDone(Bit#(32) objId, Bit#(32) base, Bit#(8) tag);
   method Action writeDone(Bit#(32) objId, Bit#(32) base, Bit#(8) tag);
endinterface

interface DmaController#(numeric type numChannels);
   // request from software
   interface Vector#(numChannels,DmaRequest) request;
   // data out to application logic
   interface Vector#(numChannels,PipeOut#(MemDataF#(DataBusWidth))) readData;
   // data in from application logic
   interface Vector#(numChannels,PipeIn#(MemDataF#(DataBusWidth)))  writeData;
   // DMA interfaces connected to MemServer
   interface Vector#(1,MemReadClient#(DataBusWidth))      readClient;
   interface Vector#(1,MemWriteClient#(DataBusWidth))     writeClient;
endinterface

typedef 14 NumOutstandingRequests;
typedef TMul#(NumOutstandingRequests,TMul#(32,4)) BufferSizeBytes;

function Bit#(dsz) memdatafToData(MemDataF#(dsz) mdf); return mdf.data; endfunction

module mkDmaController#(Vector#(numChannels,DmaIndication) indication)(DmaController#(numChannels))
   provisos (Add#(1, a__, numChannels),
	     Add#(b__, TLog#(numChannels), TAdd#(1, TLog#(TMul#(NumOutstandingRequests, numChannels)))),
	     Add#(c__, TLog#(numChannels), 6), // why is this?
	     Add#(d__, TLog#(numChannels), TLog#(TMul#(NumOutstandingRequests, numChannels)))
	     );
   MemReadEngine#(DataBusWidth,DataBusWidth,NumOutstandingRequests,numChannels)  re <- mkMemReadEngineBuff(valueOf(BufferSizeBytes));
   MemWriteEngine#(DataBusWidth,DataBusWidth,NumOutstandingRequests,numChannels) we <- mkMemWriteEngineBuff(valueOf(BufferSizeBytes));

   Vector#(numChannels, FIFO#(Tuple2#(Bit#(32),Bit#(32)))) readReqs <- replicateM(mkSizedFIFO(valueOf(NumOutstandingRequests)));
   Vector#(numChannels, FIFO#(Tuple2#(Bit#(32),Bit#(32)))) writeReqs <- replicateM(mkSizedFIFO(valueOf(NumOutstandingRequests)));
   Vector#(numChannels, FIFOF#(MemDataF#(DataBusWidth))) readFifo <- replicateM(mkFIFOF());
   Vector#(numChannels, FIFO#(Bit#(8))) writeTags <- replicateM(mkSizedFIFO(valueOf(NumOutstandingRequests)));
   Reg#(Bit#(BurstLenSize)) burstLenReg <- mkReg(64);

   for (Integer channel = 0; channel < valueOf(numChannels); channel = channel + 1) begin
       FIFO#(Bit#(8)) readTags <- mkSizedFIFO(valueOf(NumOutstandingRequests));

       rule readDataRule;
	  let mdf <- toGet(re.readServers[channel].data).get();
	  if (mdf.last)
	     readTags.enq(extend(mdf.tag));
	  readFifo[channel].enq(mdf);
       endrule
       rule readDoneRule;
	  match { .objId, .base } <- toGet(readReqs[channel]).get();
	  let tag <- toGet(readTags).get();
	  indication[channel].readDone(objId, base, tag);
       endrule
       rule writeDoneRule;
	  match { .objId, .base } <- toGet(writeReqs[channel]).get();
	  let done <- we.writeServers[channel].done.get();
	  let tag <- toGet(writeTags[channel]).get();
	  indication[channel].writeDone(objId, base, tag);
       endrule
   end

   function DmaRequest dmaRequestInterface(Integer channel);
      return (interface DmaRequest;
	 method Action burstLen(Bit#(8) burstLenBytes);
	      burstLenReg <= burstLenBytes;
	 endmethod
	 method Action read(Bit#(32) objId, Bit#(32) base, Bit#(32) bytes, Bit#(8) tag);
	      readReqs[channel].enq(tuple2(objId, base));
	      re.readServers[channel].request.put(MemengineCmd {sglId: truncate(objId),
								base: extend(base),
								burstLen: extend(burstLenReg),
								len: bytes,
								tag: truncate(tag)
								});
	 endmethod
	 method Action write(Bit#(32) objId, Bit#(32) base, Bit#(32) bytes, Bit#(8) tag);
	      writeReqs[channel].enq(tuple2(objId, base));
	      we.writeServers[channel].request.put(MemengineCmd {sglId: truncate(objId),
								 base: extend(base),
								 burstLen: extend(burstLenReg),
								 len: bytes,
								 tag: truncate(tag)
								 });
	      writeTags[channel].enq(tag);
	 endmethod
	 endinterface);
   endfunction
   function PipeIn#(Bit#(dsz)) writeServerData(MemWriteEngineServer#(dsz) s); return s.data; endfunction

   interface Vector request = genWith(dmaRequestInterface);
   interface readData = map(toPipeOut,readFifo);
   interface writeData = map(mapPipeIn(memdatafToData), map(writeServerData, we.writeServers));
   interface readClient = vec(re.dmaClient);
   interface writeClient = vec(we.dmaClient);
endmodule


fixing obvious bugs

fixing obvious bugs

//Top.bsv customized for Ring example

// bsv libraries
import Vector::*;
import FIFO::*;
import Connectable::*;

// portz libraries
import Portal::*;
import AxiMasterSlave::*;
import Directory::*;
import CtrlMux::*;
import Portal::*;
import Leds::*;

// generated by tool
import RingIndicationProxy::*;
import RingRequestWrapper::*;

// defined by user
import Ring::*;

typedef enum {RingIndication, RingRequest} IfcNames deriving (Eq,Bits);

module mkPortalTop(StdPortalTop);

   // instantiate user portals
   RingIndicationProxy ringIndicationProxy <- mkRingIndicationProxy(RingIndication);
   RingRequestInternal ringRequestInternal <- mkRingRequestInternal(ringIndicationProxy.ifc);
   RingRequestWrapper ringRequestWrapper <- mkRingRequestWrapper(RingRequest, ringRequestInternal.ifc);

   Vector#(2,StdPortal) portals;
   portals[0] = ringIndicationProxy.portalIfc;
   portals[1] = ringRequestWrapper.portalIfc;

   let interrupt_mux <- mkInterruptMux(portals);

   // instantiate system directory
   Directory dir <- mkDirectoryDbg(portals,interrupt_mux);
   Vector#(1,StdPortal) directories;
   directories[0] = dir.portalIfc;

   // when constructing the ctrl mux, directories must be the first argument
   let ctrl_mux <- mkAxiSlaveMux(directories,portals);

   interface ReadOnly interrupt = interrupt_mux;
   interface StdAxi3Slave ctrl = ctrl_mux;
   interface Axi3Client m_axi = ?;

endmodule : mkPortalTop

//Top.bsv customized for Ring example

// bsv libraries
import Vector::*;
import FIFO::*;
import Connectable::*;

// portz libraries
import Portal::*;
import AxiMasterSlave::*;
import Directory::*;
import CtrlMux::*;
import Portal::*;
import Leds::*;

// generated by tool
import RingIndicationProxy::*;
import RingRequestWrapper::*;

// defined by user
import Ring::*;

module mkPortalTop(StdPortalTop);

   // instantiate user portals
   RingIndicationProxy echoIndicationProxy <- mkRingIndicationProxy(7);
   RingRequestInternal echoRequestInternal <- mkRingRequestInternal(echoIndicationProxy.ifc);
   RingRequestWrapper echoRequestWrapper <- mkRingRequestWrapper(1008,echoRequestInternal.ifc);

   Vector#(2,StdPortal) portals;
   portals[0] = echoIndicationProxy.portalIfc;
   portals[1] = echoRequestWrapper.portalIfc;

   let interrupt_mux <- mkInterruptMux(portals);

   // instantiate system directory
   Directory dir <- mkDirectoryDbg(portals,interrupt_mux);
   Vector#(1,StdPortal) directories;
   directories[0] = dir.portalIfc;

   // when constructing the ctrl mux, directories must be the first argument
   let ctrl_mux <- mkAxiSlaveMux(directories,portals);

   interface ReadOnly interrupt = interrupt_mux;
   interface StdAxi3Slave ctrl = ctrl_mux;

endmodule : mkPortalTop