summaryrefslogtreecommitdiffstats
path: root/src/progs/pickit2v2/base/pickit2v2.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/progs/pickit2v2/base/pickit2v2.cpp')
-rw-r--r--src/progs/pickit2v2/base/pickit2v2.cpp432
1 files changed, 432 insertions, 0 deletions
diff --git a/src/progs/pickit2v2/base/pickit2v2.cpp b/src/progs/pickit2v2/base/pickit2v2.cpp
new file mode 100644
index 0000000..31dd6ca
--- /dev/null
+++ b/src/progs/pickit2v2/base/pickit2v2.cpp
@@ -0,0 +1,432 @@
+/***************************************************************************
+ * Copyright (C) 2007 Nicolas Hadacek <hadacek@kde.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; either version 2 of the License, or *
+ * (at your option) any later version. *
+ ***************************************************************************/
+#include "pickit2v2.h"
+
+#include "progs/base/prog_config.h"
+#include "progs/icd2/base/microchip.h"
+#include "pickit2v2_data.h"
+
+//-----------------------------------------------------------------------------
+const Pickit2V2::FamilyData *Pickit2V2::familyData(const Pic::Data &device)
+{
+ for (uint i=0; FAMILY_DATA[i].architecture!=Pic::Architecture::Nb_Types; i++)
+ if ( FAMILY_DATA[i].architecture==device.architecture() ) return &FAMILY_DATA[i];
+ Q_ASSERT(false);
+ return 0;
+}
+
+//-----------------------------------------------------------------------------
+Pickit2V2::Hardware::Hardware(::Programmer::Base &base)
+ : ::Programmer::PicHardware(base, new USBPort(base), QString::null),
+ _scriptBufferChecksum(0), _deviceSet(false)
+{}
+
+bool Pickit2V2::Hardware::internalConnectHardware()
+{
+ return port().open();
+}
+
+bool Pickit2V2::Hardware::setTarget()
+{
+ //setIcspSpeed(0); // #### ??
+ _fastProgramming = true; // #### ??
+ uint checksum;
+ if ( !getScriptBufferChecksum(checksum) ) return false;
+ if ( !_deviceSet || _scriptBufferChecksum!=checksum ) {
+ if ( !downloadScripts() ) return false;
+ }
+ _deviceSet = true;
+ return true;
+}
+
+bool Pickit2V2::Hardware::readStatus(ushort &status)
+{
+ if ( !port().command(ReadStatus) ) return false;
+ Array a;
+ if ( !port().receive(a) ) return false;
+ status = (a[1] << 8) + a[0];
+ return true;
+}
+
+bool Pickit2V2::Hardware::sendScript(const ushort *script, uint length)
+{
+ Array cmd;
+ cmd[0] = ClearUploadBuffer;
+ cmd[1] = ExecuteScript;
+ cmd[2] = length;
+ for (uint i=0; i<length; i++) cmd[3+i] = script[i];
+ return port().command(cmd);
+}
+
+bool Pickit2V2::Hardware::executeScript(uint i)
+{
+ Q_ASSERT( i!=0 );
+ const ScriptData &sdata = SCRIPT_DATA[i-1];
+ log(Log::DebugLevel::Extra, QString("execute script #%1: %2").arg(i).arg(sdata.name));
+ return sendScript(sdata.data, sdata.length);
+}
+
+bool Pickit2V2::Hardware::getScriptBufferChecksum(uint &checksum)
+{
+ if ( !port().command(ScriptBufferChecksum) ) return false;
+ Array array;
+ if ( !port().receive(array) ) return false;
+ checksum = (array[0] << 24) + (array[1] << 16) + (array[2] << 8) + array[3];
+ log(Log::DebugLevel::Extra, QString("get script buffer checksum: %1").arg(toHexLabel(checksum, 8)));
+ return true;
+}
+
+bool Pickit2V2::Hardware::downloadScript(ScriptType type, uint i)
+{
+ if (i==0 ) return true; // empty script
+ const ScriptData &sdata = SCRIPT_DATA[i-1];
+ log(Log::DebugLevel::Max, QString(" download script #%1 (\"%2\") at position #%3")
+ .arg(i-1).arg(sdata.name).arg(toHexLabel(type, 2)));
+ Array cmd;
+ cmd[0] = DownloadScript;
+ cmd[1] = type;
+ cmd[2] = sdata.length;
+ for (uint k=0; k<sdata.length; k++) {
+ if ( !_fastProgramming && sdata.data[k]==0xAAE7 ) {
+ ushort next = sdata.data[k+1];
+ if ( (next & 0xFF)<170 && next!=0 ) {
+ cmd[3+k] = sdata.data[k];
+ cmd[3+k+1] = next + next/2;
+ } else {
+ cmd[3+k] = DelayLong;
+ cmd[3+k+1] = 2;
+ }
+ k++;
+ } else if ( !_fastProgramming && sdata.data[k]==0xAAE8 ) {
+ ushort next = sdata.data[k+1];
+ if ( (next & 0xFF)<171 && next!=0 ) {
+ cmd[3+k] = sdata.data[k];
+ cmd[3+k+1] = next + next/2;
+ } else {
+ cmd[3+k] = DelayLong;
+ cmd[3+k+1] = 0;
+ }
+ k++;
+ } else cmd[3+k] = sdata.data[k];
+ }
+ return port().command(cmd);
+}
+
+bool Pickit2V2::Hardware::downloadScripts()
+{
+ if ( !port().command(ClearDownloadBuffer) ) return false;
+ log(Log::DebugLevel::Extra, "clear scripts buffer");
+ if ( !port().command(ClearScriptBuffer) ) return false;
+ log(Log::DebugLevel::Extra, "download scripts");
+ const Data &d = data(device().name());
+ for (uint i=0; i<Nb_ScriptTypes; i++) {
+ if ( i==TestMemoryRead || i==ConfigErase ) continue; // ### to get same checksum as pk2cmd
+ if ( !downloadScript(ScriptType(i), d.scriptIndexes[i]) ) return false;
+ }
+ return getScriptBufferChecksum(_scriptBufferChecksum);
+}
+
+bool Pickit2V2::Hardware::setTargetReset(Pic::ResetMode mode)
+{
+ ushort script[1];
+ switch (mode) {
+ case Pic::ResetHeld: script[0] = MclrGroundOn; break;
+ case Pic::ResetReleased: script[0] = MclrGroundOff; break;
+ case Pic::Nb_ResetModes: Q_ASSERT(false); return false;
+ }
+ return sendScript(script, 1);
+}
+
+bool Pickit2V2::Hardware::setVddVoltage(double value, double threshold)
+{
+ ushort v = ushort(qRound(32.0*value + 10.5)) << 6;
+ uchar vfault = uchar(qRound(256.0 * (threshold * value) / 5.0));
+ if ( vfault>210 ) vfault = 210;
+ Array cmd;
+ cmd[0] = SetVdd;
+ cmd[1] = v & 0xFF;
+ cmd[2] = v >> 8;
+ cmd[3] = vfault;
+ return port().command(cmd);
+}
+
+bool Pickit2V2::Hardware::setVppVoltage(double value, double threshold)
+{
+ Array cmd;
+ cmd[0] = SetVpp;
+ cmd[1] = 0x40;
+ cmd[2] = uchar(qRound(18.61*value));
+ cmd[3] = uchar(qRound(18.61*value*threshold));
+ return port().command(cmd);
+}
+
+bool Pickit2V2::Hardware::setVddOn(bool on)
+{
+ log(Log::DebugLevel::Extra, QString("Vdd set to %1, self powered is %2").arg(on).arg(_base.isTargetSelfPowered()));
+ ushort script[2];
+ script[0] = (on ? VddGroundOff : VddOff);
+ if ( _base.isTargetSelfPowered() ) script[1] = (on ? VddOff : VddGroundOff);
+ else script[1] = (on ? VddOn : VddGroundOn);
+ return sendScript(script, 2);
+}
+
+bool Pickit2V2::Hardware::setIcspSpeed(uchar speed)
+{
+ ushort script[2];
+ script[0] = SetIcspSpeed;
+ script[1] = speed;
+ return sendScript(script, 2);
+}
+
+bool Pickit2V2::Hardware::getMode(VersionData &version, ::Programmer::Mode &mode)
+{
+ if ( !port().command(FirmwareVersion) ) return false;
+ Array data;
+ if ( !port().receive(data) ) return false;
+ if ( data[0]=='B' ) {
+ mode = ::Programmer::BootloadMode;
+ version = VersionData();
+ } else {
+ mode = ::Programmer::NormalMode;
+ version = VersionData(data[0], data[1], data[2]);
+ }
+ return true;
+}
+
+bool Pickit2V2::Hardware::readVoltages(VoltagesData &voltagesData)
+{
+ if ( !port().command(ReadVoltages) ) return false;
+ Array array;
+ if ( !port().receive(array) ) return false;
+ double vadc = 256 * array[1] + array[0];
+ voltagesData[Pic::TargetVdd].value = 5.0 * (vadc / 65536);
+ voltagesData[Pic::TargetVdd].error = false;
+ vadc = 256 * array[3] + array[2];
+ voltagesData[Pic::TargetVpp].value = 13.7 * (vadc / 65536);
+ voltagesData[Pic::TargetVpp].error = false;
+ return true;
+}
+
+bool Pickit2V2::Hardware::downloadAddress(Address address)
+{
+ log(Log::DebugLevel::Max, QString("download address %1").arg(toHexLabel(address, 6)));
+ Array cmd;
+ cmd[0] = ClearDownloadBuffer;
+ cmd[1] = DownloadData;
+ cmd[2] = 3;
+ cmd[3] = address.toUInt() & 0xFF;
+ cmd[4] = (address.toUInt() >> 8) & 0xFF;
+ cmd[5] = (address.toUInt() >> 16) & 0xFF;
+ return port().command(cmd);
+}
+
+bool Pickit2V2::Hardware::runScript(ScriptType stype, uint repetitions, uint nbNoLens)
+{
+ log(Log::DebugLevel::Max, QString("run script %1: repetitions=%2 nbNoLen=%3")
+ .arg(toHexLabel(stype, 2)).arg(repetitions).arg(nbNoLens));
+#if 0 // ALTERNATE METHOD
+ const Data &d = data(device().name());
+ for (uint i=0; i<repetitions; i++)
+ if ( !executeScript(d.scriptIndexes[stype]) ) return false;
+ if (nbNoLens) {
+ Array cmd;
+ for (uint i=0; i<nbNoLens; i++) cmd[i] = UploadDataNoLen;
+ if ( !port().command(cmd) ) return false;
+ }
+#else
+ Array cmd;
+ cmd[0] = ClearUploadBuffer;
+ cmd[1] = RunScript;
+ cmd[2] = stype;
+ cmd[3] = repetitions;
+ for (uint i=0; i<nbNoLens; i++) cmd[4+i] = UploadDataNoLen;
+ if ( !port().command(cmd) ) return false;
+#endif
+ if ( stype==ProgExit && !setTargetReset(Pic::ResetReleased) ) return false;
+ return true;
+}
+
+bool Pickit2V2::Hardware::prepareRead(Pic::MemoryRangeType type, uint wordIndex)
+{
+ ScriptType stype = prepareReadScript(type);
+ if ( type!=Pic::MemoryRangeType::Cal && (stype==Nb_ScriptTypes || data(device().name()).scriptIndexes[stype]==0) ) return true;
+ switch (type.type()) {
+ case Pic::MemoryRangeType::Code:
+ if ( !device().architecture().data().hasAddressAccess ) return true;
+ if ( !downloadAddress(0x10000 * (wordIndex / 0x8000)) ) return false;
+ break;
+ case Pic::MemoryRangeType::Eeprom:
+ if ( device().nbBytesWord(Pic::MemoryRangeType::Eeprom)==4 ) {
+ if ( !downloadAddress(device().range(Pic::MemoryRangeType::Eeprom).start.toUInt()) ) return false; // #### correct ?
+ } else if ( !downloadAddress(0x0) ) return false;
+ break;
+ case Pic::MemoryRangeType::Config: return true; // ConfigPrepareRead unused (?)
+ case Pic::MemoryRangeType::UserId: break;
+ case Pic::MemoryRangeType::Cal: {
+ Address start = device().range(Pic::MemoryRangeType::Cal).start;
+ Q_ASSERT( start==device().range(Pic::MemoryRangeType::Code).end+1 );
+ return downloadAddress(start.toUInt());
+ }
+ default: return true;
+ case Pic::MemoryRangeType::Nb_Types: Q_ASSERT(false); return false;
+ }
+ return runScript(stype);
+}
+
+bool Pickit2V2::Hardware::readMemory(Pic::MemoryRangeType otype, Device::Array &data, const ::Programmer::VerifyData *vdata)
+{
+ uint nbWords = device().nbWords(otype);
+ data.resize(nbWords);
+ log(Log::DebugLevel::Max, QString("read %1 nbWords=%2").arg(otype.label()).arg(nbWords));
+ uint nbBytesWord = device().nbBytesWord(otype);
+ // EEPROM is read like regular program memory in midrange
+ if ( !device().is18Family() && !device().is16bitFamily() && otype==Pic::MemoryRangeType::Eeprom ) nbBytesWord = 2;
+ uint wordOffset = 0;
+ Pic::MemoryRangeType type = otype;
+ if ( otype==Pic::MemoryRangeType::Config && device().range(Pic::MemoryRangeType::Config).start==device().range(Pic::MemoryRangeType::Code).end+1 ) { // config in code memory
+ wordOffset = device().range(Pic::MemoryRangeType::Config).start.toUInt();
+ type = Pic::MemoryRangeType::Code;
+ }
+ bool setAddress = true;
+ ScriptType stype = readScript(type);
+ Q_ASSERT( stype!=Nb_ScriptTypes );
+ const FamilyData *fdata = familyData(device());
+ uint nbRunWords = QMIN(UploadBufferNbBytes / nbBytesWord, nbWords);
+ uint nbRuns = 1;
+ uint nbReceive = (nbRunWords*nbBytesWord + 63) / 64;
+ switch (type.type()) {
+ case Pic::MemoryRangeType::Code: nbRuns = nbRunWords / Pickit2V2::data(device().name()).codeMemoryNbReadWords; break;
+ case Pic::MemoryRangeType::Eeprom: nbRuns = nbRunWords / Pickit2V2::data(device().name()).eepromMemoryNbReadWords; break;
+ default: break;
+ }
+
+ if ( !runScript(ProgEntry) ) return false;
+ for (uint i=0; i<nbWords; ) {
+ if (setAddress) {
+ setAddress = false;
+ if ( !prepareRead(type, wordOffset + i) ) return false;
+ }
+ QValueVector<uint> words;
+ if ( type==Pic::MemoryRangeType::Config || type==Pic::MemoryRangeType::Cal ) {
+ if ( !runScript(stype, 1, 0) ) return false;
+ if ( !port().command(UploadData) ) return false;
+ // config memory return includes a length byte that we need to ignore
+ if ( !port().receiveWords(nbBytesWord, nbReceive, words, 1) ) return false;
+ } else {
+ if ( !runScript(stype, nbRuns, nbReceive) ) return false;
+ if ( !port().receiveWords(nbBytesWord, nbReceive, words) ) return false;
+ }
+ log(Log::DebugLevel::Max, QString("nbRunWords=%1 readNbWords=%2 index=%3/%4").arg(nbRunWords).arg(words.count()).arg(i).arg(nbWords));
+ for (uint k=0; k<words.count(); k++) {
+ if ( i>=nbWords ) break;
+ data[i] = words[k];
+ if (fdata->progMemShift) data[i] >>= 1;
+ data[i] = data[i].maskWith(device().mask(type)); // ### correct ?
+ if ( vdata && !verifyWord(i, data[i], type, *vdata) ) return false;
+ if ( type==Pic::MemoryRangeType::Code && i!=0x0 && i%0x8000==0 ) setAddress = true;
+ i++;
+ }
+ }
+ if ( !runScript(ProgExit) ) return false;
+ return true;
+}
+
+bool Pickit2V2::Hardware::eraseAll()
+{
+ const Data &d = data(device().name());
+ if ( d.scriptIndexes[ConfigErase]!=0 ) {
+ if ( !runScript(ProgEntry) ) return false;
+ if ( d.scriptIndexes[ConfigWritePrepare]!=0 ) {
+ if ( !downloadAddress(0) ) return false;
+ if ( !runScript(ConfigWritePrepare) ) return false;
+ }
+ if ( !runScript(ConfigErase) ) return false;
+ if ( !runScript(ProgExit) ) return false;
+ }
+ if ( !runScript(ProgEntry) ) return false;
+ if ( d.scriptIndexes[EraseChipPrepare]!=0 && !runScript(EraseChipPrepare) ) return false;
+ if ( !runScript(ChipErase) ) return false;
+ if ( !runScript(ProgExit) ) return false;
+ return true;
+}
+
+bool Pickit2V2::Hardware::eraseRange(Pic::MemoryRangeType type)
+{
+ if ( type==Pic::MemoryRangeType::Code ) {
+ if ( !runScript(ProgEntry) ) return false;
+ if ( !runScript(ProgMemoryErase) ) return false;
+ if ( !runScript(ProgExit) ) return false;
+ return true;
+ }
+ if ( type==Pic::MemoryRangeType::Eeprom ) {
+ if ( !runScript(ProgEntry) ) return false;
+ if ( !runScript(EepromErase) ) return false;
+ if ( !runScript(ProgExit) ) return false;
+ return true;
+ }
+ Q_ASSERT(false);
+ return false;
+}
+
+bool Pickit2V2::Hardware::prepareWrite(Pic::MemoryRangeType type, uint wordIndex)
+{
+ ScriptType stype = prepareWriteScript(type);
+ if ( stype==Nb_ScriptTypes || data(device().name()).scriptIndexes[stype]==0 ) return true;
+ switch (type.type()) {
+ case Pic::MemoryRangeType::Code:
+ case Pic::MemoryRangeType::Config:
+ if ( !device().architecture().data().hasAddressAccess ) return true;
+ if ( !downloadAddress(0x10000 * (wordIndex / 0x8000)) ) return false;
+ break;
+ case Pic::MemoryRangeType::Eeprom: {
+ Address address = (device().nbBytesWord(Pic::MemoryRangeType::Eeprom)==4 ? device().range(Pic::MemoryRangeType::Eeprom).start : 0x000000);
+ if ( !downloadAddress(address) ) return false;
+ break;
+ }
+ case Pic::MemoryRangeType::UserId: break;
+ default: return true;
+ case Pic::MemoryRangeType::Nb_Types: Q_ASSERT(false); return false;
+ }
+ return runScript(stype);
+}
+
+bool Pickit2V2::Hardware::writeMemory(Pic::MemoryRangeType otype, const Device::Array &data, bool force)
+{
+ Q_UNUSED(force);
+ return false; // ### TODO
+}
+
+//-----------------------------------------------------------------------------
+bool Pickit2V2::DeviceSpecific::canEraseRange(Pic::MemoryRangeType type) const
+{
+ const Data &d = data(device().name());
+ if ( type==Pic::MemoryRangeType::Code ) return d.scriptIndexes[ProgMemoryErase];
+ if ( type==Pic::MemoryRangeType::Eeprom ) return d.scriptIndexes[EepromErase];
+ return false;
+}
+
+bool Pickit2V2::DeviceSpecific::canReadRange(Pic::MemoryRangeType type) const
+{
+ const Data &d = data(device().name());
+ if ( type==Pic::MemoryRangeType::Cal ) return d.scriptIndexes[OsccalRead];
+ return true;
+}
+
+bool Pickit2V2::DeviceSpecific::canWriteRange(Pic::MemoryRangeType type) const
+{
+ const Data &d = data(device().name());
+ if ( type==Pic::MemoryRangeType::Cal ) return d.scriptIndexes[OsccalWrite];
+ return true;
+}
+
+bool Pickit2V2::DeviceSpecific::doWrite(Pic::MemoryRangeType type, const Device::Array &data, bool force)
+{
+ Q_ASSERT( data.size()==device().nbWords(type) );
+ return hardware().writeMemory(type, data, force);
+}