在运行时确定ARM Cortex M3 RAM大小

时间:2014-05-01 16:21:05

标签: gcc arm runtime ram cortex-m3

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

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

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

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

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

5 个答案:

答案 0 :(得分:1)

是的,我终于在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作为no-ops)。

答案 1 :(得分:1)

我发现通过设置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;
}

该代码是为Texas Instruments ARM编译器编写的,使用Texas Instruments TivaWare软件进行寄存器定义,并在基于Cortex-M4F的TM4C器件上进行了测试。从理论上讲,它应该适用于其他Cortex-M3或Cortex-M4器件。

答案 2 :(得分:1)

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

答案 3 :(得分:0)

应该有一个除闪光灯尺寸以外的寄存器,一个零件ID应该告诉你正在使用哪个部件,并从那里查找一个ram尺寸表。或者,如果闪光灯尺寸没有别的,你可以获得芯片和闪光灯尺寸附带的一系列可能的冲头尺寸?选择最小的。不确定这样的平台的代码类型是否需要额外的ram或应用程序的可变大小的ram。很快你会进入一个外围变化足以成为问题的部分......

答案 4 :(得分:-1)

  

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

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