XST Verilog - Umwandeln von reellen in ganzzahlige Konstanten

Wenn ich versuche, den folgenden Verilog-Code mit Xilinx XST zu synthetisieren, erhalte ich die Fehlermeldung „Nicht unterstützte reale Konstante“. Wenn ich versuche, diesen Ausdruck in eine $rtoi-Funktion einzubinden, gibt XST einen anderen Fehler aus: „Unsupported System Function Call“.

Ist es mit den Xilinx-Synthesewerkzeugen möglich, eine reelle Konstante in eine ganzzahlige Konstante umzuwandeln? Wenn das so ist, wie?

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

Antworten (2)

Da keine anderen Antworten vorliegen, schlage ich einen alternativen Ansatz vor.

Verwenden Sie anstelle von zwei Parametern CLOCK_HZ und BAUD_RATE einfach einen einzigen Parameter DIVIDE_RATIO.

Dann können die Vergleichswerte als DIVIDE_RATIO[n:1], DIVIDE_RATIO, (3*DIVIDE_RATIO)[n:1] usw. berechnet werden, und es wird niemals ein Fließkommawert erstellt.

Der Nachteil davon im Vergleich zu dem, was Sie haben, besteht darin, dass Ihr Ansatz, wenn das Teilungsverhältnis keine exakte Ganzzahl ist, die Fehler in der Tickrate über 10 Zyklen ausgleichen würde, während meiner im Vergleich zu etwas mehr Fehler in der Tickrate aufweisen würde das "ideale" Teilungsverhältnis.

Obwohl es nicht das ist, wonach Sie gefragt haben, würde ich außerdem vorschlagen, nach alternativen Möglichkeiten zur Anordnung Ihrer Theke zu suchen. Wie Ihr Code aussieht, verwenden Sie ein 32-Bit-Register, um einen Zähler zu halten (und 32-Bit-Vergleiche durchzuführen), der niemals über 600 zählt, vorausgesetzt, die Standardwerte, die Sie für die Parameter verwendet haben, werden nicht vom Aufrufer überschrieben. Ich denke, Sie könnten alle die gleichen Zustände mit 9 oder 10 Zustandsbits erhalten.

Bearbeiten - ein alternativer Ansatz:

Ein weiterer Weg, der sowohl das Fließkommaproblem als auch das Genauigkeitsproblem löst, ist die Verwendung eines Sprungzählers. (Freihandcode, nicht getestet):

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

Indem Sie den Zähler einfach überrollen lassen, anstatt ihn nach der Endzählung auf 0 zurückzusetzen, erhalten Sie eine Tickrate, die den Durchschnitt korrekt bis zu den Grenzen der 32-Bit-Integer-Mathematik ergibt, aber Sie haben keine Gleitkommazahlen, um den Verilog zu verwirren Compiler oder Synthesizer.

Sie müssen die Reset-Logik und einen zweiten Zähler hinzufügen, um Ticks zu zählen und das "Fertig"-Signal zu erzeugen. Um genau das zu bekommen, was Sie vorher hatten, brauchen Sie auch

wire real_tick;
assign real_tick = tick & ~done;

Wenn der Tick-Ausgang störungsfrei sein muss, müssen Sie dies in sequentieller Logik tun.

Ja, ich habe darüber nachgedacht, es so zu machen, und vielleicht werde ich das tun, sobald ich mein Projekt zum Laufen gebracht habe, aber im Moment möchte ich den Fehler niedrig halten, während ich experimentiere. Ich habe es irgendwie umgangen, indem ich "counter == 1*CLOCK_HZ/(2*BAUD_RATE)" anstelle von "counter == 0.5*CLOCK_HZ/BAUD_RATE" getestet habe, aber was ich wirklich tun wollte, war die Bodenteilung. Was die 32-Bit-Ganzzahl betrifft, berechnet mein tatsächlicher Code einen lokalen Parameter COUNTER_WIDTH=clog2(2+(TOTAL_BITS+0.5)*CLOCK_HZ/BAUD_RATE), wobei clog2(x) eine böse Inline-Funktion ist, die ceil(log2(x )), aber das habe ich der Einfachheit halber außer Acht gelassen. :)

Das Tool versucht Ihnen höflich zu sagen: "Hey, Alter, ich bin kein C-Compiler." :)

Zusätzlich zur Auswahl einer geeigneten Festkommadarstellung für die Zahlen, mit denen Sie arbeiten, sollten Sie keine Zähler in Form von Additionen und Vergleichen mit einer Endzahl synthetisieren. Starten Sie stattdessen den Zähler beim Endwert und testen Sie ihn gegen Null. Dies spart einen Addierer und verkürzt den Logikpfad.

Wie würde ich also diese Festkommadarstellung auswählen? In diesem Beispiel möchte ich eine 32,0-Darstellung. Außerdem ist es eine großartige Optimierung, den Zähler beim Endwert zu starten und dann gegen Null zu testen, aber ich finde immer noch heraus, was ich tue, daher ist mir die Lesbarkeit des Codes an dieser Stelle viel wichtiger als die Schonung von FPGA-Ressourcen.