本系列文章的主要目标是开发一种在微控制器(uC)和现场可编程门阵列(FPGA)之间快速可靠地传输数据的机制。在本系列文章中,我们将描述通信协议和相关帧的属性。
设计uC到FPGA接口的方法有很多。让我们先建立一套通用的设计要求来框定这个对话:
- 尽量减少uC和FPGA之间的线数
- 允许uC和FPGA之间有针对性的(可寻址的)信息交换
- 容纳数千个可寻址的FPGA寄存器
- 优化消息长度
- 易于在uC中编程,而不会在FPGA中过于复杂
- 高速
- 提供数据完整性的度量
- 跨平台兼容,例如,不特定于Xilinx
请注意,FPGA资源的最小利用率不是设计要求之一。本系列的 第1部分将探讨这种明显遗漏的原因。回想一下,所有FPGA模块输出都是在时钟域内注册和同步的。这种设计规定被认为是一种可接受的权衡,平衡FPGA结构的消耗与设计稳定性和减少故障排除时间。虽然本文中描述的RTL没有经过优化,但它只消耗了Digilent Basys 3的Xilinx Artix-7 XC7A35T-1CPG236C FPGA中可用总资源的一小部分。
协议的概念
设计及其需求最好通过硬件和数据协议进行可视化。这类似于开放系统互连(OSI)模型的第一层和第二层。第一层物理层描述电气规范。在本设计中,低线数意味着我们可以使用I2C, SPI,双SPI,甚至四路SPI。从这些选项中,SPI脱颖而出,因为它允许全双工通信,并且很容易从uC内部编程。顺便说一句,我们假设FPGA可以比uC更快地处理数据,从而消除了对流量控制机制的需求。
第2层数据链路级别要求我们定义一个数据交换的协议。这一层关注的是将数据分成适当大小的块,以及适当的报头和错误检测。请注意,第2层和第1层是相互独立的,因为第2层不直接与第1层指定的电接口有关。在未来,这可能允许我们的SPI接口被四SPI甚至八SPI所取代,前提是保留层之间的字节级接口。
据我所知,目前还没有使用SPI进行数据交换的既定协议。设备特定的实现是规范。例如,传感器可能有制造商或设备特定的协议来读取和写入数据。这将包括涉及单个或块寄存器的数据交换规则。选择的设备可能包含循环冗余检查(CRC),允许对数据读写操作的数据完整性进行测量。最后,大多数SPI设备都使用半双工协议。一个典型的SPI传输从一个包含读/写标志的字节开始。然后,设备将对后续的时钟八字节采取适当的动作。在MOSI和MISO线上发现同时传输的全双工是很少见的。
从uC的角度来看,一个全双工SPI主控是每个实现。这是一个Arduino实现,包括芯片选择信号内SPI传输的帧。
// prepare buf for SPI transmission
// append CRC
digitalWrite(CS_PIN, LOW);
SPI.transfer(buffer, bufferSize); // full-duplex: each uC byte in buff is replaced with an FPGA byte
digitalWrite(CS_PIN, HIGH);
// verify and handle CRC
// unpack and handle FPGA data
802.3帧对SPI的适配
在我们继续之前,有必要检查一下802.3以太网帧,因为它是SPI协议的起点。如图1所示,以太网帧以包含前言、MAC目的地加源和长度字段的报头开始。接下来是有效载荷,它可以从46到1500字节不等。后面是一个32位的CRC码。
图1:简化的802.3以太网帧。
我们可以减小以太网帧的尺寸,以更好地适应uC到FPGA的接口。有了SPI,就不需要序言和寻址了。SPI CS_not控制线和SPI的一对一特性使这些字段冗余。大小字段和可变长度的有效载荷是需要保留的特性。这将允许uC读取或写入单个FPGA寄存器。它还将允许传输一个数据块。最后,CRC将允许对数据进行验证。
出于我们的目的,字段的大小可能会减少。一个合理的折衷办法是使用单个字节来标识有效载荷的长度。这自然将帧的长度限制为256个元素,包括报头和CRC的空间。考虑到帧长度的减少,16位CRC是合理的。
均衡消息类型要求
回想一下,SPI提供了一个全双工通信通道。如上所示,从uC编程的角度来看,这并不过于复杂。然而,为了利用这种全双工模式,我们现在必须确定如何处理FPGA内部的特定硬件。
要使用这种方法,FPGA硬件必须是可寻址的。例如,FPGA训练板的led可能与一个基址相关联-安装在Basys 3上的16个led的两个字节。电路板的16个滑动开关可以与另外两个地址相关联。有了这种结构,FPGA就像uC的另一个外设一样。uC通过SPI使用帧协议对FPGA硬件进行读写。
在本文的其余部分中,我们将把FPGA硬件称为外设。这反映了uC通过SPI接口有效地使用FPGA作为高性能外设的设计。
设计要求之一是可扩展性,能够处理几个到几千个FPGA寄存器。考虑到这一要求,一个字节宽度的地址将我们限制在256个项目中过于狭窄。然而,2字节宽的接口是可以接受的,因为它允许uC在FPGA内寻址多达65536个项目。
我们可以在这一点上停下来,并为我们的SPI帧确定一个报头。它将包含指示命令类型(读或写)的一个字节、有效载荷长度的一个字节、地址的两个字节、可变长度的有效载荷和CRC。对应的长度为4字节,负载最大可达250字节,CRC为2字节。
这对于很多设计来说可能已经足够了。然而,这种方法隐含了半双工的假设。要了解原因,请考虑与SPI转移相关的因果关系。假设控制板的16个led灯的寄存器位于基址0x0200。如果我们要在这个寄存器上启动SPI传输,我们将有效地读取FPGA寄存器,然后向其写入。读取操作将是毫无意义的,因为它将包含旧数据。在这里,我们假设在所有FPGA寄存器上实现双缓冲,以防止与多字节寄存器更新相关的不稳定。
对于全双工,我们需要添加另一个寻址字段。假设我们有一个寄存器,它保存位于地址0x0202的FPGA板的开关状态。如果我们在帧中添加另一个寻址字段,我们现在可以执行有意义的读写操作。
我们现在可以读取开关状态,同时更新led灯。
新的报头长度为5个字节,有效载荷可达249个字节,CRC为2个字节。注意,命令字节不再是必需的了。
- Payload Length:(1字节)header和Payload中的num字节
- To地址字段(2字节)标识要写入的FPGA寄存器
- 来自地址字段(2字节)标识要读取的FPGA硬件
- Payload:(1 ~ 249字节)
- CRC:(2字节)
仔细比较这两个选项,就会发现一个字节的差异。具有读/写命令字节的半双工帧需要8字节的传输才能写入FPGA板的16个led。相应的全双工操作则需要9个字节。然而,该设计现在可以同时读取开关并写入led。根据你的看法,我们要么在这个过程中浪费了一个字节,要么节省了八个字节加上与事务相关的时间开销。
在考虑了这些设计权衡之后,我们将专门关注全双工选项。uC开销和FPGA编程开销是无关紧要的。小载荷传输的速度差异是最小的。对于大载荷,这种变化是无关紧要的。
这种全双工协议有一个缺点,必须加以识别。事实上,现在每一次数据传输都涉及到一次写操作。有时这样做会很不方便。我们将保留从0xFF00到0xFFFF位置的FPGA地址的空/非功能块,而不是混淆写操作。当需要进行只读内存操作时,uC将“写”到这个空位置。通过这种简单的修改,全双工操作可以作为半双工读操作来操作。
向FPGA写入和读取
全双工uC到FPGA协议的命令和响应帧如图2所示。报头内容在帧之间密切相关。我们看到读地址和长度报头信息在响应中得到呼应。注意,有一个与响应相关的因果关系问题。由于字段是同时进行逐字节传输的流,因此响应将被延迟。例如,作为发送的第一个字节的长度字段不能作为响应中的第一个字段,因为当FPGA锁定要发送的第一个字节时,它尚未被接收。这不是一个问题,因为写地址对响应帧并不重要。相反,FPGA可以发送程序特定的标志,而uC正在流式传输第一个字节的长度。
帧字段的其余部分是类似的。FPGA响应将从基读地址开始流式传输数据。同时,FPGA将同时将uC数据移动到临时缓冲区。计算出相应的CRC并附加到帧中。
图2:命令帧和响应帧之间的关系。
命令帧的数据完整性
CRC提供了数据完整性的度量。回想一下,CRC是应用于帧内容的。让我们从uC的角度来考虑这个三步流程。uC将首先准备一个带有相关数据的帧缓冲区。然后,它将调用一个函数来计算并将CRC追加到缓冲区。最后,uC将调用函数通过SPI传输数据。
FPGA将把流数据放入缓冲区,同时计算命令帧CRC。当帧完成后,FPGA将比较接收到的和计算得到的命令帧CRC。如果它们匹配,FPGA将快速地将数据从缓冲区传输到相关的硬件寄存器。这是一个相对快速的操作,需要大约5个uS来完成一个完整的负载。如果接收到的和计算的CRC不一致,FPGA将丢弃帧。它还会触发帧错误机制。
包含一个帧错误计数器用于故障排除,如图2响应帧所示。还可以增加从FPGA到uC的物理线的规定。这将允许uC快速检测并响应帧错误。
FPGA临时帧缓冲器的重要性怎么强调都不为过。在CRC验证之前,FPGA不会对新数据采取行动。这个重要的主题将在另一期文章中讨论。
响应框架的数据完整性
响应框架的完整性需要在uC中仔细编程。我们必须首先认识到,在命令帧被验证之前,FPGA将立即响应uC读取请求。因此,由uC来验证FPGA开发数据的完整性以及FPGA对命令的理解。这是通过响应帧中的帧长度和基读地址的回声来完成的。uC可以通过比较响应帧和命令帧中的数据来识别错误。
例如,假设从uC到FPGA的命令帧的读地址字段中有一个位错误。在uC到FPGA的写入操作中有一个很好的完整性措施,因为错误有非常高的可能性被FPGA的CRC机制检测到。FPGA将忽略该帧。同时,我们必须考虑与FPGA并行结构相关的因果关系。
FPGA将在它知道命令帧损坏之前很久流式传输读取的数据。事实上,它会尽职尽责地从错误的读地址开始流式传输数据。它甚至会向帧追加一个有效的CRC。
当事务完成时,uC将拥有响应框架的完整副本。在采取行动之前,它必须验证数据。这是一个两步走的过程:
- uC必须首先验证发送和接收字段的长度和读地址是否匹配。
- 然后,它必须验证响应帧的CRC。
当这两个操作完成后,uC可以采取适当的措施来处理通信错误。
第3部分 已发布
欢迎您的评论和建议。特别欢迎进一步讨论高级RTL系统设计方法。