SkillAgentSearch skills...

ComputerFromScratch

Building a computer from scratch using Logisim. Starting from basic logic gates, this project progressively constructs an Arithmetic Logic Unit (ALU) and aims to eventually create a simple CPU. Follow along as each component is designed, tested, and integrated—truly from zero to a functioning computer.

Install / Use

/learn @build-with-zou/ComputerFromScratch
About this skill

Quality Score

0/100

Category

Design

Supported Platforms

Universal

README

#ComputerFromScratch

从零开始搭建的 8 位 CPU 项目

欢迎来到我的数字电路学习项目!本项目使用 Logisim 仿真软件,从最基础的晶体管和门电路开始,一步步搭建了一个完整的、可编程的 8 位 CPU(中央处理器)。整个过程中,我坚持“自底向上、全自制”的原则,所有模块(包括与门、或门、加法器、寄存器、程序计数器、指令寄存器、控制器等)均由底层门电路手工搭建,最终成功让这个 CPU 能够自动执行存储在 ROM 中的程序。

⚠️ 关于 ROM 实现的说明

本项目最初尝试完全自底向上地构建 ROM,计划使用 256 选 1 的多路选择器(由 16 选 1 选择器树形级联而成)来模拟 256×8 位的存储阵列。然而,由于 Logisim 仿真软件的性能限制,这种大规模的门级电路在实际仿真中会出现严重卡顿,导致无法正常工作。因此,为了确保项目的可用性,最终采用了 Logisim 内置的 ROM 元件,其内部原理(地址译码 + 存储阵列)与自制方案完全一致。

理论上,完全可以使用 16 选 1 选择器级联实现 256 选 1 的 ROM,且这一过程在理解上非常有价值。 如果您有兴趣或找到了优化仿真性能的方法(例如通过减少元件数量、调整仿真参数等),非常欢迎提交 Pull Request 来改进本项目的实现,或者分享您的优化经验!


这个 README 文档将详细介绍项目的背景、结构、模块功能、使用方法以及扩展方向,希望能为同样对计算机底层原理感兴趣的朋友提供参考。


一、项目简介

本项目实现了一个 8 位 CPU,采用经典的冯·诺依曼架构,具备以下特性:

  • 数据总线宽度:8 位
  • 地址总线宽度:8 位(可寻址 256 个存储单元)
  • 指令集:16 条指令,包括算术运算、逻辑运算、移位、比较、数据传输和程序控制
  • 核心部件
    • ALU(算术逻辑单元):支持 16 种运算
    • 累加器(ACC):8 位寄存器,直接放在了顶层模块CPU中,存放 ALU 的操作数和结果
    • 程序计数器(PC):8 位寄存器,可自增、可加载跳转地址
    • 指令存储器(ROM):256 × 8 位,存放用户程序
    • 指令寄存器(IR):8 位寄存器,暂存当前指令

整个 CPU 由用户亲手搭建的门电路和触发器构成,不依赖 Logisim 内置的高级元件(仅 ROM 使用了内置元件以简化仿真,但其原理已由用户理解)。


二、设计目标

  1. 理解计算机底层工作原理:从晶体管到门电路,从组合逻辑到时序逻辑,最终构建出一个能运行程序的计算机系统。
  2. 模块化与层次化:每个功能单元封装为独立的子电路,便于调试和复用。
  3. 可编程性:CPU 能够自动执行存储在 ROM 中的指令序列,支持循环和条件跳转。
  4. 贴近真实硬件:虽然采用仿真软件,但所有电路结构均参考真实 CMOS 设计(如传输门、D 触发器、异步复位等),力求原理一致。

三、开发环境

  • 软件:Logisim 2.7.1 或更高版本(推荐 Logisim-evolution,界面更友好)
  • 平台:Windows / macOS / Linux(需安装 Java 运行环境)
  • 项目文件:所有电路保存在一个 .circ 文件中,通过内部文件夹分类管理。

四、项目结构

项目在 Logisim 中采用单文件多电路的方式,按功能模块创建了以下文件夹(电路列表中的分类):

📁 00_Gates
   ├── AND        (与门,由晶体管搭建)
   ├── OR         (或门)
   ├── NOT        (非门)
   ├── XOR        (异或门)
   ├── NAND       (与非门)
   ├── NOR        (或非门)
   ├── 2to1单刀双掷 (用来解决RST的首尾相接导致的报错问题)
   ├── 8位或门     (字面意思)
   ├── 4位与门     (字面意思)
   ├── 8位2to1单刀双掷 (字面意思)
   └── 传输门      (由 NMOS 和 PMOS 构成的双向开关)

📁 01_算术单元
   ├── 半加器
   ├── 全加器
   ├── 4位加法器
   ├── 8位加法器    (行波进位结构)
   ├── 8位减法器    (补码加法实现)
   ├── 取补器
   ├── 递增器       (A+1)
   └── 递减器       (A-1)

📁 02_逻辑单元
   ├── 8位与门阵列
   ├── 8位或门阵列
   ├── 8位异或门阵列
   └── 8位非门

📁 03_移位单元
   ├── 逻辑左移
   ├── 逻辑右移
   └── 算术右移

📁 04_比较单元
   ├── 相等比较
   ├── 无符号小于
   └── 无符号大于

📁 05_选择器
   ├── 2选1选择器
   ├── 4选1选择器
   ├── 16选1选择器
   ├── 8位16选1选择器
   └── 8位输入带宽的8位16选1选择器

📁 06_译码器
   └── 4-16译码器(4位输入OP)

📁 08_时序电路
   ├── SR锁存器(或非门版)(使用了logisim自带的或非门,后续所有的锁存器都用的是D锁存器)
   ├── D锁存器(电平触发)
   ├── D触发器(主从结构,边沿触发)
   ├── 8位寄存器(带使能和异步复位)
   ├── 程序计数器PC(8位,可自增、可加载)
   ├── 传输门 (利用晶体管实现)
   ├── 递增器1(一个尝试做储存+计算的文件,后来没有出现在CPU中)
   ├── 反向输出D锁存器 (字面意思,输出和输入相反)
   └── 正在搭建的ROM+IR (PC + ROM + IR)

📁 07_顶层模块
   ├── ALU_8bit      (完整的算术逻辑单元)        
   └── CPU           整合后的中央处理器)

每个模块均可独立测试,最终在 CPU 电路中完成全部连接。


五、核心模块详解

5.1 算术逻辑单元(ALU)

  • 功能:支持 16 种运算,由 16 选 1 多路选择器选择结果输出。
  • 输入
    • A[7:0]:第一个操作数
    • B[7:0]:第二个操作数
    • OP[3:0]:操作码
  • 输出
    • Y[7:0]:运算结果
  • 支持的操作(操作码与功能对应):

| 操作码 | 功能名称 | 描述 | |--------|----------|------| | 0000 | 加法 (ADD) | A + B | | 0001 | 减法 (SUB) | A - B | | 0010 | 递增 (INC) | A + 1 | | 0011 | 递减 (DEC) | A - 1 | | 0100 | 按位与 (AND) | A & B | | 0101 | 按位或 (OR) | A | B | | 0110 | 按位异或 (XOR) | A ^ B | | 0111 | 按位取反 (NOT) | ~A | | 1000 | 逻辑左移 (LSL) | A << 1 | | 1001 | 逻辑右移 (LSR) | A >> 1 | | 1010 | 算术右移 (ASR) | A >>> 1 | | 1011 | 相等比较 (CMPEQ) | 若 A == B 输出全1,否则全0 | | 1100 | 无符号小于 (CMPLTU) | 若 A < B 输出全1,否则全0 | | 1101 | 无符号大于 (CMPGTU) | 若 A > B 输出全1,否则全0 | | 1110 | 直通 B (PASSA) | 输出 B(用于 LOAD 指令)| | 1111 | 清零 (CLR) | 输出全 0 |

5.2 累加器(ACC) (没有单独模块,仅出现在CPU中)

  • 功能:8 位寄存器,存放 ALU 的操作数和结果。
  • 输入
    • D[7:0]:来自 ALU 的结果
    • CLK:时钟(下降沿触发)
    • WE:写使能(由控制器控制)
    • RST:异步复位(高有效)
  • 输出Q[7:0],送往 ALU 的 A 输入端。

5.3 程序计数器(PC)

  • 功能:8 位寄存器,指向下一条指令的地址。每个时钟周期自动加 1,遇到跳转指令时可加载新地址。
  • 输入
    • CLK:时钟(下降沿触发)
    • WE:写使能(通常接高电平)(在最后的实现中为了方便我直接接了电源,但留有接口可以接入信号)
    • RST:复位
    • JumpAddr[7:0]:跳转地址(来自指令的低 4 位经零扩展)
    • PC_src:选择信号(0 = PC+1,1 = JumpAddr)
  • 输出Q[7:0],送往 ROM 的地址输入端。

5.4 指令存储器(ROM)

  • 功能:存放用户程序,地址 8 位,数据 8 位。采用 Logisim 内置 ROM 元件,通过十六进制编辑器填入指令。
  • 输入A[7:0](来自 PC)
  • 输出D[7:0](指令码),送往指令寄存器。

5.5 指令寄存器(IR)

  • 功能:暂存当前执行的指令,保证在整个执行周期内指令码稳定。
  • 输入
    • D[7:0]:来自 ROM
    • CLK:时钟(上升沿触发)
    • WE:写使能(调试时可常高)
    • RST:复位
  • 输出Q[7:0],高 4 位送控制器和 ALU 操作码,低 4 位扩展后输入回PC。

5.6 控制器

  • 功能:根据指令的高 4 位产生控制信号。
  • 输入Op[3:0](来自 IR 的高 4 位)
  • 输出
    • PC_src:当指令为 JUMP(操作码 1101)时为 1,否则为 0
    • ACC_we:当指令不是 JUMP 时为 1,否则为 0(即 ACC_we = NOT PC_src
  • 实现方式:用一个 4 输入与门检测 Op == 1101,与门输出直接作为 PC_src,再经非门得到 ACC_we

5.7 时钟与复位

  • 时钟:采用手动按钮或连续脉冲。为简化调试,使用手动时钟按钮产生上升沿和下降沿。
  • 复位:所有寄存器(PC、IR、ACC)的复位端连在一起,由一个输入接口控制。

六、指令集与编码

指令长度为 8 位,格式为 [操作码(4位)] [立即数/地址(4位)]

| 操作码 | 指令名 | 功能说明 | 低 4 位含义 | |--------|--------|----------|-------------| | 0000 | ADD | ACC = ACC + 立即数 | 立即数 | | 0001 | SUB | ACC = ACC - 立即数 | 立即数 | | 0010 | INC | ACC = ACC + 1 | 忽略(填0) | | 0011 | DEC | ACC = ACC - 1 | 忽略 | | 0100 | AND | ACC = ACC & 立即数 | 立即数 | | 0101 | OR | ACC = ACC | 立即数 | 立即数 | | 0110 | XOR | ACC = ACC ^ 立即数 | 立即数 | | 0111 | NOT | ACC = ~ACC | 忽略 | | 1000 | LSL | ACC = ACC << 1 | 忽略 | | 1001 | LSR | ACC = ACC >> 1 | 忽略 | | 1010 | ASR | ACC = ACC >>> 1 | 忽略 | | 1011 | CMPEQ | 若 ACC == 立即数,输出全1,否则全0 | 立即数 | | 1100 | CMPLTU | 若 ACC < 立即数,输出全1,否则全0 | 立即数 | | 1101 | JUMP | PC = 立即数(零扩展为 8 位地址) | 跳转地址 | | 1110 | LOAD | ACC = 立即数(直通 B) | 立即数 | | 1111 | CLR | ACC = 0 | 忽略 |


七、如何使用

7.1 打开项目

  1. 下载并安装 Logisim(建议使用 Logisim-evolution)。
  2. 打开项目文件 CPU.circ(或你保存的顶层电路)。

7.2 编写程序并写入 ROM

  1. 双击打开 ROM 元件(位于 CPU 电路中)。
  2. 在弹出的十六进制编辑器中,按地址填入你需要的指令(十六进制数)。例如测试程序:
    00: E1   (LOAD 1)
    01: 02   (ADD 2)
    02: 03   (ADD 3)
    03: D3   (JUMP 3)
    其余地址填 00
    
  3. 关闭编辑器,内容自动保存。

7.3 仿真运行

  1. 使用 复位按钮(RST)将所有寄存器清零(按下后释放)。
  2. 使用 手动时钟按钮 逐步产生脉冲:
    • 先按一下产生上升沿(IR 锁存指令)
    • 再按一下产生下降沿(PC 更新,ACC 写入结果)
    • 重复以上步骤,观察累加器和 PC 的输出 LED 变化。
  3. 也可以使用连续时钟源,但建议先用单步调试确认功能。
  4. 需要说明的一点,由于仿真软件的算力限制,在打开大型文件(如顶层的CPU)时可能回在RST后依然保持报错,此时可以先重置导入每一个库,然后重置模拟器,如果还是不行,重新打开文件,一般可以解决问题。

7.4 观察结果

  • 累加器(ACC)的 8 位输出接有 8 个 LED,可直接看到当前数值。
  • 程序计数器(PC)的输出也可接 LED 观察当前指令地址。
  • 如果希望看到更多细节,可以用探针(Probe)检测任意信号线。

八、示例程序及运行结果

8.1 简单加法程序

| 地址 | 指令 | 说明 | |------|------|------| | 0x00 | E1 | LOAD 1 → ACC = 1 | | 0x01 | 02 | ADD 2 → ACC = 3 | | 0x02 | 03 | ADD 3 → ACC = 6 | | 0x03 | D3 | JUMP 3 → 死循环 |

执行过程(假设复位后 ACC=0):

  • 第 1 周期:ACC = 1
  • 第 2 周期:ACC = 3
  • 第 3 周期:ACC = 6
  • 之后 ACC 保持 6,PC 在地址 3 循环。

8.2 多功能测试程序(覆盖所有指令)

这是目前ROM中存储的内容,用来检验CPU具有正常功能

使用者也可以自己改变

| 地址 | 指令 | 说明 | 执行后 ACC(十进制) | |------|------|------|----------------------| | 0x00 | E1 | LOAD 1 | 1 | | 0x01 | 02 | ADD 2 | 3 | | 0x02 | 13 | SUB 3 | 0 | | 0x03 | 42 | AND 2 | 0 | | 0x04 | 51 | OR 1 | 1 | | 0x05 | 60 | XOR 0 | 1 | | 0x06 | 70 | NOT | 254 | | 0x07 | 20 | INC | 255 | | 0x08 | 30 | DEC | 254 | | 0x09 | 80 | LSL | 252 | | 0x0A | 90 | LSR | 126 | | 0x0B | A0 | ASR | 63 | | 0x0C | B5 | CMPEQ 5 | 0(全0) | | 0x0D | C3 | CMPLTU 3 | 255(全1) | | 0x0E | F0 | CLR | 0 | | 0x0F | D0 | JUMP 0 | 0(跳回开始) |

运行该程序可验证所有指令的正确性。


九、未来扩展

这个 CPU 已经具备了完整的功能,但仍有大量扩展空间:

  1. 增加数据存储器(RAM):实现 LOADSTORE 指令,使 CPU 能处理更多变量。
  2. 扩展指令格式:改为 16 位指令(8 位操作码 + 8 位立即数/地址),支持更大地址空间和更多指令。
  3. 增加通用寄存器组:从单累加器扩展为多个寄存器(如 8 个),提升编程灵活性。
  4. 实现条件跳转:利用比较指令的结果,实现 BEQBLT 等条件跳转。
  5. 添加输入输出端口:通过专用指令读写外部设备,如开关、LED、七段数码管。
  6. 流水线设计:将取指、译码、执行阶段重叠,提高性能。
  7. 中断处理:引入中断机制,响应外部事件。
  8. 汇编器:编写一个简单的汇编器,将助记符转换为机器码,方便编程。

十、许可证与致谢

本项目为个人学习记录,采用 MIT 许可证,欢迎任何人自由使用、修改和分享。如果你基于本项目做出了有趣的东西,欢迎告知我,一起交流。

感谢 Logisim 开发团队提供了如此优秀的教学仿真软件。感谢所有在网上分享数字电路知识的老师和朋友,你们的教程给了我无数启发。


作者:一位热爱底层的大一学生
日期:2026年3月
联系方式:可通过 GitHub Issues 或邮件联系 build.with.me@gmail.com

希望这个项目能帮助更多同学理解计算机是如何工作的。如果你有任何问题或建议,欢迎随时交流!

View on GitHub
GitHub Stars7
CategoryDesign
Updated14d ago
Forks0

Security Score

70/100

Audited on Mar 19, 2026

No findings