Verzögerungen und/oder wie man die Uhr in einer Schleife manuell durchläuft, wenn man eine Verilog-Testbench erstellt, um FSM für die Mikrocode/ROM-Konvertierung zu testen

Ich arbeite an einem Projekt, bei dem ich eine in Verilog codierte endliche Zustandsmaschine in ein ROM konvertieren muss. Dazu muss ich eine Speicherdatei für die ROM-Version des FSM erstellen, die den nächsten Zustand und die Ausgangswerte an Adressen speichert, die aus dem aktuellen Zustand und den Eingangswerten bestehen. Mein Gedanke zum Erstellen dieser Datei war, die betreffende FSM zu nehmen und die Eingabe "current_state" und die Ausgabe "next_state" hinzuzufügen und dann kleine Änderungen an der fsm vorzunehmen, sodass sie diese verwendet. Ich hatte jedoch mehrere Probleme mit diesem Testbench, die ich anscheinend nicht herausfinden kann, also dachte ich, ich würde ein kleineres FSM zusammenwerfen, um zu zeigen, was ich tue, und hoffentlich einen Einblick bekommen, was ich falsch mache. Ich glaube, mein Hauptproblem ist, dass das Taktsignal überhaupt nicht auszulösen scheint (zumindest wenn ich debugge, der Block, der bei der Posedge-Uhr aktiviert werden sollte, scheint nie ausgelöst zu werden). Das fragliche FSM, das ich zu modellieren versuche, hat so etwas wie 15 aktuelle Zustands- und Eingangsbits und 76 nächste Zustands- und Ausgangsbits, also anstatt einen der Codes zu verwenden, habe ich einfach dieses kleinere FSM zusammengeworfen (ich bekomme das gleiche Verhalten Das Problem ist also nicht zustandsmaschinenspezifisch und liegt wahrscheinlich eher an meiner schlechten Testbench).

Hier ist also die zu testende FSM-Einheit. Es ist ein grundlegendes FSM, das ich aus einem alten Lehrbuch entnommen habe:

    module mealy_traditional(input wire clk, 
                        input wire reset, 
                        input wire a, 
                        input wire b, 
                        output wire y);
    
    //Symbollic State Definition
    localparam [1:0]    state0 = 2'b00, 
                        state1 = 2'b01, 
                        state2 = 2'b10;
    //signal declaration
    reg [1:0]   stateCurrent, 
                stateNext;
    
    //state register
    always @(posedge clk, posedge reset)
        if(reset)
            stateCurrent <= state0;
        else
            stateCurrent <= stateNext;
    
    //_______________________________________________________Next State Logic
    always @* begin
        stateNext = stateCurrent
        //______________________________________________________START_CASE
        case(stateCurrent)
            //___________________________________________________state0
            state0:
                if(a)
                    if(b)
                        stateNext = state2;
                    else
                        stateNext = state1;
                else
                    stateNext = state0;
            //___________________________________________________state1
            state1:
                if(a)
                    stateNext = state0;
                else
                    stateNext = state1;
            //___________________________________________________state2
            state2: stateNext = state0;
            //__________________________________________________default
            default: stateNext = state0;
        endcase
    end
    //Mealy Output Logic, function of both current state and current inputs
    assign y = (stateCurrent == state0) & a & b;
endmodule

Und hier ist dieselbe FSM, die (glaube ich) modifiziert wurde, um den aktuellen Zustand als Eingang zu akzeptieren und den nächsten Zustandsausgang einem Drahtausgang zuzuweisen.

    module mealy_traditional(input wire clk, 
                        input wire reset, 
                        input wire [1:0] state_current, 
                        input wire a, 
                        input wire b, 
                        output wire [1:0] state_next, 
                        output wire y);
    
    //Symbollic State Definition
    localparam [1:0]    state0 = 2'b00, 
                        state1 = 2'b01, 
                        state2 = 2'b10;
    //signal declaration
    reg [1:0]   stateCurrent, 
                stateNext;
    
    //state register
    always @(posedge clk, posedge reset)
        if(reset)
            stateCurrent <= state0;
        else
            stateCurrent <= state_current;
    
    //_______________________________________________________Next State Logic
    always @* begin
        stateNext = stateCurrent;
        //______________________________________________________START_CASE
        case(stateCurrent)
            //___________________________________________________state0
            state0:
                if(a)
                    if(b)
                        stateNext = state2;
                    else
                        stateNext = state1;
                else
                    stateNext = state0;
            //___________________________________________________state1
            state1:
                if(a)
                    stateNext = state0;
                else
                    stateNext = state1;
            //___________________________________________________state2
            state2: stateNext = state0;
            //__________________________________________________default
            default: stateNext = state0;
        endcase
    end
    //Mealy Output Logic, function of both current state and current inputs
    assign y = (stateCurrent == state0) & a & b;
    assign state_next = stateNext;
endmodule

Hier liegt also mit ziemlicher Sicherheit das Problem, die Testbench. Jetzt weiß ich, dass es angesichts der begrenzten Eingaben dieser FSM sehr einfach wäre, die fsm mit jeder Eingabekombination manuell zu treffen und die Ausgabe anzuzeigen, aber offensichtlich ist dies bei der anderen FSM, die fast 100 Bit IO hat, nicht wirklich eine Option . Daher habe ich mich entschieden, eine for-Schleife zu erstellen, um einfach alle möglichen Eingabebitkombinationen zu durchlaufen und dann die Ausgaben zu generieren, um sie in der Speicherdatei zu speichern. Das Problem, auf das ich stoße, ist, dass es nicht so aussieht, als ob der Takteingang überhaupt zyklisch läuft. Derzeit versuche ich den folgenden Testbench-Code

`timescale 1ns / 1ns
module mealy_traditional_tb;
    reg             clk = 1'b0;
    reg             reset = 1'b0;
    reg [3:0]       in;
    wire [2:0]      out;
    integer         i = 0;
    integer         maxValue = 16;


    //Instantiation
    mealy_traditional 
        uut(.clk(clk), 
            .reset(reset),
            .state_current(in[3:2]),
            .a(in[1]), 
            .b(in[0]), 
            .state_next(out[2:1]),
            .y(out[0]));
    initial begin
        
        reset = 1;
        #10
        reset = 0;
        #10
        for(i = 0; i < maxValue; i = i + 1) begin
            in = i;
            clk = 1;
            clk = 0;
            clk = 1;
            clk = 0;
            $display("%b : %b", in, out);
        end
        $finish;
    end
endmodule

Anfangs hatte ich versucht, einen eigenständigen Taktzyklus zu erstellen, indem ich eine verwendete

always #10 clk = ~clk;

Und dann Verzögerungen in die for-Schleife einfügen, was Verilog NICHT mochte (würde nicht einmal kompilieren, warf Syntaxfehler).

So wie es jetzt aussieht, wird dieser Testbench-Code kompiliert und der Simulator gestartet, aber es ist ziemlich klar, dass auf den Else-Zweig der Statusregisteraktualisierung in der FSM nicht zugegriffen wird, da sie niemals ausgelöst werden, wenn ich dort Haltepunkte setze Die Ausgabe der Zustandsmaschine scheint nur der Grundzustand von einem System-Reset zu sein. (folgendermaßen)

# 0000 : 00x
# 0001 : 00x
# 0010 : 00x
# 0011 : 00x
# 0100 : 00x
# 0101 : 00x
# 0110 : 00x
# 0111 : 00x
# 1000 : 00x
# 1001 : 00x
# 1010 : 00x
# 1011 : 00x
# 1100 : 00x
# 1101 : 00x
# 1110 : 00x
# 1111 : 00x

Ich hätte gedacht, dass das manuelle Erzwingen des Taktzyklus in der for-Schleife dazu führen würde, dass die endliche Zustandsmaschine aktualisiert wird, aber anscheinend nicht. Gibt es eine Möglichkeit für mich sicherzustellen, dass der Taktzyklus auf diese Weise ausgelöst wird?

Antworten (1)

Ja, das Problem liegt in Ihrem Testbench-Code. Es ist immer eine gute Idee, die Simulationszeit anzuzeigen, wenn Sie $displayWerte angeben, da dies beim Debuggen Ihres Problems hilft. Dies zeigt, dass Ihre gesamte Ausgabe gleichzeitig erfolgt (20 ns):

              20 0000 : 00x
              20 0001 : 00x
              20 0010 : 00x
              20 0011 : 00x
              20 0100 : 00x
              20 0101 : 00x
              20 0110 : 00x
              20 0111 : 00x
              20 1000 : 00x
              20 1001 : 00x
              20 1010 : 00x
              20 1011 : 00x
              20 1100 : 00x
              20 1101 : 00x
              20 1110 : 00x
              20 1111 : 00x

Die erste Spalte ist die Simulationszeit. Das Problem ist, dass innerhalb der Schleife keine Zeit vergeht for. #Eine Möglichkeit, dies zu beheben, besteht darin , Verzögerungen in der Schleife wie folgt hinzuzufügen :

initial begin
    reset = 1;
    #10
    reset = 0;
    #10
    for(i = 0; i < maxValue; i = i + 1) begin
        in = i;
        #5 clk = 1;
        #5 clk = 0;
        #5 clk = 1;
        #5 clk = 0;
        $display($time,, "%b : %b", in, out);
    end
    $finish;
end

Das druckt:

          40 0000 : 000
          60 0001 : 000
          80 0010 : 010
         100 0011 : 101
         120 0100 : 010
         140 0101 : 010
         160 0110 : 000
         180 0111 : 000
         200 1000 : 000
         220 1001 : 000
         240 1010 : 000
         260 1011 : 000
         280 1100 : 000
         300 1101 : 000
         320 1110 : 000
         340 1111 : 000

outWie Sie sehen können, ändern sich die Werte der Simulationszeit und und outhaben keine Unbekannten mehr ( x).


Hier ist eine andere Möglichkeit, den Testbench-Code zu schreiben. Es verwendet den traditionelleren alwaysBlock für die Uhr, die Sie zum Laufen bringen wollten. Es stellt auch sicher, dass der Eingang mit der Uhr synchron ist.

initial begin
    $monitor($time,, "%b : %b", in, out);
    reset = 1;
    #10
    reset = 0;
    #10
    for (i = 0; i < maxValue; i = i + 1) begin
        @(posedge clk) in <= i;
    end
    $finish;
end

always #10 clk = ~clk;