本博客基于创龙“678ZH产品线”的SRIO代码,部分参考于网友们的博客,参考文档将在结尾列出
SRIO_2">SRIO简述
一句话:高速串行通信协议
有多高速:最高25Gbps≈3G的文件一秒传输完成
什么串行:四组口,可任意配置为用一组还是多用几组(每组最快6.25Gbps)
适合场景:任何高速数据交换场景,但其实小编只在FPGA与DSP之间应用。
例如:FPGA将图像数据传送到DSP,然后DSP经过识别处理后呈现给出结果。
另外小编觉得最神奇的是SRIO是可以直接将数据放入指定位置的,例如FPGA如果需要将数据给到DSP处理,FPGA可以直接设置放入指定的DDR地址,或者其他高速缓存地址,这是小编认为SRIO最好玩的地方。
代码前言
当说明文档太多而不知道看哪些的时候,最好的方法就是直接上代码(尤其是对于这种已经十几年前的芯片)。
笔者当前一共看过了3套C6678的SRIO相关代码,从难到简单的排序是这样的
难:pdk_c667x_2_0_16\packages\MyExampleProjects
中:C6678\STK_C6678\SRIO
易:678ZH产品线\7.案例源码\SRIO的ZYNQ+DSP核间通讯\srio_initiator
TI官方提供的例子是最难的,其次就是TI论坛上基于keystone的例子,然后就是创龙提供的简化版的例子。小编是先看了keystone的例子,必须要对照文档一步步看,因为涉及到的功能很多。然后再看的创龙,创龙的例子太简单了,只有发送和读取,非常适合刚入DSP门的同学学习。
下面SRIO的初始测试就是基于创龙的代码(创龙代码都是使用了TI官方的CSL库进行配置的,用户需要自行用include options来包含(eclipse系列用户应该容易理解),我用的是“pdk_C6678_1_1_2_6\packages\ti\csl”)。
以下创龙demo的例子大家可以去其官网上进行下载哦。
SRIO_21">SRIO配置
创龙的代码是直接与FPGA交互,但是稍微一改就可以变成回环测试,非常适合于FPGA端还没有调好的场景。
原始代码
创龙非常贴心的将代码全部放到了main一个文档里面,异常的简洁明了。
我们先来整体看一下
int main(void)
{
int8_t ret;
uint32_t core_num;
/* enable SRIO PSC */
ret = enable_srio();
if(ret != 0) {
printf("srio psc initialization failed ! \r\n");
return -1;
}
/* configure and enable SRIO subsystem */
ret = srio_device_init();
if(ret != 0) {
printf("srio system initialization failed ! \r\n");
return -1;
}
/* read the DSP Core Number */
core_num = CSL_chipReadReg(CSL_CHIP_DNUM);
/* srio transmit test: srio write(NWRITE) --> srio read(NREAD) */
ret = srio_test(core_num, TARGET_ADDRESS, TARGET_SIZE, Srio_Ftype_WRITE);
if(ret != 0) {
printf("srio test occur error! \r\n");
return -1;
}
printf("SRIO test %d cycles, " \
"errcnt: %d, total size %d KB, ", \
LOOP_TIMES, err_count, TARGET_SIZE * LOOP_TIMES / 1024);
printf("write type: NWRITE, avg write rate: %.2f Gbps, ", \
w_rate_total / LOOP_TIMES);
printf("read type: NREAD, avg read rate: %.2f Gbps\r\n\n\n", \
r_rate_total / LOOP_TIMES);
/* clear var to zero */
w_rate_total = 0.0;
r_rate_total = 0.0;
err_count = 0;
/* srio transmit test: srio write(SWRITE) --> srio read(NREAD) */
ret = srio_test(core_num, TARGET_ADDRESS, TARGET_SIZE, Srio_Ftype_SWRITE);
if(ret != 0) {
printf("srio test occur error! \r\n");
return -1;
}
printf("SRIO test %d cycles, " \
"errcnt: %d, total size %d KB, ", \
LOOP_TIMES, err_count, TARGET_SIZE * LOOP_TIMES / 1024);
printf("write type: SWRITE, avg write rate: %.2f Gbps, ", \
w_rate_total / LOOP_TIMES);
printf("read type: NREAD, avg read rate: %.2f Gbps\r\n\n\n", \
r_rate_total / LOOP_TIMES);
return 0;
}
以上代码,使能SRIO电源管理、初始化SRIO、SRIO测试,一共三步,就能跑起来测试。
1.使能电源
电源设置非常固定,只要将TI官方csl库包进来然后使用即可。
static int32_t enable_srio (void)
{
/* SRIO power domain is turned OFF by default. It needs to be turned on before doing any
* SRIO device register access. This not required for the simulator
*/
/* Set SRIO Power domain to ON */
CSL_PSC_enablePowerDomain(CSL_PSC_PD_SRIO);
/* Enable the clocks too for SRIO */
CSL_PSC_setModuleNextState(CSL_PSC_LPSC_SRIO, PSC_MODSTATE_ENABLE);
/* Start the state transition */
CSL_PSC_startStateTransition(CSL_PSC_PD_SRIO);
/* Wait until the state transition process is completed. */
while(!CSL_PSC_isStateTransitionDone(CSL_PSC_PD_SRIO));
/* Return SRIO PSC status */
if((CSL_PSC_getPowerDomainState(CSL_PSC_PD_SRIO) != PSC_PDSTATE_ON) ||
(CSL_PSC_getModuleState(CSL_PSC_LPSC_SRIO) != PSC_MODSTATE_ENABLE)) {
/* SRIO Power on failed. Return error */
return -1;
}
/* SRIO ON. Ready for use */
return 0;
}
SRIO_120">2.初始化SRIO
链接: 各部寄存器参数说明
初始化SRIO是用来选择使用几组接口、选择多大速率的一些基本操作
还有最后确认是否建立好了SRIO连接(如果FPGA没写好,那就只能用回环模式通过,否则就会报连接错误)
在这一部分,用户可以将其设置为回环测试,只用修改一个小小的地方即可
static int32_t srio_device_init (void)
{
uint32_t i, wait_time;
uint32_t status;
/* Get the CSL SRIO Handle. */
hSrio = CSL_SRIO_Open(0);
if(hSrio == NULL)
return -1;
/* Code to disable SRIO reset isolation */
if(CSL_PSC_isModuleResetIsolationEnabled(CSL_PSC_LPSC_SRIO))
CSL_PSC_disableModuleResetIsolation(CSL_PSC_LPSC_SRIO);
/* Disable the SRIO Global block */
CSL_SRIO_GlobalDisable(hSrio);
/* Disable each of the individual SRIO blocks. */
for(i = 0; i <= 9; i++)
CSL_SRIO_DisableBlock(hSrio, i);
/* BOOT_COMPLETE = 0: write enabled */
CSL_SRIO_SetBootComplete(hSrio, 0);
/* Now enable the SRIO block and all the individual blocks also. */
CSL_SRIO_GlobalEnable(hSrio);
for(i = 0; i <= 9; i++)
CSL_SRIO_EnableBlock(hSrio, i);
/* Configure SRIO ports mode. */
for(i = 0; i <= 3; i++)
// CSL_SRIO_SetNormalMode(hSrio, i);
CSL_SRIO_SetLoopbackMode(hSrio, i);
/* Enable Automatic Priority Promotion of response packets. */
CSL_SRIO_EnableAutomaticPriorityPromotion(hSrio);
/*
* Set the SRIO Prescalar select to operate in the range
* PERSCALER_SELECT = 0: 44.7 ~ 89.5 MHz
*/
CSL_SRIO_SetPrescalarSelect(hSrio, 0);
/* Unlock the Boot Configuration Kicker */
CSL_BootCfgUnlockKicker();
/*
* MPY = 0x50: 10x
* ENPLL = 1: PLL Enable
* srio_serdes_clock = RefClk(250MHz) * MPY = 2.5GHz
*/
CSL_BootCfgSetSRIOSERDESConfigPLL(0x51);
/*
* Configure the SRIO SERDES Receive Configuration
* ENOC = 1: Enable offset compensation
* EQ = 1: Fully adaptive equalization
* CDR = 5: First order with fast lock
* ALIGN = 1: Comma alignment enabled
* TERM = 1: Input termination, the only valid value for this field is 0x1
* RATE = 1: Data Rate = 2 * srio_serdes_clock = 5Gbps
* BUSWIDTH = 1: Bus width, indicate a 20-bit wide parallel bus to the clock
* ENRX = 1: Enable this receiver
*/
for(i = 0; i <= 3; i++)
CSL_BootCfgSetSRIOSERDESRxConfig(i, 0x00468495);
/*
* Configure the SRIO SERDES Transmit Configuration
* MSYNC = 1: Enables the channel as the master lane
* FIRUPT = 1: Transmitter pre and post cursor FIR filter update
* TWPST1 = 18: Adjacent post cursor Tap weight
* TWPRE = 1: Precursor Tap weight
* SWING = 16: Output swing
* RATE = 1: Data Rate = 2 * srio_serdes_clock = 5Gbps
* BUSWIDTH = 1: Bus width, indicate a 20-bit wide parallel bus to the clock
* ENRX = 1: Enable this receiver
*/
for(i = 0; i <= 3; i++)
CSL_BootCfgSetSRIOSERDESTxConfig(i, 0x001C8F95);
/* Loop around till the SERDES PLL is not locked. */
while(1) {
/* Get the SRIO SERDES Status */
CSL_BootCfgGetSRIOSERDESStatus(&status);
if(status & 0x1)
break;
}
/* Lock the Boot Configuration Kicker */
CSL_BootCfgLockKicker();
/* Clear the LSU pending interrupts. */
CSL_SRIO_ClearLSUPendingInterrupt(hSrio, 0xFFFFFFFF, 0xFFFFFFFF);
/* Set the 16 bit and 8 bit identifier for the SRIO Device */
CSL_SRIO_SetDeviceIDCSR(hSrio, CSR_LOCAL_DEVICEID_8BIT, CSR_LOCAL_DEVICEID_16BIT);
/* Configure the Base Routing Register */
CSL_SRIO_SetTLMPortBaseRoutingInfo(hSrio, 0, 1, 1, 0, 0);
CSL_SRIO_SetTLMPortBaseRoutingPatternMatch(hSrio, 0, 1, REMOTE_DEVICEID1_8BIT, 0xFF);
/* Configure the PLM for all the ports */
for (i = 0; i <= 3; i++) {
/* TODO: We need to ensure that the Port 0 is configured to support both
* the 2x and 4x modes. The Port Width field is read only. So here we simply
* ensure that the Input and Output ports are enabled */
CSL_SRIO_EnableInputPort(hSrio, i);
CSL_SRIO_EnableOutputPort(hSrio, i);
/*
* Discovery timer is specified to be 28 msec +/- 4 msec
* Discovery timer = RefClk(250MHz) period * PRESCALAR_SRV_CLK * 52429 * DISCOVERY_TIMER
* = (1 / 250Mhz) * (250 / 10) * 52429 * 5 = 26.2ms
*/
CSL_SRIO_SetPLMPortDiscoveryTimer(hSrio, i, 0x5);
}
/* Set the Port link timeout CSR */
CSL_SRIO_SetPortLinkTimeoutCSR(hSrio, 0x000FFF);
CSL_SRIO_SetPortResponseTimeoutCSR(hSrio, 0xFF0FFF);
/* Set the Port General CSR: Only executing as Master Enable */
CSL_SRIO_SetPortGeneralCSR(hSrio, 0, 1, 0);
/* Clear the sticky register bits */
CSL_SRIO_SetLLMResetControl(hSrio, 1);
/* Set the Data Streaming MTU */
CSL_SRIO_SetDataStreamingMTU(hSrio, 64);
/* Configure the path mode 4 for the ports */
CSL_SRIO_SetPLMPortPathControlMode(hSrio, 0, 4);
/*
* Set the LLM Port IP Prescalar
* PRESCALAR_SRV_CLK = RefClk(250MHz) / 10
*/
CSL_SRIO_SetLLMPortIPPrescalar(hSrio, 0x19);
/* Enable the peripheral */
CSL_SRIO_EnablePeripheral(hSrio);
/* Configuration has been completed */
/* BOOT_COMPLETE = 1: write to read only registers disabled */
CSL_SRIO_SetBootComplete(hSrio, 1);
/* This code checks if the ports are operational or not */
wait_time = 100;
while(wait_time) {
if(CSL_SRIO_IsPortOk(hSrio, 0) == TRUE) {
break;
} else {
wait_time --;
/* Delay 1 ms */
cpu_delaycycles(1000000);
}
}
if(wait_time == 0) {
printf("srio system initialization time out!\r\n");
return -1;
}
/* Initialization has been completed */
return 0;
}
波特率的计算方式如下:
参考时钟(板卡外部时钟)*倍频得出一个时钟A
时钟A经过RATE的选择,变成实际的通讯时钟
例如:外部时钟为250Mhz,经过10倍频=2.5Ghz,再经过RATE选择half模式就变成了5Gbps。
下图展示了倍频设置,对应代码中的CSL_BootCfgSetSRIOSERDESConfigPLL(0x51);
下图代表了RATE对应关系,对应代码中的
CSL_BootCfgSetSRIOSERDESRxConfig(i, 0x00468495);和
CSL_BootCfgSetSRIOSERDESTxConfig(i, 0x001C8F95);
还有一些其他需要计算的参数,按照代码中注释计算即可,但是注意要将250Mhz的参考时钟(外部时钟)改为自己板卡上的时钟再进行计算。
回环修改
将CSL_SRIO_SetNormalMode(hSrio, i);改为
CSL_SRIO_SetLoopbackMode(hSrio, i);
SRIO_312">3.SRIO测试
在此demo中,SRIO会先将数据发送,然后再回读进行比较。
发送使用的是nwrite——即无响应写入
回读使用的是nread——即无响应读取
这部分代码需要用户对SRIO有一定的了解,会涉及到目的地址与本地地址。在这小编就不做说明了哈。
static int8_t srio_test(uint8_t LSU_Number, uint32_t target_addr,
uint32_t transfer_size, Srio_Ftype w_format_type)
{
SRIO_LSU_TRANSFER tparams;
uint32_t main_pll_freq;
int32_t status = 0;
uint32_t i = 0, j = 0, timeout = 0;
uint8_t uiCompletionCode = 0, context = 0;
uint8_t contextBit = 0, transactionID = 0;
uint32_t transStart = 0, transCost = 0;
float w_rate = 0, r_rate = 0;
uint32_t w_time = 0, r_time = 0;
uint8_t *w_buff, *r_buff;
uint8_t *w_buff_global, *r_buff_global, *t_buff_global;
uint32_t *srio_trans_src, *srio_trans_dst;
/* initialize variables */
TSCL = 0;
TSCH = 0;
/* Get the cpu freq */
main_pll_freq = platform_get_main_pll_freq();
/* malloc buffer */
w_buff = malloc(transfer_size);
if(w_buff == NULL) {
status = -1;
printf("Failed to alloc meory !\r\n");
goto err_alloc_wbuff;
}
r_buff = malloc(transfer_size);
if(r_buff == NULL) {
status = -1;
printf("Failed to alloc meory !\r\n");
goto err_alloc_rbuff;
}
/* convert the buffer address to global address */
w_buff_global = (uint8_t *)Convert_CoreLocal2GlobalAddr((uint32_t)w_buff);
r_buff_global = (uint8_t *)Convert_CoreLocal2GlobalAddr((uint32_t)r_buff);
t_buff_global = (uint8_t *)target_addr;
/* initialize the test data for buffer */
for(i = 0; i < transfer_size; i++) {
srand(i);
w_buff[i] = rand() % 0xFF;;
r_buff[i] = 0;
}
/* Loop times */
for(j = 0; j < LOOP_TIMES; j++) {
/* wait loopback complete time set as 10ms, base on cpu freq as 1000MHz */
timeout = 10000000;
/* 1.1 caculate the read and write buffer for srio transfer address */
srio_trans_src = (uint32_t *)w_buff_global;
srio_trans_dst = (uint32_t *)t_buff_global;
/* 1.2 set transfer parameters, srio nwrite test, w_buff -> devmem_buff */
memset((void *)&tparams, 0, sizeof(tparams));
tparams.rapidIOLSB = (uint32_t)srio_trans_dst;
tparams.dspAddress = (uint32_t)srio_trans_src;
tparams.bytecount = transfer_size;
if(w_format_type == Srio_Ftype_WRITE)
tparams.ttype = Srio_Ttype_Write_NWRITE;
tparams.ftype = w_format_type;
tparams.dstID = REMOTE_DEVICEID1_8BIT;
tparams.outPortID = SRIO_PORT;
tparams.idSize = 0;
/* wait lsu have available shadow register */
while(1) {
if (CSL_SRIO_IsLSUFull(hSrio, LSU_Number) == FALSE)
break;
}
/* Get the LSU Context and Transaction Information. */
CSL_SRIO_GetLSUContextTransaction(hSrio, LSU_Number,
&contextBit, &transactionID);
transStart = _itoll(TSCH, TSCL);
/* start srio transfer */
CSL_SRIO_SetLSUTransfer(hSrio, LSU_Number, &tparams);
/* wait for a transfer completion interrupt occur */
while(timeout) {
CSL_SRIO_GetLSUCompletionCode(hSrio, LSU_Number, transactionID,
&uiCompletionCode, &context);
if(context == contextBit) {
/* disable pending transactions */
transactionID = 0xFF;
contextBit = 0xFF;
if(uiCompletionCode != 0) {
status = -1;
printf("SRIO transfer have error completed code %d\r\n", -(uiCompletionCode));
goto err_transfer;
}
break;
} else {
timeout--;
/* delay 1 cpu cyle */
asm (" nop");
}
}
if(timeout == 0) {
/* if transfer timeout occurs, return error status */
status = -1;
printf("SRIO transfer timeout\r\n");
goto err_transfer;
}
/* Calculate srio transfer used time */
transCost = _itoll(TSCH, TSCL) - transStart;
w_time = transCost;
/* Calculate srio transfer write rate */
w_rate = (float)transfer_size * main_pll_freq / w_time / 1024 / 1024 / 1024 * 8;
/* wait loopback complete time set as 10ms, base on cpu freq as 1000MHz */
timeout = 10000000;
/* 1.3 caculate the read and write buffer for srio transfer address */
srio_trans_src = (uint32_t *)t_buff_global;
srio_trans_dst = (uint32_t *)r_buff_global;
/* 1.4 set transfer parameters, srio nread test, devmem_buff -> r_buff */
memset((void *)&tparams, 0, sizeof(tparams));
tparams.rapidIOLSB = (uint32_t)srio_trans_src;
tparams.dspAddress = (uint32_t)srio_trans_dst;
tparams.bytecount = transfer_size;
tparams.ttype = Srio_Ttype_Request_NREAD;
tparams.ftype = Srio_Ftype_REQUEST;
tparams.dstID = REMOTE_DEVICEID1_8BIT;
tparams.outPortID = SRIO_PORT;
tparams.idSize = 0;
/* wait LSU have available shadow register */
while(1) {
if (CSL_SRIO_IsLSUFull (hSrio, LSU_Number) == FALSE)
break;
}
/* Get the LSU Context and Transaction Information. */
CSL_SRIO_GetLSUContextTransaction(hSrio, LSU_Number,
&contextBit, &transactionID);
transStart = _itoll(TSCH, TSCL);
/* start srio transfer */
CSL_SRIO_SetLSUTransfer(hSrio, LSU_Number, &tparams);
/* wait for a transfer completion interrupt occur */
while(timeout) {
CSL_SRIO_GetLSUCompletionCode(hSrio, LSU_Number, transactionID,
&uiCompletionCode, &context);
if(context == contextBit) {
/* disable pending transactions */
transactionID = 0xFF;
contextBit = 0xFF;
if(uiCompletionCode != 0) {
status = -1;
printf("SRIO transfer have error completed code %d\r\n", -(uiCompletionCode));
goto err_transfer;
}
break;
} else {
timeout--;
/* delay 1 cpu cyle */
asm (" nop");
}
}
if(timeout == 0) {
/* if transfer timeout occurs, return error status */
status = -1;
printf("SRIO transfer timeout\r\n");
goto err_transfer;
}
/* Calculate srio transfer used time */
transCost = _itoll(TSCH, TSCL) - transStart;
r_time = transCost;
/* Calculate srio transfer read rate */
r_rate = (float)transfer_size * main_pll_freq / r_time / 1024 / 1024 / 1024 * 8;
w_rate_total += w_rate;
r_rate_total += r_rate;
for(i = 0; i < transfer_size; i++) {
if(w_buff_global[i] != r_buff_global[i]) {
err_count++;
if(err_count == 1) {
printf("Frist Err occurred src addr: 0x%x, dst addr: 0x%x\r\n", \
(uint32_t)&w_buff_global[i], (uint32_t)&r_buff_global[i]);
printf("dst val: 0x%x, ", r_buff_global[i]);
printf("expet val: 0x%x\r\n", w_buff_global[i]);
}
}
}
printf("=== loop times: %d | err_count: %d | ", j, err_count);
if(w_format_type == Srio_Ftype_WRITE)
printf("trans_size: %d Byte | NWRITE write times: %d ns(%.2f Gbps) | ", \
transfer_size, w_time, w_rate);
else if(w_format_type == Srio_Ftype_SWRITE)
printf("trans_size: %d Byte | SWRITE write times: %d ns(%.2f Gbps) | ", \
transfer_size, w_time, w_rate);
printf("NREAD read times: %d ns(%.2f Gbps)\r\n", r_time, r_rate);
}
err_transfer:
free(r_buff);
err_alloc_rbuff:
free(w_buff);
err_alloc_wbuff:
return status;
}
Doorbell门铃中断
门铃中断基于keystone的demo进行修改,会涉及到三个地方
1.初始化中断函数
2.中断向量表建立
3.中断向量表的链接
1.初始化中断函数
中断部分函数是从keystone的SRIO例子中的SRIO_Test.c中摘抄出来的,原版的主要函数有
SRIO_Interrupts_Init();(初始化设置)
SRIO_Doorbell_ISR();(中断跳转函数)
KeyStone_SRIO_Interrupt_init(SRIO_Interrupt_Cfg * interrupt_cfg);(初始化应用)
以下为我这里修改的初始化,因为不需要message中断所以就只保留了doorbell的4中断。
void SRIO_Interrupts_Init(void)
{
/*map SRIO doorbell interrupts to INT4.*/
gpCGEM_regs->INTMUX1 = (CSL_GEM_INTDST_N_PLUS_16<<CSL_CGEM_INTMUX1_INTSEL4_SHIFT);
//enable INT4
CPU_interrupt_enable((1<<4));
interrupt_cfg.interrupt_map = interrupt_map;
interrupt_cfg.uiNumInterruptMap =
sizeof(interrupt_map)/sizeof(SRIO_Interrupt_Map);
/*interrupt rate control is not used in this test*/
interrupt_cfg.interrupt_rate= NULL;
interrupt_cfg.uiNumInterruptRateCfg= 0;
interrupt_cfg.doorbell_route_ctl= SRIO_DOORBELL_ROUTE_TO_DEDICATE_INT;
}
需要注意的是,中断初始化一定记到放到SRIO初始化后面,否则将会一直中断导致程序死掉。按下图放置
2.中断向量表建立
其实就是需要新建一个文件,小编直接从keystone里面复制粘贴了出来
下列代码是SRIO_vectors.asm的原始内容,由于小编不需要message中断,所以下列的.ref SRIO_Message_ISR需要去掉,然后VEC_ENTRY SRIO_Message_ISR ;interrupt 5改为VEC_DUMMY ;interrupt 5
;create interrupt vector table for C6000 DSP
;--------------------------------------------------------------
;This file can be modified to add Interrupt Service Routine(ISR)
;for an interrupt, the steps are:
;1,reference to the externally defined ISR, for example
; .ref EDMA_ISR
;2,modify the corresponding entry in the interrupt vector table.
; For example, if interrupt 8 is used for EDMA, then you should
; modify the entry number 8 like below:
; VEC_ENTRY EDMA_ISR ;interrupt 8
;--------------------------------------------------------------
;Author: Brighton Feng
;Created on 2010-12-6
;--------------------------------------------------------------
;reference to the externally defined ISR
.ref _c_int00
.ref SRIO_Doorbell_ISR
.ref SRIO_Message_ISR
.ref Exception_service_routine
.ref Nested_Exception_service_routine
.ref exception_record
.global vectors
;--------------------------------------------------------------
.sect ".text"
;create interrupt vector for NMI
NMI_ISR:
STW B1,*-B15[1]
;save some key registers when exception happens
MVKL exception_record,B1
MVKH exception_record,B1
STW B3, *+B1[0]
STW A4, *+B1[1]
STW B4, *+B1[2]
STW B14, *+B1[3]
STW B15, *+B1[4]
;jump to exception service routine
MVKL Exception_service_routine, B1
MVKH Exception_service_routine, B1
B B1
LDW *-B15[1],B1
NOP 4
;--------------------------------------------------------------
;create interrupt vector for reset (interrupt 0)
VEC_RESET .macro addr
MVKL addr,B0
MVKH addr,B0
B B0
MVC PCE1,B0
NOP 4
.align 32
.endm
;create interrupt vector for other used interrupts
VEC_ENTRY .macro addr
STW B0,*--B15
MVKL addr,B0
MVKH addr,B0
B B0
LDW *B15++,B0
NOP 4
.align 32
.endm
;create interrupt vector for unused interrupts
VEC_DUMMY .macro
unused_int?:
B unused_int? ;dead loop for unused interrupts
NOP 5
.align 32
.endm
;--------------------------------------------------------------
;interrupt vector table
.sect "vecs"
.align 1024
vectors:
VEC_RESET Nested_Exception_service_routine ;Nested exception
VEC_ENTRY NMI_ISR ;NMI/Exception
VEC_DUMMY ;RSVD
VEC_DUMMY ;RSVD
VEC_ENTRY SRIO_Doorbell_ISR ;interrupt 4
VEC_ENTRY SRIO_Message_ISR ;interrupt 5
VEC_DUMMY ;interrupt 6
VEC_DUMMY ;interrupt 7
VEC_DUMMY ;interrupt 8
VEC_DUMMY ;interrupt 9
VEC_DUMMY ;interrupt 10
VEC_DUMMY ;interrupt 11
VEC_DUMMY ;interrupt 12
VEC_DUMMY ;interrupt 13
VEC_DUMMY ;interrupt 14
VEC_DUMMY ;interrupt 15
.end
3.中断向量表的链接
其实名字不一定叫这个,这块的主要作用就是把第二步的中断表给应用进去。
需要修改的文件为C66X.cmd,没错,是个cmd文件,用CCS建立项目的时候会自动生成。
如果没有这一步,那么生成的中断将会在0x00800000里循环卡死。
至此,中断完成,跑一下看看结果
完全没问题,DOORBELL被触发!
参考链接: 如何把中断服务程序加载到ISTP中
参考链接: DSP之SRIO通信之DSP端参数设置