fpga系列 HDL:跨时钟域同步 4-phase handshake(四相握手通信协议,请求-确认机制)浅释与代码实现

发布于:2025-04-10 ⋅ 阅读:(41) ⋅ 点赞:(0)


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;

在这里插入图片描述

特点与优势

可靠性
  • 每次数据传输都需要双方确认,确保数据不会丢失或被重复处理。
  • 即使系统时钟不同步,也能正常工作,适用于异步通信。
灵活性
  • 不依赖于全局时钟信号,适合异步电路设计。
  • 支持动态调整数据传输速率,适应不同的应用场景。
双向同步
  • 发送方和接收方通过 ReqAck 信号实现双向同步,确保双方的状态一致。
容错能力
  • 如果一方出现故障(例如未及时响应),另一方可以通过超时机制或其他方式检测并处理。

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', 
  ]
}