Patching functions and libraries

Introduction

After this forum post culminating in a very nicely pitched wish-list item from Robbie King, I decided to take on the challenge of working out how to deliver "Robbies Dream".

The dream is to be able to make low cost LPC11U24 hardware that doesn't even have a crystal. It's possible in theory, but the mbed libraries are hard wired to use the on-board crystal. While it is possible to switch to the internal oscillator in software from user code, the LPC11U24 wont get as far as the user code without the crystal.

As this was clearly going to be a software exercise, I was hoping that Emilio or Samuel would step in and make it all work beautifully. Unfortunately they are both really busy on some super-cool stuff, and so I quote (literally) :

Simon Ford wrote:

"i think it should be chris' challenge to make it work :)"

The nub of the problem

As you're all aware, the mbed libraries are currently (as of 20/9/2012) closed source. If they were open, it would be possible to go into the clock set-up code that executes before main() in reached and simply set up the clocks as you please. Its fiddly work, lots of documentation to read, and lots of mistakes that are all to easy to make. I have made a few of them while doing this, and have made use of my own How to unbrick and mbed page! :-)

So, without the ability to modify the source, we need some other way to override the settings. Fortunately Emilio came to the the rescue, and introduced me to $Sub$$ and $Super$$.

At this point, it would be beneficial to have a look at Dr Rob Touslons course notes, especially the "Modular Programming" section. Understanding the process of how a program gets compiled and linked into the final binary will make everything that follows a little clearer.

$Sub$$ and $Super$$ to the rescue

These two features of the ARM compiler allow the user to patch symbols defined in one compilation unit from another. This is most often used if you are trying to patch the functionality of a functions in compiled code that you don't have the source to. They were a clear choice of how to deliver Robbies Dream, as all we really need to do is patch over the clock set up code.

The offical ARM documentation for these features are can be found here.

I often find it difficult to take abstract examples and translate them to my own use, so here is a worked example.

I have a simple function that takes an integer. It creates a DigitalOut object on LED1, and sets it on/off according to the integer passed in.

function.cpp

#include "function.h"
#include "mbed.h"

void function (int i) {
  DigitalOut led(LED1);
  led=i;
}

It is used in a simple program that simply turns the LED on and off again - Blinky!

main.cpp

#include "mbed.h"
#include "function.h"

int main() {

    while(1) {
        function(1);
        wait(0.5);
        function(0);
        wait(0.5);
    }
}

Using $Sub$$

Now imagine that function() is buried away in a library that I only have object for, but I want to change its functionality, this is exactly what $sub$$ is designed for.

I can now write a new function called void $Sub$$function(int) and give it alternate behaviour. When the final binary is linked, any call to function(int), becomes a call to my new function "$Sub$$function(int)"

main.cpp

#include "mbed.h"
#include "function.h"

void $Sub$$function (int i) {
  DigitalOut led(LED3);
  led=i;
}

int main() {

    while(1) {
        function(1);
        wait(0.5);
        function(0);
        wait(0.5);
    }
}

This is great, as I have now been able to replace one function with another. However, the story gets even better when we consider what $Super$$ can do for us

Using $Super$$

In the previous example, I have simply replaced one function with another. However, I can still get access to the original definition of the function(int) and use it. An example might be is a function requried some additional setup before it was called, or cleanup after it was called.

So in my $Sub$$ only version I simply replaced blinky on LED1 with blinky on LED3. What if I wanted to do both, with something in between?

Import program

#include "mbed.h"
#include "function.h"

extern void $Super$$function(int i);

void $Sub$$function (int i) {
  DigitalOut led(LED3);
  led=i;
  wait(0.1);
  $Super$$function(i);
}

int main() {

    while(1) {
        function(1);
        wait(0.5);
        function(0);
        wait(0.5);
    }
}

In this example "extern void $Super$$function(int i);" is a declaration that assures the compiler that a symbol called "$Super$$function" will have been compiled elsewhere and that it will all be resolved by the linker. In fact this will come to exist when $Sub$$ is used, and this renames the original "function(int)" to "$Super$$function(int)"

This example program also now makes a call to the original version of function(int) after it has executed its own commands.

The result of all this is that the contents of main() have remained unchanged, but we have gone from blinking LED1 to blinking LED3, to blinking both with a time delay, all by substituting function symbols.

Back to the Crystal issue...

So now we have a way to patch code that we don't have access to, let's have a look at how to deliver Robbies Dream.

Nosing through the (closed) source, I found a file call "system_LPC11Uxx.c", which contains a function called "void SystemInit(void)"

This does look exactly like the function that I need to patch - it is full of references to clock source settings, PLL configurations and other related things.

At this point, I have a look at the user manual for the LPC11U24

Chapter 3 is entited the System control block, and contains all the information I'll be needing. Figure 7. is the most useful diagram, as it show a nice schematic of how the clocks are generated and routed.

After some head scratching, note making and experimentation, I arrive at the conclusion of what needs to be done :

  • Patch SystemInit() - Remove all conditional compilation and force the settings I want
  • Don't power up the System Oscillator (a good experiment to ensure we're not still using it!)
  • Select the SYSTEMPLL input as "0x0" - The Internal RC oscillator
  • Ensure the MAINCLK is selecte as SYSTEMPLL output
  • Select the USBPLLCLK input as "0x0" - The internal RC oscillator - This is only good for low speed USB

And here it is, my implementation the LPC11U24 running blinking with no external crystal

Import program

00001 #include "mbed.h"
00002 
00003 DigitalOut myled(LED1);
00004 
00005 extern int stdio_retargeting_module;
00006 
00007 /**
00008  * Initialize the system
00009  *
00010  * @param  none
00011  * @return none
00012  *
00013  * @brief  Setup the microcontroller system.
00014  *         Initialize the System.
00015 */
00016 extern "C" void $Sub$$SystemInit (void)
00017 {
00018 
00019 // select the PLL input
00020     LPC_SYSCON->SYSPLLCLKSEL  = 0x0;                // Select PLL Input source 0=IRC, 1=OSC
00021     LPC_SYSCON->SYSPLLCLKUEN  = 0x01;               /* Update Clock Source      */
00022     LPC_SYSCON->SYSPLLCLKUEN  = 0x00;               /* Toggle Update Register   */
00023     LPC_SYSCON->SYSPLLCLKUEN  = 0x01;
00024     while (!(LPC_SYSCON->SYSPLLCLKUEN & 0x01));     /* Wait Until Updated       */
00025 
00026 // Power up the system PLL
00027     LPC_SYSCON->SYSPLLCTRL    = 0x00000023;
00028     LPC_SYSCON->PDRUNCFG     &= ~(1 << 7);          /* Power-up SYSPLL          */
00029     while (!(LPC_SYSCON->SYSPLLSTAT & 0x01));       /* Wait Until PLL Locked    */
00030 
00031 // Select the main clock source
00032     LPC_SYSCON->MAINCLKSEL    = 0x3;                // Select main Clock source, 0=IRC, 1=PLLin, 2=WDO, 3=PLLout
00033     LPC_SYSCON->MAINCLKUEN    = 0x01;               /* Update MCLK Clock Source */
00034     LPC_SYSCON->MAINCLKUEN    = 0x00;               /* Toggle Update Register   */
00035     LPC_SYSCON->MAINCLKUEN    = 0x01;
00036     while (!(LPC_SYSCON->MAINCLKUEN & 0x01));       /* Wait Until Updated       */
00037 
00038     LPC_SYSCON->SYSAHBCLKDIV  = 0x00000001;
00039 
00040     LPC_SYSCON->PDRUNCFG     &= ~(1 << 10);         /* Power-up USB PHY         */
00041     LPC_SYSCON->PDRUNCFG     &= ~(1 <<  8);         /* Power-up USB PLL         */
00042     LPC_SYSCON->USBPLLCLKSEL  = 0x0;                // 0=IRC, 1=System clock, only good for low speed
00043     LPC_SYSCON->USBPLLCLKUEN  = 0x01;               /* Update Clock Source      */
00044     LPC_SYSCON->USBPLLCLKUEN  = 0x00;               /* Toggle Update Register   */
00045     LPC_SYSCON->USBPLLCLKUEN  = 0x01;
00046 
00047     while (!(LPC_SYSCON->USBPLLCLKUEN & 0x01));     /* Wait Until Updated       */
00048     LPC_SYSCON->USBPLLCTRL    = 0x00000023;
00049 
00050     while (!(LPC_SYSCON->USBPLLSTAT   & 0x01));     /* Wait Until PLL Locked    */
00051     LPC_SYSCON->USBCLKSEL     = 0x00;               /* Select USB PLL           */
00052 
00053     LPC_SYSCON->USBCLKSEL     = 0x00000000;      /* Select USB Clock         */
00054     LPC_SYSCON->USBCLKDIV     = 0x00000001;      /* Set USB clock divider    */
00055 
00056     /* System clock to the IOCON needs to be enabled or
00057     most of the I/O related peripherals won't work. */
00058     LPC_SYSCON->SYSAHBCLKCTRL |= (1<<16);
00059     stdio_retargeting_module = 1;
00060 
00061 }
00062 
00063 
00064 int main()
00065 {
00066     while(1) {
00067         myled = 1;
00068         wait(0.25);
00069         myled = 0;
00070         wait(0.25);
00071     }
00072 }

Note the missing Crystal, half way up onthe right by p26/p27, savagely removed with a screwdriver!

Conclusion

As s result of this, we now have two new weapons up our sleeve, $Sub$$ and $Super$$, and we've seen how with a little insight we can still patch our way around things that are hidden along with the source code.

I hope that it also delivers Robbies Dream :-)


Report

5 comments on Patching functions and libraries:

24 Sep 2012

WOW, HUGE THANKS to Chris and @mbed team for showing us how it's done (and for making my dream a reality)! The $Sub$$ and $Super$$ features will undoubtedly be very useful to other people who want to hack away at the mbed libraries... until they become open-source of course (wink, wink)!

Many Cheers from Robbie's corner!

15 Oct 2012

Great post. How about for the LPC1768? Also, I was wondering if you could speak to the pro's and con's of eliminating the external crystal? Other than cost is anything gained? and what is lost?

22 Feb 2013
20 Apr 2013

Hi, can you give me some direction to be able to output the 32Khz clock and feed it back into the RTC clock input pin on the KL25Z. I'm using an external 32Khz clock source at the moment but would like to be able to use the KL25Z 32Khz clock source. The FRDM board uses a 32Khz clock from the SDA chip so if using the KL25Z chip on its own I have no RTC clock. An example like the one from Chris above would be nice!

18 Sep 2013

Hi Chris,

I tried to use your $Sub$$SystemInit function in my own code but I get an error during compile:

"Undefined symbol stdio_retargeting_module (referred from main.cpp.LPC11U24.o)." in file "/"

Any ideas ?

Please login to post comments.