Cross-compiling code to run on different architectures can present challenges due to differences in instruction sets, calling conventions, and runtime environments. When cross-compiling Cortex-M1 code to run on a Processing System, there are several potential issues to be aware of.
The Cortex-M1 and Processing System have different instruction sets. The Cortex-M1 uses the ARM Thumb instruction set, which has a reduced set of instructions and uses 16-bit encoding. The Processing System uses the ARM instruction set, which has more instructions and uses 32-bit encoding. This means code compiled for one cannot run directly on the other without recompilation.
Calling conventions may also differ between the architectures. The Cortex-M1 uses the ARM Architecture Procedure Call Standard for passing arguments and return values between functions, while the Processing System may use a different convention. Code relying on a particular convention will need to be adapted.
The Cortex-M1 is a bare-metal microcontroller environment while the Processing System runs an operating system like Linux. There are OS system calls and runtime library dependencies to consider when porting. For example, the startup sequence, stack setup, and OS primitives for threads and I/O will be different.
Some specific issues that may arise when cross-compiling Cortex-M1 code to run on a Processing System include:
Data Types and Endianness
The Cortex-M1 uses little endian memory layout while Processing System can use bi-endian. Data types like ints may have different sizes. This can lead to data serialization and communication issues between the systems. Endianness mismatches will need to be handled.
The Cortex-M1 does not require aligned memory access while Processing System often requires alignment for performance reasons. Unaligned accesses may abort on Processing System. Enforcing stricter alignment when compiling can avoid this.
Floating Point Arithmetic
The Cortex-M1 uses software floating point which is much slower than Processing System’s hardware floating point unit. Floating point heavy code will need optimization, and floating point results may differ due to precision differences.
Boot and Runtime Initialization
The Cortex-M1 boot sequence and runtime startup are handled by the microcontroller vendor. Processing System starts up Linux, loads services, mounts filesystems, etc. before application code runs. Ported code will require changes to work within the OS environment.
Interrupts and Concurrency
The Cortex-M1 handles interrupts differently from Processing System’s preemptive multitasking OS. Concurrency issues like race conditions may appear when ported to Processing System. Additional synchronization and locking may be required.
Memory Management and Access
The Cortex-M1 has flat address spaces while Processing System uses virtual memory with MMU. Pointer arithmetic and memory access patterns will change under virtual memory. The OS also limits memory access which may break assumptions.
The Cortex-M1 accesses peripherals via memory mapped registers. Processing System abstracts peripherals behind OS drivers and services. Accessing I/O registers directly will not work and need to be converted to OS calls.
Debugging and Diagnostics
Debugging on Cortex-M1 uses JTAG and debug registers. Processing System debugs via Linux tools like gdb. Debugging and diagnostics code will need to be ported to the Linux tools.
Timing and Delays
The Cortex-M1 relies on simple delay loops for timing. Processing System runs an OS where thread timing is non-deterministic. Tight timing code must be rewritten to use OS timers and sleeps.
Code will need to be recompiled with a Processing System cross compiler instead of the Cortex-M1 compiler. Compiler flags and link settings will be different and may require tuning for best performance.
Parameter passing, return values, stack management, and caller/callee responsibility are done differently between architectures. Shared code will need to be made agnostic to the calling convention.
Any assembly code is architecture specific and will require rewrite or removal during porting. Hand coded assembly for performance or hardware access will be a key area of change.
While cross-compiling Cortex-M1 code to run on a Processing System presents challenges, with careful planning it can be done successfully. The keys are having an understanding of the architectures’ differences, isolating architecture dependent code, and having an incremental porting and test strategy. With attention to the details covered here, a Cortex-M1 application can be modified to work reliably on a Processing System.
Some strategies to consider for smooth cross-compilation include:
- Isolate architecture dependent code like bootstrapping and hardware access into separate modules or layers that can be adapted. Use abstraction to hide hardware specifics.
- Use endianness and size agnostic data types like sized integers i.e. int32_t. Avoid assumptions about type sizes.
- Enforce memory alignment when compiling for Cortex-M1 to match Processing System expectations.
- Use compiler pragmas and flags to enforce architecture compatibility.
- Use only compiler supported floating point operations rather than software float libraries.
- Test often on Processing System hardware to catch issues early.
- Analyze compiler warnings carefully for hidden architecture assumptions.
- Use defensive programming techniques to protect from unexpected errors.
- Design with portability in mind from the beginning to minimize system dependencies.
With upfront investment in portable design and abstraction, cross-compilation between deeply embedded systems like Cortex-M1 and Processing System class machines can be done successfully. The processor architectures may be very different, but with the right preparation the migration can go smoothly.
For help getting started or more tips on porting Cortex-M1 code to Processing System, search for guides on cross-compilation for the specific OS being used. There are often excellent community resources, tutorials, and open source tools available to ease migrations between architectures. With some adaptation, a Cortex-M1 application can be made to run reliably on desktop class hardware while still leveraging past embedded software investments.
In summary, the key cross-compilation issues between Cortex-M1 and Processing System involve differences in instruction sets, data representations, calling conventions, hardware access, timing, tools, and assumptions around memory usage and behavior. Careful abstraction of hardware dependencies and stepwise validation on Processing System can mitigate the challenges. Despite the effort required, cross-compiling to Processing System allows embedded code to benefit from the improved performance, flexibility, and wider ecosystem of desktop platforms.