banking without bank switching with SILabs f12x

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.

Parents
  • 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);
        }
    

    This method costs a bit of execution time, but can be built automatically by the linker. Keil C51 is not particularly friendly to function pointers, but in this case the table would be constant, and the tool chain should be able to figure out the call tree with a minimum of OVERLAY surgery.

    To eliminate the runtime overhead, if you have writable program store, you could keep both this table of API entry points and a list of call locations, and link the two halves together once at program start. This is essentially dynamically loading one of the two banks at boot time. Again, there's some work involves to pull the call points out of the object files and store them as data for your program.

    The regular bank switching mechanism isn't particularly applicable not because of overhead, but because it's designed to support a large set of different functions, rather than multiple instance of the same function. In this problem, you don't have a larger code address space, but rather alternate address spaces.

Reply
  • 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);
        }
    

    This method costs a bit of execution time, but can be built automatically by the linker. Keil C51 is not particularly friendly to function pointers, but in this case the table would be constant, and the tool chain should be able to figure out the call tree with a minimum of OVERLAY surgery.

    To eliminate the runtime overhead, if you have writable program store, you could keep both this table of API entry points and a list of call locations, and link the two halves together once at program start. This is essentially dynamically loading one of the two banks at boot time. Again, there's some work involves to pull the call points out of the object files and store them as data for your program.

    The regular bank switching mechanism isn't particularly applicable not because of overhead, but because it's designed to support a large set of different functions, rather than multiple instance of the same function. In this problem, you don't have a larger code address space, but rather alternate address spaces.

Children
More questions in this forum