VirtualBox

source: vbox/trunk/src/VBox/Devices/Network/testcase/tstDevEEPROM.cpp@ 91942

Last change on this file since 91942 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.2 KB
Line 
1/* $Id: tstDevEEPROM.cpp 82968 2020-02-04 10:35:17Z vboxsync $ */
2/** @file
3 * EEPROM 93C46 unit tests.
4 */
5
6/*
7 * Copyright (C) 2007-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <cppunit/ui/text/TestRunner.h>
19#include <cppunit/extensions/HelperMacros.h>
20
21#include "../DevEEPROM.h"
22
23static const uint16_t initialContent[] = {
24 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
25 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
26 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
27 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
28 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
29 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
30 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
31 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f
32};
33
34/**
35 * Test fixture for 93C46-compatible EEPROM device emulation.
36 */
37class EEPROMTest : public CppUnit::TestFixture {
38 CPPUNIT_TEST_SUITE( EEPROMTest );
39
40 CPPUNIT_TEST( testRead );
41 CPPUNIT_TEST( testSequentialRead );
42 CPPUNIT_TEST( testWrite );
43 CPPUNIT_TEST( testWriteAll );
44 CPPUNIT_TEST( testWriteDisabled );
45 CPPUNIT_TEST( testErase );
46 CPPUNIT_TEST( testEraseAll );
47
48 CPPUNIT_TEST_SUITE_END();
49
50private:
51 enum Wires { DO=8, DI=4, CS=2, SK=0x01 };
52 enum OpCodes {
53 READ_OPCODE = 0x6,
54 WRITE_OPCODE = 0x5,
55 ERASE_OPCODE = 0x7,
56 EWDS_OPCODE = 0x10, // erase/write disable
57 WRAL_OPCODE = 0x11, // write all
58 ERAL_OPCODE = 0x12, // erase all
59 EWEN_OPCODE = 0x13 // erase/write enable
60 };
61 enum BitWidths {
62 READ_OPCODE_BITS = 3,
63 WRITE_OPCODE_BITS = 3,
64 ERASE_OPCODE_BITS = 3,
65 EWDS_OPCODE_BITS = 5,
66 WRAL_OPCODE_BITS = 5,
67 ERAL_OPCODE_BITS = 5,
68 EWEN_OPCODE_BITS = 5,
69 READ_ADDR_BITS = 6,
70 WRITE_ADDR_BITS = 6,
71 ERASE_ADDR_BITS = 6,
72 EWDS_ADDR_BITS = 4,
73 WRAL_ADDR_BITS = 4,
74 ERAL_ADDR_BITS = 4,
75 EWEN_ADDR_BITS = 4,
76 DATA_BITS = 16
77 };
78
79 EEPROM93C46 *eeprom;
80
81 // Helper methods
82 void shiftOutBits(uint16_t data, uint16_t count);
83 uint16_t shiftInBits(uint16_t count);
84 void getReady();
85 void standby();
86 void stop();
87 uint16_t readAt(uint16_t addr);
88 bool writeTo(uint16_t addr, uint16_t value);
89 void writeOpAddr(int opCode, int opCodeBits, uint16_t addr, int addrBits);
90 void writeData(uint16_t value) { shiftOutBits(value, DATA_BITS); }
91 bool waitForCompletion();
92
93public:
94 void setUp()
95 {
96 eeprom = new EEPROM93C46;
97 eeprom->init(initialContent);
98 }
99
100 void tearDown()
101 {
102 delete eeprom;
103 }
104
105 void testSize()
106 {
107 CPPUNIT_ASSERT_EQUAL( sizeof(initialContent), (size_t)EEPROM93C46::SIZE );
108 }
109
110 void testRead()
111 {
112 getReady();
113 for ( uint32_t wordAddr=0; wordAddr < EEPROM93C46::SIZE; wordAddr++ ) {
114 shiftOutBits(READ_OPCODE, READ_OPCODE_BITS);
115 shiftOutBits(wordAddr, READ_ADDR_BITS);
116
117 CPPUNIT_ASSERT_EQUAL( initialContent[wordAddr], (uint16_t)wordAddr );
118 CPPUNIT_ASSERT_EQUAL( initialContent[wordAddr], shiftInBits(DATA_BITS) );
119 standby();
120 }
121 stop();
122 }
123
124 void testSequentialRead()
125 {
126 getReady();
127 shiftOutBits(READ_OPCODE, READ_OPCODE_BITS);
128 shiftOutBits(0, READ_ADDR_BITS);
129 for ( int wordAddr=0; wordAddr < EEPROM93C46::SIZE; wordAddr++ ) {
130 CPPUNIT_ASSERT_EQUAL( initialContent[wordAddr], shiftInBits(DATA_BITS) );
131 }
132 stop();
133 }
134
135 void testWrite()
136 {
137 //unused: int i;
138 uint16_t wordAddr;
139
140 getReady();
141 // Enable write
142 writeOpAddr(EWEN_OPCODE, EWEN_OPCODE_BITS, 0, EWEN_ADDR_BITS);
143 standby();
144
145 for ( wordAddr=0; wordAddr < EEPROM93C46::SIZE; wordAddr++ ) {
146 //writeOpAddr(WRITE_OPCODE, WRITE_OPCODE_BITS, (uint16_t)wordAddr, WRITE_ADDR_BITS);
147 writeTo(wordAddr, 0x3F00 - (wordAddr<<8));
148 standby();
149
150 if (!waitForCompletion()) {
151 CPPUNIT_FAIL("EEPROM write was not completed");
152 stop();
153 return;
154 }
155 standby();
156 }
157
158 // Disable write
159 writeOpAddr(EWDS_OPCODE, EWDS_OPCODE_BITS, 0, EWDS_ADDR_BITS);
160
161 stop();
162
163 // Now check the result
164 getReady();
165 writeOpAddr(READ_OPCODE, READ_OPCODE_BITS, 0, READ_ADDR_BITS);
166 for ( wordAddr=0; wordAddr < EEPROM93C46::SIZE; wordAddr++ ) {
167 CPPUNIT_ASSERT_EQUAL((uint16_t)(0x3F00 - (wordAddr<<8)), shiftInBits(DATA_BITS) );
168 }
169 stop();
170 }
171
172 void testWriteDisabled()
173 {
174 getReady();
175
176 uint16_t addr = 0;
177 uint16_t oldValue = readAt(addr);
178 stop();
179 getReady();
180 if (writeTo(addr, ~oldValue)) {
181 // Write appears to be successful -- continue
182 CPPUNIT_ASSERT_EQUAL(oldValue, readAt(addr));
183 }
184 else {
185 CPPUNIT_FAIL("EEPROM write was not completed");
186 }
187 stop();
188 }
189
190 void testErase()
191 {
192 int i;
193 uint16_t addr = 0x1F;
194
195 getReady();
196 // Enable write
197 shiftOutBits(EWEN_OPCODE, EWEN_OPCODE_BITS);
198 shiftOutBits(0, EWEN_ADDR_BITS);
199 standby();
200
201 if (writeTo(addr, addr)) {
202 stop();
203 getReady();
204 // Write successful -- continue
205 CPPUNIT_ASSERT_EQUAL(addr, readAt(addr));
206 stop();
207 getReady();
208
209 shiftOutBits(ERASE_OPCODE, ERASE_OPCODE_BITS);
210 shiftOutBits(addr, ERASE_ADDR_BITS);
211
212 standby();
213
214 for (i = 0; i < 200; i++) {
215 if (eeprom->read() & DO)
216 break;
217 //usec_delay(50);
218 }
219
220 if (i == 200) {
221 CPPUNIT_FAIL("EEPROM erase was not completed");
222 stop();
223 return;
224 }
225
226 standby();
227
228 shiftOutBits(EWDS_OPCODE, EWDS_OPCODE_BITS);
229 shiftOutBits(0, EWDS_ADDR_BITS);
230
231 stop();
232 getReady();
233 CPPUNIT_ASSERT_EQUAL((uint16_t)0xFFFF, readAt(addr));
234 }
235 else {
236 CPPUNIT_FAIL("EEPROM write was not completed");
237 }
238 stop();
239 }
240
241 void testWriteAll()
242 {
243 uint16_t addr;
244
245 getReady();
246 // Enable write
247 writeOpAddr(EWEN_OPCODE, EWEN_OPCODE_BITS, 0, EWEN_ADDR_BITS);
248 standby();
249 // Fill all memory
250 writeOpAddr(WRAL_OPCODE, WRAL_OPCODE_BITS, 0, WRAL_ADDR_BITS);
251 writeData(0xABBA);
252 standby();
253
254 if (waitForCompletion()) {
255 stop();
256 getReady();
257 // Write successful -- verify all memory
258 for ( addr=0; addr < EEPROM93C46::SIZE; addr++ ) {
259 CPPUNIT_ASSERT_EQUAL((uint16_t)0xABBA, readAt(addr));
260 }
261 }
262 else {
263 CPPUNIT_FAIL("EEPROM write was not completed");
264 }
265 stop();
266 }
267
268 void testEraseAll()
269 {
270 //unused: int i;
271 uint16_t addr = 0x1F;
272
273 getReady();
274 // Enable write
275 writeOpAddr(EWEN_OPCODE, EWEN_OPCODE_BITS, 0, EWEN_ADDR_BITS);
276 standby();
277 // Fill all memory
278 writeOpAddr(WRITE_OPCODE, WRITE_OPCODE_BITS, addr, WRITE_ADDR_BITS);
279 writeData(0);
280 standby();
281
282 if (waitForCompletion()) {
283 stop();
284 getReady();
285 // Write successful -- verify random location
286 CPPUNIT_ASSERT_EQUAL((uint16_t)0, readAt(addr));
287 stop();
288 getReady();
289
290 writeOpAddr(ERAL_OPCODE, ERAL_OPCODE_BITS, addr, ERAL_ADDR_BITS);
291 standby();
292
293 if (!waitForCompletion()) {
294 CPPUNIT_FAIL("EEPROM erase was not completed");
295 stop();
296 return;
297 }
298
299 standby();
300
301 writeOpAddr(EWDS_OPCODE, EWDS_OPCODE_BITS, 0, EWDS_ADDR_BITS);
302 stop();
303
304 getReady();
305 for ( addr=0; addr < EEPROM93C46::SIZE; addr++ ) {
306 CPPUNIT_ASSERT_EQUAL((uint16_t)0xFFFF, readAt(addr));
307 }
308 }
309 else {
310 CPPUNIT_FAIL("EEPROM write was not completed");
311 }
312 stop();
313 }
314};
315
316/**
317 * shiftOutBits - Shift data bits our to the EEPROM
318 * @hw: pointer to the EEPROM object
319 * @data: data to send to the EEPROM
320 * @count: number of bits to shift out
321 *
322 * We need to shift 'count' bits out to the EEPROM. So, the value in the
323 * "data" parameter will be shifted out to the EEPROM one bit at a time.
324 * In order to do this, "data" must be broken down into bits.
325 **/
326void EEPROMTest::shiftOutBits(uint16_t data, uint16_t count) {
327 uint32_t wires = eeprom->read();
328 uint32_t mask;
329
330 mask = 0x01 << (count - 1);
331 wires &= ~DO;
332
333 do {
334 wires &= ~DI;
335
336 if (data & mask)
337 wires |= DI;
338
339 eeprom->write(wires);
340
341 // Raise clock
342 eeprom->write(wires |= SK);
343 // Lower clock
344 eeprom->write(wires &= ~SK);
345
346 mask >>= 1;
347 } while (mask);
348
349 wires &= ~DI;
350 eeprom->write(wires);
351}
352
353/**
354 * shiftInBits - Shift data bits in from the EEPROM
355 * @count: number of bits to shift in
356 *
357 * In order to read a register from the EEPROM, we need to shift 'count' bits
358 * in from the EEPROM. Bits are "shifted in" by raising the clock input to
359 * the EEPROM (setting the SK bit), and then reading the value of the data out
360 * "DO" bit. During this "shifting in" process the data in "DI" bit should
361 * always be clear.
362 **/
363uint16_t EEPROMTest::shiftInBits(uint16_t count)
364{
365 uint32_t wires;
366 uint32_t i;
367 uint16_t data;
368
369 wires = eeprom->read();
370
371 wires &= ~(DO | DI);
372 data = 0;
373
374 for (i = 0; i < count; i++) {
375 data <<= 1;
376 // Raise clock
377 eeprom->write(wires |= SK);
378
379 wires = eeprom->read();
380
381 wires &= ~DI;
382 if (wires & DO)
383 data |= 1;
384
385 // Lower clock
386 eeprom->write(wires &= ~SK);
387 }
388
389 return data;
390}
391
392/**
393 * getReady - Prepares EEPROM for read/write
394 *
395 * Setups the EEPROM for reading and writing.
396 **/
397void EEPROMTest::getReady()
398{
399 unsigned wires = eeprom->read();
400 /* Clear SK and DI */
401 eeprom->write(wires &= ~(DI | SK));
402 /* Set CS */
403 eeprom->write(wires | CS);
404}
405
406/**
407 * standby - Return EEPROM to standby state
408 *
409 * Return the EEPROM to a standby state.
410 **/
411void EEPROMTest::standby()
412{
413 unsigned wires = eeprom->read();
414
415 eeprom->write(wires &= ~(CS | SK));
416
417 // Raise clock
418 eeprom->write(wires |= SK);
419
420 // Select EEPROM
421 eeprom->write(wires |= CS);
422
423 // Lower clock
424 eeprom->write(wires &= ~SK);
425}
426
427/**
428 * stop - Terminate EEPROM command
429 *
430 * Terminates the current command by inverting the EEPROM's chip select pin.
431 **/
432void EEPROMTest::stop()
433{
434 unsigned wires = eeprom->read();
435
436 eeprom->write(wires &= ~(CS | DI));
437 // Raise clock
438 eeprom->write(wires |= SK);
439 // Lower clock
440 eeprom->write(wires &= ~SK);
441}
442
443/**
444 * readAt - Read a word at specified address
445 * @addr: address to read
446 *
447 * Returns the value of the word specified in 'addr' parameter.
448 **/
449uint16_t EEPROMTest::readAt(uint16_t addr)
450{
451 getReady();
452 shiftOutBits(READ_OPCODE, READ_OPCODE_BITS);
453 shiftOutBits(addr, READ_ADDR_BITS);
454
455 uint16_t value = shiftInBits(DATA_BITS);
456 stop();
457
458 return value;
459}
460
461/**
462 * writeTo - Write a word to specified address
463 * @addr: address to write to
464 * @value: value to store
465 *
466 * Returns false if write did not complete.
467 *
468 * Note: Make sure EEPROM is selected and writable before attempting
469 * to write. Use getReady() and stop() to select/deselect
470 * EEPROM.
471 **/
472bool EEPROMTest::writeTo(uint16_t addr, uint16_t value)
473{
474 writeOpAddr(WRITE_OPCODE, WRITE_OPCODE_BITS, addr, WRITE_ADDR_BITS);
475 writeData(value);
476 standby();
477 return waitForCompletion();
478}
479
480
481/**
482 * waitForCompletion - Wait until EEPROM clears the busy bit
483 *
484 * Returns false if the EEPROM is still busy.
485 */
486bool EEPROMTest::waitForCompletion() {
487 for (int i = 0; i < 200; i++) {
488 if (eeprom->read() & DO) {
489 standby();
490 return true;
491 }
492 // Wait 50 usec;
493 }
494
495 return false;
496}
497
498/**
499 * writeOpAddr - Write an opcode and address
500 * @opCode: operation code
501 * @opCodeBits: number of bits in opCode
502 * @addr: address to write to
503 * @addrBits: number of bits in address
504 **/
505void EEPROMTest::writeOpAddr(int opCode, int opCodeBits, uint16_t addr, int addrBits)
506{
507 shiftOutBits(opCode, opCodeBits);
508 shiftOutBits(addr, addrBits);
509}
510
511// Create text test runner and run all tests.
512int main( int argc, char **argv)
513{
514 CppUnit::TextUi::TestRunner runner;
515 runner.addTest( EEPROMTest::suite() );
516 return runner.run() ? 0 : 1;
517}
518
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette