Integrated the new Divider into the Processor.

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) 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 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);
        fetchPC <= fetchPC + 1;
    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);
        fetchPC <= jumpTo.first;
        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] $a0=%h", rf.rd1(4));
                    20'h00ff: clearCache <= 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

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::*;

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);

    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 clearTheCaches(clearCache);
        clearCache <= False;
        cache.clear;
    endrule

    rule fetchAhead(!jumpTo.notEmpty && !clearCache);
        trace($format("[Fetch %h]", fetchPC));
        cache.instCache.request.put(AvalonRequest{command: Read, addr: fetchPC, data: ?});
        execPC.enq(fetchPC);
        fetchPC <= fetchPC + 1;
    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);
        fetchPC <= jumpTo.first;
        jumpTo.deq;
    endrule

    rule exec(!pendingLoad.notEmpty && !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
                    Int#(32) r1 = unpack(rf.rd1(s.rs));
                    Int#(32) r2 = unpack(rf.rd2(s.rt));
                    r2 = r2 == 0 ? 1 : r2;  // make the simulator happy
                    lo <= pack(r1 / r2);
                    hi <= pack(r1 % r2);
                endaction
            tagged DIVU  .s:
                action
                    UInt#(32) r1 = unpack(rf.rd1(s.rs));
                    UInt#(32) r2 = unpack(rf.rd2(s.rt));
                    r2 = r2 == 0 ? 1 : r2;  // make the simulator happy
                    lo <= pack(r1 / r2);
                    hi <= pack(r1 % r2);
                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] $a0=%h", rf.rd1(4));
                    20'h00ff: clearCache <= 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


IRQs implemented (not tested yet).

IRQs implemented (not tested yet).

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

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) 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 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);
        fetchPC <= fetchPC + 1;
    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);
        fetchPC <= jumpTo.first;
        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'h00ff: clearCache <= 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


deparser parse done state

deparser parse done state

// Copyright (c) 2016 P4FPGA 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.

// Deparser Template

import BUtils::*;
import BuildVector::*;
import CBus::*;
import ClientServer::*;
import ConfigReg::*;
import Connectable::*;
import DbgDefs::*;
import DefaultValue::*;
import Ethernet::*;
import FIFO::*;
import FIFOF::*;
import FShow::*;
import GetPut::*;
import List::*;
import MIMO::*;
import MatchTable::*;
import PacketBuffer::*;
import Pipe::*;
import PrintTrace::*;
import Register::*;
import SpecialFIFOs::*;
import StmtFSM::*;
import TxRx::*;
import Utils::*;
import Vector::*;

import StructGenerated::*;

// app-specific structs
`define DEPARSER_STRUCT
`include "DeparserGenerated.bsv"
`undef DEPARSER_STRUCT

typedef TDiv#(PktDataWidth, 8) MaskWidth;
typedef TLog#(PktDataWidth) DataSize;
typedef TLog#(TDiv#(PktDataWidth, 8)) MaskSize;
typedef TAdd#(DataSize, 1) NumBits;
typedef TAdd#(MaskSize, 1) NumBytes;

interface Deparser;
   interface PipeIn#(MetadataT) metadata;
   interface PktWriteServer writeServer;
   interface PktWriteClient writeClient;
   method Action set_verbosity (int verbosity);
   method DeparserPerfRec read_perf_info ();
endinterface
module mkDeparser (Deparser);
   Reg#(int) cf_verbosity <- mkConfigRegU;
   function Action dbprint(Integer level, Fmt msg);
      action
      if (cf_verbosity > fromInteger(level)) begin
         $display("(%0d) ", $time, msg);
      end
      endaction
   endfunction

   FIFOF#(EtherData) data_in_ff <- printTimedTraceM("deparse_in_ff", mkFIFOF);
   FIFOF#(EtherData) data_out_ff <- printTimedTraceM("deparse_out_ff", mkFIFOF);
   FIFOF#(MetadataT) meta_in_ff <- printTimedTraceM("deparse_meta_in_ff", mkSizedFIFOF(16));
   FIFOF#(Maybe#(Bit#(128))) data_ff <- mkDFIFOF(tagged Invalid);
   FIFO#(DeparserState) deparse_state_ff <- printTimedTraceM("deparseState", mkPipelineFIFO());
   Array#(Reg#(Bit#(32))) rg_next_header_len <- mkCReg(3, 0);
   Array#(Reg#(Bit#(32))) rg_buffered <- mkCReg(3, 0); // number of bytes buffered in rg_tmp
   Array#(Reg#(Bit#(32))) rg_processed <- mkCReg(3, 0); // number of bytes in current header that have been sent.
   Array#(Reg#(Bit#(32))) rg_shift_amt <- mkCReg(3, 0); // number of bytes to shift to append new bytes to rg_tmp
   Array#(Reg#(Bit#(512))) rg_tmp <- mkCReg(2, 0);
   Array#(Reg#(Bool)) deparse_done <- mkCReg(2, True);
   Array#(Reg#(Bool)) header_done <- mkCReg(2, True);
   Array#(Reg#(MetadataT)) meta <- mkCReg(2, defaultValue);
   PulseWire w_deparse_header_done <- mkPulseWire();

   let mask_this_cycle = data_in_ff.first.mask;
   let sop_this_cycle = data_in_ff.first.sop;
   let eop_this_cycle = data_in_ff.first.eop;
   let data_this_cycle = data_in_ff.first.data;
   function Action report_deparse_action(String msg, Bit#(32) buffered, Bit#(32) processed, Bit#(32) shift, Bit#(512) data);
    action
      if (cf_verbosity > 0) begin
        $display("(%0d) Deparse %s buffered %d %d %d data %h", $time, msg, buffered, processed, shift, data);
      end
    endaction
  endfunction
  function Action fetch_next_header(Bit#(32) len);
    action
      rg_next_header_len[0] <= len;
    endaction
  endfunction
  function Action move_buffered_amt(Bit#(32) len);
    action
      rg_buffered[0] <= rg_buffered[0] + len;
      rg_shift_amt[0] <= rg_shift_amt[0] + len;
    endaction
  endfunction
  function Action succeed_and_next(Bit#(32) len);
    action
      rg_processed[0] <= rg_processed[0] + len;
      rg_buffered[0] <= rg_buffered[0] - len;
    endaction
  endfunction
  function Bit#(max) create_mask(LUInt#(max) count);
    Bit#(max) v = (1 << count) - 1;
    return v;
  endfunction

  // app-specific states
  `define DEPARSER_STATE
  `include "DeparserGenerated.bsv"
  `undef DEPARSER_STATE

  rule rl_start_state if (deparse_done[1] && sop_this_cycle);
    rg_tmp[1] <= 0;
    rg_buffered[2] <= 0;
    rg_shift_amt[2] <= 0;
    rg_processed[2] <= 0;
    deparse_done[1] <= False;
    header_done[1] <= False;
    let metadata = meta_in_ff.first;
    meta[1] <= metadata;
    meta_in_ff.deq;
    deparse_state_ff.enq(StateDeparseEthernet);
    fetch_next_header(112);
    dbprint(3, $format("start deparse %d", valueOf(SizeOf#(DeparserState))));
  endrule

  rule rl_deparse_payload if (deparse_done[1] && !sop_this_cycle);
    let v = data_in_ff.first;
    dbprint(3, $format("Dep payload", fshow(v)));
    data_in_ff.deq;
    dbprint(3, $format("Dep payload shift amt %d", rg_shift_amt[1]));
    if (rg_shift_amt[1] != 0) begin
      Bit#(128) data_mask = create_mask(cExtend(rg_shift_amt[1]));
      Bit#(16) mask_out = create_mask(cExtend(rg_shift_amt[1] >> 3));
      let data = EtherData { sop: v.sop, eop: v.eop, data: truncate(rg_tmp[1]) & data_mask, mask: mask_out};
      data_out_ff.enq(data);
      dbprint(3, $format("Deparser payload2 rg_shift_amt=%d", rg_shift_amt[1], fshow(data)));
      rg_shift_amt[1] <= 0;
    end
    else begin
      dbprint(3, $format("Deparser payload1 ", fshow(v)));
      data_out_ff.enq(v);
    end
  endrule

  rule rl_data_ff_load if (!deparse_done[1] && (rg_buffered[2] < rg_next_header_len[2]));
    dbprint(3, $format("dequeue data_in_ff %d < %d", rg_buffered[2], rg_next_header_len[2]));
    data_in_ff.deq;
  endrule

  rule rl_deparse_send if (!deparse_done[1] && (rg_processed[1] > 0));
    let amt = 128;
    if (rg_processed[1] < 128) begin
       amt = rg_processed[1];
    end
    let v = data_in_ff.first;
    dbprint(3, $format("Deparser ", fshow(v)));
    Bit#(128) data_out = truncate(rg_tmp[1] & create_mask(cExtend(amt)));
    Bit#(16) mask_out = create_mask(cExtend(amt >> 3));
    let data = EtherData { sop: sop_this_cycle, eop: False, data: data_out, mask: mask_out };
    rg_tmp[1] <= rg_tmp[1] >> amt;
    rg_processed[1] <= rg_processed[1] - amt;
    rg_shift_amt[1] <= rg_shift_amt[1] - amt;
    data_out_ff.enq(data);
    dbprint(3, $format("Deparser rg_processed=%d rg_shift_amt=%d amt=%d", rg_processed[1], rg_shift_amt[1], amt, fshow(data)));
  endrule

  rule rl_deparse_header_done if (w_deparse_header_done);
    fetch_next_header(0);
    header_done[0] <= True;
  endrule

  // wait till all processed bits are sent, cont. to send payload.
  // some data are buffered not processed.
  rule rl_wait_till_processed_done if (header_done[1] && (rg_processed[1] == 0));
    deparse_done[1] <= True;
  endrule

  `define DEPARSER_RULES
  `include "DeparserGenerated.bsv"
  `undef DEPARSER_RULES

  interface metadata = toPipeIn(meta_in_ff);
  interface PktWriteServer writeServer;
    interface writeData = toPut(data_in_ff);
  endinterface
  interface PktWriteClient writeClient;
    interface writeData = toGet(data_out_ff);
  endinterface
  method Action set_verbosity (int verbosity);
    cf_verbosity <= verbosity;
  endmethod
endmodule

// Copyright (c) 2016 P4FPGA 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.

// Deparser Template

import BUtils::*;
import BuildVector::*;
import CBus::*;
import ClientServer::*;
import ConfigReg::*;
import Connectable::*;
import DbgDefs::*;
import DefaultValue::*;
import Ethernet::*;
import FIFO::*;
import FIFOF::*;
import FShow::*;
import GetPut::*;
import List::*;
import MIMO::*;
import MatchTable::*;
import PacketBuffer::*;
import Pipe::*;
import PrintTrace::*;
import Register::*;
import SpecialFIFOs::*;
import StmtFSM::*;
import TxRx::*;
import Utils::*;
import Vector::*;

import StructGenerated::*;

// app-specific structs
`define DEPARSER_STRUCT
`include "DeparserGenerated.bsv"
`undef DEPARSER_STRUCT

typedef TDiv#(PktDataWidth, 8) MaskWidth;
typedef TLog#(PktDataWidth) DataSize;
typedef TLog#(TDiv#(PktDataWidth, 8)) MaskSize;
typedef TAdd#(DataSize, 1) NumBits;
typedef TAdd#(MaskSize, 1) NumBytes;

interface Deparser;
   interface PipeIn#(MetadataT) metadata;
   interface PktWriteServer writeServer;
   interface PktWriteClient writeClient;
   method Action set_verbosity (int verbosity);
   method DeparserPerfRec read_perf_info ();
endinterface
module mkDeparser (Deparser);
   Reg#(int) cf_verbosity <- mkConfigRegU;
   function Action dbprint(Integer level, Fmt msg);
      action
      if (cf_verbosity > fromInteger(level)) begin
         $display("(%0d) ", $time, msg);
      end
      endaction
   endfunction

   FIFOF#(EtherData) data_in_ff <- printTimedTraceM("deparse_in_ff", mkFIFOF);
   FIFOF#(EtherData) data_out_ff <- printTimedTraceM("deparse_out_ff", mkFIFOF);
   FIFOF#(MetadataT) meta_in_ff <- printTimedTraceM("deparse_meta_in_ff", mkSizedFIFOF(16));
   FIFOF#(Maybe#(Bit#(128))) data_ff <- mkDFIFOF(tagged Invalid);
   FIFO#(DeparserState) deparse_state_ff <- printTimedTraceM("deparseState", mkPipelineFIFO());
   Array#(Reg#(Bit#(32))) rg_next_header_len <- mkCReg(3, 0);
   Array#(Reg#(Bit#(32))) rg_buffered <- mkCReg(3, 0); // number of bytes buffered in rg_tmp
   Array#(Reg#(Bit#(32))) rg_processed <- mkCReg(3, 0); // number of bytes in current header that have been sent.
   Array#(Reg#(Bit#(32))) rg_shift_amt <- mkCReg(3, 0); // number of bytes to shift to append new bytes to rg_tmp
   Array#(Reg#(Bit#(512))) rg_tmp <- mkCReg(2, 0);
   Array#(Reg#(Bool)) deparse_done <- mkCReg(2, True);
   Array#(Reg#(Bool)) header_done <- mkCReg(2, True);
   Array#(Reg#(MetadataT)) meta <- mkCReg(2, defaultValue);
   PulseWire w_deparse_header_done <- mkPulseWire();

   let mask_this_cycle = data_in_ff.first.mask;
   let sop_this_cycle = data_in_ff.first.sop;
   let eop_this_cycle = data_in_ff.first.eop;
   let data_this_cycle = data_in_ff.first.data;
   function Action report_deparse_action(String msg, Bit#(32) buffered, Bit#(32) processed, Bit#(32) shift, Bit#(512) data);
    action
      if (cf_verbosity > 0) begin
        $display("(%0d) Deparse %s buffered %d %d %d data %h", $time, msg, buffered, processed, shift, data);
      end
    endaction
  endfunction
  function Action fetch_next_header(Bit#(32) len);
    action
      rg_next_header_len[0] <= len;
    endaction
  endfunction
  function Action move_buffered_amt(Bit#(32) len);
    action
      rg_buffered[0] <= rg_buffered[0] + len;
      rg_shift_amt[0] <= rg_shift_amt[0] + len;
    endaction
  endfunction
  function Action succeed_and_next(Bit#(32) len);
    action
      rg_processed[0] <= rg_processed[0] + len;
      rg_buffered[0] <= rg_buffered[0] - len;
    endaction
  endfunction
  function Bit#(max) create_mask(LUInt#(max) count);
    Bit#(max) v = (1 << count) - 1;
    return v;
  endfunction

  // app-specific states
  `define DEPARSER_STATE
  `include "DeparserGenerated.bsv"
  `undef DEPARSER_STATE

  rule rl_start_state if (deparse_done[1] && sop_this_cycle);
    rg_tmp[1] <= 0;
    rg_buffered[2] <= 0;
    rg_shift_amt[2] <= 0;
    rg_processed[2] <= 0;
    deparse_done[1] <= False;
    header_done[1] <= False;
    let metadata = meta_in_ff.first;
    meta[1] <= metadata;
    meta_in_ff.deq;
    deparse_state_ff.enq(StateDeparseEthernet);
    fetch_next_header(112);
    dbprint(3, $format("start deparse %d", valueOf(SizeOf#(DeparserState))));
  endrule

  rule rl_deparse_payload if (deparse_done[1] && !sop_this_cycle);
    let v = data_in_ff.first;
    dbprint(3, $format("Dep payload", fshow(v)));
    data_in_ff.deq;
    dbprint(3, $format("Dep payload shift amt %d", rg_shift_amt[1]));
    if (rg_shift_amt[1] != 0) begin
      Bit#(128) data_mask = create_mask(cExtend(rg_shift_amt[1]));
      Bit#(16) mask_out = create_mask(cExtend(rg_shift_amt[1] >> 3));
      let data = EtherData { sop: v.sop, eop: v.eop, data: truncate(rg_tmp[1]) & data_mask, mask: mask_out};
      data_out_ff.enq(data);
      dbprint(3, $format("Deparser payload2 rg_shift_amt=%d", rg_shift_amt[1], fshow(data)));
      rg_shift_amt[1] <= 0;
    end
    else begin
      dbprint(3, $format("Deparser payload1 ", fshow(v)));
      data_out_ff.enq(v);
    end
  endrule

  rule rl_data_ff_load if (!deparse_done[1] && (rg_buffered[2] < rg_next_header_len[2]));
    dbprint(3, $format("dequeue data_in_ff %d < %d", rg_buffered[2], rg_next_header_len[2]));
    data_in_ff.deq;
  endrule

  rule rl_deparse_send if (!deparse_done[1] && (rg_processed[1] > 0));
    let amt = 128;
    if (rg_processed[1] < 128) begin
       amt = rg_processed[1];
    end
    let v = data_in_ff.first;
    dbprint(3, $format("Deparser ", fshow(v)));
    Bit#(128) data_out = truncate(rg_tmp[1] & create_mask(cExtend(amt)));
    Bit#(16) mask_out = create_mask(cExtend(amt >> 3));
    let data = EtherData { sop: sop_this_cycle, eop: False, data: data_out, mask: mask_out };
    rg_tmp[1] <= rg_tmp[1] >> amt;
    rg_processed[1] <= rg_processed[1] - amt;
    rg_shift_amt[1] <= rg_shift_amt[1] - amt;
    data_out_ff.enq(data);
    dbprint(3, $format("Deparser rg_processed=%d rg_shift_amt=%d amt=%d", rg_processed[1], rg_shift_amt[1], amt, fshow(data)));
  endrule

  // wait till all processed bits are sent, cont. to send payload.
  // some data are buffered not processed.
  rule rl_wait_till_processed_done if (header_done[1] && (rg_processed[1] == 0));
    deparse_done[1] <= True;
  endrule

  `define DEPARSER_RULES
  `include "DeparserGenerated.bsv"
  `undef DEPARSER_RULES

  interface metadata = toPipeIn(meta_in_ff);
  interface PktWriteServer writeServer;
    interface writeData = toPut(data_in_ff);
  endinterface
  interface PktWriteClient writeClient;
    interface writeData = toGet(data_out_ff);
  endinterface
  method Action set_verbosity (int verbosity);
    cf_verbosity <= verbosity;
  endmethod
endmodule


not sure this was necessary, but introduced a pipeline stage to help meet timing

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

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

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

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Clocks::*;
import Vector::*;
import BuildVector::*;
import GetPut::*;
import Connectable::*;
import ClientServer::*;
import ConnectalMemory::*;
import FIFOF::*;
import Gearbox::*;
import GearboxGetPut::*;
import MemTypes::*;
import MemReadEngine::*;
import MemWriteEngine::*;
import Pipe::*;
import AxiDdr3Controller::*;
import GetPutWithClocks::*;
import AxiMasterSlave::*;
import Axi4MasterSlave::*;
import AxiDdr3Wrapper  ::*;
import AxiDma::*;
import ConnectalConfig::*;
import HostInterface::*;

interface Ddr3TestRequest;
   method Action startWriteDram(Bit#(32) sglId);
   method Action startReadDram(Bit#(32) sglId);
endinterface

interface Ddr3TestIndication;
   method Action writeDone(Bit#(32) v);
   method Action readDone(Bit#(32) v);
endinterface

interface Ddr3Test;
   interface Ddr3TestRequest request;
   interface Vector#(1, MemReadClient#(DataBusWidth)) readClient;
   interface Vector#(1, MemWriteClient#(DataBusWidth)) writeClient;
   interface Ddr3Pins ddr3;
endinterface

typedef TDiv#(Ddr3DataWidth,DataBusWidth) BusRatio;

module mkDdr3Test#(HostInterface host, Ddr3TestIndication indication)(Ddr3Test);

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

   Clock clk200 = host.tsys_clk_200mhz_buf;

   let ddr3Controller <- mkDdr3(clk200);
   MemReadEngine#(DataBusWidth,DataBusWidth,1,1)  re <- mkMemReadEngine();
   MemWriteEngine#(DataBusWidth,DataBusWidth,1,1)  we <- mkMemWriteEngine();

   FIFOF#(Bit#(32))   writeReqFifo <- mkFIFOF();
   FIFOF#(Bit#(32))   readReqFifo <- mkFIFOF();

   Gearbox#(1,BusRatio,Bit#(DataBusWidth)) dramWriteGearbox <- mk1toNGearbox(clock, reset, ddr3Controller.uiClock, ddr3Controller.uiReset);
   SyncFIFOIfc#(Axi4WriteRequest#(Ddr3AddrWidth,6)) awfifo <- mkSyncFIFO(8, clock, reset, ddr3Controller.uiClock);
   SyncFIFOIfc#(Axi4WriteResponse#(6)) bfifo <- mkSyncFIFO(8, ddr3Controller.uiClock, ddr3Controller.uiReset, clock);
   mkConnection(toGet(awfifo), ddr3Controller.slave.req_aw);
   mkConnection(ddr3Controller.slave.resp_b, toPut(bfifo));

   Reg#(Bit#(Ddr3AddrWidth)) dramWriteOffset <- mkReg(0);
   Reg#(Bit#(Ddr3AddrWidth)) dramWriteLimit <- mkReg(0);
   rule rl_req_aw if (dramWriteOffset < dramWriteLimit);
      awfifo.enq(Axi4WriteRequest {
	 address: truncate(dramWriteOffset),
	 len: 1,
	 size: axiBusSize(valueOf(Ddr3DataWidth)),
	 id: 0,
	 burst: 2'b01,
	 prot: 3'b000,   //ignored
	 cache: 4'b0011, //ignored
	 lock: 2'b00,    //ignored
	 qos: 4'b0000    //ignored
	 });
      dramWriteOffset <= dramWriteOffset + 1;
   endrule

   rule rl_wdata_gb;
      let rdata <- toGet(re.readServers[0].data).get();
      dramWriteGearbox.enq(vec(rdata.data));
   endrule
   rule rl_wdata;
      let mds <- toGet(dramWriteGearbox).get();
      ddr3Controller.slave.resp_write.put(Axi4WriteData {
	 data: pack(mds),
	 byteEnable: maxBound,
	 last: 1,
	 id: 0
	 });
   endrule

   rule rl_b;
      let b <- toGet(bfifo).get();
      // let's see an indication, but we should be counting how many words were sent
      indication.writeDone(extend(b.id));
   endrule

   rule rl_write_start;
      let sglId <- toGet(writeReqFifo).get();
      dramWriteOffset <= 0;
      dramWriteLimit <= 1024/fromInteger(valueOf(Ddr3DataWidth));
      re.readServers[0].request.put(MemengineCmd { sglId: sglId,
						  base: 0,
						  burstLen: fromInteger(valueOf(TDiv#(Ddr3DataWidth,8))),
						  len: 1024,
						  tag: 0
						  });
   endrule

   Gearbox#(BusRatio,1,Bit#(DataBusWidth)) dramReadGearbox <- mkNto1Gearbox(ddr3Controller.uiClock, ddr3Controller.uiReset, clock, reset);
   SyncFIFOIfc#(Axi4ReadRequest#(Ddr3AddrWidth,6)) arfifo <- mkSyncFIFO(8, clock, reset, ddr3Controller.uiClock);
   mkConnection(toGet(arfifo), ddr3Controller.slave.req_ar);

   Reg#(Bit#(Ddr3AddrWidth)) dramReadOffset <- mkReg(0);
   Reg#(Bit#(Ddr3AddrWidth)) dramReadLimit <- mkReg(0);
   rule rl_req_ar if (dramReadOffset < dramReadLimit);
      arfifo.enq(Axi4ReadRequest {
	 address: truncate(dramReadOffset),
	 len: 1,
	 size: axiBusSize(valueOf(Ddr3DataWidth)),
	 id: 0,
	 burst: 2'b01,
	 prot: 3'b000,   //ignored
	 cache: 4'b0011, //ignored
	 lock: 2'b00,    //ignored
	 qos: 4'b0000    //ignored
	 });
      dramReadOffset <= dramReadOffset + 1;
   endrule

   rule rl_rdata;
      let resp <- ddr3Controller.slave.resp_read.get();
      Vector#(BusRatio, Bit#(DataBusWidth)) data = unpack(resp.data);
      dramReadGearbox.enq(data);
   endrule
   FIFOF#(Bit#(DataBusWidth)) slackFifo <- mkFIFOF();
   rule rl_rdata_gb;
      Bit#(DataBusWidth) rdata <- toGet(dramReadGearbox).get();
      slackFifo.enq(rdata);
   endrule
   rule rl_rdata_slack;
      let rdata <- toGet(slackFifo).get();
      //fixme last field
      we.writeServers[0].data.enq(rdata);
   endrule

   rule rl_read_start;
      let sglId <- toGet(readReqFifo).get();
      dramReadOffset <= 0;
      dramReadLimit <= 1024/fromInteger(valueOf(Ddr3DataWidth));
      we.writeServers[0].request.put(MemengineCmd { sglId: sglId,
						   base: 0,
						   burstLen: fromInteger(valueOf(TDiv#(Ddr3DataWidth,8))),
						   len: 1024,
						   tag: 0
						   });
   endrule

   interface Ddr3TestRequest request;
      method Action startWriteDram(Bit#(32) sglId);
	 writeReqFifo.enq(sglId);
      endmethod
      method Action startReadDram(Bit#(32) sglId);
	 readReqFifo.enq(sglId);
      endmethod
   endinterface
   interface MemReadClient readClient = vec(re.dmaClient);
   interface MemWriteClient writeClient = vec(we.dmaClient);
   interface AxiDdr3 ddr3 = ddr3Controller.ddr3;
endmodule

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

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

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

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
import Clocks::*;
import Vector::*;
import BuildVector::*;
import GetPut::*;
import Connectable::*;
import ClientServer::*;
import ConnectalMemory::*;
import FIFOF::*;
import Gearbox::*;
import GearboxGetPut::*;
import MemTypes::*;
import MemReadEngine::*;
import MemWriteEngine::*;
import Pipe::*;
import AxiDdr3Controller::*;
import GetPutWithClocks::*;
import AxiMasterSlave::*;
import Axi4MasterSlave::*;
import AxiDdr3Wrapper  ::*;
import AxiDma::*;
import ConnectalConfig::*;
import HostInterface::*;

interface Ddr3TestRequest;
   method Action startWriteDram(Bit#(32) sglId);
   method Action startReadDram(Bit#(32) sglId);
endinterface

interface Ddr3TestIndication;
   method Action writeDone(Bit#(32) v);
   method Action readDone(Bit#(32) v);
endinterface

interface Ddr3Test;
   interface Ddr3TestRequest request;
   interface Vector#(1, MemReadClient#(DataBusWidth)) readClient;
   interface Vector#(1, MemWriteClient#(DataBusWidth)) writeClient;
   interface Ddr3Pins ddr3;
endinterface

typedef TDiv#(Ddr3DataWidth,DataBusWidth) BusRatio;

module mkDdr3Test#(HostInterface host, Ddr3TestIndication indication)(Ddr3Test);

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

   Clock clk200 = host.tsys_clk_200mhz_buf;

   let ddr3Controller <- mkDdr3(clk200);
   MemReadEngine#(DataBusWidth,DataBusWidth,1,1)  re <- mkMemReadEngine();
   MemWriteEngine#(DataBusWidth,DataBusWidth,1,1)  we <- mkMemWriteEngine();

   FIFOF#(Bit#(32))   writeReqFifo <- mkFIFOF();
   FIFOF#(Bit#(32))   readReqFifo <- mkFIFOF();

   Gearbox#(1,BusRatio,Bit#(DataBusWidth)) dramWriteGearbox <- mk1toNGearbox(clock, reset, ddr3Controller.uiClock, ddr3Controller.uiReset);
   SyncFIFOIfc#(Axi4WriteRequest#(Ddr3AddrWidth,6)) awfifo <- mkSyncFIFO(8, clock, reset, ddr3Controller.uiClock);
   SyncFIFOIfc#(Axi4WriteResponse#(6)) bfifo <- mkSyncFIFO(8, ddr3Controller.uiClock, ddr3Controller.uiReset, clock);
   mkConnection(toGet(awfifo), ddr3Controller.slave.req_aw);
   mkConnection(ddr3Controller.slave.resp_b, toPut(bfifo));

   Reg#(Bit#(Ddr3AddrWidth)) dramWriteOffset <- mkReg(0);
   Reg#(Bit#(Ddr3AddrWidth)) dramWriteLimit <- mkReg(0);
   rule rl_req_aw if (dramWriteOffset < dramWriteLimit);
      awfifo.enq(Axi4WriteRequest {
	 address: truncate(dramWriteOffset),
	 len: 1,
	 size: axiBusSize(valueOf(Ddr3DataWidth)),
	 id: 0,
	 burst: 2'b01,
	 prot: 3'b000,   //ignored
	 cache: 4'b0011, //ignored
	 lock: 2'b00,    //ignored
	 qos: 4'b0000    //ignored
	 });
      dramWriteOffset <= dramWriteOffset + 1;
   endrule

   rule rl_wdata_gb;
      let rdata <- toGet(re.readServers[0].data).get();
      dramWriteGearbox.enq(vec(rdata.data));
   endrule
   rule rl_wdata;
      let mds <- toGet(dramWriteGearbox).get();
      ddr3Controller.slave.resp_write.put(Axi4WriteData {
	 data: pack(mds),
	 byteEnable: maxBound,
	 last: 1,
	 id: 0
	 });
   endrule

   rule rl_b;
      let b <- toGet(bfifo).get();
      // let's see an indication, but we should be counting how many words were sent
      indication.writeDone(extend(b.id));
   endrule

   rule rl_write_start;
      let sglId <- toGet(writeReqFifo).get();
      dramWriteOffset <= 0;
      dramWriteLimit <= 1024/fromInteger(valueOf(Ddr3DataWidth));
      re.readServers[0].request.put(MemengineCmd { sglId: sglId,
						  base: 0,
						  burstLen: fromInteger(valueOf(TDiv#(Ddr3DataWidth,8))),
						  len: 1024,
						  tag: 0
						  });
   endrule

   Gearbox#(BusRatio,1,Bit#(DataBusWidth)) dramReadGearbox <- mkNto1Gearbox(ddr3Controller.uiClock, ddr3Controller.uiReset, clock, reset);
   SyncFIFOIfc#(Axi4ReadRequest#(Ddr3AddrWidth,6)) arfifo <- mkSyncFIFO(8, clock, reset, ddr3Controller.uiClock);
   mkConnection(toGet(arfifo), ddr3Controller.slave.req_ar);

   Reg#(Bit#(Ddr3AddrWidth)) dramReadOffset <- mkReg(0);
   Reg#(Bit#(Ddr3AddrWidth)) dramReadLimit <- mkReg(0);
   rule rl_req_ar if (dramReadOffset < dramReadLimit);
      arfifo.enq(Axi4ReadRequest {
	 address: truncate(dramReadOffset),
	 len: 1,
	 size: axiBusSize(valueOf(Ddr3DataWidth)),
	 id: 0,
	 burst: 2'b01,
	 prot: 3'b000,   //ignored
	 cache: 4'b0011, //ignored
	 lock: 2'b00,    //ignored
	 qos: 4'b0000    //ignored
	 });
      dramReadOffset <= dramReadOffset + 1;
   endrule

   rule rl_rdata;
      let resp <- ddr3Controller.slave.resp_read.get();
      Vector#(BusRatio, Bit#(DataBusWidth)) data = unpack(resp.data);
      dramReadGearbox.enq(data);
   endrule
   rule rl_rdata_gb;
      Bit#(DataBusWidth) rdata <- toGet(dramReadGearbox).get();
      //fixme last
      we.writeServers[0].data.enq(rdata);
   endrule

   rule rl_read_start;
      let sglId <- toGet(readReqFifo).get();
      dramReadOffset <= 0;
      dramReadLimit <= 1024/fromInteger(valueOf(Ddr3DataWidth));
      we.writeServers[0].request.put(MemengineCmd { sglId: sglId,
						   base: 0,
						   burstLen: fromInteger(valueOf(TDiv#(Ddr3DataWidth,8))),
						   len: 1024,
						   tag: 0
						   });
   endrule

   interface Ddr3TestRequest request;
      method Action startWriteDram(Bit#(32) sglId);
	 writeReqFifo.enq(sglId);
      endmethod
      method Action startReadDram(Bit#(32) sglId);
	 readReqFifo.enq(sglId);
      endmethod
   endinterface
   interface MemReadClient readClient = vec(re.dmaClient);
   interface MemWriteClient writeClient = vec(we.dmaClient);
   interface AxiDdr3 ddr3 = ddr3Controller.ddr3;
endmodule