上一篇文章讲述了HPS系统的创建方法和GMII协议接口信号。本文将描述通道的设计思想和实现。
gmii接口和avst接口进行互相转换,需要考虑以下关键点:
(1)gmii转avst。EMAC输入来的包,SFD前面的0x55并不一定是7个,需要考虑不足7个0x55的情况。
(2)avst转gmii。需要自行在一个包前增加0x55555555555555d5,在包后计算CRC32校验。同一个包GMII传输过程中,txen信号不能中途拉低。当一个包总长度不到64字节时,需要补0到至少60字节的数据和4字节的CRC校验。
(3)两个方向的转换过程中,都必须要考虑包的起止,sop和eop的设置。
(4)我们的工程上时钟频率都为250Mhz,需要进行时钟域转换,放在avst格式下进行。
设计整体方案如下图所示。

可以看到,EMAC通道的关键系统在于两个方向的转换器。转换器的设计方法如下。
1、gmii转avst
gmii协议转avalon stream协议,需要注意的关键点有以下几个:
(1)gmii协议具有前导码,在读取数据时需要确定在哪里是一个包的开始,并需要将前导码去掉;
(2)HPS发来的gmii协议数据包前导码的0x55并不一定是7个,可能是不大于7的任意数量个,需要处理;
(3)HPS发来的gmii协议数据包包括CRC校验,我们不需要自己再额外计算。
因此,gmii转avst协议,主要结构图如下。

对于前导码处理,我们将输入数据导入一个移位寄存器buffer中。buffer可以缓存8组的数据,每组数据包括8位数据寄存器和1位使能寄存器。在每个时钟周期,gmii协议接口新的数据首先缓存到最低地址字节,最低位使能寄存器存储rx_en信号,旧的数据和使能信号左移。当检测到新输入的数据是0xd5且rxen=1时,检查移位寄存器前面7组内容,如果是7组使能的0x55,或者前7组中有使能信号为低,则认为是一个新的数据包输入,将前导码之后的数据开始输入进fifo直到使能信号拉低。fifo高位扩宽了一位,用来表明一帧的起始。后续模块检测扩展位即可确定是否开始下一个数据包。
处理后的数据经过fifo后,转为avst格式。直接采用一个状态机进行控制即可。状态图如下。

为便于处理sop和eop信号,专门将处理首包的状态分离出来。在每一个包输出之后,还需要检测接收端的接口是否ready,只有ready才会继续推进。
2、avst转gmii
avst转gmii则注意以下关键点:
(1)gmii协议要求一个包途中tx_en不能拉低。因此需要等待整包生成,再输入fifo,防止出现中途等待情况;
(2)转换成gmii协议发送给HPS,需要加上前导码;
(3)如果一个数据包长度不足60字节(64字节-4字节校验),需要补0到60字节;
(4)FPGA传来的数据包没有CRC校验,需要自己计算并加上。
因此,avst转gmii模块需要先采用fifo缓存输入的avst报文,然后采用状态机进行转换。

状态机中,GETDATA和MID状态需要读fifo取出avst数据包的一帧。ADDHEAD状态为输出数据添加前导码。为便于处理填充情况,将最后一帧的处理状态单独取出。在整个处理过程中,通过一个组合逻辑的CRC计算模块在每个时钟周期实时计算当前CRC32校验值。当进入CRC状态后,直接将整体数据最终计算出来的结果使用4个时钟周期输出即可。
以上即为emac模块的实现结构。在上板调试之前,发现我们的FPGA中的virtio的device feature需要关闭VIRTIO_NET_F_STATUS才能link协商。在修改后,上板调试,HPS系统与其他片上系统之间已经互通。