From 6cdbfbe950c37117ee9dbe52b8235a4ad6ea6905 Mon Sep 17 00:00:00 2001 From: Nav Date: Tue, 23 Jul 2024 21:14:22 +0100 Subject: [PATCH] Massive refactor to accommodate RISC-V targets - Refactored entire codebase (excluding the Insight component) to accommodate multiple target architectures (no longer specific to AVR) - Deleted 'generate SVD' GDB monitor command - I will eventually move this functionality to the Bloom website - Added unit size property to address spaces - Many other changes which I couldn't be bothered to describe here --- .../TargetDescriptionFiles/AddressSpace.php | 4 +- .../TargetDescriptionFiles/MemorySegment.php | 5 +- .../Services/AtdfService.php | 1 + .../Services/ValidationService.php | 2 +- .../Services/Xml/FromXmlService.php | 1 + .../Services/Xml/ToXmlService.php | 4 + src/Application.cpp | 117 +- src/Application.hpp | 8 +- src/DebugServer/CMakeLists.txt | 6 +- src/DebugServer/DebugServerComponent.cpp | 8 +- src/DebugServer/DebugServerComponent.hpp | 2 +- src/DebugServer/Gdb/AvrGdb/AvrGdbRsp.cpp | 106 +- .../CommandPackets/EepromFill.cpp | 60 +- .../Gdb/AvrGdb/CommandPackets/EepromFill.hpp | 37 + .../Gdb/AvrGdb/CommandPackets/FlashDone.cpp | 48 +- .../Gdb/AvrGdb/CommandPackets/FlashDone.hpp | 12 +- .../Gdb/AvrGdb/CommandPackets/FlashErase.cpp | 56 +- .../Gdb/AvrGdb/CommandPackets/FlashErase.hpp | 11 +- .../Gdb/AvrGdb/CommandPackets/FlashWrite.cpp | 48 +- .../Gdb/AvrGdb/CommandPackets/FlashWrite.hpp | 2 + .../Gdb/AvrGdb/CommandPackets/ReadMemory.cpp | 206 +-- .../Gdb/AvrGdb/CommandPackets/ReadMemory.hpp | 37 +- .../AvrGdb/CommandPackets/ReadMemoryMap.cpp | 116 +- .../AvrGdb/CommandPackets/ReadMemoryMap.hpp | 12 +- .../AvrGdb/CommandPackets/ReadRegister.cpp | 66 +- .../AvrGdb/CommandPackets/ReadRegister.hpp | 2 + .../AvrGdb/CommandPackets/ReadRegisters.cpp | 112 +- .../AvrGdb/CommandPackets/ReadRegisters.hpp | 8 +- .../CommandPackets/VContContinueExecution.cpp | 11 +- .../CommandPackets/VContContinueExecution.hpp | 2 + .../AvrGdb/CommandPackets/VContRangeStep.cpp | 111 +- .../AvrGdb/CommandPackets/VContRangeStep.hpp | 10 +- .../CommandPackets/VContStepExecution.cpp | 11 +- .../CommandPackets/VContStepExecution.hpp | 2 + .../VContSupportedActionsQuery.cpp | 11 +- .../VContSupportedActionsQuery.hpp | 2 + .../Gdb/AvrGdb/CommandPackets/WriteMemory.cpp | 201 ++- .../Gdb/AvrGdb/CommandPackets/WriteMemory.hpp | 34 +- .../AvrGdb/CommandPackets/WriteRegister.cpp | 110 +- .../AvrGdb/CommandPackets/WriteRegister.hpp | 3 + src/DebugServer/Gdb/AvrGdb/DebugSession.cpp | 8 +- src/DebugServer/Gdb/AvrGdb/DebugSession.hpp | 1 - .../Gdb/AvrGdb/TargetDescriptor.cpp | 206 ++- .../Gdb/AvrGdb/TargetDescriptor.hpp | 44 +- .../Gdb/CommandPackets/ActivateInsight.cpp | 15 +- .../Gdb/CommandPackets/ActivateInsight.hpp | 2 + .../Gdb/CommandPackets/BloomVersion.cpp | 15 +- .../Gdb/CommandPackets/BloomVersion.hpp | 2 + .../CommandPackets/BloomVersionMachine.cpp | 32 +- .../CommandPackets/BloomVersionMachine.hpp | 2 + .../Gdb/CommandPackets/CommandPacket.cpp | 30 +- .../Gdb/CommandPackets/CommandPacket.hpp | 39 +- .../Gdb/CommandPackets/ContinueExecution.cpp | 22 +- .../Gdb/CommandPackets/ContinueExecution.hpp | 2 + src/DebugServer/Gdb/CommandPackets/Detach.cpp | 11 +- src/DebugServer/Gdb/CommandPackets/Detach.hpp | 2 + .../Gdb/CommandPackets/EepromFill.hpp | 29 - .../Gdb/CommandPackets/GenerateSvd.cpp | 225 ---- .../Gdb/CommandPackets/GenerateSvd.hpp | 35 - .../Gdb/CommandPackets/HelpMonitorInfo.cpp | 25 +- .../Gdb/CommandPackets/HelpMonitorInfo.hpp | 2 + .../Gdb/CommandPackets/InterruptExecution.cpp | 11 +- .../Gdb/CommandPackets/InterruptExecution.hpp | 2 + .../Gdb/CommandPackets/Monitor.cpp | 30 +- .../Gdb/CommandPackets/Monitor.hpp | 2 + .../Gdb/CommandPackets/RemoveBreakpoint.cpp | 22 +- .../Gdb/CommandPackets/RemoveBreakpoint.hpp | 2 + .../Gdb/CommandPackets/ResetTarget.cpp | 13 +- .../Gdb/CommandPackets/ResetTarget.hpp | 2 + .../Gdb/CommandPackets/SetBreakpoint.cpp | 26 +- .../Gdb/CommandPackets/SetBreakpoint.hpp | 2 + .../Gdb/CommandPackets/StepExecution.cpp | 19 +- .../Gdb/CommandPackets/StepExecution.hpp | 2 + .../CommandPackets/SupportedFeaturesQuery.cpp | 14 +- .../CommandPackets/SupportedFeaturesQuery.hpp | 2 + src/DebugServer/Gdb/Connection.cpp | 39 +- src/DebugServer/Gdb/Connection.hpp | 2 +- src/DebugServer/Gdb/DebugSession.cpp | 30 +- src/DebugServer/Gdb/DebugSession.hpp | 6 - .../Gdb/Exceptions/DebugServerInterrupted.hpp | 2 +- src/DebugServer/Gdb/GdbDebugServerConfig.cpp | 6 +- src/DebugServer/Gdb/GdbRspDebugServer.cpp | 224 ++-- src/DebugServer/Gdb/GdbRspDebugServer.hpp | 25 +- src/DebugServer/Gdb/Packet.hpp | 59 - src/DebugServer/Gdb/ProgrammingSession.hpp | 2 +- src/DebugServer/Gdb/RegisterDescriptor.hpp | 26 +- .../Gdb/Resources/GdbHelpMonitorInfo.txt.in | 4 - .../Gdb/ResponsePackets/ResponsePacket.cpp | 41 + .../Gdb/ResponsePackets/ResponsePacket.hpp | 9 +- .../SupportedFeaturesResponse.cpp | 2 +- .../Gdb/ResponsePackets/TargetStopped.hpp | 4 +- src/DebugServer/Gdb/StopReason.hpp | 4 +- src/DebugServer/Gdb/TargetDescriptor.cpp | 64 - src/DebugServer/Gdb/TargetDescriptor.hpp | 74 +- src/DebugToolDrivers/CMakeLists.txt | 9 + src/DebugToolDrivers/DebugTool.hpp | 66 +- src/DebugToolDrivers/Microchip/EdbgDevice.cpp | 100 +- src/DebugToolDrivers/Microchip/EdbgDevice.hpp | 37 +- .../Microchip/MplabPickit4/MplabPickit4.cpp | 4 +- .../Microchip/MplabSnap/MplabSnap.cpp | 4 +- .../Protocols/EDBG/AVR/Avr8Generic.hpp | 147 +-- .../Protocols/EDBG/AVR/AvrCommand.hpp | 2 +- .../Microchip/Protocols/EDBG/AVR/AvrEvent.cpp | 10 +- .../Protocols/EDBG/AVR/AvrResponse.cpp | 4 +- .../AVR8Generic/DisableDebugWire.hpp | 2 +- .../AVR/CommandFrames/AVRISP/ProgramFuse.hpp | 9 +- .../AVR/CommandFrames/AVRISP/ReadFuse.hpp | 13 +- .../CommandFrames/AVRISP/ReadSignature.hpp | 6 +- .../AVR/CommandFrames/AvrCommandFrame.hpp | 25 +- .../Protocols/EDBG/AVR/EdbgAvr8Interface.cpp | 995 +++++++------- .../Protocols/EDBG/AVR/EdbgAvr8Interface.hpp | 65 +- .../Protocols/EDBG/AVR/EdbgAvr8Session.cpp | 15 +- .../Protocols/EDBG/AVR/EdbgAvr8Session.hpp | 17 +- .../EDBG/AVR/EdbgAvrIspInterface.cpp | 85 +- .../EDBG/AVR/EdbgAvrIspInterface.hpp | 47 +- .../AVR/Events/AVR8Generic/BreakEvent.cpp | 2 +- .../AVR/Exceptions/Avr8CommandFailure.hpp | 6 +- .../AVR8Generic/DebugWireJtagParameters.cpp | 29 +- .../AVR8Generic/DebugWireJtagParameters.hpp | 6 +- .../Parameters/AVR8Generic/PdiParameters.cpp | 4 +- .../Parameters/AVR8Generic/PdiParameters.hpp | 4 +- .../Parameters/AVR8Generic/UpdiParameters.cpp | 4 +- .../Parameters/AVR8Generic/UpdiParameters.hpp | 4 +- .../AVR8Generic/Avr8GenericResponseFrame.cpp | 8 +- .../AVR8Generic/GetDeviceId.cpp | 18 +- .../AVR8Generic/GetDeviceId.hpp | 4 +- .../AVR8Generic/GetProgramCounter.hpp | 6 +- .../ResponseFrames/AVR8Generic/ReadMemory.hpp | 5 +- .../AVRISP/AvrIspResponseFrame.cpp | 2 +- .../AVR/ResponseFrames/AvrResponseFrame.cpp | 6 +- .../Discovery/DiscoveryResponseFrame.cpp | 7 +- .../EDBGControl/EdbgControlResponseFrame.cpp | 7 +- .../HouseKeepingResponseFrame.cpp | 2 +- .../Protocols/EDBG/EdbgInterface.cpp | 21 +- .../Protocols/EDBG/EdbgInterface.hpp | 6 +- .../EdbgTargetPowerManagementInterface.cpp | 8 +- .../Protocols/CMSIS-DAP/CmsisDapInterface.cpp | 9 +- .../Protocols/CMSIS-DAP/CmsisDapInterface.hpp | 9 +- .../Protocols/CMSIS-DAP/Response.cpp | 4 +- .../DebugModule/DebugModule.hpp | 5 +- .../Registers/AbstractCommandRegister.hpp | 9 +- .../AbstractControlStatusRegister.hpp | 4 +- .../DebugModule/Registers/ControlRegister.hpp | 4 +- .../Registers/MemoryAccessControlField.hpp | 8 +- .../Registers/RegisterAccessControlField.hpp | 4 +- .../Registers/RegisterAddresses.hpp | 11 +- .../DebugModule/Registers/StatusRegister.hpp | 4 +- .../RiscVDebugSpec/DebugTranslator.cpp | 592 +++++++++ .../RiscVDebugSpec/DebugTranslator.hpp | 109 ++ .../DebugTransportModuleInterface.hpp | 71 + .../Registers/CpuRegisterNumbers.hpp | 11 + .../Registers/DebugControlStatusRegister.hpp | 4 +- .../RiscVDebugSpec}/RiscVGeneric.hpp | 2 +- .../{AVR => }/AVR8/Avr8DebugInterface.hpp | 172 +-- .../{AVR => AVR8}/AvrIspInterface.hpp | 42 +- .../RiscV/RiscVDebugInterface.hpp | 120 +- .../RiscV/RiscVIdentificationInterface.hpp | 18 + .../TargetPowerManagementInterface.hpp | 15 +- src/DebugToolDrivers/USB/HID/HidInterface.cpp | 28 +- src/DebugToolDrivers/USB/HID/HidInterface.hpp | 2 +- src/DebugToolDrivers/USB/UsbDevice.cpp | 59 +- src/DebugToolDrivers/USB/UsbDevice.hpp | 6 +- src/DebugToolDrivers/USB/UsbInterface.cpp | 20 +- .../Protocols/WchLink/Commands/Command.hpp | 2 +- .../DebugModuleInterfaceOperation.hpp | 8 +- .../DebugModuleInterfaceOperationResponse.hpp | 32 +- .../Protocols/WchLink/WchLinkInterface.cpp | 79 +- .../Protocols/WchLink/WchLinkInterface.hpp | 52 +- src/DebugToolDrivers/WCH/WchLinkBase.cpp | 44 +- src/DebugToolDrivers/WCH/WchLinkBase.hpp | 28 +- src/EventManager/EventListener.cpp | 8 +- src/EventManager/EventListener.hpp | 26 +- src/EventManager/EventManager.cpp | 10 +- src/EventManager/Events/Event.hpp | 3 +- src/EventManager/Events/Events.hpp | 3 +- .../Events/MemoryWrittenToTarget.hpp | 12 +- .../Events/RegistersWrittenToTarget.hpp | 9 +- .../Events/TargetControllerErrorOccurred.hpp | 4 +- .../Events/TargetExecutionResumed.hpp | 29 - .../Events/TargetExecutionStopped.hpp | 35 - .../Events/TargetStateChanged.hpp | 32 + src/Exceptions/Exception.hpp | 2 +- .../InternalFatalErrorException.hpp | 4 - src/Exceptions/InvalidConfig.hpp | 10 +- .../TargetControllerStartupFailure.hpp | 10 +- src/Helpers/BiMap.hpp | 18 +- src/Helpers/ConditionVariableNotifier.cpp | 4 +- src/Helpers/ConditionVariableNotifier.hpp | 6 - src/Helpers/EnumToStringMappings.hpp | 11 +- src/Helpers/EpollInstance.cpp | 20 +- src/Helpers/EventFdNotifier.cpp | 15 +- src/Helpers/Pair.hpp | 19 + src/Helpers/Synchronised.hpp | 2 +- src/Helpers/Thread.hpp | 8 +- .../Tasks/RefreshTargetPinStates.hpp | 1 - .../InsightWindow/InsightWindow.hpp | 1 - .../DIP/DualInlinePackageWidget.hpp | 1 - .../Widgets/TargetWidgets/DIP/PinWidget.hpp | 1 - .../Widgets/TargetWidgets/QFP/PinWidget.hpp | 1 - .../QFP/QuadFlatPackageWidget.hpp | 1 - .../TargetWidgets/TargetPackageWidget.hpp | 1 - .../TargetWidgets/TargetPinBodyWidget.hpp | 1 - .../Widgets/TargetWidgets/TargetPinWidget.hpp | 1 - src/Logger/Logger.cpp | 12 +- src/ProjectConfig.cpp | 76 +- src/ProjectConfig.hpp | 2 +- src/ProjectSettings.cpp | 77 +- src/Services/Avr8InstructionService.cpp | 18 +- src/Services/Avr8InstructionService.hpp | 25 +- src/Services/DateTimeService.hpp | 6 +- src/Services/PathService.cpp | 6 +- src/Services/PathService.hpp | 2 +- src/Services/ProcessService.cpp | 18 +- src/Services/StringService.cpp | 41 +- src/Services/StringService.hpp | 12 +- src/Services/TargetControllerService.cpp | 129 +- src/Services/TargetControllerService.hpp | 97 +- src/Services/TargetService.hpp | 5 +- src/SignalHandler/SignalHandler.cpp | 17 +- src/TargetController/CommandManager.hpp | 4 +- .../Commands/CommandTypes.hpp | 5 +- .../Commands/EraseTargetMemory.hpp | 17 +- .../Commands/GetTargetGpioPinStates.hpp | 31 + .../Commands/GetTargetPinStates.hpp | 31 - .../Commands/ReadTargetMemory.hpp | 14 +- .../Commands/ReadTargetRegisters.hpp | 6 +- .../Commands/ResumeTargetExecution.hpp | 9 - ...PinState.hpp => SetTargetGpioPinState.hpp} | 19 +- .../Commands/SetTargetProgramCounter.hpp | 2 +- .../Commands/SetTargetStackPointer.hpp | 31 + .../Commands/StepTargetExecution.hpp | 7 - .../Commands/WriteTargetMemory.hpp | 18 +- .../Commands/WriteTargetRegisters.hpp | 6 +- .../Exceptions/DeviceCommunicationFailure.hpp | 10 +- .../Exceptions/DeviceFailure.hpp | 10 +- .../DeviceInitializationFailure.hpp | 10 +- .../Exceptions/DeviceNotFound.hpp | 10 +- .../Exceptions/TargetOperationFailure.hpp | 4 - .../Responses/ResponseTypes.hpp | 2 +- .../Responses/TargetGpioPinStates.hpp | 29 + .../Responses/TargetMemoryRead.hpp | 4 +- .../Responses/TargetPinStates.hpp | 24 - .../Responses/TargetRegistersRead.hpp | 11 +- .../Responses/TargetState.hpp | 4 +- .../TargetControllerComponent.cpp | 715 +++++----- .../TargetControllerComponent.hpp | 133 +- src/Targets/CMakeLists.txt | 18 +- src/Targets/Microchip/AVR/AVR8/Avr8.cpp | 1073 --------------- src/Targets/Microchip/AVR/AVR8/Avr8.hpp | 220 ---- .../Microchip/AVR/AVR8/PadDescriptor.hpp | 28 - .../AVR/AVR8/TargetDescriptionFile.cpp | 1141 ---------------- .../AVR/AVR8/TargetDescriptionFile.hpp | 282 ---- .../Microchip/AVR/AVR8/TargetParameters.hpp | 69 - src/Targets/Microchip/AVR/Fuse.hpp | 54 - src/Targets/Microchip/AVR8/Avr8.cpp | 1154 +++++++++++++++++ src/Targets/Microchip/AVR8/Avr8.hpp | 232 ++++ .../{AVR => }/AVR8/Avr8TargetConfig.cpp | 13 +- .../{AVR => }/AVR8/Avr8TargetConfig.hpp | 18 +- .../DebugWirePhysicalInterfaceError.hpp | 4 - .../Microchip/{AVR => }/AVR8/Family.hpp | 4 +- src/Targets/Microchip/AVR8/Fuse.hpp | 24 + .../Microchip/AVR8/GpioPadDescriptor.hpp | 34 + src/Targets/Microchip/AVR8/IspParameters.cpp | 29 + .../Microchip/{AVR => AVR8}/IspParameters.hpp | 12 +- .../{AVR => }/AVR8/OpcodeDecoder/Decoder.cpp | 16 +- .../{AVR => }/AVR8/OpcodeDecoder/Decoder.hpp | 2 +- .../Exceptions/DecodeFailure.hpp | 2 +- .../AVR8/OpcodeDecoder/Instruction.hpp | 2 +- .../{AVR => }/AVR8/OpcodeDecoder/Opcode.hpp | 6 +- .../{AVR => }/AVR8/OpcodeDecoder/Opcodes.hpp | 394 +++--- .../{AVR => }/AVR8/ProgramMemorySection.hpp | 2 +- .../{AVR => }/AVR8/ProgrammingSession.hpp | 2 +- .../Microchip/AVR8/TargetDescriptionFile.cpp | 195 +++ .../Microchip/AVR8/TargetDescriptionFile.hpp | 85 ++ .../{AVR => AVR8}/TargetSignature.hpp | 16 +- .../RiscV/Registers/RegisterNumbers.hpp | 23 - src/Targets/RiscV/RiscV.cpp | 1110 ++++++++-------- src/Targets/RiscV/RiscV.hpp | 131 +- src/Targets/RiscV/RiscVRegisterDescriptor.hpp | 38 - src/Targets/RiscV/RiscVTargetConfig.cpp | 8 + src/Targets/RiscV/RiscVTargetConfig.hpp | 15 + .../TargetDescriptionFile.cpp | 18 - src/Targets/RiscV/TargetDescriptionFile.cpp | 16 + .../TargetDescriptionFile.hpp | 11 +- src/Targets/RiscV/TargetParameters.hpp | 11 - src/Targets/Target.hpp | 101 +- src/Targets/TargetAddressSpaceDescriptor.cpp | 56 +- src/Targets/TargetAddressSpaceDescriptor.hpp | 38 +- src/Targets/TargetBitFieldDescriptor.cpp | 20 + src/Targets/TargetBitFieldDescriptor.hpp | 36 + .../TargetDescription/AddressSpace.hpp | 10 +- .../TargetDescription/MemorySegment.hpp | 8 +- .../MemorySegmentSection.hpp | 6 +- src/Targets/TargetDescription/Module.hpp | 8 +- src/Targets/TargetDescription/Peripheral.hpp | 6 +- .../TargetDescription/PhysicalInterface.hpp | 12 +- src/Targets/TargetDescription/Pinout.hpp | 4 +- .../TargetDescription/PropertyGroup.hpp | 15 +- src/Targets/TargetDescription/README.md | 20 +- src/Targets/TargetDescription/Register.hpp | 6 +- .../TargetDescription/RegisterGroup.hpp | 12 +- .../TargetDescriptionFile.cpp | 682 ++++++---- .../TargetDescriptionFile.hpp | 85 +- src/Targets/TargetDescriptor.cpp | 86 +- src/Targets/TargetDescriptor.hpp | 47 +- src/Targets/TargetGpioPinState.hpp | 40 + src/Targets/TargetMemory.hpp | 111 +- src/Targets/TargetMemoryCache.cpp | 22 +- src/Targets/TargetMemoryCache.hpp | 5 +- src/Targets/TargetMemorySegmentDescriptor.cpp | 46 + src/Targets/TargetMemorySegmentDescriptor.hpp | 42 +- src/Targets/TargetMemorySegmentType.hpp | 1 + src/Targets/TargetPeripheralDescriptor.cpp | 41 +- src/Targets/TargetPeripheralDescriptor.hpp | 26 +- .../TargetPeripheralSignalDescriptor.cpp | 19 + .../TargetPeripheralSignalDescriptor.hpp | 28 + src/Targets/TargetPhysicalInterface.cpp | 6 +- src/Targets/TargetPinDescriptor.cpp | 14 + src/Targets/TargetPinDescriptor.hpp | 60 +- src/Targets/TargetPinoutDescriptor.cpp | 22 + src/Targets/TargetPinoutDescriptor.hpp | 30 +- src/Targets/TargetRegister.hpp | 32 - src/Targets/TargetRegisterDescriptor.cpp | 93 ++ src/Targets/TargetRegisterDescriptor.hpp | 73 +- src/Targets/TargetRegisterGroupDescriptor.cpp | 94 +- src/Targets/TargetRegisterGroupDescriptor.hpp | 79 +- src/Targets/TargetState.hpp | 70 +- src/Targets/TargetVariant.hpp | 64 - src/Targets/TargetVariantDescriptor.cpp | 14 + src/Targets/TargetVariantDescriptor.hpp | 24 + src/Targets/Targets.hpp | 2 +- 331 files changed, 8815 insertions(+), 8565 deletions(-) rename src/DebugServer/Gdb/{ => AvrGdb}/CommandPackets/EepromFill.cpp (59%) create mode 100644 src/DebugServer/Gdb/AvrGdb/CommandPackets/EepromFill.hpp delete mode 100644 src/DebugServer/Gdb/CommandPackets/EepromFill.hpp delete mode 100644 src/DebugServer/Gdb/CommandPackets/GenerateSvd.cpp delete mode 100644 src/DebugServer/Gdb/CommandPackets/GenerateSvd.hpp create mode 100644 src/DebugServer/Gdb/ResponsePackets/ResponsePacket.cpp delete mode 100644 src/DebugServer/Gdb/TargetDescriptor.cpp rename src/{Targets/RiscV => DebugToolDrivers/Protocols/RiscVDebugSpec}/DebugModule/DebugModule.hpp (81%) rename src/{Targets/RiscV => DebugToolDrivers/Protocols/RiscVDebugSpec}/DebugModule/Registers/AbstractCommandRegister.hpp (74%) rename src/{Targets/RiscV => DebugToolDrivers/Protocols/RiscVDebugSpec}/DebugModule/Registers/AbstractControlStatusRegister.hpp (91%) rename src/{Targets/RiscV => DebugToolDrivers/Protocols/RiscVDebugSpec}/DebugModule/Registers/ControlRegister.hpp (95%) rename src/{Targets/RiscV => DebugToolDrivers/Protocols/RiscVDebugSpec}/DebugModule/Registers/MemoryAccessControlField.hpp (85%) rename src/{Targets/RiscV => DebugToolDrivers/Protocols/RiscVDebugSpec}/DebugModule/Registers/RegisterAccessControlField.hpp (92%) rename src/{Targets/RiscV => DebugToolDrivers/Protocols/RiscVDebugSpec}/DebugModule/Registers/RegisterAddresses.hpp (53%) rename src/{Targets/RiscV => DebugToolDrivers/Protocols/RiscVDebugSpec}/DebugModule/Registers/StatusRegister.hpp (96%) create mode 100644 src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.cpp create mode 100644 src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.hpp create mode 100644 src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTransportModuleInterface.hpp create mode 100644 src/DebugToolDrivers/Protocols/RiscVDebugSpec/Registers/CpuRegisterNumbers.hpp rename src/{Targets/RiscV => DebugToolDrivers/Protocols/RiscVDebugSpec}/Registers/DebugControlStatusRegister.hpp (95%) rename src/{Targets/RiscV => DebugToolDrivers/Protocols/RiscVDebugSpec}/RiscVGeneric.hpp (82%) rename src/DebugToolDrivers/TargetInterfaces/Microchip/{AVR => }/AVR8/Avr8DebugInterface.hpp (54%) rename src/DebugToolDrivers/TargetInterfaces/Microchip/{AVR => AVR8}/AvrIspInterface.hpp (62%) create mode 100644 src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVIdentificationInterface.hpp delete mode 100644 src/EventManager/Events/TargetExecutionResumed.hpp delete mode 100644 src/EventManager/Events/TargetExecutionStopped.hpp create mode 100644 src/EventManager/Events/TargetStateChanged.hpp create mode 100644 src/Helpers/Pair.hpp create mode 100644 src/TargetController/Commands/GetTargetGpioPinStates.hpp delete mode 100644 src/TargetController/Commands/GetTargetPinStates.hpp rename src/TargetController/Commands/{SetTargetPinState.hpp => SetTargetGpioPinState.hpp} (54%) create mode 100644 src/TargetController/Commands/SetTargetStackPointer.hpp create mode 100644 src/TargetController/Responses/TargetGpioPinStates.hpp delete mode 100644 src/TargetController/Responses/TargetPinStates.hpp delete mode 100644 src/Targets/Microchip/AVR/AVR8/Avr8.cpp delete mode 100644 src/Targets/Microchip/AVR/AVR8/Avr8.hpp delete mode 100644 src/Targets/Microchip/AVR/AVR8/PadDescriptor.hpp delete mode 100644 src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.cpp delete mode 100644 src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.hpp delete mode 100644 src/Targets/Microchip/AVR/AVR8/TargetParameters.hpp delete mode 100644 src/Targets/Microchip/AVR/Fuse.hpp create mode 100644 src/Targets/Microchip/AVR8/Avr8.cpp create mode 100644 src/Targets/Microchip/AVR8/Avr8.hpp rename src/Targets/Microchip/{AVR => }/AVR8/Avr8TargetConfig.cpp (87%) rename src/Targets/Microchip/{AVR => }/AVR8/Avr8TargetConfig.hpp (88%) rename src/Targets/Microchip/{AVR => }/AVR8/Exceptions/DebugWirePhysicalInterfaceError.hpp (72%) rename src/Targets/Microchip/{AVR => }/AVR8/Family.hpp (77%) create mode 100644 src/Targets/Microchip/AVR8/Fuse.hpp create mode 100644 src/Targets/Microchip/AVR8/GpioPadDescriptor.hpp create mode 100644 src/Targets/Microchip/AVR8/IspParameters.cpp rename src/Targets/Microchip/{AVR => AVR8}/IspParameters.hpp (62%) rename src/Targets/Microchip/{AVR => }/AVR8/OpcodeDecoder/Decoder.cpp (97%) rename src/Targets/Microchip/{AVR => }/AVR8/OpcodeDecoder/Decoder.hpp (96%) rename src/Targets/Microchip/{AVR => }/AVR8/OpcodeDecoder/Exceptions/DecodeFailure.hpp (88%) rename src/Targets/Microchip/{AVR => }/AVR8/OpcodeDecoder/Instruction.hpp (98%) rename src/Targets/Microchip/{AVR => }/AVR8/OpcodeDecoder/Opcode.hpp (99%) rename src/Targets/Microchip/{AVR => }/AVR8/OpcodeDecoder/Opcodes.hpp (80%) rename src/Targets/Microchip/{AVR => }/AVR8/ProgramMemorySection.hpp (75%) rename src/Targets/Microchip/{AVR => }/AVR8/ProgrammingSession.hpp (93%) create mode 100644 src/Targets/Microchip/AVR8/TargetDescriptionFile.cpp create mode 100644 src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp rename src/Targets/Microchip/{AVR => AVR8}/TargetSignature.hpp (77%) delete mode 100644 src/Targets/RiscV/Registers/RegisterNumbers.hpp delete mode 100644 src/Targets/RiscV/RiscVRegisterDescriptor.hpp create mode 100644 src/Targets/RiscV/RiscVTargetConfig.cpp create mode 100644 src/Targets/RiscV/RiscVTargetConfig.hpp delete mode 100644 src/Targets/RiscV/TargetDescription/TargetDescriptionFile.cpp create mode 100644 src/Targets/RiscV/TargetDescriptionFile.cpp rename src/Targets/RiscV/{TargetDescription => }/TargetDescriptionFile.hpp (69%) delete mode 100644 src/Targets/RiscV/TargetParameters.hpp create mode 100644 src/Targets/TargetBitFieldDescriptor.cpp create mode 100644 src/Targets/TargetBitFieldDescriptor.hpp create mode 100644 src/Targets/TargetGpioPinState.hpp create mode 100644 src/Targets/TargetMemorySegmentDescriptor.cpp create mode 100644 src/Targets/TargetPeripheralSignalDescriptor.cpp create mode 100644 src/Targets/TargetPeripheralSignalDescriptor.hpp create mode 100644 src/Targets/TargetPinDescriptor.cpp create mode 100644 src/Targets/TargetPinoutDescriptor.cpp delete mode 100644 src/Targets/TargetRegister.hpp delete mode 100644 src/Targets/TargetVariant.hpp create mode 100644 src/Targets/TargetVariantDescriptor.cpp create mode 100644 src/Targets/TargetVariantDescriptor.hpp diff --git a/build/scripts/Targets/TargetDescriptionFiles/AddressSpace.php b/build/scripts/Targets/TargetDescriptionFiles/AddressSpace.php index b5f63d26..54662966 100644 --- a/build/scripts/Targets/TargetDescriptionFiles/AddressSpace.php +++ b/build/scripts/Targets/TargetDescriptionFiles/AddressSpace.php @@ -8,16 +8,18 @@ class AddressSpace public ?string $key = null; public ?int $startAddress = null; public ?int $size = null; + public ?int $unitSize = null; public ?string $endianness = null; /** @var MemorySegment[] */ public array $memorySegments = []; - public function __construct(?string $key, ?int $startAddress, ?int $size, ?string $endianness) + public function __construct(?string $key, ?int $startAddress, ?int $size, ?int $unitSize, ?string $endianness) { $this->key = $key; $this->startAddress = $startAddress; $this->size = $size; + $this->unitSize = $unitSize; $this->endianness = $endianness; } diff --git a/build/scripts/Targets/TargetDescriptionFiles/MemorySegment.php b/build/scripts/Targets/TargetDescriptionFiles/MemorySegment.php index 273dc2bd..cfe1e7b0 100644 --- a/build/scripts/Targets/TargetDescriptionFiles/MemorySegment.php +++ b/build/scripts/Targets/TargetDescriptionFiles/MemorySegment.php @@ -93,10 +93,7 @@ class MemorySegment && $endAddress !== null && $other->startAddress !== null && $otherEndAddress !== null - && ( - ($other->startAddress <= $this->startAddress && $otherEndAddress >= $this->startAddress) - || ($other->startAddress >= $this->startAddress && $other->startAddress <= $endAddress) - ) + && $this->startAddress <= $otherEndAddress && $other->startAddress <= $endAddress ; } diff --git a/build/scripts/Targets/TargetDescriptionFiles/Services/AtdfService.php b/build/scripts/Targets/TargetDescriptionFiles/Services/AtdfService.php index 853c4097..868b8510 100644 --- a/build/scripts/Targets/TargetDescriptionFiles/Services/AtdfService.php +++ b/build/scripts/Targets/TargetDescriptionFiles/Services/AtdfService.php @@ -323,6 +323,7 @@ class AtdfService isset($attributes['id']) ? strtolower($attributes['id']) : null, $this->stringService->tryStringToInt($attributes['start'] ?? null), $this->stringService->tryStringToInt($attributes['size'] ?? null), + null, $attributes['endianness'] ?? null, ); diff --git a/build/scripts/Targets/TargetDescriptionFiles/Services/ValidationService.php b/build/scripts/Targets/TargetDescriptionFiles/Services/ValidationService.php index e99211c3..e0e744f9 100644 --- a/build/scripts/Targets/TargetDescriptionFiles/Services/ValidationService.php +++ b/build/scripts/Targets/TargetDescriptionFiles/Services/ValidationService.php @@ -606,7 +606,7 @@ class ValidationService if ($register->size === null) { $failures[] = 'Missing size'; - } elseif ($register->size < 1) { + } elseif ($register->size < 1 || $register->size > 8) { $failures[] = 'Invalid size (' . $register->size . ')'; } diff --git a/build/scripts/Targets/TargetDescriptionFiles/Services/Xml/FromXmlService.php b/build/scripts/Targets/TargetDescriptionFiles/Services/Xml/FromXmlService.php index 5a765c50..96cbde95 100644 --- a/build/scripts/Targets/TargetDescriptionFiles/Services/Xml/FromXmlService.php +++ b/build/scripts/Targets/TargetDescriptionFiles/Services/Xml/FromXmlService.php @@ -116,6 +116,7 @@ class FromXmlService $attributes['key'] ?? null, $this->stringService->tryStringToInt($attributes['start'] ?? null), $this->stringService->tryStringToInt($attributes['size'] ?? null), + $this->stringService->tryStringToInt($attributes['unit-size'] ?? null), $attributes['endianness'] ?? null, ); diff --git a/build/scripts/Targets/TargetDescriptionFiles/Services/Xml/ToXmlService.php b/build/scripts/Targets/TargetDescriptionFiles/Services/Xml/ToXmlService.php index 5936a18c..7f03498b 100644 --- a/build/scripts/Targets/TargetDescriptionFiles/Services/Xml/ToXmlService.php +++ b/build/scripts/Targets/TargetDescriptionFiles/Services/Xml/ToXmlService.php @@ -70,6 +70,10 @@ class ToXmlService $element->setAttribute('start', $this->stringService->tryIntToHex($addressSpace->startAddress, 8)); $element->setAttribute('size', $addressSpace->size); + if (!empty($addressSpace->unitSize)) { + $element->setAttribute('unit-size', $addressSpace->unitSize); + } + if (!empty($addressSpace->endianness)) { $element->setAttribute('endianness', strtolower($addressSpace->endianness)); } diff --git a/src/Application.cpp b/src/Application.cpp index 2dae5803..a25cf896 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -19,8 +19,6 @@ #include "src/Services/ProcessService.hpp" #include "src/Helpers/BiMap.hpp" -#include "src/Targets/TargetDescription/TargetDescriptionFile.hpp" - #include "src/Exceptions/InvalidConfig.hpp" using namespace Exceptions; @@ -75,12 +73,11 @@ int Application::run() { this->checkBloomVersion(); /* - * We can't run our own event loop here - we have to use Qt's event loop. But we still need to be able to - * process our events. To address this, we use a QTimer to dispatch our events on an interval. + * We use a QTimer to dispatch our events on an interval. * * This allows us to use Qt's event loop whilst still being able to process our own events. */ - auto* eventDispatchTimer = new QTimer(this->qtApplication.get()); + auto* eventDispatchTimer = new QTimer{this->qtApplication.get()}; QObject::connect(eventDispatchTimer, &QTimer::timeout, this, &Application::dispatchEvents); eventDispatchTimer->start(100); @@ -98,7 +95,7 @@ int Application::run() { } std::map> Application::getCommandHandlersByCommandName() { - return std::map> { + return std::map>{ { "--help", std::bind(&Application::presentHelpText, this) @@ -225,17 +222,15 @@ void Application::triggerShutdown() { void Application::loadProjectSettings() { const auto projectSettingsPath = Services::PathService::projectSettingsPath(); - auto jsonSettingsFile = QFile(QString::fromStdString(projectSettingsPath)); + auto jsonSettingsFile = QFile{QString::fromStdString(projectSettingsPath)}; if (jsonSettingsFile.exists()) { try { if (!jsonSettingsFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - throw Exception("Failed to open settings file."); + throw Exception{"Failed to open settings file."}; } - this->projectSettings = ProjectSettings( - QJsonDocument::fromJson(jsonSettingsFile.readAll()).object() - ); + this->projectSettings = ProjectSettings{QJsonDocument::fromJson(jsonSettingsFile.readAll()).object()}; jsonSettingsFile.close(); return; @@ -256,45 +251,43 @@ void Application::saveProjectSettings() { } const auto projectSettingsPath = Services::PathService::projectSettingsPath(); - auto jsonSettingsFile = QFile(QString::fromStdString(projectSettingsPath)); + auto jsonSettingsFile = QFile{QString::fromStdString(projectSettingsPath)}; Logger::debug("Saving project settings to " + projectSettingsPath); - QDir().mkpath(QString::fromStdString(Services::PathService::projectSettingsDirPath())); + QDir{}.mkpath(QString::fromStdString(Services::PathService::projectSettingsDirPath())); try { - const auto jsonDocument = QJsonDocument(this->projectSettings->toJson()); + const auto jsonDocument = QJsonDocument{this->projectSettings->toJson()}; if (!jsonSettingsFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) { - throw Exception( + throw Exception{ "Failed to open/create settings file (" + projectSettingsPath + "). Check file permissions." - ); + }; } jsonSettingsFile.write(jsonDocument.toJson()); jsonSettingsFile.close(); } catch (const Exception& exception) { - Logger::error( - "Failed to save project settings - " + exception.getMessage() - ); + Logger::error("Failed to save project settings - " + exception.getMessage()); } } void Application::loadProjectConfiguration() { - auto configFile = QFile(QString::fromStdString(Services::PathService::projectConfigPath())); + auto configFile = QFile{QString::fromStdString(Services::PathService::projectConfigPath())}; if (!configFile.exists()) { - throw Exception( + throw Exception{ "Bloom configuration file (bloom.yaml) not found. Working directory: " + Services::PathService::projectDirPath() - ); + }; } if (!configFile.open(QIODevice::ReadOnly | QIODevice::Text)) { - throw InvalidConfig( + throw InvalidConfig{ "Failed to open Bloom configuration file. Working directory: " + Services::PathService::projectDirPath() - ); + }; } try { @@ -310,9 +303,9 @@ void Application::loadProjectConfiguration() { // Validate the selected environment const auto selectedEnvironmentIt = this->projectConfig->environments.find(this->selectedEnvironmentName); if (selectedEnvironmentIt == this->projectConfig->environments.end()) { - throw InvalidConfig( + throw InvalidConfig{ "Environment (\"" + this->selectedEnvironmentName + "\") not found in configuration." - ); + }; } this->environmentConfig = selectedEnvironmentIt->second; @@ -328,7 +321,7 @@ void Application::loadProjectConfiguration() { this->debugServerConfig = this->projectConfig->debugServerConfig.value(); } else { - throw InvalidConfig("Debug server configuration missing."); + throw InvalidConfig{"Debug server configuration missing."}; } } @@ -340,18 +333,20 @@ int Application::presentHelpText() { Logger::silence(); // The file help.txt is included in Bloom's binary, as a resource. See the root-level CMakeLists.txt for more. - auto helpFile = QFile(QString::fromStdString(Services::PathService::compiledResourcesPath() + "/resources/help.txt")); + auto helpFile = QFile{ + QString::fromStdString(Services::PathService::compiledResourcesPath() + "/resources/help.txt") + }; if (!helpFile.open(QIODevice::ReadOnly)) { // This should never happen - if it does, something has gone very wrong - throw Exception( + throw Exception{ "Failed to open help file - please report this issue at " + Services::PathService::homeDomainName() + "/report-issue" - ); + }; } std::cout << "Bloom v" << Application::VERSION.toString() << "\n"; - std::cout << QTextStream(&helpFile).readAll().toUtf8().constData() << "\n"; + std::cout << QTextStream{&helpFile}.readAll().toUtf8().constData() << "\n"; return EXIT_SUCCESS; } @@ -376,14 +371,14 @@ int Application::presentVersionText() { int Application::presentVersionMachineText() { Logger::silence(); - std::cout << QJsonDocument(QJsonObject({ + std::cout << QJsonDocument{QJsonObject{ {"version", QString::fromStdString(Application::VERSION.toString())}, {"components", QJsonObject({ {"major", Application::VERSION.major}, {"minor", Application::VERSION.minor}, {"patch", Application::VERSION.patch}, })}, - })).toJson().toStdString(); + }}.toJson().toStdString(); return EXIT_SUCCESS; } @@ -393,38 +388,38 @@ int Application::presentCapabilitiesMachine() { Logger::silence(); - static const auto targetFamilyNames = BiMap({ + static const auto targetFamilyNames = BiMap{ {TargetFamily::AVR_8, "AVR8"}, {TargetFamily::RISC_V, "RISC-V"}, - }); + }; - auto supportedTargets = QJsonArray(); + auto supportedTargets = QJsonArray{}; for (const auto& [configValue, descriptor] : Services::TargetService::briefDescriptorsByConfigValue()) { - supportedTargets.push_back(QJsonObject({ + supportedTargets.push_back(QJsonObject{ {"name" , QString::fromStdString(descriptor.name)}, {"family" , targetFamilyNames.at(descriptor.family)}, {"configurationValue" , QString::fromStdString(configValue)}, - })); + }); } - std::cout << QJsonDocument(QJsonObject({ + std::cout << QJsonDocument{QJsonObject{ {"targets", supportedTargets}, #ifndef EXCLUDE_INSIGHT {"insight", true}, #else {"insight", false}, #endif - })).toJson().toStdString(); + }}.toJson().toStdString(); return EXIT_SUCCESS; } int Application::initProject() { - auto configFile = QFile(QString::fromStdString(Services::PathService::projectConfigPath())); + auto configFile = QFile{QString::fromStdString(Services::PathService::projectConfigPath())}; if (configFile.exists()) { - throw Exception("Bloom configuration file (bloom.yaml) already exists in working directory."); + throw Exception{"Bloom configuration file (bloom.yaml) already exists in working directory."}; } /* @@ -433,19 +428,19 @@ int Application::initProject() { * * We simply copy the template file into the user's working directory. */ - auto templateConfigFile = QFile( + auto templateConfigFile = QFile{ QString::fromStdString(Services::PathService::compiledResourcesPath()+ "/resources/bloom.template.yaml") - ); + }; if (!templateConfigFile.open(QIODevice::ReadOnly)) { - throw Exception( + throw Exception{ "Failed to open template configuration file - please report this issue at " + Services::PathService::homeDomainName() + "/report-issue" - ); + }; } if (!configFile.open(QIODevice::ReadWrite)) { - throw Exception("Failed to create Bloom configuration file (bloom.yaml)"); + throw Exception{"Failed to create Bloom configuration file (bloom.yaml)"}; } configFile.write(templateConfigFile.readAll()); @@ -471,7 +466,7 @@ void Application::stopSignalHandler() { * Send meaningless signal to the SignalHandler thread to have it shutdown. The signal will pull it out of * a blocking state and allow it to action the shutdown. See SignalHandler::run() for more. */ - pthread_kill(this->signalHandlerThread.native_handle(), SIGUSR1); + ::pthread_kill(this->signalHandlerThread.native_handle(), SIGUSR1); } if (this->signalHandlerThread.joinable()) { @@ -497,7 +492,7 @@ void Application::startTargetController() { >(); if (!tcStateChangeEvent.has_value() || tcStateChangeEvent->get()->getState() != ThreadState::READY) { - throw Exception("TargetController failed to start up"); + throw Exception{"TargetController failed to start up"}; } } @@ -510,7 +505,7 @@ void Application::stopTargetController() { if (tcThreadState == ThreadState::STARTING || tcThreadState == ThreadState::READY) { EventManager::triggerEvent(std::make_shared()); this->applicationEventListener->waitForEvent( - std::chrono::milliseconds(10000) + std::chrono::milliseconds{10000} ); } @@ -525,18 +520,14 @@ void Application::startDebugServer() { this->debugServer = std::make_unique( this->debugServerConfig.value() ); - - this->debugServerThread = std::thread( - &DebugServer::DebugServerComponent::run, - this->debugServer.get() - ); + this->debugServerThread = std::thread{&DebugServer::DebugServerComponent::run, this->debugServer.get()}; const auto dsStateChangeEvent = this->applicationEventListener->waitForEvent< Events::DebugServerThreadStateChanged >(); if (!dsStateChangeEvent.has_value() || dsStateChangeEvent->get()->getState() != ThreadState::READY) { - throw Exception("DebugServer failed to start up"); + throw Exception{"DebugServer failed to start up"}; } } @@ -549,7 +540,7 @@ void Application::stopDebugServer() { if (debugServerState == ThreadState::STARTING || debugServerState == ThreadState::READY) { EventManager::triggerEvent(std::make_shared()); this->applicationEventListener->waitForEvent( - std::chrono::milliseconds(5000) + std::chrono::milliseconds{5000} ); } @@ -567,12 +558,14 @@ void Application::dispatchEvents() { void Application::checkBloomVersion() { const auto currentVersionNumber = Application::VERSION; - auto* networkAccessManager = new QNetworkAccessManager(this); - auto queryVersionEndpointUrl = QUrl(QString::fromStdString(Services::PathService::homeDomainName() + "/latest-version")); + auto* networkAccessManager = new QNetworkAccessManager{this}; + auto queryVersionEndpointUrl = QUrl{ + QString::fromStdString(Services::PathService::homeDomainName() + "/latest-version") + }; queryVersionEndpointUrl.setScheme("http"); - queryVersionEndpointUrl.setQuery(QUrlQuery({ + queryVersionEndpointUrl.setQuery(QUrlQuery{ {"currentVersionNumber", QString::fromStdString(currentVersionNumber.toString())} - })); + }); QObject::connect( networkAccessManager, @@ -591,7 +584,7 @@ void Application::checkBloomVersion() { } ); - networkAccessManager->get(QNetworkRequest(queryVersionEndpointUrl)); + networkAccessManager->get(QNetworkRequest{queryVersionEndpointUrl}); } #ifndef EXCLUDE_INSIGHT diff --git a/src/Application.hpp b/src/Application.hpp index fc88bc18..b812a889 100644 --- a/src/Application.hpp +++ b/src/Application.hpp @@ -43,15 +43,9 @@ class Application: public QObject, public Thread Q_OBJECT public: - static const inline VersionNumber VERSION = VersionNumber(std::string(BLOOM_VERSION)); + static const inline VersionNumber VERSION = VersionNumber{std::string{BLOOM_VERSION}}; explicit Application(std::vector&& arguments); - - /** - * Main entry-point for the Bloom program. - * - * @return - */ int run(); private: diff --git a/src/DebugServer/CMakeLists.txt b/src/DebugServer/CMakeLists.txt index 0063078a..695bb8aa 100755 --- a/src/DebugServer/CMakeLists.txt +++ b/src/DebugServer/CMakeLists.txt @@ -8,7 +8,7 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/GdbDebugServerConfig.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/Connection.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/DebugSession.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/TargetDescriptor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/ResponsePackets/ResponsePacket.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/ResponsePackets/SupportedFeaturesResponse.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/CommandPacket.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/SupportedFeaturesQuery.cpp @@ -22,9 +22,7 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/HelpMonitorInfo.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/BloomVersion.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/BloomVersionMachine.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/GenerateSvd.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/Detach.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/CommandPackets/EepromFill.cpp # AVR GDB RSP Server ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/AvrGdbRsp.cpp @@ -43,6 +41,8 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/VContContinueExecution.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/VContStepExecution.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/VContRangeStep.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Gdb/AvrGdb/CommandPackets/EepromFill.cpp + ) if (NOT EXCLUDE_INSIGHT) diff --git a/src/DebugServer/DebugServerComponent.cpp b/src/DebugServer/DebugServerComponent.cpp index 09680037..3711f72b 100644 --- a/src/DebugServer/DebugServerComponent.cpp +++ b/src/DebugServer/DebugServerComponent.cpp @@ -29,7 +29,7 @@ namespace DebugServer } } catch (const std::exception& exception) { - Logger::error("DebugServer fatal error: " + std::string(exception.what())); + Logger::error("DebugServer fatal error: " + std::string{exception.what()}); } this->shutdown(); @@ -44,7 +44,7 @@ namespace DebugServer "avr-gdb-rsp", [this] () -> std::unique_ptr { if (this->targetDescriptor.family != Targets::TargetFamily::AVR_8) { - throw Exceptions::Exception("The AVR GDB RSP server is only compatible with AVR8 targets."); + throw Exceptions::Exception{"The AVR GDB RSP server is only compatible with AVR8 targets."}; } return std::make_unique( @@ -75,7 +75,7 @@ namespace DebugServer const auto selectedServerIt = availableServersByName.find(this->debugServerConfig.name); if (selectedServerIt == availableServersByName.end()) { - throw Exceptions::InvalidConfig("DebugServer \"" + this->debugServerConfig.name + "\" not found."); + throw Exceptions::InvalidConfig{"DebugServer \"" + this->debugServerConfig.name + "\" not found."}; } this->server = selectedServerIt->second(); @@ -111,7 +111,7 @@ namespace DebugServer ); } - void DebugServerComponent::onShutdownDebugServerEvent(const Events::ShutdownDebugServer& event) { + void DebugServerComponent::onShutdownDebugServerEvent(const Events::ShutdownDebugServer&) { this->shutdown(); } } diff --git a/src/DebugServer/DebugServerComponent.hpp b/src/DebugServer/DebugServerComponent.hpp index bc1aa99a..a777fe9f 100644 --- a/src/DebugServer/DebugServerComponent.hpp +++ b/src/DebugServer/DebugServerComponent.hpp @@ -54,7 +54,7 @@ namespace DebugServer /** * The current target descriptor. */ - Targets::TargetDescriptor targetDescriptor; + const Targets::TargetDescriptor& targetDescriptor; /** * This EventFdNotifier is injected into this->eventListener. It can be used by server implementations to diff --git a/src/DebugServer/Gdb/AvrGdb/AvrGdbRsp.cpp b/src/DebugServer/Gdb/AvrGdb/AvrGdbRsp.cpp index 28af8436..541657da 100644 --- a/src/DebugServer/Gdb/AvrGdb/AvrGdbRsp.cpp +++ b/src/DebugServer/Gdb/AvrGdb/AvrGdbRsp.cpp @@ -17,6 +17,9 @@ #include "CommandPackets/VContStepExecution.hpp" #include "CommandPackets/VContRangeStep.hpp" +#include "src/DebugServer/Gdb/CommandPackets/Monitor.hpp" +#include "CommandPackets/EepromFill.hpp" + namespace DebugServer::Gdb::AvrGdb { using namespace Exceptions; @@ -30,7 +33,7 @@ namespace DebugServer::Gdb::AvrGdb EventListener& eventListener, EventFdNotifier& eventNotifier ) - : GdbRspDebugServer(debugServerConfig, eventListener, eventNotifier) + : GdbRspDebugServer(debugServerConfig, targetDescriptor, eventListener, eventNotifier) , gdbTargetDescriptor(targetDescriptor) {} @@ -38,7 +41,6 @@ namespace DebugServer::Gdb::AvrGdb this->activeDebugSession.emplace( std::move(connection), this->getSupportedFeatures(), - this->gdbTargetDescriptor, this->debugServerConfig ); @@ -57,52 +59,57 @@ namespace DebugServer::Gdb::AvrGdb return this->activeDebugSession.has_value() ? &*(this->activeDebugSession) : nullptr; } - std::unique_ptr AvrGdbRsp::resolveCommandPacket( - const RawPacket& rawPacket - ) { - using AvrGdb::CommandPackets::ReadRegister; - using AvrGdb::CommandPackets::ReadRegisters; - using AvrGdb::CommandPackets::WriteRegister; - using AvrGdb::CommandPackets::ReadMemory; - using AvrGdb::CommandPackets::WriteMemory; - using AvrGdb::CommandPackets::ReadMemoryMap; - using AvrGdb::CommandPackets::FlashErase; - using AvrGdb::CommandPackets::FlashWrite; - using AvrGdb::CommandPackets::FlashDone; - using AvrGdb::CommandPackets::VContSupportedActionsQuery; - using AvrGdb::CommandPackets::VContContinueExecution; - using AvrGdb::CommandPackets::VContStepExecution; - using AvrGdb::CommandPackets::VContRangeStep; + std::unique_ptr AvrGdbRsp::resolveCommandPacket(const RawPacket& rawPacket) { + using Gdb::CommandPackets::Monitor; - if (rawPacket.size() >= 2) { - if (rawPacket[1] == 'p') { - return std::make_unique(rawPacket); - } + using CommandPackets::ReadRegister; + using CommandPackets::ReadRegisters; + using CommandPackets::WriteRegister; + using CommandPackets::ReadMemory; + using CommandPackets::WriteMemory; + using CommandPackets::ReadMemoryMap; + using CommandPackets::FlashErase; + using CommandPackets::FlashWrite; + using CommandPackets::FlashDone; + using CommandPackets::VContSupportedActionsQuery; + using CommandPackets::VContContinueExecution; + using CommandPackets::VContStepExecution; + using CommandPackets::VContRangeStep; + using CommandPackets::EepromFill; - if (rawPacket[1] == 'g') { - return std::make_unique(rawPacket); - } + if (rawPacket.size() < 2) { + throw Exception{"Invalid raw packet - no data"}; + } - if (rawPacket[1] == 'P') { - return std::make_unique(rawPacket); - } + if (rawPacket[1] == 'p') { + return std::make_unique(rawPacket); + } - if (rawPacket[1] == 'm') { - return std::make_unique(rawPacket, this->gdbTargetDescriptor); - } + if (rawPacket[1] == 'g') { + return std::make_unique(rawPacket, this->gdbTargetDescriptor); + } - if (rawPacket[1] == 'M') { - return std::make_unique(rawPacket, this->gdbTargetDescriptor); - } + if (rawPacket[1] == 'P') { + return std::make_unique(rawPacket); + } - const auto rawPacketString = std::string(rawPacket.begin() + 1, rawPacket.end()); + if (rawPacket[1] == 'm') { + return std::make_unique(rawPacket, this->gdbTargetDescriptor); + } + + if (rawPacket[1] == 'M') { + return std::make_unique(rawPacket, this->gdbTargetDescriptor); + } + + if (rawPacket.size() > 1) { + const auto rawPacketString = std::string{rawPacket.begin() + 1, rawPacket.end()}; if (rawPacketString.find("qXfer:memory-map:read::") == 0) { - return std::make_unique(rawPacket); + return std::make_unique(rawPacket, this->gdbTargetDescriptor); } if (rawPacketString.find("vFlashErase") == 0) { - return std::make_unique(rawPacket); + return std::make_unique(rawPacket, this->gdbTargetDescriptor); } if (rawPacketString.find("vFlashWrite") == 0) { @@ -110,7 +117,7 @@ namespace DebugServer::Gdb::AvrGdb } if (rawPacketString.find("vFlashDone") == 0) { - return std::make_unique(rawPacket); + return std::make_unique(rawPacket, this->gdbTargetDescriptor); } if (rawPacketString.find("vCont?") == 0) { @@ -127,7 +134,19 @@ namespace DebugServer::Gdb::AvrGdb if (this->debugServerConfig.rangeStepping) { if (rawPacketString.find("vCont;r") == 0) { - return std::make_unique(rawPacket); + return std::make_unique(rawPacket, this->gdbTargetDescriptor); + } + } + + if (rawPacketString.find("qRcmd") == 0) { + // This is a monitor packet + auto monitorCommand = std::make_unique(rawPacket); + + if (monitorCommand->command.find("eeprom fill") == 0) { + return std::make_unique( + std::move(*(monitorCommand.release())), + this->gdbTargetDescriptor + ); } } } @@ -184,7 +203,7 @@ namespace DebugServer::Gdb::AvrGdb Logger::debug("Attempting single step from 0x" + StringService::toHex(programAddress)); activeRangeSteppingSession->singleStepping = true; - this->targetControllerService.stepTargetExecution(std::nullopt); + this->targetControllerService.stepTargetExecution(); return; } @@ -197,10 +216,7 @@ namespace DebugServer::Gdb::AvrGdb Logger::debug("Continuing range stepping"); activeRangeSteppingSession->singleStepping = false; - this->targetControllerService.continueTargetExecution( - std::nullopt, - activeRangeSteppingSession->range.endAddress - ); + this->targetControllerService.resumeTargetExecution(); return; } @@ -213,9 +229,7 @@ namespace DebugServer::Gdb::AvrGdb * We have to end the range stepping session and report the stop to GDB. */ Logger::debug("Target stopped within stepping range, but for an unknown reason"); - this->activeDebugSession->terminateRangeSteppingSession(this->targetControllerService); - return GdbRspDebugServer::handleTargetStoppedGdbResponse(programAddress); } // Report the stop to GDB diff --git a/src/DebugServer/Gdb/CommandPackets/EepromFill.cpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/EepromFill.cpp similarity index 59% rename from src/DebugServer/Gdb/CommandPackets/EepromFill.cpp rename to src/DebugServer/Gdb/AvrGdb/CommandPackets/EepromFill.cpp index 5b33e757..be0a9b14 100644 --- a/src/DebugServer/Gdb/CommandPackets/EepromFill.cpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/EepromFill.cpp @@ -12,7 +12,7 @@ #include "src/DebugServer/Gdb/Exceptions/InvalidCommandOption.hpp" #include "src/Exceptions/Exception.hpp" -namespace DebugServer::Gdb::CommandPackets +namespace DebugServer::Gdb::AvrGdb::CommandPackets { using Services::TargetControllerService; @@ -22,59 +22,50 @@ namespace DebugServer::Gdb::CommandPackets using ::Exceptions::Exception; using Exceptions::InvalidCommandOption; - EepromFill::EepromFill(Monitor&& monitorPacket) + EepromFill::EepromFill(Monitor&& monitorPacket, const TargetDescriptor& gdbTargetDescriptor) : Monitor(std::move(monitorPacket)) + , eepromAddressSpaceDescriptor(gdbTargetDescriptor.eepromAddressSpaceDescriptor) + , eepromMemorySegmentDescriptor(gdbTargetDescriptor.eepromMemorySegmentDescriptor) { const auto fillValueOptionIt = this->commandOptions.find("value"); - - if (fillValueOptionIt == this->commandOptions.end() || !fillValueOptionIt->second.has_value()) { - return; + if (fillValueOptionIt != this->commandOptions.end() && fillValueOptionIt->second.has_value()) { + this->fillValue = Services::StringService::dataFromHex(*(fillValueOptionIt->second)); } - - const auto fillValueByteArray = QByteArray::fromHex(QByteArray::fromStdString(*fillValueOptionIt->second)); - this->fillValue = Targets::TargetMemoryBuffer(fillValueByteArray.begin(), fillValueByteArray.end()); } - void EepromFill::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) { + void EepromFill::handle( + Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, + TargetControllerService& targetControllerService + ) { Logger::info("Handling EepromFill packet"); try { - const auto& targetDescriptor = debugSession.gdbTargetDescriptor.targetDescriptor; - - const auto eepromDescriptorIt = targetDescriptor.memoryDescriptorsByType.find( - Targets::TargetMemoryType::EEPROM - ); - - if (eepromDescriptorIt == targetDescriptor.memoryDescriptorsByType.end()) { - throw Exception("Target has no EEPROM"); - } - - const auto& eepromDescriptor = eepromDescriptorIt->second; - const auto eepromSize = eepromDescriptor.size(); - + const auto eepromSize = this->eepromMemorySegmentDescriptor.size(); const auto fillValueSize = this->fillValue.size(); if (fillValueSize == 0) { - throw InvalidCommandOption("Fill value required"); + throw InvalidCommandOption{"Fill value required"}; } if (fillValueSize > eepromSize) { - throw InvalidCommandOption( - "Fill value size ("+ std::to_string(fillValueSize) + " bytes) exceeds EEPROM size (" + throw InvalidCommandOption{ + "Fill value size (" + std::to_string(fillValueSize) + " bytes) exceeds EEPROM size (" + std::to_string(eepromSize) + " bytes)" - ); + }; } if ((eepromSize % fillValueSize) != 0) { Logger::warning( "The fill value size (" + std::to_string(fillValueSize) + " bytes) is not a multiple of the EEPROM " - "size (" + std::to_string(eepromSize) + " bytes) - the fill value will be truncated" + "size (" + std::to_string(eepromSize) + " bytes) - the fill value will be truncated" ); } Logger::warning("Filling " + std::to_string(eepromSize) + " bytes of EEPROM"); - auto data = Targets::TargetMemoryBuffer(); + auto data = Targets::TargetMemoryBuffer{}; data.reserve(eepromSize); // Repeat this->fillValue until we've filled `data` @@ -92,24 +83,25 @@ namespace DebugServer::Gdb::CommandPackets Logger::debug("Filling EEPROM with values: " + hexValues); targetControllerService.writeMemory( - Targets::TargetMemoryType::EEPROM, - eepromDescriptor.addressRange.startAddress, + this->eepromAddressSpaceDescriptor, + this->eepromMemorySegmentDescriptor, + this->eepromMemorySegmentDescriptor.addressRange.startAddress, std::move(data) ); - debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex( + debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex( "Filled " + std::to_string(eepromSize) + " bytes of EEPROM, with values: " + hexValues + "\n" - ))); + )}); } catch (const InvalidCommandOption& exception) { Logger::error(exception.getMessage()); debugSession.connection.writePacket( - ResponsePacket(Services::StringService::toHex(exception.getMessage() + "\n")) + ResponsePacket{Services::StringService::toHex(exception.getMessage() + "\n")} ); } catch (const Exception& exception) { Logger::error("Failed to fill EEPROM - " + exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } } diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/EepromFill.hpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/EepromFill.hpp new file mode 100644 index 00000000..0f3e2837 --- /dev/null +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/EepromFill.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include "src/DebugServer/Gdb/CommandPackets/Monitor.hpp" + +#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp" + +#include "src/Targets/TargetAddressSpaceDescriptor.hpp" +#include "src/Targets/TargetMemorySegmentDescriptor.hpp" + +namespace DebugServer::Gdb::AvrGdb::CommandPackets +{ + /** + * The EepromFill class implements a structure for the "monitor eeprom fill" GDB command. + * + * This command fills the target's EEPROM with the given value. + */ + class EepromFill: public Gdb::CommandPackets::Monitor + { + public: + const Targets::TargetAddressSpaceDescriptor& eepromAddressSpaceDescriptor; + const Targets::TargetMemorySegmentDescriptor& eepromMemorySegmentDescriptor; + + explicit EepromFill(Monitor&& monitorPacket, const TargetDescriptor& gdbTargetDescriptor); + + void handle( + Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, + Services::TargetControllerService& targetControllerService + ) override; + + private: + Targets::TargetMemoryBuffer fillValue; + }; +} diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashDone.cpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashDone.cpp index 8299a45f..8f409901 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashDone.cpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashDone.cpp @@ -15,31 +15,41 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets using namespace Exceptions; - FlashDone::FlashDone(const RawPacket& rawPacket) + FlashDone::FlashDone(const RawPacket& rawPacket, const TargetDescriptor& targetDescriptor) : CommandPacket(rawPacket) + , programMemoryAddressSpaceDescriptor(targetDescriptor.programAddressSpaceDescriptor) + , programMemorySegmentDescriptor(targetDescriptor.programMemorySegmentDescriptor) {} - void FlashDone::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) { + void FlashDone::handle( + Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, + TargetControllerService& targetControllerService + ) { Logger::info("Handling FlashDone packet"); try { - if (debugSession.programmingSession.has_value()) { - const auto& programmingSession = debugSession.programmingSession.value(); - Logger::info( - "Flushing " + std::to_string(programmingSession.buffer.size()) + " bytes to target's program memory" - ); - - targetControllerService.enableProgrammingMode(); - - targetControllerService.writeMemory( - Targets::TargetMemoryType::FLASH, - programmingSession.startAddress, - std::move(programmingSession.buffer) - ); - - debugSession.programmingSession.reset(); + if (!debugSession.programmingSession.has_value()) { + throw Exception{"No active programming session"}; } + Logger::info( + "Flushing " + std::to_string(debugSession.programmingSession->buffer.size()) + + " bytes to target's program memory" + ); + + targetControllerService.enableProgrammingMode(); + + targetControllerService.writeMemory( + this->programMemoryAddressSpaceDescriptor, + this->programMemorySegmentDescriptor, + debugSession.programmingSession->startAddress, + std::move(debugSession.programmingSession->buffer) + ); + + debugSession.programmingSession.reset(); + Logger::warning("Program memory updated"); targetControllerService.disableProgrammingMode(); @@ -47,7 +57,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets targetControllerService.resetTarget(); Logger::info("Target reset complete"); - debugSession.connection.writePacket(OkResponsePacket()); + debugSession.connection.writePacket(OkResponsePacket{}); } catch (const Exception& exception) { Logger::error("Failed to handle FlashDone packet - " + exception.getMessage()); @@ -60,7 +70,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets Logger::error("Failed to disable programming mode - " + exception.getMessage()); } - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } } diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashDone.hpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashDone.hpp index 8378dc28..ee5827f0 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashDone.hpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashDone.hpp @@ -4,9 +4,10 @@ #include #include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp" -#include "src/DebugServer/Gdb/TargetDescriptor.hpp" +#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp" -#include "src/Targets/TargetMemory.hpp" +#include "src/Targets/TargetAddressSpaceDescriptor.hpp" +#include "src/Targets/TargetMemorySegmentDescriptor.hpp" namespace DebugServer::Gdb::AvrGdb::CommandPackets { @@ -16,10 +17,15 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets class FlashDone: public Gdb::CommandPackets::CommandPacket { public: - explicit FlashDone(const RawPacket& rawPacket); + const Targets::TargetAddressSpaceDescriptor& programMemoryAddressSpaceDescriptor; + const Targets::TargetMemorySegmentDescriptor& programMemorySegmentDescriptor; + + explicit FlashDone(const RawPacket& rawPacket, const TargetDescriptor& targetDescriptor); void handle( Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashErase.cpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashErase.cpp index 70c1167d..6b326fda 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashErase.cpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashErase.cpp @@ -3,6 +3,7 @@ #include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp" #include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp" +#include "src/Services/StringService.hpp" #include "src/Logger/Logger.hpp" #include "src/Exceptions/Exception.hpp" @@ -15,40 +16,40 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets using namespace Exceptions; - FlashErase::FlashErase(const RawPacket& rawPacket) + FlashErase::FlashErase(const RawPacket& rawPacket, const TargetDescriptor& targetDescriptor) : CommandPacket(rawPacket) + , programMemoryAddressSpaceDescriptor(targetDescriptor.programAddressSpaceDescriptor) + , programMemorySegmentDescriptor(targetDescriptor.programMemorySegmentDescriptor) { - const auto packetString = QString::fromLocal8Bit( - reinterpret_cast(this->data.data() + 12), - static_cast(this->data.size() - 12) - ); + using Services::StringService; + + if (rawPacket.size() < 8) { + throw Exception{"Invalid packet length"}; + } /* * The flash erase ('vFlashErase') packet consists of two segments, an address and a length, separated by a * comma. + * + * Example: $vFlashErase:00000000,00004f00#f4 */ - const auto packetSegments = packetString.split(","); - if (packetSegments.size() != 2) { - throw Exception( - "Unexpected number of segments in packet data: " + std::to_string(packetSegments.size()) - ); + const auto command = std::string{this->data.begin() + 12, this->data.end()}; + + const auto delimiterPos = command.find_first_of(','); + if (delimiterPos == std::string::npos) { + throw Exception{"Invalid packet"}; } - bool conversionStatus = false; - this->startAddress = packetSegments.at(0).toUInt(&conversionStatus, 16); - - if (!conversionStatus) { - throw Exception("Failed to parse start address from flash erase packet data"); - } - - this->bytes = packetSegments.at(1).toUInt(&conversionStatus, 16); - - if (!conversionStatus) { - throw Exception("Failed to parse length from flash erase packet data"); - } + this->startAddress = StringService::toUint32(command.substr(0, delimiterPos), 16); + this->bytes = StringService::toUint32(command.substr(delimiterPos + 1), 16); } - void FlashErase::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) { + void FlashErase::handle( + Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, + TargetControllerService& targetControllerService + ) { Logger::info("Handling FlashErase packet"); try { @@ -57,9 +58,12 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets Logger::warning("Erasing program memory, in preparation for programming"); // We don't erase a specific address range - we just erase the entire program memory. - targetControllerService.eraseMemory(debugSession.gdbTargetDescriptor.targetDescriptor.programMemoryType); + targetControllerService.eraseMemory( + this->programMemoryAddressSpaceDescriptor, + this->programMemorySegmentDescriptor + ); - debugSession.connection.writePacket(OkResponsePacket()); + debugSession.connection.writePacket(OkResponsePacket{}); } catch (const Exception& exception) { Logger::error("Failed to erase flash memory - " + exception.getMessage()); @@ -72,7 +76,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets Logger::error("Failed to disable programming mode - " + exception.getMessage()); } - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } } diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashErase.hpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashErase.hpp index c263352a..07b6c60f 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashErase.hpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashErase.hpp @@ -4,9 +4,10 @@ #include #include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp" -#include "src/DebugServer/Gdb/TargetDescriptor.hpp" +#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp" -#include "src/Targets/TargetMemory.hpp" +#include "src/Targets/TargetAddressSpaceDescriptor.hpp" +#include "src/Targets/TargetMemorySegmentDescriptor.hpp" namespace DebugServer::Gdb::AvrGdb::CommandPackets { @@ -19,11 +20,15 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets public: std::uint32_t startAddress = 0; std::uint32_t bytes = 0; + const Targets::TargetAddressSpaceDescriptor& programMemoryAddressSpaceDescriptor; + const Targets::TargetMemorySegmentDescriptor& programMemorySegmentDescriptor; - explicit FlashErase(const RawPacket& rawPacket); + explicit FlashErase(const RawPacket& rawPacket, const TargetDescriptor& targetDescriptor); void handle( Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashWrite.cpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashWrite.cpp index 5eac7967..4b1a6314 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashWrite.cpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashWrite.cpp @@ -1,10 +1,9 @@ #include "FlashWrite.hpp" -#include - #include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp" #include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp" +#include "src/Services/StringService.hpp" #include "src/Logger/Logger.hpp" #include "src/Exceptions/Exception.hpp" @@ -20,44 +19,41 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets FlashWrite::FlashWrite(const RawPacket& rawPacket) : CommandPacket(rawPacket) { + using Services::StringService; + if (this->data.size() < 15) { - throw Exception("Invalid packet length"); + throw Exception{"Invalid packet length"}; } /* - * The flash write ('vFlashWrite') packet consists of two segments, an address and a buffer. - * - * Seperated by a colon. + * The flash write ('vFlashWrite') packet consists of two segments: an address and a buffer, seperated by a + * colon. */ - auto colonIt = std::find(this->data.begin() + 12, this->data.end(), ':'); + const auto delimiterIt = std::find(this->data.begin() + 12, this->data.end(), ':'); - if (colonIt == this->data.end()) { - throw Exception("Failed to find colon delimiter in write flash packet."); + if (delimiterIt == this->data.end()) { + throw Exception{"Failed to find colon delimiter in write flash packet."}; } - bool conversionStatus = false; - this->startAddress = QByteArray( - reinterpret_cast(this->data.data() + 12), - std::distance(this->data.begin(), colonIt) - 12 - ).toUInt(&conversionStatus, 16); - - if (!conversionStatus) { - throw Exception("Failed to parse start address from flash write packet data"); - } - - this->buffer = Targets::TargetMemoryBuffer(colonIt + 1, this->data.end()); + this->startAddress = StringService::toUint32(std::string{this->data.begin() + 12, delimiterIt}, 16); + this->buffer = Targets::TargetMemoryBuffer{delimiterIt + 1, this->data.end()}; } - void FlashWrite::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) { + void FlashWrite::handle( + Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, + TargetControllerService& targetControllerService + ) { Logger::info("Handling FlashWrite packet"); try { if (this->buffer.empty()) { - throw Exception("Received empty buffer from GDB"); + throw Exception{"Received empty buffer from GDB"}; } if (!debugSession.programmingSession.has_value()) { - debugSession.programmingSession = ProgrammingSession(this->startAddress, this->buffer); + debugSession.programmingSession = ProgrammingSession{this->startAddress, this->buffer}; } else { auto& programmingSession = debugSession.programmingSession.value(); @@ -65,7 +61,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets const auto expectedStartAddress = (currentEndAddress + 1); if (this->startAddress < expectedStartAddress) { - throw Exception("Invalid start address from GDB - the buffer would overlap a previous buffer"); + throw Exception{"Invalid start address from GDB - the buffer would overlap a previous buffer"}; } if (this->startAddress > expectedStartAddress) { @@ -84,7 +80,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets ); } - debugSession.connection.writePacket(OkResponsePacket()); + debugSession.connection.writePacket(OkResponsePacket{}); } catch (const Exception& exception) { Logger::error("Failed to handle FlashWrite packet - " + exception.getMessage()); @@ -97,7 +93,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets Logger::error("Failed to disable programming mode - " + exception.getMessage()); } - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } } diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashWrite.hpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashWrite.hpp index 4d4f759a..3cc25e4b 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashWrite.hpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/FlashWrite.hpp @@ -24,6 +24,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets void handle( Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadMemory.cpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadMemory.cpp index 098e70e3..2f735bc8 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadMemory.cpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadMemory.cpp @@ -1,5 +1,7 @@ #include "ReadMemory.hpp" +#include + #include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp" #include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp" @@ -18,141 +20,139 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets using Exceptions::Exception; ReadMemory::ReadMemory(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor) - : CommandPacket(rawPacket) - { - if (this->data.size() < 4) { - throw Exception("Invalid packet length"); - } + : ReadMemory(rawPacket, gdbTargetDescriptor, ReadMemory::extractPacketData(rawPacket)) + {} - const auto packetString = QString::fromLocal8Bit( - reinterpret_cast(this->data.data() + 1), - static_cast(this->data.size() - 1) - ); - - /* - * The read memory ('m') packet consists of two segments, an address and a number of bytes to read. - * These are separated by a comma character. - */ - const auto packetSegments = packetString.split(","); - - if (packetSegments.size() != 2) { - throw Exception( - "Unexpected number of segments in packet data: " + std::to_string(packetSegments.size()) - ); - } - - bool conversionStatus = false; - const auto gdbStartAddress = packetSegments.at(0).toUInt(&conversionStatus, 16); - - if (!conversionStatus) { - throw Exception("Failed to parse start address from read memory packet data"); - } - - /* - * Extract the memory type from the memory address (see Gdb::TargetDescriptor::memoryOffsetsByType for more on - * this). - */ - this->memoryType = gdbTargetDescriptor.getMemoryTypeFromGdbAddress(gdbStartAddress); - this->startAddress = gdbStartAddress & ~(gdbTargetDescriptor.getMemoryOffset(this->memoryType)); - - this->bytes = packetSegments.at(1).toUInt(&conversionStatus, 16); - - if (!conversionStatus) { - throw Exception("Failed to parse read length from read memory packet data"); - } - } - - void ReadMemory::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) { + void ReadMemory::handle( + Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, + TargetControllerService& targetControllerService + ) { Logger::info("Handling ReadMemory packet"); try { - const auto& memoryDescriptorsByType = debugSession.gdbTargetDescriptor.targetDescriptor.memoryDescriptorsByType; - const auto memoryDescriptorIt = memoryDescriptorsByType.find(this->memoryType); - - if (memoryDescriptorIt == memoryDescriptorsByType.end()) { - throw Exception("Target does not support the requested memory type."); - } - if (this->bytes == 0) { - debugSession.connection.writePacket(ResponsePacket(std::vector())); + debugSession.connection.writePacket(ResponsePacket{Targets::TargetMemoryBuffer{}}); return; } - const auto& memoryDescriptor = memoryDescriptorIt->second; + const auto addressRange = Targets::TargetMemoryAddressRange{ + this->startAddress, + this->startAddress + this->bytes - 1 + }; - if (this->memoryType == Targets::TargetMemoryType::EEPROM) { - // GDB sends EEPROM addresses in relative form - we convert them to absolute form, here. - this->startAddress = memoryDescriptor.addressRange.startAddress + this->startAddress; + const auto memorySegmentDescriptors = this->addressSpaceDescriptor.getIntersectingMemorySegmentDescriptors( + addressRange + ); + + /* + * First pass to ensure that we can read all of the memory before attempting to do so. And to ensure that + * the requested address range completely resides within known memory segments. + */ + auto accessibleBytes = Targets::TargetMemorySize{0}; + for (const auto* memorySegmentDescriptor : memorySegmentDescriptors) { + if (!memorySegmentDescriptor->debugModeAccess.readable) { + throw Exception{ + "Attempted to access restricted memory segment (" + memorySegmentDescriptor->key + + ") - segment not readable in debug mode" + }; + } + + accessibleBytes += memorySegmentDescriptor->addressRange.intersectingSize(addressRange); } /* - * In AVR targets, RAM is mapped to many registers and peripherals - we don't want to block GDB from - * accessing them. + * GDB will sometimes request an excess of up to two bytes outside the memory segment address range, even + * though we provide it with a memory map. I don't know why it does this, but I do know that we must + * tolerate it, otherwise GDB will moan. */ - const auto permittedStartAddress = (this->memoryType == Targets::TargetMemoryType::RAM) - ? 0x00 - : memoryDescriptor.addressRange.startAddress; - - const auto permittedEndAddress = memoryDescriptor.addressRange.endAddress + 2; - - if ( - this->startAddress < permittedStartAddress - || (this->startAddress + (this->bytes - 1)) > permittedEndAddress - ) { + if (accessibleBytes < this->bytes && (this->bytes - accessibleBytes) > 2) { /* - * GDB can be configured to generate backtraces past the main function and the internal entry point - * of the application. Although this isn't very useful to most devs, CLion now seems to enable it by - * default. Somewhere between CLion 2021.1 and 2022.1, it began issuing the "-gdb-set backtrace past-entry on" - * command to GDB, at the beginning of each debug session. + * GDB has requested memory that, at least partially, does not reside in any known memory segment. * - * This means that GDB will attempt to walk down the stack to identify every frame. The problem is that - * GDB doesn't really know where the stack begins, so it ends up in a loop, continually issuing read - * memory commands. This has exposed an issue on our end - we need to validate the requested memory - * address range and reject any request for a range that's not within the target's memory. We do this - * here. + * This could be a result of GDB being configured to generate backtraces past the main function and + * the internal entry point of the application. This means that GDB will attempt to walk down the stack + * to identify every frame. The problem is that GDB doesn't really know where the stack begins, so it + * probes the target by continuously issuing read memory commands until the server responds with an + * error. + * + * CLion seems to enable this by default. Somewhere between CLion 2021.1 and 2022.1, it began issuing + * the "-gdb-set backtrace past-entry on" command to GDB, at the beginning of each debug session. * * We don't throw an exception here, because this isn't really an error and so it's best not to report * it as such. I don't think it's an error because it's expected behaviour, even though we respond to * GDB with an error response. */ Logger::debug( - "GDB requested access to memory which is outside the target's memory range - returning error " - "response" + "GDB requested access to memory which does not reside within any memory segment - returning error " + "response" ); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); return; } - /* - * GDB may request more bytes than what's available (even though we give it a memory map?!) - ensure that - * we don't try to read any more than what's available. - * - * We fill the out-of-bounds bytes with 0x00, below. - */ - const auto bytesToRead = (this->startAddress <= memoryDescriptor.addressRange.endAddress) - ? std::min(this->bytes, (memoryDescriptor.addressRange.endAddress - this->startAddress) + 1) - : 0; + auto buffer = Targets::TargetMemoryBuffer(this->bytes, 0x00); - auto memoryBuffer = Targets::TargetMemoryBuffer(); + { + const auto atomicSession = targetControllerService.makeAtomicSession(); - if (bytesToRead > 0) { - memoryBuffer = targetControllerService.readMemory( - this->memoryType, - this->startAddress, - bytesToRead - ); + for (const auto* memorySegmentDescriptor : memorySegmentDescriptors) { + const auto segmentStartAddress = std::max( + this->startAddress, + memorySegmentDescriptor->addressRange.startAddress + ); + + const auto segmentBuffer = targetControllerService.readMemory( + this->addressSpaceDescriptor, + *memorySegmentDescriptor, + segmentStartAddress, + memorySegmentDescriptor->addressRange.intersectingSize(addressRange) + ); + + const auto bufferOffsetIt = buffer.begin() + (segmentStartAddress - this->startAddress); + assert(segmentBuffer.size() <= std::distance(bufferOffsetIt, buffer.end())); + + std::copy(segmentBuffer.begin(), segmentBuffer.end(), bufferOffsetIt); + } } - if (bytesToRead < this->bytes) { - // GDB requested some out-of-bounds memory - fill the inaccessible bytes with 0x00 - memoryBuffer.insert(memoryBuffer.end(), (this->bytes - bytesToRead), 0x00); - } - - debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex(memoryBuffer))); + debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(buffer)}); } catch (const Exception& exception) { Logger::error("Failed to read memory from target - " + exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } + + ReadMemory::PacketData ReadMemory::extractPacketData(const RawPacket& rawPacket) { + using Services::StringService; + + if (rawPacket.size() < 8) { + throw Exception{"Invalid packet length"}; + } + + const auto command = std::string{rawPacket.begin() + 2, rawPacket.end() - 3}; + + const auto delimiterPos = command.find_first_of(','); + if (delimiterPos == std::string::npos) { + throw Exception{"Invalid packet"}; + } + + return { + StringService::toUint32(command.substr(0, delimiterPos), 16), + StringService::toUint32(command.substr(delimiterPos + 1), 16) + }; + } + + ReadMemory::ReadMemory( + const RawPacket& rawPacket, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + ReadMemory::PacketData&& packetData + ) + : CommandPacket(rawPacket) + , addressSpaceDescriptor(gdbTargetDescriptor.addressSpaceDescriptorFromGdbAddress(packetData.gdbStartAddress)) + , startAddress(gdbTargetDescriptor.translateGdbAddress(packetData.gdbStartAddress)) + , bytes(packetData.bytes) + {} } diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadMemory.hpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadMemory.hpp index 4b71bf3b..cb963b34 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadMemory.hpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadMemory.hpp @@ -4,9 +4,10 @@ #include #include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp" -#include "src/DebugServer/Gdb/TargetDescriptor.hpp" +#include "src/Targets/TargetAddressSpaceDescriptor.hpp" #include "src/Targets/TargetMemory.hpp" +#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp" namespace DebugServer::Gdb::AvrGdb::CommandPackets { @@ -17,26 +18,32 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets class ReadMemory: public Gdb::CommandPackets::CommandPacket { public: - /** - * Start address of the memory operation. - */ - Targets::TargetMemoryAddress startAddress = 0; + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor; - /** - * The type of memory to read from. - */ - Targets::TargetMemoryType memoryType = Targets::TargetMemoryType::FLASH; + Targets::TargetMemoryAddress startAddress; + Targets::TargetMemorySize bytes; - /** - * Number of bytes to read. - */ - Targets::TargetMemorySize bytes = 0; - - explicit ReadMemory(const RawPacket& rawPacket, const Gdb::TargetDescriptor& gdbTargetDescriptor); + ReadMemory(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor); void handle( Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; + + private: + struct PacketData + { + GdbMemoryAddress gdbStartAddress; + std::uint32_t bytes; + }; + + static PacketData extractPacketData(const RawPacket& rawPacket); + ReadMemory( + const RawPacket& rawPacket, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + PacketData&& packetData + ); }; } diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadMemoryMap.cpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadMemoryMap.cpp index 2b957e35..db76d7b9 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadMemoryMap.cpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadMemoryMap.cpp @@ -2,6 +2,7 @@ #include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp" +#include "src/Services/StringService.hpp" #include "src/Exceptions/Exception.hpp" namespace DebugServer::Gdb::AvrGdb::CommandPackets @@ -12,111 +13,74 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets using Exceptions::Exception; - ReadMemoryMap::ReadMemoryMap(const RawPacket& rawPacket) + ReadMemoryMap::ReadMemoryMap(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor) : CommandPacket(rawPacket) + , eepromAddressSpaceDescriptor(gdbTargetDescriptor.eepromAddressSpaceDescriptor) + , programMemorySegmentDescriptor(gdbTargetDescriptor.programMemorySegmentDescriptor) + , eepromMemorySegmentDescriptor(gdbTargetDescriptor.eepromMemorySegmentDescriptor) { - if (this->data.size() < 26) { - throw Exception("Invalid packet length"); - } + using Services::StringService; - auto packetString = QString::fromLocal8Bit( - reinterpret_cast(this->data.data() + 23), // +23 to exclude the "qXfer:memory-map:read::" - static_cast(this->data.size() - 23) - ); + if (this->data.size() < 26) { + throw Exception{"Invalid packet length"}; + } /* * The read memory map ('qXfer:memory-map:read::...') packet consists of two segments, an offset and a length. * These are separated by a comma character. */ - auto packetSegments = packetString.split(","); + const auto command = std::string{this->data.begin() + 23, this->data.end()}; - if (packetSegments.size() != 2) { - throw Exception( - "Unexpected number of segments in packet data: " + std::to_string(packetSegments.size()) - ); + const auto delimiterPos = command.find_first_of(','); + if (delimiterPos == std::string::npos) { + throw Exception{"Invalid packet"}; } - bool conversionStatus = false; - this->offset = packetSegments.at(0).toUInt(&conversionStatus, 16); - - if (!conversionStatus) { - throw Exception("Failed to parse offset from read memory map packet data"); - } - - this->length = packetSegments.at(1).toUInt(&conversionStatus, 16); - - if (!conversionStatus) { - throw Exception("Failed to parse read length from read memory map packet data"); - } + this->offset = StringService::toUint32(command.substr(0, delimiterPos), 16); + this->length = StringService::toUint32(command.substr(delimiterPos + 1), 16); } - void ReadMemoryMap::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) { + void ReadMemoryMap::handle( + Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, + TargetControllerService& targetControllerService + ) { Logger::info("Handling ReadMemoryMap packet"); - using Targets::TargetMemoryType; - const auto& memoryDescriptorsByType = debugSession.gdbTargetDescriptor.targetDescriptor.memoryDescriptorsByType; - - const auto& ramDescriptor = memoryDescriptorsByType.at(TargetMemoryType::RAM); - const auto& flashDescriptor = memoryDescriptorsByType.at(TargetMemoryType::FLASH); - - const auto eepromDescriptorIt = memoryDescriptorsByType.find(TargetMemoryType::EEPROM); - const auto eepromDescriptor = eepromDescriptorIt != memoryDescriptorsByType.end() - ? std::optional(eepromDescriptorIt->second) - : std::nullopt; - - const auto ramGdbOffset = debugSession.gdbTargetDescriptor.getMemoryOffset( - Targets::TargetMemoryType::RAM - ); - - const auto eepromGdbOffset = debugSession.gdbTargetDescriptor.getMemoryOffset( - Targets::TargetMemoryType::EEPROM - ); - - const auto flashGdbOffset = debugSession.gdbTargetDescriptor.getMemoryOffset( - Targets::TargetMemoryType::FLASH - ); - /* * We include register and EEPROM memory in our RAM section. This allows GDB to access registers and EEPROM * data via memory read/write packets. - * - * Like SRAM, GDB applies an offset to EEPROM addresses. We account for that offset in our ramSectionSize. - * - * The SRAM and EEPROM offsets allow for a maximum of 65KB of SRAM. But that must also accommodate the - * register addresses, which can vary in size. - * - * As of writing this (Dec 2022), there are no 8-bit AVR targets on sale today, with 65KB+ of SRAM. */ - const auto eepromEndAddress = eepromGdbOffset + (eepromDescriptor.has_value() ? eepromDescriptor->size() : 0); - const auto ramSectionSize = eepromEndAddress - ramGdbOffset; - - const auto flashSize = flashDescriptor.size(); - const auto flashPageSize = flashDescriptor.pageSize.value(); + const auto ramSectionEndAddress = gdbTargetDescriptor.translateTargetMemoryAddress( + this->eepromMemorySegmentDescriptor.addressRange.endAddress, + this->eepromAddressSpaceDescriptor, + this->eepromMemorySegmentDescriptor + ); + const auto ramSectionStartAddress = TargetDescriptor::SRAM_ADDRESS_MASK; + const auto ramSectionSize = ramSectionEndAddress - ramSectionStartAddress + 1; const auto memoryMap = - std::string("") - + "" - + "" - + "" + std::to_string(flashPageSize) + "" + std::string{""} + + "" + + "programMemorySegmentDescriptor.size()) + "\">" + + "" + std::to_string(this->programMemorySegmentDescriptor.pageSize.value()) + "" + "" + ""; + auto responseData = std::vector{'l'}; + if (this->offset < memoryMap.size() && this->length > 0) { - auto memoryMapData = std::vector( + responseData.insert( + responseData.end(), memoryMap.begin() + this->offset, - memoryMap.begin() + std::min( - static_cast(this->offset + this->length), - static_cast(memoryMap.size()) + memoryMap.begin() + this->offset + std::min( + static_cast(this->length), + static_cast(memoryMap.size() - this->offset) ) ); - - auto responseData = std::vector({'l'}); - std::move(memoryMapData.begin(), memoryMapData.end(), std::back_inserter(responseData)); - - debugSession.connection.writePacket(ResponsePacket(responseData)); - return; } - debugSession.connection.writePacket(ResponsePacket(std::vector({'l'}))); + debugSession.connection.writePacket(ResponsePacket{responseData}); } } diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadMemoryMap.hpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadMemoryMap.hpp index 09ce2d28..37142a51 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadMemoryMap.hpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadMemoryMap.hpp @@ -3,6 +3,10 @@ #include #include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp" +#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp" + +#include "src/Targets/TargetAddressSpaceDescriptor.hpp" +#include "src/Targets/TargetMemorySegmentDescriptor.hpp" namespace DebugServer::Gdb::AvrGdb::CommandPackets { @@ -13,6 +17,10 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets class ReadMemoryMap: public Gdb::CommandPackets::CommandPacket { public: + const Targets::TargetAddressSpaceDescriptor& eepromAddressSpaceDescriptor; + const Targets::TargetMemorySegmentDescriptor& programMemorySegmentDescriptor; + const Targets::TargetMemorySegmentDescriptor& eepromMemorySegmentDescriptor; + /** * The offset of the memory map, from which to read. */ @@ -23,10 +31,12 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets */ std::uint32_t length = 0; - explicit ReadMemoryMap(const RawPacket& rawPacket); + explicit ReadMemoryMap(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor); void handle( Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegister.cpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegister.cpp index a624d5c2..6af225ea 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegister.cpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegister.cpp @@ -1,10 +1,10 @@ #include "ReadRegister.hpp" #include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp" -#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp" #include "src/Targets/TargetRegisterDescriptor.hpp" +#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp" #include "src/Services/StringService.hpp" #include "src/Logger/Logger.hpp" @@ -24,16 +24,23 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets ReadRegister::ReadRegister(const RawPacket& rawPacket) : CommandPacket(rawPacket) { + using Services::StringService; + if (this->data.size() < 2) { - throw Exception("Invalid packet length"); + throw Exception{"Invalid packet length"}; } this->registerId = static_cast( - std::stoi(std::string(this->data.begin() + 1, this->data.end())) + StringService::toUint32(std::string{this->data.begin() + 1, this->data.end()}, 16) ); } - void ReadRegister::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) { + void ReadRegister::handle( + Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, + TargetControllerService& targetControllerService + ) { Logger::info("Handling ReadRegister packet"); try { @@ -47,49 +54,60 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets const auto programCounter = targetControllerService.getProgramCounter(); debugSession.connection.writePacket( - ResponsePacket(Services::StringService::toHex(Targets::TargetMemoryBuffer({ + ResponsePacket{Services::StringService::toHex(Targets::TargetMemoryBuffer{ static_cast(programCounter), static_cast(programCounter >> 8), static_cast(programCounter >> 16), static_cast(programCounter >> 24), - }))) + })} ); return; } - const auto& targetDescriptor = debugSession.gdbTargetDescriptor; - const auto& gdbRegisterDescriptor = targetDescriptor.gdbRegisterDescriptorsById.at(this->registerId); + if (this->registerId == TargetDescriptor::STACK_POINTER_GDB_REGISTER_ID) { + /* + * GDB has requested the program counter. We can't access this in the same way as we do with other + * registers. + */ + const auto stackPointer = targetControllerService.getStackPointer(); - const auto targetRegisterDescriptorId = targetDescriptor.getTargetRegisterDescriptorIdFromGdbRegisterId( + debugSession.connection.writePacket( + ResponsePacket{Services::StringService::toHex(Targets::TargetMemoryBuffer{ + static_cast(stackPointer), + static_cast(stackPointer >> 8), + })} + ); + + return; + } + + const auto gdbRegisterDescriptorIt = gdbTargetDescriptor.gdbRegisterDescriptorsById.find(this->registerId); + const auto targetRegisterDescriptorIt = gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.find( this->registerId ); - if (!targetRegisterDescriptorId.has_value()) { - throw Exception("GDB requested an invalid/unknown register"); + if ( + gdbRegisterDescriptorIt == gdbTargetDescriptor.gdbRegisterDescriptorsById.end() + || targetRegisterDescriptorIt == gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.end() + ) { + throw Exception{"Unknown GDB register ID (" + std::to_string(this->registerId) + ")"}; } - auto registerValue = targetControllerService.readRegisters({*targetRegisterDescriptorId}).front().value; - - // GDB expects register values to be in LSB. - std::reverse(registerValue.begin(), registerValue.end()); + auto registerValue = targetControllerService.readRegister(*(targetRegisterDescriptorIt->second)); + std::reverse(registerValue.begin(), registerValue.end()); // MSB to LSB + const auto& gdbRegisterDescriptor = gdbRegisterDescriptorIt->second; if (registerValue.size() < gdbRegisterDescriptor.size) { - /* - * The register on the target is smaller than the size expected by GDB. - * - * Insert the rest of the bytes. - */ + // The register on the target is smaller than the size expected by GDB. registerValue.insert(registerValue.end(), (gdbRegisterDescriptor.size - registerValue.size()), 0x00); } - debugSession.connection.writePacket( - ResponsePacket(Services::StringService::toHex(registerValue)) - ); + debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(registerValue)}); } catch (const Exception& exception) { Logger::error("Failed to read general registers - " + exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } } diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegister.hpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegister.hpp index 6eb43194..8ed00f59 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegister.hpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegister.hpp @@ -19,6 +19,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets void handle( Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegisters.cpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegisters.cpp index c1b2c761..b60b48c5 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegisters.cpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegisters.cpp @@ -1,13 +1,14 @@ #include "ReadRegisters.hpp" +#include +#include +#include + #include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp" -#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp" #include "src/Targets/TargetRegisterDescriptor.hpp" -#include "src/Targets/TargetRegister.hpp" #include "src/Targets/TargetMemory.hpp" -#include "src/Services/StringService.hpp" #include "src/Logger/Logger.hpp" #include "src/Exceptions/Exception.hpp" @@ -16,93 +17,78 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets { using Services::TargetControllerService; - using Targets::TargetRegister; - using Targets::TargetRegisterDescriptorIds; - using ResponsePackets::ResponsePacket; using ResponsePackets::ErrorResponsePacket; using Exceptions::Exception; - ReadRegisters::ReadRegisters(const RawPacket& rawPacket) + ReadRegisters::ReadRegisters(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor) : CommandPacket(rawPacket) + , gpRegistersMemorySegmentDescriptor(gdbTargetDescriptor.gpRegistersMemorySegmentDescriptor) {} - void ReadRegisters::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) { + void ReadRegisters::handle( + Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, + TargetControllerService& targetControllerService + ) { Logger::info("Handling ReadRegisters packet"); try { - const auto& targetDescriptor = debugSession.gdbTargetDescriptor; - auto descriptorIds = TargetRegisterDescriptorIds(); + auto buffer = Targets::TargetMemoryBuffer(39, 0x00); - // Read all target registers mapped to a GDB register - for (const auto& [gdbRegisterId, gdbRegisterDescriptor] : targetDescriptor.gdbRegisterDescriptorsById) { - const auto registerDescriptorId = targetDescriptor.getTargetRegisterDescriptorIdFromGdbRegisterId( - gdbRegisterId - ); - - if (registerDescriptorId.has_value()) { - descriptorIds.insert(*registerDescriptorId); + auto gpRegDescriptors = Targets::TargetRegisterDescriptors{}; + std::transform( + gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.begin(), + gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.end(), + std::back_inserter(gpRegDescriptors), + [] (const auto& pair) { + return pair.second; } - } - - Targets::TargetRegisters registerSet; - Targets::TargetMemoryAddress programCounter; + ); { const auto atomicSession = targetControllerService.makeAtomicSession(); - registerSet = targetControllerService.readRegisters(descriptorIds); - programCounter = targetControllerService.getProgramCounter(); - } + for (auto& [regDesc, regVal] : targetControllerService.readRegisters(gpRegDescriptors)) { + if (regDesc.type != Targets::TargetRegisterType::GENERAL_PURPOSE_REGISTER) { + // Status register (SREG) + assert(regVal.size() == 1); + buffer[32] = regVal[0]; + continue; + } - /* - * Sort each register by their respective GDB register ID - this will leave us with a collection of - * registers in the order expected by the GDB client. - */ - std::sort( - registerSet.begin(), - registerSet.end(), - [this, &targetDescriptor] (const TargetRegister& regA, const TargetRegister& regB) { - return targetDescriptor.getGdbRegisterIdFromTargetRegisterDescriptorId(regA.descriptorId).value() < - targetDescriptor.getGdbRegisterIdFromTargetRegisterDescriptorId(regB.descriptorId).value(); - } - ); + const auto bufferOffset = regDesc.startAddress + - this->gpRegistersMemorySegmentDescriptor.addressRange.startAddress; - /* - * Reverse the register values (as they're all currently in MSB, but GDB expects them in LSB), ensure that - * each register value size matches the size in the associated GDB register descriptor and implode the - * values. - */ - auto registers = std::vector(); - for (auto& reg : registerSet) { - std::reverse(reg.value.begin(), reg.value.end()); + assert((buffer.size() - bufferOffset) >= regVal.size()); - const auto gdbRegisterId = targetDescriptor.getGdbRegisterIdFromTargetRegisterDescriptorId( - reg.descriptorId - ).value(); - const auto& gdbRegisterDescriptor = targetDescriptor.gdbRegisterDescriptorsById.at(gdbRegisterId); - - if (reg.value.size() < gdbRegisterDescriptor.size) { - reg.value.insert(reg.value.end(), (gdbRegisterDescriptor.size - reg.value.size()), 0x00); + /* + * GDB expects register values in LSB form, which is why we use reverse iterators below. + * + * This isn't really necessary though, as all of the registers that are handled here are + * single-byte registers. + */ + std::copy(regVal.rbegin(), regVal.rend(), buffer.begin() + bufferOffset); } - registers.insert(registers.end(), reg.value.begin(), reg.value.end()); + const auto spValue = targetControllerService.getStackPointer(); + buffer[33] = static_cast(spValue); + buffer[34] = static_cast(spValue >> 8); + + const auto pcValue = targetControllerService.getProgramCounter(); + buffer[35] = static_cast(pcValue); + buffer[36] = static_cast(pcValue >> 8); + buffer[37] = static_cast(pcValue >> 16); + buffer[38] = static_cast(pcValue >> 24); } - // Finally, include the program counter (which GDB expects to reside at the end) - registers.insert(registers.end(), static_cast(programCounter)); - registers.insert(registers.end(), static_cast(programCounter >> 8)); - registers.insert(registers.end(), static_cast(programCounter >> 16)); - registers.insert(registers.end(), static_cast(programCounter >> 24)); - - debugSession.connection.writePacket( - ResponsePacket(Services::StringService::toHex(registers)) - ); + debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex(buffer)}); } catch (const Exception& exception) { Logger::error("Failed to read registers - " + exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } } diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegisters.hpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegisters.hpp index 788b7ccf..0a1834c7 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegisters.hpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/ReadRegisters.hpp @@ -5,6 +5,8 @@ #include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp" #include "src/DebugServer/Gdb/RegisterDescriptor.hpp" +#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp" +#include "src/Targets/TargetMemorySegmentDescriptor.hpp" namespace DebugServer::Gdb::AvrGdb::CommandPackets { @@ -15,10 +17,14 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets class ReadRegisters: public Gdb::CommandPackets::CommandPacket { public: - explicit ReadRegisters(const RawPacket& rawPacket); + const Targets::TargetMemorySegmentDescriptor& gpRegistersMemorySegmentDescriptor; + + explicit ReadRegisters(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor); void handle( Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContContinueExecution.cpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContContinueExecution.cpp index ecbe933c..ddc64c4b 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContContinueExecution.cpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContContinueExecution.cpp @@ -14,16 +14,21 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets : CommandPacket(rawPacket) {} - void VContContinueExecution::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) { + void VContContinueExecution::handle( + Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, + TargetControllerService& targetControllerService + ) { Logger::info("Handling VContContinueExecution packet"); try { - targetControllerService.continueTargetExecution(std::nullopt, std::nullopt); + targetControllerService.resumeTargetExecution(); debugSession.waitingForBreak = true; } catch (const Exception& exception) { Logger::error("Failed to continue execution on target - " + exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } } diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContContinueExecution.hpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContContinueExecution.hpp index cff79062..e22a8293 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContContinueExecution.hpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContContinueExecution.hpp @@ -17,6 +17,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets void handle( Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContRangeStep.cpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContRangeStep.cpp index bb29bf29..57a3f056 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContRangeStep.cpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContRangeStep.cpp @@ -2,6 +2,7 @@ #include +#include "src/Targets/Microchip/AVR8/OpcodeDecoder/Decoder.hpp" #include "src/Services/Avr8InstructionService.hpp" #include "src/Services/StringService.hpp" #include "src/Services/PathService.hpp" @@ -13,43 +14,42 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets { using Services::TargetControllerService; - using ResponsePackets::ErrorResponsePacket; - using ::Exceptions::Exception; - VContRangeStep::VContRangeStep(const RawPacket& rawPacket) + VContRangeStep::VContRangeStep(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor) : CommandPacket(rawPacket) + , programAddressSpaceDescriptor(gdbTargetDescriptor.programAddressSpaceDescriptor) + , programMemorySegmentDescriptor(gdbTargetDescriptor.programMemorySegmentDescriptor) { + using Services::StringService; + if (this->data.size() < 10) { - throw Exception("Unexpected VContRangeStep packet size"); + throw Exception{"Unexpected VContRangeStep packet size"}; } - const auto commandData = std::string(this->data.begin() + 7, this->data.end()); + const auto command = std::string{this->data.begin() + 7, this->data.end()}; - const auto delimiterPosition = commandData.find(','); - const auto threadIdDelimiterPosition = commandData.find(':'); - - if (delimiterPosition == std::string::npos || delimiterPosition >= (commandData.size() - 1)) { - throw Exception("Invalid VContRangeStep packet"); + const auto delimiterPos = command.find(','); + const auto threadIdDelimiterPos = command.find(':'); + if (delimiterPos == std::string::npos || delimiterPos >= (command.size() - 1)) { + throw Exception{"Invalid VContRangeStep packet"}; } - const auto& delimiterIt = commandData.begin() + static_cast( - delimiterPosition + this->startAddress = StringService::toUint32(command.substr(0, delimiterPos), 16); + this->endAddress = StringService::toUint32( + command.substr(delimiterPos + 1, threadIdDelimiterPos - (delimiterPos + 1)), + 16 ); - const auto startAddressHex = std::string(commandData.begin(), delimiterIt); - const auto endAddressHex = std::string( - delimiterIt + 1, - threadIdDelimiterPosition != std::string::npos - ? commandData.begin() + static_cast(threadIdDelimiterPosition) - : commandData.end() - ); - - this->startAddress = static_cast(std::stoi(startAddressHex, nullptr, 16)); - this->endAddress = static_cast(std::stoi(endAddressHex, nullptr, 16)); } - void VContRangeStep::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) { + void VContRangeStep::handle( + Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, + TargetControllerService& targetControllerService + ) { + using Targets::Microchip::Avr8::OpcodeDecoder::Decoder; using Services::Avr8InstructionService; using Services::StringService; @@ -59,19 +59,17 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets Logger::debug("Requested stepping range end address (exclusive): 0x" + StringService::toHex(this->endAddress)); try { - const auto& targetDescriptor = debugSession.gdbTargetDescriptor.targetDescriptor; - const auto& programMemoryAddressRange = targetDescriptor.memoryDescriptorsByType.at( - targetDescriptor.programMemoryType - ).addressRange; + const auto stepAddressRange = Targets::TargetMemoryAddressRange{this->startAddress, this->endAddress}; + const auto stepByteSize = stepAddressRange.size() - 1; // -1 because the end address is exclusive + const auto& programMemoryAddressRange = this->programMemorySegmentDescriptor.addressRange; if ( - this->startAddress > this->endAddress - || (this->startAddress % 2) != 0 - || (this->endAddress % 2) != 0 - || this->startAddress < programMemoryAddressRange.startAddress - || this->endAddress > programMemoryAddressRange.endAddress + stepAddressRange.startAddress > stepAddressRange.endAddress + || (stepAddressRange.startAddress % 2) != 0 + || (stepAddressRange.endAddress % 2) != 0 + || !programMemoryAddressRange.contains(stepAddressRange) ) { - throw Exception("Invalid address range in VContRangeStep"); + throw Exception{"Invalid address range in VContRangeStep"}; } if (debugSession.activeRangeSteppingSession.has_value()) { @@ -81,26 +79,30 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets debugSession.terminateRangeSteppingSession(targetControllerService); } - if (this->startAddress == this->endAddress || (this->endAddress - this->startAddress) <= 2) { + if (stepByteSize <= 2) { // Single step requested. No need for a range step here. - targetControllerService.stepTargetExecution(std::nullopt); + targetControllerService.stepTargetExecution(); debugSession.waitingForBreak = true; return; } - const auto addressRange = Targets::TargetMemoryAddressRange(this->startAddress, this->endAddress); - auto rangeSteppingSession = RangeSteppingSession(addressRange, {}); + auto rangeSteppingSession = RangeSteppingSession{stepAddressRange, {}}; - const auto instructionsByAddress = Avr8InstructionService::fetchInstructions( - addressRange, - targetDescriptor, - targetControllerService + const auto instructionsByAddress = Decoder::decode( + stepAddressRange.startAddress, + targetControllerService.readMemory( + this->programAddressSpaceDescriptor, + this->programMemorySegmentDescriptor, + stepAddressRange.startAddress, + stepByteSize + ) ); Logger::debug( - "Inspecting " + std::to_string(instructionsByAddress.size()) + " instructions within stepping range " - "(byte addresses) 0x" + StringService::toHex(addressRange.startAddress) + " -> 0x" - + StringService::toHex(addressRange.endAddress) + ", in preparation for new range stepping session" + "Inspecting " + std::to_string(instructionsByAddress.size()) + " instruction(s) within stepping range " + "(byte addresses) 0x" + StringService::toHex(stepAddressRange.startAddress) + " -> 0x" + + StringService::toHex(stepAddressRange.endAddress) + ", in preparation for new range stepping " + "session" ); for (const auto& [instructionAddress, instruction] : instructionsByAddress) { @@ -144,10 +146,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets continue; } - if ( - *destinationAddress < programMemoryAddressRange.startAddress - || *destinationAddress > programMemoryAddressRange.endAddress - ) { + if (!programMemoryAddressRange.contains(*destinationAddress)) { /* * This instruction may jump to an invalid address. Someone screwed up here - could be * something wrong in Bloom (opcode decoding bug, incorrect program memory address range in @@ -158,7 +157,7 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets */ Logger::debug( "Intercepting CCPF instruction (\"" + instruction->name + "\") with invalid destination " - "byte address (0x" + StringService::toHex(*destinationAddress) + "), at byte address 0x" + "byte address (0x" + StringService::toHex(*destinationAddress) + "), at byte address 0x" + StringService::toHex(instructionAddress) ); rangeSteppingSession.interceptedAddresses.insert(instructionAddress); @@ -166,8 +165,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets } if ( - *destinationAddress < addressRange.startAddress - || *destinationAddress >= addressRange.endAddress + *destinationAddress < stepAddressRange.startAddress + || *destinationAddress >= stepAddressRange.endAddress ) { /* * This instruction may jump to an address outside the requested stepping range. @@ -185,6 +184,12 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets } } + /* + * Finally, ensure that we intercept the first instruction outside the range (which is the end address + * of the range, because it's exclusive). + */ + rangeSteppingSession.interceptedAddresses.insert(stepAddressRange.endAddress); + debugSession.startRangeSteppingSession(std::move(rangeSteppingSession), targetControllerService); /* @@ -195,12 +200,12 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets * we should continue. See that member function for more. */ debugSession.activeRangeSteppingSession->singleStepping = true; - targetControllerService.stepTargetExecution(std::nullopt); + targetControllerService.stepTargetExecution(); debugSession.waitingForBreak = true; } catch (const Exception& exception) { Logger::error("Failed to start new range stepping session - " + exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } } diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContRangeStep.hpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContRangeStep.hpp index 490f96df..9d4d33fd 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContRangeStep.hpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContRangeStep.hpp @@ -4,6 +4,9 @@ #include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp" +#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp" +#include "src/Targets/TargetAddressSpaceDescriptor.hpp" +#include "src/Targets/TargetMemorySegmentDescriptor.hpp" #include "src/Targets/TargetMemory.hpp" namespace DebugServer::Gdb::AvrGdb::CommandPackets @@ -16,13 +19,18 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets class VContRangeStep: public Gdb::CommandPackets::CommandPacket { public: + const Targets::TargetAddressSpaceDescriptor& programAddressSpaceDescriptor; + const Targets::TargetMemorySegmentDescriptor& programMemorySegmentDescriptor; + Targets::TargetMemoryAddress startAddress; Targets::TargetMemoryAddress endAddress; - explicit VContRangeStep(const RawPacket& rawPacket); + explicit VContRangeStep(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor); void handle( Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContStepExecution.cpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContStepExecution.cpp index 798606dc..5bec8d36 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContStepExecution.cpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContStepExecution.cpp @@ -14,16 +14,21 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets : CommandPacket(rawPacket) {} - void VContStepExecution::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) { + void VContStepExecution::handle( + Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, + TargetControllerService& targetControllerService + ) { Logger::info("Handling VContStepExecution packet"); try { - targetControllerService.stepTargetExecution(std::nullopt); + targetControllerService.stepTargetExecution(); debugSession.waitingForBreak = true; } catch (const Exception& exception) { Logger::error("Failed to step execution on target - " + exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } } diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContStepExecution.hpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContStepExecution.hpp index 7c341c3e..5ee96fa7 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContStepExecution.hpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContStepExecution.hpp @@ -16,6 +16,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets void handle( Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContSupportedActionsQuery.cpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContSupportedActionsQuery.cpp index c0fa061b..7d6ddaf6 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContSupportedActionsQuery.cpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContSupportedActionsQuery.cpp @@ -12,15 +12,20 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets : CommandPacket(rawPacket) {} - void VContSupportedActionsQuery::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) { + void VContSupportedActionsQuery::handle( + Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, + TargetControllerService& targetControllerService + ) { Logger::info("Handling VContSupportedActionsQuery packet"); // Respond with a SupportedFeaturesResponse packet, listing all supported GDB features by Bloom - debugSession.connection.writePacket(ResponsePackets::ResponsePacket( + debugSession.connection.writePacket(ResponsePackets::ResponsePacket{ debugSession.serverConfig.rangeStepping ? "vCont;c;C;s;S;r" : "vCont;c;C;s;S" - ) + } ); } } diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContSupportedActionsQuery.hpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContSupportedActionsQuery.hpp index c2575276..4cda486c 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContSupportedActionsQuery.hpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/VContSupportedActionsQuery.hpp @@ -20,6 +20,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets void handle( Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteMemory.cpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteMemory.cpp index f7fd5e1e..694e80d0 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteMemory.cpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteMemory.cpp @@ -3,6 +3,7 @@ #include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp" #include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp" +#include "src/Services/StringService.hpp" #include "src/Logger/Logger.hpp" #include "src/Exceptions/Exception.hpp" @@ -16,124 +17,116 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets using namespace Exceptions; WriteMemory::WriteMemory(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor) - : CommandPacket(rawPacket) - { - if (this->data.size() < 4) { - throw Exception("Invalid packet length"); - } + : WriteMemory(rawPacket, gdbTargetDescriptor, WriteMemory::extractPacketData(rawPacket)) + {} - const auto packetString = QString::fromLocal8Bit( - reinterpret_cast(this->data.data() + 1), - static_cast(this->data.size() - 1) - ); - - /* - * The write memory ('M') packet consists of three segments, an address, a length and a buffer. - * The address and length are separated by a comma character, and the buffer proceeds a colon character. - */ - const auto packetSegments = packetString.split(","); - if (packetSegments.size() != 2) { - throw Exception( - "Unexpected number of segments in packet data: " + std::to_string(packetSegments.size()) - ); - } - - bool conversionStatus = false; - const auto gdbStartAddress = packetSegments.at(0).toUInt(&conversionStatus, 16); - - if (!conversionStatus) { - throw Exception("Failed to parse start address from write memory packet data"); - } - - this->memoryType = gdbTargetDescriptor.getMemoryTypeFromGdbAddress(gdbStartAddress); - this->startAddress = gdbStartAddress & ~(gdbTargetDescriptor.getMemoryOffset(this->memoryType)); - - const auto lengthAndBufferSegments = packetSegments.at(1).split(":"); - if (lengthAndBufferSegments.size() != 2) { - throw Exception( - "Unexpected number of segments in packet data: " - + std::to_string(lengthAndBufferSegments.size()) - ); - } - - const auto bufferSize = lengthAndBufferSegments.at(0).toUInt(&conversionStatus, 16); - if (!conversionStatus) { - throw Exception("Failed to parse write length from write memory packet data"); - } - - this->buffer = Packet::hexToData(lengthAndBufferSegments.at(1).toStdString()); - - if (this->buffer.size() != bufferSize) { - throw Exception("Buffer size does not match length value given in write memory packet"); - } - } - - void WriteMemory::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) { + void WriteMemory::handle( + Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, + TargetControllerService& targetControllerService + ) { Logger::info("Handling WriteMemory packet"); try { - const auto& memoryDescriptorsByType = debugSession.gdbTargetDescriptor.targetDescriptor.memoryDescriptorsByType; - const auto memoryDescriptorIt = memoryDescriptorsByType.find(this->memoryType); - - if (memoryDescriptorIt == memoryDescriptorsByType.end()) { - throw Exception("Target does not support the requested memory type."); - } - - if (this->memoryType == Targets::TargetMemoryType::FLASH) { - /* - * This shouldn't happen - GDB should send the FlashWrite (vFlashWrite) packet to write to the target's - * program memory. - * - * A number of actions have to be taken before we can write to the target's program memory - this is - * all covered in the FlashWrite and FlashDone command classes. I don't want to cover it again in here, - * so just respond with an error and request that this issue be reported. - */ - throw Exception( - "GDB attempted to write to program memory via an \"M\" packet - this is not supported. Please " - "report this issue to Bloom developers with the full debug log." - ); - } - if (this->buffer.size() == 0) { - debugSession.connection.writePacket(OkResponsePacket()); + debugSession.connection.writePacket(OkResponsePacket{}); return; } - const auto& memoryDescriptor = memoryDescriptorIt->second; - - if (this->memoryType == Targets::TargetMemoryType::EEPROM) { - // GDB sends EEPROM addresses in relative form - we convert them to absolute form, here. - this->startAddress = memoryDescriptor.addressRange.startAddress + this->startAddress; - } - - /* - * In AVR targets, RAM is mapped to many registers and peripherals - we don't want to block GDB from - * accessing them. - */ - const auto memoryStartAddress = (this->memoryType == Targets::TargetMemoryType::RAM) - ? 0x00 - : memoryDescriptor.addressRange.startAddress; - - if ( - this->startAddress < memoryStartAddress - || (this->startAddress + (this->buffer.size() - 1)) > memoryDescriptor.addressRange.endAddress - ) { - throw Exception( - "GDB requested access to memory which is outside the target's memory range" - ); - } - - targetControllerService.writeMemory( - this->memoryType, + const auto addressRange = Targets::TargetMemoryAddressRange{ this->startAddress, - this->buffer + this->startAddress + static_cast(this->buffer.size()) - 1 + }; + + const auto memorySegmentDescriptors = this->addressSpaceDescriptor.getIntersectingMemorySegmentDescriptors( + addressRange ); - debugSession.connection.writePacket(OkResponsePacket()); + auto accessibleBytes = Targets::TargetMemorySize{0}; + for (const auto* memorySegmentDescriptor : memorySegmentDescriptors) { + if (!memorySegmentDescriptor->debugModeAccess.writeable) { + throw Exception{ + "Attempted to access restricted memory segment (" + memorySegmentDescriptor->key + + ") - segment not writeable in debug mode" + }; + } + + accessibleBytes += memorySegmentDescriptor->addressRange.intersectingSize(addressRange); + } + + if (accessibleBytes < this->bytes) { + throw Exception{"GDB requested access to memory which does not reside within any memory segment"}; + } + + { + const auto atomicSession = targetControllerService.makeAtomicSession(); + + for (const auto* memorySegmentDescriptor : memorySegmentDescriptors) { + const auto segmentStartAddress = std::max( + this->startAddress, + memorySegmentDescriptor->addressRange.startAddress + ); + + const auto bufferOffsetIt = buffer.begin() + (segmentStartAddress - this->startAddress); + targetControllerService.writeMemory( + this->addressSpaceDescriptor, + *memorySegmentDescriptor, + segmentStartAddress, + Targets::TargetMemoryBuffer{ + bufferOffsetIt, + bufferOffsetIt + memorySegmentDescriptor->addressRange.intersectingSize(addressRange) + } + ); + } + } + + debugSession.connection.writePacket(OkResponsePacket{}); } catch (const Exception& exception) { Logger::error("Failed to write memory to target - " + exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); + } + } + + WriteMemory::PacketData WriteMemory::extractPacketData(const RawPacket& rawPacket) { + using Services::StringService; + + if (rawPacket.size() < 8) { + throw Exception{"Invalid packet length"}; + } + + const auto command = std::string{rawPacket.begin() + 2, rawPacket.end() - 3}; + + const auto commaDelimiterPos = command.find_first_of(','); + const auto colonDelimiterPos = command.find_first_of(':'); + if (commaDelimiterPos == std::string::npos || colonDelimiterPos == std::string::npos) { + throw Exception{"Invalid packet"}; + } + + return { + StringService::toUint32(command.substr(0, commaDelimiterPos), 16), + StringService::toUint32( + command.substr(commaDelimiterPos + 1, colonDelimiterPos - (commaDelimiterPos + 1)), + 16 + ), + StringService::dataFromHex(command.substr(colonDelimiterPos + 1)) + }; + } + + WriteMemory::WriteMemory( + const RawPacket& rawPacket, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + PacketData&& packetData + ) + : CommandPacket(rawPacket) + , addressSpaceDescriptor(gdbTargetDescriptor.addressSpaceDescriptorFromGdbAddress(packetData.gdbStartAddress)) + , startAddress(gdbTargetDescriptor.translateGdbAddress(packetData.gdbStartAddress)) + , bytes(packetData.bytes) + , buffer(std::move(packetData.buffer)) + { + if (this->buffer.size() != this->bytes) { + throw Exception{"Buffer size does not match length value given in write memory packet"}; } } } diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteMemory.hpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteMemory.hpp index c4542697..1fea029b 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteMemory.hpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteMemory.hpp @@ -4,9 +4,10 @@ #include #include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp" -#include "src/DebugServer/Gdb/TargetDescriptor.hpp" +#include "src/Targets/TargetAddressSpaceDescriptor.hpp" #include "src/Targets/TargetMemory.hpp" +#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp" namespace DebugServer::Gdb::AvrGdb::CommandPackets { @@ -17,26 +18,29 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets class WriteMemory: public Gdb::CommandPackets::CommandPacket { public: - /** - * Start address of the memory operation. - */ - Targets::TargetMemoryAddress startAddress = 0; - - /** - * The type of memory to read from. - */ - Targets::TargetMemoryType memoryType = Targets::TargetMemoryType::FLASH; - - /** - * Data to write. - */ + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor; + Targets::TargetMemoryAddress startAddress; + Targets::TargetMemorySize bytes; Targets::TargetMemoryBuffer buffer; - explicit WriteMemory(const RawPacket& rawPacket, const Gdb::TargetDescriptor& gdbTargetDescriptor); + explicit WriteMemory(const RawPacket& rawPacket, const TargetDescriptor& gdbTargetDescriptor); void handle( Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; + + private: + struct PacketData + { + std::uint32_t gdbStartAddress; + std::uint32_t bytes; + Targets::TargetMemoryBuffer buffer; + }; + + static PacketData extractPacketData(const RawPacket& rawPacket); + WriteMemory(const RawPacket& rawPacket, const Gdb::TargetDescriptor& gdbTargetDescriptor, PacketData&& packetData); }; } diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteRegister.cpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteRegister.cpp index fe7f1f01..aa022c9d 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteRegister.cpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteRegister.cpp @@ -3,9 +3,7 @@ #include "src/DebugServer/Gdb/ResponsePackets/OkResponsePacket.hpp" #include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp" -#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp" -#include "src/Targets/TargetRegister.hpp" - +#include "src/Services/StringService.hpp" #include "src/Logger/Logger.hpp" #include "src/Exceptions/Exception.hpp" @@ -13,9 +11,6 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets { using Services::TargetControllerService; - using Targets::TargetRegister; - using Targets::TargetRegisterDescriptors; - using ResponsePackets::ResponsePacket; using ResponsePackets::OkResponsePacket; using ResponsePackets::ErrorResponsePacket; @@ -25,87 +20,88 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets WriteRegister::WriteRegister(const RawPacket& rawPacket) : CommandPacket(rawPacket) { + using Services::StringService; + + if (this->data.size() < 4) { + throw Exception{"Invalid WriteRegister command packet - insufficient data in packet."}; + } + // The P packet updates a single register - auto packet = std::string(this->data.begin(), this->data.end()); + auto command = std::string{this->data.begin() + 1, this->data.end()}; - if (packet.size() < 4) { - throw Exception("Invalid WriteRegister command packet - insufficient data in packet."); + const auto delimiterPos = command.find_first_of('='); + if (delimiterPos == std::string::npos) { + throw Exception{"Invalid packet"}; } - if (packet.find('=') == std::string::npos) { - throw Exception("Invalid WriteRegister command packet - unexpected format"); - } - - const auto packetSegments = QString::fromStdString(packet).split("="); - this->registerId = static_cast(packetSegments.front().mid(1).toUInt(nullptr, 16)); - this->registerValue = Packet::hexToData(packetSegments.back().toStdString()); + this->registerId = static_cast(StringService::toUint32(command.substr(0, delimiterPos), 16)); + this->registerValue = Services::StringService::dataFromHex(command.substr(delimiterPos + 1)); if (this->registerValue.empty()) { - throw Exception("Invalid WriteRegister command packet - missing register value"); + throw Exception{"Invalid WriteRegister command packet - missing register value"}; } + // LSB to MSB std::reverse(this->registerValue.begin(), this->registerValue.end()); } - void WriteRegister::handle(Gdb::DebugSession& debugSession, TargetControllerService& targetControllerService) { + void WriteRegister::handle( + Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, + TargetControllerService& targetControllerService + ) { Logger::info("Handling WriteRegister packet"); try { if (this->registerId == TargetDescriptor::PROGRAM_COUNTER_GDB_REGISTER_ID) { + if (this->registerValue.size() != 4) { + throw Exception{"Invalid PC value register size"}; + } + targetControllerService.setProgramCounter( static_cast( - (this->registerValue.size() >= 1 ? this->registerValue[0] : 0x00) << 24 - | (this->registerValue.size() >= 2 ? this->registerValue[1] : 0x00) << 16 - | (this->registerValue.size() >= 3 ? this->registerValue[2] : 0x00) << 8 - | (this->registerValue.size() >= 4 ? this->registerValue[3] : 0x00) + this->registerValue[0] << 24 + | this->registerValue[1] << 16 + | this->registerValue[2] << 8 + | this->registerValue[3] ) ); - debugSession.connection.writePacket(OkResponsePacket()); + debugSession.connection.writePacket(OkResponsePacket{}); return; } - const auto& gdbTargetDescriptor = debugSession.gdbTargetDescriptor; - const auto descriptorId = gdbTargetDescriptor.getTargetRegisterDescriptorIdFromGdbRegisterId( + if (this->registerId == TargetDescriptor::STACK_POINTER_GDB_REGISTER_ID) { + if (this->registerValue.size() != 2) { + throw Exception{"Invalid SP register value size"}; + } + + targetControllerService.setStackPointer( + static_cast(this->registerValue[0] << 8 | this->registerValue[1]) + ); + + debugSession.connection.writePacket(OkResponsePacket{}); + return; + } + + const auto gdbRegisterDescriptorIt = gdbTargetDescriptor.gdbRegisterDescriptorsById.find(this->registerId); + const auto targetRegisterDescriptorIt = gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.find( this->registerId ); - - if (!descriptorId.has_value()) { - throw Exception("Invalid/unknown register"); + if ( + gdbRegisterDescriptorIt == gdbTargetDescriptor.gdbRegisterDescriptorsById.end() + || targetRegisterDescriptorIt == gdbTargetDescriptor.targetRegisterDescriptorsByGdbId.end() + ) { + throw Exception{"Unknown GDB register ID (" + std::to_string(this->registerId) + ")"}; } - const auto& descriptor = gdbTargetDescriptor.targetDescriptor.registerDescriptorsById.at(*descriptorId); - - if (this->registerValue.size() > descriptor.size) { - // Attempt to trim the higher zero-value bytes from the register value, until we reach the correct size. - for (auto i = this->registerValue.size() - 1; i >= descriptor.size; --i) { - if (this->registerValue.at(i) != 0x00) { - // If we reach a non-zero byte, we cannot trim anymore without changing the data - break; - } - - this->registerValue.erase(this->registerValue.begin() + i); - } - - if (this->registerValue.size() > descriptor.size) { - const auto& gdbRegisterDescriptor = gdbTargetDescriptor.gdbRegisterDescriptorsById.at( - this->registerId - ); - throw Exception( - "Cannot set value for " + gdbRegisterDescriptor.name + " - value size exceeds register size." - ); - } - } - - targetControllerService.writeRegisters({ - TargetRegister(descriptor.id, this->registerValue) - }); - - debugSession.connection.writePacket(OkResponsePacket()); + targetControllerService.writeRegister(*(targetRegisterDescriptorIt->second), this->registerValue); + debugSession.connection.writePacket(OkResponsePacket{}); } catch (const Exception& exception) { Logger::error("Failed to write registers - " + exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } } diff --git a/src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteRegister.hpp b/src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteRegister.hpp index 51dda24f..506e575f 100644 --- a/src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteRegister.hpp +++ b/src/DebugServer/Gdb/AvrGdb/CommandPackets/WriteRegister.hpp @@ -3,6 +3,7 @@ #include "src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp" #include "src/DebugServer/Gdb/RegisterDescriptor.hpp" +#include "src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp" #include "src/Targets/TargetMemory.hpp" @@ -21,6 +22,8 @@ namespace DebugServer::Gdb::AvrGdb::CommandPackets void handle( Gdb::DebugSession& debugSession, + const Gdb::TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/AvrGdb/DebugSession.cpp b/src/DebugServer/Gdb/AvrGdb/DebugSession.cpp index 8f12f26e..cf08d8f1 100644 --- a/src/DebugServer/Gdb/AvrGdb/DebugSession.cpp +++ b/src/DebugServer/Gdb/AvrGdb/DebugSession.cpp @@ -5,14 +5,8 @@ namespace DebugServer::Gdb::AvrGdb DebugSession::DebugSession( Connection&& connection, const std::set>>& supportedFeatures, - const TargetDescriptor& targetDescriptor, const GdbDebugServerConfig& serverConfig ) - : Gdb::DebugSession( - std::move(connection), - supportedFeatures, - targetDescriptor, - serverConfig - ) + : Gdb::DebugSession(std::move(connection), supportedFeatures, serverConfig) {} } diff --git a/src/DebugServer/Gdb/AvrGdb/DebugSession.hpp b/src/DebugServer/Gdb/AvrGdb/DebugSession.hpp index 26876799..1d6418f4 100644 --- a/src/DebugServer/Gdb/AvrGdb/DebugSession.hpp +++ b/src/DebugServer/Gdb/AvrGdb/DebugSession.hpp @@ -12,7 +12,6 @@ namespace DebugServer::Gdb::AvrGdb DebugSession( Connection&& connection, const std::set>>& supportedFeatures, - const TargetDescriptor& targetDescriptor, const GdbDebugServerConfig& serverConfig ); }; diff --git a/src/DebugServer/Gdb/AvrGdb/TargetDescriptor.cpp b/src/DebugServer/Gdb/AvrGdb/TargetDescriptor.cpp index f0209ee4..4c3b80e4 100644 --- a/src/DebugServer/Gdb/AvrGdb/TargetDescriptor.cpp +++ b/src/DebugServer/Gdb/AvrGdb/TargetDescriptor.cpp @@ -1,9 +1,6 @@ #include "TargetDescriptor.hpp" -#include - #include "src/Exceptions/Exception.hpp" -#include "src/Logger/Logger.hpp" namespace DebugServer::Gdb::AvrGdb { @@ -13,149 +10,110 @@ namespace DebugServer::Gdb::AvrGdb using Exceptions::Exception; TargetDescriptor::TargetDescriptor(const Targets::TargetDescriptor& targetDescriptor) - : DebugServer::Gdb::TargetDescriptor( - targetDescriptor, - { - {Targets::TargetMemoryType::FLASH, 0}, - {Targets::TargetMemoryType::RAM, 0x00800000U}, - {Targets::TargetMemoryType::EEPROM, 0x00810000U}, - }, - {}, - {}, - {} - ) + : programAddressSpaceDescriptor(targetDescriptor.getAddressSpaceDescriptor("prog")) + , eepromAddressSpaceDescriptor(targetDescriptor.getFirstAddressSpaceDescriptorContainingMemorySegment("internal_eeprom")) + , sramAddressSpaceDescriptor(targetDescriptor.getAddressSpaceDescriptor("data")) + , gpRegistersAddressSpaceDescriptor(targetDescriptor.getFirstAddressSpaceDescriptorContainingMemorySegment("gp_registers")) + , programMemorySegmentDescriptor(this->programAddressSpaceDescriptor.getMemorySegmentDescriptor("internal_program_memory")) + , eepromMemorySegmentDescriptor(this->eepromAddressSpaceDescriptor.getMemorySegmentDescriptor("internal_eeprom")) + , sramMemorySegmentDescriptor(this->sramAddressSpaceDescriptor.getMemorySegmentDescriptor("internal_ram")) + , gpRegistersMemorySegmentDescriptor(this->gpRegistersAddressSpaceDescriptor.getMemorySegmentDescriptor("gp_registers")) + , cpuGpPeripheralDescriptor(targetDescriptor.getPeripheralDescriptor("cpu_gpr")) + , cpuGpRegisterGroupDescriptor(this->cpuGpPeripheralDescriptor.getRegisterGroupDescriptor("gpr")) { - this->loadRegisterMappings(); - } - - void TargetDescriptor::loadRegisterMappings() { - const auto generalPurposeTargetRegisterDescriptorIds = this->targetDescriptor.registerDescriptorIdsForType( - TargetRegisterType::GENERAL_PURPOSE_REGISTER - ); - - const auto statusTargetRegisterDescriptorIds = this->targetDescriptor.registerDescriptorIdsForType( - TargetRegisterType::STATUS_REGISTER - ); - - const auto stackPointerTargetRegisterDescriptorIds = this->targetDescriptor.registerDescriptorIdsForType( - TargetRegisterType::STACK_POINTER - ); - - if (generalPurposeTargetRegisterDescriptorIds.size() != 32) { - throw Exception("Unexpected general purpose register count"); - } - - if (statusTargetRegisterDescriptorIds.empty()) { - throw Exception("Missing status register descriptor"); - } - - if (stackPointerTargetRegisterDescriptorIds.empty()) { - throw Exception("Missing stack pointer register descriptor"); - } - /* * For AVR targets, GDB defines 35 registers in total: * * - Register ID 0 through 31 are general purpose registers * - Register ID 32 is the status register (SREG) - * - Register ID 33 is the stack pointer register - * - Register ID 34 is the program counter - * - * For AVR targets, we don't have a target register descriptor for the program counter, so we don't map that - * GDB register ID (34) to anything here. Instead, the register command packet handlers (ReadRegisters, - * WriteRegister, etc) will handle any operations involving that GDB register. + * - Register ID 33 is the stack pointer register (SP) + * - Register ID 34 is the program counter (PC) */ - // General purpose registers - GdbRegisterId gdbRegisterId = 0; - for (const auto descriptorId : generalPurposeTargetRegisterDescriptorIds) { - auto gdbRegisterDescriptor = RegisterDescriptor( - gdbRegisterId, - 1, - "General Purpose Register " + std::to_string(gdbRegisterId) + // Create the GDB register descriptors and populate the mappings for the general purpose registers (ID 0->31) + for (const auto& [key, descriptor] : this->cpuGpRegisterGroupDescriptor.registerDescriptorsByKey) { + if (descriptor.type != TargetRegisterType::GENERAL_PURPOSE_REGISTER) { + continue; + } + + const auto gdbRegisterId = static_cast( + descriptor.startAddress - this->gpRegistersMemorySegmentDescriptor.addressRange.startAddress ); - this->gdbRegisterIdsByTargetRegisterDescriptorId.emplace(descriptorId, gdbRegisterDescriptor.id); - this->targetRegisterDescriptorIdsByGdbRegisterId.emplace(gdbRegisterDescriptor.id, descriptorId); - - this->gdbRegisterDescriptorsById.emplace(gdbRegisterDescriptor.id, std::move(gdbRegisterDescriptor)); - - gdbRegisterId++; + this->gdbRegisterDescriptorsById.emplace(gdbRegisterId, RegisterDescriptor{gdbRegisterId, 1}); + this->targetRegisterDescriptorsByGdbId.emplace(gdbRegisterId, &descriptor); } - const auto& statusTargetRegisterDescriptor = this->targetDescriptor.registerDescriptorsById.at( - *(statusTargetRegisterDescriptorIds.begin()) - ); - - auto statusGdbRegisterDescriptor = RegisterDescriptor( + this->gdbRegisterDescriptorsById.emplace( TargetDescriptor::STATUS_GDB_REGISTER_ID, - 1, - "Status Register" + RegisterDescriptor{TargetDescriptor::STATUS_GDB_REGISTER_ID, 1} ); - - if (statusTargetRegisterDescriptor.size > statusGdbRegisterDescriptor.size) { - throw Exception("AVR8 status target register size exceeds the GDB register size."); - } - - this->gdbRegisterIdsByTargetRegisterDescriptorId.emplace( - statusTargetRegisterDescriptor.id, - statusGdbRegisterDescriptor.id - ); - this->targetRegisterDescriptorIdsByGdbRegisterId.emplace( - statusGdbRegisterDescriptor.id, - statusTargetRegisterDescriptor.id - ); - - this->gdbRegisterDescriptorsById.emplace( - statusGdbRegisterDescriptor.id, - std::move(statusGdbRegisterDescriptor) - ); - - const auto& stackPointerTargetRegisterDescriptor = this->targetDescriptor.registerDescriptorsById.at( - *(stackPointerTargetRegisterDescriptorIds.begin()) - ); - - auto stackPointerGdbRegisterDescriptor = RegisterDescriptor( - TargetDescriptor::STACK_POINTER_GDB_REGISTER_ID, - 2, - "Stack Pointer Register" - ); - - if (stackPointerTargetRegisterDescriptor.size > stackPointerGdbRegisterDescriptor.size) { - throw Exception("AVR8 stack pointer target register size exceeds the GDB register size."); - } - - this->gdbRegisterIdsByTargetRegisterDescriptorId.emplace( - stackPointerTargetRegisterDescriptor.id, - stackPointerGdbRegisterDescriptor.id - ); - this->targetRegisterDescriptorIdsByGdbRegisterId.emplace( - stackPointerGdbRegisterDescriptor.id, - stackPointerTargetRegisterDescriptor.id - ); - - this->gdbRegisterDescriptorsById.emplace( - stackPointerGdbRegisterDescriptor.id, - std::move(stackPointerGdbRegisterDescriptor) + this->targetRegisterDescriptorsByGdbId.emplace( + TargetDescriptor::STATUS_GDB_REGISTER_ID, + &(targetDescriptor.getPeripheralDescriptor("cpu").getRegisterGroupDescriptor("cpu") + .getRegisterDescriptor("sreg")) ); /* - * We acknowledge the GDB program counter register here, but we don't map it to any target register descriptors. - * - * This is because we can't access the program counter on AVR targets in the same way we do with other - * registers. We don't have a register descriptor for the program counter. We have to treat it as a special - * case in the register access command packet handlers. See CommandPackets::ReadRegister, + * We don't map the SP and PC GDB register IDs to target register descriptors because of inconsistencies. + * The register command handlers will deal with these registers separately. See CommandPackets::ReadRegister, * CommandPackets::WriteRegister, etc for more. */ - auto programCounterGdbRegisterDescriptor = RegisterDescriptor( - TargetDescriptor::PROGRAM_COUNTER_GDB_REGISTER_ID, - 4, - "Program Counter" + this->gdbRegisterDescriptorsById.emplace( + TargetDescriptor::STACK_POINTER_GDB_REGISTER_ID, + RegisterDescriptor{TargetDescriptor::STACK_POINTER_GDB_REGISTER_ID, 2} ); this->gdbRegisterDescriptorsById.emplace( - programCounterGdbRegisterDescriptor.id, - std::move(programCounterGdbRegisterDescriptor) + TargetDescriptor::PROGRAM_COUNTER_GDB_REGISTER_ID, + RegisterDescriptor{TargetDescriptor::PROGRAM_COUNTER_GDB_REGISTER_ID, 4} ); } + + const Targets::TargetAddressSpaceDescriptor& TargetDescriptor::addressSpaceDescriptorFromGdbAddress( + GdbMemoryAddress address + ) const { + if ((address & TargetDescriptor::EEPROM_ADDRESS_MASK) == TargetDescriptor::EEPROM_ADDRESS_MASK) { + return this->eepromAddressSpaceDescriptor; + } + + if ((address & TargetDescriptor::SRAM_ADDRESS_MASK) == TargetDescriptor::SRAM_ADDRESS_MASK) { + return this->sramAddressSpaceDescriptor; + } + + return this->programAddressSpaceDescriptor; + } + + Targets::TargetMemoryAddress TargetDescriptor::translateGdbAddress(GdbMemoryAddress address) const { + if ((address & TargetDescriptor::EEPROM_ADDRESS_MASK) == TargetDescriptor::EEPROM_ADDRESS_MASK) { + // GDB sends EEPROM addresses in relative form - convert them to absolute form. + return this->eepromMemorySegmentDescriptor.addressRange.startAddress + + (address & ~(TargetDescriptor::EEPROM_ADDRESS_MASK)); + } + + if ((address & TargetDescriptor::SRAM_ADDRESS_MASK) == TargetDescriptor::SRAM_ADDRESS_MASK) { + return address & ~(TargetDescriptor::SRAM_ADDRESS_MASK); + } + + return address; + } + + GdbMemoryAddress TargetDescriptor::translateTargetMemoryAddress( + Targets::TargetMemoryAddress address, + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor + ) const { + if (memorySegmentDescriptor.type == Targets::TargetMemorySegmentType::FLASH) { + return address; + } + + if (memorySegmentDescriptor.type == Targets::TargetMemorySegmentType::EEPROM) { + // GDB expects EEPROM addresses in relative form + return (address - memorySegmentDescriptor.addressRange.startAddress) + | TargetDescriptor::EEPROM_ADDRESS_MASK; + } + + // We assume everything else is SRAM + return address | TargetDescriptor::SRAM_ADDRESS_MASK; + } } diff --git a/src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp b/src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp index 33ed091e..b6af786b 100644 --- a/src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp +++ b/src/DebugServer/Gdb/AvrGdb/TargetDescriptor.hpp @@ -2,28 +2,48 @@ #include "src/DebugServer/Gdb/TargetDescriptor.hpp" +#include "src/Targets/TargetDescriptor.hpp" +#include "src/Targets/TargetAddressSpaceDescriptor.hpp" +#include "src/Targets/TargetMemorySegmentDescriptor.hpp" +#include "src/Targets/TargetPeripheralDescriptor.hpp" +#include "src/Targets/TargetRegisterGroupDescriptor.hpp" + namespace DebugServer::Gdb::AvrGdb { class TargetDescriptor: public DebugServer::Gdb::TargetDescriptor { public: + static constexpr auto SRAM_ADDRESS_MASK = 0x00800000U; + static constexpr auto EEPROM_ADDRESS_MASK = 0x00810000U; + static constexpr auto STATUS_GDB_REGISTER_ID = 32; static constexpr auto STACK_POINTER_GDB_REGISTER_ID = 33; static constexpr auto PROGRAM_COUNTER_GDB_REGISTER_ID = 34; + const Targets::TargetAddressSpaceDescriptor& programAddressSpaceDescriptor; + const Targets::TargetAddressSpaceDescriptor& eepromAddressSpaceDescriptor; + const Targets::TargetAddressSpaceDescriptor& sramAddressSpaceDescriptor; + const Targets::TargetAddressSpaceDescriptor& gpRegistersAddressSpaceDescriptor; + + const Targets::TargetMemorySegmentDescriptor& programMemorySegmentDescriptor; + const Targets::TargetMemorySegmentDescriptor& eepromMemorySegmentDescriptor; + const Targets::TargetMemorySegmentDescriptor& sramMemorySegmentDescriptor; + const Targets::TargetMemorySegmentDescriptor& gpRegistersMemorySegmentDescriptor; + + const Targets::TargetPeripheralDescriptor& cpuGpPeripheralDescriptor; + const Targets::TargetRegisterGroupDescriptor& cpuGpRegisterGroupDescriptor; + explicit TargetDescriptor(const Targets::TargetDescriptor& targetDescriptor); - private: - /** - * For AVR targets, avr-gdb defines 35 registers in total: - * - * Register number 0 through 31 are general purpose registers - * Register number 32 is the status register (SREG) - * Register number 33 is the stack pointer register - * Register number 34 is the program counter register - * - * This function will prepare the appropriate GDB register numbers and mappings. - */ - void loadRegisterMappings(); + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptorFromGdbAddress( + GdbMemoryAddress address + ) const override; + + Targets::TargetMemoryAddress translateGdbAddress(GdbMemoryAddress address) const override; + GdbMemoryAddress translateTargetMemoryAddress( + Targets::TargetMemoryAddress address, + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor + ) const override; }; } diff --git a/src/DebugServer/Gdb/CommandPackets/ActivateInsight.cpp b/src/DebugServer/Gdb/CommandPackets/ActivateInsight.cpp index cceda039..898db9af 100644 --- a/src/DebugServer/Gdb/CommandPackets/ActivateInsight.cpp +++ b/src/DebugServer/Gdb/CommandPackets/ActivateInsight.cpp @@ -22,19 +22,24 @@ namespace DebugServer::Gdb::CommandPackets : Monitor(std::move(monitorPacket)) {} - void ActivateInsight::handle(DebugSession& debugSession, TargetControllerService&) { + void ActivateInsight::handle( + DebugSession& debugSession, + const TargetDescriptor&, + const Targets::TargetDescriptor&, + TargetControllerService& + ) { Logger::info("Handling ActivateInsight packet"); try { EventManager::triggerEvent(std::make_shared()); - debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex( - "The Insight GUI will be with you shortly.\n" - ))); + debugSession.connection.writePacket( + ResponsePacket{Services::StringService::toHex("The Insight GUI will be with you shortly.\n")} + ); } catch (const Exception& exception) { Logger::error("Failed to activate Insight - " + exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } } diff --git a/src/DebugServer/Gdb/CommandPackets/ActivateInsight.hpp b/src/DebugServer/Gdb/CommandPackets/ActivateInsight.hpp index a23cec75..b109367a 100644 --- a/src/DebugServer/Gdb/CommandPackets/ActivateInsight.hpp +++ b/src/DebugServer/Gdb/CommandPackets/ActivateInsight.hpp @@ -16,6 +16,8 @@ namespace DebugServer::Gdb::CommandPackets void handle( DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/CommandPackets/BloomVersion.cpp b/src/DebugServer/Gdb/CommandPackets/BloomVersion.cpp index 5916a61a..c5d4ba69 100644 --- a/src/DebugServer/Gdb/CommandPackets/BloomVersion.cpp +++ b/src/DebugServer/Gdb/CommandPackets/BloomVersion.cpp @@ -22,15 +22,20 @@ namespace DebugServer::Gdb::CommandPackets : Monitor(std::move(monitorPacket)) {} - void BloomVersion::handle(DebugSession& debugSession, TargetControllerService&) { + void BloomVersion::handle( + DebugSession& debugSession, + const TargetDescriptor&, + const Targets::TargetDescriptor&, + TargetControllerService& + ) { Logger::info("Handling BloomVersion packet"); - debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex( - std::string( + debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex( + std::string{ "Bloom v" + Application::VERSION.toString() + "\n" + Services::PathService::homeDomainName() + "\n" + "Nav Mohammed\n" - ) - ))); + } + )}); } } diff --git a/src/DebugServer/Gdb/CommandPackets/BloomVersion.hpp b/src/DebugServer/Gdb/CommandPackets/BloomVersion.hpp index 88962d98..59f17b1f 100644 --- a/src/DebugServer/Gdb/CommandPackets/BloomVersion.hpp +++ b/src/DebugServer/Gdb/CommandPackets/BloomVersion.hpp @@ -18,6 +18,8 @@ namespace DebugServer::Gdb::CommandPackets void handle( DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/CommandPackets/BloomVersionMachine.cpp b/src/DebugServer/Gdb/CommandPackets/BloomVersionMachine.cpp index 7d6850b5..84a08a2a 100644 --- a/src/DebugServer/Gdb/CommandPackets/BloomVersionMachine.cpp +++ b/src/DebugServer/Gdb/CommandPackets/BloomVersionMachine.cpp @@ -21,18 +21,28 @@ namespace DebugServer::Gdb::CommandPackets : Monitor(std::move(monitorPacket)) {} - void BloomVersionMachine::handle(DebugSession& debugSession, TargetControllerService&) { + void BloomVersionMachine::handle( + DebugSession& debugSession, + const TargetDescriptor&, + const Targets::TargetDescriptor&, + TargetControllerService& + ) { Logger::info("Handling BloomVersionMachine packet"); - debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex( - QJsonDocument(QJsonObject({ - {"version", QString::fromStdString(Application::VERSION.toString())}, - {"components", QJsonObject({ - {"major", Application::VERSION.major}, - {"minor", Application::VERSION.minor}, - {"patch", Application::VERSION.patch}, - })}, - })).toJson().toStdString() - ))); + debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex( + QJsonDocument{ + QJsonObject{ + {"version", QString::fromStdString(Application::VERSION.toString())}, + { + "components", + QJsonObject{ + {"major", Application::VERSION.major}, + {"minor", Application::VERSION.minor}, + {"patch", Application::VERSION.patch}, + } + }, + } + }.toJson().toStdString() + )}); } } diff --git a/src/DebugServer/Gdb/CommandPackets/BloomVersionMachine.hpp b/src/DebugServer/Gdb/CommandPackets/BloomVersionMachine.hpp index cd5ff4e8..cfa4f9ac 100644 --- a/src/DebugServer/Gdb/CommandPackets/BloomVersionMachine.hpp +++ b/src/DebugServer/Gdb/CommandPackets/BloomVersionMachine.hpp @@ -18,6 +18,8 @@ namespace DebugServer::Gdb::CommandPackets void handle( DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/CommandPackets/CommandPacket.cpp b/src/DebugServer/Gdb/CommandPackets/CommandPacket.cpp index 97f7e506..d608c8a7 100644 --- a/src/DebugServer/Gdb/CommandPackets/CommandPacket.cpp +++ b/src/DebugServer/Gdb/CommandPackets/CommandPacket.cpp @@ -8,6 +8,7 @@ #include "src/DebugServer/Gdb/Signal.hpp" +#include "src/DebugServer/Gdb/Exceptions/ClientCommunicationError.hpp" #include "src/Logger/Logger.hpp" namespace DebugServer::Gdb::CommandPackets @@ -20,36 +21,49 @@ namespace DebugServer::Gdb::CommandPackets using ResponsePackets::EmptyResponsePacket; using ResponsePackets::ErrorResponsePacket; - void CommandPacket::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) { - const auto packetString = std::string(this->data.begin(), this->data.end()); + CommandPacket::CommandPacket(const RawPacket& rawPacket) { + if (rawPacket.size() < 5) { + throw Exceptions::ClientCommunicationError{"Invalid raw packet size"}; + } + + this->data.insert(this->data.begin(), rawPacket.begin() + 1, rawPacket.end() - 3); + } + + void CommandPacket::handle( + DebugSession& debugSession, + const TargetDescriptor&, + const Targets::TargetDescriptor&, + TargetControllerService& + ) { + const auto packetString = std::string{this->data.begin(), this->data.end()}; if (packetString.empty()) { Logger::error("Empty GDB RSP packet received."); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); return; } if (packetString[0] == '?') { // Status report - debugSession.connection.writePacket(TargetStopped(Signal::TRAP)); + debugSession.connection.writePacket(TargetStopped{Signal::TRAP}); return; } if (packetString.find("vMustReplyEmpty") == 0) { Logger::info("Handling vMustReplyEmpty"); - debugSession.connection.writePacket(EmptyResponsePacket()); + debugSession.connection.writePacket(EmptyResponsePacket{}); return; } if (packetString.find("qAttached") == 0) { Logger::info("Handling qAttached"); - debugSession.connection.writePacket(ResponsePacket(std::vector({1}))); + debugSession.connection.writePacket(ResponsePacket{std::vector({1})}); return; } Logger::debug("Unknown GDB RSP packet: " + packetString + " - returning empty response"); - // Respond with an empty packet - debugSession.connection.writePacket(EmptyResponsePacket()); + // GDB expects an empty response for all unsupported commands + debugSession.connection.writePacket(EmptyResponsePacket{}); } } diff --git a/src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp b/src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp index 2016b562..0fbcff0a 100644 --- a/src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp +++ b/src/DebugServer/Gdb/CommandPackets/CommandPacket.hpp @@ -1,44 +1,18 @@ #pragma once #include -#include -#include "src/DebugServer/Gdb/Packet.hpp" #include "src/DebugServer/Gdb/DebugSession.hpp" - +#include "src/DebugServer/Gdb/TargetDescriptor.hpp" +#include "src/Targets/TargetDescriptor.hpp" #include "src/Services/TargetControllerService.hpp" namespace DebugServer::Gdb::CommandPackets { - /** - * GDB RSP command packets are sent to the server, from the GDB client. These packets carry instructions that the - * server is expected to carry out. Upon completion, the server is expected to respond to the client with - * a ResponsePacket. - * - * For some command packets, we define a specific data structure by extending this CommandPacket class. These - * classes extend the data structure to include fields for data which may be specific to the command. - * They also implement additional methods that allow us to easily access the additional data. An example - * of this would be the SupportedFeaturesQuery class. It extends the CommandPacket class and provides access - * to additional data fields that are specific to the command (in this case, a set of GDB features reported to be - * supported by the GDB client). - * - * Typically, command packets that require specific data structures are handled in a dedicated handler method - * in the GdbRspDebugServer. This is done by double dispatching the packet object to the appropriate handler. - * See CommandPacket::dispatchToHandler(), GdbRspDebugServer::serve() and the overloads - * for GdbRspDebugServer::handleGdbPacket() for more on this. - * - * Some command packets are so simple they do not require a dedicated data structure. An example of this is - * the halt reason packet, which contains nothing more than an ? character in the packet body. These packets are - * typically handled in the generic GdbRspDebugServer::handleGdbPacket(CommandPacket&) method. - * - * See the Packet class for information on how the raw packets are formatted. - */ - class CommandPacket: public Packet + class CommandPacket { public: - explicit CommandPacket(const RawPacket& rawPacket) - : Packet(rawPacket) - {} + explicit CommandPacket(const RawPacket& rawPacket); /** * Should handle the command for the current active debug session. @@ -50,7 +24,12 @@ namespace DebugServer::Gdb::CommandPackets */ virtual void handle( DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ); + + protected: + std::vector data; }; } diff --git a/src/DebugServer/Gdb/CommandPackets/ContinueExecution.cpp b/src/DebugServer/Gdb/CommandPackets/ContinueExecution.cpp index aa0e445a..be7a4d20 100644 --- a/src/DebugServer/Gdb/CommandPackets/ContinueExecution.cpp +++ b/src/DebugServer/Gdb/CommandPackets/ContinueExecution.cpp @@ -2,6 +2,7 @@ #include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp" +#include "src/Services/StringService.hpp" #include "src/Logger/Logger.hpp" #include "src/Exceptions/Exception.hpp" @@ -17,21 +18,34 @@ namespace DebugServer::Gdb::CommandPackets { if (this->data.size() > 2) { this->fromAddress = static_cast( - std::stoi(std::string(this->data.begin() + 2, this->data.end()), nullptr, 16) + Services::StringService::toUint32(std::string{this->data.begin() + 2, this->data.end()}, 16) ); } } - void ContinueExecution::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) { + void ContinueExecution::handle( + DebugSession& debugSession, + const TargetDescriptor&, + const Targets::TargetDescriptor&, + TargetControllerService& targetControllerService + ) { Logger::info("Handling ContinueExecution packet"); try { - targetControllerService.continueTargetExecution(this->fromAddress, std::nullopt); + { + const auto atomicSession = targetControllerService.makeAtomicSession(); + if (this->fromAddress.has_value()) { + targetControllerService.setProgramCounter(*(this->fromAddress)); + } + + targetControllerService.resumeTargetExecution(); + } + debugSession.waitingForBreak = true; } catch (const Exception& exception) { Logger::error("Failed to continue execution on target - " + exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } } diff --git a/src/DebugServer/Gdb/CommandPackets/ContinueExecution.hpp b/src/DebugServer/Gdb/CommandPackets/ContinueExecution.hpp index f456edd4..2a5c0323 100644 --- a/src/DebugServer/Gdb/CommandPackets/ContinueExecution.hpp +++ b/src/DebugServer/Gdb/CommandPackets/ContinueExecution.hpp @@ -30,6 +30,8 @@ namespace DebugServer::Gdb::CommandPackets void handle( DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/CommandPackets/Detach.cpp b/src/DebugServer/Gdb/CommandPackets/Detach.cpp index 13627ec1..01d798d7 100644 --- a/src/DebugServer/Gdb/CommandPackets/Detach.cpp +++ b/src/DebugServer/Gdb/CommandPackets/Detach.cpp @@ -20,7 +20,12 @@ namespace DebugServer::Gdb::CommandPackets : CommandPacket(rawPacket) {} - void Detach::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) { + void Detach::handle( + DebugSession& debugSession, + const TargetDescriptor&, + const Targets::TargetDescriptor&, + TargetControllerService& targetControllerService + ) { Logger::info("Handling Detach packet"); try { @@ -28,11 +33,11 @@ namespace DebugServer::Gdb::CommandPackets targetControllerService.shutdown(); } - debugSession.connection.writePacket(OkResponsePacket()); + debugSession.connection.writePacket(OkResponsePacket{}); } catch (const Exception& exception) { Logger::error("Failed to shut down TargetController - " + exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } } diff --git a/src/DebugServer/Gdb/CommandPackets/Detach.hpp b/src/DebugServer/Gdb/CommandPackets/Detach.hpp index cc155fd8..39da1199 100644 --- a/src/DebugServer/Gdb/CommandPackets/Detach.hpp +++ b/src/DebugServer/Gdb/CommandPackets/Detach.hpp @@ -11,6 +11,8 @@ namespace DebugServer::Gdb::CommandPackets void handle( DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/CommandPackets/EepromFill.hpp b/src/DebugServer/Gdb/CommandPackets/EepromFill.hpp deleted file mode 100644 index 6e6fa4cf..00000000 --- a/src/DebugServer/Gdb/CommandPackets/EepromFill.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -#include "Monitor.hpp" - -#include "src/Targets/TargetMemory.hpp" - -namespace DebugServer::Gdb::CommandPackets -{ - /** - * The EepromFill class implements a structure for the "monitor eeprom fill" GDB command. - * - * This command fills the target's EEPROM with the given value. - */ - class EepromFill: public Monitor - { - public: - explicit EepromFill(Monitor&& monitorPacket); - - void handle( - DebugSession& debugSession, - Services::TargetControllerService& targetControllerService - ) override; - - private: - Targets::TargetMemoryBuffer fillValue; - }; -} diff --git a/src/DebugServer/Gdb/CommandPackets/GenerateSvd.cpp b/src/DebugServer/Gdb/CommandPackets/GenerateSvd.cpp deleted file mode 100644 index a8dda194..00000000 --- a/src/DebugServer/Gdb/CommandPackets/GenerateSvd.cpp +++ /dev/null @@ -1,225 +0,0 @@ -#include "GenerateSvd.hpp" - -#include -#include - -#include "src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp" -#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp" - -#include "src/Targets/TargetMemory.hpp" -#include "src/Application.hpp" - -#include "src/Services/PathService.hpp" -#include "src/Services/StringService.hpp" -#include "src/Logger/Logger.hpp" - -#include "src/Exceptions/Exception.hpp" - -namespace DebugServer::Gdb::CommandPackets -{ - using Services::TargetControllerService; - - using ResponsePackets::ResponsePacket; - using ResponsePackets::ErrorResponsePacket; - - using ::Exceptions::Exception; - - GenerateSvd::GenerateSvd(Monitor&& monitorPacket) - : Monitor(std::move(monitorPacket)) - , sendOutput(this->commandOptions.contains("out")) - {} - - void GenerateSvd::handle(DebugSession& debugSession, TargetControllerService&) { - Logger::info("Handling GenerateSvd packet"); - - try { - Logger::info("Generating SVD XML for current target"); - - const auto& targetDescriptor = debugSession.gdbTargetDescriptor.targetDescriptor; - - const auto svdXml = this->generateSvd( - targetDescriptor, - debugSession.gdbTargetDescriptor.getMemoryOffset(Targets::TargetMemoryType::RAM) - ); - - if (this->sendOutput) { - debugSession.connection.writePacket( - ResponsePacket(Services::StringService::toHex(svdXml.toString().toStdString())) - ); - return; - } - - const auto svdOutputFilePath = Services::PathService::projectDirPath() + "/" + targetDescriptor.name + ".svd"; - auto outputFile = QFile(QString::fromStdString(svdOutputFilePath)); - - if (!outputFile.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) { - throw Exception( - "Failed to open/create SVD output file (" + svdOutputFilePath + "). Check file permissions." - ); - } - - outputFile.write(svdXml.toByteArray()); - outputFile.close(); - - debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex( - "SVD output saved to " + svdOutputFilePath + "\n" - ))); - - Logger::info("SVD output saved to " + svdOutputFilePath); - - } catch (const Exception& exception) { - Logger::error(exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); - } - } - - QDomDocument GenerateSvd::generateSvd( - const Targets::TargetDescriptor& targetDescriptor, - std::uint32_t baseAddressOffset - ) { - auto document = QDomDocument(); - - const auto createElement = [&document] (const QString& tagName, const QString& value) { - auto element = document.createElement(tagName); - auto textNode = document.createTextNode(value); - element.appendChild(textNode); - - return element; - }; - - document.appendChild(document.createComment( - " This SVD was generated by Bloom (https://bloom.oscillate.io/). " - "Please report any issues via https://bloom.oscillate.io/report-issue " - )); - - if (baseAddressOffset != 0) { - document.appendChild(document.createComment( - " Base addresses in this SVD have been offset by 0x" + QString::number(baseAddressOffset, 16) - + ". This offset is required for access via avr-gdb. " - )); - } - - auto deviceElement = document.createElement("device"); - - deviceElement.setAttribute("schemaVersion", "1.3"); - deviceElement.setAttribute("xmlns:xs", "http://www.w3.org/2001/XMLSchema-instance"); - deviceElement.setAttribute( - "xs:noNamespaceSchemaLocation", - QString::fromStdString(Services::PathService::homeDomainName() + "/assets/svd-schema.xsd") - ); - - deviceElement.appendChild(createElement("vendor", QString::fromStdString(targetDescriptor.vendorName))); - deviceElement.appendChild(createElement("name", QString::fromStdString(targetDescriptor.name))); - - deviceElement.appendChild(document.createComment( - " The version number below is that of the Bloom binary which generated this SVD. " - )); - deviceElement.appendChild(createElement("version", QString::fromStdString(Application::VERSION.toString()))); - - deviceElement.appendChild( - createElement( - "description", - QString::fromStdString(targetDescriptor.name) + " from " - + QString::fromStdString(targetDescriptor.vendorName) - ) - ); - - /* - * TODO: These values should be part of the TargetDescriptor, but given that Bloom only supports 8-bit AVRs, - * it really doesn't matter ATM. Will fix it later (lol no I won't). - */ - deviceElement.appendChild(createElement("addressUnitBits", "8")); - deviceElement.appendChild(createElement("width", "8")); - deviceElement.appendChild(createElement("size", "8")); - - deviceElement.appendChild(createElement("access", "read-only")); - - struct Peripheral { - QString name; - std::uint32_t baseAddress; - - Targets::TargetRegisterDescriptors registerDescriptors; - }; - - auto peripheralsByName = std::map(); - - for (const auto& [descriptorId, registerDescriptor] : targetDescriptor.registerDescriptorsById) { - if ( - !registerDescriptor.startAddress.has_value() - || !registerDescriptor.name.has_value() - || registerDescriptor.name->empty() - || !registerDescriptor.groupName.has_value() - || ( - registerDescriptor.type != Targets::TargetRegisterType::OTHER - && registerDescriptor.type != Targets::TargetRegisterType::PORT_REGISTER - ) - ) { - continue; - } - - auto peripheralIt = peripheralsByName.find(*registerDescriptor.groupName); - - if (peripheralIt == peripheralsByName.end()) { - auto peripheral = Peripheral{ - .name = QString::fromStdString( - *registerDescriptor.groupName - ).replace(QChar(' '), QChar('_')).toUpper(), - .baseAddress = baseAddressOffset - }; - - peripheralIt = peripheralsByName.insert(std::pair(*registerDescriptor.groupName, peripheral)).first; - } - - peripheralIt->second.registerDescriptors.insert(registerDescriptor); - } - - auto peripheralsElement = document.createElement("peripherals"); - - for (const auto& [peripheralName, peripheral] : peripheralsByName) { - auto peripheralElement = document.createElement("peripheral"); - - peripheralElement.appendChild(createElement("name", peripheral.name)); - peripheralElement.appendChild(createElement("baseAddress", "0x" + QString::number(peripheral.baseAddress, 16))); - - auto registersElement = document.createElement("registers"); - - for (const auto& registerDescriptor : peripheral.registerDescriptors) { - auto registerElement = document.createElement("register"); - - registerElement.appendChild( - createElement( - "name", - QString::fromStdString(*registerDescriptor.name).replace(QChar(' '), QChar('_')).toUpper() - ) - ); - - if (registerDescriptor.description.has_value()) { - registerElement.appendChild( - createElement("description", QString::fromStdString(*registerDescriptor.description)) - ); - } - - registerElement.appendChild( - createElement("addressOffset", "0x" + QString::number(*registerDescriptor.startAddress, 16)) - ); - - registerElement.appendChild( - createElement("size", QString::number(registerDescriptor.size * 8)) - ); - - registerElement.appendChild( - createElement("access", registerDescriptor.access.writable ? "read-write" : "read-only") - ); - - registersElement.appendChild(registerElement); - } - - peripheralElement.appendChild(registersElement); - peripheralsElement.appendChild(peripheralElement); - } - - deviceElement.appendChild(peripheralsElement); - document.appendChild(deviceElement); - return document; - } -} diff --git a/src/DebugServer/Gdb/CommandPackets/GenerateSvd.hpp b/src/DebugServer/Gdb/CommandPackets/GenerateSvd.hpp deleted file mode 100644 index 41856f9a..00000000 --- a/src/DebugServer/Gdb/CommandPackets/GenerateSvd.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include - -#include "Monitor.hpp" - -#include "src/Targets/TargetDescriptor.hpp" - -namespace DebugServer::Gdb::CommandPackets -{ - /** - * The GenerateSvd class implements a structure for the "monitor svd" GDB command. - * - * This command generates XML conforming to the CMSIS-SVD schema, for the connected target. Will output the XML to - * a file or send it to GDB. - */ - class GenerateSvd: public Monitor - { - public: - explicit GenerateSvd(Monitor&& monitorPacket); - - void handle( - DebugSession& debugSession, - Services::TargetControllerService& targetControllerService - ) override; - - private: - bool sendOutput = false; - - QDomDocument generateSvd( - const Targets::TargetDescriptor& targetDescriptor, - std::uint32_t baseAddressOffset = 0 - ); - }; -} diff --git a/src/DebugServer/Gdb/CommandPackets/HelpMonitorInfo.cpp b/src/DebugServer/Gdb/CommandPackets/HelpMonitorInfo.cpp index 6e098ca9..24262c39 100644 --- a/src/DebugServer/Gdb/CommandPackets/HelpMonitorInfo.cpp +++ b/src/DebugServer/Gdb/CommandPackets/HelpMonitorInfo.cpp @@ -25,7 +25,12 @@ namespace DebugServer::Gdb::CommandPackets : Monitor(std::move(monitorPacket)) {} - void HelpMonitorInfo::handle(DebugSession& debugSession, TargetControllerService&) { + void HelpMonitorInfo::handle( + DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor&, + TargetControllerService& + ) { Logger::info("Handling HelpMonitorInfo packet"); try { @@ -33,26 +38,24 @@ namespace DebugServer::Gdb::CommandPackets * The file GdbHelpMonitorInfo.txt is included in the binary image as a resource. * See src/DebugServer/CMakeLists.txt for more. */ - auto helpFile = QFile( + auto helpFile = QFile{ QString::fromStdString(":/compiled/src/DebugServer/Gdb/Resources/GdbHelpMonitorInfo.txt") - ); + }; if (!helpFile.open(QIODevice::ReadOnly)) { - throw Exception( + throw Exception{ "Failed to open GDB monitor info help file - please report this issue at " + Services::PathService::homeDomainName() + "/report-issue" - ); + }; } - debugSession.connection.writePacket( - ResponsePacket(Services::StringService::toHex( - "\n" + QTextStream(&helpFile).readAll().toUtf8().toStdString() + "\n" - )) - ); + debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex( + "\n" + QTextStream{&helpFile}.readAll().toUtf8().toStdString() + "\n" + )}); } catch (const Exception& exception) { Logger::error(exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } } diff --git a/src/DebugServer/Gdb/CommandPackets/HelpMonitorInfo.hpp b/src/DebugServer/Gdb/CommandPackets/HelpMonitorInfo.hpp index c1cca27f..8289a367 100644 --- a/src/DebugServer/Gdb/CommandPackets/HelpMonitorInfo.hpp +++ b/src/DebugServer/Gdb/CommandPackets/HelpMonitorInfo.hpp @@ -18,6 +18,8 @@ namespace DebugServer::Gdb::CommandPackets void handle( DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/CommandPackets/InterruptExecution.cpp b/src/DebugServer/Gdb/CommandPackets/InterruptExecution.cpp index 2791ca76..aa1aa67c 100644 --- a/src/DebugServer/Gdb/CommandPackets/InterruptExecution.cpp +++ b/src/DebugServer/Gdb/CommandPackets/InterruptExecution.cpp @@ -16,10 +16,15 @@ namespace DebugServer::Gdb::CommandPackets using ::Exceptions::Exception; - void InterruptExecution::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) { + void InterruptExecution::handle( + DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor&, + TargetControllerService& targetControllerService + ) { Logger::info("Handling InterruptExecution packet"); - if (targetControllerService.getTargetState() == Targets::TargetState::STOPPED) { + if (targetControllerService.getTargetState().executionState == Targets::TargetExecutionState::STOPPED) { debugSession.pendingInterrupt = true; return; } @@ -32,7 +37,7 @@ namespace DebugServer::Gdb::CommandPackets } debugSession.waitingForBreak = false; - debugSession.connection.writePacket(TargetStopped(Signal::INTERRUPTED)); + debugSession.connection.writePacket(TargetStopped{Signal::INTERRUPTED}); } catch (const Exception& exception) { Logger::error("Failed to interrupt execution - " + exception.getMessage()); diff --git a/src/DebugServer/Gdb/CommandPackets/InterruptExecution.hpp b/src/DebugServer/Gdb/CommandPackets/InterruptExecution.hpp index f4b7a9e4..7d90f9d2 100644 --- a/src/DebugServer/Gdb/CommandPackets/InterruptExecution.hpp +++ b/src/DebugServer/Gdb/CommandPackets/InterruptExecution.hpp @@ -19,6 +19,8 @@ namespace DebugServer::Gdb::CommandPackets void handle( DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/CommandPackets/Monitor.cpp b/src/DebugServer/Gdb/CommandPackets/Monitor.cpp index ce8834f4..55bf9189 100644 --- a/src/DebugServer/Gdb/CommandPackets/Monitor.cpp +++ b/src/DebugServer/Gdb/CommandPackets/Monitor.cpp @@ -2,6 +2,7 @@ #include "src/DebugServer/Gdb/ResponsePackets/EmptyResponsePacket.hpp" +#include "src/Services/StringService.hpp" #include "src/Logger/Logger.hpp" namespace DebugServer::Gdb::CommandPackets @@ -17,23 +18,28 @@ namespace DebugServer::Gdb::CommandPackets return; } - const auto decodedCommand = Packet::hexToData( - std::string(this->data.begin() + 6, this->data.end()) + const auto decodedCommand = Services::StringService::dataFromHex( + std::string{this->data.begin() + 6, this->data.end()} ); - this->command = std::string(decodedCommand.begin(), decodedCommand.end()); + this->command = std::string{decodedCommand.begin(), decodedCommand.end()}; this->command.erase(this->command.find_last_not_of(" ") + 1); this->commandOptions = this->extractCommandOptions(this->command); } - void Monitor::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) { + void Monitor::handle( + DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor&, + TargetControllerService& targetControllerService + ) { Logger::error("Unknown custom GDB command (\"" + this->command + "\") received."); - debugSession.connection.writePacket(EmptyResponsePacket()); + debugSession.connection.writePacket(EmptyResponsePacket{}); } std::map> Monitor::extractCommandOptions(const std::string& command) { - auto output = std::map>(); + auto output = std::map>{}; for (std::string::size_type cmdIndex = 1; cmdIndex < command.size(); ++cmdIndex) { const auto cmdChar = command.at(cmdIndex); @@ -43,17 +49,17 @@ namespace DebugServer::Gdb::CommandPackets continue; } - auto option = std::string(); - auto optionValue = std::optional(); + auto option = std::string{}; + auto optionValue = std::optional{}; bool quoted = false; - auto optIndex = std::string::size_type(0); + auto optIndex = std::string::size_type{0}; for (optIndex = cmdIndex + 1; optIndex < command.size(); ++optIndex) { const auto optChar = command.at(optIndex); if (!option.empty() && ((!quoted && optChar == ' ') || (quoted && optChar == '"'))) { - output.insert(std::pair(option, optionValue)); + output.emplace(option, optionValue); option.clear(); optionValue.reset(); @@ -74,7 +80,7 @@ namespace DebugServer::Gdb::CommandPackets } if (optChar == '=') { - optionValue = std::string(); + optionValue = std::string{}; continue; } @@ -83,7 +89,7 @@ namespace DebugServer::Gdb::CommandPackets } if (!option.empty()) { - output.insert(std::pair(option, optionValue)); + output.emplace(option, optionValue); cmdIndex = optIndex; } } diff --git a/src/DebugServer/Gdb/CommandPackets/Monitor.hpp b/src/DebugServer/Gdb/CommandPackets/Monitor.hpp index ed17ef36..e24a108f 100644 --- a/src/DebugServer/Gdb/CommandPackets/Monitor.hpp +++ b/src/DebugServer/Gdb/CommandPackets/Monitor.hpp @@ -33,6 +33,8 @@ namespace DebugServer::Gdb::CommandPackets void handle( DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; diff --git a/src/DebugServer/Gdb/CommandPackets/RemoveBreakpoint.cpp b/src/DebugServer/Gdb/CommandPackets/RemoveBreakpoint.cpp index c8bf8dcc..a0c136a9 100644 --- a/src/DebugServer/Gdb/CommandPackets/RemoveBreakpoint.cpp +++ b/src/DebugServer/Gdb/CommandPackets/RemoveBreakpoint.cpp @@ -25,12 +25,13 @@ namespace DebugServer::Gdb::CommandPackets : CommandPacket(rawPacket) { if (this->data.size() < 6) { - throw Exception("Unexpected RemoveBreakpoint packet size"); + throw Exception{"Unexpected RemoveBreakpoint packet size"}; } // z0 = SW breakpoint, z1 = HW breakpoint - this->type = (this->data[1] == '0') ? BreakpointType::SOFTWARE_BREAKPOINT : (this->data[1] == '1') ? - BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN; + this->type = (this->data[1] == '0') + ? BreakpointType::SOFTWARE_BREAKPOINT + : (this->data[1] == '1') ? BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN; const auto packetData = QString::fromLocal8Bit( reinterpret_cast(this->data.data() + 2), @@ -39,29 +40,34 @@ namespace DebugServer::Gdb::CommandPackets auto packetSegments = packetData.split(","); if (packetSegments.size() < 3) { - throw Exception("Unexpected number of packet segments in RemoveBreakpoint packet"); + throw Exception{"Unexpected number of packet segments in RemoveBreakpoint packet"}; } bool conversionStatus = true; this->address = packetSegments.at(1).toUInt(&conversionStatus, 16); if (!conversionStatus) { - throw Exception("Failed to convert address hex value from RemoveBreakpoint packet."); + throw Exception{"Failed to convert address hex value from RemoveBreakpoint packet."}; } } - void RemoveBreakpoint::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) { + void RemoveBreakpoint::handle( + DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor&, + TargetControllerService& targetControllerService + ) { Logger::info("Handling RemoveBreakpoint packet"); try { Logger::debug("Removing breakpoint at address " + std::to_string(this->address)); debugSession.removeExternalBreakpoint(this->address, targetControllerService); - debugSession.connection.writePacket(OkResponsePacket()); + debugSession.connection.writePacket(OkResponsePacket{}); } catch (const Exception& exception) { Logger::error("Failed to remove breakpoint on target - " + exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } } diff --git a/src/DebugServer/Gdb/CommandPackets/RemoveBreakpoint.hpp b/src/DebugServer/Gdb/CommandPackets/RemoveBreakpoint.hpp index 1473c765..468424cf 100644 --- a/src/DebugServer/Gdb/CommandPackets/RemoveBreakpoint.hpp +++ b/src/DebugServer/Gdb/CommandPackets/RemoveBreakpoint.hpp @@ -32,6 +32,8 @@ namespace DebugServer::Gdb::CommandPackets void handle( DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/CommandPackets/ResetTarget.cpp b/src/DebugServer/Gdb/CommandPackets/ResetTarget.cpp index 39245980..02fbd313 100644 --- a/src/DebugServer/Gdb/CommandPackets/ResetTarget.cpp +++ b/src/DebugServer/Gdb/CommandPackets/ResetTarget.cpp @@ -21,7 +21,12 @@ namespace DebugServer::Gdb::CommandPackets : Monitor(std::move(monitorPacket)) {} - void ResetTarget::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) { + void ResetTarget::handle( + DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor&, + TargetControllerService& targetControllerService + ) { Logger::info("Handling ResetTarget packet"); try { @@ -29,15 +34,15 @@ namespace DebugServer::Gdb::CommandPackets targetControllerService.resetTarget(); Logger::info("Target reset complete"); - debugSession.connection.writePacket(ResponsePacket(Services::StringService::toHex( + debugSession.connection.writePacket(ResponsePacket{Services::StringService::toHex( "Target reset complete\n" "Current PC: 0x" + Services::StringService::toHex(targetControllerService.getProgramCounter()) + "\n" "Use the 'continue' command to begin execution\n" - ))); + )}); } catch (const Exception& exception) { Logger::error("Failed to reset target - " + exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } } diff --git a/src/DebugServer/Gdb/CommandPackets/ResetTarget.hpp b/src/DebugServer/Gdb/CommandPackets/ResetTarget.hpp index ead9d533..980c883b 100644 --- a/src/DebugServer/Gdb/CommandPackets/ResetTarget.hpp +++ b/src/DebugServer/Gdb/CommandPackets/ResetTarget.hpp @@ -19,6 +19,8 @@ namespace DebugServer::Gdb::CommandPackets void handle( DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/CommandPackets/SetBreakpoint.cpp b/src/DebugServer/Gdb/CommandPackets/SetBreakpoint.cpp index b0adff75..f53bdddd 100644 --- a/src/DebugServer/Gdb/CommandPackets/SetBreakpoint.cpp +++ b/src/DebugServer/Gdb/CommandPackets/SetBreakpoint.cpp @@ -27,12 +27,13 @@ namespace DebugServer::Gdb::CommandPackets : CommandPacket(rawPacket) { if (this->data.size() < 6) { - throw Exception("Unexpected SetBreakpoint packet size"); + throw Exception{"Unexpected SetBreakpoint packet size"}; } // Z0 = SW breakpoint, Z1 = HW breakpoint - this->type = (this->data[1] == '0') ? BreakpointType::SOFTWARE_BREAKPOINT : (this->data[1] == '1') ? - BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN; + this->type = (this->data[1] == '0') + ? BreakpointType::SOFTWARE_BREAKPOINT + : (this->data[1] == '1') ? BreakpointType::HARDWARE_BREAKPOINT : BreakpointType::UNKNOWN; auto packetData = QString::fromLocal8Bit( reinterpret_cast(this->data.data() + 2), @@ -41,36 +42,41 @@ namespace DebugServer::Gdb::CommandPackets auto packetSegments = packetData.split(","); if (packetSegments.size() < 3) { - throw Exception("Unexpected number of packet segments in SetBreakpoint packet"); + throw Exception{"Unexpected number of packet segments in SetBreakpoint packet"}; } bool conversionStatus = true; this->address = packetSegments.at(1).toUInt(&conversionStatus, 16); if (!conversionStatus) { - throw Exception("Failed to convert address hex value from SetBreakpoint packet."); + throw Exception{"Failed to convert address hex value from SetBreakpoint packet."}; } } - void SetBreakpoint::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) { + void SetBreakpoint::handle( + DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor&, + TargetControllerService& targetControllerService + ) { Logger::info("Handling SetBreakpoint packet"); try { if (this->type == BreakpointType::UNKNOWN) { Logger::debug( "Rejecting breakpoint at address " + std::to_string(this->address) - + " - unsupported breakpoint type" + + " - unsupported breakpoint type" ); - debugSession.connection.writePacket(EmptyResponsePacket()); + debugSession.connection.writePacket(EmptyResponsePacket{}); return; } debugSession.setExternalBreakpoint(this->address, targetControllerService); - debugSession.connection.writePacket(OkResponsePacket()); + debugSession.connection.writePacket(OkResponsePacket{}); } catch (const Exception& exception) { Logger::error("Failed to set breakpoint on target - " + exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } } diff --git a/src/DebugServer/Gdb/CommandPackets/SetBreakpoint.hpp b/src/DebugServer/Gdb/CommandPackets/SetBreakpoint.hpp index 00479110..a14437bc 100644 --- a/src/DebugServer/Gdb/CommandPackets/SetBreakpoint.hpp +++ b/src/DebugServer/Gdb/CommandPackets/SetBreakpoint.hpp @@ -32,6 +32,8 @@ namespace DebugServer::Gdb::CommandPackets void handle( DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/CommandPackets/StepExecution.cpp b/src/DebugServer/Gdb/CommandPackets/StepExecution.cpp index f9987fd5..1d86b534 100644 --- a/src/DebugServer/Gdb/CommandPackets/StepExecution.cpp +++ b/src/DebugServer/Gdb/CommandPackets/StepExecution.cpp @@ -2,6 +2,7 @@ #include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp" +#include "src/Services/StringService.hpp" #include "src/Logger/Logger.hpp" #include "src/Exceptions/Exception.hpp" @@ -18,21 +19,31 @@ namespace DebugServer::Gdb::CommandPackets { if (this->data.size() > 2) { this->fromAddress = static_cast( - std::stoi(std::string(this->data.begin() + 2, this->data.end()), nullptr, 16) + Services::StringService::toUint32(std::string{this->data.begin() + 2, this->data.end()}, 16) ); } } - void StepExecution::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) { + void StepExecution::handle( + DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor&, + TargetControllerService& targetControllerService + ) { Logger::info("Handling StepExecution packet"); try { - targetControllerService.stepTargetExecution(this->fromAddress); + const auto atomicSession = targetControllerService.makeAtomicSession(); + if (this->fromAddress.has_value()) { + targetControllerService.setProgramCounter(*(this->fromAddress)); + } + + targetControllerService.stepTargetExecution(); debugSession.waitingForBreak = true; } catch (const Exception& exception) { Logger::error("Failed to step execution on target - " + exception.getMessage()); - debugSession.connection.writePacket(ErrorResponsePacket()); + debugSession.connection.writePacket(ErrorResponsePacket{}); } } } diff --git a/src/DebugServer/Gdb/CommandPackets/StepExecution.hpp b/src/DebugServer/Gdb/CommandPackets/StepExecution.hpp index eac964db..e44c1fae 100644 --- a/src/DebugServer/Gdb/CommandPackets/StepExecution.hpp +++ b/src/DebugServer/Gdb/CommandPackets/StepExecution.hpp @@ -25,6 +25,8 @@ namespace DebugServer::Gdb::CommandPackets void handle( DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; }; diff --git a/src/DebugServer/Gdb/CommandPackets/SupportedFeaturesQuery.cpp b/src/DebugServer/Gdb/CommandPackets/SupportedFeaturesQuery.cpp index 656ac53a..302fc46a 100644 --- a/src/DebugServer/Gdb/CommandPackets/SupportedFeaturesQuery.cpp +++ b/src/DebugServer/Gdb/CommandPackets/SupportedFeaturesQuery.cpp @@ -51,17 +51,23 @@ namespace DebugServer::Gdb::CommandPackets } } - void SupportedFeaturesQuery::handle(DebugSession& debugSession, TargetControllerService& targetControllerService) { + void SupportedFeaturesQuery::handle( + DebugSession& debugSession, + const TargetDescriptor&, + const Targets::TargetDescriptor&, + TargetControllerService& targetControllerService + ) { Logger::info("Handling QuerySupport packet"); - if (!this->isFeatureSupported(Feature::HARDWARE_BREAKPOINTS) + if ( + !this->isFeatureSupported(Feature::HARDWARE_BREAKPOINTS) && !this->isFeatureSupported(Feature::SOFTWARE_BREAKPOINTS) ) { // All GDB clients are expected to support breakpoints! - throw ClientNotSupported("GDB client does not support HW or SW breakpoints"); + throw ClientNotSupported{"GDB client does not support HW or SW breakpoints"}; } // Respond with a SupportedFeaturesResponse packet, listing all supported GDB features by Bloom - debugSession.connection.writePacket(SupportedFeaturesResponse(debugSession.supportedFeatures)); + debugSession.connection.writePacket(SupportedFeaturesResponse{debugSession.supportedFeatures}); } } diff --git a/src/DebugServer/Gdb/CommandPackets/SupportedFeaturesQuery.hpp b/src/DebugServer/Gdb/CommandPackets/SupportedFeaturesQuery.hpp index 78ee3368..bad14840 100644 --- a/src/DebugServer/Gdb/CommandPackets/SupportedFeaturesQuery.hpp +++ b/src/DebugServer/Gdb/CommandPackets/SupportedFeaturesQuery.hpp @@ -34,6 +34,8 @@ namespace DebugServer::Gdb::CommandPackets void handle( DebugSession& debugSession, + const TargetDescriptor& gdbTargetDescriptor, + const Targets::TargetDescriptor& targetDescriptor, Services::TargetControllerService& targetControllerService ) override; diff --git a/src/DebugServer/Gdb/Connection.cpp b/src/DebugServer/Gdb/Connection.cpp index 0acd5cad..331db8e8 100644 --- a/src/DebugServer/Gdb/Connection.cpp +++ b/src/DebugServer/Gdb/Connection.cpp @@ -45,23 +45,23 @@ namespace DebugServer::Gdb } std::string Connection::getIpAddress() const { - std::array ipAddress = {}; + auto ipAddress = std::array{}; if (::inet_ntop(AF_INET, &(socketAddress.sin_addr), ipAddress.data(), INET_ADDRSTRLEN) == nullptr) { - throw Exception("Failed to convert client IP address to text form."); + throw Exception{"Failed to convert client IP address to text form."}; } - return std::string(ipAddress.data()); + return {ipAddress.data()}; } std::vector Connection::readRawPackets() { - std::vector output; + auto output = std::vector{}; do { const auto bytes = this->read(); - std::size_t bufferSize = bytes.size(); - for (std::size_t byteIndex = 0; byteIndex < bufferSize; byteIndex++) { + auto bufferSize = bytes.size(); + for (auto byteIndex = std::size_t{0}; byteIndex < bufferSize; byteIndex++) { auto byte = bytes[byteIndex]; if (byte == 0x03) { @@ -131,7 +131,7 @@ namespace DebugServer::Gdb Logger::debug( "Read GDB packet: " + Services::StringService::replaceUnprintable( - std::string(rawPacket.begin(), rawPacket.end()) + std::string{rawPacket.begin(), rawPacket.end()} ) ); @@ -153,13 +153,13 @@ namespace DebugServer::Gdb int attempts = 0; const auto rawPacket = packet.toRawPacket(); - Logger::debug("Writing GDB packet: " + std::string(rawPacket.begin(), rawPacket.end())); + Logger::debug("Writing GDB packet: " + std::string{rawPacket.begin(), rawPacket.end()}); do { if (attempts > 10) { - throw ClientCommunicationError( + throw ClientCommunicationError{ "Failed to write GDB response packet - client failed to acknowledge receipt - retry limit reached" - ); + }; } this->write(rawPacket); @@ -177,7 +177,7 @@ namespace DebugServer::Gdb ); if (socketFileDescriptor < 0) { - throw Exception("Failed to accept GDB Remote Serial Protocol connection"); + throw Exception{"Failed to accept GDB Remote Serial Protocol connection"}; } this->socketFileDescriptor = socketFileDescriptor; @@ -219,7 +219,7 @@ namespace DebugServer::Gdb if (eventFileDescriptor.value() == this->interruptEventNotifier.getFileDescriptor()) { // Interrupted this->interruptEventNotifier.clear(); - throw DebugServerInterrupted(); + throw DebugServerInterrupted{}; } const auto bytesToRead = bytes.value_or(Connection::ABSOLUTE_MAXIMUM_PACKET_READ_SIZE); @@ -232,14 +232,14 @@ namespace DebugServer::Gdb ); if (bytesRead < 0) { - throw ClientCommunicationError( + throw ClientCommunicationError{ "Failed to read data from GDB client - error code: " + std::to_string(errno) - ); + }; } if (bytesRead == 0) { // Client has disconnected - throw ClientDisconnected(); + throw ClientDisconnected{}; } if (bytesRead != output.size()) { @@ -250,7 +250,7 @@ namespace DebugServer::Gdb } std::optional Connection::readSingleByte(bool interruptible) { - auto bytes = this->read(1, interruptible, std::chrono::milliseconds(300)); + auto bytes = this->read(1, interruptible, std::chrono::milliseconds{300}); if (!bytes.empty()) { return bytes.front(); @@ -263,19 +263,18 @@ namespace DebugServer::Gdb if (::write(this->socketFileDescriptor.value(), buffer.data(), buffer.size()) == -1) { if (errno == EPIPE || errno == ECONNRESET) { // Connection was closed - throw ClientDisconnected(); + throw ClientDisconnected{}; } - throw ClientCommunicationError( + throw ClientCommunicationError{ "Failed to write " + std::to_string(buffer.size()) + " bytes to GDP client socket - error no: " + std::to_string(errno) - ); + }; } } void Connection::disableReadInterrupts() { this->epollInstance.removeEntry(this->interruptEventNotifier.getFileDescriptor()); - this->readInterruptEnabled = false; } diff --git a/src/DebugServer/Gdb/Connection.hpp b/src/DebugServer/Gdb/Connection.hpp index 9c642932..a384a7ac 100644 --- a/src/DebugServer/Gdb/Connection.hpp +++ b/src/DebugServer/Gdb/Connection.hpp @@ -31,7 +31,7 @@ namespace DebugServer::Gdb */ static constexpr auto ABSOLUTE_MAXIMUM_PACKET_READ_SIZE = 2097000; // 2MiB - explicit Connection(int serverSocketFileDescriptor, EventFdNotifier& interruptEventNotifier); + Connection(int serverSocketFileDescriptor, EventFdNotifier& interruptEventNotifier); Connection() = delete; Connection(const Connection&) = delete; diff --git a/src/DebugServer/Gdb/DebugSession.cpp b/src/DebugServer/Gdb/DebugSession.cpp index cd0b3e0b..e9e88cea 100644 --- a/src/DebugServer/Gdb/DebugSession.cpp +++ b/src/DebugServer/Gdb/DebugSession.cpp @@ -7,17 +7,16 @@ namespace DebugServer::Gdb DebugSession::DebugSession( Connection&& connection, const std::set>>& supportedFeatures, - const TargetDescriptor& targetDescriptor, const GdbDebugServerConfig& serverConfig ) : connection(std::move(connection)) , supportedFeatures(supportedFeatures) - , gdbTargetDescriptor(targetDescriptor) , serverConfig(serverConfig) { - this->supportedFeatures.insert({ - Feature::PACKET_SIZE, std::to_string(Connection::ABSOLUTE_MAXIMUM_PACKET_READ_SIZE) - }); + this->supportedFeatures.emplace( + Feature::PACKET_SIZE, + std::to_string(Connection::ABSOLUTE_MAXIMUM_PACKET_READ_SIZE) + ); EventManager::triggerEvent(std::make_shared()); } @@ -35,18 +34,15 @@ namespace DebugServer::Gdb } const auto externalBreakpointIt = this->externalBreakpointsByAddress.find(address); - if (externalBreakpointIt != this->externalBreakpointsByAddress.end()) { // We already have an external breakpoint at this address - this->internalBreakpointsByAddress.insert(std::pair(address, externalBreakpointIt->second)); + this->internalBreakpointsByAddress.emplace(address, externalBreakpointIt->second); return; } - this->internalBreakpointsByAddress.insert( - std::pair( - address, - targetControllerService.setBreakpoint(address, Targets::TargetBreakpoint::Type::HARDWARE) - ) + this->internalBreakpointsByAddress.emplace( + address, + targetControllerService.setBreakpoint(address, Targets::TargetBreakpoint::Type::HARDWARE) ); } @@ -78,15 +74,13 @@ namespace DebugServer::Gdb if (internalBreakpointIt != this->internalBreakpointsByAddress.end()) { // We already have an internal breakpoint at this address - this->externalBreakpointsByAddress.insert(std::pair(address, internalBreakpointIt->second)); + this->externalBreakpointsByAddress.emplace(address, internalBreakpointIt->second); return; } - this->externalBreakpointsByAddress.insert( - std::pair( - address, - targetControllerService.setBreakpoint(address, Targets::TargetBreakpoint::Type::HARDWARE) - ) + this->externalBreakpointsByAddress.emplace( + address, + targetControllerService.setBreakpoint(address, Targets::TargetBreakpoint::Type::HARDWARE) ); } diff --git a/src/DebugServer/Gdb/DebugSession.hpp b/src/DebugServer/Gdb/DebugSession.hpp index dcbf5231..321461e6 100644 --- a/src/DebugServer/Gdb/DebugSession.hpp +++ b/src/DebugServer/Gdb/DebugSession.hpp @@ -32,11 +32,6 @@ namespace DebugServer::Gdb */ std::set>> supportedFeatures; - /** - * The GDB target descriptor of the connected target. - */ - const TargetDescriptor& gdbTargetDescriptor; - /** * The current server configuration. */ @@ -92,7 +87,6 @@ namespace DebugServer::Gdb DebugSession( Connection&& connection, const std::set>>& supportedFeatures, - const TargetDescriptor& targetDescriptor, const GdbDebugServerConfig& serverConfig ); diff --git a/src/DebugServer/Gdb/Exceptions/DebugServerInterrupted.hpp b/src/DebugServer/Gdb/Exceptions/DebugServerInterrupted.hpp index a5c4d086..604e6970 100644 --- a/src/DebugServer/Gdb/Exceptions/DebugServerInterrupted.hpp +++ b/src/DebugServer/Gdb/Exceptions/DebugServerInterrupted.hpp @@ -16,6 +16,6 @@ namespace DebugServer::Gdb::Exceptions class DebugServerInterrupted: public ::Exceptions::Exception { public: - explicit DebugServerInterrupted() = default; + DebugServerInterrupted() = default; }; } diff --git a/src/DebugServer/Gdb/GdbDebugServerConfig.cpp b/src/DebugServer/Gdb/GdbDebugServerConfig.cpp index a2edfd05..0578ced2 100644 --- a/src/DebugServer/Gdb/GdbDebugServerConfig.cpp +++ b/src/DebugServer/Gdb/GdbDebugServerConfig.cpp @@ -12,7 +12,7 @@ namespace DebugServer::Gdb if (!YamlUtilities::isCastable(debugServerConfig.debugServerNode["ipAddress"])) { Logger::error( "Invalid GDB debug server config parameter ('ipAddress') provided - must be a string. The " - "parameter will be ignored." + "parameter will be ignored." ); } @@ -26,7 +26,7 @@ namespace DebugServer::Gdb } else { Logger::error( "Invalid GDB debug server config parameter ('port') provided - value must be castable to a 16-bit " - "unsigned integer. The parameter will be ignored." + "unsigned integer. The parameter will be ignored." ); } } @@ -38,7 +38,7 @@ namespace DebugServer::Gdb } else { Logger::error( "Invalid GDB debug server config parameter ('rangeStepping') provided - value must be castable to " - "a boolean. The parameter will be ignored." + "a boolean. The parameter will be ignored." ); } } diff --git a/src/DebugServer/Gdb/GdbRspDebugServer.cpp b/src/DebugServer/Gdb/GdbRspDebugServer.cpp index cd6e25f3..e051fbd8 100644 --- a/src/DebugServer/Gdb/GdbRspDebugServer.cpp +++ b/src/DebugServer/Gdb/GdbRspDebugServer.cpp @@ -28,9 +28,7 @@ #include "CommandPackets/HelpMonitorInfo.hpp" #include "CommandPackets/BloomVersion.hpp" #include "CommandPackets/BloomVersionMachine.hpp" -#include "CommandPackets/GenerateSvd.hpp" #include "CommandPackets/Detach.hpp" -#include "CommandPackets/EepromFill.hpp" #ifndef EXCLUDE_INSIGHT #include "CommandPackets/ActivateInsight.hpp" @@ -51,10 +49,12 @@ namespace DebugServer::Gdb GdbRspDebugServer::GdbRspDebugServer( const DebugServerConfig& debugServerConfig, + const Targets::TargetDescriptor& targetDescriptor, EventListener& eventListener, EventFdNotifier& eventNotifier ) : debugServerConfig(GdbDebugServerConfig(debugServerConfig)) + , targetDescriptor(targetDescriptor) , eventListener(eventListener) , interruptEventNotifier(eventNotifier) {} @@ -63,28 +63,27 @@ namespace DebugServer::Gdb this->socketAddress.sin_family = AF_INET; this->socketAddress.sin_port = htons(this->debugServerConfig.listeningPortNumber); - if (::inet_pton( + if ( + ::inet_pton( AF_INET, this->debugServerConfig.listeningAddress.c_str(), &(this->socketAddress.sin_addr) ) == 0 ) { // Invalid IP address - throw InvalidConfig( - "Invalid IP address provided in config file: (\"" + this->debugServerConfig.listeningAddress - + "\")" - ); + throw InvalidConfig{ + "Invalid IP address provided in config file: (\"" + this->debugServerConfig.listeningAddress + "\")" + }; } - int socketFileDescriptor = 0; - + auto socketFileDescriptor = int{0}; if ((socketFileDescriptor = ::socket(AF_INET, SOCK_STREAM, 0)) == 0) { - throw Exception("Failed to create socket file descriptor."); + throw Exception{"Failed to create socket file descriptor."}; } - const auto enableReuseAddressSocketOption = 1; - - if (::setsockopt( + const auto enableReuseAddressSocketOption = int{1}; + if ( + ::setsockopt( socketFileDescriptor, SOL_SOCKET, SO_REUSEADDR, @@ -95,14 +94,17 @@ namespace DebugServer::Gdb Logger::error("Failed to set socket SO_REUSEADDR option."); } - if (::bind( + if ( + ::bind( socketFileDescriptor, reinterpret_cast(&(this->socketAddress)), sizeof(this->socketAddress) ) < 0 ) { - throw Exception("Failed to bind address. The selected port number (" - + std::to_string(this->debugServerConfig.listeningPortNumber) + ") may be in use."); + throw Exception{ + "Failed to bind address. The selected port number (" + + std::to_string(this->debugServerConfig.listeningPortNumber) + ") may be in use." + }; } this->serverSocketFileDescriptor = socketFileDescriptor; @@ -120,12 +122,8 @@ namespace DebugServer::Gdb Logger::info("GDB RSP address: " + this->debugServerConfig.listeningAddress); Logger::info("GDB RSP port: " + std::to_string(this->debugServerConfig.listeningPortNumber)); - this->eventListener.registerCallbackForEventType( - std::bind(&GdbRspDebugServer::onTargetExecutionStopped, this, std::placeholders::_1) - ); - - this->eventListener.registerCallbackForEventType( - std::bind(&GdbRspDebugServer::onTargetExecutionResumed, this, std::placeholders::_1) + this->eventListener.registerCallbackForEventType( + std::bind(&GdbRspDebugServer::onTargetStateChanged, this, std::placeholders::_1) ); if (Services::ProcessService::isManagedByClion()) { @@ -149,7 +147,6 @@ namespace DebugServer::Gdb Logger::info("Waiting for GDB RSP connection"); auto connection = this->waitForConnection(); - Logger::info("Accepted GDP RSP connection from " + connection.getIpAddress()); this->startDebugSession(std::move(connection)); @@ -159,9 +156,13 @@ namespace DebugServer::Gdb } const auto commandPacket = this->waitForCommandPacket(); - if (commandPacket) { - commandPacket->handle(*(this->getActiveDebugSession()), this->targetControllerService); + commandPacket->handle( + *(this->getActiveDebugSession()), + this->getGdbTargetDescriptor(), + this->targetDescriptor, + this->targetControllerService + ); } } catch (const ClientDisconnected&) { @@ -195,28 +196,25 @@ namespace DebugServer::Gdb Connection GdbRspDebugServer::waitForConnection() { if (::listen(this->serverSocketFileDescriptor.value(), 3) != 0) { - throw Exception("Failed to listen on server socket"); + throw Exception{"Failed to listen on server socket"}; } const auto eventFileDescriptor = this->epollInstance.waitForEvent(); - if ( !eventFileDescriptor.has_value() - || eventFileDescriptor.value() == this->interruptEventNotifier.getFileDescriptor() + || *eventFileDescriptor == this->interruptEventNotifier.getFileDescriptor() ) { this->interruptEventNotifier.clear(); - throw DebugServerInterrupted(); + throw DebugServerInterrupted{}; } - return Connection( - this->serverSocketFileDescriptor.value(), - this->interruptEventNotifier - ); + return {this->serverSocketFileDescriptor.value(), this->interruptEventNotifier}; } std::unique_ptr GdbRspDebugServer::waitForCommandPacket() { auto* debugSession = this->getActiveDebugSession(); const auto rawPackets = debugSession->connection.readRawPackets(); + assert(!rawPackets.empty()); if (rawPackets.size() > 1) { const auto& firstRawPacket = rawPackets.front(); @@ -224,99 +222,110 @@ namespace DebugServer::Gdb if (firstRawPacket.size() == 5 && firstRawPacket[1] == 0x03) { // Interrupt packet that came in too quickly before another packet debugSession->pendingInterrupt = true; - } - // We only process the last packet - any others will probably be duplicates from an impatient client. - Logger::warning("Multiple packets received from GDB - only the most recent will be processed"); + } else { + Logger::warning("Multiple packets received from GDB - only the most recent will be processed"); + } } return this->resolveCommandPacket(rawPackets.back()); } std::unique_ptr GdbRspDebugServer::resolveCommandPacket(const RawPacket& rawPacket) { + if (rawPacket.size() < 2) { + throw Exception{"Invalid raw packet - no data"}; + } + if (rawPacket.size() == 5 && rawPacket[1] == 0x03) { // Interrupt request return std::make_unique(rawPacket); } + if (rawPacket[1] == 'c') { + return std::make_unique(rawPacket); + } + + if (rawPacket[1] == 's') { + return std::make_unique(rawPacket); + } + + if (rawPacket[1] == 'Z') { + return std::make_unique(rawPacket); + } + + if (rawPacket[1] == 'z') { + return std::make_unique(rawPacket); + } + if (rawPacket[1] == 'D') { return std::make_unique(rawPacket); } - const auto rawPacketString = std::string(rawPacket.begin(), rawPacket.end()); + const auto rawPacketString = std::string{rawPacket.begin() + 1, rawPacket.end()}; - if (rawPacketString.size() >= 2) { - /* - * First byte of the raw packet will be 0x24 ('$'), so std::string::find() should return 1, not 0, when - * looking for a command identifier string. - */ - if (rawPacketString.find("qSupported") == 1) { - return std::make_unique(rawPacket); + /* + * First byte of the raw packet will be 0x24 ('$'), so std::string::find() should return 1, not 0, when + * looking for a command identifier string. + */ + if (rawPacketString.find("qSupported") == 0) { + return std::make_unique(rawPacket); + } + + if (rawPacketString.find("qRcmd") == 0) { + // This is a monitor packet + auto monitorCommand = std::make_unique(rawPacket); + + if (monitorCommand->command == "help") { + return std::make_unique(std::move(*(monitorCommand.release()))); } - if (rawPacketString[1] == 'c') { - return std::make_unique(rawPacket); + if (monitorCommand->command == "version") { + return std::make_unique(std::move(*(monitorCommand.release()))); } - if (rawPacketString[1] == 's') { - return std::make_unique(rawPacket); + if (monitorCommand->command == "version machine") { + return std::make_unique(std::move(*(monitorCommand.release()))); } - if (rawPacketString[1] == 'Z') { - return std::make_unique(rawPacket); + if (monitorCommand->command == "reset") { + return std::make_unique(std::move(*(monitorCommand.release()))); } - if (rawPacketString[1] == 'z') { - return std::make_unique(rawPacket); - } - - if (rawPacketString.find("qRcmd") == 1) { - // This is a monitor packet - auto monitorCommand = std::make_unique(rawPacket); - - if (monitorCommand->command == "help") { - return std::make_unique(std::move(*(monitorCommand.release()))); - } - - if (monitorCommand->command == "version") { - return std::make_unique(std::move(*(monitorCommand.release()))); - } - - if (monitorCommand->command == "version machine") { - return std::make_unique(std::move(*(monitorCommand.release()))); - } - - if (monitorCommand->command == "reset") { - return std::make_unique(std::move(*(monitorCommand.release()))); - } - - if (monitorCommand->command.find("svd") == 0) { - return std::make_unique(std::move(*(monitorCommand.release()))); - } - - if (monitorCommand->command.find("eeprom fill") == 0) { - return std::make_unique(std::move(*(monitorCommand.release()))); - } #ifndef EXCLUDE_INSIGHT - if (monitorCommand->command.find("insight") == 0) { - return std::make_unique(std::move(*(monitorCommand.release()))); - } -#endif - return monitorCommand; + if (monitorCommand->command.find("insight") == 0) { + return std::make_unique(std::move(*(monitorCommand.release()))); } +#endif + return monitorCommand; } return std::make_unique(rawPacket); } - void GdbRspDebugServer::onTargetExecutionStopped(const Events::TargetExecutionStopped& stoppedEvent) { - using Services::StringService; + void GdbRspDebugServer::onTargetStateChanged(const Events::TargetStateChanged& event) { + using Targets::TargetExecutionState; auto* debugSession = this->getActiveDebugSession(); + if (debugSession == nullptr) { + return; + } + + if (event.newState.executionState == event.previousState.executionState) { + // Execution state hasn't changed. Probably just a mode change. Ignore... + return; + } + + const auto executionState = event.newState.executionState.load(); try { - if (debugSession != nullptr && debugSession->waitingForBreak) { - this->handleTargetStoppedGdbResponse(stoppedEvent.programCounter); + if (executionState == TargetExecutionState::STOPPED && debugSession->waitingForBreak) { + this->handleTargetStoppedGdbResponse(event.newState.programCounter.load().value()); + return; + } + + if (executionState == TargetExecutionState::RUNNING || executionState == TargetExecutionState::STEPPING) { + this->handleTargetResumedGdbResponse(); + return; } } catch (const ClientDisconnected&) { @@ -335,41 +344,12 @@ namespace DebugServer::Gdb // Server was interrupted Logger::debug("GDB RSP interrupted"); return; + } catch (const Exception& exception) { Logger::error("Failed to handle target execution stopped event - " + exception.getMessage()); } } - void GdbRspDebugServer::onTargetExecutionResumed(const Events::TargetExecutionResumed&) { - auto* debugSession = this->getActiveDebugSession(); - - try { - if (debugSession != nullptr) { - this->handleTargetResumedGdbResponse(); - } - - } catch (const ClientDisconnected&) { - Logger::info("GDB RSP client disconnected"); - this->endDebugSession(); - return; - - } catch (const ClientCommunicationError& exception) { - Logger::error( - "GDB RSP client communication error - " + exception.getMessage() + " - closing connection" - ); - this->endDebugSession(); - return; - - } catch (const DebugServerInterrupted&) { - // Server was interrupted - Logger::debug("GDB RSP interrupted"); - return; - - } catch (const Exception& exception) { - Logger::error("Failed to handle target execution resumed event - " + exception.getMessage()); - } - } - void GdbRspDebugServer::handleTargetStoppedGdbResponse(Targets::TargetMemoryAddress programAddress) { auto* debugSession = this->getActiveDebugSession(); @@ -377,7 +357,7 @@ namespace DebugServer::Gdb debugSession->terminateRangeSteppingSession(this->targetControllerService); } - debugSession->connection.writePacket(ResponsePackets::TargetStopped(Signal::TRAP)); + debugSession->connection.writePacket(ResponsePackets::TargetStopped{Signal::TRAP}); debugSession->waitingForBreak = false; } @@ -392,7 +372,7 @@ namespace DebugServer::Gdb debugSession->terminateRangeSteppingSession(this->targetControllerService); } - debugSession->connection.writePacket(ResponsePackets::TargetStopped(Signal::INTERRUPTED)); + debugSession->connection.writePacket(ResponsePackets::TargetStopped{Signal::INTERRUPTED}); debugSession->pendingInterrupt = false; debugSession->waitingForBreak = false; } diff --git a/src/DebugServer/Gdb/GdbRspDebugServer.hpp b/src/DebugServer/Gdb/GdbRspDebugServer.hpp index 99f6bb52..5f4eba76 100644 --- a/src/DebugServer/Gdb/GdbRspDebugServer.hpp +++ b/src/DebugServer/Gdb/GdbRspDebugServer.hpp @@ -15,6 +15,7 @@ #include "src/Helpers/EpollInstance.hpp" #include "src/Helpers/EventFdNotifier.hpp" #include "src/Services/TargetControllerService.hpp" +#include "src/Targets/TargetDescriptor.hpp" #include "Connection.hpp" #include "TargetDescriptor.hpp" @@ -24,8 +25,7 @@ #include "Feature.hpp" #include "CommandPackets/CommandPacket.hpp" -#include "src/EventManager/Events/TargetExecutionStopped.hpp" -#include "src/EventManager/Events/TargetExecutionResumed.hpp" +#include "src/EventManager/Events/TargetStateChanged.hpp" namespace DebugServer::Gdb { @@ -43,6 +43,7 @@ namespace DebugServer::Gdb public: explicit GdbRspDebugServer( const DebugServerConfig& debugServerConfig, + const Targets::TargetDescriptor& targetDescriptor, EventListener& eventListener, EventFdNotifier& eventNotifier ); @@ -78,14 +79,10 @@ namespace DebugServer::Gdb void run() override; protected: - /** - * User project configuration specific to the GDB RSP debug server. - */ GdbDebugServerConfig debugServerConfig; - /** - * The DebugServerComponent's event listener. - */ + const Targets::TargetDescriptor& targetDescriptor; + EventListener& eventListener; /** @@ -188,17 +185,7 @@ namespace DebugServer::Gdb */ virtual const TargetDescriptor& getGdbTargetDescriptor() = 0; - /** - * If the GDB client is currently waiting for the target execution to stop, this event handler will issue - * a "stop reply" packet to the client once the target execution stops. - */ - void onTargetExecutionStopped(const Events::TargetExecutionStopped& stoppedEvent); - - /** - * Services any pending interrupts. - */ - void onTargetExecutionResumed(const Events::TargetExecutionResumed&); - + void onTargetStateChanged(const Events::TargetStateChanged& event); virtual void handleTargetStoppedGdbResponse(Targets::TargetMemoryAddress programAddress); virtual void handleTargetResumedGdbResponse(); }; diff --git a/src/DebugServer/Gdb/Packet.hpp b/src/DebugServer/Gdb/Packet.hpp index 81678915..b14ab408 100644 --- a/src/DebugServer/Gdb/Packet.hpp +++ b/src/DebugServer/Gdb/Packet.hpp @@ -32,65 +32,6 @@ namespace DebugServer::Gdb Packet& operator = (const Packet& other) = default; Packet& operator = (Packet&& other) = default; - /** - * Generates a raw packet. - * - * @return - */ - [[nodiscard]] RawPacket toRawPacket() const { - std::vector packet = {'$'}; - - for (const auto& byte : this->data) { - // Escape $ and # characters - switch (byte) { - case '$': - case '#': { - packet.push_back('}'); - packet.push_back(byte ^ 0x20); - } - default: { - packet.push_back(byte); - } - } - } - - auto dataSum = std::accumulate(packet.begin() + 1, packet.end(), 0); - packet.push_back('#'); - auto checkSum = QStringLiteral("%1").arg(dataSum % 256, 2, 16, QLatin1Char('0')).toStdString(); - - if (checkSum.size() < 2) { - packet.push_back('0'); - - if (checkSum.size() < 1) { - packet.push_back('0'); - } else { - packet.push_back(static_cast(checkSum[0])); - } - } - - packet.push_back(static_cast(checkSum[0])); - packet.push_back(static_cast(checkSum[1])); - - return packet; - } - - /** - * Converts data in hexadecimal form to raw data. - * - * @param hexData - * @return - */ - static std::vector hexToData(const std::string& hexData) { - std::vector output; - - for (auto i = 0; i < hexData.size(); i += 2) { - auto hexByte = std::string((hexData.begin() + i), (hexData.begin() + i + 2)); - output.push_back(static_cast(std::stoi(hexByte, nullptr, 16))); - } - - return output; - } - protected: std::vector data; diff --git a/src/DebugServer/Gdb/ProgrammingSession.hpp b/src/DebugServer/Gdb/ProgrammingSession.hpp index ffbee358..a16c901f 100644 --- a/src/DebugServer/Gdb/ProgrammingSession.hpp +++ b/src/DebugServer/Gdb/ProgrammingSession.hpp @@ -15,7 +15,7 @@ namespace DebugServer::Gdb */ struct ProgrammingSession { - Targets::TargetMemoryAddress startAddress = 0x00; + Targets::TargetMemoryAddress startAddress; Targets::TargetMemoryBuffer buffer; ProgrammingSession( diff --git a/src/DebugServer/Gdb/RegisterDescriptor.hpp b/src/DebugServer/Gdb/RegisterDescriptor.hpp index 98a34319..67352e7c 100644 --- a/src/DebugServer/Gdb/RegisterDescriptor.hpp +++ b/src/DebugServer/Gdb/RegisterDescriptor.hpp @@ -1,7 +1,6 @@ #pragma once #include -#include namespace DebugServer::Gdb { @@ -10,19 +9,17 @@ namespace DebugServer::Gdb /* * GDB defines a set of registers for each target architecture. * - * Each register in the set is assigned an ID, which is used to identify the registers. Although the mapping of + * Each register in the set is assigned an ID, which is used to identify the register. Although the mapping of * registers to IDs is hardcoded in GDB, GDB server implementations are expected to be aware of this mapping. */ struct RegisterDescriptor { GdbRegisterId id; std::uint16_t size; - std::string name; - RegisterDescriptor(GdbRegisterId id, std::uint16_t size, const std::string& name) + RegisterDescriptor(GdbRegisterId id, std::uint16_t size) : id(id) , size(size) - , name(name) {}; bool operator == (const RegisterDescriptor& other) const { @@ -50,22 +47,3 @@ namespace DebugServer::Gdb } }; } - -namespace std -{ - /** - * Hashing function for RegisterDescriptor type. - * - * This is required in order to use RegisterDescriptor as a key in an std::unordered_map (see the BiMap - * class). - */ - template<> - class hash - { - public: - std::size_t operator () (const DebugServer::Gdb::RegisterDescriptor& descriptor) const { - // We use the GDB register number as the hash, as it is unique to the register. - return static_cast(descriptor.id); - } - }; -} diff --git a/src/DebugServer/Gdb/Resources/GdbHelpMonitorInfo.txt.in b/src/DebugServer/Gdb/Resources/GdbHelpMonitorInfo.txt.in index fa6c020f..5d735558 100644 --- a/src/DebugServer/Gdb/Resources/GdbHelpMonitorInfo.txt.in +++ b/src/DebugServer/Gdb/Resources/GdbHelpMonitorInfo.txt.in @@ -4,10 +4,6 @@ Supported Bloom commands: version Outputs Bloom's version information. version machine Outputs Bloom's version information in JSON format. @ACTIVATE_INSIGHT_HELP_TEXT@ - svd Generates the System View Description (SVD) XML for the current target and saves it into a - file located in the current project directory. - svd --out Generates the System View Description (SVD) XML for the current target and sends it to GDB, as - command output. reset Resets the target and holds it in a stopped state. diff --git a/src/DebugServer/Gdb/ResponsePackets/ResponsePacket.cpp b/src/DebugServer/Gdb/ResponsePackets/ResponsePacket.cpp new file mode 100644 index 00000000..0be278c0 --- /dev/null +++ b/src/DebugServer/Gdb/ResponsePackets/ResponsePacket.cpp @@ -0,0 +1,41 @@ +#include "ResponsePacket.hpp" + +namespace DebugServer::Gdb::ResponsePackets +{ + RawPacket ResponsePacket::toRawPacket() const { + std::vector packet = {'$'}; + + for (const auto& byte : this->data) { + // Escape $ and # characters + switch (byte) { + case '$': + case '#': { + packet.push_back('}'); + packet.push_back(byte ^ 0x20); + } + default: { + packet.push_back(byte); + } + } + } + + auto dataSum = std::accumulate(packet.begin() + 1, packet.end(), 0); + packet.push_back('#'); + auto checkSum = QStringLiteral("%1").arg(dataSum % 256, 2, 16, QLatin1Char{'0'}).toStdString(); + + if (checkSum.size() < 2) { + packet.push_back('0'); + + if (checkSum.size() < 1) { + packet.push_back('0'); + } else { + packet.push_back(static_cast(checkSum[0])); + } + } + + packet.push_back(static_cast(checkSum[0])); + packet.push_back(static_cast(checkSum[1])); + + return packet; + } +} diff --git a/src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp b/src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp index 9b636644..180a8a32 100644 --- a/src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp +++ b/src/DebugServer/Gdb/ResponsePackets/ResponsePacket.hpp @@ -20,7 +20,14 @@ namespace DebugServer::Gdb::ResponsePackets } explicit ResponsePacket(const std::string& data) { - this->data = std::vector(data.begin(), data.end()); + this->data = std::vector{data.begin(), data.end()}; } + + /** + * Generates a raw packet. + * + * @return + */ + [[nodiscard]] RawPacket toRawPacket() const; }; } diff --git a/src/DebugServer/Gdb/ResponsePackets/SupportedFeaturesResponse.cpp b/src/DebugServer/Gdb/ResponsePackets/SupportedFeaturesResponse.cpp index 49209da4..c121ac6f 100644 --- a/src/DebugServer/Gdb/ResponsePackets/SupportedFeaturesResponse.cpp +++ b/src/DebugServer/Gdb/ResponsePackets/SupportedFeaturesResponse.cpp @@ -7,7 +7,7 @@ namespace DebugServer::Gdb::ResponsePackets ) : supportedFeatures(supportedFeatures) { - auto output = std::string("qSupported:"); + auto output = std::string{"qSupported:"}; static const auto gdbFeatureMapping = getGdbFeatureToNameMapping(); for (const auto& supportedFeature : this->supportedFeatures) { diff --git a/src/DebugServer/Gdb/ResponsePackets/TargetStopped.hpp b/src/DebugServer/Gdb/ResponsePackets/TargetStopped.hpp index e2673876..f6f25cb6 100644 --- a/src/DebugServer/Gdb/ResponsePackets/TargetStopped.hpp +++ b/src/DebugServer/Gdb/ResponsePackets/TargetStopped.hpp @@ -25,7 +25,9 @@ namespace DebugServer::Gdb::ResponsePackets : signal(signal) , stopReason(stopReason) { - std::string packetData = "T" + Services::StringService::toHex(static_cast(this->signal)); + auto packetData = std::string{"T"} + Services::StringService::toHex( + static_cast(this->signal) + ); if (this->stopReason.has_value()) { static const auto stopReasonMapping = getStopReasonToNameMapping(); diff --git a/src/DebugServer/Gdb/StopReason.hpp b/src/DebugServer/Gdb/StopReason.hpp index b0696852..8d687155 100644 --- a/src/DebugServer/Gdb/StopReason.hpp +++ b/src/DebugServer/Gdb/StopReason.hpp @@ -11,9 +11,9 @@ namespace DebugServer::Gdb }; static inline BiMap getStopReasonToNameMapping() { - return BiMap({ + return BiMap{ {StopReason::HARDWARE_BREAKPOINT, "hwbreak"}, {StopReason::SOFTWARE_BREAKPOINT, "swbreak"}, - }); + }; } } diff --git a/src/DebugServer/Gdb/TargetDescriptor.cpp b/src/DebugServer/Gdb/TargetDescriptor.cpp deleted file mode 100644 index 38b73af4..00000000 --- a/src/DebugServer/Gdb/TargetDescriptor.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "TargetDescriptor.hpp" - -namespace DebugServer::Gdb -{ - TargetDescriptor::TargetDescriptor( - const Targets::TargetDescriptor& targetDescriptor, - const BiMap& memoryOffsetsByType, - std::map gdbRegisterDescriptorsById, - std::map gdbRegisterIdsByTargetRegisterDescriptorId, - std::map targetRegisterDescriptorIdsByGdbRegisterId - ) - : targetDescriptor(targetDescriptor) - , gdbRegisterDescriptorsById(gdbRegisterDescriptorsById) - , memoryOffsetsByType(memoryOffsetsByType) - , memoryOffsets(memoryOffsetsByType.getValues()) - , gdbRegisterIdsByTargetRegisterDescriptorId(gdbRegisterIdsByTargetRegisterDescriptorId) - , targetRegisterDescriptorIdsByGdbRegisterId(targetRegisterDescriptorIdsByGdbRegisterId) - {} - - std::uint32_t TargetDescriptor::getMemoryOffset(Targets::TargetMemoryType memoryType) const { - return this->memoryOffsetsByType.valueAt(memoryType).value_or(0); - } - - Targets::TargetMemoryType TargetDescriptor::getMemoryTypeFromGdbAddress(std::uint32_t address) const { - // Start with the largest offset until we find a match - for ( - auto memoryOffsetIt = this->memoryOffsets.rbegin(); - memoryOffsetIt != this->memoryOffsets.rend(); - ++memoryOffsetIt - ) { - if ((address & *memoryOffsetIt) == *memoryOffsetIt) { - return this->memoryOffsetsByType.at(*memoryOffsetIt); - } - } - - return Targets::TargetMemoryType::FLASH; - } - - std::optional TargetDescriptor::getGdbRegisterIdFromTargetRegisterDescriptorId( - Targets::TargetRegisterDescriptorId targetRegisterDescriptorId - ) const { - const auto gdbRegisterIdIt = this->gdbRegisterIdsByTargetRegisterDescriptorId.find( - targetRegisterDescriptorId - ); - - if (gdbRegisterIdIt != this->gdbRegisterIdsByTargetRegisterDescriptorId.end()) { - return gdbRegisterIdIt->second; - } - - return std::nullopt; - } - - std::optional TargetDescriptor::getTargetRegisterDescriptorIdFromGdbRegisterId( - GdbRegisterId gdbRegisterId - ) const { - const auto registerDescriptorIdIt = this->targetRegisterDescriptorIdsByGdbRegisterId.find(gdbRegisterId); - - if (registerDescriptorIdIt != this->targetRegisterDescriptorIdsByGdbRegisterId.end()) { - return registerDescriptorIdIt->second; - } - - return std::nullopt; - } -} diff --git a/src/DebugServer/Gdb/TargetDescriptor.hpp b/src/DebugServer/Gdb/TargetDescriptor.hpp index 3d927682..413d1d89 100644 --- a/src/DebugServer/Gdb/TargetDescriptor.hpp +++ b/src/DebugServer/Gdb/TargetDescriptor.hpp @@ -9,85 +9,61 @@ #include "src/Helpers/BiMap.hpp" #include "src/Targets/TargetDescriptor.hpp" #include "src/Targets/TargetRegisterDescriptor.hpp" -#include "src/Targets/TargetRegister.hpp" #include "src/Targets/TargetMemory.hpp" #include "RegisterDescriptor.hpp" namespace DebugServer::Gdb { + using GdbMemoryAddress = std::uint32_t; + /** * GDB target descriptor. */ class TargetDescriptor { public: - Targets::TargetDescriptor targetDescriptor; std::map gdbRegisterDescriptorsById; - - explicit TargetDescriptor( - const Targets::TargetDescriptor& targetDescriptor, - const BiMap& memoryOffsetsByType, - std::map gdbRegisterDescriptorsById, - std::map gdbRegisterIdsByTargetRegisterDescriptorId, - std::map targetRegisterDescriptorIdsByGdbRegisterId - ); + std::map targetRegisterDescriptorsByGdbId; virtual ~TargetDescriptor() = default; - std::uint32_t getMemoryOffset(Targets::TargetMemoryType memoryType) const; - /** - * Helper method to extract the target memory type (Flash, RAM, etc) from a GDB memory address. + * For targets with multiple address spaces (e.g. AVR), GDB encodes address space information into memory + * addresses, by applying a mask. + * + * This function should identify the encoded address space within a GDB memory address, and return the + * relevant address space descriptor. * * @param address * @return */ - Targets::TargetMemoryType getMemoryTypeFromGdbAddress(std::uint32_t address) const; + virtual const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptorFromGdbAddress( + GdbMemoryAddress address + ) const = 0; /** - * Should retrieve the GDB register ID, given a target register descriptor ID. Or std::nullopt if the - * target register descriptor ID isn't mapped to any GDB register. + * This function should translate a GDB memory address to a target memory address. This should strip any + * GDB-specific masks and return an address that can be used within Bloom. * - * @param registerDescriptorId + * @param address * @return */ - std::optional getGdbRegisterIdFromTargetRegisterDescriptorId( - Targets::TargetRegisterDescriptorId targetRegisterDescriptorId - ) const; + virtual Targets::TargetMemoryAddress translateGdbAddress(GdbMemoryAddress address) const = 0; /** - * Should retrieve the mapped target register descriptor ID for a given GDB register ID. + * This function should translate a target memory address to a GDB memory address. It should encode any + * additional data expected by GDB. * - * This function may return std::nullopt if the GDB register ID maps to something that isn't considered a - * register on our end. For example, for AVR targets, the GDB register ID 34 maps to the program counter. But - * the program counter is not treated like any other register in Bloom (there's no TargetRegisterDescriptor for - * it). So in that case, the GDB register ID is not mapped to any target register descriptor ID. - * - * @param gdbRegisterId + * @param address + * @param addressSpaceDescriptor + * @param memorySegmentDescriptor * @return */ - std::optional getTargetRegisterDescriptorIdFromGdbRegisterId( - GdbRegisterId gdbRegisterId - ) const; - - protected: - /** - * When GDB sends us a memory address, the memory type (Flash, RAM, EEPROM, etc) is embedded within. This is - * done by ORing the address with some constant. For example, for AVR targets, RAM addresses are ORed with - * 0x00800000. Flash addresses are left unchanged. EEPROM addressing is not supported in GDB (for AVR targets). - * - * memoryOffsetsByType is a mapping of memory types to these known constants (which we're calling offsets). - * Because these offsets vary by target, the mapping lives here, in the GDB target descriptor. - */ - BiMap memoryOffsetsByType; - - /** - * Sorted set of the known memory offsets (see memoryOffsetsByType). - */ - std::set memoryOffsets; - - std::map gdbRegisterIdsByTargetRegisterDescriptorId; - std::map targetRegisterDescriptorIdsByGdbRegisterId; + virtual GdbMemoryAddress translateTargetMemoryAddress( + Targets::TargetMemoryAddress address, + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor + ) const = 0; }; } diff --git a/src/DebugToolDrivers/CMakeLists.txt b/src/DebugToolDrivers/CMakeLists.txt index 479fa601..216fb5e1 100755 --- a/src/DebugToolDrivers/CMakeLists.txt +++ b/src/DebugToolDrivers/CMakeLists.txt @@ -7,6 +7,8 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/Protocols/CMSIS-DAP/CmsisDapInterface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Protocols/CMSIS-DAP/Command.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Protocols/CMSIS-DAP/Response.cpp + + # Microchip EDBG implementation ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/AvrCommand.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/CommandFrames/AVR8Generic/ReadMemory.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/AvrResponse.cpp @@ -27,6 +29,8 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/EdbgAvr8Session.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/EdbgAvr8Interface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/Protocols/EDBG/AVR/EdbgAvrIspInterface.cpp + + # Microchip EDBG debug tools ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/EdbgDevice.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AtmelICE/AtmelIce.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/PowerDebugger/PowerDebugger.cpp @@ -37,7 +41,12 @@ target_sources( ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/XplainedNano/XplainedNano.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/CuriosityNano/CuriosityNano.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/JtagIce3/JtagIce3.cpp + + # RISC-V debug tools ${CMAKE_CURRENT_SOURCE_DIR}/WCH/Protocols/WchLink/WchLinkInterface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkBase.cpp ${CMAKE_CURRENT_SOURCE_DIR}/WCH/WchLinkE/WchLinkE.cpp + + # RISC-V Debug Translator implementation + ${CMAKE_CURRENT_SOURCE_DIR}/Protocols/RiscVDebugSpec/DebugTranslator.cpp ) diff --git a/src/DebugToolDrivers/DebugTool.hpp b/src/DebugToolDrivers/DebugTool.hpp index 988a94d5..1a66e095 100644 --- a/src/DebugToolDrivers/DebugTool.hpp +++ b/src/DebugToolDrivers/DebugTool.hpp @@ -2,14 +2,16 @@ #include "TargetInterfaces/TargetPowerManagementInterface.hpp" -#include "TargetInterfaces/Microchip/AVR/AVR8/Avr8DebugInterface.hpp" -#include "TargetInterfaces/Microchip/AVR/AvrIspInterface.hpp" -#include "src/Targets/Microchip/AVR/AVR8/Avr8TargetConfig.hpp" -#include "src/Targets/Microchip/AVR/AVR8/Family.hpp" -#include "src/Targets/Microchip/AVR/AVR8/TargetParameters.hpp" +#include "TargetInterfaces/Microchip/AVR8/Avr8DebugInterface.hpp" +#include "TargetInterfaces/Microchip/AVR8/AvrIspInterface.hpp" +#include "src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp" +#include "src/Targets/Microchip/AVR8/Avr8TargetConfig.hpp" #include "TargetInterfaces/RiscV/RiscVDebugInterface.hpp" #include "TargetInterfaces/RiscV/RiscVProgramInterface.hpp" +#include "TargetInterfaces/RiscV/RiscVIdentificationInterface.hpp" +#include "src/Targets/RiscV/TargetDescriptionFile.hpp" +#include "src/Targets/RiscV/RiscVTargetConfig.hpp" #include "src/Targets/TargetRegisterDescriptor.hpp" @@ -44,6 +46,8 @@ public: */ virtual void close() = 0; + virtual bool isInitialised() const = 0; + virtual std::string getName() = 0; virtual std::string getSerialNumber() = 0; @@ -75,11 +79,9 @@ public: * * @return */ - virtual DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8::Avr8DebugInterface* getAvr8DebugInterface( - const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig, - Targets::Microchip::Avr::Avr8Bit::Family targetFamily, - const Targets::Microchip::Avr::Avr8Bit::TargetParameters& targetParameters, - const Targets::TargetRegisterDescriptorMapping& targetRegisterDescriptorsById + virtual DebugToolDrivers::TargetInterfaces::Microchip::Avr8::Avr8DebugInterface* getAvr8DebugInterface( + const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile, + const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig ) { return nullptr; } @@ -94,8 +96,9 @@ public: * * @return */ - virtual DebugToolDrivers::TargetInterfaces::Microchip::Avr::AvrIspInterface* getAvrIspInterface( - const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig + virtual DebugToolDrivers::TargetInterfaces::Microchip::Avr8::AvrIspInterface* getAvrIspInterface( + const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile, + const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig ) { return nullptr; } @@ -110,7 +113,10 @@ public: * * @return */ - virtual DebugToolDrivers::TargetInterfaces::RiscV::RiscVDebugInterface* getRiscVDebugInterface() { + virtual DebugToolDrivers::TargetInterfaces::RiscV::RiscVDebugInterface* getRiscVDebugInterface( + const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile, + const Targets::RiscV::RiscVTargetConfig& targetConfig + ) { return nullptr; } @@ -127,19 +133,31 @@ public: * * @return */ - virtual DebugToolDrivers::TargetInterfaces::RiscV::RiscVProgramInterface* getRiscVProgramInterface() { + virtual DebugToolDrivers::TargetInterfaces::RiscV::RiscVProgramInterface* getRiscVProgramInterface( + const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile, + const Targets::RiscV::RiscVTargetConfig& targetConfig + ) { return nullptr; } - [[nodiscard]] bool isInitialised() const { - return this->initialised; + /** + * The RISC-V debug spec does not define a target ID. But vendors typically assign each model with an ID and + * provide a means to extract it from the connected target, via the debug tool. + * + * For example, WCH debug tools return the target ID in response to the target activation command. For more, see + * the implementation of the WCH-Link protocol. + * + * Bloom uses the target ID for verification purposes. We simply compare it to the one we have in the TDF and shout + * if they don't match. + * + * Note: the caller of this function will not manage the lifetime of the returned instance. + * + * @return + */ + virtual DebugToolDrivers::TargetInterfaces::RiscV::RiscVIdentificationInterface* getRiscVIdentificationInterface( + const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile, + const Targets::RiscV::RiscVTargetConfig& targetConfig + ) { + return nullptr; } - -protected: - void setInitialised(bool initialised) { - this->initialised = initialised; - } - -private: - bool initialised = false; }; diff --git a/src/DebugToolDrivers/Microchip/EdbgDevice.cpp b/src/DebugToolDrivers/Microchip/EdbgDevice.cpp index 7de77861..aeba902e 100644 --- a/src/DebugToolDrivers/Microchip/EdbgDevice.cpp +++ b/src/DebugToolDrivers/Microchip/EdbgDevice.cpp @@ -38,14 +38,14 @@ namespace DebugToolDrivers::Microchip this->setConfiguration(this->configurationIndex.value()); } - auto cmsisHidInterface = Usb::HidInterface( + auto cmsisHidInterface = Usb::HidInterface{ this->cmsisHidInterfaceNumber, this->getEndpointMaxPacketSize( this->getFirstEndpointAddress(this->cmsisHidInterfaceNumber, LIBUSB_ENDPOINT_IN) ), this->vendorId, this->productId - ); + }; cmsisHidInterface.init(); @@ -57,7 +57,7 @@ namespace DebugToolDrivers::Microchip * Because of this, we have to enforce a minimum time gap between commands. See comment * in CmsisDapInterface class declaration for more info. */ - this->edbgInterface->setMinimumCommandTimeGap(std::chrono::milliseconds(35)); + this->edbgInterface->setMinimumCommandTimeGap(std::chrono::milliseconds{35}); // We don't need to claim the CMSISDAP interface here as the HIDAPI will have already done so. if (!this->sessionStarted) { @@ -70,9 +70,7 @@ namespace DebugToolDrivers::Microchip ); } - this->edbgAvrIspInterface = std::make_unique(this->edbgInterface.get()); - - this->setInitialised(true); + this->initialised = true; } void EdbgDevice::close() { @@ -82,21 +80,50 @@ namespace DebugToolDrivers::Microchip this->edbgInterface->getUsbHidInterface().close(); UsbDevice::close(); + this->initialised = false; } - TargetInterfaces::Microchip::Avr::Avr8::Avr8DebugInterface* EdbgDevice::getAvr8DebugInterface( - const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig, - Targets::Microchip::Avr::Avr8Bit::Family targetFamily, - const Targets::Microchip::Avr::Avr8Bit::TargetParameters& targetParameters, - const Targets::TargetRegisterDescriptorMapping& targetRegisterDescriptorsById + bool EdbgDevice::isInitialised() const { + return this->initialised; + } + + std::string EdbgDevice::getSerialNumber() { + using namespace CommandFrames::Discovery; + using ResponseFrames::Discovery::ResponseId; + + const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( + Query{QueryContext::SERIAL_NUMBER} + ); + + if (responseFrame.id != ResponseId::OK) { + throw DeviceInitializationFailure{ + "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + }; + } + + const auto data = responseFrame.getPayloadData(); + return std::string{data.begin(), data.end()}; + } + + std::string EdbgDevice::getFirmwareVersionString() { + // TODO: Implement this + return "UNKNOWN"; + } + + DebugToolDrivers::TargetInterfaces::TargetPowerManagementInterface* EdbgDevice::getTargetPowerManagementInterface() + { + return this->targetPowerManagementInterface.get(); + } + + TargetInterfaces::Microchip::Avr8::Avr8DebugInterface* EdbgDevice::getAvr8DebugInterface( + const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile, + const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig ) { if (this->edbgAvr8Interface == nullptr) { this->edbgAvr8Interface = std::make_unique( this->edbgInterface.get(), - targetConfig, - targetFamily, - targetParameters, - targetRegisterDescriptorsById + targetDescriptionFile, + targetConfig ); this->configureAvr8Interface(); @@ -105,40 +132,30 @@ namespace DebugToolDrivers::Microchip return this->edbgAvr8Interface.get(); } - std::string EdbgDevice::getSerialNumber() { - using namespace CommandFrames::Discovery; - using ResponseFrames::Discovery::ResponseId; - - const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - Query(QueryContext::SERIAL_NUMBER) - ); - - if (responseFrame.id != ResponseId::OK) { - throw DeviceInitializationFailure( - "Failed to fetch serial number from device - invalid Discovery Protocol response ID." + TargetInterfaces::Microchip::Avr8::AvrIspInterface* EdbgDevice::getAvrIspInterface( + const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile, + const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig + ) { + if (this->edbgAvrIspInterface == nullptr) { + this->edbgAvrIspInterface = std::make_unique( + this->edbgInterface.get(), + targetDescriptionFile ); + + this->configureAvr8Interface(); } - const auto data = responseFrame.getPayloadData(); - return std::string(data.begin(), data.end()); - } - - std::string EdbgDevice::getFirmwareVersionString() { - // TODO: Implement this - return "UNKNOWN"; + return this->edbgAvrIspInterface.get(); } void EdbgDevice::startSession() { using namespace CommandFrames::HouseKeeping; using ResponseFrames::HouseKeeping::ResponseId; - const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - StartSession() - ); - + const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(StartSession{}); if (responseFrame.id == ResponseId::FAILED) { // Failed response returned! - throw DeviceInitializationFailure("Failed to start session with EDBG device!"); + throw DeviceInitializationFailure{"Failed to start session with EDBG device!"}; } this->sessionStarted = true; @@ -148,13 +165,10 @@ namespace DebugToolDrivers::Microchip using namespace CommandFrames::HouseKeeping; using ResponseFrames::HouseKeeping::ResponseId; - const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - EndSession() - ); - + const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(EndSession{}); if (responseFrame.id == ResponseId::FAILED) { // Failed response returned! - throw DeviceFailure("Failed to end session with EDBG device!"); + throw DeviceFailure{"Failed to end session with EDBG device!"}; } this->sessionStarted = false; diff --git a/src/DebugToolDrivers/Microchip/EdbgDevice.hpp b/src/DebugToolDrivers/Microchip/EdbgDevice.hpp index 73818daf..7b90af44 100644 --- a/src/DebugToolDrivers/Microchip/EdbgDevice.hpp +++ b/src/DebugToolDrivers/Microchip/EdbgDevice.hpp @@ -50,22 +50,7 @@ namespace DebugToolDrivers::Microchip */ void close() override; - TargetInterfaces::Microchip::Avr::Avr8::Avr8DebugInterface* getAvr8DebugInterface( - const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig, - Targets::Microchip::Avr::Avr8Bit::Family targetFamily, - const Targets::Microchip::Avr::Avr8Bit::TargetParameters& targetParameters, - const Targets::TargetRegisterDescriptorMapping& targetRegisterDescriptorsById - ) override; - - TargetInterfaces::Microchip::Avr::AvrIspInterface* getAvrIspInterface( - const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig - ) override { - return this->edbgAvrIspInterface.get(); - } - - DebugToolDrivers::TargetInterfaces::TargetPowerManagementInterface* getTargetPowerManagementInterface() override { - return this->targetPowerManagementInterface.get(); - } + [[nodiscard]] bool isInitialised() const override; /** * Retrieves the device serial number via the "Discovery" EDBG sub-protocol. @@ -81,6 +66,18 @@ namespace DebugToolDrivers::Microchip */ std::string getFirmwareVersionString() override; + DebugToolDrivers::TargetInterfaces::TargetPowerManagementInterface* getTargetPowerManagementInterface() override; + + TargetInterfaces::Microchip::Avr8::Avr8DebugInterface* getAvr8DebugInterface( + const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile, + const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig + ) override; + + TargetInterfaces::Microchip::Avr8::AvrIspInterface* getAvrIspInterface( + const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile, + const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig + ) override; + /** * Starts a session with the EDBG device using the "Housekeeping" EDBG sub-protocol. */ @@ -92,6 +89,8 @@ namespace DebugToolDrivers::Microchip void endSession(); protected: + bool initialised = false; + /** * The USB interface number of the CMSIS-DAP HID interface. */ @@ -136,9 +135,9 @@ namespace DebugToolDrivers::Microchip * ISP cannot be used for debugging operations. The EdbgAvrIspInterface class does *not* implement * the Avr8DebugInterface. * - * Currently, Bloom will only use the ISP interface as a fallback when attempting to connect to debugWire - * targets. We use the interface to inspect and update the "debugWire enable" (DWEN) fuse-bit, before making a - * second connection attempt via the debugWire interface. + * Currently, Bloom will only use the ISP interface as a fallback when attempting to connect to debugWIRE + * targets. We use the interface to inspect and update the "debugWIRE enable" (DWEN) fuse-bit, before making a + * second connection attempt via the debugWIRE interface. */ std::unique_ptr edbgAvrIspInterface = nullptr; diff --git a/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.cpp b/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.cpp index 14fa5de0..15e7fd2c 100644 --- a/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.cpp +++ b/src/DebugToolDrivers/Microchip/MplabPickit4/MplabPickit4.cpp @@ -30,10 +30,10 @@ namespace DebugToolDrivers::Microchip ); if (!nonEdbgDevices.empty()) { - throw DeviceNotFound( + throw DeviceNotFound{ "The connected MPLAB PICkit 4 device is not in \"AVR mode\". Please follow the instructions at " + Services::PathService::homeDomainName() + "/docs/avr-mode" - ); + }; } throw exception; diff --git a/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.cpp b/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.cpp index 8444dd02..a2a8e96d 100644 --- a/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.cpp +++ b/src/DebugToolDrivers/Microchip/MplabSnap/MplabSnap.cpp @@ -38,10 +38,10 @@ namespace DebugToolDrivers::Microchip } if (!nonEdbgDevices.empty()) { - throw DeviceNotFound( + throw DeviceNotFound{ "The connected MPLAB Snap device is not in \"AVR mode\". Please follow the instructions at " + Services::PathService::homeDomainName() + "/docs/avr-mode" - ); + }; } throw exception; diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Avr8Generic.hpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Avr8Generic.hpp index 328e5bd6..02d32af7 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Avr8Generic.hpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Avr8Generic.hpp @@ -8,10 +8,9 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr { struct Avr8EdbgParameter { - unsigned char context = 0x00; - unsigned char id = 0x00; + unsigned char context; + unsigned char id; - constexpr Avr8EdbgParameter() = default; constexpr Avr8EdbgParameter(unsigned char context, unsigned char id) : context(context) , id(id) @@ -20,70 +19,70 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr struct Avr8EdbgParameters { - static constexpr Avr8EdbgParameter CONFIG_VARIANT {0x00, 0x00}; - static constexpr Avr8EdbgParameter CONFIG_FUNCTION {0x00, 0x01}; - static constexpr Avr8EdbgParameter PHYSICAL_INTERFACE {0x01, 0x00}; - static constexpr Avr8EdbgParameter DW_CLOCK_DIVISION_FACTOR {0x01, 0x10}; - static constexpr Avr8EdbgParameter PDI_CLOCK_SPEED {0x01, 0x31}; - static constexpr Avr8EdbgParameter MEGA_DEBUG_CLOCK {0x01, 0x21}; - static constexpr Avr8EdbgParameter JTAG_DAISY_CHAIN_SETTINGS {0x01, 0x01}; + static constexpr Avr8EdbgParameter CONFIG_VARIANT = {0x00, 0x00}; + static constexpr Avr8EdbgParameter CONFIG_FUNCTION = {0x00, 0x01}; + static constexpr Avr8EdbgParameter PHYSICAL_INTERFACE = {0x01, 0x00}; + static constexpr Avr8EdbgParameter DW_CLOCK_DIVISION_FACTOR = {0x01, 0x10}; + static constexpr Avr8EdbgParameter PDI_CLOCK_SPEED = {0x01, 0x31}; + static constexpr Avr8EdbgParameter MEGA_DEBUG_CLOCK = {0x01, 0x21}; + static constexpr Avr8EdbgParameter JTAG_DAISY_CHAIN_SETTINGS = {0x01, 0x01}; - // debugWire and JTAG parameters - static constexpr Avr8EdbgParameter DEVICE_BOOT_START_ADDR {0x02, 0x0A}; - static constexpr Avr8EdbgParameter DEVICE_FLASH_BASE {0x02, 0x06}; - static constexpr Avr8EdbgParameter DEVICE_SRAM_START {0x02, 0x0E}; - static constexpr Avr8EdbgParameter DEVICE_EEPROM_SIZE {0x02, 0x10}; - static constexpr Avr8EdbgParameter DEVICE_EEPROM_PAGE_SIZE {0x02, 0x12}; - static constexpr Avr8EdbgParameter DEVICE_FLASH_PAGE_SIZE {0x02, 0x00}; - static constexpr Avr8EdbgParameter DEVICE_FLASH_SIZE {0x02, 0x02}; - static constexpr Avr8EdbgParameter DEVICE_OCD_REVISION {0x02, 0x13}; - static constexpr Avr8EdbgParameter DEVICE_OCD_DATA_REGISTER {0x02, 0x18}; - static constexpr Avr8EdbgParameter DEVICE_SPMCR_REGISTER {0x02, 0x1D}; - static constexpr Avr8EdbgParameter DEVICE_OSCCAL_ADDR {0x02, 0x1E}; - static constexpr Avr8EdbgParameter DEVICE_EEARH_ADDR {0x02, 0x19}; - static constexpr Avr8EdbgParameter DEVICE_EEARL_ADDR {0x02, 0x1A}; - static constexpr Avr8EdbgParameter DEVICE_EECR_ADDR {0x02, 0x1B}; - static constexpr Avr8EdbgParameter DEVICE_EEDR_ADDR {0x02, 0x1C}; + // debugWIRE and JTAG parameters + static constexpr Avr8EdbgParameter DEVICE_BOOT_START_ADDR = {0x02, 0x0A}; + static constexpr Avr8EdbgParameter DEVICE_FLASH_BASE = {0x02, 0x06}; + static constexpr Avr8EdbgParameter DEVICE_SRAM_START = {0x02, 0x0E}; + static constexpr Avr8EdbgParameter DEVICE_EEPROM_SIZE = {0x02, 0x10}; + static constexpr Avr8EdbgParameter DEVICE_EEPROM_PAGE_SIZE = {0x02, 0x12}; + static constexpr Avr8EdbgParameter DEVICE_FLASH_PAGE_SIZE = {0x02, 0x00}; + static constexpr Avr8EdbgParameter DEVICE_FLASH_SIZE = {0x02, 0x02}; + static constexpr Avr8EdbgParameter DEVICE_OCD_REVISION = {0x02, 0x13}; + static constexpr Avr8EdbgParameter DEVICE_OCD_DATA_REGISTER = {0x02, 0x18}; + static constexpr Avr8EdbgParameter DEVICE_SPMCR_REGISTER = {0x02, 0x1D}; + static constexpr Avr8EdbgParameter DEVICE_OSCCAL_ADDR = {0x02, 0x1E}; + static constexpr Avr8EdbgParameter DEVICE_EEARH_ADDR = {0x02, 0x19}; + static constexpr Avr8EdbgParameter DEVICE_EEARL_ADDR = {0x02, 0x1A}; + static constexpr Avr8EdbgParameter DEVICE_EECR_ADDR = {0x02, 0x1B}; + static constexpr Avr8EdbgParameter DEVICE_EEDR_ADDR = {0x02, 0x1C}; // PDI/XMega device parameters - static constexpr Avr8EdbgParameter DEVICE_XMEGA_APPL_BASE_ADDR {0x02, 0x00}; - static constexpr Avr8EdbgParameter DEVICE_XMEGA_BOOT_BASE_ADDR {0x02, 0x04}; - static constexpr Avr8EdbgParameter DEVICE_XMEGA_EEPROM_BASE_ADDR {0x02, 0x08}; - static constexpr Avr8EdbgParameter DEVICE_XMEGA_FUSE_BASE_ADDR {0x02, 0x0C}; - static constexpr Avr8EdbgParameter DEVICE_XMEGA_LOCKBIT_BASE_ADDR {0x02, 0x10}; - static constexpr Avr8EdbgParameter DEVICE_XMEGA_USER_SIGN_BASE_ADDR {0x02, 0x14}; - static constexpr Avr8EdbgParameter DEVICE_XMEGA_PROD_SIGN_BASE_ADDR {0x02, 0x18}; - static constexpr Avr8EdbgParameter DEVICE_XMEGA_DATA_BASE_ADDR {0x02, 0x1C}; - static constexpr Avr8EdbgParameter DEVICE_XMEGA_APPLICATION_BYTES {0x02, 0x20}; - static constexpr Avr8EdbgParameter DEVICE_XMEGA_BOOT_BYTES {0x02, 0x24}; - static constexpr Avr8EdbgParameter DEVICE_XMEGA_NVM_BASE {0x02, 0x2B}; - static constexpr Avr8EdbgParameter DEVICE_XMEGA_SIGNATURE_OFFSET {0x02, 0x2D}; - static constexpr Avr8EdbgParameter DEVICE_XMEGA_FLASH_PAGE_BYTES {0x02, 0x26}; - static constexpr Avr8EdbgParameter DEVICE_XMEGA_EEPROM_SIZE {0x02, 0x28}; - static constexpr Avr8EdbgParameter DEVICE_XMEGA_EEPROM_PAGE_SIZE {0x02, 0x2A}; + static constexpr Avr8EdbgParameter DEVICE_XMEGA_APPL_BASE_ADDR = {0x02, 0x00}; + static constexpr Avr8EdbgParameter DEVICE_XMEGA_BOOT_BASE_ADDR = {0x02, 0x04}; + static constexpr Avr8EdbgParameter DEVICE_XMEGA_EEPROM_BASE_ADDR = {0x02, 0x08}; + static constexpr Avr8EdbgParameter DEVICE_XMEGA_FUSE_BASE_ADDR = {0x02, 0x0C}; + static constexpr Avr8EdbgParameter DEVICE_XMEGA_LOCKBIT_BASE_ADDR = {0x02, 0x10}; + static constexpr Avr8EdbgParameter DEVICE_XMEGA_USER_SIGN_BASE_ADDR = {0x02, 0x14}; + static constexpr Avr8EdbgParameter DEVICE_XMEGA_PROD_SIGN_BASE_ADDR = {0x02, 0x18}; + static constexpr Avr8EdbgParameter DEVICE_XMEGA_DATA_BASE_ADDR = {0x02, 0x1C}; + static constexpr Avr8EdbgParameter DEVICE_XMEGA_APPLICATION_BYTES = {0x02, 0x20}; + static constexpr Avr8EdbgParameter DEVICE_XMEGA_BOOT_BYTES = {0x02, 0x24}; + static constexpr Avr8EdbgParameter DEVICE_XMEGA_NVM_BASE = {0x02, 0x2B}; + static constexpr Avr8EdbgParameter DEVICE_XMEGA_SIGNATURE_OFFSET = {0x02, 0x2D}; + static constexpr Avr8EdbgParameter DEVICE_XMEGA_FLASH_PAGE_BYTES = {0x02, 0x26}; + static constexpr Avr8EdbgParameter DEVICE_XMEGA_EEPROM_SIZE = {0x02, 0x28}; + static constexpr Avr8EdbgParameter DEVICE_XMEGA_EEPROM_PAGE_SIZE = {0x02, 0x2A}; // UPDI device parameters - static constexpr Avr8EdbgParameter DEVICE_UPDI_PROGMEM_BASE_ADDR {0x02, 0x00}; - static constexpr Avr8EdbgParameter DEVICE_UPDI_FLASH_PAGE_SIZE {0x02, 0x02}; - static constexpr Avr8EdbgParameter DEVICE_UPDI_EEPROM_PAGE_SIZE {0x02, 0x03}; - static constexpr Avr8EdbgParameter DEVICE_UPDI_NVMCTRL_ADDR {0x02, 0x04}; - static constexpr Avr8EdbgParameter DEVICE_UPDI_OCD_ADDR {0x02, 0x06}; - static constexpr Avr8EdbgParameter DEVICE_UPDI_FLASH_SIZE {0x02, 0x12}; - static constexpr Avr8EdbgParameter DEVICE_UPDI_EEPROM_SIZE {0x02, 0x16}; - static constexpr Avr8EdbgParameter DEVICE_UPDI_USER_SIG_SIZE {0x02, 0x18}; - static constexpr Avr8EdbgParameter DEVICE_UPDI_FUSE_SIZE {0x02, 0x1A}; - static constexpr Avr8EdbgParameter DEVICE_UPDI_EEPROM_BASE_ADDR {0x02, 0x20}; - static constexpr Avr8EdbgParameter DEVICE_UPDI_USER_SIG_BASE_ADDR {0x02, 0x22}; - static constexpr Avr8EdbgParameter DEVICE_UPDI_SIG_BASE_ADDR {0x02, 0x24}; - static constexpr Avr8EdbgParameter DEVICE_UPDI_FUSE_BASE_ADDR {0x02, 0x26}; - static constexpr Avr8EdbgParameter DEVICE_UPDI_LOCK_BASE_ADDR {0x02, 0x28}; - static constexpr Avr8EdbgParameter DEVICE_UPDI_DEVICE_ID {0x02, 0x2A}; - static constexpr Avr8EdbgParameter DEVICE_UPDI_PROGMEM_BASE_ADDR_MSB {0x02, 0x2C}; - static constexpr Avr8EdbgParameter DEVICE_UPDI_FLASH_PAGE_SIZE_MSB {0x02, 0x2D}; - static constexpr Avr8EdbgParameter DEVICE_UPDI_24_BIT_ADDRESSING_ENABLE {0x02, 0x2E}; + static constexpr Avr8EdbgParameter DEVICE_UPDI_PROGMEM_BASE_ADDR = {0x02, 0x00}; + static constexpr Avr8EdbgParameter DEVICE_UPDI_FLASH_PAGE_SIZE = {0x02, 0x02}; + static constexpr Avr8EdbgParameter DEVICE_UPDI_EEPROM_PAGE_SIZE = {0x02, 0x03}; + static constexpr Avr8EdbgParameter DEVICE_UPDI_NVMCTRL_ADDR = {0x02, 0x04}; + static constexpr Avr8EdbgParameter DEVICE_UPDI_OCD_ADDR = {0x02, 0x06}; + static constexpr Avr8EdbgParameter DEVICE_UPDI_FLASH_SIZE = {0x02, 0x12}; + static constexpr Avr8EdbgParameter DEVICE_UPDI_EEPROM_SIZE = {0x02, 0x16}; + static constexpr Avr8EdbgParameter DEVICE_UPDI_USER_SIG_SIZE = {0x02, 0x18}; + static constexpr Avr8EdbgParameter DEVICE_UPDI_FUSE_SIZE = {0x02, 0x1A}; + static constexpr Avr8EdbgParameter DEVICE_UPDI_EEPROM_BASE_ADDR = {0x02, 0x20}; + static constexpr Avr8EdbgParameter DEVICE_UPDI_USER_SIG_BASE_ADDR = {0x02, 0x22}; + static constexpr Avr8EdbgParameter DEVICE_UPDI_SIG_BASE_ADDR = {0x02, 0x24}; + static constexpr Avr8EdbgParameter DEVICE_UPDI_FUSE_BASE_ADDR = {0x02, 0x26}; + static constexpr Avr8EdbgParameter DEVICE_UPDI_LOCK_BASE_ADDR = {0x02, 0x28}; + static constexpr Avr8EdbgParameter DEVICE_UPDI_DEVICE_ID = {0x02, 0x2A}; + static constexpr Avr8EdbgParameter DEVICE_UPDI_PROGMEM_BASE_ADDR_MSB = {0x02, 0x2C}; + static constexpr Avr8EdbgParameter DEVICE_UPDI_FLASH_PAGE_SIZE_MSB = {0x02, 0x2D}; + static constexpr Avr8EdbgParameter DEVICE_UPDI_24_BIT_ADDRESSING_ENABLE = {0x02, 0x2E}; - static constexpr Avr8EdbgParameter RUN_TIMERS_WHILST_STOPPED {0x03, 0x00}; - static constexpr Avr8EdbgParameter ENABLE_HIGH_VOLTAGE_UPDI {0x03, 0x06}; + static constexpr Avr8EdbgParameter RUN_TIMERS_WHILST_STOPPED = {0x03, 0x00}; + static constexpr Avr8EdbgParameter ENABLE_HIGH_VOLTAGE_UPDI = {0x03, 0x06}; }; enum class Avr8ConfigVariant: unsigned char @@ -103,16 +102,14 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr DEBUGGING = 0x02, }; - static inline auto getPhysicalInterfaceToAvr8IdMapping() { - using Targets::TargetPhysicalInterface; - - return std::map({ - {TargetPhysicalInterface::DEBUG_WIRE, 0x05}, - {TargetPhysicalInterface::PDI, 0x06}, - {TargetPhysicalInterface::JTAG, 0x04}, - {TargetPhysicalInterface::UPDI, 0x08}, - }); - } + enum class Avr8PhysicalInterface: unsigned char + { + NONE = 0x00, + JTAG = 0x04, + DEBUG_WIRE = 0x05, + PDI = 0x06, + PDI_1W = 0x08, + }; enum class Avr8MemoryType: unsigned char { @@ -148,10 +145,10 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr /** * The FLASH_PAGE memory type can be used to read and write full flash pages on the target. * - * Only available with the JTAG and debugWire config variants. + * Only available with the JTAG and debugWIRE config variants. * * This memory type is not available with the JTAG config variant in debugging mode. Programming mode will need - * to be enabled before it can be used with JTAG targets. With the debugWire variant, this memory type *can* be + * to be enabled before it can be used with JTAG targets. With the debugWIRE variant, this memory type *can* be * used whilst in debugging mode. */ FLASH_PAGE = 0xB0, @@ -171,7 +168,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr /** * The SPM memory type can be used to read memory from the target whilst in debugging mode. * - * Only available with JTAG and debugWire config variants. + * Only available with JTAG and debugWIRE config variants. */ SPM = 0xA0, @@ -186,7 +183,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr /** * The FUSES memory type can be used to read and write AVR fuses in programming mode. * - * Not available for the debugWire config variant. + * Not available for the debugWIRE config variant. */ FUSES = 0xB2, }; diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/AvrCommand.hpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/AvrCommand.hpp index 44901d03..cbf2f03c 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/AvrCommand.hpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/AvrCommand.hpp @@ -12,7 +12,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr /** * AVR CMSIS-DAP vendor command. */ -class AvrCommand: public ::DebugToolDrivers::Protocols::CmsisDap::Command + class AvrCommand: public ::DebugToolDrivers::Protocols::CmsisDap::Command { public: /* diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/AvrEvent.cpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/AvrEvent.cpp index 62972f1e..839d1fa4 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/AvrEvent.cpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/AvrEvent.cpp @@ -12,11 +12,11 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr : Response(rawResponse) { if (this->id != 0x82) { - throw Exception("Failed to construct AvrEvent object - invalid response ID."); + throw Exception{"Failed to construct AvrEvent object - invalid response ID."}; } if (this->data.size() < 7) { - throw Exception("Failed to construct AvrEvent object - unexpected size of AVR_EVT response."); + throw Exception{"Failed to construct AvrEvent object - unexpected size of AVR_EVT response."}; } // Response size is two bytes, MSB @@ -28,17 +28,17 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr } if (this->data.size() < responsePacketSize + 7) { - throw Exception("Failed to construct AvrEvent object - invalid size of AVR_EVT response packet."); + throw Exception{"Failed to construct AvrEvent object - invalid size of AVR_EVT response packet."}; } /* * Ignore the SOF, protocol version, handler ID, sequence ID and size bytes (which all make up 7 bytes * in total). */ - this->eventData = std::vector( + this->eventData = std::vector{ this->data.begin() + 7, this->data.begin() + 7 + static_cast(responsePacketSize) - ); + }; this->eventId = static_cast(this->eventData[0]); } diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/AvrResponse.cpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/AvrResponse.cpp index 7026b8d8..3b0e4d32 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/AvrResponse.cpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/AvrResponse.cpp @@ -10,12 +10,12 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr : Response(rawResponse) { if (this->id != 0x81) { - throw Exception("Failed to construct AvrResponse object - invalid response ID."); + throw Exception{"Failed to construct AvrResponse object - invalid response ID."}; } if (this->data.empty()) { // All AVR responses should contain at least one byte (the fragment info byte) - throw Exception("Failed to construct AvrResponse object - malformed AVR_RSP data"); + throw Exception{"Failed to construct AvrResponse object - malformed AVR_RSP data"}; } if (this->data[0] == 0x00) { diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AVR8Generic/DisableDebugWire.hpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AVR8Generic/DisableDebugWire.hpp index 267a327c..d5f516c2 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AVR8Generic/DisableDebugWire.hpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AVR8Generic/DisableDebugWire.hpp @@ -11,7 +11,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::Avr8 : Avr8GenericCommandFrame() { /* - * The disable debugWire command consists of 2 bytes: + * The disable debugWIRE command consists of 2 bytes: * 1. Command ID (0x17) * 2. Version (0x00) */ diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AVRISP/ProgramFuse.hpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AVRISP/ProgramFuse.hpp index 6182f19f..89fe0d93 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AVRISP/ProgramFuse.hpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AVRISP/ProgramFuse.hpp @@ -4,17 +4,18 @@ #include "AvrIspCommandFrame.hpp" -#include "src/Targets/Microchip/AVR/Fuse.hpp" +#include "src/Targets/Microchip/AVR8/Fuse.hpp" +#include "src/Exceptions/InternalFatalErrorException.hpp" namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrIsp { class ProgramFuse: public AvrIspCommandFrame> { public: - ProgramFuse(Targets::Microchip::Avr::FuseType fuseType, unsigned char value) + ProgramFuse(Targets::Microchip::Avr8::FuseType fuseType, Targets::Microchip::Avr8::FuseValue value) : AvrIspCommandFrame() { - using Targets::Microchip::Avr::FuseType; + using Targets::Microchip::Avr8::FuseType; /* * The program fuse command consists of 5 bytes: @@ -54,7 +55,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrI break; } default: { - throw Exceptions::Exception("Unsupported fuse type"); + throw Exceptions::InternalFatalErrorException{"Unsupported fuse type"}; } } } diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AVRISP/ReadFuse.hpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AVRISP/ReadFuse.hpp index 1471b992..2e1cd7b2 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AVRISP/ReadFuse.hpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AVRISP/ReadFuse.hpp @@ -4,20 +4,18 @@ #include "AvrIspCommandFrame.hpp" -#include "src/Targets/Microchip/AVR/Fuse.hpp" +#include "src/Targets/Microchip/AVR8/Fuse.hpp" +#include "src/Exceptions/InternalFatalErrorException.hpp" namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrIsp { class ReadFuse: public AvrIspCommandFrame> { public: - ReadFuse( - Targets::Microchip::Avr::FuseType fuseType, - std::uint8_t returnAddress - ) + ReadFuse(Targets::Microchip::Avr8::FuseType fuseType, std::uint8_t returnAddress) : AvrIspCommandFrame() { - using Targets::Microchip::Avr::FuseType; + using Targets::Microchip::Avr8::FuseType; /* * The read fuse command consists of 6 bytes: @@ -60,6 +58,9 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrI this->payload[5] = 0x00; break; } + default: { + throw Exceptions::InternalFatalErrorException{"Unsupported fuse type"}; + } } } }; diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AVRISP/ReadSignature.hpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AVRISP/ReadSignature.hpp index f0d4b6e2..96ab0c0b 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AVRISP/ReadSignature.hpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AVRISP/ReadSignature.hpp @@ -1,11 +1,11 @@ #pragma once #include -#include +#include #include "AvrIspCommandFrame.hpp" -#include "src/Targets/Microchip/AVR/Fuse.hpp" +#include "src/Targets/Microchip/AVR8/Fuse.hpp" namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrIsp { @@ -18,7 +18,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::CommandFrames::AvrI ) : AvrIspCommandFrame() { - using Targets::Microchip::Avr::FuseType; + using Targets::Microchip::Avr8::FuseType; /* * The read signature command consists of 6 bytes: diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AvrCommandFrame.hpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AvrCommandFrame.hpp index 0fcf2f26..0e69f941 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AvrCommandFrame.hpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/CommandFrames/AvrCommandFrame.hpp @@ -65,7 +65,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * CommandFrames::Avr8Generic::GetDeviceId getDeviceIdCommandFrame; * * auto responseFrame = edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(getDeviceIdCommandFrame); - * Targets::Microchip::Avr::TargetSignature avrSignature = responseFrame->extractSignature(); + * Targets::Microchip::Avr8::TargetSignature avrSignature = responseFrame->extractSignature(); * * In the code above, the responseFrame object will be an instance of the ResponseFrames::Avr8Generic::GetDeviceId * class, which provides the extractSignature() function (to extract the AVR signature from the response frame). @@ -145,27 +145,30 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * A vector of sequenced AvrCommands, each containing a segment of the AvrCommandFrame. */ [[nodiscard]] std::vector generateAvrCommands(std::size_t maximumCommandPacketSize) const { - auto rawCommandFrame = this->getRawCommandFrame(); + const auto rawCommandFrame = this->getRawCommandFrame(); - std::size_t commandFrameSize = rawCommandFrame.size(); - auto commandsRequired = static_cast( + const auto commandFrameSize = rawCommandFrame.size(); + const auto commandsRequired = static_cast( std::ceil(static_cast(commandFrameSize) / static_cast(maximumCommandPacketSize)) ); - std::vector avrCommands; - std::size_t copiedPacketSize = 0; - for (std::size_t i = 0; i < commandsRequired; i++) { + auto avrCommands = std::vector{}; + auto copiedPacketSize = std::size_t{0}; + for (auto i = std::size_t{0}; i < commandsRequired; ++i) { // If we're on the last packet, the packet size will be what ever is left of the AvrCommandFrame - std::size_t commandPacketSize = ((i + 1) != commandsRequired) ? maximumCommandPacketSize - : (commandFrameSize - (maximumCommandPacketSize * i)); + const auto commandPacketSize = static_cast( + ((i + 1) != commandsRequired) + ? maximumCommandPacketSize + : (commandFrameSize - (maximumCommandPacketSize * i)) + ); avrCommands.emplace_back(AvrCommand( commandsRequired, i + 1, - std::vector( + std::vector{ rawCommandFrame.begin() + static_cast(copiedPacketSize), rawCommandFrame.begin() + static_cast(copiedPacketSize + commandPacketSize) - ) + } )); copiedPacketSize += commandPacketSize; } diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvr8Interface.cpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvr8Interface.cpp index 73a080c3..a983754d 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvr8Interface.cpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvr8Interface.cpp @@ -10,7 +10,7 @@ #include "Exceptions/Avr8CommandFailure.hpp" #include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" -#include "src/Targets/Microchip/AVR/AVR8/Exceptions/DebugWirePhysicalInterfaceError.hpp" +#include "src/Targets/Microchip/AVR8/Exceptions/DebugWirePhysicalInterfaceError.hpp" // Command frames #include "CommandFrames/AVR8Generic/SetParameter.hpp" @@ -48,8 +48,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr { - using namespace Targets::Microchip::Avr; - using namespace Avr8Bit; + using namespace Targets::Microchip::Avr8; using namespace Exceptions; using CommandFrames::Avr8Generic::Stop; @@ -81,23 +80,19 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr using Targets::TargetAddressSpaceDescriptor; using Targets::TargetMemorySegmentType; - using Targets::TargetState; + using Targets::TargetExecutionState; using Targets::TargetPhysicalInterface; - using Targets::TargetMemoryType; using Targets::TargetMemoryBuffer; using Targets::TargetMemoryAddress; using Targets::TargetMemorySize; - using Targets::TargetRegister; using Targets::TargetRegisterDescriptor; using Targets::TargetRegisterDescriptors; - using Targets::TargetRegisterDescriptorId; - using Targets::TargetRegisterDescriptorIds; - using Targets::TargetRegisters; + using Targets::TargetRegisterDescriptorAndValuePairs; EdbgAvr8Interface::EdbgAvr8Interface( EdbgInterface* edbgInterface, - const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile, - const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig + const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile, + const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig ) : edbgInterface(edbgInterface) , session(EdbgAvr8Session(targetDescriptionFile, targetConfig)) @@ -107,20 +102,20 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr if (this->session.configVariant == Avr8ConfigVariant::XMEGA) { // Default PDI clock to 4MHz // TODO: Make this adjustable via a target config parameter - this->setParameter(Avr8EdbgParameters::PDI_CLOCK_SPEED, static_cast(4000)); + this->setParameter(Avr8EdbgParameters::PDI_CLOCK_SPEED, std::uint16_t{4000}); } if (this->session.configVariant == Avr8ConfigVariant::UPDI) { // Default UPDI clock to 1.8MHz - this->setParameter(Avr8EdbgParameters::PDI_CLOCK_SPEED, static_cast(1800)); - this->setParameter(Avr8EdbgParameters::ENABLE_HIGH_VOLTAGE_UPDI, static_cast(0)); + this->setParameter(Avr8EdbgParameters::PDI_CLOCK_SPEED, std::uint16_t{1800}); + this->setParameter(Avr8EdbgParameters::ENABLE_HIGH_VOLTAGE_UPDI, std::uint8_t{0}); } if (this->session.configVariant == Avr8ConfigVariant::MEGAJTAG) { // Default clock value for mega debugging is 200KHz // TODO: Make this adjustable via a target config parameter - this->setParameter(Avr8EdbgParameters::MEGA_DEBUG_CLOCK, static_cast(200)); - this->setParameter(Avr8EdbgParameters::JTAG_DAISY_CHAIN_SETTINGS, static_cast(0)); + this->setParameter(Avr8EdbgParameters::MEGA_DEBUG_CLOCK, std::uint16_t{200}); + this->setParameter(Avr8EdbgParameters::JTAG_DAISY_CHAIN_SETTINGS, std::uint32_t{0}); } this->setParameter( @@ -133,73 +128,67 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr static_cast(Avr8ConfigFunction::DEBUGGING) ); + static const auto avr8PhysicalInterfaceMapping = std::map{ + {TargetPhysicalInterface::DEBUG_WIRE, Avr8PhysicalInterface::DEBUG_WIRE}, + {TargetPhysicalInterface::PDI, Avr8PhysicalInterface::PDI}, + {TargetPhysicalInterface::JTAG, Avr8PhysicalInterface::JTAG}, + {TargetPhysicalInterface::UPDI, Avr8PhysicalInterface::PDI_1W}, + }; + this->setParameter( Avr8EdbgParameters::PHYSICAL_INTERFACE, - getPhysicalInterfaceToAvr8IdMapping().at(this->session.targetConfig.physicalInterface) + static_cast(avr8PhysicalInterfaceMapping.at(this->session.targetConfig.physicalInterface)) ); this->setTargetParameters(); } void EdbgAvr8Interface::stop() { - const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - Stop() - ); - + const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(Stop{}); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Stop target command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Stop target command failed", responseFrame}; } - if (this->getTargetState() == TargetState::RUNNING) { + if (this->getExecutionState() == TargetExecutionState::RUNNING) { this->waitForStoppedEvent(); } } void EdbgAvr8Interface::run() { this->clearEvents(); - const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - Run() - ); + const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(Run{}); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Run command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Run command failed", responseFrame}; } - this->targetState = TargetState::RUNNING; + this->cachedExecutionState = TargetExecutionState::RUNNING; } void EdbgAvr8Interface::runTo(TargetMemoryAddress address) { this->clearEvents(); - const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - RunTo(address) - ); + const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(RunTo{address}); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Run-to command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Run-to command failed", responseFrame}; } - this->targetState = TargetState::RUNNING; + this->cachedExecutionState = TargetExecutionState::RUNNING; } void EdbgAvr8Interface::step() { - const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - Step() - ); - + const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(Step{}); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Step target command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Step target command failed", responseFrame}; } - this->targetState = TargetState::RUNNING; + this->cachedExecutionState = TargetExecutionState::STEPPING; } void EdbgAvr8Interface::reset() { - const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - Reset() - ); - + const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(Reset{}); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Reset target command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Reset target command failed", responseFrame}; } try { @@ -207,7 +196,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr this->waitForStoppedEvent(); } catch (const Exception& exception) { - throw Exception("Failed to reset AVR8 target - missing stopped event."); + throw Exception{"Failed to reset AVR8 target - missing stopped event."}; } /* @@ -217,7 +206,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * All we can really do here is introduce a small delay, to ensure that we're not issuing commands too quickly * after reset. */ - std::this_thread::sleep_for(std::chrono::milliseconds(250)); + std::this_thread::sleep_for(std::chrono::milliseconds{250}); } void EdbgAvr8Interface::activate() { @@ -233,11 +222,12 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr || activationException.code == Avr8CommandFailureCode::FAILED_TO_ENABLE_OCD ) ) { - throw DebugWirePhysicalInterfaceError( - "Failed to activate the debugWire physical interface - check target connection. " - "If the target was recently programmed via ISP, try cycling the target power. See " - + Services::PathService::homeDomainName() + "/docs/debugging-avr-debugwire for more information." - ); + throw DebugWirePhysicalInterfaceError{ + "Failed to activate the debugWIRE physical interface - check target connection. " + "If the target was recently programmed via ISP, try cycling the target power. See " + + Services::PathService::homeDomainName() + "/docs/debugging-avr-debugwire for more " + "information." + }; } throw activationException; @@ -258,13 +248,13 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr try { this->disableDebugWire(); Logger::warning( - "Successfully disabled debugWire on the AVR8 target - this is only temporary - " - "the debugWire module has lost control of the RESET pin. Bloom may no longer be able to " + "Successfully disabled debugWIRE on the AVR8 target - this is only temporary - " + "the debugWIRE module has lost control of the RESET pin. Bloom may no longer be able to " "interface with the target until the next power cycle." ); } catch (const Exception& exception) { - // Failing to disable debugWire should never prevent us from proceeding with target deactivation. + // Failing to disable debugWIRE should never prevent us from proceeding with target deactivation. Logger::error(exception.getMessage()); } } @@ -277,24 +267,42 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr } } + Targets::TargetRegisterAccess EdbgAvr8Interface::getRegisterAccess( + const TargetRegisterDescriptor& registerDescriptor, + const TargetAddressSpaceDescriptor& addressSpaceDescriptor + ) { + /* + * Currently, this implementation of the EDBG AVR8 debug interface can only access registers in the data and + * register file address space (during a debug session). + * + * There are some other memory types we can use to access some other registers during a debug session, but + * these are yet to be implemented. + */ + const auto access = addressSpaceDescriptor.key == this->session.dataAddressSpace.key + || addressSpaceDescriptor.key == this->session.registerFileAddressSpace.key; + + return {access, access}; + } + TargetMemoryAddress EdbgAvr8Interface::getProgramCounter() { - if (this->targetState != TargetState::STOPPED) { + if (this->cachedExecutionState != TargetExecutionState::STOPPED) { this->stop(); } - const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - GetProgramCounter() - ); - + const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(GetProgramCounter{}); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Get program counter command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Get program counter command failed", responseFrame}; } return responseFrame.extractProgramCounter(); } void EdbgAvr8Interface::setProgramCounter(TargetMemoryAddress programCounter) { - if (this->targetState != TargetState::STOPPED) { + if (this->cachedExecutionState != TargetExecutionState::STOPPED) { + /* + * TODO: Review - do we need to do this? Why? The TC shouldn't attempt to set the program counter if the + * target is running. Add a comment + */ this->stop(); } @@ -303,26 +311,22 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * address (16-bit) form. This is why we divide it by 2. */ const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - SetProgramCounter(programCounter / 2) + SetProgramCounter{programCounter / 2} ); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Set program counter command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Set program counter command failed", responseFrame}; } } TargetSignature EdbgAvr8Interface::getDeviceId() { if (this->session.configVariant == Avr8ConfigVariant::UPDI) { /* - * When using the UPDI physical interface, the 'Get device ID' command behaves in an odd manner, where it - * doesn't actually return the target signature, but instead a fixed four byte string reading: - * 'A', 'V', 'R' and ' ' (white space). + * In UPDI sessions, the 'Get ID' command behaves in an odd manner, where it doesn't actually return the + * target signature, but a fixed four byte string reading: 'A', 'V', 'R' and ' ' (white space). * - * So it appears we cannot use that command for UPDI sessions. As an alternative, we will just read the - * signature from memory using the signature base address. - * - * TODO: Currently, we're assuming the signature will always only ever be three bytes in size, but we may - * want to consider pulling the size from the TDF. + * So it appears we cannot use that command for UPDI sessions. For this reason, we will just read the + * signature from memory via the signature memory segment. */ const auto signatureMemory = this->readMemory( Avr8MemoryType::SRAM, @@ -331,18 +335,15 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr ); if (signatureMemory.size() != 3) { - throw Exception("Failed to read AVR8 signature from target - unexpected response size"); + throw Exception{"Failed to read AVR8 signature from target - unexpected response size"}; } return {signatureMemory[0], signatureMemory[1], signatureMemory[2]}; } - const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - GetDeviceId() - ); - + const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(GetDeviceId{}); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Get device ID command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Get device ID command failed", responseFrame}; } return responseFrame.extractSignature(this->session.targetConfig.physicalInterface); @@ -350,27 +351,27 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr void EdbgAvr8Interface::setSoftwareBreakpoint(TargetMemoryAddress address) { const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - SetSoftwareBreakpoints({address}) + SetSoftwareBreakpoints{{address}} ); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Set software breakpoint command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Set software breakpoint command failed", responseFrame}; } } void EdbgAvr8Interface::clearSoftwareBreakpoint(TargetMemoryAddress address) { const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - ClearSoftwareBreakpoints({address}) + ClearSoftwareBreakpoints{{address}} ); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Clear software breakpoint command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Clear software breakpoint command failed", responseFrame}; } } void EdbgAvr8Interface::setHardwareBreakpoint(TargetMemoryAddress address) { static const auto getAvailableBreakpointNumbers = [this] () { - auto breakpointNumbers = std::set({1, 2, 3}); + auto breakpointNumbers = std::set{1, 2, 3}; for (const auto& [address, allocatedNumber] : this->hardwareBreakpointNumbersByAddress) { breakpointNumbers.erase(allocatedNumber); @@ -382,36 +383,35 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr const auto availableBreakpointNumbers = getAvailableBreakpointNumbers(); if (availableBreakpointNumbers.empty()) { - throw Exception("Maximum hardware breakpoints have been allocated"); + throw Exception{"Maximum hardware breakpoints have been allocated"}; } const auto breakpointNumber = *(availableBreakpointNumbers.begin()); const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - SetHardwareBreakpoint(address, breakpointNumber) + SetHardwareBreakpoint{address, breakpointNumber} ); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Set hardware breakpoint command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Set hardware breakpoint command failed", responseFrame}; } - this->hardwareBreakpointNumbersByAddress.insert(std::pair(address, breakpointNumber)); + this->hardwareBreakpointNumbersByAddress.emplace(address, breakpointNumber); } void EdbgAvr8Interface::clearHardwareBreakpoint(TargetMemoryAddress address) { const auto breakpointNumberIt = this->hardwareBreakpointNumbersByAddress.find(address); - if (breakpointNumberIt == this->hardwareBreakpointNumbersByAddress.end()) { Logger::error("No hardware breakpoint at byte address 0x" + Services::StringService::toHex(address)); return; } const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - ClearHardwareBreakpoint(breakpointNumberIt->second) + ClearHardwareBreakpoint{breakpointNumberIt->second} ); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Clear hardware breakpoint command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Clear hardware breakpoint command failed", responseFrame}; } this->hardwareBreakpointNumbersByAddress.erase(address); @@ -419,11 +419,11 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr void EdbgAvr8Interface::clearAllBreakpoints() { const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - ClearAllSoftwareBreakpoints() + ClearAllSoftwareBreakpoints{} ); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Clear all software breakpoints command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Clear all software breakpoints command failed", responseFrame}; } // Clear all hardware breakpoints @@ -432,155 +432,159 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr } } - TargetRegisters EdbgAvr8Interface::readRegisters(const TargetRegisterDescriptorIds& descriptorIds) { + TargetRegisterDescriptorAndValuePairs EdbgAvr8Interface::readRegisters( + const Targets::TargetRegisterDescriptors& descriptors + ) { + using Targets::TargetRegisterType; + using Targets::TargetMemoryAddressRange; + /* * This function needs to be fast. Insight eagerly requests the values of all known registers that it can - * present to the user. It does this on numerous occasions (target stopped, user clicked refresh, etc). This + * present to the user. It does this on numerous occasions (target stopped, user clicked refresh, etc.). This * means we will be frequently loading over 100 register values in a single instance. * * For the above reason, we do not read each register value individually. That would take far too long if we - * have over 100 registers to read. Instead, we group the register descriptors into collections by register - * type, and resolve the address range for each collection. We then perform a single read operation for - * each collection and hold the memory buffer in a random access container (std::vector). Finally, we extract - * the data for each register descriptor, from the memory buffer, and construct the relevant TargetRegister - * object. - * - * TODO: We should be grouping the register descriptors by memory type, as opposed to register type. This - * isn't much of a problem ATM, as currently, we only work with registers that are stored in the data - * address space or the register file. This will need to be addressed before we can work with any other - * registers stored elsewhere. + * have over 100 registers to read. Instead, we calculate the address ranges for each memory type we'll be + * reading from, and then perform a single read operation for each memory type. */ - auto output = TargetRegisters(); - - // Group descriptors by type and resolve the address range for each type - auto descriptorIdsByType = std::map>(); + auto output = TargetRegisterDescriptorAndValuePairs{}; /* - * An address range is just an std::pair of addresses - the first being the start address, the second being - * the end address. + * AVR registers can be accessed via one of two memory types, depending on the register type and config + * variant. * - * TODO: Can't we just use the TargetMemoryAddressRange struct here? Review + * For debugWIRE and JTAG config variants, all registers (in the data address space) can be accessed via the + * SRAM memory type. This includes general purpose registers. + * + * For PDI and UPDI config variants, all registers *EXCEPT* general purpose registers can be accessed via the + * SRAM memory type. General purpose registers can only be accessed via the REGISTER_FILE memory type. */ - using AddressRange = std::pair; - auto addressRangeByType = std::map(); + auto sramAddressRange = std::optional{}; + auto registerFileAddressRange = std::optional{}; - for (const auto& descriptorId : descriptorIds) { - const auto descriptorIt = this->targetRegisterDescriptorsById.find(descriptorId); - assert(descriptorIt != this->targetRegisterDescriptorsById.end()); + auto sramRegisterDescriptors = TargetRegisterDescriptors{}; + auto registerFileRegisterDescriptors = TargetRegisterDescriptors{}; - const auto& descriptor = descriptorIt->second; + for (const auto& descriptor : descriptors) { + const auto regMemoryType = this->getRegisterMemoryType(*descriptor); + const auto endAddress = descriptor->startAddress + (descriptor->size - 1); - if (!descriptor.startAddress.has_value()) { - Logger::debug( - "Attempted to read register in the absence of a start address - register name: " - + descriptor.name.value_or("unknown") - ); - continue; + if (regMemoryType == Avr8MemoryType::REGISTER_FILE) { + // This register can only be accessed via the REGISTER_FILE memory type + if (registerFileAddressRange.has_value()) { + if (descriptor->startAddress < registerFileAddressRange->startAddress) { + registerFileAddressRange->startAddress = descriptor->startAddress; + } + + if (endAddress > registerFileAddressRange->endAddress) { + registerFileAddressRange->endAddress = endAddress; + } + + } else { + registerFileAddressRange = TargetMemoryAddressRange( + descriptor->startAddress, + descriptor->startAddress + (descriptor->size - 1) + ); + } + + registerFileRegisterDescriptors.push_back(descriptor); } - descriptorIdsByType[descriptor.type].insert(descriptor.id); + if (regMemoryType == Avr8MemoryType::SRAM) { + // This register can be accessed via the SRAM memory type + if (sramAddressRange.has_value()) { + if (descriptor->startAddress < sramAddressRange->startAddress) { + sramAddressRange->startAddress = descriptor->startAddress; + } - const auto startAddress = descriptor.startAddress.value(); - const auto endAddress = startAddress + (descriptor.size - 1); + if (endAddress > sramAddressRange->endAddress) { + sramAddressRange->endAddress = endAddress; + } - const auto addressRangeIt = addressRangeByType.find(descriptor.type); - - if (addressRangeIt == addressRangeByType.end()) { - addressRangeByType[descriptor.type] = AddressRange(startAddress, endAddress); - - } else { - auto& addressRange = addressRangeIt->second; - - if (startAddress < addressRange.first) { - addressRange.first = startAddress; + } else { + sramAddressRange = TargetMemoryAddressRange( + descriptor->startAddress, + descriptor->startAddress + (descriptor->size - 1) + ); } - if (endAddress > addressRange.second) { - addressRange.second = endAddress; - } + sramRegisterDescriptors.push_back(descriptor); } } - /* - * Now that we have our address ranges and grouped descriptors, we can perform a single read call for each - * register type. - */ - for (const auto&[registerType, descriptorIds] : descriptorIdsByType) { - const auto& addressRange = addressRangeByType[registerType]; - const auto startAddress = addressRange.first; - const auto endAddress = addressRange.second; - const auto readSize = (endAddress - startAddress) + 1; - - const auto memoryType = (registerType != TargetRegisterType::GENERAL_PURPOSE_REGISTER) - ? Avr8MemoryType::SRAM - : (this->session.configVariant == Avr8ConfigVariant::XMEGA || this->session.configVariant == Avr8ConfigVariant::UPDI - ? Avr8MemoryType::REGISTER_FILE - : Avr8MemoryType::SRAM); + if (sramAddressRange.has_value()) { + const auto readSize = (sramAddressRange->endAddress - sramAddressRange->startAddress) + 1; /* - * When reading the entire range, we must avoid any attempts to access the OCD data register (OCDDR), as - * the debug tool will reject the command and respond with a 0x36 error code (invalid address error). + * When reading from SRAM, we must avoid any attempts to access the OCD data register (OCDDR), as the + * debug tool will reject the command and respond with a 0x36 error code (invalid address error). * * For this reason, we specify the OCDDR address as an excluded address. This will mean - * the EdbgAvr8Interface::readMemory() function will employ the masked read memory command, as opposed to - * the general read memory command. The masked read memory command allows us to specify which addresses to - * read and which ones to ignore. For ignored addresses, the debug tool will just return a 0x00 byte. - * For more info, see section 7.1.22 titled 'Memory Read Masked', in the EDBG protocol document. - * - * Interestingly, the masked read memory command doesn't seem to require us to explicitly specify the OCDDR - * address as an excluded address. It seems to exclude the OCDDR automatically, even if we've not - * instructed it to do so. This is plausible, as we send the OCDDR address to the debug tool during target - * initialisation (see EdbgAvr8Interface::setDebugWireAndJtagParameters()). So this means we don't have to - * specify the OCDDR address as an excluded address, but the EdbgAvr8Interface::readMemory() function will - * only employ the masked read memory command when we supply at least one excluded address. For this reason, - * we still pass the OCDDR address to EdbgAvr8Interface::readMemory(), as an excluded address (provided we - * have it). - * - * See CommandFrames::Avr8Generic::ReadMemory(); and the Microchip EDBG documentation for more. + * the EdbgAvr8Interface::readMemory() function will avoid reading from that particular address. */ - auto excludedAddresses = std::set(); - if (memoryType == Avr8MemoryType::SRAM && this->targetParameters.ocdDataRegister.has_value()) { - excludedAddresses.insert( - this->targetParameters.ocdDataRegister.value() - + this->targetParameters.mappedIoSegmentStartAddress.value_or(0) - ); + auto excludedAddresses = std::set{}; + if (this->session.ocdDataRegister.has_value()) { + excludedAddresses.insert(*(this->session.ocdDataRegister) + this->session.ioMemorySegment.startAddress); } const auto flatMemoryData = this->readMemory( - memoryType, - startAddress, + Avr8MemoryType::SRAM, + sramAddressRange->startAddress, readSize, excludedAddresses ); if (flatMemoryData.size() != readSize) { - throw Exception( - "Failed to read memory within register type address range (" + std::to_string(startAddress) - + " - " + std::to_string(endAddress) + "). Expected " + std::to_string(readSize) + throw Exception{ + "Failed to read memory via SRAM memory type - address range: " + + std::to_string(sramAddressRange->startAddress) + " - " + + std::to_string(sramAddressRange->endAddress) + ". Expected " + std::to_string(readSize) + " bytes, got " + std::to_string(flatMemoryData.size()) - ); + }; } - // Construct our TargetRegister objects directly from the flat memory buffer - for (const auto descriptorId : descriptorIds) { - const auto descriptorIt = this->targetRegisterDescriptorsById.find(descriptorId); - const auto& descriptor = descriptorIt->second; - + for (const auto& descriptor : sramRegisterDescriptors) { /* * Multibyte AVR8 registers are stored in LSB form. * - * This is why we use reverse iterators when extracting our data from flatMemoryData. Doing so allows - * us to extract the data in MSB form (as is expected for all register values held in TargetRegister - * objects). + * We use reverse iterators here to extract the data in MSB form. */ - const auto bufferStartIt = flatMemoryData.rend() - (descriptor.startAddress.value() - startAddress) - - descriptor.size; + const auto bufferStartIt = flatMemoryData.rend() + - (descriptor->startAddress - sramAddressRange->startAddress) - descriptor->size; output.emplace_back( - TargetRegister( - descriptor.id, - TargetMemoryBuffer(bufferStartIt, bufferStartIt + descriptor.size) - ) + *descriptor, + TargetMemoryBuffer{bufferStartIt, bufferStartIt + descriptor->size} + ); + } + } + + if (registerFileAddressRange.has_value()) { + const auto readSize = (registerFileAddressRange->endAddress - registerFileAddressRange->startAddress) + 1; + + const auto flatMemoryData = this->readMemory( + Avr8MemoryType::REGISTER_FILE, + registerFileAddressRange->startAddress, + readSize, + {} + ); + + if (flatMemoryData.size() != readSize) { + throw Exception{ + "Failed to read memory via REGISTER_FILE memory type - address range: " + + std::to_string(registerFileAddressRange->startAddress) + " - " + + std::to_string(registerFileAddressRange->endAddress) + ". Expected " + + std::to_string(readSize) + " bytes, got " + std::to_string(flatMemoryData.size()) + }; + } + + for (const auto& descriptor : registerFileRegisterDescriptors) { + const auto bufferStartIt = flatMemoryData.rend() + - (descriptor->startAddress - registerFileAddressRange->startAddress) - descriptor->size; + + output.emplace_back( + *descriptor, + TargetMemoryBuffer{bufferStartIt, bufferStartIt + descriptor->size} ); } } @@ -588,117 +592,46 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr return output; } - void EdbgAvr8Interface::writeRegisters(const Targets::TargetRegisters& registers) { - for (const auto& reg : registers) { - const auto& registerDescriptorIt = this->targetRegisterDescriptorsById.find(reg.descriptorId); - assert(registerDescriptorIt != this->targetRegisterDescriptorsById.end()); + void EdbgAvr8Interface::writeRegisters( + const TargetRegisterDescriptorAndValuePairs& registers + ) { + using Targets::TargetRegisterType; - const auto& registerDescriptor = registerDescriptorIt->second; - auto registerValue = reg.value; + for (const auto& [descriptor, value] : registers) { + auto valueCopy = value; - if (registerValue.empty()) { - throw Exception("Cannot write empty register value"); + if (valueCopy.empty()) { + throw Exception{"Cannot write empty register value"}; } - if (registerValue.size() > registerDescriptor.size) { - throw Exception("Register value exceeds size specified by register descriptor."); + if (valueCopy.size() > descriptor.size) { + throw Exception{"Register value exceeds size specified by register descriptor."}; } - if (registerValue.size() < registerDescriptor.size) { + if (valueCopy.size() < descriptor.size) { // Fill the missing most-significant bytes with 0x00 - registerValue.insert(registerValue.begin(), registerDescriptor.size - registerValue.size(), 0x00); + valueCopy.insert(valueCopy.begin(), descriptor.size - valueCopy.size(), 0x00); } - if (registerValue.size() > 1) { + if (valueCopy.size() > 1) { // AVR8 registers are stored in LSB - std::reverse(registerValue.begin(), registerValue.end()); - } - - auto memoryType = Avr8MemoryType::SRAM; - if ( - registerDescriptor.type == TargetRegisterType::GENERAL_PURPOSE_REGISTER - && (this->session.configVariant == Avr8ConfigVariant::XMEGA || this->session.configVariant == Avr8ConfigVariant::UPDI) - ) { - memoryType = Avr8MemoryType::REGISTER_FILE; + std::reverse(valueCopy.begin(), valueCopy.end()); } // TODO: This can be inefficient when updating many registers, maybe do something a little smarter here. - this->writeMemory( - memoryType, - registerDescriptor.startAddress.value(), - registerValue - ); + this->writeMemory(this->getRegisterMemoryType(descriptor), descriptor.startAddress, valueCopy); } } TargetMemoryBuffer EdbgAvr8Interface::readMemory( - TargetMemoryType memoryType, + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, TargetMemoryAddress startAddress, TargetMemorySize bytes, const std::set& excludedAddressRanges ) { - if (this->programmingModeEnabled && memoryType == TargetMemoryType::RAM) { - throw Exception("Cannot access RAM when programming mode is enabled"); - } - - auto avr8MemoryType = Avr8MemoryType::SRAM; - - switch (memoryType) { - case TargetMemoryType::RAM: { - avr8MemoryType = Avr8MemoryType::SRAM; - break; - } - case TargetMemoryType::FLASH: { - if ( - this->configVariant == Avr8ConfigVariant::DEBUG_WIRE - || this->configVariant == Avr8ConfigVariant::UPDI - ) { - avr8MemoryType = Avr8MemoryType::FLASH_PAGE; - - } else if (this->configVariant == Avr8ConfigVariant::MEGAJTAG) { - avr8MemoryType = this->programmingModeEnabled ? Avr8MemoryType::FLASH_PAGE : Avr8MemoryType::SPM; - - } else if (this->configVariant == Avr8ConfigVariant::XMEGA) { - const auto bootSectionStartAddress = this->targetParameters.bootSectionStartAddress.value(); - if (startAddress >= bootSectionStartAddress) { - avr8MemoryType = Avr8MemoryType::BOOT_FLASH; - - /* - * When using the BOOT_FLASH memory type, the address should be relative to the start of the - * boot section. - */ - startAddress -= bootSectionStartAddress; - - } else { - /* - * When using the APPL_FLASH memory type, the address should be relative to the start of the - * application section. - */ - startAddress -= this->targetParameters.appSectionStartAddress.value(); - avr8MemoryType = Avr8MemoryType::APPL_FLASH; - } - } - break; - } - case TargetMemoryType::EEPROM: { - // For JTAG targets, we must use the EEPROM_PAGE memory type when in programming mode. - avr8MemoryType = (this->configVariant == Avr8ConfigVariant::MEGAJTAG && this->programmingModeEnabled) - ? Avr8MemoryType::EEPROM_PAGE - : Avr8MemoryType::EEPROM; - - if (this->configVariant == Avr8ConfigVariant::XMEGA) { - // EEPROM addresses should be in relative form, for XMEGA (PDI) targets - startAddress -= this->targetParameters.eepromStartAddress.value(); - } - break; - } - case TargetMemoryType::FUSES: { - avr8MemoryType = Avr8MemoryType::FUSES; - break; - } - default: { - break; - } + if (this->programmingModeEnabled && memorySegmentDescriptor.type == TargetMemorySegmentType::RAM) { + throw Exception{"Cannot access RAM when programming mode is enabled"}; } /* @@ -707,156 +640,198 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * * We will perform the conversion here. */ - auto excludedAddresses = std::set(); - auto endAddress = startAddress + bytes - 1; + auto excludedAddresses = std::set{}; + const auto endAddress = startAddress + bytes - 1; for (const auto& addressRange : excludedAddressRanges) { if (addressRange.startAddress > endAddress) { - // This address range is outside of the range from which we will be reading + // This address range is outside the range from which we will be reading continue; } - for (auto i = addressRange.startAddress; i <= addressRange.endAddress; i++) { + for (auto i = addressRange.startAddress; i <= addressRange.endAddress; ++i) { excludedAddresses.insert(i); } } - return this->readMemory(avr8MemoryType, startAddress, bytes, excludedAddresses); + if (memorySegmentDescriptor.type == TargetMemorySegmentType::FLASH) { + if (this->session.configVariant == Avr8ConfigVariant::MEGAJTAG) { + return this->readMemory( + this->programmingModeEnabled ? Avr8MemoryType::FLASH_PAGE : Avr8MemoryType::SPM, + startAddress, + bytes, + excludedAddresses + ); + } + + if (this->session.configVariant == Avr8ConfigVariant::XMEGA) { + const auto bootSectionStartAddress = this->session.programBootSection.value().get().startAddress; + if (startAddress >= bootSectionStartAddress) { + /* + * When using the BOOT_FLASH memory type, the address should be relative to the start of the + * boot section. + */ + return this->readMemory( + Avr8MemoryType::BOOT_FLASH, + startAddress - bootSectionStartAddress, + bytes, + excludedAddresses + ); + } + + /* + * When using the APPL_FLASH memory type, the address should be relative to the start of the + * application section. + */ + return this->readMemory( + Avr8MemoryType::APPL_FLASH, + startAddress - this->session.programAppSection.value().get().startAddress, + bytes, + excludedAddresses + ); + } + + return this->readMemory(Avr8MemoryType::FLASH_PAGE, startAddress, bytes, excludedAddresses); + } + + if (memorySegmentDescriptor.type == TargetMemorySegmentType::EEPROM) { + if (this->session.configVariant == Avr8ConfigVariant::MEGAJTAG) { + return this->readMemory( + this->programmingModeEnabled ? Avr8MemoryType::EEPROM_PAGE : Avr8MemoryType::EEPROM, + startAddress, + bytes, + excludedAddresses + ); + } + + if (this->session.configVariant == Avr8ConfigVariant::XMEGA) { + // EEPROM addresses should be in relative form, for XMEGA (PDI) targets + return this->readMemory( + Avr8MemoryType::EEPROM, + startAddress - this->session.eepromMemorySegment.startAddress, + bytes, + excludedAddresses + ); + } + + return this->readMemory(Avr8MemoryType::EEPROM, startAddress, bytes, excludedAddresses); + } + + if (memorySegmentDescriptor.type == TargetMemorySegmentType::FUSES) { + return this->readMemory(Avr8MemoryType::FUSES, startAddress, bytes, excludedAddresses); + } + + return this->readMemory(Avr8MemoryType::SRAM, startAddress, bytes, excludedAddresses); } void EdbgAvr8Interface::writeMemory( - TargetMemoryType memoryType, + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, TargetMemoryAddress startAddress, const TargetMemoryBuffer& buffer ) { - auto avr8MemoryType = Avr8MemoryType::SRAM; + if (memorySegmentDescriptor.type == TargetMemorySegmentType::FLASH) { + if (this->session.configVariant == Avr8ConfigVariant::XMEGA) { + const auto bootSectionStartAddress = this->session.programBootSection.value().get().startAddress; + if (startAddress >= bootSectionStartAddress) { + /* + * When using the BOOT_FLASH memory type, the address should be relative to the start of the + * boot section. + */ + return this->writeMemory( + Avr8MemoryType::BOOT_FLASH, + startAddress - bootSectionStartAddress, + buffer + ); - switch (memoryType) { - case TargetMemoryType::RAM: { - avr8MemoryType = Avr8MemoryType::SRAM; - break; - } - case TargetMemoryType::FLASH: { - if ( - this->session.configVariant == Avr8ConfigVariant::DEBUG_WIRE - || this->session.configVariant == Avr8ConfigVariant::UPDI - || this->session.configVariant == Avr8ConfigVariant::MEGAJTAG - ) { - avr8MemoryType = Avr8MemoryType::FLASH_PAGE; - - } else if (this->session.configVariant == Avr8ConfigVariant::XMEGA) { - const auto bootSectionStartAddress = this->session.programBootSection.value().get().startAddress; - if (startAddress >= bootSectionStartAddress) { - avr8MemoryType = Avr8MemoryType::BOOT_FLASH; - - /* - * When using the BOOT_FLASH memory type, the address should be relative to the start of the - * boot section. - */ - startAddress -= bootSectionStartAddress; - - } else { - /* - * When using the APPL_FLASH memory type, the address should be relative to the start of the - * application section. - */ - startAddress -= this->session.programAppSection.value().get().startAddress; - avr8MemoryType = Avr8MemoryType::APPL_FLASH; - } - } - break; - } - case TargetMemoryType::EEPROM: { - switch (this->session.configVariant) { - case Avr8ConfigVariant::UPDI: - case Avr8ConfigVariant::XMEGA: { - avr8MemoryType = Avr8MemoryType::EEPROM_ATOMIC; - - if (this->session.configVariant == Avr8ConfigVariant::XMEGA) { - // EEPROM addresses should be in relative form, for XMEGA (PDI) targets - startAddress -= this->session.eepromMemorySegment.startAddress; - } - - break; - } - case Avr8ConfigVariant::MEGAJTAG: { - avr8MemoryType = this->programmingModeEnabled - ? Avr8MemoryType::EEPROM_PAGE - : Avr8MemoryType::EEPROM; - break; - } - default: { - avr8MemoryType = Avr8MemoryType::EEPROM; - break; - } - } - break; - } - case TargetMemoryType::FUSES: { - avr8MemoryType = Avr8MemoryType::FUSES; - break; - } - default: { - break; - } - } - - return this->writeMemory(avr8MemoryType, startAddress, buffer); - } - - void EdbgAvr8Interface::eraseProgramMemory(std::optional section) { - if (this->session.configVariant == Avr8ConfigVariant::DEBUG_WIRE) { - // The EDBG erase command does not work on debugWire targets - we'll just write to the memory instead - return this->writeMemory( - TargetMemoryType::FLASH, - this->session.programMemorySegment.startAddress, - TargetMemoryBuffer(this->session.programMemorySegment.size, 0xFF) - ); - } - - if (this->session.configVariant == Avr8ConfigVariant::XMEGA) { - // For PDI (XMEGA) targets, we can erase flash memory without erasing EEPROM - - if (!section.has_value() || *section == Avr8Bit::ProgramMemorySection::BOOT) { - const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - EraseMemory(Avr8EraseMemoryMode::BOOT_SECTION) - ); - - if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 erase memory command (for BOOT section) failed", responseFrame); - } - } - - if (!section.has_value() || *section == Avr8Bit::ProgramMemorySection::APPLICATION) { - const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - EraseMemory(Avr8EraseMemoryMode::APPLICATION_SECTION) - ); - - if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure( - "AVR8 erase memory command (for APPLICATION section) failed", - responseFrame + } else { + /* + * When using the APPL_FLASH memory type, the address should be relative to the start of the + * application section. + */ + return this->writeMemory( + Avr8MemoryType::APPL_FLASH, + startAddress - this->session.programAppSection.value().get().startAddress, + buffer ); } + + return this->writeMemory(Avr8MemoryType::FLASH_PAGE, startAddress, buffer); } - return; + return this->writeMemory(Avr8MemoryType::FLASH_PAGE, startAddress, buffer); } - throw Exception("JTAG and UPDI targets do not support program memory erase."); + if (memorySegmentDescriptor.type == TargetMemorySegmentType::EEPROM) { + if (this->session.configVariant == Avr8ConfigVariant::MEGAJTAG) { + return this->writeMemory( + this->programmingModeEnabled ? Avr8MemoryType::EEPROM_PAGE : Avr8MemoryType::EEPROM, + startAddress, + buffer + ); + } + + if (this->session.configVariant == Avr8ConfigVariant::XMEGA) { + // EEPROM addresses should be in relative form, for XMEGA (PDI) targets + return this->writeMemory( + Avr8MemoryType::EEPROM_ATOMIC, + startAddress - this->session.eepromMemorySegment.startAddress, + buffer + ); + } + + if (this->session.configVariant == Avr8ConfigVariant::UPDI) { + return this->writeMemory(Avr8MemoryType::EEPROM_ATOMIC, startAddress, buffer); + } + + return this->writeMemory(Avr8MemoryType::EEPROM, startAddress, buffer); + } + + if (memorySegmentDescriptor.type == TargetMemorySegmentType::FUSES) { + return this->writeMemory(Avr8MemoryType::FUSES, startAddress, buffer); + } + + return this->writeMemory(Avr8MemoryType::SRAM, startAddress, buffer); + } + + void EdbgAvr8Interface::eraseProgramMemory(std::optional section) { + // The EDBG erase command with a specified "mode" parameter is only supported by XMEGA targets + if (this->session.configVariant != Avr8ConfigVariant::XMEGA) { + throw Exception{"debugWIRE, JTAG and UPDI targets do not support EDBG program memory erase command"}; + } + + if (!section.has_value() || *section == ProgramMemorySection::BOOT) { + const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( + EraseMemory{Avr8EraseMemoryMode::BOOT_SECTION} + ); + + if (responseFrame.id == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure{"AVR8 erase memory command (for BOOT section) failed", responseFrame}; + } + } + + if (!section.has_value() || *section == ProgramMemorySection::APPLICATION) { + const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( + EraseMemory{Avr8EraseMemoryMode::APPLICATION_SECTION} + ); + + if (responseFrame.id == Avr8ResponseId::FAILED) { + throw Avr8CommandFailure{"AVR8 erase memory command (for APPLICATION section) failed", responseFrame}; + } + } } void EdbgAvr8Interface::eraseChip() { const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - EraseMemory(Avr8EraseMemoryMode::CHIP) + EraseMemory{Avr8EraseMemoryMode::CHIP} ); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 erase memory command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 erase memory command failed", responseFrame}; } } - TargetState EdbgAvr8Interface::getTargetState() { + TargetExecutionState EdbgAvr8Interface::getExecutionState() { /* * We are not informed when a target goes from a stopped state to a running state, so there is no need * to query the tool when we already know the target has stopped. @@ -864,11 +839,11 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * This means we have to rely on the assumption that the target cannot enter a running state without * our instruction. */ - if (this->targetState != TargetState::STOPPED) { + if (this->cachedExecutionState != TargetExecutionState::STOPPED) { this->refreshTargetState(); } - return this->targetState; + return this->cachedExecutionState; } void EdbgAvr8Interface::enableProgrammingMode() { @@ -877,11 +852,11 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr } const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - EnterProgrammingMode() + EnterProgrammingMode{} ); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("Failed to enter programming mode on EDBG debug tool", responseFrame); + throw Avr8CommandFailure{"Failed to enter programming mode on EDBG debug tool", responseFrame}; } this->programmingModeEnabled = true; @@ -894,11 +869,11 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr } const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - LeaveProgrammingMode() + LeaveProgrammingMode{} ); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("Failed to leave programming mode on EDBG debug tool", responseFrame); + throw Avr8CommandFailure{"Failed to leave programming mode on EDBG debug tool", responseFrame}; } this->programmingModeEnabled = false; @@ -935,7 +910,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr using Services::StringService; const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - SetParameter(parameter, value) + SetParameter{parameter, value} ); Logger::debug( @@ -944,24 +919,24 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr ); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("Failed to set parameter on device!", responseFrame); + throw Avr8CommandFailure{"Failed to set parameter on device!", responseFrame}; } } std::vector EdbgAvr8Interface::getParameter(const Avr8EdbgParameter& parameter, std::uint8_t size) { const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - GetParameter(parameter, size) + GetParameter{parameter, size} ); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("Failed to get parameter from device!", responseFrame); + throw Avr8CommandFailure{"Failed to get parameter from device!", responseFrame}; } return responseFrame.getPayloadData(); } void EdbgAvr8Interface::setDebugWireAndJtagParameters() { - const auto parameters = Parameters::Avr8Generic::DebugWireJtagParameters(this->session.targetDescriptionFile); + const auto parameters = Parameters::Avr8Generic::DebugWireJtagParameters{this->session.targetDescriptionFile}; Logger::debug("Setting FLASH_PAGE_SIZE AVR8 device parameter"); this->setParameter(Avr8EdbgParameters::DEVICE_FLASH_PAGE_SIZE, parameters.flashPageSize); @@ -1012,7 +987,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr } void EdbgAvr8Interface::setPdiParameters() { - const auto parameters = Parameters::Avr8Generic::PdiParameters(this->session.targetDescriptionFile); + const auto parameters = Parameters::Avr8Generic::PdiParameters{this->session.targetDescriptionFile}; Logger::debug("Setting APPL_BASE_ADDR AVR8 parameter"); this->setParameter(Avr8EdbgParameters::DEVICE_XMEGA_APPL_BASE_ADDR, parameters.appSectionPdiOffset); @@ -1061,11 +1036,11 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr } void EdbgAvr8Interface::setUpdiParameters() { - const auto parameters = Parameters::Avr8Generic::UpdiParameters(this->session.targetDescriptionFile); + const auto parameters = Parameters::Avr8Generic::UpdiParameters{this->session.targetDescriptionFile}; /* * The program memory base address field for UPDI sessions (DEVICE_UPDI_PROGMEM_BASE_ADDR) seems to be - * limited to two bytes in size, as opposed to the four byte size for the debugWire, JTAG and PDI + * limited to two bytes in size, as opposed to the four byte size for the debugWIRE, JTAG and PDI * equivalent fields. This is why, I suspect, another field was required for the most significant byte of * the program memory base address (DEVICE_UPDI_PROGMEM_BASE_ADDR_MSB). * @@ -1087,11 +1062,12 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr static_cast(parameters.programMemoryStartAddress >> 16) ); + Logger::debug("Setting UPDI_24_BIT_ADDRESSING_ENABLE AVR8 device parameter"); this->setParameter( Avr8EdbgParameters::DEVICE_UPDI_24_BIT_ADDRESSING_ENABLE, parameters.programMemoryStartAddress > 0xFFFF - ? static_cast(1) - : static_cast(0) + ? std::uint8_t{1} + : std::uint8_t{0} ); /* @@ -1144,18 +1120,19 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr void EdbgAvr8Interface::activatePhysical(bool applyExternalReset) { const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - ActivatePhysical(applyExternalReset) + ActivatePhysical{applyExternalReset} ); if (responseFrame.id == Avr8ResponseId::FAILED) { if (!applyExternalReset) { // Try again with external reset applied - Logger::debug("Failed to activate physical interface on AVR8 target " - "- retrying with external reset applied."); + Logger::debug( + "Failed to activate physical interface on AVR8 target - retrying with external reset applied." + ); return this->activatePhysical(true); } - throw Avr8CommandFailure("AVR8 Activate physical interface command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Activate physical interface command failed", responseFrame}; } this->physicalInterfaceActivated = true; @@ -1163,11 +1140,11 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr void EdbgAvr8Interface::deactivatePhysical() { const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - DeactivatePhysical() + DeactivatePhysical{} ); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Deactivate physical interface command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Deactivate physical interface command failed", responseFrame}; } this->physicalInterfaceActivated = false; @@ -1175,21 +1152,19 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr void EdbgAvr8Interface::attach() { /* - * When attaching an ATmega target that is connected via JTAG, we must not set the breakAfterAttach flag, as - * this results in a timeout. + * When attaching an ATmega JTAG target, we must not set the breakAfterAttach flag, as this results in a + * timeout. * - * However, in this case the attach command seems to _sometimes_ halt the target anyway, regardless of the + * However, in this case the 'attach' command seems to _sometimes_ halt the target anyway, regardless of the * value of the breakAfterAttach flag. So we still expect a stop event to be received shortly after issuing - * the attach command. + * the 'attach' command. */ const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - Attach( - this->session.configVariant != Avr8ConfigVariant::MEGAJTAG - ) + Attach{this->session.configVariant != Avr8ConfigVariant::MEGAJTAG} ); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Attach command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Attach command failed", responseFrame}; } this->targetAttached = true; @@ -1199,19 +1174,14 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr this->waitForStoppedEvent(); } catch (const Exception& exception) { - Logger::warning( - "Execution on AVR8 target could not be halted post attach - " + exception.getMessage() - ); + Logger::warning("Execution on AVR8 target could not be halted post attach - " + exception.getMessage()); } } void EdbgAvr8Interface::detach() { - const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - Detach() - ); - + const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(Detach{}); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Detach command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Detach command failed", responseFrame}; } this->targetAttached = false; @@ -1231,7 +1201,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr } default: { /* - * TODO: This isn't very nice as we're performing an unnecessary copy. Maybe requestAvrEvents should + * TODO: This isn't very nice as we're performing an unnecessary copy. Maybe requestAvrEvent should * return a unique_ptr instead? */ return std::make_unique(event.value()); @@ -1243,6 +1213,16 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr while (this->getAvrEvent() != nullptr) {} } + Avr8MemoryType EdbgAvr8Interface::getRegisterMemoryType(const TargetRegisterDescriptor& descriptor) { + return ( + descriptor.type == Targets::TargetRegisterType::GENERAL_PURPOSE_REGISTER + && ( + this->session.configVariant == Avr8ConfigVariant::XMEGA + || this->session.configVariant == Avr8ConfigVariant::UPDI + ) + ) ? Avr8MemoryType::REGISTER_FILE : Avr8MemoryType::SRAM; + } + bool EdbgAvr8Interface::alignmentRequired(Avr8MemoryType memoryType) { return memoryType == Avr8MemoryType::FLASH_PAGE @@ -1254,11 +1234,8 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr ; } - TargetMemoryAddress EdbgAvr8Interface::alignMemoryAddress( - Avr8MemoryType memoryType, - TargetMemoryAddress address - ) { - std::uint16_t alignTo = 1; + TargetMemoryAddress EdbgAvr8Interface::alignMemoryAddress(Avr8MemoryType memoryType, TargetMemoryAddress address) { + auto alignTo = std::uint16_t{1}; switch (memoryType) { case Avr8MemoryType::FLASH_PAGE: @@ -1295,11 +1272,8 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr return address; } - TargetMemorySize EdbgAvr8Interface::alignMemoryBytes( - Avr8MemoryType memoryType, - TargetMemorySize bytes - ) { - std::uint16_t alignTo = 1; + TargetMemorySize EdbgAvr8Interface::alignMemoryBytes(Avr8MemoryType memoryType, TargetMemorySize bytes) { + auto alignTo = std::uint16_t{1}; switch (memoryType) { case Avr8MemoryType::FLASH_PAGE: @@ -1329,7 +1303,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr return bytes; } - std::optional EdbgAvr8Interface::maximumMemoryAccessSize(Avr8MemoryType memoryType) { + TargetMemorySize EdbgAvr8Interface::maximumMemoryAccessSize(Avr8MemoryType memoryType) { if ( memoryType == Avr8MemoryType::FLASH_PAGE || memoryType == Avr8MemoryType::APPL_FLASH @@ -1340,17 +1314,14 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr return this->session.programMemorySegment.pageSize.value(); } - if ( - memoryType == Avr8MemoryType::EEPROM_ATOMIC - || memoryType == Avr8MemoryType::EEPROM_PAGE - ) { + if (memoryType == Avr8MemoryType::EEPROM_ATOMIC || memoryType == Avr8MemoryType::EEPROM_PAGE) { // These EEPROM memory types requires single page access. return this->session.eepromMemorySegment.pageSize.value(); } if (this->maximumMemoryAccessSizePerRequest.has_value()) { // There is a memory access size limit for this entire EdbgAvr8Interface instance - return this->maximumMemoryAccessSizePerRequest; + return *(this->maximumMemoryAccessSizePerRequest); } /* @@ -1362,9 +1333,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * * The -30 is to accommodate for the bytes in the command that are not part of the main payload of the command. */ - return static_cast( - (this->edbgInterface->getUsbHidInputReportSize() - 30) * 2 - ); + return static_cast((this->edbgInterface->getUsbHidInputReportSize() - 30) * 2); } TargetMemoryBuffer EdbgAvr8Interface::readMemory( @@ -1373,10 +1342,8 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr TargetMemorySize bytes, const std::set& excludedAddresses ) { - if (type == Avr8MemoryType::FUSES) { - if (this->session.configVariant == Avr8ConfigVariant::DEBUG_WIRE) { - throw Exception("Cannot access AVR fuses via the debugWire interface"); - } + if (type == Avr8MemoryType::FUSES && this->session.configVariant == Avr8ConfigVariant::DEBUG_WIRE) { + throw Exception{"Cannot access AVR fuses via the debugWIRE interface"}; } const auto managingProgrammingMode = type == Avr8MemoryType::FUSES && !this->programmingModeEnabled; @@ -1393,7 +1360,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * All values for bytes located at excluded addresses will be returned as 0x00 - this mirrors the behaviour * of the masked read memory EDBG command. */ - auto output = TargetMemoryBuffer(); + auto output = TargetMemoryBuffer{}; output.reserve(bytes); auto segmentStartAddress = startAddress; @@ -1401,38 +1368,31 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr for (const auto excludedAddress : excludedAddresses) { if (excludedAddress < startAddress || excludedAddress > endAddress) { - // This excluded address is outside of the range from which we are reading, so it can be ignored. + // This excluded address is outside the range from which we are reading, so it can be ignored. continue; } const auto segmentSize = excludedAddress - segmentStartAddress; if (segmentSize > 0) { - auto segmentBuffer = this->readMemory( - type, - segmentStartAddress, - segmentSize - ); - + auto segmentBuffer = this->readMemory(type, segmentStartAddress, segmentSize); std::move(segmentBuffer.begin(), segmentBuffer.end(), std::back_inserter(output)); } output.emplace_back(0x00); - segmentStartAddress = excludedAddress + 1; } // Read final segment const auto finalReadBytes = (endAddress - segmentStartAddress) + 1; if (finalReadBytes > 0) { - auto segmentBuffer = this->readMemory( - type, - segmentStartAddress, - finalReadBytes - ); - + auto segmentBuffer = this->readMemory(type, segmentStartAddress, finalReadBytes); std::move(segmentBuffer.begin(), segmentBuffer.end(), std::back_inserter(output)); } + if (managingProgrammingMode) { + this->disableProgrammingMode(); + } + return output; } @@ -1444,7 +1404,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr auto memoryBuffer = this->readMemory(type, alignedStartAddress, alignedBytes, excludedAddresses); const auto offset = memoryBuffer.begin() + (startAddress - alignedStartAddress); - auto output = TargetMemoryBuffer(); + auto output = TargetMemoryBuffer{}; output.reserve(bytes); std::move(offset, offset + bytes, std::back_inserter(output)); @@ -1453,14 +1413,14 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr } const auto maximumReadSize = this->maximumMemoryAccessSize(type); - if (maximumReadSize.has_value() && bytes > *maximumReadSize) { - auto output = Targets::TargetMemoryBuffer(); + if (bytes > maximumReadSize) { + auto output = TargetMemoryBuffer{}; output.reserve(bytes); while (output.size() < bytes) { const auto bytesToRead = std::min( static_cast(bytes - output.size()), - *maximumReadSize + maximumReadSize ); auto data = this->readMemory( @@ -1476,12 +1436,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr } const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - ReadMemory( - type, - startAddress, - bytes, - excludedAddresses - ) + ReadMemory{type, startAddress, bytes, excludedAddresses} ); if (managingProgrammingMode) { @@ -1489,13 +1444,13 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr } if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Read memory command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Read memory command failed", responseFrame}; } const auto data = responseFrame.getMemoryData(); if (data.size() != bytes) { - throw Avr8CommandFailure("Unexpected number of bytes returned from EDBG debug tool"); + throw Avr8CommandFailure{"Unexpected number of bytes returned from EDBG debug tool"}; } return data; @@ -1506,10 +1461,8 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr TargetMemoryAddress startAddress, const TargetMemoryBuffer& buffer ) { - if (type == Avr8MemoryType::FUSES) { - if (this->session.configVariant == Avr8ConfigVariant::DEBUG_WIRE) { - throw Exception("Cannot access AVR fuses via the debugWire interface"); - } + if (type == Avr8MemoryType::FUSES && this->session.configVariant == Avr8ConfigVariant::DEBUG_WIRE) { + throw Exception{"Cannot access AVR fuses via the debugWIRE interface"}; } const auto managingProgrammingMode = type == Avr8MemoryType::FUSES && !this->programmingModeEnabled; @@ -1527,12 +1480,12 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr /* * We can't just forward the memory type to readMemory(), because some memory types (such as * EEPROM_ATOMIC) can only be used for writing. - * - * This nasty hack will have to do for now. */ - const auto readMemoryType = type == Avr8MemoryType::EEPROM_ATOMIC ? Avr8MemoryType::EEPROM : type; - - auto alignedBuffer = this->readMemory(readMemoryType, alignedStartAddress, alignedBytes); + auto alignedBuffer = this->readMemory( + type == Avr8MemoryType::EEPROM_ATOMIC ? Avr8MemoryType::EEPROM : type, + alignedStartAddress, + alignedBytes + ); assert(alignedBuffer.size() >= buffer.size()); const auto offset = alignedBuffer.begin() + (startAddress - alignedStartAddress); @@ -1543,22 +1496,19 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr } const auto maximumWriteSize = this->maximumMemoryAccessSize(type); - if (maximumWriteSize.has_value() && buffer.size() > *maximumWriteSize) { - auto bytesWritten = TargetMemorySize(0); + if (buffer.size() > maximumWriteSize) { + auto bytesWritten = TargetMemorySize{0}; while (bytesWritten < buffer.size()) { const auto chunkSize = std::min( static_cast(buffer.size() - bytesWritten), - *maximumWriteSize + maximumWriteSize ); this->writeMemory( type, startAddress + bytesWritten, - TargetMemoryBuffer( - buffer.begin() + bytesWritten, - buffer.begin() + bytesWritten + chunkSize - ) + TargetMemoryBuffer{buffer.begin() + bytesWritten, buffer.begin() + bytesWritten + chunkSize} ); bytesWritten += chunkSize; @@ -1568,14 +1518,9 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr } const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - WriteMemory( - type, - startAddress, - buffer - ) + WriteMemory{type, startAddress, buffer} ); - // We must disable and re-enable programming mode, in order for the changes to the fuse bit to take effect. if (type == Avr8MemoryType::FUSES) { this->disableProgrammingMode(); @@ -1586,7 +1531,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr } if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Write memory command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Write memory command failed", responseFrame}; } } @@ -1597,33 +1542,31 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr auto* breakEvent = dynamic_cast(avrEvent.get()); if (breakEvent == nullptr) { - throw Exception("Failed to process AVR8 break event"); + throw Exception{"Failed to process AVR8 break event"}; } - this->targetState = TargetState::STOPPED; + this->cachedExecutionState = TargetExecutionState::STOPPED; return; } - this->targetState = TargetState::RUNNING; + if (this->cachedExecutionState != TargetExecutionState::STEPPING) { + this->cachedExecutionState = TargetExecutionState::RUNNING; + } } void EdbgAvr8Interface::disableDebugWire() { - const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - DisableDebugWire() - ); - + const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame(DisableDebugWire{}); if (responseFrame.id == Avr8ResponseId::FAILED) { - throw Avr8CommandFailure("AVR8 Disable debugWire command failed", responseFrame); + throw Avr8CommandFailure{"AVR8 Disable debugWIRE command failed", responseFrame}; } } void EdbgAvr8Interface::waitForStoppedEvent() { auto breakEvent = this->waitForAvrEvent(); - if (breakEvent == nullptr) { - throw Exception("Failed to receive break event for AVR8 target"); + throw Exception{"Failed to receive break event for AVR8 target"}; } - this->targetState = TargetState::STOPPED; + this->cachedExecutionState = TargetExecutionState::STOPPED; } } diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvr8Interface.hpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvr8Interface.hpp index d994b427..f2618083 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvr8Interface.hpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvr8Interface.hpp @@ -6,7 +6,7 @@ #include #include -#include "src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AVR8/Avr8DebugInterface.hpp" +#include "src/DebugToolDrivers/TargetInterfaces/Microchip/AVR8/Avr8DebugInterface.hpp" #include "src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgInterface.hpp" #include "Avr8Generic.hpp" @@ -15,10 +15,8 @@ #include "src/Targets/TargetPhysicalInterface.hpp" #include "src/Targets/TargetMemory.hpp" #include "src/Targets/TargetRegisterDescriptor.hpp" -#include "src/Targets/TargetRegister.hpp" -#include "src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.hpp" -#include "src/Targets/Microchip/AVR/AVR8/Family.hpp" -#include "src/Targets/Microchip/AVR/AVR8/TargetParameters.hpp" +#include "src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp" +#include "src/Targets/Microchip/AVR8/Family.hpp" namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr { @@ -31,13 +29,13 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * This implementation should work with any Microchip EDBG-based CMSIS-DAP debug tool (such as the Atmel-ICE, * Power Debugger, the MPLAB SNAP debugger (in "AVR mode"), etc). */ - class EdbgAvr8Interface: public ::DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8::Avr8DebugInterface + class EdbgAvr8Interface: public ::DebugToolDrivers::TargetInterfaces::Microchip::Avr8::Avr8DebugInterface { public: explicit EdbgAvr8Interface( EdbgInterface* edbgInterface, - const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile, - const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig + const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile, + const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig ); /** @@ -140,6 +138,11 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr */ void deactivate() override; + Targets::TargetRegisterAccess getRegisterAccess( + const Targets::TargetRegisterDescriptor& registerDescriptor, + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor + ) override; + /** * Issues the "PC Read" command to the debug tool, to extract the current program counter. * @@ -160,7 +163,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * * @return */ - Targets::Microchip::Avr::TargetSignature getDeviceId() override; + Targets::Microchip::Avr8::TargetSignature getDeviceId() override; /** * Issues the "Software Breakpoint Set" command to the debug tool, setting a software breakpoint at the given @@ -212,27 +215,31 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * @param descriptorIds * @return */ - Targets::TargetRegisters readRegisters(const Targets::TargetRegisterDescriptorIds& descriptorIds) override; + Targets::TargetRegisterDescriptorAndValuePairs readRegisters( + const Targets::TargetRegisterDescriptors& descriptors + ) override; /** * Writes registers to target. * * @param registers */ - void writeRegisters(const Targets::TargetRegisters& registers) override; + void writeRegisters(const Targets::TargetRegisterDescriptorAndValuePairs& registers) override; /** * This is an overloaded method. * * Resolves the correct Avr8MemoryType from the given TargetMemoryType and calls readMemory(). * - * @param memoryType + * @param addressSpaceDescriptor + * @param memorySegmentDescriptor * @param startAddress * @param bytes * @return */ Targets::TargetMemoryBuffer readMemory( - Targets::TargetMemoryType memoryType, + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, Targets::TargetMemoryAddress startAddress, Targets::TargetMemorySize bytes, const std::set& excludedAddressRanges = {} @@ -243,12 +250,14 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * * Resolves the correct Avr8MemoryType from the given TargetMemoryType and calls writeMemory(). * - * @param memoryType + * @param addressSpaceDescriptor + * @param memorySegmentDescriptor * @param startAddress * @param buffer */ void writeMemory( - Targets::TargetMemoryType memoryType, + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, Targets::TargetMemoryAddress startAddress, const Targets::TargetMemoryBuffer& buffer ) override; @@ -259,7 +268,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * @param section */ void eraseProgramMemory( - std::optional section = std::nullopt + std::optional section = std::nullopt ) override; /** @@ -272,7 +281,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * * @return */ - Targets::TargetState getTargetState() override; + Targets::TargetExecutionState getExecutionState() override; /** * Enters programming mode on the EDBG debug tool. @@ -311,13 +320,14 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr bool reactivateJtagTargetPostProgrammingMode = false; /** - * We keep record of the current target state for caching purposes. We'll only refresh the target state if the - * target is running. If it has already stopped, then we assume it cannot transition to a running state without - * an instruction from us. + * We keep record of the current target execution state for caching purposes. + * + * We'll only refresh the target state if the target is running. If it has already stopped, then we assume it + * cannot transition to a running state without an instruction from us. * * @TODO: Review this. Is the above assumption correct? Always? Explore the option of polling the target state. */ - Targets::TargetState targetState = Targets::TargetState::UNKNOWN; + Targets::TargetExecutionState cachedExecutionState = Targets::TargetExecutionState::UNKNOWN; /** * Upon configuration, the physical interface must be activated on the debug tool. We keep record of this to @@ -411,7 +421,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * What parameters we need to send depend on the physical interface (and config variant) selected by the user. * For target parameters, the address (ID) of the parameter also varies across config variants. * - * - The setDebugWireAndJtagParameters() function sends the required target parameters for debugWire and JTAG + * - The setDebugWireAndJtagParameters() function sends the required target parameters for debugWIRE and JTAG * sessions. Both sessions are covered in a single function because they require the same parameters. * - The setPdiParameters() function sends the required target parameters for PDI sessions. * - The setUpdiParameters() function sends the required target parameters for UPDI sessions. @@ -459,6 +469,8 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr */ void clearEvents(); + Avr8MemoryType getRegisterMemoryType(const Targets::TargetRegisterDescriptor& descriptor); + /** * Checks if alignment is required for memory access via a given Avr8MemoryType. * @@ -486,14 +498,13 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr Targets::TargetMemorySize alignMemoryBytes(Avr8MemoryType memoryType, Targets::TargetMemorySize bytes); /** - * Checks if a maximum memory access size is imposed for a given Avr8MemoryType. + * Determines the maximum memory access size imposed on the given Avr8MemoryType. * * @param memoryType - * The imposed maximum size, or std::nullopt if a maximum isn't required. * * @return */ - std::optional maximumMemoryAccessSize(Avr8MemoryType memoryType); + Targets::TargetMemorySize maximumMemoryAccessSize(Avr8MemoryType memoryType); /** * Reads memory on the target. @@ -546,7 +557,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr void refreshTargetState(); /** - * Temporarily disables the debugWire module on the target. This does not affect the DWEN fuse. The module + * Temporarily disables the debugWIRE module on the target. This does not affect the DWEN fuse. The module * will be reactivated upon the cycling of the target power. */ void disableDebugWire(); @@ -580,7 +591,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr } } - std::this_thread::sleep_for(std::chrono::milliseconds(50)); + std::this_thread::sleep_for(std::chrono::milliseconds{50}); attemptCount++; } diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvr8Session.cpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvr8Session.cpp index a86c7eb7..f6a4c310 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvr8Session.cpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvr8Session.cpp @@ -7,13 +7,14 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr { EdbgAvr8Session::EdbgAvr8Session( - const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile, - const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig + const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile, + const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig ) : targetDescriptionFile(targetDescriptionFile) , targetConfig(targetConfig) , programAddressSpace(this->targetDescriptionFile.getProgramAddressSpace()) - , ramAddressSpace(this->targetDescriptionFile.getRamAddressSpace()) + , registerFileAddressSpace(this->targetDescriptionFile.getRegisterFileAddressSpace()) + , dataAddressSpace(this->targetDescriptionFile.getDataAddressSpace()) , eepromAddressSpace(this->targetDescriptionFile.getEepromAddressSpace()) , ioAddressSpace(this->targetDescriptionFile.getIoAddressSpace()) , signatureAddressSpace(this->targetDescriptionFile.getSignatureAddressSpace()) @@ -38,20 +39,20 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr ); if (!resolvedConfigVariant.has_value()) { - throw Exceptions::Exception( + throw Exceptions::Exception{ "Failed to resolve EDBG config variant from the selected physical interface and the AVR target family" " - please review the selected physical interface" - ); + }; } this->configVariant = *resolvedConfigVariant; } std::optional EdbgAvr8Session::tryResolveConfigVariant( - Targets::Microchip::Avr::Avr8Bit::Family avrFamily, + Targets::Microchip::Avr8::Family avrFamily, Targets::TargetPhysicalInterface physicalInterface ) { - using Targets::Microchip::Avr::Avr8Bit::Family; + using Targets::Microchip::Avr8::Family; using Targets::TargetPhysicalInterface; if (avrFamily == Family::MEGA || avrFamily == Family::TINY) { diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvr8Session.hpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvr8Session.hpp index e6a1b5bc..65375e52 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvr8Session.hpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvr8Session.hpp @@ -4,8 +4,8 @@ #include #include -#include "src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.hpp" -#include "src/Targets/Microchip/AVR/AVR8/Avr8TargetConfig.hpp" +#include "src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp" +#include "src/Targets/Microchip/AVR8/Avr8TargetConfig.hpp" #include "src/Targets/TargetMemorySegmentDescriptor.hpp" #include "src/Targets/TargetRegisterDescriptor.hpp" @@ -21,12 +21,12 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr /** * AVR8 TDF, from which we extract all target info to configure the EDBG debug tool. */ - const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile; + const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile; /** * Project's AVR8 target configuration. */ - const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig; + const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig; /** * The EDBG config variant parameter. @@ -37,7 +37,8 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr Avr8ConfigVariant configVariant = Avr8ConfigVariant::NONE; const Targets::TargetDescription::AddressSpace& programAddressSpace; - const Targets::TargetDescription::AddressSpace& ramAddressSpace; + const Targets::TargetDescription::AddressSpace& registerFileAddressSpace; + const Targets::TargetDescription::AddressSpace& dataAddressSpace; const Targets::TargetDescription::AddressSpace& eepromAddressSpace; const Targets::TargetDescription::AddressSpace& ioAddressSpace; const Targets::TargetDescription::AddressSpace& signatureAddressSpace; @@ -59,8 +60,8 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr std::optional ocdDataRegister; EdbgAvr8Session( - const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile, - const Targets::Microchip::Avr::Avr8Bit::Avr8TargetConfig& targetConfig + const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile, + const Targets::Microchip::Avr8::Avr8TargetConfig& targetConfig ); private: @@ -75,7 +76,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * any particular EDBG config variant. */ static std::optional tryResolveConfigVariant( - Targets::Microchip::Avr::Avr8Bit::Family avrFamily, + Targets::Microchip::Avr8::Family avrFamily, Targets::TargetPhysicalInterface physicalInterface ); }; diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvrIspInterface.cpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvrIspInterface.cpp index 064a355b..721a9ec9 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvrIspInterface.cpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvrIspInterface.cpp @@ -1,6 +1,7 @@ #include "EdbgAvrIspInterface.hpp" #include "src/TargetController/Exceptions/TargetOperationFailure.hpp" +#include "src/Exceptions/InternalFatalErrorException.hpp" #include "src/Logger/Logger.hpp" // Command frames @@ -13,7 +14,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr { - using namespace Targets::Microchip::Avr; + using namespace Targets::Microchip::Avr8; using CommandFrames::AvrIsp::EnterProgrammingMode; using CommandFrames::AvrIsp::LeaveProgrammingMode; @@ -26,17 +27,17 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr using Exceptions::TargetOperationFailure; - EdbgAvrIspInterface::EdbgAvrIspInterface(EdbgInterface* edbgInterface) + EdbgAvrIspInterface::EdbgAvrIspInterface( + EdbgInterface* edbgInterface, + const TargetDescriptionFile& targetDescriptionFile + ) : edbgInterface(edbgInterface) + , ispParameters(IspParameters{targetDescriptionFile}) {} - void EdbgAvrIspInterface::setIspParameters(const Targets::Microchip::Avr::IspParameters& ispParameters) { - this->ispParameters = ispParameters; - } - void EdbgAvrIspInterface::activate() { const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - EnterProgrammingMode( + EnterProgrammingMode{ this->ispParameters.programModeTimeout, this->ispParameters.programModeStabilizationDelay, this->ispParameters.programModeCommandExecutionDelay, @@ -44,42 +45,35 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr this->ispParameters.programModeByteDelay, this->ispParameters.programModePollValue, this->ispParameters.programModePollIndex - ) + } ); if (responseFrame.statusCode != StatusCode::OK) { - throw TargetOperationFailure( + throw TargetOperationFailure{ "Failed to enable programming mode via the ISP interface - check target's SPI connection " "and/or its SPIEN fuse bit." - ); + }; } } void EdbgAvrIspInterface::deactivate() { const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - LeaveProgrammingMode( - this->ispParameters.programModePreDelay, - this->ispParameters.programModePostDelay - ) + LeaveProgrammingMode{this->ispParameters.programModePreDelay, this->ispParameters.programModePostDelay} ); if (responseFrame.statusCode != StatusCode::OK) { - throw TargetOperationFailure("Failed to disable programming mode via the ISP interface."); + throw TargetOperationFailure{"Failed to disable programming mode via the ISP interface."}; } } TargetSignature EdbgAvrIspInterface::getDeviceId() { // The read signature command only allows us to read one signature byte at a time. - return TargetSignature( - this->readSignatureByte(0), - this->readSignatureByte(1), - this->readSignatureByte(2) - ); + return {this->readSignatureByte(0), this->readSignatureByte(1), this->readSignatureByte(2)}; } - Fuse EdbgAvrIspInterface::readFuse(FuseType fuseType) { + FuseValue EdbgAvrIspInterface::readFuse(const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor) { const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - ReadFuse(fuseType, this->ispParameters.readFusePollIndex) + ReadFuse{this->resolveFuseType(fuseRegisterDescriptor), this->ispParameters.readFusePollIndex} ); if ( @@ -87,17 +81,17 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr || responseFrame.payload.size() < 4 || static_cast(responseFrame.payload[3]) != StatusCode::OK ) { - throw TargetOperationFailure( + throw TargetOperationFailure{ "Failed to read fuse via ISP - response frame status code/size indicates a failure." - ); + }; } - return Fuse(fuseType, responseFrame.payload[2]); + return static_cast(responseFrame.payload[2]); } unsigned char EdbgAvrIspInterface::readLockBitByte() { const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - ReadLock(this->ispParameters.readLockPollIndex) + ReadLock{this->ispParameters.readLockPollIndex} ); if ( @@ -105,29 +99,32 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr || responseFrame.payload.size() < 4 || static_cast(responseFrame.payload[3]) != StatusCode::OK ) { - throw TargetOperationFailure( + throw TargetOperationFailure{ "Failed to read lock bit byte via ISP - response frame status code/size indicates a failure." - ); + }; } return responseFrame.payload[2]; } - void EdbgAvrIspInterface::programFuse(Targets::Microchip::Avr::Fuse fuse) { + void EdbgAvrIspInterface::programFuse( + const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor, + FuseValue value + ) { const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - ProgramFuse(fuse.type, fuse.value) + ProgramFuse{this->resolveFuseType(fuseRegisterDescriptor), value} ); if (responseFrame.statusCode != StatusCode::OK || responseFrame.payload.size() < 2) { - throw TargetOperationFailure( + throw TargetOperationFailure{ "Failed to program fuse via ISP - response frame status code/size indicates a failure." - ); + }; } } unsigned char EdbgAvrIspInterface::readSignatureByte(std::uint8_t signatureByteAddress) { const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - ReadSignature(signatureByteAddress, this->ispParameters.readSignaturePollIndex) + ReadSignature{signatureByteAddress, this->ispParameters.readSignaturePollIndex} ); if ( @@ -135,12 +132,30 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr || responseFrame.payload.size() < 4 || static_cast(responseFrame.payload[3]) != StatusCode::OK ) { - throw TargetOperationFailure( + throw TargetOperationFailure{ "Failed to read signature byte (address: " + std::to_string(signatureByteAddress) + ") via ISP - response frame status code/size indicates a failure." - ); + }; } return responseFrame.payload[2]; } + + FuseType EdbgAvrIspInterface::resolveFuseType(const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor) { + if (fuseRegisterDescriptor.key == "low") { + return FuseType::LOW; + } + + if (fuseRegisterDescriptor.key == "high") { + return FuseType::HIGH; + } + + if (fuseRegisterDescriptor.key == "extended") { + return FuseType::EXTENDED; + } + + throw Exceptions::InternalFatalErrorException{ + "Could not resolve fuse type from register descriptor (key: \"" + fuseRegisterDescriptor.key + "\")" + }; + } } diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvrIspInterface.hpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvrIspInterface.hpp index da612820..69fff7f2 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvrIspInterface.hpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/EdbgAvrIspInterface.hpp @@ -5,8 +5,9 @@ #include #include -#include "src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AvrIspInterface.hpp" +#include "src/DebugToolDrivers/TargetInterfaces/Microchip/AVR8/AvrIspInterface.hpp" #include "src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgInterface.hpp" +#include "src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp" namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr { @@ -19,25 +20,13 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * This implementation should work with any Microchip EDBG-based CMSIS-DAP debug tool with ISP support (such as * the Atmel-ICE, Power Debugger, the MPLAB SNAP debugger (in "AVR mode"), etc). */ - class EdbgAvrIspInterface: public TargetInterfaces::Microchip::Avr::AvrIspInterface + class EdbgAvrIspInterface: public TargetInterfaces::Microchip::Avr8::AvrIspInterface { public: - explicit EdbgAvrIspInterface(EdbgInterface* edbgInterface); - - /** - * The EdbgAvrIspInterface doesn't actually require any config from the user, at this point in time. So this - * function does nothing, for now. - * - * @param targetConfig - */ - void configure(const TargetConfig& targetConfig) override {}; - - /** - * Accepts the target's ISP parameters. These should be extracted from the target's TDF. - * - * @param ispParameters - */ - void setIspParameters(const Targets::Microchip::Avr::IspParameters& ispParameters) override; + explicit EdbgAvrIspInterface( + EdbgInterface* edbgInterface, + const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile + ); /** * Initialises the ISP interface by enabling "programming mode" on the debug tool. This will activate the @@ -56,15 +45,17 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * * @return */ - Targets::Microchip::Avr::TargetSignature getDeviceId() override; + Targets::Microchip::Avr8::TargetSignature getDeviceId() override; /** * Reads a particular fuse byte from the AVR target. * - * @param fuseType + * @param fuseRegisterDescriptor * @return */ - Targets::Microchip::Avr::Fuse readFuse(Targets::Microchip::Avr::FuseType fuseType) override; + Targets::Microchip::Avr8::FuseValue readFuse( + const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor + ) override; /** * Reads the lock bit byte from the AVR target. @@ -76,9 +67,13 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr /** * Programs a particular fuse on the AVR target. * - * @param fuse + * @param fuseRegisterDescriptor + * @param value */ - void programFuse(Targets::Microchip::Avr::Fuse fuse) override; + void programFuse( + const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor, + Targets::Microchip::Avr8::FuseValue value + ) override; private: /** @@ -89,7 +84,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr */ EdbgInterface* edbgInterface; - Targets::Microchip::Avr::IspParameters ispParameters; + Targets::Microchip::Avr8::IspParameters ispParameters; /** * The EDBG AVRISP protocol only allows us to read a single signature byte at a time. @@ -100,5 +95,9 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * @return */ [[nodiscard]] unsigned char readSignatureByte(std::uint8_t signatureByteAddress); + + Targets::Microchip::Avr8::FuseType resolveFuseType( + const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor + ); }; } diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Events/AVR8Generic/BreakEvent.cpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Events/AVR8Generic/BreakEvent.cpp index 0f29eefa..ea7b17c4 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Events/AVR8Generic/BreakEvent.cpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Events/AVR8Generic/BreakEvent.cpp @@ -19,7 +19,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * 1 byte for break cause * 2 bytes for extended info */ - throw Exception("Failed to process BreakEvent from AvrEvent - unexpected packet size."); + throw Exception{"Failed to process BreakEvent from AvrEvent - unexpected packet size."}; } // Program counter consists of 4 bytes diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Exceptions/Avr8CommandFailure.hpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Exceptions/Avr8CommandFailure.hpp index a126b68a..d72c14bc 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Exceptions/Avr8CommandFailure.hpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Exceptions/Avr8CommandFailure.hpp @@ -68,7 +68,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr } explicit Avr8CommandFailure(const char* message): TargetOperationFailure(message) { - this->message = std::string(message); + this->message = std::string{message}; } explicit Avr8CommandFailure( @@ -95,7 +95,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr } private: - static const inline auto failureCodeToDescription = std::map({ + static const inline auto failureCodeToDescription = std::map{ {Avr8CommandFailureCode::DEBUGWIRE_PHYSICAL_ERROR, "debugWIRE physical error"}, {Avr8CommandFailureCode::JTAGM_FAILED_TO_INITIALISE, "JTAGM failed to initialise"}, {Avr8CommandFailureCode::UNKNOWN_JTAG_ERROR, "JTAGM did something strange"}, @@ -143,6 +143,6 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr {Avr8CommandFailureCode::FEATURE_NOT_AVAILABLE, "Feature not available"}, {Avr8CommandFailureCode::UNKNOWN_COMMAND, "Command has not been implemented"}, {Avr8CommandFailureCode::UNKNOWN_ERROR, "Unknown error reported by EDBG device"}, - }); + }; }; } diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/DebugWireJtagParameters.cpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/DebugWireJtagParameters.cpp index 1d7d1dcd..31096c11 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/DebugWireJtagParameters.cpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/DebugWireJtagParameters.cpp @@ -1,13 +1,12 @@ #include "DebugWireJtagParameters.hpp" #include "src/Services/StringService.hpp" - #include "src/Exceptions/InternalFatalErrorException.hpp" namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Generic { DebugWireJtagParameters::DebugWireJtagParameters( - const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile + const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile ) { using Services::StringService; @@ -55,7 +54,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Gen this->eearAddressLow = static_cast(eearlDescriptor.startAddress); /* - * Some debugWire targets only have a single-byte `EEARL` register. + * Some debugWIRE targets only have a single-byte `EEARL` register. * * In the absence of an `EEARH` register, and if there is no high byte in the `EEARL` register, the * `eearAddressHigh` parameter should be equal to the `eearAddressLow` parameter, as stated in the @@ -80,8 +79,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Gen const auto cpuPeripheralDescriptor = targetDescriptionFile.getTargetPeripheralDescriptor("cpu"); const auto& cpuRegisterGroupDescriptor = cpuPeripheralDescriptor.getRegisterGroupDescriptor("cpu"); - const auto spmcsrDescriptor = cpuRegisterGroupDescriptor.tryGetRegisterDescriptor("spmcsr") - ?: cpuRegisterGroupDescriptor.tryGetRegisterDescriptor("spmcr"); + const auto spmcsrDescriptor = cpuRegisterGroupDescriptor.tryGetFirstRegisterDescriptor({"spmcsr", "spmcr"}); if (spmcsrDescriptor.has_value()) { this->spmcrAddress = static_cast(spmcsrDescriptor->get().startAddress); @@ -90,24 +88,27 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Gen const auto bootLoadPeripheral = targetDescriptionFile.getTargetPeripheralDescriptor("boot_load"); const auto& bootLoaderRegisterGroupDescriptor = bootLoadPeripheral.getRegisterGroupDescriptor("boot_load"); - const auto spmcsrDescriptor = bootLoaderRegisterGroupDescriptor.tryGetRegisterDescriptor("spmcsr") - ?: bootLoaderRegisterGroupDescriptor.tryGetRegisterDescriptor("spmcr"); + const auto spmcsrDescriptor = bootLoaderRegisterGroupDescriptor.tryGetFirstRegisterDescriptor( + {"spmcsr", "spmcr"} + ); if (!spmcsrDescriptor.has_value()) { - throw Exceptions::InternalFatalErrorException("Could not extract SPMCS register from TDF"); + throw Exceptions::InternalFatalErrorException{"Could not extract SPMCS register from TDF"}; } this->spmcrAddress = static_cast(spmcsrDescriptor->get().startAddress); } - const auto osccalDescriptor = cpuRegisterGroupDescriptor.tryGetRegisterDescriptor("osccal") - ?: cpuRegisterGroupDescriptor.tryGetRegisterDescriptor("osccal0") - ?: cpuRegisterGroupDescriptor.tryGetRegisterDescriptor("osccal1") - ?: cpuRegisterGroupDescriptor.tryGetRegisterDescriptor("fosccal") - ?: cpuRegisterGroupDescriptor.tryGetRegisterDescriptor("sosccala"); + const auto osccalDescriptor = cpuRegisterGroupDescriptor.tryGetFirstRegisterDescriptor({ + "osccal", + "osccal0", + "osccal1", + "fosccal", + "sosccala", + }); if (!osccalDescriptor.has_value()) { - throw Exceptions::InternalFatalErrorException("Could not extract OSCCAL register from TDF"); + throw Exceptions::InternalFatalErrorException{"Could not extract OSCCAL register from TDF"}; } this->osccalAddress = static_cast(osccalDescriptor->get().startAddress); diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/DebugWireJtagParameters.hpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/DebugWireJtagParameters.hpp index 1c39239a..46ef4c85 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/DebugWireJtagParameters.hpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/DebugWireJtagParameters.hpp @@ -3,12 +3,12 @@ #include #include -#include "src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.hpp" +#include "src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp" namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Generic { /** - * EDBG parameters for debugWire and JTAG AVR targets. + * EDBG parameters for debugWIRE and JTAG AVR targets. * * See Microchip's "EDBG-based Tools Protocols" document for more on these parameters. */ @@ -30,6 +30,6 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Gen std::uint8_t spmcrAddress; std::uint8_t osccalAddress; - DebugWireJtagParameters(const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile); + DebugWireJtagParameters(const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile); }; } diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/PdiParameters.cpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/PdiParameters.cpp index daffdcb7..f14afb44 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/PdiParameters.cpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/PdiParameters.cpp @@ -4,9 +4,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Generic { - PdiParameters::PdiParameters( - const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile - ) { + PdiParameters::PdiParameters(const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile) { using Services::StringService; const auto& pdiGroup = targetDescriptionFile.getPropertyGroup("pdi_interface"); diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/PdiParameters.hpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/PdiParameters.hpp index 23379fd5..bc1b3eda 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/PdiParameters.hpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/PdiParameters.hpp @@ -2,7 +2,7 @@ #include -#include "src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.hpp" +#include "src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp" namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Generic { @@ -29,6 +29,6 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Gen std::uint16_t nvmModuleBaseAddress; std::uint16_t signaturesPdiOffset; - PdiParameters(const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile); + PdiParameters(const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile); }; } diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/UpdiParameters.cpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/UpdiParameters.cpp index 68d863d4..bc2c1b15 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/UpdiParameters.cpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/UpdiParameters.cpp @@ -4,9 +4,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Generic { - UpdiParameters::UpdiParameters( - const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile - ) { + UpdiParameters::UpdiParameters(const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile) { using Services::StringService; const auto& updiGroup = targetDescriptionFile.getPropertyGroup("updi_interface"); diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/UpdiParameters.hpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/UpdiParameters.hpp index bac99bce..0698045a 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/UpdiParameters.hpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/Parameters/AVR8Generic/UpdiParameters.hpp @@ -2,7 +2,7 @@ #include -#include "src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.hpp" +#include "src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp" namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Generic { @@ -28,6 +28,6 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::Parameters::Avr8Gen std::uint16_t fuseSegmentStartAddress; std::uint16_t lockbitSegmentStartAddress; - UpdiParameters(const Targets::Microchip::Avr::Avr8Bit::TargetDescriptionFile& targetDescriptionFile); + UpdiParameters(const Targets::Microchip::Avr8::TargetDescriptionFile& targetDescriptionFile); }; } diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/Avr8GenericResponseFrame.cpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/Avr8GenericResponseFrame.cpp index 9aae9dd8..3b131805 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/Avr8GenericResponseFrame.cpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/Avr8GenericResponseFrame.cpp @@ -10,7 +10,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr : AvrResponseFrame(avrResponses) { if (this->payload.empty()) { - throw Exception("Response ID missing from AVR8 Generic response frame payload."); + throw Exception{"Response ID missing from AVR8 Generic response frame payload."}; } this->id = static_cast(this->payload[0]); @@ -21,11 +21,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr * AVR8 data payloads are in little endian form and include two bytes before the data (response ID and * version byte) as well as an additional byte after the data, known as the 'status code'. */ - auto data = std::vector( - this->payload.begin() + 2, - this->payload.end() - 1 - ); - + auto data = std::vector{this->payload.begin() + 2, this->payload.end() - 1}; std::reverse(data.begin(), data.end()); return data; } diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/GetDeviceId.cpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/GetDeviceId.cpp index 528b88a9..05a20441 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/GetDeviceId.cpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/GetDeviceId.cpp @@ -6,10 +6,12 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr : Avr8GenericResponseFrame(AvrResponses) {} - Targets::Microchip::Avr::TargetSignature GetDeviceId::extractSignature( + Targets::Microchip::Avr8::TargetSignature GetDeviceId::extractSignature( Targets::TargetPhysicalInterface physicalInterface ) const { using Targets::TargetPhysicalInterface; + using Targets::Microchip::Avr8::TargetSignature; + const auto payloadData = this->getPayloadData(); switch (physicalInterface) { @@ -18,14 +20,14 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr * When using the DebugWire physical interface, the get device ID command will return * four bytes, where the first can be ignored. */ - return Targets::Microchip::Avr::TargetSignature(payloadData[1], payloadData[2], payloadData[3]); + return TargetSignature{payloadData[1], payloadData[2], payloadData[3]}; } case TargetPhysicalInterface::PDI: case TargetPhysicalInterface::UPDI: { /* * When using the PDI physical interface, the signature is returned in LSB format. */ - return Targets::Microchip::Avr::TargetSignature(payloadData[3], payloadData[2], payloadData[1]); + return TargetSignature{payloadData[3], payloadData[2], payloadData[1]}; } case TargetPhysicalInterface::JTAG: { /* @@ -50,18 +52,14 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr (payloadData[0] << 24) | (payloadData[1] << 16) | (payloadData[2] << 8) | (payloadData[3]) ); - return Targets::Microchip::Avr::TargetSignature( + return TargetSignature{ 0x1E, static_cast((jtagId << 4) >> 24), static_cast((jtagId << 12) >> 24) - ); + }; } default: { - return Targets::Microchip::Avr::TargetSignature( - payloadData[0], - payloadData[1], - payloadData[2] - ); + return TargetSignature{payloadData[0], payloadData[1], payloadData[2]}; } } } diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/GetDeviceId.hpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/GetDeviceId.hpp index 2817a0b2..0374cb08 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/GetDeviceId.hpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/GetDeviceId.hpp @@ -2,7 +2,7 @@ #include "Avr8GenericResponseFrame.hpp" -#include "src/Targets/Microchip/AVR/TargetSignature.hpp" +#include "src/Targets/Microchip/AVR8/TargetSignature.hpp" #include "src/Targets/TargetPhysicalInterface.hpp" namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr8Generic @@ -12,7 +12,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr public: explicit GetDeviceId(const std::vector& AvrResponses); - Targets::Microchip::Avr::TargetSignature extractSignature( + Targets::Microchip::Avr8::TargetSignature extractSignature( Targets::TargetPhysicalInterface physicalInterface ) const; }; diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/GetProgramCounter.hpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/GetProgramCounter.hpp index 0fa48d1c..c41dd0da 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/GetProgramCounter.hpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/GetProgramCounter.hpp @@ -23,8 +23,10 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr * command ID and version, the other four being the PC. The four PC bytes are little-endian. */ if (this->payload.size() != 6) { - throw Exceptions::Exception("Failed to extract PC from payload of PC read command response " - "frame - unexpected payload size."); + throw Exceptions::Exception{ + "Failed to extract PC from payload of PC read command response " + "frame - unexpected payload size." + }; } return static_cast( diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/ReadMemory.hpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/ReadMemory.hpp index ba79c213..5dafd235 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/ReadMemory.hpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVR8Generic/ReadMemory.hpp @@ -17,10 +17,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr * AVR8 data payloads are typically in little endian form, but this does not apply to the data returned * from the READ MEMORY commands. */ - return std::vector( - this->payload.begin() + 2, - this->payload.end() - 1 - ); + return {this->payload.begin() + 2, this->payload.end() - 1}; } }; } diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVRISP/AvrIspResponseFrame.cpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVRISP/AvrIspResponseFrame.cpp index 545b5f0c..2faedba3 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVRISP/AvrIspResponseFrame.cpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AVRISP/AvrIspResponseFrame.cpp @@ -10,7 +10,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Avr : AvrResponseFrame(avrResponses) { if (this->payload.size() < 2) { - throw Exception("Status code missing from AVRISP response frame payload."); + throw Exception{"Status code missing from AVRISP response frame payload."}; } this->statusCode = static_cast(this->payload[1]); diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AvrResponseFrame.cpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AvrResponseFrame.cpp index 51c9c806..3b5af92b 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AvrResponseFrame.cpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/AvrResponseFrame.cpp @@ -8,7 +8,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr void AvrResponseFrame::initFromAvrResponses(const std::vector& avrResponses) { // Build a raw frame buffer from the AvrResponse objects and just call initFromRawFrame() - auto rawFrame = std::vector(); + auto rawFrame = std::vector{}; for (const auto& avrResponse : avrResponses) { rawFrame.insert(rawFrame.end(), avrResponse.responsePacket.begin(), avrResponse.responsePacket.end()); @@ -23,12 +23,12 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr * All AVR response frames must consist of at least four bytes (SOF, sequence ID (two bytes) and * a protocol handler ID). */ - throw Exception("Failed to construct AvrResponseFrame - unexpected end to raw frame"); + throw Exception{"Failed to construct AvrResponseFrame - unexpected end to raw frame"}; } if (rawFrame[0] != 0x0E) { // Invalid SOF byte value - throw Exception("Failed to construct AvrResponseFrame - unexpected SOF byte value in raw frame"); + throw Exception{"Failed to construct AvrResponseFrame - unexpected SOF byte value in raw frame"}; } this->sequenceId = static_cast((rawFrame[2] << 8) + rawFrame[1]); diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/Discovery/DiscoveryResponseFrame.cpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/Discovery/DiscoveryResponseFrame.cpp index 1890a88c..a1887939 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/Discovery/DiscoveryResponseFrame.cpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/Discovery/DiscoveryResponseFrame.cpp @@ -10,7 +10,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Dis : AvrResponseFrame(avrResponses) { if (this->payload.empty()) { - throw Exception("Response ID missing from DISCOVERY response frame payload."); + throw Exception{"Response ID missing from DISCOVERY response frame payload."}; } this->id = static_cast(payload[0]); @@ -22,9 +22,6 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Dis } // DISCOVERY payloads include two bytes before the data (response ID and version byte). - return std::vector( - this->payload.begin() + 2, - this->payload.end() - ); + return {this->payload.begin() + 2, this->payload.end()}; } } diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/EDBGControl/EdbgControlResponseFrame.cpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/EDBGControl/EdbgControlResponseFrame.cpp index 27f170c8..b9b8e89d 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/EDBGControl/EdbgControlResponseFrame.cpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/EDBGControl/EdbgControlResponseFrame.cpp @@ -10,7 +10,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Edb : AvrResponseFrame(avrResponses) { if (this->payload.empty()) { - throw Exception("Response ID missing from EDBG Control response frame payload."); + throw Exception{"Response ID missing from EDBG Control response frame payload."}; } this->id = static_cast(this->payload[0]); @@ -25,9 +25,6 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Edb * EDBG Control data payloads include two bytes before the data (response ID and version byte) as well as an * additional byte after the data, known as the 'status code'. */ - return std::vector( - this->payload.begin() + 2, - this->payload.end() - 1 - ); + return {this->payload.begin() + 2, this->payload.end() - 1}; } } diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/HouseKeeping/HouseKeepingResponseFrame.cpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/HouseKeeping/HouseKeepingResponseFrame.cpp index 7866ecb8..d592efc8 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/HouseKeeping/HouseKeepingResponseFrame.cpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/AVR/ResponseFrames/HouseKeeping/HouseKeepingResponseFrame.cpp @@ -10,7 +10,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg::Avr::ResponseFrames::Hou : AvrResponseFrame(avrResponses) { if (this->payload.empty()) { - throw Exception("Response ID missing from HOUSEKEEPING response frame payload."); + throw Exception{"Response ID missing from HOUSEKEEPING response frame payload."}; } this->id = static_cast(this->payload[0]); diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgInterface.cpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgInterface.cpp index ba869ba5..a5d8e6f7 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgInterface.cpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgInterface.cpp @@ -25,26 +25,25 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg } // This should never happen - throw DeviceCommunicationFailure( + throw DeviceCommunicationFailure{ "Cannot send AVR command frame - failed to generate CMSIS-DAP Vendor (AVR) commands" - ); + }; } std::optional EdbgInterface::requestAvrEvent() { - auto avrEventResponse = this->sendCommandAndWaitForResponse(Avr::AvrEventCommand()); - + auto avrEventResponse = this->sendCommandAndWaitForResponse(Avr::AvrEventCommand{}); if (avrEventResponse.id != 0x82) { throw DeviceCommunicationFailure("Unexpected response to AvrEventCommand from device"); } - return !avrEventResponse.eventData.empty() ? std::optional(avrEventResponse) : std::nullopt; + return !avrEventResponse.eventData.empty() ? std::optional{avrEventResponse} : std::nullopt; } std::vector EdbgInterface::requestAvrResponses() { using Microchip::Protocols::Edbg::Avr::AvrResponseCommand; - std::vector responses; - AvrResponseCommand responseCommand; + auto responses = std::vector{}; + const auto responseCommand = AvrResponseCommand{}; auto avrResponse = this->sendCommandAndWaitForResponse(responseCommand); responses.push_back(avrResponse); @@ -55,15 +54,13 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg auto avrResponse = this->sendCommandAndWaitForResponse(responseCommand); if (avrResponse.fragmentCount != fragmentCount) { - throw DeviceCommunicationFailure( + throw DeviceCommunicationFailure{ "Failed to fetch AvrResponse objects - invalid fragment count returned." - ); + }; } if (avrResponse.fragmentCount == 0 && avrResponse.fragmentNumber == 0) { - throw DeviceCommunicationFailure( - "Failed to fetch AvrResponse objects - unexpected empty response" - ); + throw DeviceCommunicationFailure{"Failed to fetch AvrResponse objects - unexpected empty response"}; } if (avrResponse.fragmentNumber == 0) { diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgInterface.hpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgInterface.hpp index 6bd49d9b..c5ac2f47 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgInterface.hpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgInterface.hpp @@ -23,7 +23,7 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg * The EdbgInterface class implements the EDBG sub-protocol, which takes the form of numerous CMSIS-DAP vendor * commands. */ -class EdbgInterface: public ::DebugToolDrivers::Protocols::CmsisDap::CmsisDapInterface + class EdbgInterface: public ::DebugToolDrivers::Protocols::CmsisDap::CmsisDapInterface { public: explicit EdbgInterface(Usb::HidInterface&& cmsisHidInterface); @@ -71,9 +71,9 @@ class EdbgInterface: public ::DebugToolDrivers::Protocols::CmsisDap::CmsisDapInt if (response.data[0] != 0x01) { // The last response packet should always acknowledge receipt of the AvrCommandFrame - throw Exceptions::DeviceCommunicationFailure( + throw Exceptions::DeviceCommunicationFailure{ "Failed to send AvrCommandFrame to device - device did not acknowledge receipt." - ); + }; } return typename CommandFrameType::ExpectedResponseFrameType(this->requestAvrResponses()); diff --git a/src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgTargetPowerManagementInterface.cpp b/src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgTargetPowerManagementInterface.cpp index ce278f1d..22f8c398 100644 --- a/src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgTargetPowerManagementInterface.cpp +++ b/src/DebugToolDrivers/Microchip/Protocols/EDBG/EdbgTargetPowerManagementInterface.cpp @@ -18,21 +18,21 @@ namespace DebugToolDrivers::Microchip::Protocols::Edbg void EdbgTargetPowerManagementInterface::enableTargetPower() { const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - SetParameter(EdbgParameters::CONTROL_TARGET_POWER, 0x01) + SetParameter{EdbgParameters::CONTROL_TARGET_POWER, 0x01} ); if (responseFrame.id == EdbgControlResponseId::FAILED) { - throw Exception("Failed to enable target power via EDBG Control protocol"); + throw Exception{"Failed to enable target power via EDBG Control protocol"}; } } void EdbgTargetPowerManagementInterface::disableTargetPower() { const auto responseFrame = this->edbgInterface->sendAvrCommandFrameAndWaitForResponseFrame( - SetParameter(EdbgParameters::CONTROL_TARGET_POWER, 0x00) + SetParameter{EdbgParameters::CONTROL_TARGET_POWER, 0x00} ); if (responseFrame.id == EdbgControlResponseId::FAILED) { - throw Exception("Failed to disable target power via EDBG Control protocol"); + throw Exception{"Failed to disable target power via EDBG Control protocol"}; } } } diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/CmsisDapInterface.cpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/CmsisDapInterface.cpp index 6bf5e5da..94d806b8 100644 --- a/src/DebugToolDrivers/Protocols/CMSIS-DAP/CmsisDapInterface.cpp +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/CmsisDapInterface.cpp @@ -12,12 +12,13 @@ namespace DebugToolDrivers::Protocols::CmsisDap void CmsisDapInterface::sendCommand(const Command& cmsisDapCommand) { if (this->commandDelay.count() > 0) { - using namespace std::chrono; - std::int64_t now = duration_cast(high_resolution_clock::now().time_since_epoch()).count(); - std::int64_t difference = (now - this->lastCommandSentTimeStamp); + const auto now = duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch() + ).count(); + const auto difference = (now - this->lastCommandSentTimeStamp); if (difference < this->commandDelay.count()) { - std::this_thread::sleep_for(milliseconds(this->commandDelay.count() - difference)); + std::this_thread::sleep_for(std::chrono::milliseconds{this->commandDelay.count() - difference}); } this->lastCommandSentTimeStamp = now; diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/CmsisDapInterface.hpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/CmsisDapInterface.hpp index f6b9d28f..c12ad48c 100644 --- a/src/DebugToolDrivers/Protocols/CMSIS-DAP/CmsisDapInterface.hpp +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/CmsisDapInterface.hpp @@ -65,10 +65,9 @@ namespace DebugToolDrivers::Protocols::CmsisDap "CMSIS Response type must be derived from the Response class." ); - const auto rawResponse = this->getUsbHidInterface().read(std::chrono::milliseconds(60000)); - + const auto rawResponse = this->getUsbHidInterface().read(std::chrono::milliseconds{60000}); if (rawResponse.empty()) { - throw Exceptions::DeviceCommunicationFailure("Empty CMSIS-DAP response received"); + throw Exceptions::DeviceCommunicationFailure{"Empty CMSIS-DAP response received"}; } return ResponseType(rawResponse); @@ -99,7 +98,7 @@ namespace DebugToolDrivers::Protocols::CmsisDap auto response = this->getResponse(); if (response.id != cmsisDapCommand.id) { - throw Exceptions::DeviceCommunicationFailure("Unexpected response to CMSIS-DAP command."); + throw Exceptions::DeviceCommunicationFailure{"Unexpected response to CMSIS-DAP command."}; } return response; @@ -121,7 +120,7 @@ namespace DebugToolDrivers::Protocols::CmsisDap * * Because of this, we may need to enforce a minimum time gap between sending CMSIS commands. */ - std::chrono::milliseconds commandDelay = std::chrono::milliseconds(0); + std::chrono::milliseconds commandDelay = std::chrono::milliseconds{0}; std::int64_t lastCommandSentTimeStamp = 0; }; } diff --git a/src/DebugToolDrivers/Protocols/CMSIS-DAP/Response.cpp b/src/DebugToolDrivers/Protocols/CMSIS-DAP/Response.cpp index e3c2bf8b..c9006a41 100644 --- a/src/DebugToolDrivers/Protocols/CMSIS-DAP/Response.cpp +++ b/src/DebugToolDrivers/Protocols/CMSIS-DAP/Response.cpp @@ -6,10 +6,10 @@ namespace DebugToolDrivers::Protocols::CmsisDap { Response::Response(const std::vector& rawResponse) { if (rawResponse.empty()) { - throw Exceptions::Exception("Failed to process CMSIS-DAP response - invalid response"); + throw Exceptions::Exception{"Failed to process CMSIS-DAP response - invalid response"}; } this->id = rawResponse[0]; - this->data = std::vector(rawResponse.begin() + 1, rawResponse.end()); + this->data = std::vector{rawResponse.begin() + 1, rawResponse.end()}; } } diff --git a/src/Targets/RiscV/DebugModule/DebugModule.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp similarity index 81% rename from src/Targets/RiscV/DebugModule/DebugModule.hpp rename to src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp index 47c4b9fb..c0b8d4c5 100644 --- a/src/Targets/RiscV/DebugModule/DebugModule.hpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp @@ -1,11 +1,8 @@ #pragma once #include -#include -#include -#include -namespace Targets::RiscV::DebugModule +namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule { using RegisterAddress = std::uint8_t; using RegisterValue = std::uint32_t; diff --git a/src/Targets/RiscV/DebugModule/Registers/AbstractCommandRegister.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/AbstractCommandRegister.hpp similarity index 74% rename from src/Targets/RiscV/DebugModule/Registers/AbstractCommandRegister.hpp rename to src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/AbstractCommandRegister.hpp index 86108965..57b837c4 100644 --- a/src/Targets/RiscV/DebugModule/Registers/AbstractCommandRegister.hpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/AbstractCommandRegister.hpp @@ -3,9 +3,9 @@ #include #include -#include "src/Targets/RiscV/DebugModule/DebugModule.hpp" +#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp" -namespace Targets::RiscV::DebugModule::Registers +namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers { struct AbstractCommandRegister { @@ -21,6 +21,11 @@ namespace Targets::RiscV::DebugModule::Registers AbstractCommandRegister() = default; + constexpr AbstractCommandRegister(std::uint32_t control, CommandType commandType) + : control(control) + , commandType(commandType) + {} + constexpr explicit AbstractCommandRegister(RegisterValue registerValue) : control(static_cast(registerValue & 0x00FFFFFF)) , commandType(static_cast((registerValue >> 24) & 0xFF)) diff --git a/src/Targets/RiscV/DebugModule/Registers/AbstractControlStatusRegister.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/AbstractControlStatusRegister.hpp similarity index 91% rename from src/Targets/RiscV/DebugModule/Registers/AbstractControlStatusRegister.hpp rename to src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/AbstractControlStatusRegister.hpp index f3af9e9a..2a834389 100644 --- a/src/Targets/RiscV/DebugModule/Registers/AbstractControlStatusRegister.hpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/AbstractControlStatusRegister.hpp @@ -2,9 +2,9 @@ #include -#include "src/Targets/RiscV/DebugModule/DebugModule.hpp" +#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp" -namespace Targets::RiscV::DebugModule::Registers +namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers { struct AbstractControlStatusRegister { diff --git a/src/Targets/RiscV/DebugModule/Registers/ControlRegister.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/ControlRegister.hpp similarity index 95% rename from src/Targets/RiscV/DebugModule/Registers/ControlRegister.hpp rename to src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/ControlRegister.hpp index 1bd4da23..c4f430e5 100644 --- a/src/Targets/RiscV/DebugModule/Registers/ControlRegister.hpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/ControlRegister.hpp @@ -3,9 +3,9 @@ #include #include -#include "src/Targets/RiscV/DebugModule/DebugModule.hpp" +#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp" -namespace Targets::RiscV::DebugModule::Registers +namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers { struct ControlRegister { diff --git a/src/Targets/RiscV/DebugModule/Registers/MemoryAccessControlField.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/MemoryAccessControlField.hpp similarity index 85% rename from src/Targets/RiscV/DebugModule/Registers/MemoryAccessControlField.hpp rename to src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/MemoryAccessControlField.hpp index 6d984ef6..9b549e3f 100644 --- a/src/Targets/RiscV/DebugModule/Registers/MemoryAccessControlField.hpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/MemoryAccessControlField.hpp @@ -2,9 +2,9 @@ #include -#include "src/Targets/RiscV/RiscVGeneric.hpp" +#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/RiscVGeneric.hpp" -namespace Targets::RiscV::DebugModule::Registers +namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers { struct MemoryAccessControlField { @@ -22,9 +22,9 @@ namespace Targets::RiscV::DebugModule::Registers MemorySize size:3 = MemorySize::SIZE_32; bool virtualAddress:1 = false; - MemoryAccessControlField() = default; + constexpr MemoryAccessControlField() = default; - MemoryAccessControlField( + constexpr MemoryAccessControlField( bool write, bool postIncrement, MemorySize size, diff --git a/src/Targets/RiscV/DebugModule/Registers/RegisterAccessControlField.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/RegisterAccessControlField.hpp similarity index 92% rename from src/Targets/RiscV/DebugModule/Registers/RegisterAccessControlField.hpp rename to src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/RegisterAccessControlField.hpp index d29f6781..88f497a3 100644 --- a/src/Targets/RiscV/DebugModule/Registers/RegisterAccessControlField.hpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/RegisterAccessControlField.hpp @@ -2,9 +2,9 @@ #include -#include "src/Targets/RiscV/RiscVGeneric.hpp" +#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/RiscVGeneric.hpp" -namespace Targets::RiscV::DebugModule::Registers +namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers { struct RegisterAccessControlField { diff --git a/src/Targets/RiscV/DebugModule/Registers/RegisterAddresses.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/RegisterAddresses.hpp similarity index 53% rename from src/Targets/RiscV/DebugModule/Registers/RegisterAddresses.hpp rename to src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/RegisterAddresses.hpp index 2338ebeb..e13a01da 100644 --- a/src/Targets/RiscV/DebugModule/Registers/RegisterAddresses.hpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/RegisterAddresses.hpp @@ -2,11 +2,11 @@ #include -#include "src/Targets/RiscV/DebugModule/DebugModule.hpp" +#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp" -namespace Targets::RiscV::DebugModule::Registers +namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers { - enum class RegisterAddress: ::Targets::RiscV::DebugModule::RegisterAddress + enum class RegisterAddress: RegisterAddress { ABSTRACT_DATA_0 = 0x04, ABSTRACT_DATA_1 = 0x05, @@ -15,6 +15,11 @@ namespace Targets::RiscV::DebugModule::Registers ABSTRACT_DATA_4 = 0x08, ABSTRACT_DATA_5 = 0x09, ABSTRACT_DATA_6 = 0x0a, + ABSTRACT_DATA_7 = 0x0b, + ABSTRACT_DATA_8 = 0x0c, + ABSTRACT_DATA_9 = 0x0d, + ABSTRACT_DATA_10 = 0x0e, + ABSTRACT_DATA_11 = 0x0f, CONTROL_REGISTER = 0x10, STATUS_REGISTER = 0x11, ABSTRACT_CONTROL_STATUS_REGISTER = 0x16, diff --git a/src/Targets/RiscV/DebugModule/Registers/StatusRegister.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/StatusRegister.hpp similarity index 96% rename from src/Targets/RiscV/DebugModule/Registers/StatusRegister.hpp rename to src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/StatusRegister.hpp index 277d06df..5fff2851 100644 --- a/src/Targets/RiscV/DebugModule/Registers/StatusRegister.hpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/Registers/StatusRegister.hpp @@ -2,9 +2,9 @@ #include -#include "src/Targets/RiscV/DebugModule/DebugModule.hpp" +#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp" -namespace Targets::RiscV::DebugModule::Registers +namespace DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::Registers { struct StatusRegister { diff --git a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.cpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.cpp new file mode 100644 index 00000000..406f2e5a --- /dev/null +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.cpp @@ -0,0 +1,592 @@ +#include "DebugTranslator.hpp" + +#include +#include +#include +#include +#include + +#include "Registers/CpuRegisterNumbers.hpp" +#include "DebugModule/Registers/RegisterAddresses.hpp" +#include "DebugModule/Registers/RegisterAccessControlField.hpp" +#include "DebugModule/Registers/MemoryAccessControlField.hpp" + +#include "src/Exceptions/Exception.hpp" +#include "src/Exceptions/InvalidConfig.hpp" +#include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" +#include "src/TargetController/Exceptions/TargetOperationFailure.hpp" + +#include "src/Logger/Logger.hpp" + +namespace DebugToolDrivers::Protocols::RiscVDebugSpec +{ + using Registers::DebugControlStatusRegister; + + using DebugModule::Registers::RegisterAddress; + using DebugModule::Registers::ControlRegister; + using DebugModule::Registers::StatusRegister; + using DebugModule::Registers::AbstractControlStatusRegister; + using DebugModule::Registers::AbstractCommandRegister; + + using Registers::CpuRegisterNumber; + + using namespace ::Targets::RiscV; + + using ::Targets::TargetExecutionState; + using ::Targets::TargetMemoryAddress; + using ::Targets::TargetMemoryAddressRange; + using ::Targets::TargetMemorySize; + using ::Targets::TargetMemoryBuffer; + using ::Targets::TargetStackPointer; + using ::Targets::TargetAddressSpaceDescriptor; + using ::Targets::TargetMemorySegmentDescriptor; + using ::Targets::TargetRegisterDescriptors; + using ::Targets::TargetRegisterDescriptorAndValuePairs; + + DebugTranslator::DebugTranslator( + DebugTransportModuleInterface& dtmInterface, + const TargetDescriptionFile& targetDescriptionFile, + const RiscVTargetConfig& targetConfig + ) + : dtmInterface(dtmInterface) + , targetDescriptionFile(targetDescriptionFile) + , targetConfig(targetConfig) + {} + + void DebugTranslator::init() { + // No pre-activation initialisation required. + return; + } + + void DebugTranslator::activate() { + this->dtmInterface.activate(); + + this->hartIndices = this->discoverHartIndices(); + if (this->hartIndices.empty()) { + throw Exceptions::TargetOperationFailure{"Failed to discover any RISC-V harts"}; + } + + Logger::debug("Discovered RISC-V harts: " + std::to_string(this->hartIndices.size())); + + /* + * We only support debugging a single RISC-V hart, for now. + * + * If we discover more than one, we select the first one and ensure that this is explicitly communicated to the + * user. + */ + if (this->hartIndices.size() > 1) { + Logger::warning("Bloom only supports debugging a single RISC-V hart - selecting first available"); + } + + this->selectedHartIndex = this->hartIndices.front(); + Logger::info("Selected RISC-V hart index: " + std::to_string(this->selectedHartIndex)); + + /* + * Disabling the debug module before enabling it will clear any state from a previous debug session that + * wasn't terminated properly. + */ + this->disableDebugModule(); + this->enableDebugModule(); + + this->stop(); + this->reset(); + + auto debugControlStatusRegister = this->readDebugControlStatusRegister(); + debugControlStatusRegister.breakUMode = true; + debugControlStatusRegister.breakSMode = true; + debugControlStatusRegister.breakMMode = true; + + this->writeDebugControlStatusRegister(debugControlStatusRegister); + } + + void DebugTranslator::deactivate() { + this->disableDebugModule(); + this->dtmInterface.deactivate(); + } + + TargetExecutionState DebugTranslator::getExecutionState() { + return this->readDebugModuleStatusRegister().anyRunning + ? TargetExecutionState::RUNNING + : TargetExecutionState::STOPPED; + } + + void DebugTranslator::stop() { + auto controlRegister = ControlRegister{}; + controlRegister.debugModuleActive = true; + controlRegister.selectedHartIndex = this->selectedHartIndex; + controlRegister.haltRequest = true; + + this->writeDebugModuleControlRegister(controlRegister); + + constexpr auto maxAttempts = 10; + auto statusRegister = this->readDebugModuleStatusRegister(); + + for (auto attempts = 1; !statusRegister.allHalted && attempts <= maxAttempts; ++attempts) { + std::this_thread::sleep_for(std::chrono::microseconds{10}); + statusRegister = this->readDebugModuleStatusRegister(); + } + + controlRegister.haltRequest = false; + this->writeDebugModuleControlRegister(controlRegister); + + if (!statusRegister.allHalted) { + throw Exceptions::Exception{"Target took too long to halt selected harts"}; + } + } + + void DebugTranslator::run() { + auto controlRegister = ControlRegister{}; + controlRegister.debugModuleActive = true; + controlRegister.selectedHartIndex = this->selectedHartIndex; + controlRegister.resumeRequest = true; + + this->writeDebugModuleControlRegister(controlRegister); + + constexpr auto maxAttempts = 10; + auto statusRegister = this->readDebugModuleStatusRegister(); + + for (auto attempts = 1; !statusRegister.allResumeAcknowledge && attempts <= maxAttempts; ++attempts) { + std::this_thread::sleep_for(std::chrono::microseconds{10}); + statusRegister = this->readDebugModuleStatusRegister(); + } + + controlRegister.resumeRequest = false; + this->writeDebugModuleControlRegister(controlRegister); + + if (!statusRegister.allResumeAcknowledge) { + throw Exceptions::Exception{"Target took too long to acknowledge resume request"}; + } + } + + void DebugTranslator::step() { + auto debugControlStatusRegister = this->readDebugControlStatusRegister(); + debugControlStatusRegister.step = true; + + this->writeDebugControlStatusRegister(debugControlStatusRegister); + + auto controlRegister = ControlRegister{}; + controlRegister.debugModuleActive = true; + controlRegister.selectedHartIndex = this->selectedHartIndex; + controlRegister.resumeRequest = true; + + this->writeDebugModuleControlRegister(controlRegister); + + controlRegister.resumeRequest = false; + this->writeDebugModuleControlRegister(controlRegister); + + debugControlStatusRegister.step = false; + this->writeDebugControlStatusRegister(debugControlStatusRegister); + } + + void DebugTranslator::reset() { + auto controlRegister = ControlRegister{}; + controlRegister.debugModuleActive = true; + controlRegister.selectedHartIndex = this->selectedHartIndex; + controlRegister.setResetHaltRequest = true; + controlRegister.haltRequest = true; + controlRegister.ndmReset = true; + + this->writeDebugModuleControlRegister(controlRegister); + + controlRegister.ndmReset = false; + this->writeDebugModuleControlRegister(controlRegister); + + constexpr auto maxAttempts = 10; + auto statusRegister = this->readDebugModuleStatusRegister(); + + for (auto attempts = 1; !statusRegister.allHaveReset && attempts <= maxAttempts; ++attempts) { + std::this_thread::sleep_for(std::chrono::microseconds{10}); + statusRegister = this->readDebugModuleStatusRegister(); + } + + controlRegister = ControlRegister{}; + controlRegister.debugModuleActive = true; + controlRegister.selectedHartIndex = this->selectedHartIndex; + controlRegister.clearResetHaltRequest = true; + controlRegister.acknowledgeHaveReset = true; + + this->writeDebugModuleControlRegister(controlRegister); + + if (!statusRegister.allHaveReset) { + throw Exceptions::Exception{"Target took too long to reset"}; + } + } + + void DebugTranslator::setSoftwareBreakpoint(TargetMemoryAddress address) { + + } + + void DebugTranslator::clearSoftwareBreakpoint(TargetMemoryAddress address) { + + } + + void DebugTranslator::setHardwareBreakpoint(TargetMemoryAddress address) { + + } + + void DebugTranslator::clearHardwareBreakpoint(TargetMemoryAddress address) { + + } + + void DebugTranslator::clearAllBreakpoints() { + + } + + TargetRegisterDescriptorAndValuePairs DebugTranslator::readCpuRegisters( + const TargetRegisterDescriptors& descriptors + ) { + auto output = TargetRegisterDescriptorAndValuePairs{}; + + for (const auto& descriptor : descriptors) { + const auto registerValue = this->readCpuRegister(static_cast(descriptor->startAddress)); + output.emplace_back( + *descriptor, + TargetMemoryBuffer{ + static_cast(registerValue >> 24), + static_cast(registerValue >> 16), + static_cast(registerValue >> 8), + static_cast(registerValue), + } + ); + } + + return output; + } + + void DebugTranslator::writeCpuRegisters(const TargetRegisterDescriptorAndValuePairs& registers) { + for (const auto& [descriptor, value] : registers) { + assert((value.size() * 8) > std::numeric_limits::digits); + + auto registerValue = RegisterValue{0}; + for (const auto& registerByte : value) { + registerValue = (registerValue << 8) | registerByte; + } + + this->writeCpuRegister(static_cast(descriptor.startAddress), registerValue); + } + } + + TargetMemoryBuffer DebugTranslator::readMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, + TargetMemoryAddress startAddress, + TargetMemorySize bytes, + const std::set& excludedAddressRanges + ) { + using DebugModule::Registers::MemoryAccessControlField; + + // TODO: excluded addresses + + const auto pageSize = 4; + if ((startAddress % pageSize) != 0 || (bytes % pageSize) != 0) { + // Alignment required + const auto alignedStartAddress = this->alignMemoryAddress(startAddress, pageSize); + const auto alignedBytes = this->alignMemorySize(bytes + (startAddress - alignedStartAddress), pageSize); + + const auto memoryBuffer = this->readMemory( + addressSpaceDescriptor, + memorySegmentDescriptor, + alignedStartAddress, + alignedBytes, + excludedAddressRanges + ); + + const auto offset = memoryBuffer.begin() + (startAddress - alignedStartAddress); + return TargetMemoryBuffer{offset, offset + bytes}; + } + + auto output = TargetMemoryBuffer{}; + output.reserve(bytes); + + /* + * We only need to set the address once. No need to update it as we use the post-increment function to + * increment the address. See MemoryAccessControlField::postIncrement + */ + this->dtmInterface.writeDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_1, startAddress); + + constexpr auto command = AbstractCommandRegister{ + MemoryAccessControlField{ + false, + true, + MemoryAccessControlField::MemorySize::SIZE_32, + false + }.value(), + AbstractCommandRegister::CommandType::MEMORY_ACCESS + }; + + for (auto address = startAddress; address <= (startAddress + bytes - 1); address += 4) { + this->executeAbstractCommand(command); + + const auto data = this->dtmInterface.readDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_0); + output.emplace_back(static_cast(data)); + output.emplace_back(static_cast(data >> 8)); + output.emplace_back(static_cast(data >> 16)); + output.emplace_back(static_cast(data >> 24)); + } + + return output; + } + + void DebugTranslator::writeMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, + TargetMemoryAddress startAddress, + const TargetMemoryBuffer& buffer + ) { + using DebugModule::Registers::MemoryAccessControlField; + + constexpr auto alignTo = TargetMemorySize{4}; + const auto bytes = static_cast(buffer.size()); + if ((startAddress % alignTo) != 0 || (bytes % alignTo) != 0) { + /* + * Alignment required + * + * To align the write operation, we read the front and back offset bytes and use them to construct an + * aligned buffer. + */ + const auto alignedStartAddress = this->alignMemoryAddress(startAddress, alignTo); + const auto alignedBytes = this->alignMemorySize(bytes + (startAddress - alignedStartAddress), alignTo); + + assert(alignedBytes > bytes); + + auto alignedBuffer = (alignedStartAddress < startAddress) + ? this->readMemory( + addressSpaceDescriptor, + memorySegmentDescriptor, + alignedStartAddress, + (startAddress - alignedStartAddress) + ) + : TargetMemoryBuffer{}; + alignedBuffer.reserve(alignedBytes); + + // Read the offset bytes required to align the buffer size + const auto dataBack = this->readMemory( + addressSpaceDescriptor, + memorySegmentDescriptor, + startAddress + bytes, + alignedBytes - bytes - (startAddress - alignedStartAddress) + ); + alignedBuffer.insert(alignedBuffer.end(), dataBack.begin(), dataBack.end()); + + return this->writeMemory( + addressSpaceDescriptor, + memorySegmentDescriptor, + alignedStartAddress, + alignedBuffer + ); + } + + this->dtmInterface.writeDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_1, startAddress); + + constexpr auto command = AbstractCommandRegister{ + MemoryAccessControlField{ + true, + true, + MemoryAccessControlField::MemorySize::SIZE_32, + false + }.value(), + AbstractCommandRegister::CommandType::MEMORY_ACCESS + }; + + for (TargetMemoryAddress offset = 0; offset < buffer.size(); offset += 4) { + this->dtmInterface.writeDebugModuleRegister( + RegisterAddress::ABSTRACT_DATA_0, + static_cast( + (buffer[offset + 3] << 24) + | (buffer[offset + 2] << 16) + | (buffer[offset + 1] << 8) + | (buffer[offset]) + ) + ); + + this->executeAbstractCommand(command); + } + } + + std::vector DebugTranslator::discoverHartIndices() { + auto hartIndices = std::vector{}; + + /* + * We can obtain the maximum hart index by setting all of the hartsel bits in the control register and then + * read the value back. + */ + auto controlRegister = ControlRegister{}; + controlRegister.debugModuleActive = true; + controlRegister.selectedHartIndex = 0xFFFFF; + + this->writeDebugModuleControlRegister(controlRegister); + const auto maxHartIndex = this->readDebugModuleControlRegister().selectedHartIndex; + + for (auto hartIndex = DebugModule::HartIndex{0}; hartIndex <= maxHartIndex; ++hartIndex) { + /* + * We can't just assume that everything between 0 and the maximum hart index are valid hart indices. We + * have to test each index until we find one that is non-existent. + */ + auto controlRegister = ControlRegister{}; + controlRegister.debugModuleActive = true; + controlRegister.selectedHartIndex = hartIndex; + + this->writeDebugModuleControlRegister(controlRegister); + + /* + * It's worth noting that some RISC-V targets **do not** set the non-existent flags. I'm not sure why. + * Maybe hartsel has been hardwired to 0 on targets that only support a single hart, preventing the + * selection of non-existent harts. + * + * Relying on the maximum hart index seems to be all we can do in this case. + */ + if (this->readDebugModuleStatusRegister().anyNonExistent) { + break; + } + + hartIndices.emplace_back(hartIndex); + } + + return hartIndices; + } + + ControlRegister DebugTranslator::readDebugModuleControlRegister() { + return ControlRegister{this->dtmInterface.readDebugModuleRegister(RegisterAddress::CONTROL_REGISTER)}; + } + + StatusRegister DebugTranslator::readDebugModuleStatusRegister() { + return StatusRegister{this->dtmInterface.readDebugModuleRegister(RegisterAddress::STATUS_REGISTER)}; + } + + AbstractControlStatusRegister DebugTranslator::readDebugModuleAbstractControlStatusRegister() { + return AbstractControlStatusRegister{ + this->dtmInterface.readDebugModuleRegister(RegisterAddress::ABSTRACT_CONTROL_STATUS_REGISTER) + }; + } + + DebugControlStatusRegister DebugTranslator::readDebugControlStatusRegister() { + return DebugControlStatusRegister{ + this->readCpuRegister(static_cast(CpuRegisterNumber::DEBUG_CONTROL_STATUS_REGISTER)) + }; + } + + void DebugTranslator::enableDebugModule() { + auto controlRegister = ControlRegister{}; + controlRegister.debugModuleActive = true; + controlRegister.selectedHartIndex = this->selectedHartIndex; + + this->writeDebugModuleControlRegister(controlRegister); + + constexpr auto maxAttempts = 10; + controlRegister = this->readDebugModuleControlRegister(); + + for (auto attempts = 1; !controlRegister.debugModuleActive && attempts <= maxAttempts; ++attempts) { + std::this_thread::sleep_for(std::chrono::microseconds{10}); + controlRegister = this->readDebugModuleControlRegister(); + } + + if (!controlRegister.debugModuleActive) { + throw Exceptions::Exception{"Took too long to enable debug module"}; + } + } + + void DebugTranslator::disableDebugModule() { + auto controlRegister = ControlRegister{}; + controlRegister.debugModuleActive = false; + controlRegister.selectedHartIndex = this->selectedHartIndex; + + this->writeDebugModuleControlRegister(controlRegister); + + constexpr auto maxAttempts = 10; + controlRegister = this->readDebugModuleControlRegister(); + + for (auto attempts = 1; controlRegister.debugModuleActive && attempts <= maxAttempts; ++attempts) { + std::this_thread::sleep_for(std::chrono::microseconds{10}); + controlRegister = this->readDebugModuleControlRegister(); + } + + if (controlRegister.debugModuleActive) { + throw Exceptions::Exception{"Took too long to disable debug module"}; + } + } + + RegisterValue DebugTranslator::readCpuRegister(RegisterNumber number) { + using DebugModule::Registers::RegisterAccessControlField; + + this->executeAbstractCommand(AbstractCommandRegister{ + RegisterAccessControlField{ + number, + false, + true, + false, + false, + RegisterAccessControlField::RegisterSize::SIZE_32 + }.value(), + AbstractCommandRegister::CommandType::REGISTER_ACCESS + }); + + return this->dtmInterface.readDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_0); + } + + void DebugTranslator::writeCpuRegister(RegisterNumber number, RegisterValue value) { + using DebugModule::Registers::RegisterAccessControlField; + + this->dtmInterface.writeDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_0, value); + this->executeAbstractCommand(AbstractCommandRegister{ + RegisterAccessControlField{ + number, + true, + true, + false, + false, + RegisterAccessControlField::RegisterSize::SIZE_32 + }.value(), + AbstractCommandRegister::CommandType::REGISTER_ACCESS + }); + } + + void DebugTranslator::writeDebugModuleControlRegister(const ControlRegister& controlRegister) { + this->dtmInterface.writeDebugModuleRegister(RegisterAddress::CONTROL_REGISTER, controlRegister.value()); + } + + void DebugTranslator::writeDebugControlStatusRegister(const DebugControlStatusRegister& controlRegister) { + this->writeCpuRegister( + static_cast(CpuRegisterNumber::DEBUG_CONTROL_STATUS_REGISTER), + controlRegister.value() + ); + } + + void DebugTranslator::executeAbstractCommand( + const DebugModule::Registers::AbstractCommandRegister& abstractCommandRegister + ) { + this->dtmInterface.writeDebugModuleRegister( + RegisterAddress::ABSTRACT_COMMAND_REGISTER, + abstractCommandRegister.value() + ); + + auto abstractStatusRegister = this->readDebugModuleAbstractControlStatusRegister(); + if (abstractStatusRegister.commandError != AbstractControlStatusRegister::CommandError::NONE) { + throw Exceptions::Exception{ + "Failed to execute abstract command - error: " + + Services::StringService::toHex(abstractStatusRegister.commandError) + }; + } + + constexpr auto maxAttempts = 10; + for (auto attempts = 1; abstractStatusRegister.busy && attempts <= maxAttempts; ++attempts) { + std::this_thread::sleep_for(std::chrono::microseconds{10}); + abstractStatusRegister = this->readDebugModuleAbstractControlStatusRegister(); + } + + if (abstractStatusRegister.busy) { + throw Exceptions::Exception{"Abstract command took too long to execute"}; + } + } + + TargetMemoryAddress DebugTranslator::alignMemoryAddress(TargetMemoryAddress address, TargetMemoryAddress alignTo) { + return static_cast( + std::floor(static_cast(address) / static_cast(alignTo)) + ) * alignTo; + } + + TargetMemorySize DebugTranslator::alignMemorySize(TargetMemorySize size, TargetMemorySize alignTo) { + return static_cast( + std::ceil(static_cast(size) / static_cast(alignTo)) + ) * alignTo; + } +} diff --git a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.hpp new file mode 100644 index 00000000..3be608d7 --- /dev/null +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.hpp @@ -0,0 +1,109 @@ +#pragma once + +#include +#include + +#include "src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVDebugInterface.hpp" + +#include "DebugTransportModuleInterface.hpp" + +#include "src/Targets/RiscV/TargetDescriptionFile.hpp" +#include "src/Targets/RiscV/RiscVTargetConfig.hpp" + +#include "RiscVGeneric.hpp" +#include "Registers/CpuRegisterNumbers.hpp" +#include "Registers/DebugControlStatusRegister.hpp" + +#include "DebugModule/DebugModule.hpp" +#include "DebugModule/Registers/ControlRegister.hpp" +#include "DebugModule/Registers/StatusRegister.hpp" +#include "DebugModule/Registers/AbstractControlStatusRegister.hpp" +#include "DebugModule/Registers/AbstractCommandRegister.hpp" + +namespace DebugToolDrivers::Protocols::RiscVDebugSpec +{ + /** + * Implementation of a RISC-V debug translator + */ + class DebugTranslator: public ::DebugToolDrivers::TargetInterfaces::RiscV::RiscVDebugInterface + { + public: + DebugTranslator( + DebugTransportModuleInterface& dtmInterface, + const ::Targets::RiscV::TargetDescriptionFile& targetDescriptionFile, + const ::Targets::RiscV::RiscVTargetConfig& targetConfig + ); + + virtual ~DebugTranslator() = default; + + void init() override; + void activate() override; + void deactivate() override; + + Targets::TargetExecutionState getExecutionState() override; + + void stop() override; + void run() override; + void step() override; + void reset() override; + + void setSoftwareBreakpoint(Targets::TargetMemoryAddress address) override; + void clearSoftwareBreakpoint(Targets::TargetMemoryAddress address) override; + + void setHardwareBreakpoint(Targets::TargetMemoryAddress address) override; + void clearHardwareBreakpoint(Targets::TargetMemoryAddress address) override; + void clearAllBreakpoints() override; + + Targets::TargetRegisterDescriptorAndValuePairs readCpuRegisters( + const Targets::TargetRegisterDescriptors& descriptors + ) override; + void writeCpuRegisters(const Targets::TargetRegisterDescriptorAndValuePairs& registers) override; + + Targets::TargetMemoryBuffer readMemory( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, + Targets::TargetMemoryAddress startAddress, + Targets::TargetMemorySize bytes, + const std::set& excludedAddressRanges = {} + ) override; + void writeMemory( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, + Targets::TargetMemoryAddress startAddress, + const Targets::TargetMemoryBuffer& buffer + ) override; + + private: + DebugTransportModuleInterface& dtmInterface; + + const ::Targets::RiscV::TargetDescriptionFile& targetDescriptionFile; + const ::Targets::RiscV::RiscVTargetConfig& targetConfig; + + std::vector hartIndices; + DebugModule::HartIndex selectedHartIndex = 0; + + std::vector discoverHartIndices(); + + DebugModule::Registers::ControlRegister readDebugModuleControlRegister(); + DebugModule::Registers::StatusRegister readDebugModuleStatusRegister(); + DebugModule::Registers::AbstractControlStatusRegister readDebugModuleAbstractControlStatusRegister(); + Registers::DebugControlStatusRegister readDebugControlStatusRegister(); + + void enableDebugModule(); + void disableDebugModule(); + + RegisterValue readCpuRegister(RegisterNumber number); + void writeCpuRegister(RegisterNumber number, RegisterValue value); + + void writeDebugModuleControlRegister(const DebugModule::Registers::ControlRegister& controlRegister); + void writeDebugControlStatusRegister(const Registers::DebugControlStatusRegister& controlRegister); + + void executeAbstractCommand(const DebugModule::Registers::AbstractCommandRegister& abstractCommandRegister); + + Targets::TargetMemoryAddress alignMemoryAddress( + Targets::TargetMemoryAddress address, + Targets::TargetMemoryAddress alignTo + ); + Targets::TargetMemorySize alignMemorySize(Targets::TargetMemorySize size, Targets::TargetMemorySize alignTo); + }; +} diff --git a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTransportModuleInterface.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTransportModuleInterface.hpp new file mode 100644 index 00000000..0458ac7c --- /dev/null +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTransportModuleInterface.hpp @@ -0,0 +1,71 @@ +#pragma once + +#include +#include + +#include "RiscVGeneric.hpp" +#include "DebugModule/DebugModule.hpp" +#include "DebugModule/Registers/RegisterAddresses.hpp" + +namespace DebugToolDrivers::Protocols::RiscVDebugSpec +{ + /** + * Provides access to the RISC-V target's debug transport module (DTM). + */ + class DebugTransportModuleInterface + { + public: + /** + * Should prepare for and then activate the physical interface between the debug tool and the RISC-V target. + * + * Should throw an exception if activation fails. The error will be considered fatal, and result in a shutdown. + * + * Unless otherwise stated, it can be assumed that this function will be called (and must succeed) + * before any of the other functions below this point are called. In other words, we can assume that the + * interface has been activated in the implementations of any of the functions below this point. + */ + virtual void activate() = 0; + + /** + * Should deactivate the physical interface between the debug tool and the RISC-V target. + * + * CAUTION: This function **CAN** be called before activate(), or in instances where activate() failed (threw + * an exception). Implementations must accommodate this. + */ + virtual void deactivate() = 0; + + /** + * Should read the value of a debug module register. + * + * @param address + * + * @return + */ + virtual DebugModule::RegisterValue readDebugModuleRegister(DebugModule::RegisterAddress address) = 0; + + DebugModule::RegisterValue readDebugModuleRegister(DebugModule::Registers::RegisterAddress address) { + return this->readDebugModuleRegister(static_cast(address)); + }; + + /** + * Should write a value to a debug module register. + * + * @param address + * @param value + */ + virtual void writeDebugModuleRegister( + DebugModule::RegisterAddress address, + DebugModule::RegisterValue value + ) = 0; + + void writeDebugModuleRegister( + DebugModule::Registers::RegisterAddress address, + DebugModule::RegisterValue value + ) { + return this->writeDebugModuleRegister( + static_cast(address), + value + ); + }; + }; +} diff --git a/src/DebugToolDrivers/Protocols/RiscVDebugSpec/Registers/CpuRegisterNumbers.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/Registers/CpuRegisterNumbers.hpp new file mode 100644 index 00000000..d69ccd08 --- /dev/null +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/Registers/CpuRegisterNumbers.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/RiscVGeneric.hpp" + +namespace DebugToolDrivers::Protocols::RiscVDebugSpec::Registers +{ + enum class CpuRegisterNumber: RegisterNumber + { + DEBUG_CONTROL_STATUS_REGISTER = 0x07b0, + }; +} diff --git a/src/Targets/RiscV/Registers/DebugControlStatusRegister.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/Registers/DebugControlStatusRegister.hpp similarity index 95% rename from src/Targets/RiscV/Registers/DebugControlStatusRegister.hpp rename to src/DebugToolDrivers/Protocols/RiscVDebugSpec/Registers/DebugControlStatusRegister.hpp index 4c1d8625..05ec272c 100644 --- a/src/Targets/RiscV/Registers/DebugControlStatusRegister.hpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/Registers/DebugControlStatusRegister.hpp @@ -2,9 +2,9 @@ #include -#include "src/Targets/RiscV/RiscVGeneric.hpp" +#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/RiscVGeneric.hpp" -namespace Targets::RiscV::Registers +namespace DebugToolDrivers::Protocols::RiscVDebugSpec::Registers { struct DebugControlStatusRegister { diff --git a/src/Targets/RiscV/RiscVGeneric.hpp b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/RiscVGeneric.hpp similarity index 82% rename from src/Targets/RiscV/RiscVGeneric.hpp rename to src/DebugToolDrivers/Protocols/RiscVDebugSpec/RiscVGeneric.hpp index c6c09b91..acaf102e 100644 --- a/src/Targets/RiscV/RiscVGeneric.hpp +++ b/src/DebugToolDrivers/Protocols/RiscVDebugSpec/RiscVGeneric.hpp @@ -2,7 +2,7 @@ #include -namespace Targets::RiscV +namespace DebugToolDrivers::Protocols::RiscVDebugSpec { using RegisterValue = std::uint32_t; using RegisterNumber = std::uint16_t; diff --git a/src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AVR8/Avr8DebugInterface.hpp b/src/DebugToolDrivers/TargetInterfaces/Microchip/AVR8/Avr8DebugInterface.hpp similarity index 54% rename from src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AVR8/Avr8DebugInterface.hpp rename to src/DebugToolDrivers/TargetInterfaces/Microchip/AVR8/Avr8DebugInterface.hpp index b11a0385..f6508c72 100644 --- a/src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AVR8/Avr8DebugInterface.hpp +++ b/src/DebugToolDrivers/TargetInterfaces/Microchip/AVR8/Avr8DebugInterface.hpp @@ -2,21 +2,21 @@ #include #include +#include #include -#include "src/Targets/Microchip/AVR/AVR8/Avr8TargetConfig.hpp" - -#include "src/Targets/Microchip/AVR/TargetSignature.hpp" -#include "src/Targets/Microchip/AVR/AVR8/Family.hpp" -#include "src/Targets/Microchip/AVR/AVR8/ProgramMemorySection.hpp" -#include "src/Targets/Microchip/AVR/AVR8/TargetParameters.hpp" +#include "src/Targets/Microchip/AVR8/TargetSignature.hpp" +#include "src/Targets/Microchip/AVR8/Family.hpp" +#include "src/Targets/Microchip/AVR8/ProgramMemorySection.hpp" +#include "src/Targets/TargetDescriptor.hpp" +#include "src/Targets/TargetAddressSpaceDescriptor.hpp" +#include "src/Targets/TargetMemorySegmentDescriptor.hpp" #include "src/Targets/TargetState.hpp" #include "src/Targets/TargetRegisterDescriptor.hpp" -#include "src/Targets/TargetRegister.hpp" #include "src/Targets/TargetMemory.hpp" -namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8 +namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr8 { /** * Interfacing with an AVR8 target for debugging operations can vary significantly, depending on the debug tool @@ -41,40 +41,17 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8 Avr8DebugInterface& operator = (const Avr8DebugInterface& other) = default; Avr8DebugInterface& operator = (Avr8DebugInterface&& other) = default; - /** - * Should initialise the interface between the debug tool and the AVR8 target. - */ virtual void init() = 0; - - /** - * Should stop execution on that target. - */ virtual void stop() = 0; - - /** - * Should resume execution on the AVR8 target. - */ virtual void run() = 0; - - /** - * Continue execution up to a specific byte address. - */ virtual void runTo(Targets::TargetMemoryAddress address) = 0; - - /** - * Step execution on teh AVR8 target. - */ virtual void step() = 0; - - /** - * Should reset the AVR8 target. - */ virtual void reset() = 0; /** * Should activate the physical interface between the debug tool and the AVR8 target. * - * If the debugWire interface has been selected - this function should throw a DebugWirePhysicalInterfaceError + * If the debugWIRE interface has been selected - this function should throw a DebugWirePhysicalInterfaceError * exception, in the event of a failure when activating the interface. The reason for this is to allow us the * chance to check the DWEN fuse bit, via an ISP interface. See Avr8::activate() for more. */ @@ -85,6 +62,30 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8 */ virtual void deactivate() = 0; + /** + * We can specify access restrictions for individual registers in our TDFs, but this is only at a target + * level - it does not account for any restrictions that the debug interface may be subject to. + * + * For example, EDBG debug tools cannot access fuse registers on JTAG targets, during a debug session. Those + * registers can only be accessed during a programming session. This restriction is specific to the EDBG debug + * interface. + * + * This function should communicate any access restrictions for the given register, which apply during a debug + * session. It does not need to account for any access restrictions that only apply outside of a debug session. + * + * @param registerDescriptor + * The descriptor of the register. + * + * @param addressSpaceDescriptor + * The descriptor of the address space in which the register resides. + * + * @return + */ + virtual Targets::TargetRegisterAccess getRegisterAccess( + const Targets::TargetRegisterDescriptor& registerDescriptor, + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor + ) = 0; + /** * Should retrieve the AVR8 target signature of the AVR8 target. * @@ -93,97 +94,29 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8 * * @return */ - virtual Targets::Microchip::Avr::TargetSignature getDeviceId() = 0; + virtual Targets::Microchip::Avr8::TargetSignature getDeviceId() = 0; - /** - * Should set a software breakpoint at a given address. - * - * @param address - */ virtual void setSoftwareBreakpoint(Targets::TargetMemoryAddress address) = 0; - - /** - * Should remove a software breakpoint at a given address. - * - * @param address - */ virtual void clearSoftwareBreakpoint(Targets::TargetMemoryAddress address) = 0; - - /** - * Should set a hardware breakpoint at a given address. - * - * @param address - */ virtual void setHardwareBreakpoint(Targets::TargetMemoryAddress address) = 0; - - /** - * Should remove a hardware breakpoint at a given address. - * - * @param address - */ virtual void clearHardwareBreakpoint(Targets::TargetMemoryAddress address) = 0; - - /** - * Should remove all software and hardware breakpoints on the target. - */ virtual void clearAllBreakpoints() = 0; - - /** - * Should retrieve the current program counter value from the target. - * - * @return - */ virtual Targets::TargetMemoryAddress getProgramCounter() = 0; - - /** - * Should update the program counter value on the target. - * - * @param programCounter - */ virtual void setProgramCounter(Targets::TargetMemoryAddress programCounter) = 0; - - /** - * Should read the requested registers from the target. - * - * @param descriptorIds - * A collection of register descriptor IDs, for the registers to be read. - * - * @return - */ - virtual Targets::TargetRegisters readRegisters(const Targets::TargetRegisterDescriptorIds& descriptorIds) = 0; - - /** - * Should update the value of the given registers. - * - * @param registers - */ - virtual void writeRegisters(const Targets::TargetRegisters& registers) = 0; - - /** - * Should read memory from the target, for the given memory type. - * - * @param memoryType - * @param startAddress - * @param bytes - * @param excludedAddressRanges - * @return - */ + virtual Targets::TargetRegisterDescriptorAndValuePairs readRegisters( + const Targets::TargetRegisterDescriptors& descriptors + ) = 0; + virtual void writeRegisters(const Targets::TargetRegisterDescriptorAndValuePairs& registers) = 0; virtual Targets::TargetMemoryBuffer readMemory( - Targets::TargetMemoryType memoryType, + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, Targets::TargetMemoryAddress startAddress, Targets::TargetMemorySize bytes, const std::set& excludedAddressRanges = {} ) = 0; - - /** - * Should write memory to the target, for a given memory type. - * - * @param memoryType - * @param startAddress - * @param buffer - */ virtual void writeMemory( - Targets::TargetMemoryType memoryType, + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, Targets::TargetMemoryAddress startAddress, const Targets::TargetMemoryBuffer& buffer ) = 0; @@ -195,29 +128,12 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8 * The section to erase, or std::nullopt to erase the entire program memory. */ virtual void eraseProgramMemory( - std::optional section = std::nullopt + std::optional section = std::nullopt ) = 0; - /** - * Should erase the chip. - */ virtual void eraseChip() = 0; - - /** - * Should obtain the current target state. - * - * @return - */ - virtual Targets::TargetState getTargetState() = 0; - - /** - * Should prepare the debug interface for programming the target. - */ + virtual Targets::TargetExecutionState getExecutionState() = 0; virtual void enableProgrammingMode() = 0; - - /** - * Should prepare the debug interface for resuming debugging operations after a programming session. - */ virtual void disableProgrammingMode() = 0; }; } diff --git a/src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AvrIspInterface.hpp b/src/DebugToolDrivers/TargetInterfaces/Microchip/AVR8/AvrIspInterface.hpp similarity index 62% rename from src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AvrIspInterface.hpp rename to src/DebugToolDrivers/TargetInterfaces/Microchip/AVR8/AvrIspInterface.hpp index e67301a5..196aa1e9 100644 --- a/src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AvrIspInterface.hpp +++ b/src/DebugToolDrivers/TargetInterfaces/Microchip/AVR8/AvrIspInterface.hpp @@ -2,13 +2,13 @@ #include -#include "src/Targets/Microchip/AVR/TargetSignature.hpp" -#include "src/Targets/Microchip/AVR/IspParameters.hpp" -#include "src/Targets/Microchip/AVR/Fuse.hpp" +#include "src/Targets/Microchip/AVR8/TargetSignature.hpp" +#include "src/Targets/Microchip/AVR8/IspParameters.hpp" +#include "src/Targets/Microchip/AVR8/Fuse.hpp" #include "src/ProjectConfig.hpp" -namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr +namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr8 { /** * Many AVRs can be programmed via an SPI interface. Some debug tools provide access to this interface via the AVR @@ -17,8 +17,8 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr * This interface class is incomplete - it only provides the ability to read the device ID and access AVR fuses and * lockbit bytes (as that's all we need, for now). * - * Currently, Bloom only uses the ISP interface for accessing fuses and lockbits on debugWire targets. We can't - * access fuses via the debugWire interface, so we have to use the ISP interface. + * Currently, Bloom only uses the ISP interface for accessing fuses and lockbits on debugWIRE targets. We can't + * access fuses via the debugWIRE interface, so we have to use the ISP interface. * * @see Avr8::updateDwenFuseBit() for more. */ @@ -34,20 +34,6 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr AvrIspInterface& operator = (const AvrIspInterface& other) = default; AvrIspInterface& operator = (AvrIspInterface&& other) = default; - /** - * Configures the ISP interface with user-provided config parameters. - * - * @param targetConfig - */ - virtual void configure(const TargetConfig& targetConfig) = 0; - - /** - * Configures the ISP interface with the target's ISP parameters. - * - * @param ispParameters - */ - virtual void setIspParameters(const Targets::Microchip::Avr::IspParameters& ispParameters) = 0; - /** * Should initialise and activate the ISP interface between the debug tool and the AVR target. */ @@ -63,15 +49,17 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr * * @return */ - virtual Targets::Microchip::Avr::TargetSignature getDeviceId() = 0; + virtual Targets::Microchip::Avr8::TargetSignature getDeviceId() = 0; /** * Should read a fuse from the AVR target. * - * @param fuseType + * @param fuseRegisterDescriptor * @return */ - virtual Targets::Microchip::Avr::Fuse readFuse(Targets::Microchip::Avr::FuseType fuseType) = 0; + virtual Targets::Microchip::Avr8::FuseValue readFuse( + const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor + ) = 0; /** * Should read the lock bit byte from the AVR target. @@ -83,8 +71,12 @@ namespace DebugToolDrivers::TargetInterfaces::Microchip::Avr /** * Should program a particular fuse byte. * - * @param fuse + * @param fuseRegisterDescriptor + * @param value */ - virtual void programFuse(Targets::Microchip::Avr::Fuse fuse) = 0; + virtual void programFuse( + const Targets::TargetRegisterDescriptor& fuseRegisterDescriptor, + Targets::Microchip::Avr8::FuseValue value + ) = 0; }; } diff --git a/src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVDebugInterface.hpp b/src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVDebugInterface.hpp index e16796ea..392d706c 100644 --- a/src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVDebugInterface.hpp +++ b/src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVDebugInterface.hpp @@ -1,99 +1,57 @@ #pragma once #include +#include +#include +#include -#include "src/Targets/RiscV/RiscVGeneric.hpp" -#include "src/Targets/RiscV/DebugModule/DebugModule.hpp" -#include "src/Targets/RiscV/DebugModule/Registers/RegisterAddresses.hpp" - -#include "src/Targets/RiscV/TargetParameters.hpp" +#include "src/Targets/TargetDescriptor.hpp" +#include "src/Targets/TargetAddressSpaceDescriptor.hpp" +#include "src/Targets/TargetMemorySegmentDescriptor.hpp" +#include "src/Targets/TargetState.hpp" +#include "src/Targets/TargetRegisterDescriptor.hpp" +#include "src/Targets/TargetMemory.hpp" namespace DebugToolDrivers::TargetInterfaces::RiscV { class RiscVDebugInterface { public: - RiscVDebugInterface() = default; - virtual ~RiscVDebugInterface() = default; - - RiscVDebugInterface(const RiscVDebugInterface& other) = default; - RiscVDebugInterface(RiscVDebugInterface&& other) = default; - - RiscVDebugInterface& operator = (const RiscVDebugInterface& other) = default; - RiscVDebugInterface& operator = (RiscVDebugInterface&& other) = default; - - /** - * Should prepare for and then activate the physical interface between the debug tool and the RISC-V target. - * - * Should throw an exception if activation fails. The error will be considered fatal, and result in a shutdown. - * - * Unless otherwise stated, it can be assumed that this function will be called (and must succeed) - * before any of the other functions below this point are called. In other words, we can assume that the - * interface has been activated in the implementations of any of the functions below this point. - * - * @param targetParameters - * Parameters for the RISC-V target. These can be ignored if a particular implementation does not require - * any target parameters for activation. - */ - virtual void activate(const Targets::RiscV::TargetParameters& targetParameters) = 0; - - /** - * Should deactivate the physical interface between the debug tool and the RISC-V target. - * - * CAUTION: This function **CAN** be called before activate(), or in instances where activate() failed (threw - * an exception). Implementations must accommodate this. - */ + virtual void init() = 0; + virtual void activate() = 0; virtual void deactivate() = 0; - /** - * Should retrieve the RISC-V target ID in string form. - * - * @return - * The target ID, in the form of a string. - */ - virtual std::string getDeviceId() = 0; + virtual Targets::TargetExecutionState getExecutionState() = 0; - /** - * Should read the value of a debug module register. - * - * @param address - * The address of the debug module register to read. - * - * @return - * The register value. - */ - virtual Targets::RiscV::DebugModule::RegisterValue readDebugModuleRegister( - Targets::RiscV::DebugModule::RegisterAddress address + virtual void stop() = 0; + virtual void run() = 0; + virtual void step() = 0; + virtual void reset() = 0; + + virtual void setSoftwareBreakpoint(Targets::TargetMemoryAddress address) = 0; + virtual void clearSoftwareBreakpoint(Targets::TargetMemoryAddress address) = 0; + + virtual void setHardwareBreakpoint(Targets::TargetMemoryAddress address) = 0; + virtual void clearHardwareBreakpoint(Targets::TargetMemoryAddress address) = 0; + virtual void clearAllBreakpoints() = 0; + + virtual Targets::TargetRegisterDescriptorAndValuePairs readCpuRegisters( + const Targets::TargetRegisterDescriptors& descriptors ) = 0; + virtual void writeCpuRegisters(const Targets::TargetRegisterDescriptorAndValuePairs& registers) = 0; - Targets::RiscV::DebugModule::RegisterValue readDebugModuleRegister( - Targets::RiscV::DebugModule::Registers::RegisterAddress address - ) { - return this->readDebugModuleRegister(static_cast(address)); - }; - - /** - * Should write a value to a debug module register. - * - * @param address - * The address of the debug module to update. - * - * @param value - * The value to write. - */ - virtual void writeDebugModuleRegister( - Targets::RiscV::DebugModule::RegisterAddress address, - Targets::RiscV::DebugModule::RegisterValue value + virtual Targets::TargetMemoryBuffer readMemory( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, + Targets::TargetMemoryAddress startAddress, + Targets::TargetMemorySize bytes, + const std::set& excludedAddressRanges = {} + ) = 0; + virtual void writeMemory( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, + Targets::TargetMemoryAddress startAddress, + const Targets::TargetMemoryBuffer& buffer ) = 0; - - void writeDebugModuleRegister( - Targets::RiscV::DebugModule::Registers::RegisterAddress address, - Targets::RiscV::DebugModule::RegisterValue value - ) { - return this->writeDebugModuleRegister( - static_cast(address), - value - ); - }; }; } diff --git a/src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVIdentificationInterface.hpp b/src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVIdentificationInterface.hpp new file mode 100644 index 00000000..7482bf42 --- /dev/null +++ b/src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVIdentificationInterface.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace DebugToolDrivers::TargetInterfaces::RiscV +{ + class RiscVIdentificationInterface + { + public: + /** + * Should retrieve the RISC-V target ID in string form. + * + * @return + * The target ID, in the form of a string. + */ + virtual std::string getDeviceId() = 0; + }; +} diff --git a/src/DebugToolDrivers/TargetInterfaces/TargetPowerManagementInterface.hpp b/src/DebugToolDrivers/TargetInterfaces/TargetPowerManagementInterface.hpp index b266f1fe..d940f072 100644 --- a/src/DebugToolDrivers/TargetInterfaces/TargetPowerManagementInterface.hpp +++ b/src/DebugToolDrivers/TargetInterfaces/TargetPowerManagementInterface.hpp @@ -1,7 +1,5 @@ #pragma once -#include - namespace DebugToolDrivers::TargetInterfaces { /** @@ -14,20 +12,13 @@ namespace DebugToolDrivers::TargetInterfaces TargetPowerManagementInterface() = default; virtual ~TargetPowerManagementInterface() = default; - TargetPowerManagementInterface(const TargetPowerManagementInterface& other) = default; - TargetPowerManagementInterface(TargetPowerManagementInterface&& other) = default; + TargetPowerManagementInterface(const TargetPowerManagementInterface& other) = delete; + TargetPowerManagementInterface& operator = (const TargetPowerManagementInterface& other) = delete; - TargetPowerManagementInterface& operator = (const TargetPowerManagementInterface& other) = default; + TargetPowerManagementInterface(TargetPowerManagementInterface&& other) = default; TargetPowerManagementInterface& operator = (TargetPowerManagementInterface&& other) = default; - /** - * Should enable the target power if currently disabled. - */ virtual void enableTargetPower() = 0; - - /** - * Should disable the target power if currently enabled. - */ virtual void disableTargetPower() = 0; }; } diff --git a/src/DebugToolDrivers/USB/HID/HidInterface.cpp b/src/DebugToolDrivers/USB/HID/HidInterface.cpp index db9f3a88..0d671b6b 100644 --- a/src/DebugToolDrivers/USB/HID/HidInterface.cpp +++ b/src/DebugToolDrivers/USB/HID/HidInterface.cpp @@ -29,7 +29,7 @@ namespace Usb Logger::debug("HID device path: " + hidInterfacePath); if ((hidDevice = ::hid_open_path(hidInterfacePath.c_str())) == nullptr) { - throw DeviceInitializationFailure("Failed to open HID device via hidapi."); + throw DeviceInitializationFailure{"Failed to open HID device via hidapi."}; } this->hidDevice.reset(hidDevice); @@ -41,11 +41,11 @@ namespace Usb } std::vector HidInterface::read(std::optional timeout) { - auto output = std::vector(); + auto output = std::vector{}; const auto readSize = this->inputReportSize; auto transferredByteCount = int(0); - auto totalByteCount = std::size_t(0); + auto totalByteCount = std::size_t{0}; do { output.resize(totalByteCount + readSize, 0x00); @@ -58,12 +58,12 @@ namespace Usb ); if (transferredByteCount == -1) { - throw DeviceCommunicationFailure("Failed to read from HID device."); + throw DeviceCommunicationFailure{"Failed to read from HID device."}; } if (totalByteCount == 0) { // After the first read, set the timeout to 1 millisecond, as we don't want to wait for subsequent data - timeout = std::chrono::milliseconds(1); + timeout = std::chrono::milliseconds{1}; } totalByteCount += static_cast(transferredByteCount); @@ -76,9 +76,7 @@ namespace Usb void HidInterface::write(std::vector&& buffer) { if (buffer.size() > this->inputReportSize) { - throw DeviceCommunicationFailure( - "Cannot send data via HID interface - data exceeds maximum packet size." - ); + throw DeviceCommunicationFailure{"Cannot send data via HID interface - data exceeds maximum packet size."}; } if (buffer.size() < this->inputReportSize) { @@ -93,9 +91,11 @@ namespace Usb const auto length = buffer.size(); if ((transferred = ::hid_write(this->hidDevice.get(), buffer.data(), length)) != length) { - Logger::debug("Attempted to write " + std::to_string(length) - + " bytes to HID interface. Bytes written: " + std::to_string(transferred)); - throw DeviceCommunicationFailure("Failed to write data to HID interface."); + Logger::debug( + "Attempted to write " + std::to_string(length) + + " bytes to HID interface. Bytes written: " + std::to_string(transferred) + ); + throw DeviceCommunicationFailure{"Failed to write data to HID interface."}; } } @@ -105,7 +105,7 @@ namespace Usb ::hid_free_enumeration ); - auto matchedDevice = std::optional<::hid_device_info*>(); + auto matchedDevice = std::optional<::hid_device_info*>{}; auto* hidDeviceInfo = hidDeviceInfoList.get(); while (hidDeviceInfo != nullptr) { @@ -118,9 +118,9 @@ namespace Usb } if (!matchedDevice.has_value()) { - throw DeviceInitializationFailure("Failed to match interface number with HID interface."); + throw DeviceInitializationFailure{"Failed to match interface number with HID interface."}; } - return std::string(matchedDevice.value()->path); + return {matchedDevice.value()->path}; } } diff --git a/src/DebugToolDrivers/USB/HID/HidInterface.hpp b/src/DebugToolDrivers/USB/HID/HidInterface.hpp index ce28caf0..f8aa0d0a 100644 --- a/src/DebugToolDrivers/USB/HID/HidInterface.hpp +++ b/src/DebugToolDrivers/USB/HID/HidInterface.hpp @@ -69,7 +69,7 @@ namespace Usb private: using HidDevice = std::unique_ptr<::hid_device, decltype(&::hid_close)>; - HidDevice hidDevice = HidDevice(nullptr, ::hid_close); + HidDevice hidDevice = {nullptr, ::hid_close}; std::uint16_t vendorId = 0; std::uint16_t productId = 0; diff --git a/src/DebugToolDrivers/USB/UsbDevice.cpp b/src/DebugToolDrivers/USB/UsbDevice.cpp index c45e1c06..8cf1a7c6 100644 --- a/src/DebugToolDrivers/USB/UsbDevice.cpp +++ b/src/DebugToolDrivers/USB/UsbDevice.cpp @@ -30,18 +30,18 @@ namespace Usb auto devices = this->findMatchingDevices(this->vendorId, this->productId); if (devices.empty()) { - throw DeviceNotFound( + throw DeviceNotFound{ "Failed to find USB device with matching vendor and product ID. Please examine the debug tool's USB " - "connection, as well as the selected environment's debug tool configuration, in bloom.yaml" - ); + "connection, as well as the selected environment's debug tool configuration, in bloom.yaml" + }; } if (devices.size() > 1) { // TODO: implement support for multiple devices via serial number matching? - throw DeviceInitializationFailure( + throw DeviceInitializationFailure{ "Numerous devices of matching vendor and product ID found.\n" - "Please ensure that only one debug tool is connected and then try again." - ); + "Please ensure that only one debug tool is connected and then try again." + }; } // For now, just use the first device found. @@ -51,9 +51,9 @@ namespace Usb const int libusbStatusCode = ::libusb_open(this->libusbDevice.get(), &deviceHandle); if (libusbStatusCode < 0) { - throw DeviceInitializationFailure( + throw DeviceInitializationFailure{ "Failed to open USB device - error code " + std::to_string(libusbStatusCode) + " returned." - ); + }; } this->libusbDeviceHandle.reset(deviceHandle); @@ -65,12 +65,12 @@ namespace Usb auto statusCode = ::libusb_get_device_descriptor(this->libusbDevice.get(), &desc); if (statusCode != 0) { - throw DeviceCommunicationFailure( + throw DeviceCommunicationFailure{ "Failed to retrieve USB device descriptor - status code: " + std::to_string(statusCode) - ); + }; } - auto data = std::array(); + auto data = std::array{}; const auto transferredBytes = ::libusb_get_string_descriptor_ascii( this->libusbDeviceHandle.get(), desc.iSerialNumber, @@ -79,12 +79,12 @@ namespace Usb ); if (transferredBytes <= 0) { - throw DeviceCommunicationFailure( + throw DeviceCommunicationFailure{ "Failed to retrieve serial number from USB device - status code: " + std::to_string(transferredBytes) - ); + }; } - return std::string(data.begin(), data.begin() + transferredBytes); + return {data.begin(), data.begin() + transferredBytes}; } void UsbDevice::setConfiguration(std::uint8_t configurationIndex) { @@ -96,9 +96,9 @@ namespace Usb ); if (libusbStatusCode < 0) { - throw DeviceInitializationFailure( + throw DeviceInitializationFailure{ "Failed to set USB configuration - error code " + std::to_string(libusbStatusCode) + " returned." - ); + }; } } @@ -124,7 +124,7 @@ namespace Usb } } - throw DeviceInitializationFailure("Failed to obtain address of USB endpoint"); + throw DeviceInitializationFailure{"Failed to obtain address of USB endpoint"}; } std::uint16_t UsbDevice::getEndpointMaxPacketSize(std::uint8_t endpointAddress) { @@ -142,11 +142,11 @@ namespace Usb } } - throw DeviceInitializationFailure( + throw DeviceInitializationFailure{ "Failed to obtain maximum packet size of USB endpoint (address: 0x" + Services::StringService::toHex(endpointAddress) + "). Endpoint not found. Selected configuration " "value (" + std::to_string(activeConfigDescriptor->bConfigurationValue) + ")" - ); + }; } std::vector UsbDevice::findMatchingDevices(std::uint16_t vendorId, std::uint16_t productId) { @@ -156,9 +156,9 @@ namespace Usb auto libusbStatusCode = ::libusb_get_device_list(UsbDevice::libusbContext.get(), &devices); if (libusbStatusCode < 0) { - throw DeviceInitializationFailure( + throw DeviceInitializationFailure{ "Failed to retrieve USB devices - return code: '" + std::to_string(libusbStatusCode) + "'" - ); + }; } ssize_t i = 0; @@ -167,8 +167,10 @@ namespace Usb struct ::libusb_device_descriptor desc = {}; if ((libusbStatusCode = ::libusb_get_device_descriptor(device, &desc)) < 0) { - Logger::warning("Failed to retrieve USB device descriptor - return code: '" - + std::to_string(libusbStatusCode) + "'"); + Logger::warning( + "Failed to retrieve USB device descriptor - return code: '" + + std::to_string(libusbStatusCode) + "'" + ); continue; } @@ -191,16 +193,13 @@ namespace Usb : ::libusb_get_active_config_descriptor(this->libusbDevice.get(), &configDescriptor); if (libusbStatusCode < 0) { - throw DeviceInitializationFailure( + throw DeviceInitializationFailure{ "Failed to obtain USB configuration descriptor - error code " + std::to_string(libusbStatusCode) + " returned." - ); + }; } - return LibusbConfigDescriptor( - configDescriptor, - ::libusb_free_config_descriptor - ); + return {configDescriptor, ::libusb_free_config_descriptor}; } void UsbDevice::detachKernelDriverFromInterface(std::uint8_t interfaceNumber) { @@ -214,7 +213,7 @@ namespace Usb } } else if (libusbStatusCode != 0) { - throw DeviceInitializationFailure("Failed to check for active kernel driver on USB interface."); + throw DeviceInitializationFailure{"Failed to check for active kernel driver on USB interface."}; } } diff --git a/src/DebugToolDrivers/USB/UsbDevice.hpp b/src/DebugToolDrivers/USB/UsbDevice.hpp index acccdea1..c3e07044 100644 --- a/src/DebugToolDrivers/USB/UsbDevice.hpp +++ b/src/DebugToolDrivers/USB/UsbDevice.hpp @@ -64,10 +64,10 @@ namespace Usb virtual ~UsbDevice(); protected: - static inline LibusbContext libusbContext = LibusbContext(nullptr, ::libusb_exit); + static inline LibusbContext libusbContext = {nullptr, ::libusb_exit}; - LibusbDevice libusbDevice = LibusbDevice(nullptr, ::libusb_unref_device); - LibusbDeviceHandle libusbDeviceHandle = LibusbDeviceHandle(nullptr, ::libusb_close); + LibusbDevice libusbDevice = {nullptr, ::libusb_unref_device}; + LibusbDeviceHandle libusbDeviceHandle = {nullptr, ::libusb_close}; std::vector findMatchingDevices(std::uint16_t vendorId, std::uint16_t productId); diff --git a/src/DebugToolDrivers/USB/UsbInterface.cpp b/src/DebugToolDrivers/USB/UsbInterface.cpp index 2278116d..897e7ebd 100644 --- a/src/DebugToolDrivers/USB/UsbInterface.cpp +++ b/src/DebugToolDrivers/USB/UsbInterface.cpp @@ -24,10 +24,10 @@ namespace Usb const auto statusCode = ::libusb_claim_interface(this->deviceHandle, this->interfaceNumber); if (statusCode != 0) { - throw DeviceInitializationFailure( + throw DeviceInitializationFailure{ "Failed to claim USB interface (number: " + std::to_string(this->interfaceNumber) + ") - error code: " + std::to_string(statusCode) - ); + }; } this->claimed = true; @@ -49,11 +49,11 @@ namespace Usb std::uint8_t endpointAddress, std::optional timeout ) { - auto output = std::vector(); + auto output = std::vector{}; constexpr auto transferSize = 512; - auto bytesTransferred = int(0); - auto totalByteCount = std::size_t(0); + auto bytesTransferred = int{0}; + auto totalByteCount = std::size_t{0}; do { output.resize(totalByteCount + transferSize, 0x00); @@ -68,12 +68,12 @@ namespace Usb ); if (statusCode != 0) { - throw DeviceCommunicationFailure("Failed to read from bulk endpoint"); + throw DeviceCommunicationFailure{"Failed to read from bulk endpoint"}; } if (totalByteCount == 0) { // After the first read, set the timeout to 1 millisecond, as we don't want to wait for subsequent data - timeout = std::chrono::milliseconds(1); + timeout = std::chrono::milliseconds{1}; } totalByteCount += static_cast(bytesTransferred); @@ -86,11 +86,11 @@ namespace Usb void UsbInterface::writeBulk(std::uint8_t endpointAddress, std::vector&& buffer) { if (buffer.size() > std::numeric_limits::max()) { - throw DeviceCommunicationFailure("Attempted to send too much data to bulk endpoint"); + throw DeviceCommunicationFailure{"Attempted to send too much data to bulk endpoint"}; } const auto length = static_cast(buffer.size()); - auto bytesTransferred = int(0); + auto bytesTransferred = int{0}; const auto statusCode = ::libusb_bulk_transfer( this->deviceHandle, @@ -106,7 +106,7 @@ namespace Usb "Attempted to write " + std::to_string(length) + " bytes to USB bulk endpoint. Bytes written: " + std::to_string(bytesTransferred) + ". Status code: " + std::to_string(statusCode) ); - throw DeviceCommunicationFailure("Failed to write data to bulk endpoint"); + throw DeviceCommunicationFailure{"Failed to write data to bulk endpoint"}; } } } diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp index f78692f8..a0795121 100644 --- a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp @@ -43,7 +43,7 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands rawCommand[0] = 0x81; rawCommand[1] = this->commandId; - rawCommand[2] = static_cast(this->payload.size()); + rawCommand[2] = static_cast(this->payload.size()); if (!this->payload.empty()) { std::copy(this->payload.begin(), this->payload.end(), rawCommand.begin() + 3); diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/DebugModuleInterfaceOperation.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/DebugModuleInterfaceOperation.hpp index 8471620e..3027c165 100644 --- a/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/DebugModuleInterfaceOperation.hpp +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/DebugModuleInterfaceOperation.hpp @@ -6,7 +6,7 @@ #include "src/DebugToolDrivers/WCH/Protocols/WchLink/Commands/Command.hpp" #include "src/DebugToolDrivers/WCH/Protocols/WchLink/Responses/DebugModuleInterfaceOperationResponse.hpp" -#include "src/Targets/RiscV/DebugModule/DebugModule.hpp" +#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp" namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands { @@ -16,9 +16,9 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink::Commands using ExpectedResponseType = Responses::DebugModuleInterfaceOperationResponse; DebugModuleInterfaceOperation( - Targets::RiscV::DebugModule::DmiOperation operation, - Targets::RiscV::DebugModule::RegisterAddress address, - std::optional value = std::nullopt + DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::DmiOperation operation, + DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterAddress address, + std::optional value = std::nullopt ) : Command(0x08) { diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/Responses/DebugModuleInterfaceOperationResponse.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/Responses/DebugModuleInterfaceOperationResponse.hpp index 178d5d63..fb576d84 100644 --- a/src/DebugToolDrivers/WCH/Protocols/WchLink/Responses/DebugModuleInterfaceOperationResponse.hpp +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/Responses/DebugModuleInterfaceOperationResponse.hpp @@ -2,8 +2,7 @@ #include -#include "src/Targets/RiscV/DebugModule/DebugModule.hpp" -#include "src/Helpers/BiMap.hpp" +#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugModule/DebugModule.hpp" #include "src/Services/StringService.hpp" #include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp" @@ -13,32 +12,33 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink::Responses class DebugModuleInterfaceOperationResponse { public: - Targets::RiscV::DebugModule::DmiOperationStatus operationStatus; - Targets::RiscV::DebugModule::RegisterAddress address; - Targets::RiscV::DebugModule::RegisterValue value; + DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::DmiOperationStatus operationStatus; + DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterAddress address; + DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterValue value; + + explicit DebugModuleInterfaceOperationResponse(const std::vector& payload) { + using DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::DmiOperationStatus; - explicit DebugModuleInterfaceOperationResponse(const std::vector& payload) - { if (payload.size() != 6) { - throw Exceptions::DeviceCommunicationFailure( + throw Exceptions::DeviceCommunicationFailure{ "Unexpected response payload size for DMI operation command" - ); + }; } const auto status = payload[5]; if ( - status != static_cast(Targets::RiscV::DebugModule::DmiOperationStatus::SUCCESS) - && status != static_cast(Targets::RiscV::DebugModule::DmiOperationStatus::FAILED) - && status != static_cast(Targets::RiscV::DebugModule::DmiOperationStatus::BUSY) + status != static_cast(DmiOperationStatus::SUCCESS) + && status != static_cast(DmiOperationStatus::FAILED) + && status != static_cast(DmiOperationStatus::BUSY) ) { - throw Exceptions::DeviceCommunicationFailure( + throw Exceptions::DeviceCommunicationFailure{ "Unknown DMI operation status returned: 0x" + Services::StringService::toHex(status) - ); + }; } - this->operationStatus = static_cast(status); + this->operationStatus = static_cast(status); this->address = payload[0]; - this->value = static_cast( + this->value = static_cast( (payload[1] << 24) | (payload[2] << 16) | (payload[3] << 8) | (payload[4]) ); } diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.cpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.cpp index d5238e31..55d0f99a 100644 --- a/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.cpp +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.cpp @@ -18,9 +18,10 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink { + using namespace ::DebugToolDrivers::Protocols::RiscVDebugSpec; using namespace Exceptions; - using Targets::RiscV::DebugModule::DmiOperation; + using DebugModule::DmiOperation; WchLinkInterface::WchLinkInterface(Usb::UsbInterface& usbInterface, Usb::UsbDevice& usbDevice) : usbInterface(usbInterface) @@ -29,34 +30,32 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink {} DeviceInfo WchLinkInterface::getDeviceInfo() { - const auto response = this->sendCommandAndWaitForResponse(Commands::Control::GetDeviceInfo()); - + const auto response = this->sendCommandAndWaitForResponse(Commands::Control::GetDeviceInfo{}); if (response.payload.size() < 3) { - throw Exceptions::DeviceCommunicationFailure("Cannot construct DeviceInfo response - invalid payload"); + throw Exceptions::DeviceCommunicationFailure{"Cannot construct DeviceInfo response - invalid payload"}; } - static const auto variantsById = BiMap({ + static const auto variantsById = BiMap{ {0x01, WchLinkVariant::LINK_CH549}, {0x02, WchLinkVariant::LINK_E_CH32V307}, {0x12, WchLinkVariant::LINK_E_CH32V307}, {0x03, WchLinkVariant::LINK_S_CH32V203}, - }); + }; return DeviceInfo( - WchFirmwareVersion(response.payload[0], response.payload[1]), + WchFirmwareVersion{response.payload[0], response.payload[1]}, response.payload.size() >= 4 - ? std::optional(variantsById.valueAt(response.payload[2]).value_or(WchLinkVariant::UNKNOWN)) + ? std::optional{variantsById.valueAt(response.payload[2]).value_or(WchLinkVariant::UNKNOWN)} : std::nullopt ); } - void WchLinkInterface::activate(const Targets::RiscV::TargetParameters& targetParameters) { + void WchLinkInterface::activate() { this->setClockSpeed(WchLinkTargetClockSpeed::CLK_6000_KHZ); - const auto response = this->sendCommandAndWaitForResponse(Commands::Control::AttachTarget()); - + const auto response = this->sendCommandAndWaitForResponse(Commands::Control::AttachTarget{}); if (response.payload.size() != 5) { - throw Exceptions::DeviceCommunicationFailure("Unexpected response payload size for AttachTarget command"); + throw Exceptions::DeviceCommunicationFailure{"Unexpected response payload size for AttachTarget command"}; } this->cachedTargetId = static_cast( @@ -67,10 +66,9 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink } void WchLinkInterface::deactivate() { - const auto response = this->sendCommandAndWaitForResponse(Commands::Control::DetachTarget()); - + const auto response = this->sendCommandAndWaitForResponse(Commands::Control::DetachTarget{}); if (response.payload.size() != 1) { - throw Exceptions::DeviceCommunicationFailure("Unexpected response payload size for DetachTarget command"); + throw Exceptions::DeviceCommunicationFailure{"Unexpected response payload size for DetachTarget command"}; } } @@ -78,34 +76,32 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink return "0x" + Services::StringService::toHex(this->cachedTargetId.value()); } - Targets::RiscV::DebugModule::RegisterValue WchLinkInterface::readDebugModuleRegister( - Targets::RiscV::DebugModule::RegisterAddress address - ) { - using Targets::RiscV::DebugModule::DmiOperationStatus ; + DebugModule::RegisterValue WchLinkInterface::readDebugModuleRegister(DebugModule::RegisterAddress address) { + using DebugModule::DmiOperationStatus; const auto response = this->sendCommandAndWaitForResponse( - Commands::DebugModuleInterfaceOperation(DmiOperation::READ, address) + Commands::DebugModuleInterfaceOperation{DmiOperation::READ, address} ); if (response.operationStatus != DmiOperationStatus::SUCCESS) { - throw Exceptions::DeviceCommunicationFailure("DMI operation failed"); + throw Exceptions::DeviceCommunicationFailure{"DMI operation failed"}; } return response.value; } void WchLinkInterface::writeDebugModuleRegister( - Targets::RiscV::DebugModule::RegisterAddress address, - Targets::RiscV::DebugModule::RegisterValue value + DebugModule::RegisterAddress address, + DebugModule::RegisterValue value ) { - using Targets::RiscV::DebugModule::DmiOperationStatus ; + using DebugModule::DmiOperationStatus; const auto response = this->sendCommandAndWaitForResponse( - Commands::DebugModuleInterfaceOperation(DmiOperation::WRITE, address, value) + Commands::DebugModuleInterfaceOperation{DmiOperation::WRITE, address, value} ); if (response.operationStatus != DmiOperationStatus::SUCCESS) { - throw Exceptions::DeviceCommunicationFailure("DMI operation failed"); + throw Exceptions::DeviceCommunicationFailure{"DMI operation failed"}; } } @@ -119,36 +115,35 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink std::ceil(static_cast(bufferSize) / static_cast(packetSize)) ); - for (std::uint32_t i = 0; i < packetsRequired; ++i) { + for (auto i = std::uint32_t{0}; i < packetsRequired; ++i) { const auto segmentSize = static_cast(std::min(bufferSize - (i * packetSize), packetSize)); const auto response = this->sendCommandAndWaitForResponse( - Commands::PreparePartialFlashPageWrite(startAddress + (packetSize * i), segmentSize) + Commands::PreparePartialFlashPageWrite{startAddress + (packetSize * i), segmentSize} ); if (response.payload.size() != 1) { - throw Exceptions::DeviceCommunicationFailure( + throw Exceptions::DeviceCommunicationFailure{ "Unexpected response payload size for PreparePartialFlashPageWrite command" - ); + }; } this->usbInterface.writeBulk( WchLinkInterface::USB_DATA_ENDPOINT_OUT, - std::vector( + std::vector{ buffer.begin() + (packetSize * i), buffer.begin() + (packetSize * i) + segmentSize - ) + } ); const auto rawResponse = this->usbInterface.readBulk(WchLinkInterface::USB_DATA_ENDPOINT_IN); if (rawResponse.size() != 4) { - throw Exceptions::DeviceCommunicationFailure("Unexpected response size for partial flash page write"); + throw Exceptions::DeviceCommunicationFailure{"Unexpected response size for partial flash page write"}; } /* - * I have no idea what any of these bytes mean. There's no documentation available for this. - * - * All I know is that these values indicate a successful write. + * I have no idea what any of these bytes mean. I've not been a le to find any documentation for + * this. All I know is that these values indicate a successful write. */ if ( rawResponse[0] != 0x41 @@ -156,28 +151,28 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink || rawResponse[2] != 0x01 || rawResponse[3] != 0x02 ) { - throw Exceptions::DeviceCommunicationFailure("Partial flash page write failed"); + throw Exceptions::DeviceCommunicationFailure{"Partial flash page write failed"}; } } } void WchLinkInterface::setClockSpeed(WchLinkTargetClockSpeed speed) { - const auto speedIdsBySpeed = BiMap({ + const auto speedIdsBySpeed = BiMap{ {WchLinkTargetClockSpeed::CLK_6000_KHZ, 0x01}, {WchLinkTargetClockSpeed::CLK_4000_KHZ, 0x02}, {WchLinkTargetClockSpeed::CLK_400_KHZ, 0x03}, - }); + }; const auto response = this->sendCommandAndWaitForResponse( - Commands::SetClockSpeed(this->cachedTargetGroupId.value_or(0x01), speedIdsBySpeed.at(speed)) + Commands::SetClockSpeed{this->cachedTargetGroupId.value_or(0x01), speedIdsBySpeed.at(speed)} ); if (response.payload.size() != 1) { - throw Exceptions::DeviceCommunicationFailure("Unexpected response payload size for SetClockSpeed command"); + throw Exceptions::DeviceCommunicationFailure{"Unexpected response payload size for SetClockSpeed command"}; } if (response.payload[0] != 0x01) { - throw Exceptions::DeviceCommunicationFailure("Unexpected response for SetClockSpeed command"); + throw Exceptions::DeviceCommunicationFailure{"Unexpected response for SetClockSpeed command"}; } } } diff --git a/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.hpp b/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.hpp index 355766b4..6c45e770 100644 --- a/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.hpp +++ b/src/DebugToolDrivers/WCH/Protocols/WchLink/WchLinkInterface.hpp @@ -5,15 +5,16 @@ #include #include -#include "src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVDebugInterface.hpp" +#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTransportModuleInterface.hpp" #include "src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVProgramInterface.hpp" +#include "src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVIdentificationInterface.hpp" + #include "src/DebugToolDrivers/USB/UsbInterface.hpp" #include "src/DebugToolDrivers/USB/UsbDevice.hpp" #include "src/DebugToolDrivers/WCH/WchGeneric.hpp" #include "Commands/Command.hpp" -#include "src/Targets/RiscV/DebugModule/DebugModule.hpp" #include "src/DebugToolDrivers/WCH/DeviceInfo.hpp" #include "src/TargetController/Exceptions/DeviceCommunicationFailure.hpp" @@ -21,30 +22,33 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink { /** - * The WchLinkInterface implements the WCH-Link protocol. + * Implementation of the WCH-Link protocol, which provides an implementation of a RISC-V DTM interface, a + * programming interface and a target identification interface. + * + * WCH debug tools cannot write to program memory via the target's RISC-V debug module, so we cannot program the + * target via the tool's RISC-V DTM interface. Instead, the WCH-Link protocol provides a dedicated command for + * writing to program memory, which is why this class implements the RISC-V programming interface. + * See WchLinkInterface::writeFlashMemory() for more. */ class WchLinkInterface - : public TargetInterfaces::RiscV::RiscVDebugInterface + : public ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTransportModuleInterface , public TargetInterfaces::RiscV::RiscVProgramInterface + , public TargetInterfaces::RiscV::RiscVIdentificationInterface { public: WchLinkInterface(Usb::UsbInterface& usbInterface, Usb::UsbDevice& usbDevice); DeviceInfo getDeviceInfo(); - - void activate(const Targets::RiscV::TargetParameters& targetParameters) override; - + void activate() override; void deactivate() override; - std::string getDeviceId() override; - Targets::RiscV::DebugModule::RegisterValue readDebugModuleRegister( - Targets::RiscV::DebugModule::RegisterAddress address + ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterValue readDebugModuleRegister( + ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterAddress address ) override; - void writeDebugModuleRegister( - Targets::RiscV::DebugModule::RegisterAddress address, - Targets::RiscV::DebugModule::RegisterValue value + ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterAddress address, + ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugModule::RegisterValue value ) override; void writeFlashMemory( @@ -85,9 +89,9 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink auto rawCommand = command.getRawCommand(); if (rawCommand.size() > this->commandEndpointMaxPacketSize) { - throw Exceptions::DeviceCommunicationFailure( + throw Exceptions::DeviceCommunicationFailure{ "Raw command size exceeds maximum packet size for command endpoint" - ); + }; } this->usbInterface.writeBulk(WchLinkInterface::USB_COMMAND_ENDPOINT_OUT, std::move(rawCommand)); @@ -95,30 +99,30 @@ namespace DebugToolDrivers::Wch::Protocols::WchLink const auto rawResponse = this->usbInterface.readBulk(WchLinkInterface::USB_COMMAND_ENDPOINT_IN); if (rawResponse.size() < 3) { - throw Exceptions::DeviceCommunicationFailure("Invalid response size from device"); + throw Exceptions::DeviceCommunicationFailure{"Invalid response size from device"}; } // The first byte of the response should be 0x82 (for success) or 0x81 (for failure) - if ((rawResponse[0] != 0x81 && rawResponse[0] != 0x82)) { - throw Exceptions::DeviceCommunicationFailure("Invalid response code from device"); + if (rawResponse[0] != 0x81 && rawResponse[0] != 0x82) { + throw Exceptions::DeviceCommunicationFailure{"Invalid response code from device"}; } if (rawResponse[0] == 0x81) { // TODO: Create ErrorResponse exception class and throw an instance of it here. - throw Exceptions::DeviceCommunicationFailure("Error response"); + throw Exceptions::DeviceCommunicationFailure{"Error response"}; } if (rawResponse[1] != command.commandId) { - throw Exceptions::DeviceCommunicationFailure("Missing/invalid command ID in response from device"); + throw Exceptions::DeviceCommunicationFailure{"Missing/invalid command ID in response from device"}; } if ((rawResponse.size() - 3) != rawResponse[2]) { - throw Exceptions::DeviceCommunicationFailure("Actual response payload size mismatch"); + throw Exceptions::DeviceCommunicationFailure{"Actual response payload size mismatch"}; } - return typename CommandType::ExpectedResponseType( - std::vector(rawResponse.begin() + 3, rawResponse.end()) - ); + return typename CommandType::ExpectedResponseType{ + std::vector{rawResponse.begin() + 3, rawResponse.end()} + }; } }; } diff --git a/src/DebugToolDrivers/WCH/WchLinkBase.cpp b/src/DebugToolDrivers/WCH/WchLinkBase.cpp index 28813d07..c3ca48a7 100644 --- a/src/DebugToolDrivers/WCH/WchLinkBase.cpp +++ b/src/DebugToolDrivers/WCH/WchLinkBase.cpp @@ -1,5 +1,7 @@ #include "WchLinkBase.hpp" +#include "Protocols/WchLink/WchLinkInterface.hpp" + #include "src/TargetController/Exceptions/DeviceInitializationFailure.hpp" #include "src/Logger/Logger.hpp" @@ -37,19 +39,25 @@ namespace DebugToolDrivers::Wch ); if (this->getDeviceInfo().variant != this->variant) { - throw DeviceInitializationFailure( + throw DeviceInitializationFailure{ "WCH-Link variant mismatch - device returned variant ID that doesn't match the " + this->getName() + " variant ID" - ); + }; } - this->setInitialised(true); + this->initialised = true; } void WchLinkBase::close() { if (this->wchLinkUsbInterface) { this->wchLinkUsbInterface->close(); } + + this->initialised = false; + } + + bool WchLinkBase::isInitialised() const { + return this->initialised; } std::string WchLinkBase::getSerialNumber() { @@ -60,6 +68,36 @@ namespace DebugToolDrivers::Wch return "v" + this->getDeviceInfo().firmwareVersion.toString(); } + ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslator* WchLinkBase::getRiscVDebugInterface( + const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile, + const Targets::RiscV::RiscVTargetConfig& targetConfig + ) { + using ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslator; + + if (!this->wchRiscVTranslator) { + this->wchRiscVTranslator = std::make_unique( + *(this->wchLinkInterface.get()), + targetDescriptionFile, + targetConfig + ); + } + return this->wchRiscVTranslator.get(); + } + + Protocols::WchLink::WchLinkInterface* WchLinkBase::getRiscVProgramInterface( + const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile, + const Targets::RiscV::RiscVTargetConfig& targetConfig + ) { + return this->wchLinkInterface.get(); + } + + Protocols::WchLink::WchLinkInterface* WchLinkBase::getRiscVIdentificationInterface( + const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile, + const Targets::RiscV::RiscVTargetConfig& targetConfig + ) { + return this->wchLinkInterface.get(); + } + const DeviceInfo& WchLinkBase::getDeviceInfo() const { if (!this->cachedDeviceInfo.has_value()) { this->cachedDeviceInfo = this->wchLinkInterface->getDeviceInfo(); diff --git a/src/DebugToolDrivers/WCH/WchLinkBase.hpp b/src/DebugToolDrivers/WCH/WchLinkBase.hpp index 8db93555..e47c2c29 100644 --- a/src/DebugToolDrivers/WCH/WchLinkBase.hpp +++ b/src/DebugToolDrivers/WCH/WchLinkBase.hpp @@ -6,8 +6,10 @@ #include "src/DebugToolDrivers/DebugTool.hpp" #include "src/DebugToolDrivers/USB/UsbDevice.hpp" +#include "src/DebugToolDrivers/USB/UsbInterface.hpp" #include "Protocols/WchLink/WchLinkInterface.hpp" +#include "src/DebugToolDrivers/Protocols/RiscVDebugSpec/DebugTranslator.hpp" #include "WchGeneric.hpp" #include "DeviceInfo.hpp" @@ -28,13 +30,16 @@ namespace DebugToolDrivers::Wch void close() override; + [[nodiscard]] bool isInitialised() const override; + std::string getSerialNumber() override; std::string getFirmwareVersionString() override; - DebugToolDrivers::TargetInterfaces::RiscV::RiscVDebugInterface* getRiscVDebugInterface() override { - return this->wchLinkInterface.get(); - } + ::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslator* getRiscVDebugInterface( + const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile, + const Targets::RiscV::RiscVTargetConfig& targetConfig + ) override; /** * WCH-Link debug tools cannot write to flash memory via the RISC-V debug interface (RiscVDebugInterface). @@ -46,20 +51,27 @@ namespace DebugToolDrivers::Wch * target driver forwards any flash memory writes to this implementation (instead of relying on the debug * interface). * - * The WchLinkInterface implements both the RiscVDebugInterface and the RiscVProgramInterface. - * * @return */ - DebugToolDrivers::TargetInterfaces::RiscV::RiscVProgramInterface* getRiscVProgramInterface() override { - return this->wchLinkInterface.get(); - } + Protocols::WchLink::WchLinkInterface* getRiscVProgramInterface( + const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile, + const Targets::RiscV::RiscVTargetConfig& targetConfig + ) override; + + Protocols::WchLink::WchLinkInterface* getRiscVIdentificationInterface( + const Targets::RiscV::TargetDescriptionFile& targetDescriptionFile, + const Targets::RiscV::RiscVTargetConfig& targetConfig + ) override; protected: + bool initialised = false; + WchLinkVariant variant; std::uint8_t wchLinkUsbInterfaceNumber; std::unique_ptr wchLinkUsbInterface = nullptr; std::unique_ptr wchLinkInterface = nullptr; + std::unique_ptr<::DebugToolDrivers::Protocols::RiscVDebugSpec::DebugTranslator> wchRiscVTranslator = nullptr; mutable std::optional cachedDeviceInfo = std::nullopt; diff --git a/src/EventManager/EventListener.cpp b/src/EventManager/EventListener.cpp index 5829a5ea..2492a910 100644 --- a/src/EventManager/EventListener.cpp +++ b/src/EventManager/EventListener.cpp @@ -31,7 +31,7 @@ void EventListener::waitAndDispatch(int msTimeout) { const auto& eventQueueByType = this->eventQueueByEventType.unsafeReference(); const auto registeredEventTypes = this->getRegisteredEventTypes(); - std::optional event; + auto event = std::optional{}; const auto eventsFound = [®isteredEventTypes, &event, &eventQueueByType]() -> bool { for (auto& eventQueue: eventQueueByType) { @@ -43,7 +43,7 @@ void EventListener::waitAndDispatch(int msTimeout) { }; if (msTimeout > 0) { - this->eventQueueByEventTypeCV.wait_for(queueLock, std::chrono::milliseconds(msTimeout), eventsFound); + this->eventQueueByEventTypeCV.wait_for(queueLock, std::chrono::milliseconds{msTimeout}, eventsFound); } else { this->eventQueueByEventTypeCV.wait(queueLock, eventsFound); @@ -57,7 +57,7 @@ void EventListener::dispatchEvent(const SharedGenericEventPointer& event) { Logger::debug("Dispatching event " + event->getName() + " (" + std::to_string(event->id) + ")."); // Dispatch the event to all registered handlers - auto callbacks = std::vector>(); + auto callbacks = std::vector>{}; { const auto callbackMappingAccessor = this->eventTypeToCallbacksMapping.accessor(); @@ -83,7 +83,7 @@ void EventListener::dispatchCurrentEvents() { std::vector EventListener::getEvents() { auto eventQueueByType = this->eventQueueByEventType.accessor(); - std::vector output; + auto output = std::vector{}; for (auto& eventQueue: *eventQueueByType) { while (!eventQueue.second.empty()) { diff --git a/src/EventManager/EventListener.hpp b/src/EventManager/EventListener.hpp index d3454860..f71851a7 100644 --- a/src/EventManager/EventListener.hpp +++ b/src/EventManager/EventListener.hpp @@ -40,7 +40,9 @@ class EventListener { public: - explicit EventListener(std::string name): name(std::move(name)) {}; + explicit EventListener(std::string name) + : name(std::move(name)) + {}; std::size_t getId() const { return this->id; @@ -55,7 +57,7 @@ public: bool isEventTypeRegistered(Events::EventType eventType) { return this->registeredEventTypes.accessor()->contains(eventType); - }; + } /** * Registers an event type for the listener. @@ -194,13 +196,13 @@ public: >::type >::type; - ReturnType output = std::nullopt; + auto output = ReturnType{}; auto queueLock = this->eventQueueByEventType.lock(); auto& eventQueueByType = this->eventQueueByEventType.unsafeReference(); auto eventTypes = std::set({EventTypeA::type}); - auto eventTypesToDeRegister = std::set(); + auto eventTypesToDeRegister = std::set{}; if constexpr (!std::is_same_v) { static_assert( @@ -266,23 +268,23 @@ public: // If we're looking for multiple event types, use an std::variant. if constexpr (!std::is_same_v || !std::is_same_v) { if (foundEvent->getType() == EventTypeA::type) { - output = std::optional( + output = std::optional{ std::dynamic_pointer_cast(foundEvent) - ); + }; } else if constexpr (!std::is_same_v) { if (foundEvent->getType() == EventTypeB::type) { - output = std::optional( + output = std::optional{ std::dynamic_pointer_cast(foundEvent) - ); + }; } } if constexpr (!std::is_same_v) { if (foundEvent->getType() == EventTypeC::type) { - output = std::optional( + output = std::optional{ std::dynamic_pointer_cast(foundEvent) - ); + }; } } @@ -341,7 +343,9 @@ private: * we perform a downcast before invoking the callback. See EventListener::registerCallbackForEventType() * for more) */ - Synchronised>>> eventTypeToCallbacksMapping; + Synchronised< + std::map>> + > eventTypeToCallbacksMapping; Synchronised> registeredEventTypes; NotifierInterface* interruptEventNotifier = nullptr; diff --git a/src/EventManager/EventManager.cpp b/src/EventManager/EventManager.cpp index 43f8b0e1..06ced288 100644 --- a/src/EventManager/EventManager.cpp +++ b/src/EventManager/EventManager.cpp @@ -1,17 +1,17 @@ #include "EventManager.hpp" void EventManager::registerListener(std::shared_ptr listener) { - auto registerListenersLock = std::unique_lock(EventManager::registerListenerMutex); - EventManager::registeredListeners.insert(std::pair(listener->getId(), std::move(listener))); + auto registerListenersLock = std::unique_lock{EventManager::registerListenerMutex}; + EventManager::registeredListeners.emplace(listener->getId(), std::move(listener)); } void EventManager::deregisterListener(size_t listenerId) { - auto registerListenersLock = std::unique_lock(EventManager::registerListenerMutex); + auto registerListenersLock = std::unique_lock{EventManager::registerListenerMutex}; EventManager::registeredListeners.erase(listenerId); } void EventManager::triggerEvent(const std::shared_ptr& event) { - auto registerListenersLock = std::unique_lock(EventManager::registerListenerMutex); + auto registerListenersLock = std::unique_lock{EventManager::registerListenerMutex}; for (const auto&[listenerId, listener] : EventManager::registeredListeners) { if (listener->isEventTypeRegistered(event->getType())) { @@ -21,7 +21,7 @@ void EventManager::triggerEvent(const std::shared_ptr& even } bool EventManager::isEventTypeListenedFor(Events::EventType eventType) { - auto registerListenersLock = std::unique_lock(EventManager::registerListenerMutex); + auto registerListenersLock = std::unique_lock{EventManager::registerListenerMutex}; for (const auto& [listenerId, listener] : EventManager::registeredListeners) { if (listener->isEventTypeRegistered(eventType)) { diff --git a/src/EventManager/Events/Event.hpp b/src/EventManager/Events/Event.hpp index ecce1e20..336e4540 100644 --- a/src/EventManager/Events/Event.hpp +++ b/src/EventManager/Events/Event.hpp @@ -24,8 +24,7 @@ namespace Events DEBUG_SERVER_THREAD_STATE_CHANGED, SHUTDOWN_DEBUG_SERVER, REGISTERS_WRITTEN_TO_TARGET, - TARGET_EXECUTION_RESUMED, - TARGET_EXECUTION_STOPPED, + TARGET_STATE_CHANGED, MEMORY_WRITTEN_TO_TARGET, TARGET_RESET, PROGRAMMING_MODE_ENABLED, diff --git a/src/EventManager/Events/Events.hpp b/src/EventManager/Events/Events.hpp index 7625b630..578c7f1a 100644 --- a/src/EventManager/Events/Events.hpp +++ b/src/EventManager/Events/Events.hpp @@ -12,8 +12,7 @@ #include "DebugServerThreadStateChanged.hpp" #include "ShutdownDebugServer.hpp" #include "RegistersWrittenToTarget.hpp" -#include "TargetExecutionResumed.hpp" -#include "TargetExecutionStopped.hpp" +#include "TargetStateChanged.hpp" #include "MemoryWrittenToTarget.hpp" #include "TargetReset.hpp" #include "ProgrammingModeEnabled.hpp" diff --git a/src/EventManager/Events/MemoryWrittenToTarget.hpp b/src/EventManager/Events/MemoryWrittenToTarget.hpp index fba85194..9ab5f9a3 100644 --- a/src/EventManager/Events/MemoryWrittenToTarget.hpp +++ b/src/EventManager/Events/MemoryWrittenToTarget.hpp @@ -3,6 +3,9 @@ #include #include "Event.hpp" + +#include "src/Targets/TargetAddressSpaceDescriptor.hpp" +#include "src/Targets/TargetMemorySegmentDescriptor.hpp" #include "src/Targets/TargetMemory.hpp" namespace Events @@ -13,16 +16,19 @@ namespace Events static constexpr EventType type = EventType::MEMORY_WRITTEN_TO_TARGET; static const inline std::string name = "MemoryWrittenToTarget"; - Targets::TargetMemoryType memoryType; + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor; + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor; Targets::TargetMemoryAddress startAddress; Targets::TargetMemorySize size; MemoryWrittenToTarget( - Targets::TargetMemoryType memoryType, + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, Targets::TargetMemoryAddress startAddress, Targets::TargetMemorySize size ) - : memoryType(memoryType) + : addressSpaceDescriptor(addressSpaceDescriptor) + , memorySegmentDescriptor(memorySegmentDescriptor) , startAddress(startAddress) , size(size) {}; diff --git a/src/EventManager/Events/RegistersWrittenToTarget.hpp b/src/EventManager/Events/RegistersWrittenToTarget.hpp index f2a76ebd..52a3ffa2 100644 --- a/src/EventManager/Events/RegistersWrittenToTarget.hpp +++ b/src/EventManager/Events/RegistersWrittenToTarget.hpp @@ -3,7 +3,8 @@ #include #include "Event.hpp" -#include "src/Targets/TargetRegister.hpp" + +#include "src/Targets/TargetRegisterDescriptor.hpp" namespace Events { @@ -13,7 +14,11 @@ namespace Events static constexpr EventType type = EventType::REGISTERS_WRITTEN_TO_TARGET; static const inline std::string name = "RegistersWrittenToTarget"; - Targets::TargetRegisters registers; + Targets::TargetRegisterDescriptorAndValuePairs registers; + + explicit RegistersWrittenToTarget(const Targets::TargetRegisterDescriptorAndValuePairs& registers) + : registers(registers) + {} [[nodiscard]] EventType getType() const override { return RegistersWrittenToTarget::type; diff --git a/src/EventManager/Events/TargetControllerErrorOccurred.hpp b/src/EventManager/Events/TargetControllerErrorOccurred.hpp index 1d52d1a8..39657c13 100644 --- a/src/EventManager/Events/TargetControllerErrorOccurred.hpp +++ b/src/EventManager/Events/TargetControllerErrorOccurred.hpp @@ -15,7 +15,9 @@ namespace Events std::string errorMessage; TargetControllerErrorOccurred() = default; - TargetControllerErrorOccurred(const std::string& errorMessage): errorMessage(errorMessage) {}; + TargetControllerErrorOccurred(const std::string& errorMessage) + : errorMessage(errorMessage) + {}; [[nodiscard]] EventType getType() const override { return TargetControllerErrorOccurred::type; diff --git a/src/EventManager/Events/TargetExecutionResumed.hpp b/src/EventManager/Events/TargetExecutionResumed.hpp deleted file mode 100644 index 0a6b16ea..00000000 --- a/src/EventManager/Events/TargetExecutionResumed.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -#include "Event.hpp" - -namespace Events -{ - class TargetExecutionResumed: public Event - { - public: - static constexpr EventType type = EventType::TARGET_EXECUTION_RESUMED; - static const inline std::string name = "TargetExecutionResumed"; - - bool stepping = false; - - explicit TargetExecutionResumed(bool stepping) - : stepping(stepping) - {}; - - [[nodiscard]] EventType getType() const override { - return TargetExecutionResumed::type; - } - - [[nodiscard]] std::string getName() const override { - return TargetExecutionResumed::name; - } - }; -} diff --git a/src/EventManager/Events/TargetExecutionStopped.hpp b/src/EventManager/Events/TargetExecutionStopped.hpp deleted file mode 100644 index 97616e7f..00000000 --- a/src/EventManager/Events/TargetExecutionStopped.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include -#include - -#include "Event.hpp" - -#include "src/Targets/TargetBreakpoint.hpp" -#include "src/Targets/TargetMemory.hpp" - -namespace Events -{ - class TargetExecutionStopped: public Event - { - public: - static constexpr EventType type = EventType::TARGET_EXECUTION_STOPPED; - static const inline std::string name = "TargetExecutionStopped"; - - Targets::TargetMemoryAddress programCounter; - Targets::TargetBreakCause breakCause; - - TargetExecutionStopped(Targets::TargetMemoryAddress programCounter, Targets::TargetBreakCause breakCause) - : programCounter(programCounter) - , breakCause(breakCause) - {} - - [[nodiscard]] EventType getType() const override { - return TargetExecutionStopped::type; - } - - [[nodiscard]] std::string getName() const override { - return TargetExecutionStopped::name; - } - }; -} diff --git a/src/EventManager/Events/TargetStateChanged.hpp b/src/EventManager/Events/TargetStateChanged.hpp new file mode 100644 index 00000000..74e456fc --- /dev/null +++ b/src/EventManager/Events/TargetStateChanged.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include + +#include "Event.hpp" +#include "src/Targets/TargetState.hpp" + +namespace Events +{ + class TargetStateChanged: public Event + { + public: + static constexpr EventType type = EventType::TARGET_STATE_CHANGED; + static const inline std::string name = "TargetStateChanged"; + + Targets::TargetState newState; + Targets::TargetState previousState; + + explicit TargetStateChanged(Targets::TargetState newState, Targets::TargetState previousState) + : newState(newState) + , previousState(previousState) + {}; + + [[nodiscard]] EventType getType() const override { + return TargetStateChanged::type; + } + + [[nodiscard]] std::string getName() const override { + return TargetStateChanged::name; + } + }; +} diff --git a/src/Exceptions/Exception.hpp b/src/Exceptions/Exception.hpp index 507d5bb4..85a105d8 100644 --- a/src/Exceptions/Exception.hpp +++ b/src/Exceptions/Exception.hpp @@ -18,7 +18,7 @@ namespace Exceptions explicit Exception(const char* message) : std::runtime_error(message) - , message(std::string(message)) + , message(std::string{message}) {} virtual ~Exception() = default; diff --git a/src/Exceptions/InternalFatalErrorException.hpp b/src/Exceptions/InternalFatalErrorException.hpp index d700b01d..7492f7df 100644 --- a/src/Exceptions/InternalFatalErrorException.hpp +++ b/src/Exceptions/InternalFatalErrorException.hpp @@ -14,9 +14,5 @@ namespace Exceptions + Services::PathService::homeDomainName() + "/report-issue" ) {} - - explicit InternalFatalErrorException(const char* message) - : InternalFatalErrorException(std::string(message)) - {} }; } diff --git a/src/Exceptions/InvalidConfig.hpp b/src/Exceptions/InvalidConfig.hpp index 1d5688c8..f9962fc9 100644 --- a/src/Exceptions/InvalidConfig.hpp +++ b/src/Exceptions/InvalidConfig.hpp @@ -7,12 +7,8 @@ namespace Exceptions class InvalidConfig: public Exception { public: - explicit InvalidConfig(const std::string& message): Exception(message) { - this->message = message; - } - - explicit InvalidConfig(const char* message): Exception(message) { - this->message = std::string(message); - } + explicit InvalidConfig(const std::string& message) + : Exception(message) + {} }; } diff --git a/src/Exceptions/TargetControllerStartupFailure.hpp b/src/Exceptions/TargetControllerStartupFailure.hpp index 49794ac1..6010ac62 100644 --- a/src/Exceptions/TargetControllerStartupFailure.hpp +++ b/src/Exceptions/TargetControllerStartupFailure.hpp @@ -7,12 +7,8 @@ namespace Exceptions class TargetControllerStartupFailure: public Exception { public: - explicit TargetControllerStartupFailure(const std::string& message): Exception(message) { - this->message = message; - } - - explicit TargetControllerStartupFailure(const char* message): Exception(message) { - this->message = std::string(message); - } + explicit TargetControllerStartupFailure(const std::string& message) + : Exception(message) + {} }; } diff --git a/src/Helpers/BiMap.hpp b/src/Helpers/BiMap.hpp index a4912ca3..defc165a 100644 --- a/src/Helpers/BiMap.hpp +++ b/src/Helpers/BiMap.hpp @@ -22,8 +22,8 @@ class BiMap public: BiMap(std::initializer_list> elements) { for (auto it = elements.begin(); it != elements.end(); ++it) { - this->map.insert(std::pair{it->first, it->second}); - this->flippedMap.insert(std::pair(it->second, it->first)); + this->map.emplace(it->first, it->second); + this->flippedMap.emplace(it->second, it->first); } } @@ -37,16 +37,16 @@ public: auto find(const TypeA& key) const { const auto valueIt = this->map.find(key); - return valueIt != this->map.end() ? std::optional(valueIt) : std::nullopt; + return valueIt != this->map.end() ? std::optional{valueIt} : std::nullopt; } auto find(const TypeB& key) const { const auto valueIt = this->flippedMap.find(key); - return valueIt != this->flippedMap.end() ? std::optional(valueIt) : std::nullopt; + return valueIt != this->flippedMap.end() ? std::optional{valueIt} : std::nullopt; } std::optional valueAt(const TypeA& key) const { - std::optional output; + auto output = std::optional{}; const auto valueIt = this->map.find(key); if (valueIt != this->map.end()) { @@ -57,7 +57,7 @@ public: } std::optional valueAt(const TypeB& key) const { - std::optional output; + auto output = std::optional{}; const auto valueIt = this->flippedMap.find(key); if (valueIt != this->flippedMap.end()) { @@ -80,7 +80,7 @@ public: } [[nodiscard]] std::set getKeys() const { - auto keys = std::set(); + auto keys = std::set{}; for (const auto& [key, value] : this->map) { keys.insert(key); @@ -90,7 +90,7 @@ public: } [[nodiscard]] std::set getValues() const { - auto values = std::set(); + auto values = std::set{}; for (const auto& [key, value] : this->map) { values.insert(value); @@ -101,7 +101,7 @@ public: void insert(const std::pair& pair) { auto insertResultPair = this->map.insert(pair); - this->flippedMap.insert(std::pair(pair.second, pair.first)); + this->flippedMap.emplace(pair.second, pair.first); } private: diff --git a/src/Helpers/ConditionVariableNotifier.cpp b/src/Helpers/ConditionVariableNotifier.cpp index d3903976..f8af6b37 100644 --- a/src/Helpers/ConditionVariableNotifier.cpp +++ b/src/Helpers/ConditionVariableNotifier.cpp @@ -1,7 +1,7 @@ #include "ConditionVariableNotifier.hpp" void ConditionVariableNotifier::notify() { - const auto lock = std::unique_lock(this->mutex); + const auto lock = std::unique_lock{this->mutex}; this->notified = true; this->conditionalVariable.notify_all(); } @@ -10,7 +10,7 @@ void ConditionVariableNotifier::waitForNotification(std::optionalnotified; }; - auto lock = std::unique_lock(this->mutex); + auto lock = std::unique_lock{this->mutex}; if (timeout.has_value()) { this->conditionalVariable.wait_for(lock, timeout.value(), predicate); diff --git a/src/Helpers/ConditionVariableNotifier.hpp b/src/Helpers/ConditionVariableNotifier.hpp index be2d6bfe..6def2b91 100644 --- a/src/Helpers/ConditionVariableNotifier.hpp +++ b/src/Helpers/ConditionVariableNotifier.hpp @@ -17,15 +17,9 @@ public: ConditionVariableNotifier() = default; ~ConditionVariableNotifier() override = default; - /* - * ConditionVariableNotifier objects should not be copied. - */ ConditionVariableNotifier(ConditionVariableNotifier& other) = delete; ConditionVariableNotifier& operator = (ConditionVariableNotifier& other) = delete; - /* - * TODO: Implement this. - */ ConditionVariableNotifier(ConditionVariableNotifier&& other) noexcept = delete; ConditionVariableNotifier& operator = (ConditionVariableNotifier&& other) = delete; diff --git a/src/Helpers/EnumToStringMappings.hpp b/src/Helpers/EnumToStringMappings.hpp index f1f15864..19cbd1cd 100644 --- a/src/Helpers/EnumToStringMappings.hpp +++ b/src/Helpers/EnumToStringMappings.hpp @@ -4,16 +4,15 @@ #include "BiMap.hpp" -#include "src/Targets/TargetMemory.hpp" +#include "src/Targets/TargetMemorySegmentType.hpp" class EnumToStringMappings { public: - static const inline BiMap targetMemoryTypes = { - {Targets::TargetMemoryType::RAM, "ram"}, - {Targets::TargetMemoryType::EEPROM, "eeprom"}, - {Targets::TargetMemoryType::FLASH, "flash"}, - {Targets::TargetMemoryType::OTHER, "other"}, + static const inline BiMap targetMemorySegmentTypes = { + {Targets::TargetMemorySegmentType::RAM, "ram"}, + {Targets::TargetMemorySegmentType::EEPROM, "eeprom"}, + {Targets::TargetMemorySegmentType::FLASH, "flash"}, }; static const inline BiMap targetMemoryEndianness = { diff --git a/src/Helpers/EpollInstance.cpp b/src/Helpers/EpollInstance.cpp index 4bebcded..3d9bcd14 100644 --- a/src/Helpers/EpollInstance.cpp +++ b/src/Helpers/EpollInstance.cpp @@ -13,15 +13,12 @@ EpollInstance::EpollInstance() { this->fileDescriptor = ::epoll_create(1); if (this->fileDescriptor < 0) { - throw Exception( - "Failed to create epoll instance - error number " + std::to_string(errno) - + " returned." - ); + throw Exception{"Failed to create epoll instance - error number " + std::to_string(errno) + " returned."}; } } void EpollInstance::addEntry(int fileDescriptor, std::uint16_t eventMask) { - struct ::epoll_event event = { + auto event = ::epoll_event{ .events = eventMask, .data = { .fd = fileDescriptor @@ -29,23 +26,20 @@ void EpollInstance::addEntry(int fileDescriptor, std::uint16_t eventMask) { }; if (::epoll_ctl(this->fileDescriptor.value(), EPOLL_CTL_ADD, fileDescriptor, &event) != 0) { - throw Exception( - "Failed to add entry to epoll instance - error number " + std::to_string(errno) + " returned." - ); + throw Exception{"Failed to add entry to epoll instance - error number " + std::to_string(errno) + " returned."}; } } void EpollInstance::removeEntry(int fileDescriptor) { if (::epoll_ctl(this->fileDescriptor.value(), EPOLL_CTL_DEL, fileDescriptor, NULL) != 0) { - throw Exception( - "Failed to remove entry from epoll instance - error number " + std::to_string(errno) - + " returned." - ); + throw Exception{ + "Failed to remove entry from epoll instance - error number " + std::to_string(errno) + " returned." + }; } } std::optional EpollInstance::waitForEvent(std::optional timeout) const { - std::array events = {}; + auto events = std::array{}; const auto eventCount = ::epoll_wait( this->fileDescriptor.value(), diff --git a/src/Helpers/EventFdNotifier.cpp b/src/Helpers/EventFdNotifier.cpp index 9bbb3531..84c58f6f 100644 --- a/src/Helpers/EventFdNotifier.cpp +++ b/src/Helpers/EventFdNotifier.cpp @@ -13,10 +13,7 @@ EventFdNotifier::EventFdNotifier() { this->fileDescriptor = ::eventfd(0, ::EFD_NONBLOCK); if (this->fileDescriptor < 0) { - throw Exception( - "Failed to create eventfd object - error number " + std::to_string(errno) - + " returned." - ); + throw Exception{"Failed to create eventfd object - error number " + std::to_string(errno) + " returned."}; } } @@ -32,16 +29,16 @@ EventFdNotifier::~EventFdNotifier() noexcept { void EventFdNotifier::notify() { if (::eventfd_write(this->fileDescriptor.value(), 1) < 0) { - throw Exceptions::Exception("Failed to increment eventfd counter - error number: " - + std::to_string(errno)); + throw Exceptions::Exception{"Failed to increment eventfd counter - error number: " + std::to_string(errno)}; } } void EventFdNotifier::clear() { - ::eventfd_t counter = {}; + auto counter = ::eventfd_t{}; if (::eventfd_read(this->fileDescriptor.value(), &counter) < 0 && errno != EAGAIN) { - throw Exceptions::Exception("Failed to clear EventFdNotifier object - eventfd_read failed - " - "error number: " + std::to_string(errno)); + throw Exceptions::Exception{ + "Failed to clear EventFdNotifier object - eventfd_read failed - " "error number: " + std::to_string(errno) + }; } } diff --git a/src/Helpers/Pair.hpp b/src/Helpers/Pair.hpp new file mode 100644 index 00000000..7c6edf73 --- /dev/null +++ b/src/Helpers/Pair.hpp @@ -0,0 +1,19 @@ +#pragma once + +template +struct Pair +{ + FirstType first; + SecondType second; + + Pair(FirstType&& first, SecondType&& second) + requires (!std::is_reference_v && !std::is_reference_v) + : first(std::move(first)) + , second(std::move(second)) + {} + + Pair(const FirstType& first, const SecondType& second) + : first(first) + , second(second) + {} +}; diff --git a/src/Helpers/Synchronised.hpp b/src/Helpers/Synchronised.hpp index 15486f5c..503c29d4 100644 --- a/src/Helpers/Synchronised.hpp +++ b/src/Helpers/Synchronised.hpp @@ -66,7 +66,7 @@ public: } std::unique_lock lock() { - return std::unique_lock(this->mutex); + return std::unique_lock{this->mutex}; } private: diff --git a/src/Helpers/Thread.hpp b/src/Helpers/Thread.hpp index ba5d801b..b4d5ab07 100644 --- a/src/Helpers/Thread.hpp +++ b/src/Helpers/Thread.hpp @@ -38,15 +38,15 @@ protected: * Disables signal interrupts on current thread. */ static void blockAllSignals() { - sigset_t set = {}; - sigfillset(&set); - sigprocmask(SIG_SETMASK, &set, NULL); + auto set = ::sigset_t{}; + ::sigfillset(&set); + ::sigprocmask(SIG_SETMASK, &set, NULL); } void setName(const std::string& name) { // POSIX thread names cannot exceed 16 characters, including the terminating null byte. assert(name.size() <= 15); - pthread_setname_np(pthread_self(), name.c_str()); + ::pthread_setname_np(::pthread_self(), name.c_str()); } }; diff --git a/src/Insight/InsightWorker/Tasks/RefreshTargetPinStates.hpp b/src/Insight/InsightWorker/Tasks/RefreshTargetPinStates.hpp index 448eb3b7..88704d46 100644 --- a/src/Insight/InsightWorker/Tasks/RefreshTargetPinStates.hpp +++ b/src/Insight/InsightWorker/Tasks/RefreshTargetPinStates.hpp @@ -1,7 +1,6 @@ #pragma once #include "InsightWorkerTask.hpp" -#include "src/Targets/TargetVariant.hpp" #include "src/Targets/TargetPinDescriptor.hpp" class RefreshTargetPinStates: public InsightWorkerTask diff --git a/src/Insight/UserInterfaces/InsightWindow/InsightWindow.hpp b/src/Insight/UserInterfaces/InsightWindow/InsightWindow.hpp index 168ffb43..7d6cd20f 100644 --- a/src/Insight/UserInterfaces/InsightWindow/InsightWindow.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/InsightWindow.hpp @@ -12,7 +12,6 @@ #include "src/Targets/TargetDescriptor.hpp" #include "src/Targets/TargetMemory.hpp" -#include "src/Targets/TargetVariant.hpp" #include "Widgets/Label.hpp" #include "Widgets/SvgToolButton.hpp" diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/DualInlinePackageWidget.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/DualInlinePackageWidget.hpp index a91be63c..f3968579 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/DualInlinePackageWidget.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/DualInlinePackageWidget.hpp @@ -11,7 +11,6 @@ #include "PinWidget.hpp" #include "BodyWidget.hpp" -#include "src/Targets/TargetVariant.hpp" namespace Widgets::InsightTargetWidgets::Dip { diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/PinWidget.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/PinWidget.hpp index 42dac69f..43a10de1 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/PinWidget.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/DIP/PinWidget.hpp @@ -9,7 +9,6 @@ #include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinWidget.hpp" #include "PinBodyWidget.hpp" -#include "src/Targets/TargetVariant.hpp" namespace Widgets::InsightTargetWidgets::Dip { diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/PinWidget.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/PinWidget.hpp index aae55b1b..c2b4df1f 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/PinWidget.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/PinWidget.hpp @@ -8,7 +8,6 @@ #include "src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinWidget.hpp" #include "PinBodyWidget.hpp" -#include "src/Targets/TargetVariant.hpp" namespace Widgets::InsightTargetWidgets::Qfp { diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/QuadFlatPackageWidget.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/QuadFlatPackageWidget.hpp index 4a82015f..ae4d9fe0 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/QuadFlatPackageWidget.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/QFP/QuadFlatPackageWidget.hpp @@ -8,7 +8,6 @@ #include "../TargetPackageWidget.hpp" #include "PinWidget.hpp" #include "BodyWidget.hpp" -#include "src/Targets/TargetVariant.hpp" namespace Widgets::InsightTargetWidgets::Qfp { diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPackageWidget.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPackageWidget.hpp index defcda0f..f186abf5 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPackageWidget.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPackageWidget.hpp @@ -7,7 +7,6 @@ #include #include "TargetPinWidget.hpp" -#include "src/Targets/TargetVariant.hpp" #include "src/Targets/TargetState.hpp" namespace Widgets::InsightTargetWidgets diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinBodyWidget.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinBodyWidget.hpp index fc27f887..ad27d5ab 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinBodyWidget.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinBodyWidget.hpp @@ -4,7 +4,6 @@ #include #include -#include "src/Targets/TargetVariant.hpp" #include "src/Targets/TargetPinDescriptor.hpp" namespace Widgets::InsightTargetWidgets diff --git a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinWidget.hpp b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinWidget.hpp index 9c70d2d7..17eaa483 100644 --- a/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinWidget.hpp +++ b/src/Insight/UserInterfaces/InsightWindow/Widgets/TargetWidgets/TargetPinWidget.hpp @@ -4,7 +4,6 @@ #include #include -#include "src/Targets/TargetVariant.hpp" #include "src/Targets/TargetPinDescriptor.hpp" namespace Widgets::InsightTargetWidgets diff --git a/src/Logger/Logger.cpp b/src/Logger/Logger.cpp index 7baebed3..87392bba 100644 --- a/src/Logger/Logger.cpp +++ b/src/Logger/Logger.cpp @@ -22,7 +22,7 @@ void Logger::log(const std::string& message, LogLevel level) { const auto threadName = Logger::threadName(); static const auto timezoneAbbreviation = DateTimeService::getTimeZoneAbbreviation(timestamp).toStdString(); - const auto lock = std::unique_lock(Logger::printMutex); + const auto lock = std::unique_lock{Logger::printMutex}; // Print the timestamp and id in a green font color: std::cout << "\033[32m"; @@ -62,20 +62,20 @@ void Logger::log(const std::string& message, LogLevel level) { } const std::string& Logger::threadName() { - static auto nameCache = std::map<::pthread_t, std::string>(); + static auto nameCache = std::map<::pthread_t, std::string>{}; const auto threadId = ::pthread_self(); auto nameIt = nameCache.find(threadId); if (nameIt == nameCache.end()) { - std::array threadNameBuf = {}; + auto threadNameBuf = std::array{}; if (::pthread_getname_np(::pthread_self(), threadNameBuf.data(), threadNameBuf.size()) != 0) { - static const auto emptyName = std::string(); + static const auto emptyName = std::string{}; return emptyName; } - const auto name = std::string(threadNameBuf.data()); + const auto name = std::string{threadNameBuf.data()}; /* * The name of the main thread is also the name of the process, so we have to name the @@ -84,7 +84,7 @@ const std::string& Logger::threadName() { * We override the main thread name when printing logs, to keep the format of the thread name in the * logs consistent. */ - nameIt = nameCache.insert(std::pair(threadId, name == "Bloom" ? "MT" : name)).first; + nameIt = nameCache.emplace(threadId, name == "Bloom" ? "MT" : name).first; } return nameIt->second; diff --git a/src/ProjectConfig.cpp b/src/ProjectConfig.cpp index 6697d31c..296d1a73 100644 --- a/src/ProjectConfig.cpp +++ b/src/ProjectConfig.cpp @@ -9,37 +9,35 @@ using Services::StringService; ProjectConfig::ProjectConfig(const YAML::Node& configNode) { if (!configNode["environments"]) { - throw Exceptions::InvalidConfig( + throw Exceptions::InvalidConfig{ "No environments found - please review the bloom.yaml configuration file and ensure that " - "no syntax errors are present." - ); + "no syntax errors are present." + }; } if (!configNode["environments"].IsMap()) { - throw Exceptions::InvalidConfig( + throw Exceptions::InvalidConfig{ "Invalid environments configuration provided - 'environments' must be of mapping type." - ); + }; } const auto& environments = configNode["environments"]; for (auto environmentIt = environments.begin(); environmentIt != environments.end(); environmentIt++) { - auto environmentName = std::optional(std::nullopt); + auto environmentName = std::optional{}; try { environmentName = environmentIt->first.as(); if (!StringService::isAscii(environmentName.value())) { - throw Exceptions::InvalidConfig( + throw Exceptions::InvalidConfig{ "Environment name ('" + environmentName.value() + "') is not in ASCII form." - ); + }; } - this->environments.insert( - std::pair( - environmentName.value(), - EnvironmentConfig(environmentName.value(), environmentIt->second) - ) + this->environments.emplace( + environmentName.value(), + EnvironmentConfig{environmentName.value(), environmentIt->second} ); } catch (Exceptions::InvalidConfig& exception) { @@ -51,7 +49,7 @@ ProjectConfig::ProjectConfig(const YAML::Node& configNode) { } catch (YAML::BadConversion& exception) { Logger::error( "Invalid environment name provided. Environment names must be ASCII strings. Environment will be " - "ignored" + "ignored" ); } } @@ -59,7 +57,7 @@ ProjectConfig::ProjectConfig(const YAML::Node& configNode) { if (configNode["debugServer"]) { Logger::warning( "The 'debugServer' key was renamed to 'server' in v1.0.0. Please update your bloom.yaml configuration. " - "See " + Services::PathService::homeDomainName() + "/docs/v1-0-0-migration for more." + "See " + Services::PathService::homeDomainName() + "/docs/v1-0-0-migration for more." ); } @@ -83,9 +81,9 @@ ProjectConfig::ProjectConfig(const YAML::Node& configNode) { InsightConfig::InsightConfig(const YAML::Node& insightNode) { if (!insightNode.IsMap()) { - throw Exceptions::InvalidConfig( + throw Exceptions::InvalidConfig{ "Invalid insight configuration provided - node must take the form of a YAML mapping." - ); + }; } if (insightNode["activateOnStartup"]) { @@ -101,7 +99,7 @@ EnvironmentConfig::EnvironmentConfig(std::string name, const YAML::Node& environ : name(std::move(name)) { if (!environmentNode.IsMap()) { - throw Exceptions::InvalidConfig("Environment node must take the form of a YAML mapping."); + throw Exceptions::InvalidConfig{"Environment node must take the form of a YAML mapping."}; } static auto warn = true; @@ -110,7 +108,7 @@ EnvironmentConfig::EnvironmentConfig(std::string name, const YAML::Node& environ if (environmentNode["debugTool"] && !environmentNode["tool"]) { Logger::warning( "The 'debugTool' key was renamed to 'tool' in v1.0.0. Please update your bloom.yaml configuration. " - "Bloom will fail to start up until this is resolved. See " + "Bloom will fail to start up until this is resolved. See " + Services::PathService::homeDomainName() + "/docs/v1-0-0-migration for more." ); } @@ -118,7 +116,7 @@ EnvironmentConfig::EnvironmentConfig(std::string name, const YAML::Node& environ if (environmentNode["debugServer"] && !environmentNode["server"]) { Logger::warning( "The 'debugServer' key was renamed to 'server' in v1.0.0. Please update your bloom.yaml configuration. " - "Bloom will fail to start up until this is resolved. See " + "Bloom will fail to start up until this is resolved. See " + Services::PathService::homeDomainName() + "/docs/v1-0-0-migration for more." ); } @@ -127,18 +125,18 @@ EnvironmentConfig::EnvironmentConfig(std::string name, const YAML::Node& environ } if (!environmentNode["tool"]) { - throw Exceptions::InvalidConfig("Missing debug tool configuration."); + throw Exceptions::InvalidConfig{"Missing debug tool configuration."}; } if (!environmentNode["target"]) { - throw Exceptions::InvalidConfig("Missing target configuration."); + throw Exceptions::InvalidConfig{"Missing target configuration."}; } - this->debugToolConfig = DebugToolConfig(environmentNode["tool"]); - this->targetConfig = TargetConfig(environmentNode["target"]); + this->debugToolConfig = DebugToolConfig{environmentNode["tool"]}; + this->targetConfig = TargetConfig{environmentNode["target"]}; if (environmentNode["server"]) { - this->debugServerConfig = DebugServerConfig(environmentNode["server"]); + this->debugServerConfig = DebugServerConfig{environmentNode["server"]}; } if (environmentNode["insight"]) { @@ -156,38 +154,38 @@ TargetConfig::TargetConfig(const YAML::Node& targetNode) { using Targets::TargetPhysicalInterface; if (!targetNode.IsMap()) { - throw Exceptions::InvalidConfig( + throw Exceptions::InvalidConfig{ "Invalid target configuration provided - node must take the form of a YAML mapping." - ); + }; } if (!targetNode["name"]) { - throw Exceptions::InvalidConfig("No target name found."); + throw Exceptions::InvalidConfig{"No target name found."}; } this->name = StringService::asciiToLower(targetNode["name"].as()); - static auto physicalInterfacesByConfigName = std::map({ + static auto physicalInterfacesByConfigName = std::map{ {"debugwire", TargetPhysicalInterface::DEBUG_WIRE}, // Deprecated - left here for backwards compatibility {"debug-wire", TargetPhysicalInterface::DEBUG_WIRE}, {"pdi", TargetPhysicalInterface::PDI}, {"jtag", TargetPhysicalInterface::JTAG}, {"updi", TargetPhysicalInterface::UPDI}, - }); + }; if (!targetNode["physicalInterface"]) { - throw Exceptions::InvalidConfig("No physical interface specified."); + throw Exceptions::InvalidConfig{"No physical interface specified."}; } const auto physicalInterfaceName = StringService::asciiToLower(targetNode["physicalInterface"].as()); const auto physicalInterfaceIt = physicalInterfacesByConfigName.find(physicalInterfaceName); if (physicalInterfaceIt == physicalInterfacesByConfigName.end()) { - throw Exceptions::InvalidConfig( + throw Exceptions::InvalidConfig{ "Invalid physical interface provided (\"" + physicalInterfaceName + "\") for target. " "See " + Services::PathService::homeDomainName() + "/docs/configuration/target-physical-interfaces " "for valid physical interface configuration values." - ); + }; } this->physicalInterface = physicalInterfaceIt->second; @@ -209,13 +207,13 @@ TargetConfig::TargetConfig(const YAML::Node& targetNode) { DebugToolConfig::DebugToolConfig(const YAML::Node& debugToolNode) { if (!debugToolNode.IsMap()) { - throw Exceptions::InvalidConfig( + throw Exceptions::InvalidConfig{ "Invalid debug tool configuration provided - node must take the form of a YAML mapping." - ); + }; } if (!debugToolNode["name"]) { - throw Exceptions::InvalidConfig("No debug tool name found."); + throw Exceptions::InvalidConfig{"No debug tool name found."}; } this->name = StringService::asciiToLower(debugToolNode["name"].as()); @@ -224,13 +222,13 @@ DebugToolConfig::DebugToolConfig(const YAML::Node& debugToolNode) { DebugServerConfig::DebugServerConfig(const YAML::Node& debugServerNode) { if (!debugServerNode.IsMap()) { - throw Exceptions::InvalidConfig( + throw Exceptions::InvalidConfig{ "Invalid debug server configuration provided - node must take the form of a YAML mapping." - ); + }; } if (!debugServerNode["name"]) { - throw Exceptions::InvalidConfig("No debug server name found."); + throw Exceptions::InvalidConfig{"No debug server name found."}; } this->name = StringService::asciiToLower(debugServerNode["name"].as()); diff --git a/src/ProjectConfig.hpp b/src/ProjectConfig.hpp index d52f7f44..217eeeab 100644 --- a/src/ProjectConfig.hpp +++ b/src/ProjectConfig.hpp @@ -240,7 +240,7 @@ struct ProjectConfig * omit the 'insight' node from their bloom.yaml file, entirely. In this case, Bloom should fall back to a default * constructed, project-level, InsightConfig instance. */ - InsightConfig insightConfig = InsightConfig(); + InsightConfig insightConfig = {}; bool debugLogging = false; diff --git a/src/ProjectSettings.cpp b/src/ProjectSettings.cpp index 3e241e8b..d7ca1421 100644 --- a/src/ProjectSettings.cpp +++ b/src/ProjectSettings.cpp @@ -10,7 +10,7 @@ ProjectSettings::ProjectSettings(const QJsonObject& jsonObject) { #ifndef EXCLUDE_INSIGHT if (jsonObject.contains("insight")) { - this->insightSettings = InsightProjectSettings(jsonObject.find("insight")->toObject()); + this->insightSettings = InsightProjectSettings{jsonObject.find("insight")->toObject()}; } #endif } @@ -85,25 +85,25 @@ InsightProjectSettings::InsightProjectSettings(const QJsonObject& jsonObject) { continue; } - this->memoryInspectionPaneSettingsByMemoryType.insert(std::pair( + this->memoryInspectionPaneSettingsByMemoryType.emplace( EnumToStringMappings::targetMemoryTypes.at(memoryTypeName), this->memoryInspectionPaneSettingsFromJson(settingsObj) - )); + ); } } } QJsonObject InsightProjectSettings::toJson() const { - auto insightObj = QJsonObject(); + auto insightObj = QJsonObject{}; if (this->mainWindowSize.has_value()) { - insightObj.insert("mainWindowSize", QJsonObject({ + insightObj.insert("mainWindowSize", QJsonObject{ {"width", this->mainWindowSize->width()}, {"height", this->mainWindowSize->height()}, - })); + }); } - auto memoryInspectionPaneSettingsObj = QJsonObject(); + auto memoryInspectionPaneSettingsObj = QJsonObject{}; for (const auto& [memoryType, inspectionPaneSettings] : this->memoryInspectionPaneSettingsByMemoryType) { if (!EnumToStringMappings::targetMemoryTypes.contains(memoryType)) { @@ -169,7 +169,7 @@ Widgets::TargetMemoryInspectionPaneSettings InsightProjectSettings::memoryInspec ) const { using Exceptions::Exception; - auto inspectionPaneSettings = Widgets::TargetMemoryInspectionPaneSettings(); + auto inspectionPaneSettings = Widgets::TargetMemoryInspectionPaneSettings{}; if (jsonObject.contains("refreshOnTargetStop")) { inspectionPaneSettings.refreshOnTargetStop = jsonObject.value("refreshOnTargetStop").toBool(); @@ -184,28 +184,25 @@ Widgets::TargetMemoryInspectionPaneSettings InsightProjectSettings::memoryInspec const auto hexViewerSettingsObj = jsonObject.find("hexViewerSettings")->toObject(); if (hexViewerSettingsObj.contains("groupStackMemory")) { - hexViewerSettings.groupStackMemory = - hexViewerSettingsObj.value("groupStackMemory").toBool(); + hexViewerSettings.groupStackMemory = hexViewerSettingsObj.value("groupStackMemory").toBool(); } if (hexViewerSettingsObj.contains("highlightFocusedMemory")) { - hexViewerSettings.highlightFocusedMemory = - hexViewerSettingsObj.value("highlightFocusedMemory").toBool(); + hexViewerSettings.highlightFocusedMemory = hexViewerSettingsObj.value("highlightFocusedMemory").toBool(); } if (hexViewerSettingsObj.contains("highlightHoveredRowAndCol")) { - hexViewerSettings.highlightHoveredRowAndCol = - hexViewerSettingsObj.value("highlightHoveredRowAndCol").toBool(); + hexViewerSettings.highlightHoveredRowAndCol = hexViewerSettingsObj.value( + "highlightHoveredRowAndCol" + ).toBool(); } if (hexViewerSettingsObj.contains("displayAsciiValues")) { - hexViewerSettings.displayAsciiValues = - hexViewerSettingsObj.value("displayAsciiValues").toBool(); + hexViewerSettings.displayAsciiValues = hexViewerSettingsObj.value("displayAsciiValues").toBool(); } if (hexViewerSettingsObj.contains("displayAnnotations")) { - hexViewerSettings.displayAnnotations = - hexViewerSettingsObj.value("displayAnnotations").toBool(); + hexViewerSettings.displayAnnotations = hexViewerSettingsObj.value("displayAnnotations").toBool(); } if (hexViewerSettingsObj.contains("addressLabelType")) { @@ -247,17 +244,17 @@ Widgets::TargetMemoryInspectionPaneSettings InsightProjectSettings::memoryInspec } Widgets::PanelState InsightProjectSettings::panelStateFromJson(const QJsonObject& jsonObject) const { - return Widgets::PanelState( + return Widgets::PanelState{ (jsonObject.contains("size") ? static_cast(jsonObject.value("size").toInteger()) : 0), (jsonObject.contains("open") ? jsonObject.value("open").toBool() : false) - ); + }; } Widgets::PaneState InsightProjectSettings::paneStateFromJson(const QJsonObject& jsonObject) const { - auto detachedWindowState = std::optional(std::nullopt); + auto detachedWindowState = std::optional{}; if (jsonObject.contains("detachedWindowState")) { - detachedWindowState = Widgets::DetachedWindowState(); + detachedWindowState = Widgets::DetachedWindowState{}; const auto detachedWindowStateObject = jsonObject.value("detachedWindowState").toObject(); @@ -278,11 +275,11 @@ Widgets::PaneState InsightProjectSettings::paneStateFromJson(const QJsonObject& } } - return Widgets::PaneState( + return Widgets::PaneState{ (jsonObject.contains("activated") ? jsonObject.value("activated").toBool() : false), (jsonObject.contains("attached") ? jsonObject.value("attached").toBool() : true), detachedWindowState - ); + }; } QJsonObject InsightProjectSettings::memoryInspectionPaneSettingsToJson( @@ -290,27 +287,27 @@ QJsonObject InsightProjectSettings::memoryInspectionPaneSettingsToJson( ) const { const auto& addressTypesByName = InsightProjectSettings::addressTypesByName; - auto settingsObj = QJsonObject({ + auto settingsObj = QJsonObject{ {"refreshOnTargetStop", inspectionPaneSettings.refreshOnTargetStop}, {"refreshOnActivation", inspectionPaneSettings.refreshOnActivation}, - }); + }; const auto& hexViewerSettings = inspectionPaneSettings.hexViewerWidgetSettings; - settingsObj.insert("hexViewerSettings", QJsonObject({ + settingsObj.insert("hexViewerSettings", QJsonObject{ {"groupStackMemory", hexViewerSettings.groupStackMemory}, {"highlightFocusedMemory", hexViewerSettings.highlightFocusedMemory}, {"highlightHoveredRowAndCol", hexViewerSettings.highlightHoveredRowAndCol}, {"displayAsciiValues", hexViewerSettings.displayAsciiValues}, {"displayAnnotations", hexViewerSettings.displayAnnotations}, {"addressLabelType", addressTypesByName.valueAt(hexViewerSettings.addressLabelType).value()}, - })); + }); - auto focusedRegions = QJsonArray(); + auto focusedRegions = QJsonArray{}; for (const auto& focusedRegion : inspectionPaneSettings.focusedMemoryRegions) { focusedRegions.push_back(focusedRegion.toJson()); } - auto excludedRegions = QJsonArray(); + auto excludedRegions = QJsonArray{}; for (const auto& excludedRegion : inspectionPaneSettings.excludedMemoryRegions) { excludedRegions.push_back(excludedRegion.toJson()); } @@ -322,35 +319,35 @@ QJsonObject InsightProjectSettings::memoryInspectionPaneSettingsToJson( } QJsonObject InsightProjectSettings::panelStateToJson(const Widgets::PanelState& panelState) const { - return QJsonObject({ + return QJsonObject{ {"size", panelState.size}, {"open", panelState.open}, - }); + }; } QJsonObject InsightProjectSettings::paneStateToJson(const Widgets::PaneState& paneState) const { - auto json = QJsonObject({ + auto json = QJsonObject{ {"activated", paneState.activated}, {"attached", paneState.attached}, - }); + }; if (paneState.detachedWindowState.has_value()) { - json.insert("detachedWindowState", QJsonObject({ + json.insert("detachedWindowState", QJsonObject{ { "size", - QJsonObject({ + QJsonObject{ {"width", paneState.detachedWindowState->size.width()}, {"height", paneState.detachedWindowState->size.height()}, - }) + } }, { "position", - QJsonObject({ + QJsonObject{ {"x", paneState.detachedWindowState->position.x()}, {"y", paneState.detachedWindowState->position.y()}, - }) + } } - })); + }); } return json; diff --git a/src/Services/Avr8InstructionService.cpp b/src/Services/Avr8InstructionService.cpp index ac3bb3b1..b5197ca2 100644 --- a/src/Services/Avr8InstructionService.cpp +++ b/src/Services/Avr8InstructionService.cpp @@ -2,24 +2,10 @@ namespace Services { - using Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Decoder; - - Decoder::InstructionMapping Avr8InstructionService::fetchInstructions( - const Targets::TargetMemoryAddressRange& addressRange, - const Targets::TargetDescriptor& targetDescriptor, - TargetControllerService& targetControllerService - ) { - const auto programMemory = targetControllerService.readMemory( - targetDescriptor.programMemoryType, - addressRange.startAddress, - addressRange.endAddress - addressRange.startAddress - ); - - return Decoder::decode(addressRange.startAddress, programMemory); - } + using Targets::Microchip::Avr8::OpcodeDecoder::Decoder; std::optional Avr8InstructionService::resolveProgramDestinationAddress( - const Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Instruction& instruction, + const Targets::Microchip::Avr8::OpcodeDecoder::Instruction& instruction, Targets::TargetMemoryAddress instructionAddress, const Decoder::InstructionMapping& instructions ) { diff --git a/src/Services/Avr8InstructionService.hpp b/src/Services/Avr8InstructionService.hpp index 64ff13da..2ce66c78 100644 --- a/src/Services/Avr8InstructionService.hpp +++ b/src/Services/Avr8InstructionService.hpp @@ -4,31 +4,14 @@ #include "src/Targets/TargetDescriptor.hpp" #include "src/Targets/TargetMemory.hpp" -#include "src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Instruction.hpp" -#include "src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.hpp" +#include "src/Targets/Microchip/AVR8/OpcodeDecoder/Instruction.hpp" +#include "src/Targets/Microchip/AVR8/OpcodeDecoder/Decoder.hpp" namespace Services { class Avr8InstructionService { public: - /** - * Fetches opcodes from the target's program memory and attempts to decodes them. - * - * @param addressRange - * @param targetDescriptor - * @param targetControllerService - * - * @return - * A mapping of std::optional, mapped by their byte address in program memory. The address will - * map to std::nullopt if we failed to decode the opcode at that address. - */ - static Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Decoder::InstructionMapping fetchInstructions( - const Targets::TargetMemoryAddressRange& addressRange, - const Targets::TargetDescriptor& targetDescriptor, - TargetControllerService& targetControllerService - ); - /** * For instructions that can change program flow, this function will attempt to figure out where, in program * memory, the instruction may jump to. @@ -62,9 +45,9 @@ namespace Services * Otherwise, std::nullopt. */ static std::optional resolveProgramDestinationAddress( - const Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Instruction& instruction, + const Targets::Microchip::Avr8::OpcodeDecoder::Instruction& instruction, Targets::TargetMemoryAddress instructionAddress, - const Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Decoder::InstructionMapping& instructions + const Targets::Microchip::Avr8::OpcodeDecoder::Decoder::InstructionMapping& instructions ); }; } diff --git a/src/Services/DateTimeService.hpp b/src/Services/DateTimeService.hpp index 03346ec4..241c133b 100644 --- a/src/Services/DateTimeService.hpp +++ b/src/Services/DateTimeService.hpp @@ -20,7 +20,7 @@ namespace Services * @return */ static QDateTime currentDateTime() { - const auto lock = std::unique_lock(DateTimeService::systemClockMutex); + const auto lock = std::unique_lock{DateTimeService::systemClockMutex}; return QDateTime::currentDateTime(); } @@ -30,7 +30,7 @@ namespace Services * @return */ static QDate currentDate() { - const auto lock = std::unique_lock(DateTimeService::systemClockMutex); + const auto lock = std::unique_lock{DateTimeService::systemClockMutex}; return QDateTime::currentDateTime().date(); } @@ -43,7 +43,7 @@ namespace Services * @return */ static QString getTimeZoneAbbreviation(const QDateTime& dateTime) { - const auto lock = std::unique_lock(DateTimeService::systemClockMutex); + const auto lock = std::unique_lock{DateTimeService::systemClockMutex}; return dateTime.timeZoneAbbreviation(); } diff --git a/src/Services/PathService.cpp b/src/Services/PathService.cpp index 71e241ee..4698776f 100644 --- a/src/Services/PathService.cpp +++ b/src/Services/PathService.cpp @@ -10,12 +10,12 @@ namespace Services { std::string PathService::applicationDirPath() { - auto pathCharArray = std::array(); + auto pathCharArray = std::array{}; if (::readlink("/proc/self/exe", pathCharArray.data(), PATH_MAX) < 0) { - throw Exceptions::Exception("Failed to obtain application directory path."); + throw Exceptions::Exception{"Failed to obtain application directory path."}; } - return std::filesystem::path(std::string(pathCharArray.begin(), pathCharArray.end())).parent_path(); + return std::filesystem::path{std::string{pathCharArray.begin(), pathCharArray.end()}}.parent_path(); } } diff --git a/src/Services/PathService.hpp b/src/Services/PathService.hpp index 534501d7..2273b2f1 100644 --- a/src/Services/PathService.hpp +++ b/src/Services/PathService.hpp @@ -56,7 +56,7 @@ namespace Services * * @return */ - static std::string targetDescriptionDirPath() { + static std::string targetDescriptionFilesDirPath() { return PathService::resourcesDirPath() + "/TargetDescriptionFiles"; } diff --git a/src/Services/ProcessService.cpp b/src/Services/ProcessService.cpp index f251f8e2..b801ef21 100644 --- a/src/Services/ProcessService.cpp +++ b/src/Services/ProcessService.cpp @@ -9,11 +9,11 @@ namespace Services { ::pid_t ProcessService::getProcessId() { - return getpid(); + return ::getpid(); } ::pid_t ProcessService::getParentProcessId() { - return getppid(); + return ::getppid(); } ::uid_t ProcessService::getEffectiveUserId(std::optional<::pid_t> processId) { @@ -24,9 +24,9 @@ namespace Services const auto processInfo = ProcessService::getProcessInfo(processId.value()); if (!processInfo) { - throw Exceptions::Exception( + throw Exceptions::Exception{ "Failed to fetch process info for process ID " + std::to_string(processId.value()) - ); + }; } return static_cast<::uid_t>(processInfo->euid); @@ -41,7 +41,7 @@ namespace Services processId = ProcessService::getProcessId(); } - static auto cachedResultsByProcessId = std::map<::pid_t, bool>(); + static auto cachedResultsByProcessId = std::map<::pid_t, bool>{}; const auto cachedResultIt = cachedResultsByProcessId.find(*processId); if (cachedResultIt != cachedResultsByProcessId.end()) { @@ -59,7 +59,7 @@ namespace Services auto pid = processInfo->ppid; while (const auto processInfo = ProcessService::getProcessInfo(pid)) { - const auto commandLine = std::string(*(processInfo->cmdline)); + const auto commandLine = std::string{*(processInfo->cmdline)}; if (commandLine.find("clion") != std::string::npos) { cachedResultsByProcessId[*processId] = true; @@ -74,11 +74,11 @@ namespace Services } ProcessService::Proc ProcessService::getProcessInfo(::pid_t processId) { - const auto proc = std::unique_ptr<::PROCTAB, decltype(&::closeproc)>( + const auto proc = std::unique_ptr<::PROCTAB, decltype(&::closeproc)>{ ::openproc(PROC_FILLSTAT | PROC_FILLARG | PROC_PID, &processId), ::closeproc - ); + }; - return Proc(::readproc(proc.get(), NULL), ::freeproc); + return {::readproc(proc.get(), NULL), ::freeproc}; } } diff --git a/src/Services/StringService.cpp b/src/Services/StringService.cpp index 1995ba2f..a906016a 100644 --- a/src/Services/StringService.cpp +++ b/src/Services/StringService.cpp @@ -5,6 +5,7 @@ #include #include #include +#include namespace Services { @@ -39,19 +40,19 @@ namespace Services } std::string StringService::toHex(std::uint32_t value) { - auto stream = std::stringstream(); + auto stream = std::stringstream{}; stream << std::hex << std::setfill('0') << std::setw(8) << static_cast(value); return stream.str(); } std::string StringService::toHex(unsigned char value) { - auto stream = std::stringstream(); + auto stream = std::stringstream{}; stream << std::hex << std::setfill('0') << std::setw(2) << static_cast(value); return stream.str(); } std::string StringService::toHex(const std::vector& data) { - auto stream = std::stringstream(); + auto stream = std::stringstream{}; stream << std::hex << std::setfill('0'); for (const auto& byte : data) { @@ -62,7 +63,7 @@ namespace Services } std::string StringService::toHex(const std::string& data) { - std::stringstream stream; + auto stream = std::stringstream{}; stream << std::hex << std::setfill('0'); for (const auto& byte : data) { @@ -72,20 +73,36 @@ namespace Services return stream.str(); } - std::uint64_t StringService::toUint64(const std::string& str) { - return static_cast(std::stoul(str, nullptr, 0)); + std::vector StringService::dataFromHex(const std::string& hexData) { + auto output = std::vector{}; + + for (auto i = 0; i < hexData.size(); i += 2) { + const auto hexByte = std::string{(hexData.begin() + i), (hexData.begin() + i + 2)}; + output.push_back(static_cast(std::stoi(hexByte, nullptr, 16))); + } + + return output; } - std::uint32_t StringService::toUint32(const std::string& str) { - return static_cast(StringService::toUint64(str)); + std::uint64_t StringService::toUint64(const std::string& str, int base) { + return static_cast(std::stoul(str, nullptr, base)); } - std::uint16_t StringService::toUint16(const std::string& str) { - return static_cast(StringService::toUint64(str)); + std::uint32_t StringService::toUint32(const std::string& str, int base) { + return static_cast(StringService::toUint64(str, base)); } - std::uint8_t StringService::toUint8(const std::string& str) { - return static_cast(StringService::toUint64(str)); + std::uint16_t StringService::toUint16(const std::string& str, int base) { + return static_cast(StringService::toUint64(str, base)); + } + + std::uint8_t StringService::toUint8(const std::string& str, int base) { + return static_cast(StringService::toUint64(str, base)); + } + + std::size_t StringService::hash(const std::string& str) { + static const auto hash = std::hash{}; + return hash(str); } std::vector StringService::split(std::string_view str, char delimiter) { diff --git a/src/Services/StringService.hpp b/src/Services/StringService.hpp index 9e17b6fa..d5c16b2c 100644 --- a/src/Services/StringService.hpp +++ b/src/Services/StringService.hpp @@ -22,10 +22,14 @@ namespace Services static std::string toHex(const std::vector& data); static std::string toHex(const std::string& data); - static std::uint64_t toUint64(const std::string& str); - static std::uint32_t toUint32(const std::string& str); - static std::uint16_t toUint16(const std::string& str); - static std::uint8_t toUint8(const std::string& str); + static std::vector dataFromHex(const std::string& hexData); + + static std::uint64_t toUint64(const std::string& str, int base = 0); + static std::uint32_t toUint32(const std::string& str, int base = 0); + static std::uint16_t toUint16(const std::string& str, int base = 0); + static std::uint8_t toUint8(const std::string& str, int base = 0); + + static std::size_t hash(const std::string& str); static std::vector split(std::string_view str, char delimiter); }; diff --git a/src/Services/TargetControllerService.cpp b/src/Services/TargetControllerService.cpp index 10370d16..6b613ce5 100644 --- a/src/Services/TargetControllerService.cpp +++ b/src/Services/TargetControllerService.cpp @@ -17,8 +17,9 @@ #include "src/TargetController/Commands/SetBreakpoint.hpp" #include "src/TargetController/Commands/RemoveBreakpoint.hpp" #include "src/TargetController/Commands/SetTargetProgramCounter.hpp" -#include "src/TargetController/Commands/GetTargetPinStates.hpp" -#include "src/TargetController/Commands/SetTargetPinState.hpp" +#include "src/TargetController/Commands/SetTargetStackPointer.hpp" +#include "src/TargetController/Commands/GetTargetGpioPinStates.hpp" +#include "src/TargetController/Commands/SetTargetGpioPinState.hpp" #include "src/TargetController/Commands/GetTargetStackPointer.hpp" #include "src/TargetController/Commands/GetTargetProgramCounter.hpp" #include "src/TargetController/Commands/EnableProgrammingMode.hpp" @@ -45,8 +46,9 @@ namespace Services using TargetController::Commands::SetBreakpoint; using TargetController::Commands::RemoveBreakpoint; using TargetController::Commands::SetTargetProgramCounter; - using TargetController::Commands::GetTargetPinStates; - using TargetController::Commands::SetTargetPinState; + using TargetController::Commands::SetTargetStackPointer; + using TargetController::Commands::GetTargetGpioPinStates; + using TargetController::Commands::SetTargetGpioPinState; using TargetController::Commands::GetTargetStackPointer; using TargetController::Commands::GetTargetProgramCounter; using TargetController::Commands::EnableProgrammingMode; @@ -56,10 +58,12 @@ namespace Services using Targets::TargetDescriptor; using Targets::TargetState; - using Targets::TargetRegisters; + using Targets::TargetRegisterDescriptor; using Targets::TargetRegisterDescriptors; + using Targets::TargetRegisterDescriptorAndValuePairs; - using Targets::TargetMemoryType; + using Targets::TargetAddressSpaceDescriptor; + using Targets::TargetMemorySegmentDescriptor; using Targets::TargetMemoryAddress; using Targets::TargetMemorySize; using Targets::TargetMemoryAddressRange; @@ -68,9 +72,10 @@ namespace Services using Targets::TargetBreakpoint; + using Targets::TargetPinoutDescriptor; using Targets::TargetPinDescriptor; - using Targets::TargetPinState; - using Targets::TargetPinStateMapping; + using Targets::TargetGpioPinState; + using Targets::TargetGpioPinDescriptorAndStatePairs; TargetControllerService::AtomicSession::AtomicSession(TargetControllerService& targetControllerService) : targetControllerService(targetControllerService) @@ -90,7 +95,7 @@ namespace Services } catch (const std::exception& exception) { Logger::error( "Failed to end atomic session (ID: " + std::to_string(this->sessionId) + ") - " - + std::string(exception.what()) + + std::string{exception.what()} ); } @@ -102,7 +107,7 @@ namespace Services } } - const TargetDescriptor& TargetControllerService::getTargetDescriptor() const { + const Targets::TargetDescriptor& TargetControllerService::getTargetDescriptor() const { return this->commandManager.sendCommandAndWaitForResponse( std::make_unique(), this->defaultTimeout, @@ -126,52 +131,41 @@ namespace Services ); } - void TargetControllerService::continueTargetExecution( - std::optional fromAddress, - std::optional toAddress - ) const { - auto resumeExecutionCommand = std::make_unique(); - - if (fromAddress.has_value()) { - resumeExecutionCommand->fromAddress = fromAddress.value(); - } - - if (toAddress.has_value()) { - resumeExecutionCommand->toAddress = toAddress.value(); - } - + void TargetControllerService::resumeTargetExecution() const { this->commandManager.sendCommandAndWaitForResponse( - std::move(resumeExecutionCommand), + std::make_unique(), this->defaultTimeout, this->activeAtomicSessionId ); } - void TargetControllerService::stepTargetExecution(std::optional fromAddress) const { - auto stepExecutionCommand = std::make_unique(); - - if (fromAddress.has_value()) { - stepExecutionCommand->fromProgramCounter = fromAddress.value(); - } - + void TargetControllerService::stepTargetExecution() const { this->commandManager.sendCommandAndWaitForResponse( - std::move(stepExecutionCommand), + std::make_unique(), this->defaultTimeout, this->activeAtomicSessionId ); } - TargetRegisters TargetControllerService::readRegisters( - const Targets::TargetRegisterDescriptorIds& descriptorIds + TargetRegisterDescriptorAndValuePairs TargetControllerService::readRegisters( + const TargetRegisterDescriptors& descriptors ) const { return this->commandManager.sendCommandAndWaitForResponse( - std::make_unique(descriptorIds), + std::make_unique(descriptors), this->defaultTimeout, this->activeAtomicSessionId )->registers; } - void TargetControllerService::writeRegisters(const TargetRegisters& registers) const { + TargetMemoryBuffer TargetControllerService::readRegister(const TargetRegisterDescriptor& descriptor) const { + return this->commandManager.sendCommandAndWaitForResponse( + std::make_unique(TargetRegisterDescriptors{&descriptor}), + this->defaultTimeout, + this->activeAtomicSessionId + )->registers.at(0).second; + } + + void TargetControllerService::writeRegisters(const TargetRegisterDescriptorAndValuePairs& registers) const { this->commandManager.sendCommandAndWaitForResponse( std::make_unique(registers), this->defaultTimeout, @@ -179,8 +173,20 @@ namespace Services ); } + void TargetControllerService::writeRegister( + const TargetRegisterDescriptor& descriptor, + const TargetMemoryBuffer& value + ) const { + this->commandManager.sendCommandAndWaitForResponse( + std::make_unique(TargetRegisterDescriptorAndValuePairs{{descriptor, value}}), + this->defaultTimeout, + this->activeAtomicSessionId + ); + } + TargetMemoryBuffer TargetControllerService::readMemory( - TargetMemoryType memoryType, + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, TargetMemoryAddress startAddress, TargetMemorySize bytes, bool bypassCache, @@ -188,7 +194,8 @@ namespace Services ) const { return this->commandManager.sendCommandAndWaitForResponse( std::make_unique( - memoryType, + addressSpaceDescriptor, + memorySegmentDescriptor, startAddress, bytes, bypassCache, @@ -200,20 +207,29 @@ namespace Services } void TargetControllerService::writeMemory( - TargetMemoryType memoryType, + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, TargetMemoryAddress startAddress, - const TargetMemoryBuffer& buffer + Targets::TargetMemoryBuffer&& buffer ) const { this->commandManager.sendCommandAndWaitForResponse( - std::make_unique(memoryType, startAddress, buffer), + std::make_unique( + addressSpaceDescriptor, + memorySegmentDescriptor, + startAddress, + std::move(buffer) + ), this->defaultTimeout, this->activeAtomicSessionId ); } - void TargetControllerService::eraseMemory(Targets::TargetMemoryType memoryType) const { + void TargetControllerService::eraseMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor + ) const { this->commandManager.sendCommandAndWaitForResponse( - std::make_unique(memoryType), + std::make_unique(addressSpaceDescriptor, memorySegmentDescriptor), this->defaultTimeout, this->activeAtomicSessionId ); @@ -254,17 +270,22 @@ namespace Services ); } - TargetPinStateMapping TargetControllerService::getPinStates(int variantId) const { + TargetGpioPinDescriptorAndStatePairs TargetControllerService::getGpioPinStates( + const TargetPinoutDescriptor& pinoutDescriptor + ) const { return this->commandManager.sendCommandAndWaitForResponse( - std::make_unique(variantId), + std::make_unique(pinoutDescriptor), this->defaultTimeout, this->activeAtomicSessionId - )->pinStatesByNumber; + )->gpioPinStates; } - void TargetControllerService::setPinState(TargetPinDescriptor pinDescriptor, TargetPinState pinState) const { + void TargetControllerService::setGpioPinState( + const TargetPinDescriptor& pinDescriptor, + const TargetGpioPinState& state + ) const { this->commandManager.sendCommandAndWaitForResponse( - std::make_unique(pinDescriptor, pinState), + std::make_unique(pinDescriptor, state), this->defaultTimeout, this->activeAtomicSessionId ); @@ -278,6 +299,14 @@ namespace Services )->stackPointer; } + void TargetControllerService::setStackPointer(TargetStackPointer stackPointer) const { + this->commandManager.sendCommandAndWaitForResponse( + std::make_unique(stackPointer), + this->defaultTimeout, + this->activeAtomicSessionId + ); + } + void TargetControllerService::resetTarget() const { this->commandManager.sendCommandAndWaitForResponse( std::make_unique(), @@ -311,7 +340,7 @@ namespace Services } TargetControllerService::AtomicSession TargetControllerService::makeAtomicSession() { - return AtomicSession(*this); + return AtomicSession{*this}; } TargetController::AtomicSessionIdType TargetControllerService::startAtomicSession() { diff --git a/src/Services/TargetControllerService.hpp b/src/Services/TargetControllerService.hpp index a150c787..ac11272b 100644 --- a/src/Services/TargetControllerService.hpp +++ b/src/Services/TargetControllerService.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -9,12 +10,16 @@ #include "src/TargetController/AtomicSession.hpp" #include "src/Targets/TargetState.hpp" +#include "src/Targets/TargetAddressSpaceDescriptor.hpp" +#include "src/Targets/TargetMemorySegmentDescriptor.hpp" +#include "src/Targets/TargetPeripheralDescriptor.hpp" +#include "src/Targets/TargetRegisterGroupDescriptor.hpp" #include "src/Targets/TargetRegisterDescriptor.hpp" -#include "src/Targets/TargetRegister.hpp" +#include "src/Targets/TargetPinoutDescriptor.hpp" +#include "src/Targets/TargetPinDescriptor.hpp" +#include "src/Targets/TargetGpioPinState.hpp" #include "src/Targets/TargetMemory.hpp" #include "src/Targets/TargetBreakpoint.hpp" -#include "src/Targets/TargetVariant.hpp" -#include "src/Targets/TargetPinDescriptor.hpp" #include "src/Exceptions/Exception.hpp" @@ -71,43 +76,58 @@ namespace Services void stopTargetExecution() const; /** - * Requests the TargetController to continue execution on the target. - * - * @param fromAddress + * Requests the TargetController to resume execution on the target. */ - void continueTargetExecution( - std::optional fromAddress, - std::optional toAddress - ) const; + void resumeTargetExecution() const; /** * Requests the TargetController to step execution on the target. - * - * @param fromAddress */ - void stepTargetExecution(std::optional fromAddress) const; + void stepTargetExecution() const; /** * Requests the TargetController to read register values from the target. * - * @param descriptorIds - * Descriptor IDs of the registers to read. + * @param descriptors + * Descriptors of the registers to read. * * @return */ - Targets::TargetRegisters readRegisters(const Targets::TargetRegisterDescriptorIds& descriptorIds) const; + Targets::TargetRegisterDescriptorAndValuePairs readRegisters( + const Targets::TargetRegisterDescriptors& descriptors + ) const; + + /** + * Requests the TargetController to read a single register value from the target. + * + * @param descriptor + * @return + */ + Targets::TargetMemoryBuffer readRegister(const Targets::TargetRegisterDescriptor& descriptor) const; /** * Requests the TargetController to write register values to the target. * * @param registers */ - void writeRegisters(const Targets::TargetRegisters& registers) const; + void writeRegisters(const Targets::TargetRegisterDescriptorAndValuePairs& registers) const; + + /** + * Requests the TargetController to write to a single register on the target. + * + * @param descriptor + * @param value + */ + void writeRegister( + const Targets::TargetRegisterDescriptor& descriptor, + const Targets::TargetMemoryBuffer& value + ) const; /** * Requests the TargetController to read memory from the target. * - * @param memoryType + * @param addressSpaceDescriptor + * @param memorySegmentDescriptor * @param startAddress * @param bytes * @param bypassCache @@ -115,7 +135,8 @@ namespace Services * @return */ Targets::TargetMemoryBuffer readMemory( - Targets::TargetMemoryType memoryType, + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, Targets::TargetMemoryAddress startAddress, Targets::TargetMemorySize bytes, bool bypassCache = false, @@ -125,22 +146,28 @@ namespace Services /** * Requests the TargetController to write memory to the target. * - * @param memoryType + * @param addressSpaceDescriptor + * @param memorySegmentDescriptor * @param startAddress * @param buffer */ void writeMemory( - Targets::TargetMemoryType memoryType, + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, Targets::TargetMemoryAddress startAddress, - const Targets::TargetMemoryBuffer& buffer + Targets::TargetMemoryBuffer&& buffer ) const; /** - * Requests the TargetController to erase the given target memory type. + * Requests the TargetController to erase the given target memory segment. * - * @param memoryType + * @param addressSpaceDescriptor + * @param memorySegmentDescriptor */ - void eraseMemory(Targets::TargetMemoryType memoryType) const; + void eraseMemory( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor + ) const; /** * Requests the TargetController to set a breakpoint on the target. @@ -180,17 +207,22 @@ namespace Services /** * Retrieves the pin states for a particular target variant. * - * @param variantId + * @param pinoutDescriptor */ - Targets::TargetPinStateMapping getPinStates(int variantId) const; + Targets::TargetGpioPinDescriptorAndStatePairs getGpioPinStates( + const Targets::TargetPinoutDescriptor& pinoutDescriptor + ) const; /** * Updates the pin state on the target, for a specific pin. * * @param pinDescriptor - * @param pinState + * @param state */ - void setPinState(Targets::TargetPinDescriptor pinDescriptor, Targets::TargetPinState pinState) const; + void setGpioPinState( + const Targets::TargetPinDescriptor& pinDescriptor, + const Targets::TargetGpioPinState& state + ) const; /** * Retrieves the current stack pointer value from the target. @@ -199,6 +231,13 @@ namespace Services */ Targets::TargetStackPointer getStackPointer() const; + /** + * Sets the target's stack pointer to the given value. + * + * @param stackPointer + */ + void setStackPointer(Targets::TargetStackPointer stackPointer) const; + /** * Triggers a reset on the target. The target will be held in a stopped state. */ diff --git a/src/Services/TargetService.hpp b/src/Services/TargetService.hpp index f84450bd..d1af21d5 100644 --- a/src/Services/TargetService.hpp +++ b/src/Services/TargetService.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include "src/Targets/BriefTargetDescriptor.hpp" @@ -36,7 +35,9 @@ namespace Services private: /** * This mapping contains brief target descriptor objects for every target supported by Bloom, mapped by their - * configuration value. The contents is generated by a build script and stored in an ASCII text file, located + * configuration value. + * + * The contents of this mapping is generated by a build script and stored in an ASCII text file, located * at GENERATED_BRIEF_TARGET_DESCRIPTOR_MAPPING_PATH. * * See build/scripts/GenerateBriefTargetDescriptors.php and the root CMakeLists.txt for more. diff --git a/src/SignalHandler/SignalHandler.cpp b/src/SignalHandler/SignalHandler.cpp index db91cac8..2aa80112 100644 --- a/src/SignalHandler/SignalHandler.cpp +++ b/src/SignalHandler/SignalHandler.cpp @@ -10,7 +10,7 @@ void SignalHandler::run() { try { this->startup(); const auto signalSet = this->getRegisteredSignalSet(); - int signalNumber = 0; + auto signalNumber = int{0}; Logger::debug("SignalHandler ready"); while(Thread::getThreadState() == ThreadState::READY) { @@ -26,7 +26,7 @@ void SignalHandler::run() { } } catch (std::exception& exception) { - Logger::error("SignalHandler fatal error: " + std::string(exception.what())); + Logger::error("SignalHandler fatal error: " + std::string{exception.what()}); } Logger::info("Shutting down SignalHandler"); @@ -42,15 +42,8 @@ void SignalHandler::startup() { ::sigprocmask(SIG_SETMASK, &signalSet, NULL); // Register handlers - this->handlersBySignalNum.insert(std::pair( - SIGINT, - std::bind(&SignalHandler::triggerApplicationShutdown, this) - )); - - this->handlersBySignalNum.insert(std::pair( - SIGTERM, - std::bind(&SignalHandler::triggerApplicationShutdown, this) - )); + this->handlersBySignalNum.emplace(SIGINT, std::bind(&SignalHandler::triggerApplicationShutdown, this)); + this->handlersBySignalNum.emplace(SIGTERM, std::bind(&SignalHandler::triggerApplicationShutdown, this)); // It's possible that the SignalHandler has been instructed to shut down, before it could finish starting up. if (this->getThreadState() != ThreadState::SHUTDOWN_INITIATED) { @@ -59,7 +52,7 @@ void SignalHandler::startup() { } ::sigset_t SignalHandler::getRegisteredSignalSet() const { - ::sigset_t set = {}; + auto set = ::sigset_t{}; if (::sigfillset(&set) == -1) { throw Exceptions::Exception("::sigfillset() failed - error number: " + std::to_string(errno)); } diff --git a/src/TargetController/CommandManager.hpp b/src/TargetController/CommandManager.hpp index aeac07c9..c2ca57e1 100644 --- a/src/TargetController/CommandManager.hpp +++ b/src/TargetController/CommandManager.hpp @@ -43,7 +43,7 @@ namespace TargetController Logger::debug( "Timed out whilst waiting for TargetController to respond to " + CommandType::name + " command" ); - throw Exceptions::Exception("Command timed out"); + throw Exceptions::Exception{"Command timed out"}; } auto& response = optionalResponse.value(); @@ -55,7 +55,7 @@ namespace TargetController "TargetController returned error in response to " + CommandType::name + " command (ID: " + std::to_string(commandId) + "). Error: " + errorResponse->errorMessage ); - throw Exceptions::Exception(errorResponse->errorMessage); + throw Exceptions::Exception{errorResponse->errorMessage}; } Logger::debug( diff --git a/src/TargetController/Commands/CommandTypes.hpp b/src/TargetController/Commands/CommandTypes.hpp index 605f8be5..615c52b2 100644 --- a/src/TargetController/Commands/CommandTypes.hpp +++ b/src/TargetController/Commands/CommandTypes.hpp @@ -24,8 +24,9 @@ namespace TargetController::Commands SET_BREAKPOINT, REMOVE_BREAKPOINT, SET_TARGET_PROGRAM_COUNTER, - GET_TARGET_PIN_STATES, - SET_TARGET_PIN_STATE, + SET_TARGET_STACK_POINTER, + GET_TARGET_GPIO_PIN_STATES, + SET_TARGET_GPIO_PIN_STATE, GET_TARGET_STACK_POINTER, GET_TARGET_PROGRAM_COUNTER, ENABLE_PROGRAMMING_MODE, diff --git a/src/TargetController/Commands/EraseTargetMemory.hpp b/src/TargetController/Commands/EraseTargetMemory.hpp index 1c28aabc..9533a3e2 100644 --- a/src/TargetController/Commands/EraseTargetMemory.hpp +++ b/src/TargetController/Commands/EraseTargetMemory.hpp @@ -2,7 +2,9 @@ #include "Command.hpp" -#include "src/Targets/TargetMemory.hpp" +#include "src/Targets/TargetAddressSpaceDescriptor.hpp" +#include "src/Targets/TargetMemorySegmentDescriptor.hpp" +#include "src/Targets/TargetMemorySegmentType.hpp" namespace TargetController::Commands { @@ -12,10 +14,15 @@ namespace TargetController::Commands static constexpr CommandType type = CommandType::ERASE_TARGET_MEMORY; static const inline std::string name = "EraseTargetMemory"; - Targets::TargetMemoryType memoryType; + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor; + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor; - EraseTargetMemory(Targets::TargetMemoryType memoryType) - : memoryType(memoryType) + EraseTargetMemory( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor + ) + : addressSpaceDescriptor(addressSpaceDescriptor) + , memorySegmentDescriptor(memorySegmentDescriptor) {}; [[nodiscard]] CommandType getType() const override { @@ -27,7 +34,7 @@ namespace TargetController::Commands } [[nodiscard]] bool requiresDebugMode() const override { - return this->memoryType == Targets::TargetMemoryType::RAM; + return this->memorySegmentDescriptor.type == Targets::TargetMemorySegmentType::RAM; } }; } diff --git a/src/TargetController/Commands/GetTargetGpioPinStates.hpp b/src/TargetController/Commands/GetTargetGpioPinStates.hpp new file mode 100644 index 00000000..3d04a91f --- /dev/null +++ b/src/TargetController/Commands/GetTargetGpioPinStates.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include "Command.hpp" + +#include "src/TargetController/Responses/TargetGpioPinStates.hpp" + +namespace TargetController::Commands +{ + class GetTargetGpioPinStates: public Command + { + public: + using SuccessResponseType = Responses::TargetGpioPinStates; + + static constexpr CommandType type = CommandType::GET_TARGET_GPIO_PIN_STATES; + static const inline std::string name = "GetTargetGpioPinStates"; + + const Targets::TargetPinoutDescriptor& pinoutDescriptor; + + explicit GetTargetGpioPinStates(const Targets::TargetPinoutDescriptor& pinoutDescriptor) + : pinoutDescriptor(pinoutDescriptor) + {}; + + [[nodiscard]] CommandType getType() const override { + return GetTargetGpioPinStates::type; + } + + [[nodiscard]] bool requiresStoppedTargetState() const override { + return true; + } + }; +} diff --git a/src/TargetController/Commands/GetTargetPinStates.hpp b/src/TargetController/Commands/GetTargetPinStates.hpp deleted file mode 100644 index 0d2eb174..00000000 --- a/src/TargetController/Commands/GetTargetPinStates.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -#include "Command.hpp" - -#include "src/TargetController/Responses/TargetPinStates.hpp" - -namespace TargetController::Commands -{ - class GetTargetPinStates: public Command - { - public: - using SuccessResponseType = Responses::TargetPinStates; - - static constexpr CommandType type = CommandType::GET_TARGET_PIN_STATES; - static const inline std::string name = "GetTargetPinStates"; - - int variantId = 0; - - explicit GetTargetPinStates(int variantId) - : variantId(variantId) - {}; - - [[nodiscard]] CommandType getType() const override { - return GetTargetPinStates::type; - } - - [[nodiscard]] bool requiresStoppedTargetState() const override { - return true; - } - }; -} diff --git a/src/TargetController/Commands/ReadTargetMemory.hpp b/src/TargetController/Commands/ReadTargetMemory.hpp index a22d7aa7..c89c18b2 100644 --- a/src/TargetController/Commands/ReadTargetMemory.hpp +++ b/src/TargetController/Commands/ReadTargetMemory.hpp @@ -6,6 +6,9 @@ #include "Command.hpp" #include "src/TargetController/Responses/TargetMemoryRead.hpp" +#include "src/Targets/TargetAddressSpaceDescriptor.hpp" +#include "src/Targets/TargetMemorySegmentDescriptor.hpp" +#include "src/Targets/TargetMemorySegmentType.hpp" #include "src/Targets/TargetMemory.hpp" namespace TargetController::Commands @@ -18,7 +21,8 @@ namespace TargetController::Commands static constexpr CommandType type = CommandType::READ_TARGET_MEMORY; static const inline std::string name = "ReadTargetMemory"; - Targets::TargetMemoryType memoryType; + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor; + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor; Targets::TargetMemoryAddress startAddress; Targets::TargetMemorySize bytes; @@ -30,13 +34,15 @@ namespace TargetController::Commands std::set excludedAddressRanges; ReadTargetMemory( - Targets::TargetMemoryType memoryType, + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, Targets::TargetMemoryAddress startAddress, Targets::TargetMemorySize bytes, bool bypassCache = false, const std::set& excludedAddressRanges = {} ) - : memoryType(memoryType) + : addressSpaceDescriptor(addressSpaceDescriptor) + , memorySegmentDescriptor(memorySegmentDescriptor) , startAddress(startAddress) , bytes(bytes) , bypassCache(bypassCache) @@ -52,7 +58,7 @@ namespace TargetController::Commands } [[nodiscard]] bool requiresDebugMode() const override { - return this->memoryType == Targets::TargetMemoryType::RAM; + return this->memorySegmentDescriptor.type == Targets::TargetMemorySegmentType::RAM; } }; } diff --git a/src/TargetController/Commands/ReadTargetRegisters.hpp b/src/TargetController/Commands/ReadTargetRegisters.hpp index a8df6793..80554981 100644 --- a/src/TargetController/Commands/ReadTargetRegisters.hpp +++ b/src/TargetController/Commands/ReadTargetRegisters.hpp @@ -15,10 +15,10 @@ namespace TargetController::Commands static constexpr CommandType type = CommandType::READ_TARGET_REGISTERS; static const inline std::string name = "ReadTargetRegisters"; - std::set descriptorIds; + const Targets::TargetRegisterDescriptors descriptors; - explicit ReadTargetRegisters(const std::set& descriptorIds) - : descriptorIds(descriptorIds) + explicit ReadTargetRegisters(const Targets::TargetRegisterDescriptors& descriptors) + : descriptors(descriptors) {}; [[nodiscard]] CommandType getType() const override { diff --git a/src/TargetController/Commands/ResumeTargetExecution.hpp b/src/TargetController/Commands/ResumeTargetExecution.hpp index 3f59c72e..f8ae41cb 100644 --- a/src/TargetController/Commands/ResumeTargetExecution.hpp +++ b/src/TargetController/Commands/ResumeTargetExecution.hpp @@ -1,7 +1,5 @@ #pragma once -#include - #include "Command.hpp" #include "src/Targets/TargetMemory.hpp" @@ -14,14 +12,7 @@ namespace TargetController::Commands static constexpr CommandType type = CommandType::RESUME_TARGET_EXECUTION; static const inline std::string name = "ResumeTargetExecution"; - std::optional fromAddress; - std::optional toAddress; - ResumeTargetExecution() = default; - ResumeTargetExecution(Targets::TargetMemoryAddress fromAddress, Targets::TargetMemoryAddress toAddress) - : fromAddress(fromAddress) - , toAddress(toAddress) - {}; [[nodiscard]] CommandType getType() const override { return ResumeTargetExecution::type; diff --git a/src/TargetController/Commands/SetTargetPinState.hpp b/src/TargetController/Commands/SetTargetGpioPinState.hpp similarity index 54% rename from src/TargetController/Commands/SetTargetPinState.hpp rename to src/TargetController/Commands/SetTargetGpioPinState.hpp index 79d75abc..014372fa 100644 --- a/src/TargetController/Commands/SetTargetPinState.hpp +++ b/src/TargetController/Commands/SetTargetGpioPinState.hpp @@ -3,28 +3,29 @@ #include "Command.hpp" #include "src/Targets/TargetPinDescriptor.hpp" +#include "src/Targets/TargetGpioPinState.hpp" namespace TargetController::Commands { - class SetTargetPinState: public Command + class SetTargetGpioPinState: public Command { public: - static constexpr CommandType type = CommandType::SET_TARGET_PIN_STATE; - static const inline std::string name = "SetTargetPinState"; + static constexpr CommandType type = CommandType::SET_TARGET_GPIO_PIN_STATE; + static const inline std::string name = "SetTargetGpioPinState"; - Targets::TargetPinDescriptor pinDescriptor; - Targets::TargetPinState pinState; + const Targets::TargetPinDescriptor& pinDescriptor; + Targets::TargetGpioPinState state; - SetTargetPinState( + SetTargetGpioPinState( const Targets::TargetPinDescriptor& pinDescriptor, - const Targets::TargetPinState& pinState + const Targets::TargetGpioPinState& state ) : pinDescriptor(pinDescriptor) - , pinState(pinState) + , state(state) {}; [[nodiscard]] CommandType getType() const override { - return SetTargetPinState::type; + return SetTargetGpioPinState::type; } [[nodiscard]] bool requiresStoppedTargetState() const override { diff --git a/src/TargetController/Commands/SetTargetProgramCounter.hpp b/src/TargetController/Commands/SetTargetProgramCounter.hpp index 14ad78b7..8933ace8 100644 --- a/src/TargetController/Commands/SetTargetProgramCounter.hpp +++ b/src/TargetController/Commands/SetTargetProgramCounter.hpp @@ -14,7 +14,7 @@ namespace TargetController::Commands static constexpr CommandType type = CommandType::SET_TARGET_PROGRAM_COUNTER; static const inline std::string name = "SetTargetProgramCounter"; - Targets::TargetMemoryAddress address = 0; + Targets::TargetMemoryAddress address; explicit SetTargetProgramCounter(Targets::TargetMemoryAddress address) : address(address) diff --git a/src/TargetController/Commands/SetTargetStackPointer.hpp b/src/TargetController/Commands/SetTargetStackPointer.hpp new file mode 100644 index 00000000..306102cd --- /dev/null +++ b/src/TargetController/Commands/SetTargetStackPointer.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +#include "Command.hpp" + +#include "src/Targets/TargetMemory.hpp" + +namespace TargetController::Commands +{ + class SetTargetStackPointer: public Command + { + public: + static constexpr CommandType type = CommandType::SET_TARGET_STACK_POINTER; + static const inline std::string name = "SetTargetStackPointer"; + + Targets::TargetStackPointer stackPointer; + + explicit SetTargetStackPointer(Targets::TargetStackPointer stackPointer) + : stackPointer(stackPointer) + {}; + + [[nodiscard]] CommandType getType() const override { + return SetTargetStackPointer::type; + } + + [[nodiscard]] bool requiresStoppedTargetState() const override { + return true; + } + }; +} diff --git a/src/TargetController/Commands/StepTargetExecution.hpp b/src/TargetController/Commands/StepTargetExecution.hpp index 10eeb1a0..fc5e7661 100644 --- a/src/TargetController/Commands/StepTargetExecution.hpp +++ b/src/TargetController/Commands/StepTargetExecution.hpp @@ -14,13 +14,6 @@ namespace TargetController::Commands static constexpr CommandType type = CommandType::STEP_TARGET_EXECUTION; static const inline std::string name = "StepTargetExecution"; - std::optional fromProgramCounter; - - StepTargetExecution() = default; - explicit StepTargetExecution(Targets::TargetMemoryAddress fromProgramCounter) - : fromProgramCounter(fromProgramCounter) - {}; - [[nodiscard]] CommandType getType() const override { return StepTargetExecution::type; } diff --git a/src/TargetController/Commands/WriteTargetMemory.hpp b/src/TargetController/Commands/WriteTargetMemory.hpp index f669dbc0..40fc5622 100644 --- a/src/TargetController/Commands/WriteTargetMemory.hpp +++ b/src/TargetController/Commands/WriteTargetMemory.hpp @@ -2,6 +2,9 @@ #include "Command.hpp" +#include "src/Targets/TargetAddressSpaceDescriptor.hpp" +#include "src/Targets/TargetMemorySegmentDescriptor.hpp" +#include "src/Targets/TargetMemorySegmentType.hpp" #include "src/Targets/TargetMemory.hpp" namespace TargetController::Commands @@ -12,18 +15,21 @@ namespace TargetController::Commands static constexpr CommandType type = CommandType::WRITE_TARGET_MEMORY; static const inline std::string name = "WriteTargetMemory"; - Targets::TargetMemoryType memoryType; + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor; + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor; Targets::TargetMemoryAddress startAddress; Targets::TargetMemoryBuffer buffer; WriteTargetMemory( - Targets::TargetMemoryType memoryType, + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, Targets::TargetMemoryAddress startAddress, - const Targets::TargetMemoryBuffer& buffer + Targets::TargetMemoryBuffer&& buffer ) - : memoryType(memoryType) + : addressSpaceDescriptor(addressSpaceDescriptor) + , memorySegmentDescriptor(memorySegmentDescriptor) , startAddress(startAddress) - , buffer(buffer) + , buffer(std::move(buffer)) {}; [[nodiscard]] CommandType getType() const override { @@ -35,7 +41,7 @@ namespace TargetController::Commands } [[nodiscard]] bool requiresDebugMode() const override { - return this->memoryType == Targets::TargetMemoryType::RAM; + return this->memorySegmentDescriptor.type == Targets::TargetMemorySegmentType::RAM; } }; } diff --git a/src/TargetController/Commands/WriteTargetRegisters.hpp b/src/TargetController/Commands/WriteTargetRegisters.hpp index 0d833907..386e1799 100644 --- a/src/TargetController/Commands/WriteTargetRegisters.hpp +++ b/src/TargetController/Commands/WriteTargetRegisters.hpp @@ -2,7 +2,7 @@ #include "Command.hpp" -#include "src/Targets/TargetRegister.hpp" +#include "src/Targets/TargetRegisterDescriptor.hpp" namespace TargetController::Commands { @@ -12,9 +12,9 @@ namespace TargetController::Commands static constexpr CommandType type = CommandType::WRITE_TARGET_REGISTERS; static const inline std::string name = "WriteTargetRegisters"; - Targets::TargetRegisters registers; + Targets::TargetRegisterDescriptorAndValuePairs registers; - explicit WriteTargetRegisters(const Targets::TargetRegisters& registers) + explicit WriteTargetRegisters(const Targets::TargetRegisterDescriptorAndValuePairs& registers) : registers(registers) {}; diff --git a/src/TargetController/Exceptions/DeviceCommunicationFailure.hpp b/src/TargetController/Exceptions/DeviceCommunicationFailure.hpp index 0f8d8fec..ddb40868 100644 --- a/src/TargetController/Exceptions/DeviceCommunicationFailure.hpp +++ b/src/TargetController/Exceptions/DeviceCommunicationFailure.hpp @@ -7,12 +7,8 @@ namespace Exceptions class DeviceCommunicationFailure: public DeviceFailure { public: - explicit DeviceCommunicationFailure(const std::string& message): DeviceFailure(message) { - this->message = message; - } - - explicit DeviceCommunicationFailure(const char* message): DeviceFailure(message) { - this->message = std::string(message); - } + explicit DeviceCommunicationFailure(const std::string& message) + : DeviceFailure(message) + {} }; } diff --git a/src/TargetController/Exceptions/DeviceFailure.hpp b/src/TargetController/Exceptions/DeviceFailure.hpp index 7535d824..91c46941 100644 --- a/src/TargetController/Exceptions/DeviceFailure.hpp +++ b/src/TargetController/Exceptions/DeviceFailure.hpp @@ -7,12 +7,8 @@ namespace Exceptions class DeviceFailure: public Exception { public: - explicit DeviceFailure(const std::string& message): Exception(message) { - this->message = message; - } - - explicit DeviceFailure(const char* message): Exception(message) { - this->message = std::string(message); - } + explicit DeviceFailure(const std::string& message) + : Exception(message) + {} }; } diff --git a/src/TargetController/Exceptions/DeviceInitializationFailure.hpp b/src/TargetController/Exceptions/DeviceInitializationFailure.hpp index 6df88eec..60230fbc 100644 --- a/src/TargetController/Exceptions/DeviceInitializationFailure.hpp +++ b/src/TargetController/Exceptions/DeviceInitializationFailure.hpp @@ -7,12 +7,8 @@ namespace Exceptions class DeviceInitializationFailure: public DeviceFailure { public: - explicit DeviceInitializationFailure(const std::string& message): DeviceFailure(message) { - this->message = message; - } - - explicit DeviceInitializationFailure(const char* message): DeviceFailure(message) { - this->message = std::string(message); - } + explicit DeviceInitializationFailure(const std::string& message) + : DeviceFailure(message) + {} }; } diff --git a/src/TargetController/Exceptions/DeviceNotFound.hpp b/src/TargetController/Exceptions/DeviceNotFound.hpp index 224fc4ac..a4ccda7c 100644 --- a/src/TargetController/Exceptions/DeviceNotFound.hpp +++ b/src/TargetController/Exceptions/DeviceNotFound.hpp @@ -7,12 +7,8 @@ namespace Exceptions class DeviceNotFound: public Exception { public: - explicit DeviceNotFound(const std::string& message): Exception(message) { - this->message = message; - } - - explicit DeviceNotFound(const char* message): Exception(message) { - this->message = std::string(message); - } + explicit DeviceNotFound(const std::string& message) + : Exception(message) + {} }; } diff --git a/src/TargetController/Exceptions/TargetOperationFailure.hpp b/src/TargetController/Exceptions/TargetOperationFailure.hpp index 8f3a902e..456c9557 100644 --- a/src/TargetController/Exceptions/TargetOperationFailure.hpp +++ b/src/TargetController/Exceptions/TargetOperationFailure.hpp @@ -10,9 +10,5 @@ namespace Exceptions explicit TargetOperationFailure(const std::string& message) : Exception(message) {} - - explicit TargetOperationFailure(const char* message) - : Exception(message) - {} }; } diff --git a/src/TargetController/Responses/ResponseTypes.hpp b/src/TargetController/Responses/ResponseTypes.hpp index a743cd63..bfb1cdb0 100644 --- a/src/TargetController/Responses/ResponseTypes.hpp +++ b/src/TargetController/Responses/ResponseTypes.hpp @@ -13,7 +13,7 @@ namespace TargetController::Responses TARGET_REGISTERS_READ, TARGET_MEMORY_READ, TARGET_STATE, - TARGET_PIN_STATES, + TARGET_GPIO_PIN_STATES, TARGET_STACK_POINTER, TARGET_PROGRAM_COUNTER, BREAKPOINT, diff --git a/src/TargetController/Responses/TargetGpioPinStates.hpp b/src/TargetController/Responses/TargetGpioPinStates.hpp new file mode 100644 index 00000000..e2bd851b --- /dev/null +++ b/src/TargetController/Responses/TargetGpioPinStates.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "Response.hpp" + +#include "src/Targets/TargetPinDescriptor.hpp" +#include "src/Targets/TargetGpioPinState.hpp" + +#include "src/Helpers/Pair.hpp" + +namespace TargetController::Responses +{ + class TargetGpioPinStates: public Response + { + public: + static constexpr ResponseType type = ResponseType::TARGET_GPIO_PIN_STATES; + + Targets::TargetGpioPinDescriptorAndStatePairs gpioPinStates; + + explicit TargetGpioPinStates(const Targets::TargetGpioPinDescriptorAndStatePairs& gpioPinStates) + : gpioPinStates(gpioPinStates) + {} + + [[nodiscard]] ResponseType getType() const override { + return TargetGpioPinStates::type; + } + }; +} diff --git a/src/TargetController/Responses/TargetMemoryRead.hpp b/src/TargetController/Responses/TargetMemoryRead.hpp index a0db4f1c..a2836b2d 100644 --- a/src/TargetController/Responses/TargetMemoryRead.hpp +++ b/src/TargetController/Responses/TargetMemoryRead.hpp @@ -13,8 +13,8 @@ namespace TargetController::Responses Targets::TargetMemoryBuffer data; - explicit TargetMemoryRead(const Targets::TargetMemoryBuffer& data) - : data(data) + explicit TargetMemoryRead(Targets::TargetMemoryBuffer&& data) + : data(std::move(data)) {} [[nodiscard]] ResponseType getType() const override { diff --git a/src/TargetController/Responses/TargetPinStates.hpp b/src/TargetController/Responses/TargetPinStates.hpp deleted file mode 100644 index 03533843..00000000 --- a/src/TargetController/Responses/TargetPinStates.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "Response.hpp" - -#include "src/Targets/TargetPinDescriptor.hpp" - -namespace TargetController::Responses -{ - class TargetPinStates: public Response - { - public: - static constexpr ResponseType type = ResponseType::TARGET_PIN_STATES; - - Targets::TargetPinStateMapping pinStatesByNumber; - - explicit TargetPinStates(const Targets::TargetPinStateMapping& pinStatesByNumber) - : pinStatesByNumber(pinStatesByNumber) - {} - - [[nodiscard]] ResponseType getType() const override { - return TargetPinStates::type; - } - }; -} diff --git a/src/TargetController/Responses/TargetRegistersRead.hpp b/src/TargetController/Responses/TargetRegistersRead.hpp index d2171b9b..352db64f 100644 --- a/src/TargetController/Responses/TargetRegistersRead.hpp +++ b/src/TargetController/Responses/TargetRegistersRead.hpp @@ -2,7 +2,8 @@ #include "Response.hpp" -#include "src/Targets/TargetRegister.hpp" +#include "src/Targets/TargetRegisterDescriptor.hpp" +#include "src/Targets/TargetMemory.hpp" namespace TargetController::Responses { @@ -11,10 +12,12 @@ namespace TargetController::Responses public: static constexpr ResponseType type = ResponseType::TARGET_REGISTERS_READ; - Targets::TargetRegisters registers; + Targets::TargetRegisterDescriptorAndValuePairs registers; - explicit TargetRegistersRead(const Targets::TargetRegisters& registers) - : registers(registers) + explicit TargetRegistersRead( + Targets::TargetRegisterDescriptorAndValuePairs&& registers + ) + : registers(std::move(registers)) {} [[nodiscard]] ResponseType getType() const override { diff --git a/src/TargetController/Responses/TargetState.hpp b/src/TargetController/Responses/TargetState.hpp index af281abd..a0ed962d 100644 --- a/src/TargetController/Responses/TargetState.hpp +++ b/src/TargetController/Responses/TargetState.hpp @@ -11,9 +11,9 @@ namespace TargetController::Responses public: static constexpr ResponseType type = ResponseType::TARGET_STATE; - Targets::TargetState targetState; + const Targets::TargetState& targetState; - explicit TargetState(Targets::TargetState targetState) + explicit TargetState(const Targets::TargetState& targetState) : targetState(targetState) {} diff --git a/src/TargetController/TargetControllerComponent.cpp b/src/TargetController/TargetControllerComponent.cpp index 64244fe0..42fd166c 100644 --- a/src/TargetController/TargetControllerComponent.cpp +++ b/src/TargetController/TargetControllerComponent.cpp @@ -4,8 +4,8 @@ #include #include -#include "src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.hpp" -#include "src/Targets/RiscV/TargetDescription/TargetDescriptionFile.hpp" +#include "src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp" +#include "src/Targets/RiscV/TargetDescriptionFile.hpp" #include "Responses/Error.hpp" @@ -43,8 +43,9 @@ namespace TargetController using Commands::SetBreakpoint; using Commands::RemoveBreakpoint; using Commands::SetTargetProgramCounter; - using Commands::GetTargetPinStates; - using Commands::SetTargetPinState; + using Commands::SetTargetStackPointer; + using Commands::GetTargetGpioPinStates; + using Commands::SetTargetGpioPinState; using Commands::GetTargetStackPointer; using Commands::GetTargetProgramCounter; using Commands::EnableProgrammingMode; @@ -54,7 +55,7 @@ namespace TargetController using Responses::AtomicSessionId; using Responses::TargetRegistersRead; using Responses::TargetMemoryRead; - using Responses::TargetPinStates; + using Responses::TargetGpioPinStates; using Responses::TargetStackPointer; using Responses::TargetProgramCounter; using Responses::Breakpoint; @@ -75,7 +76,7 @@ namespace TargetController Logger::debug("TargetController ready"); while (this->getThreadState() == ThreadState::READY) { - this->fireTargetEvents(); + this->refreshExecutionState(); TargetControllerComponent::notifier.waitForNotification(std::chrono::milliseconds(60)); @@ -85,7 +86,7 @@ namespace TargetController } catch (const std::exception& exception) { Logger::error("The TargetController encountered a fatal error. See below for errors:"); - Logger::error(std::string(exception.what())); + Logger::error(std::string{exception.what()}); } this->shutdown(); @@ -96,7 +97,7 @@ namespace TargetController const std::optional& atomicSessionId ) { if (TargetControllerComponent::state != TargetControllerState::ACTIVE) { - throw Exception("Command rejected - TargetController not in active state."); + throw Exception{"Command rejected - TargetController not in active state."}; } if (atomicSessionId.has_value()) { @@ -144,7 +145,7 @@ namespace TargetController TargetControllerComponent::responsesByCommandIdCv.wait(responsesByCommandIdLock, predicate); } - return (response != nullptr) ? std::optional(std::move(response)) : std::nullopt; + return (response != nullptr) ? std::optional{std::move(response)} : std::nullopt; } void TargetControllerComponent::deregisterCommandHandler(Commands::CommandType commandType) { @@ -224,16 +225,20 @@ namespace TargetController std::bind(&TargetControllerComponent::handleRemoveBreakpoint, this, std::placeholders::_1) ); + this->registerCommandHandler( + std::bind(&TargetControllerComponent::handleSetStackPointer, this, std::placeholders::_1) + ); + this->registerCommandHandler( std::bind(&TargetControllerComponent::handleSetProgramCounter, this, std::placeholders::_1) ); - this->registerCommandHandler( - std::bind(&TargetControllerComponent::handleGetTargetPinStates, this, std::placeholders::_1) + this->registerCommandHandler( + std::bind(&TargetControllerComponent::handleGetTargetGpioPinStates, this, std::placeholders::_1) ); - this->registerCommandHandler( - std::bind(&TargetControllerComponent::handleSetTargetPinState, this, std::placeholders::_1) + this->registerCommandHandler( + std::bind(&TargetControllerComponent::handleSetTargetGpioPinState, this, std::placeholders::_1) ); this->registerCommandHandler( @@ -262,11 +267,10 @@ namespace TargetController ); this->acquireHardware(); - this->loadRegisterDescriptors(); - if (this->target->getState() != TargetState::RUNNING) { - this->target->run(); - this->lastTargetState = TargetState::RUNNING; + if (this->targetState->executionState != TargetExecutionState::RUNNING) { +// this->target->run(); +// this->targetState->executionState = TargetExecutionState::RUNNING; } this->state = TargetControllerState::ACTIVE; @@ -300,7 +304,7 @@ namespace TargetController this->target.reset(); this->debugTool.reset(); Logger::error( - "Failed to properly shut down TargetController. Error: " + std::string(exception.what()) + "Failed to properly shut down TargetController. Error: " + std::string{exception.what()} ); } @@ -380,28 +384,28 @@ namespace TargetController using Services::PathService; if (briefDescriptor.family == TargetFamily::AVR_8) { - return std::make_unique( + return std::make_unique( this->environmentConfig.targetConfig, - Microchip::Avr::Avr8Bit::TargetDescription::TargetDescriptionFile( - PathService::targetDescriptionDirPath() + "/" + briefDescriptor.relativeTdfPath - ) + Microchip::Avr8::TargetDescriptionFile{ + PathService::targetDescriptionFilesDirPath() + "/" + briefDescriptor.relativeTdfPath + } ); } if (briefDescriptor.family == TargetFamily::RISC_V) { return std::make_unique( this->environmentConfig.targetConfig, - RiscV::TargetDescription::TargetDescriptionFile( - PathService::targetDescriptionDirPath() + "/" + briefDescriptor.relativeTdfPath - ) + RiscV::TargetDescriptionFile{ + PathService::targetDescriptionFilesDirPath() + "/" + briefDescriptor.relativeTdfPath + } ); } - throw Exception("Cannot construct target instance - invalid target family in BriefTargetDescriptor"); + throw Exception{"Cannot construct target instance - invalid target family in BriefTargetDescriptor"}; } void TargetControllerComponent::processQueuedCommands() { - auto commands = std::queue>(); + auto commands = std::queue>{}; commands.swap( this->activeAtomicSession.has_value() @@ -420,21 +424,24 @@ namespace TargetController const auto commandHandlerIt = this->commandHandlersByCommandType.find(commandType); if (commandHandlerIt == this->commandHandlersByCommandType.end()) { - throw Exception("No handler registered for this command."); + throw Exception{"No handler registered for this command."}; } if (this->state != TargetControllerState::ACTIVE) { - throw Exception("Command rejected - TargetController not in active state."); + throw Exception{"Command rejected - TargetController not in active state."}; } - if (command->requiresStoppedTargetState() && this->lastTargetState != TargetState::STOPPED) { - throw Exception("Command rejected - command requires target execution to be stopped."); + if ( + command->requiresStoppedTargetState() + && this->targetState->executionState != TargetExecutionState::STOPPED + ) { + throw Exception{"Command rejected - command requires target execution to be stopped."}; } if (this->target->programmingModeEnabled() && command->requiresDebugMode()) { - throw Exception( + throw Exception{ "Command rejected - command cannot be serviced whilst the target is in programming mode." - ); + }; } this->registerCommandResponse(commandId, commandHandlerIt->second(*(command.get()))); @@ -474,15 +481,15 @@ namespace TargetController const auto briefTargetDescriptor = Services::TargetService::briefDescriptor(targetName); if (debugToolIt == supportedDebugTools.end()) { - throw Exceptions::InvalidConfig( + throw Exceptions::InvalidConfig{ "Debug tool name (\"" + debugToolName + "\") not recognised. Please check your configuration!" - ); + }; } if (!briefTargetDescriptor.has_value()) { - throw Exceptions::InvalidConfig( + throw Exceptions::InvalidConfig{ "Target name (\"" + targetName + "\") not recognised. Please check your configuration!" - ); + }; } this->debugTool = debugToolIt->second(); @@ -496,29 +503,36 @@ namespace TargetController Logger::info("Debug tool firmware version: " + this->debugTool->getFirmwareVersionString()); this->target = this->constructTarget(*briefTargetDescriptor); - const auto& targetDescriptor = this->getTargetDescriptor(); if (!this->target->supportsDebugTool(this->debugTool.get())) { - throw Exceptions::InvalidConfig( + throw Exceptions::InvalidConfig{ "Debug tool \"" + this->debugTool->getName() + "\" is not compatible with target \"" - + targetDescriptor.name + "\"." - ); + + targetName + "\"." + }; } this->target->setDebugTool(this->debugTool.get()); + this->targetDescriptor = std::make_unique(this->target->targetDescriptor()); Logger::info("Activating target"); this->target->activate(); Logger::info("Target activated"); - Logger::info("Target ID: " + targetDescriptor.id); - Logger::info("Target name: " + targetDescriptor.name); + Logger::info("Target name: " + this->targetDescriptor->name); + Logger::info("Target ID: " + this->targetDescriptor->marketId); + + this->targetState = std::make_unique( + TargetExecutionState::UNKNOWN, + TargetMode::DEBUGGING, + std::nullopt + ); + this->refreshExecutionState(); if (!this->environmentConfig.targetConfig.hardwareBreakpoints) { Logger::warning("Hardware breakpoints have been disabled"); } else { - const auto& breakpointResources = targetDescriptor.breakpointResources; + const auto& breakpointResources = this->targetDescriptor->breakpointResources; if (breakpointResources.maximumHardwareBreakpoints.has_value()) { Logger::info( "Available hardware breakpoints: " + std::to_string( @@ -529,16 +543,10 @@ namespace TargetController if (breakpointResources.reservedHardwareBreakpoints > 0) { Logger::info( - "Reserved hardware breakpoints: " + std::to_string( - breakpointResources.reservedHardwareBreakpoints - ) + "Reserved hardware breakpoints: " + std::to_string(breakpointResources.reservedHardwareBreakpoints) ); } } - - this->programMemoryCache = std::make_unique( - targetDescriptor.memoryDescriptorsByType.at(targetDescriptor.programMemoryType) - ); } void TargetControllerComponent::releaseHardware() { @@ -566,7 +574,7 @@ namespace TargetController void TargetControllerComponent::startAtomicSession() { if (this->activeAtomicSession.has_value()) { - throw Exception("Atomic session already active - nested sessions are not supported"); + throw Exception{"Atomic session already active - nested sessions are not supported"}; } this->activeAtomicSession.emplace(); @@ -579,7 +587,7 @@ namespace TargetController { auto commandQueue = TargetControllerComponent::atomicSessionCommandQueue.accessor(); - auto empty = std::queue>(); + auto empty = std::queue>{}; commandQueue->swap(empty); } @@ -587,114 +595,239 @@ namespace TargetController TargetControllerComponent::notifier.notify(); } - void TargetControllerComponent::loadRegisterDescriptors() { - const auto& targetDescriptor = this->getTargetDescriptor(); + void TargetControllerComponent::refreshExecutionState() { + auto newExecutionState = this->target->getExecutionState(); - for (const auto& [registerDescriptorId, registerDescriptor] : targetDescriptor.registerDescriptorsById) { - auto startAddress = registerDescriptor.startAddress.value_or(0); - auto endAddress = startAddress + (registerDescriptor.size - 1); + if (newExecutionState != this->targetState->executionState) { + Logger::debug("Target execution state changed"); - const auto registerAddressRangeIt = this->registerAddressRangeByMemoryType.find( - registerDescriptor.memoryType - ); + auto newState = *(this->targetState); + newState.executionState = newExecutionState; - if (registerAddressRangeIt == this->registerAddressRangeByMemoryType.end()) { - this->registerAddressRangeByMemoryType.insert( - std::pair(registerDescriptor.memoryType, TargetMemoryAddressRange(startAddress, endAddress)) - ); + if (newExecutionState == TargetExecutionState::STOPPED) { + Logger::debug("Target stopped"); + newState.programCounter = this->target->getProgramCounter(); } else { - auto& addressRange = registerAddressRangeIt->second; - - if (startAddress < addressRange.startAddress) { - addressRange.startAddress = startAddress; - } - - if (endAddress > addressRange.endAddress) { - addressRange.endAddress = endAddress; - } + Logger::debug("Target resumed"); + newState.programCounter = std::nullopt; } - this->registerDescriptorsByMemoryType[registerDescriptor.memoryType].insert(registerDescriptor); + this->updateTargetState(newState); } } - TargetRegisterDescriptors TargetControllerComponent::getRegisterDescriptorsWithinAddressRange( - Targets::TargetMemoryAddress startAddress, - Targets::TargetMemoryAddress endAddress, - Targets::TargetMemoryType memoryType - ) { - auto output = TargetRegisterDescriptors(); - - const auto registerAddressRangeIt = this->registerAddressRangeByMemoryType.find(memoryType); - const auto registerDescriptorsIt = this->registerDescriptorsByMemoryType.find(memoryType); - - if ( - registerAddressRangeIt != this->registerAddressRangeByMemoryType.end() - && registerDescriptorsIt != this->registerDescriptorsByMemoryType.end() - ) { - const auto& registersAddressRange = registerAddressRangeIt->second; - - if ( - (startAddress <= registersAddressRange.startAddress && endAddress >= registersAddressRange.startAddress) - || (startAddress <= registersAddressRange.endAddress && endAddress >= registersAddressRange.startAddress) - ) { - const auto& registerDescriptors = registerDescriptorsIt->second; - - for (const auto& registerDescriptor : registerDescriptors) { - if (!registerDescriptor.startAddress.has_value() || registerDescriptor.size < 1) { - continue; - } - - const auto registerStartAddress = registerDescriptor.startAddress.value(); - const auto registerEndAddress = registerStartAddress + registerDescriptor.size; - - if ( - (startAddress <= registerStartAddress && endAddress >= registerStartAddress) - || (startAddress <= registerEndAddress && endAddress >= registerStartAddress) - ) { - output.insert(registerDescriptor); - } - } - } + void TargetControllerComponent::updateTargetState(const TargetState& newState) { + if (newState == *(this->targetState)) { + // Nothing has changed, nothing to do + return; } - return output; + const auto previousState = *(this->targetState); + *(this->targetState) = newState; + EventManager::triggerEvent(std::make_shared(*(this->targetState), previousState)); } - void TargetControllerComponent::fireTargetEvents() { - auto newTargetState = this->target->getState(); - - if (newTargetState != this->lastTargetState) { - this->lastTargetState = newTargetState; - - if (newTargetState == TargetState::STOPPED) { - Logger::debug("Target state changed - STOPPED"); - EventManager::triggerEvent(std::make_shared( - this->target->getProgramCounter(), - TargetBreakCause::UNKNOWN - )); - } - - if (newTargetState == TargetState::RUNNING) { - Logger::debug("Target state changed - RUNNING"); - EventManager::triggerEvent(std::make_shared(false)); - } + void TargetControllerComponent::stopTarget() { + if (this->target->getExecutionState() != TargetExecutionState::STOPPED) { + this->target->stop(); + this->targetState->executionState = TargetExecutionState::STOPPED; } + + auto newState = *(this->targetState); + newState.executionState = TargetExecutionState::STOPPED; + newState.programCounter = this->target->getProgramCounter(); + this->updateTargetState(newState); + } + + void TargetControllerComponent::resumeTarget() { + if (this->target->getExecutionState() != TargetExecutionState::RUNNING) { + this->target->run(std::nullopt); + } + + auto newState = *(this->targetState); + newState.executionState = TargetExecutionState::RUNNING; + newState.programCounter = std::nullopt; + this->updateTargetState(newState); + } + + void TargetControllerComponent::stepTarget() { + this->target->step(); + + auto newState = *(this->targetState); + newState.executionState = TargetExecutionState::STEPPING; + newState.programCounter = std::nullopt; + this->updateTargetState(newState); } void TargetControllerComponent::resetTarget() { this->target->reset(); - EventManager::triggerEvent(std::make_shared()); } + TargetRegisterDescriptorAndValuePairs TargetControllerComponent::readTargetRegisters( + const TargetRegisterDescriptors& descriptors + ) { + return this->target->readRegisters(descriptors); + } + + void TargetControllerComponent::writeTargetRegisters(const TargetRegisterDescriptorAndValuePairs& registers) { + this->target->writeRegisters(registers); + EventManager::triggerEvent(std::make_shared(registers)); + } + + Targets::TargetMemoryBuffer TargetControllerComponent::readTargetMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, + TargetMemoryAddress startAddress, + TargetMemorySize bytes, + const std::set& excludedAddressRanges, + bool bypassCache + ) { + if ( + !bypassCache + && this->environmentConfig.targetConfig.programMemoryCache + && this->target->isProgramMemory(addressSpaceDescriptor, memorySegmentDescriptor, startAddress, bytes) + ) { + auto& cache = this->getProgramMemoryCache(addressSpaceDescriptor); + + if (!cache.contains(startAddress, bytes)) { + Logger::debug( + "Program memory cache miss at 0x" + Services::StringService::toHex(startAddress) + ", " + + std::to_string(bytes) + " bytes" + ); + + /* + * TODO: We're currently ignoring excludedAddressRanges when populating the program + * memory cache. This isn't a big deal, so I'll sort it later. + */ + cache.insert( + startAddress, + this->target->readMemory( + addressSpaceDescriptor, + memorySegmentDescriptor, + startAddress, + std::max( + bytes, + memorySegmentDescriptor.pageSize.value_or(0) + ), + {} + ) + ); + } + + return cache.fetch(startAddress, bytes); + } + + return this->target->readMemory( + addressSpaceDescriptor, + memorySegmentDescriptor, + startAddress, + bytes, + excludedAddressRanges + ); + } + + void TargetControllerComponent::writeTargetMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, + Targets::TargetMemoryAddress startAddress, + const TargetMemoryBuffer& buffer + ) { + const auto isProgramMemory = this->target->isProgramMemory( + addressSpaceDescriptor, + memorySegmentDescriptor, + startAddress, + static_cast(buffer.size()) + ); + + if (isProgramMemory && !this->target->programmingModeEnabled()) { + throw Exception{"Cannot write to program memory - programming mode not enabled."}; + } + + this->target->writeMemory(addressSpaceDescriptor, memorySegmentDescriptor, startAddress, buffer); + + if (isProgramMemory && this->environmentConfig.targetConfig.programMemoryCache) { + this->getProgramMemoryCache(addressSpaceDescriptor).insert(startAddress, buffer); + } + + EventManager::triggerEvent( + std::make_shared( + addressSpaceDescriptor, + memorySegmentDescriptor, + startAddress, + static_cast(buffer.size()) + ) + ); + } + + void TargetControllerComponent::eraseTargetMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor + ) { + if (this->target->isProgramMemory( + addressSpaceDescriptor, + memorySegmentDescriptor, + memorySegmentDescriptor.addressRange.startAddress, + memorySegmentDescriptor.addressRange.size() + )) { + if (!this->target->programmingModeEnabled()) { + throw Exception{"Cannot erase program memory - programming mode not enabled."}; + } + + if (this->environmentConfig.targetConfig.programMemoryCache) { + Logger::debug("Clearing program memory cache"); + this->getProgramMemoryCache(addressSpaceDescriptor).clear(); + } + } + + this->target->eraseMemory(addressSpaceDescriptor, memorySegmentDescriptor); + } + + void TargetControllerComponent::setBreakpoint(const TargetBreakpoint& breakpoint) { + using Services::StringService; + + if (breakpoint.type == TargetBreakpoint::Type::HARDWARE) { + Logger::debug( + "Installing hardware breakpoint at byte address 0x" + StringService::toHex(breakpoint.address) + ); + + this->target->setHardwareBreakpoint(breakpoint.address); + this->hardwareBreakpointsByAddress.emplace(breakpoint.address, breakpoint); + return; + } + + Logger::debug("Installing software breakpoint at byte address 0x" + StringService::toHex(breakpoint.address)); + + this->target->setSoftwareBreakpoint(breakpoint.address); + this->softwareBreakpointsByAddress.emplace(breakpoint.address, breakpoint); + } + + void TargetControllerComponent::removeBreakpoint(const TargetBreakpoint& breakpoint) { + using Services::StringService; + + if (breakpoint.type == Targets::TargetBreakpoint::Type::HARDWARE) { + Logger::debug("Removing hardware breakpoint at byte address 0x" + StringService::toHex(breakpoint.address)); + + this->target->removeHardwareBreakpoint(breakpoint.address); + this->hardwareBreakpointsByAddress.erase(breakpoint.address); + return; + } + + Logger::debug("Removing software breakpoint at byte address 0x" + StringService::toHex(breakpoint.address)); + + this->target->removeSoftwareBreakpoint(breakpoint.address); + this->softwareBreakpointsByAddress.erase(breakpoint.address); + } + void TargetControllerComponent::enableProgrammingMode() { Logger::debug("Enabling programming mode"); this->target->enableProgrammingMode(); Logger::warning("Programming mode enabled"); - EventManager::triggerEvent(std::make_shared()); + auto newState = *(this->targetState); + newState.mode = TargetMode::PROGRAMMING; + this->updateTargetState(newState); } void TargetControllerComponent::disableProgrammingMode() { @@ -702,8 +835,6 @@ namespace TargetController this->target->disableProgrammingMode(); Logger::info("Programming mode disabled"); - EventManager::triggerEvent(std::make_shared()); - Logger::info("Restoring breakpoints"); this->target->stop(); @@ -714,14 +845,26 @@ namespace TargetController for (const auto& [address, breakpoint] : this->hardwareBreakpointsByAddress) { this->target->setHardwareBreakpoint(address); } + + auto newState = *(this->targetState); + newState.mode = TargetMode::DEBUGGING; + newState.executionState = TargetExecutionState::STOPPED; + this->updateTargetState(newState); } - const Targets::TargetDescriptor& TargetControllerComponent::getTargetDescriptor() { - if (!this->targetDescriptor.has_value()) { - this->targetDescriptor.emplace(this->target->getDescriptor()); + TargetMemoryCache& TargetControllerComponent::getProgramMemoryCache( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor + ) { + auto cacheIt = this->programMemoryCachesByAddressSpaceKey.find(addressSpaceDescriptor.key); + + if (cacheIt == this->programMemoryCachesByAddressSpaceKey.end()) { + cacheIt = this->programMemoryCachesByAddressSpaceKey.emplace( + addressSpaceDescriptor.key, + TargetMemoryCache(addressSpaceDescriptor) + ).first; } - return *this->targetDescriptor; + return cacheIt->second; } void TargetControllerComponent::onShutdownTargetControllerEvent(const Events::ShutdownTargetController&) { @@ -733,9 +876,9 @@ namespace TargetController return; } - if (this->target->getState() != TargetState::RUNNING) { - this->target->run(); - this->fireTargetEvents(); + if (this->target->getExecutionState() != TargetExecutionState::RUNNING) { + this->target->run(std::nullopt); + this->refreshExecutionState(); } } @@ -746,7 +889,7 @@ namespace TargetController std::unique_ptr TargetControllerComponent::handleEndAtomicSession(EndAtomicSession& command) { if (!this->activeAtomicSession.has_value() || this->activeAtomicSession->id != command.sessionId) { - throw Exception("Atomic session is not active"); + throw Exception{"Atomic session is not active"}; } this->endActiveAtomicSession(); @@ -761,41 +904,20 @@ namespace TargetController std::unique_ptr TargetControllerComponent::handleGetTargetDescriptor( GetTargetDescriptor& command ) { - return std::make_unique(this->getTargetDescriptor()); + return std::make_unique(*(this->targetDescriptor)); } std::unique_ptr TargetControllerComponent::handleGetTargetState(GetTargetState& command) { - return std::make_unique(this->target->getState()); + return std::make_unique(*(this->targetState)); } std::unique_ptr TargetControllerComponent::handleStopTargetExecution(StopTargetExecution& command) { - if (this->target->getState() != TargetState::STOPPED) { - this->target->stop(); - this->lastTargetState = TargetState::STOPPED; - } - - EventManager::triggerEvent(std::make_shared( - this->target->getProgramCounter(), - TargetBreakCause::UNKNOWN - )); - + this->stopTarget(); return std::make_unique(); } - std::unique_ptr TargetControllerComponent::handleResumeTargetExecution( - ResumeTargetExecution& command - ) { - if (this->target->getState() != TargetState::RUNNING) { - if (command.fromAddress.has_value()) { - this->target->setProgramCounter(*command.fromAddress); - } - - this->target->run(command.toAddress); - this->lastTargetState = TargetState::RUNNING; - } - - EventManager::triggerEvent(std::make_shared(false)); - + std::unique_ptr TargetControllerComponent::handleResumeTargetExecution(ResumeTargetExecution& command) { + this->resumeTarget(); return std::make_unique(); } @@ -807,170 +929,85 @@ namespace TargetController std::unique_ptr TargetControllerComponent::handleReadTargetRegisters( ReadTargetRegisters& command ) { - return std::make_unique( - !command.descriptorIds.empty() - ? this->target->readRegisters(command.descriptorIds) - : Targets::TargetRegisters() - ); + if (command.descriptors.empty()) { + throw Exception{"No register descriptors provided"}; + } + + return std::make_unique(this->readTargetRegisters(command.descriptors)); } std::unique_ptr TargetControllerComponent::handleWriteTargetRegisters(WriteTargetRegisters& command) { - if (!command.registers.empty()) { - this->target->writeRegisters(command.registers); + if (command.registers.empty()) { + throw Exception{"No register values provided"}; } - auto registersWrittenEvent = std::make_shared(); - registersWrittenEvent->registers = std::move(command.registers); - - EventManager::triggerEvent(registersWrittenEvent); - + this->writeTargetRegisters(std::move(command.registers)); return std::make_unique(); } std::unique_ptr TargetControllerComponent::handleReadTargetMemory(ReadTargetMemory& command) { - const auto& targetDescriptor = this->getTargetDescriptor(); + if (command.bytes == 0) { + throw Exception{"Zero bytes requested"}; + } + + const auto addressRange = TargetMemoryAddressRange( + command.startAddress, + command.startAddress + command.bytes - 1 + ); + if ( - command.memoryType == targetDescriptor.programMemoryType - && !command.bypassCache - && this->environmentConfig.targetConfig.programMemoryCache + !command.addressSpaceDescriptor.addressRange.contains(addressRange) + || !command.memorySegmentDescriptor.addressRange.contains(addressRange) ) { - assert(this->programMemoryCache); - - - if (!this->programMemoryCache->contains(command.startAddress, command.bytes)) { - Logger::debug( - "Program memory cache miss at 0x" + Services::StringService::toHex(command.startAddress) + ", " - + std::to_string(command.bytes) + " bytes" - ); - - /* - * TODO: We're currently ignoring command.excludedAddressRanges when populating the program - * memory cache. This isn't a big deal, so I'll sort it later. - */ - this->programMemoryCache->insert( - command.startAddress, - this->target->readMemory( - command.memoryType, - command.startAddress, - std::max( - command.bytes, - targetDescriptor.memoryDescriptorsByType.at(command.memoryType).pageSize.value_or(0) - ) - ) - ); - } - - return std::make_unique( - this->programMemoryCache->fetch(command.startAddress, command.bytes) - ); + throw Exception{"Invalid address range"}; } return std::make_unique( - command.bytes > 0 - ? this->target->readMemory( - command.memoryType, - command.startAddress, - command.bytes, - command.excludedAddressRanges - ) - : Targets::TargetMemoryBuffer() + this->readTargetMemory( + command.addressSpaceDescriptor, + command.memorySegmentDescriptor, + command.startAddress, + command.bytes, + command.excludedAddressRanges, + command.bypassCache + ) ); } std::unique_ptr TargetControllerComponent::handleWriteTargetMemory(WriteTargetMemory& command) { - const auto& buffer = command.buffer; - const auto bufferSize = command.buffer.size(); - const auto bufferStartAddress = command.startAddress; - - const auto& targetDescriptor = this->getTargetDescriptor(); - - if (command.memoryType == targetDescriptor.programMemoryType && !this->target->programmingModeEnabled()) { - throw Exception("Cannot write to program memory - programming mode not enabled."); + if (command.buffer.empty()) { + throw Exception{"Empty buffer"}; } - this->target->writeMemory(command.memoryType, bufferStartAddress, buffer); + const auto addressRange = TargetMemoryAddressRange{ + command.startAddress, + static_cast(command.startAddress + command.buffer.size() - 1) + }; if ( - command.memoryType == targetDescriptor.programMemoryType - && this->environmentConfig.targetConfig.programMemoryCache + !command.addressSpaceDescriptor.addressRange.contains(addressRange) + || !command.memorySegmentDescriptor.addressRange.contains(addressRange) ) { - this->programMemoryCache->insert(bufferStartAddress, buffer); + throw Exception{"Invalid address range"}; } - EventManager::triggerEvent( - std::make_shared(command.memoryType, bufferStartAddress, bufferSize) + this->writeTargetMemory( + command.addressSpaceDescriptor, + command.memorySegmentDescriptor, + command.startAddress, + command.buffer ); - if ( - EventManager::isEventTypeListenedFor(Events::RegistersWrittenToTarget::type) - && command.memoryType == targetDescriptor.programMemoryType - && this->registerDescriptorsByMemoryType.contains(command.memoryType) - ) { - /* - * The memory type we just wrote to contains some number of registers - if we've written to any address - * that is known to store the value of a register, trigger a RegistersWrittenToTarget event - */ - const auto bufferEndAddress = static_cast(bufferStartAddress + (bufferSize - 1)); - auto registerDescriptors = this->getRegisterDescriptorsWithinAddressRange( - bufferStartAddress, - bufferEndAddress, - command.memoryType - ); - - if (!registerDescriptors.empty()) { - auto registersWrittenEvent = std::make_shared(); - - for (const auto& registerDescriptor : registerDescriptors) { - const auto registerSize = registerDescriptor.size; - const auto registerStartAddress = registerDescriptor.startAddress.value(); - const auto registerEndAddress = registerStartAddress + (registerSize - 1); - - if (registerStartAddress < bufferStartAddress || registerEndAddress > bufferEndAddress) { - continue; - } - - const auto bufferBeginIt = buffer.begin() + (registerStartAddress - bufferStartAddress); - registersWrittenEvent->registers.emplace_back(TargetRegister( - registerDescriptor.id, - TargetMemoryBuffer(bufferBeginIt, bufferBeginIt + registerSize) - )); - } - - EventManager::triggerEvent(registersWrittenEvent); - } - } - return std::make_unique(); } std::unique_ptr TargetControllerComponent::handleEraseTargetMemory(EraseTargetMemory& command) { - if (command.memoryType == this->getTargetDescriptor().programMemoryType) { - if (!this->target->programmingModeEnabled()) { - throw Exception("Cannot erase program memory - programming mode not enabled."); - } - - if (this->environmentConfig.targetConfig.programMemoryCache) { - assert(this->programMemoryCache); - - Logger::debug("Clearing program memory cache"); - this->programMemoryCache->clear(); - } - } - - this->target->eraseMemory(command.memoryType); - + this->eraseTargetMemory(command.addressSpaceDescriptor, command.memorySegmentDescriptor); return std::make_unique(); } std::unique_ptr TargetControllerComponent::handleStepTargetExecution(StepTargetExecution& command) { - if (command.fromProgramCounter.has_value()) { - this->target->setProgramCounter(command.fromProgramCounter.value()); - } - - this->target->step(); - this->lastTargetState = TargetState::RUNNING; - EventManager::triggerEvent(std::make_shared(true)); - + this->stepTarget(); return std::make_unique(); } @@ -978,73 +1015,30 @@ namespace TargetController using Targets::TargetBreakpoint; using Services::StringService; - auto breakpoint = TargetBreakpoint(command.address, TargetBreakpoint::Type::SOFTWARE); - - const auto& targetBreakpointResources = this->getTargetDescriptor().breakpointResources; + const auto& targetBreakpointResources = this->targetDescriptor->breakpointResources; if ( command.preferredType == Targets::TargetBreakpoint::Type::HARDWARE && this->environmentConfig.targetConfig.hardwareBreakpoints - ) { - static auto exhaustedResourcesWarning = false; - - if ( + && ( !targetBreakpointResources.maximumHardwareBreakpoints.has_value() || this->hardwareBreakpointsByAddress.size() < (*(targetBreakpointResources.maximumHardwareBreakpoints) - targetBreakpointResources.reservedHardwareBreakpoints) - ) { - exhaustedResourcesWarning = true; + ) + ) { + const auto hwBreakpoint = TargetBreakpoint{command.address, TargetBreakpoint::Type::HARDWARE}; + this->setBreakpoint(hwBreakpoint); - Logger::debug( - "Installing hardware breakpoint at byte address 0x" + StringService::toHex(command.address) - ); - - this->target->setHardwareBreakpoint(command.address); - this->hardwareBreakpointsByAddress.insert(std::pair(command.address, breakpoint)); - - breakpoint.type = TargetBreakpoint::Type::HARDWARE; - return std::make_unique(breakpoint); - } - - if (exhaustedResourcesWarning) { - exhaustedResourcesWarning = false; - Logger::warning( - "Hardware breakpoint resources have been exhausted. Falling back to software breakpoints" - ); - } + return std::make_unique(hwBreakpoint); } - Logger::debug( - "Installing software breakpoint at byte address 0x" + StringService::toHex(command.address) - ); + const auto swBreakpoint = TargetBreakpoint(command.address, TargetBreakpoint::Type::SOFTWARE); + this->setBreakpoint(swBreakpoint); - this->target->setSoftwareBreakpoint(command.address); - this->softwareBreakpointsByAddress.insert(std::pair(command.address, breakpoint)); - - return std::make_unique(breakpoint); + return std::make_unique(swBreakpoint); } std::unique_ptr TargetControllerComponent::handleRemoveBreakpoint(RemoveBreakpoint& command) { - using Services::StringService; - - if (command.breakpoint.type == Targets::TargetBreakpoint::Type::HARDWARE) { - assert(this->environmentConfig.targetConfig.hardwareBreakpoints); - - Logger::debug( - "Removing hardware breakpoint at byte address 0x" + StringService::toHex(command.breakpoint.address) - ); - - this->target->removeHardwareBreakpoint(command.breakpoint.address); - this->hardwareBreakpointsByAddress.erase(command.breakpoint.address); - - } else { - Logger::debug( - "Removing software breakpoint at byte address 0x" + StringService::toHex(command.breakpoint.address) - ); - - this->target->removeSoftwareBreakpoint(command.breakpoint.address); - this->softwareBreakpointsByAddress.erase(command.breakpoint.address); - } - + this->removeBreakpoint(command.breakpoint); return std::make_unique(); } @@ -1053,12 +1047,19 @@ namespace TargetController return std::make_unique(); } - std::unique_ptr TargetControllerComponent::handleGetTargetPinStates(GetTargetPinStates& command) { - return std::make_unique(this->target->getPinStates(command.variantId)); + std::unique_ptr TargetControllerComponent::handleSetStackPointer(SetTargetStackPointer& command) { + this->target->setStackPointer(command.stackPointer); + return std::make_unique(); } - std::unique_ptr TargetControllerComponent::handleSetTargetPinState(SetTargetPinState& command) { - this->target->setPinState(command.pinDescriptor, command.pinState); + std::unique_ptr TargetControllerComponent::handleGetTargetGpioPinStates( + GetTargetGpioPinStates& command + ) { + return std::make_unique(this->target->getGpioPinStates(command.pinoutDescriptor)); + } + + std::unique_ptr TargetControllerComponent::handleSetTargetGpioPinState(SetTargetGpioPinState& command) { + this->target->setGpioPinState(command.pinDescriptor, command.state); return std::make_unique(); } diff --git a/src/TargetController/TargetControllerComponent.hpp b/src/TargetController/TargetControllerComponent.hpp index a5286923..168b2c0b 100644 --- a/src/TargetController/TargetControllerComponent.hpp +++ b/src/TargetController/TargetControllerComponent.hpp @@ -38,8 +38,9 @@ #include "Commands/SetBreakpoint.hpp" #include "Commands/RemoveBreakpoint.hpp" #include "Commands/SetTargetProgramCounter.hpp" -#include "Commands/GetTargetPinStates.hpp" -#include "Commands/SetTargetPinState.hpp" +#include "Commands/SetTargetStackPointer.hpp" +#include "Commands/GetTargetGpioPinStates.hpp" +#include "Commands/SetTargetGpioPinState.hpp" #include "Commands/GetTargetStackPointer.hpp" #include "Commands/GetTargetProgramCounter.hpp" #include "Commands/EnableProgrammingMode.hpp" @@ -52,7 +53,7 @@ #include "Responses/TargetState.hpp" #include "Responses/TargetRegistersRead.hpp" #include "Responses/TargetMemoryRead.hpp" -#include "Responses/TargetPinStates.hpp" +#include "Responses/TargetGpioPinStates.hpp" #include "Responses/TargetStackPointer.hpp" #include "Responses/TargetProgramCounter.hpp" #include "Responses/Breakpoint.hpp" @@ -120,8 +121,8 @@ namespace TargetController std::map> > responsesByCommandId; - static inline ConditionVariableNotifier notifier = ConditionVariableNotifier(); - static inline std::condition_variable responsesByCommandIdCv = std::condition_variable(); + static inline ConditionVariableNotifier notifier = {}; + static inline std::condition_variable responsesByCommandIdCv = {}; static inline std::atomic state = TargetControllerState::INACTIVE; @@ -145,27 +146,13 @@ namespace TargetController EventListenerPointer eventListener = std::make_shared("TargetControllerEventListener"); - /** - * We keep record of the last known execution state of the target. When the connected target reports a - * different state to what's stored in lastTargetState, a state change (TargetExecutionStopped/TargetExecutionResumed) - * event is emitted. - */ - Targets::TargetState lastTargetState = Targets::TargetState::UNKNOWN; + std::unique_ptr targetDescriptor = nullptr; + std::unique_ptr targetState = nullptr; /** - * Target descriptor cache. + * Target register descriptors mapped by the address space key */ - std::optional targetDescriptor; - - /** - * Target register descriptors mapped by the memory type on which the register is stored. - */ - std::map registerDescriptorsByMemoryType; - - /** - * Memory address ranges for target registers, mapped by the register memory type. - */ - std::map registerAddressRangeByMemoryType; + std::map registerDescriptorsByAddressSpaceKey; /** * The TargetController keeps track of all installed breakpoints. @@ -174,14 +161,15 @@ namespace TargetController std::map hardwareBreakpointsByAddress; /** - * The target's program memory cache. + * The target's program memory cache * * If program caching is enabled, all program memory reads will be serviced by the cache, if we have the data. * - * We use std::unique_ptr here due to delayed construction (we construct this after activating the target - * and obtaining the target descriptor). + * Most targets only have a single program memory, which resides on a single address space. But some may have + * multiple program memories, residing on multiple address spaces. We have a single cache (TargetMemoryCache + * object) for each address space. */ - std::unique_ptr programMemoryCache = nullptr; + std::map programMemoryCachesByAddressSpaceKey; /** * Registers a handler function for a particular command type. @@ -192,14 +180,12 @@ namespace TargetController */ template void registerCommandHandler(std::function(CommandType&)> callback) { - this->commandHandlersByCommandType.insert( - std::pair( - CommandType::type, - [callback] (Commands::Command& command) { - // Downcast the command to the expected type - return callback(dynamic_cast(command)); - } - ) + this->commandHandlersByCommandType.emplace( + CommandType::type, + [callback] (Commands::Command& command) { + // Downcast the command to the expected type + return callback(dynamic_cast(command)); + } ); } @@ -283,34 +269,48 @@ namespace TargetController void endActiveAtomicSession(); - /** - * Extracts address ranges and groups target register descriptors. - */ - void loadRegisterDescriptors(); + void refreshExecutionState(); - /** - * Resolves the descriptors of all target registers found within the given address range and memory type. - * - * @param startAddress - * @param endAddress - * @param memoryType - * @return - */ - Targets::TargetRegisterDescriptors getRegisterDescriptorsWithinAddressRange( - Targets::TargetMemoryAddress startAddress, - Targets::TargetMemoryAddress endAddress, - Targets::TargetMemoryType memoryType + void updateTargetState(const Targets::TargetState& newState); + + void stopTarget(); + + void resumeTarget(); + + void stepTarget(); + + void resetTarget(); + + Targets::TargetRegisterDescriptorAndValuePairs readTargetRegisters( + const Targets::TargetRegisterDescriptors& descriptors ); - /** - * Should fire any events queued on the target. - */ - void fireTargetEvents(); + void writeTargetRegisters(const Targets::TargetRegisterDescriptorAndValuePairs& registers); - /** - * Triggers a target reset and emits a TargetReset event. - */ - void resetTarget(); + Targets::TargetMemoryBuffer readTargetMemory( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, + Targets::TargetMemoryAddress startAddress, + Targets::TargetMemorySize bytes, + const std::set& excludedAddressRanges, + bool bypassCache + ); + + void writeTargetMemory( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor, + Targets::TargetMemoryAddress startAddress, + const Targets::TargetMemoryBuffer& buffer + ); + + void eraseTargetMemory( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const Targets::TargetMemorySegmentDescriptor& memorySegmentDescriptor + ); + + void setBreakpoint(const Targets::TargetBreakpoint& breakpoint); + + void removeBreakpoint(const Targets::TargetBreakpoint& breakpoint); /** * Puts the target into programming mode and disables command handlers for debug commands (commands that serve @@ -324,11 +324,15 @@ namespace TargetController void disableProgrammingMode(); /** - * Returns a cached instance of the target's TargetDescriptor. + * Fetches the program memory cache object for the given address space. If the address space has no associated + * cache object, one will be created. * + * @param addressSpaceDescriptor * @return */ - const Targets::TargetDescriptor& getTargetDescriptor(); + Targets::TargetMemoryCache& getProgramMemoryCache( + const Targets::TargetAddressSpaceDescriptor& addressSpaceDescriptor + ); /** * Invokes a shutdown. @@ -364,8 +368,11 @@ namespace TargetController std::unique_ptr handleSetBreakpoint(Commands::SetBreakpoint& command); std::unique_ptr handleRemoveBreakpoint(Commands::RemoveBreakpoint& command); std::unique_ptr handleSetProgramCounter(Commands::SetTargetProgramCounter& command); - std::unique_ptr handleGetTargetPinStates(Commands::GetTargetPinStates& command); - std::unique_ptr handleSetTargetPinState(Commands::SetTargetPinState& command); + std::unique_ptr handleSetStackPointer(Commands::SetTargetStackPointer& command); + std::unique_ptr handleGetTargetGpioPinStates( + Commands::GetTargetGpioPinStates& command + ); + std::unique_ptr handleSetTargetGpioPinState(Commands::SetTargetGpioPinState& command); std::unique_ptr handleGetTargetStackPointer( Commands::GetTargetStackPointer& command ); diff --git a/src/Targets/CMakeLists.txt b/src/Targets/CMakeLists.txt index 8f1e973f..9cf4dae2 100755 --- a/src/Targets/CMakeLists.txt +++ b/src/Targets/CMakeLists.txt @@ -3,16 +3,24 @@ target_sources( PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/TargetDescriptor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TargetAddressSpaceDescriptor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TargetMemorySegmentDescriptor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TargetPeripheralDescriptor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TargetPeripheralSignalDescriptor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TargetRegisterGroupDescriptor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TargetRegisterDescriptor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TargetBitFieldDescriptor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TargetPinoutDescriptor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TargetPinDescriptor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/TargetVariantDescriptor.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TargetMemoryCache.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TargetPhysicalInterface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/TargetDescription/TargetDescriptionFile.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AVR/AVR8/Avr8.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AVR/AVR8/Avr8TargetConfig.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AVR/AVR8/TargetDescriptionFile.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AVR8/Avr8.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AVR8/Avr8TargetConfig.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AVR8/TargetDescriptionFile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AVR8/OpcodeDecoder/Decoder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Microchip/AVR8/IspParameters.cpp ${CMAKE_CURRENT_SOURCE_DIR}/RiscV/RiscV.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/RiscV/TargetDescription/TargetDescriptionFile.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/RiscV/RiscVTargetConfig.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/RiscV/TargetDescriptionFile.cpp ) diff --git a/src/Targets/Microchip/AVR/AVR8/Avr8.cpp b/src/Targets/Microchip/AVR/AVR8/Avr8.cpp deleted file mode 100644 index 686ab38b..00000000 --- a/src/Targets/Microchip/AVR/AVR8/Avr8.cpp +++ /dev/null @@ -1,1073 +0,0 @@ -#include "Avr8.hpp" - -#include -#include -#include -#include -#include - -#include "src/Logger/Logger.hpp" -#include "src/Services/PathService.hpp" -#include "src/Services/StringService.hpp" - -#include "src/Exceptions/InvalidConfig.hpp" -#include "Exceptions/DebugWirePhysicalInterfaceError.hpp" - -namespace Targets::Microchip::Avr::Avr8Bit -{ - using namespace Exceptions; - - Avr8::Avr8(const TargetConfig& targetConfig, TargetDescription::TargetDescriptionFile&& targetDescriptionFile) - : targetConfig(Avr8TargetConfig(targetConfig)) - , targetDescriptionFile(std::move(targetDescriptionFile)) - , signature(this->targetDescriptionFile.getTargetSignature()) - , name(this->targetDescriptionFile.getTargetName()) - , family(this->targetDescriptionFile.getAvrFamily()) - , targetParameters(this->targetDescriptionFile.getTargetParameters()) - , physicalInterfaces(this->targetDescriptionFile.getPhysicalInterfaces()) - , padDescriptorsByName(this->targetDescriptionFile.getPadDescriptorsMappedByName()) - , targetVariantsById(this->targetDescriptionFile.getVariantsMappedById()) - , stackPointerRegisterDescriptor( - TargetRegisterDescriptor( - TargetRegisterType::STACK_POINTER, - this->targetParameters.stackPointerRegisterLowAddress.value(), - this->targetParameters.stackPointerRegisterSize.value(), - TargetMemoryType::OTHER, - "SP", - "CPU", - "Stack Pointer Register", - TargetRegisterAccess(true, true) - ) - ) - , statusRegisterDescriptor( - TargetRegisterDescriptor( - TargetRegisterType::STATUS_REGISTER, - this->targetParameters.statusRegisterStartAddress.value(), - this->targetParameters.statusRegisterSize.value(), - TargetMemoryType::OTHER, - "SREG", - "CPU", - "Status Register", - TargetRegisterAccess(true, true) - ) - ) - , fuseEnableStrategy(this->targetDescriptionFile.getFuseEnableStrategy().value_or(FuseEnableStrategy::CLEAR)) - { - if (!this->physicalInterfaces.contains(this->targetConfig.physicalInterface)) { - /* - * The user has selected a physical interface that does not appear to be supported by the selected - * target. - * - * Bloom's target description files provide a list of supported physical interfaces for each target - * (which is how this->physicalInterfaces is populated), but it's possible that this list may - * be wrong/incomplete. For this reason, we don't throw an exception here. Instead, we just present the - * user with a warning and a list of physical interfaces known to be supported by their selected target. - */ - const auto physicalInterfaceNames = getPhysicalInterfaceNames(); - - const auto supportedPhysicalInterfaceList = std::accumulate( - this->physicalInterfaces.begin(), - this->physicalInterfaces.end(), - std::string(), - [&physicalInterfaceNames] (const std::string& string, TargetPhysicalInterface physicalInterface) { - if (physicalInterface == TargetPhysicalInterface::ISP) { - /* - * Don't include the ISP interface in the list of supported interfaces, as doing so may - * mislead the user into thinking the ISP interface can be used for debugging operations. - */ - return string; - } - - return string + "\n - " + physicalInterfaceNames.at(physicalInterface); - } - ); - - Logger::warning( - "\nThe selected target (" + this->name + ") does not support the selected physical interface (" - + physicalInterfaceNames.at(this->targetConfig.physicalInterface) + "). Target activation " - "will likely fail. The target supports the following physical interfaces: \n" - + supportedPhysicalInterfaceList + "\n\nFor physical interface configuration values, see " - + Services::PathService::homeDomainName() + "/docs/configuration/target-physical-interfaces." - ); - } - - if ( - this->targetConfig.manageOcdenFuseBit - && this->targetConfig.physicalInterface != TargetPhysicalInterface::JTAG - ) { - Logger::warning( - "The 'manageOcdenFuseBit' parameter only applies to JTAG targets. It will be ignored in this session." - ); - } - - this->loadTargetRegisterDescriptors(); - this->loadTargetMemoryDescriptors(); - } - - bool Avr8::supportsDebugTool(DebugTool* debugTool) { - return debugTool->getAvr8DebugInterface( - this->targetConfig, - this->family, - this->targetParameters, - this->targetRegisterDescriptorsById - ) != nullptr; - } - - void Avr8::setDebugTool(DebugTool* debugTool) { - this->targetPowerManagementInterface = debugTool->getTargetPowerManagementInterface(); - this->avr8DebugInterface = debugTool->getAvr8DebugInterface( - this->targetConfig, - this->family, - this->targetParameters, - this->targetRegisterDescriptorsById - ); - - this->avrIspInterface = debugTool->getAvrIspInterface( - this->targetConfig - ); - - if (this->avrIspInterface != nullptr) { - this->avrIspInterface->configure(this->targetConfig); - } - - if ( - this->targetConfig.manageDwenFuseBit - && this->avrIspInterface == nullptr - && this->targetConfig.physicalInterface == TargetPhysicalInterface::DEBUG_WIRE - ) { - Logger::warning( - "The connected debug tool (or associated driver) does not provide any ISP interface. " - "Bloom will be unable to manage the DWEN fuse bit." - ); - } - } - - void Avr8::activate() { - if (this->isActivated()) { - return; - } - - this->avr8DebugInterface->init(); - - try { - this->avr8DebugInterface->activate(); - - } catch (const Exceptions::DebugWirePhysicalInterfaceError& debugWireException) { - // We failed to activate the debugWire physical interface. DWEN fuse bit may need updating. - - if (!this->targetConfig.manageDwenFuseBit) { - throw TargetOperationFailure( - "Failed to activate debugWire physical interface - check target connection and DWEN fuse " - "bit. Bloom can manage the DWEN fuse bit automatically. For instructions on enabling this " - "function, see " + Services::PathService::homeDomainName() + "/docs/debugging-avr-debugwire" - ); - } - - try { - Logger::warning( - "Failed to activate the debugWire physical interface - attempting to access target via " - "the ISP interface, for DWEN fuse bit inspection." - ); - this->updateDwenFuseBit(true); - - // If the debug tool provides a TargetPowerManagementInterface, attempt to cycle the target power - if ( - this->targetPowerManagementInterface != nullptr - && this->targetConfig.cycleTargetPowerPostDwenUpdate - ) { - Logger::info("Cycling target power"); - - Logger::debug("Disabling target power"); - this->targetPowerManagementInterface->disableTargetPower(); - - Logger::debug( - "Holding power off for ~" + std::to_string(this->targetConfig.targetPowerCycleDelay.count()) - + " ms" - ); - std::this_thread::sleep_for(this->targetConfig.targetPowerCycleDelay); - - Logger::debug("Enabling target power"); - this->targetPowerManagementInterface->enableTargetPower(); - - Logger::debug( - "Waiting ~" + std::to_string(this->targetConfig.targetPowerCycleDelay.count()) - + " ms for target power-up" - ); - std::this_thread::sleep_for(this->targetConfig.targetPowerCycleDelay); - } - - } catch (const Exception& exception) { - throw Exception( - "Failed to access/update DWEN fuse bit via ISP interface - " + exception.getMessage() - ); - } - - Logger::info("Retrying debugWire physical interface activation"); - this->avr8DebugInterface->activate(); - } - - if ( - this->targetConfig.physicalInterface == TargetPhysicalInterface::JTAG - && this->targetConfig.manageOcdenFuseBit - ) { - Logger::debug("Attempting OCDEN fuse bit management"); - this->updateOcdenFuseBit(true); - } - - this->activated = true; - - /* - * Validate the target signature. - * - * The signature obtained from the device should match what we loaded from the target description file. - */ - const auto targetSignature = this->avr8DebugInterface->getDeviceId(); - - if (targetSignature != this->signature) { - throw Exception( - "Failed to validate connected target - target signature mismatch.\nThe target signature" - " (\"" + targetSignature.toHex() + "\") does not match the AVR8 target description signature (\"" - + this->signature.toHex() + "\"). This will likely be due to an incorrect target name in the " - + "configuration file (bloom.yaml)." - ); - } - - this->avr8DebugInterface->reset(); - } - - void Avr8::deactivate() { - try { - if (this->avr8DebugInterface == nullptr) { - return; - } - - this->stop(); - this->clearAllBreakpoints(); - - if ( - this->targetConfig.physicalInterface == TargetPhysicalInterface::JTAG - && this->targetConfig.manageOcdenFuseBit - ) { - Logger::debug("Attempting OCDEN fuse bit management"); - this->updateOcdenFuseBit(false); - - } else { - this->avr8DebugInterface->deactivate(); - } - - this->activated = false; - - } catch (const Exception& exception) { - Logger::error("Failed to deactivate AVR8 target - " + exception.getMessage()); - } - } - - TargetDescriptor Avr8::getDescriptor() { - auto descriptor = TargetDescriptor( - this->signature.toHex(), - TargetFamily::AVR_8, - this->name, - "Microchip", - this->targetMemoryDescriptorsByType, - this->targetRegisterDescriptorsById, - this->getBreakpointResources(), - {}, - Targets::TargetMemoryType::FLASH - ); - - std::transform( - this->targetVariantsById.begin(), - this->targetVariantsById.end(), - std::back_inserter(descriptor.variants), - [] (auto& variantToIdPair) { - return variantToIdPair.second; - } - ); - - return descriptor; - } - - void Avr8::run(std::optional toAddress) { - if (toAddress.has_value()) { - return this->avr8DebugInterface->runTo(*toAddress); - } - - this->avr8DebugInterface->run(); - } - - void Avr8::stop() { - this->avr8DebugInterface->stop(); - } - - void Avr8::step() { - this->avr8DebugInterface->step(); - } - - void Avr8::reset() { - this->avr8DebugInterface->reset(); - } - - void Avr8::setSoftwareBreakpoint(TargetMemoryAddress address) { - this->avr8DebugInterface->setSoftwareBreakpoint(address); - } - - void Avr8::removeSoftwareBreakpoint(TargetMemoryAddress address) { - this->avr8DebugInterface->clearSoftwareBreakpoint(address); - } - - void Avr8::setHardwareBreakpoint(TargetMemoryAddress address) { - this->avr8DebugInterface->setHardwareBreakpoint(address); - } - - void Avr8::removeHardwareBreakpoint(TargetMemoryAddress address) { - this->avr8DebugInterface->clearHardwareBreakpoint(address); - } - - void Avr8::clearAllBreakpoints() { - this->avr8DebugInterface->clearAllBreakpoints(); - } - - TargetRegisters Avr8::readRegisters(const Targets::TargetRegisterDescriptorIds& descriptorIds) { - return this->avr8DebugInterface->readRegisters(descriptorIds); - } - - void Avr8::writeRegisters(const TargetRegisters& registers) { - this->avr8DebugInterface->writeRegisters(registers); - } - - TargetMemoryBuffer Avr8::readMemory( - TargetMemoryType memoryType, - std::uint32_t startAddress, - std::uint32_t bytes, - const std::set& excludedAddressRanges - ) { - return this->avr8DebugInterface->readMemory(memoryType, startAddress, bytes, excludedAddressRanges); - } - - void Avr8::writeMemory(TargetMemoryType memoryType, std::uint32_t startAddress, const TargetMemoryBuffer& buffer) { - if (memoryType == TargetMemoryType::FLASH && !this->programmingModeEnabled()) { - throw Exception("Attempted Flash memory write with no active programming session."); - } - - this->avr8DebugInterface->writeMemory(memoryType, startAddress, buffer); - } - - void Avr8::eraseMemory(TargetMemoryType memoryType) { - if (memoryType == TargetMemoryType::FLASH) { - if (this->targetConfig.physicalInterface == TargetPhysicalInterface::DEBUG_WIRE) { - // debugWire targets do not need to be erased - return; - } - - if (!this->programmingModeEnabled()) { - throw Exception("Attempted Flash memory erase with no active programming session."); - } - - /* - * For JTAG and UPDI targets, we must perform a chip erase. This means we could end up erasing EEPROM, - * unless the EESAVE fuse bit has been programmed. - * - * If configured to do so, we will ensure that the EESAVE fuse bit has been programmed before we perform - * the chip erase. The fuse will be restored to its original value at the end of the programming session. - */ - if ( - this->targetConfig.physicalInterface == TargetPhysicalInterface::JTAG - || this->targetConfig.physicalInterface == TargetPhysicalInterface::UPDI - ) { - if (this->targetConfig.preserveEeprom) { - Logger::debug("Inspecting EESAVE fuse bit"); - this->activeProgrammingSession->managingEesaveFuseBit = this->updateEesaveFuseBit(true); - - } else { - Logger::warning( - "Performing chip-erase with preserveEeprom disabled. All EEPROM data will be lost!" - ); - } - - return this->avr8DebugInterface->eraseChip(); - } - - return this->avr8DebugInterface->eraseProgramMemory(); - } - - /* - * Debug tools do not have to support the erasing of RAM or EEPROM memory. We just implement this as a - * write operation. - */ - this->writeMemory( - memoryType, - memoryType == TargetMemoryType::RAM - ? this->targetParameters.ramStartAddress.value() - : this->targetParameters.eepromStartAddress.value(), - TargetMemoryBuffer( - memoryType == TargetMemoryType::RAM - ? this->targetParameters.ramSize.value() - : this->targetParameters.eepromSize.value(), - 0xFF - ) - ); - } - - TargetState Avr8::getState() { - return this->avr8DebugInterface->getTargetState(); - } - - std::uint32_t Avr8::getProgramCounter() { - return this->avr8DebugInterface->getProgramCounter(); - } - - void Avr8::setProgramCounter(std::uint32_t programCounter) { - this->avr8DebugInterface->setProgramCounter(programCounter); - } - - std::uint32_t Avr8::getStackPointer() { - const auto stackPointerRegister = this->readRegisters( - {this->stackPointerRegisterDescriptor.id} - ).front(); - - std::uint32_t stackPointer = 0; - for (std::size_t i = 0; i < stackPointerRegister.size() && i < 4; i++) { - stackPointer = (stackPointer << (8 * i)) | stackPointerRegister.value[i]; - } - - return stackPointer; - } - - std::map Avr8::getPinStates(int variantId) { - const auto targetVariantIt = this->targetVariantsById.find(variantId); - - if (targetVariantIt == this->targetVariantsById.end()) { - throw Exception("Invalid target variant ID"); - } - - std::map output; - const auto& variant = targetVariantIt->second; - - /* - * To prevent the number of memory reads we perform here, we cache the data and map it by start address. - * - * This way, we only perform 3 memory reads for a target variant with 3 ports - one per port (instead of one - * per pin). - * - * We may be able to make this more efficient by combining reads for ports with aligned memory addresses. This - * will be considered when the need for it becomes apparent. - */ - std::map cachedMemoryByStartAddress; - const auto readMemoryBitset = [this, &cachedMemoryByStartAddress] (std::uint16_t startAddress) { - auto cachedByteIt = cachedMemoryByStartAddress.find(startAddress); - - if (cachedByteIt == cachedMemoryByStartAddress.end()) { - cachedByteIt = cachedMemoryByStartAddress.insert( - std::pair( - startAddress, - this->readMemory(TargetMemoryType::RAM, startAddress, 1) - ) - ).first; - } - - return std::bitset::digits>( - cachedByteIt->second.at(0) - ); - }; - - for (const auto& [pinNumber, pinDescriptor] : variant.pinDescriptorsByNumber) { - const auto padIt = this->padDescriptorsByName.find(pinDescriptor.padName); - - if (padIt != this->padDescriptorsByName.end()) { - const auto& pad = padIt->second; - - if (!pad.gpioPinNumber.has_value()) { - continue; - } - - auto pinState = TargetPinState(); - - if (pad.gpioDdrAddress.has_value()) { - const auto ddrValue = readMemoryBitset(pad.gpioDdrAddress.value()); - - pinState.ioDirection = ddrValue.test(pad.gpioPinNumber.value()) ? - TargetPinState::IoDirection::OUTPUT : TargetPinState::IoDirection::INPUT; - - if (pinState.ioDirection == TargetPinState::IoDirection::OUTPUT - && pad.gpioPortAddress.has_value() - ) { - const auto portRegisterValueBitset = readMemoryBitset(pad.gpioPortAddress.value()); - pinState.ioState = portRegisterValueBitset.test(pad.gpioPinNumber.value()) ? - TargetPinState::IoState::HIGH : TargetPinState::IoState::LOW; - - } else if (pinState.ioDirection == TargetPinState::IoDirection::INPUT - && pad.gpioPortInputAddress.has_value() - ) { - const auto portInputRegisterValue = readMemoryBitset(pad.gpioPortInputAddress.value()); - pinState.ioState = portInputRegisterValue.test(pad.gpioPinNumber.value()) ? - TargetPinState::IoState::HIGH : TargetPinState::IoState::LOW; - } - } - - output.insert(std::pair(pinNumber, pinState)); - } - } - - return output; - } - - void Avr8::setPinState(const TargetPinDescriptor& pinDescriptor, const TargetPinState& state) { - const auto targetVariantIt = this->targetVariantsById.find(pinDescriptor.variantId); - - if (targetVariantIt == this->targetVariantsById.end()) { - throw Exception("Invalid target variant ID"); - } - - const auto padDescriptorIt = this->padDescriptorsByName.find(pinDescriptor.padName); - - if (padDescriptorIt == this->padDescriptorsByName.end()) { - throw Exception("Unknown pad"); - } - - if (!state.ioDirection.has_value()) { - throw Exception("Missing IO direction state"); - } - - const auto& variant = targetVariantIt->second; - const auto& padDescriptor = padDescriptorIt->second; - auto ioState = state.ioState; - - if (state.ioDirection == TargetPinState::IoDirection::INPUT) { - // When setting the direction to INPUT, we must always set the IO pin state to LOW - ioState = TargetPinState::IoState::LOW; - } - - if ( - !padDescriptor.gpioDdrAddress.has_value() - || !padDescriptor.gpioPortAddress.has_value() - || !padDescriptor.gpioPinNumber.has_value() - ) { - throw Exception("Inadequate pad descriptor"); - } - - const auto pinNumber = padDescriptor.gpioPinNumber.value(); - const auto ddrAddress = padDescriptor.gpioDdrAddress.value(); - const auto ddrValue = this->readMemory(TargetMemoryType::RAM, ddrAddress, 1); - - if (ddrValue.empty()) { - throw Exception("Failed to read DDR value"); - } - - auto ddrValueBitset = std::bitset::digits>(ddrValue.front()); - if (ddrValueBitset.test(pinNumber) != (state.ioDirection == TargetPinState::IoDirection::OUTPUT)) { - // DDR needs updating - ddrValueBitset.set(pinNumber, (state.ioDirection == TargetPinState::IoDirection::OUTPUT)); - - this->writeMemory( - TargetMemoryType::RAM, - ddrAddress, - {static_cast(ddrValueBitset.to_ulong())} - ); - } - - if (ioState.has_value()) { - const auto portRegisterAddress = padDescriptor.gpioPortAddress.value(); - const auto portRegisterValue = this->readMemory(TargetMemoryType::RAM, portRegisterAddress, 1); - - if (portRegisterValue.empty()) { - throw Exception("Failed to read PORT register value"); - } - - auto portRegisterValueBitset = std::bitset::digits>( - portRegisterValue.front() - ); - - if (portRegisterValueBitset.test(pinNumber) != (ioState == TargetPinState::IoState::HIGH)) { - // PORT set register needs updating - portRegisterValueBitset.set(pinNumber, (ioState == TargetPinState::IoState::HIGH)); - - this->writeMemory( - TargetMemoryType::RAM, - portRegisterAddress, - {static_cast(portRegisterValueBitset.to_ulong())} - ); - } - } - } - - void Avr8::enableProgrammingMode() { - if (this->activeProgrammingSession.has_value()) { - return; - } - - this->avr8DebugInterface->enableProgrammingMode(); - this->activeProgrammingSession = ProgrammingSession(); - } - - void Avr8::disableProgrammingMode() { - if (!this->activeProgrammingSession.has_value()) { - return; - } - - if (this->activeProgrammingSession->managingEesaveFuseBit) { - this->updateEesaveFuseBit(false); - } - - this->avr8DebugInterface->disableProgrammingMode(); - this->activeProgrammingSession.reset(); - } - - bool Avr8::programmingModeEnabled() { - return this->activeProgrammingSession.has_value(); - } - - void Avr8::loadTargetRegisterDescriptors() { - this->targetRegisterDescriptorsById = this->targetDescriptionFile.getRegisterDescriptorsMappedById(); - - /* - * All AVR8 targets possess 32 general purpose CPU registers. These are not described in the TDF, so we - * construct the descriptors for them here. - */ - const auto gpRegisterStartAddress = this->targetParameters.gpRegisterStartAddress.value_or(0); - for (std::uint8_t i = 0; i <= 31; i++) { - auto generalPurposeRegisterDescriptor = TargetRegisterDescriptor( - TargetRegisterType::GENERAL_PURPOSE_REGISTER, - gpRegisterStartAddress + i, - 1, - TargetMemoryType::OTHER, - "r" + std::to_string(i), - "CPU General Purpose", - std::nullopt, - TargetRegisterAccess(true, true) - ); - - this->targetRegisterDescriptorsById.emplace( - generalPurposeRegisterDescriptor.id, - std::move(generalPurposeRegisterDescriptor) - ); - } - - this->targetRegisterDescriptorsById.emplace( - this->stackPointerRegisterDescriptor.id, - this->stackPointerRegisterDescriptor - ); - this->targetRegisterDescriptorsById.emplace( - this->statusRegisterDescriptor.id, - this->statusRegisterDescriptor - ); - } - - void Avr8::loadTargetMemoryDescriptors() { - const auto ramStartAddress = this->targetParameters.ramStartAddress.value(); - const auto flashStartAddress = this->targetParameters.flashStartAddress.value(); - - this->targetMemoryDescriptorsByType.insert(std::pair( - TargetMemoryType::RAM, - TargetMemoryDescriptor( - TargetMemoryType::RAM, - TargetMemoryAddressRange( - ramStartAddress, - ramStartAddress + this->targetParameters.ramSize.value() - 1 - ), - TargetMemoryAccess(true, true, true) - ) - )); - - this->targetMemoryDescriptorsByType.insert(std::pair( - TargetMemoryType::FLASH, - TargetMemoryDescriptor( - TargetMemoryType::FLASH, - TargetMemoryAddressRange( - flashStartAddress, - flashStartAddress + this->targetParameters.flashSize.value() - 1 - ), - TargetMemoryAccess(true, true, false), - this->targetParameters.flashPageSize - ) - )); - - if (this->targetParameters.eepromStartAddress.has_value() && this->targetParameters.eepromSize.has_value()) { - const auto eepromStartAddress = this->targetParameters.eepromStartAddress.value(); - - this->targetMemoryDescriptorsByType.insert(std::pair( - TargetMemoryType::EEPROM, - TargetMemoryDescriptor( - TargetMemoryType::EEPROM, - TargetMemoryAddressRange( - eepromStartAddress, - eepromStartAddress + this->targetParameters.eepromSize.value() - 1 - ), - TargetMemoryAccess(true, true, true) - ) - )); - } - } - - BreakpointResources Avr8::getBreakpointResources() { - auto maxHardwareBreakpoints = static_cast(0); - - switch (this->targetConfig.physicalInterface) { - case TargetPhysicalInterface::JTAG: { - maxHardwareBreakpoints = this->family == Family::XMEGA ? 2 : 3; - break; - } - case TargetPhysicalInterface::PDI: { - maxHardwareBreakpoints = 2; - break; - } - case TargetPhysicalInterface::UPDI: { - maxHardwareBreakpoints = 1; - break; - } - default: { - break; - } - } - - return BreakpointResources( - maxHardwareBreakpoints, - std::nullopt, - std::min( - static_cast(this->targetConfig.reserveSteppingBreakpoint ? 1 : 0), - maxHardwareBreakpoints - ) - ); - } - - bool Avr8::isFuseEnabled(const FuseBitsDescriptor& descriptor, unsigned char fuseByteValue) const { - const auto programmedValue = static_cast( - this->fuseEnableStrategy == FuseEnableStrategy::SET - ? (0xFF & descriptor.bitMask) - : 0 - ); - - return (fuseByteValue & descriptor.bitMask) == programmedValue; - } - - unsigned char Avr8::setFuseEnabled( - const FuseBitsDescriptor& descriptor, - unsigned char fuseByteValue, - bool enabled - ) const { - return static_cast( - this->fuseEnableStrategy == FuseEnableStrategy::SET - ? enabled - ? (fuseByteValue | descriptor.bitMask) - : fuseByteValue & ~(descriptor.bitMask) - : enabled - ? fuseByteValue & ~(descriptor.bitMask) - : (fuseByteValue | descriptor.bitMask) - ); - } - - void Avr8::updateDwenFuseBit(bool enable) { - if (this->avrIspInterface == nullptr) { - throw Exception( - "Debug tool or driver does not provide access to an ISP interface - please confirm that the " - "debug tool supports ISP and then report this issue via " + Services::PathService::homeDomainName() - + "/report-issue" - ); - } - - if (!this->physicalInterfaces.contains(TargetPhysicalInterface::DEBUG_WIRE)) { - throw Exception( - "Target does not support debugWire physical interface - check target configuration or " - "report this issue via " + Services::PathService::homeDomainName() + "/report-issue" - ); - } - - const auto dwenFuseBitsDescriptor = this->targetDescriptionFile.getDwenFuseBitsDescriptor(); - const auto spienFuseBitsDescriptor = this->targetDescriptionFile.getSpienFuseBitsDescriptor(); - - if (!dwenFuseBitsDescriptor.has_value()) { - throw Exception("Could not find DWEN bit field in TDF."); - } - - if (!spienFuseBitsDescriptor.has_value()) { - throw Exception("Could not find SPIEN bit field in TDF."); - } - - Logger::debug("Extracting ISP parameters from TDF"); - this->avrIspInterface->setIspParameters(this->targetDescriptionFile.getIspParameters()); - - Logger::info("Initiating ISP interface"); - this->avrIspInterface->activate(); - - /* - * AVR fuses are used to control certain functions within the AVR (including the debugWire interface). Care - * must be taken when updating these fuse bytes, as an incorrect value could render the AVR inaccessible to - * standard programmers. - * - * When updating the DWEN fuse, Bloom relies on data from the target description file (TDF). But there is no - * guarantee that this data is correct. For this reason, we perform additional checks in an attempt to reduce - * the likelihood of bricking the target: - * - * - Confirm target signature match - We read the AVR signature from the connected target and compare it to - * what we have in the TDF. The operation will be aborted if the signatures do not match. - * - * - SPIEN fuse bit check - we can be certain that the SPIEN fuse bit is set, because we couldn't have gotten - * this far (post ISP activation) if it wasn't. We use this axiom to verify the validity of the data in the - * TDF. If the SPIEN fuse bit appears to be cleared, we can be fairly certain that the data we have on the - * SPIEN fuse bit is incorrect. From this, we assume that the data for the DWEN fuse bit is also incorrect, - * and abort the operation. - * - * - Lock bits check - we read the lock bit byte from the target and confirm that all lock bits are cleared. - * If any lock bits are set, we abort the operation. - * - * - DWEN fuse bit check - if the DWEN fuse bit is already set to the desired value, then there is no need - * to update it. But we may be checking the wrong bit (if the TDF data is incorrect) - either way, we will - * abort the operation. - * - * The precautions described above may reduce the likelihood of Bloom bricking the connected target, but there - * is still a chance that all of the checks pass, and we still brick the device. Now would be a good time to - * remind the user of liabilities in regard to Bloom and its contributors. - */ - Logger::warning( - "Updating the DWEN fuse bit is a potentially dangerous operation. Bloom is provided \"AS IS\", " - "without warranty of any kind. You are using Bloom at your own risk. In no event shall the copyright " - "owner or contributors be liable for any damage caused as a result of using Bloom. For more details, " - "see the Bloom license at " + Services::PathService::homeDomainName() + "/license" - ); - - try { - Logger::info("Reading target signature via ISP"); - const auto ispDeviceSignature = this->avrIspInterface->getDeviceId(); - - if (ispDeviceSignature != this->signature) { - throw Exception( - "AVR target signature mismatch - expected signature \"" + this->signature.toHex() - + "\" but got \"" + ispDeviceSignature.toHex() + "\". Please check target configuration." - ); - } - - Logger::info("Target signature confirmed: " + ispDeviceSignature.toHex()); - - const auto dwenFuseByte = this->avrIspInterface->readFuse(dwenFuseBitsDescriptor->fuseType).value; - const auto spienFuseByte = (spienFuseBitsDescriptor->fuseType == dwenFuseBitsDescriptor->fuseType) - ? dwenFuseByte - : this->avrIspInterface->readFuse(spienFuseBitsDescriptor->fuseType).value; - - /* - * Keep in mind that, for AVR fuses and lock bits, a set bit (0b1) means the fuse/lock is cleared, and a - * cleared bit (0b0), means the fuse/lock is set. - */ - - if (!this->isFuseEnabled(*spienFuseBitsDescriptor, spienFuseByte)) { - /* - * If we get here, something is very wrong. The SPIEN (SPI enable) fuse bit appears to be cleared, but - * this is not possible because we're connected to the target via the SPI (the ISP interface uses a - * physical SPI between the debug tool and the target). - * - * This could be (and likely is) caused by incorrect data for the SPIEN fuse bit, in the TDF (which was - * used to construct the spienFuseBitsDescriptor). And if the data for the SPIEN fuse bit is incorrect, - * then what's to say the data for the DWEN fuse bit (dwenFuseBitsDescriptor) is any better? - * - * We must assume the worst and abort the operation. Otherwise, we risk bricking the user's hardware. - */ - throw Exception( - "Invalid SPIEN fuse bit value - suspected inaccuracies in TDF data. Please report this to " - "Bloom developers as a matter of urgency, via " + Services::PathService::homeDomainName() - + "/report-issue" - ); - } - - Logger::info("Current SPIEN fuse bit value confirmed"); - - if (this->isFuseEnabled(*dwenFuseBitsDescriptor, dwenFuseByte) == enable) { - /* - * The DWEN fuse appears to already be set to the desired value. This may be a result of incorrect data - * in the TDF, but we're not taking any chances. - * - * We don't throw an exception here, because we don't know if this is due to an error, or if the fuse - * bit is simply already set to the desired value. - */ - Logger::debug("DWEN fuse bit already set to desired value - aborting update operation"); - - this->avrIspInterface->deactivate(); - return; - } - - const auto lockBitByte = this->avrIspInterface->readLockBitByte(); - if (lockBitByte != 0xFF) { - /* - * There is at least one lock bit that is set. Setting the DWEN fuse bit with the lock bits set may - * brick the device. We must abort. - */ - throw Exception( - "At least one lock bit has been set - updating the DWEN fuse bit could potentially brick " - "the target." - ); - } - - Logger::info("Cleared lock bits confirmed"); - - const auto newFuse = Fuse( - dwenFuseBitsDescriptor->fuseType, - this->setFuseEnabled(*dwenFuseBitsDescriptor, dwenFuseByte, enable) - ); - - Logger::warning("Updating DWEN fuse bit"); - this->avrIspInterface->programFuse(newFuse); - - Logger::debug("Verifying DWEN fuse bit"); - if (this->avrIspInterface->readFuse(dwenFuseBitsDescriptor->fuseType).value != newFuse.value) { - throw Exception("Failed to update DWEN fuse bit - post-update verification failed"); - } - - Logger::info("DWEN fuse bit successfully updated"); - - this->avrIspInterface->deactivate(); - - } catch (const Exception& exception) { - this->avrIspInterface->deactivate(); - throw exception; - } - } - - void Avr8::updateOcdenFuseBit(bool enable) { - using Services::PathService; - using Services::StringService; - - if (!this->physicalInterfaces.contains(TargetPhysicalInterface::JTAG)) { - throw Exception( - "Target does not support JTAG physical interface - check target configuration or " - "report this issue via " + PathService::homeDomainName() + "/report-issue" - ); - } - - const auto targetSignature = this->avr8DebugInterface->getDeviceId(); - const auto tdSignature = this->targetDescriptionFile.getTargetSignature(); - - if (targetSignature != tdSignature) { - throw Exception( - "Failed to validate connected target - target signature mismatch.\nThe target signature" - " (\"" + targetSignature.toHex() + "\") does not match the AVR8 target description signature (\"" - + tdSignature.toHex() + "\"). This will likely be due to an incorrect target name in the " - + "configuration file (bloom.yaml)." - ); - } - - const auto ocdenFuseBitsDescriptor = this->targetDescriptionFile.getOcdenFuseBitsDescriptor(); - const auto jtagenFuseBitsDescriptor = this->targetDescriptionFile.getJtagenFuseBitsDescriptor(); - - if (!ocdenFuseBitsDescriptor.has_value()) { - throw Exception("Could not find OCDEN bit field in TDF."); - } - - if (!jtagenFuseBitsDescriptor.has_value()) { - throw Exception("Could not find JTAGEN bit field in TDF."); - } - - const auto ocdenFuseByteValue = this->avr8DebugInterface->readMemory( - TargetMemoryType::FUSES, - ocdenFuseBitsDescriptor->byteAddress, - 1 - ).at(0); - const auto jtagenFuseByteValue = jtagenFuseBitsDescriptor->byteAddress == ocdenFuseBitsDescriptor->byteAddress - ? ocdenFuseByteValue - : this->avr8DebugInterface->readMemory( - TargetMemoryType::FUSES, - jtagenFuseBitsDescriptor->byteAddress, - 1 - ).at(0) - ; - - Logger::debug("OCDEN fuse byte value (before update): 0x" + StringService::toHex(ocdenFuseByteValue)); - - if (!this->isFuseEnabled(*jtagenFuseBitsDescriptor, jtagenFuseByteValue)) { - /* - * If we get here, something has gone wrong. The JTAGEN fuse should always be programmed by this point. - * We wouldn't have been able to activate the JTAG physical interface if the fuse wasn't programmed. - * - * This means the data we have on the JTAGEN fuse bit, from the TDF, is likely incorrect. And if that's - * the case, we cannot rely on the data for the OCDEN fuse bit being any better. - */ - throw Exception( - "Invalid JTAGEN fuse bit value - suspected inaccuracies in TDF data. Please report this to " - "Bloom developers as a matter of urgency, via " + PathService::homeDomainName() + "/report-issue" - ); - } - - if (this->isFuseEnabled(*ocdenFuseBitsDescriptor, ocdenFuseByteValue) == enable) { - Logger::debug("OCDEN fuse bit already set to desired value - aborting update operation"); - return; - } - - const auto newValue = this->setFuseEnabled(*ocdenFuseBitsDescriptor, ocdenFuseByteValue, enable); - - Logger::debug("New OCDEN fuse byte value (to be written): 0x" + StringService::toHex(newValue)); - - Logger::warning("Updating OCDEN fuse bit"); - this->avr8DebugInterface->writeMemory( - TargetMemoryType::FUSES, - ocdenFuseBitsDescriptor->byteAddress, - {newValue} - ); - - Logger::debug("Verifying OCDEN fuse bit"); - const auto postUpdateOcdenByteValue = this->avr8DebugInterface->readMemory( - TargetMemoryType::FUSES, - ocdenFuseBitsDescriptor->byteAddress, - 1 - ).at(0); - - if (postUpdateOcdenByteValue != newValue) { - throw Exception("Failed to update OCDEN fuse bit - post-update verification failed"); - } - - Logger::info("OCDEN fuse bit updated"); - - this->disableProgrammingMode(); - } - - bool Avr8::updateEesaveFuseBit(bool enable) { - using Services::StringService; - - const auto eesaveFuseBitsDescriptor = this->targetDescriptionFile.getEesaveFuseBitsDescriptor(); - - if (!eesaveFuseBitsDescriptor.has_value()) { - throw Exception("Could not find EESAVE bit field in TDF."); - } - - const auto eesaveFuseByteValue = this->avr8DebugInterface->readMemory( - TargetMemoryType::FUSES, - eesaveFuseBitsDescriptor->byteAddress, - 1 - ).at(0); - - Logger::debug("EESAVE fuse byte value (before update): 0x" + StringService::toHex(eesaveFuseByteValue)); - - if (this->isFuseEnabled(*eesaveFuseBitsDescriptor, eesaveFuseByteValue) == enable) { - Logger::debug("EESAVE fuse bit already set to desired value - aborting update operation"); - return false; - } - - const auto newValue = this->setFuseEnabled(*eesaveFuseBitsDescriptor, eesaveFuseByteValue, enable); - - Logger::debug("New EESAVE fuse byte value (to be written): 0x" + StringService::toHex(newValue)); - - Logger::warning("Updating EESAVE fuse bit"); - this->avr8DebugInterface->writeMemory( - TargetMemoryType::FUSES, - eesaveFuseBitsDescriptor->byteAddress, - {newValue} - ); - - Logger::debug("Verifying EESAVE fuse bit"); - const auto postUpdateEesaveByteValue = this->avr8DebugInterface->readMemory( - TargetMemoryType::FUSES, - eesaveFuseBitsDescriptor->byteAddress, - 1 - ).at(0); - - if (postUpdateEesaveByteValue != newValue) { - throw Exception("Failed to update EESAVE fuse bit - post-update verification failed"); - } - - Logger::info("EESAVE fuse bit updated"); - - return true; - } - - ProgramMemorySection Avr8::getProgramMemorySectionFromAddress(std::uint32_t address) { - return this->targetParameters.bootSectionStartAddress.has_value() - && address >= this->targetParameters.bootSectionStartAddress.value() - ? ProgramMemorySection::BOOT - : ProgramMemorySection::APPLICATION; - } -} diff --git a/src/Targets/Microchip/AVR/AVR8/Avr8.hpp b/src/Targets/Microchip/AVR/AVR8/Avr8.hpp deleted file mode 100644 index b8b5cb31..00000000 --- a/src/Targets/Microchip/AVR/AVR8/Avr8.hpp +++ /dev/null @@ -1,220 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include "src/Targets/Target.hpp" -#include "src/DebugToolDrivers/DebugTool.hpp" - -#include "src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AVR8/Avr8DebugInterface.hpp" -#include "src/DebugToolDrivers/TargetInterfaces/Microchip/AVR/AvrIspInterface.hpp" - -#include "Family.hpp" -#include "TargetParameters.hpp" -#include "PadDescriptor.hpp" -#include "ProgramMemorySection.hpp" -#include "ProgrammingSession.hpp" - -#include "src/Targets/Microchip/AVR/Fuse.hpp" -#include "src/Targets/TargetPhysicalInterface.hpp" -#include "src/Targets/TargetRegisterDescriptor.hpp" -#include "src/Targets/TargetBreakpoint.hpp" - -#include "TargetDescription/TargetDescriptionFile.hpp" - -#include "Avr8TargetConfig.hpp" - -namespace Targets::Microchip::Avr::Avr8Bit -{ - class Avr8: public Target - { - public: - explicit Avr8( - const TargetConfig& targetConfig, - TargetDescription::TargetDescriptionFile&& targetDescriptionFile - ); - - /* - * The functions below implement the Target interface for AVR8 targets. - * - * See the Targets::Target abstract class for documentation on the expected behaviour of - * each function. - */ - - /** - * All AVR8 compatible debug tools must provide a valid Avr8Interface. - * - * @param debugTool - * @return - */ - bool supportsDebugTool(DebugTool* debugTool) override; - - void setDebugTool(DebugTool* debugTool) override; - - void activate() override; - void deactivate() override; - - TargetDescriptor getDescriptor() override; - - void run(std::optional toAddress = std::nullopt) override; - void stop() override; - void step() override; - void reset() override; - - void setSoftwareBreakpoint(TargetMemoryAddress address) override; - void removeSoftwareBreakpoint(TargetMemoryAddress address) override; - - void setHardwareBreakpoint(TargetMemoryAddress address) override; - void removeHardwareBreakpoint(TargetMemoryAddress address) override; - void clearAllBreakpoints() override; - - void writeRegisters(const TargetRegisters& registers) override; - TargetRegisters readRegisters(const Targets::TargetRegisterDescriptorIds& descriptorIds) override; - - TargetMemoryBuffer readMemory( - TargetMemoryType memoryType, - TargetMemoryAddress startAddress, - TargetMemorySize bytes, - const std::set& excludedAddressRanges = {} - ) override; - void writeMemory( - TargetMemoryType memoryType, - TargetMemoryAddress startAddress, - const TargetMemoryBuffer& buffer - ) override; - void eraseMemory(TargetMemoryType memoryType) override; - - TargetState getState() override; - - TargetMemoryAddress getProgramCounter() override; - void setProgramCounter(TargetMemoryAddress programCounter) override; - - TargetStackPointer getStackPointer() override; - - std::map getPinStates(int variantId) override; - void setPinState( - const TargetPinDescriptor& pinDescriptor, - const TargetPinState& state - ) override; - - void enableProgrammingMode() override; - - void disableProgrammingMode() override; - - bool programmingModeEnabled() override; - - protected: - DebugToolDrivers::TargetInterfaces::TargetPowerManagementInterface* targetPowerManagementInterface = nullptr; - DebugToolDrivers::TargetInterfaces::Microchip::Avr::Avr8::Avr8DebugInterface* avr8DebugInterface = nullptr; - DebugToolDrivers::TargetInterfaces::Microchip::Avr::AvrIspInterface* avrIspInterface = nullptr; - - Avr8TargetConfig targetConfig; - - TargetDescription::TargetDescriptionFile targetDescriptionFile; - - TargetSignature signature; - std::string name; - Family family; - - TargetParameters targetParameters; - - std::set physicalInterfaces; - std::map padDescriptorsByName; - std::map targetVariantsById; - - TargetRegisterDescriptor stackPointerRegisterDescriptor; - TargetRegisterDescriptor statusRegisterDescriptor; - - /** - * On some AVR8 targets, like the ATmega328P, a cleared fuse bit means the fuse is "programmed" (enabled). - * And a set bit means the fuse is "un-programmed" (disabled). But on others, like the ATmega4809, it's the - * other way around (set bit == enabled, cleared bit == disabled). - * - * The FuseEnableStrategy specifies the strategy of enabling a fuse. It's extracted from the TDF. - * See TargetDescription::getFuseEnableStrategy() for more. - */ - FuseEnableStrategy fuseEnableStrategy; - - std::map targetRegisterDescriptorsById; - - std::map targetMemoryDescriptorsByType; - - std::optional activeProgrammingSession = std::nullopt; - - /** - * Populates this->targetRegisterDescriptorsById with registers extracted from the TDF, as well as general - * purpose and other CPU registers. - */ - void loadTargetRegisterDescriptors(); - - void loadTargetMemoryDescriptors(); - - BreakpointResources getBreakpointResources(); - - /** - * Checks if a particular fuse is enabled in the given fuse byte value. Takes the target's fuse enable strategy - * into account. - * - * @param descriptor - * @param fuseByteValue - * - * @return - */ - bool isFuseEnabled(const FuseBitsDescriptor& descriptor, unsigned char fuseByteValue) const; - - /** - * Enables/disables a fuse within the given fuse byte, using the target's fuse enable strategy. - * - * @param descriptor - * @param fuseByteValue - * @param enabled - * - * @return - * The updated fuse byte value. - */ - unsigned char setFuseEnabled( - const FuseBitsDescriptor& descriptor, - unsigned char fuseByteValue, - bool enabled - ) const; - - /** - * Updates the debugWire enable (DWEN) fuse bit on the AVR target. - * - * @param enable - * True to enable the fuse, false to disable it. - */ - void updateDwenFuseBit(bool enable); - - /** - * Updates the On-chip debug enable (OCDEN) fuse bit on the AVR target. - * - * @param enable - * True to enable the fuse, false to disable it. - */ - void updateOcdenFuseBit(bool enable); - - /** - * Updates the "Preserve EEPROM" (EESAVE) fuse bit on the AVR target. - * - * @param enable - * True to enable the fuse, false to disable it. - * - * @return - * True if the fuse bit was updated. False if the fuse bit was already set to the desired value. - */ - bool updateEesaveFuseBit(bool enable); - - /** - * Resolves the program memory section from a program memory address. - * - * Currently unused, but will be needed soon. - * - * @param address - * @return - */ - ProgramMemorySection getProgramMemorySectionFromAddress(TargetMemoryAddress address); - }; -} diff --git a/src/Targets/Microchip/AVR/AVR8/PadDescriptor.hpp b/src/Targets/Microchip/AVR/AVR8/PadDescriptor.hpp deleted file mode 100644 index 81065d20..00000000 --- a/src/Targets/Microchip/AVR/AVR8/PadDescriptor.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace Targets::Microchip::Avr::Avr8Bit -{ - /** - * Pin configurations for AVR8 targets may vary across target variants. This is why we must differentiate a pin - * to a pad. A pin is mapped to a pad, but this mapping is variant specific. For example, pin 4 on - * the ATmega328P-PN (DIP variant) is mapped to a GPIO pad (PORTD/PIN2), but on the QFN variant (ATmega328P-MN), - * pin 4 is mapped to a GND pad. - * - * PadDescriptor describes a single pad on an AVR8 target. On target configuration, PadDescriptors are - * generated from the AVR8 target description file. These descriptors are mapped to pad names. - * See Avr8::loadPadDescriptors() for more. - */ - struct PadDescriptor - { - std::string name; - - std::optional gpioPinNumber; - std::optional gpioPortAddress; - std::optional gpioPortInputAddress; - std::optional gpioDdrAddress; - }; -} diff --git a/src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.cpp b/src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.cpp deleted file mode 100644 index defb37ac..00000000 --- a/src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.cpp +++ /dev/null @@ -1,1141 +0,0 @@ -#include "TargetDescriptionFile.hpp" - -#include "src/Services/PathService.hpp" -#include "src/Logger/Logger.hpp" - -#include "src/Exceptions/Exception.hpp" -#include "src/Targets/TargetDescription/Exceptions/TargetDescriptionParsingFailureException.hpp" - -namespace Targets::Microchip::Avr::Avr8Bit -{ - using namespace Exceptions; - - using Targets::TargetDescription::RegisterGroup; - using Targets::TargetDescription::AddressSpace; - using Targets::TargetDescription::MemorySegment; - using Targets::TargetDescription::Register; - using Targets::TargetVariant; - using Targets::TargetRegisterDescriptor; - using Services::StringService; - - TargetDescriptionFile::TargetDescriptionFile(const std::string& xmlFilePath) - : Targets::TargetDescription::TargetDescriptionFile(xmlFilePath) - { - this->loadPadDescriptors(); - this->loadTargetVariants(); - this->loadTargetRegisterDescriptors(); - } - - TargetSignature TargetDescriptionFile::getTargetSignature() const { - const auto& signatureGroup = this->getPropertyGroup("signatures"); - - return { - static_cast(std::stoul(signatureGroup.getProperty("signature0").value, nullptr, 16)), - static_cast(std::stoul(signatureGroup.getProperty("signature1").value, nullptr, 16)), - static_cast(std::stoul(signatureGroup.getProperty("signature2").value, nullptr, 16)) - }; - } - - Family TargetDescriptionFile::getAvrFamily() const { - static const auto targetFamiliesByName = TargetDescriptionFile::getFamilyNameToEnumMapping(); - - const auto familyIt = targetFamiliesByName.find( - QString::fromStdString(this->getDeviceAttribute("avr-family")).toLower().toStdString() - ); - - if (familyIt == targetFamiliesByName.end()) { - throw Exception("Unknown family name in target description file."); - } - - return familyIt->second; - } - - const Targets::TargetDescription::AddressSpace& TargetDescriptionFile::getProgramAddressSpace() const { - return this->getAddressSpace("prog"); - } - - const Targets::TargetDescription::AddressSpace& TargetDescriptionFile::getRamAddressSpace() const { - return this->getAddressSpace("data"); - } - - const Targets::TargetDescription::AddressSpace& TargetDescriptionFile::getEepromAddressSpace() const { - const auto addressSpace = this->tryGetAddressSpace("eeprom"); - return addressSpace.has_value() - ? addressSpace->get() - : this->getAddressSpace("data"); - } - - const Targets::TargetDescription::AddressSpace& TargetDescriptionFile::getIoAddressSpace() const { - return this->getAddressSpace("data"); - } - - const Targets::TargetDescription::AddressSpace& TargetDescriptionFile::getSignatureAddressSpace() const { - const auto addressSpace = this->tryGetAddressSpace("signatures"); - return addressSpace.has_value() - ? addressSpace->get() - : this->getAddressSpace("data"); - } - - const Targets::TargetDescription::AddressSpace& TargetDescriptionFile::getFuseAddressSpace() const { - const auto addressSpace = this->tryGetAddressSpace("fuses"); - return addressSpace.has_value() - ? addressSpace->get() - : this->getAddressSpace("data"); - } - - const Targets::TargetDescription::AddressSpace& TargetDescriptionFile::getLockbitAddressSpace() const { - const auto addressSpace = this->tryGetAddressSpace("lockbits"); - return addressSpace.has_value() - ? addressSpace->get() - : this->getAddressSpace("data"); - } - - const Targets::TargetDescription::MemorySegment& TargetDescriptionFile::getProgramMemorySegment() const { - return this->getProgramAddressSpace().getMemorySegment("internal_program_memory"); - } - - const Targets::TargetDescription::MemorySegment& TargetDescriptionFile::getRamMemorySegment() const { - return this->getRamAddressSpace().getMemorySegment("internal_ram"); - } - - const Targets::TargetDescription::MemorySegment& TargetDescriptionFile::getEepromMemorySegment() const { - return this->getEepromAddressSpace().getMemorySegment("internal_eeprom"); - } - - const Targets::TargetDescription::MemorySegment& TargetDescriptionFile::getIoMemorySegment() const { - const auto addressSpace = this->getIoAddressSpace(); - const auto segment = addressSpace.tryGetMemorySegment("io"); - return segment.has_value() - ? segment->get() - : addressSpace.getMemorySegment("mapped_io"); - } - - const Targets::TargetDescription::MemorySegment& TargetDescriptionFile::getSignatureMemorySegment() const { - return this->getSignatureAddressSpace().getMemorySegment("signatures"); - } - - const Targets::TargetDescription::MemorySegment& TargetDescriptionFile::getFuseMemorySegment() const { - return this->getFuseAddressSpace().getMemorySegment("fuses"); - } - - const Targets::TargetDescription::MemorySegment& TargetDescriptionFile::getLockbitMemorySegment() const { - return this->getLockbitAddressSpace().getMemorySegment("lockbits"); - } - - TargetParameters TargetDescriptionFile::getTargetParameters() const { - TargetParameters targetParameters; - - const auto programMemoryAddressSpace = this->getProgramMemoryAddressSpace(); - - if (programMemoryAddressSpace.has_value()) { - targetParameters.flashSize = programMemoryAddressSpace->size; - targetParameters.flashStartAddress = programMemoryAddressSpace->startAddress; - - const auto appMemorySegment = this->getFlashApplicationMemorySegment(programMemoryAddressSpace.value()); - - if (appMemorySegment.has_value()) { - targetParameters.appSectionStartAddress = appMemorySegment->startAddress; - targetParameters.appSectionSize = appMemorySegment->size; - targetParameters.flashPageSize = appMemorySegment->pageSize; - } - } - - const auto ramMemorySegment = this->getRamMemorySegment(); - if (ramMemorySegment.has_value()) { - targetParameters.ramSize = ramMemorySegment->size; - targetParameters.ramStartAddress = ramMemorySegment->startAddress; - } - - const auto ioMemorySegment = this->getIoMemorySegment(); - if (ioMemorySegment.has_value()) { - targetParameters.mappedIoSegmentSize = ioMemorySegment->size; - targetParameters.mappedIoSegmentStartAddress = ioMemorySegment->startAddress; - } - - const auto registerMemorySegment = this->getRegisterMemorySegment(); - if (registerMemorySegment.has_value()) { - targetParameters.gpRegisterSize = registerMemorySegment->size; - targetParameters.gpRegisterStartAddress = registerMemorySegment->startAddress; - } - - const auto eepromMemorySegment = this->getEepromMemorySegment(); - if (eepromMemorySegment.has_value()) { - targetParameters.eepromSize = eepromMemorySegment->size; - targetParameters.eepromStartAddress = eepromMemorySegment->startAddress; - - if (eepromMemorySegment->pageSize.has_value()) { - targetParameters.eepromPageSize = eepromMemorySegment->pageSize.value(); - } - } - - const auto firstBootSectionMemorySegment = this->getFirstBootSectionMemorySegment(); - if (firstBootSectionMemorySegment.has_value()) { - targetParameters.bootSectionStartAddress = firstBootSectionMemorySegment->startAddress / 2; - targetParameters.bootSectionSize = firstBootSectionMemorySegment->size; - } - - const auto cpuRegistersOffset = this->getPeripheralModuleRegisterAddressOffset("cpu", "cpu", "cpu"); - - const auto statusRegister = this->getStatusRegister(); - if (statusRegister.has_value()) { - targetParameters.statusRegisterStartAddress = cpuRegistersOffset + statusRegister->offset; - targetParameters.statusRegisterSize = statusRegister->size; - } - - const auto stackPointerRegister = this->getStackPointerRegister(); - if (stackPointerRegister.has_value()) { - targetParameters.stackPointerRegisterLowAddress = cpuRegistersOffset + stackPointerRegister->offset; - targetParameters.stackPointerRegisterSize = stackPointerRegister->size; - - } else { - // Sometimes the SP register is split into two register nodes, one for low, the other for high - const auto stackPointerLowRegister = this->getStackPointerLowRegister(); - const auto stackPointerHighRegister = this->getStackPointerHighRegister(); - - if (stackPointerLowRegister.has_value()) { - targetParameters.stackPointerRegisterLowAddress = cpuRegistersOffset - + stackPointerLowRegister->offset; - targetParameters.stackPointerRegisterSize = stackPointerLowRegister->size; - } - - if (stackPointerHighRegister.has_value()) { - targetParameters.stackPointerRegisterSize = - targetParameters.stackPointerRegisterSize.has_value() ? - targetParameters.stackPointerRegisterSize.value() + stackPointerHighRegister->size : - stackPointerHighRegister->size; - } - } - -// const auto& supportedPhysicalInterfaces = this->getSupportedPhysicalInterfaces(); -// -// if ( -// supportedPhysicalInterfaces.contains(PhysicalInterface::DEBUG_WIRE) -// || supportedPhysicalInterfaces.contains(PhysicalInterface::JTAG) -// ) { -// this->loadDebugWireAndJtagTargetParameters(targetParameters); -// } -// -// if (supportedPhysicalInterfaces.contains(PhysicalInterface::PDI)) { -// this->loadPdiTargetParameters(targetParameters); -// } -// -// if (supportedPhysicalInterfaces.contains(PhysicalInterface::UPDI)) { -// this->loadUpdiTargetParameters(targetParameters); -// } - - return targetParameters; - } - - IspParameters TargetDescriptionFile::getIspParameters() const { - const auto& ispParameterGroup = this->getPropertyGroup("isp_interface"); - auto output = IspParameters(); - - output.programModeTimeout = StringService::toUint8( - ispParameterGroup.getProperty("ispenterprogmode_timeout").value - ); - output.programModeStabilizationDelay = StringService::toUint8( - ispParameterGroup.getProperty("ispenterprogmode_stabdelay").value - ); - output.programModeCommandExecutionDelay = StringService::toUint8( - ispParameterGroup.getProperty("ispenterprogmode_cmdexedelay").value - ); - output.programModeSyncLoops = StringService::toUint8( - ispParameterGroup.getProperty("ispenterprogmode_synchloops").value - ); - output.programModeByteDelay = StringService::toUint8( - ispParameterGroup.getProperty("ispenterprogmode_bytedelay").value - ); - output.programModePollValue = StringService::toUint8( - ispParameterGroup.getProperty("ispenterprogmode_pollvalue").value - ); - output.programModePollIndex = StringService::toUint8( - ispParameterGroup.getProperty("ispenterprogmode_pollindex").value - ); - output.programModePreDelay = StringService::toUint8( - ispParameterGroup.getProperty("ispleaveprogmode_predelay").value - ); - output.programModePostDelay = StringService::toUint8( - ispParameterGroup.getProperty("ispleaveprogmode_postdelay").value - ); - output.readSignaturePollIndex = StringService::toUint8( - ispParameterGroup.getProperty("ispreadsign_pollindex").value - ); - output.readFusePollIndex = StringService::toUint8( - ispParameterGroup.getProperty("ispreadfuse_pollindex").value - ); - output.readLockPollIndex = StringService::toUint8( - ispParameterGroup.getProperty("ispreadlock_pollindex").value - ); - - return output; - } - - std::optional TargetDescriptionFile::getFuseEnableStrategy() const { - static const auto fuseEnableStrategies = std::map({ - {"0", FuseEnableStrategy::CLEAR}, - {"1", FuseEnableStrategy::SET}, - }); - - const auto programmingInfoPropertyGroupIt = this->propertyGroupsByKey.find("programming_info"); - - if (programmingInfoPropertyGroupIt != this->propertyGroupsByKey.end()) { - const auto& programmingInfoParamsByName = programmingInfoPropertyGroupIt->second.propertiesByKey; - const auto fuseEnabledValuePropertyIt = programmingInfoParamsByName.find("fuse_enabled_value"); - - if (fuseEnabledValuePropertyIt != programmingInfoParamsByName.end()) { - const auto fuseEnableStrategyIt = fuseEnableStrategies.find( - fuseEnabledValuePropertyIt->second.value - ); - - if (fuseEnableStrategyIt != fuseEnableStrategies.end()) { - return fuseEnableStrategyIt->second; - } - } - } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getDwenFuseBitsDescriptor() const { - return this->getFuseBitsDescriptorByName("dwen"); - } - - std::optional TargetDescriptionFile::getSpienFuseBitsDescriptor() const { - return this->getFuseBitsDescriptorByName("spien"); - } - - std::optional TargetDescriptionFile::getOcdenFuseBitsDescriptor() const { - return this->getFuseBitsDescriptorByName("ocden"); - } - - std::optional TargetDescriptionFile::getJtagenFuseBitsDescriptor() const { - return this->getFuseBitsDescriptorByName("jtagen"); - } - - std::optional TargetDescriptionFile::getEesaveFuseBitsDescriptor() const { - return this->getFuseBitsDescriptorByName("eesave"); - } - - void TargetDescriptionFile::loadPadDescriptors() { -// const auto portModuleIt = this->modulesMappedByName.find("port"); -// const auto portModule = (portModuleIt != this->modulesMappedByName.end()) -// ? std::optional(portModuleIt->second) -// : std::nullopt; -// -// const auto portPeripheralModuleIt = this->peripheralModulesMappedByName.find("port"); -// if (portPeripheralModuleIt == this->peripheralModulesMappedByName.end()) { -// return; -// } -// -// const auto& portPeripheralModule = portPeripheralModuleIt->second; - -// for (const auto& [instanceName, instance] : portPeripheralModule.instancesMappedByName) { -// if (instanceName.find("port") != 0) { -// continue; -// } -// -// const auto portRegisterAddressOffset = this->getPeripheralModuleRegisterAddressOffset( -// portPeripheralModule.name, -// instanceName, -// instanceName -// ); -// -// for (const auto& signal : instance.instanceSignals) { -// if (!signal.index.has_value()) { -// continue; -// } -// -// auto& padDescriptor = this->padDescriptorsByName.insert( -// std::pair(signal.padName, PadDescriptor()) -// ).first->second; -// -// padDescriptor.name = signal.padName; -// padDescriptor.gpioPinNumber = signal.index.value(); -// -// if (!portModule.has_value()) { -// continue; -// } -// -// const auto instanceRegisterGroupIt = portModule->registerGroupsMappedByName.find(instanceName); -// if (instanceRegisterGroupIt != portModule->registerGroupsMappedByName.end()) { -// // We have register information for this port -// const auto& registerGroup = instanceRegisterGroupIt->second; -// -// for (const auto& [registerName, portRegister] : registerGroup.registersMappedByName) { -// if (registerName.find("port") == 0) { -// // This is the data register for the port -// padDescriptor.gpioPortAddress = portRegister.offset; -// continue; -// } -// -// if (registerName.find("pin") == 0) { -// // This is the input data register for the port -// padDescriptor.gpioPortInputAddress = portRegister.offset; -// continue; -// } -// -// if (registerName.find("ddr") == 0) { -// // This is the data direction register for the port -// padDescriptor.gpioDdrAddress = portRegister.offset; -// continue; -// } -// } -// -// continue; -// } -// -// const auto portRegisterGroupIt = portModule->registerGroupsMappedByName.find("port"); -// if (portRegisterGroupIt != portModule->registerGroupsMappedByName.end()) { -// // We have generic register information for all ports on the target -// const auto& registerGroup = portRegisterGroupIt->second; -// -// for (const auto& [registerName, portRegister] : registerGroup.registersMappedByName) { -// if (registerName == "out") { -// // Include the port register offset -// padDescriptor.gpioPortAddress = portRegisterAddressOffset + portRegister.offset; -// continue; -// } -// -// if (registerName == "dir") { -// padDescriptor.gpioDdrAddress = portRegisterAddressOffset + portRegister.offset; -// continue; -// } -// -// if (registerName == "in") { -// padDescriptor.gpioPortInputAddress = portRegisterAddressOffset + portRegister.offset; -// continue; -// } -// } -// -// continue; -// } -// } -// } - } - - void TargetDescriptionFile::loadTargetVariants() { -// for (const auto& tdVariant : this->variants) { -// if (tdVariant.disabled) { -// continue; -// } -// -// auto targetVariant = TargetVariant(); -// targetVariant.id = static_cast(this->targetVariantsById.size()); -// targetVariant.name = tdVariant.name; -// targetVariant.packageName = tdVariant.package; -// -// if (tdVariant.package.find("QFP") == 0 || tdVariant.package.find("TQFP") == 0) { -// targetVariant.package = TargetPackage::QFP; -// -// } else if (tdVariant.package.find("PDIP") == 0 || tdVariant.package.find("DIP") == 0) { -// targetVariant.package = TargetPackage::DIP; -// -// } else if (tdVariant.package.find("QFN") == 0 || tdVariant.package.find("VQFN") == 0) { -// targetVariant.package = TargetPackage::QFN; -// -// } else if (tdVariant.package.find("SOIC") == 0) { -// targetVariant.package = TargetPackage::SOIC; -// -// } else if (tdVariant.package.find("SSOP") == 0) { -// targetVariant.package = TargetPackage::SSOP; -// } -// -// const auto tdPinoutIt = this->pinoutsMappedByName.find(tdVariant.pinoutName); -// if (tdPinoutIt == this->pinoutsMappedByName.end()) { -// // Missing pinouts in the target description file -// continue; -// } -// -// const auto& tdPinout = tdPinoutIt->second; -// for (const auto& tdPin : tdPinout.pins) { -// auto targetPin = TargetPinDescriptor(); -// targetPin.name = tdPin.pad; -// targetPin.padName = tdPin.pad; -// targetPin.number = tdPin.position; -// targetPin.variantId = targetVariant.id; -// -// // TODO: REMOVE THIS: -// if ( -// tdPin.pad.find("vcc") == 0 -// || tdPin.pad.find("avcc") == 0 -// || tdPin.pad.find("aref") == 0 -// || tdPin.pad.find("avdd") == 0 -// || tdPin.pad.find("vdd") == 0 -// ) { -// targetPin.type = TargetPinType::VCC; -// -// } else if (tdPin.pad.find("gnd") == 0) { -// targetPin.type = TargetPinType::GND; -// } -// -// const auto padIt = this->padDescriptorsByName.find(targetPin.padName); -// if (padIt != this->padDescriptorsByName.end()) { -// const auto& pad = padIt->second; -// if (pad.gpioPortAddress.has_value() && pad.gpioDdrAddress.has_value()) { -// targetPin.type = TargetPinType::GPIO; -// } -// } -// -// targetVariant.pinDescriptorsByNumber.insert(std::pair(targetPin.number, targetPin)); -// } -// -// this->targetVariantsById.insert(std::pair(targetVariant.id, targetVariant)); -// } - } - - void TargetDescriptionFile::loadTargetRegisterDescriptors() { -// for (const auto& [moduleName, module] : this->modulesMappedByName) { -// for (const auto& [registerGroupName, registerGroup] : module.registerGroupsMappedByName) { -// const auto peripheralRegisterGroupsIt = this->peripheralRegisterGroupsMappedByModuleRegisterGroupName.find( -// registerGroupName -// ); -// -// if (peripheralRegisterGroupsIt != this->peripheralRegisterGroupsMappedByModuleRegisterGroupName.end()) { -// const auto& peripheralRegisterGroups = peripheralRegisterGroupsIt->second; -// -// for (const auto& peripheralRegisterGroup : peripheralRegisterGroups) { -// if (peripheralRegisterGroup.addressSpaceId.value_or("") != "data") { -// // Currently, we only deal with registers in the data address space. -// continue; -// } -// -// for (const auto& [moduleRegisterName, moduleRegister] : registerGroup.registersMappedByName) { -// if (moduleRegister.size < 1) { -// continue; -// } -// -// auto registerDescriptor = TargetRegisterDescriptor( -// moduleName == "port" ? TargetRegisterType::PORT_REGISTER : TargetRegisterType::OTHER, -// moduleRegister.offset + peripheralRegisterGroup.offset.value_or(0), -// moduleRegister.size, -// TargetMemoryType::RAM, -// moduleRegisterName, -// peripheralRegisterGroup.name, -// moduleRegister.caption.has_value() && !moduleRegister.caption->empty() -// ? moduleRegister.caption -// : std::nullopt, -// moduleRegister.readWriteAccess.has_value() -// ? TargetRegisterAccess( -// moduleRegister.readWriteAccess.value().find('r') != std::string::npos, -// moduleRegister.readWriteAccess.value().find('w') != std::string::npos -// ) -// : TargetRegisterAccess(true, true) -// ); -// -// this->targetRegisterDescriptorsById.emplace( -// registerDescriptor.id, -// std::move(registerDescriptor) -// ); -// } -// } -// } -// } -// } - } - - Targets::TargetMemoryAddress TargetDescriptionFile::getPeripheralModuleRegisterAddressOffset( - const std::string& moduleName, - const std::string& instanceName, - const std::string& registerGroupName - ) const { - Targets::TargetMemoryAddress addressOffset = 0; - -// const auto peripheralModuleIt = this->peripheralModulesMappedByName.find(moduleName); -// if (peripheralModuleIt != this->peripheralModulesMappedByName.end()) { -// const auto& peripheralModule = peripheralModuleIt->second; -// -//// const auto instanceIt = peripheralModule.instancesMappedByName.find(instanceName); -//// if (instanceIt != peripheralModule.instancesMappedByName.end()) { -//// const auto& instance = instanceIt->second; -//// -//// const auto registerGroupIt = instance.registerGroupsMappedByName.find(registerGroupName); -//// if (registerGroupIt != instance.registerGroupsMappedByName.end()) { -//// addressOffset = registerGroupIt->second.offset.value_or(0); -//// } -//// } -// } - - return addressOffset; - } - - std::optional TargetDescriptionFile::getFuseBitsDescriptorByName( - const std::string& fuseBitName - ) const { - static const auto fuseTypesByName = std::map({ - {"low", FuseType::LOW}, - {"high", FuseType::HIGH}, - {"extended", FuseType::EXTENDED}, - }); - -// const auto fuseModuleIt = this->modulesMappedByName.find("fuse"); -// -// if (fuseModuleIt != this->modulesMappedByName.end()) { -// const auto& fuseModule = fuseModuleIt->second; -// auto fuseRegisterGroupIt = fuseModule.registerGroupsMappedByName.find("fuse"); -// -// if (fuseRegisterGroupIt == fuseModule.registerGroupsMappedByName.end()) { -// // Try the NVM_FUSES register group -// fuseRegisterGroupIt = fuseModule.registerGroupsMappedByName.find("nvm_fuses"); -// } -// -// if (fuseRegisterGroupIt != fuseModule.registerGroupsMappedByName.end()) { -// const auto& fuseRegisterGroup = fuseRegisterGroupIt->second; -// -// for (const auto& [fuseTypeName, fuse] : fuseRegisterGroup.registersMappedByName) { -// const auto fuseBitFieldIt = fuse.bitFieldsMappedByName.find(fuseBitName); -// -// if (fuseBitFieldIt != fuse.bitFieldsMappedByName.end()) { -// const auto fuseTypeIt = fuseTypesByName.find(fuseTypeName); -// -// return FuseBitsDescriptor( -// this->getPeripheralModuleRegisterAddressOffset("fuse", "fuse", "fuse") + fuse.offset, -// fuseTypeIt != fuseTypesByName.end() ? fuseTypeIt->second : FuseType::OTHER, -// fuseBitFieldIt->second.mask -// ); -// } -// } -// } -// } -// -// // Try the NVM module -// const auto nvmModuleIt = this->modulesMappedByName.find("nvm"); -// -// if (nvmModuleIt != this->modulesMappedByName.end()) { -// const auto& nvmModule = nvmModuleIt->second; -// const auto fuseRegisterGroupIt = nvmModule.registerGroupsMappedByName.find("nvm_fuses"); -// -// if (fuseRegisterGroupIt != nvmModule.registerGroupsMappedByName.end()) { -// const auto& fuseRegisterGroup = fuseRegisterGroupIt->second; -// -// for (const auto& [fuseTypeName, fuse] : fuseRegisterGroup.registersMappedByName) { -// const auto fuseBitFieldIt = fuse.bitFieldsMappedByName.find(fuseBitName); -// -// if (fuseBitFieldIt != fuse.bitFieldsMappedByName.end()) { -// const auto fuseTypeIt = fuseTypesByName.find(fuseTypeName); -// -// return FuseBitsDescriptor( -// this->getPeripheralModuleRegisterAddressOffset("nvm", "fuse", "fuse") + fuse.offset, -// fuseTypeIt != fuseTypesByName.end() ? fuseTypeIt->second : FuseType::OTHER, -// fuseBitFieldIt->second.mask -// ); -// } -// } -// } -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getProgramMemoryAddressSpace() const { -// const auto programAddressSpaceIt = this->addressSpacesMappedByKey.find("prog"); -// -// if (programAddressSpaceIt != this->addressSpacesMappedByKey.end()) { -// return programAddressSpaceIt->second; -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getFlashApplicationMemorySegment( - const AddressSpace& programAddressSpace - ) const { -// const auto& programMemorySegments = programAddressSpace.memorySegmentsByTypeAndName; -// -// const auto flashMemorySegmentsIt = programMemorySegments.find(MemorySegmentType::FLASH); -// if (flashMemorySegmentsIt != programMemorySegments.end()) { -// const auto& flashMemorySegments = flashMemorySegmentsIt->second; -// -// /* -// * In AVR8 TDFs, flash application memory segments are typically named "APP_SECTION", "PROGMEM" or -// * "FLASH". -// */ -// const auto appSectionSegmentIt = flashMemorySegments.find("app_section"); -// if (appSectionSegmentIt != flashMemorySegments.end()) { -// return appSectionSegmentIt->second; -// } -// -// const auto programMemSegmentIt = flashMemorySegments.find("progmem"); -// if (programMemSegmentIt != flashMemorySegments.end()) { -// return programMemSegmentIt->second; -// } -// -// const auto flashSegmentIt = flashMemorySegments.find("flash"); -// if (flashSegmentIt != flashMemorySegments.end()) { -// return flashSegmentIt->second; -// } -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getRegisterMemorySegment() const { -// const auto& addressMapping = this->addressSpacesMappedByKey; -// -// // Internal RAM ®ister attributes are usually found in the data address space -// auto dataAddressSpaceIt = addressMapping.find("data"); -// -// if (dataAddressSpaceIt != addressMapping.end()) { -// const auto& dataAddressSpace = dataAddressSpaceIt->second; -// const auto& dataMemorySegments = dataAddressSpace.memorySegmentsByTypeAndName; -// -// if (dataMemorySegments.find(MemorySegmentType::REGISTERS) != dataMemorySegments.end()) { -// const auto& registerMemorySegments = dataMemorySegments.find(MemorySegmentType::REGISTERS)->second; -// auto registerMemorySegmentIt = registerMemorySegments.begin(); -// -// if (registerMemorySegmentIt != registerMemorySegments.end()) { -// return registerMemorySegmentIt->second; -// } -// } -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getFirstBootSectionMemorySegment() const { -// const auto programAddressSpaceIt = this->addressSpacesMappedByKey.find("prog"); -// -// if (programAddressSpaceIt != this->addressSpacesMappedByKey.end()) { -// const auto& programAddressSpace = programAddressSpaceIt->second; -// const auto& programMemorySegments = programAddressSpace.memorySegmentsByTypeAndName; -// -// const auto flashMemorySegmentsit = programMemorySegments.find(MemorySegmentType::FLASH); -// -// if (flashMemorySegmentsit != programMemorySegments.end()) { -// const auto& flashMemorySegments = flashMemorySegmentsit->second; -// -// auto bootSectionSegmentIt = flashMemorySegments.find("boot_section_1"); -// if (bootSectionSegmentIt != flashMemorySegments.end()) { -// return bootSectionSegmentIt->second; -// } -// -// bootSectionSegmentIt = flashMemorySegments.find("boot_section"); -// if (bootSectionSegmentIt != flashMemorySegments.end()) { -// return bootSectionSegmentIt->second; -// } -// } -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getCpuRegisterGroup() const { -// const auto& modulesByName = this->modulesMappedByName; -// const auto cpuModuleIt = modulesByName.find("cpu"); - -// if (cpuModuleIt != modulesByName.end()) { -// const auto& cpuModule = cpuModuleIt->second; -// const auto cpuRegisterGroupIt = cpuModule.registerGroupsMappedByName.find("cpu"); -// -// if (cpuRegisterGroupIt != cpuModule.registerGroupsMappedByName.end()) { -// return cpuRegisterGroupIt->second; -// } -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getBootLoadRegisterGroup() const { -// const auto bootLoadModuleIt = this->modulesMappedByName.find("boot_load"); - -// if (bootLoadModuleIt != this->modulesMappedByName.end()) { -// const auto& bootLoadModule = bootLoadModuleIt->second; -// auto bootLoadRegisterGroupIt = bootLoadModule.registerGroupsMappedByName.find("boot_load"); -// -// if (bootLoadRegisterGroupIt != bootLoadModule.registerGroupsMappedByName.end()) { -// return bootLoadRegisterGroupIt->second; -// } -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getEepromRegisterGroup() const { -// const auto& modulesByName = this->modulesMappedByName; - -// if (modulesByName.find("eeprom") != modulesByName.end()) { -// auto eepromModule = modulesByName.find("eeprom")->second; -// auto eepromRegisterGroupIt = eepromModule.registerGroupsMappedByName.find("eeprom"); -// -// if (eepromRegisterGroupIt != eepromModule.registerGroupsMappedByName.end()) { -// return eepromRegisterGroupIt->second; -// } -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getStatusRegister() const { - auto cpuRegisterGroup = this->getCpuRegisterGroup(); - -// if (cpuRegisterGroup.has_value()) { -// auto statusRegisterIt = cpuRegisterGroup->registersMappedByName.find("sreg"); -// -// if (statusRegisterIt != cpuRegisterGroup->registersMappedByName.end()) { -// return statusRegisterIt->second; -// } -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getStackPointerRegister() const { - auto cpuRegisterGroup = this->getCpuRegisterGroup(); - -// if (cpuRegisterGroup.has_value()) { -// auto stackPointerRegisterIt = cpuRegisterGroup->registersMappedByName.find("sp"); -// -// if (stackPointerRegisterIt != cpuRegisterGroup->registersMappedByName.end()) { -// return stackPointerRegisterIt->second; -// } -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getStackPointerHighRegister() const { - auto cpuRegisterGroup = this->getCpuRegisterGroup(); - -// if (cpuRegisterGroup.has_value()) { -// auto stackPointerHighRegisterIt = cpuRegisterGroup->registersMappedByName.find("sph"); -// -// if (stackPointerHighRegisterIt != cpuRegisterGroup->registersMappedByName.end()) { -// return stackPointerHighRegisterIt->second; -// } -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getStackPointerLowRegister() const { - auto cpuRegisterGroup = this->getCpuRegisterGroup(); - -// if (cpuRegisterGroup.has_value()) { -// auto stackPointerLowRegisterIt = cpuRegisterGroup->registersMappedByName.find("spl"); -// -// if (stackPointerLowRegisterIt != cpuRegisterGroup->registersMappedByName.end()) { -// return stackPointerLowRegisterIt->second; -// } -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getOscillatorCalibrationRegister() const { - auto cpuRegisterGroup = this->getCpuRegisterGroup(); - -// if (cpuRegisterGroup.has_value()) { -// const auto& cpuRegisters = cpuRegisterGroup->registersMappedByName; -// -// auto osccalRegisterIt = cpuRegisters.find("osccal"); -// if (osccalRegisterIt != cpuRegisters.end()) { -// return osccalRegisterIt->second; -// } -// -// osccalRegisterIt = cpuRegisters.find("osccal0"); -// if (osccalRegisterIt != cpuRegisters.end()) { -// return osccalRegisterIt->second; -// } -// -// osccalRegisterIt = cpuRegisters.find("osccal1"); -// if (osccalRegisterIt != cpuRegisters.end()) { -// return osccalRegisterIt->second; -// } -// -// osccalRegisterIt = cpuRegisters.find("fosccal"); -// if (osccalRegisterIt != cpuRegisters.end()) { -// return osccalRegisterIt->second; -// } -// -// osccalRegisterIt = cpuRegisters.find("sosccala"); -// if (osccalRegisterIt != cpuRegisters.end()) { -// return osccalRegisterIt->second; -// } -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getSpmcsRegister() const { - const auto cpuRegisterGroup = this->getCpuRegisterGroup(); - -// if (cpuRegisterGroup.has_value()) { -// const auto spmcsRegisterIt = cpuRegisterGroup->registersMappedByName.find("spmcsr"); -// -// if (spmcsRegisterIt != cpuRegisterGroup->registersMappedByName.end()) { -// return spmcsRegisterIt->second; -// } -// } -// -// const auto bootLoadRegisterGroup = this->getBootLoadRegisterGroup(); -// -// if (bootLoadRegisterGroup.has_value()) { -// const auto spmcsRegisterIt = bootLoadRegisterGroup->registersMappedByName.find("spmcsr"); -// -// if (spmcsRegisterIt != bootLoadRegisterGroup->registersMappedByName.end()) { -// return spmcsRegisterIt->second; -// } -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getSpmcRegister() const { - const auto cpuRegisterGroup = this->getCpuRegisterGroup(); - -// if (cpuRegisterGroup.has_value()) { -// const auto spmcRegisterIt = cpuRegisterGroup->registersMappedByName.find("spmcr"); -// -// if (spmcRegisterIt != cpuRegisterGroup->registersMappedByName.end()) { -// return spmcRegisterIt->second; -// } -// } -// -// const auto bootLoadRegisterGroup = this->getBootLoadRegisterGroup(); -// -// if (bootLoadRegisterGroup.has_value()) { -// const auto spmcRegisterIt = bootLoadRegisterGroup->registersMappedByName.find("spmcr"); -// -// if (spmcRegisterIt != bootLoadRegisterGroup->registersMappedByName.end()) { -// return spmcRegisterIt->second; -// } -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getEepromAddressRegister() const { - auto eepromRegisterGroup = this->getEepromRegisterGroup(); - -// if (eepromRegisterGroup.has_value()) { -// auto eepromAddressRegisterIt = eepromRegisterGroup->registersMappedByName.find("eear"); -// -// if (eepromAddressRegisterIt != eepromRegisterGroup->registersMappedByName.end()) { -// return eepromAddressRegisterIt->second; -// } -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getEepromAddressLowRegister() const { - auto eepromRegisterGroup = this->getEepromRegisterGroup(); - -// if (eepromRegisterGroup.has_value()) { -// auto eepromAddressRegisterIt = eepromRegisterGroup->registersMappedByName.find("eearl"); -// -// if (eepromAddressRegisterIt != eepromRegisterGroup->registersMappedByName.end()) { -// return eepromAddressRegisterIt->second; -// } -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getEepromAddressHighRegister() const { - auto eepromRegisterGroup = this->getEepromRegisterGroup(); - -// if (eepromRegisterGroup.has_value()) { -// auto eepromAddressRegisterIt = eepromRegisterGroup->registersMappedByName.find("eearh"); -// -// if (eepromAddressRegisterIt != eepromRegisterGroup->registersMappedByName.end()) { -// return eepromAddressRegisterIt->second; -// } -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getEepromDataRegister() const { - auto eepromRegisterGroup = this->getEepromRegisterGroup(); - -// if (eepromRegisterGroup.has_value()) { -// auto eepromDataRegisterIt = eepromRegisterGroup->registersMappedByName.find("eedr"); -// -// if (eepromDataRegisterIt != eepromRegisterGroup->registersMappedByName.end()) { -// return eepromDataRegisterIt->second; -// } -// } - - return std::nullopt; - } - - std::optional TargetDescriptionFile::getEepromControlRegister() const { - auto eepromRegisterGroup = this->getEepromRegisterGroup(); - -// if (eepromRegisterGroup.has_value()) { -// auto eepromControlRegisterIt = eepromRegisterGroup->registersMappedByName.find("eecr"); -// -// if (eepromControlRegisterIt != eepromRegisterGroup->registersMappedByName.end()) { -// return eepromControlRegisterIt->second; -// } -// } - - return std::nullopt; - } - - void TargetDescriptionFile::loadDebugWireAndJtagTargetParameters(TargetParameters& targetParameters) const { - // OCD attributes can be found in property groups - const auto& ocdPropertyGroup = this->getPropertyGroup("ocd"); - targetParameters.ocdRevision = StringService::toUint8(ocdPropertyGroup.getProperty("ocd_revision").value); - targetParameters.ocdDataRegister = StringService::toUint8(ocdPropertyGroup.getProperty("ocd_datareg").value); -// const auto ocdPropertyGroupIt = this->propertyGroupsMappedByKey.find("ocd"); -// if (ocdPropertyGroupIt != this->propertyGroupsMappedByKey.end()) { -// const auto& ocdProperties = ocdPropertyGroupIt->second.propertiesMappedByKey; -// -// const auto ocdRevisionPropertyIt = ocdProperties.find("ocd_revision"); -// if (ocdRevisionPropertyIt != ocdProperties.end()) { -// targetParameters.ocdRevision = ocdRevisionPropertyIt->second.value.toUShort(nullptr, 10); -// } -// -// const auto ocdDataRegPropertyIt = ocdProperties.find("ocd_datareg"); -// if (ocdDataRegPropertyIt != ocdProperties.end()) { -// targetParameters.ocdDataRegister = ocdDataRegPropertyIt->second.value.toUShort(nullptr, 16); -// } -// } - - const auto spmcsRegister = this->getSpmcsRegister(); - if (spmcsRegister.has_value()) { - targetParameters.spmcRegisterStartAddress = spmcsRegister->offset; - - } else { - const auto spmcRegister = this->getSpmcRegister(); - if (spmcRegister.has_value()) { - targetParameters.spmcRegisterStartAddress = spmcRegister->offset; - } - } - - const auto osccalRegister = this->getOscillatorCalibrationRegister(); - if (osccalRegister.has_value()) { - targetParameters.osccalAddress = osccalRegister->offset; - } - - const auto eepromAddressRegister = this->getEepromAddressRegister(); - if (eepromAddressRegister.has_value()) { - targetParameters.eepromAddressRegisterLow = eepromAddressRegister->offset; - targetParameters.eepromAddressRegisterHigh = (eepromAddressRegister->size == 2) - ? eepromAddressRegister->offset + 1 : eepromAddressRegister->offset; - - } else { - const auto eepromAddressLowRegister = this->getEepromAddressLowRegister(); - if (eepromAddressLowRegister.has_value()) { - targetParameters.eepromAddressRegisterLow = eepromAddressLowRegister->offset; - auto eepromAddressHighRegister = this->getEepromAddressHighRegister(); - - if (eepromAddressHighRegister.has_value()) { - targetParameters.eepromAddressRegisterHigh = eepromAddressHighRegister->offset; - - } else { - targetParameters.eepromAddressRegisterHigh = eepromAddressLowRegister->offset; - } - } - } - - const auto eepromDataRegister = this->getEepromDataRegister(); - if (eepromDataRegister.has_value()) { - targetParameters.eepromDataRegisterAddress = eepromDataRegister->offset; - } - - const auto eepromControlRegister = this->getEepromControlRegister(); - if (eepromControlRegister.has_value()) { - targetParameters.eepromControlRegisterAddress = eepromControlRegister->offset; - } - } - - void TargetDescriptionFile::loadPdiTargetParameters(TargetParameters& targetParameters) const { - const auto pdiPropertyGroupIt = this->propertyGroupsByKey.find("pdi_interface"); - if (pdiPropertyGroupIt == this->propertyGroupsByKey.end()) { - return; - } - - const auto& pdiInterfaceProperties = pdiPropertyGroupIt->second.propertiesByKey; - -// const auto appOffsetPropertyIt = pdiInterfaceProperties.find("app_section_offset"); -// if (appOffsetPropertyIt != pdiInterfaceProperties.end()) { -// targetParameters.appSectionPdiOffset = appOffsetPropertyIt->second.value.toUInt(nullptr, 16); -// } -// -// const auto bootOffsetPropertyIt = pdiInterfaceProperties.find("boot_section_offset"); -// if (bootOffsetPropertyIt != pdiInterfaceProperties.end()) { -// targetParameters.bootSectionPdiOffset = bootOffsetPropertyIt->second.value.toUInt(nullptr, 16); -// } -// -// const auto dataOffsetPropertyIt = pdiInterfaceProperties.find("datamem_offset"); -// if (dataOffsetPropertyIt != pdiInterfaceProperties.end()) { -// targetParameters.ramPdiOffset = dataOffsetPropertyIt->second.value.toUInt(nullptr, 16); -// } -// -// const auto eepromOffsetPropertyIt = pdiInterfaceProperties.find("eeprom_offset"); -// if (eepromOffsetPropertyIt != pdiInterfaceProperties.end()) { -// targetParameters.eepromPdiOffset = eepromOffsetPropertyIt->second.value.toUInt(nullptr, 16); -// } -// -// const auto userSigOffsetPropertyIt = pdiInterfaceProperties.find("user_signatures_offset"); -// if (userSigOffsetPropertyIt != pdiInterfaceProperties.end()) { -// targetParameters.userSignaturesPdiOffset = userSigOffsetPropertyIt->second.value.toUInt(nullptr, 16); -// } -// -// const auto prodSigOffsetPropertyIt = pdiInterfaceProperties.find("prod_signatures_offset"); -// if (prodSigOffsetPropertyIt != pdiInterfaceProperties.end()) { -// targetParameters.productSignaturesPdiOffset = prodSigOffsetPropertyIt->second.value.toUInt(nullptr, 16); -// } -// -// const auto fuseRegOffsetPropertyIt = pdiInterfaceProperties.find("fuse_registers_offset"); -// if (fuseRegOffsetPropertyIt != pdiInterfaceProperties.end()) { -// targetParameters.fuseRegistersPdiOffset = fuseRegOffsetPropertyIt->second.value.toUInt(nullptr, 16); -// } -// -// const auto lockRegOffsetPropertyIt = pdiInterfaceProperties.find("lock_registers_offset"); -// if (lockRegOffsetPropertyIt != pdiInterfaceProperties.end()) { -// targetParameters.lockRegistersPdiOffset = lockRegOffsetPropertyIt->second.value.toUInt(nullptr, 16); -// } - - targetParameters.nvmModuleBaseAddress = this->getPeripheralModuleRegisterAddressOffset("nvm", "nvm", "nvm"); - - // TODO: Remove the mcuModuleBaseAddress param - we're not supposed to be using this. We're supposed to using - // the PDI signature offset, which I have since added to the TDFs (see "pdi_interface.signature_offset" property) - targetParameters.mcuModuleBaseAddress = this->getPeripheralModuleRegisterAddressOffset("mcu", "mcu", "mcu"); - } - - void TargetDescriptionFile::loadUpdiTargetParameters(TargetParameters& targetParameters) const { - targetParameters.nvmModuleBaseAddress = this->getPeripheralModuleRegisterAddressOffset( - "nvmctrl", - "nvmctrl", - "nvmctrl" - ); - - const auto updiPropertyGroupIt = this->propertyGroupsByKey.find("updi_interface"); - if (updiPropertyGroupIt != this->propertyGroupsByKey.end()) { - const auto& updiInterfaceProperties = updiPropertyGroupIt->second.propertiesByKey; - -// const auto ocdBaseAddressPropertyIt = updiInterfaceProperties.find("ocd_base_addr"); -// if (ocdBaseAddressPropertyIt != updiInterfaceProperties.end()) { -// targetParameters.ocdModuleAddress = ocdBaseAddressPropertyIt->second.value.toUShort(nullptr, 16); -// } -// -// const auto progMemOffsetPropertyIt = updiInterfaceProperties.find("progmem_offset"); -// if (progMemOffsetPropertyIt != updiInterfaceProperties.end()) { -// targetParameters.programMemoryUpdiStartAddress = progMemOffsetPropertyIt->second.value.toUInt( -// nullptr, -// 16 -// ); -// } - } - - const auto signatureMemorySegment = this->getSignatureMemorySegment(); - if (signatureMemorySegment.has_value()) { - targetParameters.signatureSegmentStartAddress = signatureMemorySegment->startAddress; - targetParameters.signatureSegmentSize = signatureMemorySegment->size; - } - - const auto fuseMemorySegment = this->getFuseMemorySegment(); - if (fuseMemorySegment.has_value()) { - targetParameters.fuseSegmentStartAddress = fuseMemorySegment->startAddress; - targetParameters.fuseSegmentSize = fuseMemorySegment->size; - } - - const auto lockbitsMemorySegment = this->getLockbitsMemorySegment(); - if (lockbitsMemorySegment.has_value()) { - targetParameters.lockbitsSegmentStartAddress = lockbitsMemorySegment->startAddress; - } - } -} diff --git a/src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.hpp b/src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.hpp deleted file mode 100644 index 97d334ba..00000000 --- a/src/Targets/Microchip/AVR/AVR8/TargetDescriptionFile.hpp +++ /dev/null @@ -1,282 +0,0 @@ -#pragma once - -#include -#include - -#include "src/Targets/TargetDescription/TargetDescriptionFile.hpp" - -#include "src/Targets/TargetVariant.hpp" -#include "src/Targets/TargetRegisterDescriptor.hpp" - -#include "src/Targets/Microchip/AVR/TargetSignature.hpp" -#include "src/Targets/Microchip/AVR/IspParameters.hpp" -#include "src/Targets/Microchip/AVR/Fuse.hpp" - -#include "src/Targets/Microchip/AVR/AVR8/Family.hpp" -#include "src/Targets/Microchip/AVR/AVR8/TargetParameters.hpp" -#include "src/Targets/Microchip/AVR/AVR8/PadDescriptor.hpp" - -namespace Targets::Microchip::Avr::Avr8Bit -{ - /** - * Represents an AVR8 TDF. See the Targets::TargetDescription::TargetDescriptionFile close for more on TDFs. - * - * During the build process, we generate a JSON file containing a mapping of AVR8 target signatures to target - * description file paths. Bloom uses this mapping to find a particular target description file, for AVR8 targets, - * given a target signature. See directory "build/resources/TargetDescriptionFiles". - * The generation of the JSON mapping, is done by a PHP script: - * "build/scripts/CopyAvrPartFilesAndCreateMapping.php". This script is invoked via a custom command, at build time. - * - * For more information of TDFs, see src/Targets/TargetDescription/README.md - */ - class TargetDescriptionFile: public Targets::TargetDescription::TargetDescriptionFile - { - public: - /** - * Extends TDF initialisation to include the loading of physical interfaces for debugging AVR8 targets, among - * other things. - * - * @param xml - */ - explicit TargetDescriptionFile(const std::string& xmlFilePath); - - /** - * Extracts the AVR8 target signature from the TDF. - * - * @return - */ - [[nodiscard]] TargetSignature getTargetSignature() const; - - /** - * Extracts the AVR8 target family from the TDF. - * - * @return - */ - [[nodiscard]] Family getAvrFamily() const; - - [[nodiscard]] const Targets::TargetDescription::AddressSpace& getProgramAddressSpace() const; - [[nodiscard]] const Targets::TargetDescription::AddressSpace& getRamAddressSpace() const; - [[nodiscard]] const Targets::TargetDescription::AddressSpace& getEepromAddressSpace() const; - [[nodiscard]] const Targets::TargetDescription::AddressSpace& getIoAddressSpace() const; - [[nodiscard]] const Targets::TargetDescription::AddressSpace& getSignatureAddressSpace() const; - [[nodiscard]] const Targets::TargetDescription::AddressSpace& getFuseAddressSpace() const; - [[nodiscard]] const Targets::TargetDescription::AddressSpace& getLockbitAddressSpace() const; - - [[nodiscard]] const Targets::TargetDescription::MemorySegment& getProgramMemorySegment() const; - [[nodiscard]] const Targets::TargetDescription::MemorySegment& getRamMemorySegment() const; - [[nodiscard]] const Targets::TargetDescription::MemorySegment& getEepromMemorySegment() const; - [[nodiscard]] const Targets::TargetDescription::MemorySegment& getIoMemorySegment() const; - [[nodiscard]] const Targets::TargetDescription::MemorySegment& getSignatureMemorySegment() const; - [[nodiscard]] const Targets::TargetDescription::MemorySegment& getFuseMemorySegment() const; - [[nodiscard]] const Targets::TargetDescription::MemorySegment& getLockbitMemorySegment() const; - - /** - * Constructs an instance of TargetParameters, for the AVR8 target, with data from the TDF. - * - * @return - */ - [[nodiscard]] TargetParameters getTargetParameters() const; - - /** - * Extracts the target's ISP parameters from the TDF. - * - * @return - */ - [[nodiscard]] IspParameters getIspParameters() const; - - /** - * Extracts the target's fuse enable strategy. - * - * @return - * std::nullopt if the TDF doesn't contain a fuse enable strategy. - */ - [[nodiscard]] std::optional getFuseEnableStrategy() const; - - /** - * Constructs a FuseBitDescriptor for the debugWire enable (DWEN) fuse bit, with information extracted from - * the TDF. - * - * @return - * std::nullopt if the DWEN bit field could not be found in the TDF. - */ - [[nodiscard]] std::optional getDwenFuseBitsDescriptor() const; - - /** - * Constructs a FuseBitDescriptor for the SPI enable (SPIEN) fuse bit, with information extracted from - * the TDF. - * - * @return - * std::nullopt if the SPIEN bit field could not be found in the TDF. - */ - [[nodiscard]] std::optional getSpienFuseBitsDescriptor() const; - - /** - * Constructs a FuseBitDescriptor for the OCD enable (OCDEN) fuse bit, with information extracted from - * the TDF. - * - * @return - * std::nullopt if the OCDEN bit field could not be found in the TDF. - */ - [[nodiscard]] std::optional getOcdenFuseBitsDescriptor() const; - - /** - * Constructs a FuseBitDescriptor for the JTAG enable (JTAGEN) fuse bit, with information extracted from - * the TDF. - * - * @return - * std::nullopt if the JTAGEN bit field could not be found in the TDF. - */ - [[nodiscard]] std::optional getJtagenFuseBitsDescriptor() const; - - /** - * Constructs a FuseBitDescriptor for the "Preserve EEPROM" (EESAVE) fuse bit, with information extracted from - * the TDF. - * - * @return - * std::nullopt if the EESAVE bit field could not be found in the TDF. - */ - [[nodiscard]] std::optional getEesaveFuseBitsDescriptor() const; - - /** - * Returns a mapping of all pad descriptors extracted from TDF, mapped by name. - * - * @return - */ - [[nodiscard]] const auto& getPadDescriptorsMappedByName() const { - return this->padDescriptorsByName; - } - - /** - * Returns a mapping of all target variants extracted from the TDF, mapped by ID. - * - * @return - */ - [[nodiscard]] const auto& getVariantsMappedById() const { - return this->targetVariantsById; - } - - /** - * Returns a mapping of all target register descriptors extracted from the TDF, by ID. - * - * @return - */ - [[nodiscard]] const auto& getRegisterDescriptorsMappedById() const { - return this->targetRegisterDescriptorsById; - } - - private: - /**` - * AVR8 target description files include the target family name. This method returns a mapping of part - * description family name strings to Family enum values. - * - * TODO: the difference in AVR8 family variations, like "tinyAVR" and "tinyAVR 2" may require attention. - * - * @return - */ - static inline auto getFamilyNameToEnumMapping() { - // All keys should be lower-case. - return std::map { - {"megaavr", Family::MEGA}, - {"avr mega", Family::MEGA}, - {"avr xmega", Family::XMEGA}, - {"avr tiny", Family::TINY}, - {"tinyavr", Family::TINY}, - {"tinyavr 2", Family::TINY}, - {"avr da", Family::DA}, - {"avr db", Family::DB}, - {"avr dd", Family::DD}, - {"avr ea", Family::EA}, - }; - }; - - std::string avrFamilyName; - - std::map padDescriptorsByName; - std::map targetVariantsById; - - std::map targetRegisterDescriptorsById; - - /** - * Populates this->supportedPhysicalInterfaces with physical interfaces defined in the TDF. - */ - void loadSupportedPhysicalInterfaces(); - - /** - * Generates a collection of PadDescriptor objects from data in the TDF and populates this->padDescriptorsByName. - */ - void loadPadDescriptors(); - - /** - * Loads all variants for the AVR8 target, from the TDF, and populates this->targetVariantsById. - */ - void loadTargetVariants(); - - /** - * Loads all register descriptors from the TDF, and populates this->targetRegisterDescriptorsById. - */ - void loadTargetRegisterDescriptors(); - - /** - * Gets the register address offset for a specific peripheral module. - * - * @param moduleName - * @param instanceName - * @param registerGroupName - * @return - */ - Targets::TargetMemoryAddress getPeripheralModuleRegisterAddressOffset( - const std::string& moduleName, - const std::string& instanceName, - const std::string& registerGroupName - ) const; - - [[nodiscard]] std::optional getFuseBitsDescriptorByName( - const std::string& fuseBitName - ) const; - - [[nodiscard]] std::optional getProgramMemoryAddressSpace() const; - [[nodiscard]] std::optional getFlashApplicationMemorySegment( - const Targets::TargetDescription::AddressSpace& programAddressSpace - ) const; - [[nodiscard]] std::optional getRegisterMemorySegment() const; - [[nodiscard]] std::optional getFirstBootSectionMemorySegment() const; - [[nodiscard]] std::optional getLockbitsMemorySegment() const; - [[nodiscard]] std::optional getCpuRegisterGroup() const; - [[nodiscard]] std::optional getBootLoadRegisterGroup() const; - [[nodiscard]] std::optional getEepromRegisterGroup() const; - - [[nodiscard]] std::optional getStatusRegister() const; - [[nodiscard]] std::optional getStackPointerRegister() const; - [[nodiscard]] std::optional getStackPointerHighRegister() const; - [[nodiscard]] std::optional getStackPointerLowRegister() const; - [[nodiscard]] std::optional getOscillatorCalibrationRegister() const; - [[nodiscard]] std::optional getSpmcsRegister() const; - [[nodiscard]] std::optional getSpmcRegister() const; - [[nodiscard]] std::optional getEepromAddressRegister() const; - [[nodiscard]] std::optional getEepromAddressLowRegister() const; - [[nodiscard]] std::optional getEepromAddressHighRegister() const; - [[nodiscard]] std::optional getEepromDataRegister() const; - [[nodiscard]] std::optional getEepromControlRegister() const; - - /** - * Loads target parameters that are specific to debugWire and mega JTAG sessions. - * - * @param targetParameters - */ - virtual void loadDebugWireAndJtagTargetParameters(TargetParameters& targetParameters) const; - - /** - * Loads target parameters that are specific to PDI sessions. - * - * @param targetParameters - */ - virtual void loadPdiTargetParameters(TargetParameters& targetParameters) const; - - /** - * Loads target parameters that are specific to UPDI sessions. - * - * @param targetParameters - */ - virtual void loadUpdiTargetParameters(TargetParameters& targetParameters) const; - }; -} diff --git a/src/Targets/Microchip/AVR/AVR8/TargetParameters.hpp b/src/Targets/Microchip/AVR/AVR8/TargetParameters.hpp deleted file mode 100644 index 61145441..00000000 --- a/src/Targets/Microchip/AVR/AVR8/TargetParameters.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include -#include - -#include "../TargetSignature.hpp" -#include "Family.hpp" - -namespace Targets::Microchip::Avr::Avr8Bit -{ - /** - * Holds all parameters that would be required for configuring a debug tool, for an AVR8 target. - * - * This can usually be extracted from the AVR8 TDF. - * See Targets::Microchip::Avr::Avr8Bit::TargetDescription::TargetDescriptionFile::getTargetParameters(); - */ - struct TargetParameters - { - std::optional mappedIoSegmentStartAddress; - std::optional mappedIoSegmentSize; - std::optional bootSectionStartAddress; - std::optional gpRegisterStartAddress; - std::optional gpRegisterSize; - std::optional flashPageSize; - std::optional flashSize; - std::optional flashStartAddress; - std::optional ramStartAddress; - std::optional ramSize; - std::optional eepromStartAddress; - std::optional eepromSize; - std::optional eepromPageSize; - std::optional eepromAddressRegisterHigh; - std::optional eepromAddressRegisterLow; - std::optional eepromDataRegisterAddress; - std::optional eepromControlRegisterAddress; - std::optional ocdRevision; - std::optional ocdDataRegister; - std::optional statusRegisterStartAddress; - std::optional statusRegisterSize; - std::optional stackPointerRegisterLowAddress; - std::optional stackPointerRegisterSize; - std::optional spmcRegisterStartAddress; - std::optional osccalAddress; - - // XMega/PDI/UPDI specific target params - std::optional appSectionPdiOffset; - std::optional appSectionStartAddress; - std::optional appSectionSize; - std::optional bootSectionSize; - std::optional bootSectionPdiOffset; - std::optional eepromPdiOffset; - std::optional ramPdiOffset; - std::optional fuseRegistersPdiOffset; - std::optional lockRegistersPdiOffset; - std::optional userSignaturesPdiOffset; - std::optional productSignaturesPdiOffset; - std::optional nvmModuleBaseAddress; - std::optional mcuModuleBaseAddress; - - // UPDI specific target params - std::optional ocdModuleAddress; - std::optional programMemoryUpdiStartAddress; - std::optional signatureSegmentStartAddress; - std::optional signatureSegmentSize; - std::optional fuseSegmentStartAddress; - std::optional fuseSegmentSize; - std::optional lockbitsSegmentStartAddress; - }; -} diff --git a/src/Targets/Microchip/AVR/Fuse.hpp b/src/Targets/Microchip/AVR/Fuse.hpp deleted file mode 100644 index b4c5bd44..00000000 --- a/src/Targets/Microchip/AVR/Fuse.hpp +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include - -#include "src/Targets/TargetMemory.hpp" - -namespace Targets::Microchip::Avr -{ - enum class FuseType: std::uint8_t - { - LOW, - HIGH, - EXTENDED, - OTHER, - }; - - enum class FuseEnableStrategy: std::uint8_t - { - CLEAR, - SET, - }; - - struct Fuse - { - FuseType type; - std::uint8_t value; - - Fuse(FuseType type, std::uint8_t value) - : type(type) - , value(value) - {} - }; - - struct FuseBitsDescriptor - { - TargetMemoryAddress byteAddress; - - /** - * The type of the fuse byte in which the fuse bits resides. - */ - FuseType fuseType; - - /** - * Fuse bits mask - */ - std::uint8_t bitMask; - - FuseBitsDescriptor(TargetMemoryAddress byteAddress, FuseType fuseType, std::uint8_t bitMask) - : byteAddress(byteAddress) - , fuseType(fuseType) - , bitMask(bitMask) - {} - }; -} diff --git a/src/Targets/Microchip/AVR8/Avr8.cpp b/src/Targets/Microchip/AVR8/Avr8.cpp new file mode 100644 index 00000000..e0fe94c1 --- /dev/null +++ b/src/Targets/Microchip/AVR8/Avr8.cpp @@ -0,0 +1,1154 @@ +#include "Avr8.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "IspParameters.hpp" + +#include "src/Logger/Logger.hpp" +#include "src/Services/PathService.hpp" +#include "src/Services/StringService.hpp" + +#include "src/Exceptions/InvalidConfig.hpp" +#include "Exceptions/DebugWirePhysicalInterfaceError.hpp" + +namespace Targets::Microchip::Avr8 +{ + using namespace Exceptions; + + Avr8::Avr8(const TargetConfig& targetConfig, TargetDescriptionFile&& targetDescriptionFile) + : targetConfig(Avr8TargetConfig(targetConfig)) + , targetDescriptionFile(std::move(targetDescriptionFile)) + , dataAddressSpaceDescriptor(this->targetDescriptionFile.getDataAddressSpaceDescriptor()) + , fuseAddressSpaceDescriptor(this->targetDescriptionFile.getFuseAddressSpaceDescriptor()) + , ramMemorySegmentDescriptor(this->targetDescriptionFile.getRamMemorySegmentDescriptor()) + , ioMemorySegmentDescriptor(this->targetDescriptionFile.getIoMemorySegmentDescriptor()) + , fuseMemorySegmentDescriptor(this->targetDescriptionFile.getFuseMemorySegmentDescriptor()) + , signature(this->targetDescriptionFile.getTargetSignature()) + , family(this->targetDescriptionFile.getAvrFamily()) + , physicalInterfaces(this->targetDescriptionFile.getPhysicalInterfaces()) + , gpioPortPeripheralDescriptors(this->targetDescriptionFile.gpioPortPeripheralDescriptors()) + , gpioPadDescriptorsByPadName(Avr8::generateGpioPadDescriptorMapping(this->gpioPortPeripheralDescriptors)) + , fuseEnableStrategy(this->targetDescriptionFile.getFuseEnableStrategy().value_or(FuseEnableStrategy::CLEAR)) + { + const auto cpuPeripheralDescriptor = this->targetDescriptionFile.getTargetPeripheralDescriptor("cpu"); + const auto& cpuRegisterGroup = cpuPeripheralDescriptor.getRegisterGroupDescriptor("cpu"); + + const auto spDescriptor = cpuRegisterGroup.tryGetRegisterDescriptor("sp"); + if (spDescriptor.has_value()) { + this->spRegisterDescriptor.emplace(spDescriptor->get().clone()); + } + + const auto spLowDescriptor = cpuRegisterGroup.tryGetRegisterDescriptor("spl"); + if (spLowDescriptor.has_value()) { + this->spLowRegisterDescriptor.emplace(spLowDescriptor->get().clone()); + } + + const auto spHighDescriptor = cpuRegisterGroup.tryGetRegisterDescriptor("sph"); + if (spHighDescriptor.has_value()) { + this->spHighRegisterDescriptor.emplace(spHighDescriptor->get().clone()); + } + + if (!this->physicalInterfaces.contains(this->targetConfig.physicalInterface)) { + /* + * The user has selected a physical interface that does not appear to be supported by the selected + * target. + * + * Bloom's target description files provide a list of supported physical interfaces for each target + * (which is how this->physicalInterfaces is populated), but it's possible that this list may + * be wrong/incomplete. For this reason, we don't throw an exception here. Instead, we just present the + * user with a warning and a list of physical interfaces known to be supported by their selected target. + * + * If the target truly doesn't'support the physical interface, an exception will be thrown during + * activation. + */ + const auto physicalInterfaceNames = getPhysicalInterfaceNames(); + + const auto supportedPhysicalInterfaceList = std::accumulate( + this->physicalInterfaces.begin(), + this->physicalInterfaces.end(), + std::string{}, + [&physicalInterfaceNames] (const std::string& string, TargetPhysicalInterface physicalInterface) { + if (physicalInterface == TargetPhysicalInterface::ISP) { + /* + * Don't include the ISP interface in the list of supported interfaces, as doing so may + * mislead the user into thinking the ISP interface can be used for debugging operations. + */ + return string; + } + + return string + "\n - " + physicalInterfaceNames.at(physicalInterface); + } + ); + + Logger::warning( + "\nThe selected target does not support the selected physical interface (" + + physicalInterfaceNames.at(this->targetConfig.physicalInterface) + "). Target activation " + "will likely fail. The target supports the following physical interfaces: \n" + + supportedPhysicalInterfaceList + "\n\nFor physical interface configuration values, see " + + Services::PathService::homeDomainName() + "/docs/configuration/target-physical-interfaces." + ); + } + + if ( + this->targetConfig.manageOcdenFuseBit + && this->targetConfig.physicalInterface != TargetPhysicalInterface::JTAG + ) { + Logger::warning( + "The 'manageOcdenFuseBit' parameter only applies to JTAG targets. It will be ignored in this session." + ); + } + } + + bool Avr8::supportsDebugTool(DebugTool* debugTool) { + return debugTool->getAvr8DebugInterface( + this->targetDescriptionFile, + this->targetConfig + ) != nullptr; + } + + void Avr8::setDebugTool(DebugTool* debugTool) { + this->targetPowerManagementInterface = debugTool->getTargetPowerManagementInterface(); + this->avr8DebugInterface = debugTool->getAvr8DebugInterface(this->targetDescriptionFile, this->targetConfig); + + if (this->physicalInterfaces.contains(TargetPhysicalInterface::ISP)) { + this->avrIspInterface = debugTool->getAvrIspInterface(this->targetDescriptionFile, this->targetConfig); + + if ( + this->avrIspInterface == nullptr + && this->targetConfig.manageDwenFuseBit + && this->targetConfig.physicalInterface == TargetPhysicalInterface::DEBUG_WIRE + ) { + Logger::warning( + "The connected debug tool (or associated driver) does not provide any ISP interface. " + "Bloom will be unable to manage the DWEN fuse bit." + ); + } + } + } + + void Avr8::activate() { + if (this->activated) { + return; + } + + this->avr8DebugInterface->init(); + + try { + this->avr8DebugInterface->activate(); + + } catch (const Exceptions::DebugWirePhysicalInterfaceError& debugWireException) { + // We failed to activate the debugWIRE physical interface. DWEN fuse bit may need updating. + + if (!this->targetConfig.manageDwenFuseBit) { + throw TargetOperationFailure{ + "Failed to activate debugWIRE physical interface - check target connection and DWEN fuse " + "bit. Bloom can manage the DWEN fuse bit automatically. For instructions on enabling this " + "function, see " + Services::PathService::homeDomainName() + "/docs/debugging-avr-debugwire" + }; + } + + try { + Logger::warning( + "Failed to activate the debugWIRE physical interface - attempting to access target via " + "the ISP interface, for DWEN fuse bit inspection." + ); + this->updateDwenFuseBit(true); + + // If the debug tool provides a TargetPowerManagementInterface, attempt to cycle the target power + if ( + this->targetPowerManagementInterface != nullptr + && this->targetConfig.cycleTargetPowerPostDwenUpdate + ) { + Logger::info("Cycling target power"); + + Logger::debug("Disabling target power"); + this->targetPowerManagementInterface->disableTargetPower(); + + Logger::debug( + "Holding power off for ~" + std::to_string(this->targetConfig.targetPowerCycleDelay.count()) + + " ms" + ); + std::this_thread::sleep_for(this->targetConfig.targetPowerCycleDelay); + + Logger::debug("Enabling target power"); + this->targetPowerManagementInterface->enableTargetPower(); + + Logger::debug( + "Waiting ~" + std::to_string(this->targetConfig.targetPowerCycleDelay.count()) + + " ms for target power-up" + ); + std::this_thread::sleep_for(this->targetConfig.targetPowerCycleDelay); + } + + } catch (const Exception& exception) { + throw Exception{ + "Failed to access/update DWEN fuse bit via ISP interface - " + exception.getMessage() + }; + } + + Logger::info("Retrying debugWIRE physical interface activation"); + this->avr8DebugInterface->activate(); + } + + this->stop(); + this->reset(); + + if ( + this->targetConfig.physicalInterface == TargetPhysicalInterface::JTAG + && this->targetConfig.manageOcdenFuseBit + ) { + Logger::debug("Attempting OCDEN fuse bit management"); + this->updateOcdenFuseBit(true); + } + + this->activated = true; + + /* + * Validate the target signature. + * + * The signature obtained from the device should match what we loaded from the target description file. + */ + const auto targetSignature = this->avr8DebugInterface->getDeviceId(); + + if (targetSignature != this->signature) { + throw Exception{ + "Failed to validate connected target - target signature mismatch.\nThe target signature" + " (\"" + targetSignature.toHex() + "\") does not match the AVR8 target description signature (\"" + + this->signature.toHex() + "\"). This will likely be due to an incorrect target name in the " + + "configuration file (bloom.yaml)." + }; + } + } + + void Avr8::deactivate() { + try { + if (this->avr8DebugInterface == nullptr) { + return; + } + + this->stop(); + this->clearAllBreakpoints(); + + if ( + this->targetConfig.physicalInterface == TargetPhysicalInterface::JTAG + && this->targetConfig.manageOcdenFuseBit + ) { + Logger::debug("Attempting OCDEN fuse bit management"); + this->updateOcdenFuseBit(false); + + } else { + this->avr8DebugInterface->deactivate(); + } + + this->activated = false; + + } catch (const Exception& exception) { + Logger::error("Failed to deactivate AVR8 target - " + exception.getMessage()); + } + } + + TargetDescriptor Avr8::targetDescriptor() { + auto descriptor = TargetDescriptor{ + this->targetDescriptionFile.getName(), + this->targetDescriptionFile.getFamily(), + this->signature.toHex(), + this->targetDescriptionFile.tryGetVendorName().value_or("Microchip"), + this->targetDescriptionFile.targetAddressSpaceDescriptorsByKey(), + this->targetDescriptionFile.targetPeripheralDescriptorsByKey(), + this->targetDescriptionFile.targetPinoutDescriptorsByKey(), + this->targetDescriptionFile.targetVariantDescriptors(), + this->getBreakpointResources() + }; + + /* + * General purpose CPU registers are not included in AVR8 TDFs, so we manually add them to the target + * descriptor here. + */ + const auto& registerFileAddressSpace = this->targetDescriptionFile.getRegisterFileAddressSpace(); + const auto& registerFileMemorySegment = registerFileAddressSpace.getMemorySegment("gp_registers"); + + auto& gpPeripheral = descriptor.peripheralDescriptorsByKey.emplace( + "cpu_gpr", + TargetPeripheralDescriptor{ + "cpu_gpr", + "CPU General Purpose", + {}, + {} + } + ).first->second; + + auto& gpRegisterGroup = gpPeripheral.registerGroupDescriptorsByKey.emplace( + "gpr", + TargetRegisterGroupDescriptor{ + "gpr", + "CPU General Purpose", + registerFileAddressSpace.key, + std::nullopt, + {}, + {} + } + ).first->second; + + for (auto i = std::uint8_t{0}; i <= 31; ++i) { + const auto key = "r" + std::to_string(i); + gpRegisterGroup.registerDescriptorsByKey.emplace( + key, + TargetRegisterDescriptor{ + key, + "R" + std::to_string(i), + registerFileAddressSpace.key, + registerFileMemorySegment.startAddress + i, + 1, + TargetRegisterType::GENERAL_PURPOSE_REGISTER, + TargetRegisterAccess(true, true), + std::nullopt, + {} + } + ); + } + + /* + * The debug interface may have its own access restrictions for registers. + * + * We must amend the register descriptors with the appropriate restrictions. + */ + for (auto& [peripheralKey, peripheral] : descriptor.peripheralDescriptorsByKey) { + for (auto& [groupKey, registerGroup] : peripheral.registerGroupDescriptorsByKey) { + this->applyDebugInterfaceRegisterAccessRestrictions( + registerGroup, + descriptor.getAddressSpaceDescriptor(registerGroup.addressSpaceKey) + ); + } + } + + return descriptor; + } + + void Avr8::run(std::optional toAddress) { + if (toAddress.has_value()) { + return this->avr8DebugInterface->runTo(*toAddress); + } + + this->avr8DebugInterface->run(); + } + + void Avr8::stop() { + this->avr8DebugInterface->stop(); + } + + void Avr8::step() { + this->avr8DebugInterface->step(); + } + + void Avr8::reset() { + this->avr8DebugInterface->reset(); + } + + void Avr8::setSoftwareBreakpoint(TargetMemoryAddress address) { + this->avr8DebugInterface->setSoftwareBreakpoint(address); + } + + void Avr8::removeSoftwareBreakpoint(TargetMemoryAddress address) { + this->avr8DebugInterface->clearSoftwareBreakpoint(address); + } + + void Avr8::setHardwareBreakpoint(TargetMemoryAddress address) { + this->avr8DebugInterface->setHardwareBreakpoint(address); + } + + void Avr8::removeHardwareBreakpoint(TargetMemoryAddress address) { + this->avr8DebugInterface->clearHardwareBreakpoint(address); + } + + void Avr8::clearAllBreakpoints() { + this->avr8DebugInterface->clearAllBreakpoints(); + } + + TargetRegisterDescriptorAndValuePairs Avr8::readRegisters(const Targets::TargetRegisterDescriptors& descriptors) { + return this->avr8DebugInterface->readRegisters(descriptors); + } + + void Avr8::writeRegisters(const TargetRegisterDescriptorAndValuePairs& registers) { + this->avr8DebugInterface->writeRegisters(registers); + } + + TargetMemoryBuffer Avr8::readMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, + TargetMemoryAddress startAddress, + TargetMemorySize bytes, + const std::set& excludedAddressRanges + ) { + return this->avr8DebugInterface->readMemory( + addressSpaceDescriptor, + memorySegmentDescriptor, + startAddress, + bytes, + excludedAddressRanges + ); + } + + void Avr8::writeMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, + std::uint32_t startAddress, + const TargetMemoryBuffer& buffer + ) { + if (memorySegmentDescriptor.type == TargetMemorySegmentType::FLASH && !this->programmingModeEnabled()) { + throw Exception{"Attempted Flash memory write in the absence of an active programming session."}; + } + + this->avr8DebugInterface->writeMemory(addressSpaceDescriptor, memorySegmentDescriptor, startAddress, buffer); + } + + bool Avr8::isProgramMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, + TargetMemoryAddress startAddress, + TargetMemorySize size + ) { + /* + * On AVR8 targets, memory segments that are marked as executable are executable in their entirety. + * No need for more granular checks here. + */ + return memorySegmentDescriptor.executable; + } + + void Avr8::eraseMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor + ) { + if (memorySegmentDescriptor.type == TargetMemorySegmentType::FLASH) { + if (!this->programmingModeEnabled()) { + throw Exception{"Attempted flash memory erase in the absence of an active programming session"}; + } + + if (this->targetConfig.physicalInterface == TargetPhysicalInterface::DEBUG_WIRE) { + // debugWIRE targets do not need to be erased + return; + } + + /* + * To erase program memory on JTAG and UPDI targets, we must perform a chip erase. This means we could + * end up erasing EEPROM, unless the EESAVE fuse bit has been programmed. + * + * If configured to do so, we will ensure that the EESAVE fuse bit has been programmed before we perform + * the chip erase. The fuse will be restored to its original value at the end of the programming session. + */ + if ( + this->targetConfig.physicalInterface == TargetPhysicalInterface::JTAG + || this->targetConfig.physicalInterface == TargetPhysicalInterface::UPDI + ) { + if (this->targetConfig.preserveEeprom) { + Logger::debug("Inspecting EESAVE fuse bit"); + this->activeProgrammingSession->managingEesaveFuseBit = this->updateEesaveFuseBit(true); + + } else { + Logger::warning( + "Performing chip-erase with preserveEeprom disabled. All EEPROM data will be lost!" + ); + } + + return this->avr8DebugInterface->eraseChip(); + } + + return this->avr8DebugInterface->eraseProgramMemory(); + } + + /* + * The debug interface does not have to support the erasing of RAM or EEPROM memory. We just implement this as + * a write operation. + */ + this->writeMemory( + addressSpaceDescriptor, + memorySegmentDescriptor, + memorySegmentDescriptor.addressRange.startAddress, + TargetMemoryBuffer(memorySegmentDescriptor.size(), 0xFF) + ); + } + + TargetExecutionState Avr8::getExecutionState() { + return this->avr8DebugInterface->getExecutionState(); + } + + TargetMemoryAddress Avr8::getProgramCounter() { + return this->avr8DebugInterface->getProgramCounter(); + } + + void Avr8::setProgramCounter(TargetMemoryAddress programCounter) { + this->avr8DebugInterface->setProgramCounter(programCounter); + } + + TargetStackPointer Avr8::getStackPointer() { + auto descriptors = TargetRegisterDescriptors{}; + + if (this->spRegisterDescriptor.has_value()) { + descriptors.push_back(&*(this->spRegisterDescriptor)); + } + + if (this->spLowRegisterDescriptor.has_value()) { + descriptors.push_back(&*(this->spLowRegisterDescriptor)); + } + + if (this->spHighRegisterDescriptor.has_value()) { + descriptors.push_back(&*(this->spHighRegisterDescriptor)); + } + + auto output = TargetStackPointer{0}; + + for (const auto& [descriptor, value] : this->readRegisters(descriptors)) { + if ( + this->spHighRegisterDescriptor.has_value() + && descriptor.startAddress == this->spHighRegisterDescriptor->startAddress + ) { + // SP high byte + assert(value.size() == 1); + output = (output & 0x000000FF) | static_cast(value[0] << 8); + + } else { + assert(value.size() > 0 && value.size() <= 2); + for (auto i = std::size_t{0}; i < value.size(); ++i) { + output = (output << (8 * i)) | value[i]; + } + } + } + + return output; + } + + void Avr8::setStackPointer(TargetStackPointer stackPointer) { + if (this->spRegisterDescriptor.has_value()) { + this->writeRegister( + *(this->spRegisterDescriptor), + this->spRegisterDescriptor->size > 1 + ? TargetMemoryBuffer({ + static_cast(stackPointer >> 8), + static_cast(stackPointer) + }) + : TargetMemoryBuffer({static_cast(stackPointer)}) + ); + } + + if (this->spLowRegisterDescriptor.has_value()) { + this->writeRegister( + *(this->spLowRegisterDescriptor), + TargetMemoryBuffer({static_cast(stackPointer)}) + ); + } + + if (this->spHighRegisterDescriptor.has_value()) { + this->writeRegister( + *(this->spHighRegisterDescriptor), + TargetMemoryBuffer({static_cast(stackPointer >> 8)}) + ); + } + } + + TargetGpioPinDescriptorAndStatePairs Avr8::getGpioPinStates(const TargetPinoutDescriptor& pinoutDescriptor) { + auto output = TargetGpioPinDescriptorAndStatePairs{}; + + // To reduce the number of memory reads we perform here, we cache the data and map it by start address. + auto cachedRegsByStartAddress = std::map{}; + const auto readGpioReg = [this, &cachedRegsByStartAddress] (const TargetRegisterDescriptor& descriptor) { + assert(descriptor.size == 1); + + auto cachedRegIt = cachedRegsByStartAddress.find(descriptor.startAddress); + if (cachedRegIt == cachedRegsByStartAddress.end()) { + cachedRegIt = cachedRegsByStartAddress.emplace( + descriptor.startAddress, + this->readRegister(descriptor).at(0) + ).first; + } + + return cachedRegIt->second; + }; + + for (const auto& pinDescriptor : pinoutDescriptor.pinDescriptors) { + if (pinDescriptor.type != TargetPinType::GPIO) { + continue; + } + + const auto padDescriptorIt = this->gpioPadDescriptorsByPadName.find(pinDescriptor.padName); + if (padDescriptorIt == this->gpioPadDescriptorsByPadName.end()) { + continue; + } + + const auto& padDescriptor = padDescriptorIt->second; + + const auto ddrValue = ( + readGpioReg(padDescriptor.dataDirectionRegisterDescriptor) & padDescriptor.registerMask + ) != 0 ? TargetGpioPinState::DataDirection::OUTPUT : TargetGpioPinState::DataDirection::INPUT; + + const auto& stateRegisterDescriptor = ddrValue == TargetGpioPinState::DataDirection::OUTPUT + ? padDescriptor.outputRegisterDescriptor + : padDescriptor.inputRegisterDescriptor; + + output.emplace_back( + TargetGpioPinDescriptorAndStatePair{ + pinDescriptor, + TargetGpioPinState{ + (readGpioReg(stateRegisterDescriptor) & padDescriptor.registerMask) != 0 + ? TargetGpioPinState::State::HIGH + : TargetGpioPinState::State::LOW, + ddrValue + } + } + ); + } + + return output; + } + + void Avr8::setGpioPinState(const TargetPinDescriptor& pinDescriptor, const TargetGpioPinState& state) { + using DataDirection = TargetGpioPinState::DataDirection; + using GpioState = TargetGpioPinState::State; + + const auto padDescriptorIt = this->gpioPadDescriptorsByPadName.find(pinDescriptor.padName); + + if (padDescriptorIt == this->gpioPadDescriptorsByPadName.end()) { + throw Exception{"Unknown pad"}; + } + + const auto& padDescriptor = padDescriptorIt->second; + + const auto currentDdrValue = this->readRegister(padDescriptor.dataDirectionRegisterDescriptor).at(0); + this->writeRegister( + padDescriptor.dataDirectionRegisterDescriptor, + { + static_cast( + state.direction == DataDirection::OUTPUT + ? (currentDdrValue | padDescriptor.registerMask) + : (currentDdrValue & ~(padDescriptor.registerMask)) + ) + } + ); + + if (state.direction == DataDirection::OUTPUT) { + const auto currentOutputValue = this->readRegister(padDescriptor.outputRegisterDescriptor).at(0); + this->writeRegister( + padDescriptor.outputRegisterDescriptor, + { + static_cast( + state.value == GpioState::HIGH + ? (currentOutputValue | padDescriptor.registerMask) + : (currentOutputValue & ~(padDescriptor.registerMask)) + ) + } + ); + } + } + + void Avr8::enableProgrammingMode() { + if (this->activeProgrammingSession.has_value()) { + return; + } + + this->avr8DebugInterface->enableProgrammingMode(); + this->activeProgrammingSession = ProgrammingSession(); + } + + void Avr8::disableProgrammingMode() { + if (!this->activeProgrammingSession.has_value()) { + return; + } + + if (this->activeProgrammingSession->managingEesaveFuseBit) { + this->updateEesaveFuseBit(false); + } + + this->avr8DebugInterface->disableProgrammingMode(); + this->stop(); + this->activeProgrammingSession.reset(); + } + + bool Avr8::programmingModeEnabled() { + return this->activeProgrammingSession.has_value(); + } + + std::map Avr8::generateGpioPadDescriptorMapping( + const std::vector& portPeripheralDescriptors + ) { + auto output = std::map{}; + + for (const auto& peripheralDescriptor : portPeripheralDescriptors) { + for (const auto& signalDescriptor : peripheralDescriptor.signalDescriptors) { + if (!signalDescriptor.index.has_value()) { + continue; + } + + if (output.contains(signalDescriptor.padName)) { + continue; + } + + if (peripheralDescriptor.registerGroupDescriptorsByKey.empty()) { + continue; + } + + const auto registerMask = static_cast(0x01 << *(signalDescriptor.index)); + + /* + * All port peripherals should only have a single register group instance pointing to the port register + * group. This is enforced in the TDF validation script. + * + * The key of the register group instance varies across peripherals, which is why we use begin() as + * opposed to performing a key lookup. + */ + const auto& portRegisterGroup = peripheralDescriptor.registerGroupDescriptorsByKey.begin()->second; + + // From a register layout perspective, there are two types of GPIO port modules on AVR8 targets. + if (portRegisterGroup.registerDescriptorsByKey.contains("outset")) { + output.emplace( + signalDescriptor.padName, + GpioPadDescriptor{ + registerMask, + portRegisterGroup.getRegisterDescriptor("dir"), + portRegisterGroup.getRegisterDescriptor("in"), + portRegisterGroup.getRegisterDescriptor("out") + } + ); + + continue; + } + + /* + * The older GPIO port module is a little trickier, as it has a dedicated register group for each + * instance of the module (e.g. one for PORTA, another for PORTB, etc.). + * + * And the register keys are inconsistent ("ddra", "ddrb", etc.). + */ + auto ddrDescriptor = std::optional>{}; + auto inputDescriptor = std::optional>{}; + auto outputDescriptor = std::optional>{}; + + for (const auto& [registerKey, registerDescriptor] : portRegisterGroup.registerDescriptorsByKey) { + if (registerKey.find("ddr") == 0) { + ddrDescriptor = std::cref(registerDescriptor); + continue; + } + + if (registerKey.find("pin") == 0) { + inputDescriptor = std::cref(registerDescriptor); + continue; + } + + if (registerKey.find("port") == 0) { + outputDescriptor = std::cref(registerDescriptor); + } + } + + if (ddrDescriptor.has_value() && inputDescriptor.has_value() && outputDescriptor.has_value()) { + output.emplace( + signalDescriptor.padName, + GpioPadDescriptor{ + registerMask, + ddrDescriptor->get(), + inputDescriptor->get(), + outputDescriptor->get() + } + ); + } + } + } + + return output; + } + + TargetMemoryBuffer Avr8::readRegister(const TargetRegisterDescriptor& descriptor) { + return this->readRegisters({&descriptor}).at(0).second; + } + + void Avr8::writeRegister(const TargetRegisterDescriptor& descriptor, const TargetMemoryBuffer& value) { + this->writeRegisters({{descriptor, value}}); + } + + void Avr8::applyDebugInterfaceRegisterAccessRestrictions( + TargetRegisterGroupDescriptor& groupDescriptor, + const TargetAddressSpaceDescriptor& addressSpaceDescriptor + ) { + for (auto& [subgroupKey, subgroupDescriptor] : groupDescriptor.subgroupDescriptorsByKey) { + this->applyDebugInterfaceRegisterAccessRestrictions(subgroupDescriptor, addressSpaceDescriptor); + } + + for (auto& [registerKey, registerDescriptor] : groupDescriptor.registerDescriptorsByKey) { + if (!registerDescriptor.access.readable && !registerDescriptor.access.writable) { + // This register is already inaccessible - no need to check for further restrictions + continue; + } + + const auto access = this->avr8DebugInterface->getRegisterAccess(registerDescriptor, addressSpaceDescriptor); + registerDescriptor.access.readable = registerDescriptor.access.readable && access.readable; + registerDescriptor.access.writable = registerDescriptor.access.writable && access.writable; + } + } + + BreakpointResources Avr8::getBreakpointResources() { + auto maxHardwareBreakpoints = std::uint16_t{0}; + + switch (this->targetConfig.physicalInterface) { + case TargetPhysicalInterface::JTAG: { + maxHardwareBreakpoints = this->family == Family::XMEGA ? 2 : 3; + break; + } + case TargetPhysicalInterface::PDI: { + maxHardwareBreakpoints = 2; + break; + } + case TargetPhysicalInterface::UPDI: { + maxHardwareBreakpoints = 1; + break; + } + default: { + break; + } + } + + return BreakpointResources{ + maxHardwareBreakpoints, + std::nullopt, + std::min( + static_cast(this->targetConfig.reserveSteppingBreakpoint ? 1 : 0), + maxHardwareBreakpoints + ) + }; + } + + bool Avr8::isFuseEnabled(const TargetBitFieldDescriptor& bitFieldDescriptor, FuseValue value) const { + const auto programmedValue = static_cast( + this->fuseEnableStrategy == FuseEnableStrategy::SET + ? (0xFF & bitFieldDescriptor.mask) + : 0 + ); + + return (value & bitFieldDescriptor.mask) == programmedValue; + } + + FuseValue Avr8::setFuseEnabled( + const TargetBitFieldDescriptor& bitFieldDescriptor, + FuseValue value, + bool enabled + ) const { + return static_cast( + this->fuseEnableStrategy == FuseEnableStrategy::SET + ? enabled + ? (value | bitFieldDescriptor.mask) + : value & ~(bitFieldDescriptor.mask) + : enabled + ? value & ~(bitFieldDescriptor.mask) + : (value | bitFieldDescriptor.mask) + ); + } + + void Avr8::updateDwenFuseBit(bool enable) { + if (this->avrIspInterface == nullptr) { + throw Exception{ + "Debug tool or driver does not provide access to an ISP interface - please confirm that the " + "debug tool supports ISP and then report this issue via " + Services::PathService::homeDomainName() + + "/report-issue" + }; + } + + if (!this->physicalInterfaces.contains(TargetPhysicalInterface::DEBUG_WIRE)) { + throw Exception{ + "Target does not support debugWIRE physical interface - check target configuration or " + "report this issue via " + Services::PathService::homeDomainName() + "/report-issue" + }; + } + + const auto dwenFuseBitFieldPair = this->targetDescriptionFile.getFuseRegisterBitFieldDescriptorPair("dwen"); + const auto& dwenRegisterDescriptor = dwenFuseBitFieldPair.first; + const auto& dwenBitFieldDescriptor = dwenFuseBitFieldPair.second; + + const auto spienFuseBitFieldPair = this->targetDescriptionFile.getFuseRegisterBitFieldDescriptorPair("spien"); + const auto& spienRegisterDescriptor = spienFuseBitFieldPair.first; + const auto& spienBitFieldDescriptor = spienFuseBitFieldPair.second; + + assert(dwenRegisterDescriptor.size == 1); + assert(spienRegisterDescriptor.size == 1); + + Logger::info("Initiating ISP interface"); + this->avrIspInterface->activate(); + + /* + * AVR fuses are used to control certain functions within the AVR (including the debugWIRE interface). Care + * must be taken when updating these fuse bytes, as an incorrect value could render the AVR inaccessible to + * standard programmers. + * + * When updating the DWEN fuse, Bloom relies on data from the target description file (TDF). But there is no + * guarantee that this data is correct. For this reason, we perform additional checks in an attempt to reduce + * the likelihood of bricking the target: + * + * - Confirm target signature match - We read the AVR signature from the connected target and compare it to + * what we have in the TDF. The operation will be aborted if the signatures do not match. + * + * - SPIEN fuse bit check - we can be certain that the SPIEN fuse bit is set, because we couldn't have gotten + * this far (post ISP activation) if it wasn't. We use this axiom to verify the validity of the data in the + * TDF. If the SPIEN fuse bit appears to be cleared, we can be fairly certain that the data we have on the + * SPIEN fuse bit is incorrect. From this, we assume that the data for the DWEN fuse bit is also incorrect, + * and abort the operation. + * + * - Lock bits check - we read the lock bit byte from the target and confirm that all lock bits are cleared. + * If any lock bits are set, we abort the operation. + * + * - DWEN fuse bit check - if the DWEN fuse bit is already set to the desired value, then there is no need + * to update it. But we may be checking the wrong bit (if the TDF data is incorrect) - either way, we will + * abort the operation. + * + * The precautions described above may reduce the likelihood of Bloom bricking the connected target, but there + * is still a chance that all of the checks pass, and we still brick the device. Now would be a good time to + * remind the user of liabilities in regard to Bloom and its contributors. + */ + Logger::warning( + "Updating the DWEN fuse bit is a potentially dangerous operation. Bloom is provided \"AS IS\", " + "without warranty of any kind. You are using Bloom at your own risk. In no event shall the copyright " + "owner or contributors be liable for any damage caused as a result of using Bloom. For more details, " + "see the Bloom license at " + Services::PathService::homeDomainName() + "/license" + ); + + try { + Logger::info("Reading target signature via ISP"); + const auto ispDeviceSignature = this->avrIspInterface->getDeviceId(); + + if (ispDeviceSignature != this->signature) { + throw Exception{ + "AVR target signature mismatch - expected signature \"" + this->signature.toHex() + + "\" but got \"" + ispDeviceSignature.toHex() + "\". Please check target configuration." + }; + } + + Logger::info("Target signature confirmed: " + ispDeviceSignature.toHex()); + + const auto dwenFuseByte = this->avrIspInterface->readFuse(dwenRegisterDescriptor); + const auto spienFuseByte = (spienRegisterDescriptor == dwenRegisterDescriptor) + ? dwenFuseByte + : this->avrIspInterface->readFuse(spienRegisterDescriptor); + + /* + * Keep in mind that, for AVR fuses and lock bits, a set bit (0b1) means the fuse/lock is cleared, and a + * cleared bit (0b0), means the fuse/lock is set. + */ + + if (!this->isFuseEnabled(spienBitFieldDescriptor, spienFuseByte)) { + /* + * If we get here, something is very wrong. The SPIEN (SPI enable) fuse bit appears to be cleared, but + * this is not possible because we're connected to the target via the SPI (the ISP interface uses a + * physical SPI between the debug tool and the target). + * + * This could be (and likely is) caused by incorrect data for the SPIEN fuse bit, in the TDF (which was + * used to construct the spienFuseBitsDescriptor). And if the data for the SPIEN fuse bit is incorrect, + * then what's to say the data for the DWEN fuse bit (dwenFuseBitsDescriptor) is any better? + * + * We must assume the worst and abort the operation. Otherwise, we risk bricking the user's hardware. + */ + throw Exception{ + "Invalid SPIEN fuse bit value - suspected inaccuracies in TDF data. Please report this to " + "Bloom developers as a matter of urgency, via " + Services::PathService::homeDomainName() + + "/report-issue" + }; + } + + Logger::info("Current SPIEN fuse bit value confirmed"); + + if (this->isFuseEnabled(dwenBitFieldDescriptor, dwenFuseByte) == enable) { + /* + * The DWEN fuse appears to already be set to the desired value. This may be a result of incorrect data + * in the TDF, but we're not taking any chances. + * + * We don't throw an exception here, because we don't know if this is due to an error, or if the fuse + * bit is simply already set to the desired value. + */ + Logger::debug("DWEN fuse bit already set to desired value - aborting update operation"); + + this->avrIspInterface->deactivate(); + return; + } + + const auto lockBitByte = this->avrIspInterface->readLockBitByte(); + if (lockBitByte != 0xFF) { + /* + * There is at least one lock bit that is set. Setting the DWEN fuse bit with the lock bits set may + * brick the device. We must abort. + */ + throw Exception{ + "At least one lock bit has been set - updating the DWEN fuse bit could potentially brick " + "the target." + }; + } + + Logger::info("Cleared lock bits confirmed"); + + const auto newFuseValue = this->setFuseEnabled(dwenBitFieldDescriptor, dwenFuseByte, enable); + + Logger::warning("Updating DWEN fuse bit"); + this->avrIspInterface->programFuse(dwenRegisterDescriptor, newFuseValue); + + Logger::debug("Verifying DWEN fuse bit"); + if (this->avrIspInterface->readFuse(dwenRegisterDescriptor) != newFuseValue) { + throw Exception{"Failed to update DWEN fuse bit - post-update verification failed"}; + } + + Logger::info("DWEN fuse bit successfully updated"); + + this->avrIspInterface->deactivate(); + + } catch (const Exception& exception) { + this->avrIspInterface->deactivate(); + throw exception; + } + } + + void Avr8::updateOcdenFuseBit(bool enable) { + using Services::PathService; + using Services::StringService; + + if (!this->physicalInterfaces.contains(TargetPhysicalInterface::JTAG)) { + throw Exception{ + "Target does not support JTAG physical interface - check target configuration or " + "report this issue via " + PathService::homeDomainName() + "/report-issue" + }; + } + + const auto targetSignature = this->avr8DebugInterface->getDeviceId(); + const auto tdSignature = this->targetDescriptionFile.getTargetSignature(); + + if (targetSignature != tdSignature) { + throw Exception{ + "Failed to validate connected target - target signature mismatch.\nThe target signature" + " (\"" + targetSignature.toHex() + "\") does not match the AVR8 target description signature (\"" + + tdSignature.toHex() + "\"). This will likely be due to an incorrect target name in the " + + "configuration file (bloom.yaml)." + }; + } + + const auto ocdenFuseBitFieldPair = this->targetDescriptionFile.getFuseRegisterBitFieldDescriptorPair("ocden"); + const auto& ocdenRegisterDescriptor = ocdenFuseBitFieldPair.first; + const auto& ocdenBitFieldDescriptor = ocdenFuseBitFieldPair.second; + + const auto jtagenFuseBitFieldPair = this->targetDescriptionFile.getFuseRegisterBitFieldDescriptorPair("jtagen"); + const auto& jtagenRegisterDescriptor = jtagenFuseBitFieldPair.first; + const auto& jtagenBitFieldDescriptor = jtagenFuseBitFieldPair.second; + + assert(ocdenRegisterDescriptor.size == 1); + assert(jtagenRegisterDescriptor.size == 1); + + const auto ocdenFuseByteValue = this->avr8DebugInterface->readMemory( + this->fuseAddressSpaceDescriptor, + this->fuseMemorySegmentDescriptor, + ocdenRegisterDescriptor.startAddress, + 1 + ).at(0); + const auto jtagenFuseByteValue = jtagenRegisterDescriptor == ocdenRegisterDescriptor + ? ocdenFuseByteValue + : this->avr8DebugInterface->readMemory( + this->fuseAddressSpaceDescriptor, + this->fuseMemorySegmentDescriptor, + jtagenRegisterDescriptor.startAddress, + 1 + ).at(0); + + Logger::debug("OCDEN fuse byte value (before update): 0x" + StringService::toHex(ocdenFuseByteValue)); + + if (!this->isFuseEnabled(jtagenBitFieldDescriptor, jtagenFuseByteValue)) { + /* + * If we get here, something has gone wrong. The JTAGEN fuse should always be programmed by this point. + * We wouldn't have been able to activate the JTAG physical interface if the fuse wasn't programmed. + * + * This means the data we have on the JTAGEN fuse bit, from the TDF, is likely incorrect. And if that's + * the case, we cannot rely on the data for the OCDEN fuse bit being any better. + */ + throw Exception{ + "Invalid JTAGEN fuse bit value - suspected inaccuracies in TDF data. Please report this to " + "Bloom developers as a matter of urgency, via " + PathService::homeDomainName() + "/report-issue" + }; + } + + if (this->isFuseEnabled(ocdenBitFieldDescriptor, ocdenFuseByteValue) == enable) { + Logger::debug("OCDEN fuse bit already set to desired value - aborting update operation"); + return; + } + + const auto newValue = this->setFuseEnabled(ocdenBitFieldDescriptor, ocdenFuseByteValue, enable); + + Logger::debug("New OCDEN fuse byte value (to be written): 0x" + StringService::toHex(newValue)); + + Logger::warning("Updating OCDEN fuse bit"); + this->avr8DebugInterface->writeMemory( + this->fuseAddressSpaceDescriptor, + this->fuseMemorySegmentDescriptor, + ocdenRegisterDescriptor.startAddress, + {newValue} + ); + + Logger::debug("Verifying OCDEN fuse bit"); + const auto postUpdateOcdenByteValue = this->avr8DebugInterface->readMemory( + this->fuseAddressSpaceDescriptor, + this->fuseMemorySegmentDescriptor, + ocdenRegisterDescriptor.startAddress, + 1 + ).at(0); + + if (postUpdateOcdenByteValue != newValue) { + throw Exception{"Failed to update OCDEN fuse bit - post-update verification failed"}; + } + + Logger::info("OCDEN fuse bit updated"); + + this->disableProgrammingMode(); + } + + bool Avr8::updateEesaveFuseBit(bool enable) { + using Services::StringService; + + const auto eesaveFuseBitFieldPair = this->targetDescriptionFile.getFuseRegisterBitFieldDescriptorPair("eesave"); + const auto& eesaveRegisterDescriptor = eesaveFuseBitFieldPair.first; + const auto& eesaveBitFieldDescriptor = eesaveFuseBitFieldPair.second; + + assert(eesaveRegisterDescriptor.size == 1); + + const auto eesaveFuseByteValue = this->avr8DebugInterface->readMemory( + this->fuseAddressSpaceDescriptor, + this->fuseMemorySegmentDescriptor, + eesaveRegisterDescriptor.startAddress, + 1 + ).at(0); + + Logger::debug("EESAVE fuse byte value (before update): 0x" + StringService::toHex(eesaveFuseByteValue)); + + if (this->isFuseEnabled(eesaveBitFieldDescriptor, eesaveFuseByteValue) == enable) { + Logger::debug("EESAVE fuse bit already set to desired value - aborting update operation"); + return false; + } + + const auto newValue = this->setFuseEnabled(eesaveBitFieldDescriptor, eesaveFuseByteValue, enable); + + Logger::debug("New EESAVE fuse byte value (to be written): 0x" + StringService::toHex(newValue)); + + Logger::warning("Updating EESAVE fuse bit"); + this->avr8DebugInterface->writeMemory( + this->fuseAddressSpaceDescriptor, + this->fuseMemorySegmentDescriptor, + eesaveRegisterDescriptor.startAddress, + {newValue} + ); + + Logger::debug("Verifying EESAVE fuse bit"); + const auto postUpdateEesaveByteValue = this->avr8DebugInterface->readMemory( + this->fuseAddressSpaceDescriptor, + this->fuseMemorySegmentDescriptor, + eesaveRegisterDescriptor.startAddress, + 1 + ).at(0); + + if (postUpdateEesaveByteValue != newValue) { + throw Exception{"Failed to update EESAVE fuse bit - post-update verification failed"}; + } + + Logger::info("EESAVE fuse bit updated"); + return true; + } +} diff --git a/src/Targets/Microchip/AVR8/Avr8.hpp b/src/Targets/Microchip/AVR8/Avr8.hpp new file mode 100644 index 00000000..352e83fb --- /dev/null +++ b/src/Targets/Microchip/AVR8/Avr8.hpp @@ -0,0 +1,232 @@ +#pragma once + +#include +#include +#include +#include + +#include "src/Targets/Target.hpp" +#include "src/DebugToolDrivers/DebugTool.hpp" + +#include "src/DebugToolDrivers/TargetInterfaces/Microchip/AVR8/Avr8DebugInterface.hpp" +#include "src/DebugToolDrivers/TargetInterfaces/Microchip/AVR8/AvrIspInterface.hpp" + +#include "Family.hpp" +#include "GpioPadDescriptor.hpp" +#include "ProgramMemorySection.hpp" +#include "ProgrammingSession.hpp" + +#include "src/Targets/Microchip/AVR8/Fuse.hpp" +#include "src/Targets/TargetPhysicalInterface.hpp" +#include "src/Targets/TargetRegisterDescriptor.hpp" +#include "src/Targets/TargetBitFieldDescriptor.hpp" +#include "src/Targets/TargetBreakpoint.hpp" + +#include "TargetDescriptionFile.hpp" + +#include "Avr8TargetConfig.hpp" + +namespace Targets::Microchip::Avr8 +{ + class Avr8: public Target + { + public: + explicit Avr8(const TargetConfig& targetConfig, TargetDescriptionFile&& targetDescriptionFile); + + /* + * The functions below implement the Target interface for AVR8 targets. + * + * See the Targets::Target abstract class for documentation on the expected behaviour of + * each function. + */ + + /** + * All AVR8 compatible debug tools must provide a valid Avr8Interface. + * + * @param debugTool + * @return + */ + bool supportsDebugTool(DebugTool* debugTool) override; + + void setDebugTool(DebugTool* debugTool) override; + + void activate() override; + void deactivate() override; + + TargetDescriptor targetDescriptor() override; + + void run(std::optional toAddress) override; + void stop() override; + void step() override; + void reset() override; + + void setSoftwareBreakpoint(TargetMemoryAddress address) override; + void removeSoftwareBreakpoint(TargetMemoryAddress address) override; + + void setHardwareBreakpoint(TargetMemoryAddress address) override; + void removeHardwareBreakpoint(TargetMemoryAddress address) override; + void clearAllBreakpoints() override; + + TargetRegisterDescriptorAndValuePairs readRegisters(const TargetRegisterDescriptors& descriptors) override; + void writeRegisters(const TargetRegisterDescriptorAndValuePairs& registers) override; + + TargetMemoryBuffer readMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, + TargetMemoryAddress startAddress, + TargetMemorySize bytes, + const std::set& excludedAddressRanges + ) override; + void writeMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, + TargetMemoryAddress startAddress, + const TargetMemoryBuffer& buffer + ) override; + bool isProgramMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, + TargetMemoryAddress startAddress, + TargetMemorySize size + ) override; + void eraseMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor + ) override; + + TargetExecutionState getExecutionState() override; + + TargetMemoryAddress getProgramCounter() override; + void setProgramCounter(TargetMemoryAddress programCounter) override; + + TargetStackPointer getStackPointer() override; + void setStackPointer(TargetStackPointer stackPointer) override; + + TargetGpioPinDescriptorAndStatePairs getGpioPinStates(const TargetPinoutDescriptor& pinoutDescriptor) override; + void setGpioPinState(const TargetPinDescriptor& pinDescriptor, const TargetGpioPinState& state) override; + + void enableProgrammingMode() override; + + void disableProgrammingMode() override; + + bool programmingModeEnabled() override; + + protected: + DebugToolDrivers::TargetInterfaces::TargetPowerManagementInterface* targetPowerManagementInterface = nullptr; + DebugToolDrivers::TargetInterfaces::Microchip::Avr8::Avr8DebugInterface* avr8DebugInterface = nullptr; + DebugToolDrivers::TargetInterfaces::Microchip::Avr8::AvrIspInterface* avrIspInterface = nullptr; + + Avr8TargetConfig targetConfig; + TargetDescriptionFile targetDescriptionFile; + + TargetAddressSpaceDescriptor dataAddressSpaceDescriptor; + TargetAddressSpaceDescriptor fuseAddressSpaceDescriptor; + + TargetMemorySegmentDescriptor ramMemorySegmentDescriptor; + TargetMemorySegmentDescriptor ioMemorySegmentDescriptor; + TargetMemorySegmentDescriptor fuseMemorySegmentDescriptor; + + TargetSignature signature; + Family family; + + bool activated = false; + + std::set physicalInterfaces; + + std::vector gpioPortPeripheralDescriptors; + std::map gpioPadDescriptorsByPadName; + + /** + * The stack pointer register on AVR8 targets can take several forms: + * + * 1. A single 8 or 16-bit register in the CPU peripheral, with key "sp" + * 2. Two 8-bit registers for high and low bytes in a 16-bit stack pointer, in the CPU peripheral, with keys + * "spl" and "sph" + * 3. A single 8-bit low byte register for an 8-bit stack pointer, in the CPU peripheral. This is similar + * to 1, but with key "spl" + */ + std::optional spRegisterDescriptor; + std::optional spLowRegisterDescriptor; + std::optional spHighRegisterDescriptor; + + /** + * On some AVR8 targets, like the ATmega328P, a cleared fuse bit means the fuse is "programmed" (enabled). + * And a set bit means the fuse is "un-programmed" (disabled). But on others, like the ATmega4809, it's the + * other way around (set bit == enabled, cleared bit == disabled). + * + * The FuseEnableStrategy specifies the strategy of enabling a fuse. It's extracted from the TDF. + * See TargetDescription::getFuseEnableStrategy() for more. + */ + FuseEnableStrategy fuseEnableStrategy; + + std::optional activeProgrammingSession = std::nullopt; + + static std::map generateGpioPadDescriptorMapping( + const std::vector& portPeripheralDescriptors + ); + + TargetMemoryBuffer readRegister(const TargetRegisterDescriptor& descriptor); + void writeRegister(const TargetRegisterDescriptor& descriptor, const TargetMemoryBuffer& value) ; + + void applyDebugInterfaceRegisterAccessRestrictions( + TargetRegisterGroupDescriptor& groupDescriptor, + const TargetAddressSpaceDescriptor& addressSpaceDescriptor + ); + + BreakpointResources getBreakpointResources(); + + /** + * Checks if a particular fuse is enabled in the given fuse byte value. Takes the target's fuse enable strategy + * into account. + * + * @param bitFieldDescriptor + * @param value + * + * @return + */ + bool isFuseEnabled(const TargetBitFieldDescriptor& bitFieldDescriptor, FuseValue value) const; + + /** + * Enables/disables a fuse within the given fuse byte, using the target's fuse enable strategy. + * + * @param bitFieldDescriptor + * @param value + * @param enabled + * + * @return + * The updated fuse byte value. + */ + FuseValue setFuseEnabled( + const TargetBitFieldDescriptor& bitFieldDescriptor, + FuseValue value, + bool enabled + ) const; + + /** + * Updates the debugWIRE enable (DWEN) fuse bit on the AVR target. + * + * @param enable + * True to enable the fuse, false to disable it. + */ + void updateDwenFuseBit(bool enable); + + /** + * Updates the On-chip debug enable (OCDEN) fuse bit on the AVR target. + * + * @param enable + * True to enable the fuse, false to disable it. + */ + void updateOcdenFuseBit(bool enable); + + /** + * Updates the "Preserve EEPROM" (EESAVE) fuse bit on the AVR target. + * + * @param enable + * True to enable the fuse, false to disable it. + * + * @return + * True if the fuse bit was updated. False if the fuse bit was already set to the desired value. + */ + bool updateEesaveFuseBit(bool enable); + }; +} diff --git a/src/Targets/Microchip/AVR/AVR8/Avr8TargetConfig.cpp b/src/Targets/Microchip/AVR8/Avr8TargetConfig.cpp similarity index 87% rename from src/Targets/Microchip/AVR/AVR8/Avr8TargetConfig.cpp rename to src/Targets/Microchip/AVR8/Avr8TargetConfig.cpp index d8bdaba6..a25ad95e 100644 --- a/src/Targets/Microchip/AVR/AVR8/Avr8TargetConfig.cpp +++ b/src/Targets/Microchip/AVR8/Avr8TargetConfig.cpp @@ -1,17 +1,10 @@ #include "Avr8TargetConfig.hpp" -#include "src/Services/PathService.hpp" -#include "src/Services/StringService.hpp" - -#include "src/Exceptions/InvalidConfig.hpp" - -namespace Targets::Microchip::Avr::Avr8Bit +namespace Targets::Microchip::Avr8 { Avr8TargetConfig::Avr8TargetConfig(const TargetConfig& targetConfig) : TargetConfig(targetConfig) { - using Exceptions::InvalidConfig; - const auto& targetNode = targetConfig.targetNode; // The 'manageDwenFuseBit' param used to be 'updateDwenFuseBit' - we still support the old, for now. @@ -40,9 +33,9 @@ namespace Targets::Microchip::Avr::Avr8Bit } if (targetNode["targetPowerCycleDelay"]) { - this->targetPowerCycleDelay = std::chrono::milliseconds(targetNode["targetPowerCycleDelay"].as( + this->targetPowerCycleDelay = std::chrono::milliseconds{targetNode["targetPowerCycleDelay"].as( this->targetPowerCycleDelay.count() - )); + )}; } if (targetNode["manageOcdenFuseBit"]) { diff --git a/src/Targets/Microchip/AVR/AVR8/Avr8TargetConfig.hpp b/src/Targets/Microchip/AVR8/Avr8TargetConfig.hpp similarity index 88% rename from src/Targets/Microchip/AVR/AVR8/Avr8TargetConfig.hpp rename to src/Targets/Microchip/AVR8/Avr8TargetConfig.hpp index 1b396c24..7ad6dc8f 100644 --- a/src/Targets/Microchip/AVR/AVR8/Avr8TargetConfig.hpp +++ b/src/Targets/Microchip/AVR8/Avr8TargetConfig.hpp @@ -6,7 +6,7 @@ #include "src/ProjectConfig.hpp" -namespace Targets::Microchip::Avr::Avr8Bit +namespace Targets::Microchip::Avr8 { /** * Extending the generic TargetConfig struct to accommodate AVR8 target configuration parameters. @@ -15,15 +15,15 @@ namespace Targets::Microchip::Avr::Avr8Bit { public: /** - * Because the debugWire module requires control of the reset pin on the target, enabling this module will + * Because the debugWIRE module requires control of the reset pin on the target, enabling this module will * effectively mean losing control of the reset pin. This means users won't be able to use other - * interfaces that require access to the reset pin, such as ISP, until the debugWire module is disabled. + * interfaces that require access to the reset pin, such as ISP, until the debugWIRE module is disabled. * - * The EdbgAvr8Interface provides a function for temporarily disabling the debugWire module on the target. - * This doesn't change the DWEN fuse and its affect is only temporary - the debugWire module will be + * The EdbgAvr8Interface provides a function for temporarily disabling the debugWIRE module on the target. + * This doesn't change the DWEN fuse and its affect is only temporary - the debugWIRE module will be * reactivated upon the user cycling the power to the target. * - * Bloom is able to temporarily disable the debugWire module, automatically, upon deactivating of the + * Bloom is able to temporarily disable the debugWIRE module, automatically, upon deactivating of the * target (which usually occurs after a debug session has ended). This allows users to program the target via * ISP, after they've finished a debug session. After programming the target, the user will need to cycle the * target power before Bloom can gain access for another debug session. This flag control this function. @@ -35,7 +35,7 @@ namespace Targets::Microchip::Avr::Avr8Bit bool disableDebugWireOnDeactivate = false; /** - * The manageDwenFuseBit flag determines if Bloom should manage the DWEN fuse bit, for debugWire sessions. + * The manageDwenFuseBit flag determines if Bloom should manage the DWEN fuse bit, for debugWIRE sessions. * * This parameter is optional, and the function is disabled by default. Users must explicitly enable it in * their target configuration. @@ -44,7 +44,7 @@ namespace Targets::Microchip::Avr::Avr8Bit /** * For debug tools that provide target power management functions (such as some evaluation boards), Bloom can - * automatically cycle the target power after updating the DWEN fuse bit, for debugWire sessions. This parameter + * automatically cycle the target power after updating the DWEN fuse bit, for debugWIRE sessions. This parameter * controls this function. * * This parameter is optional. The function is enabled by default. @@ -57,7 +57,7 @@ namespace Targets::Microchip::Avr::Avr8Bit * * This parameter determines how long we wait. */ - std::chrono::milliseconds targetPowerCycleDelay = std::chrono::milliseconds(250); + std::chrono::milliseconds targetPowerCycleDelay = std::chrono::milliseconds{250}; /** * The manageOcdenFuseBit flag determines if Bloom should manage the OCDEN fuse but on JTAG-enabled AVR diff --git a/src/Targets/Microchip/AVR/AVR8/Exceptions/DebugWirePhysicalInterfaceError.hpp b/src/Targets/Microchip/AVR8/Exceptions/DebugWirePhysicalInterfaceError.hpp similarity index 72% rename from src/Targets/Microchip/AVR/AVR8/Exceptions/DebugWirePhysicalInterfaceError.hpp rename to src/Targets/Microchip/AVR8/Exceptions/DebugWirePhysicalInterfaceError.hpp index e9e5b961..abcf3d33 100644 --- a/src/Targets/Microchip/AVR/AVR8/Exceptions/DebugWirePhysicalInterfaceError.hpp +++ b/src/Targets/Microchip/AVR8/Exceptions/DebugWirePhysicalInterfaceError.hpp @@ -10,9 +10,5 @@ namespace Exceptions explicit DebugWirePhysicalInterfaceError(const std::string& message) : TargetOperationFailure(message) {} - - explicit DebugWirePhysicalInterfaceError(const char* message) - : TargetOperationFailure(message) - {} }; } diff --git a/src/Targets/Microchip/AVR/AVR8/Family.hpp b/src/Targets/Microchip/AVR8/Family.hpp similarity index 77% rename from src/Targets/Microchip/AVR/AVR8/Family.hpp rename to src/Targets/Microchip/AVR8/Family.hpp index 1ac4f24d..10b3abb4 100644 --- a/src/Targets/Microchip/AVR/AVR8/Family.hpp +++ b/src/Targets/Microchip/AVR8/Family.hpp @@ -1,14 +1,14 @@ #pragma once -namespace Targets::Microchip::Avr::Avr8Bit +namespace Targets::Microchip::Avr8 { enum class Family: int { MEGA, XMEGA, TINY, - DB, DA, + DB, DD, EA, }; diff --git a/src/Targets/Microchip/AVR8/Fuse.hpp b/src/Targets/Microchip/AVR8/Fuse.hpp new file mode 100644 index 00000000..7ee6969d --- /dev/null +++ b/src/Targets/Microchip/AVR8/Fuse.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include "src/Targets/TargetMemory.hpp" + +namespace Targets::Microchip::Avr8 +{ + using FuseValue = std::uint8_t; + + enum class FuseType: std::uint8_t + { + LOW, + HIGH, + EXTENDED, + OTHER, + }; + + enum class FuseEnableStrategy: std::uint8_t + { + CLEAR, + SET, + }; +} diff --git a/src/Targets/Microchip/AVR8/GpioPadDescriptor.hpp b/src/Targets/Microchip/AVR8/GpioPadDescriptor.hpp new file mode 100644 index 00000000..9761d3da --- /dev/null +++ b/src/Targets/Microchip/AVR8/GpioPadDescriptor.hpp @@ -0,0 +1,34 @@ +#pragma once + +#include + +#include "src/Targets/TargetRegisterDescriptor.hpp" + +namespace Targets::Microchip::Avr8 +{ + /** + * This struct contains all of the relevant GPIO register descriptors for a particular pad, on an AVR8 target. + * + * We use this to read and manipulate the state of GPIO pins. + */ + struct GpioPadDescriptor + { + std::uint8_t registerMask; + + const TargetRegisterDescriptor& dataDirectionRegisterDescriptor; + const TargetRegisterDescriptor& inputRegisterDescriptor; + const TargetRegisterDescriptor& outputRegisterDescriptor; + + GpioPadDescriptor( + std::uint8_t registerMask, + const TargetRegisterDescriptor& dataDirectionRegisterDescriptor, + const TargetRegisterDescriptor& inputRegisterDescriptor, + const TargetRegisterDescriptor& outputRegisterDescriptor + ) + : registerMask(registerMask) + , dataDirectionRegisterDescriptor(dataDirectionRegisterDescriptor) + , inputRegisterDescriptor(inputRegisterDescriptor) + , outputRegisterDescriptor(outputRegisterDescriptor) + {} + }; +} diff --git a/src/Targets/Microchip/AVR8/IspParameters.cpp b/src/Targets/Microchip/AVR8/IspParameters.cpp new file mode 100644 index 00000000..ba1ba0f6 --- /dev/null +++ b/src/Targets/Microchip/AVR8/IspParameters.cpp @@ -0,0 +1,29 @@ +#include "IspParameters.hpp" + +#include "src/Services/StringService.hpp" + +namespace Targets::Microchip::Avr8 +{ + IspParameters::IspParameters(const TargetDescriptionFile& targetDescriptionFile) { + using Services::StringService; + + const auto& ispGroup = targetDescriptionFile.getPropertyGroup("isp_interface"); + + this->programModeTimeout = StringService::toUint8(ispGroup.getProperty("ispenterprogmode_timeout").value); + this->programModeStabilizationDelay = StringService::toUint8( + ispGroup.getProperty("ispenterprogmode_stabdelay").value + ); + this->programModeCommandExecutionDelay = StringService::toUint8( + ispGroup.getProperty("ispenterprogmode_cmdexedelay").value + ); + this->programModeSyncLoops = StringService::toUint8(ispGroup.getProperty("ispenterprogmode_synchloops").value); + this->programModeByteDelay = StringService::toUint8(ispGroup.getProperty("ispenterprogmode_bytedelay").value); + this->programModePollValue = StringService::toUint8(ispGroup.getProperty("ispenterprogmode_pollvalue").value); + this->programModePollIndex = StringService::toUint8(ispGroup.getProperty("ispenterprogmode_pollindex").value); + this->programModePreDelay = StringService::toUint8(ispGroup.getProperty("ispleaveprogmode_predelay").value); + this->programModePostDelay = StringService::toUint8(ispGroup.getProperty("ispleaveprogmode_postdelay").value); + this->readSignaturePollIndex = StringService::toUint8(ispGroup.getProperty("ispreadsign_pollindex").value); + this->readFusePollIndex = StringService::toUint8(ispGroup.getProperty("ispreadfuse_pollindex").value); + this->readLockPollIndex = StringService::toUint8(ispGroup.getProperty("ispreadlock_pollindex").value); + } +} diff --git a/src/Targets/Microchip/AVR/IspParameters.hpp b/src/Targets/Microchip/AVR8/IspParameters.hpp similarity index 62% rename from src/Targets/Microchip/AVR/IspParameters.hpp rename to src/Targets/Microchip/AVR8/IspParameters.hpp index 9b2ac2b1..ea660298 100644 --- a/src/Targets/Microchip/AVR/IspParameters.hpp +++ b/src/Targets/Microchip/AVR8/IspParameters.hpp @@ -2,10 +2,15 @@ #include -namespace Targets::Microchip::Avr +#include "TargetDescriptionFile.hpp" + +namespace Targets::Microchip::Avr8 { /** - * These parameters are required by the ISP interface. They can be extracted from the target's TDF. + * These parameters are required by ISP interfaces, to enter programming mode. + * + * These parameters are not specific to the EDBG protocol, which is why they do not reside in the EDBG protocol + * directory. */ struct IspParameters { @@ -18,9 +23,10 @@ namespace Targets::Microchip::Avr std::uint8_t programModePollIndex; std::uint8_t programModePreDelay; std::uint8_t programModePostDelay; - std::uint8_t readSignaturePollIndex; std::uint8_t readFusePollIndex; std::uint8_t readLockPollIndex; + + explicit IspParameters(const TargetDescriptionFile& targetDescriptionFile); }; } diff --git a/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.cpp b/src/Targets/Microchip/AVR8/OpcodeDecoder/Decoder.cpp similarity index 97% rename from src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.cpp rename to src/Targets/Microchip/AVR8/OpcodeDecoder/Decoder.cpp index 4b119200..d113c1c5 100644 --- a/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.cpp +++ b/src/Targets/Microchip/AVR8/OpcodeDecoder/Decoder.cpp @@ -6,14 +6,14 @@ #include "Exceptions/DecodeFailure.hpp" -namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder +namespace Targets::Microchip::Avr8::OpcodeDecoder { Decoder::InstructionMapping Decoder::decode( Targets::TargetMemoryAddress startByteAddress, const TargetMemoryBuffer& data, bool throwOnFailure ) { - auto output = Decoder::InstructionMapping(); + auto output = Decoder::InstructionMapping{}; static const auto decoders = Decoder::opcodeDecoders(); @@ -29,7 +29,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder if (instruction.has_value()) { const auto instructionSize = instruction->byteSize; - output.insert(std::pair(instructionByteAddress, std::move(*instruction))); + output.emplace(instructionByteAddress, std::move(*instruction)); dataIt += instructionSize; instructionByteAddress += instructionSize; @@ -40,13 +40,13 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder if (!opcodeMatched) { if (throwOnFailure) { - throw Exceptions::DecodeFailure( + throw Exceptions::DecodeFailure{ instructionByteAddress, static_cast(*(dataIt + 1) << 8) | *dataIt - ); + }; } - output.insert(std::pair(instructionByteAddress, std::nullopt)); + output.emplace(instructionByteAddress, std::nullopt); dataIt += 2; instructionByteAddress += 2; @@ -62,7 +62,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder * * I've used the same order that is used in the AVR implementation of GDB. */ - return Decoder::OpcodeDecoders({ + return Decoder::OpcodeDecoders{ std::bind(&Opcodes::UndefinedOrErased::decode, std::placeholders::_1, std::placeholders::_2), std::bind(&Opcodes::Clc::decode, std::placeholders::_1, std::placeholders::_2), std::bind(&Opcodes::Clh::decode, std::placeholders::_1, std::placeholders::_2), @@ -208,6 +208,6 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder std::bind(&Opcodes::Eicall::decode, std::placeholders::_1, std::placeholders::_2), std::bind(&Opcodes::Eijmp::decode, std::placeholders::_1, std::placeholders::_2), std::bind(&Opcodes::Des::decode, std::placeholders::_1, std::placeholders::_2), - }); + }; } } diff --git a/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.hpp b/src/Targets/Microchip/AVR8/OpcodeDecoder/Decoder.hpp similarity index 96% rename from src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.hpp rename to src/Targets/Microchip/AVR8/OpcodeDecoder/Decoder.hpp index 2f3d4fe0..b255705a 100644 --- a/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Decoder.hpp +++ b/src/Targets/Microchip/AVR8/OpcodeDecoder/Decoder.hpp @@ -11,7 +11,7 @@ #include "src/Targets/TargetMemory.hpp" #include "src/Services/BitsetService.hpp" -namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder +namespace Targets::Microchip::Avr8::OpcodeDecoder { class Decoder { diff --git a/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Exceptions/DecodeFailure.hpp b/src/Targets/Microchip/AVR8/OpcodeDecoder/Exceptions/DecodeFailure.hpp similarity index 88% rename from src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Exceptions/DecodeFailure.hpp rename to src/Targets/Microchip/AVR8/OpcodeDecoder/Exceptions/DecodeFailure.hpp index 70f24bdd..b8ecdd26 100644 --- a/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Exceptions/DecodeFailure.hpp +++ b/src/Targets/Microchip/AVR8/OpcodeDecoder/Exceptions/DecodeFailure.hpp @@ -6,7 +6,7 @@ #include "src/Targets/TargetMemory.hpp" -namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Exceptions +namespace Targets::Microchip::Avr8::OpcodeDecoder::Exceptions { class DecodeFailure: public ::Exceptions::Exception { diff --git a/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Instruction.hpp b/src/Targets/Microchip/AVR8/OpcodeDecoder/Instruction.hpp similarity index 98% rename from src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Instruction.hpp rename to src/Targets/Microchip/AVR8/OpcodeDecoder/Instruction.hpp index 9f48d4da..7984bf7a 100644 --- a/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Instruction.hpp +++ b/src/Targets/Microchip/AVR8/OpcodeDecoder/Instruction.hpp @@ -6,7 +6,7 @@ #include "src/Targets/TargetMemory.hpp" -namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder +namespace Targets::Microchip::Avr8::OpcodeDecoder { struct Instruction { diff --git a/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Opcode.hpp b/src/Targets/Microchip/AVR8/OpcodeDecoder/Opcode.hpp similarity index 99% rename from src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Opcode.hpp rename to src/Targets/Microchip/AVR8/OpcodeDecoder/Opcode.hpp index 521bc59e..b9248894 100644 --- a/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Opcode.hpp +++ b/src/Targets/Microchip/AVR8/OpcodeDecoder/Opcode.hpp @@ -13,7 +13,7 @@ #include "src/Targets/TargetMemory.hpp" #include "src/Services/BitsetService.hpp" -namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder +namespace Targets::Microchip::Avr8::OpcodeDecoder { struct InstructionParameterBase {}; @@ -520,13 +520,13 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder return std::nullopt; } - auto output = Instruction( + auto output = Instruction{ SelfType::name, opcode, byteSize, mnemonic, canChangeProgramFlow - ); + }; if constexpr (decltype(sourceRegisterParameter)::hasValue()) { constexpr auto param = sourceRegisterParameter.value; diff --git a/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Opcodes.hpp b/src/Targets/Microchip/AVR8/OpcodeDecoder/Opcodes.hpp similarity index 80% rename from src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Opcodes.hpp rename to src/Targets/Microchip/AVR8/OpcodeDecoder/Opcodes.hpp index 6d79c427..0ae58bc6 100644 --- a/src/Targets/Microchip/AVR/AVR8/OpcodeDecoder/Opcodes.hpp +++ b/src/Targets/Microchip/AVR8/OpcodeDecoder/Opcodes.hpp @@ -6,7 +6,7 @@ #include "Instruction.hpp" #include "src/Services/BitsetService.hpp" -namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes +namespace Targets::Microchip::Avr8::OpcodeDecoder::Opcodes { using Adc = Opcode< "ADC", @@ -14,11 +14,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::ADC, false, - RegisterParameter(std::to_array({ + RegisterParameter{std::to_array({ {9, 1}, {3, 4}, - })), - RegisterParameter({8, 5}) + })}, + RegisterParameter{{8, 5}} >; using Add = Opcode< @@ -27,11 +27,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::ADD, false, - RegisterParameter(std::to_array({ + RegisterParameter{std::to_array({ {9, 1}, {3, 4}, - })), - RegisterParameter({8, 5}) + })}, + RegisterParameter{{8, 5}} >; using Adiw = Opcode< @@ -41,11 +41,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::ADIW, false, std::nullopt, - RegisterParameter(24, true, {5, 2}), - InstructionParameter(std::to_array({ + RegisterParameter{24, true, {5, 2}}, + InstructionParameter{std::to_array({ {7, 2}, {3, 4}, - })) + })} >; using And = Opcode< @@ -54,11 +54,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::AND, false, - RegisterParameter(std::to_array({ + RegisterParameter{std::to_array({ {9, 1}, {3, 4}, - })), - RegisterParameter({8, 5}) + })}, + RegisterParameter{{8, 5}} >; using Andi = Opcode< @@ -68,11 +68,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::ANDI, false, std::nullopt, - RegisterParameter({7, 4}), - InstructionParameter(std::to_array({ + RegisterParameter{{7, 4}}, + InstructionParameter{std::to_array({ {11, 4}, {3, 4}, - })) + })} >; using Asr = Opcode< @@ -82,7 +82,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::ASR, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using Bclr = Opcode< @@ -97,7 +97,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({6, 3}) + InstructionParameter{{6, 3}} >; using Bld = Opcode< @@ -107,11 +107,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::BLD, false, std::nullopt, - RegisterParameter({8, 5}), + RegisterParameter{{8, 5}}, std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({2, 3}) + InstructionParameter{{2, 3}} >; using Brbc = Opcode< @@ -124,9 +124,9 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}), + InstructionParameter{{9, 7}}, std::nullopt, - InstructionParameter({2, 3}) + InstructionParameter{{2, 3}} >; using Brbs = Opcode< @@ -139,9 +139,9 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}), + InstructionParameter{{9, 7}}, std::nullopt, - InstructionParameter({2, 3}) + InstructionParameter{{2, 3}} >; using Brcc = Opcode< @@ -154,7 +154,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}) + InstructionParameter{{9, 7}} >; using Brcs = Opcode< @@ -167,7 +167,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}) + InstructionParameter{{9, 7}} >; using Break = Opcode< @@ -188,7 +188,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}) + InstructionParameter{{9, 7}} >; using Brge = Opcode< @@ -201,7 +201,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}) + InstructionParameter{{9, 7}} >; using Brhc = Opcode< @@ -214,7 +214,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}) + InstructionParameter{{9, 7}} >; using Brhs = Opcode< @@ -227,7 +227,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}) + InstructionParameter{{9, 7}} >; using Brid = Opcode< @@ -240,7 +240,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}) + InstructionParameter{{9, 7}} >; using Brie = Opcode< @@ -253,7 +253,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}) + InstructionParameter{{9, 7}} >; using Brlo = Opcode< @@ -266,7 +266,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}) + InstructionParameter{{9, 7}} >; using Brlt = Opcode< @@ -279,7 +279,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}) + InstructionParameter{{9, 7}} >; using Brmi = Opcode< @@ -292,7 +292,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}) + InstructionParameter{{9, 7}} >; using Brne = Opcode< @@ -305,7 +305,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}) + InstructionParameter{{9, 7}} >; using Brpl = Opcode< @@ -318,7 +318,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}) + InstructionParameter{{9, 7}} >; using Brsh = Opcode< @@ -331,7 +331,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}) + InstructionParameter{{9, 7}} >; using Brtc = Opcode< @@ -344,7 +344,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}) + InstructionParameter{{9, 7}} >; using Brts = Opcode< @@ -357,7 +357,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}) + InstructionParameter{{9, 7}} >; using Brvc = Opcode< @@ -370,7 +370,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}) + InstructionParameter{{9, 7}} >; using Brvs = Opcode< @@ -383,7 +383,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({9, 7}) + InstructionParameter{{9, 7}} >; using Bset = Opcode< @@ -398,7 +398,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({6, 3}) + InstructionParameter{{6, 3}} >; using Bst = Opcode< @@ -407,12 +407,12 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::BST, false, - RegisterParameter({8, 5}), + RegisterParameter{{8, 5}}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({2, 3}) + InstructionParameter{{2, 3}} >; using Call = Opcode< @@ -424,10 +424,10 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter(std::to_array({ + InstructionParameter{std::to_array({ {24, 5}, {16, 17}, - })) + })} >; using Cbi = Opcode< @@ -437,11 +437,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::CBI, false, std::nullopt, - RegisterParameter({7, 5}), + RegisterParameter{{7, 5}}, std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({2, 3}) + InstructionParameter{{2, 3}} >; using Cbr = Opcode< @@ -451,11 +451,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::CBR, false, std::nullopt, - RegisterParameter({7, 4}), - InstructionParameter(std::to_array({ + RegisterParameter{{7, 4}}, + InstructionParameter{std::to_array({ {11, 4}, {3, 4}, - })) + })} >; using Clc = Opcode< @@ -497,7 +497,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::CLR, false, std::nullopt, - RegisterParameter({9, 10}) + RegisterParameter{{9, 10}} >; using Cls = Opcode< @@ -539,7 +539,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::COM, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using Cp = Opcode< @@ -548,11 +548,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::CP, false, - RegisterParameter(std::to_array({ + RegisterParameter{std::to_array({ {9, 1}, {3, 4}, - })), - RegisterParameter({8, 5}) + })}, + RegisterParameter{{8, 5}} >; using Cpc = Opcode< @@ -561,11 +561,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::CPC, false, - RegisterParameter(std::to_array({ + RegisterParameter{std::to_array({ {9, 1}, {3, 4}, - })), - RegisterParameter({8, 5}) + })}, + RegisterParameter{{8, 5}} >; using Cpi = Opcode< @@ -574,12 +574,12 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::CPI, false, - RegisterParameter({7, 4}), + RegisterParameter{{7, 4}}, std::nullopt, - InstructionParameter(std::to_array({ + InstructionParameter{std::to_array({ {11, 4}, {3, 4}, - })) + })} >; using Cpse = Opcode< @@ -588,11 +588,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::CPSE, true, - RegisterParameter(std::to_array({ + RegisterParameter{std::to_array({ {9, 1}, {3, 4}, - })), - RegisterParameter({8, 5}), + })}, + RegisterParameter{{8, 5}}, std::nullopt, std::nullopt, std::nullopt, @@ -611,7 +611,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::DEC, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using Des = Opcode< @@ -622,7 +622,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes false, std::nullopt, std::nullopt, - InstructionParameter({7, 4}) + InstructionParameter{{7, 4}} >; using Eicall = Opcode< @@ -656,7 +656,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::ELPM, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using Elpm3 = Opcode< @@ -666,7 +666,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::ELPM, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using Eor = Opcode< @@ -675,11 +675,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::EOR, false, - RegisterParameter(std::to_array({ + RegisterParameter{std::to_array({ {9, 1}, {3, 4}, - })), - RegisterParameter({8, 5}) + })}, + RegisterParameter{{8, 5}} >; using Fmul = Opcode< @@ -688,8 +688,8 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::FMUL, false, - RegisterParameter({2, 3}), - RegisterParameter({6, 3}) + RegisterParameter{{2, 3}}, + RegisterParameter{{6, 3}} >; using Fmuls = Opcode< @@ -698,8 +698,8 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::FMULS, false, - RegisterParameter({2, 3}), - RegisterParameter({6, 3}) + RegisterParameter{{2, 3}}, + RegisterParameter{{6, 3}} >; using Fmulsu = Opcode< @@ -708,8 +708,8 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::FMULSU, false, - RegisterParameter({2, 3}), - RegisterParameter({6, 3}) + RegisterParameter{{2, 3}}, + RegisterParameter{{6, 3}} >; using Icall = Opcode< @@ -735,16 +735,16 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::IN, false, std::nullopt, - RegisterParameter({8, 5}), + RegisterParameter{{8, 5}}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, - InstructionParameter(std::to_array({ + InstructionParameter{std::to_array({ {10, 2}, {3, 4} - })) + })} >; using Inc = Opcode< @@ -754,7 +754,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::INC, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using Jmp = Opcode< @@ -766,10 +766,10 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter(std::to_array({ + InstructionParameter{std::to_array({ {24, 5}, {16, 17} - })) + })} >; using Lac = Opcode< @@ -779,7 +779,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LAC, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using Las = Opcode< @@ -789,7 +789,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LAS, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using Lat = Opcode< @@ -799,7 +799,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LAT, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using LdX1 = Opcode< @@ -809,7 +809,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LD, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using LdX2 = Opcode< @@ -819,7 +819,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LD, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using LdX3 = Opcode< @@ -829,7 +829,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LD, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using LdY1 = Opcode< @@ -839,7 +839,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LD, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using LdY2 = Opcode< @@ -849,7 +849,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LD, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using LdY3 = Opcode< @@ -859,7 +859,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LD, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using LddY = Opcode< @@ -869,18 +869,18 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LDD, false, std::nullopt, - RegisterParameter({8, 5}), + RegisterParameter{{8, 5}}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, - InstructionParameter(std::to_array({ + InstructionParameter{std::to_array({ {13, 1}, {11, 2}, {2, 3} - })) + })} >; using LdZ1 = Opcode< @@ -890,7 +890,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LD, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using LdZ2 = Opcode< @@ -900,7 +900,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LD, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using LdZ3 = Opcode< @@ -910,7 +910,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LD, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using LddZ = Opcode< @@ -920,18 +920,18 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LDD, false, std::nullopt, - RegisterParameter({8, 5}), + RegisterParameter{{8, 5}}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, - InstructionParameter(std::to_array({ + InstructionParameter{std::to_array({ {13, 1}, {11, 2}, {2, 3} - })) + })} >; using Ldi = Opcode< @@ -941,11 +941,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LDI, false, std::nullopt, - RegisterParameter(16, false, {7, 4}), - InstructionParameter(std::to_array({ + RegisterParameter{16, false, {7, 4}}, + InstructionParameter{std::to_array({ {11, 4}, {3, 4} - })) + })} >; using Lds1 = Opcode< @@ -955,14 +955,14 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LDS, false, std::nullopt, - RegisterParameter({24, 5}), + RegisterParameter{{24, 5}}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({15, 16}) + InstructionParameter{{15, 16}} >; using Lds2 = Opcode< @@ -972,17 +972,17 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LDS, false, std::nullopt, - RegisterParameter({7, 4}), + RegisterParameter{{7, 4}}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, - InstructionParameter(std::to_array({ + InstructionParameter{std::to_array({ {10, 3}, {3, 4} - })) + })} >; using Lpm1 = Opcode< @@ -1000,7 +1000,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LPM, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using Lpm3 = Opcode< @@ -1010,7 +1010,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LPM, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using Lsl = Opcode< @@ -1020,7 +1020,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LSL, false, std::nullopt, - RegisterParameter({9, 10}) + RegisterParameter{{9, 10}} >; using Lsr = Opcode< @@ -1030,7 +1030,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::LSR, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using Mov = Opcode< @@ -1039,11 +1039,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::MOV, false, - RegisterParameter(std::to_array({ + RegisterParameter{std::to_array({ {9, 1}, {3, 4}, - })), - RegisterParameter({8, 5}) + })}, + RegisterParameter{{8, 5}} >; using Movw = Opcode< @@ -1052,8 +1052,8 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::MOVW, false, - RegisterParameter(0, true, {3, 4}), - RegisterParameter(0, true, {7, 4}) + RegisterParameter{0, true, {3, 4}}, + RegisterParameter{0, true, {7, 4}} >; using Mul = Opcode< @@ -1062,11 +1062,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::MUL, false, - RegisterParameter(std::to_array({ + RegisterParameter{std::to_array({ {9, 1}, {3, 4}, - })), - RegisterParameter({8, 5}) + })}, + RegisterParameter{{8, 5}} >; using Muls = Opcode< @@ -1075,8 +1075,8 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::MULS, false, - RegisterParameter({3, 4}), - RegisterParameter({7, 4}) + RegisterParameter{{3, 4}}, + RegisterParameter{{7, 4}} >; using Mulsu = Opcode< @@ -1085,8 +1085,8 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::MULSU, false, - RegisterParameter({2, 3}), - RegisterParameter({6, 3}) + RegisterParameter{{2, 3}}, + RegisterParameter{{6, 3}} >; using Neg = Opcode< @@ -1096,7 +1096,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::NEG, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using Nop = Opcode< @@ -1113,11 +1113,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::OR, false, - RegisterParameter(std::to_array({ + RegisterParameter{std::to_array({ {9, 1}, {3, 4}, - })), - RegisterParameter({8, 5}) + })}, + RegisterParameter{{8, 5}} >; using Ori = Opcode< @@ -1127,11 +1127,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::ORI, false, std::nullopt, - RegisterParameter({7, 4}), - InstructionParameter(std::to_array({ + RegisterParameter{{7, 4}}, + InstructionParameter{std::to_array({ {11, 4}, {3, 4}, - })) + })} >; using Out = Opcode< @@ -1140,17 +1140,17 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::OUT, false, - RegisterParameter({8, 5}), + RegisterParameter{{8, 5}}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, std::nullopt, - InstructionParameter(std::to_array({ + InstructionParameter{std::to_array({ {10, 2}, {3, 4}, - })) + })} >; using Pop = Opcode< @@ -1159,7 +1159,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::POP, false, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using Push = Opcode< @@ -1168,7 +1168,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::PUSH, false, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using Rcall = Opcode< @@ -1181,7 +1181,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({11, 12}) + InstructionParameter{{11, 12}} >; using Ret = Opcode< @@ -1210,7 +1210,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({11, 12}) + InstructionParameter{{11, 12}} >; using Rol = Opcode< @@ -1220,7 +1220,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::ROL, false, std::nullopt, - RegisterParameter({9, 10}) + RegisterParameter{{9, 10}} >; using Ror = Opcode< @@ -1230,7 +1230,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::ROR, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using Sbc = Opcode< @@ -1239,11 +1239,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::SBC, false, - RegisterParameter(std::to_array({ + RegisterParameter{std::to_array({ {9, 1}, {3, 4}, - })), - RegisterParameter({8, 5}) + })}, + RegisterParameter{{8, 5}} >; using Sbci = Opcode< @@ -1253,11 +1253,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::SBCI, false, std::nullopt, - RegisterParameter(16, false, {7, 4}), - InstructionParameter(std::to_array({ + RegisterParameter{16, false, {7, 4}}, + InstructionParameter{std::to_array({ {11, 4}, {3, 4}, - })) + })} >; using Sbi = Opcode< @@ -1271,9 +1271,9 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({2, 3}), + InstructionParameter{{2, 3}}, std::nullopt, - InstructionParameter({7, 5}) + InstructionParameter{{7, 5}} >; using Sbic = Opcode< @@ -1287,9 +1287,9 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({2, 3}), + InstructionParameter{{2, 3}}, std::nullopt, - InstructionParameter({7, 5}), + InstructionParameter{{7, 5}}, std::nullopt, std::nullopt, true @@ -1306,9 +1306,9 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({2, 3}), + InstructionParameter{{2, 3}}, std::nullopt, - InstructionParameter({7, 5}), + InstructionParameter{{7, 5}}, std::nullopt, std::nullopt, true @@ -1321,11 +1321,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::SBIW, false, std::nullopt, - RegisterParameter(24, true, {5, 2}), - InstructionParameter(std::to_array({ + RegisterParameter{24, true, {5, 2}}, + InstructionParameter{std::to_array({ {7, 2}, {3, 4}, - })) + })} >; using Sbr = Opcode< @@ -1335,11 +1335,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::SBR, false, std::nullopt, - RegisterParameter(16, true, {7, 4}), - InstructionParameter(std::to_array({ + RegisterParameter{16, true, {7, 4}}, + InstructionParameter{std::to_array({ {11, 4}, {3, 4}, - })) + })} >; using Sbrc = Opcode< @@ -1348,12 +1348,12 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::SBRC, true, - RegisterParameter({8, 5}), + RegisterParameter{{8, 5}}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({2, 3}), + InstructionParameter{{2, 3}}, std::nullopt, std::nullopt, std::nullopt, @@ -1367,12 +1367,12 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::SBRS, true, - RegisterParameter({8, 5}), + RegisterParameter{{8, 5}}, std::nullopt, std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({2, 3}), + InstructionParameter{{2, 3}}, std::nullopt, std::nullopt, std::nullopt, @@ -1419,7 +1419,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::SER, false, std::nullopt, - RegisterParameter({7, 4}) + RegisterParameter{{7, 4}} >; using Ses = Opcode< @@ -1484,7 +1484,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::ST, false, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using StX2 = Opcode< @@ -1493,7 +1493,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::ST, false, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using StX3 = Opcode< @@ -1502,7 +1502,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::ST, false, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using StY1 = Opcode< @@ -1511,7 +1511,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::ST, false, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using StY2 = Opcode< @@ -1520,7 +1520,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::ST, false, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using StY3 = Opcode< @@ -1529,7 +1529,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::ST, false, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using StdY = Opcode< @@ -1538,7 +1538,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::STD, false, - RegisterParameter({8, 5}), + RegisterParameter{{8, 5}}, std::nullopt, std::nullopt, std::nullopt, @@ -1547,11 +1547,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter(std::to_array({ + InstructionParameter{std::to_array({ {13, 1}, {11, 2}, - {2, 3}, - })) + {2, 3} + })} >; using StZ1 = Opcode< @@ -1560,7 +1560,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::ST, false, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using StZ2 = Opcode< @@ -1569,7 +1569,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::ST, false, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using StZ3 = Opcode< @@ -1578,7 +1578,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::ST, false, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using StdZ = Opcode< @@ -1587,7 +1587,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::STD, false, - RegisterParameter({8, 5}), + RegisterParameter{{8, 5}}, std::nullopt, std::nullopt, std::nullopt, @@ -1596,11 +1596,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter(std::to_array({ + InstructionParameter{std::to_array({ {13, 1}, {11, 2}, - {2, 3}, - })) + {2, 3} + })} >; using Sts1 = Opcode< @@ -1609,7 +1609,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 2, Instruction::Mnemonic::STS, false, - RegisterParameter({24, 5}), + RegisterParameter{{24, 5}}, std::nullopt, std::nullopt, std::nullopt, @@ -1617,7 +1617,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes std::nullopt, std::nullopt, std::nullopt, - InstructionParameter({15, 16}) + InstructionParameter{{15, 16}} >; using Sts2 = Opcode< @@ -1626,7 +1626,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::STS, false, - RegisterParameter({7, 4}), + RegisterParameter{{7, 4}}, std::nullopt, /* * TODO: @@ -1636,10 +1636,10 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes * We're not actually making use of data space addresses ATM, so it's not a big deal for now. Will sort it * later. */ - InstructionParameter(std::to_array({ + InstructionParameter{std::to_array({ {10, 3}, {3, 4}, - })) + })} >; using Sub = Opcode< @@ -1648,11 +1648,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::SUB, false, - RegisterParameter(std::to_array({ + RegisterParameter{std::to_array({ {9, 1}, {3, 4}, - })), - RegisterParameter({8, 5}) + })}, + RegisterParameter{{8, 5}} >; using Subi = Opcode< @@ -1662,11 +1662,11 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::SUBI, false, std::nullopt, - RegisterParameter({7, 4}), - RegisterParameter(std::to_array({ + RegisterParameter{{7, 4}}, + RegisterParameter{std::to_array({ {11, 4}, {3, 4}, - })) + })} >; using Swap = Opcode< @@ -1676,7 +1676,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::SWAP, false, std::nullopt, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; using Tst = Opcode< @@ -1686,7 +1686,7 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes Instruction::Mnemonic::TST, false, std::nullopt, - RegisterParameter({9, 10}) + RegisterParameter{{9, 10}} >; /* @@ -1729,6 +1729,6 @@ namespace Targets::Microchip::Avr::Avr8Bit::OpcodeDecoder::Opcodes 1, Instruction::Mnemonic::XCH, false, - RegisterParameter({8, 5}) + RegisterParameter{{8, 5}} >; } diff --git a/src/Targets/Microchip/AVR/AVR8/ProgramMemorySection.hpp b/src/Targets/Microchip/AVR8/ProgramMemorySection.hpp similarity index 75% rename from src/Targets/Microchip/AVR/AVR8/ProgramMemorySection.hpp rename to src/Targets/Microchip/AVR8/ProgramMemorySection.hpp index 7e517408..5a023e8c 100644 --- a/src/Targets/Microchip/AVR/AVR8/ProgramMemorySection.hpp +++ b/src/Targets/Microchip/AVR8/ProgramMemorySection.hpp @@ -2,7 +2,7 @@ #include -namespace Targets::Microchip::Avr::Avr8Bit +namespace Targets::Microchip::Avr8 { enum class ProgramMemorySection: std::uint8_t { diff --git a/src/Targets/Microchip/AVR/AVR8/ProgrammingSession.hpp b/src/Targets/Microchip/AVR8/ProgrammingSession.hpp similarity index 93% rename from src/Targets/Microchip/AVR/AVR8/ProgrammingSession.hpp rename to src/Targets/Microchip/AVR8/ProgrammingSession.hpp index ac9fdcdc..277d7b51 100644 --- a/src/Targets/Microchip/AVR/AVR8/ProgrammingSession.hpp +++ b/src/Targets/Microchip/AVR8/ProgrammingSession.hpp @@ -2,7 +2,7 @@ #include -namespace Targets::Microchip::Avr::Avr8Bit +namespace Targets::Microchip::Avr8 { /** * Information relating to a specific AVR8 programming session. diff --git a/src/Targets/Microchip/AVR8/TargetDescriptionFile.cpp b/src/Targets/Microchip/AVR8/TargetDescriptionFile.cpp new file mode 100644 index 00000000..5db876ef --- /dev/null +++ b/src/Targets/Microchip/AVR8/TargetDescriptionFile.cpp @@ -0,0 +1,195 @@ +#include "TargetDescriptionFile.hpp" + +#include "src/Services/PathService.hpp" +#include "src/Services/StringService.hpp" +#include "src/Logger/Logger.hpp" + +#include "src/Exceptions/Exception.hpp" +#include "src/Targets/TargetDescription/Exceptions/TargetDescriptionParsingFailureException.hpp" + +namespace Targets::Microchip::Avr8 +{ + using Targets::TargetDescription::RegisterGroup; + using Targets::TargetDescription::AddressSpace; + using Targets::TargetDescription::MemorySegment; + using Targets::TargetDescription::Register; + using Targets::TargetDescription::Exceptions::InvalidTargetDescriptionDataException; + using Targets::TargetRegisterDescriptor; + using Services::StringService; + + TargetDescriptionFile::TargetDescriptionFile(const std::string& xmlFilePath) + : Targets::TargetDescription::TargetDescriptionFile(xmlFilePath) + {} + + TargetSignature TargetDescriptionFile::getTargetSignature() const { + const auto& signatureGroup = this->getPropertyGroup("signatures"); + + return { + StringService::toUint8(signatureGroup.getProperty("signature0").value, 16), + StringService::toUint8(signatureGroup.getProperty("signature1").value, 16), + StringService::toUint8(signatureGroup.getProperty("signature2").value, 16) + }; + } + + Family TargetDescriptionFile::getAvrFamily() const { + static const auto targetFamiliesByName = std::map{ + {"MEGA", Family::MEGA}, + {"XMEGA", Family::XMEGA}, + {"TINY", Family::TINY}, + {"DA", Family::DA}, + {"DB", Family::DB}, + {"DD", Family::DD}, + {"EA", Family::EA}, + }; + + const auto familyIt = targetFamiliesByName.find(this->getDeviceAttribute("avr-family")); + if (familyIt == targetFamiliesByName.end()) { + throw InvalidTargetDescriptionDataException{"Unknown AVR family name in target description file"}; + } + + return familyIt->second; + } + + const TargetDescription::AddressSpace& TargetDescriptionFile::getRegisterFileAddressSpace() const { + /* + * On some AVRs, the register file is accessible via the data address space. On the newer UPDI and PDI AVRs, + * it has a dedicated address space. + */ + const auto addressSpace = this->tryGetAddressSpace("register_file"); + return addressSpace.has_value() + ? addressSpace->get() + : this->getAddressSpace("data"); + } + + const TargetDescription::AddressSpace& TargetDescriptionFile::getProgramAddressSpace() const { + return this->getAddressSpace("prog"); + } + + const TargetDescription::AddressSpace& TargetDescriptionFile::getDataAddressSpace() const { + return this->getAddressSpace("data"); + } + + const TargetDescription::AddressSpace& TargetDescriptionFile::getEepromAddressSpace() const { + const auto addressSpace = this->tryGetAddressSpace("eeprom"); + return addressSpace.has_value() + ? addressSpace->get() + : this->getAddressSpace("data"); + } + + const TargetDescription::AddressSpace& TargetDescriptionFile::getIoAddressSpace() const { + return this->getAddressSpace("data"); + } + + const TargetDescription::AddressSpace& TargetDescriptionFile::getSignatureAddressSpace() const { + const auto addressSpace = this->tryGetAddressSpace("signatures"); + return addressSpace.has_value() + ? addressSpace->get() + : this->getAddressSpace("data"); + } + + const TargetDescription::AddressSpace& TargetDescriptionFile::getFuseAddressSpace() const { + const auto addressSpace = this->tryGetAddressSpace("fuses"); + return addressSpace.has_value() + ? addressSpace->get() + : this->getAddressSpace("data"); + } + + const TargetDescription::AddressSpace& TargetDescriptionFile::getLockbitAddressSpace() const { + const auto addressSpace = this->tryGetAddressSpace("lockbits"); + return addressSpace.has_value() + ? addressSpace->get() + : this->getAddressSpace("data"); + } + + const TargetDescription::MemorySegment& TargetDescriptionFile::getProgramMemorySegment() const { + return this->getProgramAddressSpace().getMemorySegment("internal_program_memory"); + } + + const TargetDescription::MemorySegment& TargetDescriptionFile::getRamMemorySegment() const { + return this->getDataAddressSpace().getMemorySegment("internal_ram"); + } + + const TargetDescription::MemorySegment& TargetDescriptionFile::getEepromMemorySegment() const { + return this->getEepromAddressSpace().getMemorySegment("internal_eeprom"); + } + + const TargetDescription::MemorySegment& TargetDescriptionFile::getIoMemorySegment() const { + const auto addressSpace = this->getIoAddressSpace(); + const auto segment = addressSpace.tryGetMemorySegment("io"); + return segment.has_value() + ? segment->get() + : addressSpace.getMemorySegment("mapped_io"); + } + + const TargetDescription::MemorySegment& TargetDescriptionFile::getSignatureMemorySegment() const { + return this->getSignatureAddressSpace().getMemorySegment("signatures"); + } + + const TargetDescription::MemorySegment& TargetDescriptionFile::getFuseMemorySegment() const { + return this->getFuseAddressSpace().getMemorySegment("fuses"); + } + + const TargetDescription::MemorySegment& TargetDescriptionFile::getLockbitMemorySegment() const { + return this->getLockbitAddressSpace().getMemorySegment("lockbits"); + } + + TargetAddressSpaceDescriptor TargetDescriptionFile::getDataAddressSpaceDescriptor() const { + return this->targetAddressSpaceDescriptorFromAddressSpace(this->getDataAddressSpace()); + } + + TargetAddressSpaceDescriptor TargetDescriptionFile::getFuseAddressSpaceDescriptor() const { + return this->targetAddressSpaceDescriptorFromAddressSpace(this->getFuseAddressSpace()); + } + + TargetMemorySegmentDescriptor TargetDescriptionFile::getRamMemorySegmentDescriptor() const { + return this->targetMemorySegmentDescriptorFromMemorySegment( + this->getRamMemorySegment(), + this->getDataAddressSpace() + ); + } + + TargetMemorySegmentDescriptor TargetDescriptionFile::getFuseMemorySegmentDescriptor() const { + return this->targetMemorySegmentDescriptorFromMemorySegment( + this->getFuseMemorySegment(), + this->getFuseAddressSpace() + ); + } + + TargetMemorySegmentDescriptor TargetDescriptionFile::getIoMemorySegmentDescriptor() const { + return this->targetMemorySegmentDescriptorFromMemorySegment( + this->getIoMemorySegment(), + this->getIoAddressSpace() + ); + } + + TargetPeripheralDescriptor TargetDescriptionFile::getFuseTargetPeripheralDescriptor() const { + return this->getTargetPeripheralDescriptor("fuse"); + } + + Pair< + TargetRegisterDescriptor, + TargetBitFieldDescriptor + > TargetDescriptionFile::getFuseRegisterBitFieldDescriptorPair(const std::string& fuseBitFieldKey) const { + const auto peripheralDescriptor = this->getFuseTargetPeripheralDescriptor(); + const auto pair = peripheralDescriptor.getRegisterGroupDescriptor("fuse").getRegisterBitFieldDescriptorPair( + fuseBitFieldKey + ); + + return {pair.first.clone(), pair.second.clone()}; + } + + std::optional TargetDescriptionFile::getFuseEnableStrategy() const { + const auto fuseEnabledValueProperty = this->tryGetProperty("programming_info", "fuse_enabled_value"); + if (fuseEnabledValueProperty.has_value()) { + if (fuseEnabledValueProperty->get().value == "0") { + return FuseEnableStrategy::CLEAR; + } + + if (fuseEnabledValueProperty->get().value == "1") { + return FuseEnableStrategy::SET; + } + } + + return std::nullopt; + } +} diff --git a/src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp b/src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp new file mode 100644 index 00000000..2e482f55 --- /dev/null +++ b/src/Targets/Microchip/AVR8/TargetDescriptionFile.hpp @@ -0,0 +1,85 @@ +#pragma once + +#include +#include + +#include "src/Targets/TargetDescription/TargetDescriptionFile.hpp" + +#include "src/Targets/TargetRegisterDescriptor.hpp" + +#include "src/Targets/Microchip/AVR8/TargetSignature.hpp" +#include "src/Targets/Microchip/AVR8/Fuse.hpp" + +#include "src/Targets/Microchip/AVR8/Family.hpp" + +namespace Targets::Microchip::Avr8 +{ + /** + * AVR8 TDF + * + * For more information of TDFs, see src/Targets/TargetDescription/README.md + */ + class TargetDescriptionFile: public TargetDescription::TargetDescriptionFile + { + public: + /** + * Extends TDF initialisation to include the loading of physical interfaces for debugging AVR8 targets, among + * other things. + * + * @param xml + */ + explicit TargetDescriptionFile(const std::string& xmlFilePath); + + /** + * Extracts the AVR8 target signature from the TDF. + * + * @return + */ + [[nodiscard]] TargetSignature getTargetSignature() const; + + /** + * Extracts the AVR8 target family from the TDF. + * + * @return + */ + [[nodiscard]] Family getAvrFamily() const; + + [[nodiscard]] const TargetDescription::AddressSpace& getRegisterFileAddressSpace() const; + [[nodiscard]] const TargetDescription::AddressSpace& getProgramAddressSpace() const; + [[nodiscard]] const TargetDescription::AddressSpace& getDataAddressSpace() const; + [[nodiscard]] const TargetDescription::AddressSpace& getEepromAddressSpace() const; + [[nodiscard]] const TargetDescription::AddressSpace& getIoAddressSpace() const; + [[nodiscard]] const TargetDescription::AddressSpace& getSignatureAddressSpace() const; + [[nodiscard]] const TargetDescription::AddressSpace& getFuseAddressSpace() const; + [[nodiscard]] const TargetDescription::AddressSpace& getLockbitAddressSpace() const; + + [[nodiscard]] const TargetDescription::MemorySegment& getProgramMemorySegment() const; + [[nodiscard]] const TargetDescription::MemorySegment& getRamMemorySegment() const; + [[nodiscard]] const TargetDescription::MemorySegment& getEepromMemorySegment() const; + [[nodiscard]] const TargetDescription::MemorySegment& getIoMemorySegment() const; + [[nodiscard]] const TargetDescription::MemorySegment& getSignatureMemorySegment() const; + [[nodiscard]] const TargetDescription::MemorySegment& getFuseMemorySegment() const; + [[nodiscard]] const TargetDescription::MemorySegment& getLockbitMemorySegment() const; + + [[nodiscard]] TargetAddressSpaceDescriptor getDataAddressSpaceDescriptor() const; + [[nodiscard]] TargetAddressSpaceDescriptor getFuseAddressSpaceDescriptor() const; + + [[nodiscard]] TargetMemorySegmentDescriptor getRamMemorySegmentDescriptor() const; + [[nodiscard]] TargetMemorySegmentDescriptor getFuseMemorySegmentDescriptor() const; + [[nodiscard]] TargetMemorySegmentDescriptor getIoMemorySegmentDescriptor() const; + + [[nodiscard]] TargetPeripheralDescriptor getFuseTargetPeripheralDescriptor() const; + [[nodiscard]] Pair< + TargetRegisterDescriptor, + TargetBitFieldDescriptor + > getFuseRegisterBitFieldDescriptorPair(const std::string& fuseBitFieldKey) const; + + /** + * Extracts the target's fuse enable strategy. + * + * @return + * std::nullopt if the TDF doesn't contain a fuse enable strategy. + */ + [[nodiscard]] std::optional getFuseEnableStrategy() const; + }; +} diff --git a/src/Targets/Microchip/AVR/TargetSignature.hpp b/src/Targets/Microchip/AVR8/TargetSignature.hpp similarity index 77% rename from src/Targets/Microchip/AVR/TargetSignature.hpp rename to src/Targets/Microchip/AVR8/TargetSignature.hpp index a374a439..8aba66af 100644 --- a/src/Targets/Microchip/AVR/TargetSignature.hpp +++ b/src/Targets/Microchip/AVR8/TargetSignature.hpp @@ -4,10 +4,12 @@ #include #include -namespace Targets::Microchip::Avr +#include "src/Services/StringService.hpp" + +namespace Targets::Microchip::Avr8 { /** - * All AVR targets carry a three-byte signature that is *usually* unique to the target. + * All AVR8 targets carry a three-byte signature that is *usually* unique to the target. * * The AVR target signature consists of three bytes: 0xAABBCC * Byte AA (byteZero) identifies the manufacture of the target (usually 1E for Atmel/Microchip) @@ -15,11 +17,7 @@ namespace Targets::Microchip::Avr * Byte CC (byteTwo) identifies the target * * Some AVR targets have been found to carry identical signatures. For example, the AT90PWM1, AT90PWM2B - * and the AT90PWM3B all carry a signature of 0x1E9383. Although these devices may not differ in - * significant ways, Bloom does still take duplicate signatures into account, to ensure that the correct - * target description file is used. - * - * This class represents an AVR target signature. + * and the AT90PWM3B all carry a signature of 0x1E9383. */ struct TargetSignature { @@ -35,7 +33,7 @@ namespace Targets::Microchip::Avr {}; explicit TargetSignature(const std::string& hex) { - const auto signature = static_cast(std::stoul(hex, nullptr, 16)); + const auto signature = Services::StringService::toUint32(hex, 16); this->byteZero = static_cast(signature >> 16); this->byteOne = static_cast(signature >> 8); @@ -43,7 +41,7 @@ namespace Targets::Microchip::Avr } [[nodiscard]] std::string toHex() const { - std::stringstream stream; + auto stream = std::stringstream{}; stream << std::hex << std::setfill('0'); stream << std::setw(2) << static_cast(this->byteZero); stream << std::setw(2) << static_cast(this->byteOne); diff --git a/src/Targets/RiscV/Registers/RegisterNumbers.hpp b/src/Targets/RiscV/Registers/RegisterNumbers.hpp deleted file mode 100644 index bbcdf95d..00000000 --- a/src/Targets/RiscV/Registers/RegisterNumbers.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include - -#include "src/Targets/RiscV/RiscVGeneric.hpp" - -namespace Targets::RiscV::Registers -{ - enum class RegisterNumberBase: ::Targets::RiscV::RegisterNumber - { - CSR = 0x0000, - GPR = 0x1000, - FPR = 0x1020, - OTHER = 0xc000, - }; - - enum class RegisterNumber: ::Targets::RiscV::RegisterNumber - { - DEBUG_CONTROL_STATUS_REGISTER = 0x07b0, - DEBUG_PROGRAM_COUNTER_REGISTER = 0x07b1, - STACK_POINTER_X2 = 0x1002, - }; -} diff --git a/src/Targets/RiscV/RiscV.cpp b/src/Targets/RiscV/RiscV.cpp index 6d5e55e8..07321c25 100644 --- a/src/Targets/RiscV/RiscV.cpp +++ b/src/Targets/RiscV/RiscV.cpp @@ -1,67 +1,65 @@ #include "RiscV.hpp" -#include -#include -#include +#include #include +#include -#include "Registers/RegisterNumbers.hpp" -#include "DebugModule/Registers/RegisterAddresses.hpp" -#include "DebugModule/Registers/RegisterAccessControlField.hpp" -#include "DebugModule/Registers/MemoryAccessControlField.hpp" +#include "src/Helpers/Pair.hpp" +#include "src/Services/StringService.hpp" +#include "src/Logger/Logger.hpp" #include "src/Exceptions/Exception.hpp" #include "src/Exceptions/InvalidConfig.hpp" #include "src/TargetController/Exceptions/TargetOperationFailure.hpp" -#include "src/Services/StringService.hpp" - -#include "src/Logger/Logger.hpp" - namespace Targets::RiscV { - using Registers::DebugControlStatusRegister; - - using DebugModule::Registers::RegisterAddress; - using DebugModule::Registers::ControlRegister; - using DebugModule::Registers::StatusRegister; - using DebugModule::Registers::AbstractControlStatusRegister; - using DebugModule::Registers::AbstractCommandRegister; - RiscV::RiscV( const TargetConfig& targetConfig, - TargetDescription::TargetDescriptionFile&& targetDescriptionFile + TargetDescriptionFile&& targetDescriptionFile ) - : targetDescriptionFile(targetDescriptionFile) - , stackPointerRegisterDescriptor( - RiscVRegisterDescriptor( - TargetRegisterType::STACK_POINTER, - static_cast(Registers::RegisterNumber::STACK_POINTER_X2), - 4, - TargetMemoryType::OTHER, - "SP", - "CPU", - "Stack Pointer Register", - TargetRegisterAccess(true, true) + : targetConfig(RiscVTargetConfig(targetConfig)) + , targetDescriptionFile(std::move(targetDescriptionFile)) + , cpuRegisterAddressSpaceDescriptor(RiscV::generateCpuRegisterAddressSpaceDescriptor()) + , csrMemorySegmentDescriptor(this->cpuRegisterAddressSpaceDescriptor.getMemorySegmentDescriptor("cs_registers")) + , gprMemorySegmentDescriptor(this->cpuRegisterAddressSpaceDescriptor.getMemorySegmentDescriptor("gp_registers")) + , cpuPeripheralDescriptor( + RiscV::generateCpuPeripheralDescriptor( + this->cpuRegisterAddressSpaceDescriptor, + this->csrMemorySegmentDescriptor, + this->gprMemorySegmentDescriptor ) ) - { - this->loadRegisterDescriptors(); - } + , csrGroupDescriptor(this->cpuPeripheralDescriptor.getRegisterGroupDescriptor("csr")) + , gprGroupDescriptor(this->cpuPeripheralDescriptor.getRegisterGroupDescriptor("gpr")) + , pcRegisterDescriptor(this->csrGroupDescriptor.getRegisterDescriptor("dpc")) + , spRegisterDescriptor(this->gprGroupDescriptor.getRegisterDescriptor("x2")) + , sysAddressSpaceDescriptor(this->targetDescriptionFile.getSystemAddressSpaceDescriptor()) + {} bool RiscV::supportsDebugTool(DebugTool* debugTool) { - return debugTool->getRiscVDebugInterface() != nullptr; + return + debugTool->getRiscVDebugInterface(this->targetDescriptionFile, this->targetConfig) != nullptr + && debugTool->getRiscVIdentificationInterface(this->targetDescriptionFile, this->targetConfig) != nullptr + ; } void RiscV::setDebugTool(DebugTool* debugTool) { - this->riscVDebugInterface = debugTool->getRiscVDebugInterface(); - this->riscVProgramInterface = debugTool->getRiscVProgramInterface(); + this->riscVDebugInterface = debugTool->getRiscVDebugInterface(this->targetDescriptionFile, this->targetConfig); + this->riscVProgramInterface = debugTool->getRiscVProgramInterface( + this->targetDescriptionFile, + this->targetConfig + ); + this->riscVIdInterface = debugTool->getRiscVIdentificationInterface( + this->targetDescriptionFile, + this->targetConfig + ); } void RiscV::activate() { - this->riscVDebugInterface->activate({}); + this->riscVDebugInterface->activate(); - const auto deviceId = this->riscVDebugInterface->getDeviceId(); + const auto deviceId = this->riscVIdInterface->getDeviceId(); const auto tdfDeviceId = this->targetDescriptionFile.getTargetId(); if (deviceId != tdfDeviceId) { throw Exceptions::InvalidConfig( @@ -69,178 +67,57 @@ namespace Targets::RiscV ". Please check target configuration." ); } - - this->hartIndices = this->discoverHartIndices(); - - if (this->hartIndices.empty()) { - throw Exceptions::TargetOperationFailure("Failed to discover a single RISC-V hart"); - } - - Logger::debug("Discovered RISC-V harts: " + std::to_string(this->hartIndices.size())); - - /* - * We only support MCUs with a single hart, for now. So select the first index and ensure that this is - * explicitly communicated to the user. - */ - if (this->hartIndices.size() > 1) { - Logger::warning( - "Bloom only supports debugging a single RISC-V hart - selecting first available hart" - ); - } - - this->selectedHartIndex = *(this->hartIndices.begin()); - Logger::info("Selected RISC-V hart index: " + std::to_string(this->selectedHartIndex)); - - /* - * Disabling the debug module before enabling it will clear any state from a previous debug session that - * wasn't terminated properly. - */ - this->disableDebugModule(); - this->enableDebugModule(); - - this->stop(); - this->reset(); - - auto debugControlStatusRegister = this->readDebugControlStatusRegister(); - debugControlStatusRegister.breakUMode = true; - debugControlStatusRegister.breakSMode = true; - debugControlStatusRegister.breakMMode = true; - - this->writeDebugControlStatusRegister(debugControlStatusRegister); } void RiscV::deactivate() { - if (this->getState() != TargetState::RUNNING) { + if (this->getExecutionState() != TargetExecutionState::RUNNING) { this->run(); } - this->disableDebugModule(); this->riscVDebugInterface->deactivate(); } - TargetDescriptor RiscV::getDescriptor() { - return TargetDescriptor( + TargetDescriptor RiscV::targetDescriptor() { + auto descriptor = TargetDescriptor{ + this->targetDescriptionFile.getName(), + this->targetDescriptionFile.getFamily(), this->targetDescriptionFile.getTargetId(), - TargetFamily::RISC_V, - this->targetDescriptionFile.getTargetName(), this->targetDescriptionFile.getVendorName(), - { - { - Targets::TargetMemoryType::FLASH, - TargetMemoryDescriptor( - Targets::TargetMemoryType::FLASH, - Targets::TargetMemoryAddressRange(0x00, 0x1000), - Targets::TargetMemoryAccess(true, true, false) - ) - } - }, - {this->registerDescriptorsById.begin(), this->registerDescriptorsById.end()}, - BreakpointResources(0, 0, 0), - {}, - TargetMemoryType::FLASH + this->targetDescriptionFile.targetAddressSpaceDescriptorsByKey(), + this->targetDescriptionFile.targetPeripheralDescriptorsByKey(), + this->targetDescriptionFile.targetPinoutDescriptorsByKey(), + this->targetDescriptionFile.targetVariantDescriptors(), + {} // TODO: populate this + }; + + // Copy the RISC-V CPU register address space and peripheral descriptor + descriptor.addressSpaceDescriptorsByKey.emplace( + this->cpuRegisterAddressSpaceDescriptor.key, + this->cpuRegisterAddressSpaceDescriptor.clone() ); + + descriptor.peripheralDescriptorsByKey.emplace( + this->cpuPeripheralDescriptor.key, + this->cpuPeripheralDescriptor.clone() + ); + + return descriptor; } void RiscV::run(std::optional toAddress) { - auto controlRegister = ControlRegister(); - controlRegister.debugModuleActive = true; - controlRegister.selectedHartIndex = this->selectedHartIndex; - controlRegister.resumeRequest = true; - - this->writeDebugModuleControlRegister(controlRegister); - - constexpr auto maxAttempts = 10; - auto statusRegister = this->readDebugModuleStatusRegister(); - - for (auto attempts = 1; !statusRegister.allResumeAcknowledge && attempts <= maxAttempts; ++attempts) { - std::this_thread::sleep_for(std::chrono::microseconds(10)); - statusRegister = this->readDebugModuleStatusRegister(); - } - - controlRegister.resumeRequest = false; - this->writeDebugModuleControlRegister(controlRegister); - - if (!statusRegister.allResumeAcknowledge) { - throw Exceptions::Exception("Target took too long to acknowledge resume request"); - } + this->riscVDebugInterface->run(); } void RiscV::stop() { - auto controlRegister = ControlRegister(); - controlRegister.debugModuleActive = true; - controlRegister.selectedHartIndex = this->selectedHartIndex; - controlRegister.haltRequest = true; - - this->writeDebugModuleControlRegister(controlRegister); - - constexpr auto maxAttempts = 10; - auto statusRegister = this->readDebugModuleStatusRegister(); - - for (auto attempts = 1; !statusRegister.allHalted && attempts <= maxAttempts; ++attempts) { - std::this_thread::sleep_for(std::chrono::microseconds(10)); - statusRegister = this->readDebugModuleStatusRegister(); - } - - controlRegister.haltRequest = false; - this->writeDebugModuleControlRegister(controlRegister); - - if (!statusRegister.allHalted) { - throw Exceptions::Exception("Target took too long to halt selected harts"); - } + this->riscVDebugInterface->stop(); } void RiscV::step() { - auto debugControlStatusRegister = this->readDebugControlStatusRegister(); - debugControlStatusRegister.step = true; - - this->writeDebugControlStatusRegister(debugControlStatusRegister); - - auto controlRegister = ControlRegister(); - controlRegister.debugModuleActive = true; - controlRegister.selectedHartIndex = this->selectedHartIndex; - controlRegister.resumeRequest = true; - - this->writeDebugModuleControlRegister(controlRegister); - - controlRegister.resumeRequest = false; - this->writeDebugModuleControlRegister(controlRegister); - - debugControlStatusRegister.step = false; - this->writeDebugControlStatusRegister(debugControlStatusRegister); + this->riscVDebugInterface->step(); } void RiscV::reset() { - auto controlRegister = ControlRegister(); - controlRegister.debugModuleActive = true; - controlRegister.selectedHartIndex = this->selectedHartIndex; - controlRegister.setResetHaltRequest = true; - controlRegister.haltRequest = true; - controlRegister.ndmReset = true; - - this->writeDebugModuleControlRegister(controlRegister); - - controlRegister.ndmReset = false; - this->writeDebugModuleControlRegister(controlRegister); - - constexpr auto maxAttempts = 10; - auto statusRegister = this->readDebugModuleStatusRegister(); - - for (auto attempts = 1; !statusRegister.allHaveReset && attempts <= maxAttempts; ++attempts) { - std::this_thread::sleep_for(std::chrono::microseconds(10)); - statusRegister = this->readDebugModuleStatusRegister(); - } - - controlRegister = ControlRegister(); - controlRegister.debugModuleActive = true; - controlRegister.selectedHartIndex = this->selectedHartIndex; - controlRegister.clearResetHaltRequest = true; - controlRegister.acknowledgeHaveReset = true; - - this->writeDebugModuleControlRegister(controlRegister); - - if (!statusRegister.allHaveReset) { - throw Exceptions::Exception("Target took too long to reset"); - } + this->riscVDebugInterface->reset(); } void RiscV::setSoftwareBreakpoint(TargetMemoryAddress address) { @@ -263,211 +140,223 @@ namespace Targets::RiscV } - TargetRegisters RiscV::readRegisters(const TargetRegisterDescriptorIds& descriptorIds) { - auto output = TargetRegisters(); + TargetRegisterDescriptorAndValuePairs RiscV::readRegisters(const TargetRegisterDescriptors& descriptors) { + auto output = TargetRegisterDescriptorAndValuePairs{}; - for (const auto& descriptorId : descriptorIds) { - const auto registerValue = this->readRegister(this->registerDescriptorsById.at(descriptorId).number); - output.emplace_back( - descriptorId, - TargetMemoryBuffer({ - static_cast(registerValue >> 24), - static_cast(registerValue >> 16), - static_cast(registerValue >> 8), - static_cast(registerValue), - }) + /* + * A "system register" is simply a register that we can access via the system address space. + * + * CPU registers (GPRs, CSRs, etc) cannot be accessed via the system address space, so we separate them from + * system registers, in order to access them separately. + */ + auto cpuRegisterDescriptors = TargetRegisterDescriptors{}; + + for (const auto& descriptor : descriptors) { + if (descriptor->addressSpaceId == this->cpuRegisterAddressSpaceDescriptor.id) { + if ( + !this->csrMemorySegmentDescriptor.addressRange.contains(descriptor->startAddress) + && !this->gprMemorySegmentDescriptor.addressRange.contains(descriptor->startAddress) + ) { + throw Exceptions::Exception( + "Cannot access CPU register \"" + descriptor->key + "\" - unknown memory segment" + ); + } + + cpuRegisterDescriptors.emplace_back(descriptor); + continue; + } + + if (descriptor->addressSpaceId != this->sysAddressSpaceDescriptor.id) { + throw Exceptions::Exception( + "Cannot access register \"" + descriptor->key + "\" - unknown address space" + ); + } + + auto value = this->riscVDebugInterface->readMemory( + this->sysAddressSpaceDescriptor, + this->resolveRegisterMemorySegmentDescriptor(*descriptor, this->sysAddressSpaceDescriptor), + descriptor->startAddress, + descriptor->size ); + + if (value.size() > 1 && this->sysAddressSpaceDescriptor.endianness == TargetMemoryEndianness::LITTLE) { + // LSB to MSB + std::reverse(value.begin(), value.end()); + } + + output.emplace_back(*descriptor, std::move(value)); + } + + if (!cpuRegisterDescriptors.empty()) { + auto cpuRegisterValues = this->riscVDebugInterface->readCpuRegisters(cpuRegisterDescriptors); + std::move(cpuRegisterValues.begin(), cpuRegisterValues.end(), std::back_inserter(output)); } return output; } - void RiscV::writeRegisters(const TargetRegisters& registers) { - for (const auto& targetRegister : registers) { - if ((targetRegister.value.size() * 8) > std::numeric_limits::digits) { - throw Exceptions::Exception("Register value bit width exceeds that of std::uintmax_t"); + void RiscV::writeRegisters(const TargetRegisterDescriptorAndValuePairs& registers) { + for (const auto& pair : registers) { + const auto& descriptor = pair.first; + + if (descriptor.addressSpaceId == this->cpuRegisterAddressSpaceDescriptor.id) { + if ( + !this->csrMemorySegmentDescriptor.addressRange.contains(descriptor.startAddress) + && !this->gprMemorySegmentDescriptor.addressRange.contains(descriptor.startAddress) + ) { + throw Exceptions::Exception("Cannot access CPU register - unknown memory segment"); + } + + this->riscVDebugInterface->writeCpuRegisters({pair}); + continue; } - auto registerValue = std::uintmax_t{0}; - - for (const auto& registerByte : targetRegister.value) { - registerValue = (registerValue << 8) | registerByte; + if (descriptor.addressSpaceId != this->sysAddressSpaceDescriptor.id) { + throw Exceptions::Exception( + "Cannot access register \"" + descriptor.key + "\" - unknown address space" + ); } - this->writeRegister( - this->registerDescriptorsById.at(targetRegister.descriptorId).number, - static_cast(registerValue) // TODO: Support larger register values + auto value = pair.second; + + if (value.size() > 1 && this->sysAddressSpaceDescriptor.endianness == TargetMemoryEndianness::LITTLE) { + // MSB to LSB + std::reverse(value.begin(), value.end()); + } + + this->riscVDebugInterface->writeMemory( + this->sysAddressSpaceDescriptor, + this->resolveRegisterMemorySegmentDescriptor(descriptor, this->sysAddressSpaceDescriptor), + descriptor.startAddress, + value ); } } TargetMemoryBuffer RiscV::readMemory( - TargetMemoryType memoryType, + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, TargetMemoryAddress startAddress, TargetMemorySize bytes, const std::set& excludedAddressRanges ) { - using DebugModule::Registers::MemoryAccessControlField; + assert(bytes > 0); - // TODO: excluded addresses + assert(addressSpaceDescriptor.addressRange.contains(startAddress)); + assert(addressSpaceDescriptor.addressRange.contains(startAddress + bytes - 1)); - const auto pageSize = 4; - if ((startAddress % pageSize) != 0 || (bytes % pageSize) != 0) { - // Alignment required - const auto alignedStartAddress = this->alignMemoryAddress(startAddress, pageSize); - const auto alignedBytes = this->alignMemorySize(bytes + (startAddress - alignedStartAddress), pageSize); + assert(memorySegmentDescriptor.addressRange.contains(startAddress)); + assert(memorySegmentDescriptor.addressRange.contains(startAddress + bytes - 1)); - auto memoryBuffer = this->readMemory( - memoryType, - alignedStartAddress, - alignedBytes, - excludedAddressRanges - ); - - const auto offset = memoryBuffer.begin() + (startAddress - alignedStartAddress); - - auto output = TargetMemoryBuffer(); - output.reserve(bytes); - std::move(offset, offset + bytes, std::back_inserter(output)); - - return output; - } - - auto output = TargetMemoryBuffer(); - output.reserve(bytes); - - /* - * We only need to set the address once. No need to update it as we use the post-increment function to - * increment the address. See MemoryAccessControlField::postIncrement - */ - this->riscVDebugInterface->writeDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_1, startAddress); - - auto command = AbstractCommandRegister(); - command.commandType = AbstractCommandRegister::CommandType::MEMORY_ACCESS; - command.control = MemoryAccessControlField( - false, - true, - MemoryAccessControlField::MemorySize::SIZE_32, - false - ).value(); - - for (auto address = startAddress; address <= (startAddress + bytes - 1); address += 4) { - this->executeAbstractCommand(command); - - const auto data = this->riscVDebugInterface->readDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_0); - output.emplace_back(static_cast(data)); - output.emplace_back(static_cast(data >> 8)); - output.emplace_back(static_cast(data >> 16)); - output.emplace_back(static_cast(data >> 24)); - } - - return output; + return this->riscVDebugInterface->readMemory( + addressSpaceDescriptor, + memorySegmentDescriptor, + startAddress, + bytes, + excludedAddressRanges + ); } void RiscV::writeMemory( - TargetMemoryType memoryType, + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, TargetMemoryAddress startAddress, const TargetMemoryBuffer& buffer ) { - using DebugModule::Registers::MemoryAccessControlField; + assert(!buffer.empty()); - constexpr auto alignTo = TargetMemorySize{4}; - const auto bytes = static_cast(buffer.size()); - if ((startAddress % alignTo) != 0 || (bytes % alignTo) != 0) { - /* - * Alignment required - * - * To align the write operation, we read the front and back offset bytes and use them to construct an - * aligned buffer. - */ - const auto alignedStartAddress = this->alignMemoryAddress(startAddress, alignTo); - const auto alignedBytes = this->alignMemorySize(bytes + (startAddress - alignedStartAddress), alignTo); + assert(addressSpaceDescriptor.addressRange.contains(startAddress)); + assert(addressSpaceDescriptor.addressRange.contains( + static_cast(startAddress + buffer.size()) - 1) + ); - auto alignedBuffer = TargetMemoryBuffer(); - alignedBuffer.reserve(alignedBytes); + assert(memorySegmentDescriptor.addressRange.contains(startAddress)); + assert(memorySegmentDescriptor.addressRange.contains( + static_cast(startAddress + buffer.size()) - 1) + ); - if (alignedStartAddress < startAddress) { - // Read the offset bytes required to align the start address - alignedBuffer = this->readMemory( - memoryType, - alignedStartAddress, - (startAddress - alignedStartAddress) - ); - } - - alignedBuffer.insert(alignedBuffer.end(), buffer.begin(), buffer.end()); - assert(alignedBytes > bytes); - - // Read the offset bytes required to align the buffer size - const auto dataBack = this->readMemory( - memoryType, - startAddress + bytes, - alignedBytes - bytes - (startAddress - alignedStartAddress) - ); - alignedBuffer.insert(alignedBuffer.end(), dataBack.begin(), dataBack.end()); - - return this->writeMemory(memoryType, alignedStartAddress, alignedBuffer); - } - - if (memoryType == TargetMemoryType::FLASH && this->riscVProgramInterface != nullptr) { + if (memorySegmentDescriptor.type == TargetMemorySegmentType::FLASH && this->riscVProgramInterface) { return this->riscVProgramInterface->writeFlashMemory(startAddress, buffer); } - this->riscVDebugInterface->writeDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_1, startAddress); - - auto command = AbstractCommandRegister(); - command.commandType = AbstractCommandRegister::CommandType::MEMORY_ACCESS; - command.control = MemoryAccessControlField( - true, - true, - MemoryAccessControlField::MemorySize::SIZE_32, - false - ).value(); - - for (TargetMemoryAddress offset = 0; offset < buffer.size(); offset += 4) { - this->riscVDebugInterface->writeDebugModuleRegister( - RegisterAddress::ABSTRACT_DATA_0, - static_cast( - (buffer[offset + 3] << 24) - | (buffer[offset + 2] << 16) - | (buffer[offset + 1] << 8) - | (buffer[offset]) - ) - ); - - this->executeAbstractCommand(command); - } + return this->riscVDebugInterface->writeMemory( + addressSpaceDescriptor, + memorySegmentDescriptor, + startAddress, + buffer + ); } - void RiscV::eraseMemory(TargetMemoryType memoryType) { + bool RiscV::isProgramMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, + TargetMemoryAddress startAddress, + TargetMemorySize size + ) { + return memorySegmentDescriptor.executable; + } + + void RiscV::eraseMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor + ) { } - TargetState RiscV::getState() { - return this->readDebugModuleStatusRegister().anyRunning ? TargetState::RUNNING : TargetState::STOPPED; + TargetExecutionState RiscV::getExecutionState() { + return this->riscVDebugInterface->getExecutionState(); } TargetMemoryAddress RiscV::getProgramCounter() { - return this->readRegister(Registers::RegisterNumber::DEBUG_PROGRAM_COUNTER_REGISTER); + const auto value = this->riscVDebugInterface->readCpuRegisters({&(this->pcRegisterDescriptor)}).at(0).second; + assert(value.size() == 4); + return static_cast( + (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | (value[3]) + ); } void RiscV::setProgramCounter(TargetMemoryAddress programCounter) { // TODO: test this - this->writeRegister(Registers::RegisterNumber::DEBUG_PROGRAM_COUNTER_REGISTER, programCounter); + this->riscVDebugInterface->writeCpuRegisters({ + { + this->pcRegisterDescriptor, + TargetMemoryBuffer{ + static_cast(programCounter >> 24), + static_cast(programCounter >> 16), + static_cast(programCounter >> 8), + static_cast(programCounter) + } + } + }); } TargetStackPointer RiscV::getStackPointer() { - return this->readRegister(Registers::RegisterNumber::STACK_POINTER_X2); + const auto value = this->riscVDebugInterface->readCpuRegisters({&(this->spRegisterDescriptor)}).at(0).second; + assert(value.size() == 4); + return static_cast( + (value[0] << 24) | (value[1] << 16) | (value[2] << 8) | (value[3]) + ); } - std::map RiscV::getPinStates(int variantId) { + void RiscV::setStackPointer(TargetStackPointer stackPointer) { + this->riscVDebugInterface->writeCpuRegisters({ + { + this->spRegisterDescriptor, + TargetMemoryBuffer{ + static_cast(stackPointer >> 24), + static_cast(stackPointer >> 16), + static_cast(stackPointer >> 8), + static_cast(stackPointer) + } + } + }); + } + + TargetGpioPinDescriptorAndStatePairs RiscV::getGpioPinStates(const TargetPinoutDescriptor& pinoutDescriptor) { return {}; } - void RiscV::setPinState( - const TargetPinDescriptor& pinDescriptor, - const TargetPinState& state - ) { + void RiscV::setGpioPinState(const TargetPinDescriptor& pinDescriptor, const TargetGpioPinState& state) { } @@ -483,225 +372,356 @@ namespace Targets::RiscV return false; } - void RiscV::loadRegisterDescriptors() { - for (std::uint8_t i = 0; i <= 31; i++) { - auto generalPurposeRegisterDescriptor = RiscVRegisterDescriptor( - TargetRegisterType::GENERAL_PURPOSE_REGISTER, - static_cast(Registers::RegisterNumberBase::GPR) + i, - 4, - TargetMemoryType::OTHER, - "x" + std::to_string(i), - "CPU General Purpose", - std::nullopt, - TargetRegisterAccess(true, true) - ); - - this->registerDescriptorsById.emplace( - generalPurposeRegisterDescriptor.id, - std::move(generalPurposeRegisterDescriptor) - ); - } - } - - std::set RiscV::discoverHartIndices() { - auto hartIndices = std::set(); - - /* - * We can obtain the maximum hart index by setting all of the hartsel bits in the control register and then - * reading the value back. - */ - auto controlRegister = ControlRegister(); - controlRegister.debugModuleActive = true; - controlRegister.selectedHartIndex = 0xFFFFF; - - this->writeDebugModuleControlRegister(controlRegister); - const auto maxHartIndex = this->readDebugModuleControlRegister().selectedHartIndex; - - for (DebugModule::HartIndex hartIndex = 0; hartIndex <= maxHartIndex; ++hartIndex) { - /* - * We can't just assume that everything between 0 and the maximum hart index are valid hart indices. We - * have to test each index until we find one that is non-existent. - */ - auto controlRegister = ControlRegister(); - controlRegister.debugModuleActive = true; - controlRegister.selectedHartIndex = hartIndex; - - this->writeDebugModuleControlRegister(controlRegister); - - /* - * It's worth noting that some RISC-V targets **do not** set the non-existent flags. I'm not sure why. - * Has hartsel been hardwired to 0 because they only support a single hart, preventing the selection - * of non-existent harts? - * - * Relying on the maximum hart index seems to be all we can do in this case. - */ - if (this->readDebugModuleStatusRegister().anyNonExistent) { - break; - } - - hartIndices.insert(hartIndex); - } - - return hartIndices; - } - - ControlRegister RiscV::readDebugModuleControlRegister() { - return ControlRegister( - this->riscVDebugInterface->readDebugModuleRegister(RegisterAddress::CONTROL_REGISTER) - ); - } - - StatusRegister RiscV::readDebugModuleStatusRegister() { - return StatusRegister( - this->riscVDebugInterface->readDebugModuleRegister(RegisterAddress::STATUS_REGISTER) - ); - } - - AbstractControlStatusRegister RiscV::readDebugModuleAbstractControlStatusRegister() { - return AbstractControlStatusRegister( - this->riscVDebugInterface->readDebugModuleRegister(RegisterAddress::ABSTRACT_CONTROL_STATUS_REGISTER) - ); - } - - DebugControlStatusRegister RiscV::readDebugControlStatusRegister() { - return DebugControlStatusRegister(this->readRegister(Registers::RegisterNumber::DEBUG_CONTROL_STATUS_REGISTER)); - } - - void RiscV::enableDebugModule() { - auto controlRegister = ControlRegister(); - controlRegister.debugModuleActive = true; - controlRegister.selectedHartIndex = this->selectedHartIndex; - - this->writeDebugModuleControlRegister(controlRegister); - - constexpr auto maxAttempts = 10; - controlRegister = this->readDebugModuleControlRegister(); - - for (auto attempts = 1; !controlRegister.debugModuleActive && attempts <= maxAttempts; ++attempts) { - std::this_thread::sleep_for(std::chrono::microseconds(10)); - controlRegister = this->readDebugModuleControlRegister(); - } - - if (!controlRegister.debugModuleActive) { - throw Exceptions::Exception("Took too long to enable debug module"); - } - } - - void RiscV::disableDebugModule() { - auto controlRegister = ControlRegister(); - controlRegister.debugModuleActive = false; - controlRegister.selectedHartIndex = this->selectedHartIndex; - - this->writeDebugModuleControlRegister(controlRegister); - - constexpr auto maxAttempts = 10; - controlRegister = this->readDebugModuleControlRegister(); - - for (auto attempts = 1; controlRegister.debugModuleActive && attempts <= maxAttempts; ++attempts) { - std::this_thread::sleep_for(std::chrono::microseconds(10)); - controlRegister = this->readDebugModuleControlRegister(); - } - - if (controlRegister.debugModuleActive) { - throw Exceptions::Exception("Took too long to disable debug module"); - } - } - - RegisterValue RiscV::readRegister(RegisterNumber number) { - using DebugModule::Registers::RegisterAccessControlField; - - auto command = AbstractCommandRegister(); - command.commandType = AbstractCommandRegister::CommandType::REGISTER_ACCESS; - command.control = RegisterAccessControlField( - number, - false, - true, - false, - false, - RegisterAccessControlField::RegisterSize::SIZE_32 - ).value(); - - this->executeAbstractCommand(command); - - return this->riscVDebugInterface->readDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_0); - } - - RegisterValue RiscV::readRegister(Registers::RegisterNumber number) { - return this->readRegister(static_cast(number)); - } - - void RiscV::writeRegister(RegisterNumber number, RegisterValue value) { - using DebugModule::Registers::RegisterAccessControlField; - - auto command = AbstractCommandRegister(); - command.commandType = AbstractCommandRegister::CommandType::REGISTER_ACCESS; - command.control = RegisterAccessControlField( - number, - true, - true, - false, - false, - RegisterAccessControlField::RegisterSize::SIZE_32 - ).value(); - - this->riscVDebugInterface->writeDebugModuleRegister(RegisterAddress::ABSTRACT_DATA_0, value); - this->executeAbstractCommand(command); - } - - void RiscV::writeRegister(Registers::RegisterNumber number, RegisterValue value) { - this->writeRegister( - static_cast(number), - value - ); - } - - void RiscV::writeDebugModuleControlRegister(const DebugModule::Registers::ControlRegister& controlRegister) { - this->riscVDebugInterface->writeDebugModuleRegister( - RegisterAddress::CONTROL_REGISTER, - controlRegister.value() - ); - } - - void RiscV::writeDebugControlStatusRegister(const DebugControlStatusRegister& controlRegister) { - this->writeRegister(Registers::RegisterNumber::DEBUG_CONTROL_STATUS_REGISTER, controlRegister.value()); - } - - void RiscV::executeAbstractCommand( - const DebugModule::Registers::AbstractCommandRegister& abstractCommandRegister + const TargetMemorySegmentDescriptor& RiscV::resolveRegisterMemorySegmentDescriptor( + const TargetRegisterDescriptor& regDescriptor, + const TargetAddressSpaceDescriptor& addressSpaceDescriptor ) { - this->riscVDebugInterface->writeDebugModuleRegister( - RegisterAddress::ABSTRACT_COMMAND_REGISTER, - abstractCommandRegister.value() + const auto segmentDescriptors = addressSpaceDescriptor.getIntersectingMemorySegmentDescriptors( + TargetMemoryAddressRange{ + regDescriptor.startAddress, + (regDescriptor.startAddress + (regDescriptor.size / addressSpaceDescriptor.unitSize) - 1) + } ); - auto abstractStatusRegister = this->readDebugModuleAbstractControlStatusRegister(); - if (abstractStatusRegister.commandError != AbstractControlStatusRegister::CommandError::NONE) { + if (segmentDescriptors.empty()) { throw Exceptions::Exception( - "Failed to execute abstract command - error: " - + Services::StringService::toHex(abstractStatusRegister.commandError) + "Cannot access system register \"" + regDescriptor.key + "\" - unknown memory segment" ); } - constexpr auto maxAttempts = 10; - for (auto attempts = 1; abstractStatusRegister.busy && attempts <= maxAttempts; ++attempts) { - std::this_thread::sleep_for(std::chrono::microseconds(10)); - abstractStatusRegister = this->readDebugModuleAbstractControlStatusRegister(); + if (segmentDescriptors.size() != 1) { + throw Exceptions::Exception( + "Cannot access system register \"" + regDescriptor.key + + "\" - register spans multiple memory segments" + ); } - if (abstractStatusRegister.busy) { - throw Exceptions::Exception("Abstract command took too long to execute"); + return *(segmentDescriptors.front()); + } + + TargetAddressSpaceDescriptor RiscV::generateCpuRegisterAddressSpaceDescriptor() { + auto addressSpace = TargetAddressSpaceDescriptor{ + "debug_module", + {0x0000, 0xFFFF}, + TargetMemoryEndianness::LITTLE, + {}, + 4 + }; + + addressSpace.segmentDescriptorsByKey.emplace( + "cs_registers", + TargetMemorySegmentDescriptor{ + addressSpace.key, + "cs_registers", + "Control Status Registers", + TargetMemorySegmentType::REGISTERS, + {0x0000, 0x0FFF}, + addressSpace.unitSize, + false, + {true, true}, + {false, false}, + std::nullopt + } + ); + + addressSpace.segmentDescriptorsByKey.emplace( + "gp_registers", + TargetMemorySegmentDescriptor{ + addressSpace.key, + "gp_registers", + "General Purpose Registers", + TargetMemorySegmentType::GENERAL_PURPOSE_REGISTERS, + {0x1000, 0x101F}, + addressSpace.unitSize, + false, + {true, true}, + {false, false}, + std::nullopt + } + ); + + return addressSpace; + } + + TargetPeripheralDescriptor RiscV::generateCpuPeripheralDescriptor( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& csrMemorySegmentDescriptor, + const TargetMemorySegmentDescriptor& gprMemorySegmentDescriptor + ) { + auto cpuPeripheralDescriptor = TargetPeripheralDescriptor{ + "cpu", + "RISC-V CPU", + {}, + {} + }; + + auto& gprGroup = cpuPeripheralDescriptor.registerGroupDescriptorsByKey.emplace( + "gpr", + TargetRegisterGroupDescriptor{ + "gpr", + "General Purpose Registers", + addressSpaceDescriptor.key, + std::nullopt, + {}, + {} + } + ).first->second; + + for (auto i = std::uint8_t{0}; i <= 31; ++i) { + const auto key = "x" + std::to_string(i); + gprGroup.registerDescriptorsByKey.emplace( + key, + TargetRegisterDescriptor{ + key, + "X" + std::to_string(i), + addressSpaceDescriptor.key, + gprMemorySegmentDescriptor.addressRange.startAddress + i, + 4, + TargetRegisterType::GENERAL_PURPOSE_REGISTER, + TargetRegisterAccess(true, true), + std::nullopt, + {} + } + ); } - } - TargetMemoryAddress RiscV::alignMemoryAddress(TargetMemoryAddress address, TargetMemoryAddress alignTo) { - return static_cast( - std::floor(static_cast(address) / static_cast(alignTo)) - ) * alignTo; - } + auto& csrGroup = cpuPeripheralDescriptor.registerGroupDescriptorsByKey.emplace( + "csr", + TargetRegisterGroupDescriptor{ + "csr", + "Control Status Registers", + addressSpaceDescriptor.key, + std::nullopt, + {}, + {} + } + ).first->second; - TargetMemorySize RiscV::alignMemorySize(TargetMemorySize size, TargetMemorySize alignTo) { - return static_cast( - std::ceil(static_cast(size) / static_cast(alignTo)) - ) * alignTo; + csrGroup.registerDescriptorsByKey.emplace( + "marchid", + TargetRegisterDescriptor{ + "marchid", + "MARCHID", + addressSpaceDescriptor.key, + csrMemorySegmentDescriptor.addressRange.startAddress + 0xF12, + 4, + TargetRegisterType::OTHER, + TargetRegisterAccess(true, false), + "Architecture ID", + {} + } + ); + + csrGroup.registerDescriptorsByKey.emplace( + "mimpid", + TargetRegisterDescriptor{ + "mimpid", + "MIMPID", + addressSpaceDescriptor.key, + csrMemorySegmentDescriptor.addressRange.startAddress + 0xF13, + 4, + TargetRegisterType::OTHER, + TargetRegisterAccess(true, false), + "Implementation ID", + {} + } + ); + + csrGroup.registerDescriptorsByKey.emplace( + "mstatus", + TargetRegisterDescriptor{ + "mstatus", + "MSTATUS", + addressSpaceDescriptor.key, + csrMemorySegmentDescriptor.addressRange.startAddress + 0x300, + 4, + TargetRegisterType::OTHER, + TargetRegisterAccess(true, true), + "Machine status", + {} + } + ); + + csrGroup.registerDescriptorsByKey.emplace( + "misa", + TargetRegisterDescriptor{ + "misa", + "MISA", + addressSpaceDescriptor.key, + csrMemorySegmentDescriptor.addressRange.startAddress + 0x301, + 4, + TargetRegisterType::OTHER, + TargetRegisterAccess(true, true), + "ISA and extensions", + {} + } + ); + + csrGroup.registerDescriptorsByKey.emplace( + "mtvec", + TargetRegisterDescriptor{ + "mtvec", + "MTVEC", + addressSpaceDescriptor.key, + csrMemorySegmentDescriptor.addressRange.startAddress + 0x305, + 4, + TargetRegisterType::OTHER, + TargetRegisterAccess(true, true), + "Machine trap-handler base address", + {} + } + ); + + csrGroup.registerDescriptorsByKey.emplace( + "mcounteren", + TargetRegisterDescriptor{ + "mcounteren", + "MCOUNTEREN", + addressSpaceDescriptor.key, + csrMemorySegmentDescriptor.addressRange.startAddress + 0x306, + 4, + TargetRegisterType::OTHER, + TargetRegisterAccess(true, true), + "Machine counter enable", + {} + } + ); + + csrGroup.registerDescriptorsByKey.emplace( + "mscratch", + TargetRegisterDescriptor{ + "mscratch", + "MSCRATCH", + addressSpaceDescriptor.key, + csrMemorySegmentDescriptor.addressRange.startAddress + 0x340, + 4, + TargetRegisterType::OTHER, + TargetRegisterAccess(true, true), + "Scratch register for machine trap handlers", + {} + } + ); + + csrGroup.registerDescriptorsByKey.emplace( + "mepc", + TargetRegisterDescriptor{ + "mepc", + "MEPC", + addressSpaceDescriptor.key, + csrMemorySegmentDescriptor.addressRange.startAddress + 0x341, + 4, + TargetRegisterType::OTHER, + TargetRegisterAccess(true, true), + "Machine exception program counter", + {} + } + ); + + csrGroup.registerDescriptorsByKey.emplace( + "mcause", + TargetRegisterDescriptor{ + "mcause", + "MCAUSE", + addressSpaceDescriptor.key, + csrMemorySegmentDescriptor.addressRange.startAddress + 0x342, + 4, + TargetRegisterType::OTHER, + TargetRegisterAccess(true, true), + "Machine trap cause", + {} + } + ); + + csrGroup.registerDescriptorsByKey.emplace( + "mtval", + TargetRegisterDescriptor{ + "mtval", + "MTVAL", + addressSpaceDescriptor.key, + csrMemorySegmentDescriptor.addressRange.startAddress + 0x343, + 4, + TargetRegisterType::OTHER, + TargetRegisterAccess(true, true), + "Machine bad address or instruction", + {} + } + ); + + csrGroup.registerDescriptorsByKey.emplace( + "mip", + TargetRegisterDescriptor{ + "mip", + "MIP", + addressSpaceDescriptor.key, + csrMemorySegmentDescriptor.addressRange.startAddress + 0x344, + 4, + TargetRegisterType::OTHER, + TargetRegisterAccess(true, true), + "Machine interrupt pending", + {} + } + ); + + csrGroup.registerDescriptorsByKey.emplace( + "dcsr", + TargetRegisterDescriptor{ + "dcsr", + "DCSR", + addressSpaceDescriptor.key, + csrMemorySegmentDescriptor.addressRange.startAddress + 0x7B0, + 4, + TargetRegisterType::OTHER, + TargetRegisterAccess(true, true), + "Debug control and status", + {} + } + ); + + csrGroup.registerDescriptorsByKey.emplace( + "dpc", + TargetRegisterDescriptor{ + "dpc", + "DPC", + addressSpaceDescriptor.key, + csrMemorySegmentDescriptor.addressRange.startAddress + 0x7B1, + 4, + TargetRegisterType::OTHER, + TargetRegisterAccess(true, true), + "Debug program counter", + {} + } + ); + + csrGroup.registerDescriptorsByKey.emplace( + "dscratch0", + TargetRegisterDescriptor{ + "dscratch0", + "DSCRATCH0", + addressSpaceDescriptor.key, + csrMemorySegmentDescriptor.addressRange.startAddress + 0x7B2, + 4, + TargetRegisterType::OTHER, + TargetRegisterAccess(true, true), + "Debug scratch 0", + {} + } + ); + + csrGroup.registerDescriptorsByKey.emplace( + "dscratch1", + TargetRegisterDescriptor{ + "dscratch1", + "DSCRATCH1", + addressSpaceDescriptor.key, + csrMemorySegmentDescriptor.addressRange.startAddress + 0x7B3, + 4, + TargetRegisterType::OTHER, + TargetRegisterAccess(true, true), + "Debug scratch 1", + {} + } + ); + + return cpuPeripheralDescriptor; } } diff --git a/src/Targets/RiscV/RiscV.hpp b/src/Targets/RiscV/RiscV.hpp index a1a938e9..e603230e 100644 --- a/src/Targets/RiscV/RiscV.hpp +++ b/src/Targets/RiscV/RiscV.hpp @@ -7,32 +7,19 @@ #include "src/Targets/Target.hpp" #include "src/DebugToolDrivers/DebugTool.hpp" -#include "TargetDescription/TargetDescriptionFile.hpp" +#include "RiscVTargetConfig.hpp" +#include "TargetDescriptionFile.hpp" #include "src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVDebugInterface.hpp" #include "src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVProgramInterface.hpp" - -#include "src/Targets/RiscV/RiscVGeneric.hpp" -#include "src/Targets/RiscV/Registers/RegisterNumbers.hpp" -#include "src/Targets/RiscV/Registers/DebugControlStatusRegister.hpp" - -#include "src/Targets/RiscV/DebugModule/DebugModule.hpp" -#include "src/Targets/RiscV/DebugModule/Registers/ControlRegister.hpp" -#include "src/Targets/RiscV/DebugModule/Registers/StatusRegister.hpp" -#include "src/Targets/RiscV/DebugModule/Registers/AbstractControlStatusRegister.hpp" -#include "src/Targets/RiscV/DebugModule/Registers/AbstractCommandRegister.hpp" - -#include "RiscVRegisterDescriptor.hpp" +#include "src/DebugToolDrivers/TargetInterfaces/RiscV/RiscVIdentificationInterface.hpp" namespace Targets::RiscV { class RiscV: public Target { public: - explicit RiscV( - const TargetConfig& targetConfig, - TargetDescription::TargetDescriptionFile&& targetDescriptionFile - ); + RiscV(const TargetConfig& targetConfig, TargetDescriptionFile&& targetDescriptionFile); /* * The functions below implement the Target interface for RISC-V targets. @@ -41,20 +28,13 @@ namespace Targets::RiscV * each function. */ - /** - * All RISC-V compatible debug tools must provide a valid RiscVDebugInterface. - * - * @param debugTool - * @return - */ bool supportsDebugTool(DebugTool* debugTool) override; - void setDebugTool(DebugTool* debugTool) override; void activate() override; void deactivate() override; - TargetDescriptor getDescriptor() override; + TargetDescriptor targetDescriptor() override; void run(std::optional toAddress = std::nullopt) override; void stop() override; @@ -68,34 +48,45 @@ namespace Targets::RiscV void removeHardwareBreakpoint(TargetMemoryAddress address) override; void clearAllBreakpoints() override; - TargetRegisters readRegisters(const TargetRegisterDescriptorIds& descriptorIds) override; - void writeRegisters(const TargetRegisters& registers) override; + TargetRegisterDescriptorAndValuePairs readRegisters(const TargetRegisterDescriptors& descriptors) override; + void writeRegisters(const TargetRegisterDescriptorAndValuePairs& registers) override; TargetMemoryBuffer readMemory( - TargetMemoryType memoryType, + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, TargetMemoryAddress startAddress, TargetMemorySize bytes, const std::set& excludedAddressRanges = {} ) override; void writeMemory( - TargetMemoryType memoryType, + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, TargetMemoryAddress startAddress, const TargetMemoryBuffer& buffer ) override; - void eraseMemory(TargetMemoryType memoryType) override; + bool isProgramMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, + TargetMemoryAddress startAddress, + TargetMemorySize size + ) override; + void eraseMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor + ) override; - TargetState getState() override; + TargetExecutionState getExecutionState() override; TargetMemoryAddress getProgramCounter() override; void setProgramCounter(TargetMemoryAddress programCounter) override; TargetStackPointer getStackPointer() override; + void setStackPointer(TargetStackPointer stackPointer) override; - std::map getPinStates(int variantId) override; - void setPinState( - const TargetPinDescriptor& pinDescriptor, - const TargetPinState& state + TargetGpioPinDescriptorAndStatePairs getGpioPinStates( + const TargetPinoutDescriptor& pinoutDescriptor ) override; + void setGpioPinState(const TargetPinDescriptor& pinDescriptor, const TargetGpioPinState& state) override; void enableProgrammingMode() override; @@ -104,43 +95,53 @@ namespace Targets::RiscV bool programmingModeEnabled() override; protected: - TargetDescription::TargetDescriptionFile targetDescriptionFile; - - std::map registerDescriptorsById; - - RiscVRegisterDescriptor stackPointerRegisterDescriptor; + RiscVTargetConfig targetConfig; + TargetDescriptionFile targetDescriptionFile; DebugToolDrivers::TargetInterfaces::RiscV::RiscVDebugInterface* riscVDebugInterface = nullptr; DebugToolDrivers::TargetInterfaces::RiscV::RiscVProgramInterface* riscVProgramInterface = nullptr; + DebugToolDrivers::TargetInterfaces::RiscV::RiscVIdentificationInterface* riscVIdInterface = nullptr; - std::set hartIndices; - DebugModule::HartIndex selectedHartIndex = 0; + /* + * On RISC-V targets, CPU registers are typically only accessible via the debug module (we can't access them + * via the system address space). So we use abstract commands to access these registers. This means we have to + * address these registers via their register numbers, as defined in the RISC-V debug spec. + * + * We effectively treat register numbers as a separate address space, with an addressable unit size of 4 bytes. + * The `cpuRegisterAddressSpaceDescriptor` member holds the descriptor for this address space. + * + * TODO: review this. This address space is specific to the RISC-V debug spec, but some debug tools may + * implement their own debug translator in firmware, and then provide a higher-level API to access the + * same registers. In that case, this address space may not be relevant. This may need to be moved. + * ATM all RISC-V debug tools supported by Bloom provide a DTM interface, so we use our own debug + * translator driver and this address space is, in fact, relevant. I will deal with this when it + * becomes a problem. + */ + TargetAddressSpaceDescriptor cpuRegisterAddressSpaceDescriptor; + const TargetMemorySegmentDescriptor& csrMemorySegmentDescriptor; + const TargetMemorySegmentDescriptor& gprMemorySegmentDescriptor; - void loadRegisterDescriptors(); + TargetPeripheralDescriptor cpuPeripheralDescriptor; + const TargetRegisterGroupDescriptor& csrGroupDescriptor; + const TargetRegisterGroupDescriptor& gprGroupDescriptor; + const TargetRegisterDescriptor& pcRegisterDescriptor; + const TargetRegisterDescriptor& spRegisterDescriptor; - std::set discoverHartIndices(); + /* + * The "system" address space is the main address space on RISC-V targets. + */ + TargetAddressSpaceDescriptor sysAddressSpaceDescriptor; - DebugModule::Registers::ControlRegister readDebugModuleControlRegister(); - DebugModule::Registers::StatusRegister readDebugModuleStatusRegister(); - DebugModule::Registers::AbstractControlStatusRegister readDebugModuleAbstractControlStatusRegister(); + const TargetMemorySegmentDescriptor& resolveRegisterMemorySegmentDescriptor( + const TargetRegisterDescriptor& regDescriptor, + const TargetAddressSpaceDescriptor& addressSpaceDescriptor + ); - Registers::DebugControlStatusRegister readDebugControlStatusRegister(); - - void enableDebugModule(); - void disableDebugModule(); - - RegisterValue readRegister(RegisterNumber number); - RegisterValue readRegister(Registers::RegisterNumber number); - void writeRegister(RegisterNumber number, RegisterValue value); - void writeRegister(Registers::RegisterNumber number, RegisterValue value); - - void writeDebugModuleControlRegister(const DebugModule::Registers::ControlRegister& controlRegister); - - void writeDebugControlStatusRegister(const Registers::DebugControlStatusRegister& controlRegister); - - void executeAbstractCommand(const DebugModule::Registers::AbstractCommandRegister& abstractCommandRegister); - - TargetMemoryAddress alignMemoryAddress(TargetMemoryAddress address, TargetMemoryAddress alignTo); - TargetMemorySize alignMemorySize(TargetMemorySize size, TargetMemorySize alignTo); + static TargetAddressSpaceDescriptor generateCpuRegisterAddressSpaceDescriptor(); + static TargetPeripheralDescriptor generateCpuPeripheralDescriptor( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& csrMemorySegmentDescriptor, + const TargetMemorySegmentDescriptor& gprMemorySegmentDescriptor + ); }; } diff --git a/src/Targets/RiscV/RiscVRegisterDescriptor.hpp b/src/Targets/RiscV/RiscVRegisterDescriptor.hpp deleted file mode 100644 index e5f61044..00000000 --- a/src/Targets/RiscV/RiscVRegisterDescriptor.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include - -#include "src/Targets/TargetRegisterDescriptor.hpp" - -#include "RiscVGeneric.hpp" - -namespace Targets::RiscV -{ - struct RiscVRegisterDescriptor: public ::Targets::TargetRegisterDescriptor - { - RegisterNumber number; - - RiscVRegisterDescriptor( - TargetRegisterType type, - RegisterNumber number, - TargetMemorySize size, - TargetMemoryType memoryType, - std::optional name, - std::optional groupName, - std::optional description, - TargetRegisterAccess access - ) - : ::Targets::TargetRegisterDescriptor( - type, - std::nullopt, - size, - memoryType, - name, - groupName, - description, - access - ) - , number(number) - {} - }; -} diff --git a/src/Targets/RiscV/RiscVTargetConfig.cpp b/src/Targets/RiscV/RiscVTargetConfig.cpp new file mode 100644 index 00000000..6b46fbea --- /dev/null +++ b/src/Targets/RiscV/RiscVTargetConfig.cpp @@ -0,0 +1,8 @@ +#include "RiscVTargetConfig.hpp" + +namespace Targets::RiscV +{ + RiscVTargetConfig::RiscVTargetConfig(const TargetConfig& targetConfig) + : TargetConfig(targetConfig) + {} +} diff --git a/src/Targets/RiscV/RiscVTargetConfig.hpp b/src/Targets/RiscV/RiscVTargetConfig.hpp new file mode 100644 index 00000000..ac5e3f16 --- /dev/null +++ b/src/Targets/RiscV/RiscVTargetConfig.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "src/ProjectConfig.hpp" + +namespace Targets::RiscV +{ + /** + * Extending the generic TargetConfig struct to accommodate RISC-V target configuration parameters. + */ + struct RiscVTargetConfig: public TargetConfig + { + public: + explicit RiscVTargetConfig(const TargetConfig& targetConfig); + }; +} diff --git a/src/Targets/RiscV/TargetDescription/TargetDescriptionFile.cpp b/src/Targets/RiscV/TargetDescription/TargetDescriptionFile.cpp deleted file mode 100644 index 16c97052..00000000 --- a/src/Targets/RiscV/TargetDescription/TargetDescriptionFile.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "TargetDescriptionFile.hpp" - -#include - -namespace Targets::RiscV::TargetDescription -{ - TargetDescriptionFile::TargetDescriptionFile(const std::string& xmlFilePath) - : Targets::TargetDescription::TargetDescriptionFile(xmlFilePath) - {} - - std::string TargetDescriptionFile::getTargetId() const { - return this->deviceAttribute("id"); - } - - std::string TargetDescriptionFile::getVendorName() const { - return this->deviceAttribute("vendor"); - } -} diff --git a/src/Targets/RiscV/TargetDescriptionFile.cpp b/src/Targets/RiscV/TargetDescriptionFile.cpp new file mode 100644 index 00000000..97dfc9e2 --- /dev/null +++ b/src/Targets/RiscV/TargetDescriptionFile.cpp @@ -0,0 +1,16 @@ +#include "TargetDescriptionFile.hpp" + +namespace Targets::RiscV +{ + TargetDescriptionFile::TargetDescriptionFile(const std::string& xmlFilePath) + : Targets::TargetDescription::TargetDescriptionFile(xmlFilePath) + {} + + std::string TargetDescriptionFile::getTargetId() const { + return this->getProperty("vendor", "target_id").value; + } + + TargetAddressSpaceDescriptor TargetDescriptionFile::getSystemAddressSpaceDescriptor() const { + return this->targetAddressSpaceDescriptorFromAddressSpace(this->getAddressSpace("system")); + } +} diff --git a/src/Targets/RiscV/TargetDescription/TargetDescriptionFile.hpp b/src/Targets/RiscV/TargetDescriptionFile.hpp similarity index 69% rename from src/Targets/RiscV/TargetDescription/TargetDescriptionFile.hpp rename to src/Targets/RiscV/TargetDescriptionFile.hpp index 569877b0..e1b02dee 100644 --- a/src/Targets/RiscV/TargetDescription/TargetDescriptionFile.hpp +++ b/src/Targets/RiscV/TargetDescriptionFile.hpp @@ -2,9 +2,7 @@ #include "src/Targets/TargetDescription/TargetDescriptionFile.hpp" -#include "src/Targets/RiscV/RiscVGeneric.hpp" - -namespace Targets::RiscV::TargetDescription +namespace Targets::RiscV { /** * Represents an RISC-V TDF. @@ -23,11 +21,6 @@ namespace Targets::RiscV::TargetDescription */ [[nodiscard]] std::string getTargetId() const; - /** - * Returns the RISC-V vendor name from the TDF. - * - * @return - */ - [[nodiscard]] std::string getVendorName() const; + [[nodiscard]] TargetAddressSpaceDescriptor getSystemAddressSpaceDescriptor() const; }; } diff --git a/src/Targets/RiscV/TargetParameters.hpp b/src/Targets/RiscV/TargetParameters.hpp deleted file mode 100644 index e93c5ab9..00000000 --- a/src/Targets/RiscV/TargetParameters.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -#include "src/Targets/TargetMemory.hpp" - -namespace Targets::RiscV -{ - struct TargetParameters - {}; -} diff --git a/src/Targets/Target.hpp b/src/Targets/Target.hpp index 1dd8d1f7..8b9c9a2e 100644 --- a/src/Targets/Target.hpp +++ b/src/Targets/Target.hpp @@ -10,11 +10,15 @@ #include "src/ProjectConfig.hpp" #include "TargetDescriptor.hpp" +#include "TargetAddressSpaceDescriptor.hpp" +#include "TargetMemorySegmentDescriptor.hpp" #include "TargetState.hpp" #include "TargetRegisterDescriptor.hpp" -#include "TargetRegister.hpp" #include "TargetMemory.hpp" #include "TargetBreakpoint.hpp" +#include "TargetPinoutDescriptor.hpp" +#include "TargetPinDescriptor.hpp" +#include "TargetGpioPinState.hpp" #include "src/DebugToolDrivers/DebugTool.hpp" @@ -34,10 +38,6 @@ namespace Targets virtual ~Target() = default; - bool isActivated() const { - return this->activated; - } - /** * Should check if the given debugTool is compatible with the target. Returning false in this function will * prevent Bloom from attempting to use the selected debug tool with the selected target. An InvalidConfig @@ -79,22 +79,18 @@ namespace Targets virtual void deactivate() = 0; /** - * Should generate and return a TargetDescriptor for the current target. - * - * This is called when a component within Bloom requests the TargetDescriptor from the TargetController. - * The TargetController will cache this upon the first request. Subsequent requests will be serviced with the - * cached value. + * Should generate a TargetDescriptor for the current target. * * @return */ - virtual TargetDescriptor getDescriptor() = 0; + virtual TargetDescriptor targetDescriptor() = 0; /** * Should resume execution on the target. * * @param toAddress */ - virtual void run(std::optional toAddress = std::nullopt) = 0; + virtual void run(std::optional toAddress) = 0; /** * Should halt execution on the target. @@ -149,23 +145,24 @@ namespace Targets /** * Should read register values of the registers described by the given descriptors. * - * @param descriptorIds + * @param descriptors * * @return */ - virtual TargetRegisters readRegisters(const Targets::TargetRegisterDescriptorIds& descriptorIds) = 0; + virtual TargetRegisterDescriptorAndValuePairs readRegisters(const TargetRegisterDescriptors& descriptors) = 0; /** * Should update the value of the given registers. * * @param registers */ - virtual void writeRegisters(const TargetRegisters& registers) = 0; + virtual void writeRegisters(const TargetRegisterDescriptorAndValuePairs& registers) = 0; /** * Should read memory from the target. * - * @param memoryType + * @param addressSpaceDescriptor + * @param memorySegmentDescriptor * @param startAddress * @param bytes * @param excludedAddressRanges @@ -173,38 +170,65 @@ namespace Targets * @return */ virtual TargetMemoryBuffer readMemory( - TargetMemoryType memoryType, + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, TargetMemoryAddress startAddress, TargetMemorySize bytes, - const std::set& excludedAddressRanges = {} + const std::set& excludedAddressRanges ) = 0; /** * Should write memory to the target. * - * @param memoryType + * @param addressSpaceDescriptor + * @param memorySegmentDescriptor * @param startAddress * @param buffer */ virtual void writeMemory( - TargetMemoryType memoryType, + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, TargetMemoryAddress startAddress, const TargetMemoryBuffer& buffer ) = 0; + /** + * Should check if the given memory is program memory. + * + * The TargetMemorySegmentDescriptor::executable flag specifies whether any part of the segment is executable, + * but this member function allows for a more granular check. + * + * @param addressSpaceDescriptor + * @param memorySegmentDescriptor + * @param startAddress + * @param size + * + * @return + */ + virtual bool isProgramMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor, + TargetMemoryAddress startAddress, + TargetMemorySize size + ) = 0; + /** * Should erase the entire address range of a given memory type. * - * @param memoryType + * @param addressSpaceDescriptor + * @param memorySegmentDescriptor */ - virtual void eraseMemory(TargetMemoryType memoryType) = 0; + virtual void eraseMemory( + const TargetAddressSpaceDescriptor& addressSpaceDescriptor, + const TargetMemorySegmentDescriptor& memorySegmentDescriptor + ) = 0; /** * Should return the current state of the target. * * @return */ - virtual TargetState getState() = 0; + virtual TargetExecutionState getExecutionState() = 0; /** * Should fetch the current program counter value. @@ -228,24 +252,30 @@ namespace Targets virtual TargetStackPointer getStackPointer() = 0; /** - * Should get the current pin states for each pin on the target, mapped by pin number + * Should update the stack pointer value on the target. * - * @param variantId + * @param stackPointer + */ + virtual void setStackPointer(TargetStackPointer stackPointer) = 0; + + /** + * Should get the current state of each GPIO pin on the target + * + * @param pinoutDescriptor * * @return */ - virtual std::map getPinStates(int variantId) = 0; + virtual TargetGpioPinDescriptorAndStatePairs getGpioPinStates( + const TargetPinoutDescriptor& pinoutDescriptor + ) = 0; /** - * Should update the pin state for the given pin, with the given state. + * Should update the pin state for the given GPIO pin, with the given state. * * @param pinDescriptor * @param state */ - virtual void setPinState( - const TargetPinDescriptor& pinDescriptor, - const TargetPinState& state - ) = 0; + virtual void setGpioPinState(const TargetPinDescriptor& pinDescriptor, const TargetGpioPinState& state) = 0; /** * Should prepare the target for programming. @@ -263,14 +293,5 @@ namespace Targets * @return */ virtual bool programmingModeEnabled() = 0; - - protected: - /** - * Target related configuration provided by the user. This is passed in via the first stage of target - * configuration. See Target::preActivationConfigure() for more. - */ - TargetConfig config; - - bool activated = false; }; } diff --git a/src/Targets/TargetAddressSpaceDescriptor.cpp b/src/Targets/TargetAddressSpaceDescriptor.cpp index c5c65a8e..83769400 100644 --- a/src/Targets/TargetAddressSpaceDescriptor.cpp +++ b/src/Targets/TargetAddressSpaceDescriptor.cpp @@ -1,5 +1,9 @@ #include "TargetAddressSpaceDescriptor.hpp" +#include +#include + +#include "src/Services/StringService.hpp" #include "src/Exceptions/InternalFatalErrorException.hpp" namespace Targets @@ -8,17 +12,27 @@ namespace Targets const std::string& key, const TargetMemoryAddressRange& addressRange, TargetMemoryEndianness endianness, - const std::map& segmentDescriptorsByKey + std::map&& segmentDescriptorsByKey, + std::uint8_t unitSize ) - : id(++(TargetAddressSpaceDescriptor::lastAddressSpaceDescriptorId)) + : id(TargetAddressSpaceDescriptor::generateId(key)) , key(key) , addressRange(addressRange) , endianness(endianness) - , segmentDescriptorsByKey(segmentDescriptorsByKey) + , segmentDescriptorsByKey(std::move(segmentDescriptorsByKey)) + , unitSize(unitSize) {} + bool TargetAddressSpaceDescriptor::operator == (const TargetAddressSpaceDescriptor& other) const { + return this->id == other.id; + } + + bool TargetAddressSpaceDescriptor::operator != (const TargetAddressSpaceDescriptor& other) const { + return !(*this == other); + } + TargetMemorySize TargetAddressSpaceDescriptor::size() const { - return this->addressRange.size(); + return this->addressRange.size() * this->unitSize; } std::optional< @@ -38,10 +52,10 @@ namespace Targets ) const { const auto segment = this->tryGetMemorySegmentDescriptor(key); if (!segment.has_value()) { - throw Exceptions::InternalFatalErrorException( + throw Exceptions::InternalFatalErrorException{ "Failed to get memory segment descriptor \"" + key + "\" from address space \"" + this->key + "\" - segment not found" - ); + }; } return segment->get(); @@ -52,7 +66,7 @@ namespace Targets > TargetAddressSpaceDescriptor::getIntersectingMemorySegmentDescriptors( const TargetMemoryAddressRange& addressRange ) const { - auto output = std::vector(); + auto output = std::vector{}; for (const auto& [key, segmentDescriptor] : this->segmentDescriptorsByKey) { if (segmentDescriptor.addressRange.intersectsWith(addressRange)) { @@ -60,6 +74,34 @@ namespace Targets } } + std::sort( + output.begin(), + output.end(), + [] (const TargetMemorySegmentDescriptor* descA, const TargetMemorySegmentDescriptor* descB) { + return descA->addressRange.startAddress < descB->addressRange.startAddress; + } + ); + return output; } + + TargetAddressSpaceDescriptor TargetAddressSpaceDescriptor::clone() const { + auto output = TargetAddressSpaceDescriptor{ + this->key, + this->addressRange, + this->endianness, + {}, + this->unitSize + }; + + for (const auto& [key, descriptor] : this->segmentDescriptorsByKey) { + output.segmentDescriptorsByKey.emplace(key, descriptor.clone()); + } + + return output; + } + + TargetAddressSpaceId TargetAddressSpaceDescriptor::generateId(const std::string& addressSpaceKey) { + return static_cast(Services::StringService::hash(addressSpaceKey)); + } } diff --git a/src/Targets/TargetAddressSpaceDescriptor.hpp b/src/Targets/TargetAddressSpaceDescriptor.hpp index 2763b7b8..d5b9654e 100644 --- a/src/Targets/TargetAddressSpaceDescriptor.hpp +++ b/src/Targets/TargetAddressSpaceDescriptor.hpp @@ -11,24 +11,47 @@ namespace Targets { - using TargetAddressSpaceDescriptorId = std::uint8_t; - struct TargetAddressSpaceDescriptor { public: - const TargetAddressSpaceDescriptorId id; - std::string key; + /* + * The ID of an address space is just a hash of the address space key, which is unique. We use the ID for + * cheap equality checks. + * + * See TargetAddressSpaceDescriptor::generateId() for more. + */ + const TargetAddressSpaceId id; + const std::string key; TargetMemoryAddressRange addressRange; TargetMemoryEndianness endianness; std::map segmentDescriptorsByKey; + /* + * In Bloom, a byte is always considered to be 8 bits in width. + * + * Not all address spaces are byte-addressable. TargetAddressSpaceDescriptor::unitSize holds the number of + * bytes within a single addressable unit. + * + * This is also available in segment descriptors, via TargetMemorySegmentDescriptor::addressSpaceUnitSize. + */ + std::uint8_t unitSize; + TargetAddressSpaceDescriptor( const std::string& key, const TargetMemoryAddressRange& addressRange, TargetMemoryEndianness endianness, - const std::map& segmentDescriptorsByKey + std::map&& segmentDescriptorsByKey, + std::uint8_t unitSize = 1 ); + TargetAddressSpaceDescriptor(const TargetAddressSpaceDescriptor& other) = delete; + TargetAddressSpaceDescriptor& operator = (const TargetAddressSpaceDescriptor& other) = delete; + + TargetAddressSpaceDescriptor(TargetAddressSpaceDescriptor&& other) noexcept = default; + + bool operator == (const TargetAddressSpaceDescriptor& other) const; + bool operator != (const TargetAddressSpaceDescriptor& other) const; + TargetMemorySize size() const; /** @@ -69,7 +92,8 @@ namespace Targets const TargetMemoryAddressRange& addressRange ) const; - private: - static inline std::atomic lastAddressSpaceDescriptorId = 0; + [[nodiscard]] TargetAddressSpaceDescriptor clone() const; + + static TargetAddressSpaceId generateId(const std::string& addressSpaceKey); }; } diff --git a/src/Targets/TargetBitFieldDescriptor.cpp b/src/Targets/TargetBitFieldDescriptor.cpp new file mode 100644 index 00000000..8e96af21 --- /dev/null +++ b/src/Targets/TargetBitFieldDescriptor.cpp @@ -0,0 +1,20 @@ +#include "TargetBitFieldDescriptor.hpp" + +namespace Targets +{ + TargetBitFieldDescriptor::TargetBitFieldDescriptor( + const std::string& key, + const std::string& name, + std::uint64_t mask, + std::optional description + ) + : key(key) + , name(name) + , mask(mask) + , description(description) + {} + + TargetBitFieldDescriptor TargetBitFieldDescriptor::clone() const { + return {*this}; + } +} diff --git a/src/Targets/TargetBitFieldDescriptor.hpp b/src/Targets/TargetBitFieldDescriptor.hpp new file mode 100644 index 00000000..4f0f12e6 --- /dev/null +++ b/src/Targets/TargetBitFieldDescriptor.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include +#include +#include + +#include "src/Helpers/Pair.hpp" + +namespace Targets +{ + struct TargetBitFieldDescriptor + { + public: + std::string key; + std::string name; + std::uint64_t mask; + std::optional description; + + TargetBitFieldDescriptor( + const std::string& key, + const std::string& name, + std::uint64_t mask, + std::optional description + ); + + TargetBitFieldDescriptor& operator = (const TargetBitFieldDescriptor& other) = delete; + + TargetBitFieldDescriptor(TargetBitFieldDescriptor&& other) noexcept = default; + TargetBitFieldDescriptor& operator = (TargetBitFieldDescriptor&& other) = default; + + [[nodiscard]] TargetBitFieldDescriptor clone() const; + + private: + TargetBitFieldDescriptor(const TargetBitFieldDescriptor& other) = default; + }; +} diff --git a/src/Targets/TargetDescription/AddressSpace.hpp b/src/Targets/TargetDescription/AddressSpace.hpp index 6d998c94..12012922 100644 --- a/src/Targets/TargetDescription/AddressSpace.hpp +++ b/src/Targets/TargetDescription/AddressSpace.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -16,6 +17,7 @@ namespace Targets::TargetDescription std::string key; TargetMemoryAddress startAddress; TargetMemorySize size; + std::uint8_t unitSize; std::optional endianness; std::map> memorySegmentsByKey; @@ -23,12 +25,14 @@ namespace Targets::TargetDescription const std::string& key, TargetMemoryAddress startAddress, TargetMemorySize size, + std::uint8_t unitSize, const std::optional& endianness, const std::map>& memorySegmentsByKey ) : key(key) , startAddress(startAddress) , size(size) + , unitSize(unitSize) , endianness(endianness) , memorySegmentsByKey(memorySegmentsByKey) {} @@ -46,10 +50,10 @@ namespace Targets::TargetDescription const MemorySegment& getMemorySegment(std::string_view key) const { const auto segment = this->tryGetMemorySegment(key); if (!segment.has_value()) { - throw Exceptions::InvalidTargetDescriptionDataException( - "Failed to get memory segment \"" + std::string(key) + throw Exceptions::InvalidTargetDescriptionDataException{ + "Failed to get memory segment \"" + std::string{key} + "\" from address space in TDF - segment not found" - ); + }; } return segment->get(); diff --git a/src/Targets/TargetDescription/MemorySegment.hpp b/src/Targets/TargetDescription/MemorySegment.hpp index 8a89be73..c08ad7b0 100644 --- a/src/Targets/TargetDescription/MemorySegment.hpp +++ b/src/Targets/TargetDescription/MemorySegment.hpp @@ -55,17 +55,17 @@ namespace Targets::TargetDescription return firstSubgroupIt != this->sectionsByKey.end() ? keys.size() > 1 ? firstSubgroupIt->second.tryGetSubSection(keys | std::ranges::views::drop(1)) - : std::optional(std::cref(firstSubgroupIt->second)) + : std::optional{std::cref(firstSubgroupIt->second)} : std::nullopt; } const MemorySegmentSection& getSection(std::string_view keyStr) const { const auto propertyGroup = this->tryGetSection(keyStr); if (!propertyGroup.has_value()) { - throw Exceptions::InvalidTargetDescriptionDataException( - "Failed to get memory segment section \"" + std::string(keyStr) + throw Exceptions::InvalidTargetDescriptionDataException{ + "Failed to get memory segment section \"" + std::string{keyStr} + "\" from memory segment in TDF - section not found" - ); + }; } return propertyGroup->get(); diff --git a/src/Targets/TargetDescription/MemorySegmentSection.hpp b/src/Targets/TargetDescription/MemorySegmentSection.hpp index 7565c9f5..1c532c73 100644 --- a/src/Targets/TargetDescription/MemorySegmentSection.hpp +++ b/src/Targets/TargetDescription/MemorySegmentSection.hpp @@ -66,10 +66,10 @@ namespace Targets::TargetDescription const MemorySegmentSection& getSubSection(std::string_view keyStr) const { const auto propertyGroup = this->tryGetSubSection(keyStr); if (!propertyGroup.has_value()) { - throw Exceptions::InvalidTargetDescriptionDataException( - "Failed to get memory segment sub-section \"" + std::string(keyStr) + throw Exceptions::InvalidTargetDescriptionDataException{ + "Failed to get memory segment sub-section \"" + std::string{keyStr} + "\" from memory segment in TDF - sub-section not found" - ); + }; } return propertyGroup->get(); diff --git a/src/Targets/TargetDescription/Module.hpp b/src/Targets/TargetDescription/Module.hpp index e98298dd..b9aeeef9 100644 --- a/src/Targets/TargetDescription/Module.hpp +++ b/src/Targets/TargetDescription/Module.hpp @@ -40,17 +40,17 @@ namespace Targets::TargetDescription return firstGroupIt != this->registerGroupsByKey.end() ? keys.size() > 1 ? firstGroupIt->second.tryGetSubgroup(keys | std::ranges::views::drop(1)) - : std::optional(std::cref(firstGroupIt->second)) + : std::optional{std::cref(firstGroupIt->second)} : std::nullopt; } const RegisterGroup& getRegisterGroup(std::string_view keyStr) const { const auto group = this->tryGetRegisterGroup(keyStr); if (!group.has_value()) { - throw Exceptions::InvalidTargetDescriptionDataException( - "Failed to get register group \"" + std::string(keyStr) + throw Exceptions::InvalidTargetDescriptionDataException{ + "Failed to get register group \"" + std::string{keyStr} + "\" from module in TDF - register group not found" - ); + }; } return group->get(); diff --git a/src/Targets/TargetDescription/Peripheral.hpp b/src/Targets/TargetDescription/Peripheral.hpp index 9a4098d3..8ee53568 100644 --- a/src/Targets/TargetDescription/Peripheral.hpp +++ b/src/Targets/TargetDescription/Peripheral.hpp @@ -51,10 +51,10 @@ namespace Targets::TargetDescription const RegisterGroupInstance& getRegisterGroupInstance(std::string_view key) const { const auto instance = this->tryGetRegisterGroupInstance(key); if (!instance.has_value()) { - throw Exceptions::InvalidTargetDescriptionDataException( - "Failed to get register group instance \"" + std::string(key) + throw Exceptions::InvalidTargetDescriptionDataException{ + "Failed to get register group instance \"" + std::string{key} + "\" from peripheral in TDF - register group instance not found" - ); + }; } return instance->get(); diff --git a/src/Targets/TargetDescription/PhysicalInterface.hpp b/src/Targets/TargetDescription/PhysicalInterface.hpp index ef7b1140..b9c3239a 100644 --- a/src/Targets/TargetDescription/PhysicalInterface.hpp +++ b/src/Targets/TargetDescription/PhysicalInterface.hpp @@ -1,21 +1,15 @@ #pragma once -#include #include namespace Targets::TargetDescription { struct PhysicalInterface { - std::string name; - std::string type; + std::string value; - PhysicalInterface( - const std::string& name, - const std::string& type - ) - : name(name) - , type(type) + PhysicalInterface(const std::string& value) + : value(value) {} }; } diff --git a/src/Targets/TargetDescription/Pinout.hpp b/src/Targets/TargetDescription/Pinout.hpp index 99d6c354..bc78cfe3 100644 --- a/src/Targets/TargetDescription/Pinout.hpp +++ b/src/Targets/TargetDescription/Pinout.hpp @@ -14,14 +14,14 @@ namespace Targets::TargetDescription { std::string key; std::string name; - ::Targets::PinoutType type; + ::Targets::TargetPinoutType type; std::optional function; std::vector pins; Pinout( const std::string& key, const std::string& name, - ::Targets::PinoutType type, + ::Targets::TargetPinoutType type, const std::optional& function, const std::vector& pins ) diff --git a/src/Targets/TargetDescription/PropertyGroup.hpp b/src/Targets/TargetDescription/PropertyGroup.hpp index cec2a27e..0dad3b05 100644 --- a/src/Targets/TargetDescription/PropertyGroup.hpp +++ b/src/Targets/TargetDescription/PropertyGroup.hpp @@ -49,7 +49,7 @@ namespace Targets::TargetDescription return std::nullopt; } - auto subgroup = std::optional(std::cref(firstSubgroupIt->second)); + auto subgroup = std::optional{std::cref(firstSubgroupIt->second)}; for (const auto key : keys | std::ranges::views::drop(1)) { subgroup = subgroup->get().tryGetSubgroup(key); @@ -68,10 +68,10 @@ namespace Targets::TargetDescription const PropertyGroup& getSubgroup(std::string_view keyStr) const { const auto propertyGroup = this->tryGetSubgroup(keyStr); if (!propertyGroup.has_value()) { - throw Exceptions::InvalidTargetDescriptionDataException( - "Failed to get subgroup \"" + std::string(keyStr) + throw Exceptions::InvalidTargetDescriptionDataException{ + "Failed to get subgroup \"" + std::string{keyStr} + "\" from property group in TDF - subgroup not found" - ); + }; } return propertyGroup->get(); @@ -90,9 +90,10 @@ namespace Targets::TargetDescription const Property& getProperty(std::string_view key) const { const auto property = this->tryGetProperty(key); if (!property.has_value()) { - throw Exceptions::InvalidTargetDescriptionDataException( - "Failed to get property \"" + std::string(key) + "\" from property group in TDF - property not found" - ); + throw Exceptions::InvalidTargetDescriptionDataException{ + "Failed to get property \"" + std::string{key} + + "\" from property group in TDF - property not found" + }; } return property->get(); diff --git a/src/Targets/TargetDescription/README.md b/src/Targets/TargetDescription/README.md index c06a1a25..90cd79a7 100644 --- a/src/Targets/TargetDescription/README.md +++ b/src/Targets/TargetDescription/README.md @@ -3,25 +3,25 @@ A target description file (TDF) is an XML file that describes a particular target. All targets that are supported by Bloom will have an associated TDF in Bloom's codebase. They can be found in `src/Targets/TargetDescriptionFiles`. -The data held by TDFs is fundamental to Bloom's operation. The following is an incomplete list of the data extracted -from TDFs: +The data held by TDFs is fundamental to Bloom's operation. The following is an incomplete list of the data contained +within Bloom's TDFs: -- Memory address space descriptions (including sections, offsets and sizes) -- Register descriptions (including offsets and sizes) -- Parameters for debug interfaces +- Memory address spaces, segments and sections +- Peripherals register groups, registers and signals +- Debug interface parameters - Target signatures - Package variant descriptions - Package pinouts -Given that Bloom currently only supports AVR8 targets, we only possess TDFs for AVR8 targets. - TDFs are distributed with Bloom. They are copied to the build directory (`${CMAKE_BINARY_DIR}/resources/TargetDescriptionFiles/`) at build time and included in the distributed packages (DEB, -RPM, etc). Upon copying the TDFs, we also generate a JSON mapping of AVR8 target IDs to TDF file paths. This +RPM, etc). + +Upon copying the TDFs, we also generate a JSON mapping of AVR8 target IDs to TDF file paths. This mapping is also distributed with Bloom and can be found in the `${CMAKE_BINARY_DIR}/resources/TargetDescriptionFiles/AVR` directory, with the name `Mapping.json`. The mapping is used by Bloom, at runtime, to resolve the appropriate TDF from an AVR8 target signature. The TDF file paths in the mapping are relative to Bloom's resource directory. -See the [`Avr8::Avr8(...)`](../../Targets/Microchip/AVR/AVR8/Avr8.cpp) constructor for more. +See the [`Avr8::Avr8(...)`](../../Targets/Microchip/AVR8/Avr8.cpp) constructor for more. See `build/scripts/Avr8TargetDescriptionFiles.php` for the script that performs the copying and generation of the JSON mapping. @@ -44,7 +44,7 @@ AVR8 TDFs describe certain constructs in a way that **may** not be employed by o AVR8 PORT registers are described in a `` node of the TDF. This method is currently considered to be specific to AVR8 TDFs. For that reason, we extend the `Targets::TargetDescription::TargetDescriptionFile` class to better represent TDFs for particular target families or architectures. For AVR8 targets, we use the -`Targets::Microchip::Avr::Avr8Bit::TargetDescription::TargetDescriptionFile` class to represent AVR8 TDFs. +`Targets::Microchip::Avr8::TargetDescription::TargetDescriptionFile` class to represent AVR8 TDFs. It would be good to keep in mind that as and when support for other target families and architectures is implemented, we may use the constructs that were initially specific to AVR8 TDFs, in other TDFs. In this case, those constructs will diff --git a/src/Targets/TargetDescription/Register.hpp b/src/Targets/TargetDescription/Register.hpp index 1ee499cb..cff232e8 100644 --- a/src/Targets/TargetDescription/Register.hpp +++ b/src/Targets/TargetDescription/Register.hpp @@ -60,9 +60,9 @@ namespace Targets::TargetDescription const BitField& getBitField(std::string_view key) const { const auto bitField = this->tryGetBitField(key); if (!bitField.has_value()) { - throw Exceptions::InvalidTargetDescriptionDataException( - "Failed to get bit field \"" + std::string(key) + "\" from register in TDF - bit field not found" - ); + throw Exceptions::InvalidTargetDescriptionDataException{ + "Failed to get bit field \"" + std::string{key} + "\" from register in TDF - bit field not found" + }; } return bitField->get(); diff --git a/src/Targets/TargetDescription/RegisterGroup.hpp b/src/Targets/TargetDescription/RegisterGroup.hpp index ca7bd661..e77b2489 100644 --- a/src/Targets/TargetDescription/RegisterGroup.hpp +++ b/src/Targets/TargetDescription/RegisterGroup.hpp @@ -67,10 +67,10 @@ namespace Targets::TargetDescription const RegisterGroup& getSubgroup(std::string_view keyStr) const { const auto subgroup = this->tryGetSubgroup(keyStr); if (!subgroup.has_value()) { - throw Exceptions::InvalidTargetDescriptionDataException( - "Failed to get subgroup \"" + std::string(keyStr) + throw Exceptions::InvalidTargetDescriptionDataException{ + "Failed to get subgroup \"" + std::string{keyStr} + "\" from register group in TDF - subgroup not found" - ); + }; } return subgroup->get(); @@ -89,10 +89,10 @@ namespace Targets::TargetDescription const Register& getRegister(std::string_view key) const { const auto reg = this->tryGetRegister(key); if (!reg.has_value()) { - throw Exceptions::InvalidTargetDescriptionDataException( - "Failed to get register \"" + std::string(key) + "\" from register group in TDF - register " + throw Exceptions::InvalidTargetDescriptionDataException{ + "Failed to get register \"" + std::string{key} + "\" from register group in TDF - register " "not found" - ); + }; } return reg->get(); diff --git a/src/Targets/TargetDescription/TargetDescriptionFile.cpp b/src/Targets/TargetDescription/TargetDescriptionFile.cpp index 26994838..4a230960 100644 --- a/src/Targets/TargetDescription/TargetDescriptionFile.cpp +++ b/src/Targets/TargetDescription/TargetDescriptionFile.cpp @@ -1,7 +1,6 @@ #include "TargetDescriptionFile.hpp" -#include -#include +#include #include "src/Services/PathService.hpp" #include "src/Services/StringService.hpp" @@ -29,22 +28,30 @@ namespace Targets::TargetDescription this->init(xml); } - const std::string& TargetDescriptionFile::getTargetName() const { + const std::string& TargetDescriptionFile::getName() const { return this->getDeviceAttribute("name"); } TargetFamily TargetDescriptionFile::getFamily() const { - const auto& family = this->getDeviceAttribute("family"); + const auto& familyName = this->getDeviceAttribute("family"); - if (family == "AVR8") { + if (familyName == "AVR8") { return TargetFamily::AVR_8; } - if (family == "RISCV") { + if (familyName == "RISCV") { return TargetFamily::RISC_V; } - throw InvalidTargetDescriptionDataException("Failed to resolve target family - invalid family name"); + throw InvalidTargetDescriptionDataException{"Failed to resolve target family - invalid family name"}; + } + + std::optional TargetDescriptionFile::tryGetVendorName() const { + return this->tryGetDeviceAttribute("vendor"); + } + + const std::string& TargetDescriptionFile::getVendorName() const { + return this->getDeviceAttribute("vendor"); } std::optional> TargetDescriptionFile::tryGetPropertyGroup( @@ -56,7 +63,7 @@ namespace Targets::TargetDescription return firstSubgroupIt != this->propertyGroupsByKey.end() ? keys.size() > 1 ? firstSubgroupIt->second.tryGetSubgroup(keys | std::ranges::views::drop(1)) - : std::optional(std::cref(firstSubgroupIt->second)) + : std::optional{std::cref(firstSubgroupIt->second)} : std::nullopt; } @@ -64,20 +71,46 @@ namespace Targets::TargetDescription const auto propertyGroup = this->tryGetPropertyGroup(keyStr); if (!propertyGroup.has_value()) { - throw InvalidTargetDescriptionDataException( - "Failed to get property group \"" + std::string(keyStr) + "\" from TDF - property group not found" - ); + throw InvalidTargetDescriptionDataException{ + "Failed to get property group \"" + std::string{keyStr} + "\" from TDF - property group not found" + }; } return propertyGroup->get(); } + std::optional> TargetDescriptionFile::tryGetProperty( + std::string_view groupKey, + std::string_view propertyKey + ) const { + const auto propertyGroup = this->tryGetPropertyGroup(groupKey); + + if (!propertyGroup.has_value()) { + return std::nullopt; + } + + return propertyGroup->get().tryGetProperty(propertyKey); + } + + const Property& TargetDescriptionFile::getProperty(std::string_view groupKey, std::string_view propertyKey) const { + const auto property = this->tryGetProperty(groupKey, propertyKey); + + if (!property.has_value()) { + throw InvalidTargetDescriptionDataException{ + "Failed to get property \"" + std::string{propertyKey} + "\" from group \"" + std::string{groupKey} + + "\", from TDF - property/group not found" + }; + } + + return property->get(); + } + std::optional> TargetDescriptionFile::tryGetAddressSpace( std::string_view key ) const { const auto addressSpaceIt = this->addressSpacesByKey.find(key); return addressSpaceIt != this->addressSpacesByKey.end() - ? std::optional(std::cref(addressSpaceIt->second)) + ? std::optional{std::cref(addressSpaceIt->second)} : std::nullopt; } @@ -85,29 +118,56 @@ namespace Targets::TargetDescription const auto addressSpace = this->tryGetAddressSpace(key); if (!addressSpace.has_value()) { - throw InvalidTargetDescriptionDataException( - "Failed to get address space \"" + std::string(key) + "\" from TDF - address space not found" - ); + throw InvalidTargetDescriptionDataException{ + "Failed to get address space \"" + std::string{key} + "\" from TDF - address space not found" + }; } return addressSpace->get(); } + std::optional> TargetDescriptionFile::tryGetMemorySegment( + std::string_view addressSpaceKey, + std::string_view segmentKey + ) const { + const auto addressSpace = this->tryGetAddressSpace(addressSpaceKey); + + if (!addressSpace.has_value()) { + return std::nullopt; + } + + return addressSpace->get().tryGetMemorySegment(segmentKey); + } + + const MemorySegment& TargetDescriptionFile::getMemorySegment( + std::string_view addressSpaceKey, + std::string_view segmentKey + ) const { + const auto segment = this->tryGetMemorySegment(addressSpaceKey, segmentKey); + + if (!segment.has_value()) { + throw InvalidTargetDescriptionDataException{ + "Failed to get memory segment \"" + std::string{segmentKey} + "\" from address space \"" + + std::string{addressSpaceKey} + "\" from TDF" + }; + } + + return segment->get(); + } + std::set TargetDescriptionFile::getPhysicalInterfaces() const { - static const auto physicalInterfacesByName = BiMap({ + static const auto physicalInterfacesByValue = BiMap{ {"updi", TargetPhysicalInterface::UPDI}, - {"debugwire", TargetPhysicalInterface::DEBUG_WIRE}, + {"debug_wire", TargetPhysicalInterface::DEBUG_WIRE}, {"jtag", TargetPhysicalInterface::JTAG}, {"pdi", TargetPhysicalInterface::PDI}, {"isp", TargetPhysicalInterface::ISP}, - }); + }; - auto output = std::set(); + auto output = std::set{}; for (const auto& physicalInterface : this->physicalInterfaces) { - const auto interface = physicalInterfacesByName.valueAt( - StringService::asciiToLower(physicalInterface.name) - ); + const auto interface = physicalInterfacesByValue.valueAt(physicalInterface.value); if (interface.has_value()) { output.insert(*interface); @@ -122,7 +182,7 @@ namespace Targets::TargetDescription ) const { const auto moduleIt = this->modulesByKey.find(key); return moduleIt != this->modulesByKey.end() - ? std::optional(std::cref(moduleIt->second)) + ? std::optional{std::cref(moduleIt->second)} : std::nullopt; } @@ -130,9 +190,9 @@ namespace Targets::TargetDescription const auto module = this->tryGetModule(key); if (!module.has_value()) { - throw InvalidTargetDescriptionDataException( - "Failed to get module \"" + std::string(key) + "\" from TDF - module not found" - ); + throw InvalidTargetDescriptionDataException{ + "Failed to get module \"" + std::string{key} + "\" from TDF - module not found" + }; } return module->get(); @@ -143,7 +203,7 @@ namespace Targets::TargetDescription ) const { const auto peripheralIt = this->peripheralsByKey.find(key); return peripheralIt != this->peripheralsByKey.end() - ? std::optional(std::cref(peripheralIt->second)) + ? std::optional{std::cref(peripheralIt->second)} : std::nullopt; } @@ -151,96 +211,151 @@ namespace Targets::TargetDescription const auto peripheral = this->tryGetPeripheral(key); if (!peripheral.has_value()) { - throw InvalidTargetDescriptionDataException( - "Failed to get peripheral \"" + std::string(key) + "\" from TDF - peripheral not found" - ); + throw InvalidTargetDescriptionDataException{ + "Failed to get peripheral \"" + std::string{key} + "\" from TDF - peripheral not found" + }; } return peripheral->get(); } - TargetDescriptor TargetDescriptionFile::targetDescriptor() const { - const auto targetFamily = this->getFamily(); - const auto vendor = this->tryGetDeviceAttribute("vendor"); + std::optional TargetDescriptionFile::tryGetTargetMemorySegmentDescriptor( + std::string_view addressSpaceKey, + std::string_view segmentKey + ) const { + const auto addressSpace = this->tryGetAddressSpace(addressSpaceKey); - auto output = TargetDescriptor( - this->getDeviceAttribute("name"), - targetFamily, - "", - vendor.has_value() - ? vendor->get() - : targetFamily == TargetFamily::AVR_8 - ? "Microchip" - : "Unknown", - {}, - {}, - {}, - BreakpointResources(std::nullopt, std::nullopt, 0) - ); - - for (const auto& [key, addressSpace] : this->addressSpacesByKey) { - auto descriptor = this->targetAddressSpaceDescriptorFromAddressSpace(addressSpace); - output.addressSpaceDescriptorsByKey.emplace(descriptor.key, std::move(descriptor)); + if (!addressSpace.has_value()) { + return std::nullopt; } - for (const auto& [key, peripheral] : this->peripheralsByKey) { - const auto& peripheralModule = this->getModule(peripheral.moduleKey); + const auto segment = addressSpace->get().tryGetMemorySegment(segmentKey); - auto descriptor = TargetPeripheralDescriptor( - peripheral.key, - peripheral.name, - {} - ); + if (!segment.has_value()) { + return std::nullopt; + } - for (const auto& [key, registerGroupInstance] : peripheral.registerGroupInstancesByKey) { - const auto& addressSpaceDescriptor = output.getAddressSpaceDescriptor( - registerGroupInstance.addressSpaceKey - ); - descriptor.registerGroupDescriptorsByKey.emplace( - key, - TargetDescriptionFile::targetRegisterGroupDescriptorFromRegisterGroup( - peripheralModule.getRegisterGroup(registerGroupInstance.registerGroupKey), - peripheralModule, - registerGroupInstance.offset, - addressSpaceDescriptor.key, - addressSpaceDescriptor.id, - registerGroupInstance.key, - registerGroupInstance.name, - registerGroupInstance.description - ) - ); - } + return TargetDescriptionFile::targetMemorySegmentDescriptorFromMemorySegment( + segment->get(), + addressSpace->get() + ); + } - output.peripheralDescriptorsByKey.emplace(descriptor.key, std::move(descriptor)); + TargetMemorySegmentDescriptor TargetDescriptionFile::getTargetMemorySegmentDescriptor( + std::string_view addressSpaceKey, + std::string_view segmentKey + ) const { + const auto& addressSpace = this->getAddressSpace(addressSpaceKey); + return TargetDescriptionFile::targetMemorySegmentDescriptorFromMemorySegment( + addressSpace.getMemorySegment(segmentKey), + addressSpace + ); + } + + std::optional TargetDescriptionFile::tryGetTargetPeripheralDescriptor( + std::string_view key + ) const { + const auto peripheral = this->tryGetPeripheral(key); + + if (!peripheral.has_value()) { + return std::nullopt; + } + + return TargetDescriptionFile::targetPeripheralDescriptorFromPeripheral( + peripheral->get(), + this->getModule(peripheral->get().moduleKey) + ); + } + + TargetPeripheralDescriptor TargetDescriptionFile::getTargetPeripheralDescriptor(std::string_view key) const { + const auto& peripheral = this->getPeripheral(key); + + return TargetDescriptionFile::targetPeripheralDescriptorFromPeripheral( + peripheral, + this->getModule(peripheral.moduleKey) + ); + } + + std::map< + std::string, + TargetAddressSpaceDescriptor + > TargetDescriptionFile::targetAddressSpaceDescriptorsByKey() const { + auto output = std::map{}; + + for (const auto& [key, addressSpace] : this->addressSpacesByKey) { + output.emplace(key, TargetDescriptionFile::targetAddressSpaceDescriptorFromAddressSpace(addressSpace)); } return output; } - std::map< - TargetAddressSpaceDescriptorId, - TargetAddressSpaceDescriptor - > TargetDescriptionFile::targetAddressSpaceDescriptorsById() const { - auto output = std::map(); + std::map TargetDescriptionFile::targetPeripheralDescriptorsByKey() const { + auto output = std::map{}; - for (const auto& [key, addressSpace] : this->addressSpacesByKey) { - auto descriptor = this->targetAddressSpaceDescriptorFromAddressSpace(addressSpace); - output.emplace(descriptor.id, std::move(descriptor)); + for (const auto& [key, peripheral] : this->peripheralsByKey) { + output.emplace( + key, + TargetDescriptionFile::targetPeripheralDescriptorFromPeripheral( + peripheral, + this->getModule(peripheral.moduleKey) + ) + ); + } + + return output; + } + + std::map TargetDescriptionFile::targetPinoutDescriptorsByKey() const { + auto output = std::map{}; + + for (const auto& [key, pinout] : this->pinoutsByKey) { + output.emplace( + key, + TargetDescriptionFile::targetPinoutDescriptorFromPinout(pinout) + ); + } + + return output; + } + + std::vector TargetDescriptionFile::targetVariantDescriptors() const { + auto output = std::vector{}; + + for (const auto& variant : this->variants) { + output.emplace_back(TargetDescriptionFile::targetVariantDescriptorFromVariant(variant)); + } + + return output; + } + + std::vector TargetDescriptionFile::gpioPortPeripheralDescriptors() const { + auto output = std::vector{}; + + const auto& gpioPortModule = this->getModule("gpio_port"); + + for (const auto& [peripheralKey, peripheral] : this->peripheralsByKey) { + if (peripheral.moduleKey != gpioPortModule.key) { + continue; + } + + output.emplace_back( + TargetDescriptionFile::targetPeripheralDescriptorFromPeripheral(peripheral, gpioPortModule) + ); } return output; } void TargetDescriptionFile::init(const std::string& xmlFilePath) { - auto file = QFile(QString::fromStdString(xmlFilePath)); + auto file = QFile{QString::fromStdString(xmlFilePath)}; if (!file.exists()) { - throw InternalFatalErrorException("Failed to load target description file - file not found"); + throw InternalFatalErrorException{"Failed to load target description file - file not found"}; } file.open(QIODevice::ReadOnly); - auto document = QDomDocument(); + auto document = QDomDocument{}; if (!document.setContent(file.readAll())) { - throw TargetDescriptionParsingFailureException(); + throw TargetDescriptionParsingFailureException{}; } this->init(document); @@ -249,17 +364,15 @@ namespace Targets::TargetDescription void TargetDescriptionFile::init(const QDomDocument& document) { const auto deviceElement = document.documentElement(); if (deviceElement.nodeName() != "device") { - throw TargetDescriptionParsingFailureException("Root \"device\" element not found."); + throw TargetDescriptionParsingFailureException{"Root \"device\" element not found."}; } const auto deviceAttributes = deviceElement.attributes(); for (auto i = 0; i < deviceAttributes.length(); ++i) { const auto deviceAttribute = deviceAttributes.item(i); - this->deviceAttributesByName.insert( - std::pair( - deviceAttribute.nodeName().toStdString(), - deviceAttribute.nodeValue().toStdString() - ) + this->deviceAttributesByName.emplace( + deviceAttribute.nodeName().toStdString(), + deviceAttribute.nodeValue().toStdString() ); } @@ -269,9 +382,7 @@ namespace Targets::TargetDescription element = element.nextSiblingElement("property-group") ) { auto propertyGroup = TargetDescriptionFile::propertyGroupFromXml(element); - this->propertyGroupsByKey.insert( - std::pair(propertyGroup.key, std::move(propertyGroup)) - ); + this->propertyGroupsByKey.emplace(propertyGroup.key, std::move(propertyGroup)); } for ( @@ -280,9 +391,7 @@ namespace Targets::TargetDescription element = element.nextSiblingElement("address-space") ) { auto addressSpace = TargetDescriptionFile::addressSpaceFromXml(element); - this->addressSpacesByKey.insert( - std::pair(addressSpace.key, std::move(addressSpace)) - ); + this->addressSpacesByKey.emplace(addressSpace.key, std::move(addressSpace)); } for ( @@ -291,9 +400,7 @@ namespace Targets::TargetDescription !element.isNull(); element = element.nextSiblingElement("physical-interface") ) { - this->physicalInterfaces.emplace_back( - TargetDescriptionFile::physicalInterfaceFromXml(element) - ); + this->physicalInterfaces.emplace_back(TargetDescriptionFile::physicalInterfaceFromXml(element)); } for ( @@ -302,9 +409,7 @@ namespace Targets::TargetDescription element = element.nextSiblingElement("module") ) { auto module = TargetDescriptionFile::moduleFromXml(element); - this->modulesByKey.insert( - std::pair(module.key, std::move(module)) - ); + this->modulesByKey.emplace(module.key, std::move(module)); } for ( @@ -313,9 +418,7 @@ namespace Targets::TargetDescription element = element.nextSiblingElement("peripheral") ) { auto peripheral = TargetDescriptionFile::peripheralFromXml(element); - this->peripheralsByKey.insert( - std::pair(peripheral.key, std::move(peripheral)) - ); + this->peripheralsByKey.emplace(peripheral.key, std::move(peripheral)); } for ( @@ -324,9 +427,7 @@ namespace Targets::TargetDescription element = element.nextSiblingElement("pinout") ) { auto pinout = TargetDescriptionFile::pinoutFromXml(element); - this->pinoutsByKey.insert( - std::pair(pinout.key, std::move(pinout)) - ); + this->pinoutsByKey.emplace(pinout.key, std::move(pinout)); } for ( @@ -354,7 +455,7 @@ namespace Targets::TargetDescription const auto attribute = this->tryGetDeviceAttribute(attributeName); if (!attribute.has_value()) { - throw InvalidTargetDescriptionDataException("Missing target device attribute (\"" + attributeName + "\")"); + throw InvalidTargetDescriptionDataException{"Missing target device attribute (\"" + attributeName + "\")"}; } return attribute->get(); @@ -365,7 +466,7 @@ namespace Targets::TargetDescription const QString& attributeName ) { return element.hasAttribute(attributeName) - ? std::optional(element.attribute(attributeName).toStdString()) + ? std::optional{element.attribute(attributeName).toStdString()} : std::nullopt; } @@ -373,17 +474,17 @@ namespace Targets::TargetDescription const auto attribute = TargetDescriptionFile::tryGetAttribute(element, attributeName); if (!attribute.has_value()) { - throw InvalidTargetDescriptionDataException( + throw InvalidTargetDescriptionDataException{ "Failed to fetch attribute from TDF element \"" + element.nodeName().toStdString() + "\" - attribute \"" + attributeName.toStdString() + "\" not found" - ); + }; } return *attribute; } PropertyGroup TargetDescriptionFile::propertyGroupFromXml(const QDomElement& xmlElement) { - auto output = PropertyGroup(TargetDescriptionFile::getAttribute(xmlElement, "key"), {}, {}); + auto output = PropertyGroup{TargetDescriptionFile::getAttribute(xmlElement, "key"), {}, {}}; for ( auto element = xmlElement.firstChildElement("property"); @@ -391,7 +492,7 @@ namespace Targets::TargetDescription element = element.nextSiblingElement("property") ) { auto property = TargetDescriptionFile::propertyFromXml(element); - output.propertiesByKey.insert(std::pair(property.key, std::move(property))); + output.propertiesByKey.emplace(property.key, std::move(property)); } for ( @@ -400,45 +501,48 @@ namespace Targets::TargetDescription element = element.nextSiblingElement("property-group") ) { auto subgroup = TargetDescriptionFile::propertyGroupFromXml(element); - output.subgroupsByKey.insert(std::pair(subgroup.key, std::move(subgroup))); + output.subgroupsByKey.emplace(subgroup.key, std::move(subgroup)); } return output; } Property TargetDescriptionFile::propertyFromXml(const QDomElement& xmlElement) { - return Property( + return { TargetDescriptionFile::getAttribute(xmlElement, "key"), TargetDescriptionFile::getAttribute(xmlElement, "value") - ); + }; } AddressSpace TargetDescriptionFile::addressSpaceFromXml(const QDomElement& xmlElement) { - static const auto endiannessByName = BiMap({ + static const auto endiannessByName = BiMap{ {"big", TargetMemoryEndianness::BIG}, {"little", TargetMemoryEndianness::LITTLE}, - }); + }; const auto endiannessName = TargetDescriptionFile::tryGetAttribute(xmlElement, "endianness"); - auto endianness = std::optional(); + auto endianness = std::optional{}; if (endiannessName.has_value()) { endianness = endiannessByName.valueAt(*endiannessName); if (!endianness.has_value()) { - throw InvalidTargetDescriptionDataException( + throw InvalidTargetDescriptionDataException{ "Failed to extract address space from TDF - invalid endianness name \"" + *endiannessName + "\"" - ); + }; } } - auto output = AddressSpace( + const auto unitSize = TargetDescriptionFile::tryGetAttribute(xmlElement, "unit-size"); + + auto output = AddressSpace{ TargetDescriptionFile::getAttribute(xmlElement, "key"), StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "start")), StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "size")), + unitSize.has_value() ? StringService::toUint8(*unitSize) : std::uint8_t{1}, endianness, {} - ); + }; for ( auto element = xmlElement.firstChildElement("memory-segment"); @@ -446,14 +550,15 @@ namespace Targets::TargetDescription element = element.nextSiblingElement("memory-segment") ) { auto section = TargetDescriptionFile::memorySegmentFromXml(element); - output.memorySegmentsByKey.insert(std::pair(section.key, std::move(section))); + output.memorySegmentsByKey.emplace(section.key, std::move(section)); } return output; } MemorySegment TargetDescriptionFile::memorySegmentFromXml(const QDomElement& xmlElement) { - static const auto typesByName = BiMap({ + static const auto typesByName = BiMap{ + {"gp_registers", TargetMemorySegmentType::GENERAL_PURPOSE_REGISTERS}, {"aliased", TargetMemorySegmentType::ALIASED}, {"regs", TargetMemorySegmentType::REGISTERS}, {"eeprom", TargetMemorySegmentType::EEPROM}, @@ -466,36 +571,36 @@ namespace Targets::TargetDescription {"production_signatures", TargetMemorySegmentType::PRODUCTION_SIGNATURES}, {"signatures", TargetMemorySegmentType::SIGNATURES}, {"user_signatures", TargetMemorySegmentType::USER_SIGNATURES}, - }); + }; const auto typeName = TargetDescriptionFile::getAttribute(xmlElement, "type"); const auto type = typesByName.valueAt(typeName); if (!type.has_value()) { - throw InvalidTargetDescriptionDataException( + throw InvalidTargetDescriptionDataException{ "Failed to extract memory segment from TDF - invalid memory segment type name \"" + typeName + "\"" - ); + }; } const auto pageSize = TargetDescriptionFile::tryGetAttribute(xmlElement, "page-size"); const auto accessString = TargetDescriptionFile::tryGetAttribute(xmlElement, "access"); - auto output = MemorySegment( + auto output = MemorySegment{ TargetDescriptionFile::getAttribute(xmlElement, "key"), TargetDescriptionFile::getAttribute(xmlElement, "name"), *type, StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "start")), StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "size")), - TargetMemoryAccess( + TargetDescriptionFile::getAttribute(xmlElement, "executable") == "1", + TargetMemoryAccess{ accessString.has_value() ? accessString->find('R') != std::string::npos : true, - accessString.has_value() ? accessString->find('W') != std::string::npos : true, accessString.has_value() ? accessString->find('W') != std::string::npos : true - ), + }, pageSize.has_value() - ? std::optional(StringService::toUint32(*pageSize)) + ? std::optional{StringService::toUint32(*pageSize)} : std::nullopt, {} - ); + }; for ( auto element = xmlElement.firstChildElement("section"); @@ -503,20 +608,20 @@ namespace Targets::TargetDescription element = element.nextSiblingElement("section") ) { auto section = TargetDescriptionFile::memorySegmentSectionFromXml(element); - output.sectionsByKey.insert(std::pair(section.key, std::move(section))); + output.sectionsByKey.emplace(section.key, std::move(section)); } return output; } MemorySegmentSection TargetDescriptionFile::memorySegmentSectionFromXml(const QDomElement& xmlElement) { - auto output = MemorySegmentSection( + auto output = MemorySegmentSection{ TargetDescriptionFile::getAttribute(xmlElement, "key"), TargetDescriptionFile::getAttribute(xmlElement, "name"), StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "start")), StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "size")), {} - ); + }; for ( auto element = xmlElement.firstChildElement("section"); @@ -524,26 +629,25 @@ namespace Targets::TargetDescription element = element.nextSiblingElement("section") ) { auto section = TargetDescriptionFile::memorySegmentSectionFromXml(element); - output.subSectionsByKey.insert(std::pair(section.key, std::move(section))); + output.subSectionsByKey.emplace(section.key, std::move(section)); } return output; } PhysicalInterface TargetDescriptionFile::physicalInterfaceFromXml(const QDomElement& xmlElement) { - return PhysicalInterface( - TargetDescriptionFile::getAttribute(xmlElement, "name"), - TargetDescriptionFile::getAttribute(xmlElement, "type") - ); + return { + TargetDescriptionFile::getAttribute(xmlElement, "value") + }; } Module TargetDescriptionFile::moduleFromXml(const QDomElement& xmlElement) { - auto output = Module( + auto output = Module{ TargetDescriptionFile::getAttribute(xmlElement, "key"), TargetDescriptionFile::getAttribute(xmlElement, "name"), TargetDescriptionFile::getAttribute(xmlElement, "description"), {} - ); + }; for ( auto element = xmlElement.firstChildElement("register-group"); @@ -551,7 +655,7 @@ namespace Targets::TargetDescription element = element.nextSiblingElement("register-group") ) { auto registerGroup = TargetDescriptionFile::registerGroupFromXml(element); - output.registerGroupsByKey.insert(std::pair(registerGroup.key, std::move(registerGroup))); + output.registerGroupsByKey.emplace(registerGroup.key, std::move(registerGroup)); } return output; @@ -560,14 +664,14 @@ namespace Targets::TargetDescription RegisterGroup TargetDescriptionFile::registerGroupFromXml(const QDomElement& xmlElement) { const auto offset = TargetDescriptionFile::tryGetAttribute(xmlElement, "offset"); - auto output = RegisterGroup( + auto output = RegisterGroup{ TargetDescriptionFile::getAttribute(xmlElement, "key"), TargetDescriptionFile::getAttribute(xmlElement, "name"), - offset.has_value() ? std::optional(StringService::toUint32(*offset)) : std::nullopt, + offset.has_value() ? std::optional{StringService::toUint32(*offset)} : std::nullopt, {}, {}, {} - ); + }; for ( auto element = xmlElement.firstChildElement("register"); @@ -575,7 +679,7 @@ namespace Targets::TargetDescription element = element.nextSiblingElement("register") ) { auto reg = TargetDescriptionFile::registerFromXml(element); - output.registersByKey.insert(std::pair(reg.key, std::move(reg))); + output.registersByKey.emplace(reg.key, std::move(reg)); } for ( @@ -584,7 +688,7 @@ namespace Targets::TargetDescription element = element.nextSiblingElement("register-group") ) { auto registerGroup = TargetDescriptionFile::registerGroupFromXml(element); - output.subgroupsByKey.insert(std::pair(registerGroup.key, std::move(registerGroup))); + output.subgroupsByKey.emplace(registerGroup.key, std::move(registerGroup)); } for ( @@ -593,22 +697,20 @@ namespace Targets::TargetDescription element = element.nextSiblingElement("register-group-reference") ) { auto registerGroupReference = TargetDescriptionFile::registerGroupReferenceFromXml(element); - output.subgroupReferencesByKey.insert( - std::pair(registerGroupReference.key, std::move(registerGroupReference)) - ); + output.subgroupReferencesByKey.emplace(registerGroupReference.key, std::move(registerGroupReference)); } return output; } RegisterGroupReference TargetDescriptionFile::registerGroupReferenceFromXml(const QDomElement& xmlElement) { - return RegisterGroupReference( + return { TargetDescriptionFile::getAttribute(xmlElement, "key"), TargetDescriptionFile::getAttribute(xmlElement, "name"), TargetDescriptionFile::getAttribute(xmlElement, "register-group-key"), StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "offset")), TargetDescriptionFile::tryGetAttribute(xmlElement, "description") - ); + }; } Register TargetDescriptionFile::registerFromXml(const QDomElement& xmlElement) { @@ -616,22 +718,22 @@ namespace Targets::TargetDescription const auto alternative = TargetDescriptionFile::tryGetAttribute(xmlElement, "alternative"); const auto accessString = TargetDescriptionFile::tryGetAttribute(xmlElement, "access"); - auto output = Register( + auto output = Register{ TargetDescriptionFile::getAttribute(xmlElement, "key"), TargetDescriptionFile::getAttribute(xmlElement, "name"), TargetDescriptionFile::tryGetAttribute(xmlElement, "description"), StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "offset")), StringService::toUint16(TargetDescriptionFile::getAttribute(xmlElement, "size")), - initialValue.has_value() ? std::optional(StringService::toUint64(*initialValue)) : std::nullopt, + initialValue.has_value() ? std::optional{StringService::toUint64(*initialValue)} : std::nullopt, accessString.has_value() - ? std::optional(TargetRegisterAccess( + ? std::optional{TargetRegisterAccess{ accessString->find('R') != std::string::npos, accessString->find('W') != std::string::npos - )) + }} : std::nullopt, - alternative.has_value() ? std::optional(*alternative == "true") : std::nullopt, + alternative.has_value() ? std::optional{*alternative == "true"} : std::nullopt, {} - ); + }; for ( auto element = xmlElement.firstChildElement("bit-field"); @@ -639,32 +741,32 @@ namespace Targets::TargetDescription element = element.nextSiblingElement("bit-field") ) { auto bitField = TargetDescriptionFile::bitFieldFromXml(element); - output.bitFieldsByKey.insert(std::pair(bitField.key, std::move(bitField))); + output.bitFieldsByKey.emplace(bitField.key, std::move(bitField)); } return output; } BitField TargetDescriptionFile::bitFieldFromXml(const QDomElement& xmlElement) { - return BitField( + return { TargetDescriptionFile::getAttribute(xmlElement, "key"), TargetDescriptionFile::getAttribute(xmlElement, "name"), TargetDescriptionFile::tryGetAttribute(xmlElement, "description"), StringService::toUint64(TargetDescriptionFile::getAttribute(xmlElement, "mask")), TargetDescriptionFile::tryGetAttribute(xmlElement, "access") - ); + }; } Peripheral TargetDescriptionFile::peripheralFromXml(const QDomElement& xmlElement) { const auto offset = TargetDescriptionFile::tryGetAttribute(xmlElement, "offset"); - auto output = Peripheral( + auto output = Peripheral{ TargetDescriptionFile::getAttribute(xmlElement, "key"), TargetDescriptionFile::getAttribute(xmlElement, "name"), TargetDescriptionFile::getAttribute(xmlElement, "module-key"), {}, {} - ); + }; for ( auto element = xmlElement.firstChildElement("register-group-instance"); @@ -672,9 +774,7 @@ namespace Targets::TargetDescription element = element.nextSiblingElement("register-group-instance") ) { auto registerGroupInstance = TargetDescriptionFile::registerGroupInstanceFromXml(element); - output.registerGroupInstancesByKey.insert( - std::pair(registerGroupInstance.key, std::move(registerGroupInstance)) - ); + output.registerGroupInstancesByKey.emplace(registerGroupInstance.key, std::move(registerGroupInstance)); } for ( @@ -689,56 +789,56 @@ namespace Targets::TargetDescription } RegisterGroupInstance TargetDescriptionFile::registerGroupInstanceFromXml(const QDomElement& xmlElement) { - return RegisterGroupInstance( + return { TargetDescriptionFile::getAttribute(xmlElement, "key"), TargetDescriptionFile::getAttribute(xmlElement, "name"), TargetDescriptionFile::getAttribute(xmlElement, "register-group-key"), TargetDescriptionFile::getAttribute(xmlElement, "address-space-key"), StringService::toUint32(TargetDescriptionFile::getAttribute(xmlElement, "offset")), TargetDescriptionFile::tryGetAttribute(xmlElement, "description") - ); + }; } Signal TargetDescriptionFile::signalFromXml(const QDomElement& xmlElement) { const auto index = TargetDescriptionFile::tryGetAttribute(xmlElement, "index"); - return Signal( + return { TargetDescriptionFile::getAttribute(xmlElement, "pad-id"), - index.has_value() ? std::optional(StringService::toUint64(*index)) : std::nullopt, + index.has_value() ? std::optional{StringService::toUint64(*index)} : std::nullopt, TargetDescriptionFile::tryGetAttribute(xmlElement, "function"), TargetDescriptionFile::tryGetAttribute(xmlElement, "group"), TargetDescriptionFile::tryGetAttribute(xmlElement, "field") - ); + }; } Pinout TargetDescriptionFile::pinoutFromXml(const QDomElement& xmlElement) { - static const auto typesByName = BiMap({ - {"soic", PinoutType::SOIC}, - {"ssop", PinoutType::SSOP}, - {"dip", PinoutType::DIP}, - {"qfn", PinoutType::QFN}, - {"mlf", PinoutType::MLF}, - {"drqfn", PinoutType::DUAL_ROW_QFN}, - {"qfp", PinoutType::QFP}, - {"bga", PinoutType::BGA}, - }); + static const auto typesByName = BiMap{ + {"soic", TargetPinoutType::SOIC}, + {"ssop", TargetPinoutType::SSOP}, + {"dip", TargetPinoutType::DIP}, + {"qfn", TargetPinoutType::QFN}, + {"mlf", TargetPinoutType::MLF}, + {"drqfn", TargetPinoutType::DUAL_ROW_QFN}, + {"qfp", TargetPinoutType::QFP}, + {"bga", TargetPinoutType::BGA}, + }; const auto typeName = TargetDescriptionFile::getAttribute(xmlElement, "type"); const auto type = typesByName.valueAt(typeName); if (!type.has_value()) { - throw InvalidTargetDescriptionDataException( + throw InvalidTargetDescriptionDataException{ "Failed to extract pinout from TDF - invalid pinout type name \"" + typeName + "\"" - ); + }; } - auto output = Pinout( + auto output = Pinout{ TargetDescriptionFile::getAttribute(xmlElement, "key"), TargetDescriptionFile::getAttribute(xmlElement, "name"), *type, TargetDescriptionFile::tryGetAttribute(xmlElement, "function"), {} - ); + }; for ( auto element = xmlElement.firstChildElement("pin"); @@ -752,37 +852,37 @@ namespace Targets::TargetDescription } Pin TargetDescriptionFile::pinFromXml(const QDomElement& xmlElement) { - return Pin( + return { TargetDescriptionFile::getAttribute(xmlElement, "position"), TargetDescriptionFile::getAttribute(xmlElement, "pad") - ); + }; } Variant TargetDescriptionFile::variantFromXml(const QDomElement& xmlElement) { - return Variant( + return { TargetDescriptionFile::getAttribute(xmlElement, "name"), TargetDescriptionFile::getAttribute(xmlElement, "pinout-key"), TargetDescriptionFile::getAttribute(xmlElement, "package") - ); + }; } TargetAddressSpaceDescriptor TargetDescriptionFile::targetAddressSpaceDescriptorFromAddressSpace( const AddressSpace& addressSpace ) { - auto output = TargetAddressSpaceDescriptor( + auto output = TargetAddressSpaceDescriptor{ addressSpace.key, - TargetMemoryAddressRange( + TargetMemoryAddressRange{ addressSpace.startAddress, addressSpace.startAddress + addressSpace.size - 1 - ), + }, addressSpace.endianness.value_or(TargetMemoryEndianness::LITTLE), {} - ); + }; for (const auto& [key, memorySegment] : addressSpace.memorySegmentsByKey) { output.segmentDescriptorsByKey.emplace( key, - TargetDescriptionFile::targetMemorySegmentDescriptorFromMemorySegment(memorySegment) + TargetDescriptionFile::targetMemorySegmentDescriptorFromMemorySegment(memorySegment, addressSpace) ); } @@ -790,55 +890,99 @@ namespace Targets::TargetDescription } TargetMemorySegmentDescriptor TargetDescriptionFile::targetMemorySegmentDescriptorFromMemorySegment( - const MemorySegment& memorySegment + const MemorySegment& memorySegment, + const AddressSpace& addressSpace ) { - return TargetMemorySegmentDescriptor( + return { + addressSpace.key, memorySegment.key, memorySegment.name, memorySegment.type, - TargetMemoryAddressRange( + TargetMemoryAddressRange{ memorySegment.startAddress, - memorySegment.startAddress + memorySegment.size - 1 - ), + memorySegment.startAddress + (memorySegment.size / addressSpace.unitSize) - 1 + }, + addressSpace.unitSize, memorySegment.executable, memorySegment.access, memorySegment.access, memorySegment.pageSize - ); + }; + } + + TargetPeripheralDescriptor TargetDescriptionFile::targetPeripheralDescriptorFromPeripheral( + const Peripheral& peripheral, + const Module& peripheralModule + ) { + auto output = TargetPeripheralDescriptor{ + peripheral.key, + peripheral.name, + {}, + {} + }; + + for (const auto& [key, registerGroupInstance] : peripheral.registerGroupInstancesByKey) { + output.registerGroupDescriptorsByKey.emplace( + key, + TargetDescriptionFile::targetRegisterGroupDescriptorFromRegisterGroup( + registerGroupInstance.key, + registerGroupInstance.name, + registerGroupInstance.addressSpaceKey, + registerGroupInstance.description, + peripheralModule.getRegisterGroup(registerGroupInstance.registerGroupKey), + peripheralModule, + registerGroupInstance.offset + ) + ); + } + + for (const auto& signal : peripheral.sigs) { + output.signalDescriptors.emplace_back( + TargetDescriptionFile::targetPeripheralSignalDescriptorFromSignal(signal) + ); + } + + return output; + } + + TargetPeripheralSignalDescriptor TargetDescriptionFile::targetPeripheralSignalDescriptorFromSignal( + const Signal& signal + ) { + return { + signal.padId, + signal.index + }; } TargetRegisterGroupDescriptor TargetDescriptionFile::targetRegisterGroupDescriptorFromRegisterGroup( - const RegisterGroup& registerGroup, - const Module& peripheralModule, - TargetMemoryAddress baseAddress, - const std::string& addressSpaceKey, - TargetAddressSpaceDescriptorId addressSpaceDescriptorId, const std::string& key, const std::string& name, - const std::optional& description + const std::string& addressSpaceKey, + const std::optional& description, + const RegisterGroup& registerGroup, + const Module& peripheralModule, + TargetMemoryAddress baseAddress ) { - auto output = TargetRegisterGroupDescriptor( + auto output = TargetRegisterGroupDescriptor{ key, name, addressSpaceKey, - addressSpaceDescriptorId, description, {}, {} - ); + }; for (const auto& [key, subgroup] : registerGroup.subgroupsByKey) { output.subgroupDescriptorsByKey.emplace( key, TargetDescriptionFile::targetRegisterGroupDescriptorFromRegisterGroup( - registerGroup, - peripheralModule, - baseAddress + registerGroup.offset.value_or(0), - addressSpaceKey, - addressSpaceDescriptorId, subgroup.key, subgroup.name, - std::nullopt + addressSpaceKey, + std::nullopt, + registerGroup, + peripheralModule, + baseAddress + registerGroup.offset.value_or(0) ) ); } @@ -848,14 +992,13 @@ namespace Targets::TargetDescription output.subgroupDescriptorsByKey.emplace( key, TargetDescriptionFile::targetRegisterGroupDescriptorFromRegisterGroup( - registerGroup, - peripheralModule, - baseAddress + subgroupReference.offset + registerGroup.offset.value_or(0), - addressSpaceKey, - addressSpaceDescriptorId, subgroupReference.key, subgroupReference.name, - subgroupReference.description + addressSpaceKey, + subgroupReference.description, + registerGroup, + peripheralModule, + baseAddress + subgroupReference.offset + registerGroup.offset.value_or(0) ) ); } @@ -866,7 +1009,6 @@ namespace Targets::TargetDescription TargetDescriptionFile::targetRegisterDescriptorFromRegister( reg, addressSpaceKey, - addressSpaceDescriptorId, baseAddress + registerGroup.offset.value_or(0) ) ); @@ -878,18 +1020,84 @@ namespace Targets::TargetDescription TargetRegisterDescriptor TargetDescriptionFile::targetRegisterDescriptorFromRegister( const Register& reg, const std::string& addressSpaceKey, - TargetAddressSpaceDescriptorId addressSpaceDescriptorId, TargetMemoryAddress baseAddress ) { - return TargetRegisterDescriptor( + auto output = TargetRegisterDescriptor{ reg.key, reg.name, addressSpaceKey, - addressSpaceDescriptorId, baseAddress + reg.offset, reg.size, - reg.access.value_or(TargetRegisterAccess(true, true)), - reg.description - ); + TargetRegisterType::OTHER, + reg.access.value_or(TargetRegisterAccess{true, true}), + reg.description, + {} + }; + + for (const auto& [key, bitField] : reg.bitFieldsByKey) { + output.bitFieldDescriptorsByKey.emplace( + key, + TargetDescriptionFile::targetBitFieldDescriptorFromBitField(bitField) + ); + } + + return output; + } + + TargetBitFieldDescriptor TargetDescriptionFile::targetBitFieldDescriptorFromBitField(const BitField& bitField) { + return { + bitField.key, + bitField.name, + bitField.mask, + bitField.description + }; + } + + TargetPinoutDescriptor TargetDescriptionFile::targetPinoutDescriptorFromPinout(const Pinout& pinout) { + auto output = TargetPinoutDescriptor{ + pinout.key, + pinout.name, + pinout.type, + {} + }; + + for (const auto& pin : pinout.pins) { + output.pinDescriptors.emplace_back(TargetDescriptionFile::targetPinDescriptorFromPin(pin)); + } + + return output; + } + + TargetPinDescriptor TargetDescriptionFile::targetPinDescriptorFromPin(const Pin& pin) { + static const auto resolvePinType = [] (const std::string& pad) -> TargetPinType { + if ( + pad.find("vcc") == 0 + || pad.find("avcc") == 0 + || pad.find("aref") == 0 + || pad.find("avdd") == 0 + || pad.find("vdd") == 0 + ) { + return TargetPinType::VCC; + } + + if (pad.find("gnd") == 0) { + return TargetPinType::GND; + } + + return TargetPinType::OTHER; + }; + + return { + pin.pad, + pin.position, + resolvePinType(pin.pad) + }; + } + + TargetVariantDescriptor TargetDescriptionFile::targetVariantDescriptorFromVariant(const Variant& variant) { + return { + variant.name, + variant.pinoutKey + }; } } diff --git a/src/Targets/TargetDescription/TargetDescriptionFile.hpp b/src/Targets/TargetDescription/TargetDescriptionFile.hpp index 8c0a18c5..ff9c31ed 100644 --- a/src/Targets/TargetDescription/TargetDescriptionFile.hpp +++ b/src/Targets/TargetDescription/TargetDescriptionFile.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include @@ -30,8 +29,13 @@ #include "src/Targets/TargetAddressSpaceDescriptor.hpp" #include "src/Targets/TargetMemorySegmentDescriptor.hpp" #include "src/Targets/TargetPeripheralDescriptor.hpp" +#include "src/Targets/TargetPeripheralSignalDescriptor.hpp" #include "src/Targets/TargetRegisterGroupDescriptor.hpp" #include "src/Targets/TargetRegisterDescriptor.hpp" +#include "src/Targets/TargetBitFieldDescriptor.hpp" +#include "src/Targets/TargetPinoutDescriptor.hpp" +#include "src/Targets/TargetPinDescriptor.hpp" +#include "src/Targets/TargetVariantDescriptor.hpp" #include "src/Targets/TargetPhysicalInterface.hpp" namespace Targets::TargetDescription @@ -46,12 +50,10 @@ namespace Targets::TargetDescription * During the build process, all target description files are copied to the distribution directory, ready * to be shipped with the Bloom binary. * - * Processing of target description files is done in this class. - * * This class may be extended to further reflect a TDF that is specific to a particular target, target architecture - * or target family. For example, the Targets::Microchip::Avr::Avr8Bit::TargetDescription::TargetDescriptionFile - * class inherits from this class, to represent TDFs for AVR8 targets. The derived class provides access to - * additional data that is only found in AVR8 TDFs (such as AVR target signature, AVR Family, etc). + * or target family. For example, the Targets::Microchip::Avr8::TargetDescriptionFile class inherits from this + * class, to represent TDFs for AVR8 targets. The derived class provides access to additional data that is only + * found in AVR8 TDFs (such as AVR target signature, AVR Family, etc). * * For more information of TDFs, see src/Targets/TargetDescription/README.md */ @@ -78,7 +80,7 @@ namespace Targets::TargetDescription * * @return */ - [[nodiscard]] const std::string& getTargetName() const; + [[nodiscard]] const std::string& getName() const; /** * Returns the target family extracted from the TDF. @@ -87,16 +89,35 @@ namespace Targets::TargetDescription */ [[nodiscard]] TargetFamily getFamily() const; + [[nodiscard]] std::optional tryGetVendorName() const; + [[nodiscard]] const std::string& getVendorName() const; + [[nodiscard]] std::optional> tryGetPropertyGroup( std::string_view keyStr ) const; [[nodiscard]] const PropertyGroup& getPropertyGroup(std::string_view keyStr) const; + [[nodiscard]] std::optional> tryGetProperty( + std::string_view groupKey, + std::string_view propertyKey + ) const; + + [[nodiscard]] const Property& getProperty(std::string_view groupKey, std::string_view propertyKey) const; + [[nodiscard]] std::optional> tryGetAddressSpace( std::string_view key ) const; [[nodiscard]] const AddressSpace& getAddressSpace(std::string_view key) const; + [[nodiscard]] std::optional> tryGetMemorySegment( + std::string_view addressSpaceKey, + std::string_view segmentKey + ) const; + [[nodiscard]] const MemorySegment& getMemorySegment( + std::string_view addressSpaceKey, + std::string_view segmentKey + ) const; + [[nodiscard]] std::set getPhysicalInterfaces() const; [[nodiscard]] std::optional> tryGetModule( @@ -109,8 +130,25 @@ namespace Targets::TargetDescription ) const; [[nodiscard]] const Peripheral& getPeripheral(std::string_view key) const; - TargetDescriptor targetDescriptor() const; - std::map targetAddressSpaceDescriptorsById() const; + [[nodiscard]] std::optional tryGetTargetMemorySegmentDescriptor( + std::string_view addressSpaceKey, + std::string_view segmentKey + ) const; + [[nodiscard]] TargetMemorySegmentDescriptor getTargetMemorySegmentDescriptor( + std::string_view addressSpaceKey, + std::string_view segmentKey + ) const; + + [[nodiscard]] std::optional tryGetTargetPeripheralDescriptor( + std::string_view key + ) const; + [[nodiscard]] TargetPeripheralDescriptor getTargetPeripheralDescriptor(std::string_view key) const; + + [[nodiscard]] std::map targetAddressSpaceDescriptorsByKey() const; + [[nodiscard]] std::map targetPeripheralDescriptorsByKey() const; + [[nodiscard]] std::map targetPinoutDescriptorsByKey() const; + [[nodiscard]] std::vector targetVariantDescriptors() const; + [[nodiscard]] std::vector gpioPortPeripheralDescriptors() const; protected: std::map deviceAttributesByName; @@ -165,25 +203,38 @@ namespace Targets::TargetDescription ); static TargetMemorySegmentDescriptor targetMemorySegmentDescriptorFromMemorySegment( - const MemorySegment& memorySegment + const MemorySegment& memorySegment, + const AddressSpace& addressSpace ); + static TargetPeripheralDescriptor targetPeripheralDescriptorFromPeripheral( + const Peripheral& peripheral, + const Module& peripheralModule + ); + + static TargetPeripheralSignalDescriptor targetPeripheralSignalDescriptorFromSignal(const Signal& signal); + static TargetRegisterGroupDescriptor targetRegisterGroupDescriptorFromRegisterGroup( - const RegisterGroup& registerGroup, - const Module& peripheralModule, - TargetMemoryAddress baseAddress, - const std::string& addressSpaceKey, - TargetAddressSpaceDescriptorId addressSpaceDescriptorId, const std::string& key, const std::string& name, - const std::optional& description + const std::string& addressSpaceKey, + const std::optional& description, + const RegisterGroup& registerGroup, + const Module& peripheralModule, + TargetMemoryAddress baseAddress ); static TargetRegisterDescriptor targetRegisterDescriptorFromRegister( const Register& reg, const std::string& addressSpaceKey, - TargetAddressSpaceDescriptorId addressSpaceDescriptorId, TargetMemoryAddress baseAddress ); + + static TargetBitFieldDescriptor targetBitFieldDescriptorFromBitField(const BitField& bitField); + + static TargetPinoutDescriptor targetPinoutDescriptorFromPinout(const Pinout& pinout); + static TargetPinDescriptor targetPinDescriptorFromPin(const Pin& pin); + + static TargetVariantDescriptor targetVariantDescriptorFromVariant(const Variant& variant); }; } diff --git a/src/Targets/TargetDescriptor.cpp b/src/Targets/TargetDescriptor.cpp index f9cd18f9..119e9cfa 100644 --- a/src/Targets/TargetDescriptor.cpp +++ b/src/Targets/TargetDescriptor.cpp @@ -1,5 +1,7 @@ #include "TargetDescriptor.hpp" +#include + #include "src/Exceptions/InternalFatalErrorException.hpp" namespace Targets @@ -9,18 +11,20 @@ namespace Targets TargetFamily family, const std::string& marketId, const std::string& vendorName, - const std::map& addressSpaceDescriptorsByKey, - const std::map& peripheralDescriptorsByKey, - const std::vector& variants, + std::map&& addressSpaceDescriptorsByKey, + std::map&& peripheralDescriptorsByKey, + std::map&& pinoutDescriptorsByKey, + std::vector&& variantDescriptors, const BreakpointResources& breakpointResources ) : name(name) , family(family) , marketId(marketId) , vendorName(vendorName) - , addressSpaceDescriptorsByKey(addressSpaceDescriptorsByKey) - , peripheralDescriptorsByKey(peripheralDescriptorsByKey) - , variants(variants) + , addressSpaceDescriptorsByKey(std::move(addressSpaceDescriptorsByKey)) + , peripheralDescriptorsByKey(std::move(peripheralDescriptorsByKey)) + , pinoutDescriptorsByKey(std::move(pinoutDescriptorsByKey)) + , variantDescriptors(std::move(variantDescriptors)) , breakpointResources(breakpointResources) {} @@ -39,10 +43,74 @@ namespace Targets const TargetAddressSpaceDescriptor& TargetDescriptor::getAddressSpaceDescriptor(const std::string& key) const { const auto descriptor = this->tryGetAddressSpaceDescriptor(key); if (!descriptor.has_value()) { - throw Exceptions::InternalFatalErrorException( - "Failed to get address space descriptor \"" + std::string(key) + throw Exceptions::InternalFatalErrorException{ + "Failed to get address space descriptor \"" + std::string{key} + "\" from target descriptor - descriptor not found" - ); + }; + } + + return descriptor->get(); + } + + const TargetAddressSpaceDescriptor& TargetDescriptor::getFirstAddressSpaceDescriptorContainingMemorySegment( + const std::string& memorySegmentKey + ) const { + for (const auto& [key, addressSpaceDescriptor] : this->addressSpaceDescriptorsByKey) { + const auto segmentDescriptor = addressSpaceDescriptor.tryGetMemorySegmentDescriptor(memorySegmentKey); + if (segmentDescriptor.has_value()) { + return addressSpaceDescriptor; + } + } + + throw Exceptions::InternalFatalErrorException{ + "Failed to get address space descriptor from target descriptor - descriptor containing memory segment \"" + + memorySegmentKey + "\" not found" + }; + } + + std::optional< + std::reference_wrapper + > TargetDescriptor::tryGetPeripheralDescriptor(const std::string& key) const { + const auto descriptorIt = this->peripheralDescriptorsByKey.find(key); + + if (descriptorIt == this->peripheralDescriptorsByKey.end()) { + return std::nullopt; + } + + return std::cref(descriptorIt->second); + } + + const TargetPeripheralDescriptor& TargetDescriptor::getPeripheralDescriptor(const std::string& key) const { + const auto descriptor = this->tryGetPeripheralDescriptor(key); + if (!descriptor.has_value()) { + throw Exceptions::InternalFatalErrorException{ + "Failed to get peripheral descriptor \"" + std::string{key} + + "\" from target descriptor - descriptor not found" + }; + } + + return descriptor->get(); + } + + std::optional< + std::reference_wrapper + > TargetDescriptor::tryGetPinoutDescriptor(const std::string& key) const { + const auto descriptorIt = this->pinoutDescriptorsByKey.find(key); + + if (descriptorIt == this->pinoutDescriptorsByKey.end()) { + return std::nullopt; + } + + return std::cref(descriptorIt->second); + } + + const TargetPinoutDescriptor& TargetDescriptor::getPinoutDescriptor(const std::string& key) const { + const auto descriptor = this->tryGetPinoutDescriptor(key); + if (!descriptor.has_value()) { + throw Exceptions::InternalFatalErrorException{ + "Failed to get pinout descriptor \"" + std::string{key} + + "\" from target descriptor - descriptor not found" + }; } return descriptor->get(); diff --git a/src/Targets/TargetDescriptor.hpp b/src/Targets/TargetDescriptor.hpp index a476db03..eed8fcd6 100644 --- a/src/Targets/TargetDescriptor.hpp +++ b/src/Targets/TargetDescriptor.hpp @@ -11,7 +11,8 @@ #include "TargetMemory.hpp" #include "TargetAddressSpaceDescriptor.hpp" #include "TargetPeripheralDescriptor.hpp" -#include "TargetVariant.hpp" +#include "TargetPinoutDescriptor.hpp" +#include "TargetVariantDescriptor.hpp" #include "TargetBreakpoint.hpp" namespace Targets @@ -24,26 +25,56 @@ namespace Targets std::string vendorName; std::map addressSpaceDescriptorsByKey; std::map peripheralDescriptorsByKey; - std::vector variants; + std::map pinoutDescriptorsByKey; + std::vector variantDescriptors; BreakpointResources breakpointResources; TargetDescriptor( const std::string& name, TargetFamily family, + const std::string& marketId, const std::string& vendorName, - const std::string& marketName, - const std::map& addressSpaceDescriptorsByKey, - const std::map& peripheralDescriptorsByKey, - const std::vector& variants, + std::map&& addressSpaceDescriptorsByKey, + std::map&& peripheralDescriptorsByKey, + std::map&& pinoutDescriptorsByKey, + std::vector&& variantDescriptors, const BreakpointResources& breakpointResources ); + TargetDescriptor(const TargetDescriptor& other) = delete; + TargetDescriptor& operator = (const TargetDescriptor& other) = delete; + + TargetDescriptor(TargetDescriptor&& other) noexcept = default; + TargetDescriptor& operator = (TargetDescriptor&& other) = default; + std::optional> tryGetAddressSpaceDescriptor( const std::string& key ) const; const TargetAddressSpaceDescriptor& getAddressSpaceDescriptor(const std::string& key) const; + + /** + * Returns the descriptor for the first address space that contains the given memory segment. + * + * @param memorySegmentKey + * Key of the memory segment that should be contained within the address space. + * + * @return + */ + const TargetAddressSpaceDescriptor& getFirstAddressSpaceDescriptorContainingMemorySegment( + const std::string& memorySegmentKey + ) const; + + std::optional> tryGetPeripheralDescriptor( + const std::string& key + ) const; + + const TargetPeripheralDescriptor& getPeripheralDescriptor(const std::string& key) const; + + std::optional> tryGetPinoutDescriptor( + const std::string& key + ) const; + + const TargetPinoutDescriptor& getPinoutDescriptor(const std::string& key) const; }; } - -Q_DECLARE_METATYPE(Targets::TargetDescriptor) diff --git a/src/Targets/TargetGpioPinState.hpp b/src/Targets/TargetGpioPinState.hpp new file mode 100644 index 00000000..9590f342 --- /dev/null +++ b/src/Targets/TargetGpioPinState.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +#include "TargetPinDescriptor.hpp" + +namespace Targets +{ + struct TargetGpioPinState + { + enum class State: std::uint8_t + { + HIGH, + LOW, + }; + + enum class DataDirection: std::uint8_t + { + INPUT, + OUTPUT, + }; + + State value; + DataDirection direction; + + TargetGpioPinState( + State value, + DataDirection direction + ) + : value(value) + , direction(direction) + {} + }; + + using TargetGpioPinDescriptorAndStatePair = Pair; + using TargetGpioPinDescriptorAndStatePairs = std::vector; +} + +Q_DECLARE_METATYPE(Targets::TargetGpioPinState) diff --git a/src/Targets/TargetMemory.hpp b/src/Targets/TargetMemory.hpp index f49f6e4d..c5a2c53b 100644 --- a/src/Targets/TargetMemory.hpp +++ b/src/Targets/TargetMemory.hpp @@ -1,9 +1,11 @@ #pragma once #include +#include #include #include #include +#include namespace Targets { @@ -12,21 +14,15 @@ namespace Targets using TargetStackPointer = TargetMemoryAddress; using TargetMemoryBuffer = std::vector; + using TargetAddressSpaceId = std::size_t; + using TargetMemorySegmentId = std::size_t; + enum class TargetMemoryEndianness: std::uint8_t { BIG, LITTLE, }; - enum class TargetMemoryType: std::uint8_t - { - FLASH, - RAM, - EEPROM, - FUSES, - OTHER, - }; - struct TargetMemoryAddressRange { TargetMemoryAddress startAddress = 0; @@ -36,7 +32,9 @@ namespace Targets TargetMemoryAddressRange(TargetMemoryAddress startAddress, TargetMemoryAddress endAddress) : startAddress(startAddress) , endAddress(endAddress) - {} + { + assert(this->startAddress <= this->endAddress); + } bool operator == (const TargetMemoryAddressRange& rhs) const { return this->startAddress == rhs.startAddress && this->endAddress == rhs.endAddress; @@ -46,27 +44,67 @@ namespace Targets return this->startAddress < rhs.startAddress; } - TargetMemorySize size() const { + /** + * Returns the number of addresses in the range. + * + * Keep in mind that the number of addresses may not be equal to the number of bytes in the range. It depends + * on whether the address space is byte-addressable. See TargetAddressSpaceDescriptor::unitSize for more. + * + * @return + */ + [[nodiscard]] TargetMemorySize size() const { return this->endAddress - this->startAddress + 1; } - [[nodiscard]] bool intersectsWith(const TargetMemoryAddressRange& other) const { - return - (other.startAddress <= this->startAddress && other.endAddress >= this->startAddress) - || (other.startAddress >= this->startAddress && other.startAddress <= this->endAddress) - ; + /** + * Checks if this range intersects with the given range. + * + * @param other + * @return + */ + [[nodiscard]] bool intersectsWith(const TargetMemoryAddressRange& other) const noexcept { + return this->startAddress <= other.endAddress && other.startAddress <= this->endAddress; } - [[nodiscard]] bool contains(TargetMemoryAddress address) const { + /** + * Returns the number of addresses in this range that intersect with the given range. + * + * @param other + * @return + */ + [[nodiscard]] TargetMemorySize intersectingSize(const TargetMemoryAddressRange& other) const noexcept { + return this->intersectsWith(other) + ? std::min(this->endAddress, other.endAddress) - std::max(this->startAddress, other.startAddress) + 1 + : 0; + } + + /** + * Checks if the given address is contained within this range. + * + * @param address + * @return + */ + [[nodiscard]] bool contains(TargetMemoryAddress address) const noexcept { return address >= this->startAddress && address <= this->endAddress; } - [[nodiscard]] bool contains(const TargetMemoryAddressRange& addressRange) const { + /** + * Checks if the given range is completely contained within this range. + * + * @param addressRange + * @return + */ + [[nodiscard]] bool contains(const TargetMemoryAddressRange& addressRange) const noexcept { return this->startAddress <= addressRange.startAddress && this->endAddress >= addressRange.endAddress; } - std::set addresses() const { - auto addresses = std::set(); + /** + * Returns a set of all addresses within this range. + * + * @return + */ + std::set addresses() const noexcept { + auto addresses = std::set{}; auto addressesIt = addresses.end(); for (auto i = this->startAddress; i <= this->endAddress; ++i) { @@ -81,44 +119,13 @@ namespace Targets { bool readable = false; bool writeable = false; - bool writeableDuringDebugSession = false; TargetMemoryAccess( bool readable, - bool writeable, - bool writeableDuringDebugSession + bool writeable ) : readable(readable) , writeable(writeable) - , writeableDuringDebugSession(writeableDuringDebugSession) {} }; - - struct TargetMemoryDescriptor - { - TargetMemoryType type; - TargetMemoryAddressRange addressRange; - TargetMemoryAccess access; - std::optional pageSize; - - TargetMemoryDescriptor( - TargetMemoryType type, - TargetMemoryAddressRange addressRange, - TargetMemoryAccess access, - std::optional pageSize = std::nullopt - ) - : type(type) - , addressRange(addressRange) - , access(access) - , pageSize(pageSize) - {} - - bool operator == (const TargetMemoryDescriptor& rhs) const { - return this->type == rhs.type && this->addressRange == rhs.addressRange; - } - - [[nodiscard]] TargetMemorySize size() const { - return (this->addressRange.endAddress - this->addressRange.startAddress) + 1; - } - }; } diff --git a/src/Targets/TargetMemoryCache.cpp b/src/Targets/TargetMemoryCache.cpp index 3b059ea0..399e5d11 100644 --- a/src/Targets/TargetMemoryCache.cpp +++ b/src/Targets/TargetMemoryCache.cpp @@ -6,22 +6,22 @@ namespace Targets { - TargetMemoryCache::TargetMemoryCache(const TargetMemoryDescriptor& memoryDescriptor) - : memoryDescriptor(memoryDescriptor) - , data(TargetMemoryBuffer(memoryDescriptor.size(), 0x00)) + TargetMemoryCache::TargetMemoryCache(const TargetAddressSpaceDescriptor& addressSpaceDescriptor) + : addressSpaceDescriptor(addressSpaceDescriptor) + , data(TargetMemoryBuffer(addressSpaceDescriptor.size(), 0x00)) {} TargetMemoryBuffer TargetMemoryCache::fetch(TargetMemoryAddress startAddress, TargetMemorySize bytes) const { - const auto startIndex = startAddress - this->memoryDescriptor.addressRange.startAddress; + const auto startIndex = startAddress - this->addressSpaceDescriptor.addressRange.startAddress; if ( - startAddress < this->memoryDescriptor.addressRange.startAddress + startAddress < this->addressSpaceDescriptor.addressRange.startAddress || (startIndex + bytes) > this->data.size() ) { - throw Exceptions::Exception("Invalid cache access"); + throw Exceptions::Exception{"Invalid cache access"}; } - return TargetMemoryBuffer(this->data.begin() + startIndex, this->data.begin() + startIndex + bytes); + return TargetMemoryBuffer{this->data.begin() + startIndex, this->data.begin() + startIndex + bytes}; } bool TargetMemoryCache::contains(TargetMemoryAddress startAddress, TargetMemorySize bytes) const { @@ -34,7 +34,7 @@ namespace Targets } void TargetMemoryCache::insert(TargetMemoryAddress startAddress, const TargetMemoryBuffer& data) { - const auto startIndex = startAddress - this->memoryDescriptor.addressRange.startAddress; + const auto startIndex = startAddress - this->addressSpaceDescriptor.addressRange.startAddress; std::copy(data.begin(), data.end(), this->data.begin() + startIndex); @@ -47,7 +47,7 @@ namespace Targets intersectingStartSegmentIt == this->populatedSegments.end() && intersectingEndSegmentIt == this->populatedSegments.end() ) { - this->populatedSegments.insert(std::pair(startAddress, endAddress)); + this->populatedSegments.emplace(startAddress, endAddress); return; } @@ -56,8 +56,8 @@ namespace Targets return; } - auto newStartSegment = std::optionalpopulatedSegments)::value_type>(); - auto newEndSegment = std::optionalpopulatedSegments)::value_type>(); + auto newStartSegment = std::optionalpopulatedSegments)::value_type>{}; + auto newEndSegment = std::optionalpopulatedSegments)::value_type>{}; if (intersectingStartSegmentIt != this->populatedSegments.end()) { newStartSegment.emplace(intersectingStartSegmentIt->first, endAddress); diff --git a/src/Targets/TargetMemoryCache.hpp b/src/Targets/TargetMemoryCache.hpp index d97fa898..88bc50ce 100644 --- a/src/Targets/TargetMemoryCache.hpp +++ b/src/Targets/TargetMemoryCache.hpp @@ -3,6 +3,7 @@ #include #include +#include "TargetAddressSpaceDescriptor.hpp" #include "TargetMemory.hpp" namespace Targets @@ -10,7 +11,7 @@ namespace Targets class TargetMemoryCache { public: - TargetMemoryCache(const TargetMemoryDescriptor& memoryDescriptor); + TargetMemoryCache(const TargetAddressSpaceDescriptor& addressSpaceDescriptor); /** * Fetches data from the cache. @@ -46,7 +47,7 @@ namespace Targets void clear(); private: - const TargetMemoryDescriptor& memoryDescriptor; + const TargetAddressSpaceDescriptor& addressSpaceDescriptor; TargetMemoryBuffer data; /** diff --git a/src/Targets/TargetMemorySegmentDescriptor.cpp b/src/Targets/TargetMemorySegmentDescriptor.cpp new file mode 100644 index 00000000..45f6dd35 --- /dev/null +++ b/src/Targets/TargetMemorySegmentDescriptor.cpp @@ -0,0 +1,46 @@ +#include "TargetMemorySegmentDescriptor.hpp" + +#include "src/Services/StringService.hpp" + +namespace Targets +{ + TargetMemorySegmentDescriptor::TargetMemorySegmentDescriptor( + const std::string& addressSpaceKey, + const std::string& key, + const std::string& name, + TargetMemorySegmentType type, + const TargetMemoryAddressRange& addressRange, + std::uint8_t addressSpaceUnitSize, + bool executable, + const TargetMemoryAccess& debugModeAccess, + const TargetMemoryAccess& programmingModeAccess, + std::optional pageSize + ) + : id(static_cast(Services::StringService::hash(addressSpaceKey + key))) + , key(key) + , name(name) + , type(type) + , addressRange(addressRange) + , addressSpaceUnitSize(addressSpaceUnitSize) + , executable(executable) + , debugModeAccess(debugModeAccess) + , programmingModeAccess(programmingModeAccess) + , pageSize(pageSize) + {} + + bool TargetMemorySegmentDescriptor::operator == (const TargetMemorySegmentDescriptor& other) const { + return this->id == other.id; + } + + bool TargetMemorySegmentDescriptor::operator != (const TargetMemorySegmentDescriptor& other) const { + return !(*this == other); + } + + TargetMemorySize TargetMemorySegmentDescriptor::size() const { + return this->addressRange.size() * this->addressSpaceUnitSize; + } + + TargetMemorySegmentDescriptor TargetMemorySegmentDescriptor::clone() const { + return {*this}; + } +} diff --git a/src/Targets/TargetMemorySegmentDescriptor.hpp b/src/Targets/TargetMemorySegmentDescriptor.hpp index 31428077..5b741ccb 100644 --- a/src/Targets/TargetMemorySegmentDescriptor.hpp +++ b/src/Targets/TargetMemorySegmentDescriptor.hpp @@ -1,7 +1,9 @@ #pragma once #include +#include #include +#include #include "TargetMemory.hpp" #include "TargetMemorySegmentType.hpp" @@ -11,34 +13,48 @@ namespace Targets struct TargetMemorySegmentDescriptor { public: - std::string key; + /* + * The ID of a memory segment is just a hash of the address space key and the memory segment key, concatenated, + * which will be unique. + * + * We use the ID for cheap equality checks. + */ + const TargetMemorySegmentId id; + const std::string key; std::string name; TargetMemorySegmentType type; TargetMemoryAddressRange addressRange; + std::uint8_t addressSpaceUnitSize; + bool executable; TargetMemoryAccess debugModeAccess; TargetMemoryAccess programmingModeAccess; std::optional pageSize; TargetMemorySegmentDescriptor( + const std::string& addressSpaceKey, const std::string& key, const std::string& name, TargetMemorySegmentType type, const TargetMemoryAddressRange& addressRange, + std::uint8_t addressSpaceUnitSize, + bool executable, const TargetMemoryAccess& debugModeAccess, const TargetMemoryAccess& programmingModeAccess, std::optional pageSize - ) - : key(key) - , name(name) - , type(type) - , addressRange(addressRange) - , debugModeAccess(debugModeAccess) - , programmingModeAccess(programmingModeAccess) - , pageSize(pageSize) - {}; + ); - TargetMemorySize size() const { - return this->addressRange.size(); - } + TargetMemorySegmentDescriptor& operator = (const TargetMemorySegmentDescriptor& other) = delete; + + TargetMemorySegmentDescriptor(TargetMemorySegmentDescriptor&& other) noexcept = default; + + bool operator == (const TargetMemorySegmentDescriptor& other) const; + bool operator != (const TargetMemorySegmentDescriptor& other) const; + + TargetMemorySize size() const; + + [[nodiscard]] TargetMemorySegmentDescriptor clone() const; + + private: + TargetMemorySegmentDescriptor(const TargetMemorySegmentDescriptor& other) = default; }; } diff --git a/src/Targets/TargetMemorySegmentType.hpp b/src/Targets/TargetMemorySegmentType.hpp index a392c5b9..f620a7fc 100644 --- a/src/Targets/TargetMemorySegmentType.hpp +++ b/src/Targets/TargetMemorySegmentType.hpp @@ -6,6 +6,7 @@ namespace Targets { enum class TargetMemorySegmentType: std::uint8_t { + GENERAL_PURPOSE_REGISTERS, ALIASED, REGISTERS, EEPROM, diff --git a/src/Targets/TargetPeripheralDescriptor.cpp b/src/Targets/TargetPeripheralDescriptor.cpp index e8019638..9230a341 100644 --- a/src/Targets/TargetPeripheralDescriptor.cpp +++ b/src/Targets/TargetPeripheralDescriptor.cpp @@ -1,6 +1,7 @@ #include "TargetPeripheralDescriptor.hpp" #include +#include #include "src/Services/StringService.hpp" @@ -11,11 +12,13 @@ namespace Targets TargetPeripheralDescriptor::TargetPeripheralDescriptor( const std::string& key, const std::string& name, - const std::map>& registerGroupDescriptorsByKey + std::map>&& registerGroupDescriptorsByKey, + std::vector&& signalDescriptors ) : key(key) , name(name) - , registerGroupDescriptorsByKey(registerGroupDescriptorsByKey) + , registerGroupDescriptorsByKey(std::move(registerGroupDescriptorsByKey)) + , signalDescriptors(std::move(signalDescriptors)) {} std::optional< @@ -27,7 +30,7 @@ namespace Targets return firstGroupIt != this->registerGroupDescriptorsByKey.end() ? keys.size() > 1 ? firstGroupIt->second.tryGetSubgroupDescriptor(keys | std::ranges::views::drop(1)) - : std::optional(std::cref(firstGroupIt->second)) + : std::optional{std::cref(firstGroupIt->second)} : std::nullopt; } @@ -36,12 +39,38 @@ namespace Targets ) const { const auto descriptor = this->tryGetRegisterGroupDescriptor(key); if (!descriptor.has_value()) { - throw Exceptions::InternalFatalErrorException( - "Failed to get register group descriptor \"" + std::string(key) + throw Exceptions::InternalFatalErrorException{ + "Failed to get register group descriptor \"" + std::string{key} + "\" from peripheral \"" + this->key + "\" - register group descriptor not found" - ); + }; } return descriptor->get(); } + + const TargetRegisterDescriptor& TargetPeripheralDescriptor::getRegisterDescriptor( + std::string_view groupKey, + const std::string& registerKey + ) const { + return this->getRegisterGroupDescriptor(groupKey).getRegisterDescriptor(registerKey); + } + + TargetPeripheralDescriptor TargetPeripheralDescriptor::clone() const { + auto output = TargetPeripheralDescriptor{ + this->key, + this->name, + {}, + {} + }; + + for (const auto& [key, descriptor] : this->registerGroupDescriptorsByKey) { + output.registerGroupDescriptorsByKey.emplace(key, descriptor.clone()); + } + + for (const auto& descriptor : this->signalDescriptors) { + output.signalDescriptors.emplace_back(descriptor.clone()); + } + + return output; + } } diff --git a/src/Targets/TargetPeripheralDescriptor.hpp b/src/Targets/TargetPeripheralDescriptor.hpp index cd7f00f0..ba1ab0fa 100644 --- a/src/Targets/TargetPeripheralDescriptor.hpp +++ b/src/Targets/TargetPeripheralDescriptor.hpp @@ -2,11 +2,14 @@ #include #include +#include #include #include #include #include "TargetRegisterGroupDescriptor.hpp" +#include "TargetRegisterDescriptor.hpp" +#include "TargetPeripheralSignalDescriptor.hpp" namespace Targets { @@ -16,17 +19,32 @@ namespace Targets std::string key; std::string name; std::map> registerGroupDescriptorsByKey; + std::vector signalDescriptors; TargetPeripheralDescriptor( const std::string& key, const std::string& name, - const std::map>& registerGroupDescriptorsByKey + std::map>&& registerGroupDescriptorsByKey, + std::vector&& signalDescriptors ); - std::optional> tryGetRegisterGroupDescriptor( - std::string_view keyStr + TargetPeripheralDescriptor(const TargetPeripheralDescriptor& other) = delete; + TargetPeripheralDescriptor& operator = (const TargetPeripheralDescriptor& other) = delete; + + TargetPeripheralDescriptor(TargetPeripheralDescriptor&& other) noexcept = default; + TargetPeripheralDescriptor& operator = (TargetPeripheralDescriptor&& other) = default; + + [[nodiscard]] std::optional< + std::reference_wrapper + > tryGetRegisterGroupDescriptor(std::string_view keyStr) const; + + [[nodiscard]] const TargetRegisterGroupDescriptor& getRegisterGroupDescriptor(std::string_view key) const; + + [[nodiscard]] const TargetRegisterDescriptor& getRegisterDescriptor( + std::string_view groupKey, + const std::string& registerKey ) const; - const TargetRegisterGroupDescriptor& getRegisterGroupDescriptor(std::string_view key) const; + [[nodiscard]] TargetPeripheralDescriptor clone() const; }; } diff --git a/src/Targets/TargetPeripheralSignalDescriptor.cpp b/src/Targets/TargetPeripheralSignalDescriptor.cpp new file mode 100644 index 00000000..0d7d0246 --- /dev/null +++ b/src/Targets/TargetPeripheralSignalDescriptor.cpp @@ -0,0 +1,19 @@ +#include "TargetPeripheralSignalDescriptor.hpp" + +namespace Targets +{ + TargetPeripheralSignalDescriptor::TargetPeripheralSignalDescriptor( + const std::string& padName, + const std::optional& index + ) + : padName(padName) + , index(index) + {} + + TargetPeripheralSignalDescriptor TargetPeripheralSignalDescriptor::clone() const { + return { + this->padName, + this->index + }; + } +} diff --git a/src/Targets/TargetPeripheralSignalDescriptor.hpp b/src/Targets/TargetPeripheralSignalDescriptor.hpp new file mode 100644 index 00000000..40c994c8 --- /dev/null +++ b/src/Targets/TargetPeripheralSignalDescriptor.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include + +namespace Targets +{ + struct TargetPeripheralSignalDescriptor + { + public: + std::string padName; + std::optional index; + + TargetPeripheralSignalDescriptor( + const std::string& padName, + const std::optional& index + ); + + TargetPeripheralSignalDescriptor(const TargetPeripheralSignalDescriptor& other) = delete; + TargetPeripheralSignalDescriptor& operator = (const TargetPeripheralSignalDescriptor& other) = delete; + + TargetPeripheralSignalDescriptor(TargetPeripheralSignalDescriptor&& other) noexcept = default; + TargetPeripheralSignalDescriptor& operator = (TargetPeripheralSignalDescriptor&& other) = default; + + [[nodiscard]] TargetPeripheralSignalDescriptor clone() const; + }; +} diff --git a/src/Targets/TargetPhysicalInterface.cpp b/src/Targets/TargetPhysicalInterface.cpp index 013054f2..46b26684 100644 --- a/src/Targets/TargetPhysicalInterface.cpp +++ b/src/Targets/TargetPhysicalInterface.cpp @@ -3,12 +3,12 @@ namespace Targets { std::map getPhysicalInterfaceNames() { - return std::map({ + return std::map{ {TargetPhysicalInterface::ISP, "ISP"}, - {TargetPhysicalInterface::DEBUG_WIRE, "debugWire"}, + {TargetPhysicalInterface::DEBUG_WIRE, "debugWIRE"}, {TargetPhysicalInterface::PDI, "PDI"}, {TargetPhysicalInterface::JTAG, "JTAG"}, {TargetPhysicalInterface::UPDI, "UPDI"}, - }); + }; } } diff --git a/src/Targets/TargetPinDescriptor.cpp b/src/Targets/TargetPinDescriptor.cpp new file mode 100644 index 00000000..4922c476 --- /dev/null +++ b/src/Targets/TargetPinDescriptor.cpp @@ -0,0 +1,14 @@ +#include "TargetPinDescriptor.hpp" + +namespace Targets +{ + TargetPinDescriptor::TargetPinDescriptor( + const std::string& padName, + const std::string& position, + TargetPinType type + ) + : padName(padName) + , position(position) + , type(type) + {} +} diff --git a/src/Targets/TargetPinDescriptor.hpp b/src/Targets/TargetPinDescriptor.hpp index e20ee240..ae8e9495 100644 --- a/src/Targets/TargetPinDescriptor.hpp +++ b/src/Targets/TargetPinDescriptor.hpp @@ -1,65 +1,35 @@ #pragma once -#include #include -#include -#include -#include -#include +#include namespace Targets { enum class TargetPinType: std::uint8_t { - UNKNOWN, GPIO, GND, VCC, + OTHER, }; struct TargetPinDescriptor { - int number; - int variantId; - bool supportsGpio = false; - std::string name; + public: std::string padName; - std::vector functions; + std::string position; + TargetPinType type = TargetPinType::OTHER; - TargetPinType type = TargetPinType::UNKNOWN; + TargetPinDescriptor( + const std::string& padName, + const std::string& position, + TargetPinType type + ); - bool operator == (const TargetPinDescriptor& pinDescriptor) const { - return this->number == pinDescriptor.number - && this->supportsGpio == pinDescriptor.supportsGpio - && this->name == pinDescriptor.name - && this->padName == pinDescriptor.padName - && this->functions == pinDescriptor.functions - && this->type == pinDescriptor.type - ; - } + TargetPinDescriptor(const TargetPinDescriptor& other) = delete; + TargetPinDescriptor& operator = (const TargetPinDescriptor& other) = delete; + + TargetPinDescriptor(TargetPinDescriptor&& other) noexcept = default; + TargetPinDescriptor& operator = (TargetPinDescriptor&& other) = default; }; - - struct TargetPinState - { - enum class IoState: std::uint8_t - { - HIGH, - LOW, - }; - - enum class IoDirection: std::uint8_t - { - INPUT, - OUTPUT, - }; - - std::optional ioState; - std::optional ioDirection; - }; - - using TargetPinStateMapping = std::map; } - -Q_DECLARE_METATYPE(Targets::TargetPinDescriptor) -Q_DECLARE_METATYPE(Targets::TargetPinState) -Q_DECLARE_METATYPE(Targets::TargetPinStateMapping) diff --git a/src/Targets/TargetPinoutDescriptor.cpp b/src/Targets/TargetPinoutDescriptor.cpp new file mode 100644 index 00000000..9deb80ef --- /dev/null +++ b/src/Targets/TargetPinoutDescriptor.cpp @@ -0,0 +1,22 @@ +#include "TargetPinoutDescriptor.hpp" + +#include + +namespace Targets +{ + TargetPinoutDescriptor::TargetPinoutDescriptor( + const std::string& key, + const std::string& name, + TargetPinoutType type, + std::vector&& pinDescriptors + ) + : key(key) + , name(name) + , type(type) + , pinDescriptors(std::move(pinDescriptors)) + {} + + bool TargetPinoutDescriptor::operator == (const TargetPinoutDescriptor& other) const { + return this->key == other.key; + } +} diff --git a/src/Targets/TargetPinoutDescriptor.hpp b/src/Targets/TargetPinoutDescriptor.hpp index fb4dd424..df44a8a1 100644 --- a/src/Targets/TargetPinoutDescriptor.hpp +++ b/src/Targets/TargetPinoutDescriptor.hpp @@ -1,10 +1,14 @@ #pragma once #include +#include +#include + +#include "TargetPinDescriptor.hpp" namespace Targets { - enum class PinoutType: std::uint8_t + enum class TargetPinoutType: std::uint8_t { SOIC, SSOP, @@ -15,4 +19,28 @@ namespace Targets MLF, BGA, }; + + struct TargetPinoutDescriptor + { + public: + std::string key; + std::string name; + TargetPinoutType type; + std::vector pinDescriptors; + + TargetPinoutDescriptor( + const std::string& key, + const std::string& name, + TargetPinoutType type, + std::vector&& pinDescriptors + ); + + TargetPinoutDescriptor(const TargetPinoutDescriptor& other) = delete; + TargetPinoutDescriptor& operator = (const TargetPinoutDescriptor& other) = delete; + + TargetPinoutDescriptor(TargetPinoutDescriptor&& other) noexcept = default; + TargetPinoutDescriptor& operator = (TargetPinoutDescriptor&& other) = default; + + bool operator == (const TargetPinoutDescriptor& other) const; + }; } diff --git a/src/Targets/TargetRegister.hpp b/src/Targets/TargetRegister.hpp deleted file mode 100644 index 0d9d7028..00000000 --- a/src/Targets/TargetRegister.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "TargetRegisterDescriptor.hpp" -#include "TargetMemory.hpp" - -namespace Targets -{ - struct TargetRegister - { - TargetRegisterDescriptorId descriptorId; - - /** - * Register values should be in MSB form - */ - TargetMemoryBuffer value; - - TargetRegister(TargetRegisterDescriptorId descriptorId, TargetMemoryBuffer value) - : descriptorId(descriptorId) - , value(std::move(value)) - {}; - - [[nodiscard]] std::size_t size() const { - return this->value.size(); - } - }; - - using TargetRegisters = std::vector; -} diff --git a/src/Targets/TargetRegisterDescriptor.cpp b/src/Targets/TargetRegisterDescriptor.cpp index 19b18844..c6914afd 100644 --- a/src/Targets/TargetRegisterDescriptor.cpp +++ b/src/Targets/TargetRegisterDescriptor.cpp @@ -1,6 +1,99 @@ #include "TargetRegisterDescriptor.hpp" +#include "TargetAddressSpaceDescriptor.hpp" +#include "src/Exceptions/InternalFatalErrorException.hpp" + namespace Targets { + TargetRegisterDescriptor::TargetRegisterDescriptor( + const std::string& key, + const std::string& name, + const std::string& addressSpaceKey, + TargetMemoryAddress startAddress, + TargetMemorySize size, + TargetRegisterType type, + TargetRegisterAccess access, + std::optional description, + std::map&& bitFieldDescriptorsByKey + ) + : key(key) + , name(name) + , addressSpaceId(TargetAddressSpaceDescriptor::generateId(addressSpaceKey)) + , addressSpaceKey(addressSpaceKey) + , startAddress(startAddress) + , size(size) + , type(type) + , access(access) + , description(description) + , bitFieldDescriptorsByKey(std::move(bitFieldDescriptorsByKey)) + {} + bool TargetRegisterDescriptor::operator == (const TargetRegisterDescriptor& other) const { + return this->key == other.key + && this->name == other.name + && this->addressSpaceKey == other.addressSpaceKey + && this->startAddress == other.startAddress + && this->size == other.size + && this->type == other.type + && this->access == other.access + && this->description == other.description + ; + } + + bool TargetRegisterDescriptor::operator < (const TargetRegisterDescriptor& other) const { + if (this->addressSpaceKey != other.addressSpaceKey) { + /* + * If the registers are within different address spaces, there is no meaningful way to sort them, so + * we just sort by name. + */ + return this->name < other.name; + } + + return this->startAddress < other.startAddress; + } + + [[nodiscard]] std::optional< + std::reference_wrapper + > TargetRegisterDescriptor::tryGetBitFieldDescriptor(const std::string& key) const { + const auto bitFieldIt = this->bitFieldDescriptorsByKey.find(key); + + if (bitFieldIt == this->bitFieldDescriptorsByKey.end()) { + return std::nullopt; + } + + return std::cref(bitFieldIt->second); + } + + [[nodiscard]] const TargetBitFieldDescriptor& TargetRegisterDescriptor::getBitFieldDescriptor( + const std::string& key + ) const { + const auto bitField = this->tryGetBitFieldDescriptor(key); + if (!bitField.has_value()) { + throw Exceptions::InternalFatalErrorException{ + "Failed to get bit field descriptor \"" + std::string{key} + "\" from register - bit field not found" + }; + } + + return bitField->get(); + } + + TargetRegisterDescriptor TargetRegisterDescriptor::clone() const { + auto output = TargetRegisterDescriptor{ + this->key, + this->name, + this->addressSpaceKey, + this->startAddress, + this->size, + this->type, + this->access, + this->description, + {} + }; + + for (const auto& [key, descriptor] : this->bitFieldDescriptorsByKey) { + output.bitFieldDescriptorsByKey.emplace(key, descriptor.clone()); + } + + return output; + } } diff --git a/src/Targets/TargetRegisterDescriptor.hpp b/src/Targets/TargetRegisterDescriptor.hpp index dc216bd9..c8ec1db2 100644 --- a/src/Targets/TargetRegisterDescriptor.hpp +++ b/src/Targets/TargetRegisterDescriptor.hpp @@ -1,21 +1,29 @@ #pragma once #include -#include #include #include -#include #include #include +#include #include "TargetMemory.hpp" #include "TargetAddressSpaceDescriptor.hpp" +#include "TargetBitFieldDescriptor.hpp" + +#include "src/Helpers/Pair.hpp" namespace Targets { using TargetRegisterDescriptorId = std::uint32_t; using TargetRegisterDescriptorIds = std::set; + enum class TargetRegisterType: std::uint8_t + { + GENERAL_PURPOSE_REGISTER, + OTHER, + }; + struct TargetRegisterAccess { bool readable = false; @@ -39,64 +47,47 @@ namespace Targets struct TargetRegisterDescriptor { public: - const TargetRegisterDescriptorId id; std::string key; std::string name; + TargetAddressSpaceId addressSpaceId; std::string addressSpaceKey; - const TargetAddressSpaceDescriptorId addressSpaceDescriptorId; TargetMemoryAddress startAddress; TargetMemorySize size; + TargetRegisterType type; TargetRegisterAccess access; std::optional description; + std::map bitFieldDescriptorsByKey; TargetRegisterDescriptor( const std::string& key, const std::string& name, const std::string& addressSpaceKey, - TargetAddressSpaceDescriptorId addressSpaceDescriptorId, TargetMemoryAddress startAddress, TargetMemorySize size, + TargetRegisterType type, TargetRegisterAccess access, - std::optional description - ) - : id(++(TargetRegisterDescriptor::lastRegisterDescriptorId)) - , key(key) - , name(name) - , addressSpaceKey(addressSpaceKey) - , addressSpaceDescriptorId(addressSpaceDescriptorId) - , startAddress(startAddress) - , size(size) - , access(access) - , description(description) - {}; + std::optional description, + std::map&& bitFieldDescriptorsByKey + ); - bool operator == (const TargetRegisterDescriptor& other) const { - return this->key == other.key - && this->name == other.name - && this->addressSpaceDescriptorId == other.addressSpaceDescriptorId - && this->startAddress == other.startAddress - && this->size == other.size - && this->access == other.access - && this->description == other.description - ; - } + TargetRegisterDescriptor(const TargetRegisterDescriptor& other) = delete; + TargetRegisterDescriptor& operator = (const TargetRegisterDescriptor& other) = delete; - bool operator < (const TargetRegisterDescriptor& other) const { - if (this->addressSpaceDescriptorId != other.addressSpaceDescriptorId) { - /* - * If the registers are within different address spaces, there is no meaningful way to sort them, so - * we just sort by name. - */ - return this->name < other.name; - } + TargetRegisterDescriptor(TargetRegisterDescriptor&& other) noexcept = default; + TargetRegisterDescriptor& operator = (TargetRegisterDescriptor&& other) = default; - return this->startAddress < other.startAddress; - } + bool operator == (const TargetRegisterDescriptor& other) const; + bool operator < (const TargetRegisterDescriptor& other) const; - private: - static inline std::atomic lastRegisterDescriptorId = 0; + [[nodiscard]] std::optional> tryGetBitFieldDescriptor( + const std::string& key + ) const; + [[nodiscard]] const TargetBitFieldDescriptor& getBitFieldDescriptor(const std::string& key) const; + + [[nodiscard]] TargetRegisterDescriptor clone() const; }; - using TargetRegisterDescriptors = std::set; - using TargetRegisterDescriptorMapping = std::map; + using TargetRegisterDescriptors = std::vector; + using TargetRegisterDescriptorAndValuePair = Pair; + using TargetRegisterDescriptorAndValuePairs = std::vector; } diff --git a/src/Targets/TargetRegisterGroupDescriptor.cpp b/src/Targets/TargetRegisterGroupDescriptor.cpp index cb8c478f..01ee6ff3 100644 --- a/src/Targets/TargetRegisterGroupDescriptor.cpp +++ b/src/Targets/TargetRegisterGroupDescriptor.cpp @@ -12,24 +12,22 @@ namespace Targets const std::string& key, const std::string& name, const std::string& addressSpaceKey, - TargetAddressSpaceDescriptorId addressSpaceDescriptorId, const std::optional& description, - const std::map& registerDescriptorsByKey, - const std::map>& subgroupDescriptorsByKey + std::map>&& registerDescriptorsByKey, + std::map>&& subgroupDescriptorsByKey ) : key(key) , name(name) , addressSpaceKey(addressSpaceKey) - , addressSpaceDescriptorId(addressSpaceDescriptorId) , description(description) - , registerDescriptorsByKey(registerDescriptorsByKey) - , subgroupDescriptorsByKey(subgroupDescriptorsByKey) + , registerDescriptorsByKey(std::move(registerDescriptorsByKey)) + , subgroupDescriptorsByKey(std::move(subgroupDescriptorsByKey)) {} TargetMemoryAddress TargetRegisterGroupDescriptor::startAddress() const { assert(!this->registerDescriptorsByKey.empty() || !this->subgroupDescriptorsByKey.empty()); - auto startAddress = TargetMemoryAddress{0}; + auto startAddress = TargetMemoryAddress{-1U}; for (const auto& [key, registerDescriptor] : this->registerDescriptorsByKey) { if (registerDescriptor.startAddress < startAddress) { @@ -76,11 +74,87 @@ namespace Targets ) const { const auto subgroup = this->tryGetSubgroupDescriptor(keyStr); if (!subgroup.has_value()) { - throw Exceptions::InternalFatalErrorException( - "Failed to get subgroup \"" + std::string(keyStr) + "\" from register group - subgroup not found" - ); + throw Exceptions::InternalFatalErrorException{ + "Failed to get subgroup \"" + std::string{keyStr} + "\" from register group - subgroup not found" + }; } return subgroup->get(); } + + [[nodiscard]] std::optional< + std::reference_wrapper + > TargetRegisterGroupDescriptor::tryGetRegisterDescriptor(const std::string& key) const { + const auto registerIt = this->registerDescriptorsByKey.find(key); + + if (registerIt == this->registerDescriptorsByKey.end()) { + return std::nullopt; + } + + return std::cref(registerIt->second); + } + + std::optional< + std::reference_wrapper + > TargetRegisterGroupDescriptor::tryGetFirstRegisterDescriptor(std::initializer_list&& keys) const { + for (const auto& key : keys) { + auto descriptor = this->tryGetRegisterDescriptor(key); + if (descriptor.has_value()) { + return descriptor; + } + } + + return std::nullopt; + } + + [[nodiscard]] const TargetRegisterDescriptor& TargetRegisterGroupDescriptor::getRegisterDescriptor( + const std::string& key + ) const { + const auto reg = this->tryGetRegisterDescriptor(key); + if (!reg.has_value()) { + throw Exceptions::InternalFatalErrorException{ + "Failed to get register descriptor \"" + std::string{key} + "\" from register group - register " + "descriptor not found" + }; + } + + return reg->get(); + } + + Pair< + const TargetRegisterDescriptor&, + const TargetBitFieldDescriptor& + > TargetRegisterGroupDescriptor::getRegisterBitFieldDescriptorPair(const std::string& bitFieldKey) const { + for (const auto& [regKey, regDescriptor] : this->registerDescriptorsByKey) { + const auto bitFieldDescriptor = regDescriptor.tryGetBitFieldDescriptor(bitFieldKey); + if (bitFieldDescriptor.has_value()) { + return {regDescriptor, bitFieldDescriptor->get()}; + } + } + + throw Exceptions::InternalFatalErrorException{ + "Failed to find bit field \"" + std::string{bitFieldKey} + "\" within register group" + }; + } + + TargetRegisterGroupDescriptor TargetRegisterGroupDescriptor::clone() const { + auto output = TargetRegisterGroupDescriptor{ + this->key, + this->name, + this->addressSpaceKey, + this->description, + {}, + {} + }; + + for (const auto& [key, descriptor] : this->subgroupDescriptorsByKey) { + output.subgroupDescriptorsByKey.emplace(key, descriptor.clone()); + } + + for (const auto& [key, descriptor] : this->registerDescriptorsByKey) { + output.registerDescriptorsByKey.emplace(key, descriptor.clone()); + } + + return output; + } } diff --git a/src/Targets/TargetRegisterGroupDescriptor.hpp b/src/Targets/TargetRegisterGroupDescriptor.hpp index 786d0ecb..bc37824a 100644 --- a/src/Targets/TargetRegisterGroupDescriptor.hpp +++ b/src/Targets/TargetRegisterGroupDescriptor.hpp @@ -6,12 +6,12 @@ #include #include #include +#include #include #include -#include "TargetMemory.hpp" -#include "TargetAddressSpaceDescriptor.hpp" #include "TargetRegisterDescriptor.hpp" +#include "src/Helpers/Pair.hpp" namespace Targets { @@ -21,21 +21,25 @@ namespace Targets std::string key; std::string name; std::string addressSpaceKey; - const TargetAddressSpaceDescriptorId addressSpaceDescriptorId; std::optional description; - std::map registerDescriptorsByKey; + std::map> registerDescriptorsByKey; std::map> subgroupDescriptorsByKey; TargetRegisterGroupDescriptor( const std::string& key, const std::string& name, const std::string& addressSpaceKey, - TargetAddressSpaceDescriptorId addressSpaceDescriptorId, const std::optional& description, - const std::map& registerDescriptorsByKey, - const std::map>& subgroupDescriptorsByKey + std::map>&& registerDescriptorsByKey, + std::map>&& subgroupDescriptorsByKey ); + TargetRegisterGroupDescriptor(const TargetRegisterGroupDescriptor& other) = delete; + TargetRegisterGroupDescriptor& operator = (const TargetRegisterGroupDescriptor& other) = delete; + + TargetRegisterGroupDescriptor(TargetRegisterGroupDescriptor&& other) noexcept = default; + TargetRegisterGroupDescriptor& operator = (TargetRegisterGroupDescriptor&& other) = default; + /** * Calculates the start address of the register group. * @@ -43,7 +47,7 @@ namespace Targets * * @return */ - TargetMemoryAddress startAddress() const; + [[nodiscard]] TargetMemoryAddress startAddress() const; /** * Calculates the size of this register group. @@ -52,7 +56,7 @@ namespace Targets * * @return */ - TargetMemorySize size() const; + [[nodiscard]] TargetMemorySize size() const; /** * Attempts to fetch a subgroup with a set of keys. @@ -89,13 +93,13 @@ namespace Targets * * @param keyStr * A string in "dot notation", containing each key seperated with a period/dot character (e.g. - * "parent.child.grandchild"). + * "grandchild.parent.child"). * * @return */ - std::optional> tryGetSubgroupDescriptor( - std::string_view keyStr - ) const; + [[nodiscard]] std::optional< + std::reference_wrapper + > tryGetSubgroupDescriptor(std::string_view keyStr) const; /** * Fetches a subgroup with a set of keys in the form of a string in "dot notation". If the subgroup is not @@ -103,10 +107,55 @@ namespace Targets * * @param keyStr * A string in "dot notation", containing each key seperated with a period/dot character (e.g. - * "parent.child.grandchild"). + * "grandchild.parent.child"). * * @return */ - const TargetRegisterGroupDescriptor& getSubgroupDescriptor(std::string_view keyStr) const; + [[nodiscard]] const TargetRegisterGroupDescriptor& getSubgroupDescriptor(std::string_view keyStr) const; + + /** + * Register descriptor lookup by key. + * + * @param key + * + * @return + * The register descriptor matching the given key, if it exists. Otherwise, std::nullopt. + */ + [[nodiscard]] std::optional> tryGetRegisterDescriptor( + const std::string& key + ) const; + + /** + * Performs a lookup of each key in the given set of keys, and returns the first matching register descriptor. + * + * @param keys + * + * @return + * std::nullopt if all keys do not map to any register descriptors. + */ + [[nodiscard]] std::optional< + std::reference_wrapper + > tryGetFirstRegisterDescriptor(std::initializer_list&& keys) const; + + [[nodiscard]] const TargetRegisterDescriptor& getRegisterDescriptor(const std::string& key) const; + + /** + * Finds the first register descriptor containing the bit field with the given key. + * + * This function will throw an InternalFatalErrorException if the bit field was not found in any of the + * registers within this register group. + * + * @param bitFieldKey + * + * @return + * A Pair object with Pair::first being the register descriptor in which the bit field was found, and + * Pair::second being the bit field descriptor. + */ + [[nodiscard]] Pair< + const TargetRegisterDescriptor&, + const TargetBitFieldDescriptor& + > getRegisterBitFieldDescriptorPair(const std::string& bitFieldKey) const; + + [[nodiscard]] TargetRegisterGroupDescriptor clone() const; }; } diff --git a/src/Targets/TargetState.hpp b/src/Targets/TargetState.hpp index 0f6facad..0558d888 100644 --- a/src/Targets/TargetState.hpp +++ b/src/Targets/TargetState.hpp @@ -1,15 +1,77 @@ #pragma once -#include +#include +#include +#include + +#include "TargetMemory.hpp" namespace Targets { - enum class TargetState: std::uint8_t + enum class TargetExecutionState: std::uint8_t { UNKNOWN, STOPPED, RUNNING, + STEPPING, + }; + + enum class TargetMode: std::uint8_t + { + DEBUGGING, + PROGRAMMING, + }; + + static_assert(std::atomic::is_always_lock_free); + static_assert(std::atomic::is_always_lock_free); + static_assert(std::atomic>::is_always_lock_free); + + struct TargetState + { + std::atomic executionState = TargetExecutionState::UNKNOWN; + std::atomic mode = TargetMode::DEBUGGING; + + /** + * Current program counter - only populated when TargetState::executionState == TargetExecutionState::STOPPED + */ + std::atomic> programCounter = {}; + + TargetState() = default; + + TargetState( + TargetExecutionState executionState, + TargetMode mode, + std::optional programCounter = std::nullopt + ) + : executionState(executionState) + , mode(mode) + , programCounter(programCounter) + {} + + TargetState(const TargetState& other) + : executionState(other.executionState.load()) + , mode(other.mode.load()) + , programCounter(other.programCounter.load()) + {} + + TargetState& operator = (const TargetState& other) { + this->executionState = other.executionState.load(); + this->mode = other.mode.load(); + this->programCounter = other.programCounter.load(); + + return *this; + } + + bool operator == (const TargetState& other) const { + return + this->executionState.load() == other.executionState.load() + && this->mode.load() == other.mode.load() + && this->programCounter.load() == other.programCounter.load() + ; + } + + bool operator != (const TargetState& other) const { + return !(*this == other); + } }; } - -Q_DECLARE_METATYPE(Targets::TargetState) diff --git a/src/Targets/TargetVariant.hpp b/src/Targets/TargetVariant.hpp deleted file mode 100644 index 084119e5..00000000 --- a/src/Targets/TargetVariant.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "TargetPinDescriptor.hpp" - -namespace Targets -{ - enum class TargetPackage: std::uint8_t - { - UNKNOWN, - - /** - * Quad flat package (QFP) - */ - QFP, - - /** - * Dual inline package (DIP) - */ - DIP, - - /** - * "Small outline integrated circuit" package (SOIC). - * - * Because of the similarities between SOIC and DIP, Insight treats SOIC packages as DIP packages. That is, - * it uses the same package widget. - */ - SOIC, - - /** - * "Shrink small outline" package (SSOP) - * - * Because of the similarities between this and DIP, Insight treats SSOP packages as DIP packages. That is, - * it uses the same package widget. - */ - SSOP, - - /** - * Quad flat no-lead (QFN) package - * - * Because of the similarities between this and QFP, Insight treats QFN packages as QFP. - */ - QFN, - }; - - struct TargetVariant - { - int id; - std::string name; - std::string packageName; - TargetPackage package = TargetPackage::UNKNOWN; - std::map pinDescriptorsByNumber; - - bool operator == (const TargetVariant& variant) const { - return this->name == variant.name - && this->packageName == variant.packageName - && this->package == variant.package - && this->pinDescriptorsByNumber == variant.pinDescriptorsByNumber; - } - }; -} diff --git a/src/Targets/TargetVariantDescriptor.cpp b/src/Targets/TargetVariantDescriptor.cpp new file mode 100644 index 00000000..ffd08004 --- /dev/null +++ b/src/Targets/TargetVariantDescriptor.cpp @@ -0,0 +1,14 @@ +#include "TargetVariantDescriptor.hpp" + +#include + +namespace Targets +{ + TargetVariantDescriptor::TargetVariantDescriptor( + const std::string& name, + const std::string& pinoutKey + ) + : name(name) + , pinoutKey(pinoutKey) + {} +} diff --git a/src/Targets/TargetVariantDescriptor.hpp b/src/Targets/TargetVariantDescriptor.hpp new file mode 100644 index 00000000..e87b138f --- /dev/null +++ b/src/Targets/TargetVariantDescriptor.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +namespace Targets +{ + struct TargetVariantDescriptor + { + public: + std::string name; + std::string pinoutKey; + + TargetVariantDescriptor( + const std::string& name, + const std::string& pinoutKey + ); + + TargetVariantDescriptor(const TargetVariantDescriptor& other) = delete; + TargetVariantDescriptor& operator = (const TargetVariantDescriptor& other) = delete; + + TargetVariantDescriptor(TargetVariantDescriptor&& other) noexcept = default; + TargetVariantDescriptor& operator = (TargetVariantDescriptor&& other) noexcept = default; + }; +} diff --git a/src/Targets/Targets.hpp b/src/Targets/Targets.hpp index 100c84b3..22d2e792 100644 --- a/src/Targets/Targets.hpp +++ b/src/Targets/Targets.hpp @@ -1,5 +1,5 @@ #pragma once #include "src/Targets/Target.hpp" -#include "src/Targets/Microchip/AVR/AVR8/Avr8.hpp" +#include "src/Targets/Microchip/AVR8/Avr8.hpp" #include "src/Targets/RiscV/RiscV.hpp"