tinyriscv/tools/regtool/topgen/top.py

123 lines
4.7 KiB
Python

# 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