Verilog UART Transmitter sendet Bytes außer Betrieb

7

Ich habe den folgenden Verilog-Code, der nach dem Drücken einer Taste nacheinander 8 Bytes an die serielle Schnittstelle sendet.

Das Problem ist, dass die Bytes nicht in der richtigen Reihenfolge gesendet werden, was ich erwarten würde.

Wenn ich zum Beispiel die Bytes 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF sende, erhält der PC 0xEF, 0xAD, 0xEF, 0xAD und empfängt manchmal nicht den Rest und legt auf.

Ich habe diesen Code oft angeschaut und kann anscheinend nicht herausfinden, warum dies so ist. Benutze ich die Teileauswahl falsch? Ich glaube nicht, aber ich weiß nicht, was sonst das Problem sein könnte.

Ich habe eine zweite Version dieses Codes angehängt, die funktioniert (dh alle Bytes werden empfangen und in der richtigen Reihenfolge), aber es dauert einen zusätzlichen Taktzyklus, da die Daten im Schieberegister nach jeder Übertragung aktualisiert werden. Wenn Sie einen Grund sehen können, warum die erste veröffentlichte Version nicht funktioniert, die zweite jedoch - lassen Sie es mich bitte wissen!

module transmission_test_2(sysclk, rxd, txd, LED, button);

input sysclk, rxd, button;
output txd;
output reg LED;

wire receiving_complete, isReceiving, isTransmitting, isError;
reg begin_transmit;
reg [7:0] tbyte;

wire [7:0] rbyte;

reg [2:0] state;

reg [63:0] plain_text;

integer byteN;


parameter IDLE = 0, BEGIN_TRANSMISSION = 1, UPDATE_DATA = 2, SEND_BYTES = 3;


uart uart1(
.clk(sysclk),
.rx(rxd),
.tx(txd),
.transmit(begin_transmit),
.tx_byte(tbyte),
.received(receiving_complete),
.rx_byte(rbyte),
.is_receiving(isReceiving), 
.is_transmitting(isTransmitting),
.recv_error(isError)
);


always @(posedge sysclk)
begin
    begin_transmit = 1'b0;
    case(state)
    IDLE: begin
        if(button==1'b0) begin
            LED = 1'b1;
            plain_text = 64'hDEADBEEFDEADBEEF;
            state = BEGIN_TRANSMISSION;
        end else begin
            LED <= 1'b0;
        end
    end
    BEGIN_TRANSMISSION: begin
        tbyte = plain_text[7:0];
        begin_transmit = 1'b1;
        byteN = 1;
        state = SEND_BYTES;
    end
    SEND_BYTES: begin
        if(!isTransmitting) begin
            tbyte = plain_text[byteN*8 +: 8];
            begin_transmit = 1'b1;
            byteN = byteN + 1;
            if(byteN == 8) begin
                state = IDLE;
            end
        end
    end
    endcase
end

endmodule

Zweite "funktionierende" Version:

module transmission_test(sysclk, rxd, txd, LED, button);


input sysclk, rxd, button;

output txd;

output reg LED;


wire receiving_complete, isReceiving, isTransmitting, isError;

reg begin_transmit;

reg [7:0] tbyte;


wire [7:0] rbyte;


reg [2:0] state;

reg [63:0] plain_text;

integer bytes_remaining;


parameter IDLE = 0, BEGIN_TRANSMISSION = 1, UPDATE_DATA = 2, SEND_BYTES = 3, DONE = 4;


uart uart1(
.clk(sysclk),
.rx(rxd),
.tx(txd),
.transmit(begin_transmit),
.tx_byte(tbyte),
.received(receiving_complete),
.rx_byte(rbyte),
.is_receiving(isReceiving), 
.is_transmitting(isTransmitting),
.recv_error(isError)
);


always @(posedge sysclk)
begin
    begin_transmit = 1'b0;
    case(state)
    IDLE: begin
        if(button==1'b0) begin
            LED = 1'b1;
            plain_text = 64'hDEADBEEFDEADBEEF;
            state = BEGIN_TRANSMISSION;
        end else begin
            LED <= 1'b0;
        end
    end
    BEGIN_TRANSMISSION: begin
        tbyte = plain_text[7:0];
        begin_transmit = 1'b1;
        bytes_remaining = 7;
        state = UPDATE_DATA;
    end
    UPDATE_DATA: begin
        plain_text = plain_text >> 8;
        state = SEND_BYTES;
    end
    SEND_BYTES: begin
        if(!isTransmitting) begin
            tbyte = plain_text[7:0];
            begin_transmit = 1'b1;
            bytes_remaining = bytes_remaining - 1;
            if(bytes_remaining == 0) begin
                state = IDLE;
            end else begin
                state = UPDATE_DATA;
            end
        end
    end
    endcase
end

endmodule

REVISION:

Dem Rat von @dwikle folgend - anstatt einen "Catch-All" -Zustand zu verwenden, der das Senden aller 8 Bytes durchläuft - habe ich für jedes zu sendende Byte einen separaten Status erstellt.

Hier ist der Code:

module transmission_test_3(sysclk, rxd, txd, LED, button);

input sysclk, rxd, button;
output txd;
output reg LED;

wire receiving_complete, isReceiving, isTransmitting, isError, reset;
reg begin_transmit;
reg [7:0] tbyte;

wire [7:0] rbyte;

reg [2:0] state = 4'b0;
reg [63:0] plain_text = 64'h0;

uart uart1(
.clk(sysclk),
.rst(reset),
.rx(rxd),
.tx(txd),
.transmit(begin_transmit),
.tx_byte(tbyte),
.received(receiving_complete),
.rx_byte(rbyte),
.is_receiving(isReceiving), 
.is_transmitting(isTransmitting),
.recv_error(isError)
);

always @(posedge sysclk)
begin
    begin_transmit = 1'b0;
    case(state)
    4'b0000: begin
        if(button==1'b0) begin
            LED = 1'b1;
            plain_text = 64'hDEADBEEFAAABACAD;
            state = 4'b0001;
        end else begin
            LED = 1'b0;
        end
    end
    4'b0001: begin
        tbyte = plain_text[7:0];
        begin_transmit = 1'b1;
        state = 4'b0010;
    end
    4'b0010: begin
        if(!isTransmitting) begin
            tbyte = plain_text[15:8];
            begin_transmit = 1'b1;
            state = 4'b0011;
        end
    end
    4'b0011: begin
            if(!isTransmitting) begin
                tbyte = plain_text[23:16];
                begin_transmit = 1'b1;
                state = 4'b0100;
            end
        end
    4'b0100: begin
        if(!isTransmitting) begin
            tbyte = plain_text[31:24];
            begin_transmit = 1'b1;
            state = 4'b0101;
        end
    end
    4'b0101: begin
        if(!isTransmitting) begin
            tbyte = plain_text[39:32];
            begin_transmit = 1'b1;
            state = 4'b0110;
        end
    end
    4'b0110: begin
        if(!isTransmitting) begin
            tbyte = plain_text[47:40];
            begin_transmit = 1'b1;
            state = 4'b0111;
        end
    end
    4'b0111: begin
        if(!isTransmitting) begin
            tbyte = plain_text[55:48];
            begin_transmit = 1'b1;
            state = 4'b1000;
        end
    end
    4'b1000: begin
        if(!isTransmitting) begin
            tbyte = plain_text[63:56];
            begin_transmit = 1'b1;
            state = 4'b0000;
        end
    end
    endcase
end
endmodule

Die Ergebnisse sind jedoch immer noch dieselben - ich erhalte jedes zweite Byte. Was gibt?

Irgendwelche Ideen?

PS - Ich habe den UART-Code in meinen Google-Dokumenten veröffentlicht - wenn Sie einen Blick darauf werfen müssen. :) :)

UART-Modul

AKTUALISIEREN:

Die ursprüngliche Frage für diesen Thread ergab sich aus dem Versuch, ein Kommunikationsproblem in einem größeren Modul zu isolieren.

Das Projekt akzeptiert einfach 64-Bit-Daten (8 Byte) vom PC - verschlüsselt die Daten mit dem DES-Algorithmus - und sendet die verschlüsselte Nachricht (wieder 64-Bit) zurück.

Die Übertragung funktioniert, legt aber (was willkürlich zu sein scheint) auf. Wenn ich versuche, 1000 Verschlüsselungen durchzuführen, kann ich durchschnittlich 250 verarbeiten - manchmal alle 1000 erfolgreich und manchmal vielleicht nur 20 oder 50.

Ich hatte gedacht, dass die Übertragung auf der Übertragungsseite der Kommunikation ein Flaschenhals war - das heißt, ich schrieb die Übertragung neu und damit den Zweck dieses Threads. Mir ist jedoch klar geworden, dass das Problem tatsächlich auf der Empfangsseite der Übertragung liegt. Was zu passieren scheint, ist, dass die Zustandsmaschine nach mehreren erfolgreichen Läufen wieder 7 Bytes sammelt und dann irgendwie das letzte Byte des 64-Bit-Blocks verfehlt und im Zustand stecken bleibt, der nach dem letzten Byte sucht.

Ich habe versucht, das Design zu simulieren - aber ich habe das Gefühl, dass der Stimulus, den ich gebe, nicht ganz koscher ist. Die Testbench sollte Daten im Wert von 8 Bytes über das Uart senden - und dann sollte die Zustandsmaschine die Nachricht tuckern, verschlüsseln und zurücksenden.

Das Bereitstellen unterschiedlicher Datenvektoren für die Empfangslinie des Uarts führt jedoch zu drastisch unterschiedlichen Ergebnissen. Abhängig von den Daten, die ich verwende, empfängt der Uart manchmal nur 7 Bytes (was das Problem ist, das ich mit echter Hardware betrachte) oder gibt sogar Fehler zurück.

Die Probleme, die ich herauszufinden versuche, sind:

1) Ich kann erfolgreich mehrere zehn bis hundert Verschlüsselungen empfangen und senden - aber die Kommunikation hängt an beliebigen Punkten ab - und es scheint, dass die Zustandsmaschine in diesem Fall 7 Bytes gesammelt hat und nach den letzten sucht.

2) Um dieses Problem zu diagnostizieren, habe ich versucht, die Simulationsergebnisse zu untersuchen. Allerdings - selbst diese scheinen unerwartetes Verhalten hervorzurufen - und ich fürchte, ich gebe möglicherweise falsche Reize.

Kommentare oder Vorschläge zur Implementierung der Testbench - oder was dazu führen kann, dass die Kommunikation unterbrochen wird - sind sehr willkommen. Wenn diese Fragen elementar erscheinen, entschuldige ich mich - ich lerne noch.

Ich habe in meinen Google-Dokumenten eine Zip-Datei mit allen relevanten Dateien, einschließlich der Testbench, angehängt.

https://docs.google.com/open?id=0B4WyEjzmIhtNN0V6a0x5U19SMUU

Ich werde hier auch das Top-Level-Modul als Referenz veröffentlichen.

module rs232_neek(sysclk, rxd, txd, reset, LED);

input sysclk, rxd, reset;
output txd;
wire receiving_complete, isTransmitting, isReceiving, isError;

output reg [3:0] LED;   //The LEDs are used simply as debug - to determine which state the machine gets held-up in.

reg begin_transmit;
reg [7:0] tbyte;

wire [7:0] rbyte;

parameter FIRST_BYTE = 0, GET_BYTES = 1, BEGIN_ENC = 2, CHECK_ENC_STATUS = 3,     BEGIN_TRANSMISSION = 4, SEND_BYTES = 5;

reg [2:0] state = 3'b0;
integer byteN = 0;

reg [3:0] sel = 4'b0;
reg [63:0] plain_text;
reg [63:0] cipher_text;
wire [63:0] cipher_net;

uart uart1(
.clk(sysclk),
 .rst(~reset),
.rx(rxd),
.tx(txd),
.transmit(begin_transmit),
.tx_byte(tbyte),
.received(receiving_complete),
.rx_byte(rbyte),
.is_transmitting(isTransmitting),
.is_receiving(isReceiving),
.recv_error(isError)
);

des des1(
 .clk(sysclk),
 .key(56'h0),
 .roundSel(sel),
 .decrypt(1'b0),
 .desIn(plain_text),
 .desOut(cipher_net)
 );

always @(posedge sysclk)
begin

    if(~reset) begin
        state = FIRST_BYTE;
    end

    LED = 4'b1111;

    case(state)
        FIRST_BYTE: begin
            LED[0] = 1'b0;
            begin_transmit = 1'b0;
            if(receiving_complete) begin
               plain_text[7:0] = rbyte;
                byteN = 1;
                state = GET_BYTES;
            end
        end
        GET_BYTES: begin
            LED[1] = 1'b0;
            if(receiving_complete) begin
                plain_text[byteN*8 +: 8] = rbyte;
                byteN = byteN + 1;
                if(byteN == 8) begin
                    state = BEGIN_ENC;
                end
            end
        end
        BEGIN_ENC: begin
            sel = 4'b0;
            state = CHECK_ENC_STATUS;
        end
        CHECK_ENC_STATUS: begin
            LED[2] = 1'b0;
            sel = sel + 1;
            if(sel == 15) begin
                state = BEGIN_TRANSMISSION;
            end
        end
        BEGIN_TRANSMISSION: begin
            cipher_text = cipher_net;
            tbyte = cipher_text[7:0];
            begin_transmit = 1'b1;
            byteN = 1;
            state = SEND_BYTES;
        end
        SEND_BYTES: begin
            LED[3] = 1'b0;
            if(!isTransmitting && !begin_transmit) begin
                tbyte = cipher_text[byteN*8 +: 8];
                begin_transmit = 1'b1;
                byteN = byteN + 1;
                if(byteN == 8) begin
                    state = FIRST_BYTE;
                end
            end else begin
                begin_transmit = 1'b0;
            end
        end
    endcase
end

endmodule
kbarber
quelle
1
Um zu sehen, was genau passiert, halte ich es für eine gute Idee, 8 verschiedene Codes zu verwenden. Wenn Sie jetzt 0xBE erhalten, wissen Sie nicht, ob es das erste oder das zweite ist.
Stevenvh
Ehrlich gesagt bin ich mir nicht sicher, wie einer von ihnen funktioniert. Soll "begin_transmit" für jedes gesendete Byte pulsieren? Ich sehe, wo Sie es einstellen, aber nicht, wo es gelöscht wird.
Dave Tweed
Ja, es wird angenommen, dass für jedes Byte ein Impuls ausgegeben wird. Ich versuche, die Flagge am Anfang des Always-Blocks zu löschen.
Kbarber
Ich habe auch versucht, verschiedene Vektoren als Eingabe zu verwenden, nur um sicherzugehen. Gleiches Problem.
Kbarber

Antworten:

4

Das Problem ist, dass Ihr "Transmission_Test" -Code so geschrieben ist, dass davon ausgegangen wird, dass "is_transmission" vom UART vor der Taktflanke, die auf die Behauptung von "begin_transmit" folgt, wahr wird. Dies ist nicht der Fall - es dauert einen Taktzyklus, bis der UART-Sender seinen "Leerlauf" -Zustand verlässt. Danach ist "is_transmission" wahr.

Infolgedessen rückt Ihre Zustandsmaschine "Transmission_test" zwei Zustände für jedes übertragene Byte vor, und Sie sehen nur jedes zweite Byte am Ausgang des UART.

Es gibt viele kleinere Probleme mit Ihren Codebeispielen, aber das Wichtigste, was Sie beheben müssen, ist zu überprüfen, ob "is_transmission" wahr ist, bevor Sie mit dem nächsten Byte in der Nachricht fortfahren.

Dies wäre ziemlich offensichtlich gewesen, wenn Sie sich die Zeit genommen hätten, dieses Projekt zu simulieren. Ich kann die Bedeutung der Simulation bei der Überprüfung des FPGA-Codes nicht genug betonen. Nehmen Sie sich Zeit, um sich mit Ihrem Simulator vertraut zu machen und zu lernen, wie Sie gute Modulprüfstände schreiben.

Dave Tweed
quelle
Dave - vielen Dank für die Antwort und die Zeit, sich mit dem Problem zu befassen. Natürlich hätte ich die Simulationsergebnisse genauer analysieren sollen. Ich lerne immer noch die meisten Nuancen von Verilog und hatte angenommen, dass ich etwas im Code selbst übersehen habe. Sie haben erwähnt, dass der Code mehrere andere Probleme enthält - ich würde mich freuen, wenn Sie etwas Licht ins Dunkel bringen könnten. Wie gesagt, ich lerne immer noch, gutes HDL zu schreiben und freue mich über Kommentare oder Kritik. Vielen Dank!
Kbarber
@KristinBarber: Nun, wenn man sich nur 'Transmission_test_3.v' ansieht, gibt es keine Zuordnung zum "Reset" -Signal. Ich bin mir nicht sicher, was Sie mit dem "Knopf" -Signal beabsichtigt haben, aber wie es eingerichtet ist, sendet die Schaltung kontinuierlich, solange sie aktiv (niedrig) ist, anstatt eine Nachricht pro Tastendruck zu senden. Und ich nehme an, es ist eher ein Stilproblem als ein Fehler, aber ich mag es wirklich nicht, wenn die "Standardzuweisung" außerhalb der case-Anweisung "mit der Übertragung beginnt". Ich habe das beim ersten Mal verpasst, und so etwas macht Ihren Code im Allgemeinen schwer lesbar.
Dave Tweed
@KristinBarber: Der uart.v-Code selbst hat einige ernstere Probleme, obwohl es so aussieht, als hätten Sie dies von jemand anderem bekommen. Der Block "if (rst) begin ..." ist völlig ineffektiv, da er vom Rest des Codes im Always-Block überschrieben wird. Der gesamte andere Code sollte sich in einem "else" befinden, das dem "if" zugeordnet ist (rst) ". Und es gibt viele interne Zustände, die beim Zurücksetzen sowieso nicht auf bekannte Werte gesetzt werden. Es mag am Ende nicht wirklich wichtig sein, aber wenn ja, sollte es als solches klar dokumentiert werden.
Dave Tweed
Dave - Danke für all die Beiträge. Ich könnte wirklich Hilfe gebrauchen, um ein größeres Problem zu diagnostizieren - das ich in einem Update des ursprünglichen Fragenpostings beschrieben habe. Wenn Sie sich das ansehen könnten, würde es mir enorm helfen.
Kbarber
@kbarber: Das Problem mit Ihrer Testbench ist, dass sie nur ein Startbit am Anfang der Nachricht sendet und nicht ein Startbit pro Byte. Dies führt zu einem datenabhängigen Verhalten, da der UART die erste Null in den Daten als Startbit und nicht als Datenbit interpretiert. Ich suche immer noch nach möglichen Problemen mit der eigentlichen Hardware.
Dave Tweed
0

Ohne die Anforderungen des UART zu kennen, liegt das Problem vermutlich bei begin_transmit. In Ihrem "funktionierenden" Fall begin_transmitwird für jedes Byte gelöscht, da Sie sich durch den Status bewegen UPDATE_DATA. Für den nicht funktionierenden Fall bleiben Sie jedoch SEND_BYTESfür die gesamte Übertragung im Status und begin_transmitwerden nur gelöscht, wenn isTransmitting == 1. (Beachten Sie, dass dies vor der case-Anweisung begin_transmitzugewiesen 0wird, wodurch eine Standardzuweisung erstellt wird.)

Ich habe den nicht funktionierenden Fall simuliert und begin_transmitpulsiere nur jedes zweite Byte, was erklären würde, was Sie sehen, wenn der UART erwartet, dass ein Posedge angezeigt wird begin_transmit. Ohne das UART-Modell habe ich isTransmittingfür die Simulation so modelliert .

always @(posedge sysclk) begin
  isTransmitting <= begin_transmit;
end
dwikle
quelle
Oh, das ist aufschlussreich - eigentlich. Ich hatte den Eindruck, dass bei jeder Eingabe des Always-Blocks alle Aussagen ausgewertet und dann der Fall bewertet würden. Sie sagen jedoch, dass in einer solchen Situation - etwas vor einer case-Anweisung in einem always-Block als Standard für den Fall behandelt wird?
Kbarber
Ich habe meinen ursprünglichen Beitrag aktualisiert, um diesen Vorschlägen Rechnung zu tragen. Ich habe aber immer noch Probleme. Bitte schauen Sie vorbei und lassen Sie mich wissen, wenn Sie weitere Vorschläge haben - wenn möglich.
Kbarber
In Bezug auf die "Standardeinstellung" scheint es mir, dass er sagte, wenn keiner der Fälle ausgeführt wird oder wenn ein Wenn-Test innerhalb des ausgewählten Falls dazu führt, dass der Rest des Codes übersprungen wird, behält die Variable den Wert bei hatte vorher - also behält es den, den es oben im Block hat - und daher ist das im Wesentlichen ein Standardwert. Standardmäßig wie in "Dies ist der Wert, sofern nichts anderes ihn ändert".
Gbarry