Split decoders by ISA size

Split decoders by ISA size

// 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"
`include "Opcodes.defines"

import BuildVector::*;
import Opcodes::*;
import RVTypes::*;
import Vector::*;
import DefaultValue::*;

function Maybe#(ExecFunc) toExecFuncRV32I(Instruction inst);
    return (case (inst) matches
            // BRANCH
            `BEQ:   tagged Valid (tagged Br Eq);
            `BNE:   tagged Valid (tagged Br Neq);
            `BLT:   tagged Valid (tagged Br Lt);
            `BGE:   tagged Valid (tagged Br Ge);
            `BLTU:  tagged Valid (tagged Br Ltu);
            `BGEU:  tagged Valid (tagged Br Geu);
            // JALR
            `JALR:  tagged Valid (tagged Br Jalr);
            // JAL
            `JAL:   tagged Valid (tagged Br Jal);
            // LUI
            `LUI:   tagged Valid (tagged Alu AluInst{op: Lui,   w: False});
            // AUIPC
            `AUIPC: tagged Valid (tagged Alu AluInst{op: Auipc, w: False});
            // OP-IMM
            `ADDI:  tagged Valid (tagged Alu AluInst{op: Add,   w: False});
            `SLLI:  tagged Valid (tagged Alu AluInst{op: Sll,   w: False});
            `SLTI:  tagged Valid (tagged Alu AluInst{op: Slt,   w: False});
            `SLTIU: tagged Valid (tagged Alu AluInst{op: Sltu,  w: False});
            `XORI:  tagged Valid (tagged Alu AluInst{op: Xor,   w: False});
            `SRLI:  tagged Valid (tagged Alu AluInst{op: Srl,   w: False});
            `SRAI:  tagged Valid (tagged Alu AluInst{op: Sra,   w: False});
            `ORI:   tagged Valid (tagged Alu AluInst{op: Or,    w: False});
            `ANDI:  tagged Valid (tagged Alu AluInst{op: And,   w: False});
            // OP
            `ADD:   tagged Valid (tagged Alu AluInst{op: Add,   w: False});
            `SUB:   tagged Valid (tagged Alu AluInst{op: Sub,   w: False});
            `SLL:   tagged Valid (tagged Alu AluInst{op: Sll,   w: False});
            `SLT:   tagged Valid (tagged Alu AluInst{op: Slt,   w: False});
            `SLTU:  tagged Valid (tagged Alu AluInst{op: Sltu,  w: False});
            `XOR:   tagged Valid (tagged Alu AluInst{op: Xor,   w: False});
            `SRL:   tagged Valid (tagged Alu AluInst{op: Srl,   w: False});
            `SRA:   tagged Valid (tagged Alu AluInst{op: Sra,   w: False});
            `OR:    tagged Valid (tagged Alu AluInst{op: Or,    w: False});
            `AND:   tagged Valid (tagged Alu AluInst{op: And,   w: False});
            // LOAD
            `LB:    tagged Valid (tagged Mem RVMemInst{op: tagged Mem Ld, size: B, isUnsigned: False});
            `LH:    tagged Valid (tagged Mem RVMemInst{op: tagged Mem Ld, size: H, isUnsigned: False});
            `LW:    tagged Valid (tagged Mem RVMemInst{op: tagged Mem Ld, size: W, isUnsigned: False});
            `LBU:   tagged Valid (tagged Mem RVMemInst{op: tagged Mem Ld, size: B, isUnsigned: True});
            `LHU:   tagged Valid (tagged Mem RVMemInst{op: tagged Mem Ld, size: H, isUnsigned: True});
            // STORE
            `SB:    tagged Valid (tagged Mem RVMemInst{op: tagged Mem St, size: B, isUnsigned: False});
            `SH:    tagged Valid (tagged Mem RVMemInst{op: tagged Mem St, size: H, isUnsigned: False});
            `SW:    tagged Valid (tagged Mem RVMemInst{op: tagged Mem St, size: W, isUnsigned: False});
            // MISC-MEM
            `FENCE:     tagged Valid (tagged Fence (tagged InterCore unpack(truncate(inst[27:20]))));
            `FENCE_I:   tagged Valid (tagged Fence (tagged IntraCore FenceI));

            default: tagged Invalid;
        endcase);
endfunction

function Maybe#(ExecFunc) toExecFuncRV64I(Instruction inst);
    return (case (inst) matches
            // OP-IMM-32
            `ADDIW: tagged Valid (tagged Alu AluInst{op: Add,   w: True});
            `SLLIW: tagged Valid (tagged Alu AluInst{op: Sll,   w: True});
            `SRLIW: tagged Valid (tagged Alu AluInst{op: Srl,   w: True});
            `SRAIW: tagged Valid (tagged Alu AluInst{op: Sra,   w: True});
            // OP-32
            `ADDW:  tagged Valid (tagged Alu AluInst{op: Add,   w: True});
            `SUBW:  tagged Valid (tagged Alu AluInst{op: Sub,   w: True});
            `SLLW:  tagged Valid (tagged Alu AluInst{op: Sll,   w: True});
            `SRLW:  tagged Valid (tagged Alu AluInst{op: Srl,   w: True});
            `SRAW:  tagged Valid (tagged Alu AluInst{op: Sra,   w: True});
            // LOAD
            `LD:    tagged Valid (tagged Mem RVMemInst{op: tagged Mem Ld, size: D, isUnsigned: False});
            `LWU:   tagged Valid (tagged Mem RVMemInst{op: tagged Mem Ld, size: W, isUnsigned: True});
            // STORE
            `SD:    tagged Valid (tagged Mem RVMemInst{op: tagged Mem St, size: D, isUnsigned: False});
            // MISC-MEM
            `FENCE:     tagged Valid (tagged Fence (tagged InterCore unpack(truncate(inst[27:20]))));
            `FENCE_I:   tagged Valid (tagged Fence (tagged IntraCore FenceI));

            default: tagged Invalid;
        endcase);
endfunction

function Maybe#(ExecFunc) toExecFuncPriv(Instruction inst);
    InstructionFields instFields = unpack(inst);
    return (case (inst) matches
            `ECALL:     tagged Valid (tagged System ECall);
            `EBREAK:    tagged Valid (tagged System EBreak);
            // `URET:      tagged Valid (tagged System URet);
            `SRET:      tagged Valid (tagged System SRet);
            // `HRET:      tagged Valid (tagged System HRet);
            `MRET:      tagged Valid (tagged System MRet);
            `SFENCE_VM: tagged Valid (tagged Fence (tagged IntraCore SFenceVM));
            `WFI:       tagged Valid (tagged System WFI);
            `CSRRW:     tagged Valid (tagged System ((instFields.rd == 0) ? CSRW : CSRRW));
            `CSRRS:     tagged Valid (tagged System ((instFields.rs1 == 0) ? CSRR : CSRRS));
            `CSRRC:     tagged Valid (tagged System ((instFields.rs1 == 0) ? CSRR : CSRRC));
            `CSRRWI:    tagged Valid (tagged System ((instFields.rd == 0) ? CSRW : CSRRW));
            `CSRRSI:    tagged Valid (tagged System ((instFields.rs1 == 0) ? CSRR : CSRRS));
            `CSRRCI:    tagged Valid (tagged System ((instFields.rs1 == 0) ? CSRR : CSRRC));

            default:    tagged Invalid;
        endcase);
endfunction

function Maybe#(ExecFunc) toExecFuncRV32M(Instruction inst);
    return (case(inst) matches
            `MUL:       tagged Valid (tagged MulDiv MulDivInst{func: Mul,  w: False, sign: Signed});
            `MULH:      tagged Valid (tagged MulDiv MulDivInst{func: Mulh, w: False, sign: Signed});
            `MULHSU:    tagged Valid (tagged MulDiv MulDivInst{func: Mulh, w: False, sign: SignedUnsigned});
            `MULHU:     tagged Valid (tagged MulDiv MulDivInst{func: Mulh, w: False, sign: Unsigned});
            `DIV:       tagged Valid (tagged MulDiv MulDivInst{func: Div,  w: False, sign: Signed});
            `DIVU:      tagged Valid (tagged MulDiv MulDivInst{func: Div,  w: False, sign: Unsigned});
            `REM:       tagged Valid (tagged MulDiv MulDivInst{func: Rem,  w: False, sign: Signed});
            `REMU:      tagged Valid (tagged MulDiv MulDivInst{func: Rem,  w: False, sign: Unsigned});

            default:    tagged Invalid;
        endcase);
endfunction

function Maybe#(ExecFunc) toExecFuncRV64M(Instruction inst);
    return (case(inst) matches
            `MULW:      tagged Valid (tagged MulDiv MulDivInst{func: Mul,  w: True,  sign: Signed});
            `DIVW:      tagged Valid (tagged MulDiv MulDivInst{func: Div,  w: True,  sign: Signed});
            `DIVUW:     tagged Valid (tagged MulDiv MulDivInst{func: Div,  w: True,  sign: Unsigned});
            `REMW:      tagged Valid (tagged MulDiv MulDivInst{func: Rem,  w: True,  sign: Signed});
            `REMUW:     tagged Valid (tagged MulDiv MulDivInst{func: Rem,  w: True,  sign: Unsigned});

            default:    tagged Invalid;
        endcase);
endfunction

function Maybe#(ExecFunc) toExecFuncRV32A(Instruction inst);
    return (case (inst) matches
            `AMOADD_W:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo Add,  size: W, isUnsigned: False});
            `AMOXOR_W:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo Xor,  size: W, isUnsigned: False});
            `AMOOR_W:   tagged Valid (tagged Mem RVMemInst{op: tagged Amo Or,   size: W, isUnsigned: False});
            `AMOAND_W:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo And,  size: W, isUnsigned: False});
            `AMOMIN_W:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo Min,  size: W, isUnsigned: False});
            `AMOMAX_W:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo Max,  size: W, isUnsigned: False});
            `AMOMINU_W: tagged Valid (tagged Mem RVMemInst{op: tagged Amo Minu, size: W, isUnsigned: False});
            `AMOMAXU_W: tagged Valid (tagged Mem RVMemInst{op: tagged Amo Maxu, size: W, isUnsigned: False});
            `AMOSWAP_W: tagged Valid (tagged Mem RVMemInst{op: tagged Amo Swap, size: W, isUnsigned: False});
            `LR_W:      tagged Valid (tagged Mem RVMemInst{op: tagged Mem Lr,   size: W, isUnsigned: False});
            `SC_W:      tagged Valid (tagged Mem RVMemInst{op: tagged Mem Sc,   size: W, isUnsigned: False});

            default:    tagged Invalid;
        endcase);
endfunction

function Maybe#(ExecFunc) toExecFuncRV64A(Instruction inst);
    return (case (inst) matches
            `AMOADD_D:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo Add,  size: D, isUnsigned: False});
            `AMOXOR_D:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo Xor,  size: D, isUnsigned: False});
            `AMOOR_D:   tagged Valid (tagged Mem RVMemInst{op: tagged Amo Or,   size: D, isUnsigned: False});
            `AMOAND_D:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo And,  size: D, isUnsigned: False});
            `AMOMIN_D:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo Min,  size: D, isUnsigned: False});
            `AMOMAX_D:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo Max,  size: D, isUnsigned: False});
            `AMOMINU_D: tagged Valid (tagged Mem RVMemInst{op: tagged Amo Minu, size: D, isUnsigned: False});
            `AMOMAXU_D: tagged Valid (tagged Mem RVMemInst{op: tagged Amo Maxu, size: D, isUnsigned: False});
            `AMOSWAP_D: tagged Valid (tagged Mem RVMemInst{op: tagged Amo Swap, size: D, isUnsigned: False});
            `LR_D:      tagged Valid (tagged Mem RVMemInst{op: tagged Mem Lr,   size: D, isUnsigned: False});
            `SC_D:      tagged Valid (tagged Mem RVMemInst{op: tagged Mem Sc,   size: D, isUnsigned: False});

            default:    tagged Invalid;
        endcase);
endfunction

function Maybe#(ExecFunc) toExecFuncRV32F(Instruction inst);
    return (case (inst) matches
            `FADD_S:    tagged Valid (tagged Fpu FpuInst{func: FAdd,     precision: Single});
            `FSUB_S:    tagged Valid (tagged Fpu FpuInst{func: FSub,     precision: Single});
            `FMUL_S:    tagged Valid (tagged Fpu FpuInst{func: FMul,     precision: Single});
`ifndef NO_FDIV
            `FDIV_S:    tagged Valid (tagged Fpu FpuInst{func: FDiv,     precision: Single});
`endif
            `FSGNJ_S:   tagged Valid (tagged Fpu FpuInst{func: FSgnj,    precision: Single});
            `FSGNJN_S:  tagged Valid (tagged Fpu FpuInst{func: FSgnjn,   precision: Single});
            `FSGNJX_S:  tagged Valid (tagged Fpu FpuInst{func: FSgnjx,   precision: Single});
            `FMIN_S:    tagged Valid (tagged Fpu FpuInst{func: FMin,     precision: Single});
            `FMAX_S:    tagged Valid (tagged Fpu FpuInst{func: FMax,     precision: Single});
`ifndef NO_FSQRT
            `FSQRT_S:   tagged Valid (tagged Fpu FpuInst{func: FSqrt,    precision: Single});
`endif

            `FLE_S:     tagged Valid (tagged Fpu FpuInst{func: FLe,      precision: Single});
            `FLT_S:     tagged Valid (tagged Fpu FpuInst{func: FLt,      precision: Single});
            `FEQ_S:     tagged Valid (tagged Fpu FpuInst{func: FEq,      precision: Single});

            `FCVT_W_S:  tagged Valid (tagged Fpu FpuInst{func: FCvt_WF,  precision: Single});
            `FCVT_WU_S: tagged Valid (tagged Fpu FpuInst{func: FCvt_WUF, precision: Single});

            `FMV_X_S:   tagged Valid (tagged Fpu FpuInst{func: FMv_XF,   precision: Single});

            `FCLASS_S:  tagged Valid (tagged Fpu FpuInst{func: FClass,   precision: Single});

            `FCVT_S_W:  tagged Valid (tagged Fpu FpuInst{func: FCvt_FW,  precision: Single});
            `FCVT_S_WU: tagged Valid (tagged Fpu FpuInst{func: FCvt_FWU, precision: Single});
            `FMV_S_X:   tagged Valid (tagged Fpu FpuInst{func: FMv_FX,   precision: Single});

            // Floating-Point Memory instructions
            `FLW:       tagged Valid (tagged Mem RVMemInst{op: tagged Mem Ld, size: W, isUnsigned: True});
            `FSW:       tagged Valid (tagged Mem RVMemInst{op: tagged Mem St, size: W, isUnsigned: True});

            // FMA instructions
            `FMADD_S:   tagged Valid (tagged Fpu FpuInst{func: FMAdd,  precision: Single});
            `FMSUB_S:   tagged Valid (tagged Fpu FpuInst{func: FMSub,  precision: Single});
            `FNMSUB_S:  tagged Valid (tagged Fpu FpuInst{func: FNMSub, precision: Single});
            `FNMADD_S:  tagged Valid (tagged Fpu FpuInst{func: FNMAdd, precision: Single});

            default:    tagged Invalid;
        endcase);
endfunction

function Maybe#(ExecFunc) toExecFuncRV64F(Instruction inst);
    return (case (inst) matches
            `FCVT_L_S:  tagged Valid (tagged Fpu FpuInst{func: FCvt_LF,  precision: Single});
            `FCVT_LU_S: tagged Valid (tagged Fpu FpuInst{func: FCvt_LUF, precision: Single});
            `FCVT_S_L:  tagged Valid (tagged Fpu FpuInst{func: FCvt_FL,  precision: Single});
            `FCVT_S_LU: tagged Valid (tagged Fpu FpuInst{func: FCvt_FLU, precision: Single});

            default:    tagged Invalid;
        endcase);
endfunction

function Maybe#(ExecFunc) toExecFuncRV32D(Instruction inst);
    return (case (inst) matches
            `FADD_D:    tagged Valid (tagged Fpu FpuInst{func: FAdd,     precision: Double});
            `FSUB_D:    tagged Valid (tagged Fpu FpuInst{func: FSub,     precision: Double});
            `FMUL_D:    tagged Valid (tagged Fpu FpuInst{func: FMul,     precision: Double});
`ifndef NO_FDIV
            `FDIV_D:    tagged Valid (tagged Fpu FpuInst{func: FDiv,     precision: Double});
`endif
            `FSGNJ_D:   tagged Valid (tagged Fpu FpuInst{func: FSgnj,    precision: Double});
            `FSGNJN_D:  tagged Valid (tagged Fpu FpuInst{func: FSgnjn,   precision: Double});
            `FSGNJX_D:  tagged Valid (tagged Fpu FpuInst{func: FSgnjx,   precision: Double});
            `FMIN_D:    tagged Valid (tagged Fpu FpuInst{func: FMin,     precision: Double});
            `FMAX_D:    tagged Valid (tagged Fpu FpuInst{func: FMax,     precision: Double});
            `FCVT_S_D:  tagged Valid (tagged Fpu FpuInst{func: FCvt_FF,  precision: Single});
            `FCVT_D_S:  tagged Valid (tagged Fpu FpuInst{func: FCvt_FF,  precision: Double});
`ifndef NO_FSQRT
            `FSQRT_D:   tagged Valid (tagged Fpu FpuInst{func: FSqrt,    precision: Double});
`endif

            `FLE_D:     tagged Valid (tagged Fpu FpuInst{func: FLe,      precision: Double});
            `FLT_D:     tagged Valid (tagged Fpu FpuInst{func: FLt,      precision: Double});
            `FEQ_D:     tagged Valid (tagged Fpu FpuInst{func: FEq,      precision: Double});

            `FCVT_W_D:  tagged Valid (tagged Fpu FpuInst{func: FCvt_WF,  precision: Double});
            `FCVT_WU_D: tagged Valid (tagged Fpu FpuInst{func: FCvt_WUF, precision: Double});

            `FCLASS_D:  tagged Valid (tagged Fpu FpuInst{func: FClass,   precision: Double});

            `FCVT_D_W:  tagged Valid (tagged Fpu FpuInst{func: FCvt_FW,  precision: Double});
            `FCVT_D_WU: tagged Valid (tagged Fpu FpuInst{func: FCvt_FWU, precision: Double});

            // Floating-Point Memory instructions
            `FLD:       tagged Valid (tagged Mem RVMemInst{op: tagged Mem Ld, size: D, isUnsigned: False});
            `FSD:       tagged Valid (tagged Mem RVMemInst{op: tagged Mem St, size: D, isUnsigned: False});

            // FMA instructions
            `FMADD_D:   tagged Valid (tagged Fpu FpuInst{func: FMAdd,  precision: Double});
            `FMSUB_D:   tagged Valid (tagged Fpu FpuInst{func: FMSub,  precision: Double});
            `FNMSUB_D:  tagged Valid (tagged Fpu FpuInst{func: FNMSub, precision: Double});
            `FNMADD_D:  tagged Valid (tagged Fpu FpuInst{func: FNMAdd, precision: Double});

            default:    tagged Invalid;
        endcase);
endfunction

function Maybe#(ExecFunc) toExecFuncRV64D(Instruction inst);
    return (case (inst) matches
            `FCVT_L_D:  tagged Valid (tagged Fpu FpuInst{func: FCvt_LF,  precision: Double});
            `FCVT_LU_D: tagged Valid (tagged Fpu FpuInst{func: FCvt_LUF, precision: Double});
            `FMV_X_D:   tagged Valid (tagged Fpu FpuInst{func: FMv_XF,   precision: Double});
            `FCVT_D_L:  tagged Valid (tagged Fpu FpuInst{func: FCvt_FL,  precision: Double});
            `FCVT_D_LU: tagged Valid (tagged Fpu FpuInst{func: FCvt_FLU, precision: Double});
            `FMV_D_X:   tagged Valid (tagged Fpu FpuInst{func: FMv_FX,   precision: Double});

            default:    tagged Invalid;
        endcase);
endfunction

// This returns a valid result if the instruction is legal, otherwise it
// returns a tagged invalid.
(* noinline *)
function Maybe#(RVDecodedInst) decodeInst(Instruction inst);
    // Construct a vector of all the results
    // We will apply a fold function on this vector to find the valid result
    let decoderResults = vec(
            toExecFuncRV32I(inst),
            toExecFuncRV64I(inst),
`ifdef m
            toExecFuncRV32M(inst),
            toExecFuncRV64M(inst),
`endif
`ifdef a
            toExecFuncRV32A(inst),
            toExecFuncRV64A(inst),
`endif
`ifdef f
            toExecFuncRV32F(inst),
            toExecFuncRV64F(inst),
`endif
`ifdef d
            toExecFuncRV32D(inst),
            toExecFuncRV64D(inst),
`endif
            toExecFuncPriv(inst)
        );

    // This fold is equivalent to going down the list of decoder results and
    // returning the first valid result
    function Maybe#(t) fold_op(Maybe#(t) x, Maybe#(t) y) provisos (Bits#(t, tSz));
        return isValid(x) ? x : y;
    endfunction
    let execFunc = fold(fold_op, decoderResults);

    if (execFunc matches tagged Valid .validExecFunc) begin
        InstType instType = toInstType(inst);
        return tagged Valid (RVDecodedInst {
                execFunc: validExecFunc,
                imm: instType.imm,
                rs1: instType.rs1,
                rs2: instType.rs2,
                rs3: instType.rs3,
                dst: instType.dst,
                inst: inst });
    end else begin
        return tagged Invalid;
    end
endfunction

// This function is used to get the immediate value from a decoded instruction
function Maybe#(Data) getImmediate(ImmType imm, Instruction inst);
    Data immI  = signExtend(inst[31:20]);
    Data immS  = signExtend({inst[31:25], inst[11:7]});
    Data immSB = signExtend({inst[31], inst[7], inst[30:25], inst[11:8], 1'b0});
    Data immU  = signExtend({inst[31:12], 12'b0});
    Data immUJ = signExtend({inst[31], inst[19:12], inst[20], inst[30:21], 1'b0});
    Data immZ  = zeroExtend(inst[19:15]);
    return (case (imm)
            S:  tagged Valid immS;
            SB: tagged Valid immSB;
            U:  tagged Valid immU;
            UJ: tagged Valid immUJ;
            I:  tagged Valid immI;
            Z:  tagged Valid immZ;
            default: tagged Invalid;
        endcase);
endfunction

function Bit#(2) getMinPriv(Instruction inst);
    return (getInstFields(inst).opcode == System) ? inst[29:28] : prvU;
endfunction

// 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"
`include "Opcodes.defines"

import Opcodes::*;
import RVTypes::*;
import Vector::*;
import DefaultValue::*;

function Maybe#(ExecFunc) toExecFuncI(Instruction inst);
    return (case (inst) matches
            // BRANCH
            `BEQ:   tagged Valid (tagged Br Eq);
            `BNE:   tagged Valid (tagged Br Neq);
            `BLT:   tagged Valid (tagged Br Lt);
            `BGE:   tagged Valid (tagged Br Ge);
            `BLTU:  tagged Valid (tagged Br Ltu);
            `BGEU:  tagged Valid (tagged Br Geu);
            // JALR
            `JALR:  tagged Valid (tagged Br Jalr);
            // JAL
            `JAL:   tagged Valid (tagged Br Jal);

            // LUI
            `LUI:   tagged Valid (tagged Alu AluInst{op: Lui,   w: False});
            // AUIPC
            `AUIPC: tagged Valid (tagged Alu AluInst{op: Auipc, w: False});
            // OP-IMM
            `ADDI:  tagged Valid (tagged Alu AluInst{op: Add,   w: False});
            `SLLI:  tagged Valid (tagged Alu AluInst{op: Sll,   w: False});
            `SLTI:  tagged Valid (tagged Alu AluInst{op: Slt,   w: False});
            `SLTIU: tagged Valid (tagged Alu AluInst{op: Sltu,  w: False});
            `XORI:  tagged Valid (tagged Alu AluInst{op: Xor,   w: False});
            `SRLI:  tagged Valid (tagged Alu AluInst{op: Srl,   w: False});
            `SRAI:  tagged Valid (tagged Alu AluInst{op: Sra,   w: False});
            `ORI:   tagged Valid (tagged Alu AluInst{op: Or,    w: False});
            `ANDI:  tagged Valid (tagged Alu AluInst{op: And,   w: False});
            // OP
            `ADD:   tagged Valid (tagged Alu AluInst{op: Add,   w: False});
            `SUB:   tagged Valid (tagged Alu AluInst{op: Sub,   w: False});
            `SLL:   tagged Valid (tagged Alu AluInst{op: Sll,   w: False});
            `SLT:   tagged Valid (tagged Alu AluInst{op: Slt,   w: False});
            `SLTU:  tagged Valid (tagged Alu AluInst{op: Sltu,  w: False});
            `XOR:   tagged Valid (tagged Alu AluInst{op: Xor,   w: False});
            `SRL:   tagged Valid (tagged Alu AluInst{op: Srl,   w: False});
            `SRA:   tagged Valid (tagged Alu AluInst{op: Sra,   w: False});
            `OR:    tagged Valid (tagged Alu AluInst{op: Or,    w: False});
            `AND:   tagged Valid (tagged Alu AluInst{op: And,   w: False});
            // OP-IMM-32
            `ADDIW: tagged Valid (tagged Alu AluInst{op: Add,   w: True});
            `SLLIW: tagged Valid (tagged Alu AluInst{op: Sll,   w: True});
            `SRLIW: tagged Valid (tagged Alu AluInst{op: Srl,   w: True});
            `SRAIW: tagged Valid (tagged Alu AluInst{op: Sra,   w: True});
            // OP-32
            `ADDW:  tagged Valid (tagged Alu AluInst{op: Add,   w: True});
            `SUBW:  tagged Valid (tagged Alu AluInst{op: Sub,   w: True});
            `SLLW:  tagged Valid (tagged Alu AluInst{op: Sll,   w: True});
            `SRLW:  tagged Valid (tagged Alu AluInst{op: Srl,   w: True});
            `SRAW:  tagged Valid (tagged Alu AluInst{op: Sra,   w: True});

            // LOAD
            `LB:    tagged Valid (tagged Mem RVMemInst{op: tagged Mem Ld, size: B, isUnsigned: False});
            `LH:    tagged Valid (tagged Mem RVMemInst{op: tagged Mem Ld, size: H, isUnsigned: False});
            `LW:    tagged Valid (tagged Mem RVMemInst{op: tagged Mem Ld, size: W, isUnsigned: False});
            `LBU:   tagged Valid (tagged Mem RVMemInst{op: tagged Mem Ld, size: B, isUnsigned: True});
            `LHU:   tagged Valid (tagged Mem RVMemInst{op: tagged Mem Ld, size: H, isUnsigned: True});
            // 64-bit Only
            `LD:    tagged Valid (tagged Mem RVMemInst{op: tagged Mem Ld, size: D, isUnsigned: False});
            `LWU:   tagged Valid (tagged Mem RVMemInst{op: tagged Mem Ld, size: W, isUnsigned: True});
            // STORE
            `SB:    tagged Valid (tagged Mem RVMemInst{op: tagged Mem St, size: B, isUnsigned: False});
            `SH:    tagged Valid (tagged Mem RVMemInst{op: tagged Mem St, size: H, isUnsigned: False});
            `SW:    tagged Valid (tagged Mem RVMemInst{op: tagged Mem St, size: W, isUnsigned: False});
            // 64-bit Only
            `SD:    tagged Valid (tagged Mem RVMemInst{op: tagged Mem St, size: D, isUnsigned: False});

            // MISC-MEM
            `FENCE:     tagged Valid (tagged Fence (tagged InterCore unpack(truncate(inst[27:20]))));
            `FENCE_I:   tagged Valid (tagged Fence (tagged IntraCore FenceI));

            default: tagged Invalid;
        endcase);
endfunction

function Maybe#(ExecFunc) toExecFuncSupervisor(Instruction inst);
    InstructionFields instFields = unpack(inst);
    return (case (inst) matches
            `ECALL:     tagged Valid (tagged System ECall);
            `EBREAK:    tagged Valid (tagged System EBreak);
            // `URET:      tagged Valid (tagged System URet);
            `SRET:      tagged Valid (tagged System SRet);
            // `HRET:      tagged Valid (tagged System HRet);
            `MRET:      tagged Valid (tagged System MRet);
            `SFENCE_VM: tagged Valid (tagged Fence (tagged IntraCore SFenceVM));
            `WFI:       tagged Valid (tagged System WFI);
            `CSRRW:     tagged Valid (tagged System ((instFields.rd == 0) ? CSRW : CSRRW));
            `CSRRS:     tagged Valid (tagged System ((instFields.rs1 == 0) ? CSRR : CSRRS));
            `CSRRC:     tagged Valid (tagged System ((instFields.rs1 == 0) ? CSRR : CSRRC));
            `CSRRWI:    tagged Valid (tagged System ((instFields.rd == 0) ? CSRW : CSRRW));
            `CSRRSI:    tagged Valid (tagged System ((instFields.rs1 == 0) ? CSRR : CSRRS));
            `CSRRCI:    tagged Valid (tagged System ((instFields.rs1 == 0) ? CSRR : CSRRC));
            default:    tagged Invalid;
        endcase);
endfunction

function Maybe#(ExecFunc) toExecFuncM(Instruction inst);
    return (case(inst) matches
            `MUL:       tagged Valid (tagged MulDiv MulDivInst{func: Mul,  w: False, sign: Signed});
            `MULH:      tagged Valid (tagged MulDiv MulDivInst{func: Mulh, w: False, sign: Signed});
            `MULHSU:    tagged Valid (tagged MulDiv MulDivInst{func: Mulh, w: False, sign: SignedUnsigned});
            `MULHU:     tagged Valid (tagged MulDiv MulDivInst{func: Mulh, w: False, sign: Unsigned});
            `DIV:       tagged Valid (tagged MulDiv MulDivInst{func: Div,  w: False, sign: Signed});
            `DIVU:      tagged Valid (tagged MulDiv MulDivInst{func: Div,  w: False, sign: Unsigned});
            `REM:       tagged Valid (tagged MulDiv MulDivInst{func: Rem,  w: False, sign: Signed});
            `REMU:      tagged Valid (tagged MulDiv MulDivInst{func: Rem,  w: False, sign: Unsigned});
            // RV64M Only
            `MULW:      tagged Valid (tagged MulDiv MulDivInst{func: Mul,  w: True,  sign: Signed});
            `DIVW:      tagged Valid (tagged MulDiv MulDivInst{func: Div,  w: True,  sign: Signed});
            `DIVUW:     tagged Valid (tagged MulDiv MulDivInst{func: Div,  w: True,  sign: Unsigned});
            `REMW:      tagged Valid (tagged MulDiv MulDivInst{func: Rem,  w: True,  sign: Signed});
            `REMUW:     tagged Valid (tagged MulDiv MulDivInst{func: Rem,  w: True,  sign: Unsigned});

            default:    tagged Invalid;
        endcase);
endfunction

function Maybe#(ExecFunc) toExecFuncA(Instruction inst);
    return (case (inst) matches
            // RV32A
            `AMOADD_W:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo Add,  size: W, isUnsigned: False});
            `AMOXOR_W:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo Xor,  size: W, isUnsigned: False});
            `AMOOR_W:   tagged Valid (tagged Mem RVMemInst{op: tagged Amo Or,   size: W, isUnsigned: False});
            `AMOAND_W:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo And,  size: W, isUnsigned: False});
            `AMOMIN_W:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo Min,  size: W, isUnsigned: False});
            `AMOMAX_W:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo Max,  size: W, isUnsigned: False});
            `AMOMINU_W: tagged Valid (tagged Mem RVMemInst{op: tagged Amo Minu, size: W, isUnsigned: False});
            `AMOMAXU_W: tagged Valid (tagged Mem RVMemInst{op: tagged Amo Maxu, size: W, isUnsigned: False});
            `AMOSWAP_W: tagged Valid (tagged Mem RVMemInst{op: tagged Amo Swap, size: W, isUnsigned: False});
            `LR_W:      tagged Valid (tagged Mem RVMemInst{op: tagged Mem Lr,   size: W, isUnsigned: False});
            `SC_W:      tagged Valid (tagged Mem RVMemInst{op: tagged Mem Sc,   size: W, isUnsigned: False});
            // RV64A
            `AMOADD_D:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo Add,  size: D, isUnsigned: False});
            `AMOXOR_D:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo Xor,  size: D, isUnsigned: False});
            `AMOOR_D:   tagged Valid (tagged Mem RVMemInst{op: tagged Amo Or,   size: D, isUnsigned: False});
            `AMOAND_D:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo And,  size: D, isUnsigned: False});
            `AMOMIN_D:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo Min,  size: D, isUnsigned: False});
            `AMOMAX_D:  tagged Valid (tagged Mem RVMemInst{op: tagged Amo Max,  size: D, isUnsigned: False});
            `AMOMINU_D: tagged Valid (tagged Mem RVMemInst{op: tagged Amo Minu, size: D, isUnsigned: False});
            `AMOMAXU_D: tagged Valid (tagged Mem RVMemInst{op: tagged Amo Maxu, size: D, isUnsigned: False});
            `AMOSWAP_D: tagged Valid (tagged Mem RVMemInst{op: tagged Amo Swap, size: D, isUnsigned: False});
            `LR_D:      tagged Valid (tagged Mem RVMemInst{op: tagged Mem Lr,   size: D, isUnsigned: False});
            `SC_D:      tagged Valid (tagged Mem RVMemInst{op: tagged Mem Sc,   size: D, isUnsigned: False});

            default:    tagged Invalid;
        endcase);
endfunction

function Maybe#(ExecFunc) toExecFuncFD(Instruction inst);
    return (case (inst) matches
            `FADD_S:    tagged Valid (tagged Fpu FpuInst{func: FAdd,     precision: Single});
            `FSUB_S:    tagged Valid (tagged Fpu FpuInst{func: FSub,     precision: Single});
            `FMUL_S:    tagged Valid (tagged Fpu FpuInst{func: FMul,     precision: Single});
`ifndef NO_FDIV
            `FDIV_S:    tagged Valid (tagged Fpu FpuInst{func: FDiv,     precision: Single});
`endif
            `FSGNJ_S:   tagged Valid (tagged Fpu FpuInst{func: FSgnj,    precision: Single});
            `FSGNJN_S:  tagged Valid (tagged Fpu FpuInst{func: FSgnjn,   precision: Single});
            `FSGNJX_S:  tagged Valid (tagged Fpu FpuInst{func: FSgnjx,   precision: Single});
            `FMIN_S:    tagged Valid (tagged Fpu FpuInst{func: FMin,     precision: Single});
            `FMAX_S:    tagged Valid (tagged Fpu FpuInst{func: FMax,     precision: Single});
`ifndef NO_FSQRT
            `FSQRT_S:   tagged Valid (tagged Fpu FpuInst{func: FSqrt,    precision: Single});
`endif

            `FADD_D:    tagged Valid (tagged Fpu FpuInst{func: FAdd,     precision: Double});
            `FSUB_D:    tagged Valid (tagged Fpu FpuInst{func: FSub,     precision: Double});
            `FMUL_D:    tagged Valid (tagged Fpu FpuInst{func: FMul,     precision: Double});
`ifndef NO_FDIV
            `FDIV_D:    tagged Valid (tagged Fpu FpuInst{func: FDiv,     precision: Double});
`endif
            `FSGNJ_D:   tagged Valid (tagged Fpu FpuInst{func: FSgnj,    precision: Double});
            `FSGNJN_D:  tagged Valid (tagged Fpu FpuInst{func: FSgnjn,   precision: Double});
            `FSGNJX_D:  tagged Valid (tagged Fpu FpuInst{func: FSgnjx,   precision: Double});
            `FMIN_D:    tagged Valid (tagged Fpu FpuInst{func: FMin,     precision: Double});
            `FMAX_D:    tagged Valid (tagged Fpu FpuInst{func: FMax,     precision: Double});
            `FCVT_S_D:  tagged Valid (tagged Fpu FpuInst{func: FCvt_FF,  precision: Single});
            `FCVT_D_S:  tagged Valid (tagged Fpu FpuInst{func: FCvt_FF,  precision: Double});
`ifndef NO_FSQRT
            `FSQRT_D:   tagged Valid (tagged Fpu FpuInst{func: FSqrt,    precision: Double});
`endif

            `FLE_S:     tagged Valid (tagged Fpu FpuInst{func: FLe,      precision: Single});
            `FLT_S:     tagged Valid (tagged Fpu FpuInst{func: FLt,      precision: Single});
            `FEQ_S:     tagged Valid (tagged Fpu FpuInst{func: FEq,      precision: Single});
            `FLE_D:     tagged Valid (tagged Fpu FpuInst{func: FLe,      precision: Double});
            `FLT_D:     tagged Valid (tagged Fpu FpuInst{func: FLt,      precision: Double});
            `FEQ_D:     tagged Valid (tagged Fpu FpuInst{func: FEq,      precision: Double});

            `FCVT_W_S:  tagged Valid (tagged Fpu FpuInst{func: FCvt_WF,  precision: Single});
            `FCVT_WU_S: tagged Valid (tagged Fpu FpuInst{func: FCvt_WUF, precision: Single});
            `FCVT_L_S:  tagged Valid (tagged Fpu FpuInst{func: FCvt_LF,  precision: Single});
            `FCVT_LU_S: tagged Valid (tagged Fpu FpuInst{func: FCvt_LUF, precision: Single});

            `FMV_X_S:   tagged Valid (tagged Fpu FpuInst{func: FMv_XF,   precision: Single});

            `FCLASS_S:  tagged Valid (tagged Fpu FpuInst{func: FClass,   precision: Single});

            `FCVT_W_D:  tagged Valid (tagged Fpu FpuInst{func: FCvt_WF,  precision: Double});
            `FCVT_WU_D: tagged Valid (tagged Fpu FpuInst{func: FCvt_WUF, precision: Double});
            `FCVT_L_D:  tagged Valid (tagged Fpu FpuInst{func: FCvt_LF,  precision: Double});
            `FCVT_LU_D: tagged Valid (tagged Fpu FpuInst{func: FCvt_LUF, precision: Double});

            `FMV_X_D:   tagged Valid (tagged Fpu FpuInst{func: FMv_XF,   precision: Double});

            `FCLASS_D:  tagged Valid (tagged Fpu FpuInst{func: FClass,   precision: Double});

            `FCVT_S_W:  tagged Valid (tagged Fpu FpuInst{func: FCvt_FW,  precision: Single});
            `FCVT_S_WU: tagged Valid (tagged Fpu FpuInst{func: FCvt_FWU, precision: Single});
            `FCVT_S_L:  tagged Valid (tagged Fpu FpuInst{func: FCvt_FL,  precision: Single});
            `FCVT_S_LU: tagged Valid (tagged Fpu FpuInst{func: FCvt_FLU, precision: Single});
            `FMV_S_X:   tagged Valid (tagged Fpu FpuInst{func: FMv_FX,   precision: Single});

            `FCVT_D_W:  tagged Valid (tagged Fpu FpuInst{func: FCvt_FW,  precision: Double});
            `FCVT_D_WU: tagged Valid (tagged Fpu FpuInst{func: FCvt_FWU, precision: Double});
            `FCVT_D_L:  tagged Valid (tagged Fpu FpuInst{func: FCvt_FL,  precision: Double});
            `FCVT_D_LU: tagged Valid (tagged Fpu FpuInst{func: FCvt_FLU, precision: Double});
            `FMV_D_X:   tagged Valid (tagged Fpu FpuInst{func: FMv_FX,   precision: Double});

            // Floating-Point Memory instructions
            `FLW:       tagged Valid (tagged Mem RVMemInst{op: tagged Mem Ld, size: W, isUnsigned: True});
            `FLD:       tagged Valid (tagged Mem RVMemInst{op: tagged Mem Ld, size: D, isUnsigned: False});
            `FSW:       tagged Valid (tagged Mem RVMemInst{op: tagged Mem St, size: W, isUnsigned: True});
            `FSD:       tagged Valid (tagged Mem RVMemInst{op: tagged Mem St, size: D, isUnsigned: False});

            // FMA instructions
            `FMADD_S:   tagged Valid (tagged Fpu FpuInst{func: FMAdd,  precision: Single});
            `FMSUB_S:   tagged Valid (tagged Fpu FpuInst{func: FMSub,  precision: Single});
            `FNMSUB_S:  tagged Valid (tagged Fpu FpuInst{func: FNMSub, precision: Single});
            `FNMADD_S:  tagged Valid (tagged Fpu FpuInst{func: FNMAdd, precision: Single});
            `FMADD_D:   tagged Valid (tagged Fpu FpuInst{func: FMAdd,  precision: Double});
            `FMSUB_D:   tagged Valid (tagged Fpu FpuInst{func: FMSub,  precision: Double});
            `FNMSUB_D:  tagged Valid (tagged Fpu FpuInst{func: FNMSub, precision: Double});
            `FNMADD_D:  tagged Valid (tagged Fpu FpuInst{func: FNMAdd, precision: Double});

            default:    tagged Invalid;
        endcase);
endfunction

// This returns a valid result if the instruction is legal, otherwise it
// returns a tagged invalid.
function Maybe#(RVDecodedInst) decodeInst(Instruction inst);
    Maybe#(ExecFunc) execFunc = toExecFuncI(inst);
    if (!isValid(execFunc)) begin
        execFunc = toExecFuncSupervisor(inst);
    end

`ifdef m
    if (`m && !isValid(execFunc)) begin
        execFunc = toExecFuncM(inst);
    end
`endif

`ifdef a
    if (`a && !isValid(execFunc)) begin
        execFunc = toExecFuncA(inst);
    end
`endif

`ifdef f
    // TODO: simplify decoder if f is enabled but not d
    if (`f && !isValid(execFunc)) begin
        execFunc = toExecFuncFD(inst);
    end
`endif

    if (execFunc matches tagged Valid .validExecFunc) begin
        InstType instType = toInstType(inst);
        return tagged Valid (RVDecodedInst {
                execFunc: validExecFunc,
                imm: instType.imm,
                rs1: instType.rs1,
                rs2: instType.rs2,
                rs3: instType.rs3,
                dst: instType.dst,
                inst: inst });
    end else begin
        return tagged Invalid;
    end
endfunction

// This function is used to get the immediate value from a decoded instruction
function Maybe#(Data) getImmediate(ImmType imm, Instruction inst);
    Data immI  = signExtend(inst[31:20]);
    Data immS  = signExtend({inst[31:25], inst[11:7]});
    Data immSB = signExtend({inst[31], inst[7], inst[30:25], inst[11:8], 1'b0});
    Data immU  = signExtend({inst[31:12], 12'b0});
    Data immUJ = signExtend({inst[31], inst[19:12], inst[20], inst[30:21], 1'b0});
    Data immZ  = zeroExtend(inst[19:15]);
    return (case (imm)
            S:  tagged Valid immS;
            SB: tagged Valid immSB;
            U:  tagged Valid immU;
            UJ: tagged Valid immUJ;
            I:  tagged Valid immI;
            Z:  tagged Valid immZ;
            default: tagged Invalid;
        endcase);
endfunction

function Bit#(2) getMinPriv(Instruction inst);
    return (getInstFields(inst).opcode == System) ? inst[29:28] : prvU;
endfunction


fixed clock domains

fixed clock domains

/*
   ../../generated/scripts/importbvi.py
   -I
   APRP
   -P
   APRP
   -r
   axi_aresetn
   -c
   axi_aclk_out
   -c
   axi_ctl_aclk_out
   -c
   REFCLK
   -o
   AxiPcieRootPort.bsv
   /home/jamey/miniitx100/miniitx100.srcs/sources_1/ip/axi_pcie_0/axi_pcie_0_stub.v
*/

import Clocks::*;
import DefaultValue::*;
import XilinxCells::*;
import GetPut::*;
import AxiBits::*;

(* always_ready, always_enabled *)
interface AprpAxi;
    interface Clock     aclk_out;
    interface Clock     ctl_aclk_out;
endinterface
(* always_ready, always_enabled *)
interface AprpInterrupt;
    method Bit#(1)     out();
endinterface
(* always_ready, always_enabled *)
interface AprpIntx;
    method Bit#(1)     msi_grant();
    method Action      msi_request(Bit#(1) v);
endinterface
(* always_ready, always_enabled *)
interface AprpM_axi;
    method Bit#(32)     araddr();
    method Bit#(2)     arburst();
    method Bit#(4)     arcache();
    method Bit#(8)     arlen();
    method Bit#(1)     arlock();
    method Bit#(3)     arprot();
    method Action      arready(Bit#(1) v);
    method Bit#(3)     arsize();
    method Bit#(1)     arvalid();
    method Bit#(32)     awaddr();
    method Bit#(2)     awburst();
    method Bit#(4)     awcache();
    method Bit#(8)     awlen();
    method Bit#(1)     awlock();
    method Bit#(3)     awprot();
    method Action      awready(Bit#(1) v);
    method Bit#(3)     awsize();
    method Bit#(1)     awvalid();
    method Bit#(1)     bready();
    method Action      bresp(Bit#(2) v);
    method Action      bvalid(Bit#(1) v);
    method Action      rdata(Bit#(64) v);
    method Action      rlast(Bit#(1) v);
    method Bit#(1)     rready();
    method Action      rresp(Bit#(2) v);
    method Action      rvalid(Bit#(1) v);
    method Bit#(64)     wdata();
    method Bit#(1)     wlast();
    method Action      wready(Bit#(1) v);
    method Bit#(8)     wstrb();
    method Bit#(1)     wvalid();
endinterface
(* always_ready, always_enabled *)
interface AprpMmcm;
    method Bit#(1)     lock();
endinterface
(* always_ready, always_enabled *)
interface AprpMsi;
    method Bit#(1)     enable();
    method Action      vector_num(Bit#(5) v);
    method Bit#(3)     vector_width();
endinterface
(* always_ready, always_enabled *)
interface AprpPci;
    method Action      exp_rxn(Bit#(4) v);
    method Action      exp_rxp(Bit#(4) v);
    method Bit#(4)     exp_txn();
    method Bit#(4)     exp_txp();
endinterface
(* always_ready, always_enabled *)
interface AprpS_axi;
    method Action      araddr(Bit#(32) v);
    method Action      arburst(Bit#(2) v);
    method Action      arid(Bit#(4) v);
    method Action      arlen(Bit#(8) v);
    method Bit#(1)     arready();
    method Action      arregion(Bit#(4) v);
    method Action      arsize(Bit#(3) v);
    method Action      arvalid(Bit#(1) v);
    method Action      awaddr(Bit#(32) v);
    method Action      awburst(Bit#(2) v);
    method Action      awid(Bit#(4) v);
    method Action      awlen(Bit#(8) v);
    method Bit#(1)     awready();
    method Action      awregion(Bit#(4) v);
    method Action      awsize(Bit#(3) v);
    method Action      awvalid(Bit#(1) v);
    method Bit#(4)     bid();
    method Action      bready(Bit#(1) v);
    method Bit#(2)     bresp();
    method Bit#(1)     bvalid();
    method Bit#(64)     rdata();
    method Bit#(4)     rid();
    method Bit#(1)     rlast();
    method Action      rready(Bit#(1) v);
    method Bit#(2)     rresp();
    method Bit#(1)     rvalid();
    method Action      wdata(Bit#(64) v);
    method Action      wlast(Bit#(1) v);
    method Bit#(1)     wready();
    method Action      wstrb(Bit#(8) v);
    method Action      wvalid(Bit#(1) v);
endinterface
(* always_ready, always_enabled *)
interface AprpS_axi_ctl;
    method Action      araddr(Bit#(32) v);
    method Bit#(1)     arready();
    method Action      arvalid(Bit#(1) v);
    method Action      awaddr(Bit#(32) v);
    method Bit#(1)     awready();
    method Action      awvalid(Bit#(1) v);
    method Action      bready(Bit#(1) v);
    method Bit#(2)     bresp();
    method Bit#(1)     bvalid();
    method Bit#(32)     rdata();
    method Action      rready(Bit#(1) v);
    method Bit#(2)     rresp();
    method Bit#(1)     rvalid();
    method Action      wdata(Bit#(32) v);
    method Bit#(1)     wready();
    method Action      wstrb(Bit#(4) v);
    method Action      wvalid(Bit#(1) v);
endinterface
(* always_ready, always_enabled *)
interface APRP;
    interface AprpAxi     axi;
    interface AprpInterrupt     interrupt;
    interface AprpIntx     intx;
    interface AprpM_axi     m_axi;
    interface AprpMmcm     mmcm;
    interface AprpMsi     msi;
    interface AprpPci     pci;
    interface AprpS_axi     s_axi;
    interface AprpS_axi_ctl     s_axi_ctl;
endinterface
import "BVI" axi_pcie_rp =
module mkAPRP#(Clock refclk, Reset reset, Clock axi_aclk, Reset axi_aresetn, Clock axi_ctl_aclk, Reset axi_ctl_aresetn)(APRP);
    default_clock clk();
    default_reset rst();
    input_clock axi_aclk() = axi_aclk;
    input_clock axi_ctl_aclk() = axi_ctl_aclk;
        input_reset reset(axi_aresetn) = reset;
        input_reset axi_aresetn() clocked_by (axi_aclk) = axi_aresetn;
	input_reset axi_ctl_aresetn() clocked_by (axi_ctl_aclk) = axi_ctl_aresetn;
    input_clock refclk(REFCLK) = refclk;
    interface AprpAxi     axi;
        output_clock aclk_out(axi_aclk_out);
        output_clock ctl_aclk_out(axi_ctl_aclk_out);
    endinterface
    interface AprpInterrupt     interrupt;
        method interrupt_out out();
    endinterface
    interface AprpIntx     intx;
        method INTX_MSI_Grant msi_grant();
        method msi_request(INTX_MSI_Request) enable((*inhigh*) EN_INTX_MSI_Request);
    endinterface
    interface AprpM_axi     m_axi;
        method m_axi_araddr araddr();
        method m_axi_arburst arburst();
        method m_axi_arcache arcache();
        method m_axi_arlen arlen();
        method m_axi_arlock arlock();
        method m_axi_arprot arprot();
        method arready(m_axi_arready) enable((*inhigh*) EN_m_axi_arready);
        method m_axi_arsize arsize();
        method m_axi_arvalid arvalid();
        method m_axi_awaddr awaddr();
        method m_axi_awburst awburst();
        method m_axi_awcache awcache();
        method m_axi_awlen awlen();
        method m_axi_awlock awlock();
        method m_axi_awprot awprot();
        method awready(m_axi_awready) enable((*inhigh*) EN_m_axi_awready);
        method m_axi_awsize awsize();
        method m_axi_awvalid awvalid();
        method m_axi_bready bready();
        method bresp(m_axi_bresp) enable((*inhigh*) EN_m_axi_bresp);
        method bvalid(m_axi_bvalid) enable((*inhigh*) EN_m_axi_bvalid);
        method rdata(m_axi_rdata) enable((*inhigh*) EN_m_axi_rdata);
        method rlast(m_axi_rlast) enable((*inhigh*) EN_m_axi_rlast);
        method m_axi_rready rready();
        method rresp(m_axi_rresp) enable((*inhigh*) EN_m_axi_rresp);
        method rvalid(m_axi_rvalid) enable((*inhigh*) EN_m_axi_rvalid);
        method m_axi_wdata wdata();
        method m_axi_wlast wlast();
        method wready(m_axi_wready) enable((*inhigh*) EN_m_axi_wready);
        method m_axi_wstrb wstrb();
        method m_axi_wvalid wvalid();
    endinterface
    interface AprpMmcm     mmcm;
        method mmcm_lock lock();
    endinterface
    interface AprpMsi     msi;
        method MSI_enable enable();
        method vector_num(MSI_Vector_Num) enable((*inhigh*) EN_MSI_Vector_Num);
        method MSI_Vector_Width vector_width();
    endinterface
    interface AprpPci     pci;
        method exp_rxn(pci_exp_rxn) enable((*inhigh*) EN_pci_exp_rxn);
        method exp_rxp(pci_exp_rxp) enable((*inhigh*) EN_pci_exp_rxp);
        method pci_exp_txn exp_txn();
        method pci_exp_txp exp_txp();
    endinterface
    interface AprpS_axi     s_axi;
        method araddr(s_axi_araddr) enable((*inhigh*) EN_s_axi_araddr) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method arburst(s_axi_arburst) enable((*inhigh*) EN_s_axi_arburst) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method arid(s_axi_arid) enable((*inhigh*) EN_s_axi_arid) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method arlen(s_axi_arlen) enable((*inhigh*) EN_s_axi_arlen) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method s_axi_arready arready() clocked_by(axi_aclk) reset_by (axi_aresetn);
        method arregion(s_axi_arregion) enable((*inhigh*) EN_s_axi_arregion) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method arsize(s_axi_arsize) enable((*inhigh*) EN_s_axi_arsize) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method arvalid(s_axi_arvalid) enable((*inhigh*) EN_s_axi_arvalid) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method awaddr(s_axi_awaddr) enable((*inhigh*) EN_s_axi_awaddr) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method awburst(s_axi_awburst) enable((*inhigh*) EN_s_axi_awburst) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method awid(s_axi_awid) enable((*inhigh*) EN_s_axi_awid) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method awlen(s_axi_awlen) enable((*inhigh*) EN_s_axi_awlen) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method s_axi_awready awready() clocked_by(axi_aclk) reset_by (axi_aresetn);
        method awregion(s_axi_awregion) enable((*inhigh*) EN_s_axi_awregion) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method awsize(s_axi_awsize) enable((*inhigh*) EN_s_axi_awsize) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method awvalid(s_axi_awvalid) enable((*inhigh*) EN_s_axi_awvalid) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method s_axi_bid bid() clocked_by(axi_aclk) reset_by (axi_aresetn);
        method bready(s_axi_bready) enable((*inhigh*) EN_s_axi_bready) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method s_axi_bresp bresp() clocked_by(axi_aclk) reset_by (axi_aresetn);
        method s_axi_bvalid bvalid() clocked_by(axi_aclk) reset_by (axi_aresetn);
        method s_axi_rdata rdata() clocked_by(axi_aclk) reset_by (axi_aresetn);
        method s_axi_rid rid() clocked_by(axi_aclk) reset_by (axi_aresetn);
        method s_axi_rlast rlast() clocked_by(axi_aclk) reset_by (axi_aresetn);
        method rready(s_axi_rready) enable((*inhigh*) EN_s_axi_rready) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method s_axi_rresp rresp() clocked_by(axi_aclk) reset_by (axi_aresetn);
        method s_axi_rvalid rvalid() clocked_by(axi_aclk) reset_by (axi_aresetn);
        method wdata(s_axi_wdata) enable((*inhigh*) EN_s_axi_wdata) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method wlast(s_axi_wlast) enable((*inhigh*) EN_s_axi_wlast) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method s_axi_wready wready() clocked_by(axi_aclk) reset_by (axi_aresetn);
        method wstrb(s_axi_wstrb) enable((*inhigh*) EN_s_axi_wstrb) clocked_by(axi_aclk) reset_by (axi_aresetn);
        method wvalid(s_axi_wvalid) enable((*inhigh*) EN_s_axi_wvalid) clocked_by(axi_aclk) reset_by (axi_aresetn);
    endinterface
    interface AprpS_axi_ctl     s_axi_ctl;
        method araddr(s_axi_ctl_araddr) enable((*inhigh*) EN_s_axi_ctl_araddr) clocked_by(axi_ctl_aclk) reset_by(axi_ctl_aresetn);
        method s_axi_ctl_arready arready() clocked_by(axi_ctl_aclk) reset_by(axi_ctl_aresetn);
        method arvalid(s_axi_ctl_arvalid) enable((*inhigh*) EN_s_axi_ctl_arvalid) clocked_by(axi_ctl_aclk) reset_by(axi_ctl_aresetn);
        method awaddr(s_axi_ctl_awaddr) enable((*inhigh*) EN_s_axi_ctl_awaddr) clocked_by(axi_ctl_aclk) reset_by(axi_ctl_aresetn);
        method s_axi_ctl_awready awready() clocked_by(axi_ctl_aclk) reset_by(axi_ctl_aresetn);
        method awvalid(s_axi_ctl_awvalid) enable((*inhigh*) EN_s_axi_ctl_awvalid) clocked_by(axi_ctl_aclk) reset_by(axi_ctl_aresetn);
        method bready(s_axi_ctl_bready) enable((*inhigh*) EN_s_axi_ctl_bready) clocked_by(axi_ctl_aclk) reset_by(axi_ctl_aresetn);
        method s_axi_ctl_bresp bresp() clocked_by(axi_ctl_aclk) reset_by(axi_ctl_aresetn);
        method s_axi_ctl_bvalid bvalid() clocked_by(axi_ctl_aclk) reset_by(axi_ctl_aresetn);
        method s_axi_ctl_rdata rdata() clocked_by(axi_ctl_aclk) reset_by(axi_ctl_aresetn);
        method rready(s_axi_ctl_rready) enable((*inhigh*) EN_s_axi_ctl_rready) clocked_by(axi_ctl_aclk) reset_by(axi_ctl_aresetn);
        method s_axi_ctl_rresp rresp() clocked_by(axi_ctl_aclk) reset_by(axi_ctl_aresetn);
        method s_axi_ctl_rvalid rvalid() clocked_by(axi_ctl_aclk) reset_by(axi_ctl_aresetn);
        method wdata(s_axi_ctl_wdata) enable((*inhigh*) EN_s_axi_ctl_wdata) clocked_by(axi_ctl_aclk) reset_by(axi_ctl_aresetn);
        method s_axi_ctl_wready wready() clocked_by(axi_ctl_aclk) reset_by(axi_ctl_aresetn);
        method wstrb(s_axi_ctl_wstrb) enable((*inhigh*) EN_s_axi_ctl_wstrb) clocked_by(axi_ctl_aclk) reset_by(axi_ctl_aresetn);
        method wvalid(s_axi_ctl_wvalid) enable((*inhigh*) EN_s_axi_ctl_wvalid) clocked_by(axi_ctl_aclk) reset_by(axi_ctl_aresetn);
    endinterface
    schedule (interrupt.out, intx.msi_grant, intx.msi_request, m_axi.araddr, m_axi.arburst, m_axi.arcache, m_axi.arlen, m_axi.arlock, m_axi.arprot, m_axi.arready, m_axi.arsize, m_axi.arvalid, m_axi.awaddr, m_axi.awburst, m_axi.awcache, m_axi.awlen, m_axi.awlock, m_axi.awprot, m_axi.awready, m_axi.awsize, m_axi.awvalid, m_axi.bready, m_axi.bresp, m_axi.bvalid, m_axi.rdata, m_axi.rlast, m_axi.rready, m_axi.rresp, m_axi.rvalid, m_axi.wdata, m_axi.wlast, m_axi.wready, m_axi.wstrb, m_axi.wvalid, mmcm.lock, msi.enable, msi.vector_num, msi.vector_width, pci.exp_rxn, pci.exp_rxp, pci.exp_txn, pci.exp_txp, s_axi.araddr, s_axi.arburst, s_axi.arid, s_axi.arlen, s_axi.arready, s_axi.arregion, s_axi.arsize, s_axi.arvalid, s_axi.awaddr, s_axi.awburst, s_axi.awid, s_axi.awlen, s_axi.awready, s_axi.awregion, s_axi.awsize, s_axi.awvalid, s_axi.bid, s_axi.bready, s_axi.bresp, s_axi.bvalid, s_axi.rdata, s_axi.rid, s_axi.rlast, s_axi.rready, s_axi.rresp, s_axi.rvalid, s_axi.wdata, s_axi.wlast, s_axi.wready, s_axi.wstrb, s_axi.wvalid, s_axi_ctl.araddr, s_axi_ctl.arready, s_axi_ctl.arvalid, s_axi_ctl.awaddr, s_axi_ctl.awready, s_axi_ctl.awvalid, s_axi_ctl.bready, s_axi_ctl.bresp, s_axi_ctl.bvalid, s_axi_ctl.rdata, s_axi_ctl.rready, s_axi_ctl.rresp, s_axi_ctl.rvalid, s_axi_ctl.wdata, s_axi_ctl.wready, s_axi_ctl.wstrb, s_axi_ctl.wvalid) CF (interrupt.out, intx.msi_grant, intx.msi_request, m_axi.araddr, m_axi.arburst, m_axi.arcache, m_axi.arlen, m_axi.arlock, m_axi.arprot, m_axi.arready, m_axi.arsize, m_axi.arvalid, m_axi.awaddr, m_axi.awburst, m_axi.awcache, m_axi.awlen, m_axi.awlock, m_axi.awprot, m_axi.awready, m_axi.awsize, m_axi.awvalid, m_axi.bready, m_axi.bresp, m_axi.bvalid, m_axi.rdata, m_axi.rlast, m_axi.rready, m_axi.rresp, m_axi.rvalid, m_axi.wdata, m_axi.wlast, m_axi.wready, m_axi.wstrb, m_axi.wvalid, mmcm.lock, msi.enable, msi.vector_num, msi.vector_width, pci.exp_rxn, pci.exp_rxp, pci.exp_txn, pci.exp_txp, s_axi.araddr, s_axi.arburst, s_axi.arid, s_axi.arlen, s_axi.arready, s_axi.arregion, s_axi.arsize, s_axi.arvalid, s_axi.awaddr, s_axi.awburst, s_axi.awid, s_axi.awlen, s_axi.awready, s_axi.awregion, s_axi.awsize, s_axi.awvalid, s_axi.bid, s_axi.bready, s_axi.bresp, s_axi.bvalid, s_axi.rdata, s_axi.rid, s_axi.rlast, s_axi.rready, s_axi.rresp, s_axi.rvalid, s_axi.wdata, s_axi.wlast, s_axi.wready, s_axi.wstrb, s_axi.wvalid, s_axi_ctl.araddr, s_axi_ctl.arready, s_axi_ctl.arvalid, s_axi_ctl.awaddr, s_axi_ctl.awready, s_axi_ctl.awvalid, s_axi_ctl.bready, s_axi_ctl.bresp, s_axi_ctl.bvalid, s_axi_ctl.rdata, s_axi_ctl.rready, s_axi_ctl.rresp, s_axi_ctl.rvalid, s_axi_ctl.wdata, s_axi_ctl.wready, s_axi_ctl.wstrb, s_axi_ctl.wvalid);
endmodule

/*
   ../../generated/scripts/importbvi.py
   -I
   APRP
   -P
   APRP
   -r
   axi_aresetn
   -c
   axi_aclk_out
   -c
   axi_ctl_aclk_out
   -c
   REFCLK
   -o
   AxiPcieRootPort.bsv
   /home/jamey/miniitx100/miniitx100.srcs/sources_1/ip/axi_pcie_0/axi_pcie_0_stub.v
*/

import Clocks::*;
import DefaultValue::*;
import XilinxCells::*;
import GetPut::*;
import AxiBits::*;

(* always_ready, always_enabled *)
interface AprpAxi;
    interface Clock     aclk_out;
    interface Clock     ctl_aclk_out;
endinterface
(* always_ready, always_enabled *)
interface AprpInterrupt;
    method Bit#(1)     out();
endinterface
(* always_ready, always_enabled *)
interface AprpIntx;
    method Bit#(1)     msi_grant();
    method Action      msi_request(Bit#(1) v);
endinterface
(* always_ready, always_enabled *)
interface AprpM_axi;
    method Bit#(32)     araddr();
    method Bit#(2)     arburst();
    method Bit#(4)     arcache();
    method Bit#(8)     arlen();
    method Bit#(1)     arlock();
    method Bit#(3)     arprot();
    method Action      arready(Bit#(1) v);
    method Bit#(3)     arsize();
    method Bit#(1)     arvalid();
    method Bit#(32)     awaddr();
    method Bit#(2)     awburst();
    method Bit#(4)     awcache();
    method Bit#(8)     awlen();
    method Bit#(1)     awlock();
    method Bit#(3)     awprot();
    method Action      awready(Bit#(1) v);
    method Bit#(3)     awsize();
    method Bit#(1)     awvalid();
    method Bit#(1)     bready();
    method Action      bresp(Bit#(2) v);
    method Action      bvalid(Bit#(1) v);
    method Action      rdata(Bit#(64) v);
    method Action      rlast(Bit#(1) v);
    method Bit#(1)     rready();
    method Action      rresp(Bit#(2) v);
    method Action      rvalid(Bit#(1) v);
    method Bit#(64)     wdata();
    method Bit#(1)     wlast();
    method Action      wready(Bit#(1) v);
    method Bit#(8)     wstrb();
    method Bit#(1)     wvalid();
endinterface
(* always_ready, always_enabled *)
interface AprpMmcm;
    method Bit#(1)     lock();
endinterface
(* always_ready, always_enabled *)
interface AprpMsi;
    method Bit#(1)     enable();
    method Action      vector_num(Bit#(5) v);
    method Bit#(3)     vector_width();
endinterface
(* always_ready, always_enabled *)
interface AprpPci;
    method Action      exp_rxn(Bit#(4) v);
    method Action      exp_rxp(Bit#(4) v);
    method Bit#(4)     exp_txn();
    method Bit#(4)     exp_txp();
endinterface
(* always_ready, always_enabled *)
interface AprpS_axi;
    method Action      araddr(Bit#(32) v);
    method Action      arburst(Bit#(2) v);
    method Action      arid(Bit#(4) v);
    method Action      arlen(Bit#(8) v);
    method Bit#(1)     arready();
    method Action      arregion(Bit#(4) v);
    method Action      arsize(Bit#(3) v);
    method Action      arvalid(Bit#(1) v);
    method Action      awaddr(Bit#(32) v);
    method Action      awburst(Bit#(2) v);
    method Action      awid(Bit#(4) v);
    method Action      awlen(Bit#(8) v);
    method Bit#(1)     awready();
    method Action      awregion(Bit#(4) v);
    method Action      awsize(Bit#(3) v);
    method Action      awvalid(Bit#(1) v);
    method Bit#(4)     bid();
    method Action      bready(Bit#(1) v);
    method Bit#(2)     bresp();
    method Bit#(1)     bvalid();
    method Bit#(64)     rdata();
    method Bit#(4)     rid();
    method Bit#(1)     rlast();
    method Action      rready(Bit#(1) v);
    method Bit#(2)     rresp();
    method Bit#(1)     rvalid();
    method Action      wdata(Bit#(64) v);
    method Action      wlast(Bit#(1) v);
    method Bit#(1)     wready();
    method Action      wstrb(Bit#(8) v);
    method Action      wvalid(Bit#(1) v);
endinterface
(* always_ready, always_enabled *)
interface AprpS_axi_ctl;
    method Action      araddr(Bit#(32) v);
    method Bit#(1)     arready();
    method Action      arvalid(Bit#(1) v);
    method Action      awaddr(Bit#(32) v);
    method Bit#(1)     awready();
    method Action      awvalid(Bit#(1) v);
    method Action      bready(Bit#(1) v);
    method Bit#(2)     bresp();
    method Bit#(1)     bvalid();
    method Bit#(32)     rdata();
    method Action      rready(Bit#(1) v);
    method Bit#(2)     rresp();
    method Bit#(1)     rvalid();
    method Action      wdata(Bit#(32) v);
    method Bit#(1)     wready();
    method Action      wstrb(Bit#(4) v);
    method Action      wvalid(Bit#(1) v);
endinterface
(* always_ready, always_enabled *)
interface APRP;
    interface AprpAxi     axi;
    interface AprpInterrupt     interrupt;
    interface AprpIntx     intx;
    interface AprpM_axi     m_axi;
    interface AprpMmcm     mmcm;
    interface AprpMsi     msi;
    interface AprpPci     pci;
    interface AprpS_axi     s_axi;
    interface AprpS_axi_ctl     s_axi_ctl;
endinterface
import "BVI" axi_pcie_0 =
module mkAPRP#(Clock refclk, Reset axi_aresetn, Reset refclk_reset)(APRP);
    default_clock clk();
    default_reset rst();
        input_reset axi_aresetn(axi_aresetn) = axi_aresetn;
    input_clock refclk(REFCLK) = refclk;
    input_reset refclk_reset() = refclk_reset; /* from clock*/
    interface AprpAxi     axi;
        output_clock aclk_out(axi_aclk_out);
        output_clock ctl_aclk_out(axi_ctl_aclk_out);
    endinterface
    interface AprpInterrupt     interrupt;
        method interrupt_out out();
    endinterface
    interface AprpIntx     intx;
        method INTX_MSI_Grant msi_grant();
        method msi_request(INTX_MSI_Request) enable((*inhigh*) EN_INTX_MSI_Request);
    endinterface
    interface AprpM_axi     m_axi;
        method m_axi_araddr araddr();
        method m_axi_arburst arburst();
        method m_axi_arcache arcache();
        method m_axi_arlen arlen();
        method m_axi_arlock arlock();
        method m_axi_arprot arprot();
        method arready(m_axi_arready) enable((*inhigh*) EN_m_axi_arready);
        method m_axi_arsize arsize();
        method m_axi_arvalid arvalid();
        method m_axi_awaddr awaddr();
        method m_axi_awburst awburst();
        method m_axi_awcache awcache();
        method m_axi_awlen awlen();
        method m_axi_awlock awlock();
        method m_axi_awprot awprot();
        method awready(m_axi_awready) enable((*inhigh*) EN_m_axi_awready);
        method m_axi_awsize awsize();
        method m_axi_awvalid awvalid();
        method m_axi_bready bready();
        method bresp(m_axi_bresp) enable((*inhigh*) EN_m_axi_bresp);
        method bvalid(m_axi_bvalid) enable((*inhigh*) EN_m_axi_bvalid);
        method rdata(m_axi_rdata) enable((*inhigh*) EN_m_axi_rdata);
        method rlast(m_axi_rlast) enable((*inhigh*) EN_m_axi_rlast);
        method m_axi_rready rready();
        method rresp(m_axi_rresp) enable((*inhigh*) EN_m_axi_rresp);
        method rvalid(m_axi_rvalid) enable((*inhigh*) EN_m_axi_rvalid);
        method m_axi_wdata wdata();
        method m_axi_wlast wlast();
        method wready(m_axi_wready) enable((*inhigh*) EN_m_axi_wready);
        method m_axi_wstrb wstrb();
        method m_axi_wvalid wvalid();
    endinterface
    interface AprpMmcm     mmcm;
        method mmcm_lock lock();
    endinterface
    interface AprpMsi     msi;
        method MSI_enable enable();
        method vector_num(MSI_Vector_Num) enable((*inhigh*) EN_MSI_Vector_Num);
        method MSI_Vector_Width vector_width();
    endinterface
    interface AprpPci     pci;
        method exp_rxn(pci_exp_rxn) enable((*inhigh*) EN_pci_exp_rxn);
        method exp_rxp(pci_exp_rxp) enable((*inhigh*) EN_pci_exp_rxp);
        method pci_exp_txn exp_txn();
        method pci_exp_txp exp_txp();
    endinterface
    interface AprpS_axi     s_axi;
        method araddr(s_axi_araddr) enable((*inhigh*) EN_s_axi_araddr);
        method arburst(s_axi_arburst) enable((*inhigh*) EN_s_axi_arburst);
        method arid(s_axi_arid) enable((*inhigh*) EN_s_axi_arid);
        method arlen(s_axi_arlen) enable((*inhigh*) EN_s_axi_arlen);
        method s_axi_arready arready();
        method arregion(s_axi_arregion) enable((*inhigh*) EN_s_axi_arregion);
        method arsize(s_axi_arsize) enable((*inhigh*) EN_s_axi_arsize);
        method arvalid(s_axi_arvalid) enable((*inhigh*) EN_s_axi_arvalid);
        method awaddr(s_axi_awaddr) enable((*inhigh*) EN_s_axi_awaddr);
        method awburst(s_axi_awburst) enable((*inhigh*) EN_s_axi_awburst);
        method awid(s_axi_awid) enable((*inhigh*) EN_s_axi_awid);
        method awlen(s_axi_awlen) enable((*inhigh*) EN_s_axi_awlen);
        method s_axi_awready awready();
        method awregion(s_axi_awregion) enable((*inhigh*) EN_s_axi_awregion);
        method awsize(s_axi_awsize) enable((*inhigh*) EN_s_axi_awsize);
        method awvalid(s_axi_awvalid) enable((*inhigh*) EN_s_axi_awvalid);
        method s_axi_bid bid();
        method bready(s_axi_bready) enable((*inhigh*) EN_s_axi_bready);
        method s_axi_bresp bresp();
        method s_axi_bvalid bvalid();
        method s_axi_rdata rdata();
        method s_axi_rid rid();
        method s_axi_rlast rlast();
        method rready(s_axi_rready) enable((*inhigh*) EN_s_axi_rready);
        method s_axi_rresp rresp();
        method s_axi_rvalid rvalid();
        method wdata(s_axi_wdata) enable((*inhigh*) EN_s_axi_wdata);
        method wlast(s_axi_wlast) enable((*inhigh*) EN_s_axi_wlast);
        method s_axi_wready wready();
        method wstrb(s_axi_wstrb) enable((*inhigh*) EN_s_axi_wstrb);
        method wvalid(s_axi_wvalid) enable((*inhigh*) EN_s_axi_wvalid);
    endinterface
    interface AprpS_axi_ctl     s_axi_ctl;
        method araddr(s_axi_ctl_araddr) enable((*inhigh*) EN_s_axi_ctl_araddr);
        method s_axi_ctl_arready arready();
        method arvalid(s_axi_ctl_arvalid) enable((*inhigh*) EN_s_axi_ctl_arvalid);
        method awaddr(s_axi_ctl_awaddr) enable((*inhigh*) EN_s_axi_ctl_awaddr);
        method s_axi_ctl_awready awready();
        method awvalid(s_axi_ctl_awvalid) enable((*inhigh*) EN_s_axi_ctl_awvalid);
        method bready(s_axi_ctl_bready) enable((*inhigh*) EN_s_axi_ctl_bready);
        method s_axi_ctl_bresp bresp();
        method s_axi_ctl_bvalid bvalid();
        method s_axi_ctl_rdata rdata();
        method rready(s_axi_ctl_rready) enable((*inhigh*) EN_s_axi_ctl_rready);
        method s_axi_ctl_rresp rresp();
        method s_axi_ctl_rvalid rvalid();
        method wdata(s_axi_ctl_wdata) enable((*inhigh*) EN_s_axi_ctl_wdata);
        method s_axi_ctl_wready wready();
        method wstrb(s_axi_ctl_wstrb) enable((*inhigh*) EN_s_axi_ctl_wstrb);
        method wvalid(s_axi_ctl_wvalid) enable((*inhigh*) EN_s_axi_ctl_wvalid);
    endinterface
    schedule (interrupt.out, intx.msi_grant, intx.msi_request, m_axi.araddr, m_axi.arburst, m_axi.arcache, m_axi.arlen, m_axi.arlock, m_axi.arprot, m_axi.arready, m_axi.arsize, m_axi.arvalid, m_axi.awaddr, m_axi.awburst, m_axi.awcache, m_axi.awlen, m_axi.awlock, m_axi.awprot, m_axi.awready, m_axi.awsize, m_axi.awvalid, m_axi.bready, m_axi.bresp, m_axi.bvalid, m_axi.rdata, m_axi.rlast, m_axi.rready, m_axi.rresp, m_axi.rvalid, m_axi.wdata, m_axi.wlast, m_axi.wready, m_axi.wstrb, m_axi.wvalid, mmcm.lock, msi.enable, msi.vector_num, msi.vector_width, pci.exp_rxn, pci.exp_rxp, pci.exp_txn, pci.exp_txp, s_axi.araddr, s_axi.arburst, s_axi.arid, s_axi.arlen, s_axi.arready, s_axi.arregion, s_axi.arsize, s_axi.arvalid, s_axi.awaddr, s_axi.awburst, s_axi.awid, s_axi.awlen, s_axi.awready, s_axi.awregion, s_axi.awsize, s_axi.awvalid, s_axi.bid, s_axi.bready, s_axi.bresp, s_axi.bvalid, s_axi.rdata, s_axi.rid, s_axi.rlast, s_axi.rready, s_axi.rresp, s_axi.rvalid, s_axi.wdata, s_axi.wlast, s_axi.wready, s_axi.wstrb, s_axi.wvalid, s_axi_ctl.araddr, s_axi_ctl.arready, s_axi_ctl.arvalid, s_axi_ctl.awaddr, s_axi_ctl.awready, s_axi_ctl.awvalid, s_axi_ctl.bready, s_axi_ctl.bresp, s_axi_ctl.bvalid, s_axi_ctl.rdata, s_axi_ctl.rready, s_axi_ctl.rresp, s_axi_ctl.rvalid, s_axi_ctl.wdata, s_axi_ctl.wready, s_axi_ctl.wstrb, s_axi_ctl.wvalid) CF (interrupt.out, intx.msi_grant, intx.msi_request, m_axi.araddr, m_axi.arburst, m_axi.arcache, m_axi.arlen, m_axi.arlock, m_axi.arprot, m_axi.arready, m_axi.arsize, m_axi.arvalid, m_axi.awaddr, m_axi.awburst, m_axi.awcache, m_axi.awlen, m_axi.awlock, m_axi.awprot, m_axi.awready, m_axi.awsize, m_axi.awvalid, m_axi.bready, m_axi.bresp, m_axi.bvalid, m_axi.rdata, m_axi.rlast, m_axi.rready, m_axi.rresp, m_axi.rvalid, m_axi.wdata, m_axi.wlast, m_axi.wready, m_axi.wstrb, m_axi.wvalid, mmcm.lock, msi.enable, msi.vector_num, msi.vector_width, pci.exp_rxn, pci.exp_rxp, pci.exp_txn, pci.exp_txp, s_axi.araddr, s_axi.arburst, s_axi.arid, s_axi.arlen, s_axi.arready, s_axi.arregion, s_axi.arsize, s_axi.arvalid, s_axi.awaddr, s_axi.awburst, s_axi.awid, s_axi.awlen, s_axi.awready, s_axi.awregion, s_axi.awsize, s_axi.awvalid, s_axi.bid, s_axi.bready, s_axi.bresp, s_axi.bvalid, s_axi.rdata, s_axi.rid, s_axi.rlast, s_axi.rready, s_axi.rresp, s_axi.rvalid, s_axi.wdata, s_axi.wlast, s_axi.wready, s_axi.wstrb, s_axi.wvalid, s_axi_ctl.araddr, s_axi_ctl.arready, s_axi_ctl.arvalid, s_axi_ctl.awaddr, s_axi_ctl.awready, s_axi_ctl.awvalid, s_axi_ctl.bready, s_axi_ctl.bresp, s_axi_ctl.bvalid, s_axi_ctl.rdata, s_axi_ctl.rready, s_axi_ctl.rresp, s_axi_ctl.rvalid, s_axi_ctl.wdata, s_axi_ctl.wready, s_axi_ctl.wstrb, s_axi_ctl.wvalid);
endmodule


cmd queue size is controled by size of cmds_in FIFO

// 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 Vector::*;
import FIFOF::*;
import FIFO::*;
import GetPut::*;
import ClientServer::*;
import BRAM::*;
import BRAMFIFO::*;
import Connectable::*;

import ConfigCounter::*;
import ConnectalMemory::*;
import MemTypes::*;
import Pipe::*;
import MemUtils::*;

module mkMemwriteEngine(MemwriteEngine#(dataWidth, cmdQDepth, numServers))
   provisos( Mul#(TDiv#(dataWidth, 8), 8, dataWidth)
	    ,Add#(1, a__, numServers)
	    ,Add#(b__, TLog#(numServers), TAdd#(1, TLog#(TMul#(cmdQDepth,numServers))))
	    ,Pipe::FunnelPipesPipelined#(1, numServers,Tuple2#(Bit#(TLog#(numServers)), MemTypes::MemengineCmd), TMin#(2,TLog#(numServers)))
	    ,Pipe::FunnelPipesPipelined#(1, numServers,Tuple2#(Bit#(dataWidth),Bool),TMin#(2, TLog#(numServers)))
	    ,Add#(c__, TLog#(numServers), TLog#(TMul#(cmdQDepth, numServers)))
	    ,Add#(1, d__, dataWidth)
	    ,FunnelPipesPipelined#(1, numServers, Tuple3#(Bit#(2),Bit#(dataWidth),Bool), TMin#(2, TLog#(numServers)))
	    ,FunnelPipesPipelined#(1, numServers,Tuple3#(Bit#(TLog#(numServers)), Bit#(dataWidth), Bool), TMin#(2,TLog#(numServers)))
	    ,Add#(e__, TLog#(numServers), 6)
	    );
   let rv <- mkMemwriteEngineBuff(valueOf(TExp#(BurstLenSize)));
   return rv;
endmodule

interface BurstFunnel#(numeric type k, numeric type w);
   method Action loadIdx(Bit#(TLog#(k)) i);
   interface Vector#(k, PipeIn#(Bit#(w))) dataIn;
   interface Vector#(k, Reg#(Bit#(BurstLenSize))) burstLen;
   interface PipeOut#(Tuple2#(Bit#(TLog#(k)),Bit#(w))) dataOut;
endinterface

module mkBurstFunnel#(Integer maxBurstLen)(BurstFunnel#(k,w))
   provisos( Log#(k,logk)
	    ,Min#(2,logk,bpc)
	    ,FunnelPipesPipelined#(1, k, Tuple3#(Bit#(2),Bit#(w),Bool), bpc)
	    );

   Reg#(Bit#(2)) nameGen <- mkReg(0);
   UGBramFifos#(4,16,Tuple2#(Bit#(w),Bool)) complBuff <- mkUGBramFifos;
   Vector#(4, ConfigCounter#(16)) compCnts <- replicateM(mkConfigCounter(0));
   Vector#(k,FIFOF#(Tuple3#(Bit#(2), Bit#(w), Bool))) data_in <- replicateM(mkFIFOF);
   Vector#(k,Reg#(Bit#(BurstLenSize))) burst_len <- replicateM(mkReg(0));
   Vector#(k,Reg#(Bit#(BurstLenSize))) drain_cnt <- replicateM(mkReg(0));
   Reg#(Bit#(BurstLenSize)) inj_ctrl <- mkReg(0);
   FIFO#(Tuple2#(Bit#(TAdd#(1,logk)),Bit#(2))) loadIdxs <- mkSizedBRAMFIFO(32);
   FIFO#(Tuple2#(Bit#(TAdd#(1,logk)),Bit#(2))) inFlight <- mkSizedBRAMFIFO(4);
   FunnelPipe#(1, k, Tuple3#(Bit#(2),Bit#(w),Bool),bpc) data_in_funnel <- mkFunnelPipesPipelined(map(toPipeOut,data_in));
   Reg#(Bit#(BurstLenSize)) drainCnt <- mkReg(0);
   FIFOF#(Tuple2#(Bit#(TLog#(k)),Bit#(w))) exit_data <- mkFIFOF;
   FIFO#(Bit#(logk)) drainRename <- mkFIFO;

   Reg#(Bit#(32)) cycle <- mkReg(0);
   Reg#(Bit#(32)) last_entry <- mkReg(0);

   rule cyc;
      cycle <= cycle+1;
   endrule

   function PipeIn#(Bit#(w)) enter_data(FIFOF#(Tuple3#(Bit#(2), Bit#(w), Bool)) f, Integer i) =
      (interface PipeIn;
   	  method Bool notFull = f.notFull;
   	  method Action enq(Bit#(w) v) if (tpl_1(loadIdxs.first) == fromInteger(i));
	     last_entry <= cycle;
	     match {.old_name, .new_name} = loadIdxs.first;
	     let first = inj_ctrl == 0;
	     let cnt = first ? burst_len[i] : inj_ctrl;
	     let new_cnt = cnt-1;
	     let last = new_cnt == 0;
	     inj_ctrl <= new_cnt;
	     if (first)
		inFlight.enq(loadIdxs.first);
	     if (last)
		loadIdxs.deq;
	     f.enq(tuple3(new_name, v, last));
	     //$display("%d enq %d", cycle-last_entry, i);
	  endmethod
       endinterface);
   Vector#(k, PipeIn#(Bit#(w))) data_in_pipes = zipWith(enter_data, data_in, genVector);

   function Reg#(Bit#(BurstLenSize)) check(Reg#(Bit#(BurstLenSize)) r) =
      (interface Reg;
	  method Action _write(Bit#(BurstLenSize) v);
	     if(v > 16) begin
		$display("ERROR mkBurstFunnel: burstLen too large");
		$finish;
	     end
	     r <= v;
	  endmethod
	  method Bit#(BurstLenSize) _read = r._read;
       endinterface);

   rule drain_funnel;
      match{.new_name,.data,.last} = data_in_funnel[0].first;
      data_in_funnel[0].deq;
      complBuff.enq(new_name,tuple2(data,last));
      compCnts[new_name].increment(1);
   endrule

   rule drain_req (compCnts[tpl_2(inFlight.first)].read > 0);
      match {.old_name, .new_name} = inFlight.first;
      let new_drainCnt = drainCnt-1;
      if (drainCnt == 0) begin
	 new_drainCnt = burst_len[old_name]-1;
	 drainRename.enq(truncate(old_name));
      end
      if (new_drainCnt == 0) begin
	 inFlight.deq;
      end
      complBuff.first_req(new_name);
      drainCnt <= new_drainCnt;
      compCnts[new_name].decrement(1);
      complBuff.deq(new_name);
   endrule

   rule drain_resp;
      match {.data,.last} <- complBuff.first_resp;
      if (last)
	 drainRename.deq;
      exit_data.enq(tuple2(drainRename.first,data));
   endrule

   method Action loadIdx(Bit#(logk) idx);
      loadIdxs.enq(tuple2(extend(idx),nameGen));
      nameGen <= nameGen+1;
   endmethod
   interface burstLen = map(check,burst_len);
   interface dataIn = data_in_pipes;
   interface PipeOut dataOut = toPipeOut(exit_data);
endmodule

module mkMemwriteEngineBuff#(Integer bufferSizeBytes)(MemwriteEngine#(dataWidth, cmdQDepth, numServers))
   provisos ( Div#(dataWidth,8,dataWidthBytes)
	     ,Mul#(dataWidthBytes,8,dataWidth)
	     ,Log#(dataWidthBytes,beatShift)
	     ,Log#(cmdQDepth,logCmdQDepth)
	     ,Mul#(cmdQDepth,numServers,cmdBuffSz)
	     ,Log#(cmdBuffSz, cmdBuffAddrSz)
	     ,Log#(numServers, serverIdxSz)
	     ,Add#(1,logCmdQDepth, outCntSz)
	     ,Add#(1, c__, numServers)
	     ,Add#(b__, TLog#(numServers), cmdBuffAddrSz)
	     ,Add#(e__, TLog#(numServers), TAdd#(1, cmdBuffAddrSz))
	     ,Add#(a__, serverIdxSz, cmdBuffAddrSz)
	     ,Min#(2,TLog#(numServers),bpc)
	     ,FunnelPipesPipelined#(1,numServers,Tuple2#(Bit#(serverIdxSz),MemengineCmd),bpc)
	     ,FunnelPipesPipelined#(1,numServers,Tuple2#(Bit#(dataWidth),Bool),bpc)
	     ,FunnelPipesPipelined#(1, numServers, Tuple3#(Bit#(2),Bit#(dataWidth),Bool), TMin#(2, serverIdxSz))
	     ,Add#(1, d__, dataWidth)
	     ,FunnelPipesPipelined#(1, numServers, Tuple3#(Bit#(serverIdxSz),Bit#(dataWidth), Bool), TMin#(2, serverIdxSz))
	     ,Add#(f__, TLog#(numServers), TAdd#(1, serverIdxSz))
	     ,Add#(g__, serverIdxSz, 6)
	     );

   Integer bufferSizeBeats = bufferSizeBytes/valueOf(dataWidthBytes);
   Vector#(numServers, Reg#(Bool))               outs1 <- replicateM(mkReg(False));
   Vector#(numServers, ConfigCounter#(16))        buffCap <- replicateM(mkConfigCounter(0));
   Vector#(numServers, Reg#(MemengineCmd))        cmdRegs <- replicateM(mkReg(unpack(0)));

   Reg#(Bool) load_in_progress <- mkReg(False);
   FIFO#(Tuple3#(MemengineCmd,Bool,Bool))         loadf_b <- mkSizedFIFO(1);
   FIFO#(Tuple2#(Bit#(serverIdxSz),MemengineCmd)) loadf_c <- mkSizedFIFO(1);
   FIFO#(Tuple3#(Bit#(8),Bit#(MemTagSize),Bool))    workf <- mkSizedBRAMFIFO(32); // is this the right size?
   FIFO#(Tuple2#(Bit#(serverIdxSz),Bool))           donef <- mkSizedBRAMFIFO(32); // is this the right size?

   Vector#(numServers, FIFO#(Bool))              outfs <- replicateM(mkSizedFIFO(1));
   Vector#(numServers, FIFOF#(MemengineCmd))    cmds_in <- replicateM(mkSizedFIFOF(1));
   Vector#(numServers, FIFOF#(Bit#(dataWidth)))  write_data_buffs <- replicateM(mkSizedBRAMFIFOF(bufferSizeBeats));

   Reg#(Bit#(8))                    respCnt <- mkReg(0);
   Reg#(Bit#(TAdd#(1,serverIdxSz))) loadIdx <- mkReg(0);
   let beat_shift = fromInteger(valueOf(beatShift));
   let cmd_q_depth = fromInteger(valueOf(cmdQDepth));

   function Action incr_loadIdx =
      (action
       if(loadIdx+1 >= fromInteger(valueOf(numServers)))
	  loadIdx <= 0;
       else
	  loadIdx <= loadIdx+1;
       endaction);

   for (Integer idx = 0; idx < valueOf(numServers); idx = idx + 1)
      rule store_cmd if (!outs1[idx]);
	 let cmd <- toGet(cmds_in[idx]).get();
	 outs1[idx] <= True;
	 cmdRegs[idx] <= cmd;
      endrule

   rule load_ctxt_a if (!load_in_progress);
      if (outs1[loadIdx]) begin
	 load_in_progress <= True;
	 let cmd = cmdRegs[loadIdx];
	 let cond0 <- buffCap[loadIdx].maybeDecrement(unpack(extend(cmd.burstLen>>beat_shift)));
	 let cond1 = cmd.len <= extend(cmd.burstLen);
	 loadf_b.enq(tuple3(cmd,cond0,cond1));
      end
      else begin
	 incr_loadIdx;
      end
   endrule

   rule load_ctxt_b if (load_in_progress);
      load_in_progress <= False;
      incr_loadIdx;
      match {.cmd,.cond0,.cond1} <- toGet(loadf_b).get;
      if  (cond0) begin
	 //$display("load_ctxt_b %h %d", cmd.base, idx);
	 let x = cmd.burstLen;
	 if (cmd.len < extend(cmd.burstLen))
	    x = truncate(cmd.len);
	 loadf_c.enq(tuple2(truncate(loadIdx),cmd));
	 if (cond1) begin
	    outs1[loadIdx] <= False;
	 end
	 else begin
	    let new_cmd = MemengineCmd{sglId:cmd.sglId, base:cmd.base+extend(cmd.burstLen), burstLen:cmd.burstLen, len:cmd.len-extend(cmd.burstLen), tag:cmd.tag};
	    cmdRegs[loadIdx] <= new_cmd;
	 end
      end
   endrule

   function PipeIn#(Bit#(w)) check_in(FIFOF#(Bit#(w)) f, Integer i) =
      (interface PipeIn;
   	  method Bool notFull = f.notFull;
   	  method Action enq(Bit#(w) v);
	     f.enq(v);
	     buffCap[i].increment(1);
	     //$display("check_in %d", i);
	     // if(i==2)
	     // 	for(Integer j = 0; j < valueOf(w); j=j+32) begin
	     // 	   Bit#(32) xx = v[j+31:j];
	     // 	   $display("%h", xx);
	     // 	end
	  endmethod
       endinterface);

   function MemwriteServer#(dataWidth) toMemwriteServer(Server#(MemengineCmd,Bool) cs, PipeIn#(Bit#(dataWidth)) p) =
      (interface MemwriteServer;
	  interface cmdServer = cs;
	  interface dataPipe  = p;
       endinterface);

   Vector#(numServers, Server#(MemengineCmd,Bool)) rs;
   for(Integer i = 0; i < valueOf(numServers); i=i+1)
      rs[i] = (interface Server#(MemengineCmd,Bool);
		  interface Put request;
		     method Action put(MemengineCmd cmd);
			Bit#(32) bsb = fromInteger(bufferSizeBytes);
`ifdef BSIM
			Bit#(32) dw = fromInteger(valueOf(dataWidthBytes));
			Bit#(32) bl = extend(cmd.burstLen);
			// this is because bsc lifts the divide operation (below)
			// and on startup the simulator gets a floating-point exception
	  		if (bl ==0)
			   bl = 1;
			let mdw0 = ((cmd.len)/bl)*bl != cmd.len;
			let mdw1 = ((cmd.len)/dw)*dw != cmd.len;
			let bbl = extend(cmd.burstLen) > bsb;
			if(bbl || mdw0 || mdw1 || cmd.len == 0) begin
			   if (bbl)
			      $display("XXXXXXXXXX mkMemwriteEngineBuff::unsupported burstLen %d %d", bsb, cmd.burstLen);
			   if (mdw0 || mdw1 || cmd.len == 0)
			      $display("XXXXXXXXXX mkMemwriteEngineBuff::unsupported len %h mdw0=%d mdw1=%d", cmd.len, mdw0, mdw1);
			end
			else begin
`endif
			   cmds_in[i].enq(cmd);
			   //$display("(%d) %h %h %h", i, cmd.base, cmd.len, cmd.burstLen);
`ifdef BSIM
			end
`endif
 		     endmethod
		  endinterface
		  interface Get response;
		     method ActionValue#(Bool) get;
			let rv <- toGet(outfs[i]).get;
			return rv;
		     endmethod
		  endinterface
	       endinterface);
   interface writeServers = rs;
   interface MemWriteClient dmaClient;
      interface Get writeReq;
	 method ActionValue#(MemRequest) get();
	    match {.idx, .cmd} <- toGet(loadf_c).get;
	    Bit#(BurstLenSize) bl = cmd.burstLen;
	    Bool last = False;
	    if (cmd.len <= extend(bl)) begin
	       last = True;
	       bl = truncate(cmd.len);
	    end
	    let new_tag = (cmd.tag << valueOf(serverIdxSz)) | extend(idx);
	    workf.enq(tuple3(truncate(bl>>beat_shift), new_tag, last));
	    //$display("writeReq %d, %h %h %h", idx, cmd.base, bl, last);
	    return MemRequest { sglId: cmd.sglId, offset: cmd.base, burstLen:bl, tag: new_tag};
	 endmethod
      endinterface
      interface Get writeData;
	 method ActionValue#(MemData#(dataWidth)) get;
	    match {.rc, .new_tag, .last} = workf.first;
	    Bit#(serverIdxSz) idx = truncate(new_tag);
	    let new_respCnt = respCnt+1;
	    if (new_respCnt == rc) begin
	       respCnt <= 0;
	       workf.deq;
	       donef.enq(tuple2(idx,last));
	    end
	    else begin
	       respCnt <= new_respCnt;
	    end
	    let wd <- toGet(write_data_buffs[idx]).get();
	    // TODO: this last field should be set correctly to mark the end of bursts
	    return MemData{data:wd, tag:new_tag, last:False};
	 endmethod
      endinterface
      interface Put writeDone;
	 method Action put(Bit#(MemTagSize) tag);
	    match {.idx, .last} <- toGet(donef).get;
	    if (last)
	       outfs[idx].enq(True);
	    //$display("writeDone %d %d", idx, last);
	 endmethod
      endinterface
   endinterface
   interface dataPipes = zipWith(check_in, write_data_buffs, genVector);
   interface write_servers = zipWith(toMemwriteServer, rs, zipWith(check_in, write_data_buffs, genVector));
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 Vector::*;
import FIFOF::*;
import FIFO::*;
import GetPut::*;
import ClientServer::*;
import BRAM::*;
import BRAMFIFO::*;
import Connectable::*;

import ConfigCounter::*;
import ConnectalMemory::*;
import MemTypes::*;
import Pipe::*;
import MemUtils::*;

module mkMemwriteEngine(MemwriteEngine#(dataWidth, cmdQDepth, numServers))
   provisos( Mul#(TDiv#(dataWidth, 8), 8, dataWidth)
	    ,Add#(1, a__, numServers)
	    ,Add#(b__, TLog#(numServers), TAdd#(1, TLog#(TMul#(cmdQDepth,numServers))))
	    ,Pipe::FunnelPipesPipelined#(1, numServers,Tuple2#(Bit#(TLog#(numServers)), MemTypes::MemengineCmd), TMin#(2,TLog#(numServers)))
	    ,Pipe::FunnelPipesPipelined#(1, numServers,Tuple2#(Bit#(dataWidth),Bool),TMin#(2, TLog#(numServers)))
	    ,Add#(c__, TLog#(numServers), TLog#(TMul#(cmdQDepth, numServers)))
	    ,Add#(1, d__, dataWidth)
	    ,FunnelPipesPipelined#(1, numServers, Tuple3#(Bit#(2),Bit#(dataWidth),Bool), TMin#(2, TLog#(numServers)))
	    ,FunnelPipesPipelined#(1, numServers,Tuple3#(Bit#(TLog#(numServers)), Bit#(dataWidth), Bool), TMin#(2,TLog#(numServers)))
	    ,Add#(e__, TLog#(numServers), 6)
	    );
   let rv <- mkMemwriteEngineBuff(valueOf(TExp#(BurstLenSize)));
   return rv;
endmodule

interface BurstFunnel#(numeric type k, numeric type w);
   method Action loadIdx(Bit#(TLog#(k)) i);
   interface Vector#(k, PipeIn#(Bit#(w))) dataIn;
   interface Vector#(k, Reg#(Bit#(BurstLenSize))) burstLen;
   interface PipeOut#(Tuple2#(Bit#(TLog#(k)),Bit#(w))) dataOut;
endinterface

module mkBurstFunnel#(Integer maxBurstLen)(BurstFunnel#(k,w))
   provisos( Log#(k,logk)
	    ,Min#(2,logk,bpc)
	    ,FunnelPipesPipelined#(1, k, Tuple3#(Bit#(2),Bit#(w),Bool), bpc)
	    );

   Reg#(Bit#(2)) nameGen <- mkReg(0);
   UGBramFifos#(4,16,Tuple2#(Bit#(w),Bool)) complBuff <- mkUGBramFifos;
   Vector#(4, ConfigCounter#(16)) compCnts <- replicateM(mkConfigCounter(0));
   Vector#(k,FIFOF#(Tuple3#(Bit#(2), Bit#(w), Bool))) data_in <- replicateM(mkFIFOF);
   Vector#(k,Reg#(Bit#(BurstLenSize))) burst_len <- replicateM(mkReg(0));
   Vector#(k,Reg#(Bit#(BurstLenSize))) drain_cnt <- replicateM(mkReg(0));
   Reg#(Bit#(BurstLenSize)) inj_ctrl <- mkReg(0);
   FIFO#(Tuple2#(Bit#(TAdd#(1,logk)),Bit#(2))) loadIdxs <- mkSizedBRAMFIFO(32);
   FIFO#(Tuple2#(Bit#(TAdd#(1,logk)),Bit#(2))) inFlight <- mkSizedBRAMFIFO(4);
   FunnelPipe#(1, k, Tuple3#(Bit#(2),Bit#(w),Bool),bpc) data_in_funnel <- mkFunnelPipesPipelined(map(toPipeOut,data_in));
   Reg#(Bit#(BurstLenSize)) drainCnt <- mkReg(0);
   FIFOF#(Tuple2#(Bit#(TLog#(k)),Bit#(w))) exit_data <- mkFIFOF;
   FIFO#(Bit#(logk)) drainRename <- mkFIFO;

   Reg#(Bit#(32)) cycle <- mkReg(0);
   Reg#(Bit#(32)) last_entry <- mkReg(0);

   rule cyc;
      cycle <= cycle+1;
   endrule

   function PipeIn#(Bit#(w)) enter_data(FIFOF#(Tuple3#(Bit#(2), Bit#(w), Bool)) f, Integer i) =
      (interface PipeIn;
   	  method Bool notFull = f.notFull;
   	  method Action enq(Bit#(w) v) if (tpl_1(loadIdxs.first) == fromInteger(i));
	     last_entry <= cycle;
	     match {.old_name, .new_name} = loadIdxs.first;
	     let first = inj_ctrl == 0;
	     let cnt = first ? burst_len[i] : inj_ctrl;
	     let new_cnt = cnt-1;
	     let last = new_cnt == 0;
	     inj_ctrl <= new_cnt;
	     if (first)
		inFlight.enq(loadIdxs.first);
	     if (last)
		loadIdxs.deq;
	     f.enq(tuple3(new_name, v, last));
	     //$display("%d enq %d", cycle-last_entry, i);
	  endmethod
       endinterface);
   Vector#(k, PipeIn#(Bit#(w))) data_in_pipes = zipWith(enter_data, data_in, genVector);

   function Reg#(Bit#(BurstLenSize)) check(Reg#(Bit#(BurstLenSize)) r) =
      (interface Reg;
	  method Action _write(Bit#(BurstLenSize) v);
	     if(v > 16) begin
		$display("ERROR mkBurstFunnel: burstLen too large");
		$finish;
	     end
	     r <= v;
	  endmethod
	  method Bit#(BurstLenSize) _read = r._read;
       endinterface);

   rule drain_funnel;
      match{.new_name,.data,.last} = data_in_funnel[0].first;
      data_in_funnel[0].deq;
      complBuff.enq(new_name,tuple2(data,last));
      compCnts[new_name].increment(1);
   endrule

   rule drain_req (compCnts[tpl_2(inFlight.first)].read > 0);
      match {.old_name, .new_name} = inFlight.first;
      let new_drainCnt = drainCnt-1;
      if (drainCnt == 0) begin
	 new_drainCnt = burst_len[old_name]-1;
	 drainRename.enq(truncate(old_name));
      end
      if (new_drainCnt == 0) begin
	 inFlight.deq;
      end
      complBuff.first_req(new_name);
      drainCnt <= new_drainCnt;
      compCnts[new_name].decrement(1);
      complBuff.deq(new_name);
   endrule

   rule drain_resp;
      match {.data,.last} <- complBuff.first_resp;
      if (last)
	 drainRename.deq;
      exit_data.enq(tuple2(drainRename.first,data));
   endrule

   method Action loadIdx(Bit#(logk) idx);
      loadIdxs.enq(tuple2(extend(idx),nameGen));
      nameGen <= nameGen+1;
   endmethod
   interface burstLen = map(check,burst_len);
   interface dataIn = data_in_pipes;
   interface PipeOut dataOut = toPipeOut(exit_data);
endmodule

module mkMemwriteEngineBuff#(Integer bufferSizeBytes)(MemwriteEngine#(dataWidth, cmdQDepth, numServers))
   provisos ( Div#(dataWidth,8,dataWidthBytes)
	     ,Mul#(dataWidthBytes,8,dataWidth)
	     ,Log#(dataWidthBytes,beatShift)
	     ,Log#(cmdQDepth,logCmdQDepth)
	     ,Mul#(cmdQDepth,numServers,cmdBuffSz)
	     ,Log#(cmdBuffSz, cmdBuffAddrSz)
	     ,Log#(numServers, serverIdxSz)
	     ,Add#(1,logCmdQDepth, outCntSz)
	     ,Add#(1, c__, numServers)
	     ,Add#(b__, TLog#(numServers), cmdBuffAddrSz)
	     ,Add#(e__, TLog#(numServers), TAdd#(1, cmdBuffAddrSz))
	     ,Add#(a__, serverIdxSz, cmdBuffAddrSz)
	     ,Min#(2,TLog#(numServers),bpc)
	     ,FunnelPipesPipelined#(1,numServers,Tuple2#(Bit#(serverIdxSz),MemengineCmd),bpc)
	     ,FunnelPipesPipelined#(1,numServers,Tuple2#(Bit#(dataWidth),Bool),bpc)
	     ,FunnelPipesPipelined#(1, numServers, Tuple3#(Bit#(2),Bit#(dataWidth),Bool), TMin#(2, serverIdxSz))
	     ,Add#(1, d__, dataWidth)
	     ,FunnelPipesPipelined#(1, numServers, Tuple3#(Bit#(serverIdxSz),Bit#(dataWidth), Bool), TMin#(2, serverIdxSz))
	     ,Add#(f__, TLog#(numServers), TAdd#(1, serverIdxSz))
	     ,Add#(g__, serverIdxSz, 6)
	     );

   Integer bufferSizeBeats = bufferSizeBytes/valueOf(dataWidthBytes);
   Vector#(numServers, Reg#(Bool))               outs1 <- replicateM(mkReg(False));
   Vector#(numServers, Reg#(Bit#(outCntSz)))     outs0 <- replicateM(mkReg(0));
   Vector#(numServers, ConfigCounter#(16))        buffCap <- replicateM(mkConfigCounter(0));
   Vector#(numServers, Reg#(MemengineCmd))        cmdRegs <- replicateM(mkReg(unpack(0)));

   Reg#(Bool) load_in_progress <- mkReg(False);
   FIFO#(Tuple3#(MemengineCmd,Bool,Bool))         loadf_b <- mkSizedFIFO(1);
   FIFO#(Tuple2#(Bit#(serverIdxSz),MemengineCmd)) loadf_c <- mkSizedFIFO(1);
   FIFO#(Tuple3#(Bit#(8),Bit#(MemTagSize),Bool))    workf <- mkSizedBRAMFIFO(32); // is this the right size?
   FIFO#(Tuple2#(Bit#(serverIdxSz),Bool))           donef <- mkSizedBRAMFIFO(32); // is this the right size?

   Vector#(numServers, FIFO#(Bool))              outfs <- replicateM(mkSizedFIFO(1));
   Vector#(numServers, FIFOF#(MemengineCmd))    cmds_in <- replicateM(mkSizedFIFOF(1));
   Vector#(numServers, FIFOF#(Bit#(dataWidth)))  write_data_buffs <- replicateM(mkSizedBRAMFIFOF(bufferSizeBeats));

   Reg#(Bit#(8))                    respCnt <- mkReg(0);
   Reg#(Bit#(TAdd#(1,serverIdxSz))) loadIdx <- mkReg(0);
   let beat_shift = fromInteger(valueOf(beatShift));
   let cmd_q_depth = fromInteger(valueOf(cmdQDepth));

   function Action incr_loadIdx =
      (action
       if(loadIdx+1 >= fromInteger(valueOf(numServers)))
	  loadIdx <= 0;
       else
	  loadIdx <= loadIdx+1;
       endaction);

   for (Integer idx = 0; idx < valueOf(numServers); idx = idx + 1)
      rule store_cmd if (!outs1[idx]);
	 let cmd <- toGet(cmds_in[idx]).get();
	 outs1[idx] <= True;
	 cmdRegs[idx] <= cmd;
      endrule

   rule load_ctxt_a if (!load_in_progress);
      if (outs1[loadIdx]) begin
	 load_in_progress <= True;
	 let cmd = cmdRegs[loadIdx];
	 let cond0 <- buffCap[loadIdx].maybeDecrement(unpack(extend(cmd.burstLen>>beat_shift)));
	 let cond1 = cmd.len <= extend(cmd.burstLen);
	 loadf_b.enq(tuple3(cmd,cond0,cond1));
      end
      else begin
	 incr_loadIdx;
      end
   endrule

   rule load_ctxt_b if (load_in_progress);
      load_in_progress <= False;
      incr_loadIdx;
      match {.cmd,.cond0,.cond1} <- toGet(loadf_b).get;
      if  (cond0) begin
	 //$display("load_ctxt_b %h %d", cmd.base, idx);
	 let x = cmd.burstLen;
	 if (cmd.len < extend(cmd.burstLen))
	    x = truncate(cmd.len);
	 loadf_c.enq(tuple2(truncate(loadIdx),cmd));
	 //write_data_funnel.loadIdx(truncate(loadIdx));
	 if (cond1) begin
	    outs1[loadIdx] <= False;
	 end
	 else begin
	    let new_cmd = MemengineCmd{sglId:cmd.sglId, base:cmd.base+extend(cmd.burstLen), burstLen:cmd.burstLen, len:cmd.len-extend(cmd.burstLen), tag:cmd.tag};
	    cmdRegs[loadIdx] <= new_cmd;
	 end
      end
   endrule

   function PipeIn#(Bit#(w)) check_in(FIFOF#(Bit#(w)) f, Integer i) =
      (interface PipeIn;
   	  method Bool notFull = f.notFull;
   	  method Action enq(Bit#(w) v);
	     f.enq(v);
	     buffCap[i].increment(1);
	     //$display("check_in %d", i);
	     // if(i==2)
	     // 	for(Integer j = 0; j < valueOf(w); j=j+32) begin
	     // 	   Bit#(32) xx = v[j+31:j];
	     // 	   $display("%h", xx);
	     // 	end
	  endmethod
       endinterface);

   function MemwriteServer#(dataWidth) toMemwriteServer(Server#(MemengineCmd,Bool) cs, PipeIn#(Bit#(dataWidth)) p) =
      (interface MemwriteServer;
	  interface cmdServer = cs;
	  interface dataPipe  = p;
       endinterface);

   Vector#(numServers, Server#(MemengineCmd,Bool)) rs;
   for(Integer i = 0; i < valueOf(numServers); i=i+1)
      rs[i] = (interface Server#(MemengineCmd,Bool);
		  interface Put request;
		     method Action put(MemengineCmd cmd) if (outs0[i] < cmd_q_depth);
			Bit#(32) bsb = fromInteger(bufferSizeBytes);
`ifdef BSIM
			Bit#(32) dw = fromInteger(valueOf(dataWidthBytes));
			Bit#(32) bl = extend(cmd.burstLen);
			// this is because bsc lifts the divide operation (below)
			// and on startup the simulator gets a floating-point exception
	  		if (bl ==0)
			   bl = 1;
			let mdw0 = ((cmd.len)/bl)*bl != cmd.len;
			let mdw1 = ((cmd.len)/dw)*dw != cmd.len;
			let bbl = extend(cmd.burstLen) > bsb;
			if(bbl || mdw0 || mdw1 || cmd.len == 0) begin
			   if (bbl)
			      $display("XXXXXXXXXX mkMemwriteEngineBuff::unsupported burstLen %d %d", bsb, cmd.burstLen);
			   if (mdw0 || mdw1 || cmd.len == 0)
			      $display("XXXXXXXXXX mkMemwriteEngineBuff::unsupported len %h mdw0=%d mdw1=%d", cmd.len, mdw0, mdw1);
			end
			else begin
`endif
			   outs0[i] <= outs0[i]+1;
			   cmds_in[i].enq(cmd);
			   //$display("(%d) %h %h %h", i, cmd.base, cmd.len, cmd.burstLen);
`ifdef BSIM
			end
`endif
 		     endmethod
		  endinterface
		  interface Get response;
		     method ActionValue#(Bool) get;
			let rv <- toGet(outfs[i]).get;
	 		outs0[i] <= outs0[i]-1;
			return rv;
		     endmethod
		  endinterface
	       endinterface);
   interface writeServers = rs;
   interface MemWriteClient dmaClient;
      interface Get writeReq;
	 method ActionValue#(MemRequest) get();
	    match {.idx, .cmd} <- toGet(loadf_c).get;
	    Bit#(BurstLenSize) bl = cmd.burstLen;
	    Bool last = False;
	    if (cmd.len <= extend(bl)) begin
	       last = True;
	       bl = truncate(cmd.len);
	    end
	    let new_tag = (cmd.tag << valueOf(serverIdxSz)) | extend(idx);
	    workf.enq(tuple3(truncate(bl>>beat_shift), new_tag, last));
	    //$display("writeReq %d, %h %h %h", idx, cmd.base, bl, last);
	    return MemRequest { sglId: cmd.sglId, offset: cmd.base, burstLen:bl, tag: new_tag};
	 endmethod
      endinterface
      interface Get writeData;
	 method ActionValue#(MemData#(dataWidth)) get;
	    match {.rc, .new_tag, .last} = workf.first;
	    Bit#(serverIdxSz) idx = truncate(new_tag);
	    let new_respCnt = respCnt+1;
	    if (new_respCnt == rc) begin
	       respCnt <= 0;
	       workf.deq;
	       donef.enq(tuple2(idx,last));
	    end
	    else begin
	       respCnt <= new_respCnt;
	    end
	    let wd <- toGet(write_data_buffs[idx]).get();
	    // TODO: this last field should be set correctly to mark the end of bursts
	    return MemData{data:wd, tag:new_tag, last:False};
	 endmethod
      endinterface
      interface Put writeDone;
	 method Action put(Bit#(MemTagSize) tag);
	    match {.idx, .last} <- toGet(donef).get;
	    if (last)
	       outfs[idx].enq(True);
	    //$display("writeDone %d %d", idx, last);
	 endmethod
      endinterface
   endinterface
   interface dataPipes = zipWith(check_in, write_data_buffs, genVector);
   interface write_servers = zipWith(toMemwriteServer, rs, zipWith(check_in, write_data_buffs, genVector));
endmodule