// 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