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.

[参考译文] TMS570LS3137:从 UART ISR 调用函数时、nError/ECC LED 激活

Guru**** 2964790 points

Other Parts Discussed in Thread: HALCOGEN, TMS570LS3137

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

https://e2e.ti.com/support/microcontrollers/arm-based-microcontrollers-group/arm-based-microcontrollers/f/arm-based-microcontrollers-forum/1661497/tms570ls3137-nerror-ecc-led-activates-when-calling-a-function-from-uart-isr

器件型号: TMS570LS3137
主题中讨论的其他器件: HALCOGEN、

您好:

我使用的是 TMS570LS3137 与 HALCoGen 4.07.01、CCS 12.2 和 TI ARM 编译器 20.2.7.LTS。

该工程在禁用优化的情况下进行编译:

  -mv7R4
  ——code_state=32
  -Ooff
  -- abi=eabi

应用中使用了两条 UART 接收路径:

1. sciREG:

  • VIM channel: 74
  • ISR: comp_pod_sci_low_level_interrupt
  • Receive interrupt vector: INTVECT1 == 11

2. scilinREG:

  • VIM channel: 27
  • ISR: comp_maintenance_sci_low_level_interrupt
  • Receive interrupt vector: INTVECT1 == 11

两个外设都配置了 UART_INT_LEVEL_1。 在两条中断路径上会观察到相同问题。

我的 UART 接收 ISR 声明如下:

    #pragma CODE_STATE(comp_pod_sci_low_level_interrupt, 32)
    #pragma INTERRUPT(comp_pod_sci_low_level_interrupt, IRQ)

    void comp_pod_sci_low_level_interrupt(void)
    {
        if (g_s_cp != NULL)
        {
            uint32_t vec = g_s_cp->pod_com.instance->INTVECT1;

            if (11U == vec)
            {
                uint8_t received_data =(uint8_t)(g_s_cp->pod_com.instance->RD & 0x00FFU);

                comp_pod_ring_buf_put(g_s_cp, received_data);
            }
        }
    }

被调用函数为:

    static result_enum_t comp_pod_ring_buf_put(comp_pod_struct_t *const p_cp, uint8_t data)
    {
        if (NULL == p_cp)
        {
            return RESULT_ERR_NULL;
        }

        uint16_t next = (p_cp->ring_buf.head + 1U) & POD_RING_BUF_MASK;

        if (next != p_cp->ring_buf.tail)
        {
            p_cp->ring_buf.buffer[p_cp->ring_buf.head] = data;
            p_cp->ring_buf.head = next;
        }

        return RESULT_SUCCESS;
    }

从 ISR 调用 comp_pod_ring_buf_put () 时、板的 ECC/nError LED 变为活动状态。

但是、应用程序会继续正常运行。 它不会保留在 ramErrorReal、flashErrorReal 或其他数据中止循环中。

如果我将相同的振铃缓冲区操作直接复制到 ISR 中、LED 不会变为活动状态:

    uint16_t next = (g_s_cp->ring_buf.head + 1U) & POD_RING_BUF_MASK;

    if (next != g_s_cp->ring_buf.tail)
    {
        g_s_cp->ring_buf.buffer[g_s_cp->ring_buf.head] = received_data;

        g_s_cp->ring_buf.head = next;
    }

在具有等效振铃缓冲功能的另一个 UART 组件中可以观察到相同的行为。

我还将 IRQ 栈从 0x100 字节增加到 0x400 字节、并相应地调整了链接器 RAM 边界。 这没有改变行为、因此简单的 IRQ 栈溢出似乎不是原因。

由于禁用了优化、因此函数不会内联。 调用它会添加一个 BL 指令、从另一个闪存地址执行代码并创建一个额外的函数堆栈帧。 将函数体复制到 ISR 中可以避免这些操作。

什么原因可能导致此问题?

此致、
Hasan

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

    尊敬的 Hasan:

    我建议您验证设置了哪个 ESM 位。 您可以参阅以下 e2e、了解如何验证这些位:

    (+) TMS570LC4357:ESMIOFFHR — 基于 Arm 的微控制器论坛 — 基于 Arm 的微控制器 — TI E2E 支持论坛中出现 ESM 高中断且无挂起中断

    我们有一个内部 AI 可以分析我们所有的内部数据库,它提供了以下建议,请参阅这一个:

    ——

    ##根本原因分析

    ###核心问题:未初始化的 IRQ 堆栈内存和 ECC

    TMS570LS3137 使用** TCRAM (SRAM) 和硬件 ECC**。 每隔** 64 位(双字)读取**计算和检查一次 ECC。 如果自上电以来从未写入过存储器位置、则其 ECC 位处于不确定状态。 读取此类位置将触发** ECC single-bit 校正或 double-bit 检测错误**、这将断言` nError `引脚并点亮 ECC LED。

    您观察到的主要区别是:

    |场景|行为|
    |--|--|---|
    |环缓冲代码**内联**在 ISR 中|无 ECC 错误|
    |**调用函数**中的振铃缓冲区代码|触发 ECC 错误|

    这种差异完全由**栈帧使用**来解释:

    -当代码**内联**时,编译器只使用已分配给 ISR 栈帧的寄存器—不会触及新的栈区域。
    -当`comp_pod_ring_buf_put ()`为**调用时,编译器会为该函数生成一个新的栈帧。 这会将链接寄存器 (`LR`)、可能`R4`–`r11`和局部变量推到** IRQ 栈**上的地址、**之前从未写入**。 后续函数结尾**读回**这些堆栈位置(通过`POP`/`LDMFD`恢复寄存器)、如果这些地址从未使用有效 ECC 进行初始化、则读取会触发 ECC 故障。

    >**简而言之:函数调用会导致一个堆栈`push`和一个`pop`。 `POP`读取从未初始化 ECC 的存储器、从而触发故障。**

    将栈大小增加到`0x400`没有任何帮助、因为问题不是*溢出*、那就是**新分配的栈页从未被写入**(因此没有有效的 ECC 综合征)。

    ——

    ##为什么应用程序继续运行

    TMS570 ECC 逻辑可区分以下各项:
    -*单一位错误(可纠正):* SECDED 硬件校正数据并断言`nError`,但继续执行。 这完全符合您的观察结果—LED 指示灯亮起、但应用程序仍在运行。
    -**双位错误(不可纠正):**这会导致数据中止。

    您在未初始化的 TCRAM**上遇到**单个位 ECC 错误、这些错误在硬件中已校正、但仍会断言` nError `。

    ——

    ##解决方案

    ### White check mark 解决方案 1:启动时初始化整个 IRQ 栈区域(推荐)

    标准修复是**在启动期间、在启用任何中断之前、将已知模式写入整个 IRQ 堆栈**。 这会为堆栈区域中的每个地址初始化 ECC。

    HALCoGen 生成的启动代码 (`sys_startup.c`) 通常通过`_memInit_`例程对整个 SRAM 执行此操作、但**如果 IRQ 栈区域最近进行了放大或链接器脚本已修改、它可能不会覆盖该区域**。

    检查您的 n ü` ys_startup.c `s是否有如下呼叫:

    ```c
    /*初始化 TCRAM ECC */
    _memInit_();
    ```μ s

    并`**整个 IRQ 栈区域**(如链接器`.cmd `文件中定义)是否在 Δ r_memInit_`涵盖的范围内。 如果您将堆栈从`0x100`扩大到`0x400`并调整了链接器 RAM 边界、请确认新边界反映在启动 ECC 初始化范围中。

    ——

    ### White check mark 解决方案 2:在您的启动代码中显式对栈进行零初始化

    在启用 VIM/中断之前、在启动程序集或 C 启动代码中添加显式堆栈清理:

    ```ASM
    ;在 sys_startup.asm 或等效格式中
    ;切换到 IRQ 模式并将堆栈归零
    MSR CPSR_c、#0xD2;IRQ 模式、IRQ/FIQ 已禁用
    MOV r0、#0
    MOV R1、#0
    ;...使用 STM/STMIA 循环填充 IRQ 堆栈区域
    ```μ s

    或在 C 中(在`main ()`之前或启用中断之前):

    ```c
    extern uint32_t __IRQ_STACK_START__;
    extern uint32_t __IRQ_STACK_END__;
    uint32_t *p =&__IRQ_STACK_START__;
    while (p <&__IRQ_STACK_END__){
    * p++= 0xDEADBEEFU;/*任何写入初始化 ECC */
    }
    ```μ s

    ——

    ### White check mark 解决方案 3:验证 HALCoGen`_memInit_`覆盖率

    在 HALCoGen 生成的工程中、打开`sys_startup.c`并查找 TCRAM 初始化块。 它通常使用**MINITGCR**和**MSIENA**寄存器来触发硬件内存初始化。 确认:

    1.硬件初始化涵盖包括堆栈在内的整个 SRAM 范围。
    2. init 完成(轮询状态位)**之前**`vimInit()`或任何中断启用。

    ——

    ### White check mark 解决方案 4:使用`__ attribute__(( noinline ))`仔细/重构

    这是一个**变通办法、不是一个修复程序**、但确认了诊断:如果将`comp_pod_ring_buf_put`标记为`__attribute__((always_inline))`、ECC 错误就会消失—因为没有创建新的堆栈帧。 您已经在手动内联中观察到了这一点。

    ——

    ##摘要检查清单

    -[]确认`s_memInit_()` in ` ys_startup.c`涵盖链接器脚本更改后的**完整 IRQ stack**地址范围。
    -[]确认内存初始化完成**之前**`vimInit()`/中断启用。
    -[]如果使用自定义链接器`.cmd `,请验证 IRQ 栈段是否放置在 ECC 清理覆盖的区域中。
    -[]有关 TCRAM ECC 初始化的任何相关器件勘误表、请查看 TI 的 TMS570LS3137 (SPNZ193) 勘误表。

    ——

    > Warning️**注意:**本分析基于对 TMS570LS3137 架构、ARM Cortex-R4 ECC 行为和 TI ARM 编译器行为的一般专家知识、因为这个特定问题不在我可用的 EDA 工具知识库的范围内。 我建议交叉参考[TI E2E 社区论坛](https://e2e.ti.com) 和 TMS570LS3137 技术参考手册 (SPNU489)、以进行权威确认。

    --
    此致、
    Jagadish。