Removed conflict in uncached mem converter that slowed down stores

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

`include "ProcConfig.bsv"

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

import Ehr::*;

import Abstraction::*;
import RVAmo::*;
import RVExec::*; // for scatterStore and gatherLoad
import RVTypes::*;

// Dummy DCache
// This module has no internal storage. It just takes care of converting
// between RISC-V memory requests and main memory requests.
module mkDummyRVDCache#(GenericMemServer#(DataSz) mainMemory)(Server#(RVDMemReq, RVDMemResp));
    FIFOF#(RVDMemReq) procMemReq <- mkFIFOF;
    FIFOF#(RVDMemResp) procMemResp <- mkFIFOF;

    // True if this dummy cache is currently handling the "modify-write" part
    // of a read-modify-write operation.
    // NOTE: normal stores with byte enables count as RMW operations.
    Reg#(Bool) rmw <- mkReg(False);

    // Forces this dummy cache to wait for writes to finish before handling new reads
    Reg#(Bool) writePending <- mkReg(False);
    Reg#(Bool) readPending <- mkReg(False);

    FIFOF#(Tuple3#(DataByteSel,RVMemSize,Bool)) outstanding <- mkFIFOF;

    function Bool requiresRMW(RVDMemReq r);
        // The AxiSharedMemoryBridge can't handle byte enables, so we are handling it here
        return ((isStore(r.op) && r.size != D) || isAmo(r.op));
    endfunction

    rule ignoreWriteResps;
        let _x <- mainMemory.response.get();
        when(_x.write == True, noAction);
        writePending <= False;
    endrule

    // hanle request
    rule handleRVDMemReq(!rmw && !writePending && !readPending);
        let r = procMemReq.first;
        // This dummy cache does not support LR/SR instructions
        if (isLoad(r.op) || requiresRMW(r)) begin
            // send read request
            // this handles all store request with size < D
            mainMemory.request.put(GenericMemReq{write: False, byteen: '1, addr: r.addr, data: ?});
            readPending <= True;
        end else if (isStore(r.op)) begin
            // send write request (only when size == D)
            mainMemory.request.put(GenericMemReq{write: True, byteen: '1, addr: r.addr, data: r.data});
            writePending <= True;
            if (r.op == tagged Mem Sc) begin
                // successful
                procMemResp.enq(0);
            end
        end
        if (requiresRMW(r)) begin
            rmw <= True;
        end else begin
            procMemReq.deq;
        end
    endrule

    rule finishRMW(rmw && !writePending);
        let r = procMemReq.first;
        // get read data
        let memResp <- mainMemory.response.get;
        when(memResp.write == False, noAction);
        readPending <= False;
        let currData = memResp.data;
        // amoExec also handles plain stores with byte enables
        // If r.op is not an AMO operation, use Swap to perform stores with byte enables
        let {permutedDataByteEn, permutedStData} = scatterStore(truncate(r.addr), r.size, r.data);
        let newData = amoExec(r.op matches tagged Amo .amoFunc ? amoFunc : Swap, permutedDataByteEn, currData, permutedStData);
        mainMemory.request.put(GenericMemReq{write: True, byteen: '1, addr: r.addr, data: newData});
        writePending <= True;
        if (r.op == tagged Mem Sc) begin
            // Successful
            procMemResp.enq(0);
        end else if (r.op matches tagged Amo .*) begin
            procMemResp.enq(currData);
        end
        // finish
        rmw <= False;
        procMemReq.deq;
    endrule

    rule handleRVDMemResp(!rmw);
        let memResp <- mainMemory.response.get;
        when(memResp.write == False, noAction);
        procMemResp.enq(memResp.data);
        readPending <= False;
    endrule

    interface Put request;
        method Action put(RVDMemReq r);
            if (isLoad(r.op) || isAmo(r.op)) begin
                DataByteSel addrByteSel = truncate(r.addr);
                outstanding.enq(tuple3(addrByteSel, r.size, r.isUnsigned));
            end else if (r.op == tagged Mem Sc) begin
                outstanding.enq(tuple3(0, B, False));
            end
            procMemReq.enq(r);
        endmethod
    endinterface
    interface Get response;
        method ActionValue#(RVDMemResp) get;
            let {addrByteSel, size, isUnsigned} = outstanding.first;
            outstanding.deq;
            procMemResp.deq;
            return gatherLoad(addrByteSel, size, isUnsigned, procMemResp.first);
        endmethod
    endinterface
endmodule

module mkRVIMemWrapper#(Server#(RVDMemReq, RVDMemResp) dMem)(Server#(RVIMemReq, RVIMemResp));
    interface Put request;
        method Action put(RVIMemReq req);
            let dReq = RVDMemReq {
                    op: tagged Mem Ld,
                    size: W,
                    isUnsigned: False,
                    addr: req,
                    data: 0 };
            dMem.request.put(dReq);
        endmethod
    endinterface
    interface Get response;
        method ActionValue#(RVIMemResp) get;
            let resp <- dMem.response.get;
            return truncate(resp);
        endmethod
    endinterface
endmodule

module mkDummyRVICache#(GenericMemServer#(DataSz) mainMemory)(Server#(RVIMemReq, RVIMemResp));
    let _dMem <- mkDummyRVDCache(mainMemory);
    let _iMem <- mkRVIMemWrapper(_dMem);
    return _iMem;
endmodule

interface RVDMMU;
    interface Put#(RVDMMUReq) request;
    interface Get#(RVDMMUResp) response;
    method Action updateVMInfo(VMInfo vm);
endinterface

interface RVIMMU;
    interface Put#(RVIMMUReq) request;
    interface Get#(RVIMMUResp) response;
    method Action updateVMInfo(VMInfo vm);
endinterface

`ifndef CONFIG_RV32
// XXX: No RV32 support for MMU's yet

// This does not support any paged virtual memory modes
// TODO: add support for getPMA
module mkBasicDummyRVDMMU#(Bool isInst, GenericMemServer#(DataSz) mainMemory)(RVDMMU);
    FIFOF#(RVDMMUReq) procMMUReq <- mkFIFOF;
    FIFOF#(RVDMMUResp) procMMUResp <- mkFIFOF;

    // TODO: This should be defaultValue
    Reg#(VMInfo) vmInfo <- mkReg(VMInfo{prv:2'b11, asid:0, vm:0, mxr: False, pum: False, base:0, bound:'1});

    rule doTranslate;
        // TODO: add misaligned address exceptions

        // let {addr: .vaddr, op: .op} = procMMUReq.first;
        let vaddr = procMMUReq.first.addr;
        let op = procMMUReq.first.op;
        procMMUReq.deq;

        Bool isStore = getsWritePermission(op); // XXX: was isStore(r.op) || isAmo(r.op) || (r.op == tagged Mem PrefetchForSt);
        Bool isSupervisor = vmInfo.prv == prvS;

        PAddr paddr = 0;
        Maybe#(ExceptionCause) exception = tagged Invalid;
        Maybe#(ExceptionCause) accessFault = tagged Valid (isInst ? InstAccessFault :
                                                            (isStore ? StoreAccessFault :
                                                                LoadAccessFault));

        if (vmInfo.prv == prvM) begin
            paddr = vaddr;
        end else begin
            case (vmInfo.vm)
                vmMbare:        paddr = vaddr;
                vmMbb, vmMbbid: begin
                                    if (vaddr >= vmInfo.bound) begin
                                        exception = accessFault;
                                    end else begin
                                        paddr = vaddr + vmInfo.base;
                                    end
                                end
                default:
                    // unsupported virtual addressing mode
                    exception = accessFault;
            endcase
        end

        procMMUResp.enq(RVDMMUResp{addr: paddr, exception: exception});
    endrule

    interface Put request;
        method Action put(RVDMMUReq r);
            procMMUReq.enq(r);
        endmethod
    endinterface
    interface Get response;
        method ActionValue#(RVDMMUResp) get;
            procMMUResp.deq;
            return procMMUResp.first;
        endmethod
    endinterface
    method Action updateVMInfo(VMInfo vm);
        vmInfo <= vm;
    endmethod
endmodule

module mkRVIMMUWrapper#(RVDMMU dMMU)(RVIMMU);
    interface Put request;
        method Action put(RVIMMUReq req);
            dMMU.request.put(RVDMMUReq{addr: req, size: W, op: Ld}); // XXX: Should this type include AMO instructions too?
        endmethod
    endinterface
    interface Get response;
        method ActionValue#(RVIMMUResp) get;
            let resp <- dMMU.response.get;
            return resp;
        endmethod
    endinterface
    method Action updateVMInfo(VMInfo vm);
        dMMU.updateVMInfo(vm);
    endmethod
endmodule

module mkDummyRVIMMU#(function PMA getPMA(PAddr addr), GenericMemServer#(DataSz) mainMemory)(RVIMMU);
    let _dMMU <- mkDummyRVDMMU(True, getPMA, mainMemory);
    let _iMMU <- mkRVIMMUWrapper(_dMMU);
    return _iMMU;
endmodule

module mkDummyRVDMMU#(Bool isInst, function PMA getPMA(PAddr addr), GenericMemServer#(DataSz) mainMemory)(RVDMMU);
    FIFOF#(RVDMMUReq) procMMUReq <- mkFIFOF;
    FIFOF#(RVDMMUResp) procMMUResp <- mkFIFOF;

    // TODO: This should be defaultValue
    Reg#(VMInfo) vmInfo <- mkReg(VMInfo{prv:2'b11, asid:0, vm:0, mxr: False, pum: False, base:0, bound:'1});

    // Registers for hardware pagetable walk
    Reg#(Bool) walking <- mkReg(False);
    Reg#(Bool) store <- mkReg(False);
    Reg#(Bool) supervisor <- mkReg(False);
    Reg#(PAddr) a <- mkReg(0);
    Reg#(Bit#(2)) i <- mkReg(0);
    Reg#(Bit#(64)) pte <- mkReg(0);
    Reg#(Vector#(3,Bit#(9))) vpn <- mkReg(replicate(0));
    Reg#(Bit#(12)) pageoffset <- mkReg(0);

    // if the response from main memory is for a write, drop it
    // XXX: this does not work if mainMemory has a synthesize boundary
    rule ignoreWriteResps;
        let _x <- mainMemory.response.get();
        when(_x.write == True, noAction);
    endrule

    rule doPageTableWalk(walking);
        let memResp <- mainMemory.response.get();
        when(memResp.write == False, noAction);

        PTE_Sv39 pte = unpack(memResp.data);
        Maybe#(ExceptionCause) accessFault = tagged Valid (isInst ? InstAccessFault :
                                                            (store ? StoreAccessFault :
                                                                LoadAccessFault));
        Bool leafPTE = isLeafPTE(pte);
        Bool legalPTE = isLegalPTE(pte);
        if ((i == 0) && !leafPTE) begin
            // non-leaf PTEs at the lowest level of the page table are treated
            // as illegal PTEs
            legalPTE = False;
        end

        if (!legalPTE) begin
            // illegal pte, access fault
            procMMUResp.enq(RVDMMUResp{addr: 0, exception: accessFault});
            walking <= False;
        end else if (leafPTE) begin
            // valid leaf page

            // translated physical address
            PAddr paddr = '1;
            if (i == 0) begin
                paddr = {0, pte.ppn2, pte.ppn1, pte.ppn0, pageoffset};
            end else if (i == 1) begin
                paddr = {0, pte.ppn2, pte.ppn1, vpn[0], pageoffset};
            end else begin // if (i == 2)
                paddr = {0, pte.ppn2, vpn[1], vpn[0], pageoffset};
            end

            // check page permissions
            Bool hasPermission = False;
            if (isInst) begin
                hasPermission = pte.x;
            end else if (store) begin
                hasPermission = pte.w;
            end else begin
                hasPermission = pte.r || (vmInfo.mxr && pte.x);
            end
            // supervisor can't access user pages if pum is set
            if (supervisor && vmInfo.pum && pte.u) begin
                hasPermission = False;
            end
            // user needs pte.u bit set
            if (!supervisor && !pte.u) begin
                hasPermission = False;
            end

            // check PMA permissions
            PMA pma = getPMA(paddr);
            if (pma == IORom) begin
                // no W permission
                if (store) begin
                    hasPermission = False;
                end
            end else if (pma == IODevice) begin
                // no AMO permission
                // TODO: change type of MMU request to include sufficient AMO
                // information (AMO Class) and check permission accordingly
                // no X permission
                if (isInst) begin
                    hasPermission = False;
                end
            end else if (pma == IOEmpty) begin
                // no R, W, or X permission
                hasPermission = False;
            end

            if (!hasPermission) begin
                // illegal, access fault
                procMMUResp.enq(RVDMMUResp{addr: 0, exception: accessFault});
                walking <= False;
            end else begin
                // legal, return translation
                procMMUResp.enq(RVDMMUResp{addr: paddr, exception: tagged Invalid});
                walking <= False;
                if (!pte.a || (store && !pte.d)) begin
                    // write back necessary
                    // update pte
                    pte.a = True;
                    if (store) begin
                        pte.d = True;
                    end
                    // send write request
                    mainMemory.request.put(GenericMemReq{write: True, byteen: '1, addr: a, data: pack(pte)});
                end
            end
        end else begin
            // go to next level
            PAddr newA = {0, pte.ppn2, pte.ppn1, pte.ppn0, 12'b0} | (zeroExtend(vpn[i-1]) << 3);
            mainMemory.request.put(GenericMemReq{write: False, byteen: '1, addr: newA, data: ?});
            a <= newA;
            i <= i - 1;
        end
    endrule

    rule doTranslate(!walking);
        // let {addr: .vaddr, op: .op} = procMMUReq.first;
        let vaddr = procMMUReq.first.addr;
        let size = procMMUReq.first.size;
        let op = procMMUReq.first.op;
        procMMUReq.deq;

        Bool isStore = (op == St || op == Sc || op == PrefetchForSt);
        Bool isSupervisor = vmInfo.prv == prvS;
        Bool pageTableWalk = False;

        PAddr paddr = 0;
        Maybe#(ExceptionCause) exception = tagged Invalid;
        Maybe#(ExceptionCause) accessFault = tagged Valid (isInst ? InstAccessFault :
                                                            (isStore ? StoreAccessFault :
                                                                LoadAccessFault));
        Maybe#(ExceptionCause) misalignedFault = tagged Valid (isInst ? InstAddrMisaligned :
                                                            (isStore ? StoreAddrMisaligned :
                                                                LoadAddrMisaligned));
        Bit#(3) alignmentBits = (case(size)
                D:       3'b111;
                W:       3'b011;
                H:       3'b001;
                B:       3'b000;
                default: 3'b111;
            endcase);
        if ((truncate(vaddr) & alignmentBits) != 0) begin
            exception = misalignedFault;
        end else if (vmInfo.prv == prvM) begin
            paddr = vaddr;
        end else begin
            case (vmInfo.vm)
                vmMbare:        paddr = vaddr;
                vmMbb, vmMbbid: begin
                                    if (vaddr >= vmInfo.bound) begin
                                        exception = accessFault;
                                    end else begin
                                        paddr = vaddr + vmInfo.base;
                                    end
                                end
                vmSv39: begin
                            // start page table walk
                            // PAddr newA = vmInfo.base + (zeroExtend(vpn[2]) << 3);
                            Vector#(3, Bit#(9)) newvpn = unpack(vaddr[38:12]);
                            PAddr newA = vmInfo.base + (zeroExtend(newvpn[2]) << 3);
                            mainMemory.request.put(GenericMemReq{write: False, byteen: '1, addr: newA, data: ?});
                            walking <= True;
                            pageTableWalk = True;
                            a <= newA;
                            i <= 2;
                            vpn <= newvpn;
                            pageoffset <= truncate(vaddr);
                            store <= isStore;
                            supervisor <= isSupervisor;
                        end
                default:
                    // unsupported virtual addressing mode
                    exception = accessFault;
            endcase
        end

        if (!pageTableWalk) begin
            procMMUResp.enq(RVDMMUResp{addr: paddr, exception: exception});
        end
    endrule

    interface Put request;
        method Action put(RVDMMUReq r);
            procMMUReq.enq(r);
        endmethod
    endinterface
    interface Get response;
        method ActionValue#(RVDMMUResp) get;
            procMMUResp.deq;
            return procMMUResp.first;
        endmethod
    endinterface
    method Action updateVMInfo(VMInfo vm);
        vmInfo <= vm;
    endmethod
endmodule

`endif

// type used in the module below
typedef struct {
    Bool isWrite;
    RVMemSize size;
    Bool isUnsigned;
} PendingUncachedReqInfo deriving (Bits, Eq, FShow);
// probably should be called mkUncachedBridge
module mkUncachedConverter#(UncachedMemServer uncachedMem)(Server#(RVDMemReq, RVDMemResp));
    // this assumes there are no RMW operations to uncached memory
    FIFOF#(PendingUncachedReqInfo) bookkeepingFIFO <- mkFIFOF;

    Ehr#(2, Maybe#(RVDMemResp)) respEhr <- mkEhr(tagged Invalid);

    rule handleResp(!isValid(respEhr[0]));
        let resp <- uncachedMem.response.get;
        if (bookkeepingFIFO.notEmpty) begin
            let reqInfo = bookkeepingFIFO.first;
            if (reqInfo.isWrite != resp.write) begin
                $fdisplay(stderr, "[ERROR] Uncached memory responses out of sync.");
                $fdisplay(stderr, "[INFO] top of bookkeeping FIFO: ", fshow(bookkeepingFIFO.first));
                $fdisplay(stderr, "[INFO] uncached mem response: ", fshow(resp));
            end
            if (!resp.write) begin
                let result = resp.data;
                let extend = reqInfo.isUnsigned ? zeroExtend : signExtend;
                result = (case (reqInfo.size)
                        B: extend(result[7:0]);
                        H: extend(result[15:0]);
                        W: extend(result[31:0]);
`ifdef CONFIG_RV64
                        D: result[63:0];
`endif
                    endcase);
                respEhr[0] <= tagged Valid result;
            end
            bookkeepingFIFO.deq;
        end else begin
            $fdisplay(stderr, "[ERROR] Unexpected uncached memory responses.");
            $fdisplay(stderr, "[INFO] uncached mem response: ", fshow(resp));
        end
    endrule

    interface Put request;
        method Action put(RVDMemReq r);
            uncachedMem.request.put( UncachedMemReq {
                    write: r.op == tagged Mem St,
                    size: r.size,
                    addr: r.addr,
                    data: r.data
                } );
            bookkeepingFIFO.enq(PendingUncachedReqInfo {isWrite: r.op == tagged Mem St, size: r.size, isUnsigned: r.isUnsigned});
        endmethod
    endinterface
    interface Get response;
        method ActionValue#(RVDMemResp) get if (respEhr[1] matches tagged Valid .resp);
            respEhr[1] <= tagged Invalid;
            return resp;
        endmethod
    endinterface
endmodule

// Memory bus takes in a vector of Servers and produces two server interfaces,
// one for a processor to attach to and one for an external interface to use.
// The processor port has priority, but if the external interface is preempted
// it will have the higher priority in the next cycle.
interface MemoryBus#(type reqT, type respT);
    interface Server#(reqT, respT) procIfc;
    interface Server#(reqT, respT) extIfc;
endinterface

typedef struct {
    Bool isExt;
    Bit#(TLog#(n)) server;
    Bool getsResp;
} MemoryBusPendingReq#(numeric type n) deriving (Bits, Eq, FShow);

module mkMemoryBus#(function Bit#(TLog#(n)) whichServer(reqT x), function Bool getsResponse(reqT x), Vector#(n, Server#(reqT, respT)) servers)(MemoryBus#(reqT, respT)) provisos (Bits#(reqT, reqTsz), Bits#(respT, respTsz));
    // ordering:
    // response < forwardResponse < request < forwardRequest

    Ehr#(4, Maybe#(reqT)) procReqEhr <- mkEhr(tagged Invalid);
    Ehr#(4, Maybe#(reqT)) extReqEhr <- mkEhr(tagged Invalid);

    Ehr#(4, Maybe#(MemoryBusPendingReq#(n))) pendingReq <- mkEhr(tagged Invalid);
    Ehr#(4, Maybe#(respT)) readyResp <- mkEhr(tagged Invalid);

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

    // request priority:
    // old procIfc.request
    // old extIfc.request
    // current procIfc.request
    // current extIfc.request

    rule forwardRequest if (!isValid(pendingReq[3])
                            && (isValid(procReqEhr[3])
                                || isValid(extReqEhr[3])));
        Bool isExt = False;
        Bit#(TLog#(n)) serverIndx = 0;

        if (procReqEhr[3] matches tagged Valid .req &&& !extReqWasPreempted) begin
            procReqEhr[3] <= tagged Invalid;
            serverIndx = whichServer(req);
            servers[serverIndx].request.put(req);
            pendingReq[3] <= tagged Valid MemoryBusPendingReq { isExt: False, server: serverIndx, getsResp: getsResponse(req) };

            if (!isValid(extReqEhr[3])) begin
                extReqWasPreempted <= False;
            end else begin
                extReqWasPreempted <= True;
            end
        end else if (extReqEhr[3] matches tagged Valid .req) begin
            extReqEhr[3] <= tagged Invalid;
            serverIndx = whichServer(req);
            servers[serverIndx].request.put(req);
            pendingReq[3] <= tagged Valid MemoryBusPendingReq { isExt: True, server: serverIndx, getsResp: getsResponse(req) };

            extReqWasPreempted <= False;
        end else begin
            // Due to the guard of this rule, the only way this can happen is
            // if extWasPreempted is true but extReqEhr was invalid.
            $fdisplay(stderr, "[ERROR] mkMemoryBus: extReqWasPreempted == True, but there is no valid extReq");
            extReqWasPreempted <= False;
        end
    endrule

    rule forwardResponse if (pendingReq[0] matches tagged Valid .req
                                &&& !isValid(readyResp[0]));
        // get response
        let resp <- servers[req.server].response.get();
        if (req.getsResp) begin
            readyResp[0] <= tagged Valid resp;
        end else begin
            pendingReq[0] <= tagged Invalid;
        end
    endrule

    // Internal interface
    interface Server procIfc;
        interface Put request;
            method Action put(reqT r) if (!isValid(procReqEhr[2]));
                procReqEhr[2] <= tagged Valid r;
            endmethod
        endinterface
        interface Get response;
            method ActionValue#(respT) get if (pendingReq[1] matches tagged Valid .req
                                                    &&& readyResp[1] matches tagged Valid .resp
                                                    &&& req.isExt == False);
                pendingReq[1] <= tagged Invalid;
                readyResp[1] <= tagged Invalid;
                return resp;
            endmethod
        endinterface
    endinterface

    // External interface
    interface Server extIfc;
        interface Put request;
            method Action put(reqT r) if (!isValid(extReqEhr[2]));
                extReqEhr[2] <= tagged Valid r;
            endmethod
        endinterface
        interface Get response;
            method ActionValue#(respT) get if (pendingReq[1] matches tagged Valid .req
                                                    &&& readyResp[1] matches tagged Valid .resp
                                                    &&& req.isExt == True);
                pendingReq[1] <= tagged Invalid;
                readyResp[1] <= tagged Invalid;
                return resp;
            endmethod
        endinterface
    endinterface
endmodule

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

`include "ProcConfig.bsv"

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

import Ehr::*;

import Abstraction::*;
import RVAmo::*;
import RVExec::*; // for scatterStore and gatherLoad
import RVTypes::*;

// Dummy DCache
// This module has no internal storage. It just takes care of converting
// between RISC-V memory requests and main memory requests.
module mkDummyRVDCache#(GenericMemServer#(DataSz) mainMemory)(Server#(RVDMemReq, RVDMemResp));
    FIFOF#(RVDMemReq) procMemReq <- mkFIFOF;
    FIFOF#(RVDMemResp) procMemResp <- mkFIFOF;

    // True if this dummy cache is currently handling the "modify-write" part
    // of a read-modify-write operation.
    // NOTE: normal stores with byte enables count as RMW operations.
    Reg#(Bool) rmw <- mkReg(False);

    // Forces this dummy cache to wait for writes to finish before handling new reads
    Reg#(Bool) writePending <- mkReg(False);
    Reg#(Bool) readPending <- mkReg(False);

    FIFOF#(Tuple3#(DataByteSel,RVMemSize,Bool)) outstanding <- mkFIFOF;

    function Bool requiresRMW(RVDMemReq r);
        // The AxiSharedMemoryBridge can't handle byte enables, so we are handling it here
        return ((isStore(r.op) && r.size != D) || isAmo(r.op));
    endfunction

    rule ignoreWriteResps;
        let _x <- mainMemory.response.get();
        when(_x.write == True, noAction);
        writePending <= False;
    endrule

    // hanle request
    rule handleRVDMemReq(!rmw && !writePending && !readPending);
        let r = procMemReq.first;
        // This dummy cache does not support LR/SR instructions
        if (isLoad(r.op) || requiresRMW(r)) begin
            // send read request
            // this handles all store request with size < D
            mainMemory.request.put(GenericMemReq{write: False, byteen: '1, addr: r.addr, data: ?});
            readPending <= True;
        end else if (isStore(r.op)) begin
            // send write request (only when size == D)
            mainMemory.request.put(GenericMemReq{write: True, byteen: '1, addr: r.addr, data: r.data});
            writePending <= True;
            if (r.op == tagged Mem Sc) begin
                // successful
                procMemResp.enq(0);
            end
        end
        if (requiresRMW(r)) begin
            rmw <= True;
        end else begin
            procMemReq.deq;
        end
    endrule

    rule finishRMW(rmw && !writePending);
        let r = procMemReq.first;
        // get read data
        let memResp <- mainMemory.response.get;
        when(memResp.write == False, noAction);
        readPending <= False;
        let currData = memResp.data;
        // amoExec also handles plain stores with byte enables
        // If r.op is not an AMO operation, use Swap to perform stores with byte enables
        let {permutedDataByteEn, permutedStData} = scatterStore(truncate(r.addr), r.size, r.data);
        let newData = amoExec(r.op matches tagged Amo .amoFunc ? amoFunc : Swap, permutedDataByteEn, currData, permutedStData);
        mainMemory.request.put(GenericMemReq{write: True, byteen: '1, addr: r.addr, data: newData});
        writePending <= True;
        if (r.op == tagged Mem Sc) begin
            // Successful
            procMemResp.enq(0);
        end else if (r.op matches tagged Amo .*) begin
            procMemResp.enq(currData);
        end
        // finish
        rmw <= False;
        procMemReq.deq;
    endrule

    rule handleRVDMemResp(!rmw);
        let memResp <- mainMemory.response.get;
        when(memResp.write == False, noAction);
        procMemResp.enq(memResp.data);
        readPending <= False;
    endrule

    interface Put request;
        method Action put(RVDMemReq r);
            if (isLoad(r.op) || isAmo(r.op)) begin
                DataByteSel addrByteSel = truncate(r.addr);
                outstanding.enq(tuple3(addrByteSel, r.size, r.isUnsigned));
            end else if (r.op == tagged Mem Sc) begin
                outstanding.enq(tuple3(0, B, False));
            end
            procMemReq.enq(r);
        endmethod
    endinterface
    interface Get response;
        method ActionValue#(RVDMemResp) get;
            let {addrByteSel, size, isUnsigned} = outstanding.first;
            outstanding.deq;
            procMemResp.deq;
            return gatherLoad(addrByteSel, size, isUnsigned, procMemResp.first);
        endmethod
    endinterface
endmodule

module mkRVIMemWrapper#(Server#(RVDMemReq, RVDMemResp) dMem)(Server#(RVIMemReq, RVIMemResp));
    interface Put request;
        method Action put(RVIMemReq req);
            let dReq = RVDMemReq {
                    op: tagged Mem Ld,
                    size: W,
                    isUnsigned: False,
                    addr: req,
                    data: 0 };
            dMem.request.put(dReq);
        endmethod
    endinterface
    interface Get response;
        method ActionValue#(RVIMemResp) get;
            let resp <- dMem.response.get;
            return truncate(resp);
        endmethod
    endinterface
endmodule

module mkDummyRVICache#(GenericMemServer#(DataSz) mainMemory)(Server#(RVIMemReq, RVIMemResp));
    let _dMem <- mkDummyRVDCache(mainMemory);
    let _iMem <- mkRVIMemWrapper(_dMem);
    return _iMem;
endmodule

interface RVDMMU;
    interface Put#(RVDMMUReq) request;
    interface Get#(RVDMMUResp) response;
    method Action updateVMInfo(VMInfo vm);
endinterface

interface RVIMMU;
    interface Put#(RVIMMUReq) request;
    interface Get#(RVIMMUResp) response;
    method Action updateVMInfo(VMInfo vm);
endinterface

`ifndef CONFIG_RV32
// XXX: No RV32 support for MMU's yet

// This does not support any paged virtual memory modes
// TODO: add support for getPMA
module mkBasicDummyRVDMMU#(Bool isInst, GenericMemServer#(DataSz) mainMemory)(RVDMMU);
    FIFOF#(RVDMMUReq) procMMUReq <- mkFIFOF;
    FIFOF#(RVDMMUResp) procMMUResp <- mkFIFOF;

    // TODO: This should be defaultValue
    Reg#(VMInfo) vmInfo <- mkReg(VMInfo{prv:2'b11, asid:0, vm:0, mxr: False, pum: False, base:0, bound:'1});

    rule doTranslate;
        // TODO: add misaligned address exceptions

        // let {addr: .vaddr, op: .op} = procMMUReq.first;
        let vaddr = procMMUReq.first.addr;
        let op = procMMUReq.first.op;
        procMMUReq.deq;

        Bool isStore = getsWritePermission(op); // XXX: was isStore(r.op) || isAmo(r.op) || (r.op == tagged Mem PrefetchForSt);
        Bool isSupervisor = vmInfo.prv == prvS;

        PAddr paddr = 0;
        Maybe#(ExceptionCause) exception = tagged Invalid;
        Maybe#(ExceptionCause) accessFault = tagged Valid (isInst ? InstAccessFault :
                                                            (isStore ? StoreAccessFault :
                                                                LoadAccessFault));

        if (vmInfo.prv == prvM) begin
            paddr = vaddr;
        end else begin
            case (vmInfo.vm)
                vmMbare:        paddr = vaddr;
                vmMbb, vmMbbid: begin
                                    if (vaddr >= vmInfo.bound) begin
                                        exception = accessFault;
                                    end else begin
                                        paddr = vaddr + vmInfo.base;
                                    end
                                end
                default:
                    // unsupported virtual addressing mode
                    exception = accessFault;
            endcase
        end

        procMMUResp.enq(RVDMMUResp{addr: paddr, exception: exception});
    endrule

    interface Put request;
        method Action put(RVDMMUReq r);
            procMMUReq.enq(r);
        endmethod
    endinterface
    interface Get response;
        method ActionValue#(RVDMMUResp) get;
            procMMUResp.deq;
            return procMMUResp.first;
        endmethod
    endinterface
    method Action updateVMInfo(VMInfo vm);
        vmInfo <= vm;
    endmethod
endmodule

module mkRVIMMUWrapper#(RVDMMU dMMU)(RVIMMU);
    interface Put request;
        method Action put(RVIMMUReq req);
            dMMU.request.put(RVDMMUReq{addr: req, size: W, op: Ld}); // XXX: Should this type include AMO instructions too?
        endmethod
    endinterface
    interface Get response;
        method ActionValue#(RVIMMUResp) get;
            let resp <- dMMU.response.get;
            return resp;
        endmethod
    endinterface
    method Action updateVMInfo(VMInfo vm);
        dMMU.updateVMInfo(vm);
    endmethod
endmodule

module mkDummyRVIMMU#(function PMA getPMA(PAddr addr), GenericMemServer#(DataSz) mainMemory)(RVIMMU);
    let _dMMU <- mkDummyRVDMMU(True, getPMA, mainMemory);
    let _iMMU <- mkRVIMMUWrapper(_dMMU);
    return _iMMU;
endmodule

module mkDummyRVDMMU#(Bool isInst, function PMA getPMA(PAddr addr), GenericMemServer#(DataSz) mainMemory)(RVDMMU);
    FIFOF#(RVDMMUReq) procMMUReq <- mkFIFOF;
    FIFOF#(RVDMMUResp) procMMUResp <- mkFIFOF;

    // TODO: This should be defaultValue
    Reg#(VMInfo) vmInfo <- mkReg(VMInfo{prv:2'b11, asid:0, vm:0, mxr: False, pum: False, base:0, bound:'1});

    // Registers for hardware pagetable walk
    Reg#(Bool) walking <- mkReg(False);
    Reg#(Bool) store <- mkReg(False);
    Reg#(Bool) supervisor <- mkReg(False);
    Reg#(PAddr) a <- mkReg(0);
    Reg#(Bit#(2)) i <- mkReg(0);
    Reg#(Bit#(64)) pte <- mkReg(0);
    Reg#(Vector#(3,Bit#(9))) vpn <- mkReg(replicate(0));
    Reg#(Bit#(12)) pageoffset <- mkReg(0);

    // if the response from main memory is for a write, drop it
    // XXX: this does not work if mainMemory has a synthesize boundary
    rule ignoreWriteResps;
        let _x <- mainMemory.response.get();
        when(_x.write == True, noAction);
    endrule

    rule doPageTableWalk(walking);
        let memResp <- mainMemory.response.get();
        when(memResp.write == False, noAction);

        PTE_Sv39 pte = unpack(memResp.data);
        Maybe#(ExceptionCause) accessFault = tagged Valid (isInst ? InstAccessFault :
                                                            (store ? StoreAccessFault :
                                                                LoadAccessFault));
        Bool leafPTE = isLeafPTE(pte);
        Bool legalPTE = isLegalPTE(pte);
        if ((i == 0) && !leafPTE) begin
            // non-leaf PTEs at the lowest level of the page table are treated
            // as illegal PTEs
            legalPTE = False;
        end

        if (!legalPTE) begin
            // illegal pte, access fault
            procMMUResp.enq(RVDMMUResp{addr: 0, exception: accessFault});
            walking <= False;
        end else if (leafPTE) begin
            // valid leaf page

            // translated physical address
            PAddr paddr = '1;
            if (i == 0) begin
                paddr = {0, pte.ppn2, pte.ppn1, pte.ppn0, pageoffset};
            end else if (i == 1) begin
                paddr = {0, pte.ppn2, pte.ppn1, vpn[0], pageoffset};
            end else begin // if (i == 2)
                paddr = {0, pte.ppn2, vpn[1], vpn[0], pageoffset};
            end

            // check page permissions
            Bool hasPermission = False;
            if (isInst) begin
                hasPermission = pte.x;
            end else if (store) begin
                hasPermission = pte.w;
            end else begin
                hasPermission = pte.r || (vmInfo.mxr && pte.x);
            end
            // supervisor can't access user pages if pum is set
            if (supervisor && vmInfo.pum && pte.u) begin
                hasPermission = False;
            end
            // user needs pte.u bit set
            if (!supervisor && !pte.u) begin
                hasPermission = False;
            end

            // check PMA permissions
            PMA pma = getPMA(paddr);
            if (pma == IORom) begin
                // no W permission
                if (store) begin
                    hasPermission = False;
                end
            end else if (pma == IODevice) begin
                // no AMO permission
                // TODO: change type of MMU request to include sufficient AMO
                // information (AMO Class) and check permission accordingly
                // no X permission
                if (isInst) begin
                    hasPermission = False;
                end
            end else if (pma == IOEmpty) begin
                // no R, W, or X permission
                hasPermission = False;
            end

            if (!hasPermission) begin
                // illegal, access fault
                procMMUResp.enq(RVDMMUResp{addr: 0, exception: accessFault});
                walking <= False;
            end else begin
                // legal, return translation
                procMMUResp.enq(RVDMMUResp{addr: paddr, exception: tagged Invalid});
                walking <= False;
                if (!pte.a || (store && !pte.d)) begin
                    // write back necessary
                    // update pte
                    pte.a = True;
                    if (store) begin
                        pte.d = True;
                    end
                    // send write request
                    mainMemory.request.put(GenericMemReq{write: True, byteen: '1, addr: a, data: pack(pte)});
                end
            end
        end else begin
            // go to next level
            PAddr newA = {0, pte.ppn2, pte.ppn1, pte.ppn0, 12'b0} | (zeroExtend(vpn[i-1]) << 3);
            mainMemory.request.put(GenericMemReq{write: False, byteen: '1, addr: newA, data: ?});
            a <= newA;
            i <= i - 1;
        end
    endrule

    rule doTranslate(!walking);
        // let {addr: .vaddr, op: .op} = procMMUReq.first;
        let vaddr = procMMUReq.first.addr;
        let size = procMMUReq.first.size;
        let op = procMMUReq.first.op;
        procMMUReq.deq;

        Bool isStore = (op == St || op == Sc || op == PrefetchForSt);
        Bool isSupervisor = vmInfo.prv == prvS;
        Bool pageTableWalk = False;

        PAddr paddr = 0;
        Maybe#(ExceptionCause) exception = tagged Invalid;
        Maybe#(ExceptionCause) accessFault = tagged Valid (isInst ? InstAccessFault :
                                                            (isStore ? StoreAccessFault :
                                                                LoadAccessFault));
        Maybe#(ExceptionCause) misalignedFault = tagged Valid (isInst ? InstAddrMisaligned :
                                                            (isStore ? StoreAddrMisaligned :
                                                                LoadAddrMisaligned));
        Bit#(3) alignmentBits = (case(size)
                D:       3'b111;
                W:       3'b011;
                H:       3'b001;
                B:       3'b000;
                default: 3'b111;
            endcase);
        if ((truncate(vaddr) & alignmentBits) != 0) begin
            exception = misalignedFault;
        end else if (vmInfo.prv == prvM) begin
            paddr = vaddr;
        end else begin
            case (vmInfo.vm)
                vmMbare:        paddr = vaddr;
                vmMbb, vmMbbid: begin
                                    if (vaddr >= vmInfo.bound) begin
                                        exception = accessFault;
                                    end else begin
                                        paddr = vaddr + vmInfo.base;
                                    end
                                end
                vmSv39: begin
                            // start page table walk
                            // PAddr newA = vmInfo.base + (zeroExtend(vpn[2]) << 3);
                            Vector#(3, Bit#(9)) newvpn = unpack(vaddr[38:12]);
                            PAddr newA = vmInfo.base + (zeroExtend(newvpn[2]) << 3);
                            mainMemory.request.put(GenericMemReq{write: False, byteen: '1, addr: newA, data: ?});
                            walking <= True;
                            pageTableWalk = True;
                            a <= newA;
                            i <= 2;
                            vpn <= newvpn;
                            pageoffset <= truncate(vaddr);
                            store <= isStore;
                            supervisor <= isSupervisor;
                        end
                default:
                    // unsupported virtual addressing mode
                    exception = accessFault;
            endcase
        end

        if (!pageTableWalk) begin
            procMMUResp.enq(RVDMMUResp{addr: paddr, exception: exception});
        end
    endrule

    interface Put request;
        method Action put(RVDMMUReq r);
            procMMUReq.enq(r);
        endmethod
    endinterface
    interface Get response;
        method ActionValue#(RVDMMUResp) get;
            procMMUResp.deq;
            return procMMUResp.first;
        endmethod
    endinterface
    method Action updateVMInfo(VMInfo vm);
        vmInfo <= vm;
    endmethod
endmodule

`endif

// type used in the module below
typedef struct {
    Bool isWrite;
    RVMemSize size;
    Bool isUnsigned;
} PendingUncachedReqInfo deriving (Bits, Eq, FShow);
// probably should be called mkUncachedBridge
module mkUncachedConverter#(UncachedMemServer uncachedMem)(Server#(RVDMemReq, RVDMemResp));
    // this assumes there are no RMW operations to uncached memory
    FIFOF#(PendingUncachedReqInfo) bookkeepingFIFO <- mkFIFOF;

    rule ignoreWriteResponses(bookkeepingFIFO.first.isWrite);
        let resp <- uncachedMem.response.get;
        if (!resp.write) begin
            $fdisplay(stderr, "[ERROR] Uncached memory responses out of sync. Expected a write response but got a read response.");
            $fdisplay(stderr, "[INFO] top of bookkeeping FIFO: ", fshow(bookkeepingFIFO.first));
            $fdisplay(stderr, "[INFO] uncached mem response: ", fshow(resp));
        end
        bookkeepingFIFO.deq;
    endrule

    rule dequeueUnexpectedResponse(!bookkeepingFIFO.notEmpty);
        // empty bookkeepingFIFO, but there is a response ready
        let resp <- uncachedMem.response.get;
        $fdisplay(stderr, "[ERROR] Unexpected uncached memory responses.");
        $fdisplay(stderr, "[INFO] uncached mem response: ", fshow(resp));
    endrule

    interface Put request;
        method Action put(RVDMemReq r);
            uncachedMem.request.put( UncachedMemReq {
                    write: r.op == tagged Mem St,
                    size: r.size,
                    addr: r.addr,
                    data: r.data
                } );
            bookkeepingFIFO.enq(PendingUncachedReqInfo {isWrite: r.op == tagged Mem St, size: r.size, isUnsigned: r.isUnsigned});
        endmethod
    endinterface
    interface Get response;
        method ActionValue#(RVDMemResp) get if (!bookkeepingFIFO.first.isWrite);
            let reqInfo = bookkeepingFIFO.first;
            bookkeepingFIFO.deq;
            let uncachedResp <- uncachedMem.response.get;
            if (uncachedResp.write) begin
                $fdisplay(stderr, "[ERROR] Uncached memory responses out of sync. Expected a read response but got a write response.");
                $fdisplay(stderr, "[INFO] top of bookkeeping FIFO: ", fshow(bookkeepingFIFO.first));
                $fdisplay(stderr, "[INFO] uncached mem response: ", fshow(uncachedResp));
            end
            let result = uncachedResp.data;
            let extend = reqInfo.isUnsigned ? zeroExtend : signExtend;
            result = (case (reqInfo.size)
                    B: extend(result[7:0]);
                    H: extend(result[15:0]);
                    W: extend(result[31:0]);
`ifdef CONFIG_RV64
                    D: result[63:0];
`endif
                endcase);
            return result;
        endmethod
    endinterface
endmodule

// Memory bus takes in a vector of Servers and produces two server interfaces,
// one for a processor to attach to and one for an external interface to use.
// The processor port has priority, but if the external interface is preempted
// it will have the higher priority in the next cycle.
interface MemoryBus#(type reqT, type respT);
    interface Server#(reqT, respT) procIfc;
    interface Server#(reqT, respT) extIfc;
endinterface

typedef struct {
    Bool isExt;
    Bit#(TLog#(n)) server;
    Bool getsResp;
} MemoryBusPendingReq#(numeric type n) deriving (Bits, Eq, FShow);

module mkMemoryBus#(function Bit#(TLog#(n)) whichServer(reqT x), function Bool getsResponse(reqT x), Vector#(n, Server#(reqT, respT)) servers)(MemoryBus#(reqT, respT)) provisos (Bits#(reqT, reqTsz), Bits#(respT, respTsz));
    // ordering:
    // response < forwardResponse < request < forwardRequest

    Ehr#(4, Maybe#(reqT)) procReqEhr <- mkEhr(tagged Invalid);
    Ehr#(4, Maybe#(reqT)) extReqEhr <- mkEhr(tagged Invalid);

    Ehr#(4, Maybe#(MemoryBusPendingReq#(n))) pendingReq <- mkEhr(tagged Invalid);
    Ehr#(4, Maybe#(respT)) readyResp <- mkEhr(tagged Invalid);

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

    // request priority:
    // old procIfc.request
    // old extIfc.request
    // current procIfc.request
    // current extIfc.request

    rule forwardRequest if (!isValid(pendingReq[3])
                            && (isValid(procReqEhr[3])
                                || isValid(extReqEhr[3])));
        Bool isExt = False;
        Bit#(TLog#(n)) serverIndx = 0;

        if (procReqEhr[3] matches tagged Valid .req &&& !extReqWasPreempted) begin
            procReqEhr[3] <= tagged Invalid;
            serverIndx = whichServer(req);
            servers[serverIndx].request.put(req);
            pendingReq[3] <= tagged Valid MemoryBusPendingReq { isExt: False, server: serverIndx, getsResp: getsResponse(req) };

            if (!isValid(extReqEhr[3])) begin
                extReqWasPreempted <= False;
            end else begin
                extReqWasPreempted <= True;
            end
        end else if (extReqEhr[3] matches tagged Valid .req) begin
            extReqEhr[3] <= tagged Invalid;
            serverIndx = whichServer(req);
            servers[serverIndx].request.put(req);
            pendingReq[3] <= tagged Valid MemoryBusPendingReq { isExt: True, server: serverIndx, getsResp: getsResponse(req) };

            extReqWasPreempted <= False;
        end else begin
            // Due to the guard of this rule, the only way this can happen is
            // if extWasPreempted is true but extReqEhr was invalid.
            $fdisplay(stderr, "[ERROR] mkMemoryBus: extReqWasPreempted == True, but there is no valid extReq");
            extReqWasPreempted <= False;
        end
    endrule

    rule forwardResponse if (pendingReq[0] matches tagged Valid .req
                                &&& !isValid(readyResp[0]));
        // get response
        let resp <- servers[req.server].response.get();
        if (req.getsResp) begin
            readyResp[0] <= tagged Valid resp;
        end else begin
            pendingReq[0] <= tagged Invalid;
        end
    endrule

    // Internal interface
    interface Server procIfc;
        interface Put request;
            method Action put(reqT r) if (!isValid(procReqEhr[2]));
                procReqEhr[2] <= tagged Valid r;
            endmethod
        endinterface
        interface Get response;
            method ActionValue#(respT) get if (pendingReq[1] matches tagged Valid .req
                                                    &&& readyResp[1] matches tagged Valid .resp
                                                    &&& req.isExt == False);
                pendingReq[1] <= tagged Invalid;
                readyResp[1] <= tagged Invalid;
                return resp;
            endmethod
        endinterface
    endinterface

    // External interface
    interface Server extIfc;
        interface Put request;
            method Action put(reqT r) if (!isValid(extReqEhr[2]));
                extReqEhr[2] <= tagged Valid r;
            endmethod
        endinterface
        interface Get response;
            method ActionValue#(respT) get if (pendingReq[1] matches tagged Valid .req
                                                    &&& readyResp[1] matches tagged Valid .resp
                                                    &&& req.isExt == True);
                pendingReq[1] <= tagged Invalid;
                readyResp[1] <= tagged Invalid;
                return resp;
            endmethod
        endinterface
    endinterface
endmodule


added dummy module for Bscan/BSIM

added dummy module for Bscan/BSIM

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

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

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

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

import FIFOF::*;
import Clocks::*;
import BRAM::*;
import BscanE2::*;
import GetPut::*;
import XilinxCells::*;
import SyncBits::*;

// From: http://siliconexposed.blogspot.com/2013/10/soc-framework-part-5.html
// Example usage: http://www.pld.ttu.ee/~vadim/tty/IAY0570/video_pipeline/psram_app/program_rom.v
// Example usage: http://ohm.bu.edu/~dean/G-2TrackerWORKING/uart_test.vhd

interface BscanTop;
   interface Reset    rst;
   interface Clock    tck;
   method Bit#(1)     reset();
   method Bit#(1)     capture();
   method Bit#(1)     shift();
   method Bit#(1)     tdi();
   method Action      tdo(Bit#(1) v);
   method Bit#(1)     update();
   method Bool        first();
endinterface
`ifdef BSIM
module mkBscanTop#(Integer bus)(BscanTop);
   Clock defaultClock <- exposeCurrentClock();
   Reset defaultReset <- exposeCurrentReset();
   interface tck = defaultClock;
   interface rst = defaultReset;
   method tdi;
       return 0;
   endmethod
   method tdo;
       return 0;
   endmethod
   method capture;
       return 0;
   endmethod
   method shift;
       return 0;
   endmethod
   method update;
       return 0;
   endmethod
   method first();
      return 0;
   endmethod
endmodule
`else
module mkBscanTop#(Integer bus)(BscanTop);
   Clock defaultClock <- exposeCurrentClock();
   Reset defaultReset <- exposeCurrentReset();
   BscanE2 bscan <- mkBscanE2(bus);
   Clock mytck <- mkClockBUFG(clocked_by bscan.tck);
   Reset myrst <- mkAsyncReset(2, defaultReset, mytck);
   SyncBitIfc#(Bool) selected <- mkSyncBits(False, mytck, myrst, defaultClock, defaultReset);
   Reg#(Bool) selectdelay <- mkReg(False);

   rule updater;
       selected.send(bscan.sel() == 1);
   endrule
   rule writed;
       selectdelay <= selected.read();
   endrule

   interface tck = mytck;
   interface rst = myrst;
   method tdi = bscan.tdi;
   method tdo = bscan.tdo;
   method capture;
       return bscan.sel & bscan.capture;
   endmethod
   method shift;
       return bscan.sel & bscan.shift;
   endmethod
   method update;
       return bscan.sel & bscan.update;
   endmethod
   method first();
      return selected.read() && !selectdelay;
   endmethod
endmodule
`endif

interface BscanLocal;
   method Action tdo1(Bit#(1) v);
   method Action tdo2(Bit#(1) v);
endinterface

module mkBscanLocal#(BscanTop bscan)(BscanLocal);
   Wire#(Bit#(1)) tdo_wire1 <- mkDWire(0);
   Wire#(Bit#(1)) tdo_wire2 <- mkDWire(0);

   rule tdo_rule;
       bscan.tdo(tdo_wire1 | tdo_wire2);
   endrule

   method Action tdo1(Bit#(1) v);
       tdo_wire1 <= v;
   endmethod
   method Action tdo2(Bit#(1) v);
       tdo_wire2 <= v;
   endmethod
endmodule

interface BscanBram#(type atype, type dtype);
   interface BRAMClient#(atype, dtype) bramClient;
   method Bit#(1) data_out();
endinterface

module mkBscanBram#(Integer id, atype addr, BscanTop bscan)(BscanBram#(atype, dtype))
   provisos (Bits#(atype, asz), Bits#(dtype,dsz), Add#(1, a__, dsz));
   let asz = valueOf(asz);
   let dsz = valueOf(dsz);

   Clock defaultClock <- exposeCurrentClock();
   Reset defaultReset <- exposeCurrentReset();

   Reg#(Bit#(asz)) addrReg <- mkReg(0);
   Reg#(Bool) readData <- mkReg(False);
   Reg#(Bool) firstItem <- mkReg(False);
   Reg#(Bool) selected <- mkReg(False);
   SyncBitIfc#(Bool) selectedj <- mkSyncBits(False, defaultClock, defaultReset, bscan.tck, bscan.rst);

   Reg#(Bit#(dsz)) shiftReg <- mkReg(0, clocked_by bscan.tck, reset_by bscan.rst);
   SyncBitIfc#(Bit#(dsz)) tojtag <- mkSyncBits(0, defaultClock, defaultReset, bscan.tck, bscan.rst);
   SyncBitIfc#(Bit#(dsz)) fromjtag <- mkSyncBits(0, bscan.tck, bscan.rst, defaultClock, defaultReset);
   SyncPulseIfc startWrite <- mkSyncHandshake(bscan.tck, bscan.rst, defaultClock);

   rule captureRule if(bscan.capture() == 1);
       shiftReg <= tojtag.read();
   endrule
   rule shiftRule if (bscan.shift() == 1);
       shiftReg <= { bscan.tdi(), shiftReg[dsz-1:1] };
   endrule
   rule updateRule if(bscan.update() == 1);
       startWrite.send();
       fromjtag.send(shiftReg);
   endrule

   rule firstRule if (bscan.first());
       firstItem <= True;
       selected <= False;
       selectedj.send(False);
       tojtag.send(0);
   endrule
   rule addrRule if (startWrite.pulse());
       let v = fromInteger(0);  // first time USER1, reset address
       if (firstItem) begin
           selected <= fromjtag.read() == fromInteger(id);
           selectedj.send(fromjtag.read() == fromInteger(id));
       end
       else
           v = addrReg + 1;
       addrReg <= v;
       firstItem <= False;
   endrule
   rule readdataRule;
       readData <= startWrite.pulse();
   endrule

   interface BRAMClient bramClient;
      interface Get request;
	 method ActionValue#(BRAMRequest#(atype,dtype)) get() if ((selected && startWrite.pulse()) || readData);
            return BRAMRequest {write:!readData, responseOnWrite:False, address:unpack(addrReg), datain:unpack(fromjtag.read())};
	 endmethod
      endinterface
      interface Put response;
	 method Action put(dtype d);
            tojtag.send(pack(d));
	 endmethod
      endinterface
   endinterface
   method Bit#(1) data_out();
      // the output lines are all OR'ed together when going back to the BSCAN core
      if (selectedj.read())
          return shiftReg[0];
      else
          return 0;
   endmethod
endmodule

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

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

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

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

import FIFOF::*;
import Clocks::*;
import BRAM::*;
import BscanE2::*;
import GetPut::*;
import XilinxCells::*;
import SyncBits::*;

// From: http://siliconexposed.blogspot.com/2013/10/soc-framework-part-5.html
// Example usage: http://www.pld.ttu.ee/~vadim/tty/IAY0570/video_pipeline/psram_app/program_rom.v
// Example usage: http://ohm.bu.edu/~dean/G-2TrackerWORKING/uart_test.vhd

interface BscanTop;
   interface Reset    rst;
   interface Clock    tck;
   method Bit#(1)     reset();
   method Bit#(1)     capture();
   method Bit#(1)     shift();
   method Bit#(1)     tdi();
   method Action      tdo(Bit#(1) v);
   method Bit#(1)     update();
   method Bool        first();
endinterface
module mkBscanTop#(Integer bus)(BscanTop);
   Clock defaultClock <- exposeCurrentClock();
   Reset defaultReset <- exposeCurrentReset();
   BscanE2 bscan <- mkBscanE2(bus);
   Clock mytck <- mkClockBUFG(clocked_by bscan.tck);
   Reset myrst <- mkAsyncReset(2, defaultReset, mytck);
   SyncBitIfc#(Bool) selected <- mkSyncBits(False, mytck, myrst, defaultClock, defaultReset);
   Reg#(Bool) selectdelay <- mkReg(False);

   rule updater;
       selected.send(bscan.sel() == 1);
   endrule
   rule writed;
       selectdelay <= selected.read();
   endrule

   interface tck = mytck;
   interface rst = myrst;
   method tdi = bscan.tdi;
   method tdo = bscan.tdo;
   method capture;
       return bscan.sel & bscan.capture;
   endmethod
   method shift;
       return bscan.sel & bscan.shift;
   endmethod
   method update;
       return bscan.sel & bscan.update;
   endmethod
   method first();
      return selected.read() && !selectdelay;
   endmethod
endmodule

interface BscanLocal;
   method Action tdo1(Bit#(1) v);
   method Action tdo2(Bit#(1) v);
endinterface

module mkBscanLocal#(BscanTop bscan)(BscanLocal);
   Wire#(Bit#(1)) tdo_wire1 <- mkDWire(0);
   Wire#(Bit#(1)) tdo_wire2 <- mkDWire(0);

   rule tdo_rule;
       bscan.tdo(tdo_wire1 | tdo_wire2);
   endrule

   method Action tdo1(Bit#(1) v);
       tdo_wire1 <= v;
   endmethod
   method Action tdo2(Bit#(1) v);
       tdo_wire2 <= v;
   endmethod
endmodule

interface BscanBram#(type atype, type dtype);
   interface BRAMClient#(atype, dtype) bramClient;
   method Bit#(1) data_out();
endinterface

module mkBscanBram#(Integer id, atype addr, BscanTop bscan)(BscanBram#(atype, dtype))
   provisos (Bits#(atype, asz), Bits#(dtype,dsz), Add#(1, a__, dsz));
   let asz = valueOf(asz);
   let dsz = valueOf(dsz);

   Clock defaultClock <- exposeCurrentClock();
   Reset defaultReset <- exposeCurrentReset();

   Reg#(Bit#(asz)) addrReg <- mkReg(0);
   Reg#(Bool) readData <- mkReg(False);
   Reg#(Bool) firstItem <- mkReg(False);
   Reg#(Bool) selected <- mkReg(False);
   SyncBitIfc#(Bool) selectedj <- mkSyncBits(False, defaultClock, defaultReset, bscan.tck, bscan.rst);

   Reg#(Bit#(dsz)) shiftReg <- mkReg(0, clocked_by bscan.tck, reset_by bscan.rst);
   SyncBitIfc#(Bit#(dsz)) tojtag <- mkSyncBits(0, defaultClock, defaultReset, bscan.tck, bscan.rst);
   SyncBitIfc#(Bit#(dsz)) fromjtag <- mkSyncBits(0, bscan.tck, bscan.rst, defaultClock, defaultReset);
   SyncPulseIfc startWrite <- mkSyncHandshake(bscan.tck, bscan.rst, defaultClock);

   rule captureRule if(bscan.capture() == 1);
       shiftReg <= tojtag.read();
   endrule
   rule shiftRule if (bscan.shift() == 1);
       shiftReg <= { bscan.tdi(), shiftReg[dsz-1:1] };
   endrule
   rule updateRule if(bscan.update() == 1);
       startWrite.send();
       fromjtag.send(shiftReg);
   endrule

   rule firstRule if (bscan.first());
       firstItem <= True;
       selected <= False;
       selectedj.send(False);
       tojtag.send(0);
   endrule
   rule addrRule if (startWrite.pulse());
       let v = fromInteger(0);  // first time USER1, reset address
       if (firstItem) begin
           selected <= fromjtag.read() == fromInteger(id);
           selectedj.send(fromjtag.read() == fromInteger(id));
       end
       else
           v = addrReg + 1;
       addrReg <= v;
       firstItem <= False;
   endrule
   rule readdataRule;
       readData <= startWrite.pulse();
   endrule

   interface BRAMClient bramClient;
      interface Get request;
	 method ActionValue#(BRAMRequest#(atype,dtype)) get() if ((selected && startWrite.pulse()) || readData);
            return BRAMRequest {write:!readData, responseOnWrite:False, address:unpack(addrReg), datain:unpack(fromjtag.read())};
	 endmethod
      endinterface
      interface Put response;
	 method Action put(dtype d);
            tojtag.send(pack(d));
	 endmethod
      endinterface
   endinterface
   method Bit#(1) data_out();
      // the output lines are all OR'ed together when going back to the BSCAN core
      if (selectedj.read())
          return shiftReg[0];
      else
          return 0;
   endmethod
endmodule


use AxiClientServer instead of AxiMasterSlave for mkFifoToAxi and mkFifoFromAxi

// Copyright (c) 2012 Nokia, Inc.

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

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

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

import RegFile::*;
import BRAMFIFO::*;
import FIFOF::*;
import AxiClientServer::*;

interface FifoToAxi#(type busWidth, type busWidthBytes, type idWidth);
   interface Reg#(Bit#(32)) base;
   interface Reg#(Bit#(32)) bounds;
   interface Reg#(Bit#(32)) threshold;
   interface Reg#(Bool) enabled;
   interface Reg#(Bool) oneBeatAddress;
   interface Reg#(Bool) thirtyTwoBitTransfer;
   interface Reg#(Bit#(32)) ptr;
   method Bool notEmpty();
   method Bool notFull();

   method Bit#(32) readStatus(Bit#(12) addr);

   interface Axi3WriteClient#(busWidth,busWidthBytes,idWidth) axi;
   method Action enq(Bit#(busWidth) value);
   method ActionValue#(Bit#(32)) getResponse();
endinterface

interface FifoFromAxi#(type busWidth, type idWidth);
   interface Reg#(Bit#(32)) base;
   interface Reg#(Bit#(32)) bounds;
   interface Reg#(Bit#(32)) threshold;
   interface Reg#(Bool) enabled;
   interface Reg#(Bool) oneBeatAddress;
   interface Reg#(Bool) thirtyTwoBitTransfer;
   interface Reg#(Bit#(32)) ptr;
   method Bool notEmpty();
   method Bool notFull();

   method Bit#(32) readStatus(Bit#(12) addr);

   interface Axi3ReadClient#(busWidth,idWidth) axi;

   method Action deq();
   method Bit#(busWidth) first();
   method ActionValue#(Bit#(32)) getResponse();
endinterface

module mkFifoToAxi(FifoToAxi#(busWidth,busWidthBytes,idWidth)) provisos(Div#(busWidth,8,busWidthBytes),Add#(1,z,busWidth));
   Reg#(Bool) enabledReg <- mkReg(False);
   Reg#(Bool) oneBeatAddressReg <- mkReg(True);
   Reg#(Bool) thirtyTwoBitTransferReg <- mkReg(False);
   Reg#(Bit#(32)) baseReg <- mkReg(0);
   Reg#(Bit#(32)) boundsReg <- mkReg(0);
   Reg#(Bit#(32)) thresholdReg <- mkReg(0);
   Reg#(Bit#(32)) ptrReg <- mkReg(0);
   Reg#(Bit#(32)) addrsBeatCount <- mkReg(0);
   Reg#(Bit#(32)) wordsWrittenCount <- mkReg(0);
   Reg#(Bit#(32)) wordsEnqCount <- mkReg(0);
   Reg#(Bit#(32)) lastDataBeatCount <- mkReg(0);
   FIFOF#(Bit#(busWidth)) dfifo <- mkSizedBRAMFIFOF(8);
   Reg#(Bit#(4)) burstCountReg <- mkReg(0);
   Reg#(Bool) operationInProgress <- mkReg(False);
   Reg#(Bool) addressPresented <- mkReg(False);
   FIFOF#(Bit#(2)) axiBrespFifo <- mkSizedBRAMFIFOF(32);

   rule updateBurstCount if (!dfifo.notFull() && !operationInProgress && enabledReg);
       burstCountReg <= 4'd8;
       operationInProgress <= True;
       addressPresented <= False;
   endrule

   method Bool notEmpty();
       return dfifo.notEmpty;
   endmethod

   method Bool notFull();
       return dfifo.notFull;
   endmethod

   interface Reg base;
       method Action _write(Bit#(32) base) if (!operationInProgress);
          if (!enabledReg) begin
              baseReg <= base;
              ptrReg <= base;
          end
       endmethod
       method Bit#(32) _read();
          return baseReg;
       endmethod
   endinterface

   interface Reg bounds;
       method Action _write(Bit#(32) bounds) if (!operationInProgress);
          if (!enabledReg) begin
              boundsReg <= bounds;
          end
       endmethod
       method Bit#(32) _read();
          return boundsReg;
       endmethod
   endinterface

   interface Reg threshold = thresholdReg;
   interface Reg enabled = enabledReg;
   interface Reg oneBeatAddress = oneBeatAddressReg;
   interface Reg thirtyTwoBitTransfer = thirtyTwoBitTransferReg;

   method Bit#(32) readStatus(Bit#(12) addr);
   Bit#(32) v = 32'h02142042;
   if (addr == 12'h000)
       v =  baseReg;
   else if (addr == 12'h004)
       v = boundsReg;
   else if (addr == 12'h008)
       v = ptrReg;
   else if (addr == 12'h00C)
       v = extend(burstCountReg);
   else if (addr == 12'h010)
       v = enabledReg ? 32'heeeeeeee : 32'hdddddddd;
   else if (addr == 12'h014)
   begin
       v = 0;
       v[3:0] = axiBrespFifo.notEmpty ? 4'h1 : 4'he;
       v[15:12] = axiBrespFifo.notFull ? 4'h0 : 4'hf;
   end
   else if (addr == 12'h018)
   begin
       v = 0;
       v[3:0] = dfifo.notEmpty ? 4'h1 : 4'he;
       v[15:12] = dfifo.notFull ? 4'h0 : 4'hf;
   end
   else if (addr == 12'h01C)
   begin
       v[31:24] = 8'hbb;
       v[23:16] = operationInProgress ? 8'haa : 8'h11;
       v[15:0] = extend(burstCountReg);
   end
   else if (addr == 12'h020)
       v = wordsEnqCount;
   else if (addr == 12'h024)
       v = addrsBeatCount;
   else if (addr == 12'h028)
       v = wordsWrittenCount;
   else if (addr == 12'h02C)
       v = lastDataBeatCount;
   return v;
   endmethod

   interface Axi3WriteClient axi;
       method ActionValue#(Axi3WriteRequest#(idWidth)) address() if (operationInProgress && !addressPresented);
           addrsBeatCount <= addrsBeatCount + 1;
           if (oneBeatAddressReg)
               addressPresented <= True;
           let ptrValue = ptrReg;
           return Axi3WriteRequest { address: ptrValue, burstLen: burstCountReg-1, id: 1};
       endmethod

       method ActionValue#(Axi3WriteData#(busWidth, busWidthBytes, idWidth)) data() if (operationInProgress && dfifo.notEmpty);
           ptrReg <= ptrReg + pack(fromInteger(valueOf(busWidth)/8));
           let bc = burstCountReg;
           if (bc == 4'd1)
           begin
               operationInProgress <= False;
               lastDataBeatCount <= lastDataBeatCount + 1;
           end
           burstCountReg <= bc - 1;
           wordsWrittenCount <= wordsWrittenCount + 1;

           let d = dfifo.first;
           dfifo.deq;
	   let last = (burstCountReg == 4'd1) ? 1'b1 : 1'b0;
           return Axi3WriteData { data: d, byteEnable: maxBound, last: last, id: 1 };
       endmethod

       method Action response(Axi3WriteResponse#(idWidth) resp);
           if (resp.code != 2'b00)
               axiBrespFifo.enq(resp.code);
       endmethod
   endinterface

   method Action enq(Bit#(busWidth) value);
       wordsEnqCount <= wordsEnqCount + 1;
       dfifo.enq(value);
   endmethod
   method ActionValue#(Bit#(32)) getResponse() if (axiBrespFifo.notEmpty);
       axiBrespFifo.deq;
       return extend(axiBrespFifo.first);
   endmethod

endmodule

module mkFifoFromAxi(FifoFromAxi#(busWidth,idWidth)) provisos (Add#(1,a,busWidth));
   Reg#(Bool) enabledReg <- mkReg(False);
   Reg#(Bool) oneBeatAddressReg <- mkReg(True);
   Reg#(Bool) thirtyTwoBitTransferReg <- mkReg(False);
   Reg#(Bit#(32)) baseReg <- mkReg(0);
   Reg#(Bit#(32)) boundsReg <- mkReg(0);
   Reg#(Bit#(32)) thresholdReg <- mkReg(0);
   Reg#(Bit#(32)) ptrReg <- mkReg(0);
   Reg#(Bit#(32)) addrsBeatCount <- mkReg(0);
   Reg#(Bit#(32)) wordsReceivedCount <- mkReg(0);
   Reg#(Bit#(32)) wordsDeqCount <- mkReg(0);
   Reg#(Bit#(32)) lastDataBeatCount <- mkReg(0);
   FIFOF#(Bit#(busWidth)) rfifo <- mkSizedBRAMFIFOF(32);
   Reg#(Bit#(4)) burstCountReg <- mkReg(0);
   Reg#(Bool) operationInProgress <- mkReg(False);
   Reg#(Bool) addressPresented <- mkReg(False);
   FIFOF#(Bit#(2)) axiRrespFifo <- mkSizedBRAMFIFOF(32);

   rule updateBurstCount if (!rfifo.notEmpty && !operationInProgress && enabledReg && ptrReg < boundsReg);
       burstCountReg <= 4'd8;
       operationInProgress <= True;
       addressPresented <= False;
   endrule

   method Bool notEmpty();
   // fixme
       return rfifo.notEmpty;
   endmethod

   method Bool notFull();
   // fixme
       return rfifo.notFull;
   endmethod

   interface Reg base;
       method Action _write(Bit#(32) base) if (!operationInProgress);
          if (!enabledReg) begin
              baseReg <= base;
              ptrReg <= base;
          end
       endmethod
       method Bit#(32) _read();
          return baseReg;
       endmethod
   endinterface

   interface Reg bounds;
       method Action _write(Bit#(32) bounds) if (!operationInProgress);
          if (!enabledReg) begin
              boundsReg <= bounds;
          end
       endmethod
       method Bit#(32) _read();
          return boundsReg;
       endmethod
   endinterface

   interface Reg threshold = thresholdReg;
   interface Reg enabled = enabledReg;
   interface Reg oneBeatAddress = oneBeatAddressReg;
   interface Reg thirtyTwoBitTransfer = thirtyTwoBitTransferReg;

   method Bit#(32) readStatus(Bit#(12) addr);
       Bit#(32) v = 32'h02142042;
       if (addr == 12'h000)
           v =  baseReg;
       else if (addr == 12'h004)
           v = boundsReg;
       else if (addr == 12'h008)
           v = ptrReg;
       else if (addr == 12'h00C)
           v = extend(burstCountReg);
       else if (addr == 12'h010)
           v = enabledReg ? 32'heeeeeeee : 32'hdddddddd;
       else if (addr == 12'h014)
       begin
           v = 0;
           v[3:0] = axiRrespFifo.notEmpty ? 4'h1 : 4'he;
           v[15:12] = axiRrespFifo.notFull ? 4'h0 : 4'hf;
       end
       else if (addr == 12'h018)
       begin
           v = 0;
           v[3:0] = rfifo.notEmpty ? 4'h1 : 4'he;
           v[15:12] = rfifo.notFull ? 4'h0 : 4'hf;
       end
       else if (addr == 12'h01C)
       begin
           v[31:24] = 8'hbb;
           v[23:16] = operationInProgress ? 8'haa : 8'h11;
           v[15:0] = extend(burstCountReg);
       end
       else if (addr == 12'h020)
           v = wordsDeqCount;
       else if (addr == 12'h024)
           v = addrsBeatCount;
       else if (addr == 12'h028)
           v = wordsReceivedCount;
       else if (addr == 12'h02C)
           v = lastDataBeatCount;
       return v;
   endmethod

   interface Axi3ReadClient axi;
       method ActionValue#(Axi3ReadRequest#(idWidth)) address() if (operationInProgress && !addressPresented);
           addrsBeatCount <= addrsBeatCount + 1;
           if (oneBeatAddressReg)
               addressPresented <= True;
           let ptrValue = ptrReg;
           return Axi3ReadRequest { address: ptrReg, burstLen: burstCountReg-1, id: 1 };
       endmethod
       method Action data(Axi3ReadResponse#(busWidth, idWidth) response) if (rfifo.notFull && operationInProgress);
           let bc = burstCountReg - 1;
           if (bc == 1)
               lastDataBeatCount <= lastDataBeatCount + 1;

           if (response.code == 2'b00)
           begin
               burstCountReg <= bc;
               ptrReg <= ptrReg + pack(fromInteger(valueOf(busWidth)/8));
               rfifo.enq(response.data);
               wordsReceivedCount <= wordsReceivedCount + 1;
           end
           else
           begin
               axiRrespFifo.enq(response.code);
           end

           if (response.code != 2'b00 || bc == 0)
               operationInProgress <= False;
       endmethod
   endinterface

   method Action deq();
       wordsDeqCount <= wordsDeqCount + 1;
       rfifo.deq();
   endmethod
   method Bit#(busWidth) first();
       return rfifo.first;
   endmethod

   method ActionValue#(Bit#(32)) getResponse() if (axiRrespFifo.notEmpty);
       axiRrespFifo.deq;
       return extend(axiRrespFifo.first);
   endmethod

endmodule

// Copyright (c) 2012 Nokia, Inc.

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

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

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

import RegFile::*;
import BRAMFIFO::*;
import FIFOF::*;
import AxiMasterSlave::*;

interface FifoToAxi#(type busWidth, type busWidthBytes);
   interface Reg#(Bit#(32)) base;
   interface Reg#(Bit#(32)) bounds;
   interface Reg#(Bit#(32)) threshold;
   interface Reg#(Bool) enabled;
   interface Reg#(Bool) oneBeatAddress;
   interface Reg#(Bool) thirtyTwoBitTransfer;
   interface Reg#(Bit#(32)) ptr;
   method Bool notEmpty();
   method Bool notFull();

   method Bit#(32) readStatus(Bit#(12) addr);

   interface AxiMasterWrite#(busWidth,busWidthBytes) axi;
   method Action enq(Bit#(busWidth) value);
   method ActionValue#(Bit#(32)) getResponse();
endinterface

interface FifoFromAxi#(type busWidth);
   interface Reg#(Bit#(32)) base;
   interface Reg#(Bit#(32)) bounds;
   interface Reg#(Bit#(32)) threshold;
   interface Reg#(Bool) enabled;
   interface Reg#(Bool) oneBeatAddress;
   interface Reg#(Bool) thirtyTwoBitTransfer;
   interface Reg#(Bit#(32)) ptr;
   method Bool notEmpty();
   method Bool notFull();

   method Bit#(32) readStatus(Bit#(12) addr);

   interface AxiMasterRead#(busWidth) axi;

   method Action deq();
   method Bit#(busWidth) first();
   method ActionValue#(Bit#(32)) getResponse();
endinterface

module mkFifoToAxi(FifoToAxi#(busWidth,busWidthBytes)) provisos(Div#(busWidth,8,busWidthBytes),Add#(1,z,busWidth));
   Reg#(Bool) enabledReg <- mkReg(False);
   Reg#(Bool) oneBeatAddressReg <- mkReg(True);
   Reg#(Bool) thirtyTwoBitTransferReg <- mkReg(False);
   Reg#(Bit#(32)) baseReg <- mkReg(0);
   Reg#(Bit#(32)) boundsReg <- mkReg(0);
   Reg#(Bit#(32)) thresholdReg <- mkReg(0);
   Reg#(Bit#(32)) ptrReg <- mkReg(0);
   Reg#(Bit#(32)) addrsBeatCount <- mkReg(0);
   Reg#(Bit#(32)) wordsWrittenCount <- mkReg(0);
   Reg#(Bit#(32)) wordsEnqCount <- mkReg(0);
   Reg#(Bit#(32)) lastDataBeatCount <- mkReg(0);
   FIFOF#(Bit#(busWidth)) dfifo <- mkSizedBRAMFIFOF(8);
   Reg#(Bit#(8)) burstCountReg <- mkReg(0);
   Reg#(Bool) operationInProgress <- mkReg(False);
   Reg#(Bool) addressPresented <- mkReg(False);
   FIFOF#(Bit#(2)) axiBrespFifo <- mkSizedBRAMFIFOF(32);

   rule updateBurstCount if (!dfifo.notFull() && !operationInProgress && enabledReg);
       burstCountReg <= 8'd8;
       operationInProgress <= True;
       addressPresented <= False;
   endrule

   method Bool notEmpty();
       return dfifo.notEmpty;
   endmethod

   method Bool notFull();
       return dfifo.notFull;
   endmethod

   interface Reg base;
       method Action _write(Bit#(32) base) if (!operationInProgress);
          if (!enabledReg) begin
              baseReg <= base;
              ptrReg <= base;
          end
       endmethod
       method Bit#(32) _read();
          return baseReg;
       endmethod
   endinterface

   interface Reg bounds;
       method Action _write(Bit#(32) bounds) if (!operationInProgress);
          if (!enabledReg) begin
              boundsReg <= bounds;
          end
       endmethod
       method Bit#(32) _read();
          return boundsReg;
       endmethod
   endinterface

   interface Reg threshold = thresholdReg;
   interface Reg enabled = enabledReg;
   interface Reg oneBeatAddress = oneBeatAddressReg;
   interface Reg thirtyTwoBitTransfer = thirtyTwoBitTransferReg;

   method Bit#(32) readStatus(Bit#(12) addr);
   Bit#(32) v = 32'h02142042;
   if (addr == 12'h000)
       v =  baseReg;
   else if (addr == 12'h004)
       v = boundsReg;
   else if (addr == 12'h008)
       v = ptrReg;
   else if (addr == 12'h00C)
       v = extend(burstCountReg);
   else if (addr == 12'h010)
       v = enabledReg ? 32'heeeeeeee : 32'hdddddddd;
   else if (addr == 12'h014)
   begin
       v = 0;
       v[3:0] = axiBrespFifo.notEmpty ? 4'h1 : 4'he;
       v[15:12] = axiBrespFifo.notFull ? 4'h0 : 4'hf;
   end
   else if (addr == 12'h018)
   begin
       v = 0;
       v[3:0] = dfifo.notEmpty ? 4'h1 : 4'he;
       v[15:12] = dfifo.notFull ? 4'h0 : 4'hf;
   end
   else if (addr == 12'h01C)
   begin
       v[31:24] = 8'hbb;
       v[23:16] = operationInProgress ? 8'haa : 8'h11;
       v[15:0] = extend(burstCountReg);
   end
   else if (addr == 12'h020)
       v = wordsEnqCount;
   else if (addr == 12'h024)
       v = addrsBeatCount;
   else if (addr == 12'h028)
       v = wordsWrittenCount;
   else if (addr == 12'h02C)
       v = lastDataBeatCount;
   return v;
   endmethod

   interface AxiMasterWrite axi;
       method ActionValue#(Bit#(32)) writeAddr() if (operationInProgress && !addressPresented);
           addrsBeatCount <= addrsBeatCount + 1;
           if (oneBeatAddressReg)
               addressPresented <= True;
           let ptrValue = ptrReg;
           return ptrReg;
       endmethod
       method Bit#(8) writeBurstLen();
           return burstCountReg-1;
       endmethod
       method Bit#(3) writeBurstWidth();
           if (valueOf(busWidth) == 32)
               return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
           else if (thirtyTwoBitTransferReg)
               return 3'b010;
           else
               return 3'b011;
       endmethod
       method Bit#(2) writeBurstType();  // drive with 2'b01 increment address
           return 2'b01; // increment address
       endmethod
       method Bit#(3) writeBurstProt(); // drive with 3'b000
           return 3'b000;
       endmethod
       method Bit#(4) writeBurstCache(); // drive with 4'b0011
           return 4'b0011;
       endmethod

       method ActionValue#(Bit#(busWidth)) writeData() if (operationInProgress && dfifo.notEmpty);
           ptrReg <= ptrReg + pack(fromInteger(valueOf(busWidth)/8));
           let bc = burstCountReg;
           if (bc == 8'd1)
           begin
               operationInProgress <= False;
               lastDataBeatCount <= lastDataBeatCount + 1;
           end
           burstCountReg <= bc - 1;
           wordsWrittenCount <= wordsWrittenCount + 1;

           let d = dfifo.first;
           dfifo.deq;
           return d;
       endmethod
       method Bit#(busWidthBytes) writeDataByteEnable();
           return maxBound;
       endmethod
       method Bit#(1) writeLastDataBeat(); // last data beat
           if (burstCountReg == 8'd1)
               return 1'b1;
           else
               return 1'b0;
       endmethod

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

   method Action enq(Bit#(busWidth) value);
       wordsEnqCount <= wordsEnqCount + 1;
       dfifo.enq(value);
   endmethod
   method ActionValue#(Bit#(32)) getResponse() if (axiBrespFifo.notEmpty);
       axiBrespFifo.deq;
       return extend(axiBrespFifo.first);
   endmethod

endmodule

module mkFifoFromAxi(FifoFromAxi#(busWidth)) provisos (Add#(1,a,busWidth));
   Reg#(Bool) enabledReg <- mkReg(False);
   Reg#(Bool) oneBeatAddressReg <- mkReg(True);
   Reg#(Bool) thirtyTwoBitTransferReg <- mkReg(False);
   Reg#(Bit#(32)) baseReg <- mkReg(0);
   Reg#(Bit#(32)) boundsReg <- mkReg(0);
   Reg#(Bit#(32)) thresholdReg <- mkReg(0);
   Reg#(Bit#(32)) ptrReg <- mkReg(0);
   Reg#(Bit#(32)) addrsBeatCount <- mkReg(0);
   Reg#(Bit#(32)) wordsReceivedCount <- mkReg(0);
   Reg#(Bit#(32)) wordsDeqCount <- mkReg(0);
   Reg#(Bit#(32)) lastDataBeatCount <- mkReg(0);
   FIFOF#(Bit#(busWidth)) rfifo <- mkSizedBRAMFIFOF(32);
   Reg#(Bit#(8)) burstCountReg <- mkReg(0);
   Reg#(Bool) operationInProgress <- mkReg(False);
   Reg#(Bool) addressPresented <- mkReg(False);
   FIFOF#(Bit#(2)) axiRrespFifo <- mkSizedBRAMFIFOF(32);

   rule updateBurstCount if (!rfifo.notEmpty && !operationInProgress && enabledReg && ptrReg < boundsReg);
       burstCountReg <= 8'd8;
       operationInProgress <= True;
       addressPresented <= False;
   endrule

   method Bool notEmpty();
   // fixme
       return rfifo.notEmpty;
   endmethod

   method Bool notFull();
   // fixme
       return rfifo.notFull;
   endmethod

   interface Reg base;
       method Action _write(Bit#(32) base) if (!operationInProgress);
          if (!enabledReg) begin
              baseReg <= base;
              ptrReg <= base;
          end
       endmethod
       method Bit#(32) _read();
          return baseReg;
       endmethod
   endinterface

   interface Reg bounds;
       method Action _write(Bit#(32) bounds) if (!operationInProgress);
          if (!enabledReg) begin
              boundsReg <= bounds;
          end
       endmethod
       method Bit#(32) _read();
          return boundsReg;
       endmethod
   endinterface

   interface Reg threshold = thresholdReg;
   interface Reg enabled = enabledReg;
   interface Reg oneBeatAddress = oneBeatAddressReg;
   interface Reg thirtyTwoBitTransfer = thirtyTwoBitTransferReg;

   method Bit#(32) readStatus(Bit#(12) addr);
       Bit#(32) v = 32'h02142042;
       if (addr == 12'h000)
           v =  baseReg;
       else if (addr == 12'h004)
           v = boundsReg;
       else if (addr == 12'h008)
           v = ptrReg;
       else if (addr == 12'h00C)
           v = extend(burstCountReg);
       else if (addr == 12'h010)
           v = enabledReg ? 32'heeeeeeee : 32'hdddddddd;
       else if (addr == 12'h014)
       begin
           v = 0;
           v[3:0] = axiRrespFifo.notEmpty ? 4'h1 : 4'he;
           v[15:12] = axiRrespFifo.notFull ? 4'h0 : 4'hf;
       end
       else if (addr == 12'h018)
       begin
           v = 0;
           v[3:0] = rfifo.notEmpty ? 4'h1 : 4'he;
           v[15:12] = rfifo.notFull ? 4'h0 : 4'hf;
       end
       else if (addr == 12'h01C)
       begin
           v[31:24] = 8'hbb;
           v[23:16] = operationInProgress ? 8'haa : 8'h11;
           v[15:0] = extend(burstCountReg);
       end
       else if (addr == 12'h020)
           v = wordsDeqCount;
       else if (addr == 12'h024)
           v = addrsBeatCount;
       else if (addr == 12'h028)
           v = wordsReceivedCount;
       else if (addr == 12'h02C)
           v = lastDataBeatCount;
       return v;
   endmethod

   interface AxiMasterRead axi;
       method ActionValue#(Bit#(32)) readAddr() if (operationInProgress && !addressPresented);
           addrsBeatCount <= addrsBeatCount + 1;
           if (oneBeatAddressReg)
               addressPresented <= True;
           let ptrValue = ptrReg;
           return ptrReg;
       endmethod
       method Bit#(8) readBurstLen();
           return burstCountReg-1;
       endmethod
       method Bit#(3) readBurstWidth();
           if (valueOf(busWidth) == 32)
               return 3'b010; // 3'b010: 32bit, 3'b011: 64bit, 3'b100: 128bit
           else if (thirtyTwoBitTransferReg)
               return 3'b010;
           else
               return 3'b011;
       endmethod
       method Bit#(2) readBurstType();  // drive with 2'b01
           return 2'b01;
       endmethod
       method Bit#(3) readBurstProt(); // drive with 3'b000
           return 3'b000;
       endmethod
       method Bit#(4) readBurstCache(); // drive with 4'b0011
           return 4'b0011;
       endmethod
       method Action readData(Bit#(busWidth) data, Bit#(2) resp, Bit#(1) last, Bit#(1) id) if (rfifo.notFull && operationInProgress);
           let bc = burstCountReg - 1;
           if (bc == 1)
               lastDataBeatCount <= lastDataBeatCount + 1;

           if (resp == 2'b00)
           begin
               burstCountReg <= bc;
               ptrReg <= ptrReg + pack(fromInteger(valueOf(busWidth)/8));
               rfifo.enq(data);
               wordsReceivedCount <= wordsReceivedCount + 1;
           end
           else
           begin
               axiRrespFifo.enq(resp);
           end

           if (resp != 2'b00 || bc == 0)
               operationInProgress <= False;
       endmethod
   endinterface

   method Action deq();
       wordsDeqCount <= wordsDeqCount + 1;
       rfifo.deq();
   endmethod
   method Bit#(busWidth) first();
       return rfifo.first;
   endmethod

   method ActionValue#(Bit#(32)) getResponse() if (axiRrespFifo.notEmpty);
       axiRrespFifo.deq;
       return extend(axiRrespFifo.first);
   endmethod

endmodule