TI6678 MSMC关闭Cache方法总结

背景

TI6678的MSMC在默认条件下是开启对L1d的缓存的,但是却没有硬件机制维护MSMC的Cache一致性,在这种情况下如果将多核共享变量存放在MSMC中,将会出现多核访问共享变量不一致的问题。在某个项目进行过程中需要使用MSMC存放核间信号量等共享数据供八核使用,经过查阅资料与实验实现了这一功能,故总结如下,仅供参考。(推荐使用方法二)

简单举例

  • 假设共享变量Flag存放于0x0c000000处,核0负责对Flag进行写,核1负责对Flag进行读。
  • 核0操作如下:
while(1)
{
    *(unsigned int*)(0x0c000000)=0x1;		//将Flag设置为0x1;
     
    *(unsigned int*)(0x0c000000)=0x2;		//将Flag设置为0x2;
}
  • 核1操作如下:
unsigned int flag=0;
unsigned int *val=(unsigned int*)(0x0c000000);
while(1)
{
    flag= *val;			//读取Flag值
}
  • 运行的具体过程为:核0先将FLag写为0x1,然后等待在写0x2的语句处,此时核1读取一次Flag。接着核0将Flag写为0x2,然后等待在写0x1的语句处,此时核1再次读取FLag,以此循环读写。

**在MSMC默认开启Cache的条件下,进行上述操作将会出现核1读取到的Flag值与核0写入的Flag值不一致的现象。**原因是核1从共享内存读取Flag的过程中,由于没有维护Cache一致性,L1D中会缓存先前的数据。因此核0向共享内存重新写入Flag后,核1读取将首先从L1D中获取到数据,结果导致两个核读写数据出现不一致。

解决办法

方法1:L1D手动写回与Cache无效
  • 核0操作如下:
while(1)
{
    *(unsigned int*)(0x0c000000)=0x1;		//将Flag设置为0x1;
    CACHE_wbInvL1d((void *)0x0c000000),4,CACHE_WAIT);	//写回数据,并无效L1DCache
    
    *(unsigned int*)(0x0c000000)=0x2;		//将Flag设置为0x2;
    CACHE_wbInvL1d((void *)0x0c000000),4,CACHE_WAIT);	//写回数据,并无效L1DCache
}
  • 核1操作如下:
unsigned int flag=0;
unsigned int *val=(unsigned int*)(0x0c000000);
while(1)
{
    CACHE_invL1d((void *)0x0c000000),4,CACHE_WAIT);//无效L1DCache
    flag= *val;			//读取Flag值
}

​ 按照上述方法,可以实现多核访问共享内存的一致性。但缺点是每次进行读写的过程中都需要进行手动进行数据写回与无效L1DCache的操作,不便于使用。因此,推荐使用第二种办法。

方法二:地址重映射

使用XMC将MSMC重映射到其他地址(如0x20000000),后续通过访问重映射地址(0x20000000)即可访问MSMC内存空间。重映射后即可利用MAR关闭重映射地址(0x20000000)的Cache使能,间接实现关闭MSMC的L1D Cache。细节可参考《TMS320C66x DSP CorePac (sprugw0b).pdf》“7.3.2.2.2 MSMC RAM Aliasing Scenarios”

  • 将MSMC进行重映射并关闭Cache函数示例

    /*
    * Function:Cache_MSMC_initial
    * Description:用于将MSMC进行重映射并关闭Cache
    */
    #include 
    #include 
    
    #define  MAR_BASE_ADDR      	(0x01848000)
    
    void Cache_MSMC_initial(Uint32 index,Uint32 bAddr,Uint8 segSize,Uint32 rAddr)
    {
    	CSL_XMC_XMPAXH 		mpaxh;		// 存储保护和地址扩展寄存器(H)
    	CSL_XMC_XMPAXL 		mpaxl;		// 存储保护和地址扩展寄存器(L)
    
    	/******************** 地址重映射 *********************/
    
    	mpaxh.bAddr   = bAddr;				// 基地址(匹配逻辑地址的高位地址)
    	mpaxh.segSize = segSize;			// 重映射区段大小
    
    	// 设置XMPAXH寄存器. Writes:XMC_XMPAXH_SEGSZ,XMC_XMPAXH_BADDR.
    	CSL_XMC_setXMPAXH (index, &mpaxh);
    
    	// 设置该区段地址的访问权限
    	mpaxl.ux = 1;
    	mpaxl.uw = 1;
    	mpaxl.ur = 1;
    	mpaxl.sx = 1;
    	mpaxl.sw = 1;
    	mpaxl.sr = 1;
    	mpaxl.rAddr = rAddr;
    
    	// 设置XMPAXL寄存器.
    	CSL_XMC_setXMPAXL (index, &mpaxl);
    
    	// 读取XMPAXL寄存器.
    	CSL_XMC_getXMPAXL (index, &mpaxl);
    
    /******************** 地址重映射 *********************/
    
    	*(unsigned int *)(MAR_BASE_ADDR+ 4*(bAddr>>24))= 0x0;		//关闭重映射地址处cache
    }
  • 核0操作如下:

    int main()
    {
    	unsigned int a=0;
    
    	Cache_initial();	//开启L1D、L1P Cache,关闭L2 Cache
    	Cache_MSMC_initial(3,0x20000,21,0x00C000);	//将MSMC重映射到0x20000000后关闭Cache
    
    	while(1){
    		*(unsigned int *)(0x20100000)=0x01;
    		
    		*(unsigned int *)(0x20100000)=0x02;
    
    	}
    }
  • 核1操作如下:

    int main()
    {
    	unsigned int flag=0;
    
    	Cache_initial();
    	Cache_MSMC_initial(3,0x20000,21,0x00C000);  //将MSMC重映射到0x20000000后关闭Cache
    
    	unsigned int*val=(unsigned int *)(0x20100000);
    	while(1){
            
    		flag=*val;
            
    	}
    }
  • 运行的具体过程为:核0先将FLag写为0x1,然后等待在写0x2的语句处,此时核1读取一次Flag。接着核0将Flag写为0x2,然后等待在写0x1的语句处,此时核1再次读取FLag,以此循环读写。在这个过程中,核1均能获取到MSMC中当前的最新数据。

    此时,如果需要将核间信号量或核间消息队列等共享数据存放在MSMC中,只需要将核间信号量或核间消息队列的基地址分配到0x20000000起始的4MB控件即可。

    ​ 2019.03.25 by zn

你可能感兴趣的:(嵌入式)