-
Notifications
You must be signed in to change notification settings - Fork 175
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Way to start register with "random" value? #562
Comments
You could use a XOR trick to make a zero-reset register appear to have an arbitrary reset value. The XOR at the register output undoes the effect of the XOR at the input. But, at reset, the output becomes the value you XOR with. m = Module()
x = Signal(16)
x_reg = Signal(16)
x_next = Signal(16)
init_x = AnyConst(16)
m.d.comb += Assume(init_x > 0x1000)
m.d.comb += x_next.eq(x + 1)
m.d.sync += x_reg.eq(x_next ^ init_x)
m.d.comb += x.eq(x_reg ^ init_x) |
Well... yeah... I'm not enthusiastic about it though :) |
@RobertBaruch Would you agree with me that this should be the default when you use |
Hmm, well... I kind of like the current meaning of Using SymbiYosys in prove mode is adequate to test all sorts of random state, but then in a complex design you have to add asserts on internal states that are intimately tied to internal implementation, and seems fragile (i.e. hard to maintain) to me. But I can see the point of desiring a third state, which is |
Maybe: init_x = AnyConst(16)
m.d.comb += Assume(init_x > 0x1000)
x = Signal(16, reset=init_x) In case the reset parameter is a signal instead of a constant, nMigen would add the XOR gates behind the scenes. |
What would change is the behavior during simulation and verification, since, unless you are only interested in behavior following a power-on reset, you cannot assume that you know the value of a I do not intend to change the meaning of
I strongly dislike any approach that adds "randomization" into the language semantics. You do not actually want random values; what you want is an universally quantified value, and randomization is a workaround for the inability to express that in most tools. In terms of implementation, you can achieve the result you want by simply deleting the |
I think this is probably the most comfortable way I can do this now: # This is the "normal" logic of the module
x = Signal(16)
m.d.sync += x.eq(x+1)
# This is like the Initial() signal, except for the 2 initial clocks
init2 = Signal()
m.d.comb += init2.eq(Initial() | Past(Initial()))
# Asserts are now only valid past the first 2 clocks, because we
# use the first clock to load the initial values.
with m.If(~init2):
m.d.comb += Assert(x == (Past(x)+1)[:16])
# This works because formal will just set init_val to 0x2000.
m.d.comb += Cover(x == 0x2000)
# Load the initial values. This works because the last equate (in code) for a signal
# overrides any previous equates.
init_x = AnyConst(16)
m.d.comb += Assume(init_x > 0x1000)
with m.If(Initial()):
m.d.sync += x.eq(init_x) The above code passes cover, BMC, and induction. |
I found that if the
I solved this by adding an |
@RobertBaruch Have you tried my suggestion with the Yosys script? |
I'd rather not, since that solution requires me to know what the RTLIL name of the signal is, and to manually add such a line to the sby file, for every signal I want uninitialized. I much rather like my |
The reason I'm asking is because your If you actually told me the reason you dislike it instead of just ignoring it, though, I would have suggested e.g. adding an |
Oh I wasn't ignoring it. I thought you meant that I should use it to get around the fact that it can't be done in any other way. I'd rather add something to the Python code than something to the sby if I can. Or, if I can add something to the sby and not touch it after that, no matter how I modify my Python code, that would work too (i.e. it becomes my standard sby skeleton). In any case, I tried self.register = Signal(32, reset=0x12345678,
attrs=[("uninitialized", "")])
...
with m.If(Initial()):
m.d.comb += Assume(example.register == 0xFFFF1111) I'd still like to be able to turn uninitialized on and off, depending on need, though. I suppose something like this: class Example(Elaboratable):
def __init__(self, uninit: bool = False):
attrs = [] if not uninit else [("uninitialized", "false")]
self.register = Signal(32, reset=0x12345678, attrs=attrs) |
Right, I see. Since this is an issue asking for nmigen improvements, I'm approaching it thinking of a way that could be solved for everyone, not just you.
Once we have a formal runner, I'm thinking that there could be two modes: one that assumes you just reset the entire FPGA (so all the registers have their reset values), and one that does not (so the reset-less registers have unknown/uninitialized values). Would this be enough for your use cases? |
But then how would I specify that I want some signals to be initialized (e.g. internals), and some to not be initialized (e.g. the states I'm really interested in initializing with My use cases would be:
This way, I can use For example, I might want an internal counter to always start at zero. But I want to initialize a CPU register with an |
Okay, I see what you mean: my |
I'm guessing the answer is just going to be, run SymbiYosys in prove mode. But just in case I'm missing something... I would love to do something like this:
The goal here is to start BMC with
x
being any value above0x1000
. As it is now, this is UNSAT of course because the reset value ofx
, a synchronous signal, is zero, and the assumption that it isn't is a contradiction.The text was updated successfully, but these errors were encountered: