Nut/ATmega256x
Contents
ATmega256x Development Rules
I decided to put some explanation here, because porting our application to 256x AVR variant was really painful. I tried to get some info in avr-gcc list, but they are probably using Free RTOS and no bootloader with a requirement to call functions in upper half with eicall instruction.
avr-gcc Limitation
The avr-gcc compiler generates eicall instructions without seeding the eind register. This is because avr-gcc still works with 16bit pointers. Eicall is generated e.g. for indirect calls through arrays or device structures. So in fact eicall instruction with current avr-gcc cannot call a function in upper half flash. With this in mind, we found all functions, that were addressed by eicall calls and we placed them in "lower" sections (in source, with attribute). These sections are then linked first to fit in lower half. Little modification of linker script is required.
Newer avr-gcc (do not forget to switch on various optimizations to stay with a small code image) is using a trampolines area. eicall target addresses point to a place in lower half, where far jmps reside. This has an advantage, that it works for (and also from) both flash halves. Disadvantage of this is an additional jmp instruction that has to be executed. Placing modules into a lower section manually is a tedious job, but produces fastest code.
Nut/OS Conflicts
All C constructs, where a function pointer is dereferenced, generates eicall. Targeting functions has to be placed in lower sections as the compiler/linker does not generate a real far call with setting eind register. In our application, where TCP, UART, NIC RTL and TWI interfaces are used, we forced following functions in lower sections:
_sputb AvrUsartGetFlowControl AvrUsartSetSpeed NutEtherOutput UsartClose AvrInterrupt5Ctl AvrUsartGetParity AvrUsartSetStatus NutEventTimeout UsartInit AvrTimer2OvfIrqCtl AvrUsartGetSpeed AvrUsartSetStopBits NutIdle UsartIOCtl AvrTwiIrqCtl AvrUsartGetStatus AvrUsartTxComplete NutTcpDeviceIOCtl UsartOpen AvrUart0RxIrqCtl AvrUsartGetStopBits AvrUsartTxEmpty NutTcpDeviceRead UsartRead AvrUart0TxDataIrqCtl AvrUsartInit AvrUsartTxStart NutTcpDeviceWrite UsartSize AvrUart1RxIrqCtl AvrUsartRxComplete NicInit NutTcpDeviceWrite_P UsartWrite AvrUart1TxDataIrqCtl AvrUsartRxStart NicInterrupt NutTcpSm UsartWrite_P AvrUart1TxIrqCtl AvrUsartSetClockMode NicOutput NutThreadEntry AvrUsartDeinit AvrUsartSetDataBits NicRx NutThreadWake AvrUsartGetClockMode AvrUsartSetFlowControl NutDhcpClient NutTimerIntr AvrUsartGetDataBits AvrUsartSetParity NutEtherInput TwInterrupt
Bootloader Specials
If you are using a bootloader which has a code, that generates eicall, you have to seed eind to 1. Example of such a code:
fn[sect_id](address, buf, len); //fn is an array of fn pointers
If you are using this bootloader from an application - purpose can be to share bootloader mechanism and to feed it with data, received e.g. via http. Then bootloader is called iteratively as packets are coming in from a TCP connection.
As I explained above, in bootloader, we need to have eind set to 1. The only solution, we found, is to disable interrupts during a time, when eind == 1. This is because, when an interrupt occurs, it cannot use eicall to reach lower half of flash. For which interrupts this is true ? For all. This is because eicall is generated by CallHandler code (see below for details).
Using a fn pointer in CallHandler generates eicall.
typedef struct { u_long ir_count; void *ir_arg; void (*ir_handler) (void *); } IRQ_HANDLER;
#define NUTSIGNAL(signame,handler) \ SIGNAL(signame) \ { CallHandler (&handler); }
IRQ_HANDLER sig_INTERRUPT5;
... NUTSIGNAL(SIG_INTERRUPT5, sig_INTERRUPT5) ...
void CallHandler(IRQ_HANDLER * irh) { irh->ir_count++; if (irh->ir_handler) (irh->ir_handler) (irh->ir_arg); //-> eicall }