mirror of https://github.com/YosysHQ/nextpnr.git
Gowin. Implement the DLLDLY primitive. (#1464)
DLLDLY is the clock delay primitive that adjust the input clock according to the DLLSTEP signal and outputs the delayed clock. These primitives are associated with clock pins and are "tapped" between the output of this IBUF and the clock networks, leaving the possibility to connect to the original unshifted signal as well, although the latter is not very practical because it is no longer possible to use fast wires. Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
parent
864c1e471d
commit
c84879e4d5
|
|
@ -1346,6 +1346,13 @@ X(TREG_LSR_NET)
|
||||||
X(NOIOBFF)
|
X(NOIOBFF)
|
||||||
X(IOBFF)
|
X(IOBFF)
|
||||||
|
|
||||||
|
// DLLDLY
|
||||||
|
X(DLLDLY)
|
||||||
|
X(DLLSTEP)
|
||||||
|
X(DLLDLY_O)
|
||||||
|
X(DLLDLY_CLKOUT0)
|
||||||
|
X(DLLDLY_CLKOUT1)
|
||||||
|
|
||||||
// segments
|
// segments
|
||||||
X(LW_TAP)
|
X(LW_TAP)
|
||||||
X(LW_TAP_0)
|
X(LW_TAP_0)
|
||||||
|
|
|
||||||
|
|
@ -57,21 +57,20 @@ struct GowinGlobalRouter
|
||||||
bool not_dsc_pip = dst_name != id_CLKOUT;
|
bool not_dsc_pip = dst_name != id_CLKOUT;
|
||||||
IdString src_type = ctx->getWireType(src);
|
IdString src_type = ctx->getWireType(src);
|
||||||
IdString dst_type = ctx->getWireType(dst);
|
IdString dst_type = ctx->getWireType(dst);
|
||||||
bool src_valid = not_dsc_pip && src_type.in(id_GLOBAL_CLK, id_IO_O, id_PLL_O, id_HCLK);
|
bool src_valid = not_dsc_pip && src_type.in(id_GLOBAL_CLK, id_IO_O, id_PLL_O, id_HCLK, id_DLLDLY);
|
||||||
bool dst_valid = not_dsc_pip && dst_type.in(id_GLOBAL_CLK, id_TILE_CLK, id_PLL_I, id_IO_I, id_HCLK);
|
bool dst_valid = not_dsc_pip && dst_type.in(id_GLOBAL_CLK, id_TILE_CLK, id_PLL_I, id_IO_I, id_HCLK);
|
||||||
|
|
||||||
bool res;
|
bool res;
|
||||||
if (src == src_wire && (!src_type.in(id_IO, id_HCLK))) {
|
if (src == src_wire && (!src_type.in(id_IO_O, id_HCLK, id_DLLDLY_O))) {
|
||||||
bool dst_is_spine = dst_name.str(ctx).rfind("SPINE", 0) == 0;
|
bool dst_is_spine = dst_name.str(ctx).rfind("SPINE", 0) == 0;
|
||||||
res = src_valid && dst_is_spine;
|
res = src_valid && dst_is_spine;
|
||||||
} else {
|
} else {
|
||||||
res = (src_valid && dst_valid) || (src_valid && is_local(dst_type)) || (is_local(src_type) && dst_valid);
|
res = (src_valid && dst_valid) || (src_valid && is_local(dst_type)) || (is_local(src_type) && dst_valid);
|
||||||
}
|
}
|
||||||
if (ctx->debug && false /*&& res*/) {
|
if (ctx->debug && false /*&& res*/) {
|
||||||
log_info("%s <- %s [%s <- %s]\n", ctx->getWireName(ctx->getPipDstWire(pip)).str(ctx).c_str(),
|
log_info("%s <- %s [%s <- %s]\n", ctx->nameOfWire(ctx->getPipDstWire(pip)),
|
||||||
ctx->getWireName(ctx->getPipSrcWire(pip)).str(ctx).c_str(), dst_type.c_str(ctx),
|
ctx->nameOfWire(ctx->getPipSrcWire(pip)), dst_type.c_str(ctx), src_type.c_str(ctx));
|
||||||
src_type.c_str(ctx));
|
log_info(" res:%d, src_valid:%d, dst_valid:%d, src local:%d, dst local:%d\n", res, src_valid, dst_valid,
|
||||||
log_info("res:%d, src_valid:%d, dst_valid:%d, src local:%d, dst local:%d\n", res, src_valid, dst_valid,
|
|
||||||
is_local(src_type), is_local(dst_type));
|
is_local(src_type), is_local(dst_type));
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
|
|
@ -103,17 +102,17 @@ struct GowinGlobalRouter
|
||||||
// If DQCE is used, then the source can only connect to SPINEs as only they can be switched off/on.
|
// If DQCE is used, then the source can only connect to SPINEs as only they can be switched off/on.
|
||||||
bool res;
|
bool res;
|
||||||
if (src == src_wire) {
|
if (src == src_wire) {
|
||||||
bool dst_is_spine = dst_name.str(ctx).rfind("SPINE", 0) == 0;
|
bool dst_is_spine = (dst_name.str(ctx).rfind("SPINE", 0) == 0 || dst_name.str(ctx).rfind("PCLK", 0) == 0 ||
|
||||||
|
dst_name.str(ctx).rfind("LWSPINE", 0) == 0);
|
||||||
res = src_valid && dst_is_spine;
|
res = src_valid && dst_is_spine;
|
||||||
} else {
|
} else {
|
||||||
res = (src_valid && dst_valid) || (src_valid && is_local(dst_type)) || (is_local(src_type) && dst_valid);
|
res = (src_valid && dst_valid) || (src_valid && is_local(dst_type)) || (is_local(src_type) && dst_valid);
|
||||||
if (ctx->debug && false /*res*/) {
|
}
|
||||||
log_info("%s <- %s [%s <- %s]\n", ctx->getWireName(ctx->getPipDstWire(pip)).str(ctx).c_str(),
|
if (ctx->debug && false /*res*/) {
|
||||||
ctx->getWireName(ctx->getPipSrcWire(pip)).str(ctx).c_str(), dst_type.c_str(ctx),
|
log_info("%s <- %s [%s <- %s]\n", ctx->nameOfWire(ctx->getPipDstWire(pip)),
|
||||||
src_type.c_str(ctx));
|
ctx->nameOfWire(ctx->getPipSrcWire(pip)), dst_type.c_str(ctx), src_type.c_str(ctx));
|
||||||
log_info("res:%d, src_valid:%d, dst_valid:%d, src local:%d, dst local:%d\n", res, src_valid, dst_valid,
|
log_info(" res:%d, src_valid:%d, dst_valid:%d, src local:%d, dst local:%d\n", res, src_valid, dst_valid,
|
||||||
is_local(src_type), is_local(dst_type));
|
is_local(src_type), is_local(dst_type));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
@ -291,6 +290,16 @@ struct GowinGlobalRouter
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// DLLDLY outputs
|
||||||
|
if (driver.cell->type == id_DLLDLY) {
|
||||||
|
if (driver.port.in(id_CLKOUT)) {
|
||||||
|
if (ctx->debug) {
|
||||||
|
log_info("%s out:%s:%s\n", driver.cell->type.c_str(ctx),
|
||||||
|
ctx->getBelName(driver.cell->bel).str(ctx).c_str(), driver.port.c_str(ctx));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -384,7 +393,26 @@ struct GowinGlobalRouter
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
WireId dst = ctx->getPipDstWire(pip);
|
WireId dst = ctx->getPipDstWire(pip);
|
||||||
|
IdString dst_name = ctx->getWireName(dst)[1];
|
||||||
|
if (dst_name.str(ctx).rfind("PCLK", 0) == 0 || dst_name.str(ctx).rfind("LWSPINE", 0) == 0) {
|
||||||
|
// step over dummy pip
|
||||||
|
for (PipId next_pip : ctx->getPipsDownhill(dst)) {
|
||||||
|
if (ctx->getBoundPipNet(next_pip) != nullptr) {
|
||||||
|
ctx->unbindPip(pip);
|
||||||
|
src = dst;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (src == dst) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (PipId pip : ctx->getPipsDownhill(src)) {
|
||||||
|
if (ctx->getBoundPipNet(pip) == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
WireId dst = ctx->getPipDstWire(pip);
|
||||||
BelId dqce_bel = gwu.get_dqce_bel(ctx->getWireName(dst)[1]);
|
BelId dqce_bel = gwu.get_dqce_bel(ctx->getWireName(dst)[1]);
|
||||||
NPNR_ASSERT(dqce_bel != BelId());
|
NPNR_ASSERT(dqce_bel != BelId());
|
||||||
|
|
||||||
|
|
@ -459,6 +487,25 @@ struct GowinGlobalRouter
|
||||||
// "spine" wires. Here we not only check this fact, but also find out
|
// "spine" wires. Here we not only check this fact, but also find out
|
||||||
// how many and what kind of "spine" wires were used for network
|
// how many and what kind of "spine" wires were used for network
|
||||||
// roaming.
|
// roaming.
|
||||||
|
for (PipId pip : ctx->getPipsDownhill(src)) {
|
||||||
|
if (ctx->getBoundPipNet(pip) == nullptr) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
WireId dst = ctx->getPipDstWire(pip);
|
||||||
|
IdString dst_name = ctx->getWireName(dst)[1];
|
||||||
|
if (dst_name.str(ctx).rfind("PCLK", 0) == 0 || dst_name.str(ctx).rfind("LWSPINE", 0) == 0) {
|
||||||
|
// step over dummy pip
|
||||||
|
for (PipId next_pip : ctx->getPipsDownhill(dst)) {
|
||||||
|
if (ctx->getBoundPipNet(next_pip) != nullptr) {
|
||||||
|
src = dst;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (src == dst) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
for (PipId pip : ctx->getPipsDownhill(src)) {
|
for (PipId pip : ctx->getPipsDownhill(src)) {
|
||||||
if (ctx->getBoundPipNet(pip) == nullptr) {
|
if (ctx->getBoundPipNet(pip) == nullptr) {
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -1222,7 +1269,7 @@ struct GowinGlobalRouter
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (ctx->verbose) {
|
if (ctx->verbose) {
|
||||||
log_info("route clock net '%s'\n", ctx->nameOf(ni));
|
log_info("route clock net '%s', src:%s\n", ctx->nameOf(ni), ctx->nameOf(ni->driver.cell));
|
||||||
}
|
}
|
||||||
route_clk_net(ni);
|
route_clk_net(ni);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,11 @@ NPNR_PACKED_STRUCT(struct Spine_bel_POD {
|
||||||
int32_t bel_z;
|
int32_t bel_z;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct Io_dlldly_bel_POD {
|
||||||
|
int32_t io;
|
||||||
|
int32_t dlldly;
|
||||||
|
});
|
||||||
|
|
||||||
NPNR_PACKED_STRUCT(struct Wire_bel_POD {
|
NPNR_PACKED_STRUCT(struct Wire_bel_POD {
|
||||||
int32_t pip_xy;
|
int32_t pip_xy;
|
||||||
int32_t pip_dst;
|
int32_t pip_dst;
|
||||||
|
|
@ -181,6 +186,7 @@ NPNR_PACKED_STRUCT(struct Extra_chip_data_POD {
|
||||||
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;
|
RelSlice<Wire_bel_POD> dhcen_bels;
|
||||||
|
RelSlice<Io_dlldly_bel_POD> io_dlldly_bels;
|
||||||
RelSlice<Segment_POD> segments;
|
RelSlice<Segment_POD> segments;
|
||||||
// chip flags
|
// chip flags
|
||||||
static constexpr int32_t HAS_SP32 = 1;
|
static constexpr int32_t HAS_SP32 = 1;
|
||||||
|
|
@ -235,6 +241,8 @@ enum
|
||||||
MIPIOBUF_Z = 301,
|
MIPIOBUF_Z = 301,
|
||||||
MIPIIBUF_Z = 302,
|
MIPIIBUF_Z = 302,
|
||||||
|
|
||||||
|
DLLDLY_Z = 303, // : 305 reserve for 2 DLLDLYs
|
||||||
|
|
||||||
// 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
|
||||||
// 18-bit equivalent.
|
// 18-bit equivalent.
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,8 @@ EMCU_Z = 300
|
||||||
MIPIOBUF_Z = 301
|
MIPIOBUF_Z = 301
|
||||||
MIPIIBUF_Z = 302
|
MIPIIBUF_Z = 302
|
||||||
|
|
||||||
|
DLLDLY_Z = 303 # : 305 reserve for 2 DLLDLYs
|
||||||
|
|
||||||
DSP_Z = 509
|
DSP_Z = 509
|
||||||
|
|
||||||
DSP_0_Z = 511 # DSP macro 0
|
DSP_0_Z = 511 # DSP macro 0
|
||||||
|
|
@ -180,6 +182,18 @@ class SpineBel(BBAStruct):
|
||||||
bba.u32(self.bel_y)
|
bba.u32(self.bel_y)
|
||||||
bba.u32(self.bel_z)
|
bba.u32(self.bel_z)
|
||||||
|
|
||||||
|
# io -> dlldly bels
|
||||||
|
@dataclass
|
||||||
|
class IoBel(BBAStruct):
|
||||||
|
io: IdString
|
||||||
|
dlldly: IdString
|
||||||
|
|
||||||
|
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||||
|
pass
|
||||||
|
def serialise(self, context: str, bba: BBAWriter):
|
||||||
|
bba.u32(self.io.index)
|
||||||
|
bba.u32(self.dlldly.index)
|
||||||
|
|
||||||
# wire -> bel for DHCEN bels
|
# wire -> bel for DHCEN bels
|
||||||
@dataclass
|
@dataclass
|
||||||
class WireBel(BBAStruct):
|
class WireBel(BBAStruct):
|
||||||
|
|
@ -249,6 +263,7 @@ class ChipExtraData(BBAStruct):
|
||||||
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)
|
dhcen_bels: list[WireBel] = field(default_factory = list)
|
||||||
|
io_dlldly_bels: list[IoBel] = field(default_factory = list)
|
||||||
segments: list[Segment] = field(default_factory = list)
|
segments: list[Segment] = field(default_factory = list)
|
||||||
|
|
||||||
def create_bottom_io(self):
|
def create_bottom_io(self):
|
||||||
|
|
@ -269,6 +284,8 @@ class ChipExtraData(BBAStruct):
|
||||||
def add_dcs_bel(self, spine: str, x: int, y: int, z: int):
|
def add_dcs_bel(self, spine: str, x: int, y: int, z: int):
|
||||||
self.dcs_bels.append(SpineBel(self.strs.id(spine), x, y, z))
|
self.dcs_bels.append(SpineBel(self.strs.id(spine), x, y, z))
|
||||||
|
|
||||||
|
def add_io_dlldly_bel(self, io: str, dlldly: str):
|
||||||
|
self.io_dlldly_bels.append(IoBel(self.strs.id(io), self.strs.id(dlldly)))
|
||||||
def add_segment(self, x: int, seg_idx: int, min_x: int, min_y: int, max_x: int, max_y: int,
|
def add_segment(self, x: int, seg_idx: int, min_x: int, min_y: int, max_x: int, max_y: int,
|
||||||
top_row: int, bottom_row: int, top_wire: str, bottom_wire: str, top_gate_wire: list, bottom_gate_wire: list):
|
top_row: int, bottom_row: int, top_wire: str, bottom_wire: str, top_gate_wire: list, bottom_gate_wire: list):
|
||||||
new_seg = Segment(x, seg_idx, min_x, min_y, max_x, max_y, top_row, bottom_row,
|
new_seg = Segment(x, seg_idx, min_x, min_y, max_x, max_y, top_row, bottom_row,
|
||||||
|
|
@ -300,6 +317,9 @@ class ChipExtraData(BBAStruct):
|
||||||
bba.label(f"{context}_dhcen_bels")
|
bba.label(f"{context}_dhcen_bels")
|
||||||
for i, t in enumerate(self.dhcen_bels):
|
for i, t in enumerate(self.dhcen_bels):
|
||||||
t.serialise(f"{context}_dhcen_bel{i}", bba)
|
t.serialise(f"{context}_dhcen_bel{i}", bba)
|
||||||
|
bba.label(f"{context}_io_dlldly_bels")
|
||||||
|
for i, t in enumerate(self.io_dlldly_bels):
|
||||||
|
t.serialise(f"{context}_io_dlldly_bel{i}", bba)
|
||||||
bba.label(f"{context}_segments")
|
bba.label(f"{context}_segments")
|
||||||
for i, t in enumerate(self.segments):
|
for i, t in enumerate(self.segments):
|
||||||
t.serialise(f"{context}_segment{i}", bba)
|
t.serialise(f"{context}_segment{i}", bba)
|
||||||
|
|
@ -311,6 +331,7 @@ class ChipExtraData(BBAStruct):
|
||||||
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))
|
bba.slice(f"{context}_dhcen_bels", len(self.dhcen_bels))
|
||||||
|
bba.slice(f"{context}_io_dlldly_bels", len(self.io_dlldly_bels))
|
||||||
bba.slice(f"{context}_segments", len(self.segments))
|
bba.slice(f"{context}_segments", len(self.segments))
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
|
@ -444,6 +465,9 @@ def create_nodes(chip: Chip, db: chipdb):
|
||||||
wire_type, node = node_hdr
|
wire_type, node = node_hdr
|
||||||
if len(node) < 2:
|
if len(node) < 2:
|
||||||
continue
|
continue
|
||||||
|
min_wire_name_len = 0
|
||||||
|
if node:
|
||||||
|
min_wire_name_len = len(next(iter(node))[2])
|
||||||
for y, x, wire in node:
|
for y, x, wire in node:
|
||||||
if wire_type:
|
if wire_type:
|
||||||
if not chip.tile_type_at(x, y).has_wire(wire):
|
if not chip.tile_type_at(x, y).has_wire(wire):
|
||||||
|
|
@ -453,7 +477,11 @@ def create_nodes(chip: Chip, db: chipdb):
|
||||||
new_node = NodeWire(x, y, wire)
|
new_node = NodeWire(x, y, wire)
|
||||||
gl_nodes = global_nodes.setdefault(node_name, [])
|
gl_nodes = global_nodes.setdefault(node_name, [])
|
||||||
if new_node not in gl_nodes:
|
if new_node not in gl_nodes:
|
||||||
gl_nodes.append(NodeWire(x, y, wire))
|
if len(wire) < min_wire_name_len:
|
||||||
|
min_wire_name_len = len(wire)
|
||||||
|
gl_nodes.insert(0, new_node)
|
||||||
|
else:
|
||||||
|
gl_nodes.append(new_node)
|
||||||
|
|
||||||
for name, node in global_nodes.items():
|
for name, node in global_nodes.items():
|
||||||
chip.add_node(node)
|
chip.add_node(node)
|
||||||
|
|
@ -462,6 +490,10 @@ def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int):
|
||||||
def get_wire_type(name):
|
def get_wire_type(name):
|
||||||
if name in {'XD0', 'XD1', 'XD2', 'XD3', 'XD4', 'XD5',}:
|
if name in {'XD0', 'XD1', 'XD2', 'XD3', 'XD4', 'XD5',}:
|
||||||
return "X0"
|
return "X0"
|
||||||
|
if name in {"PCLK_DUMMY"}:
|
||||||
|
return "GLOBAL_CLK"
|
||||||
|
if name in {"DLLDLY_OUT"}:
|
||||||
|
return "DLLDLY_O"
|
||||||
if name in {'LT00', 'LT10', 'LT20', 'LT30', 'LT02', 'LT13'}:
|
if name in {'LT00', 'LT10', 'LT20', 'LT30', 'LT02', 'LT13'}:
|
||||||
return "LW_TAP"
|
return "LW_TAP"
|
||||||
return ""
|
return ""
|
||||||
|
|
@ -550,6 +582,9 @@ dcs_bels = {}
|
||||||
# map HCLKIN wire -> dhcen bel
|
# map HCLKIN wire -> dhcen bel
|
||||||
dhcen_bels = {}
|
dhcen_bels = {}
|
||||||
|
|
||||||
|
# map io bel -> dlldly bel
|
||||||
|
io_dlldly_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
|
||||||
|
|
@ -588,6 +623,20 @@ def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int):
|
||||||
tt.add_bel_pin(bel, "CE", wire, PinType.INPUT)
|
tt.add_bel_pin(bel, "CE", wire, PinType.INPUT)
|
||||||
pip_xy, pip_dst, pip_src, side = dhcen['pip']
|
pip_xy, pip_dst, pip_src, side = dhcen['pip']
|
||||||
dhcen_bels[pip_xy, pip_dst, pip_src] = (x, y, bel_z, side)
|
dhcen_bels[pip_xy, pip_dst, pip_src] = (x, y, bel_z, side)
|
||||||
|
elif func == 'dlldly':
|
||||||
|
for idx, dlldly in desc.items():
|
||||||
|
bel_z = DLLDLY_Z + idx
|
||||||
|
bel = tt.create_bel(f"DLLDLY{idx}", "DLLDLY", z = bel_z)
|
||||||
|
for pin, wire in dlldly['in_wires'].items():
|
||||||
|
if not tt.has_wire(wire):
|
||||||
|
tt.create_wire(wire)
|
||||||
|
tt.add_bel_pin(bel, pin, wire, PinType.INPUT)
|
||||||
|
for pin, wire in dlldly['out_wires'].items():
|
||||||
|
if not tt.has_wire(wire):
|
||||||
|
tt.create_wire(wire)
|
||||||
|
tt.add_bel_pin(bel, pin, wire, PinType.OUTPUT)
|
||||||
|
io_dlldly_bels[f"{dlldly['io_loc']}/{dlldly['io_bel']}"] = f"X{x}Y{y}/DLLDLY{idx}"
|
||||||
|
|
||||||
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
|
||||||
|
|
@ -1346,6 +1395,9 @@ def create_extra_data(chip: Chip, db: chipdb, chip_flags: int):
|
||||||
# create spine->dcs bel map
|
# create spine->dcs bel map
|
||||||
for spine, bel in dcs_bels.items():
|
for spine, bel in dcs_bels.items():
|
||||||
chip.extra_data.add_dcs_bel(spine, bel[0], bel[1], bel[2])
|
chip.extra_data.add_dcs_bel(spine, bel[0], bel[1], bel[2])
|
||||||
|
# create iob->dlldly bel map
|
||||||
|
for io, dlldly in io_dlldly_bels.items():
|
||||||
|
chip.extra_data.add_io_dlldly_bel(io, dlldly)
|
||||||
# create segments
|
# create segments
|
||||||
if hasattr(db, "segments"):
|
if hasattr(db, "segments"):
|
||||||
for y_x_idx, seg in db.segments.items():
|
for y_x_idx, seg in db.segments.items():
|
||||||
|
|
|
||||||
|
|
@ -180,6 +180,17 @@ BelId GowinUtils::get_dcs_bel(IdString spine_name)
|
||||||
return BelId();
|
return BelId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BelId GowinUtils::get_dlldly_bel(BelId io_bel)
|
||||||
|
{
|
||||||
|
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
|
||||||
|
for (auto &io : extra->io_dlldly_bels) {
|
||||||
|
if (IdStringList::parse(ctx, (IdString(io.io)).str(ctx)) == ctx->getBelName(io_bel)) {
|
||||||
|
return ctx->getBelByName(IdStringList::parse(ctx, (IdString(io.dlldly)).str(ctx)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BelId();
|
||||||
|
}
|
||||||
|
|
||||||
BelId GowinUtils::get_dhcen_bel(WireId hclkin_wire, IdString &side)
|
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());
|
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ struct GowinUtils
|
||||||
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);
|
BelId get_dhcen_bel(WireId hclkin_wire, IdString &side);
|
||||||
|
BelId get_dlldly_bel(BelId io_bel);
|
||||||
|
|
||||||
// Segments
|
// Segments
|
||||||
int get_segments_count(void) const;
|
int get_segments_count(void) const;
|
||||||
|
|
|
||||||
|
|
@ -3816,6 +3816,42 @@ struct GowinPacker
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===================================
|
||||||
|
// DLLDLY
|
||||||
|
// ===================================
|
||||||
|
void pack_dlldly()
|
||||||
|
{
|
||||||
|
log_info("Pack DLLDLYs...\n");
|
||||||
|
|
||||||
|
for (auto &cell : ctx->cells) {
|
||||||
|
auto ci = cell.second.get();
|
||||||
|
if (ci->type != id_DLLDLY)
|
||||||
|
continue;
|
||||||
|
NetInfo *clkin_net = ci->getPort(id_CLKIN);
|
||||||
|
NetInfo *clkout_net = ci->getPort(id_CLKOUT);
|
||||||
|
if (clkin_net == nullptr || clkout_net == nullptr) {
|
||||||
|
log_error("%s cell has unconnected CLKIN or CLKOUT pins.\n", ctx->nameOf(ci));
|
||||||
|
}
|
||||||
|
CellInfo *clk_src = clkin_net->driver.cell;
|
||||||
|
if (!is_io(clk_src)) {
|
||||||
|
log_error("Clock source for DLLDLY %s is not IO: %s.\n", ctx->nameOf(ci), ctx->nameOf(clk_src));
|
||||||
|
}
|
||||||
|
// DLLDLY placement is fixed
|
||||||
|
BelId io_bel = clk_src->bel;
|
||||||
|
BelId dlldly_bel = gwu.get_dlldly_bel(io_bel);
|
||||||
|
if (dlldly_bel == BelId()) {
|
||||||
|
log_error("Can't use IO %s as input for DLLDLY %s.\n", ctx->nameOf(clk_src), ctx->nameOf(ci));
|
||||||
|
}
|
||||||
|
if (ctx->verbose) {
|
||||||
|
log_info(" pack %s to use clock pin at %s\n", ctx->nameOf(ci), ctx->nameOfBel(io_bel));
|
||||||
|
}
|
||||||
|
ctx->bindBel(dlldly_bel, ci, STRENGTH_LOCKED);
|
||||||
|
for (int i = 0; i < 8; ++i) {
|
||||||
|
ci->renamePort(ctx->idf("DLLSTEP[%d]", i), ctx->idf("DLLSTEP%d", i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// =========================================
|
// =========================================
|
||||||
// Create entry points to the clock system
|
// Create entry points to the clock system
|
||||||
// =========================================
|
// =========================================
|
||||||
|
|
@ -4188,6 +4224,9 @@ struct GowinPacker
|
||||||
pack_hclk();
|
pack_hclk();
|
||||||
ctx->check();
|
ctx->check();
|
||||||
|
|
||||||
|
pack_dlldly();
|
||||||
|
ctx->check();
|
||||||
|
|
||||||
pack_bandgap();
|
pack_bandgap();
|
||||||
ctx->check();
|
ctx->check();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue