GPIO pad state access and manipulation for WCH RISC-V targets

This commit is contained in:
Nav
2024-12-21 02:10:31 +00:00
parent db05a97215
commit 79b7457c89
9 changed files with 384 additions and 20 deletions

View File

@@ -287,14 +287,6 @@ namespace Targets::RiscV
});
}
TargetGpioPadDescriptorAndStatePairs RiscV::getGpioPadStates(const TargetPadDescriptors& padDescriptors) {
return {};
}
void RiscV::setGpioPadState(const TargetPadDescriptor& padDescriptor, const TargetGpioPadState& state) {
}
void RiscV::enableProgrammingMode() {
this->riscVDebugInterface->enableProgrammingMode();
this->programmingMode = true;

View File

@@ -77,9 +77,6 @@ namespace Targets::RiscV
TargetStackPointer getStackPointer() override;
void setStackPointer(TargetStackPointer stackPointer) override;
TargetGpioPadDescriptorAndStatePairs getGpioPadStates(const TargetPadDescriptors& padDescriptors) override;
void setGpioPadState(const TargetPadDescriptor& padDescriptor, const TargetGpioPadState& state) override;
void enableProgrammingMode() override;
void disableProgrammingMode() override;
bool programmingModeEnabled() override;

View File

@@ -0,0 +1,41 @@
#pragma once
#include <cstdint>
#include "src/Targets/TargetPadDescriptor.hpp"
#include "src/Targets/TargetRegisterDescriptor.hpp"
#include "src/Targets/TargetBitFieldDescriptor.hpp"
namespace Targets::RiscV::Wch
{
enum class GpioPadDirection: std::uint8_t
{
INPUT = 0x00,
OUTPUT = 0x01,
};
enum class GpioPadInputMode: std::uint8_t
{
ANALOG = 0x00,
FLOATING = 0x01,
PULLED = 0x10,
};
enum class GpioPadOutputMode: std::uint8_t
{
GENERAL_PURPOSE = 0x00,
ALTERNATE_FUNCTION = 0x01,
};
struct GpioPadDescriptor
{
const TargetBitFieldDescriptor& peripheralClockEnableBitFieldDescriptor;
const TargetRegisterDescriptor& configRegisterDescriptor;
const TargetBitFieldDescriptor& configBitFieldDescriptor;
const TargetBitFieldDescriptor& modeBitFieldDescriptor;
const TargetRegisterDescriptor& inputDataRegisterDescriptor;
const TargetBitFieldDescriptor& inputDataBitFieldDescriptor;
const TargetRegisterDescriptor& outputDataRegisterDescriptor;
const TargetBitFieldDescriptor& outputDataBitFieldDescriptor;
};
}

View File

@@ -38,6 +38,19 @@ namespace Targets::RiscV::Wch
, flashStatusRegisterDescriptor(this->flashPeripheralDescriptor.getRegisterDescriptor("flash", "statr"))
, flashStatusBootLockFieldDescriptor(this->flashStatusRegisterDescriptor.getBitFieldDescriptor("boot_lock"))
, flashStatusBootModeFieldDescriptor(this->flashStatusRegisterDescriptor.getBitFieldDescriptor("boot_mode"))
, rccPeripheralDescriptor(this->targetDescriptionFile.getTargetPeripheralDescriptor("rcc"))
, portPeripheralClockEnableRegisterDescriptor(
this->rccPeripheralDescriptor.getRegisterDescriptor("rcc", "apb2pcenr")
)
, padDescriptors(this->targetDescriptionFile.targetPadDescriptors())
, gpioPortPeripheralDescriptors(this->targetDescriptionFile.gpioPortPeripheralDescriptors())
, gpioPadDescriptorsByPadId(
WchRiscV::generateGpioPadDescriptorMapping(
this->portPeripheralClockEnableRegisterDescriptor,
this->gpioPortPeripheralDescriptors,
this->padDescriptors
)
)
{
if (
this->targetConfig.programSegmentKey.has_value()
@@ -363,6 +376,112 @@ namespace Targets::RiscV::Wch
return programCounter;
}
TargetGpioPadDescriptorAndStatePairs WchRiscV::getGpioPadStates(const TargetPadDescriptors& padDescriptors) {
auto cachedRegsById = std::unordered_map<TargetRegisterId, DynamicRegisterValue>{};
const auto readGpioReg = [this, &cachedRegsById] (const TargetRegisterDescriptor& descriptor)
-> DynamicRegisterValue& {
auto cachedRegIt = cachedRegsById.find(descriptor.id);
if (cachedRegIt == cachedRegsById.end()) {
cachedRegIt = cachedRegsById.emplace(
descriptor.id,
this->readRegisterDynamicValue(descriptor)
).first;
}
return cachedRegIt->second;
};
auto output = TargetGpioPadDescriptorAndStatePairs{};
for (const auto* padDescriptor : padDescriptors) {
const auto gpioPadDescriptorIt = this->gpioPadDescriptorsByPadId.find(padDescriptor->id);
if (gpioPadDescriptorIt == this->gpioPadDescriptorsByPadId.end()) {
continue;
}
const auto& gpioPadDescriptor = gpioPadDescriptorIt->second;
const auto& portClockEnableRegisterValue = readGpioReg(this->portPeripheralClockEnableRegisterDescriptor);
if (!portClockEnableRegisterValue.bitFieldAs<bool>(gpioPadDescriptor.peripheralClockEnableBitFieldDescriptor)) {
// The port peripheral is currently disabled. We cannot obtain any meaningful state for this pad
continue;
}
const auto& configRegisterValue = readGpioReg(gpioPadDescriptor.configRegisterDescriptor);
const auto padMode = configRegisterValue.bitField(gpioPadDescriptor.modeBitFieldDescriptor);
if (padMode == static_cast<std::uint8_t>(GpioPadDirection::INPUT)) {
output.emplace_back(
TargetGpioPadDescriptorAndStatePair{
*padDescriptor,
TargetGpioPadState{
readGpioReg(gpioPadDescriptor.inputDataRegisterDescriptor).bitFieldAs<bool>(
gpioPadDescriptor.inputDataBitFieldDescriptor
) ? TargetGpioPadState::State::HIGH : TargetGpioPadState::State::LOW,
TargetGpioPadState::DataDirection::INPUT
}
}
);
continue;
}
output.emplace_back(
TargetGpioPadDescriptorAndStatePair{
*padDescriptor,
TargetGpioPadState{
readGpioReg(gpioPadDescriptor.outputDataRegisterDescriptor).bitFieldAs<bool>(
gpioPadDescriptor.outputDataBitFieldDescriptor
) ? TargetGpioPadState::State::HIGH : TargetGpioPadState::State::LOW,
TargetGpioPadState::DataDirection::OUTPUT
}
}
);
}
return output;
}
void WchRiscV::setGpioPadState(const TargetPadDescriptor& padDescriptor, const TargetGpioPadState& state) {
const auto gpioPadDescriptorIt = this->gpioPadDescriptorsByPadId.find(padDescriptor.id);
if (gpioPadDescriptorIt == this->gpioPadDescriptorsByPadId.end()) {
throw Exceptions::Exception{"Unknown pad"};
}
const auto& gpioPadDescriptor = gpioPadDescriptorIt->second;
auto configRegisterValue = this->readRegisterDynamicValue(gpioPadDescriptor.configRegisterDescriptor);
const auto currentDir = configRegisterValue.bitField(
gpioPadDescriptor.modeBitFieldDescriptor
) == static_cast<std::uint8_t>(GpioPadDirection::INPUT)
? TargetGpioPadState::DataDirection::INPUT
: TargetGpioPadState::DataDirection::OUTPUT;
if (currentDir != state.direction) {
configRegisterValue.setBitField(
gpioPadDescriptor.modeBitFieldDescriptor,
static_cast<std::uint8_t>(
state.direction == TargetGpioPadState::DataDirection::INPUT
? GpioPadDirection::INPUT
: GpioPadDirection::OUTPUT
)
);
this->writeRegister(gpioPadDescriptor.configRegisterDescriptor, configRegisterValue.data());
}
if (state.direction == TargetGpioPadState::DataDirection::OUTPUT) {
auto outDataRegisterValue = this->readRegisterDynamicValue(gpioPadDescriptor.outputDataRegisterDescriptor);
outDataRegisterValue.setBitField(
gpioPadDescriptor.outputDataBitFieldDescriptor,
state.value == TargetGpioPadState::State::HIGH
? 0x01
: 0x00
);
this->writeRegister(gpioPadDescriptor.outputDataRegisterDescriptor, outDataRegisterValue.data());
}
}
std::string WchRiscV::passthroughCommandHelpText() {
using Services::StringService;
@@ -574,4 +693,170 @@ namespace Targets::RiscV::Wch
this->reset();
}
std::map<TargetPadId, GpioPadDescriptor> WchRiscV::generateGpioPadDescriptorMapping(
const TargetRegisterDescriptor& portPeripheralClockEnableRegisterDescriptor,
const std::vector<TargetPeripheralDescriptor>& portPeripheralDescriptors,
const std::vector<TargetPadDescriptor>& padDescriptors
) {
static const auto findConfigBitField = [] (
const TargetPadDescriptor& padDescriptor,
const TargetRegisterDescriptor& configRegisterDescriptor
) -> std::optional<std::reference_wrapper<const TargetBitFieldDescriptor>> {
return padDescriptor.key.size() >= 3
? configRegisterDescriptor.tryGetBitFieldDescriptor("cnf" + padDescriptor.key.substr(2))
: std::nullopt;
};
static const auto findModeBitField = [] (
const TargetPadDescriptor& padDescriptor,
const TargetRegisterDescriptor& configRegisterDescriptor
) -> std::optional<std::reference_wrapper<const TargetBitFieldDescriptor>> {
return padDescriptor.key.size() >= 3
? configRegisterDescriptor.tryGetBitFieldDescriptor("mode" + padDescriptor.key.substr(2))
: std::nullopt;
};
static const auto findInputDataBitField = [] (
const TargetPadDescriptor& padDescriptor,
const TargetRegisterDescriptor& inputDataRegisterDescriptor
) -> std::optional<std::reference_wrapper<const TargetBitFieldDescriptor>> {
return padDescriptor.key.size() >= 3
? inputDataRegisterDescriptor.tryGetBitFieldDescriptor("indr" + padDescriptor.key.substr(2))
: std::nullopt;
};
static const auto findOutputDataBitField = [] (
const TargetPadDescriptor& padDescriptor,
const TargetRegisterDescriptor& outputDataRegisterDescriptor
) -> std::optional<std::reference_wrapper<const TargetBitFieldDescriptor>> {
return padDescriptor.key.size() >= 3
? outputDataRegisterDescriptor.tryGetBitFieldDescriptor("odr" + padDescriptor.key.substr(2))
: std::nullopt;
};
auto output = std::map<TargetPadId, GpioPadDescriptor>{};
for (const auto& padDesc : padDescriptors) {
if (padDesc.type != TargetPadType::GPIO) {
continue;
}
for (const auto& peripheralDesc : portPeripheralDescriptors) {
if (
!peripheralDesc.tryGetFirstSignalDescriptor(padDesc.key).has_value()
|| peripheralDesc.key.size() < 5
|| peripheralDesc.key.find("port") != 0
) {
continue;
}
const auto portLetter = peripheralDesc.key.substr(4, 1);
const auto peripheralClockEnableBitFieldDescOpt = portPeripheralClockEnableRegisterDescriptor.tryGetBitFieldDescriptor(
"iop" + portLetter + "en"
);
if (!peripheralClockEnableBitFieldDescOpt.has_value()) {
continue;
}
auto configRegisterDescOpt = std::optional<std::reference_wrapper<const TargetRegisterDescriptor>>{};
auto configBitFieldDescOpt = std::optional<std::reference_wrapper<const TargetBitFieldDescriptor>>{};
auto modeBitFieldDescOpt = std::optional<std::reference_wrapper<const TargetBitFieldDescriptor>>{};
const auto portGroupDescOpt = peripheralDesc.tryGetRegisterGroupDescriptor("port");
if (!portGroupDescOpt.has_value()) {
continue;
}
const auto& portGroupDescriptor = portGroupDescOpt->get();
const auto configLowRegisterDescOpt = portGroupDescriptor.tryGetRegisterDescriptor("cfglr");
const auto configHighRegisterDescOpt = portGroupDescriptor.tryGetRegisterDescriptor("cfghr");
const auto configExtendedRegisterDescOpt = portGroupDescriptor.tryGetRegisterDescriptor("cfgxr");
if (configLowRegisterDescOpt.has_value()) {
const auto& configLowRegisterDescriptor = configLowRegisterDescOpt->get();
configBitFieldDescOpt = findConfigBitField(padDesc, configLowRegisterDescriptor);
modeBitFieldDescOpt = findModeBitField(padDesc, configLowRegisterDescriptor);
if (configBitFieldDescOpt.has_value()) {
configRegisterDescOpt = configLowRegisterDescOpt;
}
}
if (
(!configBitFieldDescOpt.has_value() || !modeBitFieldDescOpt.has_value())
&& configHighRegisterDescOpt.has_value()
) {
const auto& configHighRegisterDescriptor = configHighRegisterDescOpt->get();
configBitFieldDescOpt = findConfigBitField(padDesc, configHighRegisterDescriptor);
modeBitFieldDescOpt = findModeBitField(padDesc, configHighRegisterDescriptor);
if (configBitFieldDescOpt.has_value()) {
configRegisterDescOpt = configHighRegisterDescOpt;
}
}
if (
(!configBitFieldDescOpt.has_value() || !modeBitFieldDescOpt.has_value())
&& configExtendedRegisterDescOpt.has_value()
) {
const auto& configExtendedRegisterDescriptor = configExtendedRegisterDescOpt->get();
configBitFieldDescOpt = findConfigBitField(padDesc, configExtendedRegisterDescriptor);
modeBitFieldDescOpt = findModeBitField(padDesc, configExtendedRegisterDescriptor);
if (configBitFieldDescOpt.has_value()) {
configRegisterDescOpt = configExtendedRegisterDescOpt;
}
}
if (
!configRegisterDescOpt.has_value()
|| !configBitFieldDescOpt.has_value()
|| !modeBitFieldDescOpt.has_value()
) {
continue;
}
const auto inputDataRegisterDescOpt = portGroupDescriptor.tryGetRegisterDescriptor("indr");
const auto outputDataRegisterDescOpt = portGroupDescriptor.tryGetRegisterDescriptor("outdr");
if (!inputDataRegisterDescOpt.has_value() || !outputDataRegisterDescOpt.has_value()) {
continue;
}
const auto& inputDataRegisterDescriptor = inputDataRegisterDescOpt->get();
const auto& outputDataRegisterDescriptor = outputDataRegisterDescOpt->get();
const auto inputBitFieldDescOpt = findInputDataBitField(padDesc, inputDataRegisterDescriptor);
const auto outputBitFieldDescOpt = findOutputDataBitField(padDesc, outputDataRegisterDescriptor);
if (!inputBitFieldDescOpt.has_value() || !outputBitFieldDescOpt.has_value()) {
continue;
}
output.emplace(
padDesc.id,
GpioPadDescriptor{
.peripheralClockEnableBitFieldDescriptor = peripheralClockEnableBitFieldDescOpt->get(),
.configRegisterDescriptor = configRegisterDescOpt->get(),
.configBitFieldDescriptor = configBitFieldDescOpt->get(),
.modeBitFieldDescriptor = modeBitFieldDescOpt->get(),
.inputDataRegisterDescriptor = inputDataRegisterDescriptor,
.inputDataBitFieldDescriptor = inputBitFieldDescOpt->get(),
.outputDataRegisterDescriptor = outputDataRegisterDescriptor,
.outputDataBitFieldDescriptor = outputBitFieldDescOpt->get(),
}
);
break;
}
}
return output;
}
}

View File

@@ -3,11 +3,16 @@
#include <cstdint>
#include <optional>
#include <functional>
#include <vector>
#include <map>
#include "src/Targets/RiscV/RiscV.hpp"
#include "src/Targets/TargetPeripheralDescriptor.hpp"
#include "src/Targets/TargetPadDescriptor.hpp"
#include "WchRiscVTargetConfig.hpp"
#include "TargetDescriptionFile.hpp"
#include "GpioPadDescriptor.hpp"
namespace Targets::RiscV::Wch
{
@@ -43,6 +48,9 @@ namespace Targets::RiscV::Wch
TargetMemoryAddress getProgramCounter() override;
TargetGpioPadDescriptorAndStatePairs getGpioPadStates(const TargetPadDescriptors& padDescriptors) override;
void setGpioPadState(const TargetPadDescriptor& padDescriptor, const TargetGpioPadState& state) override;
std::string passthroughCommandHelpText() override;
std::optional<PassthroughResponse> invokePassthroughCommand(const PassthroughCommand& command) override;
@@ -84,6 +92,13 @@ namespace Targets::RiscV::Wch
const TargetBitFieldDescriptor& flashStatusBootLockFieldDescriptor;
const TargetBitFieldDescriptor& flashStatusBootModeFieldDescriptor;
const TargetPeripheralDescriptor rccPeripheralDescriptor;
const TargetRegisterDescriptor& portPeripheralClockEnableRegisterDescriptor;
std::vector<TargetPadDescriptor> padDescriptors;
std::vector<TargetPeripheralDescriptor> gpioPortPeripheralDescriptors;
std::map<TargetPadId, GpioPadDescriptor> gpioPadDescriptorsByPadId;
const TargetMemorySegmentDescriptor& resolveAliasedMemorySegment();
TargetMemoryAddress transformMappedAddress(
TargetMemoryAddress address,
@@ -94,5 +109,11 @@ namespace Targets::RiscV::Wch
void unlockBootModeBitField();
void enableBootMode();
void enableUserMode();
static std::map<TargetPadId, GpioPadDescriptor> generateGpioPadDescriptorMapping(
const TargetRegisterDescriptor& portPeripheralClockEnableRegisterDescriptor,
const std::vector<TargetPeripheralDescriptor>& portPeripheralDescriptors,
const std::vector<TargetPadDescriptor>& padDescriptors
);
};
}

View File

@@ -325,11 +325,11 @@ namespace Targets::TargetDescription
std::map<std::string, TargetPadDescriptor> TargetDescriptionFile::targetPadDescriptorsByKey() const {
auto output = std::map<std::string, TargetPadDescriptor>{};
const auto gpioPadKeys = this->getGpioPadKeys();
const auto gpioPeripheralSignalPadKeys = this->getGpioPeripheralSignalPadKeys();
for (const auto& [key, pad] : this->padsByKey) {
output.emplace(
key,
TargetDescriptionFile::targetPadDescriptorFromPad(pad, gpioPadKeys)
TargetDescriptionFile::targetPadDescriptorFromPad(pad, gpioPeripheralSignalPadKeys)
);
}
@@ -362,6 +362,17 @@ namespace Targets::TargetDescription
return output;
}
std::vector<TargetPadDescriptor> TargetDescriptionFile::targetPadDescriptors() const {
auto output = std::vector<TargetPadDescriptor>{};
const auto gpioPeripheralSignalPadKeys = this->getGpioPeripheralSignalPadKeys();
for (const auto& [key, pad] : this->padsByKey) {
output.emplace_back(TargetDescriptionFile::targetPadDescriptorFromPad(pad, gpioPeripheralSignalPadKeys));
}
return output;
}
std::vector<TargetPeripheralDescriptor> TargetDescriptionFile::gpioPortPeripheralDescriptors() const {
auto output = std::vector<TargetPeripheralDescriptor>{};
@@ -505,7 +516,7 @@ namespace Targets::TargetDescription
return attribute->get();
}
std::set<std::string> TargetDescriptionFile::getGpioPadKeys() const {
std::set<std::string> TargetDescriptionFile::getGpioPeripheralSignalPadKeys() const {
auto output = std::set<std::string>{};
for (const auto* peripheral : this->getGpioPeripherals()) {
for (const auto& signal : peripheral->sigs) {
@@ -984,6 +995,7 @@ namespace Targets::TargetDescription
memorySegment.executable,
memorySegment.access,
memorySegment.access,
false,
memorySegment.pageSize
};
}
@@ -1153,10 +1165,10 @@ namespace Targets::TargetDescription
TargetPadDescriptor TargetDescriptionFile::targetPadDescriptorFromPad(
const Pad& pad,
const std::set<std::string>& gpioPadKeys
const std::set<std::string>& gpioPeripheralSignalPadKeys
) {
static const auto resolvePadType = [&gpioPadKeys] (const Pad& pad) -> TargetPadType {
if (gpioPadKeys.contains(pad.key)) {
const auto resolvePadType = [&gpioPeripheralSignalPadKeys] (const Pad& pad) -> TargetPadType {
if (gpioPeripheralSignalPadKeys.contains(pad.key)) {
return TargetPadType::GPIO;
}

View File

@@ -130,7 +130,6 @@ namespace Targets::TargetDescription
[[nodiscard]] std::set<const Peripheral*> getModulePeripherals(const std::string& moduleKey) const;
[[nodiscard]] std::set<const Peripheral*> getGpioPeripherals() const;
[[nodiscard]] std::optional<TargetMemorySegmentDescriptor> tryGetTargetMemorySegmentDescriptor(
std::string_view addressSpaceKey,
std::string_view segmentKey
@@ -150,6 +149,7 @@ namespace Targets::TargetDescription
[[nodiscard]] std::map<std::string, TargetPadDescriptor> targetPadDescriptorsByKey() const;
[[nodiscard]] std::map<std::string, TargetPinoutDescriptor> targetPinoutDescriptorsByKey() const;
[[nodiscard]] std::map<std::string, TargetVariantDescriptor> targetVariantDescriptorsByKey() const;
[[nodiscard]] std::vector<TargetPadDescriptor> targetPadDescriptors() const;
[[nodiscard]] std::vector<TargetPeripheralDescriptor> gpioPortPeripheralDescriptors() const;
protected:
@@ -180,7 +180,7 @@ namespace Targets::TargetDescription
) const;
[[nodiscard]] const std::string& getDeviceAttribute(const std::string& attributeName) const;
[[nodiscard]] std::set<std::string> getGpioPadKeys() const;
[[nodiscard]] std::set<std::string> getGpioPeripheralSignalPadKeys() const;
static std::optional<std::string> tryGetAttribute(const QDomElement& element, const QString& attributeName);
static std::string getAttribute(const QDomElement& element, const QString& attributeName);
@@ -244,7 +244,7 @@ namespace Targets::TargetDescription
static TargetPadDescriptor targetPadDescriptorFromPad(
const Pad& pad,
const std::set<std::string>& gpioPadKeys
const std::set<std::string>& gpioPeripheralSignalPadKeys
);
static TargetPinoutDescriptor targetPinoutDescriptorFromPinout(const Pinout& pinout);

View File

@@ -66,6 +66,18 @@ namespace Targets
return this->getRegisterGroupDescriptor(groupKey).getRegisterDescriptor(registerKey);
}
std::optional<
std::reference_wrapper<const TargetPeripheralSignalDescriptor>
> TargetPeripheralDescriptor::tryGetFirstSignalDescriptor(std::string_view padKey) const {
for (const auto& signalDescriptor : this->signalDescriptors) {
if (signalDescriptor.padKey == padKey) {
return std::cref(signalDescriptor);
}
}
return std::nullopt;
}
TargetPeripheralDescriptor TargetPeripheralDescriptor::clone() const {
auto output = TargetPeripheralDescriptor{
this->key,

View File

@@ -53,6 +53,10 @@ namespace Targets
const std::string& registerKey
) const;
[[nodiscard]] std::optional<
std::reference_wrapper<const TargetPeripheralSignalDescriptor>
> tryGetFirstSignalDescriptor(std::string_view padKey) const;
[[nodiscard]] TargetPeripheralDescriptor clone() const;
};
}