Wednesday, December 30, 2015

Baking an Object Pascal and Raspberry Pie for the Holidays.

If you've not been living under a rock over the last couple of years, I'm certain you've noticed the recent release of the new Raspberry Pi 2 Model B. Packed in this little not-much-bigger-than-a-playing-card form factor is a quad-core ARM Cortex A7 along with 1GB of RAM. It also sports HDMI video/audio, Ethernet, 4-USB ports and a programmable parallel port for peripheral interfacing. Booting several versions of Linux is a snap... but what about using your favorite language and mine, Object Pascal to target this device?
Over the holidays and taking advantage of a bit of a lull in the product cycle, I set about to actually getting a simple Object Pascal (aka. Delphi) "Hello World" app running. I've done this too many times to count, so this should be a fairly simple, right? Well, when you can ask the compiler developer to go ahead and create a DCC compiler that can target 32bit ARM Linux using LLVM, things are mostly done.

Since there is already a couple of ARM targets with Android and iOS, it took only a couple of days to pull together this compiler. I'd "commissioned" this effort soon after the RPi2 was released and I had my own device. I've also booted Windows 10 on this same board, but that's another thing entirely.

This latest version of dcc, dcclinuxarm.exe sat there for several months, taunting me each time I saw it whiz by during the build process... Finally, I resolved to get a working "Hello World" up and running before the end of the year. I selected the just-released Ubuntu 15.10 MATE since we're already in the process of targeting Linux (Intel 64bit) using Ubuntu as the distribution for development and testing.

Being that this is the holiday season here (and throughout large portions of the world), it's easy to find some really good sales. It was recommended that a Class 10 micro SD card be used for the main boot drive. The local Best Buy had some 32GB Class 10 cards with 80MB/s read and write on sale for about $17.99. I bought two.

Following all the instructions for imaging the OS onto the SD card, booting the OS, then resizing the filesystem, I had a working, usable Linux system.
As an aside, anyone who has a son or daughter, niece, nephew, grand-son/daughter, or even the annoying little kid down the block, these little boards are a really inexpensive way to introduce them to computers and programming. The default install of Ubuntu comes with Scratch, which is a "Lego-style" programming environment. Of course you can also install the traditional GNU and now the Clang/LLVM toolchains.
The first order of business is to get the core of the RTL, System/SysInit, building. Since we've already have been targeting a lot of various platforms, most of what was needed was already there. The only major issues I've faced so far is to work around a lot of the old Kylix LINUX blocks from 15 years ago. We have a much more accurate and richer conditional defines that represent the CPU, the bitness and the platform.

Because of the great pains we've take to abstract as much as possible throughout the RTL, once System/SysInit are building and functional, the incremental costs of bringing most of the rest of the RTL online is very quick. If you've ever had a look throughout the sources, there are remarkably few IFDEFs once you move out past System.SysUtils.pas.

I started by manually building System.pas like this:

C:\builds\tp\bin\dcclinuxarm.exe -$D1 -$O- -$R+ -$W+ -CG -H -M -Q -V- -W^ -Z --no-config -JPHN -D;DUNIT_HELPER_TESTS -W-UNSUPPORTED_CONSTRUCT -W-UNIT_DEPRECATED -W-SYMBOL_DEPRECATED -W-UNIT_PLATFORM -W-SYMBOL_PLATFORM -W-WIDECHAR_REDUCED -RC:\builds\tp\lib\linux32arm\debug -UC:\builds\tp\lib\linux32arm\debug -OC:\builds\tp\lib\linux32arm\debug -NUC:\builds\tp\lib\linux32arm\debug -NHC:\builds\tp\include\linux\rtl -NOC:\builds\tp\lib\linux32arm\debug -M -y System.pas

This is taken directly from the internal developer build I have on my machine. This produced  System.dcu/System.o and SysInit.dcu/System.o files. Believe it or not, once those two files are built, that's all you need to create a simple console "Hello World" app. Here's mine:

program Hello;

begin
  Writeln('Hello World');
end.

As you can see, I struggled with exactly how to write this code...

There are a lot of other little gory details to get this code to actually link and produce an executable that I could copy over to the RPi2. Before that point, I was able to copy the hello.o, System.o, and SysInit.o files to the RPi2 and then use LD to link them together. Before I figured out that hello.o must always be listed last because of how thread-local-storage works, the resulting 'hello' executable would fail at startup with an infinite loop of 'Runtime Error 226' messages... this told me two very important things, first of all console output works! Secondly, this message is generated from the code that initializes the thread-local-storage blocks. The RTL was trying to initialize itself.

Once I rearranged the link-order of the object files passed into LD and produced a new 'hello' executable, it was now the moment of truth:


 Now to prove that this is the real-deal, here's a zip file with the relevant .o files and the resulting executable. You can link these yourself on a RPi2 Linux system with this command-line:

ld -o hello -e _ZN5Hello14initializationEv --gc-sections --dynamic-list hello.exp -z relro --build-id --eh-frame-hdr -m armelf_linux_eabi --dynamic-linker /lib/ld-linux-armhf.so.3 -s SysInit.o System.o hello.o -L /lib/arm-linux-gnueabihf  -lc -ldl -lpthread -lm -lgcc_s

Now that I know the generated code runs, the next step was to get it to actually link from Windows using LD built as a Windows executable which is called from dcclinuxarm.exe. This meant copying many of the relevant shared objects from the RPi2 distribution over to the Windows system in a set of folders that mimic the layout on the platform. Initially, this was failing due to various IFDEF issues. Once I resolved them, the simple 'Hello World' app would now link on Windows, and when copied to the RPi2, it would run.

As you can see from the little animated GIF above, there are also files called exception and exception.o. This is me testing exceptions... which aren't working right now because LLVM isn't generating the proper unwind tables into the .o files. I'm currently communicating with other members of the team with more knowledge about the finer intricacies of LLVM.

Now for the $100,000 question... "is this going to be in a future release?". Well, your guess is as good as mine. If I had any say whatsoever, which given the current state of the company I don't, it would. Considering the performance of the RPi2 and other similar small-form-factor devices such as the ODroid, these devices are easily suited for use as small server devices or simple workstations.

One could deploy these little devices throughout a factory floor and hook up a bunch of sensors to monitor all aspects of the manufacturing process. Even video cameras can be hooked up and the video streamed over the network.

However, right now I'll need to file this under "Ain't gonna happen"... at least not any time soon.

No comments:

Post a Comment

Please keep your comments related to the post on which you are commenting. No spam, personal attacks, or general nastiness. I will be watching and will delete comments I find irrelevant, offensive and unnecessary.