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

Commit

Permalink
generate PCFs in place.
Browse files Browse the repository at this point in the history
  • Loading branch information
kivikakk committed May 21, 2024
1 parent bdc99f6 commit 44cf001
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 33 deletions.
2 changes: 1 addition & 1 deletion src/main/scala/ee/hrzn/chryse/ChryseModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ package ee.hrzn.chryse
import chisel3._

class ChryseModule extends RawModule {
var lastPCF: Option[String] = None
override def desiredName = "chrysetop"
}
2 changes: 0 additions & 2 deletions src/main/scala/ee/hrzn/chryse/platform/GenericTop.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import ee.hrzn.chryse.platform.Platform

class GenericTop[Top <: Module](platform: Platform, genTop: => Top)
extends ChryseModule {
override def desiredName = "top"

val clock = IO(Input(Clock()))
val reset = IO(Input(Bool()))

Expand Down
6 changes: 2 additions & 4 deletions src/main/scala/ee/hrzn/chryse/platform/ecp5/ECP5Top.scala
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package ee.hrzn.chryse.platform.ecp5

import chisel3._
import ee.hrzn.chryse.platform.Platform
import ee.hrzn.chryse.ChryseModule
import ee.hrzn.chryse.platform.BoardResources
import ee.hrzn.chryse.platform.BoardPlatform
import ee.hrzn.chryse.platform.BoardResources
import ee.hrzn.chryse.platform.Platform

class ECP5Top[Top <: Module](platform: Platform, genTop: => Top)
extends ChryseModule {
override def desiredName = "top"

private val clki = IO(Input(Clock()))

// TODO: GSR stuff. (details follow.)
Expand Down
23 changes: 14 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 @@ -13,14 +13,15 @@ import ee.hrzn.chryse.platform.Platform
import ee.hrzn.chryse.platform.resource

import java.lang.reflect.Modifier
import scala.collection.mutable

class ICE40Top[Top <: Module](
platform: BoardPlatform[_ <: BoardResources],
genTop: => Top,
) extends ChryseModule {
override def desiredName = "ice40top"
var lastPCF: Option[PCF] = None

val clki = Wire(Clock())
private val clki = Wire(Clock())

private val clk_gb = Module(new SB_GB)
clk_gb.USER_SIGNAL_TO_GLOBAL_BUFFER := clki
Expand All @@ -38,7 +39,7 @@ class ICE40Top[Top <: Module](
resetTimerReg := resetTimerReg + 1.U
}

val finalReset = noPrefix {
private val finalReset = noPrefix {
// TODO: this no longer works. :)
if (platform.asInstanceOf[IceBreakerPlatform].ubtnReset) {
val io_ubtn = IO(Input(Bool()))
Expand All @@ -52,8 +53,12 @@ class ICE40Top[Top <: Module](
withClockAndReset(clk, finalReset)(Module(genTop))

// TODO: allow clock override.
// TODO: refactor this out to a non-ICE40Top level.
val sb = new StringBuilder

// 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)
Expand All @@ -65,13 +70,14 @@ class ICE40Top[Top <: Module](
// 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
// can't then sink like we would in the resource.Base[_] case.
sb.append(s"set_io $name ${clock.pinId.get}\n")
ios += name -> clock.pinId.get
freqs += name -> platform.clockHz
val io = IO(Input(Clock())).suggestName(name)
clki := io

case res: resource.Base[_] =>
if (res.ioInst.isDefined) {
sb.append(s"set_io $name ${res.pinId.get}\n")
ios += name -> res.pinId.get
val io = IO(res.makeIo()).suggestName(name)
DirectionOf(io) match {
case SpecifiedDirection.Input =>
Expand All @@ -84,10 +90,9 @@ class ICE40Top[Top <: Module](
}
case _ =>
}

lastPCF = Some(sb.toString())
}

lastPCF = Some(PCF(ios.iterator.to(Map), freqs.iterator.to(Map)))
}

object ICE40Top {
Expand Down
24 changes: 24 additions & 0 deletions src/main/scala/ee/hrzn/chryse/platform/ice40/PCF.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ee.hrzn.chryse.platform.ice40

import ee.hrzn.chryse.platform.resource.Pin

case class PCF(ios: Map[String, Pin], freqs: Map[String, Int]) {
for { name <- freqs.keysIterator }
if (!ios.isDefinedAt(name))
throw new IllegalArgumentException(
s"frequency $name doesn't have corresponding io",
)

override def toString(): String = {
val sb = new StringBuilder
for { (name, pin) <- ios } {
sb.append(s"set_io $name $pin\n")
freqs
.get(name)
.foreach { freq =>
sb.append(s"set_frequency $name ${freq.toDouble / 1_000_000.0}\n")
}
}
sb.toString()
}
}
15 changes: 10 additions & 5 deletions src/main/scala/ee/hrzn/chryse/tasks/BuildTask.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import chisel3._
import circt.stage.ChiselStage
import ee.hrzn.chryse.platform.BoardPlatform
import ee.hrzn.chryse.platform.Platform
import ee.hrzn.chryse.platform.ice40.ICE40Top
import ee.hrzn.chryse.platform.ice40.PCF

import java.nio.file.Files
import java.nio.file.Paths
Expand All @@ -22,13 +24,16 @@ object BuildTask extends BaseTask {

Files.createDirectories(Paths.get(buildDir))

val verilogPath = s"$buildDir/$name-${platform.id}.sv"
var lastPCF: Option[String] = None
val verilogPath = s"$buildDir/$name-${platform.id}.sv"
var lastPCF: Option[PCF] = None
val verilog =
ChiselStage.emitSystemVerilog(
{
val elaborated = platform(genTop)
lastPCF = elaborated.lastPCF
lastPCF = elaborated match {
case ice40: ICE40Top[_] => ice40.lastPCF
case _ => None
}
elaborated
},
if (fullStacktrace) Array("--full-stacktrace") else Array.empty,
Expand All @@ -45,7 +50,7 @@ object BuildTask extends BaseTask {
writePath(
yosysScriptPath,
s"""read_verilog -sv $verilogPath
|synth_ice40 -top top
|synth_ice40 -top chrysetop
|write_json $jsonPath""".stripMargin,
)

Expand All @@ -66,7 +71,7 @@ object BuildTask extends BaseTask {
runCu("synthesis", yosysCu)

val pcfPath = s"$buildDir/$name-${platform.id}.pcf"
writePath(pcfPath, lastPCF.get)
writePath(pcfPath, lastPCF.get.toString())

val ascPath = s"$buildDir/$name-${platform.id}.asc"
val ascCu = CompilationUnit(
Expand Down
40 changes: 28 additions & 12 deletions src/test/scala/ee/hrzn/chryse/platform/BoardResourcesSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import ee.hrzn.chryse.ChryseModule
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 org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should._
Expand All @@ -18,12 +19,19 @@ class BoardResourcesSpec extends AnyFlatSpec with Matchers {
val top = BuilderContext {
plat(new DetectionTop(_))
}
top.lastPCF.get.linesIterator.toList.sorted.mkString("\n") should be(
"""set_io clock 35
|set_io ledg 37
|set_io uart_rx 6
|set_io uart_tx 9
|set_io ubtn 10""".stripMargin,
top.lastPCF should be(
Some(
PCF(
Map(
"clock" -> 35,
"ledg" -> 37,
"uart_rx" -> 6,
"uart_tx" -> 9,
"ubtn" -> 10,
),
Map("clock" -> 12_000_000),
),
),
)
}

Expand All @@ -37,20 +45,28 @@ class BoardResourcesSpec extends AnyFlatSpec with Matchers {
},
firtoolOpts = Array("-strip-debug-info"),
)
top.lastPCF.get.linesIterator.toList.sorted.mkString("\n") should be(
"""set_io clock 35
|set_io ledg 37
|set_io uart_tx 9
|set_io ubtn 10""".stripMargin,
top.lastPCF should be(
Some(
PCF(
Map(
"clock" -> 35,
"ledg" -> 37,
"uart_tx" -> 9,
"ubtn" -> 10,
),
Map("clock" -> 12_000_000),
),
),
)

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.
"\\s+".r
.replaceAllIn(rtl, " ") should include(
"module ice40top( input clock, ubtn, output uart_tx, ledg );",
"module chrysetop( input clock, ubtn, output uart_tx, ledg );",
)
}
}
Expand Down
36 changes: 36 additions & 0 deletions src/test/scala/ee/hrzn/chryse/platform/ice40/PCFSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package ee.hrzn.chryse.platform.ice40

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

import java.io.StringWriter

class PCFSpec extends AnyFlatSpec with Matchers {
behavior.of("PCF")

it should "format IOs correctly" in {
PCF(Map("abc" -> 12, "xy" -> "A9"), Map()).toString() should be(
"""set_io abc 12
|set_io xy A9
|""".stripMargin,
)
}

it should "format attached frequencies correctly" in {
PCF(Map("abc" -> 12, "xy" -> "A9"), Map("xy" -> 120_000_000))
.toString() should be(
"""set_io abc 12
|set_io xy A9
|set_frequency xy 120.0
|""".stripMargin,
)
}

it should "detect unattached frequencies" in {
an[IllegalArgumentException] should be thrownBy PCF(
Map("abc" -> 12),
Map("xy" -> 100),
)
}
}

0 comments on commit 44cf001

Please sign in to comment.