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 分支,否则会生成不必要的锁存器,影响电路稳定性。
相关推荐
ARM|X86+FPGA工业主板厂家22 分钟前
基于嵌入式 GPU 的智能服务机器人
linux·运维·arm开发·fpga开发·机器人
Freak嵌入式1 小时前
Pico PIO 汇编语法深度详解:标识符、指令、伪指令全解析
汇编·stm32·单片机·嵌入式硬件·fpga开发·硬件架构
Riwuarua3 小时前
从近似0基础开始FPGA开发 -- part.7 FPGA的网络通信设计
fpga开发
pingping_TEL6 小时前
JESD204B_IP核使用教程
fpga开发
cmc10281 天前
292.三段式状态机第三段时序逻辑用state_c与_n的差异分析
fpga开发
智塑未来2 天前
FPGA开发板怎么选?先看资质、案例与研发底盘
fpga开发
minglie13 天前
黑金AX301的SDRAM
fpga开发
hahaha60163 天前
HLS高层次综合设计技巧--高层次综合设计探讨
fpga开发
minglie13 天前
黑金AX301的EEPROM 24LC04
fpga开发
Hello-FPGA3 天前
量子计算滨松c15550-22up单光子相机与PCIe1004采集卡
人工智能·计算机视觉·fpga开发