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开始工作. 流程: 接口定义: 编码实现:(版权所有,请勿用于商业用途, ...
随机推荐
- sudo cd的错误
问题说明 今天用MySQL建了库,想看看. 当到了这步,心里的第一个感觉就是电脑坏了.后来查了查才知道了原因. 原因 cd不是一个应用程序而是Linux内建的命令,而sudo仅仅只对应用程序起作用. ...
- React Router 4 的使用(2)
Route Rendering Props 对于给定的路由如何渲染组件,有三种选项:component.render.children.你可以查看 <Route> 的文档来获取更多的信息, ...
- Sass 基础(七)
Sass Maps 的函数-map-remove($map,$key),keywords($args) map-remove($map,$key) map-remove($map,$key)函数是用来 ...
- 使用Vscode写python
在python官网下载好python2.x 或者 3.x, 然后在vscode 下载python插件. 写一个python程序, 运行, vscode会自动提示你配置python执行路径,并帮你创建好 ...
- Java分享笔记:自定义枚举类 & 使用enum关键字定义枚举类
在JDK1.5之前没有enum关键字,如果想使用枚举类,程序员需要根据Java语言的规则自行设计.从JDK1.5开始,Java语言添加了enum关键字,可以通过该关键字方便地定义枚举类.这种枚举类有自 ...
- substr在oracle和mysql中的应用和区别
Oracle: 书写格式: (1)Select substr(字段名(string) , 起始位置(int) , 截取长度(int)) 示例: selectsubstr('123456',0,3)a ...
- Mac系统升级后在终端输入git命令时遇到的问题
Mac系统升级git会找不到并且报错:xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools) ...
- My First Marathon【我的第一次马拉松】
My First Marathon A month before my first matathon, one of my ankles was injured and this meant not ...
- (数据科学学习手册28)SQL server 2012中的查询语句汇总
一.简介 数据库管理系统(DBMS)最重要的功能就是提供数据查询,即用户根据实际需求对数据进行筛选,并以特定形式进行显示.在Microsoft SQL Serve 2012 中,可以使用通用的SELE ...
- 如何打war包
1. 利用jdk里的工具 例如我们要打包的文件在D:\myHome\dist: 运行 cmd: cd D:\myHome\dist 进入D:\myHome\dist 然后输入 D:\myHome\di ...