miércoles, 23 de noviembre de 2016

DIVISION DE ENTEROS EN VHDL

La siguiente entrada es sobre el proyecto final del curso de digitales avanzadas, que su objetivo es diseñar e implementar un procesador a la medida que realice las operaciones de división y residuo entre dos números enteros, tomando como lenguaje de descripción  VHDL y como plataforma de implementación la tarjeta NEXYS 4.


Funcionalidad:
La entrada de dos datos se hará mediante un teclado matricial 4 x 4 (TM4) y la salida a través del siete segmentos (D7S). Al iniciar el sistema, mostrará en los D7S 1 y o  “00” y el resto de D7S apagados como indicativo de estar listo. La secuencia de operación es: ingresar el dividendo de dos dígitos (que mostrará en los D7S 5 permanecerá apagado). El sistema procederá a  realizar las operaciones y mostrara resultados: el cociente (que se mostrará en los D7S 3 y 2) y el residuo (que se mostrará en  D7S 0, el D7S 1 permanecerá apagado). Tener en cuenta que los teclados presentan el efecto de rebote que debe ser considerado para la correcta operación y que los D7S comparten un canal de datos común, así que deben operarse multiplexados.
A continuación iremos explicando cada contenido del problema que se nos ha puesto, para entender cómo se maneja el teclado matricial y el 7 segmentos  se debe indagar en la entrada anterior al blog correspondiente. Además hablaremos de cómo se  hace la división de enteros en VHDL.


DIVISIÓN:
Existen muchos tipos de algoritmos de división, a continuación uno que es el que resulta más preciso a nuestra forma de dividir a mano y es por tanto más sencillo de entender.

En la imagen  se va a mostrar la división entera realizada con números enteros decimales y números enteros binarios. 



Veamos paso a paso el algoritmo para hacer la división:
Primero desplazamos a la izquierda el divisor lo más que podamos siempre que “quepa” en el dividendo (D˃ d). La cantidad de veces que se ha desplazado la memorizamos. En electrónica digital, diríamos que los guardamos en un registro que llamaremos despl z. en el ejemplo de la imagen anterior el desplazamiento es 3.


Una vez que tengamos el divisor desplazado lo más posible a la izquierda desplz = 3, restamos al dividendo el divisor y ponemos un 1 en el cociente que se va a mostrar en la siguiente imagen. En la imagen que se va a mostrar en seguida se han rellenado los ceros que faltan a la derecha provocados por el desplazamiento. En el cálculo manual se hace implícitamente aunque no se ponen para ahorrar la escritura de la resta completa. 


Ahora el divisor, que hemos desplazado tres veces a la izquierda, lo desplazamos una vez a la derecha, es decir, como si del original lo desplazamos dos veces a la izquierda por tanto desplz = 2. Probamos si cabe con el resultado de la resta anterior, si cabe D ≥ d ponemos un 1 en el cociente. 

Volvemos a desplazar el divisor a la derecha ahora desplz = 1, y volvemos a ver si cabe en la nueva resta que hemos hecho.


Vemos que esta vez no cabe, por lo tanto pondremos un 0 en el cociente, no hacemos la resta, y probamos desplazando el divisor a la derecha  ahora desplz = 0  volvemos a como estaba en principio.


Tampoco cabe, y como el cociente ha vuelto a su posición original desplz = 0 la división se termina (pues no sacamos decimales). 


Ahora se desplaza los resto a la izquierda podríamos sacar decimales. En este caso podríamos sacar el primer decimal para calcular el redondeado del cociente.  Así que la implementación en hardware de este algoritmo se podrá hacer de manera secuencial, pero también se podría desarrollar y realizarla de manera combinacional  o segmentada.
Si se realiza de manera secuencial, la división se haría en varios ciclos de reloj, que dependerá del número de bits de los operadores. Por tanto, el divisor necesitara de una parte de control que ordene la ejecución de las operaciones que haya que realizar en cada momento. Una vez terminadas las operaciones deberá proporcionar una señal de aviso que indique que el resultado está disponible. Además el módulo, tendrá un señal de entrada que ordene la ejecución de la división.

MULTIPLEXACIÓN DEL 7 SEGMENTOS EN UNA FPGA
Un multiplexor o selector de datos es un circuito lógico que acepta varias entradas y solamente permite a una de ellas alcanzar la salida. La siguiente imagen muestra el diagrama de un multiplexor, donde se observa que la salida Z puede tomar el valor de A o B, pero no de ambos a la vez, en base al valor del parámetro de selección So. Un claro ejemplo de un multiplexor se encuentra en la televisión, donde solamente se muestra en pantalla el canal de deportes o el canal de música, pero no ambos a la vez (al menos hasta hace tres años claro está). 


Desde el punto de vista de programación, equivale a una simple estructura if - else, donde la variable puede tomar o uno u otro valor. Habiendo dicho esto, es claro que el multiplexor verse como una instrucción switch al incrementar la cantidad de los valores.
A continuación seguiremos con el diagrama de estados para implementar el código en VHDL  del problema propuesto.

DIAGRAMA DE ESTADOS 




Código de proceso de división

 Donde se ejecuta el proceso de tomar el dividendo que se compone de dos dígitos y empieza a restarse con el divisor tantas veces sea necesario hasta que ya el valor de dividendo sea menor que el divisor.

En este bloque es donde definimos la variables que son entradas y salidas.

  library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity divisor is
    port (
        clk     : in std_logic;
        reset   : in std_logic;
        NUM1  : in unsigned(7 downto 0);
        NUM2  : in unsigned(3 downto 0);
        DATO  : out unsigned(7 downto 0)
    );
end divisor;

En este bloque encontramos la arquitectura donde es define todas la funciones necesarias para que se realice y funciones el programa.

architecture divisor of divisor is
    type estado is (reposo, numero1, numero2, resta, cuenta, comparar, resultado);
    signal estadoActual, estadoSiguiente : estado;
    signal SNUM1 : unsigned(7 downto 0);
    signal SNUM2 : unsigned(7 downto 0);
    signal R : unsigned(7 downto 0);
    signal C : unsigned(7 downto 0);
    signal COS : unsigned(7 downto 0);

begin

En este siguiente bloque encontramos el camino de datos donde se definirán todas las transiciones de los estados que se realizaran en el programa. 
    -------------------------------   
    -- camino de datos (datapath)--
    -------------------------------

    process(clk, estadoActual, NUM1, NUM2)
      begin 
       if (clk'event and clk = '1') then
     
         case estadoActual is
         
           when reposo =>
             R <= "00000000";
             C <= "00000000";

           when numero1 =>
             SNUM1 <= NUM1;
             
           when numero2 =>
             SNUM2 <="0000" & NUM2;
             
           when resta =>
             R <= SNUM1-SNUM2;

           when cuenta =>
             C <= C+1;
             SNUM1 <= R;

           when comparar =>

           when resultado =>
             DATO <= C;
             COS <= R;

          end case;
        end if;
    end process;

En el siguiente bloque encontramos el registro de estados donde por cada flanco de subida del reloj se realizara un cambio de estado y si se oprime la señal de reset el programa se ira directamente a estado de reposo.
    -----------------------
    -- registro de estado--
    -----------------------

   process(clk, reset)
    begin
        if (reset = '1') then
            estadoActual <= reposo;
        elsif (clk'event and clk = '1') then
            estadoActual <= estadoSiguiente;
        end if;
    end process;

En el siguiente bloque encontramos los estados  donde se definen los estados lo que cada estado va a realizar en el programa.
    -------------
    -- Estados --
    -------------

    process (estadoActual, SNUM1, SNUM2) is
    begin
      case estadoActual is       
        when reposo => estadoSiguiente <= numero1;
        when numero1 => estadoSiguiente <= numero2;
        when numero2 => estadoSiguiente <= comparar;
        when comparar => 
            if (SNUM1 >= SNUM2) then
                estadoSiguiente <= resta;
            else
                estadoSiguiente <= resultado;
            end if;
        when resta => estadoSiguiente <= cuenta;
        when cuenta => estadoSiguiente <= comparar;
        when resultado => estadoSiguiente <= reposo;
     end case;
   end process;
end divisor;
        if (reset = '1') then
            estadoActual <= reposo;
        elsif (clk'event and clk = '1') then
            estadoActual <= estadoSiguiente;
        end if;
    end process;

    -------------
    -- Estados --
    -------------

    process (estadoActual, SNUM1, SNUM2) is
    begin
      case estadoActual is       
        when reposo => estadoSiguiente <= numero1;
        when numero1 => estadoSiguiente <= numero2;
        when numero2 => estadoSiguiente <= comparar;
        when comparar => 
            if (SNUM1 >= SNUM2) then
                estadoSiguiente <= resta;
            else
                estadoSiguiente <= resultado;
            end if;
        when resta => estadoSiguiente <= cuenta;
        when cuenta => estadoSiguiente <= comparar;
        when resultado => estadoSiguiente <= reposo;
     end case;
   end process;
end divisor;

2 comentarios:

  1. disculpa al comienzo de de estas lineas de codigo me dice error
    if (reset = '1') then
    estadoActual <= reposo;
    elsif (clk'event and clk = '1') then
    estadoActual <= estadoSiguiente;
    end if;
    end process;

    ResponderEliminar
  2. Error (10500): VHDL syntax error at division.vhd(96) near text "if"; expecting "entity", or "architecture", or "use", or "library", or "package", or "configuration" me dice eso sera porque antes esta esto:
    else
    estadoSiguiente <= resultado;
    end if;
    when resta => estadoSiguiente <= cuenta;
    when cuenta => estadoSiguiente <= comparar;
    when resultado => estadoSiguiente <= reposo;
    end case;
    end process;
    end divisor;

    ResponderEliminar