Verilog 13: Procedural Block Control in Verilog

             Understanding Procedural Block Control in Verilog

In Verilog, procedural blocks form the foundation for describing behavioral models of hardware. These blocks define how a circuit reacts to inputs over time — enabling both combinational and sequential logic modeling.

Procedural blocks become active at simulation time zero and execute when triggered by events or signal changes defined in their sensitivity list.

Let’s explore how procedural control works, along with key examples and best coding practices.


🔹 What Are Procedural Blocks?

Procedural blocks are code regions that describe behavior rather than structure. They are defined using either of the two keywords:

  • always → executes continuously throughout simulation.

  • initial → executes once at simulation start.

These blocks can be controlled using event controls such as level or edge triggers.


🧩 Example 1 – D Latch Using always Block

Let’s begin with a basic example: the D Latch, one of the simplest sequential elements.

module d_latch_always(); reg q; reg d, enable; // Level-sensitive procedural block always @(d or enable) if (enable) q = d; initial begin $monitor("TIME=%0t | ENABLE=%b | D=%b | Q=%b", $time, enable, d, q); enable = 0; d = 0; #5 d = 1; #5 enable = 1; #5 d = 0; #5 enable = 0; #5 $finish; end endmodule

🧠 Concept:
Whenever d or enable changes, this block executes. The latch captures the input (d) only when enable is high.

📂 File: d_latch_always.v


⚙️ Combinational Logic with Procedural Blocks

To create combinational logic, your procedural block must be sensitive to all input signals that affect the output.
If using conditional statements (if), remember to include an else clause. Missing it can accidentally create a latch.


💡 Example 2 – 1-Bit Full Adder Using always

module full_adder_always(); reg a, b, cin; reg sum, cout; // Combinational always block always @(a or b or cin) begin sum = a ^ b ^ cin; cout = (a & b) | (b & cin) | (a & cin); end initial begin $monitor("A=%b | B=%b | CIN=%b | SUM=%b | COUT=%b", a, b, cin, sum, cout); a=0; b=0; cin=0; #5 b=1; #5 a=1; #5 cin=1; #5 $finish; end endmodule

🧠 Note:
This model performs bitwise addition using procedural combinational logic.

📂 File: full_adder_always.v


🚫 Avoiding Unintentional Latches

Unintended latches occur when certain conditions are not covered in procedural logic.
You can prevent this in two ways:

✅ Method 1 – Use else Conditions

module avoid_latch_else(); reg q, d, en; always @(d or en) if (en) q = d; else q = 0; // Avoids latch initial begin $monitor("EN=%b | D=%b | Q=%b", en, d, q); en=0; d=0; #5 en=1; d=1; #5 en=0; #5 $finish; end endmodule

✅ Method 2 – Initialize Outputs Inside the Block

module avoid_latch_init(); reg q, d, en; always @(d or en) begin q = 0; // Initialize if (en) q = d; end initial begin $monitor("EN=%b | D=%b | Q=%b", en, d, q); en=0; d=1; #5 en=1; d=0; #5 en=0; #5 $finish; end endmodule

🧠 Tip:
Initializing inside the block ensures outputs get defined in every condition, preventing synthesis issues.


⏱️ Sequential Logic Using Procedural Coding

Sequential logic relies on clock edges and optionally reset/preset signals.
Always use non-blocking assignments (<=) for sequential circuits to avoid timing errors.


💡 Example 3 – D Flip-Flop with Asynchronous Reset

module dff_async_reset(); reg clk, reset, d; reg q; always @(posedge clk or posedge reset) if (reset) q <= 0; else q <= d; initial begin $monitor("CLK=%b | RESET=%b | D=%b | Q=%b", clk, reset, d, q); clk=0; reset=0; d=0; #5 reset=1; #5 reset=0; d=1; #5 d=0; #10 $finish; end always #2 clk = ~clk; // Clock generator endmodule

🧠 Explanation:

  • The flip-flop responds instantly to a reset (posedge reset).

  • On every clock edge, it captures the input d.


⚠️ Example 4 – Incorrect Coding (Multiple Clocks)

Avoid using two clocks in a single sequential always block.
This is invalid for synthesis even if it works in simulation.

module wrong_seq_two_clocks(); reg clk1, clk2, d1, d2, q; always @(posedge clk1 or posedge clk2) if (clk1) q <= d1; else if (clk2) q <= d2; // Simulation initial begin clk1=0; clk2=0; d1=0; d2=1; #50 $finish; end always #3 clk1 = ~clk1; always #5 clk2 = ~clk2; endmodule

🧠 Why Wrong:
Real hardware can have only one active clock edge per flip-flop. This is simulation-only behavior.


🧱 Procedural Block Concurrency

In Verilog, all procedural blocks start together at time 0 and execute concurrently.
Multiple always or initial blocks in the same module run in parallel, just like independent hardware components.


💡 Example 5 – Parallel Blocks in a Module

module multiple_blocks_demo(); reg clk, reset, a, b, result; // Combinational block always @(a or b) result = a & b; // Sequential block always @(posedge clk) if (reset) b <= 0; else b <= result; // Testbench initial begin $monitor("TIME=%0t | CLK=%b | A=%b | B=%b | RESULT=%b", $time, clk, a, b, result); clk=0; reset=0; a=0; b=0; #5 reset=1; #5 reset=0; #5 a=1; b=1; #5 a=0; #10 $finish; end always #2 clk = ~clk; endmodule

🧠 Concept:
Both always blocks execute concurrently, like real digital hardware.


🧩 Example 6 – Named Blocks and Disable Statement

You can name a block to control its execution using the disable statement.
This is useful for early exit from loops or condition-based termination.

module named_block_example(); reg [15:0] data; integer i; reg [4:0] first_one_pos; always @(data) begin : FIND_FIRST_ONE first_one_pos = 16; for (i = 0; i < 16; i = i + 1) if (data[i]) begin first_one_pos = i; disable FIND_FIRST_ONE; // exit block early end end initial begin $monitor("DATA=%b | FIRST_ONE_POS=%d", data, first_one_pos); #5 data = 16'b0010100000000000; #5 data = 16'b0000000000001000; #5 data = 16'b0000000000000000; #10 $finish; end endmodule

🧠 Explanation:
This example identifies the first ‘1’ bit from LSB and stops the loop using disable.


🚀 Key Takeaways

Concept

Description

Procedural Blocks

Behavioral modeling using always and initial

Event Control

@() defines when a block executes

Combinational Logic

Sensitive to all inputs; no latches

Sequential Logic

Uses clock edges and non-blocking assignments

Concurrency

All blocks start and run in parallel

Named Blocks

Enable control and termination of loops


🏁 Conclusion

Procedural blocks are the core of behavioral modeling in Verilog HDL.
They bring hardware to life — enabling logic that reacts to inputs, clocks, and resets just like real-world circuits.

Whether designing a simple D latch or a complex synchronous flip-flop, mastering procedural control is the key to writing accurate, synthesizable Verilog code.



Comments

Popular posts from this blog

Fundamental of python : 1.Python Numbers