This articles was published on 2012-06-02

mruby on mips

Due to the reason that I paused for a moment my activities on improving the testing platform for mruby (the reason is already clarified *happy*) I took a look at some hardware which was available at my place. I found a small embedded device with the following specification:

Atheros SoC AR9331, this means a 400Mhz MIPS CPU, with USB, Ethernet and 802.11n interface. Additionally there is 4MB Flash available on the device:

20120602-143117.jpg

Porting mruby to this little device consisted of three steps

Setting up cross-compiling toolchain

I’m a huge fan of OpenWRT. Many people know that OpenWRT is an awesome embedded Linux Distribution. But what many people don’t know is, that OpenWRT has an awesome implementation of a Cross Compiling toolchain. They didn’t really invented something by themselves but they build up a nice environment in which you can just cross-compile stuff even if you don’t want to use it on their platform. Writing an OpenWRT specific Makefile is as easy as this:

include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk

PKG_NAME:=mruby
PKG_REV:=b2d0f22dee87651bca4e16e287cab61dc303db19
PKG_VERSION:=0.1
PKG_RELEASE:=1

PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=git://github.com/mruby/mruby.git
PKG_SOURCE_VERSION:=$(PKG_REV)

include $(INCLUDE_DIR)/package.mk

define Package/mruby
  SECTION:=language
  CATEGORY:=Base system
  TITLE:=Embeddable Ruby Language
  URL:=http://github.com/mruby/mruby
endef

define Package/mruby/description
  mruby is an embeddable version of the Ruby Programming language.
endef

define Package/mruby/install
  $(INSTALL_DIR) $(1)/usr/sbin
  $(INSTALL_BIN) $(PKG_BUILD_DIR)/bin/mruby $(1)/usr/sbin
endef

$(eval $(call BuildPackage,mruby))

Pre-Compiling mrbc

There is one small trap in the mruby compile process. Parts of mruby are written in Ruby itself. During the compilation process a tool called mrbc will be compiled which then compiles all Ruby files under mrblib. The result is some C-Code which then will be again compiled to the final object. The problem for cross-compiling is now that we need to compile mrbc for the host machine but the result of mrbc has to be compiled for the target machine. Our cmake branch of mruby has already some features to support that but the default Makefiles aren’t able to do that. Due to the reason that I don’t want to mess around with cmake inside of the OpenWRT toolchain I decided to just compile the mrbc static on my machine up-front. Then I deleted everything except the bin/mrbc file. Afterwards I could bootstrap the complete compile. The Makefiles are smart enough to recognize that they don’t need to compile mrbc again. So they just skip that process and they use my host compatible version of mrbc to prepare all Ruby files for the target machine.

Redirecting LL to target gcc

matz is using LL inside of his Makefiles to prepare the final libmruby. I’m not sure if this is very standard conform. Nevertheless my default toolchain doesn’t recognize this abbreviation. So I had to change the Makefile there too.

The Result

As you will see, compiling for MIPS in a proper toolchain and with a little bit of stripping you can bring down the mruby binary to a size of 429K. I bet I can make it even smaller in the future:

mruby binary for MIPS (AR9331 SoC) (ZIP 162K)

root@OpenWrt:~# ls -lah /usr/bin/mruby
-rwxr-xr-x    1 root     root      429.4K Jan  1 00:05 /usr/bin/mruby
root@OpenWrt:~# uname -a
Linux OpenWrt 3.2.5 #3 Wed Feb 15 13:46:20 SGT 2012 mips GNU/Linux
root@OpenWrt:~# cat /proc/cpuinfo
system type		: Atheros AR9330 rev 1
machine			: TP-LINK TL-WR703N v1
processor		: 0
cpu model		: MIPS 24Kc V7.4
BogoMIPS		: 265.42
wait instruction	: yes
microsecond timers	: yes
tlb_entries		: 16
extra interrupt vector	: yes
hardware watchpoint	: yes, count: 4, address/irw mask: [0x0000, 0x0060, 0x03c0, 0x0280]
ASEs implemented	: mips16
shadow register sets	: 1
kscratch registers	: 0
core			: 0
VCED exceptions		: not available
VCEI exceptions		: not available

root@OpenWrt:~# mruby -v
mruby - Embeddable Ruby  Copyright (c) 2010-2012 mruby developers
puts "Hello World"
NODE_SCOPE:
  local variables:
  NODE_BEGIN:
    NODE_CALL:
      NODE_SELF
      method='puts' (276)
      args:
        NODE_STR "Hello World" len 11
irep 110 nregs=4 nlocals=2 pools=1 syms=1
000 OP_LOADSELF	R2
001 OP_STRING	R3	'Hello World'
002 OP_LOADNIL	R4
003 OP_SEND	R2	'puts'	1
004 OP_STOP

Hello World
root@OpenWrt:~#