目录
背景
随着集成电路工艺的提升,电路导线尺寸越来越小,但是在电路中存在一类导线的线宽相比普通导线会更宽。这类较宽的导线对于上下层连接的通孔数也有更高的要求,甚至对于与这类宽导线连接的细导线,在细导线上连接上下层的通孔数也有更高的要求,这就是芯片制作过程中要求检查的最小通孔数设计规则。
为了实现对于这一规则的快速检测,可以通过使用并查集预处理的方式,提前标记通孔数量,快速判断这一类 drc。
步骤分析
使用并查集处理的步骤包括:
- 获取电路设计中的通孔数据,并为每个通孔创建独立的并查集标识符;
- 加载至少一条最小通孔数设计规则,在所述设计规则定义有通孔分组条件及最小通孔数量(即规则中要求的不同通孔之间的距离约束和数量约束);
- 针对每一条设计规则,基于所述通孔分组条件对所有通孔进行并查集合并,得到与所述设计规则对应的并查集集合,并记录所述并查集集合内的实际通孔数量;
- 遍历金属层导线,根据当前设计规则下的宽度要求判断待检测导线的类型;
- 根据待检测导线的类型获取与所述待检测导线连接的目标通孔,并查询所述目标通孔针对所述当前设计规则所归属的并查集集合以及该并查集集合内的实际通孔数量;
- 比较所述实际通孔数量与所述当前设计规则要求的最小通孔数量,若所述实际通孔数量小于所述最小通孔数量,则为所述目标通孔创建违规标志。
整个过程中主要步骤就是第3步,基于检测规则,将不同通孔用并查集进行合并和记录集合内通孔数量。在需要检查规则时,可以直接查找数量,不用再做其他操作。
同时也需要注意一些细节,比如如果存在多条规则,每个通孔对于每条规则都应该存在一个并查集 id 用于记录不同规则的合并情况;再比如,其实通孔的并查集 id 也可以被简化掉,直接记录规则和对应的集合内通孔数量;甚至并查集处理后完全可以判断出是否可能违反规则,那么使用这些通孔的导线也不需要对于此规则进行是否需要检测规则的判断。
另外,其实并查集处理完全可以放在读入数据后,作为通孔的基本属性处理,基本把检查过程简化为了最基础的数值比较,对内存和时间的开销都很小。
代码分析
预处理
cpp
UtilzDisjointSet<UInt> dSet;
// 使用并查集初始化
for (citer1.first(cut_list); !citer1.last(cut); citer1++) {
for (citer2.first(cut_list); !citer2.last(rd_cut); citer2++) {
i++;
dSet.makeSet(i);
if (cut->box()->equal(rd_cut->box())) {
cut_idx = i;
}
}
}
并查集合并和统计
cpp
// 合并通孔
i = 0;
for (citer.first(cut_list); !citer.last(rd_cut); citer++) {
i++;
ibox.set(rd_cut->box());
ibox.oversize(cut_spc, cut_spc);
j = 0;
for (citer2.first(cut_list); !citer2.last(rd_cut2); citer2++) {
j++;
if (rd_cut2->box()->equal(rd_cut->box())) { continue; }
if (!ibox.isNonTouch(rd_cut2->box())) { // touch
int fi = dSet.find(i);
int fj = dSet.find(j);
if (fi != fj) dSet.unionFunc(i, j);
}
}
}
// count cuts number
int find_cut_id = 0;
if (cut_list->getObjCount()) {
find_cut_id = dSet.find(cut_idx);
}
cuts_number = 0;
i = 0;
for (citer.first(cut_list); !citer.last(rd_cut); citer++) {
i++;
int fi = dSet.find(i);
if (find_cut_id == fi) cuts_number++;
}
并查集实现
cpp
template <typename T>
class UtilzDisjointSet {
public:
typedef int Id;
void makeSet(const T& item) {
Id id = sets_.getNumElements();
Set s(id);
sets_.append(s);
map_.insert(item, id);
}
Id find(const T& item) {
Id id = map_.getValue(item);
return find_1(id);
}
void unionFunc(const T& a, const T& b) {
union_1(find(a), find(b));
}
private:
void union_1(Id a, Id b);
Id find_1(Id id) {
Set& s = sets_[id];
if (s.parent_ == id) {
return id;
}
s.parent_ = find_1(s.parent_);
return s.parent_;
}
struct Set {
int parent_;
int rank_;
Set(Id id) {
parent_ = id;
rank_ = 0;
}
};
UtilzArray<Set> sets_;
UtilzMap<T, Id> map_;
};
template <typename T>
void UtilzDisjointSet<T>::union_1(Id x, Id y) {
Id xId = find_1(x);
Id yId = find_1(y);
if (xId == yId) {
return; // 已合并
}
Set& xSet = sets_[xId];
Set& ySet = sets_[yId];
if (xSet.rank_ < ySet.rank_) {
xSet.parent_ = yId;
} else if (xSet.rank_ > ySet.rank_) {
ySet.parent_ = xId;
} else { // 秩相等,加一
ySet.parent_ = xId;
xSet.rank_++;
}
}