Labels

Showing posts with label C. Show all posts
Showing posts with label C. Show all posts

Tuesday, 30 June 2020

Bare Metal program test

Previously we have managed to implement terminal i/o and libraries into our bare metal environment but as yet there is no breakthrough on setting up stdin, stdout or file i/o.  I will write more if / when problems are resolved but in the meantime we should try some programs.

Our simple starting point is to check whether a number is prime.
The first cut was to loop round trying all possible divisors upto the square root of the subject number.
The program was written on an RPi running linux first.  Then it was transferred to WSL where we compile using arm-none-eabi-gcc for bare metal.  In its simplest form it was quick to implement and test.
The bare metal programs we previously had working were re-organised so that the initial program (notmain.c) sets up uart i/o.  It calls a function jmain.c, which is intentionally not called main to avoid the compiler misinterpreting it.  jmain.c contains our program code with printf statements replaced by uart_puts to output strings to the serial terminal.

The second iteration improves the algorithm slightly, don't test even divisors, make test_prime() a function.  We also loop to ask the user for prime numbers.  We change the linux version of the program so it is easy to convert:
  use sprintf(buffer,.......); printf(buffer); if variable values need to be output for uart_puts convesion
  use gets(buffer); sscanf(buffer,.....) for input so uart_gets can be added on conversion.
Now we can easily enter and test programs under linux before a minimal conversion to run bare metal.


It is much more appropriate to calculate lots of prime numbers.  The program was amended to ask the user now many primes are required.  These are then calculated and printed.  To allow a flexible number of primes malloc is used to create an array of upto 1,000,000 primes which can then be calculated and printed on the screen.  
Previously I had no luck in implementing printf but for some unknown reason this started working so I updated my program to use printf and scanf, which makes life simpler.

To calculate 1,000,000 takes approximately 30 minutes on bare metal RPI 1B.



Tuesday, 9 June 2020

RPi Bare Metal C - Hello World

Background

Bare metal programming has a back to nature feel about it.  We have unimagineable amounts of software, interacting in complex ways when we want to use computer hardware.
On 8-bit processors such as PIC or Atmega you are very close.  A processor chip has a data sheet which you can use to see how you place instructions in memory so they will execute.  You add your own peripheral devices and are responsible for programming them.
The Arduino IDE allows you to program in C on small systems like Uno or ESP8266.  They are not far removed from the hardware, but have thorny implementation details removed and a simple setup/loop framework provided for your C programs.
For "real computers",32-bit or 64-bit devices which run linux (or Windows),  and are capable of running many tasks simultaneously you are far, far removed from the hardware.

Bare Metal computing on RPi allows you to rediscover the hardware in all its gory glory.
Programming in assembler is a mugs game but in fact only a tiny amount of standard assembler code is required.  Once the C environment is setup and you have a cross comiler to hand, you can write C programs without an OS.

Environment


My starting point is a RPi 1B.  RPI2 or RPI3 would be suitable but I don't need their extra power or complexity.
Not all startup steps on RPi devices are open source, we do know that after initialising hardware an RPi1B loads a program kernel.img from a FAT formatted SD card and starts the ARM processor.
Our focus is to compile an appropriate program, name it kernel.img and place it on an SD card so that it will execute.

We need a cross-compiler that will generate ARM code so I find it most practical to use GCC on Windows under WSL.

A variety of like-minded people have kindly provided tutorials.  They include valversJake Sandler, osdevS Matyukevich, BZT and David Welch have put a lot of work into explaining the intracacies to help you get started.
For RPI1B David Welch's tutorial is absolutely perfect.  His writeups are short but packed with pertinent details and his examples work faultlessly.

Blinking LEDs

Our first problem, as we start our program is that our bare RPIB has no software drivers.  As is traditional on embedded systems our objective will be to make an LED blink.

David Welch's example blinker01 provides an assembler code stub and linking script so that the program is loaded at location x8000 and initialises stack pointers and registers for C.  The C program then takes over and initialises GPIO16 (connected to "OK ACT" LED on board).
GPIO header details need to be included in the program to provide appropriate addresses for controlling the GPIO sub-system.  Finally GPIO16 is configured, enabled and set to 0/1 causing the LED to blink.

This is a huge step forward; we are running a program without any external threads or libraries, directly controlling the hardware.  In fact the executable kernel.img is only 148 bytes.  

Hello World

This is the traditional first program for most environments, printing out a message on the screen.  It is so much easier to develop programs when you have a method of communicating back to the user what is happening.  Flashing LEDs quickly lose their sparkle when they are the only way you have of understanding what the processor is doing.
Of course we don't have a screen to write output to.  We certainly don't want to delve into controlling the RPI HDMI output and GPU at this stage, but luckily we can use the RPI inbuilt UART (GPIO14/GPIO15) to send to a terminal/minicom/Putty session.  The GPIO TX/RX and GND pins were connected to an FTDI connector with a USB cable attached to Putty on the PC.

My first attempts at programming the UARt failed to work,  I don't know why.  I tried different tutorials, RPIs, compiler options, terminal connections, all without success.
On reading that the mini-UART is easier to program I tried this instead.  I was relieved and amazed when I put David Welch's UART01.bin on the SDcard and started it up to find it works perfectly, displaying digits as fast as it can.  The C program includes UART headers and initialises the UART.  It then has a simple putc function to output characters.
It is a small extra job to read input from the terminal session and echo characters out to the screen.  We can now do terminal I/O, another huge step forward.

Bootloader

Each time I want to test a program I have to take it out of the RPI, copy across a new kernel.img, replace in RPI and restart.  This quickly becomes irritating.  The marvellous David Welch has written a simple bootloader program.
To use this you put bootloader06.bin as kernel.img on the SDcard.  Now whenever you power up RPI a bootloader is started.  The bootloader waits for a program to be transferred across the serial link using xmodem file transfer.  I used minicom or ExtraPutty to transfer the file.  Pressing 'g' causes the program to run.  To use a different program simply power cycle the RPI and load another executable.
This is another huge step forward for me.  It gives me a practical environment to work in.


Monday, 4 September 2017

RPi cross-compiler

If we will be doing bare metal programming we need a cross-compiler.  Usually, when you compile a program you are expecting to execute it on the same machine or same type of system.  If I compile a C program on Windows I expect it will run on my Windows computer or other Windows computers but not on a linux system or a Mac.  A compiler which can generate programs for another system is a cross-compiler.
A compiler has to look at a program and translate it.  This is done in (at least) two stages, the first compiles the program into assembly language and the second assembles it into machine language.  For example a program snippet:
     if (letter='Z')
      {position=26;}
could compile to something like:   which is then assembled to machine code binary:
  load R1,'Z'                                          111100011111100011
  load R2,letter                                      
111100101111100011
  compare R1,R2                                   
0000110010101010
  branch-not-zero label$                       0001110000000000
  load R1,26                                          
111100101111100011
  store position, R1                                01110000001101010
 label$
The assembly code is specific to a system type and versions for Windows and Mac would have different instructions.  A new compiler version has to be written for each system.  Typically the assembler converts each line of code into a binary machine instruction so that a binary file can be loaded into the target system.  A cross-compiler needs to be told what the target system it is so that it can create appropriate binary files.

In addition to a cross-compiler we need some extra utilities to run programs a linker (to join the program with appropriate library routines) a loader (to copy the program on to the target system) and others.

Windows and linux systems are rather different so a Windows cross-compiler toolchain which generates RPi code is not terribly straightforward.  The easiest way to compile bare metal programs was to compile them on a RPi running linux and run them on another.
The  GNU (open source) C compiler gcc provides the necessary facilities to cross-compile so what we need is an environment to run gcc and the appropriate Raspberry pi definitions.
There are various options for compiling under Windows and I will look at some of them.  The main ones cygwin and mingw provide a linux environment allowing gcc to be used but there are others which I will try.

Sunday, 3 September 2017

Bare Metal RPi Intro

Bare Metal programming allows programs to be run on hardware without the need for an Operating System. It is the norm for microprocessor chips such as Atmel and PIC.  The Arduino IDE makes this particularly easy for Atmel chips by providing a mechanism to load programs and many sample working programs which can easily be executed on an Arduino.
By contrast more powerful hardware systems run Windows, MacOSX, GNU/linux or other OS variants.  Raspberry Pi (RPi) comes under this heading, it is often loaded with Raspbian which is a full featured version of Linux tailored for RPi hardware.
It is interesting to consider how to use an RPi without an OS, in particular to better understand how a system starts up, how the components in an RPi communicate with each other, what tools are needed to build basic systems and discover what the different components do.  It would be a huge amount of work to write an OS myself although this was an objective of the nandtotetris tutorials (which I will write about sometime).
Googling gives a good range of information available for RPi.
 Baking Pi - OS development by Alex Chadwick from the Cambridge University Computer Laboratory provides an excellent starting point.  Lesson 1 guides you through writing an assembler program to light the LED on RPi.  Further lessons expand knowledge of assembly language programming and move on to writing to the screen.
Valvers provides a four-step tutorial based on the content of the Baking Pi project which allows you to write C programs and uses a much simpler platform to cross-compile programs.
David Welch appears to have a variety of bare metal programs including  serial port (UART) programming which should be interesting.
Circle appears to provide 30 steps / examples which I will investigate.
OSDev.org  is a serious bare metal development site which appears to provide a small but proper kernel for development.

A number of unusual challenges face us when starting bare metal programming.
  1. How to compile a program to run on a different computer?
    Compilers are usually designed to compile programs on the computer you are using so what environment allows you to develop programs to run directly on an RPi.
  2. How to load and run an executable?
    The basic approach is to load the program onto an SD card and use the same procedure used to load linux, but it is also possible to load via a serial port (David Welch).
  3. How to write programs that don't need an OS?
    It is possible to use the special version of C used for writing the linux kernel and low level assembler libraries can be used.
  4. Where can the information on the relevant hardware, interfaces and libraries be found?
    This is crucial information which the tutorials and forum posters can help to provide.