diff --git a/components/fpdebug/fpdbgcommon.pas b/components/fpdebug/fpdbgcommon.pas index 950f25e6b0..6038f11c54 100644 --- a/components/fpdebug/fpdbgcommon.pas +++ b/components/fpdebug/fpdbgcommon.pas @@ -11,7 +11,7 @@ type // Target information, could be different from host debugger TMachineType = (mtNone, mtSPARC, mt386, mt68K, mtPPC, mtPPC64, mtARM, mtARM64, mtOLD_ALPHA, mtIA_64, mtX86_64, mtAVR8, mtALPHA, - mtMIPS, mtMIPSEL,mtLA64, mtXTENSA); + mtMIPS, mtMIPSEL,mtLA64, mtXTENSA, mtRISCV); TBitness = (bNone, b32, b64); TByteOrder = (boNone, boLSB, boMSB); TOperatingSystem = (osNone, osBSD, osDarwin, osEmbedded, osLinux, osUnix, osMac, osWindows); diff --git a/components/fpdebug/fpdbgdisasriscv.pas b/components/fpdebug/fpdbgdisasriscv.pas new file mode 100644 index 0000000000..90f8fbc0b9 --- /dev/null +++ b/components/fpdebug/fpdbgdisasriscv.pas @@ -0,0 +1,2867 @@ +{ $Id$ } +{ + --------------------------------------------------------------------------- + fpdbgdisasriscv.pp - Native Freepascal debugger - RISC-V Disassembler + --------------------------------------------------------------------------- + + This unit contains an RISC-V disassembler for the Native Freepascal debugger + + --------------------------------------------------------------------------- + + @created(Tue Jul 16th, 2024) + @lastmod($Date$) + @author() + + *************************************************************************** + * * + * This source is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This code is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * General Public License for more details. * + * * + * A copy of the GNU General Public License is available on the World * + * Wide Web at . You can also * + * obtain it by writing to the Free Software Foundation, * + * Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. * + * * + *************************************************************************** +} +unit FpDbgDisasRiscv; +{$mode objfpc}{$H+} +interface + +uses + SysUtils, + FpDbgUtil, FpDbgInfo, DbgIntfBaseTypes, FpdMemoryTools, {$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif}, + FpDbgClasses; + +const + RiscvABIRegisterNames: array[0..32] of string = ( + 'zero', + 'ra', + 'sp', + 'gp', + 'tp', + 't0', + 't1', + 't2', + 's0', + 's1', + 'a0', + 'a1', + 'a2', + 'a3', + 'a4', + 'a5', + 'a6', + 'a7', + 's2', + 's3', + 's4', + 's5', + 's6', + 's7', + 's8', + 's9', + 's10', + 's11', + 't3', + 't4', + 't5', + 't6', + 'pc' + ); + +type + //The function Disassemble decodes the instruction at the given address. + + TRiscvAsmDecoder = class; + + TRiscvOpCode = ( + A_INVALID, A_CUSTOM, A_RESERVED, + A_LB, A_LH, A_LW, A_LD, A_LBU, A_LHU, A_LWU, // Load instructions + A_FLH, A_FLW, A_FLD, A_FLQ, // Load floating point + A_FENCE_TSO, A_PAUSE, A_FENCE, A_FENCE_I, // fence instructions + A_CBO_INVAL, A_CBO_CLEAN, A_CBO_FLUSH, A_CBO_ZERO, // cache block operations + A_ADDI, A_SLLI, A_SLTI, A_SLTIU, A_XORI, A_SRLI, A_SRAI, A_ORI, A_ANDI, // immediate operands + A_AUIPC, A_LUI, + A_ADDIW, A_SLLIW, A_CLZW, A_CTZW, A_SRAIW, A_SRLIW, A_RORIW, A_SLLI_UW, // immediate operands - 32 bits + A_SB, A_SH, A_SW, A_SD, // Store instructions + A_FSH, A_FSW, A_FSD, A_FSQ, // Store floating point + A_LR, A_SC, A_AMOADD, A_AMOSWAP, A_AMOXOR, A_AMOAND, A_AMOOR, A_AMOMIN, A_AMOMAX, A_AMOMINU, A_AMOMAXU, // AMO + // Operand quadrant + A_ADD, A_SUB, A_SLL, A_SLT, A_SLTU, A_XOR, A_SRL, A_SRA, A_OR, A_AND, // Operand + A_MUL, A_MULH, A_MULHSU, A_MULHU, A_DIV, A_DIVU, A_REM, A_REMU, + A_CLMUL, A_BSET, A_BCLR, A_ROL, A_CLMULR, A_SH1ADD, A_CLMULH, A_PACK, A_SH2ADD, A_XNOR, + A_BINV, A_MINU, A_SH3ADD, A_BEXT, A_ROR, A_MAX, A_ORN, A_PACKH, A_MAXU, A_ANDN, + // 32 bit Operand quadrant + A_ADDW, A_SUBW, A_SLLW, A_SRLW, A_SRAW, A_MULW, A_DIVW, A_DIVUW, A_REMW, A_REMUW, + A_ADD_UW, A_ROLW, A_SH1ADD_UW, A_ZEXT_H, A_SH2ADD_UW, A_RORW, A_SH3ADD_UW, + // MADD + A_FMADD, A_FMSUB, A_FNMADD, A_FNMSUB, + A_FADD, A_FSUB, A_FMUL, A_FDIV, A_FSGNJ, A_FSGNJN, A_FSGNJX, A_FSQRT, A_FCVT_D_S, + A_FCVT_S_D, A_FMIN, A_FMAX, A_FLE, A_FLT, A_FEQ, A_FCVT_Int_S, A_FCVT_UInt_S, + A_FCVT_S_Int, A_FCVT_S_UInt, + A_BEQ, A_BNE, A_BLT, A_BGE, A_BLTU, A_BGEU, + A_JALR, A_JAL, + A_ECALL, A_EBREAK, // unprivileged instructions + A_SRET, A_MRET, A_WFI, // privileged instructions + // 16 bit specific + A_NOP, A_ILL, + // Zicsr extension + A_CSRRW, A_CSRRS, A_CSRRC, A_CSRRWI, A_CSRRSI, A_CSRRCI, + // Pseudo instructions + A_RET + ); + + TRiscvFormat = ( + rvfInstructionDestSrcOffset, // lw rd, imm(rs1) + rvfLoadFDestSrcOffset, // flw rd, imm(rs1) + rvfStoreSrc2Src1Offset, // sw rs2, imm(rs1) + rvfStoreFSrc2Src1Offset, // fsw rs2, imm(rs1) + rvfBranchSrc1Src2Imm, // beq rs1, rs2, imm + rvfInstruction, // ebreak + rvfFence, // fence [iorw], [iorw] + rvfCustom, // custom-imm + rvfInstructionSrcOffset, // cbo.clean (rs1) + rvfInstructionDestSrcImm, // addi rd, rs1, imm + rvfInstructionDestSrc1Src2, // sub rd, rs2, rs1 + rvfInstructionSrc1Src2Imm, // beq rs1, rs2, imm + rvfAmoDestSrc2Src1, // sc.w rd, rs2, rs1 + rvfAmoDestSrc1, // lr.w rd, rs1 + rvfAmoDestSrc2Src1_addr, // amoswap.w rd, rs2, (rs1) + rvfInstructionDestImm, // auipc rd, imm + rvfInstructionDestSrc, // clzw rd, rs1 + rvfFloatInstrDestSrc1, // fsqrt.s rd, rs1 + rvfFloatInstrDestSrc1Src2, // fsub.s rd, rs1, rs2 => rd = rs1 - rs2 + rvfFloatInstrDestSrc1Src2Src3, // fmadd.s rd, rs1, rs2, rs3 => (rs1 x rs2) + rs3 + rvfCSRDestCsrSrc1, // csrrw rd, #csr, rs1 + rvfCSRDestCsrImm // csrrw rd, #csr, imm + ); + + TAmoInfo = record + aquire: boolean; + release: boolean; + width: byte; // 2=h, 3=d + end; + + TRiscvInstruction = record + OpCode: TRiscvOpCode; + Size: integer; + imm: int32; + rd, rs1, rs2: byte; + format: TRiscvFormat; + case integer of + 0: (AmoInfo: TAmoInfo); + 1: (rs3, fpwidth: byte); + 2: (csr: int32); + end; + + { TRiscvDisassembler } + + TRiscvDisassembler = object + private type + // Different encoding formats used for some immediate values + TImmEncoding = (ieImm5376, ieImm54876, ieImm5326); + TImmEncoding2 = (ie2Imm5386, ie2Imm5496, ie2Imm5276); + private + procedure decodeR(instr: uint32; out funct7, rs2, rs1, funct3, rd: byte); + procedure decodeI(instr: uint32; out imm: int32; out rs1, funct3, rd: byte); + procedure decodeS(instr: uint32; out imm: int32; out rs2, rs1, funct3: byte); + procedure decodeB(instr: uint32; out imm: int32; out rs2, rs1, funct3: byte); + procedure decodeU(instr: uint32; out imm: int32; out rd: byte); + procedure decodeJ(instr: uint32; out imm: int32; out rd: byte); + function decodeLoad(instr: uint32): TRiscvInstruction; + function decodeLoadF(instr: uint32): TRiscvInstruction; + function customMap(i: integer; instr: uint32): TRiscvInstruction; + procedure decodeFence(imm: uint32; out fm, pred, succ: byte); + function decodeMiscMem(instr: uint32): TRiscvInstruction; + function decodeOpIm(instr: uint32): TRiscvInstruction; + function decodeAUIPC_LUI(instr: uint32; opcode: byte): TRiscvInstruction; + function decodeOpIm32(instr: uint32): TRiscvInstruction; + function decodeStore(instr: uint32): TRiscvInstruction; + function decodeStoreF(instr: uint32): TRiscvInstruction; + function decodeAMO(instr: uint32): TRiscvInstruction; + function decodeOp(instr: uint32): TRiscvInstruction; + function decodeOp32(instr: uint32): TRiscvInstruction; + procedure decodeR_fmt(instr: uint32; out rs3, fmt, rs2, rs1, roundingMode, rd: byte); + function decodeMAdd(instr: uint32): TRiscvInstruction; + function decodeMSub(instr: uint32): TRiscvInstruction; + function decodeNMSub(instr: uint32): TRiscvInstruction; + function decodeNMAdd(instr: uint32): TRiscvInstruction; + function decodeOP_FP(instr: uint32): TRiscvInstruction; + function decodeBranch(instr: uint32): TRiscvInstruction; + function decodeJALR(instr: uint32): TRiscvInstruction; + function decodeJAL(instr: uint32): TRiscvInstruction; + function decodeSystem(instr: uint32): TRiscvInstruction; + function formatOpcode(instr: TRiscvInstruction): string; + procedure decodeCIW(instr: uint16; out imm: uint16; out rd_: byte); + procedure decodeCLS(instr: uint16; out imm: uint16; out rs1_, rd_rs2_: byte; const immType: TImmEncoding); + procedure decodeCI(instr: uint16; out rs1_rd: byte; out imm: byte); + procedure decodeCJ(instr: uint16; out imm: int16); + procedure decodeCB(instr: uint16; out rs1_: byte; out imm: int16); + procedure decodeCSS(instr: uint16; out uimm: uint16; out rs2: byte; const immtype: TImmEncoding2); + function decodeAddi4spn(instr: uint16):TRiscvInstruction; + function decodeFLD(instr: uint16):TRiscvInstruction; + function decodeLW(instr: uint16):TRiscvInstruction; + function decodeFLW(instr: uint16):TRiscvInstruction; + function decodeFSD(instr: uint16):TRiscvInstruction; + function decodeSW(instr: uint16):TRiscvInstruction; + function decodeFSW(instr: uint16):TRiscvInstruction; + function decodeAddI(instr: uint16): TRiscvInstruction; + function decodeCJAL(instr: uint16): TRiscvInstruction; + function decodeLI(instr: uint16): TRiscvInstruction; + function decodeLUI(instr: uint16): TRiscvInstruction; + function decodeALU(instr: uint16):TRiscvInstruction; + function decodeCJ(instr: uint16): TRiscvInstruction; + function decodeCBNEQZ(instr: uint16; funct3: byte): TRiscvInstruction; + function decodeSLLI(instr: uint16): TRiscvInstruction; + function decodeFLDSP(instr: uint16): TRiscvInstruction; + function decodeLWSP(instr: uint16): TRiscvInstruction; + function decodeFLWSP(instr: uint16): TRiscvInstruction; + function decodeJR(instr: uint16): TRiscvInstruction; + function decodeCStore(instr: uint16; funct3: byte): TRiscvInstruction; + public + procedure Disassemble(var AAddress: Pointer; out AnInstruction: TRiscvInstruction); + end; + + + { TRiscvAsmInstruction } + + TRiscvAsmInstruction = class(TDbgAsmInstruction) + private const + INSTR_CODEBIN_LEN = 4; + private + FAsmDecoder: TRiscvAsmDecoder; + FAddress: TDBGPtr; + FCodeBin: array[0..INSTR_CODEBIN_LEN-1] of byte; + FFlags: set of (diCodeRead, diCodeReadError); + protected + procedure ReadCode; inline; + public + constructor Create(AAsmDecoder: TRiscvAsmDecoder); + procedure SetAddress(AnAddress: TDBGPtr); + function IsCallInstruction: boolean; override; + function IsReturnInstruction: boolean; override; + function IsLeaveStackFrame: boolean; override; + function InstructionLength: Integer; override; + end; + +{ TRiscvAsmDecoder } + + TRiscvAsmDecoder = class(TDbgAsmDecoder) + private const + MaxPrologueSize = 63; // Bytes, so ~22 instructions + MaxEpilogueSize = MaxPrologueSize; // Perhaps a bit smaller, since the locals/parameters do not have to be initialized + MAX_CODEBIN_LEN = MaxPrologueSize; // About 22 instructions + private + FProcess: TDbgProcess; + FLastErrWasMem: Boolean; + FCodeBin: array[0..MAX_CODEBIN_LEN-1] of byte; + FLastInstr: TRiscvAsmInstruction; + function FParsePrologue(AnAddress, AStartPC, AEndPC: TDBGPtr; out + returnAddressOffset, SPoffset: integer; out AnIsOutsideFrame: Boolean): Boolean; + function FParseEpilogue(AnAddress, AStartPC, AEndPC: TDBGPtr; out + returnAddressOffset: word; out AnIsOutsideFrame: Boolean): Boolean; + + function FormatInstruction(instr: TRiscvInstruction): string; + protected + function GetLastErrorWasMemReadErr: Boolean; override; + function GetMaxInstrSize: integer; override; + function GetMinInstrSize: integer; override; + function GetCanReverseDisassemble: boolean; override; + function ReadCodeAt(AnAddress: TDBGPtr; ALen: Cardinal): Boolean; inline; + public + procedure Disassemble(var AAddress: Pointer; out ACodeBytes: String; out ACode: String); override; overload; + procedure Disassemble(var AAddress: Pointer; out ACodeBytes: String; out ACode: String; out AnInfo: TDbgInstInfo); override; overload; + function GetInstructionInfo(AnAddress: TDBGPtr): TDbgAsmInstruction; override; + + // Rather use GetFunctionFrameReturnAddress + function GetFunctionFrameInfo(AnAddress: TDBGPtr; out + AnIsOutsideFrame: Boolean): Boolean; override; + + function GetBreakInstruction(const ALocation: TDBGPtr; out + BreakInstructionLength: Integer): QWord; + function BreakInstructionOffset: Int8; + + // AStartPC & AEndPC indicates proc limits to help with scanning for prologue/epilogue + function GetFunctionFrameReturnAddress(AnAddress, AStartPC, AEndPC: TDBGPtr; + out returnAddressOffset, SPoffset: integer; out AnIsOutsideFrame: Boolean): Boolean; + + constructor Create(AProcess: TDbgProcess); override; + destructor Destroy; + end; + + { TDbgStackUnwinderRiscv } + + TDbgStackUnwinderRiscv = class(TDbgStackUnwinder) + private + FThread: TDbgThread; + FProcess: TDbgProcess; + FAddressSize: Integer; + FLastFrameBaseIncreased: Boolean; + FSPchangeCount: integer; + FPreviousPC: TDBGPtr; + FCodeReadErrCnt: integer; + protected + property Process: TDbgProcess read FProcess; + property Thread: TDbgThread read FThread; + property AddressSize: Integer read FAddressSize; + public + constructor Create(AProcess: TDbgProcess); + procedure InitForThread(AThread: TDbgThread); override; + procedure InitForFrame(ACurrentFrame: TDbgCallstackEntry; out CodePointer, + StackPointer, FrameBasePointer: TDBGPtr); override; + procedure GetTopFrame(out CodePointer, StackPointer, FrameBasePointer: TDBGPtr; + out ANewFrame: TDbgCallstackEntry); override; + function Unwind(AFrameIndex: integer; var CodePointer, StackPointer, + FrameBasePointer: TDBGPtr; ACurrentFrame: TDbgCallstackEntry; out + ANewFrame: TDbgCallstackEntry): TTDbgStackUnwindResult; override; + end; + + +implementation + +uses + StrUtils, LazClasses, Math, + FpDbgRiscvClasses; + +var + DBG_WARNINGS: PLazLoggerLogGroup; + +const + RiscvOpcodeString: array[TRiscvOpCode] of string = ( + 'invalid', 'custom', 'reserved', + 'lb', 'lh', 'lw', 'ld', 'lbu', 'lhu', 'lwu', + 'flh', 'flw', 'fld', 'flq', + 'fence.tso', 'pause', 'fence', 'fence.i', + 'cbo.inval', 'cbo.clean', 'cbo.flush', 'cbo.zero', + 'addi', 'slli', 'slti', 'sltiu', 'xori', 'srli', 'srai', 'ori', 'andi', + 'auipc', 'lui', + 'addiw', 'slliw', 'clzw', 'ctzw', 'sraiw', 'srliw', 'roriw', 'slliw', + 'sb', 'sh', 'sw', 'sd', + 'fsh', 'fsw', 'fsd', 'fsq', + 'lr', 'sc', 'amoadd', 'amoswap', 'amoxor', 'amoand', 'amoor', 'amomin', 'amomax', 'amominu', 'amomaxu', + // Operand quadrant + 'add', 'sub', 'sll', 'slt', 'sltu', 'xor', 'srl', 'sra', 'or', 'and', + 'mul', 'mulh', 'mulhsu', 'mulhu', 'div', 'divu', 'rem', 'remu', + 'clmul', 'bset', 'bclr', 'rol', 'clmulr', 'sh1add', 'clmulh', 'pack', 'sh2add', 'xnor', + 'binv', 'minu', 'sh3add', 'bext', 'ror', 'max', 'orn', 'packh', 'maxu', 'andn', + // 32 bit Operand quadrant + 'addw', 'subw', 'sllw', 'srlw', 'sraw', 'mulw', 'divw', 'divuw', 'remw', 'remuw', + 'add.uw', 'rolw', 'sh1add.uw', 'zext.h', 'sh2add.uw', 'rorw', 'sh3add.uw', + // MADD + 'fmadd', 'fmsub', 'fnmadd', 'fnmsub', + 'fadd', 'fsub', 'fmul', 'fdiv', 'fsgnj', 'fsgnjn', 'fsgnnjx', 'fsqrt', 'fcvt.d.s', + 'fcvt.s.d', 'fmin', 'fmax', 'fle', 'flt', 'feq', 'fcvt.%s.s' {A_FCVT_Int_S}, 'fcvt.%s.s' {A_FCVT_UInt_S}, + 'fcvt.s.%s' {A_FCVT_S_Int}, 'fcvt.s.%s' {A_FCVT_S_UInt}, + 'beq', 'bne', 'blt', 'bge', 'bltu', 'bgeu', + 'jalr', 'jal', + 'ecall', 'ebreak', // unprivileged instructions + 'sret', 'mret', 'wfi', // privileged instructions + + // 16 bit specific + 'nop', 'ill', + // Zicsr extension + 'csrrw', 'csrrs', 'csrrc', 'csrrwi', 'csrrsi', 'csrrci', + + // Pseudo instructions + 'ret' + ); + +function CsrRegNumToName(regnum: integer): string; +begin + case regnum of + $001: Result := 'fflags'; // Floating-Point Accrued Exceptions + $002: Result := 'frm'; // Floating-Point Dynamic Rounding Mode + $003: Result := 'fcsr'; // Floating-Point Control and Status Register (frm + fflags) + $017: Result := 'jvt'; // Table jump base vector and control register + $300: Result := 'mstatus'; // Machine Status (lower 32 bits). + $301: Result := 'misa'; // Machine ISA + $304: Result := 'mie'; // Machine Interrupt Enable Register + $305: Result := 'mtvec'; // Machine Trap-Handler Base Address + $307: Result := 'mtvt'; // Machine Trap-Handler Vector Table Base Address + $310: Result := 'mstatush'; // Machine Status (upper 32 bits). + $320: Result := 'mcountinhibit'; // (HPM) Machine Counter-Inhibit Register + $323: Result := 'mhpmevent3'; // (HPM) Machine Performance-Monitoring Event Selector 3 + $324: Result := 'mhpmevent4'; // (HPM) Machine Performance-Monitoring Event Selector 4 + $325: Result := 'mhpmevent5'; // (HPM) Machine Performance-Monitoring Event Selector 5 + $326: Result := 'mhpmevent6'; // (HPM) Machine Performance-Monitoring Event Selector 6 + $327: Result := 'mhpmevent7'; // (HPM) Machine Performance-Monitoring Event Selector 7 + $328: Result := 'mhpmevent8'; // (HPM) Machine Performance-Monitoring Event Selector 8 + $329: Result := 'mhpmevent9'; // (HPM) Machine Performance-Monitoring Event Selector 9 + $32A: Result := 'mhpmevent10'; // (HPM) Machine Performance-Monitoring Event Selector 10 + $32B: Result := 'mhpmevent11'; // (HPM) Machine Performance-Monitoring Event Selector 11 + $32C: Result := 'mhpmevent12'; // (HPM) Machine Performance-Monitoring Event Selector 12 + $32D: Result := 'mhpmevent13'; // (HPM) Machine Performance-Monitoring Event Selector 13 + $32E: Result := 'mhpmevent14'; // (HPM) Machine Performance-Monitoring Event Selector 14 + $32F: Result := 'mhpmevent15'; // (HPM) Machine Performance-Monitoring Event Selector 15 + $330: Result := 'mhpmevent16'; // (HPM) Machine Performance-Monitoring Event Selector 16 + $331: Result := 'mhpmevent17'; // (HPM) Machine Performance-Monitoring Event Selector 17 + $332: Result := 'mhpmevent18'; // (HPM) Machine Performance-Monitoring Event Selector 18 + $333: Result := 'mhpmevent19'; // (HPM) Machine Performance-Monitoring Event Selector 19 + $334: Result := 'mhpmevent20'; // (HPM) Machine Performance-Monitoring Event Selector 20 + $335: Result := 'mhpmevent21'; // (HPM) Machine Performance-Monitoring Event Selector 21 + $336: Result := 'mhpmevent22'; // (HPM) Machine Performance-Monitoring Event Selector 22 + $337: Result := 'mhpmevent23'; // (HPM) Machine Performance-Monitoring Event Selector 23 + $338: Result := 'mhpmevent24'; // (HPM) Machine Performance-Monitoring Event Selector 24 + $339: Result := 'mhpmevent25'; // (HPM) Machine Performance-Monitoring Event Selector 25 + $33A: Result := 'mhpmevent26'; // (HPM) Machine Performance-Monitoring Event Selector 26 + $33B: Result := 'mhpmevent27'; // (HPM) Machine Performance-Monitoring Event Selector 27 + $33C: Result := 'mhpmevent28'; // (HPM) Machine Performance-Monitoring Event Selector 28 + $33D: Result := 'mhpmevent29'; // (HPM) Machine Performance-Monitoring Event Selector 29 + $33E: Result := 'mhpmevent30'; // (HPM) Machine Performance-Monitoring Event Selector 30 + $33F: Result := 'mhpmevent31'; // (HPM) Machine Performance-Monitoring Event Selector 31 + $340: Result := 'mscratch'; // Machine Scratch + $341: Result := 'mepc'; // Machine Exception Program Counter + $342: Result := 'mcause'; // Machine Trap Cause + $343: Result := 'mtval'; // Machine Trap Value + $344: Result := 'mip'; // Machine Interrupt Pending Register + $345: Result := 'mnxti'; // Interrupt handler address and enable modifier + $347: Result := 'mintthresh'; // Interrupt-level threshold + $349: Result := 'mscratchcswl'; // Conditional scratch swap on level change + $7A0: Result := 'tselect'; // Trigger Select Register + $7A1: Result := 'tdata1'; // Trigger Data Register 1 + $7A2: Result := 'tdata2'; // Trigger Data Register 2 + $7A4: Result := 'tinfo'; // Trigger Info + $7B0: Result := 'dcsr'; // Debug Control and Status + $7B1: Result := 'dpc'; // Debug PC + $7B2: Result := 'dscratch0'; // Debug Scratch Register 0 + $7B3: Result := 'dscratch1'; // Debug Scratch Register 1 + $B00: Result := 'mcycle'; // (HPM) Machine Cycle Counter + $B02: Result := 'minstret'; // (HPM) Machine Instructions-Retired Counter + $B03: Result := 'mhpmcounter3'; // (HPM) Machine Performance-Monitoring Counter 3 + $B04: Result := 'mhpmcounter4'; // (HPM) Machine Performance-Monitoring Counter 4 + $B05: Result := 'mhpmcounter5'; // (HPM) Machine Performance-Monitoring Counter 5 + $B06: Result := 'mhpmcounter6'; // (HPM) Machine Performance-Monitoring Counter 6 + $B07: Result := 'mhpmcounter7'; // (HPM) Machine Performance-Monitoring Counter 7 + $B08: Result := 'mhpmcounter8'; // (HPM) Machine Performance-Monitoring Counter 8 + $B09: Result := 'mhpmcounter9'; // (HPM) Machine Performance-Monitoring Counter 9 + $B0A: Result := 'mhpmcounter10'; // (HPM) Machine Performance-Monitoring Counter 10 + $B0B: Result := 'mhpmcounter11'; // (HPM) Machine Performance-Monitoring Counter 11 + $B0C: Result := 'mhpmcounter12'; // (HPM) Machine Performance-Monitoring Counter 12 + $B0D: Result := 'mhpmcounter13'; // (HPM) Machine Performance-Monitoring Counter 13 + $B0E: Result := 'mhpmcounter14'; // (HPM) Machine Performance-Monitoring Counter 14 + $B0F: Result := 'mhpmcounter15'; // (HPM) Machine Performance-Monitoring Counter 15 + $B10: Result := 'mhpmcounter16'; // (HPM) Machine Performance-Monitoring Counter 16 + $B11: Result := 'mhpmcounter17'; // (HPM) Machine Performance-Monitoring Counter 17 + $B12: Result := 'mhpmcounter18'; // (HPM) Machine Performance-Monitoring Counter 18 + $B13: Result := 'mhpmcounter19'; // (HPM) Machine Performance-Monitoring Counter 19 + $B14: Result := 'mhpmcounter20'; // (HPM) Machine Performance-Monitoring Counter 20 + $B15: Result := 'mhpmcounter21'; // (HPM) Machine Performance-Monitoring Counter 21 + $B16: Result := 'mhpmcounter22'; // (HPM) Machine Performance-Monitoring Counter 22 + $B17: Result := 'mhpmcounter23'; // (HPM) Machine Performance-Monitoring Counter 23 + $B18: Result := 'mhpmcounter24'; // (HPM) Machine Performance-Monitoring Counter 24 + $B19: Result := 'mhpmcounter25'; // (HPM) Machine Performance-Monitoring Counter 25 + $B1A: Result := 'mhpmcounter26'; // (HPM) Machine Performance-Monitoring Counter 26 + $B1B: Result := 'mhpmcounter27'; // (HPM) Machine Performance-Monitoring Counter 27 + $B1C: Result := 'mhpmcounter28'; // (HPM) Machine Performance-Monitoring Counter 28 + $B1D: Result := 'mhpmcounter29'; // (HPM) Machine Performance-Monitoring Counter 29 + $B1E: Result := 'mhpmcounter30'; // (HPM) Machine Performance-Monitoring Counter 30 + $B1F: Result := 'mhpmcounter31'; // (HPM) Machine Performance-Monitoring Counter 31 + $B80: Result := 'mcycleh'; // (HPM) Upper 32 Machine Cycle Counter + $B82: Result := 'minstreth'; // (HPM) Upper 32 Machine Instructions-Retired Counter + $B83: Result := 'mhpmcounterh3'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 3 + $B84: Result := 'mhpmcounterh4'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 4 + $B85: Result := 'mhpmcounterh5'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 5 + $B86: Result := 'mhpmcounterh6'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 6 + $B87: Result := 'mhpmcounterh7'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 7 + $B88: Result := 'mhpmcounterh8'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 8 + $B89: Result := 'mhpmcounterh9'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 9 + $B8A: Result := 'mhpmcounterh10'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 10 + $B8B: Result := 'mhpmcounterh11'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 11 + $B8C: Result := 'mhpmcounterh12'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 12 + $B8D: Result := 'mhpmcounterh13'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 13 + $B8E: Result := 'mhpmcounterh14'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 14 + $B8F: Result := 'mhpmcounterh15'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 15 + $B90: Result := 'mhpmcounterh16'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 16 + $B91: Result := 'mhpmcounterh17'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 17 + $B92: Result := 'mhpmcounterh18'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 18 + $B93: Result := 'mhpmcounterh19'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 19 + $B94: Result := 'mhpmcounterh20'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 20 + $B95: Result := 'mhpmcounterh21'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 21 + $B96: Result := 'mhpmcounterh22'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 22 + $B97: Result := 'mhpmcounterh23'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 23 + $B98: Result := 'mhpmcounterh24'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 24 + $B99: Result := 'mhpmcounterh25'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 25 + $B9A: Result := 'mhpmcounterh26'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 26 + $B9B: Result := 'mhpmcounterh27'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 27 + $B9C: Result := 'mhpmcounterh28'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 28 + $B9D: Result := 'mhpmcounterh29'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 29 + $B9E: Result := 'mhpmcounterh30'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 30 + $B9F: Result := 'mhpmcounterh31'; // (HPM) Upper 32 Machine Performance-Monitoring Counter 31 + $C00: Result := 'cycle'; // Cycle Counter + $C01: Result := 'time'; // Time + $C02: Result := 'instret'; // Instructions-Retired Counter + $C03: Result := 'hpmcounter3'; // (HPM) Performance-Monitoring Counter 3 + $C04: Result := 'hpmcounter4'; // (HPM) Performance-Monitoring Counter 4 + $C05: Result := 'hpmcounter5'; // (HPM) Performance-Monitoring Counter 5 + $C06: Result := 'hpmcounter6'; // (HPM) Performance-Monitoring Counter 6 + $C07: Result := 'hpmcounter7'; // (HPM) Performance-Monitoring Counter 7 + $C08: Result := 'hpmcounter8'; // (HPM) Performance-Monitoring Counter 8 + $C09: Result := 'hpmcounter9'; // (HPM) Performance-Monitoring Counter 9 + $C0A: Result := 'hpmcounter10'; // (HPM) Performance-Monitoring Counter 10 + $C0B: Result := 'hpmcounter11'; // (HPM) Performance-Monitoring Counter 11 + $C0C: Result := 'hpmcounter12'; // (HPM) Performance-Monitoring Counter 12 + $C0D: Result := 'hpmcounter13'; // (HPM) Performance-Monitoring Counter 13 + $C0E: Result := 'hpmcounter14'; // (HPM) Performance-Monitoring Counter 14 + $C0F: Result := 'hpmcounter15'; // (HPM) Performance-Monitoring Counter 15 + $C10: Result := 'hpmcounter16'; // (HPM) Performance-Monitoring Counter 16 + $C11: Result := 'hpmcounter17'; // (HPM) Performance-Monitoring Counter 17 + $C12: Result := 'hpmcounter18'; // (HPM) Performance-Monitoring Counter 18 + $C13: Result := 'hpmcounter19'; // (HPM) Performance-Monitoring Counter 19 + $C14: Result := 'hpmcounter20'; // (HPM) Performance-Monitoring Counter 20 + $C15: Result := 'hpmcounter21'; // (HPM) Performance-Monitoring Counter 21 + $C16: Result := 'hpmcounter22'; // (HPM) Performance-Monitoring Counter 22 + $C17: Result := 'hpmcounter23'; // (HPM) Performance-Monitoring Counter 23 + $C18: Result := 'hpmcounter24'; // (HPM) Performance-Monitoring Counter 24 + $C19: Result := 'hpmcounter25'; // (HPM) Performance-Monitoring Counter 25 + $C1A: Result := 'hpmcounter26'; // (HPM) Performance-Monitoring Counter 26 + $C1B: Result := 'hpmcounter27'; // (HPM) Performance-Monitoring Counter 27 + $C1C: Result := 'hpmcounter28'; // (HPM) Performance-Monitoring Counter 28 + $C1D: Result := 'hpmcounter29'; // (HPM) Performance-Monitoring Counter 29 + $C1E: Result := 'hpmcounter30'; // (HPM) Performance-Monitoring Counter 30 + $C1F: Result := 'hpmcounter31'; // (HPM) Performance-Monitoring Counter 31 + $C80: Result := 'cycleh'; // Upper 32 Cycle Counter + $C81: Result := 'timeh'; // Upper 32 Time + $C82: Result := 'instreth'; // Upper 32 Instructions-Retired Counter + $C83: Result := 'hpmcounter3'; // (HPM) Upper 32 Performance-Monitoring Counter 3 + $C84: Result := 'hpmcounter4'; // (HPM) Upper 32 Performance-Monitoring Counter 4 + $C85: Result := 'hpmcounter5'; // (HPM) Upper 32 Performance-Monitoring Counter 5 + $C86: Result := 'hpmcounter6'; // (HPM) Upper 32 Performance-Monitoring Counter 6 + $C87: Result := 'hpmcounter7'; // (HPM) Upper 32 Performance-Monitoring Counter 7 + $C88: Result := 'hpmcounter8'; // (HPM) Upper 32 Performance-Monitoring Counter 8 + $C89: Result := 'hpmcounter9'; // (HPM) Upper 32 Performance-Monitoring Counter 9 + $C8A: Result := 'hpmcounter10'; // (HPM) Upper 32 Performance-Monitoring Counter 10 + $C8B: Result := 'hpmcounter11'; // (HPM) Upper 32 Performance-Monitoring Counter 11 + $C8C: Result := 'hpmcounter12'; // (HPM) Upper 32 Performance-Monitoring Counter 12 + $C8D: Result := 'hpmcounter13'; // (HPM) Upper 32 Performance-Monitoring Counter 13 + $C8E: Result := 'hpmcounter14'; // (HPM) Upper 32 Performance-Monitoring Counter 14 + $C8F: Result := 'hpmcounter15'; // (HPM) Upper 32 Performance-Monitoring Counter 15 + $C90: Result := 'hpmcounter16'; // (HPM) Upper 32 Performance-Monitoring Counter 16 + $C91: Result := 'hpmcounter17'; // (HPM) Upper 32 Performance-Monitoring Counter 17 + $C92: Result := 'hpmcounter18'; // (HPM) Upper 32 Performance-Monitoring Counter 18 + $C93: Result := 'hpmcounter19'; // (HPM) Upper 32 Performance-Monitoring Counter 19 + $C94: Result := 'hpmcounter20'; // (HPM) Upper 32 Performance-Monitoring Counter 20 + $C95: Result := 'hpmcounter21'; // (HPM) Upper 32 Performance-Monitoring Counter 21 + $C96: Result := 'hpmcounter22'; // (HPM) Upper 32 Performance-Monitoring Counter 22 + $C97: Result := 'hpmcounter23'; // (HPM) Upper 32 Performance-Monitoring Counter 23 + $C98: Result := 'hpmcounter24'; // (HPM) Upper 32 Performance-Monitoring Counter 24 + $C99: Result := 'hpmcounter25'; // (HPM) Upper 32 Performance-Monitoring Counter 25 + $C9A: Result := 'hpmcounter26'; // (HPM) Upper 32 Performance-Monitoring Counter 26 + $C9B: Result := 'hpmcounter27'; // (HPM) Upper 32 Performance-Monitoring Counter 27 + $C9C: Result := 'hpmcounter28'; // (HPM) Upper 32 Performance-Monitoring Counter 28 + $C9D: Result := 'hpmcounter29'; // (HPM) Upper 32 Performance-Monitoring Counter 29 + $C9E: Result := 'hpmcounter30'; // (HPM) Upper 32 Performance-Monitoring Counter 30 + $C9F: Result := 'hpmcounter31'; // (HPM) Upper 32 Performance-Monitoring Counter 31 + $F11: Result := 'mvendorid'; // Machine Vendor ID + $F12: Result := 'marchid'; // Machine Architecture ID + $F13: Result := 'mimpid'; // Machine Implementation ID + $F14: Result := 'mhartid'; // Hardware Thread ID + $F15: Result := 'mconfigptr'; // Machine Configuration Pointer + $FB1: Result := 'mintstatus'; // Current interrupt levels + else + Result := '$'+HexStr(regnum, 3); + end; +end; + +{ TRiscvAsmInstruction } + +procedure TRiscvAsmInstruction.ReadCode; +begin + if not (diCodeRead in FFlags) then begin + if not FAsmDecoder.FProcess.ReadData(FAddress, INSTR_CODEBIN_LEN, FCodeBin) then + Include(FFlags, diCodeReadError); + Include(FFlags, diCodeRead); + end; +end; + +constructor TRiscvAsmInstruction.Create(AAsmDecoder: TRiscvAsmDecoder); +begin + FAsmDecoder := AAsmDecoder; + inherited Create; + AddReference; +end; + +procedure TRiscvAsmInstruction.SetAddress(AnAddress: TDBGPtr); +begin + FAddress := AnAddress; + FFlags := []; +end; + +function TRiscvAsmInstruction.IsCallInstruction: boolean; +var + op0, op1, op2, r, m: byte; +begin + Result := False; + ReadCode; + op0 := lo(FCodeBin[0]); + op1 := lo(FCodeBin[2]); + op2 := hi(FCodeBin[2]); + r := hi(FCodeBin[1]); + m := FCodeBin[0] shr 6; + + if ((op0 = 0) and (op1 = 0) and (op2 = 0) and + (r = 0) and (m = 3)) or // callx + (op0 = 5) then // call + Result := true; +end; + +function TRiscvAsmInstruction.IsReturnInstruction: boolean; +var + op0, op1, op2, r, s, t, m, n: byte; +begin + Result := False; + ReadCode; + + op0 := lo(FCodeBin[0]); + op1 := lo(FCodeBin[2]); + op2 := hi(FCodeBin[2]); + r := hi(FCodeBin[1]); + t := hi(FCodeBin[0]); + m := FCodeBin[0] shr 6; + n := hi(FCodeBin[0]) and 3; + + if ((op0 = 0) and (op1 = 0) and (op2 = 0) and + (r = 0) and (m = 2) and (n in [0, 1])) or // ret, retw + ((op0 = 13) and (r = 15) and (t in [0, 1])) then // ret.n, retw.n + Result := true; +end; + +function TRiscvAsmInstruction.IsLeaveStackFrame: boolean; +begin + Result := false; +end; + +function TRiscvAsmInstruction.InstructionLength: Integer; +begin + Result := 3; + ReadCode; + // op0 in 8..13 + if lo(FCodeBin[0]) in [8..13] then // narrow instruction series + Result := 2; +end; + +type + TPrologueState = ( + psStart, // Start of state machine + psStackCreated, // First time call to addi sp,sp, -const1 + psSavedReturnAddress, // Call to sw ra,const(sp). Return address is stored at: current sp + const1 + psAdjustedSP, // Second adjustemnt of SP (addi sp,sp,-const2). Return address is located at sp + const1 + const2 + psBody); // Start of code body, end of prologue + +function TRiscvAsmDecoder.FParsePrologue(AnAddress, AStartPC, AEndPC: TDBGPtr; + out returnAddressOffset, SPoffset: integer; out + AnIsOutsideFrame: Boolean): Boolean; +var + decoder: TRiscvDisassembler; + ADataLen: Cardinal; + AData: PByte; + frameOffset: word; + d, k: byte; + stackState: TPrologueState; + instr: TRiscvInstruction; +begin +{ RV32 function entry example + +40380c9a : +40380c9a: 1141 addi sp,sp,-16 // Make space for saved registers +40380c9c: c606 sw ra,12(sp) // Save return address +40380c9e: c422 sw s0,8(sp) // Callee saved register (possibly frame pointer) +40380ca0: c226 sw s1,4(sp) // Callee saved register +40380ca2: c04a sw s2,0(sp) // Callee saved register +40380ca4: 4785 addi a5, zero, 1 // start of body + +FPC also adjusts SP after storing parameters: +42000088
: +42000088: ff410113 addi sp,sp,-12 // Make space for saved registers +4200008c: 00112423 sw ra,8(sp) // Save return address +42000090: 00812223 sw s0,4(sp) // Callee saved register (in this case used as frame pointer) +42000094: 00912023 sw s1,0(sp) // Callee saved register +42000098: 00c10413 addi s0,sp,12 // Adjust frame pointer +4200009c: fcc10113 addi sp,sp,-52 // Extend stack frame to 64 bytes total, not clear why? +420000a0: 00000097 auipc ra,0x0 // start of body +420000a4: 0c4080e7 jalr 196(ra) # 42000164 +} + // Also read next instruction, for simple procs/interrupts it may not be easy to spot the end of prologue + // so the next instruction could tie break + ADataLen := Min(MaxPrologueSize, AnAddress - AStartPC); + Result := ReadCodeAt(AStartPC, ADataLen); + if not Result then + exit; + + AData := @FCodeBin[0]; + returnAddressOffset := 0; + SPoffset := 0; + AnIsOutsideFrame := true; + stackState := psStart; + frameOffset := 0; + + // Loop until prologue buffer is empty, or stepped inside stack frame + while (ADataLen > 1) and AnIsOutsideFrame do + begin + decoder.Disassemble(AData, instr); + inc(AData, instr.Size); + dec(ADataLen, instr.Size); + + case instr.OpCode of + // addi rd, rs1, imm + A_ADDI: + begin + if (instr.rs1 = SPindex) then + begin + if (instr.rd = SPindex) then + begin + inc(SPoffset, -instr.imm); + if instr.imm < 0 then + begin + if stackState = psStart then + begin + stackState := psStackCreated; + end + else if (stackState = psStackCreated) then + inc(returnAddressOffset, -instr.imm) + else + AnIsOutsideFrame := false; // Or error + end; + end; + end + else + AnIsOutsideFrame := false; + end; + + // sw rs2, offset(rs1) + A_SW: + begin + if instr.rs1 = SPindex then + begin + if instr.rs2 = ReturnAddressIndex then + inc(returnAddressOffset, instr.imm); + end + else + AnIsOutsideFrame := false; + end; + else + AnIsOutsideFrame := false; + end; + end; +end; + +function TRiscvAsmDecoder.FParseEpilogue(AnAddress, AStartPC, AEndPC: TDBGPtr; + out returnAddressOffset: word; out AnIsOutsideFrame: Boolean): Boolean; +begin +end; + +function TRiscvAsmDecoder.FormatInstruction(instr: TRiscvInstruction): string; +var + s1, s2: string; +begin + Result := RiscvOpcodeString[instr.OpCode]; + if instr.OpCode = A_INVALID then exit; + + case instr.format of + rvfInstructionDestSrcOffset: // lw rd, imm(rs1) + Result := format('%-8s %s, %d(%s)', [Result, RiscvABIRegisterNames[instr.rd], instr.imm, RiscvABIRegisterNames[instr.rs1]]); + + rvfLoadFDestSrcOffset: // flw rd, imm(rs1) + Result := format('%-8s ft%d, %d(%s)', [Result, instr.rd, instr.imm, RiscvABIRegisterNames[instr.rs1]]); + + rvfStoreSrc2Src1Offset: // sw rs2, imm(rs1) + Result := format('%-8s %s, %d(%s)', [Result, RiscvABIRegisterNames[instr.rs2], instr.imm, RiscvABIRegisterNames[instr.rs1]]); + + rvfStoreFSrc2Src1Offset: // fsw rs2, imm(rs1) + Result := format('%-8s ft%d, %d(%s)', [Result, instr.rs2, instr.imm, RiscvABIRegisterNames[instr.rs1]]); + + rvfBranchSrc1Src2Imm: // beq rs1, rs2, imm + Result := format('%-8s %s, %s, %d', [Result, RiscvABIRegisterNames[instr.rs1], RiscvABIRegisterNames[instr.rs2], instr.imm]); + + rvfInstruction: ; // ebreak + + rvfFence: // fence [iorw], [iorw] + begin + // fence iorw, iorw is written as fence + if (instr.imm and $FF) = $FF then exit; + + s1 := ''; + if instr.imm and (1 shl 7) > 0 then + s1 := 'i'; + if instr.imm and (1 shl 6) > 0 then + s1 := s1 + 'o'; + if instr.imm and (1 shl 5) > 0 then + s1 := s1 + 'r'; + if instr.imm and (1 shl 4) > 0 then + s1 := s1 + 'w'; + + s2 := ''; + if instr.imm and (1 shl 3) > 0 then + s2 := 'i'; + if instr.imm and (1 shl 2) > 0 then + s2 := s2 + 'o'; + if instr.imm and (1 shl 1) > 0 then + s2 := s2 + 'r'; + if instr.imm and 1 > 0 then + s2 := s2 + 'w'; + + if (s1 = '') and (s2 = '') then + Result := format('%-8s', [Result]) + else + Result := format('%-8s %s, %s', [Result, s1, s2]); + end; + + rvfCustom: // custom-imm + Result := format('%s-%d', [Result, instr.imm]); + + rvfInstructionSrcOffset: // cbo.clean (rs1) + Result := format('%-8s (%s)', [Result, RiscvABIRegisterNames[instr.rs1]]); + + rvfInstructionDestSrcImm: // addi rd, rs1, imm + Result := format('%-8s %s, %s, %d', [Result, RiscvABIRegisterNames[instr.rd], RiscvABIRegisterNames[instr.rs1], instr.imm]); + + rvfInstructionDestSrc1Src2: // sub rd, rs1, rs2 + Result := format('%-8s %s, %s, %s', [Result, RiscvABIRegisterNames[instr.rd], RiscvABIRegisterNames[instr.rs1], RiscvABIRegisterNames[instr.rs2]]); + + rvfInstructionSrc1Src2Imm: // beq rs1, rs2, imm + Result := format('%-8s %s, %s, %d', [Result, RiscvABIRegisterNames[instr.rs1], RiscvABIRegisterNames[instr.rs2], instr.imm]); + + rvfAmoDestSrc2Src1, rvfAmoDestSrc1, rvfAmoDestSrc2Src1_addr: + begin + s1 := ''; + if instr.AmoInfo.width = 2 then + s1 := '.w' + else if instr.AmoInfo.width = 3 then + s1 := '.d'; + + s2 := ''; + if instr.AmoInfo.aquire then + s2 := s2 + 'aq'; + if instr.AmoInfo.release then + s2 := s2 + 'rel'; + if s2 <> '' then + s2 := '.' + s2; + + Result := Result + s1 + s2; + if instr.format = rvfAmoDestSrc2Src1 then // sc.w rd, rs2, rs1 + Result := format('%-8s %s, %s, %s', [Result, RiscvABIRegisterNames[instr.rd], RiscvABIRegisterNames[instr.rs2], RiscvABIRegisterNames[instr.rs1]]) + else if instr.format = rvfAmoDestSrc1 then // lr.w rd, rs1 + Result := format('%-8s %s, %s', [Result, RiscvABIRegisterNames[instr.rd], RiscvABIRegisterNames[instr.rs1]]) + else if instr.format = rvfAmoDestSrc2Src1_addr then // amoswap.w.aq rd, rs2, (rs1) + Result := format('%-8s %s, %s, (%s)', [Result, RiscvABIRegisterNames[instr.rd], RiscvABIRegisterNames[instr.rs2], RiscvABIRegisterNames[instr.rs1]]); + end; + + rvfInstructionDestImm: // auipc rd, imm + Result := format('%-8s %s, 0x%x', [Result, RiscvABIRegisterNames[instr.rd], instr.imm]); + + rvfInstructionDestSrc: // clzw rd, rs1 + Result := format('%-8s %s, %s', [Result, RiscvABIRegisterNames[instr.rd], RiscvABIRegisterNames[instr.rs1]]); + + rvfFloatInstrDestSrc1, rvfFloatInstrDestSrc1Src2, rvfFloatInstrDestSrc1Src2Src3: + begin + case instr.fpwidth of + 0: s1 := '.S'; + 1: s1 := '.D'; + 2: s1 := '.H'; + 3: s1 := '.Q'; + else + s1 := '?'; + end; + + Result := Result + s1; + if instr.format = rvfFloatInstrDestSrc1 then // fsqrt.s rd, rs1 + Result := format('%-8s ft%d, ft%d', [Result, instr.rd, instr.rs1]) + else if instr.format = rvfFloatInstrDestSrc1Src2 then // fsub.s rd, rs1, rs2 => rd = rs1 - rs2 + Result := format('%-8s ft%d, ft%d, ft%d', [Result, instr.rd, instr.rs1, instr.rs2]) + else if instr.format = rvfFloatInstrDestSrc1Src2Src3 then // fmadd.s rd, rs1, rs2, rs3 => (rs1 x rs2) + rs3 + Result := format('%-8s ft%d, ft%d, ft%d, ft%d', [Result, instr.rd, instr.rs1, instr.rs2, instr.rs3]); + end; + + rvfCSRDestCsrSrc1: // csrrw rd, #csr, rs1 + Result := format('%-8s %s, %s, %s', [Result, RiscvABIRegisterNames[instr.rd], CsrRegNumToName(instr.csr), RiscvABIRegisterNames[instr.rs1]]); + rvfCSRDestCsrImm: // csrrw rd, #csr, imm + Result := format('%-8s %s, %s, %s', [Result, RiscvABIRegisterNames[instr.rd], CsrRegNumToName(instr.csr), instr.imm]); + else + Result := 'Invalid format specifier'; + end; +end; + +function TRiscvAsmDecoder.GetLastErrorWasMemReadErr: Boolean; +begin + Result := FLastErrWasMem; +end; + +function TRiscvAsmDecoder.GetMaxInstrSize: integer; +begin + Result := 4; +end; + +function TRiscvAsmDecoder.GetMinInstrSize: integer; +begin + Result := 2; +end; + +function TRiscvAsmDecoder.GetCanReverseDisassemble: boolean; +begin + Result := False; +end; + +function TRiscvAsmDecoder.ReadCodeAt(AnAddress: TDBGPtr; ALen: Cardinal + ): Boolean; +begin + Result := FProcess.ReadData(AnAddress, ALen, FCodeBin[0], ALen); + FLastErrWasMem := not Result; +end; + +procedure TRiscvAsmDecoder.Disassemble(var AAddress: Pointer; out + ACodeBytes: String; out ACode: String); +var + AnInfo: TDbgInstInfo; +begin + Disassemble(AAddress, ACodeBytes, ACode, AnInfo); +end; + +procedure TRiscvAsmDecoder.Disassemble(var AAddress: Pointer; out ACodeBytes: String; + out ACode: String; out AnInfo: TDbgInstInfo); +var + decoder: TRiscvDisassembler; + k: byte; + pcode: PByte; + instr: TRiscvInstruction; +begin + AnInfo := default(TDbgInstInfo); + pcode := AAddress; + decoder.Disassemble(AAddress, instr); + Inc(AAddress, instr.Size); + + ACodeBytes := ''; + for k := 0 to instr.Size-1 do + ACodeBytes := ACodeBytes + HexStr(pcode[k], 2) + ' '; + Delete(ACodeBytes, length(ACodeBytes), 1); + ACode := FormatInstruction(instr); + + // Todo: Indicate whether currrent instruction has a destination code address + // call jump branch(?) + // Return information in AnInfo +end; + +function TRiscvAsmDecoder.GetInstructionInfo(AnAddress: TDBGPtr + ): TDbgAsmInstruction; +begin + if (FLastInstr = nil) or (FLastInstr.RefCount > 1) then begin + ReleaseRefAndNil(FLastInstr); + FLastInstr := TRiscvAsmInstruction.Create(Self); + end; + + FLastInstr.SetAddress(AnAddress); + Result := FLastInstr; +end; + +function TRiscvAsmDecoder.GetFunctionFrameInfo(AnAddress: TDBGPtr; out + AnIsOutsideFrame: Boolean): Boolean; +begin + Result := False; +end; + +function TRiscvAsmDecoder.GetBreakInstruction(const ALocation: TDBGPtr; out + BreakInstructionLength: Integer): QWord; +begin + BreakInstructionLength := GetInstructionInfo(ALocation).InstructionLength; + + // Set the user fields s and t to 0 + if BreakInstructionLength = 2 then + Result := $000000 { ILL} // $F0D2 { BREAK } + else + Result := $F06D { ILL.N }; // 4000 { BREAK.N } +end; + +function TRiscvAsmDecoder.BreakInstructionOffset: Int8; +begin + Result := 0; // ? +end; + +function TRiscvAsmDecoder.GetFunctionFrameReturnAddress(AnAddress, AStartPC, + AEndPC: TDBGPtr; out returnAddressOffset, SPoffset: integer; out + AnIsOutsideFrame: Boolean): Boolean; +begin + result := false; + { Cases to consider: + A - if (AStartPC + MaxPrologueSize < AnAddress) and (AnAddress + MaxEpilogueSize < AEndPC) + then currently inside stack frame. Parse prologue to figure out + offset from frame pointer to return address. + + B - if (AStartPC + MaxPrologueSize < AnAddress) + then possibly before final stack frame. Need to parse prologue up to AnAddress + to figure out how far the stack has moved since entry to calculate offset + from SP to return address. If frame pointer has been configured before + AnAddress, assume inside frame and return offset relative to frame pointer. + + C - if (AnAddress + MaxEpilogueSize < AEndPC) + then possibly inside frame teardown. Need to reverse parse epilogue up to AnAddress + to figure out how much frame will unwind to calculate offset to return address. + If frame pointer has been restored before AnAddress then ouside frame. + } + + // Return address always in register ra + returnAddressOffset := 0; + SPoffset := 0; + + if (AnAddress = AStartPC) or (AnAddress = AEndPC) then + begin + AnIsOutsideFrame := true; + Exit(True); + end + else + result := FParsePrologue(AnAddress, AStartPC, AEndPC, returnAddressOffset, + SPoffset, AnIsOutsideFrame); +end; + +constructor TRiscvAsmDecoder.Create(AProcess: TDbgProcess); +begin + FProcess := AProcess; +end; + +destructor TRiscvAsmDecoder.Destroy; +begin + ReleaseRefAndNil(FLastInstr); + inherited Destroy; +end; + +{ TDbgStackUnwinderRiscv } + +constructor TDbgStackUnwinderRiscv.Create(AProcess: TDbgProcess); +begin + FProcess := AProcess; + FAddressSize := 4; // For RV32 + FCodeReadErrCnt := 0; +end; + +procedure TDbgStackUnwinderRiscv.InitForThread(AThread: TDbgThread); +begin + FThread := AThread; +end; + +procedure TDbgStackUnwinderRiscv.InitForFrame( + ACurrentFrame: TDbgCallstackEntry; out CodePointer, StackPointer, + FrameBasePointer: TDBGPtr); +var + R: TDbgRegisterValue; +begin + CodePointer := ACurrentFrame.AnAddress; + + // Frame pointer is x8 (if used). Assume no frame pointer for now + FrameBasePointer := ACurrentFrame.FrameAdress; + + StackPointer := 0; + R := ACurrentFrame.RegisterValueList.FindRegisterByDwarfIndex(SPindexDwarf); + if R = nil then exit; + StackPointer := R.NumValue; +end; + +procedure TDbgStackUnwinderRiscv.GetTopFrame(out CodePointer, StackPointer, + FrameBasePointer: TDBGPtr; out ANewFrame: TDbgCallstackEntry); +var + i: Integer; + R: TDbgRegisterValue; +begin + FLastFrameBaseIncreased := True; + FSPchangeCount := 1; + CodePointer := Thread.GetInstructionPointerRegisterValue; + StackPointer := Thread.GetStackPointerRegisterValue; + FrameBasePointer := Thread.GetStackBasePointerRegisterValue; + ANewFrame := TDbgCallstackEntry.create(Thread, 0, FrameBasePointer, CodePointer); + FPreviousPC := CodePointer; + + // Frame pointer may not have been updated yet + if FrameBasePointer > StackPointer then + FrameBasePointer := StackPointer; + + i := Thread.RegisterValueList.Count; + while i > 0 do begin + dec(i); + R := Thread.RegisterValueList[i]; + ANewFrame.RegisterValueList.DbgRegisterAutoCreate[R.Name].SetValue(R.NumValue, R.StrValue, R.Size, R.DwarfIdx); + end; +end; + +function TDbgStackUnwinderRiscv.Unwind(AFrameIndex: integer; var CodePointer, + StackPointer, FrameBasePointer: TDBGPtr; ACurrentFrame: TDbgCallstackEntry; + out ANewFrame: TDbgCallstackEntry): TTDbgStackUnwindResult; + +{ + add sp,sp,-12 // Move SP (x2) down by 12 bytes + sw ra,8(sp) // Store ra (x1, return address) at SP + 8 + sw s0,4(sp) // Store s0 (x8, frame pointer) at SP + 4 + sw s1,0(sp) // Store s1 (x9) at SP + add s0,sp,12 // Adjust frame pointer to point to start of this stack + add sp,sp,-52 // Move SP down to align to 64 byte boundary (?) +} + +const + MAX_FRAMES = 50; // safety net + Size = 4; // Default size of pointer +var + LastFrameBase: TDBGPtr; + OutSideFrame: Boolean; + startPC, endPC: TDBGPtr; + returnAddrStackOffset, SPoffset: integer; +begin + ANewFrame := nil; + Result := suFailed; + + if (StackPointer > $40000000) or (StackPointer < $3F000000) or + (CodePointer < $40000001) or {not FLastFrameBaseIncreased then} + (AFrameIndex - FSPchangeCount > 0) then + exit; + + OutSideFrame := False; + LastFrameBase := FrameBasePointer; + + // Get start/end PC of proc from debug info + if not Self.Process.FindProcStartEndPC(CodePointer, startPC, endPC) then + begin + { Assume we are at beginning of proc. GetFunctionFrameReturnAddress should then + assume we are outside the stack frame (or no stack frame exists). + } + endPC := CodePointer; + end; + + if not TRiscvAsmDecoder(Process.Disassembler).GetFunctionFrameReturnAddress(CodePointer, startPC, endPC, + returnAddrStackOffset, SPoffset, OutSideFrame) then + OutSideFrame := False; + + if OutSideFrame then begin + // Before adjustment of frame pointer, or after restoration of frame pointer, + // If SP not yet adjusted then return address should be in register ra + if returnAddrStackOffset = 0 then + CodePointer := TDbgRiscvProcess(FProcess).MainThread.RegisterValueList[ReturnAddressIndexDwarf].NumValue + else if not Process.ReadData(int64(StackPointer) + returnAddrStackOffset, Size, CodePointer) or + (CodePointer = 0) then + exit; + {$PUSH}{$R-}{$Q-} + StackPointer := StackPointer + SPoffset; + FrameBasePointer := StackPointer; // After popping return-addr from "StackPtr" + {$POP} + end + else begin + // Inside stack frame, return PC should be located by offset from FP + if not Process.ReadData(int64(FrameBasePointer) + returnAddrStackOffset, Size, CodePointer) or (CodePointer = 0) then exit; + {$PUSH}{$R-}{$Q-} + FrameBasePointer := StackPointer + SPoffset; // After popping return-addr from stack + // An estimate of SP, needed when attempting unwinding of next frame + StackPointer := FrameBasePointer; + {$POP} + end; + + FLastFrameBaseIncreased := (FrameBasePointer <> 0) and (FrameBasePointer > LastFrameBase); + if FLastFrameBaseIncreased or (FPreviousPC <> CodePointer) then + inc(FSPchangeCount) + else + exit; + FPreviousPC := CodePointer; + + ANewFrame:= TDbgCallstackEntry.create(Thread, AFrameIndex, FrameBasePointer, CodePointer); + ANewFrame.RegisterValueList.DbgRegisterAutoCreate[nPC].SetValue(CodePointer, IntToStr(CodePointer), Size, PCIndexDwarf); + ANewFrame.RegisterValueList.DbgRegisterAutoCreate[nSP].SetValue(StackPointer, IntToStr(StackPointer), Size, SPindex); + + FCodeReadErrCnt := 0; + Result := suSuccess; +end; + +{ TRiscvDisassembler } + +procedure TRiscvDisassembler.Disassemble(var AAddress: Pointer; out + AnInstruction: TRiscvInstruction); +var + pcode: PByte; + opcode, ophi, oplo: byte; + instr: uint32; + instrlen: int32; +begin + pcode := AAddress; + AnInstruction.OpCode := A_INVALID; + oplo := pcode[0] and 3; + + if oplo < 3 then + begin + // compressed (16 bit) instruction + instr := pcode[0] or (pcode[1] shl 8); + ophi := instr shr 13; + case oplo of + 0: + case ophi of + 0: AnInstruction := decodeAddi4spn(instr); + 1: AnInstruction := decodeFLD(instr); + 2: AnInstruction := decodeLW(instr); + 3: AnInstruction := decodeFLW(instr); + 4: AnInstruction.OpCode := A_RESERVED; + 5: AnInstruction := decodeFSD(instr); + 6: AnInstruction := decodeSW(instr); + 7: AnInstruction := decodeFSW(instr); + end; + + 1: + case ophi of + 0: AnInstruction := decodeAddI(instr); + 1: AnInstruction := decodeCJAL(instr); + 2: AnInstruction := decodeLI(instr); + 3: AnInstruction := decodeLUI(instr); + 4: AnInstruction := decodeALU(instr); + 5: AnInstruction := decodeCJ(instr); + 6, 7: AnInstruction := decodeCBNEQZ(instr, ophi); + end; + + 2: + case ophi of + 0: AnInstruction := decodeSLLI(instr); + 1: AnInstruction := decodeFLDSP(instr); + 2: AnInstruction := decodeLWSP(instr); + 3: AnInstruction := decodeFLWSP(instr); + 4: AnInstruction := decodeJR(instr); + 5, 6, 7: AnInstruction := decodeCStore(instr, ophi); + end; + end; + AnInstruction.Size := 2; + end + else + begin + instr := pcode[0] or (pcode[1] shl 8) or (pcode[2] shl 16) or (pcode[3] shl 24); + opcode := pcode[0] and $7F; + // Lookup based on table 70 + case opcode of + %0000011: AnInstruction := decodeLoad(instr); + %0000111: AnInstruction := decodeLoadF(instr); + %0001011: AnInstruction := customMap(0, instr); + %0001111: AnInstruction := decodeMiscMem(instr); + %0010011: AnInstruction := decodeOpIm(instr); + %0010111: AnInstruction := decodeAUIPC_LUI(instr, opcode); + %0011011: AnInstruction := decodeOpIm32(instr); + //%0011111: ; + + %0100011: AnInstruction := decodeStore(instr); + %0100111: AnInstruction := decodeStoreF(instr); + %0101011: AnInstruction := customMap(1, instr); + %0101111: AnInstruction := decodeAMO(instr); + %0110011: AnInstruction := decodeOp(instr); + %0110111: AnInstruction := decodeAUIPC_LUI(instr, opcode); + %0111011: AnInstruction := decodeOp32(instr); + //%0111111: ; + + %1000011: AnInstruction := decodeMAdd(instr); + %1000111: AnInstruction := decodeMSub(instr); + %1001011: AnInstruction := decodeNMSub(instr); + %1001111: AnInstruction := decodeMAdd(instr); + %1010011: AnInstruction := decodeOP_FP(instr); + %1010111: ;//TODO ; + %1011011: AnInstruction := customMap(2, instr); + //%1011111: ; + + %1100011: AnInstruction := decodeBranch(instr); + %1100111: AnInstruction := decodeJALR(instr); + %1101011: AnInstruction.OpCode := A_RESERVED; + %1101111: AnInstruction := decodeJAL(instr); + %1110011: AnInstruction := decodeSystem(instr); + %1110111: ; + %1111011: AnInstruction := customMap(3, instr); + //%1111111: ; + end; + AnInstruction.Size := 4; + end; +end; + +procedure TRiscvDisassembler.decodeR(instr: uint32; out funct7, rs2, rs1, funct3, rd: byte); +begin + funct7 := instr shr 25; + rs2 := (instr shr 20) and $1F; + rs1 := (instr shr 15) and $1F; + funct3 := (instr shr 12) and 7; + rd := (instr shr 7) and $1F; +end; + +procedure TRiscvDisassembler.decodeI(instr: uint32; out imm: int32; out rs1, funct3, rd: byte); +begin + imm := SarLongint(int32(instr), 20); + rs1 := (instr shr 15) and $1F; + funct3 := (instr shr 12) and 7; + rd := (instr shr 7) and $1F; +end; + +procedure TRiscvDisassembler.decodeS(instr: uint32; out imm: int32; out rs2, rs1, funct3: byte); +begin + imm := SarLongint(int32(instr), 25) shl 5; + rs2 := (instr shr 20) and $1F; + rs1 := (instr shr 15) and $1F; + funct3 := (instr shr 12) and 7; + imm := imm or ((instr shr 7) and $1F); +end; + +procedure TRiscvDisassembler.decodeB(instr: uint32; out imm: int32; out rs2, rs1, funct3: byte); +var + imm12, imm10_5, imm4_1, imm11: uint16; +begin + imm12 := instr shr 31; + imm10_5 := (instr shr 25) and $1F; + imm4_1 := (instr shr 8) and $0F; + imm11 := (instr shr 7) and 1; + imm := (imm12 shl 12) or (imm11 shl 11) or (imm10_5 shl 5) or (imm4_1 shl 1); + rs2 := (instr shr 20) and $1F; + rs1 := (instr shr 15) and $1F; + funct3 := (instr shr 12) and 7; +end; + +procedure TRiscvDisassembler.decodeU(instr: uint32; out imm: int32; out rd: byte); +begin + imm := instr shr 12; //instr and $FFFFF000; + rd := (instr shr 7) and $1F; +end; + +procedure TRiscvDisassembler.decodeJ(instr: uint32; out imm: int32; out rd: byte); +var + imm20, imm19_12, imm11, imm10_1: uint16; +begin + imm20 := instr shr 31; + imm19_12 := (instr shr 12) and $FF; + imm11 := (instr shr 20) and 1; + imm10_1 := (instr shr 21) and $1FF; + imm := (imm20 shl 20) or (imm19_12 shl 12) or (imm11 shl 11) or (imm10_1 shl 1); + rd := (instr shr 7) and $1F; +end; + +function TRiscvDisassembler.decodeLoad(instr: uint32): TRiscvInstruction; +var + imm: int32; + rs1, funct3, rd: byte; +begin + FillByte(Result, SizeOf(Result), 0); + + // lw rd, imm(rs1) + Result.format := rvfInstructionDestSrcOffset; + + Result.Size := 4; + decodeI(instr, imm, rs1, funct3, rd); + Result.rd:= rd; + Result.rs1 := rs1; + Result.imm := imm; + + case funct3 of + 0: Result.OpCode := A_LB; + 1: Result.OpCode := A_LH; + 2: Result.OpCode := A_LW; + 3: Result.OpCode := A_LD; // 64 bit + 4: Result.OpCode := A_LBU; + 5: Result.OpCode := A_LHU; + 6: Result.OpCode := A_LWU; // 64 bit + //7: Result.OpCode := A_LDU; + else + Result.OpCode := A_INVALID; + end; +end; + +function TRiscvDisassembler.decodeLoadF(instr: uint32): TRiscvInstruction; +var + imm: int32; + rs1, width, rd: byte; +begin + FillByte(Result, SizeOf(Result), 0); + + Result.format := rvfLoadFDestSrcOffset; + Result.Size := 4; + decodeI(instr, imm, rs1, width, rd); + Result.rd := rd; + Result.rs1 := rs1; + Result.imm := imm; + + case width of + 1: Result.OpCode := A_FLH; + 2: Result.OpCode := A_FLW; + 3: Result.OpCode := A_FLD; + 4: Result.OpCode := A_FLQ; + else + Result.OpCode := A_INVALID; + end; +end; + +function TRiscvDisassembler.customMap(i: integer; instr: uint32): TRiscvInstruction; +begin + FillByte(Result, SizeOf(Result), 0); + Result.format := rvfCustom; + Result.Size := 4; + Result.imm := i; +end; + +procedure TRiscvDisassembler.decodeFence(imm: uint32; out fm, pred, succ: byte); +begin + fm := imm shr 8; + pred := (imm shr 4) and $F; + succ := imm and $F; +end; + +function TRiscvDisassembler.decodeMiscMem(instr: uint32): TRiscvInstruction; +var + imm: int32; + fm, pred, succ, rs1, funct3, rd: byte; +begin + FillByte(Result, SizeOf(Result), 0); + Result.OpCode := A_INVALID; + Result.Size := 4; + decodeI(instr, imm, rs1, funct3, rd); + + case funct3 of + 0: + begin + decodeFence(imm, fm, pred, succ); + // Fence mode (fm) = 0 for normal fence, %1000 for fence.tso + if (fm = 8) and (pred = 3) and (succ = 3) then + begin + Result.OpCode := A_FENCE_TSO; + Result.format := rvfInstruction; + end + else if fm = 0 then + begin + if (pred = 1) and (succ = 0) and (rs1 = 0) and (rd = 0) then + begin + Result.OpCode := A_PAUSE; + Result.format := rvfInstruction; + end + else + begin + // Pred/succ bits are mapped in the following sequence: msb to lsb: i,o,r,w + Result.OpCode := A_FENCE; + Result.format := rvfFence; + end + end + end; // funct3 = 0 + + 1: + begin + Result.OpCode := A_FENCE_I; + Result.format := rvfInstruction; + end; // funct3 = 1 + + 2: + begin + if rd = 0 then + begin + Result.format := rvfInstructionSrcOffset; + Result.rs1 := rs1; + Result.rd := rd; + case imm of + 0: Result.OpCode := A_CBO_INVAL; + 1: Result.OpCode := A_CBO_CLEAN; + 2: Result.OpCode := A_CBO_FLUSH; + 4: Result.OpCode := A_CBO_ZERO; + end; + end; + end; // funct3 = 2 + end; // case funct3 +end; + +function TRiscvDisassembler.decodeOpIm(instr: uint32): TRiscvInstruction; +var + imm: int32; + immhi, immlo, rs1, funct3, rd: byte; +begin + FillByte(Result, SizeOf(Result), 0); + Result.OpCode := A_INVALID; + Result.format := rvfInstruction; + Result.Size := 4; + + decodeI(instr, imm, rs1, funct3, rd); + immhi := byte(imm shr 6); + immlo := byte(imm and $3F); + + Result.rd := rd; + Result.rs1 := rs1; + case funct3 of + 0: + begin + Result.OpCode := A_ADDI; + Result.imm := imm; + Result.format := rvfInstructionDestSrcImm; + end; + + 1: + begin + Result.imm := immlo; + Result.format := rvfInstructionDestSrcImm; + if immhi = 0 then + Result.OpCode := A_SLLI + else + Result.format := rvfInstruction; + end; + + 2: + begin + Result.OpCode := A_SLTI; + Result.imm := imm; + Result.format := rvfInstructionDestSrcImm; + end; + + 3: + begin + Result.OpCode := A_SLTIU; + Result.imm := imm; + Result.format := rvfInstructionDestSrcImm; + end; + + 4: + begin + Result.OpCode := A_XORI; + Result.imm := imm; + Result.format := rvfInstructionDestSrcImm; + end; + + 5: + begin + Result.imm := immlo; + Result.format := rvfInstructionDestSrcImm; + if immhi = 0 then + Result.OpCode := A_SRLI + else if immhi = 16 then + Result.OpCode := A_SRAI + else + Result.format := rvfInstruction; + end; + + 6: + begin + Result.OpCode := A_ORI; + Result.imm := imm; + Result.format := rvfInstructionDestSrcImm; + end; + + 7: + begin + Result.OpCode := A_ANDI; + Result.imm := imm; + Result.format := rvfInstructionDestSrcImm; + end; + end; +end; + +function TRiscvDisassembler.decodeAUIPC_LUI(instr: uint32; opcode: byte): TRiscvInstruction; +begin + FillChar(Result, SizeOf(Result), 0); + decodeU(instr, Result.imm, Result.rd); + Result.format := rvfInstructionDestImm; + Result.Size := 4; + + if opcode = %0010111 then + Result.OpCode := A_AUIPC + else if opcode = %0110111 then + Result.OpCode := A_LUI + else + Result.OpCode := A_INVALID; +end; + +function TRiscvDisassembler.decodeOpIm32(instr: uint32): TRiscvInstruction; +var + imm: int32; + immhi, immlo, rs1, funct3, rd: byte; +begin + FillByte(Result, SizeOf(Result), 0); + Result.OpCode := A_INVALID; + Result.format := rvfInstruction; + Result.Size := 4; + + decodeI(instr, imm, rs1, funct3, rd); + immhi := imm shr 5; + immlo := imm and $1F; + + Result.rd := rd; + Result.rs1 := rs1; + case funct3 of + 0: + begin + Result.OpCode := A_ADDIW; + Result.imm := imm; + Result.format := rvfInstructionDestSrcImm; + end; + + 1: + begin + if immhi = 0 then + begin + Result.OpCode := A_SLLIW; + Result.imm := immlo; + Result.format := rvfInstructionDestSrcImm; + end + else if (immhi = 48) then + begin + if (immlo = 0) then + begin + Result.OpCode := A_CLZW; + Result.format := rvfInstructionDestSrc; + end + else if (immlo = 1) then + begin + Result.OpCode := A_CTZW; + Result.format := rvfInstructionDestSrc; + end + end + else if (immhi = 2) then + begin + Result.OpCode := A_SLLI_UW; + Result.format := rvfInstructionDestSrcImm; + end; + end; + + 5: + begin + Result.imm := immlo; + Result.format := rvfInstructionDestSrcImm; + if immhi = 0 then + Result.OpCode := A_SRLIW + else if immhi = 32 then + Result.OpCode := A_SRAIW + else if immhi = 48 then + Result.OpCode := A_RORIW + else + Result.format := rvfInstruction; + end; + + 6: + begin + Result.OpCode := A_ORI; + Result.imm := imm; + Result.format := rvfInstructionDestSrcImm; + end; + + 7: + begin + Result.OpCode := A_ANDI; + Result.imm := imm; + Result.format := rvfInstructionDestSrcImm; + end; + end; +end; + +function TRiscvDisassembler.decodeStore(instr: uint32): TRiscvInstruction; +var + funct3: byte; +begin + FillByte(Result, SizeOf(Result), 0); + + // sd rs2, imm(rs1) + Result.format := rvfStoreSrc2Src1Offset; + + Result.Size := 4; + decodeS(instr, Result.imm, Result.rs2, Result.rs1, funct3); + + case funct3 of + 0: Result.OpCode := A_SB; + 1: Result.OpCode := A_SH; + 2: Result.OpCode := A_SW; + 3: Result.OpCode := A_SD; // 64 bit + else + Result.OpCode := A_INVALID; + end; +end; + +function TRiscvDisassembler.decodeStoreF(instr: uint32): TRiscvInstruction; +var + width: byte; +begin + FillByte(Result, SizeOf(Result), 0); + + Result.format := rvfStoreFSrc2Src1Offset; + Result.Size := 4; + decodeS(instr, Result.imm, Result.rs2, Result.rs1, width); + + case width of + 1: Result.OpCode := A_FSH; + 2: Result.OpCode := A_FSW; + 3: Result.OpCode := A_FSD; + 4: Result.OpCode := A_FSQ; + else + Result.OpCode := A_INVALID; + end; +end; + +function TRiscvDisassembler.decodeAMO(instr: uint32): TRiscvInstruction; +var + funct7, funct3: byte; +begin + FillByte(Result, SizeOf(Result), 0); + decodeR(instr, funct7, Result.rs2, Result.rs1, funct3, Result.rd); + Result.AmoInfo.aquire := (funct7 and 1) = 1; + Result.AmoInfo.release := (funct7 and 2) = 2; + Result.AmoInfo.width := funct3; + Result.format := rvfAmoDestSrc2Src1_addr; + + case (funct7 shr 2) of + 0: Result.OpCode := A_AMOADD; + 1: Result.OpCode := A_AMOSWAP; + 2: + begin + Result.OpCode := A_LR; + Result.format := rvfAmoDestSrc1; + end; + + 3: + begin + Result.OpCode := A_SC; + Result.format := rvfAmoDestSrc2Src1; + end; + + 4: Result.OpCode := A_AMOXOR; + 8: Result.OpCode := A_AMOOR; + 12: Result.OpCode := A_AMOAND; + 16: Result.OpCode := A_AMOMIN; + 20: Result.OpCode := A_AMOMAX; + 24: Result.OpCode := A_AMOMINU; + 28: Result.OpCode := A_AMOMAXU; + else + begin + Result.OpCode := A_INVALID; + Result.format := rvfInstruction; + end; + end; +end; + +function TRiscvDisassembler.decodeOp(instr: uint32): TRiscvInstruction; +var + funct3, funct7: byte; +begin + FillByte(Result, SizeOf(Result), 0); + decodeR(instr, funct7, Result.rs2, Result.rs1, funct3, Result.rd); + Result.format := rvfInstructionDestSrc1Src2; + Result.OpCode := A_INVALID; + + case funct3 of + 0: + begin + if funct7 = 0 then + Result.OpCode := A_ADD + else if funct7 = 1 then + Result.OpCode := A_MUL + else if funct7 = 32 then + Result.OpCode := A_SUB; + end; + + 1: + begin + if funct7 = 0 then + Result.OpCode := A_SLL + else if funct7 = 1 then + Result.OpCode := A_MULH + else if funct7 = 5 then + Result.OpCode := A_CLMUL + else if funct7 = 20 then + Result.OpCode := A_BSET + else if funct7 = 36 then + Result.OpCode := A_BCLR + else if funct7 = 48 then + Result.OpCode := A_ROL + end; + + 2: + begin + if funct7 = 0 then + Result.OpCode := A_SLT + else if funct7 = 1 then + Result.OpCode := A_MULHSU + else if funct7 = 5 then + Result.OpCode := A_CLMULR + else if funct7 = 16 then + Result.OpCode := A_SH1ADD + end; + + 3: + begin + if funct7 = 0 then + Result.OpCode := A_SLTU + else if funct7 = 1 then + Result.OpCode := A_MULHU + else if funct7 = 5 then + Result.OpCode := A_CLMULH + end; + + 4: + begin + if funct7 = 0 then + Result.OpCode := A_XOR + else if funct7 = 1 then + Result.OpCode := A_DIV + else if funct7 = 4 then + Result.OpCode := A_PACK // if rs2 = 0, alias: ZEXT.H + else if funct7 = 16 then + Result.OpCode := A_SH2ADD + else if funct7 = 32 then + Result.OpCode := A_XNOR + else if funct7 = 52 then + Result.OpCode := A_BINV + end; + + 5: + begin + if funct7 = 0 then + Result.OpCode := A_SRL + else if funct7 = 1 then + Result.OpCode := A_DIVU + else if funct7 = 5 then + Result.OpCode := A_MINU + else if funct7 = 16 then + Result.OpCode := A_SH3ADD + else if funct7 = 32 then + Result.OpCode := A_SRA + else if funct7 = 36 then + Result.OpCode := A_BEXT + else if funct7 = 48 then + Result.OpCode := A_ROR + end; + + 6: + begin + if funct7 = 0 then + Result.OpCode := A_OR + else if funct7 = 1 then + Result.OpCode := A_REM + else if funct7 = 5 then + Result.OpCode := A_MAX + else if funct7 = 32 then + Result.OpCode := A_ORN + end; + + 7: + begin + if funct7 = 0 then + Result.OpCode := A_AND + else if funct7 = 1 then + Result.OpCode := A_REMU + else if funct7 = 4 then + Result.OpCode := A_PACKH + else if funct7 = 5 then + Result.OpCode := A_MAXU + else if funct7 = 32 then + Result.OpCode := A_ANDN; + end; + end; +end; + +function TRiscvDisassembler.decodeOp32(instr: uint32): TRiscvInstruction; +var + funct3, funct7: byte; +begin + FillByte(Result, SizeOf(Result), 0); + decodeR(instr, funct7, Result.rs2, Result.rs1, funct3, Result.rd); + Result.format := rvfInstructionDestSrc1Src2; + Result.OpCode := A_INVALID; + + case funct3 of + 0: + begin + if funct7 = 0 then + Result.OpCode := A_ADDW + else if funct7 = 1 then + Result.OpCode := A_MULW + else if funct7 = 4 then + Result.OpCode := A_ADD_UW + else if funct7 = 32 then + Result.OpCode := A_SUBW; + end; + + 1: + begin + if funct7 = 0 then + Result.OpCode := A_SLLW + else if funct7 = 48 then + Result.OpCode := A_ROLW; + end; + + 2: + begin + if funct7 = 16 then + Result.OpCode := A_SH1ADD_UW; + end; + + //3: + //begin + // if funct7 = 0 then + // Result.OpCode := A_SLTU; + //end; + + 4: + begin + if funct7 = 1 then + Result.OpCode := A_DIVW + else if funct7 = 4 then + begin + Result.OpCode := A_ZEXT_H; + Result.format := rvfInstructionDestSrc; + end + else if funct7 = 16 then + Result.OpCode := A_SH2ADD_UW; + end; + + 5: + begin + if funct7 = 0 then + Result.OpCode := A_SRLW + else if funct7 = 1 then + Result.OpCode := A_DIVUW + else if funct7 = 32 then + Result.OpCode := A_SRAW + else if funct7 = 48 then + Result.OpCode := A_RORW; + end; + + 6: + begin + if funct7 = 0 then + Result.OpCode := A_OR + else if funct7 = 1 then + Result.OpCode := A_REMW + else if funct7 = 16 then + Result.OpCode := A_SH3ADD_UW + end; + + 7: + begin + if funct7 = 0 then + Result.OpCode := A_AND + else if funct7 = 1 then + Result.OpCode := A_REMUW; + end; + end; +end; + +procedure TRiscvDisassembler.decodeR_fmt(instr: uint32; out rs3, fmt, rs2, rs1, roundingMode, rd: byte); +begin + rs3 := instr shr 27; + fmt := (instr shr 25) and 3; + rs2 := (instr shr 20) and $1F; + rs1 := (instr shr 15) and $1F; + roundingMode := (instr shr 12) and 7; + rd := (instr shr 7) and $1F; +end; + +function TRiscvDisassembler.decodeMAdd(instr: uint32): TRiscvInstruction; +var + roundingMode: byte; +begin + FillByte(Result, SizeOf(Result), 0); + decodeR_fmt(instr, Result.rs3, Result.fpwidth, Result.rs2, Result.rs1, roundingMode, Result.rd); + Result.Size := 4; + Result.format := rvfFloatInstrDestSrc1Src2Src3; + // Rounding mode seems to be ignored + Result.OpCode := A_FMADD; +end; + +function TRiscvDisassembler.decodeMSub(instr: uint32): TRiscvInstruction; +var + roundingMode: byte; +begin + FillByte(Result, SizeOf(Result), 0); + decodeR_fmt(instr, Result.rs3, Result.fpwidth, Result.rs2, Result.rs1, roundingMode, Result.rd); + Result.Size := 4; + Result.format := rvfFloatInstrDestSrc1Src2Src3; + // Rounding mode seems to be ignored + Result.OpCode := A_FMSUB; +end; + +function TRiscvDisassembler.decodeNMSub(instr: uint32): TRiscvInstruction; +var + roundingMode: byte; +begin + FillByte(Result, SizeOf(Result), 0); + decodeR_fmt(instr, Result.rs3, Result.fpwidth, Result.rs2, Result.rs1, roundingMode, Result.rd); + Result.Size := 4; + Result.format := rvfFloatInstrDestSrc1Src2Src3; + // Rounding mode seems to be ignored + Result.OpCode := A_FNMSUB; +end; + +function TRiscvDisassembler.decodeNMAdd(instr: uint32): TRiscvInstruction; +var + roundingMode: byte; +begin + FillByte(Result, SizeOf(Result), 0); + decodeR_fmt(instr, Result.rs3, Result.fpwidth, Result.rs2, Result.rs1, roundingMode, Result.rd); + Result.Size := 4; + Result.format := rvfFloatInstrDestSrc1Src2Src3; + // Rounding mode seems to be ignored + Result.OpCode := A_FNMADD; +end; + +function TRiscvDisassembler.decodeOP_FP(instr: uint32): TRiscvInstruction; +var + funct3, roundingMode: byte; +begin + FillByte(Result, SizeOf(Result), 0); + decodeR_fmt(instr, funct3, Result.fpwidth, Result.rs2, Result.rs1, roundingMode, Result.rd); + Result.Size := 4; + Result.format := rvfFloatInstrDestSrc1Src2; + + // Rounding mode seems to be ignored + case funct3 of + 0: Result.OpCode := A_FADD; + 1: Result.OpCode := A_FSUB; + 2: Result.OpCode := A_FMUL; + 3: Result.OpCode := A_FDIV; + 4: + begin + case roundingMode of + 0: Result.OpCode := A_FSGNJ; + 1: Result.OpCode := A_FSGNJN; + 2: Result.OpCode := A_FSGNJX; + end; + end; + + 5: + begin + if Result.rs2 = 0 then + begin + Result.OpCode := A_FSQRT; + Result.format := rvfFloatInstrDestSrc1; + end; + end; + + 8: + begin + Result.format := rvfFloatInstrDestSrc1; + if (Result.rs2 = 0) and (Result.fpwidth = 1) then + Result.OpCode := A_FCVT_D_S + else if (Result.rs2 = 1) and (Result.fpwidth = 0) then + Result.OpCode := A_FCVT_S_D; + end; + + 11: + begin + case roundingMode of + 0: Result.OpCode := A_FMIN; + 1: Result.OpCode := A_FMAX; + end; + end; + + 20: + begin + case roundingMode of + 0: Result.OpCode := A_FLE; + 1: Result.OpCode := A_FLT; + 2: Result.OpCode := A_FEQ; + end; + end; + + 24: + begin + Result.format := rvfFloatInstrDestSrc1; + case Result.rs2 of + 0, 2: Result.OpCode := A_FCVT_Int_S; + 1, 3: Result.OpCode := A_FCVT_UInt_S; + end; + end; + + 26: + begin + case Result.rs2 of + 0, 2: Result.OpCode := A_FCVT_S_Int; + 1, 3: Result.OpCode := A_FCVT_S_UInt; + end; + end; + end; + +end; + +function TRiscvDisassembler.decodeBranch(instr: uint32): TRiscvInstruction; +var + funct3: byte; +begin + FillByte(Result, SizeOf(Result), 0); + decodeB(instr, Result.imm, Result.rs2, Result.rs1, funct3); + Result.format := rvfInstructionSrc1Src2Imm; + case funct3 of + 0: Result.OpCode := A_BEQ; + 1: Result.OpCode := A_BNE; + 4: Result.OpCode := A_BLT; + 5: Result.OpCode := A_BGE; + 6: Result.OpCode := A_BLTU; + 7: Result.OpCode := A_BGEU; + else + Result.OpCode := A_INVALID; + end; +end; + +function TRiscvDisassembler.decodeJALR(instr: uint32): TRiscvInstruction; +var + funct3: byte; +begin + FillByte(Result, SizeOf(Result), 0); + decodeI(instr, Result.imm, Result.rs1, funct3, Result.rd); + Result.format := rvfInstructionDestSrcOffset; + if funct3 = 0 then + begin + if (Result.rs1 = 1) and (Result.rd = 0) and (Result.imm = 0) then + begin + Result.OpCode := A_RET; + Result.format := rvfInstruction; + end + else + Result.OpCode := A_JALR; + end + else + Result.OpCode := A_INVALID; +end; + +function TRiscvDisassembler.decodeJAL(instr: uint32): TRiscvInstruction; +begin + FillByte(Result, SizeOf(Result), 0); + decodeJ(instr, Result.imm, Result.rd); + Result.format := rvfInstructionDestImm; + Result.OpCode := A_JAL +end; + +function TRiscvDisassembler.decodeSystem(instr: uint32): TRiscvInstruction; +var + funct3, funct7: byte; +begin + FillByte(Result, SizeOf(Result), 0); + decodeI(instr, Result.imm, Result.rs1, funct3, Result.rd); + Result.format := rvfInstruction; + Result.OpCode := A_INVALID; + + if (funct3 = 0) and (Result.rd = 0) and (Result.rs1 = 0) then + begin + if (Result.imm = 0) then + Result.OpCode := A_ECALL + else if (Result.imm = 1) then + Result.OpCode := A_EBREAK + else + begin + Result.rs2 := Result.imm and $1F; + funct7 := Result.imm shr 5; + if (funct7 = $08) and (Result.rs2 = $02) then + Result.OpCode := A_SRET + else if (funct7 = $08) and (Result.rs2 = $05) then + Result.OpCode := A_WFI + else if (funct7 = $18) and (Result.rs2 = $02) then + Result.OpCode := A_MRET; + end; + end + else + begin + Result.csr := Result.imm; + case funct3 of + 1: + begin + Result.OpCode := A_CSRRW; + Result.format := rvfCSRDestCsrSrc1; + end; + + 2: + begin + Result.OpCode := A_CSRRS; + Result.format := rvfCSRDestCsrSrc1; + end; + + 3: + begin + Result.OpCode := A_CSRRC; + Result.format := rvfCSRDestCsrSrc1; + end; + + 5: + begin + Result.OpCode := A_CSRRWI; + Result.imm := Result.rs1; + Result.format := rvfCSRDestCsrImm; + end; + + 6: + begin + Result.OpCode := A_CSRRSI; + Result.imm := Result.rs1; + Result.format := rvfCSRDestCsrImm; + end; + + 7: + begin + Result.OpCode := A_CSRRCI; + Result.imm := Result.rs1; + Result.format := rvfCSRDestCsrImm; + end; + end; + end; +end; + +function TRiscvDisassembler.formatOpcode(instr: TRiscvInstruction): string; +var + s1, s2: string; +begin + Result := RiscvOpcodeString[instr.OpCode]; + if instr.OpCode = A_INVALID then exit; + + case instr.format of + rvfInstructionDestSrcOffset: // lw rd, imm(rs1) + Result := format('%-8s %s, %d(%s)', [Result, RiscvABIRegisterNames[instr.rd], instr.imm, RiscvABIRegisterNames[instr.rs1]]); + + rvfLoadFDestSrcOffset: // flw rd, imm(rs1) + Result := format('%-8s ft%d, %d(%s)', [Result, instr.rd, instr.imm, RiscvABIRegisterNames[instr.rs1]]); + + rvfStoreSrc2Src1Offset: // sw rs2, imm(rs1) + Result := format('%-8s %s, %d(%s)', [Result, RiscvABIRegisterNames[instr.rs2], instr.imm, RiscvABIRegisterNames[instr.rs1]]); + + rvfStoreFSrc2Src1Offset: // fsw rs2, imm(rs1) + Result := format('%-8s ft%d, %d(%s)', [Result, instr.rs2, instr.imm, RiscvABIRegisterNames[instr.rs1]]); + + rvfBranchSrc1Src2Imm: // beq rs1, rs2, imm + Result := format('%-8s %s, %s, %d', [Result, RiscvABIRegisterNames[instr.rs1], RiscvABIRegisterNames[instr.rs2], instr.imm]); + + rvfInstruction: ; // ebreak + + rvfFence: // fence [iorw], [iorw] + begin + // fence iorw, iorw is written as fence + if (instr.imm and $FF) = $FF then exit; + + s1 := ''; + if instr.imm and (1 shl 7) > 0 then + s1 := 'i'; + if instr.imm and (1 shl 6) > 0 then + s1 := s1 + 'o'; + if instr.imm and (1 shl 5) > 0 then + s1 := s1 + 'r'; + if instr.imm and (1 shl 4) > 0 then + s1 := s1 + 'w'; + + s2 := ''; + if instr.imm and (1 shl 3) > 0 then + s2 := 'i'; + if instr.imm and (1 shl 2) > 0 then + s2 := s2 + 'o'; + if instr.imm and (1 shl 1) > 0 then + s2 := s2 + 'r'; + if instr.imm and 1 > 0 then + s2 := s2 + 'w'; + + if (s1 = '') and (s2 = '') then + Result := format('%-8s', [Result]) + else + Result := format('%-8s %s, %s', [Result, s1, s2]); + end; + + rvfCustom: // custom-imm + Result := format('%s-%d', [Result, instr.imm]); + + rvfInstructionSrcOffset: // cbo.clean (rs1) + Result := format('%-8s (%s)', [Result, RiscvABIRegisterNames[instr.rs1]]); + + rvfInstructionDestSrcImm: // addi rd, rs1, imm + Result := format('%-8s %s, %s, %d', [Result, RiscvABIRegisterNames[instr.rd], RiscvABIRegisterNames[instr.rs1], instr.imm]); + + rvfInstructionDestSrc1Src2: // sub rd, rs1, rs2 + Result := format('%-8s %s, %s, %s', [Result, RiscvABIRegisterNames[instr.rd], RiscvABIRegisterNames[instr.rs1], RiscvABIRegisterNames[instr.rs2]]); + + rvfInstructionSrc1Src2Imm: // beq rs1, rs2, imm + Result := format('%-8s %s, %s, %d', [Result, RiscvABIRegisterNames[instr.rs1], RiscvABIRegisterNames[instr.rs2], instr.imm]); + + rvfAmoDestSrc2Src1, rvfAmoDestSrc1, rvfAmoDestSrc2Src1_addr: + begin + s1 := ''; + if instr.AmoInfo.width = 2 then + s1 := '.w' + else if instr.AmoInfo.width = 3 then + s1 := '.d'; + + s2 := ''; + if instr.AmoInfo.aquire then + s2 := s2 + 'aq'; + if instr.AmoInfo.release then + s2 := s2 + 'rel'; + if s2 <> '' then + s2 := '.' + s2; + + Result := Result + s1 + s2; + if instr.format = rvfAmoDestSrc2Src1 then // sc.w rd, rs2, rs1 + Result := format('%-8s %s, %s, %s', [Result, RiscvABIRegisterNames[instr.rd], RiscvABIRegisterNames[instr.rs2], RiscvABIRegisterNames[instr.rs1]]) + else if instr.format = rvfAmoDestSrc1 then // lr.w rd, rs1 + Result := format('%-8s %s, %s', [Result, RiscvABIRegisterNames[instr.rd], RiscvABIRegisterNames[instr.rs1]]) + else if instr.format = rvfAmoDestSrc2Src1_addr then // amoswap.w.aq rd, rs2, (rs1) + Result := format('%-8s %s, %s, (%s)', [Result, RiscvABIRegisterNames[instr.rd], RiscvABIRegisterNames[instr.rs2], RiscvABIRegisterNames[instr.rs1]]); + end; + + rvfInstructionDestImm: // auipc rd, imm + Result := format('%-8s %s, 0x%x', [Result, RiscvABIRegisterNames[instr.rd], instr.imm]); + + rvfInstructionDestSrc: // clzw rd, rs1 + Result := format('%-8s %s, %s', [Result, RiscvABIRegisterNames[instr.rd], RiscvABIRegisterNames[instr.rs1]]); + + rvfFloatInstrDestSrc1, rvfFloatInstrDestSrc1Src2, rvfFloatInstrDestSrc1Src2Src3: + begin + case instr.fpwidth of + 0: s1 := '.S'; + 1: s1 := '.D'; + 2: s1 := '.H'; + 3: s1 := '.Q'; + else + s1 := '?'; + end; + + Result := Result + s1; + if instr.format = rvfFloatInstrDestSrc1 then // fsqrt.s rd, rs1 + Result := format('%-8s ft%d, ft%d', [Result, instr.rd, instr.rs1]) + else if instr.format = rvfFloatInstrDestSrc1Src2 then // fsub.s rd, rs1, rs2 => rd = rs1 - rs2 + Result := format('%-8s ft%d, ft%d, ft%d', [Result, instr.rd, instr.rs1, instr.rs2]) + else if instr.format = rvfFloatInstrDestSrc1Src2Src3 then // fmadd.s rd, rs1, rs2, rs3 => (rs1 x rs2) + rs3 + Result := format('%-8s ft%d, ft%d, ft%d, ft%d', [Result, instr.rd, instr.rs1, instr.rs2, instr.rs3]); + end; + + rvfCSRDestCsrSrc1: // csrrw rd, #csr, rs1 + Result := format('%-8s %s, %s, %s', [Result, RiscvABIRegisterNames[instr.rd], CsrRegNumToName(instr.csr), RiscvABIRegisterNames[instr.rs1]]); + rvfCSRDestCsrImm: // csrrw rd, #csr, imm + Result := format('%-8s %s, %s, %s', [Result, RiscvABIRegisterNames[instr.rd], CsrRegNumToName(instr.csr), instr.imm]); + else + Result := 'Invalid format specifier'; + end; +end; + +{ =========================== 16 bit decoding ============================ +Format Meaning 15 14 13 12 11 10 09 08 07 06 05 04 03 02 0100 + CR Register funct4 | rd/rs1 | rs2 | op + CI Immediate funct3 |im| rd/rs1 | imm | op + CSS Stack-relative Store funct3 | imm | rs2 | op + CIW Wide Immediate funct3 | imm | rd' | op + CL Load funct3 | imm | rs1' | imm | rd' | op + CS Store funct3 | imm | rs1' | imm | rs2' | op + CA Arithmetic funct6 | rd/rs' |fnc2 | rs2' | op + CB Branch/Arithmetic funct3 | offset | rd/rs' | offset | op + CJ Jump funct3 | jump target | op +} + +// Imm is 8 bits +procedure TRiscvDisassembler.decodeCIW(instr: uint16; out imm: uint16; out rd_: byte); +begin + imm := (instr and $1FFF) shr 5; + rd_ := ((instr and $1C) shr 2) + 8; +end; + +// Compressed load & store decoding +// Imm is 5 bits wide and shifted according to immType +procedure TRiscvDisassembler.decodeCLS(instr: uint16; out imm: uint16; out rs1_, rd_rs2_: byte; const immType: TImmEncoding); +begin + case immType of + ieImm5376: imm := ((instr and $1C00) shr 7) or ((instr and $0060) shl 1); + ieImm54876: imm := ((instr and $0400) shr 2) or ((instr and $1800) shr 7) or ((instr and $60) shl 1); + ieImm5326: imm := ((instr and $1C00) shr 7) or ((instr and $0040) shr 4) or (instr and $20) shl 1; + end; + rs1_ := ((instr and $380) shr 7) + 8; + rd_rs2_ := ((instr and $1C) shr 2) + 8; +end; + +procedure TRiscvDisassembler.decodeCI(instr: uint16; out rs1_rd: byte; out imm: byte); +begin + rs1_rd := ((instr and $0F80) shr 7); + imm := ((instr and $1000) shr 7) or ((instr and $7C) shr 2); +end; + +procedure TRiscvDisassembler.decodeCJ(instr: uint16; out imm: int16); +begin + // instr[12:2] = 12 11 10 09 08 07 06 05 04 03 02 + // imm = 11 04 09 08 10 06 07 03 02 01 05 + imm := ((instr and $1000) shr 1) or // 12 => 11 + ((instr and $0800) shr 7) or // 11 => 4 + ((instr and $0600) shr 1) or // 10:9 => 9:8 + ((instr and $0100) shl 2) or // 8 => 10 + ((instr and $0080) shr 1) or // 7 => 6 + ((instr and $0040) shl 1) or // 6 => 7 + ((instr and $0038) shr 2) or // 5:3 => 3:1 + ((instr and $0004) shl 3); // 2 => 5 + + // Check if sign must be extended + if (imm and $800) > 0 then + imm := imm or int16($F000); +end; + +procedure TRiscvDisassembler.decodeCB(instr: uint16; out rs1_: byte; out imm: int16); +begin +{ Format Meaning 15 14 13 12 11 10 09 08 07 06 05 04 03 02 0100 + CB Branch/Arithmetic funct3 | offset | rd/rs | offset | op +} + rs1_ := ((instr and $0380) shr 7) + 8; + // instr[12:2] = 12 11 10 06 05 04 03 02 + // imm = 08 04 03 07 06 02 01 05 + imm := ((instr and $1000) shr 4) or // 12 => 8 + ((instr and $0C00) shr 7) or // 11:10 => 4:3 + ((instr and $0060) shl 1) or // 6:5 => 7:6 + ((instr and $0018) shr 2) or // 4:3 => 2:1 + ((instr and $0004) shl 3); // 2 => 5 + + if (imm and $100) > 0 then + imm := imm or int16($FE00); +end; + +procedure TRiscvDisassembler.decodeCSS(instr: uint16; out uimm: uint16; out rs2: byte; const immtype: TImmEncoding2); +begin + rs2 := (instr shr 2) and $1F; + uimm := instr and $1F80; + case immtype of + // instr[12:10] => uimm[5:3] | instr[9:7] => uimm[8:6] + ie2Imm5386: uimm := ((uimm and $1C00) shr 7) or ((uimm and $0380) shr 1); + + // instr[12:11] => uimm[5:4] | instr[10:7] => uimm[9:6] + ie2Imm5496: uimm := ((uimm and $1800) shr 7) or ((uimm and $0780) shr 1); + + // instr[12:9] => uimm[5:2] | instr[8:7] => uimm[7:6] + ie2Imm5276: uimm := ((uimm and $1E00) shr 7) or ((uimm and $0180) shr 1); + end; +end; + +function TRiscvDisassembler.decodeAddi4spn(instr: uint16):TRiscvInstruction; +var + imm: uint16; +begin + FillByte(Result, SizeOf(Result), 0); + Result.OpCode := A_INVALID; + decodeCIW(instr, imm, Result.rd); + + if imm > 0 then + begin + // [7:6|5:2|1|0] + // [5:4|9:6|2|3] + Result.imm := ((imm and $C0) shr 2) or // 7:6 => 5:4 + ((imm and $3C) shl 4) or // 5:2 => 9:6 + ((imm and $02) shl 1) or // 1 => 2 + ((imm and $01) shl 3); // 0 => 3 + Result.rs1 := 2; + Result.OpCode := A_ADDI; + Result.format := rvfInstructionDestSrcImm; + end + else if Result.rd = 0 then + begin + Result.OpCode := A_ILL; + Result.format := rvfInstruction; + end; +end; + +function TRiscvDisassembler.decodeFLD(instr: uint16):TRiscvInstruction; +var + imm: uint16; +begin + FillByte(Result, SizeOf(Result), 0); + decodeCLS(instr, imm, Result.rs1, Result.rd, ieImm5376); + Result.imm := imm; + // For RV32/64. For RV128 this would be LQ + Result.OpCode := A_FLD; + Result.format := rvfLoadFDestSrcOffset; +end; + +function TRiscvDisassembler.decodeLW(instr: uint16):TRiscvInstruction; +var + imm: uint16; +begin + FillByte(Result, SizeOf(Result), 0); + decodeCLS(instr, imm, Result.rs1, Result.rd, ieImm5326); + Result.imm := imm; + Result.OpCode := A_LW; + Result.format := rvfInstructionDestSrcOffset; +end; + +function TRiscvDisassembler.decodeFLW(instr: uint16):TRiscvInstruction; +var + imm: uint16; +begin + FillByte(Result, SizeOf(Result), 0); + decodeCLS(instr, imm, Result.rs1, Result.rd, ieImm5326); + Result.imm := imm; + // For RV32. For RV64/128 this would be LD + Result.OpCode := A_FLW; + Result.format := rvfLoadFDestSrcOffset; +end; + +function TRiscvDisassembler.decodeFSD(instr: uint16):TRiscvInstruction; +var + imm: uint16; +begin + FillByte(Result, SizeOf(Result), 0); + decodeCLS(instr, imm, Result.rs1, Result.rs2, ieImm5326); + Result.imm := imm; + // For RV32/64. For RV128 this would be SQ + Result.OpCode := A_FSD; + Result.format := rvfStoreFSrc2Src1Offset; +end; + +function TRiscvDisassembler.decodeSW(instr: uint16):TRiscvInstruction; +var + imm: uint16; +begin + FillByte(Result, SizeOf(Result), 0); + decodeCLS(instr, imm, Result.rs1, Result.rs2, ieImm5326); + Result.imm := imm; + Result.OpCode := A_SW; + Result.format := rvfStoreSrc2Src1Offset; +end; + +function TRiscvDisassembler.decodeFSW(instr: uint16):TRiscvInstruction; +var + imm: uint16; +begin + FillByte(Result, SizeOf(Result), 0); + decodeCLS(instr, imm, Result.rs1, Result.rs2, ieImm5376); + Result.imm := imm; + // For RV32. For RV64/128 this would be SD + Result.OpCode := A_FSW; + Result.format := rvfStoreFSrc2Src1Offset; +end; + +function TRiscvDisassembler.decodeAddI(instr: uint16): TRiscvInstruction; +var + imm: byte; +begin + FillByte(Result, SizeOf(Result), 0); + decodeCI(instr, Result.rd, imm); + Result.imm := imm; + // Check sign bit + if imm > $1F then + Result.imm := Result.imm or int32($FFFFFFC0); + Result.rs1 := Result.rd; + // For RV32. For RV64/128 this would be SD + if (imm = 0) and (Result.rd = 0) then + begin + Result.OpCode := A_NOP; + Result.format := rvfInstruction; + end + else + begin + Result.OpCode := A_ADDI; + Result.format := rvfInstructionDestSrcImm; + end; +end; + +function TRiscvDisassembler.decodeCJAL(instr: uint16): TRiscvInstruction; +var + imm: int16; +begin + FillByte(Result, SizeOf(Result), 0); + decodeCJ(instr, imm); + Result.imm := imm; + Result.OpCode := A_JAL; + Result.rd := 1; + Result.format := rvfInstructionDestImm; +end; + +function TRiscvDisassembler.decodeLI(instr: uint16): TRiscvInstruction; +var + imm: byte; +begin + FillByte(Result, SizeOf(Result), 0); + decodeCI(instr, Result.rd, imm); + Result.rs1 := 0; + + Result.imm := imm; + // Check sign bit + if (imm and $20) > 0 then + Result.imm := Result.imm or int32($FFFFFFC0); + Result.OpCode := A_ADDI; + Result.format := rvfInstructionDestSrcImm; +end; + +function TRiscvDisassembler.decodeLUI(instr: uint16): TRiscvInstruction; +var + imm: byte; +begin + FillByte(Result, SizeOf(Result), 0); + decodeCI(instr, Result.rd, imm); + Result.rs1 := 0; + + if imm = 0 then + begin + Result.OpCode := A_RESERVED; + Result.format := rvfInstruction; + end + else if Result.rd = 2 then + begin + Result.imm := (int32(imm and $20) shl 4) or // 5 => 9 + (int32(imm and $10) shl 0) or // 4 => 4 + (int32(imm and $08) shl 3) or // 3 => 6 + (int32(imm and $06) shl 6) or // 2:1 => 8:7 + (int32(imm and $01) shl 5); // 0 => 5 + Result.rs1 := 2; + // Check sign bit + if (imm and $20) > 0 then + Result.imm := Result.imm or int32($FFFFFC00); + Result.OpCode := A_ADDI; + Result.format := rvfInstructionDestSrcImm; + end + else // if rd = 0 then hint + begin + Result.imm := imm; + // Check sign bit + if (imm and $20) > 0 then + Result.imm := Result.imm or int32($FFFFFC00); + Result.OpCode := A_LUI; + Result.format := rvfInstructionDestImm; + end; +end; + +function TRiscvDisassembler.decodeALU(instr: uint16):TRiscvInstruction; +var + imm5, imm40, rs_rd, rs2, funct2: byte; +begin + FillByte(Result, SizeOf(Result), 0); + + imm5 := (instr shr 12) and 1; + rs_rd := ((instr shr 7) and $07) + 8; + imm40 := (instr shr 2) and $1F; + funct2 := (instr shr 10) and $03; + + Result.rd := rs_rd; + Result.rs1 := rs_rd; + case funct2 of + 0: + begin + Result.imm := (imm5 shl 5) or imm40; + Result.OpCode := A_SRLI; + Result.format := rvfInstructionDestSrcImm; + end; + + 1: + begin + Result.imm := (imm5 shl 5) or imm40; + Result.OpCode := A_SRAI; + Result.format := rvfInstructionDestSrcImm; + end; + + 2: + begin + Result.imm := (imm5 shl 5) or imm40; + // Check sign + if imm5 = 1 then + Result.imm := Result.imm or int32($FFFFFFC0); + Result.OpCode := A_ANDI; + Result.format := rvfInstructionDestSrcImm; + end; + + 3: + begin + if imm5 = 1 then + begin + // TODO: Ignore 32 bit operations W on RV64/RV128 + Result.OpCode := A_RESERVED; + Result.format := rvfInstruction; + end + else + begin + funct2 := imm40 shr 3; + Result.rs2 := (imm40 and $03) + 8; + Result.format := rvfInstructionDestSrc1Src2; + case funct2 of + 0: Result.OpCode := A_SUB; + 1: Result.OpCode := A_XOR; + 2: Result.OpCode := A_OR; + 3: Result.OpCode := A_AND; + end; + end; + end; + end; +end; + +function TRiscvDisassembler.decodeCJ(instr: uint16): TRiscvInstruction; +var + imm: int16; +begin + FillByte(Result, SizeOf(Result), 0); + decodeCJ(instr, imm); + Result.imm := imm; + Result.OpCode := A_JAL; + Result.rd := 0; + Result.format := rvfInstructionDestImm; +end; + +function TRiscvDisassembler.decodeCBNEQZ(instr: uint16; funct3: byte): TRiscvInstruction; +var + imm: int16; +begin + FillByte(Result, SizeOf(Result), 0); + decodeCB(instr, Result.rs1, imm); + Result.imm := imm; + Result.format := rvfInstructionSrc1Src2Imm; + Result.rs2 := 0; + if funct3 = 6 then + Result.OpCode := A_BEQ + else + Result.OpCode := A_BNE; +end; + +function TRiscvDisassembler.decodeSLLI(instr: uint16): TRiscvInstruction; +var + imm: byte; +begin + FillByte(Result, SizeOf(Result), 0); + decodeCI(instr, Result.rd, imm); + // For RV32C, if imm = 0, then HINT + Result.imm := imm; + Result.rs1 := Result.rd; + if Result.imm < $20 then // Restriction only for RV32C + begin + Result.OpCode := A_SLLI; + Result.format := rvfInstructionDestSrcImm; + end + else + begin + Result.OpCode := A_CUSTOM; + Result.format := rvfCustom; + end; +end; + +function TRiscvDisassembler.decodeFLDSP(instr: uint16): TRiscvInstruction; +var + imm: byte; +begin + FillByte(Result, SizeOf(Result), 0); + decodeCI(instr, Result.rd, imm); + + Result.imm := (imm and $38) or + (imm and $07) shl 6; + Result.rs1 := 2; + // TODO: RV128C => C.LQSP + Result.OpCode := A_FLD; + Result.format := rvfLoadFDestSrcOffset; +end; + +function TRiscvDisassembler.decodeLWSP(instr: uint16): TRiscvInstruction; +var + imm: byte; +begin + FillByte(Result, SizeOf(Result), 0); + decodeCI(instr, Result.rd, imm); + + // uimm: 05 04 03 02 01 00 + // imm: 05 04 03 02 07 06 + Result.imm := (imm and $3C) or + (imm and $03) shl 6; + Result.rs1 := 2; + Result.OpCode := A_LW; + Result.format := rvfInstructionDestSrcOffset; +end; + +function TRiscvDisassembler.decodeFLWSP(instr: uint16): TRiscvInstruction; +var + imm: byte; +begin + FillByte(Result, SizeOf(Result), 0); + decodeCI(instr, Result.rd, imm); + + Result.imm := (imm and $30) or + (imm and $0F) shl 6; + Result.rs1 := 2; + // TODO: RV32/RV128C => C.LDSP + if Result.rd = 8 then + begin + Result.OpCode := A_RESERVED; + Result.format := rvfInstruction; + end + else + begin + Result.OpCode := A_FLW; + Result.format := rvfLoadFDestSrcOffset; + end; +end; + +function TRiscvDisassembler.decodeJR(instr: uint16): TRiscvInstruction; +var + imm: byte; + imm5: boolean; +begin + FillByte(Result, SizeOf(Result), 0); + decodeCI(instr, Result.rd, imm); + + imm5 := imm and $20 = $20; + Result.rs2 := (imm and $1F); + Result.rs1 := Result.rd; + + if not imm5 then + begin + // TODO: RV32/RV128C => C.LDSP + if Result.rs2 = 0 then + begin + if Result.rs1 = 0 then + begin + Result.OpCode := A_RESERVED; + Result.format := rvfInstruction; + end + else + begin + Result.rd := 0; + if (Result.rs1 = 1) and (Result.imm = 0) then + begin + Result.OpCode := A_RET; + Result.format := rvfInstruction; + end + else + begin + Result.OpCode := A_JALR; + Result.format := rvfInstructionDestSrcOffset; + end; + end + end + else // rs2 > 0 + begin + // if Result.rd = 0 then hint else + Result.rs1 := 0; + Result.OpCode := A_ADD; + Result.format := rvfInstructionDestSrc1Src2; + end; + end + else // imm5 > 0 + begin + if (Result.rd = 0) and (Result.rs2 = 0) then + begin + Result.OpCode := A_EBREAK; + Result.format := rvfInstruction; + end + else if Result.rs2 = 0 then + begin + Result.rd := 1; + Result.OpCode := A_JALR; + Result.format := rvfInstructionDestSrcOffset; + end + else // if rs2 > 0 else hint + begin + Result.OpCode := A_ADD; + Result.format := rvfInstructionDestSrc1Src2; + end; + end; +end; + +function TRiscvDisassembler.decodeCStore(instr: uint16; funct3: byte): TRiscvInstruction; +var + uimm: uint16; +begin + FillByte(Result, SizeOf(Result), 0); + Result.rs1 := 2; + + case funct3 of + 5: + begin + // if RV32 then + decodeCSS(instr, uimm, Result.rs2, ie2Imm5386); + Result.imm := uimm; + Result.OpCode := A_FSD; + Result.format := rvfStoreFSrc2Src1Offset; + end; + + 6: + begin + decodeCSS(instr, uimm, Result.rs2, ie2Imm5276); + Result.imm := uimm; + Result.OpCode := A_SW; + Result.format := rvfStoreSrc2Src1Offset; + end; + + 7: + begin + // if RV32 then + decodeCSS(instr, uimm, Result.rs2, ie2Imm5276); + Result.imm := uimm; + Result.OpCode := A_FSD; + Result.format := rvfStoreFSrc2Src1Offset; + end; + end; +end; + +initialization + DebugLogger.FindOrRegisterLogGroup('DBG_WARNINGS' {$IFDEF DBG_WARNINGS} , True {$ENDIF} ); + +end. diff --git a/components/fpdebug/fpdbgriscvclasses.pas b/components/fpdebug/fpdbgriscvclasses.pas new file mode 100644 index 0000000000..455b418896 --- /dev/null +++ b/components/fpdebug/fpdbgriscvclasses.pas @@ -0,0 +1,297 @@ +unit FpDbgRiscvClasses; + +{$mode objfpc}{$H+} +{$packrecords c} +{$modeswitch advancedrecords} + +interface + +uses + Classes, + SysUtils, + FpDbgClasses, + DbgIntfBaseTypes, DbgIntfDebuggerBase, + {$ifdef FORCE_LAZLOGGER_DUMMY} LazLoggerDummy {$else} LazLoggerBase {$endif}, + FpDbgRsp, FpDbgRspClasses, FpDbgCommon, FpdMemoryTools, + FpErrorMessages; + +const + SPindex = 2; + SPindexDwarf = 2; // Also known as a1, Index refers to active window + ReturnAddressIndex = 1; + ReturnAddressIndexDwarf = 1; + PCIndexDwarf = 32; // Dwarf index + nPC = 'pc'; + nReturnPC = 'ra'; + nSP = 'sp'; + +type + { TDbgRiscvThread } + + TDbgRiscvThread = class(TDbgRspThread) + private + const + lastCPURegIndex = 32; // 32 registers + PC + // Offsets to load specific registers from register data + // These are byte offsets, to be used when reading from the raw byte register data + RegArrayByteLength = 33*4; // Depends on qemu options, but this seems to be the smallest size to handle. Only show basic registers, so rest can be ignored for now. + PCindex = 32; + protected + procedure LoadRegisterCache; override; + procedure SaveRegisterCache; override; + function GetReturnPC: TDbgPtr; + function GetStackUnwinder: TDbgStackUnwinder; override; + public + destructor Destroy; override; + procedure LoadRegisterValues; override; + procedure SetRegisterValue(AName: string; AValue: QWord); override; + function GetInstructionPointerRegisterValue: TDbgPtr; override; + function GetStackBasePointerRegisterValue: TDbgPtr; override; + // procedure SetInstructionPointerRegisterValue(AValue: TDbgPtr); override; + function GetStackPointerRegisterValue: TDbgPtr; override; + // procedure SetStackPointerRegisterValue(AValue: TDbgPtr); override; + end; + + { TDbgRiscvProcess } + + TDbgRiscvProcess = class(TDbgRspProcess) + private const + FNumRegisters = 33; // x0..x31,pc + protected + function CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread; override; + function CreateBreakPointTargetHandler: TFpBreakPointTargetHandler; override; + public + class function isSupported(target: TTargetDescriptor): boolean; override; + constructor Create(const AFileName: string; AnOsClasses: TOSDbgClasses; + AMemManager: TFpDbgMemManager; AMemModel: TFpDbgMemModel; + AProcessConfig: TDbgProcessConfig = nil); override; + destructor Destroy; override; + end; + + TRiscvBreakInfo = object + const + _CODE: Word = $9002; // c.ebreak -> this is a 16 bit instruction + end; + + TRiscvBreakPointTargetHandler = specialize TRspBreakPointTargetHandler; + +implementation + +uses + FpDbgDisasRiscv, FpDbgDwarfDataClasses; + +var + DBG_VERBOSE, DBG_WARNINGS: PLazLoggerLogGroup; + +{ TDbgRiscvThread } + +procedure TDbgRiscvThread.LoadRegisterCache; +var + regs: TBytes; + i: integer; +begin + if not FRegs.Initialized then + begin + SetLength(regs, RegArrayByteLength); + FRegs.Initialized := TDbgRiscvProcess(Process).RspConnection.ReadRegisters(regs[0], length(regs)); + // 32 bit LE registers + for i := 0 to lastCPURegIndex do // x0..x31, PC + FRegs.regs[i] := regs[4*i] + (regs[4*i + 1] shl 8) + (regs[4*i + 2] shl 16) + (regs[4*i + 3] shl 24); + end; +end; + +procedure TDbgRiscvThread.SaveRegisterCache; + procedure CopyDWordToByteArray( const val: DWord; regs: PByte); + begin + regs[0] := val; + regs[1] := val shr 8; + regs[2] := val shr 16; + regs[3] := val shr 24; + end; + +var + regs: TBytes; + i: Integer; +begin + exit; // TODO: Need to determine which other registers will also change in case of a subroutine call on the debugger side. + + if FRegsChanged then + begin + SetLength(regs, RegArrayByteLength); + for i := 0 to lastCPURegIndex do + CopyDWordToByteArray(FRegs.regs[i], @regs[4*i]); + end; +end; + +function TDbgRiscvThread.GetReturnPC: TDbgPtr; +begin + Result := 0; + if TDbgRiscvProcess(Process).FIsTerminating then + begin + DebugLn(DBG_WARNINGS, 'TDbgRiscvProcess.GetStackPointerRegisterValue called while FIsTerminating is set.'); + exit; + end; + + if not ReadThreadState then + exit; + + DebugLn(DBG_VERBOSE, 'TDbgRiscvProcess.GetStackPointerRegisterValue requesting stack registers.'); + ReadDebugReg(PCindex, result); +end; + +function TDbgRiscvThread.GetStackUnwinder: TDbgStackUnwinder; +begin + if FUnwinder = nil then + FUnwinder := TDbgStackUnwinderRiscv.Create(Process); + Result := FUnwinder; +end; + +destructor TDbgRiscvThread.Destroy; +begin + if Assigned(FUnwinder) then + FreeAndNil(FUnwinder); + inherited Destroy; +end; + +procedure TDbgRiscvThread.LoadRegisterValues; +var + i: integer; +begin + if TDbgRiscvProcess(Process).FIsTerminating or (TDbgRiscvProcess(Process).FStatus = SIGHUP) then + begin + DebugLn(DBG_WARNINGS, 'TDbgRiscvProcess.LoadRegisterValues called while FIsTerminating is set.'); + exit; + end; + + if not ReadThreadState then + exit; + + LoadRegisterCache; + + if FRegs.Initialized then + begin + { FRegs.regs starts with 32 core registers, then PC + Todo: Where does floating point and vector registers fit in? } + + for i := 0 to 32 do + FRegisterValueList.DbgRegisterAutoCreate[RiscvABIRegisterNames[i]].SetValue(FRegs.regs[i], IntToStr(FRegs.regs[i]), 4, i); + + FRegisterValueListValid := true; + end + else + DebugLn(DBG_WARNINGS, 'Warning: Could not update registers'); +end; + +procedure TDbgRiscvThread.SetRegisterValue(AName: string; AValue: QWord); +var + i: integer; + res: boolean; +begin + res := false; + for i := low(RiscvABIRegisterNames) to high(RiscvABIRegisterNames) do + if AName = RiscvABIRegisterNames[i] then + begin + res := TDbgRspProcess(Process).RspConnection.WriteDebugReg(i, byte(AValue)); + break; + end; + + if not res then + DebugLn(DBG_WARNINGS, 'Error setting register %s to %u', [AName, AValue]); +end; + +function TDbgRiscvThread.GetInstructionPointerRegisterValue: TDbgPtr; +begin + Result := 0; + if TDbgRiscvProcess(Process).FIsTerminating then + begin + DebugLn(DBG_WARNINGS, 'TDbgRiscvProcess.GetInstructionPointerRegisterValue called while FIsTerminating is set.'); + exit; + end; + + if not ReadThreadState then + exit; + + DebugLn(DBG_VERBOSE, 'TDbgRiscvProcess.GetInstructionPointerRegisterValue requesting PC.'); + ReadDebugReg(PCindex, Result); +end; + +function TDbgRiscvThread.GetStackBasePointerRegisterValue: TDbgPtr; +begin + Result := 0; + if TDbgRiscvProcess(Process).FIsTerminating then + begin + DebugLn(DBG_WARNINGS, 'TDbgRiscvProcess.GetStackBasePointerRegisterValue called while FIsTerminating is set.'); + exit; + end; + + if not ReadThreadState then + exit; + + DebugLn(DBG_VERBOSE, 'TDbgRiscvProcess.GetStackBasePointerRegisterValue requesting base registers.'); + // Todo: check FPC implementation of stack frame for riscv + Result := GetStackPointerRegisterValue; +end; + +function TDbgRiscvThread.GetStackPointerRegisterValue: TDbgPtr; +begin + Result := 0; + if TDbgRiscvProcess(Process).FIsTerminating then + begin + DebugLn(DBG_WARNINGS, 'TDbgRiscvProcess.GetStackPointerRegisterValue called while FIsTerminating is set.'); + exit; + end; + + if not ReadThreadState then + exit; + + DebugLn(DBG_VERBOSE, 'TDbgRiscvProcess.GetStackPointerRegisterValue requesting stack registers.'); + if not ReadDebugReg(SPindex, result) then + Result := 0; +end; + +{ TDbgRiscvProcess } + +function TDbgRiscvProcess.CreateThread(AthreadIdentifier: THandle; out IsMainThread: boolean): TDbgThread; +begin + IsMainThread:=False; + if AthreadIdentifier<>feInvalidHandle then + begin + IsMainThread := AthreadIdentifier=ProcessID; + result := TDbgRiscvThread.Create(Self, AthreadIdentifier, AthreadIdentifier); + end + else + result := nil; +end; + +function + TDbgRiscvProcess.CreateBreakPointTargetHandler: TFpBreakPointTargetHandler; +begin + Result := TRiscvBreakPointTargetHandler.Create(Self); +end; + +constructor TDbgRiscvProcess.Create(const AFileName: string; + AnOsClasses: TOSDbgClasses; AMemManager: TFpDbgMemManager; + AMemModel: TFpDbgMemModel; AProcessConfig: TDbgProcessConfig); +begin + FRegArrayLength := FNumRegisters; + inherited Create(AFileName, AnOsClasses, AMemManager, AMemModel, AProcessConfig); +end; + +destructor TDbgRiscvProcess.Destroy; +begin + inherited Destroy; +end; + +class function TDbgRiscvProcess.isSupported(target: TTargetDescriptor): boolean; +begin + result := (target.OS = osEmbedded) and (target.machineType = mtRISCV); +end; + +initialization + DBG_VERBOSE := DebugLogger.FindOrRegisterLogGroup('DBG_VERBOSE' {$IFDEF DBG_VERBOSE} , True {$ENDIF} ); + DBG_WARNINGS := DebugLogger.FindOrRegisterLogGroup('DBG_WARNINGS' {$IFDEF DBG_WARNINGS} , True {$ENDIF} ); + RegisterDbgOsClasses(TOSDbgClasses.Create( + TDbgRiscvProcess, + TDbgRiscvThread, + TRiscvAsmDecoder)); +end. diff --git a/components/fpdebug/fpdebug.lpk b/components/fpdebug/fpdebug.lpk index fea9652618..66cd8133b4 100644 --- a/components/fpdebug/fpdebug.lpk +++ b/components/fpdebug/fpdebug.lpk @@ -237,6 +237,14 @@ File(s) with other licenses (see also header in file(s): + + + + + + + + diff --git a/components/fpdebug/fpdebug.pas b/components/fpdebug/fpdebug.pas index 462d01ed0b..bc02b9c02f 100644 --- a/components/fpdebug/fpdebug.pas +++ b/components/fpdebug/fpdebug.pas @@ -16,8 +16,8 @@ uses FpDbgDwarfDataClasses, FpDbgDwarfFreePascal, fpDbgSymTableContext, fpDbgSymTable, FpDbgAvrClasses, FpDbgDisasAvr, FpDbgRsp, FpDbgCommon, FpImgReaderWinPETypes, FpDbgHardcodedFreepascalInfo, FpDbgCallContextInfo, - FpWatchResultData, FpDbgDwarfCFI, FpDbgCpuX86, FpDbgRspClasses, - FpDbgDisasXtensa, FpDbgXtensaClasses; + FpWatchResultData, FpDbgDwarfCFI, FpDbgCpuX86, FpDbgRspClasses, + FpDbgDisasRiscv, FpDbgRiscvClasses, FpDbgDisasXtensa, FpDbgXtensaClasses; implementation diff --git a/components/fpdebug/fpimgreaderelf.pas b/components/fpdebug/fpimgreaderelf.pas index bc0e8ae959..aef12201c6 100644 --- a/components/fpdebug/fpimgreaderelf.pas +++ b/components/fpdebug/fpimgreaderelf.pas @@ -142,6 +142,7 @@ begin EM_X86_64: result := mtX86_64; EM_AVR: result := mtAVR8; EM_XTENSA: result := mtXTENSA; + EM_RISCV: result := mtRISCV; EM_ALPHA: result := mtALPHA; else result := mtNone; @@ -150,7 +151,7 @@ begin // If OS is not encoded in header, take some guess based on machine type if FTargetInfo.OS = osNone then begin - if result in [mtAVR8, mtXTENSA] then + if result in [mtAVR8, mtXTENSA, mtRISCV] then FTargetInfo.OS := osEmbedded else // Default to the same as host... diff --git a/components/fpdebug/fpimgreaderelftypes.pas b/components/fpdebug/fpimgreaderelftypes.pas index b6d95a8f6a..74bf5d7952 100644 --- a/components/fpdebug/fpimgreaderelftypes.pas +++ b/components/fpdebug/fpimgreaderelftypes.pas @@ -155,6 +155,7 @@ const EM_X86_64 = 62; EM_AVR = 83; EM_XTENSA = 94; + EM_RISCV = 243; EM_ALPHA = $9026; //unofficial, but used by gnu toolchain //elf version {Elf32_Hdr.e_version} diff --git a/components/lazdebuggers/lazdebuggerfprspremote/rspremotedebugger.pas b/components/lazdebuggers/lazdebuggerfprspremote/rspremotedebugger.pas index 991f889594..3566564f19 100644 --- a/components/lazdebuggers/lazdebuggerfprspremote/rspremotedebugger.pas +++ b/components/lazdebuggers/lazdebuggerfprspremote/rspremotedebugger.pas @@ -16,7 +16,7 @@ uses FpPascalParser, FPDbgController, FpDbgDwarfDataClasses, FpDbgDwarfFreePascal, FpDbgDwarf, FpDbgUtil, DebuggerPropertiesBase, - FpDbgRsp, FpDbgAvrClasses, FpDbgXtensaClasses; + FpDbgRsp, FpDbgRspClasses, FpDbgAvrClasses, FpDbgXtensaClasses, FpDbgRiscvClasses; type