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

“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中断直接联系。

目录

文章目录

      • 3.2 CIC+INTC配置

(本文的基础是FPGA能向DSP发送doorbell类型的包。)

FPGA+DSP SRIO通信(四)——中断系统(二)_第1张图片

上图所示为完整的FPGA到DSP的doorbell映射示意图,本文要实现的是上图右边红色箭头所指的蓝色框内的内容。
那么如何配置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:\ti\pdk_C6678_1_1_2_6\packages\ti\csl\example\cpintc\cpintc_test.c,我会在文末将该例程共享出来供大家学习。该官方示例程序大家可以在读完本文后仔细阅读,会对6678的中断系统有更加详细的了解。

FPGA+DSP SRIO通信(四)——中断系统(二)_第2张图片

上图为一般中断系统的运行原理,DSP在执行指令时,如果遇到中断,会优先执行中断,而中断所要执行的内容就是ISR(中断服务函数)中的内容。如果同时接收到多个中断,会优先执行优先级高的中断。

根据以上说明,我们首先得编写一个中断服务函数,在DSP收到中断后,执行相应的功能,这里以打印出一串字符串为例。

CSL_CPINTC_Handle           hnd;// 中断配置句柄
static void print_isr_handler (void* handle)
{
	// 触发中断后编程者自定义的功能
	printf("receive a interrupt from SRIO\n");
	// 使主机中断失效
	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通信(四)——中断系统(一)已经配置得到。

通过以上步骤我们已经了解了中断服务函数的组成,那么如何通过程序,编程使得下图右侧蓝色部分的关联功能实现呢?
FPGA+DSP SRIO通信(四)——中断系统(二)_第3张图片

下面这个函数体内实现了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("进入中断服务函数\n");
	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:\ti\pdk_C6678_1_1_2_6\packages\ti\platform”
“C:\ti\pdk_C6678_1_1_2_6\packages\ti\csl”
“C:\ti\pdk_C6678_1_1_2_6/packages/ti/drv/qmss”
“C:\ti\pdk_C6678_1_1_2_6/packages/ti/drv/cppi”

如果你使用的是官方封装好的lib,例如,如果你使用的是csl封好的库,其路径应该为C:\ti\pdk_C6678_1_1_2_6\packages\ti\csl\lib,其名称应该为ti.csl.ae66,那你还需要查看工程属性->Build->C6000 Linker->File Search Path中的上半部分是否添加了ti.csl.ae66,下半部分是否添加了C:\ti\pdk_C6678_1_1_2_6\packages\ti\csl\lib。

如果你使用的是裸机工程,经过上面两步还没有使你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千兆网的相关配置。

你可能感兴趣的:(SRIO学习,DSP6678,SRIO调试教程)