system verilog- Mail Box - system-verilog

Below I have posted the complete code for mailbox. It has a class Generator, class Driver and there is a top level code. My question is in the below code, inside Class Generator, how Transation tr and mailbox mbx are used? Also, in function new how mbx is used?
program mailbox_example(bus_if.TB bus...);
class Generator; // Class Generator
Transaction tr;
mailbox mbx;
function new (mailbox mbx);
this.mbx=mbx;
endfunction
task run;
repeat (10) begin
tr=new;
assert(tr.randomize);
mbx.put(tr); //send out transaction
end
endtask
endclass
class Driver; //Class Driver
Transaction tr;
mailbox mbx;
function new(mailbox mbx);
this.mbx=mbx;
endfunction
task run;
repeat(10) begin
mbx.get(tr);
#(posedge busif.cb.ack);
bus.cb.kind<=tr.kind;
...
end
endtask
endclass
mailbox mbx; //Top level
Generator gen;
Driver drv;
initial begin
mbx=new;
gen=new(mbx);
drv=new(mbx);
fork
gen.run();
drv.run();
join
end
endprogram

Mailbox is a medium, used to communicate between two blocks.
Why mailbox is in constructor..??
To communicate between two blocks, like driver and generator, there should be a common mailbox between them (like a common cable is used to carry information from DTH antenna to set-top box).
To share a common mailbox between Driver and Generator, the mailbox is instantiated in top level block (e.g. Env) and its handle is passed to the driver and generator through a constructor.
How transaction and mailbox are used?
A mailbox has default methods to store and get transactions like put(), try_put(), get(), try_get(), peek() and try_peek(). In generator, the transaction is stored into mailbox using put() or try_put() method. In driver, the transaction is retrieved using get(), try_get(), peek() or try_peek() method.
Please refer Section 15.4 Mailboxes in SV LRM for more details. :)

Related

How to get the read signals in the sequence from driver in pipeline style UVM?

I'm trying to get read data (HRDATA) from a driver in the sequence or test.
This is my driver:
////////// Pipelined UVM Driver //////////
class ahb_pipelined_driver extends uvm_driver #(ahb_seq_item);
`uvm_component_utils(ahb_pipelined_driver)
/// Virtual Interface
virtual ahb_interface ahb_if;
/// Constructor
....
function void build_phase(uvm_phase phase);
super.build_phase(phase);
...
endfunction
forever begin
ahb_if.HADDR <= req.HADDR;
ahb_if.HWRITE <= req.HWRITE;
ahb_if.HBURST <= req.HBURST;
req.HRDATA = ahb_if.HRDATA;
req.HRESP = ahb_if.HRESP;
ahb_if.HWDATA <= req.HWDATA;
end
// Return the Request as Response
seq_item_port.put(req);
end_tr(req);
end
endtask:
endclass:
I read the read data from req.HRDATA = ahb_if.HRDATA; in the driver, and I can check the value.
But, the problem is that I'd like to send read data to the sequence but this sequence immediately finished after I call seq_item_port.get() in the driver. So I can’t both wait for the read data to be available and have pipelined operation.
I want to send the read data to the sequence or test from the driver. How am I supposed to do that?
You are not following the recommended UVM approach.
The driver should not sample the "read" data and check it. You should create a UVM agent with a driver and a monitor. The monitor should sample the read data and send it to a scoreboard for checking.
I recommend that you change your driver such that it no longer tries to sample the read data. Then create a UVM monitor which collects all AHB transactions (reads and writes) and sends them to a scoreboard.
See also:
Basic UVM example
UVM AHB example on EDA Playground

Transaction automation with certain condition in smart contract

I am developing and blockchain gaming service now and I want to send some tokens to a winner automatically at the end of game, and at specific times.
/*
* This function should be called every X days
*/
function sendTokens() public {
// We send some tokens to an array of players
}
Currently, I am doing this using traditional Backend technologies such as setInterval and WebSocket - however, this is a centralized method.
What is the best way to do that? What is the professional way?
Every state-change that happens on-chain needs to be triggered by a transaction. Therefore, to run a smart contract function on a schedule, after a specific event, or some other if-else trigger, you need someone to spend gas.
With that being said, you have two options:
1. Decentralized Automation
You can use a decentralized network of oracles to call smart contracts. Using Chainlink Keepers can call the function you specified at any time or event trigger focused. The oracles pay the gas associated with the call, and you pay for a subscription model to the Chainlink nodes.
The way, you're contract stays decentralized all the way down to the automation level, and you never have to worry about a centralized actor intentionally not sending a transaction.
You'd setup the trigger using checkUpkeep by defining in your contract what event you want to wait for (like time based, some event based, etc), and then what you want to do when that trigger is hit in performUpkeep.
An example would look as such:
This contract runs performUpkeep every interval seconds.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;
// KeeperCompatible.sol imports the functions from both ./KeeperBase.sol and
// ./interfaces/KeeperCompatibleInterface.sol
import "#chainlink/contracts/src/v0.8/KeeperCompatible.sol";
contract Counter is KeeperCompatibleInterface {
/**
* Public counter variable
*/
uint public counter;
/**
* Use an interval in seconds and a timestamp to slow execution of Upkeep
*/
uint public immutable interval;
uint public lastTimeStamp;
constructor(uint updateInterval) {
interval = updateInterval;
lastTimeStamp = block.timestamp;
counter = 0;
}
function checkUpkeep(bytes calldata /* checkData */) external view override returns (bool upkeepNeeded, bytes memory /* performData */) {
upkeepNeeded = (block.timestamp - lastTimeStamp) > interval;
// We don't use the checkData in this example. The checkData is defined when the Upkeep was registered.
}
function performUpkeep(bytes calldata /* performData */) external override {
//We highly recommend revalidating the upkeep in the performUpkeep function
if ((block.timestamp - lastTimeStamp) > interval ) {
lastTimeStamp = block.timestamp;
counter = counter + 1;
}
// We don't use the performData in this example. The performData is generated by the Keeper's call to your checkUpkeep function
}
}
Other options include Gelato.
2. Centralized Automation
You can also have "traditional" infrastructure call your functions on a schedule, just keep in mind this means that you are relying on a centralized actor in your contracts.
There are forms of centralized automation that you can take such as:
Your own server/set of scripts
Openzeppelin Defender
Tenderly
Incentive Mechanisms for people to call your functions
etc
Disclaimer: I work at Chainlink Labs

UVM end of test

In case I want to end the simulation from my monitor (I know that it is not the recommended way) how can I do this?
lets say I got this code inside my monitor:
Virtual task monitor_run();
fork
forever begin
.....
end
forever begin
.....
end
forever begin
.....
end
join
endtask : monitor_run
Every forever loop check that outputs of the DUT came on time, in case they doesnt it should stop simulation.
This special monitor should break the simulation in case of mismatch(error) and there is no Scoreboard.
I still want to manage nice end of simulation behaviour. I tried use raise and drop objection but I get an error of OBJT_ZERO sometimes. Does anyone knows a good way to end the simulation in that case?
thanks!
The UVM is set up by default so that uvm_report_fatal ends the test immediately, and uvm_report_error lets the simulation continue until hitting an error limit that you can set. And you can control the actions of each severity for an individual component. See uvm_report_object which is the base class of uvm_component.
Upon ending the test, the UVM calls uvm_report_server::report_summarize() that dumps out all the severity counts. If you insist, you can create a final block in your testbench module that gathers the severity counts from the report server and print the last message. For example:
module top;
initial run_test();
uvm_report_server rs = uvm_report_server::get_server();
final if (rs.get_severity_count(UVM_FATAL) != 0 ||
rs.get_severity_count(UVM_ERROR) !=0 )
$display("Test Failed");
endmodule
But this is really unnecessary and may not catch other non-UVM errors like assertion failures or timing checks. Many tools have a TESTSTATUS exit code that reports the most severe message encounte, UVM or tool.

Basic UVM sequence simulation query

I have a couple of issues with a basic UVM based TB I'm trying out to understand sequences and their working.
bvalid is being always picked as 0 in the driver when being updated in the response item
Couple of error messages for last 2 transactions (# UVM_ERROR # 18: uvm_test_top.axi_agent1.axi_base_seqr1##axi_base_seq1 [uvm_test_top.axi_agent1.axi_base_seqr1.axi_base_seq1] Response queue overflow, response was dropped)
Here is the link to the compiling code on EDA Playground
http://www.edaplayground.com/x/3x9
Any suggestions on what I'm missing??
Thanks
venkstart
Having a look at the specification for $urandom_range it shows the signature as: function int unsigned $urandom_range( int unsigned maxval, int unsigned minval = 0 ). Change your call to $urandom_range(1, 0) and it should work.
The second error comes from the fact that you are sending responses from the driver and not picking them up in your sequence. This is the line that does it: seq_item_port.item_done(axi_item_driv_src);. Either just do seq_item_port.item_done(); (don't send responses) or put a call to get_response() inside your sequence after finish_item(). What I usually do is update the fields of the original request and just call item_done(). For example, if I start a read transaction, in my driver I would drive the control signals and wait for the DUT to respond, update the data field of the request with the data I got from the DUT and call item_done() in my driver to mark the request as done. This way if I need this data in my sequence (to constrain some future item, for example) I have it.

Using burst_read/write with register model

I've a register space of 16 registers.
These are accessible through serial bus (single as well as burst).
I've UVM reg model defined for these registers.
However none of the reg model method supports burst transaction on bus.
As a workaround
I can declare memory model for same space and whenever I need burst access I use memory model but it seems redundant to declare 2 separate classes for same thing and this approach won't mirror register values correctly.
create a function which loops for number of bytes iterations and access registers one by one however this method doesn't create burst transaction on bus.
So I would like to know if there is a way to use burst_read and burst_write methods with register model. It would be nice if burst_read and burst_write support mirroring (current implementation doesn't support this) but if not I can use .predict and .set so its not big concern.
Or can I implement a method for register model easily to support burst operation.
I found this to help get you started:
http://forums.accellera.org/topic/716-uvm-register-model-burst-access/
The guy mentions using the optional 'extension' argument that read/write take. You could store the length of the burst length inside a container object (think int vs. Integer in Java) and then pass that as an argument when calling write() on the first register.
A rough sketch (not tested):
// inside your register sequence
uvm_queue #(int) container = new("container");
container.push_front(4);
start_reg.write(status, data, .extension(container));
// inside your adapter
function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
int burst_len = 1;
uvm_reg_item reg_item = get_item();
uvm_queue #(int) extension;
if ($cast(extension, reg_item.extension))
burst_len = extension.pop_front();
// do the stuff here based on the burst length
// ...
endfunction
I've used uvm_queue because there isn't any trivial container object in UVM.
After combining opinions provided by Tudor and links in the discussion, here is what works for adding burst operation to reg model.
This implementation doesn't show all the code but only required part for adding burst operation, I've tested it for write and read operation with serial protocols (SPI / I2C). Register model values are updated correctly as well as RTL registers are updated.
Create a class to hold data and burst length:
class burst_class extends uvm_object;
`uvm_object_utils (....);
int burst_length;
byte data [$];
function new (string name);
super.new(name);
endfunction
endclass
Inside register sequence (for read don't initialize data)
burst_class obj;
obj = new ("burstInfo");
obj.burst_length = 4; // replace with actual length
obj.data.push_back (data1);
obj.data.push_back (data2);
obj.data.push_back (data3);
obj.data.push_back (data4);
start_reg.read (status,...., .extension(obj));
start_reg.write (status, ...., .extension (obj));
After successful operation data values should be written or collected in obj object
In adapter class (reg2bus is updated for write and bus2reg is updated for read)
All the information about transaction is available in reg2bus except data in case of read.
adapter class
uvm_reg_item start_reg;
int burst_length;
burst_class adapter_obj;
reg2bus implementation
start_reg = this.get_item;
adapter_obj = new ("adapter_obj");
if($cast (adapter_obj, start_reg.extension)) begin
if (adapter_obj != null) begin
burst_length = adapter_obj.burst_length;
end
else
burst_length = 1; /// so that current implementation of adapter still works
end
Update the size of transaction over here according to burst_length and assign data correctly.
As for read bus2reg needs to be updated
bus2reg implementation (Already has all control information since reg2bus is always executed before bus2reg, use the values captured in reg2bus)
According to burst_length only assign data to object passed though extension in this case adapter_obj