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

Expose primitive AND, OR, NAND, NOR, XOR, XNOR operations under descriptive names #147

Closed
whitequark opened this issue Jul 14, 2019 · 19 comments
Labels
Milestone

Comments

@whitequark
Copy link
Contributor

whitequark commented Jul 14, 2019

Verilog has these and they can be quite useful in bit-twiddling code, potentially reducing bugs as doing the same thing explicitly has a potential to introduce them. (I'm pretty sure I did introduce bugs like that.) Also, we already have Value.bool, which is really a reduce-or.

Bikeshed: how should they be called? I think Value.reduce_or, etc are OK. But another option is Value.any_bits for reduce-or, Value.all_bits for reduce-and, Value.even_bits for reduce-xor, and Value.odd_bits for reduce-xnor.

Bikeshed: should we even keep Value.bool? Does everyone agree that "any bit set" is the appropriate semantics for converting any value to a 1-bit one? Migen didn't have it so it's not even a backwards compatibility hazard.

@whitequark whitequark added this to the 0.1 milestone Jul 14, 2019
@whitequark
Copy link
Contributor Author

We probably want to keep Value.bool for two reasons: first, the Yosys $reduce_bool cell has the same semantics but different interpretation as $reduce_or cell (so it can be useful when roundtripping back to source), and second, Verilog does in fact define the mux ?:, conditional if and logical not ! semantics consistent with Value.bool.

@sbourdeauducq
Copy link
Member

Do we need those functions? It's easy enough to do it already with the Python operator module and the reduce function from functools.

@whitequark whitequark removed this from the 0.1 milestone Jul 14, 2019
@whitequark
Copy link
Contributor Author

@sbourdeauducq You can't do xnor that way, it's somewhat annoying to write, and it results in significantly less readable Verilog output / more bloated RTLIL, with one cell per bit.

@whitequark
Copy link
Contributor Author

whitequark commented Sep 12, 2019

Bikeshed suggestion by @emilazy: all_bits_set (AND), any_bits_set (OR), all_bits_clear (NOR), any_bits_clear (NAND), odd_parity (XOR), even_parity (XNOR). Looks good to me.

@whitequark whitequark changed the title Reduce-op (reduce-or, reduce-and, reduce-xor) Expose primitive AND, OR, NAND, NOR, XOR, XNOR operations under descriptive names Sep 12, 2019
@jordens
Copy link
Member

jordens commented Sep 12, 2019

What's wrong with Value._and() (or reduce_and() for some verbosity)? This is used in the title of the issue and in the definition what all_bits_set() would mean. It seems to be natural to stick to that. And it avoids potential ambiguity with alternative phrases (plural vs singular, verb versus no verb, cleared vs clear).

@emilazy
Copy link
Contributor

emilazy commented Sep 12, 2019

It's much easier to figure out what the semantics of even_parity are than reduce_xnor unless you already know, and the latter adds less because you can already just do a reduce with the operator.

@whitequark
Copy link
Contributor Author

Yes. The operation "XNOR of every bit" and the operation "compute even parity" produce the same result but are different to the reader of the code because they show different intent.

Also, reduce(nand, value) is not the same operation as a multi-input NAND, so you can't write any_bits_clear using reduce.

Also, having to add two imports (functools and operator) for something so trivial is inconvenient.

@jordens
Copy link
Member

jordens commented Sep 12, 2019

IMO the negated versions aren't that helpful anyway. For me _and(), _or(), _xor() would be sufficient. I can do the negated version with a single ~. And as you say, the negated versions have a definition problem. Whether you choose _xor() or parity() is more of a matter of taste. Since a multi-input XOR is not all that well defined anyway (is it "one-exclusive" or is it "parity"?), I am also fine with parity() since that's well defined.

@jordens
Copy link
Member

jordens commented Sep 12, 2019

Or all(), any(), parity(). The first two have prior art and analogies in python as well.
But I do think it would be weird to name these along the verbose names that you propose while every explanation of their meaning is in terms of AND and OR.

@whitequark
Copy link
Contributor Author

But I do think it would be weird to name these along the verbose names that you propose while every explanation of their meaning is in terms of AND and OR.

I don't know where you got the idea that they will be explained in terms of AND and OR. They will not be. The only reason I'm relating them to AND and OR is because that's what will go to the Verilog output, and Verilog readability is a goal (#98). The actual documentation will be something along the lines of 1 if every bit is 1, otherwise 0.

Or all(), any(), parity().

No, it would be at least .all(), .any(), .parity_even(), .parity_odd(). But it seems more useful to have a full set of operations, looking as (a) it makes sense from a generic point of view and (b) Verilog does have six of them.

@mithro
Copy link

mithro commented Sep 12, 2019

I would vote for having all / any atleast as easily accessible aliases (the others I have less opinion on...)

@emilazy
Copy link
Contributor

emilazy commented Sep 12, 2019

all/any/even_parity/odd_parity sgtm; ~ isn't much to type if you want to count low bits

@whitequark
Copy link
Contributor Author

whitequark commented Sep 12, 2019

Yeah, that's fine with me too. We can always add methods the longer names later, if it turns out they're especially useful.

@jordens
Copy link
Member

jordens commented Sep 12, 2019

Well. You did define and explain them in terms of AND/OR in this issue in the title and in your comments. That's where I got the idea from. ;)

Is there really need for the parity thing at all? Is this frequently used outside error detection? (I had thought that the unqualified "parity" is generally understood to mean "odd parity", but that's apparently not the case.)

@whitequark
Copy link
Contributor Author

It's useful quite occasionally, but it's (a) cheap to add (the method parity_even is unambiguous and unlikely to conflict with anything) and (b) results in significantly more readable Verilog when it's used, so I don't see why not.

@programmerjake
Copy link
Contributor

I think the name should be parity_even rather than even_parity since, to me, at least, parity_even means that it's checking that the parity is even (XNOR), whereas even_parity could mean either checking that the parity is even (XNOR) or computing a parity bit that makes the input and parity bit have even parity -- which is the inverted operation (XOR).

likewise for odd_parity and parity_odd.

maybe it's worth leaving them as reduce_xor and reduce_xnor or making them even more explicit: parity_is_even and parity_is_odd

an alternative could be popcnt_even and popcnt_odd

@whitequark
Copy link
Contributor Author

any and all are in b23a979.

@whitequark
Copy link
Contributor Author

@programmerjake I agree with your argument, any method with "parity" in it seems bound to be inherently ambiguous. As much as I dislike it, I think adding just .xor() alongside .any() and .all() is the only reasonable solution to this.

@whitequark
Copy link
Contributor Author

As an example of what I mean by inherently ambiguous, @emilazy suggests calling it .parity() where 0 means even and 1 means odd (which is reasonable in principle--it's popcnt mod 2, and the evenness of parity matches evenness of the number), but... x86 does the inverse of that for its parity flag.

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

No branches or pull requests

6 participants