From e18da1b6a54ee2101d1964f16a582df403847356 Mon Sep 17 00:00:00 2001 From: Anshuman Mohan <10830208+anshumanmohan@users.noreply.github.com> Date: Thu, 13 Jun 2024 09:41:18 -0400 Subject: [PATCH] eDSL, docs: new eDSL example showing the usage of guards, and a correction in syntax in docs (#2133) * Get the builder to generate ops between ports * Small example showing various forms of guards * Tweak & and | docs for guards --- calyx-py/calyx/builder.py | 16 ++++++++++ calyx-py/calyx/py_ast.py | 36 +++++++++++++++++++++++ calyx-py/test/correctness/guards.data | 12 ++++++++ calyx-py/test/correctness/guards.expect | 5 ++++ calyx-py/test/correctness/guards.py | 39 +++++++++++++++++++++++++ docs/lang/ref.md | 10 +++---- 6 files changed, 112 insertions(+), 6 deletions(-) create mode 100644 calyx-py/test/correctness/guards.data create mode 100644 calyx-py/test/correctness/guards.expect create mode 100644 calyx-py/test/correctness/guards.py diff --git a/calyx-py/calyx/builder.py b/calyx-py/calyx/builder.py index 55c0835249..8eab3f17e2 100644 --- a/calyx-py/calyx/builder.py +++ b/calyx-py/calyx/builder.py @@ -1235,6 +1235,22 @@ def __ne__(self, other: ExprBuilder): """Construct an inequality comparison with ==.""" return ExprBuilder(ast.Neq(self.expr, other.expr)) + def __lt__(self, other: ExprBuilder): + """Construct a less-than comparison with <.""" + return ExprBuilder(ast.Lt(self.expr, other.expr)) + + def __le__(self, other: ExprBuilder): + """Construct a less-than-or-equal comparison with <.""" + return ExprBuilder(ast.Lte(self.expr, other.expr)) + + def __gt__(self, other: ExprBuilder): + """Construct a greater-than comparison with <.""" + return ExprBuilder(ast.Gt(self.expr, other.expr)) + + def __ge__(self, other: ExprBuilder): + """Construct a greater-than-or-equal comparison with <.""" + return ExprBuilder(ast.Gte(self.expr, other.expr)) + @property def name(self): """Get the name of the expression.""" diff --git a/calyx-py/calyx/py_ast.py b/calyx-py/calyx/py_ast.py index 834d1da6dd..80fd3c32ab 100644 --- a/calyx-py/calyx/py_ast.py +++ b/calyx-py/calyx/py_ast.py @@ -400,6 +400,42 @@ def doc(self) -> str: return f"({self.left.doc()} != {self.right.doc()})" +@dataclass +class Lt(GuardExpr): + left: GuardExpr + right: GuardExpr + + def doc(self) -> str: + return f"({self.left.doc()} < {self.right.doc()})" + + +@dataclass +class Lte(GuardExpr): + left: GuardExpr + right: GuardExpr + + def doc(self) -> str: + return f"({self.left.doc()} <= {self.right.doc()})" + + +@dataclass +class Gt(GuardExpr): + left: GuardExpr + right: GuardExpr + + def doc(self) -> str: + return f"({self.left.doc()} > {self.right.doc()})" + + +@dataclass +class Gte(GuardExpr): + left: GuardExpr + right: GuardExpr + + def doc(self) -> str: + return f"({self.left.doc()} >= {self.right.doc()})" + + # Control @dataclass class Control(Emittable): diff --git a/calyx-py/test/correctness/guards.data b/calyx-py/test/correctness/guards.data new file mode 100644 index 0000000000..e9975bfde8 --- /dev/null +++ b/calyx-py/test/correctness/guards.data @@ -0,0 +1,12 @@ +{ + "mem": { + "data": [ + 0 + ], + "format": { + "numeric_type": "bitnum", + "is_signed": false, + "width": 32 + } + } +} \ No newline at end of file diff --git a/calyx-py/test/correctness/guards.expect b/calyx-py/test/correctness/guards.expect new file mode 100644 index 0000000000..e5e16d65cc --- /dev/null +++ b/calyx-py/test/correctness/guards.expect @@ -0,0 +1,5 @@ +{ + "mem": [ + 42 + ] +} diff --git a/calyx-py/test/correctness/guards.py b/calyx-py/test/correctness/guards.py new file mode 100644 index 0000000000..e398e87d7f --- /dev/null +++ b/calyx-py/test/correctness/guards.py @@ -0,0 +1,39 @@ +# pylint: disable=import-error +import calyx.builder as cb + + +def insert_main_component(prog): + """Insert the main component into the program. + This component will invoke the `muler`, `abs_diff`, `mux`, and `map` components. + """ + + comp = prog.component("main") + + mem = comp.seq_mem_d1("mem", 32, 1, 32, is_external=True) + + mul = comp.mult_pipe(32) + zero = cb.const(32, 0) + one = cb.const(32, 1) + + with comp.group("well-guarded_group") as wgg: + mul.left = zero @ 4 # This will never be executed + mul.left = (one <= zero) @ 5 # This will never be executed + mul.left = ~zero @ 6 # This will work + mul.right = (zero | one) @ 7 # This will work + mul.right = (zero & one) @ 8 # This will never be executed + mul.go = cb.HI + wgg.done = mul.done + + put_ans_in_mem = comp.mem_store_d1(mem, 0, mul.out, "store") + + comp.control += [wgg, put_ans_in_mem] + + +def build(): + prog = cb.Builder() + insert_main_component(prog) + return prog.program + + +if __name__ == "__main__": + build().emit() diff --git a/docs/lang/ref.md b/docs/lang/ref.md index f32ba0c91a..89dac0784b 100644 --- a/docs/lang/ref.md +++ b/docs/lang/ref.md @@ -221,13 +221,11 @@ port. Omitting a guard expression is equivalent to using `1'd1` (a constant "true") as the guard. Guards can use the following constructs: -- `port`: A port access on a defined cell -- `port op port`: A comparison between values on two ports. Valid `op` are: `>`, `<`, `>=`, `<=`, `==` +- `port`: A port access on a defined cell, such as `cond.out`, or a literal, such as `3'd2`. +- `port op port`: A comparison between values on two ports. Valid instances of `op` are: `>`, `<`, `>=`, `<=`, `==` - `!guard`: Logical negation of a guard value -- `guard || guard`: Disjunction between two guards -- `guard && guard`: Conjunction of two guards - -In the context of guards, a port can also be a literal (i.e., `counter.out == 3'd2` is a valid guard). +- `guard | guard`: Disjunction between two guards +- `guard & guard`: Conjunction of two guards > **Well-formedness**: For each input port on the LHS, only one guard should be active in any given cycle during the execution of a Calyx program.