aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/archapi.md12
-rw-r--r--ice40/arch.cc80
-rw-r--r--ice40/arch.h6
-rw-r--r--ice40/pack.cc3
4 files changed, 77 insertions, 24 deletions
diff --git a/docs/archapi.md b/docs/archapi.md
index 73443c15..6b22c6df 100644
--- a/docs/archapi.md
+++ b/docs/archapi.md
@@ -455,11 +455,17 @@ Cell Delay Methods
Returns the delay for the specified path through a cell in the `&delay` argument. The method returns
false if there is no timing relationship from `fromPort` to `toPort`.
-### TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const
+### TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const
Return the _timing port class_ of a port. This can be a register or combinational input or output; clock input or
-output; general startpoint or endpoint; or a port ignored for timing purposes. For register ports, clockPort is set
-to the associated clock port.
+output; general startpoint or endpoint; or a port ignored for timing purposes. For register ports, clockInfoCount is set
+to the number of associated _clock edges_ that can be queried by getPortClockingInfo.
+
+### TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const
+
+Return the _clocking info_ (including port name of clock, clock polarity and setup/hold/clock-to-out times) of a
+port. Where ports have more than one clock edge associated with them (such as DDR outputs), `index` can be used to obtain
+information for all edges. `index` must be in [0, clockInfoCount), behaviour is undefined otherwise.
Placer Methods
--------------
diff --git a/ice40/arch.cc b/ice40/arch.cc
index eb26ae5a..021be872 100644
--- a/ice40/arch.cc
+++ b/ice40/arch.cc
@@ -856,8 +856,9 @@ bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort
}
// Get the port class, also setting clockPort to associated clock if applicable
-TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const
+TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const
{
+ clockInfoCount = 0;
if (cell->type == id_ICESTORM_LC) {
if (port == id_CLK)
return TMG_CLOCK_INPUT;
@@ -870,18 +871,15 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id
if (cell->lcInfo.inputCount == 0)
return TMG_IGNORE;
if (cell->lcInfo.dffEnable) {
- clockPort = id_CLK;
+ clockInfoCount = 1;
return TMG_REGISTER_OUTPUT;
- }
- else
+ } else
return TMG_COMB_OUTPUT;
- }
- else {
+ } else {
if (cell->lcInfo.dffEnable) {
- clockPort = id_CLK;
+ clockInfoCount = 1;
return TMG_REGISTER_INPUT;
- }
- else
+ } else
return TMG_COMB_INPUT;
}
} else if (cell->type == id_ICESTORM_RAM) {
@@ -889,23 +887,22 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id
if (port == id_RCLK || port == id_WCLK)
return TMG_CLOCK_INPUT;
- if (port.str(this)[0] == 'R')
- clockPort = id_RCLK;
- else
- clockPort = id_WCLK;
+ clockInfoCount = 1;
if (cell->ports.at(port).type == PORT_OUT)
return TMG_REGISTER_OUTPUT;
else
return TMG_REGISTER_INPUT;
} else if (cell->type == id_ICESTORM_DSP || cell->type == id_ICESTORM_SPRAM) {
- clockPort = id_CLK;
if (port == id_CLK)
return TMG_CLOCK_INPUT;
- else if (cell->ports.at(port).type == PORT_OUT)
- return TMG_REGISTER_OUTPUT;
- else
- return TMG_REGISTER_INPUT;
+ else {
+ clockInfoCount = 1;
+ if (cell->ports.at(port).type == PORT_OUT)
+ return TMG_REGISTER_OUTPUT;
+ else
+ return TMG_REGISTER_INPUT;
+ }
} else if (cell->type == id_SB_IO) {
if (port == id_D_IN_0 || port == id_D_IN_1)
return TMG_STARTPOINT;
@@ -934,6 +931,53 @@ TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, Id
log_error("no timing info for port '%s' of cell type '%s'\n", port.c_str(this), cell->type.c_str(this));
}
+TimingClockingInfo Arch::getPortClockingInfo(const CellInfo *cell, IdString port, int index) const
+{
+ TimingClockingInfo info;
+ if (cell->type == id_ICESTORM_LC) {
+ info.clock_port = id_CLK;
+ info.edge = cell->lcInfo.negClk ? TimingClockingInfo::FALLING : TimingClockingInfo::RISING;
+ if (port == id_O) {
+ bool has_clktoq = getCellDelay(cell, id_CLK, id_O, info.clockToQ);
+ NPNR_ASSERT(has_clktoq);
+ } else {
+ info.setup.delay = 100;
+ info.hold.delay = 0;
+ }
+ } else if (cell->type == id_ICESTORM_RAM) {
+ if (port.str(this)[0] == 'R') {
+ info.clock_port = id_RCLK;
+ info.edge = bool_or_default(cell->params, id("NEG_CLK_R")) ? TimingClockingInfo::FALLING
+ : TimingClockingInfo::RISING;
+ } else {
+ info.clock_port = id_WCLK;
+ info.edge = bool_or_default(cell->params, id("NEG_CLK_W")) ? TimingClockingInfo::FALLING
+ : TimingClockingInfo::RISING;
+ }
+ if (cell->ports.at(port).type == PORT_OUT) {
+ bool has_clktoq = getCellDelay(cell, info.clock_port, port, info.clockToQ);
+ NPNR_ASSERT(has_clktoq);
+ } else {
+ info.setup.delay = 100;
+ info.hold.delay = 0;
+ }
+ } else if (cell->type == id_ICESTORM_DSP || cell->type == id_ICESTORM_SPRAM) {
+ info.clock_port = id_CLK;
+ info.edge = TimingClockingInfo::RISING;
+ if (cell->ports.at(port).type == PORT_OUT) {
+ bool has_clktoq = getCellDelay(cell, info.clock_port, port, info.clockToQ);
+ if (!has_clktoq)
+ info.clockToQ.delay = 100;
+ } else {
+ info.setup.delay = 100;
+ info.hold.delay = 0;
+ }
+ } else {
+ NPNR_ASSERT_FALSE("unhandled cell type in getPortClockingInfo");
+ }
+ return info;
+}
+
bool Arch::isGlobalNet(const NetInfo *net) const
{
if (net == nullptr)
diff --git a/ice40/arch.h b/ice40/arch.h
index bdcee3b8..ff2f7e4c 100644
--- a/ice40/arch.h
+++ b/ice40/arch.h
@@ -798,8 +798,10 @@ struct Arch : BaseCtx
// Get the delay through a cell from one port to another, returning false
// if no path exists
bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const;
- // Get the port class, also setting clockDomain if applicable
- TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockDomain) const;
+ // Get the port class, also setting clockInfoCount to the number of TimingClockingInfos associated with a port
+ TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, int &clockInfoCount) const;
+ // Get the TimingClockingInfo of a port
+ TimingClockingInfo getPortClockingInfo(const CellInfo *cell, IdString port, int index) const;
// Return true if a port is a net
bool isGlobalNet(const NetInfo *net) const;
diff --git a/ice40/pack.cc b/ice40/pack.cc
index edd12f92..b9360b74 100644
--- a/ice40/pack.cc
+++ b/ice40/pack.cc
@@ -462,7 +462,8 @@ static bool is_logic_port(BaseCtx *ctx, const PortRef &port)
static void insert_global(Context *ctx, NetInfo *net, bool is_reset, bool is_cen, bool is_logic)
{
- log_info("promoting %s%s%s%s\n", net->name.c_str(ctx), is_reset ? " [reset]" : "", is_cen ? " [cen]" : "", is_logic ? " [logic]" : "");
+ log_info("promoting %s%s%s%s\n", net->name.c_str(ctx), is_reset ? " [reset]" : "", is_cen ? " [cen]" : "",
+ is_logic ? " [logic]" : "");
std::string glb_name = net->name.str(ctx) + std::string("_$glb_") + (is_reset ? "sr" : (is_cen ? "ce" : "clk"));
std::unique_ptr<CellInfo> gb = create_ice_cell(ctx, ctx->id("SB_GB"), "$gbuf_" + glb_name);