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
Constant expression in generated block index not recognized #517
Comments
Original Redmine Comment Investigating further, the problem is that this is a very early pass. So in fact even a plain constant parameter will also fail. It looks like the strategy must be to postpone evaluation until a later pass. |
Original Redmine Comment Link at present is a textual match, and it can't resolve cross-module references statically unless they are a constant. The solution as you noted is to move that later, but it's going to be a bit of work. |
Original Redmine Comment Any update on this feature? This is very useful for parameterized designs. |
Original Redmine Comment I am also wondering if there is an update on this feature. I'm more interested in the case of addressing members in arrays of structs. Perhaps there's a way to do this, will post to the forum and ask, but it's the same error, so maybe it's to do with the same internal mechanism. |
Original Redmine Comment Any updates? I am seeing this error message from the following code:
Is this the same error? It makes it hard to create a datapath driver for a complex IP where we want to provide an efficient cycle accurate model by keeping the control logic in RTL and replace the datapaths with C++. |
Original Redmine Comment Yes, sorry this is the same issue. At present all module structures are "flat" (basically separate variables per module) so there is no easy way to index among them. |
Original Redmine Comment As mentioned in issue822, I'm interested in kicking this problem around. Caveat: I'm not a compiler-guy, but I'm certainly interested in becoming familiar with the project. Is it fair to say that the compiler needs to be able to postpone the linking of all not-immediately-discoverable references? Is this just dotted hierarchical references and array slices? Also, I'm wondering if this would / could lead to something like modular Verilator compilation? If the linking is done after compilation (as in gcc), could the intermediate step be saved to something akin to an object file (maybe more like saving the AST to a file?) so that you could compile portions of a project independently? When I can pick this up again, I was thinking I should start by commenting out the link step and seeing how things fell apart. I'm assuming the link step is the beginning bit of process() in Verilator.cpp. Is this correct, and do you think it's a reasonable first step? Thanks in advance for any advice you can provide, |
Original Redmine Comment Some notes on solutions for this: The current approach is based on Verilog 1995, which parses a module (V3Parse), determines sub-modules (V3LinkCells), parses those, then with all modules in memory determines generate statement values (V3Param), and moves on from there. The proper solution which would fix "all" the issues is to parse the top module, resolve generate statements, then based on what modules exist parse those, then repeat the generate/parse on sub modules. This obviously requires much greater coupling between what are now are separate stages. As to cells with "arrays" verilator converts FOO[0] to (basically) foo__0__ and deals with it as a flat module. Properly treating it as foo[0] would require different surgery on how the symbol table is done. It may be possible to hack in the simple cases described (generate value into a cell reference). Where verilator presently convert FOO[0] to foo__0__, instead put in a reference to e.g. a new AstNode CellArray which specifies an as-yet unknown array index number. Later the V3Param processing would determine the constant indexing. This should work as it doesn't require solving the more fundamental and complicated problems above. If someone is interested in fixing any of these we are interested and I can provide more details, but expect any to require at least a week commitment. |
Original Redmine Comment I am interested in fixing this issue and did some experiments. For the simple case described above, it seemed an early constant collapse would solve the problem. So I tried to add a ConstifyAll call before LinkDotResolve, which however did not work. I would like to know more details about the proper solution. Time and commitment are no problem to me, and I'd appreciate more discussions as they are helpful. Thank you so much! |
Original Redmine Comment A trivial fix to t_gen_index (the test code in the original post) can be found in the attachment. This fix, however, doesn't attack the deeper and more fundamental problem. Even when there is no gen loop, verilator still can't handle arrayed interfaces correctly. Please see attached test case. It is a modified version of the test case written by Todd Strader in bug 822. For this test case, after V3Inst::dearrayAll() pass, the IFACEREFTYPE entry in the type table will contain a dangling pointer to a nonexistent AST node, which is found by the program and causes the program to quit. More specifically, InstDeVisitor::visit(AstCell* nodep, ...) applies to such structure in the AST: CELL 0xabac50 ... foos -> IFACE 0xa8fe10 ... foo 3: RANGE ... 3:2: CONST ... 3:3: CONST ... (and in TYPETABLE section:) IFACEREFDTYPE 0xabd070 ... cell=foos if=foo -> CELL 0xabac50 ... foos -> IFACE 0xa8fe10 ... foo. It duplicates the CELL node according to the range expression, and hard-codes the cell index to each duplicated entry. It then removes the RANGE node and its children, as well as its parent, CELL at 0xabac50. All newly created CELL nodes have addresses other than 0xabac50. However, the type table is not updated accordingly. The IFACEREFDTYPE node at 0xabd070 is still expecting a CELL node at 0xabac50, which once existed but not anymore. This link is found to be broken and caused the program to crash. I would like to get help on fixing this issue first, as it's well-understood by me and I believe it's a good first step towards fixing the issue regarding arrayed interfaces in general. Could you please offer some suggestions on keeping the type table consistent? Thank you very much. |
Original Redmine Comment I spent some time looking at how to pull together to have the functions you suggest be callable, but it's a big mess, it violates too many assumptions. I'd suggest the alternative tack of creating an AstCellArray, then much later in V3Param converting it to an AstCell with the proper reference. |
Original Redmine Comment Sorry I mean a AstCellArrayRef, that is a reference to an as yet unknown cell number. This has a huge ripple effect. As you noted right now the full scope e.g. "a.b.c__BRA__0__KET.varname" is generated by V3LinkDot as a string and becomes part of a AstVarXRef as text. Then all later processing uses that textual scope name. Instead V3LinkDot should form something like AstVarXRef("varname", AstCellNameRef("a", AstCellNameRef("b", AstCellArrayRef("c", expression)))), that is AstNodes that specify how to scope into the cell containing that variable. Then all code that uses AstVarXRef (which is mostly V3LinkDot) needs to understand how to traverse those trees instead of simply using a string. |
Original Redmine Comment Back to this one again. Some more questions: Is it anticipated that this should only impact linking that runs into things that V3Param will later constify? Or is it believed that the linker should just build everything as this chain of references and then squash them all after V3Param has run? Will this affect just V3LinkDot or the other V3Link* passes as well? Would it be possible instead to run V3Param on the yet-to-be-constified node from within V3LinkDot instead of the deferred linking suggested above? If I understand correctly, V3Param already does this sort of thing with V3Width. Is it known if this is only a problem with array slicing? Or are there other should-be-resolvable things that need to be considered? |
Original Redmine Comment
The AstCellArrayRef idea is it's linked to a single one of the arrays. Until after V3LinkDot it doesn't really matter which one it points to as the arrays don't yet have unique variables (e.g. a[0].v and a[1].v both point at the same "v". so we can represent it instead as a[*].v and resolve the * later.)
Not sure until we try it.
No, V3LinkDot must have the entire and stable variable list across all modules for it to work.
There are other problems with the current approach, such as parsing modules that are not required. The proper approach is to parse and elaborate each module all of the way down, rather than step-by-step, but as also noted that's a lot of work. |
Original Redmine Comment First stab, doesn't do everything the issue covers, lots of caveats, etc., etc.: First off, I'm initially trying to fix t_interface_array, and the simpler variant t_interface_array_broken. t_interface_array_broken now passes, but t_interface_array fails with a new (to me) error which may or may not suggest a different issue. I'm not sure yet. Also, I don't seem to have broken anything else with this change. I also know for sure that this change won't fix a.b.foos[identity(0)] cases yet. I'm relatively sure I can pick that up easily, but I'd prefer to discuss the changes first. One major question: AstVarXRef's parent, AstNodeVarRef, explicitly forbids children. Wilson's suggestion above seems to suggest AstVarXRef having children (may be reading wrong), but instead of breaking AstNodeVarRef's assertion, I created AstUnlinkedVarXRef and stashed the proto-AstVarXRef under it until I could complete it. Is this acceptable? Beyond that, please take a look at the code and let me know if there is anything else I should consider for this particular case. Again, to be clear, this is not ready to be pulled yet. I need to do a lot of cleanup and error checking. I also want to go back and support some of the other cases discussed in the issue. Also, I think I have my head sufficiently around this. You can assign it to me. |
Original Redmine Comment I updated the GitHub repo with the solution to the error mentioned above that t_inteface_array was throwing. Basically, the unroller was checking to see if the if a genvar was being used anywhere on the LHS and aborting if so. Since I'm hiding the genvar as the select expression under AstCellArrayRef, this check was failing there. I told the unroller not to look under AstCellArrayRef nodes which gets me past this issue. What I'm seeing now is that V3Param is trying to flatten my AstUnlinkedVarXRef and throwing this:
I'm guessing this means that the genvar hasn't been expanded yet and that I need to (try to) defer this flattening until after that happens. Hopefully that works. I'll try that next. t_interface_array_genfor is a test to help me narrow down the current issue I'm seeing. I'll collapse all these tests when I have something final. |
Original Redmine Comment I updated the GitHub repo again. My last change was in error since the problem was not in the unroller, but that the LinkLvalue visitor didn't yet know about AstCellArrayRef nodes and that the select expression is not an lvalue. This has been addressed and all tests including t_interface_array are now passing. Next steps will be to handle other cases discussed here and then cleanup. Comments definitely welcomed. |
Original Redmine Comment Generally seems might work. Obviously need more in place to be sure. Only one nit so far - in the V3AstNodes.h class call the names just m_name, and make the accessors "virtual name()" and the default dumpers will print it out, so you won't need the dump() methods. |
Original Redmine Comment https://github.com/toddstrader/verilator-dev/tree/issue517 Ready to pull or for further comments. I've done a bunch of cleanup, and squashed the commits. Jeremy Bennett's test is no longer available, but I created a test based on Geoff Barrett's code snippet. This now works as well. |
Original Redmine Comment Wow thought there was going to need to be a lot more code added after your last patch, glad not much more was needed. Good stuff.
|
Original Redmine Comment Great, thanks for the feedback. I'll work on the changes and take a look at t_gen_index. Couple questions in the meantime: It turns out that the V3LinkDot AstUnlinkedVarXRef visitor is superfluous, but the other two are necessary to prevent checkNoDot() from throwing something like this:
This happens because the default visitor is checking to see if something is under a DOT which shouldn't be there. I could solve this other ways (perhaps by doing casting checks in the default visitor or checkNoDot()), but I thought this looked the cleanest. Also, I added the UINFO()'s to look more like the other V3LinkDot visitors. I'll remove the unnecessary visitor, but do you have suggestions about how to handle the other two? Should I just leave them as-is? Also, what do you do for error reporting when the node has no name (e.g. a DOT)? Is there some clever way to get a meaningful name when there is no name()? I don't see anything like expressionPrettyName(). I'm guessing this just needs to be handled on a case-by-case basis. |
Original Redmine Comment Forgot LinkDot had an unusual default visitor, so yes you need those. As you suspected for errors you'll need a special custom message. Perhaps m_ds.m_dotText is valid at this point? If there's no obvious additional information that's ok, just make sure the message is somewhat obvious as to what the user did. (As opposed to internal only errors where I'm not very descriptive - as you've seen reading the code!) |
Original Redmine Comment OK, ready to pull or for more discussion: https://github.com/toddstrader/verilator-dev/tree/issue517.2 If you'd rather see the un-squashed changes since last time, look here: https://github.com/toddstrader/verilator-dev/tree/issue517 I made the name changes requested, and tried to be more user friendly with the error messages (including node names now). t_gen_index now passes with everything else. I added t_interface_array_bad to check the error messages, but I can only hit 1/3 of them. The two elusive checks are both in the AstCellRef visitor in V3Param. They both verify that the AstCellRef's children are AstCellRef, AstCellArrayRef or AstParseRef. I can't seem to produce this scenario as everything I try gets handled at some earlier parser or syntax checking phase. I left a bunch of things that I tried as comments in the test. If anyone has suggestions on how to produce this, I'm definitely interested. Regarding the code changes, there were two major assumptions that I had previously which were broken. t_gen_index showed me that I wasn't deferring all the things that could be constified later by V3Param (arithmetic expressions in this case). To solve this, I changed V3LinkDot's AstSelBit visitor to defer all select expressions. This fixed t_gen_index, but broke some other tests, most notably t_var_xref_gen. It showed me that I assumed that the AstUnlikedVarXRef would always percolate up to the top of the DOT tree. In the case of unpacked arrays, this is not true. To handle this, I now track AstCellRef / AstCellArrayRef nodes looking for an AstVarXRef in the m_ds state so that an AstUnlinkedVarXRef can be built at an arbitrary location in the DOT tree. |
Original Redmine Comment
I'm doubtful that skipping the default visitor is the right thing to do. If for example the CellArrayRef's index value is a varxref (dotted variable from another module), I think the pointer will dangle and it will crash. Please try that in your tests. Also a nit, please put tabs to indent the front of your new code. You seem to have tabs in some cases, spaces in others. Otherwise looking good. |
Original Redmine Comment Yeah, it doesn't crash, but it can't constify the VarXRef:
You can find the test here: https://github.com/toddstrader/verilator-dev/tree/issue517.crazy_sel We get through linking fine, but when we try to constify the CellArrayRef with a VarXRef for a select expression, we get the first error message from V3Const because we hit the default visitor. Here's exactly what we're trying to constify:
So my question is, is there any simple way to get the constant value that this guy is pointing to? V3Const's error message would suggest there isn't. I'm guessing I can follow varp(), but I'm starting to get out of my depth pretty quickly here. |
Original Redmine Comment Looking at the varp I get from the VarXRef, I can tell it is a parameter. I could special case the scenario where a VarXRef is pointing at a parameter and return that constant. Is this getting to special-casey? |
Original Redmine Comment I changed the AstVarRef visitor to be an AstNodeVarRef visitor and that seems to have fixed this case: toddstrader/verilator-dev@a6d1046 I still need to clean up the whitespace, but does this look like it covers the hierarchical dotting case in question? |
Original Redmine Comment Cleaned up (including whitespace), squashed and rebased for consideration: https://github.com/toddstrader/verilator-dev/commits/issue517.3 |
Original Redmine Comment Great. I just reindented the .v files, and cleaned up some code you inherited of mine. Pushed to git towards 3.878. |
Original Redmine Comment In 3.878. |
Author Name: Jeremy Bennett (@jeremybennett)
Original Redmine Issue: 517 from https://www.veripool.org
Original Date: 2012-05-21
The following code fails to recognize the constant expression used as index into an array of generated module instances:
The error message is:
%Error: t/t_gen_index.v:49: Unsupported: Non-constant inside []'s in the cell part of a dotted reference
Where line 49 corresponds to the function's use of @`START + 1@ as an index to @bar_inst@.
I shall investigate further. In the meantime, please pull a test case from
https://github.com/jeremybennett/verilator/tree/gen-index
The text was updated successfully, but these errors were encountered: