What is `+:` and `-:`?

5.2.1 Vector bit-select and part-select addressing

Bit-selects extract a particular bit from a vector net, vector reg, integer, or time variable, or parameter. The bit can be addressed using an expression. If the bit-select is out of the address bounds or the bit-select is x or z , then the value returned by the reference shall be x . A bit-select or part-select of a scalar, or of a variable orparameter of type real or realtime, shall be illegal.

Several contiguous bits in a vector net, vector reg, integer, or time variable, or parameter can be addressed and are known as part-selects. There are two types of part-selects, a constant part-select and an indexed part-select. A constant part-select of a vector reg or net is given with the following syntax:

vect[msb_expr:lsb_expr]

Both msb_expr and lsb_expr shall be constant integer expressions. The first expression has to address a more significant bit than the second expression.

An indexed part-select of a vector net, vector reg, integer, or time variable, or parameter is given with the following syntax:

reg [15:0] big_vect;
reg [0:15] little_vect;
big_vect[lsb_base_expr +: width_expr]
little_vect[msb_base_expr +: width_expr]
big_vect[msb_base_expr -: width_expr]
little_vect[lsb_base_expr -: width_expr]

The msb_base_expr and lsb_base_expr shall be integer expressions, and the width_expr shall be a positive constant integer expression. The lsb_base_expr and msb_base_expr can vary at run time. The first two examples select bits starting at the base and ascending the bit range. The number of bits selected is equal to the width expression. The second two examples select bits starting at the base and descending the bit range.

A part-select of any type that addresses a range of bits that are completely out of the address bounds of the net, reg, integer, time variable, or parameter or a part-select that is x or z shall yield the value x when read and shall have no effect on the data stored when written. Part-selects that are partially out of range shall, when read, return x for the bits that are out of range and shall, when written, only affect the bits that are in range.

For example:

reg [31: 0] big_vect;
reg [0 :31] little_vect;
reg [63: 0] dword;
integer sel;

big_vect[ 0 +: 8] // == big_vect[ 7 : 0]
big_vect[15 -: 8] // == big_vect[15 : 8]

little_vect[ 0 +: 8] // == little_vect[0 : 7]
little_vect[15 -: 8] // == little_vect[8 :15]

dword[8sel +: 8] // variable part-select with fixed width*

Example 1—The following example specifies the single bit of acc vector that is addressed by the operand index :

acc[index]

The actual bit that is accessed by an address is, in part, determined by the declaration of acc . For instance, each of the declarations of acc shown in the next example causes a particular value of index to access a different bit:

reg [15:0] acc;
reg [2:17] acc

Example 2—The next example and the bullet items that follow it illustrate the principles of bit addressing. The code declares an 8-bit reg called vect and initializes it to a value of 4. The list describes how the separate bits of that vector can be addressed.

reg [7:0] vect;
vect = 4; // fills vect with the pattern 00000100
// msb is bit 7, lsb is bit 0
— If the value of addr is 2, then vect[addr] returns 1.
— If the value of addr is out of bounds, then vect[addr] returns x.
— If addr is 0, 1, or 3 through 7, vect[addr] returns 0.
— vect[3:0] returns the bits 0100.
— vect[5:1] returns the bits 00010.
— vect[ expression that returns x ] returns x.
— vect[ expression that returns z ] returns x.
— If any bit of addr is x or z , then the value of addr is x.

NOTE 1—Part-select indices that evaluate to x or z may be flagged as a compile time error. NOTE 2—Bit-select or part-select indices that are outside of the declared range may be flagged as a compile time error.


That particular syntax is called an indexed part select. It's very useful when you need to select a fixed number of bits from a variable offset within a multi-bit register.

Here's an example of the syntax:

reg [31:0] dword;
reg [7:0] byte0;
reg [7:0] byte1;
reg [7:0] byte2;
reg [7:0] byte3;

assign byte0 = dword[0 +: 8];    // Same as dword[7:0]
assign byte1 = dword[8 +: 8];    // Same as dword[15:8]
assign byte2 = dword[16 +: 8];   // Same as dword[23:16]
assign byte3 = dword[24 +: 8];   // Same as dword[31:24]

The biggest advantage with this syntax is that you can use a variable for the index. Normal part selects in Verilog require constants. So attempting the above with something like dword[i+7:i] is not allowed.

So if you want to select a particular byte using a variable select, you can use the indexed part select.

Example using variable:

reg [31:0] dword;
reg [7:0] byte; 
reg [1:0] i;

// This is illegal due to the variable i, even though the width is always 8 bits
assign byte = dword[(i*8)+7 : i*8];  // ** Not allowed!

// Use the indexed part select 
assign byte = dword[i*8 +: 8];

The purpose of this operator is when you need to access a slice of a bus, both MSB position and LSB positions are variables, but the width of the slice is a constant value, as in the example below:

bit[7:0] bus_in = 8'hAA;
int lsb = 3;
int msb = lsb+3;  // Setting msb=6, for out bus of 4 bits

bit[3:0] bus_out_bad = bus_in[msb:lsb]; // ILLEGAL - both boundaries are variables
bit[3:0] bus_out_ok  = bus_in[lsb+:3]; // Good - only one variable