Something went wrong. Try again.
OR-1 dataflow CPU sketch
Something went wrong. Try again.
9.5 kB · 266 lines
Python
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267"""Tests for IRGraph serialization to dfasm source.
Tests verify the serialize() function converts IRGraphs back to valid dfasm sourceand preserves structural information through round-trip parsing and lowering."""
from tests.pipeline import parse_and_lower, parse_lower_resolve
from asm.ir import ( IRGraph, IRNode, IREdge, IRRegion, RegionKind, IRDataDef)from asm.serialize import serializefrom asm.opcodes import OP_TO_MNEMONICfrom cm_inst import ArithOp, LogicOp, MemOp, Port, RoutingOp
class TestRoundTrip: """AC11.1, AC11.2: Round-trip parse → lower → serialize → parse → lower"""
def test_simple_two_node_graph(self, parser): """AC11.1: Simple graph with two nodes and one edge round-trips.""" source = """ &add|pe0 <| add &const|pe0 <| const, 42 &add |> &const:L """ # Parse and lower to get baseline IRGraph graph1 = parse_and_lower(parser, source)
# Serialize and re-parse, lower serialized = serialize(graph1) graph2 = parse_and_lower(parser, serialized)
# Check structural equivalence assert set(graph1.nodes.keys()) == set(graph2.nodes.keys()) assert len(graph1.edges) == len(graph2.edges)
def test_single_node_roundtrip(self, parser): """AC11.1: Single node without edges round-trips.""" source = "&foo|pe0 <| add"
graph1 = parse_and_lower(parser, source) serialized = serialize(graph1) graph2 = parse_and_lower(parser, serialized)
assert set(graph1.nodes.keys()) == set(graph2.nodes.keys()) assert len(graph1.edges) == len(graph2.edges)
def test_graph_with_multiple_edges(self, parser): """AC11.2: Graph with multiple edges preserves edge count.""" source = """ &a|pe0 <| add &b|pe0 <| sub &c|pe1 <| add &a |> &b:L &a |> &c:R &b |> &c:L """ graph1 = parse_and_lower(parser, source) serialized = serialize(graph1) graph2 = parse_and_lower(parser, serialized)
assert len(graph1.edges) == len(graph2.edges) assert set(graph1.nodes.keys()) == set(graph2.nodes.keys())
class TestPlacementQualifiers: """AC11.3: All inst_def lines include |pe{N} qualifiers."""
def test_all_nodes_have_pe_qualifier(self): """AC11.3: Serialized output contains |pe{N} on every inst_def.""" # Create a graph with nodes on different PEs nodes = { "&a": IRNode(name="&a", opcode=ArithOp.ADD, pe=0), "&b": IRNode(name="&b", opcode=ArithOp.SUB, pe=1), "&c": IRNode(name="&c", opcode=RoutingOp.CONST, const=42, pe=0), } graph = IRGraph(nodes=nodes)
serialized = serialize(graph) lines = serialized.strip().split('\n')
# Filter to inst_def lines (contain '<|') inst_lines = [l for l in lines if '<|' in l]
# Each should contain |pe{N} for line in inst_lines: assert '|pe' in line, f"Missing PE qualifier in: {line}"
class TestFunctionScoping: """AC11.4, AC11.7: FUNCTION regions serialize with scoping blocks."""
def test_function_region_structure(self): """AC11.4, AC11.7: FUNCTION regions emit $name |> { ... } blocks.""" # Create a function region with unqualified node names inside func_nodes = { "&add": IRNode(name="&add", opcode=ArithOp.ADD, pe=0), "&const": IRNode(name="&const", opcode=RoutingOp.CONST, const=10, pe=0), } func_body = IRGraph(nodes=func_nodes) func_region = IRRegion(tag="$main", kind=RegionKind.FUNCTION, body=func_body)
graph = IRGraph(regions=[func_region]) serialized = serialize(graph)
# Check for function block syntax assert "$main |>" in serialized assert "{" in serialized assert "}" in serialized
# Check that node names inside are unqualified (no $main. prefix) assert "&add|pe0 <| add" in serialized or "&add|pe0 <| add" in serialized.replace('\n', ' ')
def test_unqualified_names_in_function(self): """AC11.7: Names inside FUNCTION regions are unqualified.""" func_nodes = { "&label": IRNode(name="&label", opcode=ArithOp.ADD, pe=0), } func_body = IRGraph(nodes=func_nodes) func_region = IRRegion(tag="$main", kind=RegionKind.FUNCTION, body=func_body)
graph = IRGraph(regions=[func_region]) serialized = serialize(graph)
# Should not contain the qualified name $main.&label assert "$main.&label" not in serialized # Should contain unqualified &label assert "&label" in serialized
class TestDataDefs: """AC11.5: data_def entries serialize with SM placement and cell addresses."""
def test_data_def_serialization(self): """AC11.5: Data definitions serialize as @name|sm{id}:{cell} = {value}.""" data_defs = [ IRDataDef(name="@hello", sm_id=0, cell_addr=5, value=0x2a), IRDataDef(name="@world", sm_id=1, cell_addr=10, value=0x3f), ] graph = IRGraph(data_defs=data_defs)
serialized = serialize(graph)
# Check for data def entries assert "@hello|sm0:5 =" in serialized assert "@world|sm1:10 =" in serialized # Check hex values are present assert "0x2a" in serialized or "42" in serialized assert "0x3f" in serialized or "63" in serialized
class TestAnonymousNodes: """AC11.6: Anonymous nodes serialize as inst_def + plain_edge, not inline."""
def test_anonymous_node_not_inline(self): """AC11.6: Nodes with __anon_ in name use inst_def + plain_edge form.""" nodes = { "&source": IRNode(name="&source", opcode=ArithOp.ADD, pe=0), "__anon_1": IRNode(name="__anon_1", opcode=ArithOp.SUB, pe=0), "&dest": IRNode(name="&dest", opcode=ArithOp.ADD, pe=1), } edges = [ IREdge(source="&source", dest="__anon_1", port=Port.L), IREdge(source="__anon_1", dest="&dest", port=Port.R), ] graph = IRGraph(nodes=nodes, edges=edges)
serialized = serialize(graph)
# Anonymous node should be defined as a separate inst_def, not inline assert "__anon_1|pe0 <|" in serialized # Should have explicit edges, not inline syntax assert "&source |> __anon_1:L" in serialized assert "__anon_1 |> &dest:R" in serialized
class TestLocationRegions: """AC11.8: LOCATION regions serialize as bare directive + body."""
def test_location_region_structure(self): """AC11.8: LOCATION regions emit bare directive tag followed by body.""" # Create a location region with data definitions inside loc_data = [ IRDataDef(name="@data1", sm_id=0, cell_addr=5, value=100), ] loc_body = IRGraph(data_defs=loc_data) loc_region = IRRegion(tag="@data_section", kind=RegionKind.LOCATION, body=loc_body)
graph = IRGraph(regions=[loc_region]) serialized = serialize(graph)
# Location directive should appear as bare tag with trailing colon (not in $func |> form) assert "@data_section:" in serialized # Body content should follow assert "@data1|sm0:5" in serialized
class TestEdgeSerialization: """Edges serialize as plain_edge form."""
def test_edge_port_notation(self): """Edges serialize with :L and :R port notation.""" nodes = { "&a": IRNode(name="&a", opcode=ArithOp.ADD, pe=0), "&b": IRNode(name="&b", opcode=ArithOp.ADD, pe=1), } edges = [ IREdge(source="&a", dest="&b", port=Port.L), ] graph = IRGraph(nodes=nodes, edges=edges)
serialized = serialize(graph)
# Should contain edge with port notation assert "|> &b:L" in serialized
class TestMnemonicUsage: """Opcodes serialize using OP_TO_MNEMONIC."""
def test_various_opcodes(self): """Different opcodes serialize with correct mnemonics.""" nodes = { "&add": IRNode(name="&add", opcode=ArithOp.ADD, pe=0), "&sub": IRNode(name="&sub", opcode=ArithOp.SUB, pe=0), "&and": IRNode(name="&and", opcode=LogicOp.AND, pe=0), "&read": IRNode(name="&read", opcode=MemOp.READ, pe=0), } graph = IRGraph(nodes=nodes)
serialized = serialize(graph)
# Check that mnemonics appear assert "add" in serialized assert "sub" in serialized assert "and" in serialized assert "read" in serialized
class TestConstValues: """Const values serialize in inst_def format."""
def test_const_node_with_value(self): """Nodes with const values serialize with const argument.""" nodes = { "&c": IRNode(name="&c", opcode=RoutingOp.CONST, const=255, pe=0), } graph = IRGraph(nodes=nodes)
serialized = serialize(graph)
# Should include const value assert "const, 255" in serialized or "const, 0xff" in serialized
class TestEmptyGraph: """Edge case: empty graph."""
def test_empty_graph_serializes(self): """AC11.1: Empty graph serializes to empty or whitespace string.""" graph = IRGraph() serialized = serialize(graph)
# Should not raise, should be empty or whitespace assert serialized.strip() == ""