Writing an application program


2012-03-14Publicerad av Sven-Åke Andersson

Introduction


A processor without a program to execute is like a car without gas. It is useless. We will write our first c-program, compile it, run it in the OR1K simulator and in the real system. We will use the OpenRISC GNU toolchain which we have already installed.

The OpenRISC GNU toolchain


The newlib-based toolchain contains the standard GCC libraries of libgcc and libstdc++-v3 and those included in newlib; libc, libm and libgloss. The OpenRISC port provides a set of support functions for controling basic CPU functions. The libgloss port provides basic board support that should allow easy addition of new boards.

Bare metal support


The idea behind a compiler providing bare metal support is that it allows users in the process of developing OpenRISC systems to quickly compile simple programs and have them run on the board. By providing access to a basic C library and UART I/O, which can help test the basics of the system through hello-world type applications and other custom diagnostics C programs the developer can quickly determine if it is functioning as expected. It is anticipated all boards builds included in ORPSoC will have their boards also supported in libgloss, which provides a quick and easy way for users of the board ports to compile and run code on the system to check things are operational.


The c-program


Here is the program we are going to use. It calculates all prime numbers in the range we specify.




 

Create a board file


There is no board file for the Atlys board so we have create one. We can download a template file from here and rename it to atlys.S and make the changes needed. The only thing we will change is the board frequency to 50000000.


/*

* Define symbols to be used during startup - file is linked at compile time
*
*/
.global _board_mem_base
.global _board_mem_size
.global _board_clk_freq

_board_mem_base: .long 0x0
_board_mem_size: .long 0x800000

_board_clk_freq: .long 66666666

/* Peripheral information - Set base to 0 if not present*/
.global _board_uart_base
.global _board_uart_baud
.global _board_uart_IRQ

_board_uart_base: .long 0x90000000
_board_uart_baud: .long 115200
_board_uart_IRQ: .long 2


Compile the board file using this command: or32-elf-gcc -c atlys.S
Archive the compiled file using this commamnd: or32-elf-ar -rs libboard.a atlys.o
Copy the libboard.a to the directory "path_to_toolchain"/or32-elf/lib/boards/atlys



Compiling the program


At present the newlib libraries are not linked by default. The following command is used to compile our program:
or32-elf-gcc -mnewlib -mboard=atlys PrimeNumbers.c -o PrimeNumbers
The default linker script will place the code in RAM starting from address 0x0.
 


Generate an assembly listing


To study the generated code we can create an assembly listing using the following command:

or32-elf-objdump -d PrimeNumbers
 

 

Using the Or1ksim simulator


Or1ksim is a generic OpenRISC 1000 architecture simulator capable of emulating OpenRISC based computer systems. Or1ksim provides a range of features:
 
  • Free, open source code, which can be used as a standalone simulator, or as a library.
  • High level, fast, architecture simulation that allows early code analysis and system performance evaluation.
  • Models of all major OpenCores peripheral and system controller cores and easy addition of new peripheral models.
  • Easy configuration for different environments (memory configurations and sizes, OR1K processor model, configuration of peripheral devices).
  • Remote debugging via TCP/IP with the GNU Debugger (GDB) using either the GDB Remote Serial Protocol or through a modeled JTAG interface.
  • A library interface allowing simple OSCI TLM 2.0 SystemC integration.
     
A user guide can be downloaded from the OpenRisc download page.
 

Board configuration file


Or1ksim is configured through a board configuration file. This is specified through the -f parameter to the Or1ksim command, or passed as a string when initializing the Or1ksim library. Every board has a configuration file describing the hardware setup and other parameters. The Atlys board configuration file can be found here:

 


 

Running a simulation


Here is the command to run the PrimeNumber program in the or1ksim simulator:

or32-elf-sim -f $ATLYS_HOME/sim/bin/atlys-or1ksim.cfg PrimeNumbers

 





 
 

Getting help


Use this command to display all options that can be used in the simulator: or32-elf-sim -h

 


 

Program tracing 


Adding the -t option will display all instructions executed during the program run.

 

 
 

Running in the RTL simulator


We can also simulate our program in the RTL simulation environment we used in the last post. The compiled program must be converted to a format that can be loaded into the memory model. Here is how it can be done:
 
  1. Convert the compiled code to binary format
  2. Convert the binary format to vmem format.
  3. Load the vmem file during simulation

Here are the commands used:

or32-elf-objcopy -O binary PrimeNumbers PrimeNumbers.bin
/opt/home/svan/projects/orpsocv2/sw/utils/bin2vmem PrimeNumbers.bin > PrimeNumbers.vmem





The PrimeNumbers.vmem file is copied to the isim directory and renamed to sram.vmem and we are ready to start the RTL simulation.

Writing a program to access GPIO


Byron asked the following question in the OpenCores forum: Do you know how to access the GPIO pins from C or C++? 

Let's find out. The GPIO block is attached to the wishbone bus and has its own address range. We find the address in the board.h file:



 





The GPIO base address is 0x91000000. To find out the GPIO register setup we look in the rtl directory and find gpio.v






Now we have all  the information we need and can write our c-program:



 

We compile the program and generate the sram.vmem file to be included in the simulation. Here is the result from the RTL simulation.

 


Did you notice


You may wonder why the direction registrer holds 0x3fffff instead of 0xffffff. Here is the explanation taken from the gpio.v file:
 



Running on real hardware


We are ready to download the program and run it on our system. There is just one catch, we don't have a simple method to do it. We would normally use the gdb debugger, connect to our target and download the program to the SDRAM and then run it using gdb commands. The OpenRISC gdb debugger <or32-elf-gdb> will not work with the Xilinx USB cable and there is no other debug solution available. We have to find another way to load and run the program.

Loading the program from SPI flash memory


Using the Xilinx IMPACT tool we can load the program to the SPI flash. We only need a method to copy the flash content to the SDRAM and start executing the program. We have to install a bootloader program in boot rom.
 Here is how we will do it:

  1. Generate the bootloader program
  2. Add the bootloader in boot rom
  3. Synthesis the RTL code
  4. Map and place & route
  5. Generate bitstream
  6. Configure the FPGA using iMPACT
  7. Write the application program
  8. Compile the program
  9. Generate the flash prom image
  10. Load the SPI flash using iMPACT
  11. Hit the reset button on the board
  12. Hopefully the program will run successfully

Let's find out if this will work.

Running a RTL simulation


We will start by running a RTL simulation to figure out what exatly is going on.  We will use the same setup but change the bootrom code and disable the preloading of RAM.


Fixing the bootrom code


The boot loader program can be found in the  file:  $ORSPSOC_HOME/sv/bootrom/bootrom.S . Here is the beginning of the code.



The compiled and converted code looks like this:




 
This code can now be added to the rom.v file.
 
 


 

Adding the flash image code


Before we can simulate the setup we have to prepare the flash image of our application program. This data file will be loaded to the flash memory model during simulation and then copied to the system RAM. The flash image used is stored in a file called flash.in which must be stored in the simulation work directory (isim). We will use a dummy image looking like this:



 

The first four bytes holds the size of the file in bytes including the first four bytes. The file will be loaded from the flash memory model (AT26DFxxx.v). The bootloader program starts reading from address 0x1c0000 in the SPI flash and therefore we will store the image starting at address 0x1c0000.


 

 

Here is the result from simulation. We have a working bootloader.