UVM scoreboard typically receives transaction items using analysis imports connected to the write function. After we receive a transaction over an analysis port, usually we need to perform checking for some time consuming functionality.
Problem statement: Within a function, no event, delay or timing control statements are permitted, and we need to find a way to perform checking that requires a notion of time.
As an example, let’s say we need to check that after getting a transaction in the write function over analysis import, the global variable address should be different from the received item.addr after 5 clocks from receiving the item.
1.Starting task from function
This is the simplest method and could be achieved by calling a task inside fork join_none.
Here we will ditch usage of write functions and use uvm_tlm_analysis_fifo. Simply connect analysis port to analysis fifo* and use .get() function to pop* transactions out of analysis_fifo in the run phase. In the run phase make two separate processes, one to receive data over analysis_fifo and other to count clocks and check if the field has proper value.
*NOTE: It is necessary to use analysis_port rather than analysis_imp, because analysis_imp needs to be terminated with the write function.
*NOTE: .get() function is blocking, so if there are no elements in analysis_fifo it will wait until any transaction comes.
3.Use event trigger
The last option would focus on the starting process in the fork of the run phase. This can be achieved by using events or a simple flag. When transaction comes in write function: start event (set flag), and in-process use blocking statement @ or wait(event.triggered)* (or use wait(flag == 1))
*NOTE: The difference between @ and .triggered is that if event trigger and wait for event happens at the same time, there will be race condition and .triggered helps avoid that.