From e40a5399ec7887c2606f18575c809b0d05b09278 Mon Sep 17 00:00:00 2001 From: stephane Domas Date: Mon, 30 Apr 2018 18:24:27 +0200 Subject: [PATCH] added clk/rst link when creating a block --- AbstractBlock.cpp | 65 ++++++---- AbstractBlock.h | 9 +- AbstractInterface.cpp | 7 ++ AbstractInterface.h | 3 +- BlockLibraryWidget.cpp | 117 ++++++++++++++++-- BlockLibraryWidget.h | 15 ++- BoxItem.cpp | 21 ++-- Dispatcher.cpp | 124 +++++++++++++++----- Dispatcher.h | 3 +- FunctionalBlock.cpp | 14 +-- FunctionalBlock.h | 6 +- GroupBlock.cpp | 45 +++---- GroupItem.cpp | 26 ++-- InterfacePropertiesDialog.cpp | 47 ++++++++ InterfacePropertiesDialog.h | 22 ++++ InterfacePropertiesWindow.cpp | 39 ------ InterfacePropertiesWindow.h | 22 ---- MainWindow.cpp | 4 +- ReferenceBlock.cpp | 12 +- SpecialBlock.cpp | 51 ++++++++ SpecialBlock.h | 47 ++++++++ blast.creator.user | 2 +- blast.files | 6 +- blastconfig.xml | 5 +- lib/implementations/impls.bmf | Bin 6865 -> 6865 bytes lib/references/apf27-wb-master.xml | 2 +- lib/references/clkdomain_convert_1024x8.xml | 6 +- lib/references/clkrstgen.xml | 6 +- lib/references/references.bmf | Bin 25136 -> 25194 bytes object-files.txt | 5 +- 30 files changed, 521 insertions(+), 210 deletions(-) create mode 100644 InterfacePropertiesDialog.cpp create mode 100644 InterfacePropertiesDialog.h delete mode 100644 InterfacePropertiesWindow.cpp delete mode 100644 InterfacePropertiesWindow.h create mode 100644 SpecialBlock.cpp create mode 100644 SpecialBlock.h diff --git a/AbstractBlock.cpp b/AbstractBlock.cpp index 3c3dd55..116494e 100644 --- a/AbstractBlock.cpp +++ b/AbstractBlock.cpp @@ -243,51 +243,70 @@ QList AbstractBlock::getWishboneParameters() { return lst; } +void AbstractBlock::connectClock(QString clkName, int idGen) throw(Exception) { + GroupBlock* parentBlock = AB_TO_GRP(parent); + ConnectedInterface* fromClk = NULL; + ConnectedInterface* toClk = AI_TO_CON(getIfaceFromName(clkName)); -void AbstractBlock::connectClkReset() throw(Exception) { - - GroupBlock* parentBlock = AB_TO_GRP(parent); - - - - QList lstClk = getInterfaces(AbstractInterface::Input,AbstractInterface::Clock); - QList lstRst = getInterfaces(AbstractInterface::Input,AbstractInterface::Reset); + if (parentBlock->isTop()) { + QString genName = "clkrstgen_" + QString::number(idGen); + AbstractBlock* clkrstgen = parentBlock->getFunctionalBlockByName(genName); + if (clkrstgen == NULL) { + throw(Exception(IFACE_TOP_NOCLKRSTGEN,this)); + } + else { + fromClk = AI_TO_CON(clkrstgen->getIfaceFromName("clk")); + } + cout << "connecting clock for " << qPrintable(name) << " to " << qPrintable(genName) << endl; + } + else { + // searching for ext_clk_idGen + QString name = "ext_clk_"+QString::number(idGen); + fromClk = AI_TO_CON(parentBlock->getIfaceFromName(name)); + cout << "connecting clk for child " << qPrintable(name) << " of " << qPrintable(parentBlock->getName()) << endl; + } - if ((lstClk.isEmpty()) || (lstRst.isEmpty())) { - throw(Exception(IFACE_GROUP_NOCLKRST,this)); + if (fromClk == NULL) { + throw(Exception(IFACE_GROUP_NOCLKRST,parentBlock)); + } + else { + fromClk->connectTo(toClk); + cout << "connection done between " << qPrintable(toClk->getConnectedFrom()->getOwner()->getName()) << "/" << qPrintable(toClk->getConnectedFrom()->getName()); + cout << " and " << qPrintable(toClk->getOwner()->getName()) << "/" << qPrintable(toClk->getName()) << endl; } +} - ConnectedInterface* toClk = AI_TO_CON(lstClk.at(0)); - ConnectedInterface* toRst = AI_TO_CON(lstRst.at(0)); +void AbstractBlock::connectReset(QString rstName, int idGen) throw(Exception) { - ConnectedInterface* fromClk = NULL; + GroupBlock* parentBlock = AB_TO_GRP(parent); ConnectedInterface* fromRst = NULL; + ConnectedInterface* toRst = AI_TO_CON(getIfaceFromName(rstName)); if (parentBlock->isTop()) { - AbstractBlock* clkrstgen = parentBlock->getFunctionalBlockByName("clkrstgen"); + QString genName = "clkrstgen_" + QString::number(idGen); + AbstractBlock* clkrstgen = parentBlock->getFunctionalBlockByName(genName); if (clkrstgen == NULL) { throw(Exception(IFACE_TOP_NOCLKRSTGEN,this)); } else { - fromClk = AI_TO_CON(clkrstgen->getIfaceFromName("clk")); fromRst = AI_TO_CON(clkrstgen->getIfaceFromName("reset")); } - cout << "connecting clk/rst for " << qPrintable(name) << " to clkrstgen" << endl; + cout << "connecting reset for " << qPrintable(name) << " to " << qPrintable(genName) << endl; } else { - fromClk = AI_TO_CON(parentBlock->getIfaceFromName("clk")); - fromRst = AI_TO_CON(parentBlock->getIfaceFromName("reset")); - cout << "connecting clk/rst for child " << qPrintable(name) << " of " << qPrintable(parentBlock->getName()) << endl; + QString name = "ext_reset_"+QString::number(idGen); + fromRst = AI_TO_CON(parentBlock->getIfaceFromName(name)); + cout << "connecting reset for child " << qPrintable(name) << " of " << qPrintable(parentBlock->getName()) << endl; } - if ((fromClk == NULL) || (fromRst == NULL)) { + + if (fromRst == NULL) { throw(Exception(IFACE_GROUP_NOCLKRST,parentBlock)); } else { - fromClk->connectTo(toClk); fromRst->connectTo(toRst); - cout << "connection done between " << qPrintable(toClk->getConnectedFrom()->getOwner()->getName()) << "/" << qPrintable(toClk->getConnectedFrom()->getName()); - cout << " and " << qPrintable(toClk->getOwner()->getName()) << "/" << qPrintable(toClk->getName()) << endl; + cout << "connection done between " << qPrintable(toRst->getConnectedFrom()->getOwner()->getName()) << "/" << qPrintable(toRst->getConnectedFrom()->getName()); + cout << " and " << qPrintable(toRst->getOwner()->getName()) << "/" << qPrintable(toRst->getName()) << endl; } } diff --git a/AbstractBlock.h b/AbstractBlock.h index 0ca5536..fe3f90e 100644 --- a/AbstractBlock.h +++ b/AbstractBlock.h @@ -59,7 +59,14 @@ public: bool isWBConfigurable(); // others - void connectClkReset() throw(Exception); + + /*! + * \brief connectClkReset connects the clock and reset inputs to a clkrstgen block or the the group ifaces + * \param idBlockClk is the id of the clock interface (there may be severals) + * \param idGen is the id of the clkrstgen block + */ + void connectClock(QString clkName, int idGen = 0) throw(Exception); + void connectReset(QString rstName, int idGen = 0) throw(Exception); virtual QList getExternalResources() = 0; // returns the list of all external files needed for VHDL generation virtual void generateVHDL(const QString& path) throw(Exception) = 0; // main entry to generate the VHDL code void generateComponent(QTextStream& out, bool hasController=false) throw(Exception); // generate the component using reference diff --git a/AbstractInterface.cpp b/AbstractInterface.cpp index 03a1a98..3295dc8 100644 --- a/AbstractInterface.cpp +++ b/AbstractInterface.cpp @@ -243,6 +243,13 @@ bool AbstractInterface::setAssociatedIface(AbstractInterface* iface) { return true; } +AbstractInterface* AbstractInterface::getClockIface() { + if (clkIfaceType == ClockName) { + return owner->getIfaceFromName(clkIface); + } + return NULL; +} + bool AbstractInterface::setClockIface(QString name) { /* 2 cases : * - this is a Data interface diff --git a/AbstractInterface.h b/AbstractInterface.h index 0e167db..95ad4bb 100644 --- a/AbstractInterface.h +++ b/AbstractInterface.h @@ -54,8 +54,9 @@ public : QString getDirectionString(); inline AbstractBlock *getOwner() { return owner;} inline AbstractInterface* getAssociatedIface() { return associatedIface; } - inline QString getClockIface() { return clkIface; } + inline QString getClockIfaceString() { return clkIface; } inline int getClockIfaceType() { return clkIfaceType; } + AbstractInterface* getClockIface(); double getDoubleWidth() throw(QException); diff --git a/BlockLibraryWidget.cpp b/BlockLibraryWidget.cpp index e8f5c6d..62603b4 100644 --- a/BlockLibraryWidget.cpp +++ b/BlockLibraryWidget.cpp @@ -9,6 +9,11 @@ BlockLibraryWidget::BlockLibraryWidget(Dispatcher* _dispatcher, dispatcher = _dispatcher; params = _params; + nbClock = 0; + comboClkGen = NULL; + nbRst = 0; + comboRstGen = NULL; + // creating the widget : tree, buttons, ... QBoxLayout* layout = new QBoxLayout(QBoxLayout::TopToBottom, this); tree = new QTreeWidget(this); @@ -17,13 +22,23 @@ BlockLibraryWidget::BlockLibraryWidget(Dispatcher* _dispatcher, buttonAdd->setEnabled(false); comboScenes = new QComboBox(); - QHBoxLayout* layBottom = new QHBoxLayout; - layBottom->addWidget(buttonAdd); - layBottom->addWidget(comboScenes); + QHBoxLayout* layAdd = new QHBoxLayout; + layAdd->addWidget(buttonAdd); + layAdd->addWidget(comboScenes); + + layClkRst = new QGridLayout; + + boxClkRst = new QGroupBox("Clock/reset connection"); + boxClkRst->setLayout(layClkRst); + + QVBoxLayout* layBottom = new QVBoxLayout; + layBottom->addLayout(layAdd); + layBottom->addWidget(boxClkRst); + connect(tree, SIGNAL(itemClicked(QTreeWidgetItem*,int)), this, SLOT(clicked())); connect(tree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(doubleClicked())); - connect(buttonAdd, SIGNAL(clicked()), this, SLOT(addClicked())); + connect(buttonAdd, SIGNAL(clicked()), this, SLOT(addClicked())); BlockCategory* cat = params->categoryTree->searchCategory(0); @@ -36,7 +51,7 @@ BlockLibraryWidget::BlockLibraryWidget(Dispatcher* _dispatcher, layout->addLayout(layBottom); this->setLayout(layout); - this->setFixedSize(300,230); + //this->setFixedSize(300,230); } @@ -87,11 +102,23 @@ void BlockLibraryWidget::addClicked() { QTreeWidgetItem *item = tree->selectedItems().at(0); if(item->data(1,Qt::DisplayRole).isValid() && item->data(2,Qt::DisplayRole).isValid()){ - int idParent = item->data(1,Qt::DisplayRole).toInt(); + int idCat = item->data(1,Qt::DisplayRole).toInt(); int idBlock = item->data(2,Qt::DisplayRole).toInt(); - QVariant v = comboScenes->currentData(); + QVariant v = comboScenes->currentData(); + cout << "adding block to scene " << v.toInt() << endl; - dispatcher->addBlock(Dispatcher::Design, idParent, idBlock, v.toInt()); + + QHash clkRstToGen; + for(int i=0;irowCount();i++) { + QLayoutItem* item = layClkRst->itemAtPosition(i,0); + QLabel* lab = (QLabel *)(item->widget()); + item = layClkRst->itemAtPosition(i,1); + QComboBox* combo = (QComboBox *)(item->widget()); + clkRstToGen.insert(lab->text(),combo->currentIndex()); + } + + + dispatcher->addBlock(Dispatcher::Design, idCat, idBlock, v.toInt(), clkRstToGen); } // only take the first selected @@ -102,10 +129,15 @@ void BlockLibraryWidget::addClicked() { void BlockLibraryWidget::clicked() { if(tree->selectedItems().length() > 0){ QTreeWidgetItem *item = tree->selectedItems().at(0); - if(item->data(1,Qt::DisplayRole).isValid()) + if(item->data(1,Qt::DisplayRole).isValid()) { buttonAdd->setEnabled(true); - else + int idParent = item->data(1,Qt::DisplayRole).toInt(); + int idBlock = item->data(2,Qt::DisplayRole).toInt(); + updateClkRst(idParent, idBlock); + } + else { buttonAdd->setEnabled(false); + } } } @@ -122,3 +154,68 @@ void BlockLibraryWidget::updateComboScene() { comboScenes->addItem(iter.value(),QVariant(iter.key())); } } + +void BlockLibraryWidget::updateClkRst(int idCat, int idBlock) { + + while (layClkRst->count() > 0) { + QWidget* widget = layClkRst->itemAt(0)->widget(); + layClkRst->removeWidget(widget); + delete widget; + } + + if (nbClock != 0) { + for(int i=0;ideleteLater(); + } + delete [] comboClkGen; + } + if (nbRst != 0) { + for(int i=0;ideleteLater(); + } + delete [] comboRstGen; + } + + ReferenceBlock* ref = params->getReferenceBlock(idCat,idBlock); + QList lstClocks = ref->getInterfaces(AbstractInterface::Input, AbstractInterface::Clock); + nbClock = lstClocks.size(); + QList lstRst = ref->getInterfaces(AbstractInterface::Input, AbstractInterface::Reset); + nbRst = lstRst.size(); + + comboClkGen = new QComboBox*[lstClocks.size()]; + for(int i=0;iclocks) { + name = "ext_clk_"+QString::number(id)+" ("; + name += QString::number(d) + " MHz)"; + comboClkGen[i]->addItem(name); + id++; + } + } + + comboRstGen = new QComboBox*[lstRst.size()]; + for(int i=0;iclocks.size();j++) { + name = "ext_rst_"+QString::number(j); + comboRstGen[i]->addItem(name); + } + } + layClkRst->addWidget(new QLabel("Clock/Reset name"), 0, 0); + layClkRst->addWidget(new QLabel("connect to"), 0, 1); + int row = 1; + foreach(AbstractInterface* iface, lstClocks) { + layClkRst->addWidget(new QLabel(iface->getName()), row,0); + layClkRst->addWidget(comboClkGen[row-1],row, 1); + row++; + } + foreach(AbstractInterface* iface, lstRst) { + layClkRst->addWidget(new QLabel(iface->getName()), row,0); + layClkRst->addWidget(comboRstGen[row-1-nbClock],row, 1); + row++; + } + +} diff --git a/BlockLibraryWidget.h b/BlockLibraryWidget.h index 43ed5fc..bf17316 100644 --- a/BlockLibraryWidget.h +++ b/BlockLibraryWidget.h @@ -25,11 +25,12 @@ public: public slots: void updateComboScene(); + void updateClkRst(int idCat, int idBlock); private slots: - void addClicked(); - void clicked(); - void doubleClicked(); + void addClicked(); + void clicked(); + void doubleClicked(); private: @@ -38,6 +39,14 @@ private: QTreeWidget* tree; QPushButton* buttonAdd; QComboBox* comboScenes; + + QGroupBox* boxClkRst; + QGridLayout* layClkRst; + QComboBox** comboClkGen; + int nbClock; + QComboBox** comboRstGen; + int nbRst; + // other attributes void addChild(BlockCategory *catParent, QTreeWidgetItem* itemParent); diff --git a/BoxItem.cpp b/BoxItem.cpp index deee2cc..ae15002 100644 --- a/BoxItem.cpp +++ b/BoxItem.cpp @@ -406,13 +406,19 @@ void BoxItem::mouseMoveEvent(QGraphicsSceneMouseEvent *event) { //cout << "block abs. pos: " << absPos.x() << "," << absPos.y() << " | "; //cout << "block current. pos: " << currentPosition.x() << "," << currentPosition.y() << " | "; - if ((gapX < 0) && (absPos.x() == marginConn)) { + if ((position == Left) || (position == Right)) { + gapX = 0; + } + else if ((gapX < 0) && (absPos.x() == marginConn)) { gapX = 0; } else if (absPos.x()+gapX < marginConn) { gapX = marginConn-absPos.x(); } - if ((gapY < 0) && (absPos.y() == marginConn)) { + if ((position == Top) || (position == Bottom)) { + gapY = 0; + } + else if ((gapY < 0) && (absPos.y() == marginConn)) { gapY = 0; } else if (absPos.y()+gapY < marginConn) { @@ -985,17 +991,6 @@ void BoxItem::loadFunctional(QDomElement funcElement) throw(Exception) { functionalBlock->addInterface(ctlIface); } } - // connect clk and rst to group clk/rst or to clkrstgen - if ((name != "clkrstgen") && (parentGroupBlock != NULL)) { - try { - functionalBlock->connectClkReset(); - } - catch(Exception e) { - AbstractBlock* source = (AbstractBlock *)(e.getSource()); - cerr << qPrintable(source->getName()) << ":" << qPrintable(e.getMessage()) << endl; - throw(e); - } - } // creating InterfaceItem createInterfaceItems(); diff --git a/Dispatcher.cpp b/Dispatcher.cpp index 8d11649..94cdfb6 100644 --- a/Dispatcher.cpp +++ b/Dispatcher.cpp @@ -29,7 +29,9 @@ #include "DelayInputModifier.h" -#include "InterfacePropertiesWindow.h" +#include "InterfacePropertiesDialog.h" + +#include int Dispatcher::sceneCounter = 0; @@ -621,7 +623,7 @@ void Dispatcher::duplicateInterfaceItem(Context context, InterfaceItem *item) { } -BoxItem* Dispatcher::addBlock(Context context, int idCategory, int idBlock, int idScene) { +BoxItem* Dispatcher::addBlock(Context context, int idCategory, int idBlock, int idScene, QHash clkRstToGen) { static QString fctName = "Dispatcher::addBlock()"; #ifdef DEBUG_FCTNAME cout << "call to " << qPrintable(fctName) << endl; @@ -645,9 +647,26 @@ BoxItem* Dispatcher::addBlock(Context context, int idCategory, int idBlock, int scene->createSourceItem(newOne); } else { + GroupBlock* group = AB_TO_GRP(scene->getGroupItem()->getRefBlock()); FunctionalBlock* newOne = params->getGraph()->createFunctionalBlock(group, ref, true); + + // creating the box item item = scene->createBoxItem(newOne); + if (params->autoConnMainClk) { + // for now just use the first one + QHashIterator iter(clkRstToGen); + while (iter.hasNext()) { + iter.next(); + AbstractInterface* iface = newOne->getIfaceFromName(iter.key()); + if (iface->getPurpose() == AbstractInterface::Clock) { + newOne->connectClock(iface->getName(), iter.value()); + } + else if (iface->getPurpose() == AbstractInterface::Reset) { + newOne->connectReset(iface->getName(), iter.value()); + } + } + } params->blockToItem.insert(newOne,item); } params->unsaveModif = true; @@ -656,6 +675,67 @@ BoxItem* Dispatcher::addBlock(Context context, int idCategory, int idBlock, int return item; } +void Dispatcher::addClkRstGenBlock(Context context, double frequency) { + static QString fctName = "Dispatcher::addClkRstGenBlock()"; +#ifdef DEBUG_FCTNAME + cout << "call to " << qPrintable(fctName) << endl; +#endif + + + if (context == Design) { + + params->clocks.append(frequency); + + // get the top group + GroupBlock *group = params->getGraph()->getTopGroup(); + GroupScene *scene = topGroupWidget->getScene(); + + // creating the clkrstgen block + ReferenceBlock* ref = params->getHiddenReferenceBlock("clkrstgen"); + FunctionalBlock* newOne = params->getGraph()->createFunctionalBlock(group, ref, true); + + QString name = "clkrstgen_"; + name += QString::number(params->clocks.size()-1); + newOne->setName(name); + + // creating the box item + BoxItem* item = scene->createBoxItem(newOne, BoxItem::Left, AbstractBoxItem::Dimension); + item->setVisible(false); + + ConnectedInterface* fromIfaceClk = NULL; + ConnectedInterface* fromIfaceReset = NULL; + QString clkName = "ext_clk_"+QString::number(params->clocks.size()-1); + QString rstName = "ext_reset_"+QString::number(params->clocks.size()-1); + fromIfaceClk = new GroupInterface(group,clkName, AbstractInterface::Input, AbstractInterface::Clock); + fromIfaceReset = new GroupInterface(group,rstName, AbstractInterface::Input, AbstractInterface::Reset); + group->addInterface(fromIfaceClk); + group->addInterface(fromIfaceReset); + // creating top group ext_clk iface item + GroupItem* groupItem = scene->getGroupItem(); + InterfaceItem* fromIfaceItemClk = new InterfaceItem(0.5 , Parameters::West, fromIfaceClk, groupItem, params, true); + groupItem->addInterfaceItem(fromIfaceItemClk,true); + // creating top group ext_reset iface item + InterfaceItem* fromIfaceItemReset = new InterfaceItem(0.5 , Parameters::West, fromIfaceReset, groupItem, params, false); + groupItem->addInterfaceItem(fromIfaceItemReset,true); + // connecting ext_clk iface items + InterfaceItem* toIfaceItemClk = item->searchInterfaceItemByName("ext_clk"); + if (toIfaceItemClk == NULL) { + cerr << "Abnormal case while connecting top group ext_clk to clkrstgen" << endl; + } + createConnection(context,fromIfaceItemClk, toIfaceItemClk, false); + // connecting ext_reset iface items + InterfaceItem* toIfaceItemReset = item->searchInterfaceItemByName("ext_reset"); + if (toIfaceItemReset == NULL) { + cerr << "Abnormal case while connecting top group ext_reset to clkrstgen" << endl; + } + createConnection(context,fromIfaceItemReset, toIfaceItemReset, false); + + params->blockToItem.insert(newOne,item); + params->unsaveModif = true; + } +} + + GroupWidget *Dispatcher::createTopScene(Context context){ static QString fctName = "Dispatcher::createTopScene()"; @@ -690,35 +770,12 @@ GroupWidget *Dispatcher::createTopScene(Context context){ if (context == Design) { - // creating the clkrstgen block - ReferenceBlock* ref = params->getHiddenReferenceBlock("clkrstgen"); - FunctionalBlock* newOne = params->getGraph()->createFunctionalBlock(topBlock, ref, true); - // creating the clkrstgen item - BoxItem* clkResetItem = scene->createBoxItem(newOne, BoxItem::TopLeft, AbstractBoxItem::Position | AbstractBoxItem::Dimension, BoxItem::NoSpan); - params->blockToItem.insert(newOne,clkResetItem); - // creating top group ext_clk iface item - ConnectedInterface* fromIfaceClk = AI_TO_CON(topBlock->getIfaceFromName("ext_clk")); - InterfaceItem* fromIfaceItemClk = new InterfaceItem(0.5 , Parameters::West, fromIfaceClk, group, params, true); - group->addInterfaceItem(fromIfaceItemClk,true); - // creating top group ext_reset iface item - ConnectedInterface* fromIfaceReset = AI_TO_CON(topBlock->getIfaceFromName("ext_reset")); - InterfaceItem* fromIfaceItemReset = new InterfaceItem(0.5 , Parameters::West, fromIfaceReset, group, params, false); - group->addInterfaceItem(fromIfaceItemReset,true); - // connecting ext_clk iface items - InterfaceItem* toIfaceItemClk = clkResetItem->searchInterfaceItemByName("ext_clk"); - if (toIfaceItemClk == NULL) { - cerr << "Abnormal case while connecting top group ext_clk to clkrstgen" << endl; - } - createConnection(context,fromIfaceItemClk, toIfaceItemClk, false); - // connecting ext_reset iface items - InterfaceItem* toIfaceItemReset = clkResetItem->searchInterfaceItemByName("ext_reset"); - if (toIfaceItemReset == NULL) { - cerr << "Abnormal case while connecting top group ext_reset to clkrstgen" << endl; - } - createConnection(context,fromIfaceItemReset, toIfaceItemReset, false); + // create clkrstgen + double freq = params->clocks.at(0); + params->clocks.clear(); + addClkRstGenBlock(context,freq); } - groupList.append(topGroupWidget); return topGroupWidget; } @@ -728,11 +785,15 @@ GroupWidget* Dispatcher::addNewEmptyGroup(Context context, GroupScene* scene, bo #ifdef DEBUG_FCTNAME cout << "call to " << qPrintable(fctName) << endl; #endif + bool createIfaces = true; + if (context == Load) { + createIfaces = false; + } // getting the parent block in the graph GroupBlock* parent = AB_TO_GRP(scene->getGroupItem()->getRefBlock()); cout << "new group : parent = "<< qPrintable(parent->getName()) << endl; - GroupBlock* groupBlock = params->getGraph()->createChildGroupBlock(parent); + GroupBlock* groupBlock = params->getGraph()->createChildGroupBlock(parent, createIfaces); cout << "new group : child = "<< qPrintable(groupBlock->getName()) << ", child of " << qPrintable(groupBlock->getParent()->getName()) << endl; // creating the BlockItem in the scene BoxItem* newItem = scene->createBoxItem(groupBlock); @@ -1204,7 +1265,8 @@ void Dispatcher::showBlocksLibrary(){ } void Dispatcher::showProperties(Context context, InterfaceItem *inter) { - new InterfacePropertiesWindow(inter); + QDialog* dial = new InterfacePropertiesDialog(inter); + dial->exec(); } /* connectInterToGroup() : diff --git a/Dispatcher.h b/Dispatcher.h index 4354466..0fa74bd 100644 --- a/Dispatcher.h +++ b/Dispatcher.h @@ -94,7 +94,8 @@ public: /************************** * block ops *************************/ - BoxItem* addBlock(Context context, int idCategory, int idBlock, int idScene); + BoxItem* addBlock(Context context, int idCategory, int idBlock, int idScene, QHash clkRstToGen ); + void addClkRstGenBlock(Context context, double frequency); void removeBoxItem(Context context, BoxItem* item); void duplicateBoxItem(Context context, BoxItem* item); void renameFunctionalBlock(Context context, BoxItem* item); diff --git a/FunctionalBlock.cpp b/FunctionalBlock.cpp index 65c8df8..ab9611d 100644 --- a/FunctionalBlock.cpp +++ b/FunctionalBlock.cpp @@ -100,7 +100,7 @@ void FunctionalBlock::populate() { addInterface(inter); /* WARNING FOR THE FUTURE : in case of there are several clock interfaces ofr that block - it would be a godd idea to make the user choose which one + it would be a good idea to make the user choose which one must be connected to defautl clk. Presently, the first encountered is chosen */ @@ -126,18 +126,6 @@ void FunctionalBlock::populate() { } } } - - // connect clk and rst to group clk/rst or to clkrstgen - if ((name != "clkrstgen") && (parent != NULL)) { - try { - connectClkReset(); - } - catch(Exception e) { - AbstractBlock* source = (AbstractBlock *)(e.getSource()); - cerr << qPrintable(source->getName()) << ":" << qPrintable(e.getMessage()) << endl; - throw(e); - } - } } QString FunctionalBlock::getReferenceXmlFile() { diff --git a/FunctionalBlock.h b/FunctionalBlock.h index 8e0e2ed..8c469d5 100644 --- a/FunctionalBlock.h +++ b/FunctionalBlock.h @@ -67,17 +67,17 @@ public: void computeOutputPattern(int nbExec = -1) throw(Exception); void computeAdmittanceDelays() throw(Exception); // compute differences between IP and admittance -private: +protected: // patterns void createDelta() throw(Exception); void createConsumptionPattern() throw(Exception); // initialize a QList for each interface from patterns defined in implementation void createProductionPattern() throw(Exception); // initialize a QList for each interface from patterns defined in implementation void createProductionCounter() throw(Exception); // initialize a QList from counter defined in implementation void createAdmittance(int nbExec) throw(Exception); // initialize a QList from consumption pattern and delta + void createInputPattern() throw(Exception); void clearConsumptionPattern(); - void clearProductionPattern(); - void createInputPattern() throw(Exception); + void clearProductionPattern(); void clearInputPattern(); void clearOutputPattern(); void clearAdmittanceDelays(); diff --git a/GroupBlock.cpp b/GroupBlock.cpp index ea7f2f7..fda0546 100644 --- a/GroupBlock.cpp +++ b/GroupBlock.cpp @@ -12,41 +12,42 @@ int GroupBlock::counter = 1; GroupBlock::GroupBlock(GroupBlock *_parent, bool createIfaces) throw(Exception) : AbstractBlock() { + parent = _parent; GroupInterface* clk = NULL; GroupInterface* rst = NULL; // force topGroup to false if this group has a parent - if (_parent != NULL) { + if (parent != NULL) { topGroup = false; name = QString("sub_group")+"_"+QString::number(counter++); - // creating clk/rst interfaces - clk = new GroupInterface(this,"clk", AbstractInterface::Input, AbstractInterface::Clock); - rst = new GroupInterface(this,"reset", AbstractInterface::Input, AbstractInterface::Reset); - addInterface(clk); - addInterface(rst); - - try { - connectClkReset(); - } - catch(Exception e) { - AbstractBlock* source = (AbstractBlock *)(e.getSource()); - cerr << qPrintable(source->getName()) << ":" << qPrintable(e.getMessage()) << endl; - throw(e); - } } else { topGroup = true; name = QString("top_group"); // creating external clk/rst interfaces - clk = new GroupInterface(this,"ext_clk", AbstractInterface::Input, AbstractInterface::Clock); - rst = new GroupInterface(this,"ext_reset", AbstractInterface::Input, AbstractInterface::Reset); - addInterface(clk); - addInterface(rst); - // creating clkrstgen block and connecting it to this: done in Dispatcher since this has no access to library - cout << "created ext_clk and reset ifaces for top group" << endl; } - parent = _parent; + if (createIfaces) { + if (topGroup) { + clk = new GroupInterface(this,"ext_clk_0", AbstractInterface::Input, AbstractInterface::Clock); + rst = new GroupInterface(this,"ext_reset_0", AbstractInterface::Input, AbstractInterface::Reset); + addInterface(clk); + addInterface(rst); + } + else { + // get all clock and reset from parent + QList lstClk = parent->getInterfaces(AbstractInterface::Input, AbstractInterface::Clock); + QList lstRst = parent->getInterfaces(AbstractInterface::Input, AbstractInterface::Reset); + foreach(AbstractInterface* iface, lstClk) { + clk = new GroupInterface(this,iface->getName(),AbstractInterface::Input, AbstractInterface::Clock); + addInterface(clk); + } + foreach(AbstractInterface* iface, lstRst) { + rst = new GroupInterface(this,iface->getName(),AbstractInterface::Input, AbstractInterface::Reset); + addInterface(rst); + } + } + } } GroupBlock::~GroupBlock() { diff --git a/GroupItem.cpp b/GroupItem.cpp index c165d02..385b352 100644 --- a/GroupItem.cpp +++ b/GroupItem.cpp @@ -683,6 +683,7 @@ void GroupItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { QAction* removeAction = NULL; QAction* renameAction = NULL; QAction* showParameters = NULL; + QAction* addExtClkAction = NULL; InterfaceItem* ifaceItem = getInterfaceItemFromCursor(event->pos().x(), event->pos().y()); @@ -713,6 +714,10 @@ void GroupItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { showParameters = menu.addAction("Show parameters"); } renameAction = menu.addAction("Rename"); + if (refBlock->isTopGroupBlock()) { + addExtClkAction = menu.addAction("Add new external clock/reset"); + } + } QAction* selectedAction = menu.exec(event->screenPos()); @@ -732,7 +737,13 @@ void GroupItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event) { } else if(selectedAction == showParameters) { new ParametersWindow(refBlock, params, NULL); - } + } + else if (selectedAction == addExtClkAction) { + bool ok = false; + double freq = QInputDialog::getDouble(NULL,"Adding a clkrstgen","External clock frequency (in MHz)",100,0,100000,1,&ok); + if (!ok) return; + dispatcher->addClkRstGenBlock(Dispatcher::Design, freq); + } } InterfaceItem* GroupItem::isHoverInterface(QPointF point) { @@ -802,15 +813,16 @@ void GroupItem::load(QDomElement groupElement) throw(Exception) { if(!ok) throw(Exception(PROJECTFILE_CORRUPTED)); GroupInterface *groupIface = new GroupInterface(groupBlock,name,direction,purpose); - GroupInterface *groupCtlIface = new GroupInterface(groupBlock,name+"_enb",direction,AbstractInterface::Control); - groupCtlIface->setAssociatedIface(groupIface); - + groupBlock->addInterface(groupIface); InterfaceItem *interfaceItem = new InterfaceItem(position,orientation,groupIface,this,params); interfaceItem->setId(id); - - groupBlock->addInterface(groupIface); - groupBlock->addInterface(groupCtlIface); addInterfaceItem(interfaceItem, false); + + if (purpose == AbstractInterface::Data) { + GroupInterface *groupCtlIface = new GroupInterface(groupBlock,name+"_enb",direction,AbstractInterface::Control); + groupCtlIface->setAssociatedIface(groupIface); + groupBlock->addInterface(groupCtlIface); + } cout << "interface add to " << groupBlock->getName().toStdString() << endl; } diff --git a/InterfacePropertiesDialog.cpp b/InterfacePropertiesDialog.cpp new file mode 100644 index 0000000..cc0a3ad --- /dev/null +++ b/InterfacePropertiesDialog.cpp @@ -0,0 +1,47 @@ +#include "InterfacePropertiesDialog.h" + +#include "ConnectedInterface.h" + +InterfacePropertiesDialog::InterfacePropertiesDialog(InterfaceItem *_inter, QWidget *parent) : QDialog(parent) { + inter = _inter; + QGroupBox* box = new QGroupBox("Properties"); + okButton = new QPushButton(tr("OK")); + + QGridLayout* layProp = new QGridLayout; + + int w = inter->refInter->getWidth(); + QString wStr = ""; + if (w == -1) { + wStr = "invalid_size"; + } + else { + if (w == 0) w++; // 0 means a boolean thus, size of 1 bit + wStr.setNum(w); + } + + layProp->addWidget(new QLabel("Interface properties"), 0, 0); + layProp->addWidget(new QLabel(" "), 1, 0); + + layProp->addWidget(new QLabel("Name :"), 2, 0); + layProp->addWidget(new QLabel(inter->getName()), 2, 1); + layProp->addWidget(new QLabel("Width :"), 3, 0); + layProp->addWidget(new QLabel(wStr), 3, 1); + layProp->addWidget(new QLabel("Direction :"), 4, 0); + layProp->addWidget(new QLabel(inter->refInter->getDirectionString()), 4, 1); + layProp->addWidget(new QLabel("Purpose :"), 5, 0); + layProp->addWidget(new QLabel(inter->refInter->getPurposeString()), 5, 1); + layProp->addWidget(new QLabel("Type :"), 6, 0); + layProp->addWidget(new QLabel(inter->refInter->getTypeString()), 6, 1); + + QHBoxLayout* layBottom = new QHBoxLayout; + layBottom->addStretch(); + layBottom->addWidget(okButton); + + QVBoxLayout* layAll = new QVBoxLayout; + layAll->addLayout(layProp); + layAll->addLayout(layBottom); + + setLayout(layAll); + + connect(okButton,SIGNAL(clicked()),this, SLOT(accept())); +} diff --git a/InterfacePropertiesDialog.h b/InterfacePropertiesDialog.h new file mode 100644 index 0000000..14a83bf --- /dev/null +++ b/InterfacePropertiesDialog.h @@ -0,0 +1,22 @@ +#ifndef __INTERFACEPROPERTIESWINDOW_H__ +#define __INTERFACEPROPERTIESWINDOW_H__ + +#include + +#include "InterfaceItem.h" + +class InterfacePropertiesDialog : public QDialog { + + Q_OBJECT + +public: + InterfacePropertiesDialog(InterfaceItem *_inter, QWidget *parent = 0); + +private: + + QPushButton *okButton; + InterfaceItem *inter; + +}; + +#endif // INTERFACEPROPERTIESWINDOW_H diff --git a/InterfacePropertiesWindow.cpp b/InterfacePropertiesWindow.cpp deleted file mode 100644 index 5a571c0..0000000 --- a/InterfacePropertiesWindow.cpp +++ /dev/null @@ -1,39 +0,0 @@ -#include "InterfacePropertiesWindow.h" - -#include "ConnectedInterface.h" - -InterfacePropertiesWindow::InterfacePropertiesWindow(InterfaceItem *_inter, QWidget *parent) : - QWidget(parent) -{ - inter = _inter; - - layout = new QGridLayout; - - int w = inter->refInter->getWidth(); - QString wStr = ""; - if (w == -1) { - wStr = "invalid_size"; - } - else { - if (w == 0) w++; // 0 means a boolean thus, size of 1 bit - wStr.setNum(w); - } - - layout->addWidget(new QLabel("Interface properties"), 0, 0); - layout->addWidget(new QLabel(" "), 1, 0); - - layout->addWidget(new QLabel("Name :"), 2, 0); - layout->addWidget(new QLabel(inter->getName()), 2, 1); - layout->addWidget(new QLabel("Width :"), 3, 0); - layout->addWidget(new QLabel(wStr), 3, 1); - layout->addWidget(new QLabel("Direction :"), 4, 0); - layout->addWidget(new QLabel(inter->refInter->getDirectionString()), 4, 1); - layout->addWidget(new QLabel("Purpose :"), 5, 0); - layout->addWidget(new QLabel(inter->refInter->getPurposeString()), 5, 1); - layout->addWidget(new QLabel("Type :"), 6, 0); - layout->addWidget(new QLabel(inter->refInter->getTypeString()), 6, 1); - - this->setLayout(layout); - - show(); -} diff --git a/InterfacePropertiesWindow.h b/InterfacePropertiesWindow.h deleted file mode 100644 index 3b372a9..0000000 --- a/InterfacePropertiesWindow.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef __INTERFACEPROPERTIESWINDOW_H__ -#define __INTERFACEPROPERTIESWINDOW_H__ - -#include - -#include "InterfaceItem.h" - -class InterfacePropertiesWindow : public QWidget -{ - Q_OBJECT -public: - explicit InterfacePropertiesWindow(InterfaceItem *_inter, QWidget *parent = 0); - -private: - QGridLayout *layout; - InterfaceItem *inter; - - - -}; - -#endif // INTERFACEPROPERTIESWINDOW_H diff --git a/MainWindow.cpp b/MainWindow.cpp index 2b8584f..c7ff634 100644 --- a/MainWindow.cpp +++ b/MainWindow.cpp @@ -319,7 +319,7 @@ void MainWindow::slotLoadProject(){ GroupWidget* topGroup = dispatcher->loadProject(params->projectFile); if (topGroup != NULL) { addTopGroup(topGroup); - library->updateComboScene(); + library->updateComboScene(); params->isCurrentProject = true; enableProjectActions(true, PROJECT_CLOSE | PROJECT_SAVE | PROJECT_SAVEAS | PROJECT_LIB, OP_RAZ); enableAnalysisActions(true, ANALYSIS_ANALYZE | ANALYSIS_GENERATE, OP_RAZ); @@ -346,7 +346,7 @@ void MainWindow::slotNewProject(){ enableAnalysisActions(true, ANALYSIS_ANALYZE | ANALYSIS_GENERATE, OP_RAZ); GroupWidget* topGroup = dispatcher->createTopScene(Dispatcher::Design); addTopGroup(topGroup); - library->updateComboScene(); + library->updateComboScene(); library->show(); params->isCurrentProject = true; } diff --git a/ReferenceBlock.cpp b/ReferenceBlock.cpp index 7e697eb..c2d9bff 100644 --- a/ReferenceBlock.cpp +++ b/ReferenceBlock.cpp @@ -473,7 +473,7 @@ QDataStream& operator<<(QDataStream &out, const ReferenceBlock &b) { toWrite << iface->getDirection(); toWrite << iface->getMultiplicity(); toWrite << iface->getClockIfaceType(); - toWrite << iface->getClockIface(); + toWrite << iface->getClockIfaceString(); } } // secondly write control ifaces @@ -487,7 +487,7 @@ QDataStream& operator<<(QDataStream &out, const ReferenceBlock &b) { toWrite << iface->getDirection(); toWrite << iface->getMultiplicity(); toWrite << iface->getClockIfaceType(); - toWrite << iface->getClockIface(); + toWrite << iface->getClockIfaceString(); } } // secondly, write other ifaces @@ -501,7 +501,7 @@ QDataStream& operator<<(QDataStream &out, const ReferenceBlock &b) { toWrite << iface->getDirection(); toWrite << iface->getMultiplicity(); toWrite << iface->getClockIfaceType(); - toWrite << iface->getClockIface(); + toWrite << iface->getClockIfaceString(); } } toWrite << b.outputs.size(); @@ -516,7 +516,7 @@ QDataStream& operator<<(QDataStream &out, const ReferenceBlock &b) { toWrite << iface->getDirection(); toWrite << iface->getMultiplicity(); toWrite << iface->getClockIfaceType(); - toWrite << iface->getClockIface(); + toWrite << iface->getClockIfaceString(); } } // secondly, write other ifaces @@ -530,7 +530,7 @@ QDataStream& operator<<(QDataStream &out, const ReferenceBlock &b) { toWrite << iface->getDirection(); toWrite << iface->getMultiplicity(); toWrite << iface->getClockIfaceType(); - toWrite << iface->getClockIface(); + toWrite << iface->getClockIfaceString(); } } toWrite << b.bidirs.size(); @@ -543,7 +543,7 @@ QDataStream& operator<<(QDataStream &out, const ReferenceBlock &b) { toWrite << iface->getDirection(); toWrite << iface->getMultiplicity(); toWrite << iface->getClockIfaceType(); - toWrite << iface->getClockIface(); + toWrite << iface->getClockIfaceString(); } out << blockData; diff --git a/SpecialBlock.cpp b/SpecialBlock.cpp new file mode 100644 index 0000000..8edff6f --- /dev/null +++ b/SpecialBlock.cpp @@ -0,0 +1,51 @@ +#include "SpecialBlock.h" + +SpecialBlock::SpecialBlock(SpecialType _type, GroupBlock* _parent, ReferenceBlock* _reference, bool createIfaces) throw(Exception) : FunctionalBlock(_parent, _reference, createIfaces) { + type = _type; +} + +SpecialBlock::~SpecialBlock() { +} + +void SpecialBlock::checkInputPatternCompatibility() throw(Exception) { + try { + switch(type) { + case ClockConvert : + checkInputPatternCompatibilityClockConvert(); + break; + case ClkRstGen: + checkInputPatternCompatibilityClkRstGen(); + break; + } + } + catch(Exception e) { + throw (e); + } +} + +void SpecialBlock::computeOutputPattern(int nbExec) throw(Exception) { + try { + switch(type) { + case ClockConvert : + computeOutputPatternClockConvert(nbExec); + break; + case ClkRstGen: + computeOutputPatternClkRstGen(nbExec); + break; + } + } + catch(Exception e) { + throw (e); + } +} + +void SpecialBlock::checkInputPatternCompatibilityClockConvert() throw(Exception) { +} +void SpecialBlock::computeOutputPatternClockConvert(int nbExec) throw(Exception) { +} + + +void SpecialBlock::checkInputPatternCompatibilityClkRstGen() throw(Exception) { +} +void SpecialBlock::computeOutputPatternClkRstGen(int nbExec) throw(Exception) { +} diff --git a/SpecialBlock.h b/SpecialBlock.h new file mode 100644 index 0000000..06c6459 --- /dev/null +++ b/SpecialBlock.h @@ -0,0 +1,47 @@ +#ifndef __SPECIALBLOCK_H__ +#define __SPECIALBLOCK_H__ + +#include + +#include + +#include "FunctionalBlock.h" +class FunctionalBlock; + +using namespace std; +using namespace Qt; + + + +class SpecialBlock : public FunctionalBlock { +public: + + enum SpecialType { ClockConvert = 0, ClkRstGen = 1 }; + + SpecialBlock(SpecialType _type, GroupBlock* _parent, ReferenceBlock* _reference, bool createIfaces = true) throw(Exception); + ~SpecialBlock(); + // getters + + // setters + + // testers + + // others + + // patterns + void checkInputPatternCompatibility() throw(Exception); + void computeOutputPattern(int nbExec = -1) throw(Exception); + +private: + SpecialType type; + + void checkInputPatternCompatibilityClockConvert() throw(Exception); + void computeOutputPatternClockConvert(int nbExec = -1) throw(Exception); + void checkInputPatternCompatibilityClkRstGen() throw(Exception); + void computeOutputPatternClkRstGen(int nbExec = -1) throw(Exception); + +}; + + + +#endif // __SPEICALBLOCK_H__ diff --git a/blast.creator.user b/blast.creator.user index 6a8ea4d..5396519 100644 --- a/blast.creator.user +++ b/blast.creator.user @@ -1,6 +1,6 @@ - + EnvironmentId diff --git a/blast.files b/blast.files index 01d93d0..114b32d 100755 --- a/blast.files +++ b/blast.files @@ -2,6 +2,8 @@ ExternalResource.cpp ExternalResource.h CustomDialog.h CustomDialog.cpp +InterfacePropertiesDialog.cpp +InterfacePropertiesDialog.h NewProjectDialog.h NewProjectDialog.cpp Exception.h @@ -16,6 +18,8 @@ AbstractBoxItem.h AbstractBoxItem.cpp FunctionalBlock.cpp FunctionalBlock.h +SpecialBlock.cpp +SpecialBlock.h GroupBlock.h GroupBlock.cpp BlockImplementation.h @@ -83,5 +87,3 @@ ParametersWindow.h ParametersWindow.cpp ArithmeticEvaluator.cpp ArithmeticEvaluator.h -InterfacePropertiesWindow.h -InterfacePropertiesWindow.cpp diff --git a/blastconfig.xml b/blastconfig.xml index 3544650..9092eab 100644 --- a/blastconfig.xml +++ b/blastconfig.xml @@ -9,7 +9,10 @@ - + + + + diff --git a/lib/implementations/impls.bmf b/lib/implementations/impls.bmf index f2e8067992b095109f2ed871e487eeba2bcc0b83..0a2f0e6ee6405e605e6351d4cb6e2f6ba6b1127d 100644 GIT binary patch delta 67 zcmca;deL-)KJVm6UM)t$$&4(*n{#=$Fin0dkTO|b;LPMe!S|Dsgp?*b2q|rD5qiST Xp3RU7gawlwg~TVnXH}RyM@Sn05HA*D delta 75 zcmca;deL-)KJVm6UM)uB$&4(*n{#=$FfkTQekYJT`L#gWWO*PC6nsB9Nl0n3gOJkZ d7NIBXlRvV`af&fyGo%7x!DL4v@yT<9v;oo?7<~W$ diff --git a/lib/references/apf27-wb-master.xml b/lib/references/apf27-wb-master.xml index 32255ba..293b847 100644 --- a/lib/references/apf27-wb-master.xml +++ b/lib/references/apf27-wb-master.xml @@ -4,7 +4,7 @@ apf27_wb_master - + This block is the wishbone master of the design, connected to the i.MX of APF27 diff --git a/lib/references/clkdomain_convert_1024x8.xml b/lib/references/clkdomain_convert_1024x8.xml index 40ee12f..1df9cf1 100644 --- a/lib/references/clkdomain_convert_1024x8.xml +++ b/lib/references/clkdomain_convert_1024x8.xml @@ -2,7 +2,7 @@ clkdomain_convert_1024x8 - + This IP allows to pass 8 bits values from a clock domain to another. It uses a FIFO of 1024 entries. @@ -18,11 +18,11 @@ - + - + diff --git a/lib/references/clkrstgen.xml b/lib/references/clkrstgen.xml index 79b60a9..5f06f17 100644 --- a/lib/references/clkrstgen.xml +++ b/lib/references/clkrstgen.xml @@ -7,11 +7,11 @@ - This block generate a reset signal synchronous to clock + This block generates a reset signal synchronous to an external clock - This block generate a reset signal synchronous to clock and is the entry point - of the external clock and asynchronous reset. + This block generates a reset signal synchronous to clock. The block is automatically + connected to the external clock and asynchronous reset. diff --git a/lib/references/references.bmf b/lib/references/references.bmf index 67e76eea9b6b68bd9b036454f0151a154c32ccdc..633acf924abd9235d3480563ac01b262e2b28313 100644 GIT binary patch delta 360 zcmX|7Jx>Bb6rANK1apOjjSa||(BXX_ghWgXG4>R;dvF(xpow4%2?;Iz{s8|0!CbJk z;s^LUtf;J=-GY7Dm-psPX5Z{)Cw_OL45ayId1CjE09<2)1gXgqK4Jvuz-1q?a^dj~ zA(0dH7#honxSY3{)8fnCp7g6m$|s>is!z!gLds_B0@faHOeAL{Mj|3JBbri38h&1E zYF6p^9j_GQvDWJQ{6nnNPopWO>aWqWB%am6saX(b2lTGnLLCDQM3vu-iUhG1k delta 356 zcmYk1Jx;?w5QU#fgapVER7fyIAFOUHv#3~0yB8?%Vor&%4T+UH2wkG)