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.
719 lines
34 KiB
719 lines
34 KiB
/***************************************************************************
|
|
* Copyright (C) 2005-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 <tqfile.h>
|
|
#include <tqregexp.h>
|
|
|
|
#include "xml_to_data/device_xml_to_data.h"
|
|
#include "common/common/misc.h"
|
|
#include "devices/pic/base/pic_config.h"
|
|
#include "devices/pic/base/pic_register.h"
|
|
|
|
namespace Pic
|
|
{
|
|
class XmlToData : public Device::XmlToData<Data>
|
|
{
|
|
private:
|
|
virtual TQString namespaceName() const { return "Pic"; }
|
|
|
|
bool getVoltages(ProgVoltageType type, TQDomElement element)
|
|
{
|
|
TQDomElement voltages = findUniqueElement(element, "voltages", "name", type.key());
|
|
if ( voltages.isNull() ) return false;
|
|
bool ok1, ok2, ok3;
|
|
data()->_voltages[type].min = voltages.attribute("min").toDouble(&ok1);
|
|
data()->_voltages[type].max = voltages.attribute("max").toDouble(&ok2);
|
|
data()->_voltages[type].nominal = voltages.attribute("nominal").toDouble(&ok3);
|
|
if ( !ok1 || !ok2 || !ok3 ) qFatal(TQString("Cannot extract voltage value for \"%1\"").tqarg(type.key()));
|
|
if ( data()->_voltages[type].min>data()->_voltages[type].max
|
|
|| data()->_voltages[type].nominal<data()->_voltages[type].min
|
|
|| data()->_voltages[type].nominal>data()->_voltages[type].max )
|
|
qFatal("Inconsistent voltages order");
|
|
return true;
|
|
}
|
|
|
|
bool getMemoryRange(MemoryRangeType type, TQDomElement element)
|
|
{
|
|
TQDomElement range = findUniqueElement(element, "memory", "name", type.key());
|
|
if ( range.isNull() ) return false;
|
|
data()->_ranges[type].properties = Present;
|
|
bool ok;
|
|
uint nbCharsAddress = data()->nbCharsAddress();
|
|
data()->_ranges[type].start = fromHexLabel(range.attribute("start"), nbCharsAddress, &ok);
|
|
if ( !ok ) qFatal("Cannot extract start address");
|
|
data()->_ranges[type].end = fromHexLabel(range.attribute("end"), nbCharsAddress, &ok);
|
|
if ( !ok ) qFatal("Cannot extract end address");
|
|
if ( data()->_ranges[type].end<data()->_ranges[type].start ) qFatal("Memory range end is before its start");
|
|
uint nbCharsWord = data()->nbCharsWord(type);
|
|
if ( data()->nbBitsWord(type)==0 ) qFatal(TQString("Architecture doesn't contain memory range %1").tqarg(type.key()));
|
|
if ( type==MemoryRangeType::UserId ) {
|
|
data()->_userIdRecommendedMask = fromHexLabel(range.attribute("rmask"), nbCharsWord, &ok);
|
|
if ( !ok ) qFatal("Cannot extract rmask value for user id");
|
|
if ( !data()->_userIdRecommendedMask.isInside(data()->mask(type)) ) qFatal(TQString("rmask is not inside mask %1 (%2)").tqarg(toHexLabel(data()->_userIdRecommendedMask, 8)).tqarg(toHexLabel(data()->mask(type), 8)));
|
|
}
|
|
if ( range.attribute("hexfile_offset")!="?" ) {
|
|
data()->_ranges[type].properties |= Programmable;
|
|
if ( !range.attribute("hexfile_offset").isEmpty() ) {
|
|
data()->_ranges[type].hexFileOffset = fromHexLabel(range.attribute("hexfile_offset"), nbCharsAddress, &ok);
|
|
if ( !ok ) qFatal("Cannot extract hexfile_offset");
|
|
}
|
|
}
|
|
if ( type==MemoryRangeType::Cal && !data()->is18Family() ) {
|
|
data()->_calibration.opcodeMask = fromHexLabel(range.attribute("cal_opmask"), nbCharsWord, &ok);
|
|
if ( !ok ) qFatal("Cannot extract calibration opcode mask");
|
|
data()->_calibration.opcode = fromHexLabel(range.attribute("cal_opcode"), nbCharsWord, &ok);
|
|
if ( !ok ) qFatal("Cannot extract calibration opcode");
|
|
if ( !data()->_calibration.opcode.isInside(data()->_calibration.opcodeMask) ) qFatal("Calibration opcode should be inside opcode mask");
|
|
if ( !data()->_calibration.opcodeMask.isInside(data()->mask(type)) ) qFatal("Calibration mask should be inside opcode mask");
|
|
}
|
|
TQString wwa = range.attribute("word_write_align");
|
|
TQString wea = range.attribute("word_erase_align");
|
|
if ( type==MemoryRangeType::Code ) {
|
|
if ( data()->_architecture==Architecture::P18F || data()->_architecture==Architecture::P18J ) {
|
|
data()->_nbWordsCodeWrite = wwa.toUInt(&ok);
|
|
if ( !ok || data()->_nbWordsCodeWrite==0 || (data()->_nbWordsCodeWrite%4)!=0 ) qFatal("Missing or malformed word write align");
|
|
data()->_nbWordsCodeRowErase = wea.toUInt(&ok);
|
|
if ( !ok || (data()->_nbWordsCodeRowErase%4)!=0 ) qFatal("Missing or malformed word erase align");
|
|
} else {
|
|
if ( !wwa.isEmpty() || !wea.isEmpty() ) qFatal("word align should not be defined for this device family/subfamily");
|
|
data()->_nbWordsCodeWrite = 0; // #### TODO
|
|
data()->_nbWordsCodeRowErase = 0; // #### TODO
|
|
}
|
|
} else if ( !wwa.isEmpty() || !wea.isEmpty() ) qFatal("word align should not be defined for this memory range");
|
|
return true;
|
|
}
|
|
|
|
bool hasValue(const Pic::Config::Mask &mask, BitValue value)
|
|
{
|
|
for (uint i=0; i<uint(mask.values.count()); i++)
|
|
if ( mask.values[i].value==value ) return true;
|
|
return false;
|
|
}
|
|
|
|
void processName(const Pic::Config::Mask &cmask, BitValue pmask, Pic::Config::Value &cvalue)
|
|
{
|
|
TQStringList &cnames = cvalue.configNames[Pic::ConfigNameType::Default];
|
|
if ( cvalue.name=="invalid" ) {
|
|
cvalue.name = TQString();
|
|
if ( !cnames.isEmpty() ) qFatal(TQString("No cname should be defined for invalid value in mask %1").tqarg(cmask.name));
|
|
return;
|
|
}
|
|
if ( cvalue.name.isEmpty() ) qFatal(TQString("Empty value name in mask %1").tqarg(cmask.name));
|
|
if ( cmask.value.isInside(pmask) ) { // protected bits
|
|
if ( !cnames.isEmpty() ) qFatal(TQString("Config name should be null for protected config mask \"%1\"").tqarg(cmask.name));
|
|
} else {
|
|
if ( cnames.isEmpty() && cmask.name!="BSSEC" && cmask.name!="BSSIZ" && cmask.name!="SSSEC" && cmask.name!="SSSIZ" ) {
|
|
// ### FIXME: 18J 24H 30F1010/202X
|
|
if ( data()->architecture()!=Pic::Architecture::P18J && data()->architecture()!=Pic::Architecture::P24H
|
|
&& data()->architecture()!=Pic::Architecture::P24F && data()->architecture()!=Pic::Architecture::P33F
|
|
&& data()->name()!="30F1010" && data()->name()!="30F2020" && data()->name()!="30F2023" )
|
|
qFatal(TQString("cname not defined for \"%1\" (%2)").tqarg(cvalue.name).tqarg(cmask.name));
|
|
}
|
|
if ( cnames.count()==1 && cnames[0]=="_" ) cnames.clear();
|
|
for (uint i=0; i<uint(cnames.count()); i++) {
|
|
if ( cnames[i].startsWith("0x") ) {
|
|
if ( cnames.count()!=1 ) qFatal("Hex cname cannot be combined");
|
|
bool ok;
|
|
BitValue v = fromHexLabel(cnames[i], &ok);
|
|
uint nbChars = data()->nbCharsWord(MemoryRangeType::Config);
|
|
BitValue mask = cmask.value.complementInMask(maxValue(NumberBase::Hex, nbChars));
|
|
if ( ok && v==(mask | cvalue.value) ) continue;
|
|
} else if ( XOR(cnames[i].startsWith("_"), data()->architecture()==Pic::Architecture::P30F) ) continue;
|
|
qFatal(TQString("Invalid config name for \"%1\"/\"%2\"").tqarg(cmask.name).tqarg(cvalue.name));
|
|
}
|
|
TQStringList &ecnames = cvalue.configNames[Pic::ConfigNameType::Extra];
|
|
for (uint i=0; i<uint(ecnames.count()); i++)
|
|
if ( ecnames[i][0]!='_' ) qFatal(TQString("Invalid extra config name for %1").tqarg(cvalue.name));
|
|
}
|
|
}
|
|
|
|
Pic::Config::Mask toConfigMask(TQDomElement mask, BitValue pmask)
|
|
{
|
|
uint nbChars = data()->nbCharsWord(MemoryRangeType::Config);
|
|
bool ok;
|
|
TQString defName;
|
|
TQMap<Pic::ConfigNameType, TQStringList> defConfigNames;
|
|
Config::Mask cmask;
|
|
cmask.name = mask.attribute("name");
|
|
if ( !Config::hasMaskName(cmask.name) ) qFatal(TQString("Unknown mask name %1").tqarg(cmask.name));
|
|
cmask.value = fromHexLabel(mask.attribute("value"), nbChars, &ok);
|
|
if ( !ok || cmask.value==0 || cmask.value>data()->mask(MemoryRangeType::Config) )
|
|
qFatal(TQString("Malformed mask value in mask %1").tqarg(mask.attribute("name")));
|
|
//TQStringList names;
|
|
TQDomNode child = mask.firstChild();
|
|
while ( !child.isNull() ) {
|
|
TQDomElement value = child.toElement();
|
|
child = child.nextSibling();
|
|
if ( value.isNull() ) continue;
|
|
if ( value.nodeName()!="value" ) qFatal(TQString("Non value child in mask %1").tqarg(cmask.name));
|
|
if ( value.attribute("value")=="default" ) {
|
|
if ( !defName.isEmpty() ) qFatal(TQString("Default value already defined for mask %1").tqarg(cmask.name));
|
|
defName = value.attribute("name");
|
|
//if ( names.contains(defName) ) qFatal(TQString("Value name duplicated in mask %1").tqarg(cmask.name));
|
|
//names.append(defName);
|
|
FOR_EACH(Pic::ConfigNameType, type) defConfigNames[type] = TQStringList::split(' ', value.attribute(type.data().key));
|
|
continue;
|
|
}
|
|
Config::Value cvalue;
|
|
cvalue.value = fromHexLabel(value.attribute("value"), nbChars, &ok);
|
|
if ( !ok || !cvalue.value.isInside(cmask.value) ) qFatal(TQString("Malformed value in mask %1").tqarg(cmask.name));
|
|
cvalue.name = value.attribute("name");
|
|
//if ( names.contains(cvalue.name) ) qFatal(TQString("Value name duplicated in mask %1").tqarg(cmask.name));
|
|
//names.append(cvalue.name);
|
|
FOR_EACH(Pic::ConfigNameType, type) cvalue.configNames[type] = TQStringList::split(' ', value.attribute(type.data().key));
|
|
processName(cmask, pmask, cvalue);
|
|
cmask.values.append(cvalue);
|
|
}
|
|
// add default values
|
|
if ( !defName.isEmpty() ) {
|
|
uint nb = 0;
|
|
BitValue::const_iterator it;
|
|
for (it=cmask.value.begin(); it!=cmask.value.end(); ++it) {
|
|
if ( hasValue(cmask, *it) ) continue; // already set
|
|
nb++;
|
|
Config::Value cvalue;
|
|
cvalue.value = *it;
|
|
cvalue.name = defName;
|
|
cvalue.configNames = defConfigNames;
|
|
processName(cmask, pmask, cvalue);
|
|
cmask.values.append(cvalue);
|
|
}
|
|
if ( nb<=1 ) qFatal(TQString("Default value used less than twice in mask %1").tqarg(cmask.name));
|
|
}
|
|
qHeapSort(cmask.values);
|
|
return cmask;
|
|
}
|
|
|
|
Pic::Config::Word toConfigWord(TQDomElement config)
|
|
{
|
|
uint nbChars = data()->nbCharsWord(MemoryRangeType::Config);
|
|
Config::Word cword;
|
|
cword.name = config.attribute("name");
|
|
if ( cword.name.isNull() ) qFatal("Config word name not specified.");
|
|
bool ok;
|
|
cword.wmask = fromHexLabel(config.attribute("wmask"), nbChars, &ok);
|
|
BitValue gmask = data()->mask(MemoryRangeType::Config);
|
|
if ( !ok || cword.wmask>gmask ) qFatal(TQString("Missing or malformed config wmask \"%1\"").tqarg(config.attribute("wmask")));
|
|
cword.bvalue = fromHexLabel(config.attribute("bvalue"), nbChars, &ok);
|
|
if ( !ok ) qFatal(TQString("Missing or malformed config bvalue \"%1\"").tqarg(config.attribute("bvalue")));
|
|
if ( config.attribute("pmask").isEmpty() ) cword.pmask = 0;
|
|
else {
|
|
bool ok;
|
|
cword.pmask = fromHexLabel(config.attribute("pmask"), nbChars, &ok);
|
|
if ( !ok || cword.pmask>gmask ) qFatal("Missing or malformed config pmask");
|
|
}
|
|
cword.ignoredCNames = TQStringList::split(' ', config.attribute("icnames"));
|
|
for (uint i=0; i<uint(cword.ignoredCNames.count()); i++)
|
|
if ( cword.ignoredCNames[i][0]!='_' ) qFatal(TQString("Invalid ignored config name for %1").tqarg(cword.name));
|
|
TQDomNode child = config.firstChild();
|
|
while ( !child.isNull() ) {
|
|
TQDomElement mask = child.toElement();
|
|
child = child.nextSibling();
|
|
if ( mask.isNull() ) continue;
|
|
if ( mask.nodeName()!="mask" ) qFatal(TQString("Non mask child in config %1").tqarg(cword.name));
|
|
if ( mask.attribute("name").isEmpty() ) qFatal(TQString("Empty mask name in config %1").tqarg(cword.name));
|
|
Config::Mask cmask = toConfigMask(mask, cword.pmask);
|
|
if ( !cmask.value.isInside(gmask) ) qFatal(TQString("Mask value not inside mask in config %1").tqarg(cword.name));
|
|
for (uint i=0; i<uint(cword.masks.count()); i++) {
|
|
if ( cword.masks[i].name==cmask.name ) qFatal(TQString("Duplicated mask name %1 in config %2").tqarg(cmask.name).tqarg(cword.name));
|
|
if ( cmask.value.isOverlapping(cword.masks[i].value) ) qFatal(TQString("Overlapping masks in config %1").tqarg(cword.name));
|
|
}
|
|
cword.masks.append(cmask);
|
|
}
|
|
qHeapSort(cword.masks);
|
|
BitValue mask = (cword.usedMask() | cword.bvalue).clearMaskBits(cword.pmask);
|
|
if ( config.attribute("cmask").isEmpty() ) {
|
|
if ( data()->_architecture==Pic::Architecture::P30F ) cword.cmask = cword.wmask;
|
|
else cword.cmask = mask;
|
|
} else {
|
|
bool ok;
|
|
cword.cmask = fromHexLabel(config.attribute("cmask"), nbChars, &ok);
|
|
if ( !ok || cword.cmask>gmask ) qFatal("Missing or malformed config cmask");
|
|
//if ( data()->_architecture==Pic::Architecture::P30X &&cword.cmask==cword.wmask ) qFatal(TQString("Redundant cmask in %1").tqarg(cword.name));
|
|
if ( cword.cmask==mask ) qFatal(TQString("Redundant cmask in %1").tqarg(cword.name));
|
|
}
|
|
if ( !cword.pmask.isInside(cword.usedMask()) ) qFatal("pmask should be inside or'ed mask values.");
|
|
return cword;
|
|
}
|
|
|
|
TQValueVector<Pic::Config::Word> getConfigWords(TQDomElement element)
|
|
{
|
|
uint nbWords = data()->nbWords(MemoryRangeType::Config);
|
|
TQValueVector<Config::Word> configWords(nbWords);
|
|
TQDomNode child = element.firstChild();
|
|
while ( !child.isNull() ) {
|
|
TQDomElement config = child.toElement();
|
|
child = child.nextSibling();
|
|
if ( config.isNull() || config.nodeName()!="config" ) continue;
|
|
bool ok;
|
|
uint offset = fromHexLabel(config.attribute("offset"), 1, &ok);
|
|
if ( !ok ) qFatal("Missing or malformed config offset");
|
|
if ( (offset % data()->addressIncrement(MemoryRangeType::Config))!=0 ) qFatal("Config offset not aligned");
|
|
offset /= data()->addressIncrement(MemoryRangeType::Config);
|
|
if ( offset>=nbWords ) qFatal(TQString("Offset too big %1/%2").tqarg(offset).tqarg(nbWords));
|
|
if ( !configWords[offset].name.isNull() ) qFatal(TQString("Config offset %1 is duplicated").tqarg(offset));
|
|
for (uint i=0; i<nbWords; i++) {
|
|
if ( !configWords[i].name.isNull() && configWords[i].name==config.attribute("name") )
|
|
qFatal(TQString("Duplicated config name %1").tqarg(configWords[i].name));
|
|
}
|
|
configWords[offset] = toConfigWord(config);
|
|
}
|
|
return configWords;
|
|
}
|
|
|
|
TQString getChecksumData(TQDomElement checksum)
|
|
{
|
|
Checksum::Data cdata;
|
|
cdata.blankChecksum = 0x0;
|
|
cdata.checkChecksum = 0x0;
|
|
|
|
const Protection &protection = data()->_config->protection();
|
|
TQString valueName;
|
|
if ( protection.family()==Protection::BlockProtection ) {
|
|
valueName = checksum.attribute("protected_blocks");
|
|
bool ok;
|
|
uint nb = valueName.toUInt(&ok);
|
|
uint max = (protection.hasBootBlock() ? 1 : 0) + protection.nbBlocks();
|
|
if ( !ok || nb>max ) qFatal("Invalid number of protected blocks for checksum");
|
|
if ( nb>0 ) cdata.protectedMaskNames += "CPB";
|
|
for (uint i=1; i<nb; i++) cdata.protectedMaskNames += "CP_" + TQString::number(i-1);
|
|
cdata.bbsize = checksum.attribute("bbsize");
|
|
const Config::Mask *mask = data()->_config->findMask(protection.bootSizeMaskName());
|
|
if ( mask==0 ) {
|
|
if ( !cdata.bbsize.isEmpty() ) qFatal("Device does not have a variable boot size (no \"bbsize\" allowed in checksum)");
|
|
} else if ( cdata.bbsize.isEmpty() ) {
|
|
if ( nb==1 ) qFatal("\"bbsize\" should be define in checksum for \"protected_blocks\"==1");
|
|
} else {
|
|
const Config::Value *value = data()->_config->findValue(protection.bootSizeMaskName(), cdata.bbsize);
|
|
if ( value==0 ) qFatal("Invalid \"bbsize\" in checksum");
|
|
valueName += "_" + cdata.bbsize;
|
|
}
|
|
} else {
|
|
valueName = checksum.attribute("protected");
|
|
if ( protection.family()==Protection::NoProtection && !valueName.isEmpty() )
|
|
qFatal("Checksum protected attribute for device with no code protection");
|
|
}
|
|
if ( data()->_checksums.contains(valueName) ) qFatal("Duplicate checksum protected range");
|
|
|
|
TQString s = checksum.attribute("constant");
|
|
if ( s.isEmpty() ) cdata.constant = 0x0000;
|
|
else {
|
|
bool ok;
|
|
cdata.constant = fromHexLabel(s, 4, &ok);
|
|
if ( !ok ) qFatal("Malformed checksum constant");
|
|
}
|
|
|
|
s = checksum.attribute("type");
|
|
if ( s.isEmpty() ) cdata.algorithm = Checksum::Algorithm::Normal;
|
|
else {
|
|
cdata.algorithm = Checksum::Algorithm::fromKey(s);
|
|
if ( cdata.algorithm==Checksum::Algorithm::Nb_Types ) qFatal("Unrecognized checksum algorithm");
|
|
}
|
|
|
|
s = checksum.attribute("mprotected");
|
|
if ( !s.isEmpty() ) {
|
|
TQStringList list = TQStringList::split(" ", s);
|
|
for (uint i=0; i<uint(list.count()); i++) {
|
|
const Config::Mask *mask = data()->config().findMask(list[i]);
|
|
if ( mask==0 ) qFatal(TQString("Not valid mask name for \"protected\" tag in checksum: %1").tqarg(list[i]));
|
|
if ( mask->values.count()==2 ) continue;
|
|
for (uint k=0; k<uint(mask->values.count()); k++) {
|
|
TQString valueName = mask->values[k].name;
|
|
if ( valueName.isEmpty() ) continue;
|
|
if ( !protection.isNoneProtectedValueName(valueName) && !protection.isAllProtectedValueName(valueName) )
|
|
qFatal(TQString("Not switch protection from mask name for \"protected\" tag in checksum: %1").tqarg(list[i]));
|
|
}
|
|
}
|
|
cdata.protectedMaskNames = list;
|
|
}
|
|
|
|
s = checksum.attribute("bchecksum");
|
|
if ( s.isEmpty() ) qFatal("No blank checksum");
|
|
else {
|
|
bool ok;
|
|
cdata.blankChecksum = fromHexLabel(s, 4, &ok);
|
|
if ( !ok ) qFatal("Malformed blank checksum");
|
|
}
|
|
|
|
s = checksum.attribute("cchecksum");
|
|
if ( s.isEmpty() ) qFatal("No check checksum");
|
|
else {
|
|
bool ok;
|
|
cdata.checkChecksum = fromHexLabel(s, 4, &ok);
|
|
if ( !ok ) qFatal("Malformed check checksum");
|
|
}
|
|
|
|
data()->_checksums[valueName] = cdata;
|
|
return valueName;
|
|
}
|
|
|
|
virtual void processDevice(TQDomElement device)
|
|
{
|
|
Device::XmlToDataBase::processDevice(device);
|
|
|
|
TQString arch = device.attribute("architecture");
|
|
data()->_architecture = Architecture::fromKey(arch);
|
|
if ( data()->_architecture==Architecture::Nb_Types ) qFatal(TQString("Unrecognized architecture \"%1\"").tqarg(arch));
|
|
if ( (data()->_architecture==Architecture::P18F && data()->_name.contains("C"))
|
|
|| (data()->_architecture==Architecture::P18F && data()->_name.contains("J")) ) qFatal("Not matching family");
|
|
|
|
bool ok;
|
|
TQString pc = device.attribute("pc");
|
|
data()->_nbBitsPC = data()->_architecture.data().nbBitsPC;
|
|
if ( data()->_nbBitsPC==0 ) {
|
|
data()->_nbBitsPC = pc.toUInt(&ok);
|
|
if ( !ok || data()->_nbBitsPC==0 ) qFatal("Malformed or missing PC");
|
|
} else if ( !pc.isEmpty() ) qFatal("No PC should be provided for this device architecture");
|
|
|
|
TQString sw = device.attribute("self_write");
|
|
data()->_selfWrite = (data()->_memoryTechnology!=Device::MemoryTechnology::Flash ? SelfWrite::No : data()->_architecture.data().selfWrite);
|
|
if ( data()->_selfWrite==SelfWrite::Nb_Types ) {
|
|
data()->_selfWrite = SelfWrite::fromKey(sw);
|
|
if ( data()->_selfWrite==SelfWrite::Nb_Types ) qFatal("Malformed or missing self-write field");
|
|
} else if ( !sw.isEmpty() ) qFatal("Self-write is set for the whole family or non-flash device");
|
|
|
|
// device ids
|
|
FOR_EACH(Device::Special, special) {
|
|
TQString key = "id" + (special==Device::Special::Normal ? TQString() : TQString("_") + special.key());
|
|
TQString id = device.attribute(key);
|
|
if ( id.isEmpty() ) {
|
|
if ( special==Device::Special::Normal ) data()->_ids[special] = 0x0000;
|
|
} else {
|
|
data()->_ids[special] = fromHexLabel(id, 4, &ok);
|
|
if ( !ok ) qFatal("Malformed id");
|
|
}
|
|
}
|
|
|
|
// voltages
|
|
TQStringList names;
|
|
FOR_EACH(ProgVoltageType, vtype) {
|
|
names += vtype.key();
|
|
if ( !getVoltages(vtype, device) ) {
|
|
switch (vtype.type()) {
|
|
case ProgVoltageType::Vpp:
|
|
case ProgVoltageType::VddBulkErase: qFatal(TQString("Voltage \"%1\" not defined").tqarg(vtype.key()));
|
|
case ProgVoltageType::VddWrite: data()->_voltages[ProgVoltageType::VddWrite] = data()->_voltages[ProgVoltageType::VddBulkErase]; break;
|
|
case ProgVoltageType::Nb_Types: Q_ASSERT(false); break;
|
|
}
|
|
}
|
|
}
|
|
//if ( data()->vddMin()>data()->_voltages[ProgVoltageType::VddWrite].min ) qFatal("Vdd min higher than VddWrite min");
|
|
//if ( data()->vddMax()<data()->_voltages[ProgVoltageType::VddWrite].max ) qFatal("Vdd max lower than VddWrite max");
|
|
if ( data()->_voltages[ProgVoltageType::VddWrite].min>data()->_voltages[ProgVoltageType::VddBulkErase].min ) qFatal("VddWrite min higher than VddBulkErase min");
|
|
if ( data()->_voltages[ProgVoltageType::VddWrite].max<data()->_voltages[ProgVoltageType::VddBulkErase].max ) qFatal("VddWrite max lower than VddBulkErase max");
|
|
checkTagNames(device, "voltages", names);
|
|
|
|
// memory ranges
|
|
names.clear();
|
|
FOR_EACH(MemoryRangeType, i) {
|
|
names += i.key();
|
|
if ( !getMemoryRange(i, device) ) continue;
|
|
if ( !(data()->_ranges[i].properties & Programmable) ) continue;
|
|
for(MemoryRangeType k; k<i; ++k) {
|
|
if ( !(data()->_ranges[k].properties & Present)
|
|
|| !(data()->_ranges[k].properties & Programmable) ) continue;
|
|
if ( i==MemoryRangeType::DebugVector
|
|
&& k==MemoryRangeType::ProgramExecutive ) continue;
|
|
if ( k==MemoryRangeType::DebugVector
|
|
&& i==MemoryRangeType::ProgramExecutive ) continue;
|
|
Address start1 = data()->_ranges[k].start + data()->_ranges[k].hexFileOffset;
|
|
Address end1 = data()->_ranges[k].end + data()->_ranges[k].hexFileOffset;
|
|
Address start2 = data()->_ranges[i].start + data()->_ranges[i].hexFileOffset;
|
|
Address end2 = data()->_ranges[i].end + data()->_ranges[i].hexFileOffset;
|
|
if ( end1>=start2 && start1<=end2 )
|
|
qFatal(TQString("Overlapping memory ranges (%1 and %2)").tqarg(k.key()).tqarg(i.key()));
|
|
}
|
|
}
|
|
checkTagNames(device, "memory", names);
|
|
if ( XOR(data()->_ids[Device::Special::Normal]!=0x0000, (data()->_ranges[MemoryRangeType::DeviceId].properties & Present)) )
|
|
qFatal("Id present and device id memory range absent or the opposite");
|
|
|
|
// config words
|
|
TQValueVector<Config::Word> cwords = getConfigWords(device);
|
|
uint nbWords = data()->nbWords(MemoryRangeType::Config);
|
|
data()->_config->_words.resize(nbWords);
|
|
FOR_EACH(Pic::ConfigNameType, type) {
|
|
TQMap<TQString, TQString> cnames; // cname -> mask name
|
|
for (uint i=0; i<nbWords; i++) {
|
|
if ( cwords[i].name.isNull() ) qFatal(TQString("Config word #%1 not defined").tqarg(i));
|
|
data()->_config->_words[i] = cwords[i];
|
|
const Config::Word &word = data()->_config->_words[i];
|
|
for (uint j=0; j<uint(word.masks.count()); j++) {
|
|
const Config::Mask &mask = word.masks[j];
|
|
for (uint k=0; k<uint(mask.values.count()); k++) {
|
|
const TQStringList &vcnames = mask.values[k].configNames[type];
|
|
for (uint l=0; l<uint(vcnames.count()); l++) {
|
|
if ( vcnames[l].startsWith("0x") ) continue;
|
|
if ( cnames.contains(vcnames[l]) && cnames[vcnames[l]]!=mask.name )
|
|
qFatal(TQString("Duplicated config name for %1/%2").tqarg(mask.name).tqarg(mask.values[k].name));
|
|
cnames[vcnames[l]] = word.masks[j].name;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// check validity of value names
|
|
for (uint i=0; i<nbWords; i++) {
|
|
const Config::Word &word = data()->_config->_words[i];
|
|
for (uint j=0; j<uint(word.masks.count()); j++) {
|
|
const Config::Mask &mask = word.masks[j];
|
|
for (uint k=0; k<uint(mask.values.count()); k++) {
|
|
const Config::Value &value = mask.values[k];
|
|
if ( !value.isValid() ) continue;
|
|
if ( !data()->_config->checkValueName(mask.name, value.name) )
|
|
qFatal(TQString("Malformed value name \"%1\" in mask %2").tqarg(value.name).tqarg(mask.name));
|
|
}
|
|
}
|
|
}
|
|
// check if all values are explicit
|
|
for (uint i=0; i<nbWords; i++) {
|
|
const Config::Word &word = data()->_config->_words[i];
|
|
for (uint j=0; j<uint(word.masks.count()); j++) {
|
|
const Config::Mask &mask = word.masks[j];
|
|
BitValue::const_iterator it;
|
|
for (it=mask.value.begin(); it!=mask.value.end(); ++it)
|
|
if ( !hasValue(mask, *it) ) qFatal(TQString("Value %1 not defined in mask %2").tqarg(toHexLabel(*it, data()->nbCharsWord(MemoryRangeType::Config))).tqarg(mask.name));
|
|
}
|
|
}
|
|
|
|
// checksums (after config bits!)
|
|
TQDomElement checksums = findUniqueElement(device, "checksums", TQString(), TQString());
|
|
if ( checksums.isNull() ) {
|
|
// qFatal("No checksum defined"); // #### FIXME
|
|
} else {
|
|
TQMap<TQString, bool> valueNames;
|
|
const Pic::Protection &protection = data()->_config->protection();
|
|
if ( protection.family()==Protection::BasicProtection ) {
|
|
TQString maskName = protection.maskName(Protection::ProgramProtected, MemoryRangeType::Code);
|
|
const Pic::Config::Mask *mask = data()->_config->findMask(maskName);
|
|
Q_ASSERT(mask);
|
|
for (uint i=0; i<uint(mask->values.count()); i++) valueNames[mask->values[i].name] = false;
|
|
}
|
|
TQDomNode child = checksums.firstChild();
|
|
while ( !child.isNull() ) {
|
|
if ( !child.isElement() ) continue;
|
|
if ( child.nodeName()!="checksum" ) qFatal("Childs of \"checksums\" should \"checksum\"");
|
|
TQString valueName = getChecksumData(child.toElement());
|
|
if ( protection.family()==Protection::BasicProtection ) {
|
|
if ( !valueNames.contains(valueName) ) qFatal("Unknown protected attribute");
|
|
valueNames[valueName] = true;
|
|
}
|
|
child = child.nextSibling();
|
|
}
|
|
TQMap<TQString, bool>::const_iterator it;
|
|
for (it=valueNames.begin(); it!=valueNames.end(); ++it)
|
|
if ( !it.key().isEmpty() && !it.data() ) qFatal(TQString("Missing checksum \"%1\"").tqarg(it.key()));
|
|
}
|
|
}
|
|
|
|
void processMirrored(TQDomElement element)
|
|
{
|
|
TQValueVector<RangeData> mirrored;
|
|
TQDomNode child = element.firstChild();
|
|
while ( !child.isNull() ) {
|
|
if ( !child.isElement() ) qFatal("\"mirror\" child should be an element");
|
|
TQDomElement e = child.toElement();
|
|
if ( e.nodeName()!="range" ) qFatal("\"mirror\" child should be \"range\"");
|
|
RangeData rd;
|
|
bool ok;
|
|
rd.start = fromHexLabel(e.attribute("start"), &ok);
|
|
Address end = fromHexLabel(e.attribute("end"), &ok);
|
|
rd.length = end-rd.start+1;
|
|
if ( !mirrored.isEmpty() && rd.length!=mirrored[0].length )
|
|
qFatal("Mirrored are not of the same length");
|
|
mirrored.append(rd);
|
|
child = child.nextSibling();
|
|
}
|
|
if ( !mirrored.isEmpty() ) static_cast<RegistersData *>(data()->_registersData)->mirrored.append(mirrored);
|
|
}
|
|
|
|
void processUnused(TQDomElement e)
|
|
{
|
|
RangeData rd;
|
|
bool ok;
|
|
rd.start = fromHexLabel(e.attribute("start"), &ok);
|
|
if (!ok) qFatal("Malformed start for unused register");
|
|
Address end = fromHexLabel(e.attribute("end"), &ok);
|
|
rd.length = end-rd.start+1;
|
|
if (!ok) qFatal("Malformed end for unused register");
|
|
static_cast<RegistersData *>(data()->_registersData)->unused.append(rd);
|
|
}
|
|
|
|
void processSfr(TQDomElement e)
|
|
{
|
|
TQString name = e.attribute("name");
|
|
if ( name.isEmpty() ) qFatal("SFR cannot have empty name");
|
|
if ( data()->registersData().sfrs.contains(name) || data()->registersData().combined.contains(name) )
|
|
qFatal("SFR name is duplicated");
|
|
bool ok;
|
|
uint address = fromHexLabel(e.attribute("address"), &ok);
|
|
if ( !ok ) qFatal(TQString("SFR %1 address %2 is malformed").tqarg(name).tqarg(e.attribute("address")));
|
|
uint rlength = data()->registersData().nbBanks * data()->architecture().data().registerBankLength;
|
|
if ( address>=rlength ) qFatal(TQString("Address %1 outside register range").tqarg(toHexLabel(address, 3)));
|
|
RegisterData rdata;
|
|
rdata.address = address;
|
|
uint nb = data()->registersData().nbBits();
|
|
if ( nb>Device::MAX_NB_PORT_BITS ) qFatal(TQString("Need higher MAX_NB_PORT_BITS: %1").tqarg(nb));
|
|
TQString access = e.attribute("access");
|
|
if ( uint(access.length())!=nb ) qFatal("access is missing or malformed");
|
|
TQString mclr = e.attribute("mclr");
|
|
if ( uint(mclr.length())!=nb ) qFatal("mclr is missing or malformed");
|
|
TQString por = e.attribute("por");
|
|
if ( uint(por.length())!=nb ) qFatal("por is missing or malformed");
|
|
for (uint i=0; i<nb; i++) {
|
|
uint k = nb - i - 1;
|
|
bool ok;
|
|
rdata.bits[k].properties = RegisterBitProperties(fromHex(access[i].latin1(), &ok));
|
|
if ( !ok || rdata.bits[k].properties>MaxRegisterBitProperty ) qFatal(TQString("Malformed access bit %1").tqarg(k));
|
|
rdata.bits[k].mclr = RegisterBitState(fromHex(mclr[i].latin1(), &ok));
|
|
if ( !ok || rdata.bits[k].mclr>Nb_RegisterBitStates ) qFatal(TQString("Malformed mclr bit %1").tqarg(k));
|
|
rdata.bits[k].por = RegisterBitState(fromHex(por[i].latin1(), &ok));
|
|
if ( !ok || rdata.bits[k].por>Nb_RegisterBitStates ) qFatal(TQString("Malformed por bit %1").tqarg(k));
|
|
}
|
|
static_cast<RegistersData *>(data()->_registersData)->sfrs[name] = rdata;
|
|
}
|
|
|
|
void processCombined(TQDomElement e)
|
|
{
|
|
TQString name = e.attribute("name");
|
|
if ( name.isEmpty() ) qFatal("Combined register cannot have empty name");
|
|
if ( data()->registersData().sfrs.contains(name) || data()->registersData().combined.contains(name) )
|
|
qFatal("Combined register name is duplicated");
|
|
bool ok;
|
|
CombinedData rdata;
|
|
rdata.address = fromHexLabel(e.attribute("address"), &ok);
|
|
if ( !ok ) qFatal(TQString("Combined %1 address %2 is malformed").tqarg(name).tqarg(e.attribute("address")));
|
|
uint rlength = data()->registersData().nbBanks * data()->architecture().data().registerBankLength;
|
|
if ( rdata.address>=rlength ) qFatal(TQString("Address %1 outside register range").tqarg(toHexLabel(rdata.address, 3)));
|
|
rdata.nbChars = 2*e.attribute("size").toUInt(&ok);
|
|
if ( !ok || rdata.nbChars<2 ) qFatal(TQString("Combined %1 size %2 is malformed").tqarg(name).tqarg(e.attribute("size")));
|
|
Address end = rdata.address + rdata.nbChars/2 - 1;
|
|
if ( end>=rlength ) qFatal(TQString("Address %1 outside register range").tqarg(toHexLabel(end, 3)));
|
|
static_cast<RegistersData *>(data()->_registersData)->combined[name] = rdata;
|
|
}
|
|
|
|
void processDeviceRegisters(TQDomElement element)
|
|
{
|
|
TQString s = element.attribute("same_as");
|
|
if ( !s.isEmpty() ) {
|
|
if ( !_map.contains(s) ) qFatal(TQString("Registers same as unknown device %1").tqarg(s));
|
|
const Pic::Data *d = static_cast<const Pic::Data *>(_map[s]);
|
|
data()->_registersData = d->_registersData;
|
|
return;
|
|
}
|
|
|
|
RegistersData &rdata = *static_cast<RegistersData *>(data()->_registersData);
|
|
bool ok;
|
|
rdata.nbBanks = element.attribute("nb_banks").toUInt(&ok);
|
|
if ( !ok || data()->registersData().nbBanks==0 ) qFatal("Malformed number of banks");
|
|
if ( data()->is18Family() ) {
|
|
rdata.accessBankSplit = fromHexLabel(element.attribute("access_bank_split_offset"), &ok);
|
|
if ( !ok || rdata.accessBankSplit==0 || rdata.accessBankSplit>=0xFF ) qFatal("Malformed access bank split offset");
|
|
rdata.unusedBankMask = fromHexLabel(element.attribute("unused_bank_mask"), &ok);
|
|
if ( !ok || rdata.unusedBankMask>=maxValue(NumberBase::Hex, rdata.nbBanks) ) qFatal("Malformed access unused bank mask");
|
|
} else {
|
|
rdata.accessBankSplit = 0;
|
|
rdata.unusedBankMask = 0;
|
|
}
|
|
|
|
TQDomNode child = element.firstChild();
|
|
while ( !child.isNull() ) {
|
|
if ( !child.isElement() ) qFatal("\"device\" child should be an element");
|
|
TQDomElement e = child.toElement();
|
|
if ( e.nodeName()=="mirror" ) processMirrored(e);
|
|
else if ( e.nodeName()=="unused" ) processUnused(e);
|
|
else if ( e.nodeName()=="combined" ) processCombined(e);
|
|
else if ( e.nodeName()=="sfr" ) processSfr(e);
|
|
else qFatal(TQString("Node name \"%1\" is not recognized").tqarg(e.nodeName()));
|
|
child = child.nextSibling();
|
|
}
|
|
|
|
for (uint i=0; i<Device::MAX_NB_PORTS; i++) {
|
|
TQString portname = rdata.portName(i);
|
|
if ( portname.isEmpty() ) break;
|
|
bool hasPort = rdata.sfrs.contains(portname);
|
|
TQString trisname = rdata.trisName(i);
|
|
if ( trisname.isEmpty() ) continue;
|
|
bool hasTris = rdata.sfrs.contains(trisname);
|
|
if ( !hasPort && hasTris ) qFatal(TQString("%1 needs %2 to be present").tqarg(trisname).tqarg(portname));
|
|
TQString latchname = rdata.latchName(i);
|
|
if ( latchname.isEmpty() ) continue;
|
|
bool hasLatch = rdata.sfrs.contains(latchname);
|
|
if ( !hasPort && hasLatch ) qFatal(TQString("%1 needs %2 to be present").tqarg(latchname).tqarg(portname));
|
|
}
|
|
}
|
|
|
|
void processRegistersFile(const TQString &filename, TQStringList &devices)
|
|
{
|
|
TQDomDocument doc = parseFile(filename);
|
|
TQDomElement root = doc.documentElement();
|
|
if ( root.nodeName()!="registers" ) qFatal("root node should be \"registers\"");
|
|
for (TQDomNode child=root.firstChild(); !child.isNull(); child = child.nextSibling()) {
|
|
if ( child.isComment() ) qDebug("comment: %s", child.toComment().data().latin1());
|
|
else {
|
|
if ( !child.isElement() ) qFatal("\"registers\" child should be an element");
|
|
if ( child.nodeName()!="device" ) qFatal("Device node should be named \"device\"");
|
|
TQDomElement device = child.toElement();
|
|
TQString name = device.attribute("name");
|
|
if ( devices.contains(name) ) qFatal(TQString("Registers already defined for %1").tqarg(name));
|
|
if ( _map.contains(name) ) {
|
|
_data = _map[name];
|
|
processDeviceRegisters(device);
|
|
devices.append(name);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void processRegisters()
|
|
{
|
|
TQStringList devices;
|
|
processRegistersFile("registers/registers.xml", devices);
|
|
processRegistersFile("registers/registers_missing.xml", devices);
|
|
|
|
// check if we miss any register description
|
|
TQMap<TQString, Device::Data *>::const_iterator it = _map.begin();
|
|
for (; it!=_map.end(); ++it) {
|
|
_data = it.data();
|
|
if ( !devices.contains(it.key()) ) qWarning("Register description not found for %s", it.key().latin1());
|
|
}
|
|
}
|
|
|
|
virtual void checkPins(const TQMap<TQString, uint> &pinLabels) const
|
|
{
|
|
if ( !pinLabels.contains("VDD") ) qFatal("No VDD pin specified");
|
|
if ( !pinLabels.contains("VSS") ) qFatal("No VSS pin specified");
|
|
TQMap<TQString, uint>::const_iterator it;
|
|
for (it=pinLabels.begin(); it!=pinLabels.end(); ++it) {
|
|
if ( it.key()=="VDD" || it.key()=="VSS" || it.key().startsWith("CCP") ) continue;
|
|
if ( it.data()!=1 ) qFatal(TQString("Duplicated pin \"%1\"").tqarg(it.key()));
|
|
}
|
|
const Pic::RegistersData &rdata = static_cast<const Pic::RegistersData &>(*_data->registersData());
|
|
for (uint i=0; i<Device::MAX_NB_PORTS; i++) {
|
|
if ( !rdata.hasPort(i) ) continue;
|
|
for (uint k=0; k<Device::MAX_NB_PORT_BITS; k++) {
|
|
if ( !rdata.hasPortBit(i, k) ) continue;
|
|
TQString name = rdata.portBitName(i, k);
|
|
if ( !pinLabels.contains(name) ) qFatal(TQString("Pin \"%1\" not present").tqarg(name));
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void parse()
|
|
{
|
|
Device::XmlToDataBase::parse();
|
|
processRegisters();
|
|
}
|
|
|
|
}; // class Pic::XmlToData
|
|
|
|
} // namespace
|
|
|
|
//-----------------------------------------------------------------------------
|
|
XML_MAIN(Pic::XmlToData)
|