aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/nextpnr.h25
-rw-r--r--ecp5/arch.cc33
-rw-r--r--ecp5/arch.h24
-rw-r--r--ecp5/archdefs.h7
-rw-r--r--ecp5/cells.cc10
-rw-r--r--ecp5/main.cc8
-rw-r--r--ecp5/pack.cc141
-rw-r--r--ecp5/synth/blinky.v86
-rw-r--r--ecp5/synth/blinky.ys9
-rw-r--r--ecp5/synth/blinky_nopack.ys2
-rw-r--r--ecp5/synth/cells.v49
-rw-r--r--ecp5/synth/simple_map.v68
-rw-r--r--ecp5/synth/ulx3s.v18
-rw-r--r--ecp5/synth/ulx3s.ys9
-rw-r--r--ecp5/synth/wire.v11
-rw-r--r--ecp5/synth/wire.ys9
-rwxr-xr-xecp5/trellis_import.py78
-rw-r--r--generic/archdefs.h7
-rw-r--r--gui/base.qrc2
-rw-r--r--gui/basewindow.cc35
-rw-r--r--gui/basewindow.h5
-rw-r--r--gui/designwidget.cc68
-rw-r--r--gui/designwidget.h3
-rw-r--r--gui/ice40/mainwindow.cc1
-rw-r--r--gui/infotab.cc58
-rw-r--r--gui/infotab.h48
-rw-r--r--gui/resources/cross.pngbin0 -> 655 bytes
-rw-r--r--gui/resources/splash.pngbin4651 -> 0 bytes
-rw-r--r--gui/yosys_edit.cc96
-rw-r--r--gui/yosys_edit.h57
-rw-r--r--gui/yosystab.cc108
-rw-r--r--gui/yosystab.h59
-rw-r--r--ice40/arch.cc58
-rw-r--r--ice40/arch.h26
-rw-r--r--ice40/arch_place.cc50
-rw-r--r--ice40/archdefs.h25
-rw-r--r--ice40/bitstream.cc267
-rw-r--r--ice40/bitstream.h1
-rw-r--r--ice40/blinky.ys2
-rw-r--r--ice40/cells.cc76
-rw-r--r--ice40/cells.h2
-rw-r--r--ice40/chipdb.py40
-rw-r--r--ice40/gfx.cc9
-rw-r--r--ice40/gfx.h3
-rw-r--r--ice40/main.cc24
-rw-r--r--ice40/pack.cc28
-rwxr-xr-xice40/picorv32.sh2
-rwxr-xr-xice40/picorv32_arachne.sh9
-rw-r--r--ice40/place_legaliser.cc3
-rwxr-xr-xice40/transform_arachne_loc.py24
50 files changed, 911 insertions, 872 deletions
diff --git a/common/nextpnr.h b/common/nextpnr.h
index 375e1cd7..8ef958cd 100644
--- a/common/nextpnr.h
+++ b/common/nextpnr.h
@@ -72,21 +72,22 @@ class assertion_failure : public std::runtime_error
int line;
};
-inline void except_assert_impl(bool expr, const char *message, const char *expr_str, const char *filename, int line)
+NPNR_NORETURN
+inline bool assert_fail_impl(const char *message, const char *expr_str, const char *filename, int line)
{
- if (!expr)
- throw assertion_failure(message, expr_str, filename, line);
+ throw assertion_failure(message, expr_str, filename, line);
}
NPNR_NORETURN
-inline void assert_false_impl(std::string message, std::string filename, int line)
+inline bool assert_fail_impl_str(std::string message, const char *expr_str, const char *filename, int line)
{
- throw assertion_failure(message, "false", filename, line);
+ throw assertion_failure(message, expr_str, filename, line);
}
-#define NPNR_ASSERT(cond) except_assert_impl((cond), #cond, #cond, __FILE__, __LINE__)
-#define NPNR_ASSERT_MSG(cond, msg) except_assert_impl((cond), msg, #cond, __FILE__, __LINE__)
-#define NPNR_ASSERT_FALSE(msg) assert_false_impl(msg, __FILE__, __LINE__)
+#define NPNR_ASSERT(cond) ((void)((cond) || (assert_fail_impl(#cond, #cond, __FILE__, __LINE__))))
+#define NPNR_ASSERT_MSG(cond, msg) ((void)((cond) || (assert_fail_impl(msg, #cond, __FILE__, __LINE__))))
+#define NPNR_ASSERT_FALSE(msg) (assert_fail_impl(msg, "false", __FILE__, __LINE__))
+#define NPNR_ASSERT_FALSE_STR(msg) (assert_fail_impl_str(msg, "false", __FILE__, __LINE__))
struct BaseCtx;
struct Context;
@@ -205,9 +206,11 @@ struct PipMap
PlaceStrength strength = STRENGTH_NONE;
};
-struct NetInfo
+struct NetInfo : ArchNetInfo
{
IdString name;
+ int32_t udata;
+
PortRef driver;
std::vector<PortRef> users;
std::unordered_map<IdString, std::string> attrs;
@@ -230,9 +233,11 @@ struct PortInfo
PortType type;
};
-struct CellInfo
+struct CellInfo : ArchCellInfo
{
IdString name, type;
+ int32_t udata;
+
std::unordered_map<IdString, PortInfo> ports;
std::unordered_map<IdString, std::string> attrs, params;
diff --git a/ecp5/arch.cc b/ecp5/arch.cc
index 0ffede3b..1510a27f 100644
--- a/ecp5/arch.cc
+++ b/ecp5/arch.cc
@@ -118,6 +118,16 @@ Arch::Arch(ArchArgs args) : args(args)
log_error("Unsupported ECP5 chip type.\n");
}
#endif
+ package_info = nullptr;
+ for (int i = 0; i < chip_info->num_packages; i++) {
+ if (args.package == chip_info->package_info[i].name.get()) {
+ package_info = &(chip_info->package_info[i]);
+ break;
+ }
+ }
+
+ if (!package_info)
+ log_error("Unsupported package '%s' for '%s'.\n", args.package.c_str(), getChipName().c_str());
id_trellis_slice = id("TRELLIS_SLICE");
id_clk = id("CLK");
@@ -282,9 +292,28 @@ IdString Arch::getPipName(PipId pip) const
// -----------------------------------------------------------------------
-BelId Arch::getPackagePinBel(const std::string &pin) const { return BelId(); }
+BelId Arch::getPackagePinBel(const std::string &pin) const
+{
+ for (int i = 0; i < package_info->num_pins; i++) {
+ if (package_info->pin_data[i].name.get() == pin) {
+ BelId bel;
+ bel.location = package_info->pin_data[i].abs_loc;
+ bel.index = package_info->pin_data[i].bel_index;
+ return bel;
+ }
+ }
+ return BelId();
+}
-std::string Arch::getBelPackagePin(BelId bel) const { return ""; }
+std::string Arch::getBelPackagePin(BelId bel) const
+{
+ for (int i = 0; i < package_info->num_pins; i++) {
+ if (package_info->pin_data[i].abs_loc == bel.location && package_info->pin_data[i].bel_index == bel.index) {
+ return package_info->pin_data[i].name.get();
+ }
+ }
+ return "";
+}
// -----------------------------------------------------------------------
void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const
diff --git a/ecp5/arch.h b/ecp5/arch.h
index 4bb71b47..944aedea 100644
--- a/ecp5/arch.h
+++ b/ecp5/arch.h
@@ -96,13 +96,36 @@ NPNR_PACKED_STRUCT(struct LocationTypePOD {
RelPtr<PipInfoPOD> pip_data;
});
+NPNR_PACKED_STRUCT(struct PIOInfoPOD {
+ Location abs_loc;
+ int32_t bel_index;
+ RelPtr<char> function_name;
+ int16_t bank;
+ int16_t padding;
+});
+
+NPNR_PACKED_STRUCT(struct PackagePinPOD {
+ RelPtr<char> name;
+ Location abs_loc;
+ int32_t bel_index;
+});
+
+NPNR_PACKED_STRUCT(struct PackageInfoPOD {
+ RelPtr<char> name;
+ int32_t num_pins;
+ RelPtr<PackagePinPOD> pin_data;
+});
+
NPNR_PACKED_STRUCT(struct ChipInfoPOD {
int32_t width, height;
int32_t num_tiles;
int32_t num_location_types;
+ int32_t num_packages, num_pios;
RelPtr<LocationTypePOD> locations;
RelPtr<int32_t> location_type;
RelPtr<RelPtr<char>> tiletype_names;
+ RelPtr<PackageInfoPOD> package_info;
+ RelPtr<PIOInfoPOD> pio_info;
});
#if defined(_MSC_VER)
@@ -340,6 +363,7 @@ struct ArchArgs
struct Arch : BaseCtx
{
const ChipInfoPOD *chip_info;
+ const PackageInfoPOD *package_info;
mutable std::unordered_map<IdString, BelId> bel_by_name;
mutable std::unordered_map<IdString, WireId> wire_by_name;
diff --git a/ecp5/archdefs.h b/ecp5/archdefs.h
index 84a431fd..941607ba 100644
--- a/ecp5/archdefs.h
+++ b/ecp5/archdefs.h
@@ -129,6 +129,13 @@ struct DecalId
}
};
+struct ArchNetInfo
+{
+};
+struct ArchCellInfo
+{
+};
+
NEXTPNR_NAMESPACE_END
namespace std {
diff --git a/ecp5/cells.cc b/ecp5/cells.cc
index 59504735..e3532f36 100644
--- a/ecp5/cells.cc
+++ b/ecp5/cells.cc
@@ -116,6 +116,14 @@ std::unique_ptr<CellInfo> create_ecp5_cell(Context *ctx, IdString type, std::str
add_port(ctx, new_cell.get(), "I", PORT_IN);
add_port(ctx, new_cell.get(), "T", PORT_IN);
add_port(ctx, new_cell.get(), "O", PORT_OUT);
+ } else if (type == ctx->id("LUT4")) {
+ new_cell->params[ctx->id("INIT")] = "0";
+
+ add_port(ctx, new_cell.get(), "A", PORT_IN);
+ add_port(ctx, new_cell.get(), "B", PORT_IN);
+ add_port(ctx, new_cell.get(), "C", PORT_IN);
+ add_port(ctx, new_cell.get(), "D", PORT_IN);
+ add_port(ctx, new_cell.get(), "Z", PORT_OUT);
} else {
log_error("unable to create ECP5 cell of type %s", type.c_str(ctx));
}
@@ -169,7 +177,7 @@ void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool drive
void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index)
{
- lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] = str_or_default(lc->params, ctx->id("INIT"), "0");
+ lc->params[ctx->id("LUT" + std::to_string(index) + "_INITVAL")] = str_or_default(lut->params, ctx->id("INIT"), "0");
replace_port(lut, ctx->id("A"), lc, ctx->id("A" + std::to_string(index)));
replace_port(lut, ctx->id("B"), lc, ctx->id("B" + std::to_string(index)));
replace_port(lut, ctx->id("C"), lc, ctx->id("C" + std::to_string(index)));
diff --git a/ecp5/main.cc b/ecp5/main.cc
index 7521b88c..5a4a900a 100644
--- a/ecp5/main.cc
+++ b/ecp5/main.cc
@@ -68,6 +68,8 @@ int main(int argc, char *argv[])
options.add_options()("45k", "set device type to LFE5U-45F");
options.add_options()("85k", "set device type to LFE5U-85F");
+ options.add_options()("package", po::value<std::string>(), "select device package (defaults to CABGA381)");
+
options.add_options()("json", po::value<std::string>(), "JSON design file to ingest");
options.add_options()("seed", po::value<int>(), "seed value for random number generator");
@@ -123,8 +125,10 @@ int main(int argc, char *argv[])
args.type = ArchArgs::LFE5U_45F;
if (vm.count("85k"))
args.type = ArchArgs::LFE5U_85F;
-
- args.package = "CABGA381";
+ if (vm.count("package"))
+ args.package = vm["package"].as<std::string>();
+ else
+ args.package = "CABGA381";
args.speed = 6;
std::unique_ptr<Context> ctx = std::unique_ptr<Context>(new Context(args));
diff --git a/ecp5/pack.cc b/ecp5/pack.cc
index 1900eded..11cc2647 100644
--- a/ecp5/pack.cc
+++ b/ecp5/pack.cc
@@ -71,6 +71,15 @@ class Ecp5Packer
}
}
+ const NetInfo *net_or_nullptr(CellInfo *cell, IdString port)
+ {
+ auto fnd = cell->ports.find(port);
+ if (fnd == cell->ports.end())
+ return nullptr;
+ else
+ return fnd->second.net;
+ }
+
// Return whether two FFs can be packed together in the same slice
bool can_pack_ffs(CellInfo *ff0, CellInfo *ff1)
{
@@ -88,11 +97,11 @@ class Ecp5Packer
if (str_or_default(ff0->params, ctx->id("CLKMUX"), "CLK") !=
str_or_default(ff1->params, ctx->id("CLKMUX"), "CLK"))
return false;
- if (ff0->ports.at(ctx->id("CLK")).net != ff1->ports.at(ctx->id("CLK")).net)
+ if (net_or_nullptr(ff0, ctx->id("CLK")) != net_or_nullptr(ff1, ctx->id("CLK")))
return false;
- if (ff0->ports.at(ctx->id("CE")).net != ff1->ports.at(ctx->id("CE")).net)
+ if (net_or_nullptr(ff0, ctx->id("CE")) != net_or_nullptr(ff1, ctx->id("CE")))
return false;
- if (ff0->ports.at(ctx->id("LSR")).net != ff1->ports.at(ctx->id("LSR")).net)
+ if (net_or_nullptr(ff0, ctx->id("LSR")) != net_or_nullptr(ff1, ctx->id("LSR")))
return false;
return true;
}
@@ -223,8 +232,23 @@ class Ecp5Packer
} else {
log_error("TRELLIS_IO required on all top level IOs...\n");
}
+
packed_cells.insert(ci->name);
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(trio->attrs, trio->attrs.begin()));
+
+ auto loc_attr = trio->attrs.find(ctx->id("LOC"));
+ if (loc_attr != trio->attrs.end()) {
+ std::string pin = loc_attr->second;
+ BelId pinBel = ctx->getPackagePinBel(pin);
+ if (pinBel == BelId()) {
+ log_error("IO pin '%s' constrained to pin '%s', which does not exist for package '%s'.\n",
+ trio->name.c_str(ctx), pin.c_str(), ctx->args.package.c_str());
+ } else {
+ log_info("pin '%s' constrained to Bel '%s'.\n", trio->name.c_str(ctx),
+ ctx->getBelName(pinBel).c_str(ctx));
+ }
+ trio->attrs[ctx->id("BEL")] = ctx->getBelName(pinBel).str(ctx);
+ }
}
}
flush_cells();
@@ -275,6 +299,8 @@ class Ecp5Packer
ff_to_slice(ctx, ff, packed.get(), 0, true);
packed_cells.insert(ff->name);
sliceUsage[packed->name].ff0_used = true;
+ lutffPairs.erase(ci->name);
+ fflutPairs.erase(ff->name);
}
new_cells.push_back(std::move(packed));
@@ -304,10 +330,14 @@ class Ecp5Packer
if (ff0 != lutffPairs.end()) {
ff_to_slice(ctx, ctx->cells.at(ff0->second).get(), slice.get(), 0, true);
packed_cells.insert(ff0->second);
+ lutffPairs.erase(lut0->name);
+ fflutPairs.erase(ff0->second);
}
if (ff1 != lutffPairs.end()) {
ff_to_slice(ctx, ctx->cells.at(ff1->second).get(), slice.get(), 1, true);
packed_cells.insert(ff1->second);
+ lutffPairs.erase(lut1->name);
+ fflutPairs.erase(ff1->second);
}
new_cells.push_back(std::move(slice));
@@ -333,6 +363,8 @@ class Ecp5Packer
if (ff != lutffPairs.end()) {
ff_to_slice(ctx, ctx->cells.at(ff->second).get(), slice.get(), 0, true);
packed_cells.insert(ff->second);
+ lutffPairs.erase(ci->name);
+ fflutPairs.erase(ff->second);
}
new_cells.push_back(std::move(slice));
@@ -359,10 +391,113 @@ class Ecp5Packer
flush_cells();
}
+ void set_lut_input_constant(CellInfo *cell, IdString input, bool value)
+ {
+ int index = std::string("ABCD").find(input.str(ctx));
+ int init = int_or_default(cell->params, ctx->id("INIT"));
+ int new_init = 0;
+ for (int i = 0; i < 16; i++) {
+ if (((i >> index) & 0x1) != value) {
+ int other_i = (i & (~(1 << index))) | (value << index);
+ if ((init >> other_i) & 0x1)
+ new_init |= (1 << i);
+ } else {
+ if ((init >> i) & 0x1)
+ new_init |= (1 << i);
+ }
+ }
+ cell->params[ctx->id("INIT")] = std::to_string(new_init);
+ cell->ports.at(input).net = nullptr;
+ }
+
+ // Merge a net into a constant net
+ void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constnet, bool constval)
+ {
+ orig->driver.cell = nullptr;
+ for (auto user : orig->users) {
+ if (user.cell != nullptr) {
+ CellInfo *uc = user.cell;
+ if (ctx->verbose)
+ log_info("%s user %s\n", orig->name.c_str(ctx), uc->name.c_str(ctx));
+ if (is_lut(ctx, uc)) {
+ set_lut_input_constant(uc, user.port, constval);
+ } else if (is_ff(ctx, uc) && user.port == ctx->id("CE")) {
+ uc->params[ctx->id("CEMUX")] = constval ? "1" : "0";
+ uc->ports[user.port].net = nullptr;
+ } else if (is_ff(ctx, uc) && user.port == ctx->id("LSR") &&
+ ((!constval && str_or_default(uc->params, ctx->id("LSRMUX"), "LSR") == "LSR") ||
+ (constval && str_or_default(uc->params, ctx->id("LSRMUX"), "LSR") == "INV"))) {
+ uc->ports[user.port].net = nullptr;
+ } else {
+ uc->ports[user.port].net = constnet;
+ constnet->users.push_back(user);
+ }
+ }
+ }
+ orig->users.clear();
+ }
+
+ // Pack constants (simple implementation)
+ void pack_constants()
+ {
+ log_info("Packing constants..\n");
+
+ std::unique_ptr<CellInfo> gnd_cell = create_ecp5_cell(ctx, ctx->id("LUT4"), "$PACKER_GND");
+ gnd_cell->params[ctx->id("INIT")] = "0";
+ std::unique_ptr<NetInfo> gnd_net = std::unique_ptr<NetInfo>(new NetInfo);
+ gnd_net->name = ctx->id("$PACKER_GND_NET");
+ gnd_net->driver.cell = gnd_cell.get();
+ gnd_net->driver.port = ctx->id("Z");
+ gnd_cell->ports.at(ctx->id("Z")).net = gnd_net.get();
+
+ std::unique_ptr<CellInfo> vcc_cell = create_ecp5_cell(ctx, ctx->id("LUT4"), "$PACKER_VCC");
+ vcc_cell->params[ctx->id("INIT")] = "65535";
+ std::unique_ptr<NetInfo> vcc_net = std::unique_ptr<NetInfo>(new NetInfo);
+ vcc_net->name = ctx->id("$PACKER_VCC_NET");
+ vcc_net->driver.cell = vcc_cell.get();
+ vcc_net->driver.port = ctx->id("Z");
+ vcc_cell->ports.at(ctx->id("Z")).net = vcc_net.get();
+
+ std::vector<IdString> dead_nets;
+
+ bool gnd_used = false, vcc_used = false;
+
+ for (auto net : sorted(ctx->nets)) {
+ NetInfo *ni = net.second;
+ if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("GND")) {
+ IdString drv_cell = ni->driver.cell->name;
+ set_net_constant(ctx, ni, gnd_net.get(), false);
+ gnd_used = true;
+ dead_nets.push_back(net.first);
+ ctx->cells.erase(drv_cell);
+ } else if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("VCC")) {
+ IdString drv_cell = ni->driver.cell->name;
+ set_net_constant(ctx, ni, vcc_net.get(), true);
+ vcc_used = true;
+ dead_nets.push_back(net.first);
+ ctx->cells.erase(drv_cell);
+ }
+ }
+
+ if (gnd_used) {
+ ctx->cells[gnd_cell->name] = std::move(gnd_cell);
+ ctx->nets[gnd_net->name] = std::move(gnd_net);
+ }
+ if (vcc_used) {
+ ctx->cells[vcc_cell->name] = std::move(vcc_cell);
+ ctx->nets[vcc_net->name] = std::move(vcc_net);
+ }
+
+ for (auto dn : dead_nets) {
+ ctx->nets.erase(dn);
+ }
+ }
+
public:
void pack()
{
pack_io();
+ pack_constants();
find_lutff_pairs();
pack_lut5s();
pair_luts();
diff --git a/ecp5/synth/blinky.v b/ecp5/synth/blinky.v
index ac7c6ea3..9c6b187b 100644
--- a/ecp5/synth/blinky.v
+++ b/ecp5/synth/blinky.v
@@ -1,46 +1,46 @@
-module top(input clk_pin, input btn_pin, output [3:0] led_pin, output gpio0_pin);
+module top(input clk_pin, input btn_pin, output [7:0] led_pin, output gpio0_pin);
- wire clk;
- wire [7:0] led;
+ wire clk;
+ wire [7:0] led;
wire btn;
wire gpio0;
- (* BEL="X0/Y35/PIOA" *) (* IO_TYPE="LVCMOS33" *)
- TRELLIS_IO #(.DIR("INPUT")) clk_buf (.B(clk_pin), .O(clk));
+ (* LOC="G2" *) (* IO_TYPE="LVCMOS33" *)
+ TRELLIS_IO #(.DIR("INPUT")) clk_buf (.B(clk_pin), .O(clk));
- (* BEL="X4/Y71/PIOA" *) (* IO_TYPE="LVCMOS33" *)
- TRELLIS_IO #(.DIR("INPUT")) btn_buf (.B(btn_pin), .O(btn));
+ (* LOC="R1" *) (* IO_TYPE="LVCMOS33" *)
+ TRELLIS_IO #(.DIR("INPUT")) btn_buf (.B(btn_pin), .O(btn));
- (* BEL="X0/Y23/PIOC" *) (* IO_TYPE="LVCMOS33" *)
- TRELLIS_IO #(.DIR("OUTPUT")) led_buf_0 (.B(led_pin[0]), .I(led[0]));
- (* BEL="X0/Y23/PIOD" *) (* IO_TYPE="LVCMOS33" *)
- TRELLIS_IO #(.DIR("OUTPUT")) led_buf_1 (.B(led_pin[1]), .I(led[1]));
- (* BEL="X0/Y26/PIOA" *) (* IO_TYPE="LVCMOS33" *)
- TRELLIS_IO #(.DIR("OUTPUT")) led_buf_2 (.B(led_pin[2]), .I(led[2]));
- (* BEL="X0/Y26/PIOC" *) (* IO_TYPE="LVCMOS33" *)
- TRELLIS_IO #(.DIR("OUTPUT")) led_buf_3 (.B(led_pin[3]), .I(led[3]));
+ (* LOC="B2" *) (* IO_TYPE="LVCMOS33" *)
+ TRELLIS_IO #(.DIR("OUTPUT")) led_buf_0 (.B(led_pin[0]), .I(led[0]));
+ (* LOC="C2" *) (* IO_TYPE="LVCMOS33" *)
+ TRELLIS_IO #(.DIR("OUTPUT")) led_buf_1 (.B(led_pin[1]), .I(led[1]));
+ (* LOC="C1" *) (* IO_TYPE="LVCMOS33" *)
+ TRELLIS_IO #(.DIR("OUTPUT")) led_buf_2 (.B(led_pin[2]), .I(led[2]));
+ (* LOC="D2" *) (* IO_TYPE="LVCMOS33" *)
+ TRELLIS_IO #(.DIR("OUTPUT")) led_buf_3 (.B(led_pin[3]), .I(led[3]));
- (* BEL="X0/Y26/PIOB" *) (* IO_TYPE="LVCMOS33" *)
- TRELLIS_IO #(.DIR("OUTPUT")) led_buf_4 (.B(led_pin[4]), .I(led[4]));
- (* BEL="X0/Y32/PIOD" *) (* IO_TYPE="LVCMOS33" *)
- TRELLIS_IO #(.DIR("OUTPUT")) led_buf_5 (.B(led_pin[5]), .I(led[5]));
- (* BEL="X0/Y26/PIOD" *) (* IO_TYPE="LVCMOS33" *)
- TRELLIS_IO #(.DIR("OUTPUT")) led_buf_6 (.B(led_pin[6]), .I(led[6]));
- (* BEL="X0/Y29/PIOD" *) (* IO_TYPE="LVCMOS33" *)
- TRELLIS_IO #(.DIR("OUTPUT")) led_buf_7 (.B(led_pin[7]), .I(led[7]));
+ (* LOC="D1" *) (* IO_TYPE="LVCMOS33" *)
+ TRELLIS_IO #(.DIR("OUTPUT")) led_buf_4 (.B(led_pin[4]), .I(led[4]));
+ (* LOC="E2" *) (* IO_TYPE="LVCMOS33" *)
+ TRELLIS_IO #(.DIR("OUTPUT")) led_buf_5 (.B(led_pin[5]), .I(led[5]));
+ (* LOC="E1" *) (* IO_TYPE="LVCMOS33" *)
+ TRELLIS_IO #(.DIR("OUTPUT")) led_buf_6 (.B(led_pin[6]), .I(led[6]));
+ (* LOC="H3" *) (* IO_TYPE="LVCMOS33" *)
+ TRELLIS_IO #(.DIR("OUTPUT")) led_buf_7 (.B(led_pin[7]), .I(led[7]));
- (* BEL="X0/Y62/PIOD" *) (* IO_TYPE="LVCMOS33" *)
- TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0));
+ (* LOC="L2" *) (* IO_TYPE="LVCMOS33" *)
+ TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0));
localparam ctr_width = 24;
localparam ctr_max = 2**ctr_width - 1;
- reg [ctr_width-1:0] ctr = 0;
- reg [9:0] pwm_ctr = 0;
+ reg [ctr_width-1:0] ctr = 0;
+ reg [9:0] pwm_ctr = 0;
reg dir = 0;
- always@(posedge clk) begin
- ctr <= btn ? ctr : (dir ? ctr - 1'b1 : ctr + 1'b1);
+ always@(posedge clk) begin
+ ctr <= btn ? ctr : (dir ? ctr - 1'b1 : ctr + 1'b1);
if (ctr[ctr_width-1 : ctr_width-3] == 0 && dir == 1)
dir <= 1'b0;
else if (ctr[ctr_width-1 : ctr_width-3] == 7 && dir == 0)
@@ -54,24 +54,24 @@ module top(input clk_pin, input btn_pin, output [3:0] led_pin, output gpio0_pin)
genvar i;
generate
- for (i = 0; i < 8; i=i+1) begin
- always @ (posedge clk) begin
- if (ctr[ctr_width-1 : ctr_width-3] == i)
- brightness[i] <= bright_max;
- else if (ctr[ctr_width-1 : ctr_width-3] == (i - 1))
- brightness[i] <= ctr[ctr_width-4:ctr_width-13];
- else if (ctr[ctr_width-1 : ctr_width-3] == (i + 1))
- brightness[i] <= bright_max - ctr[ctr_width-4:ctr_width-13];
- else
- brightness[i] <= 0;
- led_reg[i] <= pwm_ctr < brightness[i];
- end
- end
+ for (i = 0; i < 8; i=i+1) begin
+ always @ (posedge clk) begin
+ if (ctr[ctr_width-1 : ctr_width-3] == i)
+ brightness[i] <= bright_max;
+ else if (ctr[ctr_width-1 : ctr_width-3] == (i - 1))
+ brightness[i] <= ctr[ctr_width-4:ctr_width-13];
+ else if (ctr[ctr_width-1 : ctr_width-3] == (i + 1))
+ brightness[i] <= bright_max - ctr[ctr_width-4:ctr_width-13];
+ else
+ brightness[i] <= 0;
+ led_reg[i] <= pwm_ctr < brightness[i];
+ end
+ end
endgenerate
assign led = led_reg;
// Tie GPIO0, keep board from rebooting
- TRELLIS_SLICE #(.MODE("LOGIC"), .LUT0_INITVAL(16'hFFFF)) vcc (.F0(gpio0));
+ assign gpio0 = 1'b1;
endmodule
diff --git a/ecp5/synth/blinky.ys b/ecp5/synth/blinky.ys
index c0b74636..fb359380 100644
--- a/ecp5/synth/blinky.ys
+++ b/ecp5/synth/blinky.ys
@@ -1,9 +1,2 @@
read_verilog blinky.v
-read_verilog -lib cells.v
-synth -top top
-abc -lut 4
-techmap -map simple_map.v
-splitnets
-opt_clean
-stat
-write_json blinky.json
+synth_ecp5 -noccu2 -nomux -nodram -json blinky.json
diff --git a/ecp5/synth/blinky_nopack.ys b/ecp5/synth/blinky_nopack.ys
deleted file mode 100644
index fb359380..00000000
--- a/ecp5/synth/blinky_nopack.ys
+++ /dev/null
@@ -1,2 +0,0 @@
-read_verilog blinky.v
-synth_ecp5 -noccu2 -nomux -nodram -json blinky.json
diff --git a/ecp5/synth/cells.v b/ecp5/synth/cells.v
deleted file mode 100644
index 353b8ada..00000000
--- a/ecp5/synth/cells.v
+++ /dev/null
@@ -1,49 +0,0 @@
-(* blackbox *)
-module TRELLIS_SLICE(
- input A0, B0, C0, D0,
- input A1, B1, C1, D1,
- input M0, M1,
- input FCI, FXA, FXB,
-
- input CLK, LSR, CE,
- input DI0, DI1,
-
- input WD0, WD1,
- input WAD0, WAD1, WAD2, WAD3,
- input WRE, WCK,
-
- output F0, Q0,
- output F1, Q1,
- output FCO, OFX0, OFX1,
-
- output WDO0, WDO1, WDO2, WDO3,
- output WADO0, WADO1, WADO2, WADO3
-);
-
-parameter MODE = "LOGIC";
-parameter GSR = "ENABLED";
-parameter SRMODE = "LSR_OVER_CE";
-parameter CEMUX = "1";
-parameter CLKMUX = "CLK";
-parameter LSRMUX = "LSR";
-parameter LUT0_INITVAL = 16'h0000;
-parameter LUT1_INITVAL = 16'h0000;
-parameter REG0_SD = "0";
-parameter REG1_SD = "0";
-parameter REG0_REGSET = "RESET";
-parameter REG1_REGSET = "RESET";
-parameter CCU2_INJECT1_0 = "NO";
-parameter CCU2_INJECT1_1 = "NO";
-
-endmodule
-
-(* blackbox *) (* keep *)
-module TRELLIS_IO(
- inout B,
- input I,
- input T,
- output O,
-);
-parameter DIR = "INPUT";
-
-endmodule
diff --git a/ecp5/synth/simple_map.v b/ecp5/synth/simple_map.v
deleted file mode 100644
index 550fa92c..00000000
--- a/ecp5/synth/simple_map.v
+++ /dev/null
@@ -1,68 +0,0 @@
-module \$_DFF_P_ (input D, C, output Q);
- TRELLIS_SLICE #(
- .MODE("LOGIC"),
- .CLKMUX("CLK"),
- .CEMUX("1"),
- .REG0_SD("0"),
- .REG0_REGSET("RESET"),
- .SRMODE("LSR_OVER_CE"),
- .GSR("DISABLED")
- ) _TECHMAP_REPLACE_ (
- .CLK(C),
- .M0(D),
- .Q0(Q)
- );
-endmodule
-
-module \$lut (A, Y);
- parameter WIDTH = 0;
- parameter LUT = 0;
-
- input [WIDTH-1:0] A;
- output Y;
-
- generate
- if (WIDTH == 1) begin
- TRELLIS_SLICE #(
- .MODE("LOGIC"),
- .LUT0_INITVAL({8{LUT[1:0]}})
- ) _TECHMAP_REPLACE_ (
- .A0(A[0]),
- .F0(Y)
- );
- end
- if (WIDTH == 2) begin
- TRELLIS_SLICE #(
- .MODE("LOGIC"),
- .LUT0_INITVAL({4{LUT[3:0]}})
- ) _TECHMAP_REPLACE_ (
- .A0(A[0]),
- .B0(A[1]),
- .F0(Y)
- );
- end
- if (WIDTH == 3) begin
- TRELLIS_SLICE #(
- .MODE("LOGIC"),
- .LUT0_INITVAL({2{LUT[7:0]}})
- ) _TECHMAP_REPLACE_ (
- .A0(A[0]),
- .B0(A[1]),
- .C0(A[2]),
- .F0(Y)
- );
- end
- if (WIDTH == 4) begin
- TRELLIS_SLICE #(
- .MODE("LOGIC"),
- .LUT0_INITVAL(LUT)
- ) _TECHMAP_REPLACE_ (
- .A0(A[0]),
- .B0(A[1]),
- .C0(A[2]),
- .D0(A[3]),
- .F0(Y)
- );
- end
- endgenerate
-endmodule
diff --git a/ecp5/synth/ulx3s.v b/ecp5/synth/ulx3s.v
deleted file mode 100644
index 08f6e65b..00000000
--- a/ecp5/synth/ulx3s.v
+++ /dev/null
@@ -1,18 +0,0 @@
-module top(input a_pin, output led_pin, output led2_pin, output gpio0_pin);
-
- wire a;
- wire led, led2;
- wire gpio0;
- (* BEL="X4/Y71/PIOA" *) (* IO_TYPE="LVCMOS33" *)
- TRELLIS_IO #(.DIR("INPUT")) a_buf (.B(a_pin), .O(a));
- (* BEL="X0/Y23/PIOC" *) (* IO_TYPE="LVCMOS33" *)
- TRELLIS_IO #(.DIR("OUTPUT")) led_buf (.B(led_pin), .I(led));
- (* BEL="X0/Y26/PIOA" *) (* IO_TYPE="LVCMOS33" *)
- TRELLIS_IO #(.DIR("OUTPUT")) led2_buf (.B(led2_pin), .I(led2));
- (* BEL="X0/Y62/PIOD" *) (* IO_TYPE="LVCMOS33" *)
- TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0));
- assign led = a;
- assign led2 = !a;
-
- TRELLIS_SLICE #(.MODE("LOGIC"), .LUT0_INITVAL(16'hFFFF)) vcc (.F0(gpio0));
-endmodule
diff --git a/ecp5/synth/ulx3s.ys b/ecp5/synth/ulx3s.ys
deleted file mode 100644
index d741c985..00000000
--- a/ecp5/synth/ulx3s.ys
+++ /dev/null
@@ -1,9 +0,0 @@
-read_verilog ulx3s.v
-read_verilog -lib cells.v
-synth -top top
-abc -lut 4
-techmap -map simple_map.v
-splitnets
-opt_clean
-stat
-write_json ulx3s.json
diff --git a/ecp5/synth/wire.v b/ecp5/synth/wire.v
deleted file mode 100644
index 2af68ed2..00000000
--- a/ecp5/synth/wire.v
+++ /dev/null
@@ -1,11 +0,0 @@
-module top(input a_pin, output [3:0] led_pin);
-
- wire a;
- wire [3:0] led;
-
- TRELLIS_IO #(.DIR("INPUT")) a_buf (.B(a_pin), .O(a));
- TRELLIS_IO #(.DIR("OUTPUT")) led_buf [3:0] (.B(led_pin), .I(led));
-
- //assign led[0] = !a;
- always @(posedge a) led[0] <= !led[0];
-endmodule
diff --git a/ecp5/synth/wire.ys b/ecp5/synth/wire.ys
deleted file mode 100644
index f916588b..00000000
--- a/ecp5/synth/wire.ys
+++ /dev/null
@@ -1,9 +0,0 @@
-read_verilog wire.v
-read_verilog -lib cells.v
-synth -top top
-abc -lut 4
-techmap -map simple_map.v
-splitnets
-opt_clean
-stat
-write_json wire.json
diff --git a/ecp5/trellis_import.py b/ecp5/trellis_import.py
index 2bc32169..af5386e7 100755
--- a/ecp5/trellis_import.py
+++ b/ecp5/trellis_import.py
@@ -2,6 +2,8 @@
import pytrellis
import database
import argparse
+import json
+from os import path
location_types = dict()
type_at_location = dict()
@@ -319,6 +321,49 @@ bel_types = {
"PIO": 2
}
+def get_bel_index(ddrg, loc, name):
+ loctype = ddrg.locationTypes[ddrg.typeAtLocation[loc]]
+ idx = 0
+ for bel in loctype.bels:
+ if ddrg.to_str(bel.name) == name:
+ return idx
+ idx += 1
+ assert loc.y == max_row # Only missing IO should be special pins at bottom of device
+ return None
+
+
+packages = {}
+pindata = []
+
+def process_pio_db(ddrg, device):
+ piofile = path.join(database.get_db_root(), "ECP5", dev_names[device], "iodb.json")
+ with open(piofile, 'r') as f:
+ piodb = json.load(f)
+ for pkgname, pkgdata in sorted(piodb["packages"].items()):
+ pins = []
+ for name, pinloc in sorted(pkgdata.items()):
+ x = pinloc["col"]
+ y = pinloc["row"]
+ loc = pytrellis.Location(x, y)
+ pio = "PIO" + pinloc["pio"]
+ bel_idx = get_bel_index(ddrg, loc, pio)
+ if bel_idx is not None:
+ pins.append((name, loc, bel_idx))
+ packages[pkgname] = pins
+ for metaitem in piodb["pio_metadata"]:
+ x = metaitem["col"]
+ y = metaitem["row"]
+ loc = pytrellis.Location(x, y)
+ pio = "PIO" + metaitem["pio"]
+ bank = metaitem["bank"]
+ if "function" in metaitem:
+ pinfunc = metaitem["function"]
+ else:
+ pinfunc = None
+ bel_idx = get_bel_index(ddrg, loc, pio)
+ if bel_idx is not None:
+ pindata.append((loc, bel_idx, bank, pinfunc))
+
def write_database(dev_name, ddrg, endianness):
def write_loc(loc, sym_name):
@@ -409,6 +454,32 @@ def write_database(dev_name, ddrg, endianness):
for y in range(0, max_row+1):
for x in range(0, max_col+1):
bba.u32(loctypes.index(ddrg.typeAtLocation[pytrellis.Location(x, y)]), "loctype")
+ for package, pkgdata in sorted(packages.items()):
+ bba.l("package_data_%s" % package, "PackagePinPOD")
+ for pin in pkgdata:
+ name, loc, bel_idx = pin
+ bba.s(name, "name")
+ write_loc(loc, "abs_loc")
+ bba.u32(bel_idx, "bel_index")
+
+ bba.l("package_data", "PackageInfoPOD")
+ for package, pkgdata in sorted(packages.items()):
+ bba.s(package, "name")
+ bba.u32(len(pkgdata), "num_pins")
+ bba.r("package_data_%s" % package, "pin_data")
+
+ bba.l("pio_info", "PIOInfoPOD")
+ for pin in pindata:
+ loc, bel_idx, bank, func = pin
+ write_loc(loc, "abs_loc")
+ bba.u32(bel_idx, "bel_index")
+ if func is not None:
+ bba.s(func, "function_name")
+ else:
+ bba.r(None, "function_name")
+ bba.u16(bank, "bank")
+ bba.u16(0, "padding")
+
bba.l("tiletype_names", "RelPtr<char>")
for tt in tiletype_names:
@@ -419,9 +490,15 @@ def write_database(dev_name, ddrg, endianness):
bba.u32(max_row + 1, "height")
bba.u32((max_col + 1) * (max_row + 1), "num_tiles")
bba.u32(len(location_types), "num_location_types")
+ bba.u32(len(packages), "num_packages")
+ bba.u32(len(pindata), "num_pios")
+
bba.r("locations", "locations")
bba.r("location_types", "location_type")
bba.r("tiletype_names", "tiletype_names")
+ bba.r("package_data", "package_info")
+ bba.r("pio_info", "pio_info")
+
bba.finalize()
return bba
@@ -451,6 +528,7 @@ def main():
ddrg = pytrellis.make_dedup_chipdb(chip)
max_row = chip.get_max_row()
max_col = chip.get_max_col()
+ process_pio_db(ddrg, args.device)
print("{} unique location types".format(len(ddrg.locationTypes)))
bba = write_database(args.device, ddrg, "le")
diff --git a/generic/archdefs.h b/generic/archdefs.h
index 9969014b..06d4ec6e 100644
--- a/generic/archdefs.h
+++ b/generic/archdefs.h
@@ -52,4 +52,11 @@ typedef IdString PipId;
typedef IdString GroupId;
typedef IdString DecalId;
+struct ArchNetInfo
+{
+};
+struct ArchCellInfo
+{
+};
+
NEXTPNR_NAMESPACE_END
diff --git a/gui/base.qrc b/gui/base.qrc
index bf21986b..1a848f54 100644
--- a/gui/base.qrc
+++ b/gui/base.qrc
@@ -9,6 +9,6 @@
<file>resources/resultset_previous.png</file>
<file>resources/resultset_next.png</file>
<file>resources/resultset_last.png</file>
- <file>resources/splash.png</file>
+ <file>resources/cross.png</file>
</qresource>
</RCC>
diff --git a/gui/basewindow.cc b/gui/basewindow.cc
index 81c89e45..4a225bd6 100644
--- a/gui/basewindow.cc
+++ b/gui/basewindow.cc
@@ -29,7 +29,6 @@
#include "log.h"
#include "mainwindow.h"
#include "pythontab.h"
-#include "yosystab.h"
static void initBasenameResource() { Q_INIT_RESOURCE(base); }
@@ -95,29 +94,12 @@ BaseMainWindow::BaseMainWindow(std::unique_ptr<Context> context, QWidget *parent
splitter_v->addWidget(centralTabWidget);
splitter_v->addWidget(tabWidget);
- displaySplash();
}
BaseMainWindow::~BaseMainWindow() {}
void BaseMainWindow::closeTab(int index) { delete centralTabWidget->widget(index); }
-void BaseMainWindow::displaySplash()
-{
- splash = new QSplashScreen();
- splash->setPixmap(QPixmap(":/icons/resources/splash.png"));
- splash->show();
- connect(designview, SIGNAL(finishContextLoad()), splash, SLOT(close()));
- connect(designview, SIGNAL(contextLoadStatus(std::string)), this, SLOT(displaySplashMessage(std::string)));
- QCoreApplication::instance()->processEvents();
-}
-
-void BaseMainWindow::displaySplashMessage(std::string msg)
-{
- splash->showMessage(msg.c_str(), Qt::AlignCenter | Qt::AlignBottom, Qt::white);
- QCoreApplication::instance()->processEvents();
-}
-
void BaseMainWindow::writeInfo(std::string text) { console->info(text); }
void BaseMainWindow::createMenusAndBars()
@@ -147,10 +129,6 @@ void BaseMainWindow::createMenusAndBars()
actionExit->setStatusTip("Exit the application");
connect(actionExit, SIGNAL(triggered()), this, SLOT(close()));
- QAction *actionYosys = new QAction("Yosys", this);
- actionYosys->setStatusTip("Run Yosys");
- connect(actionYosys, SIGNAL(triggered()), this, SLOT(yosys()));
-
QAction *actionAbout = new QAction("About", this);
menuBar = new QMenuBar();
@@ -183,19 +161,6 @@ void BaseMainWindow::createMenusAndBars()
mainToolBar->addAction(actionNew);
mainToolBar->addAction(actionOpen);
mainToolBar->addAction(actionSave);
- mainToolBar->addAction(actionYosys);
}
-void BaseMainWindow::yosys()
-{
- QString folder = QFileDialog::getExistingDirectory(0, ("Select Work Folder"), QDir::currentPath(),
- QFileDialog::ShowDirsOnly);
- if (!folder.isEmpty() && !folder.isNull()) {
- YosysTab *yosysTab = new YosysTab(folder);
- yosysTab->setAttribute(Qt::WA_DeleteOnClose);
- centralTabWidget->addTab(yosysTab, "Yosys");
- centralTabWidget->setCurrentWidget(yosysTab);
- centralTabWidget->setTabToolTip(centralTabWidget->indexOf(yosysTab), folder);
- }
-}
NEXTPNR_NAMESPACE_END
diff --git a/gui/basewindow.h b/gui/basewindow.h
index 087880ed..eee426c7 100644
--- a/gui/basewindow.h
+++ b/gui/basewindow.h
@@ -26,7 +26,6 @@
#include <QMenu>
#include <QMenuBar>
#include <QProgressBar>
-#include <QSplashScreen>
#include <QStatusBar>
#include <QTabWidget>
#include <QToolBar>
@@ -50,17 +49,14 @@ class BaseMainWindow : public QMainWindow
protected:
void createMenusAndBars();
- void displaySplash();
protected Q_SLOTS:
void writeInfo(std::string text);
- void displaySplashMessage(std::string msg);
void closeTab(int index);
virtual void new_proj() = 0;
virtual void open_proj() = 0;
virtual bool save_proj() = 0;
- void yosys();
Q_SIGNALS:
void contextChanged(Context *ctx);
@@ -78,7 +74,6 @@ class BaseMainWindow : public QMainWindow
QAction *actionNew;
QAction *actionOpen;
QProgressBar *progressBar;
- QSplashScreen *splash;
DesignWidget *designview;
};
diff --git a/gui/designwidget.cc b/gui/designwidget.cc
index 335ed929..d28b843b 100644
--- a/gui/designwidget.cc
+++ b/gui/designwidget.cc
@@ -125,11 +125,27 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), net
updateButtons();
});
+ actionClear = new QAction("", this);
+ actionClear->setIcon(QIcon(":/icons/resources/cross.png"));
+ actionClear->setEnabled(true);
+ connect(actionClear, &QAction::triggered, this, [this] {
+ history_index = -1;
+ history.clear();
+ QTreeWidgetItem *clickItem = treeWidget->selectedItems().at(0);
+ if (clickItem->parent()) {
+ ElementType type = static_cast<ElementTreeItem *>(clickItem)->getType();
+ if (type != ElementType::NONE)
+ addToHistory(treeWidget->selectedItems().at(0));
+ }
+ updateButtons();
+ });
+
QToolBar *toolbar = new QToolBar();
toolbar->addAction(actionFirst);
toolbar->addAction(actionPrev);
toolbar->addAction(actionNext);
toolbar->addAction(actionLast);
+ toolbar->addAction(actionClear);
QWidget *topWidget = new QWidget();
QVBoxLayout *vbox1 = new QVBoxLayout();
@@ -230,7 +246,6 @@ void DesignWidget::newContext(Context *ctx)
bel_root->setText(0, "Bels");
treeWidget->insertTopLevelItem(0, bel_root);
if (ctx) {
- Q_EMIT contextLoadStatus("Configuring bels...");
for (auto bel : ctx->getBels()) {
auto id = ctx->getBelName(bel);
QStringList items = QString(id.c_str(ctx)).split("/");
@@ -263,7 +278,6 @@ void DesignWidget::newContext(Context *ctx)
wire_root->setText(0, "Wires");
treeWidget->insertTopLevelItem(0, wire_root);
if (ctx) {
- Q_EMIT contextLoadStatus("Configuring wires...");
for (auto wire : ctx->getWires()) {
auto id = ctx->getWireName(wire);
QStringList items = QString(id.c_str(ctx)).split("/");
@@ -295,7 +309,6 @@ void DesignWidget::newContext(Context *ctx)
pip_root->setText(0, "Pips");
treeWidget->insertTopLevelItem(0, pip_root);
if (ctx) {
- Q_EMIT contextLoadStatus("Configuring pips...");
for (auto pip : ctx->getPips()) {
auto id = ctx->getPipName(pip);
QStringList items = QString(id.c_str(ctx)).split("/");
@@ -321,18 +334,7 @@ void DesignWidget::newContext(Context *ctx)
for (auto pip : nameToItem[2].toStdMap()) {
pip_root->addChild(pip.second);
}
-
- // Add nets to tree
- nets_root = new QTreeWidgetItem(treeWidget);
- nets_root->setText(0, "Nets");
- treeWidget->insertTopLevelItem(0, nets_root);
-
- // Add cells to tree
- cells_root = new QTreeWidgetItem(treeWidget);
- cells_root->setText(0, "Cells");
- treeWidget->insertTopLevelItem(0, cells_root);
-
- Q_EMIT finishContextLoad();
+ updateTree();
}
void DesignWidget::updateTree()
@@ -477,13 +479,12 @@ void DesignWidget::onItemSelectionChanged()
addToHistory(clickItem);
clearProperties();
- if (type == ElementType::BEL) {
- IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
- BelId bel = ctx->getBelByName(c);
- decals.push_back(ctx->getBelDecal(bel));
- Q_EMIT selected(decals);
+ IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
+ Q_EMIT selected(getDecals(type, c));
+ if (type == ElementType::BEL) {
+ BelId bel = ctx->getBelByName(c);
QtProperty *topItem = addTopLevelProperty("Bel");
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
@@ -494,12 +495,7 @@ void DesignWidget::onItemSelectionChanged()
ElementType::CELL);
} else if (type == ElementType::WIRE) {
- IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
WireId wire = ctx->getWireByName(c);
-
- decals.push_back(ctx->getWireDecal(wire));
- Q_EMIT selected(decals);
-
QtProperty *topItem = addTopLevelProperty("Wire");
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
@@ -551,12 +547,7 @@ void DesignWidget::onItemSelectionChanged()
}
}
} else if (type == ElementType::PIP) {
- IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
PipId pip = ctx->getPipByName(c);
-
- decals.push_back(ctx->getPipDecal(pip));
- Q_EMIT selected(decals);
-
QtProperty *topItem = addTopLevelProperty("Pip");
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
@@ -576,7 +567,6 @@ void DesignWidget::onItemSelectionChanged()
addProperty(delayItem, QVariant::Double, "Fall", delay.fallDelay());
addProperty(delayItem, QVariant::Double, "Average", delay.avgDelay());
} else if (type == ElementType::NET) {
- IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
NetInfo *net = ctx->nets.at(c).get();
QtProperty *topItem = addTopLevelProperty("Net");
@@ -613,7 +603,7 @@ void DesignWidget::onItemSelectionChanged()
auto name = ctx->getWireName(item.first).c_str(ctx);
QtProperty *wireItem = addSubGroup(wiresItem, name);
- addProperty(wireItem, QVariant::String, "Name", name);
+ addProperty(wireItem, QVariant::String, "Wire", name, ElementType::WIRE);
if (item.second.pip != PipId())
addProperty(wireItem, QVariant::String, "Pip", ctx->getPipName(item.second.pip).c_str(ctx),
@@ -625,7 +615,6 @@ void DesignWidget::onItemSelectionChanged()
}
} else if (type == ElementType::CELL) {
- IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
CellInfo *cell = ctx->cells.at(c).get();
QtProperty *topItem = addTopLevelProperty("Cell");
@@ -689,19 +678,28 @@ std::vector<DecalXY> DesignWidget::getDecals(ElementType type, IdString value)
WireId wire = ctx->getWireByName(value);
if (wire != WireId()) {
decals.push_back(ctx->getWireDecal(wire));
- Q_EMIT selected(decals);
}
} break;
case ElementType::PIP: {
PipId pip = ctx->getPipByName(value);
if (pip != PipId()) {
decals.push_back(ctx->getPipDecal(pip));
- Q_EMIT selected(decals);
}
} break;
case ElementType::NET: {
+ NetInfo *net = ctx->nets.at(value).get();
+ for (auto &item : net->wires) {
+ decals.push_back(ctx->getWireDecal(item.first));
+ if (item.second.pip != PipId()) {
+ decals.push_back(ctx->getPipDecal(item.second.pip));
+ }
+ }
} break;
case ElementType::CELL: {
+ CellInfo *cell = ctx->cells.at(value).get();
+ if (cell->bel != BelId()) {
+ decals.push_back(ctx->getBelDecal(cell->bel));
+ }
} break;
default:
break;
diff --git a/gui/designwidget.h b/gui/designwidget.h
index 1afe817d..b5877f60 100644
--- a/gui/designwidget.h
+++ b/gui/designwidget.h
@@ -65,8 +65,6 @@ class DesignWidget : public QWidget
void info(std::string text);
void selected(std::vector<DecalXY> decal);
void highlight(std::vector<DecalXY> decal, int group);
- void finishContextLoad();
- void contextLoadStatus(std::string text);
private Q_SLOTS:
void prepareMenuProperty(const QPoint &pos);
@@ -104,6 +102,7 @@ class DesignWidget : public QWidget
QAction *actionPrev;
QAction *actionNext;
QAction *actionLast;
+ QAction *actionClear;
QColor highlightColors[8];
QMap<QTreeWidgetItem *, int> highlightSelected;
diff --git a/gui/ice40/mainwindow.cc b/gui/ice40/mainwindow.cc
index 4b1f2c57..28792ed3 100644
--- a/gui/ice40/mainwindow.cc
+++ b/gui/ice40/mainwindow.cc
@@ -226,7 +226,6 @@ void MainWindow::new_proj()
ctx = std::unique_ptr<Context>(new Context(chipArgs));
actionLoadJSON->setEnabled(true);
- Q_EMIT displaySplash();
Q_EMIT contextChanged(ctx.get());
}
}
diff --git a/gui/infotab.cc b/gui/infotab.cc
deleted file mode 100644
index dd44b806..00000000
--- a/gui/infotab.cc
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * nextpnr -- Next Generation Place and Route
- *
- * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
-
-#include "infotab.h"
-#include <QGridLayout>
-
-NEXTPNR_NAMESPACE_BEGIN
-
-InfoTab::InfoTab(QWidget *parent) : QWidget(parent)
-{
- plainTextEdit = new QPlainTextEdit();
- plainTextEdit->setReadOnly(true);
- QFont f("unexistent");
- f.setStyleHint(QFont::Monospace);
- plainTextEdit->setFont(f);
-
- plainTextEdit->setContextMenuPolicy(Qt::CustomContextMenu);
- QAction *clearAction = new QAction("Clear &buffer", this);
- clearAction->setStatusTip("Clears display buffer");
- connect(clearAction, SIGNAL(triggered()), this, SLOT(clearBuffer()));
- contextMenu = plainTextEdit->createStandardContextMenu();
- contextMenu->addSeparator();
- contextMenu->addAction(clearAction);
- connect(plainTextEdit, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint)));
-
- QGridLayout *mainLayout = new QGridLayout();
- mainLayout->addWidget(plainTextEdit);
- setLayout(mainLayout);
-}
-
-void InfoTab::info(std::string str)
-{
- plainTextEdit->moveCursor(QTextCursor::End);
- plainTextEdit->insertPlainText(str.c_str());
- plainTextEdit->moveCursor(QTextCursor::End);
-}
-
-void InfoTab::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); }
-
-void InfoTab::clearBuffer() { plainTextEdit->clear(); }
-
-NEXTPNR_NAMESPACE_END
diff --git a/gui/infotab.h b/gui/infotab.h
deleted file mode 100644
index 41529973..00000000
--- a/gui/infotab.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * nextpnr -- Next Generation Place and Route
- *
- * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
-
-#ifndef INFOTAB_H
-#define INFOTAB_H
-
-#include <QMenu>
-#include <QPlainTextEdit>
-#include "nextpnr.h"
-
-NEXTPNR_NAMESPACE_BEGIN
-
-class InfoTab : public QWidget
-{
- Q_OBJECT
-
- public:
- explicit InfoTab(QWidget *parent = 0);
- void info(std::string str);
- public Q_SLOTS:
- void clearBuffer();
- private Q_SLOTS:
- void showContextMenu(const QPoint &pt);
-
- private:
- QPlainTextEdit *plainTextEdit;
- QMenu *contextMenu;
-};
-
-NEXTPNR_NAMESPACE_END
-
-#endif // INFOTAB_H
diff --git a/gui/resources/cross.png b/gui/resources/cross.png
new file mode 100644
index 00000000..1514d51a
--- /dev/null
+++ b/gui/resources/cross.png
Binary files differ
diff --git a/gui/resources/splash.png b/gui/resources/splash.png
deleted file mode 100644
index 14d2842b..00000000
--- a/gui/resources/splash.png
+++ /dev/null
Binary files differ
diff --git a/gui/yosys_edit.cc b/gui/yosys_edit.cc
deleted file mode 100644
index 72d2430d..00000000
--- a/gui/yosys_edit.cc
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * nextpnr -- Next Generation Place and Route
- *
- * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
- * Copyright (C) 2018 Alex Tsui
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
-
-#include "yosys_edit.h"
-#include <QKeyEvent>
-#include <QToolTip>
-
-NEXTPNR_NAMESPACE_BEGIN
-
-YosysLineEditor::YosysLineEditor(QWidget *parent) : QLineEdit(parent), index(0)
-{
- setContextMenuPolicy(Qt::CustomContextMenu);
- QAction *clearAction = new QAction("Clear &history", this);
- clearAction->setStatusTip("Clears line edit history");
- connect(clearAction, SIGNAL(triggered()), this, SLOT(clearHistory()));
- contextMenu = createStandardContextMenu();
- contextMenu->addSeparator();
- contextMenu->addAction(clearAction);
-
- connect(this, SIGNAL(returnPressed()), SLOT(textInserted()));
- connect(this, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint)));
-}
-
-void YosysLineEditor::keyPressEvent(QKeyEvent *ev)
-{
-
- if (ev->key() == Qt::Key_Up || ev->key() == Qt::Key_Down) {
- QToolTip::hideText();
- if (lines.empty())
- return;
- if (ev->key() == Qt::Key_Up)
- index--;
- if (ev->key() == Qt::Key_Down)
- index++;
-
- if (index < 0)
- index = 0;
- if (index >= lines.size()) {
- index = lines.size();
- clear();
- return;
- }
- setText(lines[index]);
- } else if (ev->key() == Qt::Key_Escape) {
- QToolTip::hideText();
- clear();
- return;
- } else if (ev->key() == Qt::Key_Tab) {
- return;
- }
- QToolTip::hideText();
-
- QLineEdit::keyPressEvent(ev);
-}
-
-// This makes TAB work
-bool YosysLineEditor::focusNextPrevChild(bool next) { return false; }
-
-void YosysLineEditor::textInserted()
-{
- if (lines.empty() || lines.back() != text())
- lines += text();
- if (lines.size() > 100)
- lines.removeFirst();
- index = lines.size();
- clear();
- Q_EMIT textLineInserted(lines.back());
-}
-
-void YosysLineEditor::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); }
-
-void YosysLineEditor::clearHistory()
-{
- lines.clear();
- index = 0;
- clear();
-}
-
-NEXTPNR_NAMESPACE_END
diff --git a/gui/yosys_edit.h b/gui/yosys_edit.h
deleted file mode 100644
index 05e4ae36..00000000
--- a/gui/yosys_edit.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * nextpnr -- Next Generation Place and Route
- *
- * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
- * Copyright (C) 2018 Alex Tsui
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
-
-#ifndef YOSYS_EDIT_H
-#define YOSYS_EDIT_H
-
-#include <QLineEdit>
-#include <QMenu>
-#include "nextpnr.h"
-
-NEXTPNR_NAMESPACE_BEGIN
-
-class YosysLineEditor : public QLineEdit
-{
- Q_OBJECT
-
- public:
- explicit YosysLineEditor(QWidget *parent = 0);
-
- private Q_SLOTS:
- void textInserted();
- void showContextMenu(const QPoint &pt);
- void clearHistory();
-
- Q_SIGNALS:
- void textLineInserted(QString);
-
- protected:
- void keyPressEvent(QKeyEvent *) Q_DECL_OVERRIDE;
- bool focusNextPrevChild(bool next) Q_DECL_OVERRIDE;
-
- private:
- int index;
- QStringList lines;
- QMenu *contextMenu;
-};
-
-NEXTPNR_NAMESPACE_END
-
-#endif // YOSYS_EDIT_H
diff --git a/gui/yosystab.cc b/gui/yosystab.cc
deleted file mode 100644
index d83b969e..00000000
--- a/gui/yosystab.cc
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * nextpnr -- Next Generation Place and Route
- *
- * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
-
-#include "yosystab.h"
-#include <QGridLayout>
-#include <QMessageBox>
-
-NEXTPNR_NAMESPACE_BEGIN
-
-YosysTab::YosysTab(QString folder, QWidget *parent) : QWidget(parent)
-{
- QFont f("unexistent");
- f.setStyleHint(QFont::Monospace);
-
- console = new QPlainTextEdit();
- console->setMinimumHeight(100);
- console->setReadOnly(true);
- console->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
- console->setFont(f);
-
- console->setContextMenuPolicy(Qt::CustomContextMenu);
- QAction *clearAction = new QAction("Clear &buffer", this);
- clearAction->setStatusTip("Clears display buffer");
- connect(clearAction, SIGNAL(triggered()), this, SLOT(clearBuffer()));
- contextMenu = console->createStandardContextMenu();
- contextMenu->addSeparator();
- contextMenu->addAction(clearAction);
- connect(console, SIGNAL(customContextMenuRequested(const QPoint)), this, SLOT(showContextMenu(const QPoint)));
-
- lineEdit = new YosysLineEditor();
- lineEdit->setMinimumHeight(30);
- lineEdit->setMaximumHeight(30);
- lineEdit->setFont(f);
- lineEdit->setFocus();
- lineEdit->setEnabled(false);
- lineEdit->setPlaceholderText("yosys>");
- connect(lineEdit, SIGNAL(textLineInserted(QString)), this, SLOT(editLineReturnPressed(QString)));
-
- QGridLayout *mainLayout = new QGridLayout();
- mainLayout->addWidget(console, 0, 0);
- mainLayout->addWidget(lineEdit, 1, 0);
- setLayout(mainLayout);
-
- process = new QProcess();
- connect(process, SIGNAL(readyReadStandardError()), this, SLOT(onReadyReadStandardError()));
- connect(process, SIGNAL(readyReadStandardOutput()), this, SLOT(onReadyReadStandardOutput()));
- connect(process, &QProcess::started, this, [this] { lineEdit->setEnabled(true); });
-/*
-#if QT_VERSION < QT_VERSION_CHECK(5, 6, 0)
- connect(process, &QProcess::error, this, [this](QProcess::ProcessError error) {
-#else
- connect(process, &QProcess::errorOccurred, this, [this](QProcess::ProcessError error) {
-#endif
- if (error == QProcess::FailedToStart) {
- QMessageBox::critical(
- this, QString::fromUtf8("Yosys cannot be started!"),
- QString::fromUtf8("<p>Please make sure you have Yosys installed and available in path</p>"));
- Q_EMIT deleteLater();
- }
- });
-*/
- process->setWorkingDirectory(folder);
- process->start("yosys");
-}
-
-YosysTab::~YosysTab()
-{
- process->terminate();
- process->waitForFinished(1000); // in ms
- process->kill();
- process->close();
-}
-
-void YosysTab::displayString(QString text)
-{
- QTextCursor cursor = console->textCursor();
- cursor.movePosition(QTextCursor::End);
- cursor.insertText(text);
- cursor.movePosition(QTextCursor::End);
- console->setTextCursor(cursor);
-}
-
-void YosysTab::onReadyReadStandardOutput() { displayString(process->readAllStandardOutput()); }
-void YosysTab::onReadyReadStandardError() { displayString(process->readAllStandardError()); }
-
-void YosysTab::editLineReturnPressed(QString text) { process->write(text.toLatin1() + "\n"); }
-
-void YosysTab::showContextMenu(const QPoint &pt) { contextMenu->exec(mapToGlobal(pt)); }
-
-void YosysTab::clearBuffer() { console->clear(); }
-
-NEXTPNR_NAMESPACE_END
diff --git a/gui/yosystab.h b/gui/yosystab.h
deleted file mode 100644
index 1c668d15..00000000
--- a/gui/yosystab.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * nextpnr -- Next Generation Place and Route
- *
- * Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- */
-
-#ifndef YOSYSTAB_H
-#define YOSYSTAB_H
-
-#include <QLineEdit>
-#include <QMenu>
-#include <QPlainTextEdit>
-#include <QProcess>
-#include "nextpnr.h"
-#include "yosys_edit.h"
-
-NEXTPNR_NAMESPACE_BEGIN
-
-class YosysTab : public QWidget
-{
- Q_OBJECT
-
- public:
- explicit YosysTab(QString folder, QWidget *parent = 0);
- ~YosysTab();
-
- private:
- void displayString(QString text);
- private Q_SLOTS:
- void showContextMenu(const QPoint &pt);
- void editLineReturnPressed(QString text);
- void onReadyReadStandardOutput();
- void onReadyReadStandardError();
- public Q_SLOTS:
- void clearBuffer();
-
- private:
- QPlainTextEdit *console;
- YosysLineEditor *lineEdit;
- QMenu *contextMenu;
- QProcess *process;
-};
-
-NEXTPNR_NAMESPACE_END
-
-#endif // YOSYSTAB_H
diff --git a/ice40/arch.cc b/ice40/arch.cc
index 69848aff..786d8ba1 100644
--- a/ice40/arch.cc
+++ b/ice40/arch.cc
@@ -25,7 +25,7 @@
#include "placer1.h"
#include "router1.h"
#include "util.h"
-
+#include "cells.h"
NEXTPNR_NAMESPACE_BEGIN
// -----------------------------------------------------------------------
@@ -44,8 +44,8 @@ IdString Arch::belTypeToId(BelType type) const
return id("ICESTORM_PLL");
if (type == TYPE_SB_WARMBOOT)
return id("SB_WARMBOOT");
- if (type == TYPE_SB_MAC16)
- return id("SB_MAC16");
+ if (type == TYPE_ICESTORM_DSP)
+ return id("ICESTORM_DSP");
if (type == TYPE_ICESTORM_HFOSC)
return id("ICESTORM_HFOSC");
if (type == TYPE_ICESTORM_LFOSC)
@@ -79,8 +79,8 @@ BelType Arch::belTypeFromId(IdString type) const
return TYPE_ICESTORM_PLL;
if (type == id("SB_WARMBOOT"))
return TYPE_SB_WARMBOOT;
- if (type == id("SB_MAC16"))
- return TYPE_SB_MAC16;
+ if (type == id("ICESTORM_DSP"))
+ return TYPE_ICESTORM_DSP;
if (type == id("ICESTORM_HFOSC"))
return TYPE_ICESTORM_HFOSC;
if (type == id("ICESTORM_LFOSC"))
@@ -610,8 +610,7 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
}
if (bel_type == TYPE_ICESTORM_RAM) {
- for (int i = 0; i < 2; i++)
- {
+ for (int i = 0; i < 2; i++) {
int tx = chip_info->bel_data[bel.index].x;
int ty = chip_info->bel_data[bel.index].y + i;
@@ -621,7 +620,7 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1;
- el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 + 7*logic_cell_pitch;
+ el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 + 7 * logic_cell_pitch;
el.z = 0;
ret.push_back(el);
@@ -698,4 +697,47 @@ bool Arch::isGlobalNet(const NetInfo *net) const
return net->driver.cell != nullptr && net->driver.port == id_glb_buf_out;
}
+// Assign arch arg info
+void Arch::assignArchInfo()
+{
+ for (auto &net : getCtx()->nets) {
+ NetInfo *ni = net.second.get();
+ if (isGlobalNet(ni))
+ ni->is_global = true;
+ ni->is_enable = false;
+ ni->is_reset = false;
+ for (auto usr : ni->users) {
+ if (is_enable_port(this, usr))
+ ni->is_enable = true;
+ if (is_reset_port(this, usr))
+ ni->is_reset = true;
+ }
+ }
+ for (auto &cell : getCtx()->cells) {
+ CellInfo *ci = cell.second.get();
+ assignCellInfo(ci);
+ }
+}
+
+void Arch::assignCellInfo(CellInfo *cell)
+{
+ cell->belType = belTypeFromId(cell->type);
+ if (cell->type == id_icestorm_lc) {
+ cell->lcInfo.dffEnable = bool_or_default(cell->params, id_dff_en);
+ cell->lcInfo.negClk = bool_or_default(cell->params, id_neg_clk);
+ cell->lcInfo.clk = get_net_or_empty(cell, id_clk);
+ cell->lcInfo.cen = get_net_or_empty(cell, id_cen);
+ cell->lcInfo.sr = get_net_or_empty(cell, id_sr);
+ cell->lcInfo.inputCount = 0;
+ if (get_net_or_empty(cell, id_i0))
+ cell->lcInfo.inputCount++;
+ if (get_net_or_empty(cell, id_i1))
+ cell->lcInfo.inputCount++;
+ if (get_net_or_empty(cell, id_i2))
+ cell->lcInfo.inputCount++;
+ if (get_net_or_empty(cell, id_i3))
+ cell->lcInfo.inputCount++;
+ }
+}
+
NEXTPNR_NAMESPACE_END
diff --git a/ice40/arch.h b/ice40/arch.h
index d840c6ea..805876e4 100644
--- a/ice40/arch.h
+++ b/ice40/arch.h
@@ -153,15 +153,31 @@ NPNR_PACKED_STRUCT(struct BitstreamInfoPOD {
RelPtr<IerenInfoPOD> ierens;
});
+NPNR_PACKED_STRUCT(struct BelConfigEntryPOD {
+ RelPtr<char> entry_name;
+ RelPtr<char> cbit_name;
+ int8_t x, y;
+ int16_t padding;
+});
+
+// Stores mapping between bel parameters and config bits,
+// for extra cells where this mapping is non-trivial
+NPNR_PACKED_STRUCT(struct BelConfigPOD {
+ int32_t bel_index;
+ int32_t num_entries;
+ RelPtr<BelConfigEntryPOD> entries;
+});
+
NPNR_PACKED_STRUCT(struct ChipInfoPOD {
int32_t width, height;
int32_t num_bels, num_wires, num_pips;
- int32_t num_switches, num_packages;
+ int32_t num_switches, num_belcfgs, num_packages;
RelPtr<BelInfoPOD> bel_data;
RelPtr<WireInfoPOD> wire_data;
RelPtr<PipInfoPOD> pip_data;
RelPtr<TileType> tile_grid;
RelPtr<BitstreamInfoPOD> bits_info;
+ RelPtr<BelConfigPOD> bel_config;
RelPtr<PackageInfoPOD> packages_data;
});
@@ -611,7 +627,7 @@ struct Arch : BaseCtx
range.e.cursor = chip_info->num_pips;
return range;
}
-
+
IdString getPipName(PipId pip) const;
uint32_t getPipChecksum(PipId pip) const { return pip.index; }
@@ -732,6 +748,12 @@ struct Arch : BaseCtx
// Helper function for above
bool logicCellsCompatible(const std::vector<const CellInfo *> &cells) const;
+ // -------------------------------------------------
+ // Assign architecure-specific arguments to nets and cells, which must be called between packing or further
+ // netlist modifications, and validity checks
+ void assignArchInfo();
+ void assignCellInfo(CellInfo *cell);
+
IdString id_glb_buf_out;
IdString id_icestorm_lc, id_sb_io, id_sb_gb;
IdString id_cen, id_clk, id_sr;
diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc
index dc1bc3eb..116ab7d3 100644
--- a/ice40/arch_place.cc
+++ b/ice40/arch_place.cc
@@ -31,45 +31,37 @@ bool Arch::logicCellsCompatible(const std::vector<const CellInfo *> &cells) cons
int locals_count = 0;
for (auto cell : cells) {
- if (bool_or_default(cell->params, id_dff_en)) {
+ NPNR_ASSERT(cell->belType == TYPE_ICESTORM_LC);
+ if (cell->lcInfo.dffEnable) {
if (!dffs_exist) {
dffs_exist = true;
- cen = get_net_or_empty(cell, id_cen);
- clk = get_net_or_empty(cell, id_clk);
- sr = get_net_or_empty(cell, id_sr);
+ cen = cell->lcInfo.cen;
+ clk = cell->lcInfo.clk;
+ sr = cell->lcInfo.sr;
- if (!isGlobalNet(cen) && cen != nullptr)
+ if (cen != nullptr && !cen->is_global)
locals_count++;
- if (!isGlobalNet(clk) && clk != nullptr)
+ if (clk != nullptr && !clk->is_global)
locals_count++;
- if (!isGlobalNet(sr) && sr != nullptr)
+ if (sr != nullptr && !sr->is_global)
locals_count++;
- if (bool_or_default(cell->params, id_neg_clk)) {
+ if (cell->lcInfo.negClk) {
dffs_neg = true;
}
} else {
- if (cen != get_net_or_empty(cell, id_cen))
+ if (cen != cell->lcInfo.cen)
return false;
- if (clk != get_net_or_empty(cell, id_clk))
+ if (clk != cell->lcInfo.clk)
return false;
- if (sr != get_net_or_empty(cell, id_sr))
+ if (sr != cell->lcInfo.sr)
return false;
- if (dffs_neg != bool_or_default(cell->params, id_neg_clk))
+ if (dffs_neg != cell->lcInfo.negClk)
return false;
}
}
- const NetInfo *i0 = get_net_or_empty(cell, id_i0), *i1 = get_net_or_empty(cell, id_i1),
- *i2 = get_net_or_empty(cell, id_i2), *i3 = get_net_or_empty(cell, id_i3);
- if (i0 != nullptr)
- locals_count++;
- if (i1 != nullptr)
- locals_count++;
- if (i2 != nullptr)
- locals_count++;
- if (i3 != nullptr)
- locals_count++;
+ locals_count += cell->lcInfo.inputCount;
}
return locals_count <= 32;
@@ -116,21 +108,15 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
} else if (cell->type == id_sb_io) {
return getBelPackagePin(bel) != "";
} else if (cell->type == id_sb_gb) {
- bool is_reset = false, is_cen = false;
NPNR_ASSERT(cell->ports.at(id_glb_buf_out).net != nullptr);
- for (auto user : cell->ports.at(id_glb_buf_out).net->users) {
- if (is_reset_port(this, user))
- is_reset = true;
- if (is_enable_port(this, user))
- is_cen = true;
- }
+ const NetInfo *net = cell->ports.at(id_glb_buf_out).net;
IdString glb_net = getWireName(getWireBelPin(bel, PIN_GLOBAL_BUFFER_OUTPUT));
int glb_id = std::stoi(std::string("") + glb_net.str(this).back());
- if (is_reset && is_cen)
+ if (net->is_reset && net->is_enable)
return false;
- else if (is_reset)
+ else if (net->is_reset)
return (glb_id % 2) == 0;
- else if (is_cen)
+ else if (net->is_enable)
return (glb_id % 2) == 1;
else
return true;
diff --git a/ice40/archdefs.h b/ice40/archdefs.h
index 75df678a..14b0d2be 100644
--- a/ice40/archdefs.h
+++ b/ice40/archdefs.h
@@ -54,7 +54,7 @@ enum BelType : int32_t
TYPE_SB_GB,
TYPE_ICESTORM_PLL,
TYPE_SB_WARMBOOT,
- TYPE_SB_MAC16,
+ TYPE_ICESTORM_DSP,
TYPE_ICESTORM_HFOSC,
TYPE_ICESTORM_LFOSC,
TYPE_SB_I2C,
@@ -150,6 +150,28 @@ struct DecalId
bool operator!=(const DecalId &other) const { return (type != other.type) || (index != other.index); }
};
+struct ArchNetInfo
+{
+ bool is_global = false;
+ bool is_reset = false, is_enable = false;
+};
+
+struct NetInfo;
+
+struct ArchCellInfo
+{
+ BelType belType = TYPE_NONE;
+ union
+ {
+ struct
+ {
+ bool dffEnable, negClk;
+ int inputCount;
+ const NetInfo *clk, *cen, *sr;
+ } lcInfo;
+ };
+};
+
NEXTPNR_NAMESPACE_END
namespace std {
@@ -201,5 +223,4 @@ template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DecalId>
return seed;
}
};
-
} // namespace std
diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc
index a62c6c09..1b6a9425 100644
--- a/ice40/bitstream.cc
+++ b/ice40/bitstream.cc
@@ -36,7 +36,7 @@ const ConfigEntryPOD &find_config(const TileInfoPOD &tile, const std::string &na
return tile.entries[i];
}
}
- NPNR_ASSERT_FALSE("unable to find config bit " + name);
+ NPNR_ASSERT_FALSE_STR("unable to find config bit " + name);
}
std::tuple<int8_t, int8_t, int8_t> get_ieren(const BitstreamInfoPOD &bi, int8_t x, int8_t y, int8_t z)
@@ -90,12 +90,115 @@ std::string get_param_str_or_def(const CellInfo *cell, const IdString param, std
char get_hexdigit(int i) { return std::string("0123456789ABCDEF").at(i); }
+static const BelConfigPOD &get_ec_config(const ChipInfoPOD *chip, BelId bel)
+{
+ for (int i = 0; i < chip->num_belcfgs; i++) {
+ if (chip->bel_config[i].bel_index == bel.index)
+ return chip->bel_config[i];
+ }
+ NPNR_ASSERT_FALSE("failed to find bel config");
+}
+
+typedef std::vector<std::vector<std::vector<std::vector<int8_t>>>> chipconfig_t;
+
+static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfigPOD &cell_cbits, std::string name,
+ bool value)
+{
+ const ChipInfoPOD *chip = ctx->chip_info;
+
+ for (int i = 0; i < cell_cbits.num_entries; i++) {
+ const auto &cbit = cell_cbits.entries[i];
+ if (cbit.entry_name.get() == name) {
+ const auto &ti = chip->bits_info->tiles_nonrouting[tile_at(ctx, cbit.x, cbit.y)];
+ set_config(ti, config.at(cbit.y).at(cbit.x), std::string("IpConfig.") + cbit.cbit_name.get(), value);
+ return;
+ }
+ }
+ NPNR_ASSERT_FALSE_STR("failed to config extra cell config bit " + name);
+}
+
+void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *cell,
+ const std::vector<std::pair<std::string, int>> &params, bool string_style)
+{
+ const ChipInfoPOD *chip = ctx->chip_info;
+ const auto &bc = get_ec_config(chip, cell->bel);
+ for (auto p : params) {
+ std::vector<bool> value;
+ if (string_style) {
+ // Lattice's weird string style params, not sure if
+ // prefixes other than 0b should be supported, only 0b features in docs
+ std::string raw = get_param_str_or_def(cell, ctx->id(p.first), "0b0");
+ assert(raw.substr(0, 2) == "0b");
+ raw = raw.substr(2);
+ value.resize(raw.length());
+ for (int i = 0; i < (int)raw.length(); i++) {
+ if (raw[i] == '1') {
+ value[(raw.length() - 1) - i] = 1;
+ } else {
+ assert(raw[i] == '0');
+ value[(raw.length() - 1) - i] = 0;
+ }
+ }
+ } else {
+ int ival = get_param_or_def(cell, ctx->id(p.first), 0);
+
+ for (int i = 0; i < p.second; i++)
+ value.push_back((ival >> i) & 0x1);
+ }
+
+ value.resize(p.second);
+ if (p.second == 1) {
+ set_ec_cbit(config, ctx, bc, p.first, value.at(0));
+ } else {
+ for (int i = 0; i < p.second; i++) {
+ set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i));
+ }
+ }
+ }
+}
+
+std::string tagTileType(TileType &tile)
+{
+ if (tile == TILE_NONE)
+ return "";
+ switch (tile) {
+ case TILE_LOGIC:
+ return ".logic_tile";
+ break;
+ case TILE_IO:
+ return ".io_tile";
+ break;
+ case TILE_RAMB:
+ return ".ramb_tile";
+ break;
+ case TILE_RAMT:
+ return ".ramt_tile";
+ break;
+ case TILE_DSP0:
+ return ".dsp0_tile";
+ break;
+ case TILE_DSP1:
+ return ".dsp1_tile";
+ break;
+ case TILE_DSP2:
+ return ".dsp2_tile";
+ break;
+ case TILE_DSP3:
+ return ".dsp3_tile";
+ break;
+ case TILE_IPCON:
+ return ".ipcon_tile";
+ break;
+ default:
+ NPNR_ASSERT(false);
+ }
+}
void write_asc(const Context *ctx, std::ostream &out)
{
// [y][x][row][col]
const ChipInfoPOD &ci = *ctx->chip_info;
const BitstreamInfoPOD &bi = *ci.bits_info;
- std::vector<std::vector<std::vector<std::vector<int8_t>>>> config;
+ chipconfig_t config;
config.resize(ci.height);
for (int y = 0; y < ci.height; y++) {
config.at(y).resize(ci.width);
@@ -124,7 +227,7 @@ void write_asc(const Context *ctx, std::ostream &out)
out << ".device 5k" << std::endl;
break;
default:
- NPNR_ASSERT_FALSE("unsupported device type");
+ NPNR_ASSERT_FALSE("unsupported device type\n");
}
// Set pips
for (auto pip : ctx->getPips()) {
@@ -192,7 +295,7 @@ void write_asc(const Context *ctx, std::ostream &out)
bool val = (pin_type >> i) & 0x01;
set_config(ti, config.at(y).at(x), "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i), val);
}
-
+ set_config(ti, config.at(y).at(x), "NegClk", neg_trigger);
auto ieren = get_ieren(bi, x, y, z);
int iex, iey, iez;
std::tie(iex, iey, iez) = ieren;
@@ -265,6 +368,27 @@ void write_asc(const Context *ctx, std::ostream &out)
NPNR_ASSERT(false);
}
}
+ } else if (cell.second->type == ctx->id("ICESTORM_DSP")) {
+ const std::vector<std::pair<std::string, int>> mac16_params = {{"C_REG", 1},
+ {"A_REG", 1},
+ {"B_REG", 1},
+ {"D_REG", 1},
+ {"TOP_8x8_MULT_REG", 1},
+ {"BOT_8x8_MULT_REG", 1},
+ {"PIPELINE_16x16_MULT_REG1", 1},
+ {"PIPELINE_16x16_MULT_REG2", 1},
+ {"TOPOUTPUT_SELECT", 2},
+ {"TOPADDSUB_LOWERINPUT", 2},
+ {"TOPADDSUB_UPPERINPUT", 1},
+ {"TOPADDSUB_CARRYSELECT", 2},
+ {"BOTOUTPUT_SELECT", 2},
+ {"BOTADDSUB_LOWERINPUT", 2},
+ {"BOTADDSUB_UPPERINPUT", 1},
+ {"BOTADDSUB_CARRYSELECT", 2},
+ {"MODE_8x8", 1},
+ {"A_SIGNED", 1},
+ {"B_SIGNED", 1}};
+ configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false);
} else {
NPNR_ASSERT(false);
}
@@ -341,8 +465,9 @@ void write_asc(const Context *ctx, std::ostream &out)
set_config(ti, config.at(y).at(x),
"Cascade.IPCON_LC0" + std::to_string(lc_idx) + "_inmux02_5", true);
else
- set_config(ti, config.at(y).at(x), "Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) +
- "_LC0" + std::to_string(lc_idx) + "_inmux02_5",
+ set_config(ti, config.at(y).at(x),
+ "Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) + "_LC0" +
+ std::to_string(lc_idx) + "_inmux02_5",
true);
}
}
@@ -354,39 +479,7 @@ void write_asc(const Context *ctx, std::ostream &out)
for (int y = 0; y < ci.height; y++) {
for (int x = 0; x < ci.width; x++) {
TileType tile = tile_at(ctx, x, y);
- if (tile == TILE_NONE)
- continue;
- switch (tile) {
- case TILE_LOGIC:
- out << ".logic_tile";
- break;
- case TILE_IO:
- out << ".io_tile";
- break;
- case TILE_RAMB:
- out << ".ramb_tile";
- break;
- case TILE_RAMT:
- out << ".ramt_tile";
- break;
- case TILE_DSP0:
- out << ".dsp0_tile";
- break;
- case TILE_DSP1:
- out << ".dsp1_tile";
- break;
- case TILE_DSP2:
- out << ".dsp2_tile";
- break;
- case TILE_DSP3:
- out << ".dsp3_tile";
- break;
- case TILE_IPCON:
- out << ".ipcon_tile";
- break;
- default:
- NPNR_ASSERT(false);
- }
+ out << tagTileType(tile);
out << " " << x << " " << y << std::endl;
for (auto row : config.at(y).at(x)) {
for (auto col : row) {
@@ -437,4 +530,100 @@ void write_asc(const Context *ctx, std::ostream &out)
}
}
+void read_config(Context *ctx, std::istream &in, chipconfig_t &config)
+{
+ constexpr size_t line_buf_size = 65536;
+ char buffer[line_buf_size];
+ int tile_x = -1, tile_y = -1, line_nr = -1;
+
+ while (1) {
+ in.getline(buffer, line_buf_size);
+ if (buffer[0] == '.') {
+ line_nr = -1;
+ const char *tok = strtok(buffer, " \t\r\n");
+
+ if (!strcmp(tok, ".device")) {
+ std::string config_device = strtok(nullptr, " \t\r\n");
+ std::string expected;
+ switch (ctx->args.type) {
+ case ArchArgs::LP384:
+ expected = "384";
+ break;
+ case ArchArgs::HX1K:
+ case ArchArgs::LP1K:
+ expected = "1k";
+ break;
+ case ArchArgs::HX8K:
+ case ArchArgs::LP8K:
+ expected = "8k";
+ break;
+ case ArchArgs::UP5K:
+ expected = "5k";
+ break;
+ default:
+ log_error("unsupported device type\n");
+ }
+ if (expected != config_device)
+ log_error("device type does not match\n");
+ } else if (!strcmp(tok, ".io_tile") || !strcmp(tok, ".logic_tile") || !strcmp(tok, ".ramb_tile") ||
+ !strcmp(tok, ".ramt_tile") || !strcmp(tok, ".ipcon_tile") || !strcmp(tok, ".dsp0_tile") ||
+ !strcmp(tok, ".dsp1_tile") || !strcmp(tok, ".dsp2_tile") || !strcmp(tok, ".dsp3_tile")) {
+ line_nr = 0;
+ tile_x = atoi(strtok(nullptr, " \t\r\n"));
+ tile_y = atoi(strtok(nullptr, " \t\r\n"));
+
+ TileType tile = tile_at(ctx, tile_x, tile_y);
+ if (tok != tagTileType(tile))
+ log_error("Wrong tile type for specified position\n");
+
+ } else if (!strcmp(tok, ".extra_bit")) {
+ /*
+ int b = atoi(strtok(nullptr, " \t\r\n"));
+ int x = atoi(strtok(nullptr, " \t\r\n"));
+ int y = atoi(strtok(nullptr, " \t\r\n"));
+ std::tuple<int, int, int> key(b, x, y);
+ extra_bits.insert(key);
+ */
+ } else if (!strcmp(tok, ".sym")) {
+ int net = atoi(strtok(nullptr, " \t\r\n")); (void)net;
+ const char *name = strtok(nullptr, " \t\r\n");
+ std::unique_ptr<NetInfo> created_net = std::unique_ptr<NetInfo>(new NetInfo);
+ created_net->name = ctx->id(name);
+ ctx->nets[created_net->name] = std::move(created_net);
+ }
+ } else if (line_nr >= 0 && strlen(buffer) > 0) {
+ if (line_nr > int(config.at(tile_y).at(tile_x).size() - 1))
+ log_error("Invalid data in input asc file");
+ for (int i = 0; buffer[i] == '0' || buffer[i] == '1'; i++)
+ config.at(tile_y).at(tile_x).at(line_nr).at(i) = (buffer[i] == '1') ? 1 : 0;
+ line_nr++;
+ }
+ if (in.eof())
+ break;
+ }
+}
+
+bool read_asc(Context *ctx, std::istream &in)
+{
+ try {
+ // [y][x][row][col]
+ const ChipInfoPOD &ci = *ctx->chip_info;
+ const BitstreamInfoPOD &bi = *ci.bits_info;
+ chipconfig_t config;
+ config.resize(ci.height);
+ for (int y = 0; y < ci.height; y++) {
+ config.at(y).resize(ci.width);
+ for (int x = 0; x < ci.width; x++) {
+ TileType tile = tile_at(ctx, x, y);
+ int rows = bi.tiles_nonrouting[tile].rows;
+ int cols = bi.tiles_nonrouting[tile].cols;
+ config.at(y).at(x).resize(rows, std::vector<int8_t>(cols));
+ }
+ }
+ read_config(ctx, in, config);
+ return true;
+ } catch (log_execution_error_exception) {
+ return false;
+ }
+}
NEXTPNR_NAMESPACE_END
diff --git a/ice40/bitstream.h b/ice40/bitstream.h
index 2b6cda1d..41a2ae68 100644
--- a/ice40/bitstream.h
+++ b/ice40/bitstream.h
@@ -27,6 +27,7 @@
NEXTPNR_NAMESPACE_BEGIN
void write_asc(const Context *ctx, std::ostream &out);
+bool read_asc(Context *ctx, std::istream &in);
NEXTPNR_NAMESPACE_END
diff --git a/ice40/blinky.ys b/ice40/blinky.ys
index bad0a8b4..a5dd2c85 100644
--- a/ice40/blinky.ys
+++ b/ice40/blinky.ys
@@ -1,3 +1,3 @@
read_verilog blinky.v
-synth_ice40 -top blinky -nocarry
+synth_ice40 -top blinky
write_json blinky.json
diff --git a/ice40/cells.cc b/ice40/cells.cc
index 1ba40970..71a65d44 100644
--- a/ice40/cells.cc
+++ b/ice40/cells.cc
@@ -142,6 +142,71 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri
for (int i = 0; i < 4; i++) {
add_port(ctx, new_cell.get(), "MASKWREN_" + std::to_string(i), PORT_IN);
}
+ } else if (type == ctx->id("ICESTORM_DSP")) {
+ new_cell->params[ctx->id("NEG_TRIGGER")] = "0";
+
+ new_cell->params[ctx->id("C_REG")] = "0";
+ new_cell->params[ctx->id("A_REG")] = "0";
+ new_cell->params[ctx->id("B_REG")] = "0";
+ new_cell->params[ctx->id("D_REG")] = "0";
+ new_cell->params[ctx->id("TOP_8x8_MULT_REG")] = "0";
+ new_cell->params[ctx->id("BOT_8x8_MULT_REG")] = "0";
+ new_cell->params[ctx->id("PIPELINE_16x16_MULT_REG1")] = "0";
+ new_cell->params[ctx->id("PIPELINE_16x16_MULT_REG2")] = "0";
+
+ new_cell->params[ctx->id("TOPOUTPUT_SELECT")] = "0";
+ new_cell->params[ctx->id("TOPADDSUB_LOWERINPUT")] = "0";
+ new_cell->params[ctx->id("TOPADDSUB_UPPERINPUT")] = "0";
+ new_cell->params[ctx->id("TOPADDSUB_CARRYSELECT")] = "0";
+
+ new_cell->params[ctx->id("BOTOUTPUT_SELECT")] = "0";
+ new_cell->params[ctx->id("BOTADDSUB_LOWERINPUT")] = "0";
+ new_cell->params[ctx->id("BOTADDSUB_UPPERINPUT")] = "0";
+ new_cell->params[ctx->id("BOTADDSUB_CARRYSELECT")] = "0";
+
+ new_cell->params[ctx->id("MODE_8x8")] = "0";
+ new_cell->params[ctx->id("A_SIGNED")] = "0";
+ new_cell->params[ctx->id("B_SIGNED")] = "0";
+
+ add_port(ctx, new_cell.get(), "CLK", PORT_IN);
+ add_port(ctx, new_cell.get(), "CE", PORT_IN);
+ for (int i = 0; i < 16; i++) {
+ add_port(ctx, new_cell.get(), "C_" + std::to_string(i), PORT_IN);
+ add_port(ctx, new_cell.get(), "A_" + std::to_string(i), PORT_IN);
+ add_port(ctx, new_cell.get(), "B_" + std::to_string(i), PORT_IN);
+ add_port(ctx, new_cell.get(), "D_" + std::to_string(i), PORT_IN);
+ }
+ add_port(ctx, new_cell.get(), "AHOLD", PORT_IN);
+ add_port(ctx, new_cell.get(), "BHOLD", PORT_IN);
+ add_port(ctx, new_cell.get(), "CHOLD", PORT_IN);
+ add_port(ctx, new_cell.get(), "DHOLD", PORT_IN);
+
+ add_port(ctx, new_cell.get(), "IRSTTOP", PORT_IN);
+ add_port(ctx, new_cell.get(), "IRSTBOT", PORT_IN);
+ add_port(ctx, new_cell.get(), "ORSTTOP", PORT_IN);
+ add_port(ctx, new_cell.get(), "ORSTBOT", PORT_IN);
+
+ add_port(ctx, new_cell.get(), "OLOADTOP", PORT_IN);
+ add_port(ctx, new_cell.get(), "OLOADBOT", PORT_IN);
+
+ add_port(ctx, new_cell.get(), "ADDSUBTOP", PORT_IN);
+ add_port(ctx, new_cell.get(), "ADDSUBBOT", PORT_IN);
+
+ add_port(ctx, new_cell.get(), "OHOLDTOP", PORT_IN);
+ add_port(ctx, new_cell.get(), "OHOLDBOT", PORT_IN);
+
+ add_port(ctx, new_cell.get(), "CI", PORT_IN);
+ add_port(ctx, new_cell.get(), "ACCUMCI", PORT_IN);
+ add_port(ctx, new_cell.get(), "SIGNEXTIN", PORT_IN);
+
+ for (int i = 0; i < 32; i++) {
+ add_port(ctx, new_cell.get(), "O_" + std::to_string(i), PORT_OUT);
+ }
+
+ add_port(ctx, new_cell.get(), "CO", PORT_OUT);
+ add_port(ctx, new_cell.get(), "ACCUMCO", PORT_OUT);
+ add_port(ctx, new_cell.get(), "SIGNEXTOUT", PORT_OUT);
+
} else {
log_error("unable to create iCE40 cell of type %s", type.c_str(ctx));
}
@@ -256,7 +321,10 @@ bool is_clock_port(const BaseCtx *ctx, const PortRef &port)
if (port.cell->type == ctx->id("ICESTORM_LC"))
return port.port == ctx->id("CLK");
if (is_ram(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_RAM"))
- return port.port == ctx->id("RCLK") || port.port == ctx->id("WCLK");
+ return port.port == ctx->id("RCLK") || port.port == ctx->id("WCLK") || port.port == ctx->id("RCLKN") ||
+ port.port == ctx->id("WCLKN");
+ if (is_sb_mac16(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_DSP"))
+ return port.port == ctx->id("CLK");
return false;
}
@@ -268,6 +336,9 @@ bool is_reset_port(const BaseCtx *ctx, const PortRef &port)
return port.port == ctx->id("R") || port.port == ctx->id("S");
if (port.cell->type == ctx->id("ICESTORM_LC"))
return port.port == ctx->id("SR");
+ if (is_sb_mac16(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_DSP"))
+ return port.port == ctx->id("IRSTTOP") || port.port == ctx->id("IRSTBOT") || port.port == ctx->id("ORSTTOP") ||
+ port.port == ctx->id("ORSTBOT");
return false;
}
@@ -279,6 +350,9 @@ bool is_enable_port(const BaseCtx *ctx, const PortRef &port)
return port.port == ctx->id("E");
if (port.cell->type == ctx->id("ICESTORM_LC"))
return port.port == ctx->id("CEN");
+ // FIXME
+ // if (is_sb_mac16(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_DSP"))
+ // return port.port == ctx->id("CE");
return false;
}
diff --git a/ice40/cells.h b/ice40/cells.h
index 9f99835d..2f9c77e8 100644
--- a/ice40/cells.h
+++ b/ice40/cells.h
@@ -69,6 +69,8 @@ inline bool is_sb_hfosc(const BaseCtx *ctx, const CellInfo *cell) { return cell-
inline bool is_sb_spram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_SPRAM256KA"); }
+inline bool is_sb_mac16(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_MAC16"); }
+
// Convert a SB_LUT primitive to (part of) an ICESTORM_LC, swapping ports
// as needed. Set no_dff if a DFF is not being used, so that the output
// can be reconnected
diff --git a/ice40/chipdb.py b/ice40/chipdb.py
index 698cd173..329fef56 100644
--- a/ice40/chipdb.py
+++ b/ice40/chipdb.py
@@ -38,7 +38,7 @@ switches = list()
ierens = list()
extra_cells = dict()
-
+extra_cell_config = dict()
packages = list()
wire_uphill_belport = dict()
@@ -159,7 +159,7 @@ def wire_type(name):
name = name.split('/')[-1]
wt = None
- if name.startswith("glb_netwk_"):
+ if name.startswith("glb_netwk_") or name.startswith("padin_"):
wt = "GLOBAL"
elif name.startswith("D_IN_") or name.startswith("D_OUT_"):
wt = "LOCAL"
@@ -432,6 +432,19 @@ with open(args.filename, "r") as f:
extra_cells[mode[1]].append((line[0], (int(line[1]), int(line[2]), line[3])))
continue
+def add_wire(x, y, name):
+ global num_wires
+ wire_idx = num_wires
+ num_wires = num_wires + 1
+ wname = (x, y, name)
+ wire_names[wname] = wire_idx
+ wire_names_r[wire_idx] = wname
+ wire_segments[wire_idx] = dict()
+
+# Add virtual padin wires
+for i in range(8):
+ add_wire(0, 0, "padin_%d" % i)
+
def add_bel_input(bel, wire, port):
if wire not in wire_downhill_belports:
wire_downhill_belports[wire] = set()
@@ -567,6 +580,7 @@ def is_ec_output(ec_entry):
def add_bel_ec(ec):
ectype, x, y, z = ec
bel = len(bel_name)
+ extra_cell_config[bel] = []
bel_name.append("X%d/Y%d/%s_%d" % (x, y, ectype.lower(), z))
bel_type.append(ectype)
bel_pos.append((x, y, z))
@@ -578,8 +592,7 @@ def add_bel_ec(ec):
else:
add_bel_input(bel, wire_names[entry[1]], entry[0])
else:
- # Configuration bit, need to create a structure for these
- pass
+ extra_cell_config[bel].append(entry)
for tile_xy, tile_type in sorted(tiles.items()):
if tile_type == "logic":
@@ -1175,6 +1188,23 @@ bba.l("tile_grid_%s" % dev_name, "TileType")
for t in tilegrid:
bba.u32(tiletypes[t], "tiletype")
+for bel_idx, entries in sorted(extra_cell_config.items()):
+ if len(entries) > 0:
+ bba.l("bel%d_config_entries" % bel_idx, "BelConfigEntryPOD")
+ for entry in entries:
+ bba.s(entry[0], "entry_name")
+ bba.s(entry[1][2], "cbit_name")
+ bba.u8(entry[1][0], "x")
+ bba.u8(entry[1][1], "y")
+ bba.u16(0, "padding")
+
+if len(extra_cell_config) > 0:
+ bba.l("bel_config_%s" % dev_name, "BelConfigPOD")
+ for bel_idx, entries in sorted(extra_cell_config.items()):
+ bba.u32(bel_idx, "bel_index")
+ bba.u32(len(entries), "num_entries")
+ bba.r("bel%d_config_entries" % bel_idx if len(entries) > 0 else None, "entries")
+
bba.l("package_info_%s" % dev_name, "PackageInfoPOD")
for info in packageinfo:
bba.s(info[0], "name")
@@ -1188,12 +1218,14 @@ bba.u32(len(bel_name), "num_bels")
bba.u32(num_wires, "num_wires")
bba.u32(len(pipinfo), "num_pips")
bba.u32(len(switchinfo), "num_switches")
+bba.u32(len(extra_cell_config), "num_belcfgs")
bba.u32(len(packageinfo), "num_packages")
bba.r("bel_data_%s" % dev_name, "bel_data")
bba.r("wire_data_%s" % dev_name, "wire_data")
bba.r("pip_data_%s" % dev_name, "pip_data")
bba.r("tile_grid_%s" % dev_name, "tile_grid")
bba.r("bits_info_%s" % dev_name, "bits_info")
+bba.r("bel_config_%s" % dev_name if len(extra_cell_config) > 0 else None, "bel_config")
bba.r("package_info_%s" % dev_name, "packages_data")
bba.finalize()
diff --git a/ice40/gfx.cc b/ice40/gfx.cc
index aa2fc9ce..f6ed789f 100644
--- a/ice40/gfx.cc
+++ b/ice40/gfx.cc
@@ -640,10 +640,8 @@ static bool getWireXY_local(GfxTileWireId id, float &x, float &y)
return false;
}
-void pipGfx(std::vector<GraphicElement> &g, int x, int y,
- float x1, float y1, float x2, float y2,
- float swx1, float swy1, float swx2, float swy2,
- GraphicElement::style_t style)
+void pipGfx(std::vector<GraphicElement> &g, int x, int y, float x1, float y1, float x2, float y2, float swx1,
+ float swy1, float swx2, float swy2, GraphicElement::style_t style)
{
float tx = 0.5 * (x1 + x2);
float ty = 0.5 * (y1 + y2);
@@ -693,7 +691,8 @@ edge_pip:
g.push_back(el);
}
-void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src, GfxTileWireId dst, GraphicElement::style_t style)
+void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src, GfxTileWireId dst,
+ GraphicElement::style_t style)
{
float x1, y1, x2, y2;
diff --git a/ice40/gfx.h b/ice40/gfx.h
index a1cbd65b..8a55407d 100644
--- a/ice40/gfx.h
+++ b/ice40/gfx.h
@@ -468,7 +468,8 @@ enum GfxTileWireId
};
void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style);
-void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src, GfxTileWireId dst, GraphicElement::style_t style);
+void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src, GfxTileWireId dst,
+ GraphicElement::style_t style);
NEXTPNR_NAMESPACE_END
diff --git a/ice40/main.cc b/ice40/main.cc
index e77bdd34..652196a1 100644
--- a/ice40/main.cc
+++ b/ice40/main.cc
@@ -66,6 +66,12 @@ void svg_dump_decal(const Context *ctx, const DecalXY &decal)
}
}
+void conflicting_options(const boost::program_options::variables_map &vm, const char *opt1, const char *opt2)
+{
+ if (vm.count(opt1) && !vm[opt1].defaulted() && vm.count(opt2) && !vm[opt2].defaulted())
+ log_error((std::string("Conflicting options '") + opt1 + "' and '" + opt2 + "'.").c_str());
+}
+
int main(int argc, char *argv[])
{
try {
@@ -95,6 +101,7 @@ int main(int argc, char *argv[])
options.add_options()("json", po::value<std::string>(), "JSON design file to ingest");
options.add_options()("pcf", po::value<std::string>(), "PCF constraints file to ingest");
options.add_options()("asc", po::value<std::string>(), "asc bitstream file to write");
+ options.add_options()("read", po::value<std::string>(), "asc bitstream file to read");
options.add_options()("seed", po::value<int>(), "seed value for random number generator");
options.add_options()("version,V", "show version");
options.add_options()("tmfuzz", "run path delay estimate fuzzer");
@@ -121,13 +128,17 @@ int main(int argc, char *argv[])
po::store(parsed, vm);
po::notify(vm);
- }
-
- catch (std::exception &e) {
+ } catch (std::exception &e) {
std::cout << e.what() << "\n";
return 1;
}
+ conflicting_options(vm, "read", "json");
+#ifndef ICE40_HX1K_ONLY
+ if ((vm.count("lp384") + vm.count("lp1k") + vm.count("lp8k") + vm.count("hx1k") + vm.count("hx8k") +
+ vm.count("up5k")) > 1)
+ log_error("Only one device type can be set\n");
+#endif
if (vm.count("help") || argc == 1) {
help:
std::cout << boost::filesystem::basename(argv[0])
@@ -353,6 +364,13 @@ int main(int argc, char *argv[])
if (vm.count("no-tmdriv"))
ctx->timing_driven = false;
+ if (vm.count("read")) {
+ std::string filename = vm["read"].as<std::string>();
+ std::ifstream f(filename);
+ if (!read_asc(ctx.get(), f))
+ log_error("Loading ASC failed.\n");
+ }
+
#ifndef NO_GUI
if (vm.count("gui")) {
Application a(argc, argv);
diff --git a/ice40/pack.cc b/ice40/pack.cc
index 76a52be0..7e2e389c 100644
--- a/ice40/pack.cc
+++ b/ice40/pack.cc
@@ -277,6 +277,10 @@ static void pack_ram(Context *ctx)
if (bpos != std::string::npos) {
newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2);
}
+ if (pi.name == ctx->id("RCLKN"))
+ newname = "RCLK";
+ else if (pi.name == ctx->id("WCLKN"))
+ newname = "WCLK";
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
}
new_cells.push_back(std::move(packed));
@@ -303,6 +307,10 @@ static void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constne
if ((is_lut(ctx, uc) || is_lc(ctx, uc) || is_carry(ctx, uc)) && (user.port.str(ctx).at(0) == 'I') &&
!constval) {
uc->ports[user.port].net = nullptr;
+ } else if ((is_sb_mac16(ctx, uc) || uc->type == ctx->id("ICESTORM_DSP")) &&
+ (user.port != ctx->id("CLK") &&
+ ((constval && user.port == ctx->id("CE")) || (!constval && user.port != ctx->id("CE"))))) {
+ uc->ports[user.port].net = nullptr;
} else {
uc->ports[user.port].net = constnet;
constnet->users.push_back(user);
@@ -564,6 +572,25 @@ static void pack_special(Context *ctx)
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
}
new_cells.push_back(std::move(packed));
+ } else if (is_sb_mac16(ctx, ci)) {
+ std::unique_ptr<CellInfo> packed =
+ create_ice_cell(ctx, ctx->id("ICESTORM_DSP"), ci->name.str(ctx) + "_DSP");
+ packed_cells.insert(ci->name);
+ for (auto attr : ci->attrs)
+ packed->attrs[attr.first] = attr.second;
+ for (auto param : ci->params)
+ packed->params[param.first] = param.second;
+
+ for (auto port : ci->ports) {
+ PortInfo &pi = port.second;
+ std::string newname = pi.name.str(ctx);
+ size_t bpos = newname.find('[');
+ if (bpos != std::string::npos) {
+ newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2);
+ }
+ replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
+ }
+ new_cells.push_back(std::move(packed));
}
}
@@ -589,6 +616,7 @@ bool Arch::pack()
pack_carries(ctx);
pack_ram(ctx);
pack_special(ctx);
+ ctx->assignArchInfo();
log_info("Checksum: 0x%08x\n", ctx->checksum());
return true;
} catch (log_execution_error_exception) {
diff --git a/ice40/picorv32.sh b/ice40/picorv32.sh
index 2c67f641..87426cde 100755
--- a/ice40/picorv32.sh
+++ b/ice40/picorv32.sh
@@ -2,5 +2,5 @@
set -ex
rm -f picorv32.v
wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v
-yosys -p 'synth_ice40 -nocarry -json picorv32.json -top top' picorv32.v picorv32_top.v
+yosys -p 'synth_ice40 -json picorv32.json -top top' picorv32.v picorv32_top.v
../nextpnr-ice40 --hx8k --asc picorv32.asc --json picorv32.json
diff --git a/ice40/picorv32_arachne.sh b/ice40/picorv32_arachne.sh
deleted file mode 100755
index b3960fdc..00000000
--- a/ice40/picorv32_arachne.sh
+++ /dev/null
@@ -1,9 +0,0 @@
-#!/bin/bash
-set -ex
-rm -f picorv32.v
-wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v
-yosys -p 'synth_ice40 -nocarry -blif picorv32.blif -top top' picorv32.v picorv32_top.v
-arachne-pnr -d 8k --post-place-blif picorv32_place.blif picorv32.blif -o picorv32_arachne_all.asc
-yosys -p "read_blif -wideports picorv32_place.blif; read_verilog -lib +/ice40/cells_sim.v; write_json picorv32_place.json"
-./transform_arachne_loc.py picorv32_place.json > picorv32_place_nx.json
-../nextpnr-ice40 --hx8k --asc picorv32_ar_placed.asc --json picorv32_place_nx.json --force
diff --git a/ice40/place_legaliser.cc b/ice40/place_legaliser.cc
index 5fffb4fb..2aefb839 100644
--- a/ice40/place_legaliser.cc
+++ b/ice40/place_legaliser.cc
@@ -127,6 +127,7 @@ class PlacementLegaliser
legalise_others();
legalise_logic_tiles();
bool replaced_cells = replace_cells();
+ ctx->assignArchInfo();
return legalised_carries && replaced_cells;
}
@@ -371,6 +372,7 @@ class PlacementLegaliser
NPNR_ASSERT(ctx->nets.find(co_i3_name) == ctx->nets.end());
ctx->nets[co_i3_name] = std::move(co_i3_net);
IdString name = lc->name;
+ ctx->assignCellInfo(lc.get());
ctx->cells[lc->name] = std::move(lc);
createdCells.insert(name);
return ctx->cells[name].get();
@@ -415,6 +417,7 @@ class PlacementLegaliser
ctx->nets[out_net_name] = std::move(out_net);
IdString name = lc->name;
+ ctx->assignCellInfo(lc.get());
ctx->cells[lc->name] = std::move(lc);
createdCells.insert(name);
return ctx->cells[name].get();
diff --git a/ice40/transform_arachne_loc.py b/ice40/transform_arachne_loc.py
deleted file mode 100755
index 14792845..00000000
--- a/ice40/transform_arachne_loc.py
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/usr/bin/env python3
-import json
-import sys
-import re
-
-with open(sys.argv[1]) as f:
- data = json.load(f)
-
-for mod, moddata in data["modules"].items():
- if "cells" in moddata:
- for cell, celldata in moddata["cells"].items():
- pos = re.split('[,/]', celldata["attributes"]["loc"])
- pos = [int(_) for _ in pos]
- if celldata["type"] == "ICESTORM_LC":
- celldata["attributes"]["BEL"] = "X%d/Y%d/lc%d" % (pos[0], pos[1], pos[2])
- elif celldata["type"] == "SB_IO":
- celldata["attributes"]["BEL"] = "X%d/Y%d/io%d" % (pos[0], pos[1], pos[2])
- elif "RAM" in celldata["type"]:
- celldata["attributes"]["BEL"] = "X%d/Y%d/ram" % (pos[0], pos[1])
- elif celldata["type"] == "SB_GB":
- celldata["attributes"]["BEL"] = "X%d/Y%d/gb" % (pos[0], pos[1])
- else:
- assert False
-print(json.dumps(data, sort_keys=True, indent=4)) \ No newline at end of file