@@ -164,6 +164,144 @@ struct TimingStats {
164164 worst_hold_slack : i64 ,
165165}
166166
167+ /// Trace a clock pin back through buffers/inverters to find the primary input (cell 0).
168+ /// Returns Some(pinid) if a primary input is found, None otherwise.
169+ fn trace_clock_to_primary_input (
170+ netlistdb : & NetlistDB ,
171+ start_pinid : usize ,
172+ cell_library : CellLibrary ,
173+ verbose : bool ,
174+ ) -> Option < usize > {
175+ let mut current_pinid = start_pinid;
176+ let mut visited = std:: collections:: HashSet :: new ( ) ;
177+ let mut depth = 0 ;
178+
179+ loop {
180+ if visited. contains ( & current_pinid) {
181+ if verbose {
182+ clilog:: debug!( " Clock trace: cycle detected at depth {}" , depth) ;
183+ }
184+ return None ;
185+ }
186+ visited. insert ( current_pinid) ;
187+
188+ if visited. len ( ) > 10000 {
189+ if verbose {
190+ clilog:: debug!( " Clock trace: safety limit exceeded at depth {}" , depth) ;
191+ }
192+ return None ;
193+ }
194+
195+ // If this is an input pin, follow the net to its driver
196+ if netlistdb. pindirect [ current_pinid] == Direction :: I {
197+ let netid = netlistdb. pin2net [ current_pinid] ;
198+ if Some ( netid) == netlistdb. net_zero || Some ( netid) == netlistdb. net_one {
199+ if verbose {
200+ clilog:: debug!( " Clock trace: hit constant net at depth {}" , depth) ;
201+ }
202+ return None ;
203+ }
204+
205+ // Find driver pin on the net
206+ let net_pins_start = netlistdb. net2pin . start [ netid] ;
207+ let net_pins_end = if netid + 1 < netlistdb. net2pin . start . len ( ) {
208+ netlistdb. net2pin . start [ netid + 1 ]
209+ } else {
210+ netlistdb. net2pin . items . len ( )
211+ } ;
212+
213+ let mut driver_pin = None ;
214+ for & np in & netlistdb. net2pin . items [ net_pins_start..net_pins_end] {
215+ // Check for output (driver) pin
216+ if netlistdb. pindirect [ np] == Direction :: O {
217+ driver_pin = Some ( np) ;
218+ break ;
219+ }
220+ // Check for primary input (cell 0)
221+ if netlistdb. pin2cell [ np] == 0 {
222+ driver_pin = Some ( np) ;
223+ break ;
224+ }
225+ }
226+
227+ match driver_pin {
228+ Some ( dp) => {
229+ current_pinid = dp;
230+ depth += 1 ;
231+ }
232+ None => {
233+ if verbose {
234+ clilog:: debug!( " Clock trace: no driver found for net {} at depth {}" , netid, depth) ;
235+ }
236+ return None ;
237+ }
238+ }
239+ continue ;
240+ }
241+
242+ // This is an output pin - check if it's from cell 0 (primary input)
243+ let cellid = netlistdb. pin2cell [ current_pinid] ;
244+ if cellid == 0 {
245+ use netlistdb:: GeneralPinName ;
246+ if verbose && depth <= 5 {
247+ clilog:: debug!(
248+ " Clock trace: found primary input {} at depth {}" ,
249+ netlistdb. pinnames[ current_pinid] . dbg_fmt_pin( ) ,
250+ depth
251+ ) ;
252+ }
253+ return Some ( current_pinid) ;
254+ }
255+
256+ // Check if this cell is a buffer/inverter that we can trace through
257+ let celltype = netlistdb. celltypes [ cellid] . as_str ( ) ;
258+
259+ let is_buffer_or_inv = match cell_library {
260+ CellLibrary :: SKY130 => {
261+ let ct = extract_cell_type ( celltype) ;
262+ ct. starts_with ( "inv" )
263+ || ct. starts_with ( "clkinv" )
264+ || ct. starts_with ( "buf" )
265+ || ct. starts_with ( "clkbuf" )
266+ || ct. starts_with ( "clkdlybuf" )
267+ }
268+ _ => matches ! ( celltype, "INV" | "BUF" ) ,
269+ } ;
270+
271+ if !is_buffer_or_inv {
272+ if verbose && depth <= 5 {
273+ clilog:: debug!(
274+ " Clock trace: hit non-buffer cell {} ({}) at depth {}" ,
275+ cellid,
276+ celltype,
277+ depth
278+ ) ;
279+ }
280+ return None ;
281+ }
282+
283+ // Find the input pin "A" of the buffer/inverter
284+ let mut input_pin = None ;
285+ for ipin in netlistdb. cell2pin . iter_set ( cellid) {
286+ if netlistdb. pindirect [ ipin] == Direction :: I {
287+ let pin_name = netlistdb. pinnames [ ipin] . 1 . as_str ( ) ;
288+ if pin_name == "A" {
289+ input_pin = Some ( ipin) ;
290+ break ;
291+ }
292+ }
293+ }
294+
295+ match input_pin {
296+ Some ( ip) => {
297+ current_pinid = ip;
298+ depth += 1 ;
299+ }
300+ None => return None ,
301+ }
302+ }
303+ }
304+
167305fn main ( ) {
168306 clilog:: init_stderr_color_debug ( ) ;
169307 clilog:: set_max_print_count ( clilog:: Level :: Warn , "NL_SV_LIT" , 1 ) ;
@@ -252,13 +390,18 @@ fn main() {
252390 let is_clk = matches ! ( pin_name, "CLK" | "CLKin" | "PORT_R_CLK" | "PORT_W_CLK" ) ;
253391
254392 if is_clk {
255- let netid = netlistdb. pin2net [ pinid] ;
256- if Some ( netid) == netlistdb. net_zero || Some ( netid) == netlistdb. net_one {
257- continue ;
258- }
259- let root = netlistdb. net2pin . items [ netlistdb. net2pin . start [ netid] ] ;
260- if netlistdb. pin2cell [ root] == 0 {
261- posedge_monitor. insert ( root) ;
393+ // Trace clock pin back through buffers/inverters to primary input
394+ // Only log verbose for first few DFFs to avoid spam
395+ let log_this = args. verbose && posedge_monitor. is_empty ( ) ;
396+ if let Some ( primary_clk_pin) =
397+ trace_clock_to_primary_input ( & netlistdb, pinid, cell_library, log_this)
398+ {
399+ posedge_monitor. insert ( primary_clk_pin) ;
400+ } else if args. verbose && posedge_monitor. is_empty ( ) {
401+ clilog:: debug!(
402+ "Clock pin {} could not be traced to primary input" ,
403+ pinid
404+ ) ;
262405 }
263406 }
264407 }
0 commit comments