/*************************************************************************** * Copyright (C) 2005-2007 Nicolas Hadacek * * * * 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 #include #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 { 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_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_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; iarchitecture()!=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; inbCharsWord(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; inbCharsWord(MemoryRangeType::Config); bool ok; TQString defName; TQMap 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_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 getConfigWords(TQDomElement element) { uint nbWords = data()->nbWords(MemoryRangeType::Config); TQValueVector 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_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_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; iconfig().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; kvalues.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()_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_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_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 cwords = getConfigWords(device); uint nbWords = data()->nbWords(MemoryRangeType::Config); data()->_config->_words.resize(nbWords); FOR_EACH(Pic::ConfigNameType, type) { TQMap cnames; // cname -> mask name for (uint i=0; i_config->_words[i] = cwords[i]; const Config::Word &word = data()->_config->_words[i]; for (uint j=0; j_config->_words[i]; for (uint j=0; j_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_config->_words[i]; for (uint j=0; jnbCharsWord(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 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; ivalues.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::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 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(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(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; iMaxRegisterBitProperty ) 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(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(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(_map[s]); data()->_registersData = d->_registersData; return; } RegistersData &rdata = *static_cast(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::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 &pinLabels) const { if ( !pinLabels.contains("VDD") ) qFatal("No VDD pin specified"); if ( !pinLabels.contains("VSS") ) qFatal("No VSS pin specified"); TQMap::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(*_data->registersData()); for (uint i=0; i