Something went wrong. Try again.
OR-1 dataflow CPU sketch
Something went wrong. Try again.
7.4 kB · 242 lines
Python
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243"""Tests for Enhancement 1: Opcode Parameters (macro-enh.E1.*).
Tests verify:- macro-enh.E1.1: Grammar accepts param_ref in opcode position- macro-enh.E1.2: Lower pass stores ParamRef in IRNode.opcode- macro-enh.E1.3: OPCODE accepted as positional macro argument- macro-enh.E1.4: Expand pass resolves opcode ParamRef- macro-enh.E1.5: Full pipeline with opcode params"""
from pathlib import Path
from lark import Lark
from asm import assemble, run_pipelinefrom asm.expand import expandfrom asm.lower import lowerfrom asm.errors import ErrorCategoryfrom asm.ir import IRNode, ParamReffrom cm_inst import ArithOp, LogicOp, RoutingOp, MemOp, Port
def _get_parser(): grammar_path = Path(__file__).parent.parent / "dfasm.lark" return Lark( grammar_path.read_text(), parser="earley", propagate_positions=True, )
def parse_and_lower(source: str): parser = _get_parser() tree = parser.parse(source) return lower(tree)
def parse_lower_expand(source: str): graph = parse_and_lower(source) return expand(graph)
class TestE11_GrammarAcceptsParamRefOpcode: """E1.1: Grammar accepts param_ref in opcode position."""
def test_param_ref_opcode_in_inst_def(self): """${op} in inst_def opcode position parses and lowers.""" source = """ @system pe=1, sm=1 #wrap op |> { &n <| ${op} } """ graph = parse_and_lower(source) assert not graph.errors # Macro body should have a node with ParamRef opcode assert len(graph.macro_defs) == 1 body_nodes = graph.macro_defs[0].body.nodes assert len(body_nodes) == 1 node = list(body_nodes.values())[0] assert isinstance(node.opcode, ParamRef) assert node.opcode.param == "op"
def test_param_ref_opcode_in_strong_edge(self): """${op} in strong_edge opcode position parses and lowers.""" source = """ @system pe=1, sm=1 #wrap op |> { ${op} &src |> &dst } """ graph = parse_and_lower(source) assert not graph.errors body_nodes = graph.macro_defs[0].body.nodes # Strong edge creates anonymous node anon_nodes = [n for n in body_nodes.values() if isinstance(n.opcode, ParamRef)] assert len(anon_nodes) == 1 assert anon_nodes[0].opcode.param == "op"
def test_param_ref_opcode_in_weak_edge(self): """${op} in weak_edge opcode position parses and lowers.""" source = """ @system pe=1, sm=1 #wrap op |> { &dst ${op} <| &src } """ graph = parse_and_lower(source) assert not graph.errors body_nodes = graph.macro_defs[0].body.nodes anon_nodes = [n for n in body_nodes.values() if isinstance(n.opcode, ParamRef)] assert len(anon_nodes) == 1 assert anon_nodes[0].opcode.param == "op"
class TestE13_OpcodeAsMacroArgument: """E1.3: OPCODE accepted as positional macro argument."""
def test_bare_opcode_in_macro_call(self): """#reduce_2 add parses — bare opcode as macro argument.""" source = """ @system pe=1, sm=1 #wrap op |> { &n <| ${op} } #wrap add """ graph = parse_and_lower(source) assert not graph.errors assert len(graph.macro_calls) == 1 call = graph.macro_calls[0] assert call.positional_args == ("add",)
def test_multiple_opcode_args(self): """Multiple opcodes can be passed as arguments.""" source = """ @system pe=1, sm=1 #pair op1, op2 |> { &a <| ${op1} &b <| ${op2} } #pair add, sub """ graph = parse_and_lower(source) assert not graph.errors call = graph.macro_calls[0] assert call.positional_args == ("add", "sub")
class TestE14_ExpandResolvesOpcodeParamRef: """E1.4: Expand pass resolves opcode ParamRef."""
def test_resolve_arith_opcode(self): """Opcode param 'add' resolves to ArithOp.ADD.""" source = """ @system pe=1, sm=1 #wrap op |> { &n <| ${op} } #wrap add """ graph = parse_lower_expand(source) assert not graph.errors node = list(graph.nodes.values())[0] assert node.opcode == ArithOp.ADD
def test_resolve_routing_opcode(self): """Opcode param 'gate' resolves to RoutingOp.GATE.""" source = """ @system pe=1, sm=1 #wrap op |> { &n <| ${op} } #wrap gate """ graph = parse_lower_expand(source) assert not graph.errors node = list(graph.nodes.values())[0] assert node.opcode == RoutingOp.GATE
def test_resolve_mem_opcode(self): """Opcode param 'read' resolves to MemOp.READ.""" source = """ @system pe=1, sm=1 #wrap op |> { &n <| ${op} } #wrap read """ graph = parse_lower_expand(source) assert not graph.errors node = list(graph.nodes.values())[0] assert node.opcode == MemOp.READ
def test_invalid_opcode_mnemonic_error(self): """Invalid mnemonic produces MACRO error.
Note: 'banana' lexes as IDENT and parses as a qualified_ref (label_ref &banana), so we pass it as a qualified_ref dict. The expand pass gets a dict, not a string, which produces the 'must resolve to an opcode mnemonic' error. """ source = """ @system pe=1, sm=1 #wrap op |> { &n <| ${op} } #wrap &banana """ graph = parse_lower_expand(source) macro_errors = [e for e in graph.errors if e.category == ErrorCategory.MACRO] assert len(macro_errors) >= 1 assert "opcode mnemonic" in macro_errors[0].message
def test_numeric_opcode_error(self): """Numeric value as opcode produces MACRO error.""" source = """ @system pe=1, sm=1 #wrap op |> { &n <| ${op} } #wrap 42 """ graph = parse_lower_expand(source) macro_errors = [e for e in graph.errors if e.category == ErrorCategory.MACRO] assert len(macro_errors) >= 1
class TestE15_FullPipelineOpcodeParams: """E1.5: Full pipeline with opcode params."""
def test_full_pipeline_opcode_param(self): """Opcode-parameterized macro assembles through full pipeline.""" source = """ @system pe=1, sm=1 #wrap op |> { &n <| ${op} } &seed <| const, 5 #wrap add &seed |> #wrap_0.&n:L &seed |> #wrap_0.&n:R """ result = assemble(source) assert result is not None # Should have at least one PE config assert len(result.pe_configs) >= 1
def test_full_pipeline_reduce_pattern(self): """Reduction tree pattern with opcode param.""" source = """ @system pe=1, sm=1 #reduce_2 op |> { &r <| ${op} } &a <| const, 3 &b <| const, 7 #reduce_2 add &a |> #reduce_2_0.&r:L &b |> #reduce_2_0.&r:R """ result = assemble(source) assert result is not None