¿Por qué se está optimizando este módulo Verilog del generador de reinicio de encendido?

Estoy tratando de escribir un módulo Verilog que genere una señal de reinicio de encendido durante algunos ciclos de reloj. Estoy sintetizando usando Lattice iCEcube2 + Synplify Pro apuntando a un iCE40 HX1K en el Nandland Go Board .

Si escribo el módulo de esta manera, se está optimizando:

`default_nettype none

module reset_generator #(
  parameter COUNT_WIDTH = 2
) (
  input wire    i_clk,
  output wire   o_rst
);

  reg [COUNT_WIDTH:0]   rst_count;
  assign o_rst = !rst_count[COUNT_WIDTH];

  always @(posedge i_clk) begin
    if (o_rst == 1) begin
      rst_count <= rst_count + 1;
    end
  end
endmodule

iCEcube2 emite esta advertencia:

...
@N:CL189 : reset_generator.v(13) | Register bit rst_count[4] is always 1.
@N:CL159 : reset_generator.v(6) | Input i_clk is unused.
...
@N:BN115 : ball_absolute_mv_vga_top.v(19) | Removing instance reset_gen (in view: work.ball_absolute_mv_vga_top(verilog)) of type view:work.reset_generator_4s(verilog) because it does not drive other instances.
...

Y puedo verificar que la o_rstseñal nunca se establece en 1. Pensé que todos los registros se inicializaron en 0, por lo que no estoy seguro de por qué piensa rst_count[4] is always 1.

Sin embargo, si configuro explícitamente rst_counta 0, así:

  reg [COUNT_WIDTH:0]   rst_count = 0;

Ahora ya no se optimiza y funciona como se esperaba, pero iCEcube2 muestra esta advertencia:

@W:FX1039 : reset_generator.v(13) | User-specified initial value defined for instance reset_gen.rst_count[4:0] is being ignored. 

Esta advertencia también implica que los valores iniciales son 0. Entonces, ¿por qué se optimiza la primera versión? ¿Y por qué agregar un valor inicial que se ignora cambia el comportamiento? ¿Es esto solo una peculiaridad de iCEcube2 y/o Synplify Pro?

EDIT 1 : estoy usando un reinicio de encendido porque Lattice solo puede inicializar registros a cero, y me gustaría que algunos comiencen con valores distintos de cero.

El o_rstde este reset_generatorcomponente está conectado a otro componente en un nivel superior como este:

  wire w_reset;
  reset_generator #(
    .COUNT_WIDTH(4)
  ) reset_gen (
    .i_clk(i_clk),
    .o_rst(w_reset)
  );

  ball_absolute ball_absolute (
    .clk(i_clk),
    .reset(w_reset),
    .vsync(o_vga_vsync),
    // ...
  );

La razón por la que hace esto es porque ball_absolutetiene un par de registros X/Y para la posición de la pelota en la pantalla. Me gustaría que la pelota comenzara en el medio de la pantalla, no en (0, 0). Y debido a que Lattice solo puede inicializar registros en 0, quiero usar una señal de reinicio de encendido para establecer X/Y en valores distintos de cero.

El proyecto completo está en GitHub .

EDICIÓN 2 : También entiendo que una señal de bloqueo de PLL podría usarse como un reinicio de encendido, pero desafortunadamente el HX1K no tiene ningún PLL. El Go Board utiliza un cristal externo para el reloj.

Su enfoque de diseño no es bueno. Por lo general, tendrá una entrada de reinicio externa para reiniciar/inicializar los registros internos de la lógica del generador de reserva. La lógica puede generar un restablecimiento síncrono de varios ciclos para restablecer diferentes módulos en el sistema.
¿Cómo se restablece rst_count?
Puede consultar stackoverflow.com/a/38032139/9609830 ; hay mucha información que podría resolver su problema.
@user253751: No lo hace. Comienza a las at 0en el encendido y cuenta hacia adelante. Una vez o_rstque baja, rst_countpermanece en el mismo valor distinto de cero para siempre.
@ChristianB.: Ese enlace confirma lo que estoy viendo. Dice que Lattice ignora el valor de inicialización e inicializa todos los FF a cero. Sin embargo, no explica por qué Lattice optimiza el módulo cuando falta el valor de inicialización. Además, HX1K no tiene un PLL, por lo que no es posible usarlo para reiniciar la unidad.
@DaveDribin, ¿qué hace que comience en cero al encender? Necesita un circuito de reinicio para saber en qué comenzará.
@ThePhoton: todos los flip flops se inicializan a cero al encender. ¿Tal vez esto es una peculiaridad de las herramientas y FPGA de Lattice? No sé cómo funcionan otros FPGA y herramientas.
En ese caso, ¿por qué necesita implementar un reinicio de encendido en la estructura FPGA?
Además, ¿puede compartir su código de nivel superior? Sin esto, no podemos responder por qué recibiste el mensaje "... no maneja otras instancias", y tampoco podemos ver por qué un contador declarado de manera predeterminada con 3 bits tendría un 5.° bit ( rst_count[4])
@ThePhoton: actualizó la pregunta con el "por qué" y un fragmento del nivel superior.

Respuestas (1)

Esta es probablemente una peculiaridad en synplify. Synplify está diseñado para sintetizar diseños para ASIC, no para FPGA y, como resultado, es bastante obstinado. Para los ASIC, es común no inicializar nada nunca y, en su lugar, utilizar reinicios explícitos. Y como resultado, synplify maneja los bloques iniciales y los inicializadores en línea de manera diferente a las herramientas FPGA; específicamente, en su mayoría los ignora, incluso si causa un comportamiento extraño e inesperado. De todos modos, todas las señales que no se inicializan explícitamente no comienzan en 0, comienzan en X (nivel lógico indeterminado). Y luego las herramientas pueden elegir el valor que quieran durante la síntesis si puede simplificar la lógica. Entonces, parece que eso es lo que synplify está haciendo aquí... Se da cuenta de que puede convertir esa X en particular en un 1 y, como resultado, eliminar un montón de lógica. Si inicializa ese registro a 0, entonces synplify no puede hacer esta optimización, pero le advierte que el inicializador se ignora porque synplify no entiende cómo inicializar los registros, ya que no puede hacerlo en un ASIC. Lo que debe hacer es mantener el init en línea a cero, pero también agregar una entrada de reinicio externa que también puede reiniciar ese registro.

Gracias muy útil! Creo que esto explica lo que está pasando. Pero, ¿por qué recomienda una entrada de reinicio externa? Estoy usando el reinicio de encendido para inicializar algunos registros a valores distintos de cero, ya que Lattice no puede hacer eso. No creo que necesite reiniciar después de encender?
Además, ¿qué quieres decir con reinicio externo? ¿Como un botón físico o algo así? El Go Board tiene 4 botones, pero no quiero usar uno como botón de "reinicio", si no es necesario. Podría hacer alguna combinación de botones, pero ¿eso parece una complicación innecesaria?
Bueno, supongo que depende de cuál sea la intención. Por lo general, dedico un botón para reiniciar, pero en muchos tableros hay un botón "cpu_rst" dedicado para ese propósito. Pero también, casi siempre hay un PLL involucrado, por lo que el restablecimiento interno se derivará de la salida bloqueada del PLL, y el restablecimiento del PLL provendrá de la entrada externa. Pero si no tiene un PLL y no necesita una entrada de reinicio externa, supongo que no es estrictamente necesario.
Desafortunadamente, el HX1K no tiene ningún PLL. Utiliza un reloj externo como cristal. La intención es inicializar algunos registros a valores distintos de cero, ya que iCEcube no lo permite. Creo que lo correcto aquí es configurar rst_count = 0e ignorar la advertencia.
Sí, me parece razonable, si no quieres dedicar un botón para reiniciar.