py_register_machine2.core.parts: Basic parts of the register machine
py_register_machine2.core.parts.
AddressError
(*args)[source]¶raised by a device if the requested offset exceeds the size of the device
py_register_machine2.core.parts.
BUS
(width=64, debug=0)[source]¶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
read_word
(offset)[source]¶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.
register_device
(word_device)[source]¶Register the WordDevice word_device
in the bus
returns the start address of the device.
raises: BUSSetupError, if the device cannot be registered.
py_register_machine2.core.parts.
BUSError
(*args)[source]¶raised by a BUS if an operation failed.
py_register_machine2.core.parts.
BUSSetupError
(*args)[source]¶raised by a BUS if the setup failed.
py_register_machine2.core.parts.
Integer
(value=0, width=64)[source]¶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.
py_register_machine2.core.parts.
ReadOnlyError
(*args)[source]¶raised by a device if it is read-only
py_register_machine2.core.parts.
Register
(name, width=64)[source]¶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.
py_register_machine2.core.parts.
WordDevice
(size, width=64, mode=3, debug=0)[source]¶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
read
(offset)[source]¶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.
write
(offset, value)[source]¶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.
py_register_machine2.core.memory.
BUS
(width=64, debug=0)[source]¶The processor’s memory BUS, its devices are ROM and RAM.
py_register_machine2.core.memory.
RAM
(size, width=64, debug=0)[source]¶The Random Access Memory Device
By default the RAM device is filled with zeros. After poweron the Bootcode in the ROM might perform read/write operations on the RAM. If the register machine is to execute programs from the Flash device, this code has to be copied into the RAM.
py_register_machine2.core.memory.
ROM
(size, width=64, debug=0)[source]¶The Read Only Memory Device
of the register machine stores either the boot code (for big programs) or the complete program, if the program is really small.
The ROM is attached to the same BUS as the RAM and always includes the
offset 0
. The Program Counter (PC) of the Processor points to this word on powerup.
Because RAM and ROM are in the same address space the following formula defines the size of RAM and ROM:
addr_space = 2 ** memorybus.width
ram.size + rom.size < addr_space
A write
call will raise ReadOnlyError.
program
(prog, offset=0)[source]¶Write the content of the iterable prog
starting with the optional offset offset
to the device.
Invokes program_word.
program_word
(offset, word)[source]¶Write the word word
to the memory at offset offset
.
Used to write the boot code.
Might raise AddressError, if the offset exceeds the address space.
py_register_machine2.core.device: Device BUS and attached devices
py_register_machine2.core.device.
BUS
(width=64, debug=0)[source]¶The processor’s device BUS, usually the Flash is attached to this BUS, but there might be more devices.
py_register_machine2.core.device.
Flash
(size, width=64, debug=0)[source]¶The Program Flash
If the size of the program exceeds the size of the ROM the program has to be written into the Flash. The Flash is a Read/Write device and contains
The Flash is a WordDevice and attached to the device.BUS
.
program
(prog, offset=0)[source]¶Write the content of the iterable prog
starting with the optional offset offset
to the device.
Invokes program_word
.
program_word
(offset, word)[source]¶Program one word of the Flash device. Might raise AddressError.
py_register_machine2.core.register: Registers for the register machine
py_register_machine2.core.register.
BStreamIORegister
(name, open_stream_in, open_stream_out, width=64)[source]¶Works like StreamIORegister, but open_stream_in
and open_stream_out
are byte streams (like open("fname", "rb")
).
read
operation will read width // 8
bytes and convert them to one int
.write
operation will write width // 8
bytespy_register_machine2.core.register.
OutputRegister
(name, open_stream, width=64)[source]¶Used to print data to the user.
The write
call will convert word
using chr
and write
the resulting str
to open_stream
.
The read
call will return the last written word
.
open_stream
might be a file
(like sys.stdout
) or an io.StringIO
object.
py_register_machine2.core.register.
Register
(name, width=64)[source]¶The basic standard register. Permitts read and write, does not execute any callbacks on read/write.
See also: Register
py_register_machine2.core.register.
StreamIORegister
(name, open_stream_in, open_stream_out, width=64)[source]¶Input/Output Register via streams.
The open_stream_in
has to be readable, open_stream_out
writeable.
py_register_machine2.core.processor.
EnigneControlBits
[source]¶Container for the static engine controll bits. Used by the Processor to handle his ECR.
py_register_machine2.core.processor.
Processor
(f_cpu=None, width=64, interrupts=False, clock_barrier=None, debug=0)[source]¶Fetches Opcodes from the ROM or RAM, decodes them and executes the commands.
Phases in one operation cycle:
Special Register
call
, ret
, push
and pop
Internal Constants
Constants used by the Assembler, should be set using setup_done
ROMEND_LOW
0
)ROMEND_HIGH
RAMEND_LOW
ROMEND_HIGH + rom.size
)RAMEND_HIGH
FLASH_START
0
)FLASH_END
add_interrupt
)Cycles
The number of cycles can be observed by acessing the cycles
variable.
add_interrupt
(interrupt)[source]¶Adds the interrupt to the internal interrupt storage self.interrupts
and
registers the interrupt address in the internal constants.
add_register
(register)[source]¶Adds a new register in the RegisterInterface.
Invokes add_register.
do_cycle
()[source]¶Run one clock cycle of the Processor, works according to processor_phases.
Then all on_cycle_callbacks
are executed and the internal Registers are updated.
If f_cpu
is set and the execution took not long enough,
do_cycle
will wait until the right time for the next cycle.
If clock_barrier
is set, do_cycle
will perform the clock_barrier.wait()
.
Might raise SIGILL, if there is an invalid opcode.
en_dis_able_interrupts
(mask)[source]¶This callback might be used by a Register to enable/disable Interrupts.
mask
is an int
, the Interrupts are bits in this mask, the first registered interrupt
has the bit (1 << 0)
, the n-th Interrupt the bit (1 << (n - 1))
.
If the bit is cleared (0
) the Interrupt will be disabled.
interrupt
(address)[source]¶Interrupts the Processor and forces him to jump to address
.
If push_pc
is enabled this will push the PC to the stack.
register_command
(command)[source]¶Register a Command in the Processor, the Command can now be executed by the Processor.
register_device
(device)[source]¶Registers a device in the device BUS.
Invokes register_device.
register_memory_device
(device)[source]¶Registers a device in the memory BUS.
Invokes register_device.
register_on_cycle_callback
(callback)[source]¶A on cycle callback is executed in every clock cycle of the Processor. No on cycle callback modifies the state of the Processor directly, but it might cause an Interrupt.
The on cycle callback is a function without arguments:
def on_cycle_callback():
print("One cycle done")
The return value of a callback is ignored and the callback must not raise Exceptions, but fatal Errors may stop the engine.
run
()[source]¶Runs do_cycle, until either a stop bit in the ECR is set (see EnigneControlBits), or if an Exception in do_cycle occurs.
setup_done
()[source]¶Finish the setup of the Processor.
This should be the last call before the Processor is used.
Sets the internal constants (used by the assembler) and
sets the Stack Pointer to RAMEND_HIGH, if there is a RAM attached.
If there is no RAM attached, SP will stay 0
.
If there is a RAM attached push_pc
is set.
Might raise SetupError.
py_register_machine2.core.processor.
RegisterInterface
(registers=[], debug=0, width=64)[source]¶Used by the Processor to perform read/write operations on the registers.
add_register
(register)[source]¶Adds the Register register
to the interface.
Will raise a SetupError if the interface is locked (because it is running) or if there is already a Register with the name of the new Register or if the number of Registers would exceed the size of the interface.
Returns the index of the new Register
read
(name_or_index)[source]¶Read a word from the Register with the name name_or_index
or with the index name_or_index
.
name_or_index
hat to be either str
or int
. If the type of name_or_index
is wrong an AttributeError will be raised.
If there is no Register with the specified name or index, a NameError will be raised.
write
(name_or_index, word)[source]¶Write a word in the Register with the name name_or_index
or with the index name_or_index
.
name_or_index
hat to be either str
or int
. If the type of name_or_index
is wrong an AttributeError will be raised.
If there is no Register with the specified name or index, a NameError will be raised.
py_register_machine2.core.commands: Abstract Commands
py_register_machine2.core.commands.
ArgumentType
(type_='register', can_default=False, default=0)[source]¶Represents argument types. Actually there are only the types
"register"
and "const"
but it might be helpful to
use default values. Those are stored in the ArgumentType, too.
This module provides the functions registerargument()
and
constargument()
those will return an ArgumentType(type_ = "register", can_default = False, default = 0)
and an ArgumentType(type_ = "const", can_default = False, default = 0)
py_register_machine2.core.commands.
ArithmeticCommand
(mnemonic, opcode, function)[source]¶Used for calculation commands, numargs
is always 2,
both arguments are Registers.
Example: The add
command:
add_function = lambda a,b: a+b
add_command = ArithmeticCommand("add", 2, add_function)
py_register_machine2.core.commands.
BaseCommand
(mnemonic, opcode, numargs, argtypes)[source]¶The base class for Commands.
Every Command has to be derived from BaseCommand and provide the following functions:
argtypes must be a list of ArgumentType
objects.
argtypes
()[source]¶Return a list of ArgumentType objects defining the argument types, i.e.:
[ArgumentType(type_ = "register", can_default = False, default = 0), ArgumentType(type_ = "register", can_default = False, default = 0)]
exec
(*args)[source]¶Exec will execute the Action of the Command.
The method will be provided with numargs arguments and
might read/write data via the attributes register_interface
,
membus
and devbus
provided once the Command is registered in the Processor
using register_command.
py_register_machine2.core.commands.
FunctionCommand
(mnemonic, opcode, numargs, function, argtypes)[source]¶Provides a basic handle to create Commands.
The argument function
is a function with at least three arguments:
register_interface
memory_BUS
device_BUS
The function will be able to access the Processor’s RegisterInterface and BUS es through this arguments.
If the function needs any operands the number of additional arguments have to be
in numargs
For arithmetic commands (like add
, mul
,...) see ArithmeticCommand.
Example: ld
Command:
def ld_function(register_interface, memory_BUS, device_BUS, addr_from, to):
from_ = register_interface.read(addr_from)
word = memory_BUS.read_word(from_)
register_interface.write(to, word)
ld_command = FunctionCommand("ld", 34, 2, ld_function, [constargument(), registerargument()])
Example: nop
Command:
def nop_function(register_interface, memory_BUS, device_BUS):
return
nop_command = FunctionCommand("nop", 36, 0, nop_function, [])
py_register_machine2.core.interrupts: Basic module to provide Interrupts.
py_register_machine2.core.interrupts.
Autoreset
(name, processor, overflow_size)[source]¶A really rude form of the Watchdog.
This Interrupt will force the Processor to jump to offset 0
.
py_register_machine2.core.interrupts.
Counter
(address, name, processor, overflow_size)[source]¶A Counter/Timer implementation.
The __init__
method will inject an on_cycle_callback
into the Processor.
This callback will increment the internal counter variable by one.
If the internal counter reaches a predefined value the interrupt
method will be invoked.
py_register_machine2.core.interrupts.
Interrupt
(address, name, processor)[source]¶The Base Class for Interrupts.
If Interrupt.interrupt
is invoked this will invoke Processor.interrupt
and provide the address of the
Interrupt.
This will allow one to place an ISR (Interrupt Service Routine) at this address.