目录
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 数据使用模块Receiver
REN_FIFO<=1; | 读取一位数据到REG SendIrq <= 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)
begin
if(~SYS_RST)
begin
REN_to_CMDFIFO <=0;
DataNum <= 0;
ReadCount <=0;
DelayCount <= 0;
SendIrq <= 1'b0;
SendIrq <= 1'b0;
SenderStatus<=0;
end
else
begin
case(SenderStatus) // 状态机开始位置!!!
0:
begin
if(USEDW >= 1)
begin
REN_to_CMDFIFO <= 1;
DataNum <= 0;
ReadCount <= 0;
SenderStatus <= 1;
end
end
1:
begin
DataNum <= D_from_CMDFIFO;// FIFFO的输出数据
REN_to_CMDFIFO <= 0;
DelayCount <= 0;
SenderStatus <= 2;
end
2:
begin
if( USEDW >= DataNum ) // 如果FIFO中数据量>= DataNum
begin
SenderStatus <= 4;
end
else
begin
SenderStatus <= 3; // 状态3是起延时作用
end
end
3:
begin
if(DelayCount == 500000)
begin
DelayCount <= 16'b0;
SenderStatus <= 0; // 直接回到状态2可能会导致状态机继续无效循环,而回到状态0则可以重新评估是否应该继续尝试读取数据
end
else
begin
DelayCount <= DelayCount + 1'b1;
SenderStatus <= 2;
end
end
4:
begin
if( USEDW>=1 )
begin
REN_to_CMDFIFO <= 1; // 可以进行读取了
ReadCount <= ReadCount + 1'b1;
SenderStatus <= 5;
end
end
5:
begin
SerialData[7:0] <= D_from_CMDFIFO[7:0]; // 读取一位数据
REN_to_CMDFIFO <= 0;
SendIrq <= 1'b1;
SenderStatus <= 6;
end
6:
begin
if(SendAck == 1'b1) // SendAck是和SendIrq关联的控制寄存器
begin
SendIrq <= 1'b0;
SenderStatus <= 7;
end
end
7:
begin
if(ReadCount == DataNum)
begin
SenderStatus <= 0; // 如果已经读取固定长度的数据,转到状态0
end
else
begin
SenderStatus <= 4; // ReadCount != DataNum,继续读取并发送数据
end
end
default:
begin
SenderStatus <= 0;
end
endcase
end
end
Receiver
always@(posedge CLK_for_FIFO or negedge SYS_RST)
begin
if(~SYS_RST)
begin
ReceiverStatus <= 0;
end
else
begin
case(ReceiverStatus)
0: // 等待中断信号SendIrq有效
begin
if(SendIrq == 1'b1) // 读取一位数据进行发送请求
begin
ReceiverStatus <= 1;
end
end
1: // 根据TempSend300HzBuf和PNFlag的值决定下一步操作
begin
OUT[0] = SerialData[0]; // 从FIFO读取的数据
ReceiverStatus <= 2;
end
2:
begin
if(count_receiver == 200000 ) // 状态2:延时200000个时钟周期后
begin
SendAck <= 1;
count_receiver <= 0;
ReceiverStatus <= 3;
end
else
begin
count_receiver <= count_receiver + 1'b1;
end
end
3:
begin
if(SendIrq == 1'b0) // 状态11:等待SendIrq无效后回到控制数据读取初始状态0
begin
SendAck <= 0;
ReceiverStatus <= 0;
end
end
default: // 默认情况下重置所有相关寄存器和变量
begin
TempSerialData <= 0;
ReceiverStatus <= 0;
SendAck <= 0;
end
endcase
end
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',
]
}