You have written some hardware design using amaranth-hdl, that you wish to promote into a library in order to reuse it in another project ?
You also have written some tests that use SymbiYosys(sby) to validate this library in part or in full ?
This cookbook will describe the minimal settings to run those test in a github action in order to validate a commit. It will also describe what to install locally and a way to write tests. It is based on this commit from a project of mine to gather some reusable stuff for my own projects.
Target audience
This cookbook will be easier to understand provided :
The reader already has set up github actions preferably for a python project.
The key point is the list of dependencies. In order to use up to dates versions of amaranth, I instruct the setup tools to use a recent commit on the main branch. The targeted commit hashes will have to be updated with time.
First thing first, I instructed git to ignore any file named tmp or like tmp.*. Then, my tests generate RTLIL files and their companion SBY files with names starting with tmp. This is to make sure that those artefacts will not end up in the repository.
This part will show :
The instanciation of a simple test bench
The generation of the RTLIL from the test bench
The generation of the SBY configuration file for the test bench
The invocation of sby
Instanciate a simple test bench
Embed an instance of you Elaboratable module as a submodule of your test bench. For my project, I wrote a little helper to avoid repeating myself
class Test:
@staticmethod
def _buildTestBench(dut: Elaboratable, test) -> Module:
"""
dut : 'Device Under Test'
test : a function that will add assertions to the test bench
"""
m = Module()
cd = Test._clockDomain("sync")
m.domains.sync = cd
m.submodules.dut = dut
test(m, cd)
return m
Add assertions to your test bench ; For my project, I devised for a function provided with the test bench and the clock domain, in order to add those assertions ; then, this function is given to the helper presented at step 1
def shouldAssertTheCorrectBitWhenInputIsInRange(m: Module, cd: ClockDomain):
rst = cd.rst
demux = m.submodules.dut
channelCount = demux.channelCount
for i in range(0, channelCount):
with m.If(~Past(rst) & (Past(demux.input) == i)):
m.d.sync += [
Assert(demux.output == (1 << i)),
Assert(~(demux.outOfRange)),
]
Generate the RTLIL file
Given a base name, I will instruct amaranth.back.rtill to generate the specified RTLIL file with :
ilName = f"tmp.{baseName}.il"
m = Test._buildTestBench(dut, test)
fragment = Fragment.get(m, platform)
output = rtlil.convert(
fragment,
ports=dut.ports(),
)
print(f"Generating {ilName}...")
with open(ilName, "wt") as f:
f.write(output)
Generate the SBY configuration file
Each test will require a SBY configuration file in order to target the generated RTLIL. For my project, I wrote a little helper that generate the file from the target RTLIL file and the required depth ; then I use this helper
For now I rely on amaranth._toolchain.require_tool to mimic what is done for the amaranth project, it will be necessary for the github action.
In a subprocess, we launch a simple invocation like sby -f <the-sby-configuration-file>, and we check the return code for detecting failure.
invoke_args = [require_tool("sby"), "-f", sbyName]
print(f"Running sby -f {' '.join(invoke_args)}...")
with subprocess.Popen(invoke_args) as proc:
if proc.returncode is not None and proc.returncode != 0:
exit(proc.returncode)
Testing locally
I wrote a little script to automate this part, because I includes reformating the sources, packaging and re-installing locally the project, and then launching the tests and compute coverages
# rebuild, install and test
python3 -m build && python3 -m pip install --force-reinstall dist/*.whl && \
python3 -m coverage run --source=amaranth_stuff --branch -m pytest && \
python3 -m coverage report -m && \
python3 -m coverage html
The key points when setting up the github actions are :
the list of dependencies to install, including amaranth-yosys
the export of some environment variables before running the test in order to use the sby from amaranth_yosys. That is where using the aforementioned amaranth._toolchain.require_tool is coming in handy.
Abstract
You have written some hardware design using amaranth-hdl, that you wish to promote into a library in order to reuse it in another project ? You also have written some tests that use SymbiYosys(sby) to validate this library in part or in full ?
This cookbook will describe the minimal settings to run those test in a github action in order to validate a commit. It will also describe what to install locally and a way to write tests. It is based on this commit from a project of mine to gather some reusable stuff for my own projects.
Target audience
This cookbook will be easier to understand provided :
However, the reference project is small enough to be studied at a leisurely pace and be used as an informative template
Overview
This cookbook will describe the following item, in that order
setuptools
and follows PEP 621Local Setup
The key point is to install SymbiYosys from source, meaning following the installation manual.
I installed the required parts ("Yosys, Yosys-SMTBMC and ABC", and "sby") and the recommanded part ("boolector").
Python package definition
See the reference file in full.
The key point is the list of dependencies. In order to use up to dates versions of amaranth, I instruct the setup tools to use a recent commit on the main branch. The targeted commit hashes will have to be updated with time.
Implementing tests that call sby
_See the reference file in full_
First thing first, I instructed git to ignore any file named
tmp
or liketmp.*
. Then, my tests generate RTLIL files and their companion SBY files with names starting withtmp.
This is to make sure that those artefacts will not end up in the repository.This part will show :
sby
Instanciate a simple test bench
Generate the RTLIL file
Given a base name, I will instruct
amaranth.back.rtill
to generate the specified RTLIL file with :Generate the SBY configuration file
Each test will require a SBY configuration file in order to target the generated RTLIL. For my project, I wrote a little helper that generate the file from the target RTLIL file and the required depth ; then I use this helper
Invoke sby
In a subprocess, we launch a simple invocation like
sby -f <the-sby-configuration-file>
, and we check the return code for detecting failure.Testing locally
I wrote a little script to automate this part, because I includes reformating the sources, packaging and re-installing locally the project, and then launching the tests and compute coverages
Github actions
See the reference file in full
The key points when setting up the github actions are :
amaranth-yosys
sby
from amaranth_yosys. That is where using the aforementionedamaranth._toolchain.require_tool
is coming in handy.Installation of dependencies
Running the tests