@@ -669,12 +669,14 @@ def emit_value(self, builder):
669
669
670
670
671
671
class NetlistEmitter :
672
- def __init__ (self , netlist : _nir .Netlist , design ):
672
+ def __init__ (self , netlist : _nir .Netlist , design , * , all_undef_to_ff = False ):
673
673
self .netlist = netlist
674
674
self .design = design
675
+ self .all_undef_to_ff = all_undef_to_ff
675
676
self .drivers = _ast .SignalDict ()
676
677
self .io_ports : dict [_ast .IOPort , int ] = {}
677
678
self .rhs_cache : dict [int , Tuple [_nir .Value , bool , _ast .Value ]] = {}
679
+ self .fragment_module_idx : dict [Fragment , int ] = {}
678
680
679
681
# Collected for driver conflict diagnostics only.
680
682
self .late_net_to_signal = {}
@@ -1358,6 +1360,39 @@ def emit_drivers(self):
1358
1360
src_loc = driver .signal .src_loc
1359
1361
self .connect (self .emit_signal (driver .signal ), value , src_loc = src_loc )
1360
1362
1363
+ def emit_undef_ff (self ):
1364
+ # Connect all completely undriven signals to flip-flops with const-0 clock. This is used
1365
+ # for simulation targets, so that undriven signals have allocated storage that can be
1366
+ # used by the testbench to drive them, instead of being hardwired to the init value
1367
+ # constant.
1368
+ for signal , value in self .netlist .signals .items ():
1369
+ fragment = self .design .signal_lca [signal ]
1370
+ module_idx = self .fragment_module_idx [fragment ]
1371
+ pos = 0
1372
+ while pos < len (signal ):
1373
+ net = value [pos ]
1374
+ if not net .is_late or net in self .netlist .connections :
1375
+ pos += 1
1376
+ else :
1377
+ end_pos = pos
1378
+ while (end_pos < len (signal ) and
1379
+ value [end_pos ].is_late and
1380
+ value [end_pos ] not in self .netlist .connections ):
1381
+ end_pos += 1
1382
+ init = (signal .init >> pos ) & ((1 << (end_pos - pos )) - 1 )
1383
+ cell = _nir .FlipFlop (module_idx ,
1384
+ data = value [pos :end_pos ],
1385
+ init = init ,
1386
+ clk = _nir .Net .from_const (0 ),
1387
+ clk_edge = "pos" ,
1388
+ arst = _nir .Net .from_const (0 ),
1389
+ attributes = {},
1390
+ src_loc = signal .src_loc ,
1391
+ )
1392
+ ff_value = self .netlist .add_value_cell (end_pos - pos , cell )
1393
+ self .connect (value [pos :end_pos ], ff_value , src_loc = signal .src_loc )
1394
+ pos = end_pos
1395
+
1361
1396
def emit_undriven (self ):
1362
1397
# Connect all undriven signal bits to their initial values. This can only happen for entirely
1363
1398
# undriven signals, or signals that are partially driven by instances.
@@ -1373,6 +1408,7 @@ def emit_fragment(self, fragment: _ir.Fragment, parent_module_idx: 'int | None',
1373
1408
if isinstance (fragment , _ir .Instance ):
1374
1409
assert parent_module_idx is not None
1375
1410
self .emit_instance (parent_module_idx , fragment , name = fragment_name [- 1 ])
1411
+ self .fragment_module_idx [fragment ] = parent_module_idx
1376
1412
elif isinstance (fragment , _mem .MemoryInstance ):
1377
1413
assert parent_module_idx is not None
1378
1414
memory = self .emit_memory (parent_module_idx , fragment , name = fragment_name [- 1 ])
@@ -1381,11 +1417,14 @@ def emit_fragment(self, fragment: _ir.Fragment, parent_module_idx: 'int | None',
1381
1417
write_ports .append (self .emit_write_port (parent_module_idx , fragment , port , memory ))
1382
1418
for port in fragment ._read_ports :
1383
1419
self .emit_read_port (parent_module_idx , fragment , port , memory , write_ports )
1420
+ self .fragment_module_idx [fragment ] = parent_module_idx
1384
1421
elif isinstance (fragment , _ir .IOBufferInstance ):
1385
1422
assert parent_module_idx is not None
1386
1423
self .emit_iobuffer (parent_module_idx , fragment )
1424
+ self .fragment_module_idx [fragment ] = parent_module_idx
1387
1425
elif type (fragment ) is _ir .Fragment :
1388
1426
module_idx = self .netlist .add_module (parent_module_idx , fragment_name , src_loc = fragment .src_loc , cell_src_loc = cell_src_loc )
1427
+ self .fragment_module_idx [fragment ] = module_idx
1389
1428
signal_names = self .design .fragments [fragment ].signal_names
1390
1429
self .netlist .modules [module_idx ].signal_names = signal_names
1391
1430
io_port_names = self .design .fragments [fragment ].io_port_names
@@ -1402,13 +1441,15 @@ def emit_fragment(self, fragment: _ir.Fragment, parent_module_idx: 'int | None',
1402
1441
if parent_module_idx is None :
1403
1442
self .emit_drivers ()
1404
1443
self .emit_top_ports (fragment )
1444
+ if self .all_undef_to_ff :
1445
+ self .emit_undef_ff ()
1405
1446
self .emit_undriven ()
1406
1447
else :
1407
1448
assert False # :nocov:
1408
1449
1409
1450
1410
- def _emit_netlist (netlist : _nir .Netlist , design ):
1411
- NetlistEmitter (netlist , design ).emit_fragment (design .fragment , None )
1451
+ def _emit_netlist (netlist : _nir .Netlist , design , * , all_undef_to_ff = False ):
1452
+ NetlistEmitter (netlist , design , all_undef_to_ff = all_undef_to_ff ).emit_fragment (design .fragment , None )
1412
1453
1413
1454
1414
1455
def _compute_net_flows (netlist : _nir .Netlist ):
@@ -1640,13 +1681,13 @@ def _compute_io_ports(netlist: _nir.Netlist, ports):
1640
1681
visited .update (value )
1641
1682
1642
1683
1643
- def build_netlist (fragment , ports = (), * , name = "top" , ** kwargs ):
1684
+ def build_netlist (fragment , ports = (), * , name = "top" , all_undef_to_ff = False , ** kwargs ):
1644
1685
if isinstance (fragment , Design ):
1645
1686
design = fragment
1646
1687
else :
1647
1688
design = fragment .prepare (ports = ports , hierarchy = (name ,), ** kwargs )
1648
1689
netlist = _nir .Netlist ()
1649
- _emit_netlist (netlist , design )
1690
+ _emit_netlist (netlist , design , all_undef_to_ff = all_undef_to_ff )
1650
1691
netlist .resolve_all_nets ()
1651
1692
_compute_net_flows (netlist )
1652
1693
_compute_ports (netlist )
0 commit comments