remove unneeded version of mkPareff

remove unneeded version of mkPareff

// Copyright (c) 2015 Connectal Project

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

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

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

import Vector::*;
import FIFO::*;
import FIFOF::*;
import GetPut::*;

import MemTypes::*;

interface Pareff#(numeric type dataWidth);
   method Action init(Bit#(32) id, Bit#(32) handle, Bit#(32) size);
   method Action initfd(Bit#(32) id, Bit#(32) fd);
   method Action write(Bit#(32) handle, Bit#(32) addr, Bit#(dataWidth) v);
   method Action readrequest(Bit#(32) handle, Bit#(32) addr);
   method ActionValue#(Bit#(dataWidth)) readresponse();
endinterface

`ifdef BSIM
import "BDPI" function ActionValue#(Bit#(32)) pareff_init(Bit#(32) id, Bit#(32) handle, Bit#(32) size);
import "BDPI" function ActionValue#(Bit#(32)) pareff_initfd(Bit#(32) id, Bit#(32) fd);

// implemented in BsimDma.cpp
import "BDPI" function Action write_pareff32(Bit#(32) handle, Bit#(32) addr, Bit#(32) v);
import "BDPI" function Action write_pareff64(Bit#(32) handle, Bit#(32) addr, Bit#(64) v);
import "BDPI" function ActionValue#(Bit#(32)) read_pareff32(Bit#(32) handle, Bit#(32) addr);
import "BDPI" function ActionValue#(Bit#(64)) read_pareff64(Bit#(32) handle, Bit#(32) addr);

module mkPareff(Pareff#(dataWidth) ifc)
   provisos (Mul#(TDiv#(dataWidth, 32), 32, dataWidth));
   FIFO#(Bit#(dataWidth)) dataFifo <- mkFIFO();
      method Action init(Bit#(32) id, Bit#(32) handle, Bit#(32) size);
	 let v <- pareff_init(id, handle, size);
	 //return v;
      endmethod
      method Action initfd(Bit#(32) id, Bit#(32) fd);
	 let v <- pareff_initfd(id, fd);
	 //return v;
      endmethod
      method Action write(Bit#(32) handle, Bit#(32) addr, Bit#(dataWidth) v);
	  Vector#(TDiv#(dataWidth, 32), Bit#(32)) vs = unpack(v);
	  function Action write32(Integer i, Bit#(32) vv);
	     action
		write_pareff32(handle, addr+4*fromInteger(i), vv);
	     endaction
	  endfunction
	  mapM_(uncurry(write32), zip(genVector(), vs));
      endmethod
      method Action  readrequest(Bit#(32) handle, Bit#(32) addr);
	  function ActionValue#(Bit#(32)) read32(Integer i);
	     actionvalue
		let v <- read_pareff32(handle, addr+4*fromInteger(i));
		return v;
	     endactionvalue
	  endfunction
   	  Vector#(TDiv#(dataWidth,32),Bit#(32)) vs <- mapM(read32, genVector());
	  dataFifo.enq(pack(vs));
      endmethod
      method ActionValue#(Bit#(dataWidth)) readresponse();
          let v <- toGet(dataFifo).get();
	  return v;
      endmethod
endmodule
`endif

`ifdef XSIM
interface XsimMemReadWrite;
   method Action init(Bit#(32) id, Bit#(32) handle, Bit#(32) size);
   method Action initfd(Bit#(32) id, Bit#(32) fd);
   method Action write32(Bit#(32) handle, Bit#(32) addr, Bit#(32) v);
   method Action readrequest(Bit#(32) handle, Bit#(32) addr);
   method ActionValue#(Bit#(32)) readresponse();
endinterface

import "BVI" XsimMemReadWrite =
module mkXsimReadWrite(XsimMemReadWrite);
   method init(init_id, init_handle, init_size) enable (en_init);
   method initfd(initfd_id, initfd_fd) enable (en_initfd);
   method write32(write32_handle, write32_addr, write32_data) enable (en_write32);
   method readrequest(readrequest_handle, readrequest_addr) enable (en_readrequest) ready (rdy_readrequest);
   method readresponse_data readresponse() enable (en_readresponse) ready (rdy_readresponse);
   schedule (init, initfd, write32, readrequest, readresponse) CF (init, initfd, write32, readrequest, readresponse);
endmodule

module mkPareff(Pareff#(dataWidth) ifc)
   provisos (Mul#(TDiv#(dataWidth, 32), 32, dataWidth));
   Vector#(TDiv#(dataWidth,32),XsimMemReadWrite) rws <- replicateM(mkXsimReadWrite());
   method Action init(Bit#(32) id, Bit#(32) handle, Bit#(32) size);
      rws[0].init(id, handle, size);
   endmethod
   method Action initfd(Bit#(32) id, Bit#(32) fd);
      rws[0].initfd(id, fd);
   endmethod
   method Action write(Bit#(32) handle, Bit#(32) addr, Bit#(dataWidth) v);
      Vector#(TDiv#(dataWidth, 32), Bit#(32)) vs = unpack(v);
      function Action write32(Integer i, Bit#(32) vv);
	 action
	    rws[i].write32(handle, addr+4*fromInteger(i), vv);
	 endaction
      endfunction
      mapM_(uncurry(write32), zip(genVector(), vs));
   endmethod
   method Action readrequest(Bit#(32) handle, Bit#(32) addr);
      function Action doreadrequest(Integer i);
	 action
	    rws[i].readrequest(handle, addr+4*fromInteger(i));
	 endaction
      endfunction
      Vector#(TDiv#(dataWidth,32),Integer) indexes = genVector();
      mapM_(doreadrequest, indexes);
   endmethod
   method ActionValue#(Bit#(dataWidth)) readresponse();
      function ActionValue#(Bit#(32)) readresponse32(Integer i);
	 actionvalue
	    let v <- rws[i].readresponse();
	    return v;
	 endactionvalue
      endfunction
      Vector#(TDiv#(dataWidth,32),Bit#(32)) vs <- mapM(readresponse32, genVector());
      return pack(vs);
   endmethod
endmodule
`endif

module mkPareffDmaMaster(PhysMemSlave#(serverAddrWidth,serverBusWidth))
   provisos(Div#(serverBusWidth,8,dataWidthBytes),
	    Mul#(dataWidthBytes,8,serverBusWidth),
	    Log#(dataWidthBytes,beatShift),
	    Mul#(TDiv#(serverBusWidth, 32), 32, serverBusWidth),
	    Bits#(Tuple2#(Bit#(64), PhysMemRequest#(serverAddrWidth)), a__)
	    );

   let verbose = False;
   Pareff#(serverBusWidth) rw <- mkPareff();

   Reg#(Bit#(BurstLenSize))  readLenReg <- mkReg(0);
   Reg#(Bit#(32))         readOffsetReg <- mkReg(0);

   Reg#(Bit#(serverAddrWidth)) writeAddrr <- mkReg(0);
   Reg#(Bit#(BurstLenSize))  writeLen <- mkReg(0);
   Reg#(Bit#(MemTagSize)) writeId <- mkReg(0);

   let readLatency_I = 150;
   let writeLatency_I = 150;

   Bit#(64) readLatency = fromInteger(readLatency_I);
   Bit#(64) writeLatency = fromInteger(writeLatency_I);

   Reg#(Bit#(64)) req_ar_b_ts <- mkReg(0);
   Reg#(Bit#(64)) req_aw_b_ts <- mkReg(0);
   Reg#(Bit#(64)) cycles <- mkReg(0);
   Reg#(Bit#(64)) last_reqAr <- mkReg(0);
   Reg#(Bit#(64)) last_read_eob <- mkReg(0);
   Reg#(Bit#(64)) last_write_eob <- mkReg(0);

   FIFOF#(Tuple2#(Bit#(64), PhysMemRequest#(serverAddrWidth)))  readDelayFifo <- mkSizedFIFOF(readLatency_I);
   FIFOF#(Tuple2#(Bit#(64),PhysMemRequest#(serverAddrWidth))) writeDelayFifo <- mkSizedFIFOF(writeLatency_I);

   FIFOF#(Tuple2#(Bit#(64), Bit#(MemTagSize))) bFifo <- mkSizedFIFOF(writeLatency_I);
   FIFOF#(Tuple2#(Bit#(MemTagSize),Bool)) taglastfifo <- mkFIFOF();
   rule increment_cycle;
      cycles <= cycles+1;
   endrule

   let read_jitter = True; //cycles[4:0] == 0;
   let write_jitter = True; //cycles[4:0] == 5;

   Reg#(Bit#(8))  burstReg <- mkReg(0);
   FIFO#(Bit#(8)) reqs <- mkSizedFIFO(32);

   let beat_shift = fromInteger(valueOf(beatShift));

   rule read_rule if (readDelayFifo.notEmpty() && (cycles-tpl_1(readDelayFifo.first) > readLatency));
	 match { .reqTime, .req } = readDelayFifo.first;
	 Bit#(BurstLenSize) readLen = readLenReg;
	 Bit#(32) readOffset = readOffsetReg;
	 Bit#(MemTagSize) tag = req.tag;
	 Bit#(8) handle = req.addr[39:32];

	 if (readLen == 0) begin
	    req_ar_b_ts <= cycles;
	    readLen     = req.burstLen>>beat_shift;
	    readOffset  = 0;
	 end
	 rw.readrequest(extend(handle), req.addr[31:0]+readOffset);
	 let last = (readLen == 1);
	 if (last)
	    readDelayFifo.deq();
	 taglastfifo.enq(tuple2(tag, last));
	 readLenReg <= readLen - 1;
	 readOffsetReg <= readOffset + fromInteger(valueOf(serverBusWidth)/8);
   endrule

   interface PhysMemReadServer read_server;
      interface Put readReq;
	 method Action put(PhysMemRequest#(serverAddrWidth) req);
            if (verbose) $display("%d axiSlave.read.readAddr %h bc %d", cycles, req.addr, req.burstLen);
	    //readAddrGenerator.request.put(req);
	    readDelayFifo.enq(tuple2(cycles,req));
	 endmethod
      endinterface
      interface Get readData;
	 method ActionValue#(MemData#(serverBusWidth)) get();
	     match { .tag, .last } <- toGet(taglastfifo).get();
 	     let v <- rw.readresponse();
	     return MemData { data: v, tag: tag, last: last };
	 endmethod
      endinterface
   endinterface
   interface PhysMemWriteServer write_server;
      interface Put writeReq;
	 method Action put(PhysMemRequest#(serverAddrWidth) req);
	 //$display("mkBsimHost::req_aw id=%d", req.tag);
	 writeDelayFifo.enq(tuple2(cycles,req));
	 endmethod
      endinterface
      interface Put writeData;
	 method Action put(MemData#(serverBusWidth) resp) if (((writeLen > 0) || (writeLen == 0 && (cycles-tpl_1(writeDelayFifo.first)) > writeLatency)) && write_jitter);
	    //let addrBeat <- writeAddrGenerator.addrBeat.get();
	    //let addr = addrBeat.addr;
	    //Bit#(bramAddrWidth) regFileAddr = truncate(addr/fromInteger(valueOf(TDiv#(serverBusWidth,8))));
            //br.request.put(BRAMRequest{write:True, responseOnWrite:False, address:regFileAddr, datain:resp.data});
	 Bit#(BurstLenSize) write_len = ?;
	 Bit#(serverAddrWidth) write_addr = ?;
	 Bit#(MemTagSize) write_id = ?;
	 Bit#(8) handle = ?;
	 if (writeLen == 0 && (cycles-tpl_1(writeDelayFifo.first)) > writeLatency) begin
	    req_aw_b_ts <= cycles;
	    let req = tpl_2(writeDelayFifo.first);
	    writeDelayFifo.deq;
	    write_addr = req.addr;
	    write_len = req.burstLen>>beat_shift;
	    write_id = req.tag;
	    handle = req.addr[39:32];
	    //$display("mkBsimHost::resp_write_a: %d %d", req.tag,  cycles-last_write_eob);
	    //last_write_eob <= cycles;
	 end
	 else begin
	    //$display("mkBsimHost::resp_write_b: %d %d", writeId,  cycles-last_write_eob);
	    //last_write_eob <= cycles;
	    handle = writeAddrr[39:32];
	    write_len = writeLen;
	    write_addr = writeAddrr;
	    write_id = writeId;
	 end
	 rw.write(extend(handle), write_addr[31:0], resp.data);
	 //$display("write_resp(%d): handle=%d addr=%h v=%h", cycles, handle, write_addr, resp.data);
	 writeId <= write_id;
	 writeLen <= write_len - 1;
	 writeAddrr <= write_addr + fromInteger(valueOf(serverBusWidth)/8);
	 if (write_len == 1) begin
	    bFifo.enq(tuple2(cycles,write_id));
	 end
            //if (verbose) $display("%d write_server.writeAddr %h bc %d", cycles, req.addr, req.burstLen);
            //if (verbose) $display("%d write_server.writeData %h %h %d", cycles, addr, resp.data, addrBeat.bc);
            //if (addrBeat.last)
               //writeTagFifo.enq(addrBeat.tag);
	 endmethod
      endinterface
      interface Get writeDone;
	 method ActionValue#(Bit#(MemTagSize)) get() if ((cycles-tpl_1(bFifo.first)) > writeLatency);
	 bFifo.deq();
	 return tpl_2(bFifo.first());
	 endmethod
      endinterface
   endinterface
endmodule

// Copyright (c) 2015 Connectal Project

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

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

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

import Vector::*;
import FIFO::*;
import FIFOF::*;
import GetPut::*;

import MemTypes::*;

interface Pareff#(numeric type dataWidth);
   method Action init(Bit#(32) id, Bit#(32) handle, Bit#(32) size);
   method Action initfd(Bit#(32) id, Bit#(32) fd);
   method Action write(Bit#(32) handle, Bit#(32) addr, Bit#(dataWidth) v);
   method Action readrequest(Bit#(32) handle, Bit#(32) addr);
   method ActionValue#(Bit#(dataWidth)) readresponse();
endinterface

`ifdef BSIM
import "BDPI" function ActionValue#(Bit#(32)) pareff_init(Bit#(32) id, Bit#(32) handle, Bit#(32) size);
import "BDPI" function ActionValue#(Bit#(32)) pareff_initfd(Bit#(32) id, Bit#(32) fd);

// implemented in BsimDma.cpp
import "BDPI" function Action write_pareff32(Bit#(32) handle, Bit#(32) addr, Bit#(32) v);
import "BDPI" function Action write_pareff64(Bit#(32) handle, Bit#(32) addr, Bit#(64) v);
import "BDPI" function ActionValue#(Bit#(32)) read_pareff32(Bit#(32) handle, Bit#(32) addr);
import "BDPI" function ActionValue#(Bit#(64)) read_pareff64(Bit#(32) handle, Bit#(32) addr);

module mkPareff(Pareff#(dataWidth) ifc)
   provisos (Mul#(TDiv#(dataWidth, 32), 32, dataWidth));
   FIFO#(Bit#(dataWidth)) dataFifo <- mkFIFO();
      method Action init(Bit#(32) id, Bit#(32) handle, Bit#(32) size);
	 let v <- pareff_init(id, handle, size);
	 //return v;
      endmethod
      method Action initfd(Bit#(32) id, Bit#(32) fd);
	 let v <- pareff_initfd(id, fd);
	 //return v;
      endmethod
      method Action write(Bit#(32) handle, Bit#(32) addr, Bit#(dataWidth) v);
	  Vector#(TDiv#(dataWidth, 32), Bit#(32)) vs = unpack(v);
	  function Action write32(Integer i, Bit#(32) vv);
	     action
		write_pareff32(handle, addr+4*fromInteger(i), vv);
	     endaction
	  endfunction
	  mapM_(uncurry(write32), zip(genVector(), vs));
      endmethod
      method Action  readrequest(Bit#(32) handle, Bit#(32) addr);
	  function ActionValue#(Bit#(32)) read32(Integer i);
	     actionvalue
		let v <- read_pareff32(handle, addr+4*fromInteger(i));
		return v;
	     endactionvalue
	  endfunction
   	  Vector#(TDiv#(dataWidth,32),Bit#(32)) vs <- mapM(read32, genVector());
	  dataFifo.enq(pack(vs));
      endmethod
      method ActionValue#(Bit#(dataWidth)) readresponse();
          let v <- toGet(dataFifo).get();
	  return v;
      endmethod
endmodule
`endif

`ifdef XSIM
interface XsimMemReadWrite;
   method Action init(Bit#(32) id, Bit#(32) handle, Bit#(32) size);
   method Action initfd(Bit#(32) id, Bit#(32) fd);
   method Action write32(Bit#(32) handle, Bit#(32) addr, Bit#(32) v);
   method Action readrequest(Bit#(32) handle, Bit#(32) addr);
   method ActionValue#(Bit#(32)) readresponse();
endinterface

import "BVI" XsimMemReadWrite =
module mkXsimReadWrite(XsimMemReadWrite);
   method init(init_id, init_handle, init_size) enable (en_init);
   method initfd(initfd_id, initfd_fd) enable (en_initfd);
   method write32(write32_handle, write32_addr, write32_data) enable (en_write32);
   method readrequest(readrequest_handle, readrequest_addr) enable (en_readrequest) ready (rdy_readrequest);
   method readresponse_data readresponse() enable (en_readresponse) ready (rdy_readresponse);
   schedule (init, initfd, write32, readrequest, readresponse) CF (init, initfd, write32, readrequest, readresponse);
endmodule

module mkPareff(Pareff#(dataWidth) ifc)
   provisos (Mul#(TDiv#(dataWidth, 32), 32, dataWidth));
   Vector#(TDiv#(dataWidth,32),XsimMemReadWrite) rws <- replicateM(mkXsimReadWrite());
   method Action init(Bit#(32) id, Bit#(32) handle, Bit#(32) size);
      rws[0].init(id, handle, size);
   endmethod
   method Action initfd(Bit#(32) id, Bit#(32) fd);
      rws[0].initfd(id, fd);
   endmethod
   method Action write(Bit#(32) handle, Bit#(32) addr, Bit#(dataWidth) v);
      Vector#(TDiv#(dataWidth, 32), Bit#(32)) vs = unpack(v);
      function Action write32(Integer i, Bit#(32) vv);
	 action
	    rws[i].write32(handle, addr+4*fromInteger(i), vv);
	 endaction
      endfunction
      mapM_(uncurry(write32), zip(genVector(), vs));
   endmethod
   method Action readrequest(Bit#(32) handle, Bit#(32) addr);
      function Action doreadrequest(Integer i);
	 action
	    rws[i].readrequest(handle, addr+4*fromInteger(i));
	 endaction
      endfunction
      Vector#(TDiv#(dataWidth,32),Integer) indexes = genVector();
      mapM_(doreadrequest, indexes);
   endmethod
   method ActionValue#(Bit#(dataWidth)) readresponse();
      function ActionValue#(Bit#(32)) readresponse32(Integer i);
	 actionvalue
	    let v <- rws[i].readresponse();
	    return v;
	 endactionvalue
      endfunction
      Vector#(TDiv#(dataWidth,32),Bit#(32)) vs <- mapM(readresponse32, genVector());
      return pack(vs);
   endmethod
endmodule
`endif

`ifndef BSIM
`ifndef XSIM
module mkPareff(Pareff#(dataWidth) ifc);
   method Action init(Bit#(32) id, Bit#(32) handle, Bit#(32) size);
   endmethod
   method Action initfd(Bit#(32) id, Bit#(32) fd);
   endmethod
   method Action write(Bit#(32) handle, Bit#(32) addr, Bit#(dataWidth) v);
   endmethod
   method ActionValue#(Bit#(dataWidth)) read(Bit#(32) handle, Bit#(32) addr);
      return 0;
   endmethod
endmodule
`endif
`endif

module mkPareffDmaMaster(PhysMemSlave#(serverAddrWidth,serverBusWidth))
   provisos(Div#(serverBusWidth,8,dataWidthBytes),
	    Mul#(dataWidthBytes,8,serverBusWidth),
	    Log#(dataWidthBytes,beatShift),
	    Mul#(TDiv#(serverBusWidth, 32), 32, serverBusWidth),
	    Bits#(Tuple2#(Bit#(64), PhysMemRequest#(serverAddrWidth)), a__)
	    );

   let verbose = False;
   Pareff#(serverBusWidth) rw <- mkPareff();

   Reg#(Bit#(BurstLenSize))  readLenReg <- mkReg(0);
   Reg#(Bit#(32))         readOffsetReg <- mkReg(0);

   Reg#(Bit#(serverAddrWidth)) writeAddrr <- mkReg(0);
   Reg#(Bit#(BurstLenSize))  writeLen <- mkReg(0);
   Reg#(Bit#(MemTagSize)) writeId <- mkReg(0);

   let readLatency_I = 150;
   let writeLatency_I = 150;

   Bit#(64) readLatency = fromInteger(readLatency_I);
   Bit#(64) writeLatency = fromInteger(writeLatency_I);

   Reg#(Bit#(64)) req_ar_b_ts <- mkReg(0);
   Reg#(Bit#(64)) req_aw_b_ts <- mkReg(0);
   Reg#(Bit#(64)) cycles <- mkReg(0);
   Reg#(Bit#(64)) last_reqAr <- mkReg(0);
   Reg#(Bit#(64)) last_read_eob <- mkReg(0);
   Reg#(Bit#(64)) last_write_eob <- mkReg(0);

   FIFOF#(Tuple2#(Bit#(64), PhysMemRequest#(serverAddrWidth)))  readDelayFifo <- mkSizedFIFOF(readLatency_I);
   FIFOF#(Tuple2#(Bit#(64),PhysMemRequest#(serverAddrWidth))) writeDelayFifo <- mkSizedFIFOF(writeLatency_I);

   FIFOF#(Tuple2#(Bit#(64), Bit#(MemTagSize))) bFifo <- mkSizedFIFOF(writeLatency_I);
   FIFOF#(Tuple2#(Bit#(MemTagSize),Bool)) taglastfifo <- mkFIFOF();
   rule increment_cycle;
      cycles <= cycles+1;
   endrule

   let read_jitter = True; //cycles[4:0] == 0;
   let write_jitter = True; //cycles[4:0] == 5;

   Reg#(Bit#(8))  burstReg <- mkReg(0);
   FIFO#(Bit#(8)) reqs <- mkSizedFIFO(32);

   let beat_shift = fromInteger(valueOf(beatShift));

   rule read_rule if (readDelayFifo.notEmpty() && (cycles-tpl_1(readDelayFifo.first) > readLatency));
	 match { .reqTime, .req } = readDelayFifo.first;
	 Bit#(BurstLenSize) readLen = readLenReg;
	 Bit#(32) readOffset = readOffsetReg;
	 Bit#(MemTagSize) tag = req.tag;
	 Bit#(8) handle = req.addr[39:32];

	 if (readLen == 0) begin
	    req_ar_b_ts <= cycles;
	    readLen     = req.burstLen>>beat_shift;
	    readOffset  = 0;
	 end
	 rw.readrequest(extend(handle), req.addr[31:0]+readOffset);
	 let last = (readLen == 1);
	 if (last)
	    readDelayFifo.deq();
	 taglastfifo.enq(tuple2(tag, last));
	 readLenReg <= readLen - 1;
	 readOffsetReg <= readOffset + fromInteger(valueOf(serverBusWidth)/8);
   endrule

   interface PhysMemReadServer read_server;
      interface Put readReq;
	 method Action put(PhysMemRequest#(serverAddrWidth) req);
            if (verbose) $display("%d axiSlave.read.readAddr %h bc %d", cycles, req.addr, req.burstLen);
	    //readAddrGenerator.request.put(req);
	    readDelayFifo.enq(tuple2(cycles,req));
	 endmethod
      endinterface
      interface Get readData;
	 method ActionValue#(MemData#(serverBusWidth)) get();
	     match { .tag, .last } <- toGet(taglastfifo).get();
 	     let v <- rw.readresponse();
	     return MemData { data: v, tag: tag, last: last };
	 endmethod
      endinterface
   endinterface
   interface PhysMemWriteServer write_server;
      interface Put writeReq;
	 method Action put(PhysMemRequest#(serverAddrWidth) req);
	 //$display("mkBsimHost::req_aw id=%d", req.tag);
	 writeDelayFifo.enq(tuple2(cycles,req));
	 endmethod
      endinterface
      interface Put writeData;
	 method Action put(MemData#(serverBusWidth) resp) if (((writeLen > 0) || (writeLen == 0 && (cycles-tpl_1(writeDelayFifo.first)) > writeLatency)) && write_jitter);
	    //let addrBeat <- writeAddrGenerator.addrBeat.get();
	    //let addr = addrBeat.addr;
	    //Bit#(bramAddrWidth) regFileAddr = truncate(addr/fromInteger(valueOf(TDiv#(serverBusWidth,8))));
            //br.request.put(BRAMRequest{write:True, responseOnWrite:False, address:regFileAddr, datain:resp.data});
	 Bit#(BurstLenSize) write_len = ?;
	 Bit#(serverAddrWidth) write_addr = ?;
	 Bit#(MemTagSize) write_id = ?;
	 Bit#(8) handle = ?;
	 if (writeLen == 0 && (cycles-tpl_1(writeDelayFifo.first)) > writeLatency) begin
	    req_aw_b_ts <= cycles;
	    let req = tpl_2(writeDelayFifo.first);
	    writeDelayFifo.deq;
	    write_addr = req.addr;
	    write_len = req.burstLen>>beat_shift;
	    write_id = req.tag;
	    handle = req.addr[39:32];
	    //$display("mkBsimHost::resp_write_a: %d %d", req.tag,  cycles-last_write_eob);
	    //last_write_eob <= cycles;
	 end
	 else begin
	    //$display("mkBsimHost::resp_write_b: %d %d", writeId,  cycles-last_write_eob);
	    //last_write_eob <= cycles;
	    handle = writeAddrr[39:32];
	    write_len = writeLen;
	    write_addr = writeAddrr;
	    write_id = writeId;
	 end
	 rw.write(extend(handle), write_addr[31:0], resp.data);
	 //$display("write_resp(%d): handle=%d addr=%h v=%h", cycles, handle, write_addr, resp.data);
	 writeId <= write_id;
	 writeLen <= write_len - 1;
	 writeAddrr <= write_addr + fromInteger(valueOf(serverBusWidth)/8);
	 if (write_len == 1) begin
	    bFifo.enq(tuple2(cycles,write_id));
	 end
            //if (verbose) $display("%d write_server.writeAddr %h bc %d", cycles, req.addr, req.burstLen);
            //if (verbose) $display("%d write_server.writeData %h %h %d", cycles, addr, resp.data, addrBeat.bc);
            //if (addrBeat.last)
               //writeTagFifo.enq(addrBeat.tag);
	 endmethod
      endinterface
      interface Get writeDone;
	 method ActionValue#(Bit#(MemTagSize)) get() if ((cycles-tpl_1(bFifo.first)) > writeLatency);
	 bFifo.deq();
	 return tpl_2(bFifo.first());
	 endmethod
      endinterface
   endinterface
endmodule


Rethinking IRQ-related instructions

Rethinking IRQ-related instructions

import ClientServer::*;
import GetPut::*;
import Connectable::*;
import FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import RegFile::*;
import FShow::*;
import AvalonCommon::*;
import AvalonMaster::*;
import Cache::*;
import Instructions::*;
import Trace::*;
import RFile::*;
import Divider::*;

typedef union tagged {
    struct { Ridx     r;    Bit#(32) data;               } WbREG;
    struct { Ridx     r;    Bit#(24) addr; Bit#(2) line; } WbLB;
    struct { Ridx     r;    Bit#(24) addr; Bit#(2) line; } WbLBU;
    struct { Ridx     r;    Bit#(24) addr; Bit#(1) line; } WbLH;
    struct { Ridx     r;    Bit#(24) addr; Bit#(1) line; } WbLHU;
    struct { Ridx     r;    Bit#(24) addr;               } WbLW;
    struct { Bit#( 8) data; Bit#(24) addr; Bit#(2) line; } WbSB;
    struct { Bit#(16) data; Bit#(24) addr; Bit#(1) line; } WbSH;
    struct { Bit#(32) data; Bit#(24) addr;               } WbSW;
} WBOp deriving (Eq, Bits);

instance FShow#(WBOp);
    function Fmt fshow(WBOp op);
        case (op) matches
            tagged WbREG .s: return $format("WbREG{r:%0d,data:%h}", s.r, s.data);
            tagged WbLB  .s: return $format("WbLB {r:%0d,addr:%h,line:%h}", s.r, s.addr, s.line);
            tagged WbLBU .s: return $format("WbLBU{r:%0d,addr:%h,line:%h}", s.r, s.addr, s.line);
            tagged WbLH  .s: return $format("WbLH {r:%0d,addr:%h,line:%h}", s.r, s.addr, s.line);
            tagged WbLHU .s: return $format("WbLHU{r:%0d,addr:%h,line:%h}", s.r, s.addr, s.line);
            tagged WbLW  .s: return $format("WbLW {r:%0d,addr:%h}", s.r, s.addr);
            tagged WbSB  .s: return $format("WbSB {data:%h,addr:%h,line:%h}", s.data, s.addr, s.line);
            tagged WbSH  .s: return $format("WbSH {data:%h,addr:%h,line:%h}", s.data, s.addr, s.line);
            tagged WbSW  .s: return $format("WbSW {data:%h,addr:%h}", s.data, s.addr);
        endcase
    endfunction
endinstance

module mkProcessor#(module#(AvalonMaster#(24,32)) mkMaster, function Bool ignoreCache(Bit#(24) addr)) (AvalonMasterWires#(24,32));
    AvalonMaster#(24,32) masterAdapter <- mkMaster;
    Cache#(24,12,12) cache <- mkCache(ignoreCache);

    Reg  #(Bit#(24)) fetchPC <- mkReg('h100);
    FIFOF#(Bit#(24)) jumpTo <- mkBypassFIFOF;
    FIFOF#(Bit#(24)) execPC <- mkPipelineFIFOF;
    FIFOF#(Bit#(32)) instFIFO <- mkBypassFIFOF;
    FIFOF#(Bit#(32)) dataFIFO <- mkBypassFIFOF;
    FIFOF#(WBOp) pendingLoad <- mkPipelineFIFOF;
    FIFOF#(WBOp) execToWB <- mkPipelineFIFOF;

    RFile rf <- mkRFile;
    Reg#(Bit#(32)) lo <- mkReg(0);
    Reg#(Bit#(32)) hi <- mkReg(0);

    Divider divider <- mkDivider;
    Reg#(Bool) dividerPending <- mkReg(False);

    Reg#(Bool) irqEnabled <- mkReg(False);
    Reg#(Bit#(32)) irqMask <- mkRegU;
    Reg#(Bit#(24)) irqReturnAddr <- mkRegU;
    RWire#(Bit#(32)) irqIn <- mkRWire;

    Reg#(Bool) clearCache <- mkReg(False);

    mkConnection(cache.busClient.request, masterAdapter.busServer.request);
    mkConnection(masterAdapter.busServer.response, cache.busClient.response);
    mkConnection(cache.instCache.response, toPut(instFIFO));
    mkConnection(cache.dataCache.response, toPut(dataFIFO));

    rule getIrq;
        let irq <- masterAdapter.irqGet.get;
        if(irqEnabled)
            irqIn.wset(irq);
    endrule

    rule clearTheCaches(clearCache);
        clearCache <= False;
        cache.clear;
    endrule

    rule dividerGetResult(dividerPending);
        match {.r,.q} <- divider.response.get;
        hi <= r;
        lo <= q;
        dividerPending <= False;
    endrule

    rule fetchAhead(!jumpTo.notEmpty && !clearCache);
        trace($format("[Fetch %h]", fetchPC));
        cache.instCache.request.put(AvalonRequest{command: Read, addr: fetchPC, data: ?});
        execPC.enq(fetchPC);
        if(irqIn.wget matches tagged Valid .irq)
          begin
            fetchPC <= 'h080;
            irqReturnAddr <= fetchPC + 1;
            irqMask <= irq;
            irqEnabled <= False;
          end
        else
          begin
            fetchPC <= fetchPC + 1;
          end
    endrule

    rule fetchJump(jumpTo.notEmpty && !clearCache);
        trace($format("[Fetch %h] [jump %h]", fetchPC, jumpTo.first));
        cache.instCache.request.put(AvalonRequest{command: Read, addr: fetchPC, data: ?});
        execPC.enq(fetchPC);
        if(irqIn.wget matches tagged Valid .irq)
          begin
            fetchPC <= 'h080;
            irqReturnAddr <= jumpTo.first;
            irqMask <= irq;
            irqEnabled <= False;
          end
        else
          begin
            fetchPC <= jumpTo.first;
          end
        jumpTo.deq;
    endrule

    rule exec(!pendingLoad.notEmpty && !dividerPending && !clearCache);
        Instr inst = unpack(instFIFO.first);
        let pc  = execPC.first;
        let pc1 = pc+1;
        trace($format("[Exec  %h] ", pc)+fshow(inst));
        instFIFO.deq;
        execPC.deq;

        function Bit#(32) zextSh(Sham sh) = zeroExtend(sh);

        case (inst) matches
            tagged ADD   .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rs)+rf.rd2(s.rt)                                });
            tagged ADDI  .s: execToWB.enq(WbREG{r:s.rt, data:rf.rd1(s.rs)+signExtend(s.im)                            });
            tagged ADDIU .s: execToWB.enq(WbREG{r:s.rt, data:rf.rd1(s.rs)+signExtend(s.im)                            });
            tagged ADDU  .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rs)+rf.rd2(s.rt)                                });
            tagged LUI   .s: execToWB.enq(WbREG{r:s.rt, data:{s.im,16'b0}                                             });
            tagged SUB   .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rs)-rf.rd2(s.rt)                                });
            tagged SUBU  .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rs)-rf.rd2(s.rt)                                });
            tagged SLL   .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rt)<<zextSh(s.sa)                               });
            tagged SLLV  .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rt)<<zextSh(rf.rd2(s.rs)[4:0])                  });
            tagged SRA   .s: execToWB.enq(WbREG{r:s.rd, data:signedShiftRight(rf.rd1(s.rt),zextSh(s.sa))              });
            tagged SRAV  .s: execToWB.enq(WbREG{r:s.rd, data:signedShiftRight(rf.rd1(s.rt),zextSh(rf.rd2(s.rs)[4:0])) });
            tagged SRL   .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rt)>>zextSh(s.sa)                               });
            tagged SRLV  .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rt)>>zextSh(rf.rd2(s.rs)[4:0])                  });
            tagged AND   .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rs)&rf.rd2(s.rt)                                });
            tagged ANDI  .s: execToWB.enq(WbREG{r:s.rt, data:rf.rd1(s.rs)&zeroExtend(s.im)                            });
            tagged NOR   .s: execToWB.enq(WbREG{r:s.rd, data:~(rf.rd1(s.rs)|rf.rd2(s.rt))                             });
            tagged OR    .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rs)|rf.rd2(s.rt)                                });
            tagged ORI   .s: execToWB.enq(WbREG{r:s.rt, data:rf.rd1(s.rs)|zeroExtend(s.im)                            });
            tagged XOR   .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rs)^rf.rd2(s.rt)                                });
            tagged XORI  .s: execToWB.enq(WbREG{r:s.rt, data:rf.rd1(s.rs)^zeroExtend(s.im)                            });
            tagged SLT   .s: execToWB.enq(WbREG{r:s.rd, data:zeroExtend(pack(signedLT(rf.rd1(s.rs),rf.rd2(s.rt))))    });
            tagged SLTI  .s: execToWB.enq(WbREG{r:s.rt, data:zeroExtend(pack(signedLT(rf.rd1(s.rs),signExtend(s.im))))});
            tagged SLTIU .s: execToWB.enq(WbREG{r:s.rt, data:zeroExtend(pack(         rf.rd1(s.rs)<signExtend(s.im)) )});
            tagged SLTU  .s: execToWB.enq(WbREG{r:s.rt, data:zeroExtend(pack(         rf.rd1(s.rs)<rf.rd2(s.rt)))     });
            tagged DIV   .s:
                action
                    dividerPending <= True;
                    divider.request.put(tuple3(rf.rd1(s.rs),rf.rd2(s.rt),True));
                endaction
            tagged DIVU  .s:
                action
                    dividerPending <= True;
                    divider.request.put(tuple3(rf.rd1(s.rs),rf.rd2(s.rt),False));
                endaction
            tagged MULT  .s:
                action
                    Int#(64) r1 = unpack(signExtend(rf.rd1(s.rs)));
                    Int#(64) r2 = unpack(signExtend(rf.rd2(s.rt)));
                    Bit#(64) mult = pack(r1*r2);
                    hi <= mult[63:32];
                    lo <= mult[31: 0];
                endaction
            tagged MULTU .s:
                action
                    UInt#(64) r1 = unpack(zeroExtend(rf.rd1(s.rs)));
                    UInt#(64) r2 = unpack(zeroExtend(rf.rd2(s.rt)));
                    Bit#(64) mult = pack(r1*r2);
                    hi <= mult[63:32];
                    lo <= mult[31: 0];
                endaction
            tagged MFHI  .s: execToWB.enq(WbREG{r:s.rd, data:hi});
            tagged MFLO  .s: execToWB.enq(WbREG{r:s.rd, data:lo});
            tagged BEQ   .s: if(rf.rd1(s.rs)==rf.rd2(s.rt)) jumpTo.enq(pc1 + signExtend(s.of));
            tagged BGEZ  .s: if(signedGE(rf.rd1(s.rs),0))   jumpTo.enq(pc1 + signExtend(s.of));
            tagged BGTZ  .s: if(signedGT(rf.rd1(s.rs),0))   jumpTo.enq(pc1 + signExtend(s.of));
            tagged BLEZ  .s: if(signedLE(rf.rd1(s.rs),0))   jumpTo.enq(pc1 + signExtend(s.of));
            tagged BLTZ  .s: if(signedLT(rf.rd1(s.rs),0))   jumpTo.enq(pc1 + signExtend(s.of));
            tagged BNE   .s: if(rf.rd1(s.rs)!=rf.rd2(s.rt)) jumpTo.enq(pc1 + signExtend(s.of));
            tagged J     .s: jumpTo.enq(truncate(s.tg));
            tagged JAL   .s:
                action
                    execToWB.enq(WbREG{r:31, data:zeroExtend(pc+2)<<2});
                    jumpTo.enq(truncate(s.tg));
                endaction
            tagged JALR  .s:
                action
                    execToWB.enq(WbREG{r:s.rd, data:zeroExtend(pc+2)<<2});
                    jumpTo.enq(truncate(rf.rd1(s.rs)>>2));
                endaction
            tagged JR    .s: jumpTo.enq(truncate(rf.rd1(s.rs)>>2));
            tagged LB    .s:
                action
                    Bit#(32) addr = rf.rd1(s.rb)+signExtend(s.of);
                    execToWB.enq(WbLB{r:s.rt, addr:truncate(addr>>2), line:addr[1:0]});
                endaction
            tagged LBU   .s:
                action
                    Bit#(32) addr = rf.rd1(s.rb)+signExtend(s.of);
                    execToWB.enq(WbLBU{r:s.rt, addr:truncate(addr>>2), line:addr[1:0]});
                endaction
            tagged LH    .s:
                action
                    Bit#(32) addr = rf.rd1(s.rb)+signExtend(s.of);
                    execToWB.enq(WbLH{r:s.rt, addr:truncate(addr>>2), line:addr[1]});
                endaction
            tagged LHU   .s:
                action
                    Bit#(32) addr = rf.rd1(s.rb)+signExtend(s.of);
                    execToWB.enq(WbLHU{r:s.rt, addr:truncate(addr>>2), line:addr[1]});
                endaction
            tagged LW    .s:
                action
                    Bit#(32) addr = rf.rd1(s.rb)+signExtend(s.of);
                    execToWB.enq(WbLW{r:s.rt, addr:truncate(addr>>2)});
                endaction
            tagged SB    .s:
                action
                    Bit#(32) addr = rf.rd1(s.rb)+signExtend(s.of);
                    execToWB.enq(WbSB{data:truncate(rf.rd2(s.rt)), addr:truncate(addr>>2), line:addr[1:0]});
                endaction
            tagged SH    .s:
                action
                    Bit#(32) addr = rf.rd1(s.rb)+signExtend(s.of);
                    execToWB.enq(WbSH{data:truncate(rf.rd2(s.rt)), addr:truncate(addr>>2), line:addr[1]});
                endaction
            tagged SW    .s:
                action
                    Bit#(32) addr = rf.rd1(s.rb)+signExtend(s.of);
                    execToWB.enq(WbSW{data:rf.rd2(s.rt), addr:truncate(addr>>2)});
                endaction
            tagged SYSC  .s:
                case (s.sc)
                    20'h0000: $finish();
                    20'h0001: $display("[syscall 1] %h", rf.rd1(4));
                    20'h0002: clearCache <= True;
                    20'h0003: irqEnabled <= False;
                    20'h0004: irqEnabled <= True;
                    20'h0005: execToWB.enq(WbREG{r:4,  data: irqMask});
                    20'h0006: action jumpTo.enq(irqReturnAddr); irqEnabled <= True; endaction
                endcase
            tagged ILLEGAL : $display("Exec error: Invalid instruction %h at pc=%h", pack(inst), pc);
        endcase
    endrule

    rule wbLoadResult(dataFIFO.notEmpty && !clearCache);
        let wbOp = pendingLoad.first;
        let data = dataFIFO.first;
        trace($format("[wbLoadResult] [data=%h] ",data)+fshow(wbOp));
        function Bit#(8) byteLine(Bit#(32) d, Bit#(2) line);
            return (case(line)
                2'h0: d[31:24];
                2'h1: d[23:16];
                2'h2: d[15: 8];
                2'h3: d[ 7: 0];
            endcase);
        endfunction
        function Bit#(16) halfLine(Bit#(32) d, Bit#(1) line);
            return (case(line)
                1'h0: d[31:16];
                1'h1: d[15: 0];
            endcase);
        endfunction
        function Bit#(32) byteSub(Bit#(32) d, Bit#(2) line, Bit#(8) newd);
            case(line)
                2'h0: d[31:24] = newd;
                2'h1: d[23:16] = newd;
                2'h2: d[15: 8] = newd;
                2'h3: d[ 7: 0] = newd;
            endcase
            return d;
        endfunction
        function Bit#(32) halfSub(Bit#(32) d, Bit#(1) line, Bit#(16) newd);
            case(line)
                1'h0: d[31:16] = newd;
                1'h1: d[15: 0] = newd;
            endcase
            return d;
        endfunction
        case (wbOp) matches
            tagged WbREG .s: $display("WB error: wbREG shouldn't cause a pending load");
            tagged WbLB  .s: rf.wr(s.r, signExtend(byteLine(data, s.line)));
            tagged WbLBU .s: rf.wr(s.r, zeroExtend(byteLine(data, s.line)));
            tagged WbLH  .s: rf.wr(s.r, signExtend(halfLine(data, s.line)));
            tagged WbLHU .s: rf.wr(s.r, zeroExtend(halfLine(data, s.line)));
            tagged WbLW  .s: rf.wr(s.r, data);
            tagged WbSB  .s: cache.dataCache.request.put(AvalonRequest{command: Write, addr: truncate(s.addr),
                                                                       data: byteSub(data, s.line, s.data)});
            tagged WbSH  .s: cache.dataCache.request.put(AvalonRequest{command: Write, addr: truncate(s.addr),
                                                                       data: halfSub(data, s.line, s.data)});
            tagged WbSW  .s: $display("WB error: wbSW shouldn't cause a pending load");
        endcase
        dataFIFO.deq;
        pendingLoad.deq;
    endrule

    rule wbFromExec(!dataFIFO.notEmpty && !clearCache);
        let wbOp = execToWB.first;
        trace($format("[wbFromExec] ")+fshow(wbOp));
        function Action reqLoad(Bit#(24) addr) = action
            cache.dataCache.request.put(AvalonRequest{command:Read, addr:truncate(addr), data:?});
            pendingLoad.enq(wbOp);
        endaction;
        case (wbOp) matches
            tagged WbREG .s: rf.wr(s.r, s.data);
            tagged WbLB  .s: reqLoad(s.addr);
            tagged WbLBU .s: reqLoad(s.addr);
            tagged WbLH  .s: reqLoad(s.addr);
            tagged WbLHU .s: reqLoad(s.addr);
            tagged WbLW  .s: reqLoad(s.addr);
            tagged WbSB  .s: reqLoad(s.addr);
            tagged WbSH  .s: reqLoad(s.addr);
            tagged WbSW  .s:
                cache.dataCache.request.put(AvalonRequest{command:Write, addr:truncate(s.addr), data:s.data});
        endcase
        execToWB.deq;
    endrule

    return masterAdapter.masterWires;
endmodule

import ClientServer::*;
import GetPut::*;
import Connectable::*;
import FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import RegFile::*;
import FShow::*;
import AvalonCommon::*;
import AvalonMaster::*;
import Cache::*;
import Instructions::*;
import Trace::*;
import RFile::*;
import Divider::*;

typedef union tagged {
    struct { Ridx     r;    Bit#(32) data;               } WbREG;
    struct { Ridx     r;    Bit#(24) addr; Bit#(2) line; } WbLB;
    struct { Ridx     r;    Bit#(24) addr; Bit#(2) line; } WbLBU;
    struct { Ridx     r;    Bit#(24) addr; Bit#(1) line; } WbLH;
    struct { Ridx     r;    Bit#(24) addr; Bit#(1) line; } WbLHU;
    struct { Ridx     r;    Bit#(24) addr;               } WbLW;
    struct { Bit#( 8) data; Bit#(24) addr; Bit#(2) line; } WbSB;
    struct { Bit#(16) data; Bit#(24) addr; Bit#(1) line; } WbSH;
    struct { Bit#(32) data; Bit#(24) addr;               } WbSW;
} WBOp deriving (Eq, Bits);

instance FShow#(WBOp);
    function Fmt fshow(WBOp op);
        case (op) matches
            tagged WbREG .s: return $format("WbREG{r:%0d,data:%h}", s.r, s.data);
            tagged WbLB  .s: return $format("WbLB {r:%0d,addr:%h,line:%h}", s.r, s.addr, s.line);
            tagged WbLBU .s: return $format("WbLBU{r:%0d,addr:%h,line:%h}", s.r, s.addr, s.line);
            tagged WbLH  .s: return $format("WbLH {r:%0d,addr:%h,line:%h}", s.r, s.addr, s.line);
            tagged WbLHU .s: return $format("WbLHU{r:%0d,addr:%h,line:%h}", s.r, s.addr, s.line);
            tagged WbLW  .s: return $format("WbLW {r:%0d,addr:%h}", s.r, s.addr);
            tagged WbSB  .s: return $format("WbSB {data:%h,addr:%h,line:%h}", s.data, s.addr, s.line);
            tagged WbSH  .s: return $format("WbSH {data:%h,addr:%h,line:%h}", s.data, s.addr, s.line);
            tagged WbSW  .s: return $format("WbSW {data:%h,addr:%h}", s.data, s.addr);
        endcase
    endfunction
endinstance

module mkProcessor#(module#(AvalonMaster#(24,32)) mkMaster, function Bool ignoreCache(Bit#(24) addr)) (AvalonMasterWires#(24,32));
    AvalonMaster#(24,32) masterAdapter <- mkMaster;
    Cache#(24,12,12) cache <- mkCache(ignoreCache);

    Reg  #(Bit#(24)) fetchPC <- mkReg('h100);
    FIFOF#(Bit#(24)) jumpTo <- mkBypassFIFOF;
    FIFOF#(Bit#(24)) execPC <- mkPipelineFIFOF;
    FIFOF#(Bit#(32)) instFIFO <- mkBypassFIFOF;
    FIFOF#(Bit#(32)) dataFIFO <- mkBypassFIFOF;
    FIFOF#(WBOp) pendingLoad <- mkPipelineFIFOF;
    FIFOF#(WBOp) execToWB <- mkPipelineFIFOF;

    RFile rf <- mkRFile;
    Reg#(Bit#(32)) lo <- mkReg(0);
    Reg#(Bit#(32)) hi <- mkReg(0);

    Divider divider <- mkDivider;
    Reg#(Bool) dividerPending <- mkReg(False);

    Reg#(Bool) irqEnabled <- mkReg(False);
    Reg#(Bit#(32)) irqMask <- mkRegU;
    Reg#(Bit#(24)) irqReturnAddr <- mkRegU;
    RWire#(Bit#(32)) irqIn <- mkRWire;

    Reg#(Bool) clearCache <- mkReg(False);

    mkConnection(cache.busClient.request, masterAdapter.busServer.request);
    mkConnection(masterAdapter.busServer.response, cache.busClient.response);
    mkConnection(cache.instCache.response, toPut(instFIFO));
    mkConnection(cache.dataCache.response, toPut(dataFIFO));

    rule getIrq;
        let irq <- masterAdapter.irqGet.get;
        if(irqEnabled)
            irqIn.wset(irq);
    endrule

    rule clearTheCaches(clearCache);
        clearCache <= False;
        cache.clear;
    endrule

    rule dividerGetResult(dividerPending);
        match {.r,.q} <- divider.response.get;
        hi <= r;
        lo <= q;
        dividerPending <= False;
    endrule

    rule fetchAhead(!jumpTo.notEmpty && !clearCache);
        trace($format("[Fetch %h]", fetchPC));
        cache.instCache.request.put(AvalonRequest{command: Read, addr: fetchPC, data: ?});
        execPC.enq(fetchPC);
        if(irqIn.wget matches tagged Valid .irq)
          begin
            fetchPC <= 'h080;
            irqReturnAddr <= fetchPC + 1;
            irqMask <= irq;
            irqEnabled <= False;
          end
        else
          begin
            fetchPC <= fetchPC + 1;
          end
    endrule

    rule fetchJump(jumpTo.notEmpty && !clearCache);
        trace($format("[Fetch %h] [jump %h]", fetchPC, jumpTo.first));
        cache.instCache.request.put(AvalonRequest{command: Read, addr: fetchPC, data: ?});
        execPC.enq(fetchPC);
        if(irqIn.wget matches tagged Valid .irq)
          begin
            fetchPC <= 'h080;
            irqReturnAddr <= jumpTo.first;
            irqMask <= irq;
            irqEnabled <= False;
          end
        else
          begin
            fetchPC <= jumpTo.first;
          end
        jumpTo.deq;
    endrule

    rule exec(!pendingLoad.notEmpty && !dividerPending && !clearCache);
        Instr inst = unpack(instFIFO.first);
        let pc  = execPC.first;
        let pc1 = pc+1;
        trace($format("[Exec  %h] ", pc)+fshow(inst));
        instFIFO.deq;
        execPC.deq;

        function Bit#(32) zextSh(Sham sh) = zeroExtend(sh);

        case (inst) matches
            tagged ADD   .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rs)+rf.rd2(s.rt)                                });
            tagged ADDI  .s: execToWB.enq(WbREG{r:s.rt, data:rf.rd1(s.rs)+signExtend(s.im)                            });
            tagged ADDIU .s: execToWB.enq(WbREG{r:s.rt, data:rf.rd1(s.rs)+signExtend(s.im)                            });
            tagged ADDU  .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rs)+rf.rd2(s.rt)                                });
            tagged LUI   .s: execToWB.enq(WbREG{r:s.rt, data:{s.im,16'b0}                                             });
            tagged SUB   .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rs)-rf.rd2(s.rt)                                });
            tagged SUBU  .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rs)-rf.rd2(s.rt)                                });
            tagged SLL   .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rt)<<zextSh(s.sa)                               });
            tagged SLLV  .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rt)<<zextSh(rf.rd2(s.rs)[4:0])                  });
            tagged SRA   .s: execToWB.enq(WbREG{r:s.rd, data:signedShiftRight(rf.rd1(s.rt),zextSh(s.sa))              });
            tagged SRAV  .s: execToWB.enq(WbREG{r:s.rd, data:signedShiftRight(rf.rd1(s.rt),zextSh(rf.rd2(s.rs)[4:0])) });
            tagged SRL   .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rt)>>zextSh(s.sa)                               });
            tagged SRLV  .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rt)>>zextSh(rf.rd2(s.rs)[4:0])                  });
            tagged AND   .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rs)&rf.rd2(s.rt)                                });
            tagged ANDI  .s: execToWB.enq(WbREG{r:s.rt, data:rf.rd1(s.rs)&zeroExtend(s.im)                            });
            tagged NOR   .s: execToWB.enq(WbREG{r:s.rd, data:~(rf.rd1(s.rs)|rf.rd2(s.rt))                             });
            tagged OR    .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rs)|rf.rd2(s.rt)                                });
            tagged ORI   .s: execToWB.enq(WbREG{r:s.rt, data:rf.rd1(s.rs)|zeroExtend(s.im)                            });
            tagged XOR   .s: execToWB.enq(WbREG{r:s.rd, data:rf.rd1(s.rs)^rf.rd2(s.rt)                                });
            tagged XORI  .s: execToWB.enq(WbREG{r:s.rt, data:rf.rd1(s.rs)^zeroExtend(s.im)                            });
            tagged SLT   .s: execToWB.enq(WbREG{r:s.rd, data:zeroExtend(pack(signedLT(rf.rd1(s.rs),rf.rd2(s.rt))))    });
            tagged SLTI  .s: execToWB.enq(WbREG{r:s.rt, data:zeroExtend(pack(signedLT(rf.rd1(s.rs),signExtend(s.im))))});
            tagged SLTIU .s: execToWB.enq(WbREG{r:s.rt, data:zeroExtend(pack(         rf.rd1(s.rs)<signExtend(s.im)) )});
            tagged SLTU  .s: execToWB.enq(WbREG{r:s.rt, data:zeroExtend(pack(         rf.rd1(s.rs)<rf.rd2(s.rt)))     });
            tagged DIV   .s:
                action
                    dividerPending <= True;
                    divider.request.put(tuple3(rf.rd1(s.rs),rf.rd2(s.rt),True));
                endaction
            tagged DIVU  .s:
                action
                    dividerPending <= True;
                    divider.request.put(tuple3(rf.rd1(s.rs),rf.rd2(s.rt),False));
                endaction
            tagged MULT  .s:
                action
                    Int#(64) r1 = unpack(signExtend(rf.rd1(s.rs)));
                    Int#(64) r2 = unpack(signExtend(rf.rd2(s.rt)));
                    Bit#(64) mult = pack(r1*r2);
                    hi <= mult[63:32];
                    lo <= mult[31: 0];
                endaction
            tagged MULTU .s:
                action
                    UInt#(64) r1 = unpack(zeroExtend(rf.rd1(s.rs)));
                    UInt#(64) r2 = unpack(zeroExtend(rf.rd2(s.rt)));
                    Bit#(64) mult = pack(r1*r2);
                    hi <= mult[63:32];
                    lo <= mult[31: 0];
                endaction
            tagged MFHI  .s: execToWB.enq(WbREG{r:s.rd, data:hi});
            tagged MFLO  .s: execToWB.enq(WbREG{r:s.rd, data:lo});
            tagged BEQ   .s: if(rf.rd1(s.rs)==rf.rd2(s.rt)) jumpTo.enq(pc1 + signExtend(s.of));
            tagged BGEZ  .s: if(signedGE(rf.rd1(s.rs),0))   jumpTo.enq(pc1 + signExtend(s.of));
            tagged BGTZ  .s: if(signedGT(rf.rd1(s.rs),0))   jumpTo.enq(pc1 + signExtend(s.of));
            tagged BLEZ  .s: if(signedLE(rf.rd1(s.rs),0))   jumpTo.enq(pc1 + signExtend(s.of));
            tagged BLTZ  .s: if(signedLT(rf.rd1(s.rs),0))   jumpTo.enq(pc1 + signExtend(s.of));
            tagged BNE   .s: if(rf.rd1(s.rs)!=rf.rd2(s.rt)) jumpTo.enq(pc1 + signExtend(s.of));
            tagged J     .s: jumpTo.enq(truncate(s.tg));
            tagged JAL   .s:
                action
                    execToWB.enq(WbREG{r:31, data:zeroExtend(pc+2)<<2});
                    jumpTo.enq(truncate(s.tg));
                endaction
            tagged JALR  .s:
                action
                    execToWB.enq(WbREG{r:s.rd, data:zeroExtend(pc+2)<<2});
                    jumpTo.enq(truncate(rf.rd1(s.rs)>>2));
                endaction
            tagged JR    .s: jumpTo.enq(truncate(rf.rd1(s.rs)>>2));
            tagged LB    .s:
                action
                    Bit#(32) addr = rf.rd1(s.rb)+signExtend(s.of);
                    execToWB.enq(WbLB{r:s.rt, addr:truncate(addr>>2), line:addr[1:0]});
                endaction
            tagged LBU   .s:
                action
                    Bit#(32) addr = rf.rd1(s.rb)+signExtend(s.of);
                    execToWB.enq(WbLBU{r:s.rt, addr:truncate(addr>>2), line:addr[1:0]});
                endaction
            tagged LH    .s:
                action
                    Bit#(32) addr = rf.rd1(s.rb)+signExtend(s.of);
                    execToWB.enq(WbLH{r:s.rt, addr:truncate(addr>>2), line:addr[1]});
                endaction
            tagged LHU   .s:
                action
                    Bit#(32) addr = rf.rd1(s.rb)+signExtend(s.of);
                    execToWB.enq(WbLHU{r:s.rt, addr:truncate(addr>>2), line:addr[1]});
                endaction
            tagged LW    .s:
                action
                    Bit#(32) addr = rf.rd1(s.rb)+signExtend(s.of);
                    execToWB.enq(WbLW{r:s.rt, addr:truncate(addr>>2)});
                endaction
            tagged SB    .s:
                action
                    Bit#(32) addr = rf.rd1(s.rb)+signExtend(s.of);
                    execToWB.enq(WbSB{data:truncate(rf.rd2(s.rt)), addr:truncate(addr>>2), line:addr[1:0]});
                endaction
            tagged SH    .s:
                action
                    Bit#(32) addr = rf.rd1(s.rb)+signExtend(s.of);
                    execToWB.enq(WbSH{data:truncate(rf.rd2(s.rt)), addr:truncate(addr>>2), line:addr[1]});
                endaction
            tagged SW    .s:
                action
                    Bit#(32) addr = rf.rd1(s.rb)+signExtend(s.of);
                    execToWB.enq(WbSW{data:rf.rd2(s.rt), addr:truncate(addr>>2)});
                endaction
            tagged SYSC  .s:
                case (s.sc)
                    20'h0000: $finish();
                    20'h0001: $display("[syscall 1] %h", rf.rd1(4));
                    20'h0002: clearCache <= True;
                    20'h0003: irqEnabled <= False;
                    20'h0004: execToWB.enq(WbREG{r:4,  data: irqMask});
                    20'h0005: execToWB.enq(WbREG{r:31, data: zeroExtend(irqReturnAddr)<<2});
                    20'h0004: irqEnabled <= True;
                endcase
            tagged ILLEGAL : $display("Exec error: Invalid instruction %h at pc=%h", pack(inst), pc);
        endcase
    endrule

    rule wbLoadResult(dataFIFO.notEmpty && !clearCache);
        let wbOp = pendingLoad.first;
        let data = dataFIFO.first;
        trace($format("[wbLoadResult] [data=%h] ",data)+fshow(wbOp));
        function Bit#(8) byteLine(Bit#(32) d, Bit#(2) line);
            return (case(line)
                2'h0: d[31:24];
                2'h1: d[23:16];
                2'h2: d[15: 8];
                2'h3: d[ 7: 0];
            endcase);
        endfunction
        function Bit#(16) halfLine(Bit#(32) d, Bit#(1) line);
            return (case(line)
                1'h0: d[31:16];
                1'h1: d[15: 0];
            endcase);
        endfunction
        function Bit#(32) byteSub(Bit#(32) d, Bit#(2) line, Bit#(8) newd);
            case(line)
                2'h0: d[31:24] = newd;
                2'h1: d[23:16] = newd;
                2'h2: d[15: 8] = newd;
                2'h3: d[ 7: 0] = newd;
            endcase
            return d;
        endfunction
        function Bit#(32) halfSub(Bit#(32) d, Bit#(1) line, Bit#(16) newd);
            case(line)
                1'h0: d[31:16] = newd;
                1'h1: d[15: 0] = newd;
            endcase
            return d;
        endfunction
        case (wbOp) matches
            tagged WbREG .s: $display("WB error: wbREG shouldn't cause a pending load");
            tagged WbLB  .s: rf.wr(s.r, signExtend(byteLine(data, s.line)));
            tagged WbLBU .s: rf.wr(s.r, zeroExtend(byteLine(data, s.line)));
            tagged WbLH  .s: rf.wr(s.r, signExtend(halfLine(data, s.line)));
            tagged WbLHU .s: rf.wr(s.r, zeroExtend(halfLine(data, s.line)));
            tagged WbLW  .s: rf.wr(s.r, data);
            tagged WbSB  .s: cache.dataCache.request.put(AvalonRequest{command: Write, addr: truncate(s.addr),
                                                                       data: byteSub(data, s.line, s.data)});
            tagged WbSH  .s: cache.dataCache.request.put(AvalonRequest{command: Write, addr: truncate(s.addr),
                                                                       data: halfSub(data, s.line, s.data)});
            tagged WbSW  .s: $display("WB error: wbSW shouldn't cause a pending load");
        endcase
        dataFIFO.deq;
        pendingLoad.deq;
    endrule

    rule wbFromExec(!dataFIFO.notEmpty && !clearCache);
        let wbOp = execToWB.first;
        trace($format("[wbFromExec] ")+fshow(wbOp));
        function Action reqLoad(Bit#(24) addr) = action
            cache.dataCache.request.put(AvalonRequest{command:Read, addr:truncate(addr), data:?});
            pendingLoad.enq(wbOp);
        endaction;
        case (wbOp) matches
            tagged WbREG .s: rf.wr(s.r, s.data);
            tagged WbLB  .s: reqLoad(s.addr);
            tagged WbLBU .s: reqLoad(s.addr);
            tagged WbLH  .s: reqLoad(s.addr);
            tagged WbLHU .s: reqLoad(s.addr);
            tagged WbLW  .s: reqLoad(s.addr);
            tagged WbSB  .s: reqLoad(s.addr);
            tagged WbSH  .s: reqLoad(s.addr);
            tagged WbSW  .s:
                cache.dataCache.request.put(AvalonRequest{command:Write, addr:truncate(s.addr), data:s.data});
        endcase
        execToWB.deq;
    endrule

    return masterAdapter.masterWires;
endmodule