Systemverilog(绿皮书)第七章——线程及其通信(三)线程通信

event e1,e2;
    initial begin
        $display("@%0t: 1:before trigger", $time);
        -> e1;
        @e2;
        $display("@%0t: 1:after trigger", $time);
    end
    initial begin
        $display("@%0t: 2:before trigger", $time);
        ->e2;
        @e1;
        $display("@%0t: 2:after trigger", $time);
    end

event创建了两个对象,不需要new。触发e1时等待e2,触发e2时等待e1。打印结果为:

@0:1:before trigger
@0:2:before trigger
@0:1:after trigger

 e1和e2在同一时刻被触发,但是由于delta  cycle的时间 差是的两个 初始化块可能无法等到e1或者e2。

更加安全的方式使用event的方法triggered()。

event e1,e2;
    initial begin
        $display("@%0t: 1:before trigger", $time);
        -> e1;
        wait (e2.tregger);
        $display("@%0t: 1:after trigger", $time);
    end
    initial begin
        $display("@%0t: 2:before trigger", $time);
        ->e2;
        wait (e1.tregger);
        $display("@%0t: 2:after trigger", $time);
    end

此时的打印结果为:

@0:1:before trigger
@0:2:before trigger
@0:1:after trigger
@0:2:after trigger

使用wait(e1.trigger())电平敏感来代替 边沿敏感的 阻塞语句@e1。留言功能

module road;
initial begin
    sutomatic begin
    byd.drive();
end
endmodule

class car;
    bit   static = 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 is  launched

car is moving,并行 线程变为顺序触发信号。

尽管在ar::drive中同时启动了两个线程,通过共享信号car::start来判断什么时候可以行使。

如果将上述公共变量修改为event,那么通过事件的触发和判断也可以实现一样的需求。

class car;
    event  e_start;
    task  launch();
        -> e_start;
        $display("car is launched");
    endtask
    task move();
        wait(e_start.triggered());
        $display("car is moving");
    endtask
task drive();
    fork
        this.launch();
        this.move();
    join
endtask
endclass

如果汽车需要 加速的话,速度仪表盘的信息是如何显示的呢?

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);
        end
    endtask
    task drive();
        fork
            this.launch();
            this.move();
            this.display();
        join_none
    endtask
endclass


module road;
initial begin
    automatic car byd = new();
    byd.drive();
    byd.speeduo();
    byd.speeduo();
    byd.speeduo();
end
endmodule

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

拿了钥匙的人要还钥匙,对于线程之间 共享资源的使用方式,应该遵循互斥访问(multex access)原则。

控制线程的原因在于,如果不对其进行访问 控制,可能会 出现多个线程对同一资源的 访问,进而导致不可 预测的数据损坏和线程的异常,这种现象称之为“线程不安全”。

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 zhe car", p);
    endtask
    task get_off(string p);
        $display("%s is got off the car", p);
        key.put();
        #1ns;
        $display("%s return the key", p);
    endtask
endlass

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
    jion
end
endmodule

一开始给车子配置一把钥匙new(1)。虽然 丈夫和妻子在同一时间想要开这辆车子,然而只允许以为家庭成员 来驾驶。直到丈夫归还了钥匙,妻子才可以上车。

对于semaphore key使用,key在使用 前必须做初始化,即要告诉用户它原生自带几把钥匙。semaphore初始化可以初始化0,即new()无参数,可以不停的换钥匙。

semaphore::get()/put()函数中没有传递参数,即 默认他们在等待和归还钥匙的数量为1。semaphore可以被初始化为多个钥匙,也可以支持每归还多把钥匙来控制资源访问

class carkeep;
    int key = 1;
    string q[$];            //队列
    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 car",p);
        keep.put_key(p);
        1ns;
        $display("%s return the key", p);
    endtask
endclass  

program automatic bounded;
    mailbox mbx;
    initial begin
        mbx = new(1);    //容量为1
        fork
            //线程1
            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 put(%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)

 信箱的容量只有1,当传递进去参数 之后,需要把信箱 中内容取出,再放入新的数据。

信箱设置为1的好处是,对于动态内存不占用资源。

数据通信需求

使用mailbox通信,满足各个线程之间通信实现具体的功能

class car;
    mailbox tmp_mb, spd_mb, 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 && fule <=35;);
            fuel_mb.put(fuel);
            #sample_period;
        end
    endtask
    task drive();
        fork
            sensor_tmp();
            sensor_spd();
            semsor_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

对于mailbox的用法,与FIFO使用相似。 如果我们将上述的mailbox来代替用队列 

的话,则可以 修改为下面的例码。

class car;
    int tmp_q[$],spd_q[$],fuel_q[$];
    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_q.push_back(tmp);
            #sample_period;
        end
    endtask
    task sensor_spd;
        int spd;
        forever begin
            std::randomize(spd) with (spd >=50 && spd <= 60;);
            spd_q.push_back(spd);
            #sample_period;
        end
    endtask

    task drive();
        fork
            sensor_tmp();
            sensor_spd();
            semsor_fuel();
            display(tmp_mb,"temperature");
            display(spd_mb,"speed");
            display(fuel_mb,"feul");
        join_none
    endtask
    task display(string name, ref int q[$]);    //
        int val;
        forever begin
            wait (q.size()>0);                //只有数据不为空时
            val = q.pop_front();
            $display("car::%s is % 0d", name ,val);
        end
    endtask
endclass


module road;
    car byd = new();
    initial begin
        byd.drive();
    end
endmodule

 进程间的同步需求(event同步)

class car;
    event e_stall;
    event e_park;
    task stall;
        $dsiplay("car::stall started");
        #1ns;
        ->e_stall;
        @e_park;
        $display("car::stall finished");
    endtask

    task park;
        @e_stall;
        $dsiplay("car:; park started");
        #1ns;
        ->e_park;
        $display("ccar::park finished");
    endtask
    task drive();
        fork
            this.stall();
            this.park();
        join_none
    endtask
endclass

 


版权声明:本文为Jay_who原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。