I defined an interface in system verilog and use bind statement to bind to internal RTL signals. I want to be able to force internal RTL signals through the interface. However, this is causing the RTL signal to go to 'x' if I don't force these signals explicitly, it seems bind to interface is having driving capability. I don't want RTL signal to change to 'x' when nothing is forcing it in this case, not sure what I am doing wrong here?
my code looks like this with DUT being the design:
interface myInf(
inout RTL_a,
inout RTL_b
);
bind DUT myInf myInf_inst(
.RTL_a(DUT.a),
.RTL_b(DUT.b)
);
bind DUT myDrv(myInf_inst);
where myDrv is a module which drives the ports on myInf.
In this case, DUT.a and DUT.b are internal RTL signals, they have their driver from design, but I want to be able to force them if needed. however, these signals becoming 'x' when I am just binding them to myInf without actually driving them.
The inout signals might be a non-net type. It is better to be explicit in the the declaration and define them as inout wire. Inside the interface, assign the nets to a logic and initialize the logics to z. A non-z value will apply a driver while a z will allow signals to drive. Example:
interface myInf(
inout wire RTL_a,
inout wire RTL_b
);
logic drv_a, drv_b;
initial {drv_a,drv_b} = 'z; // z means not driving
assign RTL_a = drv_a;
assign RTL_b = drv_b;
endinterface
There might be conflicting drivers, such as the normal drivers from the design. In this case you will need to override the driver. Assuming the signal being overrode is a net type, this is done by changing the assign statements to assign (supply1,suppl0) RTL_a = drv_a;. This is utilizing the Verilog concept of drive strength. Assigning to z will still all other drivers. Most nets are driven with a strength of strong1,strong0 which is weaker then supply1,supply0. Drive strength will not work for non-net types (e.g. logic & reg). These register/variable-types use a last-assignment-wins approach. Fore more on drive strength read IEEE Std 1800-2012 sections 28.11 through 28.15
Your sample code has some bugs. The pin connections for myInf_inst should use hierarchical references relative to its target scope. Unless there is an instance called DUT inside module DUT, then the DUT. should be omitted (See IEEE Std 1800-2012 ยง 23.11 Binding auxiliary code to scopes or instances). The bind statement for myDrv is missing an instance name. The code should be:
bind DUT myInf myInf_inst(
.RTL_a(a), // no DUT.
.RTL_b(b) // no DUT.
);
bind DUT myDrv myDrv_inst(myInf_inst);
sample code: http://www.edaplayground.com/x/2NG
Related
The SystemVerilog LRM (IEEE 1800-2017) describes ports in interfaces as follows:
One limitation of simple interfaces is that the nets and variables declared within the interface are only used to connect to a port with the same nets and variables. To share an external net or variable, one that makes a connection from outside the interface as well as forming a common connection to all module ports that instantiate the interface, an interface port declaration is required. The difference between nets or variables in the interface port list and other nets or variables within the interface is that only those in the port list can be connected externally by name or position when the interface is instantiated. Interface port declaration syntax and semantics are the same as those of modules (see 23.2.2).
What is the first sentence saying exactly? I don't see the limitation.
In the second sentence, what is an example of an external signal? How do you decided whether a signal should be declared inside the interface or as a port to the interface? The text used in the LRM just doesn't click for me.
The problem is shown with the simple_bus example that follows the section of the IEEE 1800-2017 SystemVerilog LRM you quoted.
There are two instances of the interface sb_intf1 and sb_intf2 each creating a unique set of internal signals (req, int, ...). If clk had also been declared as internal signal, there would also be two clock signals. What's not shown in the example is the code generating the clock signal. That could have been in the top module or another module. They would have needed to add continuous assignments to get the generated clock signal to each the internal clk in each interface instance.
By putting the shared signals in the interface in their port declarations, it makes it much easier to join the common signals.
interface simple_bus (input logic clk); // Define the interface
logic req, gnt;
logic [7:0] addr, data;
logic [1:0] mode;
logic start, rdy;
endinterface: simple_bus
module memMod(simple_bus a); // Uses just the interface
logic avail;
always #(posedge a.clk) // the clk signal from the interface
a.gnt <= a.req & avail; // a.req is in the 'simple_bus' interface
endmodule
module cpuMod(simple_bus b);
...
endmodule
module top;
logic clk = 0;
simple_bus sb_intf1(clk); // Instantiate the interface
simple_bus sb_intf2(clk); // Instantiate the interface
memMod mem1(.a(sb_intf1)); // Reference simple_bus 1 to memory 1
cpuMod cpu1(.b(sb_intf1));
memMod mem2(.a(sb_intf2)); // Reference simple_bus 2 to memory 2
cpuMod cpu2(.b(sb_intf2));
endmodule
I know how to link modules but could someone explain the flow of calling the modules to be used when I want it to be used.
Like have a state machine and depending on the state I can call a module to activate, or like if I need to repeat a process how to go back to a module earlier in a state machine.
again I get the instantiating part like this
wire clk;
wire sig;
wire out;
A a(clk, sig, topout);
B b(clk, sig);
endmodule
but can someone explain how to call modules and how the control flow works in general for them?
(I am new to HDLs so I appreciate any help)
Verilog is a language specifically developed to simulate behavior of hardware. Hardware is a set of transistors and other elements which always statically presented and function in parallel. Functioning of such elements could be enabled or disabled, but the hardware is still present.
Verilog is similar to the hardware in the sense that all its elements are always present, intended for parallel functioning.
The basic functional elements of Verilog are gates, primitives and procedural blocks (i.e., always blocks). Those blocks are connected by wires.
All those elements are then grouped in modules. Modules are used to create logical partitioning of the hardware mode. They cannot be 'called'. They can be instantiated in a hierarchical manner to describe a logical structure of the model. They cannot be instantiated conditionally since they represent pieces of hardware. Different module instances are connected via wires to express hierarchical connectivity between lower-level elements.
There is one exception however, the contents of an always block is pure software. It describes an algorithmic behavior of it and as such, software flow constructs are allowed inside always block (specific considerations must be used to make it synthesizable).
As it comes to simulation, Verilog implements an event-driven simulation mode which is intended to mimic parallel execution of hardware. In other words, a Verilog low level primitive (gate or always block) is executed only if at least one of its inputs changes.
The only flow control which is usually used in such models is a sequence of input events and clocks. The latter are used to synchronize results of multiple parallel operations and to organize pipes or other sequential functions.
As I mentioned before, hardware elements can be enabled/disabled by different methods, so the only further control you can use by implementing such methods in your hardware description. For example, all hardware inside a particular module can be turned off by disabling clock signal which the module uses. There could be specific enable/disable signals on wires or in registers, and so on.
Now to your question: your code defines hierarchical instantiation of a couple of modules.
module top(out);
output wire out;
wire clk;
wire sig;
A a(clk, sig, out);
B b(clk, sig);
endmodule
Module 'top' (missing in your example) contains instances of two other modules, A and B. A and B are module definitions. They are instantiated as corresponding instances 'a' and 'b'. The instances are connected by signals 'clk', which is probably a clock signal, some signal 'sig' which is probably an output of one of the modules and input in another. 'out' is output of module top, which is probably connected to another module or an element in a higher level of hierarchy, not shown here.
The flow control in some sense is defined by the input/output relation between modules 'A' and 'B'. For example:
module A(input clk, input sig, output out);
assign out = sig;
...
endmodule
module B(input clk, output sig);
always#(posedge clk) sig <= some-new-value;
...
endmodule
However, in general it is defined by the input/output relation of the internal elements inside module (always blocks in the above example). input/output at the module port level is mostly used for semantic checking.
In the event-driven simulation it does not matter hardware of which module is executed first. However as soon as the value of the 'sig' changes in always#(posedge clk) of module 'B', simulation will cause hardware in module 'A' (the assign statement to be evaluated (or re-evaluated). This is the only way you can express a sequence in the flow at this level. Same as in hardware.
If you are like me you are looking at Verilog with the background of a software programmer. Confident in the idea that a program executes linearly. You think of ordered execution. Line 1 before line 2...
Verilog at its heart wants to execute all the lines simultaneously. All the time.
This is a very parallel way to program and until you get it, you will struggle to think the right way about it. It is not how normal software works. (I recall it took me weeks to get my head around it.)
You can prefix blocks of simultaneous execution with conditions, which are saying execute the lines in this block when the condition is true. All the time the condition is true. One class of such conditions is the rising edge of a clock: always #(posedge clk). Using this leads to a block of code that execute once every time the clk ticks (up).
Modules are not like subroutines. They are more like C macros - they effectively inline blocks of code where you place them. These blocks of code execute all the time any conditions that apply to them are true. Typically you conditionalize the internals of a module on the state of the module arguments (or internal register state). It is the connectivity of the modules through the shared arguments that ensures the logic of a system works together.
I have a question on `default_nettype directive of SystemVerilog.
By default, the following code is ok.
module m1 (
input logic i1,
output logic o1
);
logic l1;
assign l1 = i1;
assign o1 = l1;
endmodule
However, when I change the default net type to none:
`default_nettype none
only i1 causes an error:
ERROR: [VRFC 10-1103] net type must be explicitly specified for i1 when default_nettype is none ...
My question is why only input logic i1 causes an error and requires explicit wire, but output logic o1 and logic l1 does not.
Verilog has too many implicit rules to accommodate lazy programmers (i.e. people who were interested in designing hardware, not writing software)
This error is explained in section 23.2.2.3 Rules for determining port kind, data type, and direction
For the first port in an ANSI style port list:
If the port kind is omitted:
For input and inout ports, the port shall default to a net of default net type. The default net type can be changed using the
`default_nettype compiler directive
This implicit 'net' port rule is the opposite of what is used when declaring output ports, and all other declarations outside of ports. The reason behind this is that input ports are an overwhelmingly majority of ports used in a module, and keeping ports connections as wires allows for port collapsing, which is more efficient for simulation.
This is the confusing part of SystemVerilog. Your code works on my simulator, that outputs a warning instead of an error.
If you dive enough in help messages you get that the "type" of the identifier (as in net versus var, opposing to "datatype" which is logic or whatever else) is context sensitive, and specifically input ports are by default nets, while output ports are by default variables. This means that with "default_nettype none" all your input ports are effectively not fully described, because the compiler does not know the resolution function for the net (you might want a wand, for example). Your output ports, being variables, need no resolution function and so no error is thrown there.
Since you cannot really connect the same port to more than one signal unless you really try to this seems redundant to me, but it might be needed due to net coercion rules for elaboration if the input net is driven by more than one assign elsewhere in the design.
My understanding is that "default_nettype none" is mostly used to ensure you do not have undeclared identifiers (leading to width mismatch due to single bit inference) and a port is declared, so you might check if your tool has the option of inferring a wire for ports anyway (again, my simulator outputs a warning and does this by default, and the synthesizer does not complain either).
Other than that, the only workaround I see is going for "default_nettype none" first thing after the ANSI port declaration and "default_nettype wire" last thing before endmodule, in every module.
We cannot do that, as per 1800-2017 22.8:
The directive `default_nettype controls the net type created for implicit net declarations (see 6.10). It can be used only outside design elements.
The reference for implicit net declaration is section 6.10 in IEEE 1800-2017, although following the mentioned sections from there seems to point to non-ANSI declarations only... you might need a deeper dive to fully understand the matter.
I have a module that is instantiated many times in other modules. Two of the inputs to this module are used very rarely, and to avoid code bloat I don't want to have to connect them in every instantiation. Is there a way to mark these two ports to the compiler to indicate they can be left unconnected?
eg.
module mymod(input logic foo, unused1, unused2, output logic out);
//...
endmodule
module top(...);
//...
mymod(.foo(1'b0));
endmodule
will not compile due to port mismatch errors. How can I change the code so unused1 and unused2 don't need to be connected?
Yes, you can specify a default value for a unconnected port (See 23.2.2.4 Default port values in the 1800-2017 LRM)
module mymod(input logic foo, unused1='0, unused2='0, output logic out);
//...
endmodule
Another option is to explicitly leave these ports unconnected when instantiating.
mymod(.foo(1'b0), .unused1(), .unused2() );
But in either case, your tool may have specific requirements with unconnected ports that you will have to deal with as they ask you to do.
I was reading UVM cookbook and I got confused about virtual interface connection in between monitor, driver and their BFM. Does it mean there could be multiple driver or monitor, or this is independent of interfacing that does not know either its monitor or driver. Can anybody help?
The keyword virtual is re-used a number of times in SystemVerilog. The interface is virtual in the sense that its hierarchical path is set at runtime by passing it through a variable. All other connections in Verilog/SystemVerilog are fixed paths.
This does indeed allow you to have multiple instances of the same driver code connect to multiple interface instances. It also helps in block-to-system reuse so you can change the hierarchical path as the interface gets deeper into your system level.
Verilog was not created as a programming langue, more over, it was not suitable for object oriented programming. On the other hand, System verilog test bench language was created as an object oriented programming language.
One of the issues is to semantically connect HDL verilog with TB. All verilog HDL/RTL objects are statically compiled and cannot be manipulated dynamically (which is needed at TB). You cannot get pointers to modules, variables, ... (well except through some back-door PLI mechanism).
So, System verilog came up with the interface construct, which was intended as a connectivity object in RTL world. It is similar to a module in a sense, that it is a compile-time static object. But, SV also added a trick, which allows you to have a reference to an interface. The trick is called virtual interface.
From the point of view of a programmer, you can think of it as a reference, or a pointer to the static interface object. This gives you an ability to pass this reference to different TB class, or create multiple references tot he same interface.
Here is a schematic example:
class Transaction;
virtual dut_if trans; // <<< virtual interface
function new(virtual dut_if t);
trans = t; // <<<< assign it to trans
endfunction // new
endclass // Transaction
// definition of the interface
interface dut_if import trans_pkg::*;
(input trans_t trans);
endinterface
// instantiate the interface in module 'rtl'
bind rtl dut_if dut_if(data);
program tb;
// pass it to the constructor as a virtual interface pointer.
Transaction trans1 = new (rtl.dut_if);
Transaction trans2 = new (rtl.dut_if);
endprogram // tb