# 從無到有打造 IoT 作業系統核心:以 [Piko/RT](https://github.com/PikoRT/pikoRT) 為例
**Copyright (C) 2017 Jim Huang (黃敬群) <jserv.tw @ gmail.com>**

:::warning
:warning: 注意
- 本系列講座全名為 Operating System Concepts And Renaissance [作業系統概念和文藝復興],簡稱 OSCAR。 ==[資料彙整在此](http://hackfoldr.org/oscar/)==
:::
## [Piko/RT](https://github.com/PikoRT/pikoRT) 的開發動機
* Y! 知識家 [台灣人發明的作業系統](https://tw.answers.yahoo.com/question/index?qid=20071204000010KK10989)
* 「應該是找不到了,因為只發行了第一版就不行了」
* 為了破除「持久」的迷思,我們說什麼都要繼續
* 針對 ARM Cortex-M 系列,開發部分 POSIX 相容的作業系統核心
* 填補 FreeRTOS 和 (uc)Linux 之間的空缺
* FreeRTOS [精簡有力且平台移植眾多](http://wiki.csie.ncku.edu.tw/embedded/freertos),但缺乏有效的 POSIX 相容層
* Linux 已可在 ARM Cortex-M3/M4/M7 運作,但[需要投入各式改善工作](https://elinux.org/images/d/d4/Optimize_uClinux_for_ARM_Cortex-M4.pdf),核心加上 rootfs 往往逼近 2 MB 的空間
* 從 API 到核心機制仿效 Linux 的設計,重新針對 ARM Cortex-M3/M4/M7 實作
* Piko/RT 授權採用 [2-clause BSD](https://github.com/Piko-RT/pikoRT/blob/master/LICENSE)
* 輕量級的 RTOS,允許既有應用程式移植
* [移植 MicroPython 的案例](https://github.com/Piko-RT/pikoRT/pull/16)
* 作為 hypervisor for IoT 的驗證環境
* [形式化驗證](https://hackmd.io/s/H1xxp3pF0) 的對象
## 相關專案
* [HyperC](http://socware.net/?page_id=18): 強者我前同事的大作,除了不是 open source 外,沒什麼好挑剔,高效能又精簡
* [F9 microkernel](https://github.com/f9micro): 針對 ARM Cortex-M + MPU 發展的微核心
* 商業運轉: [Genesi 的 IoTA 整套解決方案](https://genesi.company/solutions/embedded)
* [rtenv+](http://wiki.csie.ncku.edu.tw/embedded/rtenv): 另一個在成功大學發展的作業系統核心,針對 ARM Cortex-M3/M4 發展
* [NuttX](http://nuttx.org/): 支援 POSIX 的完整 RTOS,已用於 [PX4 無人飛行器的韌體](https://github.com/PX4/Firmware)
## Linux 設計的演化
Linux 發展之初,只是一個以 single thread 為導向的作業系統,multi-threaded 與 SMP 也在發展 10 年後才納入,早期甚至得用彆腳的 LinuxThread 套件來實現 multi-threading,而 Mach 與 Hurd 在設計初期,就已經考慮這些需求。在 [NPTL](https://en.wikipedia.org/wiki/Native_POSIX_Thread_Library) 出現之前,Linux 的 multi-threaded 實做非常奇怪,仍然把 process 當作最基本的abstraction,也就是說 scheduling, context switch 等基本操作對象仍是 process,而thread / LWP 只是和別人分享定址空間和資源的 process。因此:
* 用 clone() 產生的 thread 本質上仍是 process,可以說 Linux 核心中定義的 "thread" 只做到了資源共享,而不構成執行工作的最基本單位
* 嚴格來說,Linux 只實做了一半的 thread,但這並不是壞事,因為許多的應用程式不見得用到 thread,而且簡化 thread 實做的結果,使得 process 管理變得更有效率,副作用是產生出來的 "thread" 比其它作業系統的實做,顯得更 heavy-weight,可以說,過去 Linux 犧牲 thread 的效率,以換取 process 的效率
* 以 abstraction 的角度來看 Linux 過去並非在本質上支援 thread,但以 programming model 來看,Linux 的確是有 thread 可用,儘管效率較差
早期 Linux 的 process 和 thread 的效能和其他作業系統的客觀數據比較,可參照
Microsoft Research 論文 "[An Overview of the Singularity Project](http://research.microsoft.com/pubs/52716/tr-2005-135.pdf)" (2005 年) 的第 31 頁
## API 涵蓋
以 [Minimal Real-time System Profile IEEE Std 1003.13](http://www.opengroup.org/austin/papers/wp-apis.txt) (PSE51) 為基礎
```
POSIX RT ELC
Feature PSE PSE PSE PSE Min Int Full FIPS UNIX LSB UNIX
51 52 53 54 SE SE SE 151-2 98 1.x 03
Processes - - X X - X X X X X X
Pipes - - X X - X X X X X X
Files and Directories - X - X - X X X X X X
Basic I/O X X X X X X X X X X X
Signals X X X X X X X X X X X
Users and Groups - - - X - - X X X X X
File Synchronization X X X X X X X - X X X
Memory Mapped Files - X - X - X X - X X X
Memory Protection - - X X - X X - X X X
Process Priority - - X X - - - - o - o
Scheduling
Memory Locking X X X X - - - - o - o
Synchronized I/O X X X X - - - - o - o
Asynchronized I/O - X X X - X X - o o o
Hi Resolution Clocks X X X X - - - - o - o
& Timers
Realtime Signals X X X X - - - - o - o
Semaphores X X X X - - - - o - o
Shared Memory X X X X - - - - o - o
IPC Message Passing X X X X - - - - o - o
Threads X X X X * * * - X o X
Thread Safe Functions X X X X * * * - X - X
Thread Attribute X X X X * * * - X - X
Stack Address
Thread Attribute X X X X * * * - X - X
Stack Size
Thread Process Shared - - X X * * * - X - X
Thread Priority X X X X * * * - o - o
Scheduling
Thread Priority X X X X * * * - o - o
Inheritence
Thread Priority X X X X * * * - o - o
Protection
```
目標是運作以下程式:
* [lmbench](http://www.bitmover.com/lmbench/)
* Context switching
* Signal handling
* System call overhead
* Memory read latency
* [Cyclictest](https://wiki.linuxfoundation.org/realtime/documentation/howto/tools/cyclictest/start?s[]=cyclictest)
* [hackbench](https://github.com/kosaki/hackbench)
* [stress-ng](http://kernel.ubuntu.com/~cking/stress-ng/)
* [PicoTCP](https://github.com/tass-belgium/picotcp)
## 仿效 Linux 的考量
* Linux kernel data structure
* printk()
* Process: fork() clone() exit()
* Scheduler: preemption, sched_yeild()
* semaphore, mutex, spinlock
* timer
* Wait queue
* Interrupt: softirq, gen_irq
* slab: malloc(), free()
* mmap()
* IO: char_dev(), block_dev()
* File system: VFS, romfs, mtdram
* pthread
* device driver: serial(), tty
## 背景知識
- [Introduction: the ARM Cortex-M3 Exception / Interrupt](http://wiki.csie.ncku.edu.tw/embedded/arm-exceptions.pdf)
- [ARM Cortex-M3](https://www.slideshare.net/GauravVerma3/arm-cortex-processor-compatibility-mode)
- [STM32 程式開發:以 GNU Toolchain 為例](https://docs.google.com/document/d/1Ygl6cEGPXUffhTJE0K6B8zEtGmIuIdCjlZBkFlijUaE/edit)
- [Build minimal ARM Kernel from Scratch](https://paper.dropbox.com/doc/9CQXvokHAXZSSlFMBp1t6)
## ARM Cortex-M 機制
首先我們先看到 ARM Cortex-M3的架構圖,今天我們要來介紹 M系列的特色之一 Nested Vectored Interrupt Controller (NVIC),我想大家比較熟悉的詞是 Nested Interrupts,也就是在 ISR時還有可能觸發其他的 IRQ,並且有機會可以搶佔現在的 ISR,而這個 Feature在早已存在在 Linux,但他是透過軟體來實現所謂的 Nested Interrupts,而 NVIC主要是透過硬體將原本軟體所想要的功能實作出來。

注意到上面寫到支援 Interrupts的上限,和可支援的 Priority區間,這邊注意到這是官方給定的,但還是要由平台製造商(STMicro, NXP ...)決定。

這邊特別補充一下 NVIC支援的 Priority,在即時系統裡,我們希望愈高 Priority的任務可以愈快得到回應或開始執行,這同理到 Interrupts上,照裡來說 Systick 的 Priority應該要比 UART RX還來得高,因為時間的更新是比較重要且這關係到任務的排程,所以高優先權的 Interrupt是可以 Preempt低優先權的 Interrupt。
介紹完 NVIC的特性之後我們可以開始介紹不同的 Exceptions了

注意到幾個欄位,分別是 Priority 和 Vector address,有一些 Interrupt 的 Priority是固定的,也有一些是 Configurable,再來是 Vector address,也就是他們相對應的 Handler在 Vector Table的 offset。
### SVC
使用`svc`這個 instruction所啟動的 Interrupt,SVC是屬於 Synchronous Exception
code:
https://github.com/Piko-RT/pikoRT/blob/master/libc/piko/syscalls.S#L8
### PendSV
Interrupt-driven Supervisor-calling mechanism
code:
https://github.com/Piko-RT/pikoRT/blob/master/drivers/timer/systick.c#L110


### HardFault, MemFault, UsageFault, BusFault
code:
https://github.com/Piko-RT/pikoRT/blob/master/arch/v7m-faults.c#L32

### Tail-chaining
這是一個非常強大的機制,當現在處理的 Interrupt結束時,會去跟下一個 Interrupt的 Priority比較,如果沒有達到 Preemption的需求,他將會 trigger tail-chaining這個機制,核心概念就是當目前有其他的 interrupt pending,應該儘快處理下一個 interrupt而不必回去 user context。

### Late-arrival
這個機制就如同字面上的解釋,處理哪個 Interrupt可以在 Stacking之後再決定,所以 IRQ1 雖然比較晚到(優先權更高),但他卻可以先被處理。

Reference:
* - [Introduction: the ARM Cortex-M3 Exception / Interrupt](http://wiki.csie.ncku.edu.tw/embedded/arm-exceptions.pdf)
* [A Beginner’s Guide on Interrupt Latency - and Interrupt Latency of the Arm Cortex-M processors](https://community.arm.com/processors/b/blog/posts/beginner-guide-on-interrupt-latency-and-interrupt-latency-of-the-arm-cortex-m-processors?CommentId=9cca204a-05eb-4ffb-8ead-dc4243e98f59)
* [Using Cortex-M3/M4/M7 Fault Exceptions](http://www.keil.com/appnotes/files/apnt209.pdf)
* [ARM® Cortex®‑M4 Processor Technical Reference Manual](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100166_0001_00_en/index.html)
## CMSIS
有使用過嵌入式系統的朋友都知道有一種痛,叫作查 Reference Manual,不外乎就是找我要寫入或讀取哪個記憶體位置才能設定 Timer,這邊要特別注意的是從 0xA0000000 - 0xEFFFFFFF 其實是定址空間的範圍中,硬體暫存器對應的位址,也就是所謂的 Peripheral Register Mapping,以下是 M系列的 Memory Map

而 Cortex Microcontroller Software Interface Standard (CMSIS)能幫我們省去這個麻煩,他有定義一系列的 Macro,讓我們使用,不僅如此,有了 CMSIS還能讓我們很輕鬆的 Porting到其他 M系列,以下是他的架構圖:

我們比較常使用的是 CMSIS-CORE這部份,它讓我們能更容易的操作 Peripheral Register
code:
https://github.com/Piko-RT/pikoRT/blob/master/target/stm32p103/init.c#L136
Reference:
* [CMSIS-doc]http://www.keil.com/pack/doc/CMSIS/General/html/index.html
## STM32Cube HAL

code:
https://github.com/Piko-RT/pikoRT/blob/master/target/stm32f429/init.c#L32
Reference:
* [STM32Cube-doc](http://www.keil.com/pack/doc/STM32Cube/General/html/index.html)
## Tool
```
主要介紹這些工具在專案裡扮演什麼角色
```
- [ ] [gef](https://github.com/hugsy/gef)
- [ ] [gdb](https://www.gnu.org/software/gdb/)
- [ ] [st-link](https://github.com/texane/stlink)
- [ ] [openocd](http://openocd.org/)
## rtmutex
```
從 PREEMPT_RT 切入
介紹 RT 是 uni kernel
spin_lock 都換成 rtmutex
介紹 rtmutex 的 dependance
pi-futex 簡介
可以考慮介紹 futex
特殊 feature PI-supported, lockdep
這兩個可以講很久是重點
```



* PI-chain
* plist
LWN: https://lwn.net/Articles/131299/
code: http://elixir.free-electrons.com/linux/latest/source/include/linux/plist.h#L85
雜亂的 tracing code:
https://hackmd.io/GwMwHA7ARuCsC0UBMwDG8AsBDEATRsUADPNERAMx5KypGpA=
Reference:
* [PI-futex.txt](https://www.kernel.org/doc/Documentation/pi-futex.txt)
* [rtmutex.txt](https://www.kernel.org/doc/Documentation/locking/rt-mutex.txt)
* [rtmutex-design.txt](https://www.kernel.org/doc/Documentation/locking/rt-mutex-design.txt)
## tty
```
沒辦法 只好從歷史開始 螢幕+鍵盤很貴
經過時間淬鍊 什麼都可以是 tty
tty 變成app interface
n_tty 專門來做 serialization 也就是從 tty接丟下去
serial core 低階interface
實際底層實作
```


Issue:
https://github.com/Piko-RT/pikoRT/issues/14
Reference:
* [Essential Linux Device Driver](https://doc.lagout.org/operating%20system%20/linux/Essential%20Linux%20Device%20Drivers.pdf)
* [蝸窩科技 - tty framework](http://www.wowotech.net/sort/tty_framework)
## 我想幫忙,從哪裡下手?
* 嘗試移植較精簡的程式,著手回報
* 案例: [MicroPython](https://github.com/Piko-RT/pikoRT/pull/16)
* 測試、提交錯誤,協助分析: https://github.com/Piko-RT/pikoRT/issues
* 幫忙撰寫技術文件、釐清程式碼行為和註解
* 拿來當作畢業專題和研究題目 (free support!)