[转载]c c++ compiler 编译参考手册
信息来源:hk20.comCompactRISC
C/C++ Compiler
Reference Manual
Part Number: 424521772-002
January 2004
ΤΜ
ii
REVISION RECORD
VERSION RELEASE DATE SUMMARY OF CHANGES
0.6 August 1995 First release.
0.7 January 1996 Minor changes and corrections.
1.0 August 1996 CR16A Product Version.
CR32A Beta Version.
1.1 February 1997 Minor modifications and corrections.
2.a September 1997 Alpha release for CR16B.
2.0 January 1998 Beta release.
2.1 August 1998 Product release.
2.2 September 1999 Minor modifications and corrections.
3.0 December 2000 CR16C beta version.
3.1 February 2002 Product release for CR16C/CR16B.
4.0 June 2003 Alpha version for CR16C/CPlus
Includes upgrade to GCC 3.2.3 and
support for C++
4.1 January 2004 Beta release for version 4.1
iii
PREFACE
Welcome to the CompactRISC C Compiler. The CompactRISC C Compiler generates highquality
code for processors using the CompactRISC architecture. It is derived from the
well-known GNU C/C++ Compiler from the Free Software Foundation. It includes enhancements,
such as intrinsic functions, source code register control, and full structure
layout control, specifically for the development of embedded code.
The information contained in this manual is for reference only and is subject to change
without notice.
No part of this document may be reproduced in any form or by any means without the
prior written consent of National Semiconductor Corporation.
CompactRISC is a trademark of National Semiconductor Corporation.
National Semiconductor is a registered trademark of National Semiconductor Corporation.
Dinkum and Dinkumware are registered trademarks of Dinkumware Ltd.
CONTENTS-iv CompactRISC C/C++ Compiler Reference Manual
CONTENTS
Chapter 1 OVERVIEW
1.1 INTRODUCTION ................................................................................................. 1-1
1.2 INTENDED AUDIENCE....................................................................................... 1-2
Chapter 2 INVOCATION AND OPERATION
2.1 INVOKING THE COMPILER ............................................................................... 2-1
2.2 COMPILER STRUCTURE AND FLOW............................................................... 2-1
2.3 COMMAND-LINE OPTIONS................................................................................ 2-1
2.3.1 The Invocation Syntax ............................................................................ 2-1
2.3.2 Driver Name ........................................................................................... 2-2
2.3.3 Filename Conventions ............................................................................ 2-2
2.3.4 Driver and Compiler options ................................................................... 2-3
2.4 ENVIRONMENT VARIABLES ........................................................................... 2-12
2.5 PREDEFINED CPP MACROS........................................................................... 2-13
Chapter 3 EXTENSIONS TO THE C/C++ LANGUAGES
3.1 INTRODUCTION ................................................................................................. 3-1
3.2 PRAGMAS........................................................................................................... 3-1
3.2.1 Interrupt/Trap Pragma ............................................................................ 3-1
3.2.2 Section pragma ...................................................................................... 3-3
3.3 ASM STATEMENTS AND INTRINSIC FUNCTIONS............................................ 3-5
3.4 VARIABLES IN SPECIFIED REGISTERS........................................................... 3-6
3.5 INLINE FUNCTIONS ........................................................................................... 3-8
3.6 DOUBLE SLASH (‘//’) COMMENTS .................................................................... 3-9
3.7 NON-RETURNING ROUTINES........................................................................... 3-9
Chapter 4 RUN-TIME LIBRARIES
4.1 INTRODUCTION ................................................................................................. 4-1
4.1.1 The libc Library ....................................................................................... 4-1
4.1.2 The libstdc++ Library .............................................................................. 4-2
4.1.3 The libhfp and libd Libraries ................................................................... 4-2
4.1.4 The libstart Library .................................................................................. 4-2
CompactRISC C/C++ Compiler Reference Manual CONTENTS-v
4.1.5 Using the Libraries ................................................................................. 4-3
4.1.6 Initialization Requirements ..................................................................... 4-4
4.2 STANDARD ANSI C LIBRARY FUNCTIONS ..................................................... 4-5
4.3 LOW-LEVEL I/O AND TIME FUNCTIONS.......................................................... 4-6
4.3.1 Virtual I/O ............................................................................................... 4-7
4.3.2 Environment ......................................................................................... 4-11
4.3.3 Time ..................................................................................................... 4-11
4.3.4 Program Termination ........................................................................... 4-12
4.4 DIVISION EMULATION..................................................................................... 4-12
4.5 FLOATING-POINT EMULATION ...................................................................... 4-13
Chapter 5 STANDARD CALLING CONVENTION
5.1 CALLING CONVENTION.................................................................................... 5-1
5.1.1 Calling a Subroutine ............................................................................... 5-1
5.1.2 Returning from a Subroutine .................................................................. 5-2
5.1.3 Passing Parameters to a Subroutine ..................................................... 5-2
5.1.4 Parameter Passing Algorithm ................................................................ 5-2
5.1.5 Returning a Value .................................................................................. 5-3
5.1.6 Scratch and Non-scratch Registers ....................................................... 5-4
5.1.7 Program Stack ....................................................................................... 5-4
5.2 ALIGNMENT OF VARIABLES............................................................................. 5-5
Chapter 6 GUIDELINES FOR USING THE COMPILER
6.1 INTRODUCTION................................................................................................. 6-1
6.2 INITIALIZATION ISSUES.................................................................................... 6-1
6.2.1 The Start-up Routine .............................................................................. 6-1
6.2.2 Initialized Variables ................................................................................ 6-2
6.2.3 Global Constructor/Destructor Invocation .............................................. 6-4
6.3 LINKER INPUT SECTIONS GENERATED BY THE COMPILER ....................... 6-4
6.4 WORKING WITH INTERRUPTS........................................................................ 6-5
6.4.1 Writing Trap and Interrupt Handlers in C/C++ ........................................ 6-5
6.4.2 Enabling and Disabling Interrupts - Semaphores ................................... 6-6
6.4.3 The Interrupt Dispatch Table .................................................................. 6-7
6.5 CODE ADDRESSES........................................................................................... 6-9
6.5.1 Writing in C/C++ ..................................................................................... 6-9
6.5.2 Writing in Assembly .............................................................................. 6-10
CONTENTS-vi CompactRISC C/C++ Compiler Reference Manual
Chapter 7 IMPLEMENTATION-DEFINED BEHAVIOR
7.1 TRANSLATION.................................................................................................... 7-1
7.2 ENVIRONMENT .................................................................................................. 7-1
7.3 IDENTIFIERS....................................................................................................... 7-1
7.4 CHARACTERS .................................................................................................... 7-1
7.5 INTEGERS........................................................................................................... 7-2
7.6 FLOATING POINT............................................................................................... 7-3
7.7 ARRAYS AND POINTERS .................................................................................. 7-3
7.8 REGISTERS ........................................................................................................ 7-4
7.9 STRUCTURES, UNIONS, ENUMERATIONS, AND BIT-FIELDS ....................... 7-4
7.10 QUALIFIERS........................................................................................................ 7-4
7.11 DECLARATORS.................................................................................................. 7-4
7.12 STATEMENTS..................................................................................................... 7-4
7.13 PREPROCESSING DIRECTIVES....................................................................... 7-5
7.14 LIBRARY FUNCTIONS........................................................................................ 7-5
CompactRISC C/C++ Compiler Reference Manual OVERVIEW 1-1
Chapter 1
OVERVIEW
1.1 INTRODUCTION
This manual describes National Semiconductor’s CompactRISC C/C++
Optimizing Compiler for its family of CompactRISC processors.
The compiler is based on the stable, proven GNU Compiler Collection
(GCC). The compiler is available as a cross compiler running on
Microsoft Windows operating systems.
The compiler supports the C language as described by the ANSI C standard.
The compiler supports a subset of C++ that fits embedded programming
but includes more than EC++. The C++ compiler supports all C++ features,
except Run-Time Type Identification (RTTI) and Exception handling.
The run-time libraries are based on Dinkumware’s Abridged
Library, which consists of the EC++ library plus an exception-safe Standard
Template Library (STL).
In addition, the CompactRISC Optimizing Compiler includes important
extensions and options for programming embedded applications, such as:
. Interrupt/trap handling in C
. Support for user defined sections
. Intrinsic functions and an extended ___asm__ statement
. Global variables may be placed in registers for the entire program.
. Inline functions
. Optimizations can be tuned to either improve speed or save space
. Allows packing of structure members.
This manual is organized as follows:
Chapter 1 Overview (this chapter), briefly describes the contents of each chapter,
and defines the intended audience.
Chapter 2 Invocation and Operation, describes the compiler structure, its command
line options, and the environment variables that you can use to
control its functionality.
CompactRISC C/C++ Compiler Reference Manual OVERVIEW 1-2
Chapter 3 Extensions to the C/C++ Languages, describes several language features
provided by the compiler, which are not found in the ANSI C/ISO C++
standards. These features include pragma directives, using variables in
specified registers, assembler instructions, and intrinsic functions.
Chapter 4 Run-time Libraries, describes the various libraries that provide run-time
support for the CompactRISC C/C++ Compiler.
Chapter 5 Standard Calling Conventions, describes the standard routine-calling
conventions. These conventions enable routines in one module to communicate
with routines in other modules, even if they are written in different
programming languages.
Chapter 6 Guidelines for Using the Compiler, provides some guidelines for using the
compiler to port programs, using optimization options to maximum effect,
and how to avoid some common programming errors.
Chapter 7 Implementation-Defined Behavior, defines the behavior of the CompactRISC
C/C++ Compiler for cases which, according to the ANSI C/ISO C++
standards, are implementation-defined.
Appendix A Compiler Limitations, lists some compiler limitations of which you
should be aware.
1.2 INTENDED AUDIENCE
This manual is for experienced C/C++ programmers. The information
provided covers compiler options, extensions to the standard C/C++
programming languages, and implementation issues.
Less experienced programmers should use this manual in conjunction
with a standard C/C++ compiler manual, such as those listed below:
. C Standard (ANS/ISO 9899-1990).
. Harbison, Samuel and Steele, Guy. C, A Reference Manual, 2nd. ed.,
Englewood Cliffs, New Jersey: Prentice-Hall, Inc., 1984.
. Kernighan, Brian and Ritchie, Dennis. The C Programming Language,
2nd ed., Englewood Cliffs, New Jersey: Prentice-Hall, Inc.,
1989.
. C++ Standard (ISO/IEC 14882-1998).
. Embedded C++ Specification (Version WP-AM-003, 13, Oct. 1999 by
the Embedded C++ Technical Committee).
. Stroustrup, Bjarne, The C++ Programming Language, Special Edition,
Addison-Wesley, 2000
CompactRISC C/C++ Compiler Reference Manual INVOCATION AND OPERATION 2-1
Chapter 2
INVOCATION AND OPERATION
2.1 INVOKING THE COMPILER
This chapter describes the compiler structure, its command line options,
and the environment variables that control its functionality.
2.2 COMPILER STRUCTURE AND FLOW
The driver is a program that parses and interprets the command line, and
then calls each of the other programs in sequence, depending on its input
programs and the command line options.
Compilation may involve up to four stages, always in the following order:
1. Preprocessing - This expands macros (defined by #define), includes
header files (#include), and processes conditional code (e.g., code
enclosed by an #ifdef statement)
2. Compilation proper - Translates and optimizes the preprocessed C
program to a CompactRISC assembly language program.
3. Assembly - Converts the assembly program to a relocatable object
file. The object file contains CompactRISC machine code, program
data and, in addition, symbolic information, relocation information
and additional useful information.
4. Linking - Links the object file with default run-time libraries to generate
an executable object file. The default executable object file
name is a.out.
2.3 COMMAND-LINE OPTIONS
2.3.1 The Invocation Syntax
The invocation syntax of the compiler is:
driver [ {option | filename | @argfile} ]...
CompactRISC C/C++ Compiler Reference Manual INVOCATION AND OPERATION 2-2
The driver accepts one or more file arguments and compilation options.
Compilation options start with a dash (-). In addition, the compiler can
take all, or parts, of the arguments from an argument file. An argument file
is denoted by an at-sign (@) followed by a file name.
The driver generates an executable file, object file(s), assembly file(s), or
pre-processed file(s), according to the options specified.
2.3.2 Driver Name
Use crcc for C programs. Use cr++ for C++ programs. The benefit of using
cr++ with C++ programs is that it sets the language to C++, no matter
what the suffix of the input file. Moreover, it links with the C++ library.
In this document all the options can be used with either driver, that is,
with the crcc driver or the cr++ driver.
2.3.3 Filename Conventions
The driver identifies files according to their extension.
Table 2-1. Filename Conventions
File Name Extension File Type
.c C source file
.i Preprocessed C source file
.cc, .cpp, .c++,
.cxx, .cp
C++ source file
.ii Preprocessed C++ source file
.sp Assembly source file with preprocessor directives
.s Assembly source file
.o Object code file
.a Library archive file
CompactRISC C/C++ Compiler Reference Manual INVOCATION AND OPERATION 2-3
2.3.4 Driver and Compiler options
You can specify the following compilation options in the invocation line.
Options Controlling the Kind of Output
-E (PREPROCESS ONLY)
Terminates the compilation after preprocessing. Its output is
sent to the standard output, stdout. Use this option to inspect
your C code after it has been preprocessed by the C preprocessor
e.g., to see how your macros were expanded, or which parts
of the code are included.
-S (COMPILE ONLY)
Directs the driver to terminate the compilation process before
assembly. The assembly output is one or more files, whose
names are those of the source, with .s substituted for the original
extension. Use this option to inspect the assembly language
program generated from your C/C++ program. For example:
crcc -S sample.c
cr++ -S utils.cpp
creates the files sample.s and utils.s. No executable or object
file is created.
-c (COMPILE AND ASSEMBLE ONLY)
Directs the driver to perform the compilation process up to, but
not including, linking. Output is one or more object files whose
names, by default, end with .o. This option is useful when you
want to invoke the linker independently at a later stage. For example:
crcc -c prog1.c
> crcc -c prog2.c
> crld prog1.o prog2.o -o prog -lstart -lc
-o out (RENAME THE OUTPUT FILE)
Redirects the output file from the compilation process to a file
named out. For example:
crcc sample.c utils.c -o sample
generates the executable file sample from the two source files.
crcc -S sample.c -o new_sample.s
generates the assembly file new_sample.s.
CompactRISC C/C++ Compiler Reference Manual INVOCATION AND OPERATION 2-4
Options Controlling C Dialect
-ansi (ACCEPT ANSI C PROGRAMS ONLY)
In C mode, supports all ISO C90 programs, that is, turns off the
extensions that are incompatible with ISO C90. For example, it
disables recoginition of C++ style ‘//’ comments, as well as the
inline keyword. In C++ mode, remove extensions that conflict
with ISO C++. In general, it enables the ISO trigraph feature,
which is, by default, disabled. The macro __STRICT_ANSI__ is
predefined when the -ansi option is used.
Warning and Error Options
-fsyntax-only
(ERROR CHECKING ONLY)
Check the code for syntax errors but do not generate code.
-pedantic
(WARN ABOUT NON-ANSI)
Issues all the warnings demanded by strict ISO C and ISO C++;
rejects all programs that use forbidden extensions and do not
follow ISO C and ISO C++.
-Wimplicit
(WARN ABOUT IMPLICIT DECLARATIONS)
Issues a warning message when a declaration does not specify a
type, or when a function is called before being declared. In the
latter case, the function prototype is implicitly set by the compiler
according to the function call.
-Wuninitialized
(WARN ABOUT UNINITIALIZED LOCAL VARIABLES)
Issues a warning message when a local (automatic) variable is
used without first being initialized. These warnings do not occur
for variables declared volatile, variables whose address is taken,
structures, unions and arrays. The warnings are possible only
when compiling with an optimization option. The compiler does
not identify initializations that occur within an __asm__ statement.
-Wreturn-type (C language only)
(WARN WHENEVER A FUNCTION’S RETURN-TYPE DEFUALTS
TO INT)
Issues a warning message in the following cases:
- A function is defined with a return-type that defaults to int.
- A return statement with no return-value in a function whose
return-type is not void.
CompactRISC C/C++ Compiler Reference Manual INVOCATION AND OPERATION 2-5
-Wswitch
(WARN ABOUT ENUMERATED SWITCHES, WITH NO DEFAULT,
MISSING A CASE)
Issues a warning message when a switch statement has an index
of enumeral type and either of the following occurs:
- The switch lacks a case label for one or more of the named
codes of that enumeration. The presense of a default
label prevents this warning.
- The switch has case labels outside the enumeration range.
-Wunused
(WARN ABOUT UNUSED FUNCTION, VARIABLE, LABEL OR
STATEMENT’S RESULT)
Issues a warning message in the following cases:
- A static function is declared but not defined, or when a static
function is unused.
- A local variable or non-constant static variable is unused aside
from its declaration.
- A statement computes a result that is explicitly not used.
- A label is declared but not used.
-Wunknown-pragmas
(WARN ABOUT UNRECOGNIZED PRAGMAS)
Issues a warning message when a #pragma directive is encountered
which is not understood by this compiler.
-Wall
(ALL THE ABOVE)
All the above -W options combined and some more.
-Wconversion (
(WARN ABOUT POSSIBLY CONFUSING TYPE CONVERSIONS)
Issues a warning message when a prototype causes a type conversion
that is different from what would happen to the same
argument in the absence of a prototype.
-Wstrict-prototypes (C language only)
(WARN ABOUT NON-PROTOTYPED FUNCTION DECLS)
Issues a warning when a function is declared or defined without
specifying the argument types.
-Wredundant-decls
(WARN ABOUT MULTIPLE DECLATRATIONS OF THE SAME
OBJECT)
Issues a warning when anything is declared more than once in
the same scope.
CompactRISC C/C++ Compiler Reference Manual INVOCATION AND OPERATION 2-6
-Winline
(WARN WHEN AN INLINE FUNCTION CANNOT BE INLINED)
Issues a warning when a function that was declared as inline
can not be inlined.
-w (NO WARNING DIAGNOSTICS)
Suppresses all warning diagnostics, which the compiler normally
prints if there are inconsistencies in the input program.
Debugging Options
-g (COMPILE FOR DEBUGGING)
Produces symbolic debugging information. This option enables
debugging the code at source level. Use this option when you
want to debug your program. This option is normally used during
the whole development phase of the application.
Optimization Options
-O0 (DON’T OPTIMIZE)
Do not optimize. Note that the parser and the code generator
perform some local optimizations, even when optimization is not
enabled.This is the default.
-O1
or
-O (OPTIMIZE)
The compiler tries to reduce code size and execution time, without
performing any optimizations that take a great deal of compilation
time.
-O2 (OPTIMIZE EVEN MORE)
Performs nearly all supported optimizations that do not involve
a space-speed tradeoff. Does not perform loop unrolling or function
inlining. As compared to -O1 Increases compilation time
and the performance of the generated code.
-O3 (OPTIMIZE YET MORE)
Turns on all optimizations specified by -O2 and also turns on
the -finline-functions option.
-Os (OPTIMIZE FOR SIZE)
Enables all -O2 optimizations that do not typically increase code
size. Also performs further optimizations designed to reduce
code size.
CompactRISC C/C++ Compiler Reference Manual INVOCATION AND OPERATION 2-7
-funroll-loops
(LOOP UNROLLING OPTIMIAZATION)
Performs loop unrolling optimization. Loop unrolling duplicates
the body of a loop. This reduces the number of times that the
loop control code is executed. Loop unrolling is performed only
for loops whose number of iterations can be determined at compile
time, or upon entry to the loop. This optimization makes
code larger and sometimes makes the code faster. This option is
relevant only when optimization is turned on.
For example: crcc -O2 -funroll-loops filename.c
-finline-functions
(INLINE FUNCTIONS)
Integrates all simple functions into their callers. The compiler
heuristically decides which functions are simple enough to be
worth integrating in this way. If all calls to a given function are
integrated, and the function is declared static, then the function
is normally not output as assembly code in its own right. This
option is relevant only when optimization is turned on. It is enabled
automatically when -O3 option is specified.
-fkeep-inline-functions
(KEEP BODY OF INLINE FUNCTIONS)
Outputs a separate, run-time callable, version of a given function,
even if all calls to the function are integrated, and the
function is declared static. This option does not affect extern
inline functions. This option is relevant only when optimization
is turned on.
-fschedule-insns2
(REORDER INSTRUCTIONS)
Reorders instructions in order to eliminate execution stall due
to required data being unavailable. Invoking this option may improve
speed performance. This option is relevant only when
compiling for CR16CPlus core which uses a Load Store Unit
(LSU) and only when -O2 or -O3 is specified. For CR16CPlus this
option is enabled by default when compiling for speed.
-fshort-enums
(OPTIMIZE SIZE OF ENUMERATION TYPES)
Allocates to an enum type only as many bytes as it needs for the
declared range of possible values. By default the size of an enumeration
type is equal to the size of an integer.
-mmac
(GENERATE MAC INSTRUCTIONS)
Generate multiply accumulate (macsw and macuw) instructions.
The accumulations that are performed as a result do not affect
the flag bit PSR.F and the carry bit PSR.C.
CompactRISC C/C++ Compiler Reference Manual INVOCATION AND OPERATION 2-8
Target Machine Options - Core
The flags specifying the target architecture are mutually exclusive.
-mcr16c
(GENERATE CODE FOR CR16C)
Instructs the compiler to generate code for the CR16C core, assuming
the value of bit SR in CFG is 0. This is the default.
-mcr16cplus
(GENERATE CODE FOR CR16CPlus)
Instructs the compiler to generate code for the CR16CPlus core,
assuming the value of bit SR in CFG is 0.
Target Machine Options - Data Models
It is possible to link together files that were compiled with different data
model compilation options. However, this should be done with care. It is
always considered good practice that the interface between two files is only
through function calls and not through global variables. This is especially
important for an interface between two files, each being compiled with a
different data model option.
-mdata-model=standard
(STANDARD DATA MODEL)
This is the default model. Instructs the compiler to generate code
for the standard data model. This model can be used when all the
global and static variables in the module can be within the whole
16M byte address space. Note that the data address range of
CR16C is 16M, but in CR16CPlus the data address range is 4G.
Therefore, in CR16C this model can always be used.
-mdata-model=near
(NEAR DATA MODEL)
Instructs the compiler to generate code for the near data model.
This model can be used when all the global and static variables
in the module can be within the 1M-64K byte address space.
Using the near data model causes the compiler to generate more
optimized code.
Note that it is impossible to compile your whole application with
the near data model since in the CR16C/CPlus architectures
the address range (16M-64K) to 16M is reserved for I/O devices
and peripherals. Therefore, the code that defines, reads or
writes to the I/O devices and peripherals must be compiled with
the standard data model.
To provide better control over memory allocation at link time
the compiler makes sure the variables are put in the near sections
by the assembler and linker. It is the user’s responsibility
to make sure the linker script file defines the near sections and
CompactRISC C/C++ Compiler Reference Manual INVOCATION AND OPERATION 2-9
allocates them to the near address range. Note that even if there
are no const variables in your program, the compiler might
direct data into the read-only data sections. In some of these
cases, for example, strings, the compiler puts this data in .nrdat
section and in some cases, for example, switch tables, the compiler
puts them in .rdata section. Therefore, make sure your
linker script file includes a definition for all these sections. For
more details on sections see Section 6.3 “LINKER INPUT SECTIONS
GENERATED BY THE COMPILER”.
-mdata-model=far (CR16CPlus only)
(FAR DATA MODEL)
Instructs the compiler to generate code for the far data model.
This model is only valid when compiling for the CR16CPlus
architecture. Use this model when all the global and static variables
in the module might be within the whole 4G byte address
space of CR16CPlus architecture.
To provide better control over memory allocation at link time
the compiler makes sure these variables are put in the far sections
by the assembler and linker. It is the user’s responsibility
to make sure the linker script file defines the far sections and
allocates them to the far address range. Note that even if there
are no const variables in your program, the compiler might
direct data into the read-only data sections. In some of these
cases, for example, strings, the compiler puts this data in .frdat
section and in some cases, for example, switch tables, the compiler
puts them in .rdata section. Therefore, make sure your
linker script file includes a definition for all these sections. For
more details on sections refer to Section 6.3 “LINKER INPUT
SECTIONS GENERATED BY THE COMPILER”.
Code Generation Conventions Options
-ffixed-REG
(DO NOT USE A CERTAIN REGISTER)
Treats the register REG as a fixed register; generated code
should never refer to it. REG must not be used for passing parameters,
or returning a value from a routine. Moreover, REG
should not be any scratch register.
Example:
crcc -ffixed-r8 -c prog.c
instructs the compiler not to use register r8 when generating
assembly code for file prog.c,
-fpack-struct
(PACK STRUCTURE MEMBERS)
Packs all structure members together without holes.
CompactRISC C/C++ Compiler Reference Manual INVOCATION AND OPERATION 2-10
Note For correct execution of your program, compile all its compilation
units (i.e., all the C/C++ files) with the same packing shceme.
Preprocessor Options
-C (LEAVE COMMENTS IN)
Prevents the preprocessor from removing the comments from its
output. This option is useful when the preprocessor’s output
should be examined. It can only be used in conjunction with the
-E option.
-Dsymbol
(DEFINE PREPROCESSOR SYMBOL)
Predefine name as a macro, with definition 1.
-Dsymbol=defintion
(DEFINE PREPROCESSOR SYMBOL AS ...)
Predefine symbol as a macro, with defintion defintion. There
are no restrictions on the contents of defintion, but if you are
invoking the preprocessor from a shell-like program you may
need to use the shell’s quoting syntax to protect special characters
such as spaces. -D and -U options are processed in the order
they are given on the command line.
-Usymbol
(UNDEFINE PREPROCESSOR SYMBOL)
Cancel any previous definition of symbol, either built in or provided
with a -D option.
-Idir (SPECIFY DIRECTORY FOR INCLUDED FILES)
Instructs the preprocessor to use the specified directory as the
default directory for included files. Include files that are called
using double quotes, for example:
#include “filename”
are sought first in the directory of the compiled file, then in the
directories specified by -I, and finally in directories on the standard
list:
CRDIR\include\
CRDIR\lib\cr16c\3.3.2\include\
CRDIR\include\c++\3.3.2
where CRDIR is the root directory of the CompactRISC Development
Toolset. The last path is being scanned only when cr++ is
used.
If you explicitly name the file to be included using the complete
path, for example:
#include ”\a\mydir\filename”
CompactRISC C/C++ Compiler Reference Manual INVOCATION AND OPERATION 2-11
the named file is sought directly.
If angle brackets are used instead of double quotes, for example:
#include <filename>
the file is sought first in the directories specified by -I, and
then in the directories on the standard list.
-M (RUN THE PREPROCESSOR ONLY, GENERATE MAKEFILE DEPENDENCIES)
Runs the macro preprocessor on the named C programs, in order
to generate makefile dependencies. Sends the result to the
standard output, stdout.-M implies -E.
For example:
crcc -M test1.c test2.c > new.mak
runs the preprocessor on two C programs in the current directory
and generates all makefile dependencies for them. These
dependencies are then redirected to the file new.mak.
Linker Options
-lname (INCLUDE THE SPECIFIED LIBRARY)
Specifies a program library. By default, the linker looks for the library
libname.a in CRDIR\libmodel_OPT (where CRDIR is the
root directory of the CompactRISC development toolset, model is
the core architecture/model for which code is generated and OPT
is O for libraries optmized for speed and Os for libraries optimized
for space. O is the default), and links it with the program.
-KFemulation
(INCLUDE THE FLOATING-POINT EMULATION LIBRARY)
This option instructs the driver to include the floating point emulation
library (libhfp) as one of the linker inputs. By default, the
driver does not include this library. This option is necessary if the
program contains floating-point operations.
-Ldir (SPECIFY THE LIBRARY DIRECTYORY)
Defines the directory in which the linker first searches libraries
specified with the -l invocation option.
The linker searches libraries specified by the -l invocation
option first in dir, and then in the default library locations.
(See the CompactRISC Toolset - Object Tools Reference Manual).
CompactRISC C/C++ Compiler Reference Manual INVOCATION AND OPERATION 2-12
General Options
-v (VERBOSE MODE)
Prints (on standard error) the commands executed to run the
stages of compilation. Also prints the version and revision numbers
of the compiler driver program.
-### (SHOW BUT DO NOT EXECUTE)
Prints (on standard error) the commands executed to run the stages
of compilation, without actually executing the commands.
--help (SHOW HELP)
Prints (on standard output) a description of the command line options.
It is possible to pass any option to the various phases of the compiler.
-Wphase,options
(PASS OPTIONS TO COMPILATION PHASE phase)
Passes options to the C preprocessor (phase = p), the assembler
(phase = a), or the linker (phase = l). options must not contain
embedded spaces, unless quoted. An option is passed as one argument
whether it contains spaces or not. To pass multiple arguments,
use commas.
For example, the command:
crcc -Wl,-T,linker.def
instructs the linker to use the file linker.def as the linker
script file.
2.4 ENVIRONMENT VARIABLES
The following environment variables are used by the driver. The installation
procedure of the CompactRISC Toolset initializes these variables.
TMPDIR Defines the location at which temporary files are created in the
compilation process. If TMPDIR is not defined, the compiler looks
for TMP, and then for TEMP. If none of the above exist, the default
location for temporary files is the current directory.
CRDIR Must be set to the directory where the CompactRISC Toolset is
installed. .
CompactRISC C/C++ Compiler Reference Manual INVOCATION AND OPERATION 2-13
2.5 PREDEFINED CPP MACROS
The following preprocessor macros are predefined by the CompactRISC
compiler as if they were specified by the -D option, or with the #define
directive:
Macro Comments
__GNUC__=3 Always defined
__GNUC_MINOR__=3 Always defined
__GNUC_PATCHLEVEL__=2 Always defined
__OPTIMIZE__ Defined when -Oi is specified (i > 0)
__OPTIMIZE_SIZE__ Defined when -Os is specified
__STDC__ Always defined
__STRICT_ANSI__ Defined when -ansi is specified
__NSC_CRCC__ Always defined
__CRCC_VERSION__=41 Always defined
__CR__ Always defined
__CR16__ Always defined
__CR16C__ Always defined
__CR16CSTD__ Defined when -mcr16c is specified or implied
__CR16CP__ Defined when -mcr16cplus is specified
__DATA_NEAR__ Defined when -mdata-model=near is specified
__DATA_FAR__ Defined when -mdata-model=far is specified
_ABRCPP Use Abridged C++ dialect - Always defined
_NO_NS No use of namespaces - Always defined
_NO_MT No use of thread synchronization - Always defined
_NO_EX No use of exception handling - Always defined
__NO_LONG_LONG Always defined
__embedded_cplusplus Use embedded C++ dialect - Always defined
CompactRISC C/C++ Compiler Reference Manual EXTENSIONS TO THE C/C++ LANGUAGES 3-1
Chapter 3
EXTENSIONS TO THE C/C++ LANGUAGES
3.1 INTRODUCTION
The CompactRISC C/C++ compiler provides several language features
not found in the C or C++ standards. The goal of these extensions is to
enable better programming in an embedded environment.
3.2 PRAGMAS
ANSI C defines a #pragma directive which is the universal method for
extending the space of directives.
The CompactRISC Compiler uses the #pragma directive in the following
cases:
. #pragma interrupt
. #pragma trap
. #pragma section
3.2.1 Interrupt/Trap Pragma
The CompactRISC C/C++ Compiler allows trap and interrupt handlers
to be developed entirely in the C/C++ language. You do not need to
write any part of the handler in assembly. To develop your trap/interrupt
handler in C/C++, you write it as a normal C/C++ function of type
void with no arguments. The only special action you must take is to
use the #pragma directive to inform the compiler that this C/C++ function
is an interrupt or trap handler.
Syntax for interrupts:
#pragma interrupt(function_name)
Syntax for traps:
#pragma trap (function_name)
function_name is the name of the function to be marked as an interrupt/
trap handler.
CompactRISC C/C++ Compiler Reference Manual EXTENSIONS TO THE C/C++ LANGUAGES 3-2
The #pragma directive must appear before any declaration or definition
of the function.
Example extern volatile int time;
#pragma interrupt (clock_int)
void clock_int()
{
time++;
}
The function clock_int is declared as an interrupt handler.
When the compiler generates code for a function which is declared as
an interrupt, or trap handler, it does the following:
. Uses the retx (return from exception) instruction for returning
from the handler.
This instruction restores the pc and psr registers from the interrupt
stack (pointed to by the isp register).
. Saves all the scratch and non-scratch registers used by the function
on the stack. Normally a function can use scratch registers
freely without first saving them (see Chapter 5). However, since a
handler function occurs asynchronously it must also save scratch
registers.
If this function calls another function, all the scratch registers are
saved, since the caller does not know which scratch registers are
modified by the called function. For further details about the standard
calling convention, see Chapter 5.
The following is the assembly code generated by the CompactRISC
C/C++ Compiler for the previous example.
_clock_int:
push $1,r0
loadw _time,r0
addw $1,r0
storw r0,_time
pop $1,r0
retx
Using
interrupt/trap
handlers
It is your responsibility to install the address of the interrupt/trap handler
in the proper entry of the interrupt dispatch table (see Chapter 6
for more details).
You may not call an interrupt/trap handler directly from the C code.
This is because the instructions for returning from the interrupt/trap
routine, and those for returning from a regular routine, are different.
CompactRISC C/C++ Compiler Reference Manual EXTENSIONS TO THE C/C++ LANGUAGES 3-3
Note that although trap and interrupt pragma have the same effect, for
future compatibility we recommend that you use different pragma for
trap and interrupt routines.
3.2.2 Section pragma
Normally, the compiler places the objects it generates in sections like
.data and .rdata (for more details see Section 6.3). There are two
ways to place variables in specific user-defined sections:
. Section pragma
. Section attribute
This extension is especially useful when there are special memory areas
in which you would like to place specific variables.
Section
Pragma
The format for a section pragma is:
#pragma section(“section_name”, var1, ..., varn)
The variables received as parameters are placed in a section whose
name is section_name followed by an underscore ( _ ) and the alignment
of the variable (i.e., 1, 2 or 4). The separation into different sections,
based on the alignment, minimizes the amount of wasted space in
the allocation of the variables.
Use the section pragma only with an initialized definition of a global
variable, see Chapter 6 “Data Initialization”.
Do not use the section pragma with uninitialized global data because
the variable is directed to the common section. If you are not interested
in the initialization of variables you wish to direct to a user section, initialize
these variables to some value in the C/C++ code. However, if you
do not request the linker to create the ROM image (in the linker script
file) and if you do not initialize the section in run-time you avoid all
overheads (ROM and run-time).
The type of the section (const or regular data) is determined according
to the first variable in its list. You may not mix different types of variables
in one user-defined section. An attempt to do so results in a
warning message.
The section pragma must precede the declaration of the variables to
which it refers.
Section
Attribute
The format for a section attribute is:
vartype varname __attribute__ ((section ("secname"))) = value;
CompactRISC C/C++ Compiler Reference Manual EXTENSIONS TO THE C/C++ LANGUAGES 3-4
Use the section attribute only with an initialized definition of a global
variable, see Chapter 6 “data initialization”.
Example You have an area of fast memory between addresses 0xff00 and 0xffff (inclusive).
To place frequently-used global variables in that memory, declare
the variables as follows:
#pragma section (“.fast”, glob_flags, glob_counter)
unsigned char glob_flags = 1;
short glob_counter = 3;
or, using section attribute:
unsigned char glob_flags __attribute__
((section (".fast_1"))) = 1;
short glob_counter __attribute__
((section (".fast_2"))) = 3;
Note that here you must add an underscore to the section name,
followed by the variable’s alignment, to control the separation into
different sections.
The generated assembly file for the both of the above examples is:
.globl _glob_flags
.section .fast_1,”aw”,”progbits”
_glob_flags:
.byte 1
.globl _glob_counter
.section .fast_2,”aw”,”progbits”
.align 2
_glob_counter:
.word 3
To link this file (and any other files that were similarly compiled) so that
these variables reside in the fast memory, the linker script should take
the following form:
MEMORY {
...
/* Define fast memory area */
fast_mem: ORIGIN=0xff00, LENGTH=0x100
...
}
SECTIONS {
...
/* Direct the .fast section (a unification of all
.fast_* input sections) to fast memory. */
.fast : { *(.fast_2) *(.fast_1) } > fast_mem
...
}
CompactRISC C/C++ Compiler Reference Manual EXTENSIONS TO THE C/C++ LANGUAGES 3-5
3.3 ASM STATEMENTS AND INTRINSIC FUNCTIONS
There are two kinds of asm statements, each of which requires a different
treatment.
Regular asm
statements
A regular asm statement has the form __asm__(“asm string”).
The compiler puts the string, as is, in the assembly output. Take great
care if you use this kind of asm statement, since you are interfering in
the compiler code generation.
Advanced asm
statements
With advanced asm statements it is possible not only to embed an assembly
instruction into the compiler-generated code, but also to provide
information to the compiler regarding the side effects of this instruction.
For example, it is possible to report to the compiler which registers are
clobbered, whether memory is modified or not, and whether condition
code is modified or not. The full syntax of advanced asm statements is
beyond the scope of this manual. Readers who are interested in this information
can find it in Using the GNU Compiler Collection.
High-level
interface to
machine
instructions
For your convenience, the CompactRISC toolset provides a set of macros
which are implemented using advanced asm statements, and enable
you to safely embed most CompactRISC instructions into your source
code.
A high-level interface is provided for most CompactRISC machine instructions
in the header file asm.h. This file contains a set of macros
which are implemented using advanced asm statements. These macros
allow usage of machine instructions in conjunction with C/C++ variables.
Each macro may expand to one or more machine instructions depending
on the context. The usage of advanced asm statements in these
macros ensures that the assembly instructions are safely embedded
into the code, and minimize the damage to compiler optimizations.
To embed the CompactRISC instruction addw, write:
#include <asm.h>
int i,j;
...
_addw_(i,j);
More useful examples are the _spr_ and _lpr_ macros. They provide
access to the special purpose registers (e.g., PSR, ISP), which are not
otherwise available from a C/C++ program. For example:
#include <asm.h>
unsigned int tmp;
...
/* Store the PSR contents in variable ‘tmp’ */
_spr_(“psr”, tmp);
CompactRISC C/C++ Compiler Reference Manual EXTENSIONS TO THE C/C++ LANGUAGES 3-6
Other useful macros, set_i_bit, clear_i_bit, _ei_ and _di_ and
are available for a specific purpose of disabling and enabling interrupts.
For more details on their use see Chapter 6.
The following table lists all the macros available in asm.h.
3.4 VARIABLES IN SPECIFIED REGISTERS
The CompactRISC Compiler allows you to put a few global variables into
specified core registers. You can also specify the register in which an
ordinary register variable should be allocated.
Global
register
variables
Global register variables reserve registers throughout the program. This
may be useful in programs which have a couple of global variables that
are accessed very often.
You can define a global register variable by adding an __asm__ statement,
whose parameter is the name of the register, to a register variable
declaration. For example:
register int *foo __asm__("r9");
where r9 is the name of the register that should be used.
Always choose a non-scratch register (i.e., r7 to r13) for a global variable.
Non-scratch registers are saved and restored by function calls,
and thus library routines do not clobber them. For more details about
scratch and non-scratch registers refer to Chapter 5.
_addb_ _di_ _movd_ _storb_
_addw_ _ei_ _movxb_ _stord_
_addd_ _eiwait_ _movzb_ _subb_
_addub_ _excp_ _movxw_ _subw_
_adduw_ _loadb_ _movzw_ _subd_
_andb_ _loadw_ _mulb_ _tbit_
_andw_ _loadd_ _mulw_ _wait_
_andd_ _lpr_ _nop_ _xorb_
_ashub_ _lprd_ _orb_ _xorw_
_ashuw_ _lshb_ _orw_ _xord_
_ashud_ _lshw_ _ord_ set_i_bit
_cmpb_ _lshd_ _retx_ clear_i_bit
_cmpw_ _movb_ _spr_
_cmpd_ _movw_ _sprd_
CompactRISC C/C++ Compiler Reference Manual EXTENSIONS TO THE C/C++ LANGUAGES 3-7
Defining a global register variable in a certain register reserves that register
entirely for this use within the current compilation. The register
will not be allocated for any other purpose in the functions in the current
compilation. The register will not be saved and restored by these
functions. Stores into this register are never deleted even if they would
appear to be dead, but references may be deleted, moved or simplified.
It is not safe for one function that uses a global register variable, to call
another such function, foo, by way of a third function, lose, that was
compiled without knowledge of this variable (i.e., in a different source
file in which the variable was not declared). This is because lose might
save the register and put some other value there.
Example x.c :
register int globreg __asm__(“r11”);
bar()
{
globreg = something;
lose();
}
foo()
{
use(globreg);
}
y.c :
lose()
{
...
foo();
...
}
As you can see, if y.c is compiled without the knowledge that r11 is
used as a global register variable, lose may use it for other purposes
and then foo will not see its expected value.
To recompile a source file that does not use your global register variable,
and so can not use that register for any other purpose, it is sufficient
to specify the compiler option -ffixed-REG option (where REG is
the register allocated for the global variable). Alternatively, add a global
register declaration to their source code.
A function which can alter the value of a global register variable cannot
safely be called from a function compiled without this variable, because
it could clobber the value the caller expects to find there on return.
Therefore, the function which is the entry point into the part of the program
that uses the global register variable must explicitly save and restore
the value which belongs to its caller.
CompactRISC C/C++ Compiler Reference Manual EXTENSIONS TO THE C/C++ LANGUAGES 3-8
All global register variable declarations must precede all function definitions.
If such a declaration could appear after function definition, the
declaration would be too late to prevent the register from being used for
other purposes in the preceding functions.
Global register variables may not have initial values, because an executable
file has no means to supply initial contents for a register.
Local register
variables
You can define a local register variable with a specified register, in a
similar manner to a global variable, except that a local variable appears
within a function.
Defining such a register variable does not reserve the register; it remains
available for other uses in places where flow control determines that the
variable's value is not live. Excessive use of this feature may leave too few
available registers to compile certain functions.
This option does not guarantee that the compiler always generates code
with this variable in the specified register. Do not code an explicit reference
to this register in an asm statement and assume that it always refers to
this variable. Stores into local register variables may be deleted when they
appear to be dead according to data flow analysis. References to local register
variables may be deleted, moved or simplified.
3.5 INLINE FUNCTIONS
By declaring a function inline, you can direct the compiler to integrate
its code into the code for callers. This makes execution faster by eliminating
the function-call overhead. In addition, if any of the actual argument
values are constants, their known values may permit
simplifications at compile time so that not all of the inline function’s
code needs to be included.
The compiler respects a declaration of a function as an inline function
only when optimizations are in effect.
To declare a function inline, use the __inline__ keyword as a qualifier in
its declaration.
Example __inline __
int
inc (int *a)
{
(*a)++;
}
The option -finline-functions makes the compiler try to inline simple
functions.
CompactRISC C/C++ Compiler Reference Manual EXTENSIONS TO THE C/C++ LANGUAGES 3-9
If all calls to a function, that is both inline and static, are integrated
into the caller, and the function’s address is never used, the function’s
own assembler code is never referenced. In this case, the compiler does
not actually output assembler code for the function unless the option -
fkeep-inline-functions is specified.
Some calls cannot be integrated (in particular, calls that precede the
function’s definition, and recursive calls within the definition). For nonintegrated
calls, the function is compiled to assembly code as usual. A
function must also be compiled as usual if the program refers to its address,
because that cannot be inline. Using -Winline warns when a
function that was declared as inline can not be inlined, and gives the
reason for the failure.
When an inline function is not static, the compiler assumes that there
are calls from other source files, and calls therein cannot be integrated.
A non-static inline function is, therefore, always compiled on its own
in the usual fashion, although it may be inlined in functions that are
defined in the same file in which it is defined.
If you specify both inline and extern in a function definition, the definition
is used only for inlining. A function is never compiled on its own,
not even if you refer to its address explicitly. Such an address becomes
an external reference, as if you had only declared the function, and had
not defined it.
The combination of __inline__ and extern can be used like a macro.
Put a function definition in a header file, with these keywords, and copy
the definition (without __inline__ and extern) to a library file. The definition
in the header file causes most calls to the function to be inlined.
Any remaining uses of the function refer to the copy in the library.
3.6 DOUBLE SLASH (‘//’) COMMENTS
Similar to C++, a double slash (‘//’) can be used to denote a comment
in C source code. Anything following the double slash until the end of
the current line, is treated as a comment.
3.7 NON-RETURNING ROUTINES
For a non-returning routine, the compiler does not store the nonscratch
registers used, thus improving code size and performance.
To inform the compiler that a routine does not return, define it as follows:
CompactRISC C/C++ Compiler Reference Manual EXTENSIONS TO THE C/C++ LANGUAGES 3-10
void NoReturnFunc () __attribute ((__noreturn__));
void NoReturnFunc()
{
...
}
void TaskN ()
{
while (1)
{
NoReturnFunc();
}
}
CompactRISC C/C++ Compiler Reference Manual RUN-TIME LIBRARIES 4-1
Chapter 4
RUN-TIME LIBRARIES
4.1 INTRODUCTION
The CompactRISC run-time libraries provide run-time support for the
CompactRISC C/C++ Compiler. There are five libraries: libc, libstdc++,
libhfp, libd and libstart. The following list summarizes the
contents of these libraries according to functional groups:
4.1.1 The libc Library
Standard
ANSI-C library
functions
The CompactRISC standard ANSI-C library is based on the C library by
Dinkumware Ltd. See Section 4.2 for more details.
Low-level
functions
This group includes low-level functions that implement low-level functionality
on the host computer using the debugger.
The mecahnisms are:
. I/O - The Virtual I/O mechanism enables a program that is executed
on a development board, to read/write data to a host computer
(including file access).
. time - The Virtual time enables a program that is executed on a development
board, to get the calendar time on a host computer.
. exit
. getenv
These functionalities are done using a communication protocol with the
CompactRISC debugger that runs on the host. These low-level functions
are called by some of the standard ANSI-C functions that handle these
functionalities. These low-level functions may also be called directly;
see Section 4.3 for more details.
32-bit emulationThis group includes a function that provides long int (32-bit) multiplication
emulation for CR16. Currently, the CompactRISC architectures do
not provide a hardware 32-bit multiplication instruction, and therefore
multiplication must be emulated in software. The compiler automatically
calls this routine to implement 32-bit mulitplication operation. The
prototype of the function is:
long __mulsi3(long, long)
CompactRISC C/C++ Compiler Reference Manual RUN-TIME LIBRARIES 4-2
Division
emulation
This group includes division and modulu emulation functions. Currently,
the CompactRISC architectures do not provide a hardware division instruction,
and therefore division must be emulated in software. The CompactRISC
compiler automatically calls these functions to implement division
and modulu operations. See Section 4.4 for more details.
4.1.2 The libstdc++ Library
The run-time libraries are based on Dinkumware’s Abridged Library,
which consist of the EC++ library plus an exception-safe Standard Template
Library. For a detailed descriptions of the functions, refer to the
book The Dinkum Abridged Library Reference P.J. Plauger (Dinkumware,
Ltd., 1998-2001).
4.1.3 The libhfp and libd Libraries
Floating-point
emulation
The libhfp library includes floating-point emulation functions. The CompactRISC
C Compiler automatically calls these functions to implement
floating-point operations when a floating-point unit is not available in
the target hardware.
If your program does not need floating-point operations, you can use a
floating-point dummy library, libd, instead of libhfp. This is necessary
if parts of the program can perform floating-point operations, but are not
executed (for example, although the printf function can print variables
of type float, many programs do not require this facility). See
Section 4.5 for more details.
4.1.4 The libstart Library
These functions include the default start-up routines and a default interrupt
dispatch table, as well as some default trap handlers. See Chapter 6
for more details.
Default trap
handlers
The default trap handlers are functional only in a debugging environment;
in the final product you should implement your own trap and
interrupt handlers. For a definition of the interface between these functions
and the debugger, see Chapter 3 of the CompactRISC Toolset - Introduction.
CompactRISC C/C++ Compiler Reference Manual RUN-TIME LIBRARIES 4-3
4.1.5 Using the Libraries
The CompactRISC libraries are used as inputs to the CompactRISC
linker. There are two ways to include the CompactRISC libraries in the
link process: automatic invocation and direct invocation.
. When the linker is automatically invoked by the compiler driver,
some CompactRISC libraries are specified to the linker by default,
and you can add others.
. When the linker is invoked directly, you must specify each library
required by your program in order to complete the link process.
Automatic
linker
invocation
The default invocation of the crcc driver instructs the linker to include
the following libraries in the linking process: libstart, libc and libd.
The default invocation of the cr++ driver instructs the linker to include
the following libraries in the linking process: libstart, libstdc++,
libc and libd.
If your program needs to perform any floating-point operations, one of
the standard mathematical functions or the time maipulation function
difftime() (which themselves perform floating-point operations), invoke
the driver with the -KFemulation option. In this case, the driver
instructs the linker to use the real floating-point emulation library
(libhfp) rather than the dummy floating-point library (libd).
Example: Compile and link the whetstone.c benchmark program with
libhfp.
crcc whetstone.c -O2 -KFemulation -o whetstone
Direct linker
invocation
If you invoke the linker directly, use -llibrary-name-extension to specify
each library you want to use, where library-name-extension is the name
of the library, excluding the lib prefix. For example, to include the
same libraries as used by the C++ compiler by default in the linking
process, invoke the linker as follows:
crld ... -lstart -lstdc++ -lc -ld
To include floating-point emulation, replace -ld by -lhfp.
Example: Assemble and link the w.s program with libhfp.
crasm w.s
crld w.o -lc -lhfp
Refer to the CompactRISC Toolset - Object Tools Reference Manual. for
detailed explanations of the CompactRISC linker invocation.
Sets of libraries The CR16 toolset includes 10 sets of run-time libraries: one set for each
combination of core, data model and optimization option compiled with
(-Os or -O2). Table 4-1 shows the libraries and their locations. The subdirectories
are located in the root directory of the release.
CompactRISC C/C++ Compiler Reference Manual RUN-TIME LIBRARIES 4-4
When the linker is automatically invoked (see above) the compiler
selects the correct set of libraries to link with. However, when the linker
is directly invoked (see above) it selects by default the set of run-time
libraries that are compiled with CR16C standard data model (the
default mode of the toolset) and with speed optimization -O2. To
instruct the linker to use another set of libraries, use the linker option
-L.
4.1.6 Initialization Requirements
An important issue in embedded systems software is the initialization of
the program’s data. The CompactRISC development tools provide a
mechanism that supports the following type of initializations:
. Copying initialized data from ROM to RAM.
. Clearing the uninitialized data to binary zeros.
. (C++ only) Invoking the constructors of static class objects (see below).
See Chapter 6 for a detailed explanation.
Table 4-1. Library locations
Library Sub-directory
CR16C standard data model
compiled with speed optimization -O2
lib16c_O
CR16C standard data model
compiled with space optimization -Os
lib16c_Os
CR16C near data model
compiled with space optimization -O2
lib16c_near_O
CR16C near data model
compiled with space optimization -Os
lib16c_near_Os
CR16CPlus standard data model
compiled with space optimization -O2
lib16cp_O
CR16CPlus standard data model
compiled with space optimization -Os
lib16cp_Os
CR16CPlus near data model
compiled with space optimization -O2
lib16cp_near_O
CR16CPlus near data model
compiled with space optimization -Os
lib16cp_near_Os
CR16CPlus far data model
compiled with space optimization -O2
lib16cp_far_O
CR16CPlus far data model
compiled with space optimization -Os
lib16cp_far_Os
CompactRISC C/C++ Compiler Reference Manual RUN-TIME LIBRARIES 4-5
You can choose to use, or not to use, these initializations as part of your
program. However, you should be aware that part of the CompactRISC
library functions assume that these initializations are performed at the
beginning of the program.
4.2 STANDARD ANSI C LIBRARY FUNCTIONS
The standard C library conforms to the ANSI standard, with a few
exceptions due to environment limitations, efficiency and re-entrancy
considerations. For detailed descriptions of the functions, refer to any
Standard C library documentation. The non-ANSI exceptions are:
. mktime() function is not supported
. Locale functions are not supported.
The environment is set to the defaults defined in the ANSI standard.
ASCII code is assumed. Multi-byte characters are not supported.
. system() function is not supported.
This section contains extensions and exceptions from the standard ANSI
C library reference manual.
Make sure you use the appropriate header file when using each library
function. The standard header-files reside in the include and
lib\cr16c\3.3.2\include directories, located in the CompactRISC
development tools root directory.
signal.h The ANSI-C standard signal() and raise() handle only internal program-
signals. External signals are not handled.
stdio.h To implement the ANSI-C standard I/O operations, these functions call
some low-level I/O function (e.g. read(), write()), that interfaces with
the host file-system through the debugger in the development environment.
For more details on these low-level functions see Section 4.3.
You can use the extension fileno(FILE *fp) to get the file descriptor
associated with a stream. This returns the integer file-descriptor used
by the stream.
A buffer in the heap is allocated for each file from the first I/O operation
it involves, until you close the stream.
stdlib.h The standard ANSI-C allocation functions (malloc and calloc) allocate
blocks of memory space on the heap using the sbrk() low-level function
that allocates memory in the heap.
void *sbrk(ptrdiff_t size)
CompactRISC C/C++ Compiler Reference Manual RUN-TIME LIBRARIES 4-6
sbrk() allocates size bytes of memory from the unallocated memory
heap and returns the address of its lowest byte. The heap is defined as
a continuous area which resides between two symbols - __HEAP_START
and __HEAP_MAX, predefined in default linker script files. For examples
of linker script files refer to <CRDIR>\src. These examples give you an
idea of how to allocate the heap section and define __HEAP_START and
__HEAP_MAX. Note there is no run-time check of the heap, so you must
take care to allocate enough space for it, according to your specific
requirements. The heap section must reside in an area according to the
data model that is used. For example, if you choose the set of libraries
for the near data model the heap section must reside in the ‘near’ memory.
The ANSI-C standard exit() routine, that terminates the process after
cleaning, uses the low-level _exit() routine, that interfaces with the
host file-system through the debugger in the development environment.
For more details on this low-level function see section 4.3.
4.3 LOW-LEVEL I/O AND TIME FUNCTIONS
To implement I/O, file-handling, time functions, exit and getenv the
standard library calls low-level functions that interface with the debugger.
These functions can be thought of as system calls; their interface is
similar to, but not fully compliant with, that of the POSIX API. Thus we
use the term “system calls” to refer to these low-level routines.
These low-level functions are dependent on the development-board
debugging capabilities. You may use them for debugging during the program
development phase (e.g., writing error messages to the terminal,
storing and retrieving results from files, etc.). However, a program that
depends on these functions does not work in any other target system.
While these simulated system calls, and the libraries built on them,
provide a very easy and conceptually clean interface, they may be too
bulky for applications which do not require these functionalities. For
such applications you must trim the library according to your needs.
The system calls documented here only work in conjunction with the CompactRISC
Debugger. They use the debugger to perform the system call on
the host file-system. For a definition of the interface between these functions
and the debugger, see Section 3.3 of the CompactRISC Toolset - Introduction.
For independent programs, you can make your own low-level routines.
This section provides the guide lines for making a system-dependent set
of routines for any system. The rest of the library functions work correctly
as long as the simulated system calls are replaced with compatible
routines.
CompactRISC C/C++ Compiler Reference Manual RUN-TIME LIBRARIES 4-7
4.3.1 Virtual I/O
All I/O is performed via file descriptors, which are small integer numbers.
When a program starts, the file descriptor 0 is associated with the
console terminal in read mode (i.e., the keyboard) and file descriptors 1
and 2 are associated with the console terminal in write mode (i.e., the
screen).
Programs open files on the host system with the open() function, and
perform I/O and other file operations with the functions detailed below.
All standard functions, which handle I/O and files, call these system
calls.
Two include files, fcntl.h and unistd.h, contain all the low-level I/O
function prototypes and related macros. These functions are described
below.
fcntl.h Includes the open() function prototype, and the flags macro definition.
unistd.h Includes prototypes of the I/O low-level functions detailed below.
open Opens a file for reading or writing or creates a new file.
#include <fcntl.h>
#include <sys/stat.h>
open() (char *path, int flags, int mode)
open() opens the file path for reading and/or writing on the host system,
as specified by the flags argument, and returns a descriptor for
that file.
path is the address of a string of ASCII characters representing a pathname,
terminated by a null character.
The flags argument may indicate that the file is to be created if it does
not already exist (by specifying the O_CREAT flag).
To form the flags specified, OR the following values, defined in
sys/file.h:
O_RDONLY opens for reading only
O_WRONLY opens for writing only
O_RDWR opens for reading and writing
O_APPEND appends to the file if exists
O_CREAT creates file for writing, if it does not exist
O_TRUNC truncates the file to zero size
O_EXCL If set with O_CREAT and the file already exists, open()
returns an error. This can be used to implement a simple
exclusive access-locking mechanism.
CompactRISC C/C++ Compiler Reference Manual RUN-TIME LIBRARIES 4-8
The mode argument is required only when O_CREAT is specified. If the
file already exists, mode is ignored. Otherwise, mode specifies the file
permission settings, which are set when the new file is closed the first
time.
To form the mode specified, OR the following values, defined in
sys/stat.h:
S_IWRITE Writing permitted
S_IREAD Reading only permitted
No program may have more than FOPEN_MAX file descriptors open
simultaneously. FOPEN_MAX is defined in stdio.h.
Return Value On successful completion, a non-negative integer termed a “file descriptor''
is returned; Otherwise -1 is returned and errno is set to indicate
the error:
ENOENT O_CREAT is not set and the named file does not exist.
EACCES The required permissions (for reading and/or writing) are
denied for the named file.
EMFILE Too many open files.
EEXIST _EXCL has been specified and the file exists.
EINVAL An invalid value was given for one of the arguments to a
function.
See Also close(), lseek(), read(), write()
close Closes a file.
#include <unistd.h>
int close(int fd)
close() closes a file and removes its descriptor from the file descriptors
reference table.
lseek Moves the read/write pointer.
#include <unistd.h>
off_t lseek(int fildes, off_t offset, int whence)
The descriptor fildes refers to a file on the host system or device open
for reading and/or writing.
lseek() sets the file pointer of fildes as follows:
If whence is L_SET, the pointer is set to offset bytes.
If whence is L_INCR, the pointer is set to its current location plus offset.
If whence is L_XTND, the pointer is set to the size of the file plus offset.
L_SET, L_INCR and L_XTND are defined in sys\file.h, included in
fcntl.h. fcntl.h is included in unistd.h.
CompactRISC C/C++ Compiler Reference Manual RUN-TIME LIBRARIES 4-9
Return Value On successful completion, the resulting pointer location, measured in
bytes from the beginning of the file, is returned; Otherwise, the file
pointer remains unchanged, .1 is returned and errno is set to indicate
the error:
EBADF fildes is not an open file descriptor.
EINVAL whence is not a proper value.
EINVAL The resulting file pointer is negative.
See Also open()
read Reads input.
#include <unistd.h>
int read(int fildes, char *buf, int nbytes)
read() attempts to read nbytes bytes of data from the object referenced
by the descriptor fildes into the buffer pointed to by buf.
The read() starts at a position given by the pointer associated with
fildes, see lseek().
On return from read(), the pointer is incremented by the number of
bytes actually read.
The system guarantees to read the number of bytes requested if the
descriptor references a file which has that many bytes remaining before
the end-of-file, but in no other cases.
If the returned value is 0, then end-of-file has been reached.
Return Value If successful, the number of bytes actually read is returned; Otherwise,
.1 is returned and errno is set to indicate the error:
EBADF fildes is not a valid file descriptor open for reading.
EFAULT buf points outside the allocated address space.
See Also open()
rename Changes the name of a file.
#include <stdio.h>
int rename(char *old, char *new)
rename() changes the file named old to a file named new. The filename
old is effectively removed. If new already exists, the behavior of
rename() is undefined.
CompactRISC C/C++ Compiler Reference Manual RUN-TIME LIBRARIES 4-10
Return Value On successful completion 0 is returned; Otherwise a non-zero value is
returned, and errno is set to indicate the error. See the list of Return
Values below.
ENOENT File or directory specified by new is not found.
EINVAL Name contains invalid characters.
EACCES File or directory specified by new already exists or could
not be created (invalid path); or old is a directory and new specifies
a different path.
unlink Removes the directory entry of a file.
#include <unistd.h>
unlink(char *path)
unlink() removes the file on the host system whose name is given by
path.
Return Value On successful completion, 0 is returned; Otherwise, .1 is returned and
errno is set to indicate the error:
ENOENT The file or path is not found or the path specified a directory.
EACCES The path specifies a read-only file.
See Also close()
write Writes to a file.
#include <unistd.h>
write(int fildes, char *buf, int nbytes)
write() attempts to write nbytes bytes of data to the object referenced
by the descriptor fildes from the buffer pointed to by buf.
write starts at a position given by the pointer associated with fildes,
see lseek().
On return from write(), the pointer is incremented by the number of
bytes actually written.
Return Value On successful completion, the number of bytes actually written is
returned; Otherwise, the file pointer remains unchanged, .1 is returned
and errno is set to indicate the error:
EBADF fildes is not a valid descriptor open for writing.
EFBIG An attempt is made to write a file that exceeds the process
file size limit or the maximum file size.
CompactRISC C/C++ Compiler Reference Manual RUN-TIME LIBRARIES 4-11
See Also lseek() and open()
4.3.2 Environment
getenv Returns a value for an environment name.
#include <stdlib.h>
char *getenv(const char *name)
getenv() searches the environment list for a string of the form
name=value and, if the string is present, returns a pointer to the value
in the current environment.
Return Value On successful completion, a pointer to the value in the current environment
is returned; Otherwise, a null pointer is returned.
4.3.3 Time
time Determines the current calendar time.
#include <time.h>
time_t (time)(time_t *tod)
time_t is defined as type long int.
time() detemines the current calendar time on the host machine by
calling the low-level function _Time() that gets the time from the host
computer. Return it as the return value and by reference in the argument
*tod.
Return Value On successful completion, the best approximation to the current calendar
time on the host machine is returned. There is no error return.
_Time Determines the current calendar time.
#include <unistd.h>
time_t _Time(time_t *timer)
time_t is defined as type long int.
_Time() returns the current calendar time on the host machine.
Return Value On successful completion, the best approximation to the current calendar
time on the host machine is returned. There is no error return.
CompactRISC C/C++ Compiler Reference Manual RUN-TIME LIBRARIES 4-12
4.3.4 Program Termination
_exit void _exit(int stat)
_exit() passes exit code to debugger to handle program's termination
by calling _eop().
void _eop(int stat)
_eop() only returns control to the debugger. The debugger prints the
value of stat into a file named exit.cod in the current directory. This
function never returns.
4.4 DIVISION EMULATION
Division and remainder operations are performed by software emulation
routines. The compiler replaces each division, or remainder, operation
with these function calls:
int _fast_quo(int, int) /* division of two signed integers */
int _fast_uquo(unsigned int, unsigned int)
/* division of two unsigned integers */
int _fast_rem(int, int) /* remainder from two signed integers
division */
int _fast_urem(unsigned int, unsigned int)
/* remainder from two unsigned integers
division */
long __divsi3(long, long) /* signed division */
unsigned long __udivsi3(unsigned long, unsigned long)
/* unsigned division */
long _ _modsi3(long, long) /* signed remainder */
unsigned long __umodsi3(unsigned long, unsigned long)
/* unsigned remainder */
Division by 0 In development mode, libstart supplies a handler for the DVZ exception.
When division by 0 occurs during execution, the DVZ trap handler
is invoked, and passes control to the debugger on the host.
In production mode, it is recommended that the application supplies a
routine to handle DVZ exceptions. The dispatch table must be updated
to point to the DVZ trap handler.
CompactRISC C/C++ Compiler Reference Manual RUN-TIME LIBRARIES 4-13
4.5 FLOATING-POINT EMULATION
The Floating-Point Emulation Library (libhfp) is used to create floating-
point programs for the CompactRISC micro-processors that lack
Floating-Point Unit hardware (FPU). libhfp is a library of floating-point
arithmetic emulation routines. It provides an efficient, low-cost, floating-
point solution for systems without an FPU, by emulating floatingpoint
unit instructions in software.
The CompactRISC libhfp is partly based on floating-point emulation
code distributed among the source files of GCC (GNU Compiler Collection).
libhfp supports virtually all the arithmetic operations (see Table 4-2) of
the IEEE 754 format floating-point operands. However, it does not support
double-precision floating point. Issues of compatibility and conformity
to IEEE 754 standards are discussed below.
Using libhfp has an adverse effect on the size of your program code.
Each floating-point operation is translated to an emulation function
call, whose code is included in the final executable object file.
When a program is compiled with the -KFemulation option, the
CompactRISC compiler puts floating-point entities into an integer register
(or register pair if needed) and generates an emulation call whenever
a floating-point operation is required. The floating-point operands are
passed to the emulation routine in integer registers, using the standard
calling convention (see Chapter 5).
The emulation routines are re-entrant; floating-point code may therefore
be used in signal and interrupt handlers.
Conformity to
IEEE 754
The libhfp library is arithmetically compatible to the round-to-nearest
rounding mode of the IEEE 754 standard. It implements single-precision
floating-point numbers only, using the IEEE 754 standard formats.
In particular, the following ANSI/IEEE 754 features are emulated:
. Basic single-precision format (float)
. Signed zero
. Round-to-nearest
The following IEEE 754 features are not supported by libhfp:
. Basic double-precision format (long)
. Special (NaN, denormalized, infinity) arithmetic
. Round towards + ∞, round towards . ∞, round towards zero
. Unordered compare
. Inexact exception
. Reserved operand exception by the cmpf and cmpl instructions
CompactRISC C/C++ Compiler Reference Manual RUN-TIME LIBRARIES 4-14
. Division-by-zero exception. In the CompactRISC toolset, for a
division-by-zero the function returns a quiet NaN.
. Invalid operation exception
. Overflow/underflow exceptions
In addition, the libhfp emulation routines have several other attributes
which may impact the way your code works:
The libhfp
interface
The CompactRISC Compiler issues libhfp function calls as follows:
The operands of the floating-point operation are passed to the emulation
routine in a group of consecutive integer registers, according to the standard
calling convention (see Chapter 5). The operands are passed in consecutive
pairs of registers, lower-order bits (mantissa lower part) are passed in the
lower numbered register, and higher order bits (exponent + sign + high-order
bits of mantissa) are passed in the higher numbered register.
The result is returned in r0, r1.
Table 4-2. Instructions Emulated By Calls to libhfp Routines
Operation Emulation Routine
Add two floats float _ _addsf3 (float a1, float a2)
Subtract two floats float _ _subsf3 (float a1, float a2)
Multiply two floats float _ _mulsf3 (float a1, float a2)
Divide two floats float _ _divsf3 (float a1, float a2)
Negate a float float _ _negsf2 (float a1)
Convert long to float float _ _floatsisf (long a1)
Convert float to long long _ _fixsfsi (float a1)
Convert float to unsigned long unsigned long _ _fixunssfsi (float a1)
Return non-zero if a1 = a2 int _ _eqsf2 (float a1, float a2)
Return non-zero if a1 ≠ a2 int _ _nesf2 (float a1, float a2)
Return non-zero if a1 > a2 int _ _gtsf2 (float a1, float a2)
Return non-zero if a1 ≥ a2 int _ _gesf2 (float a1, float a2)
Return non-zero if a1 < a2 int _ _ltsf2 (float a1, float a2)
Return non-zero if a1 ≤ a2 int _ _lesf2 (float a1, float a2)
CompactRISC C/C++ Compiler Reference Manual STANDARD CALLING CONVENTION 5-1
Chapter 5
STANDARD CALLING CONVENTION
5.1 CALLING CONVENTION
The calling convention is defined as part of the CompactRISC architecture,
and is supported by the CompactRISC Development Tools. The
calling convention consists of a set of rules which form a handshake
between different pieces of code (subroutines), and define how control is
transferred from one to another. It thus defines a general mechanism
for calling subroutines and returning from subroutines.
If you develop your entire application in the C/C++ languages, you may
not be aware of the calling convention since it is handled by the
CompactRISC Compiler. To ensure compatibility between a calling subroutine
(C/C++ function) and a called subroutine, use function prototypes.
This ensures that a call to a function is compatible with its
definition. In other words, it ensures that all arguments are correctly
transferred from the calling function to the called function.
5.1.1 Calling a Subroutine
The CompactRISC architecture usually uses the bal or jal instruction to
call a subroutine. These instructions perform two operations:
. Save the address of the following instruction in a specified generalpurpose
register. This address is used as a return address for the
called subroutine. According to the calling convention, the return
address is always saved in the ra register.
. Transfer control to a specified location in the program (the subroutine
address).
Example bal (ra), s # call the subroutine "s"
or
jal (ra), (r8,r7) # call the subroutine whose address is
# stored in the register pair r7-r8
CompactRISC C/C++ Compiler Reference Manual STANDARD CALLING CONVENTION 5-2
5.1.2 Returning from a Subroutine
The jump and popret instructions are used to return from a subroutine.
The program jumps to the return address, which is stored in the
ra register. The retx instruction is used to return from a trap service
procedure.
Examples jump (ra) # return to caller
popret $1,r7,RA # pop r7 and ra, return to caller
retx # return from trap
5.1.3 Passing Parameters to a Subroutine
The calling sequence loads some of the arguments to registers according
to a predefined convention. The registers used are r2, r3, r4, r5.
The argument list is comprised of arguments that can be passed in registers
(qualified arguments) and arguments that cannot be passed in
registers (non-qualified arguments).
Qualified
arguments
Qualified arguments are of the following types:
. Char and integer arguments are passed in a single register.
. Long integer, float and pointer arguments are passed in a pair of
registers.
. Structures or unions with size less than, or equal to, the size of two
registers, i.e., 4 bytes, and aligned to the default bus width.
If a structure or union member must be split between two registers,
the structure or union is disqualified.
Non-qualified
arguments
Non-qualified arguments are of the following types:
. Structures or unions, other than those specified above.
. Arguments that cannot be entirely loaded into available registers.
5.1.4 Parameter Passing Algorithm
The following algorithm determines how parameters are passed to a
given routine:
. The parameter list is scanned from left to right.
CompactRISC C/C++ Compiler Reference Manual STANDARD CALLING CONVENTION 5-3
. Registers are allocated in ascending order (i.e., r2 is allocated
before r3 etc.).
. A parameter which is qualified to be passed in a register(s), is allocated
the next free register(s) (in the range r2-r5).
. If a structure or union is to be passed in registers, its layout within
the registers is as in memory. Fields are allocated in the order they
are declared, starting at the lower register. If two members reside in
the same register, the first is put in the least significant bits. The
alignment of members within the structure or union is kept by
passing the padding bytes.
. If a parameter cannot be passed in a register (either because it is
not qualified, or because the registers have been entirely allocated
to previous parameters), it is passed on the stack. Each parameter
passing on the stack is aligned to integer size. This alignment is relative
to the address of the first parameter on the stack, and is not
absolute.
There is one exception to the rule stated above:
Variable number
of arguments
For routines with a variable number of arguments, that are declared as
such using the ANSI C VARARGS declaration, all parameters are put on
the stack by the calling routine. None of the arguments are passed in
registers.
5.1.5 Returning a Value
A subroutine can return one value to its caller. The calling convention
uses register r0 for passing the return value to the caller.
For example, consider the following source code:
return 5;
The assembly code generated from this line is:
Example movw $5, r0 # pass return value
jump (ra) # return to caller
When the returned value is of size greater than that of a single register,
the (r1,r0) pair is used (r0 having the lsw). This is the case for long
integers, float values and pointers.
The only exception to this rule is a function that returns a structure or
a union. In this case, the calling function puts the address of a structure
or union in which to store the result in (r1,r0), and is responsible
for the allocation needed. The called function uses (r1,r0) as a
pointer to the resulting structure or union.
CompactRISC C/C++ Compiler Reference Manual STANDARD CALLING CONVENTION 5-4
5.1.6 Scratch and Non-scratch Registers
The calling convention divides the registers into two categories:
Scratch register. This register can be freely modified by any subroutine,
without first saving its previous value. The caller cannot assume
that its value remains the same after a subroutine has returned. If, for
any reason, the caller needs to keep this value, it is responsible for saving
the scratch register, either on the stack or in a non-scratch register,
before calling the subroutine, and to restore it after the subroutine has
returned.
Non-scratch register. Before using this register, a subroutine must first
store its previous value on the stack. Before returning to the caller, the
subroutine must restore its value. The caller can always assume that
this register is not clobbered by any subroutine that it has called.
The calling convention defines r0 - r6 as scratch registers. All the other
general-purpose registers are defined as non-scratch, except sp which is
used as the stack pointer and thus does not belong to either category.
Note An exception to the rule for using scratch registers is an interrupt/
trap subroutine. This kind of subroutine must always save
and restore all the scratch registers that may be used during the interrupt/
trap. This is because there is no real caller. The interrupt,
or trap, suspends another subroutine which is not aware of, or prepared
for, this interruption. To protect it, its scratch registers must
be saved and restored so that the interrupt or trap is transparent.
5.1.7 Program Stack
The program stack is a contiguous memory space that can be used by
your program for:
. Allocating memory for local variables which are not in registers
. Passing arguments in special cases (see passing arguments to a
subroutine)
. Saving registers before calling a subroutine, or after being called
(see scratch registers)
The stack is a dynamic memory space that begins at a fixed location
(stack bottom) and grows towards lower memory addresses. Its lowest
address (also called top of stack) is changed dynamically and is pointed
to by the Stack Pointer register (sp). The stack pointer is kept aligned,
at all times, to the internal bus width, i.e., 2 for CR16C, 4 for
CR16CPlus.
CompactRISC C/C++ Compiler Reference Manual STANDARD CALLING CONVENTION 5-5
A subroutine can allocate space on the stack by changing the value of
the sp register to adjust the top of stack. When the subroutine returns,
it must restore sp to its previous value, thereby releasing the temporary
space that it had occupied on the stack during its life-time.
In your application program, allocate space for the program stack, and
initialize the sp register to the stack bottom. This is done in the startup
routine. For more details about the start-up routine, see Section 5.7
in the CompactRISC Toolset - Introduction.
5.2 ALIGNMENT OF VARIABLES
The CompactRISC family allows non-aligned-to-size memory references,
although such accesses incur a penalty in memory access time. Usually,
the compiler aligns the memory address of all memory objects, taking
into acount the type of the object, the internal bus width and
integer size. The alignment is not more than the internal bus width.
Strings (i.e., character arrays) are aligned to integer size.
Note that internal bus width is 16 bits for CR16C and 32 bits for
CR16CPlus.
Bottom of stack
Top of stack
(SP)
Stack
space
Address 0
Highest memory
address
CompactRISC C/C++ Compiler Reference Manual STANDARD CALLING CONVENTION 5-6
In a structure padding bytes are inserted to keep the alignment of the
fields, if necessary. If the option -fpack-struct is specified, the members
are placed sequentially without any padding. The structure itself is
aligned in memory to the largest alignment of its members. In addition,
the structure is padded such that its total size is a multiple of its largest
member alignment.
The type written for a named bit-field imposes alignment for the entire
structure, as if the structure really did contain an ordinary field of that
type. In addition, the bit-field is placed within the structure so that it
would fit within such a field, not crossing a boundary for it. An
unnamed bit-field does not affect the alignment of the containing structure.
Parameters on
Stack
Parameters that are passed on the stack are aligned to integer size. The
alignment is relative to the bottom of the stack.
CompactRISC C/C++ Compiler Reference Manual GUIDELINES FOR USING THE COMPILER 6-1
Chapter 6
GUIDELINES FOR USING THE COMPILER
6.1 INTRODUCTION
The following sections are provided as guidelines for using the
CompactRISC C/C++ Compiler.
6.2 INITIALIZATION ISSUES
6.2.1 The Start-up Routine
Embedded applications normally involve code which is executed before
your “main” routine, and performs initializations essential for running
your program. For example, you can not run your C/C++ program
before the stack Pointer (sp) register has been initialized to point to the
bottom of the stack. These initializations are part of the start-up routine
which is run immediately after the CompactRISC microprocessor exits
the reset state.
The CompactRISC Toolset includes the start-up library (libstart.a)
which provides a default start-up routine. Like all other modules in the
start-up library, the start-up routine is provided in both source code
and object form. This makes it possible for you to customize this routine
for your application needs.
The default start-up routine in the CompactRISC Toolset is provided in
assembly language. Its entry point, which is also the entry point to your
program, is labeled by the name start. Note that start is also the
default entry point symbol used by the CompactRISC Linker.
The default start-up routine carries out the following:
. Initializes the intbase register.
It assigns the address of the default interrupt dispatch table to this
register. The default interrupt dispatch table is another customizable
module in the start-up library.
. Initializes the Stack Pointer (sp) register.
It assigns the address of the bottom of the stack section to this register.
CompactRISC C/C++ Compiler Reference Manual GUIDELINES FOR USING THE COMPILER 6-2
. Initializes the Interrupt Stack Pointer (isp) register.
It assigns the address of the bottom of the interrupt stack section
to this register.
. Calls the init_bss_data routine which is responsible for data initialization
(both initialized and uninitialized data). For further
details, see the next section.
. Calls the __do_global_ctors routine which is responsible for
Invoking the constructors of all the global class objects (For C++
programs). For further details, see Section 6.2.3
. Calls the main routine, which is the actual entry point to your program.
If main returns (which does not normally happen in embedded
applications), it calls the exit routine.
For an example of start-up routine refer to <CRDIR>\src\libstart
directory and view start.sp file.
6.2.2 Initialized Variables
In a non-embedded environment, if you initialize a global or a static
variable at its definition point, for example:
int i = 5;
when your program starts executing, the variable has the initial value
you defined (5 in this example).
In a native environment, initialization is handled by the compiler and
the operating system. In an embedded environment you may expect the
same to happen. However things are a bit more complicated. If your
program is to run stand-alone (i.e., from a read-only hardware device
such as ROM) the variable i should get its initial value from the readonly
hardware device. However, i itself should reside in RAM, since it
was not defined as a const variable. As you can see, for proper execution
of your program, the initial value of i should be copied from ROM
to the RAM address of i shortly after reset.
Some debuggers perform these initializations when loading a program.
However, it is preferable to rely only on the program itself.
Another aspect of this problem is the default value of uninitialized variables.
It is sometimes convenient to assume that data which was not
initialized in the program has a default initial value of binary zero.
CompactRISC C/C++ Compiler Reference Manual GUIDELINES FOR USING THE COMPILER 6-3
The following section deals with the two aspects of data initialization:
. Copying initialized data from ROM to RAM
. Clearing uninitialized data to zero
The program should take care of the following:
. In the linker script file, for each section to be initialized::
– The output section to be initialized should be described in the
linker script file.
– The AT keyword should be used to specify the load address of the
section in ROM.
– Symbols holding the start and end addresses of the section in RAM
and the load address of the section in ROM should be defined.
For more information on the linker script file refer to the CompactRISC
Toolset - Object Tools Reference Manual
. Data initialization code must be written and called by the startup
routine.
For examples refer to <CRDIR>\src. This directory contains a few examples
of linker script files and libstart source code. The start-up routine
in libstart (in the file start.sp) calls an initialization function
which is also part of libstart. This function copies initialized data sections
from ROM to RAM, and clears uninitialized data sections. You will
probably need to customize the initialization function to fit the sections
in your program. If your program does not require data initialization at
all, you can remove the call to the data initialization function.
Note that the CompactRISC C and C++ run-time libraries (libc and
libstdc++) requires data initialization. Thus, if you are using these
libraries in your program, do not skip data initialization.
CompactRISC C/C++ Compiler Reference Manual GUIDELINES FOR USING THE COMPILER 6-4
6.2.3 Global Constructor/Destructor Invocation
The toolset supplies the needed infrastructure for constructor and
destructor invocations.
If your application has global objects that need to be constructed before
reaching main, you must:
. Include in the linker script file the definitions of .ctor and .dtor
output sections.
. Include in the linker script file the symbols __CTOR_LIST,
__CTOR_END, __DTOR_LIST and __DTOR_END, which point to the beginnings
and ends of the .ctor and .dtor sections respectively.
. Make sure that the two functions __do_global_ctors and
__do_global_dtors are included in libstart.
. Call the function __do_global_ctors from the start routine.
If your application does not have global objects that need to be
constructed before reaching main, you may:
. Remove from the linker script file the definitions of the .ctor and
.dtor output sections.
. Remove the function __do_global_ctors from libstart and its
invocation from the start routine.
. Remove the function __do_global_dtors.
Note The default linker script file and start library handle the issue. Use
them as reference when writing your own linker script file and start
routines.
Refer to the CompactRISC Toolset - Object Tools Reference Manual for
further details.
6.3 LINKER INPUT SECTIONS GENERATED BY THE COMPILER
The following groups of input sections are generated by the compiler:
.text program code *
.dmodelrdat_align read-only data *
.dmodeldata_align data initialized to a value other than 0
.dmodelbss_align- uninitialized static data and any data initialized
to 0
.ctor list of pointers to constructors
.dtor - list of pointers to destructors
CompactRISC C/C++ Compiler Reference Manual GUIDELINES FOR USING THE COMPILER 6-5
dmodel stands for data model and can be nothing for the standard data
model, n for the near data model or f for the far data model. The compiler
assumes that all sections .nrdat_align, .ndata_align and
.nbss_align are located in the near data address range, that all sections
.rdata_align , .data_align and .bss_align are located in the
standard data address range and that all sections .frdat_align,
.fdata_align and .fbss_align are located in the far data address range.
For more information on the data models see Chapter 2.
The data sections are subdivided according to the alignment of the variables
that reside in them. The align stands for alignment and can be
either 1, 2 or 4. For example, section .rdata_1 includes read-only variables
whose alignment is 1, whereas .bss_2 includes uninitialized variables
whose alignment is 2.
When you develop an embedded application program, you often put the
constant and read-only part of your data in a Read-Only device, such as
ROM or Flash. This part of the data should be distinguished from other,
non-constant, data. To this end, use the ANSI C const qualifier when
you declare such data. The CompactRISC C/C++ Compiler directs
const data to a read-only linker input sections. These linker input sections
should later on be directed to ROM by using the linker script.
The compiler assumes that the sections marked with ‘*’ are defined, and
thus redirects data into them. Even if there are no const variables in
your program, the compiler might direct data into the read-only data
sections. Therefore, make sure your linker script file includes a definition
for all these sections.
Note that global data that is uninitialized is not directed to any section
by the compiler, however the linker directs it to the common sections.
By default the common section is .COMMON. If, however, the module with
uninitialized global data was compiled for the near data model, the
linker will direct the data to .ncommon section. The same is true for the
far data model, if the module with uninitialized global data was compiled
for the far data model, the linker will direct the data to .fcommon
section.
6.4 WORKING WITH INTERRUPTS
6.4.1 Writing Trap and Interrupt Handlers in C/C++
The CompactRISC C/C++ Compiler allows trap and interrupt handlers
to be developed entirely in the C/C++ language. For details see Chapter
3.
CompactRISC C/C++ Compiler Reference Manual GUIDELINES FOR USING THE COMPILER 6-6
6.4.2 Enabling and Disabling Interrupts - Semaphores
The PSR register contains two bits that control interrupt enabling, the I
bit and the E bit. Maskable interrupts are enabled only when these two
bits are set. The I bit is the global interrupt enable bit. On reset it is
cleared by the CPU, and therefore maskable interrupts are disabled.
The E bit is the local interrupt enable bit. On reset, it is set by the CPU.
The CompactRISC CPU has two dedicated instructions, di and ei, that
clear and set the E bit, respectively.
Enabling &
Disabling
Interrupts
If you want the application program to serve interrupts, you must first
enable maskable interrupts. This is done by setting the I bit of the psr
register. Normally, you should do this shortly after reset. From a C/C++
function you can use the set_i_bit macro for this purpose.
#include <asm.h>
...
set_i_bit(); /* Enable maskable interrupts. */
Another reason to enable interrupts is to enable nested interrupts.
When an interrupt or trap is serviced, the CompactRISC CPU automatically
clears the I bit of the psr register. Thus, at this point nested
interrupts are disabled. If you want to enable nested interrupts, set the
I bit of the psr from within the interrupt handler.
#include <asm.h>
...
#pragma interrupt (int_handler)
void int_handler()
{
set_i_bit(); /* Enable nested interrupts. */
...
}
By default, nested interrupts are disabled. If, in the interrupt handler,
you set the I bit of the psr, all unmasked maskable interrupts are
enabled. You can, however, selectively enable maskable interrupts as
nested interrupts by setting up the Interrupt Control Unit (ICU) of your
chip to enable specific interrupts only. This must be done in the interrupt
handler just before you set the I bit of the psr. For information
about the ICU, refer to your chip’s specification or datasheet.
Semaphores Semaphores protect the entry to critical code segments, and protect the
consistency of data (e.g., buffer counter). The aim is to make critical
sections exclusive to one task at a time. In the CompactRISC family this
is achieved by making the sections uninterruptable, using a special,
efficient, CompactRISC mechanism.
CompactRISC C/C++ Compiler Reference Manual GUIDELINES FOR USING THE COMPILER 6-7
Use the E bit and its two dedicated instructions for local interrupt disabling
and enabling. To protect a code section, call the _di_() macro,
which clears the E bit. When this section is exited, call the _ei_()
macro, which sets the E bit. This process prevents an interrupt from
occurring during the critical code section, without changing the global
interrupts status as reflected in the I bit.
When the E bit is cleared using the di instruction, maskable interrupts
are disabled. When the E bit is set using the ei instruction, interrupts
are not necessarily enabled; Their previous state is restored. If interrupts
were initially enabled (I bit was set), they are enabled again. On
the other hand, if they were initially disabled (I bit was cleared), they
remain disabled. Thus, the di and ei pair is used to disable and enable
interrupts locally, without changing their global state.
Example Process A:
if (buffer_count < BUF_LIMIT) {
/* puts a new element in the buffer */
_di_();
buffer_counter++;
_ei_();
}
Process B:
if (buffer_count > 0) {
/* gets an element from the buffer */
_di_();
buffer_counter--;
_ei_();
}
Note The CR16 architectures have non-interruptible bit manipulation instructions,
thus if the above semaphore operations involve modifying
one bit only, the _di_() and _ei_() macros are not required.
6.4.3 The Interrupt Dispatch Table
The CompactRISC architecture operates in direct exception mode. In
this mode, the address of the interrupt handler (residing in the interrupt
dispatch table) is interpreted by the CPU as a pointer. The
addresses of all exception handlers, including traps and interrupts, are
specified in the interrupt dispatch table. The CompactRISC programming
model includes a register called intbase that contains the
address of the interrupt dispatch table. When an exception occurs, the
CompactRISC processor uses the intbase register to determine the
location of the interrupt dispatch table. Each exception has a number
that is used as an index to the interrupt dispatch table to find the
address of the required exception handler. The interrupt dispatch table
CompactRISC C/C++ Compiler Reference Manual GUIDELINES FOR USING THE COMPILER 6-8
has two parts. The first part is used for exception handlers which are
common to all CompactRISC-based processors e.g., non-maskable interrupt
(NMI) handler, breakpoint trap (BPT) handler, division-by-zero trap
(DVZ) handler. The second part is used for handlers of specific interrupts
that exist on a certain derivative of the CompactRISC family.
These interrupts originate from sources which are peripherals for the
CompactRISC core, and reside either on-chip or externally. All these
interrupts are controlled by an Interrupt Control Unit (ICU). The ICU is
the component that sends the interrupt trigger to the CompactRISC
core and also tells the CompactRISC core the number of the interrupt
that is currently pending. For more details refer to the CompactRISC -
CR16C Programmer’s Reference Manual.
Here is an example of an interrupt dispatch table:
void * const _dispatch_table[] = {
0,
nmi, /* Non-Maskable Interrupt handler */
0, /* Reserved */
0, /* Reserved */
0, /* Reserved */
svc, /* Supervisor call trap handler */
dvz, /* Divide by Zero trap handler */
flg, /* Flag trap handler */
bpt, /* Breakpoint trap handler */
trc, /* Trace trap handler */
und, /* Undefined Instruction trap */
0, /* Reserved */
iad, /* Illegal address trap handler */
0, /* Reserved */
dbg, /* Debug trap handler*/
ise, /* In-System Emulator interrupt */
clock_hnd, /* Clock interrupt handler */
uart_hnd, /* UART interrupt handler */
codec_hnd, /* Codec interrupt handler */
mw_hnd /* MICROWIRE interrupt handler */
};
In your application program, you have full control over the interrupt
dispatch table. You can install your exception handlers either statically
(i.e., the same exception handlers throughout the program execution) or
dynamically (replace one exception handler by another during execution).
You can put your interrupt dispatch table in either ROM or RAM.
When you link your program with the start-up library (libstart) you get
a default interrupt dispatch table, and a set of default exception handlers.
You can always customize this table by modifying all or part of the table
entries, or even the exception handlers themselves.
CompactRISC C/C++ Compiler Reference Manual GUIDELINES FOR USING THE COMPILER 6-9
Note that in the CR16C and CR16CPlus debugging environment the
BPT, TRC, DBG and ISE traps are handled directly by the SDI+ module.
Therefore it is meaningless to implement an interrupt handler for these
traps.
To override any part of the start-up library, modify the relevant source
files of the library (provided with the CompactRISC Toolset), compile it,
and add the object file to the object list in the linking phase before the
start-up library.
6.5 CODE ADDRESSES
Since CR16 instructions must always start on an even address, the
least-significant-bit of the address is always 0, and therefore is redundant.
The code address space of the CR16C and CR16CPlus is 16
Mbytes. However, a code address is encoded in 23 bits and not in 24
bits, because code addresses are always even and bit 0 is implied. The
23 bits are encoded over a double word (32 bits).
6.5.1 Writing in C/C++
The CompactRISC Compiler takes care of address encoding and thus
this is transparent to you, as long as you write your program in C/C++.
However, we recommend you to pay even more attention to using good
coding practices when dealing with code addresses. More specifically,
follow the following two guidelines:
1st guideline Use symbols, rather than absolute addresses.
As an example, to jump to function foo at address 0x800 write the following
in the linker definition file:
_foo = 0x800
In the C/C++ file write:
foo();
Instead of writing in the C/C++ file:
((int(*)())(0x800U))();
2nd guideline Do not use arithmetic operators on pointers to functions.
Do not write in the C/C++ file:
CompactRISC C/C++ Compiler Reference Manual GUIDELINES FOR USING THE COMPILER 6-10
((int(*)())(f2 - 4))();
6.5.2 Writing in Assembly
If, however, you write your program in CompactRISC assembly, and have
a label of code, use the .code_label directive to define this label as a
label of code (rather than data) to use a pointer to this place in the code.
Example .code_label abc
# qualify abc as a label of code
abc: ...
movd $abc, (r6,r5)
# load the encoded address of abc to the
# register pair (r6,r5)
...
jump (r6,r5)
# jump to the code address in the
# register pair (r6,r5)
Since we define abc as a label of code, the movd instruction copies bits
1 to 16 of the address of abc (rather than bits 0 to 15) to r5 while bits
17 to 23 are loaded into r6. This ensures that the jump instruction
works properly.
Another good example is the interrupt dispatch table:
Example .code_label handler_1
handler_1:
...
.code_label handler_2
handler_2:
...
.code_label handler_3
handler_3:
...
__dispatch_table:
.double handler_1
.double handler_2
.double handler_3
The addresses of the handlers are encoded correctly in the dispatch
table because the respective labels have been defined as labels of code.
CompactRISC C/C++ Compiler Reference Manual IMPLEMENTATION-DEFINED BEHAVIOR 7-1
Chapter 7
IMPLEMENTATION-DEFINED BEHAVIOR
Annex G.3 of the ANSI C standard lists all the issues that are implementation
defined. This chapter describes how these issues are handled
by the CompactRISC compiler.
7.1 TRANSLATION
When a diagnostic is detected, a message is given in the following format:
<file name>:<line number>:<diagnostic description>
7.2 ENVIRONMENT
. No arguments can be passed to the function main().
. An interactive device is the terminal from which the debugger was
invoked.
7.3 IDENTIFIERS
. All characters in an identifier without external linkage are significant.
. The first 999 characters, in an identifier with external linkage, are
significant.
. Case distinctions are significant in identifiers with external linkage.
7.4 CHARACTERS
. The source character set also includes the dollar sign ($).
The execution character set also includes the dollar sign ($), the at
sign (@) and the backquote (`).
. The number of bits in a character in the execution character set is
eight.
CompactRISC C/C++ Compiler Reference Manual IMPLEMENTATION-DEFINED BEHAVIOR 7-2
. Each character in the source character set is mapped to the identical
character in the execution character set.
. The value of a character that is not part of the execution character
set but is a member of the ASCII character set is its ASCII value.
Characters that are not members of the ASCII character set are illegal.
The value of an escape sequence that is not represented in
the execution character set is the value of the octal or hexadecimal
constant following the escape sequence.
. The value of an integer character constant that contains more than
one character is the value of the second character. For example, the
value of ‘ab’ is ‘b’.
. The type char has the same range of values as signed char.
7.5 INTEGERS
. The representation of positive integers is the same number in base
2. The representation of negative integers is the 2’s complement of
its absolute value.
The following table details the minimum and maximum values of the
various integer types:
. The set of values for int is the same as that of short int.
. The set of values for unsigned int is the same as that of unsigned
short int.
. The result of converting an integer to a shorter signed integer is the
low-order bytes of the integer. The number of bytes taken is the
same as the size of the smaller signed integer.
. The result of converting an unsigned integer to a signed integer of the
same length is the 2’s complement interpretation of the unsigned integer.
Type Min Max
signed char .128 127
short int .32768 32767
long int .2147483648 2147483647
unsigned char 0 255
unsigned short int 0 65535
unsigned long int 0 4294967295
CompactRISC C/C++ Compiler Reference Manual IMPLEMENTATION-DEFINED BEHAVIOR 7-3
. The result of a bitwise operation in signed integers is the 2’s complement
interpretation of the result of the bitwise operation.
. The sign of the remainder on integer division is the same as that of
the dividend.
. The semantics of a right shift of a negative-valued signed integral
type is the same as that of a non-negative-value.
7.6 FLOATING POINT
. The size of type float is 32 bits. The size of types double and long
double is 32 bits as well. Their representation and range of values
are compatible with the IEEE standard for binary floating-point
arithmetic.
. The direction of truncation when an integral number is converted to
a floating-point number that cannot exactly represent the original
value is towards the nearest value. If the original number is halfway
between two values, the even value (least significant bit = 0) is returned.
. The direction of rounding when a floating-point number is converted
to a narrower floating-point number is to the nearest value. If
the original number is halfway between two values, the even value
(least significant bit = 0) is returned.
7.7 ARRAYS AND POINTERS
. The type size_t is equivalent to type long unsigned int.
. The result of ca