openMSX
AmdFlash.hh
Go to the documentation of this file.
1#ifndef AMDFLASH_HH
2#define AMDFLASH_HH
3
4#include "serialize_meta.hh"
5
6#include "cstd.hh"
7#include "narrow.hh"
8#include "power_of_two.hh"
9#include "ranges.hh"
10#include "static_vector.hh"
11
12#include <cstddef>
13#include <cstdint>
14#include <memory>
15#include <span>
16#include <string>
17#include <vector>
18
19namespace openmsx {
20
21class MSXMotherBoard;
22class Rom;
23class SRAM;
24class DeviceConfig;
25
27{
28public:
29 // See JEDEC JEP106 https://www.jedec.org/standards-documents/docs/jep-106ab
30 enum class ManufacturerID : uint8_t {
31 AMD = 0x01,
32 STM = 0x20,
33 };
34
35 struct AutoSelect {
37 static_vector<uint8_t, 2> device; // single-byte or double-byte (0x7E prefix)
38 uint16_t extraCode = 0x0000; // code at 3rd index
39 uint16_t undefined = 0xFFFF; // undefined values
40 bool oddZero : 1 = false; // odd bytes are zero, not mirrored
41 size_t readMask = 0x03; // read address mask
42
43 constexpr void validate() const {
44 // check parity
45 assert(std::popcount(to_underlying(manufacturer)) & 1);
46 // extended marker is not part of ID
47 assert(device.size() > 0 && device[0] != 0x7E);
48 }
49 };
50
51 enum class DeviceInterface : uint16_t {
52 x8 = 0x0000,
53 x8x16 = 0x0002,
54 };
55
56 struct Region {
57 size_t count = 0;
59 };
60
61 struct Geometry {
62 constexpr Geometry(DeviceInterface deviceInterface_, std::initializer_list<Region> regions_,
63 int writeProtectPinRange_ = 0)
64 : deviceInterface(deviceInterface_)
65 , regions(regions_)
66 , writeProtectPinRange(writeProtectPinRange_)
67 , size(sum(regions, [](Region r) { return r.count * r.size; }))
68 // Originally sum(regions, &Region::count), but seems to mis-compile to 0 on MSVC.
69 // It looks like sum with projection doesn’t work at compile-time in MSVC 2022?
70 // https://developercommunity.visualstudio.com/t/wrong-code-bug-in-constexpr-evaluation/10673004
71 , sectorCount(sum(regions, [](Region r) { return r.count; })) {}
74 int writeProtectPinRange; // sectors protected by WP#, negative: from top
77
78 constexpr void validate() const {
79 assert(ranges::all_of(regions, [](const auto& region) { return region.count > 0; }));
80 assert(narrow_cast<unsigned>(cstd::abs(writeProtectPinRange)) <= sectorCount);
81 }
82 };
83
84 struct Program {
85 bool fastCommand : 1 = false;
86 bool bufferCommand : 1 = false;
87 bool shortAbortReset : 1 = false;
89
90 constexpr void validate() const {
91 assert(!fastCommand || pageSize > 1);
92 assert(!bufferCommand || pageSize > 1);
94 }
95 };
96
97 struct CFI {
98 bool command : 1 = false;
99 bool withManufacturerDevice : 1 = false; // < 0x10 contains mfr / device
100 bool withAutoSelect : 1 = false; // < 0x10 contains autoselect
101 bool exitCommand : 1 = false; // also exit by writing 0xFF
102 size_t commandMask = 0xFF; // command address mask
103 size_t readMask = 0x7F; // read address mask
125 struct Version {
126 uint8_t major = 0;
127 uint8_t minor = 0;
128 } version = {};
129 uint8_t addressSensitiveUnlock : 2 = 0;
130 uint8_t siliconRevision : 6 = 0;
131 uint8_t eraseSuspend = 0;
132 uint8_t sectorProtect = 0;
136 uint8_t burstMode = 0;
137 uint8_t pageMode = 0;
138 struct Supply {
139 uint8_t minAcc = 0;
140 uint8_t maxAcc = 0;
141 } supply = {};
142 uint8_t bootBlockFlag = 0;
143 uint8_t programSuspend = 0;
145
146 constexpr void validate() const {
147 assert(!command || primaryAlgorithm.version.major == 1);
148 }
149 };
150
151 struct Misc {
152 bool statusCommand : 1 = false;
153 bool continuityCommand : 1 = false;
154
155 constexpr void validate() const {
157 }
158 };
159
160 struct Chip {
164 CFI cfi = {};
165 Misc misc = {};
166
167 constexpr void validate() const {
171 cfi.validate();
172 misc.validate();
173 }
174 };
175
177 constexpr ValidatedChip(const Chip& chip_) : chip(chip_) {
178 chip.validate();
179 }
180 const Chip chip;
181 };
182
198 AmdFlash(const Rom& rom, const ValidatedChip& chip,
199 std::span<const bool> writeProtectSectors,
200 const DeviceConfig& config);
201 AmdFlash(const std::string& name, const ValidatedChip& chip,
202 std::span<const bool> writeProtectSectors,
203 const DeviceConfig& config);
205
206 void reset();
213 void setVppWpPinLow(bool value) { vppWpPinLow = value; }
214
215 [[nodiscard]] power_of_two<size_t> size() const { return chip.geometry.size; }
216 [[nodiscard]] uint8_t read(size_t address);
217 [[nodiscard]] uint8_t peek(size_t address) const;
218 void write(size_t address, uint8_t value);
219 [[nodiscard]] const uint8_t* getReadCacheLine(size_t address) const;
220
221 template<typename Archive>
222 void serialize(Archive& ar, unsigned version);
223
224//private:
226 size_t addr;
227 uint8_t value;
228
229 auto operator<=>(const AddressValue&) const = default;
230
231 template<typename Archive>
232 void serialize(Archive& ar, unsigned version);
233 };
234
235 enum class State { IDLE, IDENT, CFI, STATUS, PRGERR };
236
237 struct Sector {
238 size_t address;
241 ptrdiff_t writeAddress = -1;
242 const uint8_t* readAddress = nullptr;
243
244 std::weak_ordering operator<=>(const Sector& sector) const { return address <=> sector.address; }
245 bool operator==(const Sector& sector) const { return address == sector.address; }
246 };
247
248private:
249 AmdFlash(const std::string& name, const ValidatedChip& chip,
250 const Rom* rom, std::span<const bool> writeProtectSectors,
251 const DeviceConfig& config);
252
253 [[nodiscard]] size_t getSectorIndex(size_t address) const;
254 [[nodiscard]] Sector& getSector(size_t address) { return sectors[getSectorIndex(address)]; };
255 [[nodiscard]] const Sector& getSector(size_t address) const { return sectors[getSectorIndex(address)]; };
256
257 void softReset();
258 [[nodiscard]] uint16_t peekAutoSelect(size_t address, uint16_t undefined = 0xFFFF) const;
259 [[nodiscard]] uint16_t peekCFI(size_t address) const;
260
261 void setState(State newState);
262 [[nodiscard]] bool checkCommandReset();
263 [[nodiscard]] bool checkCommandLongReset();
264 [[nodiscard]] bool checkCommandCFIQuery();
265 [[nodiscard]] bool checkCommandCFIExit();
266 [[nodiscard]] bool checkCommandStatusRead();
267 [[nodiscard]] bool checkCommandStatusClear();
268 [[nodiscard]] bool checkCommandEraseSector();
269 [[nodiscard]] bool checkCommandEraseChip();
270 [[nodiscard]] bool checkCommandProgramHelper(size_t numBytes, std::span<const uint8_t> cmdSeq);
271 [[nodiscard]] bool checkCommandProgram();
272 [[nodiscard]] bool checkCommandDoubleByteProgram();
273 [[nodiscard]] bool checkCommandQuadrupleByteProgram();
274 [[nodiscard]] bool checkCommandBufferProgram();
275 [[nodiscard]] bool checkCommandAutoSelect();
276 [[nodiscard]] bool checkCommandContinuityCheck();
277 [[nodiscard]] bool partialMatch(std::span<const uint8_t> dataSeq) const;
278
279 [[nodiscard]] bool isWritable(const Sector& sector) const;
280
281public:
282 static constexpr unsigned MAX_CMD_SIZE = 5 + 256; // longest command is BufferProgram
283
284private:
285 MSXMotherBoard& motherBoard;
286 std::unique_ptr<SRAM> ram;
287 const Chip& chip;
288 std::vector<Sector> sectors;
290 State state = State::IDLE;
291 uint8_t status = 0x80;
292 bool vppWpPinLow = false; // true = protection on
293};
295
296namespace AmdFlashChip
297{
299 using enum AmdFlash::ManufacturerID;
301
302 // AMD AM29F040
303 static constexpr ValidatedChip AM29F040 = {{
304 .autoSelect{.manufacturer = AMD, .device{0xA4}, .extraCode = 0x01},
305 .geometry{DeviceInterface::x8, {{8, 0x10000}}},
306 }};
307
308 // AMD AM29F016
309 static constexpr ValidatedChip AM29F016 = {{
310 .autoSelect{.manufacturer = AMD, .device{0xAD}},
311 .geometry{DeviceInterface::x8, {{32, 0x10000}}},
312 }};
313
314 // Numonyx M29W800DB
315 static constexpr ValidatedChip M29W800DB = {{
316 .autoSelect{.manufacturer = STM, .device{0x5B}},
317 .geometry{DeviceInterface::x8x16, {{1, 0x4000}, {2, 0x2000}, {1, 0x8000}, {15, 0x10000}}},
318 .cfi{
319 .command = true,
320 .systemInterface{{0x27, 0x36, 0x00, 0x00}, {16, 1, 1024, 1}, {16, 1, 8, 1}},
321 .primaryAlgorithm{{1, 0}, 0, 0, 2, 1, 1, 4, 0, 0, 0},
322 },
323 }};
324
325 // Micron M29W640GB
326 static constexpr ValidatedChip M29W640GB = {{
327 .autoSelect{.manufacturer = STM, .device{0x10, 0x00}, .extraCode = 0x0008, .undefined = 0, .oddZero = true, .readMask = 0x7F},
328 .geometry{DeviceInterface::x8x16, {{8, 0x2000}, {127, 0x10000}}, 2},
329 .program{.fastCommand = true, .bufferCommand = true, .shortAbortReset = true, .pageSize = 32},
330 .cfi{
331 .command = true, .withManufacturerDevice = true, .commandMask = 0xFFF, .readMask = 0xFF,
332 .systemInterface{{0x27, 0x36, 0xB5, 0xC5}, {16, 1, 1024, 1}, {16, 1, 8, 1}},
333 .primaryAlgorithm{{1, 3}, 0, 0, 2, 4, 1, 4, 0, 0, 1, {0xB5, 0xC5}, 0x02, 1},
334 },
335 }};
336
337 // Infineon / Cypress / Spansion S29GL064S70TFI040
338 static constexpr ValidatedChip S29GL064S70TFI040 = {{
339 .autoSelect{.manufacturer = AMD, .device{0x10, 0x00}, .extraCode = 0xFF0A, .readMask = 0x0F},
340 .geometry{DeviceInterface::x8x16, {{8, 0x2000}, {127, 0x10000}}, 2},
341 .program{.bufferCommand = true, .pageSize = 256},
342 .cfi{
343 .command = true, .withAutoSelect = true, .exitCommand = true, .commandMask = 0xFF, .readMask = 0x7F,
344 .systemInterface{{0x27, 0x36, 0x00, 0x00}, {256, 256, 512, 65536}, {8, 8, 2, 1}},
345 .primaryAlgorithm{{1, 3}, 0, 8, 2, 1, 0, 8, 0, 0, 2, {0xB5, 0xC5}, 0x02, 1},
346 },
347 .misc {.statusCommand = true, .continuityCommand = true},
348 }};
349}
350
351} // namespace openmsx
352
353#endif
static constexpr unsigned MAX_CMD_SIZE
Definition AmdFlash.hh:282
void write(size_t address, uint8_t value)
Definition AmdFlash.cc:456
void serialize(Archive &ar, unsigned version)
Definition AmdFlash.cc:740
const uint8_t * getReadCacheLine(size_t address) const
Definition AmdFlash.cc:444
void setVppWpPinLow(bool value)
Setting the Vpp/WP# pin LOW enables a certain kind of write protection of some sectors.
Definition AmdFlash.hh:213
uint8_t read(size_t address)
Definition AmdFlash.cc:432
power_of_two< size_t > size() const
Definition AmdFlash.hh:215
uint8_t peek(size_t address) const
Definition AmdFlash.cc:186
constexpr size_t size() const noexcept
constexpr T abs(T t)
Definition cstd.hh:17
AmdFlash::ValidatedChip ValidatedChip
Definition AmdFlash.hh:298
This file implemented 3 utility functions:
Definition Autofire.cc:11
constexpr bool all_of(InputRange &&range, UnaryPredicate pred)
Definition ranges.hh:188
#define SERIALIZE_CLASS_VERSION(CLASS, VERSION)
constexpr auto sum(InputRange &&range, Proj proj={})
Definition stl.hh:245
constexpr auto to_underlying(E e) noexcept
Definition stl.hh:465
void serialize(Archive &ar, unsigned version)
Definition AmdFlash.cc:730
auto operator<=>(const AddressValue &) const =default
ManufacturerID manufacturer
Definition AmdFlash.hh:36
constexpr void validate() const
Definition AmdFlash.hh:43
static_vector< uint8_t, 2 > device
Definition AmdFlash.hh:37
struct openmsx::AmdFlash::CFI::PrimaryAlgorithm::Supply supply
struct openmsx::AmdFlash::CFI::PrimaryAlgorithm::Version version
struct openmsx::AmdFlash::CFI::SystemInterface::Supply supply
struct openmsx::AmdFlash::CFI::SystemInterface::MaxTimeoutMultiplier maxTimeoutMult
struct openmsx::AmdFlash::CFI::SystemInterface::TypicalTimeout typTimeout
struct openmsx::AmdFlash::CFI::SystemInterface systemInterface
struct openmsx::AmdFlash::CFI::PrimaryAlgorithm primaryAlgorithm
constexpr void validate() const
Definition AmdFlash.hh:146
constexpr void validate() const
Definition AmdFlash.hh:167
constexpr Geometry(DeviceInterface deviceInterface_, std::initializer_list< Region > regions_, int writeProtectPinRange_=0)
Definition AmdFlash.hh:62
static_vector< Region, 4 > regions
Definition AmdFlash.hh:73
DeviceInterface deviceInterface
Definition AmdFlash.hh:72
constexpr void validate() const
Definition AmdFlash.hh:78
power_of_two< size_t > size
Definition AmdFlash.hh:75
constexpr void validate() const
Definition AmdFlash.hh:155
constexpr void validate() const
Definition AmdFlash.hh:90
power_of_two< size_t > pageSize
Definition AmdFlash.hh:88
power_of_two< size_t > size
Definition AmdFlash.hh:58
power_of_two< size_t > size
Definition AmdFlash.hh:239
std::weak_ordering operator<=>(const Sector &sector) const
Definition AmdFlash.hh:244
bool operator==(const Sector &sector) const
Definition AmdFlash.hh:245
const uint8_t * readAddress
Definition AmdFlash.hh:242
constexpr ValidatedChip(const Chip &chip_)
Definition AmdFlash.hh:177
A constexpr power_of_two struct for power of two numbers, with compact storage and built-in value cor...