【问题标题】:Creating a logic gate simulator创建逻辑门模拟器
【发布时间】:2009-06-10 12:11:22
【问题描述】:

我需要创建一个应用程序来创建逻辑电路并查看结果。这主要用于 A-Level(英国,一般为 16-18 岁)计算机课程。

我从来没有做过这样的应用程序,所以我不确定存储电路和评估结果的最佳设计(以可恢复的速度,比如 1.6Ghz 单核计算机上的 100Hz)。

我不想让电路由基本门(and、or、nand 等)构建,我想让这些门用于制造“芯片”,然后可以在其他电路中使用(例如,您可能想要做一个8bit的寄存器芯片,或者一个16bit的加法器)。

问题在于,此类电路的门数量大量增加,因此如果模拟在每个单独的门上工作,它将有 1000 个门要模拟,所以我需要简化这些可以放置在电路中的组件因此可以快速模拟它们。

我考虑为每个组件生成一个真值表,然后模拟可以使用查找表来查找给定输入的输出。尽管这样的表格的大小随着输入的增加而大幅增加,但我遇到了这个问题。如果一个芯片有 32 个输入,那么真值表需要 2^32 行。在许多情况下,这使用了大量的内存,因此对于重要的组件是不实用的,它也不适用于可以存储其状态的芯片(例如寄存器),因为它们不能简单地表示为输入和输出表。

我知道我可以对寄存器芯片之类的东西进行硬编码,但是由于这是出于教育目的,我想要它,以便人们可以制作自己的组件以及查看和编辑标准组件的实现。我考虑允许使用代码(例如 dll 或脚本语言)创建和编辑此类组件,例如,加法器可以表示为“输出 = 输入 A + 输入 B”,但是假设学生已经完成了足够的编程给定的语言能够理解和编写这样的插件来模仿他们电路的结果,这很可能不是这种情况......

是否有其他方法可以采用布尔逻辑电路并自动对其进行简化,以便仿真可以快速确定组件的输出?

至于存储组件,我正在考虑存储某种树结构,以便在评估链接到其输入的所有组件后评估每个组件。

例如考虑:A.B + C 模拟器将首先评估 AND 门,然后使用 AND 门和 C 的输出来评估 OR 门。

但是我突然想到,在输出链接回输入的情况下,将导致死锁,因为输入永远不会全部被评估...我该如何克服这个问题,因为程序只能评估一个一次关门?

【问题讨论】:

  • 我想知道......在这样的通用模拟器中,是否可以创建“蜂鸣器”(自创术语)?这是一些可以尽可能快地打开和关闭的电路。如果是这样,您还需要建立类似时间参考框架的东西,否则结果可能会变得不确定......
  • 啊,我已经想到了这样的方案。想象一个 XOR 门,其中它的输出连接到它的一个输入,而另一个输入是用户可控的。一旦用户给可控输入 1,XOR 门就会变得疯狂。那么在这种情况下正确的行为是什么?
  • 错误,未定义。当然,唯一要做的就是检测它并告诉用户它不允许吗?实际上,根据现有的模拟器 (tetzl.de/java_logic_simulator.html),输出为零,我确定这不是真的……
  • 好吧,如果您禁止循环(链接回输入的输出),您将大大简化任务。如果你不能禁止循环,那么我内心的声音告诉我,检测到这种“蜂鸣器”将等同于停止问题(虽然没有证据,只是一种“直觉”)。
  • 好吧,我需要允许类似用于制作人字拖的电路(例如 play-hookey.com/digital/rs_nand_flip-flop.html/...)。但是,循环返回自身的 XOR 永远不会产生定义明确、稳定的结果,因此不应该被允许。

标签: boolean-logic


【解决方案1】:

【讨论】:

  • 据我所知,它的局限性可以防止我面临的问题发生(即由于网格小,电路中的门数有限,并且没有从右到左的电线,所以你不能根据需要在门之间创建循环,比如用 NAND 门实现的 D 触发器)
【解决方案2】:

您不是第一个想要构建自己的电路模拟器的人 ;-)。

我的建议是选择一组最小的原语。当我开始我的(我打算在这些日子里恢复……)时,我有两个原语:

  • 来源:零输入,一个始终为 1 的输出。
  • 晶体管:两个输入AB,一个输出A and not B

显然,我有点误用了术语,更不用说忽略了电子产品的细节。关于第二点,我建议像我一样抽象成带有 1 和 0 的线。我从中获得了很多有趣的门和加法器图。当您可以将它们组装成电路并在集合周围画一个框(带有输入和输出)时,您可以开始构建更大的东西,例如乘法器。

如果你想要任何带有循环的东西,你需要加入某种延迟——所以每个组件都需要存储其输出的状态。在每个循环中,您都会根据上游组件的当前状态更新所有新状态。

编辑关于您对可扩展性的担忧,如何默认使用第一原理方法来模拟每个组件的状态和上游邻居,但提供优化子电路的方法:

    1234563并使用它。这可以为已识别的子电路自动完成(如果子电路出现不止一次,则可以重复使用)或选择。
  • 如果您有一个带有环路的子电路,您仍然可以生成真值表。这里有一些定点查找方法可以提供帮助。

  • 如果您的子电路有延迟(并且它们对封闭电路很重要),真值表可以包含状态列。例如。如果子电路有输入A,内部状态B,输出C,其中C

    A B | B C
    0 0 | 0 0
    0 1 | 0 0
    1 0 | 1 0
    1 1 | 1 1

  • 如果您有一个用户断言实现特定已知模式(例如“加法器”)的子电路,请提供使用硬编码实现来更新该子电路的选项,而不是通过模拟其内部部分。

【讨论】:

  • 好吧,我本来打算每根电线,但最好只有一根电线。就我个人而言,我喜欢 8 根平行线的外观,每根线的颜色为 1 或 0,一根线“代表”8,并且在某些模拟器中没有颜色编码。 Obv 我会从一个开关、LED、基本的门和电线开始。但是,我需要一种可以针对更复杂的组件进行扩展的设计,而我对真值表的第一个想法却没有。
【解决方案3】:

当我制作了一个电路仿真器(遗憾的是,它也不完整且未发布)时,我是这样处理循环的:

  • 每个电路元素都存储其布尔值
  • 当元素“E0”改变其值时,它会通知(通过观察者模式)所有依赖它的人
  • 每个观察元素都会评估其新值,并且会这样做

当 E0 发生变化时,会保留一个级别 1 的列表,其中包含所有受影响的元素。如果一个元素已经出现在这个列表中,它会被记住在一个新的 2 级列表中,但不会继续通知它的观察者。当 E0 开始的序列停止通知新元素时,处理下一个队列级别。即:按照顺序完成第一个元素添加到 level-2,然后下一个添加到 level-2,等等,直到所有 level-x 完成,然后你移动到 level-(x+1)

这绝不是完整的。如果您曾经有多个振荡器进行无限循环,那么无论您采用什么顺序,一个都可能阻止另一个轮到它。我的下一个目标是通过使用基于时钟的同步而不是级联组合来限制步骤来缓解这种情况,但我的项目从未走到这一步。

【讨论】:

    【解决方案4】:

    您可能想了解从 Nand 到俄罗斯方块的 12 步课程软件。 youtube 上有一段视频讨论它。

    课程页面在:http://www1.idc.ac.il/tecs/

    【讨论】:

    • 我对这本书的推荐度不够高,这似乎也是所有读过它的人的共识。 (在亚马逊上查看它的评论。)
    【解决方案5】:

    如果您可以禁止循环(输出链接回输入),那么您可以显着简化问题。在这种情况下,对于每个输入,都会有一个明确的输出。然而,循环可以使输出无法确定(或者更确切地说,不断变化)。

    评估没有环路的电路应该很容易 - 只需使用带有“结”(逻辑门之间的连接)的 BFS 算法作为列表中的项目。从处于“未定义”状态的所有门的所有输入开始。一旦门的所有输入“定义”(1 或 0),计算其输出并将其输出连接添加到 BFS 列表。这样,您只需评估每个门和每个路口一次。

    如果存在环路,则可以使用相同的算法,但可以以永远不会“静止”的方式构建电路,并且某些连接点总是在 1 和 0 之间变化。 s>

    OOps,实际上,这种算法不能在这种情况下使用,因为循环门(以及取决于它们的门)将永远保持为“未定义”。

    【讨论】:

    • 似乎是一个不错的计划,我只需要某种无限循环检测来捕捉不稳定的电路,这样评估过程就不会陷入无限循环,因为我不想详细介绍一个门需要多长时间来改变状态和类似的东西。
    • 假设我可以捕捉到不稳定的组合,那么只列出“脏”门怎么样。当一个门的输入改变时,它被标记为脏。如果在评估后输出与之前的输出不同,则标记输入刚刚更改为脏的门。这样循环应该能够正确达到稳定的结果?
    • 嗯?没有抓住你的想法。说-您准备为这样的应用程序支付多少费用?也许我可以为你写? :)
    • 如果你写了它,我将不得不制作另一个应用程序大声笑。无论如何,我的想法是维护一个“脏”组件列表,即由于输入更改而需要更新的组件。当该列表中的组件被更新和删除时,它会将其输出与其旧输出进行比较。如果给定的输出发生了变化,它会将使用该输出作为输入的组件添加到脏列表中以进行更新。所以最终,如果一个电路的电流输入处于稳定状态,它将达到列表为空的阶段。
    • 嗯,是的,这与我在回答中试图说的基本相同。 :) 我保留了所有已更改连接的列表,而不是脏组件。
    【解决方案6】:

    您可以向他们介绍卡诺图的概念,这将帮助他们为自己简化真值。

    【讨论】:

      【解决方案7】:

      您可以硬编码所有常见的。然后让他们从硬编码的(包括低级门)中构建自己的,这将通过评估每个子组件来评估。最后,如果其中一个“芯片”的输入/输出少于 X,您可以将其“优化”到查找表中。也许检测它有多普遍并且只对最常用的 Y 芯片执行此操作?这样您就可以在速度/空间之间取得良好的平衡。

      你总是可以 JIT 编译电路...

      因为我还没有真正考虑过,所以我不确定我会采用什么方法。但它可能是一种混合方法,我肯定也会硬编码流行的“芯片”。

      【讨论】:

      • 这是一个好计划,我也可以为硬编码的逻辑电路实现,这样它们就不会“出现”硬编码(即学生仍然可以深入研究它们以查看寄存器是如何实现的)。
      • “你总是可以 JIT 编译电路......”这就是我通过“自动简化它”所想到的。但是,我一生中从未编写过编译器或解释器。考虑到电路的结果可能比循​​环可以存储位的简单表达式复杂得多,分析电路并生成所需的代码来表示它需要做多少工作。
      • 我会使用 LLVM 或其他一些“JIT 引擎”来进行 JIT 编译。 LLVM“字节码”也可以调用 C 函数。您可以粗略地分析电路以生成一系列布尔运算 - 这应该很容易传递给 LLVM。我不知道确定逻辑操作的难易程度——尤其是如果你有反馈循环等。不过会很有趣!如果你最终做了那样的“事情”,我很想听听。
      【解决方案8】:

      当我在制作“数字电路”仿真环境时,我将每个定义的电路(一个基本门、一个多路复用器、一个解复用器和几个其他原语)都与一个传递函数(即一个函数根据当前输入计算所有输出)、“议程”结构(基本上是“何时激活特定传递函数的链表”)、虚拟线路和全局时钟。

      我任意设置线路以在输出发生变化时硬修改输入,并更改任何电路上的输入以安排在门延迟后调用传递函数。有了这个,我可以同时容纳时钟和非时钟电路元件(时钟元件设置为其传递函数在“下一个时钟转换,加上门延迟”时运行,任何非时钟元件只取决于门延迟)。

      从来没有真正开始为它构建一个 GUI,所以我从来没有发布过代码。

      【讨论】:

      • 我们中有多少人像这样开始了一个项目,令人震惊的是很少有人发布它。我也有罪。
      猜你喜欢
      • 2012-05-26
      • 1970-01-01
      • 2021-07-06
      • 2011-12-22
      • 2015-04-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-01-21
      相关资源
      最近更新 更多