GHDL + VHPIDIRECT o como crear, compilar y ejecutar un programa VHDL con llamadas a código C
GHDL ( http://ghdl.free.fr/ ) es un simulador open-source para VHDL, que permite la compilación y ejecución del código VHDL directamente en PC. Otra característica avanzada es la capacidad de utilizar funciones C desde el código VHDL.
Si no tenemos instalado GHDL en nuestro sistema, procedemos a ello:
apt-get install ghdl
Ejemplo
El ejemplo que vamos a realizar va a consistir en una implementación de un sumador en hardware (VHDL) y en software (ansi C), y se va a comparar su funcionalidad.
Empecemos con el componente hardware. La implementación consiste en una suma de dos enteros a la que se le ha introducido un error; cuando el operando A es 10 el resultado de la suma va a ser 5.
sumador.vhdl
library ieee;
use ieee.std_logic_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use ieee.std_logic_arith.all;
use std.textio.all;
entity sumador is
port( clk : in std_logic;
a : in integer;
b : in integer;
c : out integer
);
end sumador;
architecture behaviour of sumador is
signal c_out : integer := 0;
begin
c <= c_out;
process (clk)
--synopsys translate off
variable msg_line: line;
--synopsys translate on
begin
if clk'event and clk='1' then
if a=10 then
--synopsys translate off
write(msg_line, string'("Introduciendo error en la suma"));
writeline(output, msg_line);
--synopsys translate on
c_out <= 5;
else
c_out <= a + b;
end if;
end if;
end process;
end behaviour;
El componente software devuelve el resultado correcto de la suma de dos enteros.
suma_software.c
int sim_suma_software(int a,int b){
return a+b;
}
Con el objetivo de poder utilizar la función software desde el código VHDL vamos a utilizar la función de GHDL VHPIDIRECT (un equivalente a los ficheros de cabecera en ansi C). Para ello se ha de definir un paquete:
sumaC.vhdl
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
package sumaC is
function suma_software(a: integer; b: integer) return integer;
function sim_suma_software(a: integer; b: integer) return integer;
attribute foreign of sim_suma_software :
function is "VHPIDIRECT sim_suma_software";
end package;
package body sumaC is
function suma_software(a: integer; b: integer) return integer is begin
return sim_suma_software(a,b);
end suma_software;
function sim_suma_software(a: integer; b: integer) return integer is begin
assert false report "VHPI" severity failure;
end sim_suma_software;
end package body;
Por último creamos el banco de prueba, encargado de aportar la señales de entrada a los componentes y comparar su salida.
sumador_tb.vhdl
library ieee;
use ieee.std_logic_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use ieee.std_logic_arith.all;
use std.textio.all;
library work;
use work.sumaC.all;
entity sumador_tb is end sumador_tb;
architecture behaviour of sumador_tb is
signal clk : std_logic := '0';
signal a : integer := 0; -- Operando a
signal b : integer := 0; -- Operando b
signal c_h : integer := 0; -- Suma hardware
signal c_s : integer := 0; -- Suma software
component sumador is
port( clk : in std_logic;
a : in integer;
b : in integer;
c : out integer
);
end component;
begin
inst_sumador : sumador
port map (
clk => clk,
a => a,
b => b,
c => c_h
);
process
begin
wait for 10 ns;
clk <= not clk;
end process;
process (clk)
variable msg_line: line;
begin
if clk'event and clk='1' then
a <= a + 1;
b <= b + 1;
c_s <= suma_software(a,b);
if ( c_s /= c_h) then
write(msg_line, string'("Error en la suma"));
write(msg_line, string'(" -a: "));
write(msg_line, a-1);
write(msg_line, string'(" -b: "));
write(msg_line, b-1);
write(msg_line, string'(" -salida soft: "));
write(msg_line, c_s);
write(msg_line, string'(" -salida hard: "));
write(msg_line, c_h);
writeline(output, msg_line);
end if;
end if;
end process;
end behaviour;
Compilación
Una vez que tenemos todos los ingredientes pasamos a la compilación. Para simplificar el proceso se aporta el siguiente Makefile:
VHDLFILES = sumador.vhdl sumaC.vhdl sumador_tb.vhdl
CSRCS = suma_software.c
DUTIES = sumador_tb
CFLAGS = -g -Wall
LDFLAGS = -Wl,-L. -Wl,-lmysim -Wl,-lpthread
OBJS = $(CSRCS:%.c=%.o)
all: $(DUTIES)
libmysim.a: $(OBJS)
$(AR) ruv $@ $(OBJS)
work-obj93.cf: $(VHDLFILES)
ghdl -a --ieee=synopsys -fexplicit $(GHDLFLAGS) $(VHDLFILES)
sumador_tb : work-obj93.cf libmysim.a
ghdl -e --ieee=synopsys -fexplicit $(GHDLFLAGS) $(LDFLAGS) $@
clean:
rm *.o *.cf *.a sumador_tb
Con un simple make obtendremos nuestro fichero binario sumador_tb que lo podremos ejecutar normalmente.
Ejecución
Una de las ventajas de GHDL es que permite crear fichero ejecutables que no requieren ningún simulador extra. Así que procedemos a ejecutar sumador_tb:
./sumador_tb
Introduciendo error en la suma
Error en la suma -a: 10 -b: 10 -salida soft: 20 -salida hard: 5
Como se observa en el ejemplo, el programa se ejecuta y aparece como detecta el error introducido. Si además de la salida estandar se desea obtener el fichero de ondas, basta con ejecutar sumador_tb con la opción —vcd=onda.vcd. El resultado lo podremos observar con gtkwave onda.vcd.
Saludos.