DFT SDC 介绍

从阳到现在折腾了快一个月,然而还是咳嗽不停,果然只有生病的时候才知道健康是多么的宝贵。

DFT 的sdc mode, 分四种mbist , scan_atspeed (scan_ac) , scan_stuchat(scan_sa), scan_shift 。
每个sdc都是dft的一种模式。

下来我边写边介绍

set tck [get_ports JTAG_TCK]
set tdi [get_ports JTAG_TDI]
set tms [get_ports JTAG_TMS]
set trst [get_ports JTAG_TRST_N]
set tdo [get_ports JTAG_TDO]

set scan_enable [get_ports xxx]
set edt_update [get_ports xxx]
set test_clock [get_ports xxx]

scan_enable edt_update test_clock 是我前面的文章介绍的顶层port进来做 scan_en , edt 更新和 test_clock 输入的复用port (用哪一个port在tessent flow 中可以指定)

下来不同的mode下 需要给设计的一些位置case不同的值

set_case_analysis 1 [get_ports DFT_MODE]

控制DFT_MODE port让整个设计处于dft模式下

if {[sizeof_collection [get_pins -q -h *tessent_persistent_cell_all_test/Z]} {
set_case_analysis 1 [get_pins -h *tessent_persistent_cell_all_test/Z]
}

tessent flow 里 add_dft_signal all_test 添加的逻辑。 logic_test 和 non logic_test (mbist)时候都是1

if {$sdc_view == “dft.mbist”} {
set_case_analysis 0 $scan_enable
set_case_analysis 0 $edt_update
if {[sizeof_collection [get_pins -q -h *tessent_persistent_cell_memory_bypass_en/Z]]} {
set_case_analysis 0 [get_pins -h *tessent_persistent_cell_memory_bypass_en/Z]
}

tessent flow 里 add_dft_signal memory_bypass_en 添加的逻辑。当1时 隔离所有的memory
if {[sizeof_collection [get_pins -q -h *tessent_persistent_cell_ltest_en/Z]]} {
set_case_analysis 0 [get_pins -h *tessent_persistent_cell_ltest_en/Z]
}
tessent flow add_dft_signal ltest_en 添加逻辑。 被用于打开logic test mode

set_case_analysis xx [get_pins -of_objects [get_cells -hierarchical * -filter "ref_name =~ mem] -filter “full_name =~ */RET”]

case 所有mem的pin根据需求case

} elseif {$sdc_view == “dft.scan_shift”} {
set_case_analysis 1 $scan_enable

shift 模式 scan enable case 1
if {[sizeof_collection [get_pins -q -h *tessent_persistent_cell_memory_bypass_en/Z]]} {
set_case_analysis 1 [get_pins -h *tessent_persisent_cell_memory_bypass_en/Z]
}
if {[sizeof_collection [get_pins -q -h *tessent_persistent_cell_ltest_en/Z]]} {
set_case_analysis 1 [get_pins -h *tessent_persistent_cell_ltest_en/Z]
}
} elseif {$sdc_view == “dft.scan_sa”} {
if {[sizeof_collection [get_pins -q -h *tessent_persistent_cell_memory_bypass_en/Z]]} {
set_case_analysis 1 [get_pins -h *tessent_persisent_cell_memory_bypass_en/Z]
}
if {[sizeof_collection [get_pins -q -h *tessent_persistent_cell_ltest_en/Z]]} {
set_case_analysis 1 [get_pins -h *tessent_persistent_cell_ltest_en/Z]
}
包含慢速shift和stuck at 所以不case scan en value
} elseif {$sdc_view == “dft.scan_ac”} {
set_case_analysis 0 $scan_enale
高速capture shfit case 0
if {[sizeof_collection [get_pins -q -h *tessent_persistent_cell_memory_bypass_en/Z]]} {
set_case_analysis 1 [get_pins -h *tessent_persisent_cell_memory_bypass_en/Z]
}
if {[sizeof_collection [get_pins -q -h *tessent_persistent_cell_ltest_en/Z]]} {
set_case_analysis 1 [get_pins -h *tessent_persistent_cell_ltest_en/Z]
}
}

下面需要在各个occ 位置创建generated clock 。哪里有occ可以参照之前插入occ的dft spec 类似这样

tessent 创建的occ inst name 会根据dft spec 里面的Controller id name clock_intercept_node 以及design_id 生成inst

比如上图的生成的occ inst 为
u_clock_generated/u_occ_clk27/${design_id}_tessent_occ_clk27m_inst

另外要注意mini occ mem前的mbist mini occ 需要使用的切换tck & test_clock occ
mini occ的inst名字为
${design_id}_tessent_sib_sti_inst
mini occ 最后一级mux为
${design_id}_tessent_sib_sti_inst/tessent_persistent_cell_ltest_clock_mux/Z
是jtag clock和 shift 慢速的test clock 做一个mux 有时候需要细化sdc 时候需要在这个mux/Z 上做genclk

先把设计做好的occ inst和clock id 做一个数组方便在occ点上创建genclock
比如
set node(clk27__refclk) u_clock_generated/u_occ_clk27/${design_id}_tessent_occ_clk27m_inst

clk27__refclk 其中refclk是master clock name
下一步创建所有clock源头到occ这一部分的clock

创建所有pll 后的clock
set root(refclk) [get_ports xxx]
create_clock -name refclk -period $period $root(refclk) -waveform "0 [expr 0.5 * $period] " -add
创建genclock pll到 occ之间如果有div或者需要创建genclock的地方
set root(xxxx_xxxclk) xxx_target_pin
create_generated_clock xxxx_xxxclk xxx

设置master clk和genclk 的target位置以方便occ clock创建

创建scan需要的test_clock , 以及约束io的virtual clock
if {$sdc_view == “dft.scan_shift” || $sdc_view == “dft.scan_sa” || $sdc_view == “dft.scan_ac”} {
create_clock -name vir_ls_pi_clk $test_period -wave_form “0 0.25*$test_period”
create_clock -name vir_ls_po_clk $test_period -wave_form “0.25*$test_period 0.5*$test_period”
create_clock -name test_clock $test_period -wave_form “0.5*$test_period 0.75*$test_period”
}
test_clock 为啥不是百分之50占空比,这个和做pattern时候的test_clock 设置一致为啥不是百分之50我也不是很清楚

create_clock -name tck -period $jtage_period [get_ports $jtag] -waveform “0.5*$jtage_period 0.75*$jtage_period” -add
create_clock -name vir_tck -period $jtage_period -waveform “0.5*$jtage_period 0.75*$jtage_period” -add

下一步在occ的位置进行generated clock create 。这个要根据高速和低速模式分别出。比如mbist和scan_ac 出一起, shift和 stuck at出一起。

if {$design_stage == “dft” && ($sdc_view == “dft.mbist” || $sdc_view == “dft.scan_ac”)} {
foreach clk [array name node ] {
if {![regexp mini_occ $clk]} {
set clk_node [lindex $node($clk) 0]
regexp {(.)__(.)} $clk aa bb master_clk

  if {$design_stage == "dft" && $sdc_view == "dft.mbist"} {
   set clk_pin [get_pins $clk_node/tessent_persistent_cell_clock_out_mux/Z]

create_generated_clock -name hs_occ_${clk} -master_clock $master_clk
-source [get_object_name $root($master_clk)]
-edges {1 2 3} $clk_pin -add
} elseif {$design_stage == “dft” && $sdc_view == “dft.scan_ac”} {
set clk_pin [get_pins $clk_node/tessent_persistent_cell_clock_out_mux/Z]
create_generated_clock -name hs_occ_${clk} -master_clock $master_clk
-source [get_object_name $root($master_clk)]
-edges {1 2 3} $clk_pin -add
}}}
} elseif {$sdc_view == “dft.scan_shift” || $sdc_view == “dft.scan_sa”} {
foreach clk [array name node] {
set clk_node [lindex $node($clk) 0]
if {[regexp mini_occ $clk]} {
set clk_pin [get_pins $clk_node/tessent_persistent_cell_ltest_clock_mux/Z]
} else {
set clk_pin [get_pins $clk_node/tessent_persistent_cell_clock_out_mux/Z]
}
create_generated_clock -name ls_occ_${clk}
-master_clock test_clock
-source [get_ports $test_clock]
-edges {1 2 3} $clk_pin -add
}}

设置clockgroup隔离

set clk_grp {set_clock_groups -asynchronous -group {ref_clk }
-group {xxx}
-group {xxx} }

所有源头的pll clock 设置异步,比如ref_clk和其它clock用xxx代替 是异步 (设置function clk grp 隔离 )

if {$sdc_view == “dft.scan_shift” || $sdc_view == “dft.scan_sa”} {
set ls_clk_grp "set_clock_groups -name ls_occ_async -asynchronous “
foreach_in_collection clk [get_clocks ls_occ_*] {
set clk_name [get_object_name $clk]
set ls_clk_grp [concat $ls_clk_grp -group $clk_name]
}
eval $ls_clk_grp

隔离过occ的各个clock
set clk_grp [concat $clk_grp -group {“test_clock vir_ls* ls_occ*”}]
eval $clk_grp
把dft 相关clock和 function clock 隔离开

} elseif {$sdc_view == “dft.mbist”} {
foreach_in_collection clk [get_clocks hs_occ_*] {
set clk_name [get_object_name $clk]
set clk_grp [concat $clk_grp -group $clk_name
}
eval $clk_grp

隔离各个过occ的高速clock , 把function clock和dft clock 隔离开
} elseif {$sdc_view == “dft.scan_ac” } {
foreach_in_collection clk [get_clocks “hs_occ_] {
set clk_name [get_object_name $clk]
set clk_grp [concat $clk_grp -group $clk_name ]
}
隔离各个过occ 高速clock
set clk_grp [concat $clk_grp -group {"test_clock vir_ls
”}]
隔离function clock dft clock 。 与mbist 部分不同是test_clock 在 ac sa shift 情况下会有timing path)
eval $clk_grp
}

set io_margin_max 0.3
set io_margin_min 0

设置io 约束
set all_clks [get_ports -q [get_attribute [get_clocks *] sources]]

set_input_delay [expr $io_margin_max * $JTAG_PERIOD] -max -clock vir_tck [get_ports “$tdi $tms $trst”] -clock_fall -add
set_input_delay [expr $io_margin_min * $JTAG_PERIOD] -min -clok vir_tck [get_ports “$tdi $tms $trst”]
-clock_fall -add

设置jtag io约束。 jtag tdi tms trst 是下降沿打数据
JTAG PERIOD 很大 所以input output io margin 比较松

set_output_delay [expr $io_margin_max*$JTAG_PERIOD ] -max -clock vir_tck [get_ports $tdo] -add
set_output_delay [expr $io_margin_min*$JTAG_PERIOD ] -min -clock vir_tck [get_ports $tdo] -add

if {$sdc_view == “dft.mbist” } {
set_input_delay [expr $io_margin_max * $JTAG_PERIOD ] -max -clock vir_tck [all_inputs] -add
set_input_delay [expr $io_margin_min * $JTAG_PERIOD] -min -clock vir_tck [all_inputs ] -add
set_output_delay [expr $io_margin_max * $JTAG_PERIOD ] -max -clock vir_tck [all_outputs] -add
set_output_dela [expr $io_margin_min * $JTAG_PERIOD ] -min -clock vir_tck [all_ouputs ] -add
} elseif {$sdc_view == “dft.scan_shift” || $sdc_view == “dft.scan_sa” || $sdc_view == “dft.scan_ac” } {
set_input_delay [expr 0.2 * $TEST_PERIOD ] -max -clock vir_ls_pi_clk [all_inputs ] -add
set_input_delay [expr $io_margin_min * $TEST_PERIOD] -min -clock vir_ls_pi_clk [all_inputs ] -add

set channel_out [get_ports "xxx xx xx xx "]

在做edt时候哪些edt_channel out buf 后面接的PORT

set_output_delay [expr 0.2 * $TEST_PERIOD] -max -clock vir_ls_po_clk $channel_out -add
set_output_delay [expr $io_margin_min * $TEST_PERIOD] -min -clock vir_ls_po_clk $channel_out -add

set_output_delay [expr $io_margin_max * $TEST_PERIOD] -max -clock vir_ls_po_clk [remove_from_collection [all_outputs] $channel_out] -add
set_output_delay [expr $io_margin_min * $TEST_PERIOD] -min -clock vir_ls_po_clk
[remove from collection [all_outputs ] $channel_out] -add

}

所有mbist jtag 相关的用 jtag clock约束io . 所有 scan 相关的 用 test_clock 来约束io 。 为了简便所有io都上上约束。 只针对使用到的port上比如 edt_channel 连接的input和output port

设置dft exception

set all_mem_cell [get_cells -hierarchical -filter “@is_memory_cell == true”

if {$design_stge == “dft” && $sdc_view == “dft.mbist”} {
set func_reg [filter_collection [all_registers ] “is_memory_cell == false && full_name !~ tessent && full_name !~ *_interface_inst * && is_hierarchical == false”]
set_false_path -from $func_reg -to $all_mem_cell
set_false_path -to $func_reg

关掉mbist下 所有function 相关的path 。 关掉function reg和 mem 交互path。

set_false_path -from $all_mem_cell -to $all_mem_cell

关掉mem2mem path
}

set_disable_timing $2pram_name -from CLKA -to CLKB
set_disable_timing $2pram_name -from CLKB -to CLKA

关掉双口mem的 两个clock check arc 根据设计来。如果你的设计2pram 读写是独立的就可以这么干

if {$sdc_view == “dft.scan_shfit” || $sdc_view == “dft.scan_sa” || $sdc_view == “dft.scan_ac” } {
set_false_path -to $all_mem_cell
set_false_path -from $all_mem_cell

scan 模式下关掉mem的path
}

if {$sdc_view == ”dft.scan_sa"} {
set all_seq_elements [all_registers]
set all_seq_elements_si [get_pins -of $all_seq_elements -filter “full_name =~ */SI” -q]
set_false_path -to $all_seq_elements_si

sa 模式下关闭 shift 相关path

}

if {$sdc_view == “dft.scan_ac”} {
if {[get_pins -h -q *ltest_to_reset_reg/CP ] > 0 } {
set_false_path -from [get_pins -h *ltest_to_reset_reg/CP]

这是mbist使用到的复位寄存器,关掉在scan下
}

set_false_path -from SYS_CLKIN -to SYS_CLKOUT

false feedthrough path

set_clock_uncertainty -setup xxx [all_clocks]
set_clock_uncertainty -hold xxx [all_clocks]

设置drv 约束

set_max_fanout 24 [current_design]
set_input_transition -max 0.4 $in_list
set_input_transition -min 0 $in_list

set_load -max 30 $out_list
set_load -min 10 $out_list

完活, 主要是多熟悉dft逻辑和工作模式以及特殊的cell。 就能很好的完成dft sdc的约束

1 个赞