项目 | 用 VHDL 编写的 Forth SoC |
---|---|
作者 | 理查德·詹姆斯·豪 |
版权 | 2013-2019 理查德·豪 |
执照 | 麻省理工学院/LGPL |
电子邮件 | [email protected] |
该项目实现了一个基于 J1 CPU 定制的小型堆栈计算机,用于执行 Forth。该处理器已从 Verilog 用 VHDL 重写,并略有扩展。
该项目的目标如下:
这三项都已完成。
H2 处理器与 J1 一样,是基于堆栈的处理器,执行特别适合 FORTH 的指令集。
当前的目标是 Nexys3 板,带有 Xilinx Spartan-6 XC6LX16-CS324 FPGA,未来将针对新板,因为该板即将达到其使用寿命。 VHDL 以通用方式编写,硬件组件被推断而不是显式实例化,这应该使代码相当可移植,尽管 Nexys3 板组件的接口特定于该板上的外设。
可以在此处查看该项目在硬件上的实际运行视频:
SoC也可以用C语言编写的模拟器进行仿真,如下所示:
系统架构如下:
该项目使用的许可证是混合的,并且基于每个文件。对于我的代码,我使用 MIT 许可证 - 所以请随意使用它。使用的其他许可证是 LGPL 和 Apache 2.0 许可证,它们仅限于单个模块,因此如果您对 LGPL 代码有一些厌恶,可以将其删除。
目前唯一可用的目标板是 Nexys3,这应该会在未来发生变化,因为该板目前已达到其生命周期的终点。我希望支持的下一个主板是它的后继产品 Nexys 4 和 myStorm BlackIce (https://mystorm.uk/)。 myStorm 板使用完全开源的工具链进行综合、布局布线以及位文件生成。
该构建已在 Debian Linux 版本 8 下进行了测试。
您将需要:
硬件:
Xilinx ISE 可以(或可能)免费下载,但需要注册。 ISE 需要出现在您的路径上:
PATH=$PATH:/opt/Xilinx/14.7/ISE_DS/ISE/bin/lin64;
PATH=$PATH:/opt/Xilinx/14.7/ISE_DS/ISE/lib/lin64;
制作基于 C 的工具链:
make embed.hex
制作可以烧写到目标板的位文件:
make simulation synthesis implementation bitfile
将位文件上传到目标板:
make upload
查看“make仿真”生成的波形:
make viewer
基于 C 的 CLI 模拟器可以通过以下方式调用:
make run
这将汇编 H2 Forth 源文件 embed.fth,并在激活调试器的情况下在 H2 模拟器下运行汇编的目标文件。图形模拟器可以通过以下方式运行:
make gui-run
这需要 freeglut 以及 C 编译器。
原始 J1 项目位于:
该项目针对原始 J1 核心并提供 eForth 实现(使用 Gforth 编写,用于 J1 核心的元编译/交叉编译)。它还为用 C 编写的系统提供了模拟器。
元编译器所基于的 eForth 解释器可以在以下位置找到:
H2 处理器和相关外设现在相当稳定,但是源代码始终是关于指令和外设行为以及寄存器映射的权威指南。
J1 CPU 有一些修改,其中包括:
H2 CPU 的行为与 J1 CPU 非常相似,可以阅读 J1 PDF 以更好地了解该处理器。该处理器是 16 位的,指令占用一个时钟周期。大多数原始 Forth 字也可以在单个周期中执行,一个值得注意的例外是 store(“!”),它被分成两条指令。
CPU 内部有以下状态:
加载并存储到保存 H2 程序的块 RAM 中,丢弃最低位,所有其他内存操作都使用较低位(例如跳转以及加载和存储到输入/输出外设)。这样应用程序在访问程序 RAM 时可以使用最低位进行字符操作。
指令集的解码方式如下:
+---------------------------------------------------------------+
| F | E | D | C | B | A | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+---------------------------------------------------------------+
| 1 | LITERAL VALUE |
+---------------------------------------------------------------+
| 0 | 0 | 0 | BRANCH TARGET ADDRESS |
+---------------------------------------------------------------+
| 0 | 0 | 1 | CONDITIONAL BRANCH TARGET ADDRESS |
+---------------------------------------------------------------+
| 0 | 1 | 0 | CALL TARGET ADDRESS |
+---------------------------------------------------------------+
| 0 | 1 | 1 | ALU OPERATION |T2N|T2R|N2A|R2P| RSTACK| DSTACK|
+---------------------------------------------------------------+
| F | E | D | C | B | A | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+---------------------------------------------------------------+
T : Top of data stack
N : Next on data stack
PC : Program Counter
LITERAL VALUES : push a value onto the data stack
CONDITIONAL : BRANCHS pop and test the T
CALLS : PC+1 onto the return stack
T2N : Move T to N
T2R : Move T to top of return stack
N2A : STORE T to memory location addressed by N
R2P : Move top of return stack to PC
RSTACK and DSTACK are signed values (twos compliment) that are
the stack delta (the amount to increment or decrement the stack
by for their respective stacks: return and data)
所有 ALU 操作都替换 T:
价值 | 手术 | 描述 |
---|---|---|
0 | 时间 | 栈顶 |
1 | 氮 | 将 T 复制到 N |
2 | 时间+N | 添加 |
3 | 泰恩 | 按位与 |
4 | T 或 N | 按位或 |
5 | ^N | 按位异或 |
6 | ~T | 按位取反 |
7 | T=N | 平等测试 |
8 | N < T | 签名比较 |
9 | N >> T | 逻辑右移 |
10 | T-1 | 递减 |
11 | 右 | 返回栈顶 |
12 | [T] | 从地址加载 |
13 | N << T | 逻辑左移 |
14 | 深度 | 堆栈深度 |
15 | N u < T | 无符号比较 |
16 | 设置CPU状态 | 启用中断 |
17 号 | 获取CPU状态 | 中断打开了吗? |
18 | 深度 | 返回深度 stk |
19 号 | 0= | T==0? |
20 | CPU ID | CPU标识符 |
21 | 文字 | 内部指令 |
带有“o”前缀的寄存器是输出寄存器,带有“i”前缀的寄存器是输入寄存器。寄存器分为寄存器的输入和输出部分,并且输入和输出寄存器的地址并非在所有情况下都彼此对应。
VHDL SoC 中已实现以下外设,以便与 Nexys3 板上的设备连接:
SoC 还具有一组可以启用或禁用的有限中断。
输出寄存器映射:
登记 | 地址 | 描述 |
---|---|---|
奥雅特 | 0x4000 | 串口寄存器 |
VT100 | 0x4002 | VT100 终端写入 |
OLED | 0x4004 | LED输出 |
o定时器Ctrl | 0x4006 | 定时器控制 |
内存输出 | 0x4008 | 内存数据输出 |
内存控制 | 0x400A | 内存控制/高地址 |
内存地址低位 | 0x400C | 内存低地址 |
o7SegLED | 0x400E | 4 x LED 7 段显示器 |
掩码 | 0x4010 | CPU中断屏蔽 |
串口波特率 | 0x4012 | UART Tx 波特率时钟设置 |
串口波特率 | 0x4014 | UART 接收波特率时钟设置 |
输入寄存器:
登记 | 地址 | 描述 |
---|---|---|
艺术 | 0x4000 | 串口寄存器 |
iVT100 | 0x4002 | 终端状态和 PS/2 键盘 |
i开关 | 0x4004 | 按钮和开关 |
定时器Din | 0x4006 | 当前定时器值 |
iMemDin | 0x4008 | 内存数据输入 |
应按顺序阅读以下寄存器的描述,并描述外设的工作原理。
SoC 上有一个具有固定波特率和格式(115200、8 位、1 个停止位)的 UART。 UART 在 RX 和 TX 通道上都有一个深度为 8 的 FIFO。 UART 的控制分为 oUart 和 iUart。
要将值写入 UART,请置位 TXWE,同时将数据放入 TXDO。可以通过查看 iUart 寄存器来分析 FIFO 状态。
要从 UART 读取值:可以检查 iUart 以查看 FIFO 中是否存在数据,如果在 oUart 寄存器中置位 RXRE,则在下一个时钟周期,数据将存在于 iUart 寄存器中。
UART的波特率可以通过重建VHDL工程来改变,位长、奇偶校验位和停止位只能通过修改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 Write Enable
RXRE: UART RX Read Enable
TXDO: UART TX Data Output
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 Write Enable
RXRE: UART RX Read Enable
TXDO: UART TX Data Output
在 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 Output
定时器由 oTimerCtrl 寄存器控制,它是一个以 100MHz 运行的 13 位定时器,它可以选择生成中断,并且可以使用 iTimerDin 寄存器读回当前定时器内部计数。
一旦 TE 位被置位,定时器就会开始计数,一旦定时器达到 TCMP 值,它就会回绕,并且可以选择通过置位 INTE 来生成中断。这也会切换来自定时器并路由到板上引脚的 Q 和 NQ 线(有关引脚,请参阅约束文件 top.ucf)。
可以通过写入 RST 来重置定时器。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| TE | RST|INTE| TCMP |
+-------------------------------------------------------------------------------+
TE: Timer Enable
RST: Timer Reset
INTE: Interrupt Enable
TCMP: Timer Compare Value
H2 内核具有中断机制,必须通过指令启用或禁用中断。每个中断都可以通过 IMSK 中的一位来屏蔽,以启用该特定中断。 IMSK 中的一位中的“1”启用该特定中断,如果在其中启用了中断,则该中断将被传送到 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: Interrupt Mask
该寄存器仅用于设置传输的波特率和采样时钟频率。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| BTXC |
+-------------------------------------------------------------------------------+
BTXC: Baud Clock Settings
该寄存器仅用于设置接收的波特率和采样时钟频率。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| BRXC |
+-------------------------------------------------------------------------------+
BRXC: Baud Clock Settings
当 oMemControl 中发出写使能 (WE) 时,数据将输出到选定的地址。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| Data Ouput |
+-------------------------------------------------------------------------------+
该寄存器包含 Nexys3 板上板载存储器的控制寄存器。该板包含三个存储设备、两个非易失性存储设备和一个基于易失性 RAM 的设备。可通过简单 SRAM 接口访问的两个器件(一个易失性 M45W8MW16,一个非易失性 - NP8P128A13T1760E)均可访问,第三个是基于 SPI 的存储器件 NP5Q128A13ESFC0E),但目前无法访问。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| OE | WE | RST|WAIT| RCS| FCS| Address Hi |
+-------------------------------------------------------------------------------+
OE: Output Enable - enable reading from current address into iMemDin
WE: Write Enable - enable writing oMemDout into ram at current address
RST: Reset the Flash memory controller
RCS: RAM Chip Select, Enable Volatile Memory
FCS: Flash Chip Select, Enable Non-Volatile Memory
Address Hi: High Bits of RAM address
OE 和 WE 是互斥的,如果两者都设置了则没有效果。
内存控制器正在积极开发中,其接口可能会发生变化。
这是 RAM 的低地址位。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| Address Lo |
+-------------------------------------------------------------------------------+
Nexys3 板上有一组 7 段显示器,带有小数点(实际上是 8 段),可用于数字输出。 LED 段无法直接寻址。相反,L8SD 中存储的值被映射为十六进制显示值(或 BCD 值,但这需要重新生成 SoC 并修改 VHDL 中的通用值)。
值“0”对应于 LED 段上显示的零,“15”对应于“F”等。
连续有 4 个显示器。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| L7SD0 | L7SD1 | L7SD2 | L7SD3 |
+-------------------------------------------------------------------------------+
L7SD0: LED 7 Segment Display (leftmost display)
L7SD1: LED 7 Segment Display
L7SD2: LED 7 Segment Display
L7SD3: LED 7 Segment Display (right most display)
iUart 寄存器与oUart 寄存器配合使用。 iUart 寄存器中提供缓冲字节传输和接收的 FIFO 的状态以及任何接收到的字节。
+-------------------------------------------------------------------------------+
| 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 Full
TFEM: UART TX FIFO Empty
RFFL: UART RX FIFO Full
RFEM: UART RX FIFO Empty
RXDI: UART RX Data Input
iVT100 寄存器与 oVT100 寄存器配合使用。 iVT100 寄存器中提供缓冲字节传输和接收的 FIFO 的状态以及任何接收到的字节。它的工作原理与 iUart/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 Full
TFEM: VGA VT100 TX FIFO Empty
RFFL: PS2 VT100 RX FIFO Full
RFEM: PS2 VT100 RX FIFO Empty
ACHR: New character available on PS2 Keyboard
该寄存器包含定时器计数器的当前值。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| X | X | X | TCNT |
+-------------------------------------------------------------------------------+
TCNT: Timer Counter Value
iSwitches 包含来自多个源的输入线。这些按钮(BUP、BDWN、BLFT、BRGH 和 BCNT)对应于 Nexys3 板上的方向键。开关 (TSWI) 是 oLed 中提到的开关,每个开关旁边都有一个 LED。
开关和按钮已在硬件中进行去抖处理,因此从这些寄存器读入后无需进一步处理。
+-------------------------------------------------------------------------------+
| 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: Button Up
BDWN: Button Down
BLFT: Button Left
BRGH: Button Right
BCNT: Button Center
TSWI: Two Position Switches
存储器输入,来自 SRAM 或闪存,由 oMemControl 和 oMemAddrLow 索引。当从闪存读取时,这实际上可能是状态信息或来自查询表的信息。
+-------------------------------------------------------------------------------+
| 15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+-------------------------------------------------------------------------------+
| Data Input |
+-------------------------------------------------------------------------------+
定义了以下中断服务例程:
姓名 | 数字 | 描述 |
---|---|---|
没有 | 0 | 未使用 |
isrRxFifo不为空 | 1 | UART RX FIFO 不为空 |
isrRxFifoFull | 2 | UART RX FIFI 已满 |
isrTxFifoNotEmpty | 3 | UART TX FIFO 不为空 |
isrTxFifoFull | 4 | UART TX FIFO 已满 |
isrKbd新 | 5 | 新 PS/2 键盘字符 |
定时器 | 6 | 定时器计数器 |
isrDPad按钮 | 7 | 任意方向键按钮更改状态 |
当发生中断并且在处理器内启用中断时,就会执行对内存中位置的调用 - 该位置与 ISR 编号相同。例如,编号为“4”的 ISR 将执行对内存中位置“4”的调用(而不是跳转)。
中断在执行之前至少有 4-5 个周期的延迟,中断请求处理程序中有两到三个周期的延迟,然后必须调用内存中的 ISR 位置,然后调用实现 ISR 本身的字。
如果两个中断同时发生,则按照从最低中断号到最高中断号的顺序进行处理。
当发生相同编号且尚未处理的中断时,中断将丢失。
H2 的反汇编程序和基于 C 的模拟器位于单个程序中(请参阅 h2.c)。该模拟器是对 VHDL 测试平台 tb.vhd 的补充,而不是替代它。元编译器在 eForth 解释器之上运行,它包含在文件 embed.c 和 embed.blk 中。元编译器(交叉编译器的 Forth 说法)是一个 Forth 程序,用于创建在目标上运行的 eForth 映像。
该工具链目前正在不断变化,未来 h2.c 和 embed.c 之间可能会有更多集成,同时将 Embed 虚拟机更改为更类似于 H2 CPU 的虚拟机,其长期目标是创建自托管系统。
要构建两者,需要一个 C 编译器,构建目标“h2”将构建可执行文件,h2,“embed”将构建元编译器:
make h2 embed
它可以使用 make 目标在源文件 embed.fth 上运行:
make run
不需要 make 文件:
Linux:
cc -std=c99 h2.c -o h2 # To build the h2 executable
cc -std=c99 embed.c -o embed # To build the embed VM executable
./embed embed.blk embed.hex embed.fth # Create the target eForth image
./h2 -h # For a list of options
./h2 -r embed.hex # Run the assembled file
Windows:
gcc -std=c99 h2.c -o h2.exe # Builds the h2.exe executable
gcc -std=c99 embed.c -o embed.exe # Builds the embed.exe executable
embed.exe embed.blk embed.hex embed.fth # Create the target eForth iamge
h2.exe -h # For a list of options
h2.exe -r embed.hex # Run the assembled file
可用的命令行选项列表:
- stop processing options, following arguments are files
-h print a help message and exit
-v increase logging level
-d disassemble input files (default)
-D full disassembly of input files
-T Enter debug mode when running simulation
-r run hex file
-L # load symbol file
-s # number of steps to run simulation (0 = forever)
-n # specify NVRAM block file (default is nvram.blk)
file* file to process
该程序是在 MIT 许可下发布的,请随意使用并根据需要进行修改。通过最小的修改,它应该能够为原始 J1 内核汇编程序。
元编译器运行在嵌入虚拟机之上,它是一个最初源自 H2 CPU 的 16 位虚拟机。该项目包括一个元编译方案,允许 eForth 图像生成经过修改的新 eForth 图像。该系统已适应 H2 的使用,取代了用 C 编写的交叉编译器,从而可以创建 H2 的第一个映像。
元编译器是一个普通的 Forth 程序,它包含在 embed.fth 中。然后使用元编译器 Forth 程序构建能够在 H2 目标上运行的 eForth 映像。
有关 Forth 中元编译的更多信息,请参阅:
反汇编程序获取包含汇编程序的文本文件,该文件由 16 位十六进制数字组成。然后它尝试反汇编指令。它还可以提供一个符号文件,该符号文件可以由汇编器生成,并尝试找到跳转和调用指向的位置。
GTKwave 调用的 tcl 脚本使用反汇编器,它将 H2 的指令跟踪从一系列数字转换为它们代表的指令和分支目的地。这使得 VHDL 的调试变得更加容易。
紫色痕迹显示了反汇编的指令。
C 语言的模拟器实现了 H2 内核和大部分 SoC。模拟器的 IO 不是周期精确的,但可用于运行和调试程序,其结果与硬件的行为非常相似。这比重建用于闪存 FPGA 的位文件要快得多。
该模拟器还包括一个调试器,其设计类似于 DOS 中的 DEBUG.COM 程序。调试器可用于反汇编内存部分、检查外设的状态并将内存部分转储到屏幕。它还可用于设置断点、单步执行并运行代码直到命中断点。
要运行调试器,必须提供十六进制文件或源文件:
# -T turns debugging mode on
./h2 -T -r file.hex # Run simulator
两种操作模式都可以通过符号文件进行扩展,该文件列出了变量、标签和函数在组装核心中的位置。
当给出“-T”选项时,将在执行模拟之前进入调试模式。应出现提示,命令行应如下所示:
$ ./h2 -T -R h2.fth
Debugger running, type 'h' for a list of command
debug>
断点可以通过符号方式设置,也可以通过程序位置设置,“b”命令用于设置断点:
数字可以八进制(数字前缀为“0”)、十六进制(前缀为“0x”)或十进制形式输入。例如,以下三个调试命令都在同一位置设置断点:
debug> b 16
debug> b 0x10
debug> b 020
'k' 可用于列出当前设置的断点:
debug> k
0x0010
这会在函数“key?”时设置断点。称为:
debug> b key?
函数和标签都可以停止,这需要在命令行上指定符号文件,或者汇编并运行以在源文件(而不是十六进制文件)上使用。符号文件可用于源文件或十六进制文件。
要单步执行,可以使用 's' 命令,但如果关闭跟踪(默认情况下跟踪是关闭的),则不会发生太多情况。可以使用“t”命令打开或关闭跟踪:
debug> s
debug> s
debug> t
trace on
debug> s
0001: pc(089a) inst(4889) sp(0) rp(0) tos(0000) r(0000) call 889 init
debug> s
0002: pc(0889) inst(807a) sp(0) rp(1) tos(0000) r(089b) 7a
debug> s
0003: pc(088a) inst(e004) sp(1) rp(1) tos(007a) r(089b) 6004
建议在运行时发出“c”或继续命令时关闭跟踪。
这 '。'命令可用于显示 H2 内核内部状态:
debug> .
Return Stack:
0000: 0000 08aa 0883 017b 0000 031b 0000 ffb0 0000 02eb ffb5 0210 0167 0167
0167 0167
0010: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000
Variable Stack:
tos: 0000
0001: 0000 0000 0000 0001 0004 0005 0000 ffb0 0000 0000 0000 0000 0000 0000
0000 0000
0011: 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000
0000 0000
pc: 0538
rp: 0001
dp: 0000
ie: false
'p'命令可用于显示模拟外设的状态:
debug> p
LEDS: 00
VGA Cursor: 0005
VGA Control: 007a
Timer Control: 8032
Timer: 001b
IRC Mask: 0000
UART Input: 6c
LED 7seg: 0005
Switches: 00
LFSR: 40ba
Waiting: false
有关命令的完整列表,请使用“h”命令。
进入调试模式的其他方法包括将“.break”汇编程序指令放入源代码中(这仅在源文件上使用汇编和运行命令时有效,而不是在十六进制文件上使用),以及在模拟器运行时点击转义字符。尝试通过模拟 UART 或 PS/2 键盘读取数据(转义仍将传递到模拟器,但它也会激活调试模式)。
可以在Linux和Windows下编译、测试单独的程序。这模拟了 SoC 所连接的 Nexys3 板外设,但提供了图形环境,与命令行实用程序不同。与设备交互并查看其正在执行的操作更容易,但调试会话的控制较少。它需要自由的过剩。
下面是 GUI 模拟器中正在运行的会话的图像:
建筑可以通过
make gui
并运行:
make gui-run
或者:
./gui h2.hex (on Linux)
gui.exe h2.hex (on Windows)
当系统上安装了免费 glut 的开发包时,Linux 构建应该可以工作,Windows 构建可能需要更改构建系统和/或手动安装编译器、库和标头。
当前的键位图是:
Up Activate Up D-Pad Button, Release turns off
Down Activate Down D-Pad Button, Release turns off
Left Activate Left D-Pad Button, Release turns off
Right Activate Right D-Pad Button, Release turns off
F1 - F8 Toggle Switch On/Off, F1 is left most, F8 Right Most
F11 Toggle UART/PS2 Keyboard Input
F12 Toggle Debugging Information
Escape Quit simulator
所有其他键盘按键都重定向到 UART 或 PS/2 键盘输入。
可以单击开关和方向键按钮将其打开,左键单击打开开关,右键单击关闭开关。单击顶部的方向键按钮即可打开,在屏幕上的任意位置释放按键即可关闭。
该系统中使用的 VHDL 组件被设计为可在不同的工具链和供应商之间重复使用和移植。硬件组件(例如块 RAM)是推断出来的,而不是显式实例化的。这些组件也被设计得尽可能通用,其中大多数都具有可选的宽度。这可能会走向极端,但不幸的是许多供应商仍然不支持VHDL-2008标准。
文件 | 执照 | 作者 | 描述 |
---|---|---|---|
实用工具.vhd | 麻省理工学院 | 理查德·J·豪 | 通用组件的集合 |
h2.vhd | 麻省理工学院 | 理查德·J·豪 | H2第四个CPU核心 |
串口vhd | 麻省理工学院 | 理查德·J·豪 | UART TX/RX(运行时可定制) |
vga.vhd | LGPL 3.0 | 哈维尔·V·加西亚 | 文本模式 VGA 80x40 显示屏 |
理查德·J·豪 | (和 VT100 终端仿真器) | ||
KBD文件 | ??? | 斯科特·拉尔森 | PS/2 键盘 |
上面描述了用作汇编程序的类伪 Forth 语言,实际在 Forth 核心上运行的应用程序本身就是一个 Forth 解释器。本节介绍在 H2 Core 上运行的 Forth 解释器,它包含在 embed.fth 中。
待办事项:
整个项目使用了多种语言,所有这些语言都彼此截然不同,并且需要自己的一套编码标准和风格指南。
常用信号名称:
clk - The system clock
rst - A reset signal for the module
we - Write Enable
re - Read Enable
di - Data In
din - Data In
do - Data Out
dout - Data Out
control - Generally an input to a register, the documentation
for the module will need to be consulted to find out
what each bit means
signal_we - The write enable for 'signal'
signal_i - This is an input signal
signal_o - This is an output signal
一般来说,不使用“_i”和“_o”后缀,模块保持简短,并且选择名称,以便其含义显而易见。一旦项目发展,这条规则可能会被重新审视。
组件应该:
constant N: positive := 4;
signal a: std_logic_vector(N - 1 downto 0) := (others => '1');
而不是:
signal a: std_logic_vector(3 downto 0) := x"F";
样式规则如下:
格式化指南的示例,描述了一个简单的任意宽度寄存器:
-- Lots of comments about what the unit does should go
-- here. Describe the waveforms, states and use ASCII
-- art where possible.
library ieee, work;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all; -- numeric_std not std_logic_arith
entity reg is -- generic and port indented one tab, their parameters two
generic (
N: positive); -- Generic parameters make for a generic component
port (
clk: in std_logic; -- standard signal names
rst: in std_logic; --
we: in std_logic;
di: in std_logic_vector(N - 1 downto 0);
do: out std_logic_vector(N - 1 downto 0)); -- note the position of ");
end entity; -- "end entity", not "end reg"
architecture rtl of reg is
signal r_c, r_n: std_logic_vector(N - 1 downto 0) := (others => '0');
begin
do <= r_c;
process(rst, clk)
begin
if rst = '1' then -- asynchronous reset
r_c <= (others => '0');
elsif rising_edge(clk) then -- rising edge, not "clk'event and clk = '1'"
r_c <= r_n;
end if;
end process;
process(r_c, di, we)
begin
r_n <= r_c;
if we = '1' then
r_n <= di;
end if;
end process;
end; -- "end" or "end architecture"
该项目中使用了相当多的C代码,用于为H2核心制作工具链并模拟系统。
这里的 C 代码没有什么太令人惊讶的,因此应该处理一些异常。
static const char *alu_op_to_string(uint16_t instruction) {
/* notice also that the 'case' clauses are inline with the
* switch selector */
switch (ALU_OP(instruction)) {
case ALU_OP_T: return "T";
case ALU_OP_N: return "N";
case ALU_OP_T_PLUS_N: return "T+N";
case ALU_OP_T_AND_N: return "T&N";
case ALU_OP_T_OR_N: return "T|N";
case ALU_OP_T_XOR_N: return "T^N";
case ALU_OP_T_INVERT: return "~T";
case ALU_OP_T_EQUAL_N: return "N=T";
case ALU_OP_N_LESS_T: return "T>N";
case ALU_OP_N_RSHIFT_T: return "N>>T";
case ALU_OP_T_DECREMENT: return "T-1";
case ALU_OP_R: return "R";
case ALU_OP_T_LOAD: return "[T]";
case ALU_OP_N_LSHIFT_T: return "N<N";
case ALU_OP_ENABLE_INTERRUPTS: return "seti";
case ALU_OP_INTERRUPTS_ENABLED: return "iset?";
case ALU_OP_RDEPTH: return "rdepth";
case ALU_OP_T_EQUAL_0: return "0=";
case ALU_OP_CPU_ID: return "cpu-id";
default: return "unknown";
}
}
if (foo)
bar();
else
baz();
picocom --omap delbs -b 115200 -e b /dev/ttyUSB1