OR-1 dataflow CPU sketch
at main 120 lines 3.9 kB view raw
1import simpy 2 3from emu.pe import ProcessingElement 4from emu.sm import StructureMemory 5from emu.types import PEConfig, SMConfig 6from sm_mod import SMCell 7from tokens import PEToken, SMToken, Token 8 9 10class System: 11 def __init__( 12 self, 13 env: simpy.Environment, 14 pes: dict[int, ProcessingElement], 15 sms: dict[int, StructureMemory], 16 ): 17 self.env = env 18 self.pes = pes 19 self.sms = sms 20 21 def inject(self, token: Token) -> None: 22 """Inject a token into the appropriate element's input store (direct append). 23 24 Routes by type: SMToken → target SM, PEToken → target PE. 25 Uses direct list append (bypasses SimPy put), suitable for pre-simulation setup. 26 """ 27 store = self._target_store(token) 28 store.items.append(token) 29 30 def send(self, token: Token): 31 """Inject a token via SimPy store.put() with 1-cycle delivery delay (generator, yields). 32 33 Same routing as inject() but adds network latency and respects FIFO backpressure. 34 Must be called from within a SimPy process or env.process(). 35 """ 36 store = self._target_store(token) 37 yield self.env.timeout(1) # 1-cycle network delivery latency 38 yield store.put(token) 39 40 def load(self, tokens: list[Token]) -> None: 41 """Spawn a SimPy process that sends each token via store.put(). 42 43 Tokens are injected in order, respecting FIFO capacity. 44 """ 45 def _loader(): 46 for token in tokens: 47 yield from self.send(token) 48 self.env.process(_loader()) 49 50 def _target_store(self, token: Token) -> simpy.Store: 51 """Resolve the destination store for a token.""" 52 if isinstance(token, SMToken): 53 return self.sms[token.target].input_store 54 if isinstance(token, PEToken): 55 return self.pes[token.target].input_store 56 raise TypeError(f"Unknown token type: {type(token).__name__}") 57 58 59def build_topology( 60 env: simpy.Environment, 61 pe_configs: list[PEConfig], 62 sm_configs: list[SMConfig], 63 fifo_capacity: int = 8, 64) -> System: 65 pes: dict[int, ProcessingElement] = {} 66 sms: dict[int, StructureMemory] = {} 67 68 for cfg in pe_configs: 69 pe = ProcessingElement( 70 env=env, 71 pe_id=cfg.pe_id, 72 config=cfg, 73 ) 74 pes[cfg.pe_id] = pe 75 76 t0_store: list[int] = [] 77 78 for cfg in sm_configs: 79 sm = StructureMemory( 80 env=env, 81 sm_id=cfg.sm_id, 82 cell_count=cfg.cell_count, 83 fifo_capacity=fifo_capacity, 84 tier_boundary=cfg.tier_boundary, 85 on_event=cfg.on_event, 86 ) 87 sm.t0_store = t0_store 88 if cfg.initial_cells is not None: 89 for addr, (pres, data) in cfg.initial_cells.items(): 90 sm.cells[addr] = SMCell(pres, data, None) 91 sms[cfg.sm_id] = sm 92 93 pe_stores: dict[int, simpy.Store] = {pe_id: pe.input_store for pe_id, pe in pes.items()} 94 sm_stores: dict[int, simpy.Store] = {sm_id: sm.input_store for sm_id, sm in sms.items()} 95 96 for pe in pes.values(): 97 pe.route_table.update(pe_stores) 98 pe.sm_routes.update(sm_stores) 99 100 for sm in sms.values(): 101 sm.route_table.update(pe_stores) 102 103 # Apply route restrictions based on PEConfig allowed_pe_routes and allowed_sm_routes 104 for cfg in pe_configs: 105 pe = pes[cfg.pe_id] 106 if cfg.allowed_pe_routes is not None: 107 pe.route_table = { 108 pid: store for pid, store in pe.route_table.items() 109 if pid in cfg.allowed_pe_routes 110 } 111 if cfg.allowed_sm_routes is not None: 112 pe.sm_routes = { 113 sid: store for sid, store in pe.sm_routes.items() 114 if sid in cfg.allowed_sm_routes 115 } 116 117 system = System(env, pes, sms) 118 for sm in sms.values(): 119 sm.system = system 120 return system