基于FPGA的数字信号处理(20)--半减器和全减器
目录
1、前言
2、半减器
3、全减器
4、减法器
文章总目录点这里:《基于FPGA的数字信号处理》专栏的导航与说明
1、前言
既然有半加器和全加器,那自然也有半减器和全减器了。尽管在电路中减法的实现基本都是 补码 + 加法 的形式,但是正所谓技多不压身,了解一下半减器和全减器还是有一定作用的,至少能扩宽知识面嘛。
2、半减器
最简单的减法器叫做 半减器(Half Subtractor),它将2个1bit的输入数据相减,输出一个1bit的结果即 差Di(Difference),以及向高位的借位Bo(Borrow output)。例如:
-
1 - 1 = 01 -1 = 0,不需要向高位借位,差为0,即Bo = 0,Di = 0
-
1 - 0 = 01 -0 = 1,不需要向高位借位,差为1,即Bo = 0,Di = 1
-
0 - 1 = 10 -1 = 1, 需要向高位借位,差为1,即Bo = 1,Di = 1
-
0 - 0 = 00 -0 = 0,不需要向高位借位,差为0,即Bo = 0,Di = 0
2个1bit数相减,最多只有4种情况(在上面已经例出来了),据此可以写出半减器的真值表:
| 被减数 | 减数 | 差 | 向高位借位 |
|---|---|---|---|
| X | Y | Di | Bo |
| 0 | 0 | 0 | 0 |
| 0 | 1 | 1 | 1 |
| 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 0 |
从这个真值表,不难推断出输出的逻辑表达式:
-
差Di在2个输入不同时为1,所以它是输入异或的结果,即 Di = a ^ b
-
向高位借位Bo在X = 0 且 Y = 1 时才为1,即 Bo= ~X & Y
有了逻辑表达式后,可以很容易地画出对应的门电路结构:

顺便提一句,虽然半减器的基本电路是上面这个样子的,但是因为在FPGA中只有查找表LUT而没有具体的门电路,所以如果用FPGA来综合半减器的话,它的电路应该是这个样子的(因为只有2个输入和2个输出,所以只要1个LUT6就可以覆盖到所有情况):

如果你不了解LUT,可以看看这篇文章:从底层结构开始学习FPGA(2)----LUT查找表,或者看看这个专栏:从底层结构开始学习FPGA
IBUF和OBUF是Vivado自动添加的对输入输出管脚的缓冲,尽管上图显示的是2个LUT2(可能这样的显示更清晰一点),但是实际上就是1个LUT6,下面的资源显示情况也证明了这一点:

用verilog实现半减器如下:
//使用逻辑表达式来描述半减器
module half_subtractor(input x, //被减数input y, //减数output di, //差output bo //向高位借位
);
//根据化简结果分别表示:差 与 向高位借位
assign di = x ^ y;
assign bo = ~x & y;endmodule
有了RTL,接下来就该要写个对应的TB来测试电路功能是否正常。由于这个电路足够简单(一共只有4种情况),所以我们可以把所有可能的情况都穷举出来,然后观察输出是否符合预期即可。TB如下:
`timescale 1ns/1ns //时间刻度:单位1ns,精度1ns
module tb_half_subtractor();
//定义变量
reg x;
reg y;
wire di;
wire bo;
//设置初始化条件
initial begin//第1种情况x =1'b0; y =1'b0; #10 //第2种情况x =1'b0; y =1'b1; #10 //第3种情况x =1'b1; y =1'b0;#10 //第4种情况x =1'b1; y =1'b1;#10 $stop(); //结束仿真
end
//例化被测试模块
half_subtractor u_half_subtractor(.x (x),.y (y), .di (di), .bo (bo)
);endmodule
仿真结果如下:

通过和真值表的对比(或者验证逻辑表达式也可以),可以发现,电路的输出是符合预期的。
3、全减器
虽然半减器可以实现2个1bit数的减法,但在实际应用中,更常见的是要实现2个多bits数的减法,那么该如何实现?以2个2bits数的减法为例:
-
先把低位和高位的减法先分开。
-
低位是2个1bit的减法,所以可以用1个HS(半减器)来实现,它产生的差就是最终结果的低位,它产生的向高位借位要被送入到高位参与它们的减法。
-
高位除了要计算减数和被减数的高位外,还有1个来自低位的借位。
好,现在问题是半减器没有设计来自低位的借位,所以它处理不了这种情况。为此,全减器被设计出来了,它在半减器的基础上,增加了来自低位的借位输入。这样多个全减器就可以级联起来实现多bits的减法了。
全减器(Full Subtractor),它处理2个1bit的输入和来自低位的借位Bi(Borrow input)输入共3个数,输出一个差Di和向高位的借位Bi。例如(格式:被减数- 减数 - 来自低位的借位):
-
1 - 1 - 1 = 11 - 1 - 1 = 0,来自低位的借位为1, 需要向高位借位,差为0,即Bi = 1,Bo = 0,Di = 0
-
1 - 1 - 0 = 01 - 1 - 0 = 0,来自低位的借位为0,不需要向高位借位,差为0,即Bi = 0,Bo = 0,Di = 0
-
1 - 0 - 1 = 01 - 0 - 1 = 0,来自低位的借位为1,不需要向高位借位,差为0,即Bi = 1,Bo = 0,Di = 0
-
1 - 0 - 0 = 01 - 0 - 0 = 0,来自低位的借位为0,不需要向高位借位,差为1,即Bi = 0,Bo = 0,Di = 1
-
······
3个输入一共只有8种情况,把所有情况都穷举出来,就可以列出全减器的真值表:
| 被减数 | 减数 | 来自低位的借位 | 差 | 向高位的借位 |
|---|---|---|---|---|
| X | Y | Bi | Di | Bo |
| 0 | 0 | 0 | 0 | 0 |
| 0 | 0 | 1 | 1 | 1 |
| 0 | 1 | 0 | 1 | 1 |
| 0 | 1 | 1 | 0 | 1 |
| 1 | 0 | 0 | 1 | 0 |
| 1 | 0 | 1 | 0 | 0 |
| 1 | 1 | 0 | 0 | 0 |
| 1 | 1 | 1 | 1 | 1 |
从这个真值表,不难推断出两个输出的逻辑表达式:
-
Di为1的4种情况,~x&~y&bi + ~x&y&~bi + x&~y&~bi + x&y&bi=~x&(~y&bi+y&~bi)+x&(~y&~bi+y&bi) = ~x&(y^bi) + x&~(y^bi) = x ^ y ^ bi
-
Bo为1的4种情况,~x&~y&bi + ~x&y&~bi + ~x&y&bi + x&y&bi = ~x&(~y&bi+y&~bi) + (~x+x)&y&bi = ~x&(y^bi) + y&bi
有了逻辑表达式后,就很容易画出电路图了(偷了个懒,不是画的,是用vivado生成的):

根据门电路,可以写出全减器的Verilog实现:
//使用逻辑表达式来描述全减器
module full_subtractor(input x, //被减数input y, //减数input bi, //来自低位的借位output di, //差output bo //向高位借位
);
//根据化简结果分别表示:差 与 向高位借位
assign di = x ^ y ^ bi;
assign bo = ~x & (y ^ bi) | y & bi;endmodule
接下来写个TB测试电路,输入一共只有8个,所以依然用穷举法来测试:
`timescale 1ns/1ns //时间刻度:单位1ns,精度1ns
module tb_full_subtractor();
//定义变量
reg x;
reg y;
reg bi;
wire di;
wire bo;
//设置初始化条件
initial begin//第1种情况x =1'b0; y =1'b0; bi =1'b0; #10 //第2种情况x =1'b0; y =1'b1;bi =1'b0; #10 //第3种情况x =1'b1; y =1'b0;bi =1'b0;#10 //第4种情况x =1'b1; y =1'b1;bi =1'b0;#10 //第5种情况x =1'b0; y =1'b0; bi =1'b1;#10 //第6种情况x =1'b0; y =1'b1;bi =1'b1;#10 //第7种情况x =1'b1; y =1'b0;bi =1'b1;#10 //第8种情况x =1'b1; y =1'b1;bi =1'b1; #10 $stop(); //结束仿真
end
//例化被测试模块
full_subtractor u_full_subtractor(.x (x),.y (y), .di (di),.bi (bi),.bo (bo)
);endmodule
![]()
仿真结果如下所示:

通过和真值表的对比(或者验证逻辑表达式也可以),可以发现,电路的输出是符合预期的。
4、减法器
有了全减器后,就可以实现两个多bits数的减法了,这样的电路也被称为减法器。和行波进位加法器的结构类似,我们也可以将多个全减器级联起来搭建减法器,以4bits的减法为例,它的结构如下:

根据结构图,可以很快地写出它对应的Verilog代码:
//使用多个全减器建联构建减法器
module subtractor(input [3:0] x, //被减数input [3:0] y, //减数input bi, //来自低位的借位output [3:0] di, //差output bo //向高位借位
);
wire b1,b2,b3; //借位连接
//例化全减器来构建减法器
full_subtractor u0(.x (x[0]),.y (y[0]), .di (di[0]),.bi (bi),.bo (b1)
);
full_subtractor u1(.x (x[1]),.y (y[1]), .di (di[1]),.bi (b1),.bo (b2)
);
full_subtractor u2(.x (x[2]),.y (y[2]), .di (di[2]),.bi (b2),.bo (b3)
);
full_subtractor u3(.x (x[3]),.y (y[3]), .di (di[3]),.bi (b3),.bo (bo)
);
endmodule
这里记得要把之前的全减器也添加进工程。生成的示意图如下(这个排布不能很好地看出来层次结构,但确实没错):

然后写个TB测试一下这个减法器电路,因为4个bits即16×16=256种情况,加上低位借位的两种情况,也才256×2=512种情况,所以依然用穷举法来测试:
`timescale 1ns/1ns //时间刻度:单位1ns,精度1ns
module tb_full_subtractor();
//定义变量
reg [3:0] x; //被减数
reg [3:0] y; //减数
reg bi; //来自低位的借位
wire [3:0] di; //差
wire bo; //向高位借位
reg [3:0] di_real; //差的真实值,作为对比
reg bo_real; //向高位借位的真实值,作为对比
wire di_flag; //di正确标志信号
wire bo_flag; //bo正确标志信号
assign di_flag = di == di_real; //di结果正确时拉高该信号
assign bo_flag = bo == bo_real; //bo结果正确时拉高该信号
integer z,i,j; //循环变量
//设置初始化条件
initial begin//初始化x =1'b0; y =1'b0; bi =1'b0; //设定低位借位为0//穷举所有情况for(z=0;z<=1;z=z+1)beginbi = z;for(i=0;i<16;i=i+1)beginx = i;for(j=0;j<16;j=j+1)beginy = j;if((i-j-z)<0)begin //减法结果是负数di_real = (i - j - z)+ 5'd16; //加上向高位的借位bo_real = 1; //向高位的借位为1endelse begin //减法结果是正数数di_real = i - j - z; //结果就是减法本身bo_real = 0; //向高位的借位为0end#5; end endend#10 $stop(); //结束仿真
end
//例化被测试模块
subtractor u_subtractor(.x (x),.y (y), .di (di),.bi (bi),.bo (bo)
);endmodule
TB中分别用3个嵌套循环将所有情况穷举出来,即bi=0~1、x=0~15和y=0~15的所有情况。减法运算的预期结果也是很容易就可以找出来的,就是直接写减法就行。接着构建了两个标志向量di_flag和bo_flag作为电路输出与预期结果的对比值,当二者一致时即拉高这两个信号。这样我们只要观察这两个信号,即可知道电路输出是否正确。仿真结果如下:

可以看到,di_flag和bo_flag都是一直拉高的,说明电路输出正确。
相关文章:
基于FPGA的数字信号处理(20)--半减器和全减器
目录 1、前言 2、半减器 3、全减器 4、减法器 文章总目录点这里:《基于FPGA的数字信号处理》专栏的导航与说明 1、前言 既然有半加器和全加器,那自然也有半减器和全减器了。尽管在电路中减法的实现基本都是 补码 加法 的形式,但是正所谓…...
Python:单引号,双引号,三引号的区别
在Python中,单引号()、双引号(")和三引号( 或 """)都可以用来定义字符串,但它们之间有一些区别: 单引号()和双引号…...
电子电气架构 ---SOMEIP/SD初入门
我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…...
一些数学基础概念
一些数学基础概念 概率密度函数(PDF) 概率密度函数(Probability Density Function,简称 PDF)是描述连续随机变量的概率分布的一种函数。它用来表示随机变量在各个取值区间内的概率密度。 1. 定义 对于一个连续随机变量 ( X ),…...
责任有限公司的一般组织结构
责任有限公司(有限责任公司,LLC)的组织结构通常是为了确保公司运营的有效性和管理的透明度。以下是一般责任有限公司的组织结构及其主要组成部分: 1. 股东(Shareholders) 职责和角色 所有者:…...
Leetcode3227. 字符串元音游戏
Every day a Leetcode 题目来源:3227. 字符串元音游戏 解法1:博弈论 分类讨论: 如果 s 不包含任何元音,小红输。如果 s 包含奇数个元音,小红可以直接把整个 s 移除,小红赢。如果 s 包含正偶数个元音&am…...
网络流量分析在运维管理中的重要性与实施策略
在运维管理工作中,网络流量分析是一项不可或缺的技术手段。通过对网络流量的深入剖析,运维团队能够更全面地了解网络状态,及时发现潜在问题,优化网络性能,从而确保企业网络的稳定与高效运行。本文将详细探讨网络流量分…...
通信原理实验——PCM编译码
PCM编译码 实验目的 理解PCM编译码原理及PCM编译码性能熟悉PCM编译码专用集成芯片的功能和使用方法及各种时钟关系熟悉语音数字化技术的主要指标及测量方法 主要仪器设备及软件 硬件:多功能实验箱、示波器、导线 软件:无 实验原理 1. 抽样信号的量…...
matlab的strel()函数的使用方法(OK)
这个函数 是形态学的结构元素 使用方法如下 SE strel(nhood) SE strel("diamond",r) SE strel("disk",r) SE strel("disk",r,n) SE strel("octagon",r) SE strel("line",len,deg) SE strel("rectangle",…...
Linux:Linux权限解析
一、Linux下的用户分类 在Linux下,有两种用户,一种是超级用户,一种是普通用户 超级用户:可以再linux系统下做任何事情,不受权限限制(制定规则,但不需要遵守规则) 普通用户࿱…...
Spring面试篇章——IOC
IOC概念和原理 IOC概念 IOC就是控制反射,把对象创建和对象之间的调用过程,交给Spring进行管理使用IOC的目的:降低耦合度 IOC底层原理 xml解析、工厂模式、反射 图解: 原始模式 耦合度太高了,即当dao改了…...
适合制造业的项目管理软件都有哪些?
项目管理软件涉及进度、预算成本、资源、开发、流程、质量、风险、工时、知识文档、商务等各个方面,是企业项目管理领域的重要辅助工具,能够帮助组织提高项目管理水平与质量,确保项目顺利进行。 一、 奥博思 PowerProject 项目管理系统 Pow…...
微应用(Micro-Applications)、微前端(Micro Frontend)、Qiankun 框架之间的区别和联系
简简单单 Online zuozuo: 简简单单 Online zuozuo 简简单单 Online zuozuo 简简单单 Online zuozuo 简简单单 Online zuozuo :本心、输入输出、结果 简简单单 Online zuozuo :联系我们:VX :tja6288 / EMAIL: 347969164@qq.com 文章目录 微应用(Micro-Applications)、微…...
String的底层构造
1.String类对象的构造(后面有每一个接口的实现) #define _CRT_SECURE_NO_WARNINGS 1 #pragma once #include<iostream> #include<assert.h> using namespace std;namespace bit {class string{public:typedef char* iterator;typedef const…...
Binder机制的原理
Binder机制是Android系统中用于进程间通信(IPC)的核心机制,它基于C/S(客户端-服务端)模型,允许不同进程间进行高效的通信和数据交换。以下是对Binder机制原理的详细解析: 1. Binder架构 Binde…...
JavaScript输出数据的方法?
在JavaScript中,输出数据有以下几种方法: 使用console.log()函数:使用console.log()函数可以将数据输出到浏览器的控制台,例如: console.log("Hello, World!");使用alert()函数:使用alert()函数…...
Redis学习笔记——第19章 事务
第19章 事务 19.1 事务的实现 19.1.1 事务开始 使用multi命令开启一个事务 通过修改客户端的flags字段为REDIS_MULTI 19.1.2 命令入队 当开启事务之后,exec、discard、watch、multi命令立即执行,而其他命令会放入一个队列中,并返回Queue…...
元太电磁膜SUDE-10S19MI-01X驱动适配
屏规格书: dts配置: 首先要确保CONFIG_I2C_HID宏打开,i2c-hid-core.c 文件才能编译进去代码。规格书vendor product 分别为0x2d1f 和0x0165 来区别,不至于影响到整体的hid其他设备。 i2c-hid-dev10 { compatible "hid-…...
C#数据类型 全局变量 类型转换方法(汇总)
1、C#和S7-1200PLC S7.NET通信 C#和S7-1200PLC S7.NET通信-CSDN博客文章浏览阅读98次。一步步建立一个C#项目(连续读取S7-1200PLC数据)_s7协议批量读取-CSDN博客这篇博客作为C#的基础系列,和大家分享如何一步步建立一个C#项目完成对S7-1200PLC数据的连续读取。首先…...
HCIP重修总笔记(中)
第八节 BGP基础 一、BGP产生背景 BGPBorder Gateway Protocol,边界网关协议)是一种用于自治系统间的动态路出协议,是一种外部网关协议。 自治系统AS:一组同一个管理机构进行管理,对外呈现统一选路策略的路由器的集合。 自治系统编号: …...
使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
uniapp 开发ios, xcode 提交app store connect 和 testflight内测
uniapp 中配置 配置manifest 文档:manifest.json 应用配置 | uni-app官网 hbuilderx中本地打包 下载IOS最新SDK 开发环境 | uni小程序SDK hbulderx 版本号:4.66 对应的sdk版本 4.66 两者必须一致 本地打包的资源导入到SDK 导入资源 | uni小程序SDK …...
高防服务器价格高原因分析
高防服务器的价格较高,主要是由于其特殊的防御机制、硬件配置、运营维护等多方面的综合成本。以下从技术、资源和服务三个维度详细解析高防服务器昂贵的原因: 一、硬件与技术投入 大带宽需求 DDoS攻击通过占用大量带宽资源瘫痪目标服务器,因此…...
AI书签管理工具开发全记录(十八):书签导入导出
文章目录 AI书签管理工具开发全记录(十八):书签导入导出1.前言 📝2.书签结构分析 📖3.书签示例 📑4.书签文件结构定义描述 🔣4.1. 整体文档结构4.2. 核心元素类型4.3. 层级关系4.…...
Android多媒体——音/视频数据播放(十八)
在媒体数据完成解码并准备好之后,播放流程便进入了最终的呈现阶段。为了确保音视频内容能够顺利输出,系统需要首先对相应的播放设备进行初始化。只有在设备初始化成功后,才能真正开始音视频的同步渲染与播放。这一过程不仅影响播放的启动速度,也直接关系到播放的稳定性和用…...
【SSM】SpringMVC学习笔记7:前后端数据传输协议和异常处理
这篇学习笔记是Spring系列笔记的第7篇,该笔记是笔者在学习黑马程序员SSM框架教程课程期间的笔记,供自己和他人参考。 Spring学习笔记目录 笔记1:【SSM】Spring基础: IoC配置学习笔记-CSDN博客 对应黑马课程P1~P20的内容。 笔记2…...
四、Sqoop 导入表数据子集
作者:IvanCodes 日期:2025年6月4日 专栏:Sqoop教程 当不需要将关系型数据库中的整个表一次性导入,而是只需要表中的一部分数据时,Sqoop 提供了多种方式来实现数据子集的导入。这通常通过过滤条件或选择特定列来完成。 …...
直角坐标系和斜角坐标系
前情概要 笛卡尔坐标系是直角坐标系和斜角坐标系的统称。为什么会有这两种坐标系呢,教材中为什么最后只用直角坐标系呢?我们这样解释: 研究一维空间中的向量时,由于一维空间中的向量有无数条,如果我们选定一条作为基…...
