|  | # Copyright lowRISC contributors. | 
|  | # Licensed under the Apache License, Version 2.0, see LICENSE for details. | 
|  | # SPDX-License-Identifier: Apache-2.0 | 
|  |  | 
|  | '''Simple code that understands enough of otbn.hjson to get a memory layout | 
|  |  | 
|  | Each memory will have an associated "window" entry, which has an offset from | 
|  | the start of the IP block's register space. This offset will be treated as an | 
|  | LMA for this memory and these memory LMAs will be used uniformly in | 
|  | OTBN-specific tooling. This avoids the result depending on the address layout | 
|  | of the wider chip. | 
|  |  | 
|  | In particular, note that OTBN ELF binaries will use these LMAs (the VMAs are in | 
|  | OTBN's address space, with both IMEM and DMEM starting at 0). A tool that takes | 
|  | such binaries and incorporates them into a top-level image will need to do | 
|  | address translation (essentially, just adding the base address of the OTBN IP | 
|  | block). | 
|  |  | 
|  | ''' | 
|  |  | 
|  | from typing import Dict, Optional, Tuple | 
|  |  | 
|  | from reggen.reg_block import RegBlock | 
|  |  | 
|  | from .otbn_reggen import load_registers | 
|  |  | 
|  | # A window is represented as (offset, size) | 
|  | _Window = Tuple[int, int] | 
|  |  | 
|  |  | 
|  | def extract_windows(reg_byte_width: int, regs: object) -> Dict[str, _Window]: | 
|  | '''Make sense of the list of register definitions and extract memories''' | 
|  |  | 
|  | windows = {} | 
|  |  | 
|  | assert isinstance(regs, RegBlock) | 
|  | for entry in regs.windows: | 
|  | name = entry.name or 'Window at +{:#x}'.format(entry.offset) | 
|  |  | 
|  | # Should be guaranteed by RegBlock constructor | 
|  | assert name not in windows | 
|  |  | 
|  | windows[name] = (entry.offset, entry.size_in_bytes) | 
|  |  | 
|  | return windows | 
|  |  | 
|  |  | 
|  | _LAYOUT = None  # type: Optional[Dict[str, Tuple[int, int]]] | 
|  |  | 
|  |  | 
|  | def get_memory_layout() -> Dict[str, Tuple[int, int]]: | 
|  | '''Read otbn.hjson to get IMEM / DMEM layout | 
|  |  | 
|  | Returns a dictionary with two entries, keyed 'IMEM' and 'DMEM'. The value | 
|  | at each entry is a pair (offset, size_in_bytes). | 
|  |  | 
|  | ''' | 
|  | global _LAYOUT | 
|  | if _LAYOUT is not None: | 
|  | return _LAYOUT | 
|  |  | 
|  | reg_byte_width, registers = load_registers() | 
|  | windows = extract_windows(reg_byte_width, registers) | 
|  |  | 
|  | xmem_names = {'IMEM', 'DMEM'} | 
|  | for name in xmem_names: | 
|  | if name not in windows: | 
|  | raise RuntimeError("otbn.hjson doesn't have a window called {}." | 
|  | .format(name)) | 
|  | if len(windows) != 2: | 
|  | raise RuntimeError("Unexpected windows in otbn.hjson: {}" | 
|  | .format(list(set(windows.keys()) - xmem_names))) | 
|  |  | 
|  | _LAYOUT = windows | 
|  | return _LAYOUT | 
|  |  | 
|  |  | 
|  | if __name__ == '__main__': | 
|  | for name, (off, width) in get_memory_layout().items(): | 
|  | print('{}: {} bytes; LMA {:#x}'.format(name, width, off)) |