image/svg+xml
  • Contents
      • Back
      • Digital Basics
      • Verilog
      • Verification
      • SystemVerilog
      • UVM
Most Popular
Verification
  Testbench Evolution
  Constraint Random Verification
  Verification Techniques
  Verification Plan
  Code Coverage

Verilog
  Data Types
  Basic Constructs
  Behavioral Modeling
  Gate Modeling
  Simulation Basics
  Design Examples

SystemVerilog
  Data Types
  Class
  Interface
  Constraints and more!
  Testbench Examples

UVM
  Sequences
  Testbench Components
  TLM Tutorial
  Register Model Tutorial
  Testbench Examples

Digital Fundamentals
  Binary Arithmetic
  Boolean Logic
  Karnaugh Maps
  Combinational Logic
  Sequential Logic




Verilog scalar and vector

  1. Scalar and Vector
  2. Bit-selects
  3. Part-selects
  4. Common Errors

Verilog needs to represent individual bits as well as groups of bits. For example, a single bit sequential element is a flip-flop. However a 16-bit sequential element is a register that can hold 16 bits. For this purpose, Verilog has scalar and vector nets and variables.

Scalar and Vector

A net or reg declaration without a range specification is considered 1-bit wide and is a scalar. If a range is specified, then the net or reg becomes a multibit entity known as a vector.

scalar and vector in verilog

	wire 	    o_nor;           // single bit scalar net
	wire [7:0]  o_flop;          // 8-bit vector net
	reg         parity;          // single bit scalar variable
	reg  [31:0] addr;            // 32 bit vector variable to store address

The range gives the ability to address individual bits in a vector. The most significant bit of the vector should be specified as the left hand value in the range while the least significant bit of the vector should be specified on the right.


	wire  [msb:lsb]   name;
	integer           my_msb;
	
	wire [15:0]        priority;      // msb = 15, lsb = 0
	wire [my_msb: 2]   prior;         // illegal

A 16 bit wide net called priority will be created in the example above. Note that the msb and lsb should be a constant expression and cannot be substituted by a variable. But they can be any integer value - positive, negative or zero; and the lsb value can be greater than, equal to or less than msb value.

Bit-selects

Any bit in a vectored variable can be individually selected and assigned a new value as shown below. This is called as a bit-select. If the bit-select is out of bounds or the bit-select is x or z, then the value returned will be x.

bit-select in verilog

	reg [7:0]      addr;         // 8-bit reg variable [7, 6, 5, 4, 3, 2, 1, 0]
	
	addr [0] = 1;                // assign 1 to bit 0 of addr
	addr [3] = 0;                // assign 0 to bit 3 of addr
	addr [8] = 1;                // illegal : bit8  does not exist in addr

Part-selects

part-select in verilog

A range of contiguous bits can be selected and is known as a part-select. There are two types of part-selects, one with a constant part-select and another with an indexed part-select.


	reg [31:0]    addr;
	
	addr [23:16] = 8'h23;         // bits 23 to 16 will be replaced by the new value 'h23 -> constant part-select

Having a variable part-select allows it to be used effectively in loops to select parts of the vector. Although the starting bit can be varied, the width has to be constant.

[<start_bit> +: <width>]     // part-select increments from start-bit
[<start_bit> -: <width>]     // part-select decrements from start-bit

module des;
  reg [31:0]  data;
  int         i;
  
  initial begin
    data = 32'hFACE_CAFE;
    for (i = 0; i < 4; i++) begin
      $display ("data[8*%0d +: 8] = 0x%0h", i, data[8*i +: 8]);
    end
    
    $display ("data[7:0]   = 0x%0h", data[7:0]);
    $display ("data[15:8]  = 0x%0h", data[15:8]);
    $display ("data[23:16] = 0x%0h", data[23:16]);
    $display ("data[31:24] = 0x%0h", data[31:24]);
  end
  
endmodule
 Simulation Log
ncsim> run
data[8*0 +: 8] = 0xfe              // ~ data [8*0+8 : 8*0]
data[8*1 +: 8] = 0xca              // ~ data [8*1+8 : 8*1]
data[8*2 +: 8] = 0xce              // ~ data [8*2+8 : 8*2]
data[8*3 +: 8] = 0xfa              // ~ data [8*3+8 : 8*3]

data[7:0]   = 0xfe
data[15:8]  = 0xca
data[23:16] = 0xce
data[31:24] = 0xfa
ncsim: *W,RNQUIE: Simulation is complete.

Common Errors


module tb;
   reg [15:0]    data;
   
   initial begin
      $display ("data[0:9] = 0x%0h", data[0:9]);   // Error : Reversed part-select index expression ordering
   end
endmodule

Verilog case statement

  1. Syntax
  2. Example
    1. Hardware Schematic
  3. How is a case different from if-else ?
  4. Bus Protocol Constraints
-->

The case statement checks if the given expression matches one of the other expressions in the list and branches accordingly. It is typically used to implement a multiplexer. The if-else construct may not be suitable if there are many conditions to be checked and would synthesize into a priority encoder instead of a multiplexer.

Syntax

A Verilog case statement starts with the case keyword and ends with the endcase keyword. The expression within parantheses will be evaluated exactly once and is compared with the list of alternatives in the order they are written and the statements for which the alternative matches the given expression are executed. A block of multiple statements must be grouped and be within begin and end.


	// Here 'expression' should match one of the items (item 1,2,3 or 4)
	case (<expression>) 
		case_item1 : 	<single statement>
		case_item2,
		case_item3 : 	<single statement>
		case_item4 : 	begin
		          			<multiple statements>
		        			end
		default 	 : <statement>
	endcase

If none of the case items match the given expression, statements within the default item is executed. The default statement is optional, and there can be only one default statement in a case statement. Case statements can be nested.

Execution will exit the case block without doing anything if none of the items match the expression and a default statement is not given.

Example

The design module shown below has a 2-bit select signal to route one of the three other 3-bit inputs to the output signal called out . A case statement is used to assign the correct input to output based on the value of sel . Since sel is a 2-bit signal, it can have 22 combinations, 0 through 3. The default statement helps to set output to 0 if sel is 3.


module my_mux (input       [2:0] 	a, b, c, 		// Three 3-bit inputs
                           [1:0]	sel, 			  // 2-bit select signal to choose from a, b, c
               output reg  [2:0] 	out); 			// Output 3-bit signal 
  
  // This always block is executed whenever a, b, c or sel changes in value
  always @ (a, b, c, sel) begin 		
    case(sel)
      2'b00    : out = a; 		// If sel=0, output is a
      2'b01    : out = b; 		// If sel=1, output is b
      2'b10    : out = c; 		// If sel=2, output is c
      default  : out = 0; 		// If sel is anything else, out is always 0
    endcase
  end
endmodule

Hardware Schematic

The rtl code is elaborated to get a hardware schematic that represents a 4 to 1 multiplexer.

See that output is zero when sel is 3 and corresponds to the assigned inputs for other values.

 Simulation Log
ncsim> run
[0]  a=0x4 b=0x1 c=0x1 sel=0b11 out=0x0
[10] a=0x5 b=0x5 c=0x5 sel=0b10 out=0x5
[20] a=0x1 b=0x5 c=0x6 sel=0b01 out=0x5
[30] a=0x5 b=0x4 c=0x1 sel=0b10 out=0x1
[40] a=0x5 b=0x2 c=0x5 sel=0b11 out=0x0
ncsim: *W,RNQUIE: Simulation is complete.

In a case statement, the comparison only succeeds when each bit of the expression matches one of the alternatives including 0, 1, x and z. In the example shown above, if any of the bits in sel is either x or z, the default statement will be executed because none of the other alternatives matched. In such a case, output will be all zeros.

 Simulation Log
ncsim> run
[0] a=0x4 b=0x1 c=0x1 sel=0bxx
 out=0x0
[10] a=0x3 b=0x5 c=0x5 sel=0bzx out=0x0
[20] a=0x5 b=0x2 c=0x1 sel=0bxx out=0x0
[30] a=0x5 b=0x6 c=0x5 sel=0bzx
 out=0x0
[40] a=0x5 b=0x4 c=0x1 sel=0bxz out=0x0
[50] a=0x6 b=0x5 c=0x2 sel=0bxz
 out=0x0
[60] a=0x5 b=0x7 c=0x2 sel=0bzx out=0x0
[70] a=0x7 b=0x2 c=0x6 sel=0bzz
 out=0x0
[80] a=0x0 b=0x5 c=0x4 sel=0bxx out=0x0
[90] a=0x5 b=0x5 c=0x5 sel=0bxz out=0x0
ncsim: *W,RNQUIE: Simulation is complete.

If the case statement in design has x and z in the case item alternatives, the results would be quite different.


module my_mux (input  		[2:0] 	a, b, c,
													[1:0]		sel,
							output reg	[2:0] 	out);
  
  // Case items have x and z and sel has to match the exact value for
  // output to be assigned with the corresponding input
  always @ (a, b, c, sel) begin
    case(sel)
      2'bxz			:	out = a;
      2'bzx			:	out = b;
      2'bxx			:	out = c;
      default 	:	out = 0;
    endcase
  end
endmodule
 Simulation Log
ncsim> run
[0] a=0x4 b=0x1 c=0x1
 sel=0bxx
 out=0x1
[10] a=0x3 b=0x5 c=0x5 sel=0bzx out=0x5
[20] a=0x5 b=0x2 c=0x1 sel=0bxx out=0x1
[30] a=0x5 b=0x6
 c=0x5 sel=0bzx
 out=0x6
[40] a=0x5 b=0x4 c=0x1 sel=0bxz out=0x5
[50] a=0x6
 b=0x5 c=0x2 sel=0bxz
 out=0x6
[60] a=0x5 b=0x7 c=0x2 sel=0bzx out=0x7
[70] a=0x7 b=0x2 c=0x6 sel=0bzz
 out=0x0
[80] a=0x0 b=0x5 c=0x4 sel=0bxx out=0x4
[90] a=0x5 b=0x5 c=0x5 sel=0bxz out=0x5
ncsim: *W,RNQUIE: Simulation is complete.

How is a case different from if-else ?

The case statement is different from if-else-if in two ways:

  • Expressions given in a if-else block are more general while in a case block, a single expression is matched with multiple items
  • case will provide a definitive result when there are X and Z values in an expression

Bus Protocol Constraints

  1. Memory Partitioning Example
-->

Digital blocks typically communicate with each other using bus protocols, a few examples of which includes AMBA AXI, WishBone, OCP, etc. Bus masters that send out data adhering to a certain protocol provide control signals that tell the slave when the packet is valid, and whether it is a read or write, and how many bytes of data is sent. The master also sends out an address followed by the data to be stored at that address.

Let's see a quick example where the testbench acts as the master and constrains the bus packet class object with valid data.


// Burst [ 0 -> 1 byte, 1 -> 2 bytes, 2 -> 3 bytes, 3 -> 4 bytes]
// Length -> max 8 transactions per burst
// Protocol expects to send only first addr, and slave should calculate all
// other addresses from burst and length properties

class BusTransaction;
  rand int 			m_addr;
  rand bit [31:0]	m_data; 	
  rand bit [1:0] 	m_burst; 	// Size of a single transaction in bytes (4 bytes max)
  rand bit [2:0] 	m_length; 	// Total number of transactions
  
  constraint c_addr { m_addr % 4 == 0; } // Always aligned to 4-byte boundary
  
  function void display(int idx = 0);
    $display ("------ Transaction %0d------", idx);
    $display (" Addr 	= 0x%0h", m_addr);
    $display (" Data 	= 0x%0h", m_data);
    $display (" Burst 	= %0d bytes/xfr", m_burst + 1);
    $display (" Length  = %0d", m_length + 1);
  endfunction
endclass

module tb;
  int 				slave_start;
  int  				slave_end;
  BusTransaction	bt;
  
  // Assume we are targeting a slave with addr range 0x200 to 0x800
  initial begin
  	slave_start = 32'h200;
    slave_end 	= 32'h800;
    bt = new;
    
    bt.randomize() with { m_addr >= slave_start; 
                          m_addr < slave_end;
                         (m_burst + 1) * (m_length + 1) + m_addr < slave_end;
                        };
    bt.display();
  end
endmodule
 Simulation Log
ncsim> run
------ Transaction 0------
 Addr 	= 0x6e0
 Data 	= 0xbbe5ea58
 Burst 	= 4 bytes/xfr
 Length  = 5
ncsim: *W,RNQUIE: Simulation is complete.

Memory Partitioning Example

  1. Memory block randomization
  2. Equal partitions of memory
  3. SystemVerilog Array Randomization
-->

Consider the following practical examples typically encountered during actual projects.

Memory block randomization

Assume we have a 2KB SRAM in the design intended to store some data. Let's say that we need to find a block of addresses within the 2KB RAM space that can be used for some particular purpose.

block allocation in memory

class MemoryBlock;
  
  bit [31:0] 		m_ram_start; 			// Start address of RAM
  bit [31:0] 		m_ram_end; 				// End address of RAM
  
  rand bit [31:0] 	m_start_addr; 			// Pointer to start address of block
  rand bit [31:0]   m_end_addr; 			// Pointer to last addr of block
  rand int 			m_block_size; 			// Block size in KB
  
  constraint c_addr { m_start_addr >= m_ram_start; 	// Block addr should be more than RAM start 
                      m_start_addr < m_ram_end; 	// Block addr should be less than RAM end 
                      m_start_addr % 4 == 0;  		// Block addr should be aligned to 4-byte boundary
                      m_end_addr == m_start_addr + m_block_size - 1; };
   
  constraint c_blk_size { m_block_size inside {64, 128, 512 }; }; 	// Block's size should be either 64/128/512 bytes
  
  function void display();
    $display ("------ Memory Block --------");
    $display ("RAM StartAddr   = 0x%0h", m_ram_start);
    $display ("RAM EndAddr     = 0x%0h", m_ram_end);
	$display ("Block StartAddr = 0x%0h", m_start_addr);
    $display ("Block EndAddr   = 0x%0h", m_end_addr);
    $display ("Block Size      = %0d bytes", m_block_size);
  endfunction
endclass

module tb;
  initial begin
    MemoryBlock mb = new;
    mb.m_ram_start = 32'h0;
    mb.m_ram_end   = 32'h7FF; 		// 2KB RAM
    mb.randomize();
    mb.display();
  end
endmodule

In the example above, we have assumed the RAM to start from 0x0 and end at 0x7FF. The constraint example aims to allocate a block of memory space between this range with a size that is randomly chosen from 64 or 128 or 512 bytes. The start address of the block is randomized to be 0x714 and hence the end addr is 0x753.

 Simulation Log
ncsim> run
------ Memory Block --------
RAM StartAddr   = 0x0
RAM EndAddr     = 0x7ff
Block StartAddr = 0x714
Block EndAddr   = 0x753
Block Size      = 64 bytes
ncsim: *W,RNQUIE: Simulation is complete.

Equal partitions of memory

In this example, we'll try to partition the 2KB SRAM into N partitions with each parititon having equal size.

memory partitions with equal sizes

Read more: Memory Partitioning Example

SystemVerilog Array Randomization

  1. Static Arrays
  2. Dynamic Arrays
  3. Queue randomization
-->

SystemVerilog randomization also works on array data structures like static arrays, dynamic arrays and queues. The variable has to be declared with type rand or randc to enable randomization of the variable.

Static Arrays

Randomization of static arrays are straight-forward and can be done similar to any other type of SystemVerilog variable.


class Packet;
  rand bit [3:0] 	s_array [7]; 	// Declare a static array with "rand"
endclass

module tb;
  Packet pkt;
  
  // Create a new packet, randomize it and display contents
  initial begin
    pkt = new();
    pkt.randomize();
    $display("queue = %p", pkt.s_array);
  end 
endmodule
 Simulation Log
ncsim> run
queue = '{'hf, 'hf, 'h2, 'h9, 'he, 'h4, 'ha}
ncsim: *W,RNQUIE: Simulation is complete.

Dynamic Arrays

Dynamic arrays are arrays where the size is not pre-determined during array declaration. These arrays can have variable size as new members can be added to the array at any time.

Consider the example below where we declare a dynamic array as indicated by the empty square brackets [] of type rand. A constraint is defined to limit the size of the dynamic array to be somewhere in between 5 and 8. Another constraint is defined to assign each element in the array with the value of its index.


class Packet;
  rand bit [3:0] 	d_array []; 	// Declare a dynamic array with "rand"
  
  // Constrain size of dynamic array between 5 and 10  
  constraint c_array { d_array.size() > 5; d_array.size() < 10; }
  
  // Constrain value at each index to be equal to the index itself
  constraint c_val   { foreach (d_array[i]) 
    					d_array[i] == i; 
                     }
    
  // Utility function to display dynamic array contents
  function void display();
    foreach (d_array[i]) 
      $display ("d_array[%0d] = 0x%0h", i, d_array[i]);
  endfunction
endclass

module tb;
  Packet pkt;
  
  // Create a new packet, randomize it and display contents
  initial begin
    pkt = new();
    pkt.randomize();
    pkt.display();
  end 
endmodule

Randomization yields an empty array if the size is not constrainted -> applicable for dynamic arrays and queues

Note that the array size was randomized to 9 (from constraint c_array), and the element at each index has a value of the index itself (from constraint c_val.

 Simulation Log
ncsim> run
d_array[0] = 0x0
d_array[1] = 0x1
d_array[2] = 0x2
d_array[3] = 0x3
d_array[4] = 0x4
d_array[5] = 0x5
d_array[6] = 0x6
d_array[7] = 0x7
d_array[8] = 0x8
ncsim: *W,RNQUIE: Simulation is complete.

Queue randomization


class Packet;
  rand bit [3:0] 	queue [$]; 	// Declare a queue with "rand"
  
  // Constrain size of queue between 5 and 10  
  constraint c_array { queue.size() == 4; }
endclass

module tb;
  Packet pkt;
  
  // Create a new packet, randomize it and display contents
  initial begin
    pkt = new();
    pkt.randomize();
    
    // Tip : Use %p to display arrays
    $display("queue = %p", pkt.queue);
  end 
endmodule
 Simulation Log
ncsim> run
queue = '{'hf, 'hf, 'h2, 'h9}
ncsim: *W,RNQUIE: Simulation is complete.
  1. SystemVerilog Class Handle
  2. SystemVerilog wait fork
  3. SystemVerilog disable fork join
  4. SystemVerilog Soft Constraints
  5. HDL access routines

Page 22 of 63

  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
Interview Questions
  Verilog Interview Set 1
  Verilog Interview Set 2
  Verilog Interview Set 3
  Verilog Interview Set 4
  Verilog Interview Set 5

  SystemVerilog Interview Set 1
  SystemVerilog Interview Set 2
  SystemVerilog Interview Set 3
  SystemVerilog Interview Set 4
  SystemVerilog Interview Set 5

  UVM Interview Set 1
  UVM Interview Set 2
  UVM Interview Set 3
  UVM Interview Set 4
Related Topics
  Digital Fundamentals
  Verilog Tutorial

  Verification
  SystemVerilog Tutorial
  UVM Tutorial
  • Verilog Testbench
  • Verilog Coding Style Effect
  • Verilog Conditional Statements
  • Verilog Interview Set 10
  • Synchronous FIFO
  • SystemVerilog Interview Set 10
  • SystemVerilog Interview Set 9
  • SystemVerilog Interview Set 8
  • SystemVerilog Interview Set 7
  • SystemVerilog Interview Set 6
  • UVM Singleton Object
  • UVM Component [uvm_component]
  • UVM Object [uvm_object]
  • UVM Root [uvm_root]
  • UVM Interview Set 4
© 2015 - 2023 ChipVerify
Terms and Conditions | DMCA.com Protection Status