blob: 15f2cbeb91b9cb651e52a9c4e3cf3ce3c0f03fa2 [file] [log] [blame]
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package kelvin
import chisel3._
import chisel3.util._
import common._
object Bru {
def apply(p: Parameters): Bru = {
return Module(new Bru(p))
}
}
object BruOp extends ChiselEnum {
val JAL = Value
val JALR = Value
val BEQ = Value
val BNE = Value
val BLT = Value
val BGE = Value
val BLTU = Value
val BGEU = Value
val EBREAK = Value
val ECALL = Value
val EEXIT = Value
val EYIELD = Value
val ECTXSW = Value
val MPAUSE = Value
val MRET = Value
val FENCEI = Value
val UNDEF = Value
}
class BruCmd(p: Parameters) extends Bundle {
val fwd = Bool()
val op = BruOp()
val pc = UInt(p.programCounterBits.W)
val target = UInt(p.programCounterBits.W)
val link = UInt(5.W)
}
class BranchTakenIO(p: Parameters) extends Bundle {
val valid = Output(Bool())
val value = Output(UInt(p.programCounterBits.W))
}
class BranchState(p: Parameters) extends Bundle {
val fwd = Bool()
val op = BruOp()
val target = UInt(p.programCounterBits.W)
val linkValid = Bool()
val linkAddr = UInt(5.W)
val linkData = UInt(p.programCounterBits.W)
val pcEx = UInt(32.W)
}
object BranchState {
def default(p: Parameters): BranchState = {
val result = Wire(new BranchState(p))
result.fwd := false.B
result.op := BruOp.JAL
result.target := 0.U
result.linkValid := false.B
result.linkAddr := 0.U
result.linkData := 0.U
result.pcEx := 0.U
result
}
}
class Bru(p: Parameters) extends Module {
val io = IO(new Bundle {
// Decode cycle.
val req = Flipped(Valid(new BruCmd(p)))
// Execute cycle.
val csr = new CsrBruIO(p)
val rs1 = Input(new RegfileReadDataIO)
val rs2 = Input(new RegfileReadDataIO)
val rd = Valid(Flipped(new RegfileWriteDataIO))
val taken = new BranchTakenIO(p)
val target = Flipped(new RegfileBranchTargetIO)
val interlock = Output(Bool())
val iflush = Output(Bool())
})
// Interlock
val interlock = RegInit(false.B)
interlock := io.req.valid && io.req.bits.op.isOneOf(
BruOp.EBREAK, BruOp.ECALL, BruOp.EEXIT, BruOp.EYIELD, BruOp.ECTXSW,
BruOp.MPAUSE, BruOp.MRET)
io.interlock := interlock
// Assign state
val mode = io.csr.out.mode
val pcDe = io.req.bits.pc
val pc4De = io.req.bits.pc + 4.U
val mret = (io.req.bits.op === BruOp.MRET) && mode === CsrMode.Machine
val ecall = io.req.bits.op === BruOp.ECALL
val call = ((io.req.bits.op === BruOp.MRET) && mode === CsrMode.User) ||
io.req.bits.op.isOneOf(BruOp.EBREAK, BruOp.EEXIT,
BruOp.EYIELD, BruOp.ECTXSW, BruOp.MPAUSE)
val stateReg = RegInit(MakeValid(false.B, BranchState.default(p)))
val nextState = Wire(new BranchState(p))
nextState.linkValid := io.req.valid && (io.req.bits.link =/= 0.U) &&
(io.req.bits.op.isOneOf(BruOp.JAL, BruOp.JALR))
nextState.op := io.req.bits.op
nextState.fwd := io.req.valid && io.req.bits.fwd
nextState.linkAddr := io.req.bits.link
nextState.linkData := pc4De
nextState.pcEx := pcDe
nextState.target := MuxCase(io.req.bits.target, Seq(
mret -> io.csr.out.mepc,
ecall -> Cat(io.csr.out.mtvec(31,1), 0.U(1.W)),
call -> io.csr.out.mepc,
(io.req.bits.fwd || (io.req.bits.op === BruOp.FENCEI)) -> pc4De,
(io.req.bits.op === BruOp.JALR) -> io.target.data,
))
stateReg.valid := io.req.valid
stateReg.bits := nextState
// This mux sits on the critical path.
// val rs1 = Mux(readRs, io.rs1.data, 0.U)
// val rs2 = Mux(readRs, io.rs2.data, 0.U)
val rs1 = io.rs1.data
val rs2 = io.rs2.data
val eq = rs1 === rs2
val neq = !eq
val lt = rs1.asSInt < rs2.asSInt
val ge = !lt
val ltu = rs1 < rs2
val geu = !ltu
val op = stateReg.bits.op
io.taken.valid := stateReg.valid && MuxLookup(op, false.B)(Seq(
BruOp.EBREAK -> (mode === CsrMode.User),
// Any mode can execute `ecall`, but the value of `mcause` will be different.
BruOp.ECALL -> true.B,
BruOp.EEXIT -> (mode === CsrMode.User),
BruOp.EYIELD -> (mode === CsrMode.User),
BruOp.ECTXSW -> (mode === CsrMode.User),
BruOp.MPAUSE -> (mode === CsrMode.User),
BruOp.MRET -> (mode === CsrMode.Machine),
BruOp.FENCEI -> true.B,
BruOp.JAL -> (true.B =/= stateReg.bits.fwd),
BruOp.JALR -> (true.B =/= stateReg.bits.fwd),
BruOp.BEQ -> (eq =/= stateReg.bits.fwd),
BruOp.BNE -> (neq =/= stateReg.bits.fwd),
BruOp.BLT -> (lt =/= stateReg.bits.fwd),
BruOp.BGE -> (ge =/= stateReg.bits.fwd),
BruOp.BLTU -> (ltu =/= stateReg.bits.fwd),
BruOp.BGEU -> (geu =/= stateReg.bits.fwd),
))
io.taken.value := stateReg.bits.target
io.rd.valid := stateReg.valid && stateReg.bits.linkValid
io.rd.bits.addr := stateReg.bits.linkAddr
io.rd.bits.data := stateReg.bits.linkData
// Undefined Fault.
val undefFault = stateReg.valid && (op === BruOp.UNDEF)
// Usage Fault.
val usageFault = stateReg.valid && Mux(
(mode === CsrMode.User),
op.isOneOf(BruOp.MPAUSE, BruOp.MRET),
op.isOneOf(BruOp.EBREAK, BruOp.EEXIT, BruOp.EYIELD,
BruOp.ECTXSW))
io.csr.in.mode.valid := stateReg.valid && Mux(
(mode === CsrMode.User), op.isOneOf(BruOp.EBREAK, BruOp.ECALL, BruOp.EEXIT, BruOp.EYIELD,
BruOp.ECTXSW, BruOp.MPAUSE, BruOp.MRET),
(op === BruOp.MRET))
io.csr.in.mode.bits := Mux(((op === BruOp.MRET) && (mode === CsrMode.Machine)), CsrMode.Machine, CsrMode.User)
io.csr.in.mepc.valid := stateReg.valid && op === BruOp.ECALL
io.csr.in.mepc.bits := stateReg.bits.pcEx
io.csr.in.mcause.valid := stateReg.valid && (undefFault || usageFault ||
op.isOneOf(BruOp.ECALL) ||
((mode === CsrMode.User) &&
/* user mode mcause triggers */
op.isOneOf(BruOp.EBREAK,
BruOp.EEXIT, BruOp.EYIELD,
BruOp.ECTXSW),
)
)
io.csr.in.mcause.bits := MuxCase(0.U, Seq(
(op === BruOp.EBREAK) -> 3.U,
(op === BruOp.ECALL && mode === CsrMode.Machine) -> 11.U,
(op === BruOp.ECALL && mode === CsrMode.User) -> 8.U,
// Kelvin-specific things, use the custom reserved region of the encoding space.
undefFault -> (24 + 0).U,
usageFault -> (24 + 1).U,
(op === BruOp.EEXIT) -> (24 + 2).U,
(op === BruOp.EYIELD) -> (24 + 3).U,
(op === BruOp.ECTXSW) -> (24 + 4).U,
))
io.csr.in.mtval.valid := stateReg.valid && (undefFault || usageFault)
io.csr.in.mtval.bits := stateReg.bits.pcEx
io.iflush := stateReg.valid && (op === BruOp.FENCEI)
// Pipeline will be halted.
io.csr.in.halt := (stateReg.valid && (op === BruOp.MPAUSE) && (mode === CsrMode.Machine)) ||
io.csr.in.fault
io.csr.in.fault := (undefFault && (mode === CsrMode.Machine)) || (usageFault && (mode === CsrMode.Machine))
// Assertions.
val ignore = op.isOneOf(BruOp.JAL, BruOp.JALR, BruOp.EBREAK, BruOp.ECALL,
BruOp.EEXIT, BruOp.EYIELD, BruOp.ECTXSW, BruOp.MPAUSE,
BruOp.MRET, BruOp.FENCEI, BruOp.UNDEF)
assert(!(stateReg.valid && !io.rs1.valid) || ignore)
assert(!(stateReg.valid && !io.rs2.valid) || ignore)
}