本篇文章介绍命令层主动事务处理器的编写方法。命令层事务处理器就是一个一端是事务级接口,一端连接物理信号,也称为BFM。如图所示:
第一步:
第二步:
第三步:事务描述符(同命令层监视器)
第四步:主动事务处理器
现在开始写主动性事务处理器,它是由vmm_xactor基类扩展来的类。
实现读和写周期的任务在类中实现。它们定义为virtual,这样就可以在事务处理器的扩展类中修改它们的行为。它们定义为protected,以防止他们在类外被调用和出现同时访问总线的问题。
`ifndef APB_MASTER__SV
`define APB_MASTER__SV
`include “apb_if.sv”
`include “apb_rw.sv”
...
class apb_master extends vmm_xactor;
...
virtual protected task read(input bit [31:0] addr,
output logic [31:0] data);
...
endtask: read
virtual protected task write(input bit [31:0] addr,
input bit [31:0] data);
...
endtask: write
...
endclass: apb_master
本事务处理器需要一个事务级接口来接收事务和一个物理级接口。前者用vmm_channel实例,后者用虚拟端口virtual modport。它们都通过构造函数传递给事务处理器,都保存在公共类属性中。
`ifndef APB_MASTER__SV
`define APB_MASTER__SV
`include “apb_if.sv”
`include “apb_rw.sv”
...
class apb_master extends vmm_xactor;
apb_rw_channel in_chan;
virtual apb_if.master sigs;
function new(string name,
int unsigned stream_id,
virtual apb_if.master sigs,
apb_rw_channel in_chan = null);
super.new(“APB Master”, name, stream_id);
this.sigs = sigs;
if (in_chan == null)
in_chan = new(“APB Master Input Channel”, name);
this.in_chan = in_chan;
...
endfunction: new
...
virtual protected task read(input bit [31:0] addr,
output logic [31:0] data);
...
endtask: read
virtual protected task write(input bit [31:0] addr,
input bit [31:0] data);
...
endtask: write
...
endclass: apb_master
`endif
当事务处理器复位时,输入通道必须排空,关键的输出信号必须被驱动为空闲状态。这些都在vmm_xactor::reset_xactor方法的扩展类中实现。可在事务处理器的构造函数中初始化输出信号。
`ifndef APB_MASTER__SV
`define APB_MASTER__SV
`include “apb_if.sv”
`include “apb_rw.sv”
...
class apb_master extends vmm_xactor;
apb_rw_channel in_chan;
virtual apb_if.master sigs;
function new(string name,
int unsigned stream_id,
virtual apb_if.master sigs,
apb_rw_channel in_chan = null);
super.new(“APB Master”, name, stream_id);
this.sigs = sigs;
if (in_chan == null)
in_chan = new(“APB Master Input Channel”, name);
this.in_chan = in chan;
this.sigs.mck.psel <= ’0;
this.sigs.mck.penable <= ’0;
endfunction: new
virtual function void reset_xactor(reset_e rst_typ = SOFT_RST);
super.reset(rst_typ);
this.in_chan.flush(); //排空输入通道
this.sigs.mck.psel <= ’0;
this.sigs.mck.penable <= ’0;
endfunction: reset_xactor
最灵活的事务处理器执行机制使用支持阻塞,非阻塞,乱序的有效时隙。因为协议支持事务之间的暂停,所以要应该用vmm_xactor;;wart_if_stopped_or_empty方法来停止事务处理器。
virtual protected task main();
super.main();
@ (this.sigs.mck);
forever begin
apb_rw tr;
this.wait_if_stopped_or_empty(this.in_chan);
this.in_chan.activate(tr);
...
this.in_chan.start();
case (tr.kind)
apb_rw::READ : this.read(tr.addr, tr.data);
apb_rw::WRITE: this.write(tr.addr, tr.data);
endcase
this.in_chan.complete();
...
第六步:扩展点
virtual protected task main();
super.main();
@ (this.sigs.mck);
forever begin
apb_rw tr;
this.wait_if_stopped_or_empty(this.in_chan);
this.in_chan.activate(tr);
...
this.in_chan.start();
case (tr.kind)
apb_rw::READ : this.read(tr.addr, tr.data);
apb_rw::WRITE: this.write(tr.addr, tr.data);
endcase
this.in_chan.complete();
...
前面的事务处理器具有基本的功能。但是可重用性低。回调方法允许用户在不修改事务处理器本身就能扩展事务处理器的行为。应该编写在事务处理前后的回调方法。"pre-transaction"回调方法允许注入错误和延迟;"post-transaction"回调方法允许插入延迟,或将事务的值记录在功能覆盖模型中,或与期望响应做对照。
要在回调基类的扩展类(callback facade)中把回调方法定义为virtual tasks 或这virtual void functions。在"pre-transaction"回调方法中创建一个允许丢弃事务的机制无疑是一个好的想法。
`ifndef APB_MASTER__SV
`define APB_MASTER__SV
`include “apb_if.sv”
`include “apb_rw.sv”
typedef class apb_master;
class apb_master_cbs extends vmm_xactor_callbacks;
virtual task pre_cycle(apb_master xactor,
apb_rw cycle,
ref bit drop);
endtask: pre_cycle
virtual task post_cycle(apb_master xactor,
apb_rw cycle);
endtask: post_cycle
endclass: apb_master_cbs
class apb_master extends vmm_xactor;
apb_rw_channel in_chan;
virtual apb_if.master sigs;
function new(string name,
int unsigned stream_id,
virtual apb_if.master sigs,
apb_rw_channel in_chan = null);
应该在恰当的点应用回调方法,用`vmm_callback()宏来实现。
virtual protected task main();
super.main();
@ (this.sigs.mck);
forever begin
apb_rw tr;
bit drop;
this.wait_if_stopped_or_empty(this.in_chan);
this.in_chan.activate(tr);
drop = 0;
`vmm_callback(apb_master_cbs, pre_cycle(this, tr, drop));
if (drop) begin
this.in_chan.remove();
continue;
end
this.in_chan.start();
case (tr.kind)
apb_rw::READ : this.read(tr.addr, tr.data);
apb_rw::WRITE: this.write(tr.addr, tr.data);
endcase
this.in_chan.complete();
`vmm_callback(apb_master_cbs, post_cycle(this, tr));
this.in_chan.remove();
end
endtask: main
virtual protected task read(input bit [31:0] addr,
output logic [31:0] data);
...
endtask: read
virtual protected task write(input bit [31:0] addr,
input bit [31:0] data);
...
endtask: write
endclass: apb_master
`endif
第七步:调试信息
virtual protected task main();
super.main();
@ (this.sigs.mck);
forever begin
apb_rw tr;
bit drop;
this.wait_if_stopped_or_empty(this.in_chan);
this.in_chan.activate(tr);
drop = 0;
`vmm_callback(apb_master_cbs, pre_cycle(this, tr, drop));
if (drop) begin
this.in_chan.remove();
continue;
end
this.in_chan.start();
case (tr.kind)
apb_rw::READ : this.read(tr.addr, tr.data);
apb_rw::WRITE: this.write(tr.addr, tr.data);
endcase
this.in_chan.complete();
`vmm_callback(apb_master_cbs, post_cycle(this, tr));
this.in_chan.remove();
end
endtask: main
virtual protected task read(input bit [31:0] addr,
output logic [31:0] data);
...
endtask: read
virtual protected task write(input bit [31:0] addr,
input bit [31:0] data);
...
endtask: write
endclass: apb_master
`endif
为了能完全的重用,应该不用查看源代码就能理解事务处理器在做什么和调试它的操作。调试信息应该加到合适的点来表明事务处理器将做什么,正在做什么,已经做了什么操作。这些调试信息要通过`vmm_trace,`vmm_debug,`vmm_verbose发出。
virtual protected task main();
super.main();
@ (this.sigs.mck);
forever begin
apb_rw tr;
bit drop;
this.wait_if_stopped_or_empty(this.in_chan);
this.in_chan.activate(tr);
drop = 0;
`vmm_callback(apb_master_cbs, pre_cycle(this, tr, drop));
if (drop) begin
`vmm_debug(log, {“Dropping transaction.../n”,
tr.psdisplay(“ “)});
this.in_chan.remove();
continue;
end
`vmm_trace(log, {“Starting transaction.../n”,
tr.psdisplay(“ “)});
this.in_chan.start();
case (tr.kind)
apb_rw::READ : this.read(tr.addr, tr.data);
apb_rw::WRITE: this.write(tr.addr, tr.data);
endcase
this.in_chan.complete();
`vmm_trace(log, {“Completed transaction.../n”,
tr.psdisplay(“ “)});
`vmm_callback(apb_master_cbs, post_cycle(this, tr));
this.in_chan.remove();
end
endtask: main