1 #include "ReferenceBlock.h"
3 #include "ReferenceInterface.h"
4 #include "BlockParameter.h"
5 #include "BlockParameterUser.h"
6 #include "BlockParameterGeneric.h"
7 #include "BlockParameterPort.h"
8 #include "BlockParameterWishbone.h"
9 #include "Parameters.h"
11 ReferenceBlock::ReferenceBlock(const QString _xmlFile) : AbstractBlock(NULL) {
15 void ReferenceBlock::addCategory(int id) {
16 categories.append(id);
19 void ReferenceBlock::setDescription(const QString& str) {
24 void ReferenceBlock::addImplementation(BlockImplementation *impl) {
25 implementations.append(impl);
28 void ReferenceBlock::setHashMd5() {
30 if (file.open(QIODevice::ReadOnly)) {
31 QByteArray fileData = file.readAll();
32 QByteArray hashData = QCryptographicHash::hash(fileData, QCryptographicHash::Md5);
33 hashMd5 = QString(hashData.toHex());
34 cout << qPrintable(xmlFile) << " has md5 hash : " << qPrintable(hashMd5) << endl;
41 void ReferenceBlock::load(QDomElement &elt) throw(Exception) {
43 cout << "Block : get version" << endl;
44 QString verStr = elt.attribute("version","none");
45 QString specialStr = elt.attribute("special","none");
46 if (verStr != "none") {
52 setSpecialType(getSpecialTypeFromString(specialStr));
54 cout << "Block : get informations" << endl;
55 QDomElement eltInfo = elt.firstChildElement("informations");
57 loadInformations(eltInfo);
63 cout << "Block : get params" << endl;
64 QDomElement eltParams = eltInfo.nextSiblingElement("parameters");
66 loadParameters(eltParams);
72 cout << "Block : get interfaces" << endl;
73 QDomElement eltInter = eltParams.nextSiblingElement("interfaces");
75 loadInterfaces(eltInter);
81 // create interfaces that correspond to a wishbone parameter, if any.
83 createInterfaceForWishbone();
90 void ReferenceBlock::loadInformations(QDomElement &elt) throw(Exception) {
93 if ((elt.isNull()) || (elt.tagName() != "informations")) throw (Exception(BLOCKFILE_CORRUPTED));
95 cout << "Block info : get name" << endl;
96 QDomNode nodeName = elt.firstChild();
97 QDomNode nodeNameTxt = nodeName.firstChild();
98 if (nodeNameTxt.isNull()) {
102 QDomText txtName = nodeNameTxt.toText();
103 name = Parameters::normalizeName(txtName.data().trimmed());
104 cout<< "block name : " << qPrintable(name) << endl;
107 // getting categories
108 cout << "Block info : get categories" << endl;
109 QDomElement eltCat = nodeName.nextSiblingElement("category");
111 QString idsStr = eltCat.attribute("ids","none");
112 if (idsStr == "none") throw (Exception(BLOCKFILE_CORRUPTED));
113 if (idsStr.isEmpty()) {
114 categories.append(99);
117 QStringList listCat = idsStr.split(",");
118 foreach(QString str, listCat)
120 int idCat = str.toInt(&ok);
121 categories.append(idCat);
125 // getting description
126 cout << "Block info : get description" << endl;
127 QDomElement eltDesc = eltCat.nextSiblingElement("description");
129 QDomNode nodeTxt = eltDesc.firstChild();
130 if (nodeTxt.isNull()) {
131 description = "no description";
134 QDomText txtBrief = nodeTxt.toText();
135 description = txtBrief.data().trimmed();
136 cout << "block desc : " << qPrintable(description) << endl;
140 void ReferenceBlock::loadParameters(QDomElement &elt) throw(Exception) {
142 if ((elt.isNull()) || (elt.tagName() != "parameters")) throw (Exception(BLOCKFILE_CORRUPTED));
144 QDomNodeList listNodeParam = elt.elementsByTagName("parameter");
145 for(int i=0; i<listNodeParam.size(); i++) {
146 QDomNode node = listNodeParam.at(i);
147 QDomElement elt = node.toElement();
148 QString nameStr = elt.attribute("name","none");
149 QString contextStr = elt.attribute("context","none");
150 QString typeStr = elt.attribute("type","none");
151 QString valueStr = elt.attribute("value","none");
152 BlockParameter *param = NULL;
154 if(valueStr == "none"){
155 if (contextStr == "generic") throw (Exception(BLOCKFILE_CORRUPTED)); // set is required for generics
158 if (contextStr == "user") {
159 param = new BlockParameterUser(this,nameStr,typeStr,valueStr);
161 else if (contextStr == "generic") {
162 param = new BlockParameterGeneric(this,nameStr,typeStr,valueStr);
164 else if (contextStr == "wb") {
165 QString widthStr = elt.attribute("width","none");
166 QString wbStr = elt.attribute("wishbone","none");
169 QString wbValue = "";
170 QStringList listWb = wbStr.split(",");
171 cout << "wb param has:";
172 foreach(QString s, listWb) {
173 cout << qPrintable(s) << " | ";
177 if (listWb.at(0) == "r") {
178 access = BlockParameter::Read;
180 else if (listWb.at(0) == "w") {
181 access = BlockParameter::Write;
183 wbValue = listWb.at(1).toInt(&ok);
185 if(listWb.at(1) == "true" || listWb.at(1) == "false"){
186 wbValue = listWb.at(1);
191 if(listWb.at(2) == "trigger") {
192 duration = BlockParameter::Trigger;
194 else if(listWb.at(2) == "perm") {
195 duration = BlockParameter::Permanent;
198 param = new BlockParameterWishbone(this,nameStr,typeStr,widthStr,valueStr,access,wbValue,duration);
200 else if (contextStr == "port") {
201 QString ifaceStr = elt.attribute("iface","none");
202 param = new BlockParameterPort(this,nameStr,valueStr,ifaceStr);
206 throw (Exception(BLOCKFILE_CORRUPTED));
208 params.append(param);
212 void ReferenceBlock::loadInterfaces(QDomElement &elt) throw(Exception) {
223 ReferenceInterface* iface;
225 if ((elt.isNull()) || (elt.tagName() != "interfaces")) throw (Exception(BLOCKFILE_CORRUPTED));
227 QDomElement eltInputs = elt.firstChildElement("inputs");
228 // getting each input
229 QDomNodeList listNodeInputs = eltInputs.elementsByTagName("input");
231 // find all input clocks
232 QList<AbstractInterface*> clocks;
233 for(int i=0;i<listNodeInputs.size();i++) {
234 QDomNode node = listNodeInputs.at(i);
235 QDomElement eltInput = node.toElement();
236 purposeStr = eltInput.attribute("purpose","none");
237 if (purposeStr == "clock") {
238 nameStr = eltInput.attribute("name","none");
239 iface = new ReferenceInterface(this,nameStr,AbstractInterface::Input, AbstractInterface::Clock, "boolean", "1", AbstractInterface::LittleEndian, 1);
240 if (! iface->checkSetClockIface(nameStr)) {
241 throw (Exception(BLOCKFILE_CORRUPTED));
243 inputs.append(iface);
244 clocks.append(iface);
247 cout << "number of clocks: " << clocks.size() << endl;
250 for(int i=0;i<listNodeInputs.size();i++) {
251 QDomNode node = listNodeInputs.at(i);
252 QDomElement eltInput = node.toElement();
253 purposeStr = eltInput.attribute("purpose","none");
254 purpose = ReferenceInterface::translatePurpose(purposeStr);
255 if (purpose != AbstractInterface::Clock) {
256 cout << "translated purpose : " << purpose << endl;
257 nameStr = eltInput.attribute("name","none");
258 typeStr = eltInput.attribute("type","none");
259 widthStr = eltInput.attribute("width","none");
260 endianStr = eltInput.attribute("endian","none");
261 clockStr = eltInput.attribute("clock","none");
263 if ((endianStr == "none") || (endianStr == "little")) {
264 endianess = AbstractInterface::LittleEndian;
266 else if (endianStr == "big") {
267 endianess = AbstractInterface::BigEndian;
270 throw (Exception(BLOCKFILE_CORRUPTED));
273 multStr = eltInput.attribute("multiplicity","none");
274 mult = ReferenceInterface::translateMultiplicity(multStr);
276 iface = new ReferenceInterface(this,nameStr, AbstractInterface::Input, purpose, typeStr, widthStr, endianess, mult);
277 if (clockStr == "none") {
278 if (clocks.size() > 1) {
279 // if several clocks, the associated one MUST be given
280 throw (Exception(BLOCKFILE_CORRUPTED));
282 // no clock given, take the single one
283 clockStr = clocks.at(0)->getName();
285 if (! iface->checkSetClockIface(clockStr)) {
286 throw (Exception(BLOCKFILE_CORRUPTED));
288 inputs.append(iface);
291 // getting each control
292 QDomNodeList listNodeInCtl = eltInputs.elementsByTagName("control");
293 for(int i=0;i<listNodeInCtl.size();i++) {
294 QDomNode node = listNodeInCtl.at(i);
295 QDomElement eltInput = node.toElement();
296 nameStr = eltInput.attribute("iface","none");
297 AbstractInterface* dataIface = getIfaceFromName(nameStr);
298 if (dataIface == NULL) throw (Exception(BLOCKFILE_CORRUPTED));
299 QString nameEnbStr = dataIface->getName()+"_enb";
300 iface = new ReferenceInterface(this,nameEnbStr,AbstractInterface::Input, AbstractInterface::Control,"boolean","1", AbstractInterface::LittleEndian, 1);
301 if (!iface->setAssociatedIface(dataIface)) {
302 throw (Exception(BLOCKFILE_CORRUPTED));
304 clockStr = dataIface->getClockIfaceString();
305 if (! iface->checkSetClockIface(clockStr)) {
306 throw (Exception(BLOCKFILE_CORRUPTED));
308 cout << "created a control input named " << qPrintable(iface->getName()) << endl;
309 inputs.append(iface);
311 QDomElement eltOutputs = eltInputs.nextSiblingElement("outputs");
312 QDomNodeList listNodeOutputs = eltOutputs.elementsByTagName("output");
313 for(int i=0;i<listNodeOutputs.size();i++) {
314 QDomNode node = listNodeOutputs.at(i);
315 QDomElement eltOutput = node.toElement();
317 nameStr = eltOutput.attribute("name","none");
318 typeStr = eltOutput.attribute("type","none");
319 widthStr = eltOutput.attribute("width","none");
320 endianStr = eltOutput.attribute("endian","none");
321 clockStr = eltOutput.attribute("clock","none");
323 if ((endianStr == "none") || (endianStr == "little")) {
324 endianess = AbstractInterface::LittleEndian;
326 else if (endianStr == "big") {
327 endianess = AbstractInterface::BigEndian;
330 throw (Exception(BLOCKFILE_CORRUPTED));
332 purposeStr = eltOutput.attribute("purpose","none");
333 purpose = ReferenceInterface::translatePurpose(purposeStr);
334 multStr = eltOutput.attribute("multiplicity","none");
335 mult = ReferenceInterface::translateMultiplicity(multStr);
337 iface = new ReferenceInterface(this,nameStr,AbstractInterface::Output, purpose,typeStr,widthStr, endianess, mult);
338 if (clockStr == "none") {
339 if (clocks.size() > 1) {
340 // if several clocks, the associated one MUST be given
341 throw (Exception(BLOCKFILE_CORRUPTED));
343 // no clock given, take the single one
344 clockStr = clocks.at(0)->getName();
346 if (! iface->checkSetClockIface(clockStr)) {
347 throw (Exception(BLOCKFILE_CORRUPTED));
350 outputs.append(iface);
352 // getting each control
353 QDomNodeList listNodeOutCtl = eltOutputs.elementsByTagName("control");
354 for(int i=0;i<listNodeOutCtl.size();i++) {
355 QDomNode node = listNodeOutCtl.at(i);
356 QDomElement eltOutput = node.toElement();
357 nameStr = eltOutput.attribute("iface","none");
358 AbstractInterface* dataIface = getIfaceFromName(nameStr);
359 if (dataIface == NULL) throw (Exception(BLOCKFILE_CORRUPTED));
360 nameStr = dataIface->getName()+"_enb";
361 iface = new ReferenceInterface(this,nameStr,AbstractInterface::Output, AbstractInterface::Control,"boolean","1",AbstractInterface::LittleEndian, 1);
362 if (!iface->setAssociatedIface(dataIface)) {
363 throw (Exception(BLOCKFILE_CORRUPTED));
365 clockStr = dataIface->getClockIfaceString();
366 if (! iface->checkSetClockIface(clockStr)) {
367 throw (Exception(BLOCKFILE_CORRUPTED));
369 cout << "created a control output named " << qPrintable(iface->getName()) << endl;
370 outputs.append(iface);
373 QDomElement eltBidirs = eltInputs.nextSiblingElement("bidirs");
374 QDomNodeList listNodeBidirs = eltBidirs.elementsByTagName("bidir");
375 for(int i=0;i<listNodeBidirs.size();i++) {
376 QDomNode node = listNodeBidirs.at(i);
377 QDomElement eltBidir = node.toElement();
378 nameStr = eltBidir.attribute("name","none");
379 typeStr = eltBidir.attribute("type","none");
380 widthStr = eltBidir.attribute("width","none");
381 endianStr = eltBidir.attribute("endian","none");
382 clockStr = eltBidir.attribute("clock","none");
384 if ((endianStr == "none") || (endianStr == "little")) {
385 endianess = AbstractInterface::LittleEndian;
387 else if (endianStr == "big") {
388 endianess = AbstractInterface::BigEndian;
391 throw (Exception(BLOCKFILE_CORRUPTED));
393 purposeStr = eltBidir.attribute("purpose","none");
394 purpose = ReferenceInterface::translatePurpose(purposeStr);
395 multStr = eltBidir.attribute("multiplicity","none");
396 mult = ReferenceInterface::translateMultiplicity(multStr);
398 iface = new ReferenceInterface(this,nameStr,AbstractInterface::InOut, purpose,typeStr,widthStr, endianess, mult);
399 if (clockStr == "none") {
400 if (clocks.size() > 1) {
401 // if several clocks, the associated one MUST be given
402 throw (Exception(BLOCKFILE_CORRUPTED));
404 // no clock given, take the single one
405 clockStr = clocks.at(0)->getName();
407 if (! iface->checkSetClockIface(clockStr)) {
408 throw (Exception(BLOCKFILE_CORRUPTED));
411 bidirs.append(iface);
415 void ReferenceBlock::createInterfaceForWishbone() throw(Exception){
416 ReferenceInterface* iface = NULL;
417 foreach(BlockParameter* param, params) {
419 if (param->isWishboneParameter()) {
421 BlockParameterWishbone* p = (BlockParameterWishbone*)param;
422 cout << "creating interface for parameter wb " << qPrintable(p->getName()) << endl;
424 if (p->getWBAccess() == BlockParameter::Read) {
425 iface = new ReferenceInterface(this,p->getName(), AbstractInterface::Output, AbstractInterface::Wishbone, p->getTypeString(),p->getWidth(), AbstractInterface::LittleEndian, 1);
426 outputs.append(iface);
428 else if (p->getWBAccess() == BlockParameter::Write) {
429 iface = new ReferenceInterface(this,p->getName(), AbstractInterface::Input, AbstractInterface::Wishbone,p->getTypeString(),p->getWidth(),AbstractInterface::LittleEndian,1);
430 inputs.append(iface);
433 throw (Exception(BLOCKFILE_CORRUPTED));
439 void ReferenceBlock::parametersValidation(QList<AbstractBlock *> *checkedBlocks, QList<AbstractBlock *> *blocksToConfigure) {
444 only used to save all ReferenceBlock in a library in binary format, so that reference blocks
445 are read very fast at application startup.
448 QDataStream& operator<<(QDataStream &out, const ReferenceBlock &b) {
450 out.setVersion(QDataStream::Qt_5_0);
452 QByteArray blockData;
453 QDataStream toWrite(&blockData, QIODevice::WriteOnly);
456 toWrite << b.xmlFile;
457 toWrite << b.specialType;
458 toWrite << b.version;
459 toWrite << b.description;
460 toWrite << b.categories;
461 toWrite << b.hashMd5;
462 toWrite << b.params.size();
464 for(int i=0;i<b.params.size();i++) {
466 toWrite << p->getContext();
467 toWrite << p->getName();
468 toWrite << p->getTypeString();
469 toWrite << p->getValue().toString();
470 if (p->isPortParameter()) {
471 toWrite << ((BlockParameterPort*)p)->getIfaceName();
473 else if (p->isWishboneParameter()) {
474 BlockParameterWishbone* pwb = (BlockParameterWishbone*)p;
475 toWrite << pwb->getWidth();
476 toWrite << pwb->getWBAccess();
477 toWrite << pwb->getWBValue();
478 toWrite << pwb->getWBDuration();
483 toWrite << b.inputs.size();
484 // firstly write clock ifaces
485 for(int i=0; i<b.inputs.size(); i++){
486 ReferenceInterface *iface = (ReferenceInterface *)(b.inputs.at(i));
487 if (iface->getPurpose() == AbstractInterface::Clock) {
488 toWrite << iface->getName();
489 toWrite << iface->getType();
490 toWrite << iface->getWidthString();
491 toWrite << iface->getPurpose();
492 toWrite << iface->getDirection();
493 toWrite << iface->getMultiplicity();
494 toWrite << iface->getClockIfaceType();
495 toWrite << iface->getClockIfaceString();
498 // secondly write control ifaces
499 for(int i=0; i<b.inputs.size(); i++){
500 ReferenceInterface *iface = (ReferenceInterface *)(b.inputs.at(i));
501 if (iface->getPurpose() == AbstractInterface::Control) {
502 toWrite << iface->getName();
503 toWrite << iface->getType();
504 toWrite << iface->getWidthString();
505 toWrite << iface->getPurpose();
506 toWrite << iface->getDirection();
507 toWrite << iface->getMultiplicity();
508 toWrite << iface->getClockIfaceType();
509 toWrite << iface->getClockIfaceString();
512 // thirdly, write other ifaces
513 for(int i=0; i<b.inputs.size(); i++){
514 ReferenceInterface *iface = (ReferenceInterface *)(b.inputs.at(i));
515 if ((iface->getPurpose() != AbstractInterface::Control) && (iface->getPurpose() != AbstractInterface::Clock)) {
516 toWrite << iface->getName();
517 toWrite << iface->getType();
518 toWrite << iface->getWidthString();
519 toWrite << iface->getPurpose();
520 toWrite << iface->getDirection();
521 toWrite << iface->getMultiplicity();
522 toWrite << iface->getClockIfaceType();
523 toWrite << iface->getClockIfaceString();
526 toWrite << b.outputs.size();
527 // firstly write control ifaces
528 for(int i=0; i<b.outputs.size(); i++){
529 ReferenceInterface *iface = (ReferenceInterface *)(b.outputs.at(i));
530 if (iface->getPurpose() == AbstractInterface::Control) {
531 toWrite << iface->getName();
532 toWrite << iface->getType();
533 toWrite << iface->getWidthString();
534 toWrite << iface->getPurpose();
535 toWrite << iface->getDirection();
536 toWrite << iface->getMultiplicity();
537 toWrite << iface->getClockIfaceType();
538 toWrite << iface->getClockIfaceString();
541 // secondly, write other ifaces
542 for(int i=0; i<b.outputs.size(); i++){
543 ReferenceInterface *iface = (ReferenceInterface *)(b.outputs.at(i));
544 if (iface->getPurpose() != AbstractInterface::Control) {
545 toWrite << iface->getName();
546 toWrite << iface->getType();
547 toWrite << iface->getWidthString();
548 toWrite << iface->getPurpose();
549 toWrite << iface->getDirection();
550 toWrite << iface->getMultiplicity();
551 toWrite << iface->getClockIfaceType();
552 toWrite << iface->getClockIfaceString();
555 toWrite << b.bidirs.size();
556 for(int i=0; i<b.bidirs.size(); i++){
557 ReferenceInterface *iface = (ReferenceInterface *)(b.bidirs.at(i));
558 toWrite << iface->getName();
559 toWrite << iface->getType();
560 toWrite << iface->getWidthString();
561 toWrite << iface->getPurpose();
562 toWrite << iface->getDirection();
563 toWrite << iface->getMultiplicity();
564 toWrite << iface->getClockIfaceType();
565 toWrite << iface->getClockIfaceString();
573 QDataStream& operator>>(QDataStream &in, ReferenceBlock &b) {
576 ReferenceInterface* iface;
581 in.setVersion(QDataStream::Qt_5_0);
595 cout << qPrintable(b.name) << " has " << nb << " parameters" << endl;
596 for(int i=0;i<nb;i++) {
597 QString contextStr = "";
599 QString typeStr = "";
600 QString valueStr = "";
606 if (contextStr == "user") {
607 p = new BlockParameterUser(&b,nameStr,typeStr,valueStr);
609 else if (contextStr == "generic") {
610 p = new BlockParameterGeneric(&b,nameStr,typeStr,valueStr);
612 else if (contextStr == "port") {
613 QString ifaceStr = "";
615 p = new BlockParameterPort(&b,nameStr,valueStr,ifaceStr);
617 else if (contextStr == "wb") {
618 QString widthStr = "";
626 p = new BlockParameterWishbone(&b,nameStr,typeStr,widthStr,valueStr,wbAccess,wbValue,wbDuration);
633 for(int i=0;i<nb;i++) {
634 iface = new ReferenceInterface(&b);
639 iface->setType(type);
641 iface->setWidth(txt);
643 iface->setPurpose(val);
645 iface->setDirection(val);
647 iface->setMultiplicity(val);
652 iface->setClockIfaceName(clk);
653 iface->setClockIfaceType(clkType);
654 b.inputs.append(iface);
655 if (iface->getPurpose() == AbstractInterface::Data) {
656 QString ctlRefName = iface->getName()+"_enb";
657 ReferenceInterface* ctlRefIface = AI_TO_REF(b.getIfaceFromName(ctlRefName));
658 if (ctlRefIface != NULL) {
659 if (! ctlRefIface->setAssociatedIface(iface)) {
660 cerr << "Abnormal case while reading a reference block in library: cannot set associated control interface for data interface" << endl;
668 for(int i=0;i<nb;i++) {
669 iface = new ReferenceInterface(&b);
674 iface->setType(type);
676 iface->setWidth(txt);
678 iface->setPurpose(val);
680 iface->setDirection(val);
682 iface->setMultiplicity(val);
687 iface->setClockIfaceName(clk);
688 iface->setClockIfaceType(clkType);
689 b.outputs.append(iface);
690 if (iface->getPurpose() == AbstractInterface::Data) {
691 QString ctlRefName = iface->getName()+"_enb";
692 ReferenceInterface* ctlRefIface = AI_TO_REF(b.getIfaceFromName(ctlRefName));
693 if (ctlRefIface != NULL) {
694 if (! ctlRefIface->setAssociatedIface(iface)) {
695 cerr << "Abnormal case while reading a reference block in library" << endl;
703 for(int i=0;i<nb;i++) {
704 iface = new ReferenceInterface(&b);
709 iface->setType(type);
711 iface->setWidth(txt);
713 iface->setPurpose(val);
715 iface->setDirection(val);
717 iface->setMultiplicity(val);
722 iface->setClockIfaceName(clk);
723 iface->setClockIfaceType(clkType);
724 b.bidirs.append(iface);
730 void ReferenceBlock::checkInputPatternCompatibility() throw(Exception){
731 throw(Exception(INVALID_REFBLOCK_USE));
734 void ReferenceBlock::computeOutputPattern(int nbExec) throw(Exception) {
735 // does strictly nothing
736 throw(Exception(INVALID_REFBLOCK_USE));
739 void ReferenceBlock::computeAdmittanceDelays() throw(Exception) {
740 // does strictly nothing
741 throw(Exception(INVALID_REFBLOCK_USE));
745 void ReferenceBlock::generateVHDL(const QString& path) throw(Exception){
746 throw(Exception(INVALID_REFBLOCK_USE));
749 void ReferenceBlock::generateComments(QTextStream& out, QDomElement &elt, QString coreFile) throw(Exception) {
750 throw(Exception(INVALID_REFBLOCK_USE));
753 void ReferenceBlock::generateLibraries(QTextStream& out, QDomElement &elt) throw(Exception) {
754 throw(Exception(INVALID_REFBLOCK_USE));
757 void ReferenceBlock::generateArchitecture(QTextStream& out, QDomElement &elt ) throw(Exception) {
758 throw(Exception(INVALID_REFBLOCK_USE));
761 void ReferenceBlock::generateController(QTextStream& out) throw(Exception) {
762 throw(Exception(INVALID_REFBLOCK_USE));
765 void ReferenceBlock::generateEntityOrComponentBody(QTextStream &out, int indentLevel, bool hasController) throw(Exception) {
766 throw(Exception(INVALID_REFBLOCK_USE));
769 QList<QString> ReferenceBlock::getExternalResources() {