UVMF Questions - muneeb-mbytes/UVMF GitHub Wiki

1. Generate Block

  • This block is used for parallel execution.
  • Uses when the same operation or module instance is repeated multiple times.
    Syntax
generate  
genvar gv;    
endgenerate     
  • Inside block, can have module instance, user-defined primitives, continuous assignment, initial and always blocks.

genvar Genvar is the loop variable that can be declared inside or outside the generate block.

  • A genvar is a variable used in generate-for loop. It stores positive integer values. It differs from other Verilog variables in that it can be assigned values and changed during compilation and elaboration time.

In this type, we just generate the required module for the required number of times.
The below example will show how to do an instantiation of the module for the required number in another module.

Code Snap

module mod_1;
bit [3:0]a;
bit [3:0]b;
initial begin
$display("mod_1 generate");
a=2;
b=3;
end
endmodule

module mod_2;
bit [6:0]sum;

generate
  genvar gv;  //default keyword
  for (gv=0;gv<=1;gv++)
  begin
  mod_1 mod_inst(); // instance of mod_1
  end
endgenerate

initial
begin
$display("module_2");
sum = mod_2.genblk1[0].mod_inst.a+mod_2.genblk1[0].mod_inst.b;
$display("Sum generation from mod_1[0] = %0d",sum);
// both mod_1[0] and mod_1[1] executes parallelly
sum =2*(mod_2.genblk1[1].mod_inst.a+mod_2.genblk1[1].mod_inst.b);
$display("Double of sum generation from mod_1[1]= %0d",sum);
end
endmodule  

Output Snap

gen
                            fig 1- Output for generate block

2. Inter and intra Assignment delay

An inter-assignment delay statement has a delay value on the LHS of the assignment operator. this indicates that the statement itself is executed after the delay expires, and is the most commonly used form of delay control.

an intra-assignment dealy is one where there is a delay on the RHS of the assignment operator. this indicates that the statement is evaluated and values of all signals on the RHS are captured first. Then it is assigned to the resultant signal only after the delay.

Syntax pf the inter assignment delay

#<delay> <LHS> = <RHS>  
    

Syntax of the intra-assignment dealy

<delay> #<LHS> = <RHS>  
    

Example:

  module tb;
reg [3:0]a;
reg [3:0]b;
initial
begin
a=1;
b=1;
$monitor("time =%0d ; a= %0d ; b = %0d",$time,a,b);
#5 a=2;
#5 b=4;
fork
 a = #5 a+b;
 #3 a = b;
 join_none

end
endmodule

Here in the above example Inter and intra-assignment delays code

in_it drawio (1)

    Fig. 2 - output for Inter and intra-assignment delays code

The above example is the inter and intra-assignment dealy the 5-time units a and b are assigned using blocking statement. same as the 10-time units.
at 13-time b value is assigned to the a value. in the intra-assignment dealy

at 15-time a and b are evaluated but not yet assigned when the blocking statement which is a value a is executed. So when RHS of a is evaluated a additional of b still has an old value of 2 and 4 will be assigned to a. $monitor does not detect a change to display the statement.


3. Packages are not hierarchical in nature

import1 drawio

  Figure 3: General example for importing packages.

Let us assume the above example, in which the pkg1 are included with 2 files. In similar way pkg2 is imported with pkg1 along with including 2 files and pkg3 is imported with pkg2and includes two more files.

Since pkg1 is imported inside pkg2, all the files which are included in pkg1 are visible and can be reused by the pkg2.
In similar way pkg2 is imported in pkg3, hence all the files included in pkg2can be used by pkg3 files. Since the pkg1 is not imported within the pkg3, the files included within that pkg1 are not available and cannot be reused by pkg3.

Eventhough we have imported pkg1inside pkg2, and if we still try to import only pkg2 and assume that pkg1 can be internally imported by pkg2, that is a wrong assumption. Every time if we need to import any file we need to specify it explicitly. Importing one file just imports the files within it, but not the another import file within the program. By the above example it is very clear that pkg3 can use only file3.txt and file4.txt as they are included files in pkg2and import pkg1 was also existed within the pkg2but it didn't even executed as it as not been triggered.

IN UVMF:

In UVMF, for system level testbench generation there will be generation of 3 main hierarchical packages they are agent_pkg ,sub_env_pkg and env_pkg. All the files included in agent_pkg are available for sub_env_pkg as it internally imports the agent_pkg and in similar way env_pkg imports sub_env_Pkg.

import2

  Figure 4: Only sub_env_pkg is imported, agent_pkg files cannot be used in env_pkg as it has not been imported.

Now if we try to compile and simulate the whole code we will get compile error as the files which are included in agent_pkg are not available to use at environment level because env_pkg as not imported the agent_pkg. Eventhough agent is hierarchically below the environmrnt still it was not able to access the agent_pkg. By this it is very clear that imports are not hierarchically usable, If we want to use any internal blocks files still we have to import that file and has to reuse.

import3

   Figure 5: agent_pkg and sub_env_pkg are imported in env_pkg for sucessfull compilation.
  • NOTE: For Successful compilation of generated system level code using UVMF, we have to import the agent packages in the system level environment yaml file. So that all the files from sub_env blocks and agent blocks are available and can be used by the top_env block.

4. What is serial protocols?

  • Serial communication is a commonly used method with which to exchange data between computers and peripheral devices. Serial transmission between the sender and receiver are subject to strict protocols which provide security and reliability and have led to its longevity and it's examples are SPI,I2C,RS232 etc.
  • Used in shorter distances and Serial communication is the preferred method when dealing with circuits or processors with a limited number of I/O pins.

image

                              Fig.6 Working of Serial Protocols

Basic Modes of serial data transmission

  • Binary pulses are used to transmit data in serial data transmission. The binary digit one is represented by five volts or a HIGH logic. Conversely, binary zero is denoted with a logic LOW or zero volts. In order to implement serial communication, a source and destination are required. They are also referred to as the sender and receiver. Various types of serial communication can be employed and are designated as Simplex, Half Duplex, and Full Duplex

  • The Simplex method implements one-way data transmission. In this scheme, only the source or destination is active at any given time. If the source is sending data, the receiver has no choice but to accept the transmission. Simplex mode is used when broadcasting television or radio signals.

  • Half Duplex mode permits the source and destination to both be active, but not simultaneously. Transmission only occurs in one direction at a time. A case in point can be seen when using the Internet. When you make a request from your computer for a webpage, the server processes the request. It then becomes the sender when returning the information to your computer, which is now the receiver.

  • Full Duplex mode is the world’s most widely used form of serial communication. Source and destination are both active and can send and receive data simultaneously. Your smartphone is a prime example of full duplex mode in action.

What is parallel protocols?

  • More I/O lines are needed to implement parallel communication. When data is sent in a block of 8,16 or 32 bits, each bit needs its own physical I/O line. The speed of parallel transfer is faster than serial transmission but needs a larger number of I/O lines. Parallel data transfer is used in personal computers to exchange data with internal components such as the random access memory (RAM) or the CPU.

image

                            Fig.7 Working of Parallel Protocols

Difference between Serial and Parallel Protocols:

Serial Bus Parallel Bus
Sends one data bit at each clock pulse Transfers a block of data at a simultaneously
Better method for long distance communication Mostly used for short distance communication
Slow transmission speed Faster speed of communication
Requires a single wire for data transmission Requires ‘n’ number of lines for transmitting ‘n’ bits
Low installation cost Higher installation cost
Example: Computer to Computer Example: Computer to multifunction printer

Serial protocol divided into two types:

  • Serial synchronous protocol
  • Serial asynchronous protocol

Serial synchronous protocol - SPI protocol

  • Used for longer distance
  • Communication done with outside chip.
  • Master and slave have different clocks because both are working in different chip.

Limitation

  • serial synchronous protocol cannot be used for more distance.

Serial asynchronous protocol - UART

  • UART communication happens between single line.
  • Full duplex communication
  • UART has start bit and stop bit to indicate when the data transfer done.
  • High speed communication protocol
  • If distance is more, data loss may happen.
  • UART uses 128B/130B encoding for data transmission

Parallel Protocol

  • Parallel Synchronous protocol
  • Parallel Asynchronous protocol

Parallel Synchronous protocol - APB Protocol

  • Half duplex communication
  • Working on system clock

Parallel Asynchronous protocol - AXI protocol

  • Burst based transaction
  • Full duplex Communication

5. Protected variables can be get by other classes:

Protected variables can be assessed by using set() and get() method. In parent class protected (variable) should set.(variable) and we can access that variable in child class using get.(variable).

Example:

   class fruit;  
   rand apple;  
   rand orange;  
   protected a = tomato;  
   set.(a);  
   endclass  

   class dryfruits extend fruit;
   get.(a);
   endclass  

6. Print Table using do_print() Method:

rand_seq

                     Figure 8: call print() function in random sequence class

trans_do_print

                     Figure 9: add printing preference in do_print() function in transaction class

output_table

                     Figure 10: output in table format

7. UVM Deadlock Method:

image

        Figure 11: UVM Deadlock Method diagram 

Generally, a sequence that is in the test, transmits the transactions to the sequencer and then the sequencer sends the transactions to the driver. If an object is created for a sequence then the sequencer initiates the transaction to the driver. But if the object is created again using a new method that creates a memory for another sequencer and driver then that sequencer is pointing to another driver but the driver which created in the previous case is still waiting for that sequence this waiting is called UVM DEADLOCK.

In the above diagram, seqr1 is created when the object is created, but when the object is again created then new seqr2 is created which is pointing to another driver, so the previous driver is still waiting for sequencer 1 which causes UVM DEADLOCK.
To prevent this we have to make sure that object is created only once for the same sequence so that another sequencer and driver are not created thereby waiting does not takes place thereby avoiding UVM DEADLOCK


8. UVM raise objections and drop objections using the set drain method and phase_ready_to_end methods

  • Objections are used to stop the simulation process.

  • Using this simulator know where to start and stop the simulation. When the number of raised objections is equal to a number of dropped objections then the simulation stops.

  • To make the simulator when to go for the extract phase we use objections.

  • Components and Objects can raise and drop objections.

  • It remains in the same phase till all the objections are dropped.

  • These are used in Run_Phase.


Objections used for Components:-

  • phase.raise_objection(this);
  • phase.drop_objection(this);

Objections used for Objects:-

  • starting_phase.raise_objection(this);
  • starting_phase.drop_objection(this);

UVM provides an objection mechanism to allow hierarchical status communication among components which is helpful in deciding the end of test.

There is a built-in objection for each phase, which provides a way for components and objects to synchronize their testing activity and indicate when it is safe to end the phase and, ultimately, the test end.

The component or sequence will raise a phase objection at the beginning of an activity that must be completed before the phase stops, so the objection will be dropped at the end of that activity. Once all of the raised objections are dropped, the phase terminates.

Example:-

  class agent extends uvm_agent;
  task run_phase(uvm_phase phase);
  phase.raise_objection(this);
  #100;
  phase.drop_objection(this);     
  endtask

Moves from run_phase to extract_phase after 100ns after all objects are finished.

Lets consider an example:- A master agent may need to complete all its write and read operations before the run phase should be allowed to stop. In this example, typically objections can either be added in sequence or test.

Objection in sequence:- objection is raised when it started as a root sequence (a sequence which has no parent sequence), and to drop the objection when it is finished as a root sequence.

        class ex_seq extends uvm_sequence#(s_seq_item);
        task pre_body();
        // raise objection if started as a root sequence
        if(starting_phase != null)
        starting_phase.raise_objection(this);
        endtask

        task body();
        `uvm_do(req)
        endtask

        task post_body();
        // drop objection if started as a root sequence
        if(starting_phase != null)
        starting_phase.drop_objection(this);
        endtask
        endclass  

The starting_phase member is only set automatically if the sequence is started as the default sequence for a particular phase. so objections were used in the sequence if it is default sequence for a particular phase.

Objection in test:- If the sequence need to be started explicitly i.e in the test, then the objections were added in the test instead of sequence. Below code shows how to.

     class ex_test extends uvm_test;
     task main_phase(uvm_phase phase);
     phase.raise_objection(); //rasing objection
     ex_seq.start(ex_agent.sequencer);
     phase.drop_objection();  //droping objection
     endtask
     endclass

Note: When the sequence is started explicitly, the starting_phase member is null, so the sequence will not raise or drop the phase objection.

Set drain time
Drain time is the time for which the simulation halts after the last objection is dropped in a phase,It is the actual time at which the phase is ended.

  • We can set drain time in any phase before run phase. usually done in test.
    syntax:

        phase.phase_done.set_drain_time(this,2);  
    

Here phase is the uvm_phase object which has handle called phase_done in which the function set_drain_time exists.
code snippet:

     class ex_test extends uvm_test;
      ......  
     task run_phase(uvm_phase phase);                                                                  
      phase.raise_objection(this);                                                                    
      phase.phase_done.set_drain_time(this,2);                                                        
      `uvm_info("Run_phase",$sformatf("%0t all ok",$time),UVM_LOW)                                    
      #5                                                                                              
      phase.drop_objection(this);                                                                     
      #2 `uvm_info("Run_phase",$sformatf("%0t dropped obj",$time),UVM_LOW)                            
     endtask 
     endclass

Here when the test ex_test is being called each and every phase in it is called implicitly.and in run phase we set drain time as 2 which means the whole phase takes 2 nanoseconds to finish.
output:

image

    figure 12 : raise objections and drop objections 

phase_ready_to_end

  • phase_ready_to_end() is called whenever the total objection count for the current phase decrements to 0.
  • For every type of phase, phase_ready_to_end will be iterated for 19 times. After this iteration, all forks will be killed.
  • If the objection is raised and dropped in phase_ready_to_end(), it will be called again. To avoid endless loops, there is a maximum count of phase_ready_to_end() that defaults to 20.

9 How to add a sequence to UVMF testbench :

Step 1 : create a leaf sequence in the interface (here it is add_fixed_sequence)

  • After we write the yaml file , we need to generate the testcase of given DUT. When all required files are generated under UVM_Framework/UVMF_2022.3/my_adder/output/verification_ip/interface_packages/add_in_pkg/src we can observe some sequences is autogenerated which is show in under fig

seq1 drawio

                                     Fig-13 add a sequence to UVMF testbench
  • Under the src file we can create our own sequences. Here we created add_in_fixed_sequence.sv.In this sequence we gave the fixed value of input.

Code snippet of fixed_sequence

class add_in_fixed_sequence #(
      int add_width = 4
      )
  extends add_in_sequence_base #(
      .add_width(add_width)
      );

  `uvm_object_param_utils( add_in_fixed_sequence #(
                           add_width
                           ))

  // pragma uvmf custom class_item_additional begin
  // pragma uvmf custom class_item_additional end
  
  //*****************************************************************
  function new(string name = "add_in_fixed_sequence");
    super.new(name);
  endfunction: new

  // ****************************************************************************
  // TASK : body()
  // This task is automatically executed when this sequence is started using the 
  // start(sequencerHandle) task.
  //
  task body();
  
      // Construct the transaction
      req=add_in_transaction#(
                .add_width(add_width)
                )::type_id::create("req");
      start_item(req);
      req.a=4;
      req.b=7;
      finish_item(req);
      `uvm_info("SEQ", {"Response:",req.convert2string()},UVM_MEDIUM)

  endtask

endclass: add_in_fixed_sequence

// pragma uvmf custom external begin
// pragma uvmf custom external end

[lab link] (https://github.com/muneeb-mbytes/UVMF/blob/main/UVM_Framework/UVMF_2022.3/adder_yaml/newtest/verification_ip/interface_packages/add_in_pkg/src/add_in_fixed_sequence.sv)

Step 2: include the sequence created in the interface package(in our case add_in_pkg.sv)

  • After adding the sequence file in src we need to include all created file in add_in_pkg.sv ,which is located in UVM_Framework/UVMF_2022.3/adder_yaml/newtest/verification_ip/interface_packages/add_in_pkg/add_in_pkg.sv.
    We add all the created sequence.sv file under the pragma_begin end in add_in_pkg.sv

code snippet of include sequence

 // pragma uvmf custom package_item_additional begin
   `include "src/add_in_fixed_sequence.sv"
   // UVMF_CHANGE_ME : When adding new interface sequences to the src directory

[lab link] (https://github.com/muneeb-mbytes/UVMF/blob/main/UVM_Framework/UVMF_2022.3/adder_yaml/newtest/verification_ip/interface_packages/add_in_pkg/add_in_pkg.sv)

Step 3: create a handle for the leaf sequence in sequence base (in our case it is add_ben_bench_sequence_base.sv)
we need to add our created sequence handle in that file (add_ben_bench_sequence_base.svh)

CHALLANGE-Page-3 drawio

                  fig-14  create a handle for the leaf sequence in sequence base  

[lab link] (https://github.com/muneeb-mbytes/UVMF/blob/main/UVM_Framework/UVMF_2022.3/adder_yaml/newtest/project_benches/add_ben/tb/sequences/src/add_ben_bench_sequence_base.svh)

Step 4: create a virtual sequence to start leaf sequence(here add_fixed_sequence)

So in order to access the above class of sequence, we need to create an object for it and start the sequence. So we move to project_bench (UVM_Framework/UVMF_2022.3/adder_yaml/newtest/project_benches/add_ben/tb/sequences/src/). Here we create another file add_fixed_sequence.sv in which we create handle for sequence which we created in interface package(i.e handle for add_in_fixed_sequence.sv) and we start the sequence.

code snippet

`ifndef __ADD_FIXED_SEQUENCE
`define __ADD_FIXED_SEQUENCE

`include "uvm_macros.svh"

class add_fixed_sequence #(int add_width = 4) extends add_ben_bench_sequence_base;

  `uvm_object_utils(add_fixed_sequence)

  function new(string name = "add_fixed_sequence");
    super.new(name);
  endfunction : new

  virtual task body();
  add_in_agent_fixed_seq = add_in_fixed_sequence#()::type_id::create("add_in_agent_fixed_seq");

  add_in_agent_config.wait_for_reset();
  add_in_agent_config.wait_for_num_clocks(1);
  repeat (5)
begin
  add_in_agent_fixed_seq.start(add_in_agent_sequencer);
end
 // add_in_agent_config.wait_for_num_clocks(50);

endtask

endclass : add_fixed_sequence

`endif

[lab link] (https://github.com/muneeb-mbytes/UVMF/blob/main/UVM_Framework/UVMF_2022.3/adder_yaml/newtest/project_benches/add_ben/tb/sequences/src/add_fixed_sequence.sv)

Step 5: include the virtual sequence in sequence_pkg(here it is add_ben_sequences_pkg.sv)

Now we include the above file in the package file of add_ben_sequences_pkg.sv (UVM_Framework/UVMF_2022.3/adder_yaml/newtest/project_benches/add_ben/tb/sequences)


  // pragma uvmf custom package_item_additional begin
  `include "src/add_fixed_sequence.sv"
  
  // UVMF_CHANGE_ME : When adding new sequences to the src directory  

[lab link] (https://github.com/muneeb-mbytes/UVMF/blob/main/UVM_Framework/UVMF_2022.3/adder_yaml/newtest/project_benches/add_ben/tb/sequences/add_ben_sequences_pkg.sv)

Step 6: create a test to start that sequence (here it is add_fixed_test.sv)

Now we direct to path - UVM_Framework/UVMF_2022.3/adder_yaml/newtest/project_benches/add_ben/tb/tests/src and we create files for every sequence for both which are auto-generated (add_in_random_sequence.sv) and our own created sequence (add_in_fixed_sequence.sv) which is shown in the below fig.

CHALLANGE-Page-2 drawio

                                     fig-15  which are auto-generated and  own created sequence file list   

code snippet:

`ifndef __ADD_FIXED_TEST
`define __ADD_FIXED_TEST

`include "uvm_macros.svh"

class add_fixed_test extends test_top;

  `uvm_component_utils(add_fixed_test)

  function new(string name = "add_fixed_test", uvm_component parent = null );
    super.new(name, parent);
  endfunction : new


  virtual function void build_phase(uvm_phase phase );
    add_ben_bench_sequence_base::type_id::set_type_override(add_fixed_sequence #(4)::get_type());
    super.build_phase(phase);
  endfunction : build_phase 

endclass : add_fixed_test

`endif

Here we are overriding it with our created sequence handle so that it will not run the auto-generated sequences which are present in add_ben_bench_sequence_base.svh (located at UVM_Framework/UVMF_2022.3/adder_yaml/newtest/project_benches/add_ben/tb/sequences/src/add_ben_bench_sequence_base.svh)

[lab link] (https://github.com/muneeb-mbytes/UVMF/blob/main/UVM_Framework/UVMF_2022.3/adder_yaml/newtest/project_benches/add_ben/tb/tests/src/add_fixed_test.sv)

Step 7: include that test in the tests_package(here it is add_ben_tests_pkg.sv)

Now include your sequence file in the test_package (add_ben_tests_pkg.sv which is located at UVM_Framework/UVMF_2022.3/adder_yaml/newtest/project_benches/add_ben/tb/tests/add_ben_tests_pkg.sv)

code snippet:

 // pragma uvmf custom package_item_additional begin
   `include "src/add_fixed_test.sv"
  // UVMF_CHANGE_ME : When adding new tests to the src directory

[lab link] (https://github.com/muneeb-mbytes/UVMF/blob/main/UVM_Framework/UVMF_2022.3/adder_yaml/newtest/project_benches/add_ben/tb/tests/add_ben_tests_pkg.sv)


10. `ifndef and endif how it will work:

ifndefis a macro which compiles during the pre-compilation phase, this is similar to (if!)if NOT condition if a particular macro is not defined then if NOT condition becomes true and executes the statements below it. The meaning of ifndef is that if a particular macro is defined earlier consider that for compiling and it also tells the simulator not to compile the same macro again, in case if it is not defined then the codes below this macro are defined and compiled.

  • Example1: An example which uses ifndef macro to compile the set of statements (Different macros name given to different files).

    
    `include "class1.sv"       
    `include "class2.sv"  
    
    module display;
    extended_class_1 ex1_h;
    extended_class_2 ex2_h;
    initial begin
    ex1_h=new();
    ex2_h=new();
    ex1_h.display();
    ex2_h.display();
    end
    endmodule  
    
    

First file having macro name as MY_CLASS.

  `ifndef MY_CLASS
  `define MY_CLASS

     class my_class;

      function void display();
         $display("Display from class");
      endfunction

    endclass

    class extended_class_1 extends my_class;

    function void display();
     $display("------------------------------");
     $display("Display from extended_class_1");
    endfunction
    endclass
 `endif  

Another file having macros with different name and here in our file it is MY_CLASS2.

    `ifndef MY_CLASS_2
    `define MY_CLASS_2

    `include "class1.sv"

    class extended_class_2 extends my_class;

        function void display();
           $display("-------------------------------");
            $display("Display from extended_class_2");
           $display("-------------------------------");
       endfunction
    endclass
      `endif

OUTPUT:

ifndef

     Figure 16: Simulates without any error, as the macros name were diffrent.  

Example2: Macros with same name does'nt compile both the files.

include "class1.sv" include "class2.sv"

module display; extended_class_1 ex1_h; extended_class_2 ex2_h; initial begin ex1_h=new(); ex2_h=new(); ex1_h.display(); ex2_h.display(); end endmodule

First file having macro name as MY_CLASS.

  `ifndef MY_CLASS
  `define MY_CLASS

     class my_class;

      function void display();
         $display("Display from class");
      endfunction

    endclass

    class extended_class_1 extends my_class;

    function void display();
     $display("------------------------------");
     $display("Display from extended_class_1");
    endfunction
    endclass
 `endif  

Another file having macros with the same name and since this has already been compiled

    `ifndef MY_CLASS
    `define MY_CLASS

    `include "class1.sv"

    class extended_class_2 extends my_class;

        function void display();
           $display("-------------------------------");
            $display("Display from extended_class_2");
           $display("-------------------------------");
       endfunction
     endclass
     `endif  

OUTPUT: Error during compilation as two files have the same macro names.

ifndef1

   figure 17 of the Error during compilation as two files have the same macro names.

11 Leaf Sequence

  • It is the main VIP sequence provided by the vendors.
  • we use sequence layering in order to start the leaf sequence from our test case.
  • Sequence layering means using virtual sequences pointing to null in order to start the leaf sequence without disturbing the vendors VIP.

Untitled Diagram-Page-1 drawio (1)

                      Fig 18: Leaf sequence  
  • Here leaf sequence is the vendor sequence and parent, grandparent sequence is our own sequences which are virtual sequences.
  • The leaf sequence is instantiating inside the our sequences.
  • Inorder to start the leaf sequence first we start the grandparent sequence using start method by passing null as the sequencer. Then body task of grandparent contains parent sequence start method with the sequencer has null and will execute the body task of grandparent which will point to the parent sequence body task.
  • Then parent body task contains the leaf sequence start method which has real sequencer and starts the sequence of leaf sequence.

12. Differences between macros and parameters

Macros Parameters
macros are replaceable parameters are like variables
macros works in pre-compilation state parameters works in Elaboration state
syntax : `define macro_name value syntax : parameter parameter_name=value
We can use `define in any file we can use parameter within the file
we can't give datatypes in macros we can use and change datatype in parameters
Macros can have multiple lines multiple lines cannot be possible here because parameter is just like declaring variables
We can give value for a macro in command line parameters value can't be changed in command line

Execution stage of macros and parameters

Untitled Diagram drawio (44)

   figure 19: Execution stage of macros and parameters

Parameters used in macros

parameter data = 5; // data will be replaced by value 5 in Elaboration
`define  DATA data // in pre-compilation `DATA will be replaced with data
module tb();
  int a,b;
  initial begin
   
    $display("DATA=%0d",`DATA);
    
     
     b= `DATA + 2;  

    $display(" b=%0d",b);
  end 
endmodule

In the above code the parameter value of data =5 is used in the macro `DATA

Macros used in parameters

`define  DATA 25
parameter data = `DATA;

module tb();
  int a,b;
  initial begin
    $display("data=%0d",data); // in pre-compilation `DATA will be replaced with 25.
    $display("DATA=%0d",`DATA); // data will be replaced by value 25 in Elaboration 
    
     a = data +5;
     b= `DATA + 2;
    
    $display("a=%0d b=%0d",a,b);
  end 
endmodule  

In the above code the macro value of `DATA 25 is used in the parameter data.

Macros(`define) can be used in command line

-timescale 1ns/1ns +define+name=20

`define name 10

module hi;
int a;
initial begin
  $display("[%0t] a = %0d",$time,a);
  #1 a = `name;
  $display("[%0t] a = %0d",$time,a);
end
endmodule

We can give inputs to macros in command line interface using -timescale 1ns/1ns +define+name=20 in compile options .

⚠️ **GitHub.com Fallback** ⚠️