近期C6000 DSP开发小结

复数乘法指令

  TI的编译器提供的一些内联函数中可以直接调用复数乘法的汇编指令(CMPYSP,只有C66x DSP支持),完成复数乘法运算。
在这里插入图片描述

  src1和src2是连续的两个32位寄存器组成的register pair,dst是由四个连续的32位寄存器组成的register quadruplets。这里只是做了乘法运算。
在这里插入图片描述

  然后用DADDSP指令可以对上面的结果中的实数部分和虚数部分分别求和,即可实现复数乘法。

( a + b i ) ( c + d i ) = a c − b d + ( a d + b c ) i \left( {a + b{\rm{i} } } \right)\left( {c + d{\rm{i} } } \right) = ac - bd + \left( {ad + bc} \right){\rm{i} } (a+bi)(c+di)=acbd+(ad+bc)i

  另外编译器也提供了一种快速实现复共轭乘法的方式,即 p × q ∗ p\times q^* p×q。乘数中需要求复共轭的数 q q q放在src1寄存器对中作为 a + b i a+b\rm{i} a+bi,另一个乘数 p p p放在src2寄存器对中,依次完成CMPYSP和DSUBSP即可得到 p × q ∗ p\times q^* p×q的结果。
在这里插入图片描述

( a − b i ) ( c + d i ) = a c + b d + ( a d − b c ) i \left( {a - b{\rm{i} } } \right)\left( {c + d{\rm{i} } } \right) = ac + bd + \left( {ad - bc} \right){\rm{i} } (abi)(c+di)=ac+bd+(adbc)i

  总结起来就是用_complex_conjugate_mpysp(m1, m2)这个函数的时候,第一个参数m1是那个取复共轭的复数。
  另外,从运算效率上考虑,如果一个复数在存储器中的存放方式与它在寄存器中的存放方式相同,那么就可以只用一条LDDW指令就从存储器中将一个64bit表示的复数直接load到寄存器中。可以看到寄存器中实部是存在高32位而虚部是存在低32位,所以建议存储器中也按照这样的方式存放(而一般FFT支持的复数格式是实部在低地址,虚部在高地址,是相反的,需要注意!)

Cache映射方式

  Cache的映射方式包括直接映射,全相联映射和组相联映射,后者是前两者相结合的产物。C6678的L1P Cache采用的是直接映射,存储器中的code在L1P Cache中只有唯一确定的位置。
  C6678的L1D Cache采用的是两路组相联映射(two-way set associative cache),一个set里面有两个line frame,存储器中的数据映射到固定的set,但可以是任意的line frame。
在这里插入图片描述

  C6678的cache line frame是64字节,所以地址中的Offset字段固定为6bits。一个set是128字节,如果L1D的Cache大小为4kB,那么L1D Cache可以分为32个set,所以Set字段为5bits,剩下的就是Tag字段21bits。
在这里插入图片描述

  当L1D Cache设置为32kB时,set的数量也就变为了原来的8倍,因此Set字段需要8bits,相应的Tag字段减少为18bits。

TI Data Type

  调试过程中经常需要预先往存储器中加载数据,可以通过Memory Browser窗口里的Load Memory实现。
在这里插入图片描述

  需要加载的数据预先存在在一个“xxx.dat”文件里,按照TI Data的格式进行存放,在加载的时候CCS就会根据文件头的信息把数据加载到指定的位置。
在这里插入图片描述

  帧头的数都是十六进制表示的!可以在CCS的Help->Help Comtents里搜索“data format”找到相关的介绍。
在这里插入图片描述

  数据格式“9”表示一种比较新的数据格式说明方式,具体数据格式由最后一个数决定。数据个数就是数据的行数,因为一行一个数据。具体数据格式如下表:

具体数据格式(十六进制)含义
064bit Hex TI-style
164bit Hex C-style (带0x前缀)
264bit Floating Point
3Exponential Float (指数表示的浮点数)
432bit Hex TI-style
532bit Hex C-style
632bit Signed Int
732bit Unsigned Int
832bit Binary
932bit Floating Point
A32bit Exponential Float
B16bit Hex TI-style
C16bit Hex C-style
D16bit Signed Int
E16bit Unsigned Int
F16bit Binary
108bit Hex TI-style
118bit Hex C-style
128bit Signed Int
138bit Unsigned Int
148bit Binary
15Character
16Packed Char

QDMA的使用

  有时候需要手动启动DMA并且随时需要对PaRAM进行修改,而不是等待外部事件触发,那么就可以用QDMA来实现。QDMA能够在写完PaRAM时就启动DMA传输。实际应用过程中可以有这几点需要考虑:

  1. 硬件初始化
  2. 中断服务函数
  3. 启动传输

  下面是C6678上用于实现矩阵转置的一个类。三个成员函数分别对应我提到的三点要考虑的地方。

#ifndef CTSPSMANAGER_H
#define CTSPSMANAGER_H

#include <csl_edma3.h>

#define TPSP_PARAM_NUM  (10)
#define TSPS_QDMA_CHANNEL (0)
#define TSPS_TCC_CODE (8)
#define TSPS_SYSEVT_EDMA3R0 (38)
#define TSPS_REGION CSL_EDMA3_REGION_0

class CTspsManager
{
private:
    CSL_Edma3Handle m_hEdma;
    CSL_Edma3Obj m_Obj;
    Bool m_bInitDone;
    int m_nCicId;

public:
    CTspsManager();
    ~CTspsManager();

    int HwInit(int coreId);
    void startTranspose(
        void *restrict pSrc, 
        void *restrict pDst, 
        int nSrcWidth, 
        int nSrcHeight, 
        size_t nUnit);
    void ClearInterruptFlag();
};

#endif

  硬件初始化过程中,因为要配置中断,所以不同的核需要设置对应的片上中断控制器(CIC)。QDMA通道绑定队列与PaRAM,使能通道。同时也需要使能PaRAM中TCC code对应的DMA通道,和这个DMA通道的中断使能。清中断的时候需要清系统中断和EMDA的中断挂号寄存器。

int CTspsManager::HwInit(int coreId)
{
    CSL_Status status;
    CSL_Edma3HwSetup hwSetup;
    UInt nChannel;
    CSL_Edma3HwDmaChannelSetup hwDmaSetup[] =   CSL_EDMA3_DMACHANNELSETUP_DEFAULT;
    CSL_Edma3HwQdmaChannelSetup hwQdmaSetup[] = CSL_EDMA3_QDMACHANNELSETUP_DEFAULT;

    if(coreId < 0 || coreId > 7)
        return -1;

    if(m_bInitDone)
        return 0;

    m_hEdma = CSL_edma3Open(&m_Obj, CSL_EDMA3CC_0, NULL, &status);
    assert(m_hEdma != NULL);

    //--------------------- Hardware Setup --------------------------

    // DMA setup
    hwDmaSetup[TSPS_TCC_CODE].paramNum = TPSP_PARAM_NUM;
    hwDmaSetup[TSPS_TCC_CODE].que = CSL_EDMA3_QUE_0;

    hwSetup.dmaChaSetup = hwDmaSetup;
    // end of DMA setup

    // QDMA setup
    hwQdmaSetup[TSPS_QDMA_CHANNEL].paramNum = TPSP_PARAM_NUM;
    hwQdmaSetup[TSPS_QDMA_CHANNEL].que = CSL_EDMA3_QUE_0;
    hwQdmaSetup[TSPS_QDMA_CHANNEL].triggerWord = CSL_EDMA3_TRIGWORD_DEFAULT;
    hwSetup.qdmaChaSetup = hwQdmaSetup;
    // end of QDMA setup

    CSL_edma3HwSetup(m_hEdma, &hwSetup);
//--------------------- end of Hardware Setup --------------------------

//--------------------- Queue Priority Setup --------------------------
    CSL_edma3SetEventQueuePriority(m_hEdma, CSL_EDMA3_QUE_0,    CSL_EDMA3_QUE_PRI_0);
    CSL_edma3SetEventQueuePriority(m_hEdma, CSL_EDMA3_QUE_1,    CSL_EDMA3_QUE_PRI_1);
    CSL_edma3SetEventQueuePriority(m_hEdma, CSL_EDMA3_QUE_2,    CSL_EDMA3_QUE_PRI_2);
    CSL_edma3SetEventQueuePriority(m_hEdma, CSL_EDMA3_QUE_3, CSL_EDMA3_QUE_PRI_3);
//--------------------- end of Queue Priority Setup --------------------------

    CSL_edma3QdmaRegionAccessDisable(m_hEdma, TSPS_REGION, 0xF);
    CSL_edma3QdmaRegionAccessEnable(m_hEdma, TSPS_REGION, 1 << TSPS_QDMA_CHANNEL);
    CSL_edma3DmaRegionAccessDisable(m_hEdma, TSPS_REGION, 0xFFFFFFFF, 0xFFFFFFFF);
    CSL_edma3DmaRegionAccessEnable(m_hEdma, TSPS_REGION, 1 << TSPS_TCC_CODE, 0);
    CSL_edma3InterruptLoEnable(m_hEdma, TSPS_REGION, 1 << TSPS_TCC_CODE);

//--------------------- end of Region Access Setup --------------------------

    CSL_edma3QDMAChannelEnable(m_hEdma, TSPS_REGION, TSPS_QDMA_CHANNEL);
    CSL_edma3ClearDMAChannelEvent(m_hEdma, TSPS_REGION, TSPS_TCC_CODE);
    CSL_edma3DMAChannelEnable(m_hEdma, TSPS_REGION, TSPS_TCC_CODE);

    // CIC0 is used to connect EMDA finish interrupt to CPU
    m_nCicId = (coreId < 4) ? 0 : 1;
    nChannel = coreId % 4 * 11 + 33; // attach to host interrupt ID 22
    CpIntc_clearSysInt(m_nCicId, TSPS_SYSEVT_EDMA3R0);
    CpIntc_enableSysInt(m_nCicId, TSPS_SYSEVT_EDMA3R0);
    CpIntc_mapSysIntToHostInt(m_nCicId, TSPS_SYSEVT_EDMA3R0, nChannel);
    CpIntc_enableHostInt(m_nCicId, nChannel);
    CpIntc_enableAllHostInts(m_nCicId);

    return 0;
}

void CTspsManager::startTranspose(
    void *restrict pSrc, 
    void *restrict pDst, 
    int nSrcWidth, 
    int nSrcHeight, 
    size_t nUnit)
{
    CSL_Status status;
    CSL_Edma3ParamSetup paramSetup;
    CSL_Edma3ParamHandle hParam;

    Cache_wb(pSrc, nSrcWidth * nSrcHeight * nUnit, Cache_Type_ALLD, TRUE);

    hParam = CSL_edma3GetParamHandle(m_hEdma, TPSP_PARAM_NUM, &status);
    paramSetup.option = CSL_EDMA3_OPT_MAKE(CSL_EDMA3_ITCCH_EN, \
                                           CSL_EDMA3_TCCH_DIS, \
                                           CSL_EDMA3_ITCINT_DIS, \
                                           CSL_EDMA3_TCINT_EN, \
                                           TSPS_TCC_CODE, \
                                           CSL_EDMA3_TCC_NORMAL,\
                                           CSL_EDMA3_FIFOWIDTH_NONE, \
                                           CSL_EDMA3_STATIC_DIS, \
                                           CSL_EDMA3_SYNC_AB, \
                                           CSL_EDMA3_ADDRMODE_INCR, \
                                           CSL_EDMA3_ADDRMODE_INCR);
    paramSetup.srcAddr = (uint32_t)pSrc;
    paramSetup.aCntbCnt = CSL_EDMA3_CNT_MAKE(nUnit, nSrcHeight); // (acnt, bcnt)
    paramSetup.dstAddr = (uint32_t)pDst;
    paramSetup.srcDstBidx = CSL_EDMA3_BIDX_MAKE(nSrcWidth * nUnit,  nUnit); // (src, dst)
    paramSetup.linkBcntrld = CSL_EDMA3_LINKBCNTRLD_MAK  (CSL_EDMA3_LINK_NULL, 0);
    paramSetup.srcDstCidx = CSL_EDMA3_CIDX_MAKE(nUnit, nSrcHeight * nUnit); // (src, dst)
    paramSetup.cCnt = nSrcWidth;
    CSL_edma3ParamSetup(hParam, &paramSetup);
}

void CTspsManager::ClearInterruptFlag()
{
    CSL_edma3ClearLoPendingInterrupts(m_hEdma, TSPS_REGION, 1 << TSPS_TCC_CODE);
    CpIntc_clearSysInt(m_nCicId, TSPS_SYSEVT_EDMA3R0);
}

参考资料

  • SPRU187U-TMS320C6000 Optimizing Compiler v7.4
  • SPRUGH7-TMS320C66x DSP CPU and Instruction Set Reference Guide
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小裘HUST

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值