THE
PARTICLE COMPUTER DOCU
(The CCSC2SDCC Migration Guide)
This chapter summarises and explains how to migrate code written for the
CCS compiler to
code for the SDCC. (16bit targets)
Contents
Comparison of the two compilers
Feature matrix
aspect |
CCS Compiler |
SDCC |
general |
cost |
> 0 |
0 (GPL) |
linkable object files |
no |
yes |
support |
company |
community |
supported platforms |
Windows®, Linux |
Windows®, Linux, Un*x, Mac OS X |
supported target hardware |
MicroChip PIC (12, 14, 16 bit) |
MicroChip PIC (14 and 16 bit), Intel 8051, Z80 ... |
common data type sizes |
int (unsigned 8 bit), long (unsigned 16 bit), int32 (unsigned 32 bit) |
char (signed 8 bit), int (16 bit), long (32 bit) |
built-in support for RS232, I2C, LCD |
yes |
no |
stability (16bit PIC targets) |
stable/production |
in development |
function pointers |
yes (very limited) |
yes |
interrupt handling |
complex, built-in dispatcher |
basic support for 2 priority levels |
documentation |
available in print and online |
rather incomplete pdf file, active mailing lists |
call stack |
stack |
no |
yes |
reentrant functions |
no |
yes |
recursion |
no |
yes |
variable argument lists |
no |
yes |
overhead for function calls |
nearly zero |
noticable |
code quality |
inline functions |
yes |
no (use #define :-) |
code size |
very densed code (closed world assumption) |
rather large |
Language differences
If existing code for the CCS compiler is to be compiled using the SDCC,
the following language extensions have to be replaced:
Tools for your convenience
Resources
The helper package contains the following tools:
convert.pl
This Perl script replaces most pragmas, all binary literals and modifies some calls to built-in
CCS functions that cannot be handled directly by own functions. (e.g. polymorphic calls) This
script does some general work (configuration words, literals, ...), but keep in mind that most
modifications rely on our compatibility files.
before |
after |
#fuses hs,noprotect,nobrownout,nolvp,put,nowdt
(no)protect will be ignored at the moment. Multiple #fuses will be ignored.
|
code char at __CONFIG1H config1h = 0xFF & _OSC_HS_1H;
code char at __CONFIG2H config2h = 0xFF & _WDT_OFF_2H;
code char at __CONFIG2L config2l = 0xFF & _PUT_ON_2L & _BODEN_OFF_2L;
code char at __CONFIG4L config4l = 0xFF & _LVP_OFF_4L;
|
#use fast_io(A)
Has only effects on the CCS style io-functions.
|
#undef set_mode_A
#define set_mode_A(input, pin) portmode_fast(A, input, pin)
|
#use fixed_io(a_outputs=PIN_A2, PIN_A3)
Has only effects on the CCS style io-functions.
|
#undef FIXEDMASK_TRISA
#define FIXEDMASK_TRISA 0xF3
#undef set_mode_A
#define set_mode_A(input, pin) portmode_fixed(A, input, pin)
|
#use standard_io(A)
Has only effects on the CCS style io-functions.
|
#undef set_mode_A
#define set_mode_A(input, pin) portmode_standard(A, input, pin)
|
a = 0b10010110; |
a = 0x96; |
#include <path\to\file.h> |
#include <path/to/file.h> |
#asm
...
#endasm
ASIS will not be handled separately - #asm is always handled that way.
|
_asm
...
_endasm;
|
#int_timer1 fast noclear
void foo()
Interrupt handler function foo has to appear in the line immediately below
#int_X, with the whole signature in one line.
|
#define timer1_fast
#define timer1_noclear
#define timer1_handler foo
void foo()
|
#use delay(clock=20000000, restart_wdt) |
#define RESTART_WDT
#define CLOCK_SPEED 20000000
#include <delay.c>
|
delay_us(5)
(only replaced for constant values 0-19, #defines will not be recognized)
|
delay_us_short(5)
|
char a;
signed char b;
unsigned char c;
int d;
signed int e;
unsigned int f;
long g;
signed long h;
unsigned long i;
signed int8 j;
unsigned int8 k;
|
unsigned char a;
char b;
unsigned char c;
uint8_t d;
int8_t e;
uint8_t f;
uint16_t g;
int16_t h; // alternative: int
uint16_t i;
int8_t j;
uint8_t k;
|
#byte name = 0x30
|
uint8_t at 0x30 name;
Keep in mind, you must not pin variables on existing special function registers
0xF60-0xFFF or the area 0x000-0x022 used for local
variables (SDCC specific).
|
long x;
#locate x = 0x50
#locate has to appear in the line immediately below the variable
declaration; only uintX_t and intX_t types supported at the moment.
|
uint16_t at 0x50 x;
|
#use i2c(master, scl=PIN_C3, sda=PIN_C4, FORCE_HW)
I2C slave mode, WDT restart and speed selection are not supported at the
moment.
|
#undef I2C_SCL
#undef I2C_SDA
#undef I2C_SUFFIX
#define I2C_SCL PIN_C4
#define I2C_SDA PIN_C3
#define I2C_SUFFIX _PIN_C4_PIN_C3
#include <compat_i2c.h>
|
#use i2c(master, scl=PIN_E2, sda=PIN_D4)
I2C slave mode, WDT restart and fast mode are not supported at the moment.
|
#undef I2C_SCL
#undef I2C_SDA
#undef I2C_SUFFIX
#define I2C_SCL PIN_D4
#define I2C_SDA PIN_E2
#define I2C_SUFFIX _PIN_D4_PIN_E2
#include <software_i2c.c>
|
i2c_read()
argument is optional when using CCSC, defaults to 1.
|
i2c_read(1) |
Other shortcomings of this Perl script
-
Because of the limitation to regexps, convert.pl will not work properly, if there are
line endings at unexpected positions.
- The following pragmas will be ignored and/or removed and should be replaced manually
- #inline
- #separate
- #bit
- #org
- #list / #nolist
- #case
- #device
- #id
- #int_default
- #int_global
- #type
- #use rs232
- #zero_ram
- #priority
- #ignore_warnings
- #opt
-
#use pragmas will only be converted correctly when appearing outside of function
bodies! Otherwise you will get invalid C.
- The usage of macros may break our pattern matching.
ren_ident
SDCC has some reserved words that CCS does not know. If the sources contain such reserved words,
the lexer ren_ident replaces these identifiers. Example:
void foo(byte data);
becomes:
void foo(byte __PIC_data);
In addition to this, ren_ident tries to find identifiers in inline assembler code and
prefixes them with a "_", because SDCC cannot reference variable names, only
symbols known to the linker. An identifier foo is not modified to _foo but to
ASM(foo), where ASM(...) as a macro.
enum-o-matic
CCSC allows enum type definitions without the use of typedef. This non-standard
C can be fixed with this lexer (in some cases).
convert.sh
This bash script runs the programs and script mentioned above, converting all .c and
.h files in the current directory and its subdirectories. It creates the new directory
SDCC and places the current subtree with the modified source files there. Other files
are simply copied, also some of the compatibility files. (see below)
The script requires a working bash, the tools above and dos2unix. The source files
are converted by these tools in the following order: dos2unix, ren_ident,
convert.pl and enum-o-matic.
How to use
Run the convert.sh script in the root directory of your project you want to convert.
After processing the files, check your modified sources with the help of the following section.
Required manual work
-
Replace "sloppy" C
-
Insert #pragma wparam foo, bar,... to tell SDCC which functions (like foo and bar)
should use the faster parameter passing via WREG. It is not needed for functions without
arguments and must not be used for functions called via functions pointers.
-
Check time critical parts of your code and modify it if needed.
-
Variables pinned to absolute positions in the access bank may cause conflicts with the SDCC
memory layout and have to be moved.
-
Check arithmetic operations on typed pointers. +1 means "1 byte" for CCSC and
"sizeof(type)" for SDCC.
-
Replace former #inline functions by #define macros where possible and needed.
-
Put each preprocessor directive in a separate line.
-
Insert call to init_irq_priorities() if interrupts are used. See
initprio.c for usage information.
-
If you want to enable one of the low-priority interrupts you also have to add
enable_interrupts(INT_LOWPRIO);
Just enabling interrupts globally is not enough!
-
If you want to use our interrupt dispatcher, insert #include <disptab.c>. For
additional information concerning the dispatcher, read the disptab.c
documentation.
- No floating point operations have been tested so far.
- Check your inlined assembler code.
- CCS inserts BANKSELs where needed, SDCC does not.
-
CCS modifies some commands (makes branches out of gotos if possible); SDCC will not
touch anything. So you may face a slightly different runtime behaviour.
-
Labels used in inlined assembler code have to be unique. As a consequence you might
also face problems when "converting" former #inline functions to
#define macros, when they contain inlined assembler with labels and these
macros are used more than once.
All in all, keep in mind: This port of the SDCC is still in development. So: handle with care. Fragile.
Compatibility files
compat.h
This file provides most of the CCS-builtin function replacements. If you want to know which are
already implemented, have a look at the source. Generally, any of the RS232 related functions is
working, also EEPROM access, ADC, PWM, SPI or functions you would expect in string.h. But
corresponding libraries are in development from the SDCC pic16 core team.
SDCC expects one stack pragma in each project. compat.h adds such a line providing
96 bytes of stack at position 0x380. If you want to set your own stack, modify the first line
of the main file to
#define USERDEF_STACK
#include <compat.h>
compat.h also includes the files compat_defs.h, stddef.h,
stdint.h and compat_i2c.h.
compat_defs.h
This header file contains many definitions of constants and types for CCS compatibility.
stdint.h
Some standard data types are defined here.
delay.c
This file provides the functions delay_us and delay_ms and so may only be
included once. The code relys on some macros, so if you want to use the functions, include it the
way convert.pl does.
disptab.c
This source file contains our interrupt dispatche and also includes the requrired
initprio.c.
If you want to use our interrupt dispatcher,
insert #include <disptab.c> at the end of your main file. If you want to pack the
irq handlers into separate object files, you have to copy all #define macros
convert.pl created from the former #int pragma
in front of #include <disptab.c> so the dispatch table is generated properly.
If you want to write your
own dispatcher, change the first line of the main file to
#define OWN_IRQ_HANDLERS
#include <compat.h>.
initprio.c
The init_irq_priorites() function provided in initprio.c sets the interrupt
priotiy registers according to the #define X_fast macros, generated by
convert.pl from the #int directives of CCSC. It is
included by disptab.c. If you compile everything at a glance, it
is enough to include this file at the end of the main file. If you want to pack the irq handlers
into separate object files, you have to copy the #define X_fast macros in front of
#include <disptab.c> so the code is generated properly.
Call init_irq_priorities() once before enabling interrupts in your code.
compat_i2c.h, software_i2c.c, software_i2c.h
These files provide functions to access the I2C bus. compat_i2c.h is included
by compat.h, the other files have to be included manually. The code relys on many
preprocessor macros, include them the way convert.pl does.
Example
You can have a look at this example to get an impression of the
conversion.
General hints
- Use the function attribute wparam where possible to increase speed
-
Use unsigned literals where possible. If you compare an unsigned int like
if (i < 3) ... use if (i < 3U) ... to avoid signed compares and get
higher execution speed.
-
SDCC is still under development. If you face a crash or other problems, have a look at
the SDCC bug tracking system
to see if is already known. You can also contact the SDCC mailing lists, especially since
the documentation is still rather incomplete.
Links