blob: a83d157b8b3e6ecf9610ea8389af15be7c622dc1 [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))
}
}
case class BruOp() {
val JAL = 0
val JALR = 1
val BEQ = 2
val BNE = 3
val BLT = 4
val BGE = 5
val BLTU = 6
val BGEU = 7
val EBREAK = 8
val ECALL = 9
val EEXIT = 10
val EYIELD = 11
val ECTXSW = 12
val MPAUSE = 13
val MRET = 14
val FENCEI = 15
val UNDEF = 16
val Entries = 17
}
class BruIO(p: Parameters) extends Bundle {
val valid = Input(Bool())
val fwd = Input(Bool())
val op = Input(UInt(new BruOp().Entries.W))
val pc = Input(UInt(p.programCounterBits.W))
val target = Input(UInt(p.programCounterBits.W))
val link = Input(UInt(5.W))
}
class BranchTakenIO(p: Parameters) extends Bundle {
val valid = Output(Bool())
val value = Output(UInt(p.programCounterBits.W))
}
class Bru(p: Parameters) extends Module {
val io = IO(new Bundle {
// Decode cycle.
val req = new BruIO(p)
// Execute cycle.
val csr = new CsrBruIO(p)
val rs1 = Input(new RegfileReadDataIO)
val rs2 = Input(new RegfileReadDataIO)
val rd = Flipped(new RegfileWriteDataIO)
val taken = new BranchTakenIO(p)
val target = Flipped(new RegfileBranchTargetIO)
val interlock = Output(Bool())
val iflush = Output(Bool())
})
val branch = new BruOp()
val interlock = RegInit(false.B)
val readRs = RegInit(false.B)
val fwd = RegInit(false.B)
val op = RegInit(0.U(branch.Entries.W))
val target = Reg(UInt(p.programCounterBits.W))
val linkValid = RegInit(false.B)
val linkAddr = Reg(UInt(5.W))
val linkData = Reg(UInt(p.programCounterBits.W))
val pcEx = Reg(UInt(32.W))
linkValid := io.req.valid && io.req.link =/= 0.U &&
(io.req.op(branch.JAL) || io.req.op(branch.JALR))
op := Mux(io.req.valid, io.req.op, 0.U)
fwd := io.req.valid && io.req.fwd
readRs := Mux(io.req.valid,
io.req.op(branch.BEQ) || io.req.op(branch.BNE) ||
io.req.op(branch.BLT) || io.req.op(branch.BGE) ||
io.req.op(branch.BLTU) || io.req.op(branch.BGEU), false.B)
val mode = io.csr.out.mode // (0) machine, (1) user
val pcDe = io.req.pc
val pc4De = io.req.pc + 4.U
when (io.req.valid) {
val mret = io.req.op(branch.MRET) && !mode
val call = io.req.op(branch.MRET) && mode ||
io.req.op(branch.EBREAK) ||
io.req.op(branch.ECALL) ||
io.req.op(branch.EEXIT) ||
io.req.op(branch.EYIELD) ||
io.req.op(branch.ECTXSW) ||
io.req.op(branch.MPAUSE)
target := Mux(mret, io.csr.out.mepc,
Mux(call, io.csr.out.mtvec,
Mux(io.req.fwd || io.req.op(branch.FENCEI), pc4De,
Mux(io.req.op(branch.JALR), io.target.data,
io.req.target))))
linkAddr := io.req.link
linkData := pc4De
pcEx := pcDe
}
interlock := io.req.valid && (io.req.op(branch.EBREAK) ||
io.req.op(branch.ECALL) || io.req.op(branch.EEXIT) ||
io.req.op(branch.EYIELD) || io.req.op(branch.ECTXSW) ||
io.req.op(branch.MPAUSE) || io.req.op(branch.MRET))
io.interlock := interlock
// 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
io.taken.valid := op(branch.EBREAK) && mode ||
op(branch.ECALL) && mode ||
op(branch.EEXIT) && mode ||
op(branch.EYIELD) && mode ||
op(branch.ECTXSW) && mode ||
op(branch.MRET) && !mode ||
op(branch.MRET) && mode || // fault
op(branch.MPAUSE) && mode || // fault
op(branch.FENCEI) ||
(op(branch.JAL) ||
op(branch.JALR) ||
op(branch.BEQ) && eq ||
op(branch.BNE) && neq ||
op(branch.BLT) && lt ||
op(branch.BGE) && ge ||
op(branch.BLTU) && ltu ||
op(branch.BGEU) && geu) =/= fwd
io.taken.value := target
io.rd.valid := linkValid
io.rd.addr := linkAddr
io.rd.data := linkData
// Undefined Fault.
val undefFault = op(branch.UNDEF)
// Usage Fault.
val usageFault = op(branch.EBREAK) && !mode ||
op(branch.ECALL) && !mode ||
op(branch.EEXIT) && !mode ||
op(branch.EYIELD) && !mode ||
op(branch.ECTXSW) && !mode ||
op(branch.MPAUSE) && mode ||
op(branch.MRET) && mode
io.csr.in.mode.valid := op(branch.EBREAK) && mode ||
op(branch.ECALL) && mode ||
op(branch.EEXIT) && mode ||
op(branch.EYIELD) && mode ||
op(branch.ECTXSW) && mode ||
op(branch.MPAUSE) && mode || // fault
op(branch.MRET) && mode || // fault
op(branch.MRET) && !mode
io.csr.in.mode.bits := MuxOR(op(branch.MRET) && !mode, true.B)
io.csr.in.mepc.valid := op(branch.EBREAK) && mode ||
op(branch.ECALL) && mode ||
op(branch.EEXIT) && mode ||
op(branch.EYIELD) && mode ||
op(branch.ECTXSW) && mode ||
op(branch.MPAUSE) && mode || // fault
op(branch.MRET) && mode // fault
io.csr.in.mepc.bits := Mux(op(branch.EYIELD), linkData, pcEx)
io.csr.in.mcause.valid := undefFault || usageFault ||
op(branch.EBREAK) && mode ||
op(branch.ECALL) && mode ||
op(branch.EEXIT) && mode ||
op(branch.EYIELD) && mode ||
op(branch.ECTXSW) && mode
val faultMsb = 1.U << 31
io.csr.in.mcause.bits := Mux(undefFault, 2.U | faultMsb,
Mux(usageFault, 16.U | faultMsb,
MuxOR(op(branch.EBREAK), 1.U) |
MuxOR(op(branch.ECALL), 2.U) |
MuxOR(op(branch.EEXIT), 3.U) |
MuxOR(op(branch.EYIELD), 4.U) |
MuxOR(op(branch.ECTXSW), 5.U)))
io.csr.in.mtval.valid := undefFault || usageFault
io.csr.in.mtval.bits := pcEx
io.iflush := op(branch.FENCEI)
// Pipeline will be halted.
io.csr.in.halt := op(branch.MPAUSE) && !mode || io.csr.in.fault
io.csr.in.fault := undefFault && !mode || usageFault && !mode
// Assertions.
val valid = RegInit(false.B)
val ignore = op(branch.JAL) || op(branch.JALR) || op(branch.EBREAK) ||
op(branch.ECALL) || op(branch.EEXIT) || op(branch.EYIELD) ||
op(branch.ECTXSW) || op(branch.MPAUSE) || op(branch.MRET) ||
op(branch.FENCEI) || op(branch.UNDEF)
valid := io.req.valid
assert(!(valid && !io.rs1.valid) || ignore)
assert(!(valid && !io.rs2.valid) || ignore)
}