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 = 0,
// CONFIG2 chip-select time
.cs_on = 0, /* Assertion time */
.cs_rd_off = 50, /* Read deassertion time */
.cs_wr_off = 50, /* Write deassertion time */
// CONFIG3
.adv_on = 0,
.adv_rd_off = 0,
.adv_wr_off = 0,
// CONFIG4
.we_on = 20, /* WE assertion time */
.we_off = 20, /* WE deassertion time */
// CONFIG4
.oe_on = 20, /* OE assertion time */
.oe_off = 20, /* OE deassertion time */
// CONFIG5
.page_burst_access = 0,
.access = 30, /* Start-cycle to first data valid delay */
.rd_cycle = 50, /* Total read cycle time */
.wr_cycle = 50, /* Total write cycle time */
// CONFIG6
.wr_access = 0,
.wr_data_mux_bus = 0,
};
static int gpmc_config(void)
{
// first reg gpmc_init() already called; io pinmux already configed
// ti8168evm board_nand_init -> gpmc_nand_init
u32 val = 0;
int err = 0;
/*-
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, 0);
gpmc_cs_configure(GPMC_FPGA_CS, GPMC_ENABLE_IRQ, 0);
// 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 < 0){
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 < 0){
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;
}
下面是总的代码,折叠了。
1 /*
2 * fileName: fpga_perh.c
3 * for gpmc fpga communication
4 */
5 #include <linux/device.h>
6 #include <linux/fs.h>
7 #include <linux/module.h>
8 #include <linux/kernel.h>
9 #include <linux/init.h>
10 #include <linux/moduleparam.h>
11 #include <linux/list.h>
12 #include <linux/cdev.h>
13 #include <linux/proc_fs.h>
14 #include <linux/mm.h>
15 #include <linux/seq_file.h>
16 #include <linux/ioport.h>
17 #include <linux/delay.h>
18 #include <asm/io.h>
19 #include <linux/io.h>
20 #include <mach/gpio.h>
21 #include <linux/device.h>
22 #include <linux/platform_device.h>
23 // gpmc spec
24 #include <plat/gpmc.h>
25 // edma spec
26 #include <linux/interrupt.h>
27 #include <linux/dma-mapping.h>
28 #include <mach/memory.h>
29 #include <mach/hardware.h>
30 #include <mach/irqs.h>
31 #include <asm/hardware/edma.h>
32
33 #define DRIVERNAME "fpga"
34
35 // once_dma = 8KByte = 4*1024*16bit
36 #define FPGA_FIFO_SIZE SZ_4K
37
38 /*------------------------------------------------------------------------------------------------------------------*/
39 // GPMC Config
40 #define GPMC_FIFO_SIZE SZ_16K
41
42 #define GPMC_FPGA_CS 3 // gpmc use CS 3
43 #define GPMC_CONFIG1_3 0x00001010 // 16bit size NOR FLASH like
44 #define GPMC_CONFIG2_3 0x00101080 // CSWROFFTIME=16cy CSRDOFFTIME=16cy CSEXTRADELAY=1
45 #define GPMC_CONFIG3_3 0x00000000 // ADV TIME used
46 #define GPMC_CONFIG4_3 0x0F031003 // WEOFFTIME=15cy WEONTIME=3cy OEOFFTIME=16cy OEONTIME=3cy
47 #define GPMC_CONFIG5_3 0x000F1111 // RDACCESSTIME=15cy WRCYCLETIME=1cy RDCYCLETIME=1cy
48 #define GPMC_CONFIG6_3 0x0F030000 // WRACCESSTIME=15cy WRDATAONADMUXBUS=3cy Add CYCLE2CYCLEDELAY
49 #define GPMC_CONFIG7_3 0x00000F42 // set up CONFIG7 and enable cs3
50 // chip-select mask address = MASKADDRESS = 16MB
51 // CS enabled
52 // Chip-select base address = BASEADDRESS = 0x02000000
53 // Access address 0x02000000-0x02FFFFFF
54 #define GPMC_MASKADDRESS 0x00FFFFFF // fifo_size
55 #define GPMC_BASEADDRESS 0x02000000 // gpmc address
56 /*------------------------------------------------------------------------------------------------------------------*/
57 // FPGA GPIOs
58 #define CTRL_MODULE_BASE_ADDR 0x48140000
59 #define conf_gpio18 (CTRL_MODULE_BASE_ADDR + 0x0B98)
60 #define conf_gpio19 (CTRL_MODULE_BASE_ADDR + 0x0B9C)
61
62 #define WR_MEM_32(addr, data) *(unsigned int*)OMAP2_L4_IO_ADDRESS(addr) = (unsigned int)(data)
63 #define RD_MEM_32(addr) *(unsigned int*)OMAP2_L4_IO_ADDRESS(addr)
64
65 // delay for reset
66 #define _delay_ms(n) mdelay(n)
67 #define _delay_ns(n) ndelay(n)
68
69 // Read Point Low is Reset
70 #define FPGA_RRST_H gpio_set_value(18, 1);
71 #define FPGA_RRST_L gpio_set_value(18, 0);
72 /*------------------------------------------------------------------------------------------------------------------*/
73 // EDMA Config
74 #define MAX_DMA_TRANSFER_IN_BYTES (4096*2)
75 #define STATIC_SHIFT 3
76 #define TCINTEN_SHIFT 20
77 #define ITCINTEN_SHIFT 21
78 #define TCCHEN_SHIFT 22
79 #define ITCCHEN_SHIFT 23
80 /*------------------------------------------------------------------------------------------------------------------*/
81 //unsigned int fpga_buf[FPGA_FIFO_SIZE] = {0};
82 static unsigned long gpmc_membase = 0;
83 static void __iomem *fpga_membase = 0;
84 static int gpio[2];
85 dma_addr_t dmaphysdest = 0;
86 unsigned short *fpga_buf = NULL;
87 unsigned int dma_ch = 0;
88 static volatile int irqraised1 = 0;
89
90 static struct gpmc_timings fpga_timings = {
91 /*- GPMC timing configurations -*/
92 .sync_clk = 0,
93 // CONFIG2 chip-select time
94 .cs_on = 0, /* Assertion time */
95 .cs_rd_off = 50, /* Read deassertion time */
96 .cs_wr_off = 50, /* Write deassertion time */
97 // CONFIG3
98 .adv_on = 0,
99 .adv_rd_off = 0,
100 .adv_wr_off = 0,
101 // CONFIG4
102 .we_on = 20, /* WE assertion time */
103 .we_off = 20, /* WE deassertion time */
104 // CONFIG4
105 .oe_on = 20, /* OE assertion time */
106 .oe_off = 20, /* OE deassertion time */
107 // CONFIG5
108 .page_burst_access = 0,
109 .access = 30, /* Start-cycle to first data valid delay */
110 .rd_cycle = 50, /* Total read cycle time */
111 .wr_cycle = 50, /* Total write cycle time */
112 // CONFIG6
113 .wr_access = 0,
114 .wr_data_mux_bus = 0,
115 };
116
117 // static dev_t dev;
118 // static struct cdev cdev;
119 // static struct class *gpmc_edma_class = NULL;
120
121 static void callback1(unsigned lch, u16 ch_status, void *data)
122 {
123 switch(ch_status) {
124 case DMA_COMPLETE:
125 irqraised1 = 1;
126 /*DMA_PRINTK ("\n From Callback 1: Channel %d status is: %u\n", lch, ch_status);*/
127 break;
128 case DMA_CC_ERROR:
129 irqraised1 = -1;
130 printk ("\nFrom Callback 1: DMA_CC_ERROR occured on Channel %d\n", lch);
131 break;
132 default:
133 break;
134 }
135 }
136
137 static int gpio_store(void);
138 static int gpio_recover(void);
139 static int gpio_config(void);
140 static int gpmc_config(void);
141 static int edma_config(void);
142
143 static int gpio_store(void)
144 {
145 // store gpio pinmux
146 gpio[0] = RD_MEM_32(conf_gpio18);
147 gpio[1] = RD_MEM_32(conf_gpio19);
148 return 0;
149 }
150
151 static int gpio_recover(void)
152 {
153 // recover gpio pinmux
154 WR_MEM_32(conf_gpio18, gpio[0]);
155 WR_MEM_32(conf_gpio19, gpio[1]);
156 gpio_free(gpio[0]);
157 gpio_free(gpio[1]);
158 return 0;
159 }
160
161 static int gpio_config(void)
162 {
163 // config gpio direction
164 WR_MEM_32(conf_gpio18, 1); // MUXMODE=001
165 gpio_request(18, "gpio18_en"); // request gpio46
166 gpio_direction_output(18, 1);
167
168 WR_MEM_32(conf_gpio19, 1); // MUXMODE=001
169 gpio_request(19, "gpio19_en"); // request gpio47
170 gpio_direction_output(19, 1);
171
172 return 0;
173 }
174
175 static int gpmc_config(void)
176 {
177 // first reg gpmc_init() already called; io pinmux already configed
178 // ti8168evm board_nand_init -> gpmc_nand_init
179 u32 val = 0;
180 int err = 0;
181 /*-
182 EXPORT_SYMBOL(gpmc_cs_write_reg);
183 EXPORT_SYMBOL(gpmc_cs_read_reg);
184 EXPORT_SYMBOL(gpmc_cs_set_timings);
185 -*/
186 // gpmc cs disable memory
187 val = gpmc_cs_read_reg(GPMC_FPGA_CS, GPMC_CS_CONFIG7);
188 val &= ~GPMC_CONFIG7_CSVALID;
189 gpmc_cs_write_reg(GPMC_FPGA_CS, GPMC_CS_CONFIG7, val);
190
191 // disable cs3 irq
192 gpmc_cs_configure(GPMC_FPGA_CS, GPMC_SET_IRQ_STATUS, 0);
193 gpmc_cs_configure(GPMC_FPGA_CS, GPMC_ENABLE_IRQ, 0);
194
195 // set config1
196 gpmc_cs_write_reg(GPMC_FPGA_CS, GPMC_CS_CONFIG1, GPMC_CONFIG1_READTYPE_ASYNC| // set read type async
197 GPMC_CONFIG1_WRITETYPE_ASYNC| // set write type async
198 GPMC_CONFIG1_DEVICESIZE_16| // set device size 16bit
199 GPMC_CONFIG1_DEVICETYPE_NOR // set device type nor
200 );
201 val = gpmc_cs_read_reg(GPMC_FPGA_CS, GPMC_CS_CONFIG1);
202 val &= ~GPMC_CONFIG1_MUXADDDATA;
203 gpmc_cs_write_reg(GPMC_FPGA_CS, GPMC_CS_CONFIG1, val);
204
205 // set gpmc timings
206 err = gpmc_cs_set_timings(GPMC_FPGA_CS, &fpga_timings);
207 if(err < 0){
208 printk(KERN_ERR "Unable to set gpmc timings\n");
209 }
210
211 // apply gpmc select memory
212 err = gpmc_cs_request(GPMC_FPGA_CS, GPMC_FIFO_SIZE, &gpmc_membase);
213 if(err < 0){
214 printk(KERN_ERR "Cannot request GPMC CS\n");
215 return err;
216 }
217
218 // request_mem_region(gpmc_membase, GPMC_FIFO_SIZE, DRIVERNAME);
219
220 // fpga_membase = ioremap(gpmc_membase, GPMC_FIFO_SIZE);
221
222 return err;
223 }
224
225 static int edma_config(void)
226 {
227 // use AB mode, one_dma = 8KB/16bit
228 static int acnt = 4096*2;
229 static int bcnt = 1;
230 static int ccnt = 1;
231
232 int result = 0;
233 unsigned int BRCnt = 0;
234 int srcbidx = 0;
235 int desbidx = 0;
236 int srccidx = 0;
237 int descidx = 0;
238 struct edmacc_param param_set;
239
240 printk("Initializing dma transfer...\n");
241
242 // set dest memory
243 fpga_buf = dma_alloc_coherent (NULL, MAX_DMA_TRANSFER_IN_BYTES, &dmaphysdest, 0);
244 if (!fpga_buf) {
245 printk ("dma_alloc_coherent failed for physdest\n");
246 return -ENOMEM;
247 }
248
249 /* Set B count reload as B count. */
250 BRCnt = bcnt;
251
252 /* Setting up the SRC/DES Index */
253 srcbidx = 0;
254 desbidx = acnt;
255
256 /* A Sync Transfer Mode */
257 srccidx = 0;
258 descidx = acnt;
259
260 // gpmc channel
261 result = edma_alloc_channel (52, callback1, NULL, 0);
262
263 if (result < 0) {
264 printk ("edma_alloc_channel failed, error:%d", result);
265 return result;
266 }
267
268 dma_ch = result;
269 edma_set_src (dma_ch, (unsigned long)(gpmc_membase), INCR, W16BIT);
270 edma_set_dest (dma_ch, (unsigned long)(dmaphysdest), INCR, W16BIT);
271 edma_set_src_index (dma_ch, srcbidx, srccidx); // use fifo, set zero
272 edma_set_dest_index (dma_ch, desbidx, descidx); // A mode
273
274 // A Sync Transfer Mode
275 edma_set_transfer_params (dma_ch, acnt, bcnt, ccnt, BRCnt, ASYNC);
276
277 /* Enable the Interrupts on Channel 1 */
278 edma_read_slot (dma_ch, ¶m_set);
279 param_set.opt |= (1 << ITCINTEN_SHIFT);
280 param_set.opt |= (1 << TCINTEN_SHIFT);
281 param_set.opt |= EDMA_TCC(EDMA_CHAN_SLOT(dma_ch));
282 edma_write_slot (dma_ch, ¶m_set);
283
284 return 0;
285 }
286
287 static int __init fpga_perh_init(void)
288 {
289 unsigned int cnt;
290 u32 val = 0;
291 int ret = 0;
292 int chk = 0;
293
294 gpio_store(); // GPIO初始化
295 gpio_config();
296 gpmc_config(); // GPMC配置
297 edma_config(); // EDMA配置
298
299 for(cnt=0; cnt<7; cnt++){
300 val = gpmc_cs_read_reg(GPMC_FPGA_CS, GPMC_CS_CONFIG1 + cnt*0x04);
301 printk("GPMC_CS3_CONFIG_%d : [%08X]\n", cnt+1, val);
302 }
303
304 printk("Gpmc now start reading...\n");
305
306 FPGA_RRST_L;
307 _delay_ns(1); // 1us
308 FPGA_RRST_H;
309
310
311 ret = edma_start(dma_ch);
312
313 if (ret != 0) {
314 printk ("dm8168_start_dma failed, error:%d", ret);
315 return ret;
316 }
317
318 // wait for completion ISR
319 while(irqraised1 == 0u){
320 _delay_ms(10);
321 // break;
322 }
323
324
325 if (ret == 0) {
326 for (cnt=0; cnt<FPGA_FIFO_SIZE; cnt++) {
327 // fpga_buf[cnt] = readw(fpga_membase);
328 if (fpga_buf[cnt] != cnt+1) { // 进行数据校验
329 chk = cnt+1;
330 break;
331 }
332 }
333 edma_stop(dma_ch);
334 edma_free_channel(dma_ch);
335 }
336
337 if (chk == 0){
338 printk ("Gpmc&edma reading sequence data check successful!\n");
339 }else{
340 printk ("Gpmc&edma reading data check error at: %d\n", chk);
341 }
342
343 for(cnt=0; cnt<8; cnt++){
344 printk("[%04X] [%04X] [%04X] [%04X]\n", fpga_buf[cnt*4], fpga_buf[cnt*4+1], fpga_buf[cnt*4+2], fpga_buf[cnt*4+3]);
345 }
346
347 // gpmc_cs_free(GPMC_FPGA_CS);
348 return 0;
349 }
350 module_init(fpga_perh_init);
351
352 static void __exit fpga_perh_exit(void)
353 {
354 gpio_recover();
355 // free CS3
356 gpmc_cs_free(GPMC_FPGA_CS);
357 dma_free_coherent (NULL, MAX_DMA_TRANSFER_IN_BYTES, fpga_buf, dmaphysdest);
358 printk("fpga_perh exit!\n");
359 }
360 module_exit(fpga_perh_exit);
361
362 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