* Avoid using floats or doubles, since there is no floating point hardware. Floating point emulation does work in CHDK code. There is potential for issues passing floats or doubles between Canon ROM and CHDK code. It is not clear if this has been tested, since very little of the canon ROM uses FP.

* Avoid using floats or doubles, since there is no floating point hardware. Floating point emulation does work in CHDK code. There is potential for issues passing floats or doubles between Canon ROM and CHDK code. It is not clear if this has been tested, since very little of the canon ROM uses FP.

+

+

== A Warning About Using gcc Assembly Code in C Files ==

+

+

The task files that CHDK hooks into the camera's code are almost completely written in ARM assembler. This code is embedded in the C language files ''boot.c'', ''capt_seq.c'' and ''movie_rec.c''.

+

+

Note that the assembler code mnemonics embedded in those files needs to be upper case letters for the build script that creates the stubs_auto.S file to work properly.

+

== Multi tasking and intertask communication ==

== Multi tasking and intertask communication ==

Latest revision as of 01:07, July 19, 2012

THIS IS A WORK IN PROGRESS

Contents

This page documents best practices and common pitfalls specific to CHDK code and the ARM platform. The aim is to provide developers who are new to CHDK with the information they need to write safe, correct code.

If your code directly manipulates hardware or certain propcases, there is a very real danger that bad code could result in permanent physical damage to the camera. For example, commanding lens hardware when the lens is retracted. If your code interacts with hardware, think very carefully about when the operation should be allowed, and abort or panic if the requirements are not met. High level functions in the Canon generally perform sanity checking, but you should not assume that all functions do.

Most cameras have a total of between 300KB and 1MB available to malloc()/umalloc() after CHDK is loaded. If you need to manipulate large amounts of data, consider working in chunks or hijacking some other part of memory. Keep in mind that the Canon firmware needs to be able to allocate memory too. See CHDK/Camera RAM memory usage for more information.

malloc() / free() operate just like they do in C. umalloc() / ufree() work on addresses for which the CPU cache and write buffer is not enabled. Memory that is directly read/written by hardware should usually be uncached. Other memory should generally be cached.

Notes

Uncached memory should not be used in processing intensive operations, since this will be much slower.

Both cached and uncached memory come from the same pool, they are simply referred to with different addresses. In other words, either one counts against the same 300K-1M mentioned above.

Do not use free() on memory allocated with umalloc or ufree() on memory allocated with malloc()

TODO MANIPULATING ADDRESSES TO REFER TO CACHED/UNACHED MEMORY. ALSO ENABLE/DISABLE/FLUSH

These functions correspond roughly to the unix syscalls of the same name. They operate on file handles, which are numbered starting from zero. Invalid filehandles are < 0. In CHDK, read() and write() must be used only with uncached memory. This is typically obtained using umalloc.

Notes

The uncached requirement means you should not use read() and write() with normal C variables. e.g. int x; read(fd,&x,sizeof(x)); is unsafe.

Not all cameras behave the same way. Just because it appears to work on yours doesn't mean it's OK.

Because uncached memory access is slow, if you are loading data that will be used frequently or for extensive calculations, you should copy it to normal memory. Alternately, use the stdio functions described below.

Internally (in the sig and stubs files) the functions that make up this API are referred to with a capital letter, i.e. _Open, _Read, _Write. The wrappers cause these to be used when you call the lower case variants in CHDK. The low level lower case variants (_open etc) should not be used.

The Fut API provides higher level, buffered io which corresponds roughly to C stdio. CHDK wraps this to provide the normal stdio functions in a way that is mostly compatible with a normal ANSI libc. This API uses FILE * and does not require the user to provide uncached memory.

Notes

Each open file handle allocates a little over 32KB of memory. This memory is freed when the file is closed, but if more memory is allocated (but not freed) between the open and close, memory fragmentation will result. Example

U = used memory, F = free memory.
At the start, you can allocate up to 4 blocks:
FFFF
Open file, using one block:
UFFF
Allocate another block:
UUFF
Close the file. Now you have 3 blocks free, but the largest contiguous amount you can allocate is 2 blocks:
FUFF

The FILE * used by Fut funtions is not compatible with other functions that might take a FILE *.

Some fields of the FILE structure have been reverse engineered, but you should avoid using them directly unless there is no way to get equivalent functionality from the camera APIs.

When calling the original camera ROM from CHDK inline assembler, it is important to understand the B and BL instructions.
From "ARM Architecture Reference Manual" A4.1.5 B, BL

Specifies the address to branch to. The branch target address is calculated by:

Sign-extending the 24-bit signed (two's complement) immediate to 30 bits.

Shifting the result left two bits to form a 32-bit value.

Adding this to the contents of the PC, which contains the address of the branch instruction plus 8 bytes.

1) means that you cannot B or BL directly from a RAM address to a ROM address, since the offset is more than 24 bits. While the assembler may silently accept BL 0x<constant> or BL =0x<constant>, it will treat the value as a 24 bit PC relative offset, truncating or otherwise discarding any additional bits. This will NOT produce a jump to the specified address.

In platform/sub code, this is handled by the build process. C files mentioned in STUBS_AUTO_DEPS are scanned for B/BL sub_<address> and an appropriate stub is added to stubs_auto.S

From other code you may simulate a B instruction with

LDR PC, =<address>

or a BL instruction with

MOV LR, PC
LDR PC, =<address>

Note that MOV instruction has special behavior when PC is the source, such that the two instruction sequence above produces the correct return address.

Functions mentioned in stubs_entry.S or stubs_entry_2.S may also be called directly by name using B/BL.

It is frequently necessary to call a C (or self written asm) function from disassembled firmware code. It is extremely important that you understand the arm calling convention and preserve any required registers.

Example

MOV R0, R4 ; this will be the first argument to the following BL callBL sub_FFD483A4 ; call some function in Canon ROMBL capt_seq_hook_raw_here ; call our function hereMOV R2, R4 ; load up the registers for the next callMOV R1, #1BL sub_FFD43578 ; call another function in Canon ROM; ...

In this code, the call to the C function capt_seq_hook_raw_here is added to assembly code obtained by disassembling the canon firmware.

In the arm calling convention, a C function gets it's first four arguments in R0-R3, and is not required to preserve them. It is required to preserve other registers. Because of this, GCC will generate code for capt_seq_hook_raw_here which leaves these registers in an undetermined state after the call returns.

In the above case, because the call to the new function is added immediately after an existing call, we know that the firmware already expects R1-R3 to be undefined, so the fact that our function might clobber them is unimportant.

We do have to worry about R0, because it is also used for the return value. Given that the firmware loads R2 and R1, but not R0 before calling the final function (sub_FFD43578), we can assume that the return value of the preceding function (sub_FFD483A4) is expected to be the first argument to this call. If our call to capt_seq_hook_raw_here changes R0, problems could result. One easy way to avoid this is to define your function to take one argument and return it unchanged, like this:

This ensures that GCC saves the value of R0, since it will take whatever value is already in R0 as it's first argument, and return it using R0.

Now consider if we put our call a few lines later:

MOV R0, R4 ; this will be the first argument to the following BL callBL sub_FFD483A4 ; call some function in Canon ROMMOV R2, R4 ; load up the registers for the next callMOV R1, #1BL capt_seq_hook_raw_here ; call our function hereBL sub_FFD43578 ; call another function in Canon ROM; ...

This is clearly wrong, because R2 and R1 were set up for the final BL, but now our call is going to (potentially) stomp on them.

The best practice is to insert your call immediately after an existing call, preserving R0. If you want to insert calls into arbitrary assembly code, you have to preserve all registers and possibly the CPU status word.

You can carefully analyze the firmware code to decide exactly what needs to be saved, but keep in mind others might modify your code without noticing these details.

Many functions in CHDK use __attribute__((naked)). It is important to understand what this means. From the GCC manual

naked

Use this attribute on the ARM, AVR, IP2K and SPU ports to indicate that the specified function does not need prologue/epilogue sequences generated by the compiler. It is up to the programmer to provide these sequences. The only statements that can be safely included in naked functions are asm statements that do not have operands. All other statements, including declarations of local variables, if statements, and so forth, should be avoided. Naked functions should be used to implement the body of an assembly function, while allowing the compiler to construct the requisite function declaration for the assembler.

(emphasis added)

Some CHDK functions violate these rules. Other than pure asm functions, the common idiom is

The intent is to save all registers, to allow C code to be called from arbitrary asm. This is wrong, and should be eliminated from the CHDK code. However, it appears to work with the current toolchains if some rules are followed:

No local variables. The compiler will not allocate stack space for them. Statics are probably OK.

No arguments. As above.

No return statements. The stack will not be set up correctly.

Any function using this should be as simple as possible. A much safer way to do this would be putting the register save/restore in the caller, possibly using macros.

On CHDK platforms, long and int are both a 32 bit integer. Pointers are also 32 bits. Short is a 16 bit integer, and chars are 8 bits.

In general, use int or unsigned for variables and function parameters. The arm architecture is such that using shorts or chars is less efficient than using an int.

long is identical to int. Using the long keyword only serves to add confusion. CHDK is not expected to run on systems where int or long are anything other than 32 bits.

For arrays, use the smallest type that has the requisite number of bits.

For structures, alignment considerations limit the value of using chars and shorts.

TODO WHAT IS THE DEFAULT STRUCTURE PACKING ?

Avoid using floats or doubles, since there is no floating point hardware. Floating point emulation does work in CHDK code. There is potential for issues passing floats or doubles between Canon ROM and CHDK code. It is not clear if this has been tested, since very little of the canon ROM uses FP.