| # Copyright lowRISC contributors. |
| # Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| # SPDX-License-Identifier: Apache-2.0 |
| |
| import random |
| from typing import Dict, List, Tuple |
| |
| |
| class InitData: |
| '''Initialised data, written to the random binary to be loaded.''' |
| def __init__(self, values: Dict[int, int]): |
| self._values = values |
| |
| def as_segs(self) -> Dict[int, List[int]]: |
| '''Convert from dictionary representation to segments |
| |
| The dictionary representation is keyed by 4-byte aligned addresses and |
| has values 32-bit unsigned numbers. The segment representation maps |
| 4-byte aligned base addresses to lists of 32-bit unsigned numbers (and |
| the segments are disjoint). |
| |
| ''' |
| segs = {} |
| cur_seg = None |
| |
| for addr, value in self._values.items(): |
| assert addr & 3 == 0 |
| assert 0 <= value < (1 << 32) |
| |
| if cur_seg is not None: |
| base_addr, next_addr, values = cur_seg |
| if addr == next_addr: |
| values.append(value) |
| cur_seg = (base_addr, next_addr + 4, values) |
| continue |
| |
| assert base_addr not in segs |
| segs[base_addr] = values |
| cur_seg = None |
| |
| assert cur_seg is None |
| cur_seg = (addr, addr + 4, [value]) |
| |
| if cur_seg is not None: |
| base_addr, next_addr, values = cur_seg |
| assert base_addr not in segs |
| segs[base_addr] = values |
| |
| return segs |
| |
| def as_json(self) -> List[Tuple[int, int]]: |
| '''Return init_data, as it should be serialized to json''' |
| return [(addr, data) for addr, data in self._values.items()] |
| |
| def keys(self) -> List[int]: |
| return list(self._values.keys()) |
| |
| @staticmethod |
| def read(parsed: object) -> 'InitData': |
| '''Read init_data as parsed from json''' |
| if not isinstance(parsed, list): |
| raise ValueError('init_data is not a list.') |
| init_data = {} |
| for idx, item in enumerate(parsed): |
| if not (isinstance(item, list) and len(item) == 2): |
| raise ValueError('Item {} of init_data is not a length 2 list.' |
| .format(item)) |
| addr, value = item |
| |
| if not (isinstance(addr, int) and isinstance(value, int)): |
| raise ValueError('Item {} of init_data has addr or value ' |
| 'that is not an int.' |
| .format(idx)) |
| |
| if addr < 0 or addr & 3: |
| raise ValueError('Item {} of init_data has ' |
| 'an invalid address, 0x{:x}.' |
| .format(idx, addr)) |
| if not (0 <= value < (1 << 32)): |
| raise ValueError('Item {} of init_data has ' |
| 'invalid data ({} is not a u32).' |
| .format(idx, value)) |
| |
| init_data[addr] = value |
| |
| return InitData(init_data) |
| |
| @staticmethod |
| def gen(dmem_size: int) -> 'InitData': |
| '''Generate some initialised data |
| |
| This will be inserted into the program as initialised data (pre-loaded). |
| The dictionary maps (word-aligned) byte addresses to u32 values. |
| |
| ''' |
| assert dmem_size > 0 |
| |
| values = {} |
| byte_width = 32 |
| word_width = byte_width // 4 |
| hi_wword = dmem_size // byte_width - 1 |
| for blob_idx in range(random.randint(1, 4)): |
| blob_wword = random.randint(0, hi_wword) |
| blob_addr = byte_width * blob_wword |
| for word_idx in range(word_width): |
| values[blob_addr + 4 * word_idx] = random.getrandbits(32) |
| |
| return InitData(values) |