【问题标题】:Determining ARM Cortex M3 RAM Size at run time在运行时确定 ARM Cortex M3 RAM 大小
【发布时间】:2014-06-18 04:11:50
【问题描述】:

我正在为基于 STM32F103 的 ARM 微控制器(使用 GCC 编译)开发一些软件。我的一些用户希望能够将相同的二进制文件与同一芯片的不同版本(具有更多 RAM)一起使用,因此我需要一种方法来找出我在运行时有多少 RAM。

有一个地址 (0x1FFFF7E0) 包含闪存大小,但似乎没有包含 RAM 大小的地址!

显而易见的解决方案是运行内存地址检查哪些是可读/可写的,但我已经尝试过这个和从太高的 RAM 地址读取时的芯片 HardFaults(我不知道如何恢复)。

对于解决这个问题的最佳方法有什么想法吗?理想情况下,我会进行实验,因为一些芯片(比如我现在使用的 STM32F103RCT6)实际上似乎有 64kB 的 RAM,即使数据表显示它们有 48 个。例如,0x1FFFF7E0 寄存器报告 256kB 的可用闪存,即使 512kB 是可用的.

看起来我可以在 CCR 寄存器中设置 BFHFNMIGN 位,然后尝试从软件中断中访问内存 - 但是我不知道如何在 GCC+STM32 中调用或创建软件中断

【问题讨论】:

  • 好的,理想情况下我只需要一种方法来读取地址并查看它是否有效。看起来我可以通过设置 SHCSR.BUSFAULTENA 来打开 busfault 处理程序,然后我可以在 irq 处理程序中设置 SHCSR.BUSFAULTACT=0,但是从它返回后我仍然得到一个硬故障。跨度>
  • 顺便说一句,但在某些芯片上,0x1FFFF7E2 处的 2 个字节显示了工厂编程的 RAM 大小。在 F103RG 上就是这种情况,但在我拥有的 F103RC 上却不是。它也不是实际 RAM 大小 - 就像闪存大小一样,它是工厂编程的大小。
  • 您可能需要注意的一件事是,如果您发现 ram 不是用于用户代码的 - 我很确定某些 USB 芯片已经为用户代码不应该弄乱的芯片的usb部分。但是,如果您不使用这些功能,那也没关系...
  • 是的,但是我在一个非常有限的区域内搜索。例如,我知道 STM32F1 芯片可以配备高达 96kB 的 RAM,所以我只检查高达 0x20000000+96kB。据我所知,那个家庭的所有东西要么有用户 RAM,要么在那个区域没有任何东西。
  • 在生产系统上使用数据表中未提及的 RAM 是不是有点冒险?我只是想知道......当然,这部分 RAM 有 99.9% 的可能性会像其他任何部分一样,但不能保证这一点。

标签: gcc arm runtime ram cortex-m3


【解决方案1】:

在具有不同 SRAM 大小的 MCU 上运行相同二进制文件的一个相关问题 - 堆栈指针 (SP) 在复位时使用位置 0 中的值进行初始化。通常在构建程序时,编译器/启动文件/链接器脚本将 SRAM 的 TOP(适当对齐)的值放入位置 0。因此,当您的二进制文件放在具有更多 SRAM 的机器上时,堆栈现在不再位于 SRAM 的顶部。如果您想利用更大的 SRAM 大小,您的程序必须将堆栈重新定位到 SRAM 的真正顶部。

【讨论】:

    【解决方案2】:

    我发现,通过设置 FAULTMASK 位,禁用所有中断和故障处理程序,同时设置 BFHFNMIGN 位,可以探测访问地址是否会产生总线故障而不会引发异常。不引发异常的好处意味着您不必编写异常处理程序。下面的例子是一个做探测的 C 函数:

    /**
     * @brief Probe an address to see if can be read without generating a bus fault
     * @details This function must be called with the processor in privileged mode.
     *          It:
     *          - Clear any previous indication of a bus fault in the BFARV bit
     *          - Temporarily sets the processor to Ignore Bus Faults with all interrupts and fault handlers disabled
     *          - Attempt to read from read_address, ignoring the result
     *          - Checks to see if the read caused a bus fault, by checking the BFARV bit is set
     *          - Re-enables Bus Faults and all interrupts and fault handlers
     * @param[in] read_address The address to try reading a byte from
     * @return Returns true if no bus fault occurred reading from read_address, or false if a bus fault occurred.
     */
    bool read_probe (volatile const char *read_address)
    {
        bool address_readable = true;
    
        /* Clear any existing indication of a bus fault - BFARV is write one to clear */
        HWREG (NVIC_FAULT_STAT) |= NVIC_FAULT_STAT_BFARV;
    
        HWREG (NVIC_CFG_CTRL) |= NVIC_CFG_CTRL_BFHFNMIGN;
        asm volatile ("  CPSID f;");
        *read_address;
        if ((HWREG (NVIC_FAULT_STAT) & NVIC_FAULT_STAT_BFARV) != 0)
        {
            address_readable = false;
        }
        asm volatile ("  CPSIE f;");
        HWREG (NVIC_CFG_CTRL) &= ~NVIC_CFG_CTRL_BFHFNMIGN;
    
        return address_readable;
    }
    

    代码是为德州仪器 ARM 编译器编写的,使用德州仪器 TivaWare 软件进行寄存器定义,并在基于 Cortex-M4F 的 TM4C 设备上进行了测试。理论上,它应该可以在其他 Cortex-M3 或 Cortex-M4 设备上运行。

    【讨论】:

      【解决方案3】:

      好吧,我终于在ST论坛用户的帮助下想通了。

      首先,您需要启用 BusFault IRQ:

      SCB->SHCSR |= SCB_SHCSR_BUSFAULTENA;

      然后,您需要定义一个 BusFault 处理程序,它将程序计数器增加 2 以跳过有问题的指令(赌一把,它实际上是一条 2 字节指令):

      __attribute__ ((naked)) void BusFault_Handler(void) {
        /* NAKED function so we can be sure that SP is correct when we
         * run our asm code below */
      
        // DO NOT clear the busfault active flag - it causes a hard fault!
      
        /* Instead, we must increase the value of the PC, so that when we
         * return, we don't return to the same instruction.
         *
         * Registers are stacked as follows: r0,r1,r2,r3,r12,lr,pc,xPSR
         * http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0337e/Babedgea.html
         *
         * So we want PC - the 6th down * 4 bytes = 24
         *
         * Then we add 2 - which IS DANGEROUS because we're assuming that the op
         * is 2 bytes, but it COULD be 4.
         */
        __asm__(
            "ldr r0, [sp, #24]\n"  // load the PC
            "add r0, #2\n"         // increase by 2 - dangerous, see above
            "str r0, [sp, #24]\n"  // save the PC back
            "bx lr\n"              // Return (function is naked so we must do this explicitly)
        );
      }
      

      现在——终于——我们可以尝试从任意内存位置读取数据。如果错误,则会调用 BusFault 处理程序,但我们会跳过读取或写入指令,就好像它不存在一样。

      这意味着写入内存位置然后读回相对容易 - 如果你得到相同的东西,你知道它是有效的(你只需要确保你的代码不会被两个 str 所欺骗和 ldr 作为无操作)。

      【讨论】:

      • 您可能需要小心未完全寻址的内存或具有访问同一内存的替代地址。我认为 LPC1758 中有一些地址,例如使用更高地址的字节访问内存中的位。
      • 你需要有 32m 的 sram,然后才会意外地通过 ram 并进入位带别名部分:infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dai0179b/…
      • 另外,也许您可​​以通过使总线故障处理程序跳转到特定地址而不是计算一个地址来避免危险的 2 字节假设。
      • 我只是在相当狭窄的区域检查 RAM,因为我知道 STM32F103 芯片的最大 RAM 为 96kB - 幸运的是,这意味着我不应该碰到任何讨厌的东西。跳转到设定位置的问题是,如果我要在我离开的时候继续,它必须跳转到导致错误的同一函数中的标签,因此堆栈被保留 - 目前我离开了这个 IRQ一直活跃。
      • 哦,我原以为你会有一个在启动时运行的 ram-size-check 东西,然后你就会有一个从那时起使用的 ram 大小。
      【解决方案4】:

      除了闪存大小之外,还应该有一个寄存器,一个告诉您正在使用哪个部分的部件 ID,并从那里查找一个内存大小表。或者,如果闪存大小没有其他内容,您能否获得该芯片和闪存大小附带的一系列可能的 ram 大小?选择最小的。不确定这样的平台的哪种代码需要额外的内存或应用程序的可变大小的内存。很快你就会进入外围设备变化到足以成为问题的部分......

      【讨论】:

      • 问题是你最终得到的芯片有 64kB 的 RAM,但被标记为有 48。这是给 Espruino JS 解释器的——所以人们经常得到芯片并喜欢玩——如果有的话他们想使用额外的 RAM,即使(尤其是如果?)他们有一个不应该有它的芯片;)我认为实际上唯一的方法可能是通过实验进行检查。
      • 使用数据表中没有的 SRAM 总是“危险的”。基本上,芯片供应商不会保证其功能。所以它可能有效,但也可能随机失败。这意味着您可能应该使用不同的位模式执行更广泛的内存测试,以确保 RAM 空间确实可用。
      【解决方案5】:

      我的一些用户希望能够将相同的二进制文件与同一芯片的不同版本(具有更多 RAM)一起使用,因此我需要一种方法来找出我在运行时有多少 RAM。

      不,你不需要。只需为具有最小 RAM 量的芯片编译代码。二进制文件可以在更大的版本上使用而无需任何更改 - 多余的 RAM 将被闲置。

      【讨论】:

      • 我明白 - 但这是一种语言解释器,因此人们希望能够使用可用内存,这意味着确保堆栈指针/等位于正确的位置。
      • 这与您在问题中运行 same 二进制文件的说法相矛盾。如果不是二进制文件,你如何将解释后的程序放入 MCU?
      • espruino.com - 它直接从 RAM 执行 JavaScript 代码。新代码可以在运行时通过 USB/Serial 加载。
      猜你喜欢
      • 2011-10-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-16
      • 2018-09-29
      • 2014-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多