使用事务级模型非常重要,因为任何事情在某一个抽象层面的思考会比陷入一堆琐碎的细节里效率要高得多。
UVM提供了TLM的接口从而可以将各个组件通过事务级的方式连接在一起。
两个组件之间事务处理所需要的封装数据包。
producer产生transaction并通过put端口发送给consumer。
class producer extends uvm_component; uvm_blocking_put_port #(simple_trans) put_port; // 1 parameter function new( string name, uvm_component parent); put_port = new(“put_port”, this); ... endfunction virtual task run(); simple_trans t; for(int i = 0; i < N; i++) begin // Generate t. put_port.put(t); end endtask class consumer extends uvm_component; uvm_blocking_put_imp #(simple_trans, consumer) put_export; // 2 parameters ... task put(simple_trans t); case(t.kind) READ: // Do read. WRITE: // Do write. endcase endtask endclass这里,只有当consumer的put完成之后producer的put才能执行,否则将被阻塞。
这个时候consumer通过get端口向producer请求transaction。
class get_consumer extends uvm_component; uvm_blocking_get_port #(simple_trans) get_port; function new( string name, uvm_component parent); get_port = new(“get_port”, this); ... endfunction virtual task run(); simple_trans t; for(int i = 0; i < N; i++) begin // Generate t. get_port.get(t); end endtask class get_producer extends uvm_component; uvm_blocking_get_imp #(simple_trans, get_producer) get_export; ... task get(output simple_trans t); simple_trans tmp = new(); // Assign values to tmp. t = tmp; endtask endclass类似的,这里get_consumer的 get() 函数会等待get_producer’的get函数完成,否则将被阻塞。
在前面put的例子中,consumer只在调用put函数的时候才被激活,而很多情况下,我们希望当producer在创建事务的时候,consumer可以处理其他的事务,因此UVM提供了uvm_tlm_fifo来实现这个功能。在这里,producer将事务放进uvm_tlm_fifo,而consumer独立地从fifo中获取这些事务,如下图所示这样:
当fifo满了的时候,producer将被阻塞。如果没满的话,producer将把object放进fifo并离开。
同样,如果fifo里有transaction可用,那么consumer将立刻去get并从fifo中移除,如果暂时没有可用的transaction的话,那么consumer将被阻塞。
使用connect进行连接:
class my_env extends uvm_env; ... virtual function void connect_phase(uvm_phase phase); // component.port.connect(target.export); producer.blocking_put_port.connect(fifo.put_export); get_consumer.get_port.connect(fifo.get_export); ... endfunction endclassTLM-2.0定义了一个叫做generic payload的对象,用于在组件之间传递transaction。在Systemverilog里,generic payload是默认的事务类型,但并不唯一,但是如果在SystemC里则是唯一。
该对象的成员如下:
protected rand bit [63:0] m_address; protected rand uvm_tlm_command_e m_command; protected rand byte m_data[]; protected rand int unsigned m_length; protected rand uvm_tlm_response_status_e m_response_status; protected rand bit m_dmi; protected rand byte m_byte_enable[]; protected rand int unsigned m_byte_enable_length; protected rand int unsigned m_streaming_width;其中uvm_tlm_command_e和uvm_tlm_response_status_e为枚举数据类型:
typedef enum { TLM_READ_COMMAND, TLM_WRITE_COMMAND, TLM_IGNORE_COMMAND } uvm_tlm_command_e; typedef enum { TLM_OK_RESPONSE = 1, TLM_INCOMPLETE_RESPONSE = 0, TLM_GENERIC_ERROR_RESPONSE = -1, TLM_ADDRESS_ERROR_RESPONSE = -2, TLM_COMMAND_ERROR_RESPONSE = -3, TLM_BURST_ERROR_RESPONSE = -4, TLM_BYTE_ENABLE_ERROR_RESPONSE = -5 } uvm_tlm_response_status_e;以上所有的数据成员都有rand进行修饰,从而可以满足随机的要求。
通过以下方法进行访问:
virtual function uvm_tlm_command_e get_command(); virtual function void set_command(uvm_tlm_command_e command); virtual function bit is_read(); virtual function void set_read(); virtual function bit is_write(); virtual function void set_write(); virtual function void set_address(bit [63:0] addr); virtual function bit[63:0] get_address(); virtual function void get_data (output byte p []); virtual function void set_data_ptr(ref byte p []); virtual function int unsigned get_data_length(); virtual function void set_data_length(int unsigned length); virtual function int unsigned get_streaming_width(); virtual function void set_streaming_width(int unsigned width); virtual function void get_byte_enable(output byte p[]); virtual function void set_byte_enable(ref byte p[]); virtual function int unsigned get_byte_enable_length(); virtual function void set_byte_enable_length(int unsigned length); virtual function void set_dmi_allowed(bit dmi); virtual function bit is_dmi_allowed(); virtual function uvm_tlm_response_status_e get_response_status(); virtual function void set_response_status(uvm_tlm_response_status_e status); virtual function bit is_response_ok(); virtual function bit is_response_error(); virtual function string get_response_string();还可以对对象generic payload进行扩展
class gp_Xs_ext extends uvm_tlm_extension#(gp_Xs_ext); byte Xmask[]; function new(string name = ""); super.new(name); endfunction `uvm_object_utils_begin(gp_Xs_ext) `uvm_field_int_array(Xmask, UVM_ALL_ON) `uvm_object_utils_end endclass gp_Xs_ext Xs = new(); gp.set_extension(Xs);提供了一些方法对扩展进行管理:
get_num_extensions() clear_extension() clear_extensions()通过uvm_tlm_if#()类来提供组件通信接口。
class uvm_tlm_if #(type T=uvm_tlm_generic_payload, type P=uvm_tlm_phase_e); endclass第一个参数是待传输的事务类型,默认的类型是generic payload,而第二个参数的phase是一个枚举类型:
typedef enum { UNINITIALIZED_PHASE, BEGIN_REQ, END_REQ, BEGIN_RESP, END_RESP } uvm_tlm_phase_e;TLM-1和2.0的主要区别在于参数的传递方式,前一种是采用复制参数的传递方式,后一种则是ref的直接传递,这样就避免了反复复制,从而大大提升了性能。但是任何一个目标可能都会对该参数进行修改,这算是它的劣势。
公众号:程序员Marshall