Tuesday, August 12, 2025

Generate two arrays of length 10, whose elements are unique to each other.


CODE:

class mem;
  rand int a[10];
  rand int b[10];
  
  constraint c_a_b {
    unique { b };
    foreach ( a[i] ) {
      b[i] inside {[1:20]};
      a[i] inside {[1:20]};
      !(a[i] inside b);      
    }
  }
  
  function void post_randomize();
    $display ("A:%p",a);
    $display ("B:%p",b);
  endfunction: post_randomize
    
endclass: mem
    
module top;
  mem m;
  
  initial begin
    m = new;
    void'(m.randomize());
    
  end
endmodule: top


OUTPUT:

Compiler version U-2023.03-SP2_Full64; Runtime version U-2023.03-SP2_Full64; Aug 12 11:01 2025
A:'{4, 5, 17, 19, 8, 4, 5, 12, 9, 15}
B:'{18, 16, 3, 20, 1, 11, 7, 2, 10, 14}
V C S S i m u l a t i o n R e p o r t

Single Linked List in System Verilog

 Lets code a simple singly Linked List which traverse from head to tail in one direction.

Requirement is simple, you will have a Node which contains two main elements, data and pointer to next node.

On adding a new node, the new node should create a handle of the old node and assign the pointer to the new one.


CODE:

class Node;
  int id;
  int data;
  Node next_node;
  
  function new ( int i );
    id = i;
    next_node = null;
  endfunction: new
  
  function void load_data ( int val );
    data = val;
  endfunction: load_data

  function void add_node ( Node node );
    node.next_node  = new(id);
    node.next_node  = this;
  endfunction: add_node
  
 endclass: Node
  
  module top;
    Node n[10];
    
    initial begin
      foreach ( n[i] ) begin
        n[i]      = new(i);
        n[i].data = $urandom_range(10,100);
        if(i>0) n[i].add_node(n[i-1]);           
      end 
      
      foreach ( n[i] ) begin        
        if(i==9) 
          $display ( "NODE ID:%0d Data:%0d NEXT_PTR:NULL", n[i].id,n[i].data );
        else 
          $display ( "NODE ID:%0d Data:%0d NEXT_PTR:%0d", n[i].id,n[i].data,n[i].next_node.id );
      end
    end        
  endmodule: top



RESULT:

Compiler version U-2023.03-SP2_Full64; Runtime version U-2023.03-SP2_Full64; Aug 12 10:17 2025
NODE ID:0 Data:54 NEXT_PTR:1
NODE ID:1 Data:62 NEXT_PTR:2
NODE ID:2 Data:68 NEXT_PTR:3
NODE ID:3 Data:44 NEXT_PTR:4
NODE ID:4 Data:75 NEXT_PTR:5
NODE ID:5 Data:88 NEXT_PTR:6
NODE ID:6 Data:89 NEXT_PTR:7
NODE ID:7 Data:72 NEXT_PTR:8
NODE ID:8 Data:62 NEXT_PTR:9
NODE ID:9 Data:53 NEXT_PTR:NULL
V C S S i m u l a t i o n R e p o r t

Saturday, July 26, 2025

TCAM

TCAM stands for Ternary Content Addressable Memory.

In this, instead of data, the search returns address and number of entries that match the search key.

What the code does:

  • TCAM is 16 locations deep.
  • TCAM class contains 3 methods.
    • load_tcam ( )     - It takes data and address as inputs and loads the TCAM memory
    • search_tcam ( ) - Lets dive more into this method, this is where things are handled differently when compared with the normal SRAM's. The inputs contain key and mask. Key is used to search all the entries and find the matching addresses. If there is a single hit, output bit match is set to 1. For multiple entries, match_entries field corresponding to the hit index is set to 1. Mask is what differentiates a normal CAM with TCAM. When MASK field is set to 1, the entry is always treated as matched irrespective of the contents of the entry. Finally address ( output ), this can be set as per the priority order set. In this case, I choose last match as the highest priority.
    • clear_tcam() - To clear all contents of the TCAM memory.

class tcam;
  logic [31:0] tcam_mem[16];
  
  virtual task clear_tcam ( );
    foreach (tcam_mem[i]) tcam_mem[i] = 0;
  endtask: clear_tcam
  
  virtual task load_tcam ( input logic [31:0] data , input logic [31:0] addr ); 
    tcam_mem[addr] = data;
  endtask: load_tcam
  
  virtual task search_tcam ( 
    input logic [31:0] key, 
    input logic [31:0] mask, 
    output bit match, 
    output bit [15:0] match_entries, 
    output bit [31:0] address );
for ( int i=0;i<16;i++ ) begin //{ // mask[i] == 1, don't care, always matches irrespective of key if(tcam_mem[i] == key || mask[i]) begin //{ match = 1; match_entries[i] = 1; address = i; // You can choose any priority, I choose priority of Last match address end //} end //} endtask: search_tcam endclass: tcam module top; tcam t; initial begin logic match; logic [31:0] match_entries; logic [31:0] match_address; t = new; t.load_tcam(10,0); t.load_tcam(20,1); t.load_tcam(25,2); t.load_tcam(30,3); t.load_tcam(20,4); t.search_tcam(20,0,match,match_entries,match_address); $display ("Match:%0d Entries:%0b Address:%0d",match, match_entries,match_address ); end endmodule: top

OUTPUT:

Compiler version U-2023.03-SP2_Full64; Runtime version U-2023.03-SP2_Full64; Jul 26 13:19 2025
Match:1 Entries:10010 Address:4
V C S S i m u l a t i o n R e p o r t
Time: 0 ns
CPU Time: 0.530 seconds; Data structure size: 0.0Mb
Sat Jul 26 13:19:05 2025
Done



Saturday, July 19, 2025

Generating AXI Strobe based on SIZE, DATA WIDTH

AXI write strobe is generated based on the axi size and data width supported. For example, if the axi interface you are using has 64 bit wide data bus, you can have strobe of 64/8 = 8 Let us take the same data width as an example to produce axi write strobe.

In this case, the strobe is set starting at address offset and continues till the size of the transfer is met.

For example, 
ADDR = 0x3
SIZE    = 0x2 (32 bits)
LEN     = 0x2 (3 beats)

For the first beat, ADDR = 0x3, STRB = 0xF8
For the second beat, ADDR = 0x7, STRB = 0x80
For the third beat, ADDR = 0xB, STRB = 0xF8

 
module top;
  parameter int DATA_WIDTH = 64;
  bit [2:0] aw_size;
  bit [7:0] aw_len;
  bit [31:0] aw_addr;
  bit [7:0] w_strb[$];
  bit [31:0] addr_align;
  
  function automatic bit [7:0] gen_strb ( input bit [31:0] addr, input bit [2:0] aw_size );
    bit [7:0] strb;
    int size_in_bytes = 2**aw_size;
    addr_align = addr % (DATA_WIDTH/8);
    
    
    for( int i=0;i<DATA_WIDTH/8;i++) begin
      if ( i inside {[addr_align:addr_align+size_in_bytes-1]}) strb[i] = 1;
      else strb[i] = 0;
    end
    //$display("ADDR_A:%0h BY:%0d STRB:%0h",addr_align,size_in_bytes,strb);
    return strb;
  endfunction
  
  function automatic void load_strb ( );
    bit [31:0] addr_l;
    for ( int i=0;i<aw_len+1;i++) begin
      addr_l = aw_addr + (i << aw_size);
      w_strb.push_back(gen_strb(addr_l,aw_size));
      $display("ADDR:%0d STRB:%0h",addr_l,w_strb[i]);
    end    
  endfunction
  
  initial begin
    aw_addr = 'h3;
    aw_len  = 'h2;
    aw_size = 'h2;
    load_strb();
  end
  endmodule

RESULT:

Compiler version U-2023.03-SP2_Full64; Runtime version U-2023.03-SP2_Full64; Oct 16 02:13 2025
ADDR:3 STRB:78
ADDR:7 STRB:80
ADDR:11 STRB:78
V C S S i m u l a t i o n R e p o r t

Tuesday, September 17, 2024

Constraint to have N elements distributed in M bins

Code to distribute N elements into M bins, you add unique keyword to have each bin will have unique number of elements.

class test;
  parameter int M = 5;
  parameter int N = 100;
  rand bit[31:0] arr[M];
  
  constraint c_arr { arr.sum() == N; foreach(arr[i]) { arr[i] inside {[0:N-1]}; } unique {arr}; }
  
  function void post_randomize();
    foreach(arr[i])
    $display("Array[%01d]:%02d\n",i,arr[i]);
  endfunction
  
endclass

module top;
  test t;
  
  initial begin
    t = new;
    void'(t.randomize());
  end
endmodule

RESULT:

Compiler version U-2023.03-SP2_Full64; Runtime version U-2023.03-SP2_Full64; Sep 17 10:38 2024
Array[0]:23

Array[1]:01

Array[2]:10

Array[3]:11

Array[4]:15

Array[5]:21

Array[6]:14

Array[7]:03

Array[8]:02

Array[9]:00

V C S S i m u l a t i o n R e p o r t
Time: 0 ns
CPU Time: 0.430 seconds; Data structure size: 0.0Mb
Tue Sep 17 10:38:13 2024

How to kill a thread(s) in System Verilog

 The code use process class to kill the thread. 

class base;
  int id;
  
  task display();
    int x=0;
    for(int i=0;i<5;i++) begin 
      #10ns;
      x++;
      $display("%t BASE:%0d X:%0d",$time,id,x);
    end
  endtask

endclass

class test;
  process p[4];
  task run_phase();
    for(int i=0;i<4;i++) begin //{
      base b = new;
      fork: fork_p
        automatic base b1 = b;
        automatic int j = i;
        begin: run_d
          p[j] = process::self();
          b1.id = j;
          b1.display();
        end
      join_none
    end //}
    $display("%t Threads spawned",$time);
  endtask
  
endclass

module top;
  test t;
  
  initial begin
    t = new;
    fork
    t.run_phase();
    join_none
     #30ns;
    foreach(t.p[i]) t.p[i].kill();
  end
  
endmodule
  

Result:

Compiler version U-2023.03-SP2_Full64; Runtime version U-2023.03-SP2_Full64; Sep 17 10:31 2024
0 Threads spawned
10 BASE:0 X:1
10 BASE:1 X:1
10 BASE:2 X:1
10 BASE:3 X:1
20 BASE:0 X:2
20 BASE:1 X:2
20 BASE:2 X:2
20 BASE:3 X:2
V C S S i m u l a t i o n R e p o r t
Time: 30 ns
CPU Time: 0.370 seconds; Data structure size: 0.0Mb
Tue Sep 17 10:31:42 2024

How to bind multiple instances of module inside DUT

module my_mod ( input clk, input rst );
endmodule: my_mod
 
module dut ( input clk, input rst );
  genvar i;
  generate
    for(i=0;i<2;i++) begin: gen_my_mod
      my_mod u_my_mod ( .clk(clk), .rst(rst) );
    end
  endgenerate
endmodule
 
module bind_x ( input clk, input rst );
endmodule: bind_x
 
module tb;
  bit clk;
  bit rst;
  dut u_dut ( .clk(clk), .rst(rst) );
  bind u_dut.gen_my_mod[0].u_my_mod bind_x bind_inst (.clk(clk), .rst(rst));
  bind u_dut.gen_my_mod[1].u_my_mod bind_x bind_inst (.clk(clk), .rst(rst));
endmodule: tb

Saturday, April 22, 2023

KNIGHT TOUR in System Verilog

Following is the SV code for knight tour. On each randomisation, we get the next position of knight.

The concept is simple, based on which we derived the constraints.

  1. knight should operate within boundaries of the chess board.
  2. knight can move on any direction if it does not violate point (1).
  3. The position of the knight can be taken as row, col.
  4. At best it can move in 8 directions
    1. If row is incremented/decremented by 2 positions, column can move 1 position from the latest row pos.
    2. If row is incremented/decremented by 1 position, column can move 2 position from the latest row pos.
  5. The position is calculated based on the board length, current row and column.

NOTE:
I'll check if we can walk with the knight on the chess board without repeating the same chess square again.
I doubt that there might some limitations to achieve this.
I'll  update on it, as and when I get some clues ...

CODE:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class knight_walk;
  parameter int N = 5;
  int row;
  int col;
  rand int next_row;
  rand int next_col;
  rand int pos;
  
  function new ();
    row = $urandom_range(0,N-1);
    col = $urandom_range(0,N-1);
    pos = N*row + col;
    $display(" INITIAL POS:%0d ROW:%0d COL:%0d",pos,row,col);
  endfunction
  
  constraint c_knight {
    solve next_row before next_col;
    next_row inside { row+2,row-2,row+1,row-1 };
    next_col inside { col+2,col-2,col+1,col-1 };
    next_row inside {[0:4]};
    next_col inside {[0:4]};
    
    if( next_row inside {row+2,row-2} ) { next_col inside {col+1,col-1}; }
    else                                { next_col inside {col+2,col-2}; }
    
   }
  
  function void post_randomize();
    row = next_row;
    col = next_col;
    pos = row*N+col;
    $display("POST_RANDOMIZE:: POS:%0d ROW:%0d COL:%0d",pos,row,col);
  endfunction
  
  
endclass: knight_walk

module top;
  knight_walk chess;
  
  initial begin
    chess = new;
    repeat(4)
    void'(chess.randomize());
  end
endmodule: top



RESULTS:
Compiler version S-2021.09; Runtime version S-2021.09; Apr 22 04:34 2023
INITIAL POS:3 ROW:0 COL:3
POST_RANDOMIZE:: POS:12 ROW:2 COL:2
POST_RANDOMIZE:: POS:19 ROW:3 COL:4
POST_RANDOMIZE:: POS:8 ROW:1 COL:3
POST_RANDOMIZE:: POS:1 ROW:0 COL:1
POST_RANDOMIZE:: POS:8 ROW:1 COL:3
POST_RANDOMIZE:: POS:1 ROW:0 COL:1
POST_RANDOMIZE:: POS:8 ROW:1 COL:3
POST_RANDOMIZE:: POS:1 ROW:0 COL:1
POST_RANDOMIZE:: POS:8 ROW:1 COL:3
POST_RANDOMIZE:: POS:11 ROW:2 COL:1
V C S S i m u l a t i o n R e p o r t

Friday, April 21, 2023

CIRCULAR QUEUE implementation in System Verilog


NOTE:

Print Statements are implemented on every function call.

The Read pointer in dequeue is incremented by one, if not empty before the print.

Same is the case for write pointer.


In this setup, I used the MSB of wr_ptr and rd_ptr to represent a rollover of the memory ( array ).This in turn is used to generate the FULL and EMPTY signals.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
class circular_queue;
  parameter int N = 5;
  parameter int WIDTH = 3;
  
  int arr[N];
  bit isFull;
  bit isEmpty;
  bit [WIDTH:0] wr_ptr; //clog2 ??
  bit [WIDTH:0] rd_ptr;
  
  extern function new();
  extern function void enqueue(int val);
  extern function int dequeue();
  extern function int peek();  
  
endclass: circular_queue
    
    function circular_queue::new();
      isEmpty = 1;
      isFull = 0;
    endfunction: new
    
    function void circular_queue::enqueue(int val);
      if(!isFull) begin
        isEmpty     = 0;
        arr[wr_ptr[WIDTH-1:0]] = val;
        wr_ptr[WIDTH-1:0] = (wr_ptr[WIDTH-1:0]+1) % N;
        wr_ptr[WIDTH] = wr_ptr[WIDTH-1:0] == 0 ? ~wr_ptr[WIDTH] : wr_ptr[WIDTH];
      end
      isFull = ( wr_ptr[WIDTH-1:0] == rd_ptr[WIDTH-1:0] && wr_ptr[WIDTH]!=rd_ptr[WIDTH]) ? 1 : 0;
      $display("ENQUEUE:: WR_PTR:%d RD_PTR:%d VAL:%02d ARR:%p FULL:%d EMPTY:%d",wr_ptr[WIDTH-1:0],rd_ptr[WIDTH-1:0],val,arr,isFull,isEmpty);
    endfunction: enqueue
    
    function int circular_queue::dequeue();
      int val;
      if(!isEmpty) begin
        isFull = 0;
        val = arr[rd_ptr[WIDTH-1:0]];
        rd_ptr[WIDTH-1:0] = (rd_ptr[WIDTH-1:0]+1) % N;
        rd_ptr[WIDTH] = rd_ptr[WIDTH-1:0] == 0 ? ~rd_ptr[WIDTH] : rd_ptr[WIDTH]; 
      end
      isEmpty = ( wr_ptr[WIDTH-1:0] == rd_ptr[WIDTH-1:0] && wr_ptr[WIDTH]==rd_ptr[WIDTH]) ? 1 : 0;
      $display("DEQUEUE:: WR_PTR:%d RD_PTR:%d VAL:%02d ARR:%p FULL:%d EMPTY:%d",wr_ptr[WIDTH-1:0],rd_ptr[WIDTH-1:0],val,arr,isFull,isEmpty);
      return val;
    endfunction: dequeue
    
    function int circular_queue::peek();
      int val;
      if(!isEmpty)
      val = arr[rd_ptr];
      $display("PEEK   :: WR_PTR:%d RD_PTR:%d VAL:%02d ARR:%p FULL:%d EMPTY:%d",wr_ptr[WIDTH-1:0],rd_ptr[WIDTH-1:0],val,arr,isFull,isEmpty);
      return val;
    endfunction: peek
    
module top;
  circular_queue cq;
  
  initial begin
    int wr_val;
    int rd_val;
    
    cq = new;
    repeat(4) begin
      wr_val = $urandom_range(1,100);
      cq.enqueue(wr_val);
    end
    repeat(20) begin
      randcase
      	2 : begin
          wr_val = $urandom_range(1,100);
          cq.enqueue(wr_val);
        end
        2 : begin
          rd_val = cq.dequeue();
        end
        1 : begin
          rd_val = cq.peek();
        end
      endcase
    end
  end
endmodule: top


> OUTPUT::

Chronologic VCS simulator copyright 1991-2021
Contains Synopsys proprietary information.
Compiler version S-2021.09; Runtime version S-2021.09; Apr 22 02:40 2023
ENQUEUE:: WR_PTR:1 RD_PTR:0 VAL:39 ARR:'{39, 0, 0, 0, 0} FULL:0 EMPTY:0
ENQUEUE:: WR_PTR:2 RD_PTR:0 VAL:61 ARR:'{39, 61, 0, 0, 0} FULL:0 EMPTY:0
ENQUEUE:: WR_PTR:3 RD_PTR:0 VAL:74 ARR:'{39, 61, 74, 0, 0} FULL:0 EMPTY:0
ENQUEUE:: WR_PTR:4 RD_PTR:0 VAL:71 ARR:'{39, 61, 74, 71, 0} FULL:0 EMPTY:0
DEQUEUE:: WR_PTR:4 RD_PTR:1 VAL:39 ARR:'{39, 61, 74, 71, 0} FULL:0 EMPTY:0
ENQUEUE:: WR_PTR:0 RD_PTR:1 VAL:97 ARR:'{39, 61, 74, 71, 97} FULL:0 EMPTY:0
ENQUEUE:: WR_PTR:1 RD_PTR:1 VAL:23 ARR:'{23, 61, 74, 71, 97} FULL:1 EMPTY:0
PEEK :: WR_PTR:1 RD_PTR:1 VAL:61 ARR:'{23, 61, 74, 71, 97} FULL:1 EMPTY:0
ENQUEUE:: WR_PTR:1 RD_PTR:1 VAL:91 ARR:'{23, 61, 74, 71, 97} FULL:1 EMPTY:0
DEQUEUE:: WR_PTR:1 RD_PTR:2 VAL:61 ARR:'{23, 61, 74, 71, 97} FULL:0 EMPTY:0
ENQUEUE:: WR_PTR:2 RD_PTR:2 VAL:58 ARR:'{23, 58, 74, 71, 97} FULL:1 EMPTY:0
DEQUEUE:: WR_PTR:2 RD_PTR:3 VAL:74 ARR:'{23, 58, 74, 71, 97} FULL:0 EMPTY:0
DEQUEUE:: WR_PTR:2 RD_PTR:4 VAL:71 ARR:'{23, 58, 74, 71, 97} FULL:0 EMPTY:0
PEEK :: WR_PTR:2 RD_PTR:4 VAL:97 ARR:'{23, 58, 74, 71, 97} FULL:0 EMPTY:0
ENQUEUE:: WR_PTR:3 RD_PTR:4 VAL:27 ARR:'{23, 58, 27, 71, 97} FULL:0 EMPTY:0
DEQUEUE:: WR_PTR:3 RD_PTR:0 VAL:97 ARR:'{23, 58, 27, 71, 97} FULL:0 EMPTY:0
DEQUEUE:: WR_PTR:3 RD_PTR:1 VAL:23 ARR:'{23, 58, 27, 71, 97} FULL:0 EMPTY:0
DEQUEUE:: WR_PTR:3 RD_PTR:2 VAL:58 ARR:'{23, 58, 27, 71, 97} FULL:0 EMPTY:0
ENQUEUE:: WR_PTR:4 RD_PTR:2 VAL:20 ARR:'{23, 58, 27, 20, 97} FULL:0 EMPTY:0
DEQUEUE:: WR_PTR:4 RD_PTR:3 VAL:27 ARR:'{23, 58, 27, 20, 97} FULL:0 EMPTY:0
DEQUEUE:: WR_PTR:4 RD_PTR:4 VAL:20 ARR:'{23, 58, 27, 20, 97} FULL:0 EMPTY:1
DEQUEUE:: WR_PTR:4 RD_PTR:4 VAL:00 ARR:'{23, 58, 27, 20, 97} FULL:0 EMPTY:1
ENQUEUE:: WR_PTR:0 RD_PTR:4 VAL:06 ARR:'{23, 58, 27, 20, 6} FULL:0 EMPTY:0
ENQUEUE:: WR_PTR:1 RD_PTR:4 VAL:73 ARR:'{73, 58, 27, 20, 6} FULL:0 EMPTY:0
V C S S i m u l a t i o n R e p o r t
Time: 0 ns
CPU Time: 0.700 seconds; Data structure size: 0.0Mb
Sat Apr 22 02:40:54 2023

Generating prime numbers between 1 to 100

  1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class test ; int prime_q[$]; function voi...