基于actel fpga的周期反馈式纯数字锁相环
<div class='showpostmsg'> 本帖最后由 teleagle 于 2018-10-8 22:19 编辑基于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 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
</div><script> var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;" style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
if(parseInt(discuz_uid)==0){
(function($){
var postHeight = getTextHeight(400);
$(".showpostmsg").html($(".showpostmsg").html());
$(".showpostmsg").after(loginstr);
$(".showpostmsg").css({height:postHeight,overflow:"hidden"});
})(jQuery);
} </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script> //周期采样模块
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 t_o;
//---内部寄存器---
reg n_edge; // 待测时钟缓冲,用于检测时钟沿
reg n_cnt; // 周期计数器
reg n_state; // 状态
//---------------------------------------------------------
// 2D 左移位寄存器,用于检测上升沿或下降沿
//---------------------------------------------------------
always @(posedge sys_clk)
begin
if(sys_rst)
n_edge <= 0;
else
n_edge <= {n_edge,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 ti_i; // 输入时钟周期值
output clk_o; // 输出时钟
//---内部信号---
wire n_to; // 输出时钟周期值
//---内部寄存器---
reg n_terr; // 开环误差,除 M 后的误差值
reg 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 上述代码参数时的时序仿真:
这个品牌用的人不多。{:1_103:} fsyicheng 发表于 2018-10-12 22:12
这个品牌用的人不多。
<p>纯代码,与用什么器件无关.</p>
页:
[1]