From 2f239f2a9a0ad63792ded3349e650246d1c41bc4 Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Fri, 9 Mar 2018 11:29:17 -0800 Subject: [PATCH 1/9] RegFieldDesc: Add more features to support more IP-XACT like descriptions & emit them in the JSON --- src/main/scala/regmapper/RegField.scala | 41 ++++++++++++++------ src/main/scala/tilelink/RegisterRouter.scala | 21 +++++----- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/main/scala/regmapper/RegField.scala b/src/main/scala/regmapper/RegField.scala index b814732b..91c69e52 100644 --- a/src/main/scala/regmapper/RegField.scala +++ b/src/main/scala/regmapper/RegField.scala @@ -7,7 +7,6 @@ import chisel3.util.{ReadyValidIO} 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 // information which is likely to be needed by downstream SW and Documentation @@ -15,26 +14,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 RegFieldDescReserved { + def apply(): 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] = { @@ -125,8 +145,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(RegFieldDescReserved())) 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 +161,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,7 +170,7 @@ 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 diff --git a/src/main/scala/tilelink/RegisterRouter.scala b/src/main/scala/tilelink/RegisterRouter.scala index c4b4d42c..ed764c67 100644 --- a/src/main/scala/tilelink/RegisterRouter.scala +++ b/src/main/scala/tilelink/RegisterRouter.scala @@ -89,15 +89,18 @@ case class TLRegisterNode( 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}) ~ + ("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}) ~ + ("writeType" -> f.desc.map {d => d.wrType.map(_.toString)}) ~ + ("readAction" -> f.desc.map {d => d.rdAction.map(_.toString)}) ~ + ("volatile" -> f.desc.map {d => if (d.volatile) Some(true) else None}) ~ ("enumerations" -> f.desc.map {d => Option(d.enumerations.map { case (key, (name, desc)) => (("value" -> key) ~ From 3063fd1b46ad395bb0a1d0c22453a916663adbee Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Fri, 9 Mar 2018 11:47:03 -0800 Subject: [PATCH 2/9] RegFieldDesc: update DescribedReg to suppot new features --- src/main/scala/regmapper/DescribedReg.scala | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/main/scala/regmapper/DescribedReg.scala b/src/main/scala/regmapper/DescribedReg.scala index 59be4a27..658539c8 100644 --- a/src/main/scala/regmapper/DescribedReg.scala +++ b/src/main/scala/regmapper/DescribedReg.scala @@ -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) From 7458378a4a4bc16d94bcab4cfc3d48fe56a1f14c Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Fri, 9 Mar 2018 12:22:28 -0800 Subject: [PATCH 3/9] RegFieldDesc: Update reg field descs to be more correct for devices. --- src/main/scala/devices/debug/Debug.scala | 12 +++++++----- src/main/scala/devices/tilelink/Plic.scala | 19 ++++++++++++------- src/main/scala/rocket/BusErrorUnit.scala | 14 +++++++++----- 3 files changed, 28 insertions(+), 17 deletions(-) diff --git a/src/main/scala/devices/debug/Debug.scala b/src/main/scala/devices/debug/Debug.scala index 03b43ed8..486c3a41 100644 --- a/src/main/scala/devices/debug/Debug.scala +++ b/src/main/scala/devices/debug/Debug.scala @@ -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)))}) diff --git a/src/main/scala/devices/tilelink/Plic.scala b/src/main/scala/devices/tilelink/Plic.scala index eca0bc1d..78468fd6 100644 --- a/src/main/scala/devices/tilelink/Plic.scala +++ b/src/main/scala/devices/tilelink/Plic.scala @@ -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) + RegFieldDescReserved() } 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) + RegFieldDescReserved() } 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, RegFieldDescReserved()) }}) } @@ -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)) ) ) } diff --git a/src/main/scala/rocket/BusErrorUnit.scala b/src/main/scala/rocket/BusErrorUnit.scala index 38d1ccd7..838e98b7 100644 --- a/src/main/scala/rocket/BusErrorUnit.scala +++ b/src/main/scala/rocket/BusErrorUnit.scala @@ -57,24 +57,28 @@ class BusErrorUnit[T <: BusErrors](t: => T, params: BusErrorUnitParams)(implicit "cause", "Cause of error event", reset=Some(0.U(causeWidth.W)), 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 RegFieldDescReserved()} 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 RegFieldDescReserved()} 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 RegFieldDescReserved()} 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 RegFieldDescReserved()} for ((((s, en), acc), i) <- (sources zip enable zip accrued).zipWithIndex; if s.nonEmpty) { when (s.get.valid) { From 0fcacd37df0d404ede8452b103bbd9ec71cf0ff2 Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Fri, 9 Mar 2018 15:10:43 -0800 Subject: [PATCH 4/9] RegFieldDesc: mark some more registers as volatile --- src/main/scala/devices/tilelink/CLINT.scala | 10 ++++++---- src/main/scala/tilelink/Example.scala | 2 +- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/scala/devices/tilelink/CLINT.scala b/src/main/scala/devices/tilelink/CLINT.scala index 5c9d3358..cb1f340b 100644 --- a/src/main/scala/devices/tilelink/CLINT.scala +++ b/src/main/scala/devices/tilelink/CLINT.scala @@ -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)))) ) } } diff --git a/src/main/scala/tilelink/Example.scala b/src/main/scala/tilelink/Example.scala index e5f3104c..bfd85b14 100644 --- a/src/main/scala/tilelink/Example.scala +++ b/src/main/scala/tilelink/Example.scala @@ -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)))) ) } From e0c3c63826caf505c8ce915295e9987c8f2ea5dd Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Fri, 9 Mar 2018 15:10:43 -0800 Subject: [PATCH 5/9] RegFieldDesc: Update the .bytes method to emit reserved register fields instead of applying the same description to the registers that it doesn't actually do anything with (the padding registers) --- src/main/scala/regmapper/RegField.scala | 35 ++++++++++++++++++------- 1 file changed, 25 insertions(+), 10 deletions(-) diff --git a/src/main/scala/regmapper/RegField.scala b/src/main/scala/regmapper/RegField.scala index 91c69e52..c6560c52 100644 --- a/src/main/scala/regmapper/RegField.scala +++ b/src/main/scala/regmapper/RegField.scala @@ -173,23 +173,38 @@ object RegField }), 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 From d889a0ca16550ee18a9a65fc14d6f359239d503c Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Fri, 9 Mar 2018 17:20:38 -0800 Subject: [PATCH 6/9] RegFieldDesc: add volatile to cause reg in BUE --- src/main/scala/rocket/BusErrorUnit.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/rocket/BusErrorUnit.scala b/src/main/scala/rocket/BusErrorUnit.scala index 838e98b7..54f47d6d 100644 --- a/src/main/scala/rocket/BusErrorUnit.scala +++ b/src/main/scala/rocket/BusErrorUnit.scala @@ -54,7 +54,7 @@ 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, volatile=true) From 15e058e3da78f4454fd55f600082ac2ea05f911f Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Fri, 9 Mar 2018 17:38:23 -0800 Subject: [PATCH 7/9] RegFieldDesc: change how reserved is indicated --- src/main/scala/devices/tilelink/Plic.scala | 6 +++--- src/main/scala/regmapper/RegField.scala | 6 +++--- src/main/scala/rocket/BusErrorUnit.scala | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/scala/devices/tilelink/Plic.scala b/src/main/scala/devices/tilelink/Plic.scala index 78468fd6..ef8903e4 100644 --- a/src/main/scala/devices/tilelink/Plic.scala +++ b/src/main/scala/devices/tilelink/Plic.scala @@ -173,13 +173,13 @@ class TLPLIC(params: PLICParams, beatBytes: Int)(implicit p: Parameters) extends reset=if (nPriorities > 0) None else Some(1), wrType=Some(RegFieldWrType.MODIFY)) } else { - RegFieldDescReserved() + 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.", volatile = true) } else { - RegFieldDescReserved() + RegFieldDesc.reserved() } def priorityRegField(x: UInt, i: Int) = if (nPriorities > 0) RegField(32, x, priorityRegDesc(i)) else RegField.r(32, x, priorityRegDesc(i)) @@ -195,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, RegFieldDescReserved()) + RegField(1, b, RegFieldDesc.reserved()) }}) } diff --git a/src/main/scala/regmapper/RegField.scala b/src/main/scala/regmapper/RegField.scala index c6560c52..dc6247eb 100644 --- a/src/main/scala/regmapper/RegField.scala +++ b/src/main/scala/regmapper/RegField.scala @@ -46,8 +46,8 @@ case class RegFieldDesc ( ){ } -object RegFieldDescReserved { - def apply(): RegFieldDesc = RegFieldDesc("reserved", "", access=RegFieldAccessType.R, reset=Some(0)) +object RegFieldDesc { + def reserved() RegFieldDesc = RegFieldDesc("reserved", "", access=RegFieldAccessType.R, reset=Some(0)) } // Our descriptions are in terms of RegFields only, which is somewhat @@ -145,7 +145,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(RegFieldDescReserved())) + 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)) diff --git a/src/main/scala/rocket/BusErrorUnit.scala b/src/main/scala/rocket/BusErrorUnit.scala index 54f47d6d..aebf2af4 100644 --- a/src/main/scala/rocket/BusErrorUnit.scala +++ b/src/main/scala/rocket/BusErrorUnit.scala @@ -63,22 +63,22 @@ class BusErrorUnit[T <: BusErrors](t: => T, params: BusErrorUnitParams)(implicit val enable = Reg(init = Vec(sources.map(_.nonEmpty.B))) val enable_desc = sources.zipWithIndex.map { case (s, i) => if (s.nonEmpty) RegFieldDesc(s"enable_$i", "", reset=Some(1)) - else RegFieldDescReserved()} + else RegFieldDesc.reserved()} val global_interrupt = Reg(init = Vec.fill(sources.size)(false.B)) val global_interrupt_desc = sources.zipWithIndex.map { case (s, i) => if (s.nonEmpty) RegFieldDesc(s"plic_interrupt_$i", "", reset=Some(0)) - else RegFieldDescReserved()} + else RegFieldDesc.reserved()} val accrued = Reg(init = Vec.fill(sources.size)(false.B)) val accrued_desc = sources.zipWithIndex.map { case (s, i) => if (s.nonEmpty) RegFieldDesc(s"accrued_$i", "", reset=Some(0), volatile = true) - else RegFieldDescReserved()} + else RegFieldDesc.reserved()} val local_interrupt = Reg(init = Vec.fill(sources.size)(false.B)) val local_interrupt_desc = sources.zipWithIndex.map { case (s, i) => if (s.nonEmpty) RegFieldDesc(s"local_interrupt_$i", "", reset=Some(0)) - else RegFieldDescReserved()} + else RegFieldDesc.reserved()} for ((((s, en), acc), i) <- (sources zip enable zip accrued).zipWithIndex; if s.nonEmpty) { when (s.get.valid) { From ea89259dd47632c1dd35313bde28b87874bf58f3 Mon Sep 17 00:00:00 2001 From: Henry Cook Date: Sun, 11 Mar 2018 12:12:40 -0700 Subject: [PATCH 8/9] RegFieldDesc: reserved omits () --- src/main/scala/devices/tilelink/Plic.scala | 6 +++--- src/main/scala/regmapper/RegField.scala | 4 ++-- src/main/scala/rocket/BusErrorUnit.scala | 12 ++++++++---- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/scala/devices/tilelink/Plic.scala b/src/main/scala/devices/tilelink/Plic.scala index ef8903e4..cd1cf3d3 100644 --- a/src/main/scala/devices/tilelink/Plic.scala +++ b/src/main/scala/devices/tilelink/Plic.scala @@ -173,13 +173,13 @@ class TLPLIC(params: PLICParams, beatBytes: Int)(implicit p: Parameters) extends reset=if (nPriorities > 0) None else Some(1), wrType=Some(RegFieldWrType.MODIFY)) } else { - RegFieldDesc.reserved() + 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.", volatile = true) } else { - RegFieldDesc.reserved() + RegFieldDesc.reserved } def priorityRegField(x: UInt, i: Int) = if (nPriorities > 0) RegField(32, x, priorityRegDesc(i)) else RegField.r(32, x, priorityRegDesc(i)) @@ -195,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()) + RegField(1, b, RegFieldDesc.reserved) }}) } diff --git a/src/main/scala/regmapper/RegField.scala b/src/main/scala/regmapper/RegField.scala index dc6247eb..f620a178 100644 --- a/src/main/scala/regmapper/RegField.scala +++ b/src/main/scala/regmapper/RegField.scala @@ -47,7 +47,7 @@ case class RegFieldDesc ( } object RegFieldDesc { - def reserved() RegFieldDesc = RegFieldDesc("reserved", "", access=RegFieldAccessType.R, reset=Some(0)) + def reserved: RegFieldDesc = RegFieldDesc("reserved", "", access=RegFieldAccessType.R, reset=Some(0)) } // Our descriptions are in terms of RegFields only, which is somewhat @@ -145,7 +145,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())) + 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)) diff --git a/src/main/scala/rocket/BusErrorUnit.scala b/src/main/scala/rocket/BusErrorUnit.scala index aebf2af4..8c4cfaee 100644 --- a/src/main/scala/rocket/BusErrorUnit.scala +++ b/src/main/scala/rocket/BusErrorUnit.scala @@ -63,22 +63,26 @@ class BusErrorUnit[T <: BusErrors](t: => T, params: BusErrorUnitParams)(implicit val enable = Reg(init = Vec(sources.map(_.nonEmpty.B))) val enable_desc = sources.zipWithIndex.map { case (s, i) => if (s.nonEmpty) RegFieldDesc(s"enable_$i", "", reset=Some(1)) - else RegFieldDesc.reserved()} + else RegFieldDesc.reserved + } val global_interrupt = Reg(init = Vec.fill(sources.size)(false.B)) val global_interrupt_desc = sources.zipWithIndex.map { case (s, i) => if (s.nonEmpty) RegFieldDesc(s"plic_interrupt_$i", "", reset=Some(0)) - else RegFieldDesc.reserved()} + else RegFieldDesc.reserved + } val accrued = Reg(init = Vec.fill(sources.size)(false.B)) val accrued_desc = sources.zipWithIndex.map { case (s, i) => if (s.nonEmpty) RegFieldDesc(s"accrued_$i", "", reset=Some(0), volatile = true) - else RegFieldDesc.reserved()} + else RegFieldDesc.reserved + } val local_interrupt = Reg(init = Vec.fill(sources.size)(false.B)) val local_interrupt_desc = sources.zipWithIndex.map { case (s, i) => if (s.nonEmpty) RegFieldDesc(s"local_interrupt_$i", "", reset=Some(0)) - else RegFieldDesc.reserved()} + else RegFieldDesc.reserved + } for ((((s, en), acc), i) <- (sources zip enable zip accrued).zipWithIndex; if s.nonEmpty) { when (s.get.valid) { From 59d5e613668d090b094c4f1b38a36abb7cb1e1ca Mon Sep 17 00:00:00 2001 From: Henry Cook Date: Sun, 11 Mar 2018 18:06:35 -0700 Subject: [PATCH 9/9] regmapper: refactor how json is emitted --- src/main/scala/regmapper/Annotation.scala | 23 ++++++++++ src/main/scala/regmapper/RegField.scala | 25 +++++++++++ src/main/scala/tilelink/RegisterRouter.scala | 47 +++----------------- 3 files changed, 54 insertions(+), 41 deletions(-) create mode 100644 src/main/scala/regmapper/Annotation.scala diff --git a/src/main/scala/regmapper/Annotation.scala b/src/main/scala/regmapper/Annotation.scala new file mode 100644 index 00000000..20ff902b --- /dev/null +++ b/src/main/scala/regmapper/Annotation.scala @@ -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))))) + } +} diff --git a/src/main/scala/regmapper/RegField.scala b/src/main/scala/regmapper/RegField.scala index f620a178..b5a47332 100644 --- a/src/main/scala/regmapper/RegField.scala +++ b/src/main/scala/regmapper/RegField.scala @@ -5,6 +5,10 @@ package freechips.rocketchip.regmapper import Chisel._ import chisel3.util.{ReadyValidIO} +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. @@ -136,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 diff --git a/src/main/scala/tilelink/RegisterRouter.scala b/src/main/scala/tilelink/RegisterRouter.scala index ed764c67..fb74591e 100644 --- a/src/main/scala/tilelink/RegisterRouter.scala +++ b/src/main/scala/tilelink/RegisterRouter.scala @@ -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,47 +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}) ~ - ("writeType" -> f.desc.map {d => d.wrType.map(_.toString)}) ~ - ("readAction" -> f.desc.map {d => d.rdAction.map(_.toString)}) ~ - ("volatile" -> f.desc.map {d => if (d.volatile) Some(true) else None}) ~ - ("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) } }