1 /*
2  *  nextpnr -- Next Generation Place and Route
3  *
4  *  Copyright (C) 2018  Miodrag Milanovic <miodrag@symbioticeda.com>
5  *
6  *  Permission to use, copy, modify, and/or distribute this software for any
7  *  purpose with or without fee is hereby granted, provided that the above
8  *  copyright notice and this permission notice appear in all copies.
9  *
10  *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  *
18  */
19 
20 #include "mainwindow.h"
21 #include <fstream>
22 #include "bitstream.h"
23 #include "log.h"
24 
25 #include <QFileDialog>
26 #include <QInputDialog>
27 #include <QLineEdit>
28 
initMainResource()29 static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
30 
31 NEXTPNR_NAMESPACE_BEGIN
32 
MainWindow(std::unique_ptr<Context> context,CommandHandler * handler,QWidget * parent)33 MainWindow::MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent)
34         : BaseMainWindow(std::move(context), handler, parent)
35 {
36     initMainResource();
37 
38     std::string title = "nextpnr-ecp5 - [EMPTY]";
39     setWindowTitle(title.c_str());
40 
41     connect(this, &BaseMainWindow::contextChanged, this, &MainWindow::newContext);
42 
43     createMenu();
44 }
45 
~MainWindow()46 MainWindow::~MainWindow() {}
47 
newContext(Context * ctx)48 void MainWindow::newContext(Context *ctx)
49 {
50     std::string title = "nextpnr-ecp5 - " + ctx->getChipName() + " ( " + ctx->archArgs().package + " )";
51     setWindowTitle(title.c_str());
52 }
53 
createMenu()54 void MainWindow::createMenu()
55 {
56     // Add arch specific actions
57     actionLoadLPF = new QAction("Open LPF", this);
58     actionLoadLPF->setIcon(QIcon(":/icons/resources/open_lpf.png"));
59     actionLoadLPF->setStatusTip("Open LPF file");
60     actionLoadLPF->setEnabled(false);
61     connect(actionLoadLPF, &QAction::triggered, this, &MainWindow::open_lpf);
62 
63     actionSaveConfig = new QAction("Save Bitstream", this);
64     actionSaveConfig->setIcon(QIcon(":/icons/resources/save_config.png"));
65     actionSaveConfig->setStatusTip("Save Bitstream config file");
66     actionSaveConfig->setEnabled(false);
67     connect(actionSaveConfig, &QAction::triggered, this, &MainWindow::save_config);
68 
69     // Add actions in menus
70     mainActionBar->addSeparator();
71     mainActionBar->addAction(actionLoadLPF);
72     mainActionBar->addAction(actionSaveConfig);
73 
74     menuDesign->addSeparator();
75     menuDesign->addAction(actionLoadLPF);
76     menuDesign->addAction(actionSaveConfig);
77 }
78 
new_proj()79 void MainWindow::new_proj()
80 {
81     QMap<QString, int> arch;
82     if (Arch::isAvailable(ArchArgs::LFE5U_25F))
83         arch.insert("Lattice ECP5 LFE5U-25F", ArchArgs::LFE5U_25F);
84     if (Arch::isAvailable(ArchArgs::LFE5U_45F))
85         arch.insert("Lattice ECP5 LFE5U-45F", ArchArgs::LFE5U_45F);
86     if (Arch::isAvailable(ArchArgs::LFE5U_85F))
87         arch.insert("Lattice ECP5 LFE5U-85F", ArchArgs::LFE5U_85F);
88     if (Arch::isAvailable(ArchArgs::LFE5UM_25F))
89         arch.insert("Lattice ECP5 LFE5UM-25F", ArchArgs::LFE5UM_25F);
90     if (Arch::isAvailable(ArchArgs::LFE5UM_45F))
91         arch.insert("Lattice ECP5 LFE5UM-45F", ArchArgs::LFE5UM_45F);
92     if (Arch::isAvailable(ArchArgs::LFE5UM_85F))
93         arch.insert("Lattice ECP5 LFE5UM-85F", ArchArgs::LFE5UM_85F);
94     if (Arch::isAvailable(ArchArgs::LFE5UM5G_25F))
95         arch.insert("Lattice ECP5 LFE5UM5G-25F", ArchArgs::LFE5UM5G_25F);
96     if (Arch::isAvailable(ArchArgs::LFE5UM5G_45F))
97         arch.insert("Lattice ECP5 LFE5UM5G-45F", ArchArgs::LFE5UM5G_45F);
98     if (Arch::isAvailable(ArchArgs::LFE5UM5G_85F))
99         arch.insert("Lattice ECP5 LFE5UM5G-85F", ArchArgs::LFE5UM5G_85F);
100 
101     bool ok;
102     QString item = QInputDialog::getItem(this, "Select new context", "Chip:", arch.keys(), 0, false, &ok);
103     if (ok && !item.isEmpty()) {
104         ArchArgs chipArgs;
105         chipArgs.type = (ArchArgs::ArchArgsTypes)arch.value(item);
106 
107         QStringList packages;
108         for (auto package : Arch::getSupportedPackages(chipArgs.type))
109             packages.append(QLatin1String(package.data(), package.size()));
110         QString package = QInputDialog::getItem(this, "Select package", "Package:", packages, 0, false, &ok);
111 
112         if (ok && !item.isEmpty()) {
113             handler->clear();
114             currentProj = "";
115             disableActions();
116             chipArgs.package = package.toStdString().c_str();
117             ctx = std::unique_ptr<Context>(new Context(chipArgs));
118             actionLoadJSON->setEnabled(true);
119 
120             Q_EMIT contextChanged(ctx.get());
121         }
122     }
123 }
124 
open_lpf()125 void MainWindow::open_lpf()
126 {
127     QString fileName = QFileDialog::getOpenFileName(this, QString("Open LPF"), QString(), QString("*.lpf"));
128     if (!fileName.isEmpty()) {
129         std::ifstream in(fileName.toStdString());
130         if (ctx->applyLPF(fileName.toStdString(), in)) {
131             log("Loading LPF successful.\n");
132             actionPack->setEnabled(true);
133             actionLoadLPF->setEnabled(false);
134         } else {
135             actionLoadLPF->setEnabled(true);
136             log("Loading LPF failed.\n");
137         }
138     }
139 }
140 
save_config()141 void MainWindow::save_config()
142 {
143     QString fileName = QFileDialog::getSaveFileName(this, QString("Save Bitstream"), QString(), QString("*.config"));
144     if (!fileName.isEmpty()) {
145         std::string fn = fileName.toStdString();
146         disableActions();
147         write_bitstream(ctx.get(), "", fileName.toStdString());
148         log("Saving Bitstream successful.\n");
149     }
150 }
151 
onDisableActions()152 void MainWindow::onDisableActions()
153 {
154     actionLoadLPF->setEnabled(false);
155     actionSaveConfig->setEnabled(false);
156 }
157 
onUpdateActions()158 void MainWindow::onUpdateActions()
159 {
160     if (ctx->settings.find(ctx->id("pack")) == ctx->settings.end())
161         actionLoadLPF->setEnabled(true);
162     if (ctx->settings.find(ctx->id("route")) != ctx->settings.end())
163         actionSaveConfig->setEnabled(true);
164 }
165 
166 NEXTPNR_NAMESPACE_END
167