多选题:如果需要降低仿真时的内存负载,哪些措施是合理的:ABCD
A、降低模块间的信号跳变频率;B、只在必要时创建软件对象;
C、在不需要时钟时关闭时钟; D、在数据带宽需求低时降低时钟频率。
注:动态消耗取决于单位时刻(delta-cycle)里有多少事件发生,事件越多触发的时序/组合逻辑越多,从而增加动态运算量;
fork...join
需要所有线程都执行完,才能继续执行fork...join_any
只需要任何其中一个线程执行完,就可以继续执行fork...join_none
无需等待正在执行的线程,直接继续执行接下来的程序wait fork
语句来等待所有子线程结束。task run_threads;
...
fork
check_trans(tr1);
check_trans(tr2);
check_trans(tr3);//三个线程
join_none //虽然直接跳过了所有子线程,但它们会在后台执行
...
wait fork; //等待所有fork中的线程结束再退出task
endtask
parameter TIME_OUT = 1000;
task check_trans(Transaction tr);
fork
begin
//等待回应,或达到某个最大时延
fork : timeout_block
begin
wait (bus.cb.addr == tr.addr);
$display("@%0t: Addr match %d", $time, tr.addr);
end
#TIME_OUT $display("@%0t: Error: timeout", $time);
join_any
disable timeout_block; //fork块里有两个并行线程,但只希望有一个执行完就可以退出
end
join_none
endtask
initial begin
check_trans(tr0); //线程0
//创建一个线程来限制disable fork的作用范围
fork //线程1
begin
check_trans(tr1); //线程2
fork //线程3
check_trans(tr2); //线程4
join
//停止线程1-4,单独保留线程0
#(TIME_OUT/2) disable fork
end
join
end
task wait_for_time_out(int id);
if(id == 0)
fork
begin
#2;
$display("%0t: disable wait_for_time_out", $time);
disable wait_for_time_out;
end
join_none
fork : just_a_little
begin
$display("@%0t: %m: %0d entering thread", $time, id);
#TIME_OUT;
$display("@%0t: %m: %0d done", $time, id);
end
join_none
endtask
initial begin
//任务被调用了三次,从而衍生了三个线程
wait_for_time_out(0);
//在#2延时之后禁止了该任务,而由于三个线程均是同名线程
//因此这些线程都被禁止了,最终都没有完成
wait_for_time_out(1);
wait_for_time_out(2);
#(TIME_OUT * 2) $display("@%0t: All done", $time);
end
@
操作符的事件。这个操作符是边沿敏感的,所以总是阻塞着、等待事件的变化;event e1, e2; //注意event不需要new
initial begin //第一个初始化块启动,触发e1事件,然后阻塞在e2上
$display("@%0t: 1: before trigger", $time);
-> e1;
@e2;
$display("@%0t: 1: after trigger", $time);
end
initial begin //第二个初始化块启动,触发e2事件,然后阻塞在e1上
$display("@%0t: 2: before trigger", $time);
-> e2;
@e1;
$display("@%0t: 2: after trigger", $time);
end //e1和e2在同一时刻被触发
//但由于delta cycle的时间差使得两个初始化块可能无法等到e1或e2
输出结果:
@0: 1: before trigger
@0: 2: before trigger
@0: 1: after trigger
event e1, e2;
initial begin
$display("@%0t: 1: before trigger", $time);
-> e1;
wait (e2.triggered());
$display("@%0t: 1: after trigger", $time);
end
initial begin
$display("@%0t: 2: before trigger", $time);
-> e2;
wait (e1.triggered());
$display("@%0t: 2: after trigger", $time);
end
输出结果:
@0: 1: before trigger
@0: 2: before trigger
@0: 1: after trigger
@0: 2: after trigger
module road;
initial begin
automatic car byd = new();
byd.drive();
end
endmodule
输出结果:
# car is launched
# car is moving
class car;
bit start = 0;
task launch();
start = 1;
$display("car is launched");
endtask
task move();
wait(start == 1);
$display("car is moving");
endtask
task drive();
fork
this.launch();
this.move();
join
endtask
endclass //在car::drive()中同时启动两个线程move和launch。
//move通过两个线程之间的共享信号car::start判断什么时候可以行驶
//即利用了wait语句完成线程launch通知线程move
class car;
event e_start;
bit start = 0;
task launch();
-> e_start;
$display("car is launched");
endtask
task move();
wait(e_start.triggerd);
$display("car is moving");
endtask
task drive();
fork
this.launch();
this.move();
join
endtask
endclass
module road;
initial begin
automatic car byd = new();
byd.drive();
byd.speedup();
byd.speedup();
byd.speedup();
end
endmodule
输出结果:
# car is launched
# car is moving
# speed is 1
# speed is 2
# speed is 3
class car;
event e_start;
event e_speedup;
int speed = 0;
...
task speedup();
#10ns;
-> e_speedup;
endtask
task display();
forever begin
@e_speedup;
speed++;
$display("speed is %0d", speed);
endtask
task drive();
fork
this.launch();
this.move();
this.display();
join—_none
endtask
endclass
new()
方法可以创建一个带单个或多个钥匙的semaphore,使用get()
方法可以获取一个或多个钥匙,而put()
方法可以返回一个或多个钥匙(没有传递参数默认钥匙数量为1);try_get()
函数,返回1表示有足够多的钥匙,返回0则表示钥匙不够,钥匙遵循“先到先得”的原则;program automatic test(bus_ifc.TB bus);
semaphore sem; //创建一个semaphore
initial begin
sem = new(1); //分配一个钥匙
fork
sequencer();
sequencer(); //产生两个总线事务线程
join
end
task sequencer;
repeat($urandom%10) //随机等待0-9个周期
@bus.cb;
sendTrans(); //执行总显示无
endtask
task sendTrans;
sem.get(1); //获取总线钥匙
@bus.cb;
bus.cb.addr <= t.addr; //把信号驱动到总线上
...
sem.put(1); //处理完成返回钥匙,不返回会程序会死锁
endtask
endprogram
//夫妻取钥匙开车示例——资源共享
class car;
semaphore key;
function new();
key = new(1);
endfunction
task get_on(string p);
$display("%s is waiting for the key", p);
key.get();
#1ns;
$display*("%s got on the car", p);
endtask
task get_off(string p);
$display("%s got off the car". p);
key.put();
#1ns;
$display("%s returned the key", p);
endtask
endclass
module family;
car byd = new();
string p1 = "husband";
string p2 = "wife";
initial begin
fork
begin //丈夫开车
byd.get_on(p1);
byd.get_off(p1);
end
begin //妻子开车
byd.get_on(p2);
byd.get_off(p2);
end
join //不知道谁先谁后
end
endmodule
输出结果:
# husband is waiting for the key
# wife is waiting for the key
# husband got on the car
# husband got off the car
# husband returned the key
#wife got on the car
#wife got off the car
# wife returned the key
//车管家保管车钥匙(但没有优先级)
class carkeep;
int key = 1;
string q[s];
string user;
task keep_car();
fork
forever begin //管理钥匙和分发
wait(q.size()!=0 && key!=0);
user = q.pop_front();
key--;
end
join_none;
endtask
task get_key(string p); //拿钥匙
q.push_back(p);
wait(user == p);
endtask
task put_key(string p); //还钥匙
if(user == p) begin
user = "none";
key++;
end
endtask
endclass
class car;
carkeep keep;
function new();
keep = new();
endfunction
task drive();
keep.keep_car();
endtask
task get_on(string p);
$display("%s is waiting for the key", p);
keep.get_key(p);
#1ns;
$display("%s got on the car", p);
endtask
task get_off(string p);
$display("%s got off the key", p);
keep.put_key(p);
#1ns;
$display("%s returned the key", p);
endtask
endclass
module family;
car byd = new();
string p1 = "husband";
string p2 = "wife";
initial begin
byd.drive();
fork
begin //丈夫开车
byd.get_on(p1);
byd.get_off(p1);
end
begin //妻子开车
byd.get_on(p2);
byd.get_off(p2);
end
join
end
endmodule
输出结果:
# husband is waiting for the key
# wife is waiting for the key
# husband got on the car
# husband got off the car
# husband returned the key
# wife got on the car
# wife got off the car
# wife returned the key
new()
来例化,参数size为0或没有指定时,信箱则表示无限大;put()
把数据放入mailbox,get()
从信箱移除数据,peek()
可以获取对信箱里数据的拷贝而不移除它(若信箱为满,则put()阻塞;为空则get()阻塞);mailbox与queue的区别:
new()
例化,而队列只需要声明;put()
和get()
是阻塞方法,队列的push_back()
和pop_front()
方法是非阻塞的,故使用queue取数时需wait(queue.size()>0)
才可以在其后对非空的queue做取数操作。调用阻塞方法只能在task中调用,因为阻塞方法是耗时的;调用非阻塞方法在task和function中都可以;mailbox使用细节:
new(N)
的方式使其变为定长容器,这样在负载到长度N以后便无法再写入,若用new()
的方式,则表示信箱容量不限大小;put()/get()/peek()
等阻塞方法,也可考虑用try_put()/try_get()/try_peek()
等非阻塞方法;mailbox #(type = T)
的方式声明;program automatic bounded;
mailbox mbx;
initial begin
mbx = new(1); //容量为1
fork
//Producer线程
for(int i=1; i<4; i++) begin
$display("Producer: before put(%0d)", i);
mbx.put(i);
$display("Producer: after put(%0d)", i);
end
//Consumer线程
repeat(4) begin
int j;
#1ns mbx.get(j);
$display("Consumer: after get(%0d)", j);
end
join
end
endprogram
输出结果:
Producer:before put(1)
Producer:after put(1)
Producer:before put(2)
Consumer:after get(1)
Producer:after put(2)
Producer:before put(3)
Consumer:after get(2)
Producer:after put(3)
Consumer:after get(3)
//实时显示车子的状态(包括车速、油量和温度),利用mailbox满足多个线程间的数据通信
class car;
mailbox tmp_mb;
mailbox spd_mb;
mailbox fuel_mb;
int sample_period;
function new();
sample_period = 10;
tmp_mb = new();
spd_mb = new();
fuel_mb = new();
endfunction
task sensor_tmp;
int tmp;
forever begin
std::randomize(tmp) with {tmp >= 80 && tmp <= 100;};
tmp_mb.put(tmp);
#sample_period;
end
endtask
task sensor_spd;
int spd;
forever begin
std::randomize(spd) with {spd>= 50 && spd <= 60;};
spd_mb.put(spd);
#sample_period;
end
endtask
task sensor_fuel;
int fuel;
forever begin
std::randomize(fuel) with {fuel>= 30 && fuel <= 35;};
fuel_mb.put(fuel);
#sample_period;
end
endtask
task drive();
fork
sensor_tmp();
sensor_spd();
sensor_fuel();
display(tmp_mb, "temperature");
display(spd_mb, "speed");
display(fuel_mb, "feul");
join_none
endtask
task display(mailbox mb, string name="mb");
int val;
forever begin
mb.get(val);
$display("car::%s is %0d", name, val);
end
endtask
endclass
module road;
car byd = new();
initial begin
byd.drive();
end
endmodule
输出结果
# car::temperature is 100
# car::speed is 50
# car::feul is 30
# car::temperature is 96
# car::speed is 55
# car::feul is 33
...
下面对于mailbox的描述正确的有:ABCD
A、信箱在使用时需要初始化;
B、信箱的容量可以固定也可以不固定;
C、信箱可通过参数化指定其存储的数据类型;
D、多个对象可同时使用信箱存储或获取数据;
下面对于信箱和队列的说法哪些是错误的:BC
A、信箱和队列的容量都可以不固定;(队列一定不固定)
B、信箱和队列在使用前都应初始化;
C、信箱和队列都可通过赋值传递他们的指针;
D、信箱和队列都可被用作FIFO;
event、semaphore和mailbox总结: