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.
427 lines
16 KiB
427 lines
16 KiB
/***************************************************************************
|
|
* Copyright (C) 2005 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 "pic.h"
|
|
|
|
#include "common/global/global.h"
|
|
#include "common/common/misc.h"
|
|
#include "common/global/purl.h"
|
|
#include "pic_register.h"
|
|
#include "pic_config.h"
|
|
|
|
//------------------------------------------------------------------------------
|
|
const Pic::ProgVoltageType::Data Pic::ProgVoltageType::DATA[Nb_Types] = {
|
|
{ "vpp", 0 },
|
|
{ "vdd_prog", 0 },
|
|
{ "vdd_prog_write", 0 }
|
|
};
|
|
|
|
const Pic::MemoryRangeType::Data Pic::MemoryRangeType::DATA[Nb_Types] = {
|
|
{ "code", I18N_NOOP("Code memory"), Writable },
|
|
{ "calibration", I18N_NOOP("Calibration"), Writable },
|
|
{ "user_ids", I18N_NOOP("User IDs"), Writable },
|
|
{ "device_id", I18N_NOOP("Device ID"), ReadOnly },
|
|
{ "config", I18N_NOOP("Configuration Bits"), Writable },
|
|
{ "eeprom", I18N_NOOP("Data EEPROM"), Writable },
|
|
{ "debug_vector", I18N_NOOP("Debug Vector"), Writable },
|
|
{ "hardware_stack", I18N_NOOP("Hardware Stack"), ReadOnly },
|
|
{ "calibration_backup", I18N_NOOP("Calibration Backup"), Writable },
|
|
{ "program_executive", I18N_NOOP("Program Executive"), Writable }
|
|
};
|
|
|
|
const Pic::SelfWrite::Data Pic::SelfWrite::DATA[Nb_Types] = {
|
|
{ "yes", 0 },
|
|
{ "no", 0 }
|
|
};
|
|
|
|
const Pic::DeviceType::Data Pic::DeviceType::DATA[Nb_Types] = {
|
|
{ 0, I18N_NOOP("Normal") },
|
|
{ 0, I18N_NOOP("J") },
|
|
{ 0, I18N_NOOP("K") }
|
|
};
|
|
|
|
const Pic::Architecture::Data Pic::Architecture::DATA[Nb_Types] = {
|
|
// name family_label nbBytesPC nbBytesWord packed nbBitsRegister registerBankLength
|
|
// {Code, Cal, UserID, DevId, Conf, EEPROM, DebugVec, HardStack, CalBackup, Program Executive} randomAccess
|
|
{ "10X", I18N_NOOP("Baseline Family"), 0, 2, false, 8, 0x020, { 12, 12, 12, 12, 12, 8, 12, 0, 12, 0 }, false, SelfWrite::No, DeviceType::Normal }, // 9, 10, 11 or 12-bit program counter
|
|
{ "16X", I18N_NOOP("Midrange Family"), 13, 2, false, 8, 0x080, { 14, 14, 14, 14, 14, 8, 14, 0, 14, 0 }, false, SelfWrite::Nb_Types, DeviceType::Normal }, // max eeprom: 256 words
|
|
{ "17C", I18N_NOOP("17C Family"), 16, 2, false, 8, 0x100, { 16, 0, 0, 0, 16, 8, 0, 0, 0, 0 }, true, SelfWrite::No, DeviceType::Normal },
|
|
{ "18C", I18N_NOOP("18C Family"), 21, 2, true, 8, 0x100, { 16, 8, 8, 8, 8, 8, 16, 0, 8, 0 }, true, SelfWrite::No, DeviceType::Normal },
|
|
{ "18F", I18N_NOOP("18F Family"), 21, 2, true, 8, 0x100, { 16, 8, 8, 8, 8, 8, 16, 0, 8, 0 }, true, SelfWrite::Nb_Types, DeviceType::Normal },
|
|
{ "18J", I18N_NOOP("18J Family"), 21, 2, true, 8, 0x100, { 16, 8, 8, 8, 8, 8, 16, 0, 8, 0 }, true, SelfWrite::Yes, DeviceType::J },
|
|
{ "24F", I18N_NOOP("24F Family"), 23, 4, false, 16, 0x800, { 24, 0, 0, 16, 24, 0, 24, 0, 0, 24 }, true, SelfWrite::Yes, DeviceType::J },
|
|
{ "24H", I18N_NOOP("24H Family"), 23, 4, false, 16, 0x800, { 24, 0, 8, 16, 8, 0, 24, 0, 0, 24 }, true, SelfWrite::Yes, DeviceType::J },
|
|
{ "30F", I18N_NOOP("30F Family"), 23, 4, false, 16, 0xA00, { 24, 0, 24, 16, 16, 16, 24, 0, 0, 24 }, true, SelfWrite::Yes, DeviceType::Normal }, // dsPIC: eeprom max = 2 kwords = 4 kbytes
|
|
{ "33F", I18N_NOOP("33F Family"), 23, 4, false, 16, 0x800, { 24, 0, 8, 16, 8, 0, 24, 0, 0, 24 }, true, SelfWrite::Yes, DeviceType::J }
|
|
};
|
|
|
|
const Pic::Checksum::Algorithm::Data Pic::Checksum::Algorithm::DATA[Nb_Types] = {
|
|
{ "", 0 },
|
|
{ "XOR4", 0 },
|
|
{ "XNOR7", 0 },
|
|
{ "XNOR8", 0 }
|
|
};
|
|
|
|
const Pic::Feature::Data Pic::Feature::DATA[Nb_Types] = {
|
|
{ "ccp", I18N_NOOP("CCP") },
|
|
{ "adc", I18N_NOOP("ADC") },
|
|
{ "ssp", I18N_NOOP("SSP") },
|
|
{ "lvd", I18N_NOOP("Low Voltage Detect") },
|
|
{ "usb", I18N_NOOP("USB") },
|
|
{ "usart", I18N_NOOP("USART") },
|
|
{ "can", I18N_NOOP("CAN") },
|
|
{ "ecan", I18N_NOOP("ECAN") },
|
|
{ "ethernet", I18N_NOOP("Ethernet") },
|
|
{ "lcd", I18N_NOOP("LCD") },
|
|
{ "motor_control", I18N_NOOP("Motor Control") },
|
|
{ "motion_feedback", I18N_NOOP("Motion Feeback") },
|
|
{ "self_write", I18N_NOOP("Self-Write") }
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
Pic::Data::Data()
|
|
: Device::Data(new RegistersData(*this))
|
|
{
|
|
FOR_EACH(ProgVoltageType, type) {
|
|
_voltages[type].min = 0.0;
|
|
_voltages[type].max = 0.0;
|
|
_voltages[type].nominal = 0.0;
|
|
}
|
|
FOR_EACH(MemoryRangeType, type) {
|
|
_ranges[type].properties = NotPresent;
|
|
_ranges[type].start = 0;
|
|
_ranges[type].end = 0;
|
|
_ranges[type].hexFileOffset = 0;
|
|
}
|
|
_config = new Config(*this);
|
|
_calibration.opcode = 0;
|
|
_calibration.opcodeMask = 0;
|
|
}
|
|
|
|
Pic::Data::~Data()
|
|
{
|
|
delete _config;
|
|
}
|
|
|
|
bool Pic::Data::isReadable(MemoryRangeType type) const
|
|
{
|
|
return ( range(type).properties & Programmable );
|
|
}
|
|
|
|
bool Pic::Data::isWritable(MemoryRangeType type) const
|
|
{
|
|
return ( (type.data().properties & Writable) && (range(type).properties & Programmable) );
|
|
}
|
|
|
|
uint Pic::Data::addressIncrement(MemoryRangeType type) const
|
|
{
|
|
uint inc = _architecture.data().nbBytesWord;
|
|
if ( _architecture.data().packed
|
|
&& ( type==MemoryRangeType::Code || type==MemoryRangeType::DebugVector ) ) return inc;
|
|
return inc / 2;
|
|
}
|
|
|
|
uint Pic::Data::nbWords(MemoryRangeType type) const
|
|
{
|
|
if ( !isPresent(type) ) return 0;
|
|
return nbAddresses(type) / addressIncrement(type);
|
|
}
|
|
|
|
uint Pic::Data::nbAddresses(MemoryRangeType type) const
|
|
{
|
|
if ( !isPresent(type) ) return 0;
|
|
return (range(type).end - range(type).start + 1);
|
|
}
|
|
|
|
TQString Pic::Data::fname(Device::Special special) const
|
|
{
|
|
TQString s = name();
|
|
switch (special.type()) {
|
|
case Device::Special::Normal: break;
|
|
case Device::Special::LowPower:
|
|
// assume name is of form "NNX..."
|
|
s.insert(2, 'L');
|
|
break;
|
|
case Device::Special::LowVoltage:
|
|
// assume name is of form "NNXN..."
|
|
s.replace(2, 1, "LV");
|
|
break;
|
|
case Device::Special::HighVoltage:
|
|
// assume name is of form "NNXN..."
|
|
s.replace(2, 1, "HV");
|
|
break;
|
|
case Device::Special::Nb_Types: Q_ASSERT(false); break;
|
|
}
|
|
return s;
|
|
}
|
|
|
|
bool Pic::Data::matchId(BitValue rawId, Device::IdData &idata) const
|
|
{
|
|
if ( !isPresent(MemoryRangeType::DeviceId) ) return false;
|
|
TQMap<Device::Special, BitValue>::const_iterator it;
|
|
for (it=_ids.begin(); it!=_ids.end(); ++it) {
|
|
idata.special = it.key();
|
|
BitValue nid = 0x0;
|
|
switch (architecture().type()) {
|
|
case Architecture::P10X:
|
|
case Architecture::P16X:
|
|
case Architecture::P17C:
|
|
case Architecture::P18C:
|
|
case Architecture::P18F:
|
|
case Architecture::P18J:
|
|
nid = rawId.clearMaskBits(0x1F);
|
|
idata.revision = rawId.maskWith(0x1F);
|
|
break;
|
|
case Architecture::P24F:
|
|
nid = (rawId >> 16).maskWith(0x3FFF);
|
|
idata.revision = (rawId >> 6).maskWith(0x7);
|
|
idata.minorRevision = rawId.maskWith(0x7);
|
|
break;
|
|
case Architecture::P30F:
|
|
nid = (rawId >> 16).maskWith(0xFFFF);
|
|
idata.revision = (rawId >> 6).maskWith(0x3F);
|
|
idata.minorRevision = rawId.maskWith(0x3F);
|
|
idata.process = (rawId >> 12).maskWith(0xF);
|
|
break;
|
|
case Architecture::P24H:
|
|
case Architecture::P33F:
|
|
nid = (rawId >> 16).maskWith(0xFFFF);
|
|
idata.revision = rawId.maskWith(0xFFFF); // ??
|
|
break;
|
|
case Architecture::Nb_Types: Q_ASSERT(false); break;
|
|
}
|
|
if ( nid==it.data() ) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
TQStringList Pic::Data::idNames(const TQMap<TQString, Device::IdData> &ids) const
|
|
{
|
|
TQStringList list;
|
|
TQMap<TQString, Device::IdData>::const_iterator it;
|
|
for (it=ids.begin(); it!=ids.end(); ++it) {
|
|
switch (_architecture.type()) {
|
|
case Architecture::P10X:
|
|
case Architecture::P16X:
|
|
case Architecture::P17C:
|
|
case Architecture::P18C:
|
|
case Architecture::P18F:
|
|
case Architecture::P18J:
|
|
list += i18n("%1 (rev. %2)").tqarg(it.key()).tqarg(toLabel(it.data().revision));
|
|
break;
|
|
case Architecture::P24F:
|
|
list += i18n("%1 (rev. %2.%3)").tqarg(it.key()).tqarg(toLabel(it.data().revision)).tqarg(toLabel(it.data().minorRevision));
|
|
break;
|
|
case Architecture::P30F:
|
|
list += i18n("%1 (proc. %2; rev. %3.%4)").tqarg(it.key()).tqarg(toLabel(it.data().process)).tqarg(toLabel(it.data().revision)).tqarg(toLabel(it.data().minorRevision));
|
|
break;
|
|
case Architecture::P24H:
|
|
case Architecture::P33F:
|
|
list += i18n("%1 (rev. %2)").tqarg(it.key()).tqarg(toLabel(it.data().revision));
|
|
break;
|
|
case Architecture::Nb_Types: Q_ASSERT(false); break;
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
bool Pic::Data::checkCalibration(const Device::Array &data, TQString *message) const
|
|
{
|
|
Q_ASSERT( nbWords(MemoryRangeType::Cal)==data.count() );
|
|
for (uint i=0; i<data.count(); i++) {
|
|
TQString address = toHexLabel(range(MemoryRangeType::Cal).start + i*addressIncrement(MemoryRangeType::Cal), nbCharsAddress());
|
|
if ( data[i]==mask(MemoryRangeType::Cal) ) {
|
|
if (message) *message = i18n("Calibration word at address %1 is blank.").tqarg(address);
|
|
return false;
|
|
}
|
|
}
|
|
if ( data.count()==1 ) {
|
|
if ( data[0].maskWith(_calibration.opcodeMask)!=_calibration.opcode ) {
|
|
if (message) *message = i18n("Calibration word is not a compatible opcode (%2).")
|
|
.tqarg(toHexLabel(_calibration.opcode, nbCharsWord(MemoryRangeType::Code)));
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const Pic::RegistersData &Pic::Data::registersData() const
|
|
{
|
|
return static_cast<const RegistersData &>(*_registersData);
|
|
}
|
|
|
|
bool Pic::Data::hasFeature(Feature feature, bool *unknown) const
|
|
{
|
|
bool ok = ( registersData().nbBanks!=0 );
|
|
if (unknown) *unknown = !ok;
|
|
if (!ok) return false;
|
|
switch (feature.type()) {
|
|
case Feature::CCP: return registersData().sfrs.contains("CCP1CON");
|
|
case Feature::ADC: return registersData().sfrs.contains("ADCON0");
|
|
case Feature::SSP: return registersData().sfrs.contains("SSPCON");
|
|
case Feature::LVD: return registersData().sfrs.contains("LVDCON");
|
|
case Feature::USB: return registersData().sfrs.contains("UCON");
|
|
case Feature::USART:
|
|
return ( registersData().sfrs.contains("TXSTA") // 16F
|
|
|| registersData().sfrs.contains("TXSTA1") // 18F
|
|
|| registersData().sfrs.contains("U1MODE") ); // 30F
|
|
case Feature::CAN: return registersData().sfrs.contains("CANCON") && !registersData().sfrs.contains("ECANCON");
|
|
case Feature::ECAN: return registersData().sfrs.contains("ECANCON");
|
|
case Feature::Ethernet: return registersData().sfrs.contains("ETHCON1");
|
|
case Feature::LCD: return registersData().sfrs.contains("LCDCON");
|
|
case Feature::MotorControl: return registersData().sfrs.contains("PWMCON0");
|
|
case Feature::MotionFeedback: return registersData().sfrs.contains("CAP1CON");
|
|
case Feature::SelfWrite: return _selfWrite==SelfWrite::Yes;
|
|
case Feature::Nb_Types: Q_ASSERT(false); break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
Device::Array Pic::Data::gotoInstruction(Address address, bool withPageSelection) const
|
|
{
|
|
Q_ASSERT( address<addressIncrement(MemoryRangeType::Code)*nbWords(MemoryRangeType::Code) );
|
|
Device::Array a;
|
|
switch (_architecture.type()) {
|
|
case Architecture::P10X:
|
|
if ( nbWords(MemoryRangeType::Code)>0x1FF && withPageSelection)
|
|
a.append(0x4A3 | (address>0x1FF ? 0x100 : 0x000)); // bsf STATUS,PA0 or bcf STATUS,PA0
|
|
a.append(0xA00 | (address.toUInt() & 0x1FF)); // goto
|
|
break;
|
|
case Architecture::P16X:
|
|
if ( nbWords(MemoryRangeType::Code)>0x7FF && withPageSelection ) {
|
|
if ( address<=0x7FF ) a.append(0x018A); // clrf PCLATH
|
|
else {
|
|
a.append(0x3000 | (address.toUInt() >> 8)); // movl high address
|
|
a.append(0x008A); // movwf PCLATH
|
|
}
|
|
}
|
|
a.append(0x2800 | (address.toUInt() & 0x7FF));
|
|
break;
|
|
case Architecture::P17C:
|
|
a.append(0xC000 | (address.toUInt() & 0x1FFF));
|
|
break;
|
|
case Architecture::P18C:
|
|
case Architecture::P18F:
|
|
case Architecture::P18J:
|
|
a.append(0xEF00 | ((address.toUInt()/2) & 0xFF));
|
|
a.append(0xF000 | ((address.toUInt()/2) >> 8));
|
|
break;
|
|
case Architecture::P24F:
|
|
case Architecture::P24H:
|
|
case Architecture::P30F:
|
|
case Architecture::P33F:
|
|
a.append(0x040000 | (address.toUInt() & 0x00FFFE));
|
|
a.append(0X000000 | (address.toUInt() >> 16));
|
|
break;
|
|
case Architecture::Nb_Types: Q_ASSERT(false); break;
|
|
}
|
|
return a;
|
|
}
|
|
|
|
bool Pic::Data::isGotoInstruction(BitValue instruction) const
|
|
{
|
|
switch (_architecture.type()) {
|
|
case Architecture::P10X: return ( instruction.maskWith(0xE00)==0xA00 );
|
|
case Architecture::P16X: return ( instruction.maskWith(0xF800)==0x2800 );
|
|
case Architecture::P17C: return ( instruction.maskWith(0xE000)==0xC000 );
|
|
case Architecture::P18C:
|
|
case Architecture::P18F:
|
|
case Architecture::P18J: return ( instruction.maskWith(0xFF00)==0xEF00 );
|
|
case Architecture::P24F:
|
|
case Architecture::P24H:
|
|
case Architecture::P30F:
|
|
case Architecture::P33F: return ( instruction.maskWith(0xFF0000)==0x040000 );
|
|
case Architecture::Nb_Types: Q_ASSERT(false); break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint Pic::Data::nbWordsWriteAlignment(MemoryRangeType type) const
|
|
{
|
|
if ( type!=MemoryRangeType::Code ) return 1;
|
|
return TQMAX(_nbWordsCodeWrite, uint(16));
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
TQDataStream &operator <<(TQDataStream &s, const Pic::VoltageData &vd)
|
|
{
|
|
s << vd.min << vd.max << vd.nominal;
|
|
return s;
|
|
}
|
|
TQDataStream &operator >>(TQDataStream &s, Pic::VoltageData &vd)
|
|
{
|
|
s >> vd.min >> vd.max >> vd.nominal;
|
|
return s;
|
|
}
|
|
|
|
TQDataStream &operator <<(TQDataStream &s, const Pic::MemoryRangeData &mrd)
|
|
{
|
|
s << TQ_UINT8(mrd.properties) << mrd.start << mrd.end << mrd.hexFileOffset;
|
|
return s;
|
|
}
|
|
TQDataStream &operator >>(TQDataStream &s, Pic::MemoryRangeData &mrd)
|
|
{
|
|
TQ_UINT8 properties;
|
|
s >> properties >> mrd.start >> mrd.end >> mrd.hexFileOffset;
|
|
mrd.properties = Pic::MemoryRangeProperties(properties);
|
|
return s;
|
|
}
|
|
|
|
TQDataStream &operator <<(TQDataStream &s, const Pic::Checksum::Data &cd)
|
|
{
|
|
s << cd.constant << cd.bbsize << cd.algorithm << cd.protectedMaskNames;
|
|
s << cd.blankChecksum << cd.checkChecksum;
|
|
return s;
|
|
}
|
|
TQDataStream &operator >>(TQDataStream &s, Pic::Checksum::Data &cd)
|
|
{
|
|
s >> cd.constant >> cd.bbsize >> cd.algorithm >> cd.protectedMaskNames;
|
|
s >> cd.blankChecksum >> cd.checkChecksum;
|
|
return s;
|
|
}
|
|
|
|
TQDataStream &operator <<(TQDataStream &s, const Pic::CalibrationData &cd)
|
|
{
|
|
s << cd.opcode << cd.opcodeMask;
|
|
return s;
|
|
}
|
|
TQDataStream &operator >>(TQDataStream &s, Pic::CalibrationData &cd)
|
|
{
|
|
s >> cd.opcode >> cd.opcodeMask;
|
|
return s;
|
|
}
|
|
|
|
TQDataStream &Pic::operator <<(TQDataStream &s, const Pic::Data &data)
|
|
{
|
|
s << static_cast<const Device::Data &>(data);
|
|
s << data._architecture << data._ids << data._nbBitsPC;
|
|
s << data._voltages << data._ranges;
|
|
s << data._userIdRecommendedMask;
|
|
s << *data._config;
|
|
s << data._checksums;
|
|
s << data._calibration;
|
|
s << static_cast<const Pic::RegistersData &>(*data._registersData);
|
|
s << data._nbWordsCodeWrite << data._nbWordsCodeRowErase;
|
|
s << data._selfWrite;
|
|
return s;
|
|
}
|
|
TQDataStream &Pic::operator >>(TQDataStream &s, Pic::Data &data)
|
|
{
|
|
s >> static_cast<Device::Data &>(data);
|
|
s >> data._architecture >> data._ids >> data._nbBitsPC;
|
|
s >> data._voltages >> data._ranges;
|
|
s >> data._userIdRecommendedMask;
|
|
s >> *data._config;
|
|
s >> data._checksums;
|
|
s >> data._calibration;
|
|
s >> static_cast<Pic::RegistersData &>(*data._registersData);
|
|
s >> data._nbWordsCodeWrite >> data._nbWordsCodeRowErase;
|
|
s >> data._selfWrite;
|
|
return s;
|
|
}
|