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/xml_to_data/device_xml_to_data.cpp

261 lines
12 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 "device_xml_to_data.h"
#include <tqdir.h>
#include <tqfile.h>
#include <tqtextstream.h>
#include <tqregexp.h>
bool Device::XmlToDataBase::getFrequencyRange(OperatingCondition oc, Special special, TQDomElement element)
{
TQDomElement range;
for (TQDomNode child=element.firstChild(); !child.isNull(); child=child.nextSibling()) {
if ( child.nodeName()!="frequency_range" ) continue;
if ( !child.isElement() ) qFatal("\"frequency_range\" should be an element");
if ( child.toElement().attribute("name")!=oc.key() ) continue;
Special s = Special::fromKey(child.toElement().attribute("special"));
if ( s==Special::Nb_Types ) qFatal("Unrecognized special");
if ( special!=s ) continue;
if ( !range.isNull() ) qFatal("Duplicated \"frequency_range\"");
range = child.toElement();
}
if ( range.isNull() ) return false;
FrequencyRange frange;
frange.operatingCondition = oc;
frange.special = special;
for (TQDomNode child=range.firstChild(); !child.isNull(); child=child.nextSibling()) {
if ( child.nodeName()=="frequency" ) {
if ( !child.isElement() ) qFatal("Frequency is not an element");
TQDomElement frequency = child.toElement();
bool ok1, ok2, ok3, ok4;
RangeBox box;
box.start.x = frequency.attribute("start").toDouble(&ok1);
box.end.x = frequency.attribute("end").toDouble(&ok2);
box.start.yMin = frequency.attribute("vdd_min").toDouble(&ok3);
box.start.yMax = frequency.attribute("vdd_max").toDouble(&ok4);
box.end.yMax = box.start.yMax;
if ( !ok1 || !ok2 || !ok3 || !ok4
|| box.start.x<0.0 || box.start.x>box.end.x
|| box.start.yMin<0.0 || box.start.yMin>box.start.yMax )
qFatal("Malformed frequency element");
if ( frequency.attribute("vdd_min_end").isEmpty() ) box.end.yMin = box.start.yMin;
else {
box.end.yMin = frequency.attribute("vdd_min_end").toDouble(&ok1);
if ( !ok1 || box.end.yMin>box.end.yMax ) qFatal("Malformed frequency element");
}
box.mode = frequency.attribute("mode");
box.osc = frequency.attribute("osc");
box.special = frequency.attribute("special");
for (uint i=0; i<uint(frange.vdds.count()); i++)
if ( box.start.x<frange.vdds[i].end.x && box.end.x>frange.vdds[i].start.x ) {
if ( box.mode.isEmpty() && box.osc.isEmpty() && box.special.isEmpty() )
qFatal("Overlapping frequency ranges");
continue; // #### FIXME: ignore additionnal mode
}
// qDebug("add Freq Range: %s %s %f=[%f %f] %f=[%f %f]",
// Device::FrequencyRange::TYPE_LABELS[type], Device::FrequencyRange::SPECIAL_LABELS[type],
// box.start.x, box.start.yMin, box.start.yMax,
// box.end.x, box.end.yMin, box.end.yMax);
frange.vdds.append(box);
}
}
if ( frange.vdds.count()==0 ) qFatal("Empty frequency range");
_data->_frequencyRanges.append(frange);
return true;
}
bool Device::XmlToDataBase::getMemoryTechnology(TQDomElement element)
{
TQString s = element.attribute("memory_technology");
_data->_memoryTechnology = MemoryTechnology::fromKey(s);
if ( _data->_memoryTechnology!=MemoryTechnology::Nb_Types ) return true;
if ( !s.isNull() ) qFatal("Unrecognized memory technology");
return false;
}
void Device::XmlToDataBase::processDevice(TQDomElement device)
{
TQString name = device.attribute("name").upper();
if ( name.isEmpty() ) qFatal("Device has no name");
if ( _map.contains(name) ) qFatal(TQString("Device \"%1\" already defined").tqarg(name));
_data = createData();
_map[name] = _data;
_data->_name = name;
_data->_alternatives = TQStringList::split(' ', device.attribute("alternative"));
if ( _data->_alternatives.count() ) _alternatives[name] = _data->_alternatives;
_data->_status = Status::fromKey(device.attribute("status"));
switch (_data->_status.type()) {
case Status::Nb_Types:
qFatal("Unrecognized or absent device status");
break;
case Status::Future:
if ( _data->_alternatives.count() ) qFatal("Future device has alternative");
break;
case Status::NotRecommended:
case Status::Mature:
if ( _data->_alternatives.count()==0 ) warning("Not-recommended/mature device has no alternative");
break;
case Status::InProduction:
case Status::EOL:
case Status::Unknown: break;
}
// document
_data->_documents.webpage = device.attribute("document"); // ### REMOVE ME
TQDomElement documents = findUniqueElement(device, "documents", TQString(), TQString());
if ( documents.isNull() ) {
if ( _data->_documents.webpage.isEmpty() ) qFatal("Missing \"documents\" element");
} else {
if ( !_data->_documents.webpage.isEmpty() ) qFatal("document should be removed from root element");
_data->_documents.webpage = documents.attribute("webpage");
if ( _data->_documents.webpage.isEmpty() ) qFatal("Missing webpage");
_data->_documents.datasheet = documents.attribute("datasheet");
TQRegExp rexp("\\d{5}");
if ( _data->_documents.datasheet=="?" ) warning("No datasheet specified");
if ( !rexp.exactMatch(_data->_documents.datasheet) ) qFatal(TQString("Malformed datasheet \"%1\" (5 digits)").tqarg(_data->_documents.datasheet));
_data->_documents.progsheet = documents.attribute("progsheet");
if ( _data->_documents.progsheet=="?" ) warning("No progsheet specified");
if ( !rexp.exactMatch(_data->_documents.datasheet) ) qFatal(TQString("Malformed progsheet \"%1\" (5 digits)").tqarg(_data->_documents.progsheet));
_data->_documents.erratas = TQStringList::split(" ", documents.attribute("erratas"));
for (uint i=0; i<uint(_data->_documents.erratas.count()); i++) {
TQString errata = _data->_documents.erratas[i];
if ( !rexp.exactMatch(errata) ) {
TQRegExp rexp2("\\d{5}e\\d");
if ( !rexp2.exactMatch(errata) && !errata.startsWith("er") && errata.mid(2)!=_data->_name.lower() )
qFatal(TQString("Malformed erratas \"%1\" (5 digits or 5 digits + e + 1 digit or \"er\" + name)").tqarg(errata));
}
}
}
if ( _data->_documents.webpage=="?" ) warning("No webpage specified");
else {
TQRegExp rexp("\\d{6}");
if ( !rexp.exactMatch(_data->_documents.webpage) ) qFatal(TQString("Malformed webpage \"%1\" (6 digits)").tqarg(_data->_documents.webpage));
if ( _documents.contains(_data->_documents.webpage) )
qFatal(TQString("webpage duplicated (already used for %1)").tqarg(_documents[_data->_documents.webpage]));
_documents[_data->_documents.webpage] = name;
}
// frequency ranges
TQStringList names;
bool ok = false;
FOR_EACH(OperatingCondition, oc) {
names += oc.key();
FOR_EACH(Special, special)
if ( getFrequencyRange(oc, special, device) && special==Special::Normal ) ok = true;
}
if ( !ok ) qWarning("No normal frequency range defined");
checkTagNames(device, "frequency_range", names);
// memory technology
if ( !getMemoryTechnology(device) ) qFatal("Memory technology not defined");
// packages
for (TQDomNode child=device.firstChild(); !child.isNull(); child=child.nextSibling()) {
if ( !child.isElement() || child.nodeName()!="package" ) continue;
Package p = processPackage(child.toElement());
TQMap<TQString, uint> pinLabels;
for (uint i=0; i<uint(p.pins.count()); i++) {
if ( p.pins[i].isEmpty() || p.pins[i]=="N/C" ) continue;
TQStringList labels = TQStringList::split("/", p.pins[i]);
for(uint k=0; k<uint(labels.count()); k++) {
if ( pinLabels.contains(labels[k]) ) pinLabels[labels[k]]++;
else pinLabels[labels[k]] = 1;
}
}
for (uint k=0; k<uint(_data->_packages.count()); k++)
for (uint l=0; l<uint(p.types.count()); l++)
for (uint j=0; j<uint(_data->_packages[k].types.count()); j++)
if ( _data->_packages[k].types[j]==p.types[l] && _data->_packages[k].pins.count()==p.pins.count() ) qFatal("Duplicated package type");
if ( !pinLabels.isEmpty() ) checkPins(pinLabels);
_data->_packages.append(p);
}
}
Device::Package Device::XmlToDataBase::processPackage(TQDomElement element)
{
Package package;
// nb pins
bool ok;
uint nb = element.attribute("nb_pins").toUInt(&ok);
if ( !ok || nb==0 ) qFatal("Malformed \"nb_pins\"");
package.pins.resize(nb);
// types
TQStringList types = TQStringList::split(" ", element.attribute("types"));
if ( types.isEmpty() ) qFatal("No package types specified");
for (uint k=0; k<uint(types.count()); k++) {
uint i = 0;
for (; Package::TYPE_DATA[i].name; i++) {
if ( types[k]!=Package::TYPE_DATA[i].name ) continue;
for (uint j=0; j<uint(package.types.count()); j++)
if ( package.types[j]==i ) qFatal(TQString("Duplicated package type %1").tqarg(types[k]));
uint j = 0;
for (; j<Package::MAX_NB; j++)
if ( nb==Package::TYPE_DATA[i].nbPins[j] ) break;
if ( j==Package::MAX_NB ) qFatal(TQString("Package %1 does not have the correct number of pins %2 (%3)").tqarg(types[k]).tqarg(nb).tqarg(Package::TYPE_DATA[i].nbPins[0]));
package.types.append(i);
break;
}
if ( Package::TYPE_DATA[i].name==0 ) qFatal(TQString("Unknown package type \"%1\"").tqarg(types[k]));
}
// pins
TQString name = Package::TYPE_DATA[package.types[0]].name;
if ( name=="sot23" ) {
if ( package.types.count()!=1 ) qFatal("SOT23 should be a specific package");
} else if ( (nb%2)!=0 ) qFatal(TQString("\"nb_pins\" should be even for package \"%1\"").tqarg(name));
uint have_pins = false;
TQMemArray<bool> found(nb);
found.fill(false);
TQDomNode child = element.firstChild();
while ( !child.isNull() ) {
if ( child.nodeName()=="pin" ) {
if ( !child.isElement() ) qFatal("\"pin\" is not an element");
TQDomElement pin = child.toElement();
bool ok;
uint i = pin.attribute("index").toUInt(&ok);
if ( !ok || i==0 || i>nb ) qFatal("Malformed pin index");
if (found[i-1]) qFatal("Duplicated pin index");
found[i-1] = true;
TQString name = pin.attribute("name");
if ( !name.isEmpty() && name!="N/C" ) {
TQStringList labels = TQStringList::split("/", name);
if ( name.contains(" ") || labels.count()==0 ) qFatal("Malformed pin name");
if ( name!=name.upper() ) qFatal("Pin name should be uppercase");
}
package.pins[i-1] = name;
have_pins = true;
}
child = child.nextSibling();
}
if ( !have_pins ) ;//warning("Pins not specified"); // #### REMOVE ME !!
else for (uint i=0; i<nb; i++) if ( !found[i] ) qFatal(TQString("Pin #%1 not specified").tqarg(i+1));
return package;
}
void Device::XmlToDataBase::parse()
{
// process device files
TQStringList files = TQDir::current().entryList("*.xml");
for (uint i=0; i<uint(files.count()); i++) {
_data = 0;
TQDomDocument doc = parseFile(files[i]);
TQDomElement root = doc.documentElement();
if ( root.nodeName()!="device" ) qFatal("root node should be \"device\"");
processDevice(root);
}
// check alternatives
TQMap<TQString, TQStringList>::const_iterator ait = _alternatives.begin();
for (; ait!=_alternatives.end(); ++ait) {
TQStringList::const_iterator lit = ait.data().begin();
for (; lit!=ait.data().end(); ++lit)
if ( !_map.contains(*lit) ) qFatal(TQString("Unknown alternative %1 for device %2").tqarg((*lit)).tqarg(ait.key()));
}
}