Two right shifts exist. Logical >> always zero-fills, while arithmetic >>> replicates the sign bit, but only when the left operand is signed.
On an unsigned operand >>> quietly becomes >>. The operator alone never gives sign-preserving division by two.
s is a signed byte holding -16 (8'hF0) and u an unsigned byte holding 8'hF0. What are s >>> 2, s >> 2 and u >>> 2?-4 (8'hFC), 60 (8'h3C) and 60. Only a signed operand gets its sign bit copied by >>>; >> always fills with zeros, and >>> on an unsigned value does the same.
Expression | Operand | Demo result |
|---|---|---|
| signed, -16 ( | -4 ( |
| signed, -16 ( | 60 ( |
| unsigned, 240 ( | 60 (acts like |
| any | Identical: both zero-fill from the right |
Concatenation joins operands MSB-first, and replication repeats a value N times. Every operand needs a known width, so unsized literals are illegal inside.
The result is always unsigned, which poisons signed arithmetic nearby. Concatenation also splits values on the left side of assignments.
// snippet
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 [7:0] b8;
logic [15:0] sext = {{8{b8[7]}}, b8}; // manual sign extension of byte b8
// Left-hand-side split: one 9-bit add into carry plus sum
logic [7:0] a8, c8, sum8;
logic carry8, cin8;
assign {carry8, sum8} = a8 + c8 + cin8;Reduction operators fold every bit of one vector into a single bit:
`|v` is 1 when any bit is set.
`&v` is 1 when every bit is set.
`^v` is 1 for odd parity.
Streaming converts structs and arrays to bit streams. >> keeps slice order, while << reverses it at the chosen granularity.
{<<8{w}} reverses byte order and keeps bit order inside each byte. {<<{w}} with the default 1-bit slice mirrors every bit.
Form (32'h12_34_56_78) | Meaning | Result |
|---|---|---|
| Stream MSB-first | 12 34 56 78 (no change) |
| Reverse bytes | 78 56 34 12 (endianness swap) |
| Reverse halfwords | 56 78 12 34 |
| Reverse bits | Every bit mirrored |
Packing a struct, unpacking it on the left side, and both reversals, run on Verilator:
typedef struct packed {
logic [7:0] cmd;
logic [15:0] addr;
logic [31:0] data;
} pkt_t;
module tb_stream;
pkt_t pkt, back;
logic [55:0] flat;
logic [31:0] w, le, rb;
int errors = 0;
initial begin
pkt = '{cmd: 8'h5A, addr: 16'hBEEF, data: 32'h1234_5678};
flat = {>>{pkt}}; // pack the struct, MSB first
if (flat !== 56'h5A_BEEF_1234_5678) errors++;
{>>{back}} = flat; // unpack on the left side
if (back !== pkt) errors++;
w = 32'h1234_5678;
le = {<<8{w}}; // reverse bytes
rb = {<<{w}}; // reverse bits
$display("w=%h {<<8{w}}=%h {<<{w}}=%h", w, le, rb);
if (le !== 32'h7856_3412) errors++;
if (rb !== 32'h1E6A_2C48) errors++;
if (errors == 0) $display("PASS: struct packed and unpacked, bytes and bits reversed");
else $fatal(1, "FAIL: %0d streaming checks disagreed", errors);
$finish;
end
endmodulew=12345678 {<<8{w}}=78563412 {<<{w}}=1e6a2c48
PASS: struct packed and unpacked, bytes and bits reversedStreaming into a byte queue, q = {>>byte{pkt}}, is the same idea with a queue on the left; the queues lesson in Arrays shows the queue side.
Shifts, reductions and replication face fixed vectors with known answers. A second block packs, splits and reverses bytes the manual way.
module tb_pack_manual;
logic [7:0] cmd, cmd_back;
logic [15:0] addr, addr_back;
logic [31:0] data, data_back;
logic [55:0] flat;
logic [7:0] b0, b1, b2, b3, b4, b5, b6;
logic [31:0] w, le, rb;
int errors = 0;
initial begin
cmd = 8'h5A; addr = 16'hBEEF; data = 32'h1234_5678;
flat = {cmd, addr, data};
if (flat !== 56'h5ABEEF12345678) errors++;
{b0, b1, b2, b3, b4, b5, b6} = flat;
if (b0 !== 8'h5A || b1 !== 8'hBE || b2 !== 8'hEF) errors++;
if (b3 !== 8'h12 || b4 !== 8'h34 || b5 !== 8'h56 || b6 !== 8'h78) errors++;
{cmd_back, addr_back, data_back} = flat;
if (cmd_back !== cmd || addr_back !== addr || data_back !== data) errors++;
w = 32'h1234_5678;
le = {w[7:0], w[15:8], w[23:16], w[31:24]};
if (le !== 32'h7856_3412) errors++;
rb = 32'h0;
for (int i = 0; i < 32; i++) rb[i] = w[31-i];
if (rb !== 32'h1E6A_2C48) errors++;
if (errors != 0) $fatal(1, "FAIL: manual pack or reversal disagrees");
$display("PASS: manual pack splits to 7 ordered bytes and reversals hold");
$finish;
end
endmodulePASS: manual pack splits to 7 ordered bytes and reversals holdmodule tb_shifts;
timeunit 1ns; timeprecision 1ps;
logic signed [7:0] s;
logic [7:0] u, v;
int errors = 0;
initial begin
s = -8'sd16;
u = 8'hF0;
v = 8'b1011_0010;
#1;
if (s >>> 2 !== -8'sd4) errors++;
if (s >> 2 !== 8'd60) errors++;
if (u >>> 2 !== 8'd60) errors++;
if (u << 2 !== 8'hC0) errors++;
if (|v !== 1'b1) errors++;
if (&v !== 1'b0) errors++;
if (^v !== 1'b0) errors++;
if (~|v !== 1'b0) errors++;
if ({4{2'b10}} !== 8'b1010_1010) errors++;
if (errors != 0) $fatal(1, "FAIL: shift or reduction results disagree");
$display("PASS: arithmetic shift keeps -4 while logical shifts give 60");
$finish;
end
endmodulePASS: arithmetic shift keeps -4 while logical shifts give 60v, and what does it return for 8'b1011_0010?^v, the XOR of all bits. It returns 0, because the vector has four ones, an even count.
{<<8{w}} and {<<{w}} for a 32-bit w?{<<8{w}} reverses byte order and keeps bit order inside each byte. {<<{w}} with the default 1-bit slice mirrors every bit.
One way to write it:
module tb_pkt_bytes;
logic [7:0] hdr, chk;
logic [7:0] payload [0:1];
logic [3:0][7:0] wire_bytes; // four bytes as one 32-bit vector, first byte at the top
logic [7:0] h2, p0, p1, c2;
int errors = 0;
initial begin
hdr = 8'h22; // DATA, dest 2, len 2
payload[0] = 8'hA5;
payload[1] = 8'h3C;
chk = hdr ^ payload[0] ^ payload[1]; // check byte: XOR of everything before it
wire_bytes = {>>{hdr, payload[0], payload[1], chk}};
$display("check byte %h, packet on the wire %h", chk, wire_bytes);
if (chk !== 8'hBB || wire_bytes !== 32'h22A5_3CBB) errors++;
{>>{h2, p0, p1, c2}} = wire_bytes; // and back
if (h2 !== hdr || p0 !== 8'hA5 || p1 !== 8'h3C || c2 !== chk) errors++;
if ((h2 ^ p0 ^ p1 ^ c2) !== 8'h00) errors++; // a good packet XORs to zero
if (errors == 0) $display("PASS: check byte BB, packet 22 A5 3C BB packed and unpacked, XOR of all four is 0");
else $fatal(1, "FAIL: %0d packet byte checks disagreed", errors);
$finish;
end
endmodulecheck byte bb, packet on the wire 22a53cbb
PASS: check byte BB, packet 22 A5 3C BB packed and unpacked, XOR of all four is 0Run with Verilator 5.048 (--binary --timing) on 2026-10-18; every bench printed the PASS line shown, with no warnings.
Icarus 13 runs the manual-pack and shift benches too, but has no streaming operators, so the streaming and PacketFIFO byte benches need Verilator.