Amaranth to Chisel

2024-05-09 (created), 2024-06-24 (updated)

edit: A lot of the following doesn’t apply any more, though it’s all been very helpful in learning.

My days of using Amaranth are over. I don’t feel able — nor do I want — to depend on something I’m not allowed(!) to contribute to, so I need a way to continue on with my FPGA studies without it. I don’t really view just trying to cobble together Verilog as viable for me right now; I’m rather dependent on having a decent higher-level thing going, and I already feel all the wind sucked out of my sails from having to make any change whatsoever.

I’ve experimented with doing my own HDL using what I learned from working with and on Amaranth (and Yosys, which I’ll happily continue to depend on), but it’s way too much work. After surveying the scene, I’ve chosen Chisel. Scala is not exactly my favourite, and this means really learning it properly, but y’know what? That’s how I felt about Python too, but I still did some cursed stuff with it!

I plan to bootstrap my way out of this hole by creating a small component in Amaranth, workbench it using CXXRTL, then duplicating that component in Chisel, using the same CXXRTL workbench to test it. This way I’m staying connected to “doing useful/measurable stuff” in a way I know. I’m also furthering my own HDL experiments while I go, letting Amaranth and Chisel combine in my head.

Done so far:

  • Bring hdx, rainhdx, and all their dependencies — including Amaranth — up to date.
    • New abc revision.
    • Amaranth depends on a newer pdm-backend, which I needed to package since it’s not in nixpkgs.
    • Had to unbreak rainhdx’s Nix, that last refactor was bad.
  • Add basic cxxsim support to rainhdx. This was mostly pulled from I²C, oh! Big stretch, which I maintain is impeccably named.
    • There was also the option to pull the Zig–CXXRTL support from sh1107, but the extra toolchain weight doesn’t feel like it helps me move any faster here.
  • A basic UART echo, tested with Amaranth’s simulator.
  • A clone of the Python simulator with CXXRTL.
  • Learn to do a very basic Chisel module with tests and Verilog output.
  • Build the Chisel module with CXXRTL and integrate it into the simulator — it’ll be very wrong, but the key is the integration.
  • Write a little unbuffered UART pair, test them, integrate. Done.
  • Extend the test case to exercise the Amaranth version’s buffers on TX/RX.
  • Write a FIFO in Chisel and buffer the TX/RX.
  • Discover Queue and learn how to use Decoupled – use that in RX and TX.
  • Redo the base UART module using Queue.
  • Test it on the iCEBreaker!
  • Mess around with SB_RGBA_DRV. Buffer the clock input with SB_GB.
  • Drop all the Python; it’s no longer necessary.
  • Actions CI for unit tests, cxxsim, synthesis.
  • Introduce a “Platform” notion to build separately for iCE40 and CXXRTL.
  • Split off the project-independent bits.
  • Redo the testbench to have the test unit as a blackboxed instance, rather than it driving everything through lines from the top. Get it working first with Amaranth, then Chisel.

And now, 12 days later, I’m done! I have a fair bit more ground to re-cover in terms of (a) actually putting together more complex designs — I’ll start with an SPI OLED (maaaaaybe with a Zig counterpart, like sh1107) and then move onto a RISC-V core again — and (b) creating my own framework to iterate on different projects quickly, but I’ve moved really fast1 and I’m quite happy with it.

It’s been interesting to decompose “HDL and digital design, as I learned it through Amaranth” — the way I write things has become a lot more fluent, a lot less “eighty stage FSM”, and I spend a lot more time looking at Verilog, which I think is a good thing right now. I grok a lot more of what’s under the covers, especially having to reimplement some of it.

I can re-archive all my Amaranth stuff again, now that I’ve finished leaning on it.


  1. With minimal tools! At the start of these 12 days I didn’t even have a desk, or an external monitor. Or a dev board!