Frequently Asked Questions
This page contains answers to common questions handled by our customer support staff, along with some tips and tricks that we have found useful and presented here as questions.
Please consult the generic EDE FAQ for questions related to the Embedded Development Environment.
Contents
- The compiler does not seem to use registers for function parameter passing.
- I am interfacing a function written in assembly and I am using the right _? prefix, but I still get an unresolved external error on that variable?
- I want to use another tab and indentation size in EDE, how can I do that?
- I want to edit the <project>.mak makefile, but it gets overwritten every build, how can I avoid that?
- I have a large project and it takes very long before a build is being started, can this be avoided?
- I want all the generated files to be placed in a different directory from my project directory, how do I do that?
- In the generated assembly file symbols appear with an "_?" prefix, is this OK?
- The compiler gives an error on a variable name 'data'
- I am running an application on my target using the ROM monitor, now it seems to hang during downloading.
- I cannot change the internal RAM size anymore in EDE.
- All my projects seem to contain a file with the name of the project followed by _cstart.asm.
- I get a "overlapping XDATA 0-1" warning message from the linker
- My 8051 has 1k of internal RAM but I can select only up to 256 bytes of internal RAM.
- I want to attach an interrupt function to a vector address, what interrupt number should I use.
- I want to be able to call a function from functions using different register banks, how can I do this.
- The 8051 I want to use has 768 bytes of auxiliary internal RAM, but I can only use a maximum of 256 bytes.
- When I build my program I get link51 warnings like: link51 W001: unresolved external symbol '__STOREFXD', module <modulename>
- The 8051 derivate I want to use is not yet supported, can I still use it.
- I want to connect an external memory device to the external data bus, how do I set this up?
- How do I setup the memory layout for my project?
- I use the printf() routine but my program does not print floating point numbers. What is wrong?
- What is the difference between the const and _rom C-language keywords?
- I get an Address Space Overflow error message. What did I do wrong?
- I get an Unresolved External Symbol: SMALL/LARGE error message but I do not have such a variable in my application. What is it?
- The 8051 compiler supports mixed memory model applications. However, the Memory Model tab options in EDE can only be selected for the whole project, not for individual modules. Why?
- I use the _getbit() and _putbit() built-in functions for accessing individual bits in a _bitbyte. I tried to use a variable to specify the bit-offset argument in these functions, but the compiler tells me to use a constant value. Why?
- I have defined a bit variable using the _atbit() attribute. When I declare this bit as an external variable in a different module, the linker complains about an unresolved reference. Why?
- I have a piece of conditional code in my application for which the compiler generates no code at all! I also have a piece of conditional code that is executed unconditionally. Why?
- How can I configure the compiler to allow calling functions using a different register bank?
- I created a C program with some interrupt routines. It does not work and I noticed that the interrupt routines were never entered. What is wrong?
- The compiler manual states that option '-gr' emits two additional NOP instructions for every C line and that it should be enabled when debugging with the ROM monitor. Why?
Is your question not listed? Contact TASKING.
The compiler does not seem to use registers for function parameter passing.
Register parameter passing is one of the compiler optimization options and is switched off when you choose optimization level "No optimizations". To activate parameter passing use another optimization level, or select "custom optimization" and activate the "Use register Parameter Passing" directive.
I am interfacing a function written in assembly and I am using the right _? prefix, but I still get an unresolved external error on that variable.
Make sure you use the $CASE control at the top of your assembly file. By default the assembler is case insensitive, which results in all variables being implicitly converted to uppercase.
I want to use another tab and indentation size in EDE, how can I do that?
Go to the Customize | Language... dialog, in this dialog you can change the tab and indentation size and lots of other settings for any specific type of file based on its extension. In the list box "File type:" select the extension of your choice, then select the Tabs/Indenting tab. Here you can specify the tab columns. A selection of "5 9" equals a tab/indentation size of 4, "9 17" equals a tab/indentation size of 8.
I want to edit the <project>.mak makefile, but it gets overwritten every build, how can I avoid that?
The <project>.mak makefile will be generated everytime you change one of the tool options. But since v7.0 it will also be changed just before a build or rebuild is started. This is done to determine the dependencies of the project which may have been changed since the last time the makefile was generated. This can be changed in the Build | Options... dialog. In this dialog uncheck the option 'Scan dependencies before starting a build'.
I have a large project and it takes very long before a build is being started, can this be avoided?
Since v7.0 a project is scanned by default before starting a build. This is done to determine the dependencies of the project which may have been changed since the last time the project makefile was generated. This can be changed in the Build | Options... dialog. In this dialog uncheck the option 'Scan dependencies before starting a build'.
I want all the generated files to be placed in a different directory from my project directory, how do I do that?
Under Project | Directories... you can specify an alternative output directory.
In the generated assembly file symbols appear with an "_?" prefix, is this OK?
The "_?" is used as a prefix for functions using register parameters passing, opposed to functions using stack parameter passing which get a plain "_" prefix like variables and other C symbols.
The compiler gives an error on a variable name 'data'
For backwards compatibility the compiler supports language qualifiers not starting with an underscore (data, idat, xdat, rom etc.). Therefor any variable using this name is confused with the language qualifier. This backwards compatibility can be switched off by selecting Project | C Compiler options | Language extensions => 'Custom language extensions' then uncheck "Allow language extension without underscore".
I am running an application on my target using the ROM monitor, now it seems to hang during downloading.
The ROM monitor running on the target requires some resources, it is important not to use these resources in your application (for as long as it is required to run using the monitor). E.g. the serial port is used by the ROM monitor, and since it is used in interrupt mode the serial interrupt vector should not be overwritten by your application. Most ROM monitors use the standard 8051 serial vector located on addresses 0x23-0x25. As soon as your application overwrites these locations (generally during downloading) no more communication between debugger and ROM monitor is possible.
The 'cmon' directory of the installed products contains several option files (*.opt e.g. pkc505l.opt) containing the right options for each supported target board. An option file can be loaded under Project | Load Options...
I cannot change the internal RAM size anymore in EDE.
Since v6.0 EDE automatically sets the internal RAM size based on the choosen CPU. In previous versions the internal RAM size had to be set manually. If the shown size does not match the one for the selected CPU you can contact our support department (support.nl.nl). Also you can workaround it by choosing 'User specified CPU' and fill in the CPU name. For user specified CPUs you can choose the on-chip data RAM size in the Memory Tab.
Note: The internal RAM size is the size on the internal data bus. For the 8051 architecture the internal data size is limited to a maximum of 256 bytes. Currently there are many derivates offering more than 256 bytes of internal RAM. For all these derivates hold that all bytes above the first 256 are placed somewhere on the external data bus. The internal RAM size discussed here only refers to the amount of RAM on the internal data bus.
All my projects seem to contain a file with the name of the project followed by _cstart.asm.
Since v6.0r1 EDE by default generates the required startup code based on the options chosen. This generated file '<project>_cstart.asm' will be generated in the project directory and will also be added automatically to the project. For projects already using a different startup code file (probably 'cstart.asm') this may cause conflicts. In that case 2 options are available:
1. remove your own startup code file from the project. 2. switch off the automatic generation/addition of the '<project>_cstart.asm' file under: EDE | Processor Options | Startup.
I get a "overlapping XDATA 0-1" warning message from the linker
Uncheck the "reserve first byte of XDATA" checkbox in the Linker options/Memory TAB. This first byte in XDATA is reserved to prevent null-pointers.
My 8051 has 1k of internal RAM but I can select only up to 256 bytes of internal RAM.
The 8051 internal data area is restricted to a maximum of 256 bytes. If a derivate has more than 256 bytes internal RAM, everything above 256 is mapped (internally) to the external data bus, thus accessible through MOVX instructions. Using C you can access this memory using the _xdat qualifier, or by selecting the Large/Reentrant memory model.
I want to attach an interrupt function to a vector address, what interrupt number should I use.
The first actual interrupt is located on address 3, every following vector will be 8 bytes after its predecessor. This means the following relation holds:
interrupt number = (vector_address - 3)/8
vector-address interrupt-number.
3 = 0x03 0
11 = 0x0b 1
19 = 0x13 2
27 = 0x1b 3
35 = 0x23 4
43 = 0x2b 5
51 = 0x33 6
59 = 0x3b 7
67 = 0x43 8
75 = 0x4b 9
etc.
EXAMPLE: To define an interrupt function called on overflow of Timer 2 located on vector address 0x2b:
I want to be able to call a function from functions using different register banks, how can I do this?
In order to call a function from functions using different register banks it should be made register bank independent. By default the compiler generates register bank dependent code since that is faster and shorter than register bank independent code. In order to compile a single function register bank independently use the '_noregaddr' qualifier, e.g.
The 8051 I want to use has 768 bytes of auxiliary internal RAM, but I can only use a maximum of 256 bytes.
The auxiliary data page is a special page on the 8051 external data bus that can be addressed using the R0 or R1 register. This page is limited to 256 bytes. The rest of the auxiliary internal RAM can be accessed like standard external RAM.
When I build my program I get link51 warnings like: link51 W001: unresolved external symbol '__STOREFXD', module <modulename>
By default projects started in EDE do not use floating point since that requires an extra floating point stack. If you want to use floating point enable it in the 'EDE | C Compiler options | Float' Tab.
The 8051 derivate I want to use is not yet supported, can I still use it.
In general 8051 derivates only differ in the peripherals (Timers, Serial ports etc.), in the amount of internal RAM/ROM and in the ROM type (e.g. flash or OTP) they provide. The instruction set for 8051 derivates is always the same with a few rare exceptions (e.g. Dallas DS80C390 and Philips 51MX). Thus most 8051 derivates can and will be supported by our tool set. What is required is an SFR file containing all special function registers. The 'include' directory of the installed product contains all SFR files for the supported derivates (reg<cpu-name>.sfr). To add support for a new derivate you can do the following:
- define a unique name for the 8051 derivate, e.g. 'tsk80c51'
- create a sfr file using this name, reg<cpu-name>.sfr, e.g. regtsk80c51.sfr
- in 'EDE | Processor Options | Processor' tab select 'User specified' then fill in the cpu name => tsk80c51.
This ensures that each file in your project automatically includes the SFR file you just created. Or you can contact us.
I want to connect an external memory device to the external data bus, how do I set this up?
From C you can locate variables at an absolute address using the _at() keyword. Below an example for an external UART located at address 0xFD00.
In this example all addresses of the memory device are occupied, if this isn't the case then the linker may fill these gaps with other C variables which is mostly unwanted. To prevent this you should reserve the entire area occupied by the memory device under EDE | Linker Options | Memory Tab
EXAMPLE: Reserve XDATA memory: 0FD00H,0FD07H
How do I setup the memory layout for my project?
The amount of ram on the internal data bus should be automatically set based on the selected derivate. Regarding the external data and code bus the linker assumes all 64k is available, if you have less memory available you should reserve the area you -don't- want the linker to put any data/code. This can be done under EDE | Linker Options | Memory Tab.
EXAMPLE: suppose you have 32k of external data starting at offset 0x4000 and you have 16kb of code starting at 0, then you should use the following entries:
After you have build your project you should check the linker mapfile (<project-name>.l51) which shows the locations of all segments. No segments should be located at the reserved areas. Exception to this rule are absolute segments, these may be located in reserved areas, this is required to be able to use for example external memory devices.
I use the printf() routine but my program does not print floating point numbers. What is wrong?
The full ANSI-compliant printf() C library function causes a large code size overhead in small programs. We give you the choice to make a trade-off between functionality and small footprint by supplying three different formatter versions (do not confuse them with memory models):
| Formatter | Code size | Functionality |
|---|---|---|
| LARGE | 18 kb | complete ANSI functionality |
| MEDIUM | 6.5 kb | no floating point |
| SMALL | 3.5 kb | no width specifier, no period, no precision, no floating point |
The default formatter installed in the C library is the MEDIUM version, which will not print your floating point numbers. When another formatter is required, you must recompile the library modules _printf.c, _fprintf.c, _sprintf.c, _vprintf.c, _vfprintf.c, _vsprintf.c while defining macro "SMALL", "MEDIUM" or "LARGE" for selecting the right formatter. For detailed hints, see the C compiler User's Guide, section 3.4.
What is the difference between the const and _rom C-language keywords?
In the 8051 architecture, besides ROM there are several other memory spaces and most of them require special instructions to access. To designate in which memory space an object is to be defined, a 'storage specifier' keyword can be used in your C source code. The 8051 C-compiler recognizes the following storage specifiers: _data, _bdat, _idat, _pdat, _xdat, _rom.
The 8051 C-compiler treats the _rom storage type in a special way: Since the ROM memory space is physically read-only, it always implies the type qualifier const.
The ANSI-C standard states that the type qualifier const can be used to specify read-only objects. During compilation, it allows the compiler to issue an error message for a source line where an attempt is made to write to the object.
EXAMPLE:
However, a const object does not necessarily have to reside in a memory area which is physically read-only.Therefore, unlike the _rom storage specifier, the const type qualifier does not imply that the object is located in ROM memory space.
I get an "Address Space Overflow" error message. What did I do wrong?
This message tells you that you want to allocate more memory than available in the specified memory space. It is wise to have a closer look into the linker mapfile (carrying the .L51 extension). If the overflow concerns the 'DATA' memory space then here are some things to notice:
The default internal RAM size is 128 bytes. It might be that the derivative you are using has 256 bytes of RAM, but did not tell the linker/locator: From the command line perspective you should use the RAMSIZE() control. From EDE perspective, you should select:
CC51 v4.0 EDE > Linker Options > Memory > Size of internal RAM
CC51 v4.1 EDE > Processor Options > Memory
If the internal memory size was set correctly, then please note that DATA is the default memory space for all data in your application in case you selected the SMALL memory model. This means that you need to divert some of your data to external XDAT memory by explicitly defining/declaring it with the '_xdat' storage qualifier. A quick way of getting rid of an overflow is to switch to the LARGE memory model and rebuild. Default storage becomes external in XDAT memory space.
I get an "Unresolved external symbol: SMALL/LARGE" error message but I do not have such a variable in my application. What is it?
This message shows up when the linker finds that the memory model is not uniform throughout all modules of the project. This situation can occur if you change the memory model during project development. In order to pass on this change to all object files, please use the 'Rebuild' button rather than the 'Make' button in order to recompile all modules of your project.
The 8051 compiler supports mixed memory model applications. However, the Memory Model tab options in EDE can only be selected for the whole project, not for individual modules. Why?
The mixed memory model implementation still requires a default memory model to be selected for the application. The default memory model can be overruled for individual functions. By adding one of the keywords _small, _aux, _large or _reentrant to its prototype, you can specify a function to have a non-default memory model.
On compilation unit level, all C modules which make up the application must have the same default memory model. Therefore, the Memory Model tab options in EDE are greyed out for individual source modules.
I use the _getbit() and _putbit() built-in functions for accessing individual bits in a _bitbyte. I want to use a runtime variable to specify the bit-offset argument in these functions, but the compiler tells me to use a constant expression. Why?
The _getbit() and _putbit() built-in functions provide a way to utilize the 8051 bit-manipulation instructions in your C-program without having to insert pieces of inline assembly. The function arguments are mapped directly into 8051 instruction operands.
EXAMPLE:
| C-syntax | Generated 8051 assembly |
|---|---|
| _putbit(1,stat,7); | SETB _stat.7 |
| if (_getbit(stat,3)) | NB _stat.3,_label |
Due to the nature of the 8051 architecture, the bit-addressable memory areas can only be accessed through direct addressing, i.e. the address must be known at link/locate time. Hence, the same restrictions apply to the _getbit() and _putbit() functions.
I have defined a bit variable using the _atbit() attribute. When I declare this bit as an external variable in a different module, the linker complains about an unresolved reference. Why?
When you define a bit-variable using the _atbit attribute, you name a bit within an existing _bitbyte or _sfrbyte variable. You do not actually allocate a bit in the 8051 memory space, but merely give a bit in an existing bitaddressable object its individual name. A bit definition with _atbit() is similar to a preprocessor definition using #define. The compiler will not generate an object or reference to an object for it, and the linker/locator is unaware of its existence. Hence, you cannot declare it as an external variable.
The correct way to declare an _atbit() bit in your application is to include the complete definition in every module where it is used, preferably in a header file.
I have a piece of conditional code in my application for which the compiler generates no code at all! I also have a piece of conditional code that is executed unconditionally. Why?
The reason is often that the compiler assumes that the test condition yields invariantly true or false at runtime. In such case, the compiler will perform optimizations by removing the condition test and the never-to-be-executed branch of code.
EXAMPLE:
The compiler will generate an eternal loop here without code for testing the loop condition. This is a valid optimization, since the variable port_rdy is always zero at the test point, thus the condition is always true.
This may not be what you had in mind, since port_rdy might represent a memory-mapped peripheral register, or is a global variable that is written to by an interrupt service routine. In such case you must explicitly tell the compiler that the variable can be changed behind its back. By adding the volatile type qualifier to the variable definition/declaration, you force the compiler to actually perform the variable manipulations that you coded.
The following code will work as intended:
EXAMPLE:
How can I configure the compiler to allow calling functions using a different register bank?
This is not possible without disregarding the advantage of register bank switching: The 8051 instruction set lacks a MOV Rdst,Rsrc instruction. In order to move data between registers, a temporay static area or a direct address replacement for either of the two operands must be used. The latter one is preferred because it doesn't require additional MOV instructions to store/retrieve the data in the static areas. Following type of instructions are used:
(1) MOV Rdst,ARsrc
(2) MOV ARdst,Rsrc
ARsrc and ARdst are assembler labels that refer to the corresponding register in the current registerbank. The assembler will replace those labels with the appropriate direct address based on the register bank the module was compiled for. When passing parameters from one function to another there may be circumstances when the compiler will have to use MOV instructions with this addressing mode. Therefore, these two functions must have the same using, so that the registerbank used by the caller functions is the same bank as used by the callee. To explain this please take a look at the following example:
EXAMPLE:
R7 will be R7 of the current register bank. So only if AR7 equals the R7 of the current register bank the parameter is passed correctly. As AR7 is assembled for the address indicated by _using(bank) the conclusion must be that caller and callee require the same using. When you require a function to be called both from the main program and from an ISR, the only thing you need to do is to omit the _using() keyword for the interrupt service routine definitions. This will result in it being compiled for the default register bank and as this is the same one as main is running on the register banks match and the function may be shared. The following example demonstrates this:
EXAMPLE:
Note that this setup requires the current register bank to be saved which is taken care of by the compiler. Also note that 'x' must be declared reentrant because only this memory model allows reentrancy.
I created a C program with some interrupt routines. It does not work and I noticed that the interrupt routines were never entered. What is wrong?
Inspect the linker map file (.L51) and make sure that absolute sections are mapped at the right interrupt vector addresses. If this is wrong then you probably filled in the vector address as the _interrupt qualifier argument. However, you need to fill in the interrupt number, not the vector address. Through the use of a macro, you can always use the vector address instead of the interrupt number:
EXAMPLE:
The compiler manual states that option '-gr' emits two additional NOP instructions for every C line and that it should be enabled when debugging with the ROM monitor. Why?
Please take a look at the following example:
Suppose the above example has been generated by the compiler and there is a breakpoint at address 08000h. This breakpoint is actually a long call (LCALL) to the ROM-monitor and it is patched over the original JNZ-instruction. The problem is that JNZ is a two-byte instruction and that LCALL is a three-byte instruction. The first byte of the next instruction will thus be overwritten.
If the program flow continues at JNZ then there is no problem because the code is restored when resuming execution. However if somewhere during execution there is a jump to label _b (in the example at address 0800Ah) then the next instruction fetched after the jump will NOT be the MOV instruction but the third byte of the LCALL/breakpoint. At this point the program flow has become unpredictable.
By appending two NOP (No OPeration) instructions to every C-source line of generated assembly code, the minimum code size for each line of C is extended from one byte to three bytes. The insertion of these dummy instructions allows the debugger to safely patch an LCALL instruction without overwriting the subsequent program code.