Source code for slave

# -*- coding: utf8 -*-
"""

*******************
``helper/slave.py``
*******************

`GNU General Public License v3`_ © 2015-2019 University of Oxford & contributors

.. _GNU General Public License v3: _static/LICENSE

``slave.py`` is a helper module providing methods and classes used to generate slave components from ``XML``

"""
import re
import os
import sys
import string
import string_io
import reserved_word
import numpy as np

import customlogging as xml2vhdl_logging
logger = xml2vhdl_logging.config_logger(__name__)


[docs]def reverse_key_order(input_dict): """Return a reversed ordered list of keys Args: input_dict (dict): Returns: (list): Reversed list of keys on input_dict """ key_list = [] for node_id in sorted([int(x) for x in input_dict.keys()]): key_list.append(str(node_id)) return list(reversed(key_list))
[docs]def dec_check_bit(add_list, bit): """Return position of first not-equal bit among addresses in add_list Args: add_list (list): bit (???): Returns: (???): first not-equal bit among addresses in add_list or -1 if there are not different bits """ for b in reversed(range(0, bit)): for add0 in add_list: for add1 in add_list: if add0[b] != add1[b]: return b return -1
[docs]def dec_get_last_bit(bit_path): """ Args: bit_path (???): Returns: (???): """ bit_list = bit_path.split("_") ret = bit_list[len(bit_list)-1] ret = re.sub(r'v.*', "", ret) ret = int(ret) logger.debug('get_last_bit input: {ret}'.format(ret=ret)) logger.debug('get_last_bit output: {bit_path}'.format(bit_path=bit_path)) if ret == -1: ret = 32 return ret
[docs]def dec_route_add(tree_dict): """ Args: tree_dict (???): Returns: (???): """ done = 0 logger.debug('tree_dict: {tree_dict}'.format(tree_dict=tree_dict)) for path in tree_dict.keys(): add_list = tree_dict[path] b = dec_check_bit(add_list, dec_get_last_bit(path)) if b > -1: list_0 = [] list_1 = [] for add in add_list: bit_dict = {} if add[b] == 0: list_0.append(add) else: list_1.append(add) logger.debug('{path} XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'.format(path=path)) logger.debug('{add_list_len}'.format(add_list_len=len(add_list))) logger.debug('{list_0_len}'.format(list_0_len=len(list_0))) logger.debug('{list_1_len}'.format(list_1_len=len(list_1))) logger.debug('{list_01_len}'.format(list_01_len=len(list_0) + len(list_1))) bit_dict["0"] = list_0 bit_dict["1"] = list_1 tree_dict[path + "_" + str(b) + "v0"] = list_0 tree_dict[path + "_" + str(b) + "v1"] = list_1 tree_dict[path] = [] done = 1 logger.debug('tree_dict: {tree_dict}'.format(tree_dict=tree_dict)) return done, tree_dict
[docs]class Slave: """Creates a AXI4-Lite Slave """ def __init__(self, data_bus_size=32, size_indicate_bytes=0): self.logger = xml2vhdl_logging.config_logger(name=__name__, class_name=self.__class__.__name__) self.dict = {} self._key_table = {} self._node_num = 0 self._level = 0 self.reverse_dict_key = [] self.decode_dict = {} self.size_indicate_bytes = size_indicate_bytes self.data_bit_size = data_bus_size self.data_byte_size = self.data_bit_size / 8 self.atom = self.get_atomicity() self.size_normalizer = self.get_size_normalizer() self.default_mask = "0x" + "FF"*self.data_byte_size
[docs] def reset(self): """Reset the slave Returns: None """ self.dict = {} self._key_table = {} self._node_num = 0 self._level = 0 self.reverse_dict_key = [] self.decode_dict = {}
[docs] def get_atomicity(self): """ Returns: (???): """ if self.size_indicate_bytes == 0: return 1 else: return self.data_byte_size
[docs] def get_size_normalizer(self): """ Returns: (???): """ if self.size_indicate_bytes == 0: return self.data_byte_size else: return 1
[docs] def get_range_from_mask(self, mask, split="0"): """Return range from mask Args: mask (???): split (???): Returns: (???): """ if split is None: split = 0 else: split = int(split) mask_int = int(mask, 16) bit_idx = 0 range_lo = -1 range_hi = -1 while mask_int != 0: if (mask_int & 0x1) != 0: if range_lo == -1: range_lo = bit_idx else: range_hi = bit_idx mask_int /= 2 bit_idx += 1 range_hi += split * self.data_bit_size range_lo += split * self.data_bit_size return range_hi, range_lo
[docs] def populate_node_dictionary(self, xml_node, xml_parent_node, hier_level): """Populate the node dictionary Args: xml_node (???): xml_parent_node (???): hier_level (???): Returns: (???): """ this_dict = dict() this_dict['hier_level'] = hier_level this_dict['this_node'] = xml_node this_dict['parent_node'] = xml_parent_node this_dict['this_id'] = xml_node.get('id') if xml_parent_node is not None: this_dict['parent_id'] = xml_parent_node.get('id') else: this_dict['parent_id'] = None this_dict['complete_id'] = None this_dict['addressable_id'] = None this_dict['prev_id'] = None this_dict['address'] = xml_node.get('address') this_dict['decoder_mask'] = None this_dict['absolute_offset'] = None this_dict['mask'] = xml_node.get('mask') this_dict['permission'] = xml_node.get('permission') this_dict['hw_permission'] = xml_node.get('hw_permission') this_dict['hw_rst'] = xml_node.get('hw_rst') if xml_node.get('mask') is not None: this_dict['range'] = self.get_range_from_mask(xml_node.get('mask'), xml_node.get('split')) else: this_dict['range'] = [self.data_bit_size - 1, 0] if xml_node.get('hw_prio') == "bus": this_dict['hw_prio'] = False else: this_dict['hw_prio'] = True child_list = [] for child in xml_node: child_list.append(child.get('id')) this_dict['child_id'] = child_list this_dict['parent_key'] = None this_dict['child_key'] = [] this_dict['addressable'] = None this_dict['link'] = xml_node.get('link') this_dict['array'] = xml_node.get('array') this_dict['array_offset'] = xml_node.get('array_offset') this_dict['array_idx'] = xml_node.get('array_idx') if xml_node.get('size') is None: this_dict['size'] = str(self.atom) else: this_dict['size'] = str(xml_node.get('size')) this_dict['type'] = "undef" if xml_node.get('description') is None: this_dict['description'] = "Missing description" else: this_dict['description'] = xml_node.get('description') this_dict['hw_dp_ram'] = xml_node.get('hw_dp_ram') if xml_node.get('hw_dp_ram_bus_lat') is None: this_dict['hw_dp_ram_bus_lat'] = "1" else: this_dict['hw_dp_ram_bus_lat'] = xml_node.get('hw_dp_ram_bus_lat') if xml_node.get('hw_dp_ram_logic_lat') is None: this_dict['hw_dp_ram_logic_lat'] = "1" else: this_dict['hw_dp_ram_logic_lat'] = xml_node.get('hw_dp_ram_logic_lat') if xml_node.get('hw_dp_ram_width') is None: this_dict['hw_dp_ram_width'] = str(self.data_bit_size) else: this_dict['hw_dp_ram_width'] = xml_node.get('hw_dp_ram_width') if xml_node.get('hw_dp_ram_init_file') is None: this_dict['hw_dp_ram_init_file'] = "" else: this_dict['hw_dp_ram_init_file'] = xml_node.get('hw_dp_ram_init_file') if xml_node.get('hw_dp_ram_init_file_format') is None: this_dict['hw_dp_ram_init_file_format'] = "hex" else: this_dict['hw_dp_ram_init_file_format'] = xml_node.get('hw_dp_ram_init_file_format') if xml_node.get('split') is not None: this_dict['split'] = xml_node.get('split') else: this_dict['split'] = "-1" if xml_node.get('last_split') is not None: this_dict['last_split'] = xml_node.get('last_split') else: this_dict['last_split'] = "0" this_dict['hw_ignore'] = xml_node.get('hw_ignore') return this_dict
[docs] def populate_subnodes(self, node, target_level, current_level): """# populate all subnodes of a node of hierarchical level defined by target_level Args: node (???): target_level (???): current_level (???): Returns: (???): """ if node is None: return -1 else: for subnode in node: if current_level == target_level: self.dict[str(self._node_num)] = self.populate_node_dictionary(subnode, node, current_level + 1) self._key_table[str(subnode)] = int(self._node_num) self._node_num += 1 else: self.populate_subnodes(subnode, target_level, current_level + 1)
[docs] def populate_dict(self, root_node): """Populate the dictionary Args: root_node (???): Returns: (???): """ self.reset() # populate root self.dict[str(0)] = self.populate_node_dictionary(root_node, None, 0) self._key_table[str(root_node)] = 0 self._level = 0 self._node_num = 1 old_node_num = 1 # populate all nodes recursively till there are no added nodes while True: self.populate_subnodes(root_node, self._level, 0) if old_node_num != self._node_num: self._level += 1 old_node_num = self._node_num else: break self.reverse_dict_key = reverse_key_order(self.dict)
def get_node_number(self): return self._node_num def get_hierarchical_level_number(self): return self._level
[docs] def get_object(self, object_type_list, include_ignored=True): """Return a list of requested objects with specified type Specific types are: * ``block`` * ``register_without_bitfield`` * ``bitfield`` Args: object_type_list (list of str): include_ignored (bool, optional): Default value is ``True`` Returns: (list): Sorted list of requested object types """ node_list = [] sorted_list = [] for node_id in reverse_key_order(self.dict): node_id = str(node_id) if (self.dict[node_id]['type'] in object_type_list) or (self.dict[node_id]['type'] != "undef" and object_type_list == ["all"]): if self.dict[node_id]['hw_ignore'] != "yes" or include_ignored: node_list.append([self.dict[node_id]['addressable'], self.dict[node_id]['range'][1], node_id]) for node_id in sorted(node_list): sorted_list.append(node_id[2]) return sorted_list
[docs] def compute_decoder_mask(self): """ Returns: None """ addresses = [] for x in self.get_object(["block", "register_with_bitfield", "register_without_bitfield"]): addresses.append(self.dict[str(x)]['addressable']) add_num = len(addresses) logger.info('Addresses are: {add_num}' .format(add_num=add_num)) baddr = np.zeros((add_num, 32), dtype=np.int) tree_dict = {} # building address array a = 0 for add in sorted(addresses): bit_mask = 0x80000000 for n in range(32): if add & bit_mask != 0: baddr[a, 31 - n] = 1 bit_mask = bit_mask >> 1 a = a + 1 self.logger.debug('baddr: {baddr}'.format(baddr=baddr)) tree_dict["-1v0"] = baddr while True: done, tree_dict = dec_route_add(tree_dict) if done == 0: break for bit_path in tree_dict: if tree_dict[bit_path] != []: add_bits = tree_dict[bit_path][0] add = "" for n in reversed(range(32)): add = add + str(add_bits[n]) bit_list = bit_path.split("_") mask = 0 for bit in bit_list: x = int(re.sub(r'v.*', "", bit)) if x >= 0: mask = mask | (1 << x) self.decode_dict[str(int(add, 2))] = mask self.logger.debug('Decode Dict:') for address in sorted(self.decode_dict.keys()): self.logger.debug('\taddr:') self.logger.debug('\t{address}'.format(address=address)) self.logger.debug('\tmask:') self.logger.debug('\t{mask}'.format(mask=hex(self.decode_dict[address])))
[docs] def get_max_id_len(self): """Return maximum addressable_id length Returns: (int): Length """ length = 0 for node_id in reverse_key_order(self.dict): tmp = self.dict[node_id]['addressable_id'] tmp = re.sub(r"\|", "", tmp) tmp = re.sub(r"/", "", tmp) tmp = re.sub(r"\*", "", tmp) tmp = re.sub(r"<<", "_", tmp) tmp = re.sub(r">>", "", tmp) if len(tmp) > length: length = len(tmp) return length
[docs] def get_size(self): """ Returns: (int): Size """ add_max = 0 for node_id in self.get_object(["all"]): node_dict = self.dict[node_id] add = node_dict['addressable'] self.logger.debug('{add}'.format(add=add)) if add is None: self.logger.debug('{complete_id}'.format(complete_id=node_dict['complete_id'])) self.logger.debug('{type}'.format(type=node_dict['type'])) if int(node_dict['size']) > self.atom: add += int(node_dict['size']) * self.size_normalizer if abs(add) > abs(add_max): add_max = add n = 0 while True: if abs(2 ** n) >= abs(add_max): break else: n += 1 return 2 ** n
[docs] def fill_child(self): """OB fill child/parent fields Returns: None """ for this_node in self.dict: if self.dict[this_node]['parent_node'] is not None: parent_key = self._key_table[str(self.dict[this_node]['parent_node'])] self.dict[this_node]['parent_key'] = str(parent_key) self.dict[str(parent_key)]['child_key'].append(int(this_node))
[docs] def fill_absolute_offset(self): """OB fill absolute offset field walking from current node to root and accumulating offset Returns: None """ # absolute_offset = 0 for node_id in reverse_key_order(self.dict): if self.dict[node_id]['address'] is not None: absolute_offset = int(self.dict[node_id]['address'], 16) test_node = node_id while True: parent_id = self.dict[test_node]['parent_key'] if parent_id is None: break else: if self.dict[parent_id]['address'] is not None: absolute_offset += int(self.dict[parent_id]['address'], 16) test_node = parent_id self.dict[node_id]['absolute_offset'] = absolute_offset
[docs] def fill_decoder_mask(self): """OB fill decoder mask field Returns: None """ self.compute_decoder_mask() for node_id in reverse_key_order(self.dict): if self.dict[node_id]['addressable'] is not None: add = str(self.dict[node_id]['addressable']) add = str(int(add)) self.dict[node_id]['decoder_mask'] = self.decode_dict[add]
[docs] def fill_complete_id(self): """OB fill complete id field Returns: None """ for node_id in reverse_key_order(self.dict): current_node = node_id complete_id = self.dict[current_node]['this_id'] prev_id = "" while True: if self.dict[current_node]['parent_key'] is None: break else: complete_id = self.dict[current_node]['parent_id'] + "." + complete_id if prev_id == "": prev_id = self.dict[current_node]['parent_id'] else: prev_id = self.dict[current_node]['parent_id'] + "." + prev_id current_node = str(self.dict[current_node]['parent_key']) self.dict[node_id]['complete_id'] = complete_id self.dict[node_id]['addressable_id'] = re.sub(r"^(\w*\.)", "", complete_id) self.dict[node_id]['prev_id'] = prev_id
[docs] def propagate_reset_value(self): """OB propagate reset value to child nodes Returns: None """ for node_id in self.get_object(["bitfield"]): parent_node_id = self.dict[node_id]['parent_key'] if self.dict[node_id]['hw_rst'] is None and self.dict[parent_node_id]['hw_rst'] is not None: try: rst_val = int(self.dict[parent_node_id]['hw_rst'], 16) rst_val = rst_val & int(self.dict[node_id]['mask'], 16) rst_val = rst_val >> int(self.dict[node_id]['range'][1]) self.dict[node_id]['hw_rst'] = string_io.hex_format(rst_val) except: if self.dict[node_id]['range'][0] == -1: rst_val = self.dict[parent_node_id]['hw_rst'] + "(" + str(self.dict[node_id]['range'][1]) + ")" else: rst_val = self.dict[parent_node_id]['hw_rst'] + "(" + str(self.dict[node_id]['range'][0]) + " downto " + str(self.dict[node_id]['range'][1]) + ")" self.dict[node_id]['hw_rst'] = rst_val
[docs] def get_reset_generics(self): """OB create generics resets dictionary Returns: None """ reset_generics_dict = {} for node_id in self.get_object(["bitfield", "register_with_bitfield", "register_without_bitfield"]): if self.dict[node_id]['hw_rst'] is not None: try: int(self.dict[node_id]['hw_rst'], 16) except: # print self.dict[node_id]['hw_rst'] if not "(" in self.dict[node_id]['hw_rst'] and self.dict[node_id]['hw_rst'] != "no" and self.is_split_register(self.dict[node_id]) is False: lo_idx = self.dict[node_id]['range'][1] hi_idx = self.dict[node_id]['range'][0] if hi_idx == -1: reset_generics_dict[self.dict[node_id]['hw_rst']] = "std_logic" else: reset_generics_dict[self.dict[node_id]['hw_rst']] = "std_logic_vector(" + str(hi_idx - lo_idx) + " downto 0)" return reset_generics_dict
[docs] def default_permission(self): """OB write default permission where it is not specified Returns: None """ for node_id in reverse_key_order(self.dict): if self.dict[node_id]['permission'] is None: self.dict[node_id]['permission'] = 'rw'
[docs] def default_reset(self): """OB write default reset where it is not specified Returns: None """ for node_id in reverse_key_order(self.dict): if self.dict[node_id]['hw_rst'] is None: self.dict[node_id]['hw_rst'] = "0x0"
[docs] def fill_type(self): """OB fill type field Returns: None """ for node_id in reverse_key_order(self.dict): if int(self.dict[node_id]['size']) > self.atom: self.dict[node_id]['type'] = "block" elif self.dict[node_id]['child_key'] == []: if self.dict[node_id]['absolute_offset'] is not None: self.dict[node_id]['type'] = "register_without_bitfield" elif self.dict[node_id]['mask'] is not None and int(self.dict[node_id]['size']) == self.atom: self.dict[node_id]['type'] = "bitfield" self.dict[self.dict[node_id]['parent_key']]['type'] = "register_with_bitfield"
[docs] def fill_addressable(self): """OB fill addressable field An object is addressable if it is: * ``block`` * ``register_with_bitfield`` * ``register_without_bitfield`` * ``bitfield`` Returns: None """ for node_id in reverse_key_order(self.dict): if self.dict[node_id]['type'] == "block": self.dict[node_id]['addressable'] = self.dict[node_id]['absolute_offset'] elif self.dict[node_id]['type'] == "register_without_bitfield": self.dict[node_id]['addressable'] = self.dict[node_id]['absolute_offset'] elif self.dict[node_id]['type'] == "bitfield": self.dict[node_id]['addressable'] = self.dict[self.dict[node_id]['parent_key']]['absolute_offset'] self.dict[self.dict[node_id]['parent_key']]['addressable'] = self.dict[self.dict[node_id]['parent_key']]['absolute_offset']
[docs] def get_nof_not_ignored_registers(self): """OB return number of registers Returns: (int): """ return len(self.get_object(["register_without_bitfield", "register_with_bitfield"], include_ignored=False))
[docs] def get_nof_not_ignored_blocks(self): """OB return number of blocks Returns: (int): """ return len(self.get_object(["block"], include_ignored=False))
[docs] def check_offset(self): """OB check for clashing addresses Returns: None """ for x in self.get_object(["block", "register_with_bitfield", "register_without_bitfield"]): this_dict = self.dict[x] this_dict_size = int(this_dict['size']) * self.size_normalizer for y in self.get_object(["block", "register_with_bitfield", "register_without_bitfield"]): other_dict = self.dict[y] other_dict_size = int(other_dict['size']) * self.size_normalizer if this_dict['complete_id'] != other_dict['complete_id'] and "$s0_split$" not in this_dict['complete_id'] and "$s0_split$" not in other_dict['complete_id']: if this_dict['addressable'] >= other_dict['addressable'] and this_dict['addressable'] < other_dict['addressable'] + other_dict_size: # TODO Check hex is OK in the following formats. self.logger.error('Error1: conflicting offsets:') self.logger.error('\t{complete_id} {addressable}' .format(complete_id=this_dict['complete_id'], addressable=hex(this_dict['addressable']))) self.logger.error('\t{complete_id} {addressable}' .format(complete_id=other_dict['complete_id'], addressable=hex(other_dict['addressable']))) sys.exit(1) if this_dict['addressable'] + this_dict_size - 1 >= other_dict['addressable'] and this_dict['addressable'] + this_dict_size - 1 < other_dict['addressable'] + other_dict_size: self.logger.error('Error2: conflicting offsets:') self.logger.error('\t{complete_id} at offset {addressable}' .format(complete_id=this_dict['complete_id'], addressable=hex(this_dict['addressable']))) self.logger.error('\t{complete_id} at offset {addressable}' .format(complete_id=other_dict['complete_id'], addressable=hex(other_dict['addressable']))) self.logger.error('Exiting...') sys.exit(1)
[docs] def check_address_requirement(self): """OB check that the size of the slave is smaller than absolute offset Returns: None """ for x in self.get_object(["block"]): this_dict = self.dict[x] this_addr = this_dict['addressable'] this_size = int(this_dict['size']) * self.size_normalizer if this_addr & (this_size - 1) != 0: self.logger.error('It should be (absolute_offset & (size-1)) = 0x0') self.logger.error('\t{complete_id} does not meet this requirement!' .format(complete_id=this_dict['complete_id'])) self.logger.error('\t{complete_id} absolute offset is: {offset}' .format(complete_id=this_dict['complete_id'], offset=hex(this_addr))) self.logger.error('\t{complete_id} size is: {bytes}' .format(complete_id=this_dict['complete_id'], bytes=this_size)) self.logger.error('Exiting...') sys.exit(1)
[docs] def check_bitfield(self): """OB check for clashing bit-field Returns: None """ for x in self.get_object(["register_with_bitfield"]): this_dict = self.dict[x] for m in this_dict['child_key']: for n in this_dict['child_key']: if m != n: mask_0 = int(self.dict[str(m)]['mask'], 16) mask_1 = int(self.dict[str(n)]['mask'], 16) if mask_0 & mask_1 != 0: self.logger.error('conflicting bitfields!') self.logger.error('\t{complete_id} conflicts with: {conflict_id}' .format(complete_id=self.dict[str(m)]['complete_id'], conflict_id=self.dict[str(n)]['complete_id'])) self.logger.error('Exiting...') sys.exit(1)
[docs] def check_reserved_words(self): """OB check for reserved words occurrence Returns: None """ for node_id in self.dict.keys(): if string.lower(self.dict[node_id]['this_id']) in reserved_word.vhdl_reserved_words: self.logger.error('Keyword: "{id}" is used as node_id!' .format(id=self.dict[node_id]['this_id'])) self.logger.error('Exiting...') sys.exit(1)
[docs] def is_wide_register(self, node_dict): """ Args: node_dict (dict): Returns: (???): """ ret = False # if node_dict['range'] != None: # if node_dict['range'][0] > self.data_bit_size - 1: # ret = True if node_dict['mask'] != None: if int(node_dict['mask'], 16) > int(self.default_mask,16): ret = True return ret
[docs] def is_split_register(self, node_dict): """ Args: node_dict (dict): Returns: (???): """ ret = False if node_dict['split'] != None: if int(node_dict['split']) >= 0: ret = True return ret
[docs] def get_split_splice(self, node_dict): """ Args: node_dict (dict): Returns: (???): """ if node_dict['split'] == None: return -1 else: return int(node_dict['split'])