From 3b44f380d89ffcf1d960d650de2babc9aaafbf23 Mon Sep 17 00:00:00 2001 From: Megan Wachs Date: Mon, 8 Jan 2018 18:13:00 -0800 Subject: [PATCH 1/5] 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 2/5] 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 3/5] 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 4/5] 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 5/5] 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 ...."