Skip to content

Concatenation, Replication & Conditional

{}, {N{}}, ?: — bus assembly, field packing, X-merge behavior.

Module 4 · Page 4.7

Three Operators That Do Most of the Bus Work

Engineers coming from C reach for bit-shifts and OR operations to pack fields into a word. In SystemVerilog you write {hdr, payload, crc} and it is done — cleaner, self-documenting, and impossible to misalign. That is concatenation. The result width is the exact sum of the operand widths. No padding surprises, no sign-extension ambiguity.

Replication solves the copy-paste problem. Sign-extending a 4-bit value to 32 bits the long way means writing the sign bit twenty-eight times by hand. {{28{narrow[3]}}, narrow} does it in eight characters and survives a width refactor automatically.

The conditional cond ? a : b is a 2:1 mux as an expression. It works anywhere an expression is valid — inside assign, inside constraints, inside function return statements. The part that trips people up: when cond is X or Z, the result is not a blanket X. It is a bitwise merge — bits that agree across both branches survive clean; bits that disagree become X. So x ? 8'hFF : 8'hFF gives 8'hFF with no X at all. But x ? 8'hF0 : 8'hFF gives 8'hFX — upper nibble clean, lower nibble X.

What Are These Things Actually Doing?

Concatenation — Physical Bus Assembly

Think of each signal as a bundle of wires. Concatenation physically connects those bundles end-to-end. The leftmost operand occupies the most significant positions; the rightmost occupies the least significant. The result width is exactly the sum. There is no implicit padding, no sign extension — you get exactly the wires you joined.

In protocol work, this is how you assemble a frame: {start_bit, addr[6:0], rw, data[7:0], parity} — nine fields snapped together in one expression with no possibility of mis-shifting a field by one bit.

Replication — Structured Repetition

{N{expr}} is syntactic sugar for writing expr N times inside a concatenation. {4{2'b10}} is exactly {2'b10, 2'b10, 2'b10, 2'b10} = 8'b10101010. N must be a constant expression — a literal, a parameter, or a localparam. Never a variable.

Conditional — Inline Mux

cond ? expr_true : expr_false evaluates both branches syntactically and selects one at runtime. In synthesis it becomes a 2:1 multiplexer. Nested ternaries build priority mux chains. Both branches are evaluated by the simulator on every call — there is no short-circuit evaluation like in C's &&.

  • {a, b, c} — Concatenation — Joins signals MSB-first. Result width = sum of all parts. No padding. Every operand needs a defined compile-time width.
  • {N{expr}} — Replication — Repeats expr exactly N times. N must be constant. Used for sign extension, test pattern generation, zero/one padding.
  • cond ? a : b — Conditional — Selects a when cond is true, b when false. Maps to a 2:1 mux in RTL. X/Z condition triggers bitwise merge, not blanket X.

Syntax, Rules, and What the Compiler Won't Always Tell You

SystemVerilog — All Three Operator Syntax Forms
// ── CONCATENATION ────────────────────────────────────────────────────
// result = {expr1, expr2, ..., exprN}
// Width  = width(expr1) + width(expr2) + ... + width(exprN)
 
logic [3:0] a = 4'hA;
logic [3:0] b = 4'hB;
logic [7:0] ab = {a, b};         // 8'hAB — a in upper 4 bits, b in lower 4
 
// Nested concatenation — braces flatten automatically
logic [11:0] abc = {a, {b, 4'hC}};  // same as {a, b, 4'hC} = 12'hABC
 
// ILLEGAL: unsized literal has no defined width
// logic [11:0] bad = {8, a, b};   // ERROR — use 4'd8 instead
 
// ── REPLICATION ──────────────────────────────────────────────────────
// result = {count{expr}}   — count must be a constant expression
 
logic [7:0] pat  = {4{2'b10}};     // 8'b10101010 — replicate 2'b10 four times
logic [7:0] ones = {8{1'b1}};      // 8'hFF
 
parameter PAD = 4;
logic [11:0] padded = {a, {PAD{1'b0}}}; // {4'hA, 4'h0} = 12'hA00 — uses parameter: legal
 
// Sign extension using replication of MSB
logic signed [3:0] s4  = -3;              // 4'b1101
logic signed [7:0] s8  = {{4{s4[3]}}, s4};  // 8'b1111_1101 = -3
 
// ── CONDITIONAL ──────────────────────────────────────────────────────
// result = condition ? expr_true : expr_false
// Result width is determined by context (e.g., LHS width)
// Both branches are syntactically evaluated every time (no short-circuit)
 
logic       sel = 1'b1;
logic [7:0] mux_out = sel ? 8'hFF : 8'h00;  // 8'hFF
 
// Nested conditional — priority mux chain
logic [1:0] s;
logic [7:0] y = (s == 2'b00) ? a :
(s == 2'b01) ? b : 8'hFF;  // 2-level priority mux
OperatorKey constraintResult widthX/Z behavior
{a, b, c}All operands must have defined widths at compile timeSum of all operand widths — exactX/Z values pass through unchanged in their bit positions
{N{expr}}N must be a constant expression (literal/parameter/localparam). N=0 is illegal.N × width(expr)X/Z in expr replicated N times
c ? a : bNone — both branches can be any expressionDetermined by context; narrower branch zero-extendedX/Z condition → bitwise merge of a and b

Bit-Level Assembly — Seeing the Pieces Connect

Concatenation: Field Positions in the Result

Values: a = 4'hA (1010), b = 4'h5 (0101), c = 4'h3 (0011). Expression: {a, b, c} = 12-bit result.

OperandWidthBinaryOccupies result bitsComment
a41010[11:8]MSB side — leftmost operand = highest bits
b40101[7:4]Middle
c40011[3:0]LSB side — rightmost operand = lowest bits
{a, b, c}121010_0101_0011[11:0]= 12'hA53

Replication: {N{expr}} Expansion

ExpressionNexpr bitsExpanded formResult
{2{4'b1010}}24{4'b1010, 4'b1010}8'b1010_1010
{3{2'b01}}32{2'b01, 2'b01, 2'b01}6'b010101
{4{1'b1}}41{1'b1, 1'b1, 1'b1, 1'b1}4'b1111
{4{sneg[3]}}, sneg}4 (sign)1MSB copied 4 times + original 4-bit8-bit sign-extended value

Conditional X-Merge: Bit-by-Bit Behavior

Using val_a = 8'hF0 (1111_0000) and val_b = 8'hFF (1111_1111). When condition is X, the simulator merges both branches bit-by-bit:

Bitval_a bitval_b bitAgree?Result (cond=1)Result (cond=0)Result (cond=X)
[7]11Yes111
[6]11Yes111
[5]11Yes111
[4]11Yes111
[3]01No01X
[2]01No01X
[1]01No01X
[0]01No01X
Result8'hF08'hFF8'bFFFF_XXXX

Upper nibble F appears in both branches identically — it survives clean even with an X condition. Lower nibble differs (0 in F0, 1 in FF) — it collapses to X. This is why partial X on a bus often points to a conditional with an unknown select signal.

Code Examples — Field Packing to Frame Building

Example 1 — Beginner: All Three Operators

Example 1 — Core Operator Basics
module tb_core_operators;
 
logic [3:0] a    = 4'hA;          // 1010
logic [3:0] b    = 4'hB;          // 1011
logic [3:0] c    = 4'hC;          // 1100
logic       sel;
 
logic signed [3:0] sneg = -3;     // 4'b1101
logic signed [7:0] sext;
 
initial begin
 
// ── Concatenation ──────────────────────────────────────────────
$display("[Concat] {a,b}      = 0x%02h  (8-bit: AB)",  {a, b});
$display("[Concat] {a,b,c}    = 0x%03h  (12-bit: ABC)", {a, b, c});
 
// ── Replication ────────────────────────────────────────────────
$display("[Replic] {4{a}}     = 0x%04h  (16-bit: AAAA)", {4{a}});
$display("[Replic] {2{a,b}}   = 0x%04h  (16-bit: ABAB)", {2{a, b}});
$display("[Replic] {4{2'b10}} = %08b",              {4{2'b10}});
 
// ── Sign extension via replication ─────────────────────────────
sext = {{4{sneg[3]}}, sneg};    // replicate MSB 4×, append original 4 bits
$display("[SignExt] -3: 4'b%04b → 8'b%08b (%0d)", sneg, sext, $signed(sext));
 
// ── Conditional ────────────────────────────────────────────────
sel = 1'b1;
$display("[Cond] sel=1 → 0x%h", sel ? a : b);   // A
sel = 1'b0;
$display("[Cond] sel=0 → 0x%h", sel ? a : b);   // B
 
$finish;
end
 
endmodule

Expected output:

Simulation Output
[Concat] {a,b}      = 0xAB  (8-bit: AB)
[Concat] {a,b,c}    = 0xABC  (12-bit: ABC)
[Replic] {4{a}}     = 0xAAAA  (16-bit: AAAA)
[Replic] {2{a,b}}   = 0xABAB  (16-bit: ABAB)
[Replic] {4{2'b10}} = 10101010
[SignExt] -3: 4'b1101 → 8'b11111101 (-3)
[Cond] sel=1 → 0xA
[Cond] sel=0 → 0xB

Example 2 — Intermediate: 32-bit Register Field Pack / Unpack

Example 2 — STATUS Register Field Packing
// STATUS register layout:
// [31:24] = err_code  (8 bits)   [23:16] = flags    (8 bits)
// [15:8]  = burst_len (8 bits)   [7:0]   = state    (8 bits)
 
module tb_reg_packing;
 
logic [7:0]  err_code  = 8'hAB;
logic [7:0]  flags     = 8'b1010_0011;
logic [7:0]  burst_len = 8'd15;
logic [7:0]  state     = 8'h05;
logic [31:0] status;
 
initial begin
 
// ── Pack all 4 fields into one 32-bit word ─────────────────────
status = {err_code, flags, burst_len, state};
$display("Status  = 0x%08h", status);            // 0xABA30F05
 
// ── Unpack by slicing ──────────────────────────────────────────
$display("err_code  = 0x%02h", status[31:24]);   // AB
$display("flags     = 8'b%08b", status[23:16]);  // 10100011
$display("burst_len = %0d",     status[15:8]);   // 15
$display("state     = 0x%02h",  status[7:0]);    // 05
 
// ── Read-modify-write: update only the state field ─────────────
// Replace lower 8 bits, keep upper 24 unchanged
status = {status[31:8], 8'hFF};
$display("Updated = 0x%08h", status);           // 0xABA30FFF
 
$finish;
end
 
endmodule

Expected output:

Simulation Output
Status  = 0xABA30F05
err_code  = 0xAB
flags     = 8'b10100011
burst_len = 15
state     = 0x05
Updated = 0xABA30FFF

Example 3 — Verification: Scoreboard Transaction Packing

Example 3 — Scoreboard Using Concatenation
// AXI-like beat: {tid[3:0], data[15:0]} = 20-bit frame
 
module tb_scoreboard;
 
// Build expected beat from known field values
function automatic logic [19:0] pack_beat(
input logic [3:0]  tid,
input logic [15:0] payload
);
return {tid, payload};
endfunction
 
// Parse and validate received beat
task automatic check_beat(
input logic [19:0] received,
input logic [3:0]  exp_tid,
input logic [15:0] exp_data
);
logic [19:0] expected  = pack_beat(exp_tid, exp_data);
logic [3:0]  got_tid   = received[19:16];
logic [15:0] got_data  = received[15:0];
 
if (received !== expected)
$error("MISMATCH got=0x%05h exp=0x%05h [tid:%0h→%0h data:%04h→%04h]",
received, expected, exp_tid, got_tid, exp_data, got_data);
else
$display("PASS  beat=0x%05h  tid=0x%h  data=0x%04h",
received, got_tid, got_data);
endtask
 
initial begin
check_beat(20'hA_CAFE, 4'hA, 16'hCAFE);  // PASS
check_beat(20'hB_CAFE, 4'hA, 16'hCAFE);  // MISMATCH: tid corrupted
check_beat(20'hA_1234, 4'hA, 16'hCAFE);  // MISMATCH: data wrong
$finish;
end
 
endmodule

Expected output:

Simulation Output
PASS  beat=0xACAFE  tid=0xA  data=0xCAFE
MISMATCH got=0xBCAFE exp=0xACAFE [tid:A→B data:CAFE→CAFE]
MISMATCH got=0xA1234 exp=0xACAFE [tid:A→A data:CAFE→1234]

Example 4 — Corner Cases: X Merge, Nested Replication, Frame Assembly

Example 4 — Corner Cases
module tb_corner_cases;
 
logic [7:0]  val_a = 8'hF0;   // 1111_0000
logic [7:0]  val_b = 8'hFF;   // 1111_1111
logic [7:0]  result;
logic        cond;
logic [15:0] pkt;
 
initial begin
 
// ── Corner 1: X condition — bitwise merge, not blanket X ──────
cond = 1'bx;
 
result = cond ? 8'hAA : 8'hAA;          // identical branches
$display("X cond, same values  : 0x%02h", result);  // AA — no X!
 
result = cond ? val_a : val_b;             // F0 vs FF
$display("X cond, F0 vs FF     : %08b",   result);  // 1111_xxxx
 
result = cond ? 8'hFF : 8'h00;           // all bits differ
$display("X cond, FF vs 00     : %08b",   result);  // xxxxxxxx
 
// ── Corner 2: Nested replication ──────────────────────────────
$display("{3{4'b0101}}         : %012b", {3{4'b0101}});  // 010101010101
$display("{2{3{2'b01}}}        : %012b", {2{3{2'b01}}});  // 010101010101
 
// ── Corner 3: Concat + Replication = Frame Builder ────────────
// Frame: 8-bit sync header + 4 pairs of alternating bits
pkt = {8'hA5, {4{2'b10}}};
$display("Frame (sync+pattern) : 0x%04h", pkt);  // 0xA5AA
 
// ── Corner 4: Z condition — identical behavior to X ───────────
cond = 1'bz;
result = cond ? val_a : val_b;
$display("Z cond, F0 vs FF     : %08b",   result);  // 1111_xxxx
 
$finish;
end
 
endmodule

Expected output:

Simulation Output
X cond, same values  : 0xAA
X cond, F0 vs FF     : 1111xxxx
X cond, FF vs 00     : xxxxxxxx
{3{4'b0101}}         : 010101010101
{2{3{2'b01}}}        : 010101010101
Frame (sync+pattern) : 0xA5AA
Z cond, F0 vs FF     : 1111xxxx

Simulation Behavior — Width Rules and the X You Didn't Expect

Concatenation Width Is Self-Determined

A concatenation expression has a self-determined width equal to the sum of its operand widths. It does not grow to match its context. This matters when you assign a narrow concatenation to a wider variable — the lower bits of the destination receive the concatenation value, and the upper bits are zero-padded by the assignment, not by the concatenation itself. Conversely, if the concat is wider than the destination, the upper bits of the concat are silently dropped.

Width Rules — Concat Does Not Auto-Widen
logic [3:0] a = 4'hF;
logic [3:0] b = 4'hF;
logic [7:0] result8;
logic [9:0] result10;
 
result8  = {a, b};          // {4'hF, 4'hF} = 8'hFF — exact fit
result10 = {a, b};          // 8'hFF assigned to 10-bit → zero-extended: 10'h0FF
 
// Truncation: concat wider than destination
logic [5:0] narrow;
narrow = {a, b};            // 8'hFF → truncated to 6'b11_1111 = 6'h3F — MSBs lost!
 
// Safe approach: always match concat width to destination width
logic [7:0] pkt;
pkt = {4'h0, a};             // explicit zero-pad to reach 8 bits: 8'h0F
$display("pkt = 0x%02h", pkt);  // 0F

Synthesis: What Hardware Do These Generate?

ExpressionHardware generatedArea / TimingNotes
{a, b}Pure wiring — connect bus A to upper outputs, bus B to lowerZero gates, zero delayMost efficient operation in SV — just re-routes wires
{N{expr}} constant NN copies of the expr wires connected in parallelZero gates — only fanout loadSynthesis duplicates the net; no logic added
cond ? a : b2:1 multiplexer1 MUX cell × bus widthNested ternary = priority MUX chain (use case for equal priority)
Nested ternary 4-way3 cascaded 2:1 MUXes3× MUX delay — timing-critical if deepSynthesis may restructure; check timing report

Where You'll Use These in Real Projects

Real Verification Usage Patterns
// ── 1. MONITOR: parse incoming AHB address phase ──────────────────
// Raw bus: {haddr[31:0], htrans[1:0], hsize[2:0], hwrite} = 36 bits
logic [35:0] raw_beat;
logic [31:0] mon_addr  = raw_beat[35:4];
logic [1:0]  mon_trans = raw_beat[3:2];
logic [2:0]  mon_size  = {2'b00, raw_beat[1]};   // zero-extend 1-bit size field
logic        mon_write = raw_beat[0];
 
// ── 2. SCOREBOARD: build expected output for comparison ───────────
function automatic logic [35:0] build_ahb(
input logic [31:0] addr, input logic [1:0] trans,
input logic        wr,   input logic [1:0] size
);
return {addr, trans, size, wr};
endfunction
 
// ── 3. DRIVER: conditional data selection ────────────────────────
logic [7:0] drive_data = is_error_injection ? err_pattern : normal_data;
 
// ── 4. CONSTRAINT: conditional distribution ──────────────────────
// In a class:
//   rand logic [31:0] addr;
//   constraint addr_align { addr == {addr[31:2], 2'b00}; } // force 4-byte align
 
// ── 5. ASSERTION: check field value in packed word ────────────────
// Check that the opcode field [15:12] in a 16-bit command is never 0
// assert property (@(posedge clk) cmd_valid |-> cmd[15:12] != 4'h0);
 
// ── 6. COVERAGE: combine fields for cross coverage ────────────────
// coverpoint {prot_type, burst_len} covers all combinations
// cross {wr_rd, beat_count} cross-covers read/write vs burst depth
 
// ── 7. ERROR INJECTION: flip a specific bit using concat ──────────
function automatic logic [31:0] inject_bit_error(
input logic [31:0] data,
input int           bit_pos
);
// flip bit at bit_pos using conditional rebuild
logic [31:0] mask = 32'h1 << bit_pos;
return data ^ mask;
endfunction

Bugs Engineers Actually Hit — With Waveform Thinking

Bug 1 — Unsized Literal in Concatenation

Bug 1 — Unsized Literal: Compile Error
logic [7:0] data = 8'hAB;
logic [11:0] pkt;
 
// BUGGY: 5 is an unsized integer — no defined width
pkt = {4, data};    // COMPILE ERROR: unsized literal in concatenation
 
// FIXED: always size the literal explicitly
pkt = {4'd4, data};    // 4-bit value 4 = 4'b0100, then 8'hAB → 12'h4AB
 
// If you want to prepend 4 zero bits (zero-extend data to 12 bits):
pkt = {4'h0, data};    // 12'h0AB
 
// Common alternate: use streaming assignment or cast
pkt = {12'(data)};     // zero-extends data to 12 bits — same as {4'h0, data}

Bug 2 — Width Miscalculation: MSBs Silently Truncated

Bug 2 — Concat Width Mismatch: Silent MSB Drop
logic [3:0] opcode = 4'hA;
logic [2:0] mode   = 3'b110;   // only 3 bits wide
logic [7:0] cmd;
 
// BUGGY: packing 7 bits (4+3) into 8-bit cmd
// Concat result = 7'b1010_110, zero-padded to 8 bits → cmd = 8'b0_1010_110 = 8'h56
// Engineer expects opcode in bits [7:4] but it ends up in bits [6:3] — off by one!
cmd = {opcode, mode};
 
// CORRECT: ensure total operand widths equal destination width
logic [3:0] mode4 = {1'b0, mode};  // widen mode to 4 bits first
cmd = {opcode, mode4};               // 4+4 = 8 bits: 8'hA6
 
// Or pad explicitly inside the concat:
cmd = {opcode, 1'b0, mode};          // 4+1+3 = 8: same result, intent is clear

Bug 3 — X on Conditional Condition at Simulation Startup

Bug 3 — Uninitialized Condition Propagates X Through Mux
// RTL: output mux gated by 'valid' signal
logic        valid;           // not initialized — starts as X at time 0
logic [7:0] data_in = 8'hFF;
logic [7:0] data_out;
 
assign data_out = valid ? data_in : 8'h00;
// At time 0: valid=X, data_in=8'hFF, 8'h00
// Bit merge: 8'hFF vs 8'h00 → all bits differ → data_out = 8'hXX
// Every downstream consumer of data_out is now also X
 
// FIX 1: initialize in declaration
logic valid = 1'b0;          // data_out starts at 8'h00 — clean
 
// FIX 2: asynchronous reset drives known state
always_ff @(posedge clk or posedge rst)
if (rst) valid <= 1'b0;
else     valid <= next_valid;
 
// FIX 3: testbench — drive before time 0 using initial block
initial begin
valid = 1'b0;     // immediate assignment — active before any clock edge
@(posedge clk);
valid = 1'b1;
end

Bug 4 — Variable Replication Count

Bug 4 — Replication Count Must Be Constant
int           pad_n = 8;      // runtime variable
logic [7:0]  data  = 8'hAB;
logic [15:0] padded;
 
// BUGGY: pad_n is a variable — ILLEGAL, will not compile
// padded = {data, {pad_n{1'b0}}};   // ERROR
 
// FIXED option 1: use a parameter (most common solution)
parameter PAD_BITS = 8;
padded = {data, {PAD_BITS{1'b0}}};       // legal: PAD_BITS is a compile-time constant
 
// FIXED option 2: if runtime width is genuinely needed, use shift
padded = {8'h00, data} << pad_n;         // variable shift — legal, generates barrel shifter
// Note: semantics differ from padding — understand what you need
 
// FIXED option 3: use localparam in a generate block
// for parameterized modules — set pad width via module parameter

Bug 5 — Conditional Branch Width Asymmetry

Bug 5 — Narrow Branch Zero-Extended, Not Sign-Extended or Replicated
logic [7:0] wide_val   = 8'hFF;
logic [3:0] narrow_val = 4'hF;    // 4'b1111
logic [7:0] result;
logic        sel = 1'b0;
 
// BUGGY EXPECTATION: engineer expects sel=0 → result = 8'hFF (all ones)
result = sel ? wide_val : narrow_val;
// narrow_val=4'hF in an 8-bit context → zero-extended to 8'h0F — NOT 8'hFF!
$display("result = 0x%02h", result);   // 0x0F — upper nibble is ZERO
 
// FIX 1: if intent is to replicate the nibble to fill 8 bits:
result = sel ? wide_val : {2{narrow_val}};  // {4'hF, 4'hF} = 8'hFF
 
// FIX 2: if intent is sign extension (narrow is signed):
logic signed [3:0] s_narrow = 4'hF;   // -1 in signed 4-bit
result = sel ? wide_val : $unsigned({{4{s_narrow[3]}}, s_narrow});
// s_narrow[3]=1 → sign extend: 8'b1111_1111 = 8'hFF
 
// FIX 3: always match branch widths explicitly to make intent clear
result = sel ? wide_val : {4'h0, narrow_val};  // explicit zero-extend: 8'h0F

Interview Questions

Beginner Level

Q1: What is the result width of {4'b1010, 3'b101, 1'b1}? 8 bits (4 + 3 + 1 = 8). The result is 8'b1010_1011. The concatenation width is always the exact sum of all operand widths — no padding or extension is added. Q2: Can you write {0, data} to prepend a zero bit? No. 0 is an unsized integer literal with no defined bit width. SystemVerilog requires every operand in a concatenation to have a defined compile-time width. Use 1'b0 instead: {1'b0, data}. Q3: What does {2{4'b1010}} evaluate to?8'b1010_1010. The replication {2{4'b1010}} is exactly equivalent to {4'b1010, 4'b1010}. Result width = 2 × 4 = 8 bits.

Intermediate Level

Q4: What is the result of 1'bx ? 8'hF0 : 8'hFF?8'bFFFF_XXXX. When the condition is X, the simulator performs a bitwise merge: 8'hF0 = 8'b1111_0000 vs 8'hFF = 8'b1111_1111. Bits [7:4] are 1 in both — result is 1. Bits [3:0] are 0 vs 1 — they disagree — result is X. It is not a blanket 8'hXX. Q5: How do you swap the upper and lower bytes of a 16-bit word in one expression?swapped = {word[7:0], word[15:8]}; Concatenate the lower byte in the upper position and the upper byte in the lower position. No intermediate variable needed. This is one of the clearest demonstrations of concatenation's power — swapping byte lanes in a single, self-documenting expression.

Experienced Engineer Level

Q6: A scoreboard receives a 20-bit AXI beat and finds partial X on the data field [15:0] but the tid field [19:16] is clean. What is the most likely cause? The DUT has an output mux (conditional expression) on the data path whose select signal is X. Because X-merge is bitwise, bits that agree between the mux branches stay clean — if the tid field happens to carry the same value in both branches (or comes from a different path without a mux), it stays clean. Trace the signal driving the select of the output mux for the data path back to find the undriven or uninitialized signal. Q7: What hardware does a nested ternary chain generate in synthesis, and when should you prefer case? A nested ternary generates a priority MUX chain — the first condition is checked first, and subsequent conditions only matter if earlier ones fail. This creates a series of cascaded 2:1 MUXes with increasing delay toward the final output. Use case (or unique case) when all select values are mutually exclusive and equal priority — synthesis can implement them as a parallel decoder and a single-level MUX tree, which is faster and uses less area. Nested ternary is correct for priority encoding; case is correct for 1-of-N selection.

Best Practices & Coding Guidelines

  • Always size concatenation literals — Never use unsized integers in {}. Use 4'd5 not 5. Catches errors at compile time instead of simulation.
  • Comment field positions — When packing protocol frames, add a comment block documenting which bits hold which field. Future-you and reviewers will thank you when the spec changes.
  • Match branch widths in conditionals — Always make both ?: branches the same width explicitly. Relying on implicit zero-extension of the narrow branch invites silent width bugs.
  • Use parameters for replication counts — Hardcoding {28{bit}} in a 32-bit design creates a maintenance hazard. Use parameter DATA_W = 32 and write {(DATA_W-4){bit}}.
TaskCorrect approachCommon mistake
Prepend zero bits to widen a bus{4'h0, narrow_bus}{4, narrow_bus} — unsized, compile error
Sign-extend a signed value{{N{val[MSB]}}, val} or $signed(val) in wider context{N{val}} — replicates the whole value, not just the sign bit
Read-modify-write a register field{reg[31:8], new_byte}Shift-and-mask: error-prone, harder to read
Byte swap a 32-bit word{w[7:0], w[15:8], w[23:16], w[31:24]}Multiple shift+OR operations — less readable, same result
Generate N-wide all-ones mask{N{1'b1}} or '1~0 — depends on expression width context, can behave unexpectedly
Conditional with default in RTLassign y = en ? data : '0;Missing default in always block → inferred latch

Summary

Concatenation, replication, and conditional are the three operators you reach for when assembling or decomposing data. Concatenation does the structural work — no other operator packs a protocol frame as cleanly. Replication eliminates repetition and keeps parameterized sign-extension correct even when widths change. The conditional is a mux — write it in RTL where you would draw one, and use it in testbenches for inline value selection.

  • Every operand in {} must have a defined compile-time width. Unsized literals cause compile errors. Always use sized literals: 4'd5, 8'hFF, 1'b0.
  • Replication count N must be a constant. Use a parameter or localparam. If you need runtime repetition, use a variable shift instead — and understand that the semantics differ.
  • X/Z condition → bitwise merge, not blanket X. Bits that agree across both branches stay clean. Bits that disagree become X. Partial X on a bus almost always traces back to a conditional with an unknown select signal.
  • Concatenation and replication are free in hardware — pure wiring, zero gates. Only the conditional generates actual logic (a MUX).
  • Match branch widths explicitly in conditionals. Implicit zero-extension of a narrow branch is valid SV but creates subtle value bugs when the engineer expected sign-extension or replication.