[uCsimm] RT-Extension for uClinux version 0.1

From: Bernhard Kuhn (kuhn@batian.lpr.e-technik.tu-muenchen.de)
Date: Thu Jan 06 2000 - 23:51:47 EST


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
This message resent by the ucsimm@uclinux.com list server http://www.uClinux.com/



This archive was generated by hypermail 2b30 : Sun Apr 07 2002 - 00:01:33 EST