aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--common/command.cc3
-rw-r--r--common/timing.cc5
-rw-r--r--ecp5/arch.cc8
-rw-r--r--ecp5/arch.h2
-rw-r--r--ecp5/bitstream.cc19
-rw-r--r--gui/application.cc24
-rw-r--r--gui/application.h2
-rw-r--r--gui/fpgaviewwidget.cc14
-rw-r--r--gui/lineshader.h13
-rw-r--r--ice40/arch.cc8
-rw-r--r--ice40/archdefs.h4
-rw-r--r--ice40/bitstream.cc8
-rw-r--r--ice40/cells.cc16
-rw-r--r--ice40/cells.h7
-rw-r--r--ice40/chipdb.py2
-rw-r--r--ice40/constids.inc5
-rw-r--r--ice40/pack.cc37
-rw-r--r--json/jsonparse.cc57
m---------tests0
19 files changed, 183 insertions, 51 deletions
diff --git a/common/command.cc b/common/command.cc
index 49081e72..3eafdb17 100644
--- a/common/command.cc
+++ b/common/command.cc
@@ -107,6 +107,7 @@ po::options_description CommandHandler::getGeneralOptions()
general.add_options()("force,f", "keep running after errors");
#ifndef NO_GUI
general.add_options()("gui", "start gui");
+ general.add_options()("gui-no-aa", "disable anti aliasing (use together with --gui option)");
#endif
#ifndef NO_PYTHON
general.add_options()("run", po::value<std::vector<std::string>>(),
@@ -235,7 +236,7 @@ int CommandHandler::executeMain(std::unique_ptr<Context> ctx)
#ifndef NO_GUI
if (vm.count("gui")) {
- Application a(argc, argv);
+ Application a(argc, argv, (vm.count("gui-no-aa") > 0));
MainWindow w(std::move(ctx), chipArgs);
try {
if (vm.count("json")) {
diff --git a/common/timing.cc b/common/timing.cc
index 2ce9eea3..e67ac231 100644
--- a/common/timing.cc
+++ b/common/timing.cc
@@ -546,7 +546,8 @@ struct Timing
for (size_t i = 0; i < sink_net->users.size(); i++) {
auto &user = sink_net->users.at(i);
if (user.cell == drv.cell && user.port == port.first) {
- sink_nd.min_required.at(i) = net_min_required - comb_delay.maxDelay();
+ sink_nd.min_required.at(i) = std::min(sink_nd.min_required.at(i),
+ net_min_required - comb_delay.maxDelay());
break;
}
}
@@ -752,7 +753,7 @@ void timing_analysis(Context *ctx, bool print_histogram, bool print_fmax, bool p
}
if (clock_reports.empty()) {
- log_warning("No clocks found in design");
+ log_warning("No clocks found in design\n");
}
std::sort(xclock_paths.begin(), xclock_paths.end(), [ctx](const ClockPair &a, const ClockPair &b) {
diff --git a/ecp5/arch.cc b/ecp5/arch.cc
index 9da8abdf..91db8d81 100644
--- a/ecp5/arch.cc
+++ b/ecp5/arch.cc
@@ -458,7 +458,7 @@ delay_t Arch::estimateDelay(WireId src, WireId dst) const
int dx = abs(src_loc.first - dst_loc.first), dy = abs(src_loc.second - dst_loc.second);
- return (130 - 25 * args.speed) *
+ return (120 - 22 * args.speed) *
(6 + std::max(dx - 5, 0) + std::max(dy - 5, 0) + 2 * (std::min(dx, 5) + std::min(dy, 5)));
}
@@ -487,7 +487,7 @@ delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
int dx = abs(driver_loc.x - sink_loc.x), dy = abs(driver_loc.y - sink_loc.y);
- return (130 - 25 * args.speed) *
+ return (120 - 22 * args.speed) *
(6 + std::max(dx - 5, 0) + std::max(dy - 5, 0) + 2 * (std::min(dx, 5) + std::min(dy, 5)));
}
@@ -504,6 +504,8 @@ bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay
}
}
+delay_t Arch::getRipupDelayPenalty() const { return 400; }
+
// -----------------------------------------------------------------------
bool Arch::place()
@@ -512,7 +514,7 @@ bool Arch::place()
if (placer == "heap") {
PlacerHeapCfg cfg(getCtx());
- cfg.criticalityExponent = 7;
+ cfg.criticalityExponent = 4;
cfg.ioBufTypes.insert(id_TRELLIS_IO);
if (!placer_heap(getCtx(), cfg))
return false;
diff --git a/ecp5/arch.h b/ecp5/arch.h
index 3de06a42..cee071e7 100644
--- a/ecp5/arch.h
+++ b/ecp5/arch.h
@@ -942,7 +942,7 @@ struct Arch : BaseCtx
delay_t estimateDelay(WireId src, WireId dst) const;
delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const;
delay_t getDelayEpsilon() const { return 20; }
- delay_t getRipupDelayPenalty() const { return 400; }
+ delay_t getRipupDelayPenalty() const;
float getDelayNS(delay_t v) const { return v * 0.001; }
DelayInfo getDelayFromNS(float ns) const
{
diff --git a/ecp5/bitstream.cc b/ecp5/bitstream.cc
index a9c82524..d549a727 100644
--- a/ecp5/bitstream.cc
+++ b/ecp5/bitstream.cc
@@ -134,7 +134,7 @@ inline int chtohex(char c)
return hex.find(c);
}
-std::vector<bool> parse_init_str(const std::string &str, int length)
+std::vector<bool> parse_init_str(const std::string &str, int length, const char *cellname)
{
// Parse a string that may be binary or hex
std::vector<bool> result;
@@ -161,7 +161,8 @@ std::vector<bool> parse_init_str(const std::string &str, int length)
log_error("hex string value too long, expected up to %d bits and found %d.\n", length, int(str.length()));
for (int i = 0; i < int(str.length()); i++) {
char c = str.at((str.size() - i) - 1);
- NPNR_ASSERT(c == '0' || c == '1' || c == 'X' || c == 'x');
+ if (c != '0' && c != '1' && c != 'X' && c != 'x')
+ log_error("Found illegal character '%c' while processing parameters for cell '%s'\n", c, cellname);
result.at(i) = (c == '1');
}
}
@@ -970,7 +971,7 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
for (int i = 0; i <= 0x3F; i++) {
IdString param = ctx->id("INITVAL_" +
fmt_str(std::hex << std::uppercase << std::setw(2) << std::setfill('0') << i));
- auto value = parse_init_str(str_or_default(ci->params, param, "0"), 320);
+ auto value = parse_init_str(str_or_default(ci->params, param, "0"), 320, ci->name.c_str(ctx));
for (int j = 0; j < 16; j++) {
// INIT parameter consists of 16 18-bit words with 2-bit padding
int ofs = 20 * j;
@@ -1078,17 +1079,21 @@ void write_bitstream(Context *ctx, std::string base_config_file, std::string tex
tg.config.add_enum(dsp + ".MASKPAT_SOURCE",
str_or_default(ci->params, ctx->id("MASKPAT_SOURCE"), "STATIC"));
tg.config.add_word(dsp + ".MASK01",
- parse_init_str(str_or_default(ci->params, ctx->id("MASK01"), "0x00000000000000"), 56));
+ parse_init_str(str_or_default(ci->params, ctx->id("MASK01"), "0x00000000000000"), 56,
+ ci->name.c_str(ctx)));
tg.config.add_enum(dsp + ".CLK0_DIV", str_or_default(ci->params, ctx->id("CLK0_DIV"), "ENABLED"));
tg.config.add_enum(dsp + ".CLK1_DIV", str_or_default(ci->params, ctx->id("CLK1_DIV"), "ENABLED"));
tg.config.add_enum(dsp + ".CLK2_DIV", str_or_default(ci->params, ctx->id("CLK2_DIV"), "ENABLED"));
tg.config.add_enum(dsp + ".CLK3_DIV", str_or_default(ci->params, ctx->id("CLK3_DIV"), "ENABLED"));
tg.config.add_word(dsp + ".MCPAT",
- parse_init_str(str_or_default(ci->params, ctx->id("MCPAT"), "0x00000000000000"), 56));
+ parse_init_str(str_or_default(ci->params, ctx->id("MCPAT"), "0x00000000000000"), 56,
+ ci->name.c_str(ctx)));
tg.config.add_word(dsp + ".MASKPAT",
- parse_init_str(str_or_default(ci->params, ctx->id("MASKPAT"), "0x00000000000000"), 56));
+ parse_init_str(str_or_default(ci->params, ctx->id("MASKPAT"), "0x00000000000000"), 56,
+ ci->name.c_str(ctx)));
tg.config.add_word(dsp + ".RNDPAT",
- parse_init_str(str_or_default(ci->params, ctx->id("RNDPAT"), "0x00000000000000"), 56));
+ parse_init_str(str_or_default(ci->params, ctx->id("RNDPAT"), "0x00000000000000"), 56,
+ ci->name.c_str(ctx)));
tg.config.add_enum(dsp + ".GSR", str_or_default(ci->params, ctx->id("GSR"), "ENABLED"));
tg.config.add_enum(dsp + ".RESETMODE", str_or_default(ci->params, ctx->id("RESETMODE"), "SYNC"));
tg.config.add_enum(dsp + ".FORCE_ZERO_BARREL_SHIFT",
diff --git a/gui/application.cc b/gui/application.cc
index 7751e6f1..33a106bc 100644
--- a/gui/application.cc
+++ b/gui/application.cc
@@ -21,9 +21,11 @@
#include "application.h"
#include <QMessageBox>
+#include <QOpenGLContext>
#include <QSurfaceFormat>
#include <QTextStream>
#include <exception>
+#include "log.h"
NEXTPNR_NAMESPACE_BEGIN
@@ -37,12 +39,28 @@ BOOL WINAPI WinHandler(DWORD dwCtrlType)
}
#endif
-Application::Application(int &argc, char **argv) : QApplication(argc, argv)
+Application::Application(int &argc, char **argv, bool noantialiasing) : QApplication(argc, argv)
{
QSurfaceFormat fmt;
- fmt.setSamples(10);
+ if (!noantialiasing)
+ fmt.setSamples(10);
fmt.setProfile(QSurfaceFormat::CoreProfile);
+ // macOS is very picky about this version matching
+ // the version of openGL used in ImGuiRenderer
+ fmt.setMajorVersion(3);
+ fmt.setMinorVersion(2);
QSurfaceFormat::setDefaultFormat(fmt);
+
+ QOpenGLContext glContext;
+ fmt = glContext.format();
+ if (fmt.majorVersion() < 3) {
+ printf("Could not get OpenGL 3.0 context. Aborting.\n");
+ log_abort();
+ }
+ if (fmt.minorVersion() < 2) {
+ printf("Could not get OpenGL 3.2 context - trying anyway...\n ");
+ }
+
#ifdef _WIN32
SetConsoleCtrlHandler((PHANDLER_ROUTINE)WinHandler, TRUE);
#endif
@@ -53,7 +71,7 @@ bool Application::notify(QObject *receiver, QEvent *event)
bool retVal = true;
try {
retVal = QApplication::notify(receiver, event);
- } catch (assertion_failure ex) {
+ } catch (const assertion_failure &ex) {
QString msg;
QTextStream out(&msg);
out << ex.filename.c_str() << " at " << ex.line << "\n";
diff --git a/gui/application.h b/gui/application.h
index 321f6b65..ad5de62f 100644
--- a/gui/application.h
+++ b/gui/application.h
@@ -29,7 +29,7 @@ NEXTPNR_NAMESPACE_BEGIN
class Application : public QApplication
{
public:
- Application(int &argc, char **argv);
+ Application(int &argc, char **argv, bool noantialiasing);
bool notify(QObject *receiver, QEvent *event);
};
diff --git a/gui/fpgaviewwidget.cc b/gui/fpgaviewwidget.cc
index 5eab20ed..f2929d6e 100644
--- a/gui/fpgaviewwidget.cc
+++ b/gui/fpgaviewwidget.cc
@@ -59,20 +59,6 @@ FPGAViewWidget::FPGAViewWidget(QWidget *parent)
rendererArgs_->gridChanged = false;
rendererArgs_->zoomOutbound = true;
- auto fmt = format();
- fmt.setMajorVersion(3);
- fmt.setMinorVersion(2);
- setFormat(fmt);
-
- fmt = format();
- if (fmt.majorVersion() < 3) {
- printf("Could not get OpenGL 3.0 context. Aborting.\n");
- log_abort();
- }
- if (fmt.minorVersion() < 2) {
- printf("Could not get OpenGL 3.2 context - trying anyway...\n ");
- }
-
connect(&paintTimer_, SIGNAL(timeout()), this, SLOT(update()));
paintTimer_.start(1000 / 20); // paint GL 20 times per second
diff --git a/gui/lineshader.h b/gui/lineshader.h
index 98042051..4c54bf46 100644
--- a/gui/lineshader.h
+++ b/gui/lineshader.h
@@ -172,10 +172,10 @@ class LineShader
LineShader(QObject *parent) : parent_(parent), program_(nullptr) {}
static constexpr const char *vertexShaderSource_ =
- "#version 110\n"
- "attribute highp vec2 position;\n"
- "attribute highp vec2 normal;\n"
- "attribute highp float miter;\n"
+ "#version 150\n"
+ "in highp vec2 position;\n"
+ "in highp vec2 normal;\n"
+ "in highp float miter;\n"
"uniform highp float thickness;\n"
"uniform highp mat4 projection;\n"
"void main() {\n"
@@ -183,10 +183,11 @@ class LineShader
" gl_Position = projection * vec4(p, 0.0, 1.0);\n"
"}\n";
- static constexpr const char *fragmentShaderSource_ = "#version 110\n"
+ static constexpr const char *fragmentShaderSource_ = "#version 150\n"
"uniform lowp vec4 color;\n"
+ "out vec4 Out_Color;\n"
"void main() {\n"
- " gl_FragColor = color;\n"
+ " Out_Color = color;\n"
"}\n";
// Must be called on initialization.
diff --git a/ice40/arch.cc b/ice40/arch.cc
index d536ad35..80e1fb4c 100644
--- a/ice40/arch.cc
+++ b/ice40/arch.cc
@@ -1045,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;
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 9b85dff5..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}};
diff --git a/ice40/cells.cc b/ice40/cells.cc
index 5744fe50..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);
diff --git a/ice40/cells.h b/ice40/cells.h
index ec4d560d..3d9358da 100644
--- a/ice40/cells.h
+++ b/ice40/cells.h
@@ -76,6 +76,13 @@ 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"); }
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/pack.cc b/ice40/pack.cc
index 390cbf57..f520b295 100644
--- a/ice40/pack.cc
+++ b/ice40/pack.cc
@@ -450,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) {
@@ -476,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;
@@ -1038,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)) {
@@ -1113,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);
@@ -1125,14 +1148,20 @@ 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"));
diff --git a/json/jsonparse.cc b/json/jsonparse.cc
index a0479c2e..0f229aca 100644
--- a/json/jsonparse.cc
+++ b/json/jsonparse.cc
@@ -337,7 +337,7 @@ static int const_net_idx = 0;
template <typename F>
void json_import_ports(Context *ctx, const string &modname, const std::vector<IdString> &netnames,
const string &obj_name, const string &port_name, JsonNode *dir_node, JsonNode *wire_group_node,
- F visitor)
+ bool upto, int start_offset, F visitor)
{
// Examine a port of a cell or the design. For every bit of the port,
// the connected net will be processed and `visitor` will be called
@@ -406,8 +406,11 @@ void json_import_ports(Context *ctx, const string &modname, const std::vector<Id
wire_node = wire_group_node->data_array[index];
//
// Pick a name for this port
+ int ndx = index + start_offset;
+ if (upto)
+ ndx = start_offset + wire_group_node->data_array.size() - index - 1;
if (is_bus)
- this_port.name = ctx->id(port_info.name.str(ctx) + "[" + std::to_string(index) + "]");
+ this_port.name = ctx->id(port_info.name.str(ctx) + "[" + std::to_string(ndx) + "]");
else
this_port.name = port_info.name;
this_port.type = port_info.type;
@@ -584,7 +587,7 @@ void json_import_cell(Context *ctx, string modname, const std::vector<IdString>
dir_node = pdir_node->data_dict.at(port_name);
wire_group_node = connections->data_dict.at(port_name);
- json_import_ports(ctx, modname, netnames, cell->name.str(ctx), port_name, dir_node, wire_group_node,
+ json_import_ports(ctx, modname, netnames, cell->name.str(ctx), port_name, dir_node, wire_group_node, false, 0,
[&cell, ctx](PortType type, const std::string &name, NetInfo *net) {
cell->ports[ctx->id(name)] = PortInfo{ctx->id(name), net, type};
PortRef pr;
@@ -680,8 +683,20 @@ void json_import_toplevel_port(Context *ctx, const string &modname, const std::v
{
JsonNode *dir_node = node->data_dict.at("direction");
JsonNode *nets_node = node->data_dict.at("bits");
+ bool upto = false;
+ int start_offset = 0;
+ if (node->data_dict.count("upto") != 0) {
+ JsonNode *val = node->data_dict.at("upto");
+ if (val->type == 'N')
+ upto = val->data_number != 0;
+ }
+ if (node->data_dict.count("offset") != 0) {
+ JsonNode *val = node->data_dict.at("offset");
+ if (val->type == 'N')
+ start_offset = val->data_number;
+ }
json_import_ports(
- ctx, modname, netnames, "Top Level IO", portname, dir_node, nets_node,
+ ctx, modname, netnames, "Top Level IO", portname, dir_node, nets_node, upto, start_offset,
[ctx](PortType type, const std::string &name, NetInfo *net) { insert_iobuf(ctx, net, type, name); });
}
@@ -692,11 +707,22 @@ void json_import(Context *ctx, string modname, JsonNode *node)
log_info("Importing module %s\n", modname.c_str());
+ JsonNode *ports_parent = nullptr;
+ if (node->data_dict.count("ports") > 0)
+ ports_parent = node->data_dict.at("ports");
+
// Multiple labels might refer to the same net. For now we resolve conflicts thus:
+ // - (toplevel) ports are always preferred
// - names with fewer $ are always prefered
// - between equal $ counts, fewer .s are prefered
// - ties are resolved alphabetically
- auto prefer_netlabel = [](const std::string &a, const std::string &b) {
+ auto prefer_netlabel = [ports_parent](const std::string &a, const std::string &b) {
+ if (ports_parent != nullptr) {
+ if (ports_parent->data_dict.count(a))
+ return true;
+ if (ports_parent->data_dict.count(b))
+ return false;
+ }
if (b.empty())
return true;
long a_dollars = std::count(a.begin(), a.end(), '$'), b_dollars = std::count(b.begin(), b.end(), '$');
@@ -721,6 +747,18 @@ void json_import(Context *ctx, string modname, JsonNode *node)
here = cell_parent->data_dict.at(cell_parent->data_dict_keys[nnid]);
std::string basename = cell_parent->data_dict_keys[nnid];
+ bool upto = false;
+ int start_offset = 0;
+ if (here->data_dict.count("upto") != 0) {
+ JsonNode *val = here->data_dict.at("upto");
+ if (val->type == 'N')
+ upto = val->data_number != 0;
+ }
+ if (here->data_dict.count("offset") != 0) {
+ JsonNode *val = here->data_dict.at("offset");
+ if (val->type == 'N')
+ start_offset = val->data_number;
+ }
if (here->data_dict.count("bits")) {
JsonNode *bits = here->data_dict.at("bits");
assert(bits->type == 'A');
@@ -729,8 +767,11 @@ void json_import(Context *ctx, string modname, JsonNode *node)
int netid = bits->data_array.at(i)->data_number;
if (netid >= int(netlabels.size()))
netlabels.resize(netid + 1);
+ int ndx = i + start_offset;
+ if (upto)
+ ndx = start_offset + num_bits - i - 1;
std::string name =
- basename + (num_bits == 1 ? "" : std::string("[") + std::to_string(i) + std::string("]"));
+ basename + (num_bits == 1 ? "" : std::string("[") + std::to_string(ndx) + std::string("]"));
if (prefer_netlabel(name, netlabels.at(netid)))
netlabels.at(netid) = name;
}
@@ -753,9 +794,7 @@ void json_import(Context *ctx, string modname, JsonNode *node)
}
}
- if (node->data_dict.count("ports")) {
- JsonNode *ports_parent = node->data_dict.at("ports");
-
+ if (ports_parent != nullptr) {
// N.B. ports must be imported after cells for tristate behaviour
// to be correct
// Loop through all ports, first non-tristate then tristate to handle
diff --git a/tests b/tests
-Subproject 0d369eb3fe3425fa74c0f6309268a012aac5040
+Subproject 5182fd4bec49a568cc3fa37d62d9f9a82f28091