amaranth
amaranth copied to clipboard
Can I create an active-low (asynchronous) reset?
Refiling here instead of other repo. Sorry if it's a duplicate.
Specifically, I'm looking to mimic the following SystemVerilog code:
always_ff @(posedge clk, negedge rst_n)
if (~rst_n) q <= 0;
else q <= d;
async_reset=True gets halfway there, but it's still an active-high reset. Any way of making it active-low or somehow creating one manually with this behavior where I can also control the port name?
Currently there's no way to do so; it is something on my radar for a while, but also not trivial to add in a backward compatible way. Please see the discussion here: https://github.com/nmigen/nmigen/issues/185#issuecomment-596256493.
Regarding the port name, you can create a clock domain and then rename the signal using cd.rst.name = "foobar", for example. (nMigen guarantees that it will never mangle names of signals you use for ports, unless two of them clash.)
I'm not sure if I should put this here on in the other thread, but one case where an async active-low reset would be useful (even in FPGAs) is when you want to connect or adhere to something like an AHB/AXI interface. Currently, it wouldn't be possible to do this solely in nmigen unless you wrote a wrapper module around the generated code inverting the reset.
You'd have to write a wrapper module anyway to instantiate a clock domain, wouldn't you?
If the AXI clock is the same as your system/reference clock then I don't see why you would?
The reset is synchronous unless you explicitly configure it async. Maybe I'm missing some part of your plan here; could you illustrate what you mean with some code?
For example, a Zynq exposes AXI ports as one of the main means of communicating between the processing system and the programmable logic. Those might actually be synchronous resets but I don't recall. In either case, they're active-low.
So if you're taking the clock from the processing system to use as your clock in the programmable logic, then you're on the same clock domain as the AXI port you would be communicating on, so everything there is just on a single clock domain.
Yeah, I understand that. My question is on a much more practical level. If you make a single-clock AXI component in nMigen that creates its own clock domain, then it would be, in general, not reusable in nMigen code (at least nMigen code that follows the conventions) since such components shouldn't have their own clock domains but refer, through late binding, to existing sync domain in the design. On the other hand, if you want to get Verilog for that component with the domain configured for asynchronous reset, you do need to create a clock domain for it explicitly. Another relevant aspect is that nMigen's definitions of buses do not, in general, follow Verilog conventions for the signal names, so you'd have to rename the signals there as well.
This, as far as I can tell, implies that a wrapper is necessary anyway. (Such a wrapper doesn't have to be verbose or laborous--it could be a simple invocation of a Python function provided by the same library that contains the AXI bus definitions.) And if you have a wrapper, you might as well invert the reset polarity there.
@hofstee Not really about nMigen, but in the Zynq you have a reset synchronizer that provides both high and low resets in it's outputs. From there, you can have cores with both low and high synchronic resets. Also if you are thinking about using the "Automation" provided by Vivado, you'll have to explicitly configure the reset port of the nmigen-generated core as "active high" beforehand (in the block design) and it'll do everything for you correctly (for some reason it assumes a low reset).
where do i start if i were to implement active-low reset?
https://github.com/nmigen/nmigen/blob/818c8bc46485ada0f31ad8ec23182ad01a6c7da1/nmigen/back/rtlil.py#L994 looks promissing. fixing this for negedge produces
sync negedge \rst
update \v \v$next
in rtlil (look ok so far, is it a valid active-low reset in rtlil?). and verilog:
always @(posedge clk, posedge \$auto$proc_dff.cc:153:gen_dffsr$3 [0], posedge \$auto$proc_dff.cc:154:gen_dffsr$4 [0])
if (\$auto$proc_dff.cc:154:gen_dffsr$4 [0]) v[0] <= 1'b0;
else if (\$auto$proc_dff.cc:153:gen_dffsr$3 [0]) v[0] <= 1'b1;
else v[0] <= \v$next [0];
for every bit of the signal being reset, which is not really an active low reset. i tested it with https://github.com/nmigen/nmigen/blob/master/examples/basic/arst.py example
so is it actually yosys who is unable to generate active low reset from valid rtl, or rtlil should look different?
can't find explicit confirmation that rtlil supports negedge SyncRules, but yosys source annotation says negedge is a valid type for SyncRule:
http://eddiehung.github.io/yosys/d3/dc3/namespaceRTLIL.html#a79713e13c00e4e216c60fb297be1900f
update: quick tests show that sync negedge \rst is a valid rtlil SyncRule
It's not clear what the design for active-low resets should be, so you should write an RFC we can discuss before any code changes will be considered at all.