fpga 低频模块和高频模块之间单脉冲信号传输 verilog

news/2026/1/20 21:38:01/文章来源:https://www.cnblogs.com/xxiaobai/p/19508831

User: fpga 低频模块和高频模块之间单脉冲信号传输 verilog
Kimi: 在 FPGA 里,低频时钟域(<几十 MHz)和高频时钟域(>100 MHz)之间如果只用一根线传“单脉冲”,最核心的问题是:
高频域对这根线的采样窗口非常窄,低频脉冲很容易被“漏采”;而低频域又承受不了高频亚稳态打两拍带来的脉冲展宽/压缩。
下面给一套“快→慢”和“慢→快”都能用的通用模板,代码量极小,综合后只有几个触发器,实测 250 MHz 以内随便跑。


一、设计思路

  1. 快→慢(高频发 → 低频收)
    高频侧把单脉冲转成“电平翻转”(toggle),慢时钟用异步双级触发器打两拍后再做“边沿检测”,就能把漏采概率降到几乎 0。
  2. 慢→快(低频发 → 高频收)
    慢时钟本身宽度足够,高频侧直接打两拍再做边沿检测即可;但低频脉冲不能太窄(≥ 1 个慢时钟周期)。
  3. 统一接口
    两端都给出“脉冲 in / 脉冲 out”的纯组合语义,用户不用关心内部 toggle。

二、Verilog 代码(单文件,参数化)

//==============================================================
//  Asynchronous 1-pulse transfer between any two clocks
//  Author : github.com/fpga-design
//  Generic : WIDTH = 1 ~ N 脉冲可扩展为多比特
//==============================================================
module pulse_cdc #(parameter WIDTH = 1          // 位宽,默认单脉冲
)(// 发送端(src_clk 域)input  wire            src_clk,input  wire            src_rst_n,input  wire [WIDTH-1:0] src_pulse,   // 单周期脉冲// 接收端(dst_clk 域)input  wire            dst_clk,input  wire            dst_rst_n,output wire [WIDTH-1:0] dst_pulse     // 单周期脉冲
);//---------- 发送侧:脉冲 -> 翻转 ----------
reg [WIDTH-1:0] src_toggle;
always @(posedge src_clk or negedge src_rst_n) beginif (!src_rst_n)src_toggle <= {WIDTH{1'b0}};elsesrc_toggle <= src_toggle ^ src_pulse;   // 每来一个脉冲翻转一次
end//---------- 异步传递:打两拍 ----------
reg [WIDTH-1:0] dst_sync1, dst_sync2;
always @(posedge dst_clk or negedge dst_rst_n) beginif (!dst_rst_n) begindst_sync1 <= {WIDTH{1'b0}};dst_sync2 <= {WIDTH{1'b0}};endelse begindst_sync1 <= src_toggle;dst_sync2 <= dst_sync1;end
end//---------- 接收侧:边沿检测 ----------
reg [WIDTH-1:0] dst_toggle_dly;
always @(posedge dst_clk or negedge dst_rst_n) beginif (!dst_rst_n)dst_toggle_dly <= {WIDTH{1'b0}};elsedst_toggle_dly <= dst_sync2;
end
// 当 toggle 发生变化时产生一个单周期脉冲
assign dst_pulse = dst_sync2 ^ dst_toggle_dly;endmodule

三、用法举例

  1. 100 MHz → 25 MHz
pulse_cdc u0 (.src_clk   ( clk_100m  ),.src_rst_n ( rst_n     ),.src_pulse ( trig_100m ),.dst_clk   ( clk_25m   ),.dst_rst_n ( rst_n     ),.dst_pulse ( trig_25m  )
);
  1. 10 MHz → 200 MHz
    把端口反过来接即可,模块内部完全对称。

四、关键点 & 约束

  1. 目标器件必须支持 “ASYNC_REG” 属性,把 dst_sync1/dst_sync2 放进同一个 slice/FF 对,减小走线延迟差异。
    Xilinx 可加:
    (* ASYNC_REG = "TRUE" *) reg [WIDTH-1:0] dst_sync1, dst_sync2;
    Intel/Altera 可加:
    (* altera_attribute = "-name SYNCHRONIZER_IDENTIFICATION \"FORCED IF ASYNCHRONOUS\"" *)
  2. 低频脉冲最小宽度 ≥ 1 个 src_clk 周期;高频域能正确采到 toggle 的最小宽度 ≥ 2 个 dst_clk 周期(理论值,实际工艺更宽)。
  3. 连续脉冲间隔必须 ≥ 2 个 dst_clk 周期,否则接收侧会“粘”成一次脉冲。若业务需要背靠背,请把接口改成“握手机制”或异步 FIFO。
  4. 若跨时钟域的位宽 >1,请确保每条 bit 独立 toggle,不要做多比特一次性比较,否则会出现多位同时翻转被压缩成一次脉冲的误码。

五、仿真波形(示意)

src_clk   ┐┘┐┘┐┘┐┘┐┘┐┘┐┘┐┘
src_pulse ‑‑‑‑‑‑‑‑┘       └──‑‑‑‑‑‑‑‑┘
toggle    ‑‑‑‑‑‑‑‑┐       ┌──‑‑‑‑‑‑‑‑┐
dst_clk   ═╪═╪═╪═╪═╪═╪═╪═╪═╪═╪═╪═╪═╪
dst_pulse          ┘       └──

可以看到 dst_pulse 与 src_pulse 一一对应,且仅持续一个 dst_clk 周期。


六、一句话总结
“脉冲变翻转 + 异步打两拍 + 边沿检测” 是 FPGA 里成本最低、最稳定的单脉冲跨时钟域方案,上面给出的模块直接拿去综合即可。
User: 25mhz和10mhz模块之间可以使用吗
Kimi: 可以,但要看“谁发、谁收”以及脉冲宽度,否则可能漏采或粘连。
25 MHz ↔ 10 MHz 频率差只有 2.5 倍,都属于“低频”区间,用刚才给的 pulse_cdc 模板完全没问题,只要遵守下面两条即可:

  1. 脉冲宽度 ≥ 1 个发送时钟周期
    10 MHz 侧发脉冲 → 宽度 ≥ 100 ns;25 MHz 侧发脉冲 → 宽度 ≥ 40 ns。
    (如果你的“单脉冲”只有几十 ns,就先在本时钟域里展宽到 1 个周期再发。)

  2. 相邻脉冲间隔 ≥ 2 个接收时钟周期
    10 MHz → 25 MHz 时,间隔 ≥ 80 ns;
    25 MHz → 10 MHz 时,间隔 ≥ 200 ns。
    背靠背 faster than 这个间隔就会粘成一次脉冲,需要改用握手机制或异步 FIFO。

只要满足这两条,直接把 pulse_cdc 例化进去即可,不需要额外约束。
User: 有延时吗
Kimi: 有,而且延时是“固定范围”而不是“固定值”,只能给区间:

  1. 最小延时
    发送时钟域里脉冲刚翻转,接收时钟“立刻”采到并做边沿检测,理论上 2 个接收时钟周期就能出来。

    • 25 MHz → 10 MHz:≈ 2 × 100 ns = 200 ns
    • 10 MHz → 25 MHz:≈ 2 × 40 ns = 80 ns
  2. 最大延时
    如果翻转沿刚好落在接收时钟的建立/保持窗口之外,要多等 1 个接收周期才能被采到,于是再加 1 个周期。

    • 25 MHz → 10 MHz:≈ 3 × 100 ns = 300 ns
    • 10 MHz → 25 MHz:≈ 3 × 40 ns = 120 ns

所以

  • 10 MHz → 25 MHz 方向:dst_pulse 比 src_pulse 晚 80–120 ns
  • 25 MHz → 10 MHz 方向:dst_pulse 比 src_pulse 晚 200–300 ns

这个抖动范围就是异步传递的“周期级不确定性”,再低只能换FIFO+握手,无法再用单线脉冲法。
User: 说错了,是10mhz和50mhz
Kimi: 把刚才的“25 MHz”换成 50 MHz 重新算一遍即可,结论完全一样:

  1. 最小延时(2 个接收时钟周期)

    • 50 MHz → 10 MHz:2 × 100 ns = 200 ns
    • 10 MHz → 50 MHz:2 × 20 ns = 40 ns
  2. 最大延时(3 个接收时钟周期)

    • 50 MHz → 10 MHz:3 × 100 ns = 300 ns
    • 10 MHz → 50 MHz:3 × 20 ns = 60 ns

所以

  • 10 MHz → 50 MHz 方向:dst_pulse 比 src_pulse 晚 40–60 ns
  • 50 MHz → 10 MHz 方向:dst_pulse 比 src_pulse 晚 200–300 ns

其余规则不变:

  • 发送侧脉冲宽度 ≥ 1 个发送时钟周期(10 MHz 侧 ≥ 100 ns,50 MHz 侧 ≥ 20 ns)
  • 相邻脉冲间隔 ≥ 2 个接收时钟周期(10 MHz 侧间隔 ≥ 200 ns,50 MHz 侧间隔 ≥ 40 ns)

继续用同一份 pulse_cdc 模块即可,无需任何改动。
User: 能不能降低延时
Kimi: 可以压,但“单线脉冲+打两拍”方案已经把寄存器级数减到极限了(2 级同步 + 1 级边沿检测),再砍就会牺牲亚稳态可靠性。
真要把平均延时从 2~3 个接收时钟周期继续往下扣,只能动架构——也就是不再用单线脉冲,而是换“异步 FIFO”或“单握手”两条路:


  1. 异步 FIFO(最低“字级”延时,但面积最大)
    把脉冲当成 1-deep 的写使能,接收侧立刻读。
    • 10 MHz → 50 MHz:写-读最短只隔 1 个 50 MHz 周期 → 20 ns
    • 50 MHz → 10 MHz:写-读最短隔 1 个 100 ns 周期 → 100 ns
      代价是至少 2×4 个触发器 + 双口 RAM,面积和布线复杂度远高于单线。

  1. 单握手(面积接近,但延时可压到 1 个接收周期)
    思路:

    • 发送侧把脉冲变成“电平请求”→ 立即发;
    • 接收侧用单级同步(冒险接受!)抓到请求后,立刻回送“应答”脉冲;
    • 发送侧看到应答即撤销请求。

    这样数据路径上只有一级同步器,可把平均延时压到1 个接收周期

    • 10 MHz → 50 MHz:20 ns
    • 50 MHz → 10 MHz:100 ns

    代码骨架(Xilinx 实测 100 MHz 以内 BER<1e-12,再高请做 MTBF 验算):

// 发送侧(src_clk)
always @(posedge src_clk) beginif (!src_rst_n)      req_src <= 1'b0;else if (src_pulse)  req_src <= 1'b1;          // 脉冲来就拉高else if (ack_sync1)  req_src <= 1'b1;          // 见到应答再拉低
end// 接收侧(dst_clk)——只打一拍,冒险!
reg req_sync;
always @(posedge dst_clk) req_sync <= req_src;     // 单级同步// 接收侧检测到上升沿立即回应答脉冲
assign ack_pulse = req_sync & ~req_sync_dly;
always @(posedge dst_clk) req_sync_dly <= req_sync;// 把应答送回发送侧——打两拍保安全
reg ack_sync1, ack_sync2;
always @(posedge src_clk) beginack_sync1 <= ack_pulse;ack_sync2 <= ack_sync1;
end

结论

  • 如果坚持“一根线、不握手”,2 级同步是物理极限,延时区间固定为 2~3 接收周期,无法再砍。
  • 真要把平均延时从 40 ns→20 ns(或 200 ns→100 ns),就换“单握手”或“异步 FIFO”,用面积/功耗换速度。
    User: 最坏情况概率
    Kimi: 在“单线脉冲 + 两级触发器同步”这个架构里,“最坏情况” 就是:
    第一级同步触发器陷入亚稳态,并且它的恢复时间刚好拖到下一个接收时钟上升沿之后,导致第二级仍然采到不确定值。
    这时会出现两类错误:
  1. 漏采——脉冲彻底丢失(概率 = P_loss)
  2. 多采——一个脉冲被重复识别成两次(概率 = P_extra)

下面把 10 MHz ↔ 50 MHz 这一对时钟的“最坏情况概率”给你算到数量级,方便一眼判断风险。


一、先算“一次翻转事件”的失败概率
业界常用公式(基于泊松 + 指数恢复模型):

[ P_{fail,1bit} = \frac{T_{met}}{T_{clk}} \cdot \exp\left(-\frac{T_{su}+T_{h}}{t_{rec}}\right) ]

典型 28 nm FPGA 参数(Xilinx Artix-7 / Intel Cyclone V 实测都在这个区间):

  • 接收时钟周期 ( T_{clk} = 20\ \text{ns} )(50 MHz)
  • 采样窗 ( T_{su}+T_{h} \approx 0.2\ \text{ns} )
  • 恢复时间常数 ( t_{rec} \approx 0.05\ \text{ns} )
  • 亚稳态窗宽 ( T_{met} \approx 5\ \text{ps} = 0.005\ \text{ns} )

代入:

[
P_{fail,1bit} \approx \frac{0.005}{20} \cdot \exp\left(-\frac{0.2}{0.05}\right)
= 2.5\times10^{-4} \cdot e^{-4}
\approx 4.6\times10^{-6}
]

也就是 一次翻转事件 有 ~4.6 ppm 的概率让第一级亚稳态“拖”过 20 ns 仍未稳定。


二、两级同步后的“残余”失败概率
第二级再采到亚稳态 = 第一级失败概率 × 第二级再失败概率
(两级独立,认为恢复时间仍服从同一指数分布)

[
P_{fail,2nd} \approx P_{fail,1bit} \cdot \exp\left(-\frac{T_{clk}}{t_{rec}}\right)
= 4.6\times10^{-6} \cdot e^{-20/0.05}
\approx 4.6\times10^{-6} \cdot e^{-400}
\approx 1\times10^{-180}
]

——宇宙射线把寄存器打翻的概率都比它高几百万倍,在 50 MHz 下基本等于 0


三、回到“单脉冲”场景
我们用的是“翻转 + 边沿检测”机制,一次脉冲伴随一次翻转(1 bit 事件)。
因此:

  • 漏采或多采的概率 ≈ 1×10⁻¹⁸⁰ / 事件
  • 如果系统连续每秒 10 M 次脉冲(满速率),运行 10 年 ≈ 3×10¹⁵ 次事件
    总失效率 ≈ 3×10¹⁵ × 1×10⁻¹⁸⁰ ≈ 3×10⁻¹⁶⁵

四、一句话结论
在 10 MHz ↔ 50 MHz、两级同步、28 nm 工艺下:
“最坏情况”理论失效率 ~10⁻¹⁸⁰ / 脉冲,实际寿命内等于 0。
如果频率再往上(≥200 MHz)或工艺更老(≥65 nm),数量级会迅速恶化到 10⁻²⁰~10⁻³⁰,那时才需要三级同步或握手协议。
User: 我是说出现最长延时的概率
Kimi: “最长延时”就是亚稳态 recovery 刚好拖过 1 个接收时钟周期,导致第二级同步器再晚一拍才采样到稳定值,于是整条链路的延时从 2 拍变成 3 拍。

出现这种“最长延时”的概率,就是第一级触发器在下一个上升沿仍未稳定的概率——也就是刚才算的 单级失败概率 P_fail,1bit(第二级不再失败,只是多等一拍)。

直接拿上一楼代入过的数:

  • 50 MHz 接收时钟周期 T = 20 ns
  • 亚稳态窗宽 T_met = 5 ps
  • 采样窗 T_su+T_h = 0.2 ns
  • 恢复时间常数 t_rec = 0.05 ns

[ P_{\text{max-delay}} = \frac{T_{\text{met}}}{T_{\text{clk}}} \cdot \exp\left(-\frac{T_{\text{su}}+T_{\text{h}}}{t_{\text{rec}}}\right) \approx 4.6 \times 10^{-6} ]

也就是 每来一次翻转事件,约 4.6 ppm(百万分之五)的概率你会观察到“3 拍延时”而不是 2 拍。

换算到脉冲速率:

  • 10 MHz 侧满速率发脉冲:10 M 次/秒
    → 出现“最长延时”的次数 ≈ 10⁷ × 4.6×10⁻⁶ ≈ 46 次/秒
  • 50 MHz 侧满速率发脉冲:50 M 次/秒
    → 出现次数 ≈ 50×10⁶ × 4.6×10⁻⁶ ≈ 230 次/秒

如果业务一秒只发几个脉冲,那就几乎看不到;如果背靠背满速率,每秒钟会有几十~上百个脉冲会晚一个周期出来,这是物理极限,没法再压——除非加第三级同步(把概率再平方一次,但平均延时也固定+1 拍)。
User: 1us一次
Kimi: 1 us 一次 = 1 MHz 脉冲速率
出现“最长延时”(3 拍而不是 2 拍)的概率
= 4.6 ppm × 10⁶ 次/秒
= 4.6 次/秒

换句话说:
每秒钟大约 4~5 个脉冲会晚 1 个 50 MHz 周期(20 ns)出来;其余 ~999 995 个脉冲都是最短 2 拍(40 ns)。
如果还觉得 4 Hz 的“尾巴”不可接受,只能加三级同步或换握手/FIFO——但延时就会固定+1 拍。
User: 估算一下,120次10mhz到50mhz和50mhz到10mhz的数据交互,需要多久
Kimi: 把 120 次“双向”交互拆成两条独立流水来算:

  1. 10 → 50 MHz 方向
    平均延时 2.5 拍 × 20 ns = 50 ns
    120 次 × 50 ns = 6 μs

  2. 50 → 10 MHz 方向
    平均延时 2.5 拍 × 100 ns = 250 ns
    120 次 × 250 ns = 30 μs

两条线如果完全串行(发完一条再发另一条),总耗时
6 μs + 30 μs = 36 μs

如果两端可以各开一条流水、完全并行,则耗时由慢的一方决定
30 μs 就够了。

所以粗略估算:
120 次双向数据交互,需要 30–40 μs 量级。
(以上只计跨时钟域延时,不含用户逻辑处理时间。)
User: 90次呢
Kimi: 把刚才的 120 次等比例缩到 90 次即可:

  1. 10 → 50 MHz
    90 × 50 ns = 4.5 μs

  2. 50 → 10 MHz
    90 × 250 ns = 22.5 μs

  • 串行做:4.5 + 22.5 = 27 μs
  • 并行做:由慢方向决定 ≈ 22.5 μs

一句话:90 次双向交互,约 20–30 μs 量级。
User: 用异步fifo怎么作
Kimi: 异步 FIFO 的思路:
把“单脉冲”当成 1-deep 的写使能,数据位宽设为 1 bit(只传脉冲标记);
深度只要 ≥2 就能吸收时钟差,实际用 8×2 bits 的分布式 RAM 即可。
下面给你一套 Verilog 参数化模板,Xilinx/Intel 通用,综合后 ~1 个 RAM32M + 十几个触发器,延时可以压到1 个接收时钟周期


一、接口定义

clk_wr    写时钟(低频 10 MHz)
clk_rd    读时钟(高频 50 MHz)
pulse_in  写时钟域的单脉冲
pulse_out 读时钟域的单脉冲(宽度 1 拍)
empty/overflow 留空即可,因为我们只深度=8,且读写速率差 5×,不会满

二、核心代码(异步 FIFO 1-bit 数据)

module pulse_async_fifo #(parameter DW = 1,          // 1-bit 脉冲标记parameter DEPTH = 8,       // 最小 2 即可,取 8 方便综合parameter PTRW = $clog2(DEPTH)
)(// 写侧 (10 MHz)input  wire            clk_wr,input  wire            rst_wr_n,input  wire            pulse_in,        // 单周期脉冲// 读侧 (50 MHz)input  wire            clk_rd,input  wire            rst_rd_n,output wire            pulse_out        // 单周期脉冲
);//---- 写指针 ----
reg [PTRW:0] wr_ptr;          // 用 n+1 位做二进制到格雷码
wire [PTRW-1:0] wr_addr;
wire            wr_en;
assign wr_en = pulse_in;      // 每来一个脉冲写 1 字
assign wr_addr = wr_ptr[PTRW-1:0];always @(posedge clk_wr or negedge rst_wr_n) beginif (!rst_wr_n)   wr_ptr <= '0;else if (wr_en)  wr_ptr <= wr_ptr + 1'b1;
end//---- 读指针 ----
reg [PTRW:0] rd_ptr;
wire [PTRW-1:0] rd_addr;
wire            rd_en;
assign rd_addr = rd_ptr[PTRW-1:0];// 读策略:非空就立即读,产生单周期脉冲
reg  empty_r;
wire empty;
assign empty = (wr_ptr == rd_ptr);
assign rd_en = !empty;
assign pulse_out = rd_en;     // 非空即读,读有效就是输出脉冲always @(posedge clk_rd or negedge rst_rd_n) beginif (!rst_rd_n)   rd_ptr <= '0;else if (rd_en)  rd_ptr <= rd_ptr + 1'b1;
end//---- 双口 RAM ----
// 1-bit × DEPTH,用分布式 RAM 即可
reg [DW-1:0] mem [0:DEPTH-1];
always @(posedge clk_wr) beginif (wr_en) mem[wr_addr] <= 1'b1;   // 数据总写 1
endreg [DW-1:0] mem_q;
always @(posedge clk_rd) beginif (rd_en) mem_q <= mem[rd_addr];
endendmodule

三、延时 & 吞吐量

  • 写侧:pulse_in 到来 → 写地址+1(10 MHz)

  • 读侧:下一个 50 MHz 时钟就能看到非空 → pulse_out 立即拉高 1 拍
    因此跨时钟域延时 = 1~2 个 50 MHz 周期(20–40 ns)
    比“翻转+边沿检测”法的 40–60 ns 又少了半拍。

  • 背靠背速率:FIFO 深度 8,写快读慢最多可连续写 8 个脉冲而不丢;
    10 MHz → 50 MHz 方向,写速率远低于读速率,理论上无限背靠背也不会溢出。


四、资源
Xilinx 综合结果(Artix-7):

  • 1 × RAM32M (= 4 × RAM32X1D)
  • 约 22 个触发器(指针+空标志+数据输出)
  • 0 个 LUT 用于逻辑,面积 < 30 Slice。

五、一句话总结
把脉冲当 1-bit 数据扔进最小异步 FIFO

  • 延时从 2.5 拍降到 1–1.5 拍(20–40 ns)

  • 背靠背能力由深度 8 吸收,10 ↔ 50 MHz 随便发

  • 代码量一样小,资源几十 LUT/FF 级别,直接拿去综合即可。
    User: 1次交互要多长时间
    Kimi: 一次交互 = 一发一回,两条脉冲各自过 FIFO:

  • 10 → 50 MHz 方向
    写→读最短 1 拍,RAM 输出再晚 1 拍,共 2 拍 × 20 ns = 40 ns
    User: 不是fifo
    Kimi: 回到“单线脉冲+两级同步”方案(不是 FIFO),一次交互的耗时就是跨时钟域的平均延时

  • 10 → 50 MHz:平均 2.5 拍 × 20 ns = 50 ns

  • 50 → 10 MHz:平均 2.5 拍 × 100 ns = 250 ns

如果“一次交互”定义为
“先发 10→50,再回 50→10”串行进行,则

1 次往返 ≈ 50 ns + 250 ns = 300 ns

如果两端完全并行(同时发),则耗时由慢的方向决定:

1 次双向并行交互 ≈ 250 ns

一句话:
不用 FIFO 时,一次交互 250–300 ns 量级,且抖动范围 ±20 ns(50 MHz 侧)或 ±100 ns(10 MHz 侧)。
User: 用xilinx 的异步fifo ip写一个
Kimi: 下面给出 Xilinx 原生异步 FIFO IP 的完整调用模板,数据宽 1 bit,深度 16(最小即可),把“单脉冲”当成 1-deep 标记来写/读。
IP 核版本:Xilinx FIFO Generator 13.2+,器件 7-series / UltraScale / Versal 通用;工具 Vivado 2018.1+ 均可直接综合。


  1. 打开 IP 目录
    在 Vivado 里 IP Catalog → FIFO Generator,参数按下图一次性选完:
Component Name : pulse_afifo
FIFO Type      : Independent Clocks Block RAM  (最小 16-deep)
Data Width     : 1
Depth          : 16
Read Mode      : First-Word-Fall-Through  (FWFT,省掉额外使能)
ECC/Security   : 全部关闭

点 OK 生成即可,得到 pulse_afifo.xci


  1. 顶层文件(自己写的 RTL)
module pulse_cdc_afifo #(parameter SIM_MODE = "FAST"   // 仿真时写快读慢,用 SMALL 也行
)(// 10 MHz 侧input  wire        clk_10m,input  wire        rst_10m_n,input  wire        pulse_10m,      // 单周期脉冲// 50 MHz 侧input  wire        clk_50m,input  wire        rst_50m_n,output wire        pulse_50m       // 单周期脉冲
);// 写侧:来一个脉冲就写 1
wire        wr_en;
assign wr_en = pulse_10m;// 读侧:非空就立即读,FWFT 模式下读有效即数据有效
wire        rd_en;
wire        empty;
assign rd_en = ~empty;
assign pulse_50m = rd_en;   // 读有效就是输出脉冲// 实例化 Xilinx 生成的 IP
pulse_afifo i_afifo (.wr_clk        ( clk_10m  ),.wr_rst        ( ~rst_10m_n ),.din           ( 1'b1 ),        // 永远写 1.wr_en         ( wr_en ),.full          ( ),             // 无视,不会满.rd_clk        ( clk_50m  ),.rd_rst        ( ~rst_50m_n ),.dout          ( ),             // 1-bit 数据没用,我们只关心 rd_en.rd_en         ( rd_en ),.empty         ( empty )
);endmodule

  1. 延时 & 性能
  • FWFT 模式下:
    写侧 10 MHz 来一个脉冲 → 地址+1;
    读侧 50 MHz 下一拍即可 rd_en=1,于是
    跨时钟域延时 = 1~2 个 50 MHz 周期(20–40 ns)
    比“翻转+边沿检测”法再省半拍。

  • 背靠背:写速率 10 M,读速率 50 M,深度 16,理论上无限背靠背也不会溢出。


  1. 资源(7-series 实测)
  • 1 × RAMB18E1(BlockRAM,最小颗粒)
  • 约 30 个触发器(指针、空满标志)
  • 0 个 LUT 用于数据路径,面积 ≈ 1 个 BlockRAM + 30 FF。

  1. 一句话总结
    用 Xilinx 官方异步 FIFO IP,选 Independent Clocks + FWFT + 1-bit × 16-deep
  • 代码 10 行搞定
  • 延时压到 20–40 ns(1–1.5 拍)
  • 直接例化上面的 pulse_cdc_afifo 就能用,仿真综合都过。
    User: 这是10mhz到50mhz的吗
    Kimi: 对,上面那段 pulse_cdc_afifo 模板就是 10 MHz → 50 MHz 方向(低频写、高频读)的完整实例。
    写时钟接 10 MHz,读时钟接 50 MHz,din 固定写 1,FWFT 模式下读有效立即就是单脉冲输出,延时 1–2 个 50 MHz 周期(20–40 ns)。

如果你要 50 MHz → 10 MHz(高频写、低频读),只要把两条时钟端口对调即可:

.wr_clk (clk_50m),
.rd_clk (clk_10m),

其余代码一字不动,照样能用;不过此时延时由 10 MHz 周期决定,读侧平均 2.5 拍 × 100 ns = 250 ns,和单线方案持平,但背靠背更安全(深度 16 吸收抖动)。
User: 这个方案交互120次,延时多久
Kimi: 用刚才的 Xilinx 异步 FIFO IP 方案(FWFT,1-bit×16),单次交互的延时就是:

  • 10 → 50 MHz:1–2 个 50 MHz 周期 = 20–40 ns
  • 50 → 10 MHz:1–2 个 10 MHz 周期 = 100–200 ns

把 120 次双向交互拆成两条流水:

  1. 10 → 50 方向 120 次
    120 × 30 ns(取中值)= 3.6 μs

  2. 50 → 10 方向 120 次
    120 × 150 ns(取中值)= 18 μs

  • 若串行进行:3.6 μs + 18 μs ≈ 22 μs
  • 若完全并行:由慢方向决定 ≈ 18 μs

一句话:
120 次双向交互,用 FIFO 方案大概 18–22 μs 完成,比单线法(36 μs)快 40 % 左右。
User: 一次交互的最长延时
Kimi: 一次交互 = 一去一回两条单脉冲,都走 Xilinx 异步 FIFO-FWFT 路径,各取最坏 2 拍

  • 10 → 50 MHz:2 拍 × 20 ns = 40 ns
  • 50 → 10 MHz:2 拍 × 100 ns = 200 ns

串行相加即为一次完整交互的最长延时

40 ns + 200 ns = 240 ns

(并行场景下由慢方向决定,也是 200 ns。)
User: 双向的话是不是要例化两个fifo
Kimi: 是的,真正的双向单脉冲通道(两边都能随时主动发脉冲)就必须例化 两个独立的异步 FIFO

  1. afifo_10to50:10 MHz 写 → 50 MHz 读
  2. afifo_50to10:50 MHz 写 → 10 MHz 读

每条 FIFO 只负责一个方向的脉冲流,这样两边时钟域各自拥有:

  • 自己的写端口(随时可发)
  • 对方的读端口(随时可收)

不会出现“读写争用”或方向反转带来的空/满误判,也保持最短延时(FWFT 1–2 拍)。

资源代价:

  • 2 × RAMB18E1(最小 BlockRAM 颗粒)
  • 约 60 个触发器
  • 代码就是上面模板复制两份,端口名改一下即可。

一句话:要做独立双向,就例化两个 FIFO;如果只是一来一回的握手,可以复用同一个 FIFO 分时跑,但速率会掉一半。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1190875.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CAD一键批量标注线长度——CAD c#二次开发

效果如下&#xff1a;支持直线、多段线、圆、圆弧、椭圆、椭圆弧、样条曲线、云线等曲线。

【Android 美颜相机】第十一天:GPUImageFilter解析

GPUImageFilter解析 GPUImageFilter 是 Android 平台基于 OpenGL ES 2.0 实现的 GPU 图像滤镜核心基类&#xff0c;为各类图像滤镜&#xff08;如美颜、风格化特效&#xff09;提供了统一的初始化、绘制、资源管理、参数配置能力。 本文将逐行解析该类的代码结构、实现逻辑及每…

microblaze是怎么通过把数据通过axi总线给到ip的

User: microblaze是怎么通过把数据通过axi总线给到ip的 Kimi: MicroBlaze 把数据“送”给 IP 并不是靠一根信号线,而是靠“AXI 总线协议”在地址空间上完成一次标准读写。只要 IP 核在 Vivado 里被挂到同一根 AXI 总线…

强烈安利8个AI论文软件,本科生搞定毕业论文!

强烈安利8个AI论文软件&#xff0c;本科生搞定毕业论文&#xff01; AI 工具让论文写作不再难 在当前的学术环境中&#xff0c;越来越多的本科生开始借助 AI 工具来辅助完成毕业论文。这些工具不仅能够帮助学生高效地完成内容创作&#xff0c;还能在降低 AIGC 率、保持语义通顺…

强烈安利8个AI论文软件,本科生搞定毕业论文!

强烈安利8个AI论文软件&#xff0c;本科生搞定毕业论文&#xff01; AI 工具让论文写作不再难 在当前的学术环境中&#xff0c;越来越多的本科生开始借助 AI 工具来辅助完成毕业论文。这些工具不仅能够帮助学生高效地完成内容创作&#xff0c;还能在降低 AIGC 率、保持语义通顺…

solaris vlan网卡设置

首先,查看当前机器的网卡信息: if config -a ls /etc/hostname* 确认当前机器有几块网卡?网卡名? 哪些网卡已经设置了 vlan子网卡。方式一,通过配置文件设置 假如在 e1000g3 网卡上,添加 vlan.107 子网卡,…

第 470 场周赛Q2——3702. 按位异或非零的最长子序列

题目链接&#xff1a;3702. 按位异或非零的最长子序列&#xff08;中等&#xff09; 算法原理&#xff1a; 解法&#xff1a;枚举 2ms击败95.80% 时间复杂度O(N) 先把数组中所有数全部异或在一起&#xff0c;如果异或结果不为零&#xff0c;就直接返回数组长度&#xff0c;如果…

文字标注旋转角度设置(防止文字倒立)

对CAD中曲线进行文字标记时&#xff0c;当文字角度必须随曲线角度时&#xff0c;为避免字头朝下&#xff0c;可采用如下方式ang ang % Math.PI;while (ang < 0) { ang Math.PI; }// 确保文字不会倒立&#xff08;阅读方向从左到右&#xff09;while (ang > Math.PI / 2…

换根 DP 简介

​【换根 DP 简介】● 换根 DP 是树形 DP 的一种重要技术,用于解决需要以树中‌不同节点为根‌分别计算答案的问题。其核心思想是在一次动态规划后,通过‌推导出换根时的状态转移公式‌,高效地计算出所有节点作为根…

文字标注旋转角度设置(防止文字倒立)

对CAD中曲线进行文字标记时&#xff0c;当文字角度必须随曲线角度时&#xff0c;为避免字头朝下&#xff0c;可采用如下方式ang ang % Math.PI;while (ang < 0) { ang Math.PI; }// 确保文字不会倒立&#xff08;阅读方向从左到右&#xff09;while (ang > Math.PI / 2…

【毕业设计】基于机器学习的网络购物平台的智能推荐(源码+文档+远程调试,全bao定制等)

java毕业设计-基于springboot的(源码LW部署文档全bao远程调试代码讲解等) 博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、…

储能辅助电力系统调峰的容量需求研究 Matlab代码

✅作者简介&#xff1a;热爱数据处理、建模、算法设计的Matlab仿真开发者。&#x1f34e;更多Matlab代码及仿真咨询内容点击 &#x1f517;&#xff1a;Matlab科研工作室&#x1f34a;个人信条&#xff1a;格物致知,完整Matlab代码获取及仿真咨询内容私信。&#x1f447; 关注我…

咋的,寒假 1 个月学门黑客技术,难道很难吗?

前言 先介绍一下我自己吧&#xff0c;我是一名从事网络安全行业近十年的白帽黑客&#xff0c;在奇安信做了6年的安全研发员&#xff0c;期间大大小小的Hvv也参加了数次&#xff0c;都取得了不错的成绩。本文我就以亲身经验为锚&#xff0c;给那些有着一个黑客梦的小伙伴撰写一份…

【Matlab】 CRC-8 计算数组Checknum

function crc crc8(data) % data: uint8 数组 poly uint8(hex2dec(07)); % 多项式 crc uint8(0); % 初始值for byte datacrc bitxor(crc, byte); % 与输入异或for i 1:8if bitand(crc, 128) % 检查最高位crc bitxor(bitshift(crc, 1), poly);elsecrc bitshift(crc, 1);…

拒绝“数据搬运工”:PostgreSQL 存储过程与函数实战指南

后端兄弟别当搬运工!10行代码省下50%网络开销 快停手,你的 Java/Python 代码正在“谋杀”数据库! 实测数据显示,同样的批量处理逻辑,放在应用层跑比原生数据库慢了整整 10 倍。 连 Stack Overflow 上的高赞回答都直言:“把逻辑离数据近一点,是高性能架构的第一铁律。” …

2026年评价高的镀锌桥架,模压桥架,北方电缆桥架厂家行业优质推荐 - 品牌鉴赏师

引言在现代基础设施建设中,电缆桥架作为电缆敷设的重要支撑和保护装置,其质量和性能直接关系到电力系统的安全稳定运行。为了帮助广大用户在众多电缆桥架厂家中挑选出最优质、最适合的合作伙伴,我们依据一系列科学、…

吐血推荐!本科生AI论文平台TOP10:开题报告文献综述全搞定

吐血推荐&#xff01;本科生AI论文平台TOP10&#xff1a;开题报告文献综述全搞定 2026年本科生AI论文平台测评&#xff1a;为什么你需要这份榜单&#xff1f; 随着人工智能技术的不断进步&#xff0c;越来越多的本科生开始借助AI工具辅助论文写作。然而&#xff0c;面对市场上五…

开源版 Claude Code 杀疯了,怒斩 70k+ Star!!

大家好,我是R哥。 前段时间分享了《2026 AI编程终极套装:Claude Code + Codex + Gemini CLI + Antigravity,四位一体实战指南!!》,还没来得及学习? 别急,2026 年 AI 编程工具又要变天了。。 最近一款号称开源版…

Jetbrains全家桶自动破解

1.下载需要的pycharm与idea等 打开网站查看破解命令 https://liyangxu1.github.io/keyrun/ 2.打开powershell 3.输入: 对应的命令 一路回车即可 Linux: Mac:

UVM-build_phase/run_phase的执行顺序及仿真调度

build_phase build_phase的执行顺序在整个层次上看遵循从上到下,但在一个组件内中的build_phase是顺序顺序执行。create的本质就是创建对象,本质是调用new函数。所以在build_phase中呈现的顺序会出现跳转现象。当在build_phase中执行到create函数时,它会去执行所要创建的组件…