Merge branch 'master' of gitlab.com:SymbioticEDA/nextpnr into q3k/lock-2-electric-boogaloo
This commit is contained in:
commit
55d5f8f248
@ -75,21 +75,22 @@ class assertion_failure : public std::runtime_error
|
|||||||
int line;
|
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
|
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(cond) ((void)((cond) || (assert_fail_impl(#cond, #cond, __FILE__, __LINE__))))
|
||||||
#define NPNR_ASSERT_MSG(cond, msg) except_assert_impl((cond), msg, #cond, __FILE__, __LINE__)
|
#define NPNR_ASSERT_MSG(cond, msg) ((void)((cond) || (assert_fail_impl(msg, #cond, __FILE__, __LINE__))))
|
||||||
#define NPNR_ASSERT_FALSE(msg) assert_false_impl(msg, __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 IdStringDB;
|
struct IdStringDB;
|
||||||
struct Context;
|
struct Context;
|
||||||
@ -203,9 +204,11 @@ struct PipMap
|
|||||||
PlaceStrength strength = STRENGTH_NONE;
|
PlaceStrength strength = STRENGTH_NONE;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NetInfo
|
struct NetInfo : ArchNetInfo
|
||||||
{
|
{
|
||||||
IdString name;
|
IdString name;
|
||||||
|
int32_t udata;
|
||||||
|
|
||||||
PortRef driver;
|
PortRef driver;
|
||||||
std::vector<PortRef> users;
|
std::vector<PortRef> users;
|
||||||
std::unordered_map<IdString, std::string> attrs;
|
std::unordered_map<IdString, std::string> attrs;
|
||||||
@ -228,9 +231,11 @@ struct PortInfo
|
|||||||
PortType type;
|
PortType type;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CellInfo
|
struct CellInfo : ArchCellInfo
|
||||||
{
|
{
|
||||||
IdString name, type;
|
IdString name, type;
|
||||||
|
int32_t udata;
|
||||||
|
|
||||||
std::unordered_map<IdString, PortInfo> ports;
|
std::unordered_map<IdString, PortInfo> ports;
|
||||||
std::unordered_map<IdString, std::string> attrs, params;
|
std::unordered_map<IdString, std::string> attrs, params;
|
||||||
|
|
||||||
|
33
ecp5/arch.cc
33
ecp5/arch.cc
@ -118,6 +118,16 @@ Arch::Arch(ArchArgs args) : args(args)
|
|||||||
log_error("Unsupported ECP5 chip type.\n");
|
log_error("Unsupported ECP5 chip type.\n");
|
||||||
}
|
}
|
||||||
#endif
|
#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_trellis_slice = id("TRELLIS_SLICE");
|
||||||
id_clk = id("CLK");
|
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
|
void Arch::estimatePosition(BelId bel, int &x, int &y, bool &gb) const
|
||||||
|
24
ecp5/arch.h
24
ecp5/arch.h
@ -96,13 +96,36 @@ NPNR_PACKED_STRUCT(struct LocationTypePOD {
|
|||||||
RelPtr<PipInfoPOD> pip_data;
|
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 {
|
NPNR_PACKED_STRUCT(struct ChipInfoPOD {
|
||||||
int32_t width, height;
|
int32_t width, height;
|
||||||
int32_t num_tiles;
|
int32_t num_tiles;
|
||||||
int32_t num_location_types;
|
int32_t num_location_types;
|
||||||
|
int32_t num_packages, num_pios;
|
||||||
RelPtr<LocationTypePOD> locations;
|
RelPtr<LocationTypePOD> locations;
|
||||||
RelPtr<int32_t> location_type;
|
RelPtr<int32_t> location_type;
|
||||||
RelPtr<RelPtr<char>> tiletype_names;
|
RelPtr<RelPtr<char>> tiletype_names;
|
||||||
|
RelPtr<PackageInfoPOD> package_info;
|
||||||
|
RelPtr<PIOInfoPOD> pio_info;
|
||||||
});
|
});
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
@ -340,6 +363,7 @@ struct ArchArgs
|
|||||||
struct Arch : BaseCtx
|
struct Arch : BaseCtx
|
||||||
{
|
{
|
||||||
const ChipInfoPOD *chip_info;
|
const ChipInfoPOD *chip_info;
|
||||||
|
const PackageInfoPOD *package_info;
|
||||||
|
|
||||||
mutable std::unordered_map<IdString, BelId> bel_by_name;
|
mutable std::unordered_map<IdString, BelId> bel_by_name;
|
||||||
mutable std::unordered_map<IdString, WireId> wire_by_name;
|
mutable std::unordered_map<IdString, WireId> wire_by_name;
|
||||||
|
@ -129,6 +129,13 @@ struct DecalId
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ArchNetInfo
|
||||||
|
{
|
||||||
|
};
|
||||||
|
struct ArchCellInfo
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
|
@ -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(), "I", PORT_IN);
|
||||||
add_port(ctx, new_cell.get(), "T", PORT_IN);
|
add_port(ctx, new_cell.get(), "T", PORT_IN);
|
||||||
add_port(ctx, new_cell.get(), "O", PORT_OUT);
|
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 {
|
} else {
|
||||||
log_error("unable to create ECP5 cell of type %s", type.c_str(ctx));
|
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)
|
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("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("B"), lc, ctx->id("B" + std::to_string(index)));
|
||||||
replace_port(lut, ctx->id("C"), lc, ctx->id("C" + std::to_string(index)));
|
replace_port(lut, ctx->id("C"), lc, ctx->id("C" + std::to_string(index)));
|
||||||
|
@ -68,6 +68,8 @@ int main(int argc, char *argv[])
|
|||||||
options.add_options()("45k", "set device type to LFE5U-45F");
|
options.add_options()("45k", "set device type to LFE5U-45F");
|
||||||
options.add_options()("85k", "set device type to LFE5U-85F");
|
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()("json", po::value<std::string>(), "JSON design file to ingest");
|
||||||
options.add_options()("seed", po::value<int>(), "seed value for random number generator");
|
options.add_options()("seed", po::value<int>(), "seed value for random number generator");
|
||||||
|
|
||||||
@ -123,7 +125,9 @@ int main(int argc, char *argv[])
|
|||||||
args.type = ArchArgs::LFE5U_45F;
|
args.type = ArchArgs::LFE5U_45F;
|
||||||
if (vm.count("85k"))
|
if (vm.count("85k"))
|
||||||
args.type = ArchArgs::LFE5U_85F;
|
args.type = ArchArgs::LFE5U_85F;
|
||||||
|
if (vm.count("package"))
|
||||||
|
args.package = vm["package"].as<std::string>();
|
||||||
|
else
|
||||||
args.package = "CABGA381";
|
args.package = "CABGA381";
|
||||||
args.speed = 6;
|
args.speed = 6;
|
||||||
std::unique_ptr<Context> ctx = std::unique_ptr<Context>(new Context(args));
|
std::unique_ptr<Context> ctx = std::unique_ptr<Context>(new Context(args));
|
||||||
|
141
ecp5/pack.cc
141
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
|
// Return whether two FFs can be packed together in the same slice
|
||||||
bool can_pack_ffs(CellInfo *ff0, CellInfo *ff1)
|
bool can_pack_ffs(CellInfo *ff0, CellInfo *ff1)
|
||||||
{
|
{
|
||||||
@ -88,11 +97,11 @@ class Ecp5Packer
|
|||||||
if (str_or_default(ff0->params, ctx->id("CLKMUX"), "CLK") !=
|
if (str_or_default(ff0->params, ctx->id("CLKMUX"), "CLK") !=
|
||||||
str_or_default(ff1->params, ctx->id("CLKMUX"), "CLK"))
|
str_or_default(ff1->params, ctx->id("CLKMUX"), "CLK"))
|
||||||
return false;
|
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;
|
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;
|
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 false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -223,8 +232,23 @@ class Ecp5Packer
|
|||||||
} else {
|
} else {
|
||||||
log_error("TRELLIS_IO required on all top level IOs...\n");
|
log_error("TRELLIS_IO required on all top level IOs...\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
packed_cells.insert(ci->name);
|
packed_cells.insert(ci->name);
|
||||||
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(trio->attrs, trio->attrs.begin()));
|
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();
|
flush_cells();
|
||||||
@ -275,6 +299,8 @@ class Ecp5Packer
|
|||||||
ff_to_slice(ctx, ff, packed.get(), 0, true);
|
ff_to_slice(ctx, ff, packed.get(), 0, true);
|
||||||
packed_cells.insert(ff->name);
|
packed_cells.insert(ff->name);
|
||||||
sliceUsage[packed->name].ff0_used = true;
|
sliceUsage[packed->name].ff0_used = true;
|
||||||
|
lutffPairs.erase(ci->name);
|
||||||
|
fflutPairs.erase(ff->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_cells.push_back(std::move(packed));
|
new_cells.push_back(std::move(packed));
|
||||||
@ -304,10 +330,14 @@ class Ecp5Packer
|
|||||||
if (ff0 != lutffPairs.end()) {
|
if (ff0 != lutffPairs.end()) {
|
||||||
ff_to_slice(ctx, ctx->cells.at(ff0->second).get(), slice.get(), 0, true);
|
ff_to_slice(ctx, ctx->cells.at(ff0->second).get(), slice.get(), 0, true);
|
||||||
packed_cells.insert(ff0->second);
|
packed_cells.insert(ff0->second);
|
||||||
|
lutffPairs.erase(lut0->name);
|
||||||
|
fflutPairs.erase(ff0->second);
|
||||||
}
|
}
|
||||||
if (ff1 != lutffPairs.end()) {
|
if (ff1 != lutffPairs.end()) {
|
||||||
ff_to_slice(ctx, ctx->cells.at(ff1->second).get(), slice.get(), 1, true);
|
ff_to_slice(ctx, ctx->cells.at(ff1->second).get(), slice.get(), 1, true);
|
||||||
packed_cells.insert(ff1->second);
|
packed_cells.insert(ff1->second);
|
||||||
|
lutffPairs.erase(lut1->name);
|
||||||
|
fflutPairs.erase(ff1->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_cells.push_back(std::move(slice));
|
new_cells.push_back(std::move(slice));
|
||||||
@ -333,6 +363,8 @@ class Ecp5Packer
|
|||||||
if (ff != lutffPairs.end()) {
|
if (ff != lutffPairs.end()) {
|
||||||
ff_to_slice(ctx, ctx->cells.at(ff->second).get(), slice.get(), 0, true);
|
ff_to_slice(ctx, ctx->cells.at(ff->second).get(), slice.get(), 0, true);
|
||||||
packed_cells.insert(ff->second);
|
packed_cells.insert(ff->second);
|
||||||
|
lutffPairs.erase(ci->name);
|
||||||
|
fflutPairs.erase(ff->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_cells.push_back(std::move(slice));
|
new_cells.push_back(std::move(slice));
|
||||||
@ -359,10 +391,113 @@ class Ecp5Packer
|
|||||||
flush_cells();
|
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:
|
public:
|
||||||
void pack()
|
void pack()
|
||||||
{
|
{
|
||||||
pack_io();
|
pack_io();
|
||||||
|
pack_constants();
|
||||||
find_lutff_pairs();
|
find_lutff_pairs();
|
||||||
pack_lut5s();
|
pack_lut5s();
|
||||||
pair_luts();
|
pair_luts();
|
||||||
|
@ -1,36 +1,36 @@
|
|||||||
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 clk;
|
||||||
wire [7:0] led;
|
wire [7:0] led;
|
||||||
wire btn;
|
wire btn;
|
||||||
wire gpio0;
|
wire gpio0;
|
||||||
|
|
||||||
(* BEL="X0/Y35/PIOA" *) (* IO_TYPE="LVCMOS33" *)
|
(* LOC="G2" *) (* IO_TYPE="LVCMOS33" *)
|
||||||
TRELLIS_IO #(.DIR("INPUT")) clk_buf (.B(clk_pin), .O(clk));
|
TRELLIS_IO #(.DIR("INPUT")) clk_buf (.B(clk_pin), .O(clk));
|
||||||
|
|
||||||
(* BEL="X4/Y71/PIOA" *) (* IO_TYPE="LVCMOS33" *)
|
(* LOC="R1" *) (* IO_TYPE="LVCMOS33" *)
|
||||||
TRELLIS_IO #(.DIR("INPUT")) btn_buf (.B(btn_pin), .O(btn));
|
TRELLIS_IO #(.DIR("INPUT")) btn_buf (.B(btn_pin), .O(btn));
|
||||||
|
|
||||||
(* BEL="X0/Y23/PIOC" *) (* IO_TYPE="LVCMOS33" *)
|
(* LOC="B2" *) (* IO_TYPE="LVCMOS33" *)
|
||||||
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_0 (.B(led_pin[0]), .I(led[0]));
|
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_0 (.B(led_pin[0]), .I(led[0]));
|
||||||
(* BEL="X0/Y23/PIOD" *) (* IO_TYPE="LVCMOS33" *)
|
(* LOC="C2" *) (* IO_TYPE="LVCMOS33" *)
|
||||||
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_1 (.B(led_pin[1]), .I(led[1]));
|
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_1 (.B(led_pin[1]), .I(led[1]));
|
||||||
(* BEL="X0/Y26/PIOA" *) (* IO_TYPE="LVCMOS33" *)
|
(* LOC="C1" *) (* IO_TYPE="LVCMOS33" *)
|
||||||
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_2 (.B(led_pin[2]), .I(led[2]));
|
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_2 (.B(led_pin[2]), .I(led[2]));
|
||||||
(* BEL="X0/Y26/PIOC" *) (* IO_TYPE="LVCMOS33" *)
|
(* LOC="D2" *) (* IO_TYPE="LVCMOS33" *)
|
||||||
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_3 (.B(led_pin[3]), .I(led[3]));
|
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_3 (.B(led_pin[3]), .I(led[3]));
|
||||||
|
|
||||||
(* BEL="X0/Y26/PIOB" *) (* IO_TYPE="LVCMOS33" *)
|
(* LOC="D1" *) (* IO_TYPE="LVCMOS33" *)
|
||||||
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_4 (.B(led_pin[4]), .I(led[4]));
|
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_4 (.B(led_pin[4]), .I(led[4]));
|
||||||
(* BEL="X0/Y32/PIOD" *) (* IO_TYPE="LVCMOS33" *)
|
(* LOC="E2" *) (* IO_TYPE="LVCMOS33" *)
|
||||||
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_5 (.B(led_pin[5]), .I(led[5]));
|
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_5 (.B(led_pin[5]), .I(led[5]));
|
||||||
(* BEL="X0/Y26/PIOD" *) (* IO_TYPE="LVCMOS33" *)
|
(* LOC="E1" *) (* IO_TYPE="LVCMOS33" *)
|
||||||
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_6 (.B(led_pin[6]), .I(led[6]));
|
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_6 (.B(led_pin[6]), .I(led[6]));
|
||||||
(* BEL="X0/Y29/PIOD" *) (* IO_TYPE="LVCMOS33" *)
|
(* LOC="H3" *) (* IO_TYPE="LVCMOS33" *)
|
||||||
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_7 (.B(led_pin[7]), .I(led[7]));
|
TRELLIS_IO #(.DIR("OUTPUT")) led_buf_7 (.B(led_pin[7]), .I(led[7]));
|
||||||
|
|
||||||
|
|
||||||
(* BEL="X0/Y62/PIOD" *) (* IO_TYPE="LVCMOS33" *)
|
(* LOC="L2" *) (* IO_TYPE="LVCMOS33" *)
|
||||||
TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0));
|
TRELLIS_IO #(.DIR("OUTPUT")) gpio0_buf (.B(gpio0_pin), .I(gpio0));
|
||||||
|
|
||||||
localparam ctr_width = 24;
|
localparam ctr_width = 24;
|
||||||
@ -72,6 +72,6 @@ module top(input clk_pin, input btn_pin, output [3:0] led_pin, output gpio0_pin)
|
|||||||
assign led = led_reg;
|
assign led = led_reg;
|
||||||
|
|
||||||
// Tie GPIO0, keep board from rebooting
|
// Tie GPIO0, keep board from rebooting
|
||||||
TRELLIS_SLICE #(.MODE("LOGIC"), .LUT0_INITVAL(16'hFFFF)) vcc (.F0(gpio0));
|
assign gpio0 = 1'b1;
|
||||||
|
|
||||||
endmodule
|
endmodule
|
||||||
|
@ -1,9 +1,2 @@
|
|||||||
read_verilog blinky.v
|
read_verilog blinky.v
|
||||||
read_verilog -lib cells.v
|
synth_ecp5 -noccu2 -nomux -nodram -json blinky.json
|
||||||
synth -top top
|
|
||||||
abc -lut 4
|
|
||||||
techmap -map simple_map.v
|
|
||||||
splitnets
|
|
||||||
opt_clean
|
|
||||||
stat
|
|
||||||
write_json blinky.json
|
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
read_verilog blinky.v
|
|
||||||
synth_ecp5 -noccu2 -nomux -nodram -json blinky.json
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -2,6 +2,8 @@
|
|||||||
import pytrellis
|
import pytrellis
|
||||||
import database
|
import database
|
||||||
import argparse
|
import argparse
|
||||||
|
import json
|
||||||
|
from os import path
|
||||||
|
|
||||||
location_types = dict()
|
location_types = dict()
|
||||||
type_at_location = dict()
|
type_at_location = dict()
|
||||||
@ -319,6 +321,49 @@ bel_types = {
|
|||||||
"PIO": 2
|
"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_database(dev_name, ddrg, endianness):
|
||||||
def write_loc(loc, sym_name):
|
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 y in range(0, max_row+1):
|
||||||
for x in range(0, max_col+1):
|
for x in range(0, max_col+1):
|
||||||
bba.u32(loctypes.index(ddrg.typeAtLocation[pytrellis.Location(x, y)]), "loctype")
|
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>")
|
bba.l("tiletype_names", "RelPtr<char>")
|
||||||
for tt in tiletype_names:
|
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_row + 1, "height")
|
||||||
bba.u32((max_col + 1) * (max_row + 1), "num_tiles")
|
bba.u32((max_col + 1) * (max_row + 1), "num_tiles")
|
||||||
bba.u32(len(location_types), "num_location_types")
|
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("locations", "locations")
|
||||||
bba.r("location_types", "location_type")
|
bba.r("location_types", "location_type")
|
||||||
bba.r("tiletype_names", "tiletype_names")
|
bba.r("tiletype_names", "tiletype_names")
|
||||||
|
bba.r("package_data", "package_info")
|
||||||
|
bba.r("pio_info", "pio_info")
|
||||||
|
|
||||||
bba.finalize()
|
bba.finalize()
|
||||||
return bba
|
return bba
|
||||||
|
|
||||||
@ -451,6 +528,7 @@ def main():
|
|||||||
ddrg = pytrellis.make_dedup_chipdb(chip)
|
ddrg = pytrellis.make_dedup_chipdb(chip)
|
||||||
max_row = chip.get_max_row()
|
max_row = chip.get_max_row()
|
||||||
max_col = chip.get_max_col()
|
max_col = chip.get_max_col()
|
||||||
|
process_pio_db(ddrg, args.device)
|
||||||
print("{} unique location types".format(len(ddrg.locationTypes)))
|
print("{} unique location types".format(len(ddrg.locationTypes)))
|
||||||
bba = write_database(args.device, ddrg, "le")
|
bba = write_database(args.device, ddrg, "le")
|
||||||
|
|
||||||
|
@ -52,4 +52,11 @@ typedef IdString PipId;
|
|||||||
typedef IdString GroupId;
|
typedef IdString GroupId;
|
||||||
typedef IdString DecalId;
|
typedef IdString DecalId;
|
||||||
|
|
||||||
|
struct ArchNetInfo
|
||||||
|
{
|
||||||
|
};
|
||||||
|
struct ArchCellInfo
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -9,6 +9,6 @@
|
|||||||
<file>resources/resultset_previous.png</file>
|
<file>resources/resultset_previous.png</file>
|
||||||
<file>resources/resultset_next.png</file>
|
<file>resources/resultset_next.png</file>
|
||||||
<file>resources/resultset_last.png</file>
|
<file>resources/resultset_last.png</file>
|
||||||
<file>resources/splash.png</file>
|
<file>resources/cross.png</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
</RCC>
|
</RCC>
|
||||||
|
@ -29,7 +29,6 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "pythontab.h"
|
#include "pythontab.h"
|
||||||
#include "yosystab.h"
|
|
||||||
|
|
||||||
static void initBasenameResource() { Q_INIT_RESOURCE(base); }
|
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(centralTabWidget);
|
||||||
splitter_v->addWidget(tabWidget);
|
splitter_v->addWidget(tabWidget);
|
||||||
displaySplash();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseMainWindow::~BaseMainWindow() {}
|
BaseMainWindow::~BaseMainWindow() {}
|
||||||
|
|
||||||
void BaseMainWindow::closeTab(int index) { delete centralTabWidget->widget(index); }
|
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::writeInfo(std::string text) { console->info(text); }
|
||||||
|
|
||||||
void BaseMainWindow::createMenusAndBars()
|
void BaseMainWindow::createMenusAndBars()
|
||||||
@ -147,10 +129,6 @@ void BaseMainWindow::createMenusAndBars()
|
|||||||
actionExit->setStatusTip("Exit the application");
|
actionExit->setStatusTip("Exit the application");
|
||||||
connect(actionExit, SIGNAL(triggered()), this, SLOT(close()));
|
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);
|
QAction *actionAbout = new QAction("About", this);
|
||||||
|
|
||||||
menuBar = new QMenuBar();
|
menuBar = new QMenuBar();
|
||||||
@ -183,19 +161,6 @@ void BaseMainWindow::createMenusAndBars()
|
|||||||
mainToolBar->addAction(actionNew);
|
mainToolBar->addAction(actionNew);
|
||||||
mainToolBar->addAction(actionOpen);
|
mainToolBar->addAction(actionOpen);
|
||||||
mainToolBar->addAction(actionSave);
|
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
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -26,7 +26,6 @@
|
|||||||
#include <QMenu>
|
#include <QMenu>
|
||||||
#include <QMenuBar>
|
#include <QMenuBar>
|
||||||
#include <QProgressBar>
|
#include <QProgressBar>
|
||||||
#include <QSplashScreen>
|
|
||||||
#include <QStatusBar>
|
#include <QStatusBar>
|
||||||
#include <QTabWidget>
|
#include <QTabWidget>
|
||||||
#include <QToolBar>
|
#include <QToolBar>
|
||||||
@ -50,17 +49,14 @@ class BaseMainWindow : public QMainWindow
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void createMenusAndBars();
|
void createMenusAndBars();
|
||||||
void displaySplash();
|
|
||||||
|
|
||||||
protected Q_SLOTS:
|
protected Q_SLOTS:
|
||||||
void writeInfo(std::string text);
|
void writeInfo(std::string text);
|
||||||
void displaySplashMessage(std::string msg);
|
|
||||||
void closeTab(int index);
|
void closeTab(int index);
|
||||||
|
|
||||||
virtual void new_proj() = 0;
|
virtual void new_proj() = 0;
|
||||||
virtual void open_proj() = 0;
|
virtual void open_proj() = 0;
|
||||||
virtual bool save_proj() = 0;
|
virtual bool save_proj() = 0;
|
||||||
void yosys();
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void contextChanged(Context *ctx);
|
void contextChanged(Context *ctx);
|
||||||
@ -78,7 +74,6 @@ class BaseMainWindow : public QMainWindow
|
|||||||
QAction *actionNew;
|
QAction *actionNew;
|
||||||
QAction *actionOpen;
|
QAction *actionOpen;
|
||||||
QProgressBar *progressBar;
|
QProgressBar *progressBar;
|
||||||
QSplashScreen *splash;
|
|
||||||
DesignWidget *designview;
|
DesignWidget *designview;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -125,11 +125,27 @@ DesignWidget::DesignWidget(QWidget *parent) : QWidget(parent), ctx(nullptr), net
|
|||||||
updateButtons();
|
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();
|
QToolBar *toolbar = new QToolBar();
|
||||||
toolbar->addAction(actionFirst);
|
toolbar->addAction(actionFirst);
|
||||||
toolbar->addAction(actionPrev);
|
toolbar->addAction(actionPrev);
|
||||||
toolbar->addAction(actionNext);
|
toolbar->addAction(actionNext);
|
||||||
toolbar->addAction(actionLast);
|
toolbar->addAction(actionLast);
|
||||||
|
toolbar->addAction(actionClear);
|
||||||
|
|
||||||
QWidget *topWidget = new QWidget();
|
QWidget *topWidget = new QWidget();
|
||||||
QVBoxLayout *vbox1 = new QVBoxLayout();
|
QVBoxLayout *vbox1 = new QVBoxLayout();
|
||||||
@ -230,7 +246,6 @@ void DesignWidget::newContext(Context *ctx)
|
|||||||
bel_root->setText(0, "Bels");
|
bel_root->setText(0, "Bels");
|
||||||
treeWidget->insertTopLevelItem(0, bel_root);
|
treeWidget->insertTopLevelItem(0, bel_root);
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
Q_EMIT contextLoadStatus("Configuring bels...");
|
|
||||||
for (auto bel : ctx->getBels()) {
|
for (auto bel : ctx->getBels()) {
|
||||||
auto id = ctx->getBelName(bel);
|
auto id = ctx->getBelName(bel);
|
||||||
QStringList items = QString(id.c_str(ctx)).split("/");
|
QStringList items = QString(id.c_str(ctx)).split("/");
|
||||||
@ -263,7 +278,6 @@ void DesignWidget::newContext(Context *ctx)
|
|||||||
wire_root->setText(0, "Wires");
|
wire_root->setText(0, "Wires");
|
||||||
treeWidget->insertTopLevelItem(0, wire_root);
|
treeWidget->insertTopLevelItem(0, wire_root);
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
Q_EMIT contextLoadStatus("Configuring wires...");
|
|
||||||
for (auto wire : ctx->getWires()) {
|
for (auto wire : ctx->getWires()) {
|
||||||
auto id = ctx->getWireName(wire);
|
auto id = ctx->getWireName(wire);
|
||||||
QStringList items = QString(id.c_str(ctx)).split("/");
|
QStringList items = QString(id.c_str(ctx)).split("/");
|
||||||
@ -295,7 +309,6 @@ void DesignWidget::newContext(Context *ctx)
|
|||||||
pip_root->setText(0, "Pips");
|
pip_root->setText(0, "Pips");
|
||||||
treeWidget->insertTopLevelItem(0, pip_root);
|
treeWidget->insertTopLevelItem(0, pip_root);
|
||||||
if (ctx) {
|
if (ctx) {
|
||||||
Q_EMIT contextLoadStatus("Configuring pips...");
|
|
||||||
for (auto pip : ctx->getPips()) {
|
for (auto pip : ctx->getPips()) {
|
||||||
auto id = ctx->getPipName(pip);
|
auto id = ctx->getPipName(pip);
|
||||||
QStringList items = QString(id.c_str(ctx)).split("/");
|
QStringList items = QString(id.c_str(ctx)).split("/");
|
||||||
@ -331,8 +344,6 @@ void DesignWidget::newContext(Context *ctx)
|
|||||||
cells_root = new QTreeWidgetItem(treeWidget);
|
cells_root = new QTreeWidgetItem(treeWidget);
|
||||||
cells_root->setText(0, "Cells");
|
cells_root->setText(0, "Cells");
|
||||||
treeWidget->insertTopLevelItem(0, cells_root);
|
treeWidget->insertTopLevelItem(0, cells_root);
|
||||||
|
|
||||||
Q_EMIT finishContextLoad();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DesignWidget::updateTree()
|
void DesignWidget::updateTree()
|
||||||
@ -477,13 +488,12 @@ void DesignWidget::onItemSelectionChanged()
|
|||||||
addToHistory(clickItem);
|
addToHistory(clickItem);
|
||||||
|
|
||||||
clearProperties();
|
clearProperties();
|
||||||
if (type == ElementType::BEL) {
|
|
||||||
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
|
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
|
||||||
|
Q_EMIT selected(getDecals(type, c));
|
||||||
|
|
||||||
|
if (type == ElementType::BEL) {
|
||||||
BelId bel = ctx->getBelByName(c);
|
BelId bel = ctx->getBelByName(c);
|
||||||
|
|
||||||
decals.push_back(ctx->getBelDecal(bel));
|
|
||||||
Q_EMIT selected(decals);
|
|
||||||
|
|
||||||
QtProperty *topItem = addTopLevelProperty("Bel");
|
QtProperty *topItem = addTopLevelProperty("Bel");
|
||||||
|
|
||||||
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
|
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
|
||||||
@ -494,12 +504,7 @@ void DesignWidget::onItemSelectionChanged()
|
|||||||
ElementType::CELL);
|
ElementType::CELL);
|
||||||
|
|
||||||
} else if (type == ElementType::WIRE) {
|
} else if (type == ElementType::WIRE) {
|
||||||
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
|
|
||||||
WireId wire = ctx->getWireByName(c);
|
WireId wire = ctx->getWireByName(c);
|
||||||
|
|
||||||
decals.push_back(ctx->getWireDecal(wire));
|
|
||||||
Q_EMIT selected(decals);
|
|
||||||
|
|
||||||
QtProperty *topItem = addTopLevelProperty("Wire");
|
QtProperty *topItem = addTopLevelProperty("Wire");
|
||||||
|
|
||||||
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
|
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
|
||||||
@ -551,12 +556,7 @@ void DesignWidget::onItemSelectionChanged()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (type == ElementType::PIP) {
|
} else if (type == ElementType::PIP) {
|
||||||
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
|
|
||||||
PipId pip = ctx->getPipByName(c);
|
PipId pip = ctx->getPipByName(c);
|
||||||
|
|
||||||
decals.push_back(ctx->getPipDecal(pip));
|
|
||||||
Q_EMIT selected(decals);
|
|
||||||
|
|
||||||
QtProperty *topItem = addTopLevelProperty("Pip");
|
QtProperty *topItem = addTopLevelProperty("Pip");
|
||||||
|
|
||||||
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
|
addProperty(topItem, QVariant::String, "Name", c.c_str(ctx));
|
||||||
@ -576,7 +576,6 @@ void DesignWidget::onItemSelectionChanged()
|
|||||||
addProperty(delayItem, QVariant::Double, "Fall", delay.fallDelay());
|
addProperty(delayItem, QVariant::Double, "Fall", delay.fallDelay());
|
||||||
addProperty(delayItem, QVariant::Double, "Average", delay.avgDelay());
|
addProperty(delayItem, QVariant::Double, "Average", delay.avgDelay());
|
||||||
} else if (type == ElementType::NET) {
|
} else if (type == ElementType::NET) {
|
||||||
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
|
|
||||||
NetInfo *net = ctx->nets.at(c).get();
|
NetInfo *net = ctx->nets.at(c).get();
|
||||||
|
|
||||||
QtProperty *topItem = addTopLevelProperty("Net");
|
QtProperty *topItem = addTopLevelProperty("Net");
|
||||||
@ -613,7 +612,7 @@ void DesignWidget::onItemSelectionChanged()
|
|||||||
auto name = ctx->getWireName(item.first).c_str(ctx);
|
auto name = ctx->getWireName(item.first).c_str(ctx);
|
||||||
|
|
||||||
QtProperty *wireItem = addSubGroup(wiresItem, name);
|
QtProperty *wireItem = addSubGroup(wiresItem, name);
|
||||||
addProperty(wireItem, QVariant::String, "Name", name);
|
addProperty(wireItem, QVariant::String, "Wire", name, ElementType::WIRE);
|
||||||
|
|
||||||
if (item.second.pip != PipId())
|
if (item.second.pip != PipId())
|
||||||
addProperty(wireItem, QVariant::String, "Pip", ctx->getPipName(item.second.pip).c_str(ctx),
|
addProperty(wireItem, QVariant::String, "Pip", ctx->getPipName(item.second.pip).c_str(ctx),
|
||||||
@ -625,7 +624,6 @@ void DesignWidget::onItemSelectionChanged()
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if (type == ElementType::CELL) {
|
} else if (type == ElementType::CELL) {
|
||||||
IdString c = static_cast<IdStringTreeItem *>(clickItem)->getData();
|
|
||||||
CellInfo *cell = ctx->cells.at(c).get();
|
CellInfo *cell = ctx->cells.at(c).get();
|
||||||
|
|
||||||
QtProperty *topItem = addTopLevelProperty("Cell");
|
QtProperty *topItem = addTopLevelProperty("Cell");
|
||||||
@ -689,19 +687,28 @@ std::vector<DecalXY> DesignWidget::getDecals(ElementType type, IdString value)
|
|||||||
WireId wire = ctx->getWireByName(value);
|
WireId wire = ctx->getWireByName(value);
|
||||||
if (wire != WireId()) {
|
if (wire != WireId()) {
|
||||||
decals.push_back(ctx->getWireDecal(wire));
|
decals.push_back(ctx->getWireDecal(wire));
|
||||||
Q_EMIT selected(decals);
|
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case ElementType::PIP: {
|
case ElementType::PIP: {
|
||||||
PipId pip = ctx->getPipByName(value);
|
PipId pip = ctx->getPipByName(value);
|
||||||
if (pip != PipId()) {
|
if (pip != PipId()) {
|
||||||
decals.push_back(ctx->getPipDecal(pip));
|
decals.push_back(ctx->getPipDecal(pip));
|
||||||
Q_EMIT selected(decals);
|
|
||||||
}
|
}
|
||||||
} break;
|
} break;
|
||||||
case ElementType::NET: {
|
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;
|
} break;
|
||||||
case ElementType::CELL: {
|
case ElementType::CELL: {
|
||||||
|
CellInfo *cell = ctx->cells.at(value).get();
|
||||||
|
if (cell->bel != BelId()) {
|
||||||
|
decals.push_back(ctx->getBelDecal(cell->bel));
|
||||||
|
}
|
||||||
} break;
|
} break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -65,8 +65,6 @@ class DesignWidget : public QWidget
|
|||||||
void info(std::string text);
|
void info(std::string text);
|
||||||
void selected(std::vector<DecalXY> decal);
|
void selected(std::vector<DecalXY> decal);
|
||||||
void highlight(std::vector<DecalXY> decal, int group);
|
void highlight(std::vector<DecalXY> decal, int group);
|
||||||
void finishContextLoad();
|
|
||||||
void contextLoadStatus(std::string text);
|
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void prepareMenuProperty(const QPoint &pos);
|
void prepareMenuProperty(const QPoint &pos);
|
||||||
@ -104,6 +102,7 @@ class DesignWidget : public QWidget
|
|||||||
QAction *actionPrev;
|
QAction *actionPrev;
|
||||||
QAction *actionNext;
|
QAction *actionNext;
|
||||||
QAction *actionLast;
|
QAction *actionLast;
|
||||||
|
QAction *actionClear;
|
||||||
|
|
||||||
QColor highlightColors[8];
|
QColor highlightColors[8];
|
||||||
QMap<QTreeWidgetItem *, int> highlightSelected;
|
QMap<QTreeWidgetItem *, int> highlightSelected;
|
||||||
|
@ -226,7 +226,6 @@ void MainWindow::new_proj()
|
|||||||
ctx = std::unique_ptr<Context>(new Context(chipArgs));
|
ctx = std::unique_ptr<Context>(new Context(chipArgs));
|
||||||
actionLoadJSON->setEnabled(true);
|
actionLoadJSON->setEnabled(true);
|
||||||
|
|
||||||
Q_EMIT displaySplash();
|
|
||||||
Q_EMIT contextChanged(ctx.get());
|
Q_EMIT contextChanged(ctx.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
@ -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
|
|
BIN
gui/resources/cross.png
Normal file
BIN
gui/resources/cross.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 655 B |
Binary file not shown.
Before Width: | Height: | Size: 4.5 KiB |
@ -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
|
|
@ -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
|
|
108
gui/yosystab.cc
108
gui/yosystab.cc
@ -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
|
|
@ -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
|
|
@ -25,7 +25,7 @@
|
|||||||
#include "placer1.h"
|
#include "placer1.h"
|
||||||
#include "router1.h"
|
#include "router1.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
#include "cells.h"
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
// -----------------------------------------------------------------------
|
||||||
@ -44,8 +44,8 @@ IdString Arch::belTypeToId(BelType type) const
|
|||||||
return id("ICESTORM_PLL");
|
return id("ICESTORM_PLL");
|
||||||
if (type == TYPE_SB_WARMBOOT)
|
if (type == TYPE_SB_WARMBOOT)
|
||||||
return id("SB_WARMBOOT");
|
return id("SB_WARMBOOT");
|
||||||
if (type == TYPE_SB_MAC16)
|
if (type == TYPE_ICESTORM_DSP)
|
||||||
return id("SB_MAC16");
|
return id("ICESTORM_DSP");
|
||||||
if (type == TYPE_ICESTORM_HFOSC)
|
if (type == TYPE_ICESTORM_HFOSC)
|
||||||
return id("ICESTORM_HFOSC");
|
return id("ICESTORM_HFOSC");
|
||||||
if (type == TYPE_ICESTORM_LFOSC)
|
if (type == TYPE_ICESTORM_LFOSC)
|
||||||
@ -79,8 +79,8 @@ BelType Arch::belTypeFromId(IdString type) const
|
|||||||
return TYPE_ICESTORM_PLL;
|
return TYPE_ICESTORM_PLL;
|
||||||
if (type == id("SB_WARMBOOT"))
|
if (type == id("SB_WARMBOOT"))
|
||||||
return TYPE_SB_WARMBOOT;
|
return TYPE_SB_WARMBOOT;
|
||||||
if (type == id("SB_MAC16"))
|
if (type == id("ICESTORM_DSP"))
|
||||||
return TYPE_SB_MAC16;
|
return TYPE_ICESTORM_DSP;
|
||||||
if (type == id("ICESTORM_HFOSC"))
|
if (type == id("ICESTORM_HFOSC"))
|
||||||
return TYPE_ICESTORM_HFOSC;
|
return TYPE_ICESTORM_HFOSC;
|
||||||
if (type == id("ICESTORM_LFOSC"))
|
if (type == id("ICESTORM_LFOSC"))
|
||||||
@ -610,8 +610,7 @@ std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bel_type == TYPE_ICESTORM_RAM) {
|
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 tx = chip_info->bel_data[bel.index].x;
|
||||||
int ty = chip_info->bel_data[bel.index].y + i;
|
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.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
|
||||||
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
|
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.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;
|
el.z = 0;
|
||||||
ret.push_back(el);
|
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;
|
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
|
NEXTPNR_NAMESPACE_END
|
||||||
|
24
ice40/arch.h
24
ice40/arch.h
@ -153,15 +153,31 @@ NPNR_PACKED_STRUCT(struct BitstreamInfoPOD {
|
|||||||
RelPtr<IerenInfoPOD> ierens;
|
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 {
|
NPNR_PACKED_STRUCT(struct ChipInfoPOD {
|
||||||
int32_t width, height;
|
int32_t width, height;
|
||||||
int32_t num_bels, num_wires, num_pips;
|
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<BelInfoPOD> bel_data;
|
||||||
RelPtr<WireInfoPOD> wire_data;
|
RelPtr<WireInfoPOD> wire_data;
|
||||||
RelPtr<PipInfoPOD> pip_data;
|
RelPtr<PipInfoPOD> pip_data;
|
||||||
RelPtr<TileType> tile_grid;
|
RelPtr<TileType> tile_grid;
|
||||||
RelPtr<BitstreamInfoPOD> bits_info;
|
RelPtr<BitstreamInfoPOD> bits_info;
|
||||||
|
RelPtr<BelConfigPOD> bel_config;
|
||||||
RelPtr<PackageInfoPOD> packages_data;
|
RelPtr<PackageInfoPOD> packages_data;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -713,6 +729,12 @@ struct Arch : BaseCtx
|
|||||||
// Helper function for above
|
// Helper function for above
|
||||||
bool logicCellsCompatible(const std::vector<const CellInfo *> &cells) const;
|
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_glb_buf_out;
|
||||||
IdString id_icestorm_lc, id_sb_io, id_sb_gb;
|
IdString id_icestorm_lc, id_sb_io, id_sb_gb;
|
||||||
IdString id_cen, id_clk, id_sr;
|
IdString id_cen, id_clk, id_sr;
|
||||||
|
@ -31,45 +31,37 @@ bool Arch::logicCellsCompatible(const std::vector<const CellInfo *> &cells) cons
|
|||||||
int locals_count = 0;
|
int locals_count = 0;
|
||||||
|
|
||||||
for (auto cell : cells) {
|
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) {
|
if (!dffs_exist) {
|
||||||
dffs_exist = true;
|
dffs_exist = true;
|
||||||
cen = get_net_or_empty(cell, id_cen);
|
cen = cell->lcInfo.cen;
|
||||||
clk = get_net_or_empty(cell, id_clk);
|
clk = cell->lcInfo.clk;
|
||||||
sr = get_net_or_empty(cell, id_sr);
|
sr = cell->lcInfo.sr;
|
||||||
|
|
||||||
if (!isGlobalNet(cen) && cen != nullptr)
|
if (cen != nullptr && !cen->is_global)
|
||||||
locals_count++;
|
locals_count++;
|
||||||
if (!isGlobalNet(clk) && clk != nullptr)
|
if (clk != nullptr && !clk->is_global)
|
||||||
locals_count++;
|
locals_count++;
|
||||||
if (!isGlobalNet(sr) && sr != nullptr)
|
if (sr != nullptr && !sr->is_global)
|
||||||
locals_count++;
|
locals_count++;
|
||||||
|
|
||||||
if (bool_or_default(cell->params, id_neg_clk)) {
|
if (cell->lcInfo.negClk) {
|
||||||
dffs_neg = true;
|
dffs_neg = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (cen != get_net_or_empty(cell, id_cen))
|
if (cen != cell->lcInfo.cen)
|
||||||
return false;
|
return false;
|
||||||
if (clk != get_net_or_empty(cell, id_clk))
|
if (clk != cell->lcInfo.clk)
|
||||||
return false;
|
return false;
|
||||||
if (sr != get_net_or_empty(cell, id_sr))
|
if (sr != cell->lcInfo.sr)
|
||||||
return false;
|
return false;
|
||||||
if (dffs_neg != bool_or_default(cell->params, id_neg_clk))
|
if (dffs_neg != cell->lcInfo.negClk)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const NetInfo *i0 = get_net_or_empty(cell, id_i0), *i1 = get_net_or_empty(cell, id_i1),
|
locals_count += cell->lcInfo.inputCount;
|
||||||
*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++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return locals_count <= 32;
|
return locals_count <= 32;
|
||||||
@ -116,21 +108,15 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
|
|||||||
} else if (cell->type == id_sb_io) {
|
} else if (cell->type == id_sb_io) {
|
||||||
return getBelPackagePin(bel) != "";
|
return getBelPackagePin(bel) != "";
|
||||||
} else if (cell->type == id_sb_gb) {
|
} 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);
|
NPNR_ASSERT(cell->ports.at(id_glb_buf_out).net != nullptr);
|
||||||
for (auto user : cell->ports.at(id_glb_buf_out).net->users) {
|
const NetInfo *net = cell->ports.at(id_glb_buf_out).net;
|
||||||
if (is_reset_port(this, user))
|
|
||||||
is_reset = true;
|
|
||||||
if (is_enable_port(this, user))
|
|
||||||
is_cen = true;
|
|
||||||
}
|
|
||||||
IdString glb_net = getWireName(getWireBelPin(bel, PIN_GLOBAL_BUFFER_OUTPUT));
|
IdString glb_net = getWireName(getWireBelPin(bel, PIN_GLOBAL_BUFFER_OUTPUT));
|
||||||
int glb_id = std::stoi(std::string("") + glb_net.str(this).back());
|
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;
|
return false;
|
||||||
else if (is_reset)
|
else if (net->is_reset)
|
||||||
return (glb_id % 2) == 0;
|
return (glb_id % 2) == 0;
|
||||||
else if (is_cen)
|
else if (net->is_enable)
|
||||||
return (glb_id % 2) == 1;
|
return (glb_id % 2) == 1;
|
||||||
else
|
else
|
||||||
return true;
|
return true;
|
||||||
|
@ -54,7 +54,7 @@ enum BelType : int32_t
|
|||||||
TYPE_SB_GB,
|
TYPE_SB_GB,
|
||||||
TYPE_ICESTORM_PLL,
|
TYPE_ICESTORM_PLL,
|
||||||
TYPE_SB_WARMBOOT,
|
TYPE_SB_WARMBOOT,
|
||||||
TYPE_SB_MAC16,
|
TYPE_ICESTORM_DSP,
|
||||||
TYPE_ICESTORM_HFOSC,
|
TYPE_ICESTORM_HFOSC,
|
||||||
TYPE_ICESTORM_LFOSC,
|
TYPE_ICESTORM_LFOSC,
|
||||||
TYPE_SB_I2C,
|
TYPE_SB_I2C,
|
||||||
@ -150,6 +150,28 @@ struct DecalId
|
|||||||
bool operator!=(const DecalId &other) const { return (type != other.type) || (index != other.index); }
|
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
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
namespace std {
|
namespace std {
|
||||||
@ -201,5 +223,4 @@ template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DecalId>
|
|||||||
return seed;
|
return seed;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace std
|
} // namespace std
|
||||||
|
@ -36,7 +36,7 @@ const ConfigEntryPOD &find_config(const TileInfoPOD &tile, const std::string &na
|
|||||||
return tile.entries[i];
|
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)
|
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,79 @@ 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); }
|
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>> ¶ms, 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void write_asc(const Context *ctx, std::ostream &out)
|
void write_asc(const Context *ctx, std::ostream &out)
|
||||||
{
|
{
|
||||||
// [y][x][row][col]
|
// [y][x][row][col]
|
||||||
const ChipInfoPOD &ci = *ctx->chip_info;
|
const ChipInfoPOD &ci = *ctx->chip_info;
|
||||||
const BitstreamInfoPOD &bi = *ci.bits_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);
|
config.resize(ci.height);
|
||||||
for (int y = 0; y < ci.height; y++) {
|
for (int y = 0; y < ci.height; y++) {
|
||||||
config.at(y).resize(ci.width);
|
config.at(y).resize(ci.width);
|
||||||
@ -192,7 +259,7 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
bool val = (pin_type >> i) & 0x01;
|
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), "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);
|
auto ieren = get_ieren(bi, x, y, z);
|
||||||
int iex, iey, iez;
|
int iex, iey, iez;
|
||||||
std::tie(iex, iey, iez) = ieren;
|
std::tie(iex, iey, iez) = ieren;
|
||||||
@ -265,6 +332,27 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
NPNR_ASSERT(false);
|
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 {
|
} else {
|
||||||
NPNR_ASSERT(false);
|
NPNR_ASSERT(false);
|
||||||
}
|
}
|
||||||
@ -341,8 +429,9 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
set_config(ti, config.at(y).at(x),
|
set_config(ti, config.at(y).at(x),
|
||||||
"Cascade.IPCON_LC0" + std::to_string(lc_idx) + "_inmux02_5", true);
|
"Cascade.IPCON_LC0" + std::to_string(lc_idx) + "_inmux02_5", true);
|
||||||
else
|
else
|
||||||
set_config(ti, config.at(y).at(x), "Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) +
|
set_config(ti, config.at(y).at(x),
|
||||||
"_LC0" + std::to_string(lc_idx) + "_inmux02_5",
|
"Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) + "_LC0" +
|
||||||
|
std::to_string(lc_idx) + "_inmux02_5",
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
read_verilog blinky.v
|
read_verilog blinky.v
|
||||||
synth_ice40 -top blinky -nocarry
|
synth_ice40 -top blinky
|
||||||
write_json blinky.json
|
write_json blinky.json
|
||||||
|
@ -142,6 +142,71 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri
|
|||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
add_port(ctx, new_cell.get(), "MASKWREN_" + std::to_string(i), PORT_IN);
|
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 {
|
} else {
|
||||||
log_error("unable to create iCE40 cell of type %s", type.c_str(ctx));
|
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"))
|
if (port.cell->type == ctx->id("ICESTORM_LC"))
|
||||||
return port.port == ctx->id("CLK");
|
return port.port == ctx->id("CLK");
|
||||||
if (is_ram(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_RAM"))
|
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;
|
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");
|
return port.port == ctx->id("R") || port.port == ctx->id("S");
|
||||||
if (port.cell->type == ctx->id("ICESTORM_LC"))
|
if (port.cell->type == ctx->id("ICESTORM_LC"))
|
||||||
return port.port == ctx->id("SR");
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -279,6 +350,9 @@ bool is_enable_port(const BaseCtx *ctx, const PortRef &port)
|
|||||||
return port.port == ctx->id("E");
|
return port.port == ctx->id("E");
|
||||||
if (port.cell->type == ctx->id("ICESTORM_LC"))
|
if (port.cell->type == ctx->id("ICESTORM_LC"))
|
||||||
return port.port == ctx->id("CEN");
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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_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
|
// 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
|
// as needed. Set no_dff if a DFF is not being used, so that the output
|
||||||
// can be reconnected
|
// can be reconnected
|
||||||
|
@ -38,7 +38,7 @@ switches = list()
|
|||||||
ierens = list()
|
ierens = list()
|
||||||
|
|
||||||
extra_cells = dict()
|
extra_cells = dict()
|
||||||
|
extra_cell_config = dict()
|
||||||
packages = list()
|
packages = list()
|
||||||
|
|
||||||
wire_uphill_belport = dict()
|
wire_uphill_belport = dict()
|
||||||
@ -159,7 +159,7 @@ def wire_type(name):
|
|||||||
name = name.split('/')[-1]
|
name = name.split('/')[-1]
|
||||||
wt = None
|
wt = None
|
||||||
|
|
||||||
if name.startswith("glb_netwk_"):
|
if name.startswith("glb_netwk_") or name.startswith("padin_"):
|
||||||
wt = "GLOBAL"
|
wt = "GLOBAL"
|
||||||
elif name.startswith("D_IN_") or name.startswith("D_OUT_"):
|
elif name.startswith("D_IN_") or name.startswith("D_OUT_"):
|
||||||
wt = "LOCAL"
|
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])))
|
extra_cells[mode[1]].append((line[0], (int(line[1]), int(line[2]), line[3])))
|
||||||
continue
|
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):
|
def add_bel_input(bel, wire, port):
|
||||||
if wire not in wire_downhill_belports:
|
if wire not in wire_downhill_belports:
|
||||||
wire_downhill_belports[wire] = set()
|
wire_downhill_belports[wire] = set()
|
||||||
@ -567,6 +580,7 @@ def is_ec_output(ec_entry):
|
|||||||
def add_bel_ec(ec):
|
def add_bel_ec(ec):
|
||||||
ectype, x, y, z = ec
|
ectype, x, y, z = ec
|
||||||
bel = len(bel_name)
|
bel = len(bel_name)
|
||||||
|
extra_cell_config[bel] = []
|
||||||
bel_name.append("X%d/Y%d/%s_%d" % (x, y, ectype.lower(), z))
|
bel_name.append("X%d/Y%d/%s_%d" % (x, y, ectype.lower(), z))
|
||||||
bel_type.append(ectype)
|
bel_type.append(ectype)
|
||||||
bel_pos.append((x, y, z))
|
bel_pos.append((x, y, z))
|
||||||
@ -578,8 +592,7 @@ def add_bel_ec(ec):
|
|||||||
else:
|
else:
|
||||||
add_bel_input(bel, wire_names[entry[1]], entry[0])
|
add_bel_input(bel, wire_names[entry[1]], entry[0])
|
||||||
else:
|
else:
|
||||||
# Configuration bit, need to create a structure for these
|
extra_cell_config[bel].append(entry)
|
||||||
pass
|
|
||||||
|
|
||||||
for tile_xy, tile_type in sorted(tiles.items()):
|
for tile_xy, tile_type in sorted(tiles.items()):
|
||||||
if tile_type == "logic":
|
if tile_type == "logic":
|
||||||
@ -1175,6 +1188,23 @@ bba.l("tile_grid_%s" % dev_name, "TileType")
|
|||||||
for t in tilegrid:
|
for t in tilegrid:
|
||||||
bba.u32(tiletypes[t], "tiletype")
|
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")
|
bba.l("package_info_%s" % dev_name, "PackageInfoPOD")
|
||||||
for info in packageinfo:
|
for info in packageinfo:
|
||||||
bba.s(info[0], "name")
|
bba.s(info[0], "name")
|
||||||
@ -1188,12 +1218,14 @@ bba.u32(len(bel_name), "num_bels")
|
|||||||
bba.u32(num_wires, "num_wires")
|
bba.u32(num_wires, "num_wires")
|
||||||
bba.u32(len(pipinfo), "num_pips")
|
bba.u32(len(pipinfo), "num_pips")
|
||||||
bba.u32(len(switchinfo), "num_switches")
|
bba.u32(len(switchinfo), "num_switches")
|
||||||
|
bba.u32(len(extra_cell_config), "num_belcfgs")
|
||||||
bba.u32(len(packageinfo), "num_packages")
|
bba.u32(len(packageinfo), "num_packages")
|
||||||
bba.r("bel_data_%s" % dev_name, "bel_data")
|
bba.r("bel_data_%s" % dev_name, "bel_data")
|
||||||
bba.r("wire_data_%s" % dev_name, "wire_data")
|
bba.r("wire_data_%s" % dev_name, "wire_data")
|
||||||
bba.r("pip_data_%s" % dev_name, "pip_data")
|
bba.r("pip_data_%s" % dev_name, "pip_data")
|
||||||
bba.r("tile_grid_%s" % dev_name, "tile_grid")
|
bba.r("tile_grid_%s" % dev_name, "tile_grid")
|
||||||
bba.r("bits_info_%s" % dev_name, "bits_info")
|
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.r("package_info_%s" % dev_name, "packages_data")
|
||||||
|
|
||||||
bba.finalize()
|
bba.finalize()
|
||||||
|
@ -640,10 +640,8 @@ static bool getWireXY_local(GfxTileWireId id, float &x, float &y)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void pipGfx(std::vector<GraphicElement> &g, int x, int y,
|
void pipGfx(std::vector<GraphicElement> &g, int x, int y, float x1, float y1, float x2, float y2, float swx1,
|
||||||
float x1, float y1, float x2, float y2,
|
float swy1, float swx2, float swy2, GraphicElement::style_t style)
|
||||||
float swx1, float swy1, float swx2, float swy2,
|
|
||||||
GraphicElement::style_t style)
|
|
||||||
{
|
{
|
||||||
float tx = 0.5 * (x1 + x2);
|
float tx = 0.5 * (x1 + x2);
|
||||||
float ty = 0.5 * (y1 + y2);
|
float ty = 0.5 * (y1 + y2);
|
||||||
@ -693,7 +691,8 @@ edge_pip:
|
|||||||
g.push_back(el);
|
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;
|
float x1, y1, x2, y2;
|
||||||
|
|
||||||
|
@ -468,7 +468,8 @@ enum GfxTileWireId
|
|||||||
};
|
};
|
||||||
|
|
||||||
void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style);
|
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
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
|
@ -277,6 +277,10 @@ static void pack_ram(Context *ctx)
|
|||||||
if (bpos != std::string::npos) {
|
if (bpos != std::string::npos) {
|
||||||
newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2);
|
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));
|
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
|
||||||
}
|
}
|
||||||
new_cells.push_back(std::move(packed));
|
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') &&
|
if ((is_lut(ctx, uc) || is_lc(ctx, uc) || is_carry(ctx, uc)) && (user.port.str(ctx).at(0) == 'I') &&
|
||||||
!constval) {
|
!constval) {
|
||||||
uc->ports[user.port].net = nullptr;
|
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 {
|
} else {
|
||||||
uc->ports[user.port].net = constnet;
|
uc->ports[user.port].net = constnet;
|
||||||
constnet->users.push_back(user);
|
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));
|
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
|
||||||
}
|
}
|
||||||
new_cells.push_back(std::move(packed));
|
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_carries(ctx);
|
||||||
pack_ram(ctx);
|
pack_ram(ctx);
|
||||||
pack_special(ctx);
|
pack_special(ctx);
|
||||||
|
ctx->assignArchInfo();
|
||||||
log_info("Checksum: 0x%08x\n", ctx->checksum());
|
log_info("Checksum: 0x%08x\n", ctx->checksum());
|
||||||
return true;
|
return true;
|
||||||
} catch (log_execution_error_exception) {
|
} catch (log_execution_error_exception) {
|
||||||
|
@ -2,5 +2,5 @@
|
|||||||
set -ex
|
set -ex
|
||||||
rm -f picorv32.v
|
rm -f picorv32.v
|
||||||
wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/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
|
../nextpnr-ice40 --hx8k --asc picorv32.asc --json picorv32.json
|
||||||
|
@ -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
|
|
@ -127,6 +127,7 @@ class PlacementLegaliser
|
|||||||
legalise_others();
|
legalise_others();
|
||||||
legalise_logic_tiles();
|
legalise_logic_tiles();
|
||||||
bool replaced_cells = replace_cells();
|
bool replaced_cells = replace_cells();
|
||||||
|
ctx->assignArchInfo();
|
||||||
return legalised_carries && replaced_cells;
|
return legalised_carries && replaced_cells;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -371,6 +372,7 @@ class PlacementLegaliser
|
|||||||
NPNR_ASSERT(ctx->nets.find(co_i3_name) == ctx->nets.end());
|
NPNR_ASSERT(ctx->nets.find(co_i3_name) == ctx->nets.end());
|
||||||
ctx->nets[co_i3_name] = std::move(co_i3_net);
|
ctx->nets[co_i3_name] = std::move(co_i3_net);
|
||||||
IdString name = lc->name;
|
IdString name = lc->name;
|
||||||
|
ctx->assignCellInfo(lc.get());
|
||||||
ctx->cells[lc->name] = std::move(lc);
|
ctx->cells[lc->name] = std::move(lc);
|
||||||
createdCells.insert(name);
|
createdCells.insert(name);
|
||||||
return ctx->cells[name].get();
|
return ctx->cells[name].get();
|
||||||
@ -415,6 +417,7 @@ class PlacementLegaliser
|
|||||||
ctx->nets[out_net_name] = std::move(out_net);
|
ctx->nets[out_net_name] = std::move(out_net);
|
||||||
|
|
||||||
IdString name = lc->name;
|
IdString name = lc->name;
|
||||||
|
ctx->assignCellInfo(lc.get());
|
||||||
ctx->cells[lc->name] = std::move(lc);
|
ctx->cells[lc->name] = std::move(lc);
|
||||||
createdCells.insert(name);
|
createdCells.insert(name);
|
||||||
return ctx->cells[name].get();
|
return ctx->cells[name].get();
|
||||||
|
@ -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))
|
|
Loading…
Reference in New Issue
Block a user