-
Notifications
You must be signed in to change notification settings - Fork 263
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
Working Xcelium + Indago support with single command commpilation plus a basic rerun script for Xcelium #866
base: master
Are you sure you want to change the base?
Conversation
Incisive and irun were replaced by Xcelium and xrun.
…ath not supported)
Added to ignore a too-many-branches complain of pylint due to the additions.
…s a basic rerun script.
self._prefix = prefix | ||
self._libraries = [] | ||
self._log_level = log_level | ||
if cdslib is None: |
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.
Why check for None instead of doing:
if cdslib:
...
else:
...
Shorter if it fits reasonably in VUnit's line length limit (if there is one):
x = a if cdslib else b
EDIT:
And if you have a single default if not set, you could do something like:
def func(arg: Optional[str]):
arg = arg or str(Path(arg).resolve())
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 actually as per https://peps.python.org/pep-0008/#programming-recommendations, but it could be that an unset cdslib
should have a default of ""
which would then be tested as you show.
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.
Well yeah, but only if you need to know that the value is actually None
. However, all that is being checked here is if the value is set, otherwise it's being set to the default.
In this case, it is written as an anti-pattern. My comment implicitly (sorry) recommends avoiding this pattern by using the more logical pattern of handling None
and other "empty" objects:
if not value:
do this
else:
do that
Why would you check for "false" first if you can just do the following which is less confusing to read (more important for complicated conditionals):
if value:
do that
else:
do this
None
is falsy meaning you can safely use if [str, None]
and it will never be true for None
. The other advantage of not checking None
here is that it will also catch empty objects like ""
, []
, ()
, {}
, etc. and fallback to the default behavior. As it is written, those objects will require the user to read through a code traceback when it inevitably fails because the user used the wrong empty type. I think it's important here because there's no type hint, so I'm not actually sure what cdslib
is supposed to be (from reading the interface, not the code).
Regarding the PEPs warning:
Also, beware of writing if x when you really mean if x is not None – e.g. when testing whether a variable or argument that defaults to None was set to some other value. The other value might have a type (such as a container) that could be false in a boolean context!
It doesn't recommend one way or another. However, I use type-hints for this reason so the user knows what is expected and what isn't. In that case, it is safe to make assumptions about how the data can be handled as the interfaces requirements are explicit.
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.
cdslib
defaults to None
, this is the correct check. In terms of type-hinting, we didn't feel it was our place to universally add type-hinting throughout all of VUnit as much as it pained us to not do so.
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 should also clarify that ""
is a valid input for this field. If the user does not specify, or does not wish to specify the cds.lib
file, then Xcelium will infer the location of the file and handle it automatically.
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.
In terms of type-hinting, we didn't feel it was our place to universally add type-hinting throughout all of VUnit as much as it pained us to not do so.
I totally understand that.
""
, at least to me, is not obvious that it's supposed to be CWD unless I knew the implementation. Yes, Path("").resolve()
resolves to the CWD, but this to me isn't clear. I would think that if a user wanted CWD, they would pass "."
. If I were reading the code and ""
was used, I would read it as the user letting the code choose the default. My preference is to be explicit, not implicit and in this case I think treating ""
as a valid path is implicit.
EDIT: If other parts of VUnit do this, then stick with that as I'm not sure to be honest. I just wanted to point it out as if not ... elif ...
can usually be refactored slightly to be more clear.
""" | ||
cds_root_virtuoso = self.find_cds_root_virtuoso() | ||
|
||
if cds_root_virtuoso is None: |
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've never understood driving TCL interfaces with Python strings. Stuff like this is way clearer when using Jinja templates as the flow can be followed in a single file with all the for, if/else, etc. constructs instead of following python strings between functions/methods
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.
For multi-line strings, I like using inspect.cleandoc
instead to avoid \
's and also to keep the string inline with the 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.
Is Jinja already a VUnit dependency? If so, I agree, otherwise it's not quite so clear if that code warrants an extra dependency.
I didn't know about inspect.cleandoc
, interesting. I often see textwrap.dedent
used (assuming the tabs --> spaces transformation is not needed).
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 nothing else, this should use f-strings.
I think I was using Python 3.2 when I originally wrote this which apparently didn't even support {}
with auto-numbering.
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'm not sure, it's been a while since I've forked and modified VUnit. textwrap
probably makes more sense, I know cleandoc
because I've needed some other stuff it can do that dedent
can't.
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.
Ideally, I'd just convert this to format strings. But I don't see a need to change it unless people are very unhappy with it.
""" | ||
Compiles all source files and prints status information | ||
""" | ||
try: |
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 the output consistent with the rest of VUnit's reporting?
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 I had to guess, the answer would be no.
for source_file in source_files: | ||
unique_args = [] | ||
if source_file.is_vhdl: | ||
unique_args += ["%s" % self._vhdl_std_opt(source_file.get_vhdl_standard())] |
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.
f-string would be clearer
|
||
args += [source_file.name] | ||
if len(unique_args) > 0: | ||
# args += ["-filemap %s" % source_file.name] |
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'd be nice to remove commented code if it's not needed anymore. If so, else should be removed entirely
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.
We left this commented code in because we didn't have time to setup a test for it but we'd ideally like to support per-source file arguments as is supported by Xcelium.
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 see. In that case, I'd still remove it, but open a new issue with some of this code and where it should go unless you plan to add support.
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.
Whoops, didn't mean to start review.
cmd = str(Path(self._prefix) / "xrun") | ||
args = [] | ||
|
||
args += ["-compile"] |
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 would be clearer to just do
args = [
"foo",
"bar",
]
Returns command to compile a VHDL file | ||
""" | ||
cmd = str(Path(self._prefix) / "xrun") | ||
args = [] |
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 would be clearer to do:
args = [
"foo",
"bar",
]
@@ -795,6 +818,11 @@ def _main_run(self, post_run): | |||
Main with running tests | |||
""" | |||
simulator_if = self._create_simulator_if() | |||
try: | |||
simulator_if.set_global_compile_options(self._global_compile_options) |
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.
Why is every python exception caught and not the ones you care about? This could mask potentially important issues in the code that would make debugging difficult
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.
Also, it could be written more succinctly using with suppress(Exception):
(and from contextlib import suppress
)
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 know why this is catching all errors though? My concern is things like KeyError
somewhere below that should be raised and might cause confusing errors later. My opinion is that this should only catch what it cares about to avoid masking VUnit bugs that affect the user.
Every once in a while I need this pattern, but very very rarely. Usually only near the top level where I'm re-raising the exception because I knew something bad happened that I didn't expect and want to provide extra context (e.g. a log file)
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 believe this was added because we didn't care if it failed which is probably not the behavior we'd actually want in production quality code. I'll have to dig in and see what possible failures actually exist and see if we even care to catch any of them.
default=None, | ||
help="The hdl.var file to use. If not given, VUnit does not use a hdl.var file.", | ||
) | ||
# NOTE: Incisive shares the command-line arguments with Xcelium |
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 comment relates to the code that was removed, this should be in a commit message instead. I personally don't think this comment is needed, but I would clean it up to include add_argument is excluded because X
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.
IMO we could even remove all traces of Incisive, its last release was in 2015, everything after that was called Xcelium.
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.
Would the Incisive from 2015 work with the new Xcelium interface?
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 honestly don't know. Our codebase is incompatible with Incisive due to reliance on newer System Verilog features.
] # promote to error: "bad natural literal in generic association" | ||
args += [ | ||
'-xmlibdirname "%s"' % (str(Path(self._output_path) / "libraries")) | ||
] # @TODO: ugly |
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.
with f-string, you wouldn't need to type-cast as str
cds = CDSFile.parse(self._cdslib) | ||
return cds | ||
|
||
def simulate( # pylint: disable=too-many-locals, too-many-branches |
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 whole function is really complex and confusing. I would break things up a bit or find a better way to do all the branching.
args = [] | ||
for name, value in generics.items(): | ||
if _generic_needs_quoting(value): | ||
args += ['''-gpg "%s.%s => \\"%s\\""''' % (entity_name, name, value)] |
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.
First, this seems appropriate for list comprehension. Second, why are there tripple '''
?? This file has been using "
for most quotes so this was awkward at first to read.
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 code was originally mine (back when f-strings were too new), and the triple single-quotes make this more readable IMO - it's a string with quotes that contains escaped quotes.
I agree with the use of a list comprehension and would also use f-strings 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.
That's fair. I would honestly run this file through black with preview features enabled so it reformats strings and see what it comes up with.
import logging | ||
from copy import copy | ||
import traceback | ||
from vunit.sim_if.factory import SIMULATOR_FACTORY |
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.
Why was this import moved to the funcs/methods? It has the potential to be imported many, many times now which seems pointless to me.
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.
We were having an issue with it for some reason. I have it on my list of things to clean up/fix.
"-nowarn DLCVAR", | ||
"-v200x -extv200x -inc_v200x_pkg", | ||
"-work work", | ||
'-cdslib "%s"' % str(Path(self.output_path) / "cds.lib"), |
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 strongly dislike mixing quote types for no reason. Use f-string or format to clean this up.
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'm not sure, but it could be that the simulator really wants to see doublequotes 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.
I mentioned black earlier (I won't reference it more after this), but their docs/impl might have their str opinions about this case. Not sure if the PEPs have a recommendation for this. It doesn't look like VUnit enabled preview features so that's why it's probably not touching strings.
Test the Xcelium interface | ||
""" | ||
|
||
@mock.patch("vunit.sim_if.xcelium.XceliumInterface.find_cds_root_virtuoso") |
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.
Similar mocks are used for most test methods. I would decorate all the common ones on the class insted. For any duplicated code, you can set up a static or class method to configure the default mocks or leave unique mocks decorated local to the test methods if it's not shared.
help="The hdl.var file to use. If not given, VUnit does not use a hdl.var file.", | ||
) | ||
group.add_argument( | ||
"--use_indago", |
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.
"--use_indago", | |
"--use-indago", |
Python tools generally don't use underscores in options.
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 don't think VUnit does either.
@@ -795,6 +818,11 @@ def _main_run(self, post_run): | |||
Main with running tests | |||
""" | |||
simulator_if = self._create_simulator_if() | |||
try: | |||
simulator_if.set_global_compile_options(self._global_compile_options) |
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.
Also, it could be written more succinctly using with suppress(Exception):
(and from contextlib import suppress
)
Another quick thing I noticed is there is already #728. Does anyone know how this differs? |
@@ -0,0 +1,60 @@ | |||
# This Source Code Form is subject to the terms of the Mozilla Public |
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 file should be removed as it is no longer necessary.
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 it needed for older versions of Xcelium?
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. It was added by @rodrigomelo9 in their original PR and was made irrelevant by our inclusion of the patches to the files in this PR.
@GlenNicholls As mentioned in the opening of this PR, this is based on #728 which was incomplete. If you look at the commit history for this PR, it has the full history from #728 included. |
Hi. I started #728, but I'm not a VUnit user, so when it started to be complicated, I left it unfinished... |
I see, thanks for the clarification! |
I did a first pass on cleaning up the review per the comments. I've addressed most issues so far locally, but I haven't had time to finish my changes and verify them against xcelium this week. I'll probably get a new commit up sometime next week, time permitted. |
@@ -0,0 +1,60 @@ | |||
# This Source Code Form is subject to the terms of the Mozilla Public |
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 it needed for older versions of Xcelium?
default=None, | ||
help="The hdl.var file to use. If not given, VUnit does not use a hdl.var file.", | ||
) | ||
# NOTE: Incisive shares the command-line arguments with Xcelium |
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.
Would the Incisive from 2015 work with the new Xcelium interface?
@@ -489,6 +492,26 @@ def add_compile_option(self, name: str, value: str, allow_empty: Optional[bool] | |||
for source_file in check_not_empty(source_files, allow_empty, "No source files found"): | |||
source_file.add_compile_option(name, value) | |||
|
|||
|
|||
def set_global_compile_options(self, name: str, value: str, allow_empty: Optional[bool] = False): |
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 this a way to add compile options to future added source files? Is that necessary for Xcelium support or a separate feature?
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 was needed to achieve acceptable performance for Xcelium via single command compilation (We saw compilation times go from ~2m to ~11s on our NFS share). The other option was adding all compilation options into a hash map and then de-duping them, but in early testing that proved to be very error prone based on how users might decide to compose command line options in Python.
@@ -79,7 +79,11 @@ class test_runner; | |||
end | |||
|
|||
if (index == -1) begin | |||
$error("Internal error: Cannot find 'enabled_test_cases' key"); |
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.
Generally speaking we have controlled the behavior on errors by configuring the simulator. The reason is that we want the error to behave the same way regardless if it is caused by user code or VUnit features. Is that not possible with Xcelium?
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 believe we added this because it was the easiest way to ensure consistent behavior between Questa/Modelsim and Xcelium. We can look into configuring the max errors before stopping in Xcelium but I haven't done it before.
Any updates regarding this MR? I would like to use VUnit with Xcelium in an upcoming project. If you need any assistance, let me know. Maybe i can somehow contribute. |
FYI I decided to try this branch with Xcelium 2303 using the Uart example run.py - Failed. Seemed like OSVVM wasn't even compiling correctly. |
@JFMarten Due to leaving the position where I worked on this, I won't be able to continue the work on this for contractual reasons. I wasn't able to get this into a good linted state before leaving the job. The branch does work for System Verilog and Verilog. For VHDL, we didn't have anything other than toy examples to try and those appeared to work. @bryankerr1986 I believe that is likely an OSVVM + Cadence Xcelium issue not an issue with this branch if I'm remembering the communications with Cadence correctly. There were a lot of OSVVM issues in Cadence and they got fixed after this branch was created so we never got the fixes in the git submodule into this branch. For anyone who is capable of picking this up, please feel free to do so. |
Ok, well I forked your repo and rebased to the latest VUnit. I also added an option to pass compile arguments. Now I have a pretty large vhdl sim working. One thing I noticed is that your compiling the source files a little differently than incisive was doing this right? I noticed run.py --compile and run.py -m we're not working for me. Does this sound familiar? |
@bryankerr1986 We ran into a lot of issues around NFSv4 and file timestamps with Xcelium when we tried doing multi-library compiles. We also found that the per-file compilation was fundamentally broken both in terms of speed and in terms of maintaining a consistent database on NFSv4 drives (and we saw the same issue on a GlusterFS filesystem). We also tried several parallel build and simulation trials but didn't get good results using a common compilation directory with per-sim elaboration keys. And yes, I did break a good number of things from what was being done for Incisive. Overall, VUnit is not designed to handle 3-step compilation or efficient compilation particularly well. And we didn't really fully test everything. We got it to a working state for us and pushed that. Additional changes were made but I never got approval to push them. But those are mostly around automation in Indago not around the compilation and simulation flow without a debugger attached. Overall, we were in the process of moving away from VUnit as it was too inflexible for the number of simulations we were running in CI/CD and it was not scaling well. |
This work started from the work that @rodrigomelo9 presented in his PR: #728.