nexus: Basic support for differential IO types
Signed-off-by: David Shah <dave@ds0.me>
This commit is contained in:
parent
f749038959
commit
f8dca82a71
31
nexus/arch.h
31
nexus/arch.h
@ -813,6 +813,30 @@ enum CellPinMux
|
|||||||
PINMUX_INV = 3,
|
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 =
|
const int bba_version =
|
||||||
@ -1455,6 +1479,13 @@ struct Arch : BaseCtx
|
|||||||
const PadInfoPOD *get_bel_pad(BelId bel) const;
|
const PadInfoPOD *get_bel_pad(BelId bel) const;
|
||||||
std::string get_pad_functions(const PadInfoPOD *pad) const;
|
std::string get_pad_functions(const PadInfoPOD *pad) const;
|
||||||
|
|
||||||
|
// -------------------------------------------------
|
||||||
|
// Data about different IO standard, mostly used by bitgen
|
||||||
|
static const std::unordered_map<std::string, IOTypeData> 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
|
// List of IO constraints, used by PDC parser
|
||||||
|
@ -125,6 +125,18 @@ struct NexusFasmWriter
|
|||||||
write_bit(stringf("%s.%s", name.c_str(), fnd->second.c_str()));
|
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
|
// Gets the full name of a tile
|
||||||
std::string tile_name(int loc, const PhysicalTileInfoPOD &tile)
|
std::string tile_name(int loc, const PhysicalTileInfoPOD &tile)
|
||||||
{
|
{
|
||||||
@ -321,6 +333,16 @@ struct NexusFasmWriter
|
|||||||
|
|
||||||
std::unordered_set<BelId> used_io;
|
std::unordered_set<BelId> used_io;
|
||||||
|
|
||||||
|
struct BankConfig
|
||||||
|
{
|
||||||
|
bool diff_used = false;
|
||||||
|
bool lvds_used = false;
|
||||||
|
bool slvs_used = false;
|
||||||
|
bool dphy_used = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::map<int, BankConfig> bank_cfg;
|
||||||
|
|
||||||
// Write config for an SEIO33_CORE cell
|
// Write config for an SEIO33_CORE cell
|
||||||
void write_io33(const CellInfo *cell)
|
void write_io33(const CellInfo *cell)
|
||||||
{
|
{
|
||||||
@ -359,6 +381,54 @@ struct NexusFasmWriter
|
|||||||
const char *iodir = is_input ? "INPUT" : (is_output ? "OUTPUT" : "BIDIR");
|
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_bit(stringf("BASE_TYPE.%s_%s", iodir, str_or_default(cell->attrs, id_IO_TYPE, "LVCMOS18H").c_str()));
|
||||||
write_ioattr(cell, "PULLMODE", "NONE");
|
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);
|
write_cell_muxes(cell);
|
||||||
pop(2);
|
pop(2);
|
||||||
}
|
}
|
||||||
@ -505,10 +575,21 @@ struct NexusFasmWriter
|
|||||||
void write_bankcfg()
|
void write_bankcfg()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
if (i >= 3 && i <= 5)
|
if (i >= 3 && i <= 5) {
|
||||||
continue; // 1.8V banks, skip for now
|
// 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));
|
write_bit(stringf("GLOBAL.BANK%d.VCC.3V3", i));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
blank();
|
blank();
|
||||||
}
|
}
|
||||||
// Write out FASM for the whole design
|
// Write out FASM for the whole design
|
||||||
@ -536,6 +617,8 @@ struct NexusFasmWriter
|
|||||||
write_io33(ci);
|
write_io33(ci);
|
||||||
else if (ci->type == id_SEIO18_CORE)
|
else if (ci->type == id_SEIO18_CORE)
|
||||||
write_io18(ci);
|
write_io18(ci);
|
||||||
|
else if (ci->type == id_DIFFIO18_CORE)
|
||||||
|
write_diffio18(ci);
|
||||||
else if (ci->type == id_OSC_CORE)
|
else if (ci->type == id_OSC_CORE)
|
||||||
write_osc(ci);
|
write_osc(ci);
|
||||||
else if (ci->type == id_OXIDE_EBR)
|
else if (ci->type == id_OXIDE_EBR)
|
||||||
|
70
nexus/io.cc
Normal file
70
nexus/io.cc
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* nextpnr -- Next Generation Place and Route
|
||||||
|
*
|
||||||
|
* Copyright (C) 2020 David Shah <dave@ds0.me>
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* 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<std::string, IOTypeData> 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
|
@ -594,6 +594,19 @@ struct NexusPacker
|
|||||||
// Get IO type for reporting purposes
|
// Get IO type for reporting purposes
|
||||||
std::string io_type = str_or_default(ci->attrs, id_IO_TYPE, "LVCMOS33");
|
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),
|
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));
|
loc.c_str(), func.c_str(), func.empty() ? "" : "; ", ctx->nameOfBel(bel));
|
||||||
ci->attrs[id_BEL] = ctx->getBelName(bel).str(ctx);
|
ci->attrs[id_BEL] = ctx->getBelName(bel).str(ctx);
|
||||||
|
@ -58,6 +58,12 @@ static const std::unordered_map<IdString, Arch::CellPinsData> base_cell_pin_data
|
|||||||
{id_B, PINSTYLE_DEDI},
|
{id_B, PINSTYLE_DEDI},
|
||||||
{{}, PINSTYLE_PU},
|
{{}, PINSTYLE_PU},
|
||||||
}},
|
}},
|
||||||
|
{id_DIFFIO18_CORE,
|
||||||
|
{
|
||||||
|
{id_T, PINSTYLE_T},
|
||||||
|
{id_B, PINSTYLE_DEDI},
|
||||||
|
{{}, PINSTYLE_PU},
|
||||||
|
}},
|
||||||
{id_SEIO33_CORE,
|
{id_SEIO33_CORE,
|
||||||
{
|
{
|
||||||
{id_T, PINSTYLE_T},
|
{id_T, PINSTYLE_T},
|
||||||
|
Loading…
Reference in New Issue
Block a user