You are here

System Verilog - Reference

// Literatur:
//    Vorlesungsunterlagen AMV von Rainer Findenig (FH-Hagenberg)
//    Doulos       - http://www.doulos.com/knowhow/sysverilog/
//    ASIC World   - http://www.asic-world.com/systemverilog/tutorial.html
//    ElectroSofts - http://electrosofts.com/systemverilog/
 
// HDVL - Hardware Description and Verification Language
// Erweiterung zu Verilog
 
 
// Definition von Packages
// -> globale bzw. Projektspezifische Definitionen und Parameter
        package global;
            parameter DATA_WIDTH   = 8;
            parameter ADDR_WIDTH   = 8;
            parameter END_ADDRESS  = (2**ADDR_WIDTH) - 1;
 
            parameter DEAKTIVATED  = 0;
            parameter AKTIVATED    = 1;
 
            parameter CLOCK_PERIOD = 10ns;
        endpackage
 
 
// den Namensraum verfuegbar machen
        import global::*;
// Datei includieren
        `include "ram-bh.sv"
 
 
// verschiedene Module (Architekturen)
        module RAM # (
                // Paramter Liste
                parameter int gDataWidth = 32;
                parameter int gAddrWidth = 8
            )
            (
                // Port Liste
                input  logic                    clk_i;
                input  logic                    rst_i;
                input  logic [gDataWidth-1 : 0] dat_i;
                input  logic [gAddrWidth-1 : 0] adr_i;
                input  logic                    we_i;
                output logic [gDataWidth-1 : 0] dat_o
            );
            logic [gDataWidth-1 : 0] storage [2**gAddrWidth-1 : 0];
 
            // Prozess wird zu Begin einmal aufgerufen
            initial begin
                storage[0] = '0;
                storage[1] = '0;
                ...
            end
 
            // Prozess existiert immer und wird bei den Events aufgerufen
            always @(posedge clk_i or rst_i)
            begin
                ...
            end
        endmodule
 
        module top ();
            logic clk = 0;
            logic rst = AKTIVATED;
            ...
 
            initial begin
                #0ns rst = AKTIVATED;
                #1ns rst = DEAKTIVATED;
            end
 
            always #CLOCK_PERIOD clk = ~clk;
 
            // Ram Instantiieren
            RAM # (.gDataWidth (DATA_WIDTH),
                   .gAddrWidth (ADDR_WIDTH)
                  )
                duv(.clk_i (clk),
                    .rst_i (rst),
                    .dat_i (datM),
                    .adr_i (adr),
                    .we_i  (we),
                    .dat_o (datS)
                   );
        endmodule
 
        // eigenes Modul-Konstrukt fuer die Testbench
        program ram_tb ();
            // nur initial begin Bloecke
            initial begin : stimuli
                // koennen aber endlos Schleifen haben
            end : stimuli
        endprogram
 
 
// Basisdatentypen
        // 4-state-logic (0, 1, X, Z)
        reg clk = 0;                    // user-defined size
        logic                           // identical to reg in every way
                                        // logic [7:0] a_byte;
        integer                         // 32 bits, signed
 
        // 2-state-logic (0, 1) -> Simulationszeit verringert sich
        bit  	                        // user-defined size
        byte                            // 8 bits, signed
        shortint                        // 16 bits, signed
        int                             // 32 bits, signed
        longint                         // 64 bits, signed
 
        time                            // 64-bit, unsigned
        shortreal                       // like float in C
        real                            // like double in C
        realtime                        //identical to real
 
 
// Typdefinitionen und Enumerationen
        typedef bit [7 : 0] data_v;
        data_v data;
 
        enum { circle, ellipse, freeform } c;
        enum bit [3:0] {bronze=0, silver, gold} newMedal;
 
        typedef enum {Idel = 0, StartBit, Data, Parity, StopBit} aState;
        aState s = Idle;
        case(s)
            Idle:
            ...
        endcase
 
 
// Arraydefinitionen
        // packed arrays, festgelegte Darstellung als Bitstrom
        bit [7 : 0] data;
 
        // unpacked arrays, Werkzeug kann Abbildung frei waehlen
        bit data [7 : 0];
 
        bit [7 : 0] data_list [4]   // [0:3]
                                    // z.B. data_list[0]  xxxxxxxx 76543210
                                    //      data_list[1]  xxxxxxxx 76543210
                                    //      data_list[2]  xxxxxxxx 76543210
                                    //      data_list[3]  xxxxxxxx 76543210
 
        // dynamische Arrays (nur unpacked, aufsteigend Indiziert)
        bit a1[], a2[];                 // zwei leere Arrays
        a1 = new[10];                   // Array anlegen
        a2 = a1;                        // Array kopieren
        a2 = new[a2.size*2](a2);        // a2 Groesse verdoppeln
        a1 = new[5];                    // neues Array anlegen
                                        // (-> Garbage Collector)
        a1.delete;                      // Array loeschen
 
        // packed <-> unpacked (Bit-stream cast)
        typedef bit [0 : 7] data_v;
        data_v packed;
        bit unpacked [8];
        packed = data_v'(unpacked);     // unpacked -> packed
 
        // packed <-> unpacked (Streaming operators)
        int a[2], b1, b2;
        bit [64:1] x = { >> {a}};       // pack a left-to-right
        bit [63:0] x = { << {a}};       // pack a bit reverse
        bit [0:63] x = { >> {b1, b2}};  // pack b1, b2
 
            { << { 8'b0011_0101 }}      // bit reverse -> 1010_1100
            { << 4 { 6'b11_0101 }}      // 0101_11
            { >> 4 { 6'b11_0101 }}      // 1101_01
 
        // linke Seite kuerzer als rechte: Fehler
        // rechte Seite kuerzer als linke: linksbuendig, mit 0 gefuellt
 
        // Streams nicht direkt als Wert verwendbar -> Cast notwendig
        typedef bit [0 : 7] data_v;
        data_v data;
        bit ret[8];
        assert (data == data_v'({ >> {ret}}))
 
        // Array durchlaufen
        int a[] = new[100];
        foreach (a[i])
            a[i] = i;
 
        a = '0;                         // Array mit 0 fuellen
        a = '1;
        a = 'z;
        a = 'x;
 
 
// Strukturen und Unions (aehnlich zu C)
        struct {
            int x, y;
        } p;
        p.x = 1;
        p = { 1,2 };
 
        typedef struct packed {
            int x, y;
        } Point;
        Point p;
 
 
// Zuweisungen
        // non-blocking Assignment: Zuweisung im naechsten "Delta-Cycle"
        always @(posedge clk) begin
            A <= A + 1;
            B <= A;                     // B bekommt alten Zustand von A
        end
 
        // blocking Assignment: Zuweisung sofort
        always @(posedge clk) begin
            A = A + 1;
            B = A;                      // B bekommt neuen Zustand von A
        end
 
 
// Operatoren
        //Type                  Symbol              Operation
        //  Arithmetik              *                   Multiplizieren
        //                          /                   Dividieren
        //                          +                   Addieren
        //                          -                   Subtrahieren
        //                          %                   Modulo
        //                          +                   unaer Plus
        //                          -                   unaer minus
        //  Logik                   !                   logische Negation
        //                          &&                  logisches Und
        //                          ||                  logisches Oder
        //  Vergleich               >                   groeser
        //                          <                   kleiner
        //                          >=                  groeser gleich
        //                          <=                  kleiner gleich
        //  Gleichheit              ==                  gleich
        //                          !=                  ungleich
        //  Bit                     ~                   bitweise Negation
        //                          ~&                  nand
        //                          |                   or
        //                          ~|                  nor
        //                          ^                   xor
        //                          ^~                  xnor
        //                          ~^                  xnor
        //  Shift                   >>                  right shift
        //                          <<                  left shift
        //  Verkettung              { }                 Concatenation
        //  Bedingung               ?                   Conditional
 
 
// Schleifen und Bedingungen
        // IF
        if (Cond1)
            doSomething1();
        else if (Cond2)
            doSomething2();
        else
            doSomething3();
 
        if (Cond)
        begin
            doSomething1();         // werden mehrere Zeilen benoetigt,
            doSomething2();         // so braucht man einen begin-end-Block
        end
 
        // DO-WHILE
        do
            ...
        while(Cond);
 
        // WHILE
        while (Cond) begin
            ...
        end
 
        // FOR
        for (int i=15, logic j=0; i>0; i--,j=~j)
            ...
 
        // CASE
        case(address)
            0 : $display ("Adresse = 0");
            1 :
            begin
                $display ("Adresse = 1");
                $display ("weitere Zeile");
            end
            default : $display  ("default Zweig");
        endcase
 
        // REPEAT
        repeat (16) begin
            ...
        end
 
 
// Ueberschriften
        begin : label
        end : label
 
        module myModule ...
            ...
        endmodule : myModule
 
        loop : for (...)
 
 
// Ausgaben
        // Syntax aehnlich zu "printf"
        $display("@%t: data = %0d", $time, data);
        $write();
        $time                       // aktuelle Simulationszeit
        $finish                     // Simulation beenden
 
        string sv = "SystemVerilog";
        string s;
        s = {sv, " ", "ist echt einfach."}; 
        $display ("%s\n", s);       // Bildschirmausgabe:
                                    // "SystemVerilog ist echt einfach."
 
 
 
// Assertions
        assert (A == B);            // wenn A!=B wird ein Fehler ausgegeben
        assert (A == B) $display ("OK. A gleich B");
        assert (A == B) $display ("OK. A gleich B");
            else $error("Fehler: A ungleich B");
        assert (A == B) else $error("Fehler: A ungleich B");
 
 
// Timing-Kontrolle
        wait (cond);                // level-sensitive
                                    // -> warte, bis cond wahr ist
        @(posedge clk);             // warte auf steigende Flanke von clk
        repeat (2) @(negedge clk);  // warte 2x auf negative Flanke von clk
        @(sig);                     // warte, bis sich sig aendert
        @(posedge clk, rst)  ==  @(posedge clk or rst)
        #1ns rst = 0;               // 1e ns warten, danach rst auf 0 setzen
        ##1 rst = 0;                // 1en Clock Zyklus warten
        wait(0);                    // endlos Warten
 
 
// Function vs. Task
        // fuer Berechnungen (passieren in Nullzeit)
        function int calcCrc(input int data0 = '0, data1);
            ...
            calcCrc = 0;
            ...
            return crc;             // return ueberschreibt calcCrc Wert
        endfunction
        res = calcCrc(.data0(d0), .data1(d1));
        res = calcCrc(d0, d1);      // Reihenfolge wird beachtet
 
        // koennen Zeit verbrauchen
        task waitForClk(input int data_i, output int data_o);
            ...
            @(posedge clk);
        endtask
 
 
// Clocking Block - beschreibt wie die Eingaenge und Ausgaenge
//                  gesampled und synchronisiert werden
        clocking clock1 @(posedge clk1);
            default input #2ns      // default input skew
                    output #3ns;    // default output skew
            input a1, a2;
            input  #1 a3;           // eigens spezifizierte input skew
            output    b1;
            output #1 b2;           // eigens spezifizierte output skew
        endclocking
 
 
 
 
// Interfaces - fuer gemeinsame Schnittstellen
        interface wishboneBusIf(input logic clk, rst);
            logic ack, stb, cyc, we;
            logic [DATA_WIDTH-1 : 0]   datM, datS;
            logic [ADDR_WIDTH-1 : 0]   adr;
            logic [DATA_WIDTH/8-1 : 0] sel;
 
            clocking cb @(posedge clk, rst);
                input ack, datS;
                output stb, cyc, we, datM, adr, sel;
            endclocking
 
            modport master(clocking cb);
            modport slave(
                input stb,
                input cyc,
                input we,
                input datM,
                input adr,
                input sel,
                output ack,
                output datS
            );
        endinterface
 
        program wishbone_tb (
            wishboneBus_if wishboneBus
        );
            wishboneBus.clk;
            wishboneBus.master.cb.ack;
        endprogram
 
        RAM duv (
            .clk_i      (wishboneBus.clk),
            .rst_i      (wishboneBus.rst),
            .adr_i      (wishboneBus.slave.adr),
            .dat_i      (wishboneBus.slave.datM),
            ...
            .ack_o      (wishboneBus.slave.ack)
        );
 
 
// OOP - Klassen
        class wishboneBFM_c;
            // Interfaces muessen in Klassen immer virtuell sein,
            // damit die Aenderungen nach aussen sichtbar werden
            virtual wishboneBus_if wishboneBus;
            logic [ADDR_WIDTH-1 : 0] actAddr;
            local logic mem1;       // private Members
            protected logic mem2;   // protected Members
 
            // CTor, nur einer erlaubt
            function new(virtual wishboneBus_if wishboneBus);
                this.wishboneBus = wishboneBus;
            endfunction
 
            task busWrite(input logic [ADDR_WIDTH-1 : 0] addr,
                          input logic [DATA_WIDTH-1 : 0] data);
                wishboneBus.cpM.stb <= 0;
                wishboneBus.cpM.cyc <= 0;
 
                @(posedge wishboneBus.clk)
                wishboneBus.cpM.adr  <= addr;
                wishboneBus.cpM.datM <= data;
                wishboneBus.cpM.we   <= 1;
                wishboneBus.cpM.sel  <= '0;
                wishboneBus.cpM.stb  <= 1;
                wishboneBus.cpM.cyc  <= 1;
                do
                    @(posedge wishboneBus.clk);
                while(!wishboneBus.cpM.ack);
                wishboneBus.cpM.stb <= 0;
                wishboneBus.cpM.cyc <= 0;
            endtask : busWrite
 
            function bit [15 : 0] calcCrc;
                ...
                return crc;
            endfunction
        endclass
 
        wishboneBFM_c bfm = new(...);
        bfm.busWrite(...);
        int crc = bfm.calcCrc();
 
 
        // Parametrisierte Klasse
        class #(parameter int N = 1) Register;
            bit [N-1:0] data;
            ...
        endclass
 
        Register #(4) R4;           // data ist bit [3:0]
        Register #(.N(8)) R8        // data ist bit [7:0]
        Register R;                 // data ist bit [0:0]
 
 
        // Ableiten
        class ShiftRegister extends Register;
            task shiftleft;  data = data << 1; endtask
            task shiftright; data = data >> 1; endtask
        endclass
 
 
        // virtuelle Klassen
        virtual class Register;
            ...
        endclass
 
 
// Automatic Storage
        // wird eine Funktion bzw. ein Task mehrmals aufgerufen,
        // so muss die Wiedereintrittsfähigkeit gegeben sein
 
        // in Verilog wurden Parameter statisch allokiert
        // -> mittels automatic Starge auf Stack legen
        program automatic Test(...);
            task automatic doIt();
                ...
            endtask
            ...
        endprogram
 
        // Methoden in Klassen sind immer automatic
 
 
// gleichzeiteige Prozesse
        type_of_block @(sensitivity_list)
        fork
            statement1;             // zwei parallele Statements
            statement2;
            ...
        join
 
        fork
            begin
                statement1;         // 1 Prozess mit zwei Statements
                statement2;
                ...
            end
        join
 
        // join      ... der Eltern-Prozess blockiert solange
        //               bis alle fork-Prozesse fertig sind
        // join_any  ... der Eltern-Prozess blockiert solange
        //               bis ein fork-Prozess fertig ist
        // join_none ... der Eltern-Prozess setzt seine
        //               Abarbeitung ohne Warten fort
 
 
// Events
        program tb (...);
            event e;
 
            initial begin : doIt
                #1ns -> e;
                #2ns -> e;
            end : doIt
 
            initial begin : waitForEvent
                do
                begin
                    wait( executeModellEvent.triggered );
 
                    $display ("@%0t -> executeModellEvent (cmd: %0d)",
                              $time, opcodeCnt+1);
                    #1ns;
                end
                while(1);
            end : waitForEvent
        endprogram
 
        // ->                       ... triggert ein Event
        // ->>                      ... triggert ein Event non-blocking
        @(event);                   // wartet auf ein Event
        wait(event.triggered);      // -,,-
        wait_order(a, b, c);        // wartet auf Events nach bestimmten
                                    // Reihenfolge: zuerst a, dann b, dann c