Go to file
2024-04-10 16:29:48 +08:00
CMake 优化工程结构 2024-04-05 00:36:26 +08:00
Core 优化部分调试函数 2024-04-10 01:12:09 +08:00
Drivers 增加采样率可变功能 2024-04-07 00:28:05 +08:00
Middlewares 修改部分调试打印代码 2024-04-10 16:29:48 +08:00
User 修改部分调试打印代码 2024-04-10 16:29:48 +08:00
.cproject 首次提交 2024-03-03 22:46:44 +08:00
.gitignore 首次提交 2024-03-03 22:46:44 +08:00
.gitmodules 更新lvgl到8.4并使用submodule模式 2024-04-08 19:11:59 +08:00
.mxproject 增加采样率可变功能 2024-04-07 00:28:05 +08:00
.project 首次提交 2024-03-03 22:46:44 +08:00
CMakeLists.txt 使用dr_libs重构mp3播放器 2024-04-09 21:28:29 +08:00
README.md 首次提交 2024-03-03 22:46:44 +08:00
stm32h743_player.ioc 适配新版硬件 2024-04-08 01:30:47 +08:00
STM32H743.svd 添加libflac 2024-04-07 11:21:58 +08:00
STM32H743IITX_FLASH.ld 优化工程结构 2024-04-05 00:36:26 +08:00
STM32H743IITX_RAM.ld 首次提交 2024-03-03 22:46:44 +08:00

内存规划

CCMRAM

  • 用于系统默认的bss与data区
  • 用于系统默认的栈区
  • 用于FreeRTOS的动态内存分配与LVGL的动态内存分配
  • 用于解码器等算法的缓存区

AXIRAM

  • 用做用户程序的堆区
  • 用于newlib的堆区

APBRAM

  • 用于低速外设的DMA缓冲区如USART、SAI等

内存管理函数移植

为了保证C语言运行库的线程安全性标准的libc由MMU通过为每个进程分配独立的内存空间实现进程安全性而线程安全性则通过在每一个线程上下文中分别保存该线程内libc所使用的上下文来实现方法为在全局范围维护一个指向当前线程所使用的libc上下文的指针并在调度器切换线程时将该指针指向正在运行任务的libc上下文。arm-none-eabi-gcc等目标为裸机的编译器,通常使用精简的newlib替代标准的libc作为其默认的C语言运行库从而适应MCU等资源受限的应用。裸机环境中因不存在MMU而不存在进程的概念而线程则退化为RTOS的任务因此仍需保证C运行库在RTOS任务间的线程安全性。arm-none-eabi-gcc的编译选项中默认启用了newlib 4引入的一种特殊的锁机制_RETARGETABLE_LOCKING,即可重定向锁机制。该机制避免了使用标准C语言库中基于保存的newlib上下文实现的锁__malloc_lock(struct _reent *)取而代之的则是由用户提供如下的一系列的mutex与锁函数分别为newlib中各个部分添加更轻量级的锁。

struct __lock __lock___sfp_recursive_mutex;
struct __lock __lock___atexit_recursive_mutex;
struct __lock __lock___at_quick_exit_mutex;
struct __lock __lock___malloc_recursive_mutex;
struct __lock __lock___env_recursive_mutex;
struct __lock __lock___tz_mutex;
struct __lock __lock___dd_hash_mutex;
struct __lock __lock___arc4random_mutex;

void __retarget_lock_init (_LOCK_T * <[lock_ptr]>);
void __retarget_lock_init_recursive (_LOCK_T * <[lock_ptr]>);
void __retarget_lock_close (_LOCK_T <[lock]>);
void __retarget_lock_close_recursive (_LOCK_T <[lock]>);
void __retarget_lock_acquire (_LOCK_T <[lock]>);
void __retarget_lock_acquire_recursive (_LOCK_T <[lock]>);
int __retarget_lock_try_acquire (_LOCK_T <[lock]>);
int __retarget_lock_try_acquire_recursive (_LOCK_T <[lock]>);
void __retarget_lock_release (_LOCK_T <[lock]>);
void __retarget_lock_release_recursive (_LOCK_T <[lock]>);

22

{{< admonition type=info title="其他newlib中的函数" open=true >}} 即使_RETARGETABLE_LOCKING为newlib实现了一种更加轻量的锁机制但当使用到newlib中需要使用到除锁以外的上下文的函数例如 {{< /admonition >}}

11

在不使用_RETARGETABLE_LOCKINGmalloc的调用的流程如下

newlib-cygwin\newlib\libc\stdlib\malloc.c

https://sourceware.org/git/?p=newlib-cygwin.git;a=blob;f=winsup/cygwin/passwd.cc#l190

使用newlib内置的内存分配函数编译工程可以从连接器输出的map文件中看到

 .text._malloc_r
                0x000000000801c524      0x474 D:/Programs/STM32CubeCLT/GNU-tools-for-STM32/bin/../lib/gcc/arm-none-eabi/11.3.1/../../../../arm-none-eabi/lib/thumb/v7e-m+dp/hard\libg_nano.a(libc_a-mallocr.o)
                0x000000000801c524                _malloc_r

_malloc_r函数是来自newlib_nano中的mallocr.o目标文件。

set(TARGET_BUILD_NEWLIB_OPTIONS
    -Wl,-wrap,_malloc_r -Wl,-wrap,_free_r -Wl,-wrap,_realloc_r # wrap newlib memory allocator functions
)

newlib源码中newlib-cygwin\newlib\libc\stdlib\malloc.c中有

void *
malloc (size_t nbytes)		/* get a block */
{
  return _malloc_r (_REENT, nbytes);
}

void
free (void *aptr)
{
  _free_r (_REENT, aptr);
}

因此实际是调用的_malloc_r_free_r函数。这两个函数的原型在newlib-cygwin\newlib\libc\stdlib\_mallocr.c中。例如,_malloc_r的原型为

#if __STD_C
Void_t* mALLOc(RARG size_t bytes)
#else
Void_t* mALLOc(RARG bytes) RDECL size_t bytes;
#endif
{
#ifdef MALLOC_PROVIDED

  return malloc (bytes); // Make sure that the pointer returned by malloc is returned back.

#else

  mchunkptr victim;                  /* inspected/selected chunk */
  INTERNAL_SIZE_T victim_size;       /* its size */
  int       idx;                     /* index for bin traversal */
  mbinptr   bin;                     /* associated bin */
  mchunkptr remainder;               /* remainder from a split */
  long      remainder_size;          /* its size */
  int       remainder_index;         /* its bin index */
  unsigned long block;               /* block traverser bit */
  int       startidx;                /* first bin of a traversed block */
  mchunkptr fwd;                     /* misc temp for linking */
  mchunkptr bck;                     /* misc temp for linking */
  mbinptr q;                         /* misc temp */

  INTERNAL_SIZE_T nb  = request2size(bytes);  /* padded request size; */

  /* Check for overflow and just fail, if so. */
  if (nb > INT_MAX || nb < bytes)
  {
    RERRNO = ENOMEM;
    return 0;
  }

  MALLOC_LOCK;

  /* Check for exact match in a bin */

  if (is_small_request(nb))  /* Faster version for small requests */
  {
    idx = smallbin_index(nb);

    /* No traversal or size check necessary for small bins.  */

    q = bin_at(idx);
    victim = last(q);

#if MALLOC_ALIGN != 16
    /* Also scan the next one, since it would have a remainder < MINSIZE */
    if (victim == q)
    {
      q = next_bin(q);
      victim = last(q);
    }
#endif
    if (victim != q)
    {
      victim_size = chunksize(victim);
      unlink(victim, bck, fwd);
      set_inuse_bit_at_offset(victim, victim_size);
      check_malloced_chunk(victim, nb);
      MALLOC_UNLOCK;
      return chunk2mem(victim);
    }

    idx += 2; /* Set for bin scan below. We've already scanned 2 bins. */

  }
  else
  {
    idx = bin_index(nb);
    bin = bin_at(idx);

    for (victim = last(bin); victim != bin; victim = victim->bk)
    {
      victim_size = chunksize(victim);
      remainder_size = long_sub_size_t(victim_size, nb);

      if (remainder_size >= (long)MINSIZE) /* too big */
      {
        --idx; /* adjust to rescan below after checking last remainder */
        break;
      }

      else if (remainder_size >= 0) /* exact fit */
      {
        unlink(victim, bck, fwd);
        set_inuse_bit_at_offset(victim, victim_size);
        check_malloced_chunk(victim, nb);
	MALLOC_UNLOCK;
        return chunk2mem(victim);
      }
    }

    ++idx;

  }

  /* Try to use the last split-off remainder */

  if ( (victim = last_remainder->fd) != last_remainder)
  {
    victim_size = chunksize(victim);
    remainder_size = long_sub_size_t(victim_size, nb);

    if (remainder_size >= (long)MINSIZE) /* re-split */
    {
      remainder = chunk_at_offset(victim, nb);
      set_head(victim, nb | PREV_INUSE);
      link_last_remainder(remainder);
      set_head(remainder, remainder_size | PREV_INUSE);
      set_foot(remainder, remainder_size);
      check_malloced_chunk(victim, nb);
      MALLOC_UNLOCK;
      return chunk2mem(victim);
    }

    clear_last_remainder;

    if (remainder_size >= 0)  /* exhaust */
    {
      set_inuse_bit_at_offset(victim, victim_size);
      check_malloced_chunk(victim, nb);
      MALLOC_UNLOCK;
      return chunk2mem(victim);
    }

    /* Else place in bin */

    frontlink(victim, victim_size, remainder_index, bck, fwd);
  }

  /*
     If there are any possibly nonempty big-enough blocks,
     search for best fitting chunk by scanning bins in blockwidth units.
  */

  if ( (block = idx2binblock(idx)) <= binblocks)
  {

    /* Get to the first marked block */

    if ( (block & binblocks) == 0)
    {
      /* force to an even block boundary */
      idx = (idx & ~(BINBLOCKWIDTH - 1)) + BINBLOCKWIDTH;
      block <<= 1;
      while ((block & binblocks) == 0)
      {
        idx += BINBLOCKWIDTH;
        block <<= 1;
      }
    }

    /* For each possibly nonempty block ... */
    for (;;)
    {
      startidx = idx;          /* (track incomplete blocks) */
      q = bin = bin_at(idx);

      /* For each bin in this block ... */
      do
      {
        /* Find and use first big enough chunk ... */

        for (victim = last(bin); victim != bin; victim = victim->bk)
        {
          victim_size = chunksize(victim);
          remainder_size = long_sub_size_t(victim_size, nb);

          if (remainder_size >= (long)MINSIZE) /* split */
          {
            remainder = chunk_at_offset(victim, nb);
            set_head(victim, nb | PREV_INUSE);
            unlink(victim, bck, fwd);
            link_last_remainder(remainder);
            set_head(remainder, remainder_size | PREV_INUSE);
            set_foot(remainder, remainder_size);
            check_malloced_chunk(victim, nb);
	    MALLOC_UNLOCK;
            return chunk2mem(victim);
          }

          else if (remainder_size >= 0)  /* take */
          {
            set_inuse_bit_at_offset(victim, victim_size);
            unlink(victim, bck, fwd);
            check_malloced_chunk(victim, nb);
	    MALLOC_UNLOCK;
            return chunk2mem(victim);
          }

        }

       bin = next_bin(bin);

#if MALLOC_ALIGN == 16
       if (idx < MAX_SMALLBIN)
         {
           bin = next_bin(bin);
           ++idx;
         }
#endif
      } while ((++idx & (BINBLOCKWIDTH - 1)) != 0);

      /* Clear out the block bit. */

      do   /* Possibly backtrack to try to clear a partial block */
      {
        if ((startidx & (BINBLOCKWIDTH - 1)) == 0)
        {
          binblocks &= ~block;
          break;
        }
        --startidx;
       q = prev_bin(q);
      } while (first(q) == q);

      /* Get to the next possibly nonempty block */

      if ( (block <<= 1) <= binblocks && (block != 0) )
      {
        while ((block & binblocks) == 0)
        {
          idx += BINBLOCKWIDTH;
          block <<= 1;
        }
      }
      else
        break;
    }
  }


  /* Try to use top chunk */

  /* Require that there be a remainder, ensuring top always exists  */
  remainder_size = long_sub_size_t(chunksize(top), nb);
  if (chunksize(top) < nb || remainder_size < (long)MINSIZE)
  {

#if HAVE_MMAP
    /* If big and would otherwise need to extend, try to use mmap instead */
    if ((unsigned long)nb >= (unsigned long)mmap_threshold &&
        (victim = mmap_chunk(nb)) != 0)
    {
      MALLOC_UNLOCK;
      return chunk2mem(victim);
    }
#endif

    /* Try to extend */
    malloc_extend_top(RCALL nb);
    remainder_size = long_sub_size_t(chunksize(top), nb);
    if (chunksize(top) < nb || remainder_size < (long)MINSIZE)
    {
      MALLOC_UNLOCK;
      return 0; /* propagate failure */
    }
  }

  victim = top;
  set_head(victim, nb | PREV_INUSE);
  top = chunk_at_offset(victim, nb);
  set_head(top, remainder_size | PREV_INUSE);
  check_malloced_chunk(victim, nb);
  MALLOC_UNLOCK;
  return chunk2mem(victim);

#endif /* MALLOC_PROVIDED */
}

#endif /* DEFINE_MALLOC */

其中,在已定义__STD_C时,函数原型中的RARG展开为struct _reent *reent_ptr,,因此完整的函数原型为

void* mALLOc(struct _reent *reent_ptr, size_t bytes);

其中,第一个参数为使函数可重入的锁,第二个参数则为常规malloc中要申请的内存大小。