XST Verilog: conversión de constantes reales a enteras

Cuando trato de sintetizar el siguiente código Verilog usando Xilinx XST, aparece el error "Constante real no admitida". Si trato de envolver esa expresión en una función $rtoi, XST da un error diferente: "Llamada de función del sistema no admitida".

Usando las herramientas de síntesis de Xilinx, ¿es posible convertir una constante real en una constante entera? ¿Si es así, cómo?

module example(clk, n_rst, tick, done);
  parameter CLOCK_HZ  = 50_000_000;
  parameter BAUD_RATE = 3_000_000;
  input clk, n_rst;
  output reg tick, done;

  reg [31:0] counter;

  always @(posedge clk, negedge n_rst) begin
    if (!n_rst) begin
      counter <= 32'h00000000;
      tick <= 0;
      done <= 0;
    end
    else if (counter == (0.5*CLOCK_HZ/BAUD_RATE) ||    // ERROR:Xst:850 - Unsupported real constant
             counter == (1.5*CLOCK_HZ/BAUD_RATE) ||    // ERROR:Xst:850 - Unsupported real constant
             counter == (2.5*CLOCK_HZ/BAUD_RATE) ||    // ERROR:Xst:850 - Unsupported real constant
             counter == (3.5*CLOCK_HZ/BAUD_RATE) ||    // ERROR:Xst:850 - Unsupported real constant
             counter == (4.5*CLOCK_HZ/BAUD_RATE) ||    // ERROR:Xst:850 - Unsupported real constant
             counter == (5.5*CLOCK_HZ/BAUD_RATE) ||    // ERROR:Xst:850 - Unsupported real constant
             counter == (6.5*CLOCK_HZ/BAUD_RATE) ||    // ERROR:Xst:850 - Unsupported real constant
             counter == (7.5*CLOCK_HZ/BAUD_RATE) ||    // ERROR:Xst:850 - Unsupported real constant
             counter == (8.5*CLOCK_HZ/BAUD_RATE) ||    // ERROR:Xst:850 - Unsupported real constant
             counter == (9.5*CLOCK_HZ/BAUD_RATE))      // ERROR:Xst:850 - Unsupported real constant
    begin
      counter <= counter + 1;
      tick <= 1;
      done <= 0;
    end
    else if (counter == 10*CLOCK_HZ/BAUD_RATE) begin
      counter <= 32'h00000000;
      tick <= 0;
      done <= 1;
    end
    else begin
      counter <= counter + 1;
      tick <= 0;
      done <= 0;
    end
  end
endmodule

Respuestas (2)

Dado que no hay otras respuestas próximas, sugeriré un enfoque alternativo.

En lugar de dos parámetros CLOCK_HZ y BAUD_RATE, utilice un solo parámetro DIVIDE_RATIO.

Luego, los valores para la comparación se pueden calcular como DIVIDE_RATIO[n:1], DIVIDE_RATIO, (3*DIVIDE_RATIO)[n:1], etc. y nunca se crea ningún valor de coma flotante.

La desventaja de esto en relación con lo que tiene es que si la relación de división no es un número entero exacto, su enfoque suavizaría los errores en la tasa de ticks durante 10 ciclos, mientras que el mío tendría un poco más de error en la tasa de ticks en comparación con la relación de división "ideal".

Además, aunque no es lo que preguntaste, te sugiero que busques formas alternativas de organizar tu mostrador por completo. Tal como está su código, está utilizando un registro de 32 bits para mantener (y haciendo comparaciones de 32 bits) un contador que nunca contará por encima de 600, suponiendo que los valores predeterminados que utilizó para los parámetros no sean anulados por la persona que llama. Creo que podría obtener todos los mismos estados con 9 o 10 bits de estado.

Editar - un enfoque alternativo:

Otra forma de hacerlo que resuelve tanto el problema del punto flotante como el problema de la precisión es usar un contador de saltos. (código a mano alzada, no probado):

parameter jump = xxx; /// jump = 2^32 * BAUD_RATE / CLOCK_HZ
reg [32:0] ctr;

always @ (posedge clk) begin
   ctr <= ctr[31:0] + jump; 
   tick <= ctr[32];
end

Al permitir que el contador simplemente se dé la vuelta en lugar de restablecerse a 0 después del conteo de la terminal, obtiene una tasa de tic que promedia correctamente hasta los límites de las matemáticas enteras de 32 bits, pero no tiene ningún punto flotante para confundir el Verilog compilador o sintetizador.

Deberá agregar la lógica de reinicio y un segundo contador para contar los ticks y generar la señal de "hecho". Para obtener exactamente lo que tenía antes, también necesitará

wire real_tick;
assign real_tick = tick & ~done;

Si la salida de tick tiene que estar libre de fallas, tendría que hacerlo en lógica secuencial.

Sí, pensé en hacerlo de esa manera, y podría terminar haciéndolo una vez que mi proyecto funcione, pero por ahora quiero mantener el error bajo mientras experimento. Lo solucioné probando "contador == 1*CLOCK_HZ/(2*BAUD_RATE)" en lugar de "contador == 0.5*CLOCK_HZ/BAUD_RATE", pero lo que realmente quería hacer era dividir el piso. En cuanto al entero de 32 bits, mi código actual calcula un parámetro local COUNTER_WIDTH=clog2(2+(TOTAL_BITS+0.5)*CLOCK_HZ/BAUD_RATE), siendo clog2(x) una función en línea desagradable que calcula ceil(log2(x )), pero lo dejé fuera de mi pregunta por simplicidad. :)

La herramienta está tratando de decirte, cortésmente: "Oye, amigo, no soy un compilador de C". :)

Además de elegir una representación de punto fijo adecuada para los números con los que está trabajando, no debe sintetizar contadores en términos de adiciones y comparaciones con un recuento terminal. En su lugar, inicie el contador en el valor terminal y pruébelo contra cero. Esto ahorrará un sumador y acortará la ruta lógica.

Entonces, ¿cómo elegiría esa representación de punto fijo? En este ejemplo, me gustaría una representación de 32,0. Además, comenzar el contador en el valor terminal y luego probarlo contra cero es una gran optimización, pero todavía estoy averiguando lo que estoy haciendo, por lo que la legibilidad del código es mucho más importante para mí que conservar los recursos de FPGA en este punto.