Electrum Xenomai Notes
From Manuals
Real-Time (Xenomai) Debian Linux from Scratch for Electrum100
Contributed by: Razvan-Ionut Stoian - razvan@stoian.us
This tutorial is split into four sections: compilation of a kernel for Electrum100, installation of Debian Squeeze root filesystem and Xenomai user libraries onto the SBC, and a short Xenomai development example (user-land).
1. Preparing a kernel for Electrum100
The newest kernel supported by Xenomai for ARM systems was 2.6.35.9, so you need to apply the provided patch to the kernel sources downloaded from http://www.kernel.org.
First, you need to have a MicroSD card partitioned like this:
- first partition: ext2 (max 10 MB) - for uImage
- second partition: ext4 (at least 100 MB) - for the root filesystem
- third partition: swap (128 MB) - for swap space (optional)
Patch the kernel tree, so that your machine can be supported.
$ cp *.patch linux-2.6.35.9 $ cd linux-2.6.35.9 $ patch -p1 < *.patch
Next, rename the provided configuration file as .config, configure (or leave it as it is) and compile your kernel and modules with the following commands:
$ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- menuconfig $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- uImage $ make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-
Transfer uImage on the boot partition of your MicroSD and install the modules on the second partition.
$ sudo cp arch/arm/boot/uImage /media/disk1 $ sudo make ARCH=arm CROSS_COMPILE=arm-linux-gnueabi- INSTALL_MOD_PATH=/media/disk2/ modules_install
2. Rolling a Debian Squeeze filesystem
On your i686 or any Pentium-based computer with Ubuntu on it, do the following:
$ mkdir squeeze_rootfs
$ cd squeeze_rootfs
$ sudo debootstrap --include=openssh-client,openssh-server --exclude=udev --arch=armel --foreign squeeze \
./ http://ftp.us.debian.org/debian
$ sudo tar cvjf ../squeeze_rootfs.tar.bz2 *
Untar the rootfs onto the MicroSD's second partition and boot your SBC to finish the second stage of your debootstrap installation.
$ sudo tar xvjf squeeze_rootfs.tar.bz2 -C /media/disk2/ ; sync
Issue the following commands in the U-Boot interface.
U-Boot> setenv bootargs 'console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait init=/bin/sh' U-Boot> setenv bootcmd 'mmcinfo; ext2load mmc 0:1 0x20100000 uImage; bootm 0x20100000' U-Boot> boot
After you will get terminal access, the following sequence of commands will complete the installation of the root filesystem.
$ mount /proc /proc -t proc $ export PATH=/usr/loca/sbin:/usr/local/bin/:/usr/bin:/usr/sbin:/sbin:/bin $ /debootstrap/debootstrap –second-stage $ echo "T2:23:respawn:/sbin/getty -L ttyS0 115200 vt100" >> /etc/inittab $ echo ttyS0 > /etc/securetty $ passwd root
Also, in order to make the boot process faster, one should get rid of all services and utilities that are not needed at boot time (after all, this is a light weight embedded system). Just start them later, on a need to use basis (networking, cron jobs, etc.).
$ mkdir ~root/backup_services $ mv /etc/sysctl* ~root/backup_services $ cd /etc/init.d $ mv cron hwclock* ifupdown* networking rsyslog stop-bootlogd* bootmisc* mountall-* ~root/backup_services $ mv /sbin/startpar ~root/backup_services $ mv /sbin/sysctl ~root/backup_services
Now it is a good time to create entries for serial ports, MMC and onboard flash memory partitions in /dev/.
$ mknod -m 660 /dev/tty1 c 4 1 $ mknod -m 660 /dev/ttyS0 c 4 64 $ mknod -m 660 /dev/mmcblk0 b 179 0 $ mknod -m 660 /dev/mmcblk0p1 b 179 1 $ mknod -m 660 /dev/mtdblock0 b 31 0 $ mknod -m 660 /dev/mtdblock0p1 b 31 1
Finally, in order to get a clean login terminal in runlevel 2, the following lines must be commented in /etc/inittab.
1:2345:respawn:/sbin/getty 38400 tty1 2:23:respawn:/sbin/getty 38400 tty2 3:23:respawn:/sbin/getty 38400 tty3 4:23:respawn:/sbin/getty 38400 tty4
Final touches
Set-up your network interface in /etc/network/interfaces and start your network interfaces
$ /backup_services/networking restart
Update Debian repositories in /etc/apt/sources.list
$ echo “deb http://security.debian.org/ squeeze/updates main contrib non-free” >> /etc/apt/sources.list $ echo “deb http://ftp.debian.org/debian/ squeeze main contrib non-free” >> /etc/apt/sources.list $ apt-get update $ apt-upgrade
Create a user account with (temporary) root privileges (sudo) and lock the root user
$ apt-get install sudo $ adduser user $ echo "user ALL=(ALL) ALL" >> /etc/sudoers $ sudo passwd -l root
Avoid filesystem corruption
If you mount the root filesystem in read-write mode on a MicroSD card, it can happen that, over time, due to repeated power cycles and unclean reboots, some sectors on the card will become unusable. In order to overcome this, mount the root filesystem as read only. Mount /var (if you need kernel logs) and /home on different partitions. In U-Boot, modify bootargs accordingly.
U-Boot> setenv bootargs 'console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait ro'
More on filesystem corruption on embedded systems here.
The command line passed to the kernel by U-Boot must contain the lpj parameter. Without it, the BogoMIPS calculation would add to the overall boot time.
U-Boot> setenv bootcmd 'mmcinfo; ext2load mmc 0:1 0x20100000 uImage; bootm 0x20100000' U-Boot> setenv bootargs 'console=ttyS0,115200 root=/dev/mmcblk0p2 rootwait lpj=98304 noswap' U-Boot> saveenv U-Boot> boot
If you followed the previous instructions closely, you will get a login terminal in 6 seconds including kernel decompression. Enjoy your fast booting Linux SBC!
3. Xenomai 2.5.6 on Electrum100
Disclaimer: This section was more or less copy-pasted from http://www.stoian.us/misc. I put it here for the sake of consistency.
On a i686 machine, patch the kernel with Adeos I-pipe and compile it. If you are unsure of a workable configuration file, you can use the one provided.
$ tar -xvjf xenomai-2.5.6.tar.bz2 $ xenomai-2.5.6/scripts/prepare-kernel.sh –-linux=linux-2.6.35.9 --arch=arm $ cd linux-2.6.35.9 $ make CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm menuconfig $ make CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm uImage $ make CROSS_COMPILE=arm-linux-gnueabi- ARCH=arm
Xenomai-2.5.6 must be compiled from sources on the same machine.
$ mkdir /home/user/xeno_installation $ cd xenomai-2.5.6 $ ./configure -–host=arm-linux-gnueabi --enable-arm-mach=at91sam9 --enable-arm-eabi $ sudo make DESTDIR=/home/user/xeno_installation ARCH=arm install
Warning: I could not compile Xenomai using GCC-4.4 and GCC-4.5 on Ubuntu 10.10. If you want to use newer compilers to get the job done, you are on your own. If you can make it work, please shoot me an email.
If everything goes with no issues, all the libraries, benchmarks and character devices will be installed in /home/user/xeno_installation.
$ ls /home/user/xeno_installation/ dev usr $ ls /home/user/xeno_installation/usr/xenomai/ bin include lib sbin share
Transfer usr/xenomai and dev/ to the MicroSD containing the root filesystem created in the previous steps, and you are almost done. In order to have a good approximation of your system's latency, you should launch intensive computational tasks. The following script ran alongside /usr/xenomai/bin/latency for more than 3 hours on the SBC.
#! /bin/bash # running the script: chmod +x script; ./script > /dev/null cd /home/user/xenomai-2.5.6 ./configure --enable-arm-mach=at91sam9 --enable-arm-eabi while[1]; do make; make clean done
Worst case latency was 60.2 µs in userspace. If one gets negative latencies in kernel space (when running the benchmarks), /proc/xenomai/latency should be tweaked. The best value to start with is 0.
$ sudo chmod 766 /proc/xenomai/latency $ sudo echo 0 > /proc/xenomai/latency
4. Simple Xenomai Code
A simple application featuring a real-time thread is shown. Once the thread is started, a GPIO on the Electrum100 (PIOB, bit 0) is toggled inside a “while” loop. This application's Makefile has an scp line included, so that on every successful compilation, a passwordless SSH connection is initiated and the binaries are transferred directly to the SBC.
[...]
LDFLAGS+=-Xlinker -rpath -Xlinker $(shell $(XENOCONFIG) --libdir)
LDFLAGS+= -lrtdk
all:: $(APPLICATIONS)
scp $(APPLICATIONS) user@SBC_ip:
clean::
$(RM) $(APPLICATIONS) *.o
[...]
To make this setup work, do the following: on the build machine, do in terminal
$ cp -rf xeno_installation/usr/xenomai /usr
the build and target machines should be on the same network the network interface on the target should be active
$ sudo /backup_services/networking restart
the SSH server must be started on the target machine
$ sudo /backup_services/ssh start
Unless you believe the G-Men are interested in your DIY home defense Xenomai-powered AegisTM system, generate an SSH key pair of short bit length on the build machine.
$ ssh-keygen -t dsa -b 1024 $ ssh-copy-id user@sbc_ip
If your source file is gpio.c, simply type make in terminal and you can find gpio ready for execution on your SBC.
[...]
#include <stdio,mman, ...>
[...]
#include <native/task.h>
#include <native/timer.h>
#include "definitions.h" // base addresses, offsets
RT_TASK spin_task;
int fd,i,j;
unsigned char *piob_base, *aic_base, *pmc_base;
void open_controller(){
if((fd = open("/dev/mem", O_RDWR | O_SYNC)) == -1)
{
printf("Couldn't open /dev/mem. error %s\n", strerror(errno));
exit(1);
}
printf("/dev/mem opened.\n");
aic_base=mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, AT91C_BASE_AIC);
piob_base = aic_base + AT91C_BASE_PIOB - AT91C_BASE_AIC;
pmc_base = aic_base + AT91C_BASE_PMC - AT91C_BASE_AIC;
if( (aic_base == MAP_FAILED)) {
printf("Couldn't get Map-Address.(errno: %d).\n", errno);
exit(1);
}
// peripheral settings
*((unsigned int *) (piob_base + PIO_IDR)) = 1<<0;
*((unsigned int *) (piob_base + PIO_PER)) = 1<<0;
*((unsigned int *) (piob_base + PIO_CODR)) = 1<<0;
*((unsigned int *) (piob_base + PIO_PPUDR)) = 1<<0;
*((unsigned int *) (piob_base + PIO_OER)) = 1<<0;
*((unsigned int *) (piob_base + PIO_MDDR)) = 1<<0;
}
void spin(void){
rt_task_set_periodic(&spin_task,TM_NOW, 100000);
while(i<100000){
*((unsigned int *) (piob_base + PIO_SODR)) = 1<<0;
rt_task_wait_period(NULL);
*((unsigned int *) (piob_base + PIO_CODR)) = 1<<0;
//rt_timer_spin(0);
for(j=0;j<50;j++)
asm("mov r0,r0"); // no-op for small delays
i++;
}
}
void catch_signal() {}
int main(void)
{
signal(SIGTERM, catch_signal);
signal(SIGINT, catch_signal);
/* Avoids memory swapping for this program */
mlockall(MCL_CURRENT|MCL_FUTURE);
open_controller();
rt_task_create(&spin_task, "spin", 0, 99, 0);
rt_task_start(&spin_task, &spin, NULL);
getchar();
rt_task_delete(&spin_task);
close(fd);
return 0;
}
Supporting files:
- [1] Electrum100 (200) patch for vanilla 2.6.35.9 and kernel configuration file
- [2] sample code - real-time GPIO toggling (Xenomai)
Sources:
