//! RISC-V RV64I+M instruction encoding. //! //! Provides type-safe functions for encoding RV64 instructions. use std::lang::gen; ////////////////////// // Opcode Constants // ////////////////////// export constant OP_LOAD: u32 = 0x03; export constant OP_STORE: u32 = 0x23; export constant OP_BRANCH: u32 = 0x63; export constant OP_JALR: u32 = 0x67; export constant OP_JAL: u32 = 0x6F; export constant OP_OP: u32 = 0x33; export constant OP_IMM: u32 = 0x13; export constant OP_AUIPC: u32 = 0x17; export constant OP_LUI: u32 = 0x37; export constant OP_SYSTEM: u32 = 0x73; export constant OP_OP32: u32 = 0x3B; // RV64: 32-bit operations export constant OP_IMM32: u32 = 0x1B; // RV64: 32-bit immediate operations ////////////////////// // Funct3 Constants // ////////////////////// // Memory operations export constant F3_BYTE: u32 = 0x0; // LB/SB export constant F3_HALF: u32 = 0x1; // LH/SH export constant F3_WORD: u32 = 0x2; // LW/SW export constant F3_DWORD: u32 = 0x3; // LD/SD (RV64) export constant F3_BYTE_U: u32 = 0x4; // LBU export constant F3_HALF_U: u32 = 0x5; // LHU export constant F3_WORD_U: u32 = 0x6; // LWU (RV64) // ALU operations export constant F3_ADD: u32 = 0x0; // ADD/SUB/ADDI export constant F3_SLL: u32 = 0x1; // SLL/SLLI export constant F3_SLT: u32 = 0x2; // SLT/SLTI export constant F3_SLTU: u32 = 0x3; // SLTU/SLTIU export constant F3_XOR: u32 = 0x4; // XOR/XORI export constant F3_SRL: u32 = 0x5; // SRL/SRA/SRLI/SRAI export constant F3_OR: u32 = 0x6; // OR/ORI export constant F3_AND: u32 = 0x7; // AND/ANDI // Branch operations export constant F3_BEQ: u32 = 0x0; export constant F3_BNE: u32 = 0x1; export constant F3_BLT: u32 = 0x4; export constant F3_BGE: u32 = 0x5; export constant F3_BLTU: u32 = 0x6; export constant F3_BGEU: u32 = 0x7; // CSR/system operations export constant F3_CSRRW: u32 = 0x1; export constant F3_CSRRS: u32 = 0x2; export constant F3_CSRRC: u32 = 0x3; export constant F3_CSRRWI: u32 = 0x5; export constant F3_CSRRSI: u32 = 0x6; export constant F3_CSRRCI: u32 = 0x7; ////////////////////// // Funct7 Constants // ////////////////////// export constant F7_NORMAL: u32 = 0b0000000; export constant F7_SUB: u32 = 0b0100000; // Bit 5 set export constant F7_SRA: u32 = 0b0100000; // Bit 5 set export constant F7_MUL: u32 = 0b0000001; // Bit 0 set ///////////////////////// // Validation Helpers // ///////////////////////// /// Returns `true` if the value fits in a signed 12-bit immediate. export fn isSmallImm(value: i32) -> bool { return value >= super::MIN_IMM and value <= super::MAX_IMM; } /// Returns `true` if a 64-bit value fits in a 12-bit signed immediate. export fn isSmallImm64(value: i64) -> bool { return value >= (super::MIN_IMM as i64) and value <= (super::MAX_IMM as i64); } /// Returns `true` if the value is valid for branch immediates. /// Branch immediates are 13-bit signed, even aligned. export fn isBranchImm(value: i32) -> bool { return value >= -(1 << 12) and value <= ((1 << 12) - 2) and (value & 1) == 0; } /// Returns `true` if the value is valid for jump immediates (JAL). /// Jump immediates are 21-bit signed, even aligned. export fn isJumpImm(value: i32) -> bool { return value >= -(1 << 20) and value <= ((1 << 20) - 2) and (value & 1) == 0; } ////////////////////// // Format Encoders // ////////////////////// /// Encode an R-type instruction. fn encodeR(opcode: u32, rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg, funct3: u32, funct7: u32) -> u32 { return (opcode & 0x7F) | ((*rd as u32 & 0x1F) << 7) | ((funct3 & 0x07) << 12) | ((*rs1 as u32 & 0x1F) << 15) | ((*rs2 as u32 & 0x1F) << 20) | ((funct7 & 0x7F) << 25); } /// Encode an I-type instruction. fn encodeI(opcode: u32, rd: gen::Reg, rs1: gen::Reg, funct3: u32, imm: i32) -> u32 { assert isSmallImm(imm); return (opcode & 0x7F) | ((*rd as u32 & 0x1F) << 7) | ((funct3 & 0x07) << 12) | ((*rs1 as u32 & 0x1F) << 15) | ((imm as u32 & 0xFFF) << 20); } /// Encode an S-type instruction. fn encodeS(opcode: u32, rs1: gen::Reg, rs2: gen::Reg, funct3: u32, imm: i32) -> u32 { assert isSmallImm(imm); return (opcode & 0x7F) | ((imm as u32 & 0x1F) << 7) | ((funct3 & 0x07) << 12) | ((*rs1 as u32 & 0x1F) << 15) | ((*rs2 as u32 & 0x1F) << 20) | ((imm as u32 >> 5 & 0x7F) << 25); } /// Encode a B-type (branch) instruction. fn encodeB(opcode: u32, rs1: gen::Reg, rs2: gen::Reg, funct3: u32, imm: i32) -> u32 { assert isBranchImm(imm); let imm11 = (imm as u32 >> 11) & 0x1; let imm4_1 = (imm as u32 >> 1) & 0xF; let imm10_5 = (imm as u32 >> 5) & 0x3F; let imm12 = (imm as u32 >> 12) & 0x1; return (opcode & 0x7F) | (imm11 << 7) | (imm4_1 << 8) | ((funct3 & 0x07) << 12) | ((*rs1 as u32 & 0x1F) << 15) | ((*rs2 as u32 & 0x1F) << 20) | (imm10_5 << 25) | (imm12 << 31); } /// Encode a U-type instruction. fn encodeU(opcode: u32, rd: gen::Reg, imm: i32) -> u32 { return (opcode & 0x7F) | ((*rd as u32 & 0x1F) << 7) | ((imm as u32 & 0xFFFFF) << 12); } /// Encode a J-type (jump) instruction. fn encodeJ(opcode: u32, rd: gen::Reg, imm: i32) -> u32 { assert isJumpImm(imm); let imm20 = (imm as u32 >> 20) & 0x1; let imm10_1 = (imm as u32 >> 1) & 0x3FF; let imm11 = (imm as u32 >> 11) & 0x1; let imm19_12 = (imm as u32 >> 12) & 0xFF; return (opcode & 0x7F) | ((*rd as u32 & 0x1F) << 7) | (imm19_12 << 12) | (imm11 << 20) | (imm10_1 << 21) | (imm20 << 31); } //////////////////////////// // ALU Immediate (I-type) // //////////////////////////// /// Add immediate: `rd = rs1 + imm`. export fn addi(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeI(OP_IMM, rd, rs1, F3_ADD, imm); } /// Set less than immediate (signed): `rd = (rs1 < imm) ? 1 : 0`. export fn slti(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeI(OP_IMM, rd, rs1, F3_SLT, imm); } /// Set less than immediate unsigned: `rd = (rs1 < imm) ? 1 : 0`. export fn sltiu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeI(OP_IMM, rd, rs1, F3_SLTU, imm); } /// XOR immediate: `rd = rs1 ^ imm`. export fn xori(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeI(OP_IMM, rd, rs1, F3_XOR, imm); } /// OR immediate: `rd = rs1 | imm`. export fn ori(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeI(OP_IMM, rd, rs1, F3_OR, imm); } /// AND immediate: `rd = rs1 & imm`. export fn andi(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeI(OP_IMM, rd, rs1, F3_AND, imm); } /// Shift left logical immediate: `rd = rs1 << shamt`. export fn slli(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 { assert shamt >= 0 and shamt < 64; return encodeI(OP_IMM, rd, rs1, F3_SLL, shamt & 0x3F); } /// Shift right logical immediate: `rd = rs1 >> shamt` (zero-extend). export fn srli(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 { assert shamt >= 0 and shamt < 64; return encodeI(OP_IMM, rd, rs1, F3_SRL, shamt & 0x3F); } /// Shift right arithmetic immediate: `rd = rs1 >> shamt` (sign-extend). export fn srai(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 { assert shamt >= 0 and shamt < 64; // SRAI has bit 10 set in immediate field (becomes bit 30 in instruction) return encodeI(OP_IMM, rd, rs1, F3_SRL, (shamt & 0x3F) | 0b10000000000); } /////////////////////////// // ALU Register (R-type) // /////////////////////////// /// Add: `rd = rs1 + rs2`. export fn add(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP, rd, rs1, rs2, F3_ADD, F7_NORMAL); } /// Subtract: `rd = rs1 - rs2`. export fn sub(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP, rd, rs1, rs2, F3_ADD, F7_SUB); } /// Shift left logical: `rd = rs1 << rs2[5:0]`. export fn sll(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP, rd, rs1, rs2, F3_SLL, F7_NORMAL); } /// Set less than (signed): `rd = (rs1 < rs2) ? 1 : 0`. export fn slt(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP, rd, rs1, rs2, F3_SLT, F7_NORMAL); } /// Set less than unsigned: `rd = (rs1 < rs2) ? 1 : 0`. export fn sltu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP, rd, rs1, rs2, F3_SLTU, F7_NORMAL); } /// XOR: `rd = rs1 ^ rs2`. export fn xor(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP, rd, rs1, rs2, F3_XOR, F7_NORMAL); } /// Shift right logical: `rd = rs1 >> rs2[5:0]` (zero-extend). export fn srl(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP, rd, rs1, rs2, F3_SRL, F7_NORMAL); } /// Shift right arithmetic: `rd = rs1 >> rs2[5:0]` (sign-extend). export fn sra(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP, rd, rs1, rs2, F3_SRL, F7_SRA); } /// OR: `rd = rs1 | rs2`. export fn or_(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP, rd, rs1, rs2, F3_OR, F7_NORMAL); } /// AND: `rd = rs1 & rs2`. export fn and_(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP, rd, rs1, rs2, F3_AND, F7_NORMAL); } ///////////////// // M Extension // ///////////////// /// Multiply: `rd = (rs1 * rs2)[63:0]`. export fn mul(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP, rd, rs1, rs2, F3_ADD, F7_MUL); } /// Multiply high (signed x signed): `rd = (rs1 * rs2)[127:64]`. export fn mulh(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP, rd, rs1, rs2, F3_SLL, F7_MUL); } /// Multiply high (signed x unsigned): `rd = (rs1 * rs2)[127:64]`. export fn mulhsu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP, rd, rs1, rs2, F3_SLT, F7_MUL); } /// Multiply high (unsigned x unsigned): `rd = (rs1 * rs2)[127:64]`. export fn mulhu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP, rd, rs1, rs2, F3_SLTU, F7_MUL); } /// Divide (signed): `rd = rs1 / rs2`. export fn div(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP, rd, rs1, rs2, F3_XOR, F7_MUL); } /// Divide (unsigned): `rd = rs1 / rs2`. export fn divu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP, rd, rs1, rs2, F3_SRL, F7_MUL); } /// Remainder (signed): `rd = rs1 % rs2`. export fn rem(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP, rd, rs1, rs2, F3_OR, F7_MUL); } /// Remainder (unsigned): `rd = rs1 % rs2`. export fn remu(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP, rd, rs1, rs2, F3_AND, F7_MUL); } ////////////////////////// // RV64 Word Operations // ////////////////////////// /// Add immediate word (32-bit, sign-extended): `rd = sign_ext((rs1 + imm)[31:0])`. export fn addiw(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeI(OP_IMM32, rd, rs1, F3_ADD, imm); } /// Shift left logical immediate word: `rd = sign_ext((rs1 << shamt)[31:0])`. export fn slliw(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 { assert shamt >= 0 and shamt < 32; return encodeI(OP_IMM32, rd, rs1, F3_SLL, shamt & 0x1F); } /// Shift right logical immediate word: `rd = sign_ext((rs1[31:0] >> shamt))`. export fn srliw(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 { assert shamt >= 0 and shamt < 32; return encodeI(OP_IMM32, rd, rs1, F3_SRL, shamt & 0x1F); } /// Shift right arithmetic immediate word: `rd = sign_ext((rs1[31:0] >> shamt))` (sign-extended). export fn sraiw(rd: gen::Reg, rs1: gen::Reg, shamt: i32) -> u32 { assert shamt >= 0 and shamt < 32; return encodeI(OP_IMM32, rd, rs1, F3_SRL, (shamt & 0x1F) | 0b10000000000); } /// Add word: `rd = sign_ext((rs1 + rs2)[31:0])`. export fn addw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP32, rd, rs1, rs2, F3_ADD, F7_NORMAL); } /// Subtract word: `rd = sign_ext((rs1 - rs2)[31:0])`. export fn subw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP32, rd, rs1, rs2, F3_ADD, F7_SUB); } /// Shift left logical word: `rd = sign_ext((rs1 << rs2[4:0])[31:0])`. export fn sllw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP32, rd, rs1, rs2, F3_SLL, F7_NORMAL); } /// Shift right logical word: `rd = sign_ext((rs1[31:0] >> rs2[4:0]))`. export fn srlw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP32, rd, rs1, rs2, F3_SRL, F7_NORMAL); } /// Shift right arithmetic word: `rd = sign_ext((rs1[31:0] >> rs2[4:0]))` (sign-extended). export fn sraw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP32, rd, rs1, rs2, F3_SRL, F7_SRA); } /// Multiply word: `rd = sign_ext((rs1 * rs2)[31:0])`. export fn mulw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP32, rd, rs1, rs2, F3_ADD, F7_MUL); } /// Divide word (signed): `rd = sign_ext(rs1[31:0] / rs2[31:0])`. export fn divw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP32, rd, rs1, rs2, F3_XOR, F7_MUL); } /// Divide word (unsigned): `rd = sign_ext(rs1[31:0] / rs2[31:0])`. export fn divuw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP32, rd, rs1, rs2, F3_SRL, F7_MUL); } /// Remainder word (signed): `rd = sign_ext(rs1[31:0] % rs2[31:0])`. export fn remw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP32, rd, rs1, rs2, F3_OR, F7_MUL); } /// Remainder word (unsigned): `rd = sign_ext(rs1[31:0] % rs2[31:0])`. export fn remuw(rd: gen::Reg, rs1: gen::Reg, rs2: gen::Reg) -> u32 { return encodeR(OP_OP32, rd, rs1, rs2, F3_AND, F7_MUL); } /////////////////// // Load (I-type) // /////////////////// /// Load byte (sign-extend): `rd = mem[rs1 + imm][7:0]`. export fn lb(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeI(OP_LOAD, rd, rs1, F3_BYTE, imm); } /// Load halfword (sign-extend): `rd = mem[rs1 + imm][15:0]`. export fn lh(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeI(OP_LOAD, rd, rs1, F3_HALF, imm); } /// Load word (sign-extend to 64-bit): `rd = sign_ext(mem[rs1 + imm][31:0])`. export fn lw(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeI(OP_LOAD, rd, rs1, F3_WORD, imm); } /// Load byte unsigned (zero-extend): `rd = mem[rs1 + imm][7:0]`. export fn lbu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeI(OP_LOAD, rd, rs1, F3_BYTE_U, imm); } /// Load halfword unsigned (zero-extend): `rd = mem[rs1 + imm][15:0]`. export fn lhu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeI(OP_LOAD, rd, rs1, F3_HALF_U, imm); } /// Load word unsigned (zero-extend to 64-bit): `rd = mem[rs1 + imm][31:0]`. export fn lwu(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeI(OP_LOAD, rd, rs1, F3_WORD_U, imm); } /// Load doubleword: `rd = mem[rs1 + imm][63:0]`. export fn ld(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeI(OP_LOAD, rd, rs1, F3_DWORD, imm); } //////////////////// // Store (S-type) // //////////////////// /// Store byte: `mem[rs1 + imm] = rs2[7:0]`. export fn sb(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeS(OP_STORE, rs1, rs2, F3_BYTE, imm); } /// Store halfword: `mem[rs1 + imm] = rs2[15:0]`. export fn sh(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeS(OP_STORE, rs1, rs2, F3_HALF, imm); } /// Store word: `mem[rs1 + imm] = rs2[31:0]`. export fn sw(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeS(OP_STORE, rs1, rs2, F3_WORD, imm); } /// Store doubleword: `mem[rs1 + imm] = rs2[63:0]`. export fn sd(rs2: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeS(OP_STORE, rs1, rs2, F3_DWORD, imm); } ///////////////////// // Branch (B-type) // ///////////////////// /// Branch if equal: `if (rs1 == rs2) pc += imm`. export fn beq(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { return encodeB(OP_BRANCH, rs1, rs2, F3_BEQ, imm); } /// Branch if not equal: `if (rs1 <> rs2) pc += imm`. export fn bne(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { return encodeB(OP_BRANCH, rs1, rs2, F3_BNE, imm); } /// Branch if less than (signed): `if (rs1 < rs2) pc += imm`. export fn blt(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { return encodeB(OP_BRANCH, rs1, rs2, F3_BLT, imm); } /// Branch if greater or equal (signed): `if (rs1 >= rs2) pc += imm`. export fn bge(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { return encodeB(OP_BRANCH, rs1, rs2, F3_BGE, imm); } /// Branch if less than unsigned: `if (rs1 < rs2) pc += imm`. export fn bltu(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { return encodeB(OP_BRANCH, rs1, rs2, F3_BLTU, imm); } /// Branch if greater or equal unsigned: `if (rs1 >= rs2) pc += imm`. export fn bgeu(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { return encodeB(OP_BRANCH, rs1, rs2, F3_BGEU, imm); } ////////// // Jump // ////////// /// Jump and link: `rd = pc + 4; pc += imm`. export fn jal(rd: gen::Reg, imm: i32) -> u32 { return encodeJ(OP_JAL, rd, imm); } /// Jump and link register: `rd = pc + 4; pc = rs1 + imm`. export fn jalr(rd: gen::Reg, rs1: gen::Reg, imm: i32) -> u32 { return encodeI(OP_JALR, rd, rs1, 0, imm); } ///////////////////// // Upper Immediate // ///////////////////// /// Load upper immediate: `rd = imm << 12`. export fn lui(rd: gen::Reg, imm: i32) -> u32 { return encodeU(OP_LUI, rd, imm); } /// Add upper immediate to PC: `rd = pc + (imm << 12)`. export fn auipc(rd: gen::Reg, imm: i32) -> u32 { return encodeU(OP_AUIPC, rd, imm); } //////////// // System // //////////// /// Environment call (system call). export fn ecall() -> u32 { return encodeI(OP_SYSTEM, super::ZERO, super::ZERO, 0, 0); } /// Environment break (debugger breakpoint). export fn ebreak() -> u32 { return encodeI(OP_SYSTEM, super::ZERO, super::ZERO, 0, 1); } /// Full predecessor/successor memory fence (`fence rw, rw`). export fn fence() -> u32 { return fenceOrder(3, 3); } /// Encode a CSR instruction with a register source. fn encodeCsr(op: u32, rd: gen::Reg, csr: u32, funct3: u32, rs1: gen::Reg) -> u32 { return (op & 0x7F) | ((*rd as u32 & 0x1F) << 7) | ((funct3 & 0x07) << 12) | ((*rs1 as u32 & 0x1F) << 15) | ((csr & 0xFFF) << 20); } /// Encode a CSR instruction with an immediate source. fn encodeCsrImm(op: u32, rd: gen::Reg, csr: u32, funct3: u32, imm: u32) -> u32 { assert imm < 32; return (op & 0x7F) | ((*rd as u32 & 0x1F) << 7) | ((funct3 & 0x07) << 12) | ((imm & 0x1F) << 15) | ((csr & 0xFFF) << 20); } /// Read CSR into `rd`. export fn csrr(rd: gen::Reg, csr: u32) -> u32 { return encodeCsr(OP_SYSTEM, rd, csr, F3_CSRRS, super::ZERO); } /// Read/write CSR: old CSR to `rd`, write `rs1`. export fn csrrw(rd: gen::Reg, csr: u32, rs1: gen::Reg) -> u32 { return encodeCsr(OP_SYSTEM, rd, csr, F3_CSRRW, rs1); } /// Write `rs1` into CSR and discard old value. export fn csrw(csr: u32, rs1: gen::Reg) -> u32 { return encodeCsr(OP_SYSTEM, super::ZERO, csr, F3_CSRRW, rs1); } /// Clear CSR bits from `rs1` and discard old value. export fn csrc(csr: u32, rs1: gen::Reg) -> u32 { return encodeCsr(OP_SYSTEM, super::ZERO, csr, F3_CSRRC, rs1); } /// Set CSR bits from a 5-bit immediate and discard old value. export fn csrsi(csr: u32, imm: u32) -> u32 { return encodeCsrImm(OP_SYSTEM, super::ZERO, csr, F3_CSRRSI, imm); } /// Wait for interrupt. export fn wfi() -> u32 { return 0x10500073; } /// Return from machine mode trap. export fn mret() -> u32 { return 0x30200073; } ///////////////////////// // Pseudo-instructions // ///////////////////////// /// No operation: `addi zero, zero, 0`. export fn nop() -> u32 { return addi(super::ZERO, super::ZERO, 0); } /// Move: `rd = rs` (`addi rd, rs, 0`). export fn mv(rd: gen::Reg, rs: gen::Reg) -> u32 { return addi(rd, rs, 0); } /// Bitwise NOT: `rd = ~rs` (`xori rd, rs, -1`). export fn not_(rd: gen::Reg, rs: gen::Reg) -> u32 { return xori(rd, rs, -1); } /// Negate: `rd = -rs` (`sub rd, zero, rs`). export fn neg(rd: gen::Reg, rs: gen::Reg) -> u32 { return sub(rd, super::ZERO, rs); } /// Return: `jalr zero, ra, 0`. export fn ret() -> u32 { return jalr(super::ZERO, super::RA, 0); } /// Jump (unconditional): `jal zero, imm`. export fn j(imm: i32) -> u32 { return jal(super::ZERO, imm); } /// Branch if less than or equal (signed): `if (rs1 <= rs2) pc += imm`. /// Implemented as `bge rs2, rs1, imm` (swap operands). export fn ble(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { return bge(rs2, rs1, imm); } /// Branch if greater than (signed): `if (rs1 > rs2) pc += imm`. /// Implemented as `blt rs2, rs1, imm` (swap operands). export fn bgt(rs1: gen::Reg, rs2: gen::Reg, imm: i32) -> u32 { return blt(rs2, rs1, imm); } /// Set if equal to zero: `rd = (rs == 0) ? 1 : 0`. /// Implemented as `sltiu rd, rs, 1`. export fn seqz(rd: gen::Reg, rs: gen::Reg) -> u32 { return sltiu(rd, rs, 1); } /// Set if not equal to zero: `rd = (rs <> 0) ? 1 : 0`. /// Implemented as `sltu rd, zero, rs`. export fn snez(rd: gen::Reg, rs: gen::Reg) -> u32 { return sltu(rd, super::ZERO, rs); } /// Branch if equal to zero: `if (rs == 0) pc += imm`. export fn beqz(rs: gen::Reg, imm: i32) -> u32 { return beq(rs, super::ZERO, imm); } /// Branch if not equal to zero: `if (rs <> 0) pc += imm`. export fn bnez(rs: gen::Reg, imm: i32) -> u32 { return bne(rs, super::ZERO, imm); } /// Call: `jal ra, imm`. export fn call(imm: i32) -> u32 { return jal(super::RA, imm); } /// Order the specified predecessor and successor memory or I/O access classes. /// Each mask uses I=8, O=4, R=2, W=1. export fn fenceOrder(predecessor: u32, successor: u32) -> u32 { assert predecessor <= 15 and successor <= 15; return 0x0f | (predecessor << 24) | (successor << 20); } /// Synchronize subsequent instruction fetch on the executing hart. export fn fenceI() -> u32 { return 0x0000100f; }