Gowin. Add DHCEN primitive.
This primitive allows you to dynamically turn off and turn on the networks of high-speed clocks. This is done tracking the routes to the sinks and if the route passes through a special HCLK MUX (this may be the input MUX or the output MUX, as well as the interbank MUX), then the control signal of this MUX is used. Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
parent
0d5d32951c
commit
05ff80a24e
@ -1234,10 +1234,12 @@ X(BUFG)
|
|||||||
X(CLOCK)
|
X(CLOCK)
|
||||||
X(DQCE)
|
X(DQCE)
|
||||||
X(DCS)
|
X(DCS)
|
||||||
X(DCS_MODE)
|
|
||||||
X(DQCE_PIP)
|
X(DQCE_PIP)
|
||||||
|
X(DHCEN_USED)
|
||||||
X(DCS_USED)
|
X(DCS_USED)
|
||||||
X(SELFORCE)
|
X(SELFORCE)
|
||||||
|
X(DHCEN)
|
||||||
|
X(DCS_MODE)
|
||||||
|
|
||||||
//HCLK Bels
|
//HCLK Bels
|
||||||
X(CLKDIV)
|
X(CLKDIV)
|
||||||
|
@ -134,7 +134,8 @@ struct GowinGlobalRouter
|
|||||||
|
|
||||||
// Dedicated backwards BFS routing for global networks
|
// Dedicated backwards BFS routing for global networks
|
||||||
template <typename Tfilt>
|
template <typename Tfilt>
|
||||||
bool backwards_bfs_route(NetInfo *net, WireId src, WireId dst, int iter_limit, bool strict, Tfilt pip_filter)
|
bool backwards_bfs_route(NetInfo *net, WireId src, WireId dst, int iter_limit, bool strict, Tfilt pip_filter,
|
||||||
|
std::vector<PipId> *path = nullptr)
|
||||||
{
|
{
|
||||||
// Queue of wires to visit
|
// Queue of wires to visit
|
||||||
std::queue<WireId> visit;
|
std::queue<WireId> visit;
|
||||||
@ -208,6 +209,9 @@ struct GowinGlobalRouter
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ctx->bindPip(pip, net, STRENGTH_LOCKED);
|
ctx->bindPip(pip, net, STRENGTH_LOCKED);
|
||||||
|
if (path != nullptr) {
|
||||||
|
path->push_back(pip);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@ -225,6 +229,7 @@ struct GowinGlobalRouter
|
|||||||
bool driver_is_buf(const PortRef &driver) { return CellTypePort(driver) == CellTypePort(id_BUFG, id_O); }
|
bool driver_is_buf(const PortRef &driver) { return CellTypePort(driver) == CellTypePort(id_BUFG, id_O); }
|
||||||
bool driver_is_dqce(const PortRef &driver) { return CellTypePort(driver) == CellTypePort(id_DQCE, id_CLKOUT); }
|
bool driver_is_dqce(const PortRef &driver) { return CellTypePort(driver) == CellTypePort(id_DQCE, id_CLKOUT); }
|
||||||
bool driver_is_dcs(const PortRef &driver) { return CellTypePort(driver) == CellTypePort(id_DCS, id_CLKOUT); }
|
bool driver_is_dcs(const PortRef &driver) { return CellTypePort(driver) == CellTypePort(id_DCS, id_CLKOUT); }
|
||||||
|
bool driver_is_dhcen(const PortRef &driver) { return CellTypePort(driver) == CellTypePort(id_DHCEN, id_CLKOUT); }
|
||||||
bool driver_is_clksrc(const PortRef &driver)
|
bool driver_is_clksrc(const PortRef &driver)
|
||||||
{
|
{
|
||||||
// dedicated pins
|
// dedicated pins
|
||||||
@ -276,7 +281,9 @@ struct GowinGlobalRouter
|
|||||||
ROUTED_ALL
|
ROUTED_ALL
|
||||||
};
|
};
|
||||||
|
|
||||||
RouteResult route_direct_net(NetInfo *net, WireId aux_src = WireId(), bool DCS_pips = false, bool DQCE_pips = false)
|
template <typename Tfilter>
|
||||||
|
RouteResult route_direct_net(NetInfo *net, Tfilter pip_filter, WireId aux_src = WireId(),
|
||||||
|
std::vector<PipId> *path = nullptr)
|
||||||
{
|
{
|
||||||
WireId src;
|
WireId src;
|
||||||
src = aux_src == WireId() ? ctx->getNetinfoSourceWire(net) : aux_src;
|
src = aux_src == WireId() ? ctx->getNetinfoSourceWire(net) : aux_src;
|
||||||
@ -297,21 +304,9 @@ struct GowinGlobalRouter
|
|||||||
ctx->nameOf(usr.port));
|
ctx->nameOf(usr.port));
|
||||||
}
|
}
|
||||||
bool bfs_res;
|
bool bfs_res;
|
||||||
if (DCS_pips) {
|
bfs_res = backwards_bfs_route(
|
||||||
bfs_res = backwards_bfs_route(net, src, dst, 1000000, false, [&](PipId pip) {
|
net, src, dst, 1000000, false, [&](PipId pip) { return (is_relaxed_sink(usr) || pip_filter(pip)); },
|
||||||
return (is_relaxed_sink(usr) || global_DCS_pip_filter(pip));
|
path);
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (DQCE_pips) {
|
|
||||||
bfs_res = backwards_bfs_route(net, src, dst, 1000000, false, [&](PipId pip) {
|
|
||||||
return (is_relaxed_sink(usr) || global_DQCE_pip_filter(pip));
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
bfs_res = backwards_bfs_route(net, src, dst, 1000000, false, [&](PipId pip) {
|
|
||||||
return (is_relaxed_sink(usr) || global_pip_filter(pip));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (bfs_res) {
|
if (bfs_res) {
|
||||||
routed = routed == ROUTED_PARTIALLY ? routed : ROUTED_ALL;
|
routed = routed == ROUTED_PARTIALLY ? routed : ROUTED_ALL;
|
||||||
} else {
|
} else {
|
||||||
@ -345,7 +340,8 @@ struct GowinGlobalRouter
|
|||||||
src = ctx->getBelPinWire(driver.cell->bel, driver.port);
|
src = ctx->getBelPinWire(driver.cell->bel, driver.port);
|
||||||
}
|
}
|
||||||
|
|
||||||
RouteResult route_result = route_direct_net(net, src, false, true);
|
RouteResult route_result = route_direct_net(
|
||||||
|
net, [&](PipId pip) { return global_DQCE_pip_filter(pip); }, src);
|
||||||
if (route_result == NOT_ROUTED) {
|
if (route_result == NOT_ROUTED) {
|
||||||
log_error("Can't route the %s network.\n", ctx->nameOf(net));
|
log_error("Can't route the %s network.\n", ctx->nameOf(net));
|
||||||
}
|
}
|
||||||
@ -422,7 +418,8 @@ struct GowinGlobalRouter
|
|||||||
src = ctx->getBelPinWire(driver.cell->bel, driver.port);
|
src = ctx->getBelPinWire(driver.cell->bel, driver.port);
|
||||||
}
|
}
|
||||||
|
|
||||||
RouteResult route_result = route_direct_net(net, src, true);
|
RouteResult route_result = route_direct_net(
|
||||||
|
net, [&](PipId pip) { return global_DCS_pip_filter(pip); }, src);
|
||||||
if (route_result == NOT_ROUTED) {
|
if (route_result == NOT_ROUTED) {
|
||||||
log_error("Can't route the %s network.\n", ctx->nameOf(net));
|
log_error("Can't route the %s network.\n", ctx->nameOf(net));
|
||||||
}
|
}
|
||||||
@ -487,6 +484,84 @@ struct GowinGlobalRouter
|
|||||||
ctx->cells.erase(dcs_ci->name);
|
ctx->cells.erase(dcs_ci->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void route_dhcen_net(NetInfo *net)
|
||||||
|
{
|
||||||
|
// route net after dhcen source of CLKIN net
|
||||||
|
CellInfo *dhcen_ci = net->driver.cell;
|
||||||
|
|
||||||
|
NetInfo *net_before_dhcen = dhcen_ci->getPort(id_CLKIN);
|
||||||
|
NPNR_ASSERT(net_before_dhcen != nullptr);
|
||||||
|
|
||||||
|
PortRef driver = net_before_dhcen->driver;
|
||||||
|
NPNR_ASSERT_MSG(driver_is_buf(driver) || driver_is_clksrc(driver),
|
||||||
|
stringf("The input source for %s is not a clock.", ctx->nameOf(dhcen_ci)).c_str());
|
||||||
|
|
||||||
|
IdString port;
|
||||||
|
// use BUF input if there is one
|
||||||
|
if (driver_is_buf(driver)) {
|
||||||
|
port = id_I;
|
||||||
|
} else {
|
||||||
|
port = driver.port;
|
||||||
|
}
|
||||||
|
WireId src = ctx->getBelPinWire(driver.cell->bel, port);
|
||||||
|
|
||||||
|
std::vector<PipId> path;
|
||||||
|
RouteResult route_result = route_direct_net(
|
||||||
|
net, [&](PipId pip) { return global_pip_filter(pip); }, src, &path);
|
||||||
|
if (route_result == NOT_ROUTED) {
|
||||||
|
log_error("Can't route the %s network.\n", ctx->nameOf(net));
|
||||||
|
}
|
||||||
|
if (route_result == ROUTED_PARTIALLY) {
|
||||||
|
log_error("It was not possible to completely route the %s net using only global resources. This is not "
|
||||||
|
"allowed for dhcen managed networks.\n",
|
||||||
|
ctx->nameOf(net));
|
||||||
|
}
|
||||||
|
|
||||||
|
// In networks controlled by dhcen we disable/enable only HCLK - if
|
||||||
|
// there are ordinary cells among the sinks, then they are not affected
|
||||||
|
// by this primitive.
|
||||||
|
for (PipId pip : path) {
|
||||||
|
// move to upper level net
|
||||||
|
ctx->unbindPip(pip);
|
||||||
|
ctx->bindPip(pip, net_before_dhcen, STRENGTH_LOCKED);
|
||||||
|
|
||||||
|
WireId dst = ctx->getPipDstWire(pip);
|
||||||
|
IdString side;
|
||||||
|
BelId dhcen_bel = gwu.get_dhcen_bel(dst, side);
|
||||||
|
if (dhcen_bel == BelId()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// One pseudo dhcen can be implemented as several hardware dhcen.
|
||||||
|
// Here we find suitable hardware dhcens.
|
||||||
|
CellInfo *hw_dhcen = ctx->getBoundBelCell(dhcen_bel);
|
||||||
|
if (ctx->debug) {
|
||||||
|
log_info(" use %s wire and %s bel for '%s' hw cell.\n", ctx->nameOfWire(dst),
|
||||||
|
ctx->nameOfBel(dhcen_bel), ctx->nameOf(hw_dhcen));
|
||||||
|
}
|
||||||
|
|
||||||
|
// The control network must connect the CE inputs of all hardware dhcens.
|
||||||
|
hw_dhcen->setAttr(id_DHCEN_USED, 1);
|
||||||
|
dhcen_ci->copyPortTo(id_CE, hw_dhcen, id_CE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// connect all users to upper level net
|
||||||
|
std::vector<PortRef> users;
|
||||||
|
for (auto &cell_port : net->users) {
|
||||||
|
users.push_back(cell_port);
|
||||||
|
}
|
||||||
|
for (PortRef &user : users) {
|
||||||
|
user.cell->disconnectPort(user.port);
|
||||||
|
user.cell->connectPort(user.port, net_before_dhcen);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the virtual dhcen
|
||||||
|
dhcen_ci->disconnectPort(id_CLKOUT);
|
||||||
|
dhcen_ci->disconnectPort(id_CLKIN);
|
||||||
|
dhcen_ci->disconnectPort(id_CE);
|
||||||
|
ctx->cells.erase(dhcen_ci->name);
|
||||||
|
}
|
||||||
|
|
||||||
void route_buffered_net(NetInfo *net)
|
void route_buffered_net(NetInfo *net)
|
||||||
{
|
{
|
||||||
// a) route net after buf using the buf input as source
|
// a) route net after buf using the buf input as source
|
||||||
@ -496,7 +571,8 @@ struct GowinGlobalRouter
|
|||||||
NetInfo *net_before_buf = buf_ci->getPort(id_I);
|
NetInfo *net_before_buf = buf_ci->getPort(id_I);
|
||||||
NPNR_ASSERT(net_before_buf != nullptr);
|
NPNR_ASSERT(net_before_buf != nullptr);
|
||||||
|
|
||||||
RouteResult route_result = route_direct_net(net, src);
|
RouteResult route_result = route_direct_net(
|
||||||
|
net, [&](PipId pip) { return global_pip_filter(pip); }, src);
|
||||||
if (route_result == NOT_ROUTED || route_result == ROUTED_PARTIALLY) {
|
if (route_result == NOT_ROUTED || route_result == ROUTED_PARTIALLY) {
|
||||||
log_error("Can't route the %s net. It might be worth removing the BUFG buffer flag.\n", ctx->nameOf(net));
|
log_error("Can't route the %s net. It might be worth removing the BUFG buffer flag.\n", ctx->nameOf(net));
|
||||||
}
|
}
|
||||||
@ -516,7 +592,7 @@ struct GowinGlobalRouter
|
|||||||
|
|
||||||
void route_clk_net(NetInfo *net)
|
void route_clk_net(NetInfo *net)
|
||||||
{
|
{
|
||||||
RouteResult route_result = route_direct_net(net);
|
RouteResult route_result = route_direct_net(net, [&](PipId pip) { return global_pip_filter(pip); });
|
||||||
if (route_result != NOT_ROUTED) {
|
if (route_result != NOT_ROUTED) {
|
||||||
log_info(" '%s' net was routed using global resources %s.\n", ctx->nameOf(net),
|
log_info(" '%s' net was routed using global resources %s.\n", ctx->nameOf(net),
|
||||||
route_result == ROUTED_ALL ? "only" : "partially");
|
route_result == ROUTED_ALL ? "only" : "partially");
|
||||||
@ -527,7 +603,7 @@ struct GowinGlobalRouter
|
|||||||
{
|
{
|
||||||
log_info("Routing globals...\n");
|
log_info("Routing globals...\n");
|
||||||
|
|
||||||
std::vector<IdString> dqce_nets, dcs_nets, buf_nets, clk_nets;
|
std::vector<IdString> dhcen_nets, dqce_nets, dcs_nets, buf_nets, clk_nets;
|
||||||
|
|
||||||
// Determining the priority of network routing
|
// Determining the priority of network routing
|
||||||
for (auto &net : ctx->nets) {
|
for (auto &net : ctx->nets) {
|
||||||
@ -550,11 +626,24 @@ struct GowinGlobalRouter
|
|||||||
} else {
|
} else {
|
||||||
if (driver_is_dcs(ni->driver)) {
|
if (driver_is_dcs(ni->driver)) {
|
||||||
dcs_nets.push_back(net.first);
|
dcs_nets.push_back(net.first);
|
||||||
|
} else {
|
||||||
|
if (driver_is_dhcen(ni->driver)) {
|
||||||
|
dhcen_nets.push_back(net.first);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nets with DHCEN
|
||||||
|
for (IdString net_name : dhcen_nets) {
|
||||||
|
NetInfo *ni = ctx->nets.at(net_name).get();
|
||||||
|
if (ctx->verbose) {
|
||||||
|
log_info("route dhcen net '%s'\n", ctx->nameOf(ni));
|
||||||
|
}
|
||||||
|
route_dhcen_net(ni);
|
||||||
|
}
|
||||||
|
|
||||||
// nets with DQCE
|
// nets with DQCE
|
||||||
for (IdString net_name : dqce_nets) {
|
for (IdString net_name : dqce_nets) {
|
||||||
|
@ -108,12 +108,22 @@ NPNR_PACKED_STRUCT(struct Spine_bel_POD {
|
|||||||
int32_t bel_z;
|
int32_t bel_z;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct Wire_bel_POD {
|
||||||
|
int32_t wire_xy;
|
||||||
|
int32_t wire_name;
|
||||||
|
int32_t bel_x;
|
||||||
|
int32_t bel_y;
|
||||||
|
int32_t bel_z;
|
||||||
|
int32_t side;
|
||||||
|
});
|
||||||
|
|
||||||
NPNR_PACKED_STRUCT(struct Extra_chip_data_POD {
|
NPNR_PACKED_STRUCT(struct Extra_chip_data_POD {
|
||||||
int32_t chip_flags;
|
int32_t chip_flags;
|
||||||
Bottom_io_POD bottom_io;
|
Bottom_io_POD bottom_io;
|
||||||
RelSlice<IdString> diff_io_types;
|
RelSlice<IdString> diff_io_types;
|
||||||
RelSlice<Spine_bel_POD> dqce_bels;
|
RelSlice<Spine_bel_POD> dqce_bels;
|
||||||
RelSlice<Spine_bel_POD> dcs_bels;
|
RelSlice<Spine_bel_POD> dcs_bels;
|
||||||
|
RelSlice<Wire_bel_POD> dhcen_bels;
|
||||||
// chip flags
|
// chip flags
|
||||||
static constexpr int32_t HAS_SP32 = 1;
|
static constexpr int32_t HAS_SP32 = 1;
|
||||||
static constexpr int32_t NEED_SP_FIX = 2;
|
static constexpr int32_t NEED_SP_FIX = 2;
|
||||||
@ -156,6 +166,7 @@ enum
|
|||||||
|
|
||||||
DQCE_Z = 280, // : 286 reserve for 6 DQCEs
|
DQCE_Z = 280, // : 286 reserve for 6 DQCEs
|
||||||
DCS_Z = 286, // : 287 reserve for 2 DCSs
|
DCS_Z = 286, // : 287 reserve for 2 DCSs
|
||||||
|
DHCEN_Z = 288, // : 298
|
||||||
|
|
||||||
// The two least significant bits encode Z for 9-bit adders and
|
// The two least significant bits encode Z for 9-bit adders and
|
||||||
// multipliers, if they are equal to 0, then we get Z of their common
|
// multipliers, if they are equal to 0, then we get Z of their common
|
||||||
|
@ -51,6 +51,7 @@ BANDGAP_Z = 279
|
|||||||
|
|
||||||
DQCE_Z = 280 # : 286 reserve for 6 DQCEs
|
DQCE_Z = 280 # : 286 reserve for 6 DQCEs
|
||||||
DCS_Z = 286 # : 287 reserve for 2 DCSs
|
DCS_Z = 286 # : 287 reserve for 2 DCSs
|
||||||
|
DHCEN_Z = 288 # : 298
|
||||||
|
|
||||||
DSP_Z = 509
|
DSP_Z = 509
|
||||||
|
|
||||||
@ -165,6 +166,26 @@ class SpineBel(BBAStruct):
|
|||||||
bba.u32(self.bel_y)
|
bba.u32(self.bel_y)
|
||||||
bba.u32(self.bel_z)
|
bba.u32(self.bel_z)
|
||||||
|
|
||||||
|
# wire -> bel for DHCEN bels
|
||||||
|
@dataclass
|
||||||
|
class WireBel(BBAStruct):
|
||||||
|
wire_xy: IdString
|
||||||
|
wire_name: IdString
|
||||||
|
bel_x: int
|
||||||
|
bel_y: int
|
||||||
|
bel_z: int
|
||||||
|
hclk_side: IdString
|
||||||
|
|
||||||
|
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||||
|
pass
|
||||||
|
def serialise(self, context: str, bba: BBAWriter):
|
||||||
|
bba.u32(self.wire_xy.index)
|
||||||
|
bba.u32(self.wire_name.index)
|
||||||
|
bba.u32(self.bel_x)
|
||||||
|
bba.u32(self.bel_y)
|
||||||
|
bba.u32(self.bel_z)
|
||||||
|
bba.u32(self.hclk_side.index)
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ChipExtraData(BBAStruct):
|
class ChipExtraData(BBAStruct):
|
||||||
strs: StringPool
|
strs: StringPool
|
||||||
@ -173,6 +194,7 @@ class ChipExtraData(BBAStruct):
|
|||||||
diff_io_types: list[IdString] = field(default_factory = list)
|
diff_io_types: list[IdString] = field(default_factory = list)
|
||||||
dqce_bels: list[SpineBel] = field(default_factory = list)
|
dqce_bels: list[SpineBel] = field(default_factory = list)
|
||||||
dcs_bels: list[SpineBel] = field(default_factory = list)
|
dcs_bels: list[SpineBel] = field(default_factory = list)
|
||||||
|
dhcen_bels: list[WireBel] = field(default_factory = list)
|
||||||
|
|
||||||
def create_bottom_io(self):
|
def create_bottom_io(self):
|
||||||
self.bottom_io = BottomIO()
|
self.bottom_io = BottomIO()
|
||||||
@ -183,6 +205,9 @@ class ChipExtraData(BBAStruct):
|
|||||||
def add_diff_io_type(self, diff_type: str):
|
def add_diff_io_type(self, diff_type: str):
|
||||||
self.diff_io_types.append(self.strs.id(diff_type))
|
self.diff_io_types.append(self.strs.id(diff_type))
|
||||||
|
|
||||||
|
def add_dhcen_bel(self, wire_xy: str, wire_name: str, x: int, y: int, z: int, side: str):
|
||||||
|
self.dhcen_bels.append(WireBel(self.strs.id(wire_xy), self.strs.id(wire_name), x, y, z, self.strs.id(side)))
|
||||||
|
|
||||||
def add_dqce_bel(self, spine: str, x: int, y: int, z: int):
|
def add_dqce_bel(self, spine: str, x: int, y: int, z: int):
|
||||||
self.dqce_bels.append(SpineBel(self.strs.id(spine), x, y, z))
|
self.dqce_bels.append(SpineBel(self.strs.id(spine), x, y, z))
|
||||||
|
|
||||||
@ -200,6 +225,9 @@ class ChipExtraData(BBAStruct):
|
|||||||
bba.label(f"{context}_dcs_bels")
|
bba.label(f"{context}_dcs_bels")
|
||||||
for i, t in enumerate(self.dcs_bels):
|
for i, t in enumerate(self.dcs_bels):
|
||||||
t.serialise(f"{context}_dcs_bel{i}", bba)
|
t.serialise(f"{context}_dcs_bel{i}", bba)
|
||||||
|
bba.label(f"{context}_dhcen_bels")
|
||||||
|
for i, t in enumerate(self.dhcen_bels):
|
||||||
|
t.serialise(f"{context}_dhcen_bel{i}", bba)
|
||||||
|
|
||||||
def serialise(self, context: str, bba: BBAWriter):
|
def serialise(self, context: str, bba: BBAWriter):
|
||||||
bba.u32(self.flags)
|
bba.u32(self.flags)
|
||||||
@ -207,6 +235,7 @@ class ChipExtraData(BBAStruct):
|
|||||||
bba.slice(f"{context}_diff_io_types", len(self.diff_io_types))
|
bba.slice(f"{context}_diff_io_types", len(self.diff_io_types))
|
||||||
bba.slice(f"{context}_dqce_bels", len(self.dqce_bels))
|
bba.slice(f"{context}_dqce_bels", len(self.dqce_bels))
|
||||||
bba.slice(f"{context}_dcs_bels", len(self.dcs_bels))
|
bba.slice(f"{context}_dcs_bels", len(self.dcs_bels))
|
||||||
|
bba.slice(f"{context}_dhcen_bels", len(self.dhcen_bels))
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PadExtraData(BBAStruct):
|
class PadExtraData(BBAStruct):
|
||||||
@ -415,6 +444,9 @@ dqce_bels = {}
|
|||||||
# map spine -> dcs bel
|
# map spine -> dcs bel
|
||||||
dcs_bels = {}
|
dcs_bels = {}
|
||||||
|
|
||||||
|
# map HCLKIN wire -> dhcen bel
|
||||||
|
dhcen_bels = {}
|
||||||
|
|
||||||
def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int):
|
def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int):
|
||||||
if (y, x) not in db.extra_func:
|
if (y, x) not in db.extra_func:
|
||||||
return
|
return
|
||||||
@ -443,6 +475,16 @@ def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int):
|
|||||||
tt.create_wire(wire)
|
tt.create_wire(wire)
|
||||||
bel = tt.create_bel("BANDGAP", "BANDGAP", z = BANDGAP_Z)
|
bel = tt.create_bel("BANDGAP", "BANDGAP", z = BANDGAP_Z)
|
||||||
tt.add_bel_pin(bel, "BGEN", wire, PinType.INPUT)
|
tt.add_bel_pin(bel, "BGEN", wire, PinType.INPUT)
|
||||||
|
elif func == 'dhcen':
|
||||||
|
for idx, dhcen in enumerate(desc):
|
||||||
|
wire = dhcen['ce']
|
||||||
|
if not tt.has_wire(wire):
|
||||||
|
tt.create_wire(wire)
|
||||||
|
bel_z = DHCEN_Z + idx
|
||||||
|
bel = tt.create_bel(f"DHCEN{idx}", "DHCEN", z = bel_z)
|
||||||
|
tt.add_bel_pin(bel, "CE", wire, PinType.INPUT)
|
||||||
|
wire_xy, wire_name, side = dhcen['wire']
|
||||||
|
dhcen_bels[wire_xy, wire_name] = (x, y, bel_z, side)
|
||||||
elif func == 'dqce':
|
elif func == 'dqce':
|
||||||
for idx in range(6):
|
for idx in range(6):
|
||||||
bel_z = DQCE_Z + idx
|
bel_z = DQCE_Z + idx
|
||||||
@ -1144,6 +1186,9 @@ def create_extra_data(chip: Chip, db: chipdb, chip_flags: int):
|
|||||||
chip.extra_data.add_bottom_io_cnd(net_a, net_b)
|
chip.extra_data.add_bottom_io_cnd(net_a, net_b)
|
||||||
for diff_type in db.diff_io_types:
|
for diff_type in db.diff_io_types:
|
||||||
chip.extra_data.add_diff_io_type(diff_type)
|
chip.extra_data.add_diff_io_type(diff_type)
|
||||||
|
# create hclk wire->dhcen bel map
|
||||||
|
for wire, bel in dhcen_bels.items():
|
||||||
|
chip.extra_data.add_dhcen_bel(wire[0], wire[1], bel[0], bel[1], bel[2], bel[3])
|
||||||
# create spine->dqce bel map
|
# create spine->dqce bel map
|
||||||
for spine, bel in dqce_bels.items():
|
for spine, bel in dqce_bels.items():
|
||||||
chip.extra_data.add_dqce_bel(spine, bel[0], bel[1], bel[2])
|
chip.extra_data.add_dqce_bel(spine, bel[0], bel[1], bel[2])
|
||||||
|
@ -87,6 +87,21 @@ BelId GowinUtils::get_dcs_bel(IdString spine_name)
|
|||||||
return BelId();
|
return BelId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BelId GowinUtils::get_dhcen_bel(WireId hclkin_wire, IdString &side)
|
||||||
|
{
|
||||||
|
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
|
||||||
|
for (auto &wire_bel : extra->dhcen_bels) {
|
||||||
|
IdStringList wire_name = IdStringList::concat(IdString(wire_bel.wire_xy), IdString(wire_bel.wire_name));
|
||||||
|
WireId wire = normalize_wire(ctx->getWireByName(wire_name));
|
||||||
|
|
||||||
|
if (wire == hclkin_wire) {
|
||||||
|
side = IdString(wire_bel.side);
|
||||||
|
return ctx->getBelByLocation(Loc(wire_bel.bel_x, wire_bel.bel_y, wire_bel.bel_z));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BelId();
|
||||||
|
}
|
||||||
|
|
||||||
bool GowinUtils::is_simple_io_bel(BelId bel)
|
bool GowinUtils::is_simple_io_bel(BelId bel)
|
||||||
{
|
{
|
||||||
return chip_bel_info(ctx->chip_info, bel).flags & BelFlags::FLAG_SIMPLE_IO;
|
return chip_bel_info(ctx->chip_info, bel).flags & BelFlags::FLAG_SIMPLE_IO;
|
||||||
|
@ -35,6 +35,10 @@ struct GowinUtils
|
|||||||
BelId get_io_bel_from_iologic(BelId bel);
|
BelId get_io_bel_from_iologic(BelId bel);
|
||||||
BelId get_dqce_bel(IdString spine_name);
|
BelId get_dqce_bel(IdString spine_name);
|
||||||
BelId get_dcs_bel(IdString spine_name);
|
BelId get_dcs_bel(IdString spine_name);
|
||||||
|
BelId get_dhcen_bel(WireId hclkin_wire, IdString &side);
|
||||||
|
|
||||||
|
// Wires
|
||||||
|
WireId normalize_wire(WireId wire) { return ctx->normalise_wire(wire.tile, wire.index); }
|
||||||
|
|
||||||
// BSRAM
|
// BSRAM
|
||||||
bool has_SP32(void);
|
bool has_SP32(void);
|
||||||
|
@ -3072,6 +3072,32 @@ struct GowinPacker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =========================================
|
||||||
|
// Create DHCENs
|
||||||
|
// =========================================
|
||||||
|
void pack_dhcens()
|
||||||
|
{
|
||||||
|
// Allocate all available dhcen bels; we will find out which of them
|
||||||
|
// will actually be used during the routing process.
|
||||||
|
bool grab_bels = false;
|
||||||
|
for (auto &cell : ctx->cells) {
|
||||||
|
auto &ci = *cell.second;
|
||||||
|
if (ci.type == id_DHCEN) {
|
||||||
|
ci.pseudo_cell = std::make_unique<RegionPlug>(Loc(0, 0, 0));
|
||||||
|
grab_bels = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (grab_bels) {
|
||||||
|
int i = 0;
|
||||||
|
for (auto &bel : ctx->getBelsInBucket(ctx->getBelBucketForCellType(id_DHCEN))) {
|
||||||
|
IdString dhcen_name = ctx->idf("$PACKER_DHCEN_%d", ++i);
|
||||||
|
CellInfo *dhcen = ctx->createCell(dhcen_name, id_DHCEN);
|
||||||
|
dhcen->addInput(id_CE);
|
||||||
|
ctx->bindBel(bel, dhcen, STRENGTH_LOCKED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void run(void)
|
void run(void)
|
||||||
{
|
{
|
||||||
handle_constants();
|
handle_constants();
|
||||||
@ -3124,6 +3150,9 @@ struct GowinPacker
|
|||||||
pack_buffered_nets();
|
pack_buffered_nets();
|
||||||
ctx->check();
|
ctx->check();
|
||||||
|
|
||||||
|
pack_dhcens();
|
||||||
|
ctx->check();
|
||||||
|
|
||||||
pack_dqce();
|
pack_dqce();
|
||||||
ctx->check();
|
ctx->check();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user