diff --git a/himbaechel/uarch/gowin/constids.inc b/himbaechel/uarch/gowin/constids.inc index 230abc09..d92642fe 100644 --- a/himbaechel/uarch/gowin/constids.inc +++ b/himbaechel/uarch/gowin/constids.inc @@ -1056,3 +1056,4 @@ X(place) X(placer) X(route) X(router) + diff --git a/himbaechel/uarch/gowin/gowin.cc b/himbaechel/uarch/gowin/gowin.cc new file mode 100644 index 00000000..9a389ae5 --- /dev/null +++ b/himbaechel/uarch/gowin/gowin.cc @@ -0,0 +1,144 @@ +#include "himbaechel_api.h" +#include "log.h" +#include "nextpnr.h" +#include "util.h" + +#include "himbaechel_helpers.h" + +#define GEN_INIT_CONSTIDS +#define HIMBAECHEL_CONSTIDS "uarch/gowin/constids.inc" +#include "himbaechel_constids.h" + +NEXTPNR_NAMESPACE_BEGIN + +namespace { +struct GowinImpl : HimbaechelAPI +{ + + ~GowinImpl(){}; + void init_constids(Arch *arch) override { init_uarch_constids(arch); } + void init(Context *ctx) override + { + h.init(ctx); + HimbaechelAPI::init(ctx); + } + + void prePlace() override { + ctx->cells.at(ctx->id("leds_OBUF_O"))->setAttr(ctx->id("BEL"), std::string("X46Y14/IOBA")); + ctx->cells.at(ctx->id("leds_OBUF_O_1"))->setAttr(ctx->id("BEL"), std::string("X0Y15/IOBB")); + ctx->cells.at(ctx->id("leds_OBUF_O_2"))->setAttr(ctx->id("BEL"), std::string("X0Y20/IOBB")); + ctx->cells.at(ctx->id("leds_OBUF_O_3"))->setAttr(ctx->id("BEL"), std::string("X0Y21/IOBB")); + ctx->cells.at(ctx->id("leds_OBUF_O_4"))->setAttr(ctx->id("BEL"), std::string("X0Y24/IOBB")); + ctx->cells.at(ctx->id("leds_OBUF_O_5"))->setAttr(ctx->id("BEL"), std::string("X0Y25/IOBB")); + ctx->cells.at(ctx->id("rst_IBUF_I"))->setAttr(ctx->id("BEL"), std::string("X0Y4/IOBA")); + assign_cell_info(); + } + + void pack() override + { + // Trim nextpnr IOBs - assume IO buffer insertion has been done in synthesis + const pool top_ports{ + CellTypePort(id_IBUF, id_I), + CellTypePort(id_OBUF, id_O), + }; + h.remove_nextpnr_iobs(top_ports); + // Replace constants with LUTs + const dict vcc_params = {{id_INIT, Property(0xFFFF, 16)}}; + const dict gnd_params = {{id_INIT, Property(0x0000, 16)}}; + h.replace_constants(CellTypePort(id_LUT4, id_F), CellTypePort(id_LUT4, id_F), vcc_params, gnd_params); + // Constrain directly connected LUTs and FFs together to use dedicated resources + int lutffs = h.constrain_cell_pairs(pool{{id_LUT4, id_F}}, pool{{id_DFF, id_D}}, 1); + lutffs += h.constrain_cell_pairs(pool{{id_LUT3, id_F}}, pool{{id_DFF, id_D}}, 1); + lutffs += h.constrain_cell_pairs(pool{{id_LUT2, id_F}}, pool{{id_DFF, id_D}}, 1); + lutffs += h.constrain_cell_pairs(pool{{id_LUT1, id_F}}, pool{{id_DFF, id_D}}, 1); + lutffs += h.constrain_cell_pairs(pool{{id_LUT4, id_F}}, pool{{id_DFFR, id_D}}, 1); + lutffs += h.constrain_cell_pairs(pool{{id_LUT3, id_F}}, pool{{id_DFFR, id_D}}, 1); + lutffs += h.constrain_cell_pairs(pool{{id_LUT2, id_F}}, pool{{id_DFFR, id_D}}, 1); + lutffs += h.constrain_cell_pairs(pool{{id_LUT1, id_F}}, pool{{id_DFFR, id_D}}, 1); + log_info("Constrained %d LUTFF pairs.\n", lutffs); + } + + bool isBelLocationValid(BelId bel, bool explain_invalid) const override + { + Loc l = ctx->getBelLocation(bel); + if (ctx->getBelType(bel).in(id_LUT4, id_DFF)) { + return slice_valid(l.x, l.y, l.z / 2); + } else { + return true; + } + } + + // Bel bucket functions + IdString getBelBucketForCellType(IdString cell_type) const override + { + if (cell_type.in(id_IBUF, id_OBUF)) { + return id_IOB; + } + if (cell_type.in(id_LUT1, id_LUT2, id_LUT3, id_LUT4)) { + return id_LUT4; + } + return cell_type; + } + + bool isValidBelForCellType(IdString cell_type, BelId bel) const override + { + IdString bel_type = ctx->getBelType(bel); + if (bel_type == id_IOB) { + return cell_type.in(id_IBUF, id_OBUF); + } + if (bel_type == id_LUT4) { + return cell_type.in(id_LUT1, id_LUT2, id_LUT3, id_LUT4); + } + if (bel_type == id_DFF) { + return cell_type.in(id_DFF, id_DFFR); + } + return (bel_type == cell_type); + } + + private: + HimbaechelHelpers h; + + // Validity checking + struct GowinCellInfo + { + const NetInfo *lut_f = nullptr, *ff_d = nullptr; + }; + std::vector fast_cell_info; + void assign_cell_info() + { + fast_cell_info.resize(ctx->cells.size()); + for (auto &cell : ctx->cells) { + CellInfo *ci = cell.second.get(); + auto &fc = fast_cell_info.at(ci->flat_index); + if (ci->type.in(id_LUT1, id_LUT2, id_LUT3, id_LUT4)) { + fc.lut_f = ci->getPort(id_F); + } else if (ci->type.in(id_DFF, id_DFFR)) { + fc.ff_d = ci->getPort(id_D); + } + } + } + bool slice_valid(int x, int y, int z) const + { + const CellInfo *lut = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, z * 2))); + const CellInfo *ff = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, z * 2 + 1))); + if (!lut || !ff) + return true; // always valid if only LUT or FF used + const auto &lut_data = fast_cell_info.at(lut->flat_index); + const auto &ff_data = fast_cell_info.at(ff->flat_index); + if (ff_data.ff_d == lut_data.lut_f) + return true; + return false; + } +}; + +struct GowinArch : HimbaechelArch +{ + GowinArch() : HimbaechelArch("gowin"){}; + std::unique_ptr create(const dict &args) + { + return std::make_unique(); + } +} exampleArch; +} // namespace + +NEXTPNR_NAMESPACE_END diff --git a/himbaechel/uarch/gowin/gowin_arch_gen.py b/himbaechel/uarch/gowin/gowin_arch_gen.py index 716a6b6e..50764b5b 100644 --- a/himbaechel/uarch/gowin/gowin_arch_gen.py +++ b/himbaechel/uarch/gowin/gowin_arch_gen.py @@ -66,7 +66,6 @@ def create_nodes(chip: Chip, db: chipdb): NodeWire(*uturn(db, x + offs[0] * 4, y + offs[1] * 4, f'{d}8{i}4')), NodeWire(*uturn(db, x + offs[0] * 8, y + offs[1] * 8, f'{d}8{i}8'))]) for node in nodes: - print(node) chip.add_node(node) # About X and Y as parameters - in some cases, the type of manufacturer's tile @@ -82,7 +81,7 @@ def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int): for src in srcs.keys(): if not tt.has_wire(src): tt.create_wire(src) - tt.create_pip(dst, src) + tt.create_pip(src, dst) def create_null_tiletype(chip: Chip, db: chipdb, x: int, y: int): tt = chip.create_tile_type(f"NULL_{db.grid[y][x].ttyp}") @@ -106,7 +105,7 @@ def create_io_tiletype(chip: Chip, db: chipdb, x: int, y: int): # XXX 6 lut+dff only for now def create_logic_tiletype(chip: Chip, db: chipdb, x: int, y: int): N = 6 - lut_inputs = {'A', 'B', 'C', 'D'} + lut_inputs = ['A', 'B', 'C', 'D'] tt = chip.create_tile_type(f"LOGIC_{db.grid[y][x].ttyp}") # setup wires for i in range(N): @@ -121,13 +120,14 @@ def create_logic_tiletype(chip: Chip, db: chipdb, x: int, y: int): tt.create_wire(f"Q{i}", "FF_OUT") for j in range(3): tt.create_wire(f"CLK{j}", "TILE_CLK") + tt.create_wire(f"LSR{j}", "TILE_LSR") # create logic cells for i in range(N): # LUT lut = tt.create_bel(f"LUT{i}", "LUT4", z=(i*2 + 0)) for j, inp_name in enumerate(lut_inputs): - tt.add_bel_pin(lut, f"I[{j}]", f"{inp_name}{i}", PinType.INPUT) + tt.add_bel_pin(lut, f"I{j}", f"{inp_name}{i}", PinType.INPUT) tt.add_bel_pin(lut, "F", f"F{i}", PinType.OUTPUT) # FF data can come from LUT output, but we pretend that we can use # any LUT input @@ -139,6 +139,7 @@ def create_logic_tiletype(chip: Chip, db: chipdb, x: int, y: int): tt.add_bel_pin(ff, "D", f"XD{i}", PinType.INPUT) tt.add_bel_pin(ff, "CLK", f"CLK{i // 2}", PinType.INPUT) tt.add_bel_pin(ff, "Q", f"Q{i}", PinType.OUTPUT) + tt.add_bel_pin(ff, "RESET", f"LSR{i // 2}", PinType.INPUT) create_switch_matrix(tt, db, x, y) def main():