Something went wrong. Try again.
OR-1 dataflow CPU sketch
Something went wrong. Try again.
18 kB · 564 lines
Python
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565"""Tests for code generation (frame-based model).
Tests verify:- pe-frame-redesign.AC6.1: AssemblyResult includes setup_tokens field- pe-frame-redesign.AC6.2: Token stream ordering: SM init → IRAM writes → ALLOC → frame slot writes → seeds- pe-frame-redesign.AC6.3: IRAM write data uses pack_instruction()- pe-frame-redesign.AC6.6: Seed tokens use act_id, no gen field
Also tests original codegen acceptance criteria:- Direct mode produces valid PEConfig with correct IRAM contents- Direct mode produces valid SMConfig with initial cell values- Direct mode produces seed tokens for const nodes with no incoming edges- Direct mode PEConfig includes route restrictions matching edge analysis- Program with no data_defs produces empty SM init section"""
import pytest
from asm.codegen import generate_direct, generate_tokens, AssemblyResultfrom asm.ir import ( IRGraph, IRNode, IREdge, IRDataDef, SystemConfig, SourceLoc, ResolvedDest,)from cm_inst import ( Instruction, OutputStyle, TokenKind, FrameOp, FrameDest, ArithOp, MemOp, Port, RoutingOp)from tokens import MonadToken, SMToken, PELocalWriteToken, FrameControlTokenfrom emu.types import PEConfig, SMConfigfrom sm_mod import Presence
class TestFrameDirectMode: """Frame-based direct mode code generation."""
def test_assembly_result_structure(self): """AssemblyResult has all required fields including setup_tokens.""" node = IRNode( name="&add", opcode=ArithOp.ADD, pe=0, iram_offset=0, act_id=0, mode=(OutputStyle.INHERIT, False, 2), wide=False, fref=0, loc=SourceLoc(1, 1), ) system = SystemConfig(pe_count=1, sm_count=1) graph = IRGraph({"&add": node}, system=system)
result = generate_direct(graph)
# Verify AssemblyResult structure assert isinstance(result, AssemblyResult) assert hasattr(result, 'pe_configs') assert hasattr(result, 'sm_configs') assert hasattr(result, 'seed_tokens') assert hasattr(result, 'setup_tokens') assert isinstance(result.setup_tokens, list)
def test_direct_mode_peconfig_structure(self): """PEConfig has Instruction IRAM (not ALUInst/SMInst) and frame config fields.""" node = IRNode( name="&add", opcode=ArithOp.ADD, pe=0, iram_offset=0, act_id=0, mode=(OutputStyle.INHERIT, False, 2), wide=False, fref=7, loc=SourceLoc(1, 1), ) system = SystemConfig(pe_count=1, sm_count=1) graph = IRGraph({"&add": node}, system=system)
result = generate_direct(graph)
assert len(result.pe_configs) == 1 pe_config = result.pe_configs[0] assert pe_config.pe_id == 0 assert 0 in pe_config.iram
inst = pe_config.iram[0] assert isinstance(inst, Instruction) assert inst.opcode == ArithOp.ADD assert inst.output == OutputStyle.INHERIT assert inst.fref == 7
# Verify frame config fields assert hasattr(pe_config, 'frame_count') assert hasattr(pe_config, 'frame_slots') assert hasattr(pe_config, 'matchable_offsets') assert pe_config.frame_count > 0 assert pe_config.frame_slots > 0 assert pe_config.matchable_offsets > 0
def test_direct_mode_with_data_defs(self): """Direct mode produces SMConfig with initial cell values.""" data_def = IRDataDef( name="@val", sm_id=0, cell_addr=5, value=42, loc=SourceLoc(1, 1), ) node = IRNode( name="&a", opcode=ArithOp.ADD, pe=0, iram_offset=0, act_id=0, mode=(OutputStyle.INHERIT, False, 2), wide=False, fref=0, loc=SourceLoc(1, 1), ) system = SystemConfig(pe_count=1, sm_count=1) graph = IRGraph( {"&a": node}, data_defs=[data_def], system=system, )
result = generate_direct(graph)
# Verify SMConfig assert len(result.sm_configs) == 1 sm_config = result.sm_configs[0] assert sm_config.sm_id == 0 assert 5 in sm_config.initial_cells pres, val = sm_config.initial_cells[5] assert pres == Presence.FULL assert val == 42
def test_seed_tokens_use_act_id(self): """Seed tokens use act_id field, not ctx.""" seed_node = IRNode( name="&seed", opcode=RoutingOp.CONST, pe=0, iram_offset=1, act_id=3, const=99, mode=(OutputStyle.INHERIT, True, 1), wide=False, fref=0, seed=True, loc=SourceLoc(2, 1), ) # Monadic destination (not dyadic) dest_node = IRNode( name="&dest", opcode=ArithOp.INC, # INC is monadic pe=0, iram_offset=2, act_id=3, mode=(OutputStyle.INHERIT, False, 1), wide=False, fref=0, loc=SourceLoc(3, 1), ) edge = IREdge(source="&seed", dest="&dest", port=Port.L) system = SystemConfig(pe_count=1, sm_count=1) graph = IRGraph( {"&seed": seed_node, "&dest": dest_node}, edges=[edge], system=system, )
result = generate_direct(graph)
# Verify seed tokens assert len(result.seed_tokens) == 1 seed = result.seed_tokens[0] assert isinstance(seed, MonadToken) assert seed.data == 99 assert seed.act_id == 3 assert not hasattr(seed, 'gen') # No gen field
def test_const_node_with_no_incoming_edge_is_seed(self): """CONST node with no incoming edges produces seed token.""" const_node = IRNode( name="&const", opcode=RoutingOp.CONST, pe=0, iram_offset=2, act_id=0, const=99, mode=(OutputStyle.INHERIT, True, 1), wide=False, fref=0, loc=SourceLoc(1, 1), ) graph = IRGraph({"&const": const_node}, system=SystemConfig(1, 1))
result = generate_direct(graph)
assert len(result.seed_tokens) == 1 token = result.seed_tokens[0] assert isinstance(token, MonadToken) assert token.target == 0 assert token.offset == 2 assert token.act_id == 0 assert token.data == 99
def test_route_restrictions(self): """Cross-PE edges produce correct allowed_pe_routes.""" node_pe0 = IRNode( name="&a", opcode=ArithOp.ADD, pe=0, iram_offset=0, act_id=0, mode=(OutputStyle.INHERIT, False, 2), wide=False, fref=0, dest_l=ResolvedDest(name="&b", frame_dest=FrameDest( target_pe=1, offset=0, act_id=0, port=Port.L, token_kind=TokenKind.DYADIC )), loc=SourceLoc(1, 1), ) node_pe1 = IRNode( name="&b", opcode=ArithOp.SUB, pe=1, iram_offset=0, act_id=0, mode=(OutputStyle.INHERIT, False, 2), wide=False, fref=0, loc=SourceLoc(2, 1), ) edge = IREdge(source="&a", dest="&b", port=Port.L, loc=SourceLoc(1, 1)) system = SystemConfig(pe_count=2, sm_count=1) graph = IRGraph( {"&a": node_pe0, "&b": node_pe1}, edges=[edge], system=system, )
result = generate_direct(graph)
assert len(result.pe_configs) == 2 pe0_config = next(c for c in result.pe_configs if c.pe_id == 0) pe1_config = next(c for c in result.pe_configs if c.pe_id == 1)
# PE0 should have routes to {0, 1} assert 0 in pe0_config.allowed_pe_routes assert 1 in pe0_config.allowed_pe_routes
# PE1 should have route to {1} (self only) assert 1 in pe1_config.allowed_pe_routes
def test_no_data_defs_produces_empty_smconfig(self): """Program with no data_defs produces SMConfig with no initial cells.""" node = IRNode( name="&add", opcode=ArithOp.ADD, pe=0, iram_offset=0, act_id=0, mode=(OutputStyle.INHERIT, False, 2), wide=False, fref=0, loc=SourceLoc(1, 1), ) system = SystemConfig(pe_count=1, sm_count=1) graph = IRGraph({"&add": node}, system=system)
result = generate_direct(graph)
assert len(result.sm_configs) == 1 sm_config = result.sm_configs[0] assert sm_config.sm_id == 0 assert sm_config.initial_cells is None
def test_memop_instructions(self): """MemOp instructions produce Instruction objects.""" node = IRNode( name="&read", opcode=MemOp.READ, pe=0, iram_offset=0, act_id=0, sm_id=0, mode=(OutputStyle.CHANGE_TAG, False, 1), wide=True, fref=3, loc=SourceLoc(1, 1), ) system = SystemConfig(pe_count=1, sm_count=1) graph = IRGraph({"&read": node}, system=system)
result = generate_direct(graph)
assert len(result.pe_configs) == 1 pe_config = result.pe_configs[0] assert 0 in pe_config.iram
inst = pe_config.iram[0] assert isinstance(inst, Instruction) assert inst.opcode == MemOp.READ assert inst.output == OutputStyle.CHANGE_TAG assert inst.wide == True assert inst.fref == 3
class TestTokenStream: """Token stream generation and ordering."""
def test_setup_tokens_include_sm_init(self): """Setup tokens include SM init (SMToken) entries.""" data_def = IRDataDef( name="@val", sm_id=0, cell_addr=5, value=42, loc=SourceLoc(1, 1), ) node = IRNode( name="&add", opcode=ArithOp.ADD, pe=0, iram_offset=0, act_id=0, mode=(OutputStyle.INHERIT, False, 2), wide=False, fref=0, loc=SourceLoc(1, 1), ) system = SystemConfig(pe_count=1, sm_count=1) graph = IRGraph( {"&add": node}, data_defs=[data_def], system=system, )
result = generate_direct(graph) sm_tokens = [t for t in result.setup_tokens if isinstance(t, SMToken)]
assert len(sm_tokens) > 0 token = sm_tokens[0] assert token.target == 0 assert token.addr == 5 assert token.op == MemOp.WRITE assert token.data == 42
def test_setup_tokens_include_iram_writes(self): """Setup tokens include IRAM writes (PELocalWriteToken with region=0).""" node = IRNode( name="&add", opcode=ArithOp.ADD, pe=0, iram_offset=0, act_id=0, mode=(OutputStyle.INHERIT, False, 2), wide=False, fref=0, loc=SourceLoc(1, 1), ) system = SystemConfig(pe_count=1, sm_count=1) graph = IRGraph({"&add": node}, system=system)
result = generate_direct(graph) iram_tokens = [t for t in result.setup_tokens if isinstance(t, PELocalWriteToken) and t.region == 0]
assert len(iram_tokens) > 0 token = iram_tokens[0] assert token.target == 0 assert token.region == 0
def test_setup_tokens_include_alloc(self): """Setup tokens include ALLOC (FrameControlToken) entries.""" node = IRNode( name="&add", opcode=ArithOp.ADD, pe=0, iram_offset=0, act_id=2, mode=(OutputStyle.INHERIT, False, 2), wide=False, fref=0, loc=SourceLoc(1, 1), ) system = SystemConfig(pe_count=1, sm_count=1) graph = IRGraph({"&add": node}, system=system)
result = generate_direct(graph) alloc_tokens = [t for t in result.setup_tokens if isinstance(t, FrameControlToken)]
assert len(alloc_tokens) > 0 token = alloc_tokens[0] assert token.op == FrameOp.ALLOC assert token.act_id == 2
def test_generate_tokens_ordering(self): """generate_tokens() returns tokens in proper order.""" data_def = IRDataDef( name="@val", sm_id=0, cell_addr=5, value=42, loc=SourceLoc(1, 1), ) node = IRNode( name="&add", opcode=ArithOp.ADD, pe=0, iram_offset=0, act_id=0, mode=(OutputStyle.INHERIT, False, 2), wide=False, fref=0, loc=SourceLoc(1, 1), ) seed_node = IRNode( name="&const", opcode=RoutingOp.CONST, pe=0, iram_offset=10, act_id=0, const=99, mode=(OutputStyle.INHERIT, True, 1), wide=False, fref=0, seed=True, loc=SourceLoc(2, 1), ) edge = IREdge(source="&const", dest="&add", port=Port.L) system = SystemConfig(pe_count=1, sm_count=1) graph = IRGraph( {"&add": node, "&const": seed_node}, edges=[edge], data_defs=[data_def], system=system, )
tokens = generate_tokens(graph)
# Verify that setup tokens come before seed tokens sm_indices = [i for i, t in enumerate(tokens) if isinstance(t, SMToken)] iram_indices = [i for i, t in enumerate(tokens) if isinstance(t, PELocalWriteToken) and t.region == 0] seed_indices = [i for i, t in enumerate(tokens) if isinstance(t, (MonadToken))]
if sm_indices and iram_indices: assert max(sm_indices) < min(iram_indices) if iram_indices and seed_indices: assert max(iram_indices) < min(seed_indices)
class TestEdgeCases: """Edge case tests."""
def test_multiple_data_defs_same_sm(self): """Multiple data_defs targeting same SM are merged.""" data_def1 = IRDataDef( name="@val1", sm_id=0, cell_addr=5, value=42, loc=SourceLoc(1, 1), ) data_def2 = IRDataDef( name="@val2", sm_id=0, cell_addr=10, value=99, loc=SourceLoc(2, 1), ) system = SystemConfig(pe_count=1, sm_count=1) graph = IRGraph({}, data_defs=[data_def1, data_def2], system=system)
result = generate_direct(graph)
assert len(result.sm_configs) == 1 sm_config = result.sm_configs[0] assert len(sm_config.initial_cells) == 2 assert sm_config.initial_cells[5] == (Presence.FULL, 42) assert sm_config.initial_cells[10] == (Presence.FULL, 99)
def test_const_node_with_incoming_edge_not_seed(self): """CONST node with incoming edge is not a seed token.""" source_node = IRNode( name="&src", opcode=ArithOp.ADD, pe=0, iram_offset=0, act_id=0, mode=(OutputStyle.INHERIT, False, 2), wide=False, fref=0, loc=SourceLoc(1, 1), ) const_node = IRNode( name="&const", opcode=RoutingOp.CONST, pe=0, iram_offset=1, act_id=0, const=5, mode=(OutputStyle.INHERIT, True, 1), wide=False, fref=0, loc=SourceLoc(2, 1), ) edge = IREdge(source="&src", dest="&const", port=Port.L, loc=SourceLoc(1, 1)) system = SystemConfig(pe_count=1, sm_count=1) graph = IRGraph( {"&src": source_node, "&const": const_node}, edges=[edge], system=system, )
result = generate_direct(graph)
# The CONST node has an incoming edge, so it should NOT be a seed assert len(result.seed_tokens) == 0
def test_multiactivation_alloc_tokens(self): """Multiple activations generate multiple ALLOC tokens.""" node1 = IRNode( name="&a", opcode=ArithOp.ADD, pe=0, iram_offset=0, act_id=1, mode=(OutputStyle.INHERIT, False, 2), wide=False, fref=0, loc=SourceLoc(1, 1), ) node2 = IRNode( name="&b", opcode=ArithOp.SUB, pe=0, iram_offset=1, act_id=2, mode=(OutputStyle.INHERIT, False, 2), wide=False, fref=0, loc=SourceLoc(2, 1), ) system = SystemConfig(pe_count=1, sm_count=1) graph = IRGraph( {"&a": node1, "&b": node2}, system=system, )
result = generate_direct(graph) alloc_tokens = [t for t in result.setup_tokens if isinstance(t, FrameControlToken)]
# Should have 2 ALLOC tokens (one for act_id=1, one for act_id=2) assert len(alloc_tokens) == 2 assert all(t.op == FrameOp.ALLOC for t in alloc_tokens) assert {t.act_id for t in alloc_tokens} == {1, 2}