| 1 |
#include "AmdFlash.hh" |
| 2 |
|
| 3 |
#include "Rom.hh" |
| 4 |
#include "SRAM.hh" |
| 5 |
|
| 6 |
#include "MSXCPU.hh" |
| 7 |
#include "MSXCliComm.hh" |
| 8 |
#include "MSXDevice.hh" |
| 9 |
#include "MSXException.hh" |
| 10 |
#include "MSXMotherBoard.hh" |
| 11 |
|
| 12 |
#include "narrow.hh" |
| 13 |
#include "one_of.hh" |
| 14 |
#include "ranges.hh" |
| 15 |
#include "serialize.hh" |
| 16 |
#include "serialize_stl.hh" |
| 17 |
#include "xrange.hh" |
| 18 |
|
| 19 |
#include <algorithm> |
| 20 |
#include <bit> |
| 21 |
#include <cassert> |
| 22 |
#include <iterator> |
| 23 |
#include <memory> |
| 24 |
#include <utility> |
| 25 |
|
| 26 |
namespace openmsx { |
| 27 |
|
| 28 |
AmdFlash::AmdFlash(const Rom& rom, const ValidatedChip& validatedChip, |
| 29 |
std::span<const bool> writeProtectSectors, |
| 30 |
DeviceConfig& config) |
| 31 |
: AmdFlash(rom.getName() + "_flash", validatedChip, &rom, writeProtectSectors, config, {}) |
| 32 |
{ |
| 33 |
} |
| 34 |
|
| 35 |
AmdFlash::AmdFlash(const std::string& name, const ValidatedChip& validatedChip, |
| 36 |
std::span<const bool> writeProtectSectors, |
| 37 |
DeviceConfig& config, std::string_view id) |
| 38 |
: AmdFlash(name, validatedChip, nullptr, writeProtectSectors, config, id) |
| 39 |
{ |
| 40 |
} |
| 41 |
|
| 42 |
AmdFlash::AmdFlash(const std::string& name, const ValidatedChip& validatedChip, |
| 43 |
const Rom* rom, std::span<const bool> writeProtectSectors, |
| 44 |
DeviceConfig& config, std::string_view id) |
| 45 |
: motherBoard(config.getMotherBoard()) |
| 46 |
, chip(validatedChip.chip) |
| 47 |
, syncOperation(motherBoard.getScheduler()) |
| 48 |
, syncSuspend(motherBoard.getScheduler()) |
| 49 |
{ |
| 50 |
cmd.reserve(5 + chip.program.bufferPageSize); |
| 51 |
|
| 52 |
assert(writeProtectSectors.size() <= chip.geometry.sectorCount); |
| 53 |
sectors.reserve(chip.geometry.sectorCount); |
| 54 |
for (size_t address = 0; const Region& region : chip.geometry.regions) { |
| 55 |
for (size_t regionSector = 0; regionSector < region.count; ++regionSector, address += region.size) { |
| 56 |
sectors.push_back({ |
| 57 |
.index = sectors.size(), |
| 58 |
.address = address, |
| 59 |
.size = region.size, |
| 60 |
.writeProtect = sectors.size() < writeProtectSectors.size() && writeProtectSectors[sectors.size()] |
| 61 |
}); |
| 62 |
} |
| 63 |
} |
| 64 |
|
| 65 |
size_t writableSize = 0; |
| 66 |
size_t readOnlySize = 0; |
| 67 |
for (Sector& sector : sectors) { |
| 68 |
if (sector.writeProtect) { |
| 69 |
sector.writeAddress = -1; |
| 70 |
readOnlySize += sector.size; |
| 71 |
} else { |
| 72 |
sector.writeAddress = narrow<ptrdiff_t>(writableSize); |
| 73 |
writableSize += sector.size; |
| 74 |
} |
| 75 |
} |
| 76 |
assert((writableSize + readOnlySize) == size()); |
| 77 |
|
| 78 |
bool loaded = false; |
| 79 |
if (writableSize) { |
| 80 |
ram = std::make_unique<SRAM>( |
| 81 |
name, "flash rom", |
| 82 |
writableSize, config, nullptr, &loaded); |
| 83 |
} |
| 84 |
if (readOnlySize) { |
| 85 |
|
| 86 |
|
| 87 |
assert(rom); |
| 88 |
} |
| 89 |
|
| 90 |
std::unique_ptr<Rom> rom_; |
| 91 |
if (!rom && !loaded) { |
| 92 |
|
| 93 |
|
| 94 |
|
| 95 |
|
| 96 |
|
| 97 |
|
| 98 |
try { |
| 99 |
rom_ = std::make_unique<Rom>( |
| 100 |
"", "", |
| 101 |
config, id); |
| 102 |
rom = rom_.get(); |
| 103 |
config.getCliComm().printInfo( |
| 104 |
"Loaded initial content for flash ROM from ", |
| 105 |
rom->getFilename()); |
| 106 |
} catch (MSXException& e) { |
| 107 |
|
| 108 |
assert(rom == nullptr); |
| 109 |
|
| 110 |
const auto* romTag = config.getXML()->findChild("rom"); |
| 111 |
const bool initialContentSpecified = romTag && romTag->findChild("sha1"); |
| 112 |
if (initialContentSpecified) { |
| 113 |
config.getCliComm().printWarning( |
| 114 |
"Could not load specified initial content " |
| 115 |
"for flash ROM: ", e.getMessage()); |
| 116 |
} |
| 117 |
} |
| 118 |
} |
| 119 |
|
| 120 |
const size_t romSize = rom ? rom->size() : 0; |
| 121 |
size_t offset = 0; |
| 122 |
for (Sector& sector : sectors) { |
| 123 |
if (sector.writeAddress != -1) { |
| 124 |
sector.readAddress = &(*ram)[sector.writeAddress]; |
| 125 |
if (!loaded) { |
| 126 |
auto ramBlock = ram->getWriteBackdoor().subspan(sector.writeAddress, sector.size); |
| 127 |
if (offset >= romSize) { |
| 128 |
|
| 129 |
std::ranges::fill(ramBlock, 0xFF); |
| 130 |
} else if (offset + sector.size >= romSize) { |
| 131 |
|
| 132 |
const size_t last = romSize - offset; |
| 133 |
const uint8_t* romPtr = &(*rom)[offset]; |
| 134 |
copy_to_range(std::span{romPtr, last}, ramBlock); |
| 135 |
std::ranges::fill(ramBlock.subspan(last), 0xFF); |
| 136 |
} else { |
| 137 |
|
| 138 |
const uint8_t* romPtr = &(*rom)[offset]; |
| 139 |
copy_to_range(std::span{romPtr, sector.size}, ramBlock); |
| 140 |
} |
| 141 |
} |
| 142 |
} else { |
| 143 |
assert(rom); |
| 144 |
if ((offset + sector.size) <= romSize) { |
| 145 |
sector.readAddress = &(*rom)[offset]; |
| 146 |
} else { |
| 147 |
sector.readAddress = nullptr; |
| 148 |
} |
| 149 |
} |
| 150 |
offset += sector.size; |
| 151 |
} |
| 152 |
assert(offset == size()); |
| 153 |
|
| 154 |
reset(); |
| 155 |
} |
| 156 |
|
| 157 |
AmdFlash::~AmdFlash() = default; |
| 158 |
|
| 159 |
size_t AmdFlash::getSectorIndex(size_t address) const |
| 160 |
{ |
| 161 |
size_t sectorIndex = 0; |
| 162 |
for (const Region& region : chip.geometry.regions) { |
| 163 |
if (address < region.count * region.size) { |
| 164 |
return sectorIndex + address / region.size; |
| 165 |
} else { |
| 166 |
address -= region.count * region.size; |
| 167 |
sectorIndex += region.count; |
| 168 |
} |
| 169 |
} |
| 170 |
UNREACHABLE; |
| 171 |
} |
| 172 |
|
| 173 |
bool AmdFlash::isWritable(const Sector& sector) const |
| 174 |
{ |
| 175 |
if (vppWpPinLow) { |
| 176 |
const auto range = chip.geometry.writeProtectPinRange; |
| 177 |
if ((range > 0 && sector <= sectors[range - 1uz]) || |
| 178 |
(range < 0 && sector >= sectors[range + chip.geometry.sectorCount])) { |
| 179 |
return false; |
| 180 |
} |
| 181 |
} |
| 182 |
return !sector.writeProtect; |
| 183 |
} |
| 184 |
|
| 185 |
bool AmdFlash::getReadyPin() const |
| 186 |
{ |
| 187 |
return !chip.misc.readyPin || (statusRegister.ready && !status.error && !status.abort); |
| 188 |
} |
| 189 |
|
| 190 |
void AmdFlash::reset() |
| 191 |
{ |
| 192 |
cmd.clear(); |
| 193 |
status = {}; |
| 194 |
statusRegister = {}; |
| 195 |
setState(State::READ); |
| 196 |
syncOperation.removeSyncPoint(); |
| 197 |
syncSuspend.removeSyncPoint(); |
| 198 |
erase.reset(); |
| 199 |
program.reset(); |
| 200 |
} |
| 201 |
|
| 202 |
void AmdFlash::softReset() |
| 203 |
{ |
| 204 |
clearStatus(); |
| 205 |
setState(State::READ); |
| 206 |
} |
| 207 |
|
| 208 |
void AmdFlash::clearStatus() |
| 209 |
{ |
| 210 |
status.dataPolling = false; |
| 211 |
status.busyToggle = false; |
| 212 |
status.error = false; |
| 213 |
status.eraseTimer = false; |
| 214 |
status.eraseToggle = false; |
| 215 |
status.abort = false; |
| 216 |
statusRegister.eraseStatus = false; |
| 217 |
statusRegister.programStatus = false; |
| 218 |
statusRegister.bufferAbort = false; |
| 219 |
statusRegister.sectorLock = false; |
| 220 |
statusRegister.continuity = false; |
| 221 |
} |
| 222 |
|
| 223 |
void AmdFlash::setState(State newState) |
| 224 |
{ |
| 225 |
if (state == newState) return; |
| 226 |
state = newState; |
| 227 |
motherBoard.getCPU().invalidateAllSlotsRWCache(0x0000, 0x10000); |
| 228 |
} |
| 229 |
|
| 230 |
uint8_t AmdFlash::read(size_t address, EmuTime time) |
| 231 |
{ |
| 232 |
address %= size(); |
| 233 |
const uint8_t value = peek(address, time); |
| 234 |
if (state == State::READ) { |
| 235 |
if (statusRegister.eraseSuspend && erase.isPending(getSector(address))) { |
| 236 |
status.eraseToggle = !status.eraseToggle; |
| 237 |
} |
| 238 |
} else if (state == State::STATUS) { |
| 239 |
setState(State::READ); |
| 240 |
} else if (state == one_of(State::PROGRAM, State::ERROR)) { |
| 241 |
status.busyToggle = !status.busyToggle; |
| 242 |
} else if (state == State::ERASE_SECTOR) { |
| 243 |
status.busyToggle = !status.busyToggle; |
| 244 |
if (erase.isPending(getSector(address))) { |
| 245 |
status.eraseToggle = !status.eraseToggle; |
| 246 |
} |
| 247 |
} else if (state == State::ERASE_CHIP) { |
| 248 |
status.busyToggle = !status.busyToggle; |
| 249 |
status.eraseToggle = !status.eraseToggle; |
| 250 |
} |
| 251 |
return value; |
| 252 |
} |
| 253 |
|
| 254 |
uint8_t AmdFlash::peek(size_t address, EmuTime ) const |
| 255 |
{ |
| 256 |
address %= size(); |
| 257 |
if (state == State::READ) { |
| 258 |
const Sector& sector = getSector(address); |
| 259 |
if (erase.isPending(sector)) { |
| 260 |
return status.raw; |
| 261 |
} else { |
| 262 |
return sector.readAddress ? sector.readAddress[address - sector.address] : 0xFF; |
| 263 |
} |
| 264 |
} else if (state == State::AUTOSELECT) { |
| 265 |
if (chip.geometry.deviceInterface == DeviceInterface::x8x16) { |
| 266 |
if (chip.autoSelect.oddZero && (address & 1)) { |
| 267 |
|
| 268 |
return 0x00; |
| 269 |
} |
| 270 |
|
| 271 |
address >>= 1; |
| 272 |
} |
| 273 |
return narrow_cast<uint8_t>(peekAutoSelect(address, chip.autoSelect.undefined)); |
| 274 |
} else if (state == State::CFI) { |
| 275 |
if (chip.geometry.deviceInterface == DeviceInterface::x8x16) { |
| 276 |
|
| 277 |
const uint16_t result = peekCFI(address >> 1); |
| 278 |
return narrow_cast<uint8_t>(address & 1 ? result >> 8 : result); |
| 279 |
} else { |
| 280 |
return narrow_cast<uint8_t>(peekCFI(address)); |
| 281 |
} |
| 282 |
} else if (state == State::STATUS) { |
| 283 |
return statusRegister.raw; |
| 284 |
} else if (state == one_of(State::ERASE_SECTOR, State::ERASE_CHIP, State::PROGRAM, State::ERROR)) { |
| 285 |
return status.raw; |
| 286 |
} else { |
| 287 |
UNREACHABLE; |
| 288 |
} |
| 289 |
} |
| 290 |
|
| 291 |
uint16_t AmdFlash::peekAutoSelect(size_t address, uint16_t undefined) const |
| 292 |
{ |
| 293 |
switch (address & chip.autoSelect.readMask) { |
| 294 |
case 0x0: |
| 295 |
return std::to_underlying(chip.autoSelect.manufacturer); |
| 296 |
case 0x1: |
| 297 |
return chip.autoSelect.device.size() == 1 ? chip.autoSelect.device[0] | 0x2200 : 0x227E; |
| 298 |
case 0x2: |
| 299 |
if (address & 0x40) { |
| 300 |
return undefined; |
| 301 |
} |
| 302 |
if (chip.geometry.deviceInterface == DeviceInterface::x8x16) { |
| 303 |
|
| 304 |
address <<= 1; |
| 305 |
} |
| 306 |
return isWritable(getSector(address)) ? 0 : 1; |
| 307 |
case 0x3: |
| 308 |
|
| 309 |
|
| 310 |
|
| 311 |
|
| 312 |
|
| 313 |
|
| 314 |
|
| 315 |
|
| 316 |
|
| 317 |
|
| 318 |
|
| 319 |
|
| 320 |
|
| 321 |
|
| 322 |
|
| 323 |
|
| 324 |
|
| 325 |
|
| 326 |
|
| 327 |
|
| 328 |
return chip.autoSelect.extraCode; |
| 329 |
case 0xE: |
| 330 |
return chip.autoSelect.device.size() == 2 ? chip.autoSelect.device[0] | 0x2200 : undefined; |
| 331 |
case 0xF: |
| 332 |
return chip.autoSelect.device.size() == 2 ? chip.autoSelect.device[1] | 0x2200 : undefined; |
| 333 |
default: |
| 334 |
|
| 335 |
return undefined; |
| 336 |
} |
| 337 |
} |
| 338 |
|
| 339 |
uint16_t AmdFlash::peekCFI(size_t address) const |
| 340 |
{ |
| 341 |
const size_t maskedAddress = address & chip.cfi.readMask; |
| 342 |
|
| 343 |
if (maskedAddress < 0x10) { |
| 344 |
if (chip.cfi.withManufacturerDevice) { |
| 345 |
|
| 346 |
switch (maskedAddress) { |
| 347 |
case 0x0: |
| 348 |
return std::to_underlying(chip.autoSelect.manufacturer); |
| 349 |
case 0x1: |
| 350 |
return chip.autoSelect.device.size() == 1 ? chip.autoSelect.device[0] | 0x2200 : 0x227E; |
| 351 |
case 0x2: |
| 352 |
return chip.autoSelect.device.size() == 2 ? chip.autoSelect.device[0] | 0x2200 : 0xFFFF; |
| 353 |
case 0x3: |
| 354 |
return chip.autoSelect.device.size() == 2 ? chip.autoSelect.device[1] | 0x2200 : 0xFFFF; |
| 355 |
default: |
| 356 |
return 0xFFFF; |
| 357 |
} |
| 358 |
} else if (chip.cfi.withAutoSelect) { |
| 359 |
|
| 360 |
return peekAutoSelect(address | 0x40); |
| 361 |
} |
| 362 |
} |
| 363 |
|
| 364 |
switch (maskedAddress) { |
| 365 |
case 0x10: |
| 366 |
return 'Q'; |
| 367 |
case 0x11: |
| 368 |
return 'R'; |
| 369 |
case 0x12: |
| 370 |
return 'Y'; |
| 371 |
case 0x13: |
| 372 |
return 0x02; |
| 373 |
case 0x14: |
| 374 |
return 0x00; |
| 375 |
case 0x15: |
| 376 |
return 0x40; |
| 377 |
case 0x16: |
| 378 |
case 0x17: |
| 379 |
case 0x18: |
| 380 |
case 0x19: |
| 381 |
case 0x1A: |
| 382 |
return 0x00; |
| 383 |
case 0x1B: |
| 384 |
return chip.cfi.systemInterface.supply.minVcc; |
| 385 |
case 0x1C: |
| 386 |
return chip.cfi.systemInterface.supply.maxVcc; |
| 387 |
case 0x1D: |
| 388 |
return chip.cfi.systemInterface.supply.minVpp; |
| 389 |
case 0x1E: |
| 390 |
return chip.cfi.systemInterface.supply.maxVpp; |
| 391 |
case 0x1F: |
| 392 |
return chip.cfi.systemInterface.typTimeout.singleProgram.exponent; |
| 393 |
case 0x20: |
| 394 |
return chip.cfi.systemInterface.typTimeout.multiProgram.exponent; |
| 395 |
case 0x21: |
| 396 |
return chip.cfi.systemInterface.typTimeout.sectorErase.exponent; |
| 397 |
case 0x22: |
| 398 |
return chip.cfi.systemInterface.typTimeout.chipErase.exponent; |
| 399 |
case 0x23: |
| 400 |
return chip.cfi.systemInterface.maxTimeoutMult.singleProgram.exponent; |
| 401 |
case 0x24: |
| 402 |
return chip.cfi.systemInterface.maxTimeoutMult.multiProgram.exponent; |
| 403 |
case 0x25: |
| 404 |
return chip.cfi.systemInterface.maxTimeoutMult.sectorErase.exponent; |
| 405 |
case 0x26: |
| 406 |
return chip.cfi.systemInterface.maxTimeoutMult.chipErase.exponent; |
| 407 |
case 0x27: |
| 408 |
return chip.geometry.size.exponent; |
| 409 |
case 0x28: |
| 410 |
return narrow<uint8_t>(std::to_underlying(chip.geometry.deviceInterface) & 0xFF); |
| 411 |
case 0x29: |
| 412 |
return narrow<uint8_t>(std::to_underlying(chip.geometry.deviceInterface) >> 8); |
| 413 |
case 0x2A: |
| 414 |
return std::max(chip.program.fastPageSize.exponent, chip.program.bufferPageSize.exponent); |
| 415 |
case 0x2B: |
| 416 |
return 0x00; |
| 417 |
case 0x2C: |
| 418 |
return narrow<uint8_t>(chip.geometry.regions.size()); |
| 419 |
case 0x2D: |
| 420 |
case 0x31: |
| 421 |
case 0x35: |
| 422 |
case 0x39: |
| 423 |
if (const size_t index = (maskedAddress - 0x2D) >> 2; index < chip.geometry.regions.size()) { |
| 424 |
return narrow<uint8_t>((chip.geometry.regions[index].count - 1) & 0xFF); |
| 425 |
} |
| 426 |
return 0x00; |
| 427 |
case 0x2E: |
| 428 |
case 0x32: |
| 429 |
case 0x36: |
| 430 |
case 0x3A: |
| 431 |
if (const size_t index = (maskedAddress - 0x2E) >> 2; index < chip.geometry.regions.size()) { |
| 432 |
return narrow<uint8_t>((chip.geometry.regions[index].count - 1) >> 8); |
| 433 |
} |
| 434 |
return 0x00; |
| 435 |
case 0x2F: |
| 436 |
case 0x33: |
| 437 |
case 0x37: |
| 438 |
case 0x3B: |
| 439 |
if (const size_t index = (maskedAddress - 0x2F) >> 2; index < chip.geometry.regions.size()) { |
| 440 |
return narrow<uint8_t>((chip.geometry.regions[index].size >> 8) & 0xFF); |
| 441 |
} |
| 442 |
return 0x00; |
| 443 |
case 0x30: |
| 444 |
case 0x34: |
| 445 |
case 0x38: |
| 446 |
case 0x3C: |
| 447 |
if (const size_t index = (maskedAddress - 0x30) >> 2; index < chip.geometry.regions.size()) { |
| 448 |
return narrow<uint8_t>((chip.geometry.regions[index].size >> 8) >> 8); |
| 449 |
} |
| 450 |
return 0x00; |
| 451 |
case 0x40: |
| 452 |
return 'P'; |
| 453 |
case 0x41: |
| 454 |
return 'R'; |
| 455 |
case 0x42: |
| 456 |
return 'I'; |
| 457 |
case 0x43: |
| 458 |
return '0' + chip.cfi.primaryAlgorithm.version.major; |
| 459 |
case 0x44: |
| 460 |
return '0' + chip.cfi.primaryAlgorithm.version.minor; |
| 461 |
case 0x45: |
| 462 |
return narrow<uint8_t>(chip.cfi.primaryAlgorithm.addressSensitiveUnlock | (chip.cfi.primaryAlgorithm.siliconRevision << 2)); |
| 463 |
case 0x46: |
| 464 |
return chip.cfi.primaryAlgorithm.eraseSuspend; |
| 465 |
case 0x47: |
| 466 |
return chip.cfi.primaryAlgorithm.sectorProtect; |
| 467 |
case 0x48: |
| 468 |
return chip.cfi.primaryAlgorithm.sectorTemporaryUnprotect; |
| 469 |
case 0x49: |
| 470 |
return chip.cfi.primaryAlgorithm.sectorProtectScheme; |
| 471 |
case 0x4A: |
| 472 |
return chip.cfi.primaryAlgorithm.simultaneousOperation; |
| 473 |
case 0x4B: |
| 474 |
return chip.cfi.primaryAlgorithm.burstMode; |
| 475 |
case 0x4C: |
| 476 |
return chip.cfi.primaryAlgorithm.pageMode; |
| 477 |
case 0x4D: |
| 478 |
return chip.cfi.primaryAlgorithm.version.minor >= 1 ? chip.cfi.primaryAlgorithm.supply.minAcc : 0xFFFF; |
| 479 |
case 0x4E: |
| 480 |
return chip.cfi.primaryAlgorithm.version.minor >= 1 ? chip.cfi.primaryAlgorithm.supply.maxAcc : 0xFFFF; |
| 481 |
case 0x4F: |
| 482 |
return chip.cfi.primaryAlgorithm.version.minor >= 1 ? chip.cfi.primaryAlgorithm.bootBlockFlag : 0xFFFF; |
| 483 |
case 0x50: |
| 484 |
return chip.cfi.primaryAlgorithm.version.minor >= 2 ? chip.cfi.primaryAlgorithm.programSuspend : 0xFFFF; |
| 485 |
default: |
| 486 |
|
| 487 |
|
| 488 |
|
| 489 |
return 0xFFFF; |
| 490 |
} |
| 491 |
} |
| 492 |
|
| 493 |
const uint8_t* AmdFlash::getReadCacheLine(size_t address) const |
| 494 |
{ |
| 495 |
address %= size(); |
| 496 |
if (state == State::READ) { |
| 497 |
const Sector& sector = getSector(address); |
| 498 |
if (erase.isPending(sector)) { |
| 499 |
return nullptr; |
| 500 |
} else { |
| 501 |
const uint8_t* addr = sector.readAddress; |
| 502 |
return addr ? &addr[address - sector.address] : MSXDevice::unmappedRead.data(); |
| 503 |
} |
| 504 |
} else { |
| 505 |
return nullptr; |
| 506 |
} |
| 507 |
} |
| 508 |
|
| 509 |
void AmdFlash::write(size_t address, uint8_t value, EmuTime time) |
| 510 |
{ |
| 511 |
address %= size(); |
| 512 |
cmd.push_back({.addr = address, .value = value}); |
| 513 |
|
| 514 |
if (state == State::READ) { |
| 515 |
if (checkCommandAutoSelect() || |
| 516 |
checkCommandEraseSector(time) || |
| 517 |
checkCommandEraseChip(time) || |
| 518 |
checkCommandProgram(time) || |
| 519 |
checkCommandDoubleByteProgram(time) || |
| 520 |
checkCommandQuadrupleByteProgram(time) || |
| 521 |
checkCommandBufferProgram(time) || |
| 522 |
checkCommandResume(time) || |
| 523 |
checkCommandCFIQuery() || |
| 524 |
checkCommandContinuityCheck() || |
| 525 |
checkCommandStatusRead() || |
| 526 |
checkCommandStatusClear() || |
| 527 |
checkCommandLongReset() || |
| 528 |
checkCommandReset()) { |
| 529 |
|
| 530 |
} else { |
| 531 |
cmd.clear(); |
| 532 |
} |
| 533 |
} else if (state == State::AUTOSELECT) { |
| 534 |
if (checkCommandCFIQuery() || |
| 535 |
checkCommandLongReset() || |
| 536 |
checkCommandReset()) { |
| 537 |
|
| 538 |
} else { |
| 539 |
cmd.clear(); |
| 540 |
} |
| 541 |
} else if (state == State::CFI) { |
| 542 |
if (checkCommandLongReset() || |
| 543 |
checkCommandCFIExit() || |
| 544 |
checkCommandReset()) { |
| 545 |
|
| 546 |
} else { |
| 547 |
cmd.clear(); |
| 548 |
} |
| 549 |
} else if (state == State::STATUS) { |
| 550 |
|
| 551 |
if (checkCommandLongReset() || |
| 552 |
checkCommandReset()) { |
| 553 |
|
| 554 |
} else { |
| 555 |
cmd.clear(); |
| 556 |
} |
| 557 |
} else if (state == State::ERROR) { |
| 558 |
const bool shortReset = !status.abort || chip.program.shortAbortReset; |
| 559 |
if (checkCommandLongReset() || |
| 560 |
(shortReset && checkCommandReset())) { |
| 561 |
|
| 562 |
} else { |
| 563 |
cmd.clear(); |
| 564 |
} |
| 565 |
} else if (state == State::ERASE_SECTOR) { |
| 566 |
if (checkCommandEraseAdditionalSector(time) || |
| 567 |
checkCommandSuspend(time)) { |
| 568 |
|
| 569 |
} else { |
| 570 |
cmd.clear(); |
| 571 |
} |
| 572 |
} else if (state == State::ERASE_CHIP) { |
| 573 |
if (false) { |
| 574 |
|
| 575 |
} else { |
| 576 |
cmd.clear(); |
| 577 |
} |
| 578 |
} else if (state == State::PROGRAM) { |
| 579 |
if (checkCommandSuspend(time)) { |
| 580 |
|
| 581 |
} else { |
| 582 |
cmd.clear(); |
| 583 |
} |
| 584 |
} else { |
| 585 |
UNREACHABLE; |
| 586 |
} |
| 587 |
} |
| 588 |
|
| 589 |
|
| 590 |
|
| 591 |
|
| 592 |
|
| 593 |
|
| 594 |
bool AmdFlash::checkCommandReset() |
| 595 |
{ |
| 596 |
if (cmd[0].value == 0xf0) { |
| 597 |
softReset(); |
| 598 |
} |
| 599 |
return false; |
| 600 |
} |
| 601 |
|
| 602 |
bool AmdFlash::checkCommandLongReset() |
| 603 |
{ |
| 604 |
static constexpr std::array<uint8_t, 3> cmdSeq = {0xaa, 0x55, 0xf0}; |
| 605 |
if (partialMatch(cmdSeq)) { |
| 606 |
if (cmd.size() < 3) return true; |
| 607 |
softReset(); |
| 608 |
} |
| 609 |
return false; |
| 610 |
} |
| 611 |
|
| 612 |
bool AmdFlash::checkCommandAutoSelect() |
| 613 |
{ |
| 614 |
static constexpr std::array<uint8_t, 3> cmdSeq = {0xaa, 0x55, 0x90}; |
| 615 |
if (partialMatch(cmdSeq)) { |
| 616 |
if (cmd.size() < 3) return true; |
| 617 |
setState(State::AUTOSELECT); |
| 618 |
} |
| 619 |
return false; |
| 620 |
} |
| 621 |
|
| 622 |
bool AmdFlash::checkCommandCFIQuery() |
| 623 |
{ |
| 624 |
|
| 625 |
const size_t addr = (chip.geometry.deviceInterface == DeviceInterface::x8x16) ? cmd[0].addr >> 1 : cmd[0].addr; |
| 626 |
if (chip.cfi.command && (addr & chip.cfi.commandMask) == 0x55 && cmd[0].value == 0x98) { |
| 627 |
setState(State::CFI); |
| 628 |
} |
| 629 |
return false; |
| 630 |
} |
| 631 |
|
| 632 |
bool AmdFlash::checkCommandCFIExit() |
| 633 |
{ |
| 634 |
if (chip.cfi.exitCommand && cmd[0].value == 0xff) { |
| 635 |
softReset(); |
| 636 |
} |
| 637 |
return false; |
| 638 |
} |
| 639 |
|
| 640 |
bool AmdFlash::checkCommandStatusRead() |
| 641 |
{ |
| 642 |
static constexpr std::array<uint8_t, 1> cmdSeq = {0x70}; |
| 643 |
if (chip.misc.statusCommand && partialMatch(cmdSeq)) { |
| 644 |
setState(State::STATUS); |
| 645 |
} |
| 646 |
return false; |
| 647 |
} |
| 648 |
|
| 649 |
bool AmdFlash::checkCommandStatusClear() |
| 650 |
{ |
| 651 |
static constexpr std::array<uint8_t, 1> cmdSeq = {0x71}; |
| 652 |
if (chip.misc.statusCommand && partialMatch(cmdSeq)) { |
| 653 |
softReset(); |
| 654 |
} |
| 655 |
return false; |
| 656 |
} |
| 657 |
|
| 658 |
bool AmdFlash::checkCommandContinuityCheck() |
| 659 |
{ |
| 660 |
if (chip.misc.continuityCommand && cmd[0] == AddressValue{.addr = 0x5554AB, .value = 0xFF}) { |
| 661 |
if (cmd.size() < 2) return true; |
| 662 |
if (cmd.size() == 2 && cmd[1] == AddressValue{.addr = 0x2AAB54, .value = 0x00}) { |
| 663 |
statusRegister.continuity = true; |
| 664 |
} |
| 665 |
} |
| 666 |
return false; |
| 667 |
} |
| 668 |
|
| 669 |
bool AmdFlash::checkCommandEraseSector(EmuTime time) |
| 670 |
{ |
| 671 |
static constexpr std::array<uint8_t, 5> cmdSeq = {0xaa, 0x55, 0x80, 0xaa, 0x55}; |
| 672 |
if (!statusRegister.eraseSuspend && !statusRegister.programSuspend && partialMatch(cmdSeq)) { |
| 673 |
if (cmd.size() < 6) return true; |
| 674 |
if (cmd[5].value == 0x30) { |
| 675 |
clearStatus(); |
| 676 |
|
| 677 |
if (const Sector& sector = getSector(cmd[5].addr); |
| 678 |
isWritable(sector)) { |
| 679 |
erase.markPending(sector); |
| 680 |
} else { |
| 681 |
statusRegister.sectorLock = true; |
| 682 |
} |
| 683 |
|
| 684 |
statusRegister.ready = false; |
| 685 |
status.dataPolling = false; |
| 686 |
setState(State::ERASE_SECTOR); |
| 687 |
scheduleEraseOperation(time); |
| 688 |
} |
| 689 |
} |
| 690 |
return false; |
| 691 |
} |
| 692 |
|
| 693 |
bool AmdFlash::checkCommandEraseAdditionalSector(EmuTime time) |
| 694 |
{ |
| 695 |
assert(state == State::ERASE_SECTOR); |
| 696 |
if (!status.eraseTimer && cmd[0].value == 0x30) { |
| 697 |
if (const Sector& sector = getSector(cmd[0].addr); |
| 698 |
isWritable(sector)) { |
| 699 |
erase.markPending(sector); |
| 700 |
} else { |
| 701 |
statusRegister.sectorLock = true; |
| 702 |
} |
| 703 |
|
| 704 |
scheduleEraseOperation(time); |
| 705 |
} |
| 706 |
return false; |
| 707 |
} |
| 708 |
|
| 709 |
bool AmdFlash::checkCommandEraseChip(EmuTime time) |
| 710 |
{ |
| 711 |
static constexpr std::array<uint8_t, 5> cmdSeq = {0xaa, 0x55, 0x80, 0xaa, 0x55}; |
| 712 |
if (!statusRegister.eraseSuspend && !statusRegister.programSuspend && partialMatch(cmdSeq)) { |
| 713 |
if (cmd.size() < 6) return true; |
| 714 |
if (cmd[5].value == 0x10) { |
| 715 |
clearStatus(); |
| 716 |
|
| 717 |
for (const Sector& sector : sectors) { |
| 718 |
if (isWritable(sector)) { |
| 719 |
erase.markPending(sector); |
| 720 |
} else { |
| 721 |
statusRegister.sectorLock = true; |
| 722 |
} |
| 723 |
} |
| 724 |
|
| 725 |
statusRegister.ready = false; |
| 726 |
status.dataPolling = false; |
| 727 |
setState(State::ERASE_CHIP); |
| 728 |
scheduleEraseOperation(time); |
| 729 |
} |
| 730 |
} |
| 731 |
return false; |
| 732 |
} |
| 733 |
|
| 734 |
void AmdFlash::scheduleEraseOperation(EmuTime time) |
| 735 |
{ |
| 736 |
if (state == State::ERASE_SECTOR && !status.eraseTimer) { |
| 737 |
syncOperation.removeSyncPoint(); |
| 738 |
syncOperation.setSyncPoint(time + chip.erase.timerDuration); |
| 739 |
} else if (std::ranges::any_of(erase.buffer, std::identity{})) { |
| 740 |
assert(!syncOperation.isPending()); |
| 741 |
syncOperation.setSyncPoint(time + chip.erase.duration); |
| 742 |
} else { |
| 743 |
assert(!syncOperation.isPending()); |
| 744 |
syncOperation.setSyncPoint(time + chip.erase.minimumDuration); |
| 745 |
} |
| 746 |
} |
| 747 |
|
| 748 |
void AmdFlash::execEraseOperation(EmuTime time) |
| 749 |
{ |
| 750 |
assert(state == one_of(State::ERASE_SECTOR, State::ERASE_CHIP)); |
| 751 |
if (state == State::ERASE_SECTOR && !status.eraseTimer) { |
| 752 |
status.eraseTimer = true; |
| 753 |
scheduleEraseOperation(time); |
| 754 |
} else { |
| 755 |
while (erase.index < sectors.size()) { |
| 756 |
const Sector& sector = sectors[erase.index]; |
| 757 |
const bool pending = erase.isPending(sector); |
| 758 |
++erase.index; |
| 759 |
if (pending) { |
| 760 |
assert(!sector.writeProtect); |
| 761 |
ram->memset(sector.writeAddress, 0xff, sector.size); |
| 762 |
break; |
| 763 |
} |
| 764 |
} |
| 765 |
|
| 766 |
if (erase.index >= sectors.size()) { |
| 767 |
erase.reset(); |
| 768 |
statusRegister.ready = true; |
| 769 |
status.dataPolling = true; |
| 770 |
status.eraseTimer = false; |
| 771 |
setState(State::READ); |
| 772 |
} else { |
| 773 |
syncOperation.setSyncPoint(time + chip.erase.duration); |
| 774 |
} |
| 775 |
} |
| 776 |
} |
| 777 |
|
| 778 |
bool AmdFlash::EraseOperation::isPending(const Sector& sector) const |
| 779 |
{ |
| 780 |
return index <= sector.index && sector.index < buffer.size() && buffer[sector.index]; |
| 781 |
} |
| 782 |
|
| 783 |
void AmdFlash::EraseOperation::markPending(const Sector& sector) |
| 784 |
{ |
| 785 |
if (sector.index >= buffer.size()) { |
| 786 |
buffer.resize(sector.index + 1, false); |
| 787 |
} |
| 788 |
buffer[sector.index] = true; |
| 789 |
} |
| 790 |
|
| 791 |
void AmdFlash::EraseOperation::reset() |
| 792 |
{ |
| 793 |
index = 0; |
| 794 |
buffer.clear(); |
| 795 |
} |
| 796 |
|
| 797 |
bool AmdFlash::checkCommandSuspend(EmuTime time) |
| 798 |
{ |
| 799 |
if (state == State::ERASE_SECTOR) { |
| 800 |
if (chip.erase.suspend) { |
| 801 |
if (cmd[0].value == 0xB0) { |
| 802 |
if (!status.eraseTimer) { |
| 803 |
syncOperation.removeSyncPoint(); |
| 804 |
execSuspend(time); |
| 805 |
} else { |
| 806 |
if (!syncSuspend.isPending()) { |
| 807 |
syncOperation.removeSyncPoint(); |
| 808 |
syncSuspend.setSyncPoint(time + chip.erase.suspendDuration); |
| 809 |
} |
| 810 |
} |
| 811 |
} |
| 812 |
} |
| 813 |
} else if (state == State::PROGRAM) { |
| 814 |
if (chip.program.suspend) { |
| 815 |
if (cmd[0].value == 0xB0 || (chip.program.enhancedSuspend && cmd[0].value == 0x51)) { |
| 816 |
if (!syncSuspend.isPending()) { |
| 817 |
syncOperation.removeSyncPoint(); |
| 818 |
syncSuspend.setSyncPoint(time + chip.program.suspendDuration); |
| 819 |
} |
| 820 |
} |
| 821 |
} |
| 822 |
} else { |
| 823 |
UNREACHABLE; |
| 824 |
} |
| 825 |
return false; |
| 826 |
} |
| 827 |
|
| 828 |
void AmdFlash::execSuspend(EmuTime ) |
| 829 |
{ |
| 830 |
if (state == State::ERASE_SECTOR) { |
| 831 |
statusRegister.ready = true; |
| 832 |
statusRegister.eraseSuspend = true; |
| 833 |
status.dataPolling = true; |
| 834 |
status.eraseTimer = true; |
| 835 |
setState(State::READ); |
| 836 |
} else if (state == State::PROGRAM) { |
| 837 |
statusRegister.ready = true; |
| 838 |
statusRegister.programSuspend = true; |
| 839 |
setState(State::READ); |
| 840 |
} else { |
| 841 |
UNREACHABLE; |
| 842 |
} |
| 843 |
} |
| 844 |
|
| 845 |
bool AmdFlash::checkCommandResume(EmuTime time) |
| 846 |
{ |
| 847 |
if (statusRegister.programSuspend) { |
| 848 |
if (cmd[0].value == 0x30 || (chip.program.enhancedSuspend && cmd[0].value == 0x50)) { |
| 849 |
statusRegister.ready = false; |
| 850 |
statusRegister.programSuspend = false; |
| 851 |
setState(State::PROGRAM); |
| 852 |
scheduleProgramOperation(time); |
| 853 |
} |
| 854 |
} else if (statusRegister.eraseSuspend) { |
| 855 |
if (cmd[0].value == 0x30) { |
| 856 |
statusRegister.ready = false; |
| 857 |
statusRegister.eraseSuspend = false; |
| 858 |
status.dataPolling = false; |
| 859 |
setState(State::ERASE_SECTOR); |
| 860 |
scheduleEraseOperation(time); |
| 861 |
} |
| 862 |
} |
| 863 |
return false; |
| 864 |
} |
| 865 |
|
| 866 |
bool AmdFlash::checkCommandProgram(EmuTime time) |
| 867 |
{ |
| 868 |
static constexpr std::array<uint8_t, 3> cmdSeq = {0xaa, 0x55, 0xa0}; |
| 869 |
return checkCommandProgramHelper(1, cmdSeq, time); |
| 870 |
} |
| 871 |
|
| 872 |
bool AmdFlash::checkCommandDoubleByteProgram(EmuTime time) |
| 873 |
{ |
| 874 |
static constexpr std::array<uint8_t, 1> cmdSeq = {0x50}; |
| 875 |
return checkCommandProgramHelper(2, cmdSeq, time); |
| 876 |
} |
| 877 |
|
| 878 |
bool AmdFlash::checkCommandQuadrupleByteProgram(EmuTime time) |
| 879 |
{ |
| 880 |
static constexpr std::array<uint8_t, 1> cmdSeq = {0x56}; |
| 881 |
return checkCommandProgramHelper(4, cmdSeq, time); |
| 882 |
} |
| 883 |
|
| 884 |
bool AmdFlash::checkCommandProgramHelper(size_t numBytes, std::span<const uint8_t> cmdSeq, EmuTime time) |
| 885 |
{ |
| 886 |
if (numBytes <= chip.program.fastPageSize && partialMatch(cmdSeq)) { |
| 887 |
if (cmd.size() == cmdSeq.size()) clearStatus(); |
| 888 |
if (cmd.size() <= cmdSeq.size()) return true; |
| 889 |
status.dataPolling = ~cmd.back().value & 0x80; |
| 890 |
if (cmd.size() < (cmdSeq.size() + numBytes)) return true; |
| 891 |
const Sector& sector = getSector(cmd.back().addr); |
| 892 |
if (isWritable(sector) && !erase.isPending(sector)) { |
| 893 |
const size_t pageMask = chip.program.fastPageSize - 1; |
| 894 |
program.address = cmd.back().addr & ~pageMask; |
| 895 |
program.buffer.assign(chip.program.fastPageSize, 0xFFFF); |
| 896 |
|
| 897 |
|
| 898 |
|
| 899 |
|
| 900 |
for (size_t i : xrange(cmdSeq.size(), cmdSeq.size() + numBytes)) { |
| 901 |
program.buffer[cmd[i].addr & pageMask] &= cmd[i].value; |
| 902 |
} |
| 903 |
|
| 904 |
statusRegister.ready = false; |
| 905 |
setState(State::PROGRAM); |
| 906 |
scheduleProgramOperation(time); |
| 907 |
} else { |
| 908 |
|
| 909 |
statusRegister.sectorLock = true; |
| 910 |
status.dataPolling = !status.dataPolling || statusRegister.eraseSuspend; |
| 911 |
} |
| 912 |
} |
| 913 |
return false; |
| 914 |
} |
| 915 |
|
| 916 |
bool AmdFlash::checkCommandBufferProgram(EmuTime time) |
| 917 |
{ |
| 918 |
static constexpr std::array<uint8_t, 2> cmdSeq = {0xaa, 0x55}; |
| 919 |
if (chip.program.bufferPageSize > 1 && partialMatch(cmdSeq) && (cmd.size() <= 2 || cmd[2].value == 0x25)) { |
| 920 |
if (cmd.size() <= 3) { |
| 921 |
if (cmd.size() == 3) clearStatus(); |
| 922 |
return true; |
| 923 |
} else if (cmd.size() <= 4) { |
| 924 |
if (cmd[3].value < chip.program.bufferPageSize && getSector(cmd[3].addr) == getSector(cmd[2].addr)) { |
| 925 |
return true; |
| 926 |
} |
| 927 |
} else if (cmd.size() <= 5) { |
| 928 |
if (getSector(cmd[4].addr) == getSector(cmd[2].addr)) { |
| 929 |
status.dataPolling = ~cmd.back().value & 0x80; |
| 930 |
return true; |
| 931 |
} |
| 932 |
} else if (cmd.size() <= size_t(5 + cmd[3].value)) { |
| 933 |
const size_t pageMask = chip.program.bufferPageSize - 1; |
| 934 |
if ((cmd.back().addr & ~pageMask) == (cmd[4].addr & ~pageMask)) { |
| 935 |
status.dataPolling = ~cmd.back().value & 0x80; |
| 936 |
return true; |
| 937 |
} |
| 938 |
} else { |
| 939 |
const Sector& sector = getSector(cmd[2].addr); |
| 940 |
if (cmd.back().value == 0x29 && getSector(cmd.back().addr) == sector) { |
| 941 |
if (isWritable(sector) && !erase.isPending(sector)) { |
| 942 |
const size_t pageMask = chip.program.bufferPageSize - 1; |
| 943 |
program.address = cmd[4].addr & ~pageMask; |
| 944 |
program.buffer.assign(chip.program.bufferPageSize, 0xFFFF); |
| 945 |
|
| 946 |
|
| 947 |
|
| 948 |
|
| 949 |
for (size_t i : xrange<size_t>(4, cmd.size() - 1)) { |
| 950 |
program.buffer[cmd[i].addr & pageMask] = cmd[i].value; |
| 951 |
} |
| 952 |
|
| 953 |
statusRegister.ready = false; |
| 954 |
setState(State::PROGRAM); |
| 955 |
scheduleProgramOperation(time); |
| 956 |
} else { |
| 957 |
|
| 958 |
statusRegister.sectorLock = true; |
| 959 |
status.dataPolling = !status.dataPolling || statusRegister.eraseSuspend; |
| 960 |
} |
| 961 |
return false; |
| 962 |
} |
| 963 |
} |
| 964 |
status.abort = true; |
| 965 |
statusRegister.programStatus = true; |
| 966 |
statusRegister.bufferAbort = true; |
| 967 |
setState(State::ERROR); |
| 968 |
} |
| 969 |
return false; |
| 970 |
} |
| 971 |
|
| 972 |
void AmdFlash::scheduleProgramOperation(EmuTime time) |
| 973 |
{ |
| 974 |
assert(!syncOperation.isPending()); |
| 975 |
syncOperation.setSyncPoint(time + chip.program.duration); |
| 976 |
} |
| 977 |
|
| 978 |
void AmdFlash::execProgramOperation(EmuTime time) |
| 979 |
{ |
| 980 |
assert(state == State::PROGRAM); |
| 981 |
const Sector& sector = getSector(program.address); |
| 982 |
assert(!sector.writeProtect); |
| 983 |
size_t ramAddr = program.address - sector.address + sector.writeAddress + program.index; |
| 984 |
while (program.index < program.buffer.size()) { |
| 985 |
const uint16_t value = program.buffer[program.index]; |
| 986 |
++program.index; |
| 987 |
if (value < 0x100) { |
| 988 |
const uint8_t ramValue = (*ram)[ramAddr] & value; |
| 989 |
ram->write(ramAddr, ramValue); |
| 990 |
if (chip.program.bitRaiseError && ramValue != value) { |
| 991 |
program.reset(); |
| 992 |
status.error = true; |
| 993 |
setState(State::ERROR); |
| 994 |
return; |
| 995 |
} |
| 996 |
break; |
| 997 |
} |
| 998 |
++ramAddr; |
| 999 |
} |
| 1000 |
|
| 1001 |
if (program.index >= program.buffer.size()) { |
| 1002 |
program.reset(); |
| 1003 |
statusRegister.ready = true; |
| 1004 |
status.dataPolling = !status.dataPolling || statusRegister.eraseSuspend; |
| 1005 |
setState(State::READ); |
| 1006 |
} else { |
| 1007 |
syncOperation.setSyncPoint(time + chip.program.additionalDuration); |
| 1008 |
} |
| 1009 |
} |
| 1010 |
|
| 1011 |
void AmdFlash::ProgramOperation::reset() |
| 1012 |
{ |
| 1013 |
address = 0; |
| 1014 |
index = 0; |
| 1015 |
buffer.clear(); |
| 1016 |
} |
| 1017 |
|
| 1018 |
bool AmdFlash::partialMatch(std::span<const uint8_t> dataSeq) const |
| 1019 |
{ |
| 1020 |
static constexpr std::array<unsigned, 5> addrSeq = {0, 1, 0, 0, 1}; |
| 1021 |
static constexpr std::array<unsigned, 2> cmdAddr = {0x555, 0x2aa}; |
| 1022 |
(void)addrSeq; (void)cmdAddr; |
| 1023 |
|
| 1024 |
assert(dataSeq.size() <= 5); |
| 1025 |
return std::ranges::all_of(xrange(std::min(dataSeq.size(), cmd.size())), [&](const size_t i) { |
| 1026 |
|
| 1027 |
const size_t addr = (chip.geometry.deviceInterface == DeviceInterface::x8x16) ? cmd[i].addr >> 1 : cmd[i].addr; |
| 1028 |
return ((addr & 0x7FF) == cmdAddr[addrSeq[i]]) && |
| 1029 |
(cmd[i].value == dataSeq[i]); |
| 1030 |
}); |
| 1031 |
} |
| 1032 |
|
| 1033 |
void AmdFlash::execOperation(EmuTime time) |
| 1034 |
{ |
| 1035 |
if (state == one_of(State::ERASE_SECTOR, State::ERASE_CHIP)) { |
| 1036 |
execEraseOperation(time); |
| 1037 |
} else if (state == State::PROGRAM) { |
| 1038 |
execProgramOperation(time); |
| 1039 |
} else { |
| 1040 |
UNREACHABLE; |
| 1041 |
} |
| 1042 |
} |
| 1043 |
|
| 1044 |
static constexpr auto stateInfo = std::to_array<enum_string<AmdFlash::State>>({ |
| 1045 |
{ "IDLE", AmdFlash::State::READ }, |
| 1046 |
{ "IDENT", AmdFlash::State::AUTOSELECT }, |
| 1047 |
{ "PRGERR", AmdFlash::State::ERROR }, |
| 1048 |
{ "READ", AmdFlash::State::READ }, |
| 1049 |
{ "AUTOSELECT", AmdFlash::State::AUTOSELECT }, |
| 1050 |
{ "CFI", AmdFlash::State::CFI }, |
| 1051 |
{ "STATUS", AmdFlash::State::STATUS }, |
| 1052 |
{ "ERROR", AmdFlash::State::ERROR }, |
| 1053 |
{ "ERASE_SECTOR", AmdFlash::State::ERASE_SECTOR }, |
| 1054 |
{ "ERASE_CHIP", AmdFlash::State::ERASE_CHIP }, |
| 1055 |
{ "PROGRAM", AmdFlash::State::PROGRAM }, |
| 1056 |
}); |
| 1057 |
SERIALIZE_ENUM(AmdFlash::State, stateInfo); |
| 1058 |
|
| 1059 |
template<typename Archive> |
| 1060 |
void AmdFlash::AddressValue::serialize(Archive& ar, unsigned ) |
| 1061 |
{ |
| 1062 |
ar.serialize("address", addr, |
| 1063 |
"value", value); |
| 1064 |
} |
| 1065 |
|
| 1066 |
template<typename Archive> |
| 1067 |
void AmdFlash::EraseOperation::serialize(Archive& ar, unsigned ) |
| 1068 |
{ |
| 1069 |
ar.serialize("index", index, |
| 1070 |
"buffer", buffer); |
| 1071 |
} |
| 1072 |
|
| 1073 |
template<typename Archive> |
| 1074 |
void AmdFlash::ProgramOperation::serialize(Archive& ar, unsigned ) |
| 1075 |
{ |
| 1076 |
ar.serialize("address", address, |
| 1077 |
"index", index, |
| 1078 |
"buffer", buffer); |
| 1079 |
} |
| 1080 |
|
| 1081 |
|
| 1082 |
|
| 1083 |
|
| 1084 |
|
| 1085 |
template<typename Archive> |
| 1086 |
void AmdFlash::serialize(Archive& ar, unsigned version) |
| 1087 |
{ |
| 1088 |
ar.serialize("ram", *ram); |
| 1089 |
if (ar.versionAtLeast(version, 3)) { |
| 1090 |
ar.serialize("cmd", cmd, |
| 1091 |
"status", status.raw); |
| 1092 |
} else { |
| 1093 |
std::array<AddressValue, 8> cmdArray; |
| 1094 |
unsigned cmdSize = 0; |
| 1095 |
ar.serialize("cmd", cmdArray, |
| 1096 |
"cmdIdx", cmdSize); |
| 1097 |
cmd.assign(cmdArray.begin(), cmdArray.begin() + cmdSize); |
| 1098 |
} |
| 1099 |
ar.serialize("state", state); |
| 1100 |
if (ar.versionAtLeast(version, 2)) { |
| 1101 |
ar.serialize("vppWpPinLow", vppWpPinLow); |
| 1102 |
} |
| 1103 |
if (ar.versionAtLeast(version, 4)) { |
| 1104 |
ar.serialize("statusRegister", statusRegister.raw, |
| 1105 |
"erase", erase, |
| 1106 |
"program", program, |
| 1107 |
"syncOperation", syncOperation, |
| 1108 |
"syncSuspend", syncSuspend); |
| 1109 |
} |
| 1110 |
} |
| 1111 |
INSTANTIATE_SERIALIZE_METHODS(AmdFlash); |
| 1112 |
|
| 1113 |
} |