-
Notifications
You must be signed in to change notification settings - Fork 894
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
Add support for packed multidimensional arrays #4100
Conversation
@@ -44,6 +44,7 @@ input [2:0] bit; | |||
output reg y1, y2; | |||
output y3, y4; | |||
|
|||
(* nomem2reg *) | |||
reg [7:0] mem1 [3:0]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is adding the attribute related to the other changes here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not really, but while testing the branch I found that mem1
was automatically converted to registers, while the point here seems to be to test both memory (mem1
) and memory converted to registers (mem2
).
Something has probably changed along the way here wrt. the heuristics for automatic conversion to registers, possibly with the change from memory_bram
to memory_libmap
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a nit, but it's best to separate changes like this into commits of their own.
Something has probably changed along the way here wrt. the heuristics for automatic conversion to registers, possibly with the change from memory_bram to memory_libmap?
FWIW the mem2reg
/nomem2reg
attribute is understood by the frontend, once we are operating on the netlist they are not picked up by anything. Right now I don't understand the nature of the test here and the frontend heuristics for mem2reg
conversion to say if we want the attribute here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a nit, but it's best to separate changes like this into commits of their own.
Sure, I'll split this change out.
Something has probably changed along the way here wrt. the heuristics for automatic conversion to registers, possibly with the change from memory_bram to memory_libmap?
FWIW the
mem2reg
/nomem2reg
attribute is understood by the frontend, once we are operating on the netlist they are not picked up by anything. Right now I don't understand the nature of the test here and the frontend heuristics formem2reg
conversion to say if we want the attribute here.
Right. I checked why mem1
is converted to registers by default - it's because all the (non-initial) assignments to mem1
are made using constant indices (MEM2REG_FL_CONST_LHS
is set in simplify.cc
).
I'm pretty sure the (* nomem2reg *)
attribute on mem1
makes sense there, since otherwise the test of mem1
is superfluous (covered by the test of mem2
).
I checked the timeline of this. Since (* mem2reg *)
was added to the test before MEM2REG_FL_CONST_LHS
was introduced in simplify.cc
, it seems clear that adding (* nomem2reg *)
to mem1
is necessary to make the test work according to the original intention:
$ git show -s --format="%h %as %s" 19dba2561e tests/simple/memory.v
19dba2561 2013-11-20 Implemented part/bit select on memory read
$ git show -s --format="%h %as %s" 7cfae2c52 frontends/ast/simplify.cc
7cfae2c52 2019-03-01 Use mem2reg on memories that only have constant-index write ports
1edc3a2
to
741877c
Compare
I only gave this a superficial drive-by look, I don't understand the part of the frontend this is touching. We will probably need to wait for @zachjs to give this a proper review, if he has the chance. |
741877c
to
7d1a95b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a great change that both extends the frontend and improves its overall code quality. Thank you!
My most substantive request is that we add a few more tests, especially for the "nested" typedefs logic. I think we find a way to break anything in the frontend that isn't fully tested. If you can't do this (you've already done a lot!), I'd be happy to. In any event, it shouldn't prevent this from getting merged. I think we can merge it right after the next release.
frontends/verilog/verilog_parser.y
Outdated
} | ||
} | ||
if (!valid) | ||
frontend_verilog_yyerror("wire/reg/logic packed dimension must be of the form: [<expr>:<expr>], [<expr>+:<expr>], or [<expr>-:<expr>]"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's fix this error message while we're here. Yosys indeed allows +: and -: in packed dimension, but this isn't allowed in the standard (range
vs. part_select_range
), so I'd prefer not to suggest this to users. We could also mention that we allow the SV shorthand [<expr>]
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, fixed. Note that [<expr>]
would not be correct here, since checkRange
is only used to check packed ranges, and [<expr>]
is only allowed for unpacked ranges in the LRM.
I first renamed checkRange
to checkPackedRange
in order to make this clearer, but decided against it in order to keep the diff small. This could always be changed later.
frontends/ast/simplify.cc
Outdated
for (int i = unpacked_dimensions; i < GetSize(dimensions); i++) { | ||
mem_width *= dimensions[i].range_width; | ||
} | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dead code?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's probably going to have to be used at some point (arrays of structs), but I've removed it for now.
@@ -4936,6 +4849,9 @@ void AstNode::mem2reg_as_needed_pass1(dict<AstNode*, pool<std::string>> &mem2reg | |||
{ | |||
AstNode *mem = id2ast; | |||
|
|||
if (integer < (unsigned)mem->unpacked_dimensions) | |||
input_error("Insufficient number of array indices for %s.\n", log_id(str)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you add a test for this error?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is really a shortcoming in Yosys, which currently treats anything with unpacked ranges as "memory", where the memory word consists of the packed part, and only indexing which ultimately selects the packed part is allowed (contrary to the LRM).
I'd rather not add tests for this shortcoming; I think it would be better to consider what could possibly be done about this in the future. Perhaps automatic mem2reg
conversion of "memory" which has unpacked parts selected?
frontends/ast/simplify.cc
Outdated
AstNode *id_ast = NULL; | ||
|
||
// Is this needed? | ||
//while (buf->simplify(true, false, stage, width_hint, sign_hint, false)) { } | ||
//while (buf->simplify(true, stage, width_hint, sign_hint)) { } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think this is needed? If not, let's just delete this comment to avoid future confusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No idea whether it can be needed in some situation, really - I only adapted the commented out code to earlier changes in Yosys.
In any case I've removed the comment now.
} | ||
int dims = GetSize(id_ast->dimensions); | ||
if (dim < 1 || dim > dims) | ||
input_error("Dimension %d out of range in `%s', as it only has %d dimensions!\n", dim, id_ast->str.c_str(), dims); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this behavior is incorrect. It should produce 'x
. I know this is inherited from the existing logic, so you can defer fixing it and just add a TODO.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually noticed this (old) incorrect behavior as well, however I had to stop somewhere for now.
TODO added.
@@ -3158,6 +3125,8 @@ skip_dynamic_range_lvalue_expansion:; | |||
// found right-hand side identifier for memory -> replace with memory read port | |||
if (stage > 1 && type == AST_IDENTIFIER && id2ast != NULL && id2ast->type == AST_MEMORY && !in_lvalue && | |||
children.size() == 1 && children[0]->type == AST_RANGE && children[0]->children.size() == 1) { | |||
if (integer < (unsigned)id2ast->unpacked_dimensions) | |||
input_error("Insufficient number of array indices for %s.\n", log_id(str)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this error has no test coverage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't, however it is old behavior, and incorrect at that - see comment above.
type = AST_WIRE; | ||
AstNode *expr = children[0]; | ||
children.erase(children.begin()); | ||
while (is_custom_type && simplify(const_fold, stage, width_hint, sign_hint)) {}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this type substitution pattern proliferates, let's factor it out. You can leave it for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I'll keep it in mind.
It would be great if you could add the tests you find relevant, thank you! I am aware of one construct where nested typedefs will yield incorrect results; arrays of structs/unions. However this is material for another PR. |
9cc7498
to
de908ae
Compare
* Generalization of dimensions metadata (also simplifies $size et al.) * Parsing and elaboration of multidimensional packed ranges
The purpose of memtest02 in tests/simple/memory.v is to test bit select on both memory (mem1) and memory converted to registers (mem2). After 7cfae2c, mem1 was automatically converted to registers, and the test no longer worked as intended. This is fixed by adding (* nomem2reg *) to mem1.
de908ae
to
f57abd4
Compare
Fixes #667
Fixes #3776