diff --git a/HardwareVerify/Configure.h b/HardwareVerify/Configure.h index d346fdf..2da2ae6 100644 --- a/HardwareVerify/Configure.h +++ b/HardwareVerify/Configure.h @@ -1,9 +1,4 @@ -// Uncomment only one of the ARDUINO_IS_ lines to use the fast I/O code for -// the data bus, or comment them all out to use the slower bit-at-a-time code. -//#define ARDUINO_IS_MICRO #define ARDUINO_IS_UNO -//#define ARDUINO_IS_NANO - #include "PromDevice28C.h" diff --git a/HardwareVerify/HardwareVerify.ino b/HardwareVerify/HardwareVerify.ino index 405a5e9..bc19cc6 100644 --- a/HardwareVerify/HardwareVerify.ino +++ b/HardwareVerify/HardwareVerify.ino @@ -145,19 +145,20 @@ void setup() Serial.begin(115200); } - - -word start = 0; -word end = 0xff; -byte val = 0xff; - void loop() +{ + commandLoop(); +} + +static void commandLoop() { byte b; word w; - bool error = false; char line[20]; uint32_t numBytes; + unsigned long timeStart; + unsigned long timeEnd; + bool cmdError = false; Serial.print("\n#"); Serial.flush(); @@ -167,12 +168,6 @@ void loop() c |= 0x20; } - /* - * Note that the comamnds here allow for direct writing of the 28C control lines with some exceptions to - * protect the chip and the host arduino: - * 1) When the O command is used to enable chip output, the arduino data bus us set to INPUT - * 2) When the D command is used to write data from the arduino, the chip output is disabled - * 3) The R command sets to output enable (OE) on the chip (but not the chip enable (CE)) */ switch (c) { case 'a': @@ -182,8 +177,9 @@ void loop() prom.setAddress(w); } else - error = true; + cmdError = true; break; + case 'd': if (hexDigit(line[1]) <= 15) { @@ -193,12 +189,18 @@ void loop() prom.writeDataBus(b); } else - error = true; + cmdError = true; break; + case 'c': case 'o': case 'w': - if ((line[1] == 'd') || (line[1] == 'e')) { + if ((line[1] != 'd') && (line[1] != 'e')) + { + cmdError = true; + } + else + { bool enable = line[1] == 'e'; if (c == 'c') if (enable) prom.enableChip(); else prom.disableChip(); @@ -214,10 +216,6 @@ void loop() else prom.disableOutput(); } } - else - { - error = true; - } break; case 'r': @@ -236,19 +234,19 @@ void loop() case 'u': Serial.println(F("Writing the unlock code to disable Software Write Protect mode.")); - unsigned long timeStart = micros(); + timeStart = micros(); prom.disableSoftwareWriteProtect(); - unsigned long timeEnd = micros(); + timeEnd = micros(); Serial.print("Unlock command time in uSec="); Serial.println(timeEnd - timeStart); break; default: - error = true; + cmdError = true; break; } - if (error) { + if (cmdError) { Serial.print(F("Hardware Verifier - ")); Serial.println(prom.getName()); Serial.println(); diff --git a/HardwareVerify/PromAddressDriver.cpp b/HardwareVerify/PromAddressDriver.cpp index 048c190..4ba70c6 100644 --- a/HardwareVerify/PromAddressDriver.cpp +++ b/HardwareVerify/PromAddressDriver.cpp @@ -33,10 +33,10 @@ void PromAddressDriver::setAddress(word address) if (hi != lastHi) { - setAddressRegister(ADDR_CLK_HI, hi); + setAddressRegisterDirect(ADDR_CLK_HI, hi); lastHi = hi; } - setAddressRegister(ADDR_CLK_LO, lo); + setAddressRegisterDirect(ADDR_CLK_LO, lo); } @@ -67,4 +67,38 @@ void PromAddressDriver::setAddressRegister(uint8_t clkPin, byte addr) } } +// Shift an 8-bit value into one of the address shift registers. Note that +// the data pins are tied together, selecting the high or low address register +// is a matter of using the correct clock pin to shift the data in. +void PromAddressDriver::setAddressRegisterDirect(uint8_t clkPin, byte addr) +{ + byte mask = 0; + if (clkPin == A3) + mask = 0x08; + else if (clkPin == A4) + mask = 0x10; + + // Make sure the clock is low to start. + PORTC &= ~mask; + + // Shift 8 bits in, starting with the MSB. + for (int ix = 0; (ix < 8); ix++) + { + // Set the data bit + if (addr & 0x80) + { + PORTC |= 0x20; + } + else + { + PORTC &= 0xdf; + } + + // Toggle the clock high then low + PORTC |= mask; + delayMicroseconds(3); + PORTC &= ~mask; + addr <<= 1; + } +} diff --git a/HardwareVerify/PromAddressDriver.h b/HardwareVerify/PromAddressDriver.h index 918f224..89ec740 100644 --- a/HardwareVerify/PromAddressDriver.h +++ b/HardwareVerify/PromAddressDriver.h @@ -10,6 +10,7 @@ class PromAddressDriver { private: static void setAddressRegister(uint8_t clkPin, byte addr); + static void setAddressRegisterDirect(uint8_t clkPin, byte addr); }; diff --git a/HardwareVerify/PromDevice.cpp b/HardwareVerify/PromDevice.cpp index e85ae31..f85d68c 100644 --- a/HardwareVerify/PromDevice.cpp +++ b/HardwareVerify/PromDevice.cpp @@ -1,155 +1,109 @@ -#include "Configure.h" -#include "PromDevice.h" - - -PromDevice::PromDevice(unsigned long size, word blockSize, unsigned maxWriteTime, bool polling) - : mSize(size), - mBlockSize(blockSize), - mMaxWriteTime(maxWriteTime), - mSupportsDataPoll(polling) -{ -} - - -// Write a block of data to the device. If the device supports block writes, -// the data will be broken into chunks and written using the block mode. -// Otherwise, each byte will be individually written and verified. -bool PromDevice::writeData(byte data[], word len, word address) -{ - bool status = true; - - if (mBlockSize == 0) - { - // Device does not support block writes. - for (word ix = 0; (ix < len); ix++) - { - if (burnByte(data[ix], address + ix) == false) - { - status = false; - break; - } - } - } - else - { - word offset = 0; - word chunkSize; - if (address & (mBlockSize - 1)) - { - // Address does not start on a block boundary. Adjust the size of - // the first block to fit within a single block. - chunkSize = mBlockSize - (address & (mBlockSize - 1)); - chunkSize = (chunkSize > len) ? len : chunkSize; - if (burnBlock(data, chunkSize, address) == false) - { - return false; - } - offset += chunkSize; - len -= chunkSize; - } - - // All writes are now aligned to block boundaries, so write full blocks - // or remaining length, whichever is smaller. - while (len > 0) - { - chunkSize = (len > mBlockSize) ? mBlockSize : len; - if (burnBlock(data + offset, chunkSize, address + offset) == false) - { - status = false; - break; - } - offset += chunkSize; - len -= chunkSize; - } - } - - return status; -} - - -// BEGIN PRIVATE METHODS -// - -// Set the I/O state of the data bus. -// The first two bits of port D are used for serial, so the 8 bits data bus are -// on pins D2..D9. -void PromDevice::setDataBusMode(uint8_t mode) -{ -#if defined(ARDUINO_IS_UNO) || defined(ARDUINO_IS_NANO) - // On the Uno and Nano, D2..D9 maps to the upper 6 bits of port D and the - // lower 2 bits of port B. - if (mode == OUTPUT) - { - DDRB |= 0x03; - DDRD |= 0xfc; - } - else - { - DDRB &= 0xfc; - DDRD &= 0x03; - } -#elif defined(ARDUINO_IS_MICRO) - // On the Micro, D2..D9 maps to the upper 7 bits of port B and the - // lower bit of port D. - if (mode == OUTPUT) - { - DDRB |= 0xfe; - DDRD |= 0x01; - } - else - { - DDRB &= 0x01; - DDRD &= 0xfe; - } -#else - byte bit = 0x01; - for (int pin = 2; (pin <= 9); pin++) { - pinMode(pin, mode); - bit <<= 1; - } -#endif -} - - -// Read a byte from the data bus. The caller must set the bus to input_mode -// before calling this or no useful data will be returned. -byte PromDevice::readDataBus() -{ -#if defined(ARDUINO_IS_UNO) || defined(ARDUINO_IS_NANO) - return (PINB << 6) | (PIND >> 2); -#elif defined(ARDUINO_IS_MICRO) - return (PINB & 0xfe) | (PIND & 0x01); -#else - byte data = 0; - byte bit = 0x01; - for (int pin = 2; (pin <= 9); pin++) { - if (digitalRead(pin) == HIGH) { - data |= bit; - } - bit <<= 1; - } - return data; -#endif -} - - -// Write a byte to the data bus. The caller must set the bus to output_mode -// before calling this or no data will be written. -void PromDevice::writeDataBus(byte data) -{ -#if defined(ARDUINO_IS_UNO) || defined(ARDUINO_IS_NANO) - PORTB = (PORTB & 0xfc) | (data >> 6); - PORTD = (PORTD & 0x03) | (data << 2); -#elif defined(ARDUINO_IS_MICRO) - PORTB = (PORTB & 0x01) | (data & 0xfe); - PORTD = (PORTD & 0xfe) | (data & 0x01); -#else - byte bit = 0x01; - for (int pin = 2; (pin <= 9); pin++) { - digitalWrite(pin, (data & bit) ? HIGH : LOW); - bit <<= 1; - } -#endif -} - - +#include "Configure.h" +#include "PromDevice.h" + + +PromDevice::PromDevice(unsigned long size, word blockSize, unsigned maxWriteTime, bool polling) + : mSize(size), + mBlockSize(blockSize), + mMaxWriteTime(maxWriteTime), + mSupportsDataPoll(polling) +{ +} + + +// Write a block of data to the device. If the device supports block writes, +// the data will be broken into chunks and written using the block mode. +// Otherwise, each byte will be individually written and verified. +bool PromDevice::writeData(byte data[], word len, word address) +{ + bool status = true; + + if (mBlockSize == 0) + { + // Device does not support block writes. + for (word ix = 0; (ix < len); ix++) + { + if (burnByte(data[ix], address + ix) == false) + { + status = false; + break; + } + } + } + else + { + word offset = 0; + word chunkSize; + if (address & (mBlockSize - 1)) + { + // Address does not start on a block boundary. Adjust the size of + // the first block to fit within a single block. + chunkSize = mBlockSize - (address & (mBlockSize - 1)); + chunkSize = (chunkSize > len) ? len : chunkSize; + if (burnBlock(data, chunkSize, address) == false) + { + return false; + } + offset += chunkSize; + len -= chunkSize; + } + + // All writes are now aligned to block boundaries, so write full blocks + // or remaining length, whichever is smaller. + while (len > 0) + { + chunkSize = (len > mBlockSize) ? mBlockSize : len; + if (burnBlock(data + offset, chunkSize, address + offset) == false) + { + status = false; + break; + } + offset += chunkSize; + len -= chunkSize; + } + } + + return status; +} + + +// BEGIN PRIVATE METHODS +// + +// Set the I/O state of the data bus. +// The first two bits of port D are used for serial, so the 8 bits data bus are +// on pins D2..D9. +void PromDevice::setDataBusMode(uint8_t mode) +{ + // On the Uno and Nano, D2..D9 maps to the upper 6 bits of port D and the + // lower 2 bits of port B. + if (mode == OUTPUT) + { + DDRB |= 0x03; + DDRD |= 0xfc; + } + else + { + DDRB &= 0xfc; + DDRD &= 0x03; + } +} + + +// Read a byte from the data bus. The caller must set the bus to input_mode +// before calling this or no useful data will be returned. +byte PromDevice::readDataBus() +{ + return (PINB << 6) | (PIND >> 2); +} + + +// Write a byte to the data bus. The caller must set the bus to output_mode +// before calling this or no data will be written. +void PromDevice::writeDataBus(byte data) +{ + PORTB = (PORTB & 0xfc) | (data >> 6); + PORTD = (PORTD & 0x03) | (data << 2); +} + + diff --git a/HardwareVerify/PromDevice28C.cpp b/HardwareVerify/PromDevice28C.cpp index 737b88c..ffbbff2 100644 --- a/HardwareVerify/PromDevice28C.cpp +++ b/HardwareVerify/PromDevice28C.cpp @@ -204,8 +204,8 @@ void PromDevice28C::setByte(byte value, word address) enableChip(); enableWrite(); delayMicroseconds(1); - disableChip(); disableWrite(); + disableChip(); } diff --git a/HardwareVerify/README.md b/HardwareVerify/README.md index fcaa84d..61e2f3f 100644 --- a/HardwareVerify/README.md +++ b/HardwareVerify/README.md @@ -9,6 +9,7 @@ Note that the comamnds allow for direct writing of the 28C control lines with so * When the O command is used to enable chip output, the arduino data bus is set to INPUT * When the D command is used to write data from the arduino, the chip output is disabled * The R command sets the output enable (OE) on the chip, but not the chip enable (CE) +* The L and U commands reset CE, OE, and WE back to disabled on completion and change the data and address The session below shows how a write fails to a locked chip and then succeeds once the chip is unlocked.