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 是有必要好好的读读源代码,这串口实现比较有意思。以后再搞了。