--- title: Xenomai categories: embedded, arm, raspberrypi ... Building an RPi Xenomai Kernel ============================== * Install Cross complier .. code-block:: c cd wget https://github.com/raspberrypi/tools/archive/master.tar.gz tar xzf master.tar.gz * Download source files and patches - Download kernel .. code-block:: c git clone -b rpi-3.8.y --depth 1 git://github.com/raspberrypi/linux.git linux-rpi-3.8.y - Download Xenomai .. code-block:: c git clone git://git.xenomai.org/xenomai-head.git xenomai-head - Download minimal config .. code-block:: c wget https://www.dropbox.com/s/dcju74md5sz45at/rpi_xenomai_config * Apply patches - Apply ipipe core pre-patch .. code-block:: c cd linux-rpi-3.8.y patch -Np1 < ../xenomai-head/ksrc/arch/arm/patches/raspberry/ipipe-core-3.8.13-raspberry-pre-2.patch - Apply Xenomai ipipe core patch .. code-block:: c 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 - Apply ipipe core post-patch .. code-block:: c cd linux-rpi-3.8.y patch -Np1 < ../xenomai-head/ksrc/arch/arm/patches/raspberry/ipipe-core-3.8.13-raspberry-post-2.patch * Compile kernel - Create build directory .. code-block:: c mkdir linux-rpi-3.8.y/build - Configure kernel .. code-block:: c 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 - Compile .. code-block:: c make ARCH=arm O=build CROSS_COMPILE=../../tools-master/arm-bcm2708/arm-bcm2708hardfp-linux-gnueabi/bin/arm-bcm2708hardfp-linux-gnueabi- - Install modules .. code-block:: c make ARCH=arm O=build INSTALL_MOD_PATH=dist modules_install - Install headers .. code-block:: c 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`` * Cyclictest - Linux .. code-block:: c - Xenomai .. code-block:: c 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 DIST_DIR=dist dist中會出現``usr/xenomai`` 將這個資料夾移到sd卡中 ``/usr/`` 在raspberry pi中 .. code-block:: c export PATH=/usr/xenomai/bin:$PATH export LD_LIBRARY_PATH=/usr/xenomai/lib sudo modprobe xeno_posix 接著就能跑使用xenomai機制的cyclictest - RT_preempt 作業系統架構 =========== Xenomai是一個linux kernel的patch 藉由在底層增加一個架構 負責硬體與接收interrupt 並將interrupt 傳給上層的OS(這邊稱為domain) 這個底層的架構是Adeos 是另一個open source的project 基本架構示意圖 http://dchabal.developpez.com/tutoriels/linux/xenomai/images/image04.png 在api呼叫上可以看到不同層級的抽象化 ipipe_XXX -> rthal_XXX -> xnXXX 上一層對下一層的操作方式基本上會固定 但下一層的實作一硬體或config有所差異 負責傳送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 * 與 RT-PREEMPT 途徑的差異? 實作 ================== Context switch .. code-block:: prettyprint ksrc/arch/arm/switch.S /* /* * Switch context routine. * * Registers according to the ARM procedure call standard: * Reg Description * r0-r3 argument/scratch registers * r4-r9 variable register * r10=sl stack limit/variable register * r11=fp frame pointer/variable register * r12=ip intra-procedure-call scratch register * r13=sp stack pointer (auto preserved) * r14=lr link register * r15=pc program counter (auto preserved) * * Copied from __switch_to, arch/arm/kernel/entry-armv.S. * Right now it is identical, but who knows what the * future reserves us... * * XXX: All the following config options are NOT tested: * CONFIG_IWMMXT * * Calling args: * r0 = previous task_struct, r1 = previous thread_info, r2 = next thread_info */ ENTRY(rthal_thread_switch) add ip, r1, #TI_CPU_SAVE ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack THUMB( str sp, [ip], #4 ) THUMB( str lr, [ip], #4 ) load_tls r2, r4, r5 #ifdef USE_DOMAINS ldr r6, [r2, #TI_CPU_DOMAIN] #endif clear_exclusive_monitor switch_tls r1, r4, r5, r3, r7 #ifdef USE_DOMAINS mcr p15, 0, r6, c3, c0, 0 @ Set domain register #endif fpu_switch r4 ARM( add r4, r2, #TI_CPU_SAVE ) ARM( ldmia r4, {r4 - sl, fp, sp, pc} ) @ Load all regs saved previously THUMB( add ip, r2, #TI_CPU_SAVE ) THUMB( ldmia ip!, {r4 - sl, fp} ) @ Load all regs saved previously THUMB( ldr sp, [ip], #4 ) THUMB( ldr pc, [ip] ) ENDPROC(rthal_thread_switch) Timer +++++++++++ Scheduler +++++++++++ 依據預設 Xenomai 組態,nucleus 程式碼的兩個檔案未被編譯進去: - sched-tp.c - sched-sporadic.c xnpod +++++++++++ .. code-block:: prettyprint struct xnpod { xnflags_t status; /*!< Status bitmask. */ xnsched_t sched[XNARCH_NR_CPUS]; /*!< Per-cpu scheduler slots. */ xnqueue_t threadq; /*!< All existing threads. */ #ifdef CONFIG_XENO_OPT_VFILE struct xnvfile_rev_tag threadlist_tag; #endif xnqueue_t tstartq, /*!< Thread start hook queue. */ tswitchq, /*!< Thread switch hook queue. */ tdeleteq; /*!< Thread delete hook queue. */ atomic_counter_t timerlck; /*!< Timer lock depth. */ xntimer_t tslicer; /*!< Time-slicing timer for aperiodic mode */ int tsliced; /*!< Number of threads using the slicer */ int refcnt; /*!< Reference count. */ #ifdef __XENO_SIM__ void (*schedhook) (xnthread_t *thread, xnflags_t mask); /*!< Internal scheduling hook. */ #endif /* __XENO_SIM__ */ }; 硬體驅動原理 =========== * 硬體使用 Raspberry Pi 效能表現 ======= * `Cyclictest`_ * Test case: POSIX interval timer, Interval 500 micro seconds,. 100000 loops, 100% load. - Commandline: cyclictest -t1 -p 80 -i 500 -l 100000 * 使用 PREEMPT LINUX .. code-block:: prettyprint 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 * 使用 RT-PREEMPT .. code-block:: prettyprint 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 * 使用 Xenomai .. code-block:: prettyprint 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 .. code-block:: prettyprint 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 * cyclictest 做法 概念: 取得現在時間接著讓process睡一個間隔 等process醒來後再取一次時間 比對兩次取得的時間差與設定的間隔差距 pseudocode: .. code-block:: prettyprint 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 } 造成這時間差的可能原因: timer精準度 interrupt latency interrupt handler duration scheduler latency scheduler duration context switch 討論用 ====== https://embedded2014.hackpad.com/Xenomai-raspberry-note-XwJtuQn9nkD 組員 ==== * 向澐 * 林家宏 * 呂科進 * 趙愷文 * 阮志偉 * 陳建霖 參考資料 ======= * https://code.google.com/p/picnc/wiki/RPiXenomaiKernel * https://code.google.com/p/picnc/wiki/CreateRaspbianLinuxCNC * http://www.camelsoftware.com/firetail/blog/raspberry-pi/real-time-operating-systems/ * `Quadruped Linux robot feels its way over obstacles`_ * ` Choosing between Xenomai and Linux for real-time applications`_ * `Real Time Systems`_ * `http://www.cs.ru.nl/lab/xenomai/exercises/`_