You are on page 1of 17

Roman Budek

Industry Consultant RomanB60156@yahoo.com

Microcontrollers Application Note #54
Ways to reduce RAM and ROM sizes

rev: orig:

4/25/03 3/2/03

First of all, determine exactly how much RAM and ROM is being used by your software. The PRN file will show RAM and ROM sizes are used for the particular module, but not for the whole code. The best way to check the entire code size is to look at the MAP file. Therefore, we will use some sample code for the uPD789464 device.

AN_uc54.doc

The memory map from the Users Manual is:

Page 2 of 17

Now, open up the MAP file and you will see the following section for ROM: ACTUAL CODE
*** Memory map *** SPACE=REGULAR MEMORY=ROM BASE ADDRESS=0000H SIZE=2000H OUTPUT INPUT INPUT SEGMENT SEGMENT MODULE @@VECT00 CSEG AT @@VECT00 @cstart * gap * @@VECT0A CSEG AT @@VECT0A main * gap * @@VECT10 CSEG AT @@VECT10 main * gap * @@CALT CSEG CALLT0 @@CALT @cstart @@CALT main @@CALT atod @@CALT timer @@CALT @cproc @@CALT @divuw * gap * @@R_INIS CSEG UNITP @@R_INIS @@R_INIS @@R_INIS @@R_INIS @@R_INIS @@LCODE CSEG @@LCODE @@LCODE @@LCODE @@LCODE @@LCODE @@LCODE @@LCODE @@LCODE @@R_INIT CSEG @cstart @cmul @imul @lscmp @cproc @hdwinit @divuw exit 0080H 00FDH 0119H 0148H 016CH 0188H 0189H 01C0H 01EAH 007DH 001CH 002FH 0024H 001CH 0001H 0037H 002AH 0000H @cstart main atod timer @rom

COMMENTS

BASE ADDRESS 0000H 0000H 0002H 000AH 000AH 000EH 0010H 0010H 0012H 0040H 0040H 0040H 0040H 0040H 0040H 0044H 0046H 0080H 0080H 0080H 0080H 0080H 0080H 0080H

SIZE 0002H 0002H 0008H 0004H 0004H 0002H 0002H 0002H 002EH 0006H 0000H 0000H 0000H 0000H 0004H 0002H 003AH 0000H 0000H 0000H 0000H 0000H 0000H 016AH <-CALLT area as shown in the memory map. Can be allocated to regular ROM if desired.

<--

Start of C code. Exactly where the memory map shows it should be (at 0080h)

Page 3 of 17

@@R_INIT @@R_INIT @@R_INIT @@R_INIT @@R_INIT @@CNST CSEG @@CNST @@CNST @@CNST @@CNST @@CODE CSEG @@CODE @@CODE @@CODE * gap *

@cstart main atod timer @rom @cstart main atod timer main atod timer

01EAH 01EAH 01EAH 01EAH 01EAH 01EAH 01EAH 01EAH 01EAH 01EAH 01EAH 01EAH 1B9BH 1BFAH 1BFCH

0000H 0000H 0000H 0000H 0000H 0000H 0000H 0000H 0000H 0000H 1A12H 19B1H 005FH 0002H 0404H <-<-Last line of code. Last code at 1BFC and there are 404h (1028) bytes free.

Page 4 of 17

Now, open up the MAP file and you will see the following section for RAM: ACTUAL CODE
MEMORY=RAM BASE ADDRESS=FE00H SIZE=0200H OUTPUT INPUT INPUT SEGMENT SEGMENT MODULE @@INIT DSEG @@INIT @cstart @@INIT main @@INIT atod @@INIT timer @@INIT @rom * gap * @@INIS DSEG SADDRP @@INIS @cstart @@INIS main @@INIS atod @@INIS timer @@INIS @rom @@DATS DSEG SADDRP @@DATS @cstart @@DATS main @@DATS atod @@DATS timer @@DATS @rom @@DATA DSEG @@DATA @cstart @@DATA main @@DATA atod @@DATA timer @@DATA @rom @@BITS BSEG @@BITS @cstart @@BITS main @@BITS atod @@BITS timer * gap * @@RTARG0 DSEG AT @@RTARG0 @RTARG0 * gap (Not Free Area) *

COMMENTS

BASE ADDRESS FE00H FE00H FE00H FE00H FE00H FE00H FE00H FE20H FE20H FE20H FE20H FE20H FE20H FE20H FE20H FE20H FE6EH FE7AH FE7CH FE7CH FE7CH FEF4H FEF4H FEF4H FEF4H FEF4H FEF4H.0 FEF4H.0 FEF4H.0 FEF4H.0 FEF4H FEF8H FEF8H FF00H

SIZE 0000H 0000H 0000H 0000H 0000H 0000H 0020H 0000H 0000H 0000H 0000H 0000H 0000H 005CH 0000H 004EH 000CH 0002H 0000H 0078H 0078H 0000H 0000H 0000H 0000H 0000H 0000H.0 0000H.0 0000H.0 0000H.0 0004H 0008H 0008H 0100H <-<-<-RAM location of variables for each source file <-Start of RAM. Exactly where the memory map shows it should be (at FE00h)

<--

RAM used by library. Can reduce by modifying CSTART.ASM

<-<--

Last memory. Memory ends at FF00h. The gap, in this case, is for the SFR section.

Page 5 of 17

To find the Stack Size and allocation, look for the symbols _@STBEG (stack begin) and _@STEND. These can be found in the MAP and SYM files. In this case, we will again look at the MAP file. ACTUAL CODE
*** Public symbol list *** MODULE @cstart @cstart @cstart @cstart @cstart @cstart @cstart @cstart @cstart @cstart @cstart @cstart main @hdwinit exit @rom @rom @rom @rom main main main main main @cproc @cproc @cproc @rom @rom @RTARG0 @RTARG0 ATTR ADDR ADDR ADDR ADDR ADDR ADDR ADDR ADDR ADDR ADDR ADDR ADDR ADDR NUM ADDR ADDR ADDR ADDR ADDR ADDR ADDR ADDR ADDR ADDR ADDR ADDR ADDR ADDR ADDR ADDR ADDR ADDR NUM VALUE 0080H 00FDH FED2H FED4H FEF4H FE7CH FEBCH FEBEH FEC2H FEC6H FECEH FED0H 1A0BH FE20H 0188H 01C0H 01EAH 0080H FEF4H FE7CH 01EAH 01F7H 022DH 027FH 028AH 0042H 016CH 017BH FE00H FE20H FEFDH FEFFH FE00H NAME _@cstart _@cend _@BRKADR _@MEMTOP _@MEMBTM _@FNCTBL _@FNCENT _@SEED _@DIVR _@LDIVR _@TOKPTR _errno _main _@STBEG _hdwinit _exit _?R_INIT _?R_INIS _?DATA _?DATS _TurnOffLCD _TurnOnLCD _Init_LCD _MainClkOff _MainClkOn _@cdisp ?@cprep ?@cdisp _?INIT _?INIS _@RTARG5 _@RTARG7 _@STEND

COMMENTS

<--

Start of STACK (at FE20h)

<--

Stack ends at FE00h.

Page 6 of 17

So, now we can calculate the following values:

RAM/ROM ROM RAM STACK

Segment Name @@CODE @@DATA @@STACK

Start 0080 FE00 FE20

Calculated Size 1BFC 7,036KB FEF8 248B FE00 32B

End

Page 7 of 17

Now, some quick notes on how to reduce the RAM and ROM sizes.

1.

Use unsigned char variables whenever possible.
Look through the PRN file and determine which variables are 2 segments (bytes) long when they only need to be 1 segment (byte) long. Change these integer variables to unsigned char and you will save one byte per variable. This will reduce the RAM size and also dramatically reduce the ROM size because compares can be done with single instructions.

2.

Find which function calls are the longest
Look through the PRN file for each file. See which lines of the code can be made into function calls. This will slightly increase execution time but will drastically reduce ROM size.

3.

Find which states and/or assignments are repeatedly re-assigned
For instance, if LCD code is present, then typically most of the LCD is off during operation and only a few segments are turned on. Therefore, create a function call which assigns the LCD state variables to be zero. Each state of the state machine would then call the function which sets the LCD variables to zero, and then turns on the ones which are needed.

Page 8 of 17

4.

Consider writing some code in assembler
Some operations are simply easier to write and easier to implement in assembler. One good example is when writing LCD data to the register. Attached is the code which combine C code and assembler. Clear the LCD: #asm push hl push de movw hl,#0FA08h; uPD78F9468 or uPD78F9418A mov a,#0Fh; Turn on all segments ; ; ; S08 mov [hl],a decw hl S07 mov [hl],a decw hl S06 mov [hl],a decw hl pop de pop hl #endasm

Now, fill the LCD with valid data: // S06 dataXfer = 0x00; //Zero out if (SvnSegPlus==1) { dataXfer = dataXfer + 0x08; } if (SvnSegMinus==1) { dataXfer = dataXfer + 0x04; } #asm push hl push de movw hl,#0FA06h; uPD78F9468 or uPD78F9418A mov a,_dataXfer mov [hl],a pop de pop hl #endasm Implementing this same code strictly in C code would be difficult to follow and would also use much more ROM space.

Page 9 of 17

5.

Remove code from the CSTART.ASM file to reduce RAM size
CSTART.ASM file might have to be modified to remove unwanted functionality. In the case of the uPD789860 device, the CSTART.ASM file by itself will use too much RAM. 1. Copy the CSTART.ASM from the \NECTools32\SRC\CC78K0S\SRC directory into your project file directory and call it STARTUP.ASM. 2. Add STARTUP.ASM to the source file list. 3. Edit STARTUP.ASM and remove unnecessary functionality. 4. Go into the compiler options of the Project Manager:

5. 6. Unclick the “Using Startup Routine” so that the compiler will use only your STARTUP.ASM file.

7. 8. Remove all library references from Linker Options.

Page 10 of 17

6.

Use the optimizing function of the C compiler
From the Project Manager, select compiler options.

From there, click on the Optimize tab, and then select to optimize for code size.

Page 11 of 17

This will cause the compiler to use the CALLT area of the ROM space for some routines. The execution speed will increase slightly because the address of the function will have to be re-constructed at run time, but it should only be a slight increase. The code size, however, can be dramatically reduced. On the attached example, the code size went from 7.03KB down to 6.7KB just by selecting this option.

Page 12 of 17

7.

Convert INT and BOOLEAN variables to be bits of a flag variable
Look through the code and find variables that might be defined as CHAR or INT but are really only being used a Boolean on/off flags. An easy way to convert these is to use the #define statement as shown below: BEFORE CONVERTING: sreg sreg sreg sreg sreg sreg sreg sreg sreg unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned unsigned char char char char char char char char char TwoFour, FourNine, NineTwelve; Hi, Lo; Drill, Drill2, Screw; HardWood, SoftWood, Metal; LowBatt, OverTemp; TopLine, BtmLine; LowBattExists, CrashBattExists; OverTempExists; DisplayRefresh;

AFTER CONVERTING: sreg unsigned char LCDflg1; #define TwoFour LCDflg1.0 #define FourNine LCDflg1.1 #define NineTwelve LCDflg1.2 #define Hi LCDflg1.3 #define Lo LCDflg1.4 #define Drill LCDflg1.5 #define Drill2 LCDflg1.6 #define Screw LCDflg1.7 sreg unsigned char LCDflg2; #define HardWood LCDflg2.0 #define SoftWood LCDflg2.1 #define Metal LCDflg2.2 #define LowBatt LCDflg2.3 #define OverTemp LCDflg2.4 #define TopLine LCDflg2.5 #define BtmLine LCDflg2.6 #define LowBattExists LCDflg2.7 sreg unsigned char LCDflg3; #define CrashBattExists LCDflg3.0 #define OverTempExists LCDflg3.1 #define DisplayRefresh LCDflg3.2

COMMENTS: None of the logic, variable assignments, nor logical comparisons in the program changed. Each of these variables was only a zero/one value so why waste a whole byte on the variable. A total of 16 bytes of RAM were saved with this code change. The ROM size should remain roughly the same as before the code change, especially on the K0s which does not have bit manipulation instructions.

Page 13 of 17

Page 14 of 17

8.

Increasing the STACK size
The Stack size is normally assigned to the largest continuous block of free RAM available. However, in some cases it is desired to shift down the memory or to purposely set the Stack size to a certain size. The first step involves creating a directive file. For this example, we will use the uPD789464 device, whose memory map is shown above. Now, assume that the stack size needs to be increased from the default (0FE00H - 0FE20H) to a larger (0FE00H – 0FE30H). Using an ordinary ASCII text editor, create the following STACK.DR file. memory RAM : (0FE30H, 0D0H) memory STACK : (0FE00H, 30H)

Now, edit the Linker Options under the Project Manager.

Page 15 of 17

Use the Browser button to locate the STACK.DR file name. Also, check the “Create Stack Symbol” box. Finally, enter STACK in the area name. Normally, this should cause the stack size to increase to the properly assigned 0FE00H – 0FE30H. However, in some cases this still does not happen because the libraries assign their own stack size. The solution for this is to copy the following files into the project directory: 1. \NECTools32\src\cck0s\src\cstart.asm 2. \NECTools32\src\cck0s\src\macro.inc 3. \NECTools32\src\cck0s\src\def.inc Edit the cstart.asm file and change it from: ;--------------------------------------------------------------; setting the stack pointer ; ; _@STBEG is created by linker with -S option. ;-------------------------------------------------------------MOVW AX,#_@STBEG ;SP <- stack begin address

To the following: ;--------------------------------------------------------------; setting the stack pointer ; ; _@STBEG is created by linker with -S option. ;-------------------------------------------------------------MOVW AX,#0FE30H ;SP <- stack begin address

Unclick the “Using Startup Routine” so that the compiler will use only your STARTUP.ASM file. This is found under the Compiler Options -> Startup Routine menu.

Then, add cstart.asm to the source file list.

Page 16 of 17

The software should now build properly and the stack will be at 0FE00H – 0FE30H. Note that the STBEG and STEND symbols might not reflect the proper value. However, use the simulator or emulator and observe the first line of the disassembled code and you should be able to spot the 0FE30H value being loaded into AX and then being stored into SP (Stack Pointer).

Please feel free to call me if there are any questions.

Roman Budek

Page 17 of 17