C programming for ARM

Asked

Viewed 1,852 times

28

I’ll start a project in C that will have as target a plaque Colibri T20, processor-equipped NVIDIA Tegra 2 ARM, running a light version of Linux.

I wonder if, in addition to having to use a cross compiler compatible with the processor, I need to worry about some other detail specific to the ARM architecture.

From what I read, it seems that the processor architecture only matters if I’m programming in Assembly. I’m wrong?

I don’t know if it’s relevant, but man host is Ubuntu 12.04 (Linux).

  • 3

    In theory, you program into a C-defined abstract machine and it is the compiler’s job to adapt your code to run on the target architecture as if She was this abstract machine. At first you should only have to worry about this if you are using some feature not available in C, but that exists for your processor (e.g., accessing the input/output pins).

  • If your project for Open Source would be extremely interested in participating :)

6 answers

22


You’re right. My observation is that there is a misconception among many developers that porting software in C to a new architecture is difficult. Unless it refers to the type of programming found in operating systems, this is simply not true.

The kernel makes up much of the abstraction of hardware components, so much so that software can often be compiled into one architecture and another without problem. I can think of a few examples, though, when that’s not so easy:

  • Whether the software relies on another proprietary software that cannot in itself be ported. The vast majority of Linux software uses libraries that are already open source, so usually this is not a problem.
  • Whether the software contains architecture-specific optimizations. An example: The x86 architecture has MMX and SSE extensions, among others, whose specific use cannot be directly ported. While this is a concern, this is really rare, because usually the software uses a library that uses these special features. An example would be a library that does video processing. In x86 the library would use these extensions, but in Arm the library would use other extensions that only exist there. The library then assumes the responsibility of porting the implementations to new architectures, the software that uses it need not worry about how.
  • Whether the software deals directly with system-specific hardware. It is usually the kernel’s job to do this, but I have personally already found software that requires certain devices to be available on the system, and so the software does not behave very well. Generally this can be fixed by exchanging this specific hardware dependency with the use of a library that provides the same functionality on multiple architectures.

In addition to these exceptions which you should be aware of, almost every C software port to new architectures on the operating system desk is no problem because, as Guilherme said in his commentary, it is the work of the compiler to adapt the code the right way for each architecture, rarely with the intervention of the programmer.

  • 1

    A very different feature of both systems is graphical acceleration. I don’t know if Colibri T20 has Opengles graphics acceleration, but I believe so. Opengles (ES from Embedded System) is different from traditional Opengl. An Opengl-based application is certainly hardly ported to ARM, due to Opengles. But, generally, if there is no access to low-level devices and always use of libraries that exist on both platforms, the port is done without major trauma.

  • 2

    Port between Linuxes or Unixes, with completely different hardware, is quiet, as long as the code is developed with this in mind. On the other hand, porting a POSIX code to a 16-bit microcontroller or another platform other than a general-purpose operating system is never simple; there is no point in deluding yourself.

9

As stated, in theory yes, but in practice it is difficult to achieve this portability. Since you are using a large-scale embedded operating system, this makes it easier things, but this becomes different when using less widespread systems between architectures, or even no operating system.

Being Assembly one of the lowest level languages, you have great control over the generated code, so it is not uncommon to use Assembly in-line along with C for example.

See this quote taken from http://www.nongnu.org/avr-libc/user-manual/pgmspace.html

The problem is exacerbated by the Fact that the C Language was not Signed for Harvard Architectures, it was Designed for Von Neumann Architectures Where code and data exist in the same address space. This Means that any Compiler for a Harvard Architecture Processor, like the AVR, has to use other Means to Operate with Separate address Spaces.

Free translation:

The problem becomes evident by the fact that the C language was not designed for Harvard architecture, it is designed for architectures von Neumann in which code and data exist in the same address space. This means that any compiler for Harvard architectures, such as the AVR, need to use other means to operate with address spaces separated.

In this case, extensions to the C standard are used to store literals in program memory, which in theory is not common in Harvard architectures.

From what I’ve read, it seems the processor architecture only matters if I is programming in Assembly. I am wrong?

Even in Assembly it is possible to have an abstraction layer. Ex: Calling software interrupts implemented by the operating system, so that it does the abstraction.


Note: this answer does not consider the use of Linux as OS, but rather portability in a more comprehensive way.

7

Not necessarily agreeing or disagreeing with colleagues, I prefer to go another way: it depends.

It depends on the type of application you want to do. Essentially, as already said, the compiler must translate from the language C for the instruction set of target, independent of the architecture used. If you use compilers with standard support ANSI C (as gcc), the language C and the functions themselves POSIX may be written perhaps, without any difference between platforms.

But in addition to the instruction set, there are other differences between x86 desktop (which I’m assuming you have experience) and an architecture ARM. I will assume also that in both cases you use Linux:

  • Embedded hardware: The hardware available for processing is more limited: you have less primary memory and less "processing power".

  • Energy consumption: Depending on the product you are going to make, you must choose to meet some power consumption requirement.

  • Peripherals: Basic peripherals, such as those via USB, should be used in the same way on both platforms, since it is the function of the S.O. and modules to handle them and provide Apis to the programmer. But you may need to access some other peripheral on a low-level access (such as using a SPI or I2C protocol - which is very common on an embedded system), and this should be different on an ARM platform than on an x86 platform.

  • Graphic acceleration: A very different feature from an ARM system to a traditional PC is graphical acceleration. Generally in ARM systems only Opengles (ES Embedded System) is available. Opengles is different from traditional Opengl. Writing an application that uses graphics acceleration for ARM is certainly different than writing one for an x86 PC.

  • Apis, libraries and other software: You should be aware and do a survey of the libraries and external software and Apis you will use, and know if they already exist for ARM and if they work properly on the device you will use.

In general, in my experience, these are the characteristics that most differentiate systems. The difference, in my opinion, goes beyond the set of processor instructions: although software abstractions try to hide the system architecture and leave everything "transparent" to the programmer via the same Apis, the "ecosystem" that surrounds ARM is different from x86. I never had to care about the instruction set itself. If you don’t need to make an ultra-optimized application for that platform, you shouldn’t need to care about the instruction set either.

UPDATING:

The @Alexandremarcondes recalled in the comments regarding the endianess different from both platforms. In his words:

There is also the question of access to memory (little endian and big endian), also called endianess. This is the order that the platform (processor) stores numbers greater than 1 byte, whether or not there is order inversion

Know the endianess is really important for C programming especially if the programmer uses union in its code or data transmission to other peripherals or devices over the network.

  • 2

    There is also the question of access to memory (little endian and big endian), also called endianess. This is the order that the platform (processor) stores numbers greater than 1 byte, whether or not there is order inversion.

  • Well remembered! I don’t know if I can do this, but I will update my answer with this information.

  • I tried to be generic, since there was no more detailed explanation in the question; I only talked about the possibility of using graphic acceleration, since it does not say whether it will use it, but claims to use a card that meets good requirements in this sense; I believed that was a relevant point. Besides, this is just one of the above points. I did not understand your quote about Atmega48p. The OS is theoretically responsible for abstracting access to hardware, but invariably there are differences between platforms (like the ones I mentioned), even though both use Linux.

  • I think I read some other answer about Footprint and I ended up mixing it in the comment about the Atmega48p. About the OS having an obligation to have a HAL, I believe it is only a function, some RTOS do not have and its main feature is precisely the scheduling and switching of threads. But as your answer is Linux-oriented it becomes true in theory.

2

I recently ported the code of a linux-x86-i382 application for linux-armv8, the portability difficulty depends a lot on your application, at the end of the day, the ideal is to use clean interfaces and leave the code that relies on separate architecture in a folder or a file.

In general, the parts of code that are most likely to present errors are parts of IO(files and network), encryption and authentication(checksum, has, etc). The main points that usually cause problems are:

  • Integer Size: If you plan to port your code to different platforms, never assume integer size, for example, a package defined by:

    struct packet {
        int a;
        int b;
        int c;
        int b;
    }
    

It will have different sizes on 64-bit and 32-bit architectures, which means that if this structure is used in a network protocol, the two computers will not be able to communicate. The solution to this is to use (c++) or (c) types that specify the size of the integer: uint8_t, int16_t, etc.

  • Endianess: If using types that use multiple bytes, big endian architectures write bytes in the reverse order of little endian architectures. It is necessary to treat the two codes differently. In this case, there is no trivial solution. One of the ways is to treat all bytes of input as simple bytes and recover multiplex byte structures later, for example:

    Little Endian:
    uint32_t a = 0x1234567890,big_a = 0;
    for(uint8_t i=0; i<4,i++)
        big_a += ((a>>(24-8*i))&0xff) << (8*i);
    send(big_a);
    
    Big Endian:
    a=receive();
    

It is important to note that, according to the RFC1700, bytes must be transmitted over the network in a Big Endian way. However, if you are implementing your own protocol, either, since one of the two sides converts the bytes you received, has a IBM article(in English) on how to deal with endianess.

2

Don’t forget the convention ABI. For example, in EABI ARM you do not send the swi [syscall number], but you have to use the n° 7 register to click on the syscall desired, while the recorders below this (r0 until r6) are used to load arguments from syscall. And finally, you just send the swi #0. Example of writing an output:

mov r0, #1 @ stdout, 0 p/ stdin e 2 p/ stderr
ldr r1, = [label da string ascii]
ldr r2, = [label do length da string acima]
mov r7, #4 @ syscall p/ write
swi #0 @ chama o software interrupt

You can search more on Pdfs found on the ARM website about ARM EABI and APCS like this.

  • Well, I quoted ARM Assembly there, but on the ARM website there are many E-books addressing even about C++, descriptions about Floating Points (in the case of graphic works), etc...

1

Depends on the application. If programming is done using already available Apis, chances are you won’t encounter many problems that have been solved before. But even with Apis if you have to deal with device driver it’s good to know at least the endianess since in C we have the pointers to break any rule and your platform has many devices to worry about. You also need to know the set of instructions you will use to inform the ARM, Thumb, Thumb2 compiler. Your ARM has FPU will enable this?

There are also several toolchains available for ARM. Arm-None-eabi (no OS support, Bare-metal), Arm-linux-eabi (Linux), and so on.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.