Merge branch 'non-inclusive' of github.com:OpenXiangShan/HuanCun into HEAD
This commit is contained in:
commit
7e7b742b1c
|
@ -64,7 +64,7 @@ class SubDirectory[T <: Data](
|
|||
dir_init_fn: () => T,
|
||||
dir_hit_fn: T => Bool,
|
||||
invalid_way_sel: (Seq[T], UInt) => (Bool, UInt),
|
||||
replacement: String)
|
||||
replacement: String)(implicit p: Parameters)
|
||||
extends MultiIOModule {
|
||||
|
||||
val setBits = log2Ceil(sets)
|
||||
|
@ -87,6 +87,7 @@ class SubDirectory[T <: Data](
|
|||
val way = UInt(wayBits.W)
|
||||
val tag = UInt(tagBits.W)
|
||||
val dir = dir_init.cloneType
|
||||
val error = Bool()
|
||||
})
|
||||
)
|
||||
val tag_w = Flipped(DecoupledIO(new Bundle() {
|
||||
|
@ -119,12 +120,16 @@ class SubDirectory[T <: Data](
|
|||
io.tag_w.ready := true.B
|
||||
io.reads.foreach(_.ready := !io.tag_w.valid && resetFinish)
|
||||
|
||||
def tagCode: Code = Code.fromString(p(HCCacheParamsKey).tagECC)
|
||||
|
||||
val eccTagBits = tagCode.width(tagBits)
|
||||
println(s"extra ECC Tagbits:${eccTagBits - tagBits}")
|
||||
val tagArray = Array.fill(rports) {
|
||||
Module(new SRAMTemplate(UInt(tagBits.W), sets, ways, singlePort = true))
|
||||
Module(new SRAMTemplate(UInt(eccTagBits.W), sets, ways, singlePort = true))
|
||||
}
|
||||
|
||||
val tagRead = Seq.fill(rports) {
|
||||
Wire(Vec(ways, UInt(tagBits.W)))
|
||||
Wire(Vec(ways, UInt(eccTagBits.W)))
|
||||
}
|
||||
tagArray
|
||||
.zip(io.reads)
|
||||
|
@ -132,7 +137,7 @@ class SubDirectory[T <: Data](
|
|||
.foreach({
|
||||
case ((sram, rreq), rdata) =>
|
||||
val wen = io.tag_w.fire()
|
||||
sram.io.w(wen, io.tag_w.bits.tag, io.tag_w.bits.set, UIntToOH(io.tag_w.bits.way))
|
||||
sram.io.w(wen, tagCode.encode(io.tag_w.bits.tag), io.tag_w.bits.set, UIntToOH(io.tag_w.bits.way))
|
||||
assert(!wen || (wen && sram.io.w.req.ready))
|
||||
rdata := sram.io.r(!wen, rreq.bits.set).resp.data
|
||||
assert(wen || (!wen && sram.io.r.req.ready))
|
||||
|
@ -151,7 +156,7 @@ class SubDirectory[T <: Data](
|
|||
result.valid := reqValids(i)
|
||||
val tags = tagRead(i)
|
||||
val metas = metaArray(reqSets(i))
|
||||
val tagMatchVec = tags.map(_ === reqTags(i))
|
||||
val tagMatchVec = tags.map(_(tagBits-1, 0) === reqTags(i))
|
||||
val metaValidVec = metas.map(dir_hit_fn)
|
||||
hitVec(i) := tagMatchVec.zip(metaValidVec).map(a => a._1 && a._2)
|
||||
val hitWay = OHToUInt(hitVec(i))
|
||||
|
@ -163,7 +168,8 @@ class SubDirectory[T <: Data](
|
|||
result.bits.way := Mux(result.bits.hit, hitWay, chosenWay)
|
||||
val meta = metas(result.bits.way)
|
||||
result.bits.dir := meta
|
||||
result.bits.tag := tags(result.bits.way)
|
||||
result.bits.tag := tags(result.bits.way)(tagBits-1, 0)
|
||||
result.bits.error := tagCode.decode(tags(result.bits.way)).error
|
||||
}
|
||||
|
||||
for (req <- io.dir_w) {
|
||||
|
@ -200,7 +206,7 @@ abstract class SubDirectoryDoUpdate[T <: Data](
|
|||
dir_init_fn: () => T,
|
||||
dir_hit_fn: T => Bool,
|
||||
invalid_way_sel: (Seq[T], UInt) => (Bool, UInt),
|
||||
replacement: String)
|
||||
replacement: String)(implicit p: Parameters)
|
||||
extends SubDirectory[T](
|
||||
rports, wports, sets, ways, tagBits,
|
||||
dir_init_fn, dir_hit_fn, invalid_way_sel,
|
||||
|
|
|
@ -20,9 +20,9 @@
|
|||
package huancun
|
||||
|
||||
import chipsalliance.rocketchip.config.Parameters
|
||||
import chisel3.{util, _}
|
||||
import chisel3._
|
||||
import chisel3.util._
|
||||
import huancun.utils.SRAMTemplate
|
||||
import huancun.utils._
|
||||
|
||||
class DataStorage(implicit p: Parameters) extends HuanCunModule {
|
||||
val io = IO(new Bundle() {
|
||||
|
@ -54,15 +54,21 @@ class DataStorage(implicit p: Parameters) extends HuanCunModule {
|
|||
// one row ==> ******** ******** ******** ********
|
||||
// If there's no conflict, one row can be accessed in parallel by nrStacks
|
||||
|
||||
def dataCode: Code = Code.fromString(p(HCCacheParamsKey).dataECC)
|
||||
|
||||
val eccDataBits = dataCode.width(8*bankBytes)
|
||||
println(s"extra ECC Databits:${eccDataBits - (8*bankBytes)}")
|
||||
|
||||
val bankedData = Seq.fill(nrBanks)(
|
||||
Module(
|
||||
new SRAMTemplate(
|
||||
UInt((8 * bankBytes).W),
|
||||
UInt(eccDataBits.W),
|
||||
set = nrRows,
|
||||
way = 1,
|
||||
shouldReset = false,
|
||||
holdRead = false,
|
||||
singlePort = sramSinglePort
|
||||
singlePort = sramSinglePort,
|
||||
cycleFactor = cacheParams.sramCycleFactor
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -93,7 +99,7 @@ class DataStorage(implicit p: Parameters) extends HuanCunModule {
|
|||
}
|
||||
.reverse
|
||||
)
|
||||
addr.ready := accessVec(innerAddr(stackBits - 1, 0))
|
||||
addr.ready := accessVec(innerAddr(stackBits - 1, 0)) && SReg.sren()
|
||||
|
||||
out.wen := wen.B
|
||||
out.index := innerIndex
|
||||
|
@ -126,27 +132,33 @@ class DataStorage(implicit p: Parameters) extends HuanCunModule {
|
|||
}
|
||||
|
||||
val outData = Wire(Vec(nrBanks, UInt((8 * bankBytes).W)))
|
||||
val data_en = Wire(Vec(nrBanks, Bool()))
|
||||
|
||||
for (i <- 0 until nrBanks) {
|
||||
val en = reqs.map(_.bankEn(i)).reduce(_ || _)
|
||||
val en = reqs.map(_.bankEn(i)).reduce(_ || _) && SReg.sren()
|
||||
val selectedReq = PriorityMux(reqs.map(_.bankSel(i)), reqs)
|
||||
|
||||
// Write
|
||||
bankedData(i).io.w.req.valid := en && selectedReq.wen
|
||||
bankedData(i).io.w.req.bits.apply(
|
||||
setIdx = selectedReq.index,
|
||||
data = selectedReq.data(i),
|
||||
data = dataCode.encode(selectedReq.data(i)),
|
||||
waymask = 1.U
|
||||
)
|
||||
// Read
|
||||
bankedData(i).io.r.req.valid := en && !selectedReq.wen
|
||||
bankedData(i).io.r.req.bits.apply(setIdx = selectedReq.index)
|
||||
outData(i) := RegEnable(bankedData(i).io.r.resp.data(0), RegNext(en && !selectedReq.wen))
|
||||
data_en(i) := SReg.pipe(en && !selectedReq.wen)
|
||||
val decode = dataCode.decode(bankedData(i).io.r.resp.data(0))
|
||||
outData(i) := RegEnable(decode.uncorrected, data_en(i))
|
||||
when(data_en(i)) {
|
||||
assert(!decode.error)
|
||||
}
|
||||
}
|
||||
|
||||
/* Pack out-data to channels */
|
||||
val sourceDlatch = RegNext(RegNext(sourceD_rreq.bankEn))
|
||||
val sourceClatch = RegNext(RegNext(sourceC_req.bankEn))
|
||||
val sourceDlatch = RegNext(SReg.pipe(sourceD_rreq.bankEn))
|
||||
val sourceClatch = RegNext(SReg.pipe(sourceC_req.bankEn))
|
||||
|
||||
val sourceDrdata = outData.zipWithIndex.map {
|
||||
case (r, i) => Mux(sourceDlatch(i), r, 0.U)
|
||||
|
|
|
@ -28,13 +28,15 @@ import huancun.prefetch.PrefetchParameters
|
|||
|
||||
case object HCCacheParamsKey extends Field[HCCacheParameters](HCCacheParameters())
|
||||
|
||||
case class CacheParameters(
|
||||
case class CacheParameters
|
||||
(
|
||||
name: String,
|
||||
sets: Int,
|
||||
ways: Int,
|
||||
blockBytes: Int = 64,
|
||||
aliasBitsOpt: Option[Int] = None,
|
||||
inner: Seq[CacheParameters] = Nil) {
|
||||
inner: Seq[CacheParameters] = Nil
|
||||
) {
|
||||
val capacity = sets * ways * blockBytes
|
||||
val setBits = log2Ceil(sets)
|
||||
val offsetBits = log2Ceil(blockBytes)
|
||||
|
@ -46,22 +48,31 @@ case object PrefetchKey extends ControlKey[Bool](name ="needHint")
|
|||
case class PrefetchField() extends BundleField(PrefetchKey) {
|
||||
override def data: Bool = Output(Bool())
|
||||
|
||||
override def default(x: Bool): Unit = { x := false.B }
|
||||
override def default(x: Bool): Unit = {
|
||||
x := false.B
|
||||
}
|
||||
}
|
||||
|
||||
case object AliasKey extends ControlKey[UInt]("alias")
|
||||
|
||||
case class AliasField(width: Int) extends BundleField(AliasKey) {
|
||||
override def data: UInt = Output(UInt(width.W))
|
||||
|
||||
override def default(x: UInt): Unit = {x := 0.U(width.W)}
|
||||
override def default(x: UInt): Unit = {
|
||||
x := 0.U(width.W)
|
||||
}
|
||||
}
|
||||
|
||||
// try to keep data in cache is true
|
||||
// now it only works for non-inclusive cache (ignored in inclusive cache)
|
||||
case object PreferCacheKey extends ControlKey[Bool](name = "preferCache")
|
||||
|
||||
case class PreferCacheField() extends BundleField(PreferCacheKey) {
|
||||
override def data: Bool = Output(Bool())
|
||||
override def default(x: Bool): Unit = { x := false.B }
|
||||
|
||||
override def default(x: Bool): Unit = {
|
||||
x := false.B
|
||||
}
|
||||
}
|
||||
|
||||
// indicate whether this block is dirty or not (only used in handle Release/ReleaseData)
|
||||
|
@ -70,10 +81,14 @@ case object DirtyKey extends ControlKey[Bool](name = "blockisdirty")
|
|||
|
||||
case class DirtyField() extends BundleField(DirtyKey) {
|
||||
override def data: Bool = Output(Bool())
|
||||
override def default(x: Bool): Unit = { x := true.B }
|
||||
|
||||
override def default(x: Bool): Unit = {
|
||||
x := true.B
|
||||
}
|
||||
}
|
||||
|
||||
case class HCCacheParameters(
|
||||
case class HCCacheParameters
|
||||
(
|
||||
name: String = "L2",
|
||||
level: Int = 2,
|
||||
ways: Int = 4,
|
||||
|
@ -91,11 +106,14 @@ case class HCCacheParameters(
|
|||
clientCaches: Seq[CacheParameters] = Nil,
|
||||
inclusive: Boolean = true,
|
||||
alwaysReleaseData: Boolean = false,
|
||||
tagECC: Option[String] = None,
|
||||
dataECC: Option[String] = None,
|
||||
echoField: Seq[BundleFieldBase] = Nil,
|
||||
reqField: Seq[BundleFieldBase] = Nil, // master
|
||||
respKey: Seq[BundleKeyBase] = Nil,
|
||||
reqKey: Seq[BundleKeyBase] = Seq(PrefetchKey, PreferCacheKey, AliasKey), // slave
|
||||
respField: Seq[BundleFieldBase] = Nil) {
|
||||
respField: Seq[BundleFieldBase] = Nil,
|
||||
sramCycleFactor: Int = 1) {
|
||||
require(ways > 0)
|
||||
require(sets > 0)
|
||||
require(channelBytes.d.get >= 8)
|
||||
|
@ -114,4 +132,5 @@ case class HCCacheParameters(
|
|||
}
|
||||
|
||||
case object EdgeInKey extends Field[TLEdgeIn]
|
||||
|
||||
case object EdgeOutKey extends Field[TLEdgeOut]
|
||||
|
|
|
@ -23,6 +23,7 @@ import chipsalliance.rocketchip.config.Parameters
|
|||
import chisel3._
|
||||
import chisel3.util._
|
||||
import freechips.rocketchip.tilelink._
|
||||
import huancun.utils.SReg
|
||||
|
||||
class SourceC(edge: TLEdgeOut)(implicit p: Parameters) extends HuanCunModule {
|
||||
val io = IO(new Bundle() {
|
||||
|
@ -35,6 +36,7 @@ class SourceC(edge: TLEdgeOut)(implicit p: Parameters) extends HuanCunModule {
|
|||
val queue_size = 6
|
||||
val queue_flow = true
|
||||
|
||||
val en = SReg.sren()
|
||||
val bs_busy = RegInit(false.B)
|
||||
val back_pressure = RegInit(false.B)
|
||||
val queue = Module(new Queue(chiselTypeOf(io.c.bits), entries = queue_size, flow = queue_flow))
|
||||
|
@ -47,10 +49,10 @@ class SourceC(edge: TLEdgeOut)(implicit p: Parameters) extends HuanCunModule {
|
|||
}
|
||||
val task_latch = RegEnable(io.task.bits, !bs_busy && io.task.valid)
|
||||
val task = Mux(!bs_busy, io.task.bits, task_latch)
|
||||
val taskWithData = io.task.valid && !back_pressure && io.task.bits.opcode(0)
|
||||
val taskWithData = io.task.valid && !back_pressure && io.task.bits.opcode(0) && en
|
||||
when(taskWithData) { bs_busy := true.B }
|
||||
when(io.bs_raddr.fire() && beat === ~0.U(beatBits.W)) { bs_busy := false.B }
|
||||
io.task.ready := !bs_busy && !back_pressure
|
||||
io.task.ready := !bs_busy && !back_pressure && en
|
||||
|
||||
// Read Datastorage
|
||||
val has_data = taskWithData || bs_busy
|
||||
|
@ -65,17 +67,21 @@ class SourceC(edge: TLEdgeOut)(implicit p: Parameters) extends HuanCunModule {
|
|||
val task_handled = Mux(has_data, io.bs_raddr.ready, io.task.fire())
|
||||
val s1_task = RegInit(io.task.bits)
|
||||
val s1_beat = RegInit(0.U(beatBits.W))
|
||||
val s1_valid = RegNext(task_handled, false.B)
|
||||
val s1_valid = RegInit(false.B)
|
||||
when(s1_valid && en){
|
||||
s1_valid := false.B
|
||||
}
|
||||
when(task_handled) {
|
||||
s1_valid := true.B
|
||||
s1_task := task
|
||||
s1_beat := beat
|
||||
}
|
||||
|
||||
// Stage 1 => Stage 2
|
||||
val s2_valid = RegNext(s1_valid, false.B)
|
||||
val s2_valid = RegNext(s1_valid && en, false.B)
|
||||
val s2_task = RegInit(io.task.bits)
|
||||
val s2_beat = RegInit(0.U(beatBits.W))
|
||||
when(s1_valid) {
|
||||
when(s1_valid && en) {
|
||||
s2_task := s1_task
|
||||
s2_beat := s1_beat
|
||||
}
|
||||
|
|
|
@ -24,6 +24,7 @@ import chisel3._
|
|||
import chisel3.util._
|
||||
import freechips.rocketchip.tilelink._
|
||||
import freechips.rocketchip.tilelink.TLMessages.{AcquireBlock, AcquirePerm, ReleaseAck}
|
||||
import huancun.utils.SReg
|
||||
|
||||
class SourceD(implicit p: Parameters) extends HuanCunModule {
|
||||
/*
|
||||
|
@ -155,15 +156,18 @@ class SourceD(implicit p: Parameters) extends HuanCunModule {
|
|||
// stage3
|
||||
val s3_latch = s2_valid && s3_ready
|
||||
val s3_full = RegInit(false.B)
|
||||
// wait counter for sram data
|
||||
val s3_wait = Reg(UInt(log2Ceil(cacheParams.sramCycleFactor).W))
|
||||
val s3_needData = RegInit(false.B)
|
||||
val s3_req = RegEnable(s2_req, s3_latch)
|
||||
val s3_releaseAck = RegEnable(s2_releaseAck, s3_latch)
|
||||
val s3_d = Wire(io.d.cloneType)
|
||||
val s3_queue = Module(new Queue(new DSData, 3, flow = true))
|
||||
val s3_can_go = if(cacheParams.sramCycleFactor == 1) true.B else s3_wait === 0.U
|
||||
|
||||
assert(!s3_full || s3_needData, "Only data task can go to stage3!")
|
||||
|
||||
when(d.ready) {
|
||||
when(s3_d.ready && s3_can_go) {
|
||||
s3_full := false.B
|
||||
s3_needData := false.B
|
||||
}
|
||||
|
@ -171,9 +175,13 @@ class SourceD(implicit p: Parameters) extends HuanCunModule {
|
|||
s3_full := true.B
|
||||
s3_needData := s2_needData
|
||||
}
|
||||
s3_wait := Mux(s3_latch,
|
||||
(cacheParams.sramCycleFactor - 1).U,
|
||||
Mux(s3_can_go, s3_wait, s3_wait - 1.U)
|
||||
)
|
||||
|
||||
val s3_rdata = s3_queue.io.deq.bits.data
|
||||
s3_d.valid := s3_valid
|
||||
s3_d.valid := s3_valid && s3_can_go
|
||||
s3_d.bits.opcode := s3_req.opcode
|
||||
s3_d.bits.param := Mux(s3_releaseAck, 0.U, s3_req.param)
|
||||
s3_d.bits.sink := s3_req.sinkId
|
||||
|
@ -184,14 +192,15 @@ class SourceD(implicit p: Parameters) extends HuanCunModule {
|
|||
s3_d.bits.corrupt := false.B
|
||||
s3_d.bits.echo.lift(DirtyKey).foreach(_ := s3_req.dirty)
|
||||
|
||||
s3_queue.io.enq.valid := RegNext(RegNext(
|
||||
s3_queue.io.enq.valid := RegNext(SReg.pipe(
|
||||
io.bs_raddr.fire() && !Mux(busy, s1_bypass_hit_reg, s1_bypass_hit_wire),
|
||||
false.B), false.B)
|
||||
false.B
|
||||
), false.B)
|
||||
s3_queue.io.enq.bits := io.bs_rdata
|
||||
assert(!s3_queue.io.enq.valid || s3_queue.io.enq.ready)
|
||||
s3_queue.io.deq.ready := s3_d.ready && s3_needData && s3_valid
|
||||
s3_queue.io.deq.ready := s3_d.ready && s3_needData && s3_valid && s3_can_go
|
||||
|
||||
s3_ready := !s3_valid || s3_d.ready
|
||||
s3_ready := !s3_valid || s3_d.ready && s3_can_go
|
||||
s3_valid := s3_full
|
||||
|
||||
TLArbiter.lowest(edgeIn, io.d, s3_d, s2_d)
|
||||
|
|
|
@ -32,6 +32,7 @@ class DirResult(implicit p: Parameters) extends DirectoryEntry with BaseDirResul
|
|||
val hit = Bool()
|
||||
val way = UInt(wayBits.W)
|
||||
val tag = UInt(tagBits.W)
|
||||
val error = Bool()
|
||||
}
|
||||
|
||||
class DirectoryIO(implicit p: Parameters) extends BaseDirectoryIO[DirResult, DirWrite, TagWrite] {
|
||||
|
@ -91,6 +92,7 @@ class Directory(implicit p: Parameters) extends BaseDirectory[DirResult, DirWrit
|
|||
resp.bits.state := selfResp.bits.dir.state
|
||||
resp.bits.clients := selfResp.bits.dir.clients
|
||||
resp.bits.prefetch.foreach(p => p := selfResp.bits.dir.prefetch.get)
|
||||
resp.bits.error := selfResp.bits.error
|
||||
}
|
||||
// Self Tag Write
|
||||
dir.io.tag_w.valid := io.tagWReq.valid
|
||||
|
|
|
@ -212,6 +212,8 @@ class MSHR()(implicit p: Parameters) extends BaseMSHR[DirResult, DirWrite, TagWr
|
|||
probes_done := 0.U
|
||||
bad_grant := false.B
|
||||
|
||||
assert(!io.dirResult.bits.hit || !io.dirResult.bits.error)
|
||||
|
||||
when(req.fromC) {
|
||||
// Release
|
||||
s_execute := false.B
|
||||
|
|
|
@ -38,12 +38,14 @@ class SelfDirResult(implicit p: Parameters) extends SelfDirEntry {
|
|||
val hit = Bool()
|
||||
val way = UInt(wayBits.W)
|
||||
val tag = UInt(tagBits.W)
|
||||
val error = Bool()
|
||||
}
|
||||
|
||||
class ClientDirResult(implicit p: Parameters) extends ClientDirEntry with HasClientInfo {
|
||||
val hit = Bool()
|
||||
val way = UInt(clientWayBits.W)
|
||||
val tag = UInt(clientTagBits.W)
|
||||
val error = Bool()
|
||||
|
||||
def parseTag(lineAddr: UInt): UInt = {
|
||||
lineAddr(clientSetBits + clientTagBits - 1, clientSetBits)
|
||||
|
@ -267,6 +269,7 @@ class Directory(implicit p: Parameters)
|
|||
resp.bits.self.tag := selfResp.bits.tag
|
||||
resp.bits.self.dirty := selfResp.bits.dir.dirty
|
||||
resp.bits.self.state := selfResp.bits.dir.state
|
||||
resp.bits.self.error := selfResp.bits.error
|
||||
resp.bits.self.clientStates := selfResp.bits.dir.clientStates
|
||||
resp.bits.self.prefetch.foreach(p => p := selfResp.bits.dir.prefetch.get)
|
||||
resp.bits.clients.zip(clientResps).foreach {
|
||||
|
@ -276,6 +279,7 @@ class Directory(implicit p: Parameters)
|
|||
resp.tag := clientResp.bits.tag
|
||||
resp.state := clientResp.bits.dir.state
|
||||
resp.alias.foreach(_ := clientResp.bits.dir.alias.get)
|
||||
resp.error := clientResp.bits.error
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -686,6 +686,9 @@ class MSHR()(implicit p: Parameters) extends BaseMSHR[DirResult, SelfDirWrite, S
|
|||
|
||||
reset_all_flags()
|
||||
|
||||
assert(!io.dirResult.bits.self.hit || !io.dirResult.bits.self.error)
|
||||
io.dirResult.bits.clients.foreach(r => assert(!r.hit || !r.error))
|
||||
|
||||
when(req.fromC) {
|
||||
c_schedule()
|
||||
}.elsewhen(req.fromB) {
|
||||
|
|
|
@ -0,0 +1,231 @@
|
|||
/***************************************************************************************
|
||||
* Copyright (c) 2020-2021 Institute of Computing Technology, Chinese Academy of Sciences
|
||||
* Copyright (c) 2020-2021 Peng Cheng Laboratory
|
||||
*
|
||||
* XiangShan is licensed under Mulan PSL v2.
|
||||
* You can use this software according to the terms and conditions of the Mulan PSL v2.
|
||||
* You may obtain a copy of Mulan PSL v2 at:
|
||||
* http://license.coscl.org.cn/MulanPSL2
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||
*
|
||||
* See the Mulan PSL v2 for more details.
|
||||
***************************************************************************************/
|
||||
|
||||
// See LICENSE.Berkeley for license details.
|
||||
|
||||
package huancun.utils
|
||||
|
||||
import chisel3._
|
||||
import chisel3.util._
|
||||
import chisel3.util.random.LFSR
|
||||
|
||||
abstract class Decoding
|
||||
{
|
||||
def uncorrected: UInt
|
||||
def corrected: UInt
|
||||
def correctable: Bool
|
||||
def uncorrectable: Bool // If true, correctable should be ignored
|
||||
def error = correctable || uncorrectable
|
||||
}
|
||||
|
||||
abstract class Code
|
||||
{
|
||||
def canDetect: Boolean
|
||||
def canCorrect: Boolean
|
||||
|
||||
def width(w0: Int): Int
|
||||
|
||||
/** Encode x to a codeword suitable for decode.
|
||||
* If poison is true, the decoded value will report uncorrectable
|
||||
* error despite uncorrected == corrected == x.
|
||||
*/
|
||||
def encode(x: UInt, poison: Bool = false.B): UInt
|
||||
def decode(x: UInt): Decoding
|
||||
|
||||
/** Copy the bits in x to the right bit positions in an encoded word,
|
||||
* so that x === decode(swizzle(x)).uncorrected; but don't generate
|
||||
* the other code bits, so decode(swizzle(x)).error might be true.
|
||||
* For codes for which this operation is not trivial, throw an
|
||||
* UnsupportedOperationException. */
|
||||
def swizzle(x: UInt): UInt
|
||||
}
|
||||
|
||||
class IdentityCode extends Code
|
||||
{
|
||||
def canDetect = false
|
||||
def canCorrect = false
|
||||
|
||||
def width(w0: Int) = w0
|
||||
def encode(x: UInt, poison: Bool = false.B) = {
|
||||
require (poison.isLit && poison.litValue == 0, "IdentityCode can not be poisoned")
|
||||
x
|
||||
}
|
||||
def swizzle(x: UInt) = x
|
||||
def decode(y: UInt) = new Decoding {
|
||||
def uncorrected = y
|
||||
def corrected = y
|
||||
def correctable = false.B
|
||||
def uncorrectable = false.B
|
||||
}
|
||||
}
|
||||
|
||||
class ParityCode extends Code
|
||||
{
|
||||
def canDetect = true
|
||||
def canCorrect = false
|
||||
|
||||
def width(w0: Int) = w0+1
|
||||
def encode(x: UInt, poison: Bool = false.B) = Cat(x.xorR ^ poison, x)
|
||||
def swizzle(x: UInt) = Cat(false.B, x)
|
||||
def decode(y: UInt) = new Decoding {
|
||||
val uncorrected = y(y.getWidth-2,0)
|
||||
val corrected = uncorrected
|
||||
val correctable = false.B
|
||||
val uncorrectable = y.xorR
|
||||
}
|
||||
}
|
||||
|
||||
class SECCode extends Code
|
||||
{
|
||||
def canDetect = true
|
||||
def canCorrect = true
|
||||
|
||||
// SEC codes may or may not be poisonous depending on the length
|
||||
// If the code is perfect, every non-codeword is correctable
|
||||
def poisonous(n: Int) = !isPow2(n+1)
|
||||
|
||||
def width(k: Int) = {
|
||||
val m = log2Floor(k) + 1
|
||||
k + m + (if((1 << m) < m+k+1) 1 else 0)
|
||||
}
|
||||
def swizzle(x: UInt) = {
|
||||
val k = x.getWidth
|
||||
val n = width(k)
|
||||
Cat(0.U((n-k).W), x)
|
||||
}
|
||||
|
||||
// An (n=16, k=11) Hamming code is naturally encoded as:
|
||||
// PPxPxxxPxxxxxxxP where P are parity bits and x are data
|
||||
// Indexes typically start at 1, because then the P are on powers of two
|
||||
// In systematic coding, you put all the data in the front:
|
||||
// xxxxxxxxxxxPPPPP
|
||||
// Indexes typically start at 0, because Computer Science
|
||||
// For sanity when reading SRAMs, you want systematic form.
|
||||
|
||||
private def impl(n: Int, k: Int) = {
|
||||
require (n >= 3 && k >= 1 && !isPow2(n))
|
||||
val hamm2sys = IndexedSeq.tabulate(n+1) { i =>
|
||||
if (i == 0) {
|
||||
n /* undefined */
|
||||
} else if (isPow2(i)) {
|
||||
k + log2Ceil(i)
|
||||
} else {
|
||||
i - 1 - log2Ceil(i)
|
||||
}
|
||||
}
|
||||
val sys2hamm = hamm2sys.zipWithIndex.sortBy(_._1).map(_._2).toIndexedSeq
|
||||
def syndrome(j: Int) = {
|
||||
val bit = 1 << j
|
||||
("b" + Seq.tabulate(n) { i =>
|
||||
if ((sys2hamm(i) & bit) != 0) "1" else "0"
|
||||
}.reverse.mkString).U
|
||||
}
|
||||
(hamm2sys, sys2hamm, syndrome _)
|
||||
}
|
||||
|
||||
def encode(x: UInt, poison: Bool = false.B) = {
|
||||
val k = x.getWidth
|
||||
val n = width(k)
|
||||
val (_, _, syndrome) = impl(n, k)
|
||||
|
||||
require ((poison.isLit && poison.litValue == 0) || poisonous(n), s"SEC code of length ${n} cannot be poisoned")
|
||||
|
||||
/* By setting the entire syndrome on poison, the corrected bit falls off the end of the code */
|
||||
val syndromeUInt = VecInit.tabulate(n-k) { j => (syndrome(j)(k-1, 0) & x).xorR ^ poison }.asUInt
|
||||
Cat(syndromeUInt, x)
|
||||
}
|
||||
|
||||
def decode(y: UInt) = new Decoding {
|
||||
val n = y.getWidth
|
||||
val k = n - log2Ceil(n)
|
||||
val (_, sys2hamm, syndrome) = impl(n, k)
|
||||
|
||||
val syndromeUInt = VecInit.tabulate(n-k) { j => (syndrome(j) & y).xorR }.asUInt
|
||||
|
||||
val hammBadBitOH = UIntToOH(syndromeUInt, n+1)
|
||||
val sysBadBitOH = VecInit.tabulate(k) { i => hammBadBitOH(sys2hamm(i)) }.asUInt
|
||||
|
||||
val uncorrected = y(k-1, 0)
|
||||
val corrected = uncorrected ^ sysBadBitOH
|
||||
val correctable = syndromeUInt.orR
|
||||
val uncorrectable = if (poisonous(n)) { syndromeUInt > n.U } else { false.B }
|
||||
}
|
||||
}
|
||||
|
||||
class SECDEDCode extends Code
|
||||
{
|
||||
def canDetect = true
|
||||
def canCorrect = true
|
||||
|
||||
private val sec = new SECCode
|
||||
private val par = new ParityCode
|
||||
|
||||
def width(k: Int) = sec.width(k)+1
|
||||
def encode(x: UInt, poison: Bool = false.B) = {
|
||||
// toggling two bits ensures the error is uncorrectable
|
||||
// to ensure corrected == uncorrected, we pick one redundant
|
||||
// bit from SEC (the highest); correcting it does not affect
|
||||
// corrected == uncorrected. the second toggled bit is the
|
||||
// parity bit, which also does not appear in the decoding
|
||||
val toggle_lo = Cat(poison.asUInt, poison.asUInt)
|
||||
val toggle_hi = toggle_lo << (sec.width(x.getWidth)-1)
|
||||
par.encode(sec.encode(x)) ^ toggle_hi
|
||||
}
|
||||
def swizzle(x: UInt) = par.swizzle(sec.swizzle(x))
|
||||
def decode(x: UInt) = new Decoding {
|
||||
val secdec = sec.decode(x(x.getWidth-2,0))
|
||||
val pardec = par.decode(x)
|
||||
|
||||
val uncorrected = secdec.uncorrected
|
||||
val corrected = secdec.corrected
|
||||
val correctable = pardec.uncorrectable
|
||||
val uncorrectable = !pardec.uncorrectable && secdec.correctable
|
||||
}
|
||||
}
|
||||
|
||||
object ErrGen
|
||||
{
|
||||
// generate a 1-bit error with approximate probability 2^-f
|
||||
def apply(width: Int, f: Int): UInt = {
|
||||
require(width > 0 && f >= 0 && log2Up(width) + f <= 16)
|
||||
UIntToOH(LFSR(16)(log2Up(width)+f-1,0))(width-1,0)
|
||||
}
|
||||
def apply(x: UInt, f: Int): UInt = x ^ apply(x.getWidth, f)
|
||||
}
|
||||
|
||||
trait CanHaveErrors extends Bundle {
|
||||
val correctable: Option[ValidIO[UInt]]
|
||||
val uncorrectable: Option[ValidIO[UInt]]
|
||||
}
|
||||
|
||||
case class ECCParams(
|
||||
bytes: Int = 1,
|
||||
code: Code = new IdentityCode,
|
||||
notifyErrors: Boolean = false
|
||||
)
|
||||
|
||||
object Code {
|
||||
def fromString(s: Option[String]): Code = fromString(s.getOrElse("none"))
|
||||
def fromString(s: String): Code = s.toLowerCase match {
|
||||
case "none" => new IdentityCode
|
||||
case "identity" => new IdentityCode
|
||||
case "parity" => new ParityCode
|
||||
case "sec" => new SECCode
|
||||
case "secded" => new SECDEDCode
|
||||
case _ => throw new IllegalArgumentException("Unknown ECC type")
|
||||
}
|
||||
}
|
||||
|
|
@ -95,13 +95,22 @@ class SRAMTemplate[T <: Data](
|
|||
shouldReset: Boolean = false,
|
||||
holdRead: Boolean = false,
|
||||
singlePort: Boolean = false,
|
||||
bypassWrite: Boolean = false)
|
||||
bypassWrite: Boolean = false,
|
||||
cycleFactor: Int = 1)
|
||||
extends Module {
|
||||
val io = IO(new Bundle {
|
||||
val r = Flipped(new SRAMReadBus(gen, set, way))
|
||||
val w = Flipped(new SRAMWriteBus(gen, set, way))
|
||||
})
|
||||
|
||||
if(cycleFactor != 1){
|
||||
require(!shouldReset)
|
||||
require(!holdRead)
|
||||
require(singlePort)
|
||||
require(!bypassWrite)
|
||||
require(isPow2(cycleFactor))
|
||||
}
|
||||
|
||||
val wordType = UInt(gen.getWidth.W)
|
||||
val array = SyncReadMem(set, Vec(way, wordType))
|
||||
val (resetState, resetSet) = (WireInit(false.B), WireInit(0.U))
|
||||
|
@ -115,15 +124,31 @@ class SRAMTemplate[T <: Data](
|
|||
resetSet := _resetSet
|
||||
}
|
||||
|
||||
val (ren, wen) = (io.r.req.valid, io.w.req.valid || resetState)
|
||||
val cycleBits = if(cycleFactor == 1) 1 else log2Ceil(cycleFactor)
|
||||
val counter = RegInit(0.U(cycleBits.W))
|
||||
counter := counter + 1.U
|
||||
val busy = if(cycleFactor == 1) false.B else counter =/= 0.U
|
||||
|
||||
val ren_reg = RegEnable(io.r.req.valid, false.B, !busy)
|
||||
val wen_reg = RegEnable(io.w.req.valid, false.B, !busy)
|
||||
|
||||
val ren = Mux(busy, ren_reg, io.r.req.valid)
|
||||
val wen = Mux(busy, wen_reg, io.w.req.valid || resetState)
|
||||
|
||||
val rreq_reg = RegEnable(io.r.req.bits, !busy)
|
||||
val wreq_reg = RegEnable(io.w.req.bits, !busy)
|
||||
|
||||
val rreq = Mux(busy, rreq_reg, io.r.req.bits)
|
||||
val wreq = Mux(busy, wreq_reg, io.w.req.bits)
|
||||
|
||||
val realRen = (if (singlePort) ren && !wen else ren)
|
||||
|
||||
val setIdx = Mux(resetState, resetSet, io.w.req.bits.setIdx)
|
||||
val wdata = VecInit(Mux(resetState, 0.U.asTypeOf(Vec(way, gen)), io.w.req.bits.data).map(_.asTypeOf(wordType)))
|
||||
val waymask = Mux(resetState, Fill(way, "b1".U), io.w.req.bits.waymask.getOrElse("b1".U))
|
||||
val setIdx = Mux(resetState, resetSet, wreq.setIdx)
|
||||
val wdata = VecInit(Mux(resetState, 0.U.asTypeOf(Vec(way, gen)), wreq.data).map(_.asTypeOf(wordType)))
|
||||
val waymask = Mux(resetState, Fill(way, "b1".U), wreq.waymask.getOrElse("b1".U))
|
||||
when(wen) { array.write(setIdx, wdata, waymask.asBools) }
|
||||
|
||||
val raw_rdata = array.read(io.r.req.bits.setIdx, realRen)
|
||||
val raw_rdata = array.read(rreq.setIdx, realRen)
|
||||
|
||||
// bypass for dual-port SRAMs
|
||||
require(!bypassWrite || bypassWrite && !singlePort)
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package huancun.utils
|
||||
|
||||
import chipsalliance.rocketchip.config.Parameters
|
||||
import chisel3._
|
||||
import chisel3.util._
|
||||
import huancun.HCCacheParamsKey
|
||||
|
||||
object SReg {
|
||||
def get_sram_cycle()(implicit p: Parameters): (Int, Int) = {
|
||||
val cycles = p(HCCacheParamsKey).sramCycleFactor
|
||||
require(isPow2(cycles))
|
||||
val cycle_bits = log2Ceil(cycles)
|
||||
(cycles, cycle_bits)
|
||||
}
|
||||
def sren()(implicit p: Parameters): Bool = {
|
||||
val (cycle, cycle_bits) = get_sram_cycle()
|
||||
if(cycle == 1) return true.B
|
||||
val counter = RegInit(0.U(cycle_bits.W))
|
||||
counter := counter + 1.U
|
||||
counter === 0.U
|
||||
}
|
||||
def pipe[T <: Data](x: T)(implicit p: Parameters): T = {
|
||||
val (cycles, _) = get_sram_cycle()
|
||||
if(cycles == 1) return RegNext(x)
|
||||
val regs = List.tabulate[T](cycles){_ => Reg(chiselTypeOf(x))}
|
||||
regs.fold(x)((a, b) => { b := a; b })
|
||||
}
|
||||
def pipe[T <: Data](x: T, init: T)(implicit p: Parameters): T = {
|
||||
val (cycles, _) = get_sram_cycle()
|
||||
if(cycles == 1) return RegNext(x, init)
|
||||
val regs = List.tabulate[T](cycles){_ => RegInit(chiselTypeOf(x), init)}
|
||||
regs.fold(x)((a, b) => { b := a; b })
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue