Verilog学习二:设计一个一位全加器

这篇具有很好参考价值的文章主要介绍了Verilog学习二:设计一个一位全加器。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本部分将不再介绍Vivado工程的整体流程,将主要精力放在代码上面,具体的流程可参考:https://blog.csdn.net/crodemese/article/details/130438348
本部分代码也已上传到github:https://github.com/linxunxr/VerilogStudy

1. 全加器

那么什么是全加器呢?我们都知道加法,即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

这就是一个全加器的真值表。

2. 设计思路

我们都知道Verilog最大的特点就是自顶向下的设计方式,因此我们尝试对上面的全加器进行模块划分,即使用两个半加器和一个或门来实现,其模块如下图:

这样就可以实现一个一位全加器。

3. 模块化实现

  1. 首先我们来实现一个简单的或门模块,其具有两输入一输出,因此在module命名中对其定义输入a b输出c,然后将ab进行或运算并赋值给c,如下:
// 或门模块
module or_gate (
    input a,
    input b,
    output c
);
    // 或运算 
    assign c = a | b;
endmodule
  1. 接下来我们来实现较为复杂的半加器,其具有两输入两输出,通过我们上面对半加器真值表的观察可知,输出的相加信号可视为两输入信号的异或运算,输出的进位信号可视为两输入信号的与运算,因此实现如下:
// 半加器模块
module half_adder (
    input a,
    input b,
    output sum,
    output carry
);
    // 异或运算完成相加
    assign sum = a ^ b;
    // 与运算完成进位
    assign carry = a & b;
endmodule
  1. 接下来就是根据设计思路通过对半加器模块和或门模块的引用来完成一位全加器的顶层模块设计:
  2. 其基本语法仍然是赋值语句,只不过多了运算符的操作,与C语言类似,Verilog也有多种运算符(默认具有C语言基础,仅对与C语言不同的进行解释):
    1. 算数运算符:
      1. +:加法运算符,或正值运算符
      2. -:减法运算符,或负值运算符
      3. *:乘法运算符
      4. /:除法运算符
      5. %:模运算符
    2. 位运算符:
      1. ~:取反
      2. &:按位与
      3. |:按位或
      4. ^:按位异或
      5. ^~:按位同或
    3. 逻辑运算符:
      1. &&:逻辑与
      2. ||:逻辑或
      3. !:逻辑非
    4. 关系运算符:
      1. <:小于
      2. >:大于
      3. <=:小于等于
      4. >=:大于等于
    5. 等式运算符:
      1. ==:等于
      2. !=:不等于
      3. ===:等于,与上面的等于不同的是,这里的等于对两个操作数的要求是严格的,即对不定值x和高阻值z也会进行比较,只有两端严格相等时其结果才为1,因此其结果不会出现不定值x和高阻值z,下面的不等于类似。
      4. !==:不等于
    6. 移位运算符:
      1. <<:左移运算符
      2. >>:右移运算符
    7. 位拼接运算符:
      1. {}使用大括号可以将两个或多个信号的某些位拼接起来进行运算操作,其使用方法如下:{信号1的某几位,信号2的某几位,...,信号n的某几位}
      2. 如果我们需要将a信号和b信号进行拼接,可用{a,b}来进行操作
      3. 除此之外,还可以通过重复发来进行简化操作,如{4{w}}其相当于{w,w,w,w}
      4. 可以通过嵌套的方式来表达,如{b,{3{a,b}}}其相当于{b,a,b,a,b,a,b}
    8. 缩减运算符:缩减运算符是单目运算符,其也有与、或、非运算,但是与位运算不同的是,缩减运算符是对单个操作数进行或、与、非运算,最后的结果为1位的二进制数,如与运算:
// 全加器模块
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;
  1. 除此之外我们还需要对Verilog的数据类型有一定的了解:
    1. 常量
      1. 数字:
        1. 整数:
          1. 二进制整数:b或B
          2. 八进制整数:o或O
          3. 十进制整数:d或D
          4. 十六进制整数:h或H
          5. 其表示方法有三种:
            1. <位宽><进制><数字>,如2'b00就是一个两位的二进制数字00
            2. <进制><数字>,在这种情况下,数字的位宽采用默认位宽(由机器系统决定),最少为32位,因此不推荐
            3. <数字>,在这种描述方式中,采用默认进制(十进制),但是描述不清,一般仍采用第一种表示方法
        2. xz值:数电中,x代表不定值,z值代表高阻值。如:
          1. 4'b10x0表示4位的二进制数从地位数起的第二位为不定值
          2. 12'dz或12'd?表示12为十进制数,其值为高阻值
          3. 8'h4x表示8位十六进制,其低4位为不定值
        3. 负数:在位宽表达式前加上一个减号,如:-8'd5
        4. 下划线:可以具体的数字之间分隔开来以提高程序可读性,如:16'b1010_0000_1111_0101
      2. 参数:即用parameter定义一个标识符代表一个常量,以提高程序可读性和可维护性,常用于定义延迟时间和变量宽度。如:parameter msb = 7;定义参数msb为常量7。
    2. 变量:
      1. wire型:常用来表示用以assign关键字指定的组合逻辑信号,Verilog模块中的输入和输出信号默认自动定义为wire型。可用做任何方程式的输入,也可以用做assign语句或实例元件的输出。其定义格式如下:wire [n-1:0] 数据名1,数据名2,...,数据名i,其表示共有i条总线,每条总线内有n条线路
      2. reg型:即寄存器数据类型,通过赋值语句可改变寄存器存储的值,其初始值为不定值x,因此在使用reg型时一般需要赋初值,否则在仿真中会出现不定值x,无法通过仿真确认其结果。同时reg型只表示被定义的信号将用在always模块,在always模块内被赋值的每一个信号都必须被定义成reg型。其定义格式如下:reg [n-1:0] 数据名1,数据名2,...,数据名i
      3. memory型:通过对reg型变量建立数组来对存储器建模,可以描述RAM型存储器、ROM存储器和reg文件。数组中的每一个单元通过一个数组索引进行寻址。其定义格式如下:reg [n-1:0] 存储器名[m-1:0];

4.仿真文件

在生成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。— highlight: github

本部分将不再介绍Vivado工程的整体流程,将主要精力放在代码上面,具体的流程可参考:Verilog学习一: 控制LED灯的亮灭 - 掘金 (juejin.cn)

1. 全加器

那么什么是全加器呢?我们都知道加法,即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

这就是一个全加器的真值表。

2. 设计思路

我们都知道Verilog最大的特点就是自顶向下的设计方式,因此我们尝试对上面的全加器进行模块划分,即使用两个半加器和一个或门来实现,其模块如下图:

这样就可以实现一个一位全加器。

3. 模块化实现

  1. 首先我们来实现一个简单的或门模块,其具有两输入一输出,因此在module命名中对其定义输入a b输出c,然后将ab进行或运算并赋值给c,如下:
// 或门模块
module or_gate (
    input a,
    input b,
    output c
);
    // 或运算 
    assign c = a | b;
endmodule
  1. 接下来我们来实现较为复杂的半加器,其具有两输入两输出,通过我们上面对半加器真值表的观察可知,输出的相加信号可视为两输入信号的异或运算,输出的进位信号可视为两输入信号的与运算,因此实现如下:
// 半加器模块
module half_adder (
    input a,
    input b,
    output sum,
    output carry
);
    // 异或运算完成相加
    assign sum = a ^ b;
    // 与运算完成进位
    assign carry = a & b;
endmodule
  1. 接下来就是根据设计思路通过对半加器模块和或门模块的引用来完成一位全加器的顶层模块设计:
  2. 其基本语法仍然是赋值语句,只不过多了运算符的操作,与C语言类似,Verilog也有多种运算符(默认具有C语言基础,仅对与C语言不同的进行解释):
    1. 算数运算符:
      1. +:加法运算符,或正值运算符
      2. -:减法运算符,或负值运算符
      3. *:乘法运算符
      4. /:除法运算符
      5. %:模运算符
    2. 位运算符:
      1. ~:取反
      2. &:按位与
      3. |:按位或
      4. ^:按位异或
      5. ^~:按位同或
    3. 逻辑运算符:
      1. &&:逻辑与
      2. ||:逻辑或
      3. !:逻辑非
    4. 关系运算符:
      1. <:小于
      2. >:大于
      3. <=:小于等于
      4. >=:大于等于
    5. 等式运算符:
      1. ==:等于
      2. !=:不等于
      3. ===:等于,与上面的等于不同的是,这里的等于对两个操作数的要求是严格的,即对不定值x和高阻值z也会进行比较,只有两端严格相等时其结果才为1,因此其结果不会出现不定值x和高阻值z,下面的不等于类似。
      4. !==:不等于
    6. 移位运算符:
      1. <<:左移运算符
      2. >>:右移运算符
    7. 位拼接运算符:
      1. {}使用大括号可以将两个或多个信号的某些位拼接起来进行运算操作,其使用方法如下:{信号1的某几位,信号2的某几位,...,信号n的某几位}
      2. 如果我们需要将a信号和b信号进行拼接,可用{a,b}来进行操作
      3. 除此之外,还可以通过重复发来进行简化操作,如{4{w}}其相当于{w,w,w,w}
      4. 可以通过嵌套的方式来表达,如{b,{3{a,b}}}其相当于{b,a,b,a,b,a,b}
    8. 缩减运算符:缩减运算符是单目运算符,其也有与、或、非运算,但是与位运算不同的是,缩减运算符是对单个操作数进行或、与、非运算,最后的结果为1位的二进制数,如与运算:
// 全加器模块
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;
  1. 除此之外我们还需要对Verilog的数据类型有一定的了解:
    1. 常量
      1. 数字:
        1. 整数:
          1. 二进制整数:b或B
          2. 八进制整数:o或O
          3. 十进制整数:d或D
          4. 十六进制整数:h或H
          5. 其表示方法有三种:
            1. <位宽><进制><数字>,如2'b00就是一个两位的二进制数字00
            2. <进制><数字>,在这种情况下,数字的位宽采用默认位宽(由机器系统决定),最少为32位,因此不推荐
            3. <数字>,在这种描述方式中,采用默认进制(十进制),但是描述不清,一般仍采用第一种表示方法
        2. xz值:数电中,x代表不定值,z值代表高阻值。如:
          1. 4'b10x0表示4位的二进制数从地位数起的第二位为不定值
          2. 12'dz或12'd?表示12为十进制数,其值为高阻值
          3. 8'h4x表示8位十六进制,其低4位为不定值
        3. 负数:在位宽表达式前加上一个减号,如:-8'd5
        4. 下划线:可以具体的数字之间分隔开来以提高程序可读性,如:16'b1010_0000_1111_0101
      2. 参数:即用parameter定义一个标识符代表一个常量,以提高程序可读性和可维护性,常用于定义延迟时间和变量宽度。如:parameter msb = 7;定义参数msb为常量7。
    2. 变量:
      1. wire型:常用来表示用以assign关键字指定的组合逻辑信号,Verilog模块中的输入和输出信号默认自动定义为wire型。可用做任何方程式的输入,也可以用做assign语句或实例元件的输出。其定义格式如下:wire [n-1:0] 数据名1,数据名2,...,数据名i,其表示共有i条总线,每条总线内有n条线路
      2. reg型:即寄存器数据类型,通过赋值语句可改变寄存器存储的值,其初始值为不定值x,因此在使用reg型时一般需要赋初值,否则在仿真中会出现不定值x,无法通过仿真确认其结果。同时reg型只表示被定义的信号将用在always模块,在always模块内被赋值的每一个信号都必须被定义成reg型。其定义格式如下:reg [n-1:0] 数据名1,数据名2,...,数据名i
      3. memory型:通过对reg型变量建立数组来对存储器建模,可以描述RAM型存储器、ROM存储器和reg文件。数组中的每一个单元通过一个数组索引进行寻址。其定义格式如下:reg [n-1:0] 存储器名[m-1:0];

4.仿真文件

在生成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。文章来源地址https://www.toymoban.com/news/detail-713818.html

到了这里,关于Verilog学习二:设计一个一位全加器的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • FPGA编程入门——实现一位全加器

    然后通过4个1位全加器的串行级联,完成一个4位全加器的原理图设计;再改用 Verilog编程(3种模式:门电路、数据流和行为级描述),完成这个4位全加器设计,并观察Verilog代码编译综合后生成的 RTL电路,与之前电路图设计的4位全加器电路进行对比 。 如果仿真波形的逻辑功

    2024年02月04日
    浏览(31)
  • 【无标题】FPGA编程入门——实现一位全加器

    1、首先基于Quartus 软件采用原理图输入方法完成一个1位全加器的设计。然后通过4个1位全加器的串行级联,完成一个4位全加器的原理图设计;再改用 Verilog编程(3种模式:门电路、数据流和行为级描述),完成这个4位全加器设计,并观察Verilog代码编译综合后生成的 RTL电路,

    2024年02月03日
    浏览(33)
  • FPGA面试题【Verilog实现一个2位带进位全加器,画出门级电路】

    目录 题目 核心思路 答案 FPGA全貌 Verilog实现一个2位带进位全加器,画出门级电路 思路见代码注释   第1节 什么是 FPGA FPGA 的全称为 Field-Programmable Gate Array,即现场可编程门阵列。 FPGA 是在 PAL、 GAL、 CPLD 等可编程器件的基础上进一步发展的产物, 是作为专用集成电路( ASI

    2024年02月08日
    浏览(37)
  • 【FPGA】Verilog:模块化组合逻辑电路设计 | 半加器 | 全加器 | 串行加法器 | 子模块 | 主模块

    前言: 本章内容主要是演示Vivado下利用Verilog语言进行电路设计、仿真、综合和下载 示例:加法器   ​ 功能特性: 采用 Xilinx Artix-7 XC7A35T芯片  配置方式:USB-JTAG/SPI Flash 高达100MHz 的内部时钟速度  存储器:2Mbit SRAM   N25Q064A SPI Flash(样图旧款为N25Q032A) 通用IO:Switch :

    2024年02月15日
    浏览(34)
  • 【Verilog】期末复习——设计带进位输入和输出的8位全加器,包括测试模块

    数值(整数,实数,字符串)与数据类型(wire、reg、mem、parameter) 运算符 数据流建模 行为级建模 结构化建模 组合电路的设计和时序电路的设计 有限状态机的定义和分类 期末复习——数字逻辑电路分为哪两类?它们各自的特点是什么? 期末复习——VerilogHDL描述数字逻辑电

    2024年01月23日
    浏览(27)
  • 实验四 QUARTUS开发环境实验 设计半加器、全加器和四位全加器 blueee的学习笔记

    一、实验目的 1、通过实验,能熟悉QUARTUS开发环境,能够掌握QUARTUS的原理图输入法设计电路,掌握使用相关仿真工具进行功能和时序仿真的方法; 2、通过实验,加深对全加器电路的理解,并能使用QUARTUS的原理图输入法完成全加器的设计,并能在QUARTUS中完成相关的仿真验证

    2024年02月05日
    浏览(54)
  • FPGA编程入门——基于Quartus件完成一个1位全加器的设计

    基于Quartus件完成一个1位全加器的设计,分别采用:1)原理图输入 以及 2)Verilog编程 这两种设计方法。开发板基于Intel DE2-115。 在此基础上,用原理图以及Verilog 编程两种方式,完成4位全加器的设计,对比二者生成的 RTL差别;使用modelsim验证逻辑设计的正确性,并在DE2-115开

    2024年02月05日
    浏览(46)
  • verilog全加器和四位加法器

    半加器: 保存为half_addr.bsf之后,可以在该项目中添加半加器 全加器: 通过RTL-Viewer查看半加器和全加器 添加全加器到项目 在process里面先后执行start fitter 和start time analyzer 生成testbench模板 修改testbench文件: 添加到项目 注意在联合modelsim时让generate functinal simulation netlist值为

    2024年02月07日
    浏览(33)
  • 基于Verilog与器件图的1位全加器实现

    1位二进制数全加器是一个具有三个输入端和两个输出端的,能对被加数、加数以及来自低位的进位相加得到“全加和”与“全加进位”。 它的真值表如下: Ai Bi Ci-1 Ci Si 0 0 0 0 0 0 0 1 0 1 0 1 0 0 1 0 1 1 1 0 1 0 0 0 1 1 0 1 1 0 1 1 0 1 0 1 1 1 1 1 它的逻辑表达式如下: 从仿真的结果来看,

    2024年02月07日
    浏览(31)
  • 用Verilog编写1位全加器,并进行波形仿真

    Quartus II+ModelSiml 全加器英语名称为full-adder,是用门电路实现两个二进制数相加并求出和的组合线路,称为一位全加器。一位全加器可以处理低位进位,并输出本位加法进位。

    2024年02月11日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包