Hello FPGAmigos ! Today, I have an upgrade on my previous article titled From HDL to bitstream with open-source toolchain. In that article, we created a project template that enabled us to go from Verilog design to bitstream, using Yosys for synthesis, NextPnR for place and route, and Icestorm for bitstream generation. I recently came across a new tool called Chisel, a new HDL, and I wanted to give it a try. Let’s add it to our workflow and replace Verilog with it.
Chisel is a modern Hardware Description Language (HDL) for FPGA and ASIC. It is an Embedded Domain Specific Language (EDSL). Chisel is written in another language, specifically Scala, which is a General-Purpose Programming Language (GPL). Unlike GPL languages like Python or C++, which can be used to accomplish a variety of tasks, Chisel is highly specific, meaning that it excels at designing hardware but cannot be used for other purposes.
When creating a DSL for a specific niche domain, it may not keep up with the latest features or have adequate community support. By embedding a specific language, however, you can leverage the advanced features and large community of that language. This is particularly relevant for hardware design, a niche domain that benefits greatly from a specialized language like Chisel. The integration of Scala into Chisel provides a powerful tool for hardware design that is more efficient and effective than using a more general-purpose programming language or standard HDL like VHDL or Verilog.
This article is not a introduction to Chisel (which is clearly on the road map). If you can’t wait I highly recommend you the Chisel Book written by Martin Schoeberl.
Prerequisites
This article is based on a previous article From HDL to bitstream with open-source toolchain.
To begin this project, you can access the project template on my Github page on the chisel branch.
Once you have completed the initial steps, you will need to obtain a working Docker image of Chisel. You can find a dockerfile for this image on my Github here. This will enable you to continue with your FPGA project using Chisel as your hardware description language.
Chisel to bitstream: the project
Now the project tree look like this:
Makefile
src:
chisel:
- main.scala
- build.sbt
synth:
- synth.ys
pnr:
- pnr.sh
bitgen:
- bitgen.sh
load:
- load.sh
constraints:
- alchitry.pcf
build:
artifacts:
netlist:
syn:
pnr:
bitstream:
logs:
MakefileI removed the HDL folder from the “src” directory and replaced it with the chisel folder. The chisel folder includes two important files:
– “main.scala” which contains the code for the top level of the design.
“- “build.sbt” which is the build manager for Scala.
Let’s inpect them:
// As an EDSL Chisel is a module to import in scala code
import chisel3._
// Modules are classes, which inherit from the chisel Module class
class AlchitryCUTop extends Module {
// IOs are the port of your module
// Clock and reset are implicit in chisel so you don't need to declare them
// Here we only declare the output led (io_led in the generated verilog file)
val io = IO(new Bundle{
val led = Output(UInt(1.W))
})
// Since the alchitry CU board has an active low reset
// we have to make it explicit here and invert it
val reset_n = !reset.asBool
// withReset explicit the reset used. For this part of code it is reset_n
withReset(reset_n){
// The module core: declare a reg initialize with 0 with constant input of 1
val my_reg = RegInit(0.U(1.W))
my_reg := 1.U
// connect the reg to the output
io.led := my_reg
}
}
// the main in scala
object Main extends App{
// emitverilog generate the verilog of the AlchitryCUTop.
// --target-dir is the option to choose where the generated files are placed
(new chisel3.stage.ChiselStage).emitVerilog(new AlchitryCUTop, Array("--target-dir", "build/artifacts/netlist/"))
}
SBT is the building tool for scala. Which i don’t realy understand yet… but the build.sbt works !
Let’s add a command to the makefile:
netlist: src/chisel/main.scala
docker run -v $(PWD)/src/chisel/build.sbt:/project/build.sbt \
-v $(PWD)/src/chisel/main.scala:/project/src/main/scala/main.scala \
-v $(PWD)/build/artifacts/netlist:/project/build/artifacts/netlist \
-v $(PWD)/build/logs/:/project/build/logs \
-w="/project/" \
chisel /bin/bash -c "source '../root/.sdkman/bin/sdkman-init.sh'; sbt run > ./build/logs/netlist_log.txt 2>&1"
The “netlist” command depends on main.scala file, which contains your design. After that, you can run your Chisel image using Docker. However, since the Scala tree project differs from the one we are using, we need to create several volumes to ensure that the file is placed in the correct folder within our project and the project folder inside the container.
#the scala project tree
src:
main:
scala:
- main.scala
build.sbt
To begin, the entry point “source ‘../root/.sdkman/bin/sdkman-init.sh’;” loads function, variable, and configuration files into the current shell. Running “sbt run” alone will result in Bash reporting that it doesn’t recognize the sbt command.
Once “sbt run” is executed, it runs the scala program written in Chisel, which generates the RTL netlist in the form of a Verilog file, AlchitryCUTop.v. The module is simple enough that you should be able to understand the code by opening the generated Verilog file. However, for larger designs, understanding it might be challenging. Nevertheless, it’s like assembly for software engineers, and you should grasp it if you need to debug a small piece of code.
And as always “> ./build/logs/netlist_log.txt 2>&1” to redirect bash prompt into a log file.
Run:
make netlist
You should find the generated file in the build/artifacts/netlist folder.
Then we have to modify the synthesis script and the make command:
read -sv build/artifacts/netlist/AlchitryCUTop.v
hierarchy -top AlchitryCUTop
proc; opt; techmap; opt
synth_ice40 -top AlchitryCUTop -json build/artifacts/syn/synth.json
I’ve just changed top into AlthitryCUTop with the new path.
The makefile command now depends on the arctifact AlchitryCUTop.v:
synthesis: build/artifacts/netlist/AlchitryCUTop.v
docker run -v $(PWD):/project/ -w="/project/" yosys /bin/bash -c "yosys ./src/synth/synth.ys > ./build/logs/syn_log.txt 2>&1"
For the Place and Route just check that your IOs have the correct names in the src/constraints/alchitry.pcf. Check the IO name in the AlchitryCUTop.v file and the pin name in your board documentation:
set_io clock P7
set_io reset P8
set_io io_led E1
The final touch is to make change to the makefile:
# ADD clean and netlist command
all: clean build_tree netlist synthesis pnr bitstream load
build_tree:
mkdir build
mkdir build/artifacts
mkdir build/artifacts/syn/
mkdir build/artifacts/pnr/
mkdir build/artifacts/bitstream/
# ADD NETLIST FOLDER
mkdir build/artifacts/netlist/
mkdir build/logs/
Try it with:
make all
if you board is connected the LED should turn ON. And turn OFF while you press the reset button.
Conclusion
Now you are ready to code with a modern HDL language, find a job, get a wife and a house, have a lot of children and live an happy life. Thank me later 😉
Thank you for reading. If you liked, please share, comment, bookmark ! You can now subscribe to the newsletter down below !