Verilog UART Transmitter sendet Bytes in falscher Reihenfolge

Ich habe den folgenden Verilog-Code, der nacheinander 8 Bytes an die serielle Schnittstelle sendet, nachdem eine Taste gedrückt wurde.

Das Problem ist, dass die Bytes nicht in der von mir erwarteten Reihenfolge gesendet werden.

Wenn ich zum Beispiel die Bytes 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD, 0xBE, 0xEF aussende, bekommt der PC 0xEF, 0xAD, 0xEF, 0xAD und bekommt manchmal den Rest nicht und legt auf.

Ich habe mir diesen Code viele Male angesehen und kann nicht herausfinden, warum das so sein könnte. Verwende ich die Teileauswahl falsch? Ich glaube nicht, aber ich wüsste nicht, woran es sonst noch liegen könnte.

Ich habe eine zweite Version dieses Codes angehängt, die funktioniert (was bedeutet, dass alle Bytes empfangen werden und in der richtigen Reihenfolge), aber es dauert einen zusätzlichen Taktzyklus, weil es die Daten im Schieberegister nach jeder Übertragung aktualisiert. Wenn Sie einen Grund sehen, warum die erste gepostete 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:

Nach dem Rat von @dwikle - anstatt einen "Catch-All" -Zustand zu verwenden, der alle 8 Bytes sendet - habe ich einen separaten Zustand für jedes Byte erstellt, das gesendet werden musste.

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 gepostet - wenn Sie einen Blick darauf werfen müssen. :)

UART-Modul

AKTUALISIEREN:

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

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

Die Übertragung funktioniert, aber (was scheinbar willkürlich ist) wird aufgelegt. Wenn ich versuche, 1000 Verschlüsselungen durchzuführen, kann ich im Durchschnitt etwa 250 bewältigen - manchmal mache ich alle 1000 erfolgreich und manchmal vielleicht nur 20 oder 50.

Ich hatte gedacht, dass die Übertragung auf der Übertragungsseite der Kommunikation einen Engpass darstellt – das heißt, ich habe die Übertragung umgeschrieben und daher den Zweck dieses Threads. Ich habe jedoch festgestellt, dass das Problem tatsächlich auf der Empfangsseite der Übertragung liegt. Was zu passieren scheint, ist, dass die Zustandsmaschine nach mehreren erfolgreichen Läufen erneut 7 Bytes sammelt und dann irgendwie das letzte Byte des 64-Bit-Chunks verfehlt und im Zustand hängen 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 die uart senden - und dann sollte die Zustandsmaschine mittuckern, die Nachricht verschlüsseln und zurücksenden.

Das Bereitstellen unterschiedlicher Datenvektoren für die rx-Leitung des uart 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.

Also, die Probleme, die ich versuche herauszufinden, sind:

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

2) Um dieses Problem zu diagnostizieren, habe ich versucht, mir die Simulationsergebnisse anzusehen. Allerdings - selbst diese scheinen unerwartetes Verhalten zu zeigen - und ich fürchte, vielleicht gebe ich die Stimuli falsch.

Jegliche Kommentare oder Vorschläge zur Implementierung der Testbench – oder was möglicherweise dazu führt, dass die Kommunikation unterbrochen wird – würden wir sehr begrüßen. Wenn diese Fragen elementar erscheinen, entschuldige ich mich - ich lerne noch.

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

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

Ich werde auch das Top-Level-Modul hier als Referenz posten.

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
Um zu sehen, was genau passiert, wäre es meiner Meinung nach eine gute Idee, 8 verschiedene Codes zu verwenden. Wenn Sie jetzt 0xBE erhalten, wissen Sie nicht, ob es das erste oder das zweite ist.
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.
Ja, es soll für jedes Byte pulsieren. Ich versuche, das Flag am Anfang des Always-Blocks zu löschen.
Ich habe auch versucht, verschiedene Vektoren als Eingabe zu verwenden, nur um sicherzugehen. Gleiches Problem.

Antworten (2)

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

Infolgedessen erhöht Ihre "transmission_test" -Zustandsmaschine zwei Zustände für jedes übertragene Byte, 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_transmitting" wahr geworden ist, bevor Sie zum 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 Verifizierung von FPGA-Code gar nicht genug betonen. Nehmen Sie sich die Zeit, sich mit Ihrem Simulator vertraut zu machen und lernen Sie, wie man gute Modul-Testbenches schreibt.

Dave – vielen Dank für die Antwort und dafür, dass Sie sich die Zeit genommen haben, das Problem zu untersuchen. Offensichtlich 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 es mehrere andere Probleme im Code gibt - ich würde mich freuen, wenn Sie etwas Licht ins Dunkel bringen könnten. Wie ich schon sagte, ich lerne immer noch, gutes HDL zu schreiben, und freue mich über Kommentare oder Kritik. Danke dir!
@KristinBarber: Nun, wenn man sich nur 'transmission_test_3.v' ansieht, gibt es keine Zuweisung zum "Reset" -Signal. Ich bin mir nicht sicher, was Ihre Absicht mit dem "Tasten" -Signal war, aber so 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 die "Standardzuweisung", außerhalb der Case-Anweisung "mit der Übertragung zu beginnen", wirklich nicht. Ich habe das beim ersten Mal übersehen, und so etwas macht Ihren Code im Allgemeinen schwer lesbar.
@KristinBarber: Der uart.v-Code selbst hat einige schwerwiegendere Probleme, obwohl es so aussieht, als hätten Sie ihn von jemand anderem bekommen. Der „if (rst) begin …“-Block ist völlig wirkungslos, da er durch den Rest des Codes innerhalb des always-Blocks überschrieben wird – der gesamte andere Code sollte in einem „else“ stehen, das mit dem „if (zuerst)". Und es gibt viele interne Zustände, die durch das Zurücksetzen sowieso nicht auf bekannte Werte gesetzt werden. Es mag am Ende nicht wirklich wichtig sein, aber wenn doch, sollte es als solches klar dokumentiert werden.
Dave - Danke für all den Input. Ich könnte wirklich Hilfe bei der Diagnose eines größeren Problems gebrauchen, das ich in einem Update zum ursprünglichen Frageposting skizziert habe. Wenn du dir das mal anschauen könntest, würde mir das sehr helfen.
@kbarber: Das Problem mit Ihrer Testbench ist, dass sie nur ein Startbit am Anfang der Nachricht sendet und nicht ein Startbit pro Byte. Dies verursacht ein datenabhängiges 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 - Wow, was für ein dummer Fehler. Es ist schon komisch, wie man nach mehrmaligem Betrachten des Codes das Offensichtliche komplett verschweigen kann. Danke noch einmal! Wenn Sie in der Lage sind, eine Erklärung dafür zu finden, warum sich die tatsächliche Hardware schlecht verhält - ich würde es gerne hören -, ist dies seit Tagen die Wurzel meines Problems! Es ist verwirrend, warum an beliebigen Stellen - mit beliebigen Daten - einige der Daten verloren gehen.
@kbarber: Das einzige potenzielle Problem, das ich sehe, liegt im UART-Modul. Wenn es auf der Empfangsseite einen "Fehlstart" erkennt, sperrt es sich selbst für zwei Bitzeiten, was bedeutet, dass es in dieser Zeit ein legitimes Startbit verpassen könnte. Wenn Sie mit Ihrem Hardware-Setup Fehlstarts machen, könnten Sie zusammen mit einigen Datenbytes, die hauptsächlich Einsen sind (0xFF, 0xFE usw.), am Ende ein ganzes Byte aus einer Gruppe von acht verlieren. Es gibt wirklich keinen Grund, bei einem Fehlstart in den Zustand RX_ERROR zu gehen, es sollte einfach zu RX_IDLE zurückkehren und sofort mit der Suche nach dem nächsten Startbit beginnen.
Danke, ich werde versuchen, diese Änderungen vorzunehmen und es auszuprobieren.
Leider schien dies nicht zu helfen. Ich habe auch versucht, ein von Altera bereitgestelltes UART-Beispiel aus ihrem Kochbuch auf ähnliche Weise zu verwenden - für den Fall, dass es eine kleine Diskrepanz mit dem von mir verwendeten Backbone-Code gab. Trotzdem - das Problem besteht weiterhin. Glauben Sie, dass das Problem im USB-to-Serial-Adapter liegt?
Möglicherweise. Es ist Zeit, ein paar Loopbacks auszuprobieren. Zuerst einfach die Pins 2 und 3 auf dem Adapter zusammenstecken und sehen, ob er Daten zuverlässig weitergibt. Sie können dies auch im FPGA auf der seriellen Seite des UART tun. Versuchen Sie dann, die Daten direkt an der parallelen Schnittstelle des UART im FPGA zurückzuschleifen. Wenn das alles zuverlässig funktioniert, liegt das Problem woanders im FPGA. Ansonsten wissen Sie jetzt, worauf Sie Ihre Aufmerksamkeit richten müssen.

Ohne die Anforderungen des UART zu kennen, vermute ich, dass das Problem bei begin_transmit. In Ihrem "Arbeitsfall" begin_transmitwird jedes Byte gelöscht, da Sie sich durch state bewegen UPDATE_DATA. Im nicht funktionierenden Fall bleiben Sie jedoch SEND_BYTESwährend der gesamten Übertragung im Status und begin_transmitwerden nur freigegeben, wenn isTransmitting == 1. (Beachten Sie, dass 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, ein Posedge on zu sehen begin_transmit. isTransmittingOhne das UART-Modell habe ich so für die Simulation modelliert .

always @(posedge sysclk) begin
  isTransmitting <= begin_transmit;
end
Oh, das ist aufschlussreich – eigentlich. Ich hatte den Eindruck, dass jedes Mal, wenn der Always-Block eingegeben wurde, irgendwelche Aussagen ausgewertet würden und DANN der Fall ausgewertet würde. 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?
Ich habe meinen ursprünglichen Beitrag aktualisiert, um diesen Vorschlägen Rechnung zu tragen. Ich habe aber immer noch Probleme. Bitte werfen Sie einen Blick darauf und lassen Sie es mich wissen, wenn Sie weitere Vorschläge haben - wenn möglich.
In Bezug auf den "Standard" scheint er gesagt zu haben, dass die Variable den Wert it behält, wenn keiner der Fälle ausgeführt wird oder wenn ein if-Test innerhalb des ausgewählten Falls dazu führt, dass der Rest des Codes übersprungen wird vorher hatte - also behält es den, den es am Anfang des Blocks hatte - und daher ist das im Wesentlichen ein Standardwert. Standard, wie in "dies ist der Wert, es sei denn, etwas anderes ändert ihn".