From 9cbfd0b967f5804472afbb91d8df92e69dffe659 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Thu, 25 Feb 2021 09:11:32 -0800 Subject: [PATCH 1/4] Add counter test. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fpga_interchange/examples/counter/Makefile | 8 +++++++ fpga_interchange/examples/counter/counter.v | 15 +++++++++++++ fpga_interchange/examples/counter/counter.xdc | 22 +++++++++++++++++++ fpga_interchange/examples/counter/run.tcl | 15 +++++++++++++ fpga_interchange/examples/remap.v | 11 ++++++++++ 5 files changed, 71 insertions(+) create mode 100644 fpga_interchange/examples/counter/Makefile create mode 100644 fpga_interchange/examples/counter/counter.v create mode 100644 fpga_interchange/examples/counter/counter.xdc create mode 100644 fpga_interchange/examples/counter/run.tcl create mode 100644 fpga_interchange/examples/remap.v diff --git a/fpga_interchange/examples/counter/Makefile b/fpga_interchange/examples/counter/Makefile new file mode 100644 index 00000000..27d20cdf --- /dev/null +++ b/fpga_interchange/examples/counter/Makefile @@ -0,0 +1,8 @@ +DESIGN := counter +DESIGN_TOP := top +PACKAGE := cpg236 + +include ../template.mk + +build/counter.json: counter.v | build + yosys -c run.tcl diff --git a/fpga_interchange/examples/counter/counter.v b/fpga_interchange/examples/counter/counter.v new file mode 100644 index 00000000..00f52a20 --- /dev/null +++ b/fpga_interchange/examples/counter/counter.v @@ -0,0 +1,15 @@ +module top(input clk, input rst, output [7:4] io_led); + +reg [31:0] counter = 32'b0; + +assign io_led = counter >> 22; + +always @(posedge clk) +begin + if(rst) + counter <= 32'b0; + else + counter <= counter + 1; +end + +endmodule diff --git a/fpga_interchange/examples/counter/counter.xdc b/fpga_interchange/examples/counter/counter.xdc new file mode 100644 index 00000000..7cbe67f6 --- /dev/null +++ b/fpga_interchange/examples/counter/counter.xdc @@ -0,0 +1,22 @@ +## basys3 breakout board +set_property PACKAGE_PIN W5 [get_ports clk] +set_property PACKAGE_PIN V17 [get_ports rst] +#set_property PACKAGE_PIN U16 [get_ports io_led[0]] +#set_property PACKAGE_PIN E19 [get_ports io_led[1]] +#set_property PACKAGE_PIN U19 [get_ports io_led[2]] +#set_property PACKAGE_PIN V19 [get_ports io_led[3]] +set_property PACKAGE_PIN U16 [get_ports io_led[4]] +set_property PACKAGE_PIN E19 [get_ports io_led[5]] +set_property PACKAGE_PIN U19 [get_ports io_led[6]] +set_property PACKAGE_PIN V19 [get_ports io_led[7]] + +set_property IOSTANDARD LVCMOS33 [get_ports clk] +set_property IOSTANDARD LVCMOS33 [get_ports rst] +set_property IOSTANDARD LVCMOS33 [get_ports io_led[4]] +set_property IOSTANDARD LVCMOS33 [get_ports io_led[5]] +set_property IOSTANDARD LVCMOS33 [get_ports io_led[6]] +set_property IOSTANDARD LVCMOS33 [get_ports io_led[7]] +#set_property IOSTANDARD LVCMOS33 [get_ports io_led[0]] +#set_property IOSTANDARD LVCMOS33 [get_ports io_led[1]] +#set_property IOSTANDARD LVCMOS33 [get_ports io_led[2]] +#set_property IOSTANDARD LVCMOS33 [get_ports io_led[3]] diff --git a/fpga_interchange/examples/counter/run.tcl b/fpga_interchange/examples/counter/run.tcl new file mode 100644 index 00000000..245aab04 --- /dev/null +++ b/fpga_interchange/examples/counter/run.tcl @@ -0,0 +1,15 @@ +yosys -import + +read_verilog counter.v + +synth_xilinx -nolutram -nowidelut -nosrl -nocarry -nodsp +techmap -map ../remap.v + +# opt_expr -undriven makes sure all nets are driven, if only by the $undef +# net. +opt_expr -undriven +opt_clean + +setundef -zero -params + +write_json build/counter.json diff --git a/fpga_interchange/examples/remap.v b/fpga_interchange/examples/remap.v new file mode 100644 index 00000000..6dfc0b4a --- /dev/null +++ b/fpga_interchange/examples/remap.v @@ -0,0 +1,11 @@ +module INV(input I, output O); + +LUT1 #(.INIT(2'b01)) _TECHMAP_REPLACE_ (.I0(I), .O(O)); + +endmodule + +module BUF(input I, output O); + +LUT1 #(.INIT(2'b10)) _TECHMAP_REPLACE_ (.I0(I), .O(O)); + +endmodule From cfa449c3f3c5b151eb11ef79bc2cf571e98bbbed Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Wed, 24 Feb 2021 14:02:21 -0800 Subject: [PATCH 2/4] Initial LUT rotation logic. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- common/dynamic_bitarray.h | 2 + fpga_interchange/arch.cc | 197 ++++++++++++++++- fpga_interchange/arch.h | 35 ++- fpga_interchange/archdefs.h | 3 + fpga_interchange/luts.cc | 370 ++++++++++++++++++++++++++++++++ fpga_interchange/luts.h | 101 +++++++++ fpga_interchange/site_router.cc | 35 ++- fpga_interchange/site_router.h | 5 + 8 files changed, 741 insertions(+), 7 deletions(-) create mode 100644 fpga_interchange/luts.cc create mode 100644 fpga_interchange/luts.h diff --git a/common/dynamic_bitarray.h b/common/dynamic_bitarray.h index 10a85fbc..2b5ab2bc 100644 --- a/common/dynamic_bitarray.h +++ b/common/dynamic_bitarray.h @@ -70,6 +70,8 @@ template > class DynamicBitarray size_t size() const { return storage.size() * bits_per_value(); } + void clear() { return storage.clear(); } + private: Storage storage; }; diff --git a/fpga_interchange/arch.cc b/fpga_interchange/arch.cc index dc99f1cd..3b603e5e 100644 --- a/fpga_interchange/arch.cc +++ b/fpga_interchange/arch.cc @@ -158,6 +158,7 @@ Arch::Arch(ArchArgs args) : args(args) int tile_type_index = 0; size_t max_tag_count = 0; + for (const TileTypeInfoPOD &tile_type : chip_info->tile_types) { max_tag_count = std::max(max_tag_count, tile_type.tags.size()); @@ -192,6 +193,50 @@ Arch::Arch(ArchArgs args) : args(args) } } + // Initially LutElement vectors for each tile type. + tile_type_index = 0; + lut_elements.resize(chip_info->tile_types.size()); + for (const TileTypeInfoPOD &tile_type : chip_info->tile_types) { + std::vector &elements = lut_elements[tile_type_index++]; + elements.reserve(tile_type.lut_elements.size()); + for (auto &lut_element : tile_type.lut_elements) { + elements.emplace_back(); + + LutElement &element = elements.back(); + element.width = lut_element.width; + for (auto &lut_bel : lut_element.lut_bels) { + auto result = element.lut_bels.emplace(IdString(lut_bel.name), LutBel()); + NPNR_ASSERT(result.second); + LutBel &lut = result.first->second; + + lut.low_bit = lut_bel.low_bit; + lut.high_bit = lut_bel.high_bit; + + lut.pins.reserve(lut_bel.pins.size()); + for (size_t i = 0; i < lut_bel.pins.size(); ++i) { + IdString pin(lut_bel.pins[i]); + lut.pins.push_back(pin); + lut.pin_to_index[pin] = i; + } + } + + element.compute_pin_order(); + } + } + + // Map lut cell types to their LutCellPOD + for (const LutCellPOD &lut_cell : chip_info->cell_map->lut_cells) { + IdString cell_type(lut_cell.cell); + auto result = lut_cells.emplace(cell_type, &lut_cell); + NPNR_ASSERT(result.second); + } + + raw_bin_constant = std::regex("[01]+", std::regex_constants::ECMAScript | std::regex_constants::optimize); + verilog_bin_constant = + std::regex("([0-9]+)'b([01]+)", std::regex_constants::ECMAScript | std::regex_constants::optimize); + verilog_hex_constant = + std::regex("([0-9]+)'h([0-9a-fA-F]+)", std::regex_constants::ECMAScript | std::regex_constants::optimize); + default_tags.resize(max_tag_count); } @@ -603,6 +648,7 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay bool Arch::pack() { + decode_lut_cells(); merge_constant_nets(); pack_ports(); return true; @@ -666,11 +712,71 @@ bool Arch::route() } else { log_error("FPGA interchange architecture does not support router '%s'\n", router.c_str()); } + + if (result) { + result = route_vcc_to_unused_lut_pins(); + } + getCtx()->attrs[getCtx()->id("step")] = std::string("route"); archInfoToAttributes(); + return result; } +bool Arch::route_vcc_to_unused_lut_pins() { + std::string router = str_or_default(settings, id("router"), defaultRouter); + + // Fixup LUT vcc pins. + IdString vcc_net_name(chip_info->constants->vcc_net_name); + for (BelId bel : getBels()) { + CellInfo *cell = getBoundBelCell(bel); + if (cell == nullptr) { + continue; + } + + if (cell->lut_cell.vcc_pins.empty()) { + continue; + } + + for (auto bel_pin : cell->lut_cell.vcc_pins) { + PortInfo port_info; + port_info.name = bel_pin; + port_info.type = PORT_IN; + port_info.net = nullptr; + + WireId lut_pin_wire = getBelPinWire(bel, bel_pin); + auto iter = wire_to_net.find(lut_pin_wire); + if (iter != wire_to_net.end()) { + if (iter->second != nullptr) { + // This pin is now used by a route through. + continue; + } + } + + auto result = cell->ports.emplace(bel_pin, port_info); + if (result.second) { + cell->cell_bel_pins[bel_pin].push_back(bel_pin); + connectPort(vcc_net_name, cell->name, bel_pin); + cell->const_ports.emplace(bel_pin); + } else { + NPNR_ASSERT(result.first->second.net == getNetByAlias(vcc_net_name)); + auto result2 = cell->cell_bel_pins.emplace(bel_pin, std::vector({bel_pin})); + NPNR_ASSERT(result2.first->second.at(0) == bel_pin); + NPNR_ASSERT(result2.first->second.size() == 1); + } + } + } + + if (router == "router1") { + return router1(getCtx(), Router1Cfg(getCtx())); + } else if (router == "router2") { + router2(getCtx(), Router2Cfg(getCtx())); + return true; + } else { + log_error("FPGA interchange architecture does not support router '%s'\n", router.c_str()); + } +} + // ----------------------------------------------------------------------- std::vector Arch::getDecalGraphics(DecalId decal) const { return {}; } @@ -791,7 +897,20 @@ const std::vector Arch::availableRouters = {"router1", "router2"}; void Arch::map_cell_pins(CellInfo *cell, int32_t mapping, bool bind_constants) { cell->cell_mapping = mapping; - cell->cell_bel_pins.clear(); + if (cell->lut_cell.pins.empty()) { + cell->cell_bel_pins.clear(); + } else { + std::vector cell_pin_to_remove; + for (auto port_pair : cell->cell_bel_pins) { + if (!cell->lut_cell.lut_pins.count(port_pair.first)) { + cell_pin_to_remove.push_back(port_pair.first); + } + } + + for (IdString cell_pin : cell_pin_to_remove) { + NPNR_ASSERT(cell->cell_bel_pins.erase(cell_pin)); + } + } for (IdString const_port : cell->const_ports) { NPNR_ASSERT(cell->ports.erase(const_port)); } @@ -805,6 +924,11 @@ void Arch::map_cell_pins(CellInfo *cell, int32_t mapping, bool bind_constants) IdString cell_pin(pin_map.cell_pin); IdString bel_pin(pin_map.bel_pin); + // Skip assigned LUT pins, as they are already mapped! + if (cell->lut_cell.lut_pins.count(cell_pin) && cell->cell_bel_pins.count(cell_pin)) { + continue; + } + if (cell_pin.str(this) == "GND") { if (bind_constants) { PortInfo port_info; @@ -869,11 +993,17 @@ void Arch::map_cell_pins(CellInfo *cell, int32_t mapping, bool bind_constants) IdString cell_pin(pin_map.cell_pin); IdString bel_pin(pin_map.bel_pin); + // Skip assigned LUT pins, as they are already mapped! + if (cell->lut_cell.lut_pins.count(cell_pin) && cell->cell_bel_pins.count(cell_pin)) { + continue; + } + if (cell_pin.str(this) == "GND") { if (bind_constants) { PortInfo port_info; port_info.name = bel_pin; port_info.type = PORT_IN; + port_info.net = nullptr; auto result = cell->ports.emplace(bel_pin, port_info); if (result.second) { @@ -895,6 +1025,7 @@ void Arch::map_cell_pins(CellInfo *cell, int32_t mapping, bool bind_constants) PortInfo port_info; port_info.name = bel_pin; port_info.type = PORT_IN; + port_info.net = nullptr; auto result = cell->ports.emplace(bel_pin, port_info); if (result.second) { @@ -1135,6 +1266,70 @@ void Arch::report_invalid_bel(BelId bel, CellInfo *cell) const nameOfBel(bel), mapping); } +void Arch::read_lut_equation(nextpnr::DynamicBitarray<> *equation, const Property &equation_parameter) const +{ + equation->fill(false); + std::string eq_str = equation_parameter.as_string(); + std::smatch results; + if (std::regex_match(eq_str, results, raw_bin_constant)) { + size_t bit_idx = 0; + const std::string &bits = results[0]; + NPNR_ASSERT(bits.size() <= equation->size()); + for (auto bit = bits.rbegin(); bit != bits.rend(); ++bit) { + if (*bit == '0') { + equation->set(bit_idx++, false); + } else { + NPNR_ASSERT(*bit == '1'); + equation->set(bit_idx++, true); + } + } + } else if (std::regex_match(eq_str, results, verilog_bin_constant)) { + int iwidth = std::stoi(results[1]); + NPNR_ASSERT(iwidth >= 0); + size_t width = iwidth; + std::string bits = results[2]; + NPNR_ASSERT(width <= equation->size()); + NPNR_ASSERT(bits.size() <= width); + size_t bit_idx = 0; + for (auto bit = bits.rbegin(); bit != bits.rend(); ++bit) { + if (*bit == '0') { + equation->set(bit_idx++, false); + } else { + NPNR_ASSERT(*bit == '1'); + equation->set(bit_idx++, true); + } + } + } else { + NPNR_ASSERT(false); + } +} + +void Arch::decode_lut_cells() +{ + for (auto &cell_pair : cells) { + CellInfo *cell = cell_pair.second.get(); + auto iter = lut_cells.find(cell->type); + if (iter == lut_cells.end()) { + cell->lut_cell.pins.clear(); + cell->lut_cell.equation.clear(); + continue; + } + + const LutCellPOD &lut_cell = *iter->second; + + cell->lut_cell.pins.reserve(lut_cell.input_pins.size()); + for (uint32_t pin : lut_cell.input_pins) { + cell->lut_cell.pins.push_back(IdString(pin)); + cell->lut_cell.lut_pins.emplace(IdString(pin)); + } + + IdString equation_parameter(lut_cell.parameter); + const Property &equation = cell->params.at(equation_parameter); + cell->lut_cell.equation.resize(1 << cell->lut_cell.pins.size()); + read_lut_equation(&cell->lut_cell.equation, equation); + } +} + // Instance constraint templates. template void Arch::ArchConstraints::bindBel(Arch::ArchConstraints::TagState *, const Arch::ConstraintRange); template void Arch::ArchConstraints::unbindBel(Arch::ArchConstraints::TagState *, const Arch::ConstraintRange); diff --git a/fpga_interchange/arch.h b/fpga_interchange/arch.h index 13cab02f..16c79e8a 100644 --- a/fpga_interchange/arch.h +++ b/fpga_interchange/arch.h @@ -28,6 +28,7 @@ #include +#include #include "constraints.h" #include "dedicated_interconnect.h" #include "site_router.h" @@ -69,7 +70,8 @@ NPNR_PACKED_STRUCT(struct BelInfoPOD { int16_t site; int16_t site_variant; // some sites have alternative types int16_t category; - int16_t synthetic; + int8_t synthetic; + int8_t lut_element; RelPtr pin_map; // Index into CellMapPOD::cell_bel_map }); @@ -119,6 +121,18 @@ NPNR_PACKED_STRUCT(struct ConstraintTagPOD { RelSlice states; // constid }); +NPNR_PACKED_STRUCT(struct LutBelPOD { + uint32_t name; // constid + RelSlice pins; // constid + uint32_t low_bit; + uint32_t high_bit; +}); + +NPNR_PACKED_STRUCT(struct LutElementPOD { + int32_t width; + RelSlice lut_bels; +}); + NPNR_PACKED_STRUCT(struct TileTypeInfoPOD { int32_t name; // Tile type constid @@ -130,6 +144,8 @@ NPNR_PACKED_STRUCT(struct TileTypeInfoPOD { RelSlice tags; + RelSlice lut_elements; + RelSlice site_types; // constid }); @@ -190,12 +206,20 @@ NPNR_PACKED_STRUCT(struct CellBelMapPOD { RelSlice constraints; }); +NPNR_PACKED_STRUCT(struct LutCellPOD { + int32_t cell; // constid + RelSlice input_pins; // constids + int32_t parameter; +}); + NPNR_PACKED_STRUCT(struct CellMapPOD { // Cell names supported in this arch. RelSlice cell_names; // constids RelSlice cell_bel_buckets; // constids RelSlice cell_bel_map; + + RelSlice lut_cells; }); NPNR_PACKED_STRUCT(struct PackagePinPOD { @@ -1362,6 +1386,7 @@ struct Arch : ArchAPI void place_iobufs(WireId pad_wire, NetInfo *net, const std::unordered_set &tightly_attached_bels, std::unordered_set *placed_cells); void pack_ports(); + void decode_lut_cells(); bool pack() override; bool place() override; bool route() override; @@ -1706,6 +1731,14 @@ struct Arch : ArchAPI std::vector no_pins; IdString gnd_cell_pin; IdString vcc_cell_pin; + std::vector> lut_elements; + std::unordered_map lut_cells; + + std::regex raw_bin_constant; + std::regex verilog_bin_constant; + std::regex verilog_hex_constant; + void read_lut_equation(nextpnr::DynamicBitarray<> *equation, const Property &equation_parameter) const; + bool route_vcc_to_unused_lut_pins(); }; NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/archdefs.h b/fpga_interchange/archdefs.h index 33d999bb..e355a6c4 100644 --- a/fpga_interchange/archdefs.h +++ b/fpga_interchange/archdefs.h @@ -22,6 +22,8 @@ #error Include "archdefs.h" via "nextpnr.h" only. #endif +#include "luts.h" + NEXTPNR_NAMESPACE_BEGIN #include @@ -107,6 +109,7 @@ struct ArchCellInfo int32_t cell_mapping; std::unordered_map> cell_bel_pins; std::unordered_set const_ports; + LutCell lut_cell; }; NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/luts.cc b/fpga_interchange/luts.cc new file mode 100644 index 00000000..dc4e0531 --- /dev/null +++ b/fpga_interchange/luts.cc @@ -0,0 +1,370 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021 Symbiflow Authors + * + * 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 "nextpnr.h" + +#include "luts.h" +#include "log.h" + +NEXTPNR_NAMESPACE_BEGIN + +bool rotate_and_merge_lut_equation(std::vector * result, + const LutBel & lut_bel, + const nextpnr::DynamicBitarray<> &old_equation, + const std::vector &pin_map, + uint32_t used_pins) { + // pin_map maps pin indicies from the old pin to the new pin. + // So a reversal of a LUT4 would have a pin map of: + // pin_map[0] = 3; + // pin_map[1] = 2; + // pin_map[2] = 1; + // pin_map[3] = 0; + + size_t bel_width = 1 << lut_bel.pins.size(); + for(size_t bel_address = 0; bel_address < bel_width; ++bel_address) { + bool address_reachable = true; + size_t cell_address = 0; + for(size_t bel_pin_idx = 0; bel_pin_idx < lut_bel.pins.size(); ++bel_pin_idx) { + // This address line is 0, so don't translate this bit to the cell + // address. + if((bel_address & (1 << bel_pin_idx)) == 0) { + // This pin is unused, so the line will be tied high, this + // address is unreachable. + if((used_pins & (1 << bel_pin_idx)) == 0) { + address_reachable = false; + break; + } + + continue; + } + + + auto cell_pin_idx = pin_map[bel_pin_idx]; + + // Is this BEL pin used for this cell? + if(cell_pin_idx < 0) { + // This BEL pin is not used for the LUT cell, skip + continue; + } + + cell_address |= (1 << cell_pin_idx); + } + + if(!address_reachable) { + continue; + } + + bel_address += lut_bel.low_bit; + if(old_equation.get(cell_address)) { + if((*result)[bel_address] == LL_Zero) { + // Output equation has a conflict! + return false; + } + + (*result)[bel_address] = LL_One; + } else { + if((*result)[bel_address] == LL_One) { + // Output equation has a conflict! + return false; + } + (*result)[bel_address] = LL_Zero; + } + } + + return true; +} + +static constexpr bool kCheckOutputEquation = true; + +struct LutPin { + struct LutPinUser { + size_t cell_idx; + size_t cell_pin_idx; + }; + + const NetInfo * net = nullptr; + std::vector users; + + int32_t min_pin = -1; + int32_t max_pin = -1; + + void add_user(const LutBel & lut_bel, size_t cell_idx, size_t cell_pin_idx) { + if(min_pin < 0) { + min_pin = lut_bel.min_pin; + max_pin = lut_bel.max_pin; + } + + min_pin = std::max(min_pin, lut_bel.min_pin); + max_pin = std::min(max_pin, lut_bel.max_pin); + + users.emplace_back(); + users.back().cell_idx = cell_idx; + users.back().cell_pin_idx = cell_pin_idx; + } + + bool operator < (const LutPin & other) const { + return max_pin < other.max_pin; + } +}; + +bool LutMapper::remap_luts(const Context *ctx) { + std::unordered_map lut_pin_map; + std::vector lut_bels; + lut_bels.resize(cells.size()); + + for(size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) { + const CellInfo * cell = cells[cell_idx]; +#ifdef DEBUG_LUT_ROTATION + log_info("Mapping %s %s eq = %s at %s\n", cell->type.c_str(ctx), cell->name.c_str(ctx), cell->params.at(ctx->id("INIT")).c_str(), ctx->nameOfBel(cell->bel)); +#endif + + auto & bel_data = bel_info(ctx->chip_info, cell->bel); + IdString bel_name(bel_data.name); + auto & lut_bel = element.lut_bels.at(bel_name); + lut_bels[cell_idx] = &lut_bel; + + for(size_t pin_idx = 0; pin_idx < cell->lut_cell.pins.size(); ++pin_idx) { + IdString lut_pin_name = cell->lut_cell.pins[pin_idx]; + const PortInfo & port_info = cell->ports.at(lut_pin_name); + NPNR_ASSERT(port_info.net != nullptr); + + auto result = lut_pin_map.emplace(port_info.net, LutPin()); + LutPin & lut_pin = result.first->second; + lut_pin.net = port_info.net; + lut_pin.add_user(lut_bel, cell_idx, pin_idx); + } + } + + if(lut_pin_map.size() > element.pins.size()) { + // Trival conflict, more nets entering element than pins are + // available! +#ifdef DEBUG_LUT_ROTATION + log_info("Trival failure %zu > %zu, %zu %zu\n", + lut_pin_map.size(), element.pins.size(), element.width, element.lut_bels.size()); +#endif + return false; + } + + std::vector lut_pins; + lut_pins.reserve(lut_pin_map.size()); + for(auto lut_pin_pair : lut_pin_map) { + lut_pins.push_back(std::move(lut_pin_pair.second)); + } + lut_pin_map.clear(); + std::sort(lut_pins.begin(), lut_pins.end()); + + std::vector> cell_to_bel_pin_remaps; + std::vector> bel_to_cell_pin_remaps; + cell_to_bel_pin_remaps.resize(cells.size()); + bel_to_cell_pin_remaps.resize(cells.size()); + for(size_t i = 0; i < cells.size(); ++i) { + cell_to_bel_pin_remaps[i].resize(cells[i]->lut_cell.pins.size()); + bel_to_cell_pin_remaps[i].resize(lut_bels[i]->pins.size(), -1); + } + + uint32_t used_pins = 0; + size_t idx = 0; + std::vector net_pins(lut_pins.size()); + for(auto &lut_pin : lut_pins) { + size_t net_idx = idx++; + used_pins |= (1 << net_idx); + + for(auto cell_pin_idx : lut_pin.users) { + size_t cell_idx = cell_pin_idx.cell_idx; + size_t pin_idx = cell_pin_idx.cell_pin_idx; + IdString bel_pin = lut_bels[cell_idx]->pins[net_idx]; +#ifdef DEBUG_LUT_ROTATION + log_info("%s %s %s => %s (%s)\n", + cells[cell_idx]->type.c_str(ctx), + cells[cell_idx]->name.c_str(ctx), + cells[cell_idx]->lut_cell.pins[pin_idx].c_str(ctx), + bel_pin.c_str(ctx), + lut_pin.net->name.c_str(ctx)); +#endif + if(net_pins[net_idx] == IdString()) { + net_pins[net_idx] = bel_pin; + } else { + NPNR_ASSERT(net_pins[net_idx] == bel_pin); + } + + cell_to_bel_pin_remaps[cell_idx][pin_idx] = net_idx; + bel_to_cell_pin_remaps[cell_idx][net_idx] = pin_idx; + } + } + + // Try to see if the equations are mergable! + std::vector equation_result; + equation_result.resize(element.width, LL_DontCare); + for(size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) { + const CellInfo * cell = cells[cell_idx]; + auto & lut_bel = *lut_bels[cell_idx]; + if(!rotate_and_merge_lut_equation(&equation_result, + lut_bel, cell->lut_cell.equation, bel_to_cell_pin_remaps[cell_idx], used_pins)) { +#ifdef DEBUG_LUT_ROTATION + log_info("Failed to find a solution!\n"); + for(auto *cell : cells) { + log_info("%s %s : %s\b\n", + cell->type.c_str(ctx), + cell->name.c_str(ctx), + cell->params.at(ctx->id("INIT")).c_str()); + } +#endif + return false; + } + } + +#ifdef DEBUG_LUT_ROTATION + log_info("Found a solution!\n"); +#endif + + // Sanity check final equation to make sure no assumptions are violated. + if(kCheckOutputEquation) { + for(size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) { + CellInfo * cell = cells[cell_idx]; + auto & lut_bel = *lut_bels[cell_idx]; + + std::unordered_map cell_to_bel_map; + for(size_t pin_idx = 0; pin_idx < cell->lut_cell.pins.size(); ++pin_idx) { + size_t bel_pin_idx = cell_to_bel_pin_remaps[cell_idx][pin_idx]; + NPNR_ASSERT(bel_pin_idx < lut_bel.pins.size()); + cell_to_bel_map[cell->lut_cell.pins[pin_idx]] = lut_bel.pins[bel_pin_idx]; + } + + check_equation(cell->lut_cell, cell_to_bel_map, lut_bel, equation_result, used_pins); + } + } + + // Push new cell -> BEL pin maps out to cells now that equations have been + // verified! + for(size_t cell_idx = 0; cell_idx < cells.size(); ++cell_idx) { + CellInfo * cell = cells[cell_idx]; + auto & lut_bel = *lut_bels[cell_idx]; + + for(size_t pin_idx = 0; pin_idx < cell->lut_cell.pins.size(); ++pin_idx) { + auto &bel_pins = cell->cell_bel_pins[cell->lut_cell.pins[pin_idx]]; + bel_pins.clear(); + bel_pins.push_back(lut_bel.pins[cell_to_bel_pin_remaps[cell_idx][pin_idx]]); + } + + cell->lut_cell.vcc_pins.clear(); + for(size_t bel_pin_idx = 0; bel_pin_idx < lut_bel.pins.size(); ++bel_pin_idx) { + if((used_pins & (1 << bel_pin_idx)) == 0) { + NPNR_ASSERT(bel_to_cell_pin_remaps[cell_idx][bel_pin_idx] == -1); + cell->lut_cell.vcc_pins.emplace(lut_bel.pins[bel_pin_idx]); + } + } + } + + return true; +} + +void check_equation( + const LutCell & lut_cell, + const std::unordered_map &cell_to_bel_map, + const LutBel & lut_bel, + const std::vector &equation, + uint32_t used_pins) { + std::vector pin_map; + pin_map.resize(lut_bel.pins.size(), -1); + + NPNR_ASSERT(lut_cell.pins.size() < std::numeric_limits::max()); + + for(size_t cell_pin_idx = 0; cell_pin_idx < lut_cell.pins.size(); ++cell_pin_idx) { + IdString cell_pin = lut_cell.pins[cell_pin_idx]; + IdString bel_pin = cell_to_bel_map.at(cell_pin); + size_t bel_pin_idx = lut_bel.pin_to_index.at(bel_pin); + + pin_map[bel_pin_idx] = cell_pin_idx; + } + + // Iterate over all BEL addresses in the LUT, and ensure that the original + // LUT equation is respected. + size_t bel_width = 1 << lut_bel.pins.size(); + NPNR_ASSERT(lut_bel.low_bit + bel_width == lut_bel.high_bit + 1); + for(size_t bel_address = 0; bel_address < bel_width; ++bel_address) { + LogicLevel level = equation[bel_address + lut_bel.low_bit]; + + bool address_reachable = true; + size_t cell_address = 0; + for(size_t bel_pin_idx = 0; bel_pin_idx < lut_bel.pins.size(); ++bel_pin_idx) { + // This address line is 0, so don't translate this bit to the cell + // address. + if((bel_address & (1 << bel_pin_idx)) == 0) { + // This pin is unused, so the line will be tied high, this + // address is unreachable. + if((used_pins & (1 << bel_pin_idx)) == 0) { + address_reachable = false; + break; + } + continue; + } + + auto cell_pin_idx = pin_map[bel_pin_idx]; + + // Is this BEL pin used for this cell? + if(cell_pin_idx < 0) { + // This BEL pin is not used for the LUT cell, skip + continue; + } + + cell_address |= (1 << cell_pin_idx); + } + + if(!address_reachable) { + continue; + } + + if(lut_cell.equation.get(cell_address)) { + NPNR_ASSERT(level == LL_One); + } else { + NPNR_ASSERT(level == LL_Zero); + } + } +} + +void LutElement::compute_pin_order() { + pins.clear(); + pin_to_index.clear(); + + for(auto & lut_bel_pair : lut_bels) { + auto & lut_bel = lut_bel_pair.second; + + for(size_t pin_idx = 0; pin_idx < lut_bel.pins.size(); ++pin_idx) { + IdString pin = lut_bel.pins[pin_idx]; + auto result = pin_to_index.emplace(pin, pin_idx); + if(!result.second) { + // Not sure when this isn't true, but check it for now! + NPNR_ASSERT(result.first->second == pin_idx); + } + } + } + + pins.resize(pin_to_index.size()); + for(auto &pin_pair : pin_to_index) { + pins.at(pin_pair.second) = pin_pair.first; + } + + for(auto & lut_bel_pair : lut_bels) { + auto & lut_bel = lut_bel_pair.second; + lut_bel.min_pin = pin_to_index.at(lut_bel.pins.front()); + lut_bel.max_pin = pin_to_index.at(lut_bel.pins.back()); + } +} + +NEXTPNR_NAMESPACE_END diff --git a/fpga_interchange/luts.h b/fpga_interchange/luts.h new file mode 100644 index 00000000..0218653a --- /dev/null +++ b/fpga_interchange/luts.h @@ -0,0 +1,101 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2021 Symbiflow Authors + * + * 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 NEXTPNR_H +#error Include "luts.h" via "nextpnr.h" only. +#endif + +#include "dynamic_bitarray.h" + +#ifndef LUTS_H +#define LUTS_H + +NEXTPNR_NAMESPACE_BEGIN + +struct CellInfo; +struct Context; + +enum LogicLevel { + LL_Zero, + LL_One, + LL_DontCare +}; + +struct LutCell { + // LUT cell pins for equation, LSB first. + std::vector pins; + std::unordered_set lut_pins; + std::unordered_set vcc_pins; + nextpnr::DynamicBitarray<> equation; +}; + +struct LutBel { + // LUT BEL pins to LUT array index. + std::vector pins; + std::unordered_map pin_to_index; + + // What part of the LUT equation does this LUT output use? + // This assumes contiguous LUT bits. + uint32_t low_bit; + uint32_t high_bit; + + int32_t min_pin; + int32_t max_pin; +}; + +// Work forward from cell definition and cell -> bel pin map and check that +// equation is valid. +void check_equation( + const LutCell & lut_cell, + const std::unordered_map &cell_to_bel_map, + const LutBel & lut_bel, + const std::vector &equation, + uint32_t used_pins); + +struct LutElement { + size_t width; + std::unordered_map lut_bels; + + void compute_pin_order(); + + std::vector pins; + std::unordered_map pin_to_index; +}; + +struct LutMapper { + LutMapper(const LutElement & element) : element(element) {} + const LutElement & element; + + std::vector cells; + + bool remap_luts(const Context *ctx); +}; + +// Rotate and merge a LUT equation into an array of levels. +// +// If a conflict arises, return false and result is in an indeterminate state. +bool rotate_and_merge_lut_equation(std::vector * result, + const LutBel & lut_bel, + const nextpnr::DynamicBitarray<> &old_equation, + const std::vector &pin_map, + uint32_t used_pins); + +NEXTPNR_NAMESPACE_END + +#endif /* LUTS_H */ diff --git a/fpga_interchange/site_router.cc b/fpga_interchange/site_router.cc index 7232b635..56bce01a 100644 --- a/fpga_interchange/site_router.cc +++ b/fpga_interchange/site_router.cc @@ -747,17 +747,42 @@ bool SiteRouter::checkSiteRouting(const Context *ctx, const TileStatus &tile_sta return site_ok; } } - // + // FIXME: Populate "consumed_wires" with all VCC/GND tied in the site. // This will allow route_site to leverage site local constant sources. // // FIXME: Handle case where a constant is requested, but use of an // inverter is possible. This is the place to handle "bestConstant" // (e.g. route VCC's over GND's, etc). - // - // FIXME: Enable some LUT rotation! - // Default cell/bel pin map always uses high pins, which will generate - // conflicts where there are none!!! + auto tile_type_idx = ctx->chip_info->tiles[tile].type; + const std::vector &lut_elements = ctx->lut_elements.at(tile_type_idx); + std::vector lut_mappers; + lut_mappers.reserve(lut_elements.size()); + for (size_t i = 0; i < lut_elements.size(); ++i) { + lut_mappers.push_back(LutMapper(lut_elements[i])); + } + + for (CellInfo *cell : cells_in_site) { + if (cell->lut_cell.pins.empty()) { + continue; + } + + BelId bel = cell->bel; + const auto &bel_data = bel_info(ctx->chip_info, bel); + if (bel_data.lut_element != -1) { + lut_mappers[bel_data.lut_element].cells.push_back(cell); + } + } + + for (LutMapper lut_mapper : lut_mappers) { + if (lut_mapper.cells.empty()) { + continue; + } + + if (!lut_mapper.remap_luts(ctx)) { + return false; + } + } SiteInformation site_info(ctx, cells_in_site); diff --git a/fpga_interchange/site_router.h b/fpga_interchange/site_router.h index 561dae9d..6af32747 100644 --- a/fpga_interchange/site_router.h +++ b/fpga_interchange/site_router.h @@ -22,6 +22,9 @@ #error Include "site_router.h" via "nextpnr.h" only. #endif +#ifndef SITE_ROUTER_H +#define SITE_ROUTER_H + NEXTPNR_NAMESPACE_BEGIN struct Context; @@ -43,3 +46,5 @@ struct SiteRouter }; NEXTPNR_NAMESPACE_END + +#endif /* SITE_ROUTER_H */ From 78748a67be81dcd85706a06c2e1faaa39cf4394d Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Fri, 26 Feb 2021 09:44:52 -0800 Subject: [PATCH 3/4] For now just return false in the site router. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fpga_interchange/site_router.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fpga_interchange/site_router.cc b/fpga_interchange/site_router.cc index 56bce01a..9d4fc57c 100644 --- a/fpga_interchange/site_router.cc +++ b/fpga_interchange/site_router.cc @@ -697,7 +697,7 @@ bool route_site(const Context *ctx, SiteInformation *site_info) // The simplistic solution (only select when 1 solution is available) // will likely solve initial problems. Once that is show to be wrong, // come back with something more general. - NPNR_ASSERT(false); + return false; } while (!wire_to_expansion.empty()); From 71b92cb8139c63a7936fa05f2a47739b0c115b01 Mon Sep 17 00:00:00 2001 From: Keith Rothman <537074+litghost@users.noreply.github.com> Date: Fri, 26 Feb 2021 10:22:52 -0800 Subject: [PATCH 4/4] Update FPGA interchange README. Signed-off-by: Keith Rothman <537074+litghost@users.noreply.github.com> --- fpga_interchange/README.md | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/fpga_interchange/README.md b/fpga_interchange/README.md index df832b94..78dd23ce 100644 --- a/fpga_interchange/README.md +++ b/fpga_interchange/README.md @@ -36,29 +36,25 @@ library. The current implementation is missing essential features for place and route. As these features are added, this implementation will become more useful. - - [ ] Logical netlist macro expansion is not implemented, meaning that any - macro primitives are unplaceable. Common macro primitives examples are - differential IO buffers (IBUFDS) and some LUT RAM (e.g. RAM64X1D). - [ ] The router lookahead is missing, meaning that router runtime performance will be terrible. - - [ ] The routing graph that is currently emitted does not have ground and - VCC networks, so all signals must currently be tied to an IO signal. - Site pins being tied to constants also needs handling so that site - local inverters are used rather than routing signals suboptimally. - [ ] Pseudo pips (e.g. pips that consume BELs and or site resources) should block their respective resources. This effects designs that have some routing in place before placement. - [ ] Pseudo site pips (e.g. site pips that route through BELs) should block their respective resources. Without this, using some pseudo site pips could result in invalid placements. + - [ ] Implemented site router lacks important features for tight packing. + Also the current site router is relatively untested, so legal + configurations may be rejected and illegal configurations may be + accepted. + - [ ] Logical netlist macro expansion is not implemented, meaning that any + macro primitives are unplaceable. Common macro primitives examples are + differential IO buffers (IBUFDS) and some LUT RAM (e.g. RAM64X1D). - [ ] Timing information is missing from the FPGA interchange device database, so it is also currently missing from the FPGA interchange architecture. Once timing information is added to the device database schema, it needs to be added to the architecture. - - [ ] Implemented site router lacks important features for tight packing, - namely LUT rotation. Also the current site router is relatively - untested, so legal configurations may be rejected and illegal - configurations may be accepted. #### FPGA interchange fabrics