任意切换1-8分频

17 篇文章 21 订阅

今天的笔试题是某芸科技的现场笔试题,数字前端的笔试题,要求很简单,就是现场写出代码实现:

任意切换1-8分频,且无论奇分频还是偶分频,占空比均为50%,我至今仍然认为,在那种紧张且时间有限的情况下(本科大约预留15分钟),真的能设计出这种可任意切换的分频电路(之前有所准备的话可以),反正我是没写出来,笔试归来,我花了多个小时的时间写了一个且仿真了下。

个人认为,这个电路的设计的步骤分为两部分,

第一:

确定整体架构,包括输入输出:如何任意切换?我写了一个分频模块,其中一个输入为div,你输入1到8,可实现1到8的分频。

第二:

确定分频实现电路,分频大家都会写,实现过程可分为奇分频的实现以及偶分频的实现,这两种的实现方法是不一样的,偶分频简单,直接计数即可,奇分频需要三个步骤,第一个步骤就是计数,第二个步骤就是下降沿采样,第三个步骤就是相与或相或,具体实现方法后面再说。


思路简述:

上面说了这个电路的设计分为两部分,第一部分确定整体架构,其中最重要的是输入输出:

input clk,
input [3:0] div,
input rst_n,
output clk_out

确定好输入输出之后,我们想,我们是否需要一个使能信号,当输入分频述div后,我们让某一种分频模式使能,我们有8中分频模式,总不能傻傻地设置8个使能变量吧,en1,en2,...,en8;

当然这样很不好看,而且不利于后面程序编写,我的方式是:

reg [7:0] fre_en;

每次只能使能一位,fre_en[i]使能代表i+1分频模式有效。

通过div的值来确认某种模式使能,其实现如下:

	localparam DIV1 = 1, DIV2 = 2, DIV3 = 3, DIV4 = 4;
	localparam DIV5 = 5, DIV6 = 6, DIV7 = 7, DIV8 = 8;
 
	reg [7:0] fre_en;
 
 
	always@(posedge clk or negedge rst_n) begin
		if(~rst_n) begin
			fre_en <= 0;
		end
		else begin
			case(div)
				DIV1: fre_en <= 8'b0000_0001;
				DIV2: fre_en <= 8'b0000_0010;
				DIV3: fre_en <= 8'b0000_0100; 
				DIV4: fre_en <= 8'b0000_1000;
				DIV5: fre_en <= 8'b0001_0000;
				DIV6: fre_en <= 8'b0010_0000;
				DIV7: fre_en <= 8'b0100_0000;
				DIV8: fre_en <= 8'b1000_0000;
			endcase
		end
 
 
	end

我们说了,分频分为奇分频和偶分频,二者都有计数的过程,但是这里想做出一点改变,因为1分频等于没分频,2分频直接对输出时钟取反即可,其他分频就和计数有关了。

我们写一个计数器,用于其他分频模式,8分频需要计数次数最多,我们设计数器位数为4位,其实3位就够了。

根据模式使能确定计数次数。

//计数模块
	reg [3:0] fre_cnt;
	always @(posedge clk or negedge rst_n) begin
		if(~rst_n) begin
			fre_cnt <= 0;
		end 
		else begin
			case(1'b1)
				fre_en[0]: begin
					;
				end
				fre_en[1]: begin
					;
				end
				fre_en[2]: begin //3分频计数
					if(fre_cnt < 2) fre_cnt <= fre_cnt + 1;
					else fre_cnt <= 0;
				end
				fre_en[3]: begin
					if(fre_cnt < 3) fre_cnt <= fre_cnt + 1;
					else fre_cnt <= 0; 
				end
				fre_en[4]: begin
					if(fre_cnt < 4) fre_cnt <= fre_cnt + 1;
					else fre_cnt <= 0;
				end
				fre_en[5]: begin
					if(fre_cnt < 5) fre_cnt <= fre_cnt + 1;
					else fre_cnt <= 0; 
				end
				fre_en[6]: begin
					if(fre_cnt < 6) fre_cnt <= fre_cnt + 1;
					else fre_cnt <= 0;
				end
				fre_en[7]: begin
					if(fre_cnt < 7) fre_cnt <= fre_cnt + 1;
					else fre_cnt <= 0;  
				end
			endcase 
		end
	end

有了计数模块,就可以设计分频实现模块了,根据分频使能来确定分频模式,不同的分频模式对应不同的分频实现方式。

reg clk_out_r;
	reg clk_even;
 
	always @(posedge clk or negedge rst_n) begin
		if(~rst_n) begin
			clk_even <= 0;
			clk_out_r <= 0;
		end 
		else begin
			case(1'b1)
				fre_en[0]: begin //1 分频
					;
				end
				fre_en[1]: begin //2 分频
					clk_even <= ~clk_even;
				end
				fre_en[2]: begin //3 分频
					if(fre_cnt == 1) clk_out_r <= ~clk_out_r;
					else if(fre_cnt == 2)  clk_out_r <= ~clk_out_r;
					else clk_out_r <= clk_out_r;
				end
				fre_en[3]: begin //4 分频
					if(fre_cnt == 1) clk_even <= ~clk_even;
					else if(fre_cnt ==3) clk_even <= ~clk_even;
					else clk_even <= clk_even;
				end
				fre_en[4]: begin //5分频
					if(fre_cnt == 2) clk_out_r <= ~clk_out_r;
					else if(fre_cnt == 4) clk_out_r <= ~clk_out_r;
					else clk_out_r <= clk_out_r;  
				end
				fre_en[5]: begin // 6分频
					if(fre_cnt == 2) clk_even <= ~clk_even;
					else if(fre_cnt == 5) clk_even <= ~clk_even;
					else clk_even <= clk_even;
				end
				fre_en[6]: begin //7 分频
					if(fre_cnt == 3) clk_out_r <= ~clk_out_r;
					else if(fre_cnt == 6) clk_out_r <= ~clk_out_r;
					else clk_out_r <= clk_out_r; 
				end
				fre_en[7]: begin //8 分频
					if(fre_cnt == 3) clk_even <= ~clk_even;
					else if(fre_cnt == 7) clk_even <= ~clk_even;
					else clk_even <= clk_even; 
				end
			endcase  
		end
	end

解释下上面的几个reg变量以及wire变量的意义,由于考虑到奇数分频最后的输出clk_out需要一段组合逻辑,所以需要使能wire类型。

这就导致这个分频实现模块里不能直接使用clk_out作为左值。

用clk_even用作偶分频输出clk_out的缓冲;

至于clk_out_r,以及clk_out_rr是为了奇分频而声明的。

这里提一下奇分频的实现原理:(之前写过Verilog HDL 训练】第 11 天(分频电路)

假如N为奇数,那么进行N分频,需要先设计一个占空比为(N-1)/2 : N的分频输出clk_out_r,之后用时钟下降沿采样clk_out_r得到clk_out_rr;最后将clk_out_r或上clk_out_rr即可。

具体可参考上面引用的博文。

至于偶分频,则需要计数一半反转一次,计数结束反转一次即可。

好了,下面继续解决奇分频的下降沿采样问题:

//下降沿采样模块
	reg clk_out_rr;
 
	always @(negedge clk or negedge rst_n) begin
		if(~rst_n) begin
			clk_out_rr <= 0;
		end 
		else begin
			case(1'b1)
				fre_en[0]: ;
				fre_en[1]: ;
				fre_en[2]: begin
					clk_out_rr <= clk_out_r;
				end
				fre_en[3]: ;
				fre_en[4]: begin
					clk_out_rr <= clk_out_r;
				end
				fre_en[5]: ;
				fre_en[6]: begin
					clk_out_rr <= clk_out_r;
				end
				fre_en[7]: ;
 
			endcase 
 
 
 
		end
	end
最后输出分频时钟:
//产生分频时钟	
 assign clk_out = ( fre_en[0] | fre_en[1] | fre_en[3] | fre_en[5] | fre_en[7] )? clk_even : clk_out_r | clk_out_rr;
由于一次输入只能有一个使能位有效,所以上述assign自然明白了吧。

大概就是这么多了,给出一个简单的仿真,文章的最后会给出完整的代码以及仿真代码。

先给出一个仿真,假如输入div为5,则进行5分频,则:

假如输入div为6则进行6分频:

先5分频在1分频:

完美切换。

最后的最后给出完整版程序以及测试。

 


完整版程序:

 
//任意切换1——8分频电路设计(某芸科技),无论是奇分频还是偶分频,占空比均为50%
 
module Fre_Div(
	input clk,
	input [3:0] div,
	input rst_n,
	output clk_out
 
 
	);
 
	localparam DIV1 = 1, DIV2 = 2, DIV3 = 3, DIV4 = 4;
	localparam DIV5 = 5, DIV6 = 6, DIV7 = 7, DIV8 = 8;
 
	reg [7:0] fre_en;
 
 
	always@(posedge clk or negedge rst_n) begin
		if(~rst_n) begin
			fre_en <= 0;
		end
		else begin
			case(div)
				DIV1: fre_en <= 8'b0000_0001;
				DIV2: fre_en <= 8'b0000_0010;
				DIV3: fre_en <= 8'b0000_0100; 
				DIV4: fre_en <= 8'b0000_1000;
				DIV5: fre_en <= 8'b0001_0000;
				DIV6: fre_en <= 8'b0010_0000;
				DIV7: fre_en <= 8'b0100_0000;
				DIV8: fre_en <= 8'b1000_0000;
			endcase
		end
 
 
	end
	reg clk_out_r;
	reg clk_even;
 
 
	always @(posedge clk or negedge rst_n) begin
		if(~rst_n) begin
			clk_even <= 0;
			clk_out_r <= 0;
		end 
		else begin
			case(1'b1)
				fre_en[0]: begin //1 分频
					;
				end
				fre_en[1]: begin //2 分频
					clk_even <= ~clk_even;
				end
				fre_en[2]: begin //3 分频
					if(fre_cnt == 1) clk_out_r <= ~clk_out_r;
					else if(fre_cnt == 2)  clk_out_r <= ~clk_out_r;
					else clk_out_r <= clk_out_r;
				end
				fre_en[3]: begin //4 分频
					if(fre_cnt == 1) clk_even <= ~clk_even;
					else if(fre_cnt ==3) clk_even <= ~clk_even;
					else clk_even <= clk_even;
				end
				fre_en[4]: begin //5分频
					if(fre_cnt == 2) clk_out_r <= ~clk_out_r;
					else if(fre_cnt == 4) clk_out_r <= ~clk_out_r;
					else clk_out_r <= clk_out_r;  
				end
				fre_en[5]: begin // 6分频
					if(fre_cnt == 2) clk_even <= ~clk_even;
					else if(fre_cnt == 5) clk_even <= ~clk_even;
					else clk_even <= clk_even;
				end
				fre_en[6]: begin //7 分频
					if(fre_cnt == 3) clk_out_r <= ~clk_out_r;
					else if(fre_cnt == 6) clk_out_r <= ~clk_out_r;
					else clk_out_r <= clk_out_r; 
				end
				fre_en[7]: begin //8 分频
					if(fre_cnt == 3) clk_even <= ~clk_even;
					else if(fre_cnt == 7) clk_even <= ~clk_even;
					else clk_even <= clk_even; 
				end
			endcase  
		end
	end
 
	//下降沿采样模块
	reg clk_out_rr;
 
	always @(negedge clk or negedge rst_n) begin
		if(~rst_n) begin
			clk_out_rr <= 0;
		end 
		else begin
			case(1'b1)
				fre_en[0]: ;
				fre_en[1]: ;
				fre_en[2]: begin
					clk_out_rr <= clk_out_r;
				end
				fre_en[3]: ;
				fre_en[4]: begin
					clk_out_rr <= clk_out_r;
				end
				fre_en[5]: ;
				fre_en[6]: begin
					clk_out_rr <= clk_out_r;
				end
				fre_en[7]: ;
 
			endcase 
 
 
 
		end
	end
 
	
//产生分频时钟
 
 assign clk_out = fre_en[0] ? clk :( (  fre_en[1] | fre_en[3] | fre_en[5] | fre_en[7] )? clk_even : clk_out_r | clk_out_rr );
 
//计数模块
	reg [3:0] fre_cnt;
	always @(posedge clk or negedge rst_n) begin
		if(~rst_n) begin
			fre_cnt <= 0;
		end 
		else begin
			case(1'b1)
				fre_en[0]: begin
					;
				end
				fre_en[1]: begin
					;
				end
				fre_en[2]: begin //3分频计数
					if(fre_cnt < 2) fre_cnt <= fre_cnt + 1;
					else fre_cnt <= 0;
				end
				fre_en[3]: begin
					if(fre_cnt < 3) fre_cnt <= fre_cnt + 1;
					else fre_cnt <= 0; 
				end
				fre_en[4]: begin
					if(fre_cnt < 4) fre_cnt <= fre_cnt + 1;
					else fre_cnt <= 0;
				end
				fre_en[5]: begin
					if(fre_cnt < 5) fre_cnt <= fre_cnt + 1;
					else fre_cnt <= 0; 
				end
				fre_en[6]: begin
					if(fre_cnt < 6) fre_cnt <= fre_cnt + 1;
					else fre_cnt <= 0;
				end
				fre_en[7]: begin
					if(fre_cnt < 7) fre_cnt <= fre_cnt + 1;
					else fre_cnt <= 0;  
				end
			endcase 
		end
	end
 
 
 
 
 
 
endmodule

测试:

module Sim_Freq_Div(
 
    );
	
	reg clk;
	reg rst_n;
	reg [3:0] div;
	wire clk_out;
	
	initial begin
		clk = 0;
		forever
			#2 clk = ~clk;
	
	end
	
	
	initial begin
		rst_n = 0;
		div = 5;
		#15
		rst_n = 1;
		#60
		div = 1;
		
	
	end
	
	Fre_Div fre_div_tb(
	.clk(clk),
	.rst_n(rst_n),
	.div(div),
	.clk_out(clk_out)
	);
	
	
endmodule

 

小数N分频锁相环(PLL)合成器是一种常用于电子设备和通信系统中的频率合成器。它的主要功能是将输入信号的频率按照一定的比例进行合成,并且具有较高的频率稳定性和抗噪声能力。 PLL的基本原理如下:首先,将输入信号通过一个分频器将其频率分成N个等份,得到一个参考信号。然后,将参考信号与振荡器输出的信号进行相位比较,并根据比较结果调整振荡器的频率。通过不断地调整,最终使得振荡器输出的信号的频率与输入信号的频率按照一定的比例合成。 小数N分频锁相环合成器是在传统PLL的基础上增加了一个小数分频器模块。该模块可以进一步将输入信号的频率进行精细的调整,因此可以实现更高的频率合成精度。例如,传统PLL可能只能实现整数倍频合成,而小数N分频PLL可以实现任意两个整数之间的频率分数倍频合成。 小数N分频PLL合成器的应用非常广泛。比如在移动通信系统中,它可以用于产生基站的参考时钟信号;在数字电视广播系统中,可以用于实现多个不同频率信号之间的切换;在射频电路设计中,可以用于产生多个相位相同的本振信号等。 总的来说,小数N分频锁相环合成器是一种高性能的频率合成器,具有频率稳定性高、抗噪声能力强等特点。它在电子设备和通信系统中的应用非常广泛,为信号处理和调制提供了可靠的基础。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值