gowin: Himbaechel. Add simplified IO

Add processing IO located on the sides of some chips. These are IOBUF,
which are converted into IBUF and OBUF not by fuses, but by signaling to
OE.

Also added the creation of a Global Set / Reset for all chips, instead
of a list of tile types, information from the apicula database is used,
and minor fixes.

Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
YRabbit 2023-07-25 13:25:33 +10:00 committed by myrtle
parent df13104384
commit 03c413a27a
4 changed files with 79 additions and 16 deletions

View File

@ -52,7 +52,7 @@ struct GowinGlobalRouter
bool src_valid = src_type.in(id_GLOBAL_CLK, id_IO_O, id_PLL_O); 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); bool dst_valid = dst_type.in(id_GLOBAL_CLK, id_TILE_CLK, id_PLL_I, id_IO_I);
if (ctx->debug) { if (ctx->debug && false) {
log_info("%s <- %s [%s <- %s]\n", ctx->getWireName(ctx->getPipDstWire(pip)).str(ctx).c_str(), 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), ctx->getWireName(ctx->getPipSrcWire(pip)).str(ctx).c_str(), dst_type.c_str(ctx),
src_type.c_str(ctx)); src_type.c_str(ctx));

View File

@ -5,6 +5,10 @@
NEXTPNR_NAMESPACE_BEGIN NEXTPNR_NAMESPACE_BEGIN
namespace BelFlags {
static constexpr uint32_t FLAG_SIMPLE_IO = 0x100;
}
namespace { namespace {
// Return true if a cell is a LUT // Return true if a cell is a LUT
inline bool type_is_lut(IdString cell_type) { return cell_type.in(id_LUT1, id_LUT2, id_LUT3, id_LUT4); } inline bool type_is_lut(IdString cell_type) { return cell_type.in(id_LUT1, id_LUT2, id_LUT3, id_LUT4); }
@ -39,6 +43,12 @@ NPNR_PACKED_STRUCT(struct Bottom_io_POD {
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; });
inline bool have_bottom_io_cnds(const ChipInfoPOD *chip)
{
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(chip->extra_data.get());
return extra->bottom_io.conditions.size() != 0;
}
inline IdString get_bottom_io_wire_a_net(const ChipInfoPOD *chip, int8_t condition) inline IdString get_bottom_io_wire_a_net(const ChipInfoPOD *chip, int8_t condition)
{ {
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(chip->extra_data.get()); const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(chip->extra_data.get());
@ -51,6 +61,10 @@ inline IdString get_bottom_io_wire_b_net(const ChipInfoPOD *chip, int8_t conditi
return IdString(extra->bottom_io.conditions[condition].wire_b_net); return IdString(extra->bottom_io.conditions[condition].wire_b_net);
} }
inline bool getBelSimpleIO(const ChipInfoPOD *chip, BelId bel)
{
return chip_bel_info(chip, bel).flags & BelFlags::FLAG_SIMPLE_IO;
}
} // namespace } // namespace
// Bels Z ranges. It is desirable that these numbers be synchronized with the chipdb generator // Bels Z ranges. It is desirable that these numbers be synchronized with the chipdb generator

View File

@ -11,6 +11,9 @@ sys.path.append(path.join(path.dirname(__file__), "../.."))
from himbaechel_dbgen.chip import * from himbaechel_dbgen.chip import *
from apycula import chipdb from apycula import chipdb
# Bel flags
BEL_FLAG_SIMPLE_IO = 0x100
# Z of the bels # Z of the bels
# sync with C++ part! # sync with C++ part!
LUT0_Z = 0 # z(DFFx) = z(LUTx) + 1 LUT0_Z = 0 # z(DFFx) = z(LUTx) + 1
@ -171,7 +174,10 @@ def create_nodes(chip: Chip, db: chipdb):
wire_type, node = node_hdr wire_type, node = node_hdr
for y, x, wire in node: for y, x, wire in node:
if wire_type: if wire_type:
chip.tile_type_at(x, y).set_wire_type(wire, wire_type) if not chip.tile_type_at(x, y).has_wire(wire):
chip.tile_type_at(x, y).create_wire(wire, wire_type)
else:
chip.tile_type_at(x, y).set_wire_type(wire, wire_type)
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:
@ -200,7 +206,7 @@ def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int):
tt.create_wire(src, get_wire_type(src)) tt.create_wire(src, get_wire_type(src))
tt.create_pip(src, dst) tt.create_pip(src, dst)
# clock wires # clock wires
for dst, srcs in db.grid[y][x].clock_pips.items(): for dst, srcs in db.grid[y][x].pure_clock_pips.items():
if not tt.has_wire(dst): if not tt.has_wire(dst):
tt.create_wire(dst, "GLOBAL_CLK") tt.create_wire(dst, "GLOBAL_CLK")
for src in srcs.keys(): for src in srcs.keys():
@ -234,6 +240,12 @@ def create_corner_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
tt.create_wire('VCC', 'VCC') tt.create_wire('VCC', 'VCC')
gnd = tt.create_bel('VCC', 'VCC', z = VCC_Z) gnd = tt.create_bel('VCC', 'VCC', z = VCC_Z)
tt.add_bel_pin(gnd, "V", "VCC", PinType.OUTPUT) tt.add_bel_pin(gnd, "V", "VCC", PinType.OUTPUT)
# also here may be GSR
if 'GSR' in db.grid[y][x].bels.keys():
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) create_switch_matrix(tt, db, x, y)
return (ttyp, tt) return (ttyp, tt)
@ -262,15 +274,27 @@ def create_io_tiletype(chip: Chip, db: chipdb, x: int, y: int, ttyp: int):
tt = chip.create_tile_type(f"{typename}_{ttyp}") tt = chip.create_tile_type(f"{typename}_{ttyp}")
tt.extra_data = TileExtraData(chip.strs.id(typename)) tt.extra_data = TileExtraData(chip.strs.id(typename))
for i in range(2): simple_io = y in db.simplio_rows and chip.name in {'GW1N-1', 'GW1NZ-1'}
name = ['IOBA', 'IOBB'][i] if simple_io:
rng = 10
else:
rng = 2
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():
continue
# wires # wires
portmap = db.grid[y][x].bels[name].portmap portmap = db.grid[y][x].bels[name].portmap
tt.create_wire(portmap['I'], "IO_I") tt.create_wire(portmap['I'], "IO_I")
tt.create_wire(portmap['O'], "IO_O") tt.create_wire(portmap['O'], "IO_O")
tt.create_wire(portmap['OE'], "IO_OE")
# bels # bels
io = tt.create_bel(name, "IOB", z = IOBA_Z + i) io = tt.create_bel(name, "IOB", z = IOBA_Z + i)
if simple_io:
io.flags |= BEL_FLAG_SIMPLE_IO
tt.add_bel_pin(io, "I", portmap['I'], PinType.INPUT) 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, "O", portmap['O'], PinType.OUTPUT) tt.add_bel_pin(io, "O", portmap['O'], PinType.OUTPUT)
# bottom io # bottom io
if 'BOTTOM_IO_PORT_A' in portmap.keys(): if 'BOTTOM_IO_PORT_A' in portmap.keys():
@ -510,11 +534,11 @@ def main():
# The manufacturer distinguishes by externally identical tiles, so keep # The manufacturer distinguishes by externally identical tiles, so keep
# these differences (in case it turns out later that there is a slightly # these differences (in case it turns out later that there is a slightly
# different routing or something like that). # different routing or something like that).
logic_tiletypes = {12, 13, 14, 15, 16} logic_tiletypes = db.tile_types['C']
io_tiletypes = {52, 53, 54, 55, 58, 59, 64, 65, 66} io_tiletypes = db.tile_types['I']
ssram_tiletypes = {17, 18, 19} ssram_tiletypes = {17, 18, 19}
gsr_tiletypes = {1} gsr_tiletypes = {1}
pll_tiletypes = {86, 87, 42, 45} pll_tiletypes = db.tile_types['P']
# Setup tile grid # Setup tile grid
for x in range(X): for x in range(X):
for y in range(Y): for y in range(Y):

View File

@ -21,8 +21,26 @@ struct GowinPacker
// =================================== // ===================================
// IO // IO
// =================================== // ===================================
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);
if (ci.type == id_OBUF) {
ci.connectPort(id_OE, 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());
}
}
void config_bottom_row(CellInfo &ci, Loc loc, uint8_t cnd = Bottom_io_POD::NORMAL) void config_bottom_row(CellInfo &ci, Loc loc, uint8_t cnd = Bottom_io_POD::NORMAL)
{ {
if (!have_bottom_io_cnds(ctx->chip_info)) {
return;
}
if (!ci.type.in(id_OBUF, id_TBUF, id_IOBUF)) { if (!ci.type.in(id_OBUF, id_TBUF, id_IOBUF)) {
return; return;
} }
@ -93,6 +111,7 @@ struct GowinPacker
BelId bind_io(CellInfo &ci) BelId bind_io(CellInfo &ci)
{ {
BelId bel = ctx->getBelByName(IdStringList::parse(ctx, ci.attrs.at(id_BEL).as_string())); BelId bel = ctx->getBelByName(IdStringList::parse(ctx, ci.attrs.at(id_BEL).as_string()));
NPNR_ASSERT(bel != BelId());
ci.unsetAttr(id_BEL); ci.unsetAttr(id_BEL);
ctx->bindBel(bel, &ci, PlaceStrength::STRENGTH_LOCKED); ctx->bindBel(bel, &ci, PlaceStrength::STRENGTH_LOCKED);
return bel; return bel;
@ -115,6 +134,9 @@ struct GowinPacker
if (io_loc.y == ctx->getGridDimY() - 1) { if (io_loc.y == ctx->getGridDimY() - 1) {
config_bottom_row(ci, io_loc); config_bottom_row(ci, io_loc);
} }
if (getBelSimpleIO(ctx->chip_info, io_bel)) {
config_simple_io(ci);
}
} }
} }
@ -138,10 +160,11 @@ struct GowinPacker
NetInfo *constnet = net->second.get(); NetInfo *constnet = net->second.get();
for (auto user : constnet->users) { for (auto user : constnet->users) {
CellInfo *uc = user.cell; CellInfo *uc = user.cell;
if (ctx->debug)
log_info("%s user %s/%s\n", ctx->nameOf(constnet), ctx->nameOf(uc), user.port.c_str(ctx));
if (is_lut(uc) && (user.port.str(ctx).at(0) == 'I')) { if (is_lut(uc) && (user.port.str(ctx).at(0) == 'I')) {
if (ctx->debug) {
log_info("%s user %s/%s\n", ctx->nameOf(constnet), ctx->nameOf(uc), user.port.c_str(ctx));
}
auto it_param = uc->params.find(id_INIT); auto it_param = uc->params.find(id_INIT);
if (it_param == uc->params.end()) if (it_param == uc->params.end())
log_error("No initialization for lut found.\n"); log_error("No initialization for lut found.\n");
@ -318,9 +341,9 @@ struct GowinPacker
// CIN from logic // CIN from logic
cin_ci->addInput(id_I1); cin_ci->addInput(id_I1);
cin_ci->addInput(id_I3); cin_ci->addInput(id_I3);
cin_ci->addInput(id_I2);
cin_ci->connectPort(id_I1, cin_net); cin_ci->connectPort(id_I1, cin_net);
cin_ci->connectPort(id_I3, cin_net); cin_ci->connectPort(id_I3, cin_net);
cin_ci->addInput(id_I2);
cin_ci->connectPort(id_I2, ctx->nets[ctx->id("$PACKER_VCC")].get()); cin_ci->connectPort(id_I2, ctx->nets[ctx->id("$PACKER_VCC")].get());
cin_ci->params[id_ALU_MODE] = std::string("0"); // ADD cin_ci->params[id_ALU_MODE] = std::string("0"); // ADD
return cin_ci; return cin_ci;
@ -342,6 +365,8 @@ struct GowinPacker
cout_ci->connectPort(id_CIN, cin_net); cout_ci->connectPort(id_CIN, cin_net);
cout_ci->addOutput(id_SUM); cout_ci->addOutput(id_SUM);
cout_ci->connectPort(id_SUM, cout_net); cout_ci->connectPort(id_SUM, cout_net);
cout_ci->addInput(id_I2);
cout_ci->connectPort(id_I2, ctx->nets[ctx->id("$PACKER_VCC")].get());
cout_ci->params[id_ALU_MODE] = std::string("C2L"); cout_ci->params[id_ALU_MODE] = std::string("C2L");
return cout_ci; return cout_ci;
@ -425,8 +450,8 @@ struct GowinPacker
cout_block_ci->constr_y = 0; cout_block_ci->constr_y = 0;
cout_block_ci->constr_z = alu_chain_len % 6; cout_block_ci->constr_z = alu_chain_len % 6;
if (ctx->debug) { if (ctx->debug) {
log_info("Add ALU carry out to the chain (len:%d): %s\n", alu_chain_len, log_info("Add ALU carry out to the chain (len:%d): %s COUT-net: %s\n", alu_chain_len,
ctx->nameOf(cout_block_ci)); ctx->nameOf(cout_block_ci), ctx->nameOf(cout_net));
} }
++alu_chain_len; ++alu_chain_len;
@ -566,7 +591,7 @@ struct GowinPacker
// =================================== // ===================================
void pack_gsr(void) void pack_gsr(void)
{ {
log_info("Packing GSR..\n"); log_info("Pack GSR..\n");
bool user_gsr = false; bool user_gsr = false;
for (auto &cell : ctx->cells) { for (auto &cell : ctx->cells) {
@ -603,7 +628,7 @@ struct GowinPacker
// =================================== // ===================================
void pack_pll(void) void pack_pll(void)
{ {
log_info("Packing PLL..\n"); log_info("Pack PLL..\n");
for (auto &cell : ctx->cells) { for (auto &cell : ctx->cells) {
auto &ci = *cell.second; auto &ci = *cell.second;