Is there a way to dump a memory content of a memory that is used in a class instead of using $writememh() system task? I want to check for memory content in the predictor class of a UVM environment used in a UVM scoreboard. I am using 2016 Synopsys VCS simulation vendor
E.g. say I have a predictor:
class my_predictor extends uvm_subscriber#(my_item);
`uvm_component utils(my_predictor)
logic [15:0] mem [512]; // Want to observe the change of this mem content
...
/* local logics, constructor, phase(s) and write function definitions */
...
endclass : my_predictor
I have tried the following:
task run_phase(uvm_phase phase);
forever begin
$vcdplusmemon(mem);
endtask : run_phase
But I was unable to observe it even when I load vcdplus.vpd. I then setup for simv in DVE and then ran. When I tried to view the waveform of my_predictor.mem, DVE crashed.
My motivation for doing this is to compare my predicted change of memory content with the actual memory content of the DUT I am testing via a constrained randomized test.
Is it a vendor tool feature limitation?
Related
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 testbench in Vivado which has a hierarchy of IP--some custom IP and some Xilinx IP, like the Zynq Processing System. The Zynq Processing System also has an associated Verification IP library that has useful API for doing things like loading DDR.
I would like to write a task which leverages the Zynq Verification IP (and associated API) inside it. I can't figure out how I would implement this in my testbench? I am new to SV, and am guessing that I need to pass the zynq processing system object as an argument so I can access it's API inside my super-task.
Updated example of what I'm trying to do in my testbench. I realize this isn't proper SystemVerilog, it's just to demonstrate the functionality I'm trying to obtain. TOP is a module defined in some other .sv file that contains the definition of a task called T:
module tb();
TOP TIPTOP(), TIPITTYTOP();
task myTask(input TOP T);
begin
T.T;
end
endtask
initial begin
myTask(TIPTOP);
myTask(TIPITTYTOP);
end
endmodule
Another answer to the updated question
This can only be done if the module TOP is not a module but a different flavour of module, called an interface. There is a special kind of SystemVerilog variable called a virtual interface which is a variable that can store a reference to the instance of an interface. This is what you need here. So,
you need to make TOP an interface and
you need to add the keyword virtual to your task: task myTask(input virtual TOP T);
There are restrictions on an interface, however. (We are not quite using it for its normal purpose here.) The main one which might affect you is that you cannot instantiate a module inside an interface.
https://www.edaplayground.com/x/SM33
interface TOP;
task T;
$display("TOP.T");
endtask
endinterface
module tb();
TOP TIPTOP(), TIPITTYTOP();
task myTask(input virtual TOP T);
begin
T.T;
end
endtask
initial begin
myTask(TIPTOP);
myTask(TIPITTYTOP);
end
endmodule
You can call a task or function that is declared in another module. The code below has the following structure:
P
TOP ANOTHER_TOP
| |
BOT bot BOT bot
Package P and all modules have a task T declared in them. I can call all of the tasks from module TOP:
I can call the task in the package P using the score resolution operator, :::
P::T;
I can call the local task:
T;
I can call the task in the instance bot of BOT:
bot.T;
I can call the task in the other top-level module, ANOTHER_TOP:
ANOTHER_TOP.T;
I can call the task in the instance bot of BOT in the other top-level module:
ANOTHER_TOP.bot.T;
Notice how I have declared the various a tasks and modules "in the wrong order". This is OK, because Verilog takes 3 passes to compile and the relationships between the various tasks and modules are sorted out in passes 2 and 3. The package, however, has to be compiled first. This is because packages are a bit of an after-thought in the grand scheme of things.
https://www.edaplayground.com/x/KpJR
package P;
task T;
$display("P::T");
endtask
endpackage
module TOP;
initial
begin
T;
ANOTHER_TOP.T;
bot.T;
ANOTHER_TOP.bot.T;
P::T;
end
task T;
$display("TOP.T");
endtask
BOT bot ();
endmodule
module ANOTHER_TOP;
task T;
$display("ANOTHER_TOP.T");
endtask
BOT bot ();
endmodule
module BOT;
task T;
$display("BOT.T");
endtask
endmodule
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
I have written an UVM testbench that has 3 agents and am now in the process of writing a scoreboard/checker. I need to have a checker module for my SystemVerilog Assertions, but this checker module needs to be aware of register configuration that is done from the test (and can be random, decided during run_phase of the test).
I am unable to figure out how this would work? If I were to create a checker module for my assertions, and bind it at the top level (tb_top) to the dut, how does this checker module know my register configuration?
After reading some papers, I figured I could write my checker module as an interface, set it in tb_top. But this would give access to the variables in my interface to the UVCs. How does the interface access variables in the UVCs?
Any help is appreciated. I feel I am missing something key here as this has probably been done plenty of times before.
EDIT: Please don't tell me I have to implement some kind of API to set each individual register setting from my UVCs? I want to just get a handle to my reg_block (or any other config variable in my agents)
It seems that you want to pass information from tb_top to your UVC or vice versa. This information will be used by your assertion in tb_top, and shared by your UVC. My suggestion, you can either use uvm_resource_db or uvm_config_db.
I can think of two ways of achieving this communication.
First method is set the configuration in your tb_top, then your UVC grab this handle. From here on, you can communicate your register or whatever info you need for your assertion.
class my_tb_config extends uvm_object;
// ...
endclass
module tb_top;
my_tb_config tcfg;
initial begin
tcfg = new("tcfg");
uvm_config_db#(my_tb_config)::set(uvm_root::get(), "*", "my_tb_config", tcfg);
end
endmodule
// somewhere in your UVC
class my_uvc extends uvm_component;
my_tb_config tcfg;
function void build_phase(uvm_phase phase);
// now both tb_top and your UVC point to the same config object
void'(uvm_config_db#(my_tb_config)::get(this,"","my_tb_config", tcfg));
endfunction
endclass
Another method is the other way around. Pass your UVC configuration to your tb_top.
class my_other_uvc extends uvm_component;
my_tb_config tcfg;
function void build_phase(uvm_phase);
tcfg = new("tcfg");
uvm_resource_db#(my_tb_config)::set("*", "my_tb_config", tcfg);
endfunction
endclass
// somewhere in your tb_top
module tb_top;
my_tb_config tcfg;
initial begin
#1ps; // small delay, making sure resource is submitted
void'(uvm_resource_db#(my_tb_config)::read_by_name("*","my_tb_config",tcfg);
// Now both your tb_top and UVC share same object, so you can freely define your whatever communication between them
end
endmodule
I figured out a way to do this. Firstly, I realized that I had asked two separate questions:
1) My checker module needs to be aware of register configuration that is done from the test
I use cross module reference into my design to access my registers, and this provides me with up-to date register configuration as set by the test during the run-phase.
tb.sv
module tb;
dut my_dut( ... )
interface my_checker (
.input_registerA (tb.my_dut.my_sub_module.regA),
.input_registerB (tb.my_dut.my_sub_module.regB),
.input_registerC (tb.my_dut.my_other_sub_module.regC),
....
)
endmodule
my_checker.sv
interface my_checker (
input input_registerA,
input input_registerB,
input input_registerC,
....
);
// Here I can write properties/assertions that are register-aware
endinterface
2) How does the interface access variables in the UVCs?
This is a little trickier. I want to dynamically update my checker variables from the uvm_sequence or uvm_monitor etc.
I read this paper by Verilab that clearly describes the method to do this:
http://www.verilab.com/files/litterick_sva_encapsulation.pdf
In my checker module, I create a uvm_component. From this component, I now have access to the uvm_resource_db, through which I can exchange info with my UVM-testbench.
One thing to remember is that the uvm_component instantiated in the checker module is located at the top-level (uvm_root).
I want to write some tasks in a package, and then import that package in some files that use those tasks.
One of these tasks toggles a reset signal. The task is reset_board and the code is as follows:
package tb_pkg;
task reset_board(output logic rst);
rst <= 1'b0;
#1;
rst <= 1'b1;
#1;
rst <= 1'b0;
#1;
endtask
endpackage
However, if I understand this correctly, outputs are only assigned at the end of execution, so in this case, the rst signal will just get set to 0 at the end of the task's execution, which is obviously not what I want.
If this task were declared locally in the module in which it is used, I could refer to the rst signal directly (since it is declared in the module). However, this would not allow me to put the task in a separate package. I could put the task in a file and then `include it in the module, but I'm trying to avoid the nasty complications that come with the way SystemVerilog handles includes (long-story-short, it doesn't work the way C does).
So, is there any way that the task can drive an output with different values across the duration of its execution without it having to refer to a global variable?
A quick solution is to use a ref that passes the task argument by reference instead of an output argument that is copied after returning from the task.
task reset_board(ref logic rst);
There are a few drawbacks of doing it this way. You can only pass variables of matching types by reference, so when you call reset_board(*signal*), signal cannot be a wire. Another problem is you cannot use an NBA <= to assign a variable passed by reference, you must use a blocking assignment =. This is because you are allowed to pass automatic variables by reference to a task, but automatic variable are not allowed to be assigned by NBAs. There is no way for the task to check the storage type of the argument passed to it.
Standard methodologies like the UVM recommend using virtual interfaces or abstract classes to create these kinds of connections from the testbench to the DUT. See my DVCon paper for more information.