...or how to waste lots of time.
Writing portable code is more than just sticking to strict ANSI-C. If you look at the sources of programs or libraries that compile on different platforms, you will find lots of ifdef statements that provide the portability. It is actually pretty difficult to write portable code.
Currently I am into ARM development and I try to target just one OS: mine, on one platform: mine. But I want to use different compilers. I try to write code as portable as possible, without adding compiler specific things, but that just isn’t possible, especially when you target an embedded platform.
Initially I developped some code for GCC. Then I decided that it would be nice if I could use the Keil uVision tools as well. Getting my GCC code to compile with the RealView compiler (used by the uVision tools) was not too difficult, but getting it to work, was.
The problems were caused by, as always, the interrupts. In my code a timer and a UART use interrupts. With the GCC tools both worked fine, with the Keil tools only the UART worked. Uh oh... long debug hours ahead...
I remembered that the GCC code had an interrupt wrapper in the startup code, so all I had to do was to replace the Keil startup code with the GCC startup code. Easier said than done, because it turned out that the Keil assembler did not like the GCC assembly code. This code was definitely not tool-portable (even though the origin of the code clearly was Keil.).
Right, forget about GCC’s special interrupt code, let’s do it the Keil way. I found a Keil timer example that worked on my platform and I copied the code across to my GCC code and … it did not work. I examined the assembler code generated by the compiler for both cases and noticed that the entry and exit code was not the same depending on if the code was integrated in my GCC code or in the Keil example. Yet it was the same C-code, verbatim, since I copied it across, remember?
Or was it?
After stripping down my GCC code (and many hours of hair tearing) I finally discovered that the GCC code had defined away the __irq keyword! Aaargh! So that’s why I didn’t get the right entry and exit code for my ISR!
OK, so I quickly fixed this and now everything would work, right? Wrong!
Obviously there was another problem. My code was now working a bit, but it hung in a delay function. This delay function waited for the timer to reach a certain value before continuing. After some debugging I found that the problem was not so much the delay function itself, but returning from it, it made the program crash. Hmm, that smelled like stack problems.
To make a long story short, the hanging was caused by a function call from within the timer ISR to update a 10 ms timer. After many more debugging I finally found the reason: it was the example code that I had copied! The example proudly mentioned that it handled nested interrupts and I thought that that was mighty fine. But as it turned out, the way the example handled nested interrupts it could not handle function calls from within an ISR... Once the nested interrupts disabled, my Keil code finally worked as my GCC code.
In the end I did not have to make a lot of changes to my original code to port it to a different compiler. It was enough to set the include paths right, define away a GCC __extension__ keyword, add a wint_t typedef, re-allow the __irq keyword and disable nested interrupts, but these last two took me many hours to figure out. At least I now finally understand why the GCC startup code has this special interrupt handling code...
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment