基于actel fpga的周期反馈式纯数字锁相环:
简单的说就是建立一个状态机,对输入周期参数在产生时钟的每个周期的第一时间更新值,然后进入下一个状态计数,计数到和输入周期一致的时候又回到初始状态。代码综合仿真正确, 过程比较简单, 就不班门弄斧了. 不妥之处, 共同学习交流 ,一起改进, 代码如下:
//顶层模块:
module TE_DPLL_B(
sys_clk,
sys_rst,
clk_in,
clk_out
);
//---全局参数---
parameter DATA_W = 16; // 周期计数器及数据位宽,根据系统时钟与输入时钟的差值调整
parameter PN_EDGE = 2'b01; // 沿检测参数(01=上升沿,10=下降沿)
parameter DPLL_M = 2; // M 参数
parameter DPLL_N = 10; // N 参数
//---输入输出接口---
input sys_clk; // 系统时钟
input sys_rst; // 系统重置
input clk_in; // 输入时钟
output clk_out; // 输出时钟
//---内部信号---
wire [DATA_W-1:0] n_ti; // 输入时钟周期值
//---------------------------------------------------------
// 输入时钟周期测量模块,对输入时钟采样,测量输出时钟周期Ti
//---------------------------------------------------------
TE_DPLL_Figure TE_DPLL_Figure_0(
.sys_clk(sys_clk), // 系统时钟
.sys_rst(sys_rst), // 系统重置
.clk_i(clk_in), // 输入时钟
.t_o(n_ti) // 输入时钟周期值
);
defparam TE_DPLL_Figure_0.CNT_W = DATA_W; // 周期计数器位宽
defparam TE_DPLL_Figure_0.PN_EDGE = PN_EDGE; // 沿检测参数(01=上升沿,10=下降沿)
//---------------------------------------------------------
// 例化反馈模块
//---------------------------------------------------------
TE_DPLL_Feed TE_DPLL_Feed_0(
.sys_clk(sys_clk), // 系统时钟
.sys_rst(sys_rst), // 系统重置
.ti_i(n_ti), // 输入时期周期值
.clk_o(clk_out) // 输出时钟
);
defparam TE_DPLL_Feed_0.DATA_W = DATA_W; // 数据位宽
defparam TE_DPLL_Feed_0.PN_EDGE = PN_EDGE; // 沿检测参数(01=上升沿,10=下降沿)
defparam TE_DPLL_Feed_0.DPLL_M = DPLL_M; // M 参数
defparam TE_DPLL_Feed_0.DPLL_N = DPLL_N; // N 参数
endmodule
本帖最后由 teleagle 于 2018-10-8 22:19 编辑
//周期采样模块
module TE_DPLL_Figure(
sys_clk,
sys_rst,
clk_i,
t_o
);
//---全局参数---
parameter CNT_W = 16; // 周期计数器位宽
parameter PN_EDGE = 2'b01; // 沿检测参数(01=上升沿,10=下降沿)
//---输入输出接口---
input sys_clk; // 系统时钟
input sys_rst; // 系统重置
input clk_i; // 输入待测时钟
output t_o; // 输出时钟周期
reg [CNT_W-1:0] t_o;
//---内部寄存器---
reg [1:0] n_edge; // 待测时钟缓冲,用于检测时钟沿
reg [CNT_W-1:0] n_cnt; // 周期计数器
reg n_state; // 状态
//---------------------------------------------------------
// 2D 左移位寄存器,用于检测上升沿或下降沿
//---------------------------------------------------------
always @(posedge sys_clk)
begin
if(sys_rst)
n_edge <= 0;
else
n_edge <= {n_edge[0],clk_i};
end
//---------------------------------------------------------
// 一段状态机
//---------------------------------------------------------
always @(posedge sys_clk)
begin
if(sys_rst)
begin
n_state <= 0;
n_cnt <= 0;
t_o <= 0;
end
else
case(n_state)
0 : begin
n_state <= (n_edge == PN_EDGE) ? 1 : 0; // 检测正向沿(由PN_EDGE决定)
n_cnt <= (n_edge == PN_EDGE) ? 1 : 0;
end
1 : begin
n_state <= (n_edge == PN_EDGE) ? 0 : 1; // 非正向沿时(由PN_EDGE决定),跳回状态0
n_cnt <= n_cnt + 1;
t_o <= (n_edge == PN_EDGE) ? n_cnt : t_o;
end
default : n_state <= 0;
endcase
end
endmodule
// 反馈模块
module TE_DPLL_Feed(
sys_clk,
sys_rst,
ti_i,
clk_o
);
//---全局参数---
parameter DATA_W = 16; // 数据位宽
parameter PN_EDGE = 2'b01; // 沿检测参数(01=上升沿,10=下降沿)
parameter DPLL_M = 10; // M 参数
parameter DPLL_N = 10; // N 参数
//---输入输出接口---
input sys_clk; // 系统时钟
input sys_rst; // 系统重置
input [DATA_W-1:0] ti_i; // 输入时钟周期值
output clk_o; // 输出时钟
//---内部信号---
wire [DATA_W-1:0] n_to; // 输出时钟周期值
//---内部寄存器---
reg [DATA_W-1:0] n_terr; // 开环误差,除 M 后的误差值
reg [DATA_W-1:0] n_err; // 反馈误差,输入输出时钟周期误差
//---------------------------------------------------------
// 反馈误差(To/N)
// 反馈的时钟周期乘以反馈增益1/N
//---------------------------------------------------------
always @(posedge sys_clk)
begin
if(sys_rst)
n_err <= 0;
else
n_err <= ti_i - (n_to / DPLL_N); // 输入时钟周期 - (输出时钟周期 / N)
end
//---------------------------------------------------------
// 开环误差 (n_err/M)
// 反馈误差乘以开环增益1/M
//---------------------------------------------------------
always @(posedge sys_clk)
begin
if(sys_rst)
n_terr <= 0;
else
n_terr <= n_err / DPLL_M;
end
//---------------------------------------------------------
// 将 n_err/M 值反馈给控制对象
//---------------------------------------------------------
TE_DPLL_Gen TE_DPLL_Gen_0(
.sys_clk(sys_clk), // 系统时钟
.sys_rst(sys_rst), // 系统重置
.terr_i(n_terr), // 误差 n_err 除 M 后的值
.clk_o(clk_o) // 输出时钟
);
defparam TE_DPLL_Gen_0.DATA_W = DATA_W; // 数据位宽
//---------------------------------------------------------
// 输出时钟周期测量模块,对输出时钟采样,测量输出时钟周期 To
//---------------------------------------------------------
TE_DPLL_Figure TE_DPLL_Figure_1(
.sys_clk(sys_clk), // 系统时钟
.sys_rst(sys_rst), // 系统重置
.clk_i(clk_o), // 输出时钟反馈回周期测量模块
.t_o(n_to) // 输出时钟周期
);
defparam TE_DPLL_Figure_1.CNT_W = DATA_W; // 周期计数器位宽
defparam TE_DPLL_Figure_1.PN_EDGE = PN_EDGE; // 沿检测参数(01=上升沿,10=下降沿)
endmodule