This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

[参考译文] TMS320F28379D:CLA 循环展开会注入不必要的 NOP 指令

Guru**** 2463330 points


请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/microcontrollers/c2000-microcontrollers-group/c2000/f/c2000-microcontrollers-forum/1499603/tms320f28379d-cla-loop-unrolling-injects-unnecessary-nop-instructions

部件号:TMS320F28379D

工具/软件:

我将比较几种向 CLA 协处理器和 C28x 主处理器传递数据的不同方法。 我希望数据传输是可扩展的、因此使用类似 memcpy 的例程来执行复制、但是、我遇到了 CLA 的预期问题、并且缺少 RPTB 指令。 我要了解的方法有:

void element_by_element_copy(struct_t * in, struct* out){
    out->field_1 = in->field_2;
    ...
    ...
    out->field_n = in->field_n;
}

void memcpy(struct_t * in, struct* out){
    uint32_t *src = in;
    uint32_t *dst = in;
    for (uint16_t i = 0; i < (sizeof(struct_t) / 2); i++){
        dst[i] = src[i];
    }
}

void unroll_memcpy(struct_t * in, struct* out){
    uint32_t *src = in;
    uint32_t *dst = in;
    
    #pragma UNROLL(sizeof(struct_t) / 2)
    for (uint16_t i = 0; i < (sizeof(struct_t) / 2); i++){
        dst[i] = src[i];
    }
}

正如预期的那样、逐元素复制的性能最高、但我注意到使用 UNROLL pragma 与 for 循环会得到几乎相同的代码。 唯一的区别似乎是在展开循环时、CLA 编译器会复制分支所需的不必要的 MNOP 指令。 请参阅下面的汇编输出示例:

/* Assembly generation - Element by Element Copy */
        ; MAR0 assigned to dst_buff;
        
        ; Copy first element
        MMOV32    MR0,@src_buff         ; [CPU_FPU]
        MMOV32    *MAR0,MR0             ; [CPU_FPU]
	    ; Copy second element
        MMOV32    MR0,@src_buff+2       ; [CPU_FPU]
        MMOV32    *MAR0+[#2],MR0        ; [CPU_FPU]
	    ...
	    ...
	    ; Copy nth element
        MMOV32    MR0,@src_buff+n       ; [CPU_FPU]
        MMOV32    *MAR0+[#n],MR0        ; [CPU_FPU]
        
        
/* Assembly generation - Unrolled For Loop */
        ; MAR0 assigned to dst_buff;
        
        ; Copy 1st 32 bits
        MMOV32    MR0,@src_buff         ; [CPU_FPU]
        MMOV32    *MAR0,MR0             ; [CPU_FPU]
        MNOP      ; [CPU_FPU] 
        MNOP      ; [CPU_FPU] 
        MNOP      ; [CPU_FPU]
        ; Second 32 bits
        MMOV32    MR0,@src_buff+2       ; [CPU_FPU]
        MMOV32    *MAR0+[#2],MR0        ; [CPU_FPU]
        MNOP      ; [CPU_FPU] 
        MNOP      ; [CPU_FPU] 
        MNOP      ; [CPU_FPU]
        ...
	    ...
	    ; Copy nth 32 bits
        MMOV32    MR0,@src_buff+n       ; [CPU_FPU]
        MMOV32    *MAR0+[#n],MR0        ; [CPU_FPU]
        
        

那么、我的问题是、是否可以在展开循环时强制 CLA 编译器删除这些 NOP 指令? 我假设在循环仅部分展开的情况下会注入 NOP、但在我们的用例中、最好完全展开循环、因为这需要针对速度而不是代码大小进行优化。

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我尝试重现您的结果。  但我不知道。。。

    Unknown 说:
    ]逐个元素复制的性能最高、但我注意到使用带有 for 循环的 UNROLL pragma 会得到几乎相同的代码。

    附加 一个我可以构建到汇编中的源文件。  注意、它不必运行。  我只需要检查生成的汇编代码。  复制并粘贴编译器选项的文本、使其与编译器所看到的完全相同。  不要使用屏幕截图。  还要告诉我编译器的版本。

    谢谢。此致、

    -乔治

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    您好、George、

    编译器版本: v22.6.0.LTS

    编译器调用:

    cl2000 -v28 -ml -mt --cla_support=cla1 --float_support=fpu32 \
    --idiv_support=none --isr_save_vcu_regs=off --tmu_support=tmu0 \
    --vcu_support=vcu2 -O2 --opt_for_speed=2 --fp_mode=relaxed --fp_reassoc=off \
    --include_path="C:/ti/ccs1200/ccs/tools/compiler/ti-cgt-c2000_22.6.0.LTS/include" \
    --advice:performance=none -g --symdebug:dwarf_version=4 --c99 --relaxed_ansi \
    --float_operations_allowed=all --fp_single_precision_constant --diag_warning=225 \
    --diag_wrap=off --display_error_number --issue_remarks --quiet --abi=eabi \
    --cla_background_task=off --cla_signed_compare_workaround=off \
    --silicon_errata_fpu1_workaround=on --disable_inlining -k --parallel=8 \
    --obj_directory="source/manual"  "copy_from_msgram.cla"

    不知道原因、但似乎我无法上传文件。 我已将测试文件的内容放到下面的代码块中(已确认进行编译、并可以验证生成的汇编结果仍然会产生我在我的计算机上讨论的问题、希望也会出现在您的计算机上)。

    将 COPY_METHOD 宏设置为 ELEMENT_BY_ELEMENT、FOR_LOOP、UNROLL 或 MANUAL_UNROLL 可显示我观察到的行为。  

    文件: copy_from_msgram.cla

    // Sample file for reproducing issues with CLA compiler for-loop UNROLLs
    
    
    #include <stdint.h>
    #include <float.h>
    
    
    typedef struct struct_group1 {
        float val_1;
        float val_2;
        float val_3;
        float val_4;
      } group1_t;
    
    typedef struct struct_group2 {
        float val_1;
        float val_2;
        float val_3;
      } group2_t;
    typedef struct struct_c28x_to_cla {
        uint16_t val_1;
        float        val_2;
        float        val_3;
        float        val_4;
        float        val_5;
        group1_t     val_6;
        uint16_t val_7;
        float        val_8;
        uint16_t val_9;
        float        val_10;
        float        val_11;
        float        val_12;
        float        val_13;
        uint16_t val_14;
        float        val_15;
        float        val_16;
        float        val_17;
        uint16_t val_18;
        uint16_t val_19;
        group2_t     val_20;
    } c28x_to_cla_t;
    
    c28x_to_cla_t global_buff;
    
    
    // Test Configurations
    #define ELEMENT_BY_ELEMENT 1
    #define UNROLL 2
    #define FOR_LOOP 3
    #define MANUAL_UNROLL 4
    
    #define COPY_METHOD MANUAL_UNROLL
    
    void copy_from_msgram(c28x_to_cla_t *data_out) {
    
    #if (COPY_METHOD == UNROLL) || (COPY_METHOD == FOR_LOOP)
        // For Loop implementations
        uint32_t *dst = (uint32_t *)data_out;
        uint32_t *src = (uint32_t *)&global_buff;
    
        #if (COPY_METHOD == UNROLL) // UNROLL
        #pragma UNROLL(sizeof(c28x_to_cla_t) / 2) // Expect to always to be 32 bit aligned.
        #endif
    
        for (uint16_t i = 0; i < (sizeof(c28x_to_cla_t) / 2); i++){
            dst[i] = src[i];
        }
    
    #elif (COPY_METHOD == MANUAL_UNROLL)
        // Manual unrolling
        uint32_t *dst = (uint32_t *)data_out;
        uint32_t *src = (uint32_t *)&global_buff;
    
        // Expect sizeof(global_buff) == 48. So, 24 32 bit values to copy.
        dst[0] = src[0];
        dst[1] = src[1];
        dst[2] = src[2];
        dst[3] = src[3];
        dst[4] = src[4];
        dst[5] = src[5];
        dst[6] = src[6];
        dst[7] = src[7];
        dst[8] = src[8];
        dst[9] = src[9];
        dst[10] = src[10];
        dst[11] = src[11];
        dst[12] = src[12];
        dst[13] = src[13];
        dst[14] = src[14];
        dst[15] = src[15];
        dst[16] = src[16];
        dst[17] = src[17];
        dst[18] = src[18];
        dst[19] = src[19];
        dst[20] = src[20];
        dst[21] = src[21];
        dst[22] = src[22];
        dst[23] = src[23];
    
    #else 
        /*Element by Element Assignment */
        data_out->val_1        = global_buff.val_1;
        data_out->val_2        = global_buff.val_2;
        data_out->val_3        = global_buff.val_3;
        data_out->val_4        = global_buff.val_4;
        data_out->val_5        = global_buff.val_5;
        data_out->val_6.val_1  = global_buff.val_6.val_1;
        data_out->val_6.val_2  = global_buff.val_6.val_2;
        data_out->val_6.val_3  = global_buff.val_6.val_3;
        data_out->val_6.val_4  = global_buff.val_6.val_4;
        data_out->val_7        = global_buff.val_7;
        data_out->val_8        = global_buff.val_8;
        data_out->val_9        = global_buff.val_9;
        data_out->val_10       = global_buff.val_10;
        data_out->val_11       = global_buff.val_11;
        data_out->val_12       = global_buff.val_12;
        data_out->val_13       = global_buff.val_13;
        data_out->val_14       = global_buff.val_14;
        data_out->val_15       = global_buff.val_15;
        data_out->val_16       = global_buff.val_16;
        data_out->val_17       = global_buff.val_17;
        data_out->val_18       = global_buff.val_18;
        data_out->val_19       = global_buff.val_19;
        data_out->val_20.val_1 = global_buff.val_20.val_1;
        data_out->val_20.val_2 = global_buff.val_20.val_2;
        data_out->val_20.val_3 = global_buff.val_20.val_3;
    #endif
    
    } /* End of function copy_from_msgram */

    感谢您与我一起探讨这个问题、

    -泰勒

     

  • 请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

    我找到了一种解决方法。  但我只能给出部分的解释。

    添加 限制 关键字到指针的定义...

        uint32_t * restrict dst = (uint32_t *)data_out;
        uint32_t * restrict src = (uint32_t *)&global_buff;

    否则、编译器必须将表达式假定为类似 DST[0] src [1] 可能会引用相同的地址。  如果它们引用同一地址、则需要额外的 MNOP 指令以允许写入 DST[0] 完成后才能读取 src [1] 可以开始。  我不知道 CLA 的流水线行为足以提供更具体的详细信息。

    谢谢。此致、

    -乔治