1
0
Fork 0

Merge pull request #1279 from freechipsproject/ipxact_descs

More IP-XACT-like RegFieldDesc
This commit is contained in:
Megan Wachs 2018-03-14 06:46:00 -07:00 committed by GitHub
commit 78dad3e89b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 159 additions and 84 deletions

View File

@ -254,7 +254,8 @@ object WNotifyWire {
set := valid
value := data
Bool(true)
}), Some(RegFieldDesc(name = name, desc = desc, access = RegFieldAccessType.WSPECIAL)))
}), Some(RegFieldDesc(name = name, desc = desc,
access = RegFieldAccessType.W)))
}
}
@ -898,12 +899,13 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int, beatBytes: I
programBufferMem.zipWithIndex.map {case (x, i) => RegField(8, x, RegFieldDesc(s"debug_progbuf_$i", ""))}),
// These sections are read-only.
IMPEBREAK(cfg)-> {if (cfg.hasImplicitEbreak) Seq(RegField.r(32, Instructions.EBREAK.value.U, RegFieldDesc("debug_impebreak", "Debug Implicit EBREAK"))) else Nil},
WHERETO -> Seq(RegField.r(32, jalAbstract.asUInt, RegFieldDesc("debug_whereto", "Instruction filled in by Debug Module to control hart in Debug Mode"))),
IMPEBREAK(cfg)-> {if (cfg.hasImplicitEbreak) Seq(RegField.r(32, Instructions.EBREAK.value.U,
RegFieldDesc("debug_impebreak", "Debug Implicit EBREAK", reset=Some(Instructions.EBREAK.value)))) else Nil},
WHERETO -> Seq(RegField.r(32, jalAbstract.asUInt, RegFieldDesc("debug_whereto", "Instruction filled in by Debug Module to control hart in Debug Mode", volatile = true))),
ABSTRACT(cfg) -> RegFieldGroup("debug_abstract", Some("Instructions generated by Debug Module"),
abstractGeneratedMem.zipWithIndex.map{ case (x,i) => RegField.r(32, x, RegFieldDesc(s"debug_abstract_$i", ""))}),
abstractGeneratedMem.zipWithIndex.map{ case (x,i) => RegField.r(32, x, RegFieldDesc(s"debug_abstract_$i", "", volatile=true))}),
FLAGS -> RegFieldGroup("debug_flags", Some("Memory region used to control hart going/resuming in Debug Mode"),
flags.zipWithIndex.map{case(x, i) => RegField.r(8, x.asUInt(), RegFieldDesc(s"debug_flags_$i", ""))}),
flags.zipWithIndex.map{case(x, i) => RegField.r(8, x.asUInt(), RegFieldDesc(s"debug_flags_$i", "", volatile=true))}),
ROMBASE -> RegFieldGroup("debug_rom", Some("Debug ROM"),
DebugRomContents().zipWithIndex.map{case (x, i) => RegField.r(8, (x & 0xFF).U(8.W), RegFieldDesc(s"debug_rom_$i", "", reset=Some(x)))})
)

View File

@ -82,10 +82,12 @@ class CLINT(params: CLINTParams, beatBytes: Int)(implicit p: Parameters) extends
*/
node.regmap(
0 -> RegFieldGroup ("msip", Some("MSIP Bits"), ipi.zipWithIndex.map{ case (r, i) => RegField(ipiWidth, r, RegFieldDesc(s"msip_$i", s"MSIP bit for Hart $i", reset=Some(0)))}),
timecmpOffset(0) -> timecmp.zipWithIndex.flatMap{ case (t, i) =>
RegFieldGroup(s"mtimecmp_$i", Some(s"MTIMECMP for hart $i"), RegField.bytes(t, Some(RegFieldDesc(s"mtimecmp_$i", "", reset=None))))},
timeOffset -> RegFieldGroup("mtime", Some("Timer Register"), RegField.bytes(time, Some(RegFieldDesc("mtime", "", reset=Some(0)))))
0 -> RegFieldGroup ("msip", Some("MSIP Bits"), ipi.zipWithIndex.map{ case (r, i) =>
RegField(ipiWidth, r, RegFieldDesc(s"msip_$i", s"MSIP bit for Hart $i", reset=Some(0)))}),
timecmpOffset(0) -> timecmp.zipWithIndex.flatMap{ case (t, i) => RegFieldGroup(s"mtimecmp_$i", Some(s"MTIMECMP for hart $i"),
RegField.bytes(t, Some(RegFieldDesc(s"mtimecmp_$i", "", reset=None))))},
timeOffset -> RegFieldGroup("mtime", Some("Timer Register"),
RegField.bytes(time, Some(RegFieldDesc("mtime", "", reset=Some(0), volatile=true))))
)
}
}

View File

@ -170,14 +170,16 @@ class TLPLIC(params: PLICParams, beatBytes: Int)(implicit p: Parameters) extends
def priorityRegDesc(i: Int) = if (i > 0) {
RegFieldDesc(s"priority_$i", s"Acting priority of interrupt source $i",
reset=if (nPriorities > 0) None else Some(1), access=RegFieldAccessType.RWSPECIAL)
reset=if (nPriorities > 0) None else Some(1),
wrType=Some(RegFieldWrType.MODIFY))
} else {
RegFieldDesc("reserved", "", reset=Some(0), access=RegFieldAccessType.R)
RegFieldDesc.reserved
}
def pendingRegDesc(i: Int) = if (i > 0) {
RegFieldDesc(s"pending_$i", s"Set to 1 if interrupt source $i is pending, regardless of its enable or priority setting.")
RegFieldDesc(s"pending_$i", s"Set to 1 if interrupt source $i is pending, regardless of its enable or priority setting.",
volatile = true)
} else {
RegFieldDesc("reserved", "", reset=Some(0), access=RegFieldAccessType.R)
RegFieldDesc.reserved
}
def priorityRegField(x: UInt, i: Int) = if (nPriorities > 0) RegField(32, x, priorityRegDesc(i)) else RegField.r(32, x, priorityRegDesc(i))
@ -193,7 +195,7 @@ class TLPLIC(params: PLICParams, beatBytes: Int)(implicit p: Parameters) extends
e.zipWithIndex.map{case (b, j) => if (j > 0) {
RegField(1, b, RegFieldDesc(s"enable_${i}_${j}", s"Enable interrupt for source $j for target $i.", reset=None))
} else {
RegField(1, b, RegFieldDesc("reserved", "", reset=Some(0), access=RegFieldAccessType.R))
RegField(1, b, RegFieldDesc.reserved)
}})
}
@ -229,7 +231,8 @@ class TLPLIC(params: PLICParams, beatBytes: Int)(implicit p: Parameters) extends
}
def thresholdRegDesc(i: Int) = RegFieldDesc(s"threshold_$i", s"Interrupt & claim threshold for target $i. Maximum value is ${nPriorities}.",
reset=if (nPriorities > 0) None else Some(1), access=RegFieldAccessType.RWSPECIAL)
reset=if (nPriorities > 0) None else Some(1),
wrType=Some(RegFieldWrType.MODIFY))
def thresholdRegField(x: UInt, i: Int) = if (nPriorities > 0) RegField(32, x, thresholdRegDesc(i)) else RegField.r(32, x, thresholdRegDesc(i))
val hartRegFields = Seq.tabulate(nHarts) { i =>
@ -251,7 +254,9 @@ class TLPLIC(params: PLICParams, beatBytes: Int)(implicit p: Parameters) extends
s"Claim/Complete register for Target $i. Reading this register returns the claimed interrupt number and makes it no longer pending." +
s"Writing the interrupt number back completes the interrupt.",
reset = None,
access = RegFieldAccessType.RWSPECIAL))
wrType = Some(RegFieldWrType.MODIFY),
rdAction = Some(RegFieldRdAction.MODIFY),
volatile = true))
)
)
}

View File

@ -0,0 +1,23 @@
// See LICENSE.SiFive for license details.
package freechips.rocketchip.regmapper
import org.json4s.JsonDSL._
import org.json4s.jackson.JsonMethods.{pretty, render}
object RegMappingAnnotation {
def serialize(base: BigInt, name: String, mapping: RegField.Map*): String = {
val regDescs = mapping.flatMap { case (byte, seq) =>
seq.map(_.width).scanLeft(0)(_ + _).zip(seq).map { case (bit, f) =>
val anonName = s"unnamedRegField${byte.toHexString}_${bit}"
(f.desc.map{ _.name}.getOrElse(anonName)) -> f.toJson(byte, bit)
}
}
pretty(render(
("peripheral" -> (
("displayName" -> name) ~
("baseAddress" -> s"0x${base.toInt.toHexString}") ~
("regfields" -> regDescs)))))
}
}

View File

@ -8,6 +8,8 @@ import freechips.rocketchip.util.{AsyncResetRegVec, SimpleRegIO}
object DescribedReg {
import freechips.rocketchip.regmapper.RegFieldAccessType._
import freechips.rocketchip.regmapper.RegFieldWrType._
import freechips.rocketchip.regmapper.RegFieldRdAction._
def apply[T <: Data](
gen: => T,
@ -15,9 +17,12 @@ object DescribedReg {
desc: String,
reset: Option[T],
access: RegFieldAccessType = RW,
wrType: Option[RegFieldWrType] = None,
rdAction: Option[RegFieldRdAction] = None,
volatile: Boolean = false,
enumerations: Map[BigInt, (String, String)] = Map()): (T, RegFieldDesc) = {
val rdesc = RegFieldDesc(name, desc, None, None,
access, reset.map{_.litValue}, enumerations)
access, wrType, rdAction, volatile, reset.map{_.litValue}, enumerations)
val reg = reset.map{i => RegInit(i)}.getOrElse(Reg(gen))
reg.suggestName(name + "_reg")
(reg, rdesc)
@ -29,9 +34,12 @@ object DescribedReg {
desc: String,
reset: Int,
access: RegFieldAccessType = RW,
wrType: Option[RegFieldWrType] = None,
rdAction: Option[RegFieldRdAction] = None,
volatile: Boolean = false,
enumerations: Map[BigInt, (String, String)] = Map()): (SimpleRegIO, RegFieldDesc) = {
val rdesc = RegFieldDesc(name, desc, None, None,
access, Some(reset), enumerations)
access, wrType, rdAction, volatile, Some(reset), enumerations)
val reg = Module(new AsyncResetRegVec(w = width, init = reset))
reg.suggestName(name + "_reg")
(reg.io, rdesc)

View File

@ -5,8 +5,11 @@ package freechips.rocketchip.regmapper
import Chisel._
import chisel3.util.{ReadyValidIO}
import freechips.rocketchip.util.{SimpleRegIO}
import org.json4s.JsonDSL._
import org.json4s.JsonAST.JValue
import org.json4s.jackson.JsonMethods.{pretty, render}
import freechips.rocketchip.util.{SimpleRegIO}
// This information is not used internally by the regmap(...) function.
// However, the author of a RegField may be the best person to provide this
@ -15,26 +18,47 @@ import freechips.rocketchip.util.{SimpleRegIO}
object RegFieldAccessType extends scala.Enumeration {
type RegFieldAccessType = Value
val R, W, RW, RSPECIAL, WSPECIAL, RWSPECIAL, OTHER = Value
val R, W, RW = Value
}
import RegFieldAccessType._
object RegFieldWrType extends scala.Enumeration {
type RegFieldWrType = Value
val ONE_TO_CLEAR, ONE_TO_SET, ONE_TO_TOGGLE, ZERO_TO_CLEAR,
ZERO_TO_SET, ZERO_TO_TOGGLE, CLEAR, SET, MODIFY = Value
}
import RegFieldWrType._
object RegFieldRdAction extends scala.Enumeration {
type RegFieldRdAction = Value
val CLEAR, SET, MODIFY = Value
}
import RegFieldRdAction._
case class RegFieldDesc (
name: String,
desc: String,
group: Option[String] = None,
groupDesc: Option[String] = None,
access: RegFieldAccessType = RegFieldAccessType.RW,
wrType: Option[RegFieldWrType] = None,
rdAction: Option[RegFieldRdAction] = None,
volatile: Boolean = false,
// TODO: testable?
reset: Option[BigInt] = None,
enumerations: Map[BigInt, (String, String)] = Map()
){
}
// Our descriptions are in terms of RegFields only, which is somewhat unusual for
// developers who are used to things being defined as bitfields within registers.
// The "Group" allows a string & (optional) description to be added which describes the conceptual "Group"
// the RegField belongs to. This can be used by downstream flows as they see fit to
// present the information.
object RegFieldDesc {
def reserved: RegFieldDesc = RegFieldDesc("reserved", "", access=RegFieldAccessType.R, reset=Some(0))
}
// Our descriptions are in terms of RegFields only, which is somewhat
// unusual for developers who are used to things being defined as bitfields
// within registers. The "Group" allows a string & (optional) description
// to be added which describes the conceptual "Group" the RegField belongs to.
// This can be used by downstream flows as they see fit to present the information.
object RegFieldGroup {
def apply (name: String, desc: Option[String], regs: Seq[RegField], descFirstOnly: Boolean = true): Seq[RegField] = {
@ -116,8 +140,29 @@ object RegWriteFn
case class RegField(width: Int, read: RegReadFn, write: RegWriteFn, desc: Option[RegFieldDesc])
{
require (width > 0, s"RegField width must be > 0, not $width")
def pipelined = !read.combinational || !write.combinational
def readOnly = this.copy(write = (), desc = this.desc.map(_.copy(access = RegFieldAccessType.R)))
def toJson(byteOffset: Int, bitOffset: Int): JValue = {
( ("byteOffset" -> s"0x${byteOffset.toHexString}") ~
("bitOffset" -> bitOffset) ~
("bitWidth" -> width) ~
("name" -> desc.map(_.name)) ~
("description" -> desc.map{ d=> if (d.desc == "") None else Some(d.desc)}) ~
("resetValue" -> desc.map{_.reset}) ~
("group" -> desc.map{_.group}) ~
("groupDesc" -> desc.map{_.groupDesc}) ~
("accessType" -> desc.map {d => d.access.toString}) ~
("writeType" -> desc.map {d => d.wrType.map(_.toString)}) ~
("readAction" -> desc.map {d => d.rdAction.map(_.toString)}) ~
("volatile" -> desc.map {d => if (d.volatile) Some(true) else None}) ~
("enumerations" -> desc.map {d =>
Option(d.enumerations.map { case (key, (name, edesc)) =>
(("value" -> key) ~ ("name" -> name) ~ ("description" -> edesc))
}).filter(_.nonEmpty)}) )
}
}
object RegField
@ -125,8 +170,7 @@ object RegField
// Byte address => sequence of bitfields, lowest index => lowest address
type Map = (Int, Seq[RegField])
def apply(n: Int) : RegField = apply(n, (), (),
Some(RegFieldDesc("reserved", "", access = RegFieldAccessType.R, reset = Some(0))))
def apply(n: Int) : RegField = apply(n, (), (), Some(RegFieldDesc.reserved))
def apply(n: Int, r: RegReadFn, w: RegWriteFn) : RegField = apply(n, r, w, None)
def apply(n: Int, r: RegReadFn, w: RegWriteFn, desc: RegFieldDesc) : RegField = apply(n, r, w, Some(desc))
@ -142,7 +186,7 @@ object RegField
// Setting takes priority over clearing.
def w1ToClear(n: Int, reg: UInt, set: UInt, desc: Option[RegFieldDesc] = None): RegField =
RegField(n, reg, RegWriteFn((valid, data) => { reg := ~(~reg | Mux(valid, data, UInt(0))) | set; Bool(true) }),
desc.map{_.copy(access = RegFieldAccessType.RWSPECIAL)})
desc.map{_.copy(access = RegFieldAccessType.RW, wrType=Some(RegFieldWrType.ONE_TO_CLEAR), volatile = true)})
// This RegField wraps an explicit register
// (e.g. Black-Boxed Register) to create a R/W register.
@ -151,26 +195,41 @@ object RegField
bb.en := valid
bb.d := data
Bool(true)
}), desc.map{_.copy(access = RegFieldAccessType.RW)})
}), desc)
// Create byte-sized read-write RegFields out of a large UInt register.
// It is updated when any of the bytes are written. Because the RegFields
// are all byte-sized, this is also suitable when a register is larger
// It is updated when any of the (implemented) bytes are written, the non-written
// bytes are just copied over from their current value.
// Because the RegField are all byte-sized, this is also suitable when a register is larger
// than the intended bus width of the device (atomic updates are impossible).
def bytes(reg: UInt, numBytes: Int, desc: Option[RegFieldDesc]): Seq[RegField] = {
val pad = reg | UInt(0, width = 8*numBytes)
require(reg.getWidth * 8 >= numBytes, "Can't break a ${reg.getWidth}-bit-wide register into only ${numBytes} bytes.")
val numFullBytes = reg.getWidth/8
val numPartialBytes = if ((reg.getWidth % 8) > 0) 1 else 0
val numPadBytes = numBytes - numFullBytes - numPartialBytes
val pad = reg | UInt(0, width = 8*numBytes)
val oldBytes = Vec.tabulate(numBytes) { i => pad(8*(i+1)-1, 8*i) }
val newBytes = Wire(init = oldBytes)
val valids = Wire(init = Vec.fill(numBytes) { Bool(false) })
when (valids.reduce(_ || _)) { reg := newBytes.asUInt }
Seq.tabulate(numBytes) { i =>
def wrFn(i: Int): RegWriteFn = RegWriteFn((valid, data) => {
valids(i) := valid
when (valid) {newBytes(i) := data}
Bool(true)
})
val fullBytes = Seq.tabulate(numFullBytes) { i =>
val newDesc = desc.map {d => d.copy(name = d.name + s"_$i")}
RegField(8, oldBytes(i),
RegWriteFn((valid, data) => {
valids(i) := valid
when (valid) { newBytes(i) := data }
Bool(true)
}), newDesc)}}
RegField(8, oldBytes(i), wrFn(i), newDesc)}
val partialBytes = if (numPartialBytes > 0) {
val newDesc = desc.map {d => d.copy(name = d.name + s"_$numFullBytes")}
Seq(RegField(reg.getWidth % 8, oldBytes(numFullBytes), wrFn(numFullBytes), newDesc),
RegField(8 - (reg.getWidth % 8)))
} else Nil
val padBytes = Seq.fill(numPadBytes){RegField(8)}
fullBytes ++ partialBytes ++ padBytes
}
def bytes(reg: UInt, desc: Option[RegFieldDesc]): Seq[RegField] = {
val width = reg.getWidth

View File

@ -54,27 +54,35 @@ class BusErrorUnit[T <: BusErrors](t: => T, params: BusErrorUnitParams)(implicit
val causeWidth = log2Ceil(sources.lastIndexWhere(_.nonEmpty) + 1)
val (cause, cause_desc) = DescribedReg(UInt(causeWidth.W),
"cause", "Cause of error event", reset=Some(0.U(causeWidth.W)), enumerations=sources_enums.toMap)
"cause", "Cause of error event", reset=Some(0.U(causeWidth.W)), volatile=true, enumerations=sources_enums.toMap)
val (value, value_desc) = DescribedReg(UInt(width = sources.flatten.map(_.bits.getWidth).max),
"value", "Physical address of error event", reset=None)
"value", "Physical address of error event", reset=None, volatile=true)
require(value.getWidth <= regWidth)
val enable = Reg(init = Vec(sources.map(_.nonEmpty.B)))
val enable_desc = sources.zipWithIndex.map { case (s, i) =>
RegFieldDesc(s"enable_$i", "", reset=Some(if (s.nonEmpty) 1 else 0))}
if (s.nonEmpty) RegFieldDesc(s"enable_$i", "", reset=Some(1))
else RegFieldDesc.reserved
}
val global_interrupt = Reg(init = Vec.fill(sources.size)(false.B))
val global_interrupt_desc = sources.zipWithIndex.map { case (s, i) =>
RegFieldDesc(s"plic_interrupt_$i", "", reset=Some(0))}
if (s.nonEmpty) RegFieldDesc(s"plic_interrupt_$i", "", reset=Some(0))
else RegFieldDesc.reserved
}
val accrued = Reg(init = Vec.fill(sources.size)(false.B))
val accrued_desc = sources.zipWithIndex.map { case (s, i) =>
RegFieldDesc(s"accrued_$i", "", reset=Some(0))}
if (s.nonEmpty) RegFieldDesc(s"accrued_$i", "", reset=Some(0), volatile = true)
else RegFieldDesc.reserved
}
val local_interrupt = Reg(init = Vec.fill(sources.size)(false.B))
val local_interrupt_desc = sources.zipWithIndex.map { case (s, i) =>
RegFieldDesc(s"local_interrupt_$i", "", reset=Some(0))}
if (s.nonEmpty) RegFieldDesc(s"local_interrupt_$i", "", reset=Some(0))
else RegFieldDesc.reserved
}
for ((((s, en), acc), i) <- (sources zip enable zip accrued).zipWithIndex; if s.nonEmpty) {
when (s.get.valid) {

View File

@ -35,7 +35,7 @@ trait ExampleModule extends HasRegMap
Some(RegFieldDesc("pending", "Pending: Example of a special (W1ToC) Register. " +
"Writing a bit here causes it to be reset to 0. " +
"The bits are set when the corresponding bit in 'state' is high.",
reset=Some(0xF)))))
reset=Some(0xF), volatile=true))))
)
}

View File

@ -10,9 +10,6 @@ import freechips.rocketchip.interrupts._
import freechips.rocketchip.util.{HeterogeneousBag, ElaborationArtefacts}
import scala.math.{min,max}
import org.json4s.JsonDSL._
import org.json4s.jackson.JsonMethods.{pretty, render}
case class TLRegisterNode(
address: Seq[AddressSet],
device: Device,
@ -85,44 +82,15 @@ case class TLRegisterNode(
bundleIn.e.ready := Bool(true)
// Dump out the register map for documentation purposes.
val regDescs = mapping.flatMap { case (offset, seq) =>
var currentBitOffset = 0
seq.zipWithIndex.map { case (f, i) => {
val tmp = (f.desc.map{ _.name}.getOrElse(s"unnamedRegField${offset.toHexString}_${currentBitOffset}") -> (
("byteOffset" -> s"0x${offset.toHexString}") ~
("bitOffset" -> currentBitOffset) ~
("bitWidth" -> f.width) ~
("name" -> f.desc.map(_.name)) ~
("description" -> f.desc.map{d => if (d.desc == "") None else Some(d.desc)}) ~
("resetValue" -> f.desc.map{_.reset}) ~
("group" -> f.desc.map{_.group}) ~
("groupDesc" -> f.desc.map{_.groupDesc}) ~
("accessType" -> f.desc.map {d => d.access.toString}) ~
("enumerations" -> f.desc.map {d =>
Option(d.enumerations.map { case (key, (name, desc)) =>
(("value" -> key) ~
("name" -> name) ~
("description" -> desc))
}).filter(_.nonEmpty)})
))
currentBitOffset = currentBitOffset + f.width
tmp
}}
}
//TODO: It would be better to name this other than "Device at ...."
val base = s"0x${address.head.base.toInt.toHexString}"
val json = ("peripheral" -> (
("displayName" -> s"deviceAt${base}") ~
("baseAddress" -> base) ~
("regfields" -> regDescs)
))
val base = address.head.base
val baseHex = s"0x${base.toInt.toHexString}"
val name = s"deviceAt${baseHex}" //TODO: It would be better to name this other than "Device at ...."
val json = RegMappingAnnotation.serialize(base, name, mapping:_*)
var suffix = 0
while( ElaborationArtefacts.contains(s"${base}.${suffix}.regmap.json")){
while( ElaborationArtefacts.contains(s"${baseHex}.${suffix}.regmap.json")){
suffix = suffix + 1
}
ElaborationArtefacts.add(s"${base}.${suffix}.regmap.json", pretty(render(json)))
ElaborationArtefacts.add(s"${baseHex}.${suffix}.regmap.json", json)
}
}