From 3083c1d6ddc2a80b745152eb6ee406d8ed3c5d12 Mon Sep 17 00:00:00 2001 From: whitequark Date: Sun, 13 Jan 2019 08:46:28 +0000 Subject: [PATCH] hdl.dsl: accept (but warn on) cases wider than switch test value. Fixes #13. --- nmigen/hdl/ast.py | 1 + nmigen/hdl/dsl.py | 16 +++++++++++++--- nmigen/test/test_hdl_dsl.py | 12 ++++++++++++ 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/nmigen/hdl/ast.py b/nmigen/hdl/ast.py index 56c61ca..c6dee03 100644 --- a/nmigen/hdl/ast.py +++ b/nmigen/hdl/ast.py @@ -882,6 +882,7 @@ class Switch(Statement): for key, stmts in cases.items(): if isinstance(key, (bool, int)): key = "{:0{}b}".format(key, len(self.test)) + assert len(key) <= len(self.test) elif isinstance(key, str): assert len(key) == len(self.test) else: diff --git a/nmigen/hdl/dsl.py b/nmigen/hdl/dsl.py index 968f396..de0231b 100644 --- a/nmigen/hdl/dsl.py +++ b/nmigen/hdl/dsl.py @@ -1,6 +1,7 @@ from collections import OrderedDict, namedtuple from collections.abc import Iterable from contextlib import contextmanager +import warnings from ..tools import flatten, bits_for from .ast import * @@ -8,13 +9,17 @@ from .ir import * from .xfrm import * -__all__ = ["Module", "SyntaxError"] +__all__ = ["Module", "SyntaxError", "SyntaxWarning"] class SyntaxError(Exception): pass +class SyntaxWarning(Warning): + pass + + class _ModuleBuilderProxy: def __init__(self, builder, depth): object.__setattr__(self, "_builder", builder) @@ -195,7 +200,7 @@ class Module(_ModuleBuilderRoot): @contextmanager def Switch(self, test): self._check_context("Switch", context=None) - switch_data = self._set_ctrl("Switch", {"test": test, "cases": OrderedDict()}) + switch_data = self._set_ctrl("Switch", {"test": Value.wrap(test), "cases": OrderedDict()}) try: self._ctrl_context = "Switch" self.domain._depth += 1 @@ -211,9 +216,14 @@ class Module(_ModuleBuilderRoot): switch_data = self._get_ctrl("Switch") if value is None: value = "-" * len(switch_data["test"]) - if isinstance(value, str) and len(switch_data["test"]) != len(value): + if isinstance(value, str) and len(value) != len(switch_data["test"]): raise SyntaxError("Case value '{}' must have the same width as test (which is {})" .format(value, len(switch_data["test"]))) + if isinstance(value, int) and bits_for(value) > len(switch_data["test"]): + warnings.warn("Case value '{:b}' is wider than test (which has width {}); " + "comparison will be made against truncated value" + .format(value, len(switch_data["test"])), SyntaxWarning, stacklevel=3) + value &= (1 << len(switch_data["test"])) - 1 try: _outer_case, self._statements = self._statements, [] self._ctrl_context = None diff --git a/nmigen/test/test_hdl_dsl.py b/nmigen/test/test_hdl_dsl.py index 2a33864..3e3df1d 100644 --- a/nmigen/test/test_hdl_dsl.py +++ b/nmigen/test/test_hdl_dsl.py @@ -287,6 +287,18 @@ class DSLTestCase(FHDLTestCase): msg="Case value '--' must have the same width as test (which is 4)"): with m.Case("--"): pass + with self.assertWarns(SyntaxWarning, + msg="Case value '10110' is wider than test (which has width 4); comparison " + "will be made against truncated value"): + with m.Case(0b10110): + pass + self.assertRepr(m._statements, """ + ( + (switch (sig w1) + (case 0110 ) + ) + ) + """) def test_Case_outside_Switch_wrong(self): m = Module() -- 2.30.2