ecp5: LUT permutation support

Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
gatecat 2021-12-13 16:04:45 +00:00
parent 8720d79f21
commit f36188f2e1
8 changed files with 118 additions and 8 deletions

View File

@ -48,7 +48,7 @@ RUN set -e -x ;\
cd /usr/local/src ;\ cd /usr/local/src ;\
git clone --recursive https://github.com/YosysHQ/prjtrellis.git ;\ git clone --recursive https://github.com/YosysHQ/prjtrellis.git ;\
cd prjtrellis ;\ cd prjtrellis ;\
git reset --hard 210a0a72757d57b278ac7397ae6b14729f149b10 ;\ git reset --hard 7239331d5463321d4864164f320beef67310f1e5 ;\
cd libtrellis ;\ cd libtrellis ;\
cmake . ;\ cmake . ;\
make -j $(nproc) ;\ make -j $(nproc) ;\

View File

@ -144,6 +144,8 @@ Arch::Arch(ArchArgs args) : args(args)
n_pips++; n_pips++;
} }
pip2net.resize(n_pips, nullptr); pip2net.resize(n_pips, nullptr);
lutperm_allowed.resize(chip_info->width * chip_info->height * 4);
} }
// ----------------------------------------------------------------------- // -----------------------------------------------------------------------
@ -625,6 +627,8 @@ bool Arch::route()
{ {
std::string router = str_or_default(settings, id("router"), defaultRouter); std::string router = str_or_default(settings, id("router"), defaultRouter);
disable_router_lutperm = getCtx()->setting<bool>("arch.disable_router_lutperm", false);
setup_wire_locations(); setup_wire_locations();
route_ecp5_globals(getCtx()); route_ecp5_globals(getCtx());
assignArchInfo(); assignArchInfo();

View File

@ -58,8 +58,15 @@ NPNR_PACKED_STRUCT(struct PipInfoPOD {
int16_t timing_class; int16_t timing_class;
int8_t tile_type; int8_t tile_type;
int8_t pip_type; int8_t pip_type;
int16_t lutperm_flags;
int16_t padding;
}); });
inline bool is_lutperm_pip(int16_t flags) { return flags & 0x4000; }
inline uint8_t lutperm_lut(int16_t flags) { return (flags >> 4) & 0x7; }
inline uint8_t lutperm_out(int16_t flags) { return (flags >> 2) & 0x3; }
inline uint8_t lutperm_in(int16_t flags) { return flags & 0x3; }
NPNR_PACKED_STRUCT(struct PipLocatorPOD { NPNR_PACKED_STRUCT(struct PipLocatorPOD {
LocationPOD rel_loc; LocationPOD rel_loc;
int32_t index; int32_t index;
@ -446,6 +453,14 @@ struct Arch : BaseArch<ArchRanges>
mutable dict<IdStringList, PipId> pip_by_name; mutable dict<IdStringList, PipId> pip_by_name;
std::vector<CellInfo *> bel_to_cell; std::vector<CellInfo *> bel_to_cell;
enum class LutPermRule
{
NONE,
CARRY,
ALL,
};
std::vector<LutPermRule> lutperm_allowed;
bool disable_router_lutperm = false;
// faster replacements for base_pip2net, base_wire2net // faster replacements for base_pip2net, base_wire2net
// indexed by get_pip_vecidx() // indexed by get_pip_vecidx()
@ -509,6 +524,12 @@ struct Arch : BaseArch<ArchRanges>
return (bel.location.y * chip_info->width + bel.location.x) * max_loc_bels + bel.index; return (bel.location.y * chip_info->width + bel.location.x) * max_loc_bels + bel.index;
} }
int get_slice_index(int x, int y, int slice) const
{
NPNR_ASSERT(slice >= 0 && slice < 4);
return (y * chip_info->width + x) * 4 + slice;
}
void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) override void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength) override
{ {
NPNR_ASSERT(bel != BelId()); NPNR_ASSERT(bel != BelId());
@ -517,6 +538,11 @@ struct Arch : BaseArch<ArchRanges>
bel_to_cell[idx] = cell; bel_to_cell[idx] = cell;
cell->bel = bel; cell->bel = bel;
cell->belStrength = strength; cell->belStrength = strength;
if (getBelType(bel) == id_TRELLIS_SLICE) {
lutperm_allowed.at(get_slice_index(bel.location.x, bel.location.y, getBelLocation(bel).z)) =
(cell->sliceInfo.is_memory ? LutPermRule::NONE
: (cell->sliceInfo.is_carry ? LutPermRule::CARRY : LutPermRule::ALL));
}
refreshUiBel(bel); refreshUiBel(bel);
} }
@ -744,11 +770,31 @@ struct Arch : BaseArch<ArchRanges>
p2n_entry->wires.erase(dst); p2n_entry->wires.erase(dst);
p2n_entry = nullptr; p2n_entry = nullptr;
} }
bool checkPipAvail(PipId pip) const override { return getBoundPipNet(pip) == nullptr; } bool is_pip_blocked(PipId pip) const
{
auto &pip_data = loc_info(pip)->pip_data[pip.index];
int lp = pip_data.lutperm_flags;
if (is_lutperm_pip(lp)) {
if (disable_router_lutperm)
return true;
auto rule = lutperm_allowed.at(get_slice_index(pip.location.x, pip.location.y, lutperm_lut(lp) / 2));
if (rule == LutPermRule::NONE) {
// Permutation not allowed
return true;
} else if (rule == LutPermRule::CARRY) {
// Can swap A/B and C/D only
int i = lutperm_out(lp), j = lutperm_in(lp);
if ((i / 2) != (j / 2))
return true;
}
}
return false;
}
bool checkPipAvail(PipId pip) const override { return (getBoundPipNet(pip) == nullptr) && !is_pip_blocked(pip); }
bool checkPipAvailForNet(PipId pip, NetInfo *net) const override bool checkPipAvailForNet(PipId pip, NetInfo *net) const override
{ {
NetInfo *bound_net = getBoundPipNet(pip); NetInfo *bound_net = getBoundPipNet(pip);
return bound_net == nullptr || bound_net == net; return (bound_net == nullptr || bound_net == net) && !is_pip_blocked(pip);
} }
NetInfo *getBoundPipNet(PipId pip) const override { return pip2net.at(get_pip_vecidx(pip)); } NetInfo *getBoundPipNet(PipId pip) const override { return pip2net.at(get_pip_vecidx(pip)); }

View File

@ -165,6 +165,7 @@ struct ArchCellInfo : BaseClusterInfo
bool using_dff; bool using_dff;
bool has_l6mux; bool has_l6mux;
bool is_carry; bool is_carry;
bool is_memory;
IdString clk_sig, lsr_sig, clkmux, lsrmux, srmode; IdString clk_sig, lsr_sig, clkmux, lsrmux, srmode;
int sd0, sd1; int sd0, sd1;
} sliceInfo; } sliceInfo;

View File

@ -522,6 +522,55 @@ static void set_pip(Context *ctx, ChipConfig &cc, PipId pip)
cc.tiles[tile].add_arc(sink, source); cc.tiles[tile].add_arc(sink, source);
} }
static unsigned permute_lut(Context *ctx, CellInfo *cell, pool<IdString> &used_phys_pins, int k, unsigned orig_init)
{
std::array<std::vector<unsigned>, 4> phys_to_log;
const std::array<IdString, 4> ports{k ? id_A1 : id_A0, k ? id_B1 : id_B0, k ? id_C1 : id_C0, k ? id_D1 : id_D0};
for (unsigned i = 0; i < 4; i++) {
WireId pin_wire = ctx->getBelPinWire(cell->bel, ports[i]);
for (PipId pip : ctx->getPipsUphill(pin_wire)) {
if (!ctx->getBoundPipNet(pip))
continue;
unsigned lp = ctx->loc_info(pip)->pip_data[pip.index].lutperm_flags;
if (!is_lutperm_pip(lp)) { // non-permuting
phys_to_log[i].push_back(i);
} else { // permuting
unsigned from_pin = lutperm_in(lp);
unsigned to_pin = lutperm_out(lp);
NPNR_ASSERT(to_pin == i);
phys_to_log[from_pin].push_back(i);
}
}
}
for (unsigned i = 0; i < 4; i++)
if (!phys_to_log.at(i).empty())
used_phys_pins.insert(ports.at(i));
if (cell->sliceInfo.is_carry) {
// Insert dummy entries to ensure we keep the split between the two halves of a CCU2
for (unsigned i = 0; i < 4; i++) {
if (!phys_to_log.at(i).empty())
continue;
for (unsigned j = 2 * (i / 2); j < 2 * ((i / 2) + 1); j++) {
if (!ctx->getBoundWireNet(ctx->getBelPinWire(cell->bel, ports[j])))
phys_to_log.at(i).push_back(j);
}
}
}
unsigned permuted_init = 0;
for (unsigned i = 0; i < 16; i++) {
unsigned log_idx = 0;
for (unsigned j = 0; j < 4; j++) {
if ((i >> j) & 0x1) {
for (auto log_pin : phys_to_log[j])
log_idx |= (1 << log_pin);
}
}
if ((orig_init >> log_idx) & 0x1)
permuted_init |= (1 << i);
}
return permuted_init;
}
static std::vector<bool> parse_config_str(const Property &p, int length) static std::vector<bool> parse_config_str(const Property &p, int length)
{ {
std::vector<bool> word; std::vector<bool> word;
@ -787,12 +836,15 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
} }
BelId bel = ci->bel; BelId bel = ci->bel;
if (ci->type == ctx->id("TRELLIS_SLICE")) { if (ci->type == ctx->id("TRELLIS_SLICE")) {
pool<IdString> used_phys_pins;
std::string tname = ctx->get_tile_by_type_loc(bel.location.y, bel.location.x, "PLC2"); std::string tname = ctx->get_tile_by_type_loc(bel.location.y, bel.location.x, "PLC2");
std::string slice = ctx->loc_info(bel)->bel_data[bel.index].name.get(); std::string slice = ctx->loc_info(bel)->bel_data[bel.index].name.get();
int lut0_init = int_or_default(ci->params, ctx->id("LUT0_INITVAL")); int lut0_init = int_or_default(ci->params, ctx->id("LUT0_INITVAL"));
int lut1_init = int_or_default(ci->params, ctx->id("LUT1_INITVAL")); int lut1_init = int_or_default(ci->params, ctx->id("LUT1_INITVAL"));
cc.tiles[tname].add_word(slice + ".K0.INIT", int_to_bitvector(lut0_init, 16)); cc.tiles[tname].add_word(slice + ".K0.INIT",
cc.tiles[tname].add_word(slice + ".K1.INIT", int_to_bitvector(lut1_init, 16)); int_to_bitvector(permute_lut(ctx, ci, used_phys_pins, 0, lut0_init), 16));
cc.tiles[tname].add_word(slice + ".K1.INIT",
int_to_bitvector(permute_lut(ctx, ci, used_phys_pins, 1, lut1_init), 16));
cc.tiles[tname].add_enum(slice + ".MODE", str_or_default(ci->params, ctx->id("MODE"), "LOGIC")); cc.tiles[tname].add_enum(slice + ".MODE", str_or_default(ci->params, ctx->id("MODE"), "LOGIC"));
cc.tiles[tname].add_enum(slice + ".GSR", str_or_default(ci->params, ctx->id("GSR"), "ENABLED")); cc.tiles[tname].add_enum(slice + ".GSR", str_or_default(ci->params, ctx->id("GSR"), "ENABLED"));
cc.tiles[tname].add_enum(slice + ".REG0.SD", intstr_or_default(ci->params, ctx->id("REG0_SD"), "0")); cc.tiles[tname].add_enum(slice + ".REG0.SD", intstr_or_default(ci->params, ctx->id("REG0_SD"), "0"));
@ -854,7 +906,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
// Tie unused inputs high // Tie unused inputs high
for (auto input : {id_A0, id_B0, id_C0, id_D0, id_A1, id_B1, id_C1, id_D1}) { for (auto input : {id_A0, id_B0, id_C0, id_D0, id_A1, id_B1, id_C1, id_D1}) {
if (ci->ports.find(input) == ci->ports.end() || ci->ports.at(input).net == nullptr) { if (!used_phys_pins.count(input)) {
cc.tiles[tname].add_enum(slice + "." + input.str(ctx) + "MUX", "1"); cc.tiles[tname].add_enum(slice + "." + input.str(ctx) + "MUX", "1");
} }
} }

View File

@ -85,6 +85,7 @@ po::options_description ECP5CommandHandler::getArchOptions()
specific.add_options()( specific.add_options()(
"out-of-context", "out-of-context",
"disable IO buffer insertion and global promotion/routing, for building pre-routed blocks (experimental)"); "disable IO buffer insertion and global promotion/routing, for building pre-routed blocks (experimental)");
specific.add_options()("disable-router-lutperm", "don't allow the router to permute LUT inputs");
return specific; return specific;
} }
@ -255,6 +256,8 @@ std::unique_ptr<Context> ECP5CommandHandler::createContext(dict<std::string, Pro
ctx->settings[ctx->id("arch.speed")] = speedString(ctx->archArgs().speed); ctx->settings[ctx->id("arch.speed")] = speedString(ctx->archArgs().speed);
if (vm.count("out-of-context")) if (vm.count("out-of-context"))
ctx->settings[ctx->id("arch.ooc")] = 1; ctx->settings[ctx->id("arch.ooc")] = 1;
if (vm.count("disable-router-lutperm"))
ctx->settings[ctx->id("arch.disable_router_lutperm")] = 1;
return ctx; return ctx;
} }

View File

@ -3253,7 +3253,9 @@ void Arch::assignArchInfo()
ci->sliceInfo.clkmux = id(str_or_default(ci->params, id_CLKMUX, "CLK")); ci->sliceInfo.clkmux = id(str_or_default(ci->params, id_CLKMUX, "CLK"));
ci->sliceInfo.lsrmux = id(str_or_default(ci->params, id_LSRMUX, "LSR")); ci->sliceInfo.lsrmux = id(str_or_default(ci->params, id_LSRMUX, "LSR"));
ci->sliceInfo.srmode = id(str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE")); ci->sliceInfo.srmode = id(str_or_default(ci->params, id_SRMODE, "LSR_OVER_CE"));
ci->sliceInfo.is_carry = str_or_default(ci->params, id("MODE"), "LOGIC") == "CCU2"; std::string mode = str_or_default(ci->params, id("MODE"), "LOGIC");
ci->sliceInfo.is_carry = (mode == "CCU2");
ci->sliceInfo.is_memory = (mode == "DPRAM" || mode == "RAMW");
ci->sliceInfo.sd0 = std::stoi(str_or_default(ci->params, id("REG0_SD"), "0")); ci->sliceInfo.sd0 = std::stoi(str_or_default(ci->params, id("REG0_SD"), "0"));
ci->sliceInfo.sd1 = std::stoi(str_or_default(ci->params, id("REG1_SD"), "0")); ci->sliceInfo.sd1 = std::stoi(str_or_default(ci->params, id("REG1_SD"), "0"));
ci->sliceInfo.has_l6mux = false; ci->sliceInfo.has_l6mux = false;

View File

@ -426,6 +426,8 @@ def write_database(dev_name, chip, ddrg, endianness):
if cls == 1 and "PCS" in snk_name or "DCU" in snk_name or "DCU" in src_name: if cls == 1 and "PCS" in snk_name or "DCU" in snk_name or "DCU" in src_name:
cls = 2 cls = 2
bba.u8(cls, "pip_type") bba.u8(cls, "pip_type")
bba.u16(arc.lutperm_flags, "lutperm_flags")
bba.u16(0, "padding")
if len(loctype.wires) > 0: if len(loctype.wires) > 0:
for wire_idx in range(len(loctype.wires)): for wire_idx in range(len(loctype.wires)):
wire = loctype.wires[wire_idx] wire = loctype.wires[wire_idx]
@ -623,7 +625,7 @@ def main():
# print("Initialising chip...") # print("Initialising chip...")
chip = pytrellis.Chip(dev_names[args.device]) chip = pytrellis.Chip(dev_names[args.device])
# print("Building routing graph...") # print("Building routing graph...")
ddrg = pytrellis.make_dedup_chipdb(chip) ddrg = pytrellis.make_dedup_chipdb(chip, include_lutperm_pips=True)
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_timing_data() process_timing_data()