VirtualBox

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

Last change on this file was 106061, checked in by vboxsync, 8 weeks ago

Copyright year updates by scm.

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