uvm_primer ch17 communication between threads
Communication between verilog threads
The communication between producer and consumer
uses put_it and get_it for communication coordination;
module producer(output byte shared, input bit put_it, output bit get_it);
initial
repeat(3) begin
$display("Sent %0d", ++shared);
get_it = ~get_it;
@(put_it);
end
endmodule : producer
module consumer(input byte shared, output bit put_it, input bit get_it);
initial
forever begin
@(get_it);
$display("Received: %0d", shared);
put_it = ~put_it;
end
endmodule : consumer
module top;
byte shared;
producer p (shared, put_it, get_it);
consumer c (shared, put_it, get_it);
endmodule : top
oop's inter-thread communication coordination
Generally use semaphores or mailboxs; to proceed;
Communication coordination between uvm threads
Use two components to hide details
- ports The ports object constructed in uvm_component allows us to communicate with other threads in run_phase, use put port to send data to other threads, and use get port to obtain data from other threads;
- TLM fifo This TLM fifo object is used to connect put port and get port; transaction-level modeling; this fifo can not only transfer transactions, but also any type of data;
TLM fifo only saves one data element;
blocking mode
The producer generates a piece of data and puts it into the fifo, but the fifo can only store one piece of data; when the loop comes to put again, it is blocked; until the consumer uses get to fetch the data; also when the consumer gets a piece of data, the fifo is empty , Can only block, wait for the next data;
class producer extends uvm_component;
`uvm_component_utils(producer);
int shared;
uvm_put_port #(int) put_port_h;
function void build_phase(uvm_phase phase);
put_port_h = new("put_port_h", this);
endfunction : build_phase
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new
task run_phase(uvm_phase phase);
phase.raise_objection(this);
repeat (3) begin
put_port_h.put(++shared);
$display("Sent %0d", shared);
end
phase.drop_objection(this);
endtask : run_phase
endclass : producer
class consumer extends uvm_component;
`uvm_component_utils(consumer);
uvm_get_port #(int) get_port_h;
int shared;
function void build_phase(uvm_phase phase);
get_port_h = new("get_port_h", this);
endfunction : build_phase
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new
task run_phase(uvm_phase phase);
forever begin
get_port_h.get(shared);
$display("Received: %0d", shared);
end
endtask : run_phase
endclass : consumer
Connect using fifo
class communication_test extends uvm_test;
`uvm_component_utils(communication_test)
producer producer_h;
consumer consumer_h;
uvm_tlm_fifo #(int) fifo_h;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new
function void build_phase(uvm_phase phase);
producer_h = new("producer_h", this);
consumer_h = new("consumer_h", this);
fifo_h = new("fifo_h",this);
endfunction : build_phase
function void connect_phase(uvm_phase phase);
producer_h.put_port_h.connect(fifo_h.put_export);
consumer_h.get_port_h.connect(fifo_h.get_export);
endfunction : connect_phase
endclass : communication_test
The difference between uvm_tlm_fifo and uvm_analysis_fifo
Non-blocking mode
The blocking mode is suitable for the communication coordination between the threads where the producer and the consumer are both time-free;
when it comes to the clocking edge, you need to use try_put() and try_get();
try_put() and try_get() return a bit to tell you to put /get is successful;
try_get returns 1 to print; returns 0 to skip to the next clock edge;
class producer extends uvm_component;
`uvm_component_utils(producer);
int shared;
uvm_put_port #(int) put_port_h;
function void build_phase(uvm_phase phase);
put_port_h = new("put_port_h", this);
endfunction : build_phase
task run_phase(uvm_phase phase);
phase.raise_objection(this);
repeat (3) begin
#17;
put_port_h.put(++shared);
$display("%0tns Sent %0d", $time, shared);
end
#17;
phase.drop_objection(this);
endtask : run_phase
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new
endclass : producer
class consumer extends uvm_component;
`uvm_component_utils(consumer);
uvm_get_port #(int) get_port_h;
virtual clk_bfm clk_bfm_i;
int shared;
function void build_phase(uvm_phase phase);
get_port_h = new("get_port_h", this);
clk_bfm_i = example_pkg::clk_bfm_i;
endfunction : build_phase
task run_phase(uvm_phase phase);
forever begin
@(posedge clk_bfm_i.clk);
if(get_port_h.try_get(shared)) //重点
$display("%0tns Received: %0d", $time,shared);
end
endtask : run_phase
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new
endclass : consumer
problem
This chapter uses producer and consumer to explain the communication coordination mechanism between threads;
but it does not explain why the monitor and scb in ch16 are the same thread;
the producer and consumer here are different threads;