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

Commit

Permalink
BlackBoxGenerator: support nested bundles, flipping, etc.
Browse files Browse the repository at this point in the history
  • Loading branch information
kivikakk committed May 28, 2024
1 parent 02ba52b commit 51db26e
Show file tree
Hide file tree
Showing 10 changed files with 130 additions and 73 deletions.
6 changes: 3 additions & 3 deletions src/main/scala/ee/hrzn/chryse/ExampleApp.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ object ExampleApp extends ChryseApp {
override def genTop()(implicit platform: Platform) = new Top
override val targetPlatforms = Seq(IceBreakerPlatform())
override val cxxrtlOptions = Some(
CXXRTLOptions(platforms =
Seq(new CXXRTLPlatform(id = "ex", clockHz = 3_000_000) {}),
),
CXXRTLOptions(platforms = Seq(new CXXRTLPlatform("ex") {
val clockHz = 3_000_000
})),
)
}
7 changes: 2 additions & 5 deletions src/main/scala/ee/hrzn/chryse/chisel/DirectionOf.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,9 @@ import chisel3._

import scala.language.implicitConversions

private[chryse] object DirectionOf {
private[chryse] object directionOf {
def apply[T <: Data](data: T): Direction =
classOf[Data]
.getMethod("specifiedDirection") // private[chisel3]
.invoke(data)
.asInstanceOf[SpecifiedDirection] match {
specifiedDirectionOf(data) match {
case SpecifiedDirection.Input => Input
case SpecifiedDirection.Output => Output
case dir => throw new Exception(s"unhandled direction $dir")
Expand Down
13 changes: 13 additions & 0 deletions src/main/scala/ee/hrzn/chryse/chisel/specifiedDirectionOf.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ee.hrzn.chryse.chisel

import chisel3._

import scala.language.implicitConversions

private[chryse] object specifiedDirectionOf {
def apply[T <: Data](data: T): SpecifiedDirection =
classOf[Data]
.getMethod("specifiedDirection") // private[chisel3]
.invoke(data)
.asInstanceOf[SpecifiedDirection]
}
8 changes: 4 additions & 4 deletions src/main/scala/ee/hrzn/chryse/platform/ChryseTop.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package ee.hrzn.chryse.platform

import chisel3._
import chisel3.experimental.noPrefix
import ee.hrzn.chryse.chisel.DirectionOf
import ee.hrzn.chryse.chisel.directionOf

import scala.collection.mutable
import scala.language.existentials
Expand All @@ -28,10 +28,10 @@ trait ChryseTop extends RawModule {
topIo: Data,
portIo: Data,
): Unit = {
DirectionOf(topIo) match {
case DirectionOf.Input =>
directionOf(topIo) match {
case directionOf.Input =>
topIo := portIo
case DirectionOf.Output =>
case directionOf.Output =>
portIo := topIo
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package ee.hrzn.chryse.platform.cxxrtl

import chisel3._
import chisel3.experimental.ExtModule
import circt.stage.ChiselStage
import ee.hrzn.chryse.chisel.BuilderContext
import ee.hrzn.chryse.chisel.DirectionOf
import ee.hrzn.chryse.chisel.specifiedDirectionOf

import java.io.Writer

Expand All @@ -14,52 +15,74 @@ class BlackBoxGenerator(private val wr: Writer) {
// https://github.com/chipsalliance/chisel/pull/4023#issuecomment-2130283723
// Note that annotations on ports are also very required, and not even in
// firtool yet.
//
// It'd still be nice to do it even if hackily — I'd like to support bundles
// and normal stuff like that, which right now will require quite a bit more
// fun. TODO: bundles, proper descent, flipping etc.

def runOn(bb: Class[_ <: BlackBox]): Unit = {
private var elIx = 0

def runOn(bb: Class[_ <: ExtModule]): Unit = {
wr.write("attribute \\cxxrtl_blackbox 1\n")
wr.write("attribute \\blackbox 1\n")
wr.write(s"module \\${bb.getSimpleName()}\n")

BuilderContext {
val inst = bb.getConstructor().newInstance()
val io =
bb.getDeclaredMethod("io").invoke(inst).asInstanceOf[Bundle]
var elIx = 0
for {
((name, dat)) <-
io.elements.toSeq.reverseIterator
} {
// TODO: "inout"
val dir = DirectionOf(dat)
dat match {
case vec: Vec[_] =>
for { (vecEl, vecElIx) <- vec.getElements.zipWithIndex } {
emitWire(s"${name}_$vecElIx", vecEl, dir, vecEl.getWidth, elIx)
elIx += 1
}
case _ =>
emitWire(name, dat, dir, dat.getWidth, elIx)
elIx += 1
}
}
buildFrom(bb.getConstructor().newInstance())
}

wr.write("end\n")
}

def emitWire(
private def buildFrom(inst: ExtModule): Unit = {
for { f <- inst.getClass().getDeclaredFields() } {
f.setAccessible(true)
handleElement(f.getName(), f.get(inst), SpecifiedDirection.Unspecified)
}
}

private def handleElement(
objName: String,
obj: Object,
dir: SpecifiedDirection,
): Unit = {
obj match {
case bundle: Bundle =>
val prefix = s"${objName}_"
val bundleDir =
SpecifiedDirection.fromParent(dir, specifiedDirectionOf(bundle))
for { (name, data) <- bundle.elements.toSeq.reverseIterator }
handleElement(s"$prefix$name", data, bundleDir)
case data: Data =>
val dataDir =
SpecifiedDirection.fromParent(dir, specifiedDirectionOf(data))
emitData(objName, data, dataDir)
case _ =>
}
}

private def emitData(
name: String,
data: Data,
dir: SpecifiedDirection,
): Unit = {
data match {
case vec: Vec[_] =>
for { (vecEl, vecElIx) <- vec.getElements.zipWithIndex } {
emitWire(s"${name}_$vecElIx", vecEl, dir, vecEl.getWidth, elIx)
elIx += 1
}
case _ =>
emitWire(name, data, dir, data.getWidth, elIx)
elIx += 1
}
}

private def emitWire(
name: String,
dat: Data,
data: Data,
dir: SpecifiedDirection,
width: Int,
elIx: Integer,
): Unit = {
if (elIx > 0) wr.write("\n")
if (dir == SpecifiedDirection.Input && dat.isInstanceOf[Clock]) {
if (dir == SpecifiedDirection.Input && data.isInstanceOf[Clock]) {
wr.write(" attribute \\cxxrtl_edge \"p\"\n")
} else if (dir == SpecifiedDirection.Output) {
// XXX: We're assuming this is a synchronous output, but who says it is?
Expand All @@ -74,6 +97,6 @@ class BlackBoxGenerator(private val wr: Writer) {
}

object BlackBoxGenerator {
def apply(wr: Writer, bb: Class[_ <: BlackBox]): Unit =
def apply(wr: Writer, bb: Class[_ <: ExtModule]): Unit =
new BlackBoxGenerator(wr).runOn(bb)
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package ee.hrzn.chryse.platform.cxxrtl

import chisel3.BlackBox

import scala.sys.process._
import chisel3.experimental.ExtModule

final case class CXXRTLOptions(
platforms: Seq[CXXRTLPlatform],
blackboxes: Seq[Class[_ <: BlackBox]] = Seq(),
blackboxes: Seq[Class[_ <: ExtModule]] = Seq(),
cxxFlags: Seq[String] = Seq(),
ldFlags: Seq[String] = Seq(),
pkgConfig: Seq[String] = Seq(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ package ee.hrzn.chryse.platform.cxxrtl
import chisel3._
import ee.hrzn.chryse.platform.ElaboratablePlatform

abstract case class CXXRTLPlatform(id: String, clockHz: Int)
extends ElaboratablePlatform {
abstract case class CXXRTLPlatform(id: String) extends ElaboratablePlatform {
override def apply[Top <: Module](genTop: => Top) =
genTop
}
14 changes: 7 additions & 7 deletions src/main/scala/ee/hrzn/chryse/platform/ice40/ICE40Top.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import chisel3.experimental.StringParam
import chisel3.experimental.noPrefix
import chisel3.util._
import chisel3.util.experimental.forceName
import ee.hrzn.chryse.chisel.DirectionOf
import ee.hrzn.chryse.chisel.directionOf
import ee.hrzn.chryse.platform.ChryseTop
import ee.hrzn.chryse.platform.PlatformBoard
import ee.hrzn.chryse.platform.PlatformBoardResources
Expand Down Expand Up @@ -47,9 +47,9 @@ class ICE40Top[Top <: Module](
val i_type = PinType.PIN_INPUT

// as above, PIN_OUTPUT_{REGISTERED,DDR}_ENABLE_REGISTERED
val o_type = DirectionOf(portIo) match {
case DirectionOf.Input => PinType.PIN_NO_OUTPUT
case DirectionOf.Output => PinType.PIN_OUTPUT_TRISTATE
val o_type = directionOf(portIo) match {
case directionOf.Input => PinType.PIN_NO_OUTPUT
case directionOf.Output => PinType.PIN_OUTPUT_TRISTATE
}

val buffer = Module(
Expand All @@ -63,13 +63,13 @@ class ICE40Top[Top <: Module](
),
).suggestName(s"${res.name.get}_SB_IO")

DirectionOf(portIo) match {
case DirectionOf.Input =>
directionOf(portIo) match {
case directionOf.Input =>
buffer.PACKAGE_PIN := portIo
topIo := buffer.D_IN_0
buffer.OUTPUT_ENABLE := DontCare
buffer.D_OUT_0 := DontCare
case DirectionOf.Output =>
case directionOf.Output =>
buffer.OUTPUT_ENABLE := true.B
if (portIo.isInstanceOf[Clock]) {
portIo := buffer.PACKAGE_PIN.asClock
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package ee.hrzn.chryse.platform.resource
import chisel3._
import chisel3.experimental.Param
import chisel3.experimental.dataview._
import ee.hrzn.chryse.chisel.DirectionOf
import ee.hrzn.chryse.chisel.directionOf

import scala.language.implicitConversions

Expand Down Expand Up @@ -61,10 +61,10 @@ abstract class ResourceData[HW <: Data](gen: => HW, invert: Boolean = false)
}

protected def connectIo(user: HW, top: HW): Unit = {
DirectionOf(top) match {
case DirectionOf.Input =>
directionOf(top) match {
case directionOf.Input =>
user := (if (!_invert) top else ~top.asInstanceOf[Bits])
case DirectionOf.Output =>
case directionOf.Output =>
top := (if (!_invert) user else ~user.asInstanceOf[Bits])
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ee.hrzn.chryse.platform.cxxrtl

import chisel3._
import chisel3.experimental.ExtModule
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should._

Expand Down Expand Up @@ -61,29 +62,54 @@ class BlackBoxGeneratorSpec extends AnyFlatSpec with Matchers {
| attribute \cxxrtl_sync 1
| wire output 3 width 8 \d_out_1
|end
""".stripMargin)
}

it should "expand handle bundles correctly" in {
println()
val sw = new StringWriter
BlackBoxGenerator(sw, classOf[BundleBB])
sw.toString() should be("""attribute \cxxrtl_blackbox 1
|attribute \blackbox 1
|module \BundleBB
| attribute \cxxrtl_edge "p"
| wire input 1 \clock
|
| attribute \cxxrtl_sync 1
| wire output 2 width 8 \io_tx
|
| wire input 3 width 8 \io_rx_bits
|
| wire input 4 \io_rx_err
|end
""".stripMargin)
}
}

private class UnaryBB extends BlackBox {
val io = IO(new Bundle {
val clock = Input(Clock())
private class UnaryBB extends ExtModule {
val clock = IO(Input(Clock()))

val ready = Input(Bool())
val valid = Output(Bool())
})
val ready = IO(Input(Bool()))
val valid = IO(Output(Bool()))
}

private class VecOfBoolBB extends BlackBox {
val io = IO(new Bundle {
val d_in = Input(Vec(3, Bool()))
val d_out = Output(Vec(2, Bool()))
})
private class VecOfBoolBB extends ExtModule {
val d_in = IO(Input(Vec(3, Bool())))
val d_out = IO(Output(Vec(2, Bool())))
}

private class WiderElementsBB extends ExtModule {
val d_in = IO(Input(UInt(64.W)))
val d_out = IO(Output(Vec(2, SInt(8.W))))
}

private class WiderElementsBB extends BlackBox {
private class BundleBB extends ExtModule {
val clock = IO(Input(Clock()))
val io = IO(new Bundle {
val d_in = Input(UInt(64.W))
val d_out = Output(Vec(2, SInt(8.W)))
val tx = Output(UInt(8.W))
val rx = Flipped(new Bundle {
val bits = Output(UInt(8.W))
val err = Output(Bool())
})
})
}

0 comments on commit 51db26e

Please sign in to comment.