diff --git a/.gitignore b/.gitignore index 75c349e8..1a33fac9 100644 --- a/.gitignore +++ b/.gitignore @@ -51,3 +51,6 @@ ipynb-examples/.ipynb_checkpoints # VS Code .vscode + +# Python venv +pyvenv.cfg \ No newline at end of file diff --git a/pyrtl/helperfuncs.py b/pyrtl/helperfuncs.py index 9731b239..b55931c7 100644 --- a/pyrtl/helperfuncs.py +++ b/pyrtl/helperfuncs.py @@ -789,20 +789,29 @@ def _convert_verilog_str(val: str, bitwidth: int = None, raise PyrtlError('error, "signed" option with verilog-style string constants not supported') bases = {'b': 2, 'o': 8, 'd': 10, 'h': 16, 'x': 16} - passed_bitwidth = bitwidth neg = False if val.startswith('-'): neg = True val = val[1:] + split_string = val.lower().split("'") if len(split_string) != 2: raise PyrtlError('error, string not in verilog style format') try: - bitwidth = int(split_string[0]) + verilog_bitwidth = int(split_string[0]) + bitwidth = bitwidth or verilog_bitwidth # if bitwidth is None, use verilog_bitwidth + if verilog_bitwidth > bitwidth: + raise PyrtlError( + "bitwidth parameter passed (%d) cannot fit Verilog-style constant with bitwidth %d" + % (bitwidth, verilog_bitwidth) + + " (if bitwidth=None is used, PyRTL will determine the bitwidth from the " + "Verilog-style constant specification)" + ) + sval = split_string[1] if sval[0] == 's': - raise PyrtlError('error, signed integers are not supported in verilog-style constants') + raise PyrtlError('error, signed integers are not supported in Verilog-style constants') base = 10 if sval[0] in bases: base = bases[sval[0]] @@ -811,17 +820,12 @@ def _convert_verilog_str(val: str, bitwidth: int = None, num = int(sval, base) except (IndexError, ValueError): raise PyrtlError('error, string not in verilog style format') + if neg and num: if (num >> bitwidth - 1): raise PyrtlError('error, insufficient bits for negative number') num = (1 << bitwidth) - num - if passed_bitwidth and passed_bitwidth != bitwidth: - raise PyrtlError('error, bitwidth parameter of constant does not match' - ' the bitwidth infered from the verilog style specification' - ' (if bitwidth=None is used, pyrtl will determine the bitwidth from the' - ' verilog-style constant specification)') - if num >> bitwidth != 0: raise PyrtlError('specified bitwidth %d for verilog constant insufficient to store value %d' % (bitwidth, num)) diff --git a/pyrtl/wire.py b/pyrtl/wire.py index b5e5e94c..e3b30c9d 100644 --- a/pyrtl/wire.py +++ b/pyrtl/wire.py @@ -852,11 +852,15 @@ def __init__(self, bitwidth: int, name: str = '', reset_value: int = None, super(Register, self).__init__(bitwidth=bitwidth, name=name, block=block) self.reg_in = None # wire vector setting self.next if reset_value is not None: - reset_value, rst_bitwidth = infer_val_and_bitwidth(reset_value) + reset_value, rst_bitwidth = infer_val_and_bitwidth( + reset_value, + bitwidth=bitwidth, + ) if rst_bitwidth > bitwidth: raise PyrtlError( 'reset_value "%s" cannot fit in the specified %d bits for this register' - % (str(reset_value), bitwidth)) + % (str(reset_value), bitwidth) + ) self.reset_value = reset_value @property diff --git a/tests/test_wire.py b/tests/test_wire.py index 070a4960..ea50a1e7 100644 --- a/tests/test_wire.py +++ b/tests/test_wire.py @@ -263,13 +263,38 @@ def test_reset_value_as_string(self): self.assertEqual(r.reset_value, 1) def test_invalid_reset_value_too_large(self): - with self.assertRaisesRegex(pyrtl.PyrtlError, "cannot fit in the specified"): + with self.assertRaises(pyrtl.PyrtlError): r = pyrtl.Register(4, reset_value=16) def test_invalid_reset_value_too_large_as_string(self): - with self.assertRaisesRegex(pyrtl.PyrtlError, "cannot fit in the specified"): + with self.assertRaises(pyrtl.PyrtlError): r = pyrtl.Register(4, reset_value="5'd16") + def test_negative_reset_value(self): + r = pyrtl.Register(4, reset_value=-4) + self.assertEqual( + pyrtl.helperfuncs.val_to_signed_integer(r.reset_value, r.bitwidth), + -4 + ) + + def test_negative_reset_value_as_string(self): + r = pyrtl.Register(4, reset_value="-4'd1") + self.assertEqual( + pyrtl.helperfuncs.val_to_signed_integer(r.reset_value, r.bitwidth), + -1 + ) + + def test_invalid_negative_reset_value_as_string(self): + with self.assertRaises(pyrtl.PyrtlError): + r = pyrtl.Register(2, reset_value="-4'd1") + + def test_extending_negative_reset_value_as_string(self): + r = pyrtl.Register(4, reset_value="-3'd3") + self.assertEqual( + pyrtl.helperfuncs.val_to_signed_integer(r.reset_value, r.bitwidth), + -3 + ) + def test_invalid_reset_value_not_an_integer(self): with self.assertRaises(pyrtl.PyrtlError): r = pyrtl.Register(4, reset_value='hello') @@ -345,7 +370,6 @@ def test_bad_string(self): self.assert_bad_const("5'b111111'") self.assert_bad_const("'") self.assert_bad_const("'1") - self.assert_bad_const("2'b01", bitwidth=3) self.assert_bad_const("1'") def test_bool(self):