Skip to content
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

Signal(n, reset=x) silently drops any bits in x past the n-th bit #183

Closed
whitequark opened this issue Aug 22, 2019 · 9 comments
Closed
Milestone

Comments

@whitequark
Copy link
Contributor

whitequark commented Aug 22, 2019

(Was: "Signal(reset=x) should be equivalent to Signal(reset=x, max=x+1)")

Right now the reset value gets silently truncated, which is undesirable.

@whitequark whitequark added this to the 0.1 milestone Aug 22, 2019
@programmerjake
Copy link
Contributor

I think a better solution is to warn when the reset value gets truncated. having the max adjusted by the reset value seems like an excellent way to write obfuscated code.

@whitequark
Copy link
Contributor Author

I think a better solution is to warn when the reset value gets truncated.

This interacts badly with a case where the reset value is -1, because it will always be truncated if the signal is unsigned.

having the max adjusted by the reset value seems like an excellent way to write obfuscated code.

Maybe, but the case I had in mind here is things like:

        ctr = Signal(max=int(clk_freq//2), reset=int(clk_freq//2) - 1)
        with m.If(ctr == 0):
            m.d.sync += ctr.eq(ctr.reset)

where you really don't want to repeat yourself, but are basically forced to with the current code.

@programmerjake
Copy link
Contributor

I think a better solution is to warn when the reset value gets truncated.

This interacts badly with a case where the reset value is -1, because it will always be truncated if the signal is unsigned.

maybe special case negative numbers on unsigned signals when ~reset < max where max = 2**width when width is given instead.

This would allow Signal(max=16, reset=~1) which is the same as Signal(max=0x10, reset=0xE), while still catching things such as Signal(max=2, reset=-1000) or Signal(max=42, reset=1234).

in any case, I think it's a bad idea to have max derived from reset.

@whitequark
Copy link
Contributor Author

maybe special case negative numbers on unsigned signals when ~reset < max where max = 2**width when width is given instead.

That would work, yeah.

in any case, I think it's a bad idea to have max derived from reset.

The reason I don't like the pattern of Signal(reset=x, max=x+1) is because it's not only prone to off-by-1 errors, but also such errors will only be visible when x is near a power-of-2, which is pretty bad for parametric code. Any better suggestions?

@programmerjake
Copy link
Contributor

in any case, I think it's a bad idea to have max derived from reset.

The reason I don't like the pattern of Signal(reset=x, max=x+1) is because it's not only prone to off-by-1 errors, but also such errors will only be visible when x is near a power-of-2, which is pretty bad for parametric code. Any better suggestions?

What about something like Signal(max=x, reset="last")? It would be equivalent to Signal(max=x, reset=x-1) for all x, including x not being a power of 2.

@programmerjake
Copy link
Contributor

I think we should avoid Signal(max=x, reset="max") because it might be mistaken for Signal(max=x, reset=x) which would be invalid.

@whitequark
Copy link
Contributor Author

I don't think using strings as sentinel objects is a good way to design an API in general.

@whitequark whitequark changed the title Signal(reset=x) should be equivalent to Signal(reset=x, max=x+1) Signal(n, reset=x) silently drops any bits in x past the n-th bit Aug 23, 2019
@programmerjake
Copy link
Contributor

I don't think using strings as sentinel objects is a good way to design an API in general.

any other non-numeric value would work just as well since Signal.__init__ could check for it by identity:

# in ast.py
last = object()
# in user code
sig = Signal(max=23, reset=last)

@whitequark
Copy link
Contributor Author

An argument shouldn't toggle between being a number (or something castable to number) and another completely arbitrary type. You can, but you shouldn't; this generally means you're trying to overload one argument too much.

Yes, the shape argument already does that. I wouldn't design it that way; I would especially not design it in a way where the second element of the tuple is a boolean; having unadorned (with a property, a keyword argument, etc) booleans in your code is a recipe for a bug. But have to keep it for compatibility.

Anyway, I have noted your opposition to the design I proposed. You're not the one who did that so I will look at other options.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants