AHBRAM验证(3)——AHB master driver初步实现
一. lvc_ahb_driver代码
- 在这段代码中,主要是接收到seq后将他发送到对应的sequencer中
- 在uvm_driver中,uvm专门定义了端口
uvm_seq_item_pull_port#(REQ,)RSP seq_item_port
和方法get_next_item(output REQ)
,item_done(input RSP)
,用于接收数据; - 在代码中,通过端口
seq_item_port
调用get_next_item
方法来获取item,获得了item后将调用函数drive_transfer
将item发送出去,最后driver调用item_done
告知sequence自己完成数据的传输 - 需要注意的是
void'($cast(rsp, req.clone()));
,虽然在定义时使用了参数指定了req是子类句柄,但是clone函数的返回值是uvm_object
,所以必须调用$cast
将句柄转换为子类句柄 rsp.set_sequence_id(req.get_sequence_id()); rsp.set_transaction_id(req.get_transaction_id());
这段代码的作用是防止存在多个sequence在同一个sequencer上启动的清况,sequencer不知道将response返回给哪个sequence。
`ifndef LVC_AHB_DRIVER_SV
`define LVC_AHB_DRIVER_SV
class lvc_ahb_driver #(type REQ = lvc_ahb_transaction, RSP = REQ) extends uvm_driver #(REQ, RSP);
`uvm_component_utils(lvc_ahb_driver)
function new(string name = "lvc_ahb_driver", uvm_component parent );
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
endfunction
task run_phase(uvm_phase phase);
super.run_phase(phase);
fork
get_and_drive();
reset_listener();
join_none
endtask
virtual task get_and_drive();
forever begin
seq_item_port.get_next_item(req);
`uvm_info(get_type_name(), "sequencer got next item", UVM_HIGH)
drive_transfer(req);
void'($cast(rsp, req.clone()));
rsp.set_sequence_id(req.get_sequence_id());
rsp.set_transaction_id(req.get_transaction_id());
seq_item_port.item_done(rsp);
`uvm_info(get_type_name(), "sequencer item_done_triggered", UVM_HIGH)
end
endtask : get_and_drive
virtual task drive_transfer(REQ r);
//TODO implementation in child class
endtask
virtual task reset_listener();
//TODO implementation in child class
endtask
endclass
`endif //LVC_AHB_DRIVER_SV
二. lvc_ahb_master_driver代码
`ifndef LVC_AHB_MASTER_DRIVER_SV
`define LVC_AHB_MASTER_DRIVER_SV
class lvc_ahb_master_driver extends lvc_ahb_driver;
lvc_ahb_agent_configuration cfg;
virtual lvc_ahb_if vif;
`uvm_component_utils(lvc_ahb_master_driver)
function new(string name = "lvc_ahb_master_driver", uvm_component parent );
super.new(name, parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
endfunction
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
endfunction
task run_phase(uvm_phase phase);
super.run_phase(phase);
endtask
virtual task drive_transfer(REQ r);
case(r.burst_type)
SINGLE : do_automic_transfer(r);
INCR : `uvm_error("TYPEERR", "burst type no supported yet")
WRAP4 : `uvm_error("TYPEERR", "burst type no supported yet")
WRAP8 : `uvm_error("TYPEERR", "burst type no supported yet")
INCR8 : `uvm_error("TYPEERR", "burst type no supported yet")
WRAP16 : `uvm_error("TYPEERR", "burst type no supported yet")
INCR16 : `uvm_error("TYPEERR", "burst type no supported yet")
default : `uvm_error("TYPEERR", "burst type no defined")
endcase
endtask
virtual task do_automic_transfer(REQ r);
case(r.xact_type)
READ : do_read(r);
WRITE : do_write(r);
IDLE_XACT : `uvm_error("TYPEERR", "burst type no supported yet")
default : `uvm_error("TYPEERR", "xact type no defined")
endcase
endtask
virtual task do_read(REQ r);
do_init_read(r);
do_proc_read(r);
endtask
virtual task do_write(REQ r);
do_init_write(r);
do_proc_write(r);
endtask
virtual task wait_for_bus_grant();
@(vif.cb_mst iff vif.cb_mst.hgrant === 1'b1);
endtask
virtual task do_init_read(REQ r);
wait_for_bus_grant();
@(vif.cb_mst);
vif.htrans <= NSEQ;
vif.haddr <= r.addr;
vif.hburst <= r.burst_type;
vif.hsize <= r.burst_size;
vif.hwrite <= 2'b00;
@(vif.cb_mst);
forever begin
@(negedge vif.hclk);
if(vif.hready === 1'b1) begin
break;
end
else
@(vif.cb_mst);
end
r.data = new[r.current_data_beat_num + 1](r.data);
r.data[0] <= vif.hrdata;
//updata
r.trans_type = NSEQ;
r.current_data_beat_num = 0;
r.all_beat_response = new[r.current_data_beat_num + 1];
r.all_beat_response[r.current_data_beat_num ] = response_type_enum'(vif.hresp);
endtask
virtual task do_proc_read(REQ r);
do_init_idle();
endtask
virtual task do_init_write(REQ r);
wait_for_bus_grant();
@( vif.cb_mst);
vif.htrans <= NSEQ;
vif.haddr <= r.addr;
vif.hburst <= r.burst_type;
vif.hsize <= r.burst_size;
vif.hwrite <= 2'b01;
@(vif.cb_mst);
vif.hwdata <= r.data[0];
forever begin
@(negedge vif.hclk);
if(vif.hready === 1'b1) begin
break;
end
else
@(vif.cb_mst);
end
//updata
r.trans_type = NSEQ;
r.current_data_beat_num = 0;
r.all_beat_response = new[r.current_data_beat_num + 1];
r.all_beat_response[r.current_data_beat_num ] = response_type_enum'(vif.hresp);
endtask
virtual task do_proc_write(REQ r);
do_init_idle();
endtask
virtual task do_init_idle();
@(vif.cb_mst);
_do_init_idle();
endtask
virtual task _do_init_idle();
vif.htrans <= IDLE;
vif.haddr <= 0;
vif.hwrite <= 0;
vif.hsize <= 0;
vif.hport <= 0;
endtask
virtual task reset_listener();
`uvm_info(get_type_name(), "reset_listener ....", UVM_HIGH)
fork
forever begin
@(negedge vif.hresetn);
_do_init_idle();
end
join_none
endtask
endclass
`endif //LVC_AHB_MASTER_DRIVER_SV
- 上面的代码中作为公共的driver,只是实现了driver与sequencer之间的握手,对于真正的数据的发送将放在lvc_ahb_master_driver中
- 首先实现
reset_listener
代码,根据协议有如下代码
virtual task reset_listener();
`uvm_info(get_type_name(), "reset_listener ....", UVM_HIGH)
fork
forever begin
@(negedge vif.hresetn);
vif.htrans <= IDLE;
vif.haddr <= 0;
vif.hwrite <= 0;
vif.hsize <= 0;
vif.hport <= 0;
end
join_none
endtask
- 在实现item的发送
drive_transfer
时,需要考量如何的点有:item发送的模式(即burst的类型是single还是incr,还是wrap,不同的模型对于的时序是不同的)和数据传输的方向(是读还是写) - 在ahb_ram中他只是支持single的模式,代码如下,在single模式下,调用
do_automic_transfer
做数据的传输,然后在do_automic_transfer
下区分读写
virtual task drive_transfer(REQ r);
case(r.burst_type)
SINGLE : do_automic_transfer(r);
INCR : `uvm_error("TYPEERR", "burst type no supported yet")
WRAP4 : `uvm_error("TYPEERR", "burst type no supported yet")
WRAP8 : `uvm_error("TYPEERR", "burst type no supported yet")
INCR8 : `uvm_error("TYPEERR", "burst type no supported yet")
WRAP16 : `uvm_error("TYPEERR", "burst type no supported yet")
INCR16 : `uvm_error("TYPEERR", "burst type no supported yet")
default : `uvm_error("TYPEERR", "burst type no defined")
endcase
endtask
- 在do_automic_transfer中区分了读写
virtual task do_automic_transfer(REQ r);
case(r.xact_type)
READ : do_read(r);
WRITE : do_write(r);
IDLE_XACT : `uvm_error("TYPEERR", "burst type no supported yet")
default : `uvm_error("TYPEERR", "xact type no defined")
endcase
endtask
- do_write()
在构建do_write时,由于HBURST = SINGLE
所以HTRANS
只能是NONSEQ
和IDLE
,并且NONSEQ是每一次传输的一个起始标志;所以定义两个函数do_init_write(r) do_proc_write(r);
前面的一个定义为单词的传输,后面的为连续传输
virtual task do_write(REQ r);
do_init_write(r);
do_proc_write(r);
endtask
- do_init_read
在这段代码中,实现了协议时序,在拿到总线的权限后的一拍将拿到的item赋值到接口上vif.htrans vif.haddr ; vif.hburst;vif.hsize ; vif.hwrite;
,特别需要注意的是关于Hready
的处理,在上面的时序图中T5----T7,在T6时采样到hready为低,即表示数据slave并没有接受成功,所以data;trans;addr都要保持一拍直到hready拉高;如何去检测hready拉高的状态尤为准要,在这里采取在时钟的下降沿采样;等到hready为高后,将数据读出;在读出数据时,首先要将动态数组data开辟空间初始化,然后将数据从接口赋值到item中读出
这里需要注意何时用时钟块采样何时用时钟
virtual task do_init_read(REQ r);
@(vif.cb_mst iff vif.cb_mst.hgrant === 1'b1);
@(vif.cb_mst);
vif.htrans <= NSEQ;
vif.haddr <= r.addr;
vif.hburst <= r.burst_type;
vif.hsize <= r.burst_size;
vif.hwrite <= 2'b00;
@(vif.cb_mst);
forever begin
@(negedge vif.hclk);
if(vif.hready === 1'b1) begin
break;
end
else
@(vif.cb_mst);
end
r.data = new[r.current_data_beat_num + 1](r.data);
r.data[0] <= vif.hrdata;
//updata
r.trans_type = NSEQ;
r.current_data_beat_num = 0;
r.all_beat_response = new[r.current_data_beat_num + 1];
r.all_beat_response[r.current_data_beat_num ] = response_type_enum'(vif.hresp);
endtask
- do_proc_read
在这里因为是single模式,所以只是作为IDLE模式处理即可
virtual task do_proc_read(REQ r);
@(vif.cb_mst);
vif.htrans <= IDLE;
vif.haddr <= 0;
vif.hwrite <= 0;
vif.hsize <= 0;
vif.hport <= 0;
endtask
三. lvc_ahb_if 更新
添加了时钟块和一些debug信号
debug信号的存在在于便于观察,某些需要观察的信号出现的形式是数字,但是在协议中是以字符串的形式出现的,所以利用枚举类型的静态转换将其转换成字符串
`ifndef LVC_AHB_IF_SV
`define LVC_AHB_IF_SV
interface lvc_ahb_if;
`include "lvc_ahb_defines.svh"
import lvc_ahb_pkg::*;
logic hclk;
logic hresetn;
logic hgrant;
logic [(`LVC_AHB_MAX_DATA_WIDTH - 1):0] hrdata;
logic hready;
logic [1:0] hresp;
logic [(`LVC_AHB_MAX_ADDR_WIDTH - 1):0] haddr;
logic [2:0] hburst;
logic hbusreq;
logic hlock;
logic [3:0] hport;
logic [2:0] hsize;
logic [1:0] htrans;
logic [(`LVC_AHB_MAX_DATA_WIDTH - 1):0] hwdata;
logic hwrite;
response_type_enum debug_hresp;
trans_type_enum debug_htrans;
burst_size_enum debug_hsize;
burst_type_enum debug_hburst;
xact_type_enum debug_xact;
status_enum debug_status;
// debug signals assignment
assign debug_hresp = response_type_enum'(hresp);
assign debug_htrans = trans_type_enum'(htrans);
assign debug_hsize = burst_size_enum'(hsize);
assign debug_hburst = burst_type_enum'(hburst);
//the below signals to be assigned by monitor
//debug_status ..
//debug_xact ..
clocking cb_mst @(posedge hclk);
default input #1ps output #1ps;
input hgrant, hready, hresp, hrdata;
output hbusreq, hlock, htrans, haddr, hwrite, hsize, hburst, hport, hwdata;
endclocking
clocking cb_slv @(posedge hclk);
default input #1ps output #1ps;
output hgrant, hready, hresp, hrdata;
input hbusreq, hlock, htrans, haddr, hwrite, hsize, hburst, hport, hwdata;
endclocking
clocking cb_mon @(posedge hclk);
default input #1ps output #1ps;
output hgrant, hready, hresp, hrdata;
output hbusreq, hlock, htrans, haddr, hwrite, hsize, hburst, hport, hwdata;
endclocking
endinterface
`endif //LVC_AHB_IF_SV