VERILOG SERIES ยท MODULE 12

Behavioral Modelling Part 3 โ€” if, assign-deassign, repeat, for, disable โ€” VLSI Trainers
Verilog Series ยท Module 12

Behavioral Modelling โ€” Part 3

Deep coverage of if/if-else constructs, assign-deassign, the repeat construct, for loops, and the disable statement โ€” with complete synthesizable and testbench examples.

๐Ÿ”€ if and if-else Constructs โ€” Introduction

The if statement is the most fundamental control flow construct in behavioral Verilog. It allows a procedural block to conditionally execute statements based on the value of an expression โ€” modelling priority logic, enable conditions, and state transitions.

When synthesized, an if-else chain produces a priority multiplexer tree โ€” the first condition that evaluates true wins. This is fundamentally different from case, which produces a balanced multiplexer without priority.

โœ…
Conditional Execution
Execute one set of statements or another based on a run-time condition. Models hardware mux and enable logic.
โšก
Priority Hardware
Each else if has lower priority than the preceding condition โ€” synthesis produces a cascaded priority chain.
โš ๏ธ
Latch Risk
Missing else in a combinational block leaves outputs undefined for some conditions โ€” synthesis infers a latch.
๐Ÿ”
Nested Support
if statements can be nested to any depth. Use begin...end blocks for multiple statements in any branch.

๐Ÿ“ Syntax and Forms

Verilog provides three forms of the if construct, each building on the previous:

Fig 1 โ€” All three if forms: simple, if-else, if-else if-else
// โ”€โ”€ Form 1: Simple if โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
if (condition)
  statement;              // executes only when condition is TRUE
                           // no else โ€” output holds previous value!

// โ”€โ”€ Form 2: if-else โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
if (condition)
  statement_true;         // executes when condition is TRUE
else
  statement_false;        // executes when condition is FALSE

// โ”€โ”€ Form 3: if-else if-else chain โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
if      (cond_1) stmt_1; // highest priority
else if (cond_2) stmt_2;
else if (cond_3) stmt_3;
else             stmt_4; // lowest priority โ€” catches all other cases

// โ”€โ”€ Multi-statement branches: always use begin...end โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
if (rst_n == 1'b0) begin
  count <= 8'h00;
  flags <= 4'b0000;
  valid <= 1'b0;
end else begin
  count <= count + 1;
  valid <= 1'b1;
end

Fig 2 โ€” if-else Decision Diagram

Hardware view: each condition maps to a 2-to-1 MUX layer
cond_1 highest priority stmt_1 TRUE FALSE cond_2 lower priority stmt_2 TRUE else (default)

Condition Evaluation Rules

Condition valueif branch taken?Explanation
1 (or any non-zero)โœ… Yes โ€” TRUEAny non-zero value is treated as logic true
0โŒ No โ€” FALSEZero means false, else branch taken
x (unknown)โŒ No โ€” FALSEUnknown treated as false โ€” else branch taken
z (high-Z)โŒ No โ€” FALSEHigh-impedance treated as false
x and z are treated as false. If your condition contains an uninitialized signal or a floating input, the simulator will take the else branch silently. This can hide bugs during simulation โ€” always ensure condition signals are properly initialized and driven.

โš ๏ธ Latch Avoidance

The most critical rule for if statements in combinational always @(*) blocks: every output must be assigned in every possible execution path. If a path exists where an output isn’t assigned, the synthesis tool infers a latch to hold the previous value.

โŒ Missing else โ€” Latch inferred

always @(*) begin
  if (en)
    out = data;
  // No else!
  // When en=0: out holds
  // โ† synthesis infers latch
end

โœ… Default assignment โ€” No latch

always @(*) begin
  out = 8'h00; // default first
  if (en)
    out = data; // override if en
  // All paths covered
  // โœ… pure combinational
end

โŒ Multi-output: one missed

always @(*) begin
  if (sel) begin
    a = x; b = y;
  end else begin
    a = p;
    // b not assigned!
    // b gets a latch
  end
end

โœ… All outputs defaulted first

always @(*) begin
  a = 8'h00;  // defaults
  b = 8'h00;
  if (sel) begin
    a = x; b = y;
  end else
    a = p;
  // b defaults to 0 โœ…
end
Best practice โ€” default at the top: Assign all outputs to their default values at the very beginning of the combinational always block. Then override selectively with if statements. This guarantees no latches regardless of how many conditions you add later.

๐Ÿ† Priority vs Parallel Decode

The choice between if-else and case has direct hardware implications. Understanding which to use is a key RTL design skill.

Fig 3 โ€” Priority encoder (if-else) vs decoder (case): different hardware

๐ŸŸฃ if-else โ†’ Priority tree (cascaded)

always @(*) begin
  if      (irq3) grant=2'b11;
  else if (irq2) grant=2'b10;
  else if (irq1) grant=2'b01;
  else           grant=2'b00;
end
// irq3 has highest priority โ€”
// even if irq2 also asserted,
// grant still = 2'b11

๐Ÿ”ต case โ†’ Balanced MUX (equal cost)

always @(*) begin
  case (sel)
    2'b00: out=in0;
    2'b01: out=in1;
    2'b10: out=in2;
    2'b11: out=in3;
  endcase
end
// All branches equal priority
// No priority tree โ€” faster
Design needUseReason
Priority interrupt controllerif-elseHighest-numbered IRQ must always win
ALU operation selectorcaseAll opcodes equal โ€” no priority needed
Async reset / sync loadif-elseReset must dominate over all other conditions
State machine output decodecaseStates are mutually exclusive โ€” equal cost MUX
One-hot priority encodeif-else or casezNeed to detect highest-priority set bit

๐Ÿ’ก if / if-else Examples

Fig 4 โ€” Synchronous counter with enable, load, and reset

Priority chain: reset > load > enable > hold
module counter_full (
  input        clk, rst_n,
  input        en, load,
  input  [7:0] load_val,
  output reg [7:0] cnt
);
  always @(posedge clk or negedge rst_n) begin
    if      (!rst_n) cnt <= 8'h00;       // highest: async reset
    else if (load)   cnt <= load_val;   // sync load
    else if (en)     cnt <= cnt + 1;    // count when enabled
    else             cnt <= cnt;         // hold (explicit)
  end
endmodule

Fig 5 โ€” Nested if: 2-bit Arithmetic Logic Unit

Nested if-else demonstrating multi-level condition checking
always @(*) begin
  result = 8'h00;             // default โ€” prevents latches
  carry  = 1'b0;
  ovf    = 1'b0;

  if (mode == 1'b0) begin  // Arithmetic mode
    if (sub) begin
      {carry, result} = a - b;
      ovf = (a[7] != b[7]) && (result[7] != a[7]);
    end else begin
      {carry, result} = a + b;
      ovf = (a[7] == b[7]) && (result[7] != a[7]);
    end
  end else begin               // Logic mode
    if      (op == 2'b00) result = a & b;
    else if (op == 2'b01) result = a | b;
    else if (op == 2'b10) result = a ^ b;
    else                   result = ~a;
  end
end

Fig 6 โ€” if in Sequential Logic: Moore FSM Traffic Light

3-state traffic light FSM using if-else for state transitions
module traffic_fsm (
  input       clk, rst_n,
  output reg [2:0] lights   // [2]=Red [1]=Amber [0]=Green
);
  localparam RED=2'd0, GREEN=2'd1, AMBER=2'd2;
  reg [1:0] state;
  reg [4:0] timer;

  always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
      state <= RED; timer <= 5'd30;
    end else begin
      timer <= timer - 1;
      if (timer == 0) begin   // timer expired โ€” change state
        if      (state == RED)   begin state <= GREEN; timer <= 5'd25; end
        else if (state == GREEN) begin state <= AMBER; timer <= 5'd5;  end
        else                      begin state <= RED;   timer <= 5'd30; end
      end
    end
  end

  // Output decode (combinational)
  always @(*) begin
    lights = 3'b000;
    if      (state == RED)   lights = 3'b100;
    else if (state == GREEN) lights = 3'b001;
    else                     lights = 3'b010;
  end
endmodule

๐Ÿ“Œ assign โ€” deassign Construct

The procedural assign and deassign statements are a special construct used inside procedural blocks to override the normal assignment to a reg variable. They force a register to follow a continuous expression โ€” ignoring any normal procedural assignments โ€” until deassign is called to release it.

Not the same as continuous assign. The module-level assign keyword (data-flow level) is completely different from the procedural assign statement used inside always/initial blocks. The procedural form overrides the reg‘s normal driving.
๐Ÿ”’
Forces a reg
Procedural assign continuously drives a reg with an expression, overriding any clock-triggered assignments to it.
๐Ÿ”“
deassign releases
deassign releases the override. The reg retains its last forced value until a normal procedural assignment updates it.
๐Ÿงช
Testbench Use
Primary use is testbenches โ€” forcing internal signals to specific values for corner case testing without modifying the DUT.
โš ๏ธ
Not Synthesizable
Procedural assign/deassign are not supported by synthesis tools. Use only in simulation and testbenches.
Fig 7 โ€” assign / deassign syntax and operation
// โ”€โ”€ Basic syntax โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
assign   reg_var = expression;  // force reg_var to track expression
deassign reg_var;               // release; reg_var holds last value

// โ”€โ”€ Modelling an asynchronous clear D flip-flop โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
module dff_async_clr (
  input      clk, d, clr,   // clr: active-high async clear
  output reg q
);
  always @(clr) begin
    if (clr) begin
      assign   q = 1'b0;    // force q=0 asynchronously whenever clr=1
    end else begin
      deassign q;            // release q โ€” normal clocking resumes
    end
  end

  always @(posedge clk) begin
    q <= d;                  // normal D capture (overridden when clr=1)
  end
endmodule
Fig 8 โ€” Testbench use: forcing internal signals for debugging
// Force an internal signal to a specific value for N cycles
initial begin
  // Normal stimulus
  #100;

  // Force internal counter to max value to test overflow
  assign dut.counter = 8'hFF;     // hierarchical assign
  @(posedge clk);                   // wait one clock
  deassign dut.counter;             // release โ€” normal operation

  // Verify overflow flag asserted
  @(posedge clk);
  if (!dut.overflow)
    $display("FAIL: overflow not detected");
  else
    $display("PASS: overflow detected correctly");
  $finish;
end

assign / deassign vs force / release

ConstructTargetsOverridesUse case
assign / deassignreg onlyProcedural assignmentsModelling async preset/clear
force / releasereg and wireAll drivers including continuous assignsTestbench debugging, fault injection
Modern alternative: In SystemVerilog flows, force/release and UVM register backdoor access have largely replaced assign/deassign in testbenches. However, assign/deassign remains useful for modelling async clear/preset in behavioral flip-flop models.

๐Ÿ” The repeat Construct

The repeat statement executes a statement or block a fixed, pre-determined number of times. The count is evaluated once at the start โ€” it cannot be changed during execution. This makes repeat ideal for testbench timing (waiting N clock cycles) and simple initialization patterns.

repeat
Keyword
always lowercase
(
open parens
N
Count
constant or expression
evaluated once at start
)
close parens
statement
Body
or beginโ€ฆend block
๐Ÿ“
Fixed Count
The repeat count is evaluated once at the start โ€” a variable count is evaluated then fixed, not re-checked each iteration.
๐Ÿšซ
No Loop Variable
repeat has no built-in loop variable. Use for instead when you need an index.
๐Ÿงช
Testbench Staple
Most common use: repeat(N) @(posedge clk); โ€” wait exactly N clock cycles before the next test step.
โš™๏ธ
Synthesis Caution
repeat with constant count can synthesize if the body is simple combinational logic (loop unrolling). With delays or events, it is simulation-only.

๐Ÿ’ก repeat Examples

Fig 9 โ€” repeat: timing control in testbenches

All common testbench uses of repeat
// โ”€โ”€ Wait N clock cycles โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
repeat(5)  @(posedge clk);          // wait 5 rising edges
repeat(10) @(negedge clk);          // wait 10 falling edges

// โ”€โ”€ Generate N clock pulses โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
repeat(8) begin
  clk = 0; #5;
  clk = 1; #5;                      // 8 complete clock periods
end

// โ”€โ”€ Apply a burst of N data bytes โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
integer byte_idx;
byte_idx = 0;
repeat(4) begin
  @(posedge clk);
  tx_data = payload[8*byte_idx +: 8];
  byte_idx = byte_idx + 1;
end

// โ”€โ”€ Variable count (evaluated once at entry) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
integer n_cycles;
n_cycles = 12;
repeat(n_cycles) @(posedge clk);   // waits exactly 12 cycles
// Changing n_cycles inside the loop has no effect on count

// โ”€โ”€ Synthesizable repeat: bit counting (constant iterations) โ”€โ”€โ”€
integer ones;
reg [7:0] tmp;
ones = 0; tmp = data;
repeat(8) begin
  ones = ones + tmp[0];   // count 1s in LSB
  tmp  = tmp >> 1;          // shift right
end                          // ones = popcount(data)

Fig 10 โ€” Serial data transmitter using repeat

Behavioural UART transmitter task โ€” sends 8 data bits serially
task uart_send_byte;
  input [7:0] byte_in;
  integer bit_idx;
  begin
    // Start bit (low)
    tx = 1'b0;
    @(posedge baud_clk);

    // Data bits โ€” 8 iterations using repeat
    bit_idx = 0;
    repeat(8) begin
      tx = byte_in[bit_idx];    // LSB first
      @(posedge baud_clk);
      bit_idx = bit_idx + 1;
    end

    // Stop bit (high)
    tx = 1'b1;
    @(posedge baud_clk);
    $display("Sent: 0x%02h", byte_in);
  end
endtask

๐Ÿ”„ The for Loop

The for loop is the most versatile loop construct in Verilog. It provides an index variable, an exit condition, and a step expression โ€” all in one compact header. Unlike repeat, you have full access to the loop counter inside the body.

Init
i = 0
Executed once before the loop starts. Sets the starting value of the loop variable.
Condition
i < N
Checked before each iteration. Loop exits when this becomes false.
Step
i = i + 1
Executed after each iteration body. Updates the loop variable.
Fig 11 โ€” for loop syntax and execution flow
// โ”€โ”€ Basic syntax โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
for (init; condition; step) begin
  statement;
end

// โ”€โ”€ Simple 8-iteration example โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
integer i;
for (i=0; i<8; i=i+1) begin
  out[i] = in[7-i];    // bit reversal: out[0]=in[7], out[7]=in[0]
end

// โ”€โ”€ Count down โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
for (i=7; i>=0; i=i-1) begin
  $display("Bit %0d = %b", i, data[i]);
end

// โ”€โ”€ Step by 2 โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
for (i=0; i<16; i=i+2) begin
  even_mem[i/2] = mem[i]; // pack even-addressed bytes
end
Fig 12 โ€” for loop execution flowchart
1
Execute init
i = 0 โ€” runs exactly once before the loop starts
โ†“
2
Evaluate condition
i < N โ€” if FALSE โ†’ exit loop; if TRUE โ†’ execute body
โ†“
3
Execute loop body
All statements between begin and end
โ†“
4
Execute step
i = i + 1 โ€” then go back to step 2

๐Ÿ’ก for Loop Examples

Fig 13 โ€” Memory operations with for loop

Initialise, copy, and search memory arrays
reg [7:0] mem_a[0:255], mem_b[0:255];
integer i, j;

initial begin
  // Zero-initialise all 256 locations
  for (i=0; i<256; i=i+1)
    mem_a[i] = 8'h00;

  // Fill with ascending values
  for (i=0; i<256; i=i+1)
    mem_a[i] = i[7:0];

  // Deep copy: mem_b โ† mem_a
  for (i=0; i<256; i=i+1)
    mem_b[i] = mem_a[i];

  // Linear search: find value 0xAB
  j = -1;
  for (i=0; i<256; i=i+1)
    if (mem_a[i] == 8'hAB) j = i;
  if (j >= 0) $display("Found 0xAB at address %0d", j);
end

Fig 14 โ€” Synthesizable for: CRC calculation

8-bit CRC generator โ€” synthesizable because bound is constant
module crc8 (
  input  [7:0] data_in,
  input  [7:0] crc_in,    // running CRC (feed 8'h00 initially)
  output reg [7:0] crc_out
);
  integer i;
  reg [7:0] tmp;

  always @(*) begin
    tmp = data_in ^ crc_in;

    // 8 iterations โ€” synthesizer unrolls this into 8 XOR stages
    for (i=0; i<8; i=i+1) begin
      if (tmp[7])
        tmp = (tmp << 1) ^ 8'h07;    // CRC-8/SMBUS polynomial
      else
        tmp = (tmp << 1);
    end
    crc_out = tmp;
  end
endmodule

Fig 15 โ€” for loop: Barrel shifter (synthesizable)

Parameterized N-bit barrel left-shifter using for
module barrel_shift #(parameter N=8) (
  input  [N-1:0]       data_in,
  input  [$clog2(N)-1:0] shift_amt,
  output reg [N-1:0]  data_out
);
  integer i;
  always @(*) begin
    data_out = {N{1'b0}};
    for (i=0; i<N; i=i+1) begin
      if (i >= shift_amt)
        data_out[i] = data_in[i - shift_amt];
    end
  end
endmodule
for loop synthesis: A for loop with a constant bound (e.g., i < 8) is synthesized by unrolling โ€” the tool replicates the body N times. This is how complex combinational logic (CRC, parity, popcount) is efficiently described in few lines of RTL. Variable bounds are generally not synthesizable.

๐Ÿ›‘ The disable Construct

The disable statement terminates execution of a named block or named task, skipping any remaining statements within it. It is Verilog’s equivalent of break (for loops) or early return (for tasks) in C, but operates on named scopes rather than loop constructs.

๐Ÿท๏ธ
Requires Named Blocks
disable works only on named begin-end blocks or named tasks. Anonymous blocks cannot be disabled.
โ›”
Early Exit
Jumps past the end of the named block โ€” like break in a loop or return from a function.
๐Ÿงช
Simulation Context
Most useful in testbenches for aborting loops on a found condition, implementing timeouts, and cancelling tasks.
โš ๏ธ
Not Synthesizable
disable is a simulation construct. Synthesis tools do not support it โ€” use it in testbenches only.
Fig 16 โ€” Named block scopes: disable jumps to the end of the named block
begin : my_loop
stmt_1; โ† executes
stmt_2; โ† executes
disable my_loop; โ† jumps past the end of my_loop
stmt_3; โ† SKIPPED
stmt_4; โ† SKIPPED
โ†“ execution resumes here, after end of my_loop

๐Ÿ’ก disable Examples

Fig 17 โ€” break from a for loop using disable

Search memory and stop at first match โ€” disable acts as break
// โ”€โ”€ Linear search: stop at first match โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
integer i, found_addr;
found_addr = -1;

begin : search_loop              // name the block โ€” required for disable
  for (i=0; i<256; i=i+1) begin
    if (mem[i] == target) begin
      found_addr = i;
      disable search_loop;  // โ† break out of the for loop
    end
  end
end   // search_loop ends here โ€” disable jumps to this point

if (found_addr >= 0)
  $display("Found at address %0d", found_addr);
else
  $display("Not found");

Fig 18 โ€” Timeout using disable in a fork-join

Implement a watchdog timeout โ€” disable the waiting block if timeout fires
initial begin : sim_top

  fork

    // Thread 1: wait for DUT to finish
    begin : wait_done
      wait(dut_done);
      $display("DUT completed at t=%0t", $time);
      disable timeout_wdog;   // cancel the timeout
    end

    // Thread 2: watchdog timeout
    begin : timeout_wdog
      #10000;                   // maximum allowed time
      $display("TIMEOUT โ€” DUT did not finish!");
      disable wait_done;       // cancel the wait thread
      $finish;
    end

  join
end

Fig 19 โ€” disable inside a named task

Early return from a task on error condition
task send_packet;
  input [7:0] pkt [0:3];
  integer k;
  begin : send_body

    // Check link is up before sending
    if (!link_up) begin
      $display("ERROR: Link down โ€” aborting send");
      disable send_body;      // early exit โ€” equivalent to return
    end

    // Send 4 bytes
    for (k=0; k<4; k=k+1) begin
      @(posedge clk);
      tx_data  = pkt[k];
      tx_valid = 1'b1;

      // Error injection check mid-packet
      if (error_injected) begin
        $display("Aborting mid-packet at byte %0d", k);
        disable send_body;    // abort the for loop and task
      end
    end
    tx_valid = 1'b0;
    $display("Packet sent OK");

  end   // send_body
endtask

๐Ÿ“‹ Summary and Comparison

A quick reference comparing all five constructs covered in this module:

Construct Purpose Has loop variable? Synthesizable? Primary use
if / if-else Conditional branch โ€” execute one path or another โ€” โœ… Yes RTL priority logic, reset/load/enable chains
assign / deassign Force a reg to track an expression; release it โ€” โŒ No Async clear/preset models, testbench forcing
repeat(N) Execute body exactly N times โŒ No index โš ๏ธ Constant N only Testbench: wait N clocks, N-cycle bursts
for (init; cond; step) Counted loop with full index control โœ… Yes (integer) โš ๏ธ Constant bound only RTL: bit ops, CRC; TB: memory init, search
disable Early exit from a named block or task โ€” โŒ No Testbench: break from loops, timeouts, abort
Fig 20 โ€” Choosing the right construct
?
Do you need conditional execution?
โ†’ Use if / if-else โ€” synthesizable, models priority hardware
โ†“
?
Do you need to force a reg from a procedural block?
โ†’ Use assign / deassign โ€” simulation only, models async control
โ†“
?
Do you need to repeat without an index?
โ†’ Use repeat(N) โ€” compact, testbench-friendly, no loop variable
โ†“
?
Do you need an indexed loop with full counter control?
โ†’ Use for โ€” RTL (const bound) or testbench (variable bound)
โ†“
?
Do you need early exit from a loop or task?
โ†’ Use disable on a named block โ€” simulation only
RTL coding guideline: In synthesizable RTL (always @(*) and always @(posedge clk)), stick to if/else and for with constant bounds. Everything else โ€” assign/deassign, disable, and repeat with event controls โ€” belongs in testbenches and simulation models only.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top