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 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, 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;