After brushing this set of questions, I found that Verilog was so simple - HDLBits answer series

Posted by CG-Sodar on Wed, 15 Dec 2021 01:45:32 +0100

Write in front

Write a new pit: recommend a very good website for practicing Verilog. There are one or two hundred questions, which basically covers all aspects of Verilog grammar. It is a very good introductory learning website. Website connection: HDLBits

The questions are all done by ourselves. They are the correct answers that have been verified. By the way, their own ideas for solving problems are attached. Each topic only lists one method, but I know that many topics have many solutions, and the time relationship is not listed one by one. Hope to help you.

Today, we will update the three sections of the Verilog Language chapter: Modules:Heirarchy, Procedures, and More Verilog Features.

2.3,Modules:Heirarchy

2.3.1,Modules

Personal thoughts:

Two ways of modularization are mainly investigated: one-to-one correspondence according to the signal name; According to the signal position of the exemplified module.

Here, just use one method to instantiate. Personally, I suggest using the instantiation method of one-to-one correspondence according to the name, which is more intuitive.

module top_module ( input a, input b, output out );

    mod_a	mod_a_inst(
        .in1	(a),
        .in2	(b),
        .out	(out)
    );
    
endmodule

2.3.2,Connecting ports by position

Personal thoughts:

It mainly investigates the modularization according to the signal position of the instantiated module.

When instantiating the module here, pay attention to the order consistent with the signal order of the instantiated module. This method is usually not recommended. It is easy to be scolded by leaders in this way.

module top_module ( 
    input a, 
    input b, 
    input c,
    input d,
    output out1,
    output out2
);
    
    mod_a mod_a_inst(out1,out2,a,b,c,d);
    
endmodule

2.3.3,Connecting ports by name

Personal thoughts:

It mainly investigates the modularization according to the one-to-one correspondence of the signal names of the instantiated modules.

This method is usually recommended, which is intuitive and portable.

module top_module ( 
    input a, 
    input b, 
    input c,
    input d,
    output out1,
    output out2
);
    mod_a	mod_a_inst(
        .in1(a),
        .in2(b),
        .in3(c),
        .in4(d),
        .out1(out1),
        .out2(out2)
    );

endmodule

2.3.4,Three modules

Personal thoughts:

It mainly investigates the modularization and the ability to identify the module block diagram.

Instantiate three items according to the picture information_ DFF module is OK. Remember to make two wire variables for the two wires in the middle.

module top_module ( input clk, input d, output q );

	wire q1,q2;    
    my_dff	inst1(
        .clk	(clk),
        .d		(d),
        .q		(q1)
    );
    my_dff	inst2(
        .clk	(clk),
        .d		(q1),
        .q		(q2)
    );
    my_dff	inst3(
        .clk	(clk),
        .d		(q2),
        .q		(q)
    );
endmodule

2.3.5,modules and vectors

Personal thoughts:

Instantiate three items according to the picture information_ DFF module, and use the case statement to output one out of four.

module top_module ( 
    input clk, 
    input [7:0] d, 
    input [1:0] sel, 
    output [7:0] q 
);
    wire [7:0]	q1,q2,q3;
    always@(*)begin
        case(sel)
        	2'd0:	q = d;
        	2'd1:	q = q1;
            2'd2:	q = q2;
            2'd3:	q = q3;
            default:;
        endcase
    end 
	my_dff8	inst1(
        .clk	(clk),
        .d		(d),
        .q		(q1)
    );
    my_dff8	inst2(
        .clk	(clk),
        .d		(q1),
        .q		(q2)
    );
    my_dff8	inst3(
        .clk	(clk),
        .d		(q2),
        .q		(q3)
    );

endmodule

2.3.6,Adder 1

Personal thoughts:

The low 16 bits and high 16 bits of the input signal are instantiated respectively, and the two full adder modules are added. A wire variable connection is required for carry.

module top_module(
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
);
    
    wire		cout_2_cin;
    
    add16 u1_add16(
        .a      (a[15:0]	),
        .b      (b[15:0]	),
        .cin	(1'b0		),
        .sum	(sum[15:0]	),
        .cout	(cout_2_cin	)
    );
 
    add16 u2_add16(
        .a      (a[31:16]	),
        .b      (b[31:16]	),
        .cin	(cout_2_cin	),
        .sum	(sum[31:16]	),
        .cout	(   		)
    );
 
endmodule

2.3.7,Adder 2

Personal thoughts:

Follow 2.3 6 is actually the same, just that you need to write a full adder module yourself.

module top_module (
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
);//
wire	cout_2_cin;

add16	inst1(
    .a		(a[15:0]	),
    .b		(b[15:0]	),
    .cin	(1'b0		),
    .cout	(cout_2_cin	),
    .sum	(sum[15:0]	)
);
add16	inst2(
    .a		(a[31:16]	),
    .b		(b[31:16]	),
    .cin	(cout_2_cin	),
    .cout	(			),
    .sum	(sum[31:16]	)
);
    
endmodule


module add1 ( input a, input b, input cin,   output sum, output cout );

    assign {cout,sum} = a + b + cin;

endmodule

2.3.8,Carry-select adder

Personal thoughts:

A carry selection adder is implemented according to the picture information. The specific principle is not studied in depth. Here is a reference: [FPGA/IC] common test adder summary

The implementation only needs to instantiate three sub modules and add some wire variables.

module top_module(
    input [31:0] a,
    input [31:0] b,
    output [31:0] sum
);
wire [15:0]	sum1,sum2,sum3;
wire		cout;

assign	sum = cout ? {sum3,sum1} : {sum2,sum1};   
    
add16	inst1(
    .a		(a[15:0]	),
    .b		(b[15:0]	),
    .cin	(1'b0		),
    .cout	(cout		),
    .sum	(sum1		)
);
add16	inst2(
    .a		(a[31:16]	),
    .b		(b[31:16]	),
    .cin	(1'b0		),
    .cout	(			),
    .sum	(sum2 		)
);
add16	inst3(
    .a		(a[31:16]	),
    .b		(b[31:16]	),
    .cin	(1'b1		),
    .cout	(			),
    .sum	(sum3		)
);    
endmodule

2.3.9,Adder subtractor

Personal thoughts:

It can be realized according to the picture information.

module top_module(
    input [31:0] a,
    input [31:0] b,
    input sub,
    output [31:0] sum
);
    
    wire [31:0]	temp_b;
	wire		cout_2_cin;
    
    assign temp_b = {32{sub}} ^ b;
      
    add16	inst1(
        .a		(a[15:0]		),
        .b		(temp_b[15:0]	),
        .cin	(sub			),
        .cout	(cout_2_cin		),
        .sum	(sum[15:0]		)
    );
    add16	inst2(
        .a		(a[31:16]		),
        .b		(temp_b[31:16]	),
        .cin	(cout_2_cin		),
        .cout	(				),
        .sum	(sum[31:16]		)
    );
endmodule

2.4,Procedures

2.4.1,Always blocks(combination)

Personal thoughts:

Two ways of assignment in combinatorial logic are investigated.

Use assign and always @ (*) to assign values to variables respectively.

// synthesis verilog_input_version verilog_2001
module top_module(
    input a, 
    input b,
    output wire out_assign,
    output reg out_alwaysblock
);
    assign	out_assign = a & b;
    always@(*)begin
         out_alwaysblock = a & b;
    end

endmodule

2.4.2,Always blocks(blocked)

Personal thoughts:

Two ways of assignment of combinatorial logic are investigated; Investigate the assignment mode of temporal logic. Remember that combinatorial logic uses blocking assignment: "=" and sequential logic uses non blocking assignment: "< =". Refer to: Blocking assignment (=) and non blocking assignment (< =)

Assign and always @ (*) are used to assign values to variables in combination respectively; Use always@(clk) to assign a value to the sequential logic.

// synthesis verilog_input_version verilog_2001
module top_module(
    input clk,
    input a,
    input b,
    output wire out_assign,
    output reg out_always_comb,
    output reg out_always_ff   );
    
    assign out_assign = a ^ b;
    
    always@(*)begin
        out_always_comb = a ^ b;
    end
    
    always@(posedge clk)begin
        out_always_ff <= a ^ b;
    end

endmodule

2.4.3,If statement

Personal thoughts:

The use of ternary operators of combinatorial logic assignment and if else statements are investigated;

You can assign values to variables by using the ternary operator and if else statement respectively.

// synthesis verilog_input_version verilog_2001
module top_module(
    input a,
    input b,
    input sel_b1,
    input sel_b2,
    output wire out_assign,
    output reg out_always   ); 

    assign	out_assign = sel_b1 ?(sel_b2 ? b : a): a;
    
    always@(*)begin
        if(sel_b1 && sel_b2) 
        	out_always = b;
       	else
            out_always = a;	
    end
endmodule

2.4.4,If statement latches

Personal thoughts:

This paper mainly investigates how to prevent the generation of latch.

If you complete the if else in the title, there will be no latch.

// synthesis verilog_input_version verilog_2001
module top_module (
    input      cpu_overheated,
    output reg shut_off_computer,
    input      arrived,
    input      gas_tank_empty,
    output reg keep_driving  ); //

    always @(*) begin
        if (cpu_overheated)
           shut_off_computer = 1;
        else
           shut_off_computer = 0;
    end

    always @(*) begin
        if (~arrived)
           keep_driving = ~gas_tank_empty;
        else
           keep_driving = 0; 
    end

endmodule

2.4.5,Case statement

Personal thoughts:

Mainly focus on the use of case statements. Remember to use default to indicate other situations, otherwise latches will be generated. I suggest that even if all the situations are listed, you can also write a default + empty statement to form a good habit.

According to the corresponding relationship, use the case statement to give examples one by one.

// synthesis verilog_input_version verilog_2001
module top_module ( 
    input [2:0] sel, 
    input [3:0] data0,
    input [3:0] data1,
    input [3:0] data2,
    input [3:0] data3,
    input [3:0] data4,
    input [3:0] data5,
    output reg [3:0] out);//

    always@(*) begin  // This is a combinational circuit
        case(sel)
        	3'd0:	out <= data0;
            3'd1:	out <= data1;
            3'd2:	out <= data2;
            3'd3:	out <= data3;
            3'd4:	out <= data4;
            3'd5:	out <= data5;
            default:out <= 4'd0;
        endcase
    end

endmodule

2.4.6,Priority encoder

Personal thoughts:

This paper mainly investigates the use of finite decoder.

You can use a case statement. If the condition is 1, that is, the corresponding statement will be executed when the bit is 1.

// synthesis verilog_input_version verilog_2001
module top_module (
    input [3:0] in,
    output reg [1:0] pos  );

    always@(*)begin
    	case(1)
            in[0] :  pos = 2'd0;
            in[1] :  pos = 2'd1;
            in[2] :  pos = 2'd2;
            in[3] :  pos = 2'd3;
            default: pos = 2'd0;
        endcase
    end
endmodule

2.4.7,Priority encoder with casez

Personal thoughts:

It mainly investigates the use of casez statements. The casez statement indicates that the bits set to z can be ignored and only the bits set to 1 and 0.

Use the casez statement to set the corresponding bits to 1 respectively.

// synthesis verilog_input_version verilog_2001
module top_module (
    input [7:0] in,
    output reg [2:0] pos  );

    always@(*)begin
    	casez(in)
            8'bzzzzzzz1:  pos = 3'd0;
            8'bzzzzzz1z:  pos = 3'd1;
            8'bzzzzz1zz:  pos = 3'd2;
            8'bzzzz1zzz:  pos = 3'd3;            
            8'bzzz1zzzz:  pos = 3'd4;            
            8'bzz1zzzzz:  pos = 3'd5;            
            8'bz1zzzzzz:  pos = 3'd6;            
            8'b1zzzzzzz:  pos = 3'd7;
            default:	  pos = 3'd0;
        endcase
    end
endmodule

2.4.8,Avoiding latches

Personal thoughts:

This paper mainly investigates how to avoid latch. Methods include: if else completion, case statement using default completion, etc.

When using the case statement, you can assign an initial default value to the variable.

// synthesis verilog_input_version verilog_2001
module top_module (
    input [15:0] scancode,
    output reg left,
    output reg down,
    output reg right,
    output reg up  );
    
    always@(*)begin
        up = 1'b0; 
        down = 1'b0; 
        left = 1'b0; 
        right = 1'b0;
        case(scancode) 
        	16'he06b:	left = 1'b1;
            16'he072:	down = 1'b1;
            16'he074:	right = 1'b1;
            16'he075:	up = 1'b1;
        endcase
    end

endmodule

2.5,More Verilog Features

2.5.1,conditional ternary operator

Personal thoughts:

This paper mainly investigates the use of binocular selection operator.

You can compare the two first, and then compare the two smaller ones to find the smallest one. It seems that the program is not so bloated.

module top_module (
    input [7:0] a, b, c, d,
    output [7:0] min);//
    
    wire [7:0]	min1,min2;    
    assign	min1 = (a<b) ? a : b;
    assign	min2 = (c<d) ? c : d;
    assign  min = (min1<min2) ? min1 : min2;
    // assign intermediate_result1 = compare? true: false;

endmodule

2.5.2,reduction operator

Personal thoughts:

It mainly focuses on the use of reduction operators. You can refer to: Verilog decrement operator

Use the reduction operator to bitwise XOR.

module top_module (
    input [7:0] in,
    output parity); 

    assign parity = ^in;
endmodule

2.5.3,reduction : even wider gates

Personal thoughts:

Same as 2.5 2. It's just that the input digits are wider.

Use the reduction operator to differ by and, or, or.

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

2.5.4,combinational for-loop Vector reversal 2

Personal thoughts:

Mainly investigate the use of for loop statements. Be careful not to use i + + like C language, because there is no incremental operation symbol + + in Verilog.

module top_module( 
    input [99:0] in,
    output [99:0] out
);
    integer	i;
    always@(*)begin
        for(i = 0;i < 100;i = i + 1 )begin 
            out [i] = in[99-i];
        end
    end
endmodule

2.5.5,combinational for-loop 255-bit population count

Personal thoughts:

Mainly investigate the use of for loop statements and accumulation operation. Remember to initialize the output of accumulation operation first.

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

    integer	i;
    always@(*)begin
        out = 8'd0;
        for(i = 0;i < 255;i = i + 1 )begin 
            out = out + in[i];
        end
    end
endmodule

2.5.6,combinational for-loop 100-bit binary adder 2

Personal thoughts:

It mainly focuses on the use of generate statements. Remember to use the begin end block (even if there is only one statement, and the begin block should be named). You can refer to: Basic understanding of generate statement in verilog syntax

When using generate to instantiate 100 full adders or perform 100 full adder operations, it should be noted that the first time is special. The carry of the first time comes from the input rather than the carry of the previous bit, so it needs to be written separately.

module top_module( 
    input [99:0] a, b,
    input cin,
    output [99:0] cout,
    output [99:0] sum );

    generate
        genvar i;        
        for(i = 0; i <= 99; i = i + 1)begin:full_adder
        	if(i == 0)begin
            	assign {cout[0], sum[0]} = a[0] + b[0] + cin;
            end
            else begin
            	assign {cout[i], sum[i]} = a[i] + b[i] + cout[i-1];
            end         
        end        
    endgenerate
endmodule

2.5.7,combinational for-loop 100-digit BCD adder

Personal thoughts:

When using generate to instantiate 100 BCD adder s, it should be noted that the first time is special. The carry of the first time comes from the input rather than the carry of the previous bit, so it needs to be written separately.

module top_module( 
    input [399:0] a, b,
    input cin,
    output cout,
    output [399:0] sum );

    wire[99:0]	cout_to_cin;
    
    generate
        genvar i;
        for(i = 0; i <= 99; i = i + 1)begin:BCD_adder
            if(i == 0)begin
                bcd_fadd bcd_fadd_inst(
                    .a		(a[3:0]			),
                    .b		(b[3:0]			),
                    .cin	(cin			),
                    .sum	(sum[3:0]		),
                    .cout	(cout_to_cin[0]	)
                );
            end
            else begin
                bcd_fadd bcd_fadd_inst1(
                    .a		(a[4 * i + 3: 4 * i]	),
                    .b		(b[4 * i + 3: 4 * i]	),
                    .cin	(cout_to_cin[i - 1]		),
                    .sum	(sum[4 * i + 3: 4 * i]  ),
                    .cout	(cout_to_cin[i]			)
                );
            end
        end
        assign cout = cout_to_cin[99];
    endgenerate
endmodule

Topics: Verilog FPGA Hdlbits