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 <QAction>
22 #include <QFileDialog>
23 #include <QFileInfo>
24 #include <QIcon>
25 #include <QInputDialog>
26 #include <QLineEdit>
27 #include <fstream>
28 #include "bitstream.h"
29 #include "design_utils.h"
30 #include "log.h"
31 #include "pcf.h"
32 
initMainResource()33 static void initMainResource() { Q_INIT_RESOURCE(nextpnr); }
34 
35 NEXTPNR_NAMESPACE_BEGIN
36 
MainWindow(std::unique_ptr<Context> context,CommandHandler * handler,QWidget * parent)37 MainWindow::MainWindow(std::unique_ptr<Context> context, CommandHandler *handler, QWidget *parent)
38         : BaseMainWindow(std::move(context), handler, parent)
39 {
40     initMainResource();
41 
42     std::string title = "nextpnr-ice40 - [EMPTY]";
43     setWindowTitle(title.c_str());
44 
45     connect(this, &BaseMainWindow::contextChanged, this, &MainWindow::newContext);
46 
47     createMenu();
48 }
49 
~MainWindow()50 MainWindow::~MainWindow() {}
51 
createMenu()52 void MainWindow::createMenu()
53 {
54     // Add arch specific actions
55     actionLoadPCF = new QAction("Open PCF", this);
56     actionLoadPCF->setIcon(QIcon(":/icons/resources/open_pcf.png"));
57     actionLoadPCF->setStatusTip("Open PCF file");
58     actionLoadPCF->setEnabled(false);
59     connect(actionLoadPCF, &QAction::triggered, this, &MainWindow::open_pcf);
60 
61     actionSaveAsc = new QAction("Save ASC", this);
62     actionSaveAsc->setIcon(QIcon(":/icons/resources/save_asc.png"));
63     actionSaveAsc->setStatusTip("Save ASC file");
64     actionSaveAsc->setEnabled(false);
65     connect(actionSaveAsc, &QAction::triggered, this, &MainWindow::save_asc);
66 
67     // Add actions in menus
68     mainActionBar->addSeparator();
69     mainActionBar->addAction(actionLoadPCF);
70     mainActionBar->addAction(actionSaveAsc);
71 
72     menuDesign->addSeparator();
73     menuDesign->addAction(actionLoadPCF);
74     menuDesign->addAction(actionSaveAsc);
75 }
76 
new_proj()77 void MainWindow::new_proj()
78 {
79     QMap<QString, int> arch;
80     if (Arch::isAvailable(ArchArgs::LP384))
81         arch.insert("Lattice iCE40LP384", ArchArgs::LP384);
82     if (Arch::isAvailable(ArchArgs::LP1K))
83         arch.insert("Lattice iCE40LP1K", ArchArgs::LP1K);
84     if (Arch::isAvailable(ArchArgs::HX1K))
85         arch.insert("Lattice iCE40HX1K", ArchArgs::HX1K);
86     if (Arch::isAvailable(ArchArgs::U1K))
87         arch.insert("Lattice iCE5LP1K", ArchArgs::U1K);
88     if (Arch::isAvailable(ArchArgs::U2K))
89         arch.insert("Lattice iCE5LP2K", ArchArgs::U2K);
90     if (Arch::isAvailable(ArchArgs::U4K))
91         arch.insert("Lattice iCE5LP4K", ArchArgs::U4K);
92     if (Arch::isAvailable(ArchArgs::UP3K))
93         arch.insert("Lattice iCE40UP3K", ArchArgs::UP3K);
94     if (Arch::isAvailable(ArchArgs::UP5K))
95         arch.insert("Lattice iCE40UP5K", ArchArgs::UP5K);
96     if (Arch::isAvailable(ArchArgs::LP4K))
97         arch.insert("Lattice iCE40LP4K", ArchArgs::LP4K);
98     if (Arch::isAvailable(ArchArgs::LP8K))
99         arch.insert("Lattice iCE40LP8K", ArchArgs::LP8K);
100     if (Arch::isAvailable(ArchArgs::HX4K))
101         arch.insert("Lattice iCE40HX4K", ArchArgs::HX4K);
102     if (Arch::isAvailable(ArchArgs::HX8K))
103         arch.insert("Lattice iCE40HX8K", ArchArgs::HX8K);
104 
105     bool ok;
106     QString item = QInputDialog::getItem(this, "Select new context", "Chip:", arch.keys(), 0, false, &ok);
107     if (ok && !item.isEmpty()) {
108         ArchArgs chipArgs;
109         chipArgs.type = (ArchArgs::ArchArgsTypes)arch.value(item);
110 
111         QStringList packages;
112         for (auto package : Arch::getSupportedPackages(chipArgs.type))
113             packages.append(QLatin1String(package.data(), package.size()));
114         QString package = QInputDialog::getItem(this, "Select package", "Package:", packages, 0, false, &ok);
115 
116         if (ok && !item.isEmpty()) {
117             handler->clear();
118             currentProj = "";
119             disableActions();
120             chipArgs.package = package.toStdString().c_str();
121             ctx = std::unique_ptr<Context>(new Context(chipArgs));
122             actionLoadJSON->setEnabled(true);
123 
124             Q_EMIT contextChanged(ctx.get());
125         }
126     }
127 }
128 
load_pcf(std::string filename)129 void MainWindow::load_pcf(std::string filename)
130 {
131     disableActions();
132     std::ifstream f(filename);
133     if (apply_pcf(ctx.get(), filename, f)) {
134         log("Loading PCF successful.\n");
135         actionPack->setEnabled(true);
136     } else {
137         actionLoadPCF->setEnabled(true);
138         log("Loading PCF failed.\n");
139     }
140 }
141 
newContext(Context * ctx)142 void MainWindow::newContext(Context *ctx)
143 {
144     std::string title = "nextpnr-ice40 - " + ctx->getChipName() + " ( " + ctx->archArgs().package + " )";
145     setWindowTitle(title.c_str());
146 }
147 
open_pcf()148 void MainWindow::open_pcf()
149 {
150     QString fileName = QFileDialog::getOpenFileName(this, QString("Open PCF"), QString(), QString("*.pcf"));
151     if (!fileName.isEmpty()) {
152         load_pcf(fileName.toStdString());
153     }
154 }
155 
save_asc()156 void MainWindow::save_asc()
157 {
158     QString fileName = QFileDialog::getSaveFileName(this, QString("Save ASC"), QString(), QString("*.asc"));
159     if (!fileName.isEmpty()) {
160         std::string fn = fileName.toStdString();
161         disableActions();
162         std::ofstream f(fn);
163         write_asc(ctx.get(), f);
164         log("Saving ASC successful.\n");
165     }
166 }
167 
onDisableActions()168 void MainWindow::onDisableActions()
169 {
170     actionLoadPCF->setEnabled(false);
171     actionSaveAsc->setEnabled(false);
172 }
173 
onUpdateActions()174 void MainWindow::onUpdateActions()
175 {
176     if (ctx->settings.find(ctx->id("pack")) == ctx->settings.end())
177         actionLoadPCF->setEnabled(true);
178     if (ctx->settings.find(ctx->id("route")) != ctx->settings.end())
179         actionSaveAsc->setEnabled(true);
180 }
181 
182 NEXTPNR_NAMESPACE_END
183