RAL Rework Deep Dive – Code Examples and Migration Strategies

RAL Rework Deep Dive – Code Examples and Migration Strategies

FROM THE CTO'S DESK

"Theory tells you what's changing. Code shows you how to survive it."

Three months ago, we shared our analysis of the Accellera UVM Register Layer rework. The response was immediate—verification leads from five of the top ten semiconductor companies reached out asking the same question: "Where's the code?"

This edition delivers exactly that. Before/after examples. Migration scripts. Performance benchmarks. And an exclusive interview with an Accellera working group member who has been shaping these changes from the inside.

If you manage a verification team with 10,000+ registers under test, this is required reading.


PART I: BEFORE / AFTER CODE COMPARISONS

THE SCENARIO

A typical SoC verification environment with 15 IP blocks, each containing 200–500 registers, accessed via AXI, APB, and backdoor paths.

1. REGISTER BLOCK CONSTRUCTION

LEGACY (UVM 1.2 / Current RAL)

// Legacy approach: Hard-coded addresses, manual field definitions
class legacy_reg_block extends uvm_reg_block;
  `uvm_object_utils(legacy_reg_block)
  
  // Register declarations
  rand uvm_reg_field reserved;
  rand uvm_reg_field enable;
  rand uvm_reg_field mode;
  
  virtual function void build();
    // Manually create each register
    uvm_reg reg_control = uvm_reg::type_id::create("control_reg", , get_full_name());
    reg_control.configure(this, 32, 0, "RW", 1);
    
    // Manually add each field
    reserved = uvm_reg_field::type_id::create("reserved", , get_full_name());
    enable   = uvm_reg_field::type_id::create("enable", , get_full_name());
    mode     = uvm_reg_field::type_id::create("mode", , get_full_name());
    
    reserved.configure(reg_control, 31, 28, "RW", 0, 1'b1, 1, 0, 1);
    enable.configure  (reg_control, 0,  1,  "RW", 0, 1'b0, 1, 0, 1);
    mode.configure    (reg_control, 2,  2,  "RW", 0, 2'b00, 1, 0, 1);
    
    // Add to default map
    default_map.add_reg(reg_control, 32'h0000, "RW");
  endfunction
endclass        

Problems: Manual coding for every register. Addresses hard-coded. No runtime introspection. Specification changes require recompilation.

FUTURE (New RAL with IP-XACT Introspection)

// New approach: Specification-driven, introspectable
class ipxact_aware_block extends uvm_reg_block;
  `uvm_object_utils(ipxact_aware_block)
  
  virtual function void build();
    // Load IP-XACT specification at runtime
    ipxact_spec_t spec = ipxact_loader::load("block_spec.xml");
    
    // Register model built from specification
    foreach (spec.registers[i]) begin
      uvm_reg reg = uvm_reg::type_id::create(spec.registers[i].name, , get_full_name());
      reg.configure(this, spec.registers[i].width, 0, spec.registers[i].access, 1);
      
      foreach (spec.registers[i].fields[j]) begin
        uvm_reg_field field = uvm_reg_field::type_id::create(spec.registers[i].fields[j].name);
        field.configure(reg, 
                        spec.registers[i].fields[j].lsb_pos,
                        spec.registers[i].fields[j].width,
                        spec.registers[i].fields[j].access,
                        0, spec.registers[i].fields[j].reset_val, 1, 0, 1);
      end
      
      // Address mapping from spec
      default_map.add_reg(reg, spec.registers[i].address, spec.registers[i].rights);
    end
    
    // Runtime introspection enabled
    introspection_enable = 1;
  endfunction
  
  // Query register properties at runtime
  function string get_reg_access(string reg_name);
    return spec.get_access(reg_name);
  endfunction
endclass        

What changed: The register model is now built from a machine-readable specification (IP-XACT) rather than hand-coded. Addresses, field widths, access policies, and reset values are consumed directly. Runtime introspection enables sequences that adapt to the actual register configuration.

2. FRONTDOOR ACCESS (SIMPLIFIED)

LEGACY

class legacy_adapter extends uvm_reg_adapter;
  virtual function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
    bus_transaction bus_tx = bus_transaction::type_id::create("bus_tx");
    bus_tx.address = rw.addr;
    bus_tx.data    = rw.data;
    bus_tx.kind    = (rw.kind == UVM_READ) ? READ : WRITE;
    return bus_tx;
  endfunction
  
  virtual function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
    bus_transaction bus_tx;
    if (!$cast(bus_tx, bus_item)) begin
      `uvm_fatal("ADAPTER", "Failed to cast")
    end
    rw.addr = bus_tx.address;
    rw.data = bus_tx.data;
    rw.kind = (bus_tx.kind == READ) ? UVM_READ : UVM_WRITE;
    rw.status = UVM_IS_OK;
  endfunction
endclass        

Problems: Every bus protocol requires a custom adapter. Casting overhead. Manual mapping between register operations and bus transactions.

FUTURE

class simplified_adapter extends uvm_reg_adapter_new;
  virtual function void configure();
    // Declarative mapping – no per-transaction conversion logic
    set_address_mapping(AXI_PROTOCOL, address_alignment = WORD_ALIGNED);
    set_data_mapping(endianness = LITTLE_ENDIAN);
    set_response_handling(AUTO_RESPONSE);
  endfunction
  
  // No reg2bus/bus2reg required – framework handles standard protocols
endclass        

What changed: For standard bus protocols, the new RAL eliminates the need for custom adapters entirely. Declarative configuration replaces imperative conversion logic.

3. PREDICTOR CONFIGURATION

LEGACY

// Legacy: Three confusing predictor options
class legacy_env extends uvm_env;
  uvm_reg_predictor #(bus_transaction) predictor;
  
  function void connect_phase();
    predictor.map     = reg_model.default_map;
    predictor.adapter = adapter;
    predictor.predict();
    // But wait – was that auto_predict or explicit?
    // Did we need to call predict() or not?
  endfunction
endclass        

FUTURE

// New: Unified prediction model
class modern_env extends uvm_env;
  function void connect_phase();
    // Single call configures all prediction
    reg_model.set_prediction_policy(UVM_UNIFIED_PREDICTION);
    
    // Or use the simpler auto-sync model
    reg_model.enable_auto_sync();
  endfunction
endclass        

What changed: The confusing distinction between implicit, explicit, and passive prediction has been replaced by a single unified model. The working group eliminated the set_auto_predict ambiguity that has caused countless verification bugs.


PART II: MIGRATION SCRIPTS

SCRIPT 1: LEGACY RAL DETECTION AND REPORTING

#!/usr/bin/env python3
# detect_legacy_ral.py
# Scans UVM testbench for methods targeted for deprecation

import re
import os
import sys

DEPRECATED_METHODS = [
    'set_auto_predict',
    'add_reg',      # in uvm_reg_map
    'add_mem',      # in uvm_reg_map
    'get_submap',
    'uvm_reg_map::configure',  # specific signature
]

VULNERABLE_PATTERNS = [
    (r'\.set_auto_predict\s*\(', 'set_auto_predict() – replace with unified prediction'),
    (r'default_map\.add_reg\s*\(', 'add_reg() – will be deprecated, use IP-XACT generation'),
    (r'\.get_submap\s*\(', 'get_submap() – hierarchy model changing'),
]

def scan_file(filepath):
    issues = []
    with open(filepath, 'r') as f:
        for line_num, line in enumerate(f, 1):
            for pattern, msg in VULNERABLE_PATTERNS:
                if re.search(pattern, line):
                    issues.append((line_num, line.strip(), msg))
    return issues

def main():
    print("=" * 70)
    print("Suresh Chips: Legacy RAL Migration Scanner")
    print("Identifies code patterns affected by UVM RAL rework")
    print("=" * 70)
    
    total_issues = 0
    for root, dirs, files in os.walk(sys.argv[1]):
        for file in files:
            if file.endswith(('.sv', '.svh', '.v', '.vh')):
                filepath = os.path.join(root, file)
                issues = scan_file(filepath)
                if issues:
                    print(f"\n📁 {filepath}")
                    for line_num, line, msg in issues:
                        print(f"   Line {line_num}: {line[:60]}...")
                        print(f"      → {msg}")
                        total_issues += 1
    
    print("\n" + "=" * 70)
    print(f"Migration Readiness Summary:")
    print(f"  Total legacy patterns detected: {total_issues}")
    print(f"  Estimated migration effort: {total_issues * 2.5} hours")
    print("=" * 70)

if __name__ == "__main__":
    main()        

SCRIPT 2: IP-XACT GENERATOR WRAPPER

#!/usr/bin/env python3
# ipxact_to_ral_wrapper.py
# Generates new-style RAL models from IP-XACT while maintaining legacy compatibility

import xml.etree.ElementTree as ET
import sys

def generate_ral_model(ipxact_file, output_dir):
    tree = ET.parse(ipxact_file)
    root = tree.getroot()
    
    namespace = {'spirit': 'http://www.spiritconsortium.org/XMLSchema/SPIRIT/1685-2009'}
    
    ral_code = []
    ral_code.append("// Auto-generated from IP-XACT: " + ipxact_file)
    ral_code.append("// Compatible with both legacy and new RAL APIs")
    ral_code.append("")
    
    for address_block in root.findall('.//spirit:addressBlock', namespace):
        block_name = address_block.get('name')
        base_addr = address_block.find('spirit:baseAddress', namespace).text
        
        ral_code.append(f"class {block_name}_reg_block extends uvm_reg_block;")
        ral_code.append("  `uvm_object_utils({0}_reg_block)".format(block_name))
        ral_code.append("")
        ral_code.append("  function new(string name = \"{0}\");".format(block_name))
        ral_code.append("    super.new(name);")
        ral_code.append("  endfunction")
        ral_code.append("")
        ral_code.append("  virtual function void build();")
        ral_code.append("    // Legacy-compatible map")
        ral_code.append("    default_map = uvm_reg_map::type_id::create(\"default_map\", this);")
        ral_code.append(f"    default_map.configure(this, '{h.base_addr}', 4, UVM_LITTLE_ENDIAN);")
        ral_code.append("")
        ral_code.append("    // Future: Enable introspection")
        ral_code.append("    `ifdef UVM_NEW_RAL")
        ral_code.append("    this.set_introspection_enabled(1);")
        ral_code.append("    `endif")
        ral_code.append("  endfunction")
        ral_code.append("endclass")
    
    with open(f"{output_dir}/{block_name}_reg_block.sv", 'w') as f:
        f.write("\n".join(ral_code))
    
    print(f"Generated: {output_dir}/{block_name}_reg_block.sv")

if __name__ == "__main__":
    generate_ral_model(sys.argv[1], sys.argv[2])        

SCRIPT 3: BACKWARD COMPATIBILITY WRAPPER

// backward_compatibility_shim.sv
// Wraps legacy register map calls to work with new architecture

`ifdef UVM_USE_LEGACY_RAL
  // No changes needed – legacy behavior
`else
  // Shim to map legacy calls to new APIs
  function void uvm_reg_map::add_reg(uvm_reg rg, uvm_reg_addr_t offset, string rights);
    uvm_address_descriptor desc;
    desc = uvm_address_descriptor::type_id::create("desc");
    desc.set_register(rg);
    desc.set_offset(offset);
    desc.set_rights(rights);
    this.add_address_descriptor(desc);
  endfunction
  
  function void uvm_reg_map::set_auto_predict(bit enable);
    if (enable) begin
      this.get_parent_block().enable_auto_sync();
    end
  endfunction
`endif        

PART III: PERFORMANCE BENCHMARKS

The following data is based on simulations run by the Suresh Chips verification team across three real-world SoC projects, each containing between 8,000 and 25,000 registers.

TEST CONFIGURATION

Article content

RESULTS

Article content

KEY OBSERVATIONS

  1. The largest gains appear in high-register-count configurations. The 34% build time reduction on Project C (24,613 registers) suggests that the new flattened address map architecture eliminates O(n²) complexity in register traversal.
  2. Backdoor access improvements come from eliminating redundant prediction logic. The new unified prediction model removes the double-update path that plagued legacy RAL.
  3. Memory footprint reduction matters for large SoC simulations. Teams running multiple testbenches in parallel see meaningful improvement in server utilization.
  4. IP-XACT loading is a one-time cost. For projects with weekly regression runs (∼50 builds per week), the 2.1-second load time adds approximately 1.8 minutes per week—a trivial cost for the benefits of spec-driven models.

ACCELERA PUBLIC GITHUB MIGRATION AIDS

As highlighted in the DVCon 2025 workshop, the public GitHub repository now supports faster delivery of bug fixes and migration aids. Our benchmarks confirm that teams adopting the reference implementation directly from GitHub gain access to critical performance enhancements months before EDA vendor rollups.


PART IV: INTERVIEW WITH ACCELLERA WORKING GROUP MEMBER

The following is a condensed transcript of an interview conducted with a senior verification architect who has served on the Accellera UVM working group for the past four years. The participant has requested anonymity due to ongoing standardization discussions.


Q: What motivated the RAL rework after all these years?

A: The short answer is that the industry outgrew the original design. When RAL was first introduced, a typical SoC might have a few hundred registers. Today, we see designs with 50,000 to 100,000 registers. The original O(n²) traversal patterns in address map resolution simply don't scale. We also saw that teams were spending 30–40% of their verification effort just maintaining register models—not testing registers, maintaining them. That's a failure of methodology.


Q: What's the one feature you wish you could have included but couldn't?

A: Full runtime reconfiguration of address maps. We explored allowing the register model to dynamically rebind address ranges during simulation—imagine an FPGA that reconfigures its memory map on the fly. The technical challenge wasn't the implementation; it was the backward compatibility impact. Too many existing testbenches assume that once build_phase completes, the address map is frozen. Breaking that assumption would have made migration impossible. So we deferred it to a future revision.


Q: Why were certain legacy methods kept vs. deprecated?

A: We evaluated every public method in uvm_reg_map, uvm_reg_block, and uvm_reg based on actual usage across 50+ companies. Methods like get_registers() and get_fields() were heavily used—they stay. Methods like set_auto_predict() and add_reg() with the old signature were either redundant or causing more problems than they solved. The deprecation list is actually shorter than many expected because we found that most teams only use a small subset of the RAL API. The rest of the complexity is internal.


Q: What's coming after this rework—2028–2029 roadmap?

A: Two things. First, tighter integration with PSS (Portable Stimulus Standard). Matthew Ballance's work on PSS register test generation showed the way. The new introspection APIs we're adding now are the foundation for PSS to generate register tests directly from the same IP-XACT specification. Second, we're looking at multi-threaded register access. Today, RAL serializes all accesses through a single map. For designs with multiple independent register interfaces, that's a performance bottleneck we'd like to eliminate.


Q: What's the single biggest mistake you see teams making when preparing for this transition?

A: Waiting. I see teams that know the rework is coming, but they keep building new register models with the old methodology, knowing they'll have to rewrite them in two years. That's false economy. The migration path is straightforward: start generating your models from IP-XACT today. Even if you wrap them in the legacy API for now, you'll have a much easier transition when the new APIs arrive. The teams that are doing this are already seeing benefits from spec-driven verification, even before the RAL changes.


CONCLUSION: THE MIGRATION ROADMAP

PHASE 1: NOW – IMMEDIATE ACTIONS

Article content

PHASE 2: Q3 2026 – PILOT

Article content

PHASE 3: 2027 – FULL ADOPTION

Article content

ABOUT SURESH CHIPS AND SEMICONDUCTOR

We provide verification methodology consulting, training, and audit services to semiconductor companies worldwide. Our team includes former Accellera working group members and verification architects with decades of experience.

Contact us for:

  • RAL migration impact assessments
  • IP-XACT adoption workshops
  • Custom register model generation flows
  • Verification team training on new RAL APIs


Coming in May: "RAL Rework – The Complete Migration Toolkit" A downloadable package containing all scripts, shims, and examples from this newsletter, plus a detailed migration guide for large teams.


Contributing Editors: Suresh Chips CTO Office, with technical references from DVCon 2025–2026 proceedings and the Accellera UVM working group.


© 2026 Suresh Chips and Semiconductor. All rights reserved.

*UVM, IEEE 1800.2, IP-XACT, and Accellera are trademarks of their respective holders.*


To view or add a comment, sign in

Others also viewed

Explore content categories