我是 雪天鱼,一名FPGA爱好者,研究方向是FPGA架构探索。
关注公众号,拉你进“IC设计交流群
”。
受一位朋友之邀写了这篇博客,讲解 tinyriscv 中的跨时钟域传输。
所解析的代码路径:
- tinyriscv\rtl\utils\full_handshake_rx
- tinyriscv\rtl\utils\full_handshake_tx
一、full_handshake_rx
1.1 输入输出信号
先看接口,编译后的原理图如下所示:
左边为输入信号,右边为输出信号。
信号名 | 方向 | 作用 |
---|---|---|
clk | input | 时钟信号 |
rst_n | input | 复位信号 |
req_i | input | TX端请求信号 |
req_data_i | input | TX端输入数据 |
ack_o | output | RX端应答TX端信号 |
recv_data_o | output | RX端接收到的数据 |
recv_rdy_o | output | RX端是否接收到数据信号 |
1.2 代码逻辑
先看代码:
经典的三段式状态机写法,我已经用红色框标注出了,三段式状态机也就是将
- 状态的转移
- 次态的生成
- 输出结果的生成
三个分别放在三个 always块中进行描述,优点是逻辑清晰,速度快,缺点是占用的硬件资源比较多。
这个状态机,就只有两个状态,一个是 STATE_IDLE
,一个是 STATE_DEASSERT
。
刚开始复位后处于 STATE_IDLE 状态,此时
- 当请求信号 req = 1时,将从 STATE_IDLE 状态转移到 STATE_DEASSERT 状态,并且接收发送端所发送的数据,并反馈应答信号。
所以仅在 STATE_IDLE 状态且 req = 1 时,接收端才会有效的接收数据并应答。
在代码中还有一个always块,其通过打两拍来进行信号的同步。
二、full_handshake_tx
2.1 输入输出信号
先看接口,编译后的原理图如下所示:
左边为输入信号,右边为输出信号。
信号名 | 方向 | 作用 |
---|---|---|
clk | input | 时钟信号 |
rst_n | input | 复位信号 |
ack_i | input | RX端应答信号 |
req_data_i | input | TX端要发送的数据,只需持续一个时钟 |
req_i | input | TX端请求信号,只需持续一个时钟 |
idle_o | output | TX端是否空闲信号,空闲才能发数据 |
req_o | output | TX端请求信号 |
req_data_o | output | TX端要发送的数据 |
2.2 代码逻辑
先看代码:
同样的经典的三段式状态机写法。
这个状态机,就有三个状态:
- STATE_IDLE
- STATE_ASSERT
- STATE_DEASSERT
刚开始复位后处于 STATE_IDLE 状态,此时
- 当请求信号 req = 1时,将从 STATE_IDLE 状态转移到 STATE_ASSERT 状态,并且锁存所要发送的数据 ,并进行发送数据的请求。
所以仅在 STATE_IDLE 状态且 req = 1 时,发送端才会有效的发送数据并请求下级rx端接收数据。
三、跨时钟域传输总结
首先得知道为什么需要跨时钟域同步,在 tinyriscv 的调试模块 jtag_top
中,实现了两个子模块,分别是 jtag_dm
和 jtag_driver
, jtag_driver 实现了 JTAG TAP 和 DMI,用来根据外部调试器发送的JTAG信号来对 DM 进行访问操作。而这里问题就出现了,这个访问操作是根据 JTAG TCK 信号产生的,tinyriscv作者设置为 1MHz,而 jtag_dm 的时钟信号是系统时钟 50MHz,时钟不同步时进行访问绝大多数情况下是出错,要么传输数据不对,要么数据正确时控制信号不对,这应该很好理解。
通过 full_handshake_tx
和 full_handshake_rx
来实现只有握手时才会传输数据,保证数据传输的稳定性和正确性。
在两个不同时钟域的模块中,都加入这两个模块,如 jtag_driver 通过 full_handshake_tx 发送数据给 jtag_dm
那么 jtag_dm 通过 full_handshake_rx 进行接收数据, 再通过 full_handshake_tx 回传反馈给 jtag_driver,jtag_driver 通过 full_handshake_rx 接收,over。所以这也就是全握手协议
,完整的向 dm 写入数据和应答回传会握手四次。如下图所示,tx与rx的一次请求或者应答就是一次握手,即图中的一个箭头就代表一次握手,一共四次。