Skip to content
This repository has been archived by the owner on Jun 16, 2024. It is now read-only.

Commit

Permalink
work on "InOut" (essentially a Bundle (!)), drop Analog; resource can…
Browse files Browse the repository at this point in the history
… have multiple bases.
  • Loading branch information
kivikakk committed May 21, 2024
1 parent 44cf001 commit e24faf7
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 55 deletions.
12 changes: 9 additions & 3 deletions src/main/scala/ee/hrzn/chryse/platform/BoardResources.scala
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
package ee.hrzn.chryse.platform

import ee.hrzn.chryse.platform.resource
import chisel3._
import ee.hrzn.chryse.platform.resource.Base
import ee.hrzn.chryse.platform.resource.Resource

import scala.collection.mutable.ArrayBuffer

abstract class BoardResources {
private[chryse] def setNames() =
for { f <- this.getClass().getDeclaredFields() } {
f.setAccessible(true)
f.get(this) match {
case res: resource.Base[_] =>
res.name = Some(f.getName())
case res: Resource =>
res.setName(f.getName())
case _ =>
}
}

val clock: resource.ClockSource

def all: List[Base[_ <: Data]] = Resource.allFromBoardResources(this)
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class BlackBoxGenerator(private val wr: Writer) {
((name, dat)) <-
io.elements.toSeq.reverseIterator
} {
// TODO: "inout" (Chisel "Analog")
// TODO: "inout" (Chisel "Analog"? seems awk.)
val dir = DirectionOf(dat)
dat match {
case vec: Vec[_] =>
Expand Down
13 changes: 4 additions & 9 deletions src/main/scala/ee/hrzn/chryse/platform/ice40/ICE40Top.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,11 @@ class ICE40Top[Top <: Module](

// TODO: allow clock override.

// TODO: refactor out the main machinations to a non-ICE40Top level; override
// the PCF generation specifically (needs to tie in with ICE40-specific build
// process).
private val ios = mutable.Map[String, resource.Pin]()
private val freqs = mutable.Map[String, Int]()
for { f <- platform.resources.getClass().getDeclaredFields() } {
val name = f.getName()
f.setAccessible(true)
f.get(platform.resources) match {
for { res <- platform.resources.all } {
val name = res.name.get
res match {
case clock: resource.ClockSource =>
if (clock.ioInst.isDefined) {
throw new Exception("clock must be manually handled for now")
Expand All @@ -75,7 +71,7 @@ class ICE40Top[Top <: Module](
val io = IO(Input(Clock())).suggestName(name)
clki := io

case res: resource.Base[_] =>
case _ =>
if (res.ioInst.isDefined) {
ios += name -> res.pinId.get
val io = IO(res.makeIo()).suggestName(name)
Expand All @@ -88,7 +84,6 @@ class ICE40Top[Top <: Module](
throw new Exception(s"unhandled direction: $dir")
}
}
case _ =>
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,16 @@ class IceBreakerResources extends BoardResources {
val pmod1a8 = resource.InOut().onPin(48)
val pmod1a9 = resource.InOut().onPin(46)
val pmod1a10 = resource.InOut().onPin(44)
// TODO: pmod1b

val pmod1b1 = resource.InOut().onPin(43)
val pmod1b2 = resource.InOut().onPin(38)
val pmod1b3 = resource.InOut().onPin(34)
val pmod1b4 = resource.InOut().onPin(31)
val pmod1b7 = resource.InOut().onPin(42)
val pmod1b8 = resource.InOut().onPin(36)
val pmod1b9 = resource.InOut().onPin(32)
val pmod1b10 = resource.InOut().onPin(28)

val pmod2_1 = resource.InOut().onPin(27)
val pmod2_2 = resource.InOut().onPin(25)
val pmod2_3 = resource.InOut().onPin(21)
Expand Down
33 changes: 6 additions & 27 deletions src/main/scala/ee/hrzn/chryse/platform/resource/Base.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package ee.hrzn.chryse.platform.resource

import chisel3._
import chisel3.experimental.dataview._

import scala.language.implicitConversions

abstract class Base[HW <: Data](gen: => HW) {
final private[chryse] var pinId: Option[Pin] = None
final private[chryse] var name: Option[String] = None
abstract class Base[HW <: Data](gen: => HW) extends Resource {
final private[chryse] var pinId: Option[Pin] = None
final var name: Option[String] = None

// Should return Chisel datatype with Input/Output attached.
private[chryse] def makeIo(): HW = gen
Expand All @@ -27,32 +24,14 @@ abstract class Base[HW <: Data](gen: => HW) {
}
}

def setName(name: String): Unit = this.name = Some(name)

def onPin(id: Pin): this.type = {
pinId = Some(id)
this
}

object Implicits {
implicit val BaseProduct: DataProduct[Base[HW]] =
new DataProduct[Base[HW]] {
def dataIterator(
res: Base[HW],
path: String,
): Iterator[(Data, String)] =
List(res.ioInst.get.user -> path).iterator
}

implicit def view: DataView[Base[HW], HW] =
DataView(res => gen, _.ioInstOrMake().user -> _)

implicit def Base2HW(res: Base[HW]): HW =
res.viewAs[HW]
}
def bases(): List[Base[_ <: Data]] = List(this)
}

case class InstSides[HW](user: HW, top: HW)

// Note that the DataView doesn't really need or care about the generated data's
// direction or lack thereof, so this is sufficient for all Base[Bool]
// subclasses.
object BaseBool extends Base[Bool](Bool()) {}
19 changes: 17 additions & 2 deletions src/main/scala/ee/hrzn/chryse/platform/resource/InOut.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
package ee.hrzn.chryse.platform.resource

import chisel3._
import chisel3.experimental.Analog

class InOut extends Base[Analog](Analog(1.W)) {}
class InOut extends Resource {
val i = new Base[Bool](Input(Bool())) {}
val o = new Base[Bool](Output(Bool())) {}

def setName(name: String): Unit = {
i.setName(s"$name")
o.setName(s"$name")
}

def onPin(id: Pin): this.type = {
i.onPin(id)
o.onPin(id)
this
}

def bases(): List[Base[_ <: Data]] = List(i, o)
}

object InOut {
def apply() = new InOut
Expand Down
29 changes: 29 additions & 0 deletions src/main/scala/ee/hrzn/chryse/platform/resource/Resource.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package ee.hrzn.chryse.platform.resource

import chisel3._
import ee.hrzn.chryse.platform.BoardResources

import scala.collection.mutable.ArrayBuffer

trait Resource {
def setName(name: String): Unit
def onPin(id: Pin): this.type
def bases(): List[Base[_ <: Data]]
}

object Resource {
def allFromBoardResources[T <: BoardResources](
br: T,
): List[Base[_ <: Data]] = {
var out = ArrayBuffer[Base[_ <: Data]]()
for { f <- br.getClass().getDeclaredFields().iterator } {
f.setAccessible(true)
f.get(br) match {
case res: Resource =>
out.appendAll(res.bases())
case _ =>
}
}
out.toList
}
}
26 changes: 26 additions & 0 deletions src/main/scala/ee/hrzn/chryse/platform/resource/implicits.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ee.hrzn.chryse.platform.resource

import chisel3._
import chisel3.experimental.dataview._

import scala.language.implicitConversions

object implicits {
// Note that the DataView doesn't really need or care about the generated
// data's direction or lack thereof.

implicit def BaseProduct[HW <: Data]: DataProduct[Base[HW]] =
new DataProduct[Base[HW]] {
def dataIterator(
res: Base[HW],
path: String,
): Iterator[(Data, String)] =
List(res.ioInst.get.user -> path).iterator
}

implicit def viewBool: DataView[Base[Bool], Bool] =
DataView(res => Bool(), _.ioInstOrMake().user -> _)

implicit def base2Bool(res: Base[Bool]): Bool =
res.viewAs[Bool]
}
67 changes: 55 additions & 12 deletions src/test/scala/ee/hrzn/chryse/platform/BoardResourcesSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,28 @@ 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.platform.resource.BaseBool.Implicits._
import ee.hrzn.chryse.platform.resource.implicits._
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should._

class BoardResourcesSpec extends AnyFlatSpec with Matchers {
behavior.of("BoardResources")

it should "detect resource uses and generate PCFs accordingly" in {
def iceBreakerSVAndTop[Top <: Module](
gen: Platform => Top,
): (String, ICE40Top[Top]) = {
var top: ICE40Top[Top] = null
val rtl = ChiselStage.emitSystemVerilog(
{
top = IceBreakerPlatform()(gen)
top
},
firtoolOpts = Array("-strip-debug-info"),
)
(rtl, top)
}

it should "detect resource use and generate PCFs accordingly" in {
val plat = IceBreakerPlatform()
val top = BuilderContext {
plat(new DetectionTop(_))
Expand All @@ -36,15 +50,7 @@ class BoardResourcesSpec extends AnyFlatSpec with Matchers {
}

it should "invert inputs as requested and use the correct top-level IO names" in {
val plat = IceBreakerPlatform()
var top: ICE40Top[_] = null
val rtl = ChiselStage.emitSystemVerilog(
{
top = plat(new InversionTop(_))
top
},
firtoolOpts = Array("-strip-debug-info"),
)
val (rtl, top) = iceBreakerSVAndTop(new InversionTop(_))
top.lastPCF should be(
Some(
PCF(
Expand All @@ -60,7 +66,6 @@ class BoardResourcesSpec extends AnyFlatSpec with Matchers {
)

rtl should include("ledg_int = view__ubtn_int")
(rtl should not).include("uart_tx_int = view__ubtn_int")
rtl should include("uart_tx_int = ~view__ubtn_int")

// HACK: this is brittle. Parse the Verilog or something.
Expand All @@ -69,6 +74,33 @@ class BoardResourcesSpec extends AnyFlatSpec with Matchers {
"module chrysetop( input clock, ubtn, output 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,
"pmod1a1" -> 4,
"uart_rx" -> 6,
"pmod1b1" -> 43,
"ubtn" -> 10,
),
Map("clock" -> 12_000_000),
),
),
)

rtl should include("pmod1a1_int = view__uart_rx_int")
rtl should include("pmod1b1_int = ~view__ubtn_int")

"\\s+".r
.replaceAllIn(rtl, " ") should include(
"module chrysetop( input clock, ubtn, uart_rx, output pmod1a1, pmod1b1 );",
)

}
}

class DetectionTop(platform: Platform) extends Module {
Expand All @@ -85,3 +117,14 @@ class InversionTop(platform: Platform) extends Module {
// LED is inverted.
plat.resources.ledg := plat.resources.ubtn
}

class InOutTop(platform: Platform) extends Module {
val plat = platform.asInstanceOf[IceBreakerPlatform]
// Treat pmod1a1 as output, 1a2 as input.
plat.resources.pmod1a1.o := plat.resources.uart_rx
// plat.resources.uart_tx := plat.resources.pmod1a2.i

// Do the same with 1b1 and 1b2, but use inverted inputs/outputs.
plat.resources.pmod1b1.o := plat.resources.ubtn
// plat.resources.ledr := plat.resources.pmod1b2.i
}

0 comments on commit e24faf7

Please sign in to comment.