hdlbits是用verilog HDL进行小型电路硬件设计的一个练习网站,该网站提供综合和仿真,作为一个verilog HDL的初学者,我将在此记录我的学习状况和刷hdlbits的答案和感想和大家交流。个人感想通过代码中的注释或者对应题目代码段上下的文字呈现。hdlbits网站:HDLBits (01xz.net)
在此之前,先贴一个verilog HDL的教程:1.1 Verilog 教程 | 菜鸟教程 (runoob.com)
鉴于hdlbits题目太多,我计划分成多篇博客来编写,分别是verilog language部分、combinational logic部分、sequential logic部分、FSM部分以及其他部分,这一篇内容是verilog language部分。
其他hdlbits博客:
【hdlbits】个人学习交流分享(带答案)——combinational logic部分-CSDN博客
【hdlbits】个人学习交流分享(带答案)——sequential logic部分-CSDN博客
【hdlbits】个人学习交流分享(带答案)——finite state machines(FSM)-CSDN博客
【hdlbits】个人学习交流分享(带答案)——Reading Simulations和Writing Testbenches部分-CSDN博客
正文:
basics
simple wire
module top_module( input in, output out );
assign out=in;
endmodule
four wires
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
invertor
module top_module( input in, output out );
assign out=~in;
endmodule
AND gate
module top_module(
input a,
input b,
output out );
assign out=a&b;
endmodule
NOR gate
module top_module(
input a,
input b,
output out );
assign out=~(a|b);
endmodule
XNOR gate
module top_module(
input a,
input b,
output out );
assign out=~(a^b);
endmodule
declaring wires
`default_nettype none
module top_module(
input a,
input b,
input c,
input d,
output out,
output out_n );
wire e,f;
assign e=a&b;
assign f=c&d;
assign out=e|f;
assign out_n=~(e|f);
endmodule
7458 chips
module top_module (
input p1a, p1b, p1c, p1d, p1e, p1f,
output p1y,
input p2a, p2b, p2c, p2d,
output p2y );
wire a,b,c,d;
assign a=p2c&p2d;
assign b=p2a&p2b;
assign c=p1a&p1c&p1b;
assign d=p1f&p1e&p1d;
assign p2y=a|b;
assign p1y=c|d;
endmodule
vectors
vectors
module top_module (
input wire [2:0] vec,
output wire [2:0] outv,
output wire o2,
output wire o1,
output wire o0 ); // Module body starts after module declaration
assign outv=vec;
assign o2=vec[2];
assign o1=vec[1];
assign o0=vec[0];
endmodule
vectors in more detail
`default_nettype none // Disable implicit nets. Reduces some types of bugs.
module top_module(
input wire [15:0] in,
output wire [7:0] out_hi,
output wire [7:0] out_lo );
assign out_lo=in[7:0];
assign out_hi=in[15:8];
endmodule
vectors part select
module top_module(
input [31:0] in,
output [31:0] out );//
assign out[7:0]=in[31:24];
assign out[15:8]=in[23:16];
assign out[23:16]=in[15:8];
assign out[31:24]=in[7:0];
endmodule
bitwise operators
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
通过这个题目区分按位运算和逻辑运算的区别
具体的信息可以参考:https://wenku.csdn.net/answer/7f52c26fe1d9470db8f2090a0f44d3b1
four-input gates
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
vectors concatenation operator
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};
// assign { ... } = { ... };
endmodule
vector reversal 1
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
replication operator
module top_module (
input [7:0] in,
output [31:0] out );//
assign out={{24{in[7]}},in};
// assign out = { replicate-sign-bit , the-input };
endmodule
这里将8位数字扩展到了32位,8位中最高位是符号位,这种扩展是算术扩展。
区别于逻辑扩展直接在前面填0,算术扩展就是将符号位扩展,不改变真值。
逻辑扩展适合无符号数(unsigned)的扩展,算术扩展适合有符号数(signed)的扩展。
more replication
module top_module (
input a, b, c, d, e,
output [24:0] out );//
wire [24:0]vec1,vec2;
assign vec1={{5{a}},{5{b}},{5{c}},{5{d}},{5{e}}};
assign vec2={5{a,b,c,d,e}};
assign out=~(vec1^vec2);
// assign out = ~{ ... } ^ { ... };
endmodule
Modules:hierarchy
modules
module top_module ( input a, input b, output out );
mod_a(a,b,out);
endmodule
connecting ports by position
module top_module (
input a,
input b,
input c,
input d,
output out1,
output out2
);
mod_a(out1,out2,a,b,c,d);
endmodule
connecting ports by name
module top_module (
input a,
input b,
input c,
input d,
output out1,
output out2
);
mod_a(.out1(out1),.out2(out2),.in1(a),.in2(b),.in3(c),.in4(d));
endmodule
three modules
module top_module ( input clk, input d, output q );
wire q1,q2;
my_dff my_dff_1(clk,d,q1);
my_dff my_dff_2(clk,q1,q2);
my_dff my_dff_3(clk,q2,q);
endmodule
modules and vectors
module top_module (
input clk,
input [7:0] d,
input [1:0] sel,
output [7:0] q
);
wire [7:0] mido,midt,midf;//注意D触发器输出是8位q,所以这里的wire都是8位,不能漏掉[7:0]
my_dff8 my_dff8_1(clk,d,mido);
my_dff8 my_dff8_2(clk,mido,midt);
my_dff8 my_dff8_3(clk,midt,midf);
always @(*) begin//多路选择器
case(sel)
2'b00:q=d;
2'b01:q=mido;
2'b10:q=midt;
2'b11:q=midf;
endcase
end
endmodule
adder 1
module top_module(
input [31:0] a,
input [31:0] b,
output [31:0] sum
);
wire c;
add16 add16_1(a[15:0],b[15:0],1'b0,sum[15:0],c);
add16 add16_2(a[31:16],b[31:16],c,sum[31:16],1'b0);
endmodule
adder 2
module top_module (
input [31:0] a,
input [31:0] b,
output [31:0] sum
);//
wire c;
add16 add16_1(a[15:0],b[15:0],1'b0,sum[15:0],c);
add16 add16_2(a[31:16],b[31:16],c,sum[31:16],1'b0);
endmodule
module add1 ( input a, input b, input cin, output sum, output cout );
assign {cout,sum}=a+b+cin;
// Full adder module here
endmodule
carry -select adder
module top_module(
input [31:0] a,
input [31:0] b,
output [31:0] sum
);
wire sel;
wire [15:0]c,d;//c和d传输两个16bit的sum输出值,所以声明为16位
add16 add16_1(a[15:0],b[15:0],1'b0,sum[15:0],sel);
add16 add16_2(a[31:16],b[31:16],1'b0,c,1'b0);
add16 add16_3(a[31:16],b[31:16],1'b1,d,1'b0);
assign sum[31:16]=sel?d:c;
endmodule
adder-subtractor
module top_module(
input [31:0] a,
input [31:0] b,
input sub,
output [31:0] sum
);
wire [31:0]d;//XOR门的输出信号
wire c;
assign d=b^{32{sub}};//sub是一位信号,XOR门输入b和输出d都是32位,进行按位异或需要sub扩展到32位
add16 add16_1(a[15:0],d[15:0],sub,sum[15:0],c);
add16 add16_2(a[31:16],d[31:16],c,sum[31:16],1'b0);
endmodule
procedures
always blocks(combinational)
// 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
always blocks(clocked)
// 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
if statement
// 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;
always @(*)begin
if(sel_b1&sel_b2)
out_always=b;
else
out_always=a;
end
endmodule
if statement latches
latch错误:一个变量声明为寄存器时,它既可以被综合成触发器,也可能被综合成 Latch,甚至是 wire 型变量。但是大多数情况下我们希望它被综合成触发器,但是有时候由于代码书写问题,它会被综合成不期望的 Latch 结构。
Latch 的主要危害有:
1.输入状态可能多次变化,容易产生毛刺,增加了下一级电路的不确定性;
2.在大部分 FPGA 的资源中,可能需要比触发器更多的资源去实现 Latch 结构;
3.锁存器的出现使得静态时序分析变得更加复杂。
Latch 多用于门控时钟(clock gating)的控制,一般设计时,我们想要避免 Latch 的产生。
常见产生latch的原因:
1.if-else不完整:组合逻辑中,不完整的 if - else 结构,出现了没有设定过的状态,会产生 latch。
2.case语句不完整:组合逻辑中,case 语句产生 Latch 的原理和 if 语句一致。当 case 选项列表不全且没有加 default ,或多个赋值语句不完整时,出现了没有设定过的状态,会产生 Latch
3.组合逻辑中,always@() 块内敏感列表没有列全,该触发的时候没有触发,会产生latch
对应的解决办法:
1.补全 if-else 结构,或者对信号赋默认初值。
2.将 case 选项列表补充完整,或对信号赋初值。
3.把敏感信号补全或者直接用 always@(*) 。
回到本题,题给的带有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;
end
always @(*) begin
if (~arrived)
keep_driving = ~gas_tank_empty;
end
endmodule
对应生成的电路是这样的(This is the circuit described by the code, not the circuit you want to build.)
修改后(补全了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
case statement
// 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
priority encoder
这个题目学习的时候看到了下面的casez,所以学习了一下casez发现用casez更加简单干净,所以这里用casez实现
// synthesis verilog_input_version verilog_2001
module top_module (
input [3:0] in,
output reg [1:0] pos );
always @(*) begin
casez (in)
4'bzzz1: pos = 2'b00; // in[3:1] can be anything
4'bzz1z: pos = 2'b01;
4'bz1zz: pos = 2'b10;
4'b1zzz: pos = 2'b11;
default: pos = 2'b00;
endcase
end
endmodule
priority encoder with casez
// synthesis verilog_input_version verilog_2001
module top_module (
input [7:0] in,
output reg [2:0] pos );
always @(*)begin
casez(in)
8'b zzzzzzz1:pos=3'b000;
8'b zzzzzz1z:pos=3'b001;
8'b zzzzz1zz:pos=3'b010;
8'b zzzz1zzz:pos=3'b011;
8'b zzz1zzzz:pos=3'b100;
8'b zz1zzzzz:pos=3'b101;
8'b z1zzzzzz:pos=3'b110;
8'b 1zzzzzzz:pos=3'b111;
default:pos=3'b000;
endcase
end
endmodule
avoiding latches
// 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//always块中内容是顺序执行,所以赋值语句放在前面
up = 1'b0; down = 1'b0; left = 1'b0; right = 1'b0;//在if-else、case等语句前赋初值避免latch
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
more verilog features
conditional ternary operator
module top_module (
input [7:0] a, b, c, d,
output [7:0] min);//
wire [7:0]w1,w2;
//四个数比大小选最小,也就是四选一,本题介绍的三目运算符只能实现两个数比大小2选1,所以四个数分成两组分别选出小的,然后两个小的比较选出最小的,实现四选一选最小数。
assign w1=(a<b)?a:b;
assign w2=(c<d)?c:d;
assign min=(w1<w2)?w1:w2;
endmodule
reduction operators
module top_module (
input [7:0] in,
output parity);
assign parity=^in;
endmodule
reduction:even wider gates
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
combinational for-loop:vector reversal 2
module top_module(
input [99:0] in,
output [99:0] out
);
always @(*)begin
integer i;//for语句变量定义为整型变量integer
for(i=0;i<100;i++)//for是循环语句,循环语句只能在 always 或 initial 块中使用,类比C语言的for循环语句理解就好
out[i]=in[99-i];
end
endmodule
combinational for-loop:255-bit population count
//对255位输入向量中“1”的数量进行计数
module top_module(
input [254:0] in,
output [7:0] out );
always @(*)begin//always块中内容是顺序执行,所以赋值语句放在前面
integer i;
out=8'b0;//要对out赋初值,因为后面out=out+in[i]是对out的迭代,第一次运行这个式子时候需要一个out的初值
for(i=0;i<255;i++)
out=out+in[i];//数据流编写法
end
endmodule
generate for-loop:100-bit binary adder 2
题目要求通过例化100个一位全加器实现100位行波二进制加法器,例化多个相同的模块时,一个一个的手动例化会比较繁琐。用 generate 语句进行多个模块的重复例化,generate语句中定义的变量类型是genvar(为什么是这个我也不太懂)
//要求通过例化100个一位全加器实现100位行波加法器
module top_module(
input [99:0] a, b,
input cin,
output [99:0] cout,
output [99:0] sum );
generate//用generate语句进行100次一位全加器的例化
genvar i;
for(i=0;i<100;i++) begin:adder
if(i==0)
fulladder fulladder_0(a[i],b[i],cin,sum[i],cout[i]);
else
fulladder fulladder_i(a[i],b[i],cout[i-1],sum[i],cout[i]);
end
endgenerate
endmodule
module fulladder(input a,b,cin,output sum,cout);//编写一位全加器
assign {cout,sum}=a+b+cin;
endmodule
generate for-loop:100-digit BCD-adder
对比上一题不同的是,这里是100位十进制BCD加法器,可以先学习一下BCD编码,这里每4位二进制a/b/sum表示一个十进制位,[3:0]表示第0位,[7:4]表示第1位,[4*i+3:4*i]表示第i位,总共需要400位输入和400位输出。
要进行100次一位全加器的例化,同样通过generate实现。
module top_module(
input [399:0] a, b,
input cin,
output cout,
output [399:0] sum );
wire [100:0]c;//声明101位wire c连接所有一位全加器的cin和cout
assign c[0]=cin;
generate
genvar i;
for(i=0;i<100;i++)begin:bcd_fadd_loop
bcd_fadd bcd_fadd_f1(a[4*i+3:4*i],b[4*i+3:4*i],c[i],c[i+1],sum[4*i+3:4*i]);
end
endgenerate
assign cout=c[100];
endmodule