You are on page 1of 17

Roman Budek

Industry Consultant
RomanB60156@yahoo.com

rev: 4/25/03
Microcontrollers Application Note #54 orig: 3/2/03
Ways to reduce RAM and ROM sizes

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:

COMMENTS
ACTUAL CODE
*** Memory map ***

SPACE=REGULAR

MEMORY=ROM
BASE ADDRESS=0000H SIZE=2000H
OUTPUT INPUT INPUT BASE SIZE
SEGMENT SEGMENT MODULE ADDRESS
@@VECT00 0000H 0002H
CSEG AT
@@VECT00 @cstart 0000H 0002H
* gap * 0002H 0008H
@@VECT0A 000AH 0004H
CSEG AT
@@VECT0A main 000AH 0004H
* gap * 000EH 0002H
@@VECT10 0010H 0002H
CSEG AT
@@VECT10 main 0010H 0002H
<-- CALLT area as
* gap * 0012H 002EH
@@CALT 0040H 0006H shown in the
CSEG CALLT0 memory map.
@@CALT @cstart 0040H 0000H Can be allocated
@@CALT main 0040H 0000H to regular ROM if
@@CALT atod 0040H 0000H desired.
@@CALT timer 0040H 0000H
@@CALT @cproc 0040H 0004H
@@CALT @divuw 0044H 0002H
* gap * 0046H 003AH
@@R_INIS 0080H 0000H
CSEG UNITP

@@R_INIS @cstart 0080H 0000H


@@R_INIS main 0080H 0000H
@@R_INIS atod 0080H 0000H
@@R_INIS timer 0080H 0000H
@@R_INIS @rom 0080H 0000H
@@LCODE 0080H 016AH
CSEG <-- Start of C code.
@@LCODE @cstart 0080H 007DH Exactly where the
@@LCODE @cmul 00FDH 001CH memory map
@@LCODE @imul 0119H 002FH shows it should
@@LCODE @lscmp 0148H 0024H be (at 0080h)
@@LCODE @cproc 016CH 001CH
@@LCODE @hdwinit 0188H 0001H
@@LCODE @divuw 0189H 0037H
@@LCODE exit 01C0H 002AH
@@R_INIT 01EAH 0000H
CSEG

Page 3 of 17
@@R_INIT @cstart 01EAH 0000H
@@R_INIT main 01EAH 0000H
@@R_INIT atod 01EAH 0000H
@@R_INIT timer 01EAH 0000H
@@R_INIT @rom 01EAH 0000H
@@CNST 01EAH 0000H
CSEG
@@CNST @cstart 01EAH 0000H
@@CNST main 01EAH 0000H
@@CNST atod 01EAH 0000H
@@CNST timer 01EAH 0000H
@@CODE 01EAH 1A12H
CSEG
@@CODE main 01EAH 19B1H <-- Last line of code.
@@CODE atod 1B9BH 005FH <-- Last code at
@@CODE timer 1BFAH 0002H 1BFC and there
* gap * 1BFCH 0404H are 404h (1028)
bytes free.

Page 4 of 17
Now, open up the MAP file and you will see the following section for RAM:

COMMENTS
ACTUAL CODE

MEMORY=RAM
BASE ADDRESS=FE00H SIZE=0200H
OUTPUT INPUT INPUT BASE SIZE
SEGMENT SEGMENT MODULE ADDRESS
<-- Start of RAM.
@@INIT FE00H 0000H
DSEG Exactly where the
@@INIT @cstart FE00H 0000H memory map
@@INIT main FE00H 0000H shows it should
@@INIT atod FE00H 0000H be (at FE00h)
@@INIT timer FE00H 0000H
@@INIT @rom FE00H 0000H
* gap * FE00H 0020H
@@INIS FE20H 0000H
DSEG SADDRP
@@INIS @cstart FE20H 0000H
@@INIS main FE20H 0000H
@@INIS atod FE20H 0000H
@@INIS timer FE20H 0000H
@@INIS @rom FE20H 0000H
@@DATS FE20H 005CH
DSEG SADDRP
@@DATS @cstart FE20H 0000H <-- RAM location of
@@DATS main FE20H 004EH <-- variables for each
@@DATS atod FE6EH 000CH <-- source file
@@DATS timer FE7AH 0002H
@@DATS @rom FE7CH 0000H
@@DATA FE7CH 0078H
DSEG <-- RAM used by
@@DATA @cstart FE7CH 0078H library. Can
@@DATA main FEF4H 0000H reduce by
@@DATA atod FEF4H 0000H modifying
@@DATA timer FEF4H 0000H CSTART.ASM
@@DATA @rom FEF4H 0000H
@@BITS FEF4H 0000H
BSEG
@@BITS @cstart FEF4H.0 0000H.0
@@BITS main FEF4H.0 0000H.0
@@BITS atod FEF4H.0 0000H.0
@@BITS timer FEF4H.0 0000H.0
* gap * FEF4H 0004H
@@RTARG0 FEF8H 0008H <-- Last memory.
DSEG AT <-- Memory ends at
@@RTARG0 @RTARG0 FEF8H 0008H FF00h. The gap,
* gap (Not Free Area) * FF00H 0100H 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.

COMMENTS
ACTUAL CODE
*** Public symbol list ***

MODULE ATTR VALUE NAME

@cstart ADDR 0080H _@cstart


@cstart ADDR 00FDH _@cend
@cstart ADDR FED2H _@BRKADR
@cstart ADDR FED4H _@MEMTOP
@cstart ADDR FEF4H _@MEMBTM
@cstart ADDR FE7CH _@FNCTBL
@cstart ADDR FEBCH _@FNCENT
@cstart ADDR FEBEH _@SEED
@cstart ADDR FEC2H _@DIVR
@cstart ADDR FEC6H _@LDIVR
@cstart ADDR FECEH _@TOKPTR
@cstart ADDR FED0H _errno
main ADDR 1A0BH _main <-- Start of STACK
NUM FE20H _@STBEG (at FE20h)
@hdwinit ADDR 0188H _hdwinit
exit ADDR 01C0H _exit
@rom ADDR 01EAH _?R_INIT
@rom ADDR 0080H _?R_INIS
@rom ADDR FEF4H _?DATA
@rom ADDR FE7CH _?DATS
main ADDR 01EAH _TurnOffLCD
main ADDR 01F7H _TurnOnLCD
main ADDR 022DH _Init_LCD
main ADDR 027FH _MainClkOff
main ADDR 028AH _MainClkOn
@cproc ADDR 0042H _@cdisp
@cproc ADDR 016CH ?@cprep
@cproc ADDR 017BH ?@cdisp
@rom ADDR FE00H _?INIT
@rom ADDR FE20H _?INIS
@RTARG0 ADDR FEFDH _@RTARG5
<-- Stack ends at
@RTARG0 ADDR FEFFH _@RTARG7
NUM FE00H _@STEND FE00h.

Page 6 of 17
So, now we can calculate the following values:

RAM/ROM Segment Start End Calculated


Name Size
ROM @@CODE 0080 1BFC 7,036KB
RAM @@DATA FE00 FEF8 248B
STACK @@STACK FE20 FE00 32B

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 unsigned char TwoFour, FourNine, NineTwelve;


sreg unsigned char Hi, Lo;
sreg unsigned char Drill, Drill2, Screw;
sreg unsigned char HardWood, SoftWood, Metal;
sreg unsigned char LowBatt, OverTemp;
sreg unsigned char TopLine, BtmLine;
sreg unsigned char LowBattExists, CrashBattExists;
sreg unsigned char OverTempExists;
sreg unsigned char 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

You might also like