Speed up setAddr code to meet SDP timing restrictions

This commit is contained in:
Tom Nisbet 2019-06-09 20:03:50 -04:00
parent 82acfde3e3
commit ac512a2740
7 changed files with 170 additions and 187 deletions

View File

@ -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_UNO
//#define ARDUINO_IS_NANO
#include "PromDevice28C.h" #include "PromDevice28C.h"

View File

@ -145,19 +145,20 @@ void setup()
Serial.begin(115200); Serial.begin(115200);
} }
word start = 0;
word end = 0xff;
byte val = 0xff;
void loop() void loop()
{
commandLoop();
}
static void commandLoop()
{ {
byte b; byte b;
word w; word w;
bool error = false;
char line[20]; char line[20];
uint32_t numBytes; uint32_t numBytes;
unsigned long timeStart;
unsigned long timeEnd;
bool cmdError = false;
Serial.print("\n#"); Serial.print("\n#");
Serial.flush(); Serial.flush();
@ -167,12 +168,6 @@ void loop()
c |= 0x20; 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) switch (c)
{ {
case 'a': case 'a':
@ -182,8 +177,9 @@ void loop()
prom.setAddress(w); prom.setAddress(w);
} }
else else
error = true; cmdError = true;
break; break;
case 'd': case 'd':
if (hexDigit(line[1]) <= 15) if (hexDigit(line[1]) <= 15)
{ {
@ -193,12 +189,18 @@ void loop()
prom.writeDataBus(b); prom.writeDataBus(b);
} }
else else
error = true; cmdError = true;
break; break;
case 'c': case 'c':
case 'o': case 'o':
case 'w': case 'w':
if ((line[1] == 'd') || (line[1] == 'e')) { if ((line[1] != 'd') && (line[1] != 'e'))
{
cmdError = true;
}
else
{
bool enable = line[1] == 'e'; bool enable = line[1] == 'e';
if (c == 'c') if (c == 'c')
if (enable) prom.enableChip(); else prom.disableChip(); if (enable) prom.enableChip(); else prom.disableChip();
@ -214,10 +216,6 @@ void loop()
else prom.disableOutput(); else prom.disableOutput();
} }
} }
else
{
error = true;
}
break; break;
case 'r': case 'r':
@ -236,19 +234,19 @@ void loop()
case 'u': case 'u':
Serial.println(F("Writing the unlock code to disable Software Write Protect mode.")); Serial.println(F("Writing the unlock code to disable Software Write Protect mode."));
unsigned long timeStart = micros(); timeStart = micros();
prom.disableSoftwareWriteProtect(); prom.disableSoftwareWriteProtect();
unsigned long timeEnd = micros(); timeEnd = micros();
Serial.print("Unlock command time in uSec="); Serial.print("Unlock command time in uSec=");
Serial.println(timeEnd - timeStart); Serial.println(timeEnd - timeStart);
break; break;
default: default:
error = true; cmdError = true;
break; break;
} }
if (error) { if (cmdError) {
Serial.print(F("Hardware Verifier - ")); Serial.print(F("Hardware Verifier - "));
Serial.println(prom.getName()); Serial.println(prom.getName());
Serial.println(); Serial.println();

View File

@ -33,10 +33,10 @@ void PromAddressDriver::setAddress(word address)
if (hi != lastHi) if (hi != lastHi)
{ {
setAddressRegister(ADDR_CLK_HI, hi); setAddressRegisterDirect(ADDR_CLK_HI, hi);
lastHi = 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;
}
}

View File

@ -10,6 +10,7 @@ class PromAddressDriver {
private: private:
static void setAddressRegister(uint8_t clkPin, byte addr); static void setAddressRegister(uint8_t clkPin, byte addr);
static void setAddressRegisterDirect(uint8_t clkPin, byte addr);
}; };

View File

@ -1,155 +1,109 @@
#include "Configure.h" #include "Configure.h"
#include "PromDevice.h" #include "PromDevice.h"
PromDevice::PromDevice(unsigned long size, word blockSize, unsigned maxWriteTime, bool polling) PromDevice::PromDevice(unsigned long size, word blockSize, unsigned maxWriteTime, bool polling)
: mSize(size), : mSize(size),
mBlockSize(blockSize), mBlockSize(blockSize),
mMaxWriteTime(maxWriteTime), mMaxWriteTime(maxWriteTime),
mSupportsDataPoll(polling) mSupportsDataPoll(polling)
{ {
} }
// Write a block of data to the device. If the device supports block writes, // 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. // the data will be broken into chunks and written using the block mode.
// Otherwise, each byte will be individually written and verified. // Otherwise, each byte will be individually written and verified.
bool PromDevice::writeData(byte data[], word len, word address) bool PromDevice::writeData(byte data[], word len, word address)
{ {
bool status = true; bool status = true;
if (mBlockSize == 0) if (mBlockSize == 0)
{ {
// Device does not support block writes. // Device does not support block writes.
for (word ix = 0; (ix < len); ix++) for (word ix = 0; (ix < len); ix++)
{ {
if (burnByte(data[ix], address + ix) == false) if (burnByte(data[ix], address + ix) == false)
{ {
status = false; status = false;
break; break;
} }
} }
} }
else else
{ {
word offset = 0; word offset = 0;
word chunkSize; word chunkSize;
if (address & (mBlockSize - 1)) if (address & (mBlockSize - 1))
{ {
// Address does not start on a block boundary. Adjust the size of // Address does not start on a block boundary. Adjust the size of
// the first block to fit within a single block. // the first block to fit within a single block.
chunkSize = mBlockSize - (address & (mBlockSize - 1)); chunkSize = mBlockSize - (address & (mBlockSize - 1));
chunkSize = (chunkSize > len) ? len : chunkSize; chunkSize = (chunkSize > len) ? len : chunkSize;
if (burnBlock(data, chunkSize, address) == false) if (burnBlock(data, chunkSize, address) == false)
{ {
return false; return false;
} }
offset += chunkSize; offset += chunkSize;
len -= chunkSize; len -= chunkSize;
} }
// All writes are now aligned to block boundaries, so write full blocks // All writes are now aligned to block boundaries, so write full blocks
// or remaining length, whichever is smaller. // or remaining length, whichever is smaller.
while (len > 0) while (len > 0)
{ {
chunkSize = (len > mBlockSize) ? mBlockSize : len; chunkSize = (len > mBlockSize) ? mBlockSize : len;
if (burnBlock(data + offset, chunkSize, address + offset) == false) if (burnBlock(data + offset, chunkSize, address + offset) == false)
{ {
status = false; status = false;
break; break;
} }
offset += chunkSize; offset += chunkSize;
len -= chunkSize; len -= chunkSize;
} }
} }
return status; return status;
} }
// BEGIN PRIVATE METHODS // BEGIN PRIVATE METHODS
// //
// Set the I/O state of the data bus. // 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 // The first two bits of port D are used for serial, so the 8 bits data bus are
// on pins D2..D9. // on pins D2..D9.
void PromDevice::setDataBusMode(uint8_t mode) 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
// On the Uno and Nano, D2..D9 maps to the upper 6 bits of port D and the // lower 2 bits of port B.
// lower 2 bits of port B. if (mode == OUTPUT)
if (mode == OUTPUT) {
{ DDRB |= 0x03;
DDRB |= 0x03; DDRD |= 0xfc;
DDRD |= 0xfc; }
} else
else {
{ DDRB &= 0xfc;
DDRB &= 0xfc; DDRD &= 0x03;
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. // Read a byte from the data bus. The caller must set the bus to input_mode
if (mode == OUTPUT) // before calling this or no useful data will be returned.
{ byte PromDevice::readDataBus()
DDRB |= 0xfe; {
DDRD |= 0x01; return (PINB << 6) | (PIND >> 2);
} }
else
{
DDRB &= 0x01; // Write a byte to the data bus. The caller must set the bus to output_mode
DDRD &= 0xfe; // before calling this or no data will be written.
} void PromDevice::writeDataBus(byte data)
#else {
byte bit = 0x01; PORTB = (PORTB & 0xfc) | (data >> 6);
for (int pin = 2; (pin <= 9); pin++) { PORTD = (PORTD & 0x03) | (data << 2);
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
}

View File

@ -204,8 +204,8 @@ void PromDevice28C::setByte(byte value, word address)
enableChip(); enableChip();
enableWrite(); enableWrite();
delayMicroseconds(1); delayMicroseconds(1);
disableChip();
disableWrite(); disableWrite();
disableChip();
} }

View File

@ -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 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 * 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 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. The session below shows how a write fails to a locked chip and then succeeds once the chip is unlocked.