Sunday, December 21, 2008

STR91x bootloader trouble, PFQ/BC

Most start-up code (91x_init.s for example) writes 0x191 to the SCU->SCR0 register, which among other things enables the PFQ/BC (Pre-Fetch Queue/Branch Cache). This is normally a good thing. Of the 16 entries in the Branch Cache, 15 are used for generic branches while the 16th one is used only for the special IRQ branch at address 0x18. It appears that this branch cache value is initialized when the PFQ/BC is turned on AND read-only after that. That means if the jump at 0x18 changes, the BC entry will be wrong.

Why would the jump at 0x18 ever change? It changes if you remap the banks, i.e. use BANK1 as the bootloader boot bank, and then switch to BANK0 as the main application. Of course, you shouldn't even do this if you are not using at least rev H of the STR912FAW, but that is a different story (the chip reset circuit has a bug that makes this impossible).

What happens if you don't do something special a programmer's worst nightmare: jumps are random when you let the program run, but if you step through the ASM code, it works fine (because the cache is turned off if you are doing single steps). And the random jumps are not a software bug that you can fix.

But there is a fix. Change the boot code to disable the PFQ/BC, then reenable it later in your code.


; --- Enable 96K of RAM & Enable DTCM & AHB wait-states, disable PFQ/BC until it is flushed. The bootloader has cached the IRQ already and that is BAD!
LDR R0, = SCU_BASE_Address
LDR R1, = 0x0196 ; not 0x0191 as in other boot code!
STR R1, [R0, #SCU_SCR0_OFST]


Later you can reenable using the 91x_lib call (inside __low_level_init() if you are using IAR EWARM):


SCU_PFQBCCmd(ENABLE); // Enabled Branch Cache feature of STR91x.

Friday, December 19, 2008

Anatomy of STR912 boot sequence in EWARM

Power-on: waits til voltages rise above Low-Voltage Detect, then sets PC to 0x00000000

First instruction jumps to Reset Handler. The jump is usually a LD PC,[PC+xxx] command that loads the PC from a table just past the end of the vector handlers.

Reset Handler is always written in ASM because it needs to do things that are unsafe in C, like initializing flash memory interfaces, RAM interfaces and needs access to the CPSR register, which you don't have from C. This usually sets the flash memory interface, sets up stack space for each of the system modes, then jumps to ?main. In EWARM, the Reset Handler is installed at 0x180, and is given the symbol __iar_program_start. If you do not write your own, EWARM will pick one for you.

?main - This does basically 3 things:
  1. Call __low_level_init(). You can write this in C, as long as you are careful not to use global variables. This is useful for setting the clock speed and other things that don't need to be done in ASM. EWARM has a default dummy __low_level_init().
  2. Call __iar_init_memory. Not something the user should deal with. This is where the C global variables are initialized, and global C++ objects are constructed.
  3. Call main. This is the traditional C entry point.
main - Your plain old C function where the real fun begins.

I've seen several examples (the code from ST) that does the low level init in main() and not earlier. This is not a big deal, but it means that the clock will be running much slower on power-up, so if you are doing a lot of C++ initialization, it will boot slower (perhaps 4x slower if you are using a 25 MHz crystal and can run the PLL at 96 MHz).

EWARM "unable to halt core" on STR912

There are a few reasons why you might be unable to halt the core using the Jlink debugger and EWARM, on a STR912 chip.

1. You wrote to the wrong registers and misconfigured the memory configuration. In this case, you might need to erase the chip and use the debugger to step through the code until you find the bad instruction. You can erase the chip using either ST's CAPS program, or your can do it with the command line utility "J-Link STR9 Commander" that you can download from Segger as part of the free jlink tool suite.

2. Your USB J-link debugger is plugged into a slow USB port, PCMCIA-to-USB hub or USB-to-USB hub. I had lots of trouble with my PCMCIA-to-USB hub. Using the built-in USB port works great.

3. You accidentally turned on the security bit using CAPS. Once it is turned on, your program will run, but you will not be able to connect to the device (except to erase the chip). You will need to do a full chip erase to regain control. See #1 for hints on erasing the chip.

Thursday, December 18, 2008

Quick and dirty Message Box from command line

If you are building a tool chain that is primarily command line scripting, but you need to alert the user to a problem, the simplest built-in Windows method is to use VBScript and the msgbox command.

Create a one line file whose name ends in *.VBS (failmsg.vbs for example), and contains something like this:


msgbox "Build failed. Please check Log files"

then in your scripts (ie a BAT file), call the vbs file using

cscript /nologo failmsg.vbs


You could also pass parameters to the vbs script, but since I know nothing about vbs except what you just saw, I don't know how.

Perl, BAT files and quoteless quoting.

As much as I hate DOS batch files (*.BAT), sometimes you are forced to use them for integration with other build tools.

One frustration I had with a BAT file was the normal Unix rules for quoting, using nested single and double quotes, don't work.

For example, in unix (or Cygwin), you can do


perl -e 'print "Hello\n";'


But DOS doesn't know about single quotes. If you try


perl -e "print 'Hello\n';"


You will get


Hello\n%


... the newline is ignored since you used single quotes. (My version of Cygwin displays the % meaning that the output does not have an EOL character).

The work around is to use the rarely used "qq" perl functions to do quote-less quoting. Thus the following example


perl -e "print qq{Hello\n};"


prints out the correct message with the newline.


Hello

Built-in "fix me" alarms in source code

Several times during development, it makes sense not to fully implement all of the features of a particular function and just leave a stub that gets your code to compile. Somewhere in the comments, I usually put "fixme" so that it is easy to find again later when I finish everything else.

However, as time passes, sometimes you don't remember to search for all the "fixme"s and these can creep into production builds.

A simple hack is to put time bombs in your code that refuse to compile after a certain time has elapsed, ie like 2 weeks.

In your prebuild hook, write something that writes a time stamp to a header file, like this:


#! c:/perl/bin/perl

use POSIX;
open F,">buildtime.h" or die;
print F POSIX::strftime(qq{#define BUILDTIME %Y%m%d\n},localtime);


Then in your code, put in hooks like this:


#include "buildtime.h"
#if BUILDTIME > 20090125
#error Fix this bug
#endif


where the time stamp is simply the concatenation of year, month and day (YYYYMMDD), in that order to get the greater-than comparison to work. In my example, the code will refuse to build after Jan 25, 2009.

EWARM "bug" when dealing with ARM-based VIC

The VIC on the STR912 is a tricky beast if you use the debugger, because viewing the status of the registers will mess up the VIC logic. The way the VIC works is that it has 2 modes, active and inactive modes. To go from active to inactive, you read the VIC ADR register, and to return you write to the VIC ADR register. However, the debugger also reads the VIC when you halt it. Thus, if you halt the processor at all and the VIC register display is visible, basically the VIC will stop working.

The only time it is safe to view the VIC is *after* it is in inactive mode, ie after the read from the VIC ADR register.

Otherwise, the symptom is that the VIC never triggers the IRQ any more, even if the register bits all show the interrupts are asserted.