diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/Network/testcase/tstDevEEPROM.cpp | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/Network/testcase/tstDevEEPROM.cpp')
-rw-r--r-- | src/VBox/Devices/Network/testcase/tstDevEEPROM.cpp | 550 |
1 files changed, 550 insertions, 0 deletions
diff --git a/src/VBox/Devices/Network/testcase/tstDevEEPROM.cpp b/src/VBox/Devices/Network/testcase/tstDevEEPROM.cpp new file mode 100644 index 00000000..148bc7aa --- /dev/null +++ b/src/VBox/Devices/Network/testcase/tstDevEEPROM.cpp @@ -0,0 +1,550 @@ +/* $Id: tstDevEEPROM.cpp $ */ +/** @file + * EEPROM 93C46 unit tests. + */ + +/* + * Copyright (C) 2007-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * SPDX-License-Identifier: GPL-3.0-only + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#ifdef USE_CPPUNIT +# include <cppunit/ui/text/TestRunner.h> +# include <cppunit/extensions/HelperMacros.h> +#else +# include "CppUnitEmulation.h" +#endif +#include <VBox/vmm/pdmdev.h> +#include "../DevEEPROM.h" + + +/********************************************************************************************************************************* +* Global Variables * +*********************************************************************************************************************************/ +static const uint16_t g_abInitialContent[] = +{ + 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f, + 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017, + 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f, + 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027, + 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f, + 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, + 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f +}; + + +/** + * Test fixture for 93C46-compatible EEPROM device emulation. + */ +class EEPROMTest +#ifdef USE_CPPUNIT + : public CppUnit::TestFixture +#endif +{ + CPPUNIT_TEST_SUITE( tstDevEEPROM ); + + CPPUNIT_TEST( testRead ); + CPPUNIT_TEST( testSequentialRead ); + CPPUNIT_TEST( testWrite ); + CPPUNIT_TEST( testWriteAll ); + CPPUNIT_TEST( testWriteDisabled ); + CPPUNIT_TEST( testErase ); + CPPUNIT_TEST( testEraseAll ); + + CPPUNIT_TEST_SUITE_END(); + +private: + enum Wires { DO=8, DI=4, CS=2, SK=0x01 }; + enum OpCodes { + READ_OPCODE = 0x6, + WRITE_OPCODE = 0x5, + ERASE_OPCODE = 0x7, + EWDS_OPCODE = 0x10, // erase/write disable + WRAL_OPCODE = 0x11, // write all + ERAL_OPCODE = 0x12, // erase all + EWEN_OPCODE = 0x13 // erase/write enable + }; + enum BitWidths { + READ_OPCODE_BITS = 3, + WRITE_OPCODE_BITS = 3, + ERASE_OPCODE_BITS = 3, + EWDS_OPCODE_BITS = 5, + WRAL_OPCODE_BITS = 5, + ERAL_OPCODE_BITS = 5, + EWEN_OPCODE_BITS = 5, + READ_ADDR_BITS = 6, + WRITE_ADDR_BITS = 6, + ERASE_ADDR_BITS = 6, + EWDS_ADDR_BITS = 4, + WRAL_ADDR_BITS = 4, + ERAL_ADDR_BITS = 4, + EWEN_ADDR_BITS = 4, + DATA_BITS = 16 + }; + + EEPROM93C46 *eeprom; + + // Helper methods + void shiftOutBits(uint16_t data, uint16_t count); + uint16_t shiftInBits(uint16_t count); + void getReady(); + void standby(); + void stop(); + uint16_t readAt(uint16_t addr); + bool writeTo(uint16_t addr, uint16_t value); + void writeOpAddr(int opCode, int opCodeBits, uint16_t addr, int addrBits); + void writeData(uint16_t value) { shiftOutBits(value, DATA_BITS); } + bool waitForCompletion(); + +public: + void setUp() + { + eeprom = new EEPROM93C46; + eeprom->init(g_abInitialContent); + } + + void tearDown() + { + delete eeprom; + } + + void testSize() + { + CPPUNIT_ASSERT_EQUAL( sizeof(g_abInitialContent), (size_t)EEPROM93C46::SIZE ); + } + + void testRead() + { + getReady(); + for ( uint32_t wordAddr=0; wordAddr < EEPROM93C46::SIZE; wordAddr++ ) { + shiftOutBits(READ_OPCODE, READ_OPCODE_BITS); + shiftOutBits(wordAddr, READ_ADDR_BITS); + + CPPUNIT_ASSERT_EQUAL( g_abInitialContent[wordAddr], (uint16_t)wordAddr ); + CPPUNIT_ASSERT_EQUAL( g_abInitialContent[wordAddr], shiftInBits(DATA_BITS) ); + standby(); + } + stop(); + } + + void testSequentialRead() + { + getReady(); + shiftOutBits(READ_OPCODE, READ_OPCODE_BITS); + shiftOutBits(0, READ_ADDR_BITS); + for ( int wordAddr=0; wordAddr < EEPROM93C46::SIZE; wordAddr++ ) { + CPPUNIT_ASSERT_EQUAL( g_abInitialContent[wordAddr], shiftInBits(DATA_BITS) ); + } + stop(); + } + + void testWrite() + { + //unused: int i; + uint16_t wordAddr; + + getReady(); + // Enable write + writeOpAddr(EWEN_OPCODE, EWEN_OPCODE_BITS, 0, EWEN_ADDR_BITS); + standby(); + + for ( wordAddr=0; wordAddr < EEPROM93C46::SIZE; wordAddr++ ) { + //writeOpAddr(WRITE_OPCODE, WRITE_OPCODE_BITS, (uint16_t)wordAddr, WRITE_ADDR_BITS); + writeTo(wordAddr, 0x3F00 - (wordAddr<<8)); + standby(); + + if (!waitForCompletion()) { + CPPUNIT_FAIL("EEPROM write was not completed"); + stop(); + return; + } + standby(); + } + + // Disable write + writeOpAddr(EWDS_OPCODE, EWDS_OPCODE_BITS, 0, EWDS_ADDR_BITS); + + stop(); + + // Now check the result + getReady(); + writeOpAddr(READ_OPCODE, READ_OPCODE_BITS, 0, READ_ADDR_BITS); + for ( wordAddr=0; wordAddr < EEPROM93C46::SIZE; wordAddr++ ) { + CPPUNIT_ASSERT_EQUAL((uint16_t)(0x3F00 - (wordAddr<<8)), shiftInBits(DATA_BITS) ); + } + stop(); + } + + void testWriteDisabled() + { + getReady(); + + uint16_t addr = 0; + uint16_t oldValue = readAt(addr); + stop(); + getReady(); + if (writeTo(addr, ~oldValue)) { + // Write appears to be successful -- continue + CPPUNIT_ASSERT_EQUAL(oldValue, readAt(addr)); + } + else { + CPPUNIT_FAIL("EEPROM write was not completed"); + } + stop(); + } + + void testErase() + { + int i; + uint16_t addr = 0x1F; + + getReady(); + // Enable write + shiftOutBits(EWEN_OPCODE, EWEN_OPCODE_BITS); + shiftOutBits(0, EWEN_ADDR_BITS); + standby(); + + if (writeTo(addr, addr)) { + stop(); + getReady(); + // Write successful -- continue + CPPUNIT_ASSERT_EQUAL(addr, readAt(addr)); + stop(); + getReady(); + + shiftOutBits(ERASE_OPCODE, ERASE_OPCODE_BITS); + shiftOutBits(addr, ERASE_ADDR_BITS); + + standby(); + + for (i = 0; i < 200; i++) { + if (eeprom->read() & DO) + break; + //usec_delay(50); + } + + if (i == 200) { + CPPUNIT_FAIL("EEPROM erase was not completed"); + stop(); + return; + } + + standby(); + + shiftOutBits(EWDS_OPCODE, EWDS_OPCODE_BITS); + shiftOutBits(0, EWDS_ADDR_BITS); + + stop(); + getReady(); + CPPUNIT_ASSERT_EQUAL((uint16_t)0xFFFF, readAt(addr)); + } + else { + CPPUNIT_FAIL("EEPROM write was not completed"); + } + stop(); + } + + void testWriteAll() + { + uint16_t addr; + + getReady(); + // Enable write + writeOpAddr(EWEN_OPCODE, EWEN_OPCODE_BITS, 0, EWEN_ADDR_BITS); + standby(); + // Fill all memory + writeOpAddr(WRAL_OPCODE, WRAL_OPCODE_BITS, 0, WRAL_ADDR_BITS); + writeData(0xABBA); + standby(); + + if (waitForCompletion()) { + stop(); + getReady(); + // Write successful -- verify all memory + for ( addr=0; addr < EEPROM93C46::SIZE; addr++ ) { + CPPUNIT_ASSERT_EQUAL((uint16_t)0xABBA, readAt(addr)); + } + } + else { + CPPUNIT_FAIL("EEPROM write was not completed"); + } + stop(); + } + + void testEraseAll() + { + //unused: int i; + uint16_t addr = 0x1F; + + getReady(); + // Enable write + writeOpAddr(EWEN_OPCODE, EWEN_OPCODE_BITS, 0, EWEN_ADDR_BITS); + standby(); + // Fill all memory + writeOpAddr(WRITE_OPCODE, WRITE_OPCODE_BITS, addr, WRITE_ADDR_BITS); + writeData(0); + standby(); + + if (waitForCompletion()) { + stop(); + getReady(); + // Write successful -- verify random location + CPPUNIT_ASSERT_EQUAL((uint16_t)0, readAt(addr)); + stop(); + getReady(); + + writeOpAddr(ERAL_OPCODE, ERAL_OPCODE_BITS, addr, ERAL_ADDR_BITS); + standby(); + + if (!waitForCompletion()) { + CPPUNIT_FAIL("EEPROM erase was not completed"); + stop(); + return; + } + + standby(); + + writeOpAddr(EWDS_OPCODE, EWDS_OPCODE_BITS, 0, EWDS_ADDR_BITS); + stop(); + + getReady(); + for ( addr=0; addr < EEPROM93C46::SIZE; addr++ ) { + CPPUNIT_ASSERT_EQUAL((uint16_t)0xFFFF, readAt(addr)); + } + } + else { + CPPUNIT_FAIL("EEPROM write was not completed"); + } + stop(); + } +}; + +/** + * shiftOutBits - Shift data bits our to the EEPROM + * @hw: pointer to the EEPROM object + * @data: data to send to the EEPROM + * @count: number of bits to shift out + * + * We need to shift 'count' bits out to the EEPROM. So, the value in the + * "data" parameter will be shifted out to the EEPROM one bit at a time. + * In order to do this, "data" must be broken down into bits. + **/ +void EEPROMTest::shiftOutBits(uint16_t data, uint16_t count) { + uint32_t wires = eeprom->read(); + uint32_t mask; + + mask = 0x01 << (count - 1); + wires &= ~DO; + + do { + wires &= ~DI; + + if (data & mask) + wires |= DI; + + eeprom->write(wires); + + // Raise clock + eeprom->write(wires |= SK); + // Lower clock + eeprom->write(wires &= ~SK); + + mask >>= 1; + } while (mask); + + wires &= ~DI; + eeprom->write(wires); +} + +/** + * shiftInBits - Shift data bits in from the EEPROM + * @count: number of bits to shift in + * + * In order to read a register from the EEPROM, we need to shift 'count' bits + * in from the EEPROM. Bits are "shifted in" by raising the clock input to + * the EEPROM (setting the SK bit), and then reading the value of the data out + * "DO" bit. During this "shifting in" process the data in "DI" bit should + * always be clear. + **/ +uint16_t EEPROMTest::shiftInBits(uint16_t count) +{ + uint32_t wires; + uint32_t i; + uint16_t data; + + wires = eeprom->read(); + + wires &= ~(DO | DI); + data = 0; + + for (i = 0; i < count; i++) { + data <<= 1; + // Raise clock + eeprom->write(wires |= SK); + + wires = eeprom->read(); + + wires &= ~DI; + if (wires & DO) + data |= 1; + + // Lower clock + eeprom->write(wires &= ~SK); + } + + return data; +} + +/** + * getReady - Prepares EEPROM for read/write + * + * Setups the EEPROM for reading and writing. + **/ +void EEPROMTest::getReady() +{ + unsigned wires = eeprom->read(); + /* Clear SK and DI */ + eeprom->write(wires &= ~(DI | SK)); + /* Set CS */ + eeprom->write(wires | CS); +} + +/** + * standby - Return EEPROM to standby state + * + * Return the EEPROM to a standby state. + **/ +void EEPROMTest::standby() +{ + unsigned wires = eeprom->read(); + + eeprom->write(wires &= ~(CS | SK)); + + // Raise clock + eeprom->write(wires |= SK); + + // Select EEPROM + eeprom->write(wires |= CS); + + // Lower clock + eeprom->write(wires &= ~SK); +} + +/** + * stop - Terminate EEPROM command + * + * Terminates the current command by inverting the EEPROM's chip select pin. + **/ +void EEPROMTest::stop() +{ + unsigned wires = eeprom->read(); + + eeprom->write(wires &= ~(CS | DI)); + // Raise clock + eeprom->write(wires |= SK); + // Lower clock + eeprom->write(wires &= ~SK); +} + +/** + * readAt - Read a word at specified address + * @addr: address to read + * + * Returns the value of the word specified in 'addr' parameter. + **/ +uint16_t EEPROMTest::readAt(uint16_t addr) +{ + getReady(); + shiftOutBits(READ_OPCODE, READ_OPCODE_BITS); + shiftOutBits(addr, READ_ADDR_BITS); + + uint16_t value = shiftInBits(DATA_BITS); + stop(); + + return value; +} + +/** + * writeTo - Write a word to specified address + * @addr: address to write to + * @value: value to store + * + * Returns false if write did not complete. + * + * Note: Make sure EEPROM is selected and writable before attempting + * to write. Use getReady() and stop() to select/deselect + * EEPROM. + **/ +bool EEPROMTest::writeTo(uint16_t addr, uint16_t value) +{ + writeOpAddr(WRITE_OPCODE, WRITE_OPCODE_BITS, addr, WRITE_ADDR_BITS); + writeData(value); + standby(); + return waitForCompletion(); +} + + +/** + * waitForCompletion - Wait until EEPROM clears the busy bit + * + * Returns false if the EEPROM is still busy. + */ +bool EEPROMTest::waitForCompletion() { + for (int i = 0; i < 200; i++) { + if (eeprom->read() & DO) { + standby(); + return true; + } + // Wait 50 usec; + } + + return false; +} + +/** + * writeOpAddr - Write an opcode and address + * @opCode: operation code + * @opCodeBits: number of bits in opCode + * @addr: address to write to + * @addrBits: number of bits in address + **/ +void EEPROMTest::writeOpAddr(int opCode, int opCodeBits, uint16_t addr, int addrBits) +{ + shiftOutBits(opCode, opCodeBits); + shiftOutBits(addr, addrBits); +} + +int main() +{ +#ifdef USE_CPPUNIT + CppUnit::TextUi::TestRunner runner; + runner.addTest( EEPROMTest::suite() ); + return runner.run() ? 0 : 1; +#else + EEPROMTest Test; + return Test.run(); +#endif +} + |