用VHDL编写的第四款SoC

2021-02-19 02:31:55

该项目实现了一个小型堆栈计算机,该计算机专门为基于J1 CPU执行Forth而设计。该处理器已从Verilog用VHDL重写,并进行了少许扩展。

为处理器创建一个FORTH,可以从UART或USB键盘和VGA适配器获取其输入。

与J1一样,H2处理器也是基于堆栈的处理器,它执行特别适合FORTH的指令集。

当前的目标是采用Xilinx Spartan-6 XC6LX16-CS324 FPGA的Nexys3板,随着该板即将达到其使用寿命,未来将以新板为目标。 VHDL是用通用的方式编写的,可以推断出硬件组件,而不是显式实例化硬件组件,尽管与Nexys3电路板组件的接口特定于该电路板上的外围设备,但这应该使代码具有可移植性。

SoC也可以使用用C编写的仿真器进行仿真,如下所示:

项目使用的许可证是混合使用的,并且基于每个文件。对于mycode,我使用MIT许可证-请随意使用它。使用的其他许可证是LGPL和Apache 2.0许可证,它们仅限于单个模块,因此,如果您对LGPL代码有某种厌恶,可以将其删除。

目前唯一可用的目标板是Nexys3,这种情况应该在将来改变,因为该板目前正处于生命周期的尽头。我希望支持的下一个板是它的继任者Nexys 4和myStormBlackIce(https://mystorm.uk/)。 myStorm板使用完整的开源工具链进行合成,放置和布线以及位文件生成。

Nexys3开发板(如果仅需要通过UART进行通讯,则不需要VGA监视器以及USB和键盘)。

Xilinx ISE可以(或可以免费)下载,但需要注册。 ISE必须顺其自然:

它将汇编H2 Forth源文件embed.fth,并在调试器激活的情况下在H2模拟器下运行assembledobject文件。图形仿真器可以与以下程序一起运行:

该项目针对原始的J1内核,并提供了eForthimplementation(使用Gforth编写,用于对J1内核的元编译/交叉编译)。它还为用C语言编写的系统提供了一个模拟器。

H2处理器和相关的外围设备现在非常稳定,但是该资源始终是有关指令和外围设备的行为以及寄存器映射的权威指南。

CPU保持线,只要处理器处于高电平,它就将处理器保持在相同状态。

H2 CPU的行为与J1 CPU非常相似,可以阅读J1 PDF以更好地了解该处理器。处理器为16位指令,占用单个时钟周期。大部分原始的Forth单词也可以在一个周期内执行,其中一个值得注意的例外是store("!"),它被分为两条指令。

加载并存储到保存H2程序的Block RAM中,丢弃最低位,其他每一次存储操作都使用该低位(例如,跳转并加载并存储到Input / Output外设)。因此,应用程序可以在访问程序RAM时使用最低位进行字符操作。

+ ------------------------------------------------- -------------- + | F | E | D | C | B | A | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + ----------------------------------------------- ---------------- + | 1 |文学价值| + ---------------------------------------------- ----------------- + | 0 | 0 | 0 |分支目标地址| + --------------------------------------------- ------------------ + | 0 | 0 | 1 |有条件的分支目标地址| + -------------------------------------------- ------------------- + | 0 | 1 | 0 |致电目标地址| + --------------------------------------------- ------------------ + | 0 | 1 | 1 | ALU操作| T2N | T2R | N2A | R2P | RSTACK | DSTACK | + ----------------------------------------------- ---------------- + | F | E | D | C | B | A | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + ----------------------------------------------- ---------------- + T:数据堆栈的顶部N:数据堆栈的下一个PC:程序计数器的文字值:将一个值压入数据堆栈条件:分支弹出并测试TCALLS:PC + 1到返回堆栈T2N:将T移至NT2R:将T移至返回堆栈的顶部N2A:将T存储到NR2P寻址的内存位置:将返回堆栈的顶部移至PCRSTACK和DSTACK是带符号的值(二进制补码),其为堆栈增量(用于增加或减少各自堆栈的堆栈的数量:返回和数据)

标记有前缀' o'的寄存器是输出寄存器,带有&i前缀的是输入寄存器。寄存器被分为寄存器的输入和输出部分,并且输入和输出寄存器的地址在所有情况下都不相互对应。

VHDL SoC中已实现以下外围设备,以与Nexys3板上的设备接口:

VGA输出设备,仅文本模式,来自http://www.javiervalcarce.eu/html/vhdl-vga80x40-en.html的80 x 40字符。与原始版本相比,该版本已进行了重大修改,现在已实现了大多数VT100终端仿真器。它有两种可用的字体:

应按顺序阅读以下对寄存器的描述,并描述外设如何工作。

SoC上具有一个具有固定波特率和格式(115200、8位,1个停止位)的UART。 UART在RX和TX通道上均具有深度为8的FIFO。 UART的控制权分为oUart和iUart。

为了将一个值写入UART断言TXWE以及将数据放入TXDO。可以通过查看iUart寄存器来分析FIFO状态。

要从UART读取值:可以检查iUart以查看FIFO中是否存在数据,如果在oUart寄存器中将RXRE置为有效,则在下一个时钟周期,数据将在iUart寄存器中显示。

可以通过重建VHDL项目来更改UART的波特率,只能通过修改uart.vhd来更改位长,奇偶校验位和停止位

+ ------------------------------------------------- ------------------------------ + | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + ----------------------------------------------- -------------------------------- + | X | X | TXWE | X | X | RXRE | X | X | TXDO | + ----------------------------------------------- -------------------------------- + TXWE:UART TX写入使能RXRE:UART RX读取使能TXDO:UART TX数据输出

VGA文本设备模拟一个终端,用户可以通过写入oVT100寄存器与之对话。它支持VT100终端功能的子集。该接口的行为非常类似于使用相同的繁忙和控制信号写入UART。输入来自板上的PS / 2键盘,其行为类似于UART的RX机制。

+ ------------------------------------------------- ------------------------------ + | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + ----------------------------------------------- -------------------------------- + | X | X | TXWE | X | X | RXRE | X | X | TXDO | + ----------------------------------------------- -------------------------------- + TXWE:VT100 TX写EnableRXRE:UART RX读EnableTXDO:UART TX数据输出

Nexys3板上的开关旁边有一组LED,可通过写入LEDO来打开(1)或关闭(0)这些LED。这里的每个LED对应于其旁边的开关。

+ ------------------------------------------------- ------------------------------ + | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + ----------------------------------------------- -------------------------------- + | X | X | X | X | X | X | X | X | LEDO | + ----------------------------------------------- -------------------------------- + LEDO:LED输出

计时器可由oTimerCtrl寄存器控制,它是一个13位的计时器,运行频率为100MHz,可以选择生成中断,并且可以使用iTimerDin寄存器读回当前的计时器内部计数。

一旦TE位被置位,定时器就会计数,一旦定时器达到TCMP值,它就会回绕,并可以通过置位INTE产生中断(也可以切换从定时器出来的Q和NQ线,并路由到板上的引脚(请参见约束文件top.ucf中的引脚)。

+ ------------------------------------------------- ------------------------------ + | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + ----------------------------------------------- -------------------------------- + | TE | RST | INTE | TCMP | + ----------------------------------------------- -------------------------------- + TE:定时器使能RST:定时器复位INTE:中断使能TCMP:定时器比较值

H2内核具有中断机制,必须通过指令启用或禁用中断。每个中断都可以用inIMSK位屏蔽以启用该特定中断。 ' 1'如果将IMSK中的某个位使能,则会启用该特定的中断,如果在其中启用了中断,则会将其发送到CPU。

+ ------------------------------------------------- ------------------------------ + | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + ----------------------------------------------- -------------------------------- + | X | X | X | X | X | X | X | X | IMSK | + ----------------------------------------------- -------------------------------- + IMSK:中断屏蔽

+ ------------------------------------------------- ------------------------------ + | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + ----------------------------------------------- -------------------------------- + | BTXC | + ----------------------------------------------- -------------------------------- + BTXC:波特率时钟设置

+ ------------------------------------------------- ------------------------------ + | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + ----------------------------------------------- -------------------------------- + | BRXC | + ----------------------------------------------- -------------------------------- + BRXC:波特率时钟设置

+ ------------------------------------------------- ------------------------------ + | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + ----------------------------------------------- -------------------------------- + |数据输出| + ---------------------------------------------- --------------------------------- +

该寄存器包含Nexys3板上板载存储器的控制寄存器。该板包含三个存储设备,两个非易失性存储设备和一个基于易失性RAM的设备。可通过asimple SRAM接口访问的两个设备(一个易失性M45W8MW16,一个非易失性-aNP8P128A13T1760E)都可以访问,第三个是基于SPI的存储设备,NP5Q128A13ESFC0E),目前无法访问。

+ ------------------------------------------------- ------------------------------ + | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + ----------------------------------------------- -------------------------------- + | OE |我们| RST |等待| RCS | FCS |地址嗨| + ---------------------------------------------- --------------------------------- + OE:输出启用-启用从当前地址读取到iMemDinWE:写入启用-启用将oMemDout写入当前地址的ram RST:重置闪存控制器RCS:RAM芯片选择,启用易失性存储器FCS:闪存芯片选择,启用非易失性MemoryAddress高:RAM地址的高位

OE和WE是互斥的,如果同时设置,则无效。

+ ------------------------------------------------- ------------------------------ + | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + ----------------------------------------------- -------------------------------- + |地址Lo | + ---------------------------------------------- --------------------------------- +

在Nexys3板上,有一排7段显示器,带有小数点(实际上是8段),可用于数字输出。 LED段无法直接寻址。而是将存储在L8SD中的值映射到十六进制显示值(或BCD值,但这需要重新生成SoC并修改VHDL中的泛型)。

值' 0'对应于在LED段上显示的零,' 15'到F'等。

+ ------------------------------------------------- ------------------------------ + | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + ----------------------------------------------- -------------------------------- + | L7SD0 | L7SD1 | L7SD2 | L7SD3 | + ----------------------------------------------- -------------------------------- + L7SD0:LED 7段显示(最左侧显示)L7SD1:LED 7段显示L7SD2: LED 7段显示器L7SD3:LED 7段显示器(最右边的显示器)

iUart寄存器与oUart寄存器结合使用。缓冲字节发送和接收的FIFO的状态在iUart寄存器中以及任何接收到的字节中都可用。

+ ------------------------------------------------- ------------------------------ + | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + ----------------------------------------------- -------------------------------- + | X | X | X | TFFL | TFEM | X | RFFL | RFEM | RXDI | + ----------------------------------------------- -------------------------------- + TFFL:UART TX FIFO FullTFEM:UART TX FIFO空RFFL:UART RX FIFO FullRFEM: UART RX FIFO空RXDI:UART RX数据输入

iVT100寄存器与oVT100寄存器配合使用。 iVT100寄存器以及任何接收到的字节都可以缓存用于字节发送和接收的FIFO的状态。它与theUUart / oUart寄存器的工作方式相同。

+ ------------------------------------------------- ------------------------------ + | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + ----------------------------------------------- -------------------------------- + | X | X | X | TFFL | TFEM | X | RFFL | RFEM | 0 | ACHR | + ----------------------------------------------- -------------------------------- + TFFL:VGA VT100 TX FIFO FullTFEM:VGA VT100 TX FIFO空RFFL:PS2 VT100 RX FIFO FullRFEM:PS2 VT100 RX FIFO空ACHR:PS2键盘上有新字符

+ ------------------------------------------------- ------------------------------ + | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + ----------------------------------------------- -------------------------------- + | X | X | X | TCNT | + ----------------------------------------------- -------------------------------- + TCNT:计时器计数器值

iSwitches包含来自多个来源的输入线。按钮(BUP,BDWN,BLFT,BRGH和BCNT)对应于Nexys3板上的D-Pad。开关(TSWI)是本手册中提到的开关,每个开关旁边都有一个LEDnext。

开关和按钮已经在硬件中进行了防抖动处理,因此一旦从这些寄存器中读取它们就无需进一步处理。

+ ------------------------------------------------- ------------------------------ + | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + ----------------------------------------------- -------------------------------- + | X | X | X | BUP | BDWN | BLFT | BRGH | BCNT | TSWI | + ----------------------------------------------- -------------------------------- + BUP:按钮向上BDWN:按钮向下BLFT:按钮向左BRGH:按钮向右BCNT:按钮向中TSWI:两个位置开关

来自SRAM或闪存的存储器输入,由oMemControl和oMemAddrLow索引。从闪存读取时,这实际上可能是状态信息或来自查询表的信息。

+ ------------------------------------------------- ------------------------------ + | 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | + ----------------------------------------------- -------------------------------- + |数据输入| + ---------------------------------------------- --------------------------------- +

当发生中断并在处理器中启用了中断时,将对内存中的位置进行调用-该位置与ISR号相同。 ISR的数量为' 4'将会对位置' 4'进行调用(而不是跳转)例如在内存中。

采取措施之前,中断的延迟至少为4-5个周期,在中断请求处理程序中存在2至3个周期的延迟,然后必须执行对内存中ISR位置的调用,然后调用对实现字的调用ISR本身。

如果两个中断同时发生,则从最小的中断号到最大的中断号进行处理。

当发生相同编号的中断但尚未处理时,中断将丢失。

H2的基于Disassembler和C的模拟器在单个程序中(请参阅h2.c)。该模拟器是VHDL测试平台tb.vhd的补充,不能替代它。元编译器在aneForth解释器的顶部运行,它包含在embed.c和embed.blk文件中。元编译器(Forth交叉编译器的用语)是一个Forth程序,用于创建在目标上运行的eForth映像。

该工具链目前处于不断发展的过程中,因此,h2.c和embed.c之间可能会进行更多集成,同时将EmbedVirtual Machine更改为更类似于H2 CPU的目标,其长期目标是创建自托管系统。

要同时构建两者,需要使用C编译器,构建目标为" h2"。将构建可执行文件,h2和&embedded"将构建元编译器:

Linux:cc -std = c99 h2.c -o h2#生成h2可执行文件cc -std = c99 embed.c -o embed#生成embed VM可执行文件./embed embed.blk embed.hex embed.fth#创建/ h2 -h#获取选项列表./h2 -r embed.hex#运行汇编文件Windows:gcc -std = c99 h2.c -o h2.exe#构建h2.exe可执行文件gcc- std = c99 embed.c -o embed.exe#构建embed.exe可执行文件embed.exe embed.blk embed.hex embed.fth#创建目标eForth iamgeh2.exe -h#获取选项列表h2.exe -r embed .hex#运行汇编文件

-停止处理选项,以下参数为文件-h打印帮助消息并退出-v增加日志记录级别-d分解输入文件(默认)-D完全分解输入文件-T运行仿真时进入调试模式-r运行十六进制文件-L#加载符号文件-s#运行模拟的步骤数(0 =永久)-n#指定要处理的NVRAM块文件(默认为nvram.blk)文件*

该程序是根据MIT许可发布的,可以随意使用并根据需要进行修改。通过最少的修改,它应该能够为原始J1内核汇编程序。

元编译器在嵌入式虚拟机上运行,​​它是最初从H2 CPU派生的16位虚拟机。该项目包括一个元编译方案,该方案允许eForth图像生成经过修改的新eForthimage。该系统适用于H2,它取代了用C编写的交叉编译器,从而允许创建H2的第一个映像。

元编译器是一个普通的Forth程序,它包含在embed.fth中。然后,使用元编译器Forth程序来构建能够在H2目标上运行的aneForth映像。

反汇编程序获取包含已汇编程序的文本文件,该文件由16位十六进制数字组成。然后,它尝试反汇编指令。也可以向它提供一个符号文件,该文件可以由汇编器生成,并尝试查找跳转和调用指向的位置。

反汇编程序由GTKwave调用的tcl脚本使用,它将来自系列的H2的指令跟踪

......