Device Library Management

Introduction

The device library provides the means for loading and configuring a simulation model for a particular MCU model or variant. The information is also used for accessing the device internals in a ‘pythonic’ and human-readable way, mainly for testing and debugging purposes. The device library for yasimavr is built around 3 categories of classes/objects :

  • Descriptors : contains all the information describing a device: pins, interrupts, memory layout, registers, etc.

  • Builders : factory classes to instantiate and configure a device simulation model based on the information stored in descriptors.

  • Accessors : allows to access the internals of a device model, in particular the registers in a ‘Pythonic’ way. Designed primarily for automatic testing of simulation models but may also be used in simulation.

The information for configuring device models is stored in configuration files using the YAML format.

yasimavr contains pre-defined models for some device variants. To use these variants, there is no need for a user to dive into the device library package and configuration files, and one can simply use device_library.load_device(). The list of pre-defined models can be obtained in command line by executing python -m yasimavr --list-models

Note

Using the device library to instantiate a simulation model is not compulsory, it only makes it more convenient for commonly used device models. It is possible to build and configure a model from scratch using the C++ API classes if one feels like it…

Device Descriptor

A descriptor is a Python object storing the information required to build a simulated device model. There are two ways to load a Device object : by name or by file.

The information is loaded from device configuration files in YAML format.

Reg Path format

In device configuration files, registers can be referenced to by what is called a ‘reg path’. A reg path is a string representing a path through a device description tree to a register or a field. It can be absolute (from the root) or relative (from a given peripheral instance).

Valid formats for the regpath are:

  • [P]/[R]/[F] (1)

  • [R]/[F] (2)

  • [A]/[F] (3)

  • [P]/[R] (4)

  • [R] (5)

where [P] is a peripheral instance name, [R] a register name, [A] a register address, [F] a field or a combination of fields using the character ‘|’.

Register addresses can be decimal or hexadecimal. Fields can be represented by a name, a bit number X or a bit range X-Y. However, if the register is given by an address, named fields are not allowed.

Examples:

  • SLPCTRL/SMCR/SE : absolute path to the bit SE of the SMCR register belonging to the Sleep Controller

  • CPU/GPIOR0 : absolute path to the GPIOR0 register

  • SPCR/SPE : relative path to the SPI Enable bit of the SPI Control Register (valid in the context of a SPI interface controller)

  • EXTINT/PCICR/2 : absolute path to bit 2 of the PCICR register

  • EXTINT/PCMSK0/0-3 : absolute path to bits 0 to 3 of the PCMSK0 register

  • 0x49/5|7 : path to the bits 5 and 7 of hexadecimal address 0x49

  • 2/0 : path to bit 0 of decimal address 2

Note

A list of path elements can be given as a regpath and is parsed the same way. For example, [‘USART’, ‘UCSRB’, ‘RXEN’] is equivalent to ‘USART/UCSRB/RXEN’.
In some cases, registers may be split across multiple peripherals. For example in ATMega328, MCUCR is split between SLPCTRL (fields BODSE, BODS) and CPUINT (fields IVCE, IVSEL). In this case, SLPCTRL does not recognize CPUINT fields and vice-versa. However, fields are always accessible by bit number. For example, SLPCTRL/MCUCR/IVSEL is invalid but SLPCTRL/MCUCR/0 is valid.

Reference (Descriptor structure)

A descriptor is a Python object storing the information required to build a simulated device model.

There are two ways to load a Device object: * by name, using one of the builtin device models, by using DeviceDescriptor.create_from_model() * by file, using DeviceDescriptor.create_from_file()

class yasimavr.device_library.descriptors.DeviceDescriptor

Top-level descriptor for a device variant, storing the configuration from a YAML configuration file. It contains information about pins, interrupts, memory space layout, etc.

Variables:
  • name (str) – name of the device model.

  • architecture (str) – one of supported architectures, currently ‘AVR’ or ‘XT’

  • mem (MemoryDescriptor) – descriptor of the device memory

  • core_attributes (dict[str,bool]) – dictionary of the attribute values to configure the device model

  • fuses (dict[str,int]) – default values for the device fuses, usually taken from factory values

  • access_config (dict[str,bool]) – dictionary of configuration values for Accessor objects

  • pins (list[str]) – list of the pin names

  • interrupt_map (InterruptMapDescriptor) – configuration values for interrupts

  • peripherals (dict[str,PeripheralInstanceDescriptor]) – dictionary of the peripheral instances

classmethod create_from_file(filename, repositories=())

Instantiate a device descriptor from an arbitrary configuration file.

Parameters:
  • filename (str) – Device configuration file path

  • repositories (list) – list of locations for finding peripheral class configuration files

classmethod create_from_model(model)

Instantiate a device descriptor from a pre-defined device model configuration.

Parameters:

model (str) – Model/variant name. the value is case insensitive

To be valid, the model name must be included in the device list file pointed to by the global variable LibraryDatabase.

Note that device descriptor are cached and a previously instantiated descriptor may be returned.

reg_address(reg_path, default=None)

Utility method that resolves a reg path and returns the address of the register. If the register could not be resolved, with a given default value, the default value is returned otherwise a ValueError exception is raised.

Parameters:
  • reg_path (str) – reg path to a register, must be of format [P]/[R] (P: peripheral name, R: register name)

  • default (any) – if set to any value other than None, sets the default value

class yasimavr.device_library.descriptors.PeripheralInstanceDescriptor(name, loader, f, device)

Descriptor class for the instantiation of a peripheral class.

For example, a device may have PORTA, PORTB, PORTC, all are instances of a PORT peripheral class.

Variables:
  • name (str) – name of the instance

  • per_class (str) – class name of the peripheral

  • ctl_id (str) – CTLID used for the peripheral model instance

  • reg_base (int) – base address of the peripheral (-1 if not relevant)

  • class_descriptor (PeripheralClassDescriptor) – descriptor of the class of the peripheral

  • device (DeviceDescriptor) – top-level device descriptor owning the peripheral

  • config (dict) – YAML section storing the settings for configuring the peripheral model

reg_address(reg_path, default=None)

Utility method that resolves a reg path and returns the address of the register. If the register could not be resolved, with a given default value, the default value is returned otherwise a ValueError exception is raised.

Parameters:
  • reg_path (str) – reg path to a register, must be of format [R] or [P]/[R] (P: peripheral name, R: register name)

  • default (any) – if set to any value other than None, sets the default value

class yasimavr.device_library.descriptors.PeripheralClassDescriptor(per_config)

Descriptor class for a peripheral class

Parameters:

per_config – YAML configuration section for the peripheral class

Variables:
  • registers (dict[str,RegisterDescriptor]) – map of the registers owned by the peripheral class

  • config (dict) – YAML section storing the settings for configuring the peripheral simulation model

class yasimavr.device_library.descriptors.ProxyRegisterDescriptor(reg, offset)

Descriptor class for a register proxy, used to represent the high and low parts of a 16-bits register

Variables:
  • reg (RegisterDescriptor) – full-length register descriptor

  • offset (int) – offset of the part from the register address

class yasimavr.device_library.descriptors.RegisterDescriptor(reg_config)

Descriptor class for a I/O register

Parameters:

reg_config – YAML configuration section for the register

Variables:
  • name (str) – name of the register

  • address (int) – absolute address of the register (or -1 if not relevant)

  • offset (int) – offset of the register from the peripheral base address (or -1 if not relevant)

  • size (int) – size in bytes of the register

  • kind (str) – kind of the register, one of ‘RAW’, ‘INT’ or ‘’

  • fields (dict[str,RegisterFieldDescriptor]) – dict of fields composing the register

  • readonly (bool) – indicates if the register is readonly (true) or writable (false) for the CPU

  • supported (bool) – indicates if the register is supported by the simulation model

class yasimavr.device_library.descriptors.RegisterFieldDescriptor(field_name, field_config, reg_size)

Descriptor class for a field of a I/O register

Parameters:
  • field_name (str) – name of the field

  • field_config – YAML configuration section for the field

  • reg_size (int) – size of the register in bits

Note that variables differ depending on the kind of field

Variables:
  • name (str) – name of the field

  • kind (str) – data type of the field: one of ‘RAW’, ‘INT’, ‘BIT’, ‘ENUM’

  • readonly (bool) – indicates if the field is readonly (true) or writable (false) for the CPU

  • supported (bool) – indicates if the field is supported by the simulation model

  • pos (int) – (BIT only) bit position

  • one (str) – (BIT only) interpretation of the ‘one’ value (by default: 1)

  • zero (str) – (BIT only) interpretation of the ‘zero’ value (by default: 0)

  • LSB (int) – (RAW, INT and ENUM) lowest significant bit position

  • MSB (int) – (RAW, INT and ENUM) most significant bit position

  • values (dist[int,str]) – (ENUM only) interpretation for the binary values

  • unit (str) – (INT only) unit of the values

class yasimavr.device_library.descriptors.InterruptMapDescriptor(int_config)

Descriptor class for an interrupt vector map It includes the list of interrupt vectors with their names ordered by index. It also includes the sleep mask map. This maps the sleep modes supported by the device to the interrupts that can wake up the device.

Each sleep mask is a list of 8-bits flags where each bit enable or disable a vector. Example: with sleep_mask=[0xFF,0x80]: vectors 0 to 7 and 15 are enabled whilst vectors 8 to 14 and above 15 are disabled.

Parameters:

int_config – YAML configuration section for the interrupt vectors

Variables:
  • vector_size (int) – size in bytes of each vector

  • vectors (list[str]) – list of the vector names

  • sleep_mask (dict[name:list[int]]) – dict mapping the sleep masks to each sleep mode name

class yasimavr.device_library.descriptors.MemorySpaceDescriptor(size, page_size)

Named tuple class for a memory space

Parameters:
  • size (int) – size of the memory space in bytes

  • page_size (int) – size of the page for read/write operations (where relevant) in bytes

class yasimavr.device_library.descriptors.MemoryDescriptor(mem_config)

Descriptor class for the memories of a device, representing addressing spaces for the device e.g. data space, programe space, eeprom, etc. The data memory space is composed of one or more ‘segments’ representing a continuous addressable range.

Parameters:

mem_config – YAML configuration section for the memory space

Variables:
  • spaces (dict[str,MemorySpaceDescriptor]) – dictionary of the memory spaces

  • data_segments (dict[str,DataSegmentDescriptor]) – dictionary of segments composing the data space

Reference (Utility functions)

yasimavr.device_library.descriptors.convert_to_regbit(arg, per=None, dev=None, merge_bits=False)

Utility method that resolves a reg path and returns a regbit_t object.

If the register could not be resolved, or if the result spans several bytes, a ValueError exception is raised.

If arg is an integer, it is interpreted as a register address and a regbit_t object representing the whole register is returned. If arg is a list, it is interpreted as the elements of a reg path.

One of per or dev arguments should be specified to resolve reg path with names. per is required to resolve relative reg paths. If per is given, dev doesn’t need to be.

The merge_bits parameter determines the behaviour in case multiple fields are parsed. If set to False (default) a ValueError exception is raised. If set to True the fields are merged into one single regbit object that is returned. The function will raise an ValueError exception if the fields are not adjacent.

Parameters:
yasimavr.device_library.descriptors.convert_to_regbit_compound(arg, per=None, dev=None)

Utility method that resolves a reg path and returns a regbit_compound_t object. If the register could not be resolved, a ValueError exception is raised.

If arg is a list, it is interpreted as a list of reg path to merge.

One of per or dev arguments should be specified to resolve reg path with names. per is required to resolve relative reg paths. If per is given, dev doesn’t need to be.

Parameters:
yasimavr.device_library.descriptors.convert_to_regmask(arg, per=None, dev=None)

Utility method that resolves a reg path and returns a regmask_t object. The arguments are the same as convert_to_regbit(). The difference is that multiple fields of a unique register are always allowed and do not need to be adjacent.

See also convert_to_regbit()

yasimavr.device_library.descriptors.convert_to_bitspec(arg, per=None, dev=None, merge_bits=False)

Utility method that resolves a reg path and returns a bitspec_t object. The arguments are the same as convert_to_regbit()

See also convert_to_regbit()

Device Builders

class yasimavr.device_library.builders._base.DeviceBuilder(dev_descriptor)

Generic device builder object, factory for device simulation models. Users don’t normally have to instantiate directly this class but rather use one of he sub-classes for each of the core architectures. It implements a cache for both peripheral builders and configuration structure.

classmethod build_device(dev_desc, dev_class)

Builds a device simulator model using the information from a descriptor.

Parameters:
  • dev_desc – device descriptor object

  • dev_class – base class for the device model to build and configure

build_peripheral(device, per_name)

Build a peripheral from the device descriptor and attach it to a device model.

Parameters:
  • device – instance of Device model

  • per_names – name of the peripheral instance to build

build_peripherals(device, per_names)

Build a list of peripherals from the device descriptor and attach them to a device model.

Parameters:
  • device – instance of Device model

  • per_names – list of peripheral names to build

classmethod clear_cache()

Clears the internal class cache for device builders

class yasimavr.device_library.builders._builders_arch_avr.AVR_DeviceBuilder(dev_descriptor)

Specialisation of DeviceBuilder for the device models using the AVR architecture.

class yasimavr.device_library.builders._builders_arch_avr.AVR_BaseDevice(dev_descriptor, builder)

Specialisation of BaseDevicer for the device models using the AVR architecture.

class yasimavr.device_library.builders._builders_arch_xt.XT_DeviceBuilder(dev_descriptor)

Specialisation of DeviceBuilder for the device models using the XT architecture.

class yasimavr.device_library.builders._builders_arch_xt.XT_BaseDevice(dev_descriptor, builder)

Specialisation of BaseDevice for the device models using the XT architecture.

Accessors

The core library provides a DeviceDebugProbe class that allows to read and write to I/O registers as if the probe was the CPU. However the probe only deals with 8-bits raw byte values.

Accessors provide an upper layer by using the information contained in descriptors, from device configuration files, and provide two features:

  • A fine-grained access to individual I/O bits and fields,

  • Pretty printing and human-readable interpretation of values for read/write operations.

For example, the following (using the debug probe object to enable sleep):

value = probe.read_ioreg(0x33)  #reading the register SMCR
value |= 1                      #setting SE (bit 0) to 1
probe.write_ioreg(0x33, value)  #writing back to SMCR

is equivalent, using the accessor system, to :

device_accessor.SLPCTRL.SMCR.SE = 'enabled'

The following (Reading the current sleep mode):

value = probe.read_ioreg(0x33)  #reading the register SMCR
mode = (value >> 1) & 0x07      #Extracting the SM field
print('Sleep mode :', mode)     #Print the value (raw)

will output: >>> Sleep mode : 0

The equivalent, using the accessor system :

print('Sleep mode :', device_accessor.SLPCTRL.SMCR.SM)

will output: >>> Sleep mode : IDLE

Reference (Accessor structure)

class yasimavr.device_library.accessors.DeviceAccessor(arg, descriptor=None)

Accessor class for a device.

It is initialised from either a probe or a device.

If the 1st argument is a probe, it must be already attached to a device. If it’s a device, the accessor will create a debug probe and attach it.

Parameters:
  • arg (DeviceDebugProbe|Device)

  • descriptor (DeviceDescriptor) – optional device descriptor object. If not specified, the descriptor is obtained from the _descriptor_ field of the device model instance.

Variables:
  • pins (dict) – dictionary of the device model pins

  • nvms (dict) – dictionary of all non-volatile memories of the device model

property aliases

Aliases of the device model corresponding to the descriptor used

property descriptor

Device descriptor object

property name

Name of the device model corresponding to the descriptor used

class yasimavr.device_library.accessors.CPUAccessor(probeIO, name, per, byteorders)

Accessor class for the core, giving access to the CPU registers (Rxx, PC) along with access to those located in the I/O space (SREG, SP)

class yasimavr.device_library.accessors.PeripheralAccessor(probeIO, name, per, byteorders)

Accessor class for a peripheral instance

property base

Peripheral base address (when relevant)

property class_descriptor

Peripheral class descriptor

property name

Peripheral name

signal()

Peripheral signal (or None if not used)

class yasimavr.device_library.accessors.RegisterAccessor(probeIO, per, addr, name, reg)

Accessor class for a I/O register.

This class allows to access the whole 8-bits of the register or by using each field composing it.

It supports ordering and comparison to integers.

property address

Register address

property allocated

Returns true is the register is properly allocated in the device model

property name

Register name

read()

Read a value from the I/O register

Return an integer (for INT or RAW kinds) or a bytes object (for ARRAY kind)

The read always succeeds even for unsupported registers.

property size

Register size in bytes

write(value)

Write a value to the I/O register

Parameters:

value (int_or_bytes) – integer (for INT or RAW kinds) or bytes-like object (for ARRAY kind)

Raise an exception if the register is read-only or unsupported

class yasimavr.device_library.accessors.CPURegisterAccessor(probe, name, index=-1)

Accessor class for a CPU register: R0-R31, X, Y, Z, PC.

property name

Register name

read()

Read a value from the I/O register

property size

Register size

write(value)

Write a value to the I/O register :param int value: integer (8 or 16-bits depending on the register size)

class yasimavr.device_library.accessors._FieldAccessor(reg, field)

Generic accessor class for a field of a I/O register. Field accessor can be converted and compared to integers (it uses the raw bit field value)

It uses the information stored in a field descriptor (RegisterFieldDescriptor) to decode/encode the value.

read_raw()

Read a raw integer value for the field from the I/O register.

write_raw(raw_value)

Write a raw integer value for the field to the I/O register.

class yasimavr.device_library.accessors.BitFieldAccessor(reg, field)

Accessor class for a field of a I/O register consisting of one bit.

Bit fields can be written with 0, 1, False, True or any string corresponding to the defined ‘zero’ or ‘one’ parameters in the descriptor.

read()

Read the value for the field from the I/O register.

Returns:

the ‘one’ or ‘zero’ values defined in the field descriptor.

write(value)

Write the value for the field to the I/O register.

Parameters:

value (int) – integer (raw value), boolean (True or False) or the ‘one’ or ‘zero’ values defined in the field descriptor.

class yasimavr.device_library.accessors.IntFieldAccessor(reg, field)

Accessor class for a field of a I/O register consisting of an integer value.

read()

Read a value for the field from the I/O register.

write(value)

Write the value for the field to the I/O register.

class yasimavr.device_library.accessors.EnumFieldAccessor(reg, field)

Accessor class for a field of a I/O register consisting of an enumeration value.

read()

Write the value for the field to the I/O register.

Return one of the enumeration values if a enum dictionary is specified or else the raw integer value.

write(value)

Write the value for the field to the I/O register.

Parameters:

value (int) – integer (raw value) or a string from one of the enumeration values

class yasimavr.device_library.accessors.RawFieldAccessor(reg, field)

Accessor class for a field of a I/O register consisting of an raw value. It’s identical to IntFieldAccessor except it’s printed in hexadecimal.