123 lines
4.7 KiB
Python
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
|