Skip to content

Commit

Permalink
Merge 0b99995 into b66bbbe
Browse files Browse the repository at this point in the history
  • Loading branch information
leonardt committed Nov 8, 2018
2 parents b66bbbe + 0b99995 commit 2f2bcec
Show file tree
Hide file tree
Showing 15 changed files with 254 additions and 5 deletions.
4 changes: 2 additions & 2 deletions docs/operators.md
Expand Up @@ -46,8 +46,8 @@ Language](https://ieeexplore.ieee.org/document/8299595) (page 256, Table 11-1
#### Assignment
| Verilog Operator | Magma Operator | Types | Context | Comments |
|------------------|----------------| ----- | ------- | -------- |
| `=` | `m.wire`, **TODO (=)** | Any | All | Assignment cannot be overloaded for arbitrary Python variables, so in general we must use `m.wire`. There are plans to add support for assignment to attributes of magma types, such as `reg.I = io.I`. |
| `+=`, `-=`, `/=`, `*=` | `None` | None | All | Again, unsupported due to the lack of support for overloading assignment. May be added in the future for attributes of magma types |
| `=` | `m.wire`, `<=` (can only be used on magma input values) | Any | All | Assignment cannot be overloaded for arbitrary Python variables, so in general we must use `m.wire`. We have added preliminary for assignment to attributes of magma values using the `<=` operator, which may be familiar for Verilog programmers using non-blocking assignments. Example: `reg.I <= io.I`. `<=` is purely syntactic sugar defiend on output values and calls `m.wire` under the hood. |
| `+=`, `-=`, `/=`, `*=` | `None` | None | All | Support is not planned for these operators because magma cannot provide a clean semantics for them. Assignment only works for inputs to circuit instances and outputs of circuit definitions. AugAssign operators imply a value that is used both as an input an output. For example, `inst.a +=1` would imply a is an output that feeds into binary add with 1, while also an input which consumes the result of the binary add. |
| `%=` | `None` | None | All | See above |
| `&=`, `\|=`, `^=` | `None` | None | All | See above |
| `>>=`, `<<=` | `None` | None | All | See above |
Expand Down
1 change: 1 addition & 0 deletions magma/__init__.py
Expand Up @@ -58,3 +58,4 @@ def set_mantle_target(t):

from .backend.util import set_codegen_debug_info
from .enum import Enum
import magma.util
9 changes: 6 additions & 3 deletions magma/operators.py
Expand Up @@ -46,7 +46,8 @@ def wrapped(self, other):
"__mul__",
"__div__",
"__lt__",
"__le__",
# __le__ skipped because it's used for assignment on inputs
# "__le__",
"__gt__",
"__ge__"
):
Expand Down Expand Up @@ -87,7 +88,8 @@ def wrapped(self, other):
"__mul__",
"__div__",
"__lt__",
"__le__",
# __le__ skipped because it's used for assignment on inputs
# "__le__",
"__gt__",
"__ge__"
):
Expand All @@ -99,7 +101,8 @@ def wrapped(self, other):
"__mul__",
"__div__",
"__lt__",
"__le__",
# __le__ skipped because it's used for assignment on inputs
# "__le__",
"__gt__",
"__ge__"
):
Expand Down
6 changes: 6 additions & 0 deletions magma/t.py
Expand Up @@ -67,6 +67,12 @@ def debug_name(self):
defn_str = str(self.name.inst.defn.name) + "."
return f"{defn_str}{inst_str}{str(self)}"

def __le__(self, other):
if self.isinput():
self.wire(other)
else:
raise TypeError(f"Cannot use <= to assign to output: {self.debug_name} (trying to assign {other.debug_name})")


class Kind(type):
def __init__(cls, name, bases, dct):
Expand Down
9 changes: 9 additions & 0 deletions magma/util.py
@@ -0,0 +1,9 @@
import magma as m


def BitOrBits(width):
if width is None:
return m.Bit
if not isinstance(width, int):
raise ValueError(f"Expected width to be None or int, got {width}")
return m.Bits(width)
7 changes: 7 additions & 0 deletions tests/common.py
@@ -0,0 +1,7 @@
import magma as m


def DeclareAnd(width):
T = m.util.BitOrBits(width)
return m.DeclareCircuit(f'And{width}', "I0", m.In(T), "I1", m.In(T),
"O", m.Out(T))
32 changes: 32 additions & 0 deletions tests/gold/test_assign_operator2_3_coreir.json
@@ -0,0 +1,32 @@
{"top":"global.test_assign_operator2_3_coreir",
"namespaces":{
"global":{
"modules":{
"And3":{
"type":["Record",[
["I0",["Array",3,"BitIn"]],
["I1",["Array",3,"BitIn"]],
["O",["Array",3,"Bit"]]
]]
},
"test_assign_operator2_3_coreir":{
"type":["Record",[
["a",["Array",3,"BitIn"]],
["b",["Array",3,"BitIn"]],
["c",["Array",3,"Bit"]]
]],
"instances":{
"inst0":{
"modref":"global.And3"
}
},
"connections":[
["inst0.O","inst0.I0"],
["self.a","inst0.I1"],
["self.c","self.b"]
]
}
}
}
}
}
6 changes: 6 additions & 0 deletions tests/gold/test_assign_operator2_3_verilog.v
@@ -0,0 +1,6 @@
module test_assign_operator2_3_verilog (input [2:0] a, input [2:0] b, output [2:0] c);
wire [2:0] inst0_O;
And3 inst0 (.I0(inst0_O), .I1(a), .O(inst0_O));
assign c = b;
endmodule

32 changes: 32 additions & 0 deletions tests/gold/test_assign_operator2_None_coreir.json
@@ -0,0 +1,32 @@
{"top":"global.test_assign_operator2_None_coreir",
"namespaces":{
"global":{
"modules":{
"AndNone":{
"type":["Record",[
["I0","BitIn"],
["I1","BitIn"],
["O","Bit"]
]]
},
"test_assign_operator2_None_coreir":{
"type":["Record",[
["a","BitIn"],
["b","BitIn"],
["c","Bit"]
]],
"instances":{
"inst0":{
"modref":"global.AndNone"
}
},
"connections":[
["inst0.O","inst0.I0"],
["self.a","inst0.I1"],
["self.c","self.b"]
]
}
}
}
}
}
6 changes: 6 additions & 0 deletions tests/gold/test_assign_operator2_None_verilog.v
@@ -0,0 +1,6 @@
module test_assign_operator2_None_verilog (input a, input b, output c);
wire inst0_O;
AndNone inst0 (.I0(inst0_O), .I1(a), .O(inst0_O));
assign c = b;
endmodule

32 changes: 32 additions & 0 deletions tests/gold/test_assign_operator_3_coreir.json
@@ -0,0 +1,32 @@
{"top":"global.test_assign_operator_3_coreir",
"namespaces":{
"global":{
"modules":{
"And3":{
"type":["Record",[
["I0",["Array",3,"BitIn"]],
["I1",["Array",3,"BitIn"]],
["O",["Array",3,"Bit"]]
]]
},
"test_assign_operator_3_coreir":{
"type":["Record",[
["a",["Array",3,"BitIn"]],
["b",["Array",3,"BitIn"]],
["c",["Array",3,"Bit"]]
]],
"instances":{
"inst0":{
"modref":"global.And3"
}
},
"connections":[
["self.a","inst0.I0"],
["self.b","inst0.I1"],
["self.c","inst0.O"]
]
}
}
}
}
}
6 changes: 6 additions & 0 deletions tests/gold/test_assign_operator_3_verilog.v
@@ -0,0 +1,6 @@
module test_assign_operator_3_verilog (input [2:0] a, input [2:0] b, output [2:0] c);
wire [2:0] inst0_O;
And3 inst0 (.I0(a), .I1(b), .O(inst0_O));
assign c = inst0_O;
endmodule

32 changes: 32 additions & 0 deletions tests/gold/test_assign_operator_None_coreir.json
@@ -0,0 +1,32 @@
{"top":"global.test_assign_operator_None_coreir",
"namespaces":{
"global":{
"modules":{
"AndNone":{
"type":["Record",[
["I0","BitIn"],
["I1","BitIn"],
["O","Bit"]
]]
},
"test_assign_operator_None_coreir":{
"type":["Record",[
["a","BitIn"],
["b","BitIn"],
["c","Bit"]
]],
"instances":{
"inst0":{
"modref":"global.AndNone"
}
},
"connections":[
["self.a","inst0.I0"],
["self.b","inst0.I1"],
["self.c","inst0.O"]
]
}
}
}
}
}
6 changes: 6 additions & 0 deletions tests/gold/test_assign_operator_None_verilog.v
@@ -0,0 +1,6 @@
module test_assign_operator_None_verilog (input a, input b, output c);
wire inst0_O;
AndNone inst0 (.I0(a), .I1(b), .O(inst0_O));
assign c = inst0_O;
endmodule

71 changes: 71 additions & 0 deletions tests/test_operators.py
@@ -1,5 +1,8 @@
import magma as m
from magma.operators import MantleImportError
from common import DeclareAnd
import pytest
from magma.testing import check_files_equal


def test_error():
Expand All @@ -10,3 +13,71 @@ def test_error():
"Operator should throw an error since mantle is not imported"
except MantleImportError:
pass


@pytest.mark.parametrize("width", [None, 3])
@pytest.mark.parametrize("output", ["verilog", "coreir"])
def test_assign(width, output):
T = m.util.BitOrBits(width)
name = f"test_assign_operator_{width}_{output}"
circ = m.DefineCircuit(name, "a", m.In(T), "b", m.In(T),
"c", m.Out(T))
and2 = DeclareAnd(width)()
and2.I0 <= circ.a
and2.I1 <= circ.b
circ.c <= and2.O
m.EndDefine()

m.compile(f"build/{name}", circ, output)
suffix = "v" if output == "verilog" else "json"
assert check_files_equal(__file__, f"build/{name}.{suffix}",
f"gold/{name}.{suffix}")



@pytest.mark.parametrize("width", [None, 3])
@pytest.mark.parametrize("output", ["verilog", "coreir"])
def test_assign_to_var(width, output):
T = m.util.BitOrBits(width)
name = f"test_assign_operator2_{width}_{output}"
circ = m.DefineCircuit(name, "a", m.In(T), "b", m.In(T),
"c", m.Out(T))
and2 = DeclareAnd(width)()
c, I0, I1 = and2.I0, and2.I1, circ.c
I0 <= circ.a
I1 <= circ.b
c <= and2.O
m.EndDefine()

m.compile(f"build/{name}", circ, output)
suffix = "v" if output == "verilog" else "json"
assert check_files_equal(__file__, f"build/{name}.{suffix}",
f"gold/{name}.{suffix}")


@pytest.mark.parametrize("width", [None, 3])
def test_assign_error_0(width):
T = m.util.BitOrBits(width)
name = f"test_assign_operator_{width}"
circ = m.DefineCircuit(name, "a", m.In(T), "b", m.In(T),
"c", m.Out(T))
and2 = DeclareAnd(width)()
try:
and2.O <= circ.a
assert False, "Should raise type error"
except TypeError as e:
assert str(e) == f"Cannot use <= to assign to output: {and2.O.debug_name} (trying to assign {circ.a.debug_name})"


@pytest.mark.parametrize("width", [None, 3])
def test_assign_error_1(width):
T = m.util.BitOrBits(width)
name = f"test_assign_operator_{width}"
circ = m.DefineCircuit(name, "a", m.In(T), "b", m.In(T),
"c", m.Out(T))
and2 = DeclareAnd(width)()
try:
circ.a <= and2.O
assert False, "Should raise type error"
except TypeError as e:
assert str(e) == f"Cannot use <= to assign to output: {circ.a.debug_name} (trying to assign {and2.O.debug_name})"

0 comments on commit 2f2bcec

Please sign in to comment.