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.

systemverilog
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.

systemverilog
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 + sum

Streaming 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.

systemverilog
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
end
diagram
STREAMING 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.