赞
踩
本部分将不再介绍Vivado工程的整体流程,将主要精力放在代码上面,具体的流程可参考:https://blog.csdn.net/crodemese/article/details/130438348
本部分代码也已上传到github:https://github.com/linxunxr/VerilogStudy
那么什么是全加器呢?我们都知道加法,即1+1=2,当个位数相加大于9时就需要进位。在二进制中也是如此,因此,一位二进制的相加的真值表便如下图:
a | b | sum | count |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 1 | 1 | 0 |
1 | 0 | 1 | 0 |
1 | 1 | 0 | 1 |
图中a、b为输出,sum为相加的结果,count为进位,即当输入都为1时,相加的结果为0,进位信号输出1。这就是一个一位半加器。而全加器就是在此基础上再加一个输入,这个输入用来获取前一个的进位信号,其真值表如下图:
count | a | b | sum | count |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 | 0 |
0 | 1 | 0 | 1 | 0 |
0 | 1 | 1 | 0 | 1 |
1 | 0 | 0 | 1 | 0 |
1 | 0 | 1 | 0 | 1 |
1 | 1 | 0 | 0 | 1 |
1 | 1 | 1 | 1 | 1 |
这就是一个全加器的真值表。
我们都知道Verilog最大的特点就是自顶向下的设计方式,因此我们尝试对上面的全加器进行模块划分,即使用两个半加器和一个或门来实现,其模块如下图:
这样就可以实现一个一位全加器。
module
命名中对其定义输入a b
输出c
,然后将a
和b
进行或运算并赋值给c
,如下:// 或门模块
module or_gate (
input a,
input b,
output c
);
// 或运算
assign c = a | b;
endmodule
// 半加器模块
module half_adder (
input a,
input b,
output sum,
output carry
);
// 异或运算完成相加
assign sum = a ^ b;
// 与运算完成进位
assign carry = a & b;
endmodule
+
:加法运算符,或正值运算符-
:减法运算符,或负值运算符*
:乘法运算符/
:除法运算符%
:模运算符~
:取反&
:按位与|
:按位或^
:按位异或^~
:按位同或&&
:逻辑与||
:逻辑或!
:逻辑非<
:小于>
:大于<=
:小于等于>=
:大于等于==
:等于!=
:不等于===
:等于,与上面的等于不同的是,这里的等于对两个操作数的要求是严格的,即对不定值x
和高阻值z
也会进行比较,只有两端严格相等时其结果才为1
,因此其结果不会出现不定值x
和高阻值z
,下面的不等于类似。!==
:不等于<<
:左移运算符>>
:右移运算符{}
使用大括号可以将两个或多个信号的某些位拼接起来进行运算操作,其使用方法如下:{信号1的某几位,信号2的某几位,...,信号n的某几位}
a
信号和b
信号进行拼接,可用{a,b}
来进行操作{4{w}}
其相当于{w,w,w,w}
{b,{3{a,b}}}
其相当于{b,a,b,a,b,a,b}
// 全加器模块 module full_adder ( input a, input b, input cin, output sum, output cout ); // 对中间的变量进行定义 wire s1, c1, c2; // 对半加器的实例化 half_adder half_adder1(a, b, s1, c1); half_adder half_adder2(s1, cin, sum, c2); // 对或门的实例化 or_gate or_gate1(c1, c2, cout); endmodule
reg [3:0] B;
reg C;
// 对B进行缩减与运算,其运算过程相当于 C = ((B[0] & B[1]) & B[2]) & B[3]
C = &B;
b或B
o或O
d或D
h或H
2'b00
就是一个两位的二进制数字00
x
和z
值:数电中,x
代表不定值,z
值代表高阻值。如:
4'b10x0
表示4位的二进制数从地位数起的第二位为不定值12'dz或12'd?
表示12为十进制数,其值为高阻值8'h4x
表示8位十六进制,其低4位为不定值-8'd5
16'b1010_0000_1111_0101
parameter
定义一个标识符代表一个常量,以提高程序可读性和可维护性,常用于定义延迟时间和变量宽度。如:parameter msb = 7;
定义参数msb
为常量7。wire
型:常用来表示用以assign
关键字指定的组合逻辑信号,Verilog模块中的输入和输出信号默认自动定义为wire
型。可用做任何方程式的输入,也可以用做assign
语句或实例元件的输出。其定义格式如下:wire [n-1:0] 数据名1,数据名2,...,数据名i
,其表示共有i条总线,每条总线内有n条线路reg
型:即寄存器数据类型,通过赋值语句可改变寄存器存储的值,其初始值为不定值x,因此在使用reg
型时一般需要赋初值,否则在仿真中会出现不定值x,无法通过仿真确认其结果。同时reg
型只表示被定义的信号将用在always
模块,在always
模块内被赋值的每一个信号都必须被定义成reg
型。其定义格式如下:reg [n-1:0] 数据名1,数据名2,...,数据名i
。memory
型:通过对reg
型变量建立数组来对存储器建模,可以描述RAM型存储器、ROM存储器和reg文件。数组中的每一个单元通过一个数组索引进行寻址。其定义格式如下:reg [n-1:0] 存储器名[m-1:0];
在生成bit文件下载到板子之前,我们一般会通过编写仿真文件对已完成的模块进行验证,其仿真文件如下:
module full_adder_test_top; // 定义输入到全加器的信号 reg a_test, b_test, cin_test; // 定义从全加器输出的信号 wire sum_test, cout_test; // 将输入信号和输出信号与全加器相连接 full_adder full_adder_test( .a(a_test), .b(b_test), .cin(cin_test), .sum(sum_test), .cout(cout_test) ); // 开始仿真 initial begin // 初识时刻为0,对各个输入信号进行初始化 a_test = 0; b_test = 0; cin_test = 0; // 过了20ns后改变信号值 #20 a_test = 1; b_test = 0; cin_test = 0; #20 a_test = 1; b_test = 1; cin_test = 0; #20 a_test = 1; b_test = 1; cin_test = 1; #20 a_test = 0; b_test = 0; cin_test = 1; // 调用系统函数使仿真停止 #20 $stop; end endmodule
reg
型,输出一般使用wire
型;第三,使用initial
块确定仿真过程中的各种输入值的初始化以及赋值;第四,其基本单位为ns。—本部分将不再介绍Vivado工程的整体流程,将主要精力放在代码上面,具体的流程可参考:Verilog学习一: 控制LED灯的亮灭 - 掘金 (juejin.cn)
那么什么是全加器呢?我们都知道加法,即1+1=2,当个位数相加大于9时就需要进位。在二进制中也是如此,因此,一位二进制的相加的真值表便如下图:
a | b | sum | count |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 1 | 1 | 0 |
1 | 0 | 1 | 0 |
1 | 1 | 0 | 1 |
图中a、b为输出,sum为相加的结果,count为进位,即当输入都为1时,相加的结果为0,进位信号输出1。这就是一个一位半加器。而全加器就是在此基础上再加一个输入,这个输入用来获取前一个的进位信号,其真值表如下图:
count | a | b | sum | count |
---|---|---|---|---|
0 | 0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 | 0 |
0 | 1 | 0 | 1 | 0 |
0 | 1 | 1 | 0 | 1 |
1 | 0 | 0 | 1 | 0 |
1 | 0 | 1 | 0 | 1 |
1 | 1 | 0 | 0 | 1 |
1 | 1 | 1 | 1 | 1 |
这就是一个全加器的真值表。
我们都知道Verilog最大的特点就是自顶向下的设计方式,因此我们尝试对上面的全加器进行模块划分,即使用两个半加器和一个或门来实现,其模块如下图:
这样就可以实现一个一位全加器。
module
命名中对其定义输入a b
输出c
,然后将a
和b
进行或运算并赋值给c
,如下:// 或门模块
module or_gate (
input a,
input b,
output c
);
// 或运算
assign c = a | b;
endmodule
// 半加器模块
module half_adder (
input a,
input b,
output sum,
output carry
);
// 异或运算完成相加
assign sum = a ^ b;
// 与运算完成进位
assign carry = a & b;
endmodule
+
:加法运算符,或正值运算符-
:减法运算符,或负值运算符*
:乘法运算符/
:除法运算符%
:模运算符~
:取反&
:按位与|
:按位或^
:按位异或^~
:按位同或&&
:逻辑与||
:逻辑或!
:逻辑非<
:小于>
:大于<=
:小于等于>=
:大于等于==
:等于!=
:不等于===
:等于,与上面的等于不同的是,这里的等于对两个操作数的要求是严格的,即对不定值x
和高阻值z
也会进行比较,只有两端严格相等时其结果才为1
,因此其结果不会出现不定值x
和高阻值z
,下面的不等于类似。!==
:不等于<<
:左移运算符>>
:右移运算符{}
使用大括号可以将两个或多个信号的某些位拼接起来进行运算操作,其使用方法如下:{信号1的某几位,信号2的某几位,...,信号n的某几位}
a
信号和b
信号进行拼接,可用{a,b}
来进行操作{4{w}}
其相当于{w,w,w,w}
{b,{3{a,b}}}
其相当于{b,a,b,a,b,a,b}
// 全加器模块 module full_adder ( input a, input b, input cin, output sum, output cout ); // 对中间的变量进行定义 wire s1, c1, c2; // 对半加器的实例化 half_adder half_adder1(a, b, s1, c1); half_adder half_adder2(s1, cin, sum, c2); // 对或门的实例化 or_gate or_gate1(c1, c2, cout); endmodule
reg [3:0] B;
reg C;
// 对B进行缩减与运算,其运算过程相当于 C = ((B[0] & B[1]) & B[2]) & B[3]
C = &B;
b或B
o或O
d或D
h或H
2'b00
就是一个两位的二进制数字00
x
和z
值:数电中,x
代表不定值,z
值代表高阻值。如:
4'b10x0
表示4位的二进制数从地位数起的第二位为不定值12'dz或12'd?
表示12为十进制数,其值为高阻值8'h4x
表示8位十六进制,其低4位为不定值-8'd5
16'b1010_0000_1111_0101
parameter
定义一个标识符代表一个常量,以提高程序可读性和可维护性,常用于定义延迟时间和变量宽度。如:parameter msb = 7;
定义参数msb
为常量7。wire
型:常用来表示用以assign
关键字指定的组合逻辑信号,Verilog模块中的输入和输出信号默认自动定义为wire
型。可用做任何方程式的输入,也可以用做assign
语句或实例元件的输出。其定义格式如下:wire [n-1:0] 数据名1,数据名2,...,数据名i
,其表示共有i条总线,每条总线内有n条线路reg
型:即寄存器数据类型,通过赋值语句可改变寄存器存储的值,其初始值为不定值x,因此在使用reg
型时一般需要赋初值,否则在仿真中会出现不定值x,无法通过仿真确认其结果。同时reg
型只表示被定义的信号将用在always
模块,在always
模块内被赋值的每一个信号都必须被定义成reg
型。其定义格式如下:reg [n-1:0] 数据名1,数据名2,...,数据名i
。memory
型:通过对reg
型变量建立数组来对存储器建模,可以描述RAM型存储器、ROM存储器和reg文件。数组中的每一个单元通过一个数组索引进行寻址。其定义格式如下:reg [n-1:0] 存储器名[m-1:0];
在生成bit文件下载到板子之前,我们一般会通过编写仿真文件对已完成的模块进行验证,其仿真文件如下:
module full_adder_test_top; // 定义输入到全加器的信号 reg a_test, b_test, cin_test; // 定义从全加器输出的信号 wire sum_test, cout_test; // 将输入信号和输出信号与全加器相连接 full_adder full_adder_test( .a(a_test), .b(b_test), .cin(cin_test), .sum(sum_test), .cout(cout_test) ); // 开始仿真 initial begin // 初识时刻为0,对各个输入信号进行初始化 a_test = 0; b_test = 0; cin_test = 0; // 过了20ns后改变信号值 #20 a_test = 1; b_test = 0; cin_test = 0; #20 a_test = 1; b_test = 1; cin_test = 0; #20 a_test = 1; b_test = 1; cin_test = 1; #20 a_test = 0; b_test = 0; cin_test = 1; // 调用系统函数使仿真停止 #20 $stop; end endmodule
从这里我们看到一般仿真文件的编写规则:首先,仿真模块无输入无输出;第二,定义仿真的全加器输入信号一般使用reg
型,输出一般使用wire
型;第三,使用initial
块确定仿真过程中的各种输入值的初始化以及赋值;第四,其基本单位为ns。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。