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.

[参考译文] CODECOMPOSER:编译器有时将易失性32位访问拆分为两个16位访问

Guru**** 2321300 points
Other Parts Discussed in Thread: C2000-CGT, C2000WARE
请注意,本文内容源自机器翻译,可能存在语法或其它翻译错误,仅供参考。如需获取准确内容,请参阅链接中的英语原文或自行翻译。

https://e2e.ti.com/support/tools/code-composer-studio-group/ccs/f/code-composer-studio-forum/1508588/codecomposer-compiler-sometimes-splits-a-volatile-32-bit-access-into-two-16-bit-accesses

部件号:CODECOMPOSER
"主题"中讨论的其他器件:C2000-CGTC2000WARE

工具/软件:

您好:

根据手册、C2000-CGT 中的 C 编译器不应更改易失性访问的大小( spru514y.pdf、J.3.10限定符中对此进行了说明)、但似乎有一种临界情况、此处实际上并非如此、其中编译器将32位易失性读取拆分为两次16位读取。

此问题的示例、使用"cl2000.exe -c99 -Ooff -v28 -k"编译:

volatile unsigned long value;

unsigned long foo_0(unsigned long arg) { return value & arg; }
unsigned long foo_1(unsigned long arg) { return value | arg; }
unsigned long foo_2(unsigned long arg) { return value ^ arg; }

在 Windows 下使用22.6.0.LTS、22.6.1.LTS 和22.6.2.LTS 进行编译时、上述示例编译为以下汇编指令:

_foo_0:
        ADDB      SP,#2                 ; [CPU_ARAU] 
        MOVL      *-SP[2],ACC           ; [CPU_ALU] |3| 
        MOVW      DP,#_value            ; [CPU_ARAU] 
        AND       AL,@_value            ; [CPU_ALU] |3| 
        AND       AH,@$BLOCKED(_value)+1 ; [CPU_ALU] |3| 
        SUBB      SP,#2                 ; [CPU_ARAU] 
        LRETR     ; [CPU_ALU] 

_foo_1:
        ADDB      SP,#2                 ; [CPU_ARAU] 
        MOVL      *-SP[2],ACC           ; [CPU_ALU] |4| 
        MOVW      DP,#_value            ; [CPU_ARAU] 
        OR        AL,@_value            ; [CPU_ALU] |4| 
        OR        AH,@$BLOCKED(_value)+1 ; [CPU_ALU] |4| 
        SUBB      SP,#2                 ; [CPU_ARAU] 
        LRETR     ; [CPU_ALU] 

_foo_2:
        ADDB      SP,#2                 ; [CPU_ARAU] 
        MOVL      *-SP[2],ACC           ; [CPU_ALU] |5| 
        MOVW      DP,#_value            ; [CPU_ARAU] 
        XOR       AL,@_value            ; [CPU_ALU] |5| 
        XOR       AH,@$BLOCKED(_value)+1 ; [CPU_ALU] |5| 
        SUBB      SP,#2                 ; [CPU_ARAU] 
        LRETR     ; [CPU_ALU] 

无论选择的优化级别如何、这个问题都存在、即使使用"-Ooff"完全关闭优化、并且当程序从仅支持32位访问的外设读取数据时、这也可能会导致问题。 或者、对于  具有中断的环境中的线程安全、依赖单个易失性32位读取/写入作为原子形式( 据我所知、这两种都是 C2000的预期方法、因为它们也在 C2000Ware 示例和驱动程序中使用)。

针对此问题的可能权变措施似乎是使用 __byte_peripheral_32内在函数读取32位数据、这似乎阻止编译器拆分访问、但它要求用户找到与此问题相关的源代码中的所有位置。

unsigned long foo_fixed(unsigned long arg) { return __byte_peripheral_32(&value) & arg; }

然后编译到:

_foo_fixed:
        ADDB      SP,#2                 ; [CPU_ARAU] 
        MOVL      *-SP[2],ACC           ; [CPU_ALU] |6| 
        MOVL      XAR4,#_value          ; [CPU_ARAU] |6| 
        MOVL      ACC,*+XAR4[0]         ; [CPU_ALU] |6| 
        AND       AL,*-SP[2]            ; [CPU_ALU] |6| 
        AND       AH,*-SP[1]            ; [CPU_ALU] |6| 
        SUBB      SP,#2                 ; [CPU_ARAU] 
        LRETR     ; [CPU_ALU] 

上述例子是我所知唯一涉及这一问题的例子。 如果我尝试对位域或不同运算符进行其他访问、编译器会一次正确加载整个32位字、而不会将它们拆分。

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

    假定应用是不正确的  易失性 使用32位宽访问意味着编译器限制为32位宽的内存访问指令。  相反、编译器需要使用的指令可使访问每一位数据的次数与源代码相同。  您在上面显示的序列满足该约束、即使它们采用两条16位宽指令来执行该操作。   

    我知道这不是你所期望的结果。  但事实就是这样 易失性 典型工作原理。

    有关更多背景信息、请参阅 此类似主题。  它导致问题 EXT_EP-9400 。  。 发行说明 遗憾的是、该问题的一部分被截断。  以下是完整的通道:

    C2000没有32位按位(AND、OR、XOR)指令、因此32位按位运算必须使用两条16位按位指令完成。 这意味着不可能以原子方式真正处理32位按位运算、因此不可能满足对32位易失性变量进行按位运算的原子要求。 在这种情况下、编译器应遵循以下规则:对于易失性读取、编译器将仅读取变量中的每个位一次、对于每个易失性写入、编译器将仅写入变量中的每个位一次。 这意味着只有两个有效的指令序列:两个直接按位运算到存储器、或一个32位加载、后跟按位操作、后跟32位写入。 在生成两个16位按位指令时、编译器会检查16位按位指令中是否有任何一个是 tautological 指令、例如二进制或具有全零常量、在这种情况下、它会丢弃 tautological 指令。 如果指令直接对易失性存储器进行操作、这是不合法的。 本例中的错误是编译器执行了该操作。

    谢谢。此致、

    -乔治

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

    你好,谢谢你回来给我! 我认为 不允许 TI 编译器从单个32位易失性读取生成两个16位读取、原因有两个。 编译器手册就易失性访问 (spru514y.pdf、J.3.10限定符) 进行了说明、我将其理解为、易失性32位读取足以确保编译器确实会使用32位访问:

    TI 编译器不会缩小或增加易失性访问。 用户有责任确保访问大小适合仅允许访问特定宽度的器件。

    但我看到这与你的解释并不冲突,所以这是我的一个误解。 但 第二个原因是、C2000架构的 C2000Ware 驱动程序和代码示例似乎也存在这种误解。 在 C2000Ware_5_04_00_00中、在文件 driverlib\f2838x\examples\c28x\interrupt\interrupt_ex1_external.c 中、有32位易失性变量用于计算已执行的中断服务例程的数量、这些计数器的值在主函数中读取、而不在执行读取之前关闭中断、这意味着在第一个16位之后会发生中断、但编译器实际上不会在第一个16位被拆分之前被执行、 但编译器将被允许在此处拆分它)。

     我查看的 C2000Ware_5_04_00_00的另一个示例来自 MCAN 外设器件的驱动程序代码、特别是关于如何 访问寄存器 MCAN_TXBRP (文件 driverlib\f2838x\driverlib\mcan.c、第1399行)、但它确实也适用于其他寄存器。 为了返回正确的数据、该寄存器专门需要32位访问、但驱动程序中的代码似乎只是依赖易失性的访问来确保这一点。

    因此有一个好处:以 C2000为目标时、在跨异步任务访问32位数据时、访问易失性是不够的、并且需要32位读取的外设实际上需要使用__byte_peripheral_32或 __attribute__((byte_peripheral))

    感谢您发送编修。 的确切含义volatile在不同的嵌入式架构之间始终略有不同、因此我会尽力 从可用的资源中了解这一点。