ecp5: Adding support for 36-bit wide PDP RAMs
Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
parent
cb8d90bcbf
commit
d04e5954a6
21
ecp5/arch.cc
21
ecp5/arch.cc
@ -922,28 +922,41 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
|
|||||||
}
|
}
|
||||||
} else if (cell->type == id_DP16KD) {
|
} else if (cell->type == id_DP16KD) {
|
||||||
std::string port_name = port.str(this);
|
std::string port_name = port.str(this);
|
||||||
|
IdString half_clock;
|
||||||
for (auto c : boost::adaptors::reverse(port_name)) {
|
for (auto c : boost::adaptors::reverse(port_name)) {
|
||||||
if (std::isdigit(c))
|
if (std::isdigit(c))
|
||||||
continue;
|
continue;
|
||||||
if (c == 'A') {
|
if (c == 'A') {
|
||||||
info.clock_port = id_CLKA;
|
half_clock = id_CLKA;
|
||||||
break;
|
break;
|
||||||
} else if (c == 'B') {
|
} else if (c == 'B') {
|
||||||
info.clock_port = id_CLKB;
|
half_clock = id_CLKB;
|
||||||
break;
|
break;
|
||||||
} else
|
} else
|
||||||
NPNR_ASSERT_FALSE_STR("bad ram port " + port.str(this));
|
NPNR_ASSERT_FALSE_STR("bad ram port " + port.str(this));
|
||||||
}
|
}
|
||||||
|
if (cell->ramInfo.is_pdp) {
|
||||||
|
bool is_output = cell->ports.at(port).type == PORT_OUT;
|
||||||
|
// In PDP mode, all read signals are in CLKB domain and write signals in CLKA domain
|
||||||
|
if (is_output || port == id_OCEB || port == id_CEB || port == id_ADB5 || port == id_ADB6 ||
|
||||||
|
port == id_ADB7 || port == id_ADB8 || port == id_ADB9 || port == id_ADB10 || port == id_ADB11 ||
|
||||||
|
port == id_ADB12 || port == id_ADB13)
|
||||||
|
info.clock_port = id_CLKB;
|
||||||
|
else
|
||||||
|
info.clock_port = id_CLKA;
|
||||||
|
} else {
|
||||||
|
info.clock_port = half_clock;
|
||||||
|
}
|
||||||
info.edge = (str_or_default(cell->params, info.clock_port == id_CLKB ? id("CLKBMUX") : id("CLKAMUX"), "CLK") ==
|
info.edge = (str_or_default(cell->params, info.clock_port == id_CLKB ? id("CLKBMUX") : id("CLKAMUX"), "CLK") ==
|
||||||
"INV")
|
"INV")
|
||||||
? FALLING_EDGE
|
? FALLING_EDGE
|
||||||
: RISING_EDGE;
|
: RISING_EDGE;
|
||||||
if (cell->ports.at(port).type == PORT_OUT) {
|
if (cell->ports.at(port).type == PORT_OUT) {
|
||||||
bool is_path = getDelayFromTimingDatabase(id_DP16KD_REGMODE_A_NOREG_REGMODE_B_NOREG, info.clock_port, port,
|
bool is_path = getDelayFromTimingDatabase(id_DP16KD_REGMODE_A_NOREG_REGMODE_B_NOREG, half_clock, port,
|
||||||
info.clockToQ);
|
info.clockToQ);
|
||||||
NPNR_ASSERT(is_path);
|
NPNR_ASSERT(is_path);
|
||||||
} else {
|
} else {
|
||||||
getSetupHoldFromTimingDatabase(id_DP16KD_REGMODE_A_NOREG_REGMODE_B_NOREG, info.clock_port, port, info.setup,
|
getSetupHoldFromTimingDatabase(id_DP16KD_REGMODE_A_NOREG_REGMODE_B_NOREG, half_clock, port, info.setup,
|
||||||
info.hold);
|
info.hold);
|
||||||
}
|
}
|
||||||
} else if (cell->type == id_DCUA) {
|
} else if (cell->type == id_DCUA) {
|
||||||
|
@ -163,6 +163,10 @@ struct ArchCellInfo
|
|||||||
IdString clk_sig, lsr_sig, clkmux, lsrmux, srmode;
|
IdString clk_sig, lsr_sig, clkmux, lsrmux, srmode;
|
||||||
int sd0, sd1;
|
int sd0, sd1;
|
||||||
} sliceInfo;
|
} sliceInfo;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
bool is_pdp;
|
||||||
|
} ramInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -927,21 +927,25 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
|
|||||||
tg.tiles = get_bram_tiles(ctx, ci->bel);
|
tg.tiles = get_bram_tiles(ctx, ci->bel);
|
||||||
std::string ebr = "EBR" + std::to_string(loc.z);
|
std::string ebr = "EBR" + std::to_string(loc.z);
|
||||||
|
|
||||||
tg.config.add_enum(ebr + ".MODE", "DP16KD");
|
if (ci->ramInfo.is_pdp) {
|
||||||
|
tg.config.add_enum(ebr + ".MODE", "PDPW16KD");
|
||||||
|
tg.config.add_enum(ebr + ".PDPW16KD.DATA_WIDTH_R",
|
||||||
|
intstr_or_default(ci->params, ctx->id("DATA_WIDTH_B"), "36"));
|
||||||
|
} else {
|
||||||
|
tg.config.add_enum(ebr + ".MODE", "DP16KD");
|
||||||
|
tg.config.add_enum(ebr + ".DP16KD.DATA_WIDTH_A",
|
||||||
|
intstr_or_default(ci->params, ctx->id("DATA_WIDTH_A"), "18"));
|
||||||
|
tg.config.add_enum(ebr + ".DP16KD.DATA_WIDTH_B",
|
||||||
|
intstr_or_default(ci->params, ctx->id("DATA_WIDTH_B"), "18"));
|
||||||
|
tg.config.add_enum(ebr + ".DP16KD.WRITEMODE_A",
|
||||||
|
str_or_default(ci->params, ctx->id("WRITEMODE_A"), "NORMAL"));
|
||||||
|
tg.config.add_enum(ebr + ".DP16KD.WRITEMODE_B",
|
||||||
|
str_or_default(ci->params, ctx->id("WRITEMODE_B"), "NORMAL"));
|
||||||
|
}
|
||||||
|
|
||||||
auto csd_a = str_to_bitvector(str_or_default(ci->params, ctx->id("CSDECODE_A"), "0b000"), 3),
|
auto csd_a = str_to_bitvector(str_or_default(ci->params, ctx->id("CSDECODE_A"), "0b000"), 3),
|
||||||
csd_b = str_to_bitvector(str_or_default(ci->params, ctx->id("CSDECODE_B"), "0b000"), 3);
|
csd_b = str_to_bitvector(str_or_default(ci->params, ctx->id("CSDECODE_B"), "0b000"), 3);
|
||||||
|
|
||||||
tg.config.add_enum(ebr + ".DP16KD.DATA_WIDTH_A",
|
|
||||||
intstr_or_default(ci->params, ctx->id("DATA_WIDTH_A"), "18"));
|
|
||||||
tg.config.add_enum(ebr + ".DP16KD.DATA_WIDTH_B",
|
|
||||||
intstr_or_default(ci->params, ctx->id("DATA_WIDTH_B"), "18"));
|
|
||||||
|
|
||||||
tg.config.add_enum(ebr + ".DP16KD.WRITEMODE_A",
|
|
||||||
str_or_default(ci->params, ctx->id("WRITEMODE_A"), "NORMAL"));
|
|
||||||
tg.config.add_enum(ebr + ".DP16KD.WRITEMODE_B",
|
|
||||||
str_or_default(ci->params, ctx->id("WRITEMODE_B"), "NORMAL"));
|
|
||||||
|
|
||||||
tg.config.add_enum(ebr + ".REGMODE_A", str_or_default(ci->params, ctx->id("REGMODE_A"), "NOREG"));
|
tg.config.add_enum(ebr + ".REGMODE_A", str_or_default(ci->params, ctx->id("REGMODE_A"), "NOREG"));
|
||||||
tg.config.add_enum(ebr + ".REGMODE_B", str_or_default(ci->params, ctx->id("REGMODE_B"), "NOREG"));
|
tg.config.add_enum(ebr + ".REGMODE_B", str_or_default(ci->params, ctx->id("REGMODE_B"), "NOREG"));
|
||||||
|
|
||||||
@ -955,6 +959,8 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
|
|||||||
|
|
||||||
// Tie signals as appropriate
|
// Tie signals as appropriate
|
||||||
for (auto port : ci->ports) {
|
for (auto port : ci->ports) {
|
||||||
|
if (ci->ramInfo.is_pdp && (port.first == id_WEA || port.first == id_WEB || port.first == id_ADA4))
|
||||||
|
continue;
|
||||||
if (port.second.net == nullptr && port.second.type == PORT_IN) {
|
if (port.second.net == nullptr && port.second.type == PORT_IN) {
|
||||||
if (port.first == id_CLKA || port.first == id_CLKB || port.first == id_WEA ||
|
if (port.first == id_CLKA || port.first == id_CLKB || port.first == id_WEA ||
|
||||||
port.first == id_WEB || port.first == id_RSTA || port.first == id_RSTB) {
|
port.first == id_WEB || port.first == id_RSTA || port.first == id_RSTB) {
|
||||||
@ -987,7 +993,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Invert CSDECODE bits to emulate inversion muxes on CSA/CSB signals
|
// Invert CSDECODE bits to emulate inversion muxes on CSA/CSB signals
|
||||||
for (auto port : {std::make_pair("CSA", std::ref(csd_a)), std::make_pair("CSB", std::ref(csd_b))}) {
|
for (auto &port : {std::make_pair("CSA", std::ref(csd_a)), std::make_pair("CSB", std::ref(csd_b))}) {
|
||||||
for (int bit = 0; bit < 3; bit++) {
|
for (int bit = 0; bit < 3; bit++) {
|
||||||
std::string sig = port.first + std::to_string(bit);
|
std::string sig = port.first + std::to_string(bit);
|
||||||
if (str_or_default(ci->params, ctx->id(sig + "MUX"), sig) == "INV")
|
if (str_or_default(ci->params, ctx->id(sig + "MUX"), sig) == "INV")
|
||||||
@ -1000,9 +1006,10 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
|
|||||||
|
|
||||||
tg.config.add_enum(ebr + ".RSTAMUX", str_or_default(ci->params, ctx->id("RSTAMUX"), "RSTA"));
|
tg.config.add_enum(ebr + ".RSTAMUX", str_or_default(ci->params, ctx->id("RSTAMUX"), "RSTA"));
|
||||||
tg.config.add_enum(ebr + ".RSTBMUX", str_or_default(ci->params, ctx->id("RSTBMUX"), "RSTB"));
|
tg.config.add_enum(ebr + ".RSTBMUX", str_or_default(ci->params, ctx->id("RSTBMUX"), "RSTB"));
|
||||||
tg.config.add_enum(ebr + ".WEAMUX", str_or_default(ci->params, ctx->id("WEAMUX"), "WEA"));
|
if (!ci->ramInfo.is_pdp) {
|
||||||
tg.config.add_enum(ebr + ".WEBMUX", str_or_default(ci->params, ctx->id("WEBMUX"), "WEB"));
|
tg.config.add_enum(ebr + ".WEAMUX", str_or_default(ci->params, ctx->id("WEAMUX"), "WEA"));
|
||||||
|
tg.config.add_enum(ebr + ".WEBMUX", str_or_default(ci->params, ctx->id("WEBMUX"), "WEB"));
|
||||||
|
}
|
||||||
tg.config.add_enum(ebr + ".CEAMUX", str_or_default(ci->params, ctx->id("CEAMUX"), "CEA"));
|
tg.config.add_enum(ebr + ".CEAMUX", str_or_default(ci->params, ctx->id("CEAMUX"), "CEA"));
|
||||||
tg.config.add_enum(ebr + ".CEBMUX", str_or_default(ci->params, ctx->id("CEBMUX"), "CEB"));
|
tg.config.add_enum(ebr + ".CEBMUX", str_or_default(ci->params, ctx->id("CEBMUX"), "CEB"));
|
||||||
tg.config.add_enum(ebr + ".OCEAMUX", str_or_default(ci->params, ctx->id("OCEAMUX"), "OCEA"));
|
tg.config.add_enum(ebr + ".OCEAMUX", str_or_default(ci->params, ctx->id("OCEAMUX"), "OCEA"));
|
||||||
|
53
ecp5/pack.cc
53
ecp5/pack.cc
@ -1253,6 +1253,57 @@ class Ecp5Packer
|
|||||||
{
|
{
|
||||||
// Autoincrement WID (starting from 3 seems to match vendor behaviour?)
|
// Autoincrement WID (starting from 3 seems to match vendor behaviour?)
|
||||||
int wid = 3;
|
int wid = 3;
|
||||||
|
auto rename_bus = [&](CellInfo *c, const std::string &oldname, const std::string &newname, int width,
|
||||||
|
int oldoffset, int newoffset) {
|
||||||
|
for (int i = 0; i < width; i++)
|
||||||
|
rename_port(ctx, c, ctx->id(oldname + std::to_string(i + oldoffset)),
|
||||||
|
ctx->id(newname + std::to_string(i + newoffset)));
|
||||||
|
};
|
||||||
|
auto rename_param = [&](CellInfo *c, const std::string &oldname, const std::string &newname) {
|
||||||
|
IdString o = ctx->id(oldname), n = ctx->id(newname);
|
||||||
|
if (!c->params.count(o))
|
||||||
|
return;
|
||||||
|
c->params[n] = c->params[o];
|
||||||
|
c->params.erase(o);
|
||||||
|
};
|
||||||
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
|
CellInfo *ci = cell.second;
|
||||||
|
// Convert 36-bit PDP RAMs to regular 18-bit DP ones that match the Bel
|
||||||
|
if (ci->type == ctx->id("PDPW16KD")) {
|
||||||
|
ci->params[ctx->id("DATA_WIDTH_A")] = 36; // force PDP mode
|
||||||
|
ci->params.erase(ctx->id("DATA_WIDTH_W"));
|
||||||
|
rename_bus(ci, "BE", "ADA", 4, 0, 0);
|
||||||
|
rename_bus(ci, "ADW", "ADA", 9, 0, 5);
|
||||||
|
rename_bus(ci, "ADR", "ADB", 14, 0, 0);
|
||||||
|
rename_bus(ci, "CSW", "CSA", 3, 0, 0);
|
||||||
|
rename_bus(ci, "CSR", "CSB", 3, 0, 0);
|
||||||
|
rename_bus(ci, "DI", "DIA", 18, 0, 0);
|
||||||
|
rename_bus(ci, "DI", "DIB", 18, 18, 0);
|
||||||
|
rename_bus(ci, "DO", "DOA", 18, 18, 0);
|
||||||
|
rename_bus(ci, "DO", "DOB", 18, 0, 0);
|
||||||
|
rename_port(ctx, ci, ctx->id("CLKW"), ctx->id("CLKA"));
|
||||||
|
rename_port(ctx, ci, ctx->id("CLKR"), ctx->id("CLKB"));
|
||||||
|
rename_port(ctx, ci, ctx->id("CEW"), ctx->id("CEA"));
|
||||||
|
rename_port(ctx, ci, ctx->id("CER"), ctx->id("CEB"));
|
||||||
|
rename_port(ctx, ci, ctx->id("OCER"), ctx->id("OCEB"));
|
||||||
|
rename_param(ci, "CLKWMUX", "CLKAMUX");
|
||||||
|
rename_param(ci, "CLKRMUX", "CLKRMUX");
|
||||||
|
rename_param(ci, "CSDECODE_W", "CSDECODE_A");
|
||||||
|
rename_param(ci, "CSDECODE_R", "CSDECODE_B");
|
||||||
|
rename_param(ci, "REGMODE", "REGMODE_B");
|
||||||
|
rename_param(ci, "DATA_WIDTH_R", "DATA_WIDTH_B");
|
||||||
|
if (ci->ports.count(id_RST)) {
|
||||||
|
autocreate_empty_port(ci, id_RSTA);
|
||||||
|
autocreate_empty_port(ci, id_RSTB);
|
||||||
|
NetInfo *rst = ci->ports.at(id_RST).net;
|
||||||
|
connect_port(ctx, rst, ci, id_RSTA);
|
||||||
|
connect_port(ctx, rst, ci, id_RSTB);
|
||||||
|
disconnect_port(ctx, ci, id_RST);
|
||||||
|
ci->ports.erase(id_RST);
|
||||||
|
}
|
||||||
|
ci->type = id_DP16KD;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (auto cell : sorted(ctx->cells)) {
|
for (auto cell : sorted(ctx->cells)) {
|
||||||
CellInfo *ci = cell.second;
|
CellInfo *ci = cell.second;
|
||||||
if (ci->type == id_DP16KD) {
|
if (ci->type == id_DP16KD) {
|
||||||
@ -2516,6 +2567,8 @@ void Arch::assignArchInfo()
|
|||||||
if (ci->ports.count(id_FXA) && ci->ports[id_FXA].net != nullptr &&
|
if (ci->ports.count(id_FXA) && ci->ports[id_FXA].net != nullptr &&
|
||||||
ci->ports[id_FXA].net->driver.port == id_OFX0)
|
ci->ports[id_FXA].net->driver.port == id_OFX0)
|
||||||
ci->sliceInfo.has_l6mux = true;
|
ci->sliceInfo.has_l6mux = true;
|
||||||
|
} else if (ci->type == id_DP16KD) {
|
||||||
|
ci->ramInfo.is_pdp = (int_or_default(ci->params, id("DATA_WIDTH_A"), 0) == 36);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto net : sorted(nets)) {
|
for (auto net : sorted(nets)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user