Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Ing. Electrónico
rax20037 at gmail
Abril 2011
¡Empezemos!
Bueno estaba diseñando un módulo para el procesamiento de imágenes y este puede recibir los
píxeles de forma serial e igualmente expulsar los datos de forma serial. El procesamiento está
segmentado (Pipelined) y entra en la categoría de procesadores de flujo (Stream Processors).
Además de los píxeles, este módulo necesita un par de parámetros más con los cuales se controla
su funcionamiento:
Procesador de Píxeles
Entrada Salida
Memoria Principal
Para su implementación se usará un kit SP605 Embedded Edition de Xilinx, con una memoria DDR3
de 128MB, la única en la tarjeta capaz de almacenar una imagen de al menos 16MP. Para la
entrada/salida de datos las únicas interfaces con una velocidad aceptable podían ser PCI
Express(2Gb/s), Ethernet (1Gb/s). Tanto la memoria como las entradas y salidas necesitan de
controladores nada triviales de diseñar e implicarían un tiempo considerable de desarrollo. ¡Pero!,
aquí comienza la historia, Xilinx provee una interfaz AXI para todos estos módulos. Adaptando mi
módulo a esta interfaz tendría más facilidad para la comunicación de datos. Y de acuerdo a Xilinx,
junto con sus herramientas, este sería un proceso simple y tendría una solución “Plug & Play” para
todos mis proyectos. Puedes encontrar información completa sobre AXI en la página de Xilinx y su
especificación completa en la página de ARM.
http://www.xilinx.com/support/documentation/white_papers/wp379_AXI4_Plug_and_Play_IP.pdf
http://www.arm.com/products/system-ip/amba/amba-open-specifications.php
Procesador de Píxeles
Al final de esta guía se tendrá un ejemplo funcional de un periférico con estas interfaces para con
el cual se podrá seguir implementando el periférico que se desee.
ISE Design Suite 13.1, Embedded Edition .- Disponible la versión de prueba en la página de
Xilinx. Si adquieres un kit estos vienen con una licencia permanente para el programa, con
la única limitación de sólo poder usarla para un modelo de FPGAs (¡el que viene en el kit!)
Un editor sencillo de texto, te recomiendo el Notepad++.
También necesitas descargar los siguientes archivos con plantillas para los diseños:
¿How to create a custom AXI IP Core? , archivos con las plantillas de las interfaces AXI de la
página de Xilinx.
Además, necesitas un proyecto en XPS listo para poder simplemente agregar nuestro módulo. Es
decir, ya debes de tener un procesador, la memoria y demás periféricos básicos (timer, uart, leds,
botones, …) para poder trabajar sólo en este periférico. Junto a este documento estaré
adjuntando el archivo .mhs con el que empecé trabajar. No te recomiendo usar el diseño que se
crea con el asistente de XPS pues, en mi experiencia, ¡no funcionan! Hay un problemita con la
interfaz a la memoria DDR, que ya está solucionado en el diseño que adjunto. Al parecer sólo se
trata de seleccionar correctamente el orden de los pines de dirección. De cualquier forma yo baso
mi diseño en el que viene junto al kit Spartan 6 Embedded Development de Xilinx, y lo he
actualizado por completo a la interfaz AXI.
Como extra, te recomiendo empezar a reproducir un poco de música relajante como el Pop , así no
te estresas ni te cansas demasiado =).
Como se ve en la figura, para la verificación del diseño se usará un par de interfaces stream del
microblaze en modo AXI, M0_AXS y S0_AXIS.
pcores >
axi_lite_master_v1_00_a
axi_lite_slave_v1_00_a
axi_master_v1_00_a
axi_slave_v1_00_a
axi_stream_v1_00_a
Las dos plantillas resaltadas son las que vamos a usar en este proyecto. Revisa el documento
Platform Specification Format Reference Manual para poder entender cada detalle de esos
archivos. Aquí sólo se describirá lo necesario para lograr la conexión. Una vez copiados regresa a
XPS ve al Project>Rescan user repositores para que el programa los reconozca. Deberán aparecer
en el catálogo de IPs los siguientes componentes:
La siguiente pestaña es la Peripheral Flow. Debemos seleccionar la opción Create template for a
new peripheral para crear la plantilla. Luego has click en Next:
La siguiente pestaña será Repository or Project. Aquí seleccionamos To an XPS repository pues no
será necesario exportarlo a otros proyectos, así lo mantenemos de forma local. Click en Next.
En la pestaña Name and Version toca elegir un nombre a nuestra IP. Rellena los siguientes datos:
Click en Next.
Ahora toca elegir el tipo de bus a usar. Desafortunadamente, por ahora, este asistente sólo nos
ayudara en crear la interfaz AXi-Lite, las interfaces stream las agregaremos manualmente en un
paso posterior. Selecciona AXI4-Lite y haz click en siguiente:
En la pestaña IP InterConnect se especifican las señales del bus van a necesitarse. Dejamos las
señales por defecto. Click en siguiente:
Esta es la última pestaña en la que tenemos que seleccionar alguna opción. Luego aparecerá la
pestaña de Congratulations con un resumen de la plantilla creada. Revísala y a continuación hazle
click en siguiente.
En la carpeta pcores del proyecto XPS aparecerá el directorio my_first_ip_v1_00_a con todas las
plantillas del proyecto. En my_first_ip_v1_00_a\hdl se han creado las plantillas con el código para
el periférico, my_first_ip_v1_00_a\data contiene los archivos con que se importa este periférico al
EDK y en my_first_ip_v1_00_a\devl se encuentran los archivos del ISE para organizar el proyecto.
EXPLORANDO EL CÓDIGO DE LA PLANTILLA
En la carpeta my_first_ip_v1_00_a\devl\projnav se encuentra el proyecto de ISE my_first_ip,
ábrelo. Este no es el proyecto del diseño completo, sino sólo para editar el código de tu periférico.
my_first_ip.vhd
user_logic.vhd
El primero tiene referencias a librerias externas que implementan la interfaz AXI, muy bueno, así
no hay que invertir mucho tiempo lidiando con los detalles de este protocolo. Es importante la
importación de las librerías proc_common_v3_00_a y axi_lite_ipif_v1_01_a pues, aun si no las
usas, EDK no va a poder reconocer tu módulo si no están estas presentes. Como mencionamos
más arriba, sólo contiene la implementación de la interfaz AXI-Lite.
El archivo user_logic.vhd es más comprensible. Alrededor de la línea 182 vas está el código para
almacenar los datos en los registros :
case slv_reg_write_sel is
when "1000" =>
for byte_index in 0 to (C_SLV_DWIDTH/8)-1 loop
if ( Bus2IP_BE(byte_index) = '1' ) then
slv_reg0(byte_index*8+7 downto byte_index*8) <=
Bus2IP_Data(byte_index*8+7 downto byte_index*8);
end if;
end loop;
when "0100" => …
El bucle con el iterador byte_index sirve para seleccionar, hacienda usaso de la señales AXI-STRB
que bytes de tu registro deben cambiar sus datos. Te recomiendo retirar este bucle y actualizar
todo el contenido del registro, así obtienes un núcleo más ligero.
Más abajo en el código, alrededor de la línea 218, verás la sección en que se realiza la lectura de
los registros:
case slv_reg_read_sel is
when "1000" => slv_ip2bus_data <= slv_reg0;
when "0100" => slv_ip2bus_data <= slv_reg1;
when "0010" => slv_ip2bus_data <= slv_reg2;
when "0001" => slv_ip2bus_data <= slv_reg3;
when others => slv_ip2bus_data <= (others => '0');
end case;
Para este ejemplo vamos a modificar el comportamiento de esta operación. En vez de leer el
mismo dato que ha escrito, vamos a hacer que lea de regreso el complemento binario de este
dato.
Estos archivos VHDL se han agregado a la librería my_first_ip_v1_00_a del proyecto. Cualquier
otro código fuente que agreguemos a nuestro proyecto debemos agregarlo a esta librería (Source
properties>…).
MODIFICANDO LAS FUENTES
Antes de proseguir voy a hacer una observación. Al principio estaba usando la siguiente jeraquía
para agrupar las partes del periférico:
my_first_ip
No sé cuál es el problema, pero pase un buen par de noches tratando de que esto pudiera ser
reconocido por el EDK pero no obtuve ningún resultado. Era muy frustante. El error que me
aparecía era NGDBuild604. El EDK no generaba la netlist para el stream_logic.
Finalmente modifiqué el modulo con la siguiente jerarquía:
my_first_ip
user_logic (Donde se
implementa la interfaz slave y
se exportan las señales de la
interfaz stream)
stream_logic (Donde se
implementa la lógica de los
streams)
entity MiComplemento is
Port ( Entrada : in STD_LOGIC_VECTOR (31 downto 0);
Salida : out STD_LOGIC_VECTOR (31 downto 0));
end MiComplemento;
begin
Salida <= NOT Entrada;
end Behavioral;
Este archivo debe de agregarse a la librería de nuestro periférico, asi que seleccionamos este
archivo en la vista de diseño, botón derecho>Source Proeprties y en Source Library seleccionamos
nuestra librería:
Click en OK.
En user_logic.vhd vamos a importar ese componente:
library my_first_ip_v1_00_a;
use my_first_ip_v1_00_a.MiComplemento;
Agregamos la siguiente señal y componente a su arquitectura:
signal slv_reg0_operated : std_logic_vector(C_SLV_DWIDTH-1 downto 0);
component MiComplemento is
Port (
Entrada : in STD_LOGIC_VECTOR (31 downto 0);
Salida : out STD_LOGIC_VECTOR (31 downto 0)
);
end component MiComplemento;
Y finalmente instanciamos el componente:
--Mi operador
operacion : entity my_first_ip_v1_00_a.MiComplemento
port map(
Entrada => slv_reg0,
Salida => slv_reg0_operated
);
Lo último que queda es devolver esta señal a la hora de leer los registros:
case slv_reg_read_sel is
when "1000" => slv_ip2bus_data <= slv_reg0_operated;
…
Sintetiza esto. No deberían haber más errores.
PERSONALIZANDO LAS INTERFACES STREAM:
Ahora solo queda agregar las interfaces stream al periférico. Al igual que en la interfaz AXI4-Lite
vamos a hacer que a la salida devuelva el complemento de los datos que ingresan. Este es el
código fuente de la lógica del stream, StreamLogic.vhd:
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;
library proc_common_v3_00_a;
use proc_common_v3_00_a.proc_common_pkg.all;
entity StreamLogic is
generic(
C_M_AXIS_DATA_WIDTH : integer := 32;
C_S_AXIS_DATA_WIDTH : integer := 32
);
port(
axi_aclk : in std_logic;
axi_resetn : in std_logic;
-- Master Stream Ports
m_axis_tdata : out std_logic_vector(C_M_AXIS_DATA_WIDTH-1 downto 0);
m_axis_tstrb : out std_logic_vector((C_M_AXIS_DATA_WIDTH/8)-1 downto 0);
m_axis_tvalid : out std_logic;
m_axis_tready : in std_logic;
m_axis_tlast : out std_logic;
-- Slave Stream Ports
s_axis_tdata : in std_logic_vector(C_S_AXIS_DATA_WIDTH-1 downto 0);
s_axis_tstrb : in std_logic_vector((C_S_AXIS_DATA_WIDTH/8)-1 downto 0);
s_axis_tvalid : in std_logic;
s_axis_tready : out std_logic;
s_axis_tlast : in std_logic
);
attribute SIGIS : string;
attribute SIGIS of axi_aclk : signal is "CLK";
end entity StreamLogic;
begin
m_axis_tdata <= not s_axis_tdata;
m_axis_tstrb <= s_axis_tstrb;
m_axis_tvalid <= s_axis_tvalid;
s_axis_tready <= m_axis_tready;
m_axis_tlast <= s_axis_tlast;
end StreamIMP;
axi_aclk : in std_logic;
axi_resetn : in std_logic;
-- Master Stream Ports
m_axis_tdata : out std_logic_vector(C_M_AXIS_DATA_WIDTH-1 downto 0);
m_axis_tstrb : out std_logic_vector((C_M_AXIS_DATA_WIDTH/8)-1 downto 0);
m_axis_tvalid : out std_logic;
m_axis_tready : in std_logic;
m_axis_tlast : out std_logic;
-- Slave Stream Ports
s_axis_tdata : in std_logic_vector(C_S_AXIS_DATA_WIDTH-1 downto 0);
s_axis_tstrb : in std_logic_vector((C_S_AXIS_DATA_WIDTH/8)-1 downto 0);
s_axis_tvalid : in std_logic;
s_axis_tready : out std_logic;
s_axis_tlast : in std_logic
En user_logic, una vez que se han agregado las señales, se instancia este componente de la misma
forma que ‘MiComplemento.vhd’ y conecta las señales y generics de la siguiente forma:
LogicaStreaming : StreamLogic
generic map(
C_S_AXIS_DATA_WIDTH => C_S_AXIS_DATA_WIDTH,
C_M_AXIS_DATA_WIDTH => C_M_AXIS_DATA_WIDTH
)
port map(
--Interfaz axi stream
axi_aclk => axi_aclk,
axi_resetn => axi_resetn,
-- Master Stream Ports
m_axis_tdata => m_axis_tdata,
m_axis_tstrb => m_axis_tstrb,
m_axis_tvalid => m_axis_tvalid,
m_axis_tready => m_axis_tready,
m_axis_tlast => m_axis_tlast,
-- Slave Stream Ports
s_axis_tdata => s_axis_tdata,
s_axis_tstrb => s_axis_tstrb,
s_axis_tvalid => s_axis_tvalid,
s_axis_tready => s_axis_tready,
s_axis_tlast => s_axis_tlast
);
my_first_ip
user_logic (Donde se
implementa la interfaz slave y
se exportan las señales de la
interfaz stream)
stream_logic (Donde se
implementa la lógica de los MiComplemento
streams)
Usa los datos de la siguiente fugura y haz click en siguiente, Al hacerlo te va a advertir que vas a
sobre-escribir un componente anterior, haz click en OK.
En la pestaña Source File Types selecciona que solo vas a necesitar de archivos VHDL. Click en
siguiente.
La siguiente pestaña es la de HDL Analisys information. Aquí te dirá si ha podido encontrar todas
las fuentes y librerías de tu proyecto. Click en Next.
En la siguiente pestaña, Bus Interfaces, se selecciona los buses a identificar en nuestro periférico.
Selecciona AXI4-Lite- Slave. Click en Next.
Pulsa siguiente hasta llegar a la pestaña S_AXI4LITE: Parameter. Activa Register Space, como
dirección base selecciona C_BASEADDR y como dirección límite selecciona C_HIGHADDR. Click en
Next:
En la pestaña Identify Interrupt signals deselecciona Select and Configure interrupts y haz click en
next.
En la pestaña Parameter Attributes ve hacia el parámetro C_FAMILY en Default escribe Spartan6(o
el nombre de tu dispositivo). Click en Next.
En la última pestaña revisa tu diseño y haz click en finish. Tu periférico apareceré en junto a las
otras plantillas.
Agrégalo al proyecto de XPS haciéndole doble click. Deja los parámetros por defecto y cuando te
aparezca el diálogo Instantiate and Connect IP selecciona User will make necessary conenctions
and settings. Una vez agregado veraz que sólo aparece con la interfaz AXI4-Lite:
Hasta aquí se acabaron los aistentes.. todo se editará de forma manual. En la vista System
Assembly View, click derecho en my_first_ip_0 y selecciona View MPD. Aquí aparecerá el archivo
que usa XPS para reconocer los puertos de tu periférico. Casi al final verás las interfaces axi stream
con la siguiente declaración:
Finalmente ya tenemos nuestro periférico listo para ser usado en un proyecto. El siguiente paso
será verificar su funcionamiento.
VERIFICACION DEL FUNCIONAMIENTO DEL MODULO
Vamos a realizar la conexión entre el microblaze y nuestro periférico. Ya que la interfaz stream del
microblaze no usa las señales STRB, no puede hacerse la conexión directamente en la vista de
interfaz de buses, tiene que pasarse a la vista Puertos. En esta se conectan las señales del
periférico con las respectivas señales del microblaze del siguiente modo:
Aun habiendo hecho estas conexiones, no se reflejara en la vista de Interfaces de bus. Revisa las
conexiones de los relojes y la asignación de direcciones de memoria. El diseño está listo para
compilarse y grabarse en el FPGA.
SOFTWARE DE VERIFICACION
Exporta el diseño al SDK Eclipse para programar el software del microblaze que se encarga de
verificar el funcionamiento de este módulo. Usa el siguiente programa para verificar el diseño:
#include <stdio.h>
#include "platform.h"
#include "xparameters.h"
#include "xbasic_types.h"
#include "xutil.h"
#include "fsl.h"
int main()
{
int j;
printf("Hello World\n\r");
unsigned int readed;
for(j=0; j< 4; j++){
myNor[j] = 0x01U;
readed = (myNor[j]);
}
volatile unsigned int* myNor = XPAR_MY_FIRST_IP_0_BASEADDR;
init_platform();
unsigned int writed = 0x32U;
putfslx(writed, 0, FSL_NONBLOCKING);
getfslx(readed, 0, FSL_NONBLOCKING);
cleanup_platform();
return 0;
}
Al momento de depurar el programa revisa los valores de la variable readed, versa que va
cambiando de acuerdo a lo que se espera del diseño.
ENLACES
Documentación de Xilinx sobre sus herramientas de desarrollo:
http://www.xilinx.com/support/documentation/