快慢同步时钟之间,
怎样让一次传输只发生一次
慢侧保持一拍的 valid,快侧可能看到好几个采样边沿。subedge 适配要解决的,是让两侧对“这一项数据已经传过了”达成一致。
这类电路依赖确定的快慢时钟关系:慢→快用一位状态记住“已接收”,快→慢用窗口限制“何时消费”。本地流水级负责存数据和保持反压;适配逻辑本身不必包含数据缓冲。
01先把时钟关系说清楚
设快时钟为 clk_fast,慢时钟为 clk_slow。在一个固定分频模型中,慢周期是快周期的 N 倍;本文取 N=4,慢侧相邻两个上升沿之间包含四个快周期。这里把慢周期内的快侧采样边沿称为 subedge。
“频率比例为 4:1”还不够。两者必须有可约束的相位关系,并满足跨时钟路径的建立、保持时间。下文的示例取同相上升沿;这是讲解用的时序模型,不代表任意实现都天然满足。
| 通用信号 | 含义 | 需要约定的行为 |
|---|---|---|
clk_fast / clk_slow | 快、慢侧采样时钟 | 分频比、相位、偏斜及门控关系可确定 |
slow_en | 向快侧标记慢侧边界或接收窗口的使能 | 必须定义脉冲宽度及相对两侧采样沿的位置 |
valid / ready | 传输有效 / 可以接收 | 在各自约定的采样边沿,两者同时为 1 才完成传输 |
data | 完整数据总线 | 尚未完成源端握手时,有效数据保持稳定 |
分频时钟和 slow_en 由外部时钟逻辑提供。本文的适配器不生成分频时钟,也不靠内部相位计数器选择某个固定 subedge。本文所有公式均为通用教学表达,省略具体实现的层级、位宽和命名。
02慢→快:接收一次,再屏蔽重复
慢侧送出一项 A 后,通常要一直保持 valid 和 data,直到自己的采样边沿确认 ready。快侧却可能在这段时间里遇到多个上升沿。如果直接把慢侧 valid 接过去,同一项 A 可能被快侧重复接收。
一个简洁的办法是在快侧增加一位 done:它表示当前慢侧传输项已经被快侧接受。关键逻辑可以抽象为:
fast_valid = slow_valid & ~done
slow_ready = fast_ready | done
accept = fast_valid & fast_ready
// 每个快时钟上升沿更新;slow_en 清零优先
done_next = (done | accept) & ~slow_en
// 复位时 done = 0;data 直接传递,不在这里寄存
done=0时,慢侧 valid 可以向快侧传播。第一次快侧握手成功,done置 1。done=1后,fast_valid=0,同一项保持有效的数据不会在后续快沿被再次接收。slow_ready=1保留“已经接收”的事实,直到慢侧在自己的边沿观察到它并推进到下一项。- 约定的
slow_en边界清除done,重新开放下一项的接收机会。
done 不是“缓冲区里存着数据”的标志。它只记录接收事实,没有保存 data。slow_ready 中的 done 分支,也不是承诺快侧此刻能再接一项;它是在告诉慢侧:当前这一项早已被接收。
若 slow_en 与 accept 同沿为 1,清零优先,done 不会留下这次接收记录。外围时序必须避免因此重新接受同一项,或保证慢侧及时推进。不能仅靠这几个布尔公式证明所有相位下都不会重复。
03一个有明确前提的 4:1 示例
假设慢侧在 F0 前已稳定送出 A;慢沿位于 F0、F4、F8;slow_en 仅在这些边沿被快侧采到为 1。快侧在 F0 不 ready,在 F1 接收 A,在 F5 接收 B。源端遵守保持型握手,跨域信号满足建立与保持时间。
| 采样沿 | slow_en | 源端数据 沿前 → 沿后 | done 沿前 → 沿后 | fast_valid / ready 沿前值 | 结果 |
|---|---|---|---|---|---|
| F0 / 慢沿 | 1 | A → A | 0 → 0 | 1 / 0 | 清状态;两侧均未完成 A 的握手 |
| F1 | 0 | A → A | 0 → 1 | 1 / 1 | 快侧接受 A;源端继续保持 |
| F2–F3 | 0 | A → A | 1 → 1 | 0 / 任意 | 屏蔽 A 的重复接收;slow_ready=1 |
| F4 / 慢沿 | 1 | A → B | 1 → 0 | 0 / 任意 | 慢侧确认 A;沿后切换 B、重新开放窗口 |
| F5 | 0 | B → B | 0 → 1 | 1 / 1 | 快侧接受 B |
| F6–F7 | 0 | B → B | 1 → 1 | 0 / 任意 | 屏蔽 B 的重复接收 |
| F8 / 慢沿 | 1 | B → 下一项 | 1 → 0 | 0 / 任意 | 慢侧确认 B;重新开放窗口 |
关键是区分两个完成时刻:快侧在 F1 已接收 A,慢侧在 F4 才确认 A。中间由 done 保存这一事实。F1 只是示例中的首次就绪边沿;若 ready 更晚到达,接收可以发生在其他合法快沿。
本例有意让 F0 不 ready,避开“首次接收与清零同时发生”的情况。真实电路若采用其他 enable 相位或脉宽,应重新检查慢侧确认、状态清零和新数据建立的先后关系。
04快→慢:只在慢侧能采样时消费
反方向的难点是:快侧不能因为慢侧 ready 保持为 1,就在每个快沿连续释放数据。常见的适配方式是让 data 和 valid 向慢侧传播,同时用 slow_en 限制返回快侧的 ready:
slow_valid = fast_valid
slow_data = fast_data
fast_ready = slow_ready & slow_en
快侧只在 slow_ready 和接收窗口同时有效时消费当前项。窗口之外,fast_ready=0,快侧保持 data 和 valid,等待下一次慢侧接收机会。
| 窗口 | 慢侧 ready | 快侧 ready | 结果 |
|---|---|---|---|
| F0 / 慢沿,slow_en=1 | 0 | 0 | 数据 R 保留 |
| F1–F3,slow_en=0 | 任意 | 0 | 快侧不消费,继续保持 R |
| F4 / 慢沿,slow_en=1 | 0 | 0 | 继续等待 |
| F8 / 慢沿,slow_en=1 | 1 | 1 | 两侧在约定边沿完成 R 的交接 |
ready 门控成立的前提,是 slow_en 准确对应慢侧接收边沿。使能若覆盖多个有效快沿,或错开慢沿,就不能单凭 AND 门保证两侧消费次数一致。
05数据保持,由本地流水级负责
时钟适配和弹性缓冲承担不同职责。适配器解决两侧的消费时刻和消费次数;相邻的本地寄存器或弹性流水级,在反压期间保存有效数据。它们可以串接在数据通路上,但不能混称为一个异步 FIFO。
| 部分 | 存储内容 | 职责 |
|---|---|---|
| 慢→快适配状态 | 一位 done | 记住当前项已接收,避免重复 |
| 快→慢窗口逻辑 | 本例没有数据寄存器 | 限定快侧消费时机 |
| 本地弹性流水级 | data、valid 及占用状态 | 保存被阻塞的数据,并向上游反压 |
慢→快尚未被接受时,done=0 且快侧不 ready,慢侧必须保持当前项。快→慢尚未到窗口,或慢侧不 ready 时,快侧必须保持当前项。如果包含 Head、Tail 等边界标志,它们也随 data 一起保持;其中 Head 仅指 1-bit 标志,Hdr 才指数据总线中的头域。
因此,背压可以延长等待,但不会自动增加适配器的容量。实际能缓存多少项、连续传输时有多少延迟,应由完整流水级和队列结构确定。
06这个模型能用到哪里
本电路的适用前提是:整个系统都是同步系统。快、慢两侧时钟虽然频率不同,但必须是同步时钟,具有确定、可约束的频率与相位关系,并对跨时钟路径进行同步时序分析。仅有相同的频率比例,不能说明两个时钟同步。
上述适配逻辑依赖这一同步关系;不能直接用于两个独立、相位无关的异步时钟域。
| 边界 | 需要确认的条件 |
|---|---|
| 时钟与使能 | 真实分频比、slow_en 脉宽及相位,与采样模型一致;含门控后的边沿关系 |
| 实现时序 | data、valid、ready、slow_en、done 相关路径满足建立/保持要求,并计入时钟偏斜 |
| 复位 | done 初始化为 0;两侧流水级的复位释放及首个有效窗口协调一致 |
| 停钟 / 改分频 | 定义在途项如何处理及何时允许恢复传输;不能把稳态推导当作动态切换证明 |
| 功能验证 | 覆盖 ready 延迟、连续项、接收与清零同沿、复位打断和使能边界,检查无丢失、无重复及顺序保持 |
逻辑上最值得记住的是:done 把一次快侧接收保留到慢侧能够确认;slow_en 把一次快侧消费约束到慢侧能够接收。它们与两侧的数据保持协议一起工作,最终仍需要真实时钟、复位和时序约束来闭合正确性。