前言

刚学前端设计的时候,听到的就是组合逻辑、时序逻辑,很重要!但是究竟有什么用?到底怎么体现,没有多少老师可以明确指出来,当自己看的东西多了,就可以理解了,甚至可以得出自己的范式。

到目前为止,要想掌握组合逻辑,就请先掌握本文列出的编码器、译码器、数据选择器、加法器等简单的组合逻辑电路。

1.编码器和译码器

刚开始听verilog的时候,估计都听过3-8译码器,但是听过编码器吗?

编码器和译码器可以将不同输入数据变换为不同的输出数据
编码器的数据输入比编码后的输出位数大,编码器常用来减少数据通道数目;而译码器与之相反。

1.1 编码器

编码器把输入信号编写成一个对应的二进制信号,即把2^N个输入信号转化为N位编码输出。还可以分为:普通编码器和优先编码器。

普通编码器:只能一个一个的输入,如果同时输入两个信号,编码器不能识别;
优先编码器:如果输入两个信号,对高位优先识别。(联想if-else的优先级)

1.1.1 8-3普通编码器

8-3普通编码器功能真值表

在这里插入图片描述

设计代码code8_3.v
module code8_3(
	I,
	O
);

input 	[7:0]	I;
output 	[2:0]	O;
reg 		[2:0]	O;

always@(I)
begin
	case(I)
		8'b0000_0001:O = 3'b111;
		8'b0000_0010:O = 3'b110;
		8'b0000_0100:O = 3'b101;
		8'b0000_1000:O = 3'b100;
		8'b0001_0000:O = 3'b011;
		8'b0010_0000:O = 3'b010;
		8'b0100_0000:O = 3'b001;
		8'b1000_0000:O = 3'b000;
		default:	O=3'bxxx;
	endcase
end

endmodule

仿真文件code8_3_tb.v
`timescale 1ns/1ns
`define clk_period 20

module code8_3_tb;

reg 	[7:0]	I;
wire 	[2:0]	O;

reg clk;

code8_3 code8_3_inst(
	.I(I),
	.O(O)
);

initial clk = 1'b1;
always #`clk_period clk = ~clk;

initial begin
	
	I = 8'b0000_0001;
	#`clk_period;
	I = 8'b0000_0010;
	#`clk_period;
	I = 8'b0000_0100;
	#`clk_period;
	I = 8'b0000_1000;
	#`clk_period;
	I = 8'b0001_0000;
	#`clk_period;
	I = 8'b0010_0000;
	#`clk_period ;
	I = 8'b0100_0000;
	#`clk_period;
	I = 8'b1000_0000;
	#`clk_period;
	I = 8'b0000_0000;
	#`clk_period;
	
	$stop;
end

endmodule
功能仿真

在这里插入图片描述

1.1.1 8-3优先编码器

详细介绍见:优先级编码器74LS148的电路结构、工作原理及使用方法

8-3优先编码器功能真值表

在这里插入图片描述

上面这个真值表有问题,在网上找到了下面这一个
在这里插入图片描述

8-3优先编码器的逻辑符号

在这里插入图片描述

设计代码code_8_3.v
module code_8_3(
	EI_n,
	I,
	Y,
	GS_n,
	EO_n
);

input 			EI_n;
input 	[7:0]	I;
output 	[2:0]	Y;
output 			GS_n;
output 			EO_n;


reg 		[2:0]	Y;
reg				GS_n;
reg				EO_n;

always@(EI_n or I)
begin
	if(!EI_n)begin
		if			(~I[7])		begin	Y = 3'd0;EO_n = 1;GS_n = 0;end
		else if 	(~I[6])		begin	Y = 3'd1;EO_n = 1;GS_n = 0;end
		else if 	(~I[5])		begin	Y = 3'd2;EO_n = 1;GS_n = 0;end
		else if 	(~I[4])		begin	Y = 3'd3;EO_n = 1;GS_n = 0;end
		else if 	(~I[3])		begin	Y = 3'd4;EO_n = 1;GS_n = 0;end
		else if 	(~I[2])		begin	Y = 3'd5;EO_n = 1;GS_n = 0;end
		else if 	(~I[1])		begin	Y = 3'd6;EO_n = 1;GS_n = 0;end
		else if 	(~I[0])		begin	Y = 3'd7;EO_n = 1;GS_n = 0;end
		else					begin	Y = 3'd7;EO_n = 0;GS_n = 1;end
	end
	else 						begin	Y = 3'd7;EO_n = 1;GS_n = 1;end
end

endmodule

仿真文件code_8_3_tb.v
`timescale 1ns/1ns
`define clk_period 20

module code_8_3_tb;

reg 				EI_n;
reg 		[7:0]	I;
wire 		[2:0]	Y;
wire 				GS_n;
wire 				EO_n;

reg 				clk;

code_8_3 code_8_3_inst(
	.EI_n(EI_n),
	.I(I),
	.Y(Y),
	.GS_n(GS_n),
	.EO_n(EO_n)
);

initial clk = 1'b1;
always #`clk_period clk = ~clk;

initial begin
	EI_n = 1'b1;
	I = 8'b1101_1111;
	#`clk_period;
	EI_n = 1'b0;
	
	I = 8'b1111_1110;
	#`clk_period;
	I = 8'b0111_1101;
	#`clk_period;
	I = 8'b1010_1010;
	#`clk_period;
	I = 8'b1100_1111;
	#`clk_period;
	I = 8'b1111_1111;
	#`clk_period ;
	I = 8'b1110_0100;
	#`clk_period;
	I = 8'b1111_1111;
	#`clk_period;
	I = 8'b1110_0100;
	#`clk_period;
	I = 8'b1110_0110;
	#`clk_period;
	I = 8'b1111_1010;
	#`clk_period;
	I = 8'b1111_1100;
	#`clk_period;
	$stop;
end

endmodule
功能仿真

在这里插入图片描述

1.2 译码器

从电路功能上看,译码器和编码器没有实质的差别。
编码器把输入信号编写成一个对应的二进制信号,即把2^N个输入信号转化为N位编码输出。
而译码器是把输入的N位二进制信号转换成2^N个代表代码原意的状态信号并输出。

1.2.1 3-8译码器74LS138逻辑符号

在这里插入图片描述

3-8译码器74LS138的功能真值表

在这里插入图片描述

设计代码decoder3_8
module decoder3_8(
	Y,
	S,
	A
);

output 	[7:0] Y;

input 	[2:0]	S;
input		[2:0]	A;

reg		[7:0]	Y;

always@(*)
begin
	if(S[0] & (S[1]==0) & (S[2]==0))begin
		case(A)
			3'b000:Y = 8'b1111_1110;
			3'b001:Y = 8'b1111_1101;
			3'b010:Y = 8'b1111_1011;
			3'b011:Y = 8'b1111_0111;
			3'b100:Y = 8'b1110_1111;
			3'b101:Y = 8'b1101_1111;
			3'b110:Y = 8'b1011_1111;
			3'b111:Y = 8'b0111_1111;
			default Y=8'bX; 
		endcase
	end
	else Y = 8'b1111_1111;

end

endmodule
仿真文件decoder3_8_tb.v
`timescale 1ns/1ns

module decoder3_8_tb;

wire 		[7:0] Y;
reg 		[2:0]	S;
reg			[2:0]	A;

decoder3_8 decoder3_8_tb(
	.Y(Y),
	.S(S),
	.A(A)
);

initial begin
	A = 3'b000;
	S = 3'b000;	
	
	#20;
	A = 3'b001;
	S = 3'b010;	
	
	#20;
	A = 3'b000;
	S = 3'b001;	
	
	#20;
	
	A = 3'b001;
	#20;
	A = 3'b010;
	#20;
	A = 3'b011;
	#20;
	A = 3'b100;
	#20 ;
	A = 3'b101;
	#20;
	A = 3'b110;
	#20;
	A = 3'b111;
	#20;
	A = 3'b000;
	#20;
	
	$stop;
end
endmodule
功能仿真

在做前面几个仿真文件时,出现了时钟,到这里突然意识到,组合逻辑仿真时不必引入时钟,只需要利用延迟就可以得出仿真结果。
在这里插入图片描述

1.2.2 显示译码器

请点击查看译码器和数据选择器

2.数据选择器

4选1数据选择器的逻辑符号

在这里插入图片描述

4选1数据选择器的功能真值表

输入S1输入S0输出Y
00D0
01D1
10D2
11D3

4选1选择器原理图

在这里插入图片描述

4选1数据选择器的三种建模方式

(1)门级建模MUX4_1a.v

module MUX4_1a(
	D0,D1,D2,D3,
	S0,S1,
	Y
);
	input 	D0,D1,D2,D3;
	input 	S0,S1;
	
	output 	Y;

	wire notS0,notS1,Y0,Y1,Y2,Y3,Y4;
	
	not	not0(notS0,S0);
	not 	not1(notS1,S1);
	
	and	and0(Y0,notS0,notS1,D0);
	and	and1(Y1,notS0,S1,D1);
	and	and2(Y2,S0,notS1,D2);
	and	and3(Y3,S0,S1,D3);
	
	or		or1(Y,Y0,Y1,Y2,Y3);
	
endmodule
综合出来的电路

在这里插入图片描述

(2)数据流级建模

assign语句描述出来的MUX4_1c.v
module MUX4_1b(
	D0,D1,D2,D3,
	S0,S1,
	Y
);
	input 	D0,D1,D2,D3;
	input 	S0,S1;
	
	output 	Y;

	assign 	Y = ((D0 & ~S0 & ~S1)|(D1 & ~S0 & S1)|(D2 & S0 & ~S1)|(D3 & S0 & S1));
	
endmodule
综合出来的电路

在这里插入图片描述

——条件表达式描述MUX4_1c.v
module MUX4_1c(
	D0,D1,D2,D3,
	S0,S1,
	Y
);
	input 	D0,D1,D2,D3;
	input 	S0,S1;
	
	output 	Y;

	assign Y = S1?(S0?D3:D2):(S0?D1:D0);
	
endmodule
综合出来的电路

在这里插入图片描述

(3)行为级建模

——采用了if-else结构的MUX4_1d.v
module MUX4_1d(
	D0,D1,D2,D3,
	S0,S1,
	Y
);
	input 	D0,D1,D2,D3;
	input 	S0,S1;
	
	output 	Y;
	reg		Y;
	
	//采用了if-else结构
	always@(D0 or D1 or D2 or D3 or S0 or S1)
	begin
		if			({S1,S0}==2'b00)	Y=D0;
		else if	({S1,S0}==2'b01)	Y=D1;
		else if	({S1,S0}==2'b10)	Y=D2;
		else if	({S1,S0}==2'b11)	Y=D3;
		else								Y=1'bx;
		
	end
	
endmodule
if-else结构综合出来的电路

在这里插入图片描述

——采用了case语句的MUX4_1e.v
module MUX4_1e(
	D0,D1,D2,D3,
	S0,S1,
	Y
);
	input 	D0,D1,D2,D3;
	input 	S0,S1;
	
	output 	Y;
	reg		Y;
	
	//采用了case结构
	always@(D0 or D1 or D2 or D3 or S0 or S1)
	begin
		case({S1,S0})
				2'b00:Y=D0;
				2'b01:Y=D1;
				2'b10:Y=D2;
				2'b11:Y=D3;
			default:		Y=1'bx;
		endcase	
	end
	
endmodule
case语句综合出来的电路

在这里插入图片描述
随着数据选择器的输入增多,if-else和case综合出来的电路差异就很大。这种情况下就推荐使用case语句。

3.加法器

加法运算是最基本的运算,简单的乘法、除法、减法和复杂的FFT(快速傅里叶变换)都可以分解为加法运算。
点击阅读笔记连载 | Day7 【半加器、全加器、16位加法器、16位减法器设计】 【原理及verilog实现、仿真】篇

3.1半加器

半加器,就是y=a+b,不考虑进位,如下真值表,a、b表示2个相加的数,y表示和,Co表示结果有没有进位。
在这里插入图片描述
从真值表可以得出,y和Co的布尔表达式

Y   = (~a&b) | (a&~b)
Co  = a&b

在这里插入图片描述

设计代码half_adder.v

module half_adder(
      input a,             //第一个加数a
      input b,             //第二个加数b
      output sum,          //和
      output cout          //位
);     
		
    assign sum=a ^ b;     //sum=a⊕b
    assign cout=a & b;	  //cout=ab

endmodule
综合出来的电路图

在这里插入图片描述

3.2全加器

强烈推荐阅读:Verilog全加器
全加器,就是y=a+b+c_up,要考虑进位,如下真值表,a、b表示2个相加的数,c_up表示低位向本位的进位标志,Co表示计算结果有没有向高位进位
在这里插入图片描述
从真值表可以得出,y和Co的布尔表达式

y = (~a&~b&c_up) | (~a&b&~c_up) | (a&~b&~c_up) | (a&b&c_up)
Co = (~a&b&c_up) | (a&~b&c_up) | (a&b&~c_up) | (a&b&c_up)

3.3减法器

减法器,就是y=a-b-c_down,要考虑借位,如下真值表,a、b表示2个相减的数,c_down低位向本位的借位标志,Co表示计算结果有没有向高位借位
在这里插入图片描述
从真值表可以得出,y和Co的布尔表达式

y = (~a&~b&c_down) | (~a&b&~c_down) | (a&~b&~c_down) | (a&b&c_down)
C0 = (~a&~b&c_down) | (~a&b&~c_down) | (~a&b&c_down) | (a&b&c_down)

3.4级联加法器

3.5超前进位加法器

4.乘法器

4.1 移位相加乘法器

4.2 并行乘法器

5.其他组合逻辑

5.1 基本门电路

二输入与非门真值表

输入a输入b输出F
001
011
101
110

二输入与非门逻辑符号

在这里插入图片描述

数据流级描述二输入与非门

module nand_2(
	a,
	b,
	F
);

input 	a;
input 	b;
output 	F;

assign F = ~(a&b);

endmodule

在这里插入图片描述

行为级描述二输入与非门

module nand_2(
	a,
	b,
	F
);

input 	a;
input 	b;
output 	F;
reg		F;

always@(a or b)
begin
	if( a==1'b1 && b==1'b1)
		F = 0;	
	else 
		F = 1;

end

endmodule

在这里插入图片描述

5.2 三态门电路

三态门大多数挂在总线上,以实现不同数字部件之间的数据传输。

三态门是在普通电路的基础上附加控制电路,从而使得门电路的输出端除了输出高电平、低电平外,还可以出现第三种状态,即高阻状态(或称禁止态)。

三态门的逻辑符号

在这里插入图片描述
din为数据输入端,dout为数据输出端,en为控制输入端。

三态门的真值表

数据输入控制输入端数据输出
dinendout
x0z
010
111

行为级描述三态门

module tri_gate(
	din,
	en,
	dout
);
input din;
input en;
output dout;

reg dout;

always@(din or en)
begin 
	if(en) 	
		dout = din;
	else 		
		dout = 1'bz;
end

endmodule

行为级描述综合出来的电路如下:

在这里插入图片描述

数据流级描述三态门

module tri_gate(
	din,
	en,
	dout
);
input din;
input en;
output dout;

assign dout=en?din:1'bz;

endmodule

数据流级描述综合出来的电路如下:

和上面的一样
在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐