πŸ–₯️ Π§Π°ΡΡ‚ΡŒ 41: Эмулятор RISC-V процСссора Π½Π° ATmega328P

ΠœΡ‹ создадим эмулятор 32-Π±ΠΈΡ‚Π½ΠΎΠ³ΠΎ RISC-V процСссора, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ смоТСт Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒ скомпилированныС ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹!

Эмулятор RISC-V (подмноТСство RV32I):

cpp

#include <avr/pgmspace.h>

#define REG_COUNT 32
#define MEMORY_SIZE 4096

// БостояниС процСссора
struct RISC_V_CPU {
    uint32_t regs[REG_COUNT];
    uint32_t pc;
    uint32_t memory[MEMORY_SIZE];
    uint32_t instruction;
};

RISC_V_CPU cpu;

// ΠŸΠ°ΠΌΡΡ‚ΡŒ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌ (Π² PROGMEM)
const uint32_t program[] PROGMEM = {
    // ΠŸΡ€ΠΈΠΌΠ΅Ρ€: вычислСниС Ρ„Π°ΠΊΡ‚ΠΎΡ€ΠΈΠ°Π»Π° (5!)
    0x00500293,  // addi x5, x0, 5    ; x5 = 5
    0x00100293,  // addi x6, x0, 1    ; x6 = 1
    0x00400313,  // addi x6, x0, 4    ; x6 = 4
    0x00000000   // nop
};

// Π”Π΅ΠΊΠΎΠ΄Π΅Ρ€ инструкций
void decodeAndExecute() {
    // Π§ΠΈΡ‚Π°Π΅ΠΌ ΠΈΠ½ΡΡ‚Ρ€ΡƒΠΊΡ†ΠΈΡŽ ΠΈΠ· памяти (сначала ΠΈΠ· PROGMEM, Π·Π°Ρ‚Π΅ΠΌ ΠΈΠ· RAM)
    uint32_t instr;
    if (cpu.pc < sizeof(program)) {
        instr = pgm_read_dword(&program[cpu.pc / 4]);
    } else {
        instr = cpu.memory[cpu.pc / 4];
    }
    
    // Π”Π΅ΠΊΠΎΠ΄ΠΈΡ€ΡƒΠ΅ΠΌ opcode (Π±ΠΈΡ‚Ρ‹ 0-6)
    uint8_t opcode = instr & 0x7F;
    uint8_t rd = (instr >> 7) & 0x1F;
    uint8_t rs1 = (instr >> 15) & 0x1F;
    uint8_t rs2 = (instr >> 20) & 0x1F;
    uint8_t funct3 = (instr >> 12) & 0x07;
    uint8_t funct7 = (instr >> 25) & 0x7F;
    
    // Π’Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅
    switch (opcode) {
        case 0x13: {  // OP-IMM (Π°Ρ€ΠΈΡ„ΠΌΠ΅Ρ‚ΠΈΠΊΠ° с нСпосрСдствСнным)
            int32_t imm = (instr >> 20) & 0xFFF;
            if (imm & 0x800) imm |= 0xFFFFF000;  // Π—Π½Π°ΠΊΠΎΠ²ΠΎΠ΅ Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΠ΅
            
            switch (funct3) {
                case 0x00:  // ADDI
                    cpu.regs[rd] = cpu.regs[rs1] + imm;
                    break;
                case 0x04:  // XORI
                    cpu.regs[rd] = cpu.regs[rs1] ^ imm;
                    break;
                case 0x06:  // ORI
                    cpu.regs[rd] = cpu.regs[rs1] | imm;
                    break;
                case 0x07:  // ANDI
                    cpu.regs[rd] = cpu.regs[rs1] & imm;
                    break;
                case 0x01:  // SLLI (сдвиг влСво)
                    cpu.regs[rd] = cpu.regs[rs1] << (imm & 0x1F);
                    break;
                case 0x05:  // SRLI (сдвиг Π²ΠΏΡ€Π°Π²ΠΎ логичСский)
                    cpu.regs[rd] = cpu.regs[rs1] >> (imm & 0x1F);
                    break;
                case 0x21:  // SRAI (сдвиг Π²ΠΏΡ€Π°Π²ΠΎ арифмСтичСский)
                    cpu.regs[rd] = (int32_t)cpu.regs[rs1] >> (imm & 0x1F);
                    break;
            }
            cpu.pc += 4;
            break;
        }
        
        case 0x33: {  // OP (Π°Ρ€ΠΈΡ„ΠΌΠ΅Ρ‚ΠΈΠΊΠ° с рСгистрами)
            switch (funct3) {
                case 0x00:  // ADD / SUB
                    if (funct7 == 0x00) {
                        cpu.regs[rd] = cpu.regs[rs1] + cpu.regs[rs2];
                    } else if (funct7 == 0x20) {
                        cpu.regs[rd] = cpu.regs[rs1] - cpu.regs[rs2];
                    }
                    break;
                case 0x04:  // XOR
                    cpu.regs[rd] = cpu.regs[rs1] ^ cpu.regs[rs2];
                    break;
                case 0x06:  // OR
                    cpu.regs[rd] = cpu.regs[rs1] | cpu.regs[rs2];
                    break;
                case 0x07:  // AND
                    cpu.regs[rd] = cpu.regs[rs1] & cpu.regs[rs2];
                    break;
                case 0x01:  // SLL
                    cpu.regs[rd] = cpu.regs[rs1] << (cpu.regs[rs2] & 0x1F);
                    break;
                case 0x05:  // SRL / SRA
                    if (funct7 == 0x00) {
                        cpu.regs[rd] = cpu.regs[rs1] >> (cpu.regs[rs2] & 0x1F);
                    } else if (funct7 == 0x20) {
                        cpu.regs[rd] = (int32_t)cpu.regs[rs1] >> (cpu.regs[rs2] & 0x1F);
                    }
                    break;
            }
            cpu.pc += 4;
            break;
        }
        
        case 0x63: {  // BRANCH (условныС ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄Ρ‹)
            int32_t imm = ((instr >> 8) & 0xF) << 1;
            imm |= ((instr >> 25) & 0x3F) << 5;
            imm |= ((instr >> 7) & 0x1) << 11;
            if (imm & 0x1000) imm |= 0xFFFFF000;
            
            int8_t take = 0;
            switch (funct3) {
                case 0x00:  // BEQ
                    take = (cpu.regs[rs1] == cpu.regs[rs2]);
                    break;
                case 0x01:  // BNE
                    take = (cpu.regs[rs1] != cpu.regs[rs2]);
                    break;
                case 0x04:  // BLT
                    take = ((int32_t)cpu.regs[rs1] < (int32_t)cpu.regs[rs2]);
                    break;
                case 0x05:  // BGE
                    take = ((int32_t)cpu.regs[rs1] >= (int32_t)cpu.regs[rs2]);
                    break;
                case 0x06:  // BLTU
                    take = (cpu.regs[rs1] < cpu.regs[rs2]);
                    break;
                case 0x07:  // BGEU
                    take = (cpu.regs[rs1] >= cpu.regs[rs2]);
                    break;
            }
            
            if (take) {
                cpu.pc += imm;
            } else {
                cpu.pc += 4;
            }
            break;
        }
        
        case 0x37: {  // LUI (Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ° Π²Π΅Ρ€Ρ…Π½Π΅Π³ΠΎ нСпосрСдствСнного)
            uint32_t imm = (instr & 0xFFFFF000);
            cpu.regs[rd] = imm;
            cpu.pc += 4;
            break;
        }
        
        case 0x6F: {  // JAL (бСзусловный ΠΏΠ΅Ρ€Π΅Ρ…ΠΎΠ΄)
            int32_t imm = ((instr >> 21) & 0x3FF) << 1;
            imm |= ((instr >> 20) & 0x1) << 11;
            imm |= ((instr >> 12) & 0xFF) << 12;
            imm |= ((instr >> 31) & 0x1) << 20;
            if (imm & 0x100000) imm |= 0xFFF00000;
            
            cpu.regs[rd] = cpu.pc + 4;
            cpu.pc += imm;
            break;
        }
        
        case 0x03: {  // LOAD (Π·Π°Π³Ρ€ΡƒΠ·ΠΊΠ° ΠΈΠ· памяти)
            int32_t imm = (instr >> 20) & 0xFFF;
            if (imm & 0x800) imm |= 0xFFFFF000;
            
            uint32_t addr = cpu.regs[rs1] + imm;
            switch (funct3) {
                case 0x00:  // LB
                    cpu.regs[rd] = (int8_t)(cpu.memory[addr] & 0xFF);
                    break;
                case 0x01:  // LH
                    cpu.regs[rd] = (int16_t)(cpu.memory[addr] & 0xFFFF);
                    break;
                case 0x02:  // LW
                    cpu.regs[rd] = cpu.memory[addr];
                    break;
                case 0x04:  // LBU
                    cpu.regs[rd] = cpu.memory[addr] & 0xFF;
                    break;
                case 0x05:  // LHU
                    cpu.regs[rd] = cpu.memory[addr] & 0xFFFF;
                    break;
            }
            cpu.pc += 4;
            break;
        }
        
        case 0x23: {  // STORE (сохранСниС Π² ΠΏΠ°ΠΌΡΡ‚ΡŒ)
            int32_t imm = (instr >> 7) & 0x1F;
            imm |= ((instr >> 25) & 0x7F) << 5;
            if (imm & 0x800) imm |= 0xFFFFF000;
            
            uint32_t addr = cpu.regs[rs1] + imm;
            switch (funct3) {
                case 0x00:  // SB
                    cpu.memory[addr] = (cpu.memory[addr] & 0xFFFFFF00) | (cpu.regs[rs2] & 0xFF);
                    break;
                case 0x01:  // SH
                    cpu.memory[addr] = (cpu.memory[addr] & 0xFFFF0000) | (cpu.regs[rs2] & 0xFFFF);
                    break;
                case 0x02:  // SW
                    cpu.memory[addr] = cpu.regs[rs2];
                    break;
            }
            cpu.pc += 4;
            break;
        }
        
        default:
            // НСизвСстная инструкция - стоп
            cpu.pc += 4;
            break;
    }
}

// Π—Π°Π³Ρ€ΡƒΠ·ΠΊΠ° ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹ Π² ΠΏΠ°ΠΌΡΡ‚ΡŒ
void loadProgram(const uint32_t* prog, uint16_t size) {
    for (uint16_t i = 0; i < size; i++) {
        cpu.memory[i] = pgm_read_dword(&prog[i]);
    }
}

void setup() {
    Serial.begin(115200);
    
    // Π˜Π½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·Π°Ρ†ΠΈΡ CPU
    memset(&cpu, 0, sizeof(cpu));
    cpu.pc = 0;
    cpu.regs[2] = MEMORY_SIZE - 1;  // Π£ΠΊΠ°Π·Π°Ρ‚Π΅Π»ΡŒ стСка
    
    // Π—Π°Π³Ρ€ΡƒΠΆΠ°Π΅ΠΌ ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡƒ
    loadProgram(program, sizeof(program) / 4);
    
    // ВыполняСм 1000 инструкций
    for (uint16_t i = 0; i < 1000; i++) {
        decodeAndExecute();
    }
    
    // Π’Ρ‹Π²ΠΎΠ΄ΠΈΠΌ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ (Ρ„Π°ΠΊΡ‚ΠΎΡ€ΠΈΠ°Π» 5!)
    Serial.print("Π Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ (5!): ");
    Serial.println(cpu.regs[6]);  // Π”ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ 120
}

void loop() {}

Π’Π°ΠΌ Ρ‚Π°ΠΊΠΆΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚ ΠΏΠΎΠ½Ρ€Π°Π²ΠΈΡ‚ΡŒΡΡ

Π”ΠΎΠ±Π°Π²ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

Π’Π°Ρˆ адрСс email Π½Π΅ Π±ΡƒΠ΄Π΅Ρ‚ ΠΎΠΏΡƒΠ±Π»ΠΈΠΊΠΎΠ²Π°Π½. ΠžΠ±ΡΠ·Π°Ρ‚Π΅Π»ΡŒΠ½Ρ‹Π΅ поля ΠΏΠΎΠΌΠ΅Ρ‡Π΅Π½Ρ‹ *