Skip to content

Commit a968875

Browse files
committed
Revert isinstance changes from 3a1bbd2 because they weren't always working.
3a1bbd2 attempted to make `wire_struct` and `wire_matrix` pass `isinstance` checks without a call to `as_wires`. This did not work reliably because there is only one `wire_struct` or `wire_matrix` class created for each type, and that one class would get registered as subclasses for different WireVector types. So this example did not work: ``` const_byte = Byte(Byte=0xAB) input_byte = Byte(concatenated_type=pyrtl.Input) # With 3a1bbd2, this would return True, but it should return False. isinstance(const_byte, pyrtl.Input) ``` This commit reverts broken the subclass registration. The safest way to check the actual type of a `wire_struct` or `wire_matrix` is to call `as_wires` to unwrap the `WrappedWireVector`. Added documentation and tests.
1 parent 3a1bbd2 commit a968875

4 files changed

Lines changed: 63 additions & 61 deletions

File tree

pyrtl/helperfuncs.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1507,13 +1507,19 @@ def wire_struct(wire_struct_spec):
15071507
Both the instance and the slices are first-class :class:`WireVector`, so they can be
15081508
manipulated with all the usual PyRTL operators::
15091509
1510-
>>> isinstance(byte, pyrtl.WireVector)
1510+
>>> isinstance(pyrtl.as_wires(byte), pyrtl.WireVector)
15111511
True
1512-
>>> isinstance(byte.low, pyrtl.WireVector)
1512+
>>> isinstance(pyrtl.as_wires(byte.low), pyrtl.WireVector)
15131513
True
1514-
>>> isinstance(byte.high, pyrtl.WireVector)
1514+
>>> isinstance(pyrtl.as_wires(byte.high), pyrtl.WireVector)
15151515
True
15161516
1517+
.. NOTE::
1518+
1519+
The instance and slices may be proxies with the same interface as
1520+
:class:`WireVector`, so you must call :func:`as_wires` before inspecting their
1521+
types.
1522+
15171523
``len()`` returns the number of components in the ``@wire_struct``::
15181524
15191525
>>> byte = Byte(Byte=0xAB)
@@ -1609,7 +1615,7 @@ class CacheLine:
16091615
>>> byte = Byte(name="output_byte", component_type=pyrtl.Output,
16101616
... Byte=0xCD)
16111617
1612-
>>> isinstance(byte.high, pyrtl.Output)
1618+
>>> isinstance(pyrtl.as_wires(byte.high), pyrtl.Output)
16131619
True
16141620
>>> byte.high.name
16151621
'output_byte.high'
@@ -1619,7 +1625,7 @@ class CacheLine:
16191625
16201626
# Generates an Input named ``input_byte``.
16211627
>>> input_byte = Byte(name="input_byte", concatenated_type=pyrtl.Input)
1622-
>>> isinstance(input_byte, pyrtl.Input)
1628+
>>> isinstance(pyrtl.as_wires(input_byte), pyrtl.Input)
16231629
True
16241630
>>> isinstance(input_byte, Byte)
16251631
True
@@ -1823,10 +1829,6 @@ class Byte:
18231829
bitwidth=self._bitwidth, name=name, block=block
18241830
)
18251831

1826-
# Register our type as a subclass of `concatenated_type` so
1827-
# isinstance(wire_struct_instance, concatenated_type)
1828-
# returns `True`. See the documentation for `abc.ABCMeta.register()`.
1829-
concatenated_type.register(type(self))
18301832
WrappedWireVector.__init__(self, wire=concatenated)
18311833

18321834
# self._components maps from component name to each component's WireVector.
@@ -1978,11 +1980,11 @@ def wire_matrix(component_schema, size: int, class_name: str | None = None):
19781980
Both the instance and the slices are first-class :class:`WireVector`, so they can be
19791981
manipulated with all the usual PyRTL operators::
19801982
1981-
>>> isinstance(word, pyrtl.WireVector)
1983+
>>> isinstance(pyrtl.as_wires(word), pyrtl.WireVector)
19821984
True
19831985
>>> word.bitwidth
19841986
32
1985-
>>> isinstance(word[0], pyrtl.WireVector)
1987+
>>> isinstance(pyrtl.as_wires(word[0]), pyrtl.WireVector)
19861988
True
19871989
>>> word[0].bitwidth
19881990
8
@@ -2068,7 +2070,7 @@ class Byte:
20682070
... component_type=pyrtl.Output,
20692071
... values=[0x89ABCDEF])
20702072
2071-
>>> isinstance(word[1], pyrtl.Output)
2073+
>>> isinstance(pyrtl.as_wires(word[1]), pyrtl.Output)
20722074
True
20732075
>>> word[1].name
20742076
'output_word[1]'
@@ -2078,7 +2080,7 @@ class Byte:
20782080
20792081
# Generates an Input named ``input_word``.
20802082
>>> word = Word(name="input_word", concatenated_type=pyrtl.Input)
2081-
>>> isinstance(word, pyrtl.Input)
2083+
>>> isinstance(pyrtl.as_wires(word), pyrtl.Input)
20822084
True
20832085
>>> isinstance(word, Word)
20842086
True
@@ -2129,10 +2131,6 @@ def _init(
21292131
bitwidth=self._bitwidth, name=name, block=block
21302132
)
21312133

2132-
# Register our type as a subclass of `concatenated_type` so
2133-
# isinstance(wire_matrix_instance, concatenated_type)
2134-
# returns `True`. See the documentation for `abc.ABCMeta.register()`.
2135-
concatenated_type.register(type(self))
21362134
WrappedWireVector.__init__(self, wire=concatenated)
21372135

21382136
schema = []

pyrtl/memory.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import collections
2020
import numbers
2121
import types
22-
from abc import ABCMeta
2322
from collections.abc import Callable, Sequence
2423
from typing import NamedTuple
2524

@@ -48,7 +47,7 @@ def _reset_memory_indexer():
4847
_memIndex = _NameIndexer()
4948

5049

51-
class _MemIndexed(WireVector, metaclass=ABCMeta):
50+
class _MemIndexed(WireVector):
5251
"""Object used internally to route memory assigns correctly.
5352
5453
The normal PyRTL user should never need to be aware that this class exists, hence

pyrtl/wire.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919
import numbers
2020
import re
2121
import traceback
22-
from abc import ABCMeta
2322

2423
from pyrtl import core # needed for _setting_keep_wirevector_call_stack
2524
from pyrtl.core import Block, LogicNet, _NameIndexer, working_block
@@ -57,7 +56,7 @@ def next_tempvar_name(name=""):
5756
return name
5857

5958

60-
class WireVector(metaclass=ABCMeta):
59+
class WireVector:
6160
"""The main class for describing the connections between operators.
6261
6362
:class:`WireVectors<WireVector>` act much like a list of wires, except that there is
@@ -1465,7 +1464,7 @@ def _extend_with_bit(self, bitwidth, extbit):
14651464
# |__ \_/ | |__ |\ | | \ |__ | \ \ / |__ / ` | / \ |__) /__`
14661465
# |___ / \ | |___ | \| |__/ |___ |__/ \/ |___ \__, | \__/ | \ .__/
14671466
#
1468-
class Input(WireVector, metaclass=ABCMeta):
1467+
class Input(WireVector):
14691468
"""A :class:`WireVector` placeholder for inputs to a :class:`Block`.
14701469
14711470
.. doctest only::
@@ -1527,7 +1526,7 @@ def __ior__(self, _):
15271526
raise PyrtlError(msg)
15281527

15291528

1530-
class Output(WireVector, metaclass=ABCMeta):
1529+
class Output(WireVector):
15311530
"""A :class:`WireVector` type denoting outputs of a :class:`Block`.
15321531
15331532
.. doctest only::
@@ -1555,7 +1554,7 @@ def __init__(
15551554
super().__init__(bitwidth, name, block)
15561555

15571556

1558-
class Const(WireVector, metaclass=ABCMeta):
1557+
class Const(WireVector):
15591558
"""A :class:`WireVector` representation of a constant value.
15601559
15611560
Converts from :class:`bool`, :class:`int`, or Verilog-style :class:`str` to a
@@ -1661,7 +1660,7 @@ def __ior__(self, _):
16611660
raise PyrtlError(msg)
16621661

16631662

1664-
class Register(WireVector, metaclass=ABCMeta):
1663+
class Register(WireVector):
16651664
"""A WireVector with an embedded register state element.
16661665
16671666
Registers only update their outputs on the rising edges of an implicit clock signal.

tests/test_helperfuncs.py

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,15 +1351,15 @@ def test_concatenate(self):
13511351
# Concatenates to 'byte'.
13521352
byte = Byte(name="byte", high=0xA, low=0xB)
13531353
self.assertTrue(isinstance(byte, Byte))
1354-
self.assertTrue(isinstance(byte, pyrtl.WireVector))
1354+
self.assertTrue(isinstance(pyrtl.as_wires(byte), pyrtl.WireVector))
13551355
self.assertEqual(Byte.__name__, "Byte")
13561356
self.assertEqual(len(byte), 2)
13571357
self.assertEqual(byte.bitwidth, 8)
13581358
self.assertEqual(len(pyrtl.as_wires(byte)), 8)
13591359
self.assertEqual(len(byte.high), 4)
1360-
self.assertTrue(isinstance(byte.high, pyrtl.WireVector))
1360+
self.assertTrue(isinstance(pyrtl.as_wires(byte.high), pyrtl.WireVector))
13611361
self.assertEqual(len(byte.low), 4)
1362-
self.assertTrue(isinstance(byte.low, pyrtl.WireVector))
1362+
self.assertTrue(isinstance(pyrtl.as_wires(byte.low), pyrtl.WireVector))
13631363

13641364
sim = pyrtl.Simulation()
13651365
sim.step()
@@ -1392,11 +1392,11 @@ def test_slice(self):
13921392
byte = Byte(name="byte", Byte=0xCD)
13931393

13941394
# Constants are sliced immediately.
1395-
self.assertTrue(isinstance(byte, pyrtl.Const))
1395+
self.assertTrue(isinstance(pyrtl.as_wires(byte), pyrtl.Const))
13961396
self.assertEqual(byte.val, 0xCD)
1397-
self.assertTrue(isinstance(byte.high, pyrtl.Const))
1397+
self.assertTrue(isinstance(pyrtl.as_wires(byte.high), pyrtl.Const))
13981398
self.assertEqual(byte.high.val, 0xC)
1399-
self.assertTrue(isinstance(byte.low, pyrtl.Const))
1399+
self.assertTrue(isinstance(pyrtl.as_wires(byte.low), pyrtl.Const))
14001400
self.assertEqual(byte.low.val, 0xD)
14011401

14021402
def test_underscore_value_slice(self):
@@ -1407,31 +1407,37 @@ def test_underscore_value_slice(self):
14071407
byte = Byte(name="byte", _value=0xCD)
14081408

14091409
# Constants are sliced immediately.
1410-
self.assertTrue(isinstance(byte, pyrtl.Const))
1410+
self.assertTrue(isinstance(pyrtl.as_wires(byte), pyrtl.Const))
14111411
self.assertEqual(byte.val, 0xCD)
1412-
self.assertTrue(isinstance(byte.high, pyrtl.Const))
1412+
self.assertTrue(isinstance(pyrtl.as_wires(byte.high), pyrtl.Const))
14131413
self.assertEqual(byte.high.val, 0xC)
1414-
self.assertTrue(isinstance(byte.low, pyrtl.Const))
1414+
self.assertTrue(isinstance(pyrtl.as_wires(byte.low), pyrtl.Const))
14151415
self.assertEqual(byte.low.val, 0xD)
14161416

14171417
def test_input_slice(self):
14181418
"""Given Input concatenated high+low, observe high and low."""
14191419
byte = Byte(name="byte", concatenated_type=pyrtl.Input)
14201420

1421-
self.assertTrue(isinstance(byte, pyrtl.Input))
1421+
self.assertTrue(isinstance(pyrtl.as_wires(byte), pyrtl.Input))
14221422

14231423
sim = pyrtl.Simulation()
14241424
sim.step(provided_inputs={"byte": 0xAB})
14251425
self.assertEqual(sim.inspect("byte"), 0xAB)
14261426
self.assertEqual(sim.inspect("byte.high"), 0xA)
14271427
self.assertEqual(sim.inspect("byte.low"), 0xB)
14281428

1429+
# Make a Const byte and check its type. This ensures we don't get the type of
1430+
# `const_byte` mixed up with the type of `byte`.
1431+
const_byte = Byte(name="const_byte", Byte=0xCD)
1432+
self.assertEqual(type(pyrtl.as_wires(const_byte)), pyrtl.Const)
1433+
self.assertFalse(isinstance(pyrtl.as_wires(const_byte), pyrtl.Input))
1434+
14291435
def test_input_concatenate(self):
14301436
"""Given Input high and low, observe concatenated high+low."""
14311437
byte = Byte(name="byte", component_type=pyrtl.Input)
14321438

1433-
self.assertTrue(isinstance(byte.high, pyrtl.Input))
1434-
self.assertTrue(isinstance(byte.low, pyrtl.Input))
1439+
self.assertTrue(isinstance(pyrtl.as_wires(byte.high), pyrtl.Input))
1440+
self.assertTrue(isinstance(pyrtl.as_wires(byte.low), pyrtl.Input))
14351441

14361442
sim = pyrtl.Simulation()
14371443
sim.step(provided_inputs={"byte.low": 0xB, "byte.high": 0xA})
@@ -1443,8 +1449,8 @@ def test_output_slice(self):
14431449
"""Drive concatenated high+low, observe high and low as Outputs."""
14441450
byte = Byte(name="byte", component_type=pyrtl.Output, Byte=0xCD)
14451451

1446-
self.assertTrue(isinstance(byte.high, pyrtl.Output))
1447-
self.assertTrue(isinstance(byte.low, pyrtl.Output))
1452+
self.assertTrue(isinstance(pyrtl.as_wires(byte.high), pyrtl.Output))
1453+
self.assertTrue(isinstance(pyrtl.as_wires(byte.low), pyrtl.Output))
14481454

14491455
sim = pyrtl.Simulation()
14501456
sim.step()
@@ -1456,7 +1462,7 @@ def test_output_concatenate(self):
14561462
"""Drive high and low, observe concatenated high+low as Output."""
14571463
byte = Byte(name="byte", concatenated_type=pyrtl.Output, high=0xA, low=0xB)
14581464

1459-
self.assertTrue(isinstance(byte, pyrtl.Output))
1465+
self.assertTrue(isinstance(pyrtl.as_wires(byte), pyrtl.Output))
14601466

14611467
sim = pyrtl.Simulation()
14621468
sim.step()
@@ -1470,7 +1476,7 @@ def test_register_slice(self):
14701476
next_value = pyrtl.Input(name="next_value", bitwidth=8)
14711477
byte.next <<= next_value
14721478

1473-
self.assertTrue(isinstance(byte, pyrtl.Register))
1479+
self.assertTrue(isinstance(pyrtl.as_wires(byte), pyrtl.Register))
14741480

14751481
sim = pyrtl.Simulation()
14761482
# On the first cycle, set the register's value to 0xAB, but it will take one
@@ -1524,28 +1530,28 @@ def test_pixel_slice(self):
15241530
self.assertEqual(len(pyrtl.as_wires(pixel)), 24)
15251531

15261532
# Constants are sliced immediately.
1527-
self.assertTrue(isinstance(pixel, pyrtl.Const))
1533+
self.assertTrue(isinstance(pyrtl.as_wires(pixel), pyrtl.Const))
15281534
self.assertEqual(pixel.val, 0xABCDEF)
15291535

1530-
self.assertTrue(isinstance(pixel.red, pyrtl.Const))
1536+
self.assertTrue(isinstance(pyrtl.as_wires(pixel.red), pyrtl.Const))
15311537
self.assertEqual(pixel.red.val, 0xAB)
1532-
self.assertTrue(isinstance(pixel.red.high, pyrtl.Const))
1538+
self.assertTrue(isinstance(pyrtl.as_wires(pixel.red.high), pyrtl.Const))
15331539
self.assertEqual(pixel.red.high.val, 0xA)
1534-
self.assertTrue(isinstance(pixel.red.low, pyrtl.Const))
1540+
self.assertTrue(isinstance(pyrtl.as_wires(pixel.red.low), pyrtl.Const))
15351541
self.assertEqual(pixel.red.low.val, 0xB)
15361542

1537-
self.assertTrue(isinstance(pixel.green, pyrtl.Const))
1543+
self.assertTrue(isinstance(pyrtl.as_wires(pixel.green), pyrtl.Const))
15381544
self.assertEqual(pixel.green.val, 0xCD)
1539-
self.assertTrue(isinstance(pixel.green.high, pyrtl.Const))
1545+
self.assertTrue(isinstance(pyrtl.as_wires(pixel.green.high), pyrtl.Const))
15401546
self.assertEqual(pixel.green.high.val, 0xC)
1541-
self.assertTrue(isinstance(pixel.green.low, pyrtl.Const))
1547+
self.assertTrue(isinstance(pyrtl.as_wires(pixel.green.low), pyrtl.Const))
15421548
self.assertEqual(pixel.green.low.val, 0xD)
15431549

1544-
self.assertTrue(isinstance(pixel.blue, pyrtl.Const))
1550+
self.assertTrue(isinstance(pyrtl.as_wires(pixel.blue), pyrtl.Const))
15451551
self.assertEqual(pixel.blue.val, 0xEF)
1546-
self.assertTrue(isinstance(pixel.blue.high, pyrtl.Const))
1552+
self.assertTrue(isinstance(pyrtl.as_wires(pixel.blue.high), pyrtl.Const))
15471553
self.assertEqual(pixel.blue.high.val, 0xE)
1548-
self.assertTrue(isinstance(pixel.blue.low, pyrtl.Const))
1554+
self.assertTrue(isinstance(pyrtl.as_wires(pixel.blue.low), pyrtl.Const))
15491555
self.assertEqual(pixel.blue.low.val, 0xF)
15501556

15511557
def test_pixel_concatenate(self):
@@ -1693,14 +1699,14 @@ def setUp(self):
16931699
def test_wire_matrix_slice(self):
16941700
bitpair = BitPair(name="bitpair", values=[2])
16951701
self.assertTrue(isinstance(bitpair, BitPair))
1696-
self.assertTrue(isinstance(bitpair, pyrtl.WireVector))
1702+
self.assertTrue(isinstance(pyrtl.as_wires(bitpair), pyrtl.WireVector))
16971703
self.assertEqual(BitPair.__name__, "BitPair")
16981704
self.assertEqual(len(bitpair), 2)
16991705
self.assertEqual(bitpair.bitwidth, 2)
17001706
self.assertEqual(len(pyrtl.as_wires(bitpair)), 2)
1701-
self.assertTrue(isinstance(bitpair[0], pyrtl.WireVector))
1707+
self.assertTrue(isinstance(pyrtl.as_wires(bitpair[0]), pyrtl.WireVector))
17021708
self.assertEqual(len(bitpair[0]), 1)
1703-
self.assertTrue(isinstance(bitpair[1], pyrtl.WireVector))
1709+
self.assertTrue(isinstance(pyrtl.as_wires(bitpair[1]), pyrtl.WireVector))
17041710
self.assertEqual(len(bitpair[1]), 1)
17051711

17061712
# Constants are sliced immediately.
@@ -1787,8 +1793,8 @@ def test_anonymous_wire_matrix_concatenate(self):
17871793
def test_wire_matrix_input(self):
17881794
word = Word(name="word", component_type=pyrtl.Input)
17891795

1790-
self.assertTrue(isinstance(word[0], pyrtl.Input))
1791-
self.assertTrue(isinstance(word[1], pyrtl.Input))
1796+
self.assertTrue(isinstance(pyrtl.as_wires(word[0]), pyrtl.Input))
1797+
self.assertTrue(isinstance(pyrtl.as_wires(word[1]), pyrtl.Input))
17921798

17931799
sim = pyrtl.Simulation()
17941800
sim.step(provided_inputs={"word[0]": 0xAB, "word[1]": 0xCD})
@@ -1804,7 +1810,7 @@ def test_wire_matrix_register(self):
18041810
word = Word(name="word", concatenated_type=pyrtl.Register)
18051811
word.next <<= 0xABCD
18061812

1807-
self.assertTrue(isinstance(word, pyrtl.Register))
1813+
self.assertTrue(isinstance(pyrtl.as_wires(word), pyrtl.Register))
18081814

18091815
sim = pyrtl.Simulation()
18101816
sim.step()
@@ -1818,7 +1824,7 @@ def test_wire_matrix_composition_dword(self):
18181824
dword = DWord(name="dword", values=[0x89ABCDEF])
18191825

18201826
# Constants are sliced immediately.
1821-
self.assertTrue(isinstance(dword, pyrtl.Const))
1827+
self.assertTrue(isinstance(pyrtl.as_wires(dword), pyrtl.Const))
18221828
self.assertEqual(dword.val, 0x89ABCDEF)
18231829
self.assertEqual(dword[0].val, 0x89AB)
18241830
self.assertEqual(dword[0][0].val, 0x89)
@@ -1858,7 +1864,7 @@ def test_byte_matrix_const(self):
18581864
self.assertEqual(len(pyrtl.as_wires(byte_matrix)), 8)
18591865

18601866
# Constants are sliced immediately.
1861-
self.assertTrue(isinstance(byte_matrix, pyrtl.Const))
1867+
self.assertTrue(isinstance(pyrtl.as_wires(byte_matrix), pyrtl.Const))
18621868
self.assertEqual(byte_matrix.val, 0xAB)
18631869
self.assertEqual(byte_matrix[0].val, 0xAB)
18641870
self.assertEqual(byte_matrix[0].high.val, 0xA)
@@ -1867,7 +1873,7 @@ def test_byte_matrix_const(self):
18671873
def test_byte_matrix_input_slice(self):
18681874
byte_matrix = ByteMatrix(name="byte_matrix", component_type=pyrtl.Input)
18691875

1870-
self.assertTrue(isinstance(byte_matrix[0], pyrtl.Input))
1876+
self.assertTrue(isinstance(pyrtl.as_wires(byte_matrix[0]), pyrtl.Input))
18711877

18721878
sim = pyrtl.Simulation()
18731879
sim.step(provided_inputs={"byte_matrix[0]": 0xAB})
@@ -1879,7 +1885,7 @@ def test_byte_matrix_input_slice(self):
18791885
def test_byte_matrix_input_concatenate(self):
18801886
byte_matrix = ByteMatrix(name="byte_matrix", concatenated_type=pyrtl.Input)
18811887

1882-
self.assertTrue(isinstance(byte_matrix, pyrtl.Input))
1888+
self.assertTrue(isinstance(pyrtl.as_wires(byte_matrix), pyrtl.Input))
18831889

18841890
sim = pyrtl.Simulation()
18851891
sim.step(provided_inputs={"byte_matrix": 0xAB})

0 commit comments

Comments
 (0)