为LLVM/CLANG选择CPU架构



我正在设计TTL串行计算机,我正在努力选择更适合LLVM编译器后端的架构(我希望能够在那里运行任何c++软件)。没有MMU,没有乘法/除法,没有硬件堆栈,没有中断。

我有两个主要的选项:

1) 8位内存,8位ALU, 8位寄存器(~12-16)。内存地址宽度24位。所以我需要使用3个寄存器作为IP和3个寄存器用于任何内存位置。

不用说,在编译器中实现任何地址计算都是纯粹的痛苦。

2) 24位内存,24位ALU, 24位寄存器(~6-8)。平淡的记忆,不错。缺点是,由于设计的串行特性,即使我们在一些布尔值上操作,每个操作将花费3倍以上的时钟。24位内存数据宽度是昂贵的。一般来说,在硬件中实现它比较困难。


问题是:你认为在这个8位,无堆栈的硬件上实现所有c++功能是可能的吗?还是我需要更复杂的硬件来生成合理质量的代码?速度吗?

我同意使用LCC的建议。我在这个自制16位RISC项目中使用它:http://fpgacpu.org/xsoc/cc.html .

我不认为它应该有太大的区别,无论你是构建8位变体并使用3个带进位的添加来增加IP,还是24位变体并在硬件上完成整个事情。您可以在汇编程序中隐藏差异。

如果你看了我上面的文章,或者一个更简单的CPU: http://fpgacpu.org/papers/soc-gr0040-paper.pdf,你会发现你真的不需要那么多的操作符/指令来覆盖整型C重复序列。实际上,有一个lcc实用程序(ops)可以打印给定机器的最小操作符集。

有关更多信息,请参阅我关于将lcc移植到新机器的文章:http://www.fpgacpu.org/usenet/lcc.html

一旦我移植了lcc,我就编写了一个汇编程序,它从基本指令合成了一个更大的指令重复集。例如,我的机器有load-byte-unsigned,但没有load-byte-signed,所以我发出了这个序列:

lbs rd,imm(rs) ->
  lbu rd,imm(rs)
  lea r1,0x80
  xor rd,r1
  sub rd,r1

所以我认为你可以通过这个最小的操作覆盖:

  registers
  load register with constant
  load rd = *rs
  store *rs1 = rs2
  + - (w/ w/o carry)    // actually can to + with - and ^
  >> 1                  // << 1 is just +
  & ^                   // (synthesize ~ from ^, | from & and ^)
  jump-and-link rd,rs   // rd = pc, pc = rs
  skip-z/nz/n/nn rs     // skip next insn on rs==0, !=0, <0, >=0

更简单的方法是没有寄存器(或者等效地将寄存器与内存模糊——所有寄存器都有一个内存地址)。

为SP预留一个寄存器,并在编译器中编写prolog/epilog函数处理程序,这样您就不必担心堆栈指令。只有代码来存储每个被调用的保存寄存器,根据帧大小调整SP,等等。

中断(和从中断返回)是直接的。您所需要做的就是强制将跳转链接指令放入指令寄存器中。如果您选择像0这样的位模式,并将正确的地址放入源寄存器rs(特别是如果它是0),则可以通过触发器复位输入或额外的力到0和门来完成。我在上面的第二篇论文中使用了类似的技巧。

有趣的项目。我看到一场TTL/7400竞赛正在进行,我在想一台机器有多简单,如果在机器上添加一个32 KB或128 KB的异步SRAM来保存代码和数据,这会不会是作弊?

无论如何,快乐的黑客!

注。

1)你需要决定每个整型的大小。如果你愿意,你当然可以让char, short, int, long, long long等相同大小,一个24b的单词,尽管它在最小表示范围内不兼容。

2)虽然我在这里关注的是lcc,但你问的是c++。我建议先说服C。一旦你弄清楚了C的一些东西,包括软件中的*,/,%操作符等,那么无论是在LLVM还是GCC中,都应该更容易转移到完全成熟的c++。C和c++之间的区别"仅"在于处理虚函数调用、指向成员解引用的指针、动态强制转换、静态构造函数、异常处理等所需的额外的虚表和RTTI表以及代码序列(完全建立在C基本整数操作符的重复序列之外)。

IMHO,这是可能的c编译器。对于c++,我不太确定。

LLVM/CLang可能是8位计算机的艰难选择,

请先尝试lcc,然后再尝试llvm/etc, HTH。

Bill Buzbee成功重定向lcc编译器为他的Magic-1(被称为homebrewcpu)

尽管Magic-1的硬件设计和构造通常最受关注,但项目的最大部分(到目前为止)是开发/移植软件。为此,我不得不从头开始编写汇编器和链接器,重新定位C编译器,编写和移植标准C库,编写简化的操作系统,然后移植更复杂的操作系统。这是一个挑战,但也很有趣。我想我有点扭曲,但我碰巧喜欢调试困难的问题。而且,当您试图追踪的错误可能涉及以下一个或多个:硬件设计缺陷,松散或断线,松散或坏的TTL芯片,汇编程序错误,链接器错误,编译器错误,C运行时库错误,或最终有问题的程序中的错误时,有很多有趣的机会。哦,我也没有奢侈的把错误归咎于别人。

我一直惊讶于这该死的东西居然能跑得动,更不用说它跑得这么好了。

在我看来,无堆栈硬件已经不太适合C和c++代码了。如果你有嵌套函数调用,你将需要在软件中模拟堆栈,这当然要慢得多。

当走无堆栈路线时,你可能会将大多数变量分配为"静态",并且没有可重入函数。在这种情况下,6502风格的寻址模式是有效的。例如,您可以使用以下寻址模式:

  1. 直接地址(24位)作为操作码的一部分
  2. 直接地址(24bit) +索引寄存器(8bit)
  3. 间接访问:直接到内存的24位地址,其中包含实际地址间接访问:24位地址到内存,8位索引寄存器从内存添加值。

上面列出的地址模式将允许有效地访问以固定地址(静态分配)分配的数组、结构和对象。对于动态和堆栈分配的对象,它们的效率较低(但仍然可用)。

您还可以从串行设计中获得一些好处:通常24位+ 8位加法不需要24个周期,但您可以在进位为0时短路加法。

不直接将IP映射为寄存器,您可以只允许通过goto/branch指令更改它,使用与上面相同的地址模式。跳入动态计算的地址是相当罕见的,所以直接在操作码中给出整个24位地址更有意义。

我认为如果你仔细设计CPU,你可以非常有效地使用c++的许多特性。但是,不要期望任何随机的c++代码都能在如此有限的CPU上快速运行。

实现当然是可能的,但我怀疑它是否可用(至少对于c++代码)。正如已经指出的,第一个问题是缺乏堆栈。其次,一堆c++严重依赖于动态内存分配,而且c++的"内部"结构相当大。

所以,在我看来,如果你:

  1. 摆脱c++的需求(或者至少,限制自己的一些子集)
  2. 使用24位,而不是8位(寄存器也是如此)
  3. 添加硬件栈

您将无法在那里运行"任何" c++代码。例如fork(), system()等。例如,任何明显依赖于中断的东西。当然,你可以走很远的路。现在你的意思是任何可以或已经用c++编写的程序,还是你只局限于这种语言,而不是通常与C/c++相关的库?语言本身是一个更容易接受的规则。

我想更简单的问题/答案是,为什么不试试呢?到目前为止你都尝试过什么?可以认为x86是一个8位机器,不考虑对齐和许多8位指令。msp430被移植到llvm,以显示它可以多么容易和快速地完成,我希望看到这个平台有更好的支持(不是我的优势所在,否则我会这样做)一个16位平台。没有mmu。确实有一个堆栈和中断,不需要使用它们,如果你删除库规则,那么剩下什么需要中断?

我想看看llvm,但要注意的是,所产生的文档显示移植它是多么容易,是过时的和错误的,基本上你必须自己从编译器源代码中找出它。LLC有一本书,就是这样,没有优化。资源不能很好地在现代计算机上编译,总是不得不回到过去使用它,任何时候我在一个晚上之后走近它,只是试图建立它,因为我放弃了。Vbcc,简单,干净,文档化,对较小的处理器不友好。是c++吗,不记得了。在所有这些方法中,最容易让编译器启动并运行。在所有这些工具中,LLVM是最有吸引力和最有用的。不要靠近GCC,甚至不要想它,里面有胶带和钢丝把它绑在一起。

你发明指令集了吗?你们有模拟器和汇编器吗?在github上查找lsasim以找到我的指令集。你可以为我写一个llvm后端作为你的练习…(我的vbcc后端是可怕的,我需要重新开始)…

你必须对如何实现高级有一些想法,但你必须从指令集、指令集模拟器和某种汇编器开始。然后开始手动将C/c++代码转换为指令集的汇编,这应该会很快让你解决"我可以在没有堆栈的情况下完成这个任务吗"等问题。在这个过程中定义调用约定,使用调用约定手工实现更多的C/c++代码。然后深入研究编译器并制作后端。我认为你应该考虑vbcc作为一个垫脚石,然后前往LLVM,如果它看起来像它(isa)将工作。

最新更新