Skip to content

Commit

Permalink
Simulator refactor, rewrite Signal into Port and ReadPort, add a code…
Browse files Browse the repository at this point in the history
… generator oriented model for testing and circuit building. Move gflush screen refresh to use processor events.
  • Loading branch information
alcatrazEscapee committed May 13, 2022
1 parent 027fa5b commit 90090ca
Show file tree
Hide file tree
Showing 21 changed files with 711 additions and 615 deletions.
1 change: 1 addition & 0 deletions blueprints/prototype_color_lights_row.blueprint
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
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
13 changes: 2 additions & 11 deletions processorv5/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,6 @@ def format_frequency(hz: float) -> str:

def manage_processor(proc: Processor, period_ns: int, raw: Connection):
pipe = ConnectionManager(raw)
proc.gpu = AppGPU(proc, pipe)
keyboard = AppControlDevice()
proc.devices.append(keyboard)
proc.event_handle = AppEventHandle(pipe)
Expand Down Expand Up @@ -289,16 +288,6 @@ def manage_processor(proc: Processor, period_ns: int, raw: Connection):
pipe.send('halt')


class AppGPU(GPU):

def __init__(self, proc: Processor, pipe: ConnectionManager):
super().__init__(proc)
self.pipe = pipe

def flush(self):
self.pipe.send(P2C_SCREEN, self.screen)


class AppControlDevice(Device):

def __init__(self):
Expand All @@ -316,6 +305,8 @@ def __init__(self, pipe: ConnectionManager):
def __call__(self, proc: Processor, event_type: ProcessorEvent, arg: Any):
if event_type == ProcessorEvent.PRINT:
self.pipe.send(P2C_PRINT, arg)
elif event_type == ProcessorEvent.GFLUSH:
self.pipe.send(P2C_SCREEN, arg)


if __name__ == '__main__':
Expand Down
32 changes: 31 additions & 1 deletion processorv5/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
IntLike = Union[IntEnum, int]

SIGNALS_32BIT = ['wooden-chest', 'iron-chest', 'steel-chest', 'storage-tank', 'transport-belt', 'fast-transport-belt', 'express-transport-belt', 'underground-belt', 'fast-underground-belt', 'express-underground-belt', 'splitter', 'fast-splitter', 'express-splitter', 'burner-inserter', 'inserter', 'long-handed-inserter', 'fast-inserter', 'filter-inserter', 'stack-inserter', 'stack-filter-inserter', 'small-electric-pole', 'medium-electric-pole', 'big-electric-pole', 'substation', 'pipe', 'pipe-to-ground', 'pump', 'rail', 'train-stop', 'rail-signal', 'rail-chain-signal', 'locomotive']
SIGNALS_80 = ['wooden-chest', 'iron-chest', 'steel-chest', 'storage-tank', 'transport-belt', 'fast-transport-belt', 'express-transport-belt', 'underground-belt', 'fast-underground-belt', 'express-underground-belt', 'splitter', 'fast-splitter', 'express-splitter', 'burner-inserter', 'inserter', 'long-handed-inserter', 'fast-inserter', 'filter-inserter', 'stack-inserter', 'stack-filter-inserter', 'small-electric-pole', 'medium-electric-pole', 'big-electric-pole', 'substation', 'pipe', 'pipe-to-ground', 'pump', 'rail', 'train-stop', 'rail-signal', 'rail-chain-signal', 'locomotive', 'cargo-wagon', 'fluid-wagon', 'artillery-wagon', 'car', 'tank', 'spidertron', 'spidertron-remote', 'logistic-robot', 'construction-robot', 'logistic-chest-active-provider', 'logistic-chest-passive-provider', 'logistic-chest-storage', 'logistic-chest-buffer', 'logistic-chest-requester', 'roboport', 'small-lamp', 'red-wire', 'green-wire', 'arithmetic-combinator', 'decider-combinator', 'constant-combinator', 'power-switch', 'programmable-speaker', 'stone-brick', 'concrete', 'hazard-concrete', 'refined-concrete', 'refined-hazard-concrete', 'landfill', 'cliff-explosives', 'repair-pack', 'blueprint', 'deconstruction-planner', 'upgrade-planner', 'blueprint-book', 'boiler', 'steam-engine', 'solar-panel', 'accumulator', 'nuclear-reactor', 'heat-pipe', 'heat-exchanger', 'steam-turbine', 'burner-mining-drill', 'electric-mining-drill', 'offshore-pump', 'pumpjack', 'stone-furnace']

TWO_OPERAND = {'W': 1, 'X': ALUInputCode.A, 'Y': ALUInputCode.B}
OPERAND_IMMEDIATE = {'W': 1, 'X': ALUInputCode.B, 'Y': ALUInputCode.IMMEDIATE}
Expand Down Expand Up @@ -95,9 +96,11 @@
def main():
# build_signals_32bit()
# build_gpu_image_decoder()
build_control_unit()
# build_control_unit()
# build_gpu_control_unit()
# build_gpu_screen_encoder()
build_color_lights_row()
# build_signals_80_wide()
print('Done')


Expand Down Expand Up @@ -232,6 +235,25 @@ def build_gpu_screen_encoder():
assert control['second_constant'] == int32(1) << int32(x)


def build_color_lights_row():
obj = decode('prototype_color_lights_row')
index = partition(obj)
for i in range(80):
lhs, ac, rhs = index[i * 3, 0], index[i * 3 + 1, 1], index[i * 3 + 2, 0]

assert lhs['name'] == 'small-lamp'
assert ac['name'] == 'arithmetic-combinator'
assert rhs['name'] == 'small-lamp'

signal = SIGNALS_80[i]

lhs['control_behavior']['circuit_condition']['first_signal']['name'] = signal
ac['control_behavior']['arithmetic_conditions']['second_signal']['name'] = signal
rhs['control_behavior']['circuit_condition']['first_signal']['name'] = signal

encode('color_lights_row', obj)


def build_signals_32bit():
obj = decode('lights_column')
index: Dict[Tuple[int, int], Any] = partition(obj)
Expand All @@ -243,6 +265,14 @@ def build_signals_32bit():
signals.append(control['name'])
print(signals)

def build_signals_80_wide():
obj = decode('dual_rom')
index = partition(obj)
x = []
for i in range(4):
x += [index[0, i]['control_behavior']['filters'][j]['signal']['name'] for j in range(20)]
print(x)


def constant_signal(letter: str, count: IntLike, index: int):
return {
Expand Down
6 changes: 2 additions & 4 deletions processorv5/processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def __init__(self, processor: 'Processor'):
def exec(self, ir: IRData):
if ir.gpu_opcode == GPUInstruction.GFLUSH:
self.screen = self.buffer
self.flush()
ProcessorEvent.GFLUSH.post(self.processor, self.screen)
elif ir.gpu_opcode == GPUInstruction.GLSI:
self.image = self.gpu_mem_get(ir.op1)
elif ir.gpu_opcode == GPUInstruction.GLS:
Expand All @@ -97,9 +97,6 @@ def exec(self, ir: IRData):
else:
self.processor.throw(ProcessorErrorType.GPU_INVALID_OPCODE, ir.gpu_opcode)

def flush(self):
pass

def gpu_mem_get(self, addr: int32) -> ImageBuffer:
if 0 <= addr < constants.GPU_MEMORY_SIZE and addr < len(self.processor.sprites):
if (sprite := self.processor.sprites[addr]) is not None:
Expand Down Expand Up @@ -149,6 +146,7 @@ def __str__(self):

class ProcessorEvent(Enum):
PRINT = 'print'
GFLUSH = 'gflush'

def post(self, proc: 'Processor', arg: Any):
proc.event_handle(proc, self, arg)
Expand Down
4 changes: 2 additions & 2 deletions processorv5/simulator/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from simulator.signal import Signal
from simulator.port import Port, ReadPort
from simulator.entity import Entity

from simulator.arithmetic_combinator import ArithmeticCombinator, ArithmeticOperation
from simulator.decider_combinator import DeciderCombinator, DeciderOperation
from simulator.constant_combinator import ConstantCombinator

from simulator.model import Model, Simulator
from simulator.model import Model, ModelBuilder
77 changes: 41 additions & 36 deletions processorv5/simulator/arithmetic_combinator.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Dict, Callable
from enum import IntEnum, auto
from numpy import int32
from simulator import Signal, Entity
from simulator import Port, ReadPort, Entity, signals
from utils import AnyValue, AnyInt


Expand Down Expand Up @@ -55,109 +55,111 @@ class ArithmeticCombinator(Entity):
ArithmeticOperation.XOR: '^'
}

VALUES: Dict[str, ArithmeticOperation] = {v: k for k, v in KEYS.items()}

def __init__(self, left: AnyValue, right: AnyValue, out: str, operation: ArithmeticOperation):
super().__init__()
self.green_in = Signal()
self.green_out = Signal()
self.red_in = Signal()
self.red_out = Signal()
self.red_in = ReadPort()
self.green_in = ReadPort()
self.red_out = Port()
self.green_out = Port()

self.connections = {
1: {'red': self.red_in, 'green': self.green_in},
2: {'red': self.red_out, 'green': self.green_out}
}

# Signal conditions
if Signal.is_named(left):
if Signal.is_virtual(left):
if left != Signal.EACH:
if signals.is_named(left):
if signals.is_virtual(left):
if left != signals.EACH:
raise TypeError('left signal may be EACH or any single signal, not %s' % left)
if Signal.is_virtual(out):
if out != Signal.EACH:
if signals.is_virtual(out):
if out != signals.EACH:
raise TypeError('output signal may be EACH or any single signal, not %s' % out)
# EACH -> EACH
self.mode = ArithmeticMode.EACH_EACH
else:
self.mode = ArithmeticMode.EACH_SINGLE
else:
# Left is single signal
if Signal.is_virtual(out):
if signals.is_virtual(out):
raise TypeError('output signal must be single signal if left is single signal, not %s' % out)
self.mode = ArithmeticMode.SINGLE_SINGLE
self.left_constant = False
else:
left = int32(left)
self.left_constant = True

if Signal.is_named(right):
if Signal.is_virtual(right):
if signals.is_named(right):
if signals.is_virtual(right):
raise TypeError('right signal may be single signal or constant, not %s' % right)
self.right_constant = False
else:
right = int32(right)
self.right_constant = True

self.left = left
self.right = right
self.out = out
self.left: int32 | str = left
self.right: int32 | str = right
self.out: str = out
self.operator = ArithmeticCombinator.OPERATIONS[operation]

self.key = '%s:=%s%s%s' % (
Signal.from_formal(out),
str(left) if self.left_constant else Signal.from_formal(left),
self.key = '%s := %s %s %s' % (
out,
str(left) if self.left_constant else left,
ArithmeticCombinator.KEYS[operation],
str(right) if self.right_constant else Signal.from_formal(right)
str(right) if self.right_constant else right
)

def tick(self):
# Initially clear output signals
self.red_out.clear()
self.green_out.clear()

# Compute right value, will be used in both cases
# Right may be a constant or a signal
if self.right_constant:
right_value = self.right
else:
right_value = self.green_in.get(self.right) + self.red_in.get(self.right)
right_value = self.green_in[self.right] + self.red_in[self.right]

if self.mode == ArithmeticMode.SINGLE_SINGLE:
# Single signal input. Sum input values
if self.left_constant:
left_value = self.left
else:
left_value = self.green_in.get(self.left) + self.red_in.get(self.left)
left_value = self.green_in[self.left] + self.red_in[self.left]

# Apply the operation to the left and right signals
out_value = self.operator(left_value, right_value)

# Set the output on both channels
self.red_out.set(self.out, out_value)
self.green_out.set(self.out, out_value)
self.red_out[self.out] = out_value
self.green_out[self.out] = out_value

else:
# Each signal input. Sum all inputs into one signal with all values
all_in_signals: Signal = self.green_in + self.red_in
all_in_signals: Dict[str, int32] = self.read_in()
# Only used in the EACH_SINGLE case
sum_out_value = int32(0)

for signal_name, signal_value in all_in_signals.values.items():
for signal_name, signal_value in all_in_signals.items():
out_value = self.operator(signal_value, right_value)
if self.mode == ArithmeticMode.EACH_EACH:
self.red_out.set(signal_name, out_value)
self.green_out.set(signal_name, out_value)
self.red_out[signal_name] = out_value
self.green_out[signal_name] = out_value
else:
sum_out_value += out_value

if self.mode == ArithmeticMode.EACH_SINGLE:
# output the sum value to the out channel
self.red_out.set(self.out, sum_out_value)
self.green_out.set(self.out, sum_out_value)
self.red_out[self.out] = sum_out_value
self.green_out[self.out] = sum_out_value

# Handled input signals, so clear them
# Store the current inputs so when viewed, this combinator shows it's full operation
self.red_in.clear()
self.green_in.clear()

def read_in(self) -> Dict[str, int32]:
return signals.union(self.green_in.signals, self.red_in.signals)

def set_left(self, value: AnyInt):
assert self.left_constant, 'Left input is not constant: %s' % self.key
self.left = int32(value)
Expand All @@ -166,5 +168,8 @@ def set_right(self, value: AnyInt):
assert self.right_constant, 'Right input is not constant: %s' % self.key
self.right = int32(value)

def __str__(self):
return 'Arithmetic: GI = %s, RI = %s, GO = %s, RO = %s' % (str(self.green_in), str(self.red_in), str(self.green_out), str(self.red_out))
def __str__(self) -> str: return repr(self)
def __repr__(self) -> str: return '[%s <- %s <- %s]' % (self.green_out, self.key, self.read_in())

def __eq__(self, other): return isinstance(other, ArithmeticCombinator) and self.left == other.left and self.right == other.right and self.out == other.out and self.operator == other.operator
def __ne__(self, other): return not self.__eq__(other)
Loading

0 comments on commit 90090ca

Please sign in to comment.