Source code for py_register_machine2.core.parts

#!/usr/bin/python3

"""
**py_register_machine2.core.parts**: Basic parts of the register machine

"""

from ..engine_tools.operations import bitsetxor


[docs]class BUS(object): """ .. _BUS: **The BUS object** A BUS object connects the Processor with one or more Memory alike operating devices (WordDevice_). Before the BUS starts working, devices can be registered, the address spaces of the devices are organized incremental:: d1 = WordDevice(4) d2 = WordDevice(5) d3 = WordDevice(19) b = BUS() addr1 = b.register_device(d1) addr2 = b.register_device(d2) addr3 = b.register_device(d3) print( (addr1, addr2, addr3)) # (0, 4, 9) Once the BUS started working (a read/write operation has been used) BUS.register_device will raise a BUSSetupError. If the addresspace of the BUS is too small do hold a new device, BUS.register_device will raise a BUSSetupError. The number of read/write actions can be observed by accessing the variables ``reads`` and ``writes`` """ def __init__(self, width = 64, debug = 0): self.width = width self.max_addr = 2 ** width self.start_addresses = {} self.index = {} self.devices = [] self.current_max_offset = 0 self.debug = debug self._lock = False self.reads = 0 self.writes = 0 self.truncate = Integer(width = width)
[docs] def register_device(self, word_device): """ .. _register_device: Register the WordDevice_ ``word_device`` in the bus returns the start address of the device. raises: BUSSetupError_, if the device cannot be registered. """ if(self._lock): raise BUSSetupError("BUS already locked.") size = word_device.size if(self.current_max_offset + size >= self.max_addr): raise BUSSetupError("Addresspace({}) would exceed width of BUS({})".format(self.current_max_offset+ size, self.width)) self.start_addresses[word_device] = self.current_max_offset res = self.current_max_offset self.current_max_offset += size self.index[range(res, self.current_max_offset)] = word_device self.devices.append(word_device) return res
[docs] def read_word(self, offset): """ .. _read_word: Read one word from a device. The offset is ``device_addr + device_offset``, e.g.:: offset = 3 # third word of the device offset += addr2 b.read_word(offset) # reads third word of d2. Truncates the value according to ``width``. May raise BUSError_, if the offset exceeds the address space. """ self._lock = True if(offset >= self.current_max_offset): raise BUSError("Offset({}) exceeds address space of BUS({})".format(offset, self.current_max_offset)) self.reads += 1 for addresspace, device in self.index.items(): if(offset in addresspace): if(self.debug > 5): print("BUS::read({}) | startaddress({})> {}".format(offset, self.start_addresses[device], device.read(offset - self.start_addresses[device]))) self.truncate.setvalue( device.read(offset - self.start_addresses[device])) return self.truncate.getvalue()
[docs] def write_word(self, offset, word): """ .. _write_word: Writes one word from a device, see read_word_. """ self._lock = True if(offset >= self.current_max_offset): raise BUSError("Offset({}) exceeds address space of BUS({})".format(offset, self.current_max_offset)) self.writes += 1 self.truncate.setvalue(word) for addresspace, device in self.index.items(): if(offset in addresspace): device.write(offset - self.start_addresses[device], self.truncate.getvalue())
def device_count(self): return len(self.start_addresses)
[docs]class Integer(object): """ .. _Integer: The register machine may have a special width. This is handled by the Integer objects. Automatically truncates the value to the defined width. Use setvalue_ and getvalue_ or setuvalue_ and getuvalue_ to access the value. Uses a bitset internally. """ def __init__(self, value = 0, width = 64): self.width = width self.mask = 2 ** width - 1 self._value = 0 self._sign = 0 self.setvalue(value)
[docs] def setvalue(self, value): """ .. _setvalue: Set the signed value of the Integer. """ self._value = abs(value) self._sign = 0 if(value < 0): self._sign = 1
[docs] def getvalue(self): """ .. _getvalue: Get the signed value of the Integer, truncate it and handle Overflows. """ bitset = [0] * self.width zero = [1] * self.width for shift in range(self.width): bitset[shift] = (self._value & (1 << shift)) >> shift sign = 0 if((not bitset[-1]) and self._sign): bitset[-1] = 1 sign = 1 elif(bitset[-1]): bitset = bitsetxor(bitset, zero) sign = 1 value = [ bitset[shift] << shift for shift in range(self.width - 1)] value = sum(value) if(sign): return -1 * value return value
[docs] def setuvalue(self, value): """ .. _setuvalue: Set the unsigned value of the Integer. """ self._value = value self._sign = 0
[docs] def getuvalue(self): """ .. _getuvalue: Get the unsigned value of the Integer, truncate it and handle Overflows. """ bitset = [0] * self.width zero = [1] * self.width for shift in range(self.width): bitset[shift] = (self._value & (1 << shift)) >> shift if(self._sign): bitset = bitsetxor(zero, bitset) value = [ bitset[shift] << shift for shift in range(self.width)] return sum(value)
[docs]class WordDevice(object): """ .. _WordDevice: Base Device for the register machine. The words have the width ``width`` and are stored in an Integer_ object. Values are accessed by read_ and write_ """ def __init__(self, size, width = 64, mode = 0b11, debug = 0): self.size = size self.repr_ = [Integer(width = width) for i in range(size)] self.mode = mode self.debug = debug
[docs] def read(self, offset): """ .. _read: Returns the value of the memory word at ``offset``. Might raise WriteOnlyError_, if the device is write-only. Might raise AddressError_, if the offset exceeds the size of the device. """ if(not self.mode & 0b01): raise WriteOnlyError("Device is Write-Only") if(offset >= self.size): raise AddressError("Offset({}) not in address space({})".format(offset, self.size)) return self.repr_[offset].getvalue()
[docs] def write(self, offset, value): """ .. _write: Writes the memory word at ``offset`` to ``value``. Might raise ReadOnlyError_, if the device is read-only. Might raise AddressError_, if the offset exceeds the size of the device. """ if(not self.mode & 0b10): raise ReadOnlyError("Device is Read-Only") if(offset >= self.size): raise AddressError("Offset({}) not in address space({})".format(offset, self.size)) self.repr_[offset].setvalue(value)
[docs]class Register(object): """ .. _Register: Basically hold one value and permitt read/write operations. There may be several subclasses, like Input/Output Register. The name will be used by the assembler. """ def __init__(self, name, width = 64): self.repr_ = Integer(0, width = width) self.name = name self.width = width
[docs] def read(self): """ Return the content of the Register, may execute a function """ return self.repr_.getvalue()
[docs] def write(self, value): """ Set the content of the Register, may execute a function """ self.repr_.setvalue(value)
[docs]class BUSSetupError(Exception): """ .. _BUSSetupError: raised by a BUS if the setup failed. """ def __init__(self, *args): Exception.__init__(self, *args)
[docs]class BUSError(Exception): """ .. _BUSError: raised by a BUS if an operation failed. """ def __init__(self, *args): Exception.__init__(self, *args)
[docs]class ReadOnlyError(Exception): """ .. _ReadOnlyError: raised by a device if it is read-only """ def __init__(self, *args): Exception.__init__(self, *args)
[docs]class WriteOnlyError(Exception): """ .. _WriteOnlyError: raised by a device if it is write-only """ def __init__(self, *args): Exception.__init__(self, *args)
[docs]class AddressError(Exception): """ .. _AddressError: raised by a device if the requested offset exceeds the size of the device """ def __init__(self, *args): Exception.__init__(self, *args)