写在前面
写个新坑:给大家推荐一个非常好的练习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 = ∈
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