| # 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 |