I have a unique application that is about to overflow 64k. I can not use bank switching because of the overhead. PLEASE do not reply anything about how small the overhead is - I do not care, there IS overhead. Now this application process ONLY ONE OF two formats "classic" and "current". My idea is to locate all common code in the lower 32 k and, immediately after reset detect which format it is and set the COBANK bits so that either page 1 or page 2 would be code addresses 8000 to ffff to process the actual format. I know of no simple way to do this, suggestions appreciated. Erik Cross posted on the SILabs forum.
For the common code to be ignorant of which format bank is in use, there must be a defined API that is the same for both "format" banks. The common code can call this set of routines (and no other) to be sure it works with either bank. So, there is a set of addresses for the functions, say f1() through f10(). The linker's job is to put the addresses of these functions into the points of call in the common bank. Usually, the linker is allowed to locate the functions, so that it knows their addresses. In this case, you have two different sets of functions, which will likely have different code sizes, and thus which will produce different addresses if the linker is left to its own devices. One solution to this problem is to specify the address for each function in the API. This will require some tweaking to make sure enough room is left for the larger of each function so that the addresses align in both banks. This method will be fairly painful if done manually. It's possible to write a tool to choose addresses based on the larger of the two possible sizes, and output the linker control string, but I don't think you can get the linker to do so automatically. Extending the scheme to more than two format methods gets increasingly more painful. This method also wastes a fair amount of code space, as you have to leave gaps to align the functions at their absolute addresses. The usual solution to this quandary is a vector table. One well-known location, say the start of the bank region, contains a table with the addresses of the functions in the API. Common code calls to a banked function look up the function address and jump to it. You'd probably have wrapper routines in the common bank thus:
U16 FormatF2 (U8 a) { return (*FormatVector[2])(a); }
let me elaborate: I am not staing that this is the solution I absolutely want, just stating it as a description. I only need ONE call to the format dependent code. The format dependent code need to make several calls to the common code. One thought was to make 'access to format' fixed at 8000 and compile and link 2 programs ("classic" and "modern") and write one as is to the flash and half the other to page 2. this presents 3 problems a) without a lot of hoola-baloo to make the 'format group' (many modules) link above 8000 and b)a practical means of combining the result to a production friendly download and c) a means of guaranteeing that the 0-7fff link identical in both cases. Erik
One somewhat remote possibility that I've outlined in other threads here before: linking more than once. I.e. *) run the linker for the common code, once, producing a single big relocatable output file *) force this monster into the bottom of code memory both variants, either directly or with the "symbolsonly" directive Variants on this theme would include: 1) link one variant with the common code (with segment control to keep only the common code below 0x8000), then extract symbols from the map file to form lots of _at_ declarations, and use those to satisfy references for the other variant code. 2) instead of linking the monster, compile all its sources into a single object file directly --- modularity be damned.
reading a response in my crosspost, I just realized that since only one call to the 'banked' part the overhead using regular banking will be nominal since calls from banked to unbanked have no overhead, I missed that bit in my analysis. This will only be trye if I can make sure that all that refer to "classic" is on bank 2 and all that refer to "modern is in bank 1 Any suggestions to a simple means of that (approx 40 modules / 200 functions) Erik
This enforcement shouldn't be too hard to do by inspection. Assuming the fairly normal practice of only calling functions that are properly prototyped in a .h file, then you don't include the .h's for cross-bank calls, and then can't call the function. One step further of enforcement would be to segregate the .h files into different directories, and omit the "include path" variable for one of them when compiling the restricted modules. That way, you can't even accidentally include the wrong .h. The compiler won't find it. You can check the map file afterward to make sure that there aren't any undesired interbank calls, despite good intentions and prohibitions.
INTERBANK CALL TABLE OF MODULE: xxxx ADDRESS FUNCTION NAME ------------------------- C:000150H _TimerAct C:000155H _FlashBlockWrite C:00015AH _FlashBlockRead ...
This enforcement shouldn't be too hard to do by inspection. Assuming the fairly normal practice of only calling functions that are properly prototyped in a .h file, then you don't include the .h's for cross-bank calls, and then can't call the function. No prototype = error - how does that locate functions?? something I do not understand Erik
The compile error doesn't locate a function. It makes sure that you don't mistakenly create a call from bank1 <-> bank2, which won't work without the full bank switching implementation. make sure that all that refer to "classic" is on bank 2 and all that refer to "modern is in bank 1" If there are two headers, FormatClassic.h and FormatModern.h, then you make sure that all .c files that belong in bank 1 (modern) do not #include FormatClassic.h. (More generally, you could have two directories full of .h files.) You can enforce the restriction with the include path (and maybe a grep in the build process). Actually getting the .c's into the right bank is fairly straightforward. If you wind up using the bank switch mechanism, there's a dropdown in uVision (or the linker directives). If you build two projects, then you just need the linker directive to put all the .c's in a group into high memory. Again, I'd probably keep the .c's in separate directories (common, classic, modern) to make it easy to generate a list of module names for each bank. If you pick your module names carefully, you can use the wildcard feature of the linker directives to avoid having to manually maintain the list. You might write something like
(separately built) SEGMENTS (?PR?Fmt*(0x8000)) or (banked) SEGMENTS (?PR?FmtCls*(B2:), ?PR?FmtMod(B1:))
"The usual solution to this quandary is a vector table. One well-known location, say the start of the bank region, contains a table with the addresses of the functions in the API ..." Or a table of JMP instructions to minimize the overhead...
/* A table of vector (ASM JMP) instructions instead of pointers resides at * 0x8000 in both pages 1 and 2 with each vector LJMP'ing to its associated * function. The target function addresses can be different for either page, * but the vector instruction is at its known location. The overhead is an * LJMP. */ #define F0 ((FP)0x8000) #define F1 ((FP)0x8003) #define F2 ((FP)0x8006) #define F3 ((FP)0x8009) typedef void (*FP)(void); void main(void) { /* Determine format and set COBANK accordingly, then ... */ for (;;) { F0(); /* Generates an LCALL 0x8000, which LJMPs to f0. */ F1(); F2(); F3(); } }
Sorry, this is what I compiled, but not what I posted:
typedef void (code *FP)(void);
thnks all, I will keep this till it gt there I have only one format at this time (the "PC generator") for the new format is still in development, I just needed to have a row to put the ducks in. I especially like the linker wildcarding, that will make it manageable. thanks again to all Erik