gowin: Himbaechel. Add SERDES and differential IO
- experiment with notifyBelChange as an auxiliary cells reservation mechanism; - since HCLK pips depend on the coordinates, and not on the tile type, the tile type is copied if necessary; - information about supported types of differential IO primitives has been added to the extra information of the chip; Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
parent
01044cc910
commit
5e9a96d358
@ -1089,3 +1089,9 @@ X(XD3)
|
||||
X(XD4)
|
||||
X(XD5)
|
||||
|
||||
// HCLK wires
|
||||
X(HCLK_OUT0)
|
||||
X(HCLK_OUT1)
|
||||
X(HCLK_OUT2)
|
||||
X(HCLK_OUT3)
|
||||
|
||||
|
@ -52,12 +52,15 @@ struct GowinGlobalRouter
|
||||
bool src_valid = src_type.in(id_GLOBAL_CLK, id_IO_O, id_PLL_O);
|
||||
bool dst_valid = dst_type.in(id_GLOBAL_CLK, id_TILE_CLK, id_PLL_I, id_IO_I);
|
||||
|
||||
if (ctx->debug && false) {
|
||||
bool res = (src_valid && dst_valid) || (src_valid && is_local(dst_type)) || (is_local(src_type) && dst_valid);
|
||||
if (ctx->debug && res && false) {
|
||||
log_info("%s <- %s [%s <- %s]\n", ctx->getWireName(ctx->getPipDstWire(pip)).str(ctx).c_str(),
|
||||
ctx->getWireName(ctx->getPipSrcWire(pip)).str(ctx).c_str(), 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,
|
||||
// is_local(src_type), is_local(dst_type));
|
||||
}
|
||||
return (src_valid && dst_valid) || (src_valid && is_local(dst_type)) || (is_local(src_type) && dst_valid);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool is_relaxed_sink(const PortRef &sink) const { return false; }
|
||||
|
@ -29,6 +29,7 @@ struct GowinImpl : HimbaechelAPI
|
||||
void prePlace() override;
|
||||
void postPlace() override;
|
||||
void preRoute() override;
|
||||
void postRoute() override;
|
||||
|
||||
bool isBelLocationValid(BelId bel, bool explain_invalid) const override;
|
||||
|
||||
@ -37,6 +38,10 @@ struct GowinImpl : HimbaechelAPI
|
||||
|
||||
bool isValidBelForCellType(IdString cell_type, BelId bel) const override;
|
||||
|
||||
// placer hits
|
||||
void notifyBelChange(BelId bel, CellInfo *cell) override;
|
||||
bool checkBelAvail(BelId bel) const override;
|
||||
|
||||
private:
|
||||
HimbaechelHelpers h;
|
||||
GowinUtils gwu;
|
||||
@ -44,6 +49,8 @@ struct GowinImpl : HimbaechelAPI
|
||||
IdString chip;
|
||||
IdString partno;
|
||||
|
||||
std::set<BelId> inactive_bels;
|
||||
|
||||
// Validity checking
|
||||
struct GowinCellInfo
|
||||
{
|
||||
@ -154,8 +161,11 @@ void GowinImpl::postPlace()
|
||||
log_info("================== Final Placement ===================\n");
|
||||
for (auto &cell : ctx->cells) {
|
||||
auto ci = cell.second.get();
|
||||
IdStringList bel = ctx->getBelName(ci->bel);
|
||||
log_info("%s: %s\n", bel.str(ctx).c_str(), ctx->nameOf(ci));
|
||||
if (ci->bel != BelId()) {
|
||||
log_info("%s: %s\n", ctx->nameOfBel(ci->bel), ctx->nameOf(ci));
|
||||
} else {
|
||||
log_info("unknown: %s\n", ctx->nameOf(ci));
|
||||
}
|
||||
}
|
||||
log_break();
|
||||
}
|
||||
@ -163,6 +173,43 @@ void GowinImpl::postPlace()
|
||||
|
||||
void GowinImpl::preRoute() { gowin_route_globals(ctx); }
|
||||
|
||||
void GowinImpl::postRoute()
|
||||
{
|
||||
std::set<IdString> visited_hclk_users;
|
||||
|
||||
for (auto &cell : ctx->cells) {
|
||||
auto ci = cell.second.get();
|
||||
if (is_iologic(ci) && !ci->type.in(id_ODDR, id_ODDRC, id_IDDR, id_IDDRC)) {
|
||||
if (visited_hclk_users.find(ci->name) == visited_hclk_users.end()) {
|
||||
// mark FCLK<-HCLK connections
|
||||
ci->setAttr(id_IOLOGIC_FCLK, Property("UNKNOWN"));
|
||||
const NetInfo *h_net = ci->getPort(id_FCLK);
|
||||
if (h_net) {
|
||||
for (auto const &user : h_net->users) {
|
||||
if (user.port != id_FCLK) {
|
||||
continue;
|
||||
}
|
||||
visited_hclk_users.insert(user.cell->name);
|
||||
// XXX Based on the implementation, perhaps a function
|
||||
// is needed to get Pip from a Wire
|
||||
PipId up_pip = h_net->wires.at(ctx->getNetinfoSinkWire(h_net, user, 0)).pip;
|
||||
IdString up_wire_name = ctx->getWireName(ctx->getPipSrcWire(up_pip))[1];
|
||||
if (up_wire_name.in(id_HCLK_OUT0, id_HCLK_OUT1, id_HCLK_OUT2, id_HCLK_OUT3)) {
|
||||
ci->setAttr(id_IOLOGIC_FCLK, Property(up_wire_name.str(ctx)));
|
||||
}
|
||||
if (ctx->debug) {
|
||||
log_info("HCLK user cell:%s, port:%s, wire:%s, pip:%s, up wire:%s\n",
|
||||
ctx->nameOf(user.cell), user.port.c_str(ctx),
|
||||
ctx->nameOfWire(ctx->getNetinfoSinkWire(h_net, user, 0)), ctx->nameOfPip(up_pip),
|
||||
ctx->nameOfWire(ctx->getPipSrcWire(up_pip)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GowinImpl::isBelLocationValid(BelId bel, bool explain_invalid) const
|
||||
{
|
||||
Loc l = ctx->getBelLocation(bel);
|
||||
@ -198,6 +245,9 @@ IdString GowinImpl::getBelBucketForCellType(IdString cell_type) const
|
||||
if (type_is_ssram(cell_type)) {
|
||||
return id_RAM16SDP4;
|
||||
}
|
||||
if (type_is_iologic(cell_type)) {
|
||||
return id_IOLOGIC;
|
||||
}
|
||||
if (cell_type == id_GOWIN_GND) {
|
||||
return id_GND;
|
||||
}
|
||||
@ -222,6 +272,9 @@ bool GowinImpl::isValidBelForCellType(IdString cell_type, BelId bel) const
|
||||
if (bel_type == id_RAM16SDP4) {
|
||||
return type_is_ssram(cell_type);
|
||||
}
|
||||
if (bel_type == id_IOLOGIC) {
|
||||
return type_is_iologic(cell_type);
|
||||
}
|
||||
if (bel_type == id_GND) {
|
||||
return cell_type == id_GOWIN_GND;
|
||||
}
|
||||
@ -342,6 +395,29 @@ bool GowinImpl::slice_valid(int x, int y, int z) const
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// placer hits
|
||||
void GowinImpl::notifyBelChange(BelId bel, CellInfo *cell)
|
||||
{
|
||||
if (cell != nullptr) {
|
||||
// OSER8 took both IOLOGIC bels in the tile
|
||||
if (cell->type == id_OSER8) {
|
||||
Loc loc = ctx->getBelLocation(bel);
|
||||
loc.z = BelZ::IOLOGICA_Z + (1 - (loc.z - BelZ::IOLOGICA_Z));
|
||||
inactive_bels.insert(ctx->getBelByLocation(loc));
|
||||
}
|
||||
} else {
|
||||
// the unbind is about to happen
|
||||
CellInfo *ci = ctx->getBoundBelCell(bel);
|
||||
// OSER8 took both IOLOGIC bels in the tile
|
||||
if (ci->type == id_OSER8) {
|
||||
Loc loc = ctx->getBelLocation(bel);
|
||||
loc.z = BelZ::IOLOGICA_Z + (1 - (loc.z - BelZ::IOLOGICA_Z));
|
||||
inactive_bels.erase(ctx->getBelByLocation(loc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool GowinImpl::checkBelAvail(BelId bel) const { return inactive_bels.find(bel) == inactive_bels.end(); }
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -25,6 +25,16 @@ inline bool is_dff(const CellInfo *cell) { return type_is_dff(cell->type); }
|
||||
inline bool type_is_alu(IdString cell_type) { return cell_type == id_ALU; }
|
||||
inline bool is_alu(const CellInfo *cell) { return type_is_alu(cell->type); }
|
||||
|
||||
inline bool type_is_diffio(IdString cell_type)
|
||||
{
|
||||
return cell_type.in(id_ELVDS_IOBUF, id_ELVDS_IBUF, id_ELVDS_TBUF, id_ELVDS_OBUF, id_TLVDS_IOBUF, id_TLVDS_IBUF,
|
||||
id_TLVDS_TBUF, id_TLVDS_OBUF);
|
||||
}
|
||||
inline bool is_diffio(const CellInfo *cell) { return type_is_diffio(cell->type); }
|
||||
|
||||
inline bool type_is_iologic(IdString cell_type) { return cell_type.in(id_ODDR, id_ODDRC, id_OSER4, id_OSER8); }
|
||||
inline bool is_iologic(const CellInfo *cell) { return type_is_iologic(cell->type); }
|
||||
|
||||
// Return true if a cell is a SSRAM
|
||||
inline bool type_is_ssram(IdString cell_type) { return cell_type.in(id_RAM16SDP1, id_RAM16SDP2, id_RAM16SDP4); }
|
||||
inline bool is_ssram(const CellInfo *cell) { return type_is_ssram(cell->type); }
|
||||
@ -38,10 +48,26 @@ NPNR_PACKED_STRUCT(struct Bottom_io_cnd_POD {
|
||||
NPNR_PACKED_STRUCT(struct Bottom_io_POD {
|
||||
// simple OBUF
|
||||
static constexpr int8_t NORMAL = 0;
|
||||
// DDR
|
||||
static constexpr int8_t DDR = 1;
|
||||
RelSlice<Bottom_io_cnd_POD> conditions;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct Extra_chip_data_POD { Bottom_io_POD bottom_io; });
|
||||
NPNR_PACKED_STRUCT(struct Extra_chip_data_POD {
|
||||
Bottom_io_POD bottom_io;
|
||||
RelSlice<IdString> diff_io_types;
|
||||
});
|
||||
|
||||
inline bool is_diff_io_supported(const ChipInfoPOD *chip, IdString type)
|
||||
{
|
||||
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(chip->extra_data.get());
|
||||
for (auto &dtype : extra->diff_io_types) {
|
||||
if (IdString(dtype) == type) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool have_bottom_io_cnds(const ChipInfoPOD *chip)
|
||||
{
|
||||
@ -61,10 +87,12 @@ inline IdString get_bottom_io_wire_b_net(const ChipInfoPOD *chip, int8_t conditi
|
||||
return IdString(extra->bottom_io.conditions[condition].wire_b_net);
|
||||
}
|
||||
|
||||
inline bool getBelSimpleIO(const ChipInfoPOD *chip, BelId bel)
|
||||
// Bels and pips
|
||||
inline bool is_simple_io_bel(const ChipInfoPOD *chip, BelId bel)
|
||||
{
|
||||
return chip_bel_info(chip, bel).flags & BelFlags::FLAG_SIMPLE_IO;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Bels Z ranges. It is desirable that these numbers be synchronized with the chipdb generator
|
||||
@ -83,6 +111,8 @@ enum
|
||||
IOBA_Z = 50,
|
||||
IOBB_Z = 51, // +IOBC...IOBL
|
||||
|
||||
IOLOGICA_Z = 70,
|
||||
|
||||
PLL_Z = 275,
|
||||
GSR_Z = 276,
|
||||
VCC_Z = 277,
|
||||
|
@ -28,6 +28,8 @@ RAMW_Z = 36 # RAM16SDP4
|
||||
IOBA_Z = 50
|
||||
IOBB_Z = 51
|
||||
|
||||
IOLOGICA_Z = 70
|
||||
|
||||
PLL_Z = 275
|
||||
GSR_Z = 276
|
||||
VCC_Z = 277
|
||||
@ -74,6 +76,7 @@ class BottomIO(BBAStruct):
|
||||
class ChipExtraData(BBAStruct):
|
||||
strs: StringPool
|
||||
bottom_io: BottomIO
|
||||
diff_io_types: list[IdString] = field(default_factory = list)
|
||||
|
||||
def create_bottom_io(self):
|
||||
self.bottom_io = BottomIO()
|
||||
@ -81,12 +84,21 @@ class ChipExtraData(BBAStruct):
|
||||
def add_bottom_io_cnd(self, net_a: str, net_b: str):
|
||||
self.bottom_io.conditions.append(BottomIOCnd(self.strs.id(net_a), self.strs.id(net_b)))
|
||||
|
||||
def add_diff_io_type(self, diff_type: str):
|
||||
self.diff_io_types.append(self.strs.id(diff_type))
|
||||
|
||||
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||
self.bottom_io.serialise_lists(f"{context}_bottom_io", bba)
|
||||
bba.label(f"{context}_diff_io_types")
|
||||
for i, diff_io_type in enumerate(self.diff_io_types):
|
||||
bba.u32(diff_io_type.index)
|
||||
|
||||
def serialise(self, context: str, bba: BBAWriter):
|
||||
self.bottom_io.serialise(f"{context}_bottom_io", bba)
|
||||
bba.slice(f"{context}_diff_io_types", len(self.diff_io_types))
|
||||
|
||||
created_tiletypes = set()
|
||||
# { ttyp : {}}
|
||||
created_tiletypes = {}
|
||||
|
||||
# u-turn at the rim
|
||||
uturnlut = {'N': 'S', 'S': 'N', 'E': 'W', 'W': 'E'}
|
||||
@ -172,6 +184,8 @@ def create_nodes(chip: Chip, db: chipdb):
|
||||
# add nodes from the apicula db
|
||||
for node_name, node_hdr in db.nodes.items():
|
||||
wire_type, node = node_hdr
|
||||
if len(node) < 2:
|
||||
continue
|
||||
for y, x, wire in node:
|
||||
if wire_type:
|
||||
if not chip.tile_type_at(x, y).has_wire(wire):
|
||||
@ -181,17 +195,11 @@ def create_nodes(chip: Chip, db: chipdb):
|
||||
new_node = NodeWire(x, y, wire)
|
||||
gl_nodes = global_nodes.setdefault(node_name, [])
|
||||
if new_node not in gl_nodes:
|
||||
gl_nodes.append(NodeWire(x, y, wire))
|
||||
gl_nodes.append(NodeWire(x, y, wire))
|
||||
|
||||
for name, node in global_nodes.items():
|
||||
chip.add_node(node)
|
||||
|
||||
|
||||
# About X and Y as parameters - in some cases, the type of manufacturer's tile
|
||||
# is not different, but some wires are not physically present, that is, routing
|
||||
# depends on the location of otherwise identical tiles. There are many options
|
||||
# for taking this into account, but for now we make a distinction here, by
|
||||
# coordinates.
|
||||
def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int):
|
||||
def get_wire_type(name):
|
||||
if name in {'XD0', 'XD1', 'XD2', 'XD3', 'XD4', 'XD5',}:
|
||||
@ -205,6 +213,7 @@ def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int):
|
||||
if not tt.has_wire(src):
|
||||
tt.create_wire(src, get_wire_type(src))
|
||||
tt.create_pip(src, dst)
|
||||
|
||||
# clock wires
|
||||
for dst, srcs in db.grid[y][x].pure_clock_pips.items():
|
||||
if not tt.has_wire(dst):
|
||||
@ -214,21 +223,58 @@ def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int):
|
||||
tt.create_wire(src, "GLOBAL_CLK")
|
||||
tt.create_pip(src, dst)
|
||||
|
||||
def create_null_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
def create_hclk_switch_matrix(tt: TileType, db: chipdb, x: int, y: int):
|
||||
# hclk wires
|
||||
for dst, srcs in db.hclk_pips[(y, x)].items():
|
||||
if not tt.has_wire(dst):
|
||||
tt.create_wire(dst, "HCLK")
|
||||
for src in srcs.keys():
|
||||
if not tt.has_wire(src):
|
||||
tt.create_wire(src, "HCLK")
|
||||
tt.create_pip(src, dst)
|
||||
|
||||
extra_type_cnt = 1
|
||||
def create_tiletype(create_func, chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
global extra_type_cnt
|
||||
create_hclk_pips = False
|
||||
|
||||
# HCLK wires can be different in the same types of tiles, so if necessary, create a new type
|
||||
if ttyp in created_tiletypes:
|
||||
return ttyp, None
|
||||
typename = "NULL"
|
||||
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
||||
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
||||
# check if we have HCLK pips
|
||||
if (y, x) in db.hclk_pips and created_tiletypes[ttyp]['hclk_pips'] != db.hclk_pips[y, x]:
|
||||
create_hclk_pips = True
|
||||
ttyp = 100 * ttyp + extra_type_cnt
|
||||
extra_type_cnt += 1
|
||||
else:
|
||||
chip.set_tile_type(x, y, created_tiletypes[ttyp]['tiletype'])
|
||||
return
|
||||
elif (y, x) in db.hclk_pips:
|
||||
create_hclk_pips = True
|
||||
else:
|
||||
created_tiletypes[ttyp] = {}
|
||||
if create_hclk_pips:
|
||||
created_tiletypes.setdefault(ttyp, {}).update({'hclk_pips': db.hclk_pips[y, x]})
|
||||
|
||||
tiletype, tt = create_func(chip, db, x, y, ttyp)
|
||||
|
||||
if create_hclk_pips:
|
||||
create_hclk_switch_matrix(tt, db, x, y)
|
||||
create_switch_matrix(tt, db, x, y)
|
||||
return (ttyp, tt)
|
||||
chip.set_tile_type(x, y, tiletype)
|
||||
created_tiletypes.setdefault(ttyp, {}).update({'tiletype': tiletype})
|
||||
|
||||
def create_null_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
typename = "NULL"
|
||||
tiletype = f"{typename}_{ttyp}"
|
||||
tt = chip.create_tile_type(tiletype)
|
||||
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
||||
return (tiletype, tt)
|
||||
|
||||
# responsible nodes, there will be IO banks, configuration, etc.
|
||||
def create_corner_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
if ttyp in created_tiletypes:
|
||||
return ttyp, None
|
||||
typename = "CORNER"
|
||||
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
||||
tiletype = f"{typename}_{ttyp}"
|
||||
tt = chip.create_tile_type(tiletype)
|
||||
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
||||
|
||||
if x == 0 and y == 0:
|
||||
@ -241,21 +287,19 @@ def create_corner_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
gnd = tt.create_bel('VCC', 'VCC', z = VCC_Z)
|
||||
tt.add_bel_pin(gnd, "V", "VCC", PinType.OUTPUT)
|
||||
# also here may be GSR
|
||||
if 'GSR' in db.grid[y][x].bels.keys():
|
||||
if 'GSR' in db.grid[y][x].bels:
|
||||
portmap = db.grid[y][x].bels['GSR'].portmap
|
||||
tt.create_wire(portmap['GSRI'], "GSRI")
|
||||
io = tt.create_bel("GSR", "GSR", z = GSR_Z)
|
||||
tt.add_bel_pin(io, "GSRI", portmap['GSRI'], PinType.INPUT)
|
||||
|
||||
create_switch_matrix(tt, db, x, y)
|
||||
return (ttyp, tt)
|
||||
return (tiletype, tt)
|
||||
|
||||
# Global set/reset. GW2A series has special cell for it
|
||||
def create_gsr_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
if ttyp in created_tiletypes:
|
||||
return ttyp, None
|
||||
typename = "GSR"
|
||||
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
||||
tiletype = f"{typename}_{ttyp}"
|
||||
tt = chip.create_tile_type(tiletype)
|
||||
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
||||
|
||||
portmap = db.grid[y][x].bels['GSR'].portmap
|
||||
@ -263,15 +307,13 @@ def create_gsr_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
io = tt.create_bel("GSR", "GSR", z = GSR_Z)
|
||||
tt.add_bel_pin(io, "GSRI", portmap['GSRI'], PinType.INPUT)
|
||||
|
||||
create_switch_matrix(tt, db, x, y)
|
||||
return (ttyp, tt)
|
||||
return (tiletype, tt)
|
||||
|
||||
# simple IO - only A and B
|
||||
# IO
|
||||
def create_io_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
if ttyp in created_tiletypes:
|
||||
return ttyp, None
|
||||
typename = "IO"
|
||||
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
||||
tiletype = f"{typename}_{ttyp}"
|
||||
tt = chip.create_tile_type(tiletype)
|
||||
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
||||
|
||||
simple_io = y in db.simplio_rows and chip.name in {'GW1N-1', 'GW1NZ-1'}
|
||||
@ -282,7 +324,7 @@ def create_io_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
for i in range(rng):
|
||||
name = 'IOB' + 'ABCDEFGHIJ'[i]
|
||||
# XXX some IOBs excluded from generic chipdb for some reason
|
||||
if name not in db.grid[y][x].bels.keys():
|
||||
if name not in db.grid[y][x].bels:
|
||||
continue
|
||||
# wires
|
||||
portmap = db.grid[y][x].bels[name].portmap
|
||||
@ -294,25 +336,39 @@ def create_io_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
if simple_io:
|
||||
io.flags |= BEL_FLAG_SIMPLE_IO
|
||||
tt.add_bel_pin(io, "I", portmap['I'], PinType.INPUT)
|
||||
tt.add_bel_pin(io, "OE", portmap['OE'], PinType.INPUT)
|
||||
tt.add_bel_pin(io, "OEN", portmap['OE'], PinType.INPUT)
|
||||
tt.add_bel_pin(io, "O", portmap['O'], PinType.OUTPUT)
|
||||
# bottom io
|
||||
if 'BOTTOM_IO_PORT_A' in portmap.keys():
|
||||
if 'BOTTOM_IO_PORT_A' in portmap:
|
||||
if not tt.has_wire(portmap['BOTTOM_IO_PORT_A']):
|
||||
tt.create_wire(portmap['BOTTOM_IO_PORT_A'], "IO_I")
|
||||
tt.create_wire(portmap['BOTTOM_IO_PORT_B'], "IO_I")
|
||||
tt.add_bel_pin(io, "BOTTOM_IO_PORT_A", portmap['BOTTOM_IO_PORT_A'], PinType.INPUT)
|
||||
tt.add_bel_pin(io, "BOTTOM_IO_PORT_B", portmap['BOTTOM_IO_PORT_B'], PinType.INPUT)
|
||||
|
||||
create_switch_matrix(tt, db, x, y)
|
||||
return (ttyp, tt)
|
||||
# create IOLOGIC bels if any
|
||||
for idx, name in {(IOLOGICA_Z, 'IOLOGICA'), (IOLOGICA_Z + 1, 'IOLOGICB')}:
|
||||
if name not in db.grid[y][x].bels:
|
||||
continue
|
||||
iol = tt.create_bel(name, "IOLOGIC", z = idx)
|
||||
for port, wire in db.grid[y][x].bels[name].portmap.items():
|
||||
if port == 'FCLK': # XXX compatibility
|
||||
wire = f'FCLK{name[-1]}'
|
||||
if not tt.has_wire(wire):
|
||||
if port in {'CLK', 'PCLK'}:
|
||||
tt.create_wire(wire, "TILE_CLK")
|
||||
else:
|
||||
tt.create_wire(wire, "IOL_PORT")
|
||||
if port in {'Q', 'Q0', 'Q1', 'Q2', 'Q3', 'Q4', 'Q5', 'Q6', 'Q7', 'Q8', 'Q9', 'DF', 'LAG', 'LEAD'}:
|
||||
tt.add_bel_pin(iol, port, wire, PinType.OUTPUT)
|
||||
else:
|
||||
tt.add_bel_pin(iol, port, wire, PinType.INPUT)
|
||||
return (tiletype, tt)
|
||||
|
||||
# logic: luts, dffs, alu etc
|
||||
def create_logic_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
if ttyp in created_tiletypes:
|
||||
return ttyp, None
|
||||
typename = "LOGIC"
|
||||
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
||||
tiletype = f"{typename}_{ttyp}"
|
||||
tt = chip.create_tile_type(tiletype)
|
||||
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
||||
|
||||
lut_inputs = ['A', 'B', 'C', 'D']
|
||||
@ -401,14 +457,11 @@ def create_logic_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
tt.add_bel_pin(ff, "O", f"OF7", PinType.OUTPUT)
|
||||
tt.add_bel_pin(ff, "S0", f"SEL7", PinType.INPUT)
|
||||
|
||||
create_switch_matrix(tt, db, x, y)
|
||||
return (ttyp, tt)
|
||||
return (tiletype, tt)
|
||||
|
||||
def create_ssram_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
if ttyp in created_tiletypes:
|
||||
return ttyp, None
|
||||
# SSRAM is LUT based, so it's logic-like
|
||||
ttyp, tt = create_logic_tiletype(chip, db, x, y, ttyp)
|
||||
tiletype, tt = create_logic_tiletype(chip, db, x, y, ttyp)
|
||||
|
||||
lut_inputs = ['A', 'B', 'C', 'D']
|
||||
ff = tt.create_bel(f"RAM16SDP4", "RAM16SDP4", z = RAMW_Z)
|
||||
@ -424,7 +477,7 @@ def create_ssram_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
tt.add_bel_pin(ff, f"CLK", "CLK2", PinType.INPUT)
|
||||
tt.add_bel_pin(ff, f"CE", "CE2", PinType.INPUT)
|
||||
tt.add_bel_pin(ff, f"WRE", "LSR2", PinType.INPUT)
|
||||
return (ttyp, tt)
|
||||
return (tiletype, tt)
|
||||
|
||||
# PLL main tile
|
||||
_pll_inputs = {'CLKFB', 'FBDSEL0', 'FBDSEL1', 'FBDSEL2', 'FBDSEL3',
|
||||
@ -435,10 +488,9 @@ _pll_inputs = {'CLKFB', 'FBDSEL0', 'FBDSEL1', 'FBDSEL2', 'FBDSEL3',
|
||||
'FDLY0', 'FDLY1', 'FDLY2', 'FDLY3', 'CLKIN'}
|
||||
_pll_outputs = {'CLKOUT', 'LOCK', 'CLKOUTP', 'CLKOUTD', 'CLKOUTD3'}
|
||||
def create_pll_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
if ttyp in created_tiletypes:
|
||||
return ttyp, None
|
||||
typename = "PLL"
|
||||
tt = chip.create_tile_type(f"{typename}_{ttyp}")
|
||||
tiletype = f"{typename}_{ttyp}"
|
||||
tt = chip.create_tile_type(tiletype)
|
||||
tt.extra_data = TileExtraData(chip.strs.id(typename))
|
||||
|
||||
# wires
|
||||
@ -450,20 +502,16 @@ def create_pll_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
|
||||
bel_type = 'rPLL'
|
||||
portmap = db.grid[y][x].bels[pll_name].portmap
|
||||
pll = tt.create_bel("PLL", bel_type, z = PLL_Z)
|
||||
# Not sure how this will affect routing - PLLs are fixed and their outputs
|
||||
# will be handled by a dedicated router
|
||||
#pll.flags = BEL_FLAG_GLOBAL
|
||||
pll.flags = BEL_FLAG_GLOBAL
|
||||
for pin, wire in portmap.items():
|
||||
if pin in _pll_inputs:
|
||||
tt.create_wire(wire, "PLL_I")
|
||||
tt.add_bel_pin(pll, pin, wire, PinType.INPUT)
|
||||
else:
|
||||
assert pin in _pll_outputs, f"Unknown PLL pin{pin}"
|
||||
assert pin in _pll_outputs, f"Unknown PLL pin {pin}"
|
||||
tt.create_wire(wire, "PLL_O")
|
||||
tt.add_bel_pin(pll, pin, wire, PinType.OUTPUT)
|
||||
|
||||
create_switch_matrix(tt, db, x, y)
|
||||
return (ttyp, tt)
|
||||
return (tiletype, tt)
|
||||
|
||||
# pinouts, packages...
|
||||
_tbrlre = re.compile(r"IO([TBRL])(\d+)(\w)")
|
||||
@ -508,6 +556,8 @@ def create_extra_data(chip: Chip, db: chipdb):
|
||||
chip.extra_data.create_bottom_io()
|
||||
for net_a, net_b in db.bottom_io[2]:
|
||||
chip.extra_data.add_bottom_io_cnd(net_a, net_b)
|
||||
for diff_type in db.diff_io_types:
|
||||
chip.extra_data.add_diff_io_type(diff_type)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='Make Gowin BBA')
|
||||
@ -536,43 +586,30 @@ def main():
|
||||
# different routing or something like that).
|
||||
logic_tiletypes = db.tile_types['C']
|
||||
io_tiletypes = db.tile_types['I']
|
||||
ssram_tiletypes = {17, 18, 19}
|
||||
ssram_tiletypes = db.tile_types['M']
|
||||
gsr_tiletypes = {1}
|
||||
pll_tiletypes = db.tile_types['P']
|
||||
|
||||
# Setup tile grid
|
||||
for x in range(X):
|
||||
for y in range(Y):
|
||||
ttyp = db.grid[y][x].ttyp
|
||||
if (x == 0 or x == X - 1) and (y == 0 or y == Y - 1):
|
||||
assert ttyp not in created_tiletypes, "Duplication of corner types"
|
||||
ttyp, _ = create_corner_tiletype(ch, db, x, y, ttyp)
|
||||
created_tiletypes.add(ttyp)
|
||||
ch.set_tile_type(x, y, f"CORNER_{ttyp}")
|
||||
create_tiletype(create_corner_tiletype, ch, db, x, y, ttyp)
|
||||
continue
|
||||
if ttyp in gsr_tiletypes:
|
||||
ttyp, _ = create_gsr_tiletype(ch, db, x, y, ttyp)
|
||||
created_tiletypes.add(ttyp)
|
||||
ch.set_tile_type(x, y, f"GSR_{ttyp}")
|
||||
create_tiletype(create_gsr_tiletype, ch, db, x, y, ttyp)
|
||||
elif ttyp in logic_tiletypes:
|
||||
ttyp, _ = create_logic_tiletype(ch, db, x, y, ttyp)
|
||||
created_tiletypes.add(ttyp)
|
||||
ch.set_tile_type(x, y, f"LOGIC_{ttyp}")
|
||||
create_tiletype(create_logic_tiletype, ch, db, x, y, ttyp)
|
||||
elif ttyp in ssram_tiletypes:
|
||||
ttyp, _ = create_ssram_tiletype(ch, db, x, y, ttyp)
|
||||
created_tiletypes.add(ttyp)
|
||||
ch.set_tile_type(x, y, f"LOGIC_{ttyp}")
|
||||
create_tiletype(create_ssram_tiletype, ch, db, x, y, ttyp)
|
||||
elif ttyp in io_tiletypes:
|
||||
ttyp, _ = create_io_tiletype(ch, db, x, y, ttyp)
|
||||
created_tiletypes.add(ttyp)
|
||||
ch.set_tile_type(x, y, f"IO_{ttyp}")
|
||||
create_tiletype(create_io_tiletype, ch, db, x, y, ttyp)
|
||||
elif ttyp in pll_tiletypes:
|
||||
ttyp, _ = create_pll_tiletype(ch, db, x, y, ttyp)
|
||||
created_tiletypes.add(ttyp)
|
||||
ch.set_tile_type(x, y, f"PLL_{ttyp}")
|
||||
create_tiletype(create_pll_tiletype, ch, db, x, y, ttyp)
|
||||
else:
|
||||
ttyp, _ = create_null_tiletype(ch, db, x, y, ttyp)
|
||||
created_tiletypes.add(ttyp)
|
||||
ch.set_tile_type(x, y, f"NULL_{ttyp}")
|
||||
create_tiletype(create_null_tiletype, ch, db, x, y, ttyp)
|
||||
|
||||
# Create nodes between tiles
|
||||
create_nodes(ch, db)
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "design_utils.h"
|
||||
#include "log.h"
|
||||
#include "nextpnr.h"
|
||||
|
||||
@ -21,18 +22,39 @@ struct GowinPacker
|
||||
// ===================================
|
||||
// IO
|
||||
// ===================================
|
||||
// create IOB connections for gowin_pack
|
||||
// can be called repeatedly when switching inputs, disabled outputs do not change
|
||||
void make_iob_nets(CellInfo &iob)
|
||||
{
|
||||
for (const auto &port : iob.ports) {
|
||||
const NetInfo *net = iob.getPort(port.first);
|
||||
std::string connected_net = "NET";
|
||||
if (net != nullptr) {
|
||||
if (ctx->verbose) {
|
||||
log_info("%s: %s - %s\n", ctx->nameOf(&iob), port.first.c_str(ctx), ctx->nameOf(net));
|
||||
}
|
||||
if (net->name == ctx->id("$PACKER_VCC")) {
|
||||
connected_net = "VCC";
|
||||
} else if (net->name == ctx->id("$PACKER_GND")) {
|
||||
connected_net = "GND";
|
||||
}
|
||||
iob.setParam(ctx->idf("NET_%s", port.first.c_str(ctx)), connected_net);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void config_simple_io(CellInfo &ci)
|
||||
{
|
||||
if (ci.type.in(id_TBUF, id_IOBUF)) {
|
||||
return;
|
||||
}
|
||||
log_info("simple:%s\n", ctx->nameOf(&ci));
|
||||
ci.addInput(id_OE);
|
||||
ci.addInput(id_OEN);
|
||||
if (ci.type == id_OBUF) {
|
||||
ci.connectPort(id_OE, ctx->nets[ctx->id("$PACKER_GND")].get());
|
||||
ci.connectPort(id_OEN, ctx->nets[ctx->id("$PACKER_GND")].get());
|
||||
} else {
|
||||
NPNR_ASSERT(ci.type == id_IBUF);
|
||||
ci.connectPort(id_OE, ctx->nets[ctx->id("$PACKER_VCC")].get());
|
||||
ci.connectPort(id_OEN, ctx->nets[ctx->id("$PACKER_VCC")].get());
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,7 +66,7 @@ struct GowinPacker
|
||||
if (!ci.type.in(id_OBUF, id_TBUF, id_IOBUF)) {
|
||||
return;
|
||||
}
|
||||
if (cnd == Bottom_io_POD::NORMAL && loc.z != BelZ::IOBA_Z) {
|
||||
if (loc.z != BelZ::IOBA_Z) {
|
||||
return;
|
||||
}
|
||||
auto connect_io_wire = [&](IdString port, IdString net_name) {
|
||||
@ -60,6 +82,7 @@ struct GowinPacker
|
||||
}
|
||||
};
|
||||
|
||||
log_info("cnd:%d\n", cnd);
|
||||
IdString wire_a_net = get_bottom_io_wire_a_net(ctx->chip_info, cnd);
|
||||
connect_io_wire(id_BOTTOM_IO_PORT_A, wire_a_net);
|
||||
|
||||
@ -74,6 +97,8 @@ struct GowinPacker
|
||||
const pool<CellTypePort> top_ports{
|
||||
CellTypePort(id_IBUF, id_I),
|
||||
CellTypePort(id_OBUF, id_O),
|
||||
CellTypePort(id_TBUF, id_O),
|
||||
CellTypePort(id_IOBUF, id_IO),
|
||||
};
|
||||
std::vector<IdString> to_remove;
|
||||
for (auto &cell : ctx->cells) {
|
||||
@ -100,8 +125,18 @@ struct GowinPacker
|
||||
}
|
||||
}
|
||||
}
|
||||
NetInfo *io = ci.getPort(ctx->id("IO"));
|
||||
if (io && io->driver.cell) {
|
||||
if (!top_ports.count(CellTypePort(io->driver)))
|
||||
log_error("Top-level port '%s' driven by illegal port %s.%s\n", ctx->nameOf(&ci),
|
||||
ctx->nameOf(io->driver.cell), ctx->nameOf(io->driver.port));
|
||||
for (const auto &attr : ci.attrs) {
|
||||
io->driver.cell->attrs[attr.first] = attr.second;
|
||||
}
|
||||
}
|
||||
ci.disconnectPort(ctx->id("I"));
|
||||
ci.disconnectPort(ctx->id("O"));
|
||||
ci.disconnectPort(ctx->id("IO"));
|
||||
to_remove.push_back(ci.name);
|
||||
}
|
||||
for (IdString cell_name : to_remove)
|
||||
@ -124,7 +159,7 @@ struct GowinPacker
|
||||
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_IBUF, id_OBUF, id_IOBUF)) // XXX TBUF
|
||||
if (!ci.type.in(id_IBUF, id_OBUF, id_TBUF, id_IOBUF))
|
||||
continue;
|
||||
if (ci.attrs.count(id_BEL) == 0) {
|
||||
log_error("Unconstrained IO:%s\n", ctx->nameOf(&ci));
|
||||
@ -134,9 +169,242 @@ struct GowinPacker
|
||||
if (io_loc.y == ctx->getGridDimY() - 1) {
|
||||
config_bottom_row(ci, io_loc);
|
||||
}
|
||||
if (getBelSimpleIO(ctx->chip_info, io_bel)) {
|
||||
if (is_simple_io_bel(ctx->chip_info, io_bel)) {
|
||||
config_simple_io(ci);
|
||||
}
|
||||
make_iob_nets(ci);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================
|
||||
// Differential IO
|
||||
// ===================================
|
||||
static bool is_iob(const Context *ctx, CellInfo *cell)
|
||||
{
|
||||
return (cell->type.in(id_IBUF, id_OBUF, id_TBUF, id_IOBUF));
|
||||
}
|
||||
|
||||
std::pair<CellInfo *, CellInfo *> get_pn_cells(const CellInfo &ci)
|
||||
{
|
||||
CellInfo *p, *n;
|
||||
switch (ci.type.hash()) {
|
||||
case ID_ELVDS_TBUF: /* fall-through */
|
||||
case ID_TLVDS_TBUF: /* fall-through */
|
||||
case ID_ELVDS_OBUF: /* fall-through */
|
||||
case ID_TLVDS_OBUF:
|
||||
p = net_only_drives(ctx, ci.ports.at(id_O).net, is_iob, id_I, true);
|
||||
n = net_only_drives(ctx, ci.ports.at(id_OB).net, is_iob, id_I, true);
|
||||
break;
|
||||
case ID_ELVDS_IBUF: /* fall-through */
|
||||
case ID_TLVDS_IBUF:
|
||||
p = net_driven_by(ctx, ci.ports.at(id_I).net, is_iob, id_O);
|
||||
n = net_driven_by(ctx, ci.ports.at(id_IB).net, is_iob, id_O);
|
||||
break;
|
||||
case ID_ELVDS_IOBUF: /* fall-through */
|
||||
case ID_TLVDS_IOBUF:
|
||||
p = net_only_drives(ctx, ci.ports.at(id_IO).net, is_iob, id_I);
|
||||
n = net_only_drives(ctx, ci.ports.at(id_IOB).net, is_iob, id_I);
|
||||
break;
|
||||
default:
|
||||
log_error("Bad diff IO '%s' type '%s'\n", ctx->nameOf(&ci), ci.type.c_str(ctx));
|
||||
}
|
||||
return std::make_pair(p, n);
|
||||
}
|
||||
|
||||
void mark_iobs_as_diff(CellInfo &ci, std::pair<CellInfo *, CellInfo *> &pn_cells)
|
||||
{
|
||||
pn_cells.first->setParam(id_DIFF, std::string("P"));
|
||||
pn_cells.first->setParam(id_DIFF_TYPE, ci.type.str(ctx));
|
||||
pn_cells.second->setParam(id_DIFF, std::string("N"));
|
||||
pn_cells.second->setParam(id_DIFF_TYPE, ci.type.str(ctx));
|
||||
}
|
||||
|
||||
void switch_diff_ports(CellInfo &ci, std::pair<CellInfo *, CellInfo *> &pn_cells,
|
||||
std::vector<IdString> &nets_to_remove)
|
||||
{
|
||||
CellInfo *iob_p = pn_cells.first;
|
||||
CellInfo *iob_n = pn_cells.second;
|
||||
|
||||
if (ci.type.in(id_TLVDS_TBUF, id_TLVDS_OBUF, id_ELVDS_TBUF, id_ELVDS_OBUF)) {
|
||||
nets_to_remove.push_back(ci.getPort(id_O)->name);
|
||||
ci.disconnectPort(id_O);
|
||||
nets_to_remove.push_back(ci.getPort(id_OB)->name);
|
||||
ci.disconnectPort(id_OB);
|
||||
nets_to_remove.push_back(iob_n->getPort(id_I)->name);
|
||||
iob_n->disconnectPort(id_I);
|
||||
|
||||
if (ci.type.in(id_TLVDS_TBUF, id_ELVDS_TBUF)) {
|
||||
nets_to_remove.push_back(iob_n->getPort(id_OEN)->name);
|
||||
iob_n->disconnectPort(id_OEN);
|
||||
iob_p->disconnectPort(id_OEN);
|
||||
ci.movePortTo(id_OEN, iob_p, id_OEN);
|
||||
}
|
||||
iob_p->disconnectPort(id_I);
|
||||
ci.movePortTo(id_I, iob_p, id_I);
|
||||
return;
|
||||
}
|
||||
if (ci.type.in(id_TLVDS_IBUF, id_ELVDS_IBUF)) {
|
||||
nets_to_remove.push_back(ci.getPort(id_I)->name);
|
||||
ci.disconnectPort(id_I);
|
||||
nets_to_remove.push_back(ci.getPort(id_IB)->name);
|
||||
ci.disconnectPort(id_IB);
|
||||
iob_n->disconnectPort(id_O);
|
||||
iob_p->disconnectPort(id_O);
|
||||
ci.movePortTo(id_O, iob_p, id_O);
|
||||
return;
|
||||
}
|
||||
if (ci.type.in(id_TLVDS_IOBUF, id_ELVDS_IOBUF)) {
|
||||
nets_to_remove.push_back(ci.getPort(id_IO)->name);
|
||||
ci.disconnectPort(id_IO);
|
||||
nets_to_remove.push_back(ci.getPort(id_IOB)->name);
|
||||
ci.disconnectPort(id_IOB);
|
||||
nets_to_remove.push_back(iob_n->getPort(id_I)->name);
|
||||
iob_n->disconnectPort(id_I);
|
||||
iob_n->disconnectPort(id_OEN);
|
||||
|
||||
iob_p->disconnectPort(id_OEN);
|
||||
ci.movePortTo(id_OEN, iob_p, id_OEN);
|
||||
iob_p->disconnectPort(id_I);
|
||||
ci.movePortTo(id_I, iob_p, id_I);
|
||||
iob_p->disconnectPort(id_O);
|
||||
ci.movePortTo(id_O, iob_p, id_O);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void pack_diff_iobs(void)
|
||||
{
|
||||
log_info("Pack diff IOBs...\n");
|
||||
std::vector<IdString> cells_to_remove, nets_to_remove;
|
||||
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!is_diffio(&ci)) {
|
||||
continue;
|
||||
}
|
||||
if (!is_diff_io_supported(ctx->chip_info, ci.type)) {
|
||||
log_error("%s is not supported\n", ci.type.c_str(ctx));
|
||||
}
|
||||
cells_to_remove.push_back(ci.name);
|
||||
auto pn_cells = get_pn_cells(ci);
|
||||
NPNR_ASSERT(pn_cells.first != nullptr && pn_cells.second != nullptr);
|
||||
|
||||
mark_iobs_as_diff(ci, pn_cells);
|
||||
switch_diff_ports(ci, pn_cells, nets_to_remove);
|
||||
}
|
||||
|
||||
for (auto cell : cells_to_remove) {
|
||||
ctx->cells.erase(cell);
|
||||
}
|
||||
for (auto net : nets_to_remove) {
|
||||
ctx->nets.erase(net);
|
||||
}
|
||||
}
|
||||
|
||||
// ===================================
|
||||
// IO logic
|
||||
// ===================================
|
||||
// the functions of these two inputs are yet to be discovered, so we set as observed
|
||||
// in the exemplary images
|
||||
void set_daaj_nets(CellInfo &ci, BelId bel)
|
||||
{
|
||||
std::vector<IdString> pins = ctx->getBelPins(bel);
|
||||
if (std::find(pins.begin(), pins.end(), id_DAADJ0) != pins.end()) {
|
||||
ci.addInput(id_DAADJ0);
|
||||
ci.connectPort(id_DAADJ0, ctx->nets[ctx->id("$PACKER_GND")].get());
|
||||
}
|
||||
if (std::find(pins.begin(), pins.end(), id_DAADJ1) != pins.end()) {
|
||||
ci.addInput(id_DAADJ1);
|
||||
ci.connectPort(id_DAADJ1, ctx->nets[ctx->id("$PACKER_VCC")].get());
|
||||
}
|
||||
}
|
||||
|
||||
BelId get_iologic_bel(CellInfo *iob)
|
||||
{
|
||||
NPNR_ASSERT(iob->bel != BelId());
|
||||
Loc loc = ctx->getBelLocation(iob->bel);
|
||||
loc.z = loc.z - BelZ::IOBA_Z + BelZ::IOLOGICA_Z;
|
||||
return ctx->getBelByLocation(loc);
|
||||
}
|
||||
|
||||
void pack_bi_output_iol(CellInfo &ci, std::vector<IdString> &cells_to_remove, std::vector<IdString> &nets_to_remove)
|
||||
{
|
||||
// These primitives have an additional pin to control the tri-state iob - Q1.
|
||||
IdString out_port = id_Q0;
|
||||
IdString tx_port = id_Q1;
|
||||
|
||||
CellInfo *out_iob = net_only_drives(ctx, ci.ports.at(out_port).net, is_iob, id_I);
|
||||
NPNR_ASSERT(out_iob != nullptr && out_iob->bel != BelId());
|
||||
BelId iob_bel = out_iob->bel;
|
||||
|
||||
BelId l_bel = get_iologic_bel(out_iob);
|
||||
if (l_bel == BelId()) {
|
||||
log_error("Can't place IOLOGIC %s at %s\n", ctx->nameOf(&ci), ctx->nameOfBel(iob_bel));
|
||||
}
|
||||
|
||||
if (!ctx->checkBelAvail(l_bel)) {
|
||||
log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci),
|
||||
ctx->nameOfBel(l_bel), ctx->nameOf(ctx->getBoundBelCell(l_bel)));
|
||||
}
|
||||
ctx->bindBel(l_bel, &ci, PlaceStrength::STRENGTH_LOCKED);
|
||||
std::string out_mode;
|
||||
switch (ci.type.hash()) {
|
||||
case ID_ODDR:
|
||||
case ID_ODDRC:
|
||||
out_mode = "ODDRX1";
|
||||
break;
|
||||
case ID_OSER4:
|
||||
out_mode = "ODDRX2";
|
||||
break;
|
||||
case ID_OSER8:
|
||||
out_mode = "ODDRX4";
|
||||
break;
|
||||
}
|
||||
ci.setParam(ctx->id("OUTMODE"), out_mode);
|
||||
|
||||
// mark IOB as used by IOLOGIC
|
||||
out_iob->setParam(id_IOLOGIC_IOB, 1);
|
||||
// disconnect Q output: it is wired internally
|
||||
nets_to_remove.push_back(ci.getPort(out_port)->name);
|
||||
out_iob->disconnectPort(id_I);
|
||||
ci.disconnectPort(out_port);
|
||||
set_daaj_nets(ci, iob_bel);
|
||||
config_bottom_row(*out_iob, ctx->getBelLocation(iob_bel), Bottom_io_POD::DDR);
|
||||
|
||||
// if Q1 is connected then disconnect it too
|
||||
if (port_used(&ci, tx_port)) {
|
||||
NPNR_ASSERT(out_iob == net_only_drives(ctx, ci.ports.at(tx_port).net, is_iob, id_OEN));
|
||||
nets_to_remove.push_back(ci.getPort(tx_port)->name);
|
||||
out_iob->disconnectPort(id_OEN);
|
||||
ci.disconnectPort(tx_port);
|
||||
}
|
||||
make_iob_nets(*out_iob);
|
||||
}
|
||||
|
||||
void pack_iologic()
|
||||
{
|
||||
log_info("Pack IO logic...\n");
|
||||
std::vector<IdString> cells_to_remove, nets_to_remove;
|
||||
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!is_iologic(&ci)) {
|
||||
continue;
|
||||
}
|
||||
if (ctx->debug) {
|
||||
log_info("pack %s of type %s.\n", ctx->nameOf(&ci), ci.type.c_str(ctx));
|
||||
}
|
||||
if (ci.type.in(id_ODDR, id_ODDRC, id_OSER4, id_OSER8)) {
|
||||
pack_bi_output_iol(ci, cells_to_remove, nets_to_remove);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto cell : cells_to_remove) {
|
||||
ctx->cells.erase(cell);
|
||||
}
|
||||
for (auto net : nets_to_remove) {
|
||||
ctx->nets.erase(net);
|
||||
}
|
||||
}
|
||||
|
||||
@ -656,12 +924,17 @@ struct GowinPacker
|
||||
{
|
||||
handle_constants();
|
||||
pack_iobs();
|
||||
pack_diff_iobs();
|
||||
pack_iologic();
|
||||
pack_gsr();
|
||||
pack_wideluts();
|
||||
pack_alus();
|
||||
constrain_lutffs();
|
||||
pack_pll();
|
||||
pack_ram16sdp4();
|
||||
|
||||
ctx->fixupHierarchy();
|
||||
ctx->check();
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
Loading…
Reference in New Issue
Block a user