电路引擎






4.93/5 (97投票s)
逻辑电路仿真与分析系统
下载 CircuitEngine 可执行文件及示例 - 222.51 KB
下载 CircuitEngine_Code_Files_VS6.0.zip - 1.72 MB
下载 CESL_CEC_Examples.zip - 62.75 KB
下载 glut-3.7.6-bin.zip - 117.09 KB
下载 glut-3.spec.zip - 247.8 KB
下载 CircuitEngine_Code_Files_VS6.0 -> VS8.0.zip - 50.83 KB
引言
逻辑设计是计算机科学最基础也是最重要的主题之一。因此,几乎所有的计算机科学教育都会开设逻辑设计课程,以帮助学生从最基本的层面深入理解计算机的工作原理,从而能够以计算机的思维方式思考。此外,在实际的逻辑设计实验室中,学生还需要能够构建自己的电路设计。大学花费大量资金为学生提供逻辑设计理论的实践经验。从这个角度来看,本项目可以帮助计算机科学专业的学生获得独特的经验,并在参加实验室课程之前对他们的设计进行预先测试。这也可以间接降低实验室事故的发生率,从而降低实际操作的成本。
学习任何一门学科,即使你理论上掌握了所有细节,也很难在脑海中将其塑形,或者在某些点上你无法获得任何进一步的细节。为了学习,你必须亲身实践。这时,仿真就成了极端尝试的辅助工具。在模拟的计算机环境中,你可以放心地发展自己的想法,而不必担心受到伤害或损坏任何设备。从这个意义上说,本项目也实现了逻辑设计专业的学生能够在3D环境中像在真实的逻辑设计实验室课程一样尝试他们有趣的构思和设计。
此外,你不必花钱来测试你的电路设计的试运行。你可以开发自己的集成电路(IC),或者通过Circuit Engine脚本语言(CESL——一个为本项目设计的脚本语言)重新创建现实生活中已经存在过的原始芯片。我确信CESL将加速新人对逻辑设计的学习和理解时间。
本文档的第二部分将对逻辑电路进行基本解释,概述逻辑门、布尔代数、组合电路以及异步/同步时序电路。第三部分将详细介绍Circuit Engine的内部工作原理、类和数据结构,以及CESL和CEC(Circuit Engine Circuit——一个为本项目设计的文件结构)的文件结构。第四部分将通过图示展示Circuit Engine的一个示例实现。
背景
逻辑电路基本定义
本节提供了一些关于逻辑门、布尔代数、组合逻辑和同步/异步时序逻辑的基本描述。假定您对逻辑电路已有预备知识。因此,本节不具有教育目的,而仅作为参考。
2.1. 逻辑门
逻辑门是电子电路,它们对一个或多个输入信号进行操作以产生一个输出信号。数字系统中的电气信号,如电压或电流,以两种可识别的值存在。电压驱动的电路响应于代表逻辑值 1 或逻辑值 0 的二进制变量的两个不同电压范围。逻辑门的输入端接受落在指定范围内的二进制信号(例如,对于逻辑 1 为 +5 伏特,对于逻辑 0 为 0 伏特)。在 0 到 1 或 1 到 0 的允许范围之间的中间区域称为过渡区,而中间区域称为过渡区域。
如图 2.1 (1) 所示,使用图形符号来表示三种基本门——AND、OR 和 NOT。这些门是电子电路,如果施加了逻辑 1 和逻辑 0 的等效输出信号,它们会根据各自的真值表产生逻辑 1 或逻辑 0 的等效输出信号。当两个输入信号均为逻辑 1 时,AND 门输出逻辑 1 信号。如果任何输入信号为逻辑 1,则 OR 门输出逻辑 1 信号。NOT 门通常被称为逆变器。之所以这样命名,是因为它的响应(输入为逻辑 0 时输出为逻辑 1,反之亦然)很明显。输出逻辑信号是输入逻辑信号 X 的反转版本。
图 2.1 (1) - 基本逻辑门
除了基本门(NOT、AND 和 OR)之外,还有一些基于这些基本门的复合门定义,例如 NAND、NOR、XOR、XNOR。它们的图形符号如图 2.1 (2) 所示。
图 2.1 (2) - 复合逻辑门
2.2. 布尔代数和组合电路
这里的布尔代数是一种处理二进制变量和逻辑运算的代数形式。字母用字母表中的字母表示,三个基本运算是 AND、OR 和 NOT。布尔函数可以等于 1 或 0。考虑布尔函数的一个例子
F = X + Y'Z
表达式的两个部分,X 和 Y'Z,称为函数 F 的项。如果项 X 为 1,或者项 Y'Z 为 1(即 Y' 和 Z 都为 1),则函数 F 等于 1。否则,F 等于 0。补(NOT)运算指示如果 Y' = 1,则 Y 必须等于 0。因此,我们可以说 F = 1 如果 X = 1,或者如果 Y = 0 且 Z = 1。布尔函数表达了二进制变量之间的逻辑关系。通过确定变量所有可能值组合下表达式的二进制值来求值。
布尔函数可以用真值表表示。函数的真值表是所有二进制变量的 1 和 0 的组合列表,以及显示函数对每个二进制组合的值的列表。表 2.2 (1) 显示了函数 F 的真值表。

表 2.2 (1) – 函数 F 的真值表
在真值表中,行的数量是 2n,其中 n 是函数中变量的数量。真值表的二进制组合是 n 位二进制数,对应于从 0 到 2n - 1 的十进制计数。
图 2.2 (1) – F 的逻辑电路图
布尔函数可以从代数表达式转换为由逻辑门组成的电路图。F 的电路图如图 2.2 (1) 所示。输入 Y 上的逆变器生成补码 Y'。AND 门处理 Y' 和 Z,OR 门组合 X 和 Y'Z。在逻辑电路图中,函数的变量被视为电路的输入,二进制变量 F 被视为电路的输出。门通过传输逻辑信号的导线互连。这种类型的逻辑电路称为组合逻辑电路,因为变量通过逻辑运算“组合”而成。这与下一节将要介绍的时序逻辑形成对比。
2.3 时序电路
虽然每个数字系统很可能包含组合电路,但在实际中遇到的系统大多数也包含存储元件。这样的系统称为时序电路。时序电路的框图如图 2.3 (1) 所示。
组合电路和存储元件相互连接以构成时序电路。存储元件是能够存储二进制信息的电路。存储在这些元件中的二进制信息在任何给定时间定义了时序电路在此时的状态。时序电路通过输入从其环境接收二进制信息。这些输入与存储元件的当前状态一起决定了输出的二进制值。它们还决定了用于指定存储元件下一个状态的值。框图表明,时序电路的输出不仅是输入的函数,也是存储元件当前状态的函数。存储元件的下一个状态也是输入和当前状态的函数。因此,时序电路由输入、内部状态和输出的时间序列指定。
时序电路主要有两种类型,它们的分类取决于输入被观察的时间及其内部状态变化的时间。同步时序电路的行为可以根据其在离散时间点的信号来定义。异步时序电路的行为取决于任何时刻的输入以及输入变化的连续时间顺序。
从内存元件的角度来看,有一些方法可以无限期地存储二进制信息。实现存储的最流行的方法之一是通过具有延迟的缓冲元件或带有闭环的非门来构建。然而,尽管这种电路能够存储信息,但信息无法改变。通过用 NOR 或 NAND 门替换逆变器,可以改变信息。由此制造出称为锁存器的异步存储电路。图 2.3 (2) 显示了一个 SR(置位-复位)锁存器的示例。
图 2.3 (2) – 使用 NAND 门的 SR 锁存器
通常,更复杂的异步电路难以设计,因为它们的行为高度依赖于门的传播延迟和输入变化的时序。因此,符合同步模型的电路是大多数设计者的选择。
同步时序电路使用仅在离散时间点影响存储元件的信号。同步是通过一种称为时钟生成器的计时设备实现的,该设备产生周期性的时钟脉冲序列。脉冲以某种方式分布到整个系统中,以便同步存储元件仅在与每个脉冲的某些指定关系下受到影响。实际上,时钟脉冲与其他信号一起应用,这些信号指定了存储元件所需的更改。存储元件的输出仅在时钟脉冲存在时才能改变其值。图 2.3 (3) 显示了一个带有时钟控制输入的 SR 锁存器。
图 2.3 (3) – 带控制输入的 SR 锁存器
使用代码
3. Circuit Engine
本项目将采用面向对象的方法。在此意义上,选择 C/C++ 语言进行编码。本节详细解释了 Circuit Engine 的内部结构和“Circuit Engine 脚本语言”(CESL)的信息。此外,最后一部分还包含“Circuit Engine Circuit”(CEC)文件的内部结构信息。Circuit Engine 的整体概览如图 3 (1) 所示。
图 3 (1) – Circuit Engine
3.1. 基本工作原理
在 Circuit Engine 中,其工作原理非常接近真实电路元件。实际上,逻辑电路的原子元件是门结构,它在电路生命周期内完成一个原子过程。Circuit Engine 的情况也一样,但这次,连线是通过“连接指针”完成的。从这个角度来看,如果我们把任何电路元件想象成一个门列表,那么我们可以像图 3.1 (1) 所示那样表示电路。
图 3.1 (1) – Circuit Engine 的简化电路元件结构
每个电路元件都有一些门,这些门根据其设计完成电路元件的功能。您可以在图 3.1 (1) 中看到 Circuit Engine 电路元件的简化结构。由于门的输入数量可以根据需要而改变,因此门输入数组的大小不是固定的。以 2.2 节中提到的电路为例,以使其结构更清晰。如图 3.1 (2) 所示。
图 3.1 (2) – 函数 F 的逻辑电路
为了清楚地理解这一点,请将图 3.1 (2) 视为芯片的布局。它对应的 Circuit Engine 解释如图 3.1 (3) 所示。为了区分电路的输入(将在 GUI 中表示为芯片的输入引脚),还有额外的*输入门*,它们实际上除了完成芯片与外部环境的连接外,不进行任何计算。带有 X、Y 和 Z 字母的框表示从外部环境(可能是另一个芯片的门的输出)通过输入引脚进入芯片的信息。字母 F 表示相应芯片元件的输出引脚,它将 OR 门的输出位输出到外部环境(可能是另一个芯片的*输入门*的输入)。
图 3.1 (3) – 函数 F 的 Circuit Engine 表示
组合电路元件后,Circuit Engine 的*电路*结构就构成了,如图 3.1 (4) 所示。为了保持结构的清晰,图中省略了门之间以及电路元件之间的连线。注意链接的电路元件是如何构成整个电路的。

图 3.1 (4) – Circuit Engine 的简化电路结构
很明显,在完成门和电路元件之间的连线后,电路结构将转变为一个***图数据结构***。在这个图中,*电路元件*可以是芯片、LED 或电缆。与本节介绍的芯片不同,LED 和电缆是不同的电路元件。它们没有门,并且工作方式与芯片不同。电缆和 LED 的详细说明将在 3.2.5 节中介绍。
需要澄清的另一点是:芯片内的连接(图 3.1 (3) 中的黑色箭头)在 CESL 文件编译过程中完成。而*电路元件*之间的连接是在 Circuit Engine GUI 中由用户交互完成的。Circuit Engine 脚本语言的详细解释可以在 3.3 节中找到。此外,还提供了将 Circuit Engine 会话保存到 CEC 文件中,其结构在 3.4 节中进行了解释。
3.2. 类、数据结构及其关系
Circuit Engine 的运转离不开七个类。本节将解释它们及其关系。
3.2.1. BINARY
Binary 类处理基本的二进制操作,例如电路元件的状态数组、面包板的物理占用图以及编译过程中的引脚关联控制。
基本上,它是一个字节数组,其大小为指定位数(每八位一个字节)。例如,如果创建一个大小为八位的位数组,则只需要一个字节的内存来分配必要的空间。从这个角度来看,它比普通的布尔变量节省了八倍的内存空间,而普通的布尔变量每个都需要一个字节的内存。
Binary 类的缺点是在处理字节的单个位时消耗的时间。因此,它不用于程序中的时间关键进程。
3.2.2. GATE
Gate 类处理门的基本操作,例如计算输入位、设置规格(输入数量、门类型、关联引脚编号等)。Gate 类的设计如表 3.2.2 (1) 所示。
表 3.2.2 (1) – Gate 类
Gate 属性
Input: 根据用户的请求,一个门可以有多个输入。Gate 的**Input 数组是动态分配的,它允许门在 Circuit Engine 遍历门列表时指向其他门以获取它们的输出。一个有 n 个输入的门必须有一个 Input 数组,其中所有元素都不是 NULL(即,门必须连接到数组大小定义的其他门的数量)。如果数组中存在指向 NULL 的门指针,则 Gate 类会报错,抱怨门的输入不完整。否则,如果一切正常,当 Circuit Engine 请求时,门会根据其类型计算其输出,并用计算出的逻辑结果填充其输出字段。
type: 它是门的编码,表示其类型。type 属性可以是任何枚举类型;gtEmpty、gtInput、gtAND、gtNAND、gtOR、gtNOR、gtXOR、gtNOT、gtXNOR、gtVoltage 和 gtGround。它用于在 Circuit Element 遍历时确定和计算门输出。初始类型为 gtEmpty,表示不执行任何操作,但它是检查门就绪性的标准之一。gtInput 使门像一个导体一样行为,直接将输入反映到其输出。输入类型的门可以只有一个元素的输入数组。如果门类型为 gtVoltage 或 gtGround,则该门不能有输入数组,它总是表现为一个源,从其输出提供逻辑 1(gtVoltage)或逻辑 0(gtGround)。Circuit Engine 将这种类型的门用作 Circuit Element,仅具有 gtVoltage 和 gtGround 类型的门,以便为当前正在工作的整个电路提供电压和接地源。这个 Circuit Element 的名称是 Source Level。除了 gtEmpty、gtVoltage 和 gtGround 之外的其他门类型,都根据其类型的名称来行事。
output: 计算出的门结果存储在此属性中。这样,任何其他请求当前门输出的门都可以获取该门在最后一个 Circuit Engine 请求序列中计算出的输出。请求过程是在请求另一个门进行计算时完成的。
leg_no: 门的关联引脚编号使 Circuit Engine 能够将该门识别为 CE 外部的输出门。输出门(leg_no 非 0)在 CEngine 可视化环境中被视为 Circuit Element 的引脚,用户可以在 CEngine GUI 中操作它们的连接。gtInput 类型的门必须分配一个引脚编号,就像 CE 的电压和接地引脚一样,如 CESL 文档中所述。
nof_inputs: nof_inputs 指定了门的输入数量。
ready: 如果门属性已成功由类的 SetProperties() 方法初始化,则分配为 TRUE,否则,该门不能使用,任何对该门的请求都会导致错误。
next_gate: 它持有同一门列表中的下一个门的地址。当 Circuit Element 被请求进行计算时,它利用 next_gate 指针在其门列表中的成员门之间进行遍历和请求。
on: 此属性用于指定门是开启还是关闭。如果 on 为 false,门在其输出上总是给出逻辑 0,否则它执行其工作。这被认为是模拟一个损坏的门。
Gate 方法
SetInputArray(): 根据给定的大小设置门的 Input 数组。它将门更改为 n 输入门,其中 n 是给定参数。
Connect(): 必须通知门有关要连接的源,以便能够从源获取输入位。Connect 函数处理 Input 数组,并使用给定的输入索引和源门地址进行设置。设置 Input 数组后,门就可以进行计算并输出结果。
SetType(): 在首次创建时,每个门都具有 gtEmpty 类型,这意味着它是一个 NULL 门。为了操作,门必须具有指定的类型。该函数使用给定的门类型枚举来设置门的类型。
SetLeg(): 最初,门引脚编号设置为 leg-0,这意味着门的输出不会作为芯片引脚显示在 GUI 中。它处理从门设置引脚编号,该引脚编号允许门将其输出提供给外部环境(可能是其他芯片或 LED 或电缆)。此引脚信息是从 CESL 文件中通过正在设置的门的*可选 output 关键字获取的。
SetProperties(): 此函数使用 SetInputArray()、SetLeg() 和 SetType() 方法。换句话说,它使门只需一行调用即可准备就绪。它获取门的属性(类型、输入数量、输出引脚编号),并用这些信息设置门,然后设置门的 ready 位。
Calculate(): 调用 Calculate 时,它会利用输入数组并获取连接门的输出位。然后,它根据调用它的门的类型计算输出位。
3.2.3. BREAD-BOARD NODE
BREAD-BOARD NODE (BBNode) 是 BREAD-BOARD 类(下一节介绍)的基本数据结构。它保存了插入到面包板相应孔的芯片引脚信息。这些门只能是芯片的输入门或输出门(即,具有非零输出引脚定义的门)。表 3.2.3 (1) 显示了它的属性和方法。
表 3.2.3 (1) – BBNode 类
BBNode 属性
gate_info: 当一个 CE 插入到 BreadBoard 节点时,gate_info 指向与 CE 相应引脚关联的相应门。gate_info 不可能指向 NULL,因为 BBNode 类仅在 CE 引脚插入 BreadBoard 时创建(即,当需要记住连接时),并在 Circuit Element 的 ceqHover(Circuit Element Query Hover——见 3.2.5 节)状态为 TRUE 时销毁。BBNode 本可以设计成始终为每个节点提供 BBNode 类。但是,在这种情况下,内存消耗会很大(即,一个 BBNode 是 12 字节,如果我们有一个面包板,有 60 行、5 列,每个节点有 6 个孔,则会有 60*5*6 = 1800 个孔 * (12 字节) = 21600 字节 ≈ 21KB 内存空间仅用于 BB 数据结构,不包括 Circuit Elements)。
node_map_index: 它显示了 BBNode 对象与面包板上节点内的物理 BB 孔索引的关联。换句话说,它保存了 Circuit Element 引脚在相应面包板节点内的物理孔索引位置的信息。它允许在 Circuit Element 被拔出时移除其物理注册。
next_hole: 它指向同一节点中的下一个孔。
BBNode 方法
SetGateInfo(): 当 Circuit Element 插入到面包板上时,它会保存插入到相应孔的引脚门的地址。保存操作后,BBNode 元素会被插入到 BreadBoard 数据结构(见 3.2.4 节)的相应节点列表中。
SetNextHole(): 它有助于单个面包板节点列表(由 BBNode 元素组成)的链表操作。它接收新创建的 BBNode 元素,并将传入的 BBNode 设置为相应 BBNode 元素的下一个孔。
GetGateInfo(): 它允许访问先前插入到相应面包板节点的所有门信息。当另一个 Circuit Element 引脚进入同一节点时,相应的门会接触。这样,Circuit Elements 之间的连接就完成了。反向过程(拔出 Circuit Element)也借助此方法完成。
GetNodeMapIndex(): 当 Circuit Elements 被要求从面包板上拔出时。还需要重置物理占用位(见 3.2.4 节),以便以后能够插入到该节点。
3.2.4 BREAD-BOARD
BreadBoard 类通过 GUI 根据用户命令提供 Circuit Elements 之间的连接。换句话说,它是连接数据(BBNode's)的一个动态三维数组,有助于 Circuit Engine 记住 Circuit Elements 之间的连接。该类的结构如图 3.2.4 (1) 所示。
图 3.2.4 (1) – BreadBoard 类的通用数据结构
该数据结构基于面包板,并且与实际实验室中使用的面包板非常相似,因此您可以参考它们来更清晰地理解结构。
面包板的尺寸按 CESL 文件中 BREADBOARD 关键字定义的设置。从图 3.2.4 (1) 中可以看出,面包板由 BBNode(Bread-Board Node)数据结构组成。前两个维度(行数和列数)意味着面包板中的每个电气节点就像真实面包板一样。第三个维度(节点中的孔数)意味着面包板节点内的各个孔。由于面包板尺寸可能很大,因此当用户在面包板上进行插入或拔出 Circuit Elements 的操作时,第三个维度是动态创建的。这种方法避免了为未使用的孔消耗内存,因为没有必要保存面包板空孔的连接数据。Bread-Board 类的属性和功能列在表 3.2.4 (1) 中。

表 3.2.4 (1) – BreadBoard 类
BreadBoard 属性
NodeHead: *NodeHead* 持有 BBNode 列表的二维数组。最初,所有列表指针都为 NULL(即,BreadBoard 上没有任何 CE 插入)。任何 Circuit Element 的插入事件都会在此数据结构中被记忆,以便在需要时,Circuit Engine 能够记住 Circuit Elements 之间的连接。任何 *BBNode* 列表的最大长度可以是一个 Electrical Node 上允许的 Circuit Element 数量(物理上,您可以在一个 Electrical Node 中连接任意数量的不同 Circuit Elements,只要孔的数量允许)。
当一个 CE 移动到 BreadBoard 上的一个孔时(在该时刻,Circuit Element 的*ceqHover*状态为*TRUE*),BreadBoard 会被询问“将 CE 插入该孔是否正确?”,然后 BreadBoard 回答“是”或“否”。此事件会影响 Circuit Element 的*LegPlugAbility*数组,作为回答。因此,LegPlugAbility 数组的更改由 Artist 实现,以告知用户 Circuit Element 的插入能力。
CurrentHole: 当有必要对*BBNode*数据结构进行一些修改时,使用*CurrentHole*。它还通过*NewCurrentHole*()、*SetCurrentHole*() 和*AddCurrentHole*() 方法帮助插入新的*BBNode*。
NodeMapHead: 它**映射 BreadBoard 上每个节点的每个孔。当一个 Circuit Element 移动到一个孔上时,会从*node map*中检查该 Circuit Element 定位的孔是否已被另一个 CE 引脚占用。如果此检查结果通过,则 Circuit Element 上该孔的相应引脚处于物理可插入状态。使用*Binary*类映射节点比遍历*BBNode 列表*来检查此物理约束更有效。
nof_rows: BreadBoard 的行数。
nof_cols: BreadBoard 的列数。
nof_holes_in_a_node: 这是 BreadBoard 的一个电气节点中的孔数。每个电气节点由*nof_rows*和*nof_cols*属性定位,而 BreadBoard 上的每个孔由*nof_rows*、*nof_cols*和*nof_holes_in_a_node*定位。在*NodeHead*中有*nof_rows*乘以*nof_cols*个单独的连接链表(*BBNode 列表*),每个单独节点的最大长度为*nof_holes_in_a_node*,而每个相应的*NodeMapHead*元素具有*nof_holes_in_a_node*位的图。
BreadBoard 方法
NewCurrentHole(): 创建一个 BBNode 对象。
SetCurrentHole(): 通过给定门对象的地址设置最后一个创建的 BBNode 对象。
AddCurrentHole(): 将最后一个创建的 BBNode 对象插入到由相应节点的行和列号指定的 BBNode 列表中。
RemoveHole(): 删除包含给定门信息的 BBNode。如果被删除的 BBNode 包含为该节点提供资源的门的信息,并且有其他门指向该源门,那么源门与指向该源门的输入门之间的连接也会被移除。
SendCableNodeID(): 最初,每个电缆都有一个独立的节点 ID,用于识别面包板上组合的电气节点。当电缆通过面包板节点相互连接时,第一个放置的电缆的 ID 会扩展到接触的电缆。此事件避免用户进行圆圈连接电缆,这是不合理的连接。可以将其视为三个电缆的连接,呈三角形。很明显,构成三角形的一个边的电缆是不必要的,即电流已经扩散到三角形的所有节点,而不需要第三条边电缆。
CalculateCable(): 在电缆引脚插入的情况下,如果相应的 BBNode 列表中有门信息,并且给定电缆的另一引脚插入到另一个面包板节点,则输出门信息会作为 BBNode 插入到连接路径上的 BBNode 列表中,以获得电流行为。
SetLegPlugAbility(): 在逻辑电路中,将两个或多个输出连接到一个节点是不合理的。从这个意义上说,此方法设置了给定 Circuit Element 的插入能力数组,以防止用户执行这种不合理的插入。此方法在 Circuit Element 的每次移动(1 孔)时应用,这样用户就可以通过 GUI 中 BreadBoard 上相应 Circuit Element 下方出现的红色孔标记来获得信息。为了完成此过程,在 Circuit Element 移动后,会搜索 Circuit Element 下的相应 BBNode 列表,以比较两个输出门是否都与相应引脚相关联。在这种情况下,相应引脚的插入能力会被改变为 false。此位也通知 Artist 完成 GUI 的必要响应,即 Circuit Element 相应引脚下方的红色孔标记。Artist 不允许插入任何具有 LegPlugAbility 数组中 false 值的红色孔标记的 Circuit Element 到面包板上。
PlugCE(): 它帮助选定的 Circuit Element 插入到面包板上,这样,电路元件之间的连接就完成了。插入场景是这样的:当插入请求到达 BreadBoard 时,它会将由相应门信息设置的 BBNodes 插入到相应的 BreadBoard 节点列表中。然后,在任何输入门和输出门相遇的情况下,会进行必要的更改以将输入门指向输出门。这样,Circuit Elements 之间的连接就完成了。
UnplugCE(): 当请求拔出 Circuit Element 时,会从相应的 BBNode 列表中移除其相应的 BBNodes,并且输入门和输出门之间的连接会被清除。这样,选定的 Circuit Element 与面包板的隔离就完成了。
3.2.5. CIRCUIT ELEMENT
Circuit Element (CE) 类处理 Circuit Elements 上的操作。它有一些职责,例如管理门列表、定位 CE 或保存 CE 的信息。类的概述如表 3.2.5 (1) 所示。只有需要解释的方法才显示出来,代码中的其他方法行为与其名称一致。
表 3.2.5 (1) – CircuitElement 类
CircuitElement 属性
gate: 它是 CE 的门列表的头部。
next_ce & prev_ce: 分别保存电路列表中下一个和上一个 CE 的地址。
id: 它标识 CE 在 CE 列表中的位置,并在 GUI 的左下角显示 CE 的名称,以便与其他 Circuit Elements 区分。
nof_gates: 这是 CE 在其门列表中处理的门数量,在成功添加门请求时会增加。
type: 它指定 CE 的类型。它可以是以下任何枚举:cetCHIP、cetLED、cetCABLE(*cet*代表 CE 类型)。CE 根据其类型进行处理和绘制。
ce_id_counter: ID 计数器用于在创建期间为每个芯片分配 ID 号。因此,芯片根据其创建顺序(即,它们在脚本中的声明顺序)获得标识符。
position_x: 这是 CE 的左上角引脚的 x 坐标,其缺口向上。CE 通过此属性放置在 BreadBoard 上。任何 CE 的 x 坐标最小可以为 1,最大可以为“BreadBoard 上一行中的孔数”减去“CE 覆盖的孔数”。此属性也是 CEngine 的一个物理规则,它不允许 CE 在 x 轴上移出这些最小和最大范围。因此,CE 不能位于 BreadBoard 外侧。
position_y: 这与 position_x 属性相同,但适用于 BreadBoard 的 y 轴约束。芯片的 y 坐标最小可以为 1,最大可以为“BreadBoard 的行数”减去“CHIP 的引脚数 / 2”。
name: 它用于告知用户 CE。换句话说,当用户选择一个 CE 时,CE 的名称会显示在 GUI 的左下角,以便以这种方式告知用户:“这是你在脚本文件中通过‘name’声明的芯片”。此名称取自脚本文件中的 CHIP 块头。对于 LED 和电缆类型,Circuit Engine 会自动赋予默认名称‘LED’和‘CABLE’。
nof_inputs: 芯片 CE 的输入数量从脚本文件中的头信息中读取。在编译完芯片块后,将比较头信息中指定的输入数量和 CHIP 块中实际声明的输入数量,以确保芯片的一致性。换句话说,它阻止用户在脚本中做出任何错误的输入声明。如果比较结果不一致,编译器会通知用户第一个矛盾项出现在脚本中的行号。此错误会写入日志文件。
nof_outputs: 用于与 nof_inputs 属性类似的目的。用于检查“CHIP 块头中的 nof_outputs”与“与引脚编号关联的门数量”(即,被指定为 CHIP 输出门的门的数量)之间的一致性。同样,在不一致的情况下,Circuit Engine 解析器会停止并警告用户,指出第一个矛盾项出现的行号。此错误会写入日志文件。
Gate V, G: 这两个门 V(电压)和 G(接地)用于指定 CE 的电压和接地源。实际上,V 和 G 门的类型不是 gtVoltage 和 gtGround。它们被解释为门类型 gtInput。引脚关联与其他门相同,并且以这种方式,门列表中的任何其他门都不能具有与 V 和 G 的引脚关联相同的引脚关联。
CEInfo: 属性 CEInfo 是一个二进制类对象,它保存有关 CE 状态的信息。位通过*CEQuery*枚举访问:*ceqHover*、*ceqOn*、*ceqHover1*、*ceqHover2*、*ceqHasOd1*、*ceqHasOd2*。以下行解释了它们各自的目的。*ceqHover:* 它指定了 CE 在 BreadBoard 上的 z 轴位置(它在空中还是在 BreadBoard 上)。如果 CE 不处于*hover*模式,则无法将其移动到 BreadBoard 上的其他位置。换句话说,它帮助 Circuit Engine 进行 CE 移动的物理约束。*ceqOn:* 指定 CE 是否处于活动状态。如果 CE 未处于活动模式,则 CE 不工作,所有输出都给出逻辑 0。*ceqHover1:* 此位仅专用于类型为电缆的 CE。它指示电缆元件的第一个引脚是否处于悬停模式。*ceqHover2:* 与 ceqHover1 相同,但用于电缆的第二个引脚。*ceqHasOd1:* 适用于电缆连接算法。它表示,如果第一个引脚的节点列表中有来自电缆第二个引脚的输出门复制的 BBNode 信息(用于在第二个引脚节点中传播输出),则为 true,否则为 false。*ceqHasOd2:* 执行与 ceqHasOd1 相同的工作,但这次用于从第一个引脚节点传播到电缆的第二个引脚节点。
LegPlugAbility: 在 CE 的每次移动(1 孔)中,BreadBoard 会检查 CE 的插入能力,Artist 会通过“孔标记”告知用户。如果 CE 无法在当前位置插入 BreadBoard,BreadBoard 会将面包板上的相应孔位标记为 false(红色);否则,相应 CE 引脚下的孔位标记为 true(绿色)。CE 插入操作的正确性决策是这样做的;例如,如果一个 CE 引脚已插入并与 BreadBoard 节点上的一个输出门相关联,并且当一个与另一个输出门关联的 CE 引脚经过同一节点时,就会在该节点发生不合理的逻辑连接,并且有多个输出,此时 BreadBoard 会决定使 CE 无法插入 BreadBoard。因此,Artist 不允许插入 CE,方法是检查 LegPlugAbility(如 3.2.4 节所述)。
Circuit Element 方法
AddGate (): 创建一个具有给定门属性的门,并将其插入到 CE 的门列表中。
CalculateCE (): 执行 CE 的计算。它遍历门列表并请求门完成它们的计算。
SetType (): 将给定的类型设置为 CE 的类型。给定类型可以是任何枚举类型*cetCHIP*、*cetLED*或*cetCABLE*。
SetNext (): 将给定的 CE 地址设置为 next_ce 地址。
SePrev (): 将给定的 CE 地址设置为 prev_ce。
SetName (): 将给定的字符串设置为 CE 的名称。
SetNofInputs (): 将给定的整数值设置为 CE 的输入数量。
SetNofOutputs (): 将给定的整数值设置为 CE 的输出数量。
SetVoltageLegNo (): 将给定的引脚编号设置为 V 门的引脚编号。
SetGroundLegNo (): 将给定的引脚编号设置为 G 门的引脚编号。
GetGateAddrOfLeg (): 由 BreadBoard 的 PlugCE() 方法使用,用于检索连接到给定引脚编号的门的地址。
GetPositionX2 (): nof_inputs 属性也用于电缆专用目的。当 Circuit Element 被加载为 cetCABLE 时,nof_inputs 参数被视为电缆第二个引脚的 x 轴位置。从这个意义上说,GetPositionX2() 方法返回 Circuit Element 在 x 轴上的位置,并由 Artist 使用,以便将电缆的第一条引脚绘制在正确的位置。
GetPositionY2 (): 与 GetPositionY2() 相同,但用于电缆第二个引脚的 y 轴。
SetStateInfo (): 将 CEInfo 数组中的给定状态修改为 true。
ResetStateInfo (): 将 CEInfo 数组中的给定状态修改为 false。
GetStatInfo (): 返回给定的状态信息位。
MoveUp (): 将 CE 的 y 坐标增加一。
MoveDown (): 将 CE 的 y 坐标减一。
MoveLeft (): 将 CE 的 x 坐标减一。
MoveRight (): 将 CE 的 x 坐标增加一。
3.2.6. CIRCUIT ENGINE CIRCUIT
Circuit Engine Circuit (CECircuit) 类是 Circuit Engine 的核心。它包含了所有其他类,并且是 Artist 下面的一层,Artist 直接与用户交互。CECircuit 的概览如表 3.2.6 (1) 所示。
表 3.2.6 (1) – Circuit Engine Circuit 类
CECircuit 属性
circuit: 它保存 CE 的链表。CECircuit 通过此指针遍历和管理 CE。
theBreadBoard: 它是 BreadBoard 对象(在 3.2.4 节中介绍)。它管理当前 Circuit Engine 会话的面包板。
nof_elements: 这是电路列表中 CE 的数量。
CurrentCE: 当一个 CE 添加到电路中时,CurrentCE 会被动态创建,并根据 CEngine 的脚本规范填充必要的信息。然后,新来的 CE 被添加到*circuit*列表中。NewCurrentCE()、AddGate()、Connect() 和 AddCurrentCE() 类都会使用 CurrentCE。电路元件的插入过程是这样的:当需要一个新的 CE 时,它的数据结构由 NewCurrentCE() 创建,然后通过 AddGate() 和 Connect() 方法进行门插入和连接规范。AddGate() 和 Connect() 影响当前添加到 CurrentCE 的门。完成 CurrentCE 后,此过程以 AddCurrentCE() 结束,CurrentCE 被插入到电路列表中。如果 NewCurrentCE()-AddCurrentCE() 块中断,将会发生一个错误,指示中断的 CE 插入块。
CurrentGate: 它是脚本中新门的临时存储区域,在设置过程中使用。Connect() 方法准备其连接,并由 AddGate() 方法将其插入到 CurrentCE 的门列表中。
ScriptFile: 脚本文件是 ParseScriptFile() 方法的来源。
creation_counter: 用于查找当前门请求连接的门。从这个意义上说,Connect() 方法使用它。
connection_counter: 这是一个计数器,用于完成当前门的 Input 数组以进行连接。它从零开始,在当前门的每次连接后递增一,最终达到当前门 Input 数组的大小。
CECircuit 方法
ParseScriptFile (): 根据 CESL 规范解析给定的脚本文件。在 ParseScriptFile() 编译脚本文件时,它会将编译输出写入日志文件。如果发现 CESL 文件中的任何语法错误,它将停止 Circuit Engine。
SaveCircuit () : 将当前的 CECircuit 信息保存到具有相同名称但扩展名为 CEC(Circuit Engine Circuit)的文件中,以便以后加载当前的 Circuit Engine 会话。
LoadCircuit (): 将给定的 CEC 文件加载到会话中,并允许继续之前保存的会话。
NewCurrentCE (): 在编译 CESL 文件或读取 CEC 文件时,需要创建一个新的 Circuit Element 结构,以便用从文件中检索的信息进行设置,并将其插入到 CECircuit 会话中。
AddGate (): 它将一个门插入到 CurrentCE 中,并带有给定的门规范。这些规范也是从 CESL 或 CEC 文件中检索的。
Connect (): 完成 Circuit Element 内部门结构之间的连接操作。
AddCurrentCE (): 将 CurrentCE 插入到 CECircuit 的电路列表中。
RemoveCE (): 电缆和 LED 的创建不通过 CESL 文件提供(即,脚本文件中只有芯片声明)。电缆和 LED 是在 Circuit Engine 的 GUI 中通过用户请求动态创建的。同样,通过 RemoveCE() 方法可以从环境中移除插入的电缆和 LED。
3.2.7. ARTIST
Artist 是完成 GUI 过程的结构,也是用户直接与之交互的层。它利用OpenGL(***Open Graphics Library***)和GLUT(***Graphics Library Utility Toolkit* - for Win32**)来在 GUI 中绘制电路。本项目中没有 Win32 编程。窗口管理由 GLUT 组件处理。为了完成 GLUT 接口,需要有一系列回调函数注册到 GLUT。GLUT 的回调注册必须由纯 C 声明的函数完成,因此 Artist 没有类类型。相反,它由一组回调函数组成,这些函数是 CECircuit 类的友元。因此,CECircuit 类和 Artist 可以被视为一个联合结构,以便它们可以像单个类一样进行交互。换句话说,Artist 可以被解释为用户工具,以便用户可以处理或管理 CECircuit 类,并在交互式可视化界面中获得 CECircuit 类的响应。这些回调函数及其职责将在以下几行中解释。
Artist 函数
InitArtist (): 初始化 Artist 所需的变量。这些是 CircuitHeadPtr、SelectedCE 和 RepeatTimer。Artist 访问电路列表以在 GUI 中绘制 Circuit Elements。SelectedCE 用于当前选定的 Circuit Element,以便用户可以与之交互。RepeatTimer 是每十分之一秒设置的布尔变量,用于通过 Timer() 函数计算电路。
DrawChip (): 当 Artist 在遍历 Circuit Elements 列表时遇到一个芯片 Circuit Element,它使用此函数来绘制具有适当引脚数量的芯片。DrawChip() 根据给定的引脚数量绘制芯片。如果芯片的引脚数量不是偶数,则将其补足到最接近的偶数,以模拟芯片的自然外观。
DrawBB (): 包含绘制指定 BreadBoard 所需的顶点。与 DrawChip() 函数不同,DrawBB 是一个“显示列表”函数,因此它只被调用一次来向 OpenGL 解释 BreadBoard 的绘制。
DrawLED (): 像 DrawChip() 一样,当在 Circuit Elements 列表中遇到 LED Circuit Elements 时,使用此函数来绘制它们。
DrawCEMarker (): Circuit Element Marker 在 GUI 中响应用户当前选择的电路元件。它绘制一个指向当前选定 CE 的三角形箭头。
DrawHoleMarker (): 当选定的 CE 要绘制时,如果它处于悬停模式。孔标记绘制在 CE 的相应引脚下方和面包板孔上方。这使用户可以轻松地看到 CE 下方的相应孔,并且还可以通过孔标记的红色/绿色颜色来了解 CE 是否适合插入当前位置。
DrawCable (): 当 Artist 在遍历 CE 列表时遇到一根电缆,它会在正确的位置绘制电缆的每个引脚。它还会在电缆的两个节点之间绘制连接线,以在 GUI 中清晰地显示电缆的连接线。
DrawCircuit (): 是在空闲时间调用的主显示函数,用于绘制面包板和电路元件。它处理 CE 列表,并将每个元件绘制到面包板上的正确位置。它还进行必要的更新,以实现 LED 的照明过程,使其在 LED 打开时亮起,在 LED 关闭时不亮。
initOGL (): 完成 OpenGL 所需的初始化,例如环境光照的位置和绘制规范。此外,它引入了面包板、LED、CE Marker 和 Hole Markers 的显示列表(它们的形状随时间变化不大,与芯片和电缆不同),以便 OpenGL 可以更快地绘制它们。显示列表函数在它们的相应显示列表被处理时不会被调用。相反,OpenGL 会在第一次调用函数时保留并绘制顶点,当它们被声明为显示列表时。因此,显示列表的绘制过程比正常绘制过程更快(例如,定义面包板孔顶点的“for”循环不会第二次处理,而是第一次处理的顶点的输出被保存在相应的显示列表中,并在需要时从该列表中处理)。
EnterMainLoop (): 设置 GLUT 窗口所需的窗口属性并注册回调函数。您可以在附录 p.88 的代码中查看它们。
Keyboard () & SpecialKeys (): 是处理键盘输入的函数。它们获取传入的按键并执行用户相应的命令。按键编码如表 3.2.7 (1) 所示。
表 3.2.7 (1) – 按键代码定义
ReshapeWindow (): 当窗口被重塑和启动时,GLUT 会调用它。它会调整视口和相机透视,以便根据窗口的新纵横比调整视口,避免绘图对象变形。
Mouse (): 是获取和响应来自鼠标外设信息的函数。它帮助 Motion() 函数获取初始鼠标位置。
Motion (): 是鼠标按钮按下时鼠标移动时调用的函数。左鼠标按钮的移动允许用户修改相机位置,就像在一个半球体上围绕面包板旋转一样,以找到一个合适的视角来查看电路。右鼠标按钮的移动会在水平方向上移动相机,以便在面包板周围行走。当同时按下左右鼠标按钮并进行上下移动时,分别增加/减小相机的距离,用于变焦。
Idle (): 是 GLUT 空闲时调用的函数。它执行诸如在请求时修改相机位置(旋转和标准距离模式)、警告用户插入被标记为红色的 CE、发送计算定时器请求、在请求时移除临时用户消息以及在加载 CEC 文件时设置最后一个相机位置等任务。
Timer (): 是由 Idle() 函数注册的函数,用于更新电路计算。当 GLUT 处于空闲模式时,它会被调用,在十毫秒后触发电路进行计算。
CalculateTraverseCircuit (): 由 Timer() 函数调用,以触发 CECircuit 完成所有电路元件的单周期计算。
AddCable (): 当用户需要使用电缆时,它由 keyboard() 回调函数调用,以创建并添加一根新电缆到 CECircuit。
AddLED (): 与 AddCable() 方法相同,但用于 LED 插入。
AddSourceBars (): 在 CECircuit 的初始化时,它会添加一个电压源和一个接地源条,以提供电路的电压和接地源。它们被视为其他 Circuit Elements。
GiveUserMsg (): 当需要告知用户某些事件时调用。它加载 Artist 使用的消息缓冲区,并触发 Idle() 函数在屏幕左上角显示消息几秒钟。
FlyCam (): 加载 CEC 文件并开始会话时,调用它来将相机设置到上次保存电路时相机所在的位置。
3.3. CIRCUIT ENGINE SCRIPT LANGUAGE (CESL)
Circuit Engine 的结构需要 CESL 文件来检索和创建芯片,如 3.1 节所述。从这个意义上说,CESL 语言帮助用户编写自己的芯片并将它们迁移到 Circuit Engine 会话中。CESL 文件的简化结构如图 3.3 (1) 所示。
图 3.3 (1) – CESL 文件的简化结构
CESL 文件的第一个元素必须是面包板大小的定义。这样,就可以向 Circuit Engine 说明所需的面包板大小。面包板定义行的结构如下。
BREADBOARD <nof_rows> <nof_cols> <nof_holes_in_each_node>
BREADBOARD 关键字指定面包板的尺寸。<nof_rows> 和 <nof_cols> 确定面包板的电气节点数量,<nof_holes_in_each_node> 确定每个电气节点中的孔数以扩展节点。指定的 <nof_rows> 值至少是 CESL 文件中拥有最多引脚的芯片引脚数量的两倍。指定的 <nof_cols> 和 <nof_holes_in_each_node> 至少为 2,以便至少放置一个芯片在面包板上。
声明芯片所需的骨架如下

CHIP 块用于以块状方式指定其门和内部连接。当开始一个 CHIP 元素块时,它会用 <name> 命名。它的输入数量和输出数量分别由 <nof_inputs> 和 <nof_outputs> 声明。芯片头行如下。
CHIP <name> <nof_inputs> <nof_outputs>
必须在 CHIP 块中进行 VOLTAGE 和 GROUND 引脚规格,以按照以下方式定义哪个引脚将是芯片的电压引脚,哪个将是接地引脚。
VOLTAGE <ce_leg_no> GROUND <ce_leg_no>
CHIP 的 INPUT 声明必须等于 <nof_inputs>,并且它们按创建顺序编号(从 0 到声明的 INPUT 数量),就像 CHIP 块内的任何其他 GATE 一样。这个编号过程对于连接声明是必要的,并且是这样进行的:它开始为 INPUTS 编码,包括对应于从 0 到 n-1 的声明顺序的 GATE(其中 n = GATE 数量加上芯片的输入数量)。GATE 或 INPUT 的声明顺序将在后面的行中更清楚。
GATE 块用于定义 CHIP 中的一个门及其连接。
GATE <gtType> <gt_nof_inputs> [output <ce_leg_no>] <gtType> <gtDeclarationOrder> //input declarations… END GATE
在 GATE 块中,写入将是该门输入的声明。门连接是按前面提到的门创建顺序完成的。为了指示任何门的输出连接到当前正在由用户指定的 GATE 的输入,通过以下行指定输出门的类型和声明顺序。
<gtType> <gtDeclarationOrder (starts from 0)>
该行指定了类型为 <gtType> 且创建为第 <gtDeclarationOrder> 个的门的输出结果将作为当前由用户指定的 GATE 的输入。
除了连接门之外,还可以将 INPUT 连接到正在声明的门。换句话说,CHIP 的 INPUTS 可以像真实的芯片一样作为内部门的输入连接,如下行所示。
INPUT <gtDeclarationOrder>
芯片引脚的编号是标准的,就像真实的芯片一样,左上角的引脚编号为 1,右上角的引脚是最后一个,芯片缺口朝上。引脚数量将是 <nof_inputs> + <nof_outputs> + 2(用于电压和接地)。如果引脚数量是奇数,则将其补足到最接近的偶数引脚。(即,如果总引脚数为 13,则 Chip 将出现 14 引脚的外观,并且一个引脚将未关联且对 Chip 和电路无效。
为了更具体地理解电路引擎脚本语言,请检查以下示例。在此示例中,包含了第 3.1 节中提到的函数 F = X + Y'Z 的电路设计的芯片声明。
示例: 第 3.1 节的函数 F 的芯片声明。(FunctionF.cesl)
BREADBOARD 10 4 5 // specification of dimensions of the breadboard. CHIP Function_F 3 1 // chip will be 3(inputs) + 1(output) + 2(V&G) = 6 legs. VOLTAGE 6 // the Voltage is leg number 6. GROUND 3 // the Ground is leg number 3. INPUT 1 // ‘INPUT 0’ is connected to leg number 1. () INPUT 2 // ‘INPUT 1’ is connected to leg number 2. INPUT 4 // ‘INPUT 2’ is connected to leg number 4. GATE NOT 1 // this is the ‘NOT 3’ which has 1 input. INPUT 1 // getting input from ‘INPUT 1’. END GATE // closing the block of ‘GATE NOT 1’. GATE AND 2 // this is the ‘AND 4’ which has 2 inputs. NOT 3 // getting first input from ‘NOT 3’. INPUT 2 // getting second input from ‘INPUT 2’. END GATE // closing the block of ‘GATE AND 2’. GATE OR 2 output 5 // this is the ‘OR 5’ which has 2 inputs. INPUT 0 // getting first input from ‘INPUT 0’. AND 4 // getting second input from ‘AND 4’. END GATE // closing the block of ‘GATE OR 2 output 5’. // also, output of this gate is connected to leg number 5. END CHIP // closing the block of ‘CHIP Function_F 3 1’.
将移入电路引擎的函数 F 的对应芯片如图 3.3 (2) 所示。
图 3.3 (2) – 电路引擎识别的对应芯片图
3.4. 电路引擎电路 (CEC) 文件
电路引擎会话可以保存到电路引擎电路 (CEC) 文件中,以便在当前电路引擎会话结束后,仍可在后续会话中使用该会话。CEC 文件的通用结构如图 3.4 (1) 所示。
图 3.4. (1) – CEC 文件通用结构
CEC 文件包含面包板定义和电路引擎的电路元件列表。它是一个二进制文件,仅用于保存和加载电路引擎会话。
第一个字段是面包板尺寸规范字段(12 字节),其中包含面包板的尺寸信息。之后是电路列表字段。电路元件的字段大小根据其门列表中候选门而变化。您可以在上图详细观察电路元件的每个字段和门的每个字段。电路元件包含两个部分:电路元件头 (CEH) 字段和门列表 (GL) 字段。CEH 字段包含电路元件的主要属性,如类型、电路元件信息位数组、电源引脚数量、接地引脚数量、X 轴位置、Y 轴位置、输入数量、输出数量以及 GL 中的门数量。GL 部分包含 CEH 中的门数量字段所提到的门数量。GL 的每个元素又包含两个部分:门头 (GH) 和连接列表 (CL)。GH 包含门的属性字段,如类型、输入数量和引脚数量。CL 字段包含与当前门接触的门的序号。稍后,在加载 CEC 文件时,CL 会转换为相应门的地址。CEC 文件根据这些规范进行读取和写入。
4. 示例实现:全加器芯片
如我在第一节中所述,如果不进行实践,很难理解一个概念。从这个意义上说,让我通过一个示例实现来解释这个现象。让我们利用全加器设计,这是最常见的逻辑问题之一,涉及三个位的加法。
4.1 编写 CESL 代码
全加器设计如图 4.1 (1) 所示。我们需要这个设计来编写相应芯片的代码。
图 4.1 (1) – 全加器设计和真值表
我决定按照图中的圆圈数字将输入和输出连接到芯片引脚。芯片的相应 CESL 代码可以写成如下:
BREADBOARD 20 5 5 CHIP FullAdder 3 2 VOLTAGE 8 GROUND 4 INPUT 1 // ‘INPUT 0’ (BIT1) INPUT 2 // ‘INPUT 1’ (BIT2) INPUT 3 // ‘’INPUT 2’ (BIT3) GATE XOR 2 // ‘XOR 3’ INPUT 0 INPUT 1 END GATE GATE AND 2 // ‘AND 4’ INPUT 0 INPUT 1 END GATE GATE XOR 2 output 5 // ‘XOR 5’ XOR 3 INPUT 2 END GATE GATE AND 2 // ‘AND 6’ XOR 3 INPUT 3 END GATE GATE OR 2 output 6 // ‘OR 7’ AND 4 INPUT 2 END GATE END CHIP
对于本例,芯片的电源和接地引脚编号分别为引脚 8 和 4。引脚编号 1、2 和 3 分别分配给输入位 1、2 和 3。来自门输出的输出位分配给引脚编号 5(用于 SUM 输出)和引脚编号 6(用于 CARRY 输出)。填充引脚编号 7 以使芯片引脚数量为偶数,将在电路引擎环境中不关联且不活动。在下一节中,将解释电路的构造。
4.2. 电路引擎会话
在编写完全加器芯片代码后,我们可以通过将 CESL 文件名作为参数来运行电路引擎(如果您的系统中 CESL 文件与 CEngine.exe 关联,也可以双击 CESL 文件)。电路引擎环境的第一个屏幕如图 4.2 (1) 所示。
图 4.2 (1) – FullAdder.cesl 的初始电路引擎屏幕
从图中可以看出,面包板有 20 行、5 列,每个节点有 5 个孔,正如代码中所定义的。芯片以悬停模式出现在面包板的左上角。芯片左下角的绿色箭头是电路元件标记,指向选定的电路元件。芯片下方的绿色方块是孔标记,便于查看芯片当前的插入区域,并且每个方块的绿色表示可以将电路元件方便地插入到相应面包板孔中。
可以通过箭头键移动选定的电路元件,将芯片放置在面包板上的任何合适位置。移动后,可以通过 Enter 键将其插入(如果电路元件已插入状态,同一键也执行拔出操作)。按下 L 和 K 键分别添加 LED 和电缆,它们的放置方式与前面为芯片解释的相同。电缆有两条腿,可以独立于彼此移动。要切换电缆的节点选择,请使用 TAB 键。要选择下一个/上一个电路元件,请使用键 ] / [。插入电缆和 LED 并将所有电路元件插入到适当位置后,电路的视图可能如图 4.2 (2) 所示。

图 4.2 (2) – 插入操作后的电路
LED 的上引脚是它们的电源引脚,并连接到芯片编号为 5 和 6 的引脚,以便可视化相应引脚上的信息。您在面包板左上方看到的红色和黑色条是分别提供电路电源和接地连接的源条。观察电缆的连接方式,将电源扩散到整个电路。从芯片的设计可以看出,图中的上 LED 连接到 CARRY 引脚,下 LED 连接到 SUM 引脚。从芯片左侧可以看出,BIT1 和 BIT2 引脚已提供电压,但 BIT3 引脚未提供(BIT3 引脚上的电缆处于悬停模式)。在这种情况下,连接到 sum 引脚的 LED 处于关闭状态,连接到 carry 引脚的 LED 处于开启状态,这对应于二进制数 10(Carry = 1, Sum = 0),即十进制数 2。处于开启状态的 LED 响应为红色亮光,如图中所示的上 LED。
5. 结论
我相信这个程序将为学习逻辑设计以及计算机体系结构 I-II 课程的学生提供宝贵的经验。每个学生都可以在家构建自己的电路,而无需花费金钱或担心收集设计测试所需的设备。此外,电路引擎也可以用于那些无力负担逻辑实验室进行教学目的的学校。甚至,任何想在自己家中通过逻辑设计实验室的支持来学习逻辑设计的个人都可以使用它。
尽管电路引擎可以完成一些基本的逻辑设计电路任务,但它并非一个完全完成的项目。我希望这个项目能找到开发者并得到改进,以便它能在学校或其他地方为人类服务。
参考文献
Salamah, M., 1999, 计算机体系结构 I CMPE-224 讲义,东地中海大学,北塞浦路斯土耳其共和国。
Woo M. & Neider J. & Davis T. & Shreiner D., 1999, OpenGL 学习官方指南,版本 1.2 / OpenGL 体系结构审查委员会,Addison-Wesley,马萨诸塞州
Mano, M.M. & Kime, C.R., 2000, 逻辑与计算机设计基础,Prentice-Hall,新泽西州
6. 屏幕截图






