Showing posts with label SV. Show all posts
Showing posts with label SV. Show all posts

November 24, 2023

Mastering Verilog: Part 5 - Understanding Blocking and Non Blocking Statements

 

  • Procedural Statements in Verilog, such as blocking and non-blocking assignments, are categorized as elements of procedural blocks, such as ‘always’ and ‘initial.’
  • These statements play a crucial role in updating variables, and once a value is assigned, it remains unchanged until another procedural assignment modifies it. This stands in contrast to continuous assignments, where the value of a variable changes continuously.
  • In procedural assignments, the order of signal assignments and the flow of execution are explicitly determined, providing control over the sequencing of operations within the design.
  • Procedural blocks in Verilog primarily fall into two categories:
  1. always Blocks:
    Usage:
     Utilized to describe both combinational and sequential logics, triggered by events such as a clock edge (posedge or negedge).
    Example:
    always @(posedge clk) begin
    // Sequential or combinational logic here
    end
  2. initial Blocks:
    Usage:
     Employed to specify initial conditions or setup during simulation, executing once at the beginning.
    Example:
    initial begin
    // Initialization logic here
    end
  • Now let us see how these procedural statements work.

1] Blocking assignments:

  • Syntax:
    Specified using the ‘=’ operator.
  • Execution Flow:
    Statements are executed sequentially in the order specified within the procedural block.
    The execution of subsequent statements is blocked until the current assignment is completed.
  • Scope:
    Blocking assignments within one procedural block do not impact the execution of statements in other procedural blocks.
  • Example:
    Now let us consider below example to understand how blocking statements work. Below is not the complete verilog code but a module to understand the concept.

integer x,y,z;
initial
begin
x = 20;
y = 15;
z = 30;

x = y + z;
y = x + 10;
z = x — y;
end

Now the output for above logic will be as follows:

initially, x=20,y=15,z=30
x becomes 45
y becomes 55
z becomes -10

  • Initial Values:
    Initially, the values of x, y, and z are set to 20, 15, and 30, respectively.
  • Execution Steps:
    After the execution of the first statement (x = y + z), the value of x becomes 45.
    The second statement (y = x + 10) utilizes the updated value of x (now 45), resulting in y becoming 55.
    Finally, the third statement (z = x — y) uses the updated values of x (45) and y (55), causing z to become -10.

Provided below is the Verilog code for the logic mentioned above. Experiment with its implementation in simulation software to observe the output.

module blocking_assignment;
reg [31:0] x, y, z;

initial begin
x = 20;
y = 15;
z = 30;

// Blocking assignments
x = y + z; // x is assigned the value of y + z (15 + 30 = 45)
y = x + 10; // y is assigned the value of x + 10 (45 + 10 = 55)
z = x — y; // z is assigned the value of x — y (45–55 = -10)

// Displaying the values after the assignments
$display(“x = %0d, y = %0d, z = %0d”, x, y, z);
end
endmodule

  • Now let us consider the same example but with delays:

integer x,y,z;
initial
begin
x = 20;
y = 15;
z = 30;

x = y + z;
#5 y = x + 10;
#10 z = x — y;
end

  • Now the output for above logic will be as follows:

initially, x=20,y=15,z=30
x becomes 45
y becomes 55
z becomes -10

  • Initial Values:
    Initially, the values of x, y, and z are set to 20, 15, and 30, respectively.
  • Execution Steps:
    After the execution of the first statement (x = y + z), the value of x becomes 45.
    At time 0, the second statement (#5 y = x + 10) is scheduled to occur at time 5.
    However, this scheduling doesn’t affect the immediate execution of the next statement.
    At time 0, the third statement (#10 z = x — y) is scheduled to occur at time 10 + 5 = 15.
    This means that the actual execution of the third statement occurs at time 15.
    So, in the scenario with delays, the execution of the third statement (z = x — y) occurs at time 15, not immediately after the second statement. Therefore, the value of z becomes -10 at time 15.

2] Non Blocking Assignment:

  • Syntax:
    Specified using the ‘<=’ operator.
  • Key Characteristics:
    Non-blocking assignments allow concurrent execution of statements within the same procedural block and do not block the execution of the next statement.
    Particularly suitable for sequential logic implementation in Verilog, commonly used to model flip-flops and other sequential elements in digital circuits.
  • Sequential Logic Implementation:
    Non-blocking assignments help avoid race conditions in sequential logic design.
    When multiple signals are updated within the same clocked always block, non-blocking assignments ensure that all updates occur simultaneously at the next clock edge.
  • Example:
    Let’s consider the same example used for blocking statements to illustrate how non-blocking statements work:

integer x, y, z;
initial begin
x = 20;
y = 15;
z = 30;

x <= #10 y + z;
y <= #10 x + 10;
z <= #10 x — y;
end

  • Output Explanation:
    Initially, the values of x, y, and z are set to 20, 15, and 30.
    After the execution of the statements at time 10 (due to delays), the values become:
    x becomes 45
    y becomes 30
    z becomes -5
  • Initially, the values of x, y, and z are set to 20, 15, and 30, respectively.
    According to the given delay (#10), all statements within the initial block will execute at time 10 and will consider the initially defined values for calculation.

In conclusion, the significance of blocking and non-blocking assignments in Verilog coding cannot be overstated. These elements serve as the foundation for precise and effective digital circuit design, offering control over sequential and concurrent execution. As you venture further into the intricacies of Verilog, remember that mastering the art of assignments empowers you to create resilient and optimized digital systems. Happy coding!

Like, Share and Follow me if you like my content.
Thank You.

November 2, 2023

Verilog - NOT Gate

 not_gate.v

module and_gate(a,y);
input a;
output y;

not(y,a);
endmodule

tb_not_gate.v

module tb_not_gate;
reg a;
wire y;

not_gate UUT (.a(a), .y(y));

initial begin
$display(“Testing NOT gate”);

a = 0;
#10;
$display(“Input_A = %b, Output = %b”, a,y);

a = 1;
#10;
$display(“Input_A = %b, Output = %b”, a,y);

$finish;
end
endmodule

The output waveform for not gate will be as follows:


Verilog - XNOR Gate

 xnor_gate.v

module xnor_gate(a,b,y);
input a;
input b;
output y;

xnor(y,a,b);
endmodule

tb_xnor_gate.v

module tb_xnor_gate;
reg a;
reg b;
wire y;

xnor_gate UUT (.a(a), .b(b), .y(y));

initial begin
$display(“Testing XNOR gate”);

a = 0; b=0;
#10;
$display(“Input_A = %b, Input_B = %b, Output = %b”, a,b,y);

a = 0; b=1;
#10;
$display(“Input_A = %b, Input_B = %b, Output = %b”, a,b,y);

a = 1; b=0;
#10;
$display(“Input_A = %b, Input_B = %b, Output = %b”, a,b,y);

a = 1; b=1;
#10;
$display(“Input_A = %b, Input_B = %b, Output = %b”, a,b,y);

$finish;
end
endmodule

The output waveform for xnor gate will be as follows:


Verilog - XOR Gate

 xor_gate.v

module xor_gate(a,b,y);
input a;
input b;
output y;

xor(y,a,b);
endmodule

tb_xor_gate.v

module tb_xor_gate;
reg a;
reg b;
wire y;

xor_gate UUT (.a(a), .b(b), .y(y));

initial begin
$display(“Testing XOR gate”);

a = 0; b=0;
#10;
$display(“Input_A = %b, Input_B = %b, Output = %b”, a,b,y);

a = 0; b=1;
#10;
$display(“Input_A = %b, Input_B = %b, Output = %b”, a,b,y);

a = 1; b=0;
#10;
$display(“Input_A = %b, Input_B = %b, Output = %b”, a,b,y);

a = 1; b=1;
#10;
$display(“Input_A = %b, Input_B = %b, Output = %b”, a,b,y);

$finish;
end
endmodule

The output waveform for xor gate will be as follows:


Verilog - NOR Gate

 nor_gate.v

module nor_gate(a,b,y);
input a;
input b;
output y;

nor(y,a,b);
endmodule

tb_nor_gate.v

module tb_nor_gate;
reg a;
reg b;
wire y;

nor_gate UUT (.a(a), .b(b), .y(y));

initial begin
$display(“Testing NOR gate”);

a = 0; b=0;
#10;
$display(“Input_A = %b, Input_B = %b, Output = %b”, a,b,y);

a = 0; b=1;
#10;
$display(“Input_A = %b, Input_B = %b, Output = %b”, a,b,y);

a = 1; b=0;
#10;
$display(“Input_A = %b, Input_B = %b, Output = %b”, a,b,y);

a = 1; b=1;
#10;
$display(“Input_A = %b, Input_B = %b, Output = %b”, a,b,y);

$finish;
end
endmodule

The output waveform for nor gate will be as follows:


Verilog - NAND Gate

 nand_gate.v

module nand_gate(a,b,y);
input a;
input b;
output y;

nand(y,a,b);
endmodule

tb_nand_gate.v

module tb_nand_gate;
reg a;
reg b;
wire y;

nand_gate UUT (.a(a), .b(b), .y(y));

initial begin
$display("Testing NAND gate");

a = 0; b=0;
#10;
$display("Input_A = %b, Input_B = %b, Output = %b", a,b,y);

a = 0; b=1;
#10;
$display("Input_A = %b, Input_B = %b, Output = %b", a,b,y);

a = 1; b=0;
#10;
$display("Input_A = %b, Input_B = %b, Output = %b", a,b,y);

a = 1; b=1;
#10;
$display("Input_A = %b, Input_B = %b, Output = %b", a,b,y);

$finish;
end
endmodule

The output waveform for nand gate will be as follows:


Verilog - Or Gate

 or_gate.v

module or_gate(a,b,y);
input a;
input b;
output y;

or(y,a,b);
endmodule

tb_or_gate.v

module tb_or_gate;
reg a;
reg b;
wire y;

or_gate UUT (.a(a), .b(b), .y(y));

initial begin
$display(“Testing OR gate”);

a = 0; b=0;
#10;
$display(“Input_A = %b, Input_B = %b, Output = %b”, a,b,y);

a = 0; b=1;
#10;
$display(“Input_A = %b, Input_B = %b, Output = %b”, a,b,y);

a = 1; b=0;
#10;
$display(“Input_A = %b, Input_B = %b, Output = %b”, a,b,y);

a = 1; b=1;
#10;
$display(“Input_A = %b, Input_B = %b, Output = %b”, a,b,y);

$finish;
end
endmodule

The output waveform for or gate will be as follows:


September 7, 2023

Parallel Programming in System Verilog: Part 2 — Disable fork and Wait fork.

In my previous blog, I have explained how System Verilog’s fork and join constructs offer powerful tools for managing concurrency and parallelism in your verification and design processes.

Let’s now delve into an exploration of two fundamental System Verilog constructs: “disable fork” and “wait fork.”

Disable fork

While performing fork join_any we can see that the statement after join_any gets executed after any one process inside the fork gets executed. And the remaining statements inside fork gets executed after the join_any statement gets executed.

Consider below example:

module tb_fork_join_any;

    initial begin

        #1 $display (“Thread has started at time = %0t”,$time );

        fork

        //Thread 1
            #6 $display(“This statement is inside the fork join_any and starts at 6ns delay from the initial delay which is at time 1ns+6ns= %0t”,$time);

        //Thread 2
        begin
            #3 $display(“This statement is inside the begin end block and start at 3ns delay from the initial delay which is at time 1ns +3ns =%0t”, $time);
            #5 $display(“This statement is the 2nd statement inside the begin end block and start at 5ns delay from the previous delay which is at time 1ns +3ns +5ns=%0t”, $time);
        end

        // Thread 3
            #10 $display(“This statement is inside the fork join_any and start at 10ns delay from the initial delay which is at time 1ns + 10ns=%0t”, $time);

        join_any

            $display(“Fork join_any is completed at time=%0t”, $time);

    end
endmodule

OUTPUT:

Thread has started at time = 1
This statement is inside the begin end block and start at 3ns delay from the initial delay which is at time 1ns +3ns =4
This statement is inside the fork join_any and starts at 6ns delay from the initial delay which is at time 1ns+6ns=7
Fork join_any is completed at time=7
This statement is the 2nd statement inside the begin end block and start at 5ns delay from the previous delay which is at time 1ns +3ns +5ns=9
This statement is inside the fork join_any and start at 10ns delay from the initial delay which is at time 1ns + 10ns=11

Here, within the fork join_any block, we have three threads. Observing the output, it becomes evident that threads 2 and 3 continue to execute even after the main thread exits the fork join_any block. To address this situation, we can utilize the “disable fork” construct to terminate all active processes within the fork block.

Let’s examine the same example to illustrate how “disable fork” can be applied:

module tb_fork_join_any;

    initial begin

        #1 $display (“Thread has started at time = %0t”,$time );

        fork

        //Thread 1
            #6 $display(“This statement is inside the fork join_any and starts at 6ns delay from the initial delay which is at time 1ns+6ns= %0t”,$time);

        //Thread 2
        begin

            #3 $display(“This statement is inside the begin end block and start at 3ns delay from the initial delay which is at time 1ns +3ns =%0t”, $time);
            #5 $display(“This statement is the 2nd statement inside the begin end block and start at 5ns delay from the previous delay which is at time 1ns +3ns +5ns=%0t”, $time);

        end

        // Thread 3
            #10 $display(“This statement is inside the fork join_any and start at 10ns delay from the initial delay which is at time 1ns + 10ns=%0t”, $time);

        join_any

            $display(“Fork join_any is completed at time=%0t”, $time);

        disable fork;

    end
endmodule

OUTPUT:

Thread has started at time = 1
This statement is inside the begin end block and start at 3ns delay from the initial delay which is at time 1ns +3ns =4
This statement is inside the fork join_any and starts at 6ns delay from the initial delay which is at time 1ns+6ns=7
Fork join_any is completed at time=7

Here, we can see that the execution of Thread 2 and Thread 3 has been halted by applying the “disable fork” statement immediately after the join_any block.
This process can be visualized in the following diagram, which illustrates how these threads are executed over time:

Wait fork

Let’s delve into another concept related to fork-join constructs, which is “wait fork.”

Wait fork is employed when there are multiple processes and threads, and it facilitates the main process in waiting until all the forked processes have completed. To illustrate how wait fork operates, we’ll revisit the same example as before.

module tb_fork_join_any;

    initial begin

        #1 $display (“Thread has started at time = %0t”,$time );

        fork

        //Thread 1
            #6 $display(“This statement is inside the fork join_any and starts at 6ns delay from the initial delay which is at time 1ns+6ns= %0t”,$time);

        //Thread 2
        begin

            #3 $display(“This statement is inside the begin end block and start at 3ns delay from the initial delay which is at time 1ns +3ns =%0t”, $time);
            #5 $display(“This statement is the 2nd statement inside the begin end block and start at 5ns delay from the previous delay which is at time 1ns +3ns +5ns=%0t”, $time);

        end

        // Thread 3
            #10 $display(“This statement is inside the fork join_any and start at 10ns delay from the initial delay which is at time 1ns + 10ns=%0t”, $time);

        join_any

            $display(“Fork join_any is completed at time=%0t”, $time);

        wait fork;
            $display(“All Fork joins are completed execution and wait join is executed at time =%0t”, $time);

    end
endmodule

OUTPUT:

Thread has started at time = 1
This statement is inside the begin end block and start at 3ns delay from the initial delay which is at time 1ns +3ns =4
This statement is inside the fork join_any and starts at 6ns delay from the initial delay which is at time 1ns+6ns=7
Fork join_any is completed at time=7
This statement is the 2nd statement inside the begin end block and start at 5ns delay from the previous delay which is at time 1ns +3ns +5ns=9
This statement is inside the fork join_any and start at 10ns delay from the initial delay which is at time 1ns + 10ns=11
All Fork joins are completed execution and wait join is executed at time =11

Certainly, it’s clear from your explanation that the statement after the “wait fork” construct is executed once all the processes and threads within the fork block have completed their execution.

To enhance our understanding of “wait fork,” let’s explore another example that will provide a more comprehensive view of how this construct operates.

module tb_fork_join_any;

    initial begin

        $display(“Thread has started at time = %0t”,$time);

        fork

        //Thread — 1
        begin

            $display(“Thread 1 has started at time =%0t”, $time”);
            #10;
            $display(“Thread 1 has executed at time =%0t”, $time);

        end

        //Thread — 2
        begin

            $display(“Thread 2 has started at time =%0t”, $time);
            #20;
            $display(“Thread 2 has executed at time =%0t”, $time”);

        end

        join_any

        wait fork; 
            $display(“Wait join is executed at time =%0t”, $time”);

    end
endmodule

OUTPUT:

Thread has started at time = 0
Thread 1 has started at time = 0
Thread 2 has started at time = 0
Thread 1 has executed at time = 10
Thread 2 has executed at time = 20
Wait join is executed at time = 20

This process can be visualized in the following diagram, which illustrates how these threads are executed over time:

In this blog, we’ve explored two fundamental System Verilog constructs: ‘disable fork’ and ‘wait fork.’ We’ve discussed how ‘disable fork’ can be used to halt active processes within a fork block, providing greater control over parallel execution. Additionally, we’ve delved into the powerful capabilities of ‘wait fork,’ which enables the main process to wait until all forked processes have completed. These constructs are essential tools for managing concurrency in your verification and design processes, offering a deeper understanding of how to efficiently synchronize and control parallel threads in System Verilog.

Like, Share and Follow me if you like my content.
Thank You.

Explore Our Topics!

Check out the extensive list of topics we discuss:  Communication Protocols: -  USB   - RS232   -  Ethernet   -  AMBA Protocol: APB, AHB and...