XiUOS 操作系统代码结构

XiUOS 操作系统代码结构

arch

arm

cortex-m0,m3,m4,m7
shared

pendsv.S: 可悬起异常

prepare.ahwstack.c

arm32_switch.c: arm体系结构的上下文交换

Makefile

shared为公共使用的目录,根据配置的board的类型,会增加cortex下的目录

risc-v

Kconfig

需要配置CPU是否为64位

配置是否为RISCV

配置是否为ARM

配置是否为RISCV + 64位CPU

Makefile

根据ARCH的值选择对应体系结构下的DIR

根据KERNEL_ROOT选择对应路径下的compiler

board

包含各种体系结构

以KD233为例

kd233

img

kdd233自身的结构图以及其在部署时运行的界面图片

include

atomic.h: 原子操作的锁的库

bsp.h: include atomic.h, entry.h, sleep.h, encoding.h

dump.h: include syslog.h 如果syslog.h中所定义的CONFIG_LOG_LEVEL >= LOG_ERROR 就要报 core dump 错误,并输出理由和寄存器中的值

encoding.h: 编码表,把一些汇编指令与机器码相对应

entry.h: 包括init_lma(Load Memory Address) 初始化内存初始地址: 把_data 到 _edata之间的内容拷贝到_data_lma下;initBss 初始化 .bss区域: 把_bss到_ebss 设置为0;initTLS(Thread Local Storage)

interrupt.h: 设置了机器和宿主机的中断掩码为0x8000 0000 0000 0000,ReasonMask为0x7fff ffff ffff ffff

platform.h: 设置机器上一些区域分别的起始的地址

printf.h: 提供了嵌入式系统中的printf 以及 sprintf

sleep.h: 定义了usleep和msleep以及sleep函数

syscall.h: putchar() printstr() printhex()

syslog.h: 定义了系统的报错和日志的宏

util.h: 定义了输出int/double数组的函数,定义了测试两个int/double数组是否相等的函数,定义了屏障barrier让线程在同一个地方停止。lfsr: 把一个数最后两位异或的结果放到第一位去。

third_party_driver
dma

dmac.c: Direct Memory Access client

包括设置其可用、不可用、创建dmac通道,关闭dmac通道,检测通道是否为忙碌状态,DMA的中断设置、中断清除、设置通道并行、获取通道配置、设定DMA的传输地址、DMA的源和汇的控制、DMAC链表的定义和相关的添加、读取等函数、以及dmac的同步操作所需的判断空闲、等待空闲等函数

gpio

Kconfig: 设置 bus driver device 的引脚名字,默认为 pin, pin_drv, pin_dev

connect_gpio.c: 定义了pin_alloc_table,同时定义了分配引脚、获取引脚的函数、设置输入输出的引脚、设置上拉、下拉、不拉,还有根据GPIO的设置处理引脚的中断的函数,如果对应的引脚的中断与当前的中断相同时就会触发其功能,

i2c

Kconfig: 设置 SDATA 线的引脚号码,默认为15,设置SCLOCK的引脚号码,默认为17,设置总线的名字,设置驱动的名字,设置device0的名字

connect_i2c.c: SDA的GPIO为3,SCL的GPIO为4,函数包括了 I2cGpioInit 的引脚初始化,以及设置SDA为高低电平、获取SDA数据,设置SCL为0、获取SCL数据,I2C总线重置(将SDA的GPIO设置为输入高电平,如果输入变为低电平,开始100us一次将时钟设置为高,100us一次将时钟设置为低,共进行十次,十次之后再次将SDA设置为输入高电平),I2C Start: SDA低 - Delay - SCL低,I2C Restart: SDA高 - SCL高 - Delay - SDA低 - Delay - SCL低,I2C停止:SDA低 - Delay - SCL高 - Delay - SDA高 - Delay2,I2CWaitack:设置SDA为高电平,如果SDA变为低了,则收到ack为真。I2cWriteb: 将bus上的8位的data从高到低按位输出到SDA。I2cReadb: 从高到低的读取SDA中的元素到bus的data中。I2cSendBytes: 将msg中的内容全部从bus输出到SDA中。I2cSendAckOrNack: 如果是ack将SDA置为0,如果超时了返回-ERROR,没有超时返回EOK。I2cResvBytes: 从SDA上读取一定长度的数据到bus中。I2cSendAddress: 将addr输出到SDA,如果不成功就重试retries次直到成功或重试次数耗尽。I2cBitSendAddress: 如果是十位地址,就先发地址的高2位,再发地址的低8位,不然就发地址左移一位之后并上I2C_RD,I2cWriteData: I2c的设备dev,如果设置I2C_NO_START=False,那么就在除了第一次之外的每一次执行一次I2cRestart,然后发送msg中的内容到bus,然后如果设置了忽略NACK,就一直发,如果没有忽略NACK,则会在收到NACK之后直接退出,如果这是一个写操作,则会将msg中的内容写到bus中,如果ret的值小于len的值,则会跳出。如果一切顺利,则将msg跳到下一条。I2cReadData与Write类似。BoardI2cBusInit: 初始化总线,初始化驱动,初始化驱动与总线的绑定。

hardware_i2c.c: 定义了I2C Slave的实例,I2C实例,拥有三个I2C的实例放在g_i2c_instance数组中。重置i2c的CLK,之后的一些函数都是十分具体的硬件操作,类似于放在哪个寄存器、使用哪一个终端号的形式。

plic

plic.c: 初始化plic:包括将plic的所有目标的可行置为1,将每个的优先级置为0,将阈值设置为0。设置某个中断号可用/不可用:如果在32以内,则在enable[0]中,如果在32以上则在[i/32]中,将对应的plic的target[core_id]中的1<<(i%32)可用设置为1/0。设置/获取某个中断的优先级。定义了某个中断号之后,会在plic->target中对应的complete设置好,并且将已经使用的中断号+1。注册/注销某个中断号:设置/注销中断号对应的回调函数、对应的上下文。

rtc

kconfig: 设置rtc的总线名字,设置rtc的总线驱动名字,设置总线的设备名称

connect_rtc.c: GetWeekDay: 根据年月日返回星期几, RtcConfigure: 从驱动中获得命令和时间的指针,根据命令的不同,如果是获取时间信息,那么就返回一个当前的年月日周天时分秒的tm类型的结构体,如果命令时设置时间,那么就先上一个锁,然后获得Localtime,然后解锁,然后调用sysctl中重置RTC和设置RTC可用,然后设置时钟频率魏系统时钟频率,设置计数值为1,将RTC设置为运行。 BoardRtcBusInit: 初始化总线、驱动、将驱动连接到总线 BoardRtcDevBend: 根据设备名注册设备,然后将设备连接到总线。

hardware_rtc.c: RTC中调用的函数的硬件实现,包括设置一些寄存器、闹钟睡眠等中断的编写、时钟的范围、模拟时钟等等。

spi

Kconfig: 设置总线的名字,设置总线驱动的名字,设置CLK引脚(默认29),设置D0引脚(默认30),设置D1引脚(默认31),然后根据是否需要SS0、SS1、SS2、SS3来设置至多四台从设备的总线名称和引脚号(默认为32,33,26,27)

connect_spi.c: __spi_set_tmod 根据设置的spi的数量(0~3)设置tmod的值,如果=2,则tmod的偏移为8,如果=3,偏移为10。 SpiSdkInit 初始化 spi 的 sdk 接口(这里代码注释中的interface写成了intetface):将设备中的gpio引脚号和选中id赋值,将选中id的设置为GPIO Out,高电平,初始化spi,将最大频率设置为主设备的频率以及SPI_MAX_CLOCK中的较小值,并将主设备的频率更正为最大频率。SpiSdkCfg: 将给定的参数赋值给对应的驱动。SpiDrvConfigure: 根据时Init还是Configure命令执行上面的两种sdk。SpiWriteData: 如果选中了就将GPIO引脚置为低电平,然后执行dmac的写操作,写到对应的地址。SpiReadData与写类似。BoardSpiBusInit: 初始化总线、驱动和将驱动连接到总线。

hardware_spi.c: 一些硬件实现

sys_clock
timer
uart

Kconfig: 配置是否可用高速UART,默认为可用,可用时设置高速总线、驱动、设备名字,然后分别配置UART1\UART2\UART3三个通讯通道,默认都是开。

connect_uart.c: _uart_init_new: 将channel对应的时钟打开。检查参数是否合格。中断处理。输出字符。获取字符。驱动配置。总线配置。驱动总线链接。

watchdog

Kconfig: 配置是否使用watchdog0和1,默认为不使用,如果使用则需要配置watchdog的总线、驱动、设备的名字。

wdt.c: wdt_enable: 给定看门狗编号,设置该编号的cr(?)为WDT_CR_ENABLE,wdt_set_timeout 将给定编号的看门狗的torr设置为WDT_TORR_TOP(timeout),wdt_get_pclk: 获取给定编号的看门狗的时钟频率,wdt_get_top: 返回对应id上对应timeout从毫秒兑换成看门狗输入所需的数值。wdt_feed: 将对应id的看门狗的crr设置为WDR_CRR_MASK。wdt_clear_interrupt: 让对应id的eoi=它自己的eoi(?)。wdt_start: 设置plic的优先级为1,中断处理为输入参数,给看门狗设置对应的timeout,然后令其生效,中断将等待其CR的模式为WDT_CR_RMOD_INTERRUPT。wdt_stop: 令对应的看门狗失效。

connect_wdt.c: 包括wdt的初始化和配置,以及对应的总线和驱动的初始化。

makefile

设定目录为选定的board

设定编译器为对应的 Root

fs

compatibility_ch376

CH376: 文件管理控制芯片,用于单片机系统读取U盘或者SD卡中的文件。

ch376fs.c: 定义了操作指令、状态、错误对应的机器码。SetFileName: 将设置对应的文件、文件夹放到bus上,将对应的文件名写到owner_haldev中,直到读到" ", “/”, "\“字符截止。OpenRoot: 先设置”/"为文件名,然后执行OPEN和READ命令。 ch376fsOpen: 根据’/'分隔符来分隔path的地址,一层一层的OPEN和READ对应的文件夹,如果找不到文件则返回-ERROR,如果找到了返回EOK。OpenDir与Open类似,只是最后读取的内容不一样。Close: 执行CMD_FILE_CLOSE命令。 Read: 将读入的长度写入hald_ev中,然后根据Read的返回值,如果是USB_INT_SUCCESS则返回读到的大小,如果是USB_INT_DISK_READ那么执行CMD_RD_USB_DATA0操作,然后将读到的内容复制到dst的后面,并更新size,直到全部读完。Write和Read类似。Ch376fsStat: 将给定地址的文件的状态信息如文件名、是否为目录复制到buf中去。ch376fsMount: 先检查是否存在,然后SET_USB_MODE,然后执行DISK_MOUNT。ch376fsUnlink: 找到对应的文件/文件夹,执行CMD_FILE_ERASE

FATFS

iot-devicefile

iot-devicefile.c: 获取总线名、获取驱动名、打开/关闭/读取/写入设备文件、DevicefileIoctl: 给对应的总线驱动发送命令及参数,设备文件挂载(目前操作只有返回0)

lwext4

目前XiUOS还不支持lewxt4操作系统

The main goal of the lwext4 project is to provide ext2/3/4 filesystem for microcontrollers. It may be an interesting alternative for traditional MCU filesystem libraries (mostly based on FAT32). Library has some cool and unique features in microcontrollers world:

  • directory indexing - fast file find and list operations
  • extents - fast big file truncate
  • journaling transactions & recovery - power loss resistance

Lwext4 is an excellent choice for SD/MMC card, USB flash drive or any other wear leveled memory types. However it is not good for raw flash devices.

shared

include

iot-vfs.h: 作为一个抽象的文件系统,可以兼容下面的操作系统提供统一的文件系统类,类中包括了所有可以使用的文件操作。

iot-vfs_posix.h: 在posix中定义了所有不通过调用操作系统类来执行文件操作的系统函数

vfs_select.h: 定义了系统函数select的参数表

src

iot-vfs.c: 定义了vfs_posix.h中的各种文件系统的函数的具体实现

poll.c: 定义了poll的表格,增加poll对象、唤醒poll、沉睡poll某个时间段、DoPollFd:对fd描述符执行一次poll判定,PollDo:对所有的fd执行一次DoPollFd,或执行到超时后退出。poll,执行一次PollDo并且更新waitQueue,返回唤醒的fd的数目

select.c: 定义了select函数,实际是调用了poll函数中使用poll来对文件描述符检查

【p.s.】系统函数 poll 与 select 的区别:

poll本质上和select没有区别,但它没有最大连接数的限制,原因是它是基于链表来存储的

它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。

kernel

Kernel_sevice

[this file is provided for kernel switch of userapi]

xs_service.c:

KsPrintInfo: 根据输入的参数 1 - 9 输出 Task, Memory, Semphore, Event, Mutex, NULL, MessageQueue, NULL, Timer

其他的函数都是一个外包装,内部实际是调用另一个写好的函数,但是这个文件很清楚地展示了整个内核所支持的函数功能,因此对其所有的函数做一个列举

函数名实际函数
KsTaskCreateUTaskCreate
KsStartupTaskStartupTask
KsTaskDeleteKTaskDelete
KsTaskQuitKTaskQuit
KsTaskCoreCombineKTaskCoreCombine + 特判SMP架构
KsTaskCoreUnCombineKTaskCoreUnCombine
KsMdelayTaskMdelayKTask
获取Task信息相关:
KsGetTaskIDGetKTaskDescriptor
KsGetTaskNameGetTaskWithIdnodeInfo
KsGetTaskStatGetTaskWithIdnodeInfo
KsGetTaskCombinedCoreGetTaskWithIdnodeInfo + 特判SMP架构
KsGetTaskRunningCoreGetTaskWithIdnodeInfo + 特判SMP架构
KsGetTaskErrorstatusGetTaskWithIdnodeInfo
KsGetTaskPriorityGetTaskWithIdnodeInfo
KsMallocx_umalloc + 特判是否开启内存保护 (mem_access.AddRegion)
KsFreex_ufree + 特判是否开启内存保护 (mem_access.ClearRegion)
Mutex相关:
KsCreateMutexKMutexCreate
KsDeleteMutexKMutexDelete
KsMutexObtainKMutexObtain
KsMutexAbandonKMutexAbandon
Semaphore相关:
KsCreateSemaphoreKSemaphoreCreate [删、获取、释放函数与Mutex结构一致,省略]
KsSemaphoreSetValueKSemaphoreSetValue
Event相关:
KsCreateEventKEventCreate
KsDeleteEventKEventDelete
KsEventTriggerKEventTrigger
KsEventProcessKEventProcess
MessageQueue相关:
KsCreateMsgQueueKCreateMsgQueue
KsDeleteMsgQueueKDeleteMsgQueue
KsMsgQueueSendwaitKMsgQueueSendwait
KsMsgQueueSendKMsgQueueSend
KsMsgQueueUrgentSendKMsgQueueUrgentSend
KsMsgQueueRecvKMsgQueueRecv
KsMsgQueueReinitKMsgQueueReinit
File System posix:
KsOpenopen
KsReadread
KsWritewrite
KsCloseclose
KsIoctlioctl [IO control 函数,对设备的IO通道进行管理的函数,可以对设备的一些特性进行控制,例如串口的传输波特率、马达转速等]
KsLseeklseek
KsRenamerename
KsUnlinkunlink
KsStatstat
KsFstatfstat [与stat相同,只是第一个传入参数不是文件路径而是文件描述符]
KsFsyncfsync [fsync的功能是确保文件fd所有已修改的内容已经正确同步到硬盘上,该调用会阻塞等待直到设备报告IO完成]
KsFtruncateftruncate [将文件截断到给定的大小]
KsMkdirmkdir
KsClosedirclosedir
KsReaddirreddir
KsRmdirrmdir
KsChdirchdir
KsGetcwdgetcwd
KsTelldirtelldir ( 被注释掉了 ?)
KsSeekdirseekdir
KsRewinddirrewinddir
KsStatfsstatfs

同时也定义了utask的结构体

包括了name, func_entry, func_param, stack_size, prio

最后是用户的几个操作:UserTaskCreate, UserGetTaskName, UserGetTaskID, UserGetTaskCombinedCore, UserGetTaskRunningCore, UserGetTaskErrorstatus, UserGetTaskPriority

Kernel_test

Kconfig: 配置了测试的内容,包括了avl树、循环域,内存,内存,计时器,iwg(看门狗),Debug,调度,实时时钟,串行接口,硬件计时器,touch,显示器,ch438,I2C,riscv can

Memory

Makefile: byte_manage.c是必定编译的,而gatherblock和isolation分别需要在定义了Kernel_memblock和Task_isolation的情况下执行。

byte_manage.c: 具体的内存分配的代码

[宏定义部分]

定义了动态内存的块的大小,指针的大小,空闲块的大小

同时设置了相应的Mask 将内存分为了静态部分和动态部分,在动态部分又n等分成n个SRAM,每个大小为 2^16位。小块分为32B和64B的大小

具体的内存块的结构体分为已分配的和空闲的,其中空闲的会组成一个双向链表

动态内存的总管结构为DynamicBuddyMemory,其中包括了所有已分配的节点、未分配的节点,分配节点的内存和等信息。

静态内存的总管结构为segment,包括了块大小、空闲链表以及对空闲块和使用块的计数器。

之后的函数定义部分就是内存分配与释放相关的部分了,其中对内存块的大小做了区分,对不同大小的内存块会使用不同的内存分配释放函数。也对内存所属者做了区分,用户和内核使用的内存使用了不同的内存分配释放函数。

对于扩展的SRAM,使用ExtSramInitBoardMemory来对内存进行初始化的管理。

gatherblock.c:

这个文件是在定义了Mem_Block下执行的,(在这里xs变成了xiaoshan,我一直以为是xuos来着hhh)

总的管理是一个链表叫做xiaoshan_megather_head,在初始化函数中会将所有的内存块串联起来,RemoveMemGather是从全局List中删除内存块的函数,与初始化函数作用相反,会将所有的没有被唤醒的进程的状态设置为-ERROR并唤醒。CreateMemGather是创建内存块的函数,创建好之后会配置好参数,加入全局的管理链表中,DeleteMemGather是与Create相反的函数,内容与RemoveMemGather类似。AllocBlockMemGather和FreeBlockMemGather是从gatherblock中分配和释放一小块内存的函数。

isolation.c:

这个文件是在定义了TASK_ISOLATION的情况下执行的,对于ARM类型支持MPU的情况,定义了它的mem_access;对于RISCV支持PMP的情况,定义了相应的mem_access

Thread

这个文件夹下主要是与线程切换与运行有关的函数

CriticalArea.c: 临界区

appstartup.c: 启动一个Task的过程,CreateMainTask: 创建一个main函数,其中main的函数体就是用户的entrypoint,然后启动Task。

assign.c: 单CPU的调度程序, KTaskOsAssign可以调度一个最高优先级的任务

assignfifo.c: 包含了初始化时间片为0的函数以及在等待队列末尾插入任务的函数,其中等待队列是按照优先级分级的

assign_roundrobin.c: 包括了初始化时间片和插入任务的函数,以及更新时间片的操作(时间片减一,如果等于0则加满,然后换人)

assign_roundrobinremian.c: 使用了一些remain的技巧,大概是能够两倍的执行一个命令,然后可以根据节点剩下的时间来动态选择链表的头还是尾来下一个执行,然后如果这个任务一个时间片没有用完在更新的时候就会在一定范围内给他增加执行次数,使得它下次有更多的时间执行。

assignstat.c: 可以检查任务的栈,检查等待的任务的位图是否为空,可以选择最高优先级的任务唤醒并加入队列。

avl_tree: AVL树的实现代码

lib

libcpp

支持了C++的新功能

cppinit.c: C++系统的初始化

crt.cpp: 将c语言中的new, delete更改为系统中使用的x_malloc和x_free

newlib

Newlib是一个面向嵌入式系统的C运行库。最初是由Cygnus Solutions收集组装的一个源代码集合,取名为newlib,现在由Red Hat维护,最新的版本是2.1.0。

Newlib的所有库函数都建立在20个桩函数的基础上[2],这20个桩函数完成一些newlib无法实现的功能:

  1. 级I/O和文件系统访问(open、close、read、write、lseek、stat、fstat、fcntl、link、unlink、rename);

  2. 扩大内存堆的需求(sbrk);

  3. 获得当前系统的日期和时间(gettimeofday、times);

  4. 各种类型的任务管理函数(execve、fork、getpid、kill、wait、_exit);

这个目录下是这些在XiUOS实现的桩函数:

fs_syscalls.c: _fstat_r, _link_r, _sbrk_r, _wait_r 目前不支持,_isatty_r(判断是否为终端机)会直接根据fd是否在[0,3)之内返回true,其他的如打开关闭等文件系统的函数如果define了 FS_VFS 其他的函数分别会导引到之前的文件系统中编写的函数,否则也是全部不支持

mem_syscall.c: 是内存管理相关的操作系统函数的映射,包括了malloc, realloc, calloc, free

stdio.c: 设置系统的控制台设备

task_syscall.c: 支持了c语言中的abort,通过SuspendKTask来实现,同时执行一个进程切换

time_syscall.c: 配置了计时器绑定了rtc

resources

和之前的board/kd233中看到的3rd_party类似,resources目录下为XiUOS所支持的资源

包括了 CAN 总线、 以太网、 I2C总线、 LCD、 PIN、 RTC、 SDIO、 Serial、 SPI、 Timer、 usb、 watchdog。

在每一个资源的目录下分为三个源文件

bus_xxx.c drv_xxx.c dev_xxx.c 分别是其总线、驱动与设备相关的函数定义。以读写类的总线为例,在bus中就会包含bus的初始化、bus与drv的绑定,在dev中就会包括dev的初始化与drv的绑定,读,写,在drv中就会包括初始化与注册

tool

tool目录下包括一些单独使用的工具

hosttools

xs_config.sh: 定义了判断是否为包、判断是否为config类文件的函数,

scripts

其中包括了一个配置好的连接kd233的脚本,以及一个烧写bin到kd233上的脚本

shell

XiUOS下使用了letter-shell来作为它的命令行shell

具体的使用方法可以看github上的中文文档:

https://github.com/NevermindZZT/letter-shell

Kconfig

导入了 kernel, lib, fs, APP_Framework 中的 Kconfig

Makefile

支持 kd233 stm32f407-st-discovery maix-go stm32f407zgt6 aiit-riscv64-board aiit-arm32-board hifive1-rev-B hifive1-emulator k210-emulator

命令: make BORAD=kd233 menuconfig

附表

嵌入式开发中的常见英文缩写:

LMA: Load Memory Address

TLS: Thread Location Storage

DMA: Direct Memory Access

IRQ: InteRrupt Request

GPIO: General Purpose Input/Output

I2C: Inter-Integrated Circuit

PLIC: Platform-Level Interrupt Controller

ctx: context

RTC: Real Time Clock

SPI: Serial Peripheral Interface

UART: Universal Asynchronous Receiver/Tranmitter (HS = High Speed)

WDT: WatchDog Timer

POSIX: Portable Operating System Interface

CAN: Controller Area Network

MPU: Memory Protection Unit

PMP: Physical Memory Protection

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值