cons() -> vec()

cons() -> vec()

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

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

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

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import BuildVector::*;
import Clocks::*;
import DefaultValue::*;
import GetPut::*;
import Connectable::*;
import ConnectableWithTrace::*;
import Bscan::*;
import Vector::*;
import PPS7LIB::*;
import AxiMasterSlave::*;
import AxiDma::*;
import XilinxCells::*;
import ConnectalXilinxCells::*;
import ConnectalClocks::*;
import AxiBits::*;
import AxiGather::*;

(* always_ready, always_enabled *)
interface Bidir#(numeric type data_width);
    method Action             i(Bit#(data_width) v);
    method Bit#(data_width)   o();
    method Bit#(data_width)   t();
endinterface

interface PS7LIB;
`ifdef PS7EXTENDED
    interface Vector#(2, Pps7Emiocan)  can;
    interface Vector#(4, Pps7Dma)  dma;
    interface Vector#(2, Pps7Emioenet) enet;
    interface Pps7Event            event_;
//    interface Vector#(4,Pps7Fclk_clktrig)fclk_clktrig;
//    interface Pps7Fpga             fpga;
    interface Pps7Ftmd             ftmd;
    interface Pps7Ftmt             ftmt;
    interface Pps7Emiopjtag            pjtag;
    interface Vector#(2, Pps7Emiosdio) sdio;
    interface Vector#(2, Pps7Emiospi)  spi;
//    interface Pps7Sram             sram;
    interface Pps7Emiotrace            trace;
    interface Vector#(2, Pps7Emiottc)  ttc;
    interface Vector#(2, Pps7Emiouart) uart;
    interface Vector#(2, Pps7Emiousb)  usb;
    interface Pps7Emiowdt              wdt;
`endif
    interface Pps7Ddr              ddr;
    method Bit#(4)     fclkclk();
    method Action      fclkclktrign(Bit#(4) v);
    method Bit#(4)     fclkresetn();
    method Action      fpgaidlen(Bit#(1) v);
    interface Pps7Emiogpio             gpio;
    interface Vector#(2, Pps7Emioi2c)  i2c;
    interface Pps7Irq              irq;
    interface Inout#(Bit#(54))     mio;
    interface Pps7Ps               ps;

    interface Vector#(2, AxiMasterCommon#(32,32,12)) m_axi_gp;
    interface Vector#(2, AxiSlaveCommon#(32,32,6,Empty)) s_axi_gp;
    interface Vector#(4, AxiSlaveCommon#(32,64,6,HPType)) s_axi_hp;
    interface AxiSlaveCommon#(32,64,3,ACPType) s_axi_acp;
endinterface

module mkPS7LIB#(Clock axi_clock, Reset axi_reset)(PS7LIB);
    PPS7LIB foo <- mkPPS7LIB(
        axi_clock, axi_clock, axi_clock, axi_clock, axi_clock, axi_clock, axi_clock, axi_clock,
        axi_clock, axi_reset, axi_reset, axi_reset, axi_reset, axi_reset, axi_reset, axi_reset,
        axi_reset, axi_reset);
`ifdef PS7EXTENDED
    Vector#(2, Pps7Emiocan)     vcan;
    Vector#(4, Pps7Dma)     vdma;
    Vector#(2, Pps7Emioenet)     venet;
    Vector#(2, Pps7Emiosdio)     vsdio;
    Vector#(2, Pps7Emiospi)     vspi;
    Vector#(2, Pps7Emiottc)     vttc;
    Vector#(2, Pps7Emiouart)     vuart;
    Vector#(2, Pps7Emiousb)     vusb;
`endif
    Vector#(2, Pps7Emioi2c)     vi2c;
    Vector#(2, AxiMasterCommon#(32,32,12)) vtopm_axi_gp;
    Vector#(2, AxiSlaveCommon#(32,32,6,Empty)) vtops_axi_gp;
    Vector#(1, AxiSlaveCommon#(32,64,3,ACPType)) vtops_axi_acp;
    Vector#(4, AxiSlaveCommon#(32,64,6,HPType)) vtops_axi_hp;

`ifdef PS7EXTENDED
    vcan[0] = foo.emiocan0;
    vcan[1] = foo.emiocan1;
    vdma[0] = foo.dma0;
    vdma[1] = foo.dma1;
    vdma[2] = foo.dma2;
    vdma[3] = foo.dma3;
    venet[0] = foo.emioenet0;
    venet[1] = foo.emioenet1;
    vsdio[0] = foo.emiosdio0;
    vsdio[1] = foo.emiosdio1;
    vspi[0] = foo.emiospi0;
    vspi[1] = foo.emiospi1;
    vttc[0] = foo.emiottc0;
    vttc[1] = foo.emiottc1;
    vuart[0] = foo.emiouart0;
    vuart[1] = foo.emiouart1;
    vusb[0] = foo.emiousb0;
    vusb[1] = foo.emiousb1;
`endif
    vi2c[0] = foo.emioi2c0;
    vi2c[1] = foo.emioi2c1;
    vtopm_axi_gp[0] <- mkAxi3MasterGather(foo.maxigp0, clocked_by axi_clock, reset_by axi_reset);
    vtopm_axi_gp[1] <- mkAxi3MasterGather(foo.maxigp1, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_gp[0] <- mkAxi3SlaveGather(foo.saxigp0, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_gp[1] <- mkAxi3SlaveGather(foo.saxigp1, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_acp[0] <- mkAxi3SlaveGather(foo.saxiacp, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_hp[0] <- mkAxi3SlaveGather(foo.saxihp0, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_hp[1] <- mkAxi3SlaveGather(foo.saxihp1, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_hp[2] <- mkAxi3SlaveGather(foo.saxihp2, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_hp[3] <- mkAxi3SlaveGather(foo.saxihp3, clocked_by axi_clock, reset_by axi_reset);
    Wire#(Bit#(1)) fpgaidlenw <- mkDWire(1);
    rule fpgaidle;
       foo.fpgaidlen(fpgaidlenw);
    endrule
    rule misc;
       foo.emiosramintin(0);
       // UG585 "fclkclktrign is currently not supported and must be tied to ground"
       foo.fclkclktrign(0);
    endrule

`ifdef PS7EXTENDED
    interface Pps7Can can = vcan;
    interface Pps7Dma     dma = vdma;
    interface Pps7Enet     enet = venet;
    interface Pps7Sdio    sdio = vsdio;
    interface Pps7Spi    spi = vspi;
    interface Pps7Ttc    ttc = vttc;
    interface Pps7Uart    uart = vuart;
    interface Pps7Usb    usb = vusb;
    interface Pps7Event     event_ = foo.event_;
//    interface Pps7Fpga     fpga = foo.fpga;
    interface Pps7Ftmd     ftmd = foo.ftmd;
    interface Pps7Ftmt     ftmt = foo.ftmt;
    interface Pps7Pjtag     pjtag = foo.emiopjtag;
//    interface Pps7Sram     sram = foo.sram;
    interface Pps7Emiotrace     trace = foo.emiotrace;
    interface Pps7Wdt     wdt = foo.emiowdt;
`endif
    interface i2c = vi2c;
    interface ddr = foo.ddr;
    interface fclkclk = foo.fclkclk;
    interface fclkresetn = foo.fclkresetn;
    method Action      fclkclktrign(Bit#(4) v);
        foo.fclkclktrign(v);
    endmethod
    method Action      fpgaidlen(Bit#(1) v);
       fpgaidlenw <= v;
    endmethod
    interface gpio = foo.emiogpio;
    interface irq = foo.irq;
    interface mio = foo.mio;
    interface ps = foo.ps;

    interface m_axi_gp = vtopm_axi_gp;
    interface s_axi_gp = vtops_axi_gp;
    interface s_axi_hp = vtops_axi_hp;
    interface s_axi_acp = vtops_axi_acp[0];
endmodule

interface ZynqPins;
    (* prefix="DDR_Addr" *) interface Inout#(Bit#(15))     a;
    (* prefix="DDR_BankAddr" *) interface Inout#(Bit#(3))     ba;
    (* prefix="DDR_CAS_n" *) interface Inout#(Bit#(1))     casb;
    (* prefix="DDR_CKE" *) interface Inout#(Bit#(1))     cke;
    (* prefix="DDR_CS_n" *) interface Inout#(Bit#(1))     csb;
    (* prefix="DDR_Clk_n" *) interface Inout#(Bit#(1))     ckn;
    (* prefix="DDR_Clk_p" *) interface Inout#(Bit#(1))     ck;
    (* prefix="DDR_DM" *) interface Inout#(Bit#(4))     dm;
    (* prefix="DDR_DQ" *) interface Inout#(Bit#(32))     dq;
    (* prefix="DDR_DQS_n" *) interface Inout#(Bit#(4))     dqsn;
    (* prefix="DDR_DQS_p" *) interface Inout#(Bit#(4))     dqs;
    (* prefix="DDR_DRSTB" *) interface Inout#(Bit#(1))     drstb;
    (* prefix="DDR_ODT" *) interface Inout#(Bit#(1))     odt;
    (* prefix="DDR_RAS_n" *) interface Inout#(Bit#(1))     rasb;
    (* prefix="FIXED_IO_ddr_vrn" *) interface Inout#(Bit#(1))     vrn;
    (* prefix="FIXED_IO_ddr_vrp" *) interface Inout#(Bit#(1))     vrp;
    (* prefix="DDR_WEB" *) interface Inout#(Bit#(1))     web;
    (* prefix="MIO" *)
    interface Inout#(Bit#(54))       mio;
    (* prefix="FIXED_IO_ps" *)
    interface Pps7Ps ps;
endinterface

interface PS7;
    (* prefix="" *)
    interface ZynqPins pins;
    interface Vector#(2, AxiMasterCommon#(32,32,12))     m_axi_gp;
    interface Vector#(2, AxiSlaveCommon#(32,32,6,Empty)) s_axi_gp;
    interface Vector#(4, AxiSlaveCommon#(32,64,6,HPType))   s_axi_hp;
    interface Vector#(1, AxiSlaveCommon#(32,64,3,ACPType))   s_axi_acp;
    method Action                             interrupt(Bit#(1) v);
    interface Vector#(4, Clock) fclkclk;
    interface Vector#(4, Reset) fclkreset;
    interface Vector#(2, Pps7Emioi2c)  i2c;
    interface Clock portalClock;
    interface Reset portalReset;
    interface Clock derivedClock;
    interface Reset derivedReset;
`ifdef PS7EXTENDED
    interface Pps7Emiosdio emiosdio1;
    interface Pps7Emiospi  emiospi0;
`endif
endinterface

module mkPS7#(Clock axiClock)(PS7);
   // B2C converts a bit to a clock, enabling us to break the apparent cycle
   Vector#(4, B2C) b2c <- replicateM(mkB2C());

   // need the bufg here to reduce clock skew
   module mkBufferedClock#(Integer i)(Clock); let c <- mkClockBUFG(clocked_by b2c[i].c); return c; endmodule
   module mkBufferedReset#(Integer i)(Reset); let r <- mkResetBUFG(clocked_by b2c[i].c, reset_by b2c[i].r); return r; endmodule
   Vector#(4, Clock) fclk <- genWithM(mkBufferedClock);
   Vector#(4, Reset) freset <- genWithM(mkBufferedReset);

`ifndef TOP_SOURCES_PORTAL_CLOCK
   Clock single_clock = fclk[0];
`ifdef ZYNQ_NO_RESET
   freset[0]          = noReset;
`endif
   let single_reset   = freset[0];
`else
   //Clock axiClockBuf <- mkClockBUFG(clocked_by axiClock);
   Clock axiClockBuf = axiClock;
   Clock single_clock = axiClockBuf;
   Reset axiResetUnbuffered <- mkSyncReset(10, freset[0], single_clock);
   Reset axiReset <- mkResetBUFG(clocked_by axiClockBuf, reset_by axiResetUnbuffered);
   let single_reset   = axiReset;
`endif

   ClockGenerator7Params clockParams = defaultValue;
   // input clock 200MHz for speed grade -2, 100MHz for speed grade -1
   // fpll needs to be in the range 600MHz - 1200MHz for either input clock
   //
   // fclkin = 1e9 / mainClockPeriod
   // fpll = 1e9 = mult_f * 1e9 / mainClockPeriod
   // mult_f = mainClockPeriod
   //
   // fclkout0 = 1e9 / divide_f = 1e9 / derivedClockPeriod
   // divide_f = derivedClockPeriod
   //
   clockParams.clkfbout_mult_f       = mainClockPeriod;
   clockParams.clkfbout_phase     = 0.0;
   clockParams.clkfbout_phase     = 0.0;
   clockParams.clkin1_period      = mainClockPeriod;
   clockParams.clkout0_divide_f   = derivedClockPeriod;
   clockParams.clkout0_duty_cycle = 0.5;
   clockParams.clkout0_phase      = 0.0000;
   clockParams.clkout0_buffer     = True;
   clockParams.clkin_buffer = False;
   ClockGenerator7   clockGen <- mkClockGenerator7(clockParams, clocked_by single_clock, reset_by single_reset);
   let derived_clock = clockGen.clkout0;
   let derived_reset_unbuffered <- mkSyncReset(10, single_reset, derived_clock);
   let derived_reset <- mkResetBUFG(clocked_by derived_clock, reset_by derived_reset_unbuffered);

   PS7LIB ps7 <- mkPS7LIB(single_clock, single_reset, clocked_by single_clock, reset_by single_reset);

   // this rule connects the fclkclk wires to the clock net via B2C
   for (Integer i = 0; i < 4; i = i + 1) begin
      ReadOnly#(Bit#(4)) fclkb;
      ReadOnly#(Bit#(4)) fclkresetnb;
      fclkb       <- mkNullCrossingWire(b2c[i].c, ps7.fclkclk);
      fclkresetnb <- mkNullCrossingWire(b2c[i].c, ps7.fclkresetn);
`ifndef BSV_POSITIVE_RESET
      let resetValue = 0;
`else
      let resetValue = 1;
`endif
      rule b2c_rule1;
	 b2c[i].inputclock(fclkb[i]);
	 b2c[i].inputreset(fclkresetnb[i] == 0 ? resetValue : ~resetValue);
      endrule
      rule issue_rule;
         ps7.s_axi_hp[i].extra.rdissuecap1en(0);
         ps7.s_axi_hp[i].extra.wrissuecap1en(0);
      endrule
   end

   IDELAYCTRL idel <- mkIDELAYCTRL(2, clocked_by fclk[3], reset_by freset[0]);

    rule arb_rule;
        ps7.ddr.arb(4'b0);
    endrule

`ifdef PS7EXTENDED
    interface Pps7Emiosdio emiosdio1 = ps7.sdio[1];
    interface Pps7Emiospi  emiospi0  = ps7.spi[0];
`endif
    interface ZynqPins pins;
    interface a = ps7.ddr.a;
    interface ba = ps7.ddr.ba;
    interface casb = ps7.ddr.casb;
    interface cke = ps7.ddr.cke;
    interface csb = ps7.ddr.csb;
    interface ckn = ps7.ddr.ckn;
    interface ck = ps7.ddr.ckp;
    interface dm = ps7.ddr.dm;
    interface dq = ps7.ddr.dq;
    interface dqsn = ps7.ddr.dqsn;
    interface dqs = ps7.ddr.dqsp;
    interface drstb = ps7.ddr.drstb;
    interface odt = ps7.ddr.odt;
    interface rasb = ps7.ddr.rasb;
    interface vrn = ps7.ddr.vrn;
    interface vrp = ps7.ddr.vrp;
    interface web = ps7.ddr.web;
    interface mio = ps7.mio;
    interface ps = ps7.ps;
    endinterface
    interface m_axi_gp = ps7.m_axi_gp;
    interface s_axi_gp = ps7.s_axi_gp;
    interface s_axi_hp = ps7.s_axi_hp;
    interface fclkclk = fclk;
    interface fclkreset = freset;
`ifndef TOP_SOURCES_PORTAL_CLOCK
    interface portalClock = fclk[0];
    interface portalReset = freset[0];
`else
    interface portalClock = axiClockBuf;
    interface portalReset = axiReset;
`endif
    interface derivedClock = derived_clock;
    interface derivedReset = derived_reset;
    method Action interrupt(Bit#(1) v);
        ps7.irq.f2p({19'b0, v});
    endmethod
    interface i2c = ps7.i2c;
   interface s_axi_acp = vec(ps7.s_axi_acp);
endmodule

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

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

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

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Clocks::*;
import DefaultValue::*;
import GetPut::*;
import Connectable::*;
import ConnectableWithTrace::*;
import Bscan::*;
import Vector::*;
import PPS7LIB::*;
import AxiMasterSlave::*;
import AxiDma::*;
import XilinxCells::*;
import ConnectalXilinxCells::*;
import ConnectalClocks::*;
import AxiBits::*;
import AxiGather::*;

(* always_ready, always_enabled *)
interface Bidir#(numeric type data_width);
    method Action             i(Bit#(data_width) v);
    method Bit#(data_width)   o();
    method Bit#(data_width)   t();
endinterface

interface PS7LIB;
`ifdef PS7EXTENDED
    interface Vector#(2, Pps7Emiocan)  can;
    interface Vector#(4, Pps7Dma)  dma;
    interface Vector#(2, Pps7Emioenet) enet;
    interface Pps7Event            event_;
//    interface Vector#(4,Pps7Fclk_clktrig)fclk_clktrig;
//    interface Pps7Fpga             fpga;
    interface Pps7Ftmd             ftmd;
    interface Pps7Ftmt             ftmt;
    interface Pps7Emiopjtag            pjtag;
    interface Vector#(2, Pps7Emiosdio) sdio;
    interface Vector#(2, Pps7Emiospi)  spi;
//    interface Pps7Sram             sram;
    interface Pps7Emiotrace            trace;
    interface Vector#(2, Pps7Emiottc)  ttc;
    interface Vector#(2, Pps7Emiouart) uart;
    interface Vector#(2, Pps7Emiousb)  usb;
    interface Pps7Emiowdt              wdt;
`endif
    interface Pps7Ddr              ddr;
    method Bit#(4)     fclkclk();
    method Action      fclkclktrign(Bit#(4) v);
    method Bit#(4)     fclkresetn();
    method Action      fpgaidlen(Bit#(1) v);
    interface Pps7Emiogpio             gpio;
    interface Vector#(2, Pps7Emioi2c)  i2c;
    interface Pps7Irq              irq;
    interface Inout#(Bit#(54))     mio;
    interface Pps7Ps               ps;

    interface Vector#(2, AxiMasterCommon#(32,32,12)) m_axi_gp;
    interface Vector#(2, AxiSlaveCommon#(32,32,6,Empty)) s_axi_gp;
    interface Vector#(4, AxiSlaveCommon#(32,64,6,HPType)) s_axi_hp;
    interface AxiSlaveCommon#(32,64,3,ACPType) s_axi_acp;
endinterface

module mkPS7LIB#(Clock axi_clock, Reset axi_reset)(PS7LIB);
    PPS7LIB foo <- mkPPS7LIB(
        axi_clock, axi_clock, axi_clock, axi_clock, axi_clock, axi_clock, axi_clock, axi_clock,
        axi_clock, axi_reset, axi_reset, axi_reset, axi_reset, axi_reset, axi_reset, axi_reset,
        axi_reset, axi_reset);
`ifdef PS7EXTENDED
    Vector#(2, Pps7Emiocan)     vcan;
    Vector#(4, Pps7Dma)     vdma;
    Vector#(2, Pps7Emioenet)     venet;
    Vector#(2, Pps7Emiosdio)     vsdio;
    Vector#(2, Pps7Emiospi)     vspi;
    Vector#(2, Pps7Emiottc)     vttc;
    Vector#(2, Pps7Emiouart)     vuart;
    Vector#(2, Pps7Emiousb)     vusb;
`endif
    Vector#(2, Pps7Emioi2c)     vi2c;
    Vector#(2, AxiMasterCommon#(32,32,12)) vtopm_axi_gp;
    Vector#(2, AxiSlaveCommon#(32,32,6,Empty)) vtops_axi_gp;
    Vector#(1, AxiSlaveCommon#(32,64,3,ACPType)) vtops_axi_acp;
    Vector#(4, AxiSlaveCommon#(32,64,6,HPType)) vtops_axi_hp;

`ifdef PS7EXTENDED
    vcan[0] = foo.emiocan0;
    vcan[1] = foo.emiocan1;
    vdma[0] = foo.dma0;
    vdma[1] = foo.dma1;
    vdma[2] = foo.dma2;
    vdma[3] = foo.dma3;
    venet[0] = foo.emioenet0;
    venet[1] = foo.emioenet1;
    vsdio[0] = foo.emiosdio0;
    vsdio[1] = foo.emiosdio1;
    vspi[0] = foo.emiospi0;
    vspi[1] = foo.emiospi1;
    vttc[0] = foo.emiottc0;
    vttc[1] = foo.emiottc1;
    vuart[0] = foo.emiouart0;
    vuart[1] = foo.emiouart1;
    vusb[0] = foo.emiousb0;
    vusb[1] = foo.emiousb1;
`endif
    vi2c[0] = foo.emioi2c0;
    vi2c[1] = foo.emioi2c1;
    vtopm_axi_gp[0] <- mkAxi3MasterGather(foo.maxigp0, clocked_by axi_clock, reset_by axi_reset);
    vtopm_axi_gp[1] <- mkAxi3MasterGather(foo.maxigp1, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_gp[0] <- mkAxi3SlaveGather(foo.saxigp0, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_gp[1] <- mkAxi3SlaveGather(foo.saxigp1, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_acp[0] <- mkAxi3SlaveGather(foo.saxiacp, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_hp[0] <- mkAxi3SlaveGather(foo.saxihp0, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_hp[1] <- mkAxi3SlaveGather(foo.saxihp1, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_hp[2] <- mkAxi3SlaveGather(foo.saxihp2, clocked_by axi_clock, reset_by axi_reset);
    vtops_axi_hp[3] <- mkAxi3SlaveGather(foo.saxihp3, clocked_by axi_clock, reset_by axi_reset);
    Wire#(Bit#(1)) fpgaidlenw <- mkDWire(1);
    rule fpgaidle;
       foo.fpgaidlen(fpgaidlenw);
    endrule
    rule misc;
       foo.emiosramintin(0);
       // UG585 "fclkclktrign is currently not supported and must be tied to ground"
       foo.fclkclktrign(0);
    endrule

`ifdef PS7EXTENDED
    interface Pps7Can can = vcan;
    interface Pps7Dma     dma = vdma;
    interface Pps7Enet     enet = venet;
    interface Pps7Sdio    sdio = vsdio;
    interface Pps7Spi    spi = vspi;
    interface Pps7Ttc    ttc = vttc;
    interface Pps7Uart    uart = vuart;
    interface Pps7Usb    usb = vusb;
    interface Pps7Event     event_ = foo.event_;
//    interface Pps7Fpga     fpga = foo.fpga;
    interface Pps7Ftmd     ftmd = foo.ftmd;
    interface Pps7Ftmt     ftmt = foo.ftmt;
    interface Pps7Pjtag     pjtag = foo.emiopjtag;
//    interface Pps7Sram     sram = foo.sram;
    interface Pps7Emiotrace     trace = foo.emiotrace;
    interface Pps7Wdt     wdt = foo.emiowdt;
`endif
    interface i2c = vi2c;
    interface ddr = foo.ddr;
    interface fclkclk = foo.fclkclk;
    interface fclkresetn = foo.fclkresetn;
    method Action      fclkclktrign(Bit#(4) v);
        foo.fclkclktrign(v);
    endmethod
    method Action      fpgaidlen(Bit#(1) v);
       fpgaidlenw <= v;
    endmethod
    interface gpio = foo.emiogpio;
    interface irq = foo.irq;
    interface mio = foo.mio;
    interface ps = foo.ps;

    interface m_axi_gp = vtopm_axi_gp;
    interface s_axi_gp = vtops_axi_gp;
    interface s_axi_hp = vtops_axi_hp;
    interface s_axi_acp = vtops_axi_acp[0];
endmodule

interface ZynqPins;
    (* prefix="DDR_Addr" *) interface Inout#(Bit#(15))     a;
    (* prefix="DDR_BankAddr" *) interface Inout#(Bit#(3))     ba;
    (* prefix="DDR_CAS_n" *) interface Inout#(Bit#(1))     casb;
    (* prefix="DDR_CKE" *) interface Inout#(Bit#(1))     cke;
    (* prefix="DDR_CS_n" *) interface Inout#(Bit#(1))     csb;
    (* prefix="DDR_Clk_n" *) interface Inout#(Bit#(1))     ckn;
    (* prefix="DDR_Clk_p" *) interface Inout#(Bit#(1))     ck;
    (* prefix="DDR_DM" *) interface Inout#(Bit#(4))     dm;
    (* prefix="DDR_DQ" *) interface Inout#(Bit#(32))     dq;
    (* prefix="DDR_DQS_n" *) interface Inout#(Bit#(4))     dqsn;
    (* prefix="DDR_DQS_p" *) interface Inout#(Bit#(4))     dqs;
    (* prefix="DDR_DRSTB" *) interface Inout#(Bit#(1))     drstb;
    (* prefix="DDR_ODT" *) interface Inout#(Bit#(1))     odt;
    (* prefix="DDR_RAS_n" *) interface Inout#(Bit#(1))     rasb;
    (* prefix="FIXED_IO_ddr_vrn" *) interface Inout#(Bit#(1))     vrn;
    (* prefix="FIXED_IO_ddr_vrp" *) interface Inout#(Bit#(1))     vrp;
    (* prefix="DDR_WEB" *) interface Inout#(Bit#(1))     web;
    (* prefix="MIO" *)
    interface Inout#(Bit#(54))       mio;
    (* prefix="FIXED_IO_ps" *)
    interface Pps7Ps ps;
endinterface

interface PS7;
    (* prefix="" *)
    interface ZynqPins pins;
    interface Vector#(2, AxiMasterCommon#(32,32,12))     m_axi_gp;
    interface Vector#(2, AxiSlaveCommon#(32,32,6,Empty)) s_axi_gp;
    interface Vector#(4, AxiSlaveCommon#(32,64,6,HPType))   s_axi_hp;
    interface Vector#(1, AxiSlaveCommon#(32,64,3,ACPType))   s_axi_acp;
    method Action                             interrupt(Bit#(1) v);
    interface Vector#(4, Clock) fclkclk;
    interface Vector#(4, Reset) fclkreset;
    interface Vector#(2, Pps7Emioi2c)  i2c;
    interface Clock portalClock;
    interface Reset portalReset;
    interface Clock derivedClock;
    interface Reset derivedReset;
`ifdef PS7EXTENDED
    interface Pps7Emiosdio emiosdio1;
    interface Pps7Emiospi  emiospi0;
`endif
endinterface

module mkPS7#(Clock axiClock)(PS7);
   // B2C converts a bit to a clock, enabling us to break the apparent cycle
   Vector#(4, B2C) b2c <- replicateM(mkB2C());

   // need the bufg here to reduce clock skew
   module mkBufferedClock#(Integer i)(Clock); let c <- mkClockBUFG(clocked_by b2c[i].c); return c; endmodule
   module mkBufferedReset#(Integer i)(Reset); let r <- mkResetBUFG(clocked_by b2c[i].c, reset_by b2c[i].r); return r; endmodule
   Vector#(4, Clock) fclk <- genWithM(mkBufferedClock);
   Vector#(4, Reset) freset <- genWithM(mkBufferedReset);

`ifndef TOP_SOURCES_PORTAL_CLOCK
   Clock single_clock = fclk[0];
`ifdef ZYNQ_NO_RESET
   freset[0]          = noReset;
`endif
   let single_reset   = freset[0];
`else
   //Clock axiClockBuf <- mkClockBUFG(clocked_by axiClock);
   Clock axiClockBuf = axiClock;
   Clock single_clock = axiClockBuf;
   Reset axiResetUnbuffered <- mkSyncReset(10, freset[0], single_clock);
   Reset axiReset <- mkResetBUFG(clocked_by axiClockBuf, reset_by axiResetUnbuffered);
   let single_reset   = axiReset;
`endif

   ClockGenerator7Params clockParams = defaultValue;
   // input clock 200MHz for speed grade -2, 100MHz for speed grade -1
   // fpll needs to be in the range 600MHz - 1200MHz for either input clock
   //
   // fclkin = 1e9 / mainClockPeriod
   // fpll = 1e9 = mult_f * 1e9 / mainClockPeriod
   // mult_f = mainClockPeriod
   //
   // fclkout0 = 1e9 / divide_f = 1e9 / derivedClockPeriod
   // divide_f = derivedClockPeriod
   //
   clockParams.clkfbout_mult_f       = mainClockPeriod;
   clockParams.clkfbout_phase     = 0.0;
   clockParams.clkfbout_phase     = 0.0;
   clockParams.clkin1_period      = mainClockPeriod;
   clockParams.clkout0_divide_f   = derivedClockPeriod;
   clockParams.clkout0_duty_cycle = 0.5;
   clockParams.clkout0_phase      = 0.0000;
   clockParams.clkout0_buffer     = True;
   clockParams.clkin_buffer = False;
   ClockGenerator7   clockGen <- mkClockGenerator7(clockParams, clocked_by single_clock, reset_by single_reset);
   let derived_clock = clockGen.clkout0;
   let derived_reset_unbuffered <- mkSyncReset(10, single_reset, derived_clock);
   let derived_reset <- mkResetBUFG(clocked_by derived_clock, reset_by derived_reset_unbuffered);

   PS7LIB ps7 <- mkPS7LIB(single_clock, single_reset, clocked_by single_clock, reset_by single_reset);

   // this rule connects the fclkclk wires to the clock net via B2C
   for (Integer i = 0; i < 4; i = i + 1) begin
      ReadOnly#(Bit#(4)) fclkb;
      ReadOnly#(Bit#(4)) fclkresetnb;
      fclkb       <- mkNullCrossingWire(b2c[i].c, ps7.fclkclk);
      fclkresetnb <- mkNullCrossingWire(b2c[i].c, ps7.fclkresetn);
`ifndef BSV_POSITIVE_RESET
      let resetValue = 0;
`else
      let resetValue = 1;
`endif
      rule b2c_rule1;
	 b2c[i].inputclock(fclkb[i]);
	 b2c[i].inputreset(fclkresetnb[i] == 0 ? resetValue : ~resetValue);
      endrule
      rule issue_rule;
         ps7.s_axi_hp[i].extra.rdissuecap1en(0);
         ps7.s_axi_hp[i].extra.wrissuecap1en(0);
      endrule
   end

   IDELAYCTRL idel <- mkIDELAYCTRL(2, clocked_by fclk[3], reset_by freset[0]);

    rule arb_rule;
        ps7.ddr.arb(4'b0);
    endrule

`ifdef PS7EXTENDED
    interface Pps7Emiosdio emiosdio1 = ps7.sdio[1];
    interface Pps7Emiospi  emiospi0  = ps7.spi[0];
`endif
    interface ZynqPins pins;
    interface a = ps7.ddr.a;
    interface ba = ps7.ddr.ba;
    interface casb = ps7.ddr.casb;
    interface cke = ps7.ddr.cke;
    interface csb = ps7.ddr.csb;
    interface ckn = ps7.ddr.ckn;
    interface ck = ps7.ddr.ckp;
    interface dm = ps7.ddr.dm;
    interface dq = ps7.ddr.dq;
    interface dqsn = ps7.ddr.dqsn;
    interface dqs = ps7.ddr.dqsp;
    interface drstb = ps7.ddr.drstb;
    interface odt = ps7.ddr.odt;
    interface rasb = ps7.ddr.rasb;
    interface vrn = ps7.ddr.vrn;
    interface vrp = ps7.ddr.vrp;
    interface web = ps7.ddr.web;
    interface mio = ps7.mio;
    interface ps = ps7.ps;
    endinterface
    interface m_axi_gp = ps7.m_axi_gp;
    interface s_axi_gp = ps7.s_axi_gp;
    interface s_axi_hp = ps7.s_axi_hp;
    interface fclkclk = fclk;
    interface fclkreset = freset;
`ifndef TOP_SOURCES_PORTAL_CLOCK
    interface portalClock = fclk[0];
    interface portalReset = freset[0];
`else
    interface portalClock = axiClockBuf;
    interface portalReset = axiReset;
`endif
    interface derivedClock = derived_clock;
    interface derivedReset = derived_reset;
    method Action interrupt(Bit#(1) v);
        ps7.irq.f2p({19'b0, v});
    endmethod
    interface i2c = ps7.i2c;
   interface s_axi_acp = cons(ps7.s_axi_acp, nil);
endmodule


neglected to connect the inputs

neglected to connect the inputs

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

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

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

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

import Clocks::*;
import Vector::*;
import FIFO::*;
import FIFOF::*;
import BRAMFIFO::*;
import CBus::*; // extendNP and truncateNP

import Arith::*;

(* always_ready, always_enabled *)
interface X7FifoSyncMacro#(numeric type data_width);
   method Bit#(1) empty();
   method Bit#(1) full();
   method Action din(Bit#(data_width) v);
   method Action wren(Bit#(1) wren);
   method Bit#(data_width) dout();
   method Action rden(Bit#(1) rden);
   method Bit#(9) wrcount();
   method Bit#(9) rdcount();
endinterface

import "BVI" FIFO_DUALCLOCK_MACRO =
module  vmkBramFifo#(Clock wrclk, String fifo_size)(X7FifoSyncMacro#(data_width));
   parameter DEVICE = "7SERIES";
   parameter DATA_WIDTH = valueOf(data_width);
   parameter FIFO_SIZE = fifo_size;
   parameter FIRST_WORD_FALL_THROUGH = 1;
   default_clock clk(RDCLK);
   default_reset rst(RST);
   input_clock wrclk(WRCLK) = wrclk;
   method EMPTY empty();
   method FULL full();
   method din(DI) enable ((*inhigh*)EN_di);
   method wren(WREN) enable ((*inhigh*)EN_wren);
   method DO dout();
   method rden(RDEN) enable ((*inhigh*)EN_rden);
   // wrcount and rdcount ports are needed for xsim
   method WRCOUNT wrcount();
   method RDCOUNT rdcount();
   schedule (empty, full, dout, din, wren, rden, wrcount, rdcount) CF (empty, full, dout, din, wren, rden, wrcount, rdcount);
endmodule

module mkSizedBRAMFIFOF#(Integer m)(FIFOF#(t))
   provisos (Bits#(t,sizet),
	     Add#(1,a__,sizet));
   String fifo_size = "36Kb";
   let defaultClock <- exposeCurrentClock;
   let defaultReset <- exposeCurrentReset;
   let invertReset <- mkResetInverter(defaultReset);
   Vector#(TDiv#(sizet,72),X7FifoSyncMacro#(72)) fifos <- replicateM(vmkBramFifo(defaultClock, fifo_size, reset_by invertReset));
   Wire#(Bit#(1)) rdenWire <- mkDWire(0);
   Wire#(Bit#(1)) wrenWire <- mkDWire(0);
   Vector#(TDiv#(sizet,72),Wire#(Bit#(72))) dinWires <- replicateM(mkDWire(0));

   for (Integer i = 0; i < valueOf(TDiv#(sizet,72)); i = i+1) begin
      Reg#(Bit#(9)) rdcount <- mkReg(0);
      Reg#(Bit#(9)) wrcount <- mkReg(0);
      rule enables;
	 fifos[i].rden(rdenWire);
	 fifos[i].wren(wrenWire);
      endrule
      rule inputs;
	 fifos[i].din(dinWires[i]);
      endrule
      rule counts;
	 rdcount <= fifos[i].rdcount();
	 wrcount <= fifos[i].wrcount();
      endrule
   end

   function Bool fifoNotEmpty(Integer i); return fifos[i].empty == 0; endfunction
   function Bool fifoNotFull(Integer i); return fifos[i].full == 0; endfunction
   Vector#(TDiv#(sizet,72), Bool) vNotEmpty = genWith(fifoNotEmpty);
   Vector#(TDiv#(sizet,72), Bool) vNotFull = genWith(fifoNotFull);

   method t first() if (fifos[0].empty == 0);
      function Bit#(72) fifoFirst(Integer i); return fifos[i].dout(); endfunction
      Vector#(TDiv#(sizet,72), Bit#(72)) v = genWith(fifoFirst);
      return unpack(truncateNP(pack(v)));
   endmethod
   method Action deq() if (fifos[0].empty == 0);
      rdenWire <= 1;
   endmethod
   method notEmpty = (fifos[0].empty == 0);
   method Action enq(t v) if (fifos[0].full == 0);
      Vector#(TDiv#(sizet,72), Bit#(72)) vs = unpack(extendNP(pack(v)));
      Vector#(TDiv#(sizet,72), Integer) indices = genVector();
      function Action fifoEnq(Integer i); action dinWires[i] <= vs[i]; endaction endfunction
      let unused <- mapM(fifoEnq, indices);
      wrenWire <= 1;
   endmethod
   method notFull = (fifos[0].full == 0);
endmodule

module mkSizedBRAMFIFO#(Integer m)(FIFO#(t))
   provisos (Bits#(t,sizet),
	     Add#(1,a__,sizet));
   let fifo <- mkSizedBRAMFIFOF(m);
   method first = fifo.first;
   method deq = fifo.deq;
   method enq = fifo.enq;
endmodule

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

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

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

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

import Clocks::*;
import Vector::*;
import FIFO::*;
import FIFOF::*;
import BRAMFIFO::*;
import CBus::*; // extendNP and truncateNP

import Arith::*;

(* always_ready, always_enabled *)
interface X7FifoSyncMacro#(numeric type data_width);
   method Bit#(1) empty();
   method Bit#(1) full();
   method Action din(Bit#(data_width) v);
   method Action wren(Bit#(1) wren);
   method Bit#(data_width) dout();
   method Action rden(Bit#(1) rden);
   method Bit#(9) wrcount();
   method Bit#(9) rdcount();
endinterface

import "BVI" FIFO_DUALCLOCK_MACRO =
module  vmkBramFifo#(Clock wrclk, String fifo_size)(X7FifoSyncMacro#(data_width));
   parameter DEVICE = "7SERIES";
   parameter DATA_WIDTH = valueOf(data_width);
   parameter FIFO_SIZE = fifo_size;
   parameter FIRST_WORD_FALL_THROUGH = 1;
   default_clock clk(RDCLK);
   default_reset rst(RST);
   input_clock wrclk(WRCLK) = wrclk;
   method EMPTY empty();
   method FULL full();
   method din(DI) enable ((*inhigh*)EN_di);
   method wren(WREN) enable ((*inhigh*)EN_wren);
   method DO dout();
   method rden(RDEN) enable ((*inhigh*)EN_rden);
   // wrcount and rdcount ports are needed for xsim
   method WRCOUNT wrcount();
   method RDCOUNT rdcount();
   schedule (empty, full, dout, din, wren, rden, wrcount, rdcount) CF (empty, full, dout, din, wren, rden, wrcount, rdcount);
endmodule

module mkSizedBRAMFIFOF#(Integer m)(FIFOF#(t))
   provisos (Bits#(t,sizet),
	     Add#(1,a__,sizet));
   String fifo_size = "36Kb";
   let defaultClock <- exposeCurrentClock;
   let defaultReset <- exposeCurrentReset;
   let invertReset <- mkResetInverter(defaultReset);
   Vector#(TDiv#(sizet,72),X7FifoSyncMacro#(72)) fifos <- replicateM(vmkBramFifo(defaultClock, fifo_size, reset_by invertReset));
   Wire#(Bit#(1)) rdenWire <- mkDWire(0);
   Wire#(Bit#(1)) wrenWire <- mkDWire(0);
   Vector#(TDiv#(sizet,72),Wire#(Bit#(72))) dinWires <- replicateM(mkDWire(0));

   for (Integer i = 0; i < valueOf(TDiv#(sizet,72)); i = i+1) begin
      Reg#(Bit#(9)) rdcount <- mkReg(0);
      Reg#(Bit#(9)) wrcount <- mkReg(0);
      rule enables;
	 fifos[i].rden(rdenWire);
	 fifos[i].wren(wrenWire);
      endrule
      rule counts;
	 rdcount <= fifos[i].rdcount();
	 wrcount <= fifos[i].wrcount();
      endrule
   end

   function Bool fifoNotEmpty(Integer i); return fifos[i].empty == 0; endfunction
   function Bool fifoNotFull(Integer i); return fifos[i].full == 0; endfunction
   Vector#(TDiv#(sizet,72), Bool) vNotEmpty = genWith(fifoNotEmpty);
   Vector#(TDiv#(sizet,72), Bool) vNotFull = genWith(fifoNotFull);

   method t first() if (fifos[0].empty == 0);
      function Bit#(72) fifoFirst(Integer i); return fifos[i].dout(); endfunction
      Vector#(TDiv#(sizet,72), Bit#(72)) v = genWith(fifoFirst);
      return unpack(truncateNP(pack(v)));
   endmethod
   method Action deq() if (fifos[0].empty == 0);
      rdenWire <= 1;
   endmethod
   method notEmpty = (fifos[0].empty == 0);
   method Action enq(t v) if (fifos[0].full == 0);
      Vector#(TDiv#(sizet,72), Bit#(72)) vs = unpack(extendNP(pack(v)));
      Vector#(TDiv#(sizet,72), Integer) indices = genVector();
      function Action fifoEnq(Integer i); action dinWires[i] <= vs[i]; endaction endfunction
      let unused <- mapM(fifoEnq, indices);
      wrenWire <= 1;
   endmethod
   method notFull = (fifos[0].full == 0);
endmodule

module mkSizedBRAMFIFO#(Integer m)(FIFO#(t))
   provisos (Bits#(t,sizet),
	     Add#(1,a__,sizet));
   let fifo <- mkSizedBRAMFIFOF(m);
   method first = fifo.first;
   method deq = fifo.deq;
   method enq = fifo.enq;
endmodule


combine client-specific response handling into per-client rule

// 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 >>8) > fromInteger(valueOf(MaxNumSGLists)));
endfunction

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

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

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

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

typedef struct {DmaErrorType errorType;
		Bit#(32) pref; } DmaError deriving (Bits);

module mkMemReadInternal#(Integer id,
			  Vector#(numClients, ObjectReadClient#(dataWidth)) readClients,
			  DmaIndication dmaIndication,
			  Server#(ReqTup,Bit#(addrWidth)) sgl)
   (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#(numClients), 6)
      );

   // stage 0: address translation (latency = SGL_PIPELINE_DEPTH)
   FIFO#(LRec#(numClients,addrWidth)) lreqFifo <- mkSizedFIFO(valueOf(SGL_PIPELINE_DEPTH));
   // stage 1: address validation (latency = 1)
   FIFO#(RRec#(numClients,addrWidth))  reqFifo <- mkFIFO;
   // stage 2: read commands (maximum buffering to handle high latency read response times)
   Vector#(numClients, FIFOF#(DRec#(numClients,addrWidth))) dreqFifos <- replicateM(mkSizedBRAMFIFOF(valueOf(TAG_DEPTH)));
   // stage 3: read data (minimal buffering required)
   Vector#(numClients, FIFO#(Tuple2#(DRec#(numClients,addrWidth),MemData#(dataWidth)))) readDataPipelineFifo <- replicateM(mkFIFO);
   FIFO#(MemData#(dataWidth)) responseFifo <- mkFIFO;
   Vector#(numClients, Reg#(Bit#(8)))           burstRegs <- replicateM(mkReg(0));
   Vector#(numClients, Reg#(Bool))              firstRegs <- replicateM(mkReg(True));
   Vector#(numClients, Reg#(Bool))               lastRegs <- replicateM(mkReg(False));
   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

   FIFO#(DmaError) dmaErrorFifo <- mkFIFO();
   rule dmaError;
      let error <- toGet(dmaErrorFifo).get();
      dmaIndication.dmaError(extend(pack(error.errorType)), error.pref, 0, 0);
   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))
	    dmaErrorFifo.enq(DmaError { errorType: DmaErrorBadPointer4, pref: req.pointer });
   	 else begin
   	    lreqFifo.enq(LRec{req:req, client:fromInteger(selectReg)});
   	    sgl.request.put(ReqTup{id:truncate(req.pointer),off:req.offset});
   	 end
      endrule

   rule checkSglResp;
      let physAddr <- sgl.response.get;
      let req = lreqFifo.first.req;
      let client = lreqFifo.first.client;
      let rename_tag  = extend(client);
      lreqFifo.deq();
      reqFifo.enq(RRec{req:req, pa:physAddr, client:client, rename_tag:rename_tag});
      //$display("checkSglResp: client=%d, rename_tag=%d", client,rename_tag);
      //$display("mkMemReadInternal::sglResp %d %d", client, cycle_cnt-last_sglResp);
      //last_sglResp <= cycle_cnt;
   endrule

   rule read_client_response;
      let response <- toGet(responseFifo).get();
      beatCount <= beatCount+1;
      Bit#(6) response_tag = response.tag;
      let dreqFifo = dreqFifos[response_tag];
      dynamicAssert(truncate(response_tag) == dreqFifo.first.rename_tag, "mkMemReadInternal");
      readDataPipelineFifo[response_tag].enq(tuple2(dreqFifo.first, response));
   endrule

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

       Bit#(6) response_tag = fromInteger(client);
      let dreqFifo = dreqFifos[response_tag];
      let burstLen = burstRegs[response_tag];
      let first =    firstRegs[response_tag];
      let last  =    lastRegs[response_tag];
      if (first) begin
	 burstLen = dreqFifo.first.req.burstLen >> beat_shift;
	 last = dreqFifo.first.last;
	 dynamicAssert(last == (burstLen==1), "Last incorrect");
	 //$display("burstLen=%d dreqFifo.first.last=%d last=%d\n", burstLen, dreqFifo.first.last, last);
      end
      if (last) begin
	 //$display("mkMemReadInternal::eob %d", cycle_cnt-last_eob);
	 last_eob <= cycle_cnt;
	 dreqFifo.deq();
      end
      burstRegs[response_tag] <= burstLen-1;
      firstRegs[response_tag] <= (burstLen-1 == 0);
      lastRegs[response_tag] <= (burstLen-1 == 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, last:(req.burstLen == fromInteger(valueOf(dataWidthBytes)))});
	    //$display("readReq: client=%d, rename_tag=%d, physAddr=%h req.burstLen=%d beat_shift=%d last=%d", client,rename_tag,physAddr, req.burstLen, beat_shift, req.burstLen == beat_shift);
	    return MemRequest{addr:physAddr, burstLen:req.burstLen, tag: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#(ReqTup,Bit#(addrWidth)) sgl)
   (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#(numClients), 6));

   // stage 0: address translation (latency = SGL_PIPELINE_DEPTH)
   FIFO#(LRec#(numClients,addrWidth)) lreqFifo <- mkSizedFIFO(valueOf(SGL_PIPELINE_DEPTH));
   // stage 1: address validation (latency = 1)
   FIFO#(RRec#(numClients,addrWidth))  reqFifo <- mkFIFO;
   // stage 2: write commands (maximum buffering to handle high latency writes)
   FIFO#(DRec#(numClients, addrWidth)) dreqFifo <- mkSizedBRAMFIFO(valueOf(TMul#(TAG_DEPTH,numClients)));
   // 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#(numClients, FIFO#(RResp#(numClients,addrWidth))) respFifos <- replicateM(mkSizedBRAMFIFO(valueOf(TAG_DEPTH)));
   // stage 4: write done (minimal buffering required)
   FIFO#(Tuple2#(RResp#(numClients,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

   FIFO#(DmaError) dmaErrorFifo <- mkFIFO();
   rule dmaError;
      let error <- toGet(dmaErrorFifo).get();
      dmaIndication.dmaError(extend(pack(error.errorType)), error.pref, 0, 0);
   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))
	     dmaErrorFifo.enq(DmaError { errorType: DmaErrorBadPointer5, pref: req.pointer });
   	  else begin
   	     lreqFifo.enq(LRec{req:req, client:fromInteger(selectReg)});
   	     sgl.request.put(ReqTup{id:truncate(req.pointer),off:req.offset});
   	  end
       endrule

   rule checkSglResp;
      let physAddr <- sgl.response.get;
      let req = lreqFifo.first.req;
      let client = lreqFifo.first.client;
      let rename_tag  = client;
      lreqFifo.deq();
      reqFifo.enq(RRec{req:req, pa:physAddr, client:client, rename_tag:extend(rename_tag)});
      //$display("checkSglResp: client=%d, rename_tag=%d", client,rename_tag);
      //$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);
   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, last: False });
	    //$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 >>8) > fromInteger(valueOf(MaxNumSGLists)));
endfunction

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

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

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

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

typedef struct {DmaErrorType errorType;
		Bit#(32) pref; } DmaError deriving (Bits);

module mkMemReadInternal#(Integer id,
			  Vector#(numClients, ObjectReadClient#(dataWidth)) readClients,
			  DmaIndication dmaIndication,
			  Server#(ReqTup,Bit#(addrWidth)) sgl)
   (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#(numClients), 6)
      );

   // stage 0: address translation (latency = SGL_PIPELINE_DEPTH)
   FIFO#(LRec#(numClients,addrWidth)) lreqFifo <- mkSizedFIFO(valueOf(SGL_PIPELINE_DEPTH));
   // stage 1: address validation (latency = 1)
   FIFO#(RRec#(numClients,addrWidth))  reqFifo <- mkFIFO;
   // stage 2: read commands (maximum buffering to handle high latency read response times)
   Vector#(numClients, FIFOF#(DRec#(numClients,addrWidth))) dreqFifos <- replicateM(mkSizedBRAMFIFOF(valueOf(TAG_DEPTH)));
   // stage 3: read data (minimal buffering required)
   Vector#(numClients, FIFO#(Tuple2#(DRec#(numClients,addrWidth),MemData#(dataWidth)))) readDataPipelineFifo <- replicateM(mkFIFO);
   FIFO#(MemData#(dataWidth)) responseFifo <- mkFIFO;
   Vector#(numClients, Reg#(Bit#(8)))           burstRegs <- replicateM(mkReg(0));
   Vector#(numClients, Reg#(Bool))              firstRegs <- replicateM(mkReg(True));
   Vector#(numClients, Reg#(Bool))               lastRegs <- replicateM(mkReg(False));
   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

   FIFO#(DmaError) dmaErrorFifo <- mkFIFO();
   rule dmaError;
      let error <- toGet(dmaErrorFifo).get();
      dmaIndication.dmaError(extend(pack(error.errorType)), error.pref, 0, 0);
   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))
	    dmaErrorFifo.enq(DmaError { errorType: DmaErrorBadPointer4, pref: req.pointer });
   	 else begin
   	    lreqFifo.enq(LRec{req:req, client:fromInteger(selectReg)});
   	    sgl.request.put(ReqTup{id:truncate(req.pointer),off:req.offset});
   	 end
      endrule

   rule checkSglResp;
      let physAddr <- sgl.response.get;
      let req = lreqFifo.first.req;
      let client = lreqFifo.first.client;
      let rename_tag  = extend(client);
      lreqFifo.deq();
      reqFifo.enq(RRec{req:req, pa:physAddr, client:client, rename_tag:rename_tag});
      //$display("checkSglResp: client=%d, rename_tag=%d", client,rename_tag);
      //$display("mkMemReadInternal::sglResp %d %d", client, cycle_cnt-last_sglResp);
      //last_sglResp <= cycle_cnt;
   endrule

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

   rule read_client_response;
      let response <- toGet(responseFifo).get();
      beatCount <= beatCount+1;
      Bit#(6) response_tag = response.tag;
      let dreqFifo = dreqFifos[response_tag];
      dynamicAssert(truncate(response_tag) == dreqFifo.first.rename_tag, "mkMemReadInternal");
      readDataPipelineFifo[response_tag].enq(tuple2(dreqFifo.first, response));
      let burstLen = burstRegs[response_tag];
      let first =    firstRegs[response_tag];
      let last  =    lastRegs[response_tag];
      if (first) begin
	 burstLen = dreqFifo.first.req.burstLen >> beat_shift;
	 last = dreqFifo.first.last;
	 dynamicAssert(last == (burstLen==1), "Last incorrect");
	 //$display("burstLen=%d dreqFifo.first.last=%d last=%d\n", burstLen, dreqFifo.first.last, last);
      end
      if (last) begin
	 //$display("mkMemReadInternal::eob %d", cycle_cnt-last_eob);
	 last_eob <= cycle_cnt;
	 dreqFifo.deq();
      end
      burstRegs[response_tag] <= burstLen-1;
      firstRegs[response_tag] <= (burstLen-1 == 0);
      lastRegs[response_tag] <= (burstLen-1 == 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, last:(req.burstLen == fromInteger(valueOf(dataWidthBytes)))});
	    //$display("readReq: client=%d, rename_tag=%d, physAddr=%h req.burstLen=%d beat_shift=%d last=%d", client,rename_tag,physAddr, req.burstLen, beat_shift, req.burstLen == beat_shift);
	    return MemRequest{addr:physAddr, burstLen:req.burstLen, tag: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#(ReqTup,Bit#(addrWidth)) sgl)
   (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#(numClients), 6));

   // stage 0: address translation (latency = SGL_PIPELINE_DEPTH)
   FIFO#(LRec#(numClients,addrWidth)) lreqFifo <- mkSizedFIFO(valueOf(SGL_PIPELINE_DEPTH));
   // stage 1: address validation (latency = 1)
   FIFO#(RRec#(numClients,addrWidth))  reqFifo <- mkFIFO;
   // stage 2: write commands (maximum buffering to handle high latency writes)
   FIFO#(DRec#(numClients, addrWidth)) dreqFifo <- mkSizedBRAMFIFO(valueOf(TMul#(TAG_DEPTH,numClients)));
   // 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#(numClients, FIFO#(RResp#(numClients,addrWidth))) respFifos <- replicateM(mkSizedBRAMFIFO(valueOf(TAG_DEPTH)));
   // stage 4: write done (minimal buffering required)
   FIFO#(Tuple2#(RResp#(numClients,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

   FIFO#(DmaError) dmaErrorFifo <- mkFIFO();
   rule dmaError;
      let error <- toGet(dmaErrorFifo).get();
      dmaIndication.dmaError(extend(pack(error.errorType)), error.pref, 0, 0);
   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))
	     dmaErrorFifo.enq(DmaError { errorType: DmaErrorBadPointer5, pref: req.pointer });
   	  else begin
   	     lreqFifo.enq(LRec{req:req, client:fromInteger(selectReg)});
   	     sgl.request.put(ReqTup{id:truncate(req.pointer),off:req.offset});
   	  end
       endrule

   rule checkSglResp;
      let physAddr <- sgl.response.get;
      let req = lreqFifo.first.req;
      let client = lreqFifo.first.client;
      let rename_tag  = client;
      lreqFifo.deq();
      reqFifo.enq(RRec{req:req, pa:physAddr, client:client, rename_tag:extend(rename_tag)});
      //$display("checkSglResp: client=%d, rename_tag=%d", client,rename_tag);
      //$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);
   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, last: False });
	    //$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