| # Copyright lowRISC contributors. | |
| # Licensed under the Apache License, Version 2.0, see LICENSE for details. | |
| # SPDX-License-Identifier: Apache-2.0 | |
| '''Code representing the entire chip for reggen''' | |
| from typing import Dict, List, Optional, Tuple, Union | |
| from reggen.ip_block import IpBlock | |
| from reggen.params import ReggenParams | |
| from reggen.reg_block import RegBlock | |
| from reggen.window import Window | |
| _IFName = Tuple[str, Optional[str]] | |
| _Triple = Tuple[int, str, IpBlock] | |
| class Top: | |
| '''An object representing the entire chip, as seen by reggen. | |
| This contains instances of some blocks (possibly multiple instances of each | |
| block), starting at well-defined base addresses. It may also contain some | |
| windows. These are memories that don't have their own comportable IP (so | |
| aren't defined in a block), but still take up address space. | |
| ''' | |
| def __init__(self, | |
| regwidth: int, | |
| blocks: Dict[str, IpBlock], | |
| instances: Dict[str, str], | |
| if_addrs: Dict[Tuple[str, Optional[str]], int], | |
| windows: List[Window], | |
| attrs: Dict[str, str]): | |
| '''Class initializer. | |
| regwidth is the width of the registers (which must match for all the | |
| blocks) in bits. | |
| blocks is a map from block name to IpBlock object. | |
| instances is a map from instance name to the name of the block it | |
| instantiates. Every block name that appears in instances must be a key | |
| of blocks. | |
| if_addrs is a dictionary that maps the name of a device interface on | |
| some instance of some block to its base address. A key of the form (n, | |
| i) means "the device interface called i on an instance called n". If i | |
| is None, this is an unnamed device interface. Every instance name (n) | |
| that appears in connections must be a key of instances. | |
| windows is a list of windows (these contain base addresses already). | |
| attrs is a map from instance name to attr field of the block | |
| ''' | |
| self.regwidth = regwidth | |
| self.blocks = blocks | |
| self.instances = instances | |
| self.if_addrs = if_addrs | |
| self.attrs = attrs | |
| self.window_block = RegBlock(regwidth, ReggenParams()) | |
| # Generate one list of base addresses and objects (with each object | |
| # either a block name and interface name or a window). While we're at | |
| # it, construct inst_to_block_name and if_addrs. | |
| merged = [] # type: List[Tuple[int, Union[_IFName, Window]]] | |
| for full_if_name, addr in if_addrs.items(): | |
| merged.append((addr, full_if_name)) | |
| inst_name, if_name = full_if_name | |
| # The instance name must match some key in instances, whose value | |
| # should in turn match some key in blocks. | |
| assert inst_name in instances | |
| block_name = instances[inst_name] | |
| assert block_name in blocks | |
| # Check that if_name is indeed the name of a device interface for | |
| # that block. | |
| block = blocks[block_name] | |
| assert block.bus_interfaces.has_interface(False, if_name) | |
| for window in sorted(windows, key=lambda w: w.offset): | |
| merged.append((window.offset, window)) | |
| self.window_block.add_window(window) | |
| # A map from block name to the list of its instances. These instances | |
| # are listed in increasing order of the lowest base address of one of | |
| # their interfaces. The entries are added into the dict in the same | |
| # order, so an iteration over items() will give blocks ordered by their | |
| # first occurrence in the address map. | |
| self.block_instances = {} # type: Dict[str, List[str]] | |
| # Walk the merged list in order of increasing base address. Check for | |
| # overlaps and construct block_instances. | |
| offset = 0 | |
| for base_addr, item in sorted(merged, key=lambda pr: pr[0]): | |
| # Make sure that this item doesn't overlap with the previous one | |
| assert offset <= base_addr, item | |
| if isinstance(item, Window): | |
| addrsep = (regwidth + 7) // 8 | |
| offset = item.next_offset(addrsep) | |
| continue | |
| inst_name, if_name = item | |
| block_name = instances[inst_name] | |
| block = blocks[block_name] | |
| lst = self.block_instances.setdefault(block_name, []) | |
| if inst_name not in lst: | |
| lst.append(inst_name) | |
| # This should be guaranteed by the fact that we've already checked | |
| # the existence of a device interface. | |
| assert if_name in block.reg_blocks | |
| reg_block = block.reg_blocks[if_name] | |
| offset = base_addr + reg_block.offset |