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

Commit

Permalink
ecp5: very close.
Browse files Browse the repository at this point in the history
  • Loading branch information
kivikakk committed May 29, 2024
1 parent 871b49e commit c6e3e00
Show file tree
Hide file tree
Showing 19 changed files with 316 additions and 117 deletions.
41 changes: 28 additions & 13 deletions src/main/scala/ee/hrzn/chryse/platform/ChryseTop.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package ee.hrzn.chryse.platform

import chisel3._
import chisel3.experimental.Param
import chisel3.experimental.noPrefix
import ee.hrzn.chryse.chisel.directionOf
import ee.hrzn.chryse.platform.resource.ClockSource
import ee.hrzn.chryse.platform.resource.Pin
import ee.hrzn.chryse.platform.resource.ResourceData

import scala.collection.mutable
import scala.language.existentials
Expand All @@ -14,21 +17,23 @@ trait ChryseTop extends RawModule {

case class ConnectedResource(
pin: Pin,
frequencyHz: Option[Int],
attributes: Map[String, Param],
frequencyHz: Option[BigInt],
)

object ConnectedResource {
implicit def pin2Cr(pin: Pin): ConnectedResource =
ConnectedResource(pin, None)
}
sealed trait PlatformConnectResult
case class PlatformConnectResultUsePorts(topIo: Data, portIo: Data)
extends PlatformConnectResult
case object PlatformConnectResultFallthrough extends PlatformConnectResult
case object PlatformConnectResultNoop extends PlatformConnectResult

protected def platformConnect(
name: String,
res: resource.ResourceData[_ <: Data],
): Option[(Data, Data)] = None
res: ResourceData[_ <: Data],
): PlatformConnectResult = PlatformConnectResultFallthrough

protected def platformPort[HW <: Data](
res: resource.ResourceData[HW],
res: ResourceData[HW],
topIo: Data,
portIo: Data,
): Unit = {
Expand All @@ -49,7 +54,7 @@ trait ChryseTop extends RawModule {
for { res <- platform.resources.all } {
val name = res.name.get
res match {
case res: resource.ClockSource =>
case res: ClockSource =>
if (res.ioInst.isDefined) {
throw new Exception(
"clock sources must be manually handled for now",
Expand All @@ -60,21 +65,31 @@ trait ChryseTop extends RawModule {
// can't then sink like we would in the resource.Base[_] case.
connected += name -> ConnectedResource(
res.pinId.get,
res.attributes,
Some(platform.clockHz),
)
clock.get := noPrefix(IO(Input(Clock())).suggestName(name))

case _ =>
platformConnect(name, res) match {
case Some((topIo, portIo)) =>
connected += name -> res.pinId.get
case PlatformConnectResultUsePorts(topIo, portIo) =>
connected += name -> ConnectedResource(
res.pinId.get,
res.attributes,
None,
)
platformPort(res, topIo, portIo)
case None =>
case PlatformConnectResultFallthrough =>
if (res.ioInst.isDefined) {
connected += name -> res.pinId.get
connected += name -> ConnectedResource(
res.pinId.get,
res.attributes,
None,
)
val (topIo, portIo) = res.makeIoConnection()
platformPort(res, topIo, portIo)
}
case PlatformConnectResultNoop =>
}
}
}
Expand Down
6 changes: 4 additions & 2 deletions src/main/scala/ee/hrzn/chryse/platform/PlatformBoard.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ import ee.hrzn.chryse.ChryseApp

trait PlatformBoard[PBR <: PlatformBoardResources]
extends ElaboratablePlatform {
type BuildResult

def yosysSynthCommand(top: String): String

def build(
chryse: ChryseApp,
topPlatform: TopPlatform[_],
jsonPath: String,
): String
): BuildResult

def program(binPath: String): Unit
def program(buildResult: BuildResult): Unit

val resources: PBR
}
60 changes: 50 additions & 10 deletions src/main/scala/ee/hrzn/chryse/platform/ecp5/ECP5Platform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ import ee.hrzn.chryse.tasks.BaseTask

trait ECP5Platform { this: PlatformBoard[_ <: PlatformBoardResources] =>
type TopPlatform[Top <: Module] = ECP5Top[Top]
case class BuildResult(bitPath: String, svfPath: String)

val ecp5Variant: ECP5Variant
val ecp5Package: String
val ecp5Speed: Int

override def apply[Top <: Module](genTop: => Top) = {
resources.setNames()
Expand All @@ -23,7 +25,7 @@ trait ECP5Platform { this: PlatformBoard[_ <: PlatformBoardResources] =>
chryse: ChryseApp,
topPlatform: ECP5Top[_],
jsonPath: String,
): String =
): BuildResult =
buildImpl(this, chryse, topPlatform, jsonPath)

private object buildImpl extends BaseTask {
Expand All @@ -32,18 +34,56 @@ trait ECP5Platform { this: PlatformBoard[_ <: PlatformBoardResources] =>
chryse: ChryseApp,
topPlatform: ECP5Top[_],
jsonPath: String,
): String = ???
): BuildResult = {
val name = chryse.name

val lpfPath = s"$buildDir/${platform.id}/$name.lpf"
writePath(lpfPath, topPlatform.lpf.toString())

val textcfgPath = s"$buildDir/${platform.id}/$name.config"
val textcfgCu = CompilationUnit(
Some(jsonPath),
Seq(lpfPath),
textcfgPath,
Seq(
"nextpnr-ecp5",
"-q",
"--log",
s"$buildDir/${platform.id}/$name.tim",
"--json",
jsonPath,
"--lpf",
lpfPath,
"--textcfg",
textcfgPath,
ecp5Variant.arg,
"--package",
ecp5Package,
"--speed",
s"$ecp5Speed",
),
)
runCu(CmdStepPNR, textcfgCu)

val bitPath = s"$buildDir/${platform.id}/$name.bit"
val svfPath = s"$buildDir/${platform.id}/$name.svf"
val bitCu = CompilationUnit(
Some(textcfgPath),
Seq(),
bitPath,
Seq("ecppack", "--input", textcfgPath, "--bit", bitPath, "--svf",
svfPath),
)
runCu(CmdStepPack, bitCu)

BuildResult(bitPath, svfPath)
}
}

def program(binPath: String): Unit =
programImpl(binPath)
def program(bitAndSvf: BuildResult): Unit =
programImpl(bitAndSvf)

private object programImpl extends BaseTask {
def apply(binPath: String): Unit = ???
def apply(bitAndSvf: BuildResult): Unit = ???
}

val nextpnrBinary = "nextpnr-ecp5"
lazy val nextpnrArgs = Seq(ecp5Variant.arg, "--package", ecp5Package)
val packBinary = "ecppack"
val programBinary = "dfu-util"
}
29 changes: 29 additions & 0 deletions src/main/scala/ee/hrzn/chryse/platform/ecp5/ECP5Top.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,23 @@ import ee.hrzn.chryse.platform.ChryseTop
import ee.hrzn.chryse.platform.Platform
import ee.hrzn.chryse.platform.PlatformBoard
import ee.hrzn.chryse.platform.PlatformBoardResources
import ee.hrzn.chryse.platform.resource.PinString
import ee.hrzn.chryse.platform.resource.ResourceData

class ECP5Top[Top <: Module](
platform: PlatformBoard[_ <: PlatformBoardResources],
genTop: => Top,
) extends RawModule
with ChryseTop {
override protected def platformConnect(
name: String,
res: ResourceData[_ <: Data],
): PlatformConnectResult = {
println(s"evaluating: $name / $res")
// TODO (ECP5): USRMCLK
PlatformConnectResultFallthrough
}

private val clki = Wire(Clock())

private val gsr0 = Wire(Bool())
Expand All @@ -32,5 +43,23 @@ class ECP5Top[Top <: Module](
private val top =
withClockAndReset(clki, false.B)(Module(genTop))

// TODO (ECP5): allow clock source override.

val connectedResources = connectResources(platform, Some(clki))

val lpf = LPF(
connectedResources
.flatMap { case (name, cr) =>
cr.pin match {
case pin: PinString =>
Some((name, (pin, cr.attributes)))
case _ =>
None
}
}
.to(Map),
connectedResources
.flatMap { case (name, cr) => cr.frequencyHz.map((name, _)) }
.to(Map),
)
}
39 changes: 39 additions & 0 deletions src/main/scala/ee/hrzn/chryse/platform/ecp5/LPF.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package ee.hrzn.chryse.platform.ecp5

import chisel3.experimental.IntParam
import chisel3.experimental.Param
import chisel3.experimental.StringParam
import ee.hrzn.chryse.platform.resource.PinString

final case class LPF(
ios: Map[String, (PinString, Map[String, Param])],
freqs: Map[String, BigInt],
) {
private val attr2String: Param => String = {
case IntParam(v) =>
v.toString()
case StringParam(v) => v
case v => throw new Exception(s"unhandled attribute: $v")
}

override def toString(): String = {
val sb = new StringBuilder
sb.append("BLOCK ASYNCPATHS;\n")
sb.append("BLOCK RESETPATHS;\n")

for { (name, (pin, attrs)) <- ios } {
sb.append(s"LOCATE COMP \"$name\" SITE \"$pin\";\n")
if (!attrs.isEmpty) {
sb.append(s"IOBUF PORT \"$name\"");
for { (k, v) <- attrs }
sb.append(s" $k=${attr2String(v)}")
sb.append(";\n");
}
}

for { (name, freq) <- freqs }
sb.append(s"FREQUENCY PORT \"$name\" $freq HZ;\n")

sb.toString()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package ee.hrzn.chryse.platform.ecp5
import chisel3._
import ee.hrzn.chryse.platform.PlatformBoard
import ee.hrzn.chryse.platform.PlatformBoardResources
import ee.hrzn.chryse.platform.resource
import ee.hrzn.chryse.platform.resource.ClockSource

// TODO: restrict the variants to those the OrangeCrab was delivered with.
case class OrangeCrabPlatform(ecp5Variant: ECP5Variant)
Expand All @@ -13,10 +13,11 @@ case class OrangeCrabPlatform(ecp5Variant: ECP5Variant)
val clockHz = 48_000_000

val ecp5Package = "csfBGA285"
val ecp5Speed = 8

val resources = new OrangeCrabPlatformResources
}

class OrangeCrabPlatformResources extends PlatformBoardResources {
val clock = resource.ClockSource(48_000_000).onPin("A9")
val clock = ClockSource(48_000_000).onPin("A9")
}
23 changes: 12 additions & 11 deletions src/main/scala/ee/hrzn/chryse/platform/ecp5/ULX3SPlatform.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import chisel3._
import chisel3.experimental.Param
import ee.hrzn.chryse.platform.PlatformBoard
import ee.hrzn.chryse.platform.PlatformBoardResources
import ee.hrzn.chryse.platform.resource
import ee.hrzn.chryse.platform.resource.Button
import ee.hrzn.chryse.platform.resource.ClockSource
import ee.hrzn.chryse.platform.resource.ResourceData
import ee.hrzn.chryse.platform.resource.SPIFlash
import ee.hrzn.chryse.platform.resource.UART

// TODO: restrict the variants to those the ULX3S was delivered with.
// TODO: try one of these: https://github.com/emard/ulx3s/blob/master/doc/MANUAL.md#programming-over-wifi-esp32-micropython
Expand All @@ -14,35 +18,32 @@ case class ULX3SPlatform(ecp5Variant: ECP5Variant)
val id = s"ulx3s-${ecp5Variant.id}"
val clockHz = 25_000_000

val ecp5Package = "caBGA381"
val ecp5Package = "CABGA381"
val ecp5Speed = 6

val resources = new ULX3SPlatformResources
}

class ULX3SPlatformResources extends PlatformBoardResources {
override val defaultAttributes = Map("IO_TYPE" -> IOType.LVCMOS33)

val clock = resource.ClockSource(25_000_000).onPin("G2")
val clock = ClockSource(25_000_000).onPin("G2")

val program =
resource.Button().inverted.onPin("M4").withAttributes("PULLMODE" -> "UP")
Button().inverted.onPin("M4").withAttributes("PULLMODE" -> "UP")

// TODO: also expose RTS, DTR.
var uart = resource
.UART()
var uart = UART()
.onPins(rx = "M1", tx = "L4")
var uartTxEnable = new resource.ResourceData[Bool](Bool()) {
name = Some("uartTxEnable")
}
var uartTxEnable = ResourceData(Output(Bool())).onPin("L3")

// val leds =
// resource
// .LEDs()
// .onPins("B2", "C2", "C1", "D2", "D1", "E2", "E1", "H3")
// .withAttributes("DRIVE" -> "4")

val spiFlash = resource
.SPIFlash()
val spiFlash = SPIFlash()
.onPins(
csN = "R2", clock = USRMCLK, copi = "W2", cipo = "V2", wpN = "Y2",
holdN = "W1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import ee.hrzn.chryse.tasks.BaseTask

trait ICE40Platform { this: PlatformBoard[_ <: PlatformBoardResources] =>
type TopPlatform[Top <: Module] = ICE40Top[Top]
type BuildResult = String

val ice40Variant: ICE40Variant
val ice40Package: String
Expand Down Expand Up @@ -36,7 +37,7 @@ trait ICE40Platform { this: PlatformBoard[_ <: PlatformBoardResources] =>
val name = chryse.name

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

val ascPath = s"$buildDir/${platform.id}/$name.asc"
val ascCu = CompilationUnit(
Expand Down
Loading

0 comments on commit c6e3e00

Please sign in to comment.