Ich habe eine Frage zu etwas, das ich nicht verstehe und das in meinem FPGA-Projekt vor sich geht. Ich muss zwei Geräte (AGC und ADC) über einen SPI-Bus steuern. Da das FPGA das Master-Gerät sein wird, erzeuge ich ein Taktsignal, SCK , im Code, indem ich den Systemtakt teile. Ich leite dieses Signal dann über einen Tristate-Puffer an eine Ausgangsleitung. Unten ist mein relevanter Code. Es ist nicht gezeigt, aber das Signal, das den Tristate-Puffer steuert, en_SCK , das von einem FSM gesteuert wird, wenn es im Ruhezustand niedrig und dann für die restlichen Zustände hoch gesetzt wird.
output wire SDI
//for SCK_clock
reg SCK_gen, SCK_hold;
integer i;
reg en_SCK;
wire neg_edge_SCK;
//SCK_generator
always @(posedge clk)
begin
i <= i+1;
SCK_hold <= SCK_gen;
if(i == 10)
begin
SCK_gen <= ~SCK_gen;
i <= 0;
end
end
assign SCK = (en_SCK) ? SCK_gen : 1'bz;
Wenn ich das Design implementiere, erhalte ich die folgende Warnung:
WARNUNG:PhysDesignRules:372 - Gated Clock. Das Taktnetz en_SCK_not0001 wird von einem kombinatorischen Pin bezogen. Dies ist keine gute Designpraxis. Verwenden Sie den CE-Pin, um das Laden von Daten in das Flip-Flop zu steuern.
Außerdem bemerke ich, dass meine Uhr sehr verzerrt erscheint. Aber wenn ich das Tristate-Gerät nicht in meinem Code verwende und das Taktsignal direkt der Ausgangsleitung zuweise (wie im folgenden Code), bekomme ich ein schönes sauberes Taktsignal.
assign SCK = SCK_gen;
Unten ist eine Seite an Seite des Signals SCK ohne den Tristate-Puffer (links) und mit dem Tristate-Puffer (rechts). Ich bin ziemlich neu in FPGA und Verilog, aber ich verstehe, dass die Verwendung dieses Stils von Zuweisungscode einen Tristate-Puffer impliziert, daher bin ich verwirrt, warum er als getaktete Taktquelle interpretiert zu werden scheint (der von XST generierte Schaltplan zeigt ihn implementiert mit einem und Gate.Ich bin auch verwirrt darüber, wie es das Taktsignal verzerrt.Der FSM sollte den en_SCK erzwingenAktivieren Sie das Signal hoch für ein Vielfaches der Periode der Uhr, daher bin ich mir nicht sicher, was passiert. Laut Handbuch des Demoboards teilen sich andere Geräte dieses Signal, sodass ich es auf hohe Impedanz einstellen muss, wenn es nicht verwendet wird. Wenn mich jemand in die richtige Richtung weisen oder es mir erklären könnte, wäre ich sehr großartig. Danke
Hier ist ein Link zu meiner ursprünglichen Frage bei Stackoverflow.com
Hier ist das gesamte Modul, wenn das die Dinge klarer macht. Ich habe bisher nur ein paar einfache Verilog-Projekte durchgeführt, daher bin ich mir sicher, dass mein Code nicht großartig ist. Aber ich habe versucht, es mit einem FSM zu entwerfen. Vielleicht ist mein Problem nicht das, was ich dachte. Danke für deine Geduld.
module AGC_controller
(
input wire clk, reset, set_AGC, AMP_DO,
output wire SDI, SCK,
output reg inhibit_ADC, AMP_CS,
//spi disable signals ** all disabled for =1 except AD_CONV. When AD_CONV=0, disabled.
output wire DAC_CS, AD_CONV, SF_CEO, FPGA_INIT_B,
output reg [7:0] led
);
localparam [2:0]
idle = 3'b000,
set_up = 3'b001,
start_data = 3'b010,
wait_for_neg= 3'b011,
wait_4 = 3'b100,
next_bit = 3'b101;
// wait_last = 3'b110;
//signals
//for SCK_clock
reg SCK_gen, SCK_hold;
integer i;
reg en_SCK;
wire neg_edge_SCK;
initial
begin
SCK_gen = 0;
i=0;
end
//SCK_generator
always @(posedge clk)
begin
i <= i+1;
SCK_hold <= SCK_gen;
if(i == 10)
begin
SCK_gen <= ~SCK_gen;
i <= 0;
end
end
//detect neg edge of SCK
assign neg_edge_SCK = SCK_hold & ~SCK_gen;
//general signals
reg [2:0] state_reg, state_next;
integer bit_position;
reg [7:0] AGC_buf;
reg en_SDI;
//FSM control
always @(posedge clk, posedge reset)
begin
if (reset)
state_reg <= idle;
else
state_reg <= state_next;
end
//FSM next state logic
always @*
begin
state_next = state_reg;
case(state_reg)
idle:
begin
bit_position=7;
AGC_buf = 8'b10011001;
en_SCK = 1'b0;
en_SDI = 1'b0;
AMP_CS = 1'b1;
inhibit_ADC = 1'b0;
if(set_AGC)
begin
state_next = set_up;
end
end
set_up:
begin
AMP_CS = 1'b0;
inhibit_ADC = 1'b1;
if ((SCK_gen) && (i < 9))
state_next = start_data;
end
start_data:
begin
en_SDI = 1'b1;
if ((SCK_gen == 0) && (i==2))
begin
state_next = wait_for_neg;
en_SCK = 1'b1;
end
end
wait_for_neg:
begin
if (neg_edge_SCK)
begin
state_next = wait_4;
end
end
wait_4:
begin
if (i==4)
begin
//test code to light leds
led[bit_position] = SDI;
if (bit_position == 0)
begin
state_next = idle;
end
else
begin
state_next = next_bit;
end
end
end
next_bit:
begin
bit_position = bit_position - 1;
state_next = wait_for_neg;
end
endcase
end
assign SDI = (en_SDI) ? AGC_buf[bit_position] : 1'bz;
assign SCK = (en_SCK) ? SCK_gen : 1'bz;
//spi disable signals ** all disabled for =1 except AD_CONV. When AD_CONV=0, disabled.
assign DAC_CS = (state_reg != idle);
assign AD_CONV = (state_reg == idle);
assign SF_CEO = (state_reg != idle);
assign FPGA_INIT_B = (state_reg != idle);
endmodule
Die Warnung schlägt im Grunde vor, dass Sie ein explizites Uhr-Primitiv verwenden, das aus irgendeinem Grund nicht automatisch von den Tools abgeleitet wird.
Es sollte eine Referenz für Ihr spezielles FPGA geben, die angibt, welche Grundelemente verfügbar sind. Für einen Spartan3E beispielsweise beschreibt Xilinx UG617 die BUFGCE-Primitive ("Global Clock Buffer with Clock Enable") und zeigt, wie man sie instanziiert. Andere Anbieter verfügen über eine ähnliche Dokumentation.
Ihre Taktverzerrung tritt wahrscheinlich auf, weil das Tristateing einer Ausgangsleitung nicht sofort möglich ist. Was auch immer angesteuert wird, hat sowohl Induktivität als auch Kapazität. Selbst wenn der Treiber in Null-Echtzeit auf High-Z gehen könnte (was er nicht kann), klingelt das angesteuerte Netzwerk noch einige Zeit. Wenn das ein Problem ist, müssten Sie einen Weg finden, um sicherzustellen, dass die Taktleitung niedrig ist, und das schon seit einiger Zeit, bevor Sie sie tristaten. Alles in allem gibt es wahrscheinlich einen besseren Weg, um Ihr Ziel zu erreichen, als eine Uhr zu tristatieren.
Ich stimme user572 bei der Antwort auf Ihre erste Frage zu, was die Warnung verursacht. Versuchen Sie explizit, einen Taktpuffer an Ihrer clk-Eingabe zu instanziieren, und teilen Sie uns mit, ob die Warnung dadurch gelöscht wird.
Ich denke jedoch, dass das seltsame Ausgabeverhalten völlig unabhängig von der Warnung ist. Ich kann die Ursache des Problems nicht erkennen, teilweise weil Ihr FSM-Codierungsstil nicht sehr klar ist. Ich bin mir nicht sicher, ob Ihr Synthesetool mit Ihrem FSM-Code das Richtige macht oder nicht, was eine Ursache für Probleme sein könnte.
Für Ihren FSM-Code empfehle ich folgende Änderungen:
Verwenden Sie <=
statt =
. Der Unterschied ändert möglicherweise nicht die logische Bedeutung des Codes, kann jedoch zu Unterschieden zwischen simuliertem Verhalten und synthetisiertem Verhalten führen, die schwer zu debuggen sind.
Jeder Ausgang der Zustandsmaschine sollte in jedem Zweig der case
, und für jede Kombination von Eingängen zugewiesen werden. Sie verlassen sich darauf, dass Werte konstant gehalten werden, wenn Sie sie nicht zuweisen, und das sollte logischerweise passieren. In der Synthese könnte dies jedoch dazu führen, dass Latches anstelle von Flip-Flops instanziiert werden, und es handelt sich nicht um einen Best-Practice-Codierungsstil.
Ja, der Stil, den ich empfehle, ist sehr ausführlich und mühsam zu schreiben. Ausführlicher, langwieriger Code ist eine Tatsache des Lebens in Verilog. Sei einfach froh, dass du kein VHDL schreibst.
Insbesondere dort, wo Sie state_next = state_reg;
vor der Case-Anweisung stehen, bin ich mir nicht sicher, was das resultierende Verhalten sein wird. Ich denke, Sie meinen dies, um eine Standardausgabe für zu erstellen state_reg
, wenn Sie nicht state_reg
innerhalb der case-Anweisung zuweisen. state_next <= state_next
Es ist besser , in jeden Zweig des Falls einzufügen , wo Sie nicht möchten, dass es sich ändert.
Ich denke, aber Sie müssen experimentieren, um sicher herauszufinden, dass die Kombination aus dem blockierenden Zuweisungsstil und der @*
Sensibilität des Always-Blocks, der die case-Anweisung umgibt, erklären könnte, warum Sie das fehlerhafte Ausgabeverhalten sehen. Sie haben im Grunde eine zirkuläre Logik. In einem Block weisen Sie zu state_reg <= state_next
und in einem anderen (kombinatorischen) Block weisen Sie zu state_next = state_reg
.
Logischerweise bedeutet Ihr Code, dass die state_next = state_reg
Zuweisung immer dann erfolgt, wenn sich an einem der Eingänge des Blocks mit dem Fall etwas ändert. Dies könnte bei state_reg
Änderungen zu einer Race-Bedingung führen oder wenn sich ein anderer Eingang gleichzeitig mit einer clk
Flanke ändert, die eine Aktualisierung von auslöst state_reg
. Und es ist überhaupt nicht klar, wie dies synthetisiert wird.
state_next = state_reg
im always @*
Block scheint mir falsch zu sein ... Verwendet Ihr Lehrbuch wirklich diese Art von Stil?
Russell McMahon
Frank Dejay
Kohlschmied
Das Photon
Das Photon
Frank Dejay