验证平台的架构图
整个验证平台的框架如图所示,由外往内可以分为三大部分:最顶层的top部分,包含验证平台、接口和DUT;testcase部分,包含了验证环境;env部分,包含了transaction、generator、agent、driver、monitor、scoreboard、coverage部分。图中agent部分没画出来,实际上他是一个可有可无的部分,但是较为复杂的环境中都会存在agent。
验证平台的代码目录
给出的验证平台代码存放目录,ahb_sramc_sv_tb是对顶层的文件,包含doc、rtl、verif。doc部分存放设计文件,rtl存放设计代码,verif中存放agent、env、sim、tb、tests部分。agent文件夹中包含agent.sv文件,env中包含transaction、generator、driver、monitor、scoreboard、environment文件,sim文件中包含Makefile、rtl.f文件,tb中包含接口文件和top顶层文件,tests中包含测试代码文件。
第一步:根据AHB_slave接口信号编写ahb_slave_interface
ahb_slave_interface部分代码中主要定义了AHB从机的接口信号,monitor和driver的时钟块,主要作用是用于显示的额同步时钟域。modport可以防止我们把信号接反。
`ifndef AHB_SLAVE_IF_SV
`define AHB_SLAVE_IF_SV
interfece ahb_slv_if(input hclk); 时钟信号一般声明在接口名后
logic hsel;
logic [31:0] haddr;
logic hwrite;
logic [1:0] htrans;
logic [2:0] hsize;
logic [2:0] hburst;
logic [31:0] hwdata;
logic hresetn;
logic hclk;
logic hready ;
logic [1:0] hresp;
logic [31:0] hrdata;
clocking drv_cb(posedge hclk);
output hsel;
output hwrite;
output htrans;
output hsize;
output haddr;
output hwdata;
output hready;
input hrdata;
endclocking
clocking mon_cb(posedge hclk);
input hsel;
input hwrite;
input htrans;
input hsize;
input haddr;
input hwdata;
input hready;
input hrdata;
endclocking
modport driver(clocking drv_cb)
modport minitor(clocking mon_cb)
endinterface
`endif
Q:对于时钟块的输入输出方向是怎么判断的?
A:对于driver而言,它自身是一个master,通过接口interface按照AHB协议与DUT相连,所以,其信号的输入输出应以DUT为依据;对于monitor而言,它通过DUT、接口和driver相连接,他会采集所有接口上的信息,然后通过邮箱mailbox将数据送到Scoreboard进行比对,所以都是input。
第二部,根据AHB_slave输入和输出接口,进行数据建模:数据包transaction
对数据激励进行建模,产生随机化的数据包,主要对地址信号、控制信号、选择信号和读取数据进行随机化,对地址、大小、数据类型进行约束,其中进行了条件约束的操作。并不是所有的信号都放在transaction中,因为有些信号是固定死的,所以没必要去采集去看他的覆盖率或者发送的时候进行随机化。
`ifndef TRANSACTION_SV
`define TRANSACTION_SV
class transaction;
rand bit [31:0] haddr;
rand bit [1:0] hsize;
rand bit [1:0] htrans;
rand bit hsel;
rand bit hwrite;
rand bit [1:0] hburst;
rand bit [31:0] hwdata;
rand bit [31:0] hrdata;
constraint c1{
haddr inside {[32'h0:32'h0000_FFFF]};
}
constraint c2{
hsize inside {2'b00,2'b01,2'b10};
}
constraint c3{
htrans inside {2'b00,2'b11};
}
constraint c4{
(hsize==2'10) -> (haddr[1:0]==2'b00);
(hsize==2'01) -> (haddr[1:0]==2'b00) || (haddr[1:0]==2'b10);
}
endclass
`endif
第三步,编写数据生成模块
利用系统函数随机生成数据。所有的测试用例,都会用到generator,但是产生的激励可能有所不用,就需要用参数化的方式把不同的命令传进来,甘肃generator组件,我需要干什么,
`ifndef GENERATOR_SV
`define GENERATOR_SV
class generator;
int tr_num;
transaction tr;
event gen_data;
mailbox mbx=new();
extern function new(mailbox mbx,int tr_num);
extern function build();
extern task write_data32(logic [31:0] wdata,logic [31:0] addr);
extern task write_data16(logic [31:0] wdata,logic [31:0] addr);
extern task write_data8(logic [31:0] wdata,logic [31:0] addr);
extern task write_data32_random(logic [31:0] addr);
extern task write_data16_random(logic [31:0] addr);
extern task write_data8_random(logic [31:0] addr);
extern task read_data32(logic [31:0] addr);
extern task read_data16(logic [31:0] addr);
extern task read_data8(logic [31:0] addr);
extern task read_addr_random();
extern task read_write_random();
extern task all_random();
extern task no_op();
extern task run();
endtask
function new(mailbox mbx;tr_num);
this.mbx = mbx;
this.tr_num = tr_num;
endfunction
function new(mailbox mbx;tr_num);
tr = new;
if(!tr.randomsize()) begin
$display("@/0t ERROR::generator::build randonmize faild",$time);
end
endfunction
task generator::write_data32(logic [31:0] wdata,logic [31:0] addr);
tr = new;
tr.haddr = addr;
tr.hwdata = wdata;
tr.hsize = 2'b10;
tr.htrans= 2'b10;
tr.hburst= 2'b00;
tr.hwrite= 1'b1;
tr.hsel = 1'b1;
->gen_data;
endtask
task generator::write_data16(logic [31:0] wdata,logic [31:0] addr);
tr = new;
tr.haddr = addr;
tr.hwdata = wdata;
tr.hsize = 2'b01;
tr.htrans= 2'b10;
tr.hburst= 2'b00;
tr.hwrite= 1'b1;
tr.hsel = 1'b1;
->gen_data;
endtask
task generator::write_data8(logic [31:0] wdata,logic [31:0] addr);
tr = new;
tr.haddr = addr;
tr.hwdata = wdata;
tr.hsize = 2'b00;
tr.htrans= 2'b10;
tr.hburst= 2'b00;
tr.hwrite= 1'b1;
tr.hsel = 1'b1;
->gen_data;
endtask
task generator::write_data32_random(logic [31:0] addr);
tr = new;
tr.haddr = addr;
tr.hsize = 2'b10;
tr.htrans= 2'b10;
tr.hburst= 2'b00;
tr.hwrite= 1'b1;
tr.hsel = 1'b1;
->gen_data;
endtask
task generator::write_data16_random(logic [31:0] addr);
tr = new;
tr.haddr = addr;
tr.hsize = 2'b01;
tr.htrans= 2'b10;
tr.hburst= 2'b00;
tr.hwrite= 1'b1;
tr.hsel = 1'b1;
->gen_data;
endtask
task generator::write_data8_random(logic [31:0] addr);
tr = new;
tr.haddr = addr;
tr.hsize = 2'b00;
tr.htrans= 2'b10;
tr.hburst= 2'b00;
tr.hwrite= 1'b1;
tr.hsel = 1'b1;
->gen_data;
endtask
task generator::read_data32(logic [31:0] addr);
tr = new;
tr.haddr = addr;
tr.hsize = 2'b10;
tr.htrans= 2'b10;
tr.hwrite= 1'b0;
tr.hsel = 1'b1;
->gen_data;
endtask
task generator::read_data16(logic [31:0] addr);
tr = new;
tr.haddr = addr;
tr.hsize = 2'b01;
tr.htrans= 2'b10;
tr.hwrite= 1'b0;
tr.hsel = 1'b1;
->gen_data;
endtask
task generator::read_data8(logic [31:0] addr);
tr = new;
tr.haddr = addr;
tr.hsize = 2'b00;
tr.htrans= 2'b10;
tr.hwrite= 1'b0;
tr.hsel = 1'b1;
->gen_data;
endtask
task generator:: read_addr_random();
tr = new;
if(!tr.randomsize()) begin
$display("@/0t ERROR::generator::read_addr_random randonmize faild",$time);
end
tr.hsize = 2'b10;
tr.htrans= 2'b10;
tr.hwrite= 1'b0;
tr.hsel = 1'b1;
->gen_data;
endtask
task generator:: read_write_random();
tr = new;
if(!tr.randomsize()) begin
$display("@/0t ERROR::generator::read_write_random randonmize faild",$time);
end
tr.hsize = 2'b10;
tr.htrans= 2'b10;
tr.hsel = 1'b1;
->gen_data;
endtask
task generator:: all_random();
tr = new;
if(!tr.randomsize()) begin
$display("@/0t ERROR::generator::read_write_random randonmize faild",$time);
end
->gen_data;
endtask
task generator:: no_op();
tr = new;
if(!tr.randomsize()) begin
$display("@/0t ERROR::generator::read_write_random randonmize faild",$time);
end
tr.htrans= 2'b0;
tr.hsel = 1'b0;
->gen_data;
endtask
task generator:: run();
repeat(tr_num) begin
@(gen_data);
mbx.put(tr)
end
endtask
`endif
第四步、生成agent模块
agent组件的作用是相当于是数据中转站,把数据包送往不同的地方,代码中的三个邮箱代表着三组不同方向的数据,从generator中获取,然后分别送往driver和scoreboard
`ifndef AGENT_SV
`define AGENT_SV
class agent;
int tr_num;
transaction tr;
mailbox gen2agt_max=new();
mailbox agt2drv_max=new();
mailbox agt2scb_max=new();
extern function new(mailbox gen2agt_max,agt2drv_max,agt2scb_max, int tr_num);
extern taskbuild();
extern task run();
endclass
function agent::new(mailbox gen2agt,agt2drv,agt2scb,tr_num);
this.gen2agt_max = gen2agt_max;
this.agt2drv_max = agt2drv_max;
this.agt2scb_max = agt2scb_max;
this.tr_num = tr_num;
endfunction
task agent::build();
endtask
task run();
repeat(tr_num)begin
tr = new();
gen2agt_max.get(tr);
agt2drv_max.put(tr);
agt2scb_max.put(tr);
end
ebdtask
`endif
vcs编译文件命令
vcs +v2k -sverilog ./transaction.sv ./generator.sv ./agent/agent.sv (根据文件目录修改编译文件路径)
根据文件的需要,如果不需要其他文件,就只编译自身,例如:transaction.sv;如果需要其他文件,需要一同编译,例如:agent.sv