martes, 7 de noviembre de 2017

Convertir un modelo de Simulink a código VHDL con HDL Coder [Ejemplo 2]

En una entrada anterior exponía el como generar un código VHDL de una función booleana sencilla. Este será también un ejemplo sencillo pero mucho más interesante. Generaremos un controlador P para la posición de un motor DC con caja de engranes y encoder [este modelo en particular] alimentado por un driver Pololu Qik 2s12v10. El esquema del sistema es el siguiente:
 La posición del motor será leída mediante el encoder que viene integrado en el motor. El modelo de Simulink que convertiremos a código VHDL es el siguiente:

Bloques requeridos:
HDL Coder > Commonly Used Blocks > In1
HDL Coder > Commonly Used Blocks > Out1
HDL Coder > Sources > Constant
HDL Coder >  Math Operations > Divide
HDL Coder > Discontinuities > Saturation
HDL Coder > Signal Attributes > Data Type Conversion

Diagrama:
 La operación de división por números que no sean potencias de 2 no está disponible en todos los dispositivos por lo que en este caso usaremos 1/32 como aproximación de 0.03 [es posible implementar en hardware divisiones entre números arbitrarios pero serán tratados en otro ejemplo]. Es necesario establecer en todos los bloques el tipo de dato int16 dando doble click a cada bloque y cambiando el tipo de dato la siguiente forma:
 También sera necesario dar doble click en el bloque Divide, ir a Signal Attributes y activar el modo "Saturate on integer overflow" y verificar que el modo de redondeo sea "zero" o "simplest". Habiendo hecho todo lo anterior generemos el código VHDL yéndonos a la pestaña Code > HDL Code > Generate HDL. Les generará el siguiente código:

-- -------------------------------------------------------------
--
-- File Name: hdlsrc\Control_P_Div_HDL\Control_P_Div_HDL.vhd
-- Created: 2017-11-05 22:25:53
--
-- Generated by MATLAB 8.5 and HDL Coder 3.6
--
--
-- -------------------------------------------------------------
-- Rate and Clocking Details
-- -------------------------------------------------------------
-- Model base rate: 0.01
-- Target subsystem base rate: 0.01
--
-- -------------------------------------------------------------
-- -------------------------------------------------------------
--
-- Module: Control_P_Div_HDL
-- Source Path: Control_P_Div_HDL
-- Hierarchy Level: 0
--
-- -------------------------------------------------------------
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.numeric_std.ALL;
ENTITY Control_P_Div_HDL IS
PORT( Ref : IN std_logic_vector(15 DOWNTO 0); -- int16
Encoder : IN std_logic_vector(15 DOWNTO 0); -- int16
Salida : OUT std_logic_vector(7 DOWNTO 0) -- int8
);
END Control_P_Div_HDL;
ARCHITECTURE rtl OF Control_P_Div_HDL IS
-- Constants
CONSTANT C_divbyzero_p : signed(17 DOWNTO 0) := to_signed(16#1FFFF#, 18); -- sfix18
CONSTANT C_divbyzero_n : signed(17 DOWNTO 0) := to_signed(-16#E0000#, 18); -- sfix18
-- Signals
SIGNAL Ref_signed : signed(15 DOWNTO 0); -- int16
SIGNAL Encoder_signed : signed(15 DOWNTO 0); -- int16
SIGNAL Subtract1_out1 : signed(15 DOWNTO 0); -- int16
SIGNAL Subtract1_out1_dtc : signed(16 DOWNTO 0); -- sfix17
SIGNAL Constant1_out1 : signed(15 DOWNTO 0); -- int16
SIGNAL Divide_div_temp : signed(17 DOWNTO 0); -- sfix18
SIGNAL Divide_slice_cast : signed(17 DOWNTO 0); -- sfix18
SIGNAL Divide_out1 : signed(15 DOWNTO 0); -- int16
SIGNAL Saturation1_out1 : signed(15 DOWNTO 0); -- int16
SIGNAL Data_Type_Conversion_out1 : signed(7 DOWNTO 0); -- int8
BEGIN
Ref_signed <= signed(Ref);
Encoder_signed <= signed(Encoder);
Subtract1_out1 <= Ref_signed - Encoder_signed;
Subtract1_out1_dtc <= resize(Subtract1_out1, 17);
Constant1_out1 <= to_signed(16#0020#, 16);
Divide_slice_cast <= resize(Subtract1_out1_dtc, 18);
Divide_div_temp <= C_divbyzero_p WHEN (Constant1_out1 = 0) AND (Divide_slice_cast(17) = Constant1_out1(15)) ELSE
C_divbyzero_n WHEN Constant1_out1 = 0 ELSE
resize(Subtract1_out1_dtc, 18) / Constant1_out1;
Divide_out1 <= X"7FFF" WHEN (Divide_div_temp(17) = '0') AND (Divide_div_temp(16 DOWNTO 15) /= "00") ELSE
X"8000" WHEN (Divide_div_temp(17) = '1') AND (Divide_div_temp(16 DOWNTO 15) /= "11") ELSE
Divide_div_temp(15 DOWNTO 0);
Saturation1_out1 <= to_signed(16#007F#, 16) WHEN Divide_out1 > to_signed(16#007F#, 16) ELSE
to_signed(-16#007F#, 16) WHEN Divide_out1 < to_signed(-16#007F#, 16) ELSE
Divide_out1;
Data_Type_Conversion_out1 <= Saturation1_out1(7 DOWNTO 0);
Salida <= std_logic_vector(Data_Type_Conversion_out1);
END rtl;
Para complementar el controlador usaremos los siguientes módulos VHDL que ya he publicado por acá:
Agregando todos los archivos .vhd requeridos al proyecto se puede proceder a conectar los bloques de forma esquemática en ISE de la siguiente manera:
Si no están familiarizados con la creación de proyectos esquemáticos en ISE aquí hay un video-tutorial en español muy bien explicado [utilizan Verilog pero el procedimiento es el mismo]. El bloque selector es simplemente una declaración when que envía una referencia de 0 si el switch esta apagado y de 2400 (90° en cuentas de encoder) cuando esta encendido. En este video muestro el funcionamiento del sistema implementado en una tarjeta Basys2:

No hay comentarios: