有趣的excel CPU

此博客围绕算法展开,但具体内容为空,推测可能会涉及算法的原理、应用等信息技术领域相关内容。

1 Excel CPU

作者链接:

https://github.com/InkboxSoftware/excelCPU

https://www.youtube.com/watch?v=MNRKi7Rum_c

最开始看到这个是觉得很神奇,用excel就可以模拟CPU,正好当时刚刚开始全面投入嵌入式,对寄存器这些也比较有兴趣。

遇到的困难主要是对excel的很多高级操作还是不熟悉,尤其是宏,搞了很久才知道是按F9运行整个程序。另外一个困难是我的PC上现在没有office,找一个D版的现在好像很困难,找了半天都没看到。只能在笔记本上运行。

运行的界面大概这样的:

可以看到,上面有fetch unit,control unit,MUX,ALU,REGISTER FILE等等。

这部分的解释如下:

在 CPU(中央处理器)内部,各个组件协同工作以执行指令和处理数据。以下是 **Fetch Unit(取指单元)、Control Unit(控制单元)、MUX(多路复用器)、ALU(算术逻辑单元)** 和 **Register File(寄存器文件)** 的核心功能及作用:

---

### **1. Fetch Unit(取指单元)**
- **功能**:  
  负责从 **内存(Memory)** 中读取下一条要执行的指令。  
- **工作流程**:  
  1. 根据 **程序计数器(PC, Program Counter)** 中的地址,从内存或指令缓存(I-Cache)获取指令。  
  2. 将指令送入 **指令译码器(Instruction Decoder)**。  
  3. 更新 **PC**(通常指向下一条指令地址,分支/跳转指令除外)。  
- **关键组件**:  
  - **PC(Program Counter)**:存储当前指令地址。  
  - **指令缓存(I-Cache)**:加速指令读取。

---

### **2. Control Unit(控制单元)**
- **功能**:  
  解析指令并生成控制信号,协调 CPU 各部件的工作。  
- **工作流程**:  
  1. 接收来自 Fetch Unit 的指令,通过 **指令译码器(Decoder)** 解析指令类型(如加法、加载、存储等)。  
  2. 生成控制信号(如 `RegWrite`、`ALUOp`、`MemRead` 等),控制数据流向和操作。  
- **关键信号示例**:  
  - `RegWrite`:是否写入寄存器文件。  
  - `ALUSrc`:ALU 的第二个操作数来源(立即数 or 寄存器)。  
  - `Branch`:是否执行分支跳转。

---

### **3. MUX(多路复用器,Multiplexer)**
- **功能**:  
  从多个输入信号中选择一个输出,类似于“数据开关”。  
- **典型应用场景**:  
  - **选择 ALU 的输入**:  
    - 输入1:寄存器文件的数据。  
    - 输入2:立即数(来自指令)。  
    - 由 `ALUSrc` 控制信号决定选择哪个输入。  
  - **选择下一条指令地址**:  
    - 正常情况:`PC + 4`(顺序执行)。  
    - 分支情况:`PC + 偏移量`(跳转执行)。  

---

### **4. ALU(算术逻辑单元,Arithmetic Logic Unit)**
- **功能**:  
  执行所有算术和逻辑运算(如加减乘除、AND/OR/XOR 等)。  
- **输入与输出**:  
  - **输入**:  
    - 操作数1:通常来自寄存器文件。  
    - 操作数2:来自寄存器文件或立即数(由 MUX 选择)。  
    - `ALUOp` 控制信号:指定运算类型(如加法、减法)。  
  - **输出**:  
    - 运算结果(写入寄存器或内存)。  
    - 标志位(如 `Zero`、`Overflow`,用于分支判断)。  

---

### **5. Register File(寄存器文件)**
- **功能**:  
  存储 CPU 当前正在使用的数据(高速、低延迟访问)。  
- **组成**:  
  - 一组寄存器(如 32 个 32 位寄存器,MIPS 架构)。  
  - 支持 **同时读取两个寄存器**(用于 ALU 操作)和 **写入一个寄存器**。  
- **关键控制信号**:  
  - `ReadReg1/ReadReg2`:指定要读取的寄存器编号。  
  - `WriteReg`:指定要写入的寄存器编号。  
  - `RegWrite`:是否允许写入数据。  

---

### **协同工作示例(一条加法指令)**
1. **Fetch Unit** 从内存读取指令 `ADD R1, R2, R3`(将 R2 + R3 存入 R1)。  
2. **Control Unit** 解析指令,生成控制信号:  
   - `ALUSrc=0`(选择寄存器文件输入)。  
   - `ALUOp=ADD`。  
   - `RegWrite=1`(允许写入 R1)。  
3. **Register File** 读取 R2 和 R3 的值,送入 ALU。  
4. **ALU** 执行加法,输出结果。  
5. **Register File** 将结果写入 R1。  

---

### **总结:CPU 数据流关键路径**
```
Fetch Unit → Control Unit → Register File → MUX → ALU → Memory/Register File
```
- **Fetch Unit**:取指令。  
- **Control Unit**:发号施令。  
- **MUX**:数据路由选择。  
- **ALU**:干脏活累活(计算)。  
- **Register File**:临时存储数据。  

这些组件共同构成 CPU 的 **经典五级流水线(Fetch-Decode-Execute-Memory-Writeback)**,是现代处理器的基础架构。

然后支持的指令如下:

好吧,内容还是挺多的,后面有时间还会再看的。。。 

2 C++模拟版本1

其实如果不想用那个excel来弄,也是完全可以直接用C++来干。

下面就是一个支持4个寄存器,R1~R3,数组模拟内存,支持一些简单指令的CPU。

LOAD reg, addr    // 把内存 addr 位置的值加载到 reg
STORE reg, addr   // 把 reg 里的值存入 addr
ADD reg1, reg2    // reg1 = reg1 + reg2
SUB reg1, reg2    // reg1 = reg1 - reg2
JMP addr          // 跳转到 addr
HALT              // 结束程序
#include <iostream>
#include <vector>
#include <unordered_map>
#include <sstream>

using namespace std;

// CPU 结构体
struct SimpleCPU {
    int registers[4] = {0};  // 4 个通用寄存器 R0-R3
    vector<string> memory;   // 指令存储
    int pc = 0;              // 程序计数器

    // 解析指令并执行
    void execute() {
        while (pc < memory.size()) {
            stringstream ss(memory[pc]);
            string op;
            ss >> op;

            if (op == "LOAD") {
                int reg, addr;
                ss >> reg >> addr;
                registers[reg] = stoi(memory[addr]);
            } 
            else if (op == "STORE") {
                int reg, addr;
                ss >> reg >> addr;
                memory[addr] = to_string(registers[reg]);
            } 
            else if (op == "ADD") {
                int r1, r2;
                ss >> r1 >> r2;
                registers[r1] += registers[r2];
            } 
            else if (op == "SUB") {
                int r1, r2;
                ss >> r1 >> r2;
                registers[r1] -= registers[r2];
            } 
            else if (op == "JMP") {
                int addr;
                ss >> addr;
                pc = addr;
                continue;  // 直接跳转
            } 
            else if (op == "HALT") {
                break;  // 终止
            } 
            else {
                cout << "未知指令: " << op << endl;
            }
            pc++;  // 继续下一条指令
        }
    }
};

int main() {
    SimpleCPU cpu;

    // 加载程序到内存
    cpu.memory = {
        "LOAD 0 10",   // R0 = memory[10]  (5)
        "LOAD 1 11",   // R1 = memory[11]  (3)
        "ADD 0 1",     // R0 = R0 + R1    (5 + 3 = 8)
        "STORE 0 12",  // memory[12] = R0
        "HALT"         // 停止
    };

    // 初始化内存
    cpu.memory.resize(20);
    cpu.memory[10] = "5";
    cpu.memory[11] = "3";

    cout << "程序开始执行...\n";
    cpu.execute();
    cout << "程序结束!\n";

    cout << "R0: " << cpu.registers[0] << endl;
    cout << "内存[12]: " << cpu.memory[12] << endl;  // 应该存 8
    return 0;
}

3 C++模拟版本2

这个版本支持CPU 5级处理。

        1️⃣ 取指 (IF: Instruction Fetch)

  • 指令存储器 取出指令
    2️⃣ 译码 (ID: Instruction Decode)

  • 解析指令,读取 寄存器
    3️⃣ 执行 (EX: Execute)

  • ALU 执行运算(加法、减法等)
    4️⃣ 访存 (MEM: Memory Access)

  • 访问 数据存储器(LOAD、STORE)
    5️⃣ 写回 (WB: Write Back)

上面的5个指令可以并行执行。代码如下:

#include <iostream>
#include <queue>
#include <vector>
#include <unordered_map>

using namespace std;

// 指令类型
enum class OpType { NOP, ADD, SUB, LOAD, STORE, HALT };

// 指令结构体
struct Instruction {
    OpType type;
    int dest, src1, src2, addr;
};

// CPU 模拟类
class PipelineCPU {
private:
    int registers[4] = {0};  // 4 个通用寄存器 R0-R3
    unordered_map<int, int> memory;  // 模拟内存
    queue<Instruction> pipeline[5];  // 5 级流水线队列
    vector<Instruction> instructions;  // 指令存储
    int pc = 0;
    bool running = true;

public:
    // 加载指令
    void loadProgram(vector<Instruction> instrs) {
        instructions = instrs;
    }

    // 运行流水线
    void run() {
        while (running || !pipeline[4].empty()) {
            writeBack();
            memoryAccess();
            execute();
            decode();
            fetch();
            printState();
        }
    }

private:
    // **阶段 1: 取指 (IF)**
    void fetch() {
        if (pc < instructions.size()) {
            pipeline[0].push(instructions[pc++]);
        }
    }

    // **阶段 2: 译码 (ID)**
    void decode() {
        if (!pipeline[0].empty()) {
            Instruction instr = pipeline[0].front();
            pipeline[0].pop();
            pipeline[1].push(instr);
        }
    }

    // **阶段 3: 执行 (EX)**
    void execute() {
        if (!pipeline[1].empty()) {
            Instruction instr = pipeline[1].front();
            pipeline[1].pop();

            if (instr.type == OpType::ADD)
                instr.dest = registers[instr.src1] + registers[instr.src2];
            else if (instr.type == OpType::SUB)
                instr.dest = registers[instr.src1] - registers[instr.src2];

            pipeline[2].push(instr);
        }
    }

    // **阶段 4: 访存 (MEM)**
    void memoryAccess() {
        if (!pipeline[2].empty()) {
            Instruction instr = pipeline[2].front();
            pipeline[2].pop();

            if (instr.type == OpType::LOAD)
                instr.dest = memory[instr.addr];
            else if (instr.type == OpType::STORE)
                memory[instr.addr] = registers[instr.dest];

            pipeline[3].push(instr);
        }
    }

    // **阶段 5: 写回 (WB)**
    void writeBack() {
        if (!pipeline[3].empty()) {
            Instruction instr = pipeline[3].front();
            pipeline[3].pop();

            if (instr.type == OpType::ADD || instr.type == OpType::SUB || instr.type == OpType::LOAD)
                registers[instr.dest] = instr.dest;

            if (instr.type == OpType::HALT)
                running = false;

            pipeline[4].push(instr);
        }
    }

    // 打印状态
    void printState() {
        cout << "PC: " << pc << " | ";
        for (int i = 0; i < 4; i++)
            cout << "R" << i << ": " << registers[i] << " | ";
        cout << endl;
    }
};

// **测试 CPU**
int main() {
    PipelineCPU cpu;

    vector<Instruction> program = {
        {OpType::LOAD, 0, 0, 0, 100}, // R0 = memory[100] (5)
        {OpType::LOAD, 1, 0, 0, 101}, // R1 = memory[101] (3)
        {OpType::ADD, 2, 0, 1, 0},    // R2 = R0 + R1 (5 + 3 = 8)
        {OpType::STORE, 2, 0, 0, 102}, // memory[102] = R2 (8)
        {OpType::HALT, 0, 0, 0, 0}    // 停机
    };

    cpu.loadProgram(program);
    cpu.run();
    return 0;
}

4 后记

最近真的精力有限,只能简单看看了。

记得之前看过pyboy模拟器,就是真的从汇编指令开始模拟的。当时叹为天人。。。每一个设备的指令集架构 (ISA, Instruction Set Architecture)都可能不同,要模拟的东西也非常多,有时候还要把它的BUG模拟处理出来。想想还真的佩服那些极客,理想当饭吃。有这些功夫干点啥挣钱的行当不行。。。

参考:

在 Excel 中构建 16 位 CPU!国外大牛极限“整活”:128KB RAM、16 色显示,还有自定义汇编...-优快云博客

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值