Skip to content

Commit

Permalink
AmdFlash: Support continuity check command.
Browse files Browse the repository at this point in the history
This function allows the program to check for correct connectivity of the flash
chip, and would fail if there are shorts on adjacent address and data pins.

It seems to be specific to the Infineon / Cypress / Spansion S29GL064S, at least
I haven’t seen it being offered elsewhere.
  • Loading branch information
grauw committed May 20, 2024
1 parent 2687acb commit bd707fb
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 4 deletions.
48 changes: 45 additions & 3 deletions src/memory/AmdFlash.cc
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ uint8_t AmdFlash::peek(size_t address) const
} else {
return narrow_cast<uint8_t>(peekCFI(address));
}
} else if (state == State::PRGERR) {
} else if (state == one_of(State::STATUS, State::PRGERR)) {
return status;
} else {
UNREACHABLE;
Expand Down Expand Up @@ -454,9 +454,10 @@ bool AmdFlash::isSectorWritable(size_t sector) const

uint8_t AmdFlash::read(size_t address)
{
// note: after a read we stay in the same mode
const uint8_t value = peek(address);
if (state == State::PRGERR) {
if (state == State::STATUS) {
setState(State::IDLE);
} else if (state == State::PRGERR) {
status ^= 0x40;
}
return value;
Expand Down Expand Up @@ -486,6 +487,9 @@ void AmdFlash::write(size_t address, uint8_t value)
checkCommandBufferProgram() ||
checkCommandEraseChip() ||
checkCommandCFIQuery() ||
checkCommandContinuityCheck() ||
checkCommandStatusRead() ||
checkCommandStatusClear() ||
checkCommandLongReset() ||
checkCommandReset()) {
// do nothing, we're still matching a command, but it is not complete yet
Expand All @@ -508,6 +512,14 @@ void AmdFlash::write(size_t address, uint8_t value)
} else {
cmd.clear();
}
} else if (state == State::STATUS) {
// TODO confirm. S29GL064S datasheet: "it is not recommended".
if (checkCommandLongReset() ||
checkCommandReset()) {
// do nothing, we're still matching a command, but it is not complete yet
} else {
cmd.clear();
}
} else if (state == State::PRGERR) {
if (checkCommandLongReset() ||
(chip.program.shortAbortReset && checkCommandReset())) {
Expand Down Expand Up @@ -543,6 +555,35 @@ bool AmdFlash::checkCommandLongReset()
return false;
}

bool AmdFlash::checkCommandStatusRead()
{
static constexpr std::array<uint8_t, 1> cmdSeq = {0x70};
if (chip.misc.statusCommand && partialMatch(cmdSeq)) {
setState(State::STATUS);
}
return false;
}

bool AmdFlash::checkCommandStatusClear()
{
static constexpr std::array<uint8_t, 1> cmdSeq = {0x71};
if (chip.misc.statusCommand && partialMatch(cmdSeq)) {
softReset();
}
return false;
}

bool AmdFlash::checkCommandContinuityCheck()
{
if (chip.misc.continuityCommand && cmd[0] == AddressValue{0x5554AB, 0xFF}) {
if (cmd.size() < 2) return true;
if (cmd.size() == 2 && cmd[1] == AddressValue{0x2AAB54, 0x00}) {
status |= 0x01;
}
}
return false;
}

bool AmdFlash::checkCommandCFIQuery()
{
// convert byte address to native address
Expand Down Expand Up @@ -700,6 +741,7 @@ static constexpr std::initializer_list<enum_string<AmdFlash::State>> stateInfo =
{ "IDLE", AmdFlash::State::IDLE },
{ "IDENT", AmdFlash::State::IDENT },
{ "CFI", AmdFlash::State::CFI },
{ "STATUS", AmdFlash::State::STATUS },
{ "PRGERR", AmdFlash::State::PRGERR }
};
SERIALIZE_ENUM(AmdFlash::State, stateInfo);
Expand Down
18 changes: 17 additions & 1 deletion src/memory/AmdFlash.hh
Original file line number Diff line number Diff line change
Expand Up @@ -136,17 +136,28 @@ public:
}
};

struct Misc {
bool statusCommand : 1 = false;
bool continuityCommand : 1 = false;

constexpr void validate() const {
assert(!continuityCommand || statusCommand);
}
};

struct Chip {
AutoSelect autoSelect;
Geometry geometry;
Program program = {};
CFI cfi = {};
Misc misc = {};

constexpr void validate() const {
autoSelect.validate();
geometry.validate();
program.validate();
cfi.validate();
misc.validate();
}
};

Expand Down Expand Up @@ -208,11 +219,13 @@ public:
size_t addr;
uint8_t value;

auto operator<=>(const AddressValue&) const = default;

template<typename Archive>
void serialize(Archive& ar, unsigned version);
};

enum class State { IDLE, IDENT, CFI, PRGERR };
enum class State { IDLE, IDENT, CFI, STATUS, PRGERR };

private:
void init(const std::string& name, const DeviceConfig& config, Load load,
Expand All @@ -229,6 +242,8 @@ private:
[[nodiscard]] bool checkCommandLongReset();
[[nodiscard]] bool checkCommandCFIQuery();
[[nodiscard]] bool checkCommandCFIExit();
[[nodiscard]] bool checkCommandStatusRead();
[[nodiscard]] bool checkCommandStatusClear();
[[nodiscard]] bool checkCommandEraseSector();
[[nodiscard]] bool checkCommandEraseChip();
[[nodiscard]] bool checkCommandProgramHelper(size_t numBytes, std::span<const uint8_t> cmdSeq);
Expand All @@ -237,6 +252,7 @@ private:
[[nodiscard]] bool checkCommandQuadrupleByteProgram();
[[nodiscard]] bool checkCommandBufferProgram();
[[nodiscard]] bool checkCommandAutoSelect();
[[nodiscard]] bool checkCommandContinuityCheck();
[[nodiscard]] bool partialMatch(std::span<const uint8_t> dataSeq) const;

[[nodiscard]] bool isSectorWritable(size_t sector) const;
Expand Down

0 comments on commit bd707fb

Please sign in to comment.