SV 时钟移位示例代码解析

一、完整模块代码

复制代码
module phase_dly (
    input               pll_clk,
    input               rst_n,
    input               tm,
    input       [3:0]   phase_sel,
    output  reg         phase_dly_clk
);

// 1. 复位同步(防止亚稳态)
wire sync_rst_n;
reset_sync #(0, 2) u_micro_src_rst_sync (
    .q      (sync_rst_n),
    .d      (rst_n),
    .clk    (pll_clk),
    .sm     (1'b0),
    .srstn  (1'b1)
);

// 2. 测试模式控制:tm=1时强制相位选择为0
wire [3:0] phase_sel_tm = tm ? 4'b0 : phase_sel;

// 3. 移位寄存器链:生成src_clk的多拍延迟版本
reg [14:0] phase_clk;
wire src_clk; // 这里是你代码中遗漏的源时钟输入,我补上了

always @(posedge pll_clk or negedge sync_rst_n) begin
    if (~sync_rst_n) begin
        phase_clk <= 15'b0;
    end else begin
        // 第21行核心代码:src_clk作为最低位,整体左移1位
        phase_clk <= {phase_clk[13:0], src_clk};
    end
end

// 4. 组合逻辑:根据phase_sel选择对应延迟拍数的信号
always @(*) begin
    case (phase_sel_tm)
        4'd0 : phase_dly_clk = phase_clk[0];
        4'd1 : phase_dly_clk = phase_clk[1];
        4'd2 : phase_dly_clk = phase_clk[2];
        4'd3 : phase_dly_clk = phase_clk[3];
        4'd4 : phase_dly_clk = phase_clk[4];
        4'd5 : phase_dly_clk = phase_clk[5];
        4'd6 : phase_dly_clk = phase_clk[6];
        4'd7 : phase_dly_clk = phase_clk[7];
        4'd8 : phase_dly_clk = phase_clk[8];
        4'd9 : phase_dly_clk = phase_clk[9];
        4'd10: phase_dly_clk = phase_clk[10];
        4'd11: phase_dly_clk = phase_clk[11];
        4'd12: phase_dly_clk = phase_clk[12];
        4'd13: phase_dly_clk = phase_clk[13];
        4'd14: phase_dly_clk = phase_clk[14];
        default: phase_dly_clk = phase_clk[0]; // 防止锁存器生成
    endcase
end

endmodule

二、逐段解析,重点讲清第 21 行和整体逻辑

1. phase_clk <= {phase_clk[13:0], src_clk};
  • 语法拆解

    • {A, B} 是 Verilog 的位拼接运算符,会把信号 A 和信号 B 按顺序拼接成一个新的宽信号。

    • phase_clk[13:0]phase_clk 的低 14 位,src_clk 是 1 位信号,拼接后得到一个 15 位的信号。

    • 这个 15 位信号再赋值给 phase_clk,等价于:

      verilog

      复制代码
      phase_clk[14] <= phase_clk[13];
      phase_clk[13] <= phase_clk[12];
      ...
      phase_clk[1]  <= phase_clk[0];
      phase_clk[0]  <= src_clk;
  • 核心作用 :构建一个15 级的移位寄存器链src_clk 信号会被每一拍(pll_clk 周期)移位一次,从而在 phase_clk 寄存器中保存 src_clk 过去 15 拍的所有状态。

    • phase_clk[0]:当前拍的 src_clk(延迟 0 拍)
    • phase_clk[1]:上一拍的 src_clk(延迟 1 拍)
    • ...
    • phase_clk[14]:14 拍前的 src_clk(延迟 14 拍)

2. 后续的 always @(*) 组合逻辑
  • 这部分的作用是根据相位选择信号 phase_sel,从移位寄存器链中挑选出对应延迟拍数的信号 ,作为最终输出 phase_dly_clk
  • 比如 phase_sel = 4'd5,就会选择 phase_clk[5],实现 src_clk 延迟 5 个 pll_clk 周期的输出。
  • tm 信号是测试模式控制:当 tm=1 时,phase_sel_tm 会被强制设为 4'b0,输出固定为 phase_clk[0],通常用于固定相位或测试功能。

3. 模块整体功能

这是一个基于高速时钟采样的数字时钟延迟 / 移相电路

  • 利用高频的 pll_clk 采样低速的 src_clk,通过移位寄存器链生成多拍延迟版本。
  • 通过配置 phase_sel,可以灵活调整输出时钟 phase_dly_clk 相对于 src_clk 的相位(延迟拍数)。
  • 延迟的最小步长就是 pll_clk 的周期,比如 pll_clk 是 100MHz(周期 10ns),那么每拍延迟就是 10ns,最大可实现 140ns 的可调延迟。

三、补充注意事项

  1. 你原始代码里的 src_clk 没有在端口声明,这是个明显的遗漏,我在完整代码里补上了,实际使用时需要把它作为模块的输入端口。
  2. 这种延迟方式的精度由 pll_clk 的频率决定,如果需要更高精度的移相,通常会配合专用的延迟线(如 FPGA 的 IODELAY)或 PLL 的相位控制功能。
  3. always @(*) 里的 case 语句一定要加 default 分支,否则会生成不必要的锁存器,影响电路稳定性。
相关推荐
dadaobusi1 小时前
100MHz/125MHz
fpga开发
unicrom_深圳市由你创科技1 小时前
如何做FPGA的模块划分?
fpga开发
liuluyang5302 小时前
SV 移位寄存器操作
sv
szxinmai主板定制专家19 小时前
RK3568 + CODESYS+实时系统运动控制器PLC,支持 AI 视觉目标检测,预测性维护,混合多系统部署,多路模拟量采集
arm开发·人工智能·嵌入式硬件·fpga开发
GateWorld1 天前
LCD显示技术完全指南:原理·制造·驱动·FPGA实现之驱动二
fpga开发·lcd显示·fpga点亮屏幕·minilvds
GateWorld1 天前
LCD显示技术完全指南:原理·制造·驱动·FPGA实现之驱动一
fpga开发·lcd显示·minilvds·fpga点屏
XMAIPC_Robot1 天前
深度无人机自动驾驶仪,中小型无人机硬件在环仿真飞行
运维·arm开发·人工智能·fpga开发·无人机·边缘计算
小眼睛FPGA2 天前
【紫光HiYou开源入门轻量级PCIE开发板PG2L25G】实验例程1-基于紫光FPGA 的LED 流水灯
fpga开发
不会武功的火柴2 天前
SystemVerilog语法(8)-有限状态机(FSM)
嵌入式硬件·fpga开发·自动化·ic验证·rtl·uvm方法学