DEF Definition of the Scan Chains¶
In the Design Exchange Format (DEF), Scan chains are defined in the SCANCHAINS section. This section describes the connectivity and ordering of scan chains within the design, often used by Place and Route (P&R) tools to reorder scan flip-flops (scan reordering) to optimize wire length and routing congestion. The first step in this notebook is to implement Scan Chains using the predefined scan chain insertion routine, as presented in the cell below.
import netlist_carpentry
import netlist_carpentry.routines.dft.scan_chain_insertion as sc_insertion
circuit = netlist_carpentry.read(["../../files/dff_circuit.v", ], top="Top")
sc_insertion.implement_scan_chain(circuit.top)
circuit.write("../../output/dff_module_with_scan_chains.v", overwrite=True)
WARNING: Instance Top.m3 has unconnected port segments!
Extracting the Order of the Scan-FF¶
The actual order in the scan chain can either be retrieved by tracking during the implementation process, or by following the chain afterwards.
In the latter case, the chain can be followed, starting from the top module's "SI" input port and following the loads over each instance's "SO" port, whilst entering submodules on the way.
Since each instance's path only references its parent module directly (i.e. the path of a flip-flop instance Top.m3.m22.some_ff_scan will only be M22.some_ff_scan, as M22 is the module in which this instance exists), the path must be extended between the hierarchies.
In the cell below, this is achieved by providing another list hier_list containing a hierarchical list of instance names to traverse to reach the current instance.
This list is also used to recreate the full hierarchical instance path of the current instance.
Execute the cell below to retrieve the order of the Scan-Flip-Flops in the scan chain.
from typing import List
from netlist_carpentry import Instance, Module
from netlist_carpentry.core.netlist_elements.element_path import InstancePath
def get_scan_path(m: Module, hier_list: List[str] = []) -> List[InstancePath]:
paths = []
inst = m.ports["SI"].loads()[0][0].parent.parent
while inst is not None:
if isinstance(inst, Instance) and inst.is_module_instance:
paths.extend(get_scan_path(inst.module_definition, [*hier_list, inst.name]))
elif isinstance(inst, Instance):
inst_path = ".".join([*hier_list, inst.name])
paths.append(InstancePath(raw=inst_path))
inst_dict = m.get_succeeding_instances(inst.name)
if "SO" not in inst_dict:
break
inst = inst_dict["SO"][0][0]
return paths
get_scan_path(circuit.top, [circuit.top.name])
[InstancePath Top.m1.§auto§ff§cc§266§slice§48_scan, InstancePath Top.m1.§procdff§42_scan, InstancePath Top.m2.§auto§ff§cc§266§slice§48_scan, InstancePath Top.m2.§procdff§42_scan, InstancePath Top.m3.§procdff§41_scan, InstancePath Top.m3.m21.§procdff§40_scan, InstancePath Top.m3.m22.§procdff§39_scan, InstancePath Top.m3.m22.§auto§ff§cc§266§slice§49_0_scan, InstancePath Top.m3.m22.§auto§ff§cc§266§slice§49_1_scan, InstancePath Top.m3.m22.§auto§ff§cc§266§slice§49_2_scan, InstancePath Top.m3.m22.§auto§ff§cc§266§slice§49_3_scan, InstancePath Top.m3.m22.§auto§ff§cc§266§slice§49_4_scan, InstancePath Top.m3.m22.§auto§ff§cc§266§slice§49_5_scan, InstancePath Top.m3.m22.§auto§ff§cc§266§slice§49_6_scan, InstancePath Top.m3.m22.§auto§ff§cc§266§slice§49_7_scan, InstancePath Top.m3.m22.§auto§ff§cc§266§slice§50_0_scan, InstancePath Top.m3.m22.§auto§ff§cc§266§slice§50_1_scan, InstancePath Top.m3.m22.§auto§ff§cc§266§slice§50_2_scan, InstancePath Top.m3.m22.§auto§ff§cc§266§slice§50_3_scan, InstancePath Top.m3.m22.§auto§ff§cc§266§slice§50_4_scan, InstancePath Top.m3.m22.§auto§ff§cc§266§slice§50_5_scan, InstancePath Top.m3.m22.§auto§ff§cc§266§slice§50_6_scan, InstancePath Top.m3.m22.§auto§ff§cc§266§slice§50_7_scan]
Extraction of Scan Chains and Representation in DEF¶
A scan chain definition typically includes:
- Chain Name: A unique identifier for the chain.
- Start/Stop Points: The primary inputs/outputs or specific instance pins where the chain begins and ends.
- Scan Cells (Flip-Flops): The list of registers in the chain. In our case, these are defined as
ORDERED: The order of these flip-flops is fixed and should be preserved as listed (since the order is already).
In the cell below, the DEF syntax for scan chains is presented in template format.
scan_chain_template_full = """SCANCHAINS {chain_nr} ;
{chains}
END SCANCHAINS"""
single_chain_template = """ - {chain_name}
+ START {start_instance_or_PIN} {start_pin} # Chain starts at pin 'start_pin' of instance 'start_instance_or_PIN' if it is an instance, otherwise a top-level pin
+ STOP {stop_instance_or_PIN} {stop_pin} # Chain stops at pin 'stop_pin' of instance 'stop_instance_or_PIN' if it is an instance, otherwise a top-level pin
+ ORDERED
{scanffs}
;"""
single_scanff_template = "{scanff_path} ( IN SI ) ( OUT SO )"
In the given case, there is only one scan chain, so chain_nr is 1.
Accordingly, single_chain_template has to be formatted only once.
By iterating over the ordered list of scan flip-flops (retrieved via get_scan_path), the single_scanff_template can be formatted automatically for each flip-flop, where the IN pin will always be SI and OUT is SO.
Execute the cell below to extract the scan chains in DEF format.
For automation of the procedure, it could also be written directly into a file, but for simplicity, it is printed directly into the cell output here.
scanff_paths = get_scan_path(circuit.top, [circuit.top.name])
scanffs = "\n\t".join(single_scanff_template.format(scanff_path=ipath.raw) for ipath in scanff_paths)
chain_name = "SomeScanChain"
start_inst_or_PIN = "PIN"
start_name = "SI" # Top level scan-in- pin name, which is SI in our case
stop_inst_or_PIN = "PIN"
stop_name = "SO" # Top level scan-out pin name, which is SO in our case
single_chain = single_chain_template.format(
chain_name=chain_name,
start_instance_or_PIN=start_inst_or_PIN,
start_pin=start_name,
stop_instance_or_PIN=stop_inst_or_PIN,
stop_pin=stop_name,
scanffs=scanffs)
full_scan_chain_def = scan_chain_template_full.format(
chain_nr=1,
chains=single_chain)
print(full_scan_chain_def)
SCANCHAINS 1 ;
- SomeScanChain
+ START PIN SI # Chain starts at pin 'start_pin' of instance 'start_instance_or_PIN' if it is an instance, otherwise a top-level pin
+ STOP PIN SO # Chain stops at pin 'stop_pin' of instance 'stop_instance_or_PIN' if it is an instance, otherwise a top-level pin
+ ORDERED
Top.m1.§auto§ff§cc§266§slice§48_scan ( IN SI ) ( OUT SO )
Top.m1.§procdff§42_scan ( IN SI ) ( OUT SO )
Top.m2.§auto§ff§cc§266§slice§48_scan ( IN SI ) ( OUT SO )
Top.m2.§procdff§42_scan ( IN SI ) ( OUT SO )
Top.m3.§procdff§41_scan ( IN SI ) ( OUT SO )
Top.m3.m21.§procdff§40_scan ( IN SI ) ( OUT SO )
Top.m3.m22.§procdff§39_scan ( IN SI ) ( OUT SO )
Top.m3.m22.§auto§ff§cc§266§slice§49_0_scan ( IN SI ) ( OUT SO )
Top.m3.m22.§auto§ff§cc§266§slice§49_1_scan ( IN SI ) ( OUT SO )
Top.m3.m22.§auto§ff§cc§266§slice§49_2_scan ( IN SI ) ( OUT SO )
Top.m3.m22.§auto§ff§cc§266§slice§49_3_scan ( IN SI ) ( OUT SO )
Top.m3.m22.§auto§ff§cc§266§slice§49_4_scan ( IN SI ) ( OUT SO )
Top.m3.m22.§auto§ff§cc§266§slice§49_5_scan ( IN SI ) ( OUT SO )
Top.m3.m22.§auto§ff§cc§266§slice§49_6_scan ( IN SI ) ( OUT SO )
Top.m3.m22.§auto§ff§cc§266§slice§49_7_scan ( IN SI ) ( OUT SO )
Top.m3.m22.§auto§ff§cc§266§slice§50_0_scan ( IN SI ) ( OUT SO )
Top.m3.m22.§auto§ff§cc§266§slice§50_1_scan ( IN SI ) ( OUT SO )
Top.m3.m22.§auto§ff§cc§266§slice§50_2_scan ( IN SI ) ( OUT SO )
Top.m3.m22.§auto§ff§cc§266§slice§50_3_scan ( IN SI ) ( OUT SO )
Top.m3.m22.§auto§ff§cc§266§slice§50_4_scan ( IN SI ) ( OUT SO )
Top.m3.m22.§auto§ff§cc§266§slice§50_5_scan ( IN SI ) ( OUT SO )
Top.m3.m22.§auto§ff§cc§266§slice§50_6_scan ( IN SI ) ( OUT SO )
Top.m3.m22.§auto§ff§cc§266§slice§50_7_scan ( IN SI ) ( OUT SO )
;
END SCANCHAINS