From d2ac6dffbcd1bcb6dbf3cadd03f33794b2af5c80 Mon Sep 17 00:00:00 2001 From: "William D. Jones" Date: Fri, 29 Oct 2021 18:00:05 -0400 Subject: [PATCH 1/6] machxo2: Correct which PIO wires get adjusted when writing text bitstream. Add verbose logging for adjustments. --- machxo2/bitstream.cc | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/machxo2/bitstream.cc b/machxo2/bitstream.cc index ed67975a..913d7b58 100644 --- a/machxo2/bitstream.cc +++ b/machxo2/bitstream.cc @@ -19,6 +19,7 @@ */ #include +#include #include "bitstream.h" #include "config.h" @@ -59,13 +60,20 @@ static std::string get_trellis_wirename(Context *ctx, Location loc, WireId wire) // Handle MachXO2's wonderful naming quirks for wires in left/right tiles, whose // relative coords push them outside the bounds of the chip. + // Indents are based on wires proximity/purpose. auto is_pio_wire = [](std::string name) { return (name.find("DI") != std::string::npos || name.find("JDI") != std::string::npos || - name.find("PADD") != std::string::npos || name.find("INDD") != std::string::npos || - name.find("IOLDO") != std::string::npos || name.find("IOLTO") != std::string::npos || - name.find("JCE") != std::string::npos || name.find("JCLK") != std::string::npos || - name.find("JLSR") != std::string::npos || name.find("JONEG") != std::string::npos || - name.find("JOPOS") != std::string::npos || name.find("JTS") != std::string::npos || + name.find("PADD") != std::string::npos || name.find("INDD") != std::string::npos || + name.find("IOLDO") != std::string::npos || name.find("IOLTO") != std::string::npos || + // JCE0-3, JCLK0-3, JLSR0-3 connect to PIO wires named JCEA-D, JCLKA-D, JLSRA-D. + name.find("JCEA") != std::string::npos || name.find("JCEB") != std::string::npos || + name.find("JCEC") != std::string::npos || name.find("JCED") != std::string::npos || + name.find("JCLKA") != std::string::npos || name.find("JCLKB") != std::string::npos || + name.find("JCLKC") != std::string::npos || name.find("JCLKD") != std::string::npos || + name.find("JLSRA") != std::string::npos || name.find("JLSRB") != std::string::npos || + name.find("JLSRC") != std::string::npos || name.find("JLSRD") != std::string::npos || + name.find("JONEG") != std::string::npos || name.find("JOPOS") != std::string::npos || + name.find("JTS") != std::string::npos || name.find("JIN") != std::string::npos || name.find("JIP") != std::string::npos || // Connections to global mux name.find("JINCK") != std::string::npos); @@ -92,10 +100,19 @@ static std::string get_trellis_wirename(Context *ctx, Location loc, WireId wire) if (loc == wire.location) { // TODO: JINCK is not currently handled by this. if (is_pio_wire(basename)) { - if (wire.location.x == 0) - return "W1_" + basename; - else if (wire.location.x == max_col) - return "E1_" + basename; + if (wire.location.x == 0) { + std::string pio_name = "W1_" + basename; + if (ctx->verbose) + log_info("PIO wire %s was adjusted by W1 to form Trellis name %s.\n", \ + ctx->nameOfWire(wire), pio_name.c_str()); + return pio_name; + } else if (wire.location.x == max_col) { + std::string pio_name = "E1_" + basename; + if (ctx->verbose) + log_info("PIO wire %s was adjusted by E1 to form Trellis name %s.\n", \ + ctx->nameOfWire(wire), pio_name.c_str()); + return pio_name; + } } return basename; } From 365a871908f3b945fb19cfefb642fbde87dbf584 Mon Sep 17 00:00:00 2001 From: "William D. Jones" Date: Wed, 3 Nov 2021 13:53:57 -0400 Subject: [PATCH 2/6] machxo2: Add packing logic to forbid designs lacking FACADE_IO top-level ports. --- machxo2/pack.cc | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/machxo2/pack.cc b/machxo2/pack.cc index 66d2d411..231cc99d 100644 --- a/machxo2/pack.cc +++ b/machxo2/pack.cc @@ -229,6 +229,44 @@ static bool is_nextpnr_iob(Context *ctx, CellInfo *cell) static bool is_facade_iob(const Context *ctx, const CellInfo *cell) { return cell->type == id_FACADE_IO; } +static bool nextpnr_iob_connects_only_facade_iob(Context *ctx, CellInfo *iob, NetInfo *&top) { + NPNR_ASSERT(is_nextpnr_iob(ctx, iob)); + + if(iob->type == ctx->id("$nextpnr_ibuf")) { + NetInfo *o = iob->ports.at(id_O).net; + top = o; + + CellInfo *fio = net_only_drives(ctx, o, is_facade_iob, id_PAD, true); + return fio != nullptr; + } else if(iob->type == ctx->id("$nextpnr_obuf")) { + NetInfo *i = iob->ports.at(id_I).net; + top = i; + + // If connected to a FACADE_IO PAD, the net attached to an I port of an + // $nextpnr_obuf will not have a driver, only users; an inout port + // like PAD cannot be a driver in nextpnr. So net_driven_by won't + // return anything. We exclude the IOB as one of the two users because + // we already know that the net drives the $nextpnr_obuf. + CellInfo *fio = net_only_drives(ctx, i, is_facade_iob, id_PAD, true, iob); + return fio != nullptr; + } else if(iob->type == ctx->id("$nextpnr_iobuf")) { + NetInfo *o = iob->ports.at(id_O).net; + top = o; + + // When split_io is enabled in a frontend (it is for JSON), the I and O + // ports of a $nextpnr_iobuf are split; the I port connects to the + // driver of the original net before IOB insertion, and the O port + // connects everything else. Because FACADE_IO PADs cannot be a driver + // in nextpnr, the we can safely ignore the I port of an $nextpnr_iobuf + // for any JSON input we're interested in accepting. + CellInfo *fio_o = net_only_drives(ctx, o, is_facade_iob, id_PAD, true); + return fio_o != nullptr; + } + + // Unreachable! + NPNR_ASSERT(false); +} + // Pack IO buffers- Right now, all this does is remove $nextpnr_[io]buf cells. // User is expected to manually instantiate FACADE_IO with BEL/IO_TYPE // attributes. @@ -241,6 +279,14 @@ static void pack_io(Context *ctx) for (auto &cell : ctx->cells) { CellInfo *ci = cell.second.get(); if (is_nextpnr_iob(ctx, ci)) { + NetInfo *top; + + if(!nextpnr_iob_connects_only_facade_iob(ctx, ci, top)) + log_error("Top level net '%s' is not connected to a FACADE_IO PAD port.\n", top->name.c_str(ctx)); + + if (ctx->verbose) + log_info("Removing top-level IOBUF '%s' of type '%s'\n", ci->name.c_str(ctx), ci->type.c_str(ctx)); + for (auto &p : ci->ports) disconnect_port(ctx, ci, p.first); packed_cells.insert(ci->name); From be3788fa3004cfee1058293d5da6ba758f913e7e Mon Sep 17 00:00:00 2001 From: "William D. Jones" Date: Fri, 5 Nov 2021 12:49:24 -0400 Subject: [PATCH 3/6] machxo2: Remove -noiopad option when generating miters for post-pnr verification. --- machxo2/examples/mitertest.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/machxo2/examples/mitertest.sh b/machxo2/examples/mitertest.sh index cfae28b7..feafc0dd 100644 --- a/machxo2/examples/mitertest.sh +++ b/machxo2/examples/mitertest.sh @@ -61,6 +61,7 @@ do_smt() { miter -equiv -make_assert gold gate ${2}${1}_miter hierarchy -top ${2}${1}_miter; proc; opt_clean + flatten t:*FACADE_IO* write_verilog ${2}${1}_miter.v write_smt2 ${2}${1}_miter.smt2" @@ -71,7 +72,7 @@ do_smt() { set -ex ${YOSYS:-yosys} -p "read_verilog ${1}.v - synth_machxo2 -noiopad -json ${1}.json" + synth_machxo2 -json ${1}.json" ${NEXTPNR:-../../nextpnr-machxo2} $NEXTPNR_MODE --1200 --package QFN32 --no-iobs --json ${1}.json --write ${2}${1}.json ${YOSYS:-yosys} -p "read_verilog -lib +/machxo2/cells_sim.v read_json ${2}${1}.json From 4d757922572e6009777c6d533d410cdb2257c363 Mon Sep 17 00:00:00 2001 From: "William D. Jones" Date: Fri, 5 Nov 2021 13:26:05 -0400 Subject: [PATCH 4/6] machxo2: Remove no-iobs option. It was always enabled and should remain an implementation detail. --- machxo2/examples/demo-vhdl.sh | 2 +- machxo2/examples/demo.sh | 2 +- machxo2/examples/mitertest.sh | 2 +- machxo2/examples/simple.sh | 2 +- machxo2/examples/simtest.sh | 2 +- machxo2/main.cc | 3 --- 6 files changed, 5 insertions(+), 8 deletions(-) diff --git a/machxo2/examples/demo-vhdl.sh b/machxo2/examples/demo-vhdl.sh index 4bdab54a..ed1f7d80 100644 --- a/machxo2/examples/demo-vhdl.sh +++ b/machxo2/examples/demo-vhdl.sh @@ -19,6 +19,6 @@ set -ex ${YOSYS:-yosys} -p "ghdl --std=08 prims.vhd ${1}.vhd -e; attrmap -tocase LOC synth_machxo2 -json ${1}-vhdl.json" -${NEXTPNR:-../../nextpnr-machxo2} --1200 --package QFN32 --no-iobs --json $1-vhdl.json --textcfg $1-vhdl.txt +${NEXTPNR:-../../nextpnr-machxo2} --1200 --package QFN32 --json $1-vhdl.json --textcfg $1-vhdl.txt ecppack --compress $DB_ARG $1-vhdl.txt $1-vhdl.bit tinyproga -b $1-vhdl.bit diff --git a/machxo2/examples/demo.sh b/machxo2/examples/demo.sh index 00cb0cd0..634fbb4d 100644 --- a/machxo2/examples/demo.sh +++ b/machxo2/examples/demo.sh @@ -17,6 +17,6 @@ fi set -ex ${YOSYS:-yosys} -p "synth_machxo2 -json $1.json" $1.v -${NEXTPNR:-../../nextpnr-machxo2} --1200 --package QFN32 --no-iobs --json $1.json --textcfg $1.txt +${NEXTPNR:-../../nextpnr-machxo2} --1200 --package QFN32 --json $1.json --textcfg $1.txt ecppack --compress $DB_ARG $1.txt $1.bit tinyproga -b $1.bit diff --git a/machxo2/examples/mitertest.sh b/machxo2/examples/mitertest.sh index feafc0dd..b7ec2695 100644 --- a/machxo2/examples/mitertest.sh +++ b/machxo2/examples/mitertest.sh @@ -73,7 +73,7 @@ set -ex ${YOSYS:-yosys} -p "read_verilog ${1}.v synth_machxo2 -json ${1}.json" -${NEXTPNR:-../../nextpnr-machxo2} $NEXTPNR_MODE --1200 --package QFN32 --no-iobs --json ${1}.json --write ${2}${1}.json +${NEXTPNR:-../../nextpnr-machxo2} $NEXTPNR_MODE --1200 --package QFN32 --json ${1}.json --write ${2}${1}.json ${YOSYS:-yosys} -p "read_verilog -lib +/machxo2/cells_sim.v read_json ${2}${1}.json clean -purge diff --git a/machxo2/examples/simple.sh b/machxo2/examples/simple.sh index 1da60933..69706b9c 100644 --- a/machxo2/examples/simple.sh +++ b/machxo2/examples/simple.sh @@ -26,7 +26,7 @@ set -ex ${YOSYS:-yosys} -p "read_verilog ${1}.v synth_machxo2 -json ${1}.json show -format png -prefix ${1}" -${NEXTPNR:-../../nextpnr-machxo2} $NEXTPNR_MODE --1200 --package QFN32 --no-iobs --json ${1}.json --write ${2}${1}.json +${NEXTPNR:-../../nextpnr-machxo2} $NEXTPNR_MODE --1200 --package QFN32 --json ${1}.json --write ${2}${1}.json ${YOSYS:-yosys} -p "read_verilog -lib +/machxo2/cells_sim.v read_json ${2}${1}.json clean -purge diff --git a/machxo2/examples/simtest.sh b/machxo2/examples/simtest.sh index 2c7f6f30..0adf1751 100644 --- a/machxo2/examples/simtest.sh +++ b/machxo2/examples/simtest.sh @@ -30,7 +30,7 @@ set -ex ${YOSYS:-yosys} -p "read_verilog ${1}.v synth_machxo2 -json ${1}.json" -${NEXTPNR:-../../nextpnr-machxo2} $NEXTPNR_MODE --1200 --package QFN32 --no-iobs --json ${1}.json --write ${2}${1}.json +${NEXTPNR:-../../nextpnr-machxo2} $NEXTPNR_MODE --1200 --package QFN32 --json ${1}.json --write ${2}${1}.json ${YOSYS:-yosys} -p "read_verilog -lib +/machxo2/cells_sim.v read_json ${2}${1}.json clean -purge diff --git a/machxo2/main.cc b/machxo2/main.cc index 53b765fb..e29e117b 100644 --- a/machxo2/main.cc +++ b/machxo2/main.cc @@ -69,7 +69,6 @@ po::options_description MachXO2CommandHandler::getArchOptions() // specific.add_options()("lpf", po::value>(), "LPF pin constraint file(s)"); - specific.add_options()("no-iobs", "disable automatic IO buffer insertion (unimplemented- always enabled)"); return specific; } @@ -108,8 +107,6 @@ std::unique_ptr MachXO2CommandHandler::createContext(dict(new Context(chipArgs)); - if (vm.count("no-iobs")) - ctx->settings[ctx->id("disable_iobs")] = Property::State::S1; return ctx; } From 78ce9971ff9c0c5ed1b38e5a3423f325c9a1d6e5 Mon Sep 17 00:00:00 2001 From: "William D. Jones" Date: Fri, 5 Nov 2021 13:29:01 -0400 Subject: [PATCH 5/6] README.md: Add machxo2 arch to list of (experimental) supported devices. --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c1b28f15..14128b87 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Currently nextpnr supports: * Lattice Nexus devices supported by [Project Oxide](https://github.com/gatecat/prjoxide) * Gowin LittleBee devices supported by [Project Apicula](https://github.com/YosysHQ/apicula) * *(experimental)* Cyclone V devices supported by [Mistral](https://github.com/Ravenslofty/mistral) + * *(experimental)* Lattice MachXO2 devices supported by [Project Trellis](https://github.com/YosysHQ/prjtrellis) * *(experimental)* a "generic" back-end for user-defined architectures There is some work in progress towards [support for Xilinx devices](https://github.com/gatecat/nextpnr-xilinx/) but it is not upstream and not intended for end users at the present time. We hope to see more FPGA families supported in the future. We would love your help in developing this awesome new project! From 064b6d808e9b91c93258a53ac1a18b3b84431545 Mon Sep 17 00:00:00 2001 From: "William D. Jones" Date: Thu, 16 Dec 2021 17:09:29 -0500 Subject: [PATCH 6/6] clangformat. --- machxo2/bitstream.cc | 10 ++++++---- machxo2/pack.cc | 11 ++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/machxo2/bitstream.cc b/machxo2/bitstream.cc index 913d7b58..f624d91b 100644 --- a/machxo2/bitstream.cc +++ b/machxo2/bitstream.cc @@ -62,6 +62,7 @@ static std::string get_trellis_wirename(Context *ctx, Location loc, WireId wire) // relative coords push them outside the bounds of the chip. // Indents are based on wires proximity/purpose. auto is_pio_wire = [](std::string name) { + // clang-format off return (name.find("DI") != std::string::npos || name.find("JDI") != std::string::npos || name.find("PADD") != std::string::npos || name.find("INDD") != std::string::npos || name.find("IOLDO") != std::string::npos || name.find("IOLTO") != std::string::npos || @@ -77,6 +78,7 @@ static std::string get_trellis_wirename(Context *ctx, Location loc, WireId wire) name.find("JIN") != std::string::npos || name.find("JIP") != std::string::npos || // Connections to global mux name.find("JINCK") != std::string::npos); + // clang-format on }; if (prefix2 == "G_" || prefix2 == "L_" || prefix2 == "R_" || prefix7 == "BRANCH_") @@ -103,14 +105,14 @@ static std::string get_trellis_wirename(Context *ctx, Location loc, WireId wire) if (wire.location.x == 0) { std::string pio_name = "W1_" + basename; if (ctx->verbose) - log_info("PIO wire %s was adjusted by W1 to form Trellis name %s.\n", \ - ctx->nameOfWire(wire), pio_name.c_str()); + log_info("PIO wire %s was adjusted by W1 to form Trellis name %s.\n", ctx->nameOfWire(wire), + pio_name.c_str()); return pio_name; } else if (wire.location.x == max_col) { std::string pio_name = "E1_" + basename; if (ctx->verbose) - log_info("PIO wire %s was adjusted by E1 to form Trellis name %s.\n", \ - ctx->nameOfWire(wire), pio_name.c_str()); + log_info("PIO wire %s was adjusted by E1 to form Trellis name %s.\n", ctx->nameOfWire(wire), + pio_name.c_str()); return pio_name; } } diff --git a/machxo2/pack.cc b/machxo2/pack.cc index 231cc99d..c53229ba 100644 --- a/machxo2/pack.cc +++ b/machxo2/pack.cc @@ -229,16 +229,17 @@ static bool is_nextpnr_iob(Context *ctx, CellInfo *cell) static bool is_facade_iob(const Context *ctx, const CellInfo *cell) { return cell->type == id_FACADE_IO; } -static bool nextpnr_iob_connects_only_facade_iob(Context *ctx, CellInfo *iob, NetInfo *&top) { +static bool nextpnr_iob_connects_only_facade_iob(Context *ctx, CellInfo *iob, NetInfo *&top) +{ NPNR_ASSERT(is_nextpnr_iob(ctx, iob)); - if(iob->type == ctx->id("$nextpnr_ibuf")) { + if (iob->type == ctx->id("$nextpnr_ibuf")) { NetInfo *o = iob->ports.at(id_O).net; top = o; CellInfo *fio = net_only_drives(ctx, o, is_facade_iob, id_PAD, true); return fio != nullptr; - } else if(iob->type == ctx->id("$nextpnr_obuf")) { + } else if (iob->type == ctx->id("$nextpnr_obuf")) { NetInfo *i = iob->ports.at(id_I).net; top = i; @@ -249,7 +250,7 @@ static bool nextpnr_iob_connects_only_facade_iob(Context *ctx, CellInfo *iob, Ne // we already know that the net drives the $nextpnr_obuf. CellInfo *fio = net_only_drives(ctx, i, is_facade_iob, id_PAD, true, iob); return fio != nullptr; - } else if(iob->type == ctx->id("$nextpnr_iobuf")) { + } else if (iob->type == ctx->id("$nextpnr_iobuf")) { NetInfo *o = iob->ports.at(id_O).net; top = o; @@ -281,7 +282,7 @@ static void pack_io(Context *ctx) if (is_nextpnr_iob(ctx, ci)) { NetInfo *top; - if(!nextpnr_iob_connects_only_facade_iob(ctx, ci, top)) + if (!nextpnr_iob_connects_only_facade_iob(ctx, ci, top)) log_error("Top level net '%s' is not connected to a FACADE_IO PAD port.\n", top->name.c_str(ctx)); if (ctx->verbose)