Ich bin ziemlich neu bei Verilog und im Allgemeinen Digital Design. Ich arbeite an einem Projekt, das eine Zustandsmaschine hat. Das Modul empfängt in einem bestimmten Zustand ein Leseanforderungspaket von einem anderen Modul, und ich muss die erforderliche Leseantwortdatengröße decodieren und so viele Datenbytes in der Nutzlast zurücksenden. Jetzt muss ich dies alle n Taktzyklen tun.
Ich finde es schwierig zu verstehen, wo meine Zählung aktualisiert werden soll und wo ihr der nächste Zählwert zugewiesen werden soll? Hier ist die allgemeine Struktur meines Codes.
always@(posedge clk_i or negedge resetn_i) begin
if (resetn_i == 1'b0) begin
reg_read_en <= 1'b0;
reg_write_en <= 1'b0;
count <= 3'b111;
next_count <= 3'b111;
state <= S_INITIAL;
end else begin
state <= next_state;
count <= next_count;
end
Dies ist der sequentielle Block. Jetzt in meinem Kombinationsblock
always@(*) begin
next_state = state;
next_count = count;
.......
........
M_PASSIVE:begin
if(count == 3'b000)
begin
//Update some registers with new data values
end else begin
next_count = count - 3'b001;
end
Ich kann nicht verstehen, ob dies auf die erforderliche Anzahl von Zyklen wartet und dann die Reg-Werte aktualisiert, oder mir fehlt hier etwas.
Ich würde mich sehr über etwas Klarheit darüber freuen.
PS Verzeihen Sie mir, wenn ich einige grundlegende Aspekte des Verhaltensdesigns in Bezug auf den Unterschied zwischen sequentiellen und kombinatorischen Blöcken vermisse.
Hier ist ein vollständiger Code dessen, was ich versuche zu tun. Im Wesentlichen möchte ich ein Paket lesen, Pakete lesen, 7 oder 8 Taktzyklen oder N clk-Zyklen warten und dann eine Leseantwort mit den Daten in den Registern senden, die den Ausgangsports kontinuierlich zugewiesen sind.
always@(posedge clk_i or negedge resetn_i) begin
if (resetn_i == 1'b0) begin
reg_read_en <= 1'b0;
reg_write_en <= 1'b0;
count <= 3'b111;
state <= S_INITIAL;
end else begin
state <= next_state;
count <= next_count;
end
end
always@(*) begin
next_state = state;
next_count = count;
case(state)
S_INITIAL: begin //not enabled
if(enable_i == 1'b1) begin
next_state = S_ENABLED;
end
reg_read_en <= 1'b0;
reg_write_en <= 1'b0;
end
S_ENABLED: begin
if(enable_i == 1'b0) begin
next_state = S_INITIAL;
end
case(mode_i)
M_PASSIVE:begin
reg_read_en = dnoc_packet_ready;
//pkt_type = dnoc_packet_pld_header[]
if(dnoc_packet_ready)
begin
reg_read_en = 1'b0;
if(count == 3'b000)
begin
reg_write_en = 1'b1;
read_resp_tx_data_size = 3'b001;
read_resp_tx_qpe_dest = 6'b000010;
read_resp_tx_mod_dest = 5'b00000;
read_resp_tx_c_routing = 0;
read_resp_tx_pld_header = 17'b00100101000000000;
read_resp_tx_pld_address = 32'h0001;
read_resp_tx_pld_data = 256;
//count = 3'b111;
reg_write_en = 1'b0;
end
next_count = count - 3'b001;
end
end
Es gibt ein paar Dinge, auf die Sie in Ihrem Code hinweisen sollten.
1) Sie fahren die Signale reg_read_en
und reg_write_en
in beiden Blöcken immer.
always@(posedge clk_i or negedge resetn_i) begin
if (resetn_i == 1'b0) begin
reg_read_en <= 1'b0; <=
reg_write_en <= 1'b0; <=
count <= 3'b111;
next_count <= 3'b111;
state <= S_INITIAL;
end else begin
state <= next_state;
count <= next_count;
end
Und
always@(*) begin
next_state = state;
next_count = count;
case(state)
S_INITIAL: begin //not enabled
if(enable_i == 1'b1) begin
next_state = S_ENABLED;
end
reg_read_en <= 1'b0; <==
reg_write_en <= 1'b0; <==
end
Dies ist nicht synthetisierbar und würde wahrscheinlich auch von einigen Simulationstools nicht zugelassen.
2) Sie schließen Latches in Ihren kombinatorischen Blöcken ab:
always@(*) begin
next_state = state;
next_count = count;
case(state)
S_INITIAL: begin //not enabled
if(enable_i == 1'b1) begin <==
next_state = S_ENABLED; <==
end <==
reg_read_en <= 1'b0;
reg_write_en <= 1'b0;
end
Die markierte Aussage bedarf in jedem Fall if
einer Entsprechung . else
Sie müssen alle möglichen Optionen abdecken.
Aus diesem Grund habe ich in meinem Kommentar vorgeschlagen, dass Sie einen einzigen sequentiellen Block verwenden, der diese beiden Probleme behebt und alles viel einfacher zu verfolgen macht.
Hier ist ein Beispiel für die Kombination:
always@(posedge clk_i or negedge resetn_i) begin
if (resetn_i == 1'b0) begin
reg_read_en <= 1'b0;
reg_write_en <= 1'b0;
count <= 3'b111;
state <= S_INITIAL;
// I was trained to give everything a reset value
// But that's not really necessary and is more a matter of style
// as long as it gets a value when it needs one.
// including this here for completeness
read_resp_tx_data_size <= 3'b000;
read_resp_tx_qpe_dest <= 6'b000000;
read_resp_tx_mod_dest <= 5'b00000;
read_resp_tx_c_routing <= 0;
read_resp_tx_pld_header <= 17'b00000000000000000;
read_resp_tx_pld_address <= 32'h0000;
read_resp_tx_pld_data <= 0;
end
else begin
case(state)
S_INITIAL: begin //not enabled
if(enable_i == 1'b1) begin
state <= S_ENABLED;
// set count to the initial value here
// this way, you re-set it every time you switch to enabled
count <= 3'b111;
// If there's anything else you want to setup, here's a good place to do it...
end
else begin
state <= S_INITIAL;
end
reg_read_en <= 1'b0;
reg_write_en <= 1'b0;
end
S_ENABLED: begin
if(enable_i == 1'b0) begin
state <= S_INITIAL;
// here's where you would clear any signals that you want to clear before going back to the initial state
end
else begin
state <= S_ENABLED;
end
case(mode_i)
M_PASSIVE:begin
reg_read_en <= dnoc_packet_ready;
//pkt_type = dnoc_packet_pld_header[]
if(dnoc_packet_ready) begin
reg_read_en <= 1'b0;
if(count == 3'b000) begin
reg_write_en <= 1'b1;
read_resp_tx_data_size <= 3'b001;
read_resp_tx_qpe_dest <= 6'b000010;
read_resp_tx_mod_dest <= 5'b00000;
read_resp_tx_c_routing <= 0;
read_resp_tx_pld_header <= 17'b00100101000000000;
read_resp_tx_pld_address <= 32'h0001;
read_resp_tx_pld_data <= 256;
// you can reset count here to 7 if you want it to start again
count <= 3'b111;
end
else begin
// this will make reg_write_en valid only when count is 0
// and it will be disabled the rest of the time.
// if you want it valid all the time, you can set this to 1 and clear it elsewhere.
reg_write_en <= 1'b0;
// this stops the counter from underflowing
// otherwise, when it's 0 it will underflow to 111.
// Unless that's what you want, of course.
count <= count - 3'b001;
end
end
end
// other cases...
endcase
endcase
end
end
read_resp_tx_*
), aber next_state
keiner davon. Beachten Sie, dass das next_state = state;
einen Standardwert bereitstellt und damit die Bedingungen für den Synthesizer erfüllt, um es als kombinatorische Logik zu sehen. Ein Latch wird abgeleitet, wenn es mindestens einen Pfad gibt, dem die Variable nicht innerhalb des gesamten always @*
Blocks zugewiesen wird.S_ONECLK_DELAY: next_state <= S_ENABLED;
. Aber schreiben enb ist immer hoch.. Übersehe ich etwas?Du bist ganz in der Nähe. Der Fehler ist, dass 'next_count' nur im kombinatorischen Always @(*) -Block aktualisiert wird und 'count' nur im Register/getakteten Teil immer @(posedge clk_i ... aktualisiert wird.
Entfernen Sie also next_count <= 3'b111;
Ich kann den Rest Ihres Codes nicht sehen, also fangen Sie damit an.
Sie haben nicht gesagt, wie viele Zyklen Sie warten müssen: 7 oder 8? Ich werde ehrlich sein. Meistens schreibe ich Code, der mich zu 99 % weiterbringt. Wenn die Daten nach 7 oder 8 Zyklen gespeichert sind, sind Details so einfach zu lösen, dass ich oft erst die Simulation laufen lasse und dann eine +1 oder -1 Korrektur mache.
Nachbearbeitung nach neuem Code:
Ich kann Ihre Zustandsmaschine nicht für Sie debuggen, aber ich kann einige Bemerkungen machen. Sie setzen reg_write_en und löschen es einige Zeilen später wieder.
Ich gehe davon aus, dass dies wirklich in der Hardware funktionieren soll, nicht nur in der Simulation. Wenn dies der Fall ist, erzeugt Ihr Code viele Latches. Sie werden darüber erst benachrichtigt, wenn Sie den Code synthetisieren. Zum Beispiel diese:
read_resp_tx_qpe_dest
read_resp_tx_mod_dest
read_resp_tx_c_routing
reg_read_en
reg_write_en
In Ihrem kombinatorischen Abschnitt immer @(*) müssen Sie jeder Variablen in jedem Zustand einen Wert zuweisen. Wenn nicht, bekommst du Riegel. Eine Möglichkeit, dies zu tun, ist das, was Sie mit next_state und next_count tun : Legen Sie oben einen Standardwert fest. Aber dann ist es auch eine gute Praxis zu dokumentieren, dass es sich um einen Standardwert handelt, der später geändert wird.
// Defaults:
next_state = state;
next_count = count;
reg_write_en = 1'b0;
reg_read_en = 1'b0;
Die ersten beiden müssen gleich bleiben, wenn sie nicht berührt werden.
Die anderen beiden sind Beispiele für Variablen, die nur in einem oder zwei Zuständen gesetzt sind und überall sonst Null sind.
Was Ihre 'read_resp_tx ...'-Variablen betrifft: Ich weiß nicht, was sonst noch mit ihnen gemacht wird, also müssen Sie entscheiden, wie Sie damit umgehen.
Wie Sie Ihren Code in einen Register- und einen kombinatorischen Abschnitt aufteilen: Das liegt bei Ihnen. Womit Sie sich wohlfühlen. Ich neige dazu, es oft zu tun, es sei denn, es ist eine triviale Zustandsmaschine. Ich habe Code von einer sehr großen IP-Firma gesehen, die Millionen von CPU-Kernen auf der ganzen Welt bereitstellt und sie überall verwenden . Ich vermute, dass es in ihrem Coding Style Guide obligatorisch ist.
next_count <= 3'b111
aus dem sequentiellen Block entfernt. Ich habe meine Frage so bearbeitet, dass sie den vollständigen Code dessen enthält, was ich versuche zu tun. So können Sie besser verstehen, wie Regs aktualisiert werden.if ((state != next_state) && (next_state = MY_STATE))
. Das bedeutet, dass Sie statt der Bedingung, die die Zustandsänderung auslöst, die Zustandsänderung selbst erkennen , wodurch Sie die Zustandsänderungsbedingungen nur an einer Stelle ändern können. Register-Only-Zustandsmaschinen erschweren dies erheblich. Im Nur-Register-Fall müssen Sie den Zustand registrieren und ausführen state != state_z
, was um 1cc vom Übergang verzögert wird.
frisco_1989
Stanri
frisco_1989
frisco_1989
Stanri
reg
s werden in sequentiellen Prozessblöcken verwendet, also spreche ich von areg
in Verilog. (Übrigens, ist SystemVerilog eine Option für Sie zum Lernen? SystemVerilog hebt unter anderem die Registrierungs-/Kabelunterscheidung auf.)frisco_1989
frisco_1989
Stanri
Stanri
frisco_1989