hny-2026/build.mill
Nikolay Puzanov 4e01d48e7f Fix compatibility with OSS-CAD-SUITE
Rework the way the Yosys slang plugin is loaded in `build.mill`. The environment variable
`YOSYS_SLANG_SO` is now accessed with `sys.env.get` to avoid a hard failure when it is not set. If
the variable is present, `-m $YOSYS_SLANG_SO` is passed to Yosys; otherwise the plugin is loaded via
`plugin -i slang`.
2026-01-07 17:26:04 +03:00

235 lines
4.9 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package build
import mill._
import mill.scalalib._
import mill.javalib._
import java.io.File
import scala.sys.process._
import mill.api.Task.Simple
object hny2026 extends ScalaModule { module =>
def scalaVersion = "2.13.18"
val chiselVersion = "7.6.0"
val scalaTestVersion = "3.2.19"
val main = "hny2026.HNY2026"
def scalacOptions = Seq(
"-language:reflectiveCalls",
"-language:implicitConversions",
"-deprecation"
)
def mvnDeps = Seq(
mvn"org.chipsalliance::chisel:$chiselVersion",
)
def scalacPluginMvnDeps = Seq(
mvn"org.chipsalliance:::chisel-plugin:$chiselVersion",
)
object test extends ScalaTests with TestModule.ScalaTest {
def mvnDeps = module.mvnDeps() ++ Seq(
mvn"org.scalatest::scalatest::${module.scalaTestVersion}"
)
}
}
/**
* Common build flow tasks.
*/
trait Flow extends Module {
/**
* Optional top selection.
*/
def top: Option[String] = None
/**
* Clock frequency in Hz.
*/
def clockFrequency: Int
/**
* Override example:
*
* ```
* override def staticSrc = Some(Task.Sources("src0.v", "src1.v", ...))
* ```
*/
def staticSrc: Option[Simple[Seq[PathRef]]] = None
/**
* Build Chisel project.
*
* @return list of generated SV sources.
*/
def generate = Task {
hny2026.runner().run(Seq(s"clockFreq=$clockFrequency"), hny2026.main)
File(Task.dest.toURI).listFiles{
(_, fileName) => fileName.endsWith((".sv"))
}.map(f => PathRef(os.Path(f)))
}
}
/**
* Gowin flow with Yosys and Nextpnr.
*/
trait GowinFlow extends Flow {
def family: String
def device: String
/**
* Overriding example:
*
* ```
* override def cstFile = Task.Source("resources/hny2026.cst")
* ```
*/
def cstFile: Simple[PathRef]
/**
* Synth with yosys.
*
* @return path to json netlist.
*/
def synth = Task {
val out = Task.dest
val synthJson = out / "synth.json"
val genSrc = generate().map(_.path).mkString(" ")
val stSrc = if (staticSrc.isDefined) {
(staticSrc.get)().map(_.path).mkString(" ")
} else ""
val topCmd = top match {
case Some(t) => s"-top $t"
case _ => ""
}
val yosysSlangPluginSo = sys.env.get("YOSYS_SLANG_SO")
val (pluginArg, pluginCmd) = yosysSlangPluginSo match {
case Some(soName) => (Seq("-m", soName), "")
case _ => (Seq(), "plugin -i slang;")
}
os.call((
"yosys", pluginArg, "-l", s"$out/yosys.log", "-p",
s"$pluginCmd read_slang $genSrc $stSrc; synth_gowin $topCmd -nowidelut -json $synthJson"
))
PathRef(synthJson)
}
/**
* Place and route with nextpnr.
*
* @return path to PnR json.
*/
def pnr = Task {
val out = Task.dest
val pnrJson = out / "placenroute.json"
val synthJson = synth().path
val clockMhz = (clockFrequency.toDouble / 1000000).floor.toInt
os.call((
"nextpnr-himbaechel",
"--json", synthJson,
"--write", pnrJson,
"--freq", s"$clockMhz",
"--device", device,
"--vopt", s"family=$family",
"--vopt", s"cst=${cstFile().path}",
"-l", s"$out/nextpnr.log"
))
PathRef(pnrJson)
}
/**
* Build bitstream.
*
* @return path to bitstream file.
*/
def bitstream = Task {
val out = Task.dest
val bs = out / "bitstream.fs"
val pnrJson = pnr().path
os.call(("gowin_pack", "-d", device, "-o", bs, pnrJson))
PathRef(bs)
}
/**
* Load bitstream into FPGA SRAM.
*/
def load(args: String*) = Task.Command {
val bs = bitstream().path
os.call(
cmd = ("openFPGALoader", "-c", "ft2232", "-m", bs),
stdout = os.Inherit
)
}
/**
* Birn FPGA flash with bitstream.
*/
def burn(args: String*) = Task.Command {
val bs = bitstream().path
os.call(
cmd = ("openFPGALoader", "-c", "ft2232", "--unprotect-flash", "-f", bs),
stdout = os.Inherit
)
}
}
/**
* TangNano 1K target.
*
* Build command.
*
* Generate SystemVerilog files from Chisel source:
* ```
* $ mill tangNano1k.generate
* ```
*
* Synthesize the source code using Yosys:
* ```
* $ mill tangNano1k.synth
* ```
*
* Place and route using Nextpnr:
* ```
* $ mill tangNano1k.pnr
* ```
*
* Build bitstream:
* ```
* $ mill tangNano1k.bitstream
* ```
*
* Load bitstream into FPGA's SRAM:
* ```
* $ mill tangNano1k.load
* ```
*
* Burn FPGA flash with a bistream:
* ```
* $ mill tangNano1k.burn
* ```
*
* Each subsequent command automatically calls the previous one, so when 'burn' is invoked, all
* required steps will be performed SV generation, synthesis, PnR, and bitstream assembly.
*/
object tangNano1k extends Module with GowinFlow {
def top = Some("hny2026_top")
def family = "GW1NZ-1"
def device = "GW1NZ-LV1QN48C6/I5"
def clockFrequency = 27000000
def staticSrc = Some(Task.Sources("verilog/hny2026_top.v"))
def cstFile = Task.Source("resources/hny2026.cst")
}