DSP

FPGA+DSP SRIO通信(四)——中断系统(二)

2019-07-13 11:53发布

“It doesn’t matter where you are, you are nowhere compared to where you can go.”
经过上一篇博客FPGA+DSP SRIO通信(四)——中断系统(一)的学习,我们已经可以将FPGA端产生的doorbell中断变成INTDST,也就是系统中断,接下来要做的就是使系统中断主机中断 相互关联起来,再将主机中断和CPU中断映射起来,最终编写程序以使CPU知道当某个主机中断发生时,应该做出什么操作。

DSP所做的行为只与CPU中断直接联系。
目录

文章目录


(本文的基础是FPGA能向DSP发送doorbell类型的包。) 上图所示为完整的FPGA到DSP的doorbell映射示意图,本文要实现的是上图右边红 {MOD}箭头所指的蓝 {MOD}框内的内容。
那么如何配置cpintc/CIC(CorePac Interrupt Controller)和Intc(Interrupt Controller),以生成主机中断呢?往下看。 ##1、配置方法分类
笔者所接触的配置方法有两种,这两种方法的区别在于你的项目要使用sysbios程序还是裸机程序。
两种方法没有孰优孰劣,只是在代码实现方面,sysbios版本显得更加精简优雅。 在实现具体代码配置之前,我们先明确几个数字概念。 【系统事件号】
6678数据手册中的 Table7-38展示了6678的所有系统事件映射,也是一级中断映射,一级中断一共有128个,这128个中断可以被中断选择器选择,映射给12个CPU中断。笔者选择62号和63号系统事件,在表中的展示如下图:
这里写图片描述 【系统中断号】 ##2、6678裸机中断配置 笔者所参照的6678裸机中断配置例程所在位置为C: ipdk_C6678_1_1_2_6packages icslexamplecpintccpintc_test.c,我会在文末将该例程共享出来供大家学习。该官方示例程序大家可以在读完本文后仔细阅读,会对6678的中断系统有更加详细的了解。 通用中断系统 上图为一般中断系统的运行原理,DSP在执行指令时,如果遇到中断,会优先执行中断,而中断所要执行的内容就是ISR(中断服务函数)中的内容。如果同时接收到多个中断,会优先执行优先级高的中断。 根据以上说明,我们首先得编写一个中断服务函数,在DSP收到中断后,执行相应的功能,这里以打印出一串字符串为例。 CSL_CPINTC_Handle hnd;// 中断配置句柄 static void print_isr_handler (void* handle) { // 触发中断后编程者自定义的功能 printf("receive a interrupt from SRIO "); // 使主机中断失效 CSL_CPINTC_disableHostInterrupt (hnd, 2); // 清除系统中断 CSL_CPINTC_clearSysInterrupt (hnd,116); // 使能主机中断 CSL_CPINTC_enableHostInterrupt (hnd, 2); // 获得正在等待的doorbell中断状态 CSL_SRIO_GetDoorbellPendingInterrupt (hSrio, 0, &doorbellStatus); // 如果有doorbell中断正在等待,则清除正在等待的doorbell中断,以等待进入下一次中断 CSL_SRIO_ClearDoorbellPendingInterrupt (hSrio, 0, doorbellStatus); } 下面一一解决上面这段代码中的疑问。
###2.1 CSL_CPINTC_disableHostInterrupt ()是从哪里来的?
这种类似函数的使用需要在程序中包含相关的头文件,具体需要在实现中断配置的.c文件中加入以下包含代码: #include #include ###2.2 CSL_CPINTC_disableHostInterrupt (hnd, 2)中的2是什么意思?
该函数中的2为主机中断号,一般使用4-15号主机中断。 ###2.3 CSL_CPINTC_clearSysInterrupt (hnd,116)中的116是什么意思?
该函数中的116为CIC0(cpintc0)的116号输入,由于笔者配置的是SRIO doorbell中断,通过6678用户手册的表7-39可以看到,CIC0的116号输入是和INDST4系统中断相关联的,而INTDST通过FPGA+DSP SRIO通信(四)——中断系统(一)已经配置得到。 通过以上步骤我们已经了解了中断服务函数的组成,那么如何通过程序,编程使得下图右侧蓝 {MOD}部分的关联功能实现呢?
下面这个函数体内实现了INTDST->cpintc->intc->CPU中断 的映射配置。具体说明见函数中的注释。
笔者这里配置了2个中断源,即FPGA端向DSP发送doorbell包,doorbell_info为4或5,则会触发相应的CPU中断。上文只展示了2个中断服务函数其中1个,另一个copy前一个中断服务函数,修改主机中断号和系统中断号即可。
先上干货: /* Intc variable declarartion */ CSL_CPINTC_Handle hnd; CSL_IntcContext intcContext; CSL_IntcEventHandlerRecord EventHandler[30]; CSL_IntcObj intcObj_read; CSL_IntcObj intcObj_write; CSL_IntcHandle read_handle; CSL_IntcHandle write_handle; CSL_IntcGlobalEnableState state; CSL_IntcEventHandlerRecord EventRecord; CSL_IntcParam vectId_read; CSL_IntcParam vectId_write; int DoorbellIntr_Init (void) { Uint32 rawStatus; Uint16 index; /************************************************ *************** INTC Configuration ************* ************************************************/ /* INTC module initialization */ intcContext.eventhandlerRecord = EventHandler;//save eventhandler intcContext.numEvtEntries = 1;// 该变量和事件组合触发有关,如果是单事件触发,设置为1即可 if (CSL_intcInit(&intcContext) != CSL_SOK) { return -1; } /* Enable NMIs */ if (CSL_intcGlobalNmiEnable() != CSL_SOK) { return -1; } /* Enable global interrupts */ if (CSL_intcGlobalEnable(&state) != CSL_SOK) { return -1; } /* 为CPU中断4、5,事件ID 63、64打开INTC模块 */ /* CPU中断前面已经说过,INTC模块的事件ID在6678用户手册的table7-38中 * 本文的第一张图片显示了笔者要使用的事件ID */ vectId_read = CSL_INTC_VECTID_4; vectId_write = CSL_INTC_VECTID_5; read_handle = CSL_intcOpen (&intcObj_read, 62, &vectId_read , NULL); write_handle = CSL_intcOpen (&intcObj_write, 63, &vectId_write , NULL); /* 这里只检查了一个中断关联的成功性,如果不放心可以两个都检查 */ if (read_handle == NULL) { return -1; } if (write_handle == NULL) { return -1; } /* 这里将中断服务函数和中断源联系起来 */ EventHandler[0].handler = &read_isr_handler; EventHandler[0].arg = 0; EventHandler[1].handler = &write_isr_handler; EventHandler[1].arg = 0; if (CSL_intcPlugEventHandler(read_handle,&EventHandler[0]) != CSL_SOK) { return -1; } if (CSL_intcPlugEventHandler(write_handle,&EventHandler[1]) != CSL_SOK) { return -1; } /* 使能刚才配置的两种中断通路 */ /* Enabling the read events. */ if (CSL_intcHwControl(read_handle,CSL_INTC_CMD_EVTENABLE, NULL) != CSL_SOK) { return -1; } /* Enabling the write events. */ if (CSL_intcHwControl(write_handle,CSL_INTC_CMD_EVTENABLE, NULL) != CSL_SOK) { return -1; } /************************************************** ************* CPINTC-0 Configuration ************* **************************************************/ /* * CPINTC(有的文档中称为CIC) ,这里说的CPINTC-0就是6678用户手册table7-39中描述的CIC0。 * 将 INTDST4(事件号116)和INTDST5(事件号117)映射到主机中断2和主机中断3上 */ /* Open the handle to the CPINT Instance */ hnd = CSL_CPINTC_open(0); if (hnd == 0) { return -1; } /* Disable all host interrupts. */ CSL_CPINTC_disableAllHostInterrupt(hnd); /* Configure no nesting support in the CPINTC Module. */ CSL_CPINTC_setNestingMode (hnd, CPINTC_NO_NESTING); /* clear system interrupt INTDST4(116) */ CSL_CPINTC_clearSysInterrupt (hnd,116); CSL_CPINTC_clearSysInterrupt (hnd,117); /* We now enable system interrupt INTDST4(116) */ CSL_CPINTC_enableSysInterrupt (hnd, 116); CSL_CPINTC_enableSysInterrupt (hnd, 117); /* We now map System Interrupt INTDST4 to channel 2 */ CSL_CPINTC_mapSystemIntrToChannel (hnd, 116 , 2);//test if this line not exist CSL_CPINTC_mapSystemIntrToChannel (hnd, 117 , 3); /* We enable host interrupts. */ CSL_CPINTC_enableHostInterrupt (hnd, 2); CSL_CPINTC_enableHostInterrupt (hnd, 3); /* Enable all host interrupts also. */ CSL_CPINTC_enableAllHostInterrupt(hnd); return 0; } 现在就可以愉快的测试了,在两个中断服务函数中添加断点,用FPGA向DSP通过SRIO发送doorbell包,doorbell_info写4或5,就可以看到DSP停到相应的中断服务函数中啦。 ##3、6678 sysbios中断配置 sysbios的中断配置与逻辑中断配置相比,看起来更加简洁,但其实底层的步骤并没有一丝减少,只是TI减少了在配置每一步时所使用的C代码量而已。 千万不要以为sysbios减少了你这个工程师的工作量,因为你会认识到一个全新的知识架构,这将耗费你巨大的精力,而中断配置只涉及这个架构的冰山一角,我尽量简洁一点说明。 首先,FPGA+DSP SRIO通信(四)——中断系统(一)中做的工作你还是得做,因为外设中断源的配置和CPU的中断是独立开来的,不管你用得是sysbios中断配置方式还是裸机配置方式。接下来我们来看具体步骤。 ###3.1 中断服务函数(ISR)
中断服务函数是中断配置中必不可少的一环,下面给出sysbios版的中断服务函数,和裸机(CSL)版的中断服务函数类似,照例,笔者也只给出一个: /* 在该中断服务函数(ISR)中,笔者使用6号主机中断和CIC0的116号事件(系统事件) */ void interrupt_ISR() { printf("进入中断服务函数 "); CpIntc_disableHostInt(0,6); CpIntc_clearSysInt(0,116); CpIntc_enableHostInt(0,6); CSL_SRIO_GetDoorbellPendingInterrupt (hSrio, 0, &doorbellStatus); CSL_SRIO_ClearDoorbellPendingInterrupt (hSrio, 0, doorbellStatus); } 看的仔细的朋友会发现在裸机ISR中,笔者使用了CSL_CPINTC_disableHostInterrupt ();,而sysbios的ISR中,笔者使用的是CpIntc_disableHostInt(),两者底层实现是相同的,但除了函数形式的不同,还有其所在头文件的不同,如果不能正确包含头文件及其路径,会报找不到函数这种低级错误。 【DSP编译小窍门 选读部分
如果编译之后报了类似于couldn’t find xxx.h 或 undefined sysmbols之类的错误,首先要检查你的工程属性->Build->C6000 Compiler->Include Options中有没有缺少xxx.h或未定义symbols的路径,如果有,就添加上,例如我的include options中就有以下路径:
“${CG_TOOL_ROOT}/include”
“C: ipdk_C6678_1_1_2_6packages iplatform”
“C: ipdk_C6678_1_1_2_6packages icsl”
“C: ipdk_C6678_1_1_2_6/packages/ti/drv/qmss”
“C: ipdk_C6678_1_1_2_6/packages/ti/drv/cppi”
如果你使用的是官方封装好的lib,例如,如果你使用的是csl封好的库,其路径应该为C: ipdk_C6678_1_1_2_6packages icsllib,其名称应该为ti.csl.ae66,那你还需要查看工程属性->Build->C6000 Linker->File Search Path中的上半部分是否添加了ti.csl.ae66,下半部分是否添加了C: ipdk_C6678_1_1_2_6packages icsllib。 如果你使用的是裸机工程,经过上面两步还没有使你remove这两种报错,你就需要详细检查你的代码了。
如果你使用的是sysbios工程,经过上面两步还没有使你remove这两种报错,还有一招可以救工程于水火。例如,如果你使用了类似CpIntc_disableHostInt()的函数,那么它所在的库是在sysbios路径下的,打开你工程目录中的**.cfg**文件,将var cpIntc = xdc.useModule(‘ti.sysbios.family.c66.tci66xx.CpIntc’);添加进去即可。

3.2 CIC+INTC配置

sysbios的这部分实现就显得简洁了许多,笔者注释掉了2个中断映射配置中的1个,具体说明见代码注释: void interrupt_init() { Int eventID_rd; //Int eventID_wr; Hwi_Params params_rd; //Hwi_Params params_wr; //--------中断配置-------------- /* * Map the System Interrupt i.e. the Interrupt Destination 4 interrupt to the DIO ISR Handler. * Number 116 is the system interrupt */ /* 该函数将中断服务函数与系统事件相映射 */ CpIntc_dispatchPlug(116, (CpIntc_FuncPtr)interrupt_ISR, 116, TRUE); // CpIntc_dispatchPlug(117, (CpIntc_FuncPtr)write_ISR, 117, TRUE); /* * The configuration is for CPINTC0. We map system interrupt 116 to Host Interrupt 6. * Number 6 is the Host interrupt */ /* 该函数将系统事件与主机中断相映射 */ CpIntc_mapSysIntToHostInt(0, 116, 6); // CpIntc_mapSysIntToHostInt(0, 117, 6); /* Enable the Host Interrupt. */ CpIntc_enableHostInt(0, 6); // CpIntc_enableHostInt(0, 6); /* Enable the System Interrupt */ CpIntc_enableSysInt(0, 116); // CpIntc_enableSysInt(0, 117); /* Get the event id associated with the host interrupt. */ eventID_rd = CpIntc_getEventId(6); // eventID_wr = CpIntc_getEventId(6); Hwi_Params_init(¶ms_rd); // Hwi_Params_init(¶ms_wr); /* Host interrupt value*/ params_rd.arg = 6; // params_oneFrameCom.arg = 6; /* Event id for your host interrupt */ params_rd.eventId = eventID_rd; // params_wr.eventId = eventID_wr; /* Enable the Hwi */ params_rd.enableInt = TRUE; // params_wr.enableInt = TRUE; /* This plugs the interrupt vector 4 and the ISR function. */ /* When using CpIntc, you must plug the Hwi fxn with CpIntc_dispatch */ /* so it knows how to process the CpIntc interrupts.*/ /* number 4 and 5 is the interrupt vector .user can use Vector4-15*/ /* 该函数前面的配置和CPU中断相映射 */ Hwi_create(4, &CpIntc_dispatch, ¶ms_rd, NULL); // Hwi_create(5, &CpIntc_dispatch, ¶ms_wr, NULL); } 大功告成,在中断服务函数中添加断点,用FPGA向DSP通过SRIO发送doorbell包,doorbell_info写4,就可以看到DSP停到中断服务函数中啦。
6678的中断配置系统到这里就告一段落了,虽然从头到尾都在讲如何配置SRIO触发的中断,但也可以使用这个套路去配置其它外设触发的中断,这里就不详细介绍了,记得遵循**“从哪里来,经过什么,到哪里去”**这个原则,利用好外设文档,CIC,INTC,那么什么中断都能配置好的。
参考文献及文档
[1]. 6678裸机中断配置例程
[2]. SRIO官方文档
[3]. 6678用户手册
[4]. 6678中断配置官方中文文档
6678的SRIO模块相关博文到这里也就全部结束了,笔者看了一下从2016年9月4日开始写第一篇SRIO相关博客,到今天一共经历了将近2年时间,SRIO对我来说不仅仅是一个技术知识,更是一种学习态度和方法的探索过程。 对于做很多事情都半途而废的笔者来说,真的是一个巨大的achievement,想谢谢几个人。
第一个人是北航的王悦人学长,如果不是他当初给了我一个SRIO的程序,让我初窥DSP开发的门径,也就没有这一系列博客了。
第二个人是北理工的倪俊学长,一个人的精神信念永远是指引行为的不竭动力。但行好事,不问前程。
还有世界上的第一代开源人,开源几乎推动了所有信息相关行业的快速发展。 敢为天下先!
接下来博客系列是:6678千兆网的相关配置。