DM8168通过GPMC接口与FPGA高速数据通信实现
硬件:TI达芬奇TMS320DM8168(以下简称DSP)、EP4CE6E22C8N(以下简称FPGA)
软件:linux-2.6.37
转载请注明出处~
http://www.cnblogs.com/imapla/p/4122609.html
近期项目需要实现DSP与FPGA之间的高速数据交换,用到了DM8168的GPMC接口。这部分的中文资料网上还是比较少的,于是苦苦研究芯片的数据手册和参考指南,最近终于有所成果,在Linux下调用GPMC驱动函数调通了GPMC接口,因此发出调试过程与大家分享。目前以DSP端可以通过GPMC用EDMA的方式读取FPGA端的数据,读取8KB字节大概用了235us,即34MB/s的速度,实际上通过配置GPMC接口的时间参数,速度还可以更快。
GPMC的全称是 General-Purpose Memory Controller,即通用存储控制器,是TI的DSP芯片DM8168用来与外部存储设备例如NOR FLASH、NAND FLASH、SRAM等等通信的一个接口。这个接口并不是DM8168特有的,在BeagleBone Black、AM35XX芯片上也有类似接口。
1、硬件连接方式:在DM8168中GPMC接口时钟在异步模式下为125MHz,这里就把GPMC接口配置为异步模式并设置NOR FLASH、非地址数据线复用的模式与FPGA通信,但只用16位数据线,不用地址线,即采用类似于FIFO的方式与FPGA通信。目前实际只使用到了如下I/O口:
GPMC_CS3: 用CS3做片选信号
GPMC_OEN: 输出使能时钟
D[15:0]: 16位数据总线
FIFO_RRST: 用于通知FPGA读指针复位

2、接口协议:采用异步方式读取,即不使用GPMC_CLK,FPGA端在GPMC_OEN的下降沿把数据送出去。

3、Linux下DSP端代码分析
Linux中gpmc驱动源代码在 /arch/arm/mach-omap2/gpmc.c
配置方面主要包括与GPMC相关的7个特殊寄存器,其实linux函数中已经把相关配置封装成了函数,我们只需要调用相关函数就可以。
EDMA的配置也是如此,需要注意的是,EDMA中配置的地址都为物理地址,即DMA传送到源端为外设的地址即GPMC的物理地址,目的地端为你申请的内存空间物理地址。由于DSP是使用FIFO模式读取FPGA端数据的,所以EDMA还需配置源地址为不自增模式。代码如下,内容不多,所以不做详细注释。
FPGA端的代码只要是实现在每个OEN信号下降沿来的时候,把16bit的数据送到GPMC_DATA端口,并自加一次。
GPMC配置代码如下
static struct gpmc_timings fpga_timings = {
/*- GPMC timing configurations -*/
.sync_clk = ,
// CONFIG2 chip-select time
.cs_on = , /* Assertion time */
.cs_rd_off = , /* Read deassertion time */
.cs_wr_off = , /* Write deassertion time */
// CONFIG3
.adv_on = ,
.adv_rd_off = ,
.adv_wr_off = ,
// CONFIG4
.we_on = , /* WE assertion time */
.we_off = , /* WE deassertion time */
// CONFIG4
.oe_on = , /* OE assertion time */
.oe_off = , /* OE deassertion time */
// CONFIG5
.page_burst_access = ,
.access = , /* Start-cycle to first data valid delay */
.rd_cycle = , /* Total read cycle time */
.wr_cycle = , /* Total write cycle time */
// CONFIG6
.wr_access = ,
.wr_data_mux_bus = ,
};
static int gpmc_config(void)
{
// first reg gpmc_init() already called; io pinmux already configed
// ti8168evm board_nand_init -> gpmc_nand_init
u32 val = ;
int err = ;
/*-
EXPORT_SYMBOL(gpmc_cs_write_reg);
EXPORT_SYMBOL(gpmc_cs_read_reg);
EXPORT_SYMBOL(gpmc_cs_set_timings);
-*/
// gpmc cs disable memory
val = gpmc_cs_read_reg(GPMC_FPGA_CS, GPMC_CS_CONFIG7);
val &= ~GPMC_CONFIG7_CSVALID;
gpmc_cs_write_reg(GPMC_FPGA_CS, GPMC_CS_CONFIG7, val);
// disable cs3 irq
gpmc_cs_configure(GPMC_FPGA_CS, GPMC_SET_IRQ_STATUS, );
gpmc_cs_configure(GPMC_FPGA_CS, GPMC_ENABLE_IRQ, );
// set config1
gpmc_cs_write_reg(GPMC_FPGA_CS, GPMC_CS_CONFIG1, GPMC_CONFIG1_READTYPE_ASYNC| // set read type async
GPMC_CONFIG1_WRITETYPE_ASYNC| // set write type async
GPMC_CONFIG1_DEVICESIZE_16| // set device size 16bit
GPMC_CONFIG1_DEVICETYPE_NOR // set device type nor
);
val = gpmc_cs_read_reg(GPMC_FPGA_CS, GPMC_CS_CONFIG1);
val &= ~GPMC_CONFIG1_MUXADDDATA;
gpmc_cs_write_reg(GPMC_FPGA_CS, GPMC_CS_CONFIG1, val);
// set gpmc timings
err = gpmc_cs_set_timings(GPMC_FPGA_CS, &fpga_timings);
if(err < ){
printk(KERN_ERR "Unable to set gpmc timings\n");
}
// apply gpmc select memory
err = gpmc_cs_request(GPMC_FPGA_CS, GPMC_FIFO_SIZE, &gpmc_membase);
if(err < ){
printk(KERN_ERR "Cannot request GPMC CS\n");
return err;
}
// request_mem_region(gpmc_membase, GPMC_FIFO_SIZE, DRIVERNAME);
// fpga_membase = ioremap(gpmc_membase, GPMC_FIFO_SIZE);
return err;
}
下面是总的代码,折叠了。
/*
* fileName: fpga_perh.c
* for gpmc fpga communication
*/
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/list.h>
#include <linux/cdev.h>
#include <linux/proc_fs.h>
#include <linux/mm.h>
#include <linux/seq_file.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <linux/io.h>
#include <mach/gpio.h>
#include <linux/device.h>
#include <linux/platform_device.h>
// gpmc spec
#include <plat/gpmc.h>
// edma spec
#include <linux/interrupt.h>
#include <linux/dma-mapping.h>
#include <mach/memory.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#include <asm/hardware/edma.h> #define DRIVERNAME "fpga" // once_dma = 8KByte = 4*1024*16bit
#define FPGA_FIFO_SIZE SZ_4K /*------------------------------------------------------------------------------------------------------------------*/
// GPMC Config
#define GPMC_FIFO_SIZE SZ_16K #define GPMC_FPGA_CS 3 // gpmc use CS 3
#define GPMC_CONFIG1_3 0x00001010 // 16bit size NOR FLASH like
#define GPMC_CONFIG2_3 0x00101080 // CSWROFFTIME=16cy CSRDOFFTIME=16cy CSEXTRADELAY=1
#define GPMC_CONFIG3_3 0x00000000 // ADV TIME used
#define GPMC_CONFIG4_3 0x0F031003 // WEOFFTIME=15cy WEONTIME=3cy OEOFFTIME=16cy OEONTIME=3cy
#define GPMC_CONFIG5_3 0x000F1111 // RDACCESSTIME=15cy WRCYCLETIME=1cy RDCYCLETIME=1cy
#define GPMC_CONFIG6_3 0x0F030000 // WRACCESSTIME=15cy WRDATAONADMUXBUS=3cy Add CYCLE2CYCLEDELAY
#define GPMC_CONFIG7_3 0x00000F42 // set up CONFIG7 and enable cs3
// chip-select mask address = MASKADDRESS = 16MB
// CS enabled
// Chip-select base address = BASEADDRESS = 0x02000000
// Access address 0x02000000-0x02FFFFFF
#define GPMC_MASKADDRESS 0x00FFFFFF // fifo_size
#define GPMC_BASEADDRESS 0x02000000 // gpmc address
/*------------------------------------------------------------------------------------------------------------------*/
// FPGA GPIOs
#define CTRL_MODULE_BASE_ADDR 0x48140000
#define conf_gpio18 (CTRL_MODULE_BASE_ADDR + 0x0B98)
#define conf_gpio19 (CTRL_MODULE_BASE_ADDR + 0x0B9C) #define WR_MEM_32(addr, data) *(unsigned int*)OMAP2_L4_IO_ADDRESS(addr) = (unsigned int)(data)
#define RD_MEM_32(addr) *(unsigned int*)OMAP2_L4_IO_ADDRESS(addr) // delay for reset
#define _delay_ms(n) mdelay(n)
#define _delay_ns(n) ndelay(n) // Read Point Low is Reset
#define FPGA_RRST_H gpio_set_value(18, 1);
#define FPGA_RRST_L gpio_set_value(18, 0);
/*------------------------------------------------------------------------------------------------------------------*/
// EDMA Config
#define MAX_DMA_TRANSFER_IN_BYTES (4096*2)
#define STATIC_SHIFT 3
#define TCINTEN_SHIFT 20
#define ITCINTEN_SHIFT 21
#define TCCHEN_SHIFT 22
#define ITCCHEN_SHIFT 23
/*------------------------------------------------------------------------------------------------------------------*/
//unsigned int fpga_buf[FPGA_FIFO_SIZE] = {0};
static unsigned long gpmc_membase = ;
static void __iomem *fpga_membase = ;
static int gpio[];
dma_addr_t dmaphysdest = ;
unsigned short *fpga_buf = NULL;
unsigned int dma_ch = ;
static volatile int irqraised1 = ; static struct gpmc_timings fpga_timings = {
/*- GPMC timing configurations -*/
.sync_clk = ,
// CONFIG2 chip-select time
.cs_on = , /* Assertion time */
.cs_rd_off = , /* Read deassertion time */
.cs_wr_off = , /* Write deassertion time */
// CONFIG3
.adv_on = ,
.adv_rd_off = ,
.adv_wr_off = ,
// CONFIG4
.we_on = , /* WE assertion time */
.we_off = , /* WE deassertion time */
// CONFIG4
.oe_on = , /* OE assertion time */
.oe_off = , /* OE deassertion time */
// CONFIG5
.page_burst_access = ,
.access = , /* Start-cycle to first data valid delay */
.rd_cycle = , /* Total read cycle time */
.wr_cycle = , /* Total write cycle time */
// CONFIG6
.wr_access = ,
.wr_data_mux_bus = ,
}; // static dev_t dev;
// static struct cdev cdev;
// static struct class *gpmc_edma_class = NULL; static void callback1(unsigned lch, u16 ch_status, void *data)
{
switch(ch_status) {
case DMA_COMPLETE:
irqraised1 = ;
/*DMA_PRINTK ("\n From Callback 1: Channel %d status is: %u\n", lch, ch_status);*/
break;
case DMA_CC_ERROR:
irqraised1 = -;
printk ("\nFrom Callback 1: DMA_CC_ERROR occured on Channel %d\n", lch);
break;
default:
break;
}
} static int gpio_store(void);
static int gpio_recover(void);
static int gpio_config(void);
static int gpmc_config(void);
static int edma_config(void); static int gpio_store(void)
{
// store gpio pinmux
gpio[] = RD_MEM_32(conf_gpio18);
gpio[] = RD_MEM_32(conf_gpio19);
return ;
} static int gpio_recover(void)
{
// recover gpio pinmux
WR_MEM_32(conf_gpio18, gpio[]);
WR_MEM_32(conf_gpio19, gpio[]);
gpio_free(gpio[]);
gpio_free(gpio[]);
return ;
} static int gpio_config(void)
{
// config gpio direction
WR_MEM_32(conf_gpio18, ); // MUXMODE=001
gpio_request(, "gpio18_en"); // request gpio46
gpio_direction_output(, ); WR_MEM_32(conf_gpio19, ); // MUXMODE=001
gpio_request(, "gpio19_en"); // request gpio47
gpio_direction_output(, ); return ;
} static int gpmc_config(void)
{
// first reg gpmc_init() already called; io pinmux already configed
// ti8168evm board_nand_init -> gpmc_nand_init
u32 val = ;
int err = ;
/*-
EXPORT_SYMBOL(gpmc_cs_write_reg);
EXPORT_SYMBOL(gpmc_cs_read_reg);
EXPORT_SYMBOL(gpmc_cs_set_timings);
-*/
// gpmc cs disable memory
val = gpmc_cs_read_reg(GPMC_FPGA_CS, GPMC_CS_CONFIG7);
val &= ~GPMC_CONFIG7_CSVALID;
gpmc_cs_write_reg(GPMC_FPGA_CS, GPMC_CS_CONFIG7, val); // disable cs3 irq
gpmc_cs_configure(GPMC_FPGA_CS, GPMC_SET_IRQ_STATUS, );
gpmc_cs_configure(GPMC_FPGA_CS, GPMC_ENABLE_IRQ, ); // set config1
gpmc_cs_write_reg(GPMC_FPGA_CS, GPMC_CS_CONFIG1, GPMC_CONFIG1_READTYPE_ASYNC| // set read type async
GPMC_CONFIG1_WRITETYPE_ASYNC| // set write type async
GPMC_CONFIG1_DEVICESIZE_16| // set device size 16bit
GPMC_CONFIG1_DEVICETYPE_NOR // set device type nor
);
val = gpmc_cs_read_reg(GPMC_FPGA_CS, GPMC_CS_CONFIG1);
val &= ~GPMC_CONFIG1_MUXADDDATA;
gpmc_cs_write_reg(GPMC_FPGA_CS, GPMC_CS_CONFIG1, val); // set gpmc timings
err = gpmc_cs_set_timings(GPMC_FPGA_CS, &fpga_timings);
if(err < ){
printk(KERN_ERR "Unable to set gpmc timings\n");
} // apply gpmc select memory
err = gpmc_cs_request(GPMC_FPGA_CS, GPMC_FIFO_SIZE, &gpmc_membase);
if(err < ){
printk(KERN_ERR "Cannot request GPMC CS\n");
return err;
} // request_mem_region(gpmc_membase, GPMC_FIFO_SIZE, DRIVERNAME); // fpga_membase = ioremap(gpmc_membase, GPMC_FIFO_SIZE); return err;
} static int edma_config(void)
{
// use AB mode, one_dma = 8KB/16bit
static int acnt = *;
static int bcnt = ;
static int ccnt = ; int result = ;
unsigned int BRCnt = ;
int srcbidx = ;
int desbidx = ;
int srccidx = ;
int descidx = ;
struct edmacc_param param_set; printk("Initializing dma transfer...\n"); // set dest memory
fpga_buf = dma_alloc_coherent (NULL, MAX_DMA_TRANSFER_IN_BYTES, &dmaphysdest, );
if (!fpga_buf) {
printk ("dma_alloc_coherent failed for physdest\n");
return -ENOMEM;
} /* Set B count reload as B count. */
BRCnt = bcnt; /* Setting up the SRC/DES Index */
srcbidx = ;
desbidx = acnt; /* A Sync Transfer Mode */
srccidx = ;
descidx = acnt; // gpmc channel
result = edma_alloc_channel (, callback1, NULL, ); if (result < ) {
printk ("edma_alloc_channel failed, error:%d", result);
return result;
} dma_ch = result;
edma_set_src (dma_ch, (unsigned long)(gpmc_membase), INCR, W16BIT);
edma_set_dest (dma_ch, (unsigned long)(dmaphysdest), INCR, W16BIT);
edma_set_src_index (dma_ch, srcbidx, srccidx); // use fifo, set zero
edma_set_dest_index (dma_ch, desbidx, descidx); // A mode // A Sync Transfer Mode
edma_set_transfer_params (dma_ch, acnt, bcnt, ccnt, BRCnt, ASYNC); /* Enable the Interrupts on Channel 1 */
edma_read_slot (dma_ch, ¶m_set);
param_set.opt |= ( << ITCINTEN_SHIFT);
param_set.opt |= ( << TCINTEN_SHIFT);
param_set.opt |= EDMA_TCC(EDMA_CHAN_SLOT(dma_ch));
edma_write_slot (dma_ch, ¶m_set); return ;
} static int __init fpga_perh_init(void)
{
unsigned int cnt;
u32 val = ;
int ret = ;
int chk = ; gpio_store(); // GPIO初始化
gpio_config();
gpmc_config(); // GPMC配置
edma_config(); // EDMA配置 for(cnt=; cnt<; cnt++){
val = gpmc_cs_read_reg(GPMC_FPGA_CS, GPMC_CS_CONFIG1 + cnt*0x04);
printk("GPMC_CS3_CONFIG_%d : [%08X]\n", cnt+, val);
} printk("Gpmc now start reading...\n"); FPGA_RRST_L;
_delay_ns(); // 1us
FPGA_RRST_H; ret = edma_start(dma_ch); if (ret != ) {
printk ("dm8168_start_dma failed, error:%d", ret);
return ret;
} // wait for completion ISR
while(irqraised1 == 0u){
_delay_ms();
// break;
} if (ret == ) {
for (cnt=; cnt<FPGA_FIFO_SIZE; cnt++) {
// fpga_buf[cnt] = readw(fpga_membase);
if (fpga_buf[cnt] != cnt+) { // 进行数据校验
chk = cnt+;
break;
}
}
edma_stop(dma_ch);
edma_free_channel(dma_ch);
} if (chk == ){
printk ("Gpmc&edma reading sequence data check successful!\n");
}else{
printk ("Gpmc&edma reading data check error at: %d\n", chk);
} for(cnt=; cnt<; cnt++){
printk("[%04X] [%04X] [%04X] [%04X]\n", fpga_buf[cnt*], fpga_buf[cnt*+], fpga_buf[cnt*+], fpga_buf[cnt*+]);
} // gpmc_cs_free(GPMC_FPGA_CS);
return ;
}
module_init(fpga_perh_init); static void __exit fpga_perh_exit(void)
{
gpio_recover();
// free CS3
gpmc_cs_free(GPMC_FPGA_CS);
dma_free_coherent (NULL, MAX_DMA_TRANSFER_IN_BYTES, fpga_buf, dmaphysdest);
printk("fpga_perh exit!\n");
}
module_exit(fpga_perh_exit); MODULE_LICENSE("GPL");
4、实验结果
1.代码编译后通过insmod加载驱动,抓取CS3和OEN的波形如下,刚开始设计时没有用到EDMA传送,只是在linux循环读取,可以看见每个周期里片选信号CS3都会维持很长一段高电平的时间,GPMC一次的读取周期大概为250ns。

通道1为片选信号CS3,通道2为输出使能信号OEN
这样的速率大概只有 16bit / 250ns = 8MBytes/s
2.使用EDMA传送,这下读周期就小了很多了,只有57.6ns,和GPMC参数里设置的几乎一致。

3.传送8KBytes即4096次,大概用了235us,速率为 8KBytes / 235us = 34MB/s

4.fpga端使用signaltap抓取波形如下,可以看见GPMC_DATA为从1开始的自加顺序序列

5.Linux端读取数据并做校验,还打印出了GPMC的7个寄存器的内容。校验通过,说明数据一致性正确!至此DSP与FPGA通过GPMC接口用EDMA实现数据高速传输,验证可行!

总结,FPGA端代码比较简单就不上传了,如有需要欢迎交流。
DM8168这款DSP芯片,本人刚接手开发也就两个月,文中若有不对之处欢迎指出。
参考资料:
http://blog.csdn.net/hailin0716/article/details/26553389
http://blog.chinaunix.net/uid-28818752-id-3655729.html
http://blog.chinaunix.net/uid-28818752-id-3749701.html
http://blog.chinaunix.net/uid-28818752-id-3750016.html
联系本人:
hihuanglong艾特foxmail.com
有任何问题,欢迎加入 TI DSP 技术交流 QQ 群:652563558
DM8168通过GPMC接口与FPGA高速数据通信实现的更多相关文章
- AM5728通过GPMC接口与FPGA高速数据通信实现
硬件:AM5728开发板:Artix-7开发板软件:Linux am57xx-evm 4.4.19:Vivado 2015.2作者:杭州矢志信息科技有限公司邮箱:admin@sysjoint.com ...
- CC1605&CC1604 usb3.0+FPGA 高速视频采集 双目相机测评
CC1605&CC1604 usb3.0+FPGA 高速视频采集 双目相机测评 摄像头配置:ov5640.OV5642.mt9p031.mt9m001c12stm OV5640 xclk:24 ...
- EMIF接口与FPGA的互联(转)
reference: https://blog.csdn.net/ruby97/article/details/7539151 DSP6455的EMIFA模块 之前介绍了DSP6455的GPIO和中断 ...
- FPGA高速ADC接口实战——250MSPS采样率ADC9481
一.前言 最近忙于硕士毕业设计和论文,没有太多时间编写博客,现总结下之前在某个项目中用到的一个高速ADC接口设计部分.ADC这一器件经常用于无线通信.传感.测试测量等领域.目前数字系统对高速数据采集的 ...
- 基于PXIe接口的CoaXpress高速相机图像采集、回放
PXIe简介 PCI eXtensions for Instrumentation or PXI is a computer-based hardware and software platform ...
- VC709E 基于FMC接口的FPGA XC7VX690T PCIeX8 接口卡
一.板卡概述 本板卡基于Xilinx公司的FPGA XC7VX690T-FFG1761 芯片,支持PCIeX8.两组 64bit DDR3容量8GByte,HPC的FMC连接器,板卡支持各种FMC子卡 ...
- Xilinx FPGA高速串行收发器简介
1 Overview 与传统的并行实现方法相比,基于串行I/O的设计具有很多优势,包括:器件引脚数较少.降低了板空间要求.印刷电路板(PCB)层数较少.可以轻松实现PCB设计.连接器较小.电磁干扰降低 ...
- spi master接口的fpga实现
前言 当你器件的引脚贼少的时候,需要主机和从机通信,spi就派上了用场,它可以一对多,但只是片选到的从机能和主机通信,其他的挂机. spi:serial peripheral interface 串行 ...
- spi slaver接口的fpga实现
前言 spi从机接口程序,数据位8bit,sck空闲时低电平,工作时第一个沿数据传输.只有一个从机,cs低电平片选,slaver开始工作. 流程: 接口定义: 编码实现:(版权所有,请勿用于商业用途, ...
随机推荐
- 移动端Vue回到顶部
html: <div class="totop" id="totop" @click="Top" v-show="totop ...
- 自己理解的数据库shcema
不懂就被人嘲笑呀 ,你还不知道怎么说. 从定义中我们可以看出schema为数据库对象的集合,为了区分各个集合,我们需要给这个集合起个名字,这些名字就是我们在企业管理器的方案下看到的许多类似用户名的节点 ...
- IE浏览器关于ajax的缓存机制
IE浏览器对于同一个URL只返回相同结果.因为,在默认情况下,IE会缓存ajax的请求结果.对于同一个URL地址,在缓存过期之前,只有第一次请求会真正发送到服务端.大多数情况下,我们使用ajax是希望 ...
- jwplayer 参数记录
jwplayer().getPosition()://播放了多少秒 jwplayer('playerdiv').play(); || jwplayer(0).play(true/false); // ...
- Set linux mq_queue size for user
设置调整mq_queue的size*num如果大于默认(POSIX message queues),则需要调整系统限制和用户限制,不然在mq_open是会报"Too many open fi ...
- [HAOI2010]软件安装(树形背包,tarjan缩点)
题目描述 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大). 但是 ...
- poj_3256_Cow Picnic
The cows are having a picnic! Each of Farmer John's K (1 ≤ K ≤ 100) cows is grazing in one of N (1 ≤ ...
- 使用Python对Csv文件操作
csv是Comma-Separated Values的缩写,是用文本文件形式储存的表格数据,比如如下的表格: 就可以存储为csv文件,文件内容是: No.,Name,Age,Score 1,mayi, ...
- linux系统基础之---RPM管理(基于centos7.4)
- phpstudy apache启动失败,80端口占用问题解决方案
安装phpstydy,启动apache时,启动失败,提示80端口占用,需要将占用80端口的服务进程关闭 1.运行cmd, netstat -ano 找到80端口对应的pid 4 2.一般都是调用 h ...