Verilog HDLBits phase III: 2.2Vectors

Keywords: Verilog Embedded system FPGA hardware Hdlbits

catalogue

preface

2.2.1Vectors(Vector0)

Solution:

2.2.2Vectors in more detail(Vector1)

A Bit of Practice:

Solution:

2.2.3Vector part select(Vector2)

Solution:

2.2.4Bitwise operators(Vectorgates)

Solution:

2.2.5Four-input gates(Gates4)

Solution:

2.2.6Vector concatenation operator(Vector3)

A Bit of Practice:

Solution:

2.2.7Vector reversal 1(Vectorr)

Solution:

***Supplementary content***

2.2.8Replication operator(Vector4)

A Bit of Practice:

Solution:

 2.2.9More replication(Vector5)

Solution:

preface

HDLbits website is as follows

Problem sets - HDLBits (01xz.net)

Starting from this issue, we continue to study the second chapter of Verilog Language of HDLbits. The content of this issue is 2.2 vectors

2.2.1Vectors(Vector0)

Vector uses a name to group related signals for operation. For example, wire [7:0] w; An 8-bit vector named w is defined, which is equivalent to defining 8 independent wire signals.

Please note that the declaration of a vector places the dimension before the vector name, which is different from the syntax of C language. However, when selecting a bit, it is similar to the array in C language.

wire [99:0] my_vector; // Declare a vector of 100 elements

assign out = my_vector[10]; // Select a bit in the vector

Build a 3-bit input circuit, and then output the same vector. In addition, split it into three separate 1-bit outputs, connect the output o0 with the 0 bit of the output vector, connect the output o1 with the 1 bit of the output vector, and so on.

In the chart, a slash with a number next to it indicates the width of the vector (or bus), rather than drawing each wire in the vector separately.

Create a single input and single output module, which is wire type.

Solution:

module top_module(
	input [2:0] vec, 
	output [2:0] outv,
	output o2,
	output o1,
	output o0
);
	
	assign outv = vec;

	// This is ok too: assign {o2, o1, o0} = vec;
	assign o0 = vec[0];
	assign o1 = vec[1];
	assign o2 = vec[2];
	
endmodule

Also for simplicity, you can use    assign {o2, o1, o0} = vec;

2.2.2Vectors in more detail(Vector1)

Vector uses a name to group related signals for operation. For example, wire [7:0] w; An 8-bit vector named w is defined, which is equivalent to defining 8 independent wire signals.

Declaring Vectors

The vector must be declared as follows:

type [upper:lower] vector_name;

Type specifies the data type of the vector. Usually wire or reg. If you want to declare an input or output port, type can include additional port types (such as input or output). Here are some examples:

wire [7:0] w;         // 8-bit wire
reg  [4:1] x;         // 4-bit reg
output reg [0:0] y;   // 1-bit reg that is also an output port
input wire [3:-2] z;  // 6-bit wire input (negative range is also allowed, [3: - 2], i.e. 3, 2, 1, 0, - 1, - 2)
output [3:0] a;       // 4-bit output wire. Unless otherwise specified, the default type is wire
wire [0:7] b;         // 8-bit wire, where b[0] is the most important bit

The byte order (or "direction" in common) of a vector refers to whether the least significant bit has a lower index (small end order, such as [3:0]) or a higher index (large end order, such as [0:3]). In Verilog, once a vector is declared with a specific byte order, it must be used in the same way, for example, when wire [3:0 vec] is declared; After that, it is illegal to use vec[0:3]. It is a good practice to use consistent with the byte order, because strange errors will occur if vectors of different byte order are allocated or used together.

Implicit nets

Implicit nets are often a source of undetectable errors. If an undeclared vector appears / is referenced in an assign statement or an undeclared module, implicit nets will be implicitly generated in the module. Implicit nets is generally a wire variable. If it is used as a vector, it is likely to report an error. Instructions can be used

`default_nettype none 

To disable the creation of Implicit nets.

wire [2:0] a, c;   // Two vectors
assign a = 3'b101;  // a = 101
assign b = a;       // B = 1 implicitly created wire
assign c = b;       // c = 001  <-- bug
my_module i1 (d,e); // d and e are implicitly one-bit wide if not declared.
                    //(both d and e are implicitly declared as 1-bit wire)
                    // This could be a bug if the port was intended to be a vector.
                    //If the module needs a vector, a bug occurs

add to  ` default_nettype none   Will cause the code to appear ERROR.

Unpacked vs. Packed Arrays

Vector bit width is usually declared before the vector name. The bit width represents the "packed" dimension of the vector, and each signal operates together (this is related to the simulator, but different from the hardware). The unpacked dimension is defined after the vector name. They are often used to declare vector arrays.

reg [7:0] mem [255:0];   // 256 unpacked elements, each of which is a 8-bit packed vector 
                         //of reg. (there are 256 unpacked elements, each of which is an 8-bit packdreg vector)
reg mem2 [28:0];         // 29 unpacked elements, each of which is a 1-bit reg.

Accessing Vector Elements: Part-Select 

Accessing the entire vector is done with the vector name. For example: assign w = a;

Assign the 4-bit vector a to the 8-bit vector w (all declarations are described above). If the length of the left operand is different from that of the right operand, it will complement 0 or truncate as needed.

The partial selection operator can be used to access a portion of the vector:

w[3:0]      // Only the lower 4 bits of w
x[1]        // The lowest bit of x
x[1:1]      // ...also the lowest bit of x
z[-1:-2]    // Two lowest bits of z
b[3:0]      // Illegal. Vector part-select must match the direction of the declaration.
            //Illegal operation. b when used, the byte order is opposite to that when declared
b[0:3]      // The *upper* 4 bits of b
assign w[3:0] = b[0:3];    // Assign upper 4 bits of b to lower 4 bits of w. w[3]=b[0], 
                           //w[2]=b[1], etc. (assign the upper four bits of b to the lower four bits of w)

A Bit of Practice:

Construct a combinational circuit, divide the input 16 bit signal into high eight bits and low eight bits, and output them respectively.

Solution:

module top_module (
	input [15:0] in,
	output [7:0] out_hi,
	output [7:0] out_lo
);
	
	assign out_hi = in[15:8];
	assign out_lo = in[7:0];
	// Concatenation operator also works: assign {out_hi, out_lo} = in;
endmodule

Also for simplicity, you can use join operators   assign {out_hi, out_lo} = in;

2.2.3Vector part select(Vector2)

A 32-bit vector is considered to contain 4 bytes ([31:24], [23:16], etc.). A circuit is constructed to reverse the byte order of the 4-byte vector.

AaaaaaaaBbbbbbbbCcccccccDddddddd => DdddddddCcccccccBbbbbbbbAaaaaaaa

This operation is usually used when the byte order of a piece of data needs to be exchanged, such as between the small end x86 system and the large end format used in many Internet protocols.

Solution:

module top_module( 
    input [31:0] in,
    output [31:0] out );

    assign {out[31:24],out[23:16],out[15:8],out[7:0]} = 
    {in[7:0],in[15:8],in[23:16],in[31:24]};

endmodule

Hints: in the assign statement, slice selection can be used on the left or right.

Rational use of join operators can make the code more readable.

2.2.4Bitwise operators(Vectorgates)

Two three bit input circuits are constructed to output the bit or, logical or and inverse of two vectors respectively. Put the inverse of b in out_ The upper three bits of not (bits[5:3]), and the inverse of a is set to out_not third.

Bitwise vs. Logical Operators

As mentioned earlier, there are bitwise and logical operations of various Boolean operators (see 2.1.5). For a 1-bit wire signal, the functions of the two operations are the same. However, when operating on vectors, the difference between the two operator types becomes very important!

Bitwise operator: bitwise operation between two N-bit vectors copies the operation of each bit in the vector and produces n-bit output.

Logical operator: treat the entire vector as a Boolean value (non-zero is true, zero is false) and produce one bit output.

Check the simulation waveform to understand the difference between bitwise OR and logical or.

Solution:

module top_module(
	input [2:0] a, 
	input [2:0] b, 
	output [2:0] out_or_bitwise,
	output out_or_logical,
	output [5:0] out_not
);
	
	assign out_or_bitwise = a | b;
	assign out_or_logical = a || b;

	assign out_not[2:0] = ~a;	// Part-select on left side is o.
	assign out_not[5:3] = ~b;	//Assigning to [5:3] does not conflict with [2:0]
                                //Assignment to [5:3] and [2:0] does not conflict
endmodule

Hints: even if you can't assign a value to the wire signal multiple times, you can select the left side of the assign statement. You don't want to assign all to the entire vector in another statement.

Like C language, Verilog has bitwise OR (|) and logical or (|). In some cases, it is better to use logical operation for Boolean expression and bit operation for signal processing.

2.2.5Four-input gates(Gates4)

Construct a 4-input (in[3:0]) combinational circuit.

There are three outputs:

  • out_and:4 input and gate output
  • out_or:     4 input or output of gate
  • out_ XOR: output of 4-input XOR gate

Solution:

module top_module( 
    input [3:0] in,
    output out_and,
    output out_or,
    output out_xor
);
    assign out_and = in[3]&in[2]&in[1]&in[0];
    assign out_or =  in[3]|in[2]|in[1]|in[0];
    assign out_xor =  in[3]^in[2]^in[1]^in[0];   

endmodule

Another way is to use the reduction operator, which has only one operand.

assign out_ and = &in;       Equivalent to         assign out_ and = in[3]&in[2]&in[1]&in[0];

2.2.6Vector concatenation operator(Vector3)

The slice selection operation is used to select the part of the vector. The join operator {a,b,c} is used to create a larger vector by joining smaller parts of the vector together.

{3'b111, 3'b000} => 6'b111000
{1'b1, 1'b0, 3'b101} => 5'b10101
{4'ha, 4'd10} => 8'b10101010     // 4'ha and 4'd10 are both 4'b1010 in binary
                                 // 4'ha and 4'd10 are binary 4'b1010

The join operator needs to know the width of each member (how else do you know the width of the result?). Therefore, {1,2,3} is illegal and will generate ERROR: the connection operator does not allow constants of undetermined size!

Join operators can be used as left and right sides of assignment statements.

input [15:0] in;
output [23:0] out;
assign {out[7:0], out[15:8]} = in;         // Swap two bytes
assign out[15:0] = {in[7:0], in[15:8]};    //ditto
assign out = {in[7:0], in[15:8]};       // This is different from the previous. The 16 bit vector on the right expands to match the 24 bits on the left 
                                        //Vector, so out[23:16]=0
                                        //In the first two examples, out[23:16] is not assigned

A Bit of Practice:

Connect some input vectors together and re divide them into different output vectors. 5 input vectors: a, b, c, d, e and f, with 30 bits in total. w. x, y, z are all 8-bit output vectors. The output should be the connection of the input vector, where the last two bits are 11

Solution:

module top_module (
    input [4:0] a, b, c, d, e, f,
    output [7:0] w, x, y, z );//

    assign {w,x,y,z} = {a,b,c,d,e,f,2'b11};

endmodule

2.2.7Vector reversal 1(Vectorr)

Reverse the order of the 8-bit vector and output it.

Solution:

module top_module( 
    input [7:0] in,
    output [7:0] out
);
    assign {out[0],out[1],out[2],out[3],out[4],out[5],out[6],out[7]} = in;

endmodule

Hint:

  • assign out[7:0] = in[0:7];   It doesn't work because the bit order when Verilog uses vectors should be consistent with the definition
  • One assign statement can be used for connection operators, which is more concise than eight

***Supplementary content***

I know you'd love to know how to do this using circular statements:

Build a composite always block. This creates combinatorial logic that evaluates the same results as the sequential code. The for loop describes the circuit at the behavior level, not the structure level. Therefore, they can only be used in program blocks (such as the always block). The circuits created (wire s and gates) do not require any iteration: it only produces the same results as when the iteration occurs. In fact, the logic synthesizer iterates at compile time to determine what circuit to generate. (in contrast, the Verilog emulator will execute loops sequentially during the simulation.)

always @(*) begin	
		for (int i=0; i<8; i++)	// int is the type of SystemVerilog, and integer is used in pure Verilog
			out[i] = in[8-i-1];
end

This can also be done using the generate for loop. The generate loop looks like a programmatic for loop, but it is conceptually very different and not easy to understand. Generate loops are used to instantiate "things" (unlike program loops, which do not describe actions). These "things" are assign statements, module instantiations, net/variable declarations, and procedure blocks (not created in procedures). The generate loop (and genvars) is evaluated entirely at compile time. You can think of the generate block as a form of preprocessing that generates more code and then runs it through the logic synthesizer. In the following example, the generate for loop first creates eight assign statements at compile time, and then synthesizes them. Note that due to its intended use (generating code at compile time), there are some restrictions on how to use them.

For example: 1. In Quartus, you need a generate for loop to attach a named begin and block (in this case, it is called "my_block_name"). 2. In the circulatory system, genvars are read-only.

generate
		genvar i;
		for (i=0; i<8; i = i+1) begin: my_block_name
			assign out[i] = in[8-i-1];
		end
endgenerate

The translation of this topic is stumbling. I hope I can come back and optimize it in the future!

2.2.8Replication operator(Vector4)

In the 2.2.6 concatenation operator, concatenation operators concatenate vectors to form larger vectors. But for connecting the same vectors together, use  

a = {b,b,b,b,b,b};

It's too boring. The copy operator can repeat a vector and connect them together:

{num{vector}}   Copy the vector num times, Num must be a constant. And you need two sets of braces!

{5{1'b1}}           // 5'b11111 (or 5'd31 or 5'h1f)
{2{a,b,c}}          // Equivalent to {a,b,c,a,b,c}
{3'd5, {2{3'd6}}}   // 9'b101_110_110. It is the connection between the first vector and two second vectors

A Bit of Practice:

The common use of the copy operator is to expand a smaller signed number to a larger number while retaining its sign bit. This is done by copying the sign bit (most significant bit) of the smaller number to the left. For example, 4'b0101   (5) 8'b00000101 extended to 8bit   (5) , and 4'b1101   (-3)   8'b11111101 extended to 8bit   (-3).

Build a circuit that extends 8-bit symbol bit to 32-bit. This requires connecting copies of 24 symbol bits (i.e., copying bits [7] 24 times), followed by the 8-bit number itself.

Solution:

module top_module (
    input [7:0] in,
    output [31:0] out );//
    assign out = {{24{in[7]}},in[7:0]};

    // assign out = { replicate-sign-bit , the-input };

endmodule

Note that the copy operator requires two sets of braces!!!

 2.2.9More replication(Vector5)

For five 1-bit signals (a, b, c, d, e), the comparison calculation of each bit is output respectively. If the two compared bits are equal, 1 is output (i.e. the same or operation).

  • out[24] = ~a ^ a;   // a == a, so out[24] is always 1.
    out[23] = ~a ^ b;
    out[22] = ~a ^ c;
    ...
    out[ 1] = ~e ^ d;
    out[ 0] = ~e ^ e;

As shown in the figure above, using the copy and join operators will be easier.

  • The above vectors are copied 5 times for each input, and then connected together
  • The following vector connects five inputs together and then copies them five times

Solution:

module top_module (
	input a, b, c, d, e,
	output [24:0] out
);

	wire [24:0] top, bottom;
	assign top    = { {5{a}}, {5{b}}, {5{c}}, {5{d}}, {5{e}} };
	assign bottom = {5{a,b,c,d,e}};
	assign out = ~top ^ bottom;	// Bitwise XNOR

	// It can be replaced by the following line (but it is much less readable)
	// assign out = ~{ {5{a}}, {5{b}}, {5{c}}, {5{d}}, {5{e}} } ^ {5{a,b,c,d,e}};
	
endmodule

In fact, it's OK to clarify the order of copy and connection. In addition, the copy operator needs two sets of braces!

2.2 it's finally over. The translation of this issue is stumbling. I hope I can get a different harvest in the future. See you next time!

Posted by fanfavorite on Sun, 07 Nov 2021 10:02:32 -0800