刷完这套题,我才发现Verilog原来如此简单----HDLBits答案系列----Getting Started、Verilog Language

写在前面

写个新坑:给大家推荐一个非常好的练习Verilog的网站,有一两百道题,基本涵盖了Verilog语法的方方面面,是一个非常好的入门学习网站。

网站连接:HDLBits

 全部答案汇总:刷完这套题,我才发现Verilog原来如此简单----HDLBits答案汇总

题目都是自己做的,都是通过验证的正确答案,顺便附上了自己的解题思路。每个题目只列出了一种方法,但是我知道很多题目都有很多解法,时间关系就没一一列出来了。希望能帮到你。


1、Getting Started

1.1、Getting Started

个人思路:

        只需要使得输出恒定为1即可。

module top_module( 
    output one 
);

assign one = 1'b1;    //输出恒定为1

endmodule

1.2、Output Zero

个人思路:

        只需要使得输出恒定为0即可。

module top_module(
    output zero
);

    assign zero = 1'b0;       //输出为0

endmodule

2、Verilog Language

2.1、Basics

2.1.1、Simple Wire

个人思路:

        主要考察assign 语句的运用,使得输出等于输入即可。

module top_module( input in, output out );

    assign out = in;        //输出等于输入

endmodule

2.1.2、four wires

个人思路:

        主要考察assign 语句的运用,根据图片的对应关系输出响应的输入即可。

module top_module( 
    input a,b,c,
    output w,x,y,z );

    assign w = a;
    assign x = b;
    assign y = b;
    assign z = c;

endmodule

2.1.3、Invert 

个人思路:

        主要考察 非门 的运用,输出等于输入取反即可。

module top_module( input in, output out );

    assign out = ~in;

endmodule

2.1.4、AND Gate

个人思路:

        主要考察 与门 的运用,输出等于两输入相与即可。

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

    assign out = a & b;
    
endmodule

2.1.5、NOR Gate

个人思路:

        主要考察 或非门 的运用,输出等于两输入相与后取反即可。

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

    assign out = ~(a | b);
    
endmodule

2.1.6、Xnor gate 

个人思路:

        主要考察 同或门(异或非门) 的运用,输出等于两输入异或后取反即可。

module top_module( 
    input a, 
    input b, 
    output out );
    
    assign out = ~(a ^ b);
    
endmodule

2.1.7、Declaring wires 

个人思路:

        主要是wire类型的声明,以及结合assign语句的使用。

        根据图片声明对应的wire变量,以及使用对应的逻辑运算符对其进行运算即可。

module top_module(
    input a,
    input b,
    input c,
    input d,
    output out,
    output out_n   ); 

    wire ab;
    wire cd;
    wire abcd;
    
    assign ab = a & b; 
    assign cd = c & d;
    assign abcd = ab | cd;
    assign out = abcd;
    assign out_n = ~abcd;
    
endmodule

2.1.8、7458 Chip

个人思路:

        主要是wire类型的声明,以及结合assign语句的使用,图片表明的是7458芯片实现的逻辑功能。

        根据图片声明对应的wire变量,以及使用对应的逻辑运算符对其进行运算即可。

module top_module ( 
    input p1a, p1b, p1c, p1d, p1e, p1f,
    output p1y,
    input p2a, p2b, p2c, p2d,
    output p2y );

    assign p1y = (p1a & p1b & p1c) | (p1f & p1e & p1d);
    assign p2y = (p2a & p2b) | (p2c & p2d);
    
endmodule

2.2、Vectors

2.2.1、Vectors

个人思路:

        主要是vector类型变量结合assign语句的使用,以及vector类型变量的位操作。

        根据图片的对应使用assign语句赋值即可。

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

    assign outv = vec;
    assign o2 = vec[2];
    assign o1 = vec[1];
    assign o0 = vec[0];
    
endmodule

2.2.2、Vectors in more details

个人思路:

        主要是vector类型变量结合assign语句的使用,以及vector类型变量的位操作。

        将输入的高8位赋值给out_hi;低8位赋值给out_lo。

module top_module( 
    input wire [15:0] in,
    output wire [7:0] out_hi,
    output wire [7:0] out_lo );

    assign	out_hi = in[15:8];
    assign	out_lo = in[7:0];
    
endmodule

2.2.3、 Vectors part select

个人思路:

        将32位输入看作是4个8位的数据组合而成。

        将最高8位数据与最低8位数据互换,次高8位数据与次低8位数据互换。

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

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

2.2.4、Bitwise operations

个人思路:

        主要考察 位操作 与 逻辑操作,例如按位与、逻辑与、等。

        out_or_bitwise是两个输入按位或;out_or_logical是两个输入逻辑或;out_not是两个输入按位取反后拼接到一起(这里顺序应该是随便的,但是作者设定的是输入b在前)

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 = {~b,~a};

endmodule

2.2.5、Four-input gates

个人思路:

        位宽为4的输入信号,将每一位按位与、按位或、按位异或即可。

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

2.2.6、Vector concatenation operator

个人思路:

        主要考察 位拼接运算符的使用。

        根据图片给出的对应关系,使用位拼接运算符进行拼接即可。

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

    assign z = { e[0],f,2'b11};
    assign y = { d[3:0],e[4:1]};
    assign x = { b[1:0],c,d[4]};
    assign w = { a,b[4:2]};

endmodule

2.2.7、Vector reversal

个人思路:

        把8位输入反转输出,可以使用位拼接运算符,一位一位拼接。

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

2.2.8、Replication operator 

个人思路:

        主要考察 位扩展运算符 的使用。位扩展运算符类似位拼接运算符,适用于拼接重复的数据。

        输出的高24位全部改成输入in的最高位,然后拼接起来。

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

    assign out = { {24{in[7]}},in };

endmodule

2.2.9、More Replication

个人思路:

        输出可以看作是两个输入的或非门。

        一个输入是5个abcde拼接的;另一个输入是5个a、5个b、5个c、5个d、5个e拼接的。

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

    assign out = {5{a,b,c,d,e}} ~^ {{5{a}},{5{b}},{5{c}},{5{d}},{5{e}},};
    
endmodule

2.3、Modules:Heirarchy

2.3.1、Modules

个人思路:

        主要考察模块例化的两种方式:根据信号名字一一对应;根据被例化模块的信号位置。

        这里随便用一种方法例化就行了,个人建议用根据名字一一对应的例化方式,这样比较直观。

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

个人思路:

        主要考察根据被例化模块的信号位置进行模块例化。

        这里例化模块的时候,注意顺序要和被例化模块的信号顺序一致。这种方式平常不建议使用,用这种方式很容易被领导骂。

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

个人思路:

        主要考察根据被例化模块的信号名字一一对应的方式进行模块例化。

        这种方式平常建议使用,比较直观,可移植性强。

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

个人思路:

        主要考察模块例化、以及识别模块框图的能力。

        根据图片信息例化三个my_dff模块就行,中间的两根连线记得搞两个wire变量。

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

个人思路:

        根据图片信息例化三个my_dff模块,并且使用case语句进行一个4选1的输出。

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

个人思路:

        输入信号的低16位和高16位分别例化两个全加器模块相加,进位需要弄一个wire变量连接。

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

个人思路:

        跟2.3.6 其实是一样的,只是说需要自己写一个全加器模块。

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

个人思路:

        根据图片信息实现一个进位选择加法器。具体原理不深究,这里有参考:【 FPGA/IC 】常考加法器总结

        实现只要例化3个子模块,加上一些wire变量就可以。

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

个人思路:

        根据图片信息实现即可。

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)

个人思路:

        考察组合逻辑赋值的两种方式。

        分别使用assign和always@(*)对变量进行赋值即可。

// 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)

个人思路:

        考察组合逻辑赋值的两种方式;考察时序逻辑的赋值方式。记住组合逻辑用阻塞赋值:“=”,时序逻辑用非阻塞赋值:“<=”。可参考:阻塞赋值(=)与非阻塞赋值(<=)

        分别使用assign和always@(*)对变量进行组合赋值;使用always@(clk)对时序逻辑赋值即可。

// 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

个人思路:

        考察组合逻辑赋值的三目运算符、以及if-else语句的使用;

        分别使用三目运算符、以及if-else语句对变量赋值即可。

// 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

个人思路:

        主要考察如何防止锁存器的产生。

        把题目中的if-else补全就不会产生锁存器了。

// 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

个人思路:

        主要考察case语句的使用,一定要记得使用default来表明其他情况,不然会产生锁存器。个人建议哪怕是所有情况都列举出来了,也可以写个default+空语句,养成好习惯。

        根据对应关系,使用case语句一一举例即可。

// 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

个人思路:

        主要考察有限译码器的使用。

        可以使用case语句,条件为1,即哪位为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

个人思路:

        主要考察casez语句的使用。casez语句表明设定为z的位数可以不关心,只关心设定为1、0的位。

        使用casez语句分别把对应的位设定为1即可。

// 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

个人思路:

        主要考察如何避免产生锁存器。方法有:if-else补全,case语句使用default补全等。

        使用case语句的时候给变量赋一个初始默认值即可。

// 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

个人思路:

        主要考察三目选择运算符的使用。

        可以先两两比较,最后再比较2个较小者找出最小的。这样看起来程序没那么臃肿。

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

个人思路:

        主要考察缩减运算符的使用,可参考:Verilog 递减运算符

        使用缩减运算符按位异或即可。

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

    assign parity = ^in;
endmodule

2.5.3、reduction :even wider gates

个人思路:

        同2.5.2,只不过输入位数更宽而已。

        使用缩减运算符按与、或、位异即可。

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

个人思路:

        主要考察for循环语句的使用,注意不要像C语言那样使用i++,因为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

个人思路:

         主要考察for循环语句的使用和累加运算,累加运算的输出记得要先初始化。

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

个人思路:

        主要考察generate语句的使用,一定要记得使用begin-end块(哪怕只有一条语句,而且begin块要命名)。可参考:verilog语法之generate语句的基本认识

        使用generate例化100个全加器或者做100次全加器的运算,需要注意第一次比较特殊,第一次的进位来自输入,而不是前一位的进位,所以需要分开写。

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

个人思路:

        使用generate例化100个BCD adder,需要注意第一次比较特殊,第一次的进位来自输入,而不是前一位的进位,所以需要分开写。

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

        

  • 27
    点赞
  • 104
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 18
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

孤独的单刀

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值