From 1dad3e201c8f76fa6407c0d6593cdc00e4016634 Mon Sep 17 00:00:00 2001 From: Asherah Connor Date: Thu, 23 May 2024 23:11:34 +0300 Subject: [PATCH] oh lordy he comin'. (SimTop/SimPlatform, res clock optional.) --- .../ee/hrzn/chryse/platform/ChryseTop.scala | 18 ++- .../platform/PlatformBoardResources.scala | 2 - .../hrzn/chryse/platform/ice40/ICE40Top.scala | 2 +- .../hrzn/chryse/platform/resource/InOut.scala | 3 +- .../hrzn/chryse/platform/resource/Pin.scala | 2 + .../platform/PlatformBoardResourcesSpec.scala | 106 +++++++----------- .../ee/hrzn/chryse/platform/SimPlatform.scala | 45 ++++++++ .../ee/hrzn/chryse/platform/SimTop.scala | 17 +++ 8 files changed, 122 insertions(+), 73 deletions(-) create mode 100644 src/test/scala/ee/hrzn/chryse/platform/SimPlatform.scala create mode 100644 src/test/scala/ee/hrzn/chryse/platform/SimTop.scala diff --git a/src/main/scala/ee/hrzn/chryse/platform/ChryseTop.scala b/src/main/scala/ee/hrzn/chryse/platform/ChryseTop.scala index 36aee88..a61517d 100644 --- a/src/main/scala/ee/hrzn/chryse/platform/ChryseTop.scala +++ b/src/main/scala/ee/hrzn/chryse/platform/ChryseTop.scala @@ -4,15 +4,21 @@ import chisel3._ import chisel3.experimental.noPrefix import scala.collection.mutable +import scala.language.implicitConversions trait ChryseTop extends RawModule { override def desiredName = "chrysetop" case class ConnectedResource(pin: resource.Pin, frequencyHz: Option[Int]) + object ConnectedResource { + implicit def pin2Cr(pin: resource.Pin): ConnectedResource = + ConnectedResource(pin, None) + } + protected def connectResources( platform: PlatformBoard[_ <: PlatformBoardResources], - clock: Clock, + clock: Option[Clock], ): Map[String, ConnectedResource] = { val connected = mutable.Map[String, ConnectedResource]() @@ -21,7 +27,9 @@ trait ChryseTop extends RawModule { res match { case res: resource.ClockSource => if (res.ioInst.isDefined) { - throw new Exception("clock must be manually handled for now") + throw new Exception( + "clock sources must be manually handled for now", + ) } // NOTE: we can't just say clki := platform.resources.clock in our top // here, since that'll define an input IO in *this* module which we @@ -30,14 +38,14 @@ trait ChryseTop extends RawModule { res.pinId.get, Some(platform.clockHz), ) - clock := noPrefix(IO(Input(Clock())).suggestName(name)) + clock.get := noPrefix(IO(Input(Clock())).suggestName(name)) case _ => if (platformConnect(name, res)) { - connected += name -> ConnectedResource(res.pinId.get, None) + connected += name -> res.pinId.get } else if (res.ioInst.isDefined) { - connected += name -> ConnectedResource(res.pinId.get, None) res.makeIoConnection() + connected += name -> res.pinId.get } } } diff --git a/src/main/scala/ee/hrzn/chryse/platform/PlatformBoardResources.scala b/src/main/scala/ee/hrzn/chryse/platform/PlatformBoardResources.scala index 125cdbb..8930f90 100644 --- a/src/main/scala/ee/hrzn/chryse/platform/PlatformBoardResources.scala +++ b/src/main/scala/ee/hrzn/chryse/platform/PlatformBoardResources.scala @@ -17,8 +17,6 @@ abstract class PlatformBoardResources { } } - val clock: resource.ClockSource - def all: Seq[ResourceData[_ <: Data]] = ResourceBase.allFromBoardResources(this) } diff --git a/src/main/scala/ee/hrzn/chryse/platform/ice40/ICE40Top.scala b/src/main/scala/ee/hrzn/chryse/platform/ice40/ICE40Top.scala index 6b012b7..43d80ab 100644 --- a/src/main/scala/ee/hrzn/chryse/platform/ice40/ICE40Top.scala +++ b/src/main/scala/ee/hrzn/chryse/platform/ice40/ICE40Top.scala @@ -56,7 +56,7 @@ class ICE40Top[Top <: Module]( // TODO (iCE40): allow clock source override. private val connectedResources = - connectResources(platform, clki) + connectResources(platform, Some(clki)) override protected def platformConnect( name: String, diff --git a/src/main/scala/ee/hrzn/chryse/platform/resource/InOut.scala b/src/main/scala/ee/hrzn/chryse/platform/resource/InOut.scala index 37079cb..60a1e9b 100644 --- a/src/main/scala/ee/hrzn/chryse/platform/resource/InOut.scala +++ b/src/main/scala/ee/hrzn/chryse/platform/resource/InOut.scala @@ -3,7 +3,8 @@ package ee.hrzn.chryse.platform.resource import chisel3._ // TODO: it's an error to use both "i" and "o" (tristate is a different kettle -// of fish entirely). +// of fish entirely) — this'll currently throw an obscure Chisel error (since +// they'll both get the same name). class InOut extends ResourceBase with ResourceSinglePin { val i = new ResourceData[Bool](Input(Bool())) {} val o = new ResourceData[Bool](Output(Bool())) {} diff --git a/src/main/scala/ee/hrzn/chryse/platform/resource/Pin.scala b/src/main/scala/ee/hrzn/chryse/platform/resource/Pin.scala index 71f1804..79d777c 100644 --- a/src/main/scala/ee/hrzn/chryse/platform/resource/Pin.scala +++ b/src/main/scala/ee/hrzn/chryse/platform/resource/Pin.scala @@ -11,6 +11,8 @@ case class PinInt(p: Int) extends Pin { } object Pin { + def apply(p: String): Pin = string2Pin(p) + implicit def string2Pin(p: String): Pin = PinString(p) implicit def int2Pin(p: Int): Pin = PinInt(p) } diff --git a/src/test/scala/ee/hrzn/chryse/platform/PlatformBoardResourcesSpec.scala b/src/test/scala/ee/hrzn/chryse/platform/PlatformBoardResourcesSpec.scala index 311f96d..8c4b422 100644 --- a/src/test/scala/ee/hrzn/chryse/platform/PlatformBoardResourcesSpec.scala +++ b/src/test/scala/ee/hrzn/chryse/platform/PlatformBoardResourcesSpec.scala @@ -3,10 +3,6 @@ package ee.hrzn.chryse.platform import chisel3._ import chiseltest._ import circt.stage.ChiselStage -import ee.hrzn.chryse.chisel.BuilderContext -import ee.hrzn.chryse.platform.ice40.ICE40Top -import ee.hrzn.chryse.platform.ice40.IceBreakerPlatform -import ee.hrzn.chryse.platform.ice40.PCF import ee.hrzn.chryse.verilog import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should._ @@ -17,12 +13,12 @@ class PlatformBoardResourcesSpec with ChiselScalatestTester { behavior.of("PlatformBoardResources") - def iceBreakerSVAndTop[Top <: Module]( + def simSVAndTop[Top <: Module]( gen: Platform => Top, - ): (String, ICE40Top[Top]) = { - val plat = IceBreakerPlatform() + ): (String, SimTop[Top]) = { + val plat = SimPlatform() - var top: ICE40Top[Top] = null + var top: SimTop[Top] = null val rtl = ChiselStage.emitSystemVerilog( { top = plat(gen(plat)) @@ -34,36 +30,24 @@ class PlatformBoardResourcesSpec } it should "detect resource use and generate PCFs accordingly" in { - val (_, top) = iceBreakerSVAndTop(new DetectionTop()(_)) - top.lastPCF should be( - Some( - PCF( - Map( - "clock" -> 35, - "ledg" -> 37, - "uart_rx" -> 6, - "uart_tx" -> 9, - "ubtn" -> 10, - ), - Map("clock" -> 12_000_000), - ), + val (_, top) = simSVAndTop(new DetectionTop()(_)) + top.connectedResources should be( + Map[String, top.ConnectedResource]( + "ledg" -> resource.Pin("E5"), + "uart_rx" -> resource.Pin("C3"), + "uart_tx" -> resource.Pin("D4"), + "ubtn" -> resource.Pin("B2"), ), ) } it should "invert inputs as requested and use the correct top-level IO names" in { - val (rtl, top) = iceBreakerSVAndTop(new InversionTop()(_)) - top.lastPCF should be( - Some( - PCF( - Map( - "clock" -> 35, - "ledg" -> 37, - "uart_tx" -> 9, - "ubtn" -> 10, - ), - Map("clock" -> 12_000_000), - ), + val (rtl, top) = simSVAndTop(new InversionTop()(_)) + top.connectedResources should be( + Map[String, top.ConnectedResource]( + "ledg" -> resource.Pin("E5"), + "uart_tx" -> resource.Pin("D4"), + "ubtn" -> resource.Pin("B2"), ), ) @@ -74,59 +58,53 @@ class PlatformBoardResourcesSpec verilog.InterfaceExtractor(rtl) should contain( "chrysetop" -> verilog.InterfaceExtractor.Module( - inputs = Seq("clock", "ubtn"), + inputs = Seq("clock", "reset", "ubtn"), outputs = Seq("uart_tx", "ledg"), ), ) } it should "handle in/out resources" in { - val (rtl, top) = iceBreakerSVAndTop(new InOutTop()(_)) - top.lastPCF should be( - Some( - PCF( - Map( - "clock" -> 35, - "ubtn" -> 10, - "uart_rx" -> 6, - "uart_tx" -> 9, - "ledr" -> 11, - "pmod1a1" -> 4, - "pmod1a2" -> 2, - "pmod1b1" -> 43, - "pmod1b2" -> 38, - ), - Map("clock" -> 12_000_000), - ), + val (rtl, top) = simSVAndTop(new InOutTop()(_)) + top.connectedResources should be( + Map[String, top.ConnectedResource]( + "ubtn" -> resource.Pin("B2"), + "uart_rx" -> resource.Pin("C3"), + "uart_tx" -> resource.Pin("D4"), + "ledr" -> resource.Pin("F6"), + "pmod1" -> resource.Pin("H8"), + "pmod2" -> resource.Pin("I9"), + "pmod7" -> resource.Pin("L12"), + "pmod8" -> resource.Pin("M13"), ), ) // HACK: We should behaviourally evaluate the result. - rtl should include("pmod1a1_int = view__uart_rx_int") - rtl should include("uart_tx_int = view__pmod1a2_int") - rtl should include("pmod1b1_int = view__ubtn_int") + rtl should include("pmod1_int = view__uart_rx_int") + rtl should include("uart_tx_int = view__pmod2_int") + rtl should include("pmod7_int = view__ubtn_int") (rtl should include).regex(raw"\.view__ubtn_int\s*\(~ubtn\),") - rtl should include("ledr_int = view__pmod1b2_int") + rtl should include("ledr_int = view__pmod8_int") (rtl should include).regex(raw"\.ledr_int\s*\(_top_ledr_int\),") (rtl should include).regex(raw"assign ledr = ~_top_ledr_int;") verilog.InterfaceExtractor(rtl) should contain( "chrysetop" -> verilog.InterfaceExtractor.Module( - inputs = Seq("clock", "ubtn", "uart_rx", "pmod1a2", "pmod1b2"), - outputs = Seq("uart_tx", "ledr", "pmod1a1", "pmod1b1"), + inputs = Seq("clock", "reset", "ubtn", "uart_rx", "pmod2", "pmod8"), + outputs = Seq("uart_tx", "ledr", "pmod1", "pmod7"), ), ) } } class DetectionTop(implicit platform: Platform) extends Module { - val plat = platform.asInstanceOf[IceBreakerPlatform] + val plat = platform.asInstanceOf[SimPlatform] plat.resources.ledg := plat.resources.ubtn plat.resources.uart.tx := plat.resources.uart.rx } class InversionTop(implicit platform: Platform) extends Module { - val plat = platform.asInstanceOf[IceBreakerPlatform] + val plat = platform.asInstanceOf[SimPlatform] // User button is inverted. // UART isn't inverted. plat.resources.uart.tx := plat.resources.ubtn @@ -135,12 +113,12 @@ class InversionTop(implicit platform: Platform) extends Module { } class InOutTop(implicit platform: Platform) extends Module { - val plat = platform.asInstanceOf[IceBreakerPlatform] + val plat = platform.asInstanceOf[SimPlatform] // Treat pmod1a1 as output, 1a2 as input. - plat.resources.pmod1a(1).o := plat.resources.uart.rx - plat.resources.uart.tx := plat.resources.pmod1a(2).i + plat.resources.pmod(1).o := plat.resources.uart.rx + plat.resources.uart.tx := plat.resources.pmod(2).i // Do the same with 1b1 and 1b2, but use inverted inputs/outputs. - plat.resources.pmod1b(1).o := plat.resources.ubtn - plat.resources.ledr := plat.resources.pmod1b(2).i + plat.resources.pmod(7).o := plat.resources.ubtn + plat.resources.ledr := plat.resources.pmod(8).i } diff --git a/src/test/scala/ee/hrzn/chryse/platform/SimPlatform.scala b/src/test/scala/ee/hrzn/chryse/platform/SimPlatform.scala new file mode 100644 index 0000000..134b70b --- /dev/null +++ b/src/test/scala/ee/hrzn/chryse/platform/SimPlatform.scala @@ -0,0 +1,45 @@ +package ee.hrzn.chryse.platform + +import chisel3._ +import ee.hrzn.chryse.platform.PlatformBoard +import ee.hrzn.chryse.platform.PlatformBoardResources +import ee.hrzn.chryse.platform.resource + +final case class SimPlatform() extends PlatformBoard[SimPlatformResources] { + val id = "sim" + val clockHz = 1_000_000 + + val nextpnrBinary = "xxx" + val nextpnrArgs = Seq() + val packBinary = "xxx" + val programBinary = "xxx" + + val resources = new SimPlatformResources + + override def apply[Top <: Module](genTop: => Top) = { + resources.setNames() + new SimTop(this, genTop) + } +} + +class SimPlatformResources extends PlatformBoardResources { + val ubtn = resource.Button().inverted.onPin("B2") + + val uart = resource.UART().onPins(rx = "C3", tx = "D4") + + val ledg = resource.LED().inverted.onPin("E5") + val ledr = resource.LED().inverted.onPin("F6") + val led3 = resource.LED().inverted.onPin("G7") + + val pmod = resource.Connector( + resource.InOut(), + 1 -> "H8", + 2 -> "I9", + 3 -> "J10", + 4 -> "K11", + 7 -> "L12", + 8 -> "M13", + 9 -> "N14", + 10 -> "O15", + ) +} diff --git a/src/test/scala/ee/hrzn/chryse/platform/SimTop.scala b/src/test/scala/ee/hrzn/chryse/platform/SimTop.scala new file mode 100644 index 0000000..f700d7a --- /dev/null +++ b/src/test/scala/ee/hrzn/chryse/platform/SimTop.scala @@ -0,0 +1,17 @@ +package ee.hrzn.chryse.platform + +import chisel3._ +import ee.hrzn.chryse.platform.PlatformBoard +import ee.hrzn.chryse.platform.PlatformBoardResources +import ee.hrzn.chryse.platform.resource + +class SimTop[Top <: Module]( + platform: PlatformBoard[_ <: PlatformBoardResources], + genTop: => Top, +) extends Module + with ChryseTop { + private val top = Module(genTop) + + val connectedResources = + connectResources(platform, None) +}