[iverilog][testbench] iverilog error: syntax error in reg variable list.

272 views
Skip to first unread message

Carlos

unread,
Mar 21, 2017, 6:11:13 PM3/21/17
to FPGAwars: explorando el lado libre
Que tal,

Estoy haciendo otro controlador para los display de 7 segmentos, estoy copiando descaradamente el controlador (y testbench) desarrollado en este vídeo.

Para comprobar el funcionamiento estoy usando iverilog, el problema que tengo es a la hora de ejecutar iverilog, lo ejecuto con el siguiente comando:
$ iverilog -o design bcd2segments.v bcd2segments_tb.v

El error que aparece es:
bcd2segments_tb.v:5: syntax error
bcd2segments_tb
.v:5: error: syntax error in reg variable list.

Que según yo el error esta en la declaración del registro expected, sin embargo no he podido dar con el error, según lo que entiendo expected es un conjunto de 16 registros de 7 bits, donde el registro 0 es el más significativo, hasta ahi todo bien, ¿El error estará en la definición del contenido?.

El bcd2segments_tb.v es el siguiente:
`timescale 1ns / 1ps

module bcd2segments_tb ( );

reg [6:0] expected[0:15] = { 7'h3F, 7'h06, 7'h5B, 7'h4F,
                             7'h66, 7'h6D, 7'h7D, 7'h07,
                             7'h7F, 7'h67, 7'h7A, 7'h7C,
                             7'h39, 7'h5E, 7'h79, 7'h71 };

reg [3:0] bcd;
wire [6:0] segment;

bcd2segments uut( .bcd(bcd) , .segments(segment) );

initial begin

    bcd = 0;
    forever begin
        bcd = #1 bcd + 1;
    end

end

always @(expected) begin

    if ( segment != expected[bcd] ) begin
        $display("Expected %x,  Found %x for BCD %x\n", expected[bcd], segment, bcd);
        $finish;
    end else if ( bcd == 15 ) begin
        $finish;
    end

end

endmodule

Adjunto los archivos del proyecto por si alguien quiere verlos de cerca.

Saludos
test.zip

1138-4EB

unread,
Mar 21, 2017, 7:49:12 PM3/21/17
to FPGAwars: explorando el lado libre
Hola Carlos,

Estás tratando de definir un array de 7 señales de 16 bits o un array de 16 señales de bits? A botepronto, diría que quieres hacer lo segundo, pero estás haciendo lo primero. Sólo cambia los rangos.

Un saludo

Carlos

unread,
Mar 21, 2017, 9:04:55 PM3/21/17
to FPGAwars: explorando el lado libre
Hola ,

Si, Intentaba hacer lo segundo, un array de 16 elementos (el elemento 0 es el más significativo), cada elemento de 7 bits, según este manual que encontré esta bien declarado el array.
Cambie los rangos y me sigue arrojando el mismo error ;(,

Intenté con:
reg [0:15] expected[6:0] = ...

reg [6:0] expected[15:0] = ...


Después intenté inicializar los valores de expected y bcd un el bloque initial:

bcd2segments_tb.v
`timescale 1ns / 1ps

module bcd2segments_tb ( );

reg [6:0] expected[0:15];

/*reg [6:0] expected[0:15] = { 7'h3F, 7'h06, 7'h5B, 7'h4F,

                            7'h66, 7'h6D, 7'h7D, 7'h07,
                            7'h7F, 7'h67, 7'h7A, 7'h7C,
                            7'h39, 7'h5E, 7'h79, 7'h71 };*/


reg [3:0] bcd;
wire [6:0] segment;

bcd2segments uut( .bcd(bcd) , .segments(segment) );

initial begin

    // Verilog system tasks
    $dumpfile("bcd2segments_tb.vcd");
    $dumpvars(0, bcd2segments_tb);

    bcd = 0;

    expected[0] = 7'h3F;
    expected[1] = 7'h06;
    expected[2] = 7'h5B;
    expected[3] = 7'h4F;
    expected[4] = 7'h66;
    expected[5] = 7'h6D;
    expected[6] = 7'h7D;
    expected[7] = 7'h07;
    expected[8] = 7'h7F;
    expected[9] = 7'h67;
    expected[10] = 7'h7A;
    expected[11] = 7'h7C;
    expected[12] = 7'h39;
    expected[13] = 7'h5E;
    expected[14] = 7'h79;
    expected[15] = 7'h71;


    forever begin
        bcd = #1 bcd + 1;
    end

end

always @(bcd) begin


    if ( segment != expected[bcd] ) begin
        $display("Expected %X,  Found %X for BCD %X\n", expected[bcd], segment, bcd);
        $finish;
    end else if ( bcd == 16 ) begin
        $finish;
    end

end

endmodule

Ejecute el comando:

$ iverilog -o design bcd2segments.v bcd2segments_tb.v

Ejecute el archivo design con vvp:
$ vvp design

y obtuve el resultado:
VCD info: dumpfile bcd2segments_tb.vcd opened for output.
Expected 5b,  Found 06 for BCD 2

Dejo el resolver ese problema para mañana xD

Para ver el dump con GTKWave:
$ gtkwave bcd2segments_tb.vcd &

Adjunto los archivos actualizados por si quieren revisarlos.

Saludos
test.zip

Juan Gonzalez Gomez

unread,
Mar 22, 2017, 4:23:30 AM3/22/17
to FPGA-WARS: explorando el lado libre
Hola Carlos,

No he visto todavía el código. En cuanto pueda le echo un vistazo.

Sólo quería comentarte que la simulación mucho más fácil usando apio

Simplemente ejecuta este comando en el directorio donde tienes tus ficheros verilog:

$ apio sim
Info: default SConstruct file
iverilog -B "/home/obijuan/.apio/packages/toolchain-iverilog/lib/ivl" -o bcd2segments_tb.out -D VCD_OUTPUT=bcd2segments_tb "/home/obijuan/.apio/packages/toolchain-iverilog/vlib/cells_sim.v" bcd2segments.v bcd2segments_tb.v
vvp -M "/home/obijuan/.apio/packages/toolchain-iverilog/lib/ivl" bcd2segments_tb.out

VCD info: dumpfile bcd2segments_tb.vcd opened for output.
Expected 06,  Found 3f for BCD 1

gtkwave bcd2segments_tb.vcd bcd2segments_tb.gtkw

GTKWave Analyzer v3.3.66 (w)1999-2015 BSI

[0] start time.
[1000] end time.
** WARNING: Error opening save file 'bcd2segments_tb.gtkw', skipping.

Te abrirá automáticamente el gtkwave. Ahí seleccionas las señales que quieres visualizar:

Imágenes integradas 1


Luego le das a File/Write Save File.

La próxima vez que ejecutes apio sim se te abrirá el gtkwave con las señales ya incorporadas en el visor

Saludos, Obijuan



--
Has recibido este mensaje porque estás suscrito al grupo "FPGAwars: explorando el lado libre" de Grupos de Google.
Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a fpga-wars-explorando-el-lado-libre+unsubscribe@googlegroups.com.
Para publicar en este grupo, envía un correo electrónico a fpga-wars-explorando-el-lado-li...@googlegroups.com.
Visita este grupo en https://groups.google.com/group/fpga-wars-explorando-el-lado-libre.
Para ver esta conversación en el sitio web, visita https://groups.google.com/d/msgid/fpga-wars-explorando-el-lado-libre/1b41e863-ddbb-4639-8a94-7e9e4a210c5b%40googlegroups.com.

Para acceder a más opciones, visita https://groups.google.com/d/optout.

Carlos

unread,
Mar 22, 2017, 8:04:33 PM3/22/17
to FPGAwars: explorando el lado libre
Hola Obijuan,

Si me estoy arrepintiendo de no haber empezado el proyecto con apio xD, pero aprendí algunas cosas que no conocia antes. En cuanto tenga acceso al código subo la actualización, termine utilizando $readmemh para "llenar" el array.

Pero ahora tengo un problema con los resultados de la simulación :(, creo que es por el operador ternario "anidado" en el módulo, no en el testbench.

Saludos

1138-4EB

unread,
Mar 23, 2017, 6:13:41 AM3/23/17
to FPGAwars: explorando el lado libre
Hola Carlos,

En tu primera versión la asignación al array habría funcionado en SystemVerilog, pero no en Verilog: http://stackoverflow.com/questions/36391745/verilog-array-assignment

Por otro lado, si bcd es de 4 bits, nunca llegará a 16, puesto que 2⁴-1=15.

En tercer lugar, he instalado iverilog en Fedora 25, y no necesito vpp. De hecho, no existe. Directamente design es un ejecutable (cómo hace GHDL con GCC/LLVM).

Por último, tu simulación sólo terminará si no es errónea. Mientras lo sea, no se evalua bcd==16 (o ==15). Creo que no deberías hacer un "else if" sino dos if diferentes, uno detrás de otro:

`timescale 1ns / 1ps

module bcd2segments_tb ( );

reg [6:0] expected[0:15];

reg [3:0] bcd;
wire [6:0] segment;

bcd2segments uut( .bcd(bcd) , .segments(segment) );

initial begin
  $dumpfile("bcd2segments_tb.vcd");
  $dumpvars(0, bcd2segments_tb);
  expected[0] = 7'h3F;
  expected[1] = 7'h06;
  expected[2] = 7'h5B;
  expected[3] = 7'h4F;
  expected[4] = 7'h66;
  expected[5] = 7'h6D;
  expected[6] = 7'h7D;
  expected[7] = 7'h07;
  expected[8] = 7'h7F;
  expected[9] = 7'h67;
  expected[10] = 7'h7A;
  expected[11] = 7'h7C;
  expected[12] = 7'h39;
  expected[13] = 7'h5E;
  expected[14] = 7'h79;
  expected[15] = 7'h71;

  bcd = 0;
  forever begin
    bcd = bcd + 1;
    #10;
  end

end

always @(bcd) begin

  if ( segment != expected[bcd] ) begin
    $display("Expected %X,  Found %X for BCD %X\n", expected[bcd], segment, bcd);
  end
  if ( bcd == 'hf ) begin $finish; end
end

endmodule

El resultado es el siguiente:

iverilog -o design bcd2segments.v bcd2segments_tb.v
[user@localhost test]$ ./design
VCD info
: dumpfile bcd2segments_tb.vcd opened for output.

Expected 5b,  Found 06 for BCD 2
Expected 4f,  Found 5b for BCD 3
Expected 66,  Found 4f for BCD 4
Expected 6d,  Found 66 for BCD 5
Expected 7d,  Found 6d for BCD 6
Expected 07,  Found 7d for BCD 7
Expected 7f,  Found 07 for BCD 8
Expected 67,  Found 7f for BCD 9
Expected 7a,  Found 67 for BCD a
Expected 7c,  Found 7a for BCD b
Expected 39,  Found 7c for BCD c
Expected 5e,  Found 39 for BCD d
Expected 79,  Found 5e for BCD e
Expected 71,  Found 79 for BCD f


Por lo tanto, hay algún registro por ahí que está provocando un retardo de un ciclo. Pero el funcionamiento es correcto.

Un saludo

1138-4EB

unread,
Mar 23, 2017, 6:20:03 AM3/23/17
to FPGAwars: explorando el lado libre
Auto Generated Inline Image 1

Carlos

unread,
Mar 23, 2017, 3:39:03 PM3/23/17
to FPGAwars: explorando el lado libre

Hola,

Gracias por revisar el proyecto :D, en efecto la asignación del array funciona con SV, termine usando $readmemh para llenar el array, me pareció mas rápido aunque más confuso :/ ¿que opinas?, vvp aparece en el man de iverilog, pero tienes razón, se puede ejecutar el archivo que se obtiene de usar iverilog directamente :D. Y ya incluí el ultimo punto que me dices, acabar la simulación solo hasta que bcd llega a 15.

bcd2segments_tb.v
`timescale 1ns / 1ps
`
default_nettype none

module bcd2segments_tb ( );

reg
[6:0] expected[0:15];

reg
[3:0] bcd;
wire
[6:0] segment;

bcd2segments uut
( .bcd(bcd) , .segments(segment) );

initial
begin


   
// Verilog system tasks

   
// $dumpfile genera un archivo
   
// donde se almacenan todas las
   
// variables del diseño
    $dumpfile
("bcd2segments.vcd");
    $dumpvars
(0, bcd2segments_tb);

   
// ("file", array, start_addr, end_addr);
    $readmemh
("mem.data", expected, 0, 15);

   
// expected[4'd0] = 7'h3F;
   
// expected[4'd1] = 7'h06;
   
// expected[4'd2] = 7'h5B;
   
// expected[4'd3] = 7'h4F;
   
// expected[4'd4] = 7'h66;
   
// expected[4'd5] = 7'h6D;
   
// expected[4'd6] = 7'h7D;
   
// expected[4'd7] = 7'h07;
   
// expected[4'd8] = 7'h7F;
   
// expected[4'd9] = 7'h67;
   
// expected[4'd10] = 7'h7A;
   
// expected[4'd11] = 7'h7C;
   
// expected[4'd12] = 7'h39;
   
// expected[4'd13] = 7'h5E;
   
// expected[4'd14] = 7'h79;
   
// expected[4'd15] = 7'h71;


    bcd
= 0;

    forever
begin
        bcd
= bcd + 1;
       
#10;
   
end

end

always
@(bcd) begin

   
if ( segment != expected[bcd] ) begin

        $display
("Error: Expected %x,  Found %x for BCD %x", expected[bcd], segment, bcd);
   
end
   
if ( bcd == 4'hF ) begin
        $finish;
    end

end

endmodule


mem.data
3F
06
5B
4F
66
6D
7D
07
7F
67
7A
7C
39
5E
79
71

bcd2segments.v
`timescale 1ns / 1ps
`
default_nettype none

module bcd2segments
(
    input
[3:0] bcd,
    output
[6:0] segments
);

assign segments
=   (bcd == 4'h0) ? 7'h3F :
                   
(bcd == 4'h1) ? 7'h06 :
                   
(bcd == 4'h2) ? 7'h5B :
                   
(bcd == 4'h3) ? 7'h4F :
                   
(bcd == 4'h4) ? 7'h66 :
                   
(bcd == 4'h5) ? 7'h6D :
                   
(bcd == 4'h6) ? 7'h7D :
                   
(bcd == 4'h7) ? 7'h07 :
                   
(bcd == 4'h8) ? 7'h7F :
                   
(bcd == 4'h9) ? 7'h67 :
                   
(bcd == 4'hA) ? 7'h7A :
                   
(bcd == 4'hB) ? 7'h7C :
                   
(bcd == 4'hC) ? 7'h39 :
                   
(bcd == 4'hD) ? 7'h5E :
                   
(bcd == 4'hE) ? 7'h79 : 7'h71;

endmodule

No se si sea correcto pero ¿ se puede utilizar los gráficos que genera yosys para descubrir ese registro?
utilice el siguiente script:

scipt_cto.ys
read_verilog bcd2segments.v
show
-pause
proc
show
-pause
opt
show
-pause

y despues de opt me genero la imagen del inicio de correo.


Saludos y gracias por la ayuda :D

Carlos

unread,
Mar 23, 2017, 3:40:02 PM3/23/17
to FPGAwars: explorando el lado libre
Olvidaba adjuntar el zip más reciente.
test.zip

1138-4EB

unread,
Mar 23, 2017, 4:59:40 PM3/23/17
to FPGAwars: explorando el lado libre

Gracias por revisar el proyecto :D, en efecto la asignación del array funciona con SV, termine usando $readmemh para llenar el array, me pareció mas rápido aunque más confuso :/ ¿que opinas?

Creo que es interesante sobre todo que hayas encontrado una solución alternativa. En este caso en concreto igual sí es un poco overkill, y desde luego como ejemplo didáctico es mucho más explícita la primera solución. Pero yo genero a menudo ficheros con miles o millones de datos desde Matlab, Python, Golang o cualquier otro lenguaje y los cargo como entradas de un testbench. En ese caso entenderás que readmem es la única solución.

Por lo tanto, guarda ambos testbench como referencia ;).
 
vvp aparece en el man de iverilog, pero tienes razón, se puede ejecutar el archivo que se obtiene de usar iverilog directamente :D. Y ya incluí el ultimo punto que me dices, acabar la simulación solo hasta que bcd llega a 15.

No, no tengo razón XD. Bueno, a medias. El caso es que soy un poco cazurro y estaba escribiendo "vpp", en vez de "vvp". En cualquier caso, sí que funciona sin ello. No sé si en sistemas más complejos, con varios módulos compilados por separado, "vvp" será necesario para enlazarlos. Igual Juan o Jesús, que han le han dedicado más tiempo para hacer apio, te lo pueden aclarar.
 
No se si sea correcto pero ¿ se puede utilizar los gráficos que genera yosys para descubrir ese registro?
utilice el siguiente script:

La verdad es que ni me había parado a mirar el contenido de bcd2segments, sólo había abierto el testbench. Viéndolo ahora, no hay ningún registro. El "problema" tiene que ver con cómo se simulan los lenguajes HDL: cuando se incrementa bcd, hay "carreras" para ejecutar las diferentes tareas provocadas por ese cambio. En un circuito real serían la latencia de cada puerta y la de las propias conexiones. En tu caso por un lado se procesa el contenido del process y por otro el interior del módulo. No tienes ni idea de cuánto va a tardar cada uno ni en qué orden se van a procesar. Por eso el contenido es imprecedible. Si se ejecuta "display" antes de que se haya evuado el contenido del módulo, muestra el valor anterior. Fíjate que sí añades una línea como esta fuera del proceso,

assign thissegment = expected[bcd]

always @(bcd) begin
 
#1;
 
if ( segment != thissegment ) begin
    $display
("Expected %X,  Found %X for BCD %X\n", thissegment, segment, bcd);
 
end
 
if ( bcd == 'hf ) begin $finish; end
end

el resultado en consola sigue siendo incorrecto, pero la simulación con GtkWave es correcta. Se confirma que se trata de un problema que se da en los primerísimos ciclos del procesador, después de que se dé un evento.

Hay varias soluciones posibles, pero la más fácil que se me ocurre es leer todo un ciclo delta más tarde, y así te aseguras de que se ha estabilizado:

always @(bcd) begin
 
#1;

 
if ( segment != expected[bcd] ) begin

    $display
("Expected %X,  Found %X for BCD %X\n", expected[bcd], segment, bcd);
 
end
 
if ( bcd == 'hf ) begin $finish; end
end

Ese ciclo delta es una forma de decirle al simulador que evalúe todo lo que deba inmmediatamente después de un cambio (que utilice todos los ciclos de procesador que necesite para hacerlo), y cuando termine evalúe el contenido del process.

En cuanto a yosys, no tengo el ojo nada entrenado para ese tipo de diagramas. Pero en este caso es bastante limpio: hay quince comparadores con bcd en una entrada y una constante en la otra. El decimosexto caso es cuando ninguno de los comparadores está activo. Cada comparador genera una señal de un solo bit y las quince señales van a multiplexores de dos entradas y una salida. Por lo tanto, es el equivalente a:

if entrada1 then salida1
elsif entrada2 then salida2
elsif entrada3 then salida3
elsif entrada4 then salida4
...

Ahí tienes la explicación de porqué el contenido del process mostraba valores viejos. El simulador tiene que simular todos y cada uno de los multiplexores, lo que le lleva por lo menos quince ciclos, muchos menos que la comparación de dentro del process.

En el circuito implementado en la FPGA sucederá lo mismo: tendrás el máximo retardo posible desde que cambie el valor de bcd hasta que cambie la salida, porque los electrones tienen que recorrer todos los multiplexores en cadena. El efecto es que la frecuencia de reloj máxima que obtendrás será la mínima posible para este circuito.

¿Se te ocurre cómo cambiar la descripción de tu módulo para reducir el retardo? Ten en cuenta que en una FPGA puedes hacer cosas en paralelo. ¿Cómo podrías reordenar todos esos multiplexores para que el dibujo final de yosys sea más estrecho? Es decir, que de izquierda a derecha la señal más larga pase por el menor número posible de bloques.

Saludos

1138-4EB

unread,
Mar 23, 2017, 5:02:17 PM3/23/17
to FPGAwars: explorando el lado libre
He metido la pata: en el primer trozo de código elimina #1. Si lo dejas, funcionará bien y mi comentario posterior no tendrá sentido.

Carlos

unread,
Mar 23, 2017, 7:25:30 PM3/23/17
to FPGAwars: explorando el lado libre

Hola,

Ahora entiendo un poco mejor las simulaciones xD.


Con respecto a otra implementación me di cuenta que es una look-up-table, donde se tiene un array auxiliar, a la salida segments se le asigna una posición de ese array, la posición se controla con la entrada bcd. Así quedo mi implementación (por ahora xD):


bcd2segments.v

`timescale 1ns / 1ps
`
default_nettype none

module bcd2segments
(
    input
[3:0] bcd,
    output
[6:0] segments
);


reg
[6:0] LUT[0:15];

initial
begin
   
    LUT
[4'h0] = 7'h3F;
    LUT
[4'h1] = 7'h06;
    LUT
[4'h2] = 7'h5B;
    LUT
[4'h3] = 7'h4F;
    LUT
[4'h4] = 7'h66;
    LUT
[4'h5] = 7'h6D;
    LUT
[4'h6] = 7'h7D;
    LUT
[4'h7] = 7'h07;
    LUT
[4'h8] = 7'h7F;
    LUT
[4'h9] = 7'h67;
    LUT
[4'hA] = 7'h7A;
    LUT
[4'hB] = 7'h7C;
    LUT
[4'hC] = 7'h39;
    LUT
[4'hD] = 7'h5E;
    LUT
[4'hE] = 7'h79;
    LUT
[4'hF] = 7'h71;

end

assign segments
= LUT[bcd];

endmodule

El test bench tuve que añadir algunos ciclos para poder ver cuando bcd vale 0 y F:
`timescale 1ns / 1ps
`
default_nettype none

module bcd2segments_tb ( );

reg
[6:0] expected[0:15];


reg
[3:0] bcd;
wire
[6:0] segment;

bcd2segments uut
( .bcd(bcd) , .segments(segment) );

initial
begin


   
// Verilog system tasks


   
// $dumpfile genera un archivo
   
// donde se almacenan todas las
   
// variables del diseño
    $dumpfile
("bcd2segments.vcd");
    $dumpvars
(0, bcd2segments_tb);


    expected
[4'd0] = 7'h3F;

    expected
[4'd1] = 7'h06;

    expected
[4'd2] = 7'h5B;

    expected
[4'd3] = 7'h4F;

    expected
[4'd4] = 7'h66;

    expected
[4'd5] = 7'h6D;

    expected
[4'd6] = 7'h7D;

    expected
[4'd7] = 7'h07;

    expected
[4'd8] = 7'h7F;

    expected
[4'd9] = 7'h67;

    expected
[4'd10] = 7'h7A;

    expected
[4'd11] = 7'h7C;

    expected
[4'd12] = 7'h39;

    expected
[4'd13] = 7'h5E;

    expected
[4'd14] = 7'h79;

    expected
[4'd15] = 7'h71;


    bcd
= 0;

    forever
begin
       
#1;

        bcd
= bcd + 1;

       
#1;

   
end

end

always
@(bcd) begin

   
#1
    $display
("Expected %x,  Found %x for BCD %x", expected[bcd], segment, bcd);


   
if ( segment != expected[bcd] ) begin

        $display
("Error: Expected %x,  Found %x for BCD %x", expected[bcd], segment, bcd);
   
end
   
if ( bcd == 4'hF ) begin
        $finish;
    end

end

endmodule


Y el resultado en gtkwave:




Despues corri el script de yosys ($ yosys script_cto.ys) para ver como habia quedado la implementación y obtuve la imagen de abajo (solo capture lo "mas interesante") los bloques $meminit van dese ADDR = 0 hasta F y en DATA van los valores de cada elemento de la LUT. El bloque $memrd se ve como bcd controla ADDR y a la salida tenemos segments.




No se me ha ocurrido otra forma de hacer el diseño xD


Saludos


test2.zip

Carlos

unread,
Mar 23, 2017, 7:29:36 PM3/23/17
to FPGAwars: explorando el lado libre
Hasta ahora no había sintetizado, este es el resultado de  $yosys -p "synth_ice40 -blif bcd2segments.blif" bcd2segments.v

=== bcd2segments ===

   Number of wires:                  2
   Number of wire bits:             11
   Number of public wires:           2
   Number of public wire bits:      11
   Number of memories:               0
   Number of memory bits:            0
   Number of processes:              0
   Number of cells:                  7
     SB_LUT4                         7

Saludos

Carlos

unread,
Mar 23, 2017, 7:34:40 PM3/23/17
to FPGAwars: explorando el lado libre
Perdón por el spam,

Yosys reporto un warning en la inicialización del registro LUT, es con asignación continua <=, en vez de =.

Saludos

1138-4EB

unread,
Mar 24, 2017, 3:15:27 AM3/24/17
to FPGAwars: explorando el lado libre
Buenos días
 

Con respecto a otra implementación me di cuenta que es una look-up-table, donde se tiene un array auxiliar, a la salida segments se le asigna una posición de ese array, la posición se controla con la entrada bcd. Así quedo mi implementación (por ahora xD):

 
La verdad es que no era la opción que se me había pasado por la cabeza, pero es una alternatva muy válida también.

El test bench tuve que añadir algunos ciclos para poder ver cuando bcd vale 0 y F:

Es más "cómodo" que empieces con bcd=15. Así todos los estados duran los mismo ;).

No se me ha ocurrido otra forma de hacer el diseño xD


Cuando te lo comenté lo que yo tenía en la cabeza era convertir la escalera de multiplexores en un árbol. Pseudocódigo, no Verilog:


etapa1a = entrada1? constante1 ; constante2;
etapa1b
= entrada3? constante3 ; constante4;
etapa1c
= entrada5? constante5 ; constante6;
etapa1d
= entrada7? constante7 ; constante8;

etapa2a
= entrada12? etapa1a ; etapa1b;
etapa2b
= entrada56? etapa1c ; etapa1d;

etapa3
= entrada1234? etapa2a ; etapa2b;


Este es un ejemplo de 8 constantes. En la primera etapa se comparan las ocho entradas de dos en dos, y se multiplexan, de forma que en la segunda etapa hay 4 entradas. En la segunda etapa, se hace lo mismo para obtener dos salidas de las cuatro entradas, En la última, sólo quedan dos opciones que multiplexar.


Son 7 multiplexores en lugar de quince, y cada señal sólo tiene que atravesar tres etapas de izquierda a derecha. Por contra, requiere alguna puerta adicional. En general creo que es más eficiente que la primera solución. No sabría decir en comparación con esta última. Sería interesante que probaras el resultado de síntesis de las diferentes soluciones para ver hasta qué punto el sintetizador es capaz de darse cuenta de que están haciendo lo mismo de diferentes formas.


Por ejemplo, un ejercicio que hice ayer, como si fuera un sudoku, fue coger tu tabla e implementarla sólo con puertas lógicas. Sin ninguna constante ni multplexor. Aquí el desarrollo completo:


#Tabla de la verdad
3210 gfedcba
------------
0000 0111111
0001 0000110
0010 1011011
0011 1001111
0100 1100110
0101 1101101
0110 1111101
0111 0000111
1000 1111111
1001 1100111
1010 1111010
1011 1111100
1100 0111001
1101 1011110
1110 1111001
1111 1110001

#Cuándo es 1
a
= 0 2 3 5 6 7 8 9 12 14 15
b
= 0 1 2 3 4 7 8 9 10 13
c
= 0 1 3 4 5 6 7 8 9 11 13
d
= 0 2 3 5 6 8 10 11 12 13 14
e
= 0 2 6 8 10 11 12 13 14 15
f
= 0 4 5 6 8 9 10 11 12 14 15
g
= 2 3 4 5 6 7 8 9 10 11 13 14 15

# Cuándo es cero
a
= 1 4 10 11 13
b
= 5 6 11 12 14 15
c
= 2 10 12 14 15
d
= 1 4 7 9 15
e
= 1 3 4 5 7 9
f
= 1 2 3 7 13
g
= 0 1 7 12

# Parece más compacto identificar cuándo es cero y negarlo
a
= not ( 0001 or 0100 or 1010 or 1011 or 1101 )
b
= not ( 0101 or 0110 or 1011 or 1100 or 1110 or 1111 )
c
= not ( 0010 or 1010 or 1100 or 1110 or 1111 )
d
= not ( 0001 or 0100 or 0111 or 1001 or 1111 )
e
= not ( 0001 or 0011 or 0100 or 0101 or 0111 or 1001 )
f
= not ( 0001 or 0010 or 0011 or 0111 or 1101 )
g
= not ( 0000 or 0001 or 0111 or 1100 )

Y este es el módulo resultante:


module bcd2segments_gates (
  input
[3:0] i,
  output
[6:0] o
);


wire yn0n1
, y01, y012, y0n1n2, yn23, y23, y0n1, y0n1n2n3, yn2n3, yn1n2n3, yn0n123, y012n3, yn01, y2n3;

assign o
[0] = ~( ((~i[3]) & (~i[1]) & (i[0] ^ i[2])) | (yn23 & i[1]) | (y23 & y0n1) );
assign o
[1] = ~( (y2n3 & (i[1] ^ i[0])) | (yn23 & y01) | (y23 & ~(i[0] & (~i[1]))) );
assign o
[2] = ~( (yn01 & (~i[2])) | (y23 & (~y0n1)) );
assign o
[3] = ~( (y2n3 & yn0n1) | y0n1n2 | y012 );
assign o
[4] = ~( (yn2n3 & i[0]) | (y2n3 & (~yn01)) | (yn23 & y0n1) );
assign o
[5] = ~( y0n1n2n3 | (yn2n3 & i[1]) | y012n3 | (y23 & y0n1) );
assign o
[6] = ~( yn1n2n3 | y012n3 | yn0n123 );

assign y01
= i[0] & i[1];
assign y012
= y01 & i[2];
assign yn23
= (~i[2]) & i[3];
assign y23
= i[2] & i[3];
assign y0n1
= (~i[1]) & i[0];
assign y0n1n2
= y0n1 & (~i[2]);
assign y0n1n2n3
= yn2n3 & y0n1;
assign yn2n3
= (~i[2]) & (~i[3]);
assign yn1n2n3
= (~i[1]) & yn2n3;
assign yn0n123
= (~i[0]) & (~i[1]) & i[2] & i[3];
assign y012n3
= y012 & (~i[3]);
assign yn01
= (~i[0]) & i[1];
assign y2n3
= i[2] & (~i[3]);
assign yn0n1
= (~i[0]) & (~i[1]);

endmodule

Como ves, la reducción la hice a mano, sin tablas de Karnaugh ni nada, por lo que es posible que todavía se pueda optimizar un poco más. Pero creo que se entiende la idea. Aquí el testbench para compararlo con la versión anterior:


`timescale 1ns / 1ps


module bcd2segments_tb ( );

reg [6:0] expected[0:15];
wire [6:0] segment, segment_gates;
reg [3:0] bcd;

wire [6:0] thisegment;


bcd2segments uut( .bcd(bcd) , .segments(segment) );
bcd2segments_gates uut_gates( .i(bcd) , .o(segment_gates) );


initial begin
  $dumpfile("bcd2segments_tb.vcd");
  $dumpvars(0, bcd2segments_tb);
  expected[0] = 7'h3F;
  expected[1] = 7'h06;
  expected[2] = 7'h5B;
  expected[3] = 7'h4F;
  expected[4] = 7'h66;
  expected[5] = 7'h6D;
  expected[6] = 7'h7D;
  expected[7] = 7'h07;
  expected[8] = 7'h7F;
  expected[9] = 7'h67;
  expected[10] = 7'h7A;
  expected[11] = 7'h7C;
  expected[12] = 7'h39;
  expected[13] = 7'h5E;
  expected[14] = 7'h79;
  expected[15] = 7'h71;

  bcd = 15;
  forever begin

    bcd = bcd + 1;
    #200;

  end

end

always @(bcd) begin
  #1;
  $display("Expected %X,  Found %X and %X for BCD %X\n", expected[bcd], segment, segment_gates, bcd);
  if ( bcd == 'hf ) begin $finish; end
end

endmodule

Y el resultado:



Como ves, se aprecia la activación de cada señal auxiliar. Aunque pueda parecer aleatorio, la salida es correcta (idéntica a la tuya). ¿Qué diferencia habrá en el consumo de recursos?


Saludos

Auto Generated Inline Image 1

Carlos 47

unread,
Mar 24, 2017, 3:41:53 AM3/24/17
to fpga-wars-explora...@googlegroups.com
Hola,

Tendre que hacer un dibujo para poder entender el circuito xD, me intriga como pudiste implementarlo solo utilizando compuertas :D. Más tarde (son 2am aca en México) sintetizo tu diseño y vemos los resultados que arroja yosys, estuve leyendo el manual y las celdas $memrd y $memwr que obtengo en el diseño de la LUT se mapean a la arquitectura "objetivo", en este caso la ICE40, en los mensajes que arroja yosys durante las optimizaciones me aparecieron celdas de la memoria RAM pero al final no reporta que usó celdas de RAM :/.

Tal vez sea conveniente usar el visualizador en linea donde cargas el blif y te muestra el uso de recursos :D


Saludos y gracias por la ayuda.

--
Has recibido este mensaje porque estás suscrito al grupo "FPGAwars: explorando el lado libre" de Grupos de Google.
Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a fpga-wars-explorando-el-lado-libre+unsubscribe@googlegroups.com.
Para publicar en este grupo, envía un correo electrónico a fpga-wars-explorando-el-lado-li...@googlegroups.com.
Visita este grupo en https://groups.google.com/group/fpga-wars-explorando-el-lado-libre.

1138-4EB

unread,
Mar 24, 2017, 4:14:15 AM3/24/17
to FPGAwars: explorando el lado libre

Tendre que hacer un dibujo para poder entender el circuito xD, me intriga como pudiste implementarlo solo utilizando compuertas :D.

Puedes usar esto: http://johndro.github.io/ Aunque a mí a veces se me queda un poco colgado el navegador al generar el dibujo, te ahorrará bastante tiempo.

De todas formas, yo no sé qué aspecto concreto tiene el circuito. Puedo intuirlo, pero no lo sé. Es más, no es necesario. Sé que es algo parecido a esto: https://tasksofohm.files.wordpress.com/2012/11/742481.png Pero la representación concreta es irrelevante. ¡Para eso utilizamos HDL!
 
Más tarde (son 2am aca en México) sintetizo tu diseño y vemos los resultados que arroja yosys, estuve leyendo el manual y las celdas $memrd y $memwr que obtengo en el diseño de la LUT se mapean a la arquitectura "objetivo", en este caso la ICE40, en los mensajes que arroja yosys durante las optimizaciones me aparecieron celdas de la memoria RAM pero al final no reporta que usó celdas de RAM :/.

A mí también me ha llamado la atención. Pero no he dicho nada por prudencia. Esperemos a ver qué resultados obtienes con los diferentes diseños y después podrás sacar alguna conclusión. Con uno sólo es complicado.

Saludos, y ¡descansa!

Carlos

unread,
Mar 24, 2017, 6:38:45 PM3/24/17
to FPGAwars: explorando el lado libre
Hola,

Pude sintetizar todos los diseños con yosys, con las optimizaciones por defecto, no se si sea la forma correcta de obtener los recursos consumidos. Pongo los 3 diseños, el que hice para el componente en icestudio, el de la lut y el de las compuertas.

componente icestudio
`timescale 1ns / 1ps
`
default_nettype none

module bcd2segments
(
    input
[3:0] bcd,

    output reg
[6:0] segments
);

always
@(*) begin
   
case(bcd)
       
4'h0: segments <= 7'h3F;
       
4'h1: segments <= 7'h06;
       
4'h2: segments <= 7'h5B;
       
4'h3: segments <= 7'h4F;
       
4'h4: segments <= 7'h66;
       
4'h5: segments <= 7'h6D;
       
4'h6: segments <= 7'h7D;
       
4'h7: segments <= 7'h07;
       
4'h8: segments <= 7'h7F;
       
4'h9: segments <= 7'h67;
       
4'hA: segments <= 7'h7A;
       
4'hB: segments <= 7'h7C;
       
4'hC: segments <= 7'h39;
       
4'hD: segments <= 7'h5E;
       
4'hE: segments <= 7'h79;
       
4'hF: segments <= 7'h71;
       
default: segments <= 0;
    endcase
end

endmodule

sintetizamos
$ yosys -p "synth_ice40 -blif componete.blif" componente.v

resultado:

   Number of wires:                  2
   
Number of wire bits:             11
   
Number of public wires:           2
   
Number of public wire bits:      11
   
Number of memories:               0
   
Number of memory bits:            0
   
Number of processes:              0
   
Number of cells:                  7
     SB_LUT4                        
7

Diseño con la LUT:
Mismo comando para sintetizar:
$ yosys -p "synth_ice40 -blif lut.blif" lut.v

Resultado:

   Number of wires:                  2
   
Number of wire bits:             11
   
Number of public wires:           2
   
Number of public wire bits:      11
   
Number of memories:               0
   
Number of memory bits:            0
   
Number of processes:              0
   
Number of cells:                  7
     SB_LUT4                        
7

Implementación con compuertas:
`timescale 1ns / 1ps
`
default_nettype none


sintesis:
$ yosys -p "synth_ice40 -blif gates.blif" gates.v

Resultado:

   Number of wires:                  2
   
Number of wire bits:             11
   
Number of public wires:           2
   
Number of public wire bits:      11
   
Number of memories:               0
   
Number of memory bits:            0
   
Number of processes:              0
   
Number of cells:                  7
     SB_LUT4                        
7

¿Seria correcto asumir que consumen la misma cantidad de recursos? ¿Podríamos  optimizarlo mas?

Saludos







Carlos

unread,
Mar 24, 2017, 6:59:19 PM3/24/17
to FPGAwars: explorando el lado libre
Como siempre hable antes de tiempo y tampoco se si sea correcto asumir que estos son los circuitos finales, adjunto los circuitos que se generan al correr el script con los 3 diferentes diseños:

read_verilog <archivo_fuente>.v
show
-pause
proc
show
-pause
opt
show
-pause

Se generaron (desde mi punto de vista) tres circuitos muy diferentes.

Saludos
icestudio.pdf
lut.pdf
gates.pdf

Carlos

unread,
Mar 26, 2017, 4:30:58 PM3/26/17
to FPGAwars: explorando el lado libre
Que tal,

Entre ayer y hoy pude comprobar el circuito en la FPGA, esta diseñado para manejar displays de cátodo común!!, tenia algunos errores pero ya están corregidos, dejo el código por si le sirve a alguien. La versión hecha con compuertas la optimice con mapas de Karnaugh en una pagina online. Según el mensaje de yosys de recursos consumidos, ambas consumen lo mismo.

arachne reporta la siguiente información:
After packing:
IOs          11 / 96
GBs          0 / 8
    GB_IOs    
0 / 8
LCs          7 / 1280
    DFF        
0
    CARRY      
0
    CARRY
, DFF 0
    DFF PASS  
0
    CARRY PASS
0
BRAMs        0 / 16
WARMBOOTs    0 / 1
PLLs         0 / 1

After placement:
PIOs       7 / 96
PLBs       2 / 160
BRAMs      0 / 16

El código de bcd2segments_lut.v
`timescale 1ns / 1ps
`
default_nettype none

module bcd2segments
(
    input
[3:0] bcd,
    output
[6:0] segments
);

reg
[6:0] LUT[0:15];

initial
begin
   
    LUT
[4'h0] <= 7'h3F;
    LUT
[4'h1] <= 7'h06;
    LUT
[4'h2] <= 7'h5B;
    LUT
[4'h3] <= 7'h4F;
    LUT
[4'h4] <= 7'h66;
    LUT
[4'h5] <= 7'h6D;
    LUT
[4'h6] <= 7'h7D;
    LUT
[4'h7] <= 7'h07;
    LUT
[4'h8] <= 7'h7F;
    LUT
[4'h9] <= 7'h67;

    LUT
[4'hA] <= 7'h77;

    LUT
[4'hB] <= 7'h7C;
    LUT
[4'hC] <= 7'h39;
    LUT
[4'hD] <= 7'h5E;
    LUT
[4'hE] <= 7'h79;
    LUT
[4'hF] <= 7'h71;

end

assign segments
= LUT[bcd];

endmodule

código de bcd2segments_gates.v
`timescale 1ns / 1ps
`
default_nettype none

module bcd2segments_gates (

  input
[3:0] bcd,
  output
[6:0] segments
);

`define A bcd[3]
`
define B bcd[2]
`define C bcd[1]
`
define D bcd[0]

// Kmap
assign segments
[0] = (~`B & ~`D) | (~`A & `C) | (`B & `C) | (`A & ~`D) | (~`A & `B & `D) | (`A & ~`B & ~`C);
assign segments
[1] = (~`A & ~`B) | (~`B & ~`D) | (~`A & ~`C & ~`D) | (~`A & `C & `D) | (`A & ~`C & `D);
assign segments[2] = (~`
A & ~`C) | (~`A & `D) | (~`C & `D) | (~`A & `B) | (`A & ~`B);
assign segments[3] = (~`
A & ~`B & ~`D) | (~`B & `C & `D) | (`B & ~`C & `D) | (`B & `C & ~`D) | (`A & ~`C & ~`D);
assign segments
[4] = (~`B & ~`D) | (`C & ~ `D) | (`A & `C) | (`A & `B);
assign segments
[5] = (~`C & ~`D) | (`B & ~`D) | (`A & ~`B) | (`A & `C) | (~`A & `B & ~`C);
assign segments[6] = (~`
A & `B & ~`C) | (`A & ~`B) | (`C & ~`D) | (`A & `D) | (~`B & `C);

endmodule


Saludos



1138-4EB

unread,
Mar 26, 2017, 5:10:39 PM3/26/17
to FPGAwars: explorando el lado libre
Gracias por las pruebas Carlos. Me alegra ver que te animaste a hacer una versión alternativa basada en puertas lógicas ;).

La verdad es que es llamativo lo diferentes que son los diagramas y que el resultado final sea el mismo. Incluso que las dos veces que has ejecutado la versión icestudio (la de los multiplexores) el resultado sea diferente. Supongo que habrá alguna etapa intermedia de optimización de la que no somos conscientes.

Creo que había alguna herramienta online que visualizaba el circuito sobre un dibujo de la FPGA. Seguramente se verá lo mismo. Pero por probar...

Un saludo y felicidades

1138-4EB

unread,
Mar 26, 2017, 6:12:55 PM3/26/17
to FPGAwars: explorando el lado libre

Creo que había alguna herramienta online que visualizaba el circuito sobre un dibujo de la FPGA. Seguramente se verá lo mismo. Pero por probar...

Carlos

unread,
Mar 26, 2017, 11:10:43 PM3/26/17
to FPGAwars: explorando el lado libre


Hola,

A mi también me llamo la atención que con 3 implementaciones tan diferentes se llegara aparentemente al mismo resultado, este experimento lo inicie para tratar de mejorar el decoder que hice para icestudio unos meses atras, pensando que lo había implementado muy mal. En lo personal se me hizo mas fácil la implementación de la LUT, pero con el diseño con compuertas lógicas tuve que volver a leer temas que vi hace unos semestres en la u.

Al parecer yosys tiene varias opciones para optimizar los diseños, al llamar synth_ice40 se llaman otros comandos, entre ellos se llaman algunos para realizar las optimizaciones por default.

Genere el asc del diseño con LUTs (adjunto) y lo cargue en la página que mandaste, la imagen de abajo es la parte "interesante" del resultado, los bloques rosas son bloque lógicos, lo demás no lo se interpretar aun xD


Gracias por la paciencia y ayuda, si mis profesores en la u hubieran sido así no me disgustaría tanto la "escuela".

Saludos
bcd2segments_lut.asc

Jesús Arroyo

unread,
Mar 27, 2017, 3:51:15 AM3/27/17
to FPGAwars: explorando el lado libre
Buenas Carlos,

Diseños muy distintos pero con la misma semántica suelen generar circuitos equivalentes gracias a las optimizaciones en yosys (ABC) y al rutado. Puedes crear módulos verilog de muchas maneras que al final sólamente enciendan un LED. Es cierto que para circuitos complejos también puede suceder lo contrario ya que arachne-pnr tiene una componente aleatoria para el rutado y el mismo diseño puede generar rutas distintas.

El ice40_viewer te permite visualizar el circuito real que se va a sintetizar en la FPGA. Los bloques morados son los bloques lógicos o PLBs. Los bloques azules son los pines de la FPGA, PIOs, y los amarillos los bloques de memoria BRAMs. Cuando aparecen con un color claro significa que están siendo utilizados y al hacer zoom se muestra el esquema de las conexiones. Realmente es una herramienta muy útil para verificar partes del diseño o para visualizar los recursos utilizados.

Un saludo.

1138-4EB

unread,
Mar 27, 2017, 7:06:36 AM3/27/17
to FPGAwars: explorando el lado libre
Hola Jesús,

¿Sabes si hay alguna opción para generar un diagrama graphviz como los que puso Carlos anteriormente, pero de la versión optimizada? Es decir, un equivalene al ice40_viewer, pero anterior el place & route.

Un saludo

Jesús Arroyo

unread,
Mar 27, 2017, 7:43:09 AM3/27/17
to FPGAwars: explorando el lado libre
Hola,

Yosys permite visualizar el diseño en cualquier momento con el comando "show <modulename>".

Puedes visualizar directamente un fichero verilog, por ejemplo: yosys -p "show" bcd2segments.v

También puedes visualizar el blif que es el input de arachne-pnr (junto con el PCF) para generar el rutado: yosys -p "synth_ice40; show" bcd2segments.v

Un saludo.

1138-4EB

unread,
Mar 27, 2017, 7:43:47 AM3/27/17
to FPGAwars: explorando el lado libre
Muchas gracias.

Carlos

unread,
Mar 27, 2017, 2:57:16 PM3/27/17
to FPGAwars: explorando el lado libre
Hola,
Clifford tiene un AppNote sobre la visualización de los circuitos, la puedes descargar/revisar aqui: yosys AppNote 011 Design investigation

Saludos

1138-4EB

unread,
Apr 1, 2017, 10:35:01 PM4/1/17
to FPGAwars: explorando el lado libre
Muchas gracias, Carlos. Hasta ahora no lo había mirado, porque todavía no había instalado yosys debidamente. Me ha resultado muy útil.

Carlos

unread,
Apr 1, 2017, 11:15:42 PM4/1/17
to FPGAwars: explorando el lado libre
No hay de que :).
Los ultimos dias estuve investigando si es posible obtener la lista de cells utilizadas al final del diseño, creo recordar que se utilizó una SBLUT, quisiera saber el contenido de esa LUT, pero no he encontrado nada parecido.

Saludos

1138-4EB

unread,
Apr 2, 2017, 2:08:37 AM4/2/17
to FPGAwars: explorando el lado libre
Hola Carlos,

Si estás utilizando un script, prueba a ejecutar cada uno de los pasos a mano: https://github.com/cliffordwolf/yosys#getting-started

Asi te va diciendo qué optimizaciones va haciendo, qué va quitando y qué va poniendo. El contenido de una LUT lo tienes en el bitstream y en el viewer. Pero para ello necesitas saber el número de la LUT que te interesa. No es imposible, pero es tarea ardua.

Saludos
Reply all
Reply to author
Forward
0 new messages