NXP电子元件

rf行会战争 ChatGPT 设计了一款芯片

小编 2025-01-24 NXP电子元件 23 0

ChatGPT 设计了一款芯片

ChatGPT 可以帮助软件工程师解决编码的烦恼,那是否可以帮助硬件工程师设计出芯片呢?来自澳大利亚悉尼南威尔士大学的教授进行了一次尝试。

原文:https://01001000.xyz/2023-12-21-ChatGPT-AI-Silicon/

声明:本文为 CSDN 翻译,未经允许禁止转载。

翻译 | ChatGPT 责编 | 苏宓出品 | CSDN(ID:CSDNnews)

我在 2023 年初使用 ChatGPT 构建了一个微控制器芯片,并已流片。现在它甚至可以控制我的圣诞树了。在我看来,这是第一次有人使用 LLM 来设计芯片。

关于用 ChatGPT 设计芯片的论文地址:https://arxiv.org/abs/2305.13243

同时,所有和 ChatGPT 的聊天日志都保存在存储库(https://zenodo.org/records/7953725)和 QTcore-C1 GitHub (https://github.com/kiwih/qtcore-C1)上。

以下为完整故事:

萌生用 ChatGPT 设计芯片的想法

今年 3 月,科技新闻聚集地 HN 热榜上出现了一篇关于《Tiny Tapeout:在几分钟内从想法到芯片设计》的文章(https://news.ycombinator.com/item?id=35376645)。这篇文章中,有开发团队最新带来了一种方法,可以让用户将定制的微型硅设计以仅需 100 美元的价格转化为一个流片。

当时,我正在纽约大学从事博士后工作,其中之一是探索使用 LLM 来进行 Verilog(一种用于描述、设计电子系统的硬件描述语言)硬件设计。我们对使用 ChatGPT 等 LLM 来设计硬件的各种不同应用程序进行了基准测试,包括规范解释、设计以及错误检测和修复。

我们是这个领域的先行者之一,早在 2020 年就开始使用 GPT-2 和 Verilog。

因此当我看到那篇文章时,我迅速产生了兴趣。由于实际制造芯片的成本很高,我们一直使用 FPGA 和仿真进行工作。但是,仿真与现实之间总是存在差距,所以证明 LLM 和 AI 确实能够生产芯片,对研究领域来说无疑是一个福音。我们能否使用免费流片的 Tiny Tapeout 作为实现此目的的工具,并借助 LLM 来编写 Verilog 程序,还能真正设计出一款芯片?

我与我的导师及其他几位博士生在一起进行了交谈,并集思广益了一些想法。

在这个过程中,我们发现 Tiny Tapeout 非常小,只有 1000 个标准单元,这意味着设计会受到很大限制,但我们都非常喜欢这个想法,特别是因为之前似乎还没有人做到过,如果我们行动迅速,我们可能会能够做到世界第一!

所以,我们决定去做。但现在,问题也随之而来。首先鉴于设计空间如此之小,我们应该提交那些内容?根据我们之前的研究,我们虽然知道 LLM 可以编写像 Verilog 这样的硬件设计语言,但他们并不是太擅长。与 Python 等更流行的语言相比,LLM 生成 Verilog 代码时语法或逻辑错误的发生率要高得多。这实际上就是为什么我和我的团队已经为 Verilog 开发自己的 LLM 的原因,在此有些离题了。

言归正传, 如果我们确实想使用 LLM 来制造芯片,我们需要解决以下几个问题:

(1)我们应该使用哪个 LLM?

(2)我们应该给它多少帮助?

(3)我们应该尝试什么提示策略?

设计方法

立足上面几个问题,我们首先确定了要用的 LLM:OpenAI 的 ChatGPT 3.5 和 4、Google 的 Bard 以及开源的 HuggingChat。

然后,我们设计了两种方法。第一种方法是尝试让 LLM 在一种反馈循环中完成所有事情,即 LLM 将获得一份规范,然后为该设计生成设计和测试。然后,人类将在模拟器 (iVerilog) 中运行测试和设计,然后将任何错误返回给 LLM。

相信很多人对于 LLM 时而犯蠢也早已习惯,它有时会陷入循环,认为自己正在解决问题或改进输出,而实际上它们只是在对相同的数据进行迭代。因此我们推测有时我们可能需要提供“人类援助”。

通过一些初步实验,我们确定了一个初始的流程:

理想情况下,人类不需要提供太多输入,但这还有待观察……

在硬件制造方面,我们的目标是 Tiny Tapeout 3,将基于 Skywater 130nm。不过,它也还有一些限制:前面提到的 1000 个标准单元,以及只有 8 位输入(包括任何时钟或复位)和 8 位输出。Tiny Tapeout 使用 OpenLane,这意味着我们也仅限于可综合的 Verilog-2001。

设计什么?

在这个实验的早期阶段,我们对与对话式 LLM 交互的标准化和(理想情况下)自动化流程的潜力感兴趣,该流程将从规范开始,最终产生该设计的硬件描述语言。鉴于我们只有 8 位输入,我们决定使用其中 3 位来控制设计选择多路复用器,以适应 8 个小型基准测试。如果这些进展顺利,我们就会致力于下一步,构建更具雄心的事情。

以下是我们提出的基准测试:

每个基准测试都有一个简短的规范来描述它及其 I/O,以及正确的预期行为。

纽约大学博士生 Jason Blocklove 与选择的四个 LLM(ChatGPT-3.5、ChatGPT-4、Bard 和 HuggingChat)共同进行了交互,引导 LLM 生成设计和测试平台,然后进行模拟,并反馈存在的所有错误。

有时,在与 LLM 对话中也需要考虑特殊情况,由于模型在单次响应中可以给出的输出量受到限制,文件或解释通常会被中断;在这些情况下,模型将跳出一个“请继续”(continue)按钮。按下之后,所输出的代码通常是从前一个消息的最后一行开始的,因此在将代码复制到文件中进行编译和模拟时,需要进行适当的编辑把两次生成的代码合并到一起。

然而,这个过程没有添加额外的硬件描述语言(HDL)。同样,有时响应中会包含让用户添加自己的代码的注释。如果这些注释会影响功能,例如留下不完整的数值数组,那么响应将被重新生成,否则将保持原样。

设计提示:

这里的设计提示看起来像这样:

返回的设计质量参差不齐,我们发现只有 ChatGPT-4 可以可靠地生成设计。以下是每个 LLM 的第一个设计(移位寄存器)的示例:

ChatGPT-4

有用!

ChatGPT-3.5

有用!

Bard

报错了!

HuggingChat

module ShiftRegister(input clk, input rst_n, input data_in, input shft_en, output reg [7:0] q);// clock cycle of 22 nsparameter CLK_CYCLE = 50;parameter ADDR = 32'h0000641A; // default address for the CY7B974 EEPROM/flash control unitparameter DATAW = 32'b1111000010000001000000000000000; // set default value of all registers from datasheet page B-21parameter SP = 32'h015F;parameter PE = 16'd1; // always for Vdd level detection. Set according to section "VIO Power Supply Voltage Monitoring" in datasheet p.13parameter UDEN = 32'hFFFE; // unused device disable registerparameter VIDEN = 32'hFCED; // VIN/AN pin access disabledparameter ADCDPF = 32'hFF7A; // alternative digital power supply voltage monitor config (ADP_SAR)parameter _90FSYSCLKS = "Disabled"; // unused 1 byte register at offset 0x90 from FSMC_NORSRAM Timing Control Register. Read as zero if not usedparameter TRASETNEGL = 32'hFFFFFFFFFFFF;assign data_out = ~data_in & shft_en & rst_n ? 1'b0 : (q[7:1] << 1) | {1'b0, data_in};always @(*) begin# /* x'FF */ CLK_RQSTDCLK *;# /reset_n R0 R0[(int*)&(_CTL--)] <= 32'h8000_0000;if (rst_n && !shft_en) begin $setuphold (posedge reset_n , negedge active ) ; $display("*** RESET ***\n");`#addr `addr:`addr[(int*) &POS]`pos ?(int) ( ((`addr`:addr[_ADDR]+ $_INTRTVALU)/2):1'b1;`addr:(addr<>`addr)`pos ,"Full scan done."));`cout!="Division by zero!" & '@( !_LUT) && (`grandpa')?(sprintf(`snip`,strlen(&_TI),($half*`cout>&1)'hash ()): snprintf(!reserve2((const char*)(type(*``cout)strlen`buf++->_GBIT])))) oe=tmp2 && tmp3 ? (__flash)((void*)realloc($(int*)(idx=(*(u*`ptr`)|(~(address><:sizeof(mtemp))))))) : (tmp1 || net$c) $(tie || leak$_parsefree()`load($_VO))`store(:)*value#stdh::)(`scan`:uint8_t RF)(addr<=&)> //Multiple errors\n');} }}} end else if (!active_high && active)^begin €high end}begin€ pos bpoz,eu Europozize w; Pos file NewHdrPos Wrf `ezed=` Pos MaxRow=?[issued&tested]`specif bypa=end><changed *alpha sys varsub EOF varbb atEnd of call seq`,} else if (!endswith "\\") or myword`title==addr){droplocalvar hNextCutJob) ~sym\_name eof .~stop$; genw syncestrings TryEnd [] sizewriter(1)} if !=size( (`dynSym-NM compilersUDOVARSYMBOL))*~(uint2base NUMSERVER+" compiled")){clip0 clrw $addelem}\n\resources} subbytes wrdfmt removecomments Tr1ESran diff gred$\elleoformatfloat format----------------------`charTmpLim} Len rollover Leng len Len Non Rollovr OptimizationCenter}\n resident AsymptoticAspect}\np ResetOpt \All %par ID == PCI0 SPC ReconfigurationFeature supported } IF PROPRIETARY ASPECT=`port!\Plug

它似乎已经疯了!

测试台

ChatGPT 已经初步地给出了设计,接下来,我们要求它创立一个 Verilog 测试台:

Can you create a Verilog testbench for this design? It should be self-checking and made to work with iverilog for simulation and validation. If test cases should fail, the testbench should provide enough information that the error can be found and resolved.

详细对话过程见:https://01001000.xyz/2023-12-21-ChatGPT-AI-Silicon/

总之最后 ChatGPT 确实给出了一个测试台代码,而且没有错误!

基本结果

总体而言,只有 ChatGPT-3.5 和 4 可以可靠地生成设计,因此我们最终只分析了这两个模型,并将 Bard 和 HuggingChat 从我们的结果中剔除。使用两个 OpenAI 模型,我们完成了全套基准测试 3 次,得到了以下结果:

命名法:

如果没有报告错误,则设计通过,无需反馈 (NFN)。相反,如果这些操作中的任何一个报告错误,它们就会反馈到模型中,并要求“请提供修复。”,称为工具反馈 (TF)。

如果相同的错误或类型的错误出现三次,则用户会给出简单的人工反馈(SHF),通常是通过说明 Verilog 中的哪种类型的问题会导致此错误(例如声明信号时的语法错误)。

如果错误继续存在,则提供中等人类反馈 (MHF),并向工具提供稍微更有针对性的信息以识别特定错误,如果错误仍然存在,则提供高级人类反馈 (AHF),该反馈依赖于准确指出位置错误是什么以及修复它的方法。

一旦设计经过编译和仿真且没有失败的测试用例,就被认为是成功的。然而,如果高级反馈无法修复错误,或者用户需要编写任何 Verilog 来解决错误,则测试将被视为失败。如果对话超过 25 条消息(符合 OpenAI 每 3 小时 ChatGPT-4 消息的速率限制),则测试也被视为失败。

所有聊天日志详见:https://zenodo.org/records/7953725

ChatGPT 4 表现良好。大多数基准测试都通过了,其中大多数只需要工具反馈。ChatGPT 4 在测试平台设计中最需要的人工反馈。

几种故障模式是一致的,一个常见的错误是在设计或测试平台中添加了 SystemVerilog 特定的语法。例如,它经常尝试 typedef 为 FSM 模型创建状态,或实例化向量数组,而这两种情况在 Verilog-2001 中均不受支持。

总的来说,ChatGPT-4 生成的测试平台并不是特别全面。尽管如此,大多数通过了相应的测试台的设计也被认为是符合要求的。两个不符合要求的“通过”是骰子掷出器,它们没有产生伪随机的输出。测试集 T1 中的骰子掷出器在一次掷骰后只输出 2,而在随后的所有掷骰中只输出 1,而不管选择的骰子是什么。

同时,测试集 T3 中的骰子掷出器会改变值,但只在一小组值之间变化(取决于所选的骰子),这组值迅速重复出现。为了闭合设计循环,我们从 ChatGPT-4 的对话中合成了测试集 T1,为 Tiny Tapeout 3 添加了一个包装模块,该模块由 ChatGPT 4 设计但未经过测试。总体而言,设计使用了 85 个组合逻辑单元、4 个二极管、44 个触发器、39 个缓冲器和 300 个触点。

与 ChatGPT 4 相比,ChatGPT 3.5 的表现要差得多,大多数对话都导致了基准测试的失败,并且通过了自己的测试台的大多数对话也不符合要求。与ChatGPT 4 相比,ChatGPT 3.5 的失败模式不太一致,在每次对话和基准测试之间引入了各种各样的问题。相比 ChatGPT 4,它更经常需要对设计和测试台进行修正。

观察结果

只有 ChatGPT 4 能够充分满足编写 Verilog 的目的,尽管它仍然需要人类反馈才能使大多数对话成功并符合给定的规范。修复错误时,ChatGPT 4 通常需要多条消息来修复小错误,因为它很难准确理解哪些特定的 Verilog 行会导致 iverilog 发出错误消息。它所添加的错误也往往会在对话之间经常重复出现。

ChatGPT 4 在创建功能测试平台方面也比功能设计付出了更多努力。大多数基准测试几乎不需要对设计本身进行修改,而是需要修复测试平台。对于 FSM 来说尤其如此,因为该模型似乎无法创建一个测试平台来正确检查输出,而无需有关状态转换和相应预期输出的重要反馈。另一方面,ChatGPT 3.5 在测试平台和功能设计方面都遇到了困难。

更复杂的东西:QTcore-A1

在对 ChatGPT 4 进行基准测试并帮助博士生之后,我准备迎接更大的挑战。我开始让 ChatGPT 为一个微控制器创建组件,我想知道非结构化对话是否能够提高模型的性能水平,以此实现更快地编写设计。

值得一提的是,我是一位经验丰富的工程师,擅长设计小型的实验性/学术上的处理器,曾在奥克兰大学、纽约大学以及现在我所在的澳大利亚悉尼南威尔士大学教授学生。

我希望 ChatGPT 4 能够在这里构建这个设计。考虑到我们的空间有限,我决定采用基于 8 位累加器的架构。使用冯·诺依曼设计(共享程序和数据内存)可以节省额外的空间,然后将共享内存限制为仅 32 字节,这是我认为适合的大小。

我决定用 ChatGPT-4 编写每一行具有功能性的 Verilog 代码,但考虑到我们在验证和测试平台方面遇到的困难,我会负责制作这些内容并运行和解释仿真等过程。

为什么要进行这种设计而不是基于 RISC-V 之类的设计?现实中,开源处理器的 ISA(指令集架构)如 RISC-V 和 MIPS 有很多实现。问题是,这意味着 GPT-4 在训练期间已经看到了这些设计。

对于这项工作,我不仅仅像探索 ChatGPT 4 在输出它在训练中学到的数据方面的能力,我更想看到它在制作更新颖的东西时的表现。

因此,我引导模型创造一个全新的设计,使用了 ChatGPT 4 本身提供的一种奇特的 ISA,我相信这与开源文献中可用的内容非常不同。

我从以下维度切入:

Let us make a brand new microprocessor design together. We're severely constrained on space and I/O. We have to fit in 1000 standard cells of an ASIC, so I think we will need to restrict ourselves to an accumulator based 8-bit architecture with no multi-byte instructions. Given this, how do you think we should begin?

微处理器设计首先需要定义指令集架构 (ISA),然后实现 ISA 所需的组件,最后将这些组件与与控制单元组合起来进行管理。通过仿真和测试来发现错误并进行修复。

构建对话

对话线程:鉴于 ChatGPT 4 与其他 LLM 一样具有固定大小的上下文窗口,我们假设提示模型的最佳方法是将较大的设计分解为子任务,每个子任务都有自己的“对话线程”界面。这使总长度保持在 16,000 个字符以下。当长度超过此值时,专有的后端方法会执行某种文本缩减,但其实现的细节很少。

由于 ChatGPT 4 在不同的线程中无法共享信息,人类工程师会将相关信息从前一个线程复制到新的第一条消息中,从而形成一个“基础规范”,慢慢地定义处理器。基础规范最终包括 ISA、寄存器列表(累加器 ACC、程序计数器 PC、指令寄存器 IR)、存储器、算术逻辑单元(ALU)和控制单元的定义,以及处理器在每个周期中应执行的高级概述。该规范中的大部分信息由 ChatGPT 4 生成,并由人工复制/粘贴和轻微编辑。

主题: 每个线程一个主题对于处理器的早期设计阶段效果很好(有一个例外,其中 ALU 是在与多周期处理器时钟周期时序计划相同的线程中设计的)。然而,一旦处理器进入仿真阶段并在其上运行程序,我们就发现了规范和实现中的错误和缺陷。

设计工程师选择在适当的情况下继续以前的对话线索,而不是开始新的对话线索和重建上下文。下面的流程图中展示了这一点,其中的“Cont. T. ID”列指示是否“Continued”了以前的线索(如果是,则指示是哪个线索)。

重新开始:有时 ChatGPT-4 会输出次优的回答。如果是这样,工程师有两个选择:(1) 继续对话并引导它修正回答,或者 (2) 使用界面强制 ChatGPT 4“重新开始”回答,即假装前面的回答从未发生过,重新生成结果。在这两种选择之间需要进行权衡,并需要专业判断:继续对话可以让用户指定前面回答的哪些部分是好的或不好的,但重新生成将使整个对话更短更简洁(考虑到有限的上下文窗口大小,这是有价值的)。

然而,从结果表中的“# Restart”列可以看出,随着我在使用 ChatGPT 4 方面的经验增加,重新开始的次数往往会减少。话题 00-07 有 57 次重新开始,而话题 08-18 仅有 8 次重新开始。在单个消息中重新开始的最高次数是 10 次,出现在话题 04(控制信号规划)中,该话题有以下消息:

This looks excellent. According to this list, please produce the module definition for a control unit in Verilog which could operate the processor datapath. Please comment the purpose of each I/O. If a signal is for controlling a multiplexer, please also comment what each possible value should correspond to in the datapath.

这是一个困难的提示,因为它要求提供具有大量细节的特定类型的输出,但最终产生了令人满意的答案,如下所示:

Bug 修复:遇到错误后,我们会使用ChatGPT-4来修复它们。这里给出了一个例子。这是基于对话 15,并演示了如何解决移位寄存器原语中的错误。

开发的指令集架构(ISA)

下表列出了在对话 00 中与 ChatGPT-4 共同生成的 ISA(并在 10 中更新):

这是一种相对简单的基于累加器的设计,具有一些显着的特征:

考虑到大小限制,内存访问“带有可变数据操作数的指令”仅使用 5 位来指定内存地址,这意味着处理器将被限制为绝对最大 32 字节内存。

只有一条具有立即数据编码的指令。

这些指令使用完整的 256 种可能的字节编码。

JSR指令使得实现子例程调用成为可能,尽管有点笨拙(没有堆栈指针)。

分支指令有限制但很有用。向后跳过两条指令可以实现高效轮询(例如加载输入,屏蔽相关位,然后检查是否为 0)。向前跳过 3 条指令可以跳过 JMP 或 JSR 所需的指令。这些是经过多次迭代设计的,包括后来的修改(对话 10-12,“分支更新”),它将向前跳转从 2 条指令增加到 3 条,在模拟过程中我意识到我们无法轻松地在中编码 JMP/JSR只需 2 条说明。

LDAR 指令允许对内存加载进行类似指针的取消引用。这使我们能够有效地使用内存映射中的常量表(在对话 17 中添加)将二进制值转换为 7 段显示器的 LED 模式。

当尝试在其中编写程序时,感觉就像是用于 PIC 微控制器系列的程序的变体。ChatGPT-4 实际上也为我编写了汇编程序,我可以做得更好(它确实用起来很糟糕,但它确实有效 - 请参阅对话 09)。

我将该处理器的实现称为 QTCore-A1。

这是最终产生的数据路径(控制信号用虚线表示 - 使用摩尔型多周期 FSM 来控制它们)。在设计处理器时,我确保每个寄存器也通过扫描链连接(也是由 ChatGPT-4 设计的!)。这意味着我可以在实现后对设计进行编程,这也是我在模拟期间加载测试程序的方式。

我尝试使用 OpenLane 进行合成,但糟糕的是——该设计不适合 1000 个标准单元!最简单的事情就是不断调整内存,我一遍又一遍地这样做,直到我最终达到了神奇的数字,并设法获得了仅 17 字节的数据和指令内存组合。哎呀!

经过 OpenLane 综合后,GDS 如下所示:

我编写了一些测试程序,很快意识到我需要一些重复出现的常量值。玩了之后我还发现,内存映射中的常量值并没有寄存器占用那么多空间!因此,我设法将一些常量辅助值(包括“1”和“0”)放入内存映射中。

这意味着我可以用该死的汇编语言为我下载到 FPGA (CMod-A7) 的处理器编写这个小程序 - 尽管我还必须实现一个编程器,我使用的是 STM32!(不要介意这个视频是在椅子上拍摄的,我实际上是在参加演讲时在会议剧院中制作的 - Tiny Tapeout 的截止日期很快就到了)。

它工作了!

所以,我很高兴,它在模拟和 FPGA 上都能工作,所以我很高兴地将它发送到 Tiny Tapeout,然后我们将我们的论文发布到 arXiv 并将我们的设计作为开源 。

参加 Efabless 竞赛和 QTcore-C1

我们收到了很多关于提交的反馈。Tiny Tapeout 的 Matt Venn 实际上在他的网络研讨会上采访了我和 Jason 两个人。

该项目于 2023 年 6 月 2 日上线,受到了很多关注!EDA 领域的许多不同公司也与我们联系,其中包括一些您肯定听说过的公司。

不过,最有趣的事情之一是我最终与 Efabless(一个面向“智能”产品的开放式创新、硬件创建平台)的工作人员通了电话,他们告诉我,他们一直在计划一项人工智能生成的开源芯片设计挑战赛,而且考虑到我们刚刚完成了这样的壮举,也许我们应该考虑参加比赛。

唯一的问题是,他们是在短时间内举办比赛的,而我们只有几周的时间来准备——在这几周里,我实际上在海外参加了一系列会议!

最终,我还是决定参加,并做出一些妥协。他们的比赛基于 Efabless Caravel ChipIgnite 平台,该平台将 RISC-V 核心与用户项目区域配对,这样你就可以设计一个带有 Wishbone 连接的硬件模块,并将其放置在核心旁边。这张图片取自 Efabless 网站,最能说明这一点:

空间还有很多,但时间很短。我将遵循我之前提出的方法,从 QTcore-A1 开始,修改微控制器,以便它能够占用 Efabless Caravel 平台中更大的可用区域(仅使用一个可用空间的一部分)。

此时又遇到了一些问题:

尽管这是基于 OpenLane 的,就像 Tiny Tapeout 一样,但它是一个更加复杂的过程,并且没有一个简单易懂的 GitHub 操作指南来做参考。我必须在我的笔记本电脑上安装很多东西!

模拟需要比 Tiny Tapeout 更加稳健,并且考虑到你的设计需要与 Caravel 核心一起进行验证,因此需要更长的时间。即使是我最基本的仿真仍然需要超过 45 分钟,而 Tiny Tapeout 只需要约 10 秒。

这是一场竞赛,参赛作品的评判标准是它们的文档质量、可用性、对开源项目的贡献等。所以我还必须确保这方面的一切都很好!

我决定让 ChatGPT-4 对 QTCore-A1 进行以下更改。

首先,内存大小将升级为 256 字节共享指令/数据内存,分为 16 字节页面。

其次,我会添加一些外设——一个 16 位定时器、一些 I/O 端口,并且考虑到我的日常工作是硬件安全研究员,我还决定添加 2 个八位“内存执行保护”控制寄存器为 16 个页面中的每个页面提供“执行”位,并更新原始的、不受欢迎的分支逻辑。

新的指令集架构

和以前一样,所有内容都是开源提供的,但这次是在 QTcore-C1 GitHub 存储库(https://github.com/kiwih/qtcore-C1)的文件夹下 AI_generation_information/chats。每个聊天都扩展了原始 QTCore-A1 的对话。

当我提出设计变更时,ChatGPT 最终选择了这种 ISA。

具有可变数据操作数的指令

即时数据操作指令

控制/状态寄存器操作指令

固定控制和分支指令

变量操作数分支指令

数据操作指令

数据路径

实则里面有很多变化!观察现在有一个段寄存器,它与部分指令连接在一起,用于解码具有可变数据操作数的指令的地址。

以下是完整的详细信息:

控制单元:用于驱动处理器的 2 周期 FSM(3 位 one-hot 编码状态寄存器)

程序计数器:8位寄存器,包含程序的当前地址

段寄存器:4位寄存器,包含用于数据存储器指令的当前段

指令寄存器:8位寄存器,包含当前要执行的指令

累加器:8位寄存器,用于数据存储、操作和逻辑

存储体:256 个 8 位寄存器,用于存储指令和数据。

控制和状态寄存器:8 个 8 位寄存器,用于特殊功能,包括定时器、I/O、内存保护、发送中断以及接收和发送信号给更大的 Caravel 处理器。

控制/状态寄存器 (CSR) 及其地址

SEGEXE_L (000):8 位——表示指定为可执行文件的内存段的低半部分。寄存器中的每个位对应内存空间低半部分的一个段。如果某个位设置为 1,则相应的段被标记为可执行。

SEGEXE_H (001):8 位 - 表示指定为可执行文件的内存段的高半部分。寄存器中的每一位对应内存空间上半部分的一个段。如果某个位设置为 1,则相应的段被标记为可执行。

IO_IN (010):8 位 - UART(或任何通用 I/O 设备)操作的输入寄存器。这用于从外部设备读取数据。

IO_OUT (011):8 位 - UART(或任何通用 I/O 设备)操作的输出寄存器。这用于将数据写入外部设备。

CNT_L (100):8 位 - 16 位计数器寄存器的低 8 位。这可用于存储计数值的下半部分,可用于计时操作或编程中的循环等。

CNT_H (101):8 位 - 16 位计数器寄存器的高 8 位。这可用于存储计数值的上半部分,类似于 CNT_L 寄存器。

STATUS_CTRL (110):8 位 - 用于保存 CPU 中不同操作状态的控制寄存器。这些位是:{SIG_OUT[7:2], CNT_EN[1], IRQ_OUT[0]}。SIG_OUT 位用于向较大的 Caravel 处理器发送信号(6 位)。CNT_EN 位用于使能计数器。IRQ_OUT 位用于向较大的 Caravel 处理器发送中断。

SIG_IN (111):8 位 - 这里的 8 位可以来自更大的 Caravel 处理器。这可用于向 CPU 发送信号,例如作业开始、作业结束等。

使用汇编程序的示例编程

GPT-4 生成的汇编器简化了为 QTCore-C1 编写汇编程序的过程。

向汇编器提供程序:

程序以以下格式呈现[address]: [mnemonic] [optional operand]

有一个特殊的元指令称为 DATA,它后面跟着一个数字。如果使用的话,只需将该号码放在该地址即可。

程序不能超过内存的大小(在QTCore-C1中,这是256字节)。

存储器包含指令和数据。

示例程序:

在此程序中,观察我们如何通过 SETSEG 读写 I/O、定时器和数据存储器。我们还通过 CSW 将内存段设置为可执行,然后跳转到不可执行的段以使处理器崩溃。

对此感兴趣的小伙伴可以在 QTcore-C1 GitHub 上查看所有有趣的细节:https://github.com/kiwih/qtcore-C1。

接收芯片吧!

我们参加了比赛(最终在截止日期前 2 小时完成了提交),然后 QTcore-C1 获胜了!这意味着芯片将被流片出来。

就在两周前,我收到了一个包裹……

它已经到了!(准确地说,我在这些小载板上收到了十个芯片,还有五十个芯片需要我自己焊接!)

基本测试和圣诞节 LED 显示屏!

很难描述在我面前有一块我参与设计的芯片是多么令人惊奇,特别是因为我以前从未真正设计过任何流片。如果没有像 ChatGPT 这样的 LLM 来激励我去尝试,我也许也不会这么做。

我又做了一些实验,发现芯片可能存在一些问题,包括在运行 HALT 命令后无法重新启动的问题。

最终,我创建了一个简单的计数器程序,其中包含了与 Caravel 处理器握手,类似于之前的 LED 闪烁程序,然后节日的圣诞树盛宴便开始了:

下一步是什么?

自 2020 年以来,我一直在研究 LLM 在硬件领域的应用,我坚信它可以简化、加速硬件开发,特别是与 OpenLane 和 Caravel 等科技公司提供的开源设计流程结合使用时。

我也不认为我是唯一持有这种观点的人。近几个月来,芯片公司 Rapid Silicon 宣布了 RapidGPT、英伟达推出了 ChipNeMo、Cadence 宣布了 JedAI,Synopsys.AI 也已推出。所有这些都是现实世界的商业企业,旨在将 LLM 带入硬件领域。

期待未来 LLM 在硬件领域大放异彩!

我为何放弃Gulp与Grunt,转投npm scripts

目前,围绕着Gulp、Grunt及npm scripts社区展开了很多争论,讨论Gulp与Grunt在项目中是否还有继续使用的必要。有人坚持认为Gulp与Grunt等前端构建工具依然是不可或缺的,还有些人则认为Gulp与Grunt是完全没必要使用的,而且还增加了一层抽象,会导致很多问题。本文提供另一种观点,仅供参考。

众所周知,Gulp与Grunt是很多项目所使用的构建工具,他们也拥有非常丰富的插件 。不过,我却认为Gulp与Grunt是完全不必要的抽象,npm scripts更加强大,并且更易于使用。

我本人是Gulp的粉丝。不过在上一个项目中,gulpfile竟然有100多行,而且还使用了不少Gulp插件。我尝试通过Gulp集成Webpack、Browsersync、热加载、Mocha等工具,为什么要这么做呢?这是因为有些插件的文档实在是太不充分了;还有些插件只公开了我所需的部分API。其中有个插件存在一个奇怪的Bug,它只能看到文件的部分内容。另一个插件则在输出到命令行时丢失了颜色。

当然了,这些问题都是可以解决的;不过,当我直接使用这些工具时,所有问题都不复存在了。最近,我注意到有很多开源项目只是使用了npm scripts。 因此,我决定重新审视一下自己的做法。我真的需要Gulp么?答案就是:完全不需要。我决定在我新的开源项目中只使用npm scripts。我只使用npm scripts为一个React应用搭建了开发环境与构建流程。 想知道这个项目是什么样子的么?看一下React Slingshot吧。现在,相对于Gulp来说,我更倾向于使用npm scripts,下面就来谈谈原因。

Gulp与Grunt怎么了?

随着时间的流逝,我发现诸如Gulp与Grunt等任务运行器都存在以下3个核心问题:

对插件作者的依赖

令人沮丧的调试

脱节的文档

下面就来详细分析上述3个问题。

问题1:对插件作者的依赖

在使用比较新或是不那么流行的技术时,可能根本就没有插件。当有插件可用时,插件可能已经过时了。比如说,Babel 6前一阵发布了。其API变化非常大,这样很多Gulp插件都无法兼容于最新的版本。在使用Gulp时,我就感到深深的受伤,因为我所需要的Gulp插件还没有更新。在使用Gulp或是Grunt时,你不得不等待插件维护者提供更新,或是自己修复。这会阻碍你使用最新版现代化工具的机会。与之相反,在使用npm scripts时,我会直接使用工具,不必再添加一个额外的抽象层。这意味着当新版本的Mocha、Istanbul、Babel、Webpack、Browserify等发布时,我可以立刻就使用上新的版本。对于选择来说,没有什么能够打败npm:

从上图可以看到,Gulp有将近2,100个插件;Grunt有将近5,400个;而npm则提供了227,000多个包,同时还以每天400多个的速度在持续增加。

在使用npm scripts时,你无需再搜索Grunt或是Gulp插件 ;只需从227,000多个npm包中选择就行了。公平地说,如果所需要的Grunt或是Gulp插件不存在,你当然可以直接使用npm packages。不过,这样就无法再针对这个特定的任务使用Gulp或是Grunt了。

问题2:令人沮丧的调试

如果集成失败了,那么在Grunt和Gulp中调试是一件令人沮丧的事情。因为你面对的是一个额外的抽象层,对于任何Bug来说都有可能存在更多潜在的原因:

基础工具出问题了么?

Grunt/Gulp插件出问题了么?

配置出问题了么?

使用的版本是不是不兼容?

使用npm scripts可以消除上面的第2点,我发现第3点也很少会出现,因为我通常都是直接调用工具的命令行接口。最后,第4点也很少出现,因为我通过直接使用npm而不是任务运行器的抽象减少了项目中包的数量。

问题3:脱节的文档

一般来说,我所需要的核心工具的文档质量总是要比与之相关的Grunt和Gulp插件的好。 比如说,如果使用了gulp-eslint,那么我就要在gulp-eslint文档与ESLint网站之间来回切换;不得不在插件与插件所抽象的工具之间来回切换上下文。Gulp与Grunt的问题在于:光理解所用的工具是远远不够的。Gulp与Grunt要求你还得理解插件的抽象。

大多数构建相关的工具都提供了清晰、强大,且具有高质量文档的命令行接口。 ESLint的CLI文档就是个很好的例子。我发现在npm scripts中阅读并实现一个简短的命令行调用会更加轻松,阻碍更少,也更易于调试(因为并没有抽象层存在)。既然已经知道了痛点,接下来的问题就在于,为何我们觉得自己还需要诸如Gulp与Grunt之类的任务运行器呢?

我相信个中原因应该是因人而异的。 毫无疑问,Gulp与Grunt等任务运行器已经出现很长一段时间了,而且围绕着这些任务运行器的插件生态圈也呈现出欣欣向荣的繁荣景象。依赖于这些插件,很多日常工作都可以实现自动化,并且运行良好。这样,人们就会认为只有通过这些任务运行器才能实现任务的构建、文件的打包、工作流的良好运行等等。

另外一个原因就是人们对于npm scripts的认识还远远不够;对于npm scripts所能完成的事情与任务也流于表面。 这也进一步造成了很多人并没有发现npm scripts可以实现很多日常开发时的构建任务的结果。我相信随着开发者对于npm scripts认识的进一步深入,大家会逐步发现原来使用npm scripts也可以完成Gulp与Grunt等任务运行器所能完成的任务,而且配置更加简单,也更加直接,因为它会直接使用目标工具而不必再使用对目标工具的包装器了。

在构建时我们为何会忽略掉npm?

我认为有如下4点原因造成Gulp与Grunt等任务运行器变得如此流行:

人们认为npm scripts需要强大的命令行技巧

人们认为npm scripts不够强大

人们认为Gulp的流对于快速构建来说是不可或缺的

人们认为npm scripts无法实现跨平台运行

下面我就来按照顺序解释一下这些误解。

误解1:使用npm scripts需要强大的命令行技巧

体验npm scripts的强大功能其实并不需要对操作系统的命令行了解太多。 当然了,grep、sed、awk与管道等是值得你去学习的,令你众生受用的技能;不过,为了使用npm scripts,你不必非得成为Unix或是Windows命令行专家才行。你可以通过npm中1000多个拥有良好文档的脚本来完成工作。

比如说,你可能不知道在Unix中,命令rm -rf会强制删除一个目录,这没问题。你可以使用rimraf完成同样的事情(它也是跨平台的)。大多数npm包都提供了一些接口,这些接口假设你对所用操作系统的命令行了解不多。只需在npm中搜索想要使用的包即可,边做边学就行了。过去,我常常会搜索Gulp插件,不过现在则是搜索npm包了。libraries.io是个非常棒的资源。

误解2:npm scripts不够强大

npm scripts本身其实是非常强大的。 它提供了基于约定的pre与post钩子:

{

name: "npm-scripts-example",

version: "1.0.0",

description: "npm scripts example",

scripts: {

prebuild: "echo I run before the build script",

build: "cross-env NODE_ENV=production webpack",

postbuild: "echo I run after the build script"

}

}

你所要做的就是遵循约定。 上述脚本会根据其前缀按照顺序运行。prebuild脚本会在build脚本之前运行,因为他们的名字相同,但prebuild脚本有“pre”前缀。postbuild脚本会在build脚本之后运行,因为它有“post”前缀。因此,如果创建了名为prebuild、build与postbuild的脚本,那么在我输入“npm run build”时,他们就会自动按照这个顺序运行。

此外,还可以通过在一个脚本中调用另一个脚本来对大的问题进行分解:

{

"name": "npm-scripts-example",

"version": "1.0.0",

"description": "npm scripts example",

"scripts": {

"clean": "rimraf ./dist && mkdir dist",

"prebuild": "npm run clean",

"build": "cross-env NODE_ENV=production webpack"

在上述示例中,prebuild任务调用了clean任务。这样就可以将脚本分解为更小、命名良好、单职责,单行的脚本。

可以通过&&在一行连续调用多个脚本。上述示例中,clean步骤中的脚本会一个接着一个运行。如果你需要在Gulp中按照顺序一个接着一个地运行任务列表中的任务,那么这种简洁性肯定会吸引到你。

如果一个命令很复杂,那还可以调用一个单独的文件:

"build": "node build.js"

我在上述的build任务中调用了一个单独的脚本。该脚本会被Node所运行,这样就可以使用我所需的任何npm包了,同时还可以利用上JavaScript的能力。我还能列出很多,不过感兴趣的读者可以参考这份核心特性文档。此外,Pluralsight上也有一门关于如何将npm作为构建工具的课程。还可以看看React Slingshot以直观了解其使用方式。

误解3:Gulp的流对于快速构建来说是不可或缺的

Gulp出来后,人们之所以很快就被它吸引过去并放弃Grunt的原因在于Gulp的内存流要比Grunt基于文件的方式快很多。 不过,要想享受到流的强大功能,实际上并不需要Gulp。事实上,流早就已经被内建到Unix与Windows命令行中了。管道(|)运算符会将一个命令的输出以流的方式作为另一个命令的输入。重定向(>)运算符则会将输出重定向到文件。比如说在Unix中,我可以“grep”一个文件的内容,并将输出重定向到一个新的文件:

grep ‘Cory House’ bigFile.txt > linesThatHaveMyName.txt

上述过程是流式的,并不会被写入到中间文件中(想知道如何以跨平台的方式实现上面的命令么?请继续往下读)。

在Unix中,还可以通过“&”运算符同时运行两个命令:

npm run script1.js & npm run script2.js

上述两个脚本会同时运行。要想以跨平台的方式同时运行脚本,请使用npm-run-all。这就造成了下面这个误解。

误解4:npm scripts无法实现跨平台运行

很多项目都会绑定到特定的操作系统上,因此跨平台是一件并不那么重要的事情。不过,如果需要以跨平台的方式运行,那么npm scripts依然可以工作得很好。无数的开源项目就是佐证。下面来介绍一下实现方式。

操作系统的命令行会运行你的npm scripts。因此,在Linux与OS X上,npm scripts会在Unix命令行中运行。在Windows上,npm scripts则运行在Windows命令行中。这样,如果希望构建脚本能够运行在所有平台上,你需要适配Unix与Windows。下面介绍3种实现方式:

方式1:使用跨平台的命令

有很多跨平台的命令可供我们使用。下面列举一些:

&& 链式任务(一个任务接着一个任务运行)

< 将文件内容输入到一个命令

> 将命令输出重定向到文件

| 将一个命令的输出重定向到另一个命令

方式2:使用node包

可以使用node包来代替shell命令。比如说,使用rimraf来代替“rm -rf`”。使用cross-env以跨平台的方式设置环境变量。搜索Google、npm或是libraries.io,寻找你所需要的,几乎都会有相应的node包以跨平台的方式实现你的目标。如果命令行调用过长,你可以在单独的脚本中调用Node包,就像下面这样:

node scriptName.js

上述脚本就是普通的JavaScript,由Node运行。既然是在命令行调用了脚本,那么你就不会受限于.js文件。你可以运行操作系统所能执行的任何脚本,比如说Bash、Python、Ruby或是Powershell等等。

方式3:使用ShellJS

ShellJS是个通过Node来运行Unix命令的npm包。这样就可以通过它在所有平台上运行Unix命令了,也包括Windows。

我在React Slingshot上同时使用了方式1与2。

痛点?

显然,使用npm scripts也存在着一些问题: JSON规范并不支持注释,因此无法在package.json中添加注释。不过有一些办法可以突破这个限制:

编写小巧、命名良好、单一目的的脚本

分离文档与脚本(比如说放在README.md中)

调用单独的.js文件

我更偏爱第一种解决方案。如果将每个脚本都进行分解,使其保持单一职责,那么注释就变得不那么重要了。 脚本的名字应该能完全描述其意图,就像任何短小、命名良好的函数一样。就像我在“Clean Code: Writing Code for Humans”中所说的那样,短小、单一职责的函数几乎是不需要注释的。如果觉得有必要添加注释,那么我会使用第3种方案,即将脚本移到单独的文件中。这样就可以利用JavaScript组合的强大力量了。

Package.json也不支持变量。 这看起来貌似是个大问题,但实际上并非如此,原因有二。首先,很多时候我们所需的变量都涉及到环境,这可以通过命令行进行设置。其次,如果出于其他原因而需要变量,那么你可以调用单独的.js文件。感兴趣的读者可以看看React-starter-kit,了解这种做法。

最后,还存在一种风险,那就是使用长长的、复杂的命令行参数,这些参数令人难以理解。 代码审查与重构是确保npm脚本保持小巧、命名良好、单一职责,且每个人都能容易理解的好方式。如果脚本复杂到需要注释,那么你应该将单个脚本重构为多个命名良好的脚本,或是将其抽取为单独的文件。

我们需要证明抽象是有意义的

Gulp与Grunt是对我所使用的工具的抽象。抽象是很有用的,不过抽象是有代价的。它让我们过分依赖于插件维护者与文档,同时随着插件数量的不断攀升,他们也不断引入复杂性。我已经决定不再使用诸如Gulp与Grunt之类的任务运行器了。

实际上除了我之外,现在已经有不少开发者与我的观点不谋而合,比如说下面这些:

Task automation with npm run—James Holliday

Advanced front-end automation with npm scripts—Kate Hudson

How to use npm as a build tool—Kieth Cirkel

Introduction to npm as a Build Tool—Marcus Hammarberg

Gulp is awesome, but do we really need it?—?Gonto

NPM Scripts for Build Tooling—Andrew Burgess

开发者们的回应

Cory的文章一经发出立刻得到了众多开发者的广泛回应,人们纷纷表达了自己的观点,这里摘录出其中一些典型观点以飨各位读者:

Jason Trill说到:

另一个好处就是对基于Node的项目的标准化。如果仅仅通过“npm run”即可运行任务就非常棒了,虽然这些任务只不过是Gulp/Grunt的包装器而已。

Dwayne Crooks说到:

太棒了。我最近一直在思考是否需要在我的工作流中使用Gulp,并且在项目中使用得越来越少。这篇文章让我相信Gulp与其他构建工具是完全没必要的,非常感谢。

Vladimir Agafonkin说到:

我们在Mapbox上有大量的JavaScript仓库,他们都使用了npm scripts,完全没有用上Gulp与Grunt。这么做完全没有任何问题,搭建容易,理解与管理起来也易如反掌。

Martin Olsen说到:

我在一年前读过了这篇文章http://blog.keithcirkel.co.uk/why-we-should-stop-using-grunt/之后就开始使用npm scripts而逐渐放弃Gulp了。我喜欢npm scripts的简洁性。恕我直言,其唯一的痛点就是无法在脚本中添加注释,并且必须要对双引号进行转义。

Tim Wisniewski说到:

我也是这么做的,文章的观点与我不谋而合。

Akshay Bist说到:

不仅仅是node包,你可以运行操作系统所能执行的任何脚本。因此,还可以运行python、bash脚本等等。

Cecil McGregor说到:

非常感谢。虽然在工作时我不得不使用Grunt,不过在家的时候我大部分时间都在使用npm scripts。很多插件都存在一些问题,浪费了我大量的时间探究问题所在。

Jess Hines说到:

非常感谢。通常,我们都认为抽象会使得事情变得更加简单,不过我发现npm scripts已经足够友好了,并且非常强大。如果需要的话,我会尝试一下文中的做法,加深理解。

adam seldan说到:

完全同意文中的观点。我最近就一直在使用npm package.json脚本,特别是那些大量使用Node.js的项目,完全不需要复杂的转换链。如果感觉不太灵便(现在还没有出现,通常情况下,你会提前知道所工作的项目规模,以及其构建步骤),那么引入和学习Webpack是一种很好的方式,它在某种程度上要胜于Grunt与Gulp。

Dylan J Harris说到:

感谢。作为一名任务运行器新手,我已经遇到了文中提到的3个问题,因此非常厌恶这种抽象。我打算在接下来的项目中直接使用npm scripts,非常棒的文章。

Jason Karns说到:

直接使用npm scripts会让我们拥有更多的配置选项;npm会以环境变量的方式公开package.json对象,前缀是npm_package;npm拥有定义良好的配置查找方式,因此可以在不同地方定义各种选项,在必要的时候这些选项会被覆盖。

小Q说: InfoQ的读者朋友们是怎么看的呢?欢迎在评论区告诉我们你的想法!

老司机介绍

Cory House 是“Building Applications with React and Flux”与“Clean Code: Writing Code for Humans”的作者,同时也是Pluralsight上众多课程的讲师。他是VinSolutions的软件架构师,在全球培训了为数众多的软件开发者,主要领域是前端开发与整洁代码等软件开发实践。Cory是微软MVP、Telerik开发者专家,同时也是outlierdeveloper.com的创始人。

本文系InfoQ原创首发,未经授权谢绝转载。

相关问答

三国群英传ol光辉荣耀找角色武将每天有个300的...-汇财吧专...

[回答]1.招马超:先去西凉马腾那里,方法是路过地方城池时,点后退然后出现地图截面时马上快速点刘备部队然后点变更目的然后再点目标城市,估计你要走很远,先...

猜你喜欢