Merge pull request #89 from YosysHQ/ecp5_bram

ECP5 BRAM support
This commit is contained in:
David Shah 2018-10-17 11:14:27 +01:00 committed by GitHub
commit b53a4862db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 619 additions and 10 deletions

View File

@ -237,6 +237,12 @@ class ConstraintLegaliseWorker
return false;
}
}
// Don't place at tiles where any strongly bound Bels exist, as we might need to rip them up later
for (auto tilebel : ctx->getBelsByTile(loc.x, loc.y)) {
CellInfo *tcell = ctx->getBoundBelCell(tilebel);
if (tcell && tcell->belStrength >= STRENGTH_STRONG)
return false;
}
usedLocations.insert(loc);
for (auto child : cell->constr_children) {
IncreasingDiameterSearch xSearch, ySearch, zSearch;

View File

@ -19,6 +19,7 @@
*/
#include <algorithm>
#include <boost/range/adaptor/reversed.hpp>
#include <cmath>
#include <cstring>
#include "gfx.h"
@ -76,11 +77,13 @@ Arch::Arch(ArchArgs args) : args(args)
log_error("Unsupported ECP5 chip type.\n");
}
#else
if (args.type == ArchArgs::LFE5U_25F) {
if (args.type == ArchArgs::LFE5U_25F || args.type == ArchArgs::LFE5UM_25F || args.type == ArchArgs::LFE5UM5G_25F) {
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_25k));
} else if (args.type == ArchArgs::LFE5U_45F) {
} else if (args.type == ArchArgs::LFE5U_45F || args.type == ArchArgs::LFE5UM_45F ||
args.type == ArchArgs::LFE5UM5G_45F) {
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_45k));
} else if (args.type == ArchArgs::LFE5U_85F) {
} else if (args.type == ArchArgs::LFE5U_85F || args.type == ArchArgs::LFE5UM_85F ||
args.type == ArchArgs::LFE5UM5G_85F) {
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_85k));
} else {
log_error("Unsupported ECP5 chip type.\n");
@ -110,6 +113,18 @@ std::string Arch::getChipName() const
return "LFE5U-45F";
} else if (args.type == ArchArgs::LFE5U_85F) {
return "LFE5U-85F";
} else if (args.type == ArchArgs::LFE5UM_25F) {
return "LFE5UM-25F";
} else if (args.type == ArchArgs::LFE5UM_45F) {
return "LFE5UM-45F";
} else if (args.type == ArchArgs::LFE5UM_85F) {
return "LFE5UM-85F";
} else if (args.type == ArchArgs::LFE5UM5G_25F) {
return "LFE5UM5G-25F";
} else if (args.type == ArchArgs::LFE5UM5G_45F) {
return "LFE5UM5G-45F";
} else if (args.type == ArchArgs::LFE5UM5G_85F) {
return "LFE5UM5G-85F";
} else {
log_error("Unknown chip\n");
}
@ -125,6 +140,18 @@ IdString Arch::archArgsToId(ArchArgs args) const
return id("lfe5u_45f");
if (args.type == ArchArgs::LFE5U_85F)
return id("lfe5u_85f");
if (args.type == ArchArgs::LFE5UM_25F)
return id("lfe5um_25f");
if (args.type == ArchArgs::LFE5UM_45F)
return id("lfe5um_45f");
if (args.type == ArchArgs::LFE5UM_85F)
return id("lfe5um_85f");
if (args.type == ArchArgs::LFE5UM5G_25F)
return id("lfe5um5g_25f");
if (args.type == ArchArgs::LFE5UM5G_45F)
return id("lfe5um5g_45f");
if (args.type == ArchArgs::LFE5UM5G_85F)
return id("lfe5um5g_85f");
return IdString();
}
@ -531,6 +558,19 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort
return true;
}
return false;
} else if (cell->type == id_DP16KD) {
if (fromPort == id_CLKA) {
if (toPort.str(this).substr(0, 3) == "DOA") {
delay.delay = 4260;
return true;
}
} else if (fromPort == id_CLKB) {
if (toPort.str(this).substr(0, 3) == "DOB") {
delay.delay = 4280;
return true;
}
}
return false;
} else {
return false;
}
@ -582,6 +622,22 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id
if (port == id_CLKO)
return TMG_COMB_OUTPUT;
return TMG_IGNORE;
} else if (cell->type == id_DP16KD) {
if (port == id_CLKA || port == id_CLKB)
return TMG_CLOCK_INPUT;
std::string port_name = port.str(this);
for (auto c : boost::adaptors::reverse(port_name)) {
if (std::isdigit(c))
continue;
if (c == 'A')
clockPort = id_CLKA;
else if (c == 'B')
clockPort = id_CLKB;
else
NPNR_ASSERT_FALSE_STR("bad ram port");
return (cell->ports.at(port).type == PORT_OUT) ? TMG_REGISTER_OUTPUT : TMG_REGISTER_INPUT;
}
NPNR_ASSERT_FALSE_STR("no timing type for RAM port '" + port.str(this) + "'");
} else {
NPNR_ASSERT_FALSE_STR("no timing data for cell type '" + cell->type.str(this) + "'");
}

View File

@ -392,6 +392,12 @@ struct ArchArgs
LFE5U_25F,
LFE5U_45F,
LFE5U_85F,
LFE5UM_25F,
LFE5UM_45F,
LFE5UM_85F,
LFE5UM5G_25F,
LFE5UM5G_45F,
LFE5UM5G_85F,
} type = NONE;
std::string package;
int speed = 6;

View File

@ -20,6 +20,8 @@
#include "bitstream.h"
#include <fstream>
#include <iomanip>
#include <regex>
#include <streambuf>
#include "config.h"
@ -61,6 +63,101 @@ static std::vector<bool> int_to_bitvector(int val, int size)
return bv;
}
static std::vector<bool> str_to_bitvector(std::string str, int size)
{
std::vector<bool> bv;
bv.resize(size, 0);
if (str.substr(0, 2) != "0b")
log_error("error parsing value '%s', expected 0b prefix\n", str.c_str());
for (int i = 0; i < int(str.size()) - 2; i++) {
char c = str.at((str.size() - i) - 1);
NPNR_ASSERT(c == '0' || c == '1');
bv.at(i) = (c == '1');
}
return bv;
}
// Tie a wire using the CIB ties
static void tie_cib_signal(Context *ctx, ChipConfig &cc, WireId wire, bool value)
{
static const std::regex cib_re("J([A-D]|CE|LSR|CLK)[0-7]");
WireId cibsig = wire;
std::string basename = ctx->getWireBasename(wire).str(ctx);
while (!std::regex_match(basename, cib_re)) {
auto uphill = ctx->getPipsUphill(cibsig);
NPNR_ASSERT(uphill.begin() != uphill.end()); // At least one uphill pip
auto iter = uphill.begin();
cibsig = ctx->getPipSrcWire(*iter);
basename = ctx->getWireBasename(cibsig).str(ctx);
++iter;
NPNR_ASSERT(!(iter != uphill.end())); // Exactly one uphill pip
}
bool out_value = value;
if (basename.substr(0, 3) == "JCE")
NPNR_ASSERT(value);
if (basename.substr(0, 4) == "JCLK" || basename.substr(0, 4) == "JLSR") {
NPNR_ASSERT(value);
out_value = 0;
}
for (const auto &tile : ctx->getTilesAtLocation(cibsig.location.y, cibsig.location.x)) {
if (tile.second.substr(0, 3) == "CIB" || tile.second.substr(0, 4) == "VCIB") {
cc.tiles[tile.first].add_enum("CIB." + basename + "MUX", out_value ? "1" : "0");
return;
}
}
NPNR_ASSERT_FALSE("CIB tile not found at location");
}
inline int chtohex(char c)
{
static const std::string hex = "0123456789ABCDEF";
return hex.find(c);
}
std::vector<bool> parse_init_str(const std::string &str, int length)
{
// Parse a string that may be binary or hex
std::vector<bool> result;
result.resize(length, false);
if (str.substr(0, 2) == "0x") {
// Lattice style hex string
if (int(str.length()) > (2 + ((length + 3) / 4)))
log_error("hex string value too long, expected up to %d chars and found %d.\n", (2 + ((length + 3) / 4)),
int(str.length()));
for (int i = 0; i < int(str.length()) - 2; i++) {
char c = str.at((str.size() - i) - 1);
int nibble = chtohex(c);
result.at(i * 4) = nibble & 0x1;
result.at(i * 4 + 1) = nibble & 0x2;
result.at(i * 4 + 2) = nibble & 0x4;
result.at(i * 4 + 3) = nibble & 0x8;
}
} else {
// Yosys style binary string
if (int(str.length()) > length)
log_error("hex string value too long, expected up to %d bits and found %d.\n", length, int(str.length()));
for (int i = 0; i < int(str.length()); i++) {
char c = str.at((str.size() - i) - 1);
NPNR_ASSERT(c == '0' || c == '1' || c == 'X' || c == 'x');
result.at(i) = (c == '1');
}
}
return result;
}
inline uint16_t bit_reverse(uint16_t x, int size)
{
uint16_t y = 0;
for (int i = 0; i < size; i++)
if (x & (1 << i))
y |= (1 << ((size - 1) - i));
return y;
}
// Get the PIO tile corresponding to a PIO bel
static std::string get_pio_tile(Context *ctx, BelId bel)
{
@ -144,6 +241,77 @@ static std::string get_pic_tile(Context *ctx, BelId bel)
}
}
// Get the list of tiles corresponding to a blockram
std::vector<std::string> get_bram_tiles(Context *ctx, BelId bel)
{
std::vector<std::string> tiles;
Loc loc = ctx->getBelLocation(bel);
static const std::set<std::string> ebr0 = {"MIB_EBR0", "EBR_CMUX_UR", "EBR_CMUX_LR", "EBR_CMUX_LR_25K"};
static const std::set<std::string> ebr8 = {"MIB_EBR8", "EBR_SPINE_UL1", "EBR_SPINE_UR1", "EBR_SPINE_LL1",
"EBR_CMUX_UL", "EBR_SPINE_LL0", "EBR_CMUX_LL", "EBR_SPINE_LR0",
"EBR_SPINE_LR1", "EBR_CMUX_LL_25K", "EBR_SPINE_UL2", "EBR_SPINE_UL0",
"EBR_SPINE_UR2", "EBR_SPINE_LL2", "EBR_SPINE_LR2", "EBR_SPINE_UR0"};
switch (loc.z) {
case 0:
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x, ebr0));
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + 1, "MIB_EBR1"));
break;
case 1:
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x, "MIB_EBR2"));
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + 1, "MIB_EBR3"));
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + 2, "MIB_EBR4"));
break;
case 2:
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x, "MIB_EBR4"));
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + 1, "MIB_EBR5"));
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + 2, "MIB_EBR6"));
break;
case 3:
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x, "MIB_EBR6"));
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + 1, "MIB_EBR7"));
tiles.push_back(ctx->getTileByTypeAndLocation(loc.y, loc.x + 2, ebr8));
break;
default:
NPNR_ASSERT_FALSE("bad EBR z loc");
}
return tiles;
}
void fix_tile_names(Context *ctx, ChipConfig &cc)
{
// Remove the V prefix/suffix on certain tiles if device is a SERDES variant
if (ctx->args.type == ArchArgs::LFE5UM_25F || ctx->args.type == ArchArgs::LFE5UM_45F ||
ctx->args.type == ArchArgs::LFE5UM_85F || ctx->args.type == ArchArgs::LFE5UM5G_25F ||
ctx->args.type == ArchArgs::LFE5UM5G_45F || ctx->args.type == ArchArgs::LFE5UM5G_85F) {
std::map<std::string, std::string> tiletype_xform;
for (const auto &tile : cc.tiles) {
std::string newname = tile.first;
auto vcib = tile.first.find("VCIB");
if (vcib != std::string::npos) {
// Remove the V
newname.erase(vcib);
tiletype_xform[tile.first] = newname;
} else if (tile.first.back() == 'V') {
// BMID_0V or BMID_2V
if (tile.first.at(tile.first.size() - 2) == '0') {
newname.at(tile.first.size() - 1) = 'H';
tiletype_xform[tile.first] = newname;
} else if (tile.first.at(tile.first.size() - 2) == '2') {
newname.pop_back();
tiletype_xform[tile.first] = newname;
}
}
}
// Apply the name changes
for (auto xform : tiletype_xform) {
cc.tiles[xform.second] = cc.tiles.at(xform.first);
cc.tiles.erase(xform.first);
}
}
}
void write_bitstream(Context *ctx, std::string base_config_file, std::string text_config_file)
{
ChipConfig cc;
@ -289,11 +457,8 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
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);
}
@ -344,11 +509,120 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
}
} else if (ci->type == ctx->id("DCCA")) {
// Nothing to do
} else if (ci->type == ctx->id("DP16KD")) {
TileGroup tg;
Loc loc = ctx->getBelLocation(ci->bel);
tg.tiles = get_bram_tiles(ctx, ci->bel);
std::string ebr = "EBR" + std::to_string(loc.z);
tg.config.add_enum(ebr + ".MODE", "DP16KD");
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);
tg.config.add_enum(ebr + ".DP16KD.DATA_WIDTH_A", str_or_default(ci->params, ctx->id("DATA_WIDTH_A"), "18"));
tg.config.add_enum(ebr + ".DP16KD.DATA_WIDTH_B", str_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_B", str_or_default(ci->params, ctx->id("REGMODE_B"), "NOREG"));
tg.config.add_enum(ebr + ".RESETMODE", str_or_default(ci->params, ctx->id("RESETMODE"), "SYNC"));
tg.config.add_enum(ebr + ".ASYNC_RESET_RELEASE",
str_or_default(ci->params, ctx->id("ASYNC_RESET_RELEASE"), "SYNC"));
tg.config.add_enum(ebr + ".GSR", str_or_default(ci->params, ctx->id("GSR"), "DISABLED"));
tg.config.add_word(ebr + ".WID",
int_to_bitvector(bit_reverse(int_or_default(ci->attrs, ctx->id("WID"), 0), 9), 9));
// Tie signals as appropriate
for (auto port : ci->ports) {
if (port.second.net == nullptr && port.second.type == PORT_IN) {
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) {
// CIB clock or LSR. Tie to "1" (also 0 in prjtrellis db?) in CIB
// If MUX doesn't exist, set to INV to emulate default 0
tie_cib_signal(ctx, cc, ctx->getBelPinWire(ci->bel, port.first), true);
if (!ci->params.count(ctx->id(port.first.str(ctx) + "MUX")))
ci->params[ctx->id(port.first.str(ctx) + "MUX")] = "INV";
} else if (port.first == id_CEA || port.first == id_CEB || port.first == id_OCEA ||
port.first == id_OCEB) {
// CIB CE. Tie to "1" in CIB
// If MUX doesn't exist, set to passthru to emulate default 1
tie_cib_signal(ctx, cc, ctx->getBelPinWire(ci->bel, port.first), true);
if (!ci->params.count(ctx->id(port.first.str(ctx) + "MUX")))
ci->params[ctx->id(port.first.str(ctx) + "MUX")] = port.first.str(ctx);
} else if (port.first == id_CSA0 || port.first == id_CSA1 || port.first == id_CSA2 ||
port.first == id_CSB0 || port.first == id_CSB1 || port.first == id_CSB2) {
// CIB CE. Tie to "1" in CIB.
// If MUX doesn't exist, set to INV to emulate default 0
tie_cib_signal(ctx, cc, ctx->getBelPinWire(ci->bel, port.first), true);
if (!ci->params.count(ctx->id(port.first.str(ctx) + "MUX")))
ci->params[ctx->id(port.first.str(ctx) + "MUX")] = "INV";
} else {
// CIB ABCD signal
// Tie signals low unless explicit MUX param specified
bool value = bool_or_default(ci->params, ctx->id(port.first.str(ctx) + "MUX"), false);
tie_cib_signal(ctx, cc, ctx->getBelPinWire(ci->bel, port.first), value);
}
}
}
// 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 (int bit = 0; bit < 3; bit++) {
std::string sig = port.first + std::to_string(bit);
if (str_or_default(ci->params, ctx->id(sig + "MUX"), sig) == "INV")
port.second.at(bit) = !port.second.at(bit);
}
}
tg.config.add_enum(ebr + ".CLKAMUX", str_or_default(ci->params, ctx->id("CLKAMUX"), "CLKA"));
tg.config.add_enum(ebr + ".CLKBMUX", str_or_default(ci->params, ctx->id("CLKBMUX"), "CLKB"));
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 + ".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 + ".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 + ".OCEBMUX", str_or_default(ci->params, ctx->id("OCEBMUX"), "OCEB"));
tg.config.add_word(ebr + ".CSDECODE_A", csd_a);
tg.config.add_word(ebr + ".CSDECODE_B", csd_b);
std::vector<uint16_t> init_data;
init_data.resize(2048, 0x0);
// INIT_00 .. INIT_3F
for (int i = 0; i <= 0x3F; i++) {
IdString param = ctx->id("INITVAL_" +
fmt_str(std::hex << std::uppercase << std::setw(2) << std::setfill('0') << i));
auto value = parse_init_str(str_or_default(ci->params, param, "0"), 320);
for (int j = 0; j < 16; j++) {
// INIT parameter consists of 16 18-bit words with 2-bit padding
int ofs = 20 * j;
for (int k = 0; k < 18; k++) {
if (value.at(ofs + k))
init_data.at(i * 32 + j * 2 + (k / 9)) |= (1 << (k % 9));
}
}
}
int wid = int_or_default(ci->attrs, ctx->id("WID"), 0);
NPNR_ASSERT(!cc.bram_data.count(wid));
cc.bram_data[wid] = init_data;
cc.tilegroups.push_back(tg);
} else {
NPNR_ASSERT_FALSE("unsupported cell type");
}
}
// Fixup tile names
fix_tile_names(ctx, cc);
// Configure chip
if (!text_config_file.empty()) {
std::ofstream out_config(text_config_file);

View File

@ -19,6 +19,7 @@
#include "config.h"
#include <boost/range/adaptor/reversed.hpp>
#include <iomanip>
#include "log.h"
NEXTPNR_NAMESPACE_BEGIN
@ -274,6 +275,28 @@ std::ostream &operator<<(std::ostream &out, const ChipConfig &cc)
out << std::endl;
}
}
for (const auto &bram : cc.bram_data) {
out << ".bram_init " << bram.first << std::endl;
std::ios_base::fmtflags f(out.flags());
for (size_t i = 0; i < bram.second.size(); i++) {
out << std::setw(3) << std::setfill('0') << std::hex << bram.second.at(i);
if (i % 8 == 7)
out << std::endl;
else
out << " ";
}
out.flags(f);
out << std::endl;
}
for (const auto &tg : cc.tilegroups) {
out << ".tile_group";
for (const auto &tile : tg.tiles) {
out << " " << tile;
}
out << std::endl;
out << tg.config;
out << std::endl;
}
return out;
}
@ -294,6 +317,29 @@ std::istream &operator>>(std::istream &in, ChipConfig &cc)
TileConfig tc;
in >> tc;
cc.tiles[tilename] = tc;
} else if (verb == ".tile_group") {
TileGroup tg;
std::string line;
getline(in, line);
std::stringstream ss2(line);
std::string tile;
while (ss2) {
ss2 >> tile;
tg.tiles.push_back(tile);
}
in >> tg.config;
cc.tilegroups.push_back(tg);
} else if (verb == ".bram_init") {
uint16_t bram;
in >> bram;
std::ios_base::fmtflags f(in.flags());
while (!skip_check_eor(in)) {
uint16_t value;
in >> std::hex >> value;
cc.bram_data[bram].push_back(value);
}
in.flags(f);
} else {
log_error("unrecognised config entry %s\n", verb.c_str());
}

View File

@ -98,6 +98,14 @@ std::ostream &operator<<(std::ostream &out, const TileConfig &tc);
std::istream &operator>>(std::istream &in, TileConfig &ce);
// A group of tiles to configure at once for a particular feature that is split across tiles
// TileGroups are currently for non-routing configuration only
struct TileGroup
{
std::vector<std::string> tiles;
TileConfig config;
};
// This represents the configuration of a chip at a high level
class ChipConfig
{
@ -105,6 +113,8 @@ class ChipConfig
std::string chip_name;
std::vector<std::string> metadata;
std::map<std::string, TileConfig> tiles;
std::vector<TileGroup> tilegroups;
std::map<uint16_t, std::vector<uint16_t>> bram_data;
};
std::ostream &operator<<(std::ostream &out, const ChipConfig &cc);

View File

@ -54,3 +54,121 @@ X(SRMODE)
X(CLKI)
X(CLKO)
X(DP16KD)
X(DIA0)
X(DIA1)
X(DIA2)
X(DIA3)
X(DIA4)
X(DIA5)
X(DIA6)
X(DIA7)
X(DIA8)
X(DIA9)
X(DIA10)
X(DIA11)
X(DIA12)
X(DIA13)
X(DIA14)
X(DIA15)
X(DIA16)
X(DIA17)
X(ADA0)
X(ADA1)
X(ADA2)
X(ADA3)
X(ADA4)
X(ADA5)
X(ADA6)
X(ADA7)
X(ADA8)
X(ADA9)
X(ADA10)
X(ADA11)
X(ADA12)
X(ADA13)
X(CEA)
X(OCEA)
X(CLKA)
X(WEA)
X(CSA2)
X(CSA1)
X(CSA0)
X(RSTA)
X(DIB0)
X(DIB1)
X(DIB2)
X(DIB3)
X(DIB4)
X(DIB5)
X(DIB6)
X(DIB7)
X(DIB8)
X(DIB9)
X(DIB10)
X(DIB11)
X(DIB12)
X(DIB13)
X(DIB14)
X(DIB15)
X(DIB16)
X(DIB17)
X(ADB0)
X(ADB1)
X(ADB2)
X(ADB3)
X(ADB4)
X(ADB5)
X(ADB6)
X(ADB7)
X(ADB8)
X(ADB9)
X(ADB10)
X(ADB11)
X(ADB12)
X(ADB13)
X(CEB)
X(OCEB)
X(CLKB)
X(WEB)
X(CSB2)
X(CSB1)
X(CSB0)
X(RSTB)
X(DOA0)
X(DOA1)
X(DOA2)
X(DOA3)
X(DOA4)
X(DOA5)
X(DOA6)
X(DOA7)
X(DOA8)
X(DOA9)
X(DOA10)
X(DOA11)
X(DOA12)
X(DOA13)
X(DOA14)
X(DOA15)
X(DOA16)
X(DOA17)
X(DOB0)
X(DOB1)
X(DOB2)
X(DOB3)
X(DOB4)
X(DOB5)
X(DOB6)
X(DOB7)
X(DOB8)
X(DOB9)
X(DOB10)
X(DOB11)
X(DOB12)
X(DOB13)
X(DOB14)
X(DOB15)
X(DOB16)
X(DOB17)

View File

@ -24,7 +24,7 @@
#include "cells.h"
#include "log.h"
#include "nextpnr.h"
#include "place_common.h"
#define fmt_str(x) (static_cast<const std::ostringstream &>(std::ostringstream() << x).str())
NEXTPNR_NAMESPACE_BEGIN
@ -260,15 +260,24 @@ class Ecp5GlobalRouter
// Attempt to place a DCC
void place_dcc(CellInfo *dcc)
{
BelId best_bel;
wirelen_t best_wirelen = 9999999;
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) == id_DCCA && ctx->checkBelAvail(bel)) {
if (ctx->isValidBelForCell(dcc, bel)) {
ctx->bindBel(bel, dcc, STRENGTH_LOCKED);
return;
float tns;
wirelen_t wirelen = get_net_metric(ctx, dcc->ports.at(id_CLKI).net, MetricType::WIRELENGTH, tns);
if (wirelen < best_wirelen) {
best_bel = bel;
best_wirelen = wirelen;
}
ctx->unbindBel(bel);
}
}
}
NPNR_ASSERT_FALSE("failed to place dcca");
NPNR_ASSERT(best_bel != BelId());
ctx->bindBel(best_bel, dcc, STRENGTH_LOCKED);
}
// Insert a DCC into a net to promote it to a global

View File

@ -50,6 +50,12 @@ po::options_description ECP5CommandHandler::getArchOptions()
specific.add_options()("25k", "set device type to LFE5U-25F");
specific.add_options()("45k", "set device type to LFE5U-45F");
specific.add_options()("85k", "set device type to LFE5U-85F");
specific.add_options()("um-25k", "set device type to LFE5UM-25F");
specific.add_options()("um-45k", "set device type to LFE5UM-45F");
specific.add_options()("um-85k", "set device type to LFE5UM-85F");
specific.add_options()("um5g-25k", "set device type to LFE5UM5G-25F");
specific.add_options()("um5g-45k", "set device type to LFE5UM5G-45F");
specific.add_options()("um5g-85k", "set device type to LFE5UM5G-85F");
specific.add_options()("package", po::value<std::string>(), "select device package (defaults to CABGA381)");
specific.add_options()("basecfg", po::value<std::string>(), "base chip configuration in Trellis text format");
specific.add_options()("textcfg", po::value<std::string>(), "textual configuration in Trellis format to write");
@ -84,6 +90,18 @@ std::unique_ptr<Context> ECP5CommandHandler::createContext()
chipArgs.type = ArchArgs::LFE5U_45F;
if (vm.count("85k"))
chipArgs.type = ArchArgs::LFE5U_85F;
if (vm.count("um-25k"))
chipArgs.type = ArchArgs::LFE5UM_25F;
if (vm.count("um-45k"))
chipArgs.type = ArchArgs::LFE5UM_45F;
if (vm.count("um-85k"))
chipArgs.type = ArchArgs::LFE5UM_85F;
if (vm.count("um5g-25k"))
chipArgs.type = ArchArgs::LFE5UM5G_25F;
if (vm.count("um5g-45k"))
chipArgs.type = ArchArgs::LFE5UM5G_45F;
if (vm.count("um5g-85k"))
chipArgs.type = ArchArgs::LFE5UM5G_85F;
if (vm.count("package"))
chipArgs.package = vm["package"].as<std::string>();
else

View File

@ -844,6 +844,19 @@ class Ecp5Packer
((!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 if (uc->type == id_DP16KD) {
if (user.port == id_CLKA || user.port == id_CLKB || user.port == id_RSTA || user.port == id_RSTB ||
user.port == id_WEA || user.port == id_WEB || user.port == id_CEA || user.port == id_CEB ||
user.port == id_OCEA || user.port == id_OCEB || user.port == id_CSA0 || user.port == id_CSA1 ||
user.port == id_CSA2 || user.port == id_CSB0 || user.port == id_CSB1 || user.port == id_CSB2) {
// Connect to CIB CLK, LSR or CE. Default state is 1
uc->params[ctx->id(user.port.str(ctx) + "MUX")] = constval ? user.port.str(ctx) : "INV";
} else {
// Connected to CIB ABCD. Default state is bitstream configurable
uc->params[ctx->id(user.port.str(ctx) + "MUX")] = constval ? "1" : "0";
}
uc->ports[user.port].net = nullptr;
} else {
uc->ports[user.port].net = constnet;
constnet->users.push_back(user);
@ -909,10 +922,63 @@ class Ecp5Packer
}
}
void autocreate_empty_port(CellInfo *cell, IdString port)
{
if (!cell->ports.count(port)) {
cell->ports[port].name = port;
cell->ports[port].net = nullptr;
cell->ports[port].type = PORT_IN;
}
}
// Pack EBR
void pack_ebr()
{
// Autoincrement WID (starting from 3 seems to match vendor behaviour?)
int wid = 3;
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ci->type == id_DP16KD) {
// Add ports, even if disconnected, to ensure correct tie-offs
for (int i = 0; i < 14; i++) {
autocreate_empty_port(ci, ctx->id("ADA" + std::to_string(i)));
autocreate_empty_port(ci, ctx->id("ADB" + std::to_string(i)));
}
for (int i = 0; i < 18; i++) {
autocreate_empty_port(ci, ctx->id("DIA" + std::to_string(i)));
autocreate_empty_port(ci, ctx->id("DIB" + std::to_string(i)));
}
for (int i = 0; i < 3; i++) {
autocreate_empty_port(ci, ctx->id("CSA" + std::to_string(i)));
autocreate_empty_port(ci, ctx->id("CSB" + std::to_string(i)));
}
for (int i = 0; i < 3; i++) {
autocreate_empty_port(ci, ctx->id("CSA" + std::to_string(i)));
autocreate_empty_port(ci, ctx->id("CSB" + std::to_string(i)));
}
autocreate_empty_port(ci, id_CLKA);
autocreate_empty_port(ci, id_CEA);
autocreate_empty_port(ci, id_OCEA);
autocreate_empty_port(ci, id_WEA);
autocreate_empty_port(ci, id_RSTA);
autocreate_empty_port(ci, id_CLKB);
autocreate_empty_port(ci, id_CEB);
autocreate_empty_port(ci, id_OCEB);
autocreate_empty_port(ci, id_WEB);
autocreate_empty_port(ci, id_RSTB);
ci->attrs[ctx->id("WID")] = std::to_string(wid++);
}
}
}
public:
void pack()
{
pack_io();
pack_ebr();
pack_constants();
pack_dram();
pack_carries();