By: Fabio Hernández
 

INTRODUCCIÓN:

 
El presente documento pretende mostrar la manera de generar software para una imagen de Linux implementada en la De0nano SOC, para ello se hará un pequeño marco teórico, luego se explicaran los recursos que se necesitan para realizar el tutorial y por ultimo se hará la explicación para generar un programa utilizando periféricos.
 
 
REQUERIMIENTOS:

 
 
MARCO TEÓRICO:
 
  • ARM CORTEX A9:  El procesador ARM Cortex-A9 tiene arquitectura RISC (Reduced Instruction Set Computer). Esta aritmética y operadores lógicas se realizan a través de registros de propósito general. Los datos se mueven entre la memoria y estos registros por medio de instrucciones de carga y almacenamiento.
    El tamaño de las palabras del procesador es de 32 bits, donde el bit más significativo es el que se encuentra mas a la izquierda y los menos significativos mas a la derecha de la palabra.
Actualmente la arquitectura ARM, con diseños en los que se integran más de 30.000 transistores, está presente en un gran número de dispositivos electrónicos utilizados por usuarios particulares y profesionales, y a diario se venden más de 16 millones de productos basados en esta arquitectura de ARM Holdings. 
 
  • LINUX la palabra linux actualmente puede hacer referencia a un núcleo de sistema operativo o a un sistema operativo, dependiendo del contexto. Linux se empezó a crear en 1991 por un joven llamado Linus Torvalds quien mediante simples ideas y con la ayuda de muchas personas lanzó la versión 0.01 de Linux de manera gratuita y a través del tiempo paso de tener 10239 lineas de código a tener mas de 14000000 y su código fuente puede ser descargado de manera gratuita en la red. Linux es famoso por su terminal en la cual se introducen diferentes comandos para tener control del sistema operativo, entre los comandos mas importantes se encuentran:
      • CAT: cat (de concatenar), es una utilidad que nos permite visualizar el contenido de un archivo de texto sin la necesidad de un editor. Para utilizarlo solo debemos mencionarlo junto al archivo que deseamos visualizar: 
$ cat prueba.txt 
      • LS: ls (de listar), permite listar el contenido de un directorio o fichero. La sintaxis es:
$ ls /home/directorio ​
 
      • CD: cd (de change directory), es como su nombre lo indica el comando que necesitarás para acceder a una ruta distinta de la que te encuentras. Por ejemplo, si estas en el directorio /home y deseas acceder a /home/ejercicios, seria:
$ cd /home/ejercicios 
 
Si estas en el directorio /home y deseas acceder a /home/ejercicios, seria:
 
$cd ..
 
      • CP: cp ( de copy o copiar), copia un archivo o directorio origen a un archivo o directorio destino. Por ejemplo, para copiar el archivo prueba.txt ubicado en /home a un directorio de respaldo, podemos usar:
$ cp/home/prueba.txt /home/respaldo/prueba.txt
 
      • MV: mv (de move), mueve un archivo a una ruta específica, y a diferencia de cp, lo elimina del origen finalizada la operación. Por ejemplo:
$ mv /home/prueba.txt /home/respaldos/prueba2.txt
 
      • RM: rm (de remove), es el comando necesario para borrar un archivo o directorio. Para borrar el archivo prueba.txt ubicado en /home, ejecutamos:
$ rm /home/prueba.txt
 
 
Usted puede encontrar mas de 400 comandos en el siguiente enlace: 
http://blog.desdelinux.net/mas-de-400-comandos-para-gnulinux-que-deberias-conocer/


  • DESCRIPCIÓN DE LOS PERIFÉRICOS:  la siguiente imagen muestra los periféricos conectados a la Ciclone V SOC a los cuales se puede tener acceso mediante un sistema operativo que se implemente en el ARM. 
 
A continuación se explicara cada uno de ellos:

  • LTC 2x7 Headeres un header de 14 pines, que es usado principalmente para comunicaciones con tarjetas de la empresa Linear Technology. es conectado mediante puertos SPI Master e I2C del HPS, también se pueden usar como GPIO, SPI, o I2C con el HPS, la conexión entre el HPS y el LTC se muestra a continuación:
 
  • Push-buttons & LEDs: Similar a la FPGA, el HPS también tiene sus propios switchs, buttons, LEds que pueden ser controladas desde la programación interna del HPS. Tenga en cuenta que el push-button al que se puede tener acceso es llamado User push Button y el LED es llamado USER LED. Como se muestra en la imagen:
 
  • Gigabit Ethernet: La de0nano SOC soporta transferencias de datos vía Gigabit Ethernet a través de un chip externo Micrel KSZ9031RN PHY que soporta interfaces RGMII MAC, la conexión RJ-45-Ethernet Chip-HPS se muestra a continuación:
    •  
    • UART: La de0nano SOC tiene una interfaz UART conectada con el HPS. esta interfaz no soporta control de señales por HW. La interfaz física es implementada con un conversor UART-USB a través de un FT232R chip como se muestra a continuación:
  • Memoria DDR3: La memoria DDR3 tiene una capacidad de 1GB con un ancho de banda para datos de 32 bits, las señales deben ser conectadas a través de un controlador dedicado con una velocidad de 400 MHz.
 
  • Micro SD Card Socket: La de0nano SOC tiene una interfaz para una tarjeta Micro SD, con x4 lineas de datos. Esto sirve como un almacenamiento interno para el HPS, pero también puede servir como el boot de un sistema operativo como se hace en una Raspberry Pi. La siguiente imagen muestra la conexión entre la SD y el HPS.
 
 
 
  • MEMORY MAP CYCLONE V SOC:  El HPS de la Cyclone V SOC, maneja diferentes direcciones de memoria que van dirigidas directamente hacia los perifericos descritos anteriormente, para poder por ejemplo apagar y prender el User LED, usted debera apuntar a la dirección de memoría del User LED y escribir en el registro "1" o "0" dependiendo si lo desea apagar o prender, en la siguiente tabla se encuentran todas las direcciones de memoria de los diferentes periféricos:
 
Cada uno de los anteriores periféricos contiene diferentes puertos dentro del HPS, estos puertos se muestran a continuación:
 
De esta manera por ejemplo si se quiere encender el User LED se debe ir a la dirección de memoria del puerto GPIO 62,  esto se hace haciendo uso de los registros
de escritura y lectura propios  de la dirección de memoria correspondiente a los GPIOs, en este enlace se encuentra la descripción de los diferentes registros.
          
Si consultó el enlace se habrá dado cuenta que los registros de datos (valor) y su dirección (lectura o escritura) se modifican a través de dos registros específicos; el gpio_swporta_dr y el gpio_swporta_ddr.



La descripción de estos dos registros se muestra a continuación:


Usted puede ver la aplicación de lo explicado aquí.

 

DESARROLLO:

 
  • LINUX TEST:  
    • Lo primero que se debe realizar es verificar que la tarjeta SD esté insertada en la ranura correspondiente dentro en la De0nano SOC, esta SD tiene una imagen de Linux de fabrica, luego debe conectar el cable USB que antes conectaba para programar la FPGA en el puerto llamado UART, como se muestra en la siguiente imagen: 
 
    • Siguiente conectar el cable USB al computador, ir al administrador de dispositivos y tener el cuenta el número del nuevo puerto COM que se ha generado.
    • Luego abra el Putty y configure como se muestra en la siguiente imagen:
    • Inicie sesión como root, y escriba ls, esto mostrará los archivos que se encuentran en el directorio donde usted se encuentra
    •  Usted puede ver los dispositivos que se encuentran conectados al sistema operativo y su dirección de memoria en escribiendo lo siguiente
 
  • ECLIPSE DS-5 LICENSE: 
Antes de continuar con el tutorial, se debe activar alguna licencia para Eclipse DS-5, para ello siga los siguientes pasos:  
 
 
    • Abra Eclipse DS-5 y vaya a help--> ARM LICENSE MANAGER
 
    • De click en add license y seleccione como se muestra a continuación:
    • De click en Next y y pegue el número que se copió con anterioridad:
 
    •  Seleccione el Hardware mediante el cual se tiene conexión a internet (en este caso se esta conectado a una red Wi-Fi):
 
    • Cree una cuenta o ingrese con una cuenta ya existente y de click en finish:
    • Edite la licencia de acuerdo a sus necesidades y con esto ya tiene acceso a las características del programa necesarias para continuar el tutorial. 
      • Cree la cuenta usando el siguiente link, aveces el link para crear la cuenta no funciona en el formulario de la aplicacion.
 
  • FIRST HELLO WORLD:
    • Para ello debe abrir el programa Eclipse for DS-5 e ir a file--> New Project --> C project y seleccionar GCC 4X como se muestra en la siguiente imagen: 
    • Vaya a la carpeta del proyecto que se generó, dele click derecho --> properties  --> c/c++ build --> settings --> Includes y añada un nuevo path como se indica en la imagen: 
 
    • Vaya a la ruta donde se encuentra la carpeta Altera instalada y añada los directorios que se muestra a continuación:  
 
    • Ahora vaya a a miscellaneus y copie el texto -lm -lpthread y de click en ok como se muestra en la imagen: 

 
    • Agregue un nuevo archivo .c y llamelo main.c como se muestra a continuación: 
    • En el archivo main.c escriba el código que se muestra a continuación y construya el proyecto (Build project) : 
 
    • Usted habrá notado que se generó una nueva carpeta llamada binaries con un archivo binario principal, este archivo es el que se debe portar al sistema operativo con el fin de ejecutar el programa que hemos escrito, para ello vaya a la carpeta, usted puede encontrar la ruta del archivo como se muestra en la siguiente imagen:
 
    • Conecte el cable Ethernet de la De0nano SOC a la red, haga un Warm Reset al Linux y en Putty escriba ifconfig y copie la dirección ip que se muestra a continuación:
 
    • Vaya a Window --> Open Perspective --> Other y seleccione Remote System Explorer:
    • En Local de click Derecho --> New connection --> SSH Only --> y agregue la dirección IP copiada:
 
    • Es recomendable crear un contraseña de acceso al sistema operativo en la terminal de Linux desde el PuTTY como se muestra a continuación:
 
    • Vuelva al DS5 Eclipse, vaya al nuevo proyecto generado y abra la carpeta root, añada User ID root y la contraseña creada en el paso anterior: 
    •     Ahora en la carpeta Root usted tiene acceso a todo el sistema de ficheros de Linux, el siguiente paso es ejecutar el código anteriormente escrito en el sistema operativo, para ello hay dos caminos, el primero es copiar el archivo BSP que se creo anteriormente en la ruta home/root, cambiar los permisos a ejecutable, volver a PuTTY y desde allí ejecutar el programa como se muestra a continuación:
 
    • La otra manera es hacer un debug desde el DS5 Eclipse, para ello debe regresar a la c/c++ perspective, dar click derecho en el proyecto y ejecutar el Build configurations y configurar como se muestra a continuación:
 
    • Una vez de click en Debug se abrirá una nueva Perspectiva, desde allí de click en continue y rectifique que el programa se hallá ejecutado como se muestra a continuación:
 
 
 
  • ACCEDIENDO A GPIOS:
    • Lo primero que se debe hacer es crear un nuevo proyecto llamado ejemplo1 siguiendo los primeros cuatro pasos del item anterior:
    • En el main.c copie el siguiente Codigo

      /*
       * main.c
       *
       *  Created on: Jun 20, 2016
       *      Author: Holguer
       */
      
      #include <stdio.h> 					// Libreria encargada de printf y scanf, comunicaci�n serial
      #include <unistd.h>					// Libreria para realizar usleep (retardos)
      #include <fcntl.h>					// Libreria para realizar escritura y lectura de la memoria
      #include <sys/mman.h>				// Libreria para realizar mapeo de memoria
      #include "socal.h"					// Funciones para escritura en palabras de GPIOs
      #include "hps.h"					// Instancia las direcci�nes de GPIOs del HPS
      #include "alt_gpio.h"				// Configuracion de GPIOs
      #include "hwlib.h"					// Definici�n de hardware
      
      #define HW_REGS_BASE ( ALT_STM_OFST ) // Define HW_REGS_BASE como
      #define HW_REGS_SPAN ( 0x04000000 )   // Tama�o de la region de memoria
      #define HW_REGS_MASK ( HW_REGS_SPAN - 1 ) // enmascaramiento del tama�o de la region de memoria
      
      #define USER_IO_DIR     (0x01000000)
      #define BIT_LED         (0x01000000)
      #define BUTTON_MASK     (0x02000000)
      
      int main(int argc, char **argv) { //
      
      	void *virtual_base;
      	int fd;
      	uint32_t scan_input;
      	int i;
      	// map the address space for the LED registers into user space so we can interact with them.
      	// we'll actually map in the entire CSR span of the HPS since we want to access various registers within that span
      	if ((fd = open("/dev/mem", ( O_RDWR | O_SYNC))) == -1) { // open abre archivos abre la memoria RDWR de forma sincrona ir a consola cd dev -- ls aparece el mem el men contiene todo el elspacio qe el sistema operativo tiene acceso a travez del mmu que es el management memory unit
      		printf("ERROR: could not open \"/dev/mem\"...\n"); // si es -1 el dev men no existe y sale un error
      		return (1);
      	} // de lo contrario en fd se guarda el puntero que apunta a la direccion de dev/mem en fd se guarda ese valor
      
      	virtual_base = mmap( NULL, HW_REGS_SPAN, ( PROT_READ | PROT_WRITE),
      	MAP_SHARED, fd, HW_REGS_BASE); // mapea el mapa de memoria  y se limita para que no tenga acceso a otro lugar con mmap cuyos argumentos se pueden ver en la funcion---vamos a escribir o leer, map_shared significa que es una memoria compartida, fd direccion de memoria, hw_regs_base empiezan registros de perifericos todos inician en fc
      
      	if (virtual_base == MAP_FAILED) {  // si falla envia error
      		printf("ERROR: mmap() failed...\n");
      		close(fd);
      		return (1);
      	}
      	// initialize the pio controller
      	// led: set the direction of the HPS GPIO1 bits attached to LEDs to output
      	alt_setbits_word(
      			( virtual_base + ( ( uint32_t )( ALT_GPIO1_SWPORTA_DDR_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ),
      			USER_IO_DIR);
      	int numero_flashes = 0;
      	int delay = 300;
      	/*if (argc > 1) { // argumento del main
      		if (argc > 2) {
      			sscanf(argv[2], "%d", &delay);
      		} else {
      			sscanf(argv[1], "%d", &numero_flashes);
      		}
      	} else {
      		printf("please use the next input style: ./ejemplo_gpio3 #flashes \n");
      		printf("or input style: ./ejemplo_gpio3 #flashes #delay \n");
      	}*/
      
      	printf("led test\r\n");
      	printf("the led flash 2 times\r\n");
      	for (i = 0; i < 2; i++) {
      		alt_setbits_word(
      				( virtual_base + ( ( uint32_t )( ALT_GPIO1_SWPORTA_DR_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ),
      				BIT_LED);
      		usleep(delay * 1000);
      		alt_clrbits_word(
      				( virtual_base + ( ( uint32_t )( ALT_GPIO1_SWPORTA_DR_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ),
      				BIT_LED);
      		usleep(delay* 1000);
      	}
      
      	printf("user key test \r\n");
      	printf("press key to control led\r\n");
      	while (1) {
      		scan_input =
      				alt_read_word(
      						( virtual_base + ( ( uint32_t )( ALT_GPIO1_EXT_PORTA_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ));
      		//usleep(1000*1000);
      		if (~scan_input & BUTTON_MASK)
      			alt_setbits_word(
      					( virtual_base + ( ( uint32_t )( ALT_GPIO1_SWPORTA_DR_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ),
      					BIT_LED);
      		else
      			alt_clrbits_word(
      					( virtual_base + ( ( uint32_t )( ALT_GPIO1_SWPORTA_DR_ADDR ) & ( uint32_t )( HW_REGS_MASK ) ) ),
      					BIT_LED);
      	} */
      	// clean up our memory mapping and exit
      	if (munmap(virtual_base, HW_REGS_SPAN) != 0) {
      		printf("ERROR: munmap() failed...\n");
      		close(fd);
      		return (1);
      	}
      
      	close(fd);
      	return (0);
      }
      

       

    • Modifique el hwlib.h como se muestra en la siguiente imagen:
    • Genere el proyecto (Build proyect) y ejecutelo siguiendo los pasos descritos anteriormente:
    • El resultado se muestra en el siguiente video:

Vídeo de YouTube

    • Ahora haga las siguientes modificaciones:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include "socal.h"
#include "hps.h"
#include "alt_gpio.h"
#include "hwlib.h"

#define HW_REGS_BASE (ALT_STM_OFST)
#define HW_REGS_SPAN (0x04000000)
#define HW_REGS_MASK (HW_REGS_SPAN - 1)

#define USER_IO_DIR (0x01000000)
#define BIT_LED (0x01000000)
#define BUTTON_MASK (0x02000000)

#define LED_SET() alt_setbits_word((virtual_base + ((uint32_t)(ALT_GPIO1_SWPORTA_DR_ADDR) & (uint32_t)(HW_REGS_MASK))), BIT_LED)
#define LED_CLR() alt_clrbits_word((virtual_base + ((uint32_t)(ALT_GPIO1_SWPORTA_DR_ADDR) & (uint32_t)(HW_REGS_MASK))), BIT_LED)
#define READ_KEY() alt_read_word((virtual_base + ((uint32_t)(ALT_GPIO1_EXT_PORTA_ADDR) & (uint32_t)(HW_REGS_MASK))))

int main(int argc, char **argv)
{

    void *virtual_base;
    int fd;
    uint32_t scan_input;
    int i;
    // map the address space for the LED registers into user space so we can interact with them.
    // we'll actually map in the entire CSR span of the HPS since we want to access various registers within that span
    if ((fd = open("/dev/mem", (O_RDWR | O_SYNC))) == -1)
    {
        printf("ERROR: could not open \"/dev/mem\"...\n");
        return (1);
    }

    virtual_base = mmap(NULL, HW_REGS_SPAN, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, HW_REGS_BASE);

    if (virtual_base == MAP_FAILED)
    {
        printf("ERROR: mmap() failed...\n");
        close(fd);
        return (1);
    }
    // initialize the pio controller
    // led: set the direction of the HPS GPIO1 bits attached to LEDs to output
    alt_setbits_word((virtual_base + ((uint32_t)(ALT_GPIO1_SWPORTA_DDR_ADDR) & (uint32_t)(HW_REGS_MASK))), USER_IO_DIR);

    int numero_flashes = 0;
    int delay = 100;

    if (argc > 1)
    {
        sscanf(argv[1], "%d", &numero_flashes);
        if (argc > 2)
        {
            sscanf(argv[2], "%d", &delay);
        }
    }
    else
    {
        printf("Please use the next input style: ./%s #flashes\n", argv[0]);
        printf("or use the next input style: ./%s #flashes delay\n", argv[0]);
        return 0;
    }

    printf("led test\r\n");
    printf("the led flashes %d times\r\n", numero_flashes);
    for (i = 0; i < numero_flashes; i++)
    {
        LED_SET();
        usleep(delay * 1000);
        LED_CLR();
        usleep(delay * 1000);
    }
    /*printf("user key test \r\n");
printf("press key to control led\r\n");
while(1){
scan_input =READ_KEY();
//usleep(1000*1000);
if(~scan_input&BUTTON_MASK){
LED_SET();
}
else{
LED_CLR();
}
}*/

    // clean up our memory mapping and exit
    if (munmap(virtual_base, HW_REGS_SPAN) != 0)
    {
        printf("ERROR: munmap() failed...\n");
        close(fd);
        return (1);
    }
    close(fd);
    return (0);
}
 
    • Vuelva a generar el proyecto (Build Proyect) y ejecútelo, el resultado se muestra a continuación:

Vídeo de YouTube