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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章