Modports in SystemVerilog

Modports define different "views" of an interface for different modules. A master sees some signals as outputs while a slave sees the same signals as inputs. Modports enforce these directions and prevent accidental errors.

What Problem Do Modports Solve?

In a bus, the master drives address and write data, while the slave receives them. Without modports, nothing prevents a slave from accidentally driving the address bus!

Think of modports like roles in a phone call. The caller (master) dials the number. The receiver (slave) picks up. Both use the same phone line, but their roles are different. Modports define these roles.

Why Use Modports?

  • Enforce directions - Catch wrong connections at compile time
  • Document intent - Clear which signals go which way
  • Enable checking - Simulation tools can verify correctness
  • Improve reusability - Same interface works for master and slave

Defining Modports

Modports are defined inside the interface using the modport keyword:

Interface with Modports
interface ahb_if (input logic HCLK);
    // Signals
    logic        HRESETn;
    logic [31:0] HADDR;
    logic [1:0]  HTRANS;
    logic        HWRITE;
    logic [2:0]  HSIZE;
    logic [31:0] HWDATA;
    logic [31:0] HRDATA;
    logic        HREADY;
    logic [1:0]  HRESP;
    
    // Master's view - drives address/control, receives data/response
    modport master (
        input  HCLK, HRESETn,
        output HADDR, HTRANS, HWRITE, HSIZE, HWDATA,
        input  HRDATA, HREADY, HRESP
    );
    
    // Slave's view - receives address/control, drives data/response
    modport slave (
        input  HCLK, HRESETn,
        input  HADDR, HTRANS, HWRITE, HSIZE, HWDATA,
        output HRDATA, HREADY, HRESP
    );
    
    // Monitor's view - only observes, never drives
    modport monitor (
        input HCLK, HRESETn,
        input HADDR, HTRANS, HWRITE, HSIZE, HWDATA,
        input HRDATA, HREADY, HRESP
    );
endinterface

Direction is Relative!

input and output are from the module's perspective. What's an output for master is an input for slave.

Using Modports in Modules

When a module connects to an interface, it specifies which modport to use. This restricts its access to only the allowed signals and directions.

Module Using Modport
// Master module - uses master modport
module ahb_master (
    ahb_if.master bus  // Specify modport!
);
    always @(posedge bus.HCLK) begin
        if (!bus.HRESETn) begin
            bus.HTRANS <= 2'b00;  // IDLE
        end else begin
            // Master can write to HADDR (output in master modport)
            bus.HADDR  <= next_addr;
            bus.HTRANS <= 2'b10;  // NONSEQ
            bus.HWRITE <= 1'b1;
            
            // Master can read HREADY (input in master modport)
            if (bus.HREADY) begin
                // Transfer complete
            end
        end
    end
endmodule

// Slave module - uses slave modport
module ahb_slave (
    ahb_if.slave bus  // Specify modport!
);
    always @(posedge bus.HCLK) begin
        // Slave can read HADDR (input in slave modport)
        if (bus.HTRANS != 2'b00) begin
            addr_reg <= bus.HADDR;
        end
        
        // Slave can write to HRDATA (output in slave modport)
        bus.HRDATA <= memory[addr_reg];
        bus.HREADY <= 1'b1;
    end
endmodule

Compile-Time Safety

If a module using the master modport tries to drive HRDATA, the compiler will report an error. This catches bugs early!

Connecting with Modports

Top-Level Connections
module tb_top;
    // Clock
    logic clk;
    initial begin
        clk = 0;
        forever #5 clk = ~clk;
    end
    
    // Instantiate interface
    ahb_if bus(clk);
    
    // Reset
    initial begin
        bus.HRESETn = 0;
        #20 bus.HRESETn = 1;
    end
    
    // Connect master - uses master modport
    ahb_master u_master (
        .bus(bus)  // Modport specified in module
    );
    
    // Connect slave - uses slave modport
    ahb_slave u_slave (
        .bus(bus)  // Modport specified in module
    );
    
    // Monitor - uses monitor modport
    ahb_monitor u_monitor (
        .bus(bus)
    );
endmodule

Modport Selection

The modport is specified in the module definition, not at instantiation. Everyone connects to the same interface instance, but each sees their appropriate view.

Modports with Tasks and Functions

Modports can also include tasks and functions, providing different functionality to different users.

Modport with Tasks
interface bus_if (input logic clk);
    logic [31:0] addr;
    logic [31:0] data;
    logic        valid;
    logic        ready;
    
    // Task for driving transactions
    task automatic drive_txn(input [31:0] a, input [31:0] d);
        @(posedge clk);
        addr  <= a;
        data  <= d;
        valid <= 1;
        @(posedge clk);
        while (!ready) @(posedge clk);
        valid <= 0;
    endtask
    
    // Task for monitoring
    task automatic monitor_txn(output [31:0] a, output [31:0] d);
        @(posedge clk);
        while (!valid) @(posedge clk);
        a = addr;
        d = data;
    endtask
    
    // Driver modport - can use drive task
    modport driver (
        import drive_txn,
        output addr, data, valid,
        input  ready
    );
    
    // Monitor modport - can use monitor task
    modport monitor (
        import monitor_txn,
        input addr, data, valid, ready
    );
endinterface

Quick Summary

  • Modports define signal directions for different users
  • Directions are from the module's perspective
  • Specified using modport keyword in interface
  • Used as interface_name.modport_name in module ports
  • Provide compile-time safety against wrong connections
  • Can include tasks and functions

What's Next?

Complete your interface knowledge: