最近想用DSP对FPGA里的IP进行配置,感觉没有什么特别好的办法。如果能像Zynq一样直接有能够配置外设的AXI-Lite接口就好了。EMIF是DSP的外部存储器访问接口,支持对存储器的同步或异步访问。在我现有的条件下,利用EMIF接口配置FPGA内部的寄存器是一个可行的选择。Gitee链接
整体方案
EMIF接口相比于AXI-Lite少了握手的过程。不能仅通过简单的同步/异步访问完成寄存器的读写。整体方案如下图:
写命令需要缓存地址和数据,因为不知道AW和W通道什么时候才会握手。
读命令需要有两步才能完成,首先缓存地址,将地址发送到AR通道,等R通道返回数据之后,DSP再次读数据才能得到真正的结果。
缓存读命令的地址可以采用与缓存写地址用的不同的FIFO,但我采用的方案是读写地址都用同一个FIFO缓存,并且读命令的第一步是向要读的地址写任意数据(这个数据被丢弃),用来缓存写地址。
读写地址的区分额外用了一根地址线,因此DSP在FPGA上实际的访存空间只有逻辑地址的一半。
EMIF地址映射
FT-M6678的EMIF每个片选有64MB的空间,而一般外设的控制寄存器可能只有几kB。我们不需要把整块EMIF地址空间都映射到AXI-Lite接口上,而且很可能我们需要将这64MB的空间分成几部分,分别对应到不同的IP的配置空间中。
通过设置EMIF Base Address,来确定EMIF的高位地址线的值,从而以不同的高位地址线区分不同的AXI-Lite空间。
上图是这个IP的配置界面,需要用户配置需要访问的AXI-Lite接口的基地址和对应的空间大小。
实验测试
实例化一块4kB的Block RAM和一个AXI BRAM控制器,对BRAM进行读写测试。
DSP将EMIF接口配置为同步32bit访问模式,在连续的字地址上依次写入10个从0递增的32bit数据。然后再将刚刚写入的数据读出,可以看到结果正确。
下图是在测试过程中,FPGA端抓到的波形,与设计一致。
DSP端测试代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include "periConfig.h" #define LOOP (10) int main (void ) { int i; Uint32 RdBack[LOOP]; gpioConfig(); emifConfig(8 ); for (i = 0 ; i<LOOP; ++i){ writeReg(i * 4 , i); } for (i = 0 ; i<LOOP; ++i){ RdBack[i] = readReg(i * 4 ); } return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 #ifndef _PERICONFIG_H_ #define _PERICONFIG_H_ #ifdef __cplusplus extern "C" {#endif #include <stdint.h> #include <csl_gpio.h> #define EMIF2AXIL_BASE (0x7C000000) #define AXIL_SPACE_SIZE (0x1000) void gpioConfig () ;void emifConfig (int nEclkRatio) ;static inline Bool isRdFifoEmpty () { return CSL_gpioGetInputBit(CSL_GPIO_PIN4); } static inline Bool isAddrFifoFull () { return CSL_gpioGetInputBit(CSL_GPIO_PIN5); } static inline Bool isBusy () { return CSL_gpioGetInputBit(CSL_GPIO_PIN6); } static inline void writeReg (Uint32 offset, Uint32 val) { while (CSL_gpioGetInputBit(CSL_GPIO_PIN5)); *((Uint32 *)(EMIF2AXIL_BASE + offset)) = val; } static inline Uint32 readReg (Uint32 offset) { while (CSL_gpioGetInputBit(CSL_GPIO_PIN5)); *((Uint32 *)(EMIF2AXIL_BASE + offset + AXIL_SPACE_SIZE)) = 0 ; while (CSL_gpioGetInputBit(CSL_GPIO_PIN4)); return *((Uint32 *)(EMIF2AXIL_BASE)); } #ifdef __cplusplus } #endif #endif