Bluespec uses active-low reset signals, whereas the ECP5 primitives
use active-high. So this was holding the EBRs in reset after the rest
of the design was running. Oops.
With this you can feed a stream of bytes in and get multi-byte structs
out, or vice versa. Handy for hooking up stuff like debuggers to
narrower serial busses.
The numeric types vs numeric value thing sucks, but there's a mild
workaround where you just recurse through numeric types until you
find one that matches the value you wanted. It's icky, but it ensures
registers are exactly the correct width instead of relying on later
synthesis to find and execute the width reduction.
I was mostly using a separate interface to be able to mark the methods
always_enabled and always_ready, but you can attach those annotations
to the module constructor instead.
A delay line takes a write and echoes it back N cycles later,
with N fixed at compile time. It's a handy primitive to have
when wrapping Verilog blackbox modules because the blackbox
often specifies something like having 2 cycles of latency,
and so you need to bubble the fact that a write occurred 2
cycles ago through to the output so that you can wire up the
right implicit conditions.
Callers can still specify whacky cross-domain RAMs in the cfg, but the
default is what you usually want: a dual-port RAM with both ports in the
caller's clock/reset domain.