#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Register map
"""
from . import utils
from . import config
from .reg import Register
from .bitfield import BitField
from .enum import EnumValue
import json
import yaml
[docs]class RegisterMap():
"""CSR map"""
def __init__(self):
self._regs = []
def __eq__(self, other):
if self.__class__ != other.__class__:
raise TypeError("Failed to compare '%s' with '%s'!" % (repr(self), repr(other)))
else:
return self.as_dict() == other.as_dict()
def __ne__(self, other):
if self.__class__ != other.__class__:
raise TypeError("Failed to compare '%s' with '%s'!" % (repr(self), repr(other)))
else:
return not self.__eq__(other)
def __repr__(self):
return 'RegisterMap()'
def __str__(self):
return self.as_str()
[docs] def as_str(self, indent=''):
"""Create indented string with the information about register map."""
inner_indent = indent + ' '
regs = [reg.as_str(inner_indent) for reg in self.regs]
regs_str = '\n'.join(regs) if regs else inner_indent + 'empty'
return indent + 'register map:\n' + regs_str
[docs] def as_dict(self):
"""Return register map as a dictionary."""
return {reg.name: reg.as_dict() for reg in self.regs}
def __len__(self):
"""Calculate number of the registers"""
return len(self._regs)
def __iter__(self):
"""Create iterator over registers"""
return iter(self._regs)
def __getitem__(self, key):
"""Get register by name or index"""
try:
if isinstance(key, str):
return next(reg for reg in self if reg.name == key)
else:
return self._regs[key]
except (StopIteration, TypeError, KeyError, IndexError):
raise KeyError("There is no register with a name/index '%s'!" % (key))
def __setitem__(self, key, value):
"""Set register by key"""
raise KeyError("Not able to set '%s' register directly!"
" Try use add_registers() method." % (key))
@property
def reg_names(self):
"""List with all register names."""
return [reg.name for reg in self]
def _addr_resolve(self, reg):
"""Resolve address for a register with no address."""
# some error checks
assert len(self) != 0, \
"Register '%s' with no address is not allowed to be the first register in a map!" % (reg.name)
assert config.globcfg['address_increment'] != 'none', \
"Register '%s' with no address is not allowed when address auto increment is disabled!" % (reg.name)
prev_addr = self.regs[-1].address
if config.globcfg['address_increment'] == 'data_width':
addr_step = config.globcfg['data_width'] // 8
else:
addr_step = config.globcfg['address_increment']
reg.address = prev_addr + addr_step
def _addr_check_alignment(self, reg):
"""Check address alignment."""
if config.globcfg['address_alignment'] == 'none':
align_val = 1
elif config.globcfg['address_alignment'] == 'data_width':
align_val = config.globcfg['data_width'] // 8
else:
align_val = config.globcfg['address_alignment']
assert (reg.address % align_val) == 0, \
"Register '%s' with address '%d' is not %d bytes alligned!" % (reg.name, reg.address, align_val)
def _addr_check_conflicts(self, reg):
addresses = [reg.address for reg in self]
if reg.address in addresses:
conflict_reg = self[addresses.index(reg.address)]
assert False, "Register '%s' with address '%d' conflicts with register '%s' with the same address!" % \
(reg.name, reg.address, conflict_reg.name)
@property
def regs(self):
"""List with register objects."""
return self._regs
[docs] def add_registers(self, new_regs):
"""Add list of registers.
Register are automatically sorted and stored in the ascending order of addresses.
"""
# hack to handle single elements
new_regs = utils.listify(new_regs)
# add registers to the list one by one
for reg in new_regs:
# check existance
assert reg.name not in self.reg_names, \
"Register with name '%s' is already present!" % (reg.name)
# aplly calculated address if register address is empty
if reg.address is None:
self._addr_resolve(reg)
# check address alignment
self._addr_check_alignment(reg)
# check address conflicts
self._addr_check_conflicts(reg)
# if we here - all is ok and register can be added
try:
# find position to insert register and not to break ascending order of addresses
reg_idx = next(i for i, r in enumerate(self._regs) if r.address > reg.address)
self._regs.insert(reg_idx, reg)
except StopIteration:
# when registers list is empty or all addresses are less than the current one
self._regs.append(reg)
return self
[docs] def validate(self):
"""Validate the register map."""
for reg in self.regs:
assert self.reg_names.count(reg.name) == 1, \
"Register '%s' name is not unique!" % (reg.name)
reg.validate()
[docs] def read_file(self, path):
"""Read register map from file (based on extension)."""
ext = utils.get_file_ext(path)
if ext in ['.yaml', '.yml']:
self.read_yaml(path)
elif ext == '.json':
self.read_json(path)
elif ext == '.txt':
self.read_txt(path)
else:
raise ValueError("Unknown extension '%s' of the file '%s'" % (ext, path))
[docs] def read_json(self, path):
"""Read register map from JSON file."""
with open(path, 'r') as f:
data = json.load(f)
self._fill_from_file_data(data['regmap'])
[docs] def read_yaml(self, path):
"""Read register map from YAML file."""
with open(path, 'r') as f:
data = yaml.safe_load(f)
self._fill_from_file_data(data['regmap'])
[docs] def read_txt(self, path):
"""Read register map from text file."""
with open(path, 'r') as f:
raw_lines = f.readlines()
data = []
reg_start_idx = None
for i, line in enumerate(raw_lines):
if '-----' in line:
reg_start_idx = i + 1
if reg_start_idx and i >= reg_start_idx:
# register with one bitfield template
reg = {"name": None, "description": None,
"bitfields": [{"width": None, "access": None, "hardware": None}]}
# prepare the line
line_data = [s.strip() for s in line.split("|")[1:-1]]
if len(line_data) != 7:
raise ValueError("Not enough / too much columns in line %d. Plese fix!" % (i + 1))
# extract register properties
if line_data[0]: # address value can be ommited
reg["address"] = line_data[0]
reg["address"] = line_data[0] if line_data[0] else None
reg["name"] = line_data[1]
reg["bitfields"][0]["width"] = line_data[2]
reg["bitfields"][0]["access"] = line_data[3]
reg["bitfields"][0]["hardware"] = line_data[4]
if line_data[4]: # reset value can be ommited
reg["bitfields"][0]["reset"] = line_data[5]
reg["description"] = line_data[6]
data.append(reg)
if not reg_start_idx:
raise ValueError("Can't find table with registers!")
self._fill_from_file_data(data)
def _fill_from_file_data(self, data):
"""Fill register map with data from file."""
self._regs = []
for data_reg in data:
data_reg_filtered = {k: v for k, v in data_reg.items() if k != 'bitfields'}
reg = Register(**data_reg_filtered)
for data_bf in data_reg['bitfields']:
data_bf_filtered = {k: v for k, v in data_bf.items() if k != 'enums'}
bf = BitField(**data_bf_filtered)
if 'enums' in data_bf.keys():
for data_enum in data_bf['enums']:
bf.add_enums(EnumValue(**data_enum))
reg.add_bitfields(bf)
self.add_registers(reg)