# PDI-2025: Practice 0. Description of the development environment. Cross compilation, building and running programs. ## Objetive <div style="text-align: justify"> The objective of this practice is to understand the operation of the development and test environment that will be used for the realization of the practices. This environment is installed in a virtual machine with a Linux Ubuntu 22.04 (Jammy Jellyfish) distribution, although this configuration can be transferred to other Linux distributions without changes. </div><br> <div style="text-align: justify"> In order to set up the development environment, please follow this <a href="https://hackmd.io/@dasilvin/PDI-LAB">LINK</a>. </div><br> ![](https://i.imgur.com/XhW6JrK.png) <div style="text-align: justify"> As is shown in the figure above, the configuration that is described corresponds to a cross-compilation environment where binaries are generated for a different system than the one used for writing and compiling the programs, in this case a RISC-V platform. </div><br> ## Environment Overview ![](https://hackmd.io/_uploads/Bk52Pr-ph.png) The elements of the development and test environment can be seen in the figure above and are: * Eclipse C editing environment * Cross-compiler for RISCV processors * SPIKE: RISCV system simulator The goal is to have a good understanding of the configuration so that it can be successfully replicated for the following practices. ## ECLIPSE editing environment It is recommended to remember the concepts already used in the course *Fundamentos de la Programación* regarding the use of eclipse to compile and execute C programs. ([https://hackmd.io/@parraman/prog-lab-gfie-practica-0#Entorno-integrado-de-desarrollo-Eclipse](https://hackmd.io/@parraman/prog-lab-gfie-practica-0#Entorno-integrado-de-desarrollo-Eclipse)). ![](https://hackmd.io/_uploads/HkB-dS-p3.png) The figure above shows the main eclipse screen with the P0 project displayed. It shows the following main elements that will be described below: * ```include``` folder with the header files (*\*.h*) of the application * ```src``` folder with the application source code files (*\*.c*) * Files *debugP0*, *runP0*, *spike.cfg* and *uart_demo.yaml* The *main.c* file contains the program entry point and makes use of the *printf* function to print a message on the console. This function accepts a parameter list equal to *printf* and can be used to print execution traces of the programs to be developed. ```c= #include "log.h" int main() { printf("Programación de Dispositivos e Interfaces\n"); return 0; } ``` The files *debugP0*, *runP0*, *spike.cfg* and *uart_demo.yaml* will be explained latter. ## Build Configuration of the P0 project In eclipse you can define different configurations when building (*build*) an executable. Through the option **Project -> Build Configurations -> Manage** you can access to the defined configurations. Among them there will be an active one that will be the one being applied. :::info :information_source: Usually there will be Debug and Release configurations. The first one will be used for the development and debugging of the code and the second one to generate the final version. ::: ### Debug Configuration, properties of P0 project The **Project -> Properties** option is used to access the properties of the active configuration. It defines various aspects related to the configuration of the cross-compilation process. ### Definition of build tools (Tool Chain) ![](https://i.imgur.com/XiD3bfb.png) In the **Settings -> Tool Settings -> Cross Settings** section you can see the declaration of the prefix of the compilation tools (*Tool chain*) to be used and the system path where they are installed. In this case: * Prefix: riscv-gaisler-elf- * Path: /opt/ncc-1.0.3-gcc/bin :::info :information_source: Eclipse adds to the prefix the name of the tool it needs to use at each time. So when compiling C code it adds *gcc* to the prefix and forms ```riscv-gaisler-elf-gcc``` to get the name of the compiler. For assembly code (*\*.S*) adds *as* to the prefix and form ```riscv-gaisler-elf-as``` and so on with all the tools in the tool chain. Look at the ```/opt/ncc-1.0.3-gcc/bin``` directory and see how many tools are available and try to find out their functionality. ::: The **Settings -> Tool Settings -> Cross GCC Compiler -> includes** defines the folders where the compiler will look for the *header* files that are included (```#include```) in the code. There can be several folders, in this case it is ```$(workspace_loc:/P0/include)```. That is, starting in the folder where the workspace is located, the *include* folder of the *P0* project. ![](https://i.imgur.com/poCmrbn.png) Note other compilation options such as those related to optimization or *warning*. In addition to the compiler, there are other configuration aspects that apply to the *Linker* and the *Assembler*. ### Build project P0 By the option **Project -> Build Project** the project is build. If there are no errors, an output similar to the one shown in the following figure should be displayed, informing about the compilation of the files that have been modified, in this case *main.c* and the link of all the code (*Linker*) to generate the program binary. ![](https://hackmd.io/_uploads/r1UZYBWT2.png) :::info :information_source: Remember to save the source files when making modifications. Otherwise you will be compiling a previous version and the changes will not be reflected in the binary. ::: If there are compilation or linking errors in the code, they will also be displayed in this window. For example, remove the character at the end of the ```printf("Programming Devices and Interfaces");``` line and recompile. The output should look similar to the one shown in the following figure. ![](https://hackmd.io/_uploads/rJ98Yr-pn.png) :::warning :warning: If you have many compilation errors, look only at the first one and correct it. It is likely that many of the following errors were caused by the first one. In these cases pay special attention to closing quotation marks, parentheses, brackets and other language elements in pairs. In the case of linking errors, it is likely that the names used in the invocation of a function do not match the name that is declared. ::: ## Steps to be followed for the execution of the project Once the project has been compiled without errors, we will proceed to its execution. To do this, select the project in the project explorer (*Project Explorer*) located on the left side of eclipse. Right click on it and a contextual menu will appear, select the option **Show in Local Terminal -> Terminal**. ![](https://hackmd.io/_uploads/Sk9sFHZ6n.png) This operation opens a terminal in a new eclipse tab. The current folder is the P0 project folder, in the figure ```atcsol@vmatc:~/eclipse-workspace/P0$```. In this terminal you can type commands, for example *ls* which displays the contents of the folder. Note how the contents match the folders and files displayed by the project explorer. Type ```./runP0``` to run the previously compiled binary. Basically its mission is to launch the PDI simulator and tell it which binary to run, in this case the P0 project binary. ```./runP0``` is a script whose content you can view and edit. In successive practices we will modify this script to adapt it to the new projects of the practices. ![](https://hackmd.io/_uploads/HJ-B5BWa2.png) As you can see the program executes and prints in the terminal the message indicated in ```main``` using ```printf```. Although not used in this practice, the runP0 script also launches the simulator graphical interface. ![](https://hackmd.io/_uploads/HyH_cSW6h.png) ### Suggested Modifications Modify the message text in ```main```, compile and run the program again and see if the new message is printed. You can also place more than one call to ```printf```. ## Student work As an exercise, it is proposed an extension of the code. The tasks to be carried out are the following: 1. Creation of a new source file (```factorial.c```) in the *src* folder 1. Creation of a new header file (```factorial.h```) in the *include* folder` In the *factorial.h* file add the following lines of code that define the prototype of the C function *factorial*, this function receives as parameter a 32-bit unsigned integer and returns as result a 32-bit unsigned integer. ```c= #ifndef FACTORIAL_H #define FACTORIAL_H uint32_t factorial(uint32_t); #endif /* FACTORIAL_H */ ``` :::info :information_source: The lines: ```c #ifndef FACTORIAL_H #define FACTORIAL_H /* Source code */ #endif /* FACTORIAL_H */ ``` are known as conditional compilation directives. If ```FACTORIAL_H``` is not defined, it is defined and the block code is included for compilation. If it is already defined, because the ```FACTORIAL_H``` has already been included, the block code is not compiled. This avoids that in case this file is included several times, an error of *redefinition* of constants, new types, etc. will occur. ::: In the file *factorial.c* add the following lines of code that implement the functionality of the *factorial* function ```c= #include "riscv_types.h" #include "factorial.h" uint32_t factorial(uint32_t num) { if (num == 0) return 1; else { return num * factorial(num - 1); } } ``` :::info :information_source: The *riscv_types.h* header containing the *uint32_t* type declaration is included ```c #ifndef RISCV_TYPES_H #define RISCV_TYPES_H typedef unsigned char byte_t; typedef unsigned short int word16_t; typedef unsigned int word32_t; typedef unsigned long long word64_t; typedef signed char int8_t; typedef signed short int int16_t; typedef signed int int32_t; typedef signed long long int64_t; typedef unsigned char uint8_t; typedef unsigned short int uint16_t; typedef unsigned int uint32_t; typedef unsigned long long uint64_t; #endif /* RISCV_TYPES_H_ */ ``` :information_source: Also included is *factorial.h* which includes the function prototype definition. Note that the *factorial* function is recursive, that is, it calls itself. Recall that $fact(0) = 1$ and $fact(n) = n * fact( n-1)$ ::: In the file *main.c*, call the function *factorial* and print the result. ```c= #include "log.h" #include "riscv_types.h" #include "factorial.h" int main() { uint32_t fact; printf("Programación de Dispositivos e Interfaces\n"); fact = factorial( 5 ); printf("Factorial: %u\n", fact); return 0; } ``` ### Student work steps 1. Compile and run the program to observe its output 2. Modify the function invocation in *main* to calculate the factorial of another number, for example ````fact = factorial( 4 );```` 3. The result of the factorial function grows very fast. What do you think is the maximum value that can be calculated with this implementation?