In short: The concatenation operator {a, b} glues vectors into a wider one and the replication operator {N{x}} repeats a pattern N times, which together give you bus assembly, sign extension and bit reversal as pure wiring with zero logic gates.
Why these two operators matter
Almost every datapath you will write in RTL has a moment where signals of different widths must meet: a 12-bit immediate becomes a 32-bit operand, a 16-bit sample is packed with a 4-bit tag, or an FFT block needs its address bits in reversed order. Verilog solves all of these with braces. Because both operators only rearrange or fan out existing nets, synthesis tools such as Synopsys Design Compiler, Cadence Genus and Vivado map them to plain wires. No LUT, no gate, no delay.
That is exactly why interviewers love them. A candidate who writes a multiplexer to reverse bits has missed that the answer costs nothing in silicon.
Concatenation: the basics
Concatenation joins operands from left to right, with the leftmost item landing in the most significant bits. Every operand must have a self-determined width, so unsized constants such as {4, a} are illegal. Write {4'd4, a} instead.
wire [3:0] hi = 4'hA;
wire [3:0] lo = 4'h5;
wire [7:0] byte_w = {hi, lo}; // 8'hA5
wire [15:0] bus16 = {byte_w, 8'h00}; // 16'hA500
wire [8:0] sum_c = {1'b0, hi} + {1'b0, lo}; // keep the carry
Key fact: The result width of a concatenation is the sum of its operand widths, and the result is always unsigned, even when every operand is declared signed.
Concatenation also works on the left-hand side of an assignment, which is how you split a wide result in one statement: {carry, sum} = a + b; assigns the low bits to sum and the overflow bit to carry. The addition is evaluated at the width of the widest operand on either side, so declare carry and sum so that the total is at least one bit wider than a and b. If you are unsure how nets and variables differ in such assignments, read our guide on reg vs wire in Verilog.
Replication: repeating a pattern
The syntax is {count{expr}}, and count must be a non-negative constant known at elaboration time, such as a literal or a parameter. A variable count is illegal, so {n{x}} with a runtime n will not compile. Replication is the shortest way to build masks and fills:
wire [31:0] all_ones = {32{1'b1}};
wire [15:0] pattern = {4{4'b1010}}; // 16'hAAAA
wire [7:0] gated = data & {8{en}}; // enable mask
The last line is a classic trick: {8{en}} turns a single enable bit into an 8-bit AND mask, replacing a verbose conditional. Note that replication with a count of zero is only allowed inside a larger concatenation, and the whole expression must still have a positive width.
Key fact: {8{en}} and {8{1'b1}} differ only in fan-out. The first drives eight AND gates from one net, which matters for timing when the width grows to 64 or 128 bits.
Sign extension, done correctly
To widen a two's-complement value you must copy its sign bit into the new upper bits. With replication that is one line:
wire [11:0] imm12;
wire [31:0] imm32 = {{20{imm12[11]}}, imm12};
This is the exact expression used for I-type immediates in RISC-V decoders. Check the arithmetic: imm12 = 12'hFF6 is -10. The top bit is 1, so twenty ones are prepended, giving 32'hFFFFFFF6, which is still -10. If you used zero-extension, {20'b0, imm12}, you would get +4086, a silent functional bug that passes simple positive-value tests. For a deeper treatment of signed arithmetic rules, see our article on sign extension in Verilog.
Bit reversal and field swaps
Reversing a vector is a rewiring exercise. For an 8-bit input a:
wire [7:0] rev = {a[0],a[1],a[2],a[3],a[4],a[5],a[6],a[7]};
For parameterised widths a generate loop with assign rev[i] = a[W-1-i]; does the same job. FFT address generation, CRC engines and serial-to-parallel converters use this constantly. Byte swapping for endianness is the same idea at coarser granularity: {a[7:0], a[15:8], a[23:16], a[31:24]}.
Concatenation in motion: shift registers
The most common sequential use is a shift register. Instead of looping, one line shifts everything and inserts a new bit:
always @(posedge clk)
sh <= {sh[6:0], din}; // shift left, din enters at bit 0
Pair this with non-blocking assignment, as explained in blocking vs non-blocking Verilog, so every bit samples its neighbour's old value. The animation below shows four bits marching left on each clock while a new bit enters.
Common pitfalls and what to remember
| Mistake | Consequence | Fix |
|---|---|---|
Unsized constant such as {1, a} | Compile error | Use {1'b1, a} |
| Zero-extending a signed value | Wrong negative numbers | Replicate the MSB |
| Width mismatch on assignment | Silent truncation or padding (lint warns) | Check every operand width |
| Variable replication count | Elaboration error | Use a parameter |
Run a lint pass (Synopsys SpyGlass, Cadence HAL or the Siemens Questa lint flow) early. Width-mismatch warnings are the usual symptom of a wrong brace. When you want to see these operators inside larger hierarchies, our notes on structural modeling in Verilog show how port connections use the same concatenation syntax, and the Verilog data types guide clarifies how signedness interacts with them. Teams at ChipXpert drill these idioms in hands-on RTL labs.
Key fact: Concatenation and replication consume zero gates. If a reversal or extension shows area in your synthesis report, the logic came from somewhere else, usually a mux or a mis-sized operand.
Want to practise these patterns on real FPGA and ASIC flows? Explore the guided labs in our CourseTron courses and build your Verilog skills step by step.
Frequently asked questions
Is concatenation signed or unsigned?
Always unsigned, regardless of the operands. If you need a signed result, wrap it with $signed({...}) after building it.
Can I use a variable as the replication count?
No. The count must be a constant expression resolved at elaboration, such as a parameter or literal. Use a loop in a generate block or a mask for runtime control.
Does bit reversal cost hardware?
No. It is crossed routing only. Placement may add some wire delay on an FPGA, but there is no logic element involved.
What is the difference between {a,b} and a+b?
Concatenation packs bits side by side into a wider word, while addition computes arithmetic sum at the operand width. They solve different problems.