gowin: Himbaechel. Add BSRAM for all chips.

The following primitives are implemented for the GW1N-1, GW2A-18,
    GW2AR-18C, GW1NSR-4C, GW1NR-9C, GW1NR-9 and GW1N-4 chips:

    * pROM     - read only memory - (bitwidth: 1, 2, 4, 8, 16, 32).
    * pROMX9   - read only memory - (bitwidth: 9, 18, 36).
    * SDPB     - semidual port    - (bitwidth: 1, 2, 4, 8, 16, 32).
    * SDPX9B   - semidual port    - (bitwidth: 9, 18, 36).
    * DPB      - dual port        - (bitwidth: 16).
    * DPX9B    - dual port        - (bitwidth: 18).
    * SP       - single port      - (bitwidth: 1, 2, 4, 8, 16, 32).
    * SPX9     - single port      - (bitwidth: 9, 18, 36).

    For GW1NSR-4C and GW1NR-9 chips, SP/SPX9 primitives with data widths
    of 32/36 bits are implemented using a pair of 16-bit wide
    primitives.

Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
YRabbit 2023-11-26 20:51:16 +10:00 committed by myrtle
parent 90d4863dd4
commit c13b34f20e
6 changed files with 149 additions and 39 deletions

View File

@ -1022,6 +1022,9 @@ X(CLKOUTP)
X(CLKOUTD)
X(CLKOUTD3)
X(LOCK)
X(AD)
X(DI)
X(DO)
// PLL parameters
X(CLKOUTPS)
@ -1133,3 +1136,4 @@ X(HCLK_OUT3)
X(BUFG)
X(CLOCK)

View File

@ -39,7 +39,7 @@ inline bool is_iologic(const CellInfo *cell) { return type_is_iologic(cell->type
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); }
// Return true if a cell is a SSRAM
// Return true if a cell is a BSRAM
inline bool type_is_bsram(IdString cell_type)
{
return cell_type.in(id_SP, id_SPX9, id_pROM, id_pROMX9, id_ROM, id_SDP, id_SDPB, id_SDPX9B, id_DP, id_DPB,
@ -70,8 +70,11 @@ NPNR_PACKED_STRUCT(struct Bottom_io_POD {
});
NPNR_PACKED_STRUCT(struct Extra_chip_data_POD {
int32_t chip_flags;
Bottom_io_POD bottom_io;
RelSlice<IdString> diff_io_types;
// chip flags
static constexpr int32_t HAS_SP32 = 0;
});
} // namespace

View File

@ -14,6 +14,9 @@ from apycula import chipdb
# Bel flags
BEL_FLAG_SIMPLE_IO = 0x100
# Chip flags
CHIP_HAS_SP32 = 0x1
# Z of the bels
# sync with C++ part!
LUT0_Z = 0 # z(DFFx) = z(LUTx) + 1
@ -86,6 +89,7 @@ class BottomIO(BBAStruct):
@dataclass
class ChipExtraData(BBAStruct):
strs: StringPool
flags: int
bottom_io: BottomIO
diff_io_types: list[IdString] = field(default_factory = list)
@ -105,6 +109,7 @@ class ChipExtraData(BBAStruct):
bba.u32(diff_io_type.index)
def serialise(self, context: str, bba: BBAWriter):
bba.u32(self.flags)
self.bottom_io.serialise(f"{context}_bottom_io", bba)
bba.slice(f"{context}_diff_io_types", len(self.diff_io_types))
@ -691,8 +696,8 @@ def create_packages(chip: Chip, db: chipdb):
pad = pkg.create_pad(pinno, tile, bel, pad_func, bank)
# Extra chip data
def create_extra_data(chip: Chip, db: chipdb):
chip.extra_data = ChipExtraData(chip.strs, None)
def create_extra_data(chip: Chip, db: chipdb, chip_flags: int):
chip.extra_data = ChipExtraData(chip.strs, chip_flags, None)
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)
@ -710,6 +715,10 @@ def main():
with gzip.open(importlib.resources.files("apycula").joinpath(f"{device}.pickle"), 'rb') as f:
db = pickle.load(f)
chip_flags = 0;
if device not in {"GW1NS-4", "GW1N-9"}:
chip_flags &= CHIP_HAS_SP32;
X = db.cols;
Y = db.rows;
@ -753,7 +762,7 @@ def main():
# Create nodes between tiles
create_nodes(ch, db)
create_extra_data(ch, db)
create_extra_data(ch, db, chip_flags)
ch.write_bba(args.output)
if __name__ == '__main__':
main()

View File

@ -91,4 +91,17 @@ IdString GowinUtils::get_bottom_io_wire_b_net(int8_t condition)
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
return IdString(extra->bottom_io.conditions[condition].wire_b_net);
}
bool GowinUtils::have_SP32(void)
{
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
return extra->chip_flags & Extra_chip_data_POD::HAS_SP32;
}
std::unique_ptr<CellInfo> GowinUtils::create_cell(IdString name, IdString type)
{
NPNR_ASSERT(!ctx->cells.count(name));
return std::make_unique<CellInfo>(ctx, name, type);
}
NEXTPNR_NAMESPACE_END

View File

@ -38,6 +38,12 @@ struct GowinUtils
// wires
inline bool is_wire_type_default(IdString wire_type) { return wire_type == IdString(); }
// chip dependent
bool have_SP32(void);
// make cell but do not include it in the list of chip cells.
std::unique_ptr<CellInfo> create_cell(IdString name, IdString type);
};
NEXTPNR_NAMESPACE_END

View File

@ -118,7 +118,7 @@ struct GowinPacker
log_error("Top-level port '%s' driven by illegal port %s.%s\n", ctx->nameOf(&ci),
ctx->nameOf(i->driver.cell), ctx->nameOf(i->driver.port));
for (const auto &attr : ci.attrs) {
i->driver.cell->attrs[attr.first] = attr.second;
i->driver.cell->setAttr(attr.first, attr.second);
}
}
NetInfo *o = ci.getPort(id_O);
@ -128,7 +128,7 @@ struct GowinPacker
log_error("Top-level port '%s' driving illegal port %s.%s\n", ctx->nameOf(&ci),
ctx->nameOf(usr.cell), ctx->nameOf(usr.port));
for (const auto &attr : ci.attrs) {
usr.cell->attrs[attr.first] = attr.second;
usr.cell->setAttr(attr.first, attr.second);
}
// network/port attributes that can be set in the
// restriction file and that need to be transferred to real
@ -150,7 +150,7 @@ struct GowinPacker
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;
io->driver.cell->setAttr(attr.first, attr.second);
}
}
ci.disconnectPort(id_I);
@ -1011,13 +1011,13 @@ struct GowinPacker
cin_ci->connectPort(id_COUT, cout_net);
if (cin_net->name == ctx->id("$PACKER_GND")) {
cin_ci->params[id_ALU_MODE] = std::string("C2L");
cin_ci->setParam(id_ALU_MODE, std::string("C2L"));
cin_ci->addInput(id_I2);
cin_ci->connectPort(id_I2, ctx->nets.at(ctx->id("$PACKER_VCC")).get());
return cin_ci;
}
if (cin_net->name == ctx->id("$PACKER_VCC")) {
cin_ci->params[id_ALU_MODE] = std::string("ONE2C");
cin_ci->setParam(id_ALU_MODE, std::string("ONE2C"));
cin_ci->addInput(id_I2);
cin_ci->connectPort(id_I2, ctx->nets.at(ctx->id("$PACKER_VCC")).get());
return cin_ci;
@ -1031,7 +1031,7 @@ struct GowinPacker
cin_ci->connectPort(id_I3, cin_net);
cin_ci->addInput(id_I2);
cin_ci->connectPort(id_I2, ctx->nets.at(ctx->id("$PACKER_VCC")).get());
cin_ci->params[id_ALU_MODE] = std::string("0"); // ADD
cin_ci->setParam(id_ALU_MODE, std::string("0")); // ADD
return cin_ci;
}
@ -1054,7 +1054,7 @@ struct GowinPacker
cout_ci->addInput(id_I2);
cout_ci->connectPort(id_I2, ctx->nets.at(ctx->id("$PACKER_VCC")).get());
cout_ci->params[id_ALU_MODE] = std::string("C2L");
cout_ci->setParam(id_ALU_MODE, std::string("C2L"));
return cout_ci;
}
@ -1065,7 +1065,7 @@ struct GowinPacker
IdString name_id = ctx->id(name);
auto dummy_ci = std::make_unique<CellInfo>(ctx, name_id, id_ALU);
dummy_ci->params[id_ALU_MODE] = std::string("C2L");
dummy_ci->setParam(id_ALU_MODE, std::string("C2L"));
return dummy_ci;
}
@ -1116,7 +1116,7 @@ struct GowinPacker
ci->constr_y = 0;
ci->constr_z = alu_chain_len % 6;
// XXX mode 0 - ADD
if (ci->params[id_ALU_MODE].as_int64() == 0) {
if (ci->params.at(id_ALU_MODE).as_int64() == 0) {
ci->renamePort(id_I3, id_I2);
ci->renamePort(id_I0, id_I3);
ci->renamePort(id_I2, id_I0);
@ -1210,9 +1210,9 @@ struct GowinPacker
}
IdString init_name = ctx->idf("INIT_%d", index);
if (ci->params.count(init_name)) {
lut_ci->params[id_INIT] = ci->params.at(init_name);
lut_ci->setParam(id_INIT, ci->params.at(init_name));
} else {
lut_ci->params[id_INIT] = std::string("1111111111111111");
lut_ci->setParam(id_INIT, std::string("1111111111111111"));
}
return lut_ci;
}
@ -1296,12 +1296,14 @@ struct GowinPacker
void pack_ROM(CellInfo *ci)
{
int default_bw = 32;
// XXX use block 111
ci->setParam(ctx->id("BLK_SEL"), Property(7, 32));
if (ci->type == id_pROM) {
ci->setAttr(id_BSRAM_SUBTYPE, Property(""));
} else {
ci->setAttr(id_BSRAM_SUBTYPE, Property("X9"));
default_bw = 36;
}
NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
@ -1321,10 +1323,10 @@ struct GowinPacker
ci->connectPort(id_WREB, vss_net);
if (!ci->params.count(id_BIT_WIDTH)) {
ci->setParam(id_BIT_WIDTH, Property(32, 32));
ci->setParam(id_BIT_WIDTH, Property(default_bw, 32));
}
int bit_width = ci->params[id_BIT_WIDTH].as_int64();
int bit_width = ci->params.at(id_BIT_WIDTH).as_int64();
if (bit_width == 32 || bit_width == 36) {
ci->copyPortTo(id_CLK, ci, id_CLKB);
ci->copyPortTo(id_CE, ci, id_CEB);
@ -1354,10 +1356,12 @@ struct GowinPacker
void pack_SDPB(CellInfo *ci)
{
int default_bw = 32;
if (ci->type == id_SDPB) {
ci->setAttr(id_BSRAM_SUBTYPE, Property(""));
} else {
ci->setAttr(id_BSRAM_SUBTYPE, Property("X9"));
default_bw = 36;
}
NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
@ -1380,18 +1384,18 @@ struct GowinPacker
ci->connectPort(id_WRE, vcc_net);
if (!ci->params.count(id_BIT_WIDTH_0)) {
ci->setParam(id_BIT_WIDTH_0, Property(32, 32));
ci->setParam(id_BIT_WIDTH_0, Property(default_bw, 32));
}
int bit_width = ci->params[id_BIT_WIDTH_0].as_int64();
int bit_width = ci->params.at(id_BIT_WIDTH_0).as_int64();
bsram_rename_ports(ci, bit_width, "DI[%d]", "DI%d");
// Port B
ci->addInput(id_WREB);
if (!ci->params.count(id_BIT_WIDTH_1)) {
ci->setParam(id_BIT_WIDTH_1, Property(32, 32));
ci->setParam(id_BIT_WIDTH_1, Property(default_bw, 32));
}
bit_width = ci->params[id_BIT_WIDTH_1].as_int64();
bit_width = ci->params.at(id_BIT_WIDTH_1).as_int64();
if (bit_width == 32 || bit_width == 36) {
ci->connectPort(id_WREB, vcc_net);
bsram_rename_ports(ci, bit_width, "DO[%d]", "DO%d");
@ -1403,10 +1407,12 @@ struct GowinPacker
void pack_DPB(CellInfo *ci)
{
int default_bw = 16;
if (ci->type == id_DPB) {
ci->setAttr(id_BSRAM_SUBTYPE, Property(""));
} else {
ci->setAttr(id_BSRAM_SUBTYPE, Property("X9"));
default_bw = 18;
}
for (int i = 0; i < 14; ++i) {
@ -1420,44 +1426,109 @@ struct GowinPacker
}
if (!ci->params.count(id_BIT_WIDTH_0)) {
ci->setParam(id_BIT_WIDTH_0, Property(16, 32));
}
int bit_width = ci->params[id_BIT_WIDTH_0].as_int64();
if (bit_width == 32 || bit_width == 36) {
log_error("Bit width %d is not supported\n", bit_width);
ci->setParam(id_BIT_WIDTH_0, Property(default_bw, 32));
}
int bit_width = ci->params.at(id_BIT_WIDTH_0).as_int64();
bsram_rename_ports(ci, bit_width, "DIA[%d]", "DIA%d");
bsram_rename_ports(ci, bit_width, "DOA[%d]", "DOA%d");
if (!ci->params.count(id_BIT_WIDTH_1)) {
ci->setParam(id_BIT_WIDTH_1, Property(16, 32));
}
bit_width = ci->params[id_BIT_WIDTH_1].as_int64();
if (bit_width == 32 || bit_width == 36) {
log_error("Bit width %d is not supported\n", bit_width);
ci->setParam(id_BIT_WIDTH_1, Property(default_bw, 32));
}
bit_width = ci->params.at(id_BIT_WIDTH_1).as_int64();
bsram_rename_ports(ci, bit_width, "DIB[%d]", "DIB%d");
bsram_rename_ports(ci, bit_width, "DOB[%d]", "DOB%d");
}
void pack_SP(CellInfo *ci)
void divide_sp(CellInfo *ci, std::vector<std::unique_ptr<CellInfo>> &new_cells)
{
int bw = ci->params.at(id_BIT_WIDTH).as_int64();
NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get();
IdString cell_type = id_SP;
if (bw == 36) {
cell_type = id_SPX9;
}
IdString name = ctx->idf("%s_AUX", ctx->nameOf(ci));
auto sp_cell = gwu.create_cell(name, cell_type);
CellInfo *sp = sp_cell.get();
ci->copyPortTo(id_CLK, sp, id_CLK);
ci->copyPortTo(id_OCE, sp, id_OCE);
ci->copyPortTo(id_CE, sp, id_CE);
ci->copyPortTo(id_RESET, sp, id_RESET);
ci->copyPortTo(id_WRE, sp, id_WRE);
// XXX Separate "byte enable" port
ci->movePortTo(ctx->id("AD[2]"), sp, ctx->id("AD0"));
ci->movePortTo(ctx->id("AD[3]"), sp, ctx->id("AD1"));
ci->connectPort(ctx->id("AD[2]"), vss_net);
ci->connectPort(ctx->id("AD[3]"), vss_net);
sp->addInput(ctx->id("AD2"));
sp->connectPort(ctx->id("AD2"), vss_net);
sp->addInput(ctx->id("AD3"));
sp->connectPort(ctx->id("AD3"), vss_net);
ci->disconnectPort(ctx->id("AD[4]"));
ci->connectPort(ctx->id("AD[4]"), vss_net);
sp->addInput(ctx->id("AD4"));
sp->connectPort(ctx->id("AD4"), vcc_net);
ci->copyPortBusTo(id_AD, 5, true, sp, id_AD, 5, false, 14 - 5 + 1);
sp->params = ci->params;
sp->setAttr(id_BSRAM_SUBTYPE, ci->attrs.at(id_BSRAM_SUBTYPE));
if (bw == 32) {
ci->setParam(id_BIT_WIDTH, Property(16, 32));
sp->setParam(id_BIT_WIDTH, Property(16, 32));
ci->movePortBusTo(id_DI, 16, true, sp, id_DI, 0, false, 16);
ci->movePortBusTo(id_DO, 16, true, sp, id_DO, 0, false, 16);
} else {
ci->setParam(id_BIT_WIDTH, Property(18, 32));
sp->setParam(id_BIT_WIDTH, Property(18, 32));
ci->movePortBusTo(id_DI, 18, true, sp, id_DI, 0, false, 18);
ci->movePortBusTo(id_DO, 18, true, sp, id_DO, 0, false, 18);
}
ci->copyPortBusTo(ctx->id("BLKSEL"), 0, true, sp, ctx->id("BLKSEL"), 0, false, 3);
new_cells.push_back(std::move(sp_cell));
}
void pack_SP(CellInfo *ci, std::vector<std::unique_ptr<CellInfo>> &new_cells)
{
int default_bw = 32;
if (ci->type == id_SP) {
ci->setAttr(id_BSRAM_SUBTYPE, Property(""));
} else {
ci->setAttr(id_BSRAM_SUBTYPE, Property("X9"));
default_bw = 36;
}
if (!ci->params.count(id_BIT_WIDTH)) {
ci->setParam(id_BIT_WIDTH, Property(default_bw, 32));
}
int bit_width = ci->params.at(id_BIT_WIDTH).as_int64();
// XXX UG285-1.3.6_E Gowin BSRAM & SSRAM User Guide:
// For GW1N-9/GW1NR-9/GW1NS-4 series, 32/36-bit SP/SPX9 is divided into two
// SP/SPX9s, which occupy two BSRAMs.
// So divide it here
if ((bit_width == 32 || bit_width == 36) && !gwu.have_SP32()) {
divide_sp(ci, new_cells);
bit_width = ci->params.at(id_BIT_WIDTH).as_int64();
}
NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
for (int i = 0; i < 3; ++i) {
ci->renamePort(ctx->idf("BLKSEL[%d]", i), ctx->idf("BLKSEL%d", i));
ci->copyPortTo(ctx->idf("BLKSEL%d", i), ci, ctx->idf("BLKSELB%d", i));
if (bit_width == 32 || bit_width == 36) {
ci->copyPortTo(ctx->idf("BLKSEL%d", i), ci, ctx->idf("BLKSELB%d", i));
}
}
if (!ci->params.count(id_BIT_WIDTH)) {
ci->setParam(id_BIT_WIDTH, Property(32, 32));
}
int bit_width = ci->params[id_BIT_WIDTH].as_int64();
for (int i = 0; i < 14; ++i) {
ci->renamePort(ctx->idf("AD[%d]", i), ctx->idf("AD%d", i));
if (bit_width == 32 || bit_width == 36) {
@ -1479,11 +1550,11 @@ struct GowinPacker
void pack_bsram(void)
{
std::vector<std::unique_ptr<CellInfo>> new_cells;
log_info("Pack BSRAMs...\n");
for (auto &cell : ctx->cells) {
auto ci = cell.second.get();
if (is_bsram(ci)) {
if (ctx->verbose) {
log_info(" pack %s\n", ci->type.c_str(ctx));
@ -1506,7 +1577,7 @@ struct GowinPacker
break;
case ID_SPX9: /* fallthrough */
case ID_SP:
pack_SP(ci);
pack_SP(ci, new_cells);
ci->type = id_SP;
break;
default:
@ -1514,6 +1585,10 @@ struct GowinPacker
}
}
}
for (auto &cell : new_cells) {
ctx->cells[cell->name] = std::move(cell);
}
}
// ===================================