aboutsummaryrefslogtreecommitdiffstats
path: root/ecp5
diff options
context:
space:
mode:
authorDavid Shah <davey1576@gmail.com>2018-10-03 13:45:42 +0100
committerGitHub <noreply@github.com>2018-10-03 13:45:42 +0100
commit22973527727a3747349f2d6f234f20fd459f05c3 (patch)
tree15f999b641cc7a609bcb8a9ab858ebfd362cb833 /ecp5
parent15f38829a55843d747b817525325e4115d984dc4 (diff)
parentbf7161d2b49ff5660626a0ac4af5a7eeb3fb77c1 (diff)
downloadnextpnr-22973527727a3747349f2d6f234f20fd459f05c3.tar.gz
nextpnr-22973527727a3747349f2d6f234f20fd459f05c3.tar.bz2
nextpnr-22973527727a3747349f2d6f234f20fd459f05c3.zip
Merge pull request #83 from YosysHQ/ecp5_dram
Adding support for ECP5 distributed RAM
Diffstat (limited to 'ecp5')
-rw-r--r--ecp5/arch.cc4
-rw-r--r--ecp5/bitstream.cc28
-rw-r--r--ecp5/cells.cc105
-rw-r--r--ecp5/cells.h2
-rw-r--r--ecp5/globals.cc18
-rw-r--r--ecp5/pack.cc122
6 files changed, 270 insertions, 9 deletions
diff --git a/ecp5/arch.cc b/ecp5/arch.cc
index 830dfc7c..9c059005 100644
--- a/ecp5/arch.cc
+++ b/ecp5/arch.cc
@@ -511,12 +511,12 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort
delay.delay = 193;
return true;
}
-
+#if 0 // FIXME
if (fromPort == id_WCK && (toPort == id_F0 || toPort == id_F1)) {
delay.delay = 717;
return true;
}
-
+#endif
if ((fromPort == id_A0 && toPort == id_WADO3) || (fromPort == id_A1 && toPort == id_WDO1) ||
(fromPort == id_B0 && toPort == id_WADO1) || (fromPort == id_B1 && toPort == id_WDO3) ||
(fromPort == id_C0 && toPort == id_WADO2) || (fromPort == id_C1 && toPort == id_WDO0) ||
diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc
index bfd51666..296ea753 100644
--- a/ecp5/bitstream.cc
+++ b/ecp5/bitstream.cc
@@ -260,6 +260,17 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
str_or_default(ci->params, ctx->id("SRMODE"), "LSR_OVER_CE"));
cc.tiles[tname].add_enum("LSR1.LSRMUX", str_or_default(ci->params, ctx->id("LSRMUX"), "LSR"));
}
+
+ NetInfo *clknet = nullptr;
+ if (ci->ports.find(ctx->id("CLK")) != ci->ports.end() && ci->ports.at(ctx->id("CLK")).net != nullptr)
+ clknet = ci->ports.at(ctx->id("CLK")).net;
+ if (ctx->getBoundWireNet(ctx->getWireByName(
+ ctx->id(fmt_str("X" << bel.location.x << "/Y" << bel.location.y << "/CLK0")))) == clknet) {
+ cc.tiles[tname].add_enum("CLK0.CLKMUX", str_or_default(ci->params, ctx->id("CLKMUX"), "CLK"));
+ } else if (ctx->getBoundWireNet(ctx->getWireByName(ctx->id(
+ fmt_str("X" << bel.location.x << "/Y" << bel.location.y << "/CLK1")))) == clknet) {
+ cc.tiles[tname].add_enum("CLK1.CLKMUX", str_or_default(ci->params, ctx->id("CLKMUX"), "CLK"));
+ }
}
if (str_or_default(ci->params, ctx->id("MODE"), "LOGIC") == "CCU2") {
@@ -267,6 +278,23 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
str_or_default(ci->params, ctx->id("INJECT1_0"), "YES"));
cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_1",
str_or_default(ci->params, ctx->id("INJECT1_1"), "YES"));
+ } else {
+ // Don't interfere with cascade mux wiring
+ cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_0",
+ str_or_default(ci->params, ctx->id("INJECT1_0"), "_NONE_"));
+ cc.tiles[tname].add_enum(slice + ".CCU2.INJECT1_1",
+ str_or_default(ci->params, ctx->id("INJECT1_1"), "_NONE_"));
+ }
+
+ if (str_or_default(ci->params, ctx->id("MODE"), "LOGIC") == "DPRAM" && slice == "SLICEA") {
+ cc.tiles[tname].add_enum(slice + ".WREMUX", str_or_default(ci->params, ctx->id("WREMUX"), "WRE"));
+
+ NetInfo *wcknet = nullptr;
+ std::string wckmux = str_or_default(ci->params, ctx->id("WCKMUX"), "WCK");
+ wckmux = (wckmux == "WCK") ? "CLK" : wckmux;
+ if (ci->ports.find(ctx->id("WCK")) != ci->ports.end() && ci->ports.at(ctx->id("WCK")).net != nullptr)
+ wcknet = ci->ports.at(ctx->id("WCK")).net;
+ cc.tiles[tname].add_enum("CLK1.CLKMUX", wckmux);
}
// Tie unused inputs high
diff --git a/ecp5/cells.cc b/ecp5/cells.cc
index 7e101ab2..a728104d 100644
--- a/ecp5/cells.cc
+++ b/ecp5/cells.cc
@@ -185,6 +185,8 @@ void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool drive
set_param_safe(has_ff, lc, ctx->id("GSR"), str_or_default(ff->params, ctx->id("GSR"), "DISABLED"));
set_param_safe(has_ff, lc, ctx->id("CEMUX"), str_or_default(ff->params, ctx->id("CEMUX"), "1"));
set_param_safe(has_ff, lc, ctx->id("LSRMUX"), str_or_default(ff->params, ctx->id("LSRMUX"), "LSR"));
+ set_param_safe(has_ff, lc, ctx->id("CLKMUX"), str_or_default(ff->params, ctx->id("CLKMUX"), "CLK"));
+
lc->params[ctx->id(reg + "_SD")] = driven_by_lut ? "1" : "0";
lc->params[ctx->id(reg + "_REGSET")] = str_or_default(ff->params, ctx->id("REGSET"), "RESET");
replace_port_safe(has_ff, ff, ctx->id("CLK"), lc, ctx->id("CLK"));
@@ -238,4 +240,107 @@ void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc)
replace_port(ccu, ctx->id("COUT"), lc, ctx->id("FCO"));
}
+void dram_to_ramw(Context *ctx, CellInfo *ram, CellInfo *lc)
+{
+ lc->params[ctx->id("MODE")] = "RAMW";
+ replace_port(ram, ctx->id("WAD[0]"), lc, ctx->id("D0"));
+ replace_port(ram, ctx->id("WAD[1]"), lc, ctx->id("B0"));
+ replace_port(ram, ctx->id("WAD[2]"), lc, ctx->id("C0"));
+ replace_port(ram, ctx->id("WAD[3]"), lc, ctx->id("A0"));
+
+ replace_port(ram, ctx->id("DI[0]"), lc, ctx->id("C1"));
+ replace_port(ram, ctx->id("DI[1]"), lc, ctx->id("A1"));
+ replace_port(ram, ctx->id("DI[2]"), lc, ctx->id("D1"));
+ replace_port(ram, ctx->id("DI[3]"), lc, ctx->id("B1"));
+}
+
+static unsigned get_dram_init(const Context *ctx, const CellInfo *ram, int bit)
+{
+ const std::string &idata = str_or_default(ram->params, ctx->id("INITVAL"),
+ "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
+ NPNR_ASSERT(idata.length() == 64);
+ unsigned value = 0;
+ for (int i = 0; i < 16; i++) {
+ char c = idata.at(63 - (4 * i + bit));
+ if (c == '1')
+ value |= (1 << i);
+ else
+ NPNR_ASSERT(c == '0' || c == 'x');
+ }
+ return value;
+}
+
+void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw, int index)
+{
+ lc->params[ctx->id("MODE")] = "DPRAM";
+ lc->params[ctx->id("WREMUX")] = str_or_default(ram->params, ctx->id("WREMUX"), "WRE");
+ lc->params[ctx->id("WCKMUX")] = str_or_default(ram->params, ctx->id("WCKMUX"), "WCK");
+
+ unsigned permuted_init0 = 0, permuted_init1 = 0;
+ unsigned init0 = get_dram_init(ctx, ram, index * 2), init1 = get_dram_init(ctx, ram, index * 2 + 1);
+
+ for (int i = 0; i < 16; i++) {
+ int permuted_addr = 0;
+ if (i & 1)
+ permuted_addr |= 8;
+ if (i & 2)
+ permuted_addr |= 2;
+ if (i & 4)
+ permuted_addr |= 4;
+ if (i & 8)
+ permuted_addr |= 1;
+ if (init0 & (1 << permuted_addr))
+ permuted_init0 |= (1 << i);
+ if (init1 & (1 << permuted_addr))
+ permuted_init1 |= (1 << i);
+ }
+
+ lc->params[ctx->id("LUT0_INITVAL")] = std::to_string(permuted_init0);
+ lc->params[ctx->id("LUT1_INITVAL")] = std::to_string(permuted_init1);
+
+ if (ram->ports.count(ctx->id("RAD[0]"))) {
+ connect_port(ctx, ram->ports.at(ctx->id("RAD[0]")).net, lc, ctx->id("D0"));
+ connect_port(ctx, ram->ports.at(ctx->id("RAD[0]")).net, lc, ctx->id("D1"));
+ }
+ if (ram->ports.count(ctx->id("RAD[1]"))) {
+ connect_port(ctx, ram->ports.at(ctx->id("RAD[1]")).net, lc, ctx->id("B0"));
+ connect_port(ctx, ram->ports.at(ctx->id("RAD[1]")).net, lc, ctx->id("B1"));
+ }
+ if (ram->ports.count(ctx->id("RAD[2]"))) {
+ connect_port(ctx, ram->ports.at(ctx->id("RAD[2]")).net, lc, ctx->id("C0"));
+ connect_port(ctx, ram->ports.at(ctx->id("RAD[2]")).net, lc, ctx->id("C1"));
+ }
+ if (ram->ports.count(ctx->id("RAD[3]"))) {
+ connect_port(ctx, ram->ports.at(ctx->id("RAD[3]")).net, lc, ctx->id("A0"));
+ connect_port(ctx, ram->ports.at(ctx->id("RAD[3]")).net, lc, ctx->id("A1"));
+ }
+
+ if (ram->ports.count(ctx->id("WRE")))
+ connect_port(ctx, ram->ports.at(ctx->id("WRE")).net, lc, ctx->id("WRE"));
+ if (ram->ports.count(ctx->id("WCK")))
+ connect_port(ctx, ram->ports.at(ctx->id("WCK")).net, lc, ctx->id("WCK"));
+
+ connect_ports(ctx, ramw, id_WADO0, lc, id_WAD0);
+ connect_ports(ctx, ramw, id_WADO1, lc, id_WAD1);
+ connect_ports(ctx, ramw, id_WADO2, lc, id_WAD2);
+ connect_ports(ctx, ramw, id_WADO3, lc, id_WAD3);
+
+ if (index == 0) {
+ connect_ports(ctx, ramw, id_WDO0, lc, id_WD0);
+ connect_ports(ctx, ramw, id_WDO1, lc, id_WD1);
+
+ replace_port(ram, ctx->id("DO[0]"), lc, id_F0);
+ replace_port(ram, ctx->id("DO[1]"), lc, id_F1);
+
+ } else if (index == 1) {
+ connect_ports(ctx, ramw, id_WDO2, lc, id_WD0);
+ connect_ports(ctx, ramw, id_WDO3, lc, id_WD1);
+
+ replace_port(ram, ctx->id("DO[2]"), lc, id_F0);
+ replace_port(ram, ctx->id("DO[3]"), lc, id_F1);
+ } else {
+ NPNR_ASSERT_FALSE("bad DPRAM index");
+ }
+}
+
NEXTPNR_NAMESPACE_END
diff --git a/ecp5/cells.h b/ecp5/cells.h
index d2ea5490..a5229fe0 100644
--- a/ecp5/cells.h
+++ b/ecp5/cells.h
@@ -49,6 +49,8 @@ inline bool is_l6mux(const BaseCtx *ctx, const CellInfo *cell) { return cell->ty
void ff_to_slice(Context *ctx, CellInfo *ff, CellInfo *lc, int index, bool driven_by_lut);
void lut_to_slice(Context *ctx, CellInfo *lut, CellInfo *lc, int index);
void ccu2c_to_slice(Context *ctx, CellInfo *ccu, CellInfo *lc);
+void dram_to_ramw(Context *ctx, CellInfo *ram, CellInfo *lc);
+void dram_to_ram_slice(Context *ctx, CellInfo *ram, CellInfo *lc, CellInfo *ramw, int index);
NEXTPNR_NAMESPACE_END
diff --git a/ecp5/globals.cc b/ecp5/globals.cc
index 91d224e5..364e4bca 100644
--- a/ecp5/globals.cc
+++ b/ecp5/globals.cc
@@ -52,7 +52,7 @@ class Ecp5GlobalRouter
private:
bool is_clock_port(const PortRef &user)
{
- if (user.cell->type == id_TRELLIS_SLICE && user.port == id_CLK)
+ if (user.cell->type == id_TRELLIS_SLICE && (user.port == id_CLK || user.port == id_WCK))
return true;
return false;
}
@@ -302,6 +302,14 @@ class Ecp5GlobalRouter
ctx->nets[glbnet->name] = std::move(glbnet);
return glbptr;
}
+
+ int global_route_priority(const PortRef &load)
+ {
+ if (load.port == id_WCK || load.port == id_WRE)
+ return 90;
+ return 99;
+ }
+
Context *ctx;
public:
@@ -333,7 +341,13 @@ class Ecp5GlobalRouter
NetInfo *global = insert_dcc(clock);
bool routed = route_onto_global(global, glbid);
NPNR_ASSERT(routed);
- for (const auto &user : global->users) {
+
+ // WCK must have routing priority
+ auto sorted_users = global->users;
+ std::sort(sorted_users.begin(), sorted_users.end(), [this](const PortRef &a, const PortRef &b) {
+ return global_route_priority(a) < global_route_priority(b);
+ });
+ for (const auto &user : sorted_users) {
route_logic_tile_global(global, glbid, user);
}
}
diff --git a/ecp5/pack.cc b/ecp5/pack.cc
index 14b387d5..0045617b 100644
--- a/ecp5/pack.cc
+++ b/ecp5/pack.cc
@@ -108,7 +108,7 @@ class Ecp5Packer
}
// Return whether or not an FF can be added to a tile (pairing checks must also be done using the fn above)
- bool can_add_ff_to_file(const std::vector<CellInfo *> &tile_ffs, CellInfo *ff0)
+ bool can_add_ff_to_tile(const std::vector<CellInfo *> &tile_ffs, CellInfo *ff0)
{
for (const auto &existing : tile_ffs) {
if (net_or_nullptr(existing, ctx->id("CLK")) != net_or_nullptr(ff0, ctx->id("CLK")))
@@ -128,6 +128,20 @@ class Ecp5Packer
return true;
}
+ // Return true if a FF can be added to a DPRAM slice
+ bool can_pack_ff_dram(CellInfo *dpram, CellInfo *ff)
+ {
+ std::string wckmux = str_or_default(dpram->params, ctx->id("WCKMUX"), "WCK");
+ std::string clkmux = str_or_default(ff->params, ctx->id("CLKMUX"), "CLK");
+ if (wckmux != clkmux && !(wckmux == "WCK" && clkmux == "CLK"))
+ return false;
+ std::string wremux = str_or_default(dpram->params, ctx->id("WREMUX"), "WRE");
+ std::string lsrmux = str_or_default(ff->params, ctx->id("LSRMUX"), "LSR");
+ if (wremux != lsrmux && !(wremux == "WRE" && lsrmux == "LSR"))
+ return false;
+ return true;
+ }
+
// Return true if two LUTs can be paired considering FF compatibility
bool can_pack_lutff(IdString lut0, IdString lut1)
{
@@ -496,6 +510,7 @@ class Ecp5Packer
std::vector<std::vector<CellInfo *>> packed_chains;
// Chain packing
+ std::vector<std::tuple<CellInfo *, CellInfo *, int>> ff_packing;
for (auto &chain : all_chains) {
int cell_count = 0;
std::vector<CellInfo *> tile_ffs;
@@ -512,8 +527,8 @@ class Ecp5Packer
NetInfo *f0net = slice->ports.at(ctx->id("F0")).net;
if (f0net != nullptr) {
ff0 = net_only_drives(ctx, f0net, is_ff, ctx->id("DI"), false);
- if (ff0 != nullptr && can_add_ff_to_file(tile_ffs, ff0)) {
- ff_to_slice(ctx, ff0, slice.get(), 0, true);
+ if (ff0 != nullptr && can_add_ff_to_tile(tile_ffs, ff0)) {
+ ff_packing.push_back(std::make_tuple(ff0, slice.get(), 0));
tile_ffs.push_back(ff0);
packed_cells.insert(ff0->name);
}
@@ -524,8 +539,8 @@ class Ecp5Packer
if (f1net != nullptr) {
ff1 = net_only_drives(ctx, f1net, is_ff, ctx->id("DI"), false);
if (ff1 != nullptr && (ff0 == nullptr || can_pack_ffs(ff0, ff1)) &&
- can_add_ff_to_file(tile_ffs, ff1)) {
- ff_to_slice(ctx, ff1, slice.get(), 1, true);
+ can_add_ff_to_tile(tile_ffs, ff1)) {
+ ff_packing.push_back(std::make_tuple(ff1, slice.get(), 1));
tile_ffs.push_back(ff1);
packed_cells.insert(ff1->name);
}
@@ -538,6 +553,9 @@ class Ecp5Packer
packed_chains.push_back(packed_chain);
}
+ for (auto ff : ff_packing)
+ ff_to_slice(ctx, std::get<0>(ff), std::get<1>(ff), std::get<2>(ff), true);
+
// Relative chain placement
for (auto &chain : packed_chains) {
chain.at(0)->constr_abs_z = true;
@@ -555,6 +573,98 @@ class Ecp5Packer
flush_cells();
}
+ // Pack distributed RAM
+ void pack_dram()
+ {
+ for (auto cell : sorted(ctx->cells)) {
+ CellInfo *ci = cell.second;
+ if (is_dpram(ctx, ci)) {
+
+ // Create RAMW slice
+ std::unique_ptr<CellInfo> ramw_slice =
+ create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE"), ci->name.str(ctx) + "$RAMW_SLICE");
+ dram_to_ramw(ctx, ci, ramw_slice.get());
+
+ // Create actual RAM slices
+ std::unique_ptr<CellInfo> ram0_slice =
+ create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE"), ci->name.str(ctx) + "$DPRAM0_SLICE");
+ dram_to_ram_slice(ctx, ci, ram0_slice.get(), ramw_slice.get(), 0);
+
+ std::unique_ptr<CellInfo> ram1_slice =
+ create_ecp5_cell(ctx, ctx->id("TRELLIS_SLICE"), ci->name.str(ctx) + "$DPRAM1_SLICE");
+ dram_to_ram_slice(ctx, ci, ram1_slice.get(), ramw_slice.get(), 1);
+
+ // Disconnect ports of original cell after packing
+ disconnect_port(ctx, ci, id_WCK);
+ disconnect_port(ctx, ci, id_WRE);
+
+ disconnect_port(ctx, ci, ctx->id("RAD[0]"));
+ disconnect_port(ctx, ci, ctx->id("RAD[1]"));
+ disconnect_port(ctx, ci, ctx->id("RAD[2]"));
+ disconnect_port(ctx, ci, ctx->id("RAD[3]"));
+
+ // Attempt to pack FFs into RAM slices
+ std::vector<std::tuple<CellInfo *, CellInfo *, int>> ff_packing;
+ std::vector<CellInfo *> tile_ffs;
+ for (auto slice : {ram0_slice.get(), ram1_slice.get()}) {
+ CellInfo *ff0 = nullptr;
+ NetInfo *f0net = slice->ports.at(ctx->id("F0")).net;
+ if (f0net != nullptr) {
+ ff0 = net_only_drives(ctx, f0net, is_ff, ctx->id("DI"), false);
+ if (ff0 != nullptr && can_add_ff_to_tile(tile_ffs, ff0)) {
+ if (can_pack_ff_dram(slice, ff0)) {
+ ff_packing.push_back(std::make_tuple(ff0, slice, 0));
+ tile_ffs.push_back(ff0);
+ packed_cells.insert(ff0->name);
+ }
+ }
+ }
+
+ CellInfo *ff1 = nullptr;
+ NetInfo *f1net = slice->ports.at(ctx->id("F1")).net;
+ if (f1net != nullptr) {
+ ff1 = net_only_drives(ctx, f1net, is_ff, ctx->id("DI"), false);
+ if (ff1 != nullptr && (ff0 == nullptr || can_pack_ffs(ff0, ff1)) &&
+ can_add_ff_to_tile(tile_ffs, ff1)) {
+ if (can_pack_ff_dram(slice, ff1)) {
+ ff_packing.push_back(std::make_tuple(ff1, slice, 1));
+ tile_ffs.push_back(ff1);
+ packed_cells.insert(ff1->name);
+ }
+ }
+ }
+ }
+
+ for (auto ff : ff_packing)
+ ff_to_slice(ctx, std::get<0>(ff), std::get<1>(ff), std::get<2>(ff), true);
+
+ // Setup placement constraints
+ ram0_slice->constr_abs_z = true;
+ ram0_slice->constr_z = 0;
+
+ ram1_slice->constr_parent = ram0_slice.get();
+ ram1_slice->constr_abs_z = true;
+ ram1_slice->constr_x = 0;
+ ram1_slice->constr_y = 0;
+ ram1_slice->constr_z = 1;
+ ram0_slice->constr_children.push_back(ram1_slice.get());
+
+ ramw_slice->constr_parent = ram0_slice.get();
+ ramw_slice->constr_abs_z = true;
+ ramw_slice->constr_x = 0;
+ ramw_slice->constr_y = 0;
+ ramw_slice->constr_z = 2;
+ ram0_slice->constr_children.push_back(ramw_slice.get());
+
+ new_cells.push_back(std::move(ram0_slice));
+ new_cells.push_back(std::move(ram1_slice));
+ new_cells.push_back(std::move(ramw_slice));
+ packed_cells.insert(ci->name);
+ }
+ }
+ flush_cells();
+ }
+
// Pack LUTs that have been paired together
void pack_lut_pairs()
{
@@ -804,6 +914,7 @@ class Ecp5Packer
{
pack_io();
pack_constants();
+ pack_dram();
pack_carries();
find_lutff_pairs();
pack_lut5s();
@@ -811,6 +922,7 @@ class Ecp5Packer
pack_lut_pairs();
pack_remaining_luts();
pack_remaining_ffs();
+ ctx->check();
}
private: