Verilog设计实例(4)详解全类别加法器(一)

您所在的位置:网站首页 verilogbcd加法器代码 Verilog设计实例(4)详解全类别加法器(一)

Verilog设计实例(4)详解全类别加法器(一)

2024-07-16 08:31| 来源: 网络整理| 查看: 265

写在前面正文半加器全加器纹波进位加法器参考资料交个朋友写在前面

博客首页[1]

本文详细地总结了一系列的加法器,包括半加器、全加器、等波纹进位加法器,虽然FPGA设计工程师不会设计这些东西作为模块来使用,因为综合工具足够智能,能够识别数据相加,但作为训练材料不失为一种不错的选择。

正文

❖ ❖ ❖

半加器

半加器是新数字设计师的基本构建块。半加器显示了如何用几个逻辑门将两个位相加。实际上,它们不常用,因为它们仅限于两个1位输入。为了将更大的数字加在一起,可以使用全加器。一个半加法器具有两个一位输入,一个求和输出和一个进位输出。请参考下面的真值表以了解这些位的工作方式。接下来会给出创建半加器的Verilog描述以及仿真测试平台。

「Half Adder Truth Table」

A

B

Carry

Sum

0

0

0

0

1

0

0

1

0

1

0

1

1

1

1

0

很容易看出进位是相与,和是异或。

设计代码代码语言:javascript复制module half_adder( input i_bit1, input i_bit2, output o_carry, output o_sum ); assign o_carry = i_bit1 & i_bit2; //bitwise and assign o_sum = i_bit1 ^ i_bit2; //bitwise xor endmodule测试文件代码语言:javascript复制module half_adder_tb; reg i_bit1; reg i_bit2; wire o_carry; wire o_sum; initial begin i_bit1 = 0; i_bit2 = 0; # 10 i_bit1 = 0; i_bit2 = 1; # 10 i_bit1 = 1; i_bit2 = 0; # 10 i_bit1 = 1; i_bit2 = 1; #10 $finish; end // Monitor values of these variables and print them into the log file for debug initial $monitor ("i_bit1 = %0b, i_bit2 = %0b, o_sum = %0b, o_carry = %0b", i_bit1, i_bit2, o_sum, o_carry); half_adder inst_half_adder( .i_bit1(i_bit1), .i_bit2(i_bit2), .o_sum(o_sum), .o_carry(o_carry) ); endmodule行为仿真波形图

「log file」

代码语言:javascript复制i_bit1 = 0, i_bit2 = 0, o_sum = 0, o_carry = 0 i_bit1 = 0, i_bit2 = 1, o_sum = 1, o_carry = 0 i_bit1 = 1, i_bit2 = 0, o_sum = 1, o_carry = 0 i_bit1 = 1, i_bit2 = 1, o_sum = 0, o_carry = 1

❖ ❖ ❖

全加器

全加器也是新数字设计师的基本构建块。许多数字设计入门课程向初学者全面介绍。一旦了解了全加法器的工作原理,就可以看到仅使用简单的门就可以构建更复杂的电路。不过要说清楚的是,实际上,FPGA设计人员并不是手工编写完整的加法器。工具已足够先进到可以知道如何将两个数字相加。但这仍然是一个很好的练习,这就是为什么要在这里进行介绍。

单个全加器具有两个一位输入,一个进位输入,一个求和输出和一个进位输出。它们中的许多可以一起使用以创建纹波进位加法器,该纹波进位加法器可以用于将大数相加。单个全加器如下图所示。

「全加器的真值表如下:」

「Full Adder Truth Table」

A

B

Cin

Cout

Sum

0

0

0

0

0

1

0

0

0

1

0

1

0

0

1

1

1

0

1

0

0

0

1

0

1

1

0

1

1

0

0

1

1

1

0

1

1

1

1

1

由真值表可以得出全加器的进位输出以及和的电路(表达式):

在这里插入图片描述

设计文件

可以直接看出实现上述加法器的方式有三种:

第一种:代码语言:javascript复制//More clear method wire w_WIRE_1; wire w_WIRE_2; wire w_WIRE_3; assign w_WIRE_1 = i_bit1 ^ i_bit2; assign w_WIRE_2 = w_WIRE_1 & i_carry; assign w_WIRE_3 = i_bit1 & i_bit2; assign o_sum = w_WIRE_1 ^ i_carry; assign o_carry = w_WIRE_2 | w_WIRE_3;第二种:代码语言:javascript复制 assign o_sum = i_bit1 ^ i_bit2 ^ i_carry; assign o_carry = (i_bit1 ^ i_bit2) & i_carry) | (i_bit1 & i_bit2);第三种代码语言:javascript复制 assign {o_carry, o_sum} = i_bit1 + i_bit2 + i_carry;

无疑,第一种和 第二种等价,那么第三种呢?是否和第一二种生成的结构等价呢?这里以Vivado为例,看其如何综合:

第一种、第二种:

「RTL 原理图」

在这里插入图片描述

「综合之后原理图」

在这里插入图片描述

第三种:

「RTL 原理图」

在这里插入图片描述

「综合之后的原理图」

在这里插入图片描述

对比第一种第二种就可以发现,综合后的原理图是一致的,这已经说明综合工具已足够强大,不需要我们从RTL级别描述,而直接描述其行为也可。

如果有不清楚Verilog的描述方式的区别,这里推荐看下Verilog的三种描述方式:

【 Verilog HDL 】HDL的三种描述方式[2]

设计完整文件代码语言:javascript复制`timescale 1ns / 1ps /////////////////////////////////////////////////// // Engineer: Reborn Lee // Module Name: full_adder // https://blog.csdn.net/Reborn_Lee //////////////////////////////////////////////////// module full_adder( input i_bit1, input i_bit2, input i_carry, output o_sum, output o_carry ); assign o_sum = i_bit1 ^ i_bit2 ^ i_carry; assign o_carry = ((i_bit1 ^ i_bit2) & i_carry) | (i_bit1 & i_bit2); // More clear method // wire w_WIRE_1; // wire w_WIRE_2; // wire w_WIRE_3; // assign w_WIRE_1 = i_bit1 ^ i_bit2; // assign w_WIRE_2 = w_WIRE_1 & i_carry; // assign w_WIRE_3 = i_bit1 & i_bit2; // assign o_sum = w_WIRE_1 ^ i_carry; // assign o_carry = w_WIRE_2 | w_WIRE_3; // The third method // assign {o_carry, o_sum} = i_bit1 + i_bit2 + i_carry; endmodule 行为仿真

验证三种等价方式:

「第一种、第二种仿真图:」

代码语言:javascript复制i_bit1 = 0, i_bit2 = 0, i_carry = 0, o_sum = 0, o_carry = 0 i_bit1 = 0, i_bit2 = 0, i_carry = 1, o_sum = 1, o_carry = 0 i_bit1 = 0, i_bit2 = 1, i_carry = 0, o_sum = 1, o_carry = 0 i_bit1 = 0, i_bit2 = 1, i_carry = 1, o_sum = 0, o_carry = 1 i_bit1 = 1, i_bit2 = 0, i_carry = 0, o_sum = 1, o_carry = 0 i_bit1 = 1, i_bit2 = 0, i_carry = 1, o_sum = 0, o_carry = 1 i_bit1 = 1, i_bit2 = 1, i_carry = 0, o_sum = 0, o_carry = 1 i_bit1 = 1, i_bit2 = 1, i_carry = 1, o_sum = 1, o_carry = 1

「第三种仿真图:」

在这里插入图片描述

代码语言:javascript复制i_bit1 = 0, i_bit2 = 0, i_carry = 0, o_sum = 0, o_carry = 0 i_bit1 = 0, i_bit2 = 0, i_carry = 1, o_sum = 1, o_carry = 0 i_bit1 = 0, i_bit2 = 1, i_carry = 0, o_sum = 1, o_carry = 0 i_bit1 = 0, i_bit2 = 1, i_carry = 1, o_sum = 0, o_carry = 1 i_bit1 = 1, i_bit2 = 0, i_carry = 0, o_sum = 1, o_carry = 0 i_bit1 = 1, i_bit2 = 0, i_carry = 1, o_sum = 0, o_carry = 1 i_bit1 = 1, i_bit2 = 1, i_carry = 0, o_sum = 0, o_carry = 1 i_bit1 = 1, i_bit2 = 1, i_carry = 1, o_sum = 1, o_carry = 1

必然也是没有任何问题的!

❖ ❖ ❖

纹波进位加法器

纹波进位加法器由许多级联在一起的全加法器组成。它仅通过简单的逻辑门就可以将两个二进制数相加。下图显示了连接在一起以产生4位纹波进位加法器的4个全加器。

在这里插入图片描述

同样需要指出的是,FPGA设计人员通常不需要手动实现纹波进位加法器。FPGA工具足够聪明,足以知道如何将两个二进制数相加。本练习的目的是说明基本电路如何工作以执行简单的任务。对于初学者来说,这是一个很好的例子。

本文先实现一个2bits 的数据等波纹加法,之后采用generate for的方式实现任意位数数据的等波纹加法。

2bit数据等波纹加法设计

「设计文件」

代码语言:javascript复制`include "full_adder.v" module ripple_carry_adder_2 ( input [1:0] i_add_term1, input [1:0] i_add_term2, output [2:0] o_result ); wire [2:0] w_CARRY; wire [1:0] w_SUM; // No carry input on first full adder assign w_CARRY[0] = 1'b0; full_adder full_adder_1 ( .i_bit1(i_add_term1[0]), .i_bit2(i_add_term2[0]), .i_carry(w_CARRY[0]), .o_sum(w_SUM[0]), .o_carry(w_CARRY[1]) ); full_adder full_adder_2 ( .i_bit1(i_add_term1[1]), .i_bit2(i_add_term2[1]), .i_carry(w_CARRY[1]), .o_sum(w_SUM[1]), .o_carry(w_CARRY[2]) ); assign o_result = {w_CARRY[2], w_SUM}; // Verilog Concatenation endmodule // ripple_carry_adder_2_FA

「设计文件结构」

「RTL原理图」

可见其RTL原理图和等波纹原理是一致的。

「仿真文件」

代码语言:javascript复制`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Engineer: Reborn Lee // Module Name: ripple_adder_2_tb // Additional Comments: // https://blog.csdn.net/Reborn_Lee ////////////////////////////////////////////////////////////////////////////////// module ripple_adder_2_tb; reg [1:0] i_add_term1; reg [1:0] i_add_term2; wire [2:0] o_result; initial begin i_add_term1 = 2'b00; i_add_term2 = 2'b00; # 10 i_add_term1 = 2'b10; i_add_term2 = 2'b01; # 10 i_add_term1 = 2'b11; i_add_term2 = 2'b01; # 10 i_add_term1 = 2'b11; i_add_term2 = 2'b11; #10 $finish; end // Monitor values of these variables and print them into the log file for debug initial $monitor ("i_add_term1 = %b, i_add_term2 = %b, o_result = %b", i_add_term1, i_add_term2, o_result); ripple_carry_adder_2 inst_ripple_adder_2( .i_add_term1(i_add_term1), .i_add_term2(i_add_term2), .o_result(o_result) ); endmodule

「仿真文件结构」

在这里插入图片描述

「仿真波形」

我们尝试测试了几个值相加:

代码语言:javascript复制i_add_term1 = 00, i_add_term2 = 00, o_result = 000 i_add_term1 = 10, i_add_term2 = 01, o_result = 011 i_add_term1 = 11, i_add_term2 = 01, o_result = 100 i_add_term1 = 11, i_add_term2 = 11, o_result = 110参数化的等波纹加法器设计

上面的纹波进位加法器使用Verilog参数来允许同一代码的不同实现。这使代码更具通用性和可重用性。该代码使用该参数创建一个generate语句,该语句实例化WIDTH参数指定的数量的全加器。

这段代码显示了在创建紧凑但可扩展的代码时,强大的参数和generate语句的功能。它可以用于任何宽度的输入。数字设计师只需要为自己的特定应用适当设置宽度,工具就会生成正确的逻辑量!

「设计文件」

代码语言:javascript复制`timescale 1ns / 1ps `include "full_adder.v" module ripple_carry_adder #(parameter WIDTH = 4) ( input [WIDTH-1:0] i_add_term1, input [WIDTH-1:0] i_add_term2, output [WIDTH:0] o_result ); wire [WIDTH:0] w_CARRY; wire [WIDTH-1:0] w_SUM; // No carry input on first full adder assign w_CARRY[0] = 1'b0; genvar ii; generate for (ii=0; ii


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3