RemoteVariable
RemoteVariable maps a Variable onto
hardware-accessible memory. In most PyRogue applications, this means a
register or bit field in an FPGA or in a device reachable through the Rogue
memory path. It is the standard choice for register-style control and
monitoring.
When To Use RemoteVariable
Use RemoteVariable when the tree value should correspond to a specific
memory-mapped hardware location.
That usually means:
Configuration registers
Status registers
Counters, flags, masks, and control fields
Wide logical values assembled from more than one register location
Unlike LocalVariable, a RemoteVariable participates in the hardware
transaction path. Reads and writes flow through the parent Device, the
Block structure built during attachment, and the underlying memory
interface.
What You Usually Set
Most RemoteVariable definitions combine the shared Variable parameters from
Variable with a small set of hardware-mapping
parameters:
offsetfor the memory location.bitSizeandbitOffsetfor the field layout.basefor the typed interpretation of the data.verifywhen write-back verification policy matters.overlapEnwhen multiple Variables share one register word.numValues,valueBits, andvalueStridefor arrays or tables.bulkOpEnwhen large Variables need different transaction behavior.
The first three parameters define where the value lives and how its bits should be interpreted. The remaining options mainly control transaction policy and layout edge cases, such as write verification, shared register words, packed arrays, and bulk-operation participation.
Memory Mapping Parameters
A RemoteVariable is defined by memory-mapping parameters such as:
offsetbitSizebitOffsetOptional
base/type Model and packing parameters
These define where the value exists in the hardware address space and how its
bits should be interpreted. The base Model tells PyRogue whether the value
should behave like an unsigned integer, signed integer, boolean, floating
point quantity, byte array, and so on.
At the tree level, this means a RemoteVariable gives users a typed,
formatted value-oriented interface while PyRogue handles the lower-level
details of byte packing, block grouping, and memory transactions underneath.
offset, bitSize, and bitOffset can each be either a single value or
lists. List form is used when one logical Variable is split across multiple
register locations.
If the value uses a fixed-point interpretation such as base=pr.Fixed(...)
or base=pr.UFixed(...), see Fixed-Point Models
for the scaling and display model behind those types.
How RemoteVariable Fits The Tree
RemoteVariable is where the Variable and Device narratives meet:
Variableprovides the user-facing typed value interfaceDeviceprovides the write/verify/read/check transaction modelBlockgroups one or more compatible RemoteVariables into efficient transaction units
So when a user calls get(read=True) or set(write=True), the actual
hardware activity still runs through the parent Device and its Block
operations.
Example Patterns
Most RemoteVariable definitions in real PyRogue device trees fall into a
small set of patterns:
A single control or status field in one register word
An enumerated field where raw bit values map to named hardware modes
A logical value assembled from multiple register locations
A packed array exposed through
numValues,valueBits, andvalueStrideMultiple Variables intentionally sharing one register word through
overlapEn=True
The following examples mirror those patterns.
Single Register Fields
This is the most common case: a normal register field with a fixed offset, bit position, and access mode.
import pyrogue as pr
class MyDevice(pr.Device):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.add(pr.RemoteVariable(
name='ScratchPad',
description='Register used to test reads and writes',
offset=0x00,
bitSize=32,
bitOffset=0,
mode='RW',
base=pr.UInt,
disp='{:#08x}',
))
self.add(pr.RemoteVariable(
name='RxReady',
description='Polled status bit',
offset=0x04,
bitSize=1,
bitOffset=0,
mode='RO',
base=pr.Bool,
pollInterval=1,
))
Enumerated Fields
Enum mappings are also common. They let the Variable present hardware-meaningful state names while still using the underlying integer register field.
import pyrogue as pr
class MyDevice(pr.Device):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.add(pr.RemoteVariable(
name='RxDataWidth',
offset=0x10,
bitOffset=11,
bitSize=3,
mode='RW',
base=pr.UInt,
enum={
2: '16',
3: '20',
4: '32',
5: '40',
6: '64',
7: '80',
},
))
Split Variables Across Multiple Addresses
Some hardware maps place one logical field across multiple words. In that
case, pass lists for offset, bitOffset, and bitSize.
In this example, one logical 7-bit field is split across two 32-bit words in the memory map:
offset=0x34contributes bit 15 (1 bit)offset=0x38contributes bits 5:0 (6 bits)
PyRogue combines those segments in list order into one Variable value.
import pyrogue as pr
class GtRegs(pr.Device):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.add(pr.RemoteVariable(
name='RXDFELPMRESET_TIME',
offset=[0x34, 0x38],
bitOffset=[15, 0],
bitSize=[1, 6],
mode='RW',
base=pr.UInt,
))
Packed Arrays
When hardware exposes a register table or memory-backed array, use
numValues, valueBits, and valueStride. This pattern is common for
tables, coefficient memories, and page/register dumps.
The array below exposes 256 32-bit elements at one base offset.
import pyrogue as pr
class TableRegs(pr.Device):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.add(pr.RemoteVariable(
name='DataBlock',
offset=0x0000,
bitSize=32 * 0x100,
bitOffset=0,
numValues=0x100,
valueBits=32,
valueStride=32,
updateNotify=True,
bulkOpEn=True,
overlapEn=True,
verify=True,
hidden=True,
base=pr.UInt,
mode='RW',
groups=['NoStream', 'NoState', 'NoConfig'],
))
For very large arrays, projects often disable per-write verification or bulk operations to control traffic and latency.
self.add(pr.RemoteVariable(
name='PixelData',
offset=0x4000,
bitSize=32 * num_cols,
bitOffset=0,
numValues=num_cols,
valueBits=32,
valueStride=32,
bulkOpEn=False,
verify=False,
hidden=True,
base=pr.UInt,
mode='RW',
groups=['NoStream', 'NoState', 'NoConfig'],
))
Overlapping Variables In One Register
Sometimes one register word is exposed both as a full-word access point and as
smaller subfields. In that case, each Variable sharing the same address range
must enable overlapEn=True.
import pyrogue as pr
class SharedWordRegs(pr.Device):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.add(pr.RemoteVariable(
name='ConnectionConfig',
offset=0x4014,
base=pr.UIntBE,
mode='WO',
overlapEn=True,
))
self.add(pr.RemoteVariable(
name='ConnectionSpeed',
offset=0x4014,
bitSize=16,
bitOffset=16,
mode='RO',
overlapEn=True,
enum={
0x28: 'CXP_1',
0x30: 'CXP_2',
0x38: 'CXP_3',
0x40: 'CXP_5',
0x48: 'CXP_6',
0x50: 'CXP_10',
0x58: 'CXP_12',
},
base=pr.UIntBE,
))
Wide Values Across Many Registers
List form also works well for read-only counters or masks split across many consecutive words.
For ES_QUALIFIER, the logical value is assembled from five 16-bit segments:
offset=0xB0-> bits 15:0offset=0xB4-> bits 31:16offset=0xB8-> bits 47:32offset=0xBC-> bits 63:48offset=0xC0-> bits 79:64
For RX_PRBS_ERR_CNT, the logical value is assembled from two 16-bit
segments:
offset=0x978-> bits 15:0offset=0x97C-> bits 31:16
import pyrogue as pr
class EyeScanRegs(pr.Device):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.add(pr.RemoteVariable(
name='ES_QUALIFIER',
offset=[0xB0, 0xB4, 0xB8, 0xBC, 0xC0],
bitOffset=[0, 0, 0, 0, 0],
bitSize=[16, 16, 16, 16, 16],
mode='RO',
base=pr.UInt,
))
self.add(pr.RemoteVariable(
name='RX_PRBS_ERR_CNT',
offset=[0x978, 0x97C],
bitOffset=[0, 0],
bitSize=[16, 16],
mode='RO',
base=pr.UInt,
))
What To Explore Next
Software-owned Variables: LocalVariable
Derived Variables: LinkVariable
Block grouping and transaction units: Blocks
Device transaction sequencing: Device Block Operations
API Reference
See RemoteVariable for generated API details.