change some things in rename table; maybe working now

// Copyright (c) 2017 Massachusetts Institute of Technology
//
// 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.

// Alternate free list design:
// The FreeList has the same number of slots as #PhyReg - #ArchReg. When the
// ROB is empty, the FreeList is full. When the ROB is full, the FreeList is
// empty. The FreeList deq method takes the old name for the renamed register
// and writes it to the location just dequeued. On step-by-step rollback, the
// old name is taken from the FreeList and moved back to the RenamingTable.

import Vector::*;
import GetPut::*;
import List::*;
import RevertingVirtualReg::*;
import Types::*;
import ProcTypes::*;
import HasSpecBits::*;
import Ehr::*;

typedef struct {
    PhyRegs phy_regs;
} RenameResult deriving(Bits, Eq, FShow);

interface RTRename;
    // This will rename the architectural registers in r to physical registers.
    // XXX This will **ALWAYS** claim a new physical register as a speculative
    // in-flight renaming, no matter whether the dst reg is valid or not.
    // This simplifies the implementation for superscalar renaming
    method RenameResult getRename(ArchRegs r);
    method Action claimRename(ArchRegs r, SpecBits sb);
    method Bool canRename; // guard of rename
endinterface

interface RTCommit;
    // Commits the oldest instructions. This will free the previous physical
    // register used to hold the value for the architectural destination
    // register.
    method Action commit;
    method Bool canCommit; // guard of commit
endinterface

interface RegRenamingTable;
    interface Vector#(SupSize, RTRename) rename; // rename port
    interface Vector#(SupSize, RTCommit) commit; // commit port

    // This subinterface contains the methods specifying correct and incorrect
    // speculation. If the speculation is correct, the dependencies on that
    // SpecTag should be removed from all SpecBits. If the speculation is
    // incorrect, then all renamings that depended on the SpecTag should be
    // reverted.
    interface SpeculationUpdate specUpdate;
    // methods: method Action incorrectSpeculation(SpecTag tag);
    //          method Action correctSpeculation(SpecTag tag);
endinterface

// actions when we claim a new phy reg in rename
typedef struct {
    Maybe#(ArchRIndx) arch;
    PhyRIndx phy;
    SpecBits specBits;
} RenameClaim deriving(Bits, Eq, FShow);

// actions in case of wrongSpec
typedef struct {
    SpecTag specTag;
} RTWrongSpec deriving(Bits, Eq, FShow);

(* synthesize *)
module mkRegRenamingTable(RegRenamingTable) provisos (
    NumAlias#(size, TSub#(NumPhyReg, NumArchReg)),
    Alias#(indexT, Bit#(TLog#(size))),
    Alias#(vTagT, Bit#(TLog#(TMul#(2, size)))) // virtual tag: 0 -- size*2-1
);
    // ordering: commit < rename < correctSpec
    // commit < wrongSpec
    // wrongSpec C rename

    // NOTES:
    // rename do not need to see the effect of commit
    // rename is conflict with wrongSpec, so don't need to see its effect
    // The real actions of commit, wrongSpec and rename will be done in canon rule

    // rename is split into two parts in EHR port assignment:
    // - get (get the phy reg for src and dst arch regs, in rename method)
    // - claim (truly claim a free phy reg for dst arch reg, in canon rule)

    // Although the EHR ports of commit should also be split into two parts,
    // we don't need to do so, because the method of commit is directly followed by canon rule.

    // EHR ports for renaming_table
    Integer rt_get_port = 0;
    function Integer rt_commit_port(Integer i) = i;

    // EHR ports for valid
    Integer valid_get_port = 0;
    Integer valid_commit_port = 0;
    Integer valid_wrongSpec_port = 1;
    Integer valid_claim_port = 1;

    // EHR ports for spec_bits
    Integer sb_wrongSpec_port = 0;
    Integer sb_claim_port = 0;
    Integer sb_correctSpec_port = 1;

    // non-speculative renaming table at commit port
    // initially arch reg i --> phy reg i
    Vector#(NumArchReg, Ehr#(SupSize, PhyRIndx)) renaming_table <- genWithM(compose(mkEhr, fromInteger));

    // A FIFO of
    // - in-flight renaming: when valid = True i.e. within [enqP, deqP)
    // - free phy reg: when valid = False i.e. outside [enqP, deqP)
    // We store the arch reg and phy reg separately to reduce EHR ports
    // XXX A valid in-flight renaming may have arch reg being invalid
    // This happens in case we claim a phy reg while the dst arch reg is invalid
    Vector#(size, Reg#(Maybe#(ArchRIndx))) new_renamings_arch <- replicateM(mkRegU);
    function m#(Reg#(PhyRIndx)) genNewRenamingsPhy(Integer i) provisos (IsModule#(m, a__));
        return mkReg(fromInteger(i + valueOf(NumArchReg))); // free phy regs initially
    endfunction
    Vector#(size, Reg#(PhyRIndx)) new_renamings_phy <- genWithM(genNewRenamingsPhy);
    Vector#(size, Ehr#(2, Bool)) valid <- replicateM(mkEhr(False));
    Vector#(size, Ehr#(2, SpecBits)) spec_bits <- replicateM(mkEhr(0));
    Reg#(indexT) enqP <- mkReg(0); // point to claim free phy reg
    Reg#(indexT) deqP <- mkReg(0); // point to commit renaming and make phy reg free

    // wires/EHRs to record actions
    Vector#(SupSize, RWire#(RenameClaim)) claimEn <- replicateM(mkUnsafeRWire);
    Vector#(SupSize, PulseWire) commitEn <- replicateM(mkPulseWire);
    RWire#(RTWrongSpec) wrongSpecEn <- mkRWire;

    // ordering regs
    Vector#(SupSize, Reg#(Bool)) commit_SB_rename <- replicateM(mkRevertingVirtualReg(True));
    Reg#(Bool) commit_SB_wrongSpec <- mkRevertingVirtualReg(True);

    // wrong spec conflict with rename
    Vector#(SupSize, RWire#(void)) wrongSpec_rename_conflict <- replicateM(mkRWire);

    function indexT getNextIndex(indexT idx);
        return idx == fromInteger(valueof(size) - 1) ? 0 : idx + 1;
    endfunction

    function indexT incrIndex(indexT idx, SupCnt incr);
        Bit#(TLog#(TAdd#(size, 1))) newIdx = zeroExtend(idx) + zeroExtend(incr);
        if(newIdx >= fromInteger(valueof(size))) begin
            newIdx = newIdx - fromInteger(valueof(size));
        end
        return truncate(newIdx);
    endfunction

    // get the index to query renaming_table
    function Bit#(TLog#(NumArchReg)) getRTIndex(ArchRIndx arch) = pack(arch);

    // vector of index to claim free phy regs for each rename port
    Vector#(SupSize, indexT) claimIndex;
    for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
        claimIndex[i] = incrIndex(enqP, fromInteger(i));
    end

    // vector of index to commit phy regs for each commit port
    Vector#(SupSize, indexT) commitIndex;
    for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
        commitIndex[i] = incrIndex(deqP, fromInteger(i));
    end

    // similar to LSQ, get virtual tag by using enqP as pivot (enqP is changed at end of cycle)
    // valid entry i --> i < enqP ? i + size : i
    // NOTE that virtual tag only works for **valid** entry
    function vTagT getVTag(indexT i);
        return i < enqP ? zeroExtend(i) + fromInteger(valueof(size)) : zeroExtend(i);
    endfunction
    Vector#(size, vTagT) vTags = map(getVTag, genWith(fromInteger));

    // find oldest entry using virtual tag (i.e. smallest)
    function Maybe#(indexT) findOldest(Vector#(size, Bool) pred);
        function indexT getOlder(indexT a, indexT b);
            if(!pred[a]) begin
                return b;
            end
            else if(!pred[b]) begin
                return a;
            end
            else begin
                return vTags[a] < vTags[b] ? a : b;
            end
        endfunction
        Vector#(size, indexT) idxVec = genWith(fromInteger);
        indexT tag = fold(getOlder, idxVec);
        return pred[tag] ? Valid (tag) : Invalid;
    endfunction

    // find youngest entry using virtual tag (i.e. largest)
    function Maybe#(indexT) findYoungest(Vector#(size, Bool) pred);
        function indexT getOlder(indexT a, indexT b);
            if(!pred[a]) begin
                return b;
            end
            else if(!pred[b]) begin
                return a;
            end
            else begin
                return vTags[a] < vTags[b] ? b : a;
            end
        endfunction
        Vector#(size, indexT) idxVec = genWith(fromInteger);
        indexT tag = fold(getOlder, idxVec);
        return pred[tag] ? Valid (tag) : Invalid;
    endfunction

    // canonicalize deq, wrongSpec/enq
    (* fire_when_enabled, no_implicit_conditions *)
    rule canon;
        Vector#(SupSize, SupWaySel) supIdxVec = genWith(fromInteger);

        // apply commit actions
        for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
            if(commitEn[i]) begin
                indexT curDeqP = commitIndex[i]; // deqP for new renamings
                PhyRIndx commit_phy_reg = new_renamings_phy[curDeqP];
                Maybe#(ArchRIndx) commit_arch_reg = new_renamings_arch[curDeqP];
                if(commit_arch_reg matches tagged Valid .arch) begin
                    let rtIdx = getRTIndex(arch);
                    // free phy reg being overwritten in the renaming_table (arch reg is don't care)
                    PhyRIndx freed_phy_reg = renaming_table[rtIdx][rt_commit_port(i)];
                    new_renamings_phy[curDeqP] <= freed_phy_reg;
                    valid[curDeqP][valid_commit_port] <= False;
                    // update renaming_table
                    renaming_table[rtIdx][rt_commit_port(i)] <= commit_phy_reg;
                end
                else begin
                    // free the phy reg claimed by this renaming (arch reg is don't care)
                    valid[curDeqP][valid_commit_port] <= False;
                end
                // sanity check
                doAssert(valid[curDeqP][valid_commit_port], "committing entry must be valid");
            end
        end
        // move deqP: find the first non-commit port
        function Bool notCommit(SupWaySel i) = !commitEn[i];
        indexT nextDeqP;
        if(find(notCommit, supIdxVec) matches tagged Valid .idx) begin
            nextDeqP = commitIndex[idx];
            // sanity check: commit is done consecutively
            for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
                doAssert((fromInteger(i) < idx) == commitEn[i], "commit must be consecutive");
            end
        end
        else begin
            nextDeqP = incrIndex(deqP, fromInteger(valueof(SupSize)));
        end
        deqP <= nextDeqP;

        // do wrongSpec OR claim free phy reg
        if(wrongSpecEn.wget matches tagged Valid .x) begin
            SpecTag specTag = x.specTag;
            Vector#(size, indexT) idxVec = genWith(fromInteger);
            // do wrongSpec, first kill entries (make in-flight renaming to free phy reg)
            function Bool needKill(indexT i);
                return spec_bits[i][sb_wrongSpec_port][specTag] == 1;
            endfunction
            Vector#(size, Bool) isKill = map(needKill, idxVec);
            function Action kill(indexT i);
            action
                if(isKill[i]) begin
                    valid[i][valid_wrongSpec_port] <= False;
                end
            endaction
            endfunction
            joinActions(map(kill, idxVec));
            // move enqP: find the oldest **valid** entry being killed
            Vector#(size, Bool) killValid = zipWith( \&& , isKill , readVEhr(valid_wrongSpec_port, valid) );
            indexT nextEnqP = enqP;
            if(findOldest(killValid) matches tagged Valid .idx) begin
                nextEnqP = idx;
            end
            enqP <= nextEnqP;
        end
        else begin
            // claim phy reg
            for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
                if(claimEn[i].wget matches tagged Valid .claim) begin
                    indexT curEnqP = claimIndex[i];
                    new_renamings_arch[curEnqP] <= claim.arch; // keep phy reg unchanged
                    valid[curEnqP][valid_claim_port] <= True;
                    spec_bits[curEnqP][sb_claim_port] <= claim.specBits;
                    // sanity check
                    doAssert(!valid[curEnqP][valid_get_port], "claiming entry must be invalid");
                    doAssert(claim.phy == new_renamings_phy[curEnqP], "phy reg should match");
                end
            end
            // move enqP: find the first non-claim port
            function Bool notClaim(SupWaySel i) = !isValid(claimEn[i].wget);
            indexT nextEnqP;
            if(find(notClaim, supIdxVec) matches tagged Valid .idx) begin
                nextEnqP = claimIndex[idx];
                // sanity check: rename is consecutive
                for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
                    doAssert((fromInteger(i) < idx) == isValid(claimEn[i].wget), "claim is consecutive");
                end
            end
            else begin
                nextEnqP = incrIndex(enqP, fromInteger(valueof(SupSize)));
            end
            enqP <= nextEnqP;
        end
    endrule

`ifdef BSIM
    // sanity check in simulation
    // all valid entry are within [deqP, enqP), outsiders are invalid entries
    (* fire_when_enabled, no_implicit_conditions *)
    rule sanityCheck;
        Bool empty = all( \== (False), readVEhr(0, valid) );
        function Bool in_range(indexT i);
            // i is within [deqP, enqP)
            if(empty) begin
                return False;
            end
            else begin
                if(deqP < enqP) begin
                    return deqP <= i && i < enqP;
                end
                else begin
                    return deqP <= i || i < enqP;
                end
            end
        endfunction
        for(Integer i = 0; i < valueof(size); i = i+1) begin
            doAssert(in_range(fromInteger(i)) == valid[i][0],
                "entries inside [deqP, enqP) should be valid, otherwise invalid"
            );
        end
        doAssert(enqP <= fromInteger(valueof(size) - 1), "enqP < size");
        doAssert(deqP <= fromInteger(valueof(size) - 1), "deqP < size");
    endrule
`endif

    // function to search claimed phy regs just in this cycle to
    // get phy reg for an arch reg for get_renaming at port getPort
    function Maybe#(PhyRIndx) search_claimed_renamings(Integer getPort, ArchRIndx arch_reg);
        List#(RWire#(RenameClaim)) claims = List::take(getPort, toList(claimEn));
        function Maybe#(PhyRIndx) getHit(RWire#(RenameClaim) clm);
            if(clm.wget matches tagged Valid .e) begin
                return e.arch == (Valid (arch_reg)) ? Valid (e.phy) : Invalid;
            end
            else begin
                return Invalid;
            end
        endfunction
        List#(Maybe#(PhyRIndx)) hitList = List::map(getHit, claims);
        // find the most recently claimed phy reg (i.e. largest list index)
        Maybe#(Maybe#(PhyRIndx)) hit = List::find(isValid, List::reverse(hitList));
        return fromMaybe(Invalid, hit);
    endfunction

    // function to search new_renamings to get phy reg for an arch reg
    function Maybe#(PhyRIndx) search_new_src_renamings(ArchRIndx arch_reg);
        // first get all hitting in-flight renamings
        function Bool hit(Integer i);
            return valid[i][valid_get_port] && new_renamings_arch[i] == (Valid (arch_reg));
        endfunction
        Vector#(size, Bool) isHit = map(hit, genVector);
        // find the youngest
        if(findYoungest(isHit) matches tagged Valid .idx) begin
            return Valid (new_renamings_phy[idx]);
        end
        else begin
            return Invalid;
        end
    endfunction

    // function to get phy reg for an arch reg for get_renaming at port getPort
    // priority: newly claimed -> new_renamings -> renaming_table
    function PhyRIndx get_src_renaming(Integer getPort, ArchRIndx arch_reg);
        let claim_phy_reg = search_claimed_renamings(getPort, arch_reg);
        let new_phy_reg = search_new_src_renamings(arch_reg);
        let existing_phy_reg = renaming_table[getRTIndex(arch_reg)][rt_get_port];
        return fromMaybe(fromMaybe(existing_phy_reg, new_phy_reg), claim_phy_reg);
    endfunction

    // function to find a free phy reg to claim (at port claimPort) for a dst arch reg
    function PhyRIndx get_dst_renaming(Integer claimPort);
        return new_renamings_phy[claimIndex[claimPort]];
    endfunction

    function Bool isFpuReg(ArchRIndx arch_reg);
        return arch_reg matches tagged Fpu .* ? True : False;
    endfunction

    Vector#(SupSize, RTRename) renameIfc;
    for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
        // XXX we always claim a free phy reg, but only return it if arch dst reg is valid
        Bool guard = !valid[claimIndex[i]][valid_get_port];
        PhyRIndx claim_phy_reg = get_dst_renaming(i);
        renameIfc[i] = (interface RTRename;
            method RenameResult getRename(ArchRegs r) if(guard);
                // get renamings
                PhyRegs phy_regs = PhyRegs {
                    src1: tagged Invalid,
                    src2: tagged Invalid,
                    src3: tagged Invalid,
                    dst: tagged Invalid
                };
                if (r.src1 matches tagged Valid .valid_src1) begin
                    phy_regs.src1 = Valid (get_src_renaming(i, valid_src1));
                end
                if (r.src2 matches tagged Valid .valid_src2) begin
                    phy_regs.src2 = Valid (get_src_renaming(i, valid_src2));
                end
                if (r.src3 matches tagged Valid .valid_src3) begin
                    phy_regs.src3 = tagged Valid (get_src_renaming(i, tagged Fpu valid_src3));
                end
                if (r.dst matches tagged Valid .valid_dst) begin
                    phy_regs.dst = Valid (PhyDst {
                        indx: claim_phy_reg,
                        isFpuReg: isFpuReg(valid_dst)
                    });
                end

                return RenameResult {
                    phy_regs: phy_regs
                };
            endmethod

            method Action claimRename(ArchRegs r, SpecBits sb) if(guard);
                // record the claim
                claimEn[i].wset(RenameClaim {
                    arch: r.dst,
                    phy: claim_phy_reg,
                    specBits: sb
                });
                // conflict with wrong spec
                wrongSpec_rename_conflict[i].wset(?);
                // ordering with commit
                commit_SB_rename[i] <= False;
            endmethod

            method canRename = guard;
        endinterface);
    end

    Vector#(SupSize, RTCommit) commitIfc;
    for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
        Bool guard = valid[commitIndex[i]][valid_commit_port] &&
                     all(id, readVReg(commit_SB_rename)) && // ordering: commit < rename
                     commit_SB_wrongSpec; // ordering: commit < wrongSpec
        commitIfc[i] = (interface RTCommit;
            method Action commit if(guard);
                commitEn[i].send; // record commit action
            endmethod
            method canCommit = guard;
        endinterface);
    end

    interface rename = renameIfc;
    interface commit = commitIfc;

    interface SpeculationUpdate specUpdate;
        method Action incorrectSpeculation(SpecTag specTag);
            // record wrongSpec
            wrongSpecEn.wset(RTWrongSpec {
                specTag: specTag
            });
            // conflict with rename
            for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
                wrongSpec_rename_conflict[i].wset(?);
            end
            // order after commit
            commit_SB_wrongSpec <= False;
        endmethod
        method Action correctSpeculation(SpecBits mask);
            function Action correctSpec(Integer i);
            action
                spec_bits[i][sb_correctSpec_port] <= spec_bits[i][sb_correctSpec_port] & mask;
            endaction
            endfunction
            Vector#(size, Integer) idxVec = genVector;
            joinActions(map(correctSpec, idxVec));
        endmethod
    endinterface
endmodule

// Copyright (c) 2017 Massachusetts Institute of Technology
//
// 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.

// Alternate free list design:
// The FreeList has the same number of slots as #PhyReg - #ArchReg. When the
// ROB is empty, the FreeList is full. When the ROB is full, the FreeList is
// empty. The FreeList deq method takes the old name for the renamed register
// and writes it to the location just dequeued. On step-by-step rollback, the
// old name is taken from the FreeList and moved back to the RenamingTable.

import Vector::*;
import List::*;
import RevertingVirtualReg::*;
import Types::*;
import ProcTypes::*;
import HasSpecBits::*;
import Ehr::*;

typedef struct {
    PhyRegs phy_regs;
} RenameResult deriving(Bits, Eq, FShow);

interface RTRename;
    // This will rename the architectural registers in r to physical registers.
    // XXX This will **ALWAYS** claim a new physical register as a speculative
    // in-flight renaming, no matter whether the dst reg is valid or not.
    // This simplifies the implementation for superscalar renaming
    method RenameResult getRename(ArchRegs r);
    method Action claimRename(ArchRegs r, SpecBits sb);
    method Bool canRename; // guard of rename
endinterface

interface RTCommit;
    // Commits the oldest instructions. This will free the previous physical
    // register used to hold the value for the architectural destination
    // register.
    method Action commit;
    method Bool canCommit; // guard of commit
endinterface

interface RegRenamingTable;
    interface Vector#(SupSize, RTRename) rename; // rename port
    interface Vector#(SupSize, RTCommit) commit; // commit port

    // This subinterface contains the methods specifying correct and incorrect
    // speculation. If the speculation is correct, the dependencies on that
    // SpecTag should be removed from all SpecBits. If the speculation is
    // incorrect, then all renamings that depended on the SpecTag should be
    // reverted.
    interface SpeculationUpdate specUpdate;
    // methods: method Action incorrectSpeculation(SpecTag tag);
    //          method Action correctSpeculation(SpecTag tag);
endinterface

// actions when we claim a new phy reg in rename
typedef struct {
    Maybe#(ArchRIndx) arch;
    PhyRIndx phy;
    SpecBits specBits;
} RenameClaim deriving(Bits, Eq, FShow);

// actions in case of wrongSpec
typedef struct {
    SpecTag specTag;
} RTWrongSpec deriving(Bits, Eq, FShow);

(* synthesize *)
module mkRegRenamingTable(RegRenamingTable) provisos (
    NumAlias#(size, TSub#(NumPhyReg, NumArchReg)),
    Alias#(indexT, Bit#(TLog#(size))),
    Alias#(vTagT, Bit#(TLog#(TMul#(2, size)))) // virtual tag: 0 -- size*2-1
);
    // ordering: commit < rename < correctSpec
    // commit < wrongSpec
    // wrongSpec C rename

    // NOTES:
    // rename do not need to see the effect of commit
    // rename is conflict with wrongSpec, so don't need to see its effect
    // The real actions of commit, wrongSpec and rename will be done in canon rule

    // rename is split into two parts in EHR port assignment:
    // - get (get the phy reg for src and dst arch regs, in rename method)
    // - claim (truly claim a free phy reg for dst arch reg, in canon rule)

    // Although the EHR ports of commit should also be split into two parts,
    // we don't need to do so, because the method of commit is directly followed by canon rule.

    // EHR ports for renaming_table
    Integer rt_get_port = 0;
    function Integer rt_commit_port(Integer i) = i;

    // EHR ports for valid
    Integer valid_get_port = 0;
    Integer valid_commit_port = 0;
    Integer valid_wrongSpec_port = 1;
    Integer valid_claim_port = 1;

    // EHR ports for spec_bits
    Integer sb_wrongSpec_port = 0;
    Integer sb_claim_port = 0;
    Integer sb_correctSpec_port = 1;

    // non-speculative renaming table at commit port
    // initially arch reg i --> phy reg i
    Vector#(NumArchReg, Ehr#(SupSize, PhyRIndx)) renaming_table <- genWithM(compose(mkEhr, fromInteger));

    // A FIFO of
    // - in-flight renaming: when valid = True i.e. within [enqP, deqP)
    // - free phy reg: when valid = False i.e. outside [enqP, deqP)
    // We store the arch reg and phy reg separately to reduce EHR ports
    // XXX A valid in-flight renaming may have arch reg being invalid
    // This happens in case we claim a phy reg while the dst arch reg is invalid
    Vector#(size, Reg#(Maybe#(ArchRIndx))) new_renamings_arch <- replicateM(mkRegU);
    function m#(Reg#(PhyRIndx)) genNewRenamingsPhy(Integer i) provisos (IsModule#(m, a__));
        return mkReg(fromInteger(i + valueOf(NumArchReg))); // free phy regs initially
    endfunction
    Vector#(size, Reg#(PhyRIndx)) new_renamings_phy <- genWithM(genNewRenamingsPhy);
    Vector#(size, Ehr#(2, Bool)) valid <- replicateM(mkEhr(False));
    Vector#(size, Ehr#(2, SpecBits)) spec_bits <- replicateM(mkEhr(0));
    Reg#(indexT) enqP <- mkReg(0); // point to claim free phy reg
    Reg#(indexT) deqP <- mkReg(0); // point to commit renaming and make phy reg free

    // wires/EHRs to record actions
    Vector#(SupSize, Ehr#(2, Maybe#(RenameClaim))) claimEn <- replicateM(mkEhr(Invalid));
    Vector#(SupSize, PulseWire) commitEn <- replicateM(mkPulseWire);
    RWire#(RTWrongSpec) wrongSpecEn <- mkRWire;

    // ordering regs
    Vector#(SupSize, Reg#(Bool)) commit_SB_rename <- replicateM(mkRevertingVirtualReg(True));

    // wrong spec conflict with rename
    Vector#(SupSize, RWire#(void)) wrongSpec_rename_conflict <- replicateM(mkRWire);

    function indexT getNextIndex(indexT idx);
        return idx == fromInteger(valueof(size) - 1) ? 0 : idx + 1;
    endfunction

    function indexT incrIndex(indexT idx, Integer incr);
        incr = incr % valueof(size); // incr <= size-1
        Integer remain = valueof(size) - incr; // 1 <= remain <= size
        if(remain == valueof(size) || idx <= fromInteger(remain - 1)) begin
            // add remain == valueof(size) may optmize a little
            // because compiler may not know that idx < size
            return idx + fromInteger(incr);
        end
        else begin
            return idx - fromInteger(remain);
        end
    endfunction

    function Maybe#(t) findLast(function Bool pred(t x), Vector#(n, t) in);
        return find(pred, reverse(in));
    endfunction

    function List#(t) readListEhr(Integer i, List#(Ehr#(n, t)) ehrList);
        function t getVal(Ehr#(n, t) e) = e[i];
        return List::map(getVal, ehrList);
    endfunction

    // get the index to query renaming_table
    function Bit#(TLog#(NumArchReg)) getRTIndex(ArchRIndx arch) = pack(arch);

    // vector of index to claim free phy regs for each rename port
    Vector#(SupSize, indexT) claimIndex = map(incrIndex(enqP), genVector);

    // vector of index to commit phy regs for each commit port
    Vector#(SupSize, indexT) commitIndex = map(incrIndex(deqP), genVector);

    // similar to LSQ, get virtual tag by using enqP as pivot (enqP is changed at end of cycle)
    // valid entry i --> i < enqP ? i + size : i
    // NOTE that virtual tag only works for **valid** entry
    function vTagT getVTag(indexT i);
        return i < enqP ? zeroExtend(i) + fromInteger(valueof(size)) : zeroExtend(i);
    endfunction
    Vector#(size, vTagT) vTags = map(getVTag, genWith(fromInteger));

    // find oldest entry using virtual tag (i.e. smallest)
    function Maybe#(indexT) findOldest(Vector#(size, Bool) pred);
        function indexT getOlder(indexT a, indexT b);
            if(!pred[a]) begin
                return b;
            end
            else if(!pred[b]) begin
                return a;
            end
            else begin
                return vTags[a] < vTags[b] ? a : b;
            end
        endfunction
        Vector#(size, indexT) idxVec = genWith(fromInteger);
        indexT tag = fold(getOlder, idxVec);
        return pred[tag] ? Valid (tag) : Invalid;
    endfunction

    // find youngest entry using virtual tag (i.e. largest)
    function Maybe#(indexT) findYoungest(Vector#(size, Bool) pred);
        function indexT getOlder(indexT a, indexT b);
            if(!pred[a]) begin
                return b;
            end
            else if(!pred[b]) begin
                return a;
            end
            else begin
                return vTags[a] < vTags[b] ? b : a;
            end
        endfunction
        Vector#(size, indexT) idxVec = genWith(fromInteger);
        indexT tag = fold(getOlder, idxVec);
        return pred[tag] ? Valid (tag) : Invalid;
    endfunction

    // canonicalize deq, wrongSpec/enq
    (* fire_when_enabled, no_implicit_conditions *)
    rule canon;
        Vector#(SupSize, SupWaySel) supIdxVec = genWith(fromInteger);

        // apply commit actions
        for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
            if(commitEn[i]) begin
                indexT curDeqP = commitIndex[i]; // deqP for new renamings
                PhyRIndx commit_phy_reg = new_renamings_phy[curDeqP];
                Maybe#(ArchRIndx) commit_arch_reg = new_renamings_arch[curDeqP];
                if(commit_arch_reg matches tagged Valid .arch) begin
                    let rtIdx = getRTIndex(arch);
                    // free phy reg being overwritten in the renaming_table (arch reg is don't care)
                    PhyRIndx freed_phy_reg = renaming_table[rtIdx][rt_commit_port(i)];
                    new_renamings_phy[curDeqP] <= freed_phy_reg;
                    valid[curDeqP][valid_commit_port] <= False;
                    // update renaming_table
                    renaming_table[rtIdx][rt_commit_port(i)] <= commit_phy_reg;
                end
                else begin
                    // free the phy reg claimed by this renaming (arch reg is don't care)
                    valid[curDeqP][valid_commit_port] <= False;
                end
                // sanity check
                doAssert(valid[curDeqP][valid_commit_port], "committing entry must be valid");
            end
        end
        // move deqP: find the first non-commit port
        function Bool notCommit(SupWaySel i) = !commitEn[i];
        if(find(notCommit, supIdxVec) matches tagged Valid .idx) begin
            deqP <= commitIndex[idx];
            // sanity check: commit is done consecutively
            for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
                doAssert((fromInteger(i) < idx) == commitEn[i], "commit must be consecutive");
            end
        end
        else begin
            deqP <= incrIndex(deqP, valueof(SupSize));
        end

        // do wrongSpec OR claim free phy reg
        if(wrongSpecEn.wget matches tagged Valid .x) begin
            SpecTag specTag = x.specTag;
            Vector#(size, indexT) idxVec = genWith(fromInteger);
            // do wrongSpec, first kill entries (make in-flight renaming to free phy reg)
            function Bool needKill(indexT i);
                return spec_bits[i][sb_wrongSpec_port][specTag] == 1;
            endfunction
            Vector#(size, Bool) isKill = map(needKill, idxVec);
            function Action kill(indexT i);
            action
                if(isKill[i]) begin
                    valid[i][valid_wrongSpec_port] <= False;
                end
            endaction
            endfunction
            joinActions(map(kill, idxVec));
            // move enqP: find the oldest **valid** entry being killed
            Vector#(size, Bool) killValid = zipWith( \&& , isKill , readVEhr(valid_wrongSpec_port, valid) );
            if(findOldest(killValid) matches tagged Valid .idx) begin
                enqP <= idx;
            end
        end
        else begin
            // claim phy reg
            for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
                if(claimEn[i][1] matches tagged Valid .claim) begin
                    indexT curEnqP = claimIndex[i];
                    new_renamings_arch[curEnqP] <= claim.arch; // keep phy reg unchanged
                    valid[curEnqP][valid_claim_port] <= True;
                    spec_bits[curEnqP][sb_claim_port] <= claim.specBits;
                    // sanity check
                    doAssert(!valid[curEnqP][valid_get_port], "claiming entry must be invalid");
                    doAssert(claim.phy == new_renamings_phy[curEnqP], "phy reg should match");
                end
            end
            // move enqP: find the first non-claim port
            function Bool notClaim(SupWaySel i) = !isValid(claimEn[i][1]);
            if(find(notClaim, supIdxVec) matches tagged Valid .idx) begin
                enqP <= claimIndex[idx];
                // sanity check: rename is consecutive
                for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
                    doAssert((fromInteger(i) < idx) == isValid(claimEn[i][1]), "claim is consecutive");
                end
            end
            else begin
                enqP <= incrIndex(enqP, valueof(SupSize));
            end
        end

        // clear wire EHRs
        for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
            claimEn[i][1] <= Invalid;
        end
    endrule

`ifdef BSIM
    // sanity check in simulation
    // all valid entry are within [deqP, enqP), outsiders are invalid entries
    (* fire_when_enabled, no_implicit_conditions *)
    rule sanityCheck;
        Bool empty = all( \== (False), readVEhr(0, valid) );
        function Bool in_range(indexT i);
            // i is within [deqP, enqP)
            if(empty) begin
                return False;
            end
            else begin
                if(deqP < enqP) begin
                    return deqP <= i && i < enqP;
                end
                else begin
                    return deqP <= i || i < enqP;
                end
            end
        endfunction
        for(Integer i = 0; i < valueof(size); i = i+1) begin
            doAssert(in_range(fromInteger(i)) == valid[i][0],
                "entries inside [deqP, enqP) should be valid, otherwise invalid"
            );
        end
        doAssert(enqP <= fromInteger(valueof(size) - 1), "enqP < size");
        doAssert(deqP <= fromInteger(valueof(size) - 1), "deqP < size");
    endrule
`endif

    // function to search claimed phy regs just in this cycle to
    // get phy reg for an arch reg for get_renaming at port getPort
    function Maybe#(PhyRIndx) search_claimed_renamings(Integer getPort, ArchRIndx arch_reg);
        List#(Maybe#(RenameClaim)) claims = readListEhr(1, List::take(getPort, toList(claimEn)));
        function Maybe#(PhyRIndx) getHit(Maybe#(RenameClaim) x);
            if(x matches tagged Valid .e) begin
                return e.arch == (Valid (arch_reg)) ? Valid (e.phy) : Invalid;
            end
            else begin
                return Invalid;
            end
        endfunction
        List#(Maybe#(PhyRIndx)) hitList = List::map(getHit, claims);
        // find the most recently claimed phy reg (i.e. largest list index)
        Maybe#(Maybe#(PhyRIndx)) hit = List::find(isValid, List::reverse(hitList));
        return fromMaybe(Invalid, hit);
    endfunction

    // function to search new_renamings to get phy reg for an arch reg
    function Maybe#(PhyRIndx) search_new_src_renamings(ArchRIndx arch_reg);
        // first get all hitting in-flight renamings
        function Bool hit(Integer i);
            return valid[i][valid_get_port] && new_renamings_arch[i] == (Valid (arch_reg));
        endfunction
        Vector#(size, Bool) isHit = map(hit, genVector);
        // find the youngest
        if(findYoungest(isHit) matches tagged Valid .idx) begin
            return Valid (new_renamings_phy[idx]);
        end
        else begin
            return Invalid;
        end
    endfunction

    // function to get phy reg for an arch reg for get_renaming at port getPort
    // priority: newly claimed -> new_renamings -> renaming_table
    function PhyRIndx get_src_renaming(Integer getPort, ArchRIndx arch_reg);
        let claim_phy_reg = search_claimed_renamings(getPort, arch_reg);
        let new_phy_reg = search_new_src_renamings(arch_reg);
        let existing_phy_reg = renaming_table[getRTIndex(arch_reg)][rt_get_port];
        return fromMaybe(fromMaybe(existing_phy_reg, new_phy_reg), claim_phy_reg);
    endfunction

    // function to find a free phy reg to claim (at port claimPort) for a dst arch reg
    function PhyRIndx get_dst_renaming(Integer claimPort);
        return new_renamings_phy[claimIndex[claimPort]];
    endfunction

    function Bool isFpuReg(ArchRIndx arch_reg);
        return arch_reg matches tagged Fpu .* ? True : False;
    endfunction

    Vector#(SupSize, RTRename) renameIfc;
    for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
        // XXX we always claim a free phy reg, but only return it if arch dst reg is valid
        Bool guard = !valid[claimIndex[i]][valid_get_port];
        PhyRIndx claim_phy_reg = get_dst_renaming(i);
        renameIfc[i] = (interface RTRename;
            method RenameResult getRename(ArchRegs r) if(guard);
                // get renamings
                PhyRegs phy_regs = PhyRegs {
                    src1: tagged Invalid,
                    src2: tagged Invalid,
                    src3: tagged Invalid,
                    dst: tagged Invalid
                };
                if (r.src1 matches tagged Valid .valid_src1) begin
                    phy_regs.src1 = Valid (get_src_renaming(i, valid_src1));
                end
                if (r.src2 matches tagged Valid .valid_src2) begin
                    phy_regs.src2 = Valid (get_src_renaming(i, valid_src2));
                end
                if (r.src3 matches tagged Valid .valid_src3) begin
                    phy_regs.src3 = tagged Valid (get_src_renaming(i, tagged Fpu valid_src3));
                end
                if (r.dst matches tagged Valid .valid_dst) begin
                    phy_regs.dst = Valid (PhyDst {
                        indx: claim_phy_reg,
                        isFpuReg: isFpuReg(valid_dst)
                    });
                end

                return RenameResult {
                    phy_regs: phy_regs
                };
            endmethod

            method Action claimRename(ArchRegs r, SpecBits sb) if(guard);
                // record the claim
                claimEn[i][0] <= Valid (RenameClaim {
                    arch: r.dst,
                    phy: claim_phy_reg,
                    specBits: sb
                });
                // conflict with wrong spec
                wrongSpec_rename_conflict[i].wset(?);
                // ordering with commit
                commit_SB_rename[i] <= False;
            endmethod

            method canRename = guard;
        endinterface);
    end

    Vector#(SupSize, RTCommit) commitIfc;
    for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
        Bool guard = valid[commitIndex[i]][valid_commit_port] && all(id, readVReg(commit_SB_rename)); // ordering: commit < rename
        commitIfc[i] = (interface RTCommit;
            method Action commit if(guard);
                commitEn[i].send; // record commit action
            endmethod
            method canCommit = guard;
        endinterface);
    end

    interface rename = renameIfc;
    interface commit = commitIfc;

    interface SpeculationUpdate specUpdate;
        method Action incorrectSpeculation(SpecTag specTag);
            // record wrongSpec
            wrongSpecEn.wset(RTWrongSpec {
                specTag: specTag
            });
            // conflict with rename
            for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
                wrongSpec_rename_conflict[i].wset(?);
            end
        endmethod
        method Action correctSpeculation(SpecBits mask);
            function Action correctSpec(Integer i);
            action
                spec_bits[i][sb_correctSpec_port] <= spec_bits[i][sb_correctSpec_port] & mask;
            endaction
            endfunction
            Vector#(size, Integer) idxVec = genVector;
            joinActions(map(correctSpec, idxVec));
        endmethod
    endinterface
endmodule


fix build error in strstr

fix build error in strstr

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

// portz libraries
import Directory::*;
import CtrlMux::*;
import Portal::*;
import Leds::*;
import PortalMemory::*;
import Dma::*;
import DmaUtils::*;
import MemServer::*;

// generated by tool
import StrstrRequestWrapper::*;
import DmaConfigWrapper::*;
import StrstrIndicationProxy::*;
import DmaIndicationProxy::*;

// defined by user
import Strstr::*;

typedef enum {StrstrIndication, StrstrRequest, DmaIndication, DmaConfig} IfcNames deriving (Eq,Bits);
typedef 2 DegPar;

module mkPortalTop(StdPortalTop#(addrWidth))

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

   DmaIndicationProxy dmaIndicationProxy <- mkDmaIndicationProxy(DmaIndication);
   Vector#(DegPar,DmaReadBuffer#(64,1)) haystack_read_chans <- replicateM(mkDmaReadBuffer());
   Vector#(DegPar,DmaReadBuffer#(64,1)) needle_read_chans <- replicateM(mkDmaReadBuffer());
   Vector#(DegPar,DmaReadBuffer#(64,1)) mp_next_read_chans <- replicateM(mkDmaReadBuffer());

   Vector#(TMul#(3,DegPar), ObjectReadClient#(64)) readClients = newVector();
   for(Integer i = 0; i < valueOf(DegPar); i=i+1) begin
      readClients[(3*i)+0] = haystack_read_chans[i].dmaClient;
      readClients[(3*i)+1] = needle_read_chans[i].dmaClient;
      readClients[(3*i)+2] = mp_next_read_chans[i].dmaClient;
   end

   MemServer#(addrWidth,64) dma <- mkMemServerR(dmaIndicationProxy.ifc, readClients);
   DmaConfigWrapper dmaConfigWrapper <- mkDmaConfigWrapper(DmaConfig, dma.request);

   function ObjectReadServer#(x) rs(DmaReadBuffer#(x,y) rb);
      return rb.dmaServer;
   endfunction

   StrstrIndicationProxy strstrIndicationProxy <- mkStrstrIndicationProxy(StrstrIndication);
   StrstrRequest strstrRequest <- mkStrstrRequest(strstrIndicationProxy.ifc, map(rs,haystack_read_chans),
						  map(rs,needle_read_chans), map(rs,mp_next_read_chans));
   StrstrRequestWrapper strstrRequestWrapper <- mkStrstrRequestWrapper(StrstrRequest,strstrRequest);

   Vector#(4,StdPortal) portals;
   portals[0] = strstrRequestWrapper.portalIfc;
   portals[1] = strstrIndicationProxy.portalIfc;
   portals[2] = dmaConfigWrapper.portalIfc;
   portals[3] = dmaIndicationProxy.portalIfc;

   StdDirectory dir <- mkStdDirectory(portals);
   let ctrl_mux <- mkSlaveMux(dir,portals);

   interface interrupt = getInterruptVector(portals);
   interface slave = ctrl_mux;
   interface master = dma.master;
   interface leds = default_leds;
endmodule

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

// portz libraries
import Directory::*;
import CtrlMux::*;
import Portal::*;
import Leds::*;
import PortalMemory::*;
import Dma::*;
import DmaUtils::*;
import MemServer::*;

// generated by tool
import StrstrRequestWrapper::*;
import DmaConfigWrapper::*;
import StrstrIndicationProxy::*;
import DmaIndicationProxy::*;

// defined by user
import Strstr::*;

typedef enum {StrstrIndication, StrstrRequest, DmaIndication, DmaConfig} IfcNames deriving (Eq,Bits);
typedef 2 DegPar;

module mkPortalTop(StdPortalTop#(addrWidth))

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

   DmaIndicationProxy dmaIndicationProxy <- mkDmaIndicationProxy(DmaIndication);
   Vector#(DegPar,DmaReadBuffer#(64,1)) haystack_read_chans <- replicateM(mkDmaReadBuffer());
   Vector#(DegPar,DmaReadBuffer#(64,1)) needle_read_chans <- replicateM(mkDmaReadBuffer());
   Vector#(DegPar,DmaReadBuffer#(64,1)) mp_next_read_chans <- replicateM(mkDmaReadBuffer());

   Vector#(TMul#(3,DegPar), ObjectReadClient#(64)) readClients = newVector();
   for(Integer i = 0; i < valueOf(DegPar); i=i+1) begin
      readClients[(3*i)+0] = haystack_read_chans[i].dmaClient;
      readClients[(3*i)+1] = needle_read_chans[i].dmaClient;
      readClients[(3*i)+2] = mp_next_read_chans[i].dmaClient;
   end

   Vector#(0, ObjectWriteClient#(64)) writeClients = newVector();
   MemServer#(addrWidth,64) dma <- mkMemServer(dmaIndicationProxy.ifc, readClients, writeClients);
   DmaConfigWrapper dmaConfigWrapper <- mkDmaConfigWrapper(DmaConfig, dma.request);

   function ObjectReadServer#(x) rs(DmaReadBuffer#(x,y) rb);
      return rb.dmaServer;
   endfunction

   StrstrIndicationProxy strstrIndicationProxy <- mkStrstrIndicationProxy(StrstrIndication);
   StrstrRequest strstrRequest <- mkStrstrRequest(strstrIndicationProxy.ifc, map(rs,haystack_read_chans),
						  map(rs,needle_read_chans), map(rs,mp_next_read_chans));
   StrstrRequestWrapper strstrRequestWrapper <- mkStrstrRequestWrapper(StrstrRequest,strstrRequest);

   Vector#(4,StdPortal) portals;
   portals[0] = strstrRequestWrapper.portalIfc;
   portals[1] = strstrIndicationProxy.portalIfc;
   portals[2] = dmaConfigWrapper.portalIfc;
   portals[3] = dmaIndicationProxy.portalIfc;

   StdDirectory dir <- mkStdDirectory(portals);
   let ctrl_mux <- mkSlaveMux(dir,portals);

   interface interrupt = getInterruptVector(portals);
   interface slave = ctrl_mux;
   interface master = dma.master;
   interface leds = default_leds;
endmodule


interface SimulatorMMU -> Pareff

interface SimulatorMMU -> Pareff

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

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

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

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

// BSV Libraries
import RegFile::*;
import FIFO::*;
import FIFOF::*;
import Vector::*;
import GetPut::*;
import BRAMFIFO::*;
import BRAM::*;
import MemTypes::*;
import StmtFSM::*;
import ClientServer::*;
import ConnectalMemory::*;
import ConnectalCompletionBuffer::*;

typedef 32 MaxNumSGLists;
typedef Bit#(TLog#(MaxNumSGLists)) SGListId;
typedef 12 SGListPageShift0;
typedef 16 SGListPageShift4;
typedef 20 SGListPageShift8;
typedef Bit#(TLog#(MaxNumSGLists)) RegionsIdx;

typedef 8 IndexWidth;

typedef struct {
   SGListId             id;
   Bit#(MemOffsetSize) off;
} ReqTup deriving (Eq,Bits,FShow);

interface Pareff;
   method ActionValue#(Bit#(32)) init(Bit#(32) id, Bit#(32) handle, Bit#(32) size);
   method ActionValue#(Bit#(32)) initfd(Bit#(32) id, Bit#(32) fd);
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);

module mkPareff(Pareff);
   method ActionValue#(Bit#(32)) init(Bit#(32) id, Bit#(32) handle, Bit#(32) size);
      let v <- pareff_init(id, handle, size);
      return v;
   endmethod
   method ActionValue#(Bit#(32)) initfd(Bit#(32) id, Bit#(32) fd);
      let v <- pareff_initfd(id, fd);
      return v;
   endmethod
endmodule
`else
module mkPareff(Pareff);
   method ActionValue#(Bit#(32)) init(Bit#(32) id, Bit#(32) handle, Bit#(32) size);
      return 0;
   endmethod
   method ActionValue#(Bit#(32)) initfd(Bit#(32) id, Bit#(32) fd);
      return 0;
   endmethod
endmodule
`endif

interface MMU#(numeric type addrWidth);
   interface MMURequest request;
   interface Vector#(2,Server#(ReqTup,Bit#(addrWidth))) addr;
endinterface

typedef struct {
   Bit#(2) pageSize;
   Bit#(SGListPageShift8) value;
} Offset deriving (Eq,Bits,FShow);

typedef Bit#(TSub#(MemOffsetSize,SGListPageShift0)) Page0;
typedef Bit#(TSub#(MemOffsetSize,SGListPageShift4)) Page4;
typedef Bit#(TSub#(MemOffsetSize,SGListPageShift8)) Page8;

typedef struct {
   Bit#(TSub#(MemOffsetSize,SGListPageShift0)) barrier;
   Bit#(IndexWidth) idxOffset;
   } SingleRegion deriving (Eq,Bits,FShow);

typedef struct {
   SingleRegion reg8;
   SingleRegion reg4;
   SingleRegion reg0;
   } Region deriving (Eq,Bits,FShow);

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

// the address translation servers (addr[0], addr[1]) have a latency of 8 and are fully pipelined
module mkMMU#(Integer iid, Bool hostMapped, MMUIndication mmuIndication)(MMU#(addrWidth))
   provisos(Log#(MaxNumSGLists, listIdxSize),
	    Add#(listIdxSize,8, entryIdxSize),
	    Add#(a__,addrWidth,MemOffsetSize));

   let verbose = False;
   TagGen#(MaxNumSGLists) sglId_gen <- mkTagGen;
   rule complete_sglId_gen;
      let __x <- sglId_gen.complete;
   endrule

   // for simulators
   let pareff <- mkPareff();

   // stage 0 (latency == 1)
   Vector#(2, FIFO#(ReqTup)) incomingReqs <- replicateM(mkFIFO);

   // stage 1 (latency == 2)
   BRAM_Configure bramConfig = defaultValue;
   bramConfig.latency        = 2;
   BRAM2Port#(RegionsIdx, Maybe#(Region)) regall <- mkBRAM2Server(bramConfig);
   Vector#(2,FIFOF#(ReqTup))          reqs0 <- replicateM(mkSizedFIFOF(3));

   // stage 2 (latency == 1)
   Vector#(2,FIFOF#(Tuple3#(Bool,Bool,Bool))) conds <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(Tuple3#(Bit#(IndexWidth),Bit#(IndexWidth),Bit#(IndexWidth)))) idxOffsets0 <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(ReqTup))           reqs1 <- replicateM(mkSizedFIFOF(3));

   // stage 3 (latency == 1)
   Vector#(2,FIFOF#(Offset))           offs0 <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(Bit#(IndexWidth)))         pbases <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(Bit#(IndexWidth)))    idxOffsets1 <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(SGListId))         ptrs1 <- replicateM(mkFIFOF);

   // stage 4 (latency == 2)
   BRAM2Port#(Bit#(entryIdxSize),Page0) pages <- mkBRAM2Server(bramConfig);
   Vector#(2,FIFOF#(Offset))           offs1 <- replicateM(mkSizedFIFOF(3));

   // stage 4 (latnecy == 1)
   Vector#(2,FIFOF#(Bit#(addrWidth))) pageResponseFifos <- replicateM(mkFIFOF);

   FIFO#(DmaError) dmaErrorFifo <- mkFIFO();
   rule dmaError;
      let error <- toGet(dmaErrorFifo).get();
      mmuIndication.error(extend(pack(error.errorType)), error.pref, extend(error.off), fromInteger(iid));
   endrule

   let page_shift0 = fromInteger(valueOf(SGListPageShift0));
   let page_shift4 = fromInteger(valueOf(SGListPageShift4));
   let page_shift8 = fromInteger(valueOf(SGListPageShift8));

   function BRAMServer#(a,b) portsel(BRAM2Port#(a,b) x, Integer i);
      if(i==0) return x.portA;
      else return x.portB;
   endfunction

   for (Integer i = 0; i < 2; i=i+1)
      rule stage1;  // first read in the address cutoff values between regions
	 ReqTup req <- toGet(incomingReqs[i]).get();
	 portsel(regall, i).request.put(BRAMRequest{write:False, responseOnWrite:False,
            address:truncate(req.id), datain:?});
	 reqs0[i].enq(req);
      endrule

   // pipeline the address lookup
   for(Integer i = 0; i < 2; i=i+1) begin
      rule stage2; // Now compare address cutoffs with requested offset
	 ReqTup req <- toGet(reqs0[i]).get;
	 Maybe#(Region) m_regionall <- portsel(regall,i).response.get;

	 case (m_regionall) matches
	    tagged Valid .regionall: begin
               Page0 off0 = truncate(req.off >> valueOf(SGListPageShift0));
               Page4 off4 = truncate(req.off >> valueOf(SGListPageShift4));
               Page8 off8 = truncate(req.off >> valueOf(SGListPageShift8));
	       let cond8 = off8 < truncate(regionall.reg8.barrier);
	       let cond4 = off4 < truncate(regionall.reg4.barrier);
	       let cond0 = off0 < regionall.reg0.barrier;

	       if (verbose) $display("mkMMU::stage2: id=%d off=%h (%h %h %h) (%h %h %h)", req.id, req.off,
				     regionall.reg8.barrier, regionall.reg4.barrier, regionall.reg0.barrier,
				     off8, off4, off0);

	       conds[i].enq(tuple3(cond8,cond4,cond0));
	       idxOffsets0[i].enq(tuple3(regionall.reg8.idxOffset,regionall.reg4.idxOffset, regionall.reg0.idxOffset));
	       reqs1[i].enq(req);
	    end
	    tagged Invalid:
	       dmaErrorFifo.enq(DmaError { errorType: DmaErrorSGLIdInvalid, pref: extend(req.id), off:req.off });
	 endcase
      endrule
      rule stage3; // Based on results of comparision, select a region, putting it into 'o.pageSize'.  idxOffset holds offset in sglist table of relevant entry
	 ReqTup req <- toGet(reqs1[i]).get;
	 Offset o = Offset{pageSize: 0, value: truncate(req.off)};
	 Bit#(IndexWidth) pbase = 0;
	 Bit#(IndexWidth) idxOffset = 0;

	 match{.cond8,.cond4,.cond0} <- toGet(conds[i]).get;
	 match{.idxOffset8,.idxOffset4,.idxOffset0} <- toGet(idxOffsets0[i]).get;

	 if (cond8) begin
	    if (verbose) $display("mkMMU::request: req.id=%h req.off=%h", req.id, req.off);
	    o.pageSize = 3;
	    pbase = truncate(req.off>>page_shift8);
	    idxOffset = idxOffset8;
	 end
	 else if (cond4) begin
	    if (verbose) $display("mkMMU::request: req.id=%h req.off=%h", req.id, req.off);
	    o.pageSize = 2;
	    pbase = truncate(req.off>>page_shift4);
	    idxOffset = idxOffset4;
	 end
	 else if (cond0) begin
	    if (verbose) $display("mkMMU::request: req.id=%h req.off=%h", req.id, req.off);
	    o.pageSize = 1;
	    pbase = truncate(req.off>>page_shift0);
	    idxOffset = idxOffset0;
	 end
	 offs0[i].enq(o);
	 pbases[i].enq(pbase);
	 idxOffsets1[i].enq(idxOffset);
	 ptrs1[i].enq(req.id);
      endrule
      rule stage4; // Read relevant sglist entry
	 let off <- toGet(offs0[i]).get();
	 let pbase <- toGet(pbases[i]).get();
	 let idxOffset <- toGet(idxOffsets1[i]).get();
	 let ptr <- toGet(ptrs1[i]).get();
	 Bit#(IndexWidth) p = pbase + idxOffset;
	 if (off.pageSize == 0) begin
	    if (verbose) $display("mkMMU::addr[%d].request.put: ERROR   ptr=%h off=%h\n", i, ptr, off);
	    dmaErrorFifo.enq(DmaError { errorType: DmaErrorOffsetOutOfRange, pref: extend(ptr), off:extend(off.value) });
	 end
	 else begin
	    if (verbose) $display("mkMMU::pages[%d].read %h", i, {ptr,p});
	    portsel(pages, i).request.put(BRAMRequest{write:False, responseOnWrite:False,
						      address:{ptr,p}, datain:?});
	    offs1[i].enq(off);
	 end
      endrule
      rule stage5; // Concatenate page base address from sglist entry with LSB offset bits from request and return
	 Page0 page <- portsel(pages, i).response.get;
	 let offset <- toGet(offs1[i]).get();
	 if (verbose) $display("mkMMU::p ages[%d].response page=%h offset=%h", i, page, offset);
	 Bit#(MemOffsetSize) rv = ?;
	 Page4 b4 = truncate(page);
	 Page8 b8 = truncate(page);
	 case (offset.pageSize)
	    1: rv = {page,truncate(offset.value)};
	    2: rv = {b4,truncate(offset.value)};
	    3: rv = {b8,truncate(offset.value)};
	 endcase
	 pageResponseFifos[i].enq(truncate(rv));
      endrule
   end

   FIFO#(SGListId) configRespFifo <- mkFIFO;
   rule sendConfigResp;
      let ptr <- toGet(configRespFifo).get();
      mmuIndication.configResp(extend(ptr));
   endrule

   FIFOF#(Bit#(32)) idReturnFifo <- mkSizedBRAMFIFOF(valueOf(MaxNumSGLists));
   rule idReturnRule;
      let sglId <- toGet(idReturnFifo).get;
      sglId_gen.returnTag(truncate(sglId));
      portsel(regall, 1).request.put(BRAMRequest{write:True, responseOnWrite:False, address: truncate(sglId), datain: tagged Invalid });
      $display("idReturn %h", sglId);
   endrule

   Vector#(2,Server#(ReqTup,Bit#(addrWidth))) addrServers;
   for(Integer i = 0; i < 2; i=i+1)
      addrServers[i] =
      (interface Server#(ReqTup,Bit#(addrWidth));
	  interface Put request;
	     method Action put(ReqTup req);
		incomingReqs[i].enq(req);
	     endmethod
	  endinterface
	  interface Get response;
	     method ActionValue#(Bit#(addrWidth)) get();
		let rv <- toGet(pageResponseFifos[i]).get();
`ifdef BSIM
		rv = rv | (fromInteger(iid)<<valueOf(addrWidth)-3);
`endif
		return rv;
	     endmethod
	  endinterface
       endinterface);

   interface MMURequest request;
   method Action idRequest(SpecialTypeForSendingFd fd);
      let nextId <- sglId_gen.getTag;
      let resp = (fromInteger(iid) << 16) | extend(nextId);
      if (verbose) $display("mkMMU::idRequest %d", fd);
      let va <- pareff.initfd(resp, fd);
      mmuIndication.idResponse(resp);
   endmethod
   method Action idReturn(Bit#(32) sglId);
      idReturnFifo.enq(sglId);
   endmethod
   method Action region(Bit#(32) pointer, Bit#(64) barr8, Bit#(32) index8, Bit#(64) barr4, Bit#(32) index4, Bit#(64) barr0, Bit#(32) index0);
      portsel(regall, 1).request.put(BRAMRequest{write:True, responseOnWrite:False,
          address: truncate(pointer), datain: tagged Valid Region{
             reg8: SingleRegion{barrier: truncate(barr8), idxOffset: truncate(index8)},
             reg4: SingleRegion{barrier: truncate(barr4), idxOffset: truncate(index4)},
             reg0: SingleRegion{barrier: truncate(barr0), idxOffset: truncate(index0)}} });
      if (verbose) $display("mkMMU::region pointer=%d barr8=%h barr4=%h barr0=%h", pointer, barr8, barr4, barr0);
      configRespFifo.enq(truncate(pointer));
   endmethod

   method Action sglist(Bit#(32) pointer, Bit#(32) pointerIndex, Bit#(64) addr,  Bit#(32) len);
         if (fromInteger(iid) != pointer[31:16]) begin
	    $display("mkMMU::sglist ERROR");
	    $finish();
	 end
	 if(hostMapped)
	    let va <- pareff.init({0,pointer[31:16]}, {0,pointer[15:0]}, len);
         Bit#(IndexWidth) ind = truncate(pointerIndex);
	 portsel(pages, 0).request.put(BRAMRequest{write:True, responseOnWrite:False,
             address:{truncate(pointer),ind}, datain:truncate(addr)});
         if (verbose) $display("mkMMU::sglist pointer=%d pointerIndex=%d addr=%d len=%d", pointer, pointerIndex, addr, len);
   endmethod
   method Action setInterface(Bit#(32) interfaceId, Bit#(32) sglId);
       /* this method is only implemented in s/w responders */
   endmethod
   endinterface
   interface addr = addrServers;

endmodule

interface MMUAddrServer#(numeric type addrWidth, numeric type numServers);
   interface Vector#(numServers,Server#(ReqTup,Bit#(addrWidth))) servers;
endinterface

module mkMMUAddrServer#(Server#(ReqTup,Bit#(addrWidth)) server) (MMUAddrServer#(addrWidth,numServers));

   FIFOF#(Bit#(TAdd#(1,TLog#(numServers)))) tokFifo <- mkSizedFIFOF(9);
   Vector#(numServers, Server#(ReqTup,Bit#(addrWidth))) addrServers;
   Reg#(Bit#(TLog#(numServers))) arb <- mkReg(0);

   // this is a very crude arbiter.  something more sophisticated may be required (mdk)
   rule inc_arb;
      arb <= arb+1;
   endrule

   for(Integer i = 0; i < valueOf(numServers); i=i+1)
      addrServers[i] =
      (interface Server#(ReqTup,Bit#(addrWidth));
	  interface Put request;
	     method Action put(ReqTup req) if (arb == fromInteger(i));
		tokFifo.enq(fromInteger(i));
		server.request.put(req);
	     endmethod
	  endinterface
	  interface Get response;
	     method ActionValue#(Bit#(addrWidth)) get() if (tokFifo.first == fromInteger(i));
		tokFifo.deq;
		let rv <- server.response.get;
		return rv;
	     endmethod
	  endinterface
       endinterface);

   interface servers = addrServers;

endmodule

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

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

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

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

// BSV Libraries
import RegFile::*;
import FIFO::*;
import FIFOF::*;
import Vector::*;
import GetPut::*;
import BRAMFIFO::*;
import BRAM::*;
import MemTypes::*;
import StmtFSM::*;
import ClientServer::*;
import ConnectalMemory::*;
import ConnectalCompletionBuffer::*;

typedef 32 MaxNumSGLists;
typedef Bit#(TLog#(MaxNumSGLists)) SGListId;
typedef 12 SGListPageShift0;
typedef 16 SGListPageShift4;
typedef 20 SGListPageShift8;
typedef Bit#(TLog#(MaxNumSGLists)) RegionsIdx;

typedef 8 IndexWidth;

typedef struct {
   SGListId             id;
   Bit#(MemOffsetSize) off;
} ReqTup deriving (Eq,Bits,FShow);

interface Pareff;
   method ActionValue#(Bit#(32)) init(Bit#(32) id, Bit#(32) handle, Bit#(32) size);
   method ActionValue#(Bit#(32)) initfd(Bit#(32) id, Bit#(32) fd);
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);

module mkPareff(SimulatorMMU);
   method ActionValue#(Bit#(32)) init(Bit#(32) id, Bit#(32) handle, Bit#(32) size);
      let v <- pareff_init(id, handle, size);
      return v;
   endmethod
   method ActionValue#(Bit#(32)) initfd(Bit#(32) id, Bit#(32) fd);
      let v <- pareff_initfd(id, fd);
      return v;
   endmethod
endmodule
`else
module mkPareff(SimulatorMMU);
   method ActionValue#(Bit#(32)) init(Bit#(32) id, Bit#(32) handle, Bit#(32) size);
   endmethod
   method ActionValue#(Bit#(32)) initfd(Bit#(32) id, Bit#(32) fd);
   endmethod
endmodule
`endif

interface MMU#(numeric type addrWidth);
   interface MMURequest request;
   interface Vector#(2,Server#(ReqTup,Bit#(addrWidth))) addr;
endinterface

typedef struct {
   Bit#(2) pageSize;
   Bit#(SGListPageShift8) value;
} Offset deriving (Eq,Bits,FShow);

typedef Bit#(TSub#(MemOffsetSize,SGListPageShift0)) Page0;
typedef Bit#(TSub#(MemOffsetSize,SGListPageShift4)) Page4;
typedef Bit#(TSub#(MemOffsetSize,SGListPageShift8)) Page8;

typedef struct {
   Bit#(TSub#(MemOffsetSize,SGListPageShift0)) barrier;
   Bit#(IndexWidth) idxOffset;
   } SingleRegion deriving (Eq,Bits,FShow);

typedef struct {
   SingleRegion reg8;
   SingleRegion reg4;
   SingleRegion reg0;
   } Region deriving (Eq,Bits,FShow);

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

// the address translation servers (addr[0], addr[1]) have a latency of 8 and are fully pipelined
module mkMMU#(Integer iid, Bool hostMapped, MMUIndication mmuIndication)(MMU#(addrWidth))
   provisos(Log#(MaxNumSGLists, listIdxSize),
	    Add#(listIdxSize,8, entryIdxSize),
	    Add#(a__,addrWidth,MemOffsetSize));

   let verbose = False;
   TagGen#(MaxNumSGLists) sglId_gen <- mkTagGen;
   rule complete_sglId_gen;
      let __x <- sglId_gen.complete;
   endrule

   // for simulators
   let pareff <- mkPareff();

   // stage 0 (latency == 1)
   Vector#(2, FIFO#(ReqTup)) incomingReqs <- replicateM(mkFIFO);

   // stage 1 (latency == 2)
   BRAM_Configure bramConfig = defaultValue;
   bramConfig.latency        = 2;
   BRAM2Port#(RegionsIdx, Maybe#(Region)) regall <- mkBRAM2Server(bramConfig);
   Vector#(2,FIFOF#(ReqTup))          reqs0 <- replicateM(mkSizedFIFOF(3));

   // stage 2 (latency == 1)
   Vector#(2,FIFOF#(Tuple3#(Bool,Bool,Bool))) conds <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(Tuple3#(Bit#(IndexWidth),Bit#(IndexWidth),Bit#(IndexWidth)))) idxOffsets0 <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(ReqTup))           reqs1 <- replicateM(mkSizedFIFOF(3));

   // stage 3 (latency == 1)
   Vector#(2,FIFOF#(Offset))           offs0 <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(Bit#(IndexWidth)))         pbases <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(Bit#(IndexWidth)))    idxOffsets1 <- replicateM(mkFIFOF);
   Vector#(2,FIFOF#(SGListId))         ptrs1 <- replicateM(mkFIFOF);

   // stage 4 (latency == 2)
   BRAM2Port#(Bit#(entryIdxSize),Page0) pages <- mkBRAM2Server(bramConfig);
   Vector#(2,FIFOF#(Offset))           offs1 <- replicateM(mkSizedFIFOF(3));

   // stage 4 (latnecy == 1)
   Vector#(2,FIFOF#(Bit#(addrWidth))) pageResponseFifos <- replicateM(mkFIFOF);

   FIFO#(DmaError) dmaErrorFifo <- mkFIFO();
   rule dmaError;
      let error <- toGet(dmaErrorFifo).get();
      mmuIndication.error(extend(pack(error.errorType)), error.pref, extend(error.off), fromInteger(iid));
   endrule

   let page_shift0 = fromInteger(valueOf(SGListPageShift0));
   let page_shift4 = fromInteger(valueOf(SGListPageShift4));
   let page_shift8 = fromInteger(valueOf(SGListPageShift8));

   function BRAMServer#(a,b) portsel(BRAM2Port#(a,b) x, Integer i);
      if(i==0) return x.portA;
      else return x.portB;
   endfunction

   for (Integer i = 0; i < 2; i=i+1)
      rule stage1;  // first read in the address cutoff values between regions
	 ReqTup req <- toGet(incomingReqs[i]).get();
	 portsel(regall, i).request.put(BRAMRequest{write:False, responseOnWrite:False,
            address:truncate(req.id), datain:?});
	 reqs0[i].enq(req);
      endrule

   // pipeline the address lookup
   for(Integer i = 0; i < 2; i=i+1) begin
      rule stage2; // Now compare address cutoffs with requested offset
	 ReqTup req <- toGet(reqs0[i]).get;
	 Maybe#(Region) m_regionall <- portsel(regall,i).response.get;

	 case (m_regionall) matches
	    tagged Valid .regionall: begin
               Page0 off0 = truncate(req.off >> valueOf(SGListPageShift0));
               Page4 off4 = truncate(req.off >> valueOf(SGListPageShift4));
               Page8 off8 = truncate(req.off >> valueOf(SGListPageShift8));
	       let cond8 = off8 < truncate(regionall.reg8.barrier);
	       let cond4 = off4 < truncate(regionall.reg4.barrier);
	       let cond0 = off0 < regionall.reg0.barrier;

	       if (verbose) $display("mkMMU::stage2: id=%d off=%h (%h %h %h) (%h %h %h)", req.id, req.off,
				     regionall.reg8.barrier, regionall.reg4.barrier, regionall.reg0.barrier,
				     off8, off4, off0);

	       conds[i].enq(tuple3(cond8,cond4,cond0));
	       idxOffsets0[i].enq(tuple3(regionall.reg8.idxOffset,regionall.reg4.idxOffset, regionall.reg0.idxOffset));
	       reqs1[i].enq(req);
	    end
	    tagged Invalid:
	       dmaErrorFifo.enq(DmaError { errorType: DmaErrorSGLIdInvalid, pref: extend(req.id), off:req.off });
	 endcase
      endrule
      rule stage3; // Based on results of comparision, select a region, putting it into 'o.pageSize'.  idxOffset holds offset in sglist table of relevant entry
	 ReqTup req <- toGet(reqs1[i]).get;
	 Offset o = Offset{pageSize: 0, value: truncate(req.off)};
	 Bit#(IndexWidth) pbase = 0;
	 Bit#(IndexWidth) idxOffset = 0;

	 match{.cond8,.cond4,.cond0} <- toGet(conds[i]).get;
	 match{.idxOffset8,.idxOffset4,.idxOffset0} <- toGet(idxOffsets0[i]).get;

	 if (cond8) begin
	    if (verbose) $display("mkMMU::request: req.id=%h req.off=%h", req.id, req.off);
	    o.pageSize = 3;
	    pbase = truncate(req.off>>page_shift8);
	    idxOffset = idxOffset8;
	 end
	 else if (cond4) begin
	    if (verbose) $display("mkMMU::request: req.id=%h req.off=%h", req.id, req.off);
	    o.pageSize = 2;
	    pbase = truncate(req.off>>page_shift4);
	    idxOffset = idxOffset4;
	 end
	 else if (cond0) begin
	    if (verbose) $display("mkMMU::request: req.id=%h req.off=%h", req.id, req.off);
	    o.pageSize = 1;
	    pbase = truncate(req.off>>page_shift0);
	    idxOffset = idxOffset0;
	 end
	 offs0[i].enq(o);
	 pbases[i].enq(pbase);
	 idxOffsets1[i].enq(idxOffset);
	 ptrs1[i].enq(req.id);
      endrule
      rule stage4; // Read relevant sglist entry
	 let off <- toGet(offs0[i]).get();
	 let pbase <- toGet(pbases[i]).get();
	 let idxOffset <- toGet(idxOffsets1[i]).get();
	 let ptr <- toGet(ptrs1[i]).get();
	 Bit#(IndexWidth) p = pbase + idxOffset;
	 if (off.pageSize == 0) begin
	    if (verbose) $display("mkMMU::addr[%d].request.put: ERROR   ptr=%h off=%h\n", i, ptr, off);
	    dmaErrorFifo.enq(DmaError { errorType: DmaErrorOffsetOutOfRange, pref: extend(ptr), off:extend(off.value) });
	 end
	 else begin
	    if (verbose) $display("mkMMU::pages[%d].read %h", i, {ptr,p});
	    portsel(pages, i).request.put(BRAMRequest{write:False, responseOnWrite:False,
						      address:{ptr,p}, datain:?});
	    offs1[i].enq(off);
	 end
      endrule
      rule stage5; // Concatenate page base address from sglist entry with LSB offset bits from request and return
	 Page0 page <- portsel(pages, i).response.get;
	 let offset <- toGet(offs1[i]).get();
	 if (verbose) $display("mkMMU::p ages[%d].response page=%h offset=%h", i, page, offset);
	 Bit#(MemOffsetSize) rv = ?;
	 Page4 b4 = truncate(page);
	 Page8 b8 = truncate(page);
	 case (offset.pageSize)
	    1: rv = {page,truncate(offset.value)};
	    2: rv = {b4,truncate(offset.value)};
	    3: rv = {b8,truncate(offset.value)};
	 endcase
	 pageResponseFifos[i].enq(truncate(rv));
      endrule
   end

   FIFO#(SGListId) configRespFifo <- mkFIFO;
   rule sendConfigResp;
      let ptr <- toGet(configRespFifo).get();
      mmuIndication.configResp(extend(ptr));
   endrule

   FIFOF#(Bit#(32)) idReturnFifo <- mkSizedBRAMFIFOF(valueOf(MaxNumSGLists));
   rule idReturnRule;
      let sglId <- toGet(idReturnFifo).get;
      sglId_gen.returnTag(truncate(sglId));
      portsel(regall, 1).request.put(BRAMRequest{write:True, responseOnWrite:False, address: truncate(sglId), datain: tagged Invalid });
      $display("idReturn %h", sglId);
   endrule

   Vector#(2,Server#(ReqTup,Bit#(addrWidth))) addrServers;
   for(Integer i = 0; i < 2; i=i+1)
      addrServers[i] =
      (interface Server#(ReqTup,Bit#(addrWidth));
	  interface Put request;
	     method Action put(ReqTup req);
		incomingReqs[i].enq(req);
	     endmethod
	  endinterface
	  interface Get response;
	     method ActionValue#(Bit#(addrWidth)) get();
		let rv <- toGet(pageResponseFifos[i]).get();
`ifdef BSIM
		rv = rv | (fromInteger(iid)<<valueOf(addrWidth)-3);
`endif
		return rv;
	     endmethod
	  endinterface
       endinterface);

   interface MMURequest request;
   method Action idRequest(SpecialTypeForSendingFd fd);
      let nextId <- sglId_gen.getTag;
      let resp = (fromInteger(iid) << 16) | extend(nextId);
      if (verbose) $display("mkMMU::idRequest %d", fd);
      let va <- pareff.initfd(resp, fd);
      mmuIndication.idResponse(resp);
   endmethod
   method Action idReturn(Bit#(32) sglId);
      idReturnFifo.enq(sglId);
   endmethod
   method Action region(Bit#(32) pointer, Bit#(64) barr8, Bit#(32) index8, Bit#(64) barr4, Bit#(32) index4, Bit#(64) barr0, Bit#(32) index0);
      portsel(regall, 1).request.put(BRAMRequest{write:True, responseOnWrite:False,
          address: truncate(pointer), datain: tagged Valid Region{
             reg8: SingleRegion{barrier: truncate(barr8), idxOffset: truncate(index8)},
             reg4: SingleRegion{barrier: truncate(barr4), idxOffset: truncate(index4)},
             reg0: SingleRegion{barrier: truncate(barr0), idxOffset: truncate(index0)}} });
      if (verbose) $display("mkMMU::region pointer=%d barr8=%h barr4=%h barr0=%h", pointer, barr8, barr4, barr0);
      configRespFifo.enq(truncate(pointer));
   endmethod

   method Action sglist(Bit#(32) pointer, Bit#(32) pointerIndex, Bit#(64) addr,  Bit#(32) len);
         if (fromInteger(iid) != pointer[31:16]) begin
	    $display("mkMMU::sglist ERROR");
	    $finish();
	 end
	 if(hostMapped)
	    let va <- pareff.init({0,pointer[31:16]}, {0,pointer[15:0]}, len);
         Bit#(IndexWidth) ind = truncate(pointerIndex);
	 portsel(pages, 0).request.put(BRAMRequest{write:True, responseOnWrite:False,
             address:{truncate(pointer),ind}, datain:truncate(addr)});
         if (verbose) $display("mkMMU::sglist pointer=%d pointerIndex=%d addr=%d len=%d", pointer, pointerIndex, addr, len);
   endmethod
   method Action setInterface(Bit#(32) interfaceId, Bit#(32) sglId);
       /* this method is only implemented in s/w responders */
   endmethod
   endinterface
   interface addr = addrServers;

endmodule

interface MMUAddrServer#(numeric type addrWidth, numeric type numServers);
   interface Vector#(numServers,Server#(ReqTup,Bit#(addrWidth))) servers;
endinterface

module mkMMUAddrServer#(Server#(ReqTup,Bit#(addrWidth)) server) (MMUAddrServer#(addrWidth,numServers));

   FIFOF#(Bit#(TAdd#(1,TLog#(numServers)))) tokFifo <- mkSizedFIFOF(9);
   Vector#(numServers, Server#(ReqTup,Bit#(addrWidth))) addrServers;
   Reg#(Bit#(TLog#(numServers))) arb <- mkReg(0);

   // this is a very crude arbiter.  something more sophisticated may be required (mdk)
   rule inc_arb;
      arb <= arb+1;
   endrule

   for(Integer i = 0; i < valueOf(numServers); i=i+1)
      addrServers[i] =
      (interface Server#(ReqTup,Bit#(addrWidth));
	  interface Put request;
	     method Action put(ReqTup req) if (arb == fromInteger(i));
		tokFifo.enq(fromInteger(i));
		server.request.put(req);
	     endmethod
	  endinterface
	  interface Get response;
	     method ActionValue#(Bit#(addrWidth)) get() if (tokFifo.first == fromInteger(i));
		tokFifo.deq;
		let rv <- server.response.get;
		return rv;
	     endmethod
	  endinterface
       endinterface);

   interface servers = addrServers;

endmodule