Skip to content

SystemVerilog · Module 8

Modport — Restricting Port Directions

Per-role direction control on interface signals, master/slave/monitor modports, task imports, and structural enforcement of bus role contracts.

Module 8 · Page 8.2

How modport gives each module a correctly directed view of a shared interface — enforcing who can drive and who can only read each signal, at compile time. This page covers the problem modport solves, syntax and anatomy, three ways to apply a modport, importing protocol tasks, multi-modport AXI-Lite design, UVM driver/monitor patterns, elaboration-time direction checking, five real debugging labs, twelve interview questions, and ten production best practices.

The Problem — Interfaces Without Directions

In page 8.1 you saw how an interface bundles all APB signals into a single named connection. But there is a fundamental issue: inside an interface, all signals are declared as plain logic — with no direction. By default, any module connected to the interface can drive any signal.

That means nothing stops the slave from accidentally driving paddr (which belongs to the master), or the master from driving prdata (which belongs to the slave). The simulator will not catch this — you will get a multiple-driver conflict that produces X's and takes hours to track down.

SystemVerilog — dangerous: no direction enforcement
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// Without modport: slave can accidentally drive master signals
module apb_slave (apb_if bus);   // generic interface — no direction enforcement
    always_ff @(posedge bus.pclk) begin
        bus.pready  <= 1;
        bus.prdata  <= 32'hBEEF_CAFE;
 
        // BUG: nothing prevents the slave from writing these master signals!
        bus.paddr   <= 32'h0;    // ← should be impossible — no error!
        bus.pwrite  <= 0;        // ← same — multiple drivers → X
    end
endmodule

Modport fixes this. It gives each module a named, direction-restricted view of the interface. The slave module that uses apb_if.slave physically cannot drive paddr — the compiler rejects it as a direction violation.

What is modport?

modport stands for module port. It is a named subset declaration inside an interface that specifies, from one particular module's perspective, which signals are inputs, which are outputs, and which tasks/functions are accessible. One interface can have many modports — one per participant in the protocol.

The master modport gives the master module output access to paddr, psel, penable, pwrite, pwdata and input access to prdata, pready, pslverr. The slave modport is the exact mirror view: input access to paddr, psel, penable, pwrite, pwdata, and output access to prdata, pready, pslverr. The monitor modport gives input-only access to every signal — read everything, drive nothing. Each direction is enforced at compile time.

Declaring a modport — Syntax & Anatomy

SystemVerilog — annotated modport declaration
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
interface apb_if (input logic pclk, presetn);
 
    logic [31:0] paddr, pwdata, prdata;
    logic        psel, penable, pwrite, pready, pslverr;
 
    // ①   ②        ③      ④              ⑤
    modport master (input  pclk, presetn, prdata, pready, pslverr,
                    output paddr, psel, penable, pwrite, pwdata);
 
    modport slave  (input  pclk, presetn, paddr, psel, penable, pwrite, pwdata,
                    output prdata, pready, pslverr);
 
    // Monitor sees everything as input — never drives
    modport monitor (input pclk, presetn, paddr, psel, penable, pwrite,
                              pwdata, prdata, pready, pslverr);
 
endinterface
  1. modport keyword — declares a named, direction-restricted view. Multiple modports can exist in one interface — one per role in the protocol.
  2. master — the modport name. Used when connecting a module: apb_if.master. Pick meaningful names that match roles: master/slave, driver/monitor, source/sink, producer/consumer.
  3. input — signals this module can only read. Attempting to drive an input-declared signal from this module causes a compile error — exactly what we want.
  4. pclk, presetn, prdata, pready, pslverr — the specific signals the master receives. Note: clock and reset are always input to every module.
  5. output — signals this module can drive. The simulator treats these as the authoritative source for these signals. Two modules both declared as output drivers of the same signal will cause a multiple-driver conflict.

Using modport in a Module — Three Ways

There are three places where the modport can be specified. All three are legal. The first two are most common.

SystemVerilog — three ways to apply a modport
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ─── Way 1: In the module port declaration (most explicit) ────────────
// The modport is locked in at the module definition level.
// This module will ALWAYS use the master view, regardless of how it's instantiated.
module apb_master (apb_if.master bus);
    // bus.paddr  ← output — can drive
    // bus.prdata ← input  — can only read
    always_ff @(posedge bus.pclk) bus.paddr <= 32'h4000_0000;
endmodule
 
 
// ─── Way 2: At instantiation in the parent (flexible) ─────────────────
// The module port is a generic interface type, modport chosen at connect time.
module apb_master (apb_if bus);   // no modport here
    always_ff @(posedge bus.pclk) bus.paddr <= 32'h4000_0000;
endmodule
 
// In TB top: modport specified when connecting
apb_if apb (.pclk(clk), .presetn(rst_n));
apb_master u_mst (.bus(apb.master));   // ← modport applied at connection
apb_slave  u_slv (.bus(apb.slave));    // ← same interface, different modport
 
 
// ─── Way 3: No modport — full interface (monitor use case) ────────────
// No direction enforcement — the module can read all signals freely.
// Use only for monitors/checkers that observe but never drive.
module apb_monitor (apb_if bus);
    always @(posedge bus.pclk)
        if (bus.psel && bus.penable)
            $display("APB: addr=%h data=%h wr=%b",
                      bus.paddr, bus.pwrite?bus.pwdata:bus.prdata, bus.pwrite);
endmodule

modport with import — Exposing Tasks

A modport can also make specific tasks or functions from the interface accessible to the module — using the import keyword inside the modport declaration. Without this, a module connected via a modport cannot call tasks defined in the interface.

SystemVerilog — modport with import task
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
interface apb_if (input logic pclk, presetn);
    logic [31:0] paddr, pwdata, prdata;
    logic        psel, penable, pwrite, pready, pslverr;
 
    // ── Protocol tasks defined once, used by any driver ─────────────
    task automatic apb_write(
        input logic [31:0] addr, data
    );
        @(posedge pclk);  #1;
        paddr   = addr;  pwdata = data;
        psel    = 1;    pwrite = 1;
        @(posedge pclk);  #1;
        penable = 1;
        @(posedge pclk iff pready);
        psel = 0; penable = 0; pwrite = 0;
    endtask
 
    task automatic apb_read(
        input  logic [31:0] addr,
        output logic [31:0] data
    );
        @(posedge pclk);  #1;
        paddr  = addr; psel = 1; pwrite = 0;
        @(posedge pclk);  #1;
        penable = 1;
        @(posedge pclk iff pready);
        data = prdata;
        psel = 0; penable = 0;
    endtask
 
    // ── modport: signals + imported tasks ────────────────────────────
    modport master (
        input  pclk, presetn, prdata, pready, pslverr,
        output paddr, psel, penable, pwrite, pwdata,
        import apb_write,     // ← task accessible through this modport
        import apb_read        // ← tasks not listed here are NOT accessible
    );
 
    // Monitor: only reads, no task access
    modport monitor (
        input pclk, presetn, paddr, psel, penable,
                pwrite, pwdata, prdata, pready, pslverr
    );
 
endinterface
 
 
// ── Driver module: calls tasks via the modport ─────────────────────────
module apb_driver (apb_if.master bus);
    initial begin
        // Clean, readable protocol calls — no low-level signal juggling
        bus.apb_write(32'h4000_0000, 32'h1234_5678);
        bus.apb_write(32'h4000_0004, 32'h9ABC_DEF0);
 
        logic [31:0] rd_data;
        bus.apb_read(32'h4000_0000, rd_data);
        $display("Read back: 0x%0h", rd_data);
    end
endmodule

Multiple Modports — AXI-Lite Example

A realistic bus interface needs modports for every participant in the protocol. Here is an AXI-Lite interface with modports for the master, slave, and monitor — showing how one interface definition supports an entire bus ecosystem.

SystemVerilog — AXI-Lite interface with three modports
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
interface axi_lite_if #(parameter DW = 32, AW = 32)
    (input logic aclk, aresetn);
 
    // Write address channel
    logic [AW-1:0] awaddr;
    logic           awvalid, awready;
 
    // Write data channel
    logic [DW-1:0] wdata;
    logic [DW/8-1:0] wstrb;
    logic           wvalid, wready;
 
    // Write response channel
    logic [1:0]    bresp;
    logic           bvalid, bready;
 
    // Read address channel
    logic [AW-1:0] araddr;
    logic           arvalid, arready;
 
    // Read data channel
    logic [DW-1:0] rdata;
    logic [1:0]    rresp;
    logic           rvalid, rready;
 
    // ── Master modport: initiates transactions ──────────────────────
    modport master (
        input  aclk, aresetn,
        input  awready, wready, bvalid, bresp, arready, rvalid, rdata, rresp,
        output awaddr, awvalid, wdata, wstrb, wvalid, bready,
               araddr, arvalid, rready
    );
 
    // ── Slave modport: responds to transactions ─────────────────────
    modport slave (
        input  aclk, aresetn,
        input  awaddr, awvalid, wdata, wstrb, wvalid, bready,
               araddr, arvalid, rready,
        output awready, wready, bvalid, bresp, arready, rvalid, rdata, rresp
    );
 
    // ── Monitor modport: observes only ──────────────────────────────
    modport monitor (
        input  aclk, aresetn,
        input  awaddr, awvalid, awready,
               wdata, wstrb, wvalid, wready,
               bresp, bvalid, bready,
               araddr, arvalid, arready,
               rdata, rresp, rvalid, rready
    );
 
endinterface

modport Direction Keywords — Full Reference

DirectionMeaningUse when
inputModule reads this signal — cannot drive itSignals driven by the other side of the bus (e.g., prdata in the master modport)
outputModule drives this signal — is the sole driverSignals this module is responsible for (e.g., paddr in the master modport)
inoutModule can both drive and read (tri-state)Bidirectional buses (I2C SDA, tri-state data buses)
refPass by reference — no direction enforcedTask arguments that need to modify the signal value; not commonly used in synthesisable code
import task/funcMakes an interface task callable through this modportProtocol helper tasks that should be accessible from the module using this modport
export task/funcModule provides a task that the interface can callRare — callback mechanisms where the interface calls back into the module

Common Mistakes & How to Fix Them

SystemVerilog — modport mistakes and fixes
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ════ MISTAKE 1: Forgetting to add clock/reset to every modport ═══
// ❌ Missing pclk and presetn
modport master (output paddr, psel, penable, pwrite, pwdata,
                input  prdata, pready);
// Master module's always @(posedge bus.pclk) → "signal not in modport" error
// ✅ FIX: always include clock/reset as input in every modport
modport master (input  pclk, presetn, prdata, pready,
                output paddr, psel, penable, pwrite, pwdata);
 
 
// ════ MISTAKE 2: Same signal as output in two modports — multi-driver ═══
// ❌ Both master and slave claim 'paddr' as output → multi-driver X
modport master (output paddr, psel, penable, ...);
modport slave  (output paddr, prdata, pready);    // ← paddr again!
// ✅ FIX: exactly one modport per signal as output; all others input
modport master (output paddr, psel, penable, pwrite, pwdata,
                input  prdata, pready);
modport slave  (input  paddr, psel, penable, pwrite, pwdata,
                output prdata, pready);
 
 
// ════ MISTAKE 3: Calling interface tasks without importing them ═══
// ❌ Modport has no import — task call is rejected
modport master (input pclk, output paddr, psel, ...);  // no import!
module drv (apb_if.master bus);
    initial bus.apb_write(addr, data);    // ← compile error
endmodule
// ✅ FIX: import every task the module needs to call
modport master (input pclk, output paddr, psel, ...,
                import apb_write, import apb_read);
 
 
// ════ MISTAKE 4: Omitting a signal from a modport — invisible ═══
// ❌ 'pslverr' missing from master modport
modport master (input pclk, prdata, pready,            // ← pslverr missing
                output paddr, psel, penable, pwrite, pwdata);
// Master tries: if (bus.pslverr) ... → "signal not visible" error
// ✅ FIX: every signal the module accesses must be listed in its modport
modport master (input pclk, prdata, pready, pslverr,
                output paddr, psel, penable, pwrite, pwdata);

Quick Reference — modport at a Glance

SystemVerilog — modport quick reference
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// ── Declare modports inside interface ────────────────────────────────
interface my_if (input logic clk, rst_n);
    logic [7:0] data;
    logic        valid, ready;
 
    task automatic send(input logic [7:0] d); /*...*/ endtask
 
    modport source  (input  clk, rst_n, ready,
                     output data, valid,
                     import send);          // task accessible here
 
    modport sink    (input  clk, rst_n, data, valid,
                     output ready);
 
    modport monitor (input  clk, rst_n, data, valid, ready);
endinterface
 
// ── Way 1: modport in module port declaration ─────────────────────────
module producer (my_if.source intf);  // direction enforced at definition
 
// ── Way 2: modport at instantiation ──────────────────────────────────
module producer (my_if intf);         // generic — modport applied when connected
my_if bus (.clk(clk), .rst_n(rst_n));
producer u (.intf(bus.source));       // modport chosen at instantiation
 
// ── Compile-time enforcement ──────────────────────────────────────────
// Inside producer (using source modport):
intf.data  = 8'hAB;   // ✓  valid — data is output in source modport
intf.ready = 1;       // ✗  ERROR: ready is input in source modport
intf.send(8'h42);     // ✓  valid — send is imported in source modport

Verification Usage — Modport Role in UVM Components

Every UVM component that touches the bus picks the modport that matches its role. The driver attaches via the master (or slave) modport — it drives the bus. The monitor attaches via the monitor modport — it observes without ever being able to drive. The scoreboard never holds a virtual interface directly; it receives transactions from the monitor. This three-tier separation is enforced at elaboration by the modport directions; no test can accidentally turn the monitor into a driver.

SystemVerilog — UVM driver and monitor on the same interface, different modports
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
interface apb_if(input logic clk);
    logic [31:0] paddr, pwdata, prdata;
    logic        psel, penable, pwrite, pready;
 
    modport master  (output paddr, pwdata, pwrite, psel, penable,
                       input  prdata, pready);
 
    modport slave   (input  paddr, pwdata, pwrite, psel, penable,
                       output prdata, pready);
 
    modport monitor (input  paddr, pwdata, pwrite, psel, penable,
                       input  prdata, pready);
endinterface
 
// ── UVM driver — uses master modport ──────────────────────────
class apb_driver extends uvm_driver;
    virtual apb_if.master vif;   // ← role baked into type
 
    virtual function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        if (!uvm_config_db#(virtual apb_if.master)::get(
                this, "", "vif", vif))
            `uvm_fatal("NOVIF", "No master vif")
    endfunction
 
    task run_phase(uvm_phase phase);
        forever begin
            apb_xact tr; seq_item_port.get_next_item(tr);
            vif.paddr  <= tr.paddr;       // ✓ master can drive paddr
            vif.pwdata <= tr.pwdata;
            vif.psel   <= 1'b1;
            // vif.prdata = ...    ✗ would fail at elaboration
            @(posedge vif.clk);
            seq_item_port.item_done();
        end
    endtask
endclass
 
// ── UVM monitor — uses monitor modport ───────────────────────
class apb_monitor extends uvm_monitor;
    virtual apb_if.monitor vif;    // ← cannot drive ANY signal
 
    task run_phase(uvm_phase phase);
        forever begin
            @(posedge vif.clk);
            if (vif.psel && vif.penable && vif.pready) begin
                apb_xact obs = apb_xact::type_id::create("obs");
                obs.paddr  = vif.paddr;     // ✓ monitor can read everything
                obs.pwdata = vif.pwdata;
                // vif.paddr = ...   ✗ would fail at elaboration
                ap.write(obs);
            end
        end
    endtask
endclass

Simulation Behavior — How Modports Resolve at Elaboration

The elaborator processes each modport connection by walking every signal reference inside the connected module and checking it against the modport's declared direction. The check is structural: a connected module declared as modport master may write only signals listed as output or inout in the master modport. Any violation fails elaboration with a specific error pointing to the offending line.

SystemVerilog — What the elaborator catches at compile time
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
interface bus_if;
    logic [7:0] data;
    logic       valid, ready;
 
    modport producer(output data, valid, input  ready);
    modport consumer(input  data, valid, output ready);
endinterface
 
module producer_block(bus_if.producer bus);
    always_ff @(posedge clk) begin
        bus.data  <= next_data;   // ✓ data is output in producer
        bus.valid <= 1'b1;        // ✓ valid is output in producer
        // bus.ready = 1'b0;     ✗ ERROR: ready is input in producer
        last_seen <= bus.ready;   // ✓ producer may READ ready
    end
endmodule
 
// Elaboration output (Questa/VCS/Xcelium produce the same kind of error):
//   Error: producer_block.sv:7: 'bus.ready' is declared as 'input' in
//          modport 'producer' of interface 'bus_if'.  Cannot assign.

Waveform Analysis — Identifying Modport Connections in Verdi/DVE

Modport restrictions don't appear in the waveform — the signals are normal signals once elaborated. But the connection topology is visible: opening a module's instance in Verdi shows the interface port labelled with its modport name (e.g., apb.master), making it instantly clear which role the block plays. This matters for debug: when a bus signal is misbehaving, you can confirm the modport role at a glance and rule out wrong-role wiring as a cause.

Expected waveform — APB handshake with modport-restricted access
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
cycle            0    1    2    3    4    5
 
clk            _│‾│_│‾│_│‾│_│‾│_│‾│_│‾│_
 
DRIVER side (apb.master modport — outputs paddr,pwdata,psel,penable; reads pready):
  u_drv.paddr     --   100  100  100  --   --     ← driven by master
  u_drv.pwdata    --   AA   AA   AA   --   --     ← driven by master
  u_drv.psel      0    1    1    1    0    0     ← driven by master
  u_drv.penable   0    0    1    1    0    0     ← driven by master
 
SLAVE side  (apb.slave  modport — reads paddr,pwdata,psel,penable; outputs pready,prdata):
  u_dut.paddr     --   100  100  100  --   --     ← READ by slave (same wire)
  u_dut.pready    0    0    0    1    0    0     ← driven by slave
 
MONITOR side (apb.monitor — all input):
  u_mon.paddr     --   100  100  100  --   --     ← READ-only visibility
  u_mon.psel      0    1    1    1    0    0     ← READ-only visibility
 
The waveform shows the same signal values everywhere (paddr=100 across cycles 1-3
on driver/slave/monitor) — modport doesn't duplicate signals, it only restricts
WHO can write them. The monitor sees everything but couldn't even attempt to drive
without elaboration failure.

Industry Insights — How Senior Teams Use Modports

Debugging Academy — 5 Real Modport Bugs

Each lab is a real failure mode pulled from production projects. Buggy code, symptom, root cause, fix.

1

Driver Wired Through monitor Modport — All Writes Fail Silently

WRONG ROLE
Buggy Code
Driver attached via .monitor (or config_db published as monitor)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
interface apb_if(input logic clk);
    logic [31:0] paddr, pwdata;
    logic        psel, penable;
 
    modport master  (output paddr, pwdata, psel, penable);
    modport monitor (input  paddr, pwdata, psel, penable);
endinterface
 
// In TB top — wrong modport assigned to driver:
apb_if u_apb(.clk);
apb_driver u_drv(.apb(u_apb.monitor));   // ← should be .master!
 
// Or in UVM:
uvm_config_db#(virtual apb_if.monitor)::set(  // ← wrong type!
    null, "uvm_test_top.env.agent.driver", "vif", u_apb);
Symptom

Elaboration fails with an error like "variable 'paddr' is read-only in modport 'monitor'". If the driver was already written with vif.paddr <= tr.paddr, the elaborator points directly at the offending line. If the configuration lookup is by type, the config_db::get returns false because the published type (virtual apb_if.monitor) doesn't match the requested type (virtual apb_if.master) — the driver gets a null vif and `uvm_fatals at build_phase.

Root Cause

The TB wired the driver as if it were a monitor. The bug is either at instantiation (passing the wrong modport on the port connection) or in the config_db set/get pair (publishing one modport-typed handle and requesting another).

Fix

Both the port connection and the config_db type must match the role. Driver: .apb(u_apb.master) on the instantiation, virtual apb_if.master on the config_db set/get. Monitor: .apb(u_apb.monitor) and virtual apb_if.monitor. Enforce with a code-review checklist: every TB component declares its modport role next to its port; every config_db lookup uses the matching type.

2

Two Masters on the Same Interface — Multi-Driver X

DUPLICATE ROLE
Buggy Code
Two drivers, both wired to .master
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
apb_if u_apb(.clk);
 
apb_driver u_drv (.apb(u_apb.master));   // master #1
apb_driver u_drv2(.apb(u_apb.master));   // ← second driver, also master!
apb_slave  u_dut (.apb(u_apb.slave));
 
// Both drv and drv2 drive paddr, pwdata, etc.
// Tool may or may not warn — many don't.
Symptom

paddr, pwdata, psel, penable show X in the waveform whenever both drivers are simultaneously active. Tests pass when only one driver is exercising the bus; fail when both fire near the same clock edge.

Root Cause

Modport restricts direction per role, but doesn't enforce "at most one connected module per master role." Two modules legally claim the master role; both drive the same signals; the simulator resolves to X. The language is silent here; lint must catch it.

Fix

Lint rule: at most one connected module per master/slave modport on the same interface instance (Spyglass modport_multi_use). For genuine multi-master buses (e.g., an SoC interconnect crossbar), use separate interface instances per master, then arbitrate explicitly in a bridge module. The "multiple masters on one interface" pattern shouldn't be modelled by wiring two masters to the same interface; it should be modelled by an explicit arbiter.

3

Forgotten Signal in Modport — Module Sees X

MISSING SIGNAL
Buggy Code
perr added to interface, but never added to master modport
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
interface apb_if(input logic clk);
    logic [31:0] paddr, pwdata, prdata;
    logic        psel, penable, pwrite, pready, perr;  // ← perr added
 
    // Modport declared BEFORE perr was added — never updated
    modport master(output paddr, pwdata, pwrite, psel, penable,
                   input  prdata, pready);
    //              ^^^^^ perr missing — master can't see it!
endinterface
 
// Master tries to use the new error signal:
module apb_master(apb_if.master apb);
    always_ff @(posedge apb.clk)
        if (apb.perr) ...                  // ← compile error or X-read
endmodule
Symptom

Compile error: "signal 'perr' not visible through modport 'master'." Engineer adds input perr to the modport, recompiles, problem "solved." But if the modport check is too loose (older tools), the access silently sees X because the signal isn't routed through the modport interface — looks like a runtime X bug.

Root Cause

Adding a signal to the interface requires updating every modport that needs to access it. The error message is clear when the tool catches it, but it's an extra step that engineers sometimes skip — especially when adding a "debug-only" signal that the master "doesn't really need."

Fix

Discipline: every time a signal is added to an interface, every modport declaration in that interface gets reviewed. Some teams use a "wildcard-style" pattern — though SystemVerilog doesn't have one natively, you can declare an "all" modport that lists every signal as inout for internal helper modules; never expose this to integration code, only use it in interface-internal tasks. Best is to keep the signal list small enough that updating modports stays manageable.

4

Imported Task Drives Bus from Wrong Side

TASK BLEED-THROUGH
Buggy Code
do_write imported into BOTH master and slave
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
interface apb_if(input logic clk);
    logic [31:0] paddr, pwdata, prdata;
    logic        psel, penable, pwrite, pready;
 
    // Master-side handshake task — drives the bus
    task automatic do_write(input logic [31:0] a, d);
        @(posedge clk);
        paddr  <= a;    psel  <= 1;
        @(posedge clk); penable <= 1;
        while (!pready) @(posedge clk);
        psel  <= 0; penable <= 0;
    endtask
 
    // ❌ Imported into BOTH modports
    modport master(/*...*/, import do_write);
    modport slave (/*...*/, import do_write);   // ← slave shouldn't have this!
endinterface
 
// Slave code does this:
module apb_slave(apb_if.slave apb);
    always @(error_condition) apb.do_write(32'h0, 32'h0);  // slave drives bus
endmodule
Symptom

Two writers (the real master and the slave's misuse) end up driving the same bus signals concurrently → multi-driver X. The slave's "helpful" initialisation behaviour corrupts every master-initiated transaction near the same time. The symptom looks like random bus glitches; the cause is structural.

Root Cause

The handshake task was imported into both modports as a convenience. A slave-side developer used it because it was available. The interface didn't stop them — the import allowed the call; the modport directions allowed the task body's writes (because the task itself runs in the interface's scope, not the caller's modport context).

Fix

Import each task into only the modport that owns the role it encodes. do_write is a master-side action; import only into master. If the slave needs a response-driving helper, write a separate do_respond task and import only into slave. Lint rule: "modport task import must match the modport's owned role" (custom rule; most production teams add this).

5

Modport Mismatch Between TB Top and UVM Component Type

CONFIG_DB DRIFT
Buggy Code
TB publishes bare type; driver looks up master-qualified type
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// TB top
apb_if u_apb(.clk);
initial begin
    // Published as the BASE interface type
    uvm_config_db#(virtual apb_if)::set(null, "*", "vif", u_apb);
    run_test();
end
 
// Driver expects the MASTER modport type:
class apb_driver extends uvm_driver;
    virtual apb_if.master vif;
 
    virtual function void build_phase(uvm_phase phase);
        if (!uvm_config_db#(virtual apb_if.master)::get(  // ← mismatch
                this, "", "vif", vif))
            `uvm_fatal("NOVIF", "No master vif found")
    endfunction
endclass
Symptom

Driver's build_phase calls `uvm_fatal("NOVIF", "No master vif found"). Engineer assumes something is wrong with the interface instantiation or the config_db scope string, spends an hour grepping for typos before noticing the type mismatch.

Root Cause

config_db is type-strict. Publishing as virtual apb_if and looking up as virtual apb_if.master are different types; the lookup fails. The same handle could be accessed via the master modport, but config_db doesn't perform that conversion.

Fix

Publish the handle with the exact modport-qualified type the consumers expect. Either publish per-role (uvm_config_db#(virtual apb_if.master)::set(...), uvm_config_db#(virtual apb_if.monitor)::set(...)), or pick a convention where the TB top always publishes the bare type and every component looks up with the bare type and uses .master/.monitor only at access time. Mixing conventions is the bug; pick one and enforce it.

Interview Q&A — 12 Questions on Modports

Drawn from real interviews. Try to answer before reading each response.

A modport is a named view of an interface that declares the direction of each signal from a particular connected module's perspective. It solves the "anyone can drive anything" problem of a plain interface: without modports, any module connected to the interface can drive any signal, which makes multi-driver bugs trivial to introduce. With modports, the compiler enforces who can read and who can write each signal — wrong-direction access fails at elaboration, not in silicon.

Best Practices — Modport Rules to Walk Away With

  1. Declare modports the moment a second module connects. Don't defer until the multi-driver bug bites.
  2. Every protocol interface has master, slave, and monitor at minimum. Add tb only if backdoor fault injection is needed.
  3. Type virtual interfaces to specific modports in UVM components. virtual apb_if.master vif; — not the bare virtual apb_if.
  4. config_db set and get types must match exactly. Both virtual apb_if.master on the publish and the lookup, or both bare.
  5. Import tasks into only the modport that owns the role they encode. Master-handshake tasks → master only. Never imported into all modports.
  6. Use the monitor modport for every observer. Coverage, assertions, scoreboard — none should hold a modport that can drive.
  7. Lint for "at most one module per master/slave modport instance." Spyglass modport_multi_use or equivalent — multi-driver bugs catch in CI, not silicon.
  8. Document the IP's modport contract in the user guide. Each connected module's required modport and the signal directions on each.
  9. Update every modport when adding a signal to the interface. The new signal needs to be declared in every modport that should access it; review all modports as one transaction.
  10. For runtime role switching, instantiate two modules. SystemVerilog doesn't support dynamic modport rebinding; use explicit mux/control logic between two role-fixed instances.