blob: 7397bb789d586f643d44df925c3c4f2cbdfc078d [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.
// Regfile: 32 entry scalar register file with 8 read ports and 6
// write ports. Houses a global scoreboard that informs of interlock
// deps inside the decoders.
package kelvin
import chisel3._
import chisel3.util._
import common._
import _root_.circt.stage.ChiselStage
object Regfile {
def apply(p: Parameters): Regfile = {
return Module(new Regfile(p))
}
}
class RegfileReadAddrIO extends Bundle {
val valid = Input(Bool())
val addr = Input(UInt(5.W))
}
class RegfileReadSetIO extends Bundle {
val valid = Input(Bool())
val value = Input(UInt(32.W))
}
class RegfileReadDataIO extends Bundle {
val valid = Output(Bool())
val data = Output(UInt(32.W))
}
class RegfileWriteAddrIO extends Bundle {
val valid = Input(Bool())
val addr = Input(UInt(5.W))
}
class RegfileWriteDataIO extends Bundle {
val valid = Input(Bool())
val addr = Input(UInt(5.W))
val data = Input(UInt(32.W))
}
class RegfileBusAddrIO extends Bundle {
val valid = Input(Bool())
val bypass = Input(Bool())
val immen = Input(Bool())
val immed = Input(UInt(32.W))
}
class RegfileBusPortIO(p: Parameters) extends Bundle {
val addr = Output(Vec(p.instructionLanes, UInt(32.W)))
val data = Output(Vec(p.instructionLanes, UInt(32.W)))
}
class RegfileLinkPortIO extends Bundle {
val valid = Output(Bool())
val value = Output(UInt(32.W))
}
class RegfileBranchTargetIO extends Bundle {
val data = Output(UInt(32.W))
}
class Regfile(p: Parameters) extends Module {
val io = IO(new Bundle {
// Decode cycle.
val readAddr = Vec(p.instructionLanes * 2, new RegfileReadAddrIO)
val readSet = Vec(p.instructionLanes * 2, new RegfileReadSetIO)
val writeAddr = Vec(p.instructionLanes, new RegfileWriteAddrIO)
val busAddr = Vec(p.instructionLanes, new RegfileBusAddrIO)
val target = Vec(p.instructionLanes, new RegfileBranchTargetIO)
val linkPort = new RegfileLinkPortIO
val busPort = new RegfileBusPortIO(p)
// Execute cycle.
val readData = Vec(p.instructionLanes * 2, new RegfileReadDataIO)
val writeData = Vec(p.instructionLanes + 2, new RegfileWriteDataIO)
val writeMask = Vec(p.instructionLanes, new Bundle {val valid = Input(Bool())})
val scoreboard = new Bundle {
val regd = Output(UInt(32.W))
val comb = Output(UInt(32.W))
}
val rfwriteCount = Output(UInt(6.W))
})
// The scalar registers.
val regfile = Reg(Vec(32, UInt(32.W)))
// ***************************************************************************
// The scoreboard.
// ***************************************************************************
val scoreboard = RegInit(0.U(32.W))
// The write Addr:Data contract is against speculated opcodes. If an opcode
// is in the shadow of a taken branch it will still Set:Clr the scoreboard,
// but the actual write will be Masked.
val scoreboard_set = io.writeAddr
.map(x => MuxOR(x.valid, UIntToOH(x.addr, 32))).reduce(_|_)
val scoreboard_clr0 = io.writeData
.map(x => MuxOR(x.valid, UIntToOH(x.addr, 32))).reduce(_|_)
val scoreboard_clr = Cat(scoreboard_clr0(31,1), 0.U(1.W))
when (scoreboard_set =/= 0.U || scoreboard_clr =/= 0.U) {
val nxtScoreboard = (scoreboard & ~scoreboard_clr) | scoreboard_set
scoreboard := Cat(nxtScoreboard(31,1), 0.U(1.W))
}
io.scoreboard.regd := scoreboard
io.scoreboard.comb := scoreboard & ~scoreboard_clr
// ***************************************************************************
// The read port response.
// ***************************************************************************
val readDataReady = RegInit(VecInit(Seq.fill(p.instructionLanes * 2){false.B}))
val readDataBits = Reg(Vec(p.instructionLanes * 2, UInt(32.W)))
val nxtReadDataBits = Wire(Vec(p.instructionLanes * 2, UInt(32.W)))
for (i <- 0 until (p.instructionLanes * 2)) {
io.readData(i).valid := readDataReady(i)
io.readData(i).data := readDataBits(i)
}
// ***************************************************************************
// One hot write ports.
// ***************************************************************************
val writeValid = Wire(Vec(32, Bool()))
val writeData = Wire(Vec(32, UInt(32.W)))
writeValid(0) := true.B // do not require special casing of indices
writeData(0) := 0.U // regfile(0) is optimized away
for (i <- 1 until 32) {
val valid = Cat(
Array(io.writeData(p.instructionLanes + 1).valid && io.writeData(p.instructionLanes + 1).addr === i.U,
io.writeData(p.instructionLanes).valid && io.writeData(p.instructionLanes).addr === i.U) ++
(0 until p.instructionLanes).reverse.map(x => io.writeData(x).valid && io.writeData(x).addr === i.U && !io.writeMask(x).valid)
)
val data = (0 until p.instructionLanes + 2).map(x => MuxOR(valid(x), io.writeData(x).data)).reduce(_|_)
writeValid(i) := valid =/= 0.U
writeData(i) := data
assert(PopCount(valid) <= 1.U)
}
for (i <- 0 until 32) {
when (writeValid(i)) {
regfile(i) := writeData(i)
}
}
// We care if someone tried to write x0 (e.g. nop is encoded this way), but want
// it separate for above mentioned optimization.
val x0 =
(0 until p.instructionLanes).map(x =>
io.writeData(x).valid &&
io.writeData(x).addr === 0.U &&
!io.writeMask(x).valid) ++
(p.instructionLanes until p.instructionLanes + 2).map(x => io.writeData(x).valid && io.writeData(x).addr === 0.U)
io.rfwriteCount := PopCount(writeValid) - writeValid(0) + PopCount(x0)
// ***************************************************************************
// Read ports with write forwarding.
// ***************************************************************************
val rdata = Wire(Vec((p.instructionLanes * 2), UInt(32.W)))
val wdata = Wire(Vec((p.instructionLanes * 2), UInt(32.W)))
val rwdata = Wire(Vec((p.instructionLanes * 2), UInt(32.W)))
for (i <- 0 until (p.instructionLanes * 2)) {
val idx = io.readAddr(i).addr
val write = VecAt(writeValid, idx)
rdata(i) := VecAt(regfile, idx)
wdata(i) := VecAt(writeData, idx)
rwdata(i) := Mux(write, wdata(i), rdata(i))
}
for (i <- 0 until (p.instructionLanes * 2)) {
val setValid = io.readSet(i).valid
val setValue = io.readSet(i).value
val nxtReadDataReady = io.readAddr(i).valid || setValid
readDataReady(i) := nxtReadDataReady
nxtReadDataBits(i) := Mux(setValid, setValue, rwdata(i))
when (nxtReadDataReady) {
readDataBits(i) := nxtReadDataBits(i)
}
}
// Bus port priority encoded address.
val busAddr = Wire(Vec(p.instructionLanes, UInt(32.W)))
val busValid = Cat((0 until p.instructionLanes).reverse.map(x => io.busAddr(x).valid))
for (i <- 0 until p.instructionLanes) {
busAddr(i) := Mux(io.busAddr(i).bypass, rwdata(2 * i),
Mux(io.busAddr(i).immen, rdata(2 * i) + io.busAddr(i).immed,
rdata(2 * i)))
}
for (i <- 0 until p.instructionLanes) {
io.busPort.addr(i) := busAddr(i)
io.busPort.data(i) := nxtReadDataBits(2 * i + 1)
}
// Branch target address combinatorial.
for (i <- 0 until p.instructionLanes) {
io.target(i).data := busAddr(i)
}
// ***************************************************************************
// Link port.
// ***************************************************************************
io.linkPort.valid := !scoreboard(1)
io.linkPort.value := regfile(1)
// ***************************************************************************
// Assertions.
// ***************************************************************************
for (i <- 0 until p.instructionLanes) {
assert(busAddr(i).getWidth == p.lsuAddrBits)
}
for (i <- 0 until p.instructionLanes + 2) {
for (j <- (i + 1) until p.instructionLanes + 2) {
// Delay the failure a cycle for debugging purposes.
val write_fail = RegInit(false.B)
write_fail := io.writeData(i).valid && io.writeData(j).valid &&
io.writeData(i).addr === io.writeData(j).addr &&
io.writeData(i).addr =/= 0.U
assert(!write_fail)
}
}
val scoreboard_error = RegInit(false.B)
scoreboard_error := (scoreboard & scoreboard_clr) =/= scoreboard_clr
assert(!scoreboard_error)
}
object EmitRegfile extends App {
val p = new Parameters
ChiselStage.emitSystemVerilogFile(new Regfile(p), args)
}