From 4ab1585a78a17f8a391de8baf377692ccd4a20ed Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Mon, 8 Jan 2018 11:20:37 -0800 Subject: [PATCH 01/10] Register Field: Add a more verbose description object Add versions of the RegField functions to take it in, and update Example device to use it. --- src/main/scala/regmapper/RegField.scala | 89 +++++++++++++++++++------ src/main/scala/tilelink/Example.scala | 12 +++- 2 files changed, 79 insertions(+), 22 deletions(-) diff --git a/src/main/scala/regmapper/RegField.scala b/src/main/scala/regmapper/RegField.scala index 4f618ca4..34e062b5 100644 --- a/src/main/scala/regmapper/RegField.scala +++ b/src/main/scala/regmapper/RegField.scala @@ -7,6 +7,46 @@ 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 +// tools. + +object RegFieldAccessType extends scala.Enumeration { + type RegFieldAccessType = Value + val R, W, RW, RSPECIAL, WSPECIAL, RWSPECIAL, OTHER = Value +} +import RegFieldAccessType._ + +case class RegFieldDesc ( + name: String, + desc: String, + group: Option[String] = None, + groupDesc: Option[String] = None, + access: RegFieldAccessType = RegFieldAccessType.RW, + reset: Option[BigInt] = None, + enumerations: Map[String, BigInt] = 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 RegFieldGroup { + def apply (name: String, desc: Option[String], regs: Seq[RegField], descFirstOnly: Boolean = true): Seq[RegField] = { + regs.zipWithIndex.map {case (r, i) => + val gDesc = if ((i > 0) & descFirstOnly) None else desc + r.desc.map { d => + r.copy(desc = Some(d.copy(group = Some(name), groupDesc = gDesc)) ) + }.getOrElse(r) + } + } +} + case class RegReadFn private(combinational: Boolean, fn: (Bool, Bool) => (Bool, Bool, UInt)) object RegReadFn { @@ -73,11 +113,11 @@ object RegWriteFn implicit def apply(x: Unit): RegWriteFn = RegWriteFn((valid, data) => { Bool(true) }) } -case class RegField(width: Int, read: RegReadFn, write: RegWriteFn, name: String, description: String) +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 = ()) + def readOnly = this.copy(write = (), desc = this.desc.map(_.copy(access = RegFieldAccessType.R))) } object RegField @@ -85,51 +125,62 @@ object RegField // Byte address => sequence of bitfields, lowest index => lowest address type Map = (Int, Seq[RegField]) - def apply(n: Int) : RegField = apply(n, (), (), "", "") - def apply(n: Int, r: RegReadFn, w: RegWriteFn) : RegField = apply(n, r, w, "", "") - def apply(n: Int, rw: UInt) : RegField = apply(n, rw, rw, "", "") - def apply(n: Int, rw: UInt, name: String, description: String) : RegField = apply(n, rw, rw, name, description) - def r(n: Int, r: RegReadFn, name: String = "", description: String = "") : RegField = apply(n, r, (), name, description) - def w(n: Int, w: RegWriteFn, name: String = "", description: String = "") : RegField = apply(n, (), w, name, description) + def apply(n: Int) : RegField = apply(n, (), (), + Some(RegFieldDesc("reserved", "", access = RegFieldAccessType.R, reset = Some(0)))) + + 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)) + def apply(n: Int, rw: UInt) : RegField = apply(n, rw, rw, None) + def apply(n: Int, rw: UInt, desc: RegFieldDesc) : RegField = apply(n, rw, rw, Some(desc)) + def r(n: Int, r: RegReadFn) : RegField = apply(n, r, (), None) + def r(n: Int, r: RegReadFn, desc: RegFieldDesc) : RegField = apply(n, r, (), Some(desc.copy(access = RegFieldAccessType.R))) + def w(n: Int, w: RegWriteFn) : RegField = apply(n, (), w, None) + def w(n: Int, w: RegWriteFn, desc: RegFieldDesc) : RegField = apply(n, (), w, Some(desc.copy(access = RegFieldAccessType.W))) // This RegField allows 'set' to set bits in 'reg'. // and to clear bits when the bus writes bits of value 1. // Setting takes priority over clearing. - def w1ToClear(n: Int, reg: UInt, set: UInt): RegField = - RegField(n, reg, RegWriteFn((valid, data) => { reg := ~(~reg | Mux(valid, data, UInt(0))) | set; Bool(true) })) + 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)}) // This RegField wraps an explicit register // (e.g. Black-Boxed Register) to create a R/W register. - def rwReg(n: Int, bb: SimpleRegIO, name: String = "", description: String = "") : RegField = + def rwReg(n: Int, bb: SimpleRegIO, desc: Option[RegFieldDesc] = None) : RegField = RegField(n, bb.q, RegWriteFn((valid, data) => { bb.en := valid bb.d := data Bool(true) - }), name, description) + }), desc.map{_.copy(access = RegFieldAccessType.RW)}) // 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 - // than the intended bus width of the device (atomic updates are impossible). - def bytes(reg: UInt, numBytes: Int): Seq[RegField] = { - val pad = reg | UInt(0, width = 8*numBytes) + // 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) 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 => + val newDesc = desc.map {d => d.copy(name = d.name + s"[${i*8-1}:${i*8}]")} RegField(8, oldBytes(i), RegWriteFn((valid, data) => { valids(i) := valid when (valid) { newBytes(i) := data } Bool(true) - }))}} + }), newDesc)}} - def bytes(reg: UInt): Seq[RegField] = { + def bytes(reg: UInt, desc: Option[RegFieldDesc]): Seq[RegField] = { val width = reg.getWidth require (width % 8 == 0, s"RegField.bytes must be called on byte-sized reg, not ${width} bits") - bytes(reg, width/8) + bytes(reg, width/8, desc) } + + def bytes(reg: UInt, numBytes: Int): Seq[RegField] = bytes(reg, numBytes, None) + def bytes(reg: UInt): Seq[RegField] = bytes(reg, None) + } trait HasRegMap @@ -138,4 +189,4 @@ trait HasRegMap val interrupts: Vec[Bool] } -// See GPIO.scala for an example of how to use regmap +// See Example.scala for an example of how to use regmap diff --git a/src/main/scala/tilelink/Example.scala b/src/main/scala/tilelink/Example.scala index b9c41df4..e5f3104c 100644 --- a/src/main/scala/tilelink/Example.scala +++ b/src/main/scala/tilelink/Example.scala @@ -20,7 +20,7 @@ trait ExampleModule extends HasRegMap val io: ExampleBundle val interrupts: Vec[Bool] - val state = RegInit(UInt(0)) + val state = RegInit(UInt(0, width = params.num)) val pending = RegInit(UInt(0xf, width = 4)) io.gpio := state @@ -28,9 +28,15 @@ trait ExampleModule extends HasRegMap regmap( 0 -> Seq( - RegField(params.num, state)), + RegField(params.num, state, + RegFieldDesc("state", "State: Example of a R/W Register with description.", reset = Some(0)))), 4 -> Seq( - RegField.w1ToClear(4, pending, state))) + RegField.w1ToClear(4, pending, state, + 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))))) + ) } // Create a concrete TL2 version of the abstract Example slave From 7abf6e1c8aca06863f7581c5101dda235312193a Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Sat, 10 Feb 2018 13:17:38 -0800 Subject: [PATCH 02/10] RegMapper: Update cover props to use new RegFieldDesc objects --- src/main/scala/regmapper/RegMapper.scala | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/scala/regmapper/RegMapper.scala b/src/main/scala/regmapper/RegMapper.scala index 72c78373..aa83a63d 100644 --- a/src/main/scala/regmapper/RegMapper.scala +++ b/src/main/scala/regmapper/RegMapper.scala @@ -144,10 +144,13 @@ object RegMapper val (f_wiready, f_wovalid) = field.write.fn(f_wivalid, f_woready, data(high, low)) // cover reads and writes to register - cover(f_rivalid && f_riready, field.name + "_Reg_read_start", field.description + " RegField Read Request Initiate") - cover(f_rovalid && f_roready, field.name + "_Reg_read_out", field.description + " RegField Read Request Complete") - cover(f_wivalid && f_wiready, field.name + "_Reg_write_start", field.description + " RegField Write Request Initiate") - cover(f_wovalid && f_woready, field.name + "_Reg_write_out", field.description + " RegField Write Request Complete") + val fname = field.desc.map{_.name}.getOrElse("") + val fdesc = field.desc.map{_.desc + ":"}.getOrElse("") + + cover(f_rivalid && f_riready, fname + "_Reg_read_start", fdesc + " RegField Read Request Initiate") + cover(f_rovalid && f_roready, fname + "_Reg_read_out", fdesc + " RegField Read Request Complete") + cover(f_wivalid && f_wiready, fname + "_Reg_write_start", fdesc + " RegField Write Request Initiate") + cover(f_wovalid && f_woready, fname + "_Reg_write_out", fdesc + " RegField Write Request Complete") def litOR(x: Bool, y: Bool) = if (x.isLit && x.litValue == 1) Bool(true) else x || y // Add this field to the ready-valid signals for the register From 13b120fb0144e19f9eb9db74276fd452c50d806a Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Sat, 10 Feb 2018 20:11:24 -0800 Subject: [PATCH 03/10] Debug: Annotate regmaps with RegFieldDescs --- src/main/scala/devices/debug/Debug.scala | 71 ++++++++++++++---------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/src/main/scala/devices/debug/Debug.scala b/src/main/scala/devices/debug/Debug.scala index 9167a715..5e0c3fc6 100644 --- a/src/main/scala/devices/debug/Debug.scala +++ b/src/main/scala/devices/debug/Debug.scala @@ -89,7 +89,7 @@ import DebugModuleAccessType._ object DebugAbstractCommandError extends scala.Enumeration { type DebugAbstractCommandError = Value - val None, ErrBusy, ErrNotSupported, ErrException, ErrHaltResume = Value + val Success, ErrBusy, ErrNotSupported, ErrException, ErrHaltResume = Value } import DebugAbstractCommandError._ @@ -250,18 +250,18 @@ class DebugCtrlBundle (nComponents: Int)(implicit val p: Parameters) extends Par // Local reg mapper function : Notify when written, but give the value as well. object WNotifyWire { - def apply(n: Int, value: UInt, set: Bool) : RegField = { + def apply(n: Int, value: UInt, set: Bool, name: String, desc: String) : RegField = { RegField(n, UInt(0), RegWriteFn((valid, data) => { set := valid value := data Bool(true) - })) + }), Some(RegFieldDesc(name = name, desc = desc, access = RegFieldAccessType.WSPECIAL))) } } // Local reg mapper function : Notify when accessed either as read or write. object RWNotify { - def apply (n: Int, rVal: UInt, wVal: UInt, rNotify: Bool, wNotify: Bool) : RegField = { + def apply (n: Int, rVal: UInt, wVal: UInt, rNotify: Bool, wNotify: Bool, desc: Option[RegFieldDesc] = None): RegField = { RegField(n, RegReadFn ((ready) => {rNotify := ready ; (Bool(true), rVal)}), RegWriteFn((valid, data) => { @@ -269,7 +269,7 @@ object RWNotify { when (valid) {wVal := data} Bool(true) } - )) + ), desc) } } @@ -344,7 +344,7 @@ class TLDebugModuleOuter(device: Device)(implicit p: Parameters) extends LazyMod // DMCONTROL is the only register, so it's at offset 0. dmiNode.regmap( 0 -> Seq(RWNotify(32, DMCONTROLRdData.asUInt(), - DMCONTROLWrDataVal, DMCONTROLRdEn, DMCONTROLWrEn)) + DMCONTROLWrDataVal, DMCONTROLRdEn, DMCONTROLWrEn, Some(RegFieldDesc("dmi_dmcontrol", "", reset=Some(0))))) ) //-------------------------------------------------------------- @@ -726,20 +726,25 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int)(implicit p: // Program Buffer Access (DMI ... System Bus can override) //-------------------------------------------------------------- dmiNode.regmap( - (DMI_DMSTATUS << 2) -> Seq(RegField.r(32, DMSTATUSRdData.asUInt())), + (DMI_DMSTATUS << 2) -> Seq(RegField.r(32, DMSTATUSRdData.asUInt(), RegFieldDesc("dmi_dmstatus", ""))), //TODO (DMI_CFGSTRADDR0 << 2) -> cfgStrAddrFields, - (DMI_HARTINFO << 2) -> Seq(RegField.r(32, HARTINFORdData.asUInt())), - (DMI_HALTSUM << 2) -> Seq(RegField.r(32, HALTSUMRdData.asUInt())), - (DMI_ABSTRACTCS << 2) -> Seq(RWNotify(32, ABSTRACTCSRdData.asUInt(), ABSTRACTCSWrDataVal, ABSTRACTCSRdEn, ABSTRACTCSWrEnMaybe)), - (DMI_ABSTRACTAUTO<< 2) -> Seq(RWNotify(32, ABSTRACTAUTORdData.asUInt(), ABSTRACTAUTOWrDataVal, ABSTRACTAUTORdEn, ABSTRACTAUTOWrEnMaybe)), - (DMI_COMMAND << 2) -> Seq(RWNotify(32, COMMANDRdData.asUInt(), COMMANDWrDataVal, COMMANDRdEn, COMMANDWrEnMaybe)), - (DMI_DATA0 << 2) -> abstractDataMem.zipWithIndex.map{case (x, i) => RWNotify(8, x, abstractDataNxt(i), + (DMI_HARTINFO << 2) -> Seq(RegField.r(32, HARTINFORdData.asUInt(), RegFieldDesc("dmi_hartinfo", "" /*, reset=Some(HARTINFORdData.litValue)*/))), + (DMI_HALTSUM << 2) -> Seq(RegField.r(32, HALTSUMRdData.asUInt(), RegFieldDesc("dmi_haltsum", ""))), + (DMI_ABSTRACTCS << 2) -> Seq(RWNotify(32, ABSTRACTCSRdData.asUInt(), ABSTRACTCSWrDataVal, ABSTRACTCSRdEn, ABSTRACTCSWrEnMaybe, + Some(RegFieldDesc("dmi_abstractcs", "" /*, reset=Some(ABSTRACTCSReset.litValue)*/)))), + (DMI_ABSTRACTAUTO<< 2) -> Seq(RWNotify(32, ABSTRACTAUTORdData.asUInt(), ABSTRACTAUTOWrDataVal, ABSTRACTAUTORdEn, ABSTRACTAUTOWrEnMaybe, + Some(RegFieldDesc("dmi_abstractauto", "", reset=Some(0))))), + (DMI_COMMAND << 2) -> Seq(RWNotify(32, COMMANDRdData.asUInt(), COMMANDWrDataVal, COMMANDRdEn, COMMANDWrEnMaybe, + Some(RegFieldDesc("dmi_command", "", reset=Some(0))))), + (DMI_DATA0 << 2) -> RegFieldGroup("dmi_data", None, abstractDataMem.zipWithIndex.map{case (x, i) => RWNotify(8, x, abstractDataNxt(i), dmiAbstractDataRdEn(i), - dmiAbstractDataWrEnMaybe(i))}, - (DMI_PROGBUF0 << 2) -> programBufferMem.zipWithIndex.map{case (x, i) => RWNotify(8, x, programBufferNxt(i), + dmiAbstractDataWrEnMaybe(i), + Some(RegFieldDesc(s"dmi_data_$i", "", reset = Some(0))))}), + (DMI_PROGBUF0 << 2) -> RegFieldGroup("dmi_progbuf", None, programBufferMem.zipWithIndex.map{case (x, i) => RWNotify(8, x, programBufferNxt(i), dmiProgramBufferRdEn(i), - dmiProgramBufferWrEnMaybe(i))}, - (DMIConsts.dmi_haltStatusAddr << 2) -> haltedStatus.map(x => RegField.r(32, x)) + dmiProgramBufferWrEnMaybe(i), + Some(RegFieldDesc(s"dmi_progbuf_$i", "", reset = Some(0))))}), + (DMIConsts.dmi_haltStatusAddr << 2) -> RegFieldGroup("dmi_halt_status", None, haltedStatus.zipWithIndex.map{case (x, i) => RegField.r(32, x, RegFieldDesc(s"halt_status_$i", ""))}) ) abstractDataMem.zipWithIndex.foreach { case (x, i) => @@ -880,20 +885,28 @@ class TLDebugModuleInner(device: Device, getNComponents: () => Int)(implicit p: tlNode.regmap( // This memory is writable. - HALTED -> Seq(WNotifyWire(sbIdWidth, hartHaltedId, hartHaltedWrEn)), - GOING -> Seq(WNotifyWire(sbIdWidth, hartGoingId, hartGoingWrEn)), - RESUMING -> Seq(WNotifyWire(sbIdWidth, hartResumingId, hartResumingWrEn)), - EXCEPTION -> Seq(WNotifyWire(sbIdWidth, hartExceptionId, hartExceptionWrEn)), - DATA -> abstractDataMem.map(x => RegField(8, x)), - PROGBUF(cfg)-> programBufferMem.map(x => RegField(8, x)), + HALTED -> Seq(WNotifyWire(sbIdWidth, hartHaltedId, hartHaltedWrEn, + "debug_hart_halted", "Debug ROM Causes hart to write its hartID here when it is in Debug Mode.")), + GOING -> Seq(WNotifyWire(sbIdWidth, hartGoingId, hartGoingWrEn, + "debug_hart_going", "Debug ROM causes hart to write 0 here when it begins executing Debug Mode instructions.")), + RESUMING -> Seq(WNotifyWire(sbIdWidth, hartResumingId, hartResumingWrEn, + "debug_hart_resuming", "Debug ROM causes hart to write 0 here when it leaves Debug Mode.")), + EXCEPTION -> Seq(WNotifyWire(sbIdWidth, hartExceptionId, hartExceptionWrEn, + "debug_hart_exception", "Debug ROM causes hart to write 0 here if it gets an exception in Debug Mode.")), + DATA -> RegFieldGroup("debug_data", Some("Data used to communicate with Debug Module"), + abstractDataMem.zipWithIndex.map {case (x, i) => RegField(8, x, RegFieldDesc(s"debug_data_$i", ""))}), + PROGBUF(cfg)-> RegFieldGroup("debug_progbuf", Some("Program buffer used to communicate with Debug Module"), + 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)) else Nil}, - WHERETO -> Seq(RegField.r(32, jalAbstract.asUInt)), - ABSTRACT(cfg) -> abstractGeneratedMem.map{x => RegField.r(32, x)}, - FLAGS -> flags.map{x => RegField.r(8, x.asUInt())}, - ROMBASE -> DebugRomContents().map(x => RegField.r(8, (x & 0xFF).U(8.W))) - + 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"))), + 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", ""))}), + 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", ""))}), + 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)))}) ) // Override System Bus accesses with dmactive reset. From 718c88a8f96590ce9f3baabbf3c428ae2dce09fe Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Sun, 11 Feb 2018 21:05:17 -0800 Subject: [PATCH 04/10] PLIC: Annotate regmap with RegFieldDescs --- src/main/scala/devices/tilelink/Plic.scala | 27 ++++++++++++++++------ 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/src/main/scala/devices/tilelink/Plic.scala b/src/main/scala/devices/tilelink/Plic.scala index 2c056bb6..9ca39385 100644 --- a/src/main/scala/devices/tilelink/Plic.scala +++ b/src/main/scala/devices/tilelink/Plic.scala @@ -169,12 +169,18 @@ class TLPLIC(params: PLICParams)(implicit p: Parameters) extends LazyModule harts(hart) := ShiftRegister(Reg(next = maxPri) > Cat(UInt(1), threshold(hart)), params.intStages) } - def priorityRegField(x: UInt) = if (nPriorities > 0) RegField(32, x) else RegField.r(32, x) - val priorityRegFields = Seq(PLICConsts.priorityBase -> priority.map(p => priorityRegField(p))) - val pendingRegFields = Seq(PLICConsts.pendingBase -> pending .map(b => RegField.r(1, b))) + def priorityRegDesc(i: Int) = RegFieldDesc(s"priority_$i", s"Acting priority of interrupt source $i", reset=if (nPriorities > 0) None else Some(1)) + def pendingRegDesc(i: Int) = RegFieldDesc(s"pending_$i", s"Set to 1 if interrupt source $i is pending, regardless of its enable or priority setting.") + def priorityRegField(x: UInt, i: Int) = if (nPriorities > 0) RegField(32, x, priorityRegDesc(i)) else RegField.r(32, x, priorityRegDesc(i)) + val priorityRegFields = Seq(PLICConsts.priorityBase -> RegFieldGroup("priority", Some("Acting priorities of each interrupt source. 32 bits for each interrupt source."), + priority.zipWithIndex.map{case (p, i) => priorityRegField(p, i)})) + val pendingRegFields = Seq(PLICConsts.pendingBase -> RegFieldGroup("pending", Some("Pending Bit Array. 1 Bit for each interrupt source."), + pending.zipWithIndex.map{case (b, i) => RegField.r(1, b, pendingRegDesc(i))})) + val enableRegFields = enables.zipWithIndex.map { case (e, i) => - PLICConsts.enableBase(i) -> e.map(b => RegField(1, b)) + PLICConsts.enableBase(i) -> RegFieldGroup("enable", Some("Enable bits for each interrupt source. 1 bit for each interrupt source."), + e.map(b => RegField(1, b, RegFieldDesc(s"enable_$i", "Enable interrupt and claim for source $i", reset=None)))) } // When a hart reads a claim/complete register, then the @@ -208,9 +214,12 @@ class TLPLIC(params: PLICParams)(implicit p: Parameters) extends LazyModule g.complete := c } + def thresholdRegDesc(i: Int) = RegFieldDesc(s"threshold_$i", s"Interrupt & claim threshold for target $i", reset=if (nPriorities > 0) None else Some(1)) + 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 => PLICConsts.hartBase(i) -> Seq( - priorityRegField(threshold(i)), + thresholdRegField(threshold(i), i), RegField(32, RegReadFn { valid => claimer(i) := valid @@ -222,10 +231,14 @@ class TLPLIC(params: PLICParams)(implicit p: Parameters) extends LazyModule completerDev := data.extract(log2Ceil(nDevices+1)-1, 0) completer(i) := valid && enables(i)(completerDev) Bool(true) - } + }, + Some(RegFieldDesc(s"claim_complete_$i", ("Claim/Complete register for Target $i. Reading this register returns the claimed interrupt number and makes it no longer pending." + + "Writing the interrupt number back completes the interrupt."), + reset = None, + access = RegFieldAccessType.RWSPECIAL)) ) ) - } + } node.regmap((priorityRegFields ++ pendingRegFields ++ enableRegFields ++ hartRegFields):_*) From 256f8ffc6bd0dbe2b747fd97dfee60ce4030556c Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Sun, 11 Feb 2018 21:33:09 -0800 Subject: [PATCH 05/10] Clint: Annotate regmap with RegFieldDesc --- src/main/scala/devices/tilelink/Clint.scala | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/scala/devices/tilelink/Clint.scala b/src/main/scala/devices/tilelink/Clint.scala index 94a75fee..ecc6a49f 100644 --- a/src/main/scala/devices/tilelink/Clint.scala +++ b/src/main/scala/devices/tilelink/Clint.scala @@ -83,9 +83,11 @@ class CoreplexLocalInterrupter(params: ClintParams)(implicit p: Parameters) exte */ node.regmap( - 0 -> ipi.map(r => RegField(ipiWidth, r)), - timecmpOffset(0) -> timecmp.flatMap(RegField.bytes(_)), - timeOffset -> RegField.bytes(time)) + 0 -> RegFieldGroup ("msip", Some("MSIP Bits"), ipi.zipWithIndex.map{ case (r, i) => RegField(ipiWidth, r, RegFieldDesc(s"msip_$i", "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("mtimecmp_$i", "", reset=None))))}, + timeOffset -> RegFieldGroup("mtime", Some("Timer Register"), RegField.bytes(time, Some(RegFieldDesc("mtime", "", reset=Some(0))))) + ) } } From 3b44f380d89ffcf1d960d650de2babc9aaafbf23 Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Mon, 8 Jan 2018 18:13:00 -0800 Subject: [PATCH 06/10] TLRegMapper: emit a JSON file describing the register fields --- build.sbt | 2 ++ src/main/scala/tilelink/RegisterRouter.scala | 35 +++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 2fbb499d..f3cb18fd 100644 --- a/build.sbt +++ b/build.sbt @@ -15,6 +15,7 @@ lazy val commonSettings = Seq( traceLevel := 15, scalacOptions ++= Seq("-deprecation","-unchecked"), libraryDependencies ++= Seq("org.scala-lang" % "scala-reflect" % scalaVersion.value), + libraryDependencies ++= Seq("org.json4s" %% "json4s-jackson" % "3.5.0"), addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full) ) @@ -44,3 +45,4 @@ val chipSettings = Seq( s"make -C $makeDir -j $jobs $target".! } ) + diff --git a/src/main/scala/tilelink/RegisterRouter.scala b/src/main/scala/tilelink/RegisterRouter.scala index 44df9669..5fedd959 100644 --- a/src/main/scala/tilelink/RegisterRouter.scala +++ b/src/main/scala/tilelink/RegisterRouter.scala @@ -7,9 +7,12 @@ import freechips.rocketchip.config.Parameters import freechips.rocketchip.diplomacy._ import freechips.rocketchip.regmapper._ import freechips.rocketchip.interrupts._ -import freechips.rocketchip.util.HeterogeneousBag +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, @@ -80,6 +83,36 @@ case class TLRegisterNode( bundleIn.b.valid := Bool(false) bundleIn.c.ready := Bool(true) bundleIn.e.ready := Bool(true) + + // Dump out the register map for documentation purposes. + val registerDescriptions = mapping.map { case (offset, seq) => + var currentBitOffset = 0 + s"regAt0x${offset.toHexString}" -> ( + ("description" -> "None Provided") ~ + ("addressOffset" -> s"0x${offset.toHexString}") ~ + ("fields" -> seq.zipWithIndex.map { case (f, i) => { + val tmp = (f.description.map{ _.displayName }.getOrElse(s"unnamedRegField${i}") -> ( + ("description" -> f.description.map{_.description}.getOrElse("No Description Provided")) ~ + ("bitOffset" -> currentBitOffset) ~ + ("bitWidth" -> f.width) ~ + ("resetMask" -> f.description.map { d => if (d.resetType != RegFieldResetType.N) "all" else "none"}.getOrElse("none")) ~ + ("resetValue" -> f.description.map { _.resetValue}.getOrElse(0)) ~ + ("headerName" -> f.description.map { _.headerName}.getOrElse("")))) + currentBitOffset = currentBitOffset + f.width + tmp + }}))} + + val simpleDev = device.asInstanceOf[SimpleDevice] + val base = s"0x${address.head.base.toInt.toHexString}" + val json = ("peripheral" -> ( + ("displayName" -> s"deviceAt${base}") ~ + ("description" -> s"None Provided") ~ + ("baseAddress" -> base) ~ + ("regWidth" -> beatBytes) ~ + ("access" -> "rw") ~ // specified at field level + ("registers" -> registerDescriptions) + )) + ElaborationArtefacts.add(s"${base}.regmap.json", pretty(render(json))) } } From 5ab4204e8aaaf809303090d340b4c144b152622e Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Wed, 10 Jan 2018 15:53:13 -0800 Subject: [PATCH 07/10] RegField: the JSON will just leave things out of type None --- src/main/scala/tilelink/RegisterRouter.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/scala/tilelink/RegisterRouter.scala b/src/main/scala/tilelink/RegisterRouter.scala index 5fedd959..18c990af 100644 --- a/src/main/scala/tilelink/RegisterRouter.scala +++ b/src/main/scala/tilelink/RegisterRouter.scala @@ -92,12 +92,12 @@ case class TLRegisterNode( ("addressOffset" -> s"0x${offset.toHexString}") ~ ("fields" -> seq.zipWithIndex.map { case (f, i) => { val tmp = (f.description.map{ _.displayName }.getOrElse(s"unnamedRegField${i}") -> ( - ("description" -> f.description.map{_.description}.getOrElse("No Description Provided")) ~ ("bitOffset" -> currentBitOffset) ~ ("bitWidth" -> f.width) ~ - ("resetMask" -> f.description.map { d => if (d.resetType != RegFieldResetType.N) "all" else "none"}.getOrElse("none")) ~ - ("resetValue" -> f.description.map { _.resetValue}.getOrElse(0)) ~ - ("headerName" -> f.description.map { _.headerName}.getOrElse("")))) + ("description" -> f.description.map{ _.description}) ~ + ("resetMask" -> f.description.map { d => if (d.resetType != RegFieldResetType.N) "all" else "none"}) ~ + ("resetValue" -> f.description.map { _.resetValue}) + ("headerName" -> f.description.map { _.headerName})) currentBitOffset = currentBitOffset + f.width tmp }}))} From 08acbe1a298895c9a1499ab5b0653a17d7d957df Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Sun, 11 Feb 2018 23:57:57 -0800 Subject: [PATCH 08/10] RegFieldDesc: Clean up both descriptions and JSON presentations --- src/main/scala/devices/tilelink/Clint.scala | 4 +- src/main/scala/devices/tilelink/Plic.scala | 9 +++-- src/main/scala/regmapper/RegField.scala | 2 +- src/main/scala/tilelink/RegisterRouter.scala | 41 ++++++++++---------- 4 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/main/scala/devices/tilelink/Clint.scala b/src/main/scala/devices/tilelink/Clint.scala index ecc6a49f..f4281417 100644 --- a/src/main/scala/devices/tilelink/Clint.scala +++ b/src/main/scala/devices/tilelink/Clint.scala @@ -83,9 +83,9 @@ class CoreplexLocalInterrupter(params: ClintParams)(implicit p: Parameters) exte */ node.regmap( - 0 -> RegFieldGroup ("msip", Some("MSIP Bits"), ipi.zipWithIndex.map{ case (r, i) => RegField(ipiWidth, r, RegFieldDesc(s"msip_$i", "MSIP bit for Hart $i", 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("mtimecmp_$i", "", reset=None))))}, + 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))))) ) } diff --git a/src/main/scala/devices/tilelink/Plic.scala b/src/main/scala/devices/tilelink/Plic.scala index 9ca39385..b995b816 100644 --- a/src/main/scala/devices/tilelink/Plic.scala +++ b/src/main/scala/devices/tilelink/Plic.scala @@ -179,8 +179,8 @@ class TLPLIC(params: PLICParams)(implicit p: Parameters) extends LazyModule val enableRegFields = enables.zipWithIndex.map { case (e, i) => - PLICConsts.enableBase(i) -> RegFieldGroup("enable", Some("Enable bits for each interrupt source. 1 bit for each interrupt source."), - e.map(b => RegField(1, b, RegFieldDesc(s"enable_$i", "Enable interrupt and claim for source $i", reset=None)))) + PLICConsts.enableBase(i) -> RegFieldGroup(s"enables_${i}", Some("Enable bits for each interrupt source for target $i. 1 bit for each interrupt source."), + e.zipWithIndex.map{case (b, j) => RegField(1, b, RegFieldDesc(s"enable_$i_$j", s"Enable interrupt for source $j for target $i.", reset=None))}) } // When a hart reads a claim/complete register, then the @@ -232,8 +232,9 @@ class TLPLIC(params: PLICParams)(implicit p: Parameters) extends LazyModule completer(i) := valid && enables(i)(completerDev) Bool(true) }, - Some(RegFieldDesc(s"claim_complete_$i", ("Claim/Complete register for Target $i. Reading this register returns the claimed interrupt number and makes it no longer pending." + - "Writing the interrupt number back completes the interrupt."), + Some(RegFieldDesc(s"claim_complete_$i", + 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)) ) diff --git a/src/main/scala/regmapper/RegField.scala b/src/main/scala/regmapper/RegField.scala index 34e062b5..0f5e64cc 100644 --- a/src/main/scala/regmapper/RegField.scala +++ b/src/main/scala/regmapper/RegField.scala @@ -164,7 +164,7 @@ object RegField val valids = Wire(init = Vec.fill(numBytes) { Bool(false) }) when (valids.reduce(_ || _)) { reg := newBytes.asUInt } Seq.tabulate(numBytes) { i => - val newDesc = desc.map {d => d.copy(name = d.name + s"[${i*8-1}:${i*8}]")} + val newDesc = desc.map {d => d.copy(name = d.name + s"[${(i+1)*8-1}:${i*8}]")} RegField(8, oldBytes(i), RegWriteFn((valid, data) => { valids(i) := valid diff --git a/src/main/scala/tilelink/RegisterRouter.scala b/src/main/scala/tilelink/RegisterRouter.scala index 18c990af..7bb04d66 100644 --- a/src/main/scala/tilelink/RegisterRouter.scala +++ b/src/main/scala/tilelink/RegisterRouter.scala @@ -85,32 +85,31 @@ case class TLRegisterNode( bundleIn.e.ready := Bool(true) // Dump out the register map for documentation purposes. - val registerDescriptions = mapping.map { case (offset, seq) => - var currentBitOffset = 0 - s"regAt0x${offset.toHexString}" -> ( - ("description" -> "None Provided") ~ - ("addressOffset" -> s"0x${offset.toHexString}") ~ - ("fields" -> seq.zipWithIndex.map { case (f, i) => { - val tmp = (f.description.map{ _.displayName }.getOrElse(s"unnamedRegField${i}") -> ( - ("bitOffset" -> currentBitOffset) ~ - ("bitWidth" -> f.width) ~ - ("description" -> f.description.map{ _.description}) ~ - ("resetMask" -> f.description.map { d => if (d.resetType != RegFieldResetType.N) "all" else "none"}) ~ - ("resetValue" -> f.description.map { _.resetValue}) - ("headerName" -> f.description.map { _.headerName})) - currentBitOffset = currentBitOffset + f.width - tmp - }}))} + val regDescs = mapping.map { case (offset, seq) => + var currentBitOffset = 0 + (s"0x${offset.toHexString}" -> seq.zipWithIndex.map { case (f, i) => { + val tmp = (f.desc.map{ _.name}.getOrElse(s"unnamedRegField${i}") -> ( + ("byteOffset" -> s"0x${offset.toHexString}") ~ + ("bitOffset" -> currentBitOffset) ~ + ("bitWidth" -> f.width) ~ + ("name" -> f.desc.map(_.name) + ("description" -> f.desc.map{if _.desc == "" None else Some(_.desc)}) ~ + ("resetValue" -> f.desc.map{_.reset}) ~ + ("group" -> f.desc.map{_.group}) ~ + ("groupDesc" -> f.desc.map{_.groupDesc}) ~ + ("accessType" -> f.desc.map {d => d.access.toString}) + )) + currentBitOffset = currentBitOffset + f.width + tmp + }}) + } - val simpleDev = device.asInstanceOf[SimpleDevice] + //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}") ~ - ("description" -> s"None Provided") ~ ("baseAddress" -> base) ~ - ("regWidth" -> beatBytes) ~ - ("access" -> "rw") ~ // specified at field level - ("registers" -> registerDescriptions) + ("regfields" -> regDescs) )) ElaborationArtefacts.add(s"${base}.regmap.json", pretty(render(json))) } From 7bf0121f07457f22f363e90544f5d64861e35e8f Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Mon, 12 Feb 2018 08:31:29 -0800 Subject: [PATCH 09/10] PLIC: correct some descriptions --- src/main/scala/devices/tilelink/Plic.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/scala/devices/tilelink/Plic.scala b/src/main/scala/devices/tilelink/Plic.scala index b995b816..9de673d3 100644 --- a/src/main/scala/devices/tilelink/Plic.scala +++ b/src/main/scala/devices/tilelink/Plic.scala @@ -179,8 +179,8 @@ class TLPLIC(params: PLICParams)(implicit p: Parameters) extends LazyModule val enableRegFields = enables.zipWithIndex.map { case (e, i) => - PLICConsts.enableBase(i) -> RegFieldGroup(s"enables_${i}", Some("Enable bits for each interrupt source for target $i. 1 bit for each interrupt source."), - e.zipWithIndex.map{case (b, j) => RegField(1, b, RegFieldDesc(s"enable_$i_$j", s"Enable interrupt for source $j for target $i.", reset=None))}) + PLICConsts.enableBase(i) -> RegFieldGroup(s"enables_${i}", Some(s"Enable bits for each interrupt source for target $i. 1 bit for each interrupt source."), + e.zipWithIndex.map{case (b, j) => RegField(1, b, RegFieldDesc(s"enable_${i}_${j}", s"Enable interrupt for source $j for target $i.", reset=None))}) } // When a hart reads a claim/complete register, then the From de91672e9a5fd135e4e2b9bc9c1fbc52851b8275 Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Mon, 12 Feb 2018 08:32:52 -0800 Subject: [PATCH 10/10] RegFieldDesc: simplify the output RegFieldDesc JSON to just a list of reg fields --- src/main/scala/tilelink/RegisterRouter.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/scala/tilelink/RegisterRouter.scala b/src/main/scala/tilelink/RegisterRouter.scala index 7bb04d66..2d99a693 100644 --- a/src/main/scala/tilelink/RegisterRouter.scala +++ b/src/main/scala/tilelink/RegisterRouter.scala @@ -85,23 +85,23 @@ case class TLRegisterNode( bundleIn.e.ready := Bool(true) // Dump out the register map for documentation purposes. - val regDescs = mapping.map { case (offset, seq) => + val regDescs = mapping.flatMap { case (offset, seq) => var currentBitOffset = 0 - (s"0x${offset.toHexString}" -> seq.zipWithIndex.map { case (f, i) => { + seq.zipWithIndex.map { case (f, i) => { val tmp = (f.desc.map{ _.name}.getOrElse(s"unnamedRegField${i}") -> ( ("byteOffset" -> s"0x${offset.toHexString}") ~ ("bitOffset" -> currentBitOffset) ~ ("bitWidth" -> f.width) ~ - ("name" -> f.desc.map(_.name) - ("description" -> f.desc.map{if _.desc == "" None else Some(_.desc)}) ~ + ("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}) - )) + )) currentBitOffset = currentBitOffset + f.width tmp - }}) + }} } //TODO: It would be better to name this other than "Device at ...."