Cross-compiling > Ruby >
Richard Keene - Colt Technologies
rmkeene at comcast.net
- This is also at was cpjava.net
-
Please visit my site and support my web server. Thanks
Keywords: cross compile ARM Ruby SBC SBC1625 SBC-1625 Micro/Sys configure gcc kermit uCLinux
Ruby cross compile to ARM processor from PC Linux i386 machine
Target is a sbc-1625 from Micro/Sys with an extra 4 port serial board.
- PC-104 form factor.
- 16MB ram, 32MB rom, no insertable flash memory/disk
-
uCLinux pre-loaded in flash.
- The host linux PC is 192.168.1.53
-
The ARM PC104 board is 192.168.1.200
Serial Monitor ∞
There is a mistake in the docs for the SBC1625.
The serial port is COM1, not COM2
I use kermit to talk to it and here is the .mykermrc which gets run when you run kermit...
set line /dev/ttyS0 set speed 115200 set flow none set carrier-watch off connect
You will need to be superuser and
chmod 777 /dev/ttyS0
for the above scripts to work as a normal user.
general uCLinux ∞
The SBC1625 boots up with an IP address of 192.2.....
which is not what my subnet is. In my copy of the file system I changed the rc file to have the correct address.
Until that was correct, I do
ifconfig ixp0 192.168.1.200
to set the IP address after boot.
Test of Compiler and ARM ∞
First get a simple helloworld.c
to compile on the i386 host and run on the ARM processor. This is a good sanity check to see if you installed the SBC1625 CDROM correctly on your linux box. If this does not work then when configure runs (below) it will complain "Compiler cannot create executables". My CD is SW4122 Rev. D
The helloworld.c
is like
#include <stdio,h> int main() { printf("Hello world\n"); }
Then cross compile it...
/usr/local/bin/arm-linux/gcc -mbig-endian main.c
and you get an a.out file. If not then you probably need -I /usr/local/arm-linux/include
on the gcc line too:
/usr/local/bin/arm-linux/gcc -mbig-endian -I /usr/local/arm-linux/include main.c
I then rename a.out
to hello
and copy it to /tftpboot
Then boot the ARM processor.
Then
tftp -r hello -l hello -g 192.168.1.53
(See below if tftp is not working.)
Then run it...
./hello
It should print Hello world
. to the console.
Now we need to build Ruby.
Get Ruby from www.ruby-lang.org
I unpacked it into a directory called ruby-arm
.
The autoconf
configures for your system. It reads the Makefile.in
and other files and generates the configure file.
In ext
you need to edit the Setup
file to enable features you need.
I uncommented the option nodynamic
and socket
then run...
autoconf
Now we run configure to generate the Makefile
s.
configure -host i386-pc-linux -target arm-linux
Then edit the Makefile
to fix the -mbig-endian
problem in the CC=
statement.
I think this is because the ARM processor can be wired to run in big or little-endian modes. The SBC1625 is big-endian.
It also seems to get the gcc path wrong. I think it expects your path to be correct for the cross compiler, and expects it to be called gcc
.
Instead it is called arm-linux-gcc
.
Any way change the Makefile
to read...
CC=/usr/local/bin/arm-linux-gcc
and also
CFLAGS = -mbig-endian -g -O2 -DRUBY_EXPORT
Now...
make clean make # ... takes a while ... make ruby
Now you have an executable called ruby
that should run on the ARM.
Sub build of special packages. ∞
Some packages in ruby are not ...rb
files in lib
. Instead they are ...o
or ...so
or ...a
files that are direct C compiled code.
A good example of this is socket. When you say require 'socket' it actually links in socket.o
.
You must have miniruby compiled to build these, but the miniruby compiled is for the target ARM processor. What I did after the make ran and failed on miniruby is copied the Linux PC ruby from /usr/bin/ruby
to min-ruby. Then I changed the Makefile
so MINIRUBY=min-ruby
and re-ran make
. This caused the ext
directory to get looked at and Setup to get read and the modules to get read. EXCEPT, the ext's were compiled for i386 not ARM. Oops.
After compiling the ext's Makefile
then tries to build the final static Ruby executable. Since it is static, Ruby never need to load a xxxx.o
file.
Also the Makefile
in ext/socket is messed up.
I put in...
COMPREFIX = /usr/local/bin/arm-linux-
and then put that on the CC
and AR
lines like...
CC = ${COMPREFIX}gcc
and
AR = ${COMPREFIX}ar
Also I had to put the -mbig-endian
in two places.
CFLAGS = -mbig-endian -DINET6 -g -O2 -DENABLE_IPV6
and
LDSHARED = $(CC) -mbig-endian -shared
and then run make in the socket directory.
Then re-run make in the top level ruby directory, and I got an executable Ruby.
The file systems ∞
Now we need to create some files systems to upload to RAM, and later to ROM.
My root directory is a copy of the original root on the SBC and then modified some. I also have a readonly file system in ROM mounted at /colt-readonly
.
Lets get a copy of the actual file system on the SBC... On the sbc when the boot up shows a '+
' hit control-c
twice and wait for it to drop into RedBoot's prompt.
Then
fis list
And you see...
Name FLASH addr Mem addr Length Entry point RedBoot 0x50000000 0x50000000 0x00040000 0x00000000 zImage 0x50040000 0x01600000 0x000C0000 0x01600000 ramdisk 0x50100000 0x00800000 0x00360000 0x00800000 FIS directory 0x50FE0000 0x50FE0000 0x0001F000 0x00000000 RedBoot config 0x50FFF000 0x50FFF000 0x00001000 0x00000000
These are pre-loaded chunks of info that can be copied to ram and run.
It turns out that the ramdisk image is called /dev/rom0
when Linux is booted.
Anyway, cycle the power and reboot the board to Linux.
/dev/rom2
is the filesystem.
so...
ifconfig ixp0 192.168.1.200 tftp -l /dev/rom2 -r fs.gz -p 192.168.1.53
Then on the host as super user...
cd /tftpboot gunzip myrom2
(ignore the error message abut the file being truncated.)
Now you have a file fs that is the file system image.
You can mount it...
mkdir /mnt/fs mount -o loop fs /mnt/fs
And then...
ls /mnt/fs
And you see...
bin dev etc home lib lost+found mnt proc sbin tmp usr var
This is the root file system.
MAKE A SAFE BACKUP OF THIS FILE
You can now modify the file system and then load it back onto the SBC and see it there.
cd /mnt/fs vi junk.txt # [Edit in a nifty message and save back out with vi] cd /tftpboot umount /mnt/fs
Then back on the SBC cycle the power and at the '+
' type control-c
twice
Then at the RedBoot prompt type the following (The -- comments on the end are not typed)
ip_address -l 192.168.1.200 -h 192.168.1.53 -- This sets the IP up fis load zImage -- Copies linux from rom to ram load -r -b 0x00800000 fs -- Copies the files system with tftp from the host go -n 0x01600000 -- Runs linux.
You should see linux boot.
Then
cd / ls
And you should see the junk.txt
more junk.txt
You should see your nifty message.
Usefull sequences of commands ∞
The ...txt
commands I usually list to the screen and cut-and-past to the kermit window.
The ....sh
commands are run directly as root to mount and unmount files systems.
As the SBC is booting it emits a '+
'
If you press control-c
twice or more it will stop RedBoot and give you a RedBoot>
prompt.
Here you can do nifty things like
fis list
This lists the flash memory segments and what they are called.
Here are some scripts I use. The 'fs
' is the read-write root file system image on the host. 'rofs
' is the readonly file system with ruby in it. It stays in /dev/rom3
and gets mounted from there.
This is the sequence of commands to set the IP addresses up, move Linux from ROM to RAM, then tftp
down
the file system image, then run Linux.
Note that we also setup the IP address of the board and host. That is how load knows where to go.
uploadfs.txt ∞
ip_address -l 192.168.1.200 -h 192.168.1.53 fis load zImage load -r -b 0x00800000 fs go -n 0x01600000
This next one uploads both zLinux and the 'fs' root file system.
uploadboth.txt ∞
ip_address -l 192.168.1.200 -h 192.168.1.53 load -r -b 0x01600000 zImage load -r -b 0x00800000 fs go -n 0x01600000
This downloads the readonly files system with ruby and our app to rom3
. You do this after Linux is booted on the SBC-1625. This takes a while to run since the device is writing to flash ROM. (more than a minute)
rom3upload.txt ∞
umount /colt-readonly tftp -r rofs -l /dev/rom3 -g 192.168.1.53 mount -o loop /dev/rom3 /colt-readonly
I strongly recommend that you copy /dev/rom0,1,2,3
to your host for safe keeping. You can do this by booting the board and using tftp
to copy the files.
Like...
tftp -l /dev/rom0 -r rom0 -p 192,168.1.53
I think rom0
is RedBoot, rom1
is zLinux.
I know rom2
is the file system. rom3
is empty and we will later put a read-only file system with Ruby and our application in it.
Here are some shell scripts to automate host side stuff.
Note: If the file systems are mounted in /mnt
on the host and you make a change, you can just type sync
on the host and then load the file system. You do not have to always be typing mnt.sh
and umnt.sh
mnt.sh ∞
#!/bin/bash umount /mnt/fs umount /mnt/rofs mount -o loop /tftpboot/fs /mnt/fs mount -o loop /tftpboot/rofs /mnt/rofs echo /tftpboot/fs is now mounted on /mnt/fs echo /tftpboot/rofs is now mounted on /mnt/rofs
umnt.sh ∞
#!/bin/bash umount /mnt/fs umount /mnt/rofs
Setting up tftp on Fedora Linux host ∞
You need to have tftpd running. You must edit the /etc/xinted.d/tftp
file so it allows creation of files by clients.
Spoiler
# default: off # description: The tftp server serves files using the trivial file transfer \ # protocol. The tftp protocol is often used to boot diskless \ # workstations, download configuration files to network-aware printers, \ # and to start the installation process for some operating systems. service tftp { disable = no socket_type = dgram protocol = udp wait = yes user = root server = /usr/sbin/in.tftpd server_args = -c -s /tftpboot per_source = 11 cps = 100 2 flags = IPv4 }
The -c
there allows creation of files.
If the sbc says tftp: timeout
it means that the tftpd is not running. You now can put files in /tftpboot
and the RedBoot can copy them. Also you can use tftp
on the SBC in Linux to get and put files.
Serial Ports ∞
On Linux, serial ports are called /dev/ttyS0
and such. You set them with the stty
command, like
stty -F /dev/ttyS1 1200 raw
which sets to 1200 baud and raw data, no parity, no hardware or XON/XOFF control. You usually need to be super user and set the serial port bits like
chmod 666 /dev/ttyS1
Final burn in ∞
When your application works perfectly you are ready to commit to ROM. We need to burn /dev/rom0
with our new root file system, and above we have shown how to burn /dev/rom3
with our readonly stuff.
Ruby Notes ∞
Ruby chokes if /usr
is world writable. I had it at 777 and it should be 755. Same for /usr/bin
, /etc
, /sbin
, /usr
, /usr/sbin
The resulting cross compiled ruby fails at floating point number.
The code
sprintf("%6.2f", 101.5)
or
puts 101.5.to_s
FIXME - prints some huge weird number. Any ideas as to how to fix this out there in Ruby-land? Maybe a big-endian issue with Ruby?
Ok, stupid programmer trick #1: My application opens and sets baud rates on serial ports. I had the command to set /dev/ttyS0
to 1200 baud.
This caused all output to kermit to stop. Well duh! This is a good example of why you must have EVERYTHING working correctly before modifying /dev/rom0
which is the root file system. The ttyS0 error caused the system to unusable if in rom0. Luckily I tested it first.
Last updated 2020-11-18 at 03:26:47
2006-06-20 or earlier -- Some basic layout was done, but this is a horrid mess.
ported, with a style update and some minor corrections
The date is from the previous content management system.
I never investigated these notes. They were probably notes for getting Ruby running on my Zaurus SL-C1000