Part 1 · Language Foundations · Intermediate
Shifts, Concatenation & Streaming
Logical vs arithmetic shifts, reduction operators, replication, and streaming operators for pack/unpack and byte reversal.
Logical vs arithmetic shift
SystemVerilog has two right shifts: >> (logical) always fills vacated bits with zero, while >>> (arithmetic) replicates the sign bit — but only when the left operand is signed . Applied to an unsigned operand, >>> silently degrades to a logical shift, which is the trap: the operator alone does not give you sign-preserving division by two; the operand's declared signedness does. << and <<< are identical (both zero-fill from the right); <<< exists purely for symmetry.
logic signed [7:0] s = -8'sd16; // 8'hF0
logic [7:0] u = 8'hF0; // 240
initial begin
$display("%0d", s >>> 2); // -4 : sign bits replicated (1111_1100)
$display("%0d", s >> 2); // 60 : zero fill (0011_1100) — sign lost!
$display("%0d", u >>> 2); // 60 : operand unsigned → >>> acts like >>
end
// Reduction operators: collapse a vector to 1 bit
logic [7:0] v = 8'b1011_0010;
logic any_set = |v; // OR-reduce → 1
logic all_set = &v; // AND-reduce → 0
logic parity = ^v; // XOR-reduce → even/odd parity
logic none_set = ~|v; // NOR-reduce → 0 (true only if v == 0)Reduction operators (& | ^ ~& ~| ~^) apply one operator between all bits of a single operand, producing one bit. They synthesize to wide gate trees and are the idiomatic way to write any-bit-set, all-bits-set, and parity logic — far clearer than comparing against literals.
Concatenation and replication
Concatenation {a, b, c} joins operands MSB-first into one vector; replication {N{x}} repeats a value N times. Two semantic rules matter: every operand must have a known width (unsized literals are illegal inside a concat), and the result is always unsigned regardless of operand signedness — a frequent source of poisoning in arithmetic expressions (see the signed-arithmetic lesson). Concatenation works on the left of assignments too, splitting a value across multiple targets.
logic [3:0] hi = 4'hA, lo = 4'h5;
logic [7:0] w = {hi, lo}; // 8'hA5
logic [31:0] rep = {4{8'hFF}}; // 32'hFFFF_FFFF
logic [15:0] sext = {{8{b[7]}}, b}; // manual sign extension of byte b
// Left-hand-side concatenation: split one value into pieces
logic c_out;
logic [7:0] sum;
assign {c_out, sum} = a + b + c_in; // 9-bit add split into carry + sumStreaming operators — pack, unpack, reverse
The streaming operators {>>{...}} and {<<{...}} convert between structured data (structs, arrays, queues) and bit streams. >> streams left-to-right (MSB first, the "natural" order); << reverses the order of slices. The optional slice size controls the granularity of reversal: {<<8{data}} reverses byte order while preserving each byte's internal bit order — the canonical one-line endianness swap. In verification, streaming is the standard way to pack a transaction struct into a byte queue for a serial driver and unpack monitored bytes back into a struct.
typedef struct packed {
logic [7:0] cmd;
logic [15:0] addr;
logic [31:0] data;
} pkt_t; // 56 bits total
pkt_t pkt;
logic [55:0] flat;
byte q[$];
initial begin
pkt = '{cmd: 8'h5A, addr: 16'hBEEF, data: 32'h1234_5678};
flat = {>>{pkt}}; // pack struct → bit stream (MSB first)
q = {>>byte{pkt}}; // pack struct → byte queue
// q = {5A, BE, EF, 12, 34, 56, 78}
{>>{pkt}} = flat; // unpack stream → struct (LHS use)
// Byte reversal: classic endianness swap
logic [31:0] le = {<<8{32'h1234_5678}}; // 32'h7856_3412
// Bit reversal of the whole word:
logic [31:0] rb = {<<{32'h8000_0001}}; // 32'h8000_0001 mirrored
endSTREAMING SLICE SIZE CONTROLS REVERSAL GRANULARITY
source word: 32'h12_34_56_78
byte3 byte2 byte1 byte0
{>>{w}} stream MSB-first : 12 34 56 78 (no change)
{<<8{w}} reverse BYTES : 78 56 34 12 (endianness swap)
{<<16{w}} reverse HALFWORDS: 56 78 12 34
{<<{w}} reverse BITS : every bit mirrored
rule: << reverses the order of slices; slice = N bits (default 1)Interview angle
"Swap byte order in one line" — {<<8{word}}: slice size 8 reverses bytes, preserves bits within bytes.
">> vs >>> ?" — arithmetic shift sign-extends only if the operand is signed; on unsigned it is identical to >>.
"Pack a struct into a byte queue" — q = {>>byte{pkt}}; unpack with the streaming expression on the LHS.
Key takeaways
>>> sign-extends only when the left operand is signed — the operator alone is not enough.
Reduction operators collapse vectors to one bit: |v any-set, &v all-set, ^v parity.
Concatenation results are always unsigned and every operand needs a known width.
{<<8{w}} reverses bytes (endianness); {<<{w}} reverses bits; streaming works on both LHS and RHS.
Common pitfalls
Using >>> on an unsigned operand and expecting sign extension — it silently zero-fills.
Putting an unsized literal inside a concatenation — illegal; size every operand.
Confusing {<<{w}} (bit reverse) with {<<8{w}} (byte reverse) — the slice size is the whole answer.
Forgetting concat results are unsigned — embedding one in arithmetic poisons the signed context.