OpenROAD icon indicating copy to clipboard operation
OpenROAD copied to clipboard

Global placement with `-timing_driven` results in critical error

Open mole99 opened this issue 3 months ago • 21 comments

Describe the bug

When performing global placement for a full chip design on gf180mcuD, I get the following error:

[CRITICAL RSZ-2008] buffering pin gf180mcu_fd_io__in_s_clk/Y: wire step options empty

If I omit -timing_driven, placement completes successfully.

Expected Behavior

Global placement should complete

Environment

Recent OpenROAD from 2025-09-01 (c47bc3b81431e1ce5f305ab75bad126c3adb2858)

To Reproduce

Here is a standalone reproducible:

gf180mcu_reproducible.zip

Relevant log output


Screenshots

No response

Additional Context

No response

mole99 avatar Sep 04 '25 15:09 mole99

@povik And this one as well :)

mole99 avatar Sep 18 '25 07:09 mole99

This is an interesting case. The issue is downstream of the IO cell specifying max fanout of 1 in the .lib. It will need some adjustment to the buffering algorithm to handle this.

povik avatar Sep 18 '25 11:09 povik

If this is a case of the .lib having unrealistic constraints, I think you can ignore it an issue a warning. Even if it's a commercial IP. It seems like the gf180 IO cells are not exactly the best.

rovinski avatar Sep 19 '25 03:09 rovinski

The gf180mcu I/O cells not being the best is an understatement. Turns out despite two separate voltage domains, you cannot have a HV and LV domain due to a lack of level shifters 😆

A warning would be a good enough solution I think.

mole99 avatar Sep 19 '25 15:09 mole99

Hi, just checking in if a warning could be added with relatively little effort? I'm currently blocked on this for the gf180mcu-project-template.

mole99 avatar Sep 26 '25 08:09 mole99

As a workaround, I tried increasing the fanout to 2 in the Liberty files for the I/O cells. However, that was not quite enough, I still got the same error. Increasing the fanout to 5 got me through the error.

Should OpenROAD not be able to add a buffer directly after the I/O cell output?

mole99 avatar Sep 30 '25 17:09 mole99

As a workaround, I tried increasing the fanout to 2 in the Liberty files for the I/O cells.

I'm surprised increasing to 2 wasn't enough but there will be some internal reason for it.

Should OpenROAD not be able to add a buffer directly after the I/O cell output?

That's what it should do but isn't smart enough to do yet. Another workaround would be to insert the buffer into the incoming netlist. It would need a (* dont_touch *) attribute to not get removed during optimization steps.

povik avatar Sep 30 '25 17:09 povik

I see! That would actually be a great workaround, if it worked ^^

    // Normal input
    gf180mcu_fd_io__in_c rst_n_pad (    
        .Y      (rst_n_PAD2CORE),
        .PAD    (rst_n_PAD),
        
        .PU     (1'b0),
        .PD     (1'b0)
    );
    
    (* keep, dont_touch *)
    gf180mcu_fd_sc_mcu7t5v0__buf_1 buf_rst_n (
        .I      (rst_n_PAD2CORE),
        .Z      (rst_n_PAD2CORE_buf)    
    );

Unfortunately I still get:

[CRITICAL RSZ-2008] buffering pin rst_n_pad/Y: wire step options empty                                                                                            

Through the OpenROAD GUI, I can confirm that buf_rst_n is not being removed.

mole99 avatar Oct 01 '25 08:10 mole99

@mole99 I wanted to see why the workaround doesn't work but it seems to work for me. I've used the following script to patch the design in your reproducible.

set db [ord::get_db]
set block [ord::get_db_block]
set i 1

foreach pad_inst [$block getInsts] {
	if {[[$pad_inst getMaster] getName] == "gf180mcu_fd_io__in_s" || [[$pad_inst getMaster] getName] == "gf180mcu_fd_io__in_c"} {
		puts "visiting [$pad_inst getName]"
		set buf [$db findMaster gf180mcu_fd_sc_mcu7t5v0__buf_1]
		set buf_in [$buf findMTerm I]
		set buf_out [$buf findMTerm Z]
		set pad_out [[$pad_inst getMaster] findMTerm Y]
		set pad_pin [$pad_inst getITerm $pad_out]
		set n1 [odb::dbNet_create $block "fixup_net$i"]
		set i1 [odb::dbInst_create $block $buf "fixup_buf$i"]
		set i [expr {$i + 1}]
		[$i1 getITerm $buf_in] connect $n1
		[$i1 getITerm $buf_out] connect [$pad_pin getNet]
		$i1 setDoNotTouch true
		$pad_pin connect $n1
	}
}

povik avatar Oct 06 '25 09:10 povik

Yours and mine way of applying the workaround are different so it's possible one works but not the other. If you package a reproducible, I'd be interested to take a look as it could suggest a different issue with the buffering algorithm than the one originally reported.

povik avatar Oct 06 '25 09:10 povik

Hi Martin, thank you for digging into this.

The latest reproducible is here: gf180mcu_reproducible.zip

I added a buffer directly after the input pad:

    (* keep, dont_touch *)
    gf180mcu_fd_sc_mcu7t5v0__buf_1 buf_rst_n (
        .I      (rst_n_PAD2CORE),
        .Z      (rst_n_PAD2CORE_buf)    
    );

I confirmed its existence in the design through the GUI.

Using this approach, the error remains:

[CRITICAL RSZ-2008] buffering pin rst_n_pad/Y: wire step options empty

mole99 avatar Oct 09 '25 08:10 mole99

Thanks Leo.

It looks like the attributes haven't propagated into a "dont touch" flag inside odb. If I add a command set_dont_touch buf_rst_n the error no longer shows up on rst_n_pad/Y but on another pin which doesn't have the buffer. Let me look into this more.

povik avatar Oct 09 '25 09:10 povik

Sorry Martin! That errors seems to be on my side. The netlist synthesized by yosys does not seem to keep any of the attributes in LibreLane. Let me look into this as well.

mole99 avatar Oct 09 '25 09:10 mole99

That's alright. Are you perhaps passing -noattr to write_verilog in your yosys script?

povik avatar Oct 09 '25 09:10 povik

Yes, that's what happens, but I think on purpose.

I can confirm that by manually injecting the attribute into the synthesized netlist and starting the flow after synthesis, it successfully runs through.

  (* dont_touch *)
  gf180mcu_fd_sc_mcu7t5v0__buf_1 buf_rst_n (
    .I(rst_n_PAD2CORE),
    .Z(\i_chip_core.rst_n )
  );

However, I also get:

WARNING  [ODB-0383] buf_rst_n is marked do not touch and will be skipped in global connections.

Which is why adding the dont_touch attribute to the Verilog might not be the best approach.

I just remembered that LibreLane has a RSZ_DONT_TOUCH_LIST variable that it uses to apply the dont_touch attribute before operations such as resizing, and removes it afterwards.

However, RSZ_DONT_TOUCH_LIST is not used for global placement due to this issue: https://github.com/librelane/librelane/issues/695

I'm setting both:

RSZ_DONT_TOUCH_LIST:
- buf_rst_n

PL_KEEP_RESIZE_BELOW_OVERFLOW: 0

But it still gets stuck at global placement. Maybe a wrong configuration from my side...

It would be really helpful to hear your thoughts about this dont_touch situation, what would be the best approach to it?

To me, it looks like a dont_resize option might be useful. It seems a similar feature was proposed here: https://github.com/The-OpenROAD-Project/OpenROAD/issues/478

mole99 avatar Oct 09 '25 09:10 mole99

I've opened a discussion thread

povik avatar Oct 09 '25 10:10 povik

I encountered this issue in another design, where it does not happen on the I/O pads, but on a stdcell:

...
[INFO RSZ-0034] Found 3 slew violations.
[INFO RSZ-0035] Found 46 fanout violations.
[INFO RSZ-0038] Inserted 232 buffers in 46 nets.
   Iter   |    Area   | Removed | Inserted |   Pins
          |           | Buffers | Buffers  | Remaining
-------------------------------------------------------
        0 |     +0.0% |       0 |        0 |       468
       46 |     +0.0% |       0 |        0 |       422
       92 |     +0.0% |       0 |        0 |       376
[CRITICAL RSZ-2008] buffering pin _545_/ZN: wire step options empty

(I also found out how to package a reproducible in LibreLane, which doesn't help much since it's packaged for LibreLane, but it's slightly faster than doing it completely manually.)

reproducible.zip

Simply run: ./run.sh

mole99 avatar Oct 11 '25 09:10 mole99

@mole99 In LibreLane it's librelane.steps create-reproducible to create the LibreLane reproducible (for us as maintainers) and then ./run_ol.sh eject to create a shell-only reproducible (to report issues to tools upstream.)

donn avatar Oct 28 '25 08:10 donn

The issue persists btw. More up-to-date reproducible in #8752

donn avatar Oct 28 '25 08:10 donn

EDIT: Wait, duh. That value only controls what is kept from the resizing, not whether the resizer is run at all.


~~Incidentally -keep_resize_below_overflow 0 as recommended in https://github.com/The-OpenROAD-Project/OpenROAD/issues/6643#issuecomment-2637837736 does not appear to disable the resizer either:~~

+ global_placement -density 0.5 -timing_driven -routability_driven -pad_right 0 -pad_left 0 -init_wirelength_coef 0.25 -keep_resize_below_overflow 0
[INFO GPL-0005] Execute conjugate gradient initial placement.
[INFO GPL-0002] DBU: 2000
[INFO GPL-0003] SiteSize: (  0.560  3.920 ) um
[INFO GPL-0004] CoreBBox: (  6.720 15.680 ) ( 153.440 160.720 ) um
[INFO GPL-0032] Initializing region: Top-level
[INFO GPL-0006] Number of instances:               430
[INFO GPL-0007] Movable instances:                 287
[INFO GPL-0008] Fixed instances:                   137
[INFO GPL-0009] Dummy instances:                     6
[INFO GPL-0010] Number of nets:                    322
[INFO GPL-0011] Number of pins:                    958
[INFO GPL-0012] Die BBox:  (  0.000  0.000 ) ( 160.600 178.520 ) um
[INFO GPL-0013] Core BBox: (  6.720 15.680 ) ( 153.440 160.720 ) um
[INFO GPL-0016] Core area:                   21280.269 um^2
[INFO GPL-0014] Region name: top-level.
[INFO GPL-0015] Region area:                 21280.269 um^2
[INFO GPL-0017] Fixed instances area:         1088.819 um^2
[INFO GPL-0018] Movable instances area:       8662.259 um^2
[INFO GPL-0019] Utilization:                    42.901 %
[INFO GPL-0020] Standard cells area:          8662.259 um^2
[INFO GPL-0021] Large instances area:            0.000 um^2
[InitialPlace]  Iter: 1 conjugate gradient residual: 0.00000008 HPWL: 28631920
[InitialPlace]  Iter: 2 conjugate gradient residual: 0.00000341 HPWL: 19050441
[InitialPlace]  Iter: 3 conjugate gradient residual: 0.00000024 HPWL: 15292196
[InitialPlace]  Iter: 4 conjugate gradient residual: 0.00000012 HPWL: 14493627
[InitialPlace]  Iter: 5 conjugate gradient residual: 0.00000011 HPWL: 14360746
[INFO GPL-0033] Initializing Nesterov region: Top-level
[INFO GPL-0023] Placement target density:       0.5000
[INFO GPL-0024] Movable insts average area:     30.182 um^2
[INFO GPL-0025] Ideal bin area:                 60.364 um^2
[INFO GPL-0026] Ideal bin count:                   352
[INFO GPL-0027] Total bin area:              21280.269 um^2
[INFO GPL-0028] Bin count (X, Y):          16 ,     16
[INFO GPL-0029] Bin size (W * H):       9.170 *  9.065 um
[INFO GPL-0030] Number of bins:                    256
[INFO GPL-0007] Execute nesterov global placement.
[INFO GPL-0031] HPWL: Half-Perimeter Wirelength
Iteration | Overflow |     HPWL (um) |  HPWL(%) |   Penalty | Group
---------------------------------------------------------------
        0 |   0.8615 |  3.393388e+03 |   +0.00% |  2.59e-15 |      
       10 |   0.8518 |  3.585352e+03 |   +5.66% |  4.21e-15 |      
       20 |   0.8581 |  3.743566e+03 |   +4.41% |  6.86e-15 |      
       30 |   0.8601 |  3.761171e+03 |   +0.47% |  1.12e-14 |      
       40 |   0.8589 |  3.732109e+03 |   -0.77% |  1.82e-14 |      
       50 |   0.8586 |  3.728040e+03 |   -0.11% |  2.96e-14 |      
       60 |   0.8592 |  3.741616e+03 |   +0.36% |  4.83e-14 |      
       70 |   0.8591 |  3.743905e+03 |   +0.06% |  7.86e-14 |      
       80 |   0.8587 |  3.740039e+03 |   -0.10% |  1.28e-13 |      
       90 |   0.8584 |  3.744221e+03 |   +0.11% |  2.09e-13 |      
      100 |   0.8581 |  3.755209e+03 |   +0.29% |  3.40e-13 |      
      110 |   0.8572 |  3.766849e+03 |   +0.31% |  5.54e-13 |      
      120 |   0.8552 |  3.783177e+03 |   +0.43% |  9.02e-13 |      
      130 |   0.8511 |  3.810751e+03 |   +0.73% |  1.47e-12 |      
      140 |   0.8440 |  3.849413e+03 |   +1.01% |  2.39e-12 |      
      150 |   0.8083 |  3.891445e+03 |   +1.09% |  3.90e-12 |      
      160 |   0.7984 |  3.931663e+03 |   +1.03% |  6.35e-12 |      
      170 |   0.8060 |  4.050019e+03 |   +3.01% |  1.03e-11 |      
      180 |   0.7561 |  4.248249e+03 |   +4.89% |  1.68e-11 |      
      190 |   0.7443 |  4.344758e+03 |   +2.27% |  2.74e-11 |      
      200 |   0.7105 |  4.589575e+03 |   +5.63% |  4.47e-11 |      
      210 |   0.6394 |  4.680180e+03 |   +1.97% |  7.28e-11 |      
[INFO GPL-0100] Timing-driven iteration 1/2, virtual: true.
[INFO GPL-0101]    Iter: 212, overflow: 0.633, keep resizer changes at: 0, HPWL: 9367148
Iteration |   Area    | Resized | Buffers | Nets repaired | Remaining
---------------------------------------------------------------------
        0 |     +0.0% |       0 |       0 |             0 |       322
    final |    +33.6% |       0 |      39 |             2 |         0
---------------------------------------------------------------------
[INFO RSZ-0034] Found 1 slew violations.
[INFO RSZ-0035] Found 2 fanout violations.
[INFO RSZ-0038] Inserted 39 buffers in 2 nets.
   Iter   |    Area   | Removed | Inserted |   Pins
          |           | Buffers | Buffers  | Remaining
-------------------------------------------------------
        0 |     +0.0% |       0 |        0 |       289
       28 |     +0.0% |       0 |        0 |       261
       56 |     +0.0% |       0 |        0 |       233
       84 |     +0.0% |       0 |        0 |       205
      112 |     +0.0% |       0 |        0 |       177
      140 |     +0.0% |       0 |        0 |       149
      168 |     +0.0% |       0 |        0 |       121
      196 |     +0.0% |       0 |        0 |        93
      224 |     +0.0% |       0 |        0 |        65
      252 |     +0.0% |       0 |        0 |        37
      280 |     +0.0% |       0 |        0 |         9
[CRITICAL RSZ-2008] buffering pin fanout39/Z: wire step options empty

donn avatar Oct 28 '25 08:10 donn

Hi @povik, is there any news on this issue? Can we provide additional information to help resolve it?

As you can see from the latest reproducibles, this appears to be a more general issue that also occurs with standard cells and not just with I/O cells.

mole99 avatar Nov 09 '25 11:11 mole99

I tried your test case and it is also mispackaged:

[ERROR ORD-0007] /home/leo/Repositories/ws-submission-2025/example_project/runs/latest/26-odb-customioplacement/user_project_example.odb does not exist.

I'm not going to fix again as I did in #8642

maliberty avatar Dec 17 '25 05:12 maliberty

@maliberty I'm really sorry about this. I was under the impression that LibreLane would rewrite the entire reproducible. However, there were some absolute and wrong paths left under _env.tcl. I have fixed them and grepped for any remaining full paths that I may have missed, but it seems I have caught them all now. Please let me know right away if you encounter any further issues.

Here is the updated reproducible: reproducible.zip

./run.sh invokes OpenROAD to reproduce the issue:

...
[INFO RSZ-0038] Inserted 230 buffers in 46 nets.
   Iter   |    Area   | Removed | Inserted |   Pins
          |           | Buffers | Buffers  | Remaining
-------------------------------------------------------
        0 |     +0.0% |       0 |        0 |       468
       46 |     +0.0% |       0 |        0 |       422
       92 |     +0.0% |       0 |        0 |       376
[CRITICAL RSZ-2008] buffering pin _545_/ZN: wire step options empty

mole99 avatar Dec 17 '25 12:12 mole99