@@ -669,12 +669,14 @@ def emit_value(self, builder):
669669
670670
671671class NetlistEmitter :
672- def __init__ (self , netlist : _nir .Netlist , design ):
672+ def __init__ (self , netlist : _nir .Netlist , design , * , all_undef_to_ff = False ):
673673 self .netlist = netlist
674674 self .design = design
675+ self .all_undef_to_ff = all_undef_to_ff
675676 self .drivers = _ast .SignalDict ()
676677 self .io_ports : dict [_ast .IOPort , int ] = {}
677678 self .rhs_cache : dict [int , Tuple [_nir .Value , bool , _ast .Value ]] = {}
679+ self .fragment_module_idx : dict [Fragment , int ] = {}
678680
679681 # Collected for driver conflict diagnostics only.
680682 self .late_net_to_signal = {}
@@ -1358,6 +1360,39 @@ def emit_drivers(self):
13581360 src_loc = driver .signal .src_loc
13591361 self .connect (self .emit_signal (driver .signal ), value , src_loc = src_loc )
13601362
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+
13611396 def emit_undriven (self ):
13621397 # Connect all undriven signal bits to their initial values. This can only happen for entirely
13631398 # 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',
13731408 if isinstance (fragment , _ir .Instance ):
13741409 assert parent_module_idx is not None
13751410 self .emit_instance (parent_module_idx , fragment , name = fragment_name [- 1 ])
1411+ self .fragment_module_idx [fragment ] = parent_module_idx
13761412 elif isinstance (fragment , _mem .MemoryInstance ):
13771413 assert parent_module_idx is not None
13781414 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',
13811417 write_ports .append (self .emit_write_port (parent_module_idx , fragment , port , memory ))
13821418 for port in fragment ._read_ports :
13831419 self .emit_read_port (parent_module_idx , fragment , port , memory , write_ports )
1420+ self .fragment_module_idx [fragment ] = parent_module_idx
13841421 elif isinstance (fragment , _ir .IOBufferInstance ):
13851422 assert parent_module_idx is not None
13861423 self .emit_iobuffer (parent_module_idx , fragment )
1424+ self .fragment_module_idx [fragment ] = parent_module_idx
13871425 elif type (fragment ) is _ir .Fragment :
13881426 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
13891428 signal_names = self .design .fragments [fragment ].signal_names
13901429 self .netlist .modules [module_idx ].signal_names = signal_names
13911430 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',
14021441 if parent_module_idx is None :
14031442 self .emit_drivers ()
14041443 self .emit_top_ports (fragment )
1444+ if self .all_undef_to_ff :
1445+ self .emit_undef_ff ()
14051446 self .emit_undriven ()
14061447 else :
14071448 assert False # :nocov:
14081449
14091450
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 )
14121453
14131454
14141455def _compute_net_flows (netlist : _nir .Netlist ):
@@ -1640,13 +1681,13 @@ def _compute_io_ports(netlist: _nir.Netlist, ports):
16401681 visited .update (value )
16411682
16421683
1643- def build_netlist (fragment , ports = (), * , name = "top" , ** kwargs ):
1684+ def build_netlist (fragment , ports = (), * , name = "top" , all_undef_to_ff = False , ** kwargs ):
16441685 if isinstance (fragment , Design ):
16451686 design = fragment
16461687 else :
16471688 design = fragment .prepare (ports = ports , hierarchy = (name ,), ** kwargs )
16481689 netlist = _nir .Netlist ()
1649- _emit_netlist (netlist , design )
1690+ _emit_netlist (netlist , design , all_undef_to_ff = all_undef_to_ff )
16501691 netlist .resolve_all_nets ()
16511692 _compute_net_flows (netlist )
16521693 _compute_ports (netlist )
0 commit comments