Removed old comment

Removed old comment

// 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 Connectable::*;
import DefaultValue::*;
import FIFO::*;
import GetPut::*;
import Vector::*;

import Ehr::*;

import Abstraction::*;
import RegUtil::*;
import RVRFile::*;
import RVCsrFile::*;
import RVExec::*;
import RVTypes::*;
import VerificationPacket::*;

import RVAlu::*;
import RVControl::*;
import RVDecode::*;
import RVMemory::*;
`ifdef CONFIG_M
import RVMulDiv::*;
`endif

// This interface is the combination of FrontEnd and BackEnd
interface Core;
    method Action start(Addr startPc);
    method Action stop;

    method ActionValue#(VerificationPacket) getVerificationPacket;
    method ActionValue#(VMInfo) updateVMInfoI;
    method ActionValue#(VMInfo) updateVMInfoD;

    interface Client#(FenceReq, FenceResp) fence;
endinterface

instance Connectable#(Core, MemorySystem);
    module mkConnection#(Core core, MemorySystem mem)(Empty);
        mkConnection(core.fence, mem.fence);
        mkConnection(toGet(core.updateVMInfoI), toPut(mem.updateVMInfoI));
        mkConnection(toGet(core.updateVMInfoD), toPut(mem.updateVMInfoD));
    endmodule
endinstance

typedef struct {
    Addr pc;
} FetchState deriving (Bits, Eq, FShow);

typedef struct {
    Addr pc;
} ExecuteState deriving (Bits, Eq, FShow);

typedef struct {
    Addr              pc;
    Maybe#(TrapCause) trap;
    RVDecodedInst     dInst;
    Addr              addr;
    Data              data;
} WriteBackState deriving (Bits, Eq, FShow);

module mkThreeStageCore#(
            Server#(RVIMemReq, RVIMemResp) ifetch,
            Server#(RVDMemReq, RVDMemResp) dmem,
            Bool ipi,
            Bool timerInterrupt,
            Bit#(64) timer,
            Bool externalInterrupt,
            Data hartID
        )(Core);
    ArchRFile rf <- mkBypassArchRFile;
    RVCsrFile csrf <- mkRVCsrFile(hartID, timer, timerInterrupt, ipi, externalInterrupt);

`ifdef CONFIG_M
    MulDivExec mulDiv <- mkBoothRoughMulDivExec;
`endif

    Ehr#(4, Maybe#(FetchState)) fetchStateEhr <- mkEhr(tagged Invalid);
    Ehr#(4, Maybe#(ExecuteState)) executeStateEhr <- mkEhr(tagged Invalid);
    Ehr#(4, Maybe#(WriteBackState)) writeBackStateEhr <- mkEhr(tagged Invalid);

    FIFO#(VerificationPacket) verificationPackets <- mkFIFO;

    rule doFetch(fetchStateEhr[2] matches tagged Valid .fetchState
                    &&& executeStateEhr[2] == tagged Invalid);
        // get and clear the fetch state
        let pc = fetchState.pc;
        fetchStateEhr[2] <= tagged Invalid;

        // request instruction
        ifetch.request.put(zeroExtend(pc));

        // pass to execute state
        executeStateEhr[2] <= tagged Valid ExecuteState{ pc: pc };
    endrule

    rule doExecute(executeStateEhr[1] matches tagged Valid .executeState
                    &&& writeBackStateEhr[1] == tagged Invalid);
        // get and clear the execute state
        let pc = executeState.pc;
        executeStateEhr[1] <= tagged Invalid;

        // check for interrupts
        Maybe#(TrapCause) trap = tagged Invalid;
        if (csrf.readyInterrupt matches tagged Valid .validInterrupt) begin
            trap = tagged Valid (tagged Interrupt validInterrupt);
        end

        // get the instruction
        let inst <- ifetch.response.get;

        // decode the instruction
        let maybeDInst = decodeInst(inst);
        if (maybeDInst == tagged Invalid && trap == tagged Invalid) begin
            trap = tagged Valid (tagged Exception IllegalInst);
        end
        let dInst = fromMaybe(?, maybeDInst);

        // $display("[Execute] pc: 0x%0x, inst: 0x%0x, dInst: ", pc, inst, fshow(dInst));

        // read registers
        let rVal1 = rf.rd1(toFullRegIndex(dInst.rs1, getInstFields(inst).rs1));
        let rVal2 = rf.rd2(toFullRegIndex(dInst.rs2, getInstFields(inst).rs2));

        // execute instruction
        let execResult = basicExec(dInst, rVal1, rVal2, pc);
        let data = execResult.data;
        let addr = execResult.addr;
        let nextPc = execResult.nextPc;

        // check for next address alignment
        if (nextPc[1:0] != 0 && trap == tagged Invalid) begin
            trap = tagged Valid (tagged Exception InstAddrMisaligned);
        end

`ifdef CONFIG_M
        if (dInst.execFunc matches tagged MulDiv .mulDivInst &&& trap == tagged Invalid) begin
            mulDiv.exec(mulDivInst, rVal1, rVal2);
        end
`endif

        if (dInst.execFunc matches tagged Mem .memInst &&& trap == tagged Invalid) begin
            // check allignment
            Bool aligned = (case (memInst.size)
                                B: True;
                                H: (execResult.addr[0] == 0);
                                W: (execResult.addr[1:0] == 0);
                                D: (execResult.addr[2:0] == 0);
                            endcase);
            if (aligned) begin
                // send the request to the memory
                dmem.request.put( RVDMemReq {
                    op: dInst.execFunc.Mem.op,
                    size: dInst.execFunc.Mem.size,
                    isUnsigned: dInst.execFunc.Mem.isUnsigned,
                    addr: zeroExtend(addr),
                    data: data } );
            end else begin
                // misaligned address exception
                if ((memInst.op == tagged Mem Ld) || (memInst.op == tagged Mem Lr)) begin
                    trap = tagged Valid (tagged Exception LoadAddrMisaligned);
                end else begin
                    trap = tagged Valid (tagged Exception StoreAddrMisaligned);
                end
            end
        end

        // update next pc for fetch stage if no trap
        if (trap == tagged Invalid) begin
            fetchStateEhr[1] <= tagged Valid FetchState{ pc: nextPc };
        end
        // store things for next stage
        writeBackStateEhr[1] <= tagged Valid WriteBackState{
                                                    pc: pc,
                                                    trap: trap,
                                                    dInst: dInst,
                                                    addr: addr,
                                                    data: data
                                                };
    endrule

    rule doWriteBack(writeBackStateEhr[0] matches tagged Valid .writeBackState);
        let pc = writeBackState.pc;
        let trap = writeBackState.trap;
        let dInst = writeBackState.dInst;
        let inst = dInst.inst;
        let addr = writeBackState.addr;
        let data = writeBackState.data;
        writeBackStateEhr[0] <= tagged Invalid;

`ifdef CONFIG_M
        if (dInst.execFunc matches tagged MulDiv .* &&& trap == tagged Invalid) begin
            data = mulDiv.result_data;
            mulDiv.result_deq;
        end
`endif

        if (dInst.execFunc matches tagged Mem .memInst &&& trap == tagged Invalid) begin
            if (getsResponse(memInst.op)) begin
                data <- dmem.response.get;
            end
        end

        let csrfResult <- csrf.wr(
                pc,
                // performing system instructions
                dInst.execFunc matches tagged System .sysInst ? tagged Valid sysInst : tagged Invalid,
                getInstFields(inst).csr,
                data, // either rf[rs1] or zimm, computed in basicExec
                addr,
                // handling exceptions
                trap,
                // indirect writes
                0,
                False,
                False);

        Maybe#(Addr) maybeNextPc = tagged Invalid;
        Maybe#(Data) maybeData = tagged Invalid;
        Maybe#(TrapCause) maybeTrap = tagged Invalid;
        case (csrfResult) matches
            tagged Exception .exc:
                begin
                    maybeNextPc = tagged Valid exc.trapHandlerPC;
                    maybeTrap = tagged Valid exc.exception;
                end
            tagged RedirectPC .newPc:
                maybeNextPc = tagged Valid newPc;
            tagged CsrData .data:
                maybeData = tagged Valid data;
            tagged None:
                noAction;
        endcase

        // send verification packet
        Bool isInterrupt = False;
        Bool isException = False;
        Bit#(4) trapCause = 0;
        case (maybeTrap) matches
            tagged Valid (tagged Interrupt .x):
                begin
                    isInterrupt = True;
                    trapCause = pack(x);
                end
            tagged Valid (tagged Exception .x):
                begin
                    isException = True;
                    trapCause = pack(x);
                end
        endcase
        verificationPackets.enq( VerificationPacket {
                skippedPackets: 0,
                pc: signExtend(pc),
                data: signExtend(fromMaybe(data, maybeData)),
                addr: signExtend(addr),
                instruction: inst,
                dst: {pack(dInst.dst), getInstFields(inst).rd},
                exception: isException,
                interrupt: isInterrupt,
                cause: trapCause } );

        // This split attribute is required to produce multiple rules so the
        // ifetch.response.get method doesn't cause this rule to conflict
        // with the execute rule.
        (* split *)
        if (maybeNextPc matches tagged Valid .replayPc) begin
            // This instruction is not writing to the register file
            // it is either an instruction that requires flushing the pipeline
            // or it caused an exception
            fetchStateEhr[0] <= tagged Valid FetchState{ pc: replayPc };
            // kill other instructions
            if (executeStateEhr[0] matches tagged Valid .*) begin
                let ignore <- ifetch.response.get;
            end
            executeStateEhr[0] <= tagged Invalid;
        end else begin
            // This instruction retired
            // write to the register file
            rf.wr(toFullRegIndex(dInst.dst, getInstFields(inst).rd), fromMaybe(data, maybeData));
        end
    endrule

    method Action start(Addr startPc);
        fetchStateEhr[3] <= tagged Valid FetchState { pc: startPc };
        executeStateEhr[3] <= tagged Invalid;
        writeBackStateEhr[3] <= tagged Invalid;
    endmethod
    method Action stop;
        fetchStateEhr[3] <= tagged Invalid;
        executeStateEhr[3] <= tagged Invalid;
        writeBackStateEhr[3] <= tagged Invalid;
    endmethod

    method ActionValue#(VerificationPacket) getVerificationPacket;
        let verificationPacket = verificationPackets.first;
        verificationPackets.deq;
        return verificationPacket;
    endmethod
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 Connectable::*;
import DefaultValue::*;
import FIFO::*;
import GetPut::*;
import Vector::*;

import Ehr::*;

import Abstraction::*;
import RegUtil::*;
import RVRFile::*;
import RVCsrFile::*;
import RVExec::*;
import RVTypes::*;
import VerificationPacket::*;

import RVAlu::*;
import RVControl::*;
import RVDecode::*;
import RVMemory::*;
`ifdef CONFIG_M
import RVMulDiv::*;
`endif

// This interface is the combination of FrontEnd and BackEnd
interface Core;
    method Action start(Addr startPc);
    method Action stop;

    method ActionValue#(VerificationPacket) getVerificationPacket;
    method ActionValue#(VMInfo) updateVMInfoI;
    method ActionValue#(VMInfo) updateVMInfoD;

    interface Client#(FenceReq, FenceResp) fence;
endinterface

instance Connectable#(Core, MemorySystem);
    module mkConnection#(Core core, MemorySystem mem)(Empty);
        mkConnection(core.fence, mem.fence);
        mkConnection(toGet(core.updateVMInfoI), toPut(mem.updateVMInfoI));
        mkConnection(toGet(core.updateVMInfoD), toPut(mem.updateVMInfoD));
    endmodule
endinstance

typedef struct {
    Addr pc;
} FetchState deriving (Bits, Eq, FShow);

typedef struct {
    Addr pc;
} ExecuteState deriving (Bits, Eq, FShow);

typedef struct {
    Addr              pc;
    Maybe#(TrapCause) trap;
    RVDecodedInst     dInst;
    Addr              addr;
    Data              data;
} WriteBackState deriving (Bits, Eq, FShow);

module mkThreeStageCore#(
            Server#(RVIMemReq, RVIMemResp) ifetch,
            Server#(RVDMemReq, RVDMemResp) dmem,
            Bool ipi,
            Bool timerInterrupt,
            Bit#(64) timer,
            Bool externalInterrupt,
            Data hartID
        )(Core);
    ArchRFile rf <- mkBypassArchRFile;
    RVCsrFile csrf <- mkRVCsrFile(hartID, timer, timerInterrupt, ipi, externalInterrupt);

`ifdef CONFIG_M
    MulDivExec mulDiv <- mkBoothRoughMulDivExec;
`endif

    Ehr#(4, Maybe#(FetchState)) fetchStateEhr <- mkEhr(tagged Invalid);
    Ehr#(4, Maybe#(ExecuteState)) executeStateEhr <- mkEhr(tagged Invalid);
    Ehr#(4, Maybe#(WriteBackState)) writeBackStateEhr <- mkEhr(tagged Invalid);

    FIFO#(VerificationPacket) verificationPackets <- mkFIFO;

    rule doFetch(fetchStateEhr[2] matches tagged Valid .fetchState
                    &&& executeStateEhr[2] == tagged Invalid);
        // get and clear the fetch state
        let pc = fetchState.pc;
        fetchStateEhr[2] <= tagged Invalid;

        // request instruction
        ifetch.request.put(zeroExtend(pc));

        // pass to execute state
        executeStateEhr[2] <= tagged Valid ExecuteState{ pc: pc };
    endrule

    rule doExecute(executeStateEhr[1] matches tagged Valid .executeState
                    &&& writeBackStateEhr[1] == tagged Invalid);
        // get and clear the execute state
        let pc = executeState.pc;
        executeStateEhr[1] <= tagged Invalid;

        // check for interrupts
        Maybe#(TrapCause) trap = tagged Invalid;
        // TODO: Implement interrupts without creating a conflict between execute and write-back
        if (csrf.readyInterrupt matches tagged Valid .validInterrupt) begin
            trap = tagged Valid (tagged Interrupt validInterrupt);
        end

        // get the instruction
        let inst <- ifetch.response.get;

        // decode the instruction
        let maybeDInst = decodeInst(inst);
        if (maybeDInst == tagged Invalid && trap == tagged Invalid) begin
            trap = tagged Valid (tagged Exception IllegalInst);
        end
        let dInst = fromMaybe(?, maybeDInst);

        // $display("[Execute] pc: 0x%0x, inst: 0x%0x, dInst: ", pc, inst, fshow(dInst));

        // read registers
        let rVal1 = rf.rd1(toFullRegIndex(dInst.rs1, getInstFields(inst).rs1));
        let rVal2 = rf.rd2(toFullRegIndex(dInst.rs2, getInstFields(inst).rs2));

        // execute instruction
        let execResult = basicExec(dInst, rVal1, rVal2, pc);
        let data = execResult.data;
        let addr = execResult.addr;
        let nextPc = execResult.nextPc;

        // check for next address alignment
        if (nextPc[1:0] != 0 && trap == tagged Invalid) begin
            trap = tagged Valid (tagged Exception InstAddrMisaligned);
        end

`ifdef CONFIG_M
        if (dInst.execFunc matches tagged MulDiv .mulDivInst &&& trap == tagged Invalid) begin
            mulDiv.exec(mulDivInst, rVal1, rVal2);
        end
`endif

        if (dInst.execFunc matches tagged Mem .memInst &&& trap == tagged Invalid) begin
            // check allignment
            Bool aligned = (case (memInst.size)
                                B: True;
                                H: (execResult.addr[0] == 0);
                                W: (execResult.addr[1:0] == 0);
                                D: (execResult.addr[2:0] == 0);
                            endcase);
            if (aligned) begin
                // send the request to the memory
                dmem.request.put( RVDMemReq {
                    op: dInst.execFunc.Mem.op,
                    size: dInst.execFunc.Mem.size,
                    isUnsigned: dInst.execFunc.Mem.isUnsigned,
                    addr: zeroExtend(addr),
                    data: data } );
            end else begin
                // misaligned address exception
                if ((memInst.op == tagged Mem Ld) || (memInst.op == tagged Mem Lr)) begin
                    trap = tagged Valid (tagged Exception LoadAddrMisaligned);
                end else begin
                    trap = tagged Valid (tagged Exception StoreAddrMisaligned);
                end
            end
        end

        // update next pc for fetch stage if no trap
        if (trap == tagged Invalid) begin
            fetchStateEhr[1] <= tagged Valid FetchState{ pc: nextPc };
        end
        // store things for next stage
        writeBackStateEhr[1] <= tagged Valid WriteBackState{
                                                    pc: pc,
                                                    trap: trap,
                                                    dInst: dInst,
                                                    addr: addr,
                                                    data: data
                                                };
    endrule

    rule doWriteBack(writeBackStateEhr[0] matches tagged Valid .writeBackState);
        let pc = writeBackState.pc;
        let trap = writeBackState.trap;
        let dInst = writeBackState.dInst;
        let inst = dInst.inst;
        let addr = writeBackState.addr;
        let data = writeBackState.data;
        writeBackStateEhr[0] <= tagged Invalid;

`ifdef CONFIG_M
        if (dInst.execFunc matches tagged MulDiv .* &&& trap == tagged Invalid) begin
            data = mulDiv.result_data;
            mulDiv.result_deq;
        end
`endif

        if (dInst.execFunc matches tagged Mem .memInst &&& trap == tagged Invalid) begin
            if (getsResponse(memInst.op)) begin
                data <- dmem.response.get;
            end
        end

        let csrfResult <- csrf.wr(
                pc,
                // performing system instructions
                dInst.execFunc matches tagged System .sysInst ? tagged Valid sysInst : tagged Invalid,
                getInstFields(inst).csr,
                data, // either rf[rs1] or zimm, computed in basicExec
                addr,
                // handling exceptions
                trap,
                // indirect writes
                0,
                False,
                False);

        Maybe#(Addr) maybeNextPc = tagged Invalid;
        Maybe#(Data) maybeData = tagged Invalid;
        Maybe#(TrapCause) maybeTrap = tagged Invalid;
        case (csrfResult) matches
            tagged Exception .exc:
                begin
                    maybeNextPc = tagged Valid exc.trapHandlerPC;
                    maybeTrap = tagged Valid exc.exception;
                end
            tagged RedirectPC .newPc:
                maybeNextPc = tagged Valid newPc;
            tagged CsrData .data:
                maybeData = tagged Valid data;
            tagged None:
                noAction;
        endcase

        // send verification packet
        Bool isInterrupt = False;
        Bool isException = False;
        Bit#(4) trapCause = 0;
        case (maybeTrap) matches
            tagged Valid (tagged Interrupt .x):
                begin
                    isInterrupt = True;
                    trapCause = pack(x);
                end
            tagged Valid (tagged Exception .x):
                begin
                    isException = True;
                    trapCause = pack(x);
                end
        endcase
        verificationPackets.enq( VerificationPacket {
                skippedPackets: 0,
                pc: signExtend(pc),
                data: signExtend(fromMaybe(data, maybeData)),
                addr: signExtend(addr),
                instruction: inst,
                dst: {pack(dInst.dst), getInstFields(inst).rd},
                exception: isException,
                interrupt: isInterrupt,
                cause: trapCause } );

        // This split attribute is required to produce multiple rules so the
        // ifetch.response.get method doesn't cause this rule to conflict
        // with the execute rule.
        (* split *)
        if (maybeNextPc matches tagged Valid .replayPc) begin
            // This instruction is not writing to the register file
            // it is either an instruction that requires flushing the pipeline
            // or it caused an exception
            fetchStateEhr[0] <= tagged Valid FetchState{ pc: replayPc };
            // kill other instructions
            if (executeStateEhr[0] matches tagged Valid .*) begin
                let ignore <- ifetch.response.get;
            end
            executeStateEhr[0] <= tagged Invalid;
        end else begin
            // This instruction retired
            // write to the register file
            rf.wr(toFullRegIndex(dInst.dst, getInstFields(inst).rd), fromMaybe(data, maybeData));
        end
    endrule

    method Action start(Addr startPc);
        fetchStateEhr[3] <= tagged Valid FetchState { pc: startPc };
        executeStateEhr[3] <= tagged Invalid;
        writeBackStateEhr[3] <= tagged Invalid;
    endmethod
    method Action stop;
        fetchStateEhr[3] <= tagged Invalid;
        executeStateEhr[3] <= tagged Invalid;
        writeBackStateEhr[3] <= tagged Invalid;
    endmethod

    method ActionValue#(VerificationPacket) getVerificationPacket;
        let verificationPacket = verificationPackets.first;
        verificationPackets.deq;
        return verificationPacket;
    endmethod
endmodule


Do not include FPU registers if CONFIG_F is not defined

// 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.

import Ehr::*;
import RVTypes::*;
import Vector::*;

interface ArchRFile;
    // TODO: change Bit#(5) to named type
    method Action wr(Maybe#(FullRegIndex) fullRegIndex, Data data);
    method Data rd1(Maybe#(FullRegIndex) fullRegIndex);
    method Data rd2(Maybe#(FullRegIndex) fullRegIndex);
    method Data rd3(Maybe#(FullRegIndex) fullRegIndex);
endinterface

// This is a merged GPR/FPU register file
(* synthesize *)
module mkArchRFile( ArchRFile );
    let verbose = False;
    File fout = stdout;

    Vector#(32, Reg#(Data)) gpr_rfile <- replicateM(mkReg(0));
`ifdef CONFIG_F
    Vector#(32, Reg#(Data)) fpu_rfile <- replicateM(mkReg(0));
`else
    Vector#(32, Reg#(Data)) fpu_rfile <- replicateM(mkReadOnlyReg(0));
`endif

    function Data read(Maybe#(FullRegIndex) fullRegIndex);
        return (case (fullRegIndex) matches
                tagged Valid (tagged Gpr 0): 0;
                tagged Valid (tagged Gpr .rIndex): gpr_rfile[rIndex];
                tagged Valid (tagged Fpu .rIndex): fpu_rfile[rIndex];
                default: 0;
            endcase);
    endfunction

    method Action wr(Maybe#(FullRegIndex) fullRegIndex, Data data);
        if (verbose) $fdisplay(fout, fshow(fullRegIndex), " <= %h", data);
        case (fullRegIndex) matches
            tagged Valid (tagged Gpr 0): noAction;
            tagged Valid (tagged Gpr .rIndex): gpr_rfile[rIndex] <= data;
            tagged Valid (tagged Fpu .rIndex): fpu_rfile[rIndex] <= data;
            default: noAction;
        endcase
    endmethod

    method Data rd1(Maybe#(FullRegIndex) fullRegIndex) = read(fullRegIndex);
    method Data rd2(Maybe#(FullRegIndex) fullRegIndex) = read(fullRegIndex);
    method Data rd3(Maybe#(FullRegIndex) fullRegIndex) = read(fullRegIndex);
endmodule

import RegUtil::*;

// wr < {rd1, rd2, rd3}
(* synthesize *)
module mkBypassArchRFile( ArchRFile );
    let verbose = False;
    File fout = stdout;

    Vector#(32, Reg#(Data)) gpr_rfile <- replicateM(mkReg(0));
`ifdef CONFIG_F
    Vector#(32, Reg#(Data)) fpu_rfile <- replicateM(mkReg(0));
`else
    Vector#(32, Reg#(Data)) fpu_rfile <- replicateM(mkReadOnlyReg(0));
`endif
    // gpr_rfile[0]._write = constfn(noAction);
    gpr_rfile[0] = readOnlyReg(0);

    // A write req to gpr 0 is the same as no write request
    Ehr#(2, Tuple2#(FullRegIndex, Data)) writeReq <- mkEhr(tuple2(tagged Gpr 0, 0));

    rule performWrite;
        case (tpl_1(writeReq[1])) matches
            tagged Gpr 0: noAction;
            tagged Gpr .rIndex: gpr_rfile[rIndex] <= tpl_2(writeReq[1]);
            tagged Fpu .rIndex: fpu_rfile[rIndex] <= tpl_2(writeReq[1]);
            default: noAction;
        endcase
        writeReq[1] <= tuple2(tagged Gpr 0, 0);
    endrule

    function Data read(Maybe#(FullRegIndex) fullRegIndex);
        Data result = 0;
        if (fullRegIndex matches tagged Valid .validIndex) begin
            if (validIndex == tagged Gpr 0) begin
                result = 0;
            end else if (validIndex == tpl_1(writeReq[1])) begin
                // bypass path from write to read
                result = tpl_2(writeReq[1]);
            end else begin
                result = (case (validIndex) matches
                        tagged Gpr .rIndex: gpr_rfile[rIndex];
                        tagged Fpu .rIndex: fpu_rfile[rIndex];
                    endcase);
            end
        end
        return result;
    endfunction

    method Action wr(Maybe#(FullRegIndex) fullRegIndex, Data data) if (tpl_1(writeReq[0]) == tagged Gpr 0);
        if (verbose) $fdisplay(fout, fshow(fullRegIndex), " <= %h", data);
        writeReq[0] <= tuple2(fromMaybe(tagged Gpr 0, fullRegIndex), data);
    endmethod

    method Data rd1(Maybe#(FullRegIndex) fullRegIndex) = read(fullRegIndex);
    method Data rd2(Maybe#(FullRegIndex) fullRegIndex) = read(fullRegIndex);
    method Data rd3(Maybe#(FullRegIndex) fullRegIndex) = read(fullRegIndex);
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.

import Ehr::*;
import RVTypes::*;
import Vector::*;

interface ArchRFile;
    // TODO: change Bit#(5) to named type
    method Action wr(Maybe#(FullRegIndex) fullRegIndex, Data data);
    method Data rd1(Maybe#(FullRegIndex) fullRegIndex);
    method Data rd2(Maybe#(FullRegIndex) fullRegIndex);
    method Data rd3(Maybe#(FullRegIndex) fullRegIndex);
endinterface

// This is a merged GPR/FPU register file
(* synthesize *)
module mkArchRFile( ArchRFile );
    let verbose = False;
    File fout = stdout;

    Vector#(32, Reg#(Data)) gpr_rfile <- replicateM(mkReg(0));
    Vector#(32, Reg#(Data)) fpu_rfile <- replicateM(mkReg(0));

    function Data read(Maybe#(FullRegIndex) fullRegIndex);
        return (case (fullRegIndex) matches
                tagged Valid (tagged Gpr 0): 0;
                tagged Valid (tagged Gpr .rIndex): gpr_rfile[rIndex];
                tagged Valid (tagged Fpu .rIndex): fpu_rfile[rIndex];
                default: 0;
            endcase);
    endfunction

    method Action wr(Maybe#(FullRegIndex) fullRegIndex, Data data);
        if (verbose) $fdisplay(fout, fshow(fullRegIndex), " <= %h", data);
        case (fullRegIndex) matches
            tagged Valid (tagged Gpr 0): noAction;
            tagged Valid (tagged Gpr .rIndex): gpr_rfile[rIndex] <= data;
            tagged Valid (tagged Fpu .rIndex): fpu_rfile[rIndex] <= data;
            default: noAction;
        endcase
    endmethod

    method Data rd1(Maybe#(FullRegIndex) fullRegIndex) = read(fullRegIndex);
    method Data rd2(Maybe#(FullRegIndex) fullRegIndex) = read(fullRegIndex);
    method Data rd3(Maybe#(FullRegIndex) fullRegIndex) = read(fullRegIndex);
endmodule

import RegUtil::*;

// wr < {rd1, rd2, rd3}
(* synthesize *)
module mkBypassArchRFile( ArchRFile );
    let verbose = False;
    File fout = stdout;

    Vector#(32, Reg#(Data)) gpr_rfile <- replicateM(mkReg(0));
    Vector#(32, Reg#(Data)) fpu_rfile <- replicateM(mkReg(0));
    // gpr_rfile[0]._write = constfn(noAction);
    gpr_rfile[0] = readOnlyReg(0);

    // A write req to gpr 0 is the same as no write request
    Ehr#(2, Tuple2#(FullRegIndex, Data)) writeReq <- mkEhr(tuple2(tagged Gpr 0, 0));

    rule performWrite;
        case (tpl_1(writeReq[1])) matches
            tagged Gpr 0: noAction;
            tagged Gpr .rIndex: gpr_rfile[rIndex] <= tpl_2(writeReq[1]);
            tagged Fpu .rIndex: fpu_rfile[rIndex] <= tpl_2(writeReq[1]);
            default: noAction;
        endcase
        writeReq[1] <= tuple2(tagged Gpr 0, 0);
    endrule

    function Data read(Maybe#(FullRegIndex) fullRegIndex);
        Data result = 0;
        if (fullRegIndex matches tagged Valid .validIndex) begin
            if (validIndex == tagged Gpr 0) begin
                result = 0;
            end else if (validIndex == tpl_1(writeReq[1])) begin
                // bypass path from write to read
                result = tpl_2(writeReq[1]);
            end else begin
                result = (case (validIndex) matches
                        tagged Gpr .rIndex: gpr_rfile[rIndex];
                        tagged Fpu .rIndex: fpu_rfile[rIndex];
                    endcase);
            end
        end
        return result;
    endfunction

    method Action wr(Maybe#(FullRegIndex) fullRegIndex, Data data) if (tpl_1(writeReq[0]) == tagged Gpr 0);
        if (verbose) $fdisplay(fout, fshow(fullRegIndex), " <= %h", data);
        writeReq[0] <= tuple2(fromMaybe(tagged Gpr 0, fullRegIndex), data);
    endmethod

    method Data rd1(Maybe#(FullRegIndex) fullRegIndex) = read(fullRegIndex);
    method Data rd2(Maybe#(FullRegIndex) fullRegIndex) = read(fullRegIndex);
    method Data rd3(Maybe#(FullRegIndex) fullRegIndex) = read(fullRegIndex);
endmodule


added linkUp to xsim implementation of mkSimLink

// Copyright (c) 2015 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 GetPut            :: *;
import Connectable       :: *;
import FIFOF             :: *;
import Pipe              :: *;
import Portal            :: *;
import MsgFormat         :: *;
import CnocPortal        :: *;

interface SimLink#(numeric type dataWidth);
   method Action start(Bit#(32) linknumber, Bool listening);
   method Bool   linkUp();
   interface PipeOut#(Bit#(dataWidth)) rx;
   interface PipeIn#(Bit#(dataWidth)) tx;
endinterface

`ifdef BSIM
import "BDPI" function Action                 bsimLinkOpen(Bit#(32) linknumber, Bool listening);
import "BDPI" function Bit#(1)                bsimLinkUp(Bit#(32) linknumber, Bool listening);
import "BDPI" function Bool                   bsimLinkCanReceive(Bit#(32) linknumber, Bool listening);
import "BDPI" function Bool                   bsimLinkCanTransmit(Bit#(32) linknumber, Bool listening);
import "BDPI" function ActionValue#(Bit#(32)) bsimLinkReceive32(Bit#(32) linknumber, Bool listening);
import "BDPI" function Action                 bsimLinkTransmit32(Bit#(32) linknumber, Bool listening, Bit#(32) value);
import "BDPI" function ActionValue#(Bit#(64)) bsimLinkReceive64(Bit#(32) linknumber, Bool listening);
import "BDPI" function Action                 bsimLinkTransmit64(Bit#(32) linknumber, Bool listening, Bit#(64) value);

typeclass SelectLinkWidth#(numeric type dsz);
   function ActionValue#(Bit#(dsz)) bsimLinkReceive(Bit#(32) linknumber, Bool listening);
   function Action bsimLinkTransmit(Bit#(32) linknumber, Bool listening, Bit#(dsz) value);
endtypeclass

instance SelectLinkWidth#(32);
   function ActionValue#(Bit#(32)) bsimLinkReceive(Bit#(32) linknumber, Bool listening);
   actionvalue
      let v <- bsimLinkReceive32(linknumber, listening);
      return v;
   endactionvalue
   endfunction
   function Action bsimLinkTransmit(Bit#(32) linknumber, Bool listening, Bit#(32) value);
   action
      bsimLinkTransmit32(linknumber, listening, value);
   endaction
   endfunction
endinstance
instance SelectLinkWidth#(64);
   function ActionValue#(Bit#(64)) bsimLinkReceive(Bit#(32) linknumber, Bool listening);
   actionvalue
      let v <- bsimLinkReceive64(linknumber, listening);
      return v;
   endactionvalue
   endfunction
   function Action bsimLinkTransmit(Bit#(32) linknumber, Bool listening, Bit#(64) value);
   action
      bsimLinkTransmit64(linknumber, listening, value);
   endaction
   endfunction
endinstance

module mkSimLink(SimLink#(dataWidth)) provisos (SelectLinkWidth#(dataWidth));
   FIFOF#(Bit#(dataWidth)) rxFifo <- mkFIFOF();
   FIFOF#(Bit#(dataWidth)) txFifo <- mkFIFOF();
   Reg#(Bit#(32)) linknumber <- mkReg(0);
   Reg#(Bool) opened    <- mkReg(False);
   Reg#(Bool) listening <- mkReg(False);
   Reg#(Bool) started   <- mkReg(False);

   rule open if (!opened && started);
      bsimLinkOpen(linknumber, listening);
      opened <= True;
   endrule

   rule receive if (bsimLinkCanReceive(linknumber, listening));
      let v <- bsimLinkReceive(linknumber, listening);
      rxFifo.enq(v);
   endrule

   rule transmit if (bsimLinkCanTransmit(linknumber, listening));
      let v <- toGet(txFifo).get();
      bsimLinkTransmit(linknumber, listening, v);
   endrule

   interface rx = toPipeOut(rxFifo);
   interface tx = toPipeIn(txFifo);
   method Action start(Bit#(32) number, Bool l);
      linknumber <= number;
      started <= True;
      listening <= l;
   endmethod
   method Bool linkUp();
      if (started)
	 return unpack(bsimLinkUp(linknumber, listening));
      else
	 return False;
   endmethod
endmodule
`endif

`ifdef XSIM
import "BVI" XsimLink =
module mkSimLink(SimLink#(32));

   method start(start_linknumber, start_listening) enable (en_start);
   method link_up linkUp();
   interface PipeOut rx;
      method rx_first first() ready (rdy_rx_first);
      method deq() enable (en_rx_deq) ready (rdy_rx_deq);
      method rx_not_empty notEmpty();
   endinterface
   interface PipeIn tx;
      method enq(tx_enq_v) enable (en_tx_enq) ready (rdy_tx_enq);
      method tx_not_full notFull();
   endinterface
   schedule (rx_first, rx_notEmpty, tx_notFull, rx_deq, tx_enq, start) CF (rx_first, rx_notEmpty, tx_notFull, rx_deq, tx_enq, start);
endmodule
`endif

// Copyright (c) 2015 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 GetPut            :: *;
import Connectable       :: *;
import FIFOF             :: *;
import Pipe              :: *;
import Portal            :: *;
import MsgFormat         :: *;
import CnocPortal        :: *;

interface SimLink#(numeric type dataWidth);
   method Action start(Bit#(32) linknumber, Bool listening);
   method Bool   linkUp();
   interface PipeOut#(Bit#(dataWidth)) rx;
   interface PipeIn#(Bit#(dataWidth)) tx;
endinterface

`ifdef BSIM
import "BDPI" function Action                 bsimLinkOpen(Bit#(32) linknumber, Bool listening);
import "BDPI" function Bit#(1)                bsimLinkUp(Bit#(32) linknumber, Bool listening);
import "BDPI" function Bool                   bsimLinkCanReceive(Bit#(32) linknumber, Bool listening);
import "BDPI" function Bool                   bsimLinkCanTransmit(Bit#(32) linknumber, Bool listening);
import "BDPI" function ActionValue#(Bit#(32)) bsimLinkReceive32(Bit#(32) linknumber, Bool listening);
import "BDPI" function Action                 bsimLinkTransmit32(Bit#(32) linknumber, Bool listening, Bit#(32) value);
import "BDPI" function ActionValue#(Bit#(64)) bsimLinkReceive64(Bit#(32) linknumber, Bool listening);
import "BDPI" function Action                 bsimLinkTransmit64(Bit#(32) linknumber, Bool listening, Bit#(64) value);

typeclass SelectLinkWidth#(numeric type dsz);
   function ActionValue#(Bit#(dsz)) bsimLinkReceive(Bit#(32) linknumber, Bool listening);
   function Action bsimLinkTransmit(Bit#(32) linknumber, Bool listening, Bit#(dsz) value);
endtypeclass

instance SelectLinkWidth#(32);
   function ActionValue#(Bit#(32)) bsimLinkReceive(Bit#(32) linknumber, Bool listening);
   actionvalue
      let v <- bsimLinkReceive32(linknumber, listening);
      return v;
   endactionvalue
   endfunction
   function Action bsimLinkTransmit(Bit#(32) linknumber, Bool listening, Bit#(32) value);
   action
      bsimLinkTransmit32(linknumber, listening, value);
   endaction
   endfunction
endinstance
instance SelectLinkWidth#(64);
   function ActionValue#(Bit#(64)) bsimLinkReceive(Bit#(32) linknumber, Bool listening);
   actionvalue
      let v <- bsimLinkReceive64(linknumber, listening);
      return v;
   endactionvalue
   endfunction
   function Action bsimLinkTransmit(Bit#(32) linknumber, Bool listening, Bit#(64) value);
   action
      bsimLinkTransmit64(linknumber, listening, value);
   endaction
   endfunction
endinstance

module mkSimLink(SimLink#(dataWidth)) provisos (SelectLinkWidth#(dataWidth));
   FIFOF#(Bit#(dataWidth)) rxFifo <- mkFIFOF();
   FIFOF#(Bit#(dataWidth)) txFifo <- mkFIFOF();
   Reg#(Bit#(32)) linknumber <- mkReg(0);
   Reg#(Bool) opened    <- mkReg(False);
   Reg#(Bool) listening <- mkReg(False);
   Reg#(Bool) started   <- mkReg(False);

   rule open if (!opened && started);
      bsimLinkOpen(linknumber, listening);
      opened <= True;
   endrule

   rule receive if (bsimLinkCanReceive(linknumber, listening));
      let v <- bsimLinkReceive(linknumber, listening);
      rxFifo.enq(v);
   endrule

   rule transmit if (bsimLinkCanTransmit(linknumber, listening));
      let v <- toGet(txFifo).get();
      bsimLinkTransmit(linknumber, listening, v);
   endrule

   interface rx = toPipeOut(rxFifo);
   interface tx = toPipeIn(txFifo);
   method Action start(Bit#(32) number, Bool l);
      linknumber <= number;
      started <= True;
      listening <= l;
   endmethod
   method Bool linkUp();
      if (started)
	 return unpack(bsimLinkUp(linknumber, listening));
      else
	 return False;
   endmethod
endmodule
`endif

`ifdef XSIM
import "BVI" XsimLink =
module mkSimLink(SimLink#(32));

   method start(linknumber, listening) enable (en_start);
   interface PipeOut rx;
      method rx_first first() ready (rdy_rx_first);
      method deq() enable (en_rx_deq) ready (rdy_rx_deq);
      method rx_not_empty notEmpty();
   endinterface
   interface PipeIn tx;
      method enq(tx_enq_v) enable (en_tx_enq) ready (rdy_tx_enq);
      method tx_not_full notFull();
   endinterface
   schedule (rx_first, rx_notEmpty, tx_notFull, rx_deq, tx_enq, start) CF (rx_first, rx_notEmpty, tx_notFull, rx_deq, tx_enq, start);
endmodule
`endif


temporary static initialization of a and b string lengths

// 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 StmtFSM::*;
import BRAM::*;

module mkHirschA#(BRAMServer#(Bit#(strIndexWidth), Bit#(8)) strA, BRAMServer#(Bit#(strIndexWidth), Bit#(8)) strB, BRAMServer#(Bit#(lIndexWidth), Bit#(16)) matL)(FSM)
      provisos(Add#(0, 7, strIndexWidth),
	       Add#(0, 14, lIndexWidth));

    Reg#(Bit#(7)) aLenReg <- mkReg(21);
  Reg#(Bit#(7)) bLenReg <- mkReg(18);
  Reg#(Bit#(14)) rLenReg <- mkReg(0);
  Reg#(Bit#(7)) ii <- mkReg(0);
  Reg#(Bit#(7)) jj <- mkReg(0);
   Reg#(Bit#(8)) aData <- mkReg(0);
   Reg#(Bit#(8)) bData <- mkReg(0);
   Reg#(Bit#(16)) lim1jm1 <- mkReg(0);
   Reg#(Bit#(16)) lim1j <- mkReg(0);
   Reg#(Bit#(16)) lijm1 <- mkReg(0);

   Stmt hirschA =
   seq
//      $display("hirschA running alen %d blen %d", aLenReg, bLenReg);
      for (ii<= 0; ii < aLenReg; ii <= ii + 1)
	 seq
	    matL.request.put(BRAMRequest{write: True, responseOnWrite: False, address: {ii,0}, datain: 0});
	    endseq
      for (ii<= 0; ii < aLenReg; ii <= ii + 1)
	 seq
	    matL.request.put(BRAMRequest{write: True, responseOnWrite: False, address: {0,ii}, datain: 0});
	 endseq
      for (ii<= 1; ii <= aLenReg; ii <= ii + 1)
	 for (jj<= 1; jj <= bLenReg; jj <= jj + 1)
	    seq
	       strA.request.put(BRAMRequest{write: False, responseOnWrite: False, address: ii-1, datain: 0});
	       strB.request.put(BRAMRequest{write: False, responseOnWrite: False, address: jj-1, datain: 0});
	       action
		  let ta <- strA.response.get();
		  let tb <- strB.response.get();
		  aData <= ta;
		  bData <= tb;
	       endaction
	       if (aData == bData)
		  seq
		     matL.request.put(BRAMRequest{write: False, responseOnWrite: False, address: {ii-1,jj-1}, datain: 0});
		     action
			let temp <- matL.response.get();
			lim1jm1 <= temp;
		     endaction
		     matL.request.put(BRAMRequest{write: True, responseOnWrite: False, address: {ii,jj}, datain: lim1jm1+1});

		  endseq
	       else
		  seq
		     matL.request.put(BRAMRequest{write: False, responseOnWrite: False, address: {ii,jj-1}, datain: 0});
		     action
			let tlijm1 <- matL.response.get();
			lijm1 <= tlijm1;
		     endaction
		     matL.request.put(BRAMRequest{write: False, responseOnWrite: False, address: {ii-1,jj}, datain: 0});
		     action
			let tlim1j <- matL.response.get();
			lim1j <= tlim1j;
		     endaction
			matL.request.put(BRAMRequest{write: True, responseOnWrite: False, address: {ii,jj}, datain: max(lijm1,lim1j)});
		  endseq
	    endseq
   endseq;
   FSM hA <- mkFSM(hirschA);
   return(hA);

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 StmtFSM::*;
import BRAM::*;

module mkHirschA#(BRAMServer#(Bit#(strIndexWidth), Bit#(8)) strA, BRAMServer#(Bit#(strIndexWidth), Bit#(8)) strB, BRAMServer#(Bit#(lIndexWidth), Bit#(16)) matL)(FSM);

    Reg#(Bit#(7)) aLenReg <- mkReg(0);
  Reg#(Bit#(7)) bLenReg <- mkReg(0);
  Reg#(Bit#(14)) rLenReg <- mkReg(0);
  Reg#(Bit#(7)) ii <- mkReg(0);
  Reg#(Bit#(7)) jj <- mkReg(0);
   Reg#(Bit#(8)) aData <- mkReg(0);
   Reg#(Bit#(8)) bData <- mkReg(0);
   Reg#(Bit#(16)) lim1jm1 <- mkReg(0);
   Reg#(Bit#(16)) lim1j <- mkReg(0);
   Reg#(Bit#(16)) lijm1 <- mkReg(0);

   Stmt hirschA =
   seq
      for (ii<= 0; ii < aLenReg; ii <= ii + 1)
	 seq
	    matL.request.put(BRAMRequest{write: True, responseOnWrite: False, address: {ii,0}, datain: 0});
	    endseq
      for (ii<= 0; ii < aLenReg; ii <= ii + 1)
	 seq
	    matL.request.put(BRAMRequest{write: True, responseOnWrite: False, address: {0,ii}, datain: 0});
	 endseq
      for (ii<= 1; ii <= aLenReg; ii <= ii + 1)
	 for (jj<= 1; jj <= bLenReg; jj <= jj + 1)
	    seq
	       strA.request.put(BRAMRequest{write: False, responseOnWrite: False, address: ii-1, datain: 0});
	       strB.request.put(BRAMRequest{write: False, responseOnWrite: False, address: jj-1, datain: 0});
	       action
		  let ta <- strA.response.get();
		  let tb <- strB.response.get();
		  aData <= ta;
		  bData <= tb;
	       endaction
	       if (aData == bData)
		  seq
		     matL.request.put(BRAMRequest{write: False, responseOnWrite: False, address: {ii-1,jj-1}, datain: 0});
		     action
			let temp <- matL.response.get();
			lim1jm1 <= temp;
		     endaction
		     matL.request.put(BRAMRequest{write: True, responseOnWrite: False, address: {ii,jj}, datain: lim1jm1+1});

		  endseq
	       else
		  seq
		     matL.request.put(BRAMRequest{write: False, responseOnWrite: False, address: {ii,jj-1}, datain: 0});
		     action
			let tlijm1 <- matL.response.get();
			lijm1 <= tlijm1;
		     endaction
		     matL.request.put(BRAMRequest{write: False, responseOnWrite: False, address: {ii-1,jj}, datain: 0});
		     action
			let tlim1j <- matL.response.get();
			lim1j <= tlim1j;
		     endaction
			matL.request.put(BRAMRequest{write: True, responseOnWrite: False, address: {ii,jj}, datain: max(lijm1,lim1j)});
		  endseq
	    endseq
   endseq;
   mkFSM(hirschA);

endmodule


fix strstr termination

fix strstr termination

// 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.

/*
 * Implementation of:
 *    MP algorithm on pages 7-11 from "Pattern Matching Algorithms" by
 *       Alberto Apostolico, Zvi Galil, 1997
 */

`include "ConnectalProjectConfig.bsv"
import FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import Vector::*;
import BRAM::*;
import Gearbox::*;
import Connectable::*;
import ConfigReg::*;
import StmtFSM::*;
import Probe::*;

import MemUtils::*;
import MemTypes::*;
import Dma2BRAM::*;
import Pipe::*;
import EHR::*;

interface MPEngine#(numeric type haystackBusWidth, numeric type configBusWidth);
   interface PipeIn#(Triplet#(Bit#(32))) setsearch;
   interface PipeOut#(Int#(32)) locdone;
endinterface

interface MPStreamEngine#(numeric type haystackBusWidth, numeric type configBusWidth);
   interface PipeIn#(MemDataF#(configBusWidth)) needle;
   interface PipeIn#(MemDataF#(configBusWidth)) mpNext;
   interface PipeIn#(MemDataF#(haystackBusWidth)) haystack;
   interface PipeOut#(Int#(32)) locdone;
   method Action clear();
   method Action start(Bit#(32) needleLen);
endinterface

typedef Bit#(8) Char;
typedef Bit#(64) DWord;
typedef Bit#(32) Word;

typedef 1024 MaxNeedleLen;
typedef TLog#(MaxNeedleLen) NeedleIdxWidth;
typedef Bit#(NeedleIdxWidth) NeedleIdx;

typedef enum {Config_needle, Config_mpNext, Initialized, Search} Stage deriving (Eq, Bits);

module mkMPEngine#(MemReadEngineServer#(haystackBusWidth) haystackReader,
		   MemReadEngineServer#(configBusWidth) configReader) (MPEngine#(haystackBusWidth,configBusWidth))

   provisos(Add#(a__, 8, haystackBusWidth),
	    Div#(haystackBusWidth,8,haystackBusBytes),
	    Mul#(haystackBusBytes,8,haystackBusWidth),
	    Add#(1, b__, haystackBusBytes),
	    Add#(c__, 32, haystackBusWidth),
	    Add#(1, d__, TDiv#(haystackBusWidth, 32)),
	    Mul#(TDiv#(haystackBusWidth, 32), 32, haystackBusWidth),
	    Add#(e__, TLog#(haystackBusBytes), 32),
	    Add#(f__, TLog#(TDiv#(haystackBusWidth, 32)), 32)
	    ,Mul#(TDiv#(configBusWidth, 8), 8, configBusWidth)
	    ,Add#(1, g__, TDiv#(configBusWidth, 8))
	    ,Add#(h__, TLog#(TDiv#(configBusWidth, 8)), 32)
	    ,Add#(i__, TLog#(TDiv#(configBusWidth, 32)), 32)
	    ,Add#(1, j__, TDiv#(configBusWidth, 32))
	    ,Mul#(TDiv#(configBusWidth, 32), 32, configBusWidth)
	    );

   FIFOF#(Int#(32)) locf <- mkFIFOF;
   FIFO#(Bool) conff <- mkSizedFIFO(1);

   let verbose = True;
   let debug = False;

   Clock clk <- exposeCurrentClock;
   Reset rst <- exposeCurrentReset;
   BRAM2Port#(NeedleIdx, Char) needle  <- mkBRAM2Server(defaultValue);
   BRAM2Port#(NeedleIdx, Bit#(32)) mpNext <- mkBRAM2Server(defaultValue);
   Gearbox#(haystackBusBytes,1,Char) haystack <- mkNto1Gearbox(clk,rst,clk,rst);

   Reg#(Bit#(32)) cycleCnt <- mkReg(0);
   Reg#(Bit#(32)) lastHD <- mkReg(0);

   Reg#(Stage)    stage <- mkReg(Config_needle);
   Reg#(Bit#(32)) needleLenReg <- mkReg(0);
   Reg#(Bit#(32)) haystackLenReg <- mkReg(0);
   Reg#(Bit#(32)) haystackBase <- mkReg(0);
   Reg#(Bit#(32)) jReg <- mkReg(0); // offset in haystack
   Reg#(Bit#(32)) iReg <- mkReg(0); // offset in needle
   Reg#(Bit#(2))  epochReg <- mkReg(0);

   BRAMWriter#(NeedleIdxWidth,configBusWidth) n2b <- mkBRAMWriter(0, needle.portB, configReader);
   BRAMWriter#(NeedleIdxWidth,configBusWidth) mp2b <- mkBRAMWriter(1, mpNext.portB, configReader);

   FIFOF#(Tuple2#(Bit#(2),Bit#(32))) efifo <- mkSizedFIFOF(2);
   FIFOF#(Triplet#(Bit#(32))) ssfifo <- mkFIFOF;
   FIFO#(void) doneFifo <- mkFIFO;

   rule countCycles;
      if (debug) $display("******************************************** %d", cycleCnt);
      cycleCnt <= cycleCnt+1;
   endrule

   rule haystackResp;
      if (debug) $display("mkMPEngine::haystackResp");
      let rv <- toGet(haystackReader.data).get;
      haystack.enq(unpack(rv.data));
      if (rv.last)
         conff.deq;
   endrule

   rule haystackDrain(stage != Search);
      if (debug) $display("mkMPEngine::haystackDrain");
      haystack.deq;
   endrule

   rule bramDrain(stage != Search);
      if (debug) $display("mkMPEngine::mpNextDrain");
      let x <- mpNext.portA.response.get;
      let y <- needle.portA.response.get;
      efifo.deq;
   endrule

`define OPTIMIZE_MISMATCH
`ifdef OPTIMIZE_MISMATCH
   rule matchNeedleReq(stage == Search);
      if (debug) $display("mkMPEngine::matchNeedleReq %d %d", epochReg, iReg);
      needle.portA.request.put(BRAMRequest{write:False, address: truncate(iReg-1), datain:?, responseOnWrite:?});
      mpNext.portA.request.put(BRAMRequest{write:False, address: truncate(iReg), datain:?, responseOnWrite:?});
      efifo.enq(tuple2(epochReg,iReg));
      iReg <= 1;
   endrule

   rule matchNeedleResp(stage == Search);
      let nv <- needle.portA.response.get;
      let mp <- mpNext.portA.response.get;
      let epoch = tpl_1(efifo.first);
      efifo.deq;
      if (debug) $display("mkMPEngine::matchNeedleResp %d %d", epochReg, epoch);
      if (epoch == epochReg) begin
	 Bool deq_haystack = False;
	 let n = haystackLenReg;
	 let m = needleLenReg;
	 let hv = haystack.first;
	 let i = tpl_2(efifo.first);
	 let j = jReg;
	 if (debug) $display("mkMPEngine::feck %d %d %d %d %x %x", n, m, i, j, hv[0], nv);
	 if (j > n) begin
	    // jReg points to the end of the haystack; we are done
	    stage <= Config_needle;
	    if (debug) $display("mkMPEngine::end of search %d", j);
	    locf.enq(-1);
	 end
	 else if (i==m+1) begin
	    // iReg points to the end of the needle; we have a match
	    if (debug) $display("mkMPEngine::string match %d", j);
	    locf.enq(unpack(haystackBase+j-i));
	 end
	 else if (nv != hv[0]) begin
	    // mismatch betwen head of haystack and head of needle; rewind iReg
	    if (debug) $display("mkMPEngine::char mismatch %d %d MP_Next[i]=%d", i, j, mp);
	    if (mp == 0) begin
	       iReg <= 1;
	       jReg <= j+1;
	       deq_haystack = True;
	    end
	    else begin
	       epochReg <= epochReg + 1;
	       iReg <= mp;
	    end
	 end
	 else begin
	    // match between head of needle and head of haystack; increment haystack
	    if (debug) $display("mkMPEngine::char match(%d) %d %d", (nv == hv[0]), i, j);
	    deq_haystack = True;
	    jReg <= j+1;
	    epochReg <= epochReg + 1;
	    iReg <= i+1;
	 end
	 if (deq_haystack) begin
	    haystack.deq;
	    lastHD <= cycleCnt;
	    if (debug) $display("mkMPEngine:: deq haystack(%d)", cycleCnt-lastHD);
	 end
      end
      else begin
	 if (debug) $display("mkMPEngine::discard");
	 noAction;
      end
   endrule
`else
   rule matchNeedleReq(stage == Search);
      if (debug) $display("mkMPEngine::matchNeedleReq %d %d", epochReg, iReg);
      needle.portA.request.put(BRAMRequest{write:False, address: truncate(iReg-1), datain:?, responseOnWrite:?});
      mpNext.portA.request.put(BRAMRequest{write:False, address: truncate(iReg), datain:?, responseOnWrite:?});
      efifo.enq(tuple2(epochReg,iReg));
      iReg <= iReg+1;
   endrule

   rule matchNeedleResp(stage == Search);
      let nv <- needle.portA.response.get;
      let mp <- mpNext.portA.response.get;
      let epoch = tpl_1(efifo.first);
      efifo.deq;
      if (debug) $display("mkMPEngine::matchNeedleResp %d %d", epochReg, epoch);
      if (epoch == epochReg) begin
	 Bool deq_haystack = False;
	 let n = haystackLenReg;
	 let m = needleLenReg;
	 let hv = haystack.first;
	 let i = tpl_2(efifo.first);
	 let j = jReg;
	 if (debug) $display("mkMPEngine::feck %d %d %d %d %x %x", n, m, i, j, hv[0], nv);
	 if (j > n) begin
	    // jReg points to the end of the haystack; we are done
	    stage <= Config_needle;
	    if (debug) $display("mkMPEngine::end of search %d", j);
	 end
	 else if (i==m+1) begin
	    // iReg points to the end of the needle; we have a match
	    if (debug) $display("mkMPEngine::string match %d", j);
	    locf.enq(unpack(haystackBase+j-i));
	    epochReg <= epochReg + 1;
	    iReg <= 1;
	 end
	 else if (nv != hv[0]) begin
	    // mismatch betwen head of haystack and head of needle; rewind iReg
	    if (debug) $display("mkMPEngine::char mismatch %d %d MP_Next[i]=%d", i, j, mp);
	    epochReg <= epochReg + 1;
	    if (mp == 0) begin
	       iReg <= 1;
	       jReg <= j+1;
	       deq_haystack = True;
	    end
	    else begin
	       iReg <= mp;
	    end
	 end
	 else begin
	    // match between head of needle and head of haystack; increment haystack
	    if (debug) $display("mkMPEngine::char match(%d) %d %d", (nv == hv[0]), i, j);
	    deq_haystack = True;
	    jReg <= j+1;
	 end
	 if (deq_haystack) begin
	    haystack.deq;
	    lastHD <= cycleCnt;
	    if (debug) $display("mkMPEngine:: deq haystack(%d)", cycleCnt-lastHD);
	 end
      end
      else begin
	 if (debug) $display("mkMPEngine::discard");
	 noAction;
      end
   endrule
`endif

   rule finish_setup_n2b;
      if (verbose) $display("mkMPEngine::finish_setup_n2b");
      let x <- n2b.finish;
      conff.deq;
      stage <= Config_mpNext;
   endrule

   rule finish_setup_mp2b;
      if (verbose) $display("mkMPEngine::finish_setup_mp2b");
      let y <- mp2b.finish;
      conff.deq;
      stage <= Initialized;
   endrule

   rule setup_needle (stage == Config_needle);
      conff.enq(True);
      match {.needle_sglId, .mpNext_sglId, .needle_len} = ssfifo.first;
      needleLenReg <= extend(needle_len);
      if (verbose) $display("mkMPEngine::setup_needle %d %d", needle_sglId, needle_len);
      n2b.start(needle_sglId, 0, 0, pack(truncate(needle_len)));
   endrule

   rule setup_mpNext (stage == Config_mpNext);
      conff.enq(True);
      match {.needle_sglId, .mpNext_sglId, .needle_len} = ssfifo.first;
      needleLenReg <= extend(needle_len);
      if (verbose) $display("mkMPEngine::setup_mpNext %d %d", mpNext_sglId, needle_len);
      mp2b.start(mpNext_sglId, 0, 0, pack(truncate(needle_len)));
      ssfifo.deq;
   endrule

   rule search (stage == Initialized && !efifo.notEmpty && !haystack.notEmpty);
      stage <= Search;
      conff.enq(True);
      match {.haystack_sglId, .haystack_len, .haystack_base} <- toGet(ssfifo).get;
      haystackLenReg <= extend(haystack_len);
      haystackBase <= extend(haystack_base);
      iReg <= 1;
      jReg <= 1;
      epochReg <= 0;
      Bit#(32) haystack_len_ds = haystack_len+fromInteger(valueOf(haystackBusBytes)-1);
      Bit#(TLog#(haystackBusBytes)) zeros = 0;
      Bit#(32) haystack_len_bytes = {zeros,haystack_len_ds[31:valueOf(TLog#(haystackBusBytes))]} * fromInteger(valueOf(haystackBusBytes));
      $display("haystack read offset=%d burstLen=%d", haystack_base, fromInteger(8*valueOf(haystackBusBytes)));
      haystackReader.request.put(MemengineCmd{sglId:haystack_sglId, base:extend(haystack_base), len:haystack_len_bytes, burstLen:fromInteger(8*valueOf(haystackBusBytes)), tag: 0});
      if (verbose) $display("mkMPEngine::search %d %d %d",  haystack_sglId, haystack_base, haystack_len_bytes);
   endrule

   interface PipeIn setsearch = toPipeIn(ssfifo);
   interface PipeOut locdone = toPipeOut(locf);

endmodule

module mkMPStreamEngine(MPStreamEngine#(haystackBusWidth,configBusWidth))

   provisos(Add#(a__, 8, haystackBusWidth),
	    Div#(haystackBusWidth,8,haystackBusBytes),
	    Mul#(haystackBusBytes,8,haystackBusWidth),
	    Add#(1, b__, haystackBusBytes),
	    Add#(c__, 32, haystackBusWidth),
	    Add#(1, d__, TDiv#(haystackBusWidth, 32)),
	    Mul#(TDiv#(haystackBusWidth, 32), 32, haystackBusWidth),
	    Add#(e__, TLog#(haystackBusBytes), 32),
	    Add#(f__, TLog#(TDiv#(haystackBusWidth, 32)), 32)
	    ,Mul#(TDiv#(configBusWidth, 8), 8, configBusWidth)
	    ,Add#(1, g__, TDiv#(configBusWidth, 8))
	    ,Add#(h__, TLog#(TDiv#(configBusWidth, 8)), 32)
	    ,Add#(i__, TLog#(TDiv#(configBusWidth, 32)), 32)
	    ,Add#(1, j__, TDiv#(configBusWidth, 32))
	    ,Mul#(TDiv#(configBusWidth, 32), 32, configBusWidth)
	    );

   FIFOF#(Int#(32)) locf <- mkFIFOF;

   let verbose = True;
   let debug = True;

   Clock clk <- exposeCurrentClock;
   Reset rst <- exposeCurrentReset;
   BRAM2Port#(NeedleIdx, Char) needleBram  <- mkBRAM2Server(defaultValue);
   BRAM2Port#(NeedleIdx, Bit#(32)) mpNextBram <- mkBRAM2Server(defaultValue);
   FIFOF#(MemDataF#(haystackBusWidth)) haystackFifo <- mkFIFOF();
   Gearbox#(haystackBusBytes,1,Char) haystackGb <- mkNto1Gearbox(clk,rst,clk,rst);

   Reg#(Bit#(32)) cycleCnt <- mkReg(0);
   Reg#(Bit#(32)) lastHD <- mkReg(0);

   Reg#(Stage)    stage <- mkReg(Initialized);
   Reg#(Bit#(32)) needleLenReg <- mkReg(0);
   Reg#(Bit#(32)) jReg <- mkReg(1); // offset in haystack
   Reg#(Bit#(32)) iReg <- mkReg(1); // offset in needle

   BRAMPipeIn#(NeedleIdxWidth,configBusWidth) n2b <- mkBRAMPipeIn(0, needleBram.portB);
   BRAMPipeIn#(NeedleIdxWidth,configBusWidth) mp2b <- mkBRAMPipeIn(1, mpNextBram.portB);

   FIFOF#(Tuple2#(Bit#(2),Bit#(32))) efifo <- mkSizedFIFOF(2);
   FIFO#(void) doneFifo <- mkFIFO;

   rule countCycles;
      //if (debug) $display("******************************************** %d", cycleCnt);
      cycleCnt <= cycleCnt+1;
   endrule

   rule haystackResp;
      let rv <- toGet(haystackFifo).get;
      if (debug) $display("mkMPEngine::haystackResp rv=%h", rv.data);
      haystackGb.enq(unpack(rv.data));
   endrule

   let x_i <- mkReg(0);
   let t_j <- mkReg(0);
   let m = needleLenReg;
   let i = iReg;
   let matchFsm <- mkAutoFSM(seq
      while (stage != Search) seq
	 iReg <= 1;
      endseq
      action
	 iReg <= 1;
	 jReg <= 1;
	 let t = haystackGb.first[0]; haystackGb.deq();
	 t_j <= t;
	 needleBram.portA.request.put(BRAMRequest{write:False, address: truncate(iReg-1), datain:?, responseOnWrite:?});
      endaction
      action
	 let xNext <- needleBram.portA.response.get();
	 x_i <= xNext;
      endaction
      while (stage == Search) seq
	 $display("initial i=%d j=%d x_i=%h t_j=%h", iReg, jReg, x_i, t_j);
	 while ((i == m + 1) || (i > 0 && x_i != t_j)) seq
	    action
	       mpNextBram.portA.request.put(BRAMRequest{write:False, address: truncate(iReg), datain:?, responseOnWrite:?});
      	    endaction
	    action
	       Bit#(32) mpNext <- mpNextBram.portA.response.get();
	       let iNext = mpNext[15:0];
	       let xNext = mpNext[31:16];
	       iReg <= extend(iNext);
	       x_i <= truncate(xNext);
	       $display("i=%d iNext=%d m+1=%d x_i=%h t_j=%h mpNext=%h", i, iNext, m+1, xNext, t_j, mpNext);
	    endaction
	 endseq
	 action
	    iReg <= iReg + 1;
	    jReg <= jReg + 1;
	    needleBram.portA.request.put(BRAMRequest{write:False, address: truncate(iReg+1-1), datain:?, responseOnWrite:?});
	    let t = haystackGb.first[0]; haystackGb.deq();
	    t_j <= t;
	    if (t == 0) begin
	       $display("iReg=%d jReg=%d t==0 locf.enq(-1)", iReg, jReg, t);
	       locf.enq(-1); // this seems to be needed for the strstr example to terminate
	    end
	 endaction
	 action
	    let xNext <- needleBram.portA.response.get();
	    x_i <= xNext;
	    $display("step    i=%d j=%d x_i=%h t_j=%h", iReg, jReg, xNext, t_j);
	    if (iReg == m + 1) begin
	       $display("match at j=%d", jReg - iReg);
	       locf.enq(unpack(jReg - iReg));
	    end
	 endaction
      endseq // stage == Search
      endseq);

   interface PipeIn needle = n2b.pipe;
   interface PipeIn mpNext = mp2b.pipe;
   interface PipeIn haystack = toPipeIn(haystackFifo);
   interface PipeOut locdone = toPipeOut(locf);

   method Action clear();
      stage <= Initialized;
   endmethod
   method Action start(Bit#(32) needleLen);
      stage <= Search;
      jReg <= 1;
      iReg <= 1;
      needleLenReg <= needleLen;
   endmethod

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.

/*
 * Implementation of:
 *    MP algorithm on pages 7-11 from "Pattern Matching Algorithms" by
 *       Alberto Apostolico, Zvi Galil, 1997
 */

`include "ConnectalProjectConfig.bsv"
import FIFO::*;
import FIFOF::*;
import SpecialFIFOs::*;
import Vector::*;
import BRAM::*;
import Gearbox::*;
import Connectable::*;
import ConfigReg::*;
import StmtFSM::*;
import Probe::*;

import MemUtils::*;
import MemTypes::*;
import Dma2BRAM::*;
import Pipe::*;
import EHR::*;

interface MPEngine#(numeric type haystackBusWidth, numeric type configBusWidth);
   interface PipeIn#(Triplet#(Bit#(32))) setsearch;
   interface PipeOut#(Int#(32)) locdone;
endinterface

interface MPStreamEngine#(numeric type haystackBusWidth, numeric type configBusWidth);
   interface PipeIn#(MemDataF#(configBusWidth)) needle;
   interface PipeIn#(MemDataF#(configBusWidth)) mpNext;
   interface PipeIn#(MemDataF#(haystackBusWidth)) haystack;
   interface PipeOut#(Int#(32)) locdone;
   method Action clear();
   method Action start(Bit#(32) needleLen);
endinterface

typedef Bit#(8) Char;
typedef Bit#(64) DWord;
typedef Bit#(32) Word;

typedef 1024 MaxNeedleLen;
typedef TLog#(MaxNeedleLen) NeedleIdxWidth;
typedef Bit#(NeedleIdxWidth) NeedleIdx;

typedef enum {Config_needle, Config_mpNext, Initialized, Search} Stage deriving (Eq, Bits);

module mkMPEngine#(MemReadEngineServer#(haystackBusWidth) haystackReader,
		   MemReadEngineServer#(configBusWidth) configReader) (MPEngine#(haystackBusWidth,configBusWidth))

   provisos(Add#(a__, 8, haystackBusWidth),
	    Div#(haystackBusWidth,8,haystackBusBytes),
	    Mul#(haystackBusBytes,8,haystackBusWidth),
	    Add#(1, b__, haystackBusBytes),
	    Add#(c__, 32, haystackBusWidth),
	    Add#(1, d__, TDiv#(haystackBusWidth, 32)),
	    Mul#(TDiv#(haystackBusWidth, 32), 32, haystackBusWidth),
	    Add#(e__, TLog#(haystackBusBytes), 32),
	    Add#(f__, TLog#(TDiv#(haystackBusWidth, 32)), 32)
	    ,Mul#(TDiv#(configBusWidth, 8), 8, configBusWidth)
	    ,Add#(1, g__, TDiv#(configBusWidth, 8))
	    ,Add#(h__, TLog#(TDiv#(configBusWidth, 8)), 32)
	    ,Add#(i__, TLog#(TDiv#(configBusWidth, 32)), 32)
	    ,Add#(1, j__, TDiv#(configBusWidth, 32))
	    ,Mul#(TDiv#(configBusWidth, 32), 32, configBusWidth)
	    );

   FIFOF#(Int#(32)) locf <- mkFIFOF;
   FIFO#(Bool) conff <- mkSizedFIFO(1);

   let verbose = True;
   let debug = False;

   Clock clk <- exposeCurrentClock;
   Reset rst <- exposeCurrentReset;
   BRAM2Port#(NeedleIdx, Char) needle  <- mkBRAM2Server(defaultValue);
   BRAM2Port#(NeedleIdx, Bit#(32)) mpNext <- mkBRAM2Server(defaultValue);
   Gearbox#(haystackBusBytes,1,Char) haystack <- mkNto1Gearbox(clk,rst,clk,rst);

   Reg#(Bit#(32)) cycleCnt <- mkReg(0);
   Reg#(Bit#(32)) lastHD <- mkReg(0);

   Reg#(Stage)    stage <- mkReg(Config_needle);
   Reg#(Bit#(32)) needleLenReg <- mkReg(0);
   Reg#(Bit#(32)) haystackLenReg <- mkReg(0);
   Reg#(Bit#(32)) haystackBase <- mkReg(0);
   Reg#(Bit#(32)) jReg <- mkReg(0); // offset in haystack
   Reg#(Bit#(32)) iReg <- mkReg(0); // offset in needle
   Reg#(Bit#(2))  epochReg <- mkReg(0);

   BRAMWriter#(NeedleIdxWidth,configBusWidth) n2b <- mkBRAMWriter(0, needle.portB, configReader);
   BRAMWriter#(NeedleIdxWidth,configBusWidth) mp2b <- mkBRAMWriter(1, mpNext.portB, configReader);

   FIFOF#(Tuple2#(Bit#(2),Bit#(32))) efifo <- mkSizedFIFOF(2);
   FIFOF#(Triplet#(Bit#(32))) ssfifo <- mkFIFOF;
   FIFO#(void) doneFifo <- mkFIFO;

   rule countCycles;
      if (debug) $display("******************************************** %d", cycleCnt);
      cycleCnt <= cycleCnt+1;
   endrule

   rule haystackResp;
      if (debug) $display("mkMPEngine::haystackResp");
      let rv <- toGet(haystackReader.data).get;
      haystack.enq(unpack(rv.data));
      if (rv.last)
         conff.deq;
   endrule

   rule haystackDrain(stage != Search);
      if (debug) $display("mkMPEngine::haystackDrain");
      haystack.deq;
   endrule

   rule bramDrain(stage != Search);
      if (debug) $display("mkMPEngine::mpNextDrain");
      let x <- mpNext.portA.response.get;
      let y <- needle.portA.response.get;
      efifo.deq;
   endrule

`define OPTIMIZE_MISMATCH
`ifdef OPTIMIZE_MISMATCH
   rule matchNeedleReq(stage == Search);
      if (debug) $display("mkMPEngine::matchNeedleReq %d %d", epochReg, iReg);
      needle.portA.request.put(BRAMRequest{write:False, address: truncate(iReg-1), datain:?, responseOnWrite:?});
      mpNext.portA.request.put(BRAMRequest{write:False, address: truncate(iReg), datain:?, responseOnWrite:?});
      efifo.enq(tuple2(epochReg,iReg));
      iReg <= 1;
   endrule

   rule matchNeedleResp(stage == Search);
      let nv <- needle.portA.response.get;
      let mp <- mpNext.portA.response.get;
      let epoch = tpl_1(efifo.first);
      efifo.deq;
      if (debug) $display("mkMPEngine::matchNeedleResp %d %d", epochReg, epoch);
      if (epoch == epochReg) begin
	 Bool deq_haystack = False;
	 let n = haystackLenReg;
	 let m = needleLenReg;
	 let hv = haystack.first;
	 let i = tpl_2(efifo.first);
	 let j = jReg;
	 if (debug) $display("mkMPEngine::feck %d %d %d %d %x %x", n, m, i, j, hv[0], nv);
	 if (j > n) begin
	    // jReg points to the end of the haystack; we are done
	    stage <= Config_needle;
	    if (debug) $display("mkMPEngine::end of search %d", j);
	    locf.enq(-1);
	 end
	 else if (i==m+1) begin
	    // iReg points to the end of the needle; we have a match
	    if (debug) $display("mkMPEngine::string match %d", j);
	    locf.enq(unpack(haystackBase+j-i));
	 end
	 else if (nv != hv[0]) begin
	    // mismatch betwen head of haystack and head of needle; rewind iReg
	    if (debug) $display("mkMPEngine::char mismatch %d %d MP_Next[i]=%d", i, j, mp);
	    if (mp == 0) begin
	       iReg <= 1;
	       jReg <= j+1;
	       deq_haystack = True;
	    end
	    else begin
	       epochReg <= epochReg + 1;
	       iReg <= mp;
	    end
	 end
	 else begin
	    // match between head of needle and head of haystack; increment haystack
	    if (debug) $display("mkMPEngine::char match(%d) %d %d", (nv == hv[0]), i, j);
	    deq_haystack = True;
	    jReg <= j+1;
	    epochReg <= epochReg + 1;
	    iReg <= i+1;
	 end
	 if (deq_haystack) begin
	    haystack.deq;
	    lastHD <= cycleCnt;
	    if (debug) $display("mkMPEngine:: deq haystack(%d)", cycleCnt-lastHD);
	 end
      end
      else begin
	 if (debug) $display("mkMPEngine::discard");
	 noAction;
      end
   endrule
`else
   rule matchNeedleReq(stage == Search);
      if (debug) $display("mkMPEngine::matchNeedleReq %d %d", epochReg, iReg);
      needle.portA.request.put(BRAMRequest{write:False, address: truncate(iReg-1), datain:?, responseOnWrite:?});
      mpNext.portA.request.put(BRAMRequest{write:False, address: truncate(iReg), datain:?, responseOnWrite:?});
      efifo.enq(tuple2(epochReg,iReg));
      iReg <= iReg+1;
   endrule

   rule matchNeedleResp(stage == Search);
      let nv <- needle.portA.response.get;
      let mp <- mpNext.portA.response.get;
      let epoch = tpl_1(efifo.first);
      efifo.deq;
      if (debug) $display("mkMPEngine::matchNeedleResp %d %d", epochReg, epoch);
      if (epoch == epochReg) begin
	 Bool deq_haystack = False;
	 let n = haystackLenReg;
	 let m = needleLenReg;
	 let hv = haystack.first;
	 let i = tpl_2(efifo.first);
	 let j = jReg;
	 if (debug) $display("mkMPEngine::feck %d %d %d %d %x %x", n, m, i, j, hv[0], nv);
	 if (j > n) begin
	    // jReg points to the end of the haystack; we are done
	    stage <= Config_needle;
	    if (debug) $display("mkMPEngine::end of search %d", j);
	 end
	 else if (i==m+1) begin
	    // iReg points to the end of the needle; we have a match
	    if (debug) $display("mkMPEngine::string match %d", j);
	    locf.enq(unpack(haystackBase+j-i));
	    epochReg <= epochReg + 1;
	    iReg <= 1;
	 end
	 else if (nv != hv[0]) begin
	    // mismatch betwen head of haystack and head of needle; rewind iReg
	    if (debug) $display("mkMPEngine::char mismatch %d %d MP_Next[i]=%d", i, j, mp);
	    epochReg <= epochReg + 1;
	    if (mp == 0) begin
	       iReg <= 1;
	       jReg <= j+1;
	       deq_haystack = True;
	    end
	    else begin
	       iReg <= mp;
	    end
	 end
	 else begin
	    // match between head of needle and head of haystack; increment haystack
	    if (debug) $display("mkMPEngine::char match(%d) %d %d", (nv == hv[0]), i, j);
	    deq_haystack = True;
	    jReg <= j+1;
	 end
	 if (deq_haystack) begin
	    haystack.deq;
	    lastHD <= cycleCnt;
	    if (debug) $display("mkMPEngine:: deq haystack(%d)", cycleCnt-lastHD);
	 end
      end
      else begin
	 if (debug) $display("mkMPEngine::discard");
	 noAction;
      end
   endrule
`endif

   rule finish_setup_n2b;
      if (verbose) $display("mkMPEngine::finish_setup_n2b");
      let x <- n2b.finish;
      conff.deq;
      stage <= Config_mpNext;
   endrule

   rule finish_setup_mp2b;
      if (verbose) $display("mkMPEngine::finish_setup_mp2b");
      let y <- mp2b.finish;
      conff.deq;
      stage <= Initialized;
   endrule

   rule setup_needle (stage == Config_needle);
      conff.enq(True);
      match {.needle_sglId, .mpNext_sglId, .needle_len} = ssfifo.first;
      needleLenReg <= extend(needle_len);
      if (verbose) $display("mkMPEngine::setup_needle %d %d", needle_sglId, needle_len);
      n2b.start(needle_sglId, 0, 0, pack(truncate(needle_len)));
   endrule

   rule setup_mpNext (stage == Config_mpNext);
      conff.enq(True);
      match {.needle_sglId, .mpNext_sglId, .needle_len} = ssfifo.first;
      needleLenReg <= extend(needle_len);
      if (verbose) $display("mkMPEngine::setup_mpNext %d %d", mpNext_sglId, needle_len);
      mp2b.start(mpNext_sglId, 0, 0, pack(truncate(needle_len)));
      ssfifo.deq;
   endrule

   rule search (stage == Initialized && !efifo.notEmpty && !haystack.notEmpty);
      stage <= Search;
      conff.enq(True);
      match {.haystack_sglId, .haystack_len, .haystack_base} <- toGet(ssfifo).get;
      haystackLenReg <= extend(haystack_len);
      haystackBase <= extend(haystack_base);
      iReg <= 1;
      jReg <= 1;
      epochReg <= 0;
      Bit#(32) haystack_len_ds = haystack_len+fromInteger(valueOf(haystackBusBytes)-1);
      Bit#(TLog#(haystackBusBytes)) zeros = 0;
      Bit#(32) haystack_len_bytes = {zeros,haystack_len_ds[31:valueOf(TLog#(haystackBusBytes))]} * fromInteger(valueOf(haystackBusBytes));
      $display("haystack read offset=%d burstLen=%d", haystack_base, fromInteger(8*valueOf(haystackBusBytes)));
      haystackReader.request.put(MemengineCmd{sglId:haystack_sglId, base:extend(haystack_base), len:haystack_len_bytes, burstLen:fromInteger(8*valueOf(haystackBusBytes)), tag: 0});
      if (verbose) $display("mkMPEngine::search %d %d %d",  haystack_sglId, haystack_base, haystack_len_bytes);
   endrule

   interface PipeIn setsearch = toPipeIn(ssfifo);
   interface PipeOut locdone = toPipeOut(locf);

endmodule

module mkMPStreamEngine(MPStreamEngine#(haystackBusWidth,configBusWidth))

   provisos(Add#(a__, 8, haystackBusWidth),
	    Div#(haystackBusWidth,8,haystackBusBytes),
	    Mul#(haystackBusBytes,8,haystackBusWidth),
	    Add#(1, b__, haystackBusBytes),
	    Add#(c__, 32, haystackBusWidth),
	    Add#(1, d__, TDiv#(haystackBusWidth, 32)),
	    Mul#(TDiv#(haystackBusWidth, 32), 32, haystackBusWidth),
	    Add#(e__, TLog#(haystackBusBytes), 32),
	    Add#(f__, TLog#(TDiv#(haystackBusWidth, 32)), 32)
	    ,Mul#(TDiv#(configBusWidth, 8), 8, configBusWidth)
	    ,Add#(1, g__, TDiv#(configBusWidth, 8))
	    ,Add#(h__, TLog#(TDiv#(configBusWidth, 8)), 32)
	    ,Add#(i__, TLog#(TDiv#(configBusWidth, 32)), 32)
	    ,Add#(1, j__, TDiv#(configBusWidth, 32))
	    ,Mul#(TDiv#(configBusWidth, 32), 32, configBusWidth)
	    );

   FIFOF#(Int#(32)) locf <- mkFIFOF;

   let verbose = True;
   let debug = True;

   Clock clk <- exposeCurrentClock;
   Reset rst <- exposeCurrentReset;
   BRAM2Port#(NeedleIdx, Char) needleBram  <- mkBRAM2Server(defaultValue);
   BRAM2Port#(NeedleIdx, Bit#(32)) mpNextBram <- mkBRAM2Server(defaultValue);
   FIFOF#(MemDataF#(haystackBusWidth)) haystackFifo <- mkFIFOF();
   Gearbox#(haystackBusBytes,1,Char) haystackGb <- mkNto1Gearbox(clk,rst,clk,rst);

   Reg#(Bit#(32)) cycleCnt <- mkReg(0);
   Reg#(Bit#(32)) lastHD <- mkReg(0);

   Reg#(Stage)    stage <- mkReg(Initialized);
   Reg#(Bit#(32)) needleLenReg <- mkReg(0);
   Reg#(Bit#(32)) jReg <- mkReg(1); // offset in haystack
   Reg#(Bit#(32)) iReg <- mkReg(1); // offset in needle

   BRAMPipeIn#(NeedleIdxWidth,configBusWidth) n2b <- mkBRAMPipeIn(0, needleBram.portB);
   BRAMPipeIn#(NeedleIdxWidth,configBusWidth) mp2b <- mkBRAMPipeIn(1, mpNextBram.portB);

   FIFOF#(Tuple2#(Bit#(2),Bit#(32))) efifo <- mkSizedFIFOF(2);
   FIFO#(void) doneFifo <- mkFIFO;

   rule countCycles;
      //if (debug) $display("******************************************** %d", cycleCnt);
      cycleCnt <= cycleCnt+1;
   endrule

   rule haystackResp;
      let rv <- toGet(haystackFifo).get;
      if (debug) $display("mkMPEngine::haystackResp rv=%h", rv.data);
      haystackGb.enq(unpack(rv.data));
   endrule

   let x_i <- mkReg(0);
   let t_j <- mkReg(0);
   let m = needleLenReg;
   let i = iReg;
   let matchFsm <- mkAutoFSM(seq
      while (stage != Search) seq
	 iReg <= 1;
      endseq
      action
	 iReg <= 1;
	 jReg <= 1;
	 let t = haystackGb.first[0]; haystackGb.deq();
	 t_j <= t;
	 needleBram.portA.request.put(BRAMRequest{write:False, address: truncate(iReg-1), datain:?, responseOnWrite:?});
      endaction
      action
	 let xNext <- needleBram.portA.response.get();
	 x_i <= xNext;
      endaction
      while (stage == Search) seq
	 $display("initial i=%d j=%d x_i=%h t_j=%h", iReg, jReg, x_i, t_j);
	 while ((i == m + 1) || (i > 0 && x_i != t_j)) seq
	    action
	       mpNextBram.portA.request.put(BRAMRequest{write:False, address: truncate(iReg), datain:?, responseOnWrite:?});
      	    endaction
	    action
	       Bit#(32) mpNext <- mpNextBram.portA.response.get();
	       let iNext = mpNext[15:0];
	       let xNext = mpNext[31:16];
	       iReg <= extend(iNext);
	       x_i <= truncate(xNext);
	       $display("i=%d iNext=%d m+1=%d x_i=%h t_j=%h mpNext=%h", i, iNext, m+1, xNext, t_j, mpNext);
	    endaction
	 endseq
	 action
	    iReg <= iReg + 1;
	    jReg <= jReg + 1;
	    needleBram.portA.request.put(BRAMRequest{write:False, address: truncate(iReg+1-1), datain:?, responseOnWrite:?});
	    let t = haystackGb.first[0]; haystackGb.deq();
	    t_j <= t;
	    if (t == 0)
	       locf.enq('hdeadbf); // this seems to be needed for the strstr example to terminate
	 endaction
	 action
	    let xNext <- needleBram.portA.response.get();
	    x_i <= xNext;
	    $display("step    i=%d j=%d x_i=%h t_j=%h", iReg, jReg, xNext, t_j);
	    if (iReg == m + 1) begin
	       $display("match at j=%d", jReg - iReg);
	       locf.enq(unpack(jReg - iReg));
	    end
	 endaction
      endseq // stage == Search
      endseq);

   interface PipeIn needle = n2b.pipe;
   interface PipeIn mpNext = mp2b.pipe;
   interface PipeIn haystack = toPipeIn(haystackFifo);
   interface PipeOut locdone = toPipeOut(locf);

   method Action clear();
      stage <= Initialized;
   endmethod
   method Action start(Bit#(32) needleLen);
      stage <= Search;
      jReg <= 1;
      iReg <= 1;
      needleLenReg <= needleLen;
   endmethod

endmodule


concurrency is hard

concurrency is hard

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

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

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

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

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

import Pipe::*;
import MemTypes::*;
import MemreadEngine::*;
import Pipe::*;

typedef 32 NumEngineServers;

interface MemreadRequest;
   method Action startRead(Bit#(32) pointer, Bit#(32) numWords, Bit#(32) burstLen, Bit#(32) iterCnt);
   method Action getStateDbg();
endinterface

interface Memread;
   interface MemreadRequest request;
   interface ObjectReadClient#(64) dmaClient;
endinterface

interface MemreadIndication;
   method Action started(Bit#(32) numWords);
   method Action reportStateDbg(Bit#(32) streamRdCnt, Bit#(32) mismatchCount);
   method Action readDone(Bit#(32) mismatchCount);
endinterface

module [Module] mkMemread#(MemreadIndication indication) (Memread);

   Reg#(ObjectPointer)   pointer <- mkReg(0);
   Reg#(Bit#(32))       numWords <- mkReg(0);
   Reg#(Bit#(32))       burstLen <- mkReg(0);
   Reg#(Bit#(32))    mismatchCnt <- mkReg(0);
   FIFO#(void)                cf <- mkSizedFIFO(1);

   Reg#(Bit#(32))                                   iterCnt <- mkReg(0);
   Vector#(NumEngineServers, Reg#(Bit#(32)))       iterCnts <- replicateM(mkReg(0));
   Vector#(NumEngineServers, Reg#(Bit#(32)))        srcGens <- replicateM(mkReg(0));
   Vector#(NumEngineServers, Reg#(Bit#(32))) mismatchCounts <- replicateM(mkReg(0));
   MemreadEngineV#(64,1,NumEngineServers)                re <- mkMemreadEngineV;
   Vector#(NumEngineServers, FIFOF#(Bit#(32))) mismatchFifos <- replicateM(mkFIFOF);
   Bit#(ObjectOffsetSize) chunk = (extend(numWords)/fromInteger(valueOf(NumEngineServers)))*4;

   for(Integer i = 0; i < valueOf(NumEngineServers); i=i+1) begin
      rule start (iterCnts[i] > 0);
	 re.readServers[i].request.put(MemengineCmd{pointer:pointer, base:fromInteger(i)*chunk, len:truncate(chunk), burstLen:truncate(burstLen*4)});
	 $display("start %d, %d", i, iterCnts[i]);
	 srcGens[i] <= fromInteger(i)*truncate(chunk/4);
      endrule
      rule finish;
	 $display("finish %d %d", i, iterCnts[i]);
	 iterCnts[i] <= iterCnts[i]-1;
	 let rv <- re.readServers[i].response.get;
	 // need to pipeline this also
	 //mismatchCnt <= mismatchCnt+mismatchCounts[i];
	 mismatchCounts[i] <= 0;
	 mismatchFifos[i].enq(mismatchCounts[i]);
      endrule
      rule check;
	 let v <- toGet(re.dataPipes[i]).get;
	 let expectedV = {srcGens[i]+1,srcGens[i]};
	 let misMatch = v != expectedV;
	 mismatchCounts[i] <= mismatchCounts[i] + (misMatch ? 1 : 0);
	 srcGens[i] <= srcGens[i]+2;
      endrule
   end

   function Bit#(32) my_add(Tuple2#(Bit#(32),Bit#(32)) xy); match { .x, .y } = xy; return x + y; endfunction

   PipeOut#(Vector#(NumEngineServers, Bit#(32))) mismatchCountsPipe <- mkJoinVector(id, map(toPipeOut, mismatchFifos));
   PipeOut#(Bit#(32)) mismatchCountPipe <- mkReducePipe(mkMap(my_add), mismatchCountsPipe);

   rule indicate_finish;
      let mc <- toGet(mismatchCountPipe).get();
      mc = mc + mismatchCnt;
      if (iterCnt == 1) begin
	 cf.deq;
	 indication.readDone(mc);
	 mc = 0;
      end
      mismatchCnt <= mc;
      iterCnt <= iterCnt - 1;
   endrule

   interface dmaClient = re.dmaClient;
   interface MemreadRequest request;
      method Action startRead(Bit#(32) rp, Bit#(32) nw, Bit#(32) bl, Bit#(32) ic);
	 indication.started(nw);
	 pointer <= rp;
	 cf.enq(?);
	 numWords  <= nw;
	 burstLen  <= bl;
	 iterCnt <= ic;
	 for(Integer i = 0; i < valueOf(NumEngineServers); i=i+1) begin
	    iterCnts[i] <= ic;
	    mismatchCounts[i] <= 0;
	 end
      endmethod
   endinterface
endmodule

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

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

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

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

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

import Pipe::*;
import MemTypes::*;
import MemreadEngine::*;
import Pipe::*;

typedef 32 NumEngineServers;

interface MemreadRequest;
   method Action startRead(Bit#(32) pointer, Bit#(32) numWords, Bit#(32) burstLen, Bit#(32) iterCnt);
   method Action getStateDbg();
endinterface

interface Memread;
   interface MemreadRequest request;
   interface ObjectReadClient#(64) dmaClient;
endinterface

interface MemreadIndication;
   method Action started(Bit#(32) numWords);
   method Action reportStateDbg(Bit#(32) streamRdCnt, Bit#(32) mismatchCount);
   method Action readDone(Bit#(32) mismatchCount);
endinterface

module [Module] mkMemread#(MemreadIndication indication) (Memread);

   Reg#(ObjectPointer)   pointer <- mkReg(0);
   Reg#(Bit#(32))       numWords <- mkReg(0);
   Reg#(Bit#(32))       burstLen <- mkReg(0);
   Reg#(Bit#(32))    mismatchCnt <- mkReg(0);
   FIFO#(void)                cf <- mkSizedFIFO(1);

   Reg#(Bit#(32))                                   iterCnt <- mkReg(0);
   Vector#(NumEngineServers, Reg#(Bit#(32)))       iterCnts <- replicateM(mkReg(0));
   Vector#(NumEngineServers, Reg#(Bit#(32)))        srcGens <- replicateM(mkReg(0));
   Vector#(NumEngineServers, Reg#(Bit#(32))) mismatchCounts <- replicateM(mkReg(0));
   MemreadEngineV#(64,1,NumEngineServers)                re <- mkMemreadEngineV;
   Vector#(NumEngineServers, FIFOF#(Bit#(32))) mismatchFifos <- replicateM(mkFIFOF);
   Bit#(ObjectOffsetSize) chunk = (extend(numWords)/fromInteger(valueOf(NumEngineServers)))*4;

   for(Integer i = 0; i < valueOf(NumEngineServers); i=i+1) begin
      rule start (iterCnts[i] > 0);
	 re.readServers[i].request.put(MemengineCmd{pointer:pointer, base:fromInteger(i)*chunk, len:truncate(chunk), burstLen:truncate(burstLen*4)});
	 $display("start %d, %d", i, iterCnts[i]);
      endrule
      rule finish;
	 $display("finish %d %d", i, iterCnts[i]);
	 iterCnts[i] <= iterCnts[i]-1;
	 let rv <- re.readServers[i].response.get;
	 // need to pipeline this also
	 //mismatchCnt <= mismatchCnt+mismatchCounts[i];
	 mismatchCounts[i] <= 0;
	 mismatchFifos[i].enq(mismatchCounts[i]);
	 srcGens[i] <= fromInteger(i)*truncate(chunk/4);
      endrule
      rule check;
	 let v <- toGet(re.dataPipes[i]).get;
	 let expectedV = {srcGens[i]+1,srcGens[i]};
	 let misMatch = v != expectedV;
	 mismatchCounts[i] <= mismatchCounts[i] + (misMatch ? 1 : 0);
	 srcGens[i] <= srcGens[i]+2;
      endrule
   end

   function Bit#(32) my_add(Tuple2#(Bit#(32),Bit#(32)) xy); match { .x, .y } = xy; return x + y; endfunction

   PipeOut#(Vector#(NumEngineServers, Bit#(32))) mismatchCountsPipe <- mkJoinVector(id, map(toPipeOut, mismatchFifos));
   PipeOut#(Bit#(32)) mismatchCountPipe <- mkReducePipe(mkMap(my_add), mismatchCountsPipe);

   rule indicate_finish;
      let mc <- toGet(mismatchCountPipe).get();
      mc = mc + mismatchCnt;
      if (iterCnt == 1) begin
	 cf.deq;
	 indication.readDone(mc);
	 mc = 0;
      end
      mismatchCnt <= mc;
      iterCnt <= iterCnt - 1;
   endrule

   interface dmaClient = re.dmaClient;
   interface MemreadRequest request;
      method Action startRead(Bit#(32) rp, Bit#(32) nw, Bit#(32) bl, Bit#(32) ic);
	 indication.started(nw);
	 pointer <= rp;
	 cf.enq(?);
	 numWords  <= nw;
	 burstLen  <= bl;
	 iterCnt <= ic;
	 for(Integer i = 0; i < valueOf(NumEngineServers); i=i+1) begin
	    iterCnts[i] <= ic;
	    mismatchCounts[i] <= 0;
	    srcGens[i] <= fromInteger(i)*truncate(chunk/4);
	 end
      endmethod
   endinterface
endmodule