TMS320C6678开发笔记---SRIO 启动

15.11节  zynq ps端控制srio ip核与6678通讯

  • zynq端使用xilinx官网提供的历程xsrio_dma_lookback_example.c,(C:\Xilinx\SDK\2017.4\data\embeddedsw\XilinxProcessorIPLib\drivers\srio_v1_1\examples)对其内部代码修改,将srio的初始化代码全部屏蔽,只留下DMA的操作代码,(对于ps端看见的是一个DMA控制器)。
  • ps端需要对组一个SRIO HELLO包头,具体格式参考:

《SRIO中的关键数据包格式总结》------HELLO包格式(重点)

https://blog.csdn.net/kunkliu/article/details/108518618

 

  • ps端发送数据个数要求:(发送长度不正确会导致6678接收不到数据,由于此问题查找了很久)
    • 使用SWRITE时:HELLO包头中没有长度,实际的发送长度由DMA决定,数据长度+HELLO包头(8个字节)。
    • 使用NWRITE时: DMA实际发送长度为: HELLO包头中的长度信息+ HELLO包头(8个字节)。

具体发送代码如下:

int XSrioDmaLoopbackExample(XSrio *InstancePtr, u16 DeviceId)
{
	XSrio_Config *SrioConfig;
	XAxiDma_Config *DmaConfig;
	int Status = XST_SUCCESS;
	int Count = 0;
	
#if  0		// SRIO 初始化代码屏蔽 , SRIO IP中已经做好
	/* Initialize the SRIO Device Configuration Interface driver */
	SrioConfig = XSrio_LookupConfig(DeviceId);
	if (!SrioConfig) {
		xil_printf("No SRIO config found for %d\r\n", DeviceId);
		return XST_FAILURE;
	}

	/**< This is where the virtual address would be used, this example
	 * uses physical address.
	 */
	Status = XSrio_CfgInitialize(InstancePtr, SrioConfig,
				SrioConfig->BaseAddress);
	if (Status != XST_SUCCESS) {
		xil_printf("Initialization failed for SRIO\n\r");
		return Status;
	}
	 
	 /* Check for PE Configuration */
	Status = XSrio_GetPEType(InstancePtr); 
	if (Status != XSRIO_IS_MEMORY) {
		xil_printf("SRIO is not configured as the Memory \n\r");
		return XST_FAILURE;
	}
		
	/* Clearing the Memory */
	for(Count=0; Count<(128*1024); Count += 4) {
		*(u32 *)(MEM_ADDR + Count) = 0;
	}
	
	/**< Check whether Streaming Write Operation is Supported by the 
	 * Core or not Since it is a loopback Example Checking at the both 
	 * Target and source Operations.
	 */
	Status = XSrio_IsOperationSupported(InstancePtr, XSRIO_OP_MODE_SWRITE,
						XSRIO_DIR_TX);
	if (Status != XST_SUCCESS) {
                return XST_FAILURE;
        }
	
	Status = XSrio_IsOperationSupported(InstancePtr, XSRIO_OP_MODE_SWRITE,
						XSRIO_DIR_RX);
	if (Status != XST_SUCCESS) {
                return XST_FAILURE;
        }
#endif	
	/**< Frame the SRIO Write-Stream Packet in the Memory 
	 * The Packet-format used here is HELLO Packet format
	 * More details look into pg 3.1 version:73 page(HELLO PACKET FORMAT).
	 */
	// 初始化HELLO包头
	*(u32 *)(MEM_ADDR + 0x00) = 0x0C000000;	// 对应DSP内部的物理地址
		/* Lower word of the HELLO Packet */
	*(u32 *)(MEM_ADDR + 0x04) = 0x00542FF4; 	// 54:NWRITE,FF:写入256个字节 (size-1)
		/* Upper word of the HELLO packet */
	
// 初始化将要发送的数据
	Count = 8;
	while(Count<(DATA_SIZE * 2)) {
		*(u32 *)(MEM_ADDR + Count) = Count;	// 数据紧接着HELLO包头
		 Count += 4;
	}
#if	0		// 设置 SRIO 配置信息,此处省略,有IP核配置
	/* SRIO Configuration */
	/* Set the Local Configuration Space Base Address */
	XSrio_SetLCSBA(InstancePtr, 0xFFF);
        /* Set the Water Mark Level to transfer priority 0,1,2 packet */
	XSrio_SetWaterMark(InstancePtr, 0x5, 0x4, 0x3);
        /* Set the Port Response timeout value */
	XSrio_SetPortRespTimeOutValue(InstancePtr, 0x010203);
#endif
	
	/* DMA Configuration */
	DmaConfig = XAxiDma_LookupConfig(DMA_DEV_ID);
    	if (!DmaConfig) {
           xil_printf("No DMA config found for %d\r\n", DMA_DEV_ID);
           return XST_FAILURE;
    	}

	/* 初始化DMA控制器
* Initialize DMA engine
 */
    	Status = XAxiDma_CfgInitialize(&AxiDma, DmaConfig);
	if (Status != XST_SUCCESS) {
           xil_printf("Initialization failed %d\r\n", Status);
           return XST_FAILURE;
     }
	
	/* 配置发送端DMA
* < Configure the DMA Tx Side 
      * MEM_ADDR is the address where Tx packet is formed
	 */
	XAxiDma_WriteReg(DmaConfig->BaseAddr, XAXIDMA_CR_OFFSET,
				XAXIDMA_CR_RUNSTOP_MASK);
	XAxiDma_IntrEnable(&AxiDma,XAXIDMA_IRQ_IOC_MASK, XAXIDMA_DMA_TO_DEVICE);
	XAxiDma_WriteReg(DmaConfig->BaseAddr, XAXIDMA_SRCADDR_OFFSET, MEM_ADDR);

	/* 配置接收端DMA
	 * Configure the DMA Rx Side
     * MEM_ADDR+0x5000 is the address where Rx packet is formed
	 */
	XAxiDma_WriteReg(DmaConfig->BaseAddr,
		XAXIDMA_RX_OFFSET+XAXIDMA_CR_OFFSET, XAXIDMA_CR_RUNSTOP_MASK);
	XAxiDma_IntrEnable(&AxiDma, XAXIDMA_IRQ_IOC_MASK, XAXIDMA_DEVICE_TO_DMA);
	XAxiDma_WriteReg(DmaConfig->BaseAddr,
		XAXIDMA_RX_OFFSET + XAXIDMA_DESTADDR_OFFSET, MEM_ADDR+0x5000);
		
	// DMA发送数据代码MM2S 
	/* 发送数据长度为256+8,因为前面NWRITE HELLO包中的SRIO数据为FF,此处DMA发送的数据为SRIO+HELLO包头,即256+8 */
	XAxiDma_WriteReg(DmaConfig->BaseAddr, XAXIDMA_BUFFLEN_OFFSET, 256+8);
	/* Wait till DMA MM2S Transfer Complete */
	while(!(XAxiDma_ReadReg(DmaConfig->BaseAddr, XAXIDMA_SR_OFFSET)& 0x1000));

	// DMA接收数据代码 S2MM, DMA 发送接收数据均采用轮询的方式。
	XAxiDma_WriteReg(DmaConfig->BaseAddr,XAXIDMA_RX_OFFSET+XAXIDMA_BUFFLEN_OFFSET, 256);
	 /* Wait till S2MM Transfer Complete */
	  while(!( XAxiDma_ReadReg(DmaConfig->BaseAddr,	XAXIDMA_RX_OFFSET+XAXIDMA_SR_OFFSET) & 0x1000));
	  	
	/* Verifying the Data */
	// 省略。。。。

	return Status;
}

15.12节  SRIO 启动

15.12.1参考资料

TI提供的SRIO boot工程:C:\ti\mcsdk_2_01_02_06\tools\boot_loader\examples\srio

  • srioboot_example   :SRIO发送数据工程,此工程需要移植到其他平台,如ZYNQ,用于启动DSP
  • srioboot_helloworld:被启动的工程,此工程生成的bin文件,通过srioboot_example工程发送给DSP并启动,工程内简单实现了串口打印helloworld示例。
  • srioboot_ddrinit     :DDR初始化工程,如果应用程序的代码段数据段存储在DDR中,必须使用工程srioboot_example现将srioboot_ddrinit生成的代码通过SRIO发送给DSP,然后运行初始化DDR,之后在发送srioboot_helloworld代码。

15.12.2 SRIO启动BootMode配置

15.12.3启动bin文件制作

helloworld_elf2HBin.bat

set C6000_CG_DIR="C:\ti\C6000 Code Generation Tools 7.3.0"
set TOOL_DIR="..\..\..\..\..\..\"
set TARGET=6678
set ENDIAN=little
set PATH=%PATH%;%SystemRoot%\system32;%SystemRoot%;

@echo off

echo C6000_CG_DIR set as: %C6000_CG_DIR%
echo TARGET set as: %TARGET%
echo IBL_ROOT_DIR set as : %IBL_ROOT_DIR%

echo Converting .out to HEX ...
if %ENDIAN% == little (
%C6000_CG_DIR%\bin\hex6x -order L helloworld_image.rmd srioboot_helloworld_evm%TARGET%l.out
) else (
%C6000_CG_DIR%\bin\hex6x -order M helloworld_image.rmd srioboot_helloworld_evm%TARGET%l.out
)

..\..\..\..\..\..\bttbl2hfile\Bttbl2Hfile srioboot_helloworld.btbl srioboot_helloworld.h srioboot_helloworld.bin

..\..\..\..\..\..\hfile2array\hfile2array srioboot_helloworld.h srioBootCode.h bootCode

move srioBootCode.h ..\..\..\srioboot_example\src\
pause
  • 这里制作的bin文件,最终转换成了srioboot_helloworld.h中的bootCode数组,并且拷贝到了工程srioboot_example中。

15.12.4启动代码移植到ZYNQ裸核

  •  移植过程中DDR的处理:TI提供的srioboot_example例程,先通过SRIO传递DDR初始化代码,然后在传递应用程序代码(因为应用程序代码端分配在DDR中,所以必须先初始化DDR)。此处简单处理,将应用程序代码不分配在DDR中,DDR在应用程序中初始化。即SRIO只需要传递应用程序即可。
  • 移植过程中6678 bin文件的处理,转换后的bin文件并不是纯粹的二进制文件里面包含地址和长度信息,经过解析后才可以发送给6678。解析参见下文。
  • 6678正确上电后会初始化SRIO,TI给出的SRIO boot例程中,6678 SRIO接收端的ID号为0,port为0(发送端ID号我没有找到说法)。实际测试中发现ZYNQ SRIO发送 ID 设为0x13,接收ID设为0x12,6678 SRIO也能够接收到数据。ZYNQ与6678 SRIO直接相连。
  • ZYNQ SRIO采用SWRITE形式发送数据只能是256字节,6678才可以接收到。多余256字节程序自行分包。
  • 裸核调试中必须关闭cache,srio数据才可发送正确
	//Xil_ICacheEnable();
	//Xil_DCacheEnable();
	Xil_DCacheDisable();
	Xil_ICacheDisable();

15.12.5传输到6678中的bin文件最终确定

  • 确认方法:

1、确定一个可以正常运行的工程srioboot_helloworld_evmc6678l,将srioboot_helloworld_evmc6678l.out文件制作成工程srioboot_example_evmc6678l中的srioBootCode.h文件。

2、通过仿真srioboot_example_evmc6678l工程,确定srioboot_helloworld.bin文件分为几个部分。起始地址与长度信息:

起始地址:

长度:

0x0C04B000

0x10A0

0x0C04C0E0

0x30

0x0C04C0A0

0x1E

3、通过仿真器下载led_play.out,结合第2步中的几个部分,通过memory browser窗口查看最终在6678中的二进制文件。

4、可以通过save memory保存内存中的数据,保存长度为word (4个字节)

15.12.6 ZYNQ平台上bin文件的转换

  • bin文件基本格式如下:

其中入口地址只有一个,长度和起始地址每个段各有一个。

  • 解析思想:根据长度信息将内容写入到起始地址对应的6678内存区域,然后判断下一段的长度信息,直到长度为0,结束。

int32_t pushData2Srio(uint8_t *pDspCode,int32_t DeviceID)
{
    uint32_t i,ret;
    uint32_t size;
    uint32_t count, remainder;
    uint32_t startaddr;

    /* Get the boot entry address */
    bootEntryAddr = byteto32bits(pDspCode);	// 获取入口地址
    pDspCode +=4;
/* 经测试C6678的bin文件,长度字节与起始地址可以直接通过byteto32bits函数转换得到
	 * 但是数据信息必须先进行32bit字节翻转,然后在进行64bit字节翻转
	 */
    while(1) {
        /* Get the size */
        size = 	byteto32bits(pDspCode);	// 获取长度
        if(size == 0)	
        	break;		// 长度为0 时,整个bin文件解析成功
        pDspCode += 4;

        startaddr = byteto32bits(pDspCode);	// 获取起始地址
        pDspCode+= 4;

        count = size/MAX_TX_SIZE;
        remainder = size - count * MAX_TX_SIZE;

        for(i=0; i<count; i++) {
            /* This is not needed if the .out to be booted is big endian already */
            Convert2BigEndian32((uint32_t *)pDspCode,MAX_TX_SIZE);
            Convert2BigEndian64((uint64_t *)pDspCode,MAX_TX_SIZE);
            /* Transfer boot tables to DSP */
            if(SRIOTransfer(rio_id, port, (uint32_t )pDspCode, (uint32_t )startaddr, MAX_TX_SIZE)!=0)
            	return(-1);

            pDspCode += MAX_TX_SIZE;
            startaddr += MAX_TX_SIZE;
        }

        /* This is not needed if the .out to be booted is big endian already */
        Convert2BigEndian32((uint32_t *)pDspCode,remainder);
        ret = Convert2BigEndian64((uint64_t *)pDspCode,remainder);
        if(SRIOTransfer(rio_id, port, (uint32_t )pDspCode, (uint32_t )startaddr, remainder)!=0)
        	return(-1);
        if(ret)	// 如果不是8的整数倍,将最后一次64bit,翻转回来,保证下一次取长度信息正确
        	Convert2BigEndian64((uint64_t *)pDspCode+remainder/8,8);
        pDspCode += remainder;
    }

    /* Write to Core 0 boot magic address to boot Core 0 */
    Convert2BigEndian32(&bootEntryAddr,1);
    if (SRIOTransfer(rio_id, port,(uint32_t )&bootEntryAddr, BOOT_MAGIC_ADDR(0), 4)!=0) //0x0c04BEE0
        return(-1);

    return(0);
}
  • 调试过程中SRIO总线上数据如下

第一个64bit:0x00600ff40c0b000 HELLO包头

第二个64bit:0x4602f7252b6a002,必须以此种顺序,DSP才能正常启动。需要做的处理如下

  • 转换过程如下:

1、先将25 F7 02 46 字节反转 46 02 F7 25

2、再将02 60 6A 2B字节反转2B 6A 60 02

3、将46 02 F7 25 2B 6A 60 02转为 02 60 6A 2B 25 F7 02 46

4、进行memcpy 256字节

注意:在进行64bit字节反转时,当最后剩余的字节数不足8个字节时,由于反转后会影响下一次长度的获取值出错,故,在srio发送完成后,将最后不足的8个字节,再次反转回去,保证下一次获取长度正确。

代码如下:

int SRIOTransfer(int rio_num, int port_num, unsigned int host_addr, unsigned int dsp_addr, int size_bytes)
{
	u8 *dma_addr = NULL;

	*(u32 *)(MEM_ADDR + 0x00) = dsp_addr;       /* Lower word of the HELLO Packet */
	*(u32 *)(MEM_ADDR + 0x04) = 0x00600FF4;    /* Upper word of the HELLO packet */

	dma_addr = (u8 *)(MEM_ADDR + 0x08);
	memcpy(dma_addr, (u8 *)host_addr, size_bytes);

	XAxiDma_WriteReg(DmaConfig->BaseAddr, XAXIDMA_CR_OFFSET, XAXIDMA_CR_RUNSTOP_MASK);
	XAxiDma_IntrEnable(&AxiDma,XAXIDMA_IRQ_IOC_MASK, XAXIDMA_DMA_TO_DEVICE);
	XAxiDma_WriteReg(DmaConfig->BaseAddr, XAXIDMA_SRCADDR_OFFSET, MEM_ADDR);

	XAxiDma_WriteReg(DmaConfig->BaseAddr, XAXIDMA_BUFFLEN_OFFSET, size_bytes+8);
	/* Wait till DMA MM2S Transfer Complete */
	while(!(XAxiDma_ReadReg(DmaConfig->BaseAddr, XAXIDMA_SR_OFFSET)	& 0x1000));

    return(0);
}
void Convert2BigEndian32(uint32_t *pDspCode, int32_t size)
{
    uint32_t i;
    uint32_t temp;

    for(i=0; i<size; i+=4, pDspCode++) {
        temp = *pDspCode;

        temp =	(temp>>24) |
            ((temp<<8) & 0x00FF0000) |
            ((temp>>8) & 0x0000FF00) |
            (temp<<24);

        *pDspCode= temp;
    }
}

int Convert2BigEndian64(uint64_t *pDspCode,int32_t size)
{
    uint32_t i;
    uint64_t temp;

    for(i=0; i<size; i+=8, pDspCode++) {
        temp = *pDspCode;
        temp =	(temp>>56) |
            ((temp<<40) & 0x00FF000000000000) |
            ((temp>>40) & 0x000000000000FF00) |
			((temp<<24) & 0x0000FF0000000000) |
			((temp>>24) & 0x0000000000FF0000) |
			((temp<<8) & 0x000000FF00000000) |
			((temp>>8) & 0x00000000FF000000) |
            (temp<<56);

        *pDspCode= temp;
    }
    if(size%8)
    	return 1;
    return 0;
}

15.12.7启动代码移植到ZYNQ系统

       在linux系统下,bin文件的处理操作、SRIO的控制操作(通过DMA控制)和在裸核下操作一样。linux系统下可以读取srioboot_helloworld.bin文件获取二进制文件。DMA控制器使用虚拟地址映射进行操作,操作过程只包括:reset、写控制寄存器、打开中断、写启动位、写长度、轮询完成标志。

 

 

15.12.8 每次ZYNQ必须复位,DSP才能加载成功问题

问题描述:通过ZYNQ SRIO在线加载6678程序,发现只有在ZYNQ复位后才可以加载成功,第二次加载,6678不能正常启动。

 

分析原因:1、ZYNQ SRIO加载C6678程序时,会对C6678进行断电复位,第二次加载不成功后,连接仿真器,看到C6678对应的内存中并没有正确代码,推测:ZYNQ SRIO第二次与C6678连接时,没有能够正常连接,导致数据没有写的C6678内部。所以决定每次加载程序时,ZYNQ SRIO IP核进行一次复位操作。

          2、断电复位C6678时,同时会复位时钟,由于ZYNQ的SRIO时钟与C6678的时钟由同一时钟芯片控制,为了不影响ZNYQ SRIO,所以决定复位C6678时不复位时钟芯片。

 

解决方案:经上述分析,最终解决方法为:ZYNQ上电运行后,初始化一次时钟芯片,每次C6678加载程序时,ZYNQ IP核复位一次,然后断电复位C6678,即可再次加载C6678程序成功。 (ZYNQ通过对一个寄存器操作实现)

          经测试发现:如果C6678完成一次上电时序控制,SRIO在线加载时,只对RESETFULL进行复位也可以加载成功。

 

 

  • 4
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值