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

AUTOs for wrapping a module around an interface #539

Closed
veripoolbot opened this issue Jul 27, 2012 · 9 comments
Closed

AUTOs for wrapping a module around an interface #539

veripoolbot opened this issue Jul 27, 2012 · 9 comments
Assignees
Labels

Comments

@veripoolbot
Copy link
Collaborator


Author Name: Brad Dobbie
Original Redmine Issue: 539 from https://www.veripool.org
Original Date: 2012-07-27
Original Assignee: Wilson Snyder (@wsnyder)


As our team transitions to a SV/UVM-based verification environment, we have found our method of connecting interfaces to the RTL to be somewhat cumbersome. Sounds like a job of AUTOs...

The basic ideas is to create a wrapper module around an interface, which then can be instantiated either within the dut (monitor) or in the testbench (driver). Doing so will make available the powerful AUTOINST/AUTO_TEMPLATE/AUTOWIRE constructs, which we are very familiar with.

Verilog-mode will look into the interface definition to find:

  • signal names
  • signal widths
  • modports
  • clocking blocks
  • signal directions for each modport

We propose 2 new AUTO functions:

/AUTOIFACEPORTS("intf_name" "modport" "regexp" "?prefix?")/

/AUTOIFACEASSIGN("intf_name" "intf_inst" "modport" "regexp" "?prefix?")/

Here are examples of what might be produced:

  /*AUTOIFACEPORTS("auto_intf" "req_mon_mp")*/
  // Beginning of ...
  input                 req_credit,             // To auto_i of auto_intf.sv
  input [63:0]          req_data,               // To auto_i of auto_intf.sv
  input                 req_val,                // To auto_i of auto_intf.sv
  // End of ...

    /*AUTOIFACEASSIGN("auto_intf" "auto_i0" "req_mon_mp")*/
    // Beginning of ...
    assign auto_i.req_credit         = req_credit;
    assign auto_i.req_data           = req_data;
    assign auto_i.req_val            = req_val;
    // End of ...

Please find the following files attached:

  • auto_intf.sv
  • auto_moudle.v
@veripoolbot
Copy link
Collaborator Author


Original Redmine Comment
Author Name: Wilson Snyder (@wsnyder)
Original Date: 2012-07-27T21:10:18Z


Do you think it should look for @() in the clocking block and imply that they are inputs?

@veripoolbot
Copy link
Collaborator Author


Original Redmine Comment
Author Name: Brad Dobbie
Original Date: 2012-07-30T18:54:51Z


My initial opinion would be no, unless the @() can be driven with an assign statement. I'll try it out and report back.

I found something tricky. The signal 'rst_n' is defined as an input to the interface, as well as a signal in the each modport (or clocking block). If AUTOIFACEASSIGN creates an assign statement for that signal, that's a syntax error.

    assign auto_i.rst_n              = rst_n;

Error-[VIPCBD] Variable input ports cannot be driven
project/verif/vkits/iox/auto_module.v, 40
  Variable input ports cannot be driven.
  The input variable port "rst_n" of interface "auto_intf" cannot be driven.
  Source info: assign auto_module.auto_i.rst_n = rst_n;
  Interface 'auto_intf' is defined at "project/verif/vkits/iox/auto_intf.sv", 
  4

AUTOIFACE* should probably not generate ports/assigns for modport signals that are already declared as interface modports. The other option would be to declare such usage illegal; I'll check with some verification engineers to see why one would want do this.

@veripoolbot
Copy link
Collaborator Author


Original Redmine Comment
Author Name: Brad Dobbie
Original Date: 2012-07-31T16:23:19Z


Brad Dobbie wrote:

My initial opinion would be no, unless the @() can be driven with an assign statement. I'll try it out and report back.

I have confirmed that the @() can be driven with an assign statement, but only if they are not inputs to the interface. I think there's value in differentiating between interface inputs and modport inputs. Signals like clock and reset are not really part of the "interface" or "bus", and you probably don't wan't users to be required to put them in a modport just so they can be auto-connected with assigns.

In my example, I have rst_n declared as an input in both req_mon_cb and rsp_drv_cb. While there may be a better way to handle this, it is a legal thing to do and should be supported. So, let's only generate assigns for modport signals (explicitly listed as input/output) that are not inputs/outputs of the interface.

@veripoolbot
Copy link
Collaborator Author


Original Redmine Comment
Author Name: Brad Dobbie
Original Date: 2012-07-31T16:28:36Z


A verification engineer made a request that AUTOIFACEWIRE (and AUTOIFACELOGIC) be supported such that AUTOIFACEASSIGN could be used without a wrapper module. Similar to how AUTOWIRE (and AUTOLOGIC) work, AUTOIFACEWIRE would be used in place of AUTOIFACEPORT. Only drivers (modport outputs) would be declared.

  /*AUTOIFACEWIRE("auto_intf" "rsp_drv_mp")*/
  // Beginning of ...
  wire [1:0]            rsp_cmd;                // From auto_i of auto_intf.sv
  wire [63:0]           rsp_data;                // From auto_i of auto_intf.sv
  // End of ...

@veripoolbot
Copy link
Collaborator Author


Original Redmine Comment
Author Name: Clifford Cummings
Original Date: 2012-07-31T17:53:12Z


Hi, Wilson -

Per your email, allow me to offer advice on this topic. Unless I misunderstand Brad's request, I don't think the request is a good approach. Allow me offer some OVM/UVM usage insight.

The usage of interfaces and virtual interfaces is rather poorly documented in existing materials.
The idea is to do the following:
(1) You have a DUT module (nothing new here) with ports.
(2) You create an interface with all the same signals as the DUT ports. There are two methods to consider (discussed below).
(3) There is a top module that instantiates the interface and the DUT.
(a) method #1 is to use named ports on the DUT that connect hierarchically to the internal interface signals. This becomes a rather large list of hierarchical port connections. The interfaces internal signals should all be of the logic type unless the DUT port is bidirectional, in which case you will need the inout signals to be declared as wires in the interface (every other input/output signal is of type logic).
(b) method #2 is to define the interfaces with all of the signals declared as interface inputs, outputs, and inouts. Advantage: .* will connect all of the DUT ports to the interface ports. Disadvantage: all of the signals must also be declared in the top module (DUT inputs/outputs as logics and inouts as wires). Method #2 might lend itself nicely to your autos. The Autos could make all of the top module declarations, and declare an interface with DUT outputs declared as interface inputs, DUT inputs declared as interface outputs, and DUT inouts declared as interface inouts.

I personally disagree with declaring two clocking blocks as shown by Brad. I would only declare one clocking block per clock domain and the UVM driver only drives the DUT inputs and inouts, and the UVM monitor samples the DUT outputs and inouts. I see no compelling reason to separate the drives and samples into two separate clocking blocks and to maintain more than one clocking block.

I also disagree with adding modports to an interface that is only used by a testbench. Adding modports does give minimal additional checks to make sure that the ports are connected correctly, but those bugs are found quickly without the modports and the maintenance and additional reference to the modport-name from the driver and monitor are a pain and not worth the additional checking. If you have declared all internal interface signals to be of type logic, the SystemVerilog compiler will report a compilation error you if you ever accidentally drive the same logic signals from more than one source.

The only reason we even use an interface in an OVM/UVM verification top-module is because an interface instance name can be passed as a handle to the class-based driver and monitor. If we could pass a module name as a handle (illegal in SystemVerilog), then we could pass the DUT module handle directly to the testbench and there would be little need to use the interface. The driver and monitor talk directly to the internal interface signals; hence, they are talking directly to the DUT signals through the interface connections. That's why the interface and virtual interface are used in a verification environment (including OVM and UVM).

Last comment, I don't particularly like Brad's assertion handling with the interface always block. I would use the simple macros that I have described in my Assertions paper:
http://www.sunburst-design.com/papers/CummingsSNUG2009SJ_SVA_Bind.pdf
Add long labels to the assertions and concatenate most of the $isunknown signals into a single test. Brad might want to do a couple of the tests separately. Brads tests have the singular advantage of printing separate error messages for each type of unknown. Using the assertions (and placing them into bind files) have the advantages:
(1) assertions can be disabled by command line options
(2) assertions in bind files can be omitted if not used
(3) Error messages only show up in transcript windows and not in the waveform viewer. Labeled assertions (with long descriptive labels) show up in the default error messages and also show up in the waveform viewer for easy debugging. Assertions can be easily expanded in the waveform viewer to show all tested signals and easily identify the offending unknown signal(s).
(4) Easy to add another signal to the concatenated list of signals to be tested for unknowns.
(5) Much more concise coding style

Summary:
(Method #1) Your autos could either instantiate the DUT into the top module with all of the hierarchical references to the internal interface signals
(Method #2) Your autos could update the declarations in the top module, and declare the interface with appropriate inputs/outputs/inouts and then .* can make the connections. You already have a cool trick to expand .* connections for debugging and I credit you with that technique and show it in all of my SystemVerilog Fundamentals training classes.

Side note: OVM & UVM are exceptionally hard to learn from existing documentation. I had to learn the hard way and I believe I do a nice job explaining the important concepts in simple terms to help engineers understand why-tha-heck OVM and UVM are built the way they are. I have multiple clarification papers in the pipeline that I plan to deliver at future conferences. Watch this space!

Hope this helps.

Regards - Cliff
Verilog & SystemVerilog Guru
www.sunburst-design.com

@veripoolbot
Copy link
Collaborator Author


Original Redmine Comment
Author Name: Brad Dobbie
Original Date: 2012-07-31T18:30:35Z


Thanks for the detailed reply, Cliff. Let me take your suggestions back to the verification team and see what they say. I'm an RTLer (who hasn't worked in UVM) trying to improve the way we connect up interfaces.

We currently are doing method #1, but that breaks down when trying to monitor signals internal to the design (e.g. bus between two blocks in a full-chip testbench). Ideally, we'd like to be able to instantiate monitors within the design to solve that issue, so we don't have to drop down interfaces at the top of each testbench that contains a particular block. Drivers will always be outside the design. Declaring the signals as ports of the interface seems to have the disadvantage of not being able to use the same interface for driving and monitoring (due to the single direction of ports).

I agree wholeheartedly with your comment about the assertions. We use labels and macros to declare assertions within the RTL team, as you suggest. I'll pass the suggestion along as the verification team refines their methodology. I haven't heard of the bind concept, so I'll have to read up on that.

@veripoolbot
Copy link
Collaborator Author


Original Redmine Comment
Author Name: Clifford Cummings
Original Date: 2012-07-31T20:57:53Z


Brad Dobbie wrote:

We currently are doing method #1, but that breaks down when trying to monitor signals internal to the design (e.g. bus between two blocks in a full-chip testbench). Ideally, we'd like to be able to instantiate monitors within the design to solve that issue, so we don't have to drop down interfaces at the top of each testbench that contains a particular block.

Actually, neither method is designed to handle monitoring of internal signals.

I can think of two approaches to this situation.
(1) The interface can declare an interface-local bus and then use a continuous assignment to assign the internally referenced hierarchical bus to the interface bus - now the interface bus can be monitored.
(2) Compliant SystemVerilog simulators should allow you to again declare an interface-internal bus, then use the clocking blocking alias mechanism to declare the interface-internal bus as an input of the form: input tempbus = top.dut.subblk.bus1. This sets up an alias so that the monitor can access the tempbus as if it were the internal bus itself.

When referencing internal buses, there is no automatic method to gain access and it will be necessary to hierarchically list all internal signals.

Brad Dobie wrote:

Drivers will always be outside the design. Declaring the signals as ports of the interface seems to have the disadvantage of not being able to use the same interface for driving and monitoring (due to the single direction of ports).

Whether the signals are listed as internal interface signals or interface port signals makes no difference. In either case, the OVM/UVM testbench will access the interface internal signals, which could be added to the interface by input/output/inout declarations. The only reason to list the interface signals as interface ports is so that the DUT ports can be automatically connected to the interface ports using implicit .* connections.

Method #1 seems to be generally preferred in industry, but as I examine large DUT designs, I wonder if it is easier to do the port connections as opposed to listing all of the named hierarchical connections. This is a topic that I am still exploring.

Regards - Cliff

@veripoolbot
Copy link
Collaborator Author


Original Redmine Comment
Author Name: Brad Dobbie
Original Date: 2012-08-01T20:50:50Z


One of the major concepts we utilize is "vertical reuse", which means that a given interface should be able to be used across testbenches as you work up the hierarchy (block_tb, group_tb, fullchip_tb, etc.). Using hierarchical references inside the interface prevents vertical reuse, because the hierarchical references will change from testbench to testbench.

Another problem with internal hierarchical references comes with multiply instantiated blocks. Imagine you have multiple identical processing engines that you need to monitor. If you have internal hierarchical references to signals, you'd need each interface to be different. Perhaps you could case on an engine_id input to select the right hierarchical reference, but that's ugly. There's benefit to placing the monitor inside the processing engine so that you can define the connections once, and get those connections inherently every time you instantiate the engine. This isn't true if you place N interfaces at the top of testbench, then connect the signals N times via hierarchical references.

If we want a single interface to be able to be used as a monitor or a driver, then I think you are suggesting that we declare the signals as inouts of the interface as well as inouts in the clocking block. I need to play around with this concept. I believe AUTOWIRE creates a declaration for inouts when using AUTOINST (or .*), so it might do what I want. Unfortunately, there's a bug with AUTOINSTing an interface (http://www.veripool.org/issues/540).

While this may not have been the intended use of clocking blocks, declaring one for "driver" and one for "monitor" allows us to specify the information inside the interface about how it should be (automatically) connected. We could investigate if the modports are actually needed or not in the original example. If the extra clocking blocks serve a purpose (to provide directional information), is there any disadvantage to having them? I'm not concerned with the maintenance of multiple clocking blocks if that means we don't have to maintain two separate interfaces.

Thanks,
Brad

@veripoolbot
Copy link
Collaborator Author


Original Redmine Comment
Author Name: Wilson Snyder (@wsnyder)
Original Date: 2012-08-19T23:33:21Z


Ok, thanks for the feedback all. It seems like the original proposal is still relevant, so rev813 adds AUTOINOUTMODPORT and AUTOASSIGNMODPORT. (In general the auto-naming is AUTO{what_it_makes}{what_it_comes_from}. See the appropriate new documentation for examples.

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

2 participants