Skip to content

Commit

Permalink
eDSL, docs: new eDSL example showing the usage of guards, and a corre…
Browse files Browse the repository at this point in the history
…ction 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
  • Loading branch information
anshumanmohan authored Jun 13, 2024
1 parent 9f0a0ad commit e18da1b
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 6 deletions.
16 changes: 16 additions & 0 deletions calyx-py/calyx/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down
36 changes: 36 additions & 0 deletions calyx-py/calyx/py_ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
12 changes: 12 additions & 0 deletions calyx-py/test/correctness/guards.data
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"mem": {
"data": [
0
],
"format": {
"numeric_type": "bitnum",
"is_signed": false,
"width": 32
}
}
}
5 changes: 5 additions & 0 deletions calyx-py/test/correctness/guards.expect
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"mem": [
42
]
}
39 changes: 39 additions & 0 deletions calyx-py/test/correctness/guards.py
Original file line number Diff line number Diff line change
@@ -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()
10 changes: 4 additions & 6 deletions docs/lang/ref.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit e18da1b

Please sign in to comment.