零基础入门硬件,我花 30 天设计了一套 RISC-V 风格极简指令集 MinimaArch

2 阅读7分钟

零基础入门硬件,我花 30 天设计了一套 RISC-V 风格极简指令集 MinimaArch

项目背景

个人背景

我是 Michael-Hardy-2026,在此之前,我完全没有接触过计算机硬件。在 2025 年,我了解了 RISC-V 的设计过程后,诞生了设计自己的指令集架构(ISA)的想法。于是我开始深入学习计算机的底层知识,为接下来的设计工作奠定基础。

在 2026 年 3 月 1 日,这个 ISA 项目正式启动,采用 Apache 2.0 开源协议,命名为 MinimaArch。目前只有我一个人在推进这个 ISA 的开发。

MinimaArch 这个词源于 MinimalistArchitecture 两个词,前者表示简洁,后者表示架构。项目的核心设计理念就是简洁,但这不意味着简单,我们需要让架构保持正交性和统一性。

学习背景

在真正动手设计 MinimaArch 之前,我其实是完全零基础的硬件小白,既没有写过 Verilog,也没有系统学过计算机组成原理,更没有接触过任何一款 CPU 的微架构。

最开始只是对 RISC‑V 产生了好奇,我从最基础的资料开始看:先搞懂什么是指令集架构,什么是寄存器、指令格式、加载 / 存储体系、分支跳转逻辑。那段时间几乎每天都会抽 1–2 小时,一点点啃指令编码、操作码分配、立即数扩展这些以前完全陌生的概念。

一开始最大的困难是理解 “正交性” 到底意味着什么。我反复对比 RISC‑V 的几种指令格式,思考为什么要分成 R、I、S、B 这类格式,为什么寄存器编号要统一放在固定位置,为什么立即数要拆分编码。为了真正消化,我甚至在纸上手绘了好几版指令格式布局,不断推翻重排,直到自己觉得逻辑通顺、没有冗余。

之后我开始尝试抽象出自己的设计原则

  • 指令数量不求多,但每条都必须有明确用途
  • 寄存器布局统一,降低编译器实现难度
  • 立即数编码尽量规整,减少特殊情况
  • 控制状态寄存器(CSR)保持简单,不做过度扩展

从模糊理解,到画出第一版指令格式草案,再到整理成 YAML 规范文件,大概花了将近一个月的碎片化学习。整个过程没有老师带,也没有现成的项目可以照搬,很多设计决策都是在 “查资料→思考→推翻→重设计” 的循环里慢慢定型。

等到 2026 年 3 月,我感觉整体思路已经足够清晰,才正式建立仓库,启动 MinimaArch 项目,把之前零散的思考全部沉淀为规范文档。

项目特点

简洁设计

指令集设计遵循简洁原则,减少冗余和复杂性。每一条指令都经过精心设计,只保留最核心的功能,避免过度设计。

正交架构

指令设计保持正交性,提高代码生成效率。各指令功能独立,组合灵活,使得编译器能够更高效地生成优化代码。

统一风格

整个架构保持一致的设计风格,从指令格式到寄存器定义,都遵循统一的规范和命名约定。

开源协议

采用 Apache 2.0 开源协议,鼓励社区参与和贡献,任何人都可以自由使用、修改和分发项目代码。

技术规范

指令格式

MinimaArch 支持多种指令格式,满足不同场景的需求:

  • R 格式:32位寄存器-寄存器操作
  • R4 格式:32位四寄存器操作
  • I 格式:32位立即数操作
  • S 格式:32位存储操作
  • B 格式:32位分支操作
  • U 格式:32位高位立即数操作
  • J 格式:32位跳转操作
  • JALR 格式:32位跳转并链接寄存器操作
  • CSRI 格式:32位CSR立即数操作

指令集

当前实现的指令集包括:

  • MA32I:32位整数指令集,包含基本算术逻辑运算、加载存储、分支跳转等操作

寄存器规范

MinimaArch 定义了三类寄存器:

  • 通用寄存器 (x_regs):32个,用于整数操作
  • 浮点寄存器 (f_regs):32个,用于浮点操作
  • 向量寄存器 (v_regs):32个,用于向量操作

项目结构

MinimaArch/
├── docs/              # 项目文档
│   ├── fmt_chart.png  # 指令格式图表
│   └── why.md         # 项目背景说明
├── spec/              # 规范文件
│   ├── inst_set/      # 指令集规范
│   │   ├── fmts.yaml  # 指令格式定义
│   │   └── inst/      # 具体指令定义
│   │       └── ma32i.yaml  # MA32I指令集
│   └── reg/           # 寄存器规范
│       └── x&f&v_abi.yaml  # 寄存器ABI定义
├── license.txt        # 许可证文件
└── readme.md          # 项目说明文件

下一步

设计 MA32I 的伪指令,编写简单模拟器。

后续开始对内存模型的探究。

如何参与

  1. 查看 contributing.md 文件了解贡献指南
  2. 提交 Issue 报告 bug 或提出新功能建议

许可证

本项目采用 Apache 2.0 开源许可证,详情请查看 license.txt 文件。

联系方式


感谢阅读!如果你对 MinimaArch 项目感兴趣,欢迎关注和参与!

如果你现在就有建议或其他想法,可以直接发在评论区,你的评论是我学习并设计 MinimaArch 道路上的重要指引。

附件:MinimaArch 32 位指令的 9 种格式,以及绘制这张图的脚本(可复制

fmt_chart.png

'''
field_png_printer.py
绘制指令的位图
'''

import matplotlib.pyplot as plt
import matplotlib.patches as patches

color_alpha = 0.25
src_words = [
    {
        'name': 'R-fmt',
        'words': [
            ('funct7', 31, 25, 'purple'),
            ('rs2', 24, 20, 'blue'),
            ('rs1', 19, 15, 'green'),
            ('funct3', 14, 12, 'purple'),
            ('rd', 11, 7, 'red'),
            ('opcode', 6, 0, 'orange')
        ]
    },
    {
        'name': 'R4-fmt',
        'words': [
            ('funct2', 31, 30, 'purple'),
            ('rs3', 29, 25, 'yellow'),
            ('rs2', 24, 20, 'blue'),
            ('rs1', 19, 15, 'green'),
            ('funct3', 14, 12, 'purple'),
            ('rd', 11, 7, 'red'),
            ('opcode', 6, 0, 'orange')
        ]
    },
    {
        'name': 'I-fmt',
        'words': [
            ('imm[11:0]', 31, 20, 'pink'),
            ('rs1', 19, 15, 'green'),
            ('funct3', 14, 12, 'purple'),
            ('rd', 11, 7, 'red'),
            ('opcode', 6, 0, 'orange')
        ]
    },
    {
        'name': 'S-fmt',
        'words': [
            ('imm[11:5]', 31, 25, 'pink'),
            ('rs2', 24, 20, 'blue'),
            ('rs1', 19, 15, 'green'),
            ('funct3', 14, 12, 'purple'),
            ('imm[4:0]', 11, 7, 'pink'),
            ('opcode', 6, 0, 'orange')
        ]
    },
    {
        'name': 'B-fmt',
        'words': [
            ('imm[12]', 31, 31, 'pink'),
            ('imm[10:5]', 30, 25, 'pink'),
            ('rs2', 24, 20, 'blue'),
            ('rs1', 19, 15, 'green'),
            ('funct3', 14, 12, 'purple'),
            ('imm[4:1]', 11, 8, 'pink'),
            ('imm[11]', 7, 7, 'pink'),
            ('opcode', 6, 0, 'orange')
        ]
    },
    {
        'name': 'U-fmt',
        'words': [
            ('imm[31:12]', 31, 12, 'pink'),
            ('rd', 11, 7, 'red'),
            ('opcode', 6, 0, 'orange')
        ]
    },
    {
        'name': 'J-fmt',
        'words': [
            ('imm[20]', 31, 31, 'pink'),
            ('imm[9:1]', 30, 21, 'pink'),
            ('imm[11]', 20, 20, 'pink'),
            ('imm[19:12]', 19, 12, 'pink'),
            ('rd', 11, 7, 'red'),
            ('opcode', 6, 0, 'orange')
        ]
    },
    {
        'name': 'JALR-fmt',
        'words': [
            ('imm[12]', 31, 31, 'pink'),
            ('imm[10:1]', 30, 21, 'pink'),
            ('imm[11]', 20, 20, 'pink'),
            ('rs1', 19, 15, 'green'),
            ('funct3', 14, 12, 'purple'),
            ('rd', 11, 7, 'red'),
            ('opcode', 6, 0, 'orange')
        ]
    },
    {
        'name': 'CSRI-fmt',
        'words': [
            ('csr[11:0]', 31, 20, 'purple'),
            ('imm[4:0]', 19, 15, 'pink'),
            ('funct3', 14, 12, 'purple'),
            ('rd', 11, 7, 'red'),
            ('opcode', 6, 0, 'orange')
        ]
    }
]

def draw_field_chart(data):
    # 计算子图数量和布局
    n = len(data)
    rows = n  # 每行1个图
    cols = 1
    
    # 创建画布和子图,增加右侧空间
    fig, axs = plt.subplots(rows, cols, figsize=(10, rows * 0.8))
    axs = axs.flatten() if n > 1 else [axs]
    
    # 遍历每个指令格式
    for i, fmt in enumerate(data):
        ax = axs[i]
        
        # 收集所有字段边界
        field_boundaries_start = set()
        field_boundaries_end = set()
        for field in fmt['words']:
            _, start, end, _ = field
            field_boundaries_start.add(start)
            field_boundaries_end.add(end)
        
        # 绘制字段
        for field in fmt['words']:
            name, start, end, color = field
            width = start - end + 1
            x = end
            y = 0
            height = 0.4
            
            if name.startswith('imm'):
                name = name[3:]

            # 绘制矩形
            rect = patches.Rectangle((x, y), width, height, facecolor=color, alpha=color_alpha, edgecolor='black')
            ax.add_patch(rect)
            
            # 添加字段名称
            ax.text(x + width/2, y + height/2, name, ha='center', va='center', fontsize=8)
            
            # 在字段首尾两端的位上方添加数字
            ax.text(start+0.5, 0.5, str(start), ha='center', va='bottom', fontsize=8)
            ax.text(end+0.5, 0.5, str(end), ha='center', va='bottom', fontsize=8)
        
        # 绘制不是字段边界的位边界竖线
        for bit in range(32):
            if bit not in field_boundaries_end:
                # 绘制上下各0.02高度的竖线
                ax.plot([bit, bit], [0, 0.02], color='black', linewidth=1)
                ax.plot([bit, bit], [0.4, 0.38], color='black', linewidth=1)
        
        # 设置x轴范围和标签
        ax.set_xlim(32, 0)
        ax.set_ylim(0, 0.8)
        ax.set_xticks([])
        ax.set_yticks([])
        
        # 在右侧添加总标签
        ax.text(-0.5, 0.2, fmt['name'], ha='left', va='center', fontsize=10, fontweight='bold')
        
    # 隐藏多余的子图
    for i in range(n, len(axs)):
        axs[i].set_visible(False)
    
    # 调整布局
    plt.tight_layout()
    plt.savefig('temp/field_chart.png')
    #plt.show()

if __name__=='__main__':
    draw_field_chart(src_words)