Skip to content

Commit 27859c8

Browse files
Allow negative Register reset values (#464)
* Allow negative Register reset values. Pass bitwidth to `infer_val_and_bitwidth` in `Register.__init__` to allow negative Register reset values. Add/edit tests for this behavior. * `infer_val_and_bitwidth` extends the bitwidth of Verilog-style constants up to the bitwidth passed to `infer_val_and_bitwidth`.
1 parent 7b8d86a commit 27859c8

File tree

4 files changed

+49
-14
lines changed

4 files changed

+49
-14
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,6 @@ ipynb-examples/.ipynb_checkpoints
5151

5252
# VS Code
5353
.vscode
54+
55+
# Python venv
56+
pyvenv.cfg

pyrtl/helperfuncs.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -789,20 +789,29 @@ def _convert_verilog_str(val: str, bitwidth: int = None,
789789
raise PyrtlError('error, "signed" option with verilog-style string constants not supported')
790790

791791
bases = {'b': 2, 'o': 8, 'd': 10, 'h': 16, 'x': 16}
792-
passed_bitwidth = bitwidth
793792

794793
neg = False
795794
if val.startswith('-'):
796795
neg = True
797796
val = val[1:]
797+
798798
split_string = val.lower().split("'")
799799
if len(split_string) != 2:
800800
raise PyrtlError('error, string not in verilog style format')
801801
try:
802-
bitwidth = int(split_string[0])
802+
verilog_bitwidth = int(split_string[0])
803+
bitwidth = bitwidth or verilog_bitwidth # if bitwidth is None, use verilog_bitwidth
804+
if verilog_bitwidth > bitwidth:
805+
raise PyrtlError(
806+
"bitwidth parameter passed (%d) cannot fit Verilog-style constant with bitwidth %d"
807+
% (bitwidth, verilog_bitwidth)
808+
+ " (if bitwidth=None is used, PyRTL will determine the bitwidth from the "
809+
"Verilog-style constant specification)"
810+
)
811+
803812
sval = split_string[1]
804813
if sval[0] == 's':
805-
raise PyrtlError('error, signed integers are not supported in verilog-style constants')
814+
raise PyrtlError('error, signed integers are not supported in Verilog-style constants')
806815
base = 10
807816
if sval[0] in bases:
808817
base = bases[sval[0]]
@@ -811,17 +820,12 @@ def _convert_verilog_str(val: str, bitwidth: int = None,
811820
num = int(sval, base)
812821
except (IndexError, ValueError):
813822
raise PyrtlError('error, string not in verilog style format')
823+
814824
if neg and num:
815825
if (num >> bitwidth - 1):
816826
raise PyrtlError('error, insufficient bits for negative number')
817827
num = (1 << bitwidth) - num
818828

819-
if passed_bitwidth and passed_bitwidth != bitwidth:
820-
raise PyrtlError('error, bitwidth parameter of constant does not match'
821-
' the bitwidth infered from the verilog style specification'
822-
' (if bitwidth=None is used, pyrtl will determine the bitwidth from the'
823-
' verilog-style constant specification)')
824-
825829
if num >> bitwidth != 0:
826830
raise PyrtlError('specified bitwidth %d for verilog constant insufficient to store value %d'
827831
% (bitwidth, num))

pyrtl/wire.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -852,11 +852,15 @@ def __init__(self, bitwidth: int, name: str = '', reset_value: int = None,
852852
super(Register, self).__init__(bitwidth=bitwidth, name=name, block=block)
853853
self.reg_in = None # wire vector setting self.next
854854
if reset_value is not None:
855-
reset_value, rst_bitwidth = infer_val_and_bitwidth(reset_value)
855+
reset_value, rst_bitwidth = infer_val_and_bitwidth(
856+
reset_value,
857+
bitwidth=bitwidth,
858+
)
856859
if rst_bitwidth > bitwidth:
857860
raise PyrtlError(
858861
'reset_value "%s" cannot fit in the specified %d bits for this register'
859-
% (str(reset_value), bitwidth))
862+
% (str(reset_value), bitwidth)
863+
)
860864
self.reset_value = reset_value
861865

862866
@property

tests/test_wire.py

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,13 +263,38 @@ def test_reset_value_as_string(self):
263263
self.assertEqual(r.reset_value, 1)
264264

265265
def test_invalid_reset_value_too_large(self):
266-
with self.assertRaisesRegex(pyrtl.PyrtlError, "cannot fit in the specified"):
266+
with self.assertRaises(pyrtl.PyrtlError):
267267
r = pyrtl.Register(4, reset_value=16)
268268

269269
def test_invalid_reset_value_too_large_as_string(self):
270-
with self.assertRaisesRegex(pyrtl.PyrtlError, "cannot fit in the specified"):
270+
with self.assertRaises(pyrtl.PyrtlError):
271271
r = pyrtl.Register(4, reset_value="5'd16")
272272

273+
def test_negative_reset_value(self):
274+
r = pyrtl.Register(4, reset_value=-4)
275+
self.assertEqual(
276+
pyrtl.helperfuncs.val_to_signed_integer(r.reset_value, r.bitwidth),
277+
-4
278+
)
279+
280+
def test_negative_reset_value_as_string(self):
281+
r = pyrtl.Register(4, reset_value="-4'd1")
282+
self.assertEqual(
283+
pyrtl.helperfuncs.val_to_signed_integer(r.reset_value, r.bitwidth),
284+
-1
285+
)
286+
287+
def test_invalid_negative_reset_value_as_string(self):
288+
with self.assertRaises(pyrtl.PyrtlError):
289+
r = pyrtl.Register(2, reset_value="-4'd1")
290+
291+
def test_extending_negative_reset_value_as_string(self):
292+
r = pyrtl.Register(4, reset_value="-3'd3")
293+
self.assertEqual(
294+
pyrtl.helperfuncs.val_to_signed_integer(r.reset_value, r.bitwidth),
295+
-3
296+
)
297+
273298
def test_invalid_reset_value_not_an_integer(self):
274299
with self.assertRaises(pyrtl.PyrtlError):
275300
r = pyrtl.Register(4, reset_value='hello')
@@ -345,7 +370,6 @@ def test_bad_string(self):
345370
self.assert_bad_const("5'b111111'")
346371
self.assert_bad_const("'")
347372
self.assert_bad_const("'1")
348-
self.assert_bad_const("2'b01", bitwidth=3)
349373
self.assert_bad_const("1'")
350374

351375
def test_bool(self):

0 commit comments

Comments
 (0)