You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
piklab/src/progs/pickit2v2/base/pickit2v2.cpp

433 lines
15 KiB

/***************************************************************************
* 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), TQString()),
_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::readtqStatus(ushort &status)
{
if ( !port().command(ReadtqStatus) ) 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, TQString("execute script #%1: %2").tqarg(i).tqarg(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, TQString("get script buffer checksum: %1").tqarg(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, TQString(" download script #%1 (\"%2\") at position #%3")
.tqarg(i-1).tqarg(sdata.name).tqarg(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(tqRound(32.0*value + 10.5)) << 6;
uchar vfault = uchar(tqRound(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(tqRound(18.61*value));
cmd[3] = uchar(tqRound(18.61*value*threshold));
return port().command(cmd);
}
bool Pickit2V2::Hardware::setVddOn(bool on)
{
log(Log::DebugLevel::Extra, TQString("Vdd set to %1, self powered is %2").tqarg(on).tqarg(_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, TQString("download address %1").tqarg(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, TQString("run script %1: repetitions=%2 nbNoLen=%3")
.tqarg(toHexLabel(stype, 2)).tqarg(repetitions).tqarg(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, TQString("read %1 nbWords=%2").tqarg(otype.label()).tqarg(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 = TQMIN(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;
}
TQValueVector<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, TQString("nbRunWords=%1 readNbWords=%2 index=%3/%4").tqarg(nbRunWords).tqarg(words.count()).tqarg(i).tqarg(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);
}