How to improve the reuse of ISR(interrupt service routine) for a block?

I have a general questions about how to improve the ISR(interrupt service routine) in a verification environment.

Usually we have will define an ISR for a particular block in the block verification environment. The block ISR will be called once the block interrupt output is triggered, and it will call write/read APIs of uvm_reg through register model to respond to the interrupt. And we want to reuse the ISR to system level testing also.

In my mind, in system level ISR, it usually polls some system registers to find out the source of the interrupt, and if it is due to the particular block interrupt, it will call the ISR for the block which is reused from the block environment. This is just to emulate real software ISR behavior.

But the question is since block environment and system environment has different contexts, and the register models visible in block level and system level are different. If we use hierarchical reference such as regmodel.$block.$reg.write() to do the write, the hierarchical path will be different for block and system level, so the the same block ISR can’t be reused from block level to system level. I know uvm_reg provide method to search for the register through the register name, but it may have problems if we have multiple registers with the same name, and I am also concerned about the performance.
Another solution I can think of is use universal Macros for register accessing and replace those Macro with different definitions in system level, but that seems not a graceful way.

Can anyone give some suggestions? Thanks a lot.

In reply to pinegreen:

In your module level ISR, you’ll typically have a field for the block level register model:


class module_isr_sequece extends uvm_reg_sequence;
  module_reg_block regmodel;
  
  // ...
endclass

When doing register access, you dig into it to get the registers:


class module_isr_sequece extends uvm_reg_sequence;
  virtual task body();
    regmodel.some_reg.write(...);
    regmodel.some_other_reg.write(...);
  endtask
endclass

In your system level ISR, you’ll have a different register model:


class system_isr_sequece extends uvm_reg_sequence;
  system_reg_block regmodel;
  
  // ...
endclass

The system regmodel contains one (or more) instance(s) of the module register model. When figuring out that you need to start that certain ISR, you just need to provide it the appropriate module regmodel:


class system_isr_sequece extends uvm_reg_sequence;
  virtual task body();
    module_isr_sequence module_isr;
    
    // ... figure out what ISR to start and create it
    // assume it's 'module_isr'
    
    module_isr.regmodel = regmodel.module_block;
    `uvm_send(module_isr, ...)
  endtask
endclass

You shouldn’t have any problems with paths being different.

In reply to Tudor Timi:

sounds a good solution, Thank you Tudor.