Hi! Now i finished the inital work for the RealTime Extension for uClinux (RTEC), that basicaly follow V. Yodaiken's idea of enhancing Linux with RT-capabilities. Currently, there is only the very basic interrupt-handling implemented, but along with a timer, you can have at least on periodic "RT-Task", i.e. for measurement with minimum jitter (or for a scheduler that runs in "period mode" shouldn't be to compicate to port from RTAI or RTL to RTEC) Actualy, the thing should be called "RTEC". When it looks more like RTL or RTAI, then we can rename it :-) If the code will be part of RTL or RTAI in future: quite fine! The Code is GPL, of course. have fun, but beware: this stuff is bleading edge! Bernhard P.S.: Sorry for x-posting to so many mailing lists :-) Realtime Extension for uClinux - Version 0.1 ******************************************** 1. Introduction 2. API definition and demo application 3. Installation Instruction 4. Detailed theory of operation 5. Thanks 1. Introduction ############### uClinux is the port of the Linux Operation System to target-processors without memory management unit - expecialy for embedded systems - initiated by D. Jeff Dionne and Kenneth Albanowski. RTLinux is a realtime extension for the Linux Operating System (i386-architecture) to have minimum possible and (rather) deterministic interrupt-latencies initiated by Victor Yodaiken and Michael Barabanov. As CPUs without MMU are cheaper and thus more often used in embedded environments with high production volumes, it is a good idea to join both results. Actually, there is only the basic interrupt handling mechanism done for M68328, along with a simple timer function. But that already enables the potential user of having at least one periodic hard-realtime "task" using a timer and sporadic "rt-tasks" (interrupts). All rt-interrupts are scheduled "round robin" and are actually not premptable! Furthermore, the current efforts are only resulting in patches to the MC68328, since the only "platform" i actually have for testing is the xcopilot. Consequently there doesn't exist measurements about worst case latencies - there even doesn't exist a proove that the damn stuff i have coded is hard-rt capable at all :-) But never mind: there is a detailed discussion about every change in the source code later in this "paper". But first, i'd like to invent the simplest API you have probably ever seen in your live :-) Then there will be a detailed description how to install the stuff. And last but not least, the patch itself will be mentioned. 2. API definition and demo application ###################################### Currently, there doesn't exist a priority scheduler. You can only do interrupt handling, but using timer1 of the M68328, you can have one "period hard-rt task" When a RT-interrupt occurs, then *all* interrupts are disabled. The rt-interrupts could be re-enabled selectivly while running a rt-interrupt service routine, so that higher priority interrupts could take over control, but i didn't yet bothered around with that. When a linux-interrupt occurs, all interrupts occupied by linux-drivers are disabled selectivly, so that a rt-interrupt can take over control at any time. The rt-stuff is initialisized during kernel-boot (just after linux-irq-init and before linux-scheduler-init), and a the function rt_init_app() is called automaticaly to start the rt-application of the user, that currently has to be placed in the file ../linux/arch/m68knommu/platform/68328/rt_application.c (the Makefile is prepared) Actually, there are only these few API-functions available (defined in ../linux/arch/m68knommu/platform/68328/rt_core.c): extern int rt_request_irq(unsigned int irq, void (*handler)()); extern void rt_free_irq(unsigned int irq); extern void rt_starttimer(int period); rt_request_irq() enables and binds an interrupt to given handler, while rt_free_irq() disables it. rt_starttimer() just startes timer1 of the M68328 with the given period (clock=sysclock, prescalar=1, sysclock-divider=1). With this "API" you are able to create a simple rt-application such like that you can find in ../linux/arch/m68knommu/platform/68328/rt_application.c: after patching the kernel (see Chapter 3): rt_init_app() starts timer1 and occupies the interrupts for timer1 and pen. After booting the kernel with xcopilot, you can see two "counter-dashes" on the LCD-Display. Have a look into the comments of rt_application.c for detailed information. (The second "dash" will apear, when clicking the touchpanel. As the pen-interrupt is pending as long as you press the mouse button, the standard linux-kernel will not have a chance run until you are releasing the button. So don't forget to reset the interrupt - if possible, i.e. timer1). 3. Installation instruction ########################### If you just want the patch and know everything else, then fetch it at: wget http://www.rcs.ei.tum.de/~kuhn/uclinux/uClinux-2.0.38.1pre1_rt-0.1.diff.gz Don't forget to patch xcopilot due to a broken timer-emulation: wget http://www.rcs.ei.tum.de/~kuhn/uclinux/xcopilot-0.6.6_timerpatch.diff the following is a step by step description how to get things going: Downloading all Files: ====================== DOWNLOAD_PATH=/var/tmp #(1) cd $DOWNLOAD_PATH # stuff for xcopilot wget http://xcopilot.cuspy.com/build/xcopilot-0.6.6.tar.gz wget http://www.rcs.ei.tum.de/~kuhn/uclinux/xcopilot-0.6.6_display.c_diff wget http://www.rcs.ei.tum.de/~kuhn/uclinux/xcopilot-0.6.6_timerpatch.diff # stuff for gcc wget ftp://ftp.gnu.org/gnu/gcc/gcc-2.7.2.3.tar.gz wget ftp://ftp.gnu.org/gnu/binutils/binutils-2.9.1.tar.gz wget http://www.uclinux.org/pub/uClinux/uclinuxgcc-kit-160899.tar.gz # stuff for kernel wget ftp://ftp.de.kernel.org/pub/linux/kernel/v2.0/linux-2.0.38.tar.gz wget http://www.uclinux.org/pub/uClinux/uClinux-2.0.38.1pre1.diff.gz wget http://www.rcs.ei.tum.de/~kuhn/uclinux/uClinux-2.0.38.1pre1_rt-0.1.diff.gz # stuff for user-space # wget http://www.uclinux.org/pub/uClinux/pilot-310899.tar.gz #(2) wget http://www.uclinux.org/pub/uClinux/pilot-160899.tar.gz wget http://www.uclinux.org/pub/uClinux/genromfs-0.3.tar.bz2 wget http://www.rcs.ei.tum.de/~kuhn/uclinux/genromfs-0.3.patch # wget http://www.rcs.ei.tum.de/~kuhn/uclinux/coff2flt-0.4.tar.bz2 #(2) wget ftp://ryeham.ee.ryerson.ca/pub/uClinux/coff2flt-0.3.tar.bz2 wget http://www.uclinux.org/pub/uClinux/uC-libc-160899.tar.gz wget http://www.uclinux.org/pub/uClinux/uC-libm-060199.tar.bz2 (1) or whereever you want to have downloaded your files (2) i didn't succeeded with the more recent versions ... Installing xcopilot: ==================== DOWNLOAD_PATH=/var/tmp #(1) cd /tmp #(2) tar -xzf $DOWNLOAD_PATH/xcopilot-0.6.6.tar.gz cd xcopilot-0.6.6 patch display.c < $DOWNLOAD_PATH/xcopilot-0.6.6_display.c_diff #(3) patch -p1 < $DOWNLOAD_PATH/xcopilot-0.6.6_timerpatch.diff #(4) ./configure ; make cp xcopilot /usr/local/bin #(5) cd .. ; rm -rf xcopilot-0.6.6 (1) or whereever you have downloaded your files (2) or whereever you have spare disk space (who has such?) (3) only needed if you want to emulate the buttons (power, phonebook, etc.) of the PalmPilot by keyboard. (4) mandatory since timer1 is broken in xcopilot (5) usualy, you will have to be root to do this. Installing gcc: =============== DOWNLOAD_PATH=/var/tmp #(1) cd /tmp mkdir m68knommu-gcc cd m68knommu-gcc cp $DOWNLOAD_PATH/binutils-2.9.1.tar.gz . cp $DOWNLOAD_PATH/gcc-2.7.2.3.tar.gz . tar -xzf $DOWNLOAD_PATH/uclinuxgcc-kit-160899.tar.gz cd uclinuxgcc-kit-160899 make #(2) cd ../.. rm -rf m68knommu-gcc echo export PATH=/usr/local/gnu/bin:\$PATH > /usr/local/bin/init68k #(3) chmod a+x /usr/local/bin/init68k #(3) (1) or whereever you have downloaded your files (2) be logged in as root to do this (3) when you do a ". init68k", you are prepared to crosscompile some stuff Installing (uC)linux-sources: ============================= DOWNLOAD_PATH=/var/tmp #(1) UCLINUX_PATH=/opt/src/uclinux #(2) install -d $UCLINUX_PATH # create directory # kernel stuff cd $UCLINUX_PATH tar -xzf $DOWNLOAD_PATH/linux-2.0.38.tar.gz cd linux gunzip -cd $DOWNLOAD_PATH/uClinux-2.0.38.1pre1.diff.gz | patch -p1 gunzip -cd $DOWNLOAD_PATH/uClinux-2.0.38.1pre1_rt-0.1.diff.gz | patch -p1 make menuconfig #(3) make clean && make dep ln -s ../pilot/romdisk/romdisk.img romfs.img # create missing link # user-land stuff cd $UCLINUX_PATH gunzip -cd $DOWNLOAD_PATH/pilot-160899.tar.gz | tar -x #(4) bzip2 -cd $DOWNLOAD_PATH/genromfs-0.3.tar.bz2 | tar -x # gunzip -cd $DOWNLOAD_PATH/coff2flt-0.4.tar.gz | tar -x bzip2 -cd $DOWNLOAD_PATH/coff2flt-0.3.tar.bz2 | tar -x # gunzip -cd $DOWNLOAD_PATH/uC-libc-310899.tar.gz | tar -x gunzip -cd $DOWNLOAD_PATH/uC-libc-160899.tar.gz | tar -x bzip2 -cd $DOWNLOAD_PATH/uC-libm-060199.tar.bz2 | tar -x cd genromfs-0.3 patch genromfs.c < $DOWNLOAD_PATH/genromfs-0.3.patch cd .. cd pilot patch -p1 < $DOWNLOAD_PATH/pilot-160899.rt-0.1.diff #(5) cd .. (1) or whereever you have downloaded your files (2) or whereever you have spare disk space (who has such?). ensure that the directory already exists. (3) just exit menuconfig and save the default (4) you will have to be root to unpack this, because of the directory "/dev" within the rom-filesystem. (5) disable network devices in "loattach" to get rid of anoying messages Compiling (uC)linux-sources: ============================ UCLINUX_PATH=/opt/src/uclinux cd $UCLINUX_PATH . init68k #(1) make -C genromfs-0.3 #(2) # make -C coff2flt-0.4 #(3) make -C coff2flt-0.3 make -C uC-libc make -C uC-libm make -C pilot #(4) make -C linux linux.rom #(4) xcopilot -romfile $UCLINUX_PATH/linux/linux.rom #(5) (1) from here on, we need m68k-gcc stuff (2) romfs-generator; ignore warnings (3) converter for coff fileformat to flat fileformat; ignore warnings (4) repeat this to steps whenever you change the source code (5) ignore errors about missing device "19" - that is just the nonexistant ethernet-device. Now, the rt-task should already run. The following instructions are only informal derived from earlier installation instructions. Login with telnet: ================== cat << EOF > /tmp/pilot_ppp #(1) TIMEOUT 5 >-->--> pppd EOF xcopilot -serial -romfile $UCLINUX_PATH/linux/vmlinux.rom & /usr/sbin/pppd 192.168.1.1:192.168.1.2 connect \ '/usr/sbin/chat -vf /tmp/pilot_ppp' /dev/ttyqe mtu 296 ping 192.168.1.2 #(2) telnet 192.168.1.2 (1) create a pppd-chat skript (2) mostly, telnet doesn't work without pinging the virtual PalmPilot - don't know why Setting up an NFS-connection ============================ First, we have to export the filesystem on server-side (assuming that basic NFS-Services are up and running): echo "192.168.1.2 pilot" >> /etc/hosts echo "/ pilot(rw,no_root_squash,link_relative,insecure)" >> /etc/exports #(1) killall -HUP rpc.mountd #(2) killall -HUP rpc.nfsd #(2) (1) I don't know, if the export-options are good ones (concerning security) (2) restart NFS-daemons Bring up XCopilot as described in "Login with telnet" (look above), login and type the following commands: cd /tmp mkdir mnt /bin/mount -t nfs -o rsize=256,wsize=256 192.168.1.1:/ mnt cd mnt ls -al #(1) (1) NFS-transfer is rather slow with XCopilot ... 4. Detailed theory of operation ############################### Here i will describe the kernel-patch step by step: ../linux/Makefile ----------------- Add compiler and assembler flag "-D__M68328_RTL__" for M68328 ../linux/arch/m68knommu/config.in --------------------------------- Just add an option for "Hard Real-Time Support" for M68328 ../linux/arch/m68knommu/defconfig --------------------------------- Defaulting to options that are more convenient for M68328 ../linux/arch/m68knommu/platform/68328/Makefile ----------------------------------------------- Add objects "rt_core.o" and "rt_application.o", that both will be compiled into the kernel. ../linux/arch/m68knommu/platform/68328/entry.S ---------------------------------------------- >From here on, it is begining to get interesting: basicaly, at the entrance of every interrupt handler, there is placed a piece of code that checks for rt-interrupts To do that, we need several macros: enable interrupts that are not mask in IMR (IMR = interrupt mask register) +#define ENABLE_HARDINTERRUPTS \ + andi.w #0xf8ff,%sr + disable all interrupts +#define DISABLE_HARDINTERRUPTS \ + ori.w #0x0700,%sr + enable linux-interrupt selectivly: unmask flags in IMR +#define ENABLE_LXINTERRUPTS \ + move.l SYMBOL_NAME(lx_imr),%d0; \ + not.l %d0; \ + and.l %d0,0xf304.w + disable linux-interrupt selectivly: mask flags in IMR +#define DISABLE_LXINTERRUPTS \ + move.l SYMBOL_NAME(lx_imr),%d0; \ + or.l %d0,0xf304.w + place this peace of code at the befining of ever interrupt handler: +#define ENTER_INTERRUPT \ + DISABLE_HARDINTERRUPTS; \ disable all irqs + SAVE_ALL; \ save registers + move.l 0xf310.w,%d0; \ check for ... + and.l SYMBOL_NAME(rt_imr),%d0; \ ... rt-interrupts + beq 0f; \ No? then it must have ... ... been a linux-irq ... ... -> process it! + jsr SYMBOL_NAME(process_rt); \ otherwise call process_rt() .. .. in ../68328/rt_core.c + move.l 0xf304.w,%d0; \ check if ... + not.l %d0; \ ... linux-irq ... + and.l 0xf310.w,%d0; \ ... occured ... + and.l SYMBOL_NAME(lx_imr),%d0; \ ... meanwhile ... + bne 0f; \ Yes? then process it + RESTORE_ALL; \ otherwise restore regs and exit +0: DISABLE_LXINTERRUPTS; \ before processing linux-irqs ... + ENABLE_HARDINTERRUPTS ... linux-irqs have to be ... ... disabled and the ... ... irq priority has to be lowered down, so that rt-irqs ... ... with a lower hardwired priority than the current linux-irq ... ... can also occur during linux-irq processing reenable linux-irqs when exiting a linux-interrupt +#define EXIT_INTERRUPT \ + ENABLE_LXINTERRUPTS; \ + RESTORE_ALL in function "resume", irqs have to be disabled and enabled. tell how to do that +#define ENABLE_INTERRUPTS_IN_RESUME ENABLE_LXINTERRUPTS +#define DISABLE_INTERRUPTS_IN_RESUME DISABLE_LXINTERRUPTS The following are fall-back macros, when Hard-RT is disabled via menuconfig, and they are just doing the usual thing as before: +#define ENTER_INTERRUPT \ + SAVE_ALL; \ + ori.w #0x700,%sr + +#define EXIT_INTERRUPT \ + RESTORE_ALL + +#define DISABLE_INTERRUPTS_IN_RESUME \ + ori.w #0x0700,%sr + +#define ENABLE_INTERRUPTS_IN_RESUME \ + movew %a1@(LTSS_SR),%sr + +#endif /* #ifdef __M68328_RTL__ */ RESTORE_ALL has to be replaced with EXIT_INTERRUPT several times - RESTORE_ALL /* Does RTE */ + EXIT_INTERRUPT at the begining of each interrupt handler, you will find this: - SAVE_ALL - oriw #0x700,%sr replace it with + ENTER_INTERRUPT replace the irq enable/disable functions in "resume" - oriw #0x0700,%sr + DISABLE_INTERRUPTS_IN_RESUME - movew %a1@(LTSS_SR),%sr + ENABLE_INTERRUPTS_IN_RESUME ../linux/arch/m68knommu/platform/68328/ints.c --------------------------------------------- tell how to enable or disable interrupts from within standrd linux-code (rt and non-rt fallback) +#ifdef CONFIG_M68328_RTL +#define ENABLE_IRQ(irq) ENABLE_LXIRQ(irq) +#define DISABLE_IRQ(irq) DISABLE_LXIRQ(irq) +#else +#define ENABLE_IRQ(irq) *(volatile unsigned long *)0xfffff304 &= ~(1<<irq) +#define DISABLE_IRQ(irq) *(volatile unsigned long *)0xfffff304 |= 1<<irq +#endif + replace old irq-enable code several times - *(volatile unsigned long *)0xfffff304 &= ~(1<<irq); + ENABLE_IRQ(irq); replace old irq-disable code several times - *(volatile unsigned long *)0xfffff304 |= 1<<irq; + DISABLE_IRQ(irq); only take care of linux-irqs in interupt pending register (in "do_irq"): +#ifdef CONFIG_M68328_RTL + unsigned long pend = *(volatile unsigned long *)0xfffff310 & lx_imr; +#else unsigned long pend = *(volatile unsigned long *)0xfffff310; +#endif ../linux/arch/m68knommu/platform/68328/rt_application.c ------------------------------------------------------- place your own rt-application code here, or have a look at the existing one. ../linux/arch/m68knommu/platform/68328/rt_core.c ------------------------------------------------ rt-core functions: rt_request_irq(), rt_free_irq(), rt_starttimer(); the rt-interrupt "scheduler" +asmlinkage void process_rt(void) { + + int ipr; + + /* repeat as long as there are rt-interrupt pending */ + while( ipr=(IPR & rt_imr) ) { + + /* check every (rt-)interrupt */ + int i,mask=1; + for(i=0;i<RT_INTERNAL_IRQS;i++) { + + /* interrupt pending? */ + if(ipr & mask) + + /* yes, then call handler (if exists) */ + if(rt_handler[i]) rt_handler[i](); + + mask<<=1; + }; + }; +}; ../linux/include/asm-m68knommu/system.h --------------------------------------- replace sti(), cli(), save_flags() and restore_flags(), so that only linux-interrupt are enabled or disabled selectivly. But to do that, we need some macros: +#define ENABLE_IMASK(irqmask) __asm__ __volatile__ ( \ + "and.l %0,0xf304.w\n\t" \ + : : "d" (~irqmask) : "memory" ); \ + +#define DISABLE_IMASK(irqmask) __asm__ __volatile__ ( \ + "or.l %0,0xf304.w\n\t" \ + : : "d" (irqmask) : "memory" ); \ + +#define ENABLE_LXIRQ(lxirq) { \ + unsigned long imask = 1 << lxirq; \ + lx_imr |= imask; \ + ENABLE_IMASK(imask); }; + +#define DISABLE_LXIRQ(lxirq) { \ + unsigned long imask = 1 << lxirq; \ + lx_imr &= ~imask; \ + DISABLE_IMASK(imask); }; + +#define ENABLE_RTIRQ(rtirq) { \ + unsigned long imask = 1 << rtirq; \ + rt_imr |= imask; \ + ENABLE_IMASK(imask); }; + +#define DISABLE_RTIRQ(rtirq) { \ + unsigned long imask = 1 << rtirq; \ + rt_imr &= ~imask; \ + DISABLE_IMASK(imask); }; + here are the redefinitions to avoid disabling rt-interrupts from within a linux-interrupt: +#define sti() ENABLE_IMASK(lx_imr); +#define cli() DISABLE_IMASK(lx_imr); +#define save_flags(x) __asm__ __volatile__( \ + "move.l 0xf304.w,%0\n\t" \ + "not.l %0\n\t" \ + "and.l lx_imr,%0\n\t" \ + : "=d" (x) : : "memory") +#define restore_flags(x) __asm__ __volatile__( \ + "move.l %0,0xf304.w\n\t" \ + : : "d" (~(x|rt_imr)) : "memory") ../linux/init/main.c -------------------- Just call rt_init() after linux-interrupt init and before linux-scheduler init 5. Thanks ######### Many thanks to K. Albanowski and J. Dionne for porting Linux, gcc and binutils to non-MMU-m68k and to M. Barabanov and V. Yodaiken for thier phantastic method to give linux hard realtime capabilites. Bernhard Kuhn, Fre Jan 7 05:27:42 CET 2000 --- [rtl] --- To unsubscribe: echo "unsubscribe rtl" | mail [EMAIL PROTECTED] OR echo "unsubscribe rtl <Your_email>" | mail [EMAIL PROTECTED] ---- For more information on Real-Time Linux see: http://www.rtlinux.org/~rtlinux/