当前位置:网站首页>[quick start of Digital IC Verification] 19. Basic grammar of SystemVerilog learning 6 (thread internal communication... Including practical exercises)

[quick start of Digital IC Verification] 19. Basic grammar of SystemVerilog learning 6 (thread internal communication... Including practical exercises)

2022-07-07 15:26:00 luoganttcc

Reading guide : The author has the honor to be a pioneer in the field of electronic information in China “ University of electronic technology ” During postgraduate study , Touch the cutting edge Numbers IC Verification knowledge , I heard something like Huawei Hisilicon Tsinghua purple light MediaTek technology And other top IC related enterprises in the industry , Pairs of numbers IC Verify some knowledge accumulation and learning experience . Want to get started for help IC Verified friends , After one or two thoughts , This column is specially opened , In order to spend the shortest time , Take the least detours , Most learned IC Verify technical knowledge .

One 、 Description of content

  • event event
    • The control of time sequence between threads
  • Flag language ( Semaphore )
    • Management of shared areas between threads
  • mailbox :mailbox
    • Data transmission between threads uses

 Insert picture description here

notes : The application scenarios of the three mechanisms are different , Can't replace each other .

Two 、 Internal thread communication mechanism :Verilog event

2.1、Verilog event

  • Verilog Used in language event Synchronization thread

  • Trigger Operator of event :->

    • It will not block the execution of the thread
  • wait for Event is triggered :@

    • Edge sensitive , total Thread execution is blocked
    • Only when an event occurs change when , The thread will continue execution
  • When a thread is triggered before it is blocked , May cause competition The phenomenon

    • Trigger is later than wait

2.2、SystemVerilog event

  • Synchronization thread
  • event Is a handle to a synchronization object , The Party group can be used as a parameter to pass to the subroutine
    • There is no need to declare it as a global variable , It can be event Use as a shared resource
  • The operator that triggers an event :-> and ->>
    • ->> Triggering requires The edge of a clock complete
  • The operator waiting for an event to be triggered :@ and wait()
    • @ Is waiting for an edge ;wait Is waiting A state ;
    • @ Waiting for an edge is easy to cause the situation that you can never wait for the trigger ,SV Improvements have been made. , Introduced triggered.
  • triggered Function is used to check whether an event has been triggered , The return value is a status
  • wait(event_variable.triggered)
    • If within the current simulation time range , event It has been triggered , Statements will not be blocked
    • otherwise ,wait(event.triggered) The statement will wait until it is triggered
    • therefore , It is the safest and safest waiting statement

2.3、 event event : The statement blocks at the edge of the event

  • Verilog Used in language event Synchronization thread , The event duration is the time point when the whole event is triggered .
  • The operator that triggers the event :->( It will not block the execution of the thread )
  • Wait for the event to be triggered :@( edge-triggered )

 Insert picture description here

  • initial In terms of software, it is parallel , But the actual operation time is also poor , So the first one initial Will execute first , the second initial After the meeting , But the difference between the two is very short !
  • so ,e1 At the first initial Is triggered first , In the second initial Wait for the trigger , obviously e1 I can't wait ;e2 At the first initial Wait first , In the second initial Is triggered again , therefore e2 Can be triggered .

2.4、 event event: Wait for an event to trigger

  • event Edge sensitivity
  • wait(event_variable.triggered) It is level sensitive
    • There is no sequence with the trigger time
       Insert picture description here

2.5、 The cycle of events

  • When synchronizing events between two threads, you must pay attention to
    • If used in a loop wait(evnet.triggered), It must be ensured that before waiting for the next cycle Update simulation time
    • otherwise , The code will go into 0 Delayed cycle , Wait again and again when a single event triggers

 Insert picture description here

  • Be careful with zero delay cycles , Not recommended wait(event_variable.triggered) This usage , It doesn't block threads ; Suggest or use @ event_variable, It blocks threads .

2.6、event Can be used as a parameter

 Insert picture description here

  • program The variables inside are static by default , added automatic Then it's not static .
  • this.done It is the instantiated object in the class done

2.7、 Internal thread communication mechanism :event

  • Blocking event triggers ( Immediate effect

    • Use -> The operator
    • Triggering an event will not block all processes currently waiting for the event
    • The behavioral edge sensitive signal that triggers the event
  • Non blocking event triggers ( Wait until the non blocking trigger interval , Such as rising edge

    • Use ->> The operator
    • Create a non blocking assignment at the time of the event
    • In the non blocking area of simulation time ( Such as rising edge ) Update Events
  • Waiting for an event (SV & Verilog All have )

    • @event_name
    • @ The operator will prevent the process from executing , Until the event is blocked
    • Before the trigger process executes the trigger behavior , The waiting process must be executed @ sentence , Triggering behavior does not prevent the process from waiting for events
      • If the triggering behavior occurs first , Then the waiting process will continue to wait
  • Persistent trigger :triggered(SV alone possess )

    • In the current simulation time interval , An event has been triggered , that triggered The event attribute is true , Otherwise it is false .
    • wait(event_name.triggered)
  • give an example :

 Insert picture description here

notes : event done_too = done;: It is called the merging of events ( two-way ). operation done It's equivalent to operating done_too, In the same way done_too It is also equivalent to operation done;

  • Event sequence :wait_order()
    • When a specified series of events occur in left to right order ,wait_order The process begins to execute
    • If the specified series of events are executed out of order ,wait_order The process will not execute
event a, b, c;

wait_order(a, b, c); // Must follow a->b->c The order of

  • 1
  • 2
  • 3
  • 4
  • 5
  • Event variables : Merge variables
    • event Is a separate data type , You can assign values
    • When assigning an event to another event , The original event and the destination event share the original event
    • When assigning an event to another event , Two events are merged into one event

 Insert picture description here

  • Event variables
    • When events merge , The assignment operation will only affect the execution of the destination event or the waiting operation
    • When put event Assign a value to event1 when , If a thread is waiting event1, that The current waiting thread will be blocked , Unable to execute .

 Insert picture description here

  • because E2 = E1 Lead to T1 This thread is blocked for a long time and cannot be executed .

  • terms of settlement : stay fork Assign values outside first , Wait again E2 Trigger , Here's the picture :

 Insert picture description here

notes :while There is no time to update , So there's a problem , Running simulation will always block

  • Cancel event
    • When an event is assigned null when , The process synchronized with this time variable is invalid

 Insert picture description here

  • Compare events
    • Different events can be compared
      • be equal to ==
      • It's not equal to !=
      • All equal to ===( And equal to == There is no difference )
      • Not all equal to !==
      • If an event is null, Other event bits 1, The Boolean expression is 0

 Insert picture description here

3、 ... and 、 Internal thread communication mechanism :semaphore

3.1、 Flag language ( Semaphore ) semaphore

  • Semaphore Usually used for Shared resources Allocation and synchronization of
  • Shared resources in different processes are Mutually exclusive Use
  • Create... In memory semaphore when , Similar to creating a basket (bucket), The basket contains a certain number of keys (keys
  • Process before execution You must get a key from the basket
  • When a particular process needs a key , Only A certain amount of All processes are running at the same time
  • Semaphore yes SV Built in class , Provide the following Method function / task
    • establish semaphore:new(keys_numbers)
    • Obtain one or more keys :get()
    • Return one or more keys :put()
    • Non blocking Get one or more keys :try_get()
  • In the verification platform , Often used semaphore Allocate shared resources , such as The system bus , At the same time , Only one driver can use the bus

 Insert picture description here

 Insert picture description here

Be careful : The above functions , If you don't write parameters , It has default parameters !

3.2、 Use new function , establish semaphore buckets

 Insert picture description here

  • Be careful :sem_a = new[4]; Just declare the array , Each semaphore has not specified the number of keys , So it's impossible to put Of !
  • Class creation (new) Entities use parentheses :sem = new(2);; Array creation (new) Entities use square brackets :sem_a = new[4];

3.3、 Semaphore acquisition and release and try_get And get paraphrase

 Insert picture description here

  • try_get Return if you can't get it 0;get Wait until you can't get it

Four 、 Internal thread communication mechanism :mailbox

4.1、 mailbox mailbox

  • Mailbox yes SV Communication mode between different processes , adopt mailbox Information can be passed between different processes
  • Data in a process , adopt mailbox Pass it on to another process ; When mailbox When there is no data in , The thread will wait
  • mailbox Similar to a FIFO, You can set a certain depth queue size
    • When the amount of information in the mailbox reaches the depth of the mailbox , Mailbox is full
    • If the mailbox is full , The process can no longer store information in the mailbox , Until the information in the mailbox is taken , The mailbox is no longer full
  • Email is SV Built in class , Provide the following methods
    • New Mailbox :new()
    • Put the information into the mailbox :put()
    • Try to put the information into the mailbox without blocking :try_put()
    • Get the information from the mailbox :get() or peek()
    • Non blocking retrieval of information from the mailbox :try_get() or try_peek()

 Insert picture description here

4.2、 Use new New Mailbox

 Insert picture description here

4.3、 Verify the mailbox in the platform

  • How to pass information between two threads ?
    • generator (generator) Generate transaction packets , Then pass it to the drive (driver
      • driver Pass packets to DUT,driver Also do some follow sequential Related work !
    • Generators and drives must be Asynchronous operations ( Mailbox is asynchronous FIFO)
      • Synchronous operation also requires handshake , There is no constraint relationship between sending and fetching asynchronous operations
program mailbox_example(bus_if.TB bus, ...);
class Generator
	Transaction tr; // Classes are nested inside ,tr  Is handle 
	mailbox mbx; //*****  Define mailbox 
	function new(mailbox mbx); //  This new yes generator Do initialization new function , When initializing the function outside, you will create a mailbox , That is to say new The mailbox will be created when , then mbx Pass to this.mbx.  Note that the parameters inside are formal parameters , With the above mbx It can be different 
		this.mbx = mbx;
	endfunction
	task run; //  The function is to generate random excitation , Then put the random incentives into the mailbox ,class Driver I will go in the afternoon get This incentive 
		repeat(10) begin
			tr = new;
			assert(tr.randomize); // Randomize all variables 
			mbx.put(tr); //***** tr Put it in the mailbox 
		end
	entask
endclass

 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
class Driver
	Transaction tr;
	mailbox mbx; //*****  Define mailbox 
	function new(mailbox mbx);
		this.mbx = mbx; 
	endfunction
	task run;
		repeat(10) begin
			mbx.get(tr); //*****  Get tr
			@(posedge busif.cb.ack)
			... // Actual physical timing 
		end
	endtask
endclass

 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

These two classes are just defined Generator and Driver, Equivalent to drawing , To use , We also need the code to talk about its instantiation , The code is as follows :

mailbox mbx; //*****  In the same mbx It transmits data 
Generator gen;
Driver drv;
initial begin
	mbx = new; //*****  Create a mailbox with infinite depth 
	gen = new(mbx); //*****  Create two objects , That's instantiation , When creating objects, put mbx Pass it on 
	drv = new(mbx); //*****
end
fork
	gen.run(); //  Generate random excitation 
	drv.run();
join
endprogram

 
  
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • Generator The generated message is usually defined as Transaction

Summary : such as generator Want to driver send data , First of all, it will be in gen Create a mailbox inside , then put Put in the data , And then driver Create a mailbox inside , adopt get To get data . Last but not least env Instantiate inside , Connect the two mailboxes .

4.4、 Verify the communication between asynchronous threads in the platform

  • If synchronization is required between the generator and the drive , You need to add additional handshake information

 Insert picture description here

4.5、 Verify the communication between synchronization threads in the platform

  • If synchronization is required between the generator and the drive , You need to add additional handshake messages

 Insert picture description here

4.6、 Verify threads and internal communication in the platform

 Insert picture description here

  • A lot of components , The communication between components is through mailbox

 Insert picture description here

Environmental Science ENV, First time to understand the use of email

 Insert picture description here

  • Don't pay too much attention to the following , Not actually complete code , It doesn't make much sense , Just know the mailbox section !

 Insert picture description here

 Insert picture description here

x、 Practice

X.1、event practice

x.1.1、 Thread waiting @ And trigger ->demo

thread_communication.sv

module thread_com();
  event e1, e2;
  initial begin
    fork 
      begin
        $display("******@%0d, event1 before trigger", $time);
        ->e1;
        @e2;
        $display("******@%0d, event1 after trigger", $time);
      end
      begin
        $display("******@%0d, event2 before trigger", $time);
        ->e2;
        @e1;
        $display("******@%0d, event2 after trigger", $time);
      end
    join
  end 
endmodule 
******@0, event1 before trigger
******@0, event2 before trigger
******@0, event1 after trigger


 
  
  • The concurrent behavior of concurrent threads is the behavior of simulation tools , At the same time at zero , But in fact, when the instruction is executed , Or execute the above thread first , There is a slight δ Time difference
  • In the above code e1 Because it is triggered first and then wait , So you can't wait !

x.1.2、 Thread waiting wait(event_name.triggered) And trigger ->demo

thread_communication.sv

module thread_com();
  event e1, e2;
  initial begin
    fork 
      begin
        $display("******@%0d, event1 before trigger", $time);
        ->e1;
        //@e2;
        wait(e2.triggered)
        $display("******@%0d, event1 after trigger", $time);
      end
      begin
        $display("******@%0d, event2 before trigger", $time);
        ->e2;
        //@e1;
        wait(e2.triggered)
        $display("******@%0d, event2 after trigger", $time);
      end
    join
  end 
endmodule 

******@0, event1 before trigger
******@0, event2 before trigger
******@0, event2 after trigger
******@0, event1 after trigger


   
    
  • wait(event_name.triggered) wait for , As long as it has been triggered before !

x.1.3、event Pass as a parameter demo

thread_communication.sv

  class trigger_gen;
    event done;
    function new(input event ext_done);
      this.done = ext_done;
    endfunction
    task run();
      #10;
      $display("******@%0d:trigger done by -> done", $time);
      ->done;
    endtask
  endclass
  //Parameter event
  event ext_done;
  trigger_gen trg_gen;
  initial begin
    #5;
    fork
      begin
        trg_gen = new(ext_done);
        trg_gen.run;
      end
      begin
        $display("******@%0d:wait for ext_done trg_gen", $time);
        @(ext_done);
        $display("******@%0d:after ext_done trigger_gen by gen.run", $time);
      end
    join
  end 

******@5:wait for ext_done trg_gen
******@15:trigger done by -> done
******@15:after ext_done trigger_gen by gen.run


     
      
  • meanwhile , Here, also pay attention to two-way assignment ( Merger of events )

X.2、semaphore practice

thread_communication.sv

  //Semaphore 
  semaphore sem[];
 task send(input int sem_num, thread_num);
    sem[sem_num].get(1);
    $display("******@%0d:thread(%0d) get the key", $time, thread_num);
    sem[sem_num].put(1);
 endtask 
 initial begin
  #50;
  sem = new[4];
  foreach(sem[i])
    sem[i] = new(1);
  repeat(3) begin
    fork
      send(0, 0);
      send(0, 1);
    join
    #1;
  end
 end 

rslt.log

******@50:thread(0) get the key
******@50:thread(1) get the key
******@51:thread(0) get the key
******@51:thread(1) get the key
******@52:thread(0) get the key
******@52:thread(1) get the key


       
        
  • If the addition delay , But you can't put it directly send Medium , belong fork...join outside

X.3、mailbox practice

X.3.1、 Basic integer passing ( Mailbox asynchrony )Demo

thread_communication.sv

  mailbox mb;
  int mb_in;
  int mb_out;
  initial begin
    #80;
    mb = new(20);
    fork
      for(int i; i<8; i++) begin
        #1;
        mb_in = 2*i+1;
        $display("******@%0d, mb in(%0d) : %0d", $time, i, mb_in);
        mb.put(mb_in);
      end
      for(int i; i<8; i++) begin
        #2;
        mb.get(mb_out);
        $display("******@%0d, mb out(%0d) : %0d", $time, i, mb_out);
      end
    join
  end 

rslt.log

******@81, mb in(0) : 1
******@82, mb out(0) : 1
******@82, mb in(1) : 3
******@83, mb in(2) : 5
******@84, mb out(1) : 3
******@84, mb in(3) : 7
******@85, mb in(4) : 9
******@86, mb out(2) : 5
******@86, mb in(5) : 11
******@87, mb in(6) : 13
******@88, mb out(3) : 7
******@88, mb in(7) : 15
******@90, mb out(4) : 9
******@92, mb out(5) : 11
******@94, mb out(6) : 13
******@96, mb out(7) : 15

X.3.2、 Basic integer passing ( Mailbox synchronization )Demo

thread_communication.sv

  mailbox mb;
  int mb_in;
  int mb_out;
  event mb_e;
  initial begin
    #80;
    mb = new(20);
    fork
      for(int i; i<8; i++) begin
        #1;
        mb_in = 2*i+1;
        $display("******@%0d, mb in(%0d) : %0d", $time, i, mb_in);
        mb.put(mb_in);
        @mb_e;
      end
      for(int i; i<8; i++) begin
        #2;
        mb.get(mb_out);
        $display("******@%0d, mb out(%0d) : %0d", $time, i, mb_out);
        ->mb_e;
      end
    join
  end 

******@81, mb in(0) : 1
******@82, mb out(0) : 1
******@83, mb in(1) : 3
******@84, mb out(1) : 3
******@85, mb in(2) : 5
******@86, mb out(2) : 5
******@87, mb in(3) : 7
******@88, mb out(3) : 7
******@89, mb in(4) : 9
******@90, mb out(4) : 9
******@91, mb in(5) : 11
******@92, mb out(5) : 11
******@93, mb in(6) : 13
******@94, mb out(6) : 13
******@95, mb in(7) : 15
******@96, mb out(7) : 15

X.3.3、 Pass on class data

thread_communication.sv

  class transaction;
    rand bit [15:0] addr;
    rand bit [31:0] data;
    constraint c1{
               
      addr inside {
               [0:100], [1000:2000]};
      data inside {
               32'h5a5a_5a5a, 32'ha5a5a5, 32'hFFFF_FFFF};
    }
  endclass
  class generator;
    transaction tr;
    mailbox mbx;
    int i=0;
    function new(input mailbox mb);
      this.mbx = mb;
    endfunction
    task run();
      repeat(10) begin
        tr = new();
        tr.randomize();
        $display("******@%0d:generator %0d th (addr=%h, data=%h)", $time, i, tr.addr, tr.data);
        mbx.put(tr);
        i++;
      end
    endtask
  endclass
  class driver;
    transaction tr;
    mailbox mbx;
    int i=0;  
    function new(input mailbox mb);
      this.mbx = mb;
    endfunction
    task run();
      repeat(10) begin
        mbx.get(tr);
        $display("******@%0d:driver %0d th (addr=%h, data=%h)", $time, i, tr.addr, tr.data);
        i++;
      end
    endtask
  endclass
  mailbox mb1 = new(20);
  generator gen = new(mb1);
  driver drv = new(mb1);
  initial begin
    #100;
    fork
      gen.run();
      drv.run();
    join
  end 


rslt.log

******@100:generator 0 th (addr=047f, data=ffffffff)
******@100:generator 1 th (addr=04b9, data=5a5a5a5a)
******@100:generator 2 th (addr=06e0, data=00a5a5a5)
******@100:generator 3 th (addr=0489, data=5a5a5a5a)
******@100:generator 4 th (addr=005e, data=5a5a5a5a)
******@100:generator 5 th (addr=0567, data=5a5a5a5a)
******@100:generator 6 th (addr=05ca, data=ffffffff)
******@100:generator 7 th (addr=040d, data=ffffffff)
******@100:generator 8 th (addr=0490, data=ffffffff)
******@100:generator 9 th (addr=05fe, data=00a5a5a5)
******@100:driver 0 th (addr=047f, data=ffffffff)
******@100:driver 1 th (addr=04b9, data=5a5a5a5a)
******@100:driver 2 th (addr=06e0, data=00a5a5a5)
******@100:driver 3 th (addr=0489, data=5a5a5a5a)
******@100:driver 4 th (addr=005e, data=5a5a5a5a)
******@100:driver 5 th (addr=0567, data=5a5a5a5a)
******@100:driver 6 th (addr=05ca, data=ffffffff)
******@100:driver 7 th (addr=040d, data=ffffffff)
******@100:driver 8 th (addr=0490, data=ffffffff)
******@100:driver 9 th (addr=05fe, data=00a5a5a5)


             
              
  • 32'ha5a5a5 namely 32'h00a5_a5a5
  • class driver Medium tr did not new, Here we will have another in-depth understanding new The effect of ;tr = new; The way of is actually an address randomly generated by the system ( Memory space entity ) Given to tr, Although not used here new But we used mailbox get It means getting the address from the outside ( Memory space entity )!
原网站

版权声明
本文为[luoganttcc]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/188/202207071321524414.html