2017-12-03 1 views
0

Ich debugge ein Stück Verilog-Code für Tage, insbesondere Senden und Empfangen von Bytes von einem FX2LP (Cypress CY7C68016A) USB-Controller. Ohne in viele Details zu gehen, werden Daten in jedem Zyklus byteweise gesendet und übertragen. Für meinen Test verwende ich einen 16-Byte-Puffer, den ich zuerst auffülle und dann zurücksende (Echotest).Bit-Verschiebung im sequentiellen Block schlägt fehl, im Kombinationsmodus nicht. Warum?

Der wesentliche Teil meines Code wie folgt aussieht:

reg [127:0] dataBuf; // 16 byte buffer for USB data 

reg [7:0] cntByte; // counter for number of bytes 
reg [7:0] nextCntByte; 

reg shiftBufRx, shiftBufTx; // flags whether buffer should be shifted 
reg [7:0] currentByte; // current read byte 

// in transmit cycle, byte is read from USB_DATAOUT 
assign USB_DATAOUT = dataBuf[7:0]; 

always @(posedge FIFO_CLK) begin 
    // update state variables 
    CurrentState <= NextState; 
    cntByte <= nextCntByte; 

    if(shiftBufRx) begin // cycle was a receive 
     dataBuf <= { currentByte , dataBuf[127:8] }; 
    end 
    if(shiftBufTx) begin // cycle was a transmit 
     dataBuf <= { dataBuf[127-8:0] , 8'h00 }; 
    end 
end 

always @(*) begin 
    // avoid race conditions 
    NextState = CurrentState; 
    nextCntByte = cntByte; 
    nextDataBuf = dataBuf; 
    currentByte = 0; 
    shiftBufRx = 0; 
    shiftBufTx = 0; 

    case(CurrentState) 
     [...] 
     STATE_USBRX: begin 
      if(cntByte < 16) begin 
       nextCntByte = cntByte + 1; 
       currentByte = USB_DATAIN; // contains received byte in receive cycle 
       shiftBufRx = 1; // shift buffer after this cycle 
      end 
      [...] 
     end 
     STATE_USBTX: begin 
      if(cntByte < 15) begin 
       shiftBufTx = 1; // shift buffer after this cycle 
       nextCntByte = cntByte + 1; 
      end 
      [...] 
     end 
     [...] 
    endcase 
end 

Dieser Code perfekt in Simulation (iVerilog) arbeitet. Aber beim Synthetisieren und Ausführen auf einem Altera Cyclone bekomme ich sehr seltsame Fehler. Zum Beispiel wird das erste Byte, das an das FPGA übertragen wird, meistens für jedes Byte gelesen. Wenn Sie beispielsweise 11 22 33 44 55 66 ... senden, erhalten Sie 11 11 11 11 11 11 ....

Nun, wenn ich stattdessen eine neue Variable vorstellen:

reg [127:0] nextDataBuf; 

und das Teil in dem sequentiellen always @(posedge FIFO_CLK) Block mit ersetzen:

if(shiftBufRx) begin 
    dataBuf <= nextDataBuf; 
end 
if(shiftBufTx) begin 
    dataBuf <= nextDataBuf; 
end 

und in dem kombinatorischen Teil:

 STATE_USBRX: begin 
      if(cntByte < 16) begin 
       nextCntByte = cntByte + 1; 
       //currentByte = FIFO_DATAIN; 
       nextDataBuf = { dataBuf[127-8:0] , FIFO_DATAIN }; 
       shiftBufRx = 1; 
      end 
      [...] 
     end 
     STATE_USBTX: begin 
      if(cntByte < 15) begin 
       shiftBufTx = 1; 
       nextCntByte = cntByte + 1; 
       nextDataBuf = { 8'h00 , dataBuf[127:8] }; 
      end 
      [...] 
     end 

Dann funktioniert es!

Das bedeutet: Alles, was ich mache, ist die Verschiebung der Verschiebung des Registers vom sequentiellen Block zum kombinatorischen Block.

Ich sehe keine Race-Bedingungen in meinem Code und in der Simulation (iVerilog) beide Versionen sind identisch.

Was könnte der Grund sein?

+0

überprüfen Sie die Beziehung zwischen Tx/Rx Flags. Seltsames Singen kann passieren, wenn beide zur selben Zeit auf sind. – Serge

+0

Ihre Datenstrukturen stimmen nicht überein. '{dataBuf [127-8: 0], 8'h00}'! = '{8'h00, dataBuf [127: 8]}' – Greg

Antwort

0

Als erstes sollten Sie unbedingt eine MVCE posten. Dies ist fast ein MVCE, aber es enthält keine Testbench und die Ellipse [...] verdeckt Dinge, die relevant sein könnten, wie die Zustandsübergänge von RX zu TX.

Trotzdem, da es eng war, baute ich einen kleinen Prüfstand und fügte minimalen Code hinzu, um es zum Laufen zu bringen. In meinem Fall hat es NICHT funktioniert "perfekt in der Simulation". In der Tat gibt es einen eindeutigen Fehler in dem Schieberegister, wie auf @greg hingewiesen wird.

In Ihrem Vorher-Code werden Sie aus dem Register heraus verschoben, übertragen aber die Low-Bytes. Der erste Zyklus ist 8'h11 und danach entweder 8'h00 oder bleibt gleich, abhängig davon, wann shiftBufTx wahr ist, und dies hängt von der Logik ab, die nicht gepostet wird. Es gibt genug gepostet, um zu sehen, dass es möglicherweise nicht richtig funktionieren kann wie es ist.

assign USB_DATAOUT = dataBuf[7:0]; 
    ... 
    if(shiftBufTx) begin // cycle was a transmit 
     dataBuf <= { dataBuf[127-8:0] , 8'h00 }; 
    end 

In der nach dem Code, Sie verschieben rechts und deshalb funktioniert es:

nextDataBuf = { 8'h00 , dataBuf[127:8] }; 

Es ist möglich, dass die SIM-Karte nicht Synthese aus einem ähnlichen Grund überein. Der Code sieht ziemlich synthetisierbar aus.Einige Vorschläge:

  • Es gibt keinen Grund unabhängige Fahnen haben für Tx und Rx
  • Wie @serge wies darauf hin, was passiert, wenn sie in Konflikt geraten? Dies kann sim in Ordnung, aber wird nicht arbeiten in Hardware.
  • Sie könnten einfach ein einzelnes Flag verwenden und es in der Ausgabe von jedem Zustand setzen.
  • Wenn Sie mehr als eine Bedingung benötigen, müssen Sie ein anderes haben, dass-nicht blockiert nur ein Pfad versichert kann jeder ändern, um eine var Zuordnung zu jeder Taktflanke aktiv ist:

    if(shiftBufRx) begin // cycle was a receive 
        dataBuf <= { currentByte , dataBuf[127:8] }; 
    end 
    **else** if(shiftBufTx) begin // cycle was a transmit 
        dataBuf <= **{ 8'h00 , dataBuf[127:8] }**; 
    end 
    

Warum schien es in Sim zu funktionieren? Neben kleinen Tippfehlern oder Vertauschungen von vermeintlich identischem Code, verzeiht die Simulation mehrere Schreibvorgänge in derselben Variablen (die spätere wird "gewinnen").

Aber das Synthese-Tool muss Ihre Absicht in Register intuitiv erfassen. Abhängig davon, wie komplex die Logik ist, kann sie möglicherweise nicht wissen, dass beide Flags nicht gleichzeitig wahr sein können. Manchmal werden solche Dinge als zwei parallele "Schreibfreigabe" -Bedingungen für dataBuf interpretiert und zwei parallele Register erstellt. Die zweite Kopie wurde wahrscheinlich nie wieder zurückgesetzt, nachdem sie auf 8'h11 gesetzt wurde, was den Symptomen entspricht. Sie sollten den tatsächlichen Ausgangsschaltkreis nach der Ausarbeitung untersuchen, um diese Art von Fehler zu erkennen. Leider scheitern nicht alle oder geben sogar deutliche Warnungen. In Xilinx Vivado können Sie den Schaltplan hochziehen.

Verwandte Themen