Implementación Verilog del PLL

  El bucle de bloqueo de fase (PLL) es un algoritmo de seguimiento de fase y frecuencia de uso común, que se usa ampliamente en la demodulación de señales, la conexión a la red de CA y otros campos. Este artículo presenta el principio del bucle de bloqueo de fase totalmente digital y luego proporciona la implementación y simulación de Verilog.

Principio de bloqueo PLL

  La estructura del bucle de bloqueo de fase se muestra en la siguiente figura, que se compone principalmente de un detector de fase, un filtro de bucle, un oscilador controlado por voltaje, etc.

inserte la descripción de la imagen aquí

Entre ellos, el detector de fase es un multiplicador y la señal de referencia ui u_i está configuradatuyo, señal de casa uo u_otuoAmbas son señales sinusoidales
ui ( t ) = cos ( ω 1 t + φ 1 ) u_i(t)=cos(\omega_1 t+\varphi_1)tuyo( t )=porque ( oh1t+Fi1)

uo ( t ) = cos ( ω 2 t + ϕ 2 ) u_o(t)=cos(\omega_2 t+\varphi_2)tuo( t )=porque ( oh2t+Fi2)

  Según el producto y la fórmula de diferencia, ui u_ituyodale uo u_otuoEl producto de contendrá ω 1 + ω 2 \omega_1+\omega_2Vaya1+Vaya2Suma ω 1 − ω 2 \omega_1-\omega_2Vaya1Vaya2Dos componentes de frecuencia, después del filtrado de paso bajo de LF, solo queda la señal de frecuencia diferente de los dos
uc = cos [ ( ω 1 − ω 2 ) t + ( φ 1 − φ 2 ) ] = cos [ 2 π ( f 1 − f 2 ) t + ( φ 1 − φ 2 ) ] \begin{aligned} u_c&=cos[(\omega_1-\omega_2)t+(\varphi_1-\varphi_2)]\\ &=cos[2\pi(f_1 - f_2)t+(\varphi_1-\varphi_2)] \end{alineado}tuc=porque [( oh1Vaya2) t+( f1Fi2)]=porque [ 2 π ( f1F2) t+( f1Fi2) ]
使用f 2 = f 0 + K 0 uc f_2=f_0+K_0 u_cF2=F0+k0tucEl bloqueo de fase se puede lograr controlando la frecuencia del oscilador controlado por voltaje (digital generalmente generado por tecnología DDS).

  Supongamos que la señal de entrada es relativa a la frecuencia de referencia f 0 f_0F0existeΔ f \Delta fΔ f desviación de frecuencia, entonces las dos señales tendrán una desviación de fase fijaΔ φ \Delta \varphiΔ φ , las siguientes relaciones
Δ f = K 0 cos ( Δ φ ) \Delta f=K_0cos(\Delta \varphi)f_ _=k0cos ( Δ φ ) Por supuesto, también debemos prestar atención al Δ φ \Delta \varphi
aquíNo se puede determinar el signo de Δφ .

implementación de verilog

  El programa principal del módulo PLL es el siguiente

/* 
 * file			: ADPLL.v
 * author		: 今朝无言
 * lab		    : WHU-EIS-LMSWE
 * date			: 2023-08-03
 * version		: v1.0
 * description	: 锁相环
 * Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.
 */
module ADPLL(
input						clk,
input						rst_n,

input		signed	[15:0]	A,		//参考信号
input		signed	[15:0]	B,		//本地信号

output		signed	[15:0]	df		//频偏
);

parameter	CLK_FREQ	= 1_000_000;	//采样频率

reg signed	[15:0]	df	= 16'd0;

//-----------------------multi---------------------------------
reg	signed	[31:0]	multi	= 32'd0;

always @(posedge clk) begin
	if(~rst_n) begin
		multi	<= 32'd0;
	end
	else begin
		multi	<= A*B;
	end
end

//------------------------FIR---------------------------------
wire	signed	[15:0]	multi_filt  [1:3];

localparam	FIR_N = 20;	//FIR阶数

wire	[16*(FIR_N+1)-1:0]	FIR_params;

FIR_params_0d1 FIR_params_inst(
	.params		(FIR_params)
);

wire    clk_div10;
wire    clk_div100;

clkdiv #(.N(10)) clkdiv10(
	.clk_in     (clk),
	.clk_out    (clk_div10)
);

clkdiv #(.N(100)) clkdiv100(
	.clk_in     (clk),
	.clk_out    (clk_div100)
);

//低通滤波						多级低通滤波,中间穿插下采样
FIR_filter #(.N(FIR_N + 1))
FIR_filter_inst1(
	.clk			(clk),
	.rst_n			(rst_n),

	.filter_params	(FIR_params),

	.data_in		(multi[31:16]),
	.data_out		(multi_filt[1])
);

//低通滤波
FIR_filter #(.N(FIR_N + 1))
FIR_filter_inst2(
	.clk			(clk_div10),
	.rst_n			(rst_n),

	.filter_params	(FIR_params),

	.data_in		(multi_filt[1]),
	.data_out		(multi_filt[2])
);

//低通滤波
FIR_filter #(.N(FIR_N + 1))
FIR_filter_inst3(
	.clk			(clk_div100),
	.rst_n			(rst_n),

	.filter_params	(FIR_params),

	.data_in		(multi_filt[2]),
	.data_out		(multi_filt[3])
);

//---------------------control---------------------------------
always @(posedge clk_div100) begin
	df	<= multi_filt[3];		//  df=K*multi_filt,此处省略鉴相灵敏度K,外部请自行设置合理的K值s
end

endmodule

  El filtro de paso bajo y su código de parámetro son los siguientes

/* 
 * file         : FIR_filter.v
 * author       : 今朝无言
 * lab		    : WHU-EIS-LMSWE
 * date		    : 2023-07-03
 * version      : v1.0
 * description  : FIR 滤波器
 */
module FIR_filter(
input							clk,
input							rst_n,

input				[16*N-1:0]	filter_params,

input		signed	[15:0]		data_in,
output	reg	signed	[15:0]		data_out
);

parameter	N		= 32;	//滤波器参数个数
parameter	div_N	= 16;	//sum结果除 2^div_N,作为 filter 的输出

//FIR 滤波器参数
reg	signed	[15:0] b[0:N-1];

integer	m;
always @(*) begin
	for(m=0; m<N; m=m+1) begin
		b[m]	<= filter_params[(m << 4) +: 16];
	end
end

reg	signed	[15:0]	shift_reg[0:N-1];

integer	i;
always @(posedge clk) begin
	if(~rst_n) begin
		for(i=N-1; i>=0; i=i-1) begin
			shift_reg[i]	<= 16'd0;
		end
	end
	else begin
		for(i=N-1; i>0; i=i-1) begin
			shift_reg[i]	<= shift_reg[i-1];
		end
		shift_reg[0]		<= data_in;
	end
end

reg		signed	[31:0]	multi[0:N-1];

integer	j;
always @(*) begin
	for(j=0; j<N; j=j+1) begin
		multi[j]	<= shift_reg[j] * b[j];
		//这里可以考虑使用multiplier IP核,使用LUT搭建(而这里直接乘使用的是DSP资源,一般的FPGA芯片只有几百个)
	end
end

reg		signed	[47:0]	sum;

integer	k;
always @(*) begin
	sum		= 0;
	for(k=0; k<N; k=k+1) begin
		sum	= sum + multi[k];
	end
end

always @(posedge clk) begin
	data_out	<= sum[47-div_N : 32-div_N];
end

endmodule
/* 
 * file			: FIR_params.v
 * author		: 今朝无言
 * lab			: WHU-EIS-LMSWE
 * date			: 2023-08-04
 * version		: v1.0
 * description	: FIR 滤波器    lowpass   N=20   fc=0.1 fs
 */
module FIR_params_0d1(
output	[335:0]	params
);

assign	params[15:0]	= 16'h0000;
assign	params[31:16]	= 16'h0057;
assign	params[47:32]	= 16'h0131;
assign	params[63:48]	= 16'h0302;
assign	params[79:64]	= 16'h0616;
assign	params[95:80]	= 16'h0A6D;
assign	params[111:96]	= 16'h0FA8;
assign	params[127:112]	= 16'h1518;
assign	params[143:128]	= 16'h19E1;
assign	params[159:144]	= 16'h1D28;
assign	params[175:160]	= 16'h1E53;
assign	params[191:176]	= 16'h1D28;
assign	params[207:192]	= 16'h19E1;
assign	params[223:208]	= 16'h1518;
assign	params[239:224]	= 16'h0FA8;
assign	params[255:240]	= 16'h0A6D;
assign	params[271:256]	= 16'h0616;
assign	params[287:272]	= 16'h0302;
assign	params[303:288]	= 16'h0131;
assign	params[319:304]	= 16'h0057;
assign	params[335:320]	= 16'h0000;

endmodule

Para conocer esta parte del filtro FIR, consulte la publicación de mi blog anterior.

simulación

  El código de prueba de simulación es el siguiente.

`timescale 100ns/1ns

module PLL_tb();

reg		clk_1M	= 1'b1;
always #5 begin
	clk_1M	<= ~clk_1M;
end

reg		rst_n	= 1'b1;

//---------------------参考信号A-------------------------------
wire			[15:0]	A_out_tmp;
wire	signed	[15:0]	A_out;		//参考信号

localparam	f0	= 24'd10_000;
localparam	df	= -24'd9;		//频率偏差

DDS #(
	.Freq(1_000_000)
)
DDS_inst1(
	.clk		(clk_1M),
	.rst_n		(rst_n),

	.fout		(f0+df),
	.phase0		(16'd0),

	.sin_out	(A_out_tmp)
);

assign	A_out	= A_out_tmp - 16'd32768;

//---------------------本地信号B-------------------------------
wire			[15:0]	B_out_tmp;
wire	signed	[15:0]	B_out;

wire	signed	[23:0]	df2;		//控制本地信号的频偏

DDS #(
	.Freq		(1_000_000)
)
DDS_inst2(
	.clk		(clk_1M),
	.rst_n		(rst_n),

	.fout		(f0+df2),
	.phase0		(16'd0),

	.sin_out	(B_out_tmp)
);

assign	B_out	= B_out_tmp - 16'd32768;

//-----------------------PLL---------------------------------
wire	signed	[15:0]	df_PLL;

ADPLL #(
	.Freq		(1_000_000)
)
PLL_inst(
	.clk		(clk_1M),
	.rst_n		(rst_n),

	.A			(A_out),		//参考信号
	.B			(B_out),		//本地信号

	.df			(df_PLL)		//频偏
);

assign	df2	= df_PLL/64;

//-----------------------tb---------------------------------
initial begin
	rst_n	<= 1'b0;
	#5000;
	rst_n	<= 1'b1;
	#100;

	#1000000;
	$stop;
end

endmodule

  El código DDS es el siguiente

/* 
 * file			: DDS.v
 * author		: 今朝无言
 * Lab			: WHU-EIS-LMSWE
 * date			: 2023-05-17
 * version		: v1.0
 * description	: 根据给定频率输出正弦信号
 * Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.
 */
module DDS(
input			clk,
input			rst_n,

input	[23:0]	fout,		//输出正弦波的频率  1k-10M 要24位
input	[15:0]	phase0,		//初相

output	[15:0]	sin_out
);

parameter	Freq	= 100_000_000;		//clk频率,Hz

//-----------------相位累加器-----------------------
reg		[47:0]	int_f_16	= 48'd0;	//相位累加器,x-16定点数
wire	[55:0]	dphi_16;				//相位步进

//dphi*Freq=fout*T, T=65536
assign	dphi_16	= (fout << 32)/Freq;

always @(posedge clk or negedge rst_n) begin
	if(~rst_n) begin
		int_f_16	<= 48'd0;
	end
	else begin
		int_f_16	<= int_f_16 + dphi_16;
	end
end

//-----------------正弦查找表-----------------------
wire	[15:0]	phase;

sin_gen sin_gen_inst(
	.clk		(clk),

	.phase		(phase),		//相位
	.sin_out	(sin_out)
);

assign phase	= phase0 + (int_f_16 >> 16);

endmodule

La tabla de búsqueda de senos correspondiente es la siguiente (este módulo utiliza el método de interpolación lineal, que reduce el error de cuantificación en dos órdenes de magnitud con solo un pequeño aumento en el consumo de recursos; esta parte también se puede encontrar en mi publicación de blog anterior)

/* 
 * file			: sin_gen.v
 * author		: 今朝无言
 * Lab			: WHU-EIS-LMSWE
 * date			: 2023-05-17
 * version		: v1.0
 * description	: 根据给定相位输出正弦信号
 * Copyright © 2023 WHU-EIS-LMSWE, All Rights Reserved.
 */
module sin_gen(
input			clk,

input	[15:0]	phase,		//相位,0~65535对应[0~2pi)
output	[15:0]	sin_out
);

//---------------------正弦查找表-------------------------
wire	[7:0]	addr1;
wire	[7:0]	addr2;
wire	[15:0]	sin_dat1;
wire	[15:0]	sin_dat2;

//sin rom, 16bit, 256 depth
sin_rom sin_rom_inst1(
	.clka	(clk),
	.addra	(addr1),
	.douta	(sin_dat1)
);

sin_rom sin_rom_inst2(
	.clka	(clk),
	.addra	(addr2),
	.douta	(sin_dat2)
);

//-----------线性插值获取更精确的相位分辨率-------------------
assign	addr1	= (phase>>8);
assign	addr2	= (phase>>8)+1;

wire	[15:0]	phase1;
wire	[15:0]	phase2;

assign	phase1	= addr1<<8;
assign	phase2	= addr2<<8;

reg		[15:0]	phase_d0;
reg		[15:0]	phase_d1;	//由于rom数据2拍后才给出,因此phase需要与之同步
reg		[15:0]	phase1_d0;
reg		[15:0]	phase1_d1;

always @(posedge clk) begin
	phase_d0	<= phase;
	phase_d1	<= phase_d0;

	phase1_d0	<= phase1;
	phase1_d1	<= phase1_d0;
end

wire	[31:0]	multi;
assign	multi	= (sin_dat2 > sin_dat1)? 
				(sin_dat2 - sin_dat1)*(phase_d1 - phase1_d1) : 
				(sin_dat1 - sin_dat2)*(phase_d1 - phase1_d1);

assign	sin_out	= (sin_dat2 > sin_dat1)? 
				sin_dat1 + (multi >> 8) : sin_dat1 - (multi >> 8);

endmodule

  Los resultados de la simulación son los siguientes.

inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/qq_43557686/article/details/132131695
Recomendado
Clasificación