PyMTL 3 (Mamba) Documentation

repository·master·Indexed 19 days ago

https://github.com/pymtl/pymtl3

A Python-based framework for hardware generation, simulation, and verification supporting multi-level hardware modeling. It includes tools for translating designs to Register Transfer Level Intermediate Representation (RTLIR) via the RTLIRTranslator and supports co-simulation with Verilator for SystemVerilog blackbox integration.

Tokens
10.8K
Snippets
36
Records
54
Agent score
65%

What's inside PyMTL 3

  1. Overview of PyMTL pass types

    master

    PyMTL organizes its passes into several functional categories:

    • Simulation passes: Used for analyzing or instrumenting components during simulation.
    • Translation passes: Used to transform PyMTL components into other representations.
    • Import passes: Used to bring external definitions (like Verilog) into the PyMTL environment.
    • Translation-import passes: A hybrid category for passes that perform both translation and import tasks.
    • Placeholder passes: Specialized passes for handling placeholders in the hierarchy.
  2. Overview of PyMTL 3 (Mamba)

    master

    PyMTL 3 (Mamba) is an open-source, Python-based framework for hardware generation, simulation, and verification. It supports multi-level hardware modeling.

    Note: PyMTL 3 is currently in beta and is under active development.

  3. Understand Bits and integer operation semantics

    master

    PyMTL3 uses BitsN objects (with explicit bitwidth N) and Python int objects (with inferred bitwidth). When performing operations between them, follow these rules:

    • Implicit truncation is forbidden.
    • Implicit zero extension is allowed to resolve bitwidth mismatches.
    • Unsigned semantics are preserved for all operations.

    Bitwidth Determination Rules

    Binary Operations (except << and >>)

    • Both sides have explicit bitwidth: An error is raised if bitwidths mismatch. The result bitwidth follows the operation rules.
    • Both sides have inferred bitwidth: The shorter side is zero-extended to match the longer side. The result bitwidth is inferred.
    • One explicit, one inferred: If the inferred bitwidth is smaller than the explicit bitwidth, an error is raised. Otherwise, the inferred side is zero-extended to the explicit bitwidth. The result bitwidth is explicit.

    Shift Operations (<< and >>)

    • The bitwidth of the right-hand side is ignored.
    • The result bitwidth is determined by the left-hand side (explicit or inferred).

    Unary Operations

    • The result bitwidth matches the operand's bitwidth (explicit or inferred).
  4. What are Placeholder components in PyMTL3?

    master

    A Placeholder is a property of a PyMTL Component that indicates the component declares its interface (ports) but leaves its implementation to be backed by external designs, such as Verilog modules.

    This allows you to describe a PyMTL component hierarchy where certain components are interfaced to the rest of the hierarchy in a disciplined way, even if their actual logic resides in Verilog. You can use these placeholders as submodules within concrete PyMTL components.

    from pymtl3 import *
    
    # A component using VerilogPlaceholder to indicate external implementation
    class FullAdder( Component, VerilogPlaceholder ):
      def construct( s ):
        s.a = InPort()
        s.b = InPort()
        s.cin  = InPort()
        s.sum  = OutPort()
        s.cout = OutPort()
  5. Understand the RTLIR hierarchy and types

    master

    RTLIR is divided into two main representations: Behavioral and Structural.

    Behavioral RTLIR

    Describes the logic within update blocks. The implementation is layered (L1-L5), but users should only interact with the highest level (L5) via the top-level API.

    • L1: Assignments, value ports, integer constants, and free variables.
    • L2: Adds If, For, comparisons, arithmetic, and temporary variables.
    • L3: Adds Struct data types.
    • L4: Adds PyMTL interface support.
    • L5: Adds component hierarchy support.

    Structural RTLIR

    Describes the connectivity and hardware structure. The implementation is layered (L1-L4), but users should only interact with the highest level (L4) via the top-level API.

    • L1: Value ports/wires, integer constants, and connections.
    • L2: Adds Struct data types and attribute access.
    • L3: Adds PyMTL interface and attribute signal access.
    • L4: Adds component hierarchy and port connections.

    Core RTLIR Classes

    • BehavioralRTLIR: Contains definitions for classes appearing in an IR AST for an update block.
    • RTLIRType: Definitions of all possible RTLIR instance classes.
    • RTLIRDataType: Definitions of all possible RTLIR data type classes.
    • StructuralSignalExpr: Definitions of operations appearing in an IR signal expression (e.g., slicing or attribute access like s.in_[2].bar[0:4]).
  6. TinyRV0 Instruction Encoding Formats

    master

    TinyRV0 uses standard RISC-V instruction encoding with four types and five immediate encodings. All immediates are sign-extended, and the sign-bit is always located at instruction bit 31.

    Instruction Types

    • R-type: Used for register-to-register operations (e.g., ADD, AND, SLL, SRL).
    • I-type: Used for operations involving an immediate or a memory load (e.g., ADDI, LW, CSRR, CSRW).
    • S-type: Used for store operations and branches (e.g., SW, BNE).

    Immediate Formats

    Immediates are constructed by concatenating bits from the instruction in an asymmetric order.

    FormatBit Construction
    I-immediateimm[31] | imm[30:25] | imm[24:21] | imm[20]
    S-immediateimm[31] | imm[30:25] | imm[11:8] | imm[7]
    B-immediateimm[31] | imm[7] | imm[30:25] | imm[11:8] | 0
  7. Import Verilog modules with array ports

    master

    When importing Verilog modules with array ports, the PyMTL placeholder must match the array structure:

    • Unpacked Arrays: Use a nested list of PyMTL ports. For example, a [31:0] foo_in [0:2][0:3] Verilog port requires a 3x4 nested list of InPort(32).
    • Packed Arrays: Use a single PyMTL port with a width equal to the total bit-width of the packed array.
    # Unpacked array: input logic [31:0] foo_in [0:2][0:3]
    class foo( Component, VerilogPlaceholder ):
      def construct( s ):
        s.foo_in = [ [ InPort(32) for _ in range(4) ] for _ in range(3) ]
    
    # Packed array: input logic [2:0][3:0][31:0] foo_in
    class foo( Component, VerilogPlaceholder ):
      def construct( s ):
        s.foo_in = InPort(3*4*32)
  8. Communicate with passes using metadata

    master

    PyMTL passes are modular programs used to analyze, instrument, or transform component hierarchies. You can customize the behavior of these passes by attaching metadata to specific components.

    To use metadata:

    1. Identify the MetadataKey attribute on the pass class you wish to configure (e.g., VerilatorImportPass.enable).
    2. Use the Component.set_metadata(key, value) method on the target component to set the desired configuration.

    If a pass does not require customization or the default behavior is sufficient, no metadata needs to be set.

    # Example: Enabling the Verilator import pass on component 'm'
    m.set_metadata(VerilatorImportPass.enable, True)
  9. Workaround for generating parametrized Verilog modules

    master

    The current PyMTL3 translation mechanism does not support generating parametrized Verilog modules directly. It treats the translated Verilog as a specific design instance.

    Workaround: Instead of using Python parameters to define component behavior (which creates unique components for every parameter combination), declare the parameters as input ports. This allows you to instantiate the same translated Verilog module multiple times in a parent module or test harness, supplying different values through the ports.

  10. What constructs are translatable in PyMTL RTL

    master

    PyMTL RTL translation passes only support a specific subset of Python and PyMTL constructs. To ensure successful translation to backends like Verilog, your design must adhere to these rules:

    Structural Constructs

    • Data types: All Bits and BitStruct types.
    • Constants: Python integers, Bits objects, and BitStruct constant instances.
    • Signals: InPort, OutPort, and Wire of Bits or BitStruct type.
    • Interfaces: All interfaces where all child interfaces are translatable.
    • Components: All components with translatable interfaces, signals, and update blocks.

    Behavioral Constructs (Update Blocks)

    • Blocks: @update and @update_ff blocks.
    • Control Flow: if-else statements and for loops (where the loop index is a single index and the range is specified via range()).
    • Assignments: Signal assignments using @= and <<=, and temporary variable assignments using =.
    • Functions: BitsN(), BitStruct(), zext(), sext(), and trunc().
    • Operations: Comparisons between signals and constants; arithmetic and logic operations on signals and constants.

    Non-translatable Constructs (Avoid these)

    • Common Python data structures like set, dict, or list containing non-translatable items.
    • Arbitrary Python function calls.
  11. Understand the TinyRV0 ISA Architecture

    master

    TinyRV0 is a minimal 32-bit subset of the RISC-V RV32IZicsr ISA, containing only 10 instructions. It is designed for illustrative and teaching purposes.

    Key Architectural Constraints:

    • Data Formats: Supports only 4-byte (32-bit) signed and unsigned integers. No byte, half-word, or floating-point support.
    • Registers: 31 general-purpose registers (x1-x31). Register x0 is hardwired to zero. All registers are 32 bits wide.
    • Memory:
      • 1MB virtual/physical address space (0x00000000 to 0x000fffff).
      • Endianness: Little-endian.
      • Alignment: LW (Load Word) and SW (Store Word) instructions require 4-byte aligned addresses. Unaligned access behavior is undefined.
    • Privilege Mode: Only supports M-mode (Machine mode). There is no distinction between user and privileged modes.
    • Reset Vector: The processor starts execution at 0x00000200.