1. 构建generator
stimulator只发送数据,构建generator产生数据发送给stimulator
数组与队列用法见[[SystemVerilog学习笔记(五)]]
1.1改造
定义put_pkt函数,将结构体p放入队列pkts里面
function void put_pkt(input rt_packet_t p);
pkts.push_back(p);
endfunction
定义get_pkt任务,将p从队列pkts里面取出
task get_pkt(output rt_packet_t p);
wait(pkts.size() >0 ) //队列不为空
p = pkts.pop_front();
endtask
数据发送过程
## 1.2改造后的tb.sv ```systemverilog typedef struct{ bit [3:0] src; bit [3:0] dst; bit [7:0] data []; }rt_packet_t;//定义结构体,结构体成员为发送端口,目标端口,发送数据//********************************** stimulator **********************************//
module rt_stimulator(
input clock
,input reset_n
,output logic [15:0] din
,output logic [15:0] frame_n
,output logic [15:0] valid_n
,input [15:0] dout
,input [15:0] valido_n
,input [15:0] busy_n
,input [15:0] frameo_n
);
//for debug purpose from waveform //定义检测状态的变量
typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t;
drv_state_t dbg_state;
byte unsigned dbg_din_chnl0_data;
//receive p from generator
rt_packet_t pkts[$];//定义stimulator中的的队列okts
function void put_pkt(input rt_packet_t p);
pkts.push_back(p); //将generator中传过来的p放入stimulator的pkts中(在pkts队列尾插入p)
endfunction
//reset阶段,复位时,reset_n为低电平,frame_n和valid_n为高电平
initial begin : drive_reset_proc
drive_reset();
end
task drive_reset();
@(negedge reset_n);
dbg_state <= DRV_RESET;
din <= 0;
frame_n <= ‘1;//等效16’hFFFF
valid_n <= ‘1;
endtask
// drive chaannel 0 - chanel 15 (din[15:0])定义数据输出地址及数据
bit [3:0] daddr;
byte unsigned data[];
initial begin : drive_chnl0_proc
rt_packet_t p;
@(negedge reset_n);
repeat(10) @(posedge clock);//延迟10个时钟周期
forever begin
wait(pkts.size()>0);
p = pkts.pop_front(); //将p从队列pkts里面取出
drive_chnl(p.src,p.dst,p.data);//从P中拿到发送端口,目标端口,发送数据
end
//drive_chnl(0,3,’{8’h33,8’h77});//name mapping
//drive_chnl(0,5,’{8’h55,8’h66});//name mapping
end
initial begin : drive_chnl3_proc
@(negedge reset_n);
repeat(10) @(posedge clock);//延迟10个时钟周期
drive_chnl(3,6,’{8’h77,8’h88,8’h22});//name mapping
end
task automatic drive_chnl(bit[3:0] saddr, bit [3:0] daddr, byte unsigned data[]);
$display(“[DRV]src_chnl[%0d],dest_chnl[%0d] data trans started”,saddr,daddr);
// drive address phase 输入地址位阶段
for(int i=0;i<4;i++)begin //4 clock
@(posedge clock);
dbg_state <=DRV_ADDR;
din[saddr] <= daddr[i];
valid_n[saddr] <= $urandom_range(0,1); //valid_n在din的地址输入时间段可为任意值x
frame_n[saddr] <= 1’b0; //frame_n需要为低
end
// drive pad phase //隔离阶段
for (int i=0;i<5;i++)begin //5 clock
@(posedge clock);
dbg_state <=DRV_PAD;
din[saddr] <= 1’b1;
valid_n[saddr] <= 1’b1; //valid_n需为高电平
frame_n[saddr] <= 1’b0; //frame_n需为低电平
end
// drive data phase 传输数据阶段
foreach(data[id])begin
for(int i=0;i<8;i++)begin
@(posedge clock);
dbg_state <=DRV_DATA;
dbg_din_chnl0_data <= data[id];
din[saddr] <= data[id][i];
valid_n[saddr] <=1’b0;
frame_n[saddr] <= (id == data.size()-1 && i == 7) ? 1’b1 : 1’b0;//packet最后一位输出数据时frameo_n为高
end
end
// drive idle phase 闲置(没有数据传输)阶段
@(posedge clock);
dbg_state <=DRV_IDLE;
dbg_din_chnl0_data <= 0;
din[saddr] <= 1’b0;
valid_n[saddr] <= 1’b1;
frame_n[saddr] <= 1’b1;
$display(“[DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished”,saddr,daddr);
endtask
endmodule
//********************************** generator **********************************//
module rt_generator; //generator产生数据交给stimulator
rt_packet_t pkts[$]; //定义队列
function void put_pkt(input rt_packet_t p);
pkts.push_back(p); //将p放入队列pkts里面(在pkts队列尾插入p)
endfunction
task get_pkt(output rt_packet_t p);
wait(pkts.size() >0 ) //队列不为空
p = pkts.pop_front(); //将p从队列pkts里面取出,提取队列首元素
endtask
//generate a random packet
function void gen_pkt(int src = -1, int dst = -1);
endfunction
endmodule
//********************************** tb **********************************//
module tb;
bit clk,rstn;
logic [15:0] din, frame_n, valid_n;
logic [15:0] dout, valido_n, busy_n, frameo_n;
// 产生时钟,周期为10ns
initial
forever #5ns clk <= !clk;
// 产生复位信号
initial begin
#2ns rstn <= 1;
#10ns rstn <= 0;
#10ns rstn <= 1;
end
//例化router为DUT
router dut(
.reset_n(rstn),
.clock(clk),
.* //其余端口名称均相同
);
//例化stimulator
rt_stimulator stim(
.reset_n(rstn),
.clock(clk),
.*
);
//例化generator
rt_generator gen();
initial begin:generate_data_proc//产生数据
rt_packet_t p;
p = ‘{0,3,’{8’h33,8’h77}};
gen.put_pkt(p); //调用genarator里面的put_pkt函数
gen.put_pkt(‘{0,5,’{8’h55,8’h66}});
end
initial begin:genarator_to_stimulator_proc//取出genarator中的数据给stimulator
rt_packet_t p;
forever begin
gen.get_pkt(p);
stim.put_pkt(p);
end
end
endmodule
## 1.3仿真结果
同前面一样
<div align=center><img src=" https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211051044480.png" width="100%" height="100%"></div>
## 1.4问题
但是如果生成数据采用如下方式,不同source_chnl不能同时发送数据(<span style="background:rgba(240, 200, 0, 0.2)">上一节是因为采用了两个initial begin end语句块才能并行发送</span>)
>gen.put_pkt('{0,3,'{8'h33,8'h77}});
>gen.put_pkt('{0,5,'{8'h55,8'h66}});
>gen.put_pkt('{3,6,'{8'h77,8'h88,8'h22}});
>//source_chnl0和source_chnl3不能同时发送
仿真结果如下
<div align=center><img src="https://cloudpicture-1313887899.cos.ap-chongqing.myqcloud.com/blog_picture/202211051554321.png " width="100%" height="100%"></div>
# 2. 来自于不同的source chnl可以并行发送
## 2.1改造
**增加要求**:如果两个chnl没有冲突,可以同时发送
**具体为:**
来自于不同的source chnl就可以并行发送
source chnl、destination chnl不一样可以并行发送
**方法:**
* **定义关联数组**
int src_chnl_status[int]; //关联数组
* **定义判断src_chnl是否空闲的任务**
task判断src chnl是否被占用,是否需要等待。src_chnl_status关联数组里面不含p.src,(表示未占用任何dest_chnl,一开始不满足)。反之,如果含有,表示当前正在使用src_chnl,对应dest_chnl就是p.dst(p.src端口在向p.dst端口发送数据)。如果在给0,1,2...dest_chnl发送数据(被占用),需要等待,,否则不需要等待。直到src_chnl_status[p.src] == -1(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)
>task automatic wait_src_chnl_avail(rt_packet_t p);
if(!src_chnl_status.exists(p.src))//src_chnl_status关联数组里面不含p.src,(表示未占用任何dest_chnl,一开始不满足)
src_chnl_status[p.src] = p.dst;//表示当前正在使用src_chnl,对应dest_chnl就是p.dst(p.src端口在向p.dst端口发送数据)
else if(src_chnl_status[p.src] >= 0)//如果在给0,1,2...dest_chnl发送数据(被占用),需要等待,,否则不需要等待
wait(src_chnl_status[p.src] == -1);//直到src_chnl_status[p.src] == -1(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)
endtask
* **采用fork join none后台线程,判断src chnl是否被占用,是否需要等待**
>fork//后台触发线程,触发线程在后台运行,继续执行剩下内容
begin
wait_src_chnl_avail(p);//判断src chnl是否被占用,是否需要等待
drive_chnl(p.src, p.dst, p.data);//从P中拿到发送端口,目标端口,发送数据
set_src_chnl_avail(p);
end
join_none
* 然后把src_chnl置位
>function automatic set_src_chnl_avail(rt_packet_t p);
src_chnl_status[p.src] = -1;//如果chnl发送完后,把src_chnl置回来(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)
endfunction
## 2.2改造后的tb.sv
```systemverilog
typedef struct{
bit [3:0] src;
bit [3:0] dst;
bit [7:0] data [];
}rt_packet_t;//定义结构体,结构体成员为发送端口,目标端口,发送数据
//********************************** stimulator **********************************//
module rt_stimulator(
input clock
,input reset_n
,output logic [15:0] din
,output logic [15:0] frame_n
,output logic [15:0] valid_n
,input [15:0] dout
,input [15:0] valido_n
,input [15:0] busy_n
,input [15:0] frameo_n
);
//for debug purpose from waveform //定义检测状态的变量
typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t;
drv_state_t dbg_state;
byte unsigned dbg_din_chnl0_data;
int src_chnl_status[int]; //关联数组,后面int:src_chnl的number;前面int:dest_chnl的Number ??
//receive p from generator
rt_packet_t pkts[$];//定义stimulator中的的队列okts
function void put_pkt(input rt_packet_t p);
pkts.push_back(p); //将generator中传过来的p放入stimulator的pkts中(在pkts队列尾插入p)
endfunction
//reset阶段,复位时,reset_n为低电平,frame_n和valid_n为高电平
initial begin : drive_reset_proc
drive_reset();
end
task drive_reset();
@(negedge reset_n);
dbg_state <= DRV_RESET;
din <= 0;
frame_n <= '1;//等效16'hFFFF
valid_n <= '1;
endtask
// 发送数据
bit [3:0] daddr;
byte unsigned data[];
initial begin : drive_chnl_proc
//rt_packet_t p;
@(negedge reset_n);
repeat(10) @(posedge clock);//延迟10个时钟周期
forever begin
automatic rt_packet_t p;//声明一个动态的
wait(pkts.size()>0);
p = pkts.pop_front(); //将p从队列pkts里面取出
fork//后台触发线程,触发线程在后台运行,继续执行剩下内容
begin
wait_src_chnl_avail(p);//判断src chnl是否被占用,是否需要等待
drive_chnl(p.src, p.dst, p.data);//从P中拿到发送端口,目标端口,发送数据
set_src_chnl_avail(p);
end
join_none
end
//drive_chnl(0,3,'{8'h33,8'h77});//name mapping
//drive_chnl(0,5,'{8'h55,8'h66});//name mapping
end
task automatic wait_src_chnl_avail(rt_packet_t p);//判断src chnl是否被占用,是否需要等待
if(!src_chnl_status.exists(p.src))//src_chnl_status关联数组里面不含p.src,(表示未占用任何dest_chnl,一开始不满足)
src_chnl_status[p.src] = p.dst;//表示当前正在使用src_chnl,对应dest_chnl就是p.dst(p.src端口在向p.dst端口发送数据)
else if(src_chnl_status[p.src] >= 0)//如果在给0,1,2...dest_chnl发送数据(被占用),需要等待,,否则不需要等待
wait(src_chnl_status[p.src] == -1);//直到src_chnl_status[p.src] == -1(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)
endtask
function automatic set_src_chnl_avail(rt_packet_t p);
src_chnl_status[p.src] = -1;//如果chnl发送完后,把src_chnl置回来(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)
endfunction
task automatic drive_chnl(bit[3:0] saddr, bit [3:0] daddr, byte unsigned data[]);
$display("@%0t:[DRV]src_chnl[%0d],dest_chnl[%0d] data trans started",$time,saddr,daddr);
// drive address phase 输入地址位阶段
for(int i=0;i<4;i++)begin //4 clock
@(posedge clock);
dbg_state <=DRV_ADDR;
din[saddr] <= daddr[i];
valid_n[saddr] <= $urandom_range(0,1); //valid_n在din的地址输入时间段可为任意值x
frame_n[saddr] <= 1'b0; //frame_n需要为低
end
// drive pad phase //隔离阶段
for (int i=0;i<5;i++)begin //5 clock
@(posedge clock);
dbg_state <=DRV_PAD;
din[saddr] <= 1'b1;
valid_n[saddr] <= 1'b1; //valid_n需为高电平
frame_n[saddr] <= 1'b0; //frame_n需为低电平
end
// drive data phase 传输数据阶段
foreach(data[id])begin
for(int i=0;i<8;i++)begin
@(posedge clock);
dbg_state <=DRV_DATA;
dbg_din_chnl0_data <= data[id];
din[saddr] <= data[id][i];
valid_n[saddr] <=1'b0;
frame_n[saddr] <= (id == data.size()-1 && i == 7) ? 1'b1 : 1'b0;//packet最后一位输出数据时frameo_n为高
end
end
// drive idle phase 闲置(没有数据传输)阶段
@(posedge clock);
dbg_state <=DRV_IDLE;
dbg_din_chnl0_data <= 0;
din[saddr] <= 1'b0;
valid_n[saddr] <= 1'b1;
frame_n[saddr] <= 1'b1;
$display("@%0t:[DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished",$time,saddr,daddr);
endtask
endmodule
//********************************** generator **********************************//
module rt_generator; //generator产生数据交给stimulator
rt_packet_t pkts[$]; //定义队列
function void put_pkt(input rt_packet_t p);
pkts.push_back(p); //将p放入队列pkts里面(在pkts队列尾插入p)
endfunction
task get_pkt(output rt_packet_t p);
wait(pkts.size() >0 ) //队列不为空
p = pkts.pop_front(); //将p从队列pkts里面取出,提取队列首元素
endtask
//generate a random packet
function void gen_pkt(int src = -1, int dst = -1);
endfunction
endmodule
//********************************** tb **********************************//
module tb;
bit clk,rstn;
logic [15:0] din, frame_n, valid_n;
logic [15:0] dout, valido_n, busy_n, frameo_n;
// 产生时钟,周期为10ns
initial
forever #5ns clk <= !clk;
// 产生复位信号
initial begin
#2ns rstn <= 1;
#10ns rstn <= 0;
#10ns rstn <= 1;
end
//例化router为DUT
router dut(
.reset_n(rstn),
.clock(clk),
.* //其余端口名称均相同
);
//例化stimulator
rt_stimulator stim(
.reset_n(rstn),
.clock(clk),
.*
);
//例化generator
rt_generator gen();
initial begin:generate_data_proc//产生数据
rt_packet_t p;
gen.put_pkt('{0,3,'{8'h33,8'h77}}); //调用genarator里面的put_pkt函数
gen.put_pkt('{0,5,'{8'h55,8'h66}});
gen.put_pkt('{3,6,'{8'h77,8'h88,8'h22}});
gen.put_pkt('{4,7,'{8'haa,8'hcc,8'h33}});
end
initial begin:genarator_to_stimulator_proc//取出genarator中的数据给stimulator
rt_packet_t p;
forever begin
gen.get_pkt(p);
stim.put_pkt(p);
end
end
endmodule
2.3仿真结果
3. 增加接口(interface)
interface用法见[[SystemVerilog学习笔记(三)]]
3.1改进
增加接口模块
interface rt_interface();
logic clock;
logic reset_n;
logic [15:0] din;
logic [15:0] frame_n;
logic [15:0] valid_n;
logic [15:0] dout;
logic [15:0] valido_n;
logic [15:0] busy_n;
logic [15:0] frameo_n;
endinterface
3.2改造后的tb.sv
typedef struct{
bit [3:0] src;
bit [3:0] dst;
bit [7:0] data [];
}rt_packet_t;//定义结构体,结构体成员为发送端口,目标端口,发送数据
interface rt_interface();
logic clock;
logic reset_n;
logic [15:0] din;
logic [15:0] frame_n;
logic [15:0] valid_n;
logic [15:0] dout;
logic [15:0] valido_n;
logic [15:0] busy_n;
logic [15:0] frameo_n;
endinterface
//********************************** stimulator **********************************//
module rt_stimulator(rt_interface intf);
//for debug purpose from waveform //定义检测状态的变量
typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t;
drv_state_t dbg_state;
byte unsigned dbg_din_chnl0_data;
int src_chnl_status[int]; //关联数组,后面int:src_chnl的number;前面int:dest_chnl的Number ??
//receive p from generator
rt_packet_t pkts[$];//定义stimulator中的的队列okts
function void put_pkt(input rt_packet_t p);
pkts.push_back(p); //将generator中传过来的p放入stimulator的pkts中(在pkts队列尾插入p)
endfunction
//reset阶段,复位时,reset_n为低电平,frame_n和valid_n为高电平
initial begin : drive_reset_proc
drive_reset();
end
task drive_reset();
@(negedge intf.reset_n);
dbg_state <= DRV_RESET;
intf.din <= 0;
intf.frame_n <= '1;//等效16'hFFFF
intf.valid_n <= '1;
endtask
// 发送数据
bit [3:0] daddr;
byte unsigned data[];
initial begin : drive_chnl_proc
//rt_packet_t p;
@(negedge intf.reset_n);
repeat(10) @(posedge intf.clock);//延迟10个时钟周期
forever begin
automatic rt_packet_t p;//声明一个动态的
wait(pkts.size()>0);
p = pkts.pop_front(); //将p从队列pkts里面取出
fork//后台触发线程,触发线程在后台运行,继续执行剩下内容
begin
wait_src_chnl_avail(p);//判断src chnl是否被占用,是否需要等待
drive_chnl(p.src, p.dst, p.data);//从P中拿到发送端口,目标端口,发送数据
set_src_chnl_avail(p);
end
join_none
end
//drive_chnl(0,3,'{8'h33,8'h77});//name mapping
//drive_chnl(0,5,'{8'h55,8'h66});//name mapping
end
task automatic wait_src_chnl_avail(rt_packet_t p);//判断src chnl是否被占用,是否需要等待
if(!src_chnl_status.exists(p.src))//src_chnl_status关联数组里面不含p.src,(表示未占用任何dest_chnl,一开始不满足)
src_chnl_status[p.src] = p.dst;//表示当前正在使用src_chnl,对应dest_chnl就是p.dst(p.src端口在向p.dst端口发送数据)
else if(src_chnl_status[p.src] >= 0)//如果在给0,1,2...dest_chnl发送数据(被占用),需要等待,,否则不需要等待
wait(src_chnl_status[p.src] == -1);//直到src_chnl_status[p.src] == -1(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)
endtask
function automatic set_src_chnl_avail(rt_packet_t p);
src_chnl_status[p.src] = -1;//如果chnl发送完后,把src_chnl置回来(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)
endfunction
task automatic drive_chnl(bit[3:0] saddr, bit [3:0] daddr, byte unsigned data[]);
$display("@%0t:[DRV]src_chnl[%0d],dest_chnl[%0d] data trans started",$time,saddr,daddr);
// drive address phase 输入地址位阶段
for(int i=0;i<4;i++)begin //4 clock
@(posedge intf.clock);
dbg_state <=DRV_ADDR;
intf.din[saddr] <= daddr[i];
intf.valid_n[saddr] <= $urandom_range(0,1); //valid_n在din的地址输入时间段可为任意值x
intf.frame_n[saddr] <= 1'b0; //frame_n需要为低
end
// drive pad phase //隔离阶段
for (int i=0;i<5;i++)begin //5 clock
@(posedge intf.clock);
dbg_state <=DRV_PAD;
intf.din[saddr] <= 1'b1;
intf.valid_n[saddr] <= 1'b1; //valid_n需为高电平
intf.frame_n[saddr] <= 1'b0; //frame_n需为低电平
end
// drive data phase 传输数据阶段
foreach(data[id])begin
for(int i=0;i<8;i++)begin
@(posedge intf.clock);
dbg_state <=DRV_DATA;
dbg_din_chnl0_data <= data[id];
intf.din[saddr] <= data[id][i];
intf.valid_n[saddr] <=1'b0;
intf.frame_n[saddr] <= (id == data.size()-1 && i == 7) ? 1'b1 : 1'b0;//packet最后一位输出数据时frameo_n为高
end
end
// drive idle phase 闲置(没有数据传输)阶段
@(posedge intf.clock);
dbg_state <=DRV_IDLE;
dbg_din_chnl0_data <= 0;
intf.din[saddr] <= 1'b0;
intf.valid_n[saddr] <= 1'b1;
intf.frame_n[saddr] <= 1'b1;
$display("@%0t:[DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished",$time,saddr,daddr);
endtask
endmodule
//********************************** generator **********************************//
module rt_generator; //generator产生数据交给stimulator
rt_packet_t pkts[$]; //定义队列
function void put_pkt(input rt_packet_t p);
pkts.push_back(p); //将p放入队列pkts里面(在pkts队列尾插入p)
endfunction
task get_pkt(output rt_packet_t p);
wait(pkts.size() >0 ) //队列不为空
p = pkts.pop_front(); //将p从队列pkts里面取出,提取队列首元素
endtask
//generate a random packet
function void gen_pkt(int src = -1, int dst = -1);
endfunction
endmodule
//********************************** tb **********************************//
module tb;
bit clk,rstn;
logic [15:0] din, frame_n, valid_n;
logic [15:0] dout, valido_n, busy_n, frameo_n;
// 产生时钟,周期为10ns
initial
forever #5ns clk <= !clk;
// 产生复位信号
initial begin
#2ns rstn <= 1;
#10ns rstn <= 0;
#10ns rstn <= 1;
end
//例化router为DUT
router dut(
.reset_n(rstn),
.clock(clk),
.frame_n(intf.frame_n),
.valid_n(intf.valid_n),
.dout(intf.dout),
.busy_n(intf.busy_n),
.valido_n(intf.valido_n),
.frameo_n(intf.frameo_n)
);
rt_interface intf();//例化接口
assign intf.reset_n = rstn;
assign intf.clock = clk;
//例化stimulator
rt_stimulator stim(intf);
//例化generator
rt_generator gen();
initial begin:generate_data_proc//产生数据
rt_packet_t p;
gen.put_pkt('{0,3,'{8'h33,8'h77}}); //调用genarator里面的put_pkt函数
gen.put_pkt('{0,5,'{8'h55,8'h66}});
gen.put_pkt('{3,6,'{8'h77,8'h88,8'h22}});
gen.put_pkt('{4,7,'{8'haa,8'hcc,8'h33}});
end
initial begin:genarator_to_stimulator_proc//取出genarator中的数据给stimulator
rt_packet_t p;
forever begin
gen.get_pkt(p);
stim.put_pkt(p);
end
end
endmodule
3.3仿真结果
只增加了接口,仿真结果未变
硬件层次结构如下 # 4. 初步TestBench概览5. 增加monitor
5.1 改造
增加组件monitor,监测输入数据和输出数据
监测数据输入的task:
task automatic mon_chnl_in (bit[3:0]id);//监测数据输入的任务
rt_packet_t pkt; //定义结构体变量
forever begin
//clear content for the same struct variable。清除pkt
pkt.data.delete();
pkt.src = id; //第id个输入端口
//monitor specific channel-in data and put it into the queue
// monitor address phase
@(negedge intf.frame_n[id]);//监测frame_n下降沿(frame_n由时钟驱动)
$display ("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans started",$time,pkt.src,pkt.dst);
for(int i=0; i<4; i++)begin
@(negedge intf.clock); //frame_n下降沿后监测4个clk negedge
pkt.dst[i]= intf.din[id];
end
//pass pad phase 不考虑pad阶段是否满足协议要求
repeat(5) @(negedge intf.clock);
do begin
pkt.data = new [pkt.data.size + 1](pkt.data);//创建动态数组并复制pkt.data
for ( int i=0; i<8; i++) begin
@(negedge intf.clock); //在8个clk negedge监测8bit数据
pkt.data[pkt.data.size-1][i] = intf.din[id];
end
end while(!intf.frame_n[id]);
in_pkts[id].push_back(pkt); //将monitor拿到的数据放入in_pkts
$display("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans [%0p] finished",$time,pkt.src,pkt.dst,pkt.data);
end
endtask
监测数据输入的task:
task automatic mon_chnl_out(bit[ 3:0]id);//监测数据输出的任务
rt_packet_t pkt;
forever begin
//clear content for the same struct variable
pkt.data.delete();
pkt.src = 0;
pkt.dst = id;
@(negedge intf.frameo_n[id]);
$display( "@%0t: [MON] CH_OUT dest_chn1[%0d] data trans started",$time,pkt.dst);
do begin
pkt.data = new [ pkt.data.size + 1](pkt.data);
for(int i=0;i<8; i++) begin
@(negedge intf.clock iff !intf.valido_n[id]);//clock与valido_n信号同时为低
pkt.data[pkt.data.size-1][i]= intf.dout [id];
end
end while(!intf.frameo_n[id]);
out_pkts[id].push_back(pkt);
$display("@%0t: [MON] CH_OUT dest_chn1[%0d] data trans [%0p] finished",$time,pkt.dst,pkt.data);
//monitor specific channel-out data and put it into the queue
end
endtask
5.2 改造后的tb.sv
typedef struct{
bit [3:0] src;
bit [3:0] dst;
bit [7:0] data [];
}rt_packet_t;//定义结构体,结构体成员为发送端口,目标端口,发送数据
interface rt_interface();
logic clock;
logic reset_n;
logic [15:0] din;
logic [15:0] frame_n;
logic [15:0] valid_n;
logic [15:0] dout;
logic [15:0] valido_n;
logic [15:0] busy_n;
logic [15:0] frameo_n;
endinterface
//********************************** stimulator **********************************//
module rt_stimulator(
rt_interface intf
);
//for debug purpose from waveform //定义检测状态的变量
typedef enum {DRV_RESET,DRV_IDLE,DRV_ADDR,DRV_PAD,DRV_DATA} drv_state_t;
drv_state_t dbg_state;
byte unsigned dbg_din_chnl0_data;
int src_chnl_status[int]; //关联数组,后面int:src_chnl的number;前面int:dest_chnl的Number ??
//generator传送p给stimulator
rt_packet_t pkts[$];//定义stimulator中的的队列pkts
function void put_pkt(input rt_packet_t p);
pkts.push_back(p); //将generator中传过来的p放入stimulator的pkts中(在pkts队列尾插入p)
endfunction
//reset阶段,复位时,reset_n为低电平,frame_n和valid_n为高电平
initial begin : drive_reset_proc
drive_reset();
end
task drive_reset();
@(negedge intf.reset_n);
dbg_state <= DRV_RESET;
intf.din <= 0;
intf.frame_n <= '1;//等效16'hFFFF
intf.valid_n <= '1;
endtask
// 发送数据
initial begin : drive_chnl_proc
//rt_packet_t p;
@(negedge intf.reset_n);
repeat(10) @(posedge intf.clock);//延迟10个时钟周期
forever begin
automatic rt_packet_t p;//声明一个动态的
wait(pkts.size()>0);
p = pkts.pop_front(); //将p从队列pkts里面取出
fork//后台触发线程,触发线程在后台运行,继续执行剩下内容
begin
wait_src_chnl_avail(p);//判断src chnl是否被占用,是否需要等待
drive_chnl(p.src, p.dst, p.data);//从P中拿到发送端口,目标端口,发送数据
set_src_chnl_avail(p);
end
join_none
end
end
task automatic wait_src_chnl_avail(rt_packet_t p);//判断src chnl是否被占用,是否需要等待
if(!src_chnl_status.exists(p.src))//src_chnl_status关联数组里面不含p.src,(表示未占用任何dest_chnl,一开始不满足)
src_chnl_status[p.src] = p.dst;//表示当前正在使用src_chnl,对应dest_chnl就是p.dst(p.src端口在向p.dst端口发送数据)
else if(src_chnl_status[p.src] >= 0)//如果在给0,1,2...dest_chnl发送数据(被占用),需要等待,,否则不需要等待
wait(src_chnl_status[p.src] == -1);//直到src_chnl_status[p.src] == -1(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)
endtask
function automatic set_src_chnl_avail(rt_packet_t p);
src_chnl_status[p.src] = -1;//如果chnl发送完后,把src_chnl置回来(自定义的dest_chnl端口数之外任意的数,表示未占用任何dest_chnl)
endfunction
task automatic drive_chnl(bit[3:0] saddr, bit [3:0] daddr, byte unsigned data[]);
$display("@%0t: [DRV]src_chnl[%0d],dest_chnl[%0d] data trans started",$time,saddr,daddr);
// drive address phase 输入地址位阶段
for(int i=0;i<4;i++)begin //4 clock
@(posedge intf.clock);
dbg_state <=DRV_ADDR;
intf.din[saddr] <= daddr[i];
intf.valid_n[saddr] <= $urandom_range(0,1); //valid_n在din的地址输入时间段可为任意值x
intf.frame_n[saddr] <= 1'b0; //frame_n需要为低
end
// drive pad phase //隔离阶段
for (int i=0;i<5;i++)begin //5 clock
@(posedge intf.clock);
dbg_state <=DRV_PAD;
intf.din[saddr] <= 1'b1;
intf.valid_n[saddr] <= 1'b1; //valid_n需为高电平
intf.frame_n[saddr] <= 1'b0; //frame_n需为低电平
end
// drive data phase 传输数据阶段
foreach(data[id])begin
for(int i=0;i<8;i++)begin
@(posedge intf.clock);
dbg_state <=DRV_DATA;
dbg_din_chnl0_data <= data[id];
intf.din[saddr] <= data[id][i];
intf.valid_n[saddr] <=1'b0;
intf.frame_n[saddr] <= (id == data.size()-1 && i == 7) ? 1'b1 : 1'b0;//packet最后一位输出数据时frameo_n为高
end
end
// drive idle phase 闲置(没有数据传输)阶段
@(posedge intf.clock);
dbg_state <=DRV_IDLE;
dbg_din_chnl0_data <= 0;
intf.din[saddr] <= 1'b0;
intf.valid_n[saddr] <= 1'b1;
intf.frame_n[saddr] <= 1'b1;
$display("@%0t: [DRV]src_chnl[%0d],dest_chnl[%0d] data trans finished",$time,saddr,daddr);
endtask
endmodule
//********************************** generator **********************************//
module rt_generator; //generator产生数据交给stimulator
rt_packet_t pkts[$]; //定义队列
function void put_pkt(input rt_packet_t p);
pkts.push_back(p); //将p放入队列pkts里面(在pkts队列尾插入p)
endfunction
task get_pkt(output rt_packet_t p);
wait(pkts.size() >0 ) //队列不为空
p = pkts.pop_front(); //将p从队列pkts里面取出,提取队列首元素
endtask
//generate a random packet
function void gen_pkt(int src = -1, int dst = -1);
endfunction
endmodule
//********************************** monitor **********************************//
module rt_monitor(rt_interface intf);
rt_packet_t in_pkts[16][$];
rt_packet_t out_pkts[16][$];
initial begin : mon_chnl_proc
foreach(in_pkts[i]) begin
automatic int chid = i;
fork
mon_chnl_in(chid);//每个输入端口均调用mon_chnl_in任务,监测数据输入
mon_chnl_out(chid);//每个输出端口均调用mon_chnl_out任务,监测数据输入
join_none
end
end
task automatic mon_chnl_in (bit[3:0]id);//监测数据输入的任务
rt_packet_t pkt; //定义结构体变量
forever begin
//clear content for the same struct variable。清除pkt
pkt.data.delete();
pkt.src = id; //第id个输入端口
//monitor specific channel-in data and put it into the queue
// monitor address phase
@(negedge intf.frame_n[id]);//监测frame_n下降沿(frame_n由时钟驱动)
for(int i=0; i<4; i++)begin
@(negedge intf.clock); //frame_n下降沿后监测4个clk negedge
pkt.dst[i]= intf.din[id];
end
$display ("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans started",$time,pkt.src,pkt.dst);
//pass pad phase 不考虑pad阶段是否满足协议要求
repeat(5) @(negedge intf.clock);
do begin
pkt.data = new [pkt.data.size + 1](pkt.data);//创建动态数组并复制pkt.data
for ( int i=0; i<8; i++) begin
@(negedge intf.clock); //在8个clk negedge监测8bit数据
pkt.data[pkt.data.size-1][i] = intf.din[id];
end
end while(!intf.frame_n[id]);
in_pkts[id].push_back(pkt); //将monitor拿到的数据放入in_pkts
$display("@%0t: [MON] src_chn1[%0d] & dest_chn1[%0d] data trans [%0p] finished",$time,pkt.src,pkt.dst,pkt.data);
end
endtask
task automatic mon_chnl_out(bit[ 3:0]id);//监测数据输出的任务
rt_packet_t pkt;
forever begin
//clear content for the same struct variable
pkt.data.delete();
pkt.src = 0;
pkt.dst = id;
@(negedge intf.frameo_n[id]);
$display( "@%0t: [MON] CH_OUT dest_chn1[%0d] data trans started",$time,pkt.dst);
do begin
pkt.data = new [ pkt.data.size + 1](pkt.data);
for(int i=0;i<8; i++) begin
@(negedge intf.clock iff !intf.valido_n[id]);//clock与valido_n信号同时为低
pkt.data[pkt.data.size-1][i]= intf.dout [id];
end
end while(!intf.frameo_n[id]);
out_pkts[id].push_back(pkt);
$display("@%0t: [MON] CH_OUT dest_chn1[%0d] data trans [%0p] finished",$time,pkt.dst,pkt.data);
//monitor specific channel-out data and put it into the queue
end
endtask
endmodule
//********************************** tb **********************************//
module tb;
bit clk,rstn;
logic [15:0] din, frame_n, valid_n;
logic [15:0] dout, valido_n, busy_n, frameo_n;
// 产生时钟,周期为10ns
initial
forever #5ns clk <= !clk;
// 产生复位信号
initial begin
#2ns rstn <= 1;
#10ns rstn <= 0;
#10ns rstn <= 1;
end
//例化router为DUT
router dut(
.reset_n(rstn),
.clock(clk),
.frame_n(intf.frame_n),
.valid_n(intf.valid_n),
.din(intf.din),
.dout(intf.dout),
.busy_n(intf.busy_n),
.valido_n(intf.valido_n),
.frameo_n(intf.frameo_n)
);
rt_interface intf();//例化接口
assign intf.reset_n = rstn;
assign intf.clock = clk;
//例化stimulator
rt_stimulator stim(intf);
//例化Monitor
rt_monitor mon(intf);
//例化generator
rt_generator gen();
initial begin:generate_data_proc//产生数据
rt_packet_t p;
gen.put_pkt('{0,3,'{8'h33,8'h77}}); //调用genarator里面的put_pkt函数
gen.put_pkt('{0,5,'{8'h55,8'h66}});
gen.put_pkt('{3,6,'{8'h77,8'h88,8'h22}});
gen.put_pkt('{4,7,'{8'haa,8'hcc,8'h33}});
end
initial begin:genarator_to_stimulator_proc//取出genarator中的数据给stimulator
rt_packet_t p;
forever begin
gen.get_pkt(p);
stim.put_pkt(p);
end
end
endmodule