VirtualBox

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

Last change on this file since 98102 was 96407, checked in by vboxsync, 2 years ago

scm copyright and license note update

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