{josuah.net} | {panoramix-labs.fr} (DIR) • {josuah.net} (DIR) • {panoramix-labs.fr} {git} | {cv} | {links} | {quotes} | {ascii} | {tgtimes} | {gopher} | {mail} (DIR) • {git} (BIN) • {cv} (DIR) • {links} (DIR) • {quotes} (DIR) • {ascii} (HTM) • {tgtimes} (DIR) • {gopher} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Interface in Open-Source SystemVerilog Synthesis ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ Verilog lacks a way to build complex data types, akin to C's `struct`. (HTM) SystemVerilog introduces a {`struct`} feature, but it cannot declare `input`s and `output`s. (HTM) SystemVerilog introduces an {`interface`} feature for use in module ports, (HTM) along with {`modport`}. It simplify connections between modules, that do not need to declare the signals one by one anymore. For instance for bundling together the many signals of • a peripheral on a bus (like Wishbone, AXI, PCIe...), • an external protocol (like SPI, UART, I²C, DDR memories, ...), • interrupts lines, • DMA ports, • clock domain crossing, Declaring an interface ────────────────────── The syntax follows the same as the `module` keyword: ┊ interface bus_if; ┊ logic req; ┊ logic ack; ┊ logic[7:0] data; ┊ endinterface There is an implicit typedef in interface declaration: `bus_if` then becomes a type that you can use instead of `logic`, `wire`, `reg`, etc. Using the interface ─────────────────── The type can be used anywhere You can also access the interface individual signals with the dot notation as below. ┊ module lighter ( ┊ input logic clk, ┊ bus_if bus, // the interface is passed like any signal ┊ output logic[2:0] rgb_leds ┊ ); ┊ assign bus.ack = bus.req; ┊ ┊ always_ff @(posedge clk) begin ┊ if (bus.req) begin ┊ rgb_leds <= bus.data[2:0]; ┊ end ┊ end ┊ endmodule Instanciating a verilog module goes by calling its name and filling all signals. Interfaces are passed in the list of signals as if it was a regular signal. For that, the interface must be instantiated separately: ┊ module top ( ┊ input logic clk ┊ ); ┊ bus_if bus (); ┊ // at this point, "bus" is an interface with empty signals, ┊ // each signal has to be integrated in the parent module as ┊ // needed ┊ ┊ mBusController controller ( ┊ .clk(clk), ┊ .bus(bus) // writes to "req" and "data", reads from "ack" ┊ ); ┊ ┊ lighter peripheral1 ( ┊ .clk(clk), ┊ .bus(bus) // reads from "req" and "data", writes to "ack" ┊ ); ┊ endmodule Note that input or output directions are not described, which is the role of modport. Declaring modports ────────────────── Modport specify the input or output direction of interfaces signals. Taking the analogy of a physical connector, modport permits to differentiate a male and female plug, give a polarity to the signals. ┊ interface bus_if; ┊ // the list of signals are preserved even with modport ┊ logic req; ┊ logic ack; ┊ logic[7:0] data; ┊ ┊ modport mpController ( ┊ output req, ┊ output data, ┊ input ack ┊ ); ┊ ┊ modport mpPeripheral ( ┊ input req, ┊ input data, ┊ output ack ┊ ); ┊ endinterface Then we have the choice to pass the entire interface to modules, or to pass only a modport that restricts the direction for extra safety: ┊ module lighter ( ┊ input logic clk, ┊ bus_if.mpPeripheral bus, // the modport is passed instead ┊ output logic[2:0] rgb_leds ┊ ); ┊ assign bus.ack = bus.req; // no syntax change here ┊ [...] ┊ endmodule ┊ ┊ module top ( ┊ input logic clk ┊ ); ┊ bus_if bus (); // instantiate the interface with all its modports ┊ ┊ always @(posedge clk) begin ┊ if (bus.req) begin // accessing the interface signals directly ┊ bus.mpController.req <= 1; // or through the modport ┊ end ┊ end ┊ ┊ lighter peripheral1 ( ┊ .clk(clk), ┊ .bus(bus.mpPeripheral) // passing the modport for safety ┊ ); ┊ endmodule Integrating external signals ──────────────────────────── Interfaces can declare signals that always have the same direction. This is a convenience for clock and resets signals: ┊ interface bus_if ( ┊ input clk, // declared as input here, they will be input ┊ input rst // signals everywhere in the modport ┊ ); ┊ logic req; ┊ logic ack; ┊ logic[7:0] data; ┊ ┊ modport mpController ( ┊ input clk, // they also need to be declared here for them ┊ input rst, // to be reachable through the modport ┊ output req, ┊ output data, ┊ input ack ┊ ); ┊ ┊ modport mpPeripheral ( ┊ input clk, // to be declared in every modport they need to be ┊ input rst, // used ┊ input req, ┊ input data, ┊ output ack ┊ ); ┊ endinterface Integrating parameters ────────────────────── Like for modules, interfaces can have parameters, such as the size of some signals: ┊ interface bus_if #( ┊ parameter pDataSize = 8 // this parameter is optional ┊ ) ( ┊ input clk // external signals list are next, like for modules ┊ ); ┊ logic[pDataSize-:0] data; // note the use ┊ ... ┊ endinterface ┊ ┊ module top ( ... ); ┊ bus_if #( pDataSize = 8 ) bus; // same syntax as modules ┊ ... ┊ endmodule Complete example ──────────────── `bus_if.sv`: ┊ interface bus_if ( ┊ input logic clk ┊ ); ┊ logic req; ┊ ┊ modport mpController ( ┊ input clk, ┊ output req ┊ ); ┊ ┊ modport mpPeripheral ( ┊ input clk, ┊ input req ┊ ); ┊ endinterface `lighter.sv`: ┊ module lighter ( ┊ bus_if.mpPeripheral bus, ┊ output logic led ┊ ); ┊ always_ff @(posedge bus.clk) begin ┊ if (bus.req) begin ┊ led <= 1; ┊ end ┊ end ┊ endmodule `top.sv`: ┊ module top ( ┊ input logic clk, ┊ output logic led // led ┊ ); ┊ ┊ // instantiate the bus interface ┊ bus_if bus ( .clk(clk) ); ┊ ┊ // default value for simulation only ┊ initial begin ┊ bus.req = 0; ┊ end ┊ ┊ // issue a request over the bus ┊ always_ff @(posedge clk) begin ┊ // set a toggle train for demo ┊ bus.req <= !bus.req; ┊ end ┊ ┊ // instantiate the module ┊ lighter peripheral0 ( ┊ .bus(bus.mpPeripheral), ┊ .led(led) ┊ ); ┊ endmodule `mSynthesis.sv`: ┊ module mSynthesis ( ┊ output logic gpio_1 ┊ ); ┊ // Lattice way to setup the clock ┊ SB_HFOSC hfosc ( .CLKHFPU(1'b1), .CLKHFEN(1'b1), .CLKHF(clk) ); ┊ ┊ // instantiate the top module and bind the GPIO pins to it ┊ top top ( ┊ .clk(clk), ┊ .led(gpio_1) ┊ ); ┊ endmodule `simulation.cpp` ┊ #include "verilated.h" ┊ #include "verilated_vcd_c.h" ┊ #include "Vtop.h" ┊ ┊ int ┊ main(int argc, char **argv) ┊ { ┊ Verilated::commandArgs(argc, argv); ┊ Verilated::traceEverOn(true); ┊ ┊ Vtop *vsim = new Vtop; ┊ VerilatedVcdC *vcd = new VerilatedVcdC; ┊ ┊ vsim->trace(vcd, 99); ┊ vcd->open("simulation.vcd"); ┊ ┊ vsim->eval(); ┊ vcd->dump(0); ┊ ┊ for (unsigned long long ns = 0; ns < 1000;) { ┊ vsim->clk = 1; ┊ vsim->eval(); ┊ vcd->dump(ns += 100); ┊ ┊ vsim->clk = 0; ┊ vsim->eval(); ┊ vcd->dump(ns += 100); ┊ } ┊ ┊ vcd->flush(); ┊ } When calling Verilator: ┊ $ verilator --version ┊ Verilator 4.224 2022-06-19 rev v4.224 ┊ ┊ $ verilator -Wall --trace --sv -cc --top-module top top.sv lighter.sv bus_if.sv ┊ ┊ $ make -C obj_dir -f Vtop.mk ┊ gmake: Entering directory '/home/josuah/example/obj_dir' ┊ /usr/bin/perl /usr/local/share/verilator/bin/verilator_includer -DVL_INCLUDE_OPT=include Vtop.cpp Vtop.cpp Vtop.cpp Vtop.cpp Vtop.cpp Vtop.cpp Vtop.cpp Vtop.cpp Vtop.cpp Vtop.cpp Vtop.cpp > Vtop.cpp ┊ c++ -I. -MMD -I/usr/local/share/verilator/include -I/usr/local/share/verilator/include/vltstd -DVM_COVERAGE=0 -DVM_SC=0 -DVM_TRACE=1 -DVM_TRACE_FST=0 -DVM_TRACE_VCD=1 -faligned-new -fbracket-depth=4096 -fcf-protection=none -Qunused-arguments -Wno-bool-operation -Wno-tautological-bitwise-compare -Wno-parentheses-equality -Wno-sign-compare -Wno-uninitialized -Wno-unused-but-set-variable -Wno-unused-parameter -Wno-unused-variable -Wno-shadow -std=gnu++14 -Os -c -o Vtop.o Vtop.cpp ┊ echo "" > Vtop.verilator_deplist.tmp ┊ Archive ar -rcs Vtop.a Vtop.o ┊ rm Vtop.verilator_deplist.tmp ┊ gmake: Leaving directory '/home/josuah/example/obj_dir' ┊ ┊ $ VERILATOR_INC=/usr/local/share/verilator/include ┊ ┊ $ c++ -I${VERILATOR_INC} -Iobj_dir -o simulation.elf simulation.cpp ${VERILATOR_INC}/verilated.cpp ${VERILATOR_INC}/verilated_vcd_c.cpp obj_dir/Vtop.a ┊ ┊ $ ./simulation.elf ┊ ┊ $ gtkwave simulation.vcd When calling yosys ┊ $ yosys --version ┊ Yosys 0.9+4081 (git sha1 UNKNOWN, c++ 13.0.0 -O2 -fPIC -Os) ┊ ┊ $ yosys -p "read_verilog -sv mSynthesis.sv top.sv lighter.sv bus_if.sv; synth_ice40 -top mSynthesis -json synthesis.json" ┊ [a lot of output from yosys] Still (2022-07-06) not support from Icarus Verilog: ┊ $ iverilog -g2012 bus_if.sv lighter.sv mSynthesis.sv top.sv ┊ lighter.sv:2: syntax error ┊ lighter.sv:2: Errors in port declarations. Debugging interface syntax ────────────────────────── Using SystemVerilog constructs might require to add a command-line flag like `--sv`. (HTM) Yosys {does not like} `default_nettype none` along with interfaces, you will have to disable it if you want to use interfaces, until the feature is implemented. Discussion ────────── SystemVerilog interfaces are fairly well supported by recent versions of Verilator (increasingly through 3.x and 4.x) and Yosys (I tested with 0.9+4081). (HTM) {Slightly less} with Icarus Verilog (iverilog). Some features might not be available on all versions, and observing the mailing list and bugtrackers of both project would help checking if a bug is from misuse of interfaces, or lack of support by the upstream tool. In every situation discussing about interfaces, the use-case was bundling these numerous master/slave bus signals present at the top of every module. Without `struct` or `interface`, these signals have to be written one by one on every module declaration or instantiation, and this takes-up a lot of visual space distracting from the real content. Interfaces was being discouraged by {LowRISC coding style} for a {bug encountered in 2018} (HTM) • {LowRISC coding style} (HTM) • {bug encountered in 2018} n one power-domain checking tool. ┊ This may have gotten a lot better since. -- `@tjaychen` in June 2021 This at least works fairly well for the synthesis and simulation path today! Links ───── (HTM) {https://www.chipverify.com/systemverilog/systemverilog-struct} (HTM) {https://www.chipverify.com/systemverilog/systemverilog-interface} (HTM) {https://www.chipverify.com/systemverilog/systemverilog-modport} (HTM) {https://www.bilibili.com/video/BV1uS4y1z7JN} (video) (HTM) {https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md}