STM32F407 添加外部 SRAM

发布于 2019-08-12 22:21:46

本帖最后由 wuhanstudio 于 2019-8-12 22:36 编辑

为什么要添加外部 SRAM

最近准备在 RT-Thread 上加载一些机器学习模型,通常做目标检测 (object detection) 标准模型 (Yolov3, SSD) 一般在 200MB-300MB,精简版 (tiny-yolov3, tiny ssd) 也在 20MB-30MB,一些特别精简的模型 (SqueezeDet) 可以只有 4MB,但是 STM32 的 SRAM 都是按照 KB 来计算的,所以要加载模型无法避免地要用到外部 SRAM,于是决定在 STM32F407ZGT6 上面外扩一个 SRAM。

SRAM 种类

板子上的 SRAM 是 IS62WV51216 (1MB),虽然很明显就算外扩了这个 1MB 的内存,也是没办法完整加载模型的,但是最后还是试了一下,因为以前没有尝试过外扩 SRAM,刚好也试一试做个总结。

其实如果要加载大模型,应当用 SDRAM (Synchronous Dynamic Random Access Memory) 而不是 SRAM (Static Random Access Memory),这两者的区别在于静态随机存储器 (SRAM) 是使用具有记忆功能的触发器 (Flip-Flop),它物理材料是比较占用空间的晶体管 (Transistor),而动态随机存储器 (SDRAM) 则是用的电容 (Capacitor),于是相同大小的物理空间,如果 SDRAM 可以存储几个 GB 的数据,那么 SRAM 只能保存几十 MB 的数据。因此相同存储容量 SRAM 芯片尺寸就会比 SDRAM 大很多,电脑的大内存也是 DDR SDRAM。

但是 SRAM 也有好处,因为它用的是晶体管 (Transistor),所以只要上电了它的数据就可以自动保存;如果是 SDRAM 的话,因为它用的是电容 (Capacitor),电量就会逐渐衰减导致数据丢失,因此需要反复刷新充电。这样的结果就是 SRAM 操作起来非常容易,不需要像 SDRAM 那样需要有同步时钟反复刷新。另一方便,晶体管 (Transistor) 的开断速度通常要远快于电容 (Capacitor) 的充电速度,所以 SRAM 的速度也会比 SDRAM 快很多。

总结一下,就是 SRAM 速度快,容量小,操作简单;SDRAM 速度相对慢,容量大,需要反复刷新。

[url=http://www.differencebetween.net/object/difference-between-sram-and-sdram/]http://www.differencebetween.net/object/difference-between-sram-and-sdram/[/url]

RT-Thread 外置 SRAM

要在 RT-Thread 上使用外置的 SRAM,可以利用 STM32 的 FSMC,首先利用 CubeMX 根据自己的 SRAM 时序配置,并点击 Generate Code 生成代码:

[/size]
[attach]10084[/attach]
[size=2]
/size然后在自己 bsp 下添加 sram 的驱动,例如在 F:rt-threadbspstm32stm32f407-atk-explorerboardports 里添加 drv_sram.c/size
[/backcolor]

#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>

#ifdef BSP_USING_SRAM
#include <sram_port.h>

#define DRV_DEBUG
#define LOG_TAG             "drv.sram"
#include <drv_log.h>

static SRAM_HandleTypeDef hsram;
static FMC_NORSRAM_TimingTypeDef SRAM_Timing;
#ifdef RT_USING_MEMHEAP_AS_HEAP
static struct rt_memheap system_heap;
#endif

static int SRAM_Init(void)
{
    int result = RT_EOK;

    /* SRAM device configuration */
    hsram.Instance  = FMC_NORSRAM_DEVICE;
    hsram.Extended  = FMC_NORSRAM_EXTENDED_DEVICE;

    /* SRAM device configuration */
    SRAM_Timing.AddressSetupTime       = ADDRESSSETUPTIME;
    SRAM_Timing.AddressHoldTime        = ADDRESSHOLDTIME;          /* Min value, Don't care on SRAM Access mode A */
    SRAM_Timing.DataSetupTime          = DATASETUPTIME;
    // SRAM_Timing.DataHoldTime           = DATAHOLDTIME;
    SRAM_Timing.BusTurnAroundDuration  = BUSTURNAROUNDDURATION;
    SRAM_Timing.CLKDivision            = CLKDIVISION;              /* Min value, Don't care on SRAM Access mode A */
    SRAM_Timing.DataLatency            = DATALATENCY;              /* Min value, Don't care on SRAM Access mode A */
    SRAM_Timing.AccessMode             = ACCESSMODE;

    hsram.Init.NSBank             = FSMC_NORSRAM_BANK3;
    hsram.Init.DataAddressMux     = FSMC_DATA_ADDRESS_MUX_DISABLE;
    hsram.Init.MemoryType         = FSMC_MEMORY_TYPE_SRAM;
#if SRAM_DATA_WIDTH == 8
    hsram.Init.MemoryDataWidth    = FMC_NORSRAM_MEM_BUS_WIDTH_8;
#elif SRAM_DATA_WIDTH == 16
    hsram.Init.MemoryDataWidth    = FMC_NORSRAM_MEM_BUS_WIDTH_16;
#else
    hsram.Init.MemoryDataWidth    = FMC_NORSRAM_MEM_BUS_WIDTH_32;
#endif
    hsram.Init.BurstAccessMode    = FSMC_BURST_ACCESS_MODE_DISABLE;
    hsram.Init.WriteOperation     = FSMC_WRITE_OPERATION_ENABLE;
    hsram.Init.WaitSignal         = FSMC_WAIT_SIGNAL_DISABLE;
    hsram.Init.WaitSignalActive   = FSMC_WAIT_TIMING_BEFORE_WS;
    hsram.Init.WaitSignalPolarity = FSMC_WAIT_SIGNAL_POLARITY_LOW;
    hsram.Init.ExtendedMode       = FSMC_EXTENDED_MODE_DISABLE;
    hsram.Init.AsynchronousWait   = FSMC_ASYNCHRONOUS_WAIT_DISABLE;
    hsram.Init.WriteBurst         = FSMC_WRITE_BURST_DISABLE;
    hsram.Init.ContinuousClock    = FSMC_CONTINUOUS_CLOCK_SYNC_ONLY;
    // hsram.Init.WriteFifo          = FSMC_WRITE_FIFO_DISABLE;
    // hsram.Init.NBLSetupTime       = 0;
    hsram.Init.PageSize           = FSMC_PAGE_SIZE_NONE;

    /* Initialize the SRAM controller */
    if (HAL_SRAM_Init(&hsram, &SRAM_Timing, &SRAM_Timing) != HAL_OK)
    {
        LOG_E("SRAM init failed!");
        result = -RT_ERROR;
    }
    else
    {
        LOG_D("sram init success, mapped at 0x%X, size is %d bytes, data width is %d", SRAM_BANK_ADDR, SRAM_SIZE, SRAM_DATA_WIDTH);
#ifdef RT_USING_MEMHEAP_AS_HEAP
        /* If RT_USING_MEMHEAP_AS_HEAP is enabled, SRAM is initialized to the heap */
        rt_memheap_init(&system_heap, "sram", (void *)SRAM_BANK_ADDR, SRAM_SIZE);
#endif
    }

    return result;
}
INIT_DEVICE_EXPORT(SRAM_Init);

#ifdef DRV_DEBUG
#ifdef FINSH_USING_MSH
int sram_test(void)
{
    int i = 0;
    uint32_t start_time = 0, time_cast = 0;
#if SRAM_DATA_WIDTH == 8
    char data_width = 1;
    uint8_t data = 0;
    uint8_t *ptr = (uint8_t *)SRAM_BANK_ADDR;
#elif SRAM_DATA_WIDTH == 16
    char data_width = 2;
    uint16_t data = 0;
    uint16_t *ptr = (uint16_t *)SRAM_BANK_ADDR;
#else
    char data_width = 4;
    uint32_t data = 0;
    uint32_t *ptr = (uint32_t *)SRAM_BANK_ADDR;
#endif

    /* write data */
    LOG_D("Writing the %ld bytes data, waiting....", SRAM_SIZE);
    start_time = rt_tick_get();
    for (i = 0; i < SRAM_SIZE / data_width; i++)
    {
#if SRAM_DATA_WIDTH == 8
        ((__IO uint8_t *)ptr) = (uint8_t)0x55;
#elif SRAM_DATA_WIDTH == 16
        ((__IO uint16_t *)ptr) = (uint16_t)0x5555;
#else
        ((__IO uint32_t *)ptr) = (uint32_t)0x55555555;
#endif
    }
    time_cast = rt_tick_get() - start_time;
    LOG_D("Write data success, total time: %d.%03dS.", time_cast / RT_TICK_PER_SECOND,
          time_cast % RT_TICK_PER_SECOND / ((RT_TICK_PER_SECOND * 1 + 999) / 1000));

    /* read data */
    LOG_D("start Reading and verifying data, waiting....");
    for (i = 0; i < SRAM_SIZE / data_width; i++)
    {
#if SRAM_DATA_WIDTH == 8
        data = ((__IO uint8_t *)ptr);
        if (data != 0x55)
        {
            LOG_E("SRAM test failed at %d!", i);
            continue;
        }
#elif SRAM_DATA_WIDTH == 16
        data = ((__IO uint16_t *)ptr);
        if (data != 0x5555)
        {
            LOG_E("SRAM test failed at %d!", i);
            continue;
        }
#else
        data = ((__IO uint32_t *)ptr);
        if (data != 0x55555555)
        {
            LOG_E("SRAM test failed at %d!", i);
            continue;
        }
#endif
    }

    if (i >= SRAM_SIZE / data_width)
    {
        LOG_D("SRAM test end!");
    }

    return RT_EOK;
}
MSH_CMD_EXPORT(sram_test, sram test);
#endif /* FINSH_USING_MSH */
#endif /* DRV_DEBUG */
#endif /* BSP_USING_SRAM */

以及对应的配置文件 sram_port.h

#ifndef __SDRAM_PORT_H__
#define __SDRAM_PORT_H__

/* parameters for sdram peripheral */
/* Bank1 */
#define SRAM_TARGET_BANK                3
/* stm32f4 Bank1:0x60000000 */
#define SRAM_BANK_ADDR                  ((uint32_t)0x68000000)
/* data width: 8, 16, 32 */
#define SRAM_DATA_WIDTH                 16
/* sram size */
#define SRAM_SIZE                       ((uint32_t)0x100000)

/* Timing configuration for IS61WV102416BLL-10MLI */
#define ADDRESSSETUPTIME                0
#define ADDRESSHOLDTIME                 0
#define DATASETUPTIME                   9
#define DATAHOLDTIME                    1
#define BUSTURNAROUNDDURATION           0
#define CLKDIVISION                     0
#define DATALATENCY                     0
#define ACCESSMODE                      FSMC_ACCESS_MODE_A
/* Timing configuration for IS61WV102416BLL-10MLI */

#endif

[size=2]还需要需要修改对应的 Sconscript,这样就生成项目就可以自动添加文件了,例如 F:rt-threadbspstm32stm32f407-atk-explorerboardSconscript 里添加:[/size]
[code]if GetDepend(['BSP_USING_SRAM']):

src += Glob('ports/drv_sram.c')

/code如果希望用 rt_malloc 和 rt_free 统一管理这块内存,需要打开 memheap[/size]

[attach]10090[/attach]

[size=2]最后由于用到了 FSMC 和 SRAM 需要在 rtconfig.h 里添加:[/size]
[code]#define BSP_USING_SRAM

define BSP_USING_FMC

define BSP_USING_EXT_FMC_IO

/code这样就可以生成项目文件并编译了:[/size]
[code]scons --target=mdk5 -s[/code]
[size=5]RT-Thread 外置 SRAM 测试[/size]

[size=2]如果一切正常的话开机在 msh 里输入 free 应当可以看到对应的内存块:/size
[/size]

\ | /
- RT -     Thread Operating System
/ | \     4.0.2 build Aug 12 2019
2006 - 2019 Copyright by rt-thread team
[D/drv.sram] sram init success, mapped at 0x68000000, size is 1048576 bytes, data width is 16
msh >free
memheap pool size max used size available size
-------- ---------- ------------- --------------
sram 1048576 48 1048528
heap 126480 7192 119288

msh>[/code]
[size=2]可以看到这里我外置的 SRAM 大小就是 1M,如果输入 sram_test 可以对挂载的内存进行测试,确保数据读写是正常的:[/size]
[code]msh >sram_test
[D/drv.sram] Writing the 1048576 bytes data, waiting....
[D/drv.sram] Write data success, total time: 0.053S.
[D/drv.sram] start Reading and verifying data, waiting....
[D/drv.sram] SRAM test end!
msh >

总结

[size=2]最后 STM32F407 的 SRAM1 有 128KB 的内存,加上外置的 1MB,一共就有了 1152KB 的内存了,但是这并不意味着可以一次性 malloc 这么多内存,因为它们在2个不同的物理内存单元上,RTT的内存管理可以将两个不同的物理内存单元合并为一个单元统一由 rt_malloc 和 rt_free 管理,但是只能在相同的物理内存单元上合并相邻的内存碎片,如果要申请的内存太大,即使跨物理单元有一块那么大的内存加在一起有那么大,也是没办法成功申请的。

还是希望能帮助到觉得内存不够的小伙伴。[/size]

[attach]10091[/attach]
[attach]10092[/attach]
[attach]10093[/attach]

查看更多

关注者
0
被浏览
2.6k
17 个回答

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友

手机
浏览

扫码手机浏览