VirtualBox

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

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

Devices/E1000: Made the two testcases build again. bugref:10348

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

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