参考文章:
https://blog.csdn.net/mcupro/article/details/121668833?spm=1001.2014.3001.5501
近期计划设计一个PCIE接口的使用AD9361芯片的SDR前端外设。思路就是用XILINX提供的XDMA的PCIE核,用AXIS_MASTER和AXIS_SLAVE 接口实现数据流接驳AD9361的数据口,使用PCIE核提供的AXI_LITE_MASTER控制若干需要的AXI_LITE外设。有了这个AXI_LITE外设其实就可以将之前在ZYNQ里面运行的裸奔的C代码移植到PC机上运行。
大约1年前我就打通了数据流的的交互试验,这篇文章我主要记录一下AXI_LITE控制接口的打通。也就是实现用PC编程使用AXI_LITE接口实现对AXI_LITE_SLAVE的寄存器的读写。
图中看到XDMA的PCIE核通过AXI 交换器接了5个外设,其实还可以接更多。
设置PCIE:BARs时候默认如图:
我们看到这其实相当于给AXI_LITE_MASTER 留了1M的寻址空间。其中VALUE我觉得可能是MASK的意思,正好有20个比特0,对应1M空间,挂在此AXI_LITE_MASTER下的外设最高地址必须小于1M。
我们看一下AXI_LITE外设的地址空间设置:
我每个外设给分配了64K字节的空间,偏移地址64K递增。这个TOOLS的IP是我写的具有SPI接口和GPIO接口等,其中第一个寄存器也就是REG0我设置了是一个可读写寄存器,能读出上次写入的数值,下列试验代码用到。
这里列出访问的代码:
#ifdef __cplusplus
extern "C" {
#endif
#include <assert.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <pthread.h>
#include <semaphore.h>
#include <stdarg.h>
#include <syslog.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/sysinfo.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
/* ltoh: little to host */
/* htol: little to host */
#if __BYTE_ORDER == __LITTLE_ENDIAN
# define ltohl(x) (x)
# define ltohs(x) (x)
# define htoll(x) (x)
# define htols(x) (x)
#elif __BYTE_ORDER == __BIG_ENDIAN
# define ltohl(x) __bswap_32(x)
# define ltohs(x) __bswap_16(x)
# define htoll(x) __bswap_32(x)
# define htols(x) __bswap_16(x)
#endif
#define MAP_SIZE (1024*1024UL)
#define MAP_MASK (MAP_SIZE - 1)
#define LED_BASE 0x30000
#define BTN_BASE 0x40000
#define TOOLS_BASE 0x10000
void *control_base;
int control_fd;
static int open_control(char *filename)
{
int fd;
fd = open(filename, O_RDWR | O_SYNC);
if(fd == -1)
{
printf("open control error\n");
return -1;
}
return fd;
}
static void *mmap_control(int fd,long mapsize)
{
void *vir_addr;
vir_addr = mmap(0, mapsize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
return vir_addr;
}
void write_io(int offset,uint32_t val)
{
uint32_t writeval = htoll(val);
*((uint32_t *)(control_base+offset)) = writeval;
}
uint32_t read_io(int offset)
{
uint32_t read_result = *((uint32_t *)(control_base+offset));
read_result = ltohl(read_result);
return read_result;
}
int pcie_init()
{
control_fd = open_control("/dev/xdma0_user");
if(control_fd < 0)
return -5;
control_base = mmap_control(control_fd,MAP_SIZE);
return 1;
}
void pcie_deinit()
{
close(control_fd);
}
void main( void ){
unsigned int i , t ;
pcie_init() ;
for(i=0;i<10;++i){
write_io ( TOOLS_BASE,i );
t = read_io(TOOLS_BASE ) ;
if (i==t) printf("OK\n");else printf("ERROR\n");
}
}
#ifdef __cplusplus
}
#endif
这里我们看到使用了mmap函数,首先获取_user文件的基本虚拟地址,之后再加上图ADDERESS EDITER里面列的偏移地址,就是AXI_LITE_SALVE的REG0地址,当然REGn的话只需要在这个偏移量基础上加4*n就可以。
运行后显示OK,试验成功。
{{aAxvOXMOIvVUoXMxvoxiowMwWV8xxWTxoxOIOVIUUOvwVOUiIoUvvTMMVMwovWHWX8vOUOvwVVTxwOTHUHiv8IUiIvMMxTiwMUmvTTIHMUXIHMvvHOMoiHIoVU8VvmvIWXTvvOvv8xvMovOWMMOxxTMi8UiUZz}}