Refactored AVR8 target pin state manipulation - removed unnecessary register accesses when setting pin states

This commit is contained in:
Nav
2022-06-22 22:23:00 +01:00
parent 951c697ef6
commit af16b4bdf8
3 changed files with 61 additions and 155 deletions

View File

@@ -452,23 +452,23 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
auto pinState = TargetPinState(); auto pinState = TargetPinState();
if (pad.ddrSetAddress.has_value()) { if (pad.gpioDdrAddress.has_value()) {
auto dataDirectionRegisterValue = readMemoryBitset(pad.ddrSetAddress.value()); const auto ddrValue = readMemoryBitset(pad.gpioDdrAddress.value());
pinState.ioDirection = dataDirectionRegisterValue.test(pad.gpioPinNumber.value()) ?
pinState.ioDirection = ddrValue.test(pad.gpioPinNumber.value()) ?
TargetPinState::IoDirection::OUTPUT : TargetPinState::IoDirection::INPUT; TargetPinState::IoDirection::OUTPUT : TargetPinState::IoDirection::INPUT;
if (pinState.ioDirection == TargetPinState::IoDirection::OUTPUT if (pinState.ioDirection == TargetPinState::IoDirection::OUTPUT
&& pad.gpioPortSetAddress.has_value() && pad.gpioPortAddress.has_value()
) { ) {
auto portRegisterValue = readMemoryBitset(pad.gpioPortSetAddress.value()); const auto portRegisterValueBitset = readMemoryBitset(pad.gpioPortAddress.value());
pinState.ioState = portRegisterValue.test(pad.gpioPinNumber.value()) ? pinState.ioState = portRegisterValueBitset.test(pad.gpioPinNumber.value()) ?
TargetPinState::IoState::HIGH : TargetPinState::IoState::LOW; TargetPinState::IoState::HIGH : TargetPinState::IoState::LOW;
} else if (pinState.ioDirection == TargetPinState::IoDirection::INPUT } else if (pinState.ioDirection == TargetPinState::IoDirection::INPUT
&& pad.gpioPortInputAddress.has_value() && pad.gpioPortInputAddress.has_value()
) { ) {
auto portInputRegisterValue = readMemoryBitset(pad.gpioPortInputAddress.value()); const auto portInputRegisterValue = readMemoryBitset(pad.gpioPortInputAddress.value());
auto h = portInputRegisterValue.to_string();
pinState.ioState = portInputRegisterValue.test(pad.gpioPinNumber.value()) ? pinState.ioState = portInputRegisterValue.test(pad.gpioPinNumber.value()) ?
TargetPinState::IoState::HIGH : TargetPinState::IoState::LOW; TargetPinState::IoState::HIGH : TargetPinState::IoState::LOW;
} }
@@ -495,8 +495,8 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
throw Exception("Missing IO direction state"); throw Exception("Missing IO direction state");
} }
auto& variant = this->targetVariantsById.at(variantId); const auto& variant = this->targetVariantsById.at(variantId);
auto& padDescriptor = this->padDescriptorsByName.at(pinDescriptor.padName); const auto& padDescriptor = this->padDescriptorsByName.at(pinDescriptor.padName);
auto ioState = state.ioState; auto ioState = state.ioState;
if (state.ioDirection == TargetPinState::IoDirection::INPUT) { if (state.ioDirection == TargetPinState::IoDirection::INPUT) {
@@ -505,116 +505,53 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
} }
if ( if (
!padDescriptor.ddrSetAddress.has_value() !padDescriptor.gpioDdrAddress.has_value()
|| !padDescriptor.gpioPortSetAddress.has_value() || !padDescriptor.gpioPortAddress.has_value()
|| !padDescriptor.gpioPinNumber.has_value() || !padDescriptor.gpioPinNumber.has_value()
) { ) {
throw Exception("Inadequate pad descriptor"); throw Exception("Inadequate pad descriptor");
} }
auto pinNumber = padDescriptor.gpioPinNumber.value(); const auto pinNumber = padDescriptor.gpioPinNumber.value();
auto ddrSetAddress = padDescriptor.ddrSetAddress.value(); const auto ddrAddress = padDescriptor.gpioDdrAddress.value();
auto ddrSetValue = this->readMemory(TargetMemoryType::RAM, ddrSetAddress, 1); const auto ddrValue = this->readMemory(TargetMemoryType::RAM, ddrAddress, 1);
if (ddrSetValue.empty()) { if (ddrValue.empty()) {
throw Exception("Failed to read DDSR value"); throw Exception("Failed to read DDR value");
} }
auto ddrSetBitset = std::bitset<std::numeric_limits<unsigned char>::digits>(ddrSetValue.front()); auto ddrValueBitset = std::bitset<std::numeric_limits<unsigned char>::digits>(ddrValue.front());
if (ddrSetBitset.test(pinNumber) != (state.ioDirection == TargetPinState::IoDirection::OUTPUT)) { if (ddrValueBitset.test(pinNumber) != (state.ioDirection == TargetPinState::IoDirection::OUTPUT)) {
// DDR needs updating // DDR needs updating
ddrSetBitset.set(pinNumber, (state.ioDirection == TargetPinState::IoDirection::OUTPUT)); ddrValueBitset.set(pinNumber, (state.ioDirection == TargetPinState::IoDirection::OUTPUT));
this->writeMemory( this->writeMemory(
TargetMemoryType::RAM, TargetMemoryType::RAM,
ddrSetAddress, ddrAddress,
{static_cast<unsigned char>(ddrSetBitset.to_ulong())} {static_cast<unsigned char>(ddrValueBitset.to_ulong())}
); );
} }
if (padDescriptor.ddrClearAddress.has_value() && padDescriptor.ddrClearAddress != ddrSetAddress) {
// We also need to ensure the data direction clear register value is correct
auto ddrClearAddress = padDescriptor.ddrClearAddress.value();
auto ddrClearValue = this->readMemory(TargetMemoryType::RAM, ddrClearAddress, 1);
if (ddrClearValue.empty()) {
throw Exception("Failed to read DDCR value");
}
auto ddrClearBitset = std::bitset<std::numeric_limits<unsigned char>::digits>(ddrClearValue.front());
if (ddrClearBitset.test(pinNumber) == (state.ioDirection == TargetPinState::IoDirection::INPUT)) {
ddrClearBitset.set(pinNumber, (state.ioDirection == TargetPinState::IoDirection::INPUT));
this->writeMemory(
TargetMemoryType::RAM,
ddrClearAddress,
{static_cast<unsigned char>(ddrClearBitset.to_ulong())}
);
}
}
if (ioState.has_value()) { if (ioState.has_value()) {
auto portSetAddress = padDescriptor.gpioPortSetAddress.value(); const auto portRegisterAddress = padDescriptor.gpioPortAddress.value();
const auto portRegisterValue = this->readMemory(TargetMemoryType::RAM, portRegisterAddress, 1);
if (ioState == TargetPinState::IoState::HIGH if (portRegisterValue.empty()) {
|| !padDescriptor.gpioPortClearAddress.has_value()
|| padDescriptor.gpioPortClearAddress == portSetAddress
) {
if (padDescriptor.gpioPortClearAddress != portSetAddress) {
/*
* We don't need to read the SET register if the SET and CLEAR operations are performed via
* different registers.
*
* Instead, we can just set the appropriate bit against the SET register.
*/
this->writeMemory(
TargetMemoryType::RAM,
portSetAddress,
{static_cast<unsigned char>(0x01 << pinNumber)}
);
} else {
auto portSetRegisterValue = this->readMemory(
TargetMemoryType::RAM,
portSetAddress,
1
);
if (portSetRegisterValue.empty()) {
throw Exception("Failed to read PORT register value"); throw Exception("Failed to read PORT register value");
} }
auto portSetBitset = std::bitset<std::numeric_limits<unsigned char>::digits>( auto portRegisterValueBitset = std::bitset<std::numeric_limits<unsigned char>::digits>(
portSetRegisterValue.front() portRegisterValue.front()
); );
if (portSetBitset.test(pinNumber) != (ioState == TargetPinState::IoState::HIGH)) {
if (portRegisterValueBitset.test(pinNumber) != (ioState == TargetPinState::IoState::HIGH)) {
// PORT set register needs updating // PORT set register needs updating
portSetBitset.set(pinNumber, (ioState == TargetPinState::IoState::HIGH)); portRegisterValueBitset.set(pinNumber, (ioState == TargetPinState::IoState::HIGH));
this->writeMemory( this->writeMemory(
TargetMemoryType::RAM, TargetMemoryType::RAM,
portSetAddress, portRegisterAddress,
{static_cast<unsigned char>(portSetBitset.to_ulong())} {static_cast<unsigned char>(portRegisterValueBitset.to_ulong())}
);
}
}
}
/*
* We only need to update the PORT clear register if the IO state was set to LOW, and the clear register is
* not the same register as the set register.
*/
if (ioState == TargetPinState::IoState::LOW
&& padDescriptor.gpioPortClearAddress.has_value()
&& padDescriptor.gpioPortClearAddress != portSetAddress
) {
// We also need to ensure the PORT clear register value is correct
auto portClearAddress = padDescriptor.gpioPortClearAddress.value();
this->writeMemory(
TargetMemoryType::RAM,
portClearAddress,
{static_cast<unsigned char>(0x01 << pinNumber)}
); );
} }
} }

View File

@@ -21,22 +21,8 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit
std::string name; std::string name;
std::optional<std::uint8_t> gpioPinNumber; std::optional<std::uint8_t> gpioPinNumber;
std::optional<std::uint16_t> gpioPortAddress;
/**
* AVR8 GPIO pins can be manipulated by writing to an IO register address. The gpioPortAddress member
* holds this address.
*/
std::optional<std::uint16_t> gpioPortSetAddress;
std::optional<std::uint16_t> gpioPortInputAddress; std::optional<std::uint16_t> gpioPortInputAddress;
std::optional<std::uint16_t> gpioDdrAddress;
std::optional<std::uint16_t> gpioPortClearAddress;
/**
* The data direction of a pin is configured via a data direction register (DDR), which, like the
* gpioPortSetAddress, is an IO register.
*/
std::optional<std::uint16_t> ddrSetAddress;
std::optional<std::uint16_t> ddrClearAddress;
}; };
} }

View File

@@ -441,8 +441,7 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription
for (const auto& [registerName, portRegister] : registerGroup.registersMappedByName) { for (const auto& [registerName, portRegister] : registerGroup.registersMappedByName) {
if (registerName.find("port") == 0) { if (registerName.find("port") == 0) {
// This is the data register for the port // This is the data register for the port
padDescriptor.gpioPortSetAddress = portRegister.offset; padDescriptor.gpioPortAddress = portRegister.offset;
padDescriptor.gpioPortClearAddress = portRegister.offset;
} else if (registerName.find("pin") == 0) { } else if (registerName.find("pin") == 0) {
// This is the input data register for the port // This is the input data register for the port
@@ -450,8 +449,7 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription
} else if (registerName.find("ddr") == 0) { } else if (registerName.find("ddr") == 0) {
// This is the data direction register for the port // This is the data direction register for the port
padDescriptor.ddrSetAddress = portRegister.offset; padDescriptor.gpioDdrAddress = portRegister.offset;
padDescriptor.ddrClearAddress = portRegister.offset;
} }
} }
@@ -460,46 +458,31 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription
auto registerGroup = portModule->registerGroupsMappedByName.find("port")->second; auto registerGroup = portModule->registerGroupsMappedByName.find("port")->second;
for (const auto& [registerName, portRegister] : registerGroup.registersMappedByName) { for (const auto& [registerName, portRegister] : registerGroup.registersMappedByName) {
if (registerName.find("outset") == 0) { if (registerName == "out") {
// Include the port register offset // Include the port register offset
padDescriptor.gpioPortSetAddress = (portPeripheralRegisterGroup.has_value() padDescriptor.gpioPortAddress = (
&& portPeripheralRegisterGroup->offset.has_value()) ? portPeripheralRegisterGroup.has_value()
portPeripheralRegisterGroup->offset.value_or(0) : 0; && portPeripheralRegisterGroup->offset.has_value()
)
? portPeripheralRegisterGroup->offset.value_or(0) + portRegister.offset
: 0 + portRegister.offset;
padDescriptor.gpioPortSetAddress = padDescriptor.gpioPortSetAddress.value()
+ portRegister.offset;
} else if (registerName.find("outclr") == 0) { } else if (registerName == "dir") {
padDescriptor.gpioPortClearAddress = (portPeripheralRegisterGroup.has_value() padDescriptor.gpioDdrAddress = (
&& portPeripheralRegisterGroup->offset.has_value()) ? portPeripheralRegisterGroup.has_value()
portPeripheralRegisterGroup->offset.value_or(0) : 0; && portPeripheralRegisterGroup->offset.has_value()
)
padDescriptor.gpioPortClearAddress = padDescriptor.gpioPortClearAddress.value() ? portPeripheralRegisterGroup->offset.value_or(0) + portRegister.offset
+ portRegister.offset; : 0 + portRegister.offset;
} else if (registerName.find("dirset") == 0) {
padDescriptor.ddrSetAddress = (portPeripheralRegisterGroup.has_value()
&& portPeripheralRegisterGroup->offset.has_value()) ?
portPeripheralRegisterGroup->offset.value_or(0) : 0;
padDescriptor.ddrSetAddress = padDescriptor.ddrSetAddress.value()
+ portRegister.offset;
} else if (registerName.find("dirclr") == 0) {
padDescriptor.ddrClearAddress = (portPeripheralRegisterGroup.has_value()
&& portPeripheralRegisterGroup->offset.has_value()) ?
portPeripheralRegisterGroup->offset.value_or(0) : 0;
padDescriptor.ddrClearAddress = padDescriptor.ddrClearAddress.value()
+ portRegister.offset;
} else if (registerName == "in") { } else if (registerName == "in") {
padDescriptor.gpioPortInputAddress = (portPeripheralRegisterGroup.has_value() padDescriptor.gpioPortInputAddress = (
&& portPeripheralRegisterGroup->offset.has_value()) ? portPeripheralRegisterGroup.has_value()
portPeripheralRegisterGroup->offset.value_or(0) : 0; && portPeripheralRegisterGroup->offset.has_value()
)
padDescriptor.gpioPortInputAddress = padDescriptor.gpioPortInputAddress.value() ? portPeripheralRegisterGroup->offset.value_or(0) + portRegister.offset
+ portRegister.offset; : 0 + portRegister.offset;
} }
} }
} }
@@ -570,7 +553,7 @@ namespace Bloom::Targets::Microchip::Avr::Avr8Bit::TargetDescription
if (this->padDescriptorsByName.contains(targetPin.padName)) { if (this->padDescriptorsByName.contains(targetPin.padName)) {
const auto& pad = this->padDescriptorsByName.at(targetPin.padName); const auto& pad = this->padDescriptorsByName.at(targetPin.padName);
if (pad.gpioPortSetAddress.has_value() && pad.ddrSetAddress.has_value()) { if (pad.gpioPortAddress.has_value() && pad.gpioDdrAddress.has_value()) {
targetPin.type = TargetPinType::GPIO; targetPin.type = TargetPinType::GPIO;
} }
} }