转载至:http://wiki.csie.ncku.edu.tw/embedded/xenomai
下載 Raspbian http://www.raspberrypi.org/downloads/
Install Cross complier
cd <working dir>
wget https://github.com/raspberrypi/tools/archive/master.tar.gz
tar xzfv master.tar.gz
git clone -b rpi-3.8.y --depth 1 git://github.com/raspberrypi/linux.git linux-rpi-3.8.y
git clone git://git.xenomai.org/xenomai-head.git xenomai-head
wget https://www.dropbox.com/s/dcju74md5sz45at/rpi_xenomai_config
cd linux-rpi-3.8.y
patch -Np1 < ../xenomai-head/ksrc/arch/arm/patches/raspberry/ipipe-core-3.8.13-raspberry-pre-2.patch
cd <working dir>
./xenomai-head/scripts/prepare-kernel.sh --arch=arm --linux=linux-rpi-3.8.y --adeos=xenomai-head/ksrc/arch/arm/patches/ipipe-core-3.8.13-arm-3.patch
cd linux-rpi-3.8.y
patch -Np1 < ../xenomai-head/ksrc/arch/arm/patches/raspberry/ipipe-core-3.8.13-raspberry-post-2.patch
mkdir linux-rpi-3.8.y/build
cp rpi_xenomai_config linux-rpi-3.8.y/build/.config
cd linux-rpi-3.8.y
make mrproper
make ARCH=arm O=build oldconfig
make ARCH=arm O=build CROSS_COMPILE=/home/$USER/workspace/tools-master/arm-bcm2708/arm-bcm2708hardfp-linux-gnueabi/bin/arm-bcm2708hardfp-linux-gnueabi-
make ARCH=arm O=build INSTALL_MOD_PATH=dist modules_install
make ARCH=arm O=build INSTALL_HDR_PATH=dist headers_install
find build/dist/include \( -name .install -o -name ..install.cmd \) -delete
編譯好的kernelImage,移到SD卡的 /boot/
路徑下並更改名稱為kernel.img
將linux-rpi-3.8.y/build/dist
中的Module,移到SD卡中的/lib/modules
Compile Xenomai
cd xenomai-head
export PATH=../tools-master/arm-bcm2708/arm-bcm2708hardfp-linux-gnueabi/bin/:$PATH
./configure --host=arm-bcm2708hardfp-linux-gnueabi
cd src
mkdir dist
make install DESTDIR=`pwd`/dist
dist中會出現usr/xenomai
, 將這個資料夾移到sd卡中 /usr/
用 minicom 連進 raspberry pi 中執行以下動作
export PATH=/usr/xenomai/bin:$PATH
export LD_LIBRARY_PATH=/usr/xenomai/lib
sudo modprobe xeno_posix
Soft Real Time
根據 Scheduling 的結果執行任務。
95% Hard Real Time
系統至少有 95% 在指定時間內完成任務。
100% Hard Real Time
系統能 100% 在指定時間內完成任務。
Safe Real Time
系統能 100% 在指定時間內完成任務。
Xenomai是一個linux kernel的patch 藉由在底層增加一個架構 負責硬體與接收interrupt 並將interrupt 傳給上層的OS(這邊稱為domain)
這個底層的架構是Adeos 是另一個open source的project
在api呼叫上可以看到不同層級的抽象化
ipipe_XXX -> rthal_XXX -> xnXXX
負責傳送interrupt的程式稱為ipipe 示意圖 http://www.xenomai.org/documentation/xenomai-2.6/html/pictures/life-with-adeos-img4.jpg
可以找到ipipe_raise_irq()將interrupt推到pipeline
在ipipe上每個domain都有自己的優先度 高優先度的domain會先接收到interrupt 高優先度的domain的thread 可以preempt低優先度domain的thread
主要負責處理irq 與 timer(HRT), ipipe的工作很簡單 就是設定timer並將interrupt往上丟
相關檔案︰
gic.c :
Generic Interrupt Controller, Interrupt prioritization and distribution to each CPU interface. This is known as the Distributor. Priority masking and preemption handling for each CPU. This is known as the CPU Interface.
it8152.c:IRQ相關
timer-sp.c:dual timer module(sp804)
vic.c:
The VIC provides a software interface to the interrupt system. In a system with an interrupt controller, software must determine the source that is requesting service and where its service routine is loaded. A VIC does both of these in hardware.
功能為提供一個programable的介面讓使用者設定
ipipe-tsc.c:設定精準度(刻度)
ipipe/compat.c:interrupt
sched/clock.c:取得cpu_clock 解析度為nanosecond,開機後從0開始上數
GIC大約是上圖的distributor的位置
VIC則是CPU interface的位置
但raspberry pi只有一顆CPU所以不會有SMP與 CPU affinity設定的問題
Hardware Abstract Layer:process 透過HAL呼叫ipipe的服務。這一層主要是包裝ipipe 與底層資訊 讓nucleus可以不用看到硬體資訊。
Xenomai的kernel, 包含一個scheduler,優先執行real-time tasks.
優先處理realtime task ,linux也被視為其中一個thread,本身也有scheduler,但須等到沒有real-time task時(idle state),才會執行linux thread
呼叫xenomai的界面, 有native rtdm posix等。
與 RT-PREEMPT 途徑的差異?
原本無法preempt的地方讓他可以preemt,讓spinlock 區塊在區分成可以preempt的地方跟不能preempt的地方,將IRQ handler移到thread中執行。
Priority inheritance 是讓握有spinlock 或 semaphore的process可以暫時的提高優先權 讓他可以盡快做完critical section釋放spinlock或semaphore
高Priority的 process才有辦法繼續執行
RT_PREEMPT 與 xenomai的差異
RT_PREEMPT是基於linux架構去改進 讓更多地方能preempt 達到real-time的能力
Xenomai則是改變整個系統架構 新增一個scheduler與IRQ管理的機制
讓處理real-time task流程簡化到只剩ipipe->scheduler 就能執行
不會因linux龐大的架構影響到real-time task的處理時間
pi@raspberrypi:~$ cat /proc/xenomai/stat CPU PID MSW CSW PF STAT %CPU NAME 0 0 0 206 0 00500080 100.0 ROOT 0 0 0 2688553 0 00000000 0.0 IRQ3: [timer]
當process從primary mode轉成secondary mode或是secondary mode轉成primary mode時,將會紀錄一次的轉換。
cyclictest的RT task因為會執行到memset,所以會從xenomai schedule跳到linux schedule,MSW+1,而執行完memset後將在跳回xenomai schedule,故再+1
pi@raspberrypi:~$ sudo /usr/xenomai/bin/cyclictest >/dev/null 2>/dev/null & [1] 2253
pi@raspberrypi:~$ ps aux | grep -i "cy" root 2253 0.5 0.3 4580 1464 ? S 03:34 0:00 sudo /usr/xenomai/bin/cyclictest root 2254 2.7 0.4 2340 2132 ? SLl 03:34 0:00 /usr/xenomai/bin/cyclictest pi 2259 0.0 0.1 3540 820 ttyAMA0 S+ 03:34 0:00 grep --color=auto -i cy
pi@raspberrypi:~$ cat /proc/xenomai/stat CPU PID MSW CSW PF STAT %CPU NAME 0 0 0 255 0 00500080 100.0 ROOT 0 2254 1 1 0 00b00380 0.0 cyclictest 0 2256 2 48 0 00300184 0.0 cyclictest 0 0 0 2913946 0 00000000 0.0 IRQ3: [timer]
pi@raspberrypi:~$ watch -n 1 cat /proc/xenomai/stat Every 1.0s: cat /proc/xenomai/stat Wed Jan 8 03:38:43 2014 CPU PID MSW CSW PF STAT %CPU NAME 0 0 0 442 0 00500080 99.9 ROOT 0 2254 1 1 0 00b00380 0.0 cyclictest 0 2256 2 235 0 00300184 0.0 cyclictest 0 0 0 2953543 0 00000000 0.1 IRQ3: [timer]
在這邊可以看到cyclictest有兩個pid,因為/usr/xenomai/bin/cyclictest它會先創一個thread,並讓這個thread跑nanosleep,所以會有兩個process。接著看向CSW,pid 2254的cyclictest, 他的CSW只有1。pid 2256的卻有235,這是因為2256是一個xenomai realtime task,而 2254是一個 linux的process,所以2256會優先執行,直到realtime task都做完才會換low priority的linux domain process取得CPU,因此2254的CSW值才會是1而沒有增加。
pi@raspberrypi:~$ sudo kill 2254 pi@raspberrypi:~$ ps aux | grep -i "cy" pi 2324 0.0 0.1 3540 820 ttyAMA0 R+ 03:46 0:00 grep --color=auto -i cy [1]+ Done sudo /usr/xenomai/bin/cyclictest > /dev/null 2> /dev/null pi@raspberrypi:~$ sudo /usr/xenomai/bin/cyclictest -p FIFO >/dev/null 2>/dev/null &
在我們了解MSW時,嘗試了在-p後面加上了文字(如:FIFO、RR……)
發現MSV的值開始往上增加,也發現一開始對於MSW的定義理解錯誤
CPU PID MSW CSW PF STAT %CPU NAME 0 0 0 75266 0 00500080 99.9 ROOT 0 2978 1 1 0 00b00380 0.0 cyclictest 0 2980 2 26846 0 00300184 0.0 cyclictest 0 7559 1 1 0 00b00380 0.0 cyclictest 0 7561 66 130 0 00b00184 0.0 cyclictest 0 0 0 11266931 0 00000000 0.1 IRQ3: [timer]
cyclictest -p 90 - m -c 0 -i 200 -n -h 100 -q -l 1000 >log
cyclictest -p 90 - m -c 0 -i 200 -n -h 100 -q -l 1000 >log
/usr/xenomai/bin/cyclictest -p 90 - m -c 0 -i 200 -n -v 100 -q -l 100" >log
概念:設定一個時間間隔->取得現在時間->讓process 睡一個間隔->process醒來後再取一次時間->比對兩次取得的時間差與設定的間隔差距
pseudocode:
clock_gettime((&now)) next = now + par->interval while (!shutdown) { clock_nanosleep((&next)) clock_gettime((&now)) diff = calcdiff(now, next) # update stat-> min, max, total latency, cycles # update the histogram data next += interval }
造成時間差的原因
Cyclictest 實作流程
1.cyclictest建立一個timerthread, 它一個 realtime 的 thread
2.timerthread會重複的執行取第一次時間 nanosleep(interval) 取第二次時間 比對兩次時間差與interval的差異
3.最後將結果輸出在terminal
Clock_nanosleep 的 timer
clock_nanosleep 使用的timer 是 high resolution timer(HRT) ,讓睡眠時間可以更精確,達到nanosecond的精準度(但還是要看硬體能提供的精準度)
因為能在更準確得時間讓process醒來並取的nanoscecond單位的時間 所以可以計算到由systick無法計算到的duration + latency
Clock_nanosleep 實作流程
1.使用 spinlock (xnlock_get_irqsave) 令 CPU 不接受 Interrupt
2.使用 xnpod_suspend_thread 改變目前 thread 的狀態
3.使用 xntimer_get_timeout_stopped 取得 tick
4.使用 ticks2ts 轉換時間單位
int clock_nanosleep (clockid_t clock_id, int flags, const struct timespec *rqtp, struct timespec *rmtp) { xnthread_t *cur; spl_t s; int err = 0; if (xnpod_unblockable_p()) return EPERM; if (clock_id != CLOCK_MONOTONIC && clock_id != CLOCK_REALTIME) return ENOTSUP; if ((unsigned long)rqtp->tv_nsec >= ONE_BILLION) return EINVAL; if (flags & ~TIMER_ABSTIME) return EINVAL; cur = xnpod_current_thread(); xnlock_get_irqsave(&nklock, s); thread_cancellation_point(cur); xnpod_suspend_thread(cur, XNDELAY, ts2ticks_ceil(rqtp) + 1,clock_flag(flags, clock_id), NULL); thread_cancellation_point(cur); if (xnthread_test_info(cur, XNBREAK)) { if (flags == 0 && rmtp) { xnsticks_t rem; rem = xntimer_get_timeout_stopped(&cur->rtimer); xnlock_put_irqrestore(&nklock, s); ticks2ts(rmtp, rem > 1 ? rem : 0); } else xnlock_put_irqrestore(&nklock, s); return EINTR; } xnlock_put_irqrestore(&nklock, s); return err; }
root@raspberrypi:/home/pi# sudo ./cyclictest -t1 -p 80 -i 500 -l 100000 # /dev/cpu_dma_latency set to 0us policy: fifo: loadavg: 0.00 0.01 0.05 1/61 2064 T: 0 ( 2063) P:80 I:500 C: 100000 Min: 27 Act: 49 Avg: 42 Max: 1060
Linux raspberrypi 3.6.11+ #474 PREEMPT Thu Jun 13 17:14:42 BST 2013 armv6l GNU/Linux Min: 22 Act: 31 Avg: 32 Max: 169
Linux raspberrypi 3.8.13-core+ #1 Thu Feb 27 03:02:16 CST 2014 armv6l GNU/Linux Min: 1 Act: 5 Avg: 6 Max: 41
root@raspberrypi:/home/pi# /usr/xenomai/bin/cyclictest -t1 -p 80 -i 500 -l 10000 0.08 0.06 0.05 1/61 2060 T: 0 ( 2060) P:80 I: 500 C: 100000 Min: -4 Act: -2 Avg: 0 Max: 30
T:thread
P:priority
I:interval
C:執行cycle數
Min:最小延遲
Act:此次延遲時間
Avg:平均延遲
Max:最大延遲
最重要的是Max值 為了確保realtime 要能知道worst case
讓開發者可以評估最差的情況可以在多少時間內可以做出回應
討論&紀錄 https://embedded2014.hackpad.com/Xenomai-raspberry-note-XwJtuQn9nkD
整理 https://embedded2014.hackpad.com/Xenomai-z2CJPjPLTer