RTEMS 的libio ,work area ,heap,串口驱动等的一些阅读源代码分析笔记

  ram_end = (uintptr_t)RamBase + (uintptr_t)RamSize;
  *work_area_start = WorkAreaBase;
  *work_area_size  = ram_end - (uintptr_t) WorkAreaBase;
  *heap_start      = BSP_BOOTCARD_HEAP_USES_WORK_AREA;
  *heap_size       = (uintptr_t) HeapSize;


这些变量是在连接脚本中
_sdram_base = DEFINED(_sdram_base) ? _sdram_base : 0x30000000;
_sdram_size = DEFINED(_sdram_size) ? _sdram_size : 64M;


RamBase = _sdram_base;
RamSize = _sdram_size;
HeapSize = DEFINED(HeapSize) ? HeapSize : 0x0;


  _end = . ;
  __end__ = . ;
  PROVIDE (end = _end);
  WorkAreaBase = .;


可见,work are 是在 ZI 段之后没有用的内存。大小就是根据配置的RAM的总大小减去已经占用的大小。
HeapSize 则需要根据是否有定义,没有的话就是0了。应该是通过makefile等手段去指定的,目前暂时查找不到根源


这个 work area 和配置中的 ram需求大小做判断,如果需求大于供给,则会错误
if ( work_area_size <= Configuration.work_space_size ) {
    printk(
      "bootcard: work space too big for work area: %p > %p\n",
      (void *) Configuration.work_space_size,
      (void *) work_area_size
    );
    bsp_cleanup();
    return -1;
  }


  rtems_configuration_table Configuration = {
    NULL,                                     /* filled in by BSP */
    CONFIGURE_EXECUTIVE_RAM_SIZE, 


CONFIGURE_EXECUTIVE_RAM_SIZE 是用户定义的,默认是根据自动计算的。一般一些网络应用应该至少给 256K 以上的 RAM


bootcard_bsp_libc_helper 函数中先设置 heap ,如果 CONFIGURE_UNIFIED_WORK_AREAS ,表示 work area 和 heap
使用统一的空间,则初始化为 heap 的 work area 开始
如果为独立空间,则 heap 跟在 work area 的后面的所有配置空间


+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


LIBIO 是设备的输入输出接口
CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS 可以由用户配置,默认 3个


有全局变量去保存
uint32_t rtems_libio_number_iops = CONFIGURE_LIBIO_MAXIMUM_FILE_DESCRIPTORS;


很简单,维护3个全局变量,信号量,指向这块内存的,空的。
rtems_id           rtems_libio_semaphore;
rtems_libio_t     *rtems_libio_iops;
rtems_libio_t     *rtems_libio_iop_freelist;


void rtems_libio_init( void )
{
    rtems_status_code rc;
    uint32_t i;
    rtems_libio_t *iop;


    if (rtems_libio_number_iops > 0)
    {
        rtems_libio_iops = (rtems_libio_t *) calloc(rtems_libio_number_iops,
                                                    sizeof(rtems_libio_t));
        if (rtems_libio_iops == NULL)
            rtems_fatal_error_occurred(RTEMS_NO_MEMORY);


        iop = rtems_libio_iop_freelist = rtems_libio_iops;
        for (i = 0 ; (i + 1) < rtems_libio_number_iops ; i++, iop++)
          iop->data1 = iop + 1;
        iop->data1 = NULL;
    }


很简单,分配 N 个(由用户配置)描述符结构体,然后初始化连接起来,连接到 rtems_libio_iop_freelist
上面。这样做的好处是不会随便的分配,固定分配,更加稳定。这个和 UCOS 的 TCB 是一样的。
  rc = rtems_semaphore_create(
    RTEMS_LIBIO_SEM,
    1,
    RTEMS_BINARY_SEMAPHORE | RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY,
    RTEMS_NO_PRIORITY,
    &rtems_libio_semaphore
  );
  if ( rc != RTEMS_SUCCESSFUL )
    rtems_fatal_error_occurred( rc );
接着占用一个信号量使用。




+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


device driver 的初始化
首先,有多少个驱动支持是用户指定的,没有的话自动根据 Device_drivers 配置,里面放的是静态
的驱动
    CONFIGURE_MAXIMUM_DRIVERS,                /* maximum device drivers */
    CONFIGURE_NUMBER_OF_DRIVERS,              /* static device drivers */
    Device_drivers,    


在初始化函数 _IO_Manager_initialization 的时候赋值到全局变量中了。
    _IO_Driver_address_table = driver_table;
    _IO_Number_of_drivers = number_of_drivers;


在 _IO_Initialize_all_drivers 函数中将会根据这个表中的驱动进行初始化。


可以看看这个表,根据配置支持哪些驱动都在里面了
  rtems_driver_address_table Device_drivers[] = {
    #ifdef CONFIGURE_BSP_PREREQUISITE_DRIVERS
      CONFIGURE_BSP_PREREQUISITE_DRIVERS,
    #endif
    #ifdef CONFIGURE_APPLICATION_PREREQUISITE_DRIVERS
      CONFIGURE_APPLICATION_PREREQUISITE_DRIVERS,
    #endif
    #ifdef CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
      CONSOLE_DRIVER_TABLE_ENTRY,
    #endif
    #ifdef CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
      CLOCK_DRIVER_TABLE_ENTRY,
    #endif
    #ifdef CONFIGURE_APPLICATION_NEEDS_RTC_DRIVER
      RTC_DRIVER_TABLE_ENTRY,
    #endif
    #ifdef CONFIGURE_APPLICATION_NEEDS_WATCHDOG_DRIVER
      WATCHDOG_DRIVER_TABLE_ENTRY,
    #endif
 .........
  };


比较关心的是console 驱动和clock驱动
#define CLOCK_DRIVER_TABLE_ENTRY \
  { Clock_initialize, NULL, NULL, NULL, NULL, NULL }


Clock_initialize 里面初始化时钟,由BSP中做了
Clock_driver_support_install_isr( Clock_isr, Old_ticker );
按照 中断处理句柄,最终是会调用 rtems_clock_tick ,这个比较重要
是操作系统知道一个 tick,会发生任务切换的。
rtems_status_code rtems_clock_tick( void )
{
}
所以不知道为什么,这么重要的,为什么可以配置为不使用,或者是用户自己去调用 rtems_clock_tick
又或者是根本不需要任务切换。






#define CONSOLE_DRIVER_TABLE_ENTRY \
  { console_initialize, console_open, console_close, \
    console_read, console_write, console_control }


console_initialize
根据全局变量 Console_Port_Count 确定有多少个端口,Console_Port_Tbl 确定具体的设备,这个设备表
是由具体的bsp去实现的,例如 gp32/console 里面的文件,最后注册为设备 /dev/console


console_open 打开
  cptr = &Console_Port_Tbl[minor];
  Callbacks.firstOpen            = cptr->pDeviceFns->deviceFirstOpen;
  Callbacks.lastClose            = cptr->pDeviceFns->deviceLastClose;
......
打开设备,则从之前的全局变量中找到已经注册到的串口设备,然后构造callback
调用为设备自己提供的函数
status = rtems_termios_open ( major, minor, arg, &Callbacks );
                /*
* Set callbacks
*/
tty->device = *callbacks;
最后将用户自己提供的串口函数,连接为 termios 的回调函数,那么 termios就能输入输出了。


这样能基本解释怎么使用BSP提供的串口函数了。


最后我觉得 termios 是有必要好好的读读源代码,这串口实现比较有意思。以后再搞了。
发布了64 篇原创文章 · 获赞 9 · 访问量 13万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章