Sunday, October 30, 2016

UVM Virtual Sequence Example.

// Base Virtual Sequence Class
class base_vseq extends uvm_sequence #(uvm_sequence_item);
   `uvm_object_utils(base_vseq)
 
   // Target Agent Sequencers
   uvm_sequencer #(ahb_txn) SQR_AHB;
   uvm_sequencer #(axi_txn) SQR_AXI;
 
   // Constructor
   function new (string name = "base_vseq");
      super.new(name);
   endfunction: new
 
endclass: vseq_base

// Virtual Sequence Class
class my_vseq extends base_vseq;
   `uvm_object_utils(my_vseq)
 
   // Constructor
   function new (string name = "my_vseq");
      super.new(name);
   endfunction: new
 
   // Sequence Body Task
   task body();
 
      ahb_seqeunce ahb_seq;
      axi_sequence axi_seq;
 
      ahb_seq = ahb_sequence::type_id::create("ahb_seq");
      axi_seq = axi_sequence::type_id::create("axi_seq");
 
      fork
         abh_seq.start(SQR_AHB);
         axi_seq.start(SQR_AXI);
     join
   endtask
 
endclass: my_vseq

// Base Test Class
class base_test extends uvm_test;
   `uvm_component_utils(base_test);
 
   // Environment Class Instantiation
   top_env Env;
 
   // Constructor
   function new (string name = "base_test", uvm_component parent = null);
      super.new(name, parent);
   endfunction: new
 
   // Build Phase
   function void build_phase (uvm_phase phase);
      Env = top_env::type_id::create("Env");
   endfunction: build_phase
 
   // Method to Connect Sequencer Handles in VSEQ
   function void init_vseq (base_vseq vseq);
      vseq.SQR_AHB = test.env.ahb_agent.SQR_AHB;
      vseq.SQR_AXI = test.env.axi_agent.SQR_AXI;
   endfunction: init_vseq
 
endclass: base_test

// Main Test
class test extends base_test;
   `uvm_component_utils(test)
 
   // Constructor
   function new (string name = "test", uvm_component parent = null);
      super.new(name, parent);
   endfunction: new
 
   // Run Phase
   task run_phase (uvm_phase phase);
      // Create the Virtual Sequence
      my_vseq vseq = my_vseq::type_id::create("vseq");
      phase.raise_objection(this);
      // Virtual Sequence Initialization
      init_vseq(vseq);
      // Start the Virtual Sequence
      vseq.start(null);
      phase.drop_objection(this);
   endtask: run_phase
 
endclass: test

UVM AHB Driver Example.

////////// Sequence Item //////////
class ahb_seq_item extends uvm_sequence_item;
   `uvm_object_utils(ahb_seq_item)
 
   // Master to Slave Data Flow
   rand logic [31:0] HADDR;
   rand logic [31:0] HWDATA;
   rand logic HWRITE;
   ahb_burst_e HBURST;
 
   // Slave to Master Data Flow
   logic [31:0] HRDATA;
   ahb_resp_e HRESP;
 
   // Constructor
   function new (string name);
      super.new(name);
   endfunction: new
 
   // Constraints
   constraint addr_for_32bit {HADDR[1:0] == 0;}
 
endclass: ahb_seq_item


////////// AHB Interface //////////
interface ahb_interface;
 
   // TO both Master & Slave
   logic HCLK;
   logic RESETn;
 
   // Master to Slave
   logic [31:0] HADDR;
   logic [31:0] HWDATA;
   logic HWRITE;
   ahb_burst_e HBURST;
 
   // Slave to Master
   logic [31:0] HRDATA;
   ahb_resp_e HRESP;
   logic HREADY;
 
endinterface: ahb_interface


////////// Pipelined UVM Driver //////////
class ahb_pipelined_driver extends uvm_driver #(ahb_seq_item);
   `uvm_component_utils(ahb_pipelined_driver)
 
   // Virtual Interface
   virtual ahb_interface ahb_if;
 
   // Constructor
   function new (string name, uvm_component parent);
      super.new(name, parent);
   endfunction: new
 
   // Semaphore Declaration
   semaphore pipeline_lock = new(1);
 
   // Run Phase Task
   task run_phase (uvm_phase phase);
 
      @(posedge ahb_if.HRESETn);
      @(posedge ahb_if.HCLK);
 
      fork
         do_pipelined_transfer;
         do_pipelined_transfer;
      join
 
   endtask: run_phase
 
   // do_pipelined_transfer task
   task automatic do_pipelined_transfer;
 
    ahb_seq_item req;
 
    forever begin
      pipeline_lock.get();
      seq_item_port.get(req);
      accept_tr(req, $time);
      void'(begin_tr(req, "pipelined_driver");
      ahb_if.HADDR <= req.HADDR;
      ahb_if.HWRITE <= req.HWRITE;
      ahb_if.HBURST <= req.HBURST;
      @(posedge ahb_if.HCLK);
      while(!ahb_if.HREADY == 1) begin
         @(posedge ahb_if.HCLK);
      end
      // Command phase ends here
      // Unlock semaphore
      pipeline_lock.put();
      // Data phase starts here
      if (req.HWRITE == 0) begin
         @(posedge ahb_if.HCLK);
         while(ahb_if.HREADY != 1) begin
            @(posedge ahb_if.HCLK);
         end
        req.HRDATA = ahb_if.HRDATA;
        req.HRESP = ahb_if.HRESP;
      end
      else begin
         ahb_if.HWDATA <= req.HWDATA;
         @(posedge ahb_if.HCLK);
         while(ahb_if.HREADY != 1) begin
            @(posedge ahb_if.HCLK);
         end
         req.HRESP = ahb_if.HRESP;
      end
      // Return the Request as Response
      seq_item_port.put(req);
      end_tr(req);
    end
   endtask: do_pipelined_transfer
 
 endclass: ahb_pipelined_driver


////////// Pipelined Sequence ////////// 
 class ahb_pipelined_seq extends uvm_sequence  #(ahb_seq_item);
   `uvm_object_utils(ahb_pipelined_seq)
 
   logic [31:0] addr[10]; // To save addresses
   int count; // To ensure that the seq does'nt complete too early
 
   // Constructor
   function new (string name);
      super.new(name);
   endfunction: new
 
   // Task body()
   task body;
 
      ahb_seq_item req;
      req = ahb_seq_item::type_id::create("req", this);
      use_response_handler(1); // Enable Response Handler
      count = 0;
 
      for(int i=0; i<10; i++) begin
        start_item(req);
        assert(req.randomize() 
            with {MWRITE == 1; HBURST == SINGLE; HADDR 
            inside {[32'h0010_1000:32'h0010_1FFC]};});
        addr[i] = req.HADDR;
        finish_item(req);
      end
 
      foreach (addr[i]) begin
        start_item(req);
        req.HADDR = addr[i];
        req.HWRITE = 0;
        finish_item(req);
      end
 
      // Wait till last seq item is over
      wait(count == 20);
   endtask: body
 
   // This response_handler function is enabled 
   // to keep the sequence response FIFO empty
   function void response_handler(uvm_sequence_item response);
      count++;
   endfunction: response_handler
 
 endclass: ahb_pipelined_seq

UVM APB Driver Example.

// APB Bus Interface
interface apb_interface;
 
   logic clk;
   logic rstn;
   logic [31:0] Addr;
   logic [31:0] Write_Data;
   logic RNW;
   logic Valid;
   logic Ready;
   logic [31:0] Read_Data;
   logic Error;
 
endinterface: apb_interface

// APB Bus Sequence Item Class
class apb_seq_item extends uvm_sequence_item;
   `uvm_object_utils(apb_seq_item)
 
   // Request fields
   rand logic [31:0] addr;
   rand logic [31:0] write_data;
   rand bit read_not_write;
   rand int delay;
 
   // Response fields
   bit error;
   logic [31:0] read_data;
 
endclass: apb_seq_item

// APB Bus Driver Class
class apb_bus_driver extends uvm_driver #(apb_seq_item);
   `uvm_component_utils(apb_bus_driver)
 
   apb_seq_item req;
 
   virtual apb_interface apb_if;
 
   function new (string name, uvm_component parent);
      super.new(name, parent);
      endfunction: new
 
   task run_phase (uvm_phase phase);
 
      apb_if.Valid <= 0;
      apb_if.RNW <= 0;
 
      // Out of Reset
      @(posedge apb_if.rstn);
      // Pin Level Transfer
      forever begin
         seq_item_port.get_next_item(req);
         repeat(req.delay) begin
            @(posedge apb_if.clk);
         end
         apb_if.Valid <= 1;
         apb_if.Addr <= req.addr;
         apb_if.RNW <= req.read_not_write;
         if (req.read_not_write == 0) begin
            apb_if.Write_Data <= req.write_data;
         end
         while(apb_if.Ready != 1) begin
            @(posedge apb_if.clk);
         end
         // At the end of pin level bus transaction
         // Copy response data into the req related fields
         if (req.read_not_write == 1) begin
            req.read_data <= apb_if.Read_Data;
         end
         req.error <= apb_if.Error;
         apb_if.Valid <= 0;
         seq_item_port.item_done();
      end
   endtask: run_phase
endclass: apb_bus_driver

// APB Bus Sequence
class apb_bus_seq extends uvm_sequence #(apb_seq_item)
   `uvm_object_utils(apb_bus_seq)
 
   function new(string name);
      super.new(name);
   endfunction: new
 
   // No. of iterations
   rand int iti = 30
 
   apb_seq_item req;
 
   task body();
      req = apb_seq_item::type_id::create("req", this);
      repeat(iti) begin
         start_item(req);
         assert (!req.randomize() 
            with {addr inside {[32`h1000_0000:32`h1000_001C]};}) 
         begin
            `uvm_error("Body", "Randomization Error");
         end 
         finish_item(req);
         `uvm_info ("Response Fields", 
            $sformatf("Read Data = %h", req.read_data), UVM_LOW)
      end
   endtask: body
 
endclass: apb_bus_seq

UVM PCM Driver Example.

// Interface Declaration
interface adpcm_int;
 
   logic clk;
   logic frame;
   logic [3:0] data;
 
endinterface: adpcm_int


// Transaction item class adpcm_txn
class adpcm_txn extends uvm_sequence_item;
   `uvm_object_util(adpcm_txn)
 
   rand logic[3:0] data;
   rand int delay;
 
   function new (string name);
      super.new(name);
   endfunction: new
 
   constraint del_bet_pac {delay inside {[1:20]};}
 
endclass: adpcm_txn


// Driver class adpcm_drv
class adpcm_drv extends uvm_driver #(adpcm_txn);
   `uvm_component_utils (adpcm_drv)
 
   adpcm_txn req;
 
   virtual adpcm_int adpcm_if;
 
   function new(string name, uvm_components parent)
      super.new(name, parent);
   endfunction: new
 
   task run_phase (uvm_phase phase);
 
      // Default conditions
      adpcm_if.frame <= 0;
      adpcm_if.data <= 0;
      forever begin
         seq_item_port.get_next_item(req);
         repeat(req.delay) begin
            @(posedge adpcm_if.clk);
         end
 
         adpcm_if.frame <= 1;
         for (int i = 0; i < 8; i++) begin
            @(posedge adpcm_if.clk);
            adpcm_if.data <= req.data[3:0];
            req.data = req.data >> 4;
         end
         adpcm_if.frame <= 0;
         seq_item_port.item_done();
      end
   endtask: run_phase
 
endclass: adpcm_drv

                                                                                                                                                                   
// Sequence Class adpcm_seq
class adpcm_seq extends uvm_sequence #(adpcm_txn);
   `uvm_component_utils (adpcm_seq)
 
   adpcm_txn req;
 
   rand int no_of_reps = 10;
 
   function new (string name);
      super.new(name);
   endfunction: new
 
   task body;
      req = adpcm_txn::type_id::create("req", this);
      for (int i = 0; i < no_of_reps; i++) begin
         start_item(req);
         if (!req.randomize()) begin
            `uvm_error("BODY", "req randomization failed")
         end
         finish_item(req);
         `uvm_info("ADPCM_SEQ BODY", 
            $sformatf("Transmitted frame %0d", i), UVM_LOW)
      end
   endtask: body
 
endclass: adpcm_seq

Saturday, October 29, 2016

AMBA 101.

Advanced Microcontroller Bus Architecture by ARM
  • ASB - advanced system bus
  • APB - advanced peripheral bus (used for low power peripheral modules)
  • AHB - advanced high performance bus
    • Arbitration
      • round robin priority scheme (default priority: master0)
      • fair chance
      • random access
      • high priority
    • burst transfer 
      • undefined length or 4, 8, 16 in length
      • fixed address, incremental address, or wrap address for cache line
    • split transactions
    • multiple masters
      • through a central arbiter
      • through  multiple slave level arbiters in multi-layer AHB-Lite system.
      • masters could be DMA controller, a peripheral core master interface, etc.
    • wider address bus, separate read or write data bus
    • larger data paths (multiplexed, not tri-state)
    • pipelined operation
    • wait-state insertion
    • data-valid signal HREADY
  • AHB and APB is linked through a bridge that acts as the master to the peripheral bus slave devices.

AHB Signals
  • General
    • HCLK
    • HRESET
  • Arbiter / Master
    • HBUSREQ (one from each master) 
    • HGRANT (one for each master)
  • Master / Slave
    • HRESP ([1:0] or [0] in AHB-Lite)
      • 00 - OKAY
      • 01 - ERROR
      • 10 - RETRY
      • 11 - SPLIT
    • HREADY (used by slave to insert wait state)
      • when it's low (from slave) all control signals shall hold their values.
    • HTRANS[1:0] (set according to RD or WR)
      • 00 - IDLE
      • 01 - BUSY (master can use BUSY to insert wait state during bursting)
      • 10 - NONSEQ
        • First transfer of an AHB sequence is always NONSEQ
      • 11 - SEQ
        • All transfer following the first shall be SEQ in nature.
    • HWRITE
    • HADDR [31:0] (muxed from individual masters to slaves and address decoder)
    • HRDATA / HWDATA
  • Slave / Arbiter
    • HMASTER [3:0] / HMASTLOCK - from arbiter
    • HSPLIT# [15:0] - one from each slave
  • Address Decoder / Slave
    • HSEL# (one for each slave unit)
  • More from Masters to Slave
    • HSIZE[2:0]
    • HBURST[2:0]
      • 4, 8, 16 in size
      • incremental
      • wrapping
      • 000 - single
      • 001 - INCR
      • 010 - WRAP4
      • 011 - INCR4
      • 100 - WRAP8
      • 101 - INCR8
      • 110 - WRAP16
      • 111 - INCR16
    • HPROT[3:0] - protection control
    • HLOCK# - locked transfer, one from each master
      • lowered during the last transfer of an AHB sequence
  • Example Auxilary Signals for AHB Masters
    • REQ_VLD - input to bus master, asserted when HBUSREQ is asserted
    • WR - input to bus master, high when HWRITE is high
    • RD -  input to bus master, high when HWRITE is low
    • IN_ADDR[31:0] - input to bus master, used to decide HADDR[31:0]
    • IN_DATA[31:0]
    • LEN#
    • LAST
    • OUT_DATA[31:0] - output from bus master
    • REQ_WR_DATA - output from bus master
      • It shall be high when (REQ_ADDR & WR) to ensure data shall be put on data bus one cycle after address is on.
      • When REQ_WR_DATA ==1, IN_DATA will be put to data bus in next cycle.
    • REQ_RD_DATA - output from bus master
      • It shall be high when it's a read transfer and HREADY == 1.
    • REQ_ADDR - output from bus master
      • It shall be high when (HREADY & HGRANT) to ensure master can put address in next cycle.
      • When REQ_ADDR == 1, IN_ADDR will be put to address bus in next cycle.

AHB Phases
  • address/control 
  • phase data phase

AHB Protocols
  1. bus master sends HBUSREQ to arbiter and an address, decoded by central decoder to provide a dedicated HSEL to selected slave unit.
  2. Slaves should only sample address when both HSEL and HREADY are high.
  3. If the slave is under re-configuration or needs to terminate a transaction, it saves the Hmaster number, and sends RETRY or SPLIT to arbiter and arbiter remove the requesting master from list until the targeted slave de-assert Hsplit. If RETRY is sent, the transaction is dropped.
  4. When the slave is ready, it signals the arbiter with the saved master number and the arbiter grants the bus access to the master to restart the transaction.
  5. No master can have more than one pending split transaction.
  6. Otherwise, master gets Hgrant and lock the bus. 
  7. During burst mode (size indicated by HBURST[2:0]), master can insert wait state by sending HTRANS == BUSY. The slave has to send immediate OKAY in next cycle as for the case of IDLE and ignore the transfer.
  8.  First burst cycle is always NONSEQUENTIAL.
  9. HADDR must be aligned to the HBURST size, that is, A[1:0] == 2'b00 in word transfer and A[0] == 1'b0 in halfword transfer.
  10. If a master is granted the bus without requesting it, it has to drive the transfer type IDLE.

APB Signals
  • PCLK
  • PRESETn - active LOW
  • PSEL# - APB select
  • PEN or PENABLE - APB strobe
  • PADDR[31:0] - Address bus
  • PWRITE - Transfer direction
  • PWDATA[31:0] - Write data bus (can be 8, 16, 32 bit)
  • PRDATA[31:0]  - Read data bus
  • PREADY

APB States
  • IDLE       : (~PSEL & ~PEN)
  • SETUP    : (  PSEL & ~PEN)
  • ACCESS : (  PSEL &   PEN)

AHB/APB Bridge Approach
  • Cross clock domain data transfer
    • handshake signaling method (double stage synchronizer)
    • asynchronous FIFO

ASB Signals
  • AGNT# - Bus grant
  • AREQ# - Bus request
  • BA[31:0] - Address bus
  • BCLK - Bus clock
  • BD[31:0] - Data bus
  • BERROR - Error response
  • BLAST - Last response
  • BLOK - Locked transfer
  • BnRES - Reset
  • BPROT[1:0] - Protection control
  • BSIZE[1:0] - Transfer size
  • BTRAN[1:0] - Transfer bype (driven when AGNT# is asserted)
    • ADDRESS-ONLY
    • NON-SEQUENTIAL
    • SEQUENTIAL
  • BWAIT - Wait response
  • BWRITE - Transfer direction
  • DSEL# - Slave select


UVM Coverage.

Basics
  • Put all covergroups in a class or module
  • Use local variables in the class or module
  • Make covergroups sensitive to a variable or explicitly sample a variable
  • Have a coverage test plan
  • Use automatic bins for simple covergroup
  • Code coverage is not functional coverage
    • line coverage
    • block / statement coverage
    • branch coverage
    • path coverage
    • toggle coverage
    • expression coverage
    • FSM coverage
    • transition coverage

<coverpoint_name>  :  coverpoint  <expression>  { bins <bin_name> = { <list of values > } }
class coverage extends uvm_agent;
   `uvm_component_utils (coverage)

   tlm_analysis_fifo #(mem_req) req_fifo;
   mem_req req;
   mem_op op;
   logic [15:0] addr;

   covergroup mem_ops;
      coverpoint memop {
         bins action[] = {read, write};
      }

   coverpoint addr {
         bins zeros  = {0};
         bins others = {[1 : 16'hFFFE]};
         bins ones   = {16'hFFFF};
      }
      edges : cross op, addr;
   endgroup

   covergroup alu_cv;

      all_ops : coverpoint op;
      a_op: coverpoint A {bins Ais0 = {'h00};}
      b_op: coverpoint B {bins Bis0 = {'h00};}

   endgroup

   function new (string name, uvm_component parent);
      super.new(name, parent);
      mem_ops = new();
      alu_cv = new();
   endfunction : new

   task run();
      mem_data cln;
      mem_req req_tx;
      forever begin : run_loop
         req_fifo.get (req_tx);
         op = req_tx.op;
         addr = req_tx.addr;
         mem_ops.sample();
      end
   endtask : run

endclass


Example: automatic bins
   typedef enum {add, and, xor, mul, rst, nop} op_t;

   covergroup opcov;
      coverpoint op;
   endgroup : opcov

   task sample_req;
      A = req.A;
      B = req.B;
      op = req.op;
      opcov.sample();
   endtask

   covergroup alu_cv2;

      coverpoint op;
      coverpoint A;
      coverpoint B;

      option.auto_bin_max = 4; // default to 64 bins

   endgroup


Example: Basic Bins
   covergroup opcov;
      coverpoint op;
      A00FF : coverpoint A {
         bins zeros = { 0 };
         bins ones  = { 8'hFF };
      }
      B00FF : coverpoint B {
         bins zeros_ones  = { 0, 8'hFF };
      }
   endgroup


Bins with Ranges, Bins with sequences
   typedef enum {add, and, xor, mul, rst, nop} op_t;

   covergroup opcov;
      coverpoint op {
         bins single_cyc = {[add : xor] , rst, nop};
         bins multi_cyc  = {mul};
      }
   endgroup : opcov

   // Automatic bins with ranges
   bins onepervalue [] = {< list of="" values="" >};
   bins n_bins [n] = { < list of="" values="" >};
   bins threebins [3] = {[1:2],[2:6]};
   // same as 
   // bins threebins [3] = {1,2,2,3,4,5,6};

   // Bins with sequences
   // run multi-cycle after reset
   // bins bin_name = ( < value1 > => < value2 >);

   covergroup opcov;
      coverpoint op {
         bins single_cycle = {[add : xor], rst, nop};
         bins multi_cycle  = {mul};
         bins mult_rst = (mul => rst);
         bins rst_mult = (rst => mul);
      }
   endgroup


Bins with multiple value transitions in sequences
 
   // Bins with multiple value transitions
   // bins bin_name = (< value list >  =>  < value list >);
   // 1, 2 => [3:5], 7
   // 1=>3, 2=>3, 1=>4, 2=>4, 1=>5, 2=>5, 1=>7, 2=>7
   bins op_rst[]   = ( [add : nop ] => rst );
   bins rst_mult[] = ( rst => [add : nop]);

   // multi-cycle after single-cycle
   bins singl_mul[]   = ( [add : xor ], nop => mul );

   // single-cycle after multi-cycle
   bins mul_sngl[]   = ( mul => [add : xor ], nop );

   // Run all operations twice in a row 
   // bins <name> = (<value list> [* n]); bins <name> = (<value list> [* n:m]);
   // Ex: Run 3-5 Multiplies in a row
   bins twoops[] = ([add:nop] [*2]);
   bins manymult = (mul [* 3:5]);

   // Nonconsecutive Repetition
   // <value list> [= n:m];     // nonconsecutive operator 
   // <value list> [-> n:m]; // goto operator
   // • Nonconsecutive Operator (=) matches if n:m values occur 
   //        in a list of value regardless of the terminating value.
   // • Goto Operator (->) matches if n:m values occur in a list 
   //        of values and then a terminating value appears.

   bins rstmulrst   = (rst => mul [=  2] => rst);
   // rstmulrst (match): rst => mul => xor => mul => and => rst
   bins rstmulrstim = (rst => mul [-> 2] => rst);
   // rstmulrst and rstmulrstim (match): rst => mul => xor => and => mul => rst


Cross Coverage : Use Cross and Binsof to capture combinations of values
 
   covergroup zeros_ones_ops;

      all_ops : coverpoint op {
         ignore_bins null_ops = {rst, nop};
      }
      a_op: coverpoint A {
         bins zeros  = {'h00};
         bins others = {['h01:'hFE]};
         bins ones   = {'hFF};
      }
      b_op: coverpoint B {
         bins zeros  = {'h00};
         bins others = {['h01:'hFE]};
         bins ones   = {'hFF};
      }

      basic : cross a_op, b_op, all_ops;

      with_a0bin : cross a_op, b_op, all_ops {
         bins a0bin  = binsof (a_op.zeros);
      }

      // bins <bin> = binsof(<somebins>) && binsof(<otherbins>); 
      // bins <bin> = binsof(<somebins>) || binsof(<otherbins>);
      zeros_ones : cross a_op, b_op, all_ops {
         bins AorBzero  = binsof (a_op.zeros) || binsof (b_op.zeros);
         bins AorBones  = binsof (a_op.ones)  || binsof (b_op.ones);
         ignore_bins the_others =
                binsof (a_op.others) && binsof (b_op.others);
      }

      // intersect qualifier
      // bins <bin> = binsof(<somebins> intersect (<value_list>))
      zeros_ones_2 : cross a_op, b_op, all_ops {
         bins add_bin  = binsof (all_ops) intersect {add};
         ignore_bins x = ! binsof (all_ops) intersect {add};
      }

   endgroup



Friday, October 28, 2016

SystemVerilog Question.


  • Automatic vs Static
    For a static task, multiple invocations of the same task will reference the same local variables. For an automatic task, the local variables will be unique to each invocation of the task. The function should be automatic so that there will be multiple memory allocations for the index variable to show increasing values instead of a single value.
  • packed vs unpacked
    Unpacked array is an array with gap between variables.
  • Join / Join_any / Join_none
    Join (and all done) / Join_any (or any done) / Join_none (none is done, non-blocking)
    (use "wait fork;" or implement watch_dog timer for "join_none" for synchronization)
  • Wire vs Logic
    Logic and wire are almost the same except wire can be driven by multiple sources. Logic can only driven by single source.
  • Virtual
    Means "abstract"