一. bit 和 logic
二. 数组的方法
2.1 数组的分类:定宽数组,动态数组,队列,关联数组
2.2 数组的方法:缩减,定位,排序
三. 对代码中的问题解释
一. bit 和 logic
关于地址使用 bit 数据使用 logic , 我的总结如下:
-
logic 类型只能有一个驱动,可以被连续赋值
-
logic 是 四状态类型(0, 1, X, Z)
-
bit 是 二状态类型 (0,1)
-
它们都是无符号的
故,bit 更适合地址,logic 更适合数据,(后续还有发现,俺再来改)
二. 数组的方法
2.1 数组的分类:定宽数组,动态数组,队列,关联数组
本实验使用的是队列,并对队列进行了foreach 和 for 循环遍历,还使用了一些数组的方法如下:
其中,foreach中的索引index不用声明。
学习就要总结,故对数组的方法(适用于非合并数组) 总结如下:
2.2 数组的方法:缩减,定位,排序
数组的缩减: 假定数组名为 arrayname
求和: arrayname.sum()
求积:arrayname.product()
与:arrayname.and()
或:arrayname.or()
异或: arrayname.xor()
求数组的宽度:都可以使用系统函数 $size(arrayname), 对于队列和动态数组还可以使用 arrayname.size()
从数组中,随机选择一个元素的方法,systemverilog没有提供专门的方法,可以使用$urandom_range($size(arrayname) - 1)
, 队列和动态数组还可以使用 $urandom_range(arrayname.size() - 1)
(关于$urandom_range
可以看绿皮书6.10节)
数组的定位:返回的是队列,不是标量 ( 定义 int d[] = '{9,1,8,3,4,4}😉
找最小值: d.min() // {1}
找最大值: d.max() // {9}
找数组中具有唯一值的队列:d.unique() // {9,1,8,3}
有关find的:
d.find with ( item > 3) // {9,8,4,4} 返回满足条件的元素的队列
d.find_index with (item > 3) // {0,2,4,5} 返回满足条件的元素索引的队列
d.find_first with (item > 99) // {} 返回满足条件的第一个元素
d.find_first_index with (item == 8) // {2} 返回满足条件的第一个元素的索引
d.find_last with (item == 4) // {4} 返回满足条件的最后一个元素
d.find_last_index with (item == 4) // {5} 返回满足条件的最后一个元素的索引
有关sum的:
d.sum with ( item > 7) // 2 有两个元素满足条件 故返回2
d.sum with ((item > 7) * item) // 17 满足条件的元素的和
d.sum with (item <8 ) // 4
d.sum with (item < 8 ? item : 0 ) // 12
d.sum with (item == 4 ) // 2
总结是: d.sum 求的是后面的条件表达式为真的次数,返回值是index(索引)的数组定位方法,返回的值是 int , 不是 integer
数组的排序: int d[] = '{9,1,8,3,4,4};
d.reverse() // '{4,4,3,8,1,9} 翻转数组
d.sort() // '{1,3,4,4,8,9} 从小到大排序
d.rsort() // '{9,8,4,4,3,1} 从大到小排序
d.shuffle() // '{9,4,3,8,1,4} 随机排序
三. 对代码中的问题解释
`timescale 1ns/100ps
program automatic test(router_io.TB rtr_io);
bit[3:0] sa; //source address (input port)
bit[3:0] da; // destination address (output port)
logic [7:0] payload[$]; // packet data array
int run_for_n_packets;
initial begin
reset();
run_for_n_packets = 21;
repeat(run_for_n_packets) begin
gen();
send();
end
repeat(10) @(rtr_io.cb); // 问题1: send() 后为什么要再触发10个clocking,10个够不够?
end
task reset();
rtr_io.reset_n = 1'b0;
rtr_io.cb.frame_n <= '1;
rtr_io.cb.valid_n <= '1;
#2 rtr_io.cb.reset_n <= 1'b1;
repeat(15) @(rtr_io.cb);
endtask: reset
//实验2的重点1在这个激励生成器
task gen();
sa = 3;
da = 7;
payload.delete();
repeat($urandom_range(2,4)) //$urandom_range(2,4) 产生值在2-4范围内的
payload.push_back($urandom); // $urandom 平均分布, 返回32位无符号随机数 // 问题2: 送数据送了多少次,每次送多少
endtask: gen
//实验2的重点2在这三个任务的撰写
task send();
send_addrs();
send_pad();
send_payload();
endtask: send
task send_addrs();
rtr_io.cb.frame_n[sa] <= 1'b0; // 问题3: 怎么知道frame_n在时钟上升沿拉低的?
rtr_io.cb.valid_n[sa] <= 1'b1;
for(int i = 0; i < 4; i++) begin
rtr_io.cb.din[sa] <= da[i]; //送 4位地址到 3号端口, 从低到高
@(rtr_io.cb); // 问题4: 这个时钟触发等于什么?
end
endtask: send_addrs
task send_pad();
rtr_io.cb.frame_n[sa] <= 1'b0;
rtr_io.cb.valid_n[sa] <= 1'b1;
rtr_io.cb.din[sa] <= 1'b1;
repeat(5) @(rtr_io.cb);
endtask: send_pad
task send_payload();
foreach(payload[index]) begin
for(int i = 0; i < 8; i++) begin
rtr_io.cb.din[sa] <= payload[index][i];
rtr_io.cb.valid_n[sa] <= 1'b0;
rtr_io.cb.frame_n[sa] <= ((i == 7) && (index == payload.size() - 1 )) // frame_n 比 valid_n 早一个时钟沿拉高
@(rtr_io.cb)
end
end
rtr_io.cb.valid_n[sa] <= 1'b1;
endtask: send_payload
endprogram: test
问题1: send() 后为什么要再触发10个clocking,10个够不够?
我理解是在发数据前正好有10个时钟,所以在最后再触发10个时钟,是为了等待下次的数据发送,10个应该是至少的
问题2: 送数据送了多少次,每次送多少?
送了2-4之间的随机次,每次送8位,因为payload定义为了 8位
问题3: 怎么知道frame_n在时钟上升沿拉低的?
这个和问题4有关联,因为是非阻塞赋值( <= ),故只有在rtr_io.cb触发的时候才赋值,
问题4: 这个时钟触发等于什么?
@(rtr_io.cb) 等于 @(posedge clock)
因为在interface中定义了 cb ,cb模块是有上升沿的时钟,所以问题3也就是在时钟上升沿拉低的