Hotdry.

Article

从零构建FPGA计算器:BCD架构、状态机与七段显示的数字逻辑设计

深入解析FPGA科学计算器的硬件实现,涵盖自定义BCD CPU指令集、按键矩阵扫描消抖、OLED显示驱动及哈佛内存架构的工程化设计要点。

2026-05-18systems

在嵌入式系统设计中,计算器的实现通常被视为入门项目 —— 但当你决定用 FPGA 从零构建一台支持三角函数、对数运算的科学计算器时,挑战便从软件算法转向了数字逻辑与硬件架构的深层设计。本文将深入剖析一个完整 FPGA 计算器项目的硬件实现细节,涵盖输入扫描、显示驱动、自定义 CPU 指令集以及 BCD(二进制编码十进制)运算单元的工程化设计思路。

为什么选择 BCD 架构

大多数现代处理器采用二进制补码表示数值,但计算器面向的是人类习惯的十进制运算。若用标准字节寻址 CPU 处理 16 位十进制尾数,开发者将陷入不断的移位、掩码和双 nibble 拼接操作中。HP 公司在 1984 年为 HP-71B 和 HP-48 系列开发的 Saturn 处理器采用了 nibble 级 BCD 架构,每个寄存器 64 位宽(16 个 nibble),操作可直接作用于任意 nibble 字段。

借鉴这一思路,本项目设计了一款专门的 BCD 处理器:4 位 ALU 原生支持 nibble 运算,内存按 nibble 寻址,指令编码围绕 nibble 粒度访问展开。这种架构使 16 位十进制尾数的逐位遍历变得 trivial,大幅简化了算术函数的微码实现。

输入系统:按键矩阵扫描与消抖

计算器采用 7×5 的按键矩阵布局,共 35 个轻触开关。硬件扫描逻辑由 FPGA 实现:依次将每一列驱动为低电平,读取行线状态,通过上拉电阻确保未按下时行线保持高电平。

机械开关的触点弹跳是硬件设计中的经典问题。实测表明,所用 Cherry 等效 6mm 轻触开关的弹跳时间低于 5 毫秒。消抖逻辑在检测到电平变化后启动计数器,只有连续稳定超过阈值才确认有效按键。消抖时间过短会记录幽灵按键,过长则让用户感到响应迟滞 ——5 毫秒是基于实测数据的工程折中。

按键编码通过专用输入端口送入 CPU,KEYCALL指令可根据按键码直接跳转至对应的处理程序,实现高效的按键分发。

显示驱动:兼容 HD44780 的 OLED 接口

显示模块选用 Newhaven NHD-0216AW,这是一款 2 行 ×16 字符的 OLED 模组,兼容 1987 年 Hitachi 推出的 HD44780 并行接口协议。接口包含 8 位数据线、RS(寄存器选择)和 E(使能)信号。

OLED 初始化序列对时序有严格要求:控制器内部复位较慢,主机必须在特定命令间插入延迟。这些时序参数先在 ModelSim 中仿真验证,再上板测试,确保显示模块一次点亮。驱动代码通过LCDWC(写控制字)、LCDWD(写 ASCII 数据)和LCDWR(写寄存器值为十六进制数字)三条专用指令与 CPU 集成。

自定义 CPU:12 位定长指令与哈佛架构

为满足 BCD 运算的特殊需求,项目设计了一套全新的指令集架构:

指令格式:采用 12 位定长指令,恰好容纳 3 个 nibble,与系统的 nibble 导向设计理念一致。8 位指令空间受限,16 位又显浪费,12 位是精妙的折中 ——DEC 的 PDP-8 小型机(1965 年)同样采用 12 位指令和 12 位地址空间(4096 字)。

内存模型:采用哈佛架构,指令空间与数据空间完全分离。指令总线 12 位宽(4096 条指令),数据总线 4 位宽(4096 个 nibble 地址),使代码扩展不会挤占数据空间。

寄存器设计:指令编码按 nibble 划分,寄存器索引自然适配 4 位字段,理论上支持 16 个通用寄存器(R0-R15)。实际实现采用 8 个寄存器,通过 SystemVerilog 参数可在综合时选择,逻辑单元占用差异仅约 3%。8 个寄存器已满足全部微码需求。

指令集概览:包含加载 / 存储(LDM/STMLDX/STX索引寻址)、ALU 运算(14 种操作,含DAA/DAS十进制调整)、乘法(MUL,通过查表实现)、控制流(条件跳转 / 调用 / 返回)、I/O(LCD 专用指令)等。CALLI指令在微码分析后发现可节省 5.3% 的代码空间 —— 这是一个典型的 ISA 优化案例,源于对实际指令频率的测量。

BCD 运算单元与 ALU 设计

ALU 宽度为 4 位,核心挑战在于 BCD 运算的正确性。DAA(加法十进制调整)在 nibble 加法结果大于 9 时自动加 6 修正,并设置进位标志;DAS(减法十进制调整)则在减法后执行类似修正。这两条指令 borrowed directly from the 8086 processor,是 BCD 串行算法的硬件基础。

BSHR(BCD 右移)实现真正的十进制除 2 操作:当前数字除以 2,加上来自低位的半值(若进位输入为 1 则加 5)。进位输出为当前数字的最低位,传递给高位。该指令与 HP Saturn 架构的 SRB(Shift Right BCD)微原语功能等效。

内存映射与地址空间

数据地址空间布局如下:

  • 0x000-0x0FF:寄存器文件(16 个寄存器 ×16nibble 尾数)
  • 0x100-0x11F:指数区(16 个寄存器 ×2nibble)
  • 0x120-0x12F:符号记录(16 个寄存器 ×1nibble)
  • 0x130-0x13F:系统变量(显示格式、状态位等)
  • 0x140-0x209:用户存储区(STO/RCL 寄存器)
  • 0x300-0x3FF:数据栈(向下增长,0x300 为下溢保护阈值)
  • 0x400-0x5FF:常量 ROM(π、e、CORDIC 表等)
  • 0x600-0x7FF:MMIO(LED 控制、随机数生成器、按键状态)
  • 0x800-0xFFF:脚本 ROM(4 位 token 的脚本解释器)

从仿真到硬件:验证流程

设计采用迭代验证策略:每添加一条指令,同步更新汇编器、编写测试用例,通过 Verilator 编译为周期精确的 C++ 模型进行验证,确认正确后才继续。test_self_check.asm作为第一道防线,运行每条指令并检查结果,失败时触发 HALT 并输出故障地址。

硬件原型采用双板方案:EP2C5 开发板通过 40 针 IDC 排线连接按键 / 显示板。这种分离策略隔离了风险 —— 若出现问题,几乎可以确定在新设计的按键板上。实测验证了消抖时间、上拉电阻值和 OLED 初始化时序后,才进入单板集成阶段。

最终实现的计算器占用 1593 个逻辑单元,仅占小型廉价 FPGA 的 35%。整个设计证明了:在资源受限的 FPGA 上,通过精心定制的指令集和针对性的硬件架构,完全可以实现功能完备的科学计算器。


资料来源

  • Baltazar Studios. "A Calculator (5): The Hardware." baltazarstudios.com, 2026.
  • Baltazar Studios. "A Calculator (6): Designing the CPU." baltazarstudios.com, 2026.

systems

内容声明:本文无广告投放、无付费植入。

如有事实性问题,欢迎发送勘误至 i@hotdrydog.com