A ver, que esto ya toca el tema de mi tesis, así que algo puedo aportar.
Primero: Verilog sí es un lenguaje. Jepalza, ¿qué purista dice que no lo es? Si la cosa va por mi, te r epito que lo que yo digo es que Verilog no es un lenguaje para programar, sino uno lenguaje para describir. Se enmarca en eso que llamamos HDL (Hardware Language Description).
Segundo: sobre qué sentido tiene un traductor de C a Verilog.... pues lo tiene. De hecho no es el único tipo de traducción que hay. También existen traductores de Python a VHDL/Verilog. Estos traductores son basicamente de dos tipos:
- Traductores que cogen una descripción hecha en C y la pasan a VHDL/Verilog. Esto es, se define un subconjunto del lenguaje C suficiente para que permita describir hardware, y se usa para eso, para describir hardware. El traductor coge esa descripción y la convierte en otra equivalente en VHDL/Verilog, porque son esos lenguajes los que entiende el sintetizador. Estos traductores están pensados para que el ingeniero que viene del mundo C pueda introducirse mejor en esto de la descripción de hardware ya que estarán acostumbrados a expresar comportamiento usando la sintaxis del C. Dado que Verilog en concreto se parece un poco a C, las descripciones que se hagan en C quedarán muy similares en Verilog. Algo así:
Código: Seleccionar todo
char Multiplexor (bit s, char a, char b)
{
if (!s)
return a;
else
return b;
}
Que se convierte en:
Código: Seleccionar todo
module Multiplexor (
input s,
input[7:0] a,
input[7:0] b,
output reg [7:0] returnvalue
);
always @(*) begin
if (!s)
returnvalue = a;
else
returnvalue = b;
end
endmodule
- Traductores que cogen un código C (o cualquier otro lenguaje de
programación) en el que se expresa un programa, un algoritmo, y traducen ese algoritmo (ya no es un subconjunto de C) a una descripción de un hardware que implementa ese algoritmo.
Esto es precisamente una de las cosas que estoy haciendo para la tesis. Consiste en que tú puedas escribir un programa que haga "algo" (que ejecute un algoritmo) y eso que has escrito, convertirlo a una pieza de hardware que realiza la misma función. Es la base de lo que se denomina "codiseño" que consiste en dado un programa de ordenador, y dado un sistema que contiene un procesador convencional y una FPGA, el ingeniero (o el compilador) elige qué partes del programa se ejecutan en el procesador, y qué partes se convierten en módulos hardware (se dice que "se ejecutan en silicio"). El código sintetizable generado de esta forma suele ser una máquina de estados finita que implementa los pasos del algoritmo. Algo así:
Código: Seleccionar todo
short Checksum (char v[1024])
{
short suma=0;
short i;
for (i=0;i<1024;i++)
suma = suma + v[i];
return suma;
}
Esta sencilla función realiza un checksum de un bloque de memoria de 1024 bytes (por ejemplo, con vistas a comprobar que el bloque es correcto). Si se está en un ambiente de codiseño y este módulo se requiere que funcione a muy alta velocidad, o se requiere que funcione de forma simultánea en varios flujos de datos, el compilador puede optar a convertirlo en un módulo hardware, que se comunicaría con el procesador bien por puertos de E/S, o bien en forma de nuevas instrucciones añadidas al procesador. En Verilog, el módulo podría quedar más o menos así (según el traductor que estoy haciendo

)
Código: Seleccionar todo
module Checksum (
input clk, // reloj del FSM
input startf, // señal para comenzar la funcion
output reg endf, // señal que indica que el valor de retorno es valido
output reg[9:0] addr_v, // bus de direcciones para V
output reg read_v, // señal de lectura para V
input[7:0] datain_v, // dato de entrada de V
output reg[15:0] returnvalue // valor de retorno
);
reg [15:0] i; // indice del bucle
reg [15:0] suma; // checksum
reg estado_fsm; // estado del autómata
initial begin
suma = 0;
estado_fsm = 0;
endf = 0;
read_v = 0;
end
always @(posedge clk) begin
case (estado_fsm)
0 : begin
if (startf) // solo pasamos de estado si pedimos que comience
estado_fsm <= 1;
endf <= 0;
i <= 0; // inicializacion del FOR
end
1 : begin
if (i==1024) // condicion de parada del FOR
estado <= 4
else
estado <= 2;
end
2 : begin
addr_v <= i[9:0]; // preparamos a la memoria para
read_v <= 1; // ser leida en la direccion i
estado_fsm <= 3;
end
3 : begin
suma <= suma + {8{datain_v[7]},datain_v}; // leemos memoria y
read_v <= 0; // sumamos su contenido, extendido en signo, a "suma"
i <= i + 1; // siguiente vuelta del FOR
estado_fsm <= 1; // vamos a ella
end
4 : begin
returnvalue <= suma; // actualizamos salida del circuito
endf <= 1; // señalamos que la salida es correcta
if (!startf) // handshake: solo salimos de aqui cuando
estado <= 0; // no esté activa startf
end
endcase
end
endmodule
Esto que se ve aquí es una descripción de un FSM que a su vez ejecuta el equivalente al código C que hace el checksum. Como "pieza hardware" que es, puede replicarse tantas veces como quepa en la FPGA para realizar checksums en paralelo (en bloques de memoria distintos, claro), o bien se puede usar como una función para hacer checksum muy rápida: el cuerpo del bucle FOR está implementado en tres estados del FSM, y estos estados se suceden 1024 veces, más el estado inicial y final, pues nos dan: 1+1+1024*3=3074 ciclos de reloj para esta implementación.
A modo de comparación, he escrito la misma rutina en lenguaje ensamblador del Z80 (no es precisamente un ARM ni va a 1GHz, pero a efectos de comparación vale). La rutina (seguramente mejorable) es ésta:
Código: Seleccionar todo
V equ 0 ;por ponerlo en algun sitio...
Start ld ix,V ; Vector V
ld hl,0 ; el checksum
ld bc,1024
VueltaBucle ld e,(ix+0) ; Leemos valor del vector
ld a,e ;
rlca ; Extensión de signo
sbc a,a ;
ld d,a ;
add hl,de ; sumamos a checksum
inc hl
dec bc
ld a,b
or c
jr nz,VueltaBucle
ret
Este algoritmo tarda 34 ciclos en la inicialización (las tres instrucciones desde Start). El bucle da 1024 vueltas, de las cuales 1023 tardan lo mismo (78 ciclos). La última vuelta tarda un poco menos porque el salto condicional, cuando no se cumple, usa menos ciclos (7 en lugar de 12), así que esta última vuelta tarda 73 ciclos. El total de ciclos consumidos es de 34+1023*78+73=79901.
Esto es, la implementación hardware del algoritmo tarda 3074 ciclos de reloj frente a los 79901 que tarda la versión para procesador. Es decir, la versión hardware es casi casi 26 veces más rápida (usando la misma frecuencia de reloj) que la versión del Z80.
Si alguien piensa que comparar una FPGA con un humilde Z80 es una comparación injusta, allá va el mismo código escrito para x86 (IA32) subconjunto RISC:
Código: Seleccionar todo
Start mov esi,V
mov ecx,0
mov edx,0
BucleChecksum movsx eax,byte ptr [esi+ecx]
add edx,eax
inc ecx
cmp ecx,1024
jnz BucleChecksum
ret
Voy a suponer que todas las instrucciones duran 1 ciclo de reloj, lo cual no es en absoluto cierto. De hecho ni siquiera el pipeline aquí puede comenzar una nueva instrucción en el siguiente ciclo de reloj porque hay dependencia real entre muchas de ellas dentro del bucle. Probablemente pueda haber bypass entre INC y CMP, pero definitivamente ADD tendrá que esperar a que MOVSX termine de leer la memoria.
Aun con esta simplificación, el algoritmo tarda 3+1024*5 = 5123 ciclos de reloj , 1.66 veces más lento (a la misma frecuencia de reloj) que la implementación en silicio.
PD: mi traductor no optimiza la FSM generada... si la hiciera a mano, podría meter el contenido del estado 2 en una de las ramas del IF del estado 1, con lo que tendría no 3 ciclos por vuelta, sino 2, aumentando dramáticamente la velocidad de ejecución en silicio, que pasaría de 3074 ciclos a 1+1+2*1024=2050 ciclos. Los números anteriores se transforman en: casi 39 veces más rápido el FSM que el Z80, y 2.5 veces más lento el x86 que el FSM.