Reporte del reto final - FredyCansecoSantos/Reto_Diseno_con_Logica_Programable GitHub Wiki

Integrantes del equipo: Fredy Yahir Canseco Santos (A01735589) - Daniel Ruán Aguilar (A01731921)

Introducción:

El bloque de: Diseño con lógica programable tiene como objetivo el diseñar un sistema ciberfísico, que puede ser definido como una combinación de software (ciber) y hardware que incluye dispositivos electrónicos configurables (FPGAs, Microcontroladores, Procesadores Digitales de Señales, etc.), que están conectados al mundo real a través de sensores y actuadores que además de estar conectados entre ellos, también pueden interactuar con un usuario o a otros sistemas como el internet.

En la siguiente publicación se describe la funcionalidad de la integración del reto final, que consta de una animación realizada en el entorno de processing, que mediante el protocolo de comunicación SPI y UART logra comunicar un periférico de salida (acelerómtero ADXL345) con el soft-core Picoblaze integrado en la tarjeta FPGA de Xilinx llamada "Arty", para así enviar los datos correspondientes a la computadora, y de la computadora al entorno de animación.

Desarrollo:

Comenzando con la elaboración del proyecto tuvimos que hacer un bosquejo de nuestro sistema, integrado mediante un diagrama de bloques (Observe la imágen de abajo), en los cuales se colocaron los componentes considerados como necesarios, tales como: el bloque de Picoblaze, el bloque del módulo UART, el bloque de módulo SPI, el bloque de registro de entrada y el bloque de multiplexor.

Diagrama de bloques

Una vez ya hecho el diagrama se procedió a hacer el proyecto en el entorno de Vivado, en dicho entorno de programación se cargaron los programas correspondientes (Los archivos se encuentran en el apartado de "Code" en este mismo repositorio de Git Hub) para la generación de nuestro "Top_Level" que al tratarse de un diseño de bloques es el bloque que mayor jerarquía posee. Posteriormente se declaró cada uno de los puertos necesarios dentro de nuestro "Top_Level" que pueden ser vistos en el fragmento de código de abajo.

   entity Top_Level is
     Port ( clk : in std_logic;
         rst : in std_logic;
         MISO : in std_logic;
         sclk : buffer std_logic; 
         ss_n : buffer std_logic_vector(0 downto 0);  
         MOSI : out std_logic;
         rx : in std_logic;
         tx : out std_logic );
   end Top_Level;

Justo depués de esto comenzamos a declaramos a cada uno de los componentes a utilizar en el proyecto, dichos componentes pueden ser vistos en el fragmento de código de abajo:


-- Componente del módulo SPI
   component modulo_spi is
    Port (         CLK : in STD_LOGIC;
                   RST : in STD_LOGIC;
               PORT_ID : in STD_LOGIC_VECTOR (7 downto 0);
           OUTPUT_PORT : out STD_LOGIC_VECTOR (7 downto 0);
                  MISO : in  STD_LOGIC;                     
                  SCLK : buffer STD_LOGIC;                      
                  SS_N : buffer STD_LOGIC_VECTOR(0 DOWNTO 0);  
                  MOSI : out    STD_LOGIC);
    end component;


-- Componente del módulo de registro 
    component registro_puerto_entrada is
	 generic(
				n : integer := 8			
	 );
    Port ( 
			  CLK  : in  STD_LOGIC;
              RST  : in  STD_LOGIC;
                D  : in  STD_LOGIC_VECTOR(n-1 downto 0);
                Q  : out STD_LOGIC_VECTOR(n-1 downto 0)
          );
end component;
-- Componente del módulo embedded_kcpsm6
component embedded_kcpsm6 is
  port (                   
                             in_port : in std_logic_vector(7 downto 0);
                            out_port : out std_logic_vector(7 downto 0);
                             port_id : out std_logic_vector(7 downto 0);
                        write_strobe : out std_logic;
                      --k_write_strobe : out std_logic;
                        -- read_strobe : out std_logic;
                           --interrupt : in std_logic;
                       --interrupt_ack : out std_logic;
                           --    sleep : in std_logic;
                                 clk : in std_logic;
                                 rst : in std_logic);
end component;
-- Componente del módulo UART
component modulo_uart is
    Port ( 
                     CLK : in STD_LOGIC;
                     RST : in STD_LOGIC;
                     --pines de comunicación con PicoBlaze
                 PORT_ID : in STD_LOGIC_VECTOR (7 downto 0);
              INPUT_PORT : in STD_LOGIC_VECTOR (7 downto 0);
             OUTPUT_PORT : out STD_LOGIC_VECTOR (7 downto 0);
            WRITE_STROBE : in STD_LOGIC;
                      --pines de comunicación serial
                      TX : out STD_LOGIC;
                      RX : in STD_LOGIC);
end component;

Posterior a esto declaramos las señales que necesitamos para crear el "alambrado" entre los componentes, este "alambrado" se realizó con ayuda del diagrama de bloques visto anteriormente. A continuación en el próximo fragmento de código se mostrará el "alambrado" del proyecto.

   -- Declaración de Señales
   --Señal entre PORT ID's
   signal puerto_ID :  std_logic_vector(7 downto 0);
   --Señal entre el Out de picoblaze y la entrada de UART
   signal input_puertos : std_logic_vector(7 downto 0);
   signal write :  std_logic;
   signal dato1_MUX : std_logic_vector(7 downto 0);
   signal dato2_MUX : std_logic_vector(7 downto 0);
   signal puerto_D :  std_logic_vector(7 downto 0);
   signal puerto_Q :  std_logic_vector(7 downto 0);


begin
 
 puerto_D <= dato1_MUX when (puerto_ID(6) = '1') else
 dato2_MUX when (puerto_ID(4) = '1') else
 (others => '0');
 
 
 
  moduloSPI : modulo_spi port map(
		                                CLK => clk,
                                        RST => rst,
                                        PORT_ID => puerto_ID,
                                        OUTPUT_PORT => dato1_MUX,
                                        MISO => MISO,                  
                                        SCLK => sclk,                   
                                        SS_N => ss_n,
                                        MOSI => MOSI );
    
  registroPuertoEntrada : registro_puerto_entrada port map(
		                                 CLK => clk,
                                         RST => rst,
                                         D  => puerto_D,
                                         Q =>  puerto_Q );
                                         
  moduloUART : modulo_uart port map(
		                                CLK => clk,
                                        RST => rst,
                                        --pines de comunicación con PicoBlaze
                                        PORT_ID => puerto_ID,
                                        INPUT_PORT => input_puertos,
                                        OUTPUT_PORT => dato2_MUX,
                                        WRITE_STROBE => write,
                                        --pines de comunicación serial
                                        TX => tx,
                                        RX => rx);
                                    
                                        
  modulo_PICO_BLAZE : embedded_kcpsm6 port map (                   
                                        in_port => puerto_Q,
                                        out_port => input_puertos,
                                        port_id => puerto_ID,
                                        write_strobe => write,
                                        clk => clk,
                                        rst => rst);

 end Behavioral;

Una vez realizadas las conexiones pertinentes entre los diferentes componentes procedemos a crear nuestro archivo en lenguaje ensamblador, en el cual le programamos la lógica necesaria para que mediante el protocolo SPI pueda recopilar los datos que el sensor acelerómetro ADXL345 envía en cada interacción, luego de esto el protocolo UART se encarga de que los datos recopilados se envíen a nuestra computadora sin modificaciones para que el entorno de programación Processing pueda trabajar con ellos.

   ;puertos del UART
		CONSTANT PuertoLeeListoTX,    11
		CONSTANT PuertoEscribeDatoTX, 12
		CONSTANT PuertoLeeDatoRX,     13
		CONSTANT PuertoDatoListoRX,   14
		CONSTANT PuertoDatoRXLeido,   15
		;
   ;puertos del spi
		CONSTANT PuertoLeeXLSB, 41
		CONSTANT PuertoLeeXMSB, 42
		CONSTANT PuertoLeeYLSB, 43
		CONSTANT PuertoLeeYMSB, 44
		CONSTANT PuertoLeeZLSB, 45
		CONSTANT PuertoLeeZMSB, 46
		;
		NAMEREG s3, DatoAccess
		NAMEREG s6, DatoSerial
		NAMEREG s7, EstadoTX
		;NAMEREG sB, DatoPrueba
		;
   start:
		;leemos el dato LSB del eje x del accel
		INPUT		DatoAccess, PuertoLeeXLSB
		LOAD		DatoSerial, DatoAccess
		CALL		tx_uart
		CALL		delay_1s
    ;leemos el dato MSB del eje x del accel
    INPUT		DatoAccess, PuertoLeeXMSB
    LOAD		DatoSerial, DatoAccess
    CALL		tx_uart
    CALL		delay_1s

  tx_uart:
    INPUT		EstadoTX, PuertoLeeListoTX
    COMPARE		EstadoTX, 01
    JUMP		Z, tx_uart
    OUTPUT		DatoSerial, PuertoEscribeDatoTX
    CALL    delay_1s
    ;CALL    delay_1s
    ;CALL    delay_1s
    RETURN

    JUMP start
    ;

  delay_1s:
		LOAD s2, BE
		LOAD s1, BC
		LOAD s0, 20
  delay_loop:
		SUB 		s0, 1'd
		SUBCY 		s1, 0'd
		SUBCY 		s2, 0'd
		JUMP 		NZ, delay_loop
		RETURN
		;


Una vez creada la interacción entre nuestros distintos componentes junto con el programa en lenguaje ensablador pasamos a generar la animación en Processing, esto lo haremos utilizando una librería especial para la comunicación serial entre la computadora y nuestra tarjeta Arty, luego de esto leeremos los datos mediante una variable declarada como entero, esta variable nos ayudará a generar una serie de condicionales, en los cuales podremos mover como decidamos a nuestro personaje dentro de nuestro entorno de animación.

  import processing.serial.*; 
  import processing.sound.*;

  Serial port;  // Create object from Serial class
  int val;      // Data received from the serial port 
  char inByte;

  SoundFile file;

  String audioName = "Super Mario medley.mp3";
  String path;

  final int WIDTH = 50;
  final int HEIGHT = 60;
  int[][] level = new int[HEIGHT][WIDTH];

  Player p1;
 
  //booleans for key presses to get a simple yes or no press and
  //to not have to worry about the a,aaaaaaaaaaaaa thing
  boolean right = false, left = false, up = false;
 
 //SETUP/////////////////////////////
  void setup() {
  size(500,253);
  p1 = new Player(WIDTH*1,HEIGHT*1);
  
   path = sketchPath(audioName);
  file = new SoundFile(this, path);
  file.play();
  port = new Serial(this, "COM5", 115200); 
  
  }//END SETUP//////////////////////////
 //DRAW///////////////////////////////


  void draw() {

  if (0 < port.available()) {  // If data is available to read,
    
    inByte = port.readChar();
    
  }
  
  //PImage es un tipo de dato que almacena imagenes
  PImage bck;
  
  //Cargamos a una variable la imagen
  bck = loadImage("Super Mario 1.png");
  
  //Asignamos esa variable al fondo
  background(bck);
  p1.display();
  p1.update();
  
}
//END DRAW///////////////////////////
  boolean place_free(int xx,int yy) {
   xx = int(floor(xx));
   yy = int(floor(yy));
  if (yy < height-height/8) {
      return true;
  }
  return false;
}

Segunda parte del código: En esta segunda parte del código de animación es en donde se crea la clase que generará al objeto jugador, además de generar las condicionales del movimiento que nuestro personaje tendrá, se programó para que en un intervalo de entre 100 y 255 en el eje x se desplazara hacia la derecha y en un intervalo de entre 0 y 30 se desplazara hacia la izquierda.

class Player {
  int x,y;
  float xSpeed,ySpeed;
  float accel,deccel;
  float maxXspd,maxYspd;
  float xSave,ySave;
  int xRep,yRep;
  float gravity;
  float state;
  float stateRate;
  float stateShift;
  int charXShift;
  int charYShift;
  int pace;
  
  Player(int _x, int _y ) {
    x = _x;
    y = _y;
    xSpeed = 0;
    ySpeed = 0;
    accel = 0.1;
    deccel = 0.10;
    maxXspd = 2;
    maxYspd = 12;
    xSave = 0;
    ySave = 0;
    xRep = 0;
    yRep = 0;
    gravity = 0.3;
    stateRate = .3;
    stateShift = 2;
    charXShift = 22;
    charYShift = 42;
    pace = 2;
  }
  
  void update() {
    
    if (( inByte >= 100 ) && (inByte <= 255 )){
      xSpeed += accel;
      if ( xSpeed > maxXspd ) {
        xSpeed = maxXspd;
      }
      state = state + stateRate;
            if (state >2) {
            state =  stateRate;
           }
    }
    
      else if (( inByte >= 0 ) && (inByte <= 30 )){
      xSpeed -= accel;
      if ( xSpeed < -maxXspd ) {
        xSpeed = -maxXspd;
      }
      state = state - stateRate;
            if (state <-2) {
            state = - stateRate;
           }
    }
    
    else { //neither right or left pressed, decelerate
      state = 0;
      if ( xSpeed > 0 ) {
        xSpeed -= deccel;
        if ( xSpeed < 0 ) {
           xSpeed = 0;
        }
      }
      if ( xSpeed < 0 ) {
        xSpeed += deccel;
        if ( xSpeed > 0 ) {
           xSpeed = 0;
        }
      }
    }
     
    
    
    
    ySpeed += gravity;
    
    int signX = (xSpeed<0) ? -pace : pace;
    int signY = (ySpeed<0) ? -pace : pace;
                 println("signX is" + signX);
                 println("signY is" + signY);
        
    xRep += floor(abs(xSpeed));
    yRep += floor(abs(ySpeed));
                 println(xRep);
                 println("yRep is" + yRep);             
                 
    for ( ; yRep > 0; yRep-- ) {
      if ( place_free(x,y+signY) && place_free(x+charXShift,y+signY)) {
        y += signY;
      }
      else {
        ySpeed = 0;
      }
    }
    
    
    for ( ; xRep > 0; xRep-- ) {
      if ( place_free(x+signX,y-charYShift) && place_free(x+signX,y) ) {
        x += signX;
        if(x == 490){
          x=-10;
        }
        
        else if(x == -20){
          x=480;
        }
    }
    else{
      xSpeed = 0;
    }
  }
  
  }
  void display() {
    if (state <0 && state >=-1){
     PImage izq_1;
     izq_1 = loadImage("izq_1.png");//loadImage("lefta.png");
     image(izq_1, x, y-charYShift);
     
    }
    
    
    if (state <-1 && state >=-2){
     PImage izq_2;
     izq_2 = loadImage("izq_2.png");//loadImage("lefta.png");
     image(izq_2, x, y-charYShift);
     
    }
    
   
     else if (state <-2 && state >=-3){ 
     PImage izq_3;
     izq_3 = loadImage("izq_3.png");//loadImage("leftb.png");
     image(izq_3, x, y-charYShift);
    }
    
   
      else if (state >0 && state <=1){
      PImage der_1;
     der_1 = loadImage("der_1.png");//loadImage("righta.png");
     image(der_1, x,y-charYShift);
     
    }
    

     else if (state >1 && state <=2){
     PImage der_2;
     der_2 = loadImage("der_2.png");//loadImage("rightb.png");
     image(der_2, x, y-charYShift);
     
    }
    
    
     else if (state >1 && state <=2){
     PImage der_3;
     der_3 = loadImage("der_3.png");//loadImage("rightb.png");
     image(der_3, x, y-charYShift);
    }
    
 
    else if(state == 0){
     PImage frente_izq;
     frente_izq = loadImage("frente_ider.png");//loadImage("still.png");
     image(frente_izq, x, y-charYShift);
    }  
  }
}

El resultado final del proyecto fue una animación basada en el conocido videojuego de Mario Bros, en el cual un personaje se desplaza en varias direcciones para sortear obstáculos y enemigos, para así rescatar a una princesa en un castillo, dicha animación inspirada en el anterior personaje la podemos ver en la siguiente imagen:

Animacion_Mario

Conclusión:

Fue gracias a este proyecto y al bloque en general que se pudieron adquirir conocimientos imprescindibles sobre la construcción y el funcionamiento en materia de la lógica programable, se pudo comprender cómo es que un procesador softcore puede operar a muy bajo nivel pues con las prácticas desarrolladas se pudo comunicar de una manera directa con el mismo, además de poder llegar a comprender los diferentes protocolos de comunicación, sus características, sus aplicaciones, sus ventajas y desventajas. Asímismo se adquirió conocimiento sobre el diseño jerárquico que es crucial al momento del diseño de sistemas digitales complejos.

Enlace a video descriptivo sobre el proyecto final:

https://drive.google.com/file/d/1Re0Ar7E-Y3r-qvyGpzcGMyGfWdt6I5Cr/view?usp=sharing