blob: 1e19d1c2f86ab508b903fe820e10a143d36cb64a [file] [log] [blame]
# Copyright lowRISC contributors.
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
# SPDX-License-Identifier: Apache-2.0
'''Code to load instruction words into a simulator'''
import struct
from typing import Iterator, List, Optional, Tuple
from .constants import ErrBits
from .isa import INSNS_FILE, OTBNInsn
from .insn import INSN_CLASSES
from .state import OTBNState
MNEM_TO_CLASS = {cls.insn.mnemonic: cls for cls in INSN_CLASSES}
class IllegalInsn(OTBNInsn):
'''A catch-all subclass of Instruction for bad data
This handles anything that doesn't decode correctly. Doing so for OTBN is
much easier than if we wanted to support compressed-mode (RV32IC), because
we don't need to worry about whether we have 16 or 32 bits of rubbish.
Note that we declare this with an opcode of zero. Note that this implies
the bottom two bits are 0, which would imply a compressed instruction, so
we know this doesn't match any real instruction.
'''
def __init__(self, pc: int, raw: int, msg: str) -> None:
super().__init__(raw, {})
self.msg = msg
# Override the memoized disassembly for the instruction, avoiding us
# disassembling the underlying DummyInsn.
self._disasm = (pc, '?? 0x{:08x}'.format(raw))
def execute(self, state: OTBNState) -> Optional[Iterator[None]]:
state.stop_at_end_of_cycle(ErrBits.ILLEGAL_INSN)
return None
class EmptyInsn(OTBNInsn):
'''A subclass of Instruction that represents non-existent data
This is what we generate on a fetch error, where we don't really have any
instruction data at all.
'''
# We don't have any instruction data
has_bits = False
def __init__(self, pc: int) -> None:
super().__init__(0, {})
# Override the memoized disassembly for the instruction, avoiding us
# disassembling the underlying DummyInsn.
self._disasm = (pc, '?? (no instruction data)')
def execute(self, state: 'OTBNState') -> Optional[Iterator[None]]:
state.stop_at_end_of_cycle(ErrBits.IMEM_INTG_VIOLATION)
return None
def _decode_word(pc: int, word: int) -> OTBNInsn:
mnem = INSNS_FILE.mnem_for_word(word)
if mnem is None:
return IllegalInsn(pc, word, 'No legal decoding')
cls = MNEM_TO_CLASS.get(mnem)
if cls is None:
return IllegalInsn(pc, word, f'No insn class for mnemonic {mnem}')
# Decode the instruction. We know that we have an encoding (we checked in
# get_insn_masks).
assert cls.insn.encoding is not None
enc_vals = cls.insn.encoding.extract_operands(word)
# Make sense of these encoded values as "operand values" (doing any
# shifting, sign interpretation etc.)
op_vals = cls.insn.enc_vals_to_op_vals(pc, enc_vals)
return cls(word, op_vals)
def decode_words(base_addr: int,
data: List[Tuple[bool, int]]) -> List[OTBNInsn]:
'''Decode instruction bytes as instructions'''
ret = []
for idx, (vld, w32) in enumerate(data):
pc = 4 * idx
ret.append(_decode_word(pc, w32) if vld else EmptyInsn(pc))
return ret
def decode_file(base_addr: int, path: str) -> List[OTBNInsn]:
with open(path, 'rb') as handle:
raw_bytes = handle.read()
# Each 32-bit word is represented by a 5 bytes, consisting of a validity
# byte (0 or 1) followed by 4 bytes for the word itself.
if len(raw_bytes) % 5:
raise ValueError('Trying to load {} bytes of data from {}, '
'which is not a multiple of 5.'
.format(path, len(raw_bytes)))
data = []
for idx32, (vld, u32) in enumerate(struct.iter_unpack('<BI', raw_bytes)):
if vld not in [0, 1]:
raise ValueError('The validity byte for 32-bit word {} '
'at {} is {}, not 0 or 1.'
.format(idx32, path, vld))
data.append((vld == 1, u32))
return decode_words(base_addr, data)