System Verilog: IC Design

Instantiating a module:
  • Module declarations are templates and their actual objects (instances) are created (instantiations) and they are actually instantiated.
  • Instantiating all modules in the design is a must except the top level module which creates its own instance for simulation and synthesis.
  • If the design is complex, the whole design is sub-divided into sub-blocks, and different modules are defined for those sub-blocks. Then all the sub-blocks are instantiated in the main/top module, and the top module is created automatically when we are synthesizing.
  • Syntax:   module_name   instance_name   ( port_connection_list );

Different styles of instantiating a module:
  • SV Code Examples:

module adder (sum, d1, d2);
output logic [7:0] sum;
input logic [7:0] d1, d2;
        //module functionality//
endmodule: adder

   ------------------------Instantiation of 'adder' module---------------------------

module alu(result, data1,data2,opcode);

output logic [16:0] result;
input logic [16:0] data1, data2;
input logic [1:0] opcode;

logic [7:0] tmp1, tmp2, tmp3, tmp4, tmp5, tmp6;
logic [7:0] sum1, sum2, sum3, sum4;
logic [7:0] sum, d1, d2;

adder add1 (sum1, tmp1, tmp2);

adder add2 (sum2, tmp3, tmp4);

adder add3 (.d1(tmp5), .sum(sum3), .d2(tmp6));

adder add4 (.*); //only instantiated once

adder add5 (.sum(sum4), .*); //since the above has already been instatiated, only for reference 

// module functionality

endmodule: alu

  • When instantiating, remember to connect the same signals with the same width in the instance.
  • Don't connect two signals in two different instances of a module.

Parameterized Modules:
  • Modules can use parameters internal to them and get their values while instantiating.
  • Syntax: module_name #() instance_name (port_connection_list);

module memory (addr,data,cntl);

parameter ADDR_WIDTH = 8;
parameter DATA_WIDTH = 16;

input logic [ADDR_WIDTH - 1 :0] addr;
input logic [DATA_WIDTH - 1 :0] data;
input logic [5:0] cntl;

//Module funtionality

endmodule: memory

module cpu_fun();

//variable declarations

//Instantiating memory

//No parameter values are given. Parameter gets their default values ADDR_WIDTH=8, DATA_WIDTH=16
memory mem1(.addr(a1), .data(d1), .cntl(c1));

//setting parameter values ADDR_WIDTH=12, DATA_WIDTH=24
memory #(12,24) mem2 (a1,d1,c1);

//Explicitly passing parameters. Setting parameter values as ADDR_WIDTH=16, DATAWIDTH=32
memory #(.ADDR_WIDTH(16), .DATA_WIDTH(32) )
mem2( .addr(a1), .data(d1), .cntl(c1) );

endmodule: cpu_fun
  • This is a very useful and powerful way. The same module can be instantiated with different values.

Functions & Tasks: Examples
  • Categorized into two types: Functions or a task
  • Functions: Don't consume time. Very similar to a module. 
  • Tasks: Consume time. They aren't synthesizable for RTL design. Very useful in creating Testbenches but we will never use them in design/synthesis coding.
Function definition with an example:
function logic [31:0] find_sum (input logic [31:0] a, b);
         logic [31:0] temp;
         temp = a+b;
         return temp;

endfunction: find_sum


function logic [31:0] find_sum (input logic [31:0] a,b);

         find_sum = a+b;

endfunction: find_sum

Task function: Not used for synthesizable RTL design - task task_name(followed by the argument list);
task my_task(input logic [31:0] a,b,output int x );
endtask: my_task