OXYGENHUB
◆ main openmsx / src / unittest / eeprom.cc
8.5 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "catch.hpp"
2
#include "EEPROM_93C46.hh"
3
4
#include "EmuTime.hh"
5
#include "XMLElement.hh"
6
7
#include "xrange.hh"
8
9
using namespace openmsx;
10
11
static constexpr auto step = EmuDuration::usec(10);
12
13
static void input_pattern(EEPROM_93C46& eeprom, EmuTime& time, uint32_t pattern, unsigned len)
14
{
15
	assert(len <= 32);
16
	for (auto i : xrange(len)) {
17
		eeprom.write_DI(pattern & (1 << (len - 1 - i)), time);
18
		time += step;
19
		eeprom.write_CLK(true, time);
20
		time += step;
21
		eeprom.write_CLK(false, time);
22
		time += step;
23
	}
24
}
25
26
static void write(EEPROM_93C46& eeprom, EmuTime& time, unsigned addr, uint8_t value)
27
{
28
	assert(addr < EEPROM_93C46::NUM_ADDRESSES);
29
30
	eeprom.write_CS(true, time);
31
	time += step;
32
33
	uint32_t pattern = 0b101; // start-bit + write-opcode
34
	pattern <<= EEPROM_93C46::ADDRESS_BITS;
35
	pattern |= addr;
36
	pattern <<= EEPROM_93C46::DATA_BITS;
37
	pattern |= value;
38
	unsigned len = 3 + EEPROM_93C46::ADDRESS_BITS + EEPROM_93C46::DATA_BITS;
39
	input_pattern(eeprom, time, pattern, len);
40
41
	eeprom.write_CS(false, time);
42
	time += step;
43
}
44
45
static void write_all(EEPROM_93C46& eeprom, EmuTime& time, uint8_t value)
46
{
47
	eeprom.write_CS(true, time);
48
	time += step;
49
50
	uint32_t pattern = 0b100; // start-bit + write-opcode
51
	pattern <<= EEPROM_93C46::ADDRESS_BITS;
52
	pattern |= 0b0100000; // 0b01xxxxx
53
	pattern <<= EEPROM_93C46::DATA_BITS;
54
	pattern |= value;
55
	unsigned len = 3 + EEPROM_93C46::ADDRESS_BITS + EEPROM_93C46::DATA_BITS;
56
	input_pattern(eeprom, time, pattern, len);
57
58
	eeprom.write_CS(false, time);
59
	time += step;
60
}
61
62
static void write_enable(EEPROM_93C46& eeprom, EmuTime& time)
63
{
64
	eeprom.write_CS(true, time);
65
	time += step;
66
67
	uint32_t pattern = 0b100; // start-bit + '00'-opcode
68
	pattern <<= EEPROM_93C46::ADDRESS_BITS;
69
	pattern |= 0b1100000; // 0b11xxxxx
70
	unsigned len = 3 + EEPROM_93C46::ADDRESS_BITS;
71
	input_pattern(eeprom, time, pattern, len);
72
73
	eeprom.write_CS(false, time);
74
	time += step;
75
}
76
77
static void write_disable(EEPROM_93C46& eeprom, EmuTime& time)
78
{
79
	eeprom.write_CS(true, time);
80
	time += step;
81
82
	uint32_t pattern = 0b100; // start-bit + '00'-opcode
83
	pattern <<= EEPROM_93C46::ADDRESS_BITS;
84
	pattern |= 0b0000000; // 0b00xxxxx
85
	unsigned len = 3 + EEPROM_93C46::ADDRESS_BITS;
86
	input_pattern(eeprom, time, pattern, len);
87
88
	eeprom.write_CS(false, time);
89
	time += step;
90
}
91
92
static bool waitIdle(EEPROM_93C46& eeprom, EmuTime& time)
93
{
94
	eeprom.write_CS(true, time);
95
	time += step;
96
97
	bool wasBusy = false;
98
	int i = 0;
99
	while (i < 10'000) {
100
		bool ready = eeprom.read_DO(time);
101
		time += step;
102
		if (ready) break;
103
		wasBusy = true;
104
	}
105
	CHECK(i < 10'000); // we must not wait indefinitely
106
107
	eeprom.write_CS(false, time);
108
	time += step;
109
110
	return wasBusy;
111
}
112
113
static uint8_t read(EEPROM_93C46& eeprom, EmuTime& time, unsigned addr)
114
{
115
	assert(addr < EEPROM_93C46::NUM_ADDRESSES);
116
117
	eeprom.write_CS(true, time);
118
	time += step;
119
120
	uint32_t pattern = 0b110; // start-bit + write-opcode
121
	pattern <<= EEPROM_93C46::ADDRESS_BITS;
122
	pattern |= addr;
123
	unsigned len = 3 + EEPROM_93C46::ADDRESS_BITS;
124
	input_pattern(eeprom, time, pattern, len);
125
126
	CHECK(eeprom.read_DO(time) == false); // initial 0-bit
127
	uint8_t result = 0;
128
	for (auto i : xrange(EEPROM_93C46::DATA_BITS)) {
129
		(void)i;
130
		eeprom.write_CLK(true, time);
131
		time += step;
132
		result <<= 1;
133
		result |= eeprom.read_DO(time);
134
		time += step;
135
		eeprom.write_CLK(false, time);
136
		time += step;
137
	}
138
139
	eeprom.write_CS(false, time);
140
	time += step;
141
142
	return result;
143
}
144
145
static void read_block(EEPROM_93C46& eeprom, EmuTime& time, unsigned addr,
146
                       unsigned num, uint8_t* output)
147
{
148
	assert(addr < EEPROM_93C46::NUM_ADDRESSES);
149
150
	eeprom.write_CS(true, time);
151
	time += step;
152
153
	uint32_t pattern = 0b110; // start-bit + write-opcode
154
	pattern <<= EEPROM_93C46::ADDRESS_BITS;
155
	pattern |= addr;
156
	unsigned len = 3 + EEPROM_93C46::ADDRESS_BITS;
157
	input_pattern(eeprom, time, pattern, len);
158
159
	CHECK(eeprom.read_DO(time) == false); // initial 0-bit
160
	for (auto j : xrange(num)) {
161
		(void)j;
162
		*output = 0;
163
		for (auto i : xrange(EEPROM_93C46::DATA_BITS)) {
164
			(void)i;
165
			eeprom.write_CLK(true, time);
166
			time += step;
167
			*output <<= 1;
168
			*output |= eeprom.read_DO(time);
169
			time += step;
170
			eeprom.write_CLK(false, time);
171
			time += step;
172
		}
173
		++output;
174
	}
175
176
	eeprom.write_CS(false, time);
177
	time += step;
178
}
179
180
static void erase(EEPROM_93C46& eeprom, EmuTime& time, unsigned addr)
181
{
182
	assert(addr < EEPROM_93C46::NUM_ADDRESSES);
183
184
	eeprom.write_CS(true, time);
185
	time += step;
186
187
	uint32_t pattern = 0b111; // start-bit + '00'-opcode
188
	pattern <<= EEPROM_93C46::ADDRESS_BITS;
189
	pattern |= addr;
190
	unsigned len = 3 + EEPROM_93C46::ADDRESS_BITS;
191
	input_pattern(eeprom, time, pattern, len);
192
193
	eeprom.write_CS(false, time);
194
	time += step;
195
}
196
197
static void erase_all(EEPROM_93C46& eeprom, EmuTime& time)
198
{
199
	eeprom.write_CS(true, time);
200
	time += step;
201
202
	uint32_t pattern = 0b100; // start-bit + '00'-opcode
203
	pattern <<= EEPROM_93C46::ADDRESS_BITS;
204
	pattern |= 0b1000000; // 0b10xxxxx
205
	unsigned len = 3 + EEPROM_93C46::ADDRESS_BITS;
206
	input_pattern(eeprom, time, pattern, len);
207
208
	eeprom.write_CS(false, time);
209
	time += step;
210
}
211
212
TEST_CASE("EEPROM_93C46")
213
{
214
	static const XMLElement* xml = [] {
215
		auto& doc = XMLDocument::getStaticDocument();
216
		return doc.allocateElement("dummy");
217
	}();
218
	EEPROM_93C46 eeprom(*xml);
219
	const uint8_t* data = eeprom.backdoor();
220
	EmuTime time = EmuTime::zero();
221
222
	// initially filled with 255
223
	for (auto addr : xrange(EEPROM_93C46::NUM_ADDRESSES)) {
224
		CHECK(data[addr] == 255);
225
	}
226
227
	// write 123 to address 45 .. but eeprom is still write-protected
228
	write(eeprom, time, 45, 123);
229
	CHECK(!waitIdle(eeprom, time));
230
	for (auto addr : xrange(EEPROM_93C46::NUM_ADDRESSES)) {
231
		CHECK(data[addr] == 255);
232
	}
233
234
	// again, but first write-enable
235
	write_enable(eeprom, time);
236
	CHECK(!waitIdle(eeprom, time));
237
	write(eeprom, time, 45, 123);
238
	CHECK(waitIdle(eeprom, time));
239
	for (auto addr : xrange(EEPROM_93C46::NUM_ADDRESSES)) {
240
		uint8_t expected = (addr == 45) ? 123 : 255;
241
		CHECK(data[addr] == expected);
242
	}
243
244
	// read address 45
245
	CHECK(int(read(eeprom, time, 45)) == 123); // contains 123
246
	CHECK(!waitIdle(eeprom, time)); // not busy after read
247
	CHECK(int(read(eeprom, time, 46)) == 255); // other addr still contains 255
248
	CHECK(!waitIdle(eeprom, time)); // not busy after read
249
250
	// write to address 45 without first erasing it
251
	//   You might expect to read the value 123 & 20 == 16, but the M93C46
252
	//   datasheet documents that the write command does an auto-erase.
253
	write(eeprom, time, 45, 20);
254
	CHECK(waitIdle(eeprom, time));
255
	for (auto addr : xrange(EEPROM_93C46::NUM_ADDRESSES)) {
256
		uint8_t expected = (addr == 45) ? 20 : 255;
257
		CHECK(data[addr] == expected);
258
	}
259
260
	// erase addr 99 -> doesn't influence addr 45
261
	erase(eeprom, time, 99);
262
	CHECK(waitIdle(eeprom, time));
263
	for (auto addr : xrange(EEPROM_93C46::NUM_ADDRESSES)) {
264
		uint8_t expected = (addr == 45) ? 20 : 255;
265
		CHECK(data[addr] == expected);
266
	}
267
268
	// erase addr 45 -> all 255 again
269
	erase(eeprom, time, 45);
270
	CHECK(waitIdle(eeprom, time));
271
	for (auto addr : xrange(EEPROM_93C46::NUM_ADDRESSES)) {
272
		CHECK(data[addr] == 255);
273
	}
274
275
	// write all
276
	write_all(eeprom, time, 77);
277
	CHECK(waitIdle(eeprom, time));
278
	for (auto addr : xrange(EEPROM_93C46::NUM_ADDRESSES)) {
279
		CHECK(data[addr] == 77);
280
	}
281
282
	// write to the end and the start of the address space
283
	write(eeprom, time, EEPROM_93C46::NUM_ADDRESSES - 2, 5);
284
	CHECK(waitIdle(eeprom, time));
285
	write(eeprom, time, EEPROM_93C46::NUM_ADDRESSES - 1, 11);
286
	CHECK(waitIdle(eeprom, time));
287
	write(eeprom, time, 0, 22);
288
	CHECK(waitIdle(eeprom, time));
289
290
	// write-disable and one more write
291
	write_disable(eeprom, time);
292
	CHECK(!waitIdle(eeprom, time));
293
	write(eeprom, time, 1, 33); // not executed
294
	CHECK(!waitIdle(eeprom, time));
295
	for (auto addr : xrange(EEPROM_93C46::NUM_ADDRESSES)) {
296
		uint8_t expected = (addr == 126) ?  5
297
		                 : (addr == 127) ? 11
298
		                 : (addr ==   0) ? 22
299
		                                 : 77;
300
		CHECK(data[addr] == expected);
301
	}
302
303
	// read-block, with wrap-around
304
	std::array<uint8_t, 6> buf;
305
	read_block(eeprom, time, EEPROM_93C46::NUM_ADDRESSES - 3, 6, buf.data());
306
	CHECK(!waitIdle(eeprom, time));
307
	CHECK(buf[0] == 77);
308
	CHECK(buf[1] ==  5);
309
	CHECK(buf[2] == 11);
310
	CHECK(buf[3] == 22);  // wrapped to address 0
311
	CHECK(buf[4] == 77); // was write protected
312
	CHECK(buf[5] == 77);
313
314
	// erase-all, but write-protected
315
	erase_all(eeprom, time);
316
	CHECK(!waitIdle(eeprom, time));
317
	CHECK(data[0] == 22); // not changed
318
319
	// erase-all, write-enabled
320
	write_enable(eeprom, time);
321
	CHECK(!waitIdle(eeprom, time));
322
	erase_all(eeprom, time);
323
	CHECK(waitIdle(eeprom, time));
324
	for (auto addr : xrange(EEPROM_93C46::NUM_ADDRESSES)) {
325
		CHECK(data[addr] == 255);
326
	}
327
}