Added in missing AXI wid signals

Added in missing AXI wid signals

// Copyright (c) 2012 Nokia, Inc.
// 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 RegFile::*;
import BRAMFIFO::*;
import FIFO::*;
import FIFOF::*;
import FIFOLevel::*;
import SpecialFIFOs::*;
import AxiClientServer::*;

interface AxiMasterRead#(type busWidth);
   method ActionValue#(Bit#(32)) readAddr();
   method Bit#(8) readBurstLen();
   method Bit#(3) readBurstWidth();
   method Bit#(2) readBurstType();  // drive with 2'b01
   method Bit#(3) readBurstProt(); // drive with 3'b000
   method Bit#(4) readBurstCache(); // drive with 4'b0011
   method Bit#(1) readId();

   method Action readData(Bit#(busWidth) data, Bit#(2) resp, Bit#(1) last, Bit#(1) id);
endinterface

interface AxiMasterWrite#(type busWidth, type busWidthBytes);
   method ActionValue#(Bit#(32)) writeAddr();
   method Bit#(8) writeBurstLen();
   method Bit#(3) writeBurstWidth();
   method Bit#(2) writeBurstType();  // drive with 2'b01
   method Bit#(3) writeBurstProt(); // drive with 3'b000
   method Bit#(4) writeBurstCache(); // drive with 4'b0011
   method Bit#(1) writeId();

   method ActionValue#(Bit#(busWidth)) writeData();
   method Bit#(1) writeWid();
   method Bit#(busWidthBytes) writeDataByteEnable();
   method Bit#(1) writeLastDataBeat(); // last data beat
   method Action writeResponse(Bit#(2) responseCode, Bit#(1) id);
endinterface

interface AxiMaster#(type busWidth, type busWidthBytes);
   interface AxiMasterRead#(busWidth) read;
   interface AxiMasterWrite#(busWidth, busWidthBytes) write;
endinterface

interface Axi3MasterRead#(type busWidth, type idWidth);
   method ActionValue#(Bit#(32)) readAddr();
   method Bit#(4) readBurstLen();
   method Bit#(3) readBurstWidth();
   method Bit#(2) readBurstType();  // drive with 2'b01
   method Bit#(2) readBurstProt(); // drive with 3'b000
   method Bit#(3) readBurstCache(); // drive with 4'b0011
   method Bit#(idWidth) readId();

   method Action readData(Bit#(busWidth) data, Bit#(2) resp, Bit#(1) last, Bit#(idWidth) id);
endinterface

interface Axi3MasterWrite#(type busWidth, type busWidthBytes, type idWidth);
   method ActionValue#(Bit#(32)) writeAddr();
   method Bit#(4) writeBurstLen();
   method Bit#(3) writeBurstWidth();
   method Bit#(2) writeBurstType();  // drive with 2'b01
   method Bit#(2) writeBurstProt(); // drive with 3'b000
   method Bit#(3) writeBurstCache(); // drive with 4'b0011
   method Bit#(idWidth) writeId();

   method ActionValue#(Bit#(busWidth)) writeData();
   method Bit#(idWidth) writeWid();
   method Bit#(busWidthBytes) writeDataByteEnable();
   method Bit#(1) writeLastDataBeat(); // last data beat
   method Action writeResponse(Bit#(2) responseCode, Bit#(idWidth) id);
endinterface

interface Axi3Master#(type busWidth, type busWidthBytes, type idWidth);
   interface Axi3MasterRead#(busWidth, idWidth) read;
   interface Axi3MasterWrite#(busWidth, busWidthBytes, idWidth) write;
endinterface

interface AxiSlaveRead#(type busWidth, type busWidthBytes);
   method Action readAddr(Bit#(32) addr, Bit#(8) burstLen, Bit#(3) burstWidth,
                          Bit#(2) burstType, Bit#(3) burstProt, Bit#(4) burstCache);

   method ActionValue#(Bit#(busWidth)) readData();
   method Bit#(1) last();
   // method Action readResponse(Bit#(2) responseCode);
endinterface

//FIXME: should have transaction ID's
interface AxiSlaveWrite#(type busWidth, type busWidthBytes);
   method Action writeAddr(Bit#(32) addr, Bit#(8) burstLen, Bit#(3) burstWidth,
                           Bit#(2) burstType, Bit#(3) burstProt, Bit#(4) burstCache);
   method Action writeData(Bit#(busWidth) data, Bit#(busWidthBytes) byteEnable, Bit#(1) last);
   method ActionValue#(Bit#(2)) writeResponse();
endinterface

interface AxiSlave#(type busWidth, type busWidthBytes);
   interface AxiSlaveRead#(busWidth, busWidthBytes) read;
   interface AxiSlaveWrite#(busWidth, busWidthBytes) write;
endinterface

interface Axi3SlaveRead#(type busWidth, type busWidthBytes);
   method Action readAddr(Bit#(32) addr, Bit#(4) burstLen, Bit#(3) burstWidth,
                          Bit#(2) burstType, Bit#(2) burstProt, Bit#(3) burstCache, Bit#(12) arid);

   method ActionValue#(Bit#(busWidth)) readData();
   method Bit#(1) last();
   method Bit#(12) rid();
   // method Action readResponse(Bit#(2) responseCode);
endinterface

interface Axi3SlaveWrite#(type busWidth, type busWidthBytes);
   method Action writeAddr(Bit#(32) addr, Bit#(4) burstLen, Bit#(3) burstWidth,
                           Bit#(2) burstType, Bit#(2) burstProt, Bit#(3) burstCache, Bit#(12) awid);
   method Action writeData(Bit#(busWidth) data, Bit#(busWidthBytes) byteEnable, Bit#(1) last);
   method ActionValue#(Bit#(2)) writeResponse();
   method ActionValue#(Bit#(12)) bid();
endinterface

interface Axi3Slave#(type busWidth, type busWidthBytes);
   interface Axi3SlaveRead#(busWidth, busWidthBytes) read;
   interface Axi3SlaveWrite#(busWidth, busWidthBytes) write;
endinterface

interface AxiMasterServer#(type busWidth, type busWidthBytes, type tagSize);
   method Action readAddr(Bit#(32) addr, Bit#(8) numWords);
   method Action readAddrTagged(Bit#(32) addr, Bit#(8) numWords, Bit#(tagSize) tag);
   method ActionValue#(Bit#(busWidth)) readData();
   method Bit#(tagSize) readTag();

   method Action writeAddr(Bit#(32) addr, Bit#(8) numWords);
   method Action writeData(Bit#(busWidth) data);
   method ActionValue#(Bit#(2)) writeResponse();

   interface AxiMaster#(busWidth,busWidthBytes) axi;
endinterface

typedef struct {
    Bit#(32) addr;
    Bit#(8) numWords;
} AddrBurst deriving (Bits);

module mkAxiMasterServer(AxiMasterServer#(busWidth,busWidthBytes,tagSize)) provisos(Div#(busWidth,8,busWidthBytes),Add#(1,a,busWidth));
   Reg#(Bit#(32)) wAddrReg <- mkReg(0);
   Reg#(Bit#(1)) writeIdReg <- mkReg(0);
   Reg#(Bit#(1)) readIdReg <- mkReg(0);
   FIFOF#(Bit#(busWidth)) wfifo <- mkSizedBRAMFIFOF(32);
   FIFOF#(Bit#(2)) bfifo <- mkSizedBRAMFIFOF(32);
   Reg#(Bit#(8)) wBurstCountReg <- mkReg(0);
   Reg#(Bool) wAddressPresented <- mkReg(False);

   FIFOF#(AddrBurst) raddrFifo <- mkSizedBypassFIFOF(4);
   FIFOF#(Bit#(tagSize)) tagFifo <- mkSizedFIFOF(4);
   //FIFOF#(AddrBurst) raddrFifo <- mkSizedBRAMFIFOF(8);
   let rfifoDepth = 1024;
   FIFOF#(Bit#(busWidth)) rfifo <- mkSizedBRAMFIFOF(rfifoDepth);
   Reg#(Bit#(11)) rfifoWordsAvail <- mkReg(rfifoDepth);
   PulseWire enqPulse <- mkPulseWire();
   PulseWire deqPulse <- mkPulseWire();
   Wire#(Bit#(8)) readCount <- mkDWire(0);

   rule updateWordsAvailCount;
      let avail = rfifoWordsAvail;
      if (enqPulse)
          avail = avail - extend(readCount);
      if (deqPulse)
          avail = avail + 1;
      rfifoWordsAvail <= avail;
   endrule

   method Action readAddr(Bit#(32) addr, Bit#(8) numWords);
       //$display("readAddr addr %h burstCountReg %d", addr, numWords);
       raddrFifo.enq(AddrBurst { addr: addr, numWords: numWords});
       tagFifo.enq(0);
   endmethod

   method Action readAddrTagged(Bit#(32) addr, Bit#(8) numWords, Bit#(tagSize) tag);
       //$display("readAddr addr %h burstCountReg %d", addr, numWords);
       raddrFifo.enq(AddrBurst { addr: addr, numWords: numWords});
       tagFifo.enq(tag);
   endmethod

   method ActionValue#(Bit#(busWidth)) readData() if (rfifo.notEmpty);
       //$display("axiMaster.readData %h", rfifo.first);
       deqPulse.send;
       rfifo.deq;
       tagFifo.deq;
       return rfifo.first;
   endmethod

   method Bit#(tagSize) readTag();
       return tagFifo.first;
   endmethod

   method Action writeAddr(Bit#(32) addr, Bit#(8) numWords) if (wBurstCountReg == 0);
       wAddrReg <= addr;
       wBurstCountReg <= numWords;
       wAddressPresented <= False;
   endmethod

   method Action writeData(Bit#(busWidth) data) if (wfifo.notFull);
       wfifo.enq(data);
   endmethod
   method ActionValue#(Bit#(2)) writeResponse() if (bfifo.notEmpty);
       bfifo.deq;
       return bfifo.first;
   endmethod

   interface AxiMaster axi;
       interface AxiMasterWrite write;
           method ActionValue#(Bit#(32)) writeAddr() if (wBurstCountReg != 0 && !wAddressPresented);
               wAddressPresented <= True;
               writeIdReg <= writeIdReg + 1;
               return wAddrReg;
           endmethod
           method Bit#(8) writeBurstLen();
               return wBurstCountReg-1;
           endmethod
           method Bit#(3) writeBurstWidth();
               if (valueOf(busWidth) == 32)
                   return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
               else if (valueOf(busWidth) == 64)
                   return 3'b011;
               else
                   return 3'b100;
           endmethod
           method Bit#(2) writeBurstType();  // drive with 2'b01 increment address
               return 2'b01; // increment address
           endmethod
           method Bit#(3) writeBurstProt(); // drive with 3'b000
               return 3'b000;
           endmethod
           method Bit#(4) writeBurstCache(); // drive with 4'b0011
               return 4'b0011;
           endmethod
           method Bit#(1) writeId();
               return writeIdReg;
           endmethod

           method ActionValue#(Bit#(busWidth)) writeData() if (wBurstCountReg != 0 && wfifo.notEmpty);
               wBurstCountReg <= wBurstCountReg - 1;

               let d = wfifo.first;
               wfifo.deq;
               return d;
           endmethod
           method Bit#(1) writeWid();
               return writeIdReg;
           endmethod
           method Bit#(busWidthBytes) writeDataByteEnable();
               return maxBound;
           endmethod
           method Bit#(1) writeLastDataBeat(); // last data beat
               return (wBurstCountReg == 8'd1) ? 1'b1 : 1'b0;
           endmethod

           method Action writeResponse(Bit#(2) responseCode, Bit#(1) id) if (bfifo.notFull);
               if (responseCode != 0)
                   bfifo.enq(responseCode);
           endmethod
       endinterface

       interface AxiMasterRead read;
           method ActionValue#(Bit#(32)) readAddr() if (extend(unpack(raddrFifo.first.numWords)) <= rfifoWordsAvail);
               enqPulse.send;
               readCount <= raddrFifo.first.numWords;
               raddrFifo.deq();
               readIdReg <= readIdReg + 1;
               return raddrFifo.first().addr;
           endmethod
           method Bit#(8) readBurstLen();
               return raddrFifo.first().numWords-1;
           endmethod
           method Bit#(3) readBurstWidth();
               if (valueOf(busWidth) == 32)
                   return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
               else if (valueOf(busWidth) == 64)
                   return 3'b011;
               else
                   return 3'b100;
           endmethod
           method Bit#(2) readBurstType();  // drive with 2'b01
               return 2'b01;
           endmethod
           method Bit#(3) readBurstProt(); // drive with 3'b000
               return 3'b000;
           endmethod
           method Bit#(4) readBurstCache(); // drive with 4'b0011
               return 4'b0011;
           endmethod
           method Bit#(1) readId();
               return readIdReg;
           endmethod
           method Action readData(Bit#(busWidth) data, Bit#(2) resp, Bit#(1) last, Bit#(1) id) if (rfifo.notFull);
	       //$display("axi.read.readData %h bc %d", data, rBurstCountReg);
               rfifo.enq(data);
           endmethod
       endinterface
   endinterface
endmodule

module mkNullAxiMaster(AxiMaster#(busWidth,busWidthBytes)) provisos(Div#(busWidth,8,busWidthBytes),Add#(1,a,busWidth));
   interface AxiMasterWrite write;
       method ActionValue#(Bit#(32)) writeAddr() if (False);
           return 0;
       endmethod
       method Bit#(8) writeBurstLen();
           return 0;
       endmethod
       method Bit#(3) writeBurstWidth();
           if (valueOf(busWidth) == 32)
               return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
           else if (valueOf(busWidth) == 64)
               return 3'b011;
           else
               return 3'b100;
       endmethod
       method Bit#(2) writeBurstType();  // drive with 2'b01 increment address
           return 2'b01; // increment address
       endmethod
       method Bit#(3) writeBurstProt(); // drive with 3'b000
           return 3'b000;
       endmethod
       method Bit#(4) writeBurstCache(); // drive with 4'b0011
           return 4'b0011;
       endmethod
       method Bit#(1) writeId();
           return 0;
       endmethod

       method ActionValue#(Bit#(busWidth)) writeData();
           return 0;
       endmethod
       method Bit#(1) writeWid();
           return 0;
       endmethod
       method Bit#(busWidthBytes) writeDataByteEnable();
           return maxBound;
       endmethod
       method Bit#(1) writeLastDataBeat(); // last data beat
           return 0;
       endmethod

       method Action writeResponse(Bit#(2) responseCode, Bit#(1) id);
       endmethod
   endinterface

   interface AxiMasterRead read;
       method ActionValue#(Bit#(32)) readAddr() if (False);
           return 0;
       endmethod
       method Bit#(8) readBurstLen();
           return 0;
       endmethod
       method Bit#(3) readBurstWidth();
           if (valueOf(busWidth) == 32)
               return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
           else if (valueOf(busWidth) == 64)
               return 3'b011;
           else
               return 3'b100;
       endmethod
       method Bit#(2) readBurstType();  // drive with 2'b01
           return 2'b01;
       endmethod
       method Bit#(3) readBurstProt(); // drive with 3'b000
           return 3'b000;
       endmethod
       method Bit#(4) readBurstCache(); // drive with 4'b0011
           return 4'b0011;
       endmethod
       method Bit#(1) readId();
           return 0;
       endmethod
       method Action readData(Bit#(busWidth) data, Bit#(2) resp, Bit#(1) last, Bit#(1) id);
       endmethod
   endinterface
endmodule

module mkNullAxi3Master(Axi3Master#(busWidth,busWidthBytes,idWidth)) provisos(Div#(busWidth,8,busWidthBytes),Add#(1,a,busWidth));
   interface Axi3MasterWrite write;
       method ActionValue#(Bit#(32)) writeAddr() if (False);
           return 0;
       endmethod
       method Bit#(4) writeBurstLen();
           return 0;
       endmethod
       method Bit#(3) writeBurstWidth();
           if (valueOf(busWidth) == 32)
               return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
           else if (valueOf(busWidth) == 64)
               return 3'b011;
           else
               return 3'b100;
       endmethod
       method Bit#(2) writeBurstType();  // drive with 2'b01 increment address
           return 2'b01; // increment address
       endmethod
       method Bit#(2) writeBurstProt(); // drive with 3'b000
           return 2'b00;
       endmethod
       method Bit#(3) writeBurstCache(); // drive with 4'b0011
           return 3'b011;
       endmethod
       method Bit#(idWidth) writeId();
           return 0;
       endmethod

       method ActionValue#(Bit#(busWidth)) writeData();
           return 0;
       endmethod
       method Bit#(idWidth) writeWid();
           return 0;
       endmethod
       method Bit#(busWidthBytes) writeDataByteEnable();
           return maxBound;
       endmethod
       method Bit#(1) writeLastDataBeat(); // last data beat
           return 0;
       endmethod

       method Action writeResponse(Bit#(2) responseCode, Bit#(idWidth) id);
       endmethod
   endinterface

   interface Axi3MasterRead read;
       method ActionValue#(Bit#(32)) readAddr() if (False);
           return 0;
       endmethod
       method Bit#(4) readBurstLen();
           return 0;
       endmethod
       method Bit#(3) readBurstWidth();
           if (valueOf(busWidth) == 32)
               return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
           else if (valueOf(busWidth) == 64)
               return 3'b011;
           else
               return 3'b100;
       endmethod
       method Bit#(2) readBurstType();  // drive with 2'b01
           return 2'b01;
       endmethod
       method Bit#(2) readBurstProt(); // drive with 3'b000
           return 2'b00;
       endmethod
       method Bit#(3) readBurstCache(); // drive with 4'b0011
           return 3'b011;
       endmethod
       method Bit#(idWidth) readId();
           return 0;
       endmethod
       method Action readData(Bit#(busWidth) data, Bit#(2) resp, Bit#(1) last, Bit#(idWidth) id);
       endmethod
   endinterface
endmodule

module mkAxiSlaveFromRegFile#(RegFile#(Bit#(regFileBusWidth), Bit#(busWidth)) rf)
                             (AxiSlave#(busWidth, busWidthBytes)) provisos(Div#(busWidth,8,busWidthBytes),
                                                                           Add#(nz, regFileBusWidth, 32));
    Reg#(Bit#(regFileBusWidth)) readAddrReg <- mkReg(0);
    Reg#(Bit#(regFileBusWidth)) writeAddrReg <- mkReg(0);
    Reg#(Bit#(8)) readBurstCountReg <- mkReg(0);
    Reg#(Bit#(8)) writeBurstCountReg <- mkReg(0);
    FIFO#(Bit#(2)) writeRespFifo <- mkFIFO();

    Bool verbose = False;
    interface AxiSlaveRead read;
        method Action readAddr(Bit#(32) addr, Bit#(8) burstLen, Bit#(3) burstWidth,
                               Bit#(2) burstType, Bit#(3) burstProt, Bit#(4) burstCache) if (readBurstCountReg == 0);
            if (verbose) $display("axiSlave.read.readAddr %h bc %d", addr, burstLen+1);
            readAddrReg <= truncate(addr/fromInteger(valueOf(busWidthBytes)));
            readBurstCountReg <= burstLen+1;
        endmethod

        method ActionValue#(Bit#(busWidth)) readData() if (readBurstCountReg > 0);
            let data = rf.sub(readAddrReg);
            if (verbose) $display("axiSlave.read.readData %h %h %d", readAddrReg, data, readBurstCountReg);
            readBurstCountReg <= readBurstCountReg - 1;
            readAddrReg <= readAddrReg + 1;
            return data;
        endmethod
        method Bit#(1) last();
            return (readBurstCountReg == 1) ? 1 : 0;
        endmethod
    endinterface

    interface AxiSlaveWrite write;
       method Action writeAddr(Bit#(32) addr, Bit#(8) burstLen, Bit#(3) burstWidth,
                               Bit#(2) burstType, Bit#(3) burstProt, Bit#(4) burstCache) if (writeBurstCountReg == 0);
           if (verbose) $display("axiSlave.write.writeAddr %h bc %d", addr, burstLen+1);
           writeAddrReg <= truncate(addr/fromInteger(valueOf(busWidthBytes)));
           writeBurstCountReg <= burstLen+1;
       endmethod

       method Action writeData(Bit#(busWidth) data, Bit#(busWidthBytes) byteEnable, Bit#(1) last) if (writeBurstCountReg > 0);
           if (verbose) $display("writeData %h %h %d", writeAddrReg, data, writeBurstCountReg);
           rf.upd(writeAddrReg, data);
           writeAddrReg <= writeAddrReg + 1;
           writeBurstCountReg <= writeBurstCountReg - 1;
            if (verbose) $display("axiSlave.write.writeData %h %h %d", writeAddrReg, data, writeBurstCountReg);
           if (writeBurstCountReg == 1)
               writeRespFifo.enq(0);
       endmethod
       method ActionValue#(Bit#(2)) writeResponse();
           writeRespFifo.deq;
           return writeRespFifo.first;
       endmethod
    endinterface
endmodule

module mkAxiSlaveRegFile(AxiSlave#(busWidth, busWidthBytes)) provisos(Div#(busWidth,8,busWidthBytes));
       RegFile#(Bit#(21), Bit#(busWidth)) rf <- mkRegFileFull();
       AxiSlave#(busWidth, busWidthBytes) axiSlave <- mkAxiSlaveFromRegFile(rf);
       return axiSlave;
endmodule

module mkAxiSlaveRegFileLoad#(String fileName)(AxiSlave#(busWidth, busWidthBytes)) provisos(Div#(busWidth,8,busWidthBytes));
       RegFile#(Bit#(21), Bit#(busWidth)) rf <- mkRegFileFullLoad(fileName);
       AxiSlave#(busWidth, busWidthBytes) axiSlave <- mkAxiSlaveFromRegFile(rf);
       return axiSlave;
endmodule

module mkAxi3SlaveFromRegFile#(RegFile#(Bit#(regFileBusWidth), Bit#(busWidth)) rf)
                              (Axi3Slave#(busWidth, busWidthBytes)) provisos(Div#(busWidth,8,busWidthBytes),
                                                                           Add#(nz, regFileBusWidth, 32));
    Reg#(Bit#(regFileBusWidth)) readAddrReg <- mkReg(0);
    Reg#(Bit#(regFileBusWidth)) writeAddrReg <- mkReg(0);
    Reg#(Bit#(12)) readIdReg <- mkReg(0);
    Reg#(Bit#(12)) writeIdReg <- mkReg(0);
    Reg#(Bit#(4)) readBurstCountReg <- mkReg(0);
    Reg#(Bit#(4)) writeBurstCountReg <- mkReg(0);
    FIFO#(Bit#(2)) writeRespFifo <- mkFIFO();
    FIFO#(Bit#(12)) writeIdFifo <- mkFIFO();

    Bool verbose = False;
    interface Axi3SlaveRead read;
        method Action readAddr(Bit#(32) addr, Bit#(4) burstLen, Bit#(3) burstWidth,
                               Bit#(2) burstType, Bit#(2) burstProt, Bit#(3) burstCache, Bit#(12) id) if (readBurstCountReg == 0);
            if (verbose) $display("axiSlave.read.readAddr %h bc %d", addr, burstLen+1);
            readAddrReg <= truncate(addr/fromInteger(valueOf(busWidthBytes)));
	    readIdReg <= id;
            readBurstCountReg <= burstLen+1;
        endmethod

        method ActionValue#(Bit#(busWidth)) readData() if (readBurstCountReg > 0);
            let data = rf.sub(readAddrReg);
            if (verbose) $display("axiSlave.read.readData %h %h %d", readAddrReg, data, readBurstCountReg);
            readBurstCountReg <= readBurstCountReg - 1;
            readAddrReg <= readAddrReg + 1;
            return data;
        endmethod
        method Bit#(1) last();
            return (readBurstCountReg == 1) ? 1 : 0;
        endmethod
	method Bit#(12) rid();
	    return readIdReg;
	endmethod
    endinterface

    interface Axi3SlaveWrite write;
       method Action writeAddr(Bit#(32) addr, Bit#(4) burstLen, Bit#(3) burstWidth,
                               Bit#(2) burstType, Bit#(2) burstProt, Bit#(3) burstCache, Bit#(12) id) if (writeBurstCountReg == 0);
           if (verbose) $display("axiSlave.write.writeAddr %h bc %d", addr, burstLen+1);
           writeAddrReg <= truncate(addr/fromInteger(valueOf(busWidthBytes)));
	   writeIdReg <= id;
           writeBurstCountReg <= burstLen+1;
           writeIdFifo.enq(id);
       endmethod

       method Action writeData(Bit#(busWidth) data, Bit#(busWidthBytes) byteEnable, Bit#(1) last) if (writeBurstCountReg > 0);
           if (verbose) $display("writeData %h %h %d", writeAddrReg, data, writeBurstCountReg);
           rf.upd(writeAddrReg, data);
           writeAddrReg <= writeAddrReg + 1;
           writeBurstCountReg <= writeBurstCountReg - 1;
            if (verbose) $display("axiSlave.write.writeData %h %h %d", writeAddrReg, data, writeBurstCountReg);
           if (writeBurstCountReg == 1)
	   begin
               writeRespFifo.enq(0);
           end
       endmethod
       method ActionValue#(Bit#(2)) writeResponse();
           writeRespFifo.deq;
           return writeRespFifo.first;
       endmethod
	method ActionValue#(Bit#(12)) bid();
	    writeIdFifo.deq;
	    return writeIdFifo.first;
	endmethod
    endinterface
endmodule

module mkAxi3SlaveRegFile(Axi3Slave#(busWidth, busWidthBytes)) provisos(Div#(busWidth,8,busWidthBytes));
       RegFile#(Bit#(21), Bit#(busWidth)) rf <- mkRegFileFull();
       Axi3Slave#(busWidth, busWidthBytes) axiSlave <- mkAxi3SlaveFromRegFile(rf);
       return axiSlave;
endmodule

module mkMasterSlaveConnection#(AxiMasterWrite#(busWidth, busWidthBytes) axiw,
                                AxiMasterRead#(busWidth) axir,
                                AxiSlave#(busWidth, busWidthBytes) axiSlave)
                                ();

    Reg#(Bit#(8)) writeBurstCountReg <- mkReg(0);
    Bool verbose = False;

    rule readAddr;
        Bit#(32) addr <-axir.readAddr;
        let burstLen = axir.readBurstLen;
        let burstWidth = axir.readBurstWidth;
        let burstType = axir.readBurstType;
        let burstProt = axir.readBurstProt;
        let burstCache = axir.readBurstCache;
        axiSlave.read.readAddr(addr, burstLen, burstWidth, burstType, burstProt, burstCache);
	if (verbose) $display("        MasterSlaveConnection.readAddr %h %d", addr, burstLen+1);
    endrule
    rule readData;
        let data <- axiSlave.read.readData();
        axir.readData(data, 2'b00, 0, 0);
        if (verbose) $display("        MasterSlaveConnection.readData %h", data);
    endrule
    rule writeAddr;
        Bit#(32) addr <- axiw.writeAddr;
        let burstLen = axiw.writeBurstLen;
        let burstWidth = axiw.writeBurstWidth;
        let burstType = axiw.writeBurstType;
        let burstProt = axiw.writeBurstProt;
        let burstCache = axiw.writeBurstCache;
        axiSlave.write.writeAddr(addr, burstLen, burstWidth, burstType, burstProt, burstCache);
        if (verbose) $display("        MasterSlaveConnection.writeAddr %h %d", addr, burstLen+1);
    endrule
    rule writeData;
        let data <- axiw.writeData;
	let id = axiw.writeWid;
        let byteEnable = axiw.writeDataByteEnable;
        let last = axiw.writeLastDataBeat;
        axiSlave.write.writeData(data, byteEnable, last);
        if (verbose) $display("        MasterSlaveConnection.writeData %h", data);
    endrule
    rule writeResponse;
        let response <- axiSlave.write.writeResponse();
        axiw.writeResponse(response, 0);
    endrule
endmodule

module mkClientSlaveConnection#(Axi3WriteClient#(busWidth, busWidthBytes,idWidth) axicw,
                                Axi3ReadClient#(busWidth,idWidth) axicr,
                                Axi3Slave#(busWidth, busWidthBytes) axiSlave)
                                () provisos (Div#(busWidth, 8, busWidthBytes),
				             Add#(1, a__, busWidth),
					     Add#(busWidth, b__, 65),
					     Add#(idWidth, 1, 13));

    Reg#(Bit#(8)) writeBurstCountReg <- mkReg(0);
    Bool verbose = False;

    Axi3Client#(busWidth,busWidthBytes,idWidth) axic <- mkAxi3Client(axicw, axicr);
    Axi3Master#(busWidth,busWidthBytes,idWidth) m_axiMaster <- mkAxi3Master(axic);
    let axir = m_axiMaster.read;
    let axiw = m_axiMaster.write;

    rule readAddr;
        Bit#(32) addr <-axir.readAddr;
        let burstLen = axir.readBurstLen;
        let burstWidth = axir.readBurstWidth;
        let burstType = axir.readBurstType;
        let burstProt = axir.readBurstProt;
        let burstCache = axir.readBurstCache;
	let id = axir.readId;
        axiSlave.read.readAddr(addr, burstLen, burstWidth, burstType, burstProt, burstCache, id);
	if (verbose) $display("        MasterSlaveConnection.readAddr %h %d", addr, burstLen+1);
    endrule
    rule readData;
        let data <- axiSlave.read.readData();
	let id = axiSlave.read.rid();
        axir.readData(data, 2'b00, 0, id);
        if (verbose) $display("        MasterSlaveConnection.readData %h", data);
    endrule
    rule writeAddr;
        Bit#(32) addr <- axiw.writeAddr;
        let burstLen = axiw.writeBurstLen;
        let burstWidth = axiw.writeBurstWidth;
        let burstType = axiw.writeBurstType;
        let burstProt = axiw.writeBurstProt;
        let burstCache = axiw.writeBurstCache;
	let id = axiw.writeId;
        axiSlave.write.writeAddr(addr, burstLen, burstWidth, burstType, burstProt, burstCache, id);
        if (verbose) $display("        MasterSlaveConnection.writeAddr %h %d", addr, burstLen+1);
    endrule
    rule writeData;
        let data <- axiw.writeData;
        let id = axiw.writeWid;
        let byteEnable = axiw.writeDataByteEnable;
        let last = axiw.writeLastDataBeat;
        axiSlave.write.writeData(data, byteEnable, last);
        if (verbose) $display("        MasterSlaveConnection.writeData %h", data);
    endrule
    rule writeResponse;
        let response <- axiSlave.write.writeResponse();
        let id <- axiSlave.write.bid();
        axiw.writeResponse(response, id);
    endrule
endmodule

module mkAxi3MasterWires#(Axi3Client#(busWidth,busWidthBytes,idWidth) client)(Axi3Master#(busWidth,busWidthBytes,idWidth))
	 provisos(Div#(busWidth,8,busWidthBytes),Add#(1,a,busWidth));

    Wire#(Axi3ReadRequest#(idWidth))                      wReadRequest <- mkDWire(unpack(0));
    Wire#(Axi3WriteRequest#(idWidth))                     wWriteRequest <- mkDWire(unpack(0));
    Wire#(Axi3WriteData#(busWidth,busWidthBytes,idWidth)) wWriteData <- mkDWire(unpack(0));

    interface Axi3MasterWrite write;
	method ActionValue#(Bit#(32)) writeAddr();
	    let r <- client.write.address();
	    wWriteRequest <= r;
	    return r.address;
	endmethod
	method Bit#(4) writeBurstLen();
	    return wWriteRequest.burstLen;
	endmethod
	method Bit#(3) writeBurstWidth();
	    if (valueOf(busWidth) == 32)
		return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
	    else if (valueOf(busWidth) == 64)
		return 3'b011;
	    else
		return 3'b100;
	endmethod
	method Bit#(2) writeBurstType();  // drive with 2'b01 increment address
	    return 2'b01; // increment address
	endmethod
	method Bit#(2) writeBurstProt(); // drive with 3'b000
	    return 2'b000;
	endmethod
	method Bit#(3) writeBurstCache(); // drive with 4'b0011
	    return 3'b0011;
	endmethod
	method Bit#(idWidth) writeId();
	    return wWriteRequest.id;
	endmethod

	method ActionValue#(Bit#(busWidth)) writeData();
	    let d <- client.write.data();
	    wWriteData <= d;
	    return d.data;
	endmethod
	method Bit#(idWidth) writeWid();
	    return wWriteData.id;
	endmethod
	method Bit#(busWidthBytes) writeDataByteEnable();
	    return wWriteData.byteEnable;
	endmethod
	method Bit#(1) writeLastDataBeat(); // last data beat
	    return wWriteData.last;
	endmethod

	method Action writeResponse(Bit#(2) responseCode, Bit#(idWidth) id);
	    client.write.response(Axi3WriteResponse { code: responseCode, id: id});
	endmethod
    endinterface

    interface Axi3MasterRead read;
	method ActionValue#(Bit#(32)) readAddr();
	    let r <- client.read.address();
	    wReadRequest <= r;
	    return r.address;
	endmethod
	method Bit#(4) readBurstLen();
	    return wReadRequest.burstLen;
	endmethod
	method Bit#(3) readBurstWidth();
	    if (valueOf(busWidth) == 32)
		return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
	    else if (valueOf(busWidth) == 64)
		return 3'b011;
	    else
		return 3'b100;
	endmethod
	method Bit#(2) readBurstType();  // drive with 2'b01
	    return 2'b01;
	endmethod
	method Bit#(2) readBurstProt(); // drive with 3'b000
	    return 2'b000;
	endmethod
	method Bit#(3) readBurstCache(); // drive with 4'b0011
	    return 3'b0011;
	endmethod
	method Bit#(idWidth) readId();
	    return wReadRequest.id;
	endmethod
	method Action readData(Bit#(busWidth) data, Bit#(2) code, Bit#(1) last, Bit#(idWidth) id);
	    client.read.data(Axi3ReadResponse { data: data, code: code, last: last, id: id});
	endmethod
   endinterface
endmodule

module mkAxi3Master#(Axi3Client#(busWidth,busWidthBytes,idWidth) client)(Axi3Master#(busWidth,busWidthBytes,idWidth))
	 provisos(Div#(busWidth,8,busWidthBytes),Add#(1,a,busWidth),Add#(busWidth,b,65));

    FIFOF#(Axi3ReadRequest#(idWidth))                      fReadRequest <- mkSizedBRAMFIFOF(8);
    FIFOF#(Axi3WriteRequest#(idWidth))                     fWriteRequest <- mkSizedBRAMFIFOF(8);
    FIFOF#(Axi3WriteData#(busWidth,busWidthBytes,idWidth)) fWriteData <- mkSizedBRAMFIFOF(8);

    rule writAddrRule;
        let r <- client.write.address();
	fWriteRequest.enq(r);
    endrule

    rule writeDataRule;
        let d <- client.write.data();
	fWriteData.enq(d);
    endrule

    rule readAddrRule;
        let r <- client.read.address();
	fReadRequest.enq(r);
    endrule

    interface Axi3MasterWrite write;
	method ActionValue#(Bit#(32)) writeAddr();
	    fWriteRequest.deq;
	    return fWriteRequest.first.address;
	endmethod
	method Bit#(4) writeBurstLen();
	    return fWriteRequest.first.burstLen;
	endmethod
	method Bit#(3) writeBurstWidth();
	    if (valueOf(busWidth) == 32)
		return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
	    else if (valueOf(busWidth) == 64)
		return 3'b011;
	    else
		return 3'b100;
	endmethod
	method Bit#(2) writeBurstType();  // drive with 2'b01 increment address
	    return 2'b01; // increment address
	endmethod
	method Bit#(2) writeBurstProt(); // drive with 3'b000
	    return 2'b000;
	endmethod
	method Bit#(3) writeBurstCache(); // drive with 4'b0011
	    return 3'b0011;
	endmethod
	method Bit#(idWidth) writeId();
	    return fWriteRequest.first.id;
	endmethod

	method ActionValue#(Bit#(busWidth)) writeData();
	    fWriteData.deq;
	    return fWriteData.first.data;
	endmethod
        method Bit#(idWidth) writeWid();
            return fWriteData.first.id;
        endmethod
	method Bit#(busWidthBytes) writeDataByteEnable();
	    return fWriteData.first.byteEnable;
	endmethod
	method Bit#(1) writeLastDataBeat(); // last data beat
	    return fWriteData.first.last;
	endmethod

	method Action writeResponse(Bit#(2) responseCode, Bit#(idWidth) id);
	    client.write.response(Axi3WriteResponse { code: responseCode, id: id});
	endmethod
    endinterface

    interface Axi3MasterRead read;
	method ActionValue#(Bit#(32)) readAddr();
	    fReadRequest.deq;
	    return fReadRequest.first.address;
	endmethod
	method Bit#(4) readBurstLen();
	    return fReadRequest.first.burstLen;
	endmethod
	method Bit#(3) readBurstWidth();
	    if (valueOf(busWidth) == 32)
		return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
	    else if (valueOf(busWidth) == 64)
		return 3'b011;
	    else
		return 3'b100;
	endmethod
	method Bit#(2) readBurstType();  // drive with 2'b01
	    return 2'b01;
	endmethod
	method Bit#(2) readBurstProt(); // drive with 3'b000
	    return 2'b000;
	endmethod
	method Bit#(3) readBurstCache(); // drive with 4'b0011
	    return 3'b0011;
	endmethod
	method Bit#(idWidth) readId();
	    return fReadRequest.first.id;
	endmethod
	method Action readData(Bit#(busWidth) data, Bit#(2) code, Bit#(1) last, Bit#(idWidth) id);
	    client.read.data(Axi3ReadResponse { data: data, code: code, last: last, id: id});
	endmethod
   endinterface
endmodule

// Copyright (c) 2012 Nokia, Inc.
// 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 RegFile::*;
import BRAMFIFO::*;
import FIFO::*;
import FIFOF::*;
import FIFOLevel::*;
import SpecialFIFOs::*;
import AxiClientServer::*;

interface AxiMasterRead#(type busWidth);
   method ActionValue#(Bit#(32)) readAddr();
   method Bit#(8) readBurstLen();
   method Bit#(3) readBurstWidth();
   method Bit#(2) readBurstType();  // drive with 2'b01
   method Bit#(3) readBurstProt(); // drive with 3'b000
   method Bit#(4) readBurstCache(); // drive with 4'b0011
   method Bit#(1) readId();

   method Action readData(Bit#(busWidth) data, Bit#(2) resp, Bit#(1) last, Bit#(1) id);
endinterface

interface AxiMasterWrite#(type busWidth, type busWidthBytes);
   method ActionValue#(Bit#(32)) writeAddr();
   method Bit#(8) writeBurstLen();
   method Bit#(3) writeBurstWidth();
   method Bit#(2) writeBurstType();  // drive with 2'b01
   method Bit#(3) writeBurstProt(); // drive with 3'b000
   method Bit#(4) writeBurstCache(); // drive with 4'b0011
   method Bit#(1) writeId();

   method ActionValue#(Bit#(busWidth)) writeData();
   method Bit#(busWidthBytes) writeDataByteEnable();
   method Bit#(1) writeLastDataBeat(); // last data beat
   method Action writeResponse(Bit#(2) responseCode, Bit#(1) id);
endinterface

interface AxiMaster#(type busWidth, type busWidthBytes);
   interface AxiMasterRead#(busWidth) read;
   interface AxiMasterWrite#(busWidth, busWidthBytes) write;
endinterface

interface Axi3MasterRead#(type busWidth, type idWidth);
   method ActionValue#(Bit#(32)) readAddr();
   method Bit#(4) readBurstLen();
   method Bit#(3) readBurstWidth();
   method Bit#(2) readBurstType();  // drive with 2'b01
   method Bit#(2) readBurstProt(); // drive with 3'b000
   method Bit#(3) readBurstCache(); // drive with 4'b0011
   method Bit#(idWidth) readId();

   method Action readData(Bit#(busWidth) data, Bit#(2) resp, Bit#(1) last, Bit#(idWidth) id);
endinterface

interface Axi3MasterWrite#(type busWidth, type busWidthBytes, type idWidth);
   method ActionValue#(Bit#(32)) writeAddr();
   method Bit#(4) writeBurstLen();
   method Bit#(3) writeBurstWidth();
   method Bit#(2) writeBurstType();  // drive with 2'b01
   method Bit#(2) writeBurstProt(); // drive with 3'b000
   method Bit#(3) writeBurstCache(); // drive with 4'b0011
   method Bit#(idWidth) writeId();

   method ActionValue#(Bit#(busWidth)) writeData();
   method Bit#(busWidthBytes) writeDataByteEnable();
   method Bit#(1) writeLastDataBeat(); // last data beat
   method Action writeResponse(Bit#(2) responseCode, Bit#(idWidth) id);
endinterface

interface Axi3Master#(type busWidth, type busWidthBytes, type idWidth);
   interface Axi3MasterRead#(busWidth, idWidth) read;
   interface Axi3MasterWrite#(busWidth, busWidthBytes, idWidth) write;
endinterface

interface AxiSlaveRead#(type busWidth, type busWidthBytes);
   method Action readAddr(Bit#(32) addr, Bit#(8) burstLen, Bit#(3) burstWidth,
                          Bit#(2) burstType, Bit#(3) burstProt, Bit#(4) burstCache);

   method ActionValue#(Bit#(busWidth)) readData();
   method Bit#(1) last();
   // method Action readResponse(Bit#(2) responseCode);
endinterface

interface AxiSlaveWrite#(type busWidth, type busWidthBytes);
   method Action writeAddr(Bit#(32) addr, Bit#(8) burstLen, Bit#(3) burstWidth,
                           Bit#(2) burstType, Bit#(3) burstProt, Bit#(4) burstCache);
   method Action writeData(Bit#(busWidth) data, Bit#(busWidthBytes) byteEnable, Bit#(1) last);
   method ActionValue#(Bit#(2)) writeResponse();
endinterface

interface AxiSlave#(type busWidth, type busWidthBytes);
   interface AxiSlaveRead#(busWidth, busWidthBytes) read;
   interface AxiSlaveWrite#(busWidth, busWidthBytes) write;
endinterface

interface Axi3SlaveRead#(type busWidth, type busWidthBytes);
   method Action readAddr(Bit#(32) addr, Bit#(4) burstLen, Bit#(3) burstWidth,
                          Bit#(2) burstType, Bit#(2) burstProt, Bit#(3) burstCache, Bit#(12) arid);

   method ActionValue#(Bit#(busWidth)) readData();
   method Bit#(1) last();
   method Bit#(12) rid();
   // method Action readResponse(Bit#(2) responseCode);
endinterface

interface Axi3SlaveWrite#(type busWidth, type busWidthBytes);
   method Action writeAddr(Bit#(32) addr, Bit#(4) burstLen, Bit#(3) burstWidth,
                           Bit#(2) burstType, Bit#(2) burstProt, Bit#(3) burstCache, Bit#(12) awid);
   method Action writeData(Bit#(busWidth) data, Bit#(busWidthBytes) byteEnable, Bit#(1) last);
   method ActionValue#(Bit#(2)) writeResponse();
   method ActionValue#(Bit#(12)) bid();
endinterface

interface Axi3Slave#(type busWidth, type busWidthBytes);
   interface Axi3SlaveRead#(busWidth, busWidthBytes) read;
   interface Axi3SlaveWrite#(busWidth, busWidthBytes) write;
endinterface

interface AxiMasterServer#(type busWidth, type busWidthBytes, type tagSize);
   method Action readAddr(Bit#(32) addr, Bit#(8) numWords);
   method Action readAddrTagged(Bit#(32) addr, Bit#(8) numWords, Bit#(tagSize) tag);
   method ActionValue#(Bit#(busWidth)) readData();
   method Bit#(tagSize) readTag();

   method Action writeAddr(Bit#(32) addr, Bit#(8) numWords);
   method Action writeData(Bit#(busWidth) data);
   method ActionValue#(Bit#(2)) writeResponse();

   interface AxiMaster#(busWidth,busWidthBytes) axi;
endinterface

typedef struct {
    Bit#(32) addr;
    Bit#(8) numWords;
} AddrBurst deriving (Bits);

module mkAxiMasterServer(AxiMasterServer#(busWidth,busWidthBytes,tagSize)) provisos(Div#(busWidth,8,busWidthBytes),Add#(1,a,busWidth));
   Reg#(Bit#(32)) wAddrReg <- mkReg(0);
   Reg#(Bit#(1)) writeIdReg <- mkReg(0);
   Reg#(Bit#(1)) readIdReg <- mkReg(0);
   FIFOF#(Bit#(busWidth)) wfifo <- mkSizedBRAMFIFOF(32);
   FIFOF#(Bit#(2)) bfifo <- mkSizedBRAMFIFOF(32);
   Reg#(Bit#(8)) wBurstCountReg <- mkReg(0);
   Reg#(Bool) wAddressPresented <- mkReg(False);

   FIFOF#(AddrBurst) raddrFifo <- mkSizedBypassFIFOF(4);
   FIFOF#(Bit#(tagSize)) tagFifo <- mkSizedFIFOF(4);
   //FIFOF#(AddrBurst) raddrFifo <- mkSizedBRAMFIFOF(8);
   let rfifoDepth = 1024;
   FIFOF#(Bit#(busWidth)) rfifo <- mkSizedBRAMFIFOF(rfifoDepth);
   Reg#(Bit#(11)) rfifoWordsAvail <- mkReg(rfifoDepth);
   PulseWire enqPulse <- mkPulseWire();
   PulseWire deqPulse <- mkPulseWire();
   Wire#(Bit#(8)) readCount <- mkDWire(0);

   rule updateWordsAvailCount;
      let avail = rfifoWordsAvail;
      if (enqPulse)
          avail = avail - extend(readCount);
      if (deqPulse)
          avail = avail + 1;
      rfifoWordsAvail <= avail;
   endrule

   method Action readAddr(Bit#(32) addr, Bit#(8) numWords);
       //$display("readAddr addr %h burstCountReg %d", addr, numWords);
       raddrFifo.enq(AddrBurst { addr: addr, numWords: numWords});
       tagFifo.enq(0);
   endmethod

   method Action readAddrTagged(Bit#(32) addr, Bit#(8) numWords, Bit#(tagSize) tag);
       //$display("readAddr addr %h burstCountReg %d", addr, numWords);
       raddrFifo.enq(AddrBurst { addr: addr, numWords: numWords});
       tagFifo.enq(tag);
   endmethod

   method ActionValue#(Bit#(busWidth)) readData() if (rfifo.notEmpty);
       //$display("axiMaster.readData %h", rfifo.first);
       deqPulse.send;
       rfifo.deq;
       tagFifo.deq;
       return rfifo.first;
   endmethod

   method Bit#(tagSize) readTag();
       return tagFifo.first;
   endmethod

   method Action writeAddr(Bit#(32) addr, Bit#(8) numWords) if (wBurstCountReg == 0);
       wAddrReg <= addr;
       wBurstCountReg <= numWords;
       wAddressPresented <= False;
   endmethod

   method Action writeData(Bit#(busWidth) data) if (wfifo.notFull);
       wfifo.enq(data);
   endmethod
   method ActionValue#(Bit#(2)) writeResponse() if (bfifo.notEmpty);
       bfifo.deq;
       return bfifo.first;
   endmethod

   interface AxiMaster axi;
       interface AxiMasterWrite write;
           method ActionValue#(Bit#(32)) writeAddr() if (wBurstCountReg != 0 && !wAddressPresented);
               wAddressPresented <= True;
               writeIdReg <= writeIdReg + 1;
               return wAddrReg;
           endmethod
           method Bit#(8) writeBurstLen();
               return wBurstCountReg-1;
           endmethod
           method Bit#(3) writeBurstWidth();
               if (valueOf(busWidth) == 32)
                   return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
               else if (valueOf(busWidth) == 64)
                   return 3'b011;
               else
                   return 3'b100;
           endmethod
           method Bit#(2) writeBurstType();  // drive with 2'b01 increment address
               return 2'b01; // increment address
           endmethod
           method Bit#(3) writeBurstProt(); // drive with 3'b000
               return 3'b000;
           endmethod
           method Bit#(4) writeBurstCache(); // drive with 4'b0011
               return 4'b0011;
           endmethod
           method Bit#(1) writeId();
               return writeIdReg;
           endmethod

           method ActionValue#(Bit#(busWidth)) writeData() if (wBurstCountReg != 0 && wfifo.notEmpty);
               wBurstCountReg <= wBurstCountReg - 1;

               let d = wfifo.first;
               wfifo.deq;
               return d;
           endmethod
           method Bit#(busWidthBytes) writeDataByteEnable();
               return maxBound;
           endmethod
           method Bit#(1) writeLastDataBeat(); // last data beat
               return (wBurstCountReg == 8'd1) ? 1'b1 : 1'b0;
           endmethod

           method Action writeResponse(Bit#(2) responseCode, Bit#(1) id) if (bfifo.notFull);
               if (responseCode != 0)
                   bfifo.enq(responseCode);
           endmethod
       endinterface

       interface AxiMasterRead read;
           method ActionValue#(Bit#(32)) readAddr() if (extend(unpack(raddrFifo.first.numWords)) <= rfifoWordsAvail);
               enqPulse.send;
               readCount <= raddrFifo.first.numWords;
               raddrFifo.deq();
               readIdReg <= readIdReg + 1;
               return raddrFifo.first().addr;
           endmethod
           method Bit#(8) readBurstLen();
               return raddrFifo.first().numWords-1;
           endmethod
           method Bit#(3) readBurstWidth();
               if (valueOf(busWidth) == 32)
                   return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
               else if (valueOf(busWidth) == 64)
                   return 3'b011;
               else
                   return 3'b100;
           endmethod
           method Bit#(2) readBurstType();  // drive with 2'b01
               return 2'b01;
           endmethod
           method Bit#(3) readBurstProt(); // drive with 3'b000
               return 3'b000;
           endmethod
           method Bit#(4) readBurstCache(); // drive with 4'b0011
               return 4'b0011;
           endmethod
           method Bit#(1) readId();
               return readIdReg;
           endmethod
           method Action readData(Bit#(busWidth) data, Bit#(2) resp, Bit#(1) last, Bit#(1) id) if (rfifo.notFull);
	       //$display("axi.read.readData %h bc %d", data, rBurstCountReg);
               rfifo.enq(data);
           endmethod
       endinterface
   endinterface
endmodule

module mkNullAxiMaster(AxiMaster#(busWidth,busWidthBytes)) provisos(Div#(busWidth,8,busWidthBytes),Add#(1,a,busWidth));
   interface AxiMasterWrite write;
       method ActionValue#(Bit#(32)) writeAddr() if (False);
           return 0;
       endmethod
       method Bit#(8) writeBurstLen();
           return 0;
       endmethod
       method Bit#(3) writeBurstWidth();
           if (valueOf(busWidth) == 32)
               return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
           else if (valueOf(busWidth) == 64)
               return 3'b011;
           else
               return 3'b100;
       endmethod
       method Bit#(2) writeBurstType();  // drive with 2'b01 increment address
           return 2'b01; // increment address
       endmethod
       method Bit#(3) writeBurstProt(); // drive with 3'b000
           return 3'b000;
       endmethod
       method Bit#(4) writeBurstCache(); // drive with 4'b0011
           return 4'b0011;
       endmethod
       method Bit#(1) writeId();
           return 0;
       endmethod

       method ActionValue#(Bit#(busWidth)) writeData();
           return 0;
       endmethod
       method Bit#(busWidthBytes) writeDataByteEnable();
           return maxBound;
       endmethod
       method Bit#(1) writeLastDataBeat(); // last data beat
           return 0;
       endmethod

       method Action writeResponse(Bit#(2) responseCode, Bit#(1) id);
       endmethod
   endinterface

   interface AxiMasterRead read;
       method ActionValue#(Bit#(32)) readAddr() if (False);
           return 0;
       endmethod
       method Bit#(8) readBurstLen();
           return 0;
       endmethod
       method Bit#(3) readBurstWidth();
           if (valueOf(busWidth) == 32)
               return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
           else if (valueOf(busWidth) == 64)
               return 3'b011;
           else
               return 3'b100;
       endmethod
       method Bit#(2) readBurstType();  // drive with 2'b01
           return 2'b01;
       endmethod
       method Bit#(3) readBurstProt(); // drive with 3'b000
           return 3'b000;
       endmethod
       method Bit#(4) readBurstCache(); // drive with 4'b0011
           return 4'b0011;
       endmethod
       method Bit#(1) readId();
           return 0;
       endmethod
       method Action readData(Bit#(busWidth) data, Bit#(2) resp, Bit#(1) last, Bit#(1) id);
       endmethod
   endinterface
endmodule

module mkNullAxi3Master(Axi3Master#(busWidth,busWidthBytes,idWidth)) provisos(Div#(busWidth,8,busWidthBytes),Add#(1,a,busWidth));
   interface Axi3MasterWrite write;
       method ActionValue#(Bit#(32)) writeAddr() if (False);
           return 0;
       endmethod
       method Bit#(4) writeBurstLen();
           return 0;
       endmethod
       method Bit#(3) writeBurstWidth();
           if (valueOf(busWidth) == 32)
               return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
           else if (valueOf(busWidth) == 64)
               return 3'b011;
           else
               return 3'b100;
       endmethod
       method Bit#(2) writeBurstType();  // drive with 2'b01 increment address
           return 2'b01; // increment address
       endmethod
       method Bit#(2) writeBurstProt(); // drive with 3'b000
           return 2'b00;
       endmethod
       method Bit#(3) writeBurstCache(); // drive with 4'b0011
           return 3'b011;
       endmethod
       method Bit#(idWidth) writeId();
           return 0;
       endmethod

       method ActionValue#(Bit#(busWidth)) writeData();
           return 0;
       endmethod
       method Bit#(busWidthBytes) writeDataByteEnable();
           return maxBound;
       endmethod
       method Bit#(1) writeLastDataBeat(); // last data beat
           return 0;
       endmethod

       method Action writeResponse(Bit#(2) responseCode, Bit#(idWidth) id);
       endmethod
   endinterface

   interface Axi3MasterRead read;
       method ActionValue#(Bit#(32)) readAddr() if (False);
           return 0;
       endmethod
       method Bit#(4) readBurstLen();
           return 0;
       endmethod
       method Bit#(3) readBurstWidth();
           if (valueOf(busWidth) == 32)
               return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
           else if (valueOf(busWidth) == 64)
               return 3'b011;
           else
               return 3'b100;
       endmethod
       method Bit#(2) readBurstType();  // drive with 2'b01
           return 2'b01;
       endmethod
       method Bit#(2) readBurstProt(); // drive with 3'b000
           return 2'b00;
       endmethod
       method Bit#(3) readBurstCache(); // drive with 4'b0011
           return 3'b011;
       endmethod
       method Bit#(idWidth) readId();
           return 0;
       endmethod
       method Action readData(Bit#(busWidth) data, Bit#(2) resp, Bit#(1) last, Bit#(idWidth) id);
       endmethod
   endinterface
endmodule

module mkAxiSlaveFromRegFile#(RegFile#(Bit#(regFileBusWidth), Bit#(busWidth)) rf)
                             (AxiSlave#(busWidth, busWidthBytes)) provisos(Div#(busWidth,8,busWidthBytes),
                                                                           Add#(nz, regFileBusWidth, 32));
    Reg#(Bit#(regFileBusWidth)) readAddrReg <- mkReg(0);
    Reg#(Bit#(regFileBusWidth)) writeAddrReg <- mkReg(0);
    Reg#(Bit#(8)) readBurstCountReg <- mkReg(0);
    Reg#(Bit#(8)) writeBurstCountReg <- mkReg(0);
    FIFO#(Bit#(2)) writeRespFifo <- mkFIFO();

    Bool verbose = False;
    interface AxiSlaveRead read;
        method Action readAddr(Bit#(32) addr, Bit#(8) burstLen, Bit#(3) burstWidth,
                               Bit#(2) burstType, Bit#(3) burstProt, Bit#(4) burstCache) if (readBurstCountReg == 0);
            if (verbose) $display("axiSlave.read.readAddr %h bc %d", addr, burstLen+1);
            readAddrReg <= truncate(addr/fromInteger(valueOf(busWidthBytes)));
            readBurstCountReg <= burstLen+1;
        endmethod

        method ActionValue#(Bit#(busWidth)) readData() if (readBurstCountReg > 0);
            let data = rf.sub(readAddrReg);
            if (verbose) $display("axiSlave.read.readData %h %h %d", readAddrReg, data, readBurstCountReg);
            readBurstCountReg <= readBurstCountReg - 1;
            readAddrReg <= readAddrReg + 1;
            return data;
        endmethod
        method Bit#(1) last();
            return (readBurstCountReg == 1) ? 1 : 0;
        endmethod
    endinterface

    interface AxiSlaveWrite write;
       method Action writeAddr(Bit#(32) addr, Bit#(8) burstLen, Bit#(3) burstWidth,
                               Bit#(2) burstType, Bit#(3) burstProt, Bit#(4) burstCache) if (writeBurstCountReg == 0);
           if (verbose) $display("axiSlave.write.writeAddr %h bc %d", addr, burstLen+1);
           writeAddrReg <= truncate(addr/fromInteger(valueOf(busWidthBytes)));
           writeBurstCountReg <= burstLen+1;
       endmethod

       method Action writeData(Bit#(busWidth) data, Bit#(busWidthBytes) byteEnable, Bit#(1) last) if (writeBurstCountReg > 0);
           if (verbose) $display("writeData %h %h %d", writeAddrReg, data, writeBurstCountReg);
           rf.upd(writeAddrReg, data);
           writeAddrReg <= writeAddrReg + 1;
           writeBurstCountReg <= writeBurstCountReg - 1;
            if (verbose) $display("axiSlave.write.writeData %h %h %d", writeAddrReg, data, writeBurstCountReg);
           if (writeBurstCountReg == 1)
               writeRespFifo.enq(0);
       endmethod
       method ActionValue#(Bit#(2)) writeResponse();
           writeRespFifo.deq;
           return writeRespFifo.first;
       endmethod
    endinterface
endmodule

module mkAxiSlaveRegFile(AxiSlave#(busWidth, busWidthBytes)) provisos(Div#(busWidth,8,busWidthBytes));
       RegFile#(Bit#(21), Bit#(busWidth)) rf <- mkRegFileFull();
       AxiSlave#(busWidth, busWidthBytes) axiSlave <- mkAxiSlaveFromRegFile(rf);
       return axiSlave;
endmodule

module mkAxiSlaveRegFileLoad#(String fileName)(AxiSlave#(busWidth, busWidthBytes)) provisos(Div#(busWidth,8,busWidthBytes));
       RegFile#(Bit#(21), Bit#(busWidth)) rf <- mkRegFileFullLoad(fileName);
       AxiSlave#(busWidth, busWidthBytes) axiSlave <- mkAxiSlaveFromRegFile(rf);
       return axiSlave;
endmodule

module mkAxi3SlaveFromRegFile#(RegFile#(Bit#(regFileBusWidth), Bit#(busWidth)) rf)
                              (Axi3Slave#(busWidth, busWidthBytes)) provisos(Div#(busWidth,8,busWidthBytes),
                                                                           Add#(nz, regFileBusWidth, 32));
    Reg#(Bit#(regFileBusWidth)) readAddrReg <- mkReg(0);
    Reg#(Bit#(regFileBusWidth)) writeAddrReg <- mkReg(0);
    Reg#(Bit#(12)) readIdReg <- mkReg(0);
    Reg#(Bit#(12)) writeIdReg <- mkReg(0);
    Reg#(Bit#(4)) readBurstCountReg <- mkReg(0);
    Reg#(Bit#(4)) writeBurstCountReg <- mkReg(0);
    FIFO#(Bit#(2)) writeRespFifo <- mkFIFO();
    FIFO#(Bit#(12)) writeIdFifo <- mkFIFO();

    Bool verbose = False;
    interface Axi3SlaveRead read;
        method Action readAddr(Bit#(32) addr, Bit#(4) burstLen, Bit#(3) burstWidth,
                               Bit#(2) burstType, Bit#(2) burstProt, Bit#(3) burstCache, Bit#(12) id) if (readBurstCountReg == 0);
            if (verbose) $display("axiSlave.read.readAddr %h bc %d", addr, burstLen+1);
            readAddrReg <= truncate(addr/fromInteger(valueOf(busWidthBytes)));
	    readIdReg <= id;
            readBurstCountReg <= burstLen+1;
        endmethod

        method ActionValue#(Bit#(busWidth)) readData() if (readBurstCountReg > 0);
            let data = rf.sub(readAddrReg);
            if (verbose) $display("axiSlave.read.readData %h %h %d", readAddrReg, data, readBurstCountReg);
            readBurstCountReg <= readBurstCountReg - 1;
            readAddrReg <= readAddrReg + 1;
            return data;
        endmethod
        method Bit#(1) last();
            return (readBurstCountReg == 1) ? 1 : 0;
        endmethod
	method Bit#(12) rid();
	    return readIdReg;
	endmethod
    endinterface

    interface Axi3SlaveWrite write;
       method Action writeAddr(Bit#(32) addr, Bit#(4) burstLen, Bit#(3) burstWidth,
                               Bit#(2) burstType, Bit#(2) burstProt, Bit#(3) burstCache, Bit#(12) id) if (writeBurstCountReg == 0);
           if (verbose) $display("axiSlave.write.writeAddr %h bc %d", addr, burstLen+1);
           writeAddrReg <= truncate(addr/fromInteger(valueOf(busWidthBytes)));
	   writeIdReg <= id;
           writeBurstCountReg <= burstLen+1;
           writeIdFifo.enq(id);
       endmethod

       method Action writeData(Bit#(busWidth) data, Bit#(busWidthBytes) byteEnable, Bit#(1) last) if (writeBurstCountReg > 0);
           if (verbose) $display("writeData %h %h %d", writeAddrReg, data, writeBurstCountReg);
           rf.upd(writeAddrReg, data);
           writeAddrReg <= writeAddrReg + 1;
           writeBurstCountReg <= writeBurstCountReg - 1;
            if (verbose) $display("axiSlave.write.writeData %h %h %d", writeAddrReg, data, writeBurstCountReg);
           if (writeBurstCountReg == 1)
	   begin
               writeRespFifo.enq(0);
           end
       endmethod
       method ActionValue#(Bit#(2)) writeResponse();
           writeRespFifo.deq;
           return writeRespFifo.first;
       endmethod
	method ActionValue#(Bit#(12)) bid();
	    writeIdFifo.deq;
	    return writeIdFifo.first;
	endmethod
    endinterface
endmodule

module mkAxi3SlaveRegFile(Axi3Slave#(busWidth, busWidthBytes)) provisos(Div#(busWidth,8,busWidthBytes));
       RegFile#(Bit#(21), Bit#(busWidth)) rf <- mkRegFileFull();
       Axi3Slave#(busWidth, busWidthBytes) axiSlave <- mkAxi3SlaveFromRegFile(rf);
       return axiSlave;
endmodule

module mkMasterSlaveConnection#(AxiMasterWrite#(busWidth, busWidthBytes) axiw,
                                AxiMasterRead#(busWidth) axir,
                                AxiSlave#(busWidth, busWidthBytes) axiSlave)
                                ();

    Reg#(Bit#(8)) writeBurstCountReg <- mkReg(0);
    Bool verbose = False;

    rule readAddr;
        Bit#(32) addr <-axir.readAddr;
        let burstLen = axir.readBurstLen;
        let burstWidth = axir.readBurstWidth;
        let burstType = axir.readBurstType;
        let burstProt = axir.readBurstProt;
        let burstCache = axir.readBurstCache;
        axiSlave.read.readAddr(addr, burstLen, burstWidth, burstType, burstProt, burstCache);
	if (verbose) $display("        MasterSlaveConnection.readAddr %h %d", addr, burstLen+1);
    endrule
    rule readData;
        let data <- axiSlave.read.readData();
        axir.readData(data, 2'b00, 0, 0);
        if (verbose) $display("        MasterSlaveConnection.readData %h", data);
    endrule
    rule writeAddr;
        Bit#(32) addr <- axiw.writeAddr;
        let burstLen = axiw.writeBurstLen;
        let burstWidth = axiw.writeBurstWidth;
        let burstType = axiw.writeBurstType;
        let burstProt = axiw.writeBurstProt;
        let burstCache = axiw.writeBurstCache;
        axiSlave.write.writeAddr(addr, burstLen, burstWidth, burstType, burstProt, burstCache);
        if (verbose) $display("        MasterSlaveConnection.writeAddr %h %d", addr, burstLen+1);
    endrule
    rule writeData;
        let data <- axiw.writeData;
        let byteEnable = axiw.writeDataByteEnable;
        let last = axiw.writeLastDataBeat;
        axiSlave.write.writeData(data, byteEnable, last);
        if (verbose) $display("        MasterSlaveConnection.writeData %h", data);
    endrule
    rule writeResponse;
        let response <- axiSlave.write.writeResponse();
        axiw.writeResponse(response, 0);
    endrule
endmodule

module mkClientSlaveConnection#(Axi3WriteClient#(busWidth, busWidthBytes,idWidth) axicw,
                                Axi3ReadClient#(busWidth,idWidth) axicr,
                                Axi3Slave#(busWidth, busWidthBytes) axiSlave)
                                () provisos (Div#(busWidth, 8, busWidthBytes),
				             Add#(1, a__, busWidth),
					     Add#(busWidth, b__, 65),
					     Add#(idWidth, 1, 13));

    Reg#(Bit#(8)) writeBurstCountReg <- mkReg(0);
    Bool verbose = False;

    Axi3Client#(busWidth,busWidthBytes,idWidth) axic <- mkAxi3Client(axicw, axicr);
    Axi3Master#(busWidth,busWidthBytes,idWidth) m_axiMaster <- mkAxi3Master(axic);
    let axir = m_axiMaster.read;
    let axiw = m_axiMaster.write;

    rule readAddr;
        Bit#(32) addr <-axir.readAddr;
        let burstLen = axir.readBurstLen;
        let burstWidth = axir.readBurstWidth;
        let burstType = axir.readBurstType;
        let burstProt = axir.readBurstProt;
        let burstCache = axir.readBurstCache;
	let id = axir.readId;
        axiSlave.read.readAddr(addr, burstLen, burstWidth, burstType, burstProt, burstCache, id);
	if (verbose) $display("        MasterSlaveConnection.readAddr %h %d", addr, burstLen+1);
    endrule
    rule readData;
        let data <- axiSlave.read.readData();
	let id = axiSlave.read.rid();
        axir.readData(data, 2'b00, 0, id);
        if (verbose) $display("        MasterSlaveConnection.readData %h", data);
    endrule
    rule writeAddr;
        Bit#(32) addr <- axiw.writeAddr;
        let burstLen = axiw.writeBurstLen;
        let burstWidth = axiw.writeBurstWidth;
        let burstType = axiw.writeBurstType;
        let burstProt = axiw.writeBurstProt;
        let burstCache = axiw.writeBurstCache;
	let id = axiw.writeId;
        axiSlave.write.writeAddr(addr, burstLen, burstWidth, burstType, burstProt, burstCache, id);
        if (verbose) $display("        MasterSlaveConnection.writeAddr %h %d", addr, burstLen+1);
    endrule
    rule writeData;
        let data <- axiw.writeData;
        let byteEnable = axiw.writeDataByteEnable;
        let last = axiw.writeLastDataBeat;
        axiSlave.write.writeData(data, byteEnable, last);
        if (verbose) $display("        MasterSlaveConnection.writeData %h", data);
    endrule
    rule writeResponse;
        let response <- axiSlave.write.writeResponse();
        let id <- axiSlave.write.bid();
        axiw.writeResponse(response, id);
    endrule
endmodule

module mkAxi3MasterWires#(Axi3Client#(busWidth,busWidthBytes,idWidth) client)(Axi3Master#(busWidth,busWidthBytes,idWidth))
	 provisos(Div#(busWidth,8,busWidthBytes),Add#(1,a,busWidth));

    Wire#(Axi3ReadRequest#(idWidth))                      wReadRequest <- mkDWire(unpack(0));
    Wire#(Axi3WriteRequest#(idWidth))                     wWriteRequest <- mkDWire(unpack(0));
    Wire#(Axi3WriteData#(busWidth,busWidthBytes,idWidth)) wWriteData <- mkDWire(unpack(0));

    interface Axi3MasterWrite write;
	method ActionValue#(Bit#(32)) writeAddr();
	    let r <- client.write.address();
	    wWriteRequest <= r;
	    return r.address;
	endmethod
	method Bit#(4) writeBurstLen();
	    return wWriteRequest.burstLen;
	endmethod
	method Bit#(3) writeBurstWidth();
	    if (valueOf(busWidth) == 32)
		return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
	    else if (valueOf(busWidth) == 64)
		return 3'b011;
	    else
		return 3'b100;
	endmethod
	method Bit#(2) writeBurstType();  // drive with 2'b01 increment address
	    return 2'b01; // increment address
	endmethod
	method Bit#(2) writeBurstProt(); // drive with 3'b000
	    return 2'b000;
	endmethod
	method Bit#(3) writeBurstCache(); // drive with 4'b0011
	    return 3'b0011;
	endmethod
	method Bit#(idWidth) writeId();
	    return wWriteRequest.id;
	endmethod

	method ActionValue#(Bit#(busWidth)) writeData();
	    let d <- client.write.data();
	    wWriteData <= d;
	    return d.data;
	endmethod
	method Bit#(busWidthBytes) writeDataByteEnable();
	    return wWriteData.byteEnable;
	endmethod
	method Bit#(1) writeLastDataBeat(); // last data beat
	    return wWriteData.last;
	endmethod

	method Action writeResponse(Bit#(2) responseCode, Bit#(idWidth) id);
	    client.write.response(Axi3WriteResponse { code: responseCode, id: id});
	endmethod
    endinterface

    interface Axi3MasterRead read;
	method ActionValue#(Bit#(32)) readAddr();
	    let r <- client.read.address();
	    wReadRequest <= r;
	    return r.address;
	endmethod
	method Bit#(4) readBurstLen();
	    return wReadRequest.burstLen;
	endmethod
	method Bit#(3) readBurstWidth();
	    if (valueOf(busWidth) == 32)
		return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
	    else if (valueOf(busWidth) == 64)
		return 3'b011;
	    else
		return 3'b100;
	endmethod
	method Bit#(2) readBurstType();  // drive with 2'b01
	    return 2'b01;
	endmethod
	method Bit#(2) readBurstProt(); // drive with 3'b000
	    return 2'b000;
	endmethod
	method Bit#(3) readBurstCache(); // drive with 4'b0011
	    return 3'b0011;
	endmethod
	method Bit#(idWidth) readId();
	    return wReadRequest.id;
	endmethod
	method Action readData(Bit#(busWidth) data, Bit#(2) code, Bit#(1) last, Bit#(idWidth) id);
	    client.read.data(Axi3ReadResponse { data: data, code: code, last: last, id: id});
	endmethod
   endinterface
endmodule

module mkAxi3Master#(Axi3Client#(busWidth,busWidthBytes,idWidth) client)(Axi3Master#(busWidth,busWidthBytes,idWidth))
	 provisos(Div#(busWidth,8,busWidthBytes),Add#(1,a,busWidth),Add#(busWidth,b,65));

    FIFOF#(Axi3ReadRequest#(idWidth))                      fReadRequest <- mkSizedBRAMFIFOF(8);
    FIFOF#(Axi3WriteRequest#(idWidth))                     fWriteRequest <- mkSizedBRAMFIFOF(8);
    FIFOF#(Axi3WriteData#(busWidth,busWidthBytes,idWidth)) fWriteData <- mkSizedBRAMFIFOF(8);

    rule writAddrRule;
        let r <- client.write.address();
	fWriteRequest.enq(r);
    endrule

    rule writeDataRule;
        let d <- client.write.data();
	fWriteData.enq(d);
    endrule

    rule readAddrRule;
        let r <- client.read.address();
	fReadRequest.enq(r);
    endrule

    interface Axi3MasterWrite write;
	method ActionValue#(Bit#(32)) writeAddr();
	    fWriteRequest.deq;
	    return fWriteRequest.first.address;
	endmethod
	method Bit#(4) writeBurstLen();
	    return fWriteRequest.first.burstLen;
	endmethod
	method Bit#(3) writeBurstWidth();
	    if (valueOf(busWidth) == 32)
		return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
	    else if (valueOf(busWidth) == 64)
		return 3'b011;
	    else
		return 3'b100;
	endmethod
	method Bit#(2) writeBurstType();  // drive with 2'b01 increment address
	    return 2'b01; // increment address
	endmethod
	method Bit#(2) writeBurstProt(); // drive with 3'b000
	    return 2'b000;
	endmethod
	method Bit#(3) writeBurstCache(); // drive with 4'b0011
	    return 3'b0011;
	endmethod
	method Bit#(idWidth) writeId();
	    return fWriteRequest.first.id;
	endmethod

	method ActionValue#(Bit#(busWidth)) writeData();
	    fWriteData.deq;
	    return fWriteData.first.data;
	endmethod
	method Bit#(busWidthBytes) writeDataByteEnable();
	    return fWriteData.first.byteEnable;
	endmethod
	method Bit#(1) writeLastDataBeat(); // last data beat
	    return fWriteData.first.last;
	endmethod

	method Action writeResponse(Bit#(2) responseCode, Bit#(idWidth) id);
	    client.write.response(Axi3WriteResponse { code: responseCode, id: id});
	endmethod
    endinterface

    interface Axi3MasterRead read;
	method ActionValue#(Bit#(32)) readAddr();
	    fReadRequest.deq;
	    return fReadRequest.first.address;
	endmethod
	method Bit#(4) readBurstLen();
	    return fReadRequest.first.burstLen;
	endmethod
	method Bit#(3) readBurstWidth();
	    if (valueOf(busWidth) == 32)
		return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
	    else if (valueOf(busWidth) == 64)
		return 3'b011;
	    else
		return 3'b100;
	endmethod
	method Bit#(2) readBurstType();  // drive with 2'b01
	    return 2'b01;
	endmethod
	method Bit#(2) readBurstProt(); // drive with 3'b000
	    return 2'b000;
	endmethod
	method Bit#(3) readBurstCache(); // drive with 4'b0011
	    return 3'b0011;
	endmethod
	method Bit#(idWidth) readId();
	    return fReadRequest.first.id;
	endmethod
	method Action readData(Bit#(busWidth) data, Bit#(2) code, Bit#(1) last, Bit#(idWidth) id);
	    client.read.data(Axi3ReadResponse { data: data, code: code, last: last, id: id});
	endmethod
   endinterface
endmodule


tag_v = completionTagMimo.first for readability

Change-Id: I333681385f59c863fff1c055fd57a284abea46de

// 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 FIFOF        :: *;
import GetPut       :: *;
import GetPutF      :: *;
import Connectable  :: *;
import PCIE         :: *;
import DefaultValue :: *;
import MIMO         :: *;
import Vector       :: *;

import AxiMasterSlave :: *;

interface AxiSlaveEngine#(type buswidth);
    interface GetPut#(TLPData#(16))   tlps;
    interface Axi3Slave#(40,buswidth,6)  slave;
    method Bool tlpOutFifoNotEmpty();
    interface Reg#(Bool) use4dw;
endinterface: AxiSlaveEngine

module mkAxiSlaveEngine#(PciId my_id)(AxiSlaveEngine#(buswidth))
   provisos (Div#(buswidth, 8, busWidthBytes),
	     Div#(buswidth, 32, busWidthWords),
	     Bits#(Vector#(busWidthWords, Bit#(32)), buswidth),
	     Add#(aaa, 32, buswidth),
	     Add#(bbb, buswidth, 256),
	     Add#(ccc, TMul#(8, busWidthWords), 64),
	     Add#(ddd, TMul#(32, busWidthWords), 256),
	     Add#(eee, busWidthWords, 8));

    FIFOF#(TLPData#(16)) tlpOutFifo <- mkFIFOF;
    FIFOF#(TLPData#(16)) tlpInFifo <- mkFIFOF;
    FIFOF#(TLPData#(16)) tlpWriteHeaderFifo <- mkFIFOF;

    Reg#(Bit#(7)) hitReg <- mkReg(0);
    Reg#(Bool) use4dwReg <- mkReg(True);

    // default configuration for MIMO is for guarded enq() and deq().
    // However, the implicit guard only checks for space for 1 element for enq(), and availability of 1 element for deq().
    MIMOConfiguration mimoCfg = defaultValue;
    MIMO#(4,busWidthWords,8,Bit#(32)) completionMimo <- mkMIMO(mimoCfg);
    MIMO#(4,busWidthWords,8,TLPTag) completionTagMimo <- mkMIMO(mimoCfg);
    MIMO#(busWidthWords,4,8,Bit#(32)) writeDataMimo <- mkMIMO(mimoCfg);
    Reg#(TLPTag) lastTag <- mkReg(0);
    Reg#(Bit#(9)) writeBurstCount <- mkReg(0);
    Reg#(TLPLength)  writeDwCount <- mkReg(0);
    Reg#(TLPTag) writeTag <- mkReg(0);
    FIFOF#(TLPTag) doneTag <- mkFIFOF();

    function Integer tlpWordCount(TLPData#(16) tlp);
       if (tlp.be == 16'h0000)
	  return 0;
       else if (tlp.be == 16'h000f || tlp.be == 16'hf000)
	  return 1;
       else if (tlp.be == 16'h00ff || tlp.be == 16'hff00)
	  return 2;
       else if (tlp.be == 16'h0fff || tlp.be == 16'hfff0)
	  return 3;
       else if (tlp.be == 16'hffff)
	  return 4;
       else
	  return 0;
    endfunction

   rule writeHeaderTlp if (writeDwCount == 0);
      let tlp = tlpWriteHeaderFifo.first;

      TLPMemory4DWHeader hdr_4dw = unpack(tlp.data);
      TLPLength dwCount = hdr_4dw.length;

      TLPMemoryIO3DWHeader hdr_3dw = unpack(tlp.data);
      Bool sendit = False;
      if (hdr_3dw.format == MEM_WRITE_3DW_DATA && writeDataMimo.deqReadyN(1)) begin
	 dwCount = hdr_3dw.length;
	 Vector#(4, Bit#(32)) v = writeDataMimo.first();
	 writeDataMimo.deq(1);
	 hdr_3dw.data = byteSwap(v[0]);
	 tlp.be = 16'hffff;
	 if (dwCount == 1)
	    tlp.eof = True;
	 dwCount = dwCount - 1;
	 tlp.data = pack(hdr_3dw);
	 sendit = True;
      end
      else if (hdr_3dw.format == MEM_WRITE_3DW_DATA) begin
	 // retry until the data is available in writeDataMimo
      end
      if (hdr_4dw.format == MEM_WRITE_4DW_DATA && writeDataMimo.deqReadyN(1)) begin
	 tlp.be = 16'hffff;
	 sendit = True;
      end
      else if (hdr_4dw.format == MEM_WRITE_4DW_DATA) begin
	 sendit = False;
      end
      else begin
	 sendit = True;
      end
      if (sendit) begin
	 tlpWriteHeaderFifo.deq();
	 tlpOutFifo.enq(tlp);
	 $display("writeHeaderTlp dwCount=%d", dwCount);
	 writeDwCount <= dwCount;
      end
   endrule

   rule writeTlps if (writeDwCount > 0);
      TLPData#(16) tlp = defaultValue;
      tlp.sof = False;
      Vector#(4, Bit#(32)) v = unpack(0);
      Bool sendit = False;
      // The MIMO implicit guard only checks for availability of 1 element
      // so we explicitly check for the number of elements required
      if (writeDwCount > 4 && writeDataMimo.deqReadyN(4)) begin
	 v = writeDataMimo.first();

	 writeDataMimo.deq(4);
	 writeDwCount <= writeDwCount - 4;
	 tlp.eof = False;
	 tlp.be = 16'hffff;
	 sendit = True;
      end
      else if (writeDwCount <= 4 && writeDataMimo.deqReadyN(unpack(truncate(writeDwCount)))) begin
	 v = writeDataMimo.first();
	 writeDataMimo.deq(unpack(truncate(writeDwCount)));
	 writeDwCount <= 0;
	 doneTag.enq(writeTag);
	 $display("writeDwCount=%d will be zero", writeDwCount);
	 tlp.eof = True;
	 if (writeDwCount == 4)
	    tlp.be = 16'hffff;
	 else if (writeDwCount == 3)
	    tlp.be = 16'hfff0;
	 else if (writeDwCount == 2)
	    tlp.be = 16'hff00;
	 else if (writeDwCount == 1)
	    tlp.be = 16'hf000;
	 sendit = True;
      end
      else begin
	 // wait for more data in writeDataMimo
	 $display("waiting for more data dwCount=%d count=%d writeBurstCount=%d enqReady=%d",
	    writeDwCount, writeDataMimo.count(), writeBurstCount, writeDataMimo.enqReadyN(fromInteger(valueOf(busWidthWords))));
      end
      if (sendit) begin
	 for (Integer i = 0; i < 4; i = i + 1)
	    tlp.data[(i+1)*32-1:i*32] = byteSwap(v[3-i]);
	 tlpOutFifo.enq(tlp);
      end
   endrule

   rule handleTlpIn;
      let tlp = tlpInFifo.first;
      Bool handled = False;
      TLPMemoryIO3DWHeader h = unpack(tlp.data);
      hitReg <= tlp.hit;
      TLPMemoryIO3DWHeader hdr_3dw = unpack(tlp.data);
      Vector#(4, Bit#(32)) vec = unpack(0);
      Vector#(4, Bit#(32)) tlpvec = unpack(tlp.data);

      if (!tlp.sof) begin
	 let count = tlpWordCount(tlp);
	 // if sof is false, then count will be at least 1
	 function Bit#(32) f(Integer i);
	    begin
	       if (i < count)
		  return tlpvec[3-i];
	       else
		  return 32'hbad0beef;
	    end
	 endfunction
	 vec = genWith(f);
	 // The MIMO implicit guard only checks for space to enqueue 1 element
	 // so we explicitly check for the number of elements required
	 // otherwise elements in the queue will be overwritten.
	 if (completionMimo.enqReadyN(fromInteger(count))
	    && completionTagMimo.enqReadyN(fromInteger(count)))
	    begin
	       completionMimo.enq(fromInteger(count), vec);
	       completionTagMimo.enq(fromInteger(count), replicate(lastTag));
	       handled = True;
	    end
      end
      else if (hdr_3dw.format == MEM_WRITE_3DW_DATA
	       && hdr_3dw.pkttype == COMPLETION
	       && completionMimo.enqReadyN(1)
	       && completionTagMimo.enqReadyN(1)) begin
	    vec[0] = hdr_3dw.data;
	    completionMimo.enq(1, vec);
	    lastTag <= hdr_3dw.tag;
	    completionTagMimo.enq(1, replicate(hdr_3dw.tag));
	    handled = True;
      end
      //$display("tlpIn handled=%d tlp=%h\n", handled, tlp);
      if (handled)
	 tlpInFifo.deq();
   endrule

    interface GetPut tlps = tuple2(toGet(tlpOutFifo),toPut(tlpInFifo));
    interface Axi3Slave slave;
	interface Put req_aw;
	   method Action put(Axi3WriteRequest#(40, 6) req)
	      if (writeBurstCount == 0);

	      let burstLen = req.len;
	      let addr = req.address;
	      let awid = req.id;

	      TLPLength tlplen = fromInteger(valueOf(busWidthWords))*(extend(burstLen) + 1);
	      TLPData#(16) tlp = defaultValue;
	      tlp.sof = True;
	      tlp.eof = False;
	      tlp.hit = 7'h00;
	      tlp.be = 16'hffff;

	      $display("slave.writeAddr tlplen=%d burstLen=%d", tlplen, burstLen);
	      if ((addr >> 32) != 0) begin
		 TLPMemory4DWHeader hdr_4dw = defaultValue;
		 hdr_4dw.format = MEM_WRITE_4DW_DATA;
		 hdr_4dw.tag = extend(awid);
		 hdr_4dw.reqid = my_id;
		 hdr_4dw.nosnoop = SNOOPING_REQD;
		 hdr_4dw.addr = addr[40-1:2];
		 hdr_4dw.length = tlplen;
		 hdr_4dw.firstbe = 4'hf;
		 hdr_4dw.lastbe = (tlplen > 1) ? 4'hf : 0;
		 tlp.data = pack(hdr_4dw);
	      end
	      else begin
		 TLPMemoryIO3DWHeader hdr_3dw = defaultValue;
		 hdr_3dw.format = MEM_WRITE_3DW_DATA;
		 hdr_3dw.tag = extend(awid);
		 hdr_3dw.reqid = my_id;
		 hdr_3dw.nosnoop = SNOOPING_REQD;
		 hdr_3dw.addr = addr[32-1:2];
		 hdr_3dw.length = tlplen;
		 hdr_3dw.firstbe = 4'hf;
		 hdr_3dw.lastbe = (tlplen > 1) ? 4'hf : 0;

		 tlp.be = 16'hfff0; // no data word in this TLP

		 tlp.data = pack(hdr_3dw);
	      end
	      tlpWriteHeaderFifo.enq(tlp);
	      writeBurstCount <= zeroExtend(burstLen)+1;
	      writeTag <= extend(awid);
           endmethod
	endinterface : req_aw
       interface Put resp_write;
	   method Action put(Axi3WriteData#(busWidth,6) wdata)
	      provisos (Bits#(Vector#(busWidthWords, Bit#(32)), busWidth)) if (writeBurstCount > 0 && writeDataMimo.enqReadyN(fromInteger(valueOf(busWidthWords))));

	      writeBurstCount <= writeBurstCount - 1;
	      Vector#(busWidthWords, Bit#(32)) v = unpack(wdata.data);
	      writeDataMimo.enq(fromInteger(valueOf(busWidthWords)), v);
           endmethod
       endinterface : resp_write
       interface GetF resp_b;
	   method ActionValue#(Axi3WriteResponse#(6)) get();
	      let tag = doneTag.first();
	      doneTag.deq();
	      return Axi3WriteResponse { resp: 0, id: truncate(tag)};
           endmethod
	   method Bool notEmpty() = doneTag.notEmpty();
	endinterface: resp_b
       interface Put req_ar;
	   method Action put(Axi3ReadRequest#(40,6) req) if (writeDwCount == 0);
	      let burstLen = req.len;
	      let addr = req.address;
	      let arid = req.id;

	       TLPData#(16) tlp = defaultValue;
	       tlp.sof = True;
	       tlp.eof = True;
	       tlp.hit = 7'h00;
	       TLPLength tlplen = fromInteger(valueOf(busWidthWords))*(extend(burstLen) + 1);
	       if (addr[39:32] != 0) begin
		   TLPMemory4DWHeader hdr_4dw = defaultValue;
		   hdr_4dw.format = MEM_READ_4DW_NO_DATA;
		   hdr_4dw.tag = extend(arid);
		   hdr_4dw.reqid = my_id;
		   hdr_4dw.nosnoop = SNOOPING_REQD;
		   hdr_4dw.addr = addr[40-1:2];
		   hdr_4dw.length = tlplen;
		   hdr_4dw.firstbe = 4'hf;
		   hdr_4dw.lastbe = (tlplen > 1) ? 4'hf : 0;
		   tlp.data = pack(hdr_4dw);
		   tlp.be = 16'hffff;
	       end
	       else begin
		   TLPMemoryIO3DWHeader hdr_3dw = defaultValue;
		   hdr_3dw.format = MEM_READ_3DW_NO_DATA;
		   hdr_3dw.tag = extend(arid);
		   hdr_3dw.reqid = my_id;
		   hdr_3dw.nosnoop = SNOOPING_REQD;
		   hdr_3dw.addr = addr[32-1:2];
		   hdr_3dw.length = tlplen;
		   hdr_3dw.firstbe = 4'hf;
		   hdr_3dw.lastbe = (tlplen > 1) ? 4'hf : 0;
		   tlp.data = pack(hdr_3dw);
		   tlp.be = 16'hfff0;
	       end
	       tlpOutFifo.enq(tlp);
           endmethod
       endinterface : req_ar
       interface GetF resp_read;
	   method ActionValue#(Axi3ReadResponse#(buswidth,6)) get() if (completionMimo.deqReadyN(fromInteger(valueOf(busWidthWords))));
	      let data_v = completionMimo.first;
	      let tag_v = completionTagMimo.first;
	      completionMimo.deq(fromInteger(valueOf(busWidthWords)));
	      completionTagMimo.deq(fromInteger(valueOf(busWidthWords)));
              Bit#(buswidth) v = 0;
	      for (Integer i = 0; i < valueOf(busWidthWords); i = i+1)
		 v[(i+1)*32-1:i*32] = byteSwap(data_v[i]);
	      return Axi3ReadResponse { data: v, last: 0, id: truncate(tag_v[0]), resp: 0 };
           endmethod
	  method Bool notEmpty();
	     return (completionMimo.deqReadyN(fromInteger(valueOf(busWidthWords))));
	  endmethod
	endinterface: resp_read
    endinterface: slave
   method Bool tlpOutFifoNotEmpty() = tlpOutFifo.notEmpty;
   interface Reg use4dw = use4dwReg;
endmodule: mkAxiSlaveEngine

// 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 FIFOF        :: *;
import GetPut       :: *;
import GetPutF      :: *;
import Connectable  :: *;
import PCIE         :: *;
import DefaultValue :: *;
import MIMO         :: *;
import Vector       :: *;

import AxiMasterSlave :: *;

interface AxiSlaveEngine#(type buswidth);
    interface GetPut#(TLPData#(16))   tlps;
    interface Axi3Slave#(40,buswidth,6)  slave;
    method Bool tlpOutFifoNotEmpty();
    interface Reg#(Bool) use4dw;
endinterface: AxiSlaveEngine

module mkAxiSlaveEngine#(PciId my_id)(AxiSlaveEngine#(buswidth))
   provisos (Div#(buswidth, 8, busWidthBytes),
	     Div#(buswidth, 32, busWidthWords),
	     Bits#(Vector#(busWidthWords, Bit#(32)), buswidth),
	     Add#(aaa, 32, buswidth),
	     Add#(bbb, buswidth, 256),
	     Add#(ccc, TMul#(8, busWidthWords), 64),
	     Add#(ddd, TMul#(32, busWidthWords), 256),
	     Add#(eee, busWidthWords, 8));

    FIFOF#(TLPData#(16)) tlpOutFifo <- mkFIFOF;
    FIFOF#(TLPData#(16)) tlpInFifo <- mkFIFOF;
    FIFOF#(TLPData#(16)) tlpWriteHeaderFifo <- mkFIFOF;

    Reg#(Bit#(7)) hitReg <- mkReg(0);
    Reg#(Bool) use4dwReg <- mkReg(True);

    // default configuration for MIMO is for guarded enq() and deq().
    // However, the implicit guard only checks for space for 1 element for enq(), and availability of 1 element for deq().
    MIMOConfiguration mimoCfg = defaultValue;
    MIMO#(4,busWidthWords,8,Bit#(32)) completionMimo <- mkMIMO(mimoCfg);
    MIMO#(4,busWidthWords,8,TLPTag) completionTagMimo <- mkMIMO(mimoCfg);
    MIMO#(busWidthWords,4,8,Bit#(32)) writeDataMimo <- mkMIMO(mimoCfg);
    Reg#(TLPTag) lastTag <- mkReg(0);
    Reg#(Bit#(9)) writeBurstCount <- mkReg(0);
    Reg#(TLPLength)  writeDwCount <- mkReg(0);
    Reg#(TLPTag) writeTag <- mkReg(0);
    FIFOF#(TLPTag) doneTag <- mkFIFOF();

    function Integer tlpWordCount(TLPData#(16) tlp);
       if (tlp.be == 16'h0000)
	  return 0;
       else if (tlp.be == 16'h000f || tlp.be == 16'hf000)
	  return 1;
       else if (tlp.be == 16'h00ff || tlp.be == 16'hff00)
	  return 2;
       else if (tlp.be == 16'h0fff || tlp.be == 16'hfff0)
	  return 3;
       else if (tlp.be == 16'hffff)
	  return 4;
       else
	  return 0;
    endfunction

   rule writeHeaderTlp if (writeDwCount == 0);
      let tlp = tlpWriteHeaderFifo.first;

      TLPMemory4DWHeader hdr_4dw = unpack(tlp.data);
      TLPLength dwCount = hdr_4dw.length;

      TLPMemoryIO3DWHeader hdr_3dw = unpack(tlp.data);
      Bool sendit = False;
      if (hdr_3dw.format == MEM_WRITE_3DW_DATA && writeDataMimo.deqReadyN(1)) begin
	 dwCount = hdr_3dw.length;
	 Vector#(4, Bit#(32)) v = writeDataMimo.first();
	 writeDataMimo.deq(1);
	 hdr_3dw.data = byteSwap(v[0]);
	 tlp.be = 16'hffff;
	 if (dwCount == 1)
	    tlp.eof = True;
	 dwCount = dwCount - 1;
	 tlp.data = pack(hdr_3dw);
	 sendit = True;
      end
      else if (hdr_3dw.format == MEM_WRITE_3DW_DATA) begin
	 // retry until the data is available in writeDataMimo
      end
      if (hdr_4dw.format == MEM_WRITE_4DW_DATA && writeDataMimo.deqReadyN(1)) begin
	 tlp.be = 16'hffff;
	 sendit = True;
      end
      else if (hdr_4dw.format == MEM_WRITE_4DW_DATA) begin
	 sendit = False;
      end
      else begin
	 sendit = True;
      end
      if (sendit) begin
	 tlpWriteHeaderFifo.deq();
	 tlpOutFifo.enq(tlp);
	 $display("writeHeaderTlp dwCount=%d", dwCount);
	 writeDwCount <= dwCount;
      end
   endrule

   rule writeTlps if (writeDwCount > 0);
      TLPData#(16) tlp = defaultValue;
      tlp.sof = False;
      Vector#(4, Bit#(32)) v = unpack(0);
      Bool sendit = False;
      // The MIMO implicit guard only checks for availability of 1 element
      // so we explicitly check for the number of elements required
      if (writeDwCount > 4 && writeDataMimo.deqReadyN(4)) begin
	 v = writeDataMimo.first();

	 writeDataMimo.deq(4);
	 writeDwCount <= writeDwCount - 4;
	 tlp.eof = False;
	 tlp.be = 16'hffff;
	 sendit = True;
      end
      else if (writeDwCount <= 4 && writeDataMimo.deqReadyN(unpack(truncate(writeDwCount)))) begin
	 v = writeDataMimo.first();
	 writeDataMimo.deq(unpack(truncate(writeDwCount)));
	 writeDwCount <= 0;
	 doneTag.enq(writeTag);
	 $display("writeDwCount=%d will be zero", writeDwCount);
	 tlp.eof = True;
	 if (writeDwCount == 4)
	    tlp.be = 16'hffff;
	 else if (writeDwCount == 3)
	    tlp.be = 16'hfff0;
	 else if (writeDwCount == 2)
	    tlp.be = 16'hff00;
	 else if (writeDwCount == 1)
	    tlp.be = 16'hf000;
	 sendit = True;
      end
      else begin
	 // wait for more data in writeDataMimo
	 $display("waiting for more data dwCount=%d count=%d writeBurstCount=%d enqReady=%d",
	    writeDwCount, writeDataMimo.count(), writeBurstCount, writeDataMimo.enqReadyN(fromInteger(valueOf(busWidthWords))));
      end
      if (sendit) begin
	 for (Integer i = 0; i < 4; i = i + 1)
	    tlp.data[(i+1)*32-1:i*32] = byteSwap(v[3-i]);
	 tlpOutFifo.enq(tlp);
      end
   endrule

   rule handleTlpIn;
      let tlp = tlpInFifo.first;
      Bool handled = False;
      TLPMemoryIO3DWHeader h = unpack(tlp.data);
      hitReg <= tlp.hit;
      TLPMemoryIO3DWHeader hdr_3dw = unpack(tlp.data);
      Vector#(4, Bit#(32)) vec = unpack(0);
      Vector#(4, Bit#(32)) tlpvec = unpack(tlp.data);

      if (!tlp.sof) begin
	 let count = tlpWordCount(tlp);
	 // if sof is false, then count will be at least 1
	 function Bit#(32) f(Integer i);
	    begin
	       if (i < count)
		  return tlpvec[3-i];
	       else
		  return 32'hbad0beef;
	    end
	 endfunction
	 vec = genWith(f);
	 // The MIMO implicit guard only checks for space to enqueue 1 element
	 // so we explicitly check for the number of elements required
	 // otherwise elements in the queue will be overwritten.
	 if (completionMimo.enqReadyN(fromInteger(count))
	    && completionTagMimo.enqReadyN(fromInteger(count)))
	    begin
	       completionMimo.enq(fromInteger(count), vec);
	       completionTagMimo.enq(fromInteger(count), replicate(lastTag));
	       handled = True;
	    end
      end
      else if (hdr_3dw.format == MEM_WRITE_3DW_DATA
	       && hdr_3dw.pkttype == COMPLETION
	       && completionMimo.enqReadyN(1)
	       && completionTagMimo.enqReadyN(1)) begin
	    vec[0] = hdr_3dw.data;
	    completionMimo.enq(1, vec);
	    lastTag <= hdr_3dw.tag;
	    completionTagMimo.enq(1, replicate(hdr_3dw.tag));
	    handled = True;
      end
      //$display("tlpIn handled=%d tlp=%h\n", handled, tlp);
      if (handled)
	 tlpInFifo.deq();
   endrule

    interface GetPut tlps = tuple2(toGet(tlpOutFifo),toPut(tlpInFifo));
    interface Axi3Slave slave;
	interface Put req_aw;
	   method Action put(Axi3WriteRequest#(40, 6) req)
	      if (writeBurstCount == 0);

	      let burstLen = req.len;
	      let addr = req.address;
	      let awid = req.id;

	      TLPLength tlplen = fromInteger(valueOf(busWidthWords))*(extend(burstLen) + 1);
	      TLPData#(16) tlp = defaultValue;
	      tlp.sof = True;
	      tlp.eof = False;
	      tlp.hit = 7'h00;
	      tlp.be = 16'hffff;

	      $display("slave.writeAddr tlplen=%d burstLen=%d", tlplen, burstLen);
	      if ((addr >> 32) != 0) begin
		 TLPMemory4DWHeader hdr_4dw = defaultValue;
		 hdr_4dw.format = MEM_WRITE_4DW_DATA;
		 hdr_4dw.tag = extend(awid);
		 hdr_4dw.reqid = my_id;
		 hdr_4dw.nosnoop = SNOOPING_REQD;
		 hdr_4dw.addr = addr[40-1:2];
		 hdr_4dw.length = tlplen;
		 hdr_4dw.firstbe = 4'hf;
		 hdr_4dw.lastbe = (tlplen > 1) ? 4'hf : 0;
		 tlp.data = pack(hdr_4dw);
	      end
	      else begin
		 TLPMemoryIO3DWHeader hdr_3dw = defaultValue;
		 hdr_3dw.format = MEM_WRITE_3DW_DATA;
		 hdr_3dw.tag = extend(awid);
		 hdr_3dw.reqid = my_id;
		 hdr_3dw.nosnoop = SNOOPING_REQD;
		 hdr_3dw.addr = addr[32-1:2];
		 hdr_3dw.length = tlplen;
		 hdr_3dw.firstbe = 4'hf;
		 hdr_3dw.lastbe = (tlplen > 1) ? 4'hf : 0;

		 tlp.be = 16'hfff0; // no data word in this TLP

		 tlp.data = pack(hdr_3dw);
	      end
	      tlpWriteHeaderFifo.enq(tlp);
	      writeBurstCount <= zeroExtend(burstLen)+1;
	      writeTag <= extend(awid);
           endmethod
	endinterface : req_aw
       interface Put resp_write;
	   method Action put(Axi3WriteData#(busWidth,6) wdata)
	      provisos (Bits#(Vector#(busWidthWords, Bit#(32)), busWidth)) if (writeBurstCount > 0 && writeDataMimo.enqReadyN(fromInteger(valueOf(busWidthWords))));

	      writeBurstCount <= writeBurstCount - 1;
	      Vector#(busWidthWords, Bit#(32)) v = unpack(wdata.data);
	      writeDataMimo.enq(fromInteger(valueOf(busWidthWords)), v);
           endmethod
       endinterface : resp_write
       interface GetF resp_b;
	   method ActionValue#(Axi3WriteResponse#(6)) get();
	      let tag = doneTag.first();
	      doneTag.deq();
	      return Axi3WriteResponse { resp: 0, id: truncate(tag)};
           endmethod
	   method Bool notEmpty() = doneTag.notEmpty();
	endinterface: resp_b
       interface Put req_ar;
	   method Action put(Axi3ReadRequest#(40,6) req) if (writeDwCount == 0);
	      let burstLen = req.len;
	      let addr = req.address;
	      let arid = req.id;

	       TLPData#(16) tlp = defaultValue;
	       tlp.sof = True;
	       tlp.eof = True;
	       tlp.hit = 7'h00;
	       TLPLength tlplen = fromInteger(valueOf(busWidthWords))*(extend(burstLen) + 1);
	       if (addr[39:32] != 0) begin
		   TLPMemory4DWHeader hdr_4dw = defaultValue;
		   hdr_4dw.format = MEM_READ_4DW_NO_DATA;
		   hdr_4dw.tag = extend(arid);
		   hdr_4dw.reqid = my_id;
		   hdr_4dw.nosnoop = SNOOPING_REQD;
		   hdr_4dw.addr = addr[40-1:2];
		   hdr_4dw.length = tlplen;
		   hdr_4dw.firstbe = 4'hf;
		   hdr_4dw.lastbe = (tlplen > 1) ? 4'hf : 0;
		   tlp.data = pack(hdr_4dw);
		   tlp.be = 16'hffff;
	       end
	       else begin
		   TLPMemoryIO3DWHeader hdr_3dw = defaultValue;
		   hdr_3dw.format = MEM_READ_3DW_NO_DATA;
		   hdr_3dw.tag = extend(arid);
		   hdr_3dw.reqid = my_id;
		   hdr_3dw.nosnoop = SNOOPING_REQD;
		   hdr_3dw.addr = addr[32-1:2];
		   hdr_3dw.length = tlplen;
		   hdr_3dw.firstbe = 4'hf;
		   hdr_3dw.lastbe = (tlplen > 1) ? 4'hf : 0;
		   tlp.data = pack(hdr_3dw);
		   tlp.be = 16'hfff0;
	       end
	       tlpOutFifo.enq(tlp);
           endmethod
       endinterface : req_ar
       interface GetF resp_read;
	   method ActionValue#(Axi3ReadResponse#(buswidth,6)) get() if (completionMimo.deqReadyN(fromInteger(valueOf(busWidthWords))));
	      let data_v = completionMimo.first;
	      completionMimo.deq(fromInteger(valueOf(busWidthWords)));
	      completionTagMimo.deq(fromInteger(valueOf(busWidthWords)));
              Bit#(buswidth) v = 0;
	      for (Integer i = 0; i < valueOf(busWidthWords); i = i+1)
		 v[(i+1)*32-1:i*32] = byteSwap(data_v[i]);
	      return Axi3ReadResponse { data: v, last: 0, id: truncate(completionTagMimo.first[0]), resp: 0 };
           endmethod
	  method Bool notEmpty();
	     return (completionMimo.deqReadyN(fromInteger(valueOf(busWidthWords))));
	  endmethod
	endinterface: resp_read
    endinterface: slave
   method Bool tlpOutFifoNotEmpty() = tlpOutFifo.notEmpty;
   interface Reg use4dw = use4dwReg;
endmodule: mkAxiSlaveEngine