布局与电路图教程

在本教程中,我们将实现一个玩具 VLSI 布局验证应用程序。在 VLSI CAD 中,设计的一个重要步骤是签核检查,该检查验证掩模设计人员和自动化工具绘制的物理布局是否实现了设计工程师指定的逻辑电路图。物理布局被建模为用于在制造过程中将布局打印到硅晶片上的多边形层。在花费数百万美元准备光刻掩模并运行测试批次的晶片之前,最好先发现物理设计错误。

真正的布局文件格式是二进制的,通常经过压缩,并表示布局的折叠层次模型,其中一组多边形可以组合成一个单元,并作为一个组实例化到其他单元中。在本教程中,我们假设一个简化的 ASCII 布局文件格式,没有设计层次结构,在 VLSI 行话中我们称之为“扁平”。同样,我们假设一个扁平的 ASCII 逻辑电路图网络列表文件格式。网络是电路设计中命名的电气连接。布局验证教程的目标是解析这两种文件格式,将 Boost.Polygon 提供的几何运算应用于布局数据,以生成表示物理布局中实现内容的逻辑电路图,然后将输入电路图与生成的电路图进行比较,以确定它们是否相同。

首先,让我们定义一些我们在玩具布局验证应用程序设计中需要用到的对象

布局矩形:具有关联层的轴平行矩形
布局引脚:具有关联层和网络(电信号)名称的轴平行矩形
布局数据库:层名称到多边形集的关联容器
连通性数据库:网络名称到布局数据库的关联容器
物理器件:在多个层上具有特定几何排列,具有一个或多个输入网络和一个输出网络
逻辑网络:命名的图节点
逻辑器件:具有器件类型的未命名图节点
逻辑引脚:定义电路输入或输出的特殊网络
电路图数据库:由网络和逻辑器件组成的图

接下来,让我们定义玩具布局与电路图应用程序执行的操作顺序

解析布局:将布局矩形和多边形流式传输到布局数据库,并将布局引脚流式传输到连通性数据库
提取连通性:通过物理接触或重叠关系将布局数据库中的多边形添加到连通性数据库
提取器件:根据布局几何结构中识别的物理器件,使用逻辑器件填充电路图数据库,并从连通性数据库中提取其终端
提取网络列表:从布局派生的电路图数据库中表示的完整图
解析电路图:将逻辑网络、器件和引脚流式传输到电路图数据库
比较电路图:评估提取的电路图数据库是否与输入电路图数据库等效并输出结果

为了测试我们的应用程序,我们将提取单个逻辑门。逻辑门是几个晶体管一起工作以执行特定逻辑功能。逻辑功能包括通常理解的布尔逻辑运算,例如布尔 AND、OR、XOR 和 INVERT。也经常使用 NAND 和 NOR,它们分别是 AND 和 OR 运算后跟 INVERT 运算。NAND 门可以在 CMOS 电路系列中用四个晶体管实现。NAND 门有两个输入和一个输出。每个输入连接到两个晶体管,一个 p 型晶体管和一个 n 型晶体管。“p”代表正,“n”代表负。当 p 型晶体管导通时,它将输出上拉到与电压源相同的电压。当 n 型晶体管导通时,它将输出下拉到与地相同的电压。创建 p 型晶体管的过程首先通过“掺杂”硅衬底来创建 n 型材料。这块 n 型材料区域将在我们的测试数据中称为 NWELL 层。在 NWELL 区域内,通过进一步掺杂硅来创建 p 扩散区域以创建 p 型材料。这块 p 型材料区域将在我们的测试数据中称为 PDIFF。在 PDIFF 区域的中间,生长了多晶硅条,在扩散区域上形成导电线。多晶硅材料区域将在我们的测试数据中称为 POLY。在这些多晶硅线中的某些下方,一层薄薄的氧化硅提供绝缘,但允许多晶硅的电压场与扩散相互作用。这些绝缘的多晶硅线中的每一条都是晶体管的“栅极”。栅极区域将在我们的测试数据中称为 GATE。当栅极处的电压与地电压相同时,p 型晶体管“导通”并且可以将电流从电压源传递到输出。未绝缘的多晶硅线为晶体管创建与输出信号和电源电压的电气连接。n 型晶体管与 p 型晶体管的不同之处在于其扩散是 NWELL 区域外的 n 型材料。当 n 型晶体管栅极处的电压处于电源电压电平时,电流可以从输出传递到地。在多晶硅层上方是一层氧化硅绝缘体,其中挖出了孔,并用金属填充。这些金属填充孔称为通孔,我们将在测试数据中将此层称为 VIA0。在 VIA0 之上是一层金属多边形,它们之间有氧化硅绝缘体。这些金属多边形是导线,我们将在测试数据中将它们称为 METAL1。我们测试数据中的布局引脚将在 METAL1 上。在 NAND 门中,两个 n 型晶体管串联配置,这意味着一个的输出是另一个的输入电压源。只有当 NAND 门的两个 n 型晶体管都“导通”时,输出才会连接到地,表示逻辑“假”。NAND 门中的两个 p 型晶体管并联配置。如果 NAND 门的任一输入为逻辑“假”,则与其连接的 p 型晶体管将“导通”,并且门的输出将为逻辑“真”,因为晶体管会将其连接到电压电源。下图是 NAND 门可能如何布局的示例,并且未针对任何实际工艺技术按比例绘制。扩散材料旨在通过布尔 NOT 运算在栅极材料下方切除,并且仅为了便于绘制而表示为晶体管栅极下方的实心条。

以下是上述 NAND 门布局的输入布局文件,矩形格式为 XL XH YL YH

矩形 0 60 24 48 NWELL
矩形 3 57 32 43 PDIFF
矩形 3 57 5 16 NDIFF
矩形 5 7 0 17 POLY
矩形 5 7 22 45 POLY
矩形 17 19 3 45 POLY
矩形 29 31 31 48 POLY
矩形 41 43 3 45 POLY
矩形 53 55 3 45 POLY
矩形 17 19 4 17 GATE
矩形 17 19 31 44 GATE
矩形 41 43 4 17 GATE
矩形 41 43 31 44 GATE
矩形 5 7 0 2 VIA0
矩形 5 7 23 25 VIA0
矩形 17 19 28 30 VIA0
矩形 29 31 46 48 VIA0
矩形 41 43 18 20 VIA0
矩形 53 55 23 25 VIA0
矩形 0 60 0 2 METAL1
矩形 3 57 28 30 METAL1
矩形 0 60 46 48 METAL1
矩形 3 57 18 20 METAL1
矩形 3 57 23 25 METAL1
引脚 29 31 0 2 METAL1 GND
引脚 29 31 23 25 METAL1 OUTPUT

引脚 29 31 28 30 METAL1 INPUT1

引脚 29 31 46 48 METAL1 VDD
引脚 29 31 18 20 METAL1 INPUT2

以下是上述 NAND 门电路图的逻辑电路图网络列表文件

引脚 OUTPUT
引脚 INPUT1
引脚 INPUT2
引脚 VDD
引脚 GND
器件 PTRANS VDD INPUT1 OUTPUT
器件 PTRANS VDD INPUT2 OUTPUT
器件 NTRANS GND INPUT1 NET1
器件 NTRANS NET1 INPUT2 OUTPUT

人们可以通过查看此电路图并将其与上面绘制的 NAND 门布局进行比较,以在几秒钟内验证绘制的布局是否与电路图内容匹配。如果您现在这样做,您可能会找到 p 型和 n 型晶体管,并跟踪输入和电源到晶体管终端再到输出的连接。由于如今单个芯片上大约有 10 亿个晶体管,因此我们需要比人工检查布局的速度快得多,并且出错更少。使用 Boost.Polygon 提供的多边形集运算和多边形连通性提取,我们将自动识别晶体管并跟踪连通性。根据对 Boost.Polygon 性能的分析,我们可以预期这种方法可以轻松扩展到标准工作站上的百万门级模块,并在系统内存足够的情况下扩展到任意大型设计。让我们从为我们的应用程序实现一些数据结构开始。

结构体 layout_rectangle {
  整型 xl, yl, xh, yh;
  字符串类型 layer;
};

我们的布局矩形简洁且最小,足以存储其数据。它在 layout_rectangle.hpp 中定义。接下来,让我们以类似的方式实现布局引脚。

结构体 layout_pin {
  整型 xl, yl, xh, yh;
  字符串类型 layer;
  字符串类型 net;
};

我们的布局引脚在 layout_pin.hpp 中定义。现在让我们在 layout_database.hpp 中定义一个布局数据库对象并使用我们解析的布局数据填充它。

类型定义 std::map<std::string, boost::polygon::polygon_90_set_data<int> > layout_database;

//将布局矩形数据类型映射到 boost::polygon::rectangle_concept
命名空间 boost { 命名空间 polygon{
  模板 <>
  结构体 rectangle_traits<layout_rectangle> {
    类型定义 int coordinate_type;
    类型定义 interval_data<int> interval_type;
    静态行内 interval_type get(常量 layout_rectangle& rectangle, orientation_2d orient) {
      如果(orient == HORIZONTAL)
        返回 interval_type(rectangle.xl, rectangle.xh);
      返回 interval_type(rectangle.yl, rectangle.yh);
    }
  };

  模板 <>
  结构体 geometry_concept<layout_rectangle> { 类型定义 rectangle_concept type; };
}}

//将布局矩形插入布局数据库
行内 void populate_layout_database(layout_database& layout, std::vector<layout_rectangle>& rects) {
  循环(std::size_t i = 0; i < rects.size(); ++i) {
    layout[rects[i].layer].insert(rects[i]);
  }
}

我们不需要将引脚插入布局数据库,因为它只了解几何形状,而不知道连通性。但是,我们确实需要了解一些关于连通性的知识才能将电路图与布局进行比较,因此我们需要定义连通性数据库和一些逻辑对象。首先,我们在 device.hpp 中定义一个逻辑器件对象。由于我们很懒,这个对象可以兼作引脚和两种类型的晶体管。传统的面向对象设计可能会声明一个带有虚析构函数的基类,并从中派生每个设备。由于我们不是按代码行数付费的,所以让我们保持简单。

结构体 device {
  字符串类型 type;
  std::vector<std::string> terminals;
};

现在让我们在 schematic_database.hpp 中定义一个电路图数据库对象,并使用我们解析的电路图数据填充它。

结构体 schematic_database{
  std::vector<device> devices;
  std::map<std::string, std::set<std::size_t> > nets;
};

// 给定一个设备向量,填充网络名称到设备索引集合的映射
inline void extract_netlist(std::map<std::string, std::set<std::size_t> >& nets,
                            std::vector<device>& devices) {
  for(std::size_t i = 0; i < devices.size(); ++i) {
    for(std::size_t j = 0; j < devices[i].terminals.size(); ++j) {
      // 创建网络名称和设备 ID 之间的关联
      nets[devices[i].terminals[j]].insert(i);
    }
  }
}

我们的原理图数据库只是一个设备向量,这些设备通过其终端按名称与网络关联,以及一个从网络名称到向量中设备索引集合的映射,通过将网络与其设备关联来完成图。 给定设备及其终端网络,我们可以使用 extract_netlist 操作轻松构建从网络到设备的映射。 现在我们准备开始将布局提取到派生的原理图数据库。 但是,首先我们需要构建一个包含几何图形的物理连接数据库,然后才能从布局构建逻辑连接数据库。 我们在 connectivity_database.hpp 中定义了一个简单的连接数据库,作为网络名称到连接到该网络的几何图形布局数据库的映射,并使用布局数据库和引脚数据填充它。

typedef std::map<std::string, layout_database > connectivity_database;

// 将布局引脚数据类型映射到 boost::polygon::rectangle_concept
命名空间 boost { 命名空间 polygon{
  模板 <>
  struct rectangle_traits<layout_pin> {
    类型定义 int coordinate_type;
    类型定义 interval_data<int> interval_type;
    static inline interval_type get(const layout_pin& pin, orientation_2d orient) {
      如果(orient == HORIZONTAL)
        return interval_type(pin.xl, pin.xh);
      return interval_type(pin.yl, pin.yh);
    }
  };

  模板 <>
  struct geometry_concept<layout_pin> { typedef rectangle_concept type; };
}}

// 给定一个 layout_database,我们填充一个连接数据库
inline void populate_connectivity_database(connectivity_database& connectivity,
                        std::vector<layout_pin>& pins, layout_database& layout) {
  using namespace boost::polygon;
  using namespace boost::polygon::operators;
  for(std::size_t i = 0; i < pins.size(); ++i) {
    connectivity[pins[i].net][pins[i].layer].insert(pins[i]);
  }
  int internal_net_suffix = 0;
  // 将 metal1 布局连接到 metal1 上的引脚
  connect_layout_to_layer(connectivity, layout["METAL1"], "METAL1",
                          "METAL1", "__internal_net_", internal_net_suffix);
  // 将 via0 布局连接到 metal1
  connect_layout_to_layer(connectivity, layout["VIA0"], "VIA0",
                          "METAL1", "__internal_net_", internal_net_suffix);
  // 多边形需要从中减去栅极以防止通过晶体管短路
  polygon_set poly_not_gate = layout["POLY"] - layout["GATE"];
  // 将 poly 减去 gate 连接到 via0
  connect_layout_to_layer(connectivity, poly_not_gate, "POLY",
                          "VIA0", "__internal_net_", internal_net_suffix);
  // 我们不希望信号通过晶体管短路,因此我们从扩散区减去栅极区域
  // 从扩散
  polygon_set diff_not_gate = (layout["PDIFF"] + layout["NDIFF"]) - layout["GATE"];
  // 将 diffusion 减去 gate 连接到 poly
  // 请注意,我为组合的 P 和 NDIFF 编造了 DIFF 层名称
  connect_layout_to_layer(connectivity, diff_not_gate, "DIFF", "POLY", "__internal_net_", internal_net_suffix);
                         
  // 将 gate 连接到 poly 以通过 poly 上的 gate 进行连接
  connect_layout_to_layer(connectivity, layout["GATE"], "GATE", "POLY", "__internal_net_", internal_net_suffix);
                         
  // 现在我们已经将布局的连通性追溯到晶体管级别
  // 任何未连接到引脚的多边形都已分配内部网络名称
}

这个填充连接数据库函数是我们应用程序中 Boost.Polygon 的第一个真正用途。 例如,这里我们对布局层进行布尔(多边形集)运算以将 PDIFF 和 NDIFF 层合并在一起,并从结果中切掉 GATE 层。 我们从引脚开始连接布局,并沿着层堆栈向下到晶体管级别。 从层堆栈向上或以任何顺序连接事物同样有效,但这种方式产生的需要在稍后发现它们之间的连接时合并的临时内部网络更少。 上面使用的连接布局到层函数需要在我们填充连接数据库之前实现。

inline void connect_layout_to_layer(connectivity_database& connectivity, polygon_set& layout,
                                    std::string layout_layer, std::string layer,
                                    std::string net_prefix, int& net_suffix) {
  if(layout_layer.empty())
    return;
  boost::polygon::connectivity_extraction_90<int> ce;
  std::vector<std::string> net_ids;
  for(connectivity_database::iterator itr = connectivity.begin(); itr != connectivity.end(); ++itr) {
    net_ids.push_back((*itr).first);
    ce.insert((*itr).second[layer]);
  }
  std::vector<polygon> polygons;
  layout.get_polygons(polygons);
  std::size_t polygon_id_offset = net_ids.size();
  for(std::size_t i = 0; i < polygons.size(); ++i) {
    ce.insert(polygons[i]);
  }
  std::vector<std::set<int> > graph(polygons.size() + net_ids.size(), std::set<int>());
  ce.extract(graph);
  std::vector<int> polygon_color(polygons.size() + net_ids.size(), 0);
  // 为 net_ids 中的每个网络填充连接的组件
  for(std::size_t node_id = 0; node_id < net_ids.size(); ++node_id) {
    populate_connected_component(connectivity, polygons, polygon_color, graph, node_id,
    polygon_id_offset, net_ids[node_id], net_ids,
    net_prefix, layout_layer);
  }
  // 为每个 polygon_color 为零的组件填充 net_prefix + net_suffix++
  for(std::size_t i = 0; i < polygons.size(); ++i) {
  for(std::size_t i = 0; i < polygons.size(); ++i)
    if(polygon_color[i + polygon_id_offset] == 0) {
      std::stringstream ss;
      ss << net_prefix << net_suffix++;
      std::string internal_net;
      ss >> internal_net;
      populate_connected_component(connectivity, polygons, polygon_color, graph,
      i + polygon_id_offset,
      polygon_id_offset, internal_net, net_ids,
    }
  }
}

connect_layout_to_layer 函数使用 Boost.Polygon 的连通性提取功能为输入多边形集和指定层上的连接数据库中的多边形构建连通性图。 然后,它通过图遍历找到与连接数据库中现有网络关联的多边形,并将它们插入到连接数据库中。 最后,未连接到现有网络的多边形将插入到自动生成的内部网络名称的连接数据库中。 将连接组件插入连接数据库的操作由我们接下来实现的连通性图的递归遍历处理。

inline void populate_connected_component
(connectivity_database& connectivity, std::vector<polygon>& polygons,
 std::vector<int> polygon_color, std::vector<std::set<int> >& graph,
 std::size_t node_id, std::size_t polygon_id_offset, std::string& net,
 std::vector<std::string>& net_ids, std::string net_prefix,
 std::string& layout_layer) {
  if(polygon_color[node_id] == 1)
    return;
   return;
  if(node_id < polygon_id_offset && net_ids[node_id] != net) {
    // 合并连接数据库中的网络
    // 如果其中一个网络是内部网络,则将其合并到另一个网络中
    std::string net1 = net_ids[node_id];
    std::string net2 = net;
    if(net.compare(0, net_prefix.length(), net_prefix) == 0) {
      net = net1;
      std::swap(net1, net2);
    } else {
      net_ids[node_id] = net;
    }
    connectivity_database::iterator itr = connectivity.find(net1);
    if(itr != connectivity.end()) {
      for(layout_database::iterator itr2 = (*itr).second.begin();
          itr2 != (*itr).second.end(); ++itr2) {
        connectivity[net2][(*itr2).first].insert((*itr2).second);
      }
      connectivity.erase(itr);
    }
  }
  if(node_id >= polygon_id_offset)
   connectivity[net][layout_layer].insert(polygons[node_id - polygon_id_offset]);
  for(std::set<int>::iterator itr = graph[node_id].begin();
  itr != graph[node_id].end(); ++itr) {
    populate_connected_component(connectivity, polygons, polygon_color, graph,
    *itr, polygon_id_offset, net, net_ids, net_prefix, layout_layer);
  }
}

我们希望将内部生成的网络合并到引脚网络中,这是这个简单过程中最复杂的部分。 现在我们已经从引脚到晶体管提取了连接数据库,我们需要提取晶体管并建立晶体管终端与连接数据库中网络之间的关系。 首先,让我们使用 extract_devices.hpp 中定义的函数提取晶体管。

typedef boost::polygon::connectivity_extraction_90<int> connectivity_extraction;
inline std::vector<std::set<int> >
extract_layer(connectivity_extraction& ce, std::vector<std::string>& net_ids,
              connectivity_database& connectivity, polygon_set& layout,
              std::string layer) {
  for(connectivity_database::iterator itr = connectivity.begin(); itr != connectivity.end(); ++itr) {
    net_ids.push_back((*itr).first);
    ce.insert((*itr).second[layer]);
  }
  std::vector<polygon> polygons;
  layout.get_polygons(polygons);
  for(std::size_t i = 0; i < polygons.size(); ++i) {
    ce.insert(polygons[i]);
  }
  std::vector<std::set<int> > graph(polygons.size() + net_ids.size(), std::set<int>());
  ce.extract(graph);
  return graph;
}

这个提取层算法在输入多边形集中的多边形和连通性数据库给定层中的多边形之间构建连通性图。 它用于在提取特定晶体管类型的函数中形成晶体管与其终端网络之间的关联。

内联 void extract_device_type(std::vector<device>& devices, connectivity_database& connectivity,
                                polygon_set& layout, std::string type) {
  //回想一下,在连接数据库中,P 和 NDIFF 已合并到一个 DIFF 层中
  //查找 DIFF 层上与每个晶体管交互的两个网络
  //然后查找多晶硅层上与每个晶体管交互的网络
  boost::polygon::connectivity_extraction_90<int> cediff;
  std::vector<std::string> net_ids_diff;
  std::vector<std::set<int> > graph_diff =
    extract_layer(cediff, net_ids_diff, connectivity, layout, "DIFF");
  boost::polygon::connectivity_extraction_90<int> cepoly;
  std::vector<std::string> net_ids_poly;
  std::vector<std::set<int> > graph_poly =
    extract_layer(cepoly, net_ids_poly, connectivity, layout, "POLY");
  std::vector<device> tmp_devices(graph_diff.size() - net_ids_poly.size());
  for(std::size_t i = net_ids_poly.size(); i < graph_diff.size(); ++i) {
    tmp_devices[i - net_ids_diff.size()].type = type;
    tmp_devices[i - net_ids_diff.size()].terminals = std::vector<std::string>(3, std::string());
    std::size_t j = 0;
    for(std::set<int>::iterator itr = graph_diff[i].begin();
        itr != graph_diff[i].end(); ++itr, ++j) {
      if(j == 0) {
        tmp_devices[i - net_ids_diff.size()].terminals[0] = net_ids_diff[*itr];
      } else if(j == 1) {
        tmp_devices[i - net_ids_diff.size()].terminals[2] = net_ids_diff[*itr];
      } else {
        //错误,过多的 diff 连接
        tmp_devices[i - net_ids_diff.size()].terminals = std::vector<std::string>(3, std::string());
      }
    }
    j = 0;
    for(std::set<int>::iterator itr = graph_poly[i].begin();
        itr != graph_poly[i].end(); ++itr, ++j) {
      if(j == 0) {
        tmp_devices[i - net_ids_diff.size()].terminals[1] = net_ids_poly[*itr];
      } else {
        //错误,过多的 poly 连接
        tmp_devices[i - net_ids_poly.size()].terminals = std::vector<std::string>(3, std::string());
      }
    }
  }

  devices.insert(devices.end(), tmp_devices.begin(), tmp_devices.end());
}

我们将晶体管附加到器件向量上,并使用从连接数据库中提取的网络名称填充其终端。晶体管的终端通过 POLY 和 DIFF 层连接,其中 DIFF 包含 PDIFF 和 NDIFF。连接到 POLY 层的是晶体管的栅极,而连接到晶体管沟道两侧的 DIFF 的是源极和漏极。我们可以使用它来提取 p 型和 n 型晶体管。

//根据连接和布局数据填充器件向量
内联 void extract_devices(std::vector<device>& devices, connectivity_database& connectivity,
                            layout_database& layout) {
  using namespace boost::polygon::operators;
  //p 型晶体管是与 p 扩散区和 n阱交互的栅极
  polygon_set ptransistors = layout["GATE"];
  ptransistors.interact(layout["PDIFF"]);
  ptransistors.interact(layout["NWELL"]);
  //n 型晶体管是与 n 扩散区交互而不是与 n阱交互的栅极
  polygon_set ntransistors = layout["GATE"];
  ntransistors.interact(layout["NDIFF"]);
  polygon_set not_ntransistors = ntransistors;
  not_ntransistors.interact(layout["NWELL"]);
  ntransistors -= not_ntransistors;
  extract_device_type(devices, connectivity, ptransistors, "PTRANS");
  extract_device_type(devices, connectivity, ntransistors, "NTRANS");
}

当我们排除 NDIFF 上方但不在 NWELL 上方的 GATE 材料以提取 n 型晶体管时,提取器件过程更多地利用了布局数据上的 Boost.Polygon 布尔运算。我们还在多边形集上使用了“interact”操作,该操作是根据连通性提取实现的,并保留了与另一个多边形集中的多边形接触或重叠的多边形集的所有多边形。现在我们有了一个器件向量,我们可以通过调用 extract_netlist 函数来构建原理图数据库。然后,我们可以使用 compare_schematics.hpp 中定义的函数将从文件中读取的提取原理图与原理图进行比较。由于比较两个原理图没有几何方面,因此我们不会在本教程中介绍该过程,而是跳到在 extract.cpp 中定义的所有这些过程的集成,以构建布局到原理图的比较算法。

bool compare_files(std::string layout_file, std::string schematic_file) {
  std::ifstream sin(schematic_file.c_str());
  std::ifstream lin(layout_file.c_str());

  std::vector<layout_rectangle> rects;
  std::vector<layout_pin> pins;
  parse_layout(rects, pins, lin);

  schematic_database reference_schematic;
  parse_schematic_database(reference_schematic, sin);

  layout_database layout;
  populate_layout_database(layout, rects);

  connectivity_database connectivity;
  populate_connectivity_database(connectivity, pins, layout);

  schematic_database schematic;
  std::vector<device>& devices = schematic.devices;
  for(std::size_t i = 0; i < pins.size(); ++i) {
    devices.push_back(device());
    devices.back().type = "PIN";
    devices.back().terminals.push_back(pins[i].net);
  }
  extract_devices(devices, connectivity, layout);
  extract_netlist(schematic.nets, devices);
  return compare_schematics(reference_schematic, schematic);
}

int main(int argc, char **argv) {
  if(argc < 3) {
    std::cout << "用法: " << argv[0] << " <布局文件> <原理图文件>" << std::endl;
    return -1;
  }
  bool result = compare_files(argv[1], argv[2]);
  if(result == false) {
    std::cout << "布局与原理图不匹配。" << std::endl;
    return 1;
  }
  std::cout << "布局与原理图匹配。" << std::endl;
  return 0;
}

我们使用两个原理图和三个布局测试程序。这些包括与非门和或非门的布局和原理图,以及一个不正确的与非门布局。与非门布局和原理图与上面显示的相同。

> lvs
用法: lvs <布局文件> <原理图文件>
> lvs nand.layout nand.schematic
布局与原理图匹配。
> lvs nand_short.layout nand.schematic
布局与原理图不匹配。
> lvs nand.layout nor.schematic
布局与原理图不匹配。
> lvs nor.layout nor.schematic
布局与原理图匹配。
> lvs nor.layout nand.schematic
布局与原理图不匹配。

至此,我们关于如何基于 Boost.Polygon 库功能构建简单的布局到原理图验证应用程序的教程到此结束。此应用程序的实现做出了许多在现实世界中无效的简化假设,并对许多在实际布局验证应用程序中需要可配置的内容进行了硬编码。但是,它确实提供了解决如何使用 Boost.Polygon 解决实际问题,并指出了实际应用程序如何使用 Boost.Polygon 的方向。

版权 版权所有 � 英特尔公司 2008-2010。
许可证 根据 Boost 软件许可证,版本 1.0 分发。(请参阅随附文件 LICENSE_1_0.txt或复制于 https://boost.ac.cn/LICENSE_1_0.txt)