IC设计基础知识-典型电路Verilog编程

俗话说设计验证不分家,一个好的verifier一定也是一个好的designer,因此至少需要掌握一些典型电路的Verilog设计。

  1. 01011011101111…序列生成
    这种有规律的序列生成问题,一般来说都可以使用计数器解决。第1段序列为01,第2段为011,第3段为0111,由此可知每一段都是一个0和“段数个”1,因此需要两个计数器,cnt0记录当前是第几段,cnt1记录当前段输出了几个1,通过状态机实现。
    S0:输出0;
    S1:输出1;
    采用三段式写法,代码如下:
module seq_gen
	#(parameter cnt_size = 8)
	(input clk,
	input rstn,
	output seq);
		reg [cnt_size-1:0] cnt0;
		reg [cnt_size-1:0] cnt1;
		localparam S0 = 1'd0;
		localparam S1 = 1'd1;
		reg current_s;
		reg next_s;
		reg seq_r;
		always@(posedge clk or negedge rstn) begin
			if(!rstn)
				current_s <= S0;
			else
				current_s <= next_s;
		end
		always@(*) begin
				case(current_s)
					S0:	next_s <= S1;
					S1:	if(cnt1==0)
								next_s <= S0;
						else
								next_s <= S1;
					default: next_s <= S0;
				endcase
		end
		always@(posedge clk or negedge rstn) begin
			if(!rstn) begin
				cnt0 <= 0;
				cnt1 <= 0;
				seq_r <= 0;
			end
			else 	
				case(current_s)
						S0:	begin
								cnt0 <= cnt0 + 1;
								cnt1 <= cnt0;
								seq_r <= 0;
							end
						S1:	begin
								cnt1 <= cnt1 - 1;
								seq_r <= 1;
							end
						default: begin
									cnt0 <= 0;
									cnt1 <= 0;
									seq_r <= 0;
								end
					endcase
		end
		assign seq = seq_r;
endmodule

//test bench
`timescale 1ns/1ps
module tb();
	reg clk;
	reg rstn;
	wire seq;
	initial begin
		clk <= 1'b0;
		forever begin
			#5 clk <= !clk;
		end
	end
	initial begin
		#10 rstn <= 1'b0;
		repeat(10) @(posedge clk);
		rstn <= 1'b1;
	end
	seq_gen#(.cnt_size(8))
	dut(.clk(clk),
	.rstn(rstn),
	.seq(seq));
endmodule

modelsim仿真波形如下:
仿真波形

  1. 译码器
    译码器属于组合逻辑电路,用verilog表示的话,使用case语句即可,如下
//3-8译码器
module decode3_8(input [2:0] in,output reg [7:0] out);
	always@(*) begin
		case(in)
			3'd0:	out = 8'b0000_0001;
			3'd1:	out = 8'b0000_0010;
			3'd2:	out = 8'b0000_0100;
			3'd3:	out = 8'b0000_1000;
			3'd4:	out = 8'b0001_0000;
			3'd5:	out = 8'b0010_0000;
			3'd6:	out = 8'b0100_0000;
			3'd7:	out = 8'b1000_0000;
			default:	out = 8'b0000_0000;
		endcase
	end
endmodule 

//BCD译码器(4-10译码器),BCD码又称为8421码,用4位2进制数来表示十进制的0-9
module decode_BCD(input [3:0] in,output reg [9:0] out);
	always@(*) begin
		case(in)
			4'd0:	out = 10'b1111_1111_10;
			4'd1:	out = 10'b1111_1111_01;
			4'd2:	out = 10'b1111_1110_11;
			4'd3:	out = 10'b1111_1101_11;
			4'd4:	out = 10'b1111_1011_11;
			4'd5:	out = 10'b1111_0111_11;
			4'd6:	out = 10'b1110_1111_11;
			4'd7:	out = 10'b1101_1111_11;
			4'd8:	out = 10'b1011_1111_11;
			4'd9:	out = 10'b0111_1111_11;
			default:	out = 10'b1111_1111_11;
		endcase
	end
endmodule
//其他的也一样,用assign语句据说会好一些,不容易产生不定态。assign的思路就是根据真值表,单独对输出的每一位赋值就行了。

仿真略

  1. 编码器
    与译码器相反,也可以用always结合case或if…else…实现组合逻辑或者根据真值表由assign连续赋值完成。

  2. 加法器
    最基础的加法器是半加器和全加器,半加器不考虑来自地位的进位(一般不咋用),全加器考虑来自低位的进位。设计思路一般是通过较少位数的全加器构成多位数的加法器,比如1bit的全加器构成4bit加法器,4bit加法器再构成16bit加法器等等。在构成的过程中有两种方式:串联起来,构成行波进位加法器;通过一个额外的组合逻辑提前计算出最终的进位输出,称为超前进位加法器。
    半加器、全加器、行波进位加法器的原理很简单,行波进位加法器由于是一级级串联,低级的进位作为高级的输入,因此需要多个时钟周期才能计算完成,延时很大,多位加法器不采用这种方法;超前进位加法器通过数学原理提前计算出最终的进位和输出表达式,可以在一个周期完成计算,具体原理如下:
    对于1bit的全加器而言,sum = a ^ b ^ cin;cout = (a&&b) || (cin && (a^b));
    P=a ^ bG=a&&b,则有:sum = P ^ cincout =G || (cin && P)
    对于2bit的全加器而言,有sum[0] = P[0] ^ cin,cout[0] = G[0] || (cin && P[0]),sum[1] = P[1] ^ cout[0],cout = G[1] || (cout[0] && P[1]);
    对于N bit的全加器而言,有
    sum[0] = P[0] ^ cin,cout[0] = G[0] || (cin && P[0])
    sum[1] = P[1] ^ cout[0],cout = G[1] || (cout[0] && P[1]);
    sum[N-1] = P[N-1] ^ cout[N-2],cout = G[N-1] || (cout[N-2] && P[N-1]);
    综上,个人理解的超前进位加法器就是通过将cout[N-2],cout[N-3]等数全部用cin和p,g的组合逻辑表示出来,从而能够对输入变化产生立即响应

//1bit全加器
module adder_1(input a, input b, input cin, output sum, output cout);
	assign sum = a^b^cin;//奇数个1相异或的结果为1
	assign cout = (a&b) || (cin&(a^b));
endmodule

//4bit全加器-行波进位加法器
module adder_4(input [3:0] a, input [3:0] b, input cin, output [3:0] sum, output cout);
	wire c1, c2, c3;
	adder_1 dut1(.a(a[0]), .b(b[0]), .cin(cin), .sum(sum[0]), .cout(c1));
	adder_1 dut2(.a(a[1]), .b(b[0]), .cin(c1), .sum(sum[1]), .cout(c2));
	adder_1 dut3(.a(a[2]), .b(b[0]), .cin(c2), .sum(sum[2]), .cout(c3));
	adder_1 dut4(.a(a[3]), .b(b[0]), .cin(c3), .sum(sum[3]), .cout(cout));
endmodule

//4bit全加器-超前进位加法器
module adder_4(input [3:0] a, input [3:0] b, input cin, output [3:0] sum, output co);
	wire [3:0] p, g;
	wire [2:0] cout;
	//给P赋值
	assign p = {a[3]^b[3],a[2]^b[2],a[1]^b[1],a[0]^b[0]};
	//给G赋值
	assign g = {a[3]&&b[3],a[2]&&b[2],a[1]&&b[1],a[0]&&b[0]};
	//给进位cout赋值
 	assign cout[0] = g[0] || cin && p[0];
 	assign cout[1] = g[1] || (g[0] || cin && p[0]) && p[1];
 	assign cout[2] = g[2] || (g[1] || (g[0] || cin && p[0]) && p[1]) && p[2];
 	assign co = g[3] || (g[2] || (g[1] || (g[0] || cin && p[0]) && p[1]) && p[2]) && p[3];
 	//给sum赋值
	assign sum[0] = p[0] ^ cin;
	assign sum[1] = p[1] ^ (g[0] || cin && p[0]);
	assign sum[2] = p[2] ^ (g[1] || (g[0] || cin && p[0]) && p[1]);
 	assign sum[3] = p[3] ^ (g[2] || (g[1] || (g[0] || cin && p[0]) && p[1]) && p[2]);
endmodule
//4bit超前进位加法器testbench
 `timescale 1ns/1ps
 module tb ();
 	reg [3:0] a,b;
 	reg cin;
 	wire [3:0] sum;
 	wire cout;
 	initial begin 
 		a <= 1;
 		b <= 1;
 		cin <= 0;
 		forever begin
 			#20 a <= {$random}%16;
 				b <= {$random}%16;
 				cin <= {$random}%2;
 		end
 	end
 	adder_4 dut(.a(a),.b(b),.cin(cin),.sum(sum),.co(cout));
 endmodule

下图为4bit超前进位加法器综合电路(quartus综合):
4bit超前进位加法器综合电路

  1. 乘法器
    加减乘除都是由加法完成的。
    1bit x 1bit为例,有a x b = 1 when a&b;
    1bit x 4bit为例,有abcd x a = abcd & {4{a}};
    4bit x 4bit为例,有abcd x abcd = abcd x d x 2^0 +abcd x c x 2^1 + abcd x b x 2^2 + abcd x a x 2^3 = abcd&{4{a}} << 3 + abcd & {4{b}} << 2 + abcd &{4{c}} << 1 + abcd&{4{d}} << 0;
    因此,n位 x m位的乘法为:a x b = a & {m{b[m1]}} << (n-1) + … + a & {m{b[0]}} << 0;一般将位宽小的作为乘数,可以减小逻辑运算和加法运算次数。代码如下(移位前需要进行扩位):
//加法器树形乘法器
module mul_4x4(input [3:0] a, input 
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值