aboutsummaryrefslogtreecommitdiffstats
path: root/ice40
diff options
context:
space:
mode:
authorSylvain Munaut <tnt@246tNt.com>2019-03-22 23:29:34 +0100
committerSylvain Munaut <tnt@246tNt.com>2019-03-25 23:48:59 +0100
commitd401e3e1a09e2e5d78f18f32405c82293ce68545 (patch)
treefe46510d61a491a9d1b85ffba102e0eb9c69b8c2 /ice40
parentc2d87846d8e6c9603f432c1b021f58023f7625b4 (diff)
downloadnextpnr-d401e3e1a09e2e5d78f18f32405c82293ce68545.tar.gz
nextpnr-d401e3e1a09e2e5d78f18f32405c82293ce68545.tar.bz2
nextpnr-d401e3e1a09e2e5d78f18f32405c82293ce68545.zip
ice40: Add support for SB_I2C and SB_SPI
Signed-off-by: Sylvain Munaut <tnt@246tNt.com>
Diffstat (limited to 'ice40')
-rw-r--r--ice40/arch.cc21
-rw-r--r--ice40/bitstream.cc22
-rw-r--r--ice40/cells.cc47
-rw-r--r--ice40/cells.h4
-rw-r--r--ice40/pack.cc19
5 files changed, 112 insertions, 1 deletions
diff --git a/ice40/arch.cc b/ice40/arch.cc
index b0839fa5..bfcadc0b 100644
--- a/ice40/arch.cc
+++ b/ice40/arch.cc
@@ -1053,6 +1053,16 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
if (port == id_CLK || port == id_CLOCK)
return TMG_CLOCK_INPUT;
return TMG_IGNORE;
+ } else if (cell->type == id_SB_I2C || cell->type == id_SB_SPI) {
+ if (port == this->id("SBCLKI"))
+ return TMG_CLOCK_INPUT;
+
+ clockInfoCount = 1;
+
+ if (cell->ports.at(port).type == PORT_OUT)
+ return TMG_REGISTER_OUTPUT;
+ else
+ return TMG_REGISTER_INPUT;
}
log_error("cell type '%s' is unsupported (instantiated as '%s')\n", cell->type.c_str(this), cell->name.c_str(this));
}
@@ -1144,6 +1154,17 @@ TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port
info.setup.delay = 100;
info.hold.delay = 0;
}
+ } else if (cell->type == id_SB_I2C || cell->type == id_SB_SPI) {
+ info.clock_port = this->id("SBCLKI");
+ info.edge = RISING_EDGE;
+ if (cell->ports.at(port).type == PORT_OUT) {
+ /* Dummy number */
+ info.clockToQ.delay = 1500;
+ } else {
+ /* Dummy number */
+ info.setup.delay = 1500;
+ info.hold.delay = 0;
+ }
} else {
NPNR_ASSERT_FALSE("unhandled cell type in getPortClockingInfo");
}
diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc
index fe0d592d..9b85dff5 100644
--- a/ice40/bitstream.cc
+++ b/ice40/bitstream.cc
@@ -618,6 +618,28 @@ void write_asc(const Context *ctx, std::ostream &out)
} else if (cell.second->type == ctx->id("SB_WARMBOOT") || cell.second->type == ctx->id("ICESTORM_LFOSC") ||
cell.second->type == ctx->id("SB_LEDDA_IP")) {
// No config needed
+ } else if (cell.second->type == ctx->id("SB_I2C")) {
+ bool sda_in_dly = !cell.second->attrs.count(ctx->id("SDA_INPUT_DELAYED")) ||
+ std::stoi(cell.second->attrs[ctx->id("SDA_INPUT_DELAYED")]);
+ bool sda_out_dly = !cell.second->attrs.count(ctx->id("SDA_OUTPUT_DELAYED")) ||
+ std::stoi(cell.second->attrs[ctx->id("SDA_OUTPUT_DELAYED")]);
+ set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "SDA_INPUT_DELAYED", sda_in_dly,
+ "IpConfig.");
+ set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "SDA_OUTPUT_DELAYED", sda_out_dly,
+ "IpConfig.");
+ set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "I2C_ENABLE_0", true,
+ "IpConfig.");
+ set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "I2C_ENABLE_1", true,
+ "IpConfig.");
+ } else if (cell.second->type == ctx->id("SB_SPI")) {
+ set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "SPI_ENABLE_0", true,
+ "IpConfig.");
+ set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "SPI_ENABLE_1", true,
+ "IpConfig.");
+ set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "SPI_ENABLE_2", true,
+ "IpConfig.");
+ set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "SPI_ENABLE_3", true,
+ "IpConfig.");
} else if (cell.second->type == ctx->id("ICESTORM_SPRAM")) {
const BelInfoPOD &beli = ci.bel_data[bel.index];
int x = beli.x, y = beli.y, z = beli.z;
diff --git a/ice40/cells.cc b/ice40/cells.cc
index 35a5346f..5744fe50 100644
--- a/ice40/cells.cc
+++ b/ice40/cells.cc
@@ -275,6 +275,53 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri
add_port(ctx, new_cell.get(), "PWMOUT1", PORT_OUT);
add_port(ctx, new_cell.get(), "PWMOUT2", PORT_OUT);
add_port(ctx, new_cell.get(), "LEDDON", PORT_OUT);
+ } else if (type == ctx->id("SB_I2C")) {
+ new_cell->params[ctx->id("I2C_SLAVE_INIT_ADDR")] = "0b1111100001";
+ new_cell->params[ctx->id("BUS_ADDR74")] = "0b0001";
+ for (int i = 0; i < 8; i++) {
+ add_port(ctx, new_cell.get(), "SBADRI" + std::to_string(i), PORT_IN);
+ add_port(ctx, new_cell.get(), "SBDATI" + std::to_string(i), PORT_IN);
+ add_port(ctx, new_cell.get(), "SBDATO" + std::to_string(i), PORT_OUT);
+ }
+ add_port(ctx, new_cell.get(), "SBCLKI", PORT_IN);
+ add_port(ctx, new_cell.get(), "SBRWI", PORT_IN);
+ add_port(ctx, new_cell.get(), "SBSTBI", PORT_IN);
+ add_port(ctx, new_cell.get(), "SCLI", PORT_IN);
+ add_port(ctx, new_cell.get(), "SDAI", PORT_IN);
+ add_port(ctx, new_cell.get(), "SBACKO", PORT_OUT);
+ add_port(ctx, new_cell.get(), "I2CIRQ", PORT_OUT);
+ add_port(ctx, new_cell.get(), "I2CWKUP", PORT_OUT);
+ add_port(ctx, new_cell.get(), "SCLO", PORT_OUT);
+ add_port(ctx, new_cell.get(), "SCLOE", PORT_OUT);
+ add_port(ctx, new_cell.get(), "SDAO", PORT_OUT);
+ add_port(ctx, new_cell.get(), "SDAOE", PORT_OUT);
+ } else if (type == ctx->id("SB_SPI")) {
+ new_cell->params[ctx->id("BUS_ADDR74")] = "0b0000";
+ for (int i = 0; i < 8; i++) {
+ add_port(ctx, new_cell.get(), "SBADRI" + std::to_string(i), PORT_IN);
+ add_port(ctx, new_cell.get(), "SBDATI" + std::to_string(i), PORT_IN);
+ add_port(ctx, new_cell.get(), "SBDATO" + std::to_string(i), PORT_OUT);
+ }
+ add_port(ctx, new_cell.get(), "SBCLKI", PORT_IN);
+ add_port(ctx, new_cell.get(), "SBRWI", PORT_IN);
+ add_port(ctx, new_cell.get(), "SBSTBI", PORT_IN);
+ add_port(ctx, new_cell.get(), "MI", PORT_IN);
+ add_port(ctx, new_cell.get(), "SI", PORT_IN);
+ add_port(ctx, new_cell.get(), "SCKI", PORT_IN);
+ add_port(ctx, new_cell.get(), "SCSNI", PORT_IN);
+ add_port(ctx, new_cell.get(), "SBACKO", PORT_OUT);
+ add_port(ctx, new_cell.get(), "SPIIRQ", PORT_OUT);
+ add_port(ctx, new_cell.get(), "SPIWKUP", PORT_OUT);
+ add_port(ctx, new_cell.get(), "SO", PORT_OUT);
+ add_port(ctx, new_cell.get(), "SOE", PORT_OUT);
+ add_port(ctx, new_cell.get(), "MO", PORT_OUT);
+ add_port(ctx, new_cell.get(), "MOE", PORT_OUT);
+ add_port(ctx, new_cell.get(), "SCKO", PORT_OUT);
+ add_port(ctx, new_cell.get(), "SCKOE", PORT_OUT);
+ for (int i = 0; i < 4; i++) {
+ add_port(ctx, new_cell.get(), "MCSNO" + std::to_string(i), PORT_OUT);
+ add_port(ctx, new_cell.get(), "MCSNOE" + std::to_string(i), PORT_OUT);
+ }
} else {
log_error("unable to create iCE40 cell of type %s", type.c_str(ctx));
}
diff --git a/ice40/cells.h b/ice40/cells.h
index 93ef3db4..ec4d560d 100644
--- a/ice40/cells.h
+++ b/ice40/cells.h
@@ -78,6 +78,10 @@ inline bool is_sb_rgba_drv(const BaseCtx *ctx, const CellInfo *cell) { return ce
inline bool is_sb_ledda_ip(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_LEDDA_IP"); }
+inline bool is_sb_i2c(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_I2C"); }
+
+inline bool is_sb_spi(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_SPI"); }
+
inline bool is_sb_pll40(const BaseCtx *ctx, const CellInfo *cell)
{
return cell->type == ctx->id("SB_PLL40_PAD") || cell->type == ctx->id("SB_PLL40_2_PAD") ||
diff --git a/ice40/pack.cc b/ice40/pack.cc
index c22c4e8c..4de88abd 100644
--- a/ice40/pack.cc
+++ b/ice40/pack.cc
@@ -1056,7 +1056,24 @@ static void pack_special(Context *ctx)
} else if (is_sb_ledda_ip(ctx, ci)) {
/* Force placement (no choices anyway) */
cell_place_unique(ctx, ci);
-
+ } else if (is_sb_i2c(ctx, ci) || is_sb_spi(ctx, ci)) {
+ const std::map<std::tuple<IdString, std::string>, Loc> map_ba74 = {
+ {std::make_tuple(id_SB_SPI, "0b0000"), Loc(0, 0, 0)},
+ {std::make_tuple(id_SB_I2C, "0b0001"), Loc(0, 31, 0)},
+ {std::make_tuple(id_SB_SPI, "0b0010"), Loc(25, 0, 1)},
+ {std::make_tuple(id_SB_I2C, "0b0011"), Loc(25, 31, 0)},
+ };
+ if (map_ba74.find(std::make_tuple(ci->type, ci->params[ctx->id("BUS_ADDR74")])) == map_ba74.end())
+ log_error("Invalid value for BUS_ADDR74 for cell '%s' of type '%s'\n", ci->name.c_str(ctx),
+ ci->type.c_str(ctx));
+ Loc bel_loc = map_ba74.at(std::make_tuple(ci->type, ci->params[ctx->id("BUS_ADDR74")]));
+ BelId bel = ctx->getBelByLocation(bel_loc);
+ if (bel == BelId() || ctx->getBelType(bel) != ci->type)
+ log_error("Unable to find placement for cell '%s' of type '%s'\n", ci->name.c_str(ctx),
+ ci->type.c_str(ctx));
+ IdString bel_name = ctx->getBelName(bel);
+ ci->attrs[ctx->id("BEL")] = bel_name.str(ctx);
+ log_info(" constrained %s '%s' to %s\n", ci->type.c_str(ctx), ci->name.c_str(ctx), bel_name.c_str(ctx));
} else if (is_sb_pll40(ctx, ci)) {
bool is_pad = is_sb_pll40_pad(ctx, ci);
bool is_core = !is_pad;