1- # https://gist.github.com/brabect1/7695ead3d79be47576890bbcd61fe426
1+ # mock-cpu: multi-clock async-FIFO bridge macro.
2+ #
3+ # PR #4170 idiom (multi-clock variant): optimization targets use
4+ # set_max_delay -ignore_clock_latency so hold-fixing does not invent
5+ # phantom budgets against the deep clock tree's insertion delay. See
6+ # flow/platforms/asap7/constraints.sdc lines 1-56 for the single-clock
7+ # rationale; this file can't `source` that template because mock-cpu
8+ # has two async clocks.
9+ #
10+ # The IO optimization targets below are deliberately surgical:
11+ # set_max_delay from top-level ports to fifo_in/<pin> and from
12+ # fifo_out/<pin> to top-level ports, rather than -to [all_registers] /
13+ # -from [all_registers]. Functionally equivalent for this topology
14+ # (all IO paths begin/end at the FIFO), but it exercises more flow
15+ # features — SYNTH_KEEP_MODULES hierarchy preservation, hierarchical
16+ # get_pins selection, and io2fifo/fifo2io path grouping. Intentional
17+ # regression coverage; do not "simplify" back to [all_registers].
218#
3- # This fifo is from http://www.sunburst-design.com/papers/CummingsSNUG2002SJ_FIFO1.pdf
19+ # (* keep_hierarchy *) on the fifo1 module (src/fifo/fifo1.v) preserves
20+ # the FIFO instance boundary through Yosys flattening so the fifo_in/<pin>
21+ # and fifo_out/<pin> paths below resolve. An RTL attribute is used rather
22+ # than SYNTH_KEEP_MODULES because the latter matches exact module names
23+ # and hierarchy elaboration specializes fifo1 into $paramod$<hash>\fifo1
24+ # before SYNTH_KEEP_MODULES runs.
25+ #
26+ # FIFO RTL: Cummings SNUG 2002 — gray-coded pointers, 2-FF synchronizers
27+ # (sync_r2w, sync_w2r). Metastability handled by construction.
28+ # https://gist.github.com/brabect1/7695ead3d79be47576890bbcd61fe426
429
530source $::env(SDC_FILE_EXTRA)
631
@@ -10,57 +35,73 @@ set clk_period 333
1035set clk2_period 1000
1136
1237set clk1_name clk
13- create_clock -name $clk1_name -period $clk_period -waveform \
14- [list 0 [expr $clk_period /2]] [get_ports $clk1_name ]
38+ create_clock -name $clk1_name -period $clk_period \
39+ -waveform [list 0 [expr $clk_period /2]] [get_ports $clk1_name ]
1540set_clock_uncertainty 10 [get_clocks $clk1_name ]
1641
1742set clk2_name clk_uncore
18- create_clock -name $clk2_name -period $clk2_period -waveform \
19- [list 0 [expr $clk_period /2]] [get_ports $clk2_name ]
43+ create_clock -name $clk2_name -period $clk2_period \
44+ -waveform [list 0 [expr $clk2_period /2]] [get_ports $clk2_name ]
2045set_clock_uncertainty 10 [get_clocks $clk2_name ]
46+
2147set_clock_groups -group $clk1_name -group $clk2_name -asynchronous -allow_paths
2248
49+ # Async reset distribution.
2350set_false_path -from [get_ports *rst_n]
2451set_false_path -to [get_ports *rst_n]
2552
26- # The mock-cpu is a macro connecting to a slower peripheral bus and possibly DRAM.
27- # Avoid using set_input/output_delay here.
28- # Register-to-register paths are checked at the mock-cpu level or from the mock-cpu
29- # .lib file to an external register.
30- # Timing closure is ensured at the SoC level where the mock-cpu is connected.
31- # Instead, set strict optimization targets for inputs and outputs to ensure
32- # constraints are not too loose.
33- set non_clk_inputs {}
34- set clock_ports [list [get_ports $clk1_name ] [get_ports $clk2_name ]]
35- foreach input [all_inputs] {
36- if { [lsearch -exact $clock_ports $input ] == -1 } {
37- lappend non_clk_inputs $input
38- }
39- }
53+ # Timing firewall: surgical port <-> FIFO boundary optimization targets.
54+ # Internal 1024-stage pipeline is reg2reg, constrained by the clock
55+ # period alone. IO paths end/begin at the FIFO boundary — no further.
56+ set io_target 80
4057
41- set_max_delay 80 -from $non_clk_inputs -to [all_outputs]
42- group_path -name in2out -from $non_clk_inputs -to [all_outputs]
58+ set fifo_in_wdata [get_pins fifo_in/wdata[*]]
59+ set fifo_in_winc [get_pins fifo_in/winc]
60+ set fifo_out_rinc [get_pins fifo_out/rinc]
4361
44- set all_register_outputs [get_pins -of_objects [all_registers] -filter {direction == output}]
45- set_max_delay 80 -from $non_clk_inputs -to [all_registers]
46- set_max_delay 80 -from $all_register_outputs -to [all_outputs]
47- group_path -name in2reg -from $non_clk_inputs -to [all_registers]
62+ # Port -> FIFO. -to on a hierarchical instance input pin is accepted:
63+ # OpenSTA traverses into the instance and finds the leaf endpoint.
64+ set_max_delay -ignore_clock_latency $io_target \
65+ -from [get_ports wdata*] -to $fifo_in_wdata
66+ set_max_delay -ignore_clock_latency $io_target \
67+ -from [get_ports winc] -to $fifo_in_winc
68+ set_max_delay -ignore_clock_latency $io_target \
69+ -from [get_ports rinc] -to $fifo_out_rinc
70+
71+ # FIFO -> Port. The symmetric surgical form -from $fifo_out_<pin>
72+ # hits STA-1554 ("not a valid start point") because a hierarchical
73+ # instance output pin has no implicit launch clock. Use
74+ # [all_registers] instead — OPENROAD_HIERARCHICAL=1 plus the fifo1
75+ # keep_hierarchy makes [all_registers] enumerate leaf flops inside
76+ # fifo_out (fifomem and pointer-sync flops) whose Q pins are valid
77+ # start points, matching the platform template's single-clock form.
78+ # rdata is excluded here; it's false_path'd at the bottom.
79+ set_max_delay -ignore_clock_latency $io_target \
80+ -from [all_registers] -to [get_ports rempty]
81+ set_max_delay -ignore_clock_latency $io_target \
82+ -from [all_registers] -to [get_ports wfull]
83+
84+ group_path -name io2fifo \
85+ -from [all_inputs -no_clocks] \
86+ -to [list $fifo_in_wdata $fifo_in_winc $fifo_out_rinc ]
4887group_path -name reg2out -from [all_registers] -to [all_outputs]
4988group_path -name reg2reg -from [all_registers] -to [all_registers]
5089
51- # # Dual clock fifo timing constraints
52- # Using fastest clock as constaint
90+ # Dual-clock FIFO CDC: bound combinational delay on pointer-sync paths
91+ # (sync_r2w, sync_w2r) to the fastest clock period, ignore clock
92+ # latency (deep tree), and declare hold false — gray-coded pointers
93+ # and 2-FF synchronizers handle metastability by construction.
5394set cdc_max_delay $clk_period
54-
55- # rdata from fifo_out goes directly to I/O-pins so we need special handling of this case
56- # to ignore timing path from wclk -> rdata for this special case
57- # In normal cases fifo output (rdata) will most likely have a FF on I/O output signal
58- set_false_path -from $clk1_name -to [match_pins rdata* output 0]
59-
60- # Set timing constraint between clock domains
6195set_max_delay $cdc_max_delay -from $clk1_name -to $clk2_name -ignore_clock_latency
6296set_max_delay $cdc_max_delay -from $clk2_name -to $clk1_name -ignore_clock_latency
63-
64- # Hold times between clock domain makes no sense, and should just be ignored
6597set_false_path -hold -from $clk1_name -to $clk2_name
6698set_false_path -hold -from $clk2_name -to $clk1_name
99+
100+ # rdata port has no launch FF on the IO side. It's driven
101+ # combinationally by fifo_out.fifomem (mem[raddr]):
102+ # clk-clocked fifomem flops -> rdata (wclk launch path)
103+ # clk_uncore-clocked rbin -> raddr -> mem mux -> rdata (rclk launch)
104+ # Both are "valid when rempty is low" by FIFO protocol, not a
105+ # single-cycle timing. Declare every path to rdata as false — normal
106+ # FIFO deployments would put an FF on rdata in the consumer domain.
107+ set_false_path -to [get_ports rdata*]
0 commit comments