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

[vpi] Structure test case #3781

Open
wants to merge 8 commits into
base: master
Choose a base branch
from

Conversation

AndrewNolte
Copy link
Contributor

Uses golden files to easily compare verilog symbol structure between simulators. Split off from #3756

@AndrewNolte AndrewNolte force-pushed the vpi-structure-tc branch 4 times, most recently from 8b37f46 to b1c9f40 Compare March 18, 2024 17:07
Copy link

codecov bot commented Mar 18, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 72.70%. Comparing base (53a5f5e) to head (94cfe4d).
Report is 25 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #3781      +/-   ##
==========================================
- Coverage   73.54%   72.70%   -0.84%     
==========================================
  Files          49       49              
  Lines        7916     8050     +134     
  Branches     2195     2208      +13     
==========================================
+ Hits         5822     5853      +31     
- Misses       1581     1682     +101     
- Partials      513      515       +2     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@AndrewNolte
Copy link
Contributor Author

I'd like to land this before putting back up the vpi iteration fixes for verilator, the last of those PRs is almost landed: verilator/verilator#4965

@AndrewNolte
Copy link
Contributor Author

Any update on this? verilator/verilator#4965 was landed recently @ktbarrett

@ktbarrett
Copy link
Member

I've been very busy with my move to Colorado. I probably won't have time to work on cocotb until next week at the earliest.

My question is why do we need to add a new test and source code for this? It does something g very similar to test_iteration and test_discovery (albeit in a lot better way IMO). This is just more code that's going to have to be refactored eventually when someone takes a cracked at replacing all discovery-type and value access tests with a more holistic approach.

That refactor was next on my radar after my current PRs are in.

@ktbarrett
Copy link
Member

I guess I'm fine with this coming in if it's blocking your other work. Just know it will probably be rm -rf'd in the near future.

@AndrewNolte
Copy link
Contributor Author

I agree, these tests try to achieve something similar. I initially just wanted to measure the progress on verilator vpi, so didn't thinking recreating this on sample_module or endian_swapper would be worth my time due to expected ci breaking of other tests.

I think we should move towards golden files for a lot of the cocotb tests for a few reasons:

  • It makes it easier to write tests
  • It expands the amount we're checking between simulators
  • CI can automatically commit the golden output of a simulator that an individual user doesn't have access to

I think this provides a start for moving more of the structure / simulator differentiating tests to use golden files

@ktbarrett
Copy link
Member

Questa failure.

#      0.00ns INFO     cocotb.regression                  running vpi_structure.test_structure (1/2)
#                                                             Tests that name, fullname, handle type, and length match across simulators.
# t: HierarchyObject                                 t
#   LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND: LogicObject[1] t.LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND
#   a: LogicObject[8]                                t.a
#   clk: LogicObject[1]                              t.clk
#   count: LogicObject[32]                           t.count
#   do_generate: LogicObject[32]                     t.do_generate
#   fourthreetwoone: ArrayObject[2]                  t.fourthreetwoone
#   half_count: LogicObject[32]                      t.half_count
#   long_int: LogicObject[64]                        t.long_int
#   onebit: LogicObject[1]                           t.onebit
#   quads: ArrayObject[2]                            t.quads
#   real1: RealObject[None]                          t.real1
#   status: IntegerObject[None]                      t.status
#   str1: StringObject[0]                            t.str1
#   text: LogicObject[512]                           t.text
#   text_byte: LogicObject[8]                        t.text_byte
#   text_half: LogicObject[16]                       t.text_half
#   text_long: LogicObject[64]                       t.text_long
#   text_word: LogicObject[32]                       t.text_word
#   twoone: LogicObject[2]                           t.twoone
#   x: LogicObject[8]                                t.x
#   arr: HierarchyArrayObject                        t.arr
#     arr[1]: HierarchyObject                        t.arr[1]
#       i: LogicObject[32]                           t.arr[1].i
#       arr: HierarchyObject                         t.arr[1].arr
#         LENGTH: LogicObject[32]                    t.arr[1].arr.LENGTH
#         check: LogicObject[1]                      t.arr[1].arr.check
#         rfr: LogicObject[1]                        t.arr[1].arr.rfr
#         sig: LogicObject[1]                        t.arr[1].arr.sig
#         verbose: LogicObject[1]                    t.arr[1].arr.verbose
#     arr[2]: HierarchyObject                        t.arr[2]
#       i: LogicObject[32]                           t.arr[2].i
#       arr: HierarchyObject                         t.arr[2].arr
#         LENGTH: LogicObject[32]                    t.arr[2].arr.LENGTH
#         check: LogicObject[1]                      t.arr[2].arr.check
#         rfr: LogicObject[2]                        t.arr[2].arr.rfr
#         sig: LogicObject[2]                        t.arr[2].arr.sig
#         verbose: LogicObject[1]                    t.arr[2].arr.verbose
#   bus1: HierarchyObject                            t.bus1
#     adr: LogicObject[32]                           t.bus1.adr
#     dat: LogicObject[32]                           t.bus1.dat
#     sel: IntegerObject[None]                       t.bus1.sel
#   cond_scope: HierarchyObject                      t.cond_scope
#     scoped_wire: LogicObject[32]                   t.cond_scope.scoped_wire
#     scoped_sub: HierarchyObject                    t.cond_scope.scoped_sub
#       redundant: LogicObject[1]                    t.cond_scope.scoped_sub.redundant
#       subsig1: LogicObject[1]                      t.cond_scope.scoped_sub.subsig1
#       subsig2: LogicObject[1]                      t.cond_scope.scoped_sub.subsig2
#     sub_wrap_gen: HierarchyObject                  t.cond_scope.sub_wrap_gen
#       gen_wrap: HierarchyObject                    t.cond_scope.sub_wrap_gen.gen_wrap
#         LENGTH: LogicObject[32]                    t.cond_scope.sub_wrap_gen.gen_wrap.LENGTH
#         gen_loop: HierarchyArrayObject             t.cond_scope.sub_wrap_gen.gen_wrap.gen_loop
#           gen_loop[0]: HierarchyObject             t.cond_scope.sub_wrap_gen.gen_wrap.gen_loop[0]
#             i: LogicObject[32]                     t.cond_scope.sub_wrap_gen.gen_wrap.gen_loop[0].i
#             wrapped_sub: HierarchyObject           t.cond_scope.sub_wrap_gen.gen_wrap.gen_loop[0].wrapped_sub
#               redundant: LogicObject[1]            t.cond_scope.sub_wrap_gen.gen_wrap.gen_loop[0].wrapped_sub.redundant
#               subsig1: LogicObject[1]              t.cond_scope.sub_wrap_gen.gen_wrap.gen_loop[0].wrapped_sub.subsig1
#               subsig2: LogicObject[1]              t.cond_scope.sub_wrap_gen.gen_wrap.gen_loop[0].wrapped_sub.subsig2
#       my_sub: HierarchyObject                      t.cond_scope.sub_wrap_gen.my_sub
#         redundant: LogicObject[1]                  t.cond_scope.sub_wrap_gen.my_sub.redundant
#         subsig1: LogicObject[1]                    t.cond_scope.sub_wrap_gen.my_sub.subsig1
#         subsig2: LogicObject[1]                    t.cond_scope.sub_wrap_gen.my_sub.subsig2
#   intf_arr[0]: HierarchyObject                     t.intf_arr[0]
#     addr: LogicObject[32]                          t.intf_arr[0].addr
#   intf_arr[1]: HierarchyObject                     t.intf_arr[1]
#     addr: LogicObject[32]                          t.intf_arr[1].addr
#   outer_scope: HierarchyArrayObject                t.outer_scope
#     outer_scope[1]: HierarchyObject                t.outer_scope[1]
#       i: LogicObject[32]                           t.outer_scope[1].i
#       scoped_param: LogicObject[32]                t.outer_scope[1].scoped_param
#       inner_scope: HierarchyArrayObject            t.outer_scope[1].inner_scope
#         inner_scope[1]: HierarchyObject            t.outer_scope[1].inner_scope[1]
#           j: LogicObject[32]                       t.outer_scope[1].inner_scope[1].j
#           scoped_param_inner: LogicObject[32]      t.outer_scope[1].inner_scope[1].scoped_param_inner
#           arr: HierarchyObject                     t.outer_scope[1].inner_scope[1].arr
#             LENGTH: LogicObject[32]                t.outer_scope[1].inner_scope[1].arr.LENGTH
#             check: LogicObject[1]                  t.outer_scope[1].inner_scope[1].arr.check
#             rfr: LogicObject[3]                    t.outer_scope[1].inner_scope[1].arr.rfr
#             sig: LogicObject[3]                    t.outer_scope[1].inner_scope[1].arr.sig
#             verbose: LogicObject[1]                t.outer_scope[1].inner_scope[1].arr.verbose
#         inner_scope[2]: HierarchyObject            t.outer_scope[1].inner_scope[2]
#           j: LogicObject[32]                       t.outer_scope[1].inner_scope[2].j
#           scoped_param_inner: LogicObject[32]      t.outer_scope[1].inner_scope[2].scoped_param_inner
#           arr: HierarchyObject                     t.outer_scope[1].inner_scope[2].arr
#             LENGTH: LogicObject[32]                t.outer_scope[1].inner_scope[2].arr.LENGTH
#             check: LogicObject[1]                  t.outer_scope[1].inner_scope[2].arr.check
#             rfr: LogicObject[3]                    t.outer_scope[1].inner_scope[2].arr.rfr
#             sig: LogicObject[3]                    t.outer_scope[1].inner_scope[2].arr.sig
#             verbose: LogicObject[1]                t.outer_scope[1].inner_scope[2].arr.verbose
#     outer_scope[2]: HierarchyObject                t.outer_scope[2]
#       i: LogicObject[32]                           t.outer_scope[2].i
#       scoped_param: LogicObject[32]                t.outer_scope[2].scoped_param
#       inner_scope: HierarchyArrayObject            t.outer_scope[2].inner_scope
#         inner_scope[1]: HierarchyObject            t.outer_scope[2].inner_scope[1]
#           j: LogicObject[32]                       t.outer_scope[2].inner_scope[1].j
#           scoped_param_inner: LogicObject[32]      t.outer_scope[2].inner_scope[1].scoped_param_inner
#           arr: HierarchyObject                     t.outer_scope[2].inner_scope[1].arr
#             LENGTH: LogicObject[32]                t.outer_scope[2].inner_scope[1].arr.LENGTH
#             check: LogicObject[1]                  t.outer_scope[2].inner_scope[1].arr.check
#             rfr: LogicObject[5]                    t.outer_scope[2].inner_scope[1].arr.rfr
#             sig: LogicObject[5]                    t.outer_scope[2].inner_scope[1].arr.sig
#             verbose: LogicObject[1]                t.outer_scope[2].inner_scope[1].arr.verbose
#         inner_scope[2]: HierarchyObject            t.outer_scope[2].inner_scope[2]
#           j: LogicObject[32]                       t.outer_scope[2].inner_scope[2].j
#           scoped_param_inner: LogicObject[32]      t.outer_scope[2].inner_scope[2].scoped_param_inner
#           arr: HierarchyObject                     t.outer_scope[2].inner_scope[2].arr
#             LENGTH: LogicObject[32]                t.outer_scope[2].inner_scope[2].arr.LENGTH
#             check: LogicObject[1]                  t.outer_scope[2].inner_scope[2].arr.check
#             rfr: LogicObject[5]                    t.outer_scope[2].inner_scope[2].arr.rfr
#             sig: LogicObject[5]                    t.outer_scope[2].inner_scope[2].arr.sig
#             verbose: LogicObject[1]                t.outer_scope[2].inner_scope[2].arr.verbose
#   sub: HierarchyObject                             t.sub
#     redundant: LogicObject[1]                      t.sub.redundant
#     subsig1: LogicObject[1]                        t.sub.subsig1
#     subsig2: LogicObject[1]                        t.sub.subsig2
#   sub_wrap: HierarchyObject                        t.sub_wrap
#     gen_wrap: HierarchyObject                      t.sub_wrap.gen_wrap
#       LENGTH: LogicObject[32]                      t.sub_wrap.gen_wrap.LENGTH
#       gen_loop: HierarchyArrayObject               t.sub_wrap.gen_wrap.gen_loop
#         gen_loop[0]: HierarchyObject               t.sub_wrap.gen_wrap.gen_loop[0]
#           i: LogicObject[32]                       t.sub_wrap.gen_wrap.gen_loop[0].i
#           wrapped_sub: HierarchyObject             t.sub_wrap.gen_wrap.gen_loop[0].wrapped_sub
#             redundant: LogicObject[1]              t.sub_wrap.gen_wrap.gen_loop[0].wrapped_sub.redundant
#             subsig1: LogicObject[1]                t.sub_wrap.gen_wrap.gen_loop[0].wrapped_sub.subsig1
#             subsig2: LogicObject[1]                t.sub_wrap.gen_wrap.gen_loop[0].wrapped_sub.subsig2
#     my_sub: HierarchyObject                        t.sub_wrap.my_sub
#       redundant: LogicObject[1]                    t.sub_wrap.my_sub.redundant
#       subsig1: LogicObject[1]                      t.sub_wrap.my_sub.subsig1
#       subsig2: LogicObject[1]                      t.sub_wrap.my_sub.subsig2
#      0.00ns WARNING  cocotb                             No expected output file found for modelsim. Pass plusarg +update to update output file
#      0.00ns INFO     cocotb.regression                  vpi_structure.test_structure passed
#      0.00ns INFO     cocotb.regression                  running vpi_structure.test_name_matches_iter (2/2)
#                                                             Test name accessibility and handle lengths.
#      0.00ns INFO     cocotb                             t
#      0.00ns INFO     cocotb.regression                  vpi_structure.test_name_matches_iter failed
#                                                         Traceback (most recent call last):
#                                                           File "/opt/actions-runner/_work/cocotb/cocotb/tests/test_cases/test_vpi_structure/vpi_structure.py", line 138, in test_name_matches_iter
#                                                             raise DirectLenMismatch(
#                                                         vpi_structure.DirectLenMismatch: len of direct object does not match iterated object direct_obj=HierarchyObject(t)[0], obj=HierarchyObject(t)[28]
#      0.00ns INFO     cocotb.regression                  **********************************************************************************************
#                                                         ** TEST                                  STATUS  SIM TIME (ns)  REAL TIME (s)  RATIO (ns/s) **
#                                                         **********************************************************************************************
#                                                         ** vpi_structure.test_structure           PASS           0.00           0.01          0.16  **
#                                                         ** vpi_structure.test_name_matches_iter   FAIL           0.00           0.00          1.33  **
#                                                         **********************************************************************************************
#                                                         ** TESTS=2 PASS=1 FAIL=1 SKIP=0                          0.00           0.02          0.12  **
#                                                         **********************************************************************************************

Comment on lines +105 to +110
@cocotb.test(
skip=SIM_NAME.startswith("riviera"),
expect_error=DirectLenMismatch
if SIM_NAME.startswith(("icarus", "verilator", "xmsim", "modelsim"))
else (),
)
Copy link
Member

@ktbarrett ktbarrett May 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So only GHDL and NVC work here. Interestingly, they are VHDL-only simulators, I wonder if there's some reason for that? EDIT: Big dumb. This is Verilog only. So ALL simulators fail this test.

Is there really any reason to test or enforce this? Why do we care if iteration handles and access by name handles being the same? Is there some reason to not just remove this test?

@ian-l-kennedy
Copy link

ian-l-kennedy commented May 11, 2024

@marlonjames @AndrewNolte @ktbarrett:
Will the addition of this PR ensure simple struct elaboration into LogicObject in python is working for each simulator? I currently use xcelium, which elaborates the fields of a simple (no nesting) SV struct into dut.<some struct port>.<some member>. It works as you would expect, and I can loop through all the attributes available in Python for a dut.<some struct port> and see all the members.

Verilator though, using cocotb latest main and verilator 5.022 is not elaborating the struct members.

@marlonjames is this expected? I had thought you fixed this in past PRs - in all your work in all the comments and PRs linked here: #1275

@ktbarrett
Copy link
Member

@ian-l-kennedy

Will the addition of this PR ensure simple struct elaboration into LogicObject in python is working for each simulator?

No.

As stated in the issue you linked, this is a Verilator issue, and moreover a IEEE standards issue. Indexing into packed objects, structure or array, through the VPI is probably not standards compliant behavior (I'd have to find the exact section). Additionally, indexing into packed arrays doesn't work in any simulator (AFAIK), so any kind of complex packed data structure such as a packed array of packed structures can't be indexed into; only the very trivial case of a top-level packed struct.

#3608 attempts to allow the behavior in the other direction: getting the value of a top-level packed structure as a single value. Regardless of how that PR falls out, if you want to write portable standards compliant code, I'd stop relying on the ability to index into packed structures.

@ian-l-kennedy
Copy link

@ktbarrett,

Thanks for getting back to me.

If you follow all the links to understand the breadth of the work, you will see mentioned that @marlonjames also closed out issues in verilator/verilator. I was aware this would start as a verilator issue, then if resolved in verilator become a cocotb issue.

For example, there is confirmation of this here: #3237

Great work @marlonjames, thanks a lot for the many issue references and the work you did upstream with Verilator as well!

There is a wide net of linking from issue to issue, so I was having trouble finding the definitive answer to the question I had posted, so I thought to reach out.

I now see some more explicit evidence in the makefiles that verilator does not support struct constructs in the portmap/port signature.

# Verilator doesn't support structs (gh-1275) in this commit Verilator: Expect failures when accessing structs (see https://github.com/cocotb/cocotb/issues/1275)

@ktbarrett if you have a moment could you please point me to the standards that you are referencing here: Regardless of how that PR falls out, if you want to write portable standards compliant code, I'd stop relying on the ability to index into packed structures.. I am especially interested in removing SystemVerilog code constructs from my synthesizable code. Especially ones that are "against the grain" of any portability standard.

@ktbarrett
Copy link
Member

ktbarrett commented May 12, 2024

@ian-l-kennedy You can simply remove the packed from the structure definition to treat them like regular structures. If you are still having issues, that's probably just an issue with Verilator not supporting something it should yet, or hitting some corner case. It happens. #1275 is about packed structs as you can see in the example in the OP.

@ian-l-kennedy
Copy link

@ktbarrett if you have a moment could you please point me to the standards that you are referencing here: Regardless of how that PR falls out, if you want to write portable standards compliant code, I'd stop relying on the ability to index into packed structures.. I am especially interested in removing SystemVerilog code constructs from my synthesizable code. Especially ones that are "against the grain" of any portability standard.

@ktbarrett
Copy link
Member

Hmmm. So IEEE 1800-2017 section 37.18 states that we should be able to use vpiElement to dive into packed objects. We just aren't using vpiElement yet. Currently the few simulators that allow finding struct members are discovering them as a different kind of VPI object. I guess we need to detect packed objects and switch the kind of iteration we are doing.

I wonder how it's supposed to work with getting/setting values. The standard doesn't mention anything wrt to that =/

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

Successfully merging this pull request may close these issues.

None yet

4 participants