sprintf() + --model-large option : freezes

  • frenchy82


    I am working on a nRF24E1 eval board from Nordic Semiconductor (4k XRAM, 8051 compatible core). I'm using SDCC 2.6.0. I compile C code with SDCC and then program the nRF24E1 eeprom with NRFPROG 1.31.

    My program freezes when I use both sprintf function and --model-large compiler option.

    My program uses Timer1 for baud generator and Timer0 for counting seconds.
    Each second, "Tick" + elapsed seconds is sent to UART. Each second, ports P0.3 to P0.5 are switched to blink 3 LEDS.

    To sum up,
    program without    sprintf without    option --model-large : WORKS : "Tick" every second
    program with    sprintf without    option --model-large : WORKS : "Tick" +elapsed seconds every second
    program with    sprintf with    option --model-large : FAILS : "Tick" only once.

    So the problem seems to come from the stdio lib in large memory model.
    Could anybody give me any help please?


    #include "reg24e1.h"
    #include <stdio.h>
    __data unsigned int seconds;
    __data unsigned int timer0_tick;

    void PutChar(char c)
        TI = 0;
        SBUF = c;

    void PutString(char *s)
        while(*s != 0)

    void Init(void)
        TH1 = 243;      // 19200@16MHz (when T1M=1 and SMOD=1)
        CKCON |= 0x10;  // T1M=1 (/4 timer clock)
        PCON = 0x80;    // SMOD=1 (double baud rate)
        SCON = 0x52;    // Serial mode1, enable receiver
        TMOD = 0x20;    // Timer1 8bit auto reload
        TR1 = 1;        // Start timer1
        P0_DIR |= 0x02; // P0.1 (RxD) is an input
        P0_DIR &= 0xC7; // P0.3, P0.4 and P0.5 are outputs
        P0_ALT |= 0x06; // Select alternate functions on pins P0.1 and P0.2
        P0_ALT &= 0xC7; // Select GPIO functions on pins P0.3, P0.4 and P0.5

    void InitTimer0 (void)
        EA = 0;
        timer0_tick = 0;
        seconds = 0;
        TR0 = 0;
        TMOD &= 0xF0;
        TMOD |= 0x01;
        TL0 = 0xCE;
        TH0 = 0xCB;
        PT0 = 1;    //set high priority interrupt for timer 0
        ET0 = 1;    //enable timer 0 interrupt
        TR0 = 1;    //start timer 0
           EA = 1;        //enable interrupts

    static void Timer0ISR (void) interrupt 1 using 1
        TR0 = 0; //stop timer 0
        TL0 = 0xCE;
        TH0 = 0xCB;
        TR0 = 1; //start timer 0

    unsigned int SecCounter()
        char buf[3];
            P0 = P0 ^ 0x38;
            PutString("Tick ");
            timer0_tick = 100; // reset timer
        return seconds;

    int main(void)


    #ifndef REG24E1_H__
    #define REG24E1_H__

    /* BYTE Registers */

    __sfr __at (0x80) P0;
    __sfr __at (0x81) SP;
    __sfr __at (0x82) DPL;
    __sfr __at (0x83) DPH ;
    __sfr __at (0x84) DPL1 ;
    __sfr __at (0x85) DPH1 ;
    __sfr __at (0x86) DPS ;
    __sfr __at (0x87) PCON ;
    __sfr __at (0x88) TCON ;
    __sfr __at (0x89) TMOD ;
    __sfr __at (0x8A) TL0 ;
    __sfr __at (0x8B) TL1 ;
    __sfr __at (0x8C) TH0 ;
    __sfr __at (0x8D) TH1 ;
    __sfr __at (0x8E) CKCON ;
    __sfr __at (0x90) P1 ;
    __sfr __at (0x91) EXIF ;
    __sfr __at (0x92) MPAGE ;
    __sfr __at (0x98) SCON ;
    __sfr __at (0x99) SBUF ;
    __sfr __at (0xA8) IE ;
    __sfr __at (0xB8) IP ;
    __sfr __at (0xC8) T2CON ;
    __sfr __at (0xCA) RCAP2L ;
    __sfr __at (0xCB) RCAP2H ;
    __sfr __at (0xCC) TL2 ;
    __sfr __at (0xCD) TH2 ;
    __sfr __at (0xD0) PSW ;
    __sfr __at (0xD8) EICON ;
    __sfr __at (0xE0) ACC ;
    __sfr __at (0xF0) B ;
    __sfr __at (0xE8) EIE ;
    __sfr __at (0xF8) EIP ;

    __sfr __at (0x94) P0_DIR ;
    __sfr __at (0x95) P0_ALT ;
    __sfr __at (0x96) P1_DIR;
    __sfr __at (0x97) P1_ALT;
    __sfr __at (0xA0) RADIO ;
    __sfr __at (0xA1) ADCCON;
    __sfr __at (0xA2) ADCDATAH;
    __sfr __at (0xA3) ADCDATAL ;
    __sfr __at (0xA4) ADCSTATIC ;
    __sfr __at (0xA9) PWMCON ;
    __sfr __at (0xAA) PWMDUTY ;
    __sfr __at (0xAB) REGX_MSB ;
    __sfr __at (0xAC) REGX_LSB ;
    __sfr __at (0xAD) REGX_CTRL ;
    __sfr __at (0xB1) RSTREAS ;
    __sfr __at (0xB2) SPI_DATA ;
    __sfr __at (0xB3) SPI_CTRL ;
    __sfr __at (0xB4) SPICLK ;
    __sfr __at (0xB5) TICK_DV ;
    __sfr __at (0xB6) CK_CTRL ;

    /* BIT Registers */

    /* PSW */
    __sbit __at (0xD7) CY;
    __sbit __at (0xD6) AC;
    __sbit __at (0xD5) F0;
    __sbit __at (0xD4) RS1;
    __sbit __at (0xD3) RS0;
    __sbit __at (0xD2) OV;
    __sbit __at (0xD1) F1;
    __sbit __at (0xD0) P;

    /* TCON */
    __sbit __at (0x8F) TF1;
    __sbit __at (0x8E) TR1;
    __sbit __at (0x8D) TF0;
    __sbit __at (0x8C) TR0;
    __sbit __at (0x8B) IE1;
    __sbit __at (0x8A) IT1;
    __sbit __at (0x89) IE0;
    __sbit __at (0x88) IT0;

    /* IE */
    __sbit __at (0xAF) EA;
    __sbit __at (0xAD) ET2;
    __sbit __at (0xAC) ES;
    __sbit __at (0xAB) ET1;
    __sbit __at (0xAA) EX1;
    __sbit __at (0xA9) ET0;
    __sbit __at (0xA8) EX0;

    /* IP */
    __sbit __at (0xBD) PT2;
    __sbit __at (0xBC) PS;
    __sbit __at (0xBB) PT1;
    __sbit __at (0xBA) PX1;
    __sbit __at (0xB9) PT0;
    __sbit __at (0xB8) PX0;

    /* P0 */
    __sbit __at (0x86) T1;
    __sbit __at (0x85) T0;
    __sbit __at (0x84) INT1;
    __sbit __at (0x83) INT0;

    /* P1 */
    __sbit __at (0x90) T2;

    /* SCON */
    __sbit __at (0x9F) SM0;
    __sbit __at (0x9E) SM1;
    __sbit __at (0x9D) SM2;
    __sbit __at (0x9C) REN;
    __sbit __at (0x9B) TB8;
    __sbit __at (0x9A) RB8;
    __sbit __at (0x99) TI;
    __sbit __at (0x98) RI;

    /* T2CON */
    __sbit __at (0xCF) TF2;
    __sbit __at (0xCE) EXF2;
    __sbit __at (0xCD) RCLK;
    __sbit __at (0xCC) TCLK;
    __sbit __at (0xCB) EXEN2;
    __sbit __at (0xCA) TR2;
    __sbit __at (0xC9) C_T2;
    __sbit __at (0xC8) CP_RL2;

    /* EICON */
    __sbit __at (0xDF) SMOD1;
    __sbit __at (0xDB) WDTI;

    /* EIE */
    __sbit __at (0xEC) EWDI;
    __sbit __at (0xEB) EX5;
    __sbit __at (0xEA) EX4;
    __sbit __at (0xE9) EX3;
    __sbit __at (0xE8) EX2;

    /* EIP */
    __sbit __at (0xFC) PWDI;
    __sbit __at (0xFB) PX5;
    __sbit __at (0xFA) PX4;
    __sbit __at (0xF9) PX3;
    __sbit __at (0xF8) PX2;

    /* RADIO */
    __sbit __at (0xA7) PWR_UP;
    __sbit __at (0xA6) DR2;
    __sbit __at (0xA6) CE;
    __sbit __at (0xA5) CLK2;
    __sbit __at (0xA4) DOUT2;
    __sbit __at (0xA3) CS;
    __sbit __at (0xA2) DR1;
    __sbit __at (0xA1) CLK1;
    __sbit __at (0xA0) DATA;


    • Maarten Brock
      Maarten Brock


      This mcu has internal xdata. During startup initialization data is copied from code to xdata memory using MOVX @R0 (see crtxinit.asm in the library). Normally this requires that the high byte of the address is presented at P2, but for derivatives with internal xdata another SFR is used that goes by the name of XPAG, MPAGE, EMI0CN or whatever the mfg chose this time. SDCC calls this sfr _XPAGE and it defaults to P2. For the nRF24E1 it must be declared identical to MPAGE. See also the manual "4.1.1 pdata access by SFR".

      What you need to do is add this line to reg24e1.h :

      __sfr __at (0x92) _XPAGE ;

      If this fixes the problem, are you willing to give the header file to SDCC so others can benefit from it? If so please upload it in the patch tracker.


    • frenchy82

      Hello Maarten,

      thanks for you answer. Unfortunately, this didn't help.

      I have deeper investigated the problem. It doesn't come from sprintf and the stdio library as it is still there when I remove sprintf function.

      I couldn't debug the code on chip, but I have used LEDS connected to P0.3 to P0.5. to monitor code execution.

      It seems that when compiling with --large-model option, and running, the execution flow never reaches Timer0ISR interrupt service routine. But the main loop is indeed running, because I can have a LED blink there.

      Is there anything in large memory model that could avoid interrupts from being triggered?

    • Maarten Brock
      Maarten Brock

      My next thought was the watchdog, but it seems it is disabled at startup.

      But wait, there's more. This mcu runs code from ram. The code memory space and xdata memory space are essentially the same! So what options did you use except --model-large? You need to tell SDCC to put xdata somewhere else than from zero up. At address 0 the reset vector lives and the interrupt vectors are next. By default SDCC puts xdata at 0 too and thus overwrites your vectors!

      And there is only 4kB of RAM. So it's wise to let the linker check for overflows.

      I recommend to use something like:

      --code-size 0x0C00
      --xram-loc  0x0C00
      --xram-size 0x0400

      This limits code to the first 3kB, puts xdata at 0x0C00 and limits it to 1kB.

      One last warning: sprintf is a very large function. You will not be able to put much more code in 4kB.


    • frenchy82

      Ok I have added these options :
      --code-size 0x0C00
      --xram-loc 0x0C00
      --xram-size 0x0400

      and it works!

      You were absolutely right, on this nRF24E1 mcu, program code and data share the same memory and adresses.
      This is because the mcu is 8XC52 - based.

      Could you add a warning for this issue in SDCC doc? It may spare time to other people in the future!

      Thanks a lot Maarten