GHDL + VHPIDIRECT o como crear, compilar y ejecutar un programa VHDL con llamadas a código C

Arco

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.

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

Muy buenas Paco, ¿qué tal va

Muy buenas Paco, ¿qué tal va todo?

Muy buen post. Muy útil poder utilizar código C para crear bancos de prueba para entidades en VHDL. Sabía que con Verilog se podía hacer lo mismo usando iverilog, lo cual me resultó muy útil hace algún tiempo cuando me puse a aprender el lenguaje para poder testear un pequeño filtro FIR que escribí antes de cargarlo en mi FPGA. Además el modelado de objetos mock necesarios para simular otras entidades se hace mucho mas rápido de este modo.

También busqué algo parecido para VHDL y oí hablar de GHDL, pero pensaba que estaba en una fase muy beta y no le hice mucho caso, así que mis bancos de pruebas para VHDL los escribía como scripts para Modelsim y además generaba archivos vcd para usarlos con GTKWave.

He probado tu receta con mis testbenches y funciona genial, obtengo los mismos resultados y el ejecutable también me puede generar archivos vcd, así que creo que Modelsim tiene los días contados en mi máquina...

Un saludo para todos allí.

The cause of the problem is:
The vendor put the bug there.
-- Meta amigo informático --