ΠΡ ΡΠΎΠ·Π΄Π°Π΄ΠΈΠΌ ΡΠΌΡΠ»ΡΡΠΎΡ 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() {}