From f8dca82a713d2d90d29c70357c5c276bed5d7862 Mon Sep 17 00:00:00 2001 From: David Shah Date: Thu, 22 Oct 2020 16:02:58 +0100 Subject: [PATCH] nexus: Basic support for differential IO types Signed-off-by: David Shah --- nexus/arch.h | 31 ++++++++++++++++++ nexus/fasm.cc | 89 +++++++++++++++++++++++++++++++++++++++++++++++++-- nexus/io.cc | 70 ++++++++++++++++++++++++++++++++++++++++ nexus/pack.cc | 13 ++++++++ nexus/pins.cc | 6 ++++ 5 files changed, 206 insertions(+), 3 deletions(-) create mode 100644 nexus/io.cc diff --git a/nexus/arch.h b/nexus/arch.h index 5924732b..85278272 100644 --- a/nexus/arch.h +++ b/nexus/arch.h @@ -813,6 +813,30 @@ enum CellPinMux PINMUX_INV = 3, }; +// This represents the various kinds of IO pins +enum IOStyle +{ + IOBANK_WR = 0x1, // needs wide range IO bank + IOBANK_HP = 0x2, // needs high perf IO bank + + IOMODE_REF = 0x10, // IO is referenced + IOMODE_DIFF = 0x20, // IO is true differential + IOMODE_PSEUDO_DIFF = 0x40, // IO is pseduo differential + + IOSTYLE_SE_WR = 0x01, // single ended, wide range + IOSTYLE_SE_HP = 0x02, // single ended, high perf + IOSTYLE_PD_WR = 0x41, // pseudo diff, wide range + + IOSTYLE_REF_HP = 0x12, // referenced high perf + IOSTYLE_DIFF_HP = 0x22, // differential high perf +}; + +struct IOTypeData +{ + IOStyle style; + int vcco; // required Vcco in 10mV +}; + // ----------------------------------------------------------------------- const int bba_version = @@ -1455,6 +1479,13 @@ struct Arch : BaseCtx const PadInfoPOD *get_bel_pad(BelId bel) const; std::string get_pad_functions(const PadInfoPOD *pad) const; + // ------------------------------------------------- + // Data about different IO standard, mostly used by bitgen + static const std::unordered_map io_types; + int get_io_type_vcc(const std::string &io_type) const; + bool is_io_type_diff(const std::string &io_type) const; + bool is_io_type_ref(const std::string &io_type) const; + // ------------------------------------------------- // List of IO constraints, used by PDC parser diff --git a/nexus/fasm.cc b/nexus/fasm.cc index fdab3bca..bd710ae4 100644 --- a/nexus/fasm.cc +++ b/nexus/fasm.cc @@ -125,6 +125,18 @@ struct NexusFasmWriter write_bit(stringf("%s.%s", name.c_str(), fnd->second.c_str())); } } + void write_ioattr_postfix(const CellInfo *cell, const std::string &name, const std::string &postfix, + const std::string &defval = "") + { + auto fnd = cell->attrs.find(ctx->id(name)); + if (fnd == cell->attrs.end()) { + if (!defval.empty()) + write_bit(stringf("%s_%s.%s", name.c_str(), postfix.c_str(), defval.c_str())); + } else { + write_bit(stringf("%s_%s.%s", name.c_str(), postfix.c_str(), fnd->second.c_str())); + } + } + // Gets the full name of a tile std::string tile_name(int loc, const PhysicalTileInfoPOD &tile) { @@ -321,6 +333,16 @@ struct NexusFasmWriter std::unordered_set used_io; + struct BankConfig + { + bool diff_used = false; + bool lvds_used = false; + bool slvs_used = false; + bool dphy_used = false; + }; + + std::map bank_cfg; + // Write config for an SEIO33_CORE cell void write_io33(const CellInfo *cell) { @@ -359,6 +381,54 @@ struct NexusFasmWriter const char *iodir = is_input ? "INPUT" : (is_output ? "OUTPUT" : "BIDIR"); write_bit(stringf("BASE_TYPE.%s_%s", iodir, str_or_default(cell->attrs, id_IO_TYPE, "LVCMOS18H").c_str())); write_ioattr(cell, "PULLMODE", "NONE"); + pop(); + write_cell_muxes(cell); + pop(); + } + // Write config for an SEIO18_CORE cell + void write_diffio18(const CellInfo *cell) + { + BelId bel = cell->bel; + + Loc bel_loc = ctx->getBelLocation(bel); + for (int i = 0; i < 2; i++) { + // Mark both A and B pins as used + used_io.insert(ctx->getBelByLocation(Loc(bel_loc.x, bel_loc.y, i))); + } + push_belgroup(bel); + push("PIOA"); + push("DIFFIO18"); + + auto &bank = bank_cfg[ctx->get_bel_pad(ctx->getBelByLocation(Loc(bel_loc.x, bel_loc.y, 0)))->bank]; + + bank.diff_used = true; + + const NetInfo *t = get_net_or_empty(cell, id_T); + auto tmux = ctx->get_cell_pinmux(cell, id_T); + bool is_input = false, is_output = false; + if (tmux == PINMUX_0) { + is_output = true; + } else if (tmux == PINMUX_1 || t == nullptr) { + is_input = true; + } + + const char *iodir = is_input ? "INPUT" : (is_output ? "OUTPUT" : "BIDIR"); + std::string type = str_or_default(cell->attrs, id_IO_TYPE, "LVDS"); + write_bit(stringf("BASE_TYPE.%s_%s", iodir, type.c_str())); + if (type == "LVDS") { + write_ioattr_postfix(cell, "DIFFDRIVE", "LVDS", "3P5"); + bank.lvds_used = true; + } else if (type == "SLVS") { + write_ioattr_postfix(cell, "DIFFDRIVE", "SLVS", "2P0"); + bank.slvs_used = true; + } else if (type == "MIPI_DPHY") { + write_ioattr_postfix(cell, "DIFFDRIVE", "MIPI_DPHY", "2P0"); + bank.dphy_used = true; + } + + write_ioattr(cell, "PULLMODE", "FAILSAFE"); + write_ioattr(cell, "DIFFRESISTOR"); + pop(); write_cell_muxes(cell); pop(2); } @@ -505,9 +575,20 @@ struct NexusFasmWriter void write_bankcfg() { for (int i = 0; i < 8; i++) { - if (i >= 3 && i <= 5) - continue; // 1.8V banks, skip for now - write_bit(stringf("GLOBAL.BANK%d.VCC.3V3", i)); + if (i >= 3 && i <= 5) { + // 1.8V banks + push(stringf("GLOBAL.BANK%d", i)); + auto &bank = bank_cfg[i]; + write_bit("DIFF_IO.ON", bank.diff_used); + write_bit("LVDS_IO.ON", bank.lvds_used); + write_bit("SLVS_IO.ON", bank.slvs_used); + write_bit("MIPI_DPHY_IO.ON", bank.dphy_used); + + pop(); + } else { + // 3.3V banks, this should eventually be set based on the bank config + write_bit(stringf("GLOBAL.BANK%d.VCC.3V3", i)); + } } blank(); } @@ -536,6 +617,8 @@ struct NexusFasmWriter write_io33(ci); else if (ci->type == id_SEIO18_CORE) write_io18(ci); + else if (ci->type == id_DIFFIO18_CORE) + write_diffio18(ci); else if (ci->type == id_OSC_CORE) write_osc(ci); else if (ci->type == id_OXIDE_EBR) diff --git a/nexus/io.cc b/nexus/io.cc new file mode 100644 index 00000000..fd5e584f --- /dev/null +++ b/nexus/io.cc @@ -0,0 +1,70 @@ +/* + * nextpnr -- Next Generation Place and Route + * + * Copyright (C) 2020 David Shah + * + * + * 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 "log.h" +#include "nextpnr.h" + +NEXTPNR_NAMESPACE_BEGIN + +const std::unordered_map Arch::io_types = { + {"LVCMOS33", {IOSTYLE_SE_WR, 330}}, {"LVCMOS25", {IOSTYLE_SE_WR, 250}}, + {"LVCMOS18", {IOSTYLE_SE_WR, 180}}, {"LVCMOS15", {IOSTYLE_SE_WR, 150}}, + {"LVCMOS12", {IOSTYLE_SE_WR, 120}}, {"LVCMOS10", {IOSTYLE_SE_WR, 120}}, + + {"LVCMOS33D", {IOSTYLE_PD_WR, 330}}, {"LVCMOS25D", {IOSTYLE_PD_WR, 250}}, + + {"LVCMOS18H", {IOSTYLE_SE_HP, 180}}, {"LVCMOS15H", {IOSTYLE_SE_HP, 150}}, + {"LVCMOS12H", {IOSTYLE_SE_HP, 120}}, {"LVCMOS10R", {IOSTYLE_SE_HP, 120}}, + {"LVCMOS10H", {IOSTYLE_SE_HP, 100}}, + + {"HSTL15_I", {IOSTYLE_REF_HP, 150}}, {"SSTL15_I", {IOSTYLE_REF_HP, 150}}, + {"SSTL15_II", {IOSTYLE_REF_HP, 150}}, {"SSTL135_I", {IOSTYLE_REF_HP, 135}}, + {"SSTL135_II", {IOSTYLE_REF_HP, 135}}, {"HSUL12", {IOSTYLE_REF_HP, 120}}, + + {"LVDS", {IOSTYLE_DIFF_HP, 180}}, {"SLVS", {IOSTYLE_DIFF_HP, 120}}, + {"MIPI_DPHY", {IOSTYLE_DIFF_HP, 120}}, {"HSUL12D", {IOSTYLE_DIFF_HP, 120}}, + + {"HSTL15D_I", {IOSTYLE_DIFF_HP, 150}}, {"SSTL15D_I", {IOSTYLE_DIFF_HP, 150}}, + {"SSTL15D_II", {IOSTYLE_DIFF_HP, 150}}, {"SSTL135D_I", {IOSTYLE_DIFF_HP, 135}}, + {"SSTL135D_II", {IOSTYLE_DIFF_HP, 135}}, {"HSUL12D", {IOSTYLE_DIFF_HP, 120}}, +}; + +int Arch::get_io_type_vcc(const std::string &io_type) const +{ + if (!io_types.count(io_type)) + log_error("IO type '%s' not supported.\n", io_type.c_str()); + return io_types.at(io_type).vcco; +} + +bool Arch::is_io_type_diff(const std::string &io_type) const +{ + if (!io_types.count(io_type)) + log_error("IO type '%s' not supported.\n", io_type.c_str()); + return io_types.at(io_type).style & IOMODE_DIFF; +} + +bool Arch::is_io_type_ref(const std::string &io_type) const +{ + if (!io_types.count(io_type)) + log_error("IO type '%s' not supported.\n", io_type.c_str()); + return io_types.at(io_type).style & IOMODE_REF; +} + +NEXTPNR_NAMESPACE_END diff --git a/nexus/pack.cc b/nexus/pack.cc index abe963ba..e12ce305 100644 --- a/nexus/pack.cc +++ b/nexus/pack.cc @@ -594,6 +594,19 @@ struct NexusPacker // Get IO type for reporting purposes std::string io_type = str_or_default(ci->attrs, id_IO_TYPE, "LVCMOS33"); + if (ctx->is_io_type_diff(io_type)) { + // Convert from SEIO18 to DIFFIO18 + if (ctx->getBelType(bel) != id_SEIO18_CORE) + log_error("IO '%s' uses differential type '%s' but is placed on wide range pin '%s'\n", + ctx->nameOf(ci), io_type.c_str(), loc.c_str()); + Loc bel_loc = ctx->getBelLocation(bel); + if (bel_loc.z != 0) + log_error("IO '%s' uses differential type '%s' but is placed on 'B' side pin '%s'\n", + ctx->nameOf(ci), io_type.c_str(), loc.c_str()); + bel_loc.z = 2; + bel = ctx->getBelByLocation(bel_loc); + } + log_info("Constraining %s IO '%s' to pin %s (%s%sbel %s)\n", io_type.c_str(), ctx->nameOf(ci), loc.c_str(), func.c_str(), func.empty() ? "" : "; ", ctx->nameOfBel(bel)); ci->attrs[id_BEL] = ctx->getBelName(bel).str(ctx); diff --git a/nexus/pins.cc b/nexus/pins.cc index de92f2b6..f2946657 100644 --- a/nexus/pins.cc +++ b/nexus/pins.cc @@ -58,6 +58,12 @@ static const std::unordered_map base_cell_pin_data {id_B, PINSTYLE_DEDI}, {{}, PINSTYLE_PU}, }}, + {id_DIFFIO18_CORE, + { + {id_T, PINSTYLE_T}, + {id_B, PINSTYLE_DEDI}, + {{}, PINSTYLE_PU}, + }}, {id_SEIO33_CORE, { {id_T, PINSTYLE_T},