Skip to content

Commit c7f719a

Browse files
tpwruleswhitequark
authored andcommitted
hdl.ast: allow Signals to be privately named using name=""
* Given a private name `$\d+` in RTLIL (as they are not named in the IR) * Not automatically added to VCD files (as they are not named in the IR) * Cannot be traced to a VCD (as they have no name to put in the file) * Cannot be used with an unnamed top-level port (as there is no name)
1 parent 6ffafef commit c7f719a

File tree

6 files changed

+56
-6
lines changed

6 files changed

+56
-6
lines changed

amaranth/hdl/_ast.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1884,7 +1884,8 @@ class Signal(Value, DUID, metaclass=_SignalMeta):
18841884
If not specified, ``shape`` defaults to 1-bit and non-signed.
18851885
name : str
18861886
Name hint for this signal. If ``None`` (default) the name is inferred from the variable
1887-
name this ``Signal`` is assigned to.
1887+
name this ``Signal`` is assigned to. If the empty string, then this ``Signal`` is treated
1888+
as private and is generally hidden from view.
18881889
init : int or integral Enum
18891890
Reset (synchronous) or default (combinatorial) value.
18901891
When this ``Signal`` is assigned to in synchronous context and the corresponding clock
@@ -1920,7 +1921,10 @@ def __init__(self, shape=None, *, name=None, init=None, reset=None, reset_less=F
19201921

19211922
if name is not None and not isinstance(name, str):
19221923
raise TypeError(f"Name must be a string, not {name!r}")
1923-
self.name = name or tracer.get_var_name(depth=2 + src_loc_at, default="$signal")
1924+
if name is None:
1925+
self.name = tracer.get_var_name(depth=2 + src_loc_at, default="$signal")
1926+
else:
1927+
self.name = name
19241928

19251929
orig_shape = shape
19261930
if shape is None:
@@ -2102,7 +2106,10 @@ def _rhs_signals(self):
21022106
return SignalSet((self,))
21032107

21042108
def __repr__(self):
2105-
return f"(sig {self.name})"
2109+
if self.name != "":
2110+
return f"(sig {self.name})"
2111+
else:
2112+
return "(sig)"
21062113

21072114

21082115
@final

amaranth/hdl/_ir.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,8 @@ def _assign_port_names(self):
552552
assigned_names = {name for name, conn, dir in self.ports if name is not None}
553553
for name, conn, dir in self.ports:
554554
if name is None:
555+
if conn.name == "": # Nothing to name this port!
556+
raise TypeError("Signals with private names cannot be used in unnamed top-level ports")
555557
name = _add_name(assigned_names, conn.name)
556558
assigned_names.add(name)
557559
new_ports.append((name, conn, dir))
@@ -590,7 +592,7 @@ def _assign_names(self, fragment: Fragment, hierarchy: "tuple[str]"):
590592
frag_info.io_port_names[conn] = name
591593

592594
for signal in frag_info.used_signals:
593-
if signal not in frag_info.signal_names:
595+
if signal not in frag_info.signal_names and signal.name != "": # Private name shouldn't be added.
594596
frag_info.signal_names[signal] = _add_name(frag_info.assigned_names, signal.name)
595597
for port in frag_info.used_io_ports:
596598
if port not in frag_info.io_port_names:

amaranth/sim/core.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from .._utils import deprecated
55
from ..hdl._cd import *
66
from ..hdl._ir import *
7+
from ..hdl._ast import Value, ValueLike
78
from ._base import BaseEngine
89

910

@@ -238,5 +239,15 @@ def write_vcd(self, vcd_file, gtkw_file=None, *, traces=(), fs_per_delta=0):
238239
file.close()
239240
raise ValueError("Cannot start writing waveforms after advancing simulation time")
240241

242+
for trace in traces:
243+
if isinstance(trace, ValueLike):
244+
trace_cast = Value.cast(trace)
245+
for trace_signal in trace_cast._rhs_signals():
246+
if trace_signal.name == "":
247+
if trace_signal is trace:
248+
raise TypeError("Cannot trace signal with private name")
249+
else:
250+
raise TypeError(f"Cannot trace signal with private name (within {trace!r})")
251+
241252
return self._engine.write_vcd(vcd_file=vcd_file, gtkw_file=gtkw_file,
242253
traces=traces, fs_per_delta=fs_per_delta)

tests/test_hdl_ast.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,6 +1180,8 @@ def test_name(self):
11801180
self.assertEqual(s1.name, "s1")
11811181
s2 = Signal(name="sig")
11821182
self.assertEqual(s2.name, "sig")
1183+
s3 = Signal(name="")
1184+
self.assertEqual(s3.name, "")
11831185

11841186
def test_init(self):
11851187
s1 = Signal(4, init=0b111, reset_less=True)
@@ -1294,6 +1296,8 @@ def test_attrs(self):
12941296
def test_repr(self):
12951297
s1 = Signal()
12961298
self.assertEqual(repr(s1), "(sig s1)")
1299+
s2 = Signal(name="")
1300+
self.assertEqual(repr(s2), "(sig)")
12971301

12981302
def test_like(self):
12991303
s1 = Signal.like(Signal(4))

tests/test_hdl_ir.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -962,6 +962,7 @@ def test_assign_names_to_signals(self):
962962
o1 = Signal()
963963
o2 = Signal()
964964
o3 = Signal()
965+
o4 = Signal(name="")
965966
i1 = Signal(name="i")
966967

967968
f = Fragment()
@@ -980,6 +981,7 @@ def test_assign_names_to_signals(self):
980981
"o1": (o1, PortDirection.Output),
981982
"o2": (o2, PortDirection.Output),
982983
"o3": (o3, PortDirection.Output),
984+
"o4": (o4, PortDirection.Output),
983985
}
984986
design = f.prepare(ports)
985987
self.assertEqual(design.fragments[design.fragment].signal_names, SignalDict([
@@ -988,12 +990,20 @@ def test_assign_names_to_signals(self):
988990
(o1, "o1"),
989991
(o2, "o2"),
990992
(o3, "o3"),
993+
# (o4, "o4"), # Signal has a private name.
991994
(cd_sync.clk, "clk"),
992-
(cd_sync.rst, "rst$6"),
995+
(cd_sync.rst, "rst$7"),
993996
(cd_sync_norst.clk, "sync_norst_clk"),
994-
(i1, "i$7"),
997+
(i1, "i$8"),
995998
]))
996999

1000+
def test_wrong_private_unnamed_toplevel_ports(self):
1001+
s = Signal(name="")
1002+
f = Fragment()
1003+
with self.assertRaisesRegex(TypeError,
1004+
r"^Signals with private names cannot be used in unnamed top-level ports$"):
1005+
Design(f, ports=((None, s, None),), hierarchy=("top",))
1006+
9971007
def test_assign_names_to_fragments(self):
9981008
f = Fragment()
9991009
f.add_subfragment(a := Fragment())

tests/test_sim.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from amaranth.hdl._ir import *
1515
from amaranth.sim import *
1616
from amaranth.lib.memory import Memory
17+
from amaranth.lib.data import View, StructLayout
1718

1819
from .utils import *
1920
from amaranth._utils import _ignore_deprecated
@@ -1042,6 +1043,21 @@ def test_vcd_wrong_nonzero_time(self):
10421043
with sim.write_vcd(f):
10431044
pass
10441045

1046+
def test_vcd_private_signal(self):
1047+
sim = Simulator(Module())
1048+
with self.assertRaisesRegex(TypeError,
1049+
r"^Cannot trace signal with private name$"):
1050+
with open(os.path.devnull, "w") as f:
1051+
with sim.write_vcd(f, traces=(Signal(name=""),)):
1052+
pass
1053+
1054+
sim = Simulator(Module())
1055+
with self.assertRaisesRegex(TypeError,
1056+
r"^Cannot trace signal with private name \(within \(cat \(sig x\) \(sig\)\)\)$"):
1057+
with open(os.path.devnull, "w") as f:
1058+
with sim.write_vcd(f, traces=(Cat(Signal(name="x"), Signal(name="")),)):
1059+
pass
1060+
10451061
def test_no_negated_boolean_warning(self):
10461062
m = Module()
10471063
a = Signal()

0 commit comments

Comments
 (0)