fpga系列 HDL:跨时钟域同步 4-phase handshake(四相握手通信协议,请求-确认机制)浅释与代码实现
目录
- 应用场景
- 四相握手的四个阶段
- Phase 1: 数据准备与请求
- Phase 2: 数据传输与确认
- Phase 3: 请求信号复位
- Phase 4: 应答信号复位
- 特点与优势
- 可靠性
- 灵活性
- 双向同步
- 容错能力
- CODE
- Sender
- Receiver
- CG
- 上图绘制代码
4-phase handshake(四相握手协议) 是一种用于同步和通信的协议,广泛应用于异步电路设计、数据传输以及硬件模块之间的通信。它的核心目标是确保发送方和接收方之间的数据传递是可靠且有序的,避免数据丢失或冲突。
应用场景
- 异步电路设计:用于模块之间的通信,尤其是在没有全局时钟的情况下。
- 总线通信:如处理器与外设之间的数据传输。
- FIFO 缓冲区管理:用于控制 FIFO 的读写操作,确保数据不会溢出或丢失。
- 分布式系统:用于节点之间的消息传递和同步。
四相握手的四个阶段
- 四相握手协议是一种基于请求(Request)和应答(Acknowledge)信号的通信机制。它通过四个阶段完成一次数据传输,因此得名“四相握手”。
Phase 1: 数据准备与请求
- 发送方将数据准备好,并将
Req
信号置为高电平(或激活状态),通知接收方数据已准备好。 - 接收方检测到
Req
信号的变化后,知道可以开始接收数据。
Phase 2: 数据传输与确认
- 接收方读取数据,并在成功接收后将
Ack
信号置为高电平(或激活状态),通知发送方数据已被接收。 - 发送方检测到
Ack
信号的变化后,知道数据传输已完成。
Phase 3: 请求信号复位
- 发送方将
Req
信号复位(置为低电平或非激活状态),表示当前数据传输已结束。 - 接收方检测到
Req
信号的变化后,知道可以准备接收下一个数据。
Phase 4: 应答信号复位
- 接收方将
Ack
信号复位(置为低电平或非激活状态),表示已经准备好接收下一次数据。 - 发送方检测到
Ack
信号的变化后,知道可以开始下一轮数据传输。
FIFO数据控制模块Sender 数据使用模块ReceiverREN_FIFO<=1;| 读取一位数据到REGSendIrq <= 1'b1; ======== a~b =========> if(SendIrq == 1'b1) |使用读取的数据 if(SendAck == 1'b1) <======= b~c ========== SendAck <= 1'b1;|SendIrq <= 1'b0; ======== c~d =========> if(SendIrq == 1'b0) |SendAck <= 1'b0;Status <= 1'b0;
特点与优势
可靠性
- 每次数据传输都需要双方确认,确保数据不会丢失或被重复处理。
- 即使系统时钟不同步,也能正常工作,适用于异步通信。
灵活性
- 不依赖于全局时钟信号,适合异步电路设计。
- 支持动态调整数据传输速率,适应不同的应用场景。
双向同步
- 发送方和接收方通过
Req
和Ack
信号实现双向同步,确保双方的状态一致。
容错能力
- 如果一方出现故障(例如未及时响应),另一方可以通过超时机制或其他方式检测并处理。
CODE
Sender
// FIFO 控制,第一个数为数据的长度,通过循环读取确保数据读取完成
always@(negedge CLK_for_FIFO or negedge SYS_RST)
beginif(~SYS_RST)begin REN_to_CMDFIFO <=0; DataNum <= 0; ReadCount <=0;DelayCount <= 0; SendIrq <= 1'b0;SendIrq <= 1'b0;SenderStatus<=0;end else begincase(SenderStatus) // 状态机开始位置!!!0:beginif(USEDW >= 1)beginREN_to_CMDFIFO <= 1;DataNum <= 0;ReadCount <= 0;SenderStatus <= 1; endend 1:beginDataNum <= D_from_CMDFIFO;// FIFFO的输出数据REN_to_CMDFIFO <= 0;DelayCount <= 0;SenderStatus <= 2;end2: beginif( USEDW >= DataNum ) // 如果FIFO中数据量>= DataNumbeginSenderStatus <= 4;endelsebeginSenderStatus <= 3; // 状态3是起延时作用end end3:begin if(DelayCount == 500000)beginDelayCount <= 16'b0;SenderStatus <= 0; // 直接回到状态2可能会导致状态机继续无效循环,而回到状态0则可以重新评估是否应该继续尝试读取数据end elsebeginDelayCount <= DelayCount + 1'b1;SenderStatus <= 2;endend4:beginif( USEDW>=1 )beginREN_to_CMDFIFO <= 1; // 可以进行读取了ReadCount <= ReadCount + 1'b1;SenderStatus <= 5; end end5:beginSerialData[7:0] <= D_from_CMDFIFO[7:0]; // 读取一位数据 REN_to_CMDFIFO <= 0;SendIrq <= 1'b1; SenderStatus <= 6; end6:beginif(SendAck == 1'b1) // SendAck是和SendIrq关联的控制寄存器beginSendIrq <= 1'b0;SenderStatus <= 7;endend7:beginif(ReadCount == DataNum)beginSenderStatus <= 0; // 如果已经读取固定长度的数据,转到状态0endelsebeginSenderStatus <= 4; // ReadCount != DataNum,继续读取并发送数据end enddefault:beginSenderStatus <= 0;endendcaseend
end
Receiver
always@(posedge CLK_for_FIFO or negedge SYS_RST)
beginif(~SYS_RST)beginReceiverStatus <= 0;endelsebegincase(ReceiverStatus)0: // 等待中断信号SendIrq有效beginif(SendIrq == 1'b1) // 读取一位数据进行发送请求beginReceiverStatus <= 1;endend1: // 根据TempSend300HzBuf和PNFlag的值决定下一步操作begin OUT[0] = SerialData[0]; // 从FIFO读取的数据ReceiverStatus <= 2;end2:beginif(count_receiver == 200000 ) // 状态2:延时200000个时钟周期后beginSendAck <= 1;count_receiver <= 0; ReceiverStatus <= 3;endelsebegincount_receiver <= count_receiver + 1'b1;end end3:beginif(SendIrq == 1'b0) // 状态11:等待SendIrq无效后回到控制数据读取初始状态0beginSendAck <= 0;ReceiverStatus <= 0;endenddefault: // 默认情况下重置所有相关寄存器和变量beginTempSerialData <= 0;ReceiverStatus <= 0;SendAck <= 0;endendcaseend
end
CG
- 数字芯片设计中常见的三个握手协议
上图绘制代码
// https://wavedrom.com/editor.html
{signal: [{name: 'clk', wave: 'p.........'}, {name: 'req', wave: '0.1...0...',node: '..a...c...'},{name: 'ack', wave: '0....1...0',node: '.....b...d'},{name: 'dat', wave: '.x=..x....', data: ['data']},
],head:{text:'4-phase Handshake',tick:0,every:2},edge: ['a~b', 'b~c', 'c~d', ]
}