(8)HDLBits-Circuits-Combinational Logic-Arithmetic Circuits

Hadd

一、题目要求
创建一个半加法器。半加法器将两个位相加(没有进位)并产生和和进位。
二、分析
加法器是指完成两个二进制数之间的算术运算加操作,如果不考虑有来自低位的进位,将两个1位的二进制数相加,称为半加,实现半加运算的电路称为半加器。这里假定A,B为两个加数,S是相加的和,CO是向高位的进位,则半加器的真值表如下

ABSCO
0000
0110
1010
1101

根据真值表画出卡诺图(略),可以得到半加器的逻辑表达式为

S=A ^ B,CO = A & B。

半加器是组合逻辑电路,赋值语句使用assign即可。这里ab对应上边的AB,sum对应S,cout对应CO。

根据表达式可以得到以下代码

module top_module( 
	input a, b,
	output cout, sum );

	assign sum = a ^ b;
	assign cout = a & b;

endmodule

三、仿真图

出现下图表示仿真成功

在这里插入图片描述

Fadd

一、题目要求
创建一个完整的加法器。全加法器加3位(包括进位)并产生和和进位。
二、分析
full adder,即全加器。在将两个多位的二进制数相加时,除了最低位以外,每一位都应该考虑来自低位的进位,即将两个对应位的加数和来自低位的进位三个数相加。称这种运算为全加,所用的电路称为全加器。下边给定1位全加器的真值表,假定输入为A,B,CI,CI为来自低位的进位,S为相加的和,CO为相加后向高位的进位。真值表如下

CIABSCO
00000
00110
01010
01101
10010
10101
11001
11111

画出卡诺图(略),得到以下逻辑表达式

S=A ^ B ^ CI,CO=A&B  | ((A^B)&CI)。

全加器是组合逻辑电路,赋值语句使用assign即可。这里ab对应上边的AB,cin对应CI,cout对应CO,sum对应S。

根据表达式可以得到以下代码

module top_module( 
	input a, b, cin,
	output cout, sum );

	assign sum = a ^ b ^ cin;
	assign cout = a&b | ((a^b)&cin);

endmodule

三、仿真图

在这里插入图片描述

Adder3

一、题目要求
现在您已经知道了如何构建一个完整的加法器,创建3个实例来创建一个3位二进制波纹进位加法器。加法器将两个3位数字和一个进位相加以产生3位和并执行。为了鼓励您实际实例化完整加法器,还需要在波纹进位加法器中输出每个完整加法器的进位。Cout[2]是最后一个完整加法器的最后一个执行结果,是您通常看到的执行结果。

二、分析
这里给出四位二进制全加器的电路图,题目要求的是三位的,原理相同,可以看到最右边是最高位,CO单独输出。最左边是最低位,CI单独输出,而其他的高一级的CI的输入是低一级CO的输出。因为这里需要多个模块的实例化,可以使用generate块来实现,具体generate块的用法可以自行查阅。当然,这里只涉及三个全加器,也可以一个一个实例化。
在这里插入图片描述

下边给定1位全加器的真值表,假定输入为A,B,CI,CI为来自低位的进位,S为相加的和,CO为相加后向高位的进位。真值表如下

CIABSCO
00000
00110
01010
01101
10010
10101
11001
11111

画出卡诺图(略),得到以下逻辑表达式

S=A ^ B ^ CI,CO=A&B  | ((A^B)&CI)。

全加器是组合逻辑电路,赋值语句使用assign即可。注意分清题目给的变量和真值表的对应。

下边给两个方法

1.逐个实例化,代码如下

module top_module( 
	input [2:0] a, b,
	input cin,
	output [2:0] cout,
	output [2:0] sum );

	assign sum[0] = a[0] ^ b[0] ^ cin;
	assign cout[0] = a[0]&b[0] | ((a[0]^b[0])& cin);//实例化第一个全加器

	assign sum[1]=a[1] ^ b[1] ^ cout[0];
	assign cout[1]=a[1]&b[1] | ((a[1]^b[1])& cout[0]);//实例化第二个全加器

	assign sum[2]=a[2] ^ b[2] ^ cout[1];
	assign cout[2]=a[2]&b[2] | ((a[2]^b[2])& cout[1]);//实例化第三个全加器

endmodule

2.使用generate块实例化,代码如下

module top_module( 
	input [2:0] a, b,
	input cin,
	output [2:0] cout,
	output [2:0] sum );

	assign sum[0] = a[0] ^ b[0] ^ cin;
	assign cout[0] = a[0]&b[0] | ((a[0]^b[0])& cin);//实例化第一个全加器

	genvar i;
	generate
    	for(i=1;i<=2;i++)
        	begin:fadd_loop//fadd表示全加器,loop表示循环,即循环实例化fadd模块,fadd_loop可以自定义名称
        	assign sum[i]=a[i] ^ b[i] ^ cout[i-1];
        	assign cout[i]=a[i]&b[i] | ((a[i]^b[i])& cout[i-1]);
        	end
	endgenerate

endmodule

三、仿真图

在这里插入图片描述

Exams/m2014 q4j

一、题目要求
实例化下列电路,FA表示全加器。

在这里插入图片描述

二、分析
这里是四个全加器的级联,和Adder3相似,区别在于这里cout没有给,需要你定义中间变量来存储进位的结果,变量类型可以是wire,也可以是reg,并且第一个全加器的cin,即进位输入是没有给的,也就是说,第一个全加器的进位输入可0可1,具体是0还是1判断不出,这里做以下操作,化简第一个全加器的逻辑表达式。
在这里插入图片描述
这里尝试CI=0,代码如下

module top_module (
	input [3:0] x,
	input [3:0] y, 
	output [4:0] sum);

	reg [3:0] cout;

	assign sum[0] = ~(x[0]^y[0]);
 	assign cout[0] = x[0]&y[0];

	genvar i ;

	generate 
  	  	for(i=1;i<=3;i++)begin:fadd_loop
       	 	assign sum[i] = x[i]^y[i]^cout[i-1];
        	assign cout[i] = x[i]&y[i] | ((x[i]^y[i])&cout[i-1]);
    	end
 	endgenerate

	assign sum[4]=cout[3];

endmodule

得到下图所示的真值表
在这里插入图片描述
说明CI=0不对,再尝试CI=1,代码如下

module top_module (
	input [3:0] x,
	input [3:0] y, 
	output [4:0] sum);

	reg [3:0] cout;

	assign sum[0] = x[0]^y[0];
	assign cout[0] = x[0]|y[0];

	genvar i;

	generate 
  	  	for(i=1;i<=3;i++)begin:fadd_loop
       	 	assign sum[i] = x[i]^y[i]^cout[i-1];
        	assign cout[i] = x[i]&y[i] | ((x[i]^y[i])&cout[i-1]);
    	end
 	endgenerate

	assign sum[4]=cout[3];
        
endmodule

得到如下的仿真图

在这里插入图片描述
这题很奇怪,CO=0和CI=1都不是正确答案。
这里查看其他博主的答案,发现cout[0] = x[0]&y[0],具体本题如何分析,如果有懂的小伙伴,欢迎指正,本人对此题也颇感疑惑。

综上,给出正确代码如下

module top_module (
	input [3:0] x,
	input [3:0] y, 
	output [4:0] sum);

	reg [3:0] cout;

 	assign sum[0] = x[0]^y[0];
 	assign cout[0] = x[0]&y[0];

	genvar i;

	generate 
  	  	for(i=1;i<=3;i++)begin:fadd_loop
       	 	assign sum[i] = x[i]^y[i]^cout[i-1];
        	assign cout[i] = x[i]&y[i] | ((x[i]^y[i])&cout[i-1]);
    	end
 	endgenerate

	assign sum[4]=cout[3];
        

endmodule

官方给的标准答案如下,用的是assign sum=x+y;注释里边说的是verilog加法会自动生成进位信号。

module top_module (
	input [3:0] x,
	input [3:0] y,
	output [4:0] sum
);

// This circuit is a 4-bit ripple-carry adder with carry-out.
	assign sum = x+y;	// Verilog addition automatically produces the carry-out bit.

// Verilog quirk: Even though the value of (x+y) includes the carry-out, (x+y) is still considered to be a 4-bit number (The max width of the two operands).
// This is correct:
// assign sum = (x+y);
// But this is incorrect:
// assign sum = {x+y};	// Concatenation operator: This discards the carry-out
endmodule

三、仿真图

出现下图表示仿真成功

在这里插入图片描述

Exams/ece241 2014 q1c

一、题目要求
假设你有两个8位补码,a[7:0]和b[7:0]。这些数字相加得到s[7:0]。还要计算是否发生了(带符号的)溢出。原文中的2’s complement表示补码的意思。

二、分析

这里给了两个补码a,b,要求相加得到c,并且还要判断是否发生了带符号位的溢出。
这里稍微解释一下原码,反码,补码以及有符号和无符号二进制的知识,这些知识来源于《数字电子技术基础》第六版,清华大学。

我们都知道,在数字电路里边,用二进制的0和1来表示低电平和高电平。那么如何表示一个数的正负呢,比如说+5,-5。我们可以发现正负5的区分只是其前边有个符号位“+”和“-”而已。并且正负是两种不同的状态。这刚好我们二进制的0和1也是两种状态,所以通常在二进制数的前面添加一个符号位(最高位),符号位为0表示正,为1表示负。这样表示的二进制数就叫有符号数,相应的不带符号位的数就叫无符号数。分析的时候,题目没有明确说明是有符号数都当做无符号数处理。这里我们知道一个N位的二进制数,如果是无符号的,可以表示0-2^N-1个数,如果是有符号的呢,因为最高位是表示正负的,所以实质上有效位只有N-1位,并且还能表示正负,所以范围为-2 ^ (N-1) - 2 ^(N-1)-1。

如果要表示一个数,不管是无符号的还是有符号的,将它转化为二进制,并且适当添上符号位,这样的表示方式就叫原码。比如8按照十-二进制转换的方法得到的4位1000,3位111就是原码。而反码就是在原码的基础之上逐位取反,补码则是在原码的基础之上逐位取反,最后在最末位加1。补码的概念类似于“互补”的概念,比如数学里边的平角为180°。如果∠A和∠B之和为180°,就说这两个角互补。补码主要是方便减法运算而产生的。这里再举一个书中的例子。例如你在5点钟的时候发现自己的手表停在10点了,因此需要把表拨回到5点。这里有两种拨法,一种是往回拨5格,10-5=5,拨到了5点。另一种是往前拨7格,10+7=17.由于表盘的最大数只有12,超过12以后的进位将自动消失,于是就指剩下减去12以后的余数了,即17-12=5,也可以将表拨回5点。这个例子说明10-5的减法运算可以用10+7的加法运算来代替。因为5和7相加正好等于产生进位的模数12,称7为-5对模12的补数也叫补码。不管是原码,反码,补码,都是为了方便计算而规定的。

这里还有解释一下溢出。溢出是说两个数相加得到的数超过了原来两个数可以表示的范围。比如是两个1位的相加,得到了两位数,也可以说是最高位产生了向更高一位的进位。计算机判断溢出的方法通常采用双高位判别法,利用符号位(最高位)和最高数值位(次高位)的进位情况来判断是否溢出,用C1表示符号位的进位情况,进位C1=1,否则C1=0,用C2来表示次高位的进位情况进位C2=1,否则C1=0。

当两个正数补码相加时,若数值之和大于2 ^(n-1),则数值位必有进位,则C2=1,而符号位没有进位,C1=0,这时C1C2状态为“01”,发生正溢出。

当两个负数补码相加时,若数值绝对值之和大于2 ^(n-1),则数值部分补码之和必定小于2 ^(n-1),此时数值位没进位C2=0,符号位有进位C1=1,此时C1C2状态为“10”,发生负溢出。

当C1C2状态相同时,即“00”或者“11”,不发生溢出。

根据上述关系,得到溢出位的判断为最高位和次高位进位的异或,即二者相同无溢出,相异有溢出。

本题是一个七位的二进制补码全加器,全加器的逻辑关系如下,假定输入为A,B,CI,CI为来自低位的进位,S为相加的和,CO为相加后向高位的进位。真值表如下

CIABSCO
00000
00110
01010
01101
10010
10101
11001
11111

画出卡诺图(略),得到以下逻辑表达式

S=A ^ B ^ CI,CO=A&B | ((A^B)&CI)。本题ab对于上边的AB,s对应S,而进位信号CO没有给出,需要定义中间变量,类型可以是wire或者reg。这里是全加器的模块重复实例化,用generate块生成,同时本题也没有给出最低位的进位信号,所以s[0]=a[0] ^ b[0],cout[0]=a[0]&b[0],代码如下

module top_module (
	input [7:0] a,
	input [7:0] b,
	output [7:0] s,
	output overflow
); //

// assign s = ...
// assign overflow = ...

	reg [7:0] cout;

	assign s[0]=a[0] ^ b[0];
	assign cout[0]=a[0]&b[0];

	genvar i;
	generate
    	for(i=1;i<=7;i++)begin:fadd_loop
        	assign s[i]=a[i]^b[i]^cout[i-1];
        	assign cout[i]=a[i]&b[i] | ((a[i]^b[i])&cout[i-1]);
    	end
	endgenerate

	assign overflow = cout[7] ^ cout[6];
            
endmodule

三、仿真图

在这里插入图片描述

Adder100

一、题目要求
创建一个100位二进制加法器。加法器将两个100位的数字和一个进位相加,得到一个100位的和并执行。
二、分析
这里要求的是100位的二进制加法器,跟3位的加法器是一样的。需要重复生成全加器的模块,使用generate块生成。

下边给定1位全加器的真值表,假定输入为A,B,CI,CI为来自低位的进位,S为相加的和,CO为相加后向高位的进位。真值表如下

CIABSCO
00000
00110
01010
01101
10010
10101
11001
11111

画出卡诺图(略),得到以下逻辑表达式

S=A ^ B ^ CI,CO=A&B | ((A^B)&CI)。题目的ab对应上边真值表的AB,cin对应CI,cout对应最高位的进位,sum对应S。这里CO是向高位的进位,这里没有给出,需要你定义中间变量来存储,中间变量的类型可以是wire或者reg。代码如下

module top_module( 
	input [99:0] a, b,
	input cin,
	output cout,
	output [99:0] sum );

	reg [99:0] cout1;
	assign sum[0]=a[0]^b[0]^cin;
	assign cout1[0]=a[0]&b[0]  | ((a[0]^b[0])&cin);

	genvar i;
	generate
    	for(i=1;i<=99;i++)begin:fadd_loop
            assign sum[i]=a[i] ^ b[i] ^ cout1[i-1];
            assign cout1[i] = a[i]&b[i]  | ((a[i]^b[i])&cout1[i-1]);
    	end
	endgenerate

	assign cout=cout1[99];
  
endmodule

三、仿真图

出现下图表示仿真正确

在这里插入图片描述

Bcdadd4

一、题目要求
实例化bcd_fadd的4个副本以创建一个4位BCD波纹进位加法器。您的加法器应该将两个4位BCD数字(打包成16位向量)和一个进位来生成4位和并执行。
module bcd_fadd (
input [3:0] a,
input [3:0] b,
input cin,
output cout,
output [3:0] sum );
二、分析
题目已经给了bcd_fadd模块,这里我们不需要知道它里边的逻辑关系是什么,只需要实例化就行,这个模块的输入输出是4位的,而题目要求16位的,所以这里就是输入的每四位实例化为一个BCD加法器,模块连接有名称连接和位置连接,这里使用名称连接。重复实例化模块使用generate块较方便,第一个模块为a[3:0],第二个为a[7:4],第三个为a[11:8],第四个为a[15:12],第i个为a[(4*i)+:4]。这个是数组的递增写法。数组的下标一般是常量,如果要用变量表示,该怎么办呢。

这里有两种方法:
[<starting_bit>+:width] - part-select increments from starting bit
[<starting_bit>-:width] - part-select decrements from starting bit
starting_bit表示数组的起始位,+表示在起始位的基础之上加上某个值(递增),width表示你需要在起始位增加的宽度
比如[0+:4]和[4:0]是等价的。[15-:8]和[15:8]是等价的。

参考文章:https://blog.csdn.net/carldada/article/details/109587397

代码如下

module top_module ( 
	input [15:0] a, b,
	input cin,
	output cout,
	output [15:0] sum );

	reg[3:0] cout1;
	bcd_fadd bcd_fadd0(
		.a(a[3:0]),
		.b(b[3:0]),
		.cin(cin),
		.cout(cout1[0]),
		.sum(sum[3:0])
	);
	genvar i;
	generate
		for (i = 1;i<=3 ; i=i+1) begin:bcdadd
    		bcd_fadd bcd_fadd0(
			.a(a[(4*i)+:4]),
			.b(b[(4*i)+:4]),
			.cin(cout1[i-1]),
			.cout(cout1[i]),
			.sum(sum[(4*i)+:4])
        	);
		end
	endgenerate
	assign cout = cout1[3];

endmodule

三、仿真图

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值