Merge pull request #79 from YosysHQ/ice40lvds
ice40: Adding LVDS input support
This commit is contained in:
commit
07cf349ee4
@ -959,7 +959,6 @@ void Arch::assignArchInfo()
|
|||||||
|
|
||||||
void Arch::assignCellInfo(CellInfo *cell)
|
void Arch::assignCellInfo(CellInfo *cell)
|
||||||
{
|
{
|
||||||
cell->belType = cell->type;
|
|
||||||
if (cell->type == id_ICESTORM_LC) {
|
if (cell->type == id_ICESTORM_LC) {
|
||||||
cell->lcInfo.dffEnable = bool_or_default(cell->params, id_DFF_ENABLE);
|
cell->lcInfo.dffEnable = bool_or_default(cell->params, id_DFF_ENABLE);
|
||||||
cell->lcInfo.carryEnable = bool_or_default(cell->params, id_CARRY_ENABLE);
|
cell->lcInfo.carryEnable = bool_or_default(cell->params, id_CARRY_ENABLE);
|
||||||
@ -976,6 +975,8 @@ void Arch::assignCellInfo(CellInfo *cell)
|
|||||||
cell->lcInfo.inputCount++;
|
cell->lcInfo.inputCount++;
|
||||||
if (get_net_or_empty(cell, id_I3))
|
if (get_net_or_empty(cell, id_I3))
|
||||||
cell->lcInfo.inputCount++;
|
cell->lcInfo.inputCount++;
|
||||||
|
} else if (cell->type == id_SB_IO) {
|
||||||
|
cell->ioInfo.lvds = str_or_default(cell->params, id_IO_STANDARD, "SB_LVCMOS") == "SB_LVDS_INPUT";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ bool Arch::logicCellsCompatible(const CellInfo** it, const size_t size) const
|
|||||||
int locals_count = 0;
|
int locals_count = 0;
|
||||||
|
|
||||||
for (auto cell : boost::make_iterator_range(it, it+size)) {
|
for (auto cell : boost::make_iterator_range(it, it+size)) {
|
||||||
NPNR_ASSERT(cell->belType == id_ICESTORM_LC);
|
NPNR_ASSERT(cell->type == id_ICESTORM_LC);
|
||||||
if (cell->lcInfo.dffEnable) {
|
if (cell->lcInfo.dffEnable) {
|
||||||
if (!dffs_exist) {
|
if (!dffs_exist) {
|
||||||
dffs_exist = true;
|
dffs_exist = true;
|
||||||
@ -139,6 +139,27 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loc ioLoc = getBelLocation(bel);
|
||||||
|
Loc compLoc = ioLoc;
|
||||||
|
compLoc.z = 1 - compLoc.z;
|
||||||
|
|
||||||
|
// Check LVDS pairing
|
||||||
|
if (cell->ioInfo.lvds) {
|
||||||
|
// Check correct z and complement location is free
|
||||||
|
if (ioLoc.z != 0)
|
||||||
|
return false;
|
||||||
|
BelId compBel = getBelByLocation(compLoc);
|
||||||
|
CellInfo *compCell = getBoundBelCell(compBel);
|
||||||
|
if (compCell)
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
// Check LVDS IO is not placed at complement location
|
||||||
|
BelId compBel = getBelByLocation(compLoc);
|
||||||
|
CellInfo *compCell = getBoundBelCell(compBel);
|
||||||
|
if (compCell && compCell->ioInfo.lvds)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return getBelPackagePin(bel) != "";
|
return getBelPackagePin(bel) != "";
|
||||||
} else if (cell->type == id_SB_GB) {
|
} else if (cell->type == id_SB_GB) {
|
||||||
NPNR_ASSERT(cell->ports.at(id_GLOBAL_BUFFER_OUTPUT).net != nullptr);
|
NPNR_ASSERT(cell->ports.at(id_GLOBAL_BUFFER_OUTPUT).net != nullptr);
|
||||||
|
@ -134,7 +134,6 @@ struct NetInfo;
|
|||||||
|
|
||||||
struct ArchCellInfo
|
struct ArchCellInfo
|
||||||
{
|
{
|
||||||
IdString belType;
|
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
struct
|
struct
|
||||||
@ -145,6 +144,11 @@ struct ArchCellInfo
|
|||||||
int inputCount;
|
int inputCount;
|
||||||
const NetInfo *clk, *cen, *sr;
|
const NetInfo *clk, *cen, *sr;
|
||||||
} lcInfo;
|
} lcInfo;
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
bool lvds;
|
||||||
|
// TODO: clk packing checks...
|
||||||
|
} ioInfo;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -447,6 +447,8 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
unsigned pin_type = get_param_or_def(cell.second.get(), ctx->id("PIN_TYPE"));
|
unsigned pin_type = get_param_or_def(cell.second.get(), ctx->id("PIN_TYPE"));
|
||||||
bool neg_trigger = get_param_or_def(cell.second.get(), ctx->id("NEG_TRIGGER"));
|
bool neg_trigger = get_param_or_def(cell.second.get(), ctx->id("NEG_TRIGGER"));
|
||||||
bool pullup = get_param_or_def(cell.second.get(), ctx->id("PULLUP"));
|
bool pullup = get_param_or_def(cell.second.get(), ctx->id("PULLUP"));
|
||||||
|
bool lvds = get_param_str_or_def(cell.second.get(), ctx->id("IO_STANDARD")) == "SB_LVDS_INPUT";
|
||||||
|
|
||||||
for (int i = 0; i < 6; i++) {
|
for (int i = 0; i < 6; i++) {
|
||||||
bool val = (pin_type >> i) & 0x01;
|
bool val = (pin_type >> i) & 0x01;
|
||||||
set_config(ti, config.at(y).at(x), "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i), val);
|
set_config(ti, config.at(y).at(x), "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i), val);
|
||||||
@ -457,12 +459,20 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
std::tie(iex, iey, iez) = ieren;
|
std::tie(iex, iey, iez) = ieren;
|
||||||
NPNR_ASSERT(iez != -1);
|
NPNR_ASSERT(iez != -1);
|
||||||
|
|
||||||
bool input_en = false;
|
bool input_en;
|
||||||
if ((ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_0).index] != nullptr) ||
|
if (lvds) {
|
||||||
(ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_1).index] != nullptr)) {
|
input_en = false;
|
||||||
input_en = true;
|
pullup = false;
|
||||||
|
} else {
|
||||||
|
if ((ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_0).index] != nullptr) ||
|
||||||
|
(ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_1).index] != nullptr)) {
|
||||||
|
input_en = true;
|
||||||
|
} else {
|
||||||
|
input_en = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
|
if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
|
||||||
set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), !input_en);
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), !input_en);
|
||||||
set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
|
||||||
@ -478,6 +488,33 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_35", !pullup);
|
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_35", !pullup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lvds) {
|
||||||
|
NPNR_ASSERT(z == 0);
|
||||||
|
set_config(ti, config.at(y).at(x), "IoCtrl.LVDS", true);
|
||||||
|
// Set comp IO config
|
||||||
|
auto comp_ieren = get_ieren(bi, x, y, 1);
|
||||||
|
int ciex, ciey, ciez;
|
||||||
|
std::tie(ciex, ciey, ciez) = comp_ieren;
|
||||||
|
|
||||||
|
if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
|
||||||
|
set_config(ti, config.at(ciey).at(ciex), "IoCtrl.IE_" + std::to_string(ciez), !input_en);
|
||||||
|
set_config(ti, config.at(ciey).at(ciex), "IoCtrl.REN_" + std::to_string(ciez), !pullup);
|
||||||
|
} else {
|
||||||
|
set_config(ti, config.at(ciey).at(ciex), "IoCtrl.IE_" + std::to_string(ciez), input_en);
|
||||||
|
set_config(ti, config.at(ciey).at(ciex), "IoCtrl.REN_" + std::to_string(ciez), !pullup);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ctx->args.type == ArchArgs::UP5K) {
|
||||||
|
if (ciez == 0) {
|
||||||
|
set_config(ti, config.at(ciey).at(ciex), "IoCtrl.cf_bit_39", !pullup);
|
||||||
|
} else if (iez == 1) {
|
||||||
|
set_config(ti, config.at(ciey).at(ciex), "IoCtrl.cf_bit_35", !pullup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
} else if (cell.second->type == ctx->id("SB_GB")) {
|
} else if (cell.second->type == ctx->id("SB_GB")) {
|
||||||
// no cell config bits
|
// no cell config bits
|
||||||
} else if (cell.second->type == ctx->id("ICESTORM_RAM")) {
|
} else if (cell.second->type == ctx->id("ICESTORM_RAM")) {
|
||||||
@ -630,6 +667,13 @@ void write_asc(const Context *ctx, std::ostream &out)
|
|||||||
int iex, iey, iez;
|
int iex, iey, iez;
|
||||||
std::tie(iex, iey, iez) = ieren;
|
std::tie(iex, iey, iez) = ieren;
|
||||||
if (iez != -1) {
|
if (iez != -1) {
|
||||||
|
// IO is not actually unused if part of an LVDS pair
|
||||||
|
if (z == 1) {
|
||||||
|
BelId lvds0 = ctx->getBelByLocation(Loc{x, y, 0});
|
||||||
|
const CellInfo *lvds0cell = ctx->getBoundBelCell(lvds0);
|
||||||
|
if (lvds0cell != nullptr && lvds0cell->ioInfo.lvds)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
|
set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
|
||||||
set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
|
set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
|
||||||
}
|
}
|
||||||
|
@ -314,7 +314,7 @@ void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_l
|
|||||||
replace_port(dff, ctx->id("Q"), lc, ctx->id("O"));
|
replace_port(dff, ctx->id("Q"), lc, ctx->id("O"));
|
||||||
}
|
}
|
||||||
|
|
||||||
void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio)
|
void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, std::unordered_set<IdString> &todelete_cells)
|
||||||
{
|
{
|
||||||
if (nxio->type == ctx->id("$nextpnr_ibuf")) {
|
if (nxio->type == ctx->id("$nextpnr_ibuf")) {
|
||||||
sbio->params[ctx->id("PIN_TYPE")] = "1";
|
sbio->params[ctx->id("PIN_TYPE")] = "1";
|
||||||
@ -341,12 +341,16 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio)
|
|||||||
sbio->params[ctx->id("PIN_TYPE")] = "41";
|
sbio->params[ctx->id("PIN_TYPE")] = "41";
|
||||||
replace_port(tbuf, ctx->id("A"), sbio, ctx->id("D_OUT_0"));
|
replace_port(tbuf, ctx->id("A"), sbio, ctx->id("D_OUT_0"));
|
||||||
replace_port(tbuf, ctx->id("E"), sbio, ctx->id("OUTPUT_ENABLE"));
|
replace_port(tbuf, ctx->id("E"), sbio, ctx->id("OUTPUT_ENABLE"));
|
||||||
ctx->nets.erase(donet->name);
|
|
||||||
if (!donet->users.empty())
|
if (donet->users.size() > 1) {
|
||||||
|
for (auto user : donet->users)
|
||||||
|
log_info(" remaining tristate user: %s.%s\n", user.cell->name.c_str(ctx), user.port.c_str(ctx));
|
||||||
log_error("unsupported tristate IO pattern for IO buffer '%s', "
|
log_error("unsupported tristate IO pattern for IO buffer '%s', "
|
||||||
"instantiate SB_IO manually to ensure correct behaviour\n",
|
"instantiate SB_IO manually to ensure correct behaviour\n",
|
||||||
nxio->name.c_str(ctx));
|
nxio->name.c_str(ctx));
|
||||||
ctx->cells.erase(tbuf->name);
|
}
|
||||||
|
ctx->nets.erase(donet->name);
|
||||||
|
todelete_cells.insert(tbuf->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,7 +98,7 @@ void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff = tr
|
|||||||
void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut = false);
|
void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut = false);
|
||||||
|
|
||||||
// Convert a nextpnr IO buffer to a SB_IO
|
// Convert a nextpnr IO buffer to a SB_IO
|
||||||
void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio);
|
void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio, std::unordered_set<IdString> &todelete_cells);
|
||||||
|
|
||||||
// Return true if a port is a clock port
|
// Return true if a port is a clock port
|
||||||
bool is_clock_port(const BaseCtx *ctx, const PortRef &port);
|
bool is_clock_port(const BaseCtx *ctx, const PortRef &port);
|
||||||
|
@ -435,3 +435,4 @@ X(ICESTORM_SPRAM)
|
|||||||
X(DFF_ENABLE)
|
X(DFF_ENABLE)
|
||||||
X(CARRY_ENABLE)
|
X(CARRY_ENABLE)
|
||||||
X(NEG_CLK)
|
X(NEG_CLK)
|
||||||
|
X(IO_STANDARD)
|
@ -424,7 +424,7 @@ static void pack_io(Context *ctx)
|
|||||||
// Create a SB_IO buffer
|
// Create a SB_IO buffer
|
||||||
std::unique_ptr<CellInfo> ice_cell =
|
std::unique_ptr<CellInfo> ice_cell =
|
||||||
create_ice_cell(ctx, ctx->id("SB_IO"), ci->name.str(ctx) + "$sb_io");
|
create_ice_cell(ctx, ctx->id("SB_IO"), ci->name.str(ctx) + "$sb_io");
|
||||||
nxio_to_sb(ctx, ci, ice_cell.get());
|
nxio_to_sb(ctx, ci, ice_cell.get(), packed_cells);
|
||||||
new_cells.push_back(std::move(ice_cell));
|
new_cells.push_back(std::move(ice_cell));
|
||||||
sb = new_cells.back().get();
|
sb = new_cells.back().get();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user