aboutsummaryrefslogtreecommitdiffstats
path: root/ice40
diff options
context:
space:
mode:
Diffstat (limited to 'ice40')
-rw-r--r--ice40/arch.cc63
-rw-r--r--ice40/arch.h3
-rw-r--r--ice40/arch_place.cc36
-rw-r--r--ice40/arch_pybindings.cc7
-rw-r--r--ice40/archdefs.h4
-rw-r--r--ice40/bitstream.cc30
-rw-r--r--ice40/cells.cc63
-rw-r--r--ice40/cells.h11
-rw-r--r--ice40/chipdb.py2
-rw-r--r--ice40/constids.inc5
-rw-r--r--ice40/examples/blinky/blinky.pcf (renamed from ice40/blinky.pcf)0
-rw-r--r--ice40/examples/blinky/blinky.proj (renamed from ice40/blinky.proj)0
-rwxr-xr-xice40/examples/blinky/blinky.sh (renamed from ice40/blinky.sh)0
-rw-r--r--ice40/examples/blinky/blinky.v (renamed from ice40/blinky.v)0
-rw-r--r--ice40/examples/blinky/blinky.ys (renamed from ice40/blinky.ys)0
-rw-r--r--ice40/examples/blinky/blinky_tb.v (renamed from ice40/blinky_tb.v)0
-rw-r--r--ice40/examples/floorplan/.gitignore4
-rw-r--r--ice40/examples/floorplan/floorplan.py5
-rwxr-xr-xice40/examples/floorplan/floorplan.sh6
-rw-r--r--ice40/examples/floorplan/floorplan.v22
-rw-r--r--ice40/examples/floorplan/icebreaker.pcf5
-rw-r--r--ice40/main.cc1
-rw-r--r--ice40/pack.cc175
-rw-r--r--ice40/pcf.cc5
24 files changed, 419 insertions, 28 deletions
diff --git a/ice40/arch.cc b/ice40/arch.cc
index c822d5c4..80e1fb4c 100644
--- a/ice40/arch.cc
+++ b/ice40/arch.cc
@@ -26,10 +26,10 @@
#include "log.h"
#include "nextpnr.h"
#include "placer1.h"
+#include "placer_heap.h"
#include "router1.h"
#include "timing_opt.h"
#include "util.h"
-
NEXTPNR_NAMESPACE_BEGIN
// -----------------------------------------------------------------------
@@ -56,7 +56,7 @@ const char *chipdb_blob_5k = nullptr;
const char *chipdb_blob_u4k = nullptr;
const char *chipdb_blob_8k = nullptr;
-boost::iostreams::mapped_file_source blob_files[4];
+boost::iostreams::mapped_file_source blob_files[5];
const char *mmap_file(int index, const char *filename)
{
@@ -75,8 +75,8 @@ void load_chipdb()
chipdb_blob_384 = mmap_file(0, EXTERNAL_CHIPDB_ROOT "/ice40/chipdb-384.bin");
chipdb_blob_1k = mmap_file(1, EXTERNAL_CHIPDB_ROOT "/ice40/chipdb-1k.bin");
chipdb_blob_5k = mmap_file(2, EXTERNAL_CHIPDB_ROOT "/ice40/chipdb-5k.bin");
- chipdb_blob_u4k = mmap_file(2, EXTERNAL_CHIPDB_ROOT "/ice40/chipdb-u4k.bin");
- chipdb_blob_8k = mmap_file(3, EXTERNAL_CHIPDB_ROOT "/ice40/chipdb-8k.bin");
+ chipdb_blob_u4k = mmap_file(3, EXTERNAL_CHIPDB_ROOT "/ice40/chipdb-u4k.bin");
+ chipdb_blob_8k = mmap_file(4, EXTERNAL_CHIPDB_ROOT "/ice40/chipdb-8k.bin");
}
#endif
Arch::Arch(ArchArgs args) : args(args)
@@ -671,8 +671,18 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay
bool Arch::place()
{
- if (!placer1(getCtx(), Placer1Cfg(getCtx())))
- return false;
+ std::string placer = str_or_default(settings, id("placer"), defaultPlacer);
+ if (placer == "heap") {
+ PlacerHeapCfg cfg(getCtx());
+ cfg.ioBufTypes.insert(id_SB_IO);
+ if (!placer_heap(getCtx(), cfg))
+ return false;
+ } else if (placer == "sa") {
+ if (!placer1(getCtx(), Placer1Cfg(getCtx())))
+ return false;
+ } else {
+ log_error("iCE40 architecture does not support placer '%s'\n", placer.c_str());
+ }
if (bool_or_default(settings, id("opt_timing"), false)) {
TimingOptCfg tocfg(getCtx());
tocfg.cellTypes.insert(id_ICESTORM_LC);
@@ -1035,6 +1045,14 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, in
return TMG_COMB_INPUT;
} else if (cell->type == id_SB_WARMBOOT) {
return TMG_ENDPOINT;
+ } else if (cell->type == id_SB_LED_DRV_CUR) {
+ if (port == id_LEDPU)
+ return TMG_IGNORE;
+ return TMG_ENDPOINT;
+ } else if (cell->type == id_SB_RGB_DRV) {
+ if (port == id_RGB0 || port == id_RGB1 || port == id_RGB2 || port == id_RGBPU)
+ return TMG_IGNORE;
+ return TMG_ENDPOINT;
} else if (cell->type == id_SB_RGBA_DRV) {
if (port == id_RGB0 || port == id_RGB1 || port == id_RGB2)
return TMG_IGNORE;
@@ -1043,6 +1061,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));
}
@@ -1134,6 +1162,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");
}
@@ -1190,12 +1229,20 @@ void Arch::assignCellInfo(CellInfo *cell)
} else if (cell->type == id_SB_IO) {
cell->ioInfo.lvds = str_or_default(cell->params, id_IO_STANDARD, "SB_LVCMOS") == "SB_LVDS_INPUT";
cell->ioInfo.global = bool_or_default(cell->attrs, this->id("GLOBAL"));
- cell->ioInfo.pintype = int_or_default(cell->attrs, this->id("PIN_TYPE"));
- cell->ioInfo.negtrig = bool_or_default(cell->attrs, this->id("NEG_TRIGGER"));
+ cell->ioInfo.pintype = int_or_default(cell->params, this->id("PIN_TYPE"));
+ cell->ioInfo.negtrig = bool_or_default(cell->params, this->id("NEG_TRIGGER"));
} else if (cell->type == id_SB_GB) {
cell->gbInfo.forPadIn = bool_or_default(cell->attrs, this->id("FOR_PAD_IN"));
}
}
+const std::string Arch::defaultPlacer = "sa";
+
+const std::vector<std::string> Arch::availablePlacers = {"sa",
+#ifdef WITH_HEAP
+ "heap"
+#endif
+};
+
NEXTPNR_NAMESPACE_END
diff --git a/ice40/arch.h b/ice40/arch.h
index 706043b2..ea29f4f1 100644
--- a/ice40/arch.h
+++ b/ice40/arch.h
@@ -897,6 +897,9 @@ struct Arch : BaseCtx
IdString glb_net = getWireName(getBelPinWire(bel, id_GLOBAL_BUFFER_OUTPUT));
return std::stoi(std::string("") + glb_net.str(this).back());
}
+
+ static const std::string defaultPlacer;
+ static const std::vector<std::string> availablePlacers;
};
void ice40DelayFuzzerMain(Context *ctx);
diff --git a/ice40/arch_place.cc b/ice40/arch_place.cc
index 90bb62b9..ede8d47f 100644
--- a/ice40/arch_place.cc
+++ b/ice40/arch_place.cc
@@ -91,6 +91,18 @@ bool Arch::isBelLocationValid(BelId bel) const
}
}
+static inline bool _io_pintype_need_clk_in(unsigned pin_type) { return (pin_type & 0x01) == 0x00; }
+
+static inline bool _io_pintype_need_clk_out(unsigned pin_type)
+{
+ return ((pin_type & 0x30) == 0x30) || ((pin_type & 0x3c) && ((pin_type & 0x0c) != 0x08));
+}
+
+static inline bool _io_pintype_need_clk_en(unsigned pin_type)
+{
+ return _io_pintype_need_clk_in(pin_type) || _io_pintype_need_clk_out(pin_type);
+}
+
bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
{
if (cell->type == id_ICESTORM_LC) {
@@ -157,6 +169,30 @@ bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
CellInfo *compCell = getBoundBelCell(compBel);
if (compCell && compCell->ioInfo.lvds)
return false;
+
+ // Check for conflicts on shared nets
+ // - CLOCK_ENABLE
+ // - OUTPUT_CLK
+ // - INPUT_CLK
+ if (compCell) {
+ bool use[6] = {
+ _io_pintype_need_clk_in(cell->ioInfo.pintype),
+ _io_pintype_need_clk_in(compCell->ioInfo.pintype),
+ _io_pintype_need_clk_out(cell->ioInfo.pintype),
+ _io_pintype_need_clk_out(compCell->ioInfo.pintype),
+ _io_pintype_need_clk_en(cell->ioInfo.pintype),
+ _io_pintype_need_clk_en(compCell->ioInfo.pintype),
+ };
+ NetInfo *nets[] = {
+ cell->ports[id_INPUT_CLK].net, compCell->ports[id_INPUT_CLK].net,
+ cell->ports[id_OUTPUT_CLK].net, compCell->ports[id_OUTPUT_CLK].net,
+ cell->ports[id_CLOCK_ENABLE].net, compCell->ports[id_CLOCK_ENABLE].net,
+ };
+
+ for (int i = 0; i < 6; i++)
+ if (use[i] && (nets[i] != nets[i ^ 1]) && (use[i ^ 1] || (nets[i ^ 1] != nullptr)))
+ return false;
+ }
}
return getBelPackagePin(bel) != "";
diff --git a/ice40/arch_pybindings.cc b/ice40/arch_pybindings.cc
index f0ca584b..bc0bfb84 100644
--- a/ice40/arch_pybindings.cc
+++ b/ice40/arch_pybindings.cc
@@ -144,6 +144,13 @@ void arch_wrap_python()
fn_wrapper_2a_v<Context, decltype(&Context::addClock), &Context::addClock, conv_from_str<IdString>,
pass_through<float>>::def_wrap(ctx_cls, "addClock");
+ fn_wrapper_5a_v<Context, decltype(&Context::createRectangularRegion), &Context::createRectangularRegion,
+ conv_from_str<IdString>, pass_through<int>, pass_through<int>, pass_through<int>,
+ pass_through<int>>::def_wrap(ctx_cls, "createRectangularRegion");
+ fn_wrapper_2a_v<Context, decltype(&Context::addBelToRegion), &Context::addBelToRegion, conv_from_str<IdString>,
+ conv_from_str<BelId>>::def_wrap(ctx_cls, "addBelToRegion");
+ fn_wrapper_2a_v<Context, decltype(&Context::constrainCellToRegion), &Context::constrainCellToRegion,
+ conv_from_str<IdString>, conv_from_str<IdString>>::def_wrap(ctx_cls, "constrainCellToRegion");
WRAP_RANGE(Bel, conv_to_str<BelId>);
WRAP_RANGE(Wire, conv_to_str<WireId>);
diff --git a/ice40/archdefs.h b/ice40/archdefs.h
index 956fcb4c..89591af5 100644
--- a/ice40/archdefs.h
+++ b/ice40/archdefs.h
@@ -159,6 +159,10 @@ struct ArchCellInfo
{
bool forPadIn;
} gbInfo;
+ struct
+ {
+ bool ledCurConnected;
+ } ledInfo;
};
};
diff --git a/ice40/bitstream.cc b/ice40/bitstream.cc
index fe0d592d..7632b443 100644
--- a/ice40/bitstream.cc
+++ b/ice40/bitstream.cc
@@ -610,6 +610,14 @@ void write_asc(const Context *ctx, std::ostream &out)
set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_1", write_mode & 0x2);
set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_2", read_mode & 0x1);
set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_3", read_mode & 0x2);
+ } else if (cell.second->type == ctx->id("SB_LED_DRV_CUR")) {
+ set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "LED_DRV_CUR_EN", true,
+ "IpConfig.");
+ } else if (cell.second->type == ctx->id("SB_RGB_DRV")) {
+ const std::vector<std::pair<std::string, int>> rgb_params = {
+ {"RGB0_CURRENT", 6}, {"RGB1_CURRENT", 6}, {"RGB2_CURRENT", 6}};
+ configure_extra_cell(config, ctx, cell.second.get(), rgb_params, true, std::string("IpConfig."));
+ set_ec_cbit(config, ctx, get_ec_config(ctx->chip_info, cell.second->bel), "RGB_DRV_EN", true, "IpConfig.");
} else if (cell.second->type == ctx->id("SB_RGBA_DRV")) {
const std::vector<std::pair<std::string, int>> rgba_params = {
{"CURRENT_MODE", 1}, {"RGB0_CURRENT", 6}, {"RGB1_CURRENT", 6}, {"RGB2_CURRENT", 6}};
@@ -618,6 +626,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..a2abcea4 100644
--- a/ice40/cells.cc
+++ b/ice40/cells.cc
@@ -260,6 +260,22 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri
add_port(ctx, new_cell.get(), "RGB0", PORT_OUT);
add_port(ctx, new_cell.get(), "RGB1", PORT_OUT);
add_port(ctx, new_cell.get(), "RGB2", PORT_OUT);
+ } else if (type == ctx->id("SB_LED_DRV_CUR")) {
+ add_port(ctx, new_cell.get(), "EN", PORT_IN);
+ add_port(ctx, new_cell.get(), "LEDPU", PORT_OUT);
+ } else if (type == ctx->id("SB_RGB_DRV")) {
+ new_cell->params[ctx->id("RGB0_CURRENT")] = "0b000000";
+ new_cell->params[ctx->id("RGB1_CURRENT")] = "0b000000";
+ new_cell->params[ctx->id("RGB2_CURRENT")] = "0b000000";
+
+ add_port(ctx, new_cell.get(), "RGBPU", PORT_IN);
+ add_port(ctx, new_cell.get(), "RGBLEDEN", PORT_IN);
+ add_port(ctx, new_cell.get(), "RGB0PWM", PORT_IN);
+ add_port(ctx, new_cell.get(), "RGB1PWM", PORT_IN);
+ add_port(ctx, new_cell.get(), "RGB2PWM", PORT_IN);
+ add_port(ctx, new_cell.get(), "RGB0", PORT_OUT);
+ add_port(ctx, new_cell.get(), "RGB1", PORT_OUT);
+ add_port(ctx, new_cell.get(), "RGB2", PORT_OUT);
} else if (type == ctx->id("SB_LEDDA_IP")) {
add_port(ctx, new_cell.get(), "LEDDCS", PORT_IN);
add_port(ctx, new_cell.get(), "LEDDCLK", PORT_IN);
@@ -275,6 +291,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..3d9358da 100644
--- a/ice40/cells.h
+++ b/ice40/cells.h
@@ -76,8 +76,19 @@ inline bool is_sb_mac16(const BaseCtx *ctx, const CellInfo *cell) { return cell-
inline bool is_sb_rgba_drv(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_RGBA_DRV"); }
+inline bool is_sb_rgb_drv(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_RGB_DRV"); }
+
+inline bool is_sb_led_drv_cur(const BaseCtx *ctx, const CellInfo *cell)
+{
+ return cell->type == ctx->id("SB_LED_DRV_CUR");
+}
+
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/chipdb.py b/ice40/chipdb.py
index 42ca6ac1..cc7be01f 100644
--- a/ice40/chipdb.py
+++ b/ice40/chipdb.py
@@ -81,6 +81,8 @@ constids["SPI"] = constids["SB_SPI"]
constids["LEDDA_IP"] = constids["SB_LEDDA_IP"]
constids["RGBA_DRV"] = constids["SB_RGBA_DRV"]
constids["SPRAM"] = constids["ICESTORM_SPRAM"]
+constids["LED_DRV_CUR"] = constids["SB_LED_DRV_CUR"]
+constids["RGB_DRV"] = constids["SB_RGB_DRV"]
with open(args.gfxh) as f:
state = 0
diff --git a/ice40/constids.inc b/ice40/constids.inc
index 366a3a9d..6aa5c4c0 100644
--- a/ice40/constids.inc
+++ b/ice40/constids.inc
@@ -355,6 +355,9 @@ X(PWMOUT0)
X(PWMOUT1)
X(PWMOUT2)
+X(LEDPU)
+X(EN)
+X(RGBPU)
X(CURREN)
X(RGB0PWM)
X(RGB1PWM)
@@ -438,6 +441,8 @@ X(IO_I3C)
X(SB_LEDDA_IP)
X(SB_RGBA_DRV)
X(ICESTORM_SPRAM)
+X(SB_LED_DRV_CUR)
+X(SB_RGB_DRV)
// cell parameters
X(DFF_ENABLE)
diff --git a/ice40/blinky.pcf b/ice40/examples/blinky/blinky.pcf
index 141dfcc8..141dfcc8 100644
--- a/ice40/blinky.pcf
+++ b/ice40/examples/blinky/blinky.pcf
diff --git a/ice40/blinky.proj b/ice40/examples/blinky/blinky.proj
index f5bb9f88..f5bb9f88 100644
--- a/ice40/blinky.proj
+++ b/ice40/examples/blinky/blinky.proj
diff --git a/ice40/blinky.sh b/ice40/examples/blinky/blinky.sh
index a2326fc3..a2326fc3 100755
--- a/ice40/blinky.sh
+++ b/ice40/examples/blinky/blinky.sh
diff --git a/ice40/blinky.v b/ice40/examples/blinky/blinky.v
index 36eaee86..36eaee86 100644
--- a/ice40/blinky.v
+++ b/ice40/examples/blinky/blinky.v
diff --git a/ice40/blinky.ys b/ice40/examples/blinky/blinky.ys
index a5dd2c85..a5dd2c85 100644
--- a/ice40/blinky.ys
+++ b/ice40/examples/blinky/blinky.ys
diff --git a/ice40/blinky_tb.v b/ice40/examples/blinky/blinky_tb.v
index f80b5e64..f80b5e64 100644
--- a/ice40/blinky_tb.v
+++ b/ice40/examples/blinky/blinky_tb.v
diff --git a/ice40/examples/floorplan/.gitignore b/ice40/examples/floorplan/.gitignore
new file mode 100644
index 00000000..d93659be
--- /dev/null
+++ b/ice40/examples/floorplan/.gitignore
@@ -0,0 +1,4 @@
+*.json
+*.asc
+*.bin
+__pycache__ \ No newline at end of file
diff --git a/ice40/examples/floorplan/floorplan.py b/ice40/examples/floorplan/floorplan.py
new file mode 100644
index 00000000..85c53ccd
--- /dev/null
+++ b/ice40/examples/floorplan/floorplan.py
@@ -0,0 +1,5 @@
+ctx.createRectangularRegion("osc", 1, 1, 1, 4)
+for cell, cellinfo in ctx.cells:
+ if "ringosc" in cellinfo.attrs:
+ print("Floorplanned cell %s" % cell)
+ ctx.constrainCellToRegion(cell, "osc")
diff --git a/ice40/examples/floorplan/floorplan.sh b/ice40/examples/floorplan/floorplan.sh
new file mode 100755
index 00000000..e0ed7a64
--- /dev/null
+++ b/ice40/examples/floorplan/floorplan.sh
@@ -0,0 +1,6 @@
+#!/usr/bin/env bash
+set -ex
+yosys -p "synth_ice40 -top top -json floorplan.json" floorplan.v
+../../../nextpnr-ice40 --up5k --json floorplan.json --pcf icebreaker.pcf --asc floorplan.asc --ignore-loops --pre-place floorplan.py
+icepack floorplan.asc floorplan.bin
+iceprog floorplan.bin
diff --git a/ice40/examples/floorplan/floorplan.v b/ice40/examples/floorplan/floorplan.v
new file mode 100644
index 00000000..8f99ed4e
--- /dev/null
+++ b/ice40/examples/floorplan/floorplan.v
@@ -0,0 +1,22 @@
+module top(output LED1, LED2, LED3, LED4, LED5);
+ localparam N = 31;
+ wire [N:0] x;
+ assign x[0] = x[N];
+
+ genvar ii;
+ generate
+
+ for (ii = 0; ii < N; ii = ii + 1) begin
+ (* ringosc *)
+ SB_LUT4 #(.LUT_INIT(1)) lut_i(.I0(x[ii]), .I1(), .I2(), .I3(), .O(x[ii+1]));
+ end
+ endgenerate
+
+ assign clk = x[N];
+
+
+ reg [19:0] ctr;
+ always @(posedge clk)
+ ctr <= ctr + 1'b1;
+ assign {LED5, LED4, LED3, LED2, LED1} = ctr[19:15];
+endmodule
diff --git a/ice40/examples/floorplan/icebreaker.pcf b/ice40/examples/floorplan/icebreaker.pcf
new file mode 100644
index 00000000..ac7ebf9e
--- /dev/null
+++ b/ice40/examples/floorplan/icebreaker.pcf
@@ -0,0 +1,5 @@
+set_io -nowarn LED1 26
+set_io -nowarn LED2 27
+set_io -nowarn LED3 25
+set_io -nowarn LED4 23
+set_io -nowarn LED5 21
diff --git a/ice40/main.cc b/ice40/main.cc
index 2313c2ae..9b79a08c 100644
--- a/ice40/main.cc
+++ b/ice40/main.cc
@@ -176,7 +176,6 @@ std::unique_ptr<Context> Ice40CommandHandler::createContext()
ctx->settings[ctx->id("opt_timing")] = "1";
if (vm.count("pcf-allow-unconstrained"))
ctx->settings[ctx->id("pcf_allow_unconstrained")] = "1";
-
return ctx;
}
diff --git a/ice40/pack.cc b/ice40/pack.cc
index c22c4e8c..f520b295 100644
--- a/ice40/pack.cc
+++ b/ice40/pack.cc
@@ -383,12 +383,9 @@ static void pack_constants(Context *ctx)
}
}
-static std::unique_ptr<CellInfo> create_padin_gbuf(Context *ctx, CellInfo *cell, IdString port_name,
- std::string gbuf_name)
+static BelId find_padin_gbuf(Context *ctx, BelId bel, IdString port_name)
{
- // Find the matching SB_GB BEL connected to the same global network
BelId gb_bel;
- BelId bel = ctx->getBelByName(ctx->id(cell->attrs[ctx->id("BEL")]));
auto wire = ctx->getBelPinWire(bel, port_name);
if (wire == WireId())
@@ -401,6 +398,15 @@ static std::unique_ptr<CellInfo> create_padin_gbuf(Context *ctx, CellInfo *cell,
}
}
+ return gb_bel;
+}
+
+static std::unique_ptr<CellInfo> create_padin_gbuf(Context *ctx, CellInfo *cell, IdString port_name,
+ std::string gbuf_name)
+{
+ // Find the matching SB_GB BEL connected to the same global network
+ BelId bel = ctx->getBelByName(ctx->id(cell->attrs[ctx->id("BEL")]));
+ BelId gb_bel = find_padin_gbuf(ctx, bel, port_name);
NPNR_ASSERT(gb_bel != BelId());
// Create a SB_GB Cell and lock it there
@@ -444,7 +450,8 @@ static void pack_io(Context *ctx)
} else if (ci->type == ctx->id("$nextpnr_obuf")) {
NetInfo *net = ci->ports.at(ctx->id("I")).net;
sb = net_only_drives(ctx, net, is_ice_iob, ctx->id("PACKAGE_PIN"), true, ci);
- if (net && net->driver.cell && is_sb_rgba_drv(ctx, net->driver.cell))
+ if (net && net->driver.cell &&
+ (is_sb_rgba_drv(ctx, net->driver.cell) || is_sb_rgb_drv(ctx, net->driver.cell)))
rgb = net->driver.cell;
}
if (sb != nullptr) {
@@ -470,7 +477,8 @@ static void pack_io(Context *ctx)
}
}
} else if (rgb != nullptr) {
- log_info("%s use by SB_RGBA_DRV %s, not creating SB_IO\n", ci->name.c_str(ctx), rgb->name.c_str(ctx));
+ log_info("%s use by SB_RGBA_DRV/SB_RGB_DRV %s, not creating SB_IO\n", ci->name.c_str(ctx),
+ rgb->name.c_str(ctx));
disconnect_port(ctx, ci, ctx->id("I"));
packed_cells.insert(ci->name);
continue;
@@ -502,6 +510,19 @@ static void pack_io(Context *ctx)
// Make it a normal SB_IO with global marker
ci->type = ctx->id("SB_IO");
ci->attrs[ctx->id("GLOBAL")] = "1";
+ } else if (is_sb_io(ctx, ci)) {
+ // Disconnect unused inputs
+ NetInfo *net_in0 = ci->ports.count(id_D_IN_0) ? ci->ports[id_D_IN_0].net : nullptr;
+ NetInfo *net_in1 = ci->ports.count(id_D_IN_1) ? ci->ports[id_D_IN_1].net : nullptr;
+
+ if (net_in0 != nullptr && net_in0->users.size() == 0) {
+ delete_nets.insert(net_in0->name);
+ ci->ports[id_D_IN_0].net = nullptr;
+ }
+ if (net_in1 != nullptr && net_in1->users.size() == 0) {
+ delete_nets.insert(net_in1->name);
+ ci->ports[id_D_IN_1].net = nullptr;
+ }
}
}
for (auto pcell : packed_cells) {
@@ -691,14 +712,15 @@ static void promote_globals(Context *ctx)
// Figure out where to place PLLs
static void place_plls(Context *ctx)
{
- std::map<BelId, std::pair<BelPin, BelPin>> pll_all_bels;
+ std::map<BelId, std::tuple<BelPin, BelId, BelPin, BelId>> pll_all_bels;
std::map<BelId, CellInfo *> pll_used_bels;
std::vector<CellInfo *> pll_cells;
std::map<BelId, CellInfo *> bel2io;
+ std::map<BelId, CellInfo *> bel2gb;
log_info("Placing PLLs..\n");
- // Find all the PLLs BELs and matching IO sites
+ // Find all the PLLs BELs and matching IO sites and global networks
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) != id_ICESTORM_PLL)
continue;
@@ -707,8 +729,10 @@ static void place_plls(Context *ctx)
auto io_a_pin = ctx->getIOBSharingPLLPin(bel, id_PLLOUT_A);
auto io_b_pin = ctx->getIOBSharingPLLPin(bel, id_PLLOUT_B);
+ auto gb_a = find_padin_gbuf(ctx, bel, id_PLLOUT_A_GLOBAL);
+ auto gb_b = find_padin_gbuf(ctx, bel, id_PLLOUT_B_GLOBAL);
- pll_all_bels[bel] = std::make_pair(io_a_pin, io_b_pin);
+ pll_all_bels[bel] = std::make_tuple(io_a_pin, gb_a, io_b_pin, gb_b);
}
// Find all the PLLs cells we need to place and do pre-checks
@@ -813,7 +837,8 @@ static void place_plls(Context *ctx)
for (auto placed_pll : pll_used_bels) {
BelPin pll_io_a, pll_io_b;
- std::tie(pll_io_a, pll_io_b) = pll_all_bels[placed_pll.first];
+ BelId gb_a, gb_b;
+ std::tie(pll_io_a, gb_a, pll_io_b, gb_b) = pll_all_bels[placed_pll.first];
if (io_bel == pll_io_a.bel) {
// All the PAD type PLL stuff already checked above,so only
// check for conflict with a user placed CORE PLL
@@ -831,6 +856,47 @@ static void place_plls(Context *ctx)
bel2io[io_bel] = io_ci;
}
+ // Scan all SB_GBs to check for conflicts with PLL BELs
+ for (auto gb_cell : sorted(ctx->cells)) {
+ CellInfo *gb_ci = gb_cell.second;
+ if (!is_gbuf(ctx, gb_ci))
+ continue;
+
+ // Only consider the bound ones
+ if (!gb_ci->attrs.count(ctx->id("BEL")))
+ continue;
+
+ // Check all placed PLL (either forced by user, or forced by PACKAGEPIN)
+ BelId gb_bel = ctx->getBelByName(ctx->id(gb_ci->attrs[ctx->id("BEL")]));
+
+ for (auto placed_pll : pll_used_bels) {
+ CellInfo *ci = placed_pll.second;
+
+ // Used global connections
+ bool gb_a_used = ci->ports.count(id_PLLOUT_A_GLOBAL) && (ci->ports[id_PLLOUT_A_GLOBAL].net != nullptr) &&
+ (ci->ports[id_PLLOUT_A_GLOBAL].net->users.size() > 0);
+ bool gb_b_used = is_sb_pll40_dual(ctx, ci) && ci->ports.count(id_PLLOUT_B_GLOBAL) &&
+ (ci->ports[id_PLLOUT_B_GLOBAL].net != nullptr) &&
+ (ci->ports[id_PLLOUT_B_GLOBAL].net->users.size() > 0);
+
+ // Check for conflict
+ BelPin pll_io_a, pll_io_b;
+ BelId gb_a, gb_b;
+ std::tie(pll_io_a, gb_a, pll_io_b, gb_b) = pll_all_bels[placed_pll.first];
+ if (gb_a_used && (gb_bel == gb_a)) {
+ log_error("PLL '%s' A output conflict with SB_GB '%s'\n", placed_pll.second->name.c_str(ctx),
+ gb_cell.second->name.c_str(ctx));
+ }
+ if (gb_b_used && (gb_bel == gb_b)) {
+ log_error("PLL '%s' B output conflicts with SB_GB '%s'\n", placed_pll.second->name.c_str(ctx),
+ gb_cell.second->name.c_str(ctx));
+ }
+ }
+
+ // Save for later checks
+ bel2gb[gb_bel] = gb_ci;
+ }
+
// Scan all the CORE PLLs and place them in remaining available PLL BELs
// (in two pass ... first do the dual ones, harder to place, then single port)
for (int i = 0; i < 2; i++) {
@@ -849,6 +915,13 @@ static void place_plls(Context *ctx)
log_error("PLL '%s' is of CORE type but doesn't have a valid REFERENCECLK connection\n",
ci->name.c_str(ctx));
+ // Used global connections
+ bool gb_a_used = ci->ports.count(id_PLLOUT_A_GLOBAL) && (ci->ports[id_PLLOUT_A_GLOBAL].net != nullptr) &&
+ (ci->ports[id_PLLOUT_A_GLOBAL].net->users.size() > 0);
+ bool gb_b_used = is_sb_pll40_dual(ctx, ci) && ci->ports.count(id_PLLOUT_B_GLOBAL) &&
+ (ci->ports[id_PLLOUT_B_GLOBAL].net != nullptr) &&
+ (ci->ports[id_PLLOUT_B_GLOBAL].net->users.size() > 0);
+
// Could this be a PAD PLL ?
bool could_be_pad = false;
BelId pad_bel;
@@ -858,8 +931,11 @@ static void place_plls(Context *ctx)
// Find a BEL for it
BelId found_bel;
for (auto bel_pll : pll_all_bels) {
+ if (pll_used_bels.count(bel_pll.first))
+ continue;
BelPin pll_io_a, pll_io_b;
- std::tie(pll_io_a, pll_io_b) = bel_pll.second;
+ BelId gb_a, gb_b;
+ std::tie(pll_io_a, gb_a, pll_io_b, gb_b) = bel_pll.second;
if (bel2io.count(pll_io_a.bel)) {
if (pll_io_a.bel == pad_bel)
could_be_pad = !bel2io.count(pll_io_b.bel) || !is_sb_pll40_dual(ctx, ci);
@@ -867,6 +943,10 @@ static void place_plls(Context *ctx)
}
if (bel2io.count(pll_io_b.bel) && is_sb_pll40_dual(ctx, ci))
continue;
+ if (gb_a_used && bel2gb.count(gb_a))
+ continue;
+ if (gb_b_used && bel2gb.count(gb_b))
+ continue;
found_bel = bel_pll.first;
break;
}
@@ -960,6 +1040,27 @@ static void pack_special(Context *ctx)
std::unordered_set<IdString> packed_cells;
std::vector<std::unique_ptr<CellInfo>> new_cells;
+ // Handle LED_DRV_CUR first to set the ledCurConnected flag before RGB_DRV is handled below.
+ for (auto cell : sorted(ctx->cells)) {
+ CellInfo *ci = cell.second;
+ if (is_sb_led_drv_cur(ctx, ci)) {
+ /* Force placement (no choices anyway) */
+ cell_place_unique(ctx, ci);
+
+ NetInfo *ledpu_net = ci->ports.at(ctx->id("LEDPU")).net;
+ for (auto &user : ledpu_net->users) {
+ if (!is_sb_rgb_drv(ctx, user.cell)) {
+ log_error("SB_LED_DRV_CUR LEDPU port can only be connected to SB_RGB_DRV!\n");
+ } else {
+ user.cell->ledInfo.ledCurConnected = true;
+ user.cell->ports.at(user.port).net = nullptr;
+ }
+ }
+ ci->ports.erase(ctx->id("LEDPU"));
+ ctx->nets.erase(ledpu_net->name);
+ }
+ }
+
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (is_sb_lfosc(ctx, ci)) {
@@ -983,9 +1084,14 @@ static void pack_special(Context *ctx)
create_ice_cell(ctx, ctx->id("ICESTORM_HFOSC"), ci->name.str(ctx) + "_OSC");
packed_cells.insert(ci->name);
cell_place_unique(ctx, packed.get());
+ packed->params[ctx->id("TRIM_EN")] = str_or_default(ci->params, ctx->id("TRIM_EN"), "0b0");
packed->params[ctx->id("CLKHF_DIV")] = str_or_default(ci->params, ctx->id("CLKHF_DIV"), "0b00");
replace_port(ci, ctx->id("CLKHFEN"), packed.get(), ctx->id("CLKHFEN"));
replace_port(ci, ctx->id("CLKHFPU"), packed.get(), ctx->id("CLKHFPU"));
+ for (int i = 0; i < 10; i++) {
+ auto port = ctx->id("TRIM" + std::to_string(i));
+ replace_port(ci, port, packed.get(), port);
+ }
if (bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))) {
replace_port(ci, ctx->id("CLKHF"), packed.get(), ctx->id("CLKHF_FABRIC"));
} else {
@@ -1030,7 +1136,7 @@ static void pack_special(Context *ctx)
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
}
new_cells.push_back(std::move(packed));
- } else if (is_sb_rgba_drv(ctx, ci)) {
+ } else if (is_sb_rgba_drv(ctx, ci) || is_sb_rgb_drv(ctx, ci)) {
/* Force placement (no choices anyway) */
cell_place_unique(ctx, ci);
@@ -1042,21 +1148,44 @@ static void pack_special(Context *ctx)
if (net == nullptr)
continue;
+
if ((pi.name != ctx->id("RGB0")) && (pi.name != ctx->id("RGB1")) && (pi.name != ctx->id("RGB2")))
continue;
if (net->users.size() > 0)
- log_error("SB_RGBA_DRV port connected to more than just package pin !\n");
+ log_error("SB_RGB_DRV/SB_RGBA_DRV port connected to more than just package pin !\n");
ctx->nets.erase(net->name);
}
+
+ if (is_sb_rgb_drv(ctx, ci) && !ci->ledInfo.ledCurConnected)
+ log_error("Port RGBPU of SB_RGB_DRV should be driven by port LEDPU of SB_LED_DRV_CUR!\n");
+
+ ci->ports.erase(ctx->id("RGBPU"));
ci->ports.erase(ctx->id("RGB0"));
ci->ports.erase(ctx->id("RGB1"));
ci->ports.erase(ctx->id("RGB2"));
} 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;
@@ -1259,10 +1388,20 @@ static void pack_special(Context *ctx)
else
continue;
- std::unique_ptr<CellInfo> gb =
- create_padin_gbuf(ctx, packed.get(), pi.name,
- "$gbuf_" + ci->name.str(ctx) + "_pllout_" + (is_b_port ? "b" : "a"));
- new_cells.push_back(std::move(gb));
+ // Only if there is actually a net ...
+ if (pi.net != nullptr) {
+ // ... and it's used
+ if (pi.net->users.size() > 0) {
+ std::unique_ptr<CellInfo> gb =
+ create_padin_gbuf(ctx, packed.get(), pi.name,
+ "$gbuf_" + ci->name.str(ctx) + "_pllout_" + (is_b_port ? "b" : "a"));
+ new_cells.push_back(std::move(gb));
+ } else {
+ // If not, remove it to avoid routing issues
+ ctx->nets.erase(pi.net->name);
+ packed->ports[pi.name].net = nullptr;
+ }
+ }
}
new_cells.push_back(std::move(packed));
diff --git a/ice40/pcf.cc b/ice40/pcf.cc
index a8273dd6..15132800 100644
--- a/ice40/pcf.cc
+++ b/ice40/pcf.cc
@@ -77,8 +77,11 @@ bool apply_pcf(Context *ctx, std::string filename, std::istream &in)
}
args_end++;
}
- if (args_end >= words.size() - 1)
+ if (args_end > words.size() - 2)
log_error("expected PCF syntax 'set_io cell pin' (on line %d)\n", lineno);
+ else if (args_end < words.size() - 2 && !nowarn)
+ log_warning("Ignoring trailing PCF settings (on line %d)\n", lineno);
+
std::string cell = words.at(args_end);
std::string pin = words.at(args_end + 1);
auto fnd_cell = ctx->cells.find(ctx->id(cell));