Dear All,
I am working on a project involving XC2287M microcontroller + AX88796B Ethernet chip and use PK166 & ARTX-166 TCP/IP stack, no ARTX uOS, HLarge memory model. I had to develop firmware for the same proprietary PCB but with support of 2 targets: a) Internal memory mode - running from on-chip MCU' flash and using on-chip MCU's RAM b) External memory mode - running from an external flash and using an external RAM
Options for Target -> C166 are as follows (checked ones are shown):
- favor speed - reorder instructions - alias checking - save DPP - save temporary variables on user stack - MODV2 - for a) mode: optimization level #7, common tail merging - for b) mode: optimization level #6, constant propagation
For shown options, firmware executes OK both for a) and b) but here is the problem: when I switch to the optimization level #7 for target b), firmware crashes.
From ASM listings I see the difference in generated code: inside a function 'X', right before '}' statement in C code (i.e. return), level #7 gives JMPS instead of CALLS + RETS as for level #6: before the '}' in 'X' there is a call to another function 'Y'.
Could someone clarify: 1) If this is (un-)known compiler bug (?) - looks incorrectly to quit the function with no RETS - would not this destroy the stack? 2) How to ensure that the rest of code - other C modules - will not have similar issues when compiled at a given optimization level? 3) Why firmware running from internal memory is OK but firmware built for external memory fails? It is seen from the map file that 'distance' between function addresses is 'longer' for faulty case
I found a fix: inserting a single 'nop' in 'X' in source C file after call to 'Y' and right before '}' solves the issue, i.e. eliminates JMPS optimization. Unfortunately this fix is non-reliable, there are too many pieces of code where it is so easy to overlook inserting this extra nop.
Looking for an advice to do it on IDE/compiler level and if I am doing something wrong or missing some nessesary setting?
Below are details of my findings.
Regards, Nikolay.
******************************************************************************
Ethernet driver snippets (Ax88796.c is a part of Keil's TCP/IP, was modified partially to work with 'B' chip):
: void int_enable_eth (void) { /* Ethernet Interrupt Enable function. */ HVAR(U8, CSR_IMR) = IMR_PRXE | IMR_PTXE | IMR_TXEE; }
The function that was found to be problematic:
void send_frame (OS_FRAME *frame) { ... while (TxActive); ... TxActive = __TRUE; int_enable_eth (); }
ASM code for Optimization Level #7 shows JMPS with no RETS (!):
; FUNCTION send_frame (BEGIN RMASK = @0x5DF0) ... 0578 ?C0057: 0578 8A00FE00 R JB TxActive,?C0057 ... 0602 0F00 R BSET TxActive 0604 FA000000 R JMPS SEG (int_enable_eth),int_enable_eth ; FUNCTION send_frame (END RMASK = @0x5DF0)
ASM code for the same C source file compiled for optimization level #6 gives expected RETS:
; FUNCTION send_frame ... 0578 ?C0057: 0578 8A00FE00 R JB TxActive,?C0057 ... 0602 0F00 R BSET TxActive 0604 DA000000 R CALLS SEG (int_enable_eth),int_enable_eth 0608 DB00 RETS ; FUNCTION send_frame
In other words, for Level #7, compiler 'optimizes' RETS instruction replacing it with JMPS to another function (int_enable_eth) at the end of caller's function (send_frame): it happens if call is placed right before return statement in C code.
... TxActive = __TRUE; int_enable_eth (); _nop_(); // this one 'nop' fixes the problem }
Map file snippets (note different 'distance' in addresses for 2 functions): a) Internal memory mode
: C0AA42H int_enable_eth LABEL --- FCODE ?PR?AX88796 C0AA42H BLOCK LVL=0 000CH --- int_enable_eth : C0AC16H send_frame LABEL --- FCODE ?PR?AX88796 C0AC16H BLOCK LVL=0 0090H --- send_frame
b) External memory mode
012EBCH int_enable_eth LABEL --- FCODE ?PR?AX88796 012EBCH BLOCK LVL=0 000CH --- int_enable_eth : 013090H send_frame LABEL --- FCODE ?PR?AX88796 013090H BLOCK LVL=0 0092H --- send_frame
a) Memory map for internal memory mode
CLASSES (ICODE (0xC00000-0xC0EFFF), FCODE (0xC00000-0xC0EFFF, 0xC10000-0xCCFFFF), FCONST (0xC00000-0xC0EFFF, 0xC10000-0xCCFFFF), HCONST (0xC00000-0xC0EFFF, 0xC10000-0xCCFFFF), XCONST (0xC00000-0xC0EFFF, 0xC10000-0xCCFFFF), NCONST (0xC04000-0xC07FFF), NDATA (0xE00000-0xE03FFF), NDATA0 (0xE00000-0xE03FFF), SDATA (0xC000-0xDFFF, 0xF600-0xFDFF), SDATA0 (0xC000-0xDFFF, 0xF600-0xFDFF), IDATA (0xF600-0xFDFF), IDATA0 (0xF600-0xFDFF), FDATA (0xA000-0xDFFF, 0xE00000-0xE07FFF), FDATA0 (0xA000-0xDFFF, 0xE00000-0xE07FFF), HDATA (0xA000-0xDFFF, 0xE00000-0xE07FFF), HDATA0 (0xA000-0xDFFF, 0xE00000-0xE07FFF), XDATA (0xA000-0xDFFF, 0xE00000-0xE07FFF), XDATA0 (0xA000-0xDFFF, 0xE00000-0xE07FFF)) CINITTAB (0xC10000-0xCCFFFF)
b) Memory map for external memory mode (flash starts from address 0)
CLASSES (ICODE (0x0-0x7FFF), FCODE (0x0-0x7FFF, 0x10000-0xFFFFF), FCONST (0x0-0x7FFF, 0x10000-0xFFFFF), HCONST (0x0-0x7FFF, 0x10000-0xFFFFF), XCONST (0x0-0x7FFF, 0x10000-0xFFFFF), NCONST (0x4000-0x7FFF), NDATA (0x400000-0x403FFF), NDATA0 (0x400000-0x403FFF), SDATA (0xF600-0xFDFF), SDATA0 (0xF600-0xFDFF), IDATA (0xF600-0xFDFF), IDATA0 (0xF600-0xFDFF), FDATA (0x400000-0x4FFFFF), FDATA0 (0x400000-0x4FFFFF), HDATA (0x400000-0x4FFFFF), HDATA0 (0x400000-0x4FFFFF), XDATA (0x400000-0x4FFFFF), XDATA0 (0x400000-0x4FFFFF)) CINITTAB (0x10000-0xFFFFF)
Keil IDE information:
IDE-Version: uVision V4.22.0.0 Toolchain: PK166 Prof. Developers Kit Version: 7.05 Middleware: AR166 Advanced RTOS Version 3.20 EC++ Compiler: EC166.Exe V1.09d C Compiler: C166.Exe V7.05 Assembler: A166.Exe V5.34 Linker/Locator: L166.Exe V5.25 Librarian: LIB166.Exe V4.29 Hex Converter: OH166.Exe V4.7a CPU DLL: S166.DLL V3.80.0.0 Dialog DLL: D167.DLL V2.53 Target DLL: BIN\UL2OCDS.DLL V1.26.0.0 Dialog DLL: T167.DLL V2.50.0.1
--- END