Merge branch 'non-inclusive' of github.com:OpenXiangShan/HuanCun into HEAD

This commit is contained in:
LinJiawei 2021-09-29 20:36:16 +08:00
commit 7e7b742b1c
12 changed files with 422 additions and 69 deletions

View File

@ -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,

View File

@ -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)

View File

@ -5,7 +5,7 @@
* 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
* 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,
@ -28,40 +28,51 @@ import huancun.prefetch.PrefetchParameters
case object HCCacheParamsKey extends Field[HCCacheParameters](HCCacheParameters())
case class CacheParameters(
name: String,
sets: Int,
ways: Int,
blockBytes: Int = 64,
aliasBitsOpt: Option[Int] = None,
inner: Seq[CacheParameters] = Nil) {
case class CacheParameters
(
name: String,
sets: Int,
ways: Int,
blockBytes: Int = 64,
aliasBitsOpt: Option[Int] = None,
inner: Seq[CacheParameters] = Nil
) {
val capacity = sets * ways * blockBytes
val setBits = log2Ceil(sets)
val offsetBits = log2Ceil(blockBytes)
val needResolveAlias = aliasBitsOpt.nonEmpty
}
case object PrefetchKey extends ControlKey[Bool](name ="needHint")
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,32 +81,39 @@ 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(
name: String = "L2",
level: Int = 2,
ways: Int = 4,
sets: Int = 128,
blockBytes: Int = 64,
pageBytes: Int = 4096,
replacement: String = "plru",
mshrs: Int = 16,
dirReadPorts: Int = 1,
dirReg: Boolean = true,
enableDebug: Boolean = false,
enablePerf: Boolean = false,
channelBytes: TLChannelBeatBytes = TLChannelBeatBytes(32),
prefetch: Option[PrefetchParameters] = None,
clientCaches: Seq[CacheParameters] = Nil,
inclusive: Boolean = true,
case class HCCacheParameters
(
name: String = "L2",
level: Int = 2,
ways: Int = 4,
sets: Int = 128,
blockBytes: Int = 64,
pageBytes: Int = 4096,
replacement: String = "plru",
mshrs: Int = 16,
dirReadPorts: Int = 1,
dirReg: Boolean = true,
enableDebug: Boolean = false,
enablePerf: Boolean = false,
channelBytes: TLChannelBeatBytes = TLChannelBeatBytes(32),
prefetch: Option[PrefetchParameters] = None,
clientCaches: Seq[CacheParameters] = Nil,
inclusive: Boolean = true,
alwaysReleaseData: Boolean = false,
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) {
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,
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]

View File

@ -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
}

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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
}
}

View File

@ -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) {

View File

@ -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")
}
}

View File

@ -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)

View File

@ -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 })
}
}