Gowin. Implement ALU for the GW5A series. (#1541)

The ALUs in the GW5A series have undergone changes compared to previous
chips.

The most significant change is the appearance of an input MUX for
carry — it is now possible to switch between VCC, GND, and COUT of the
previous ALU, as well as generate carry in logic.

The granularity of resource allocation for ALUs has also changed — it is
now possible to use each half of a slice independently for ALUs.

Not all new features are reflected in this commit:

  - since there is one CIN MUX for every six ALUs and it only works for
    ALUs with index 0, the new granularity is not very useful: the head of
    the chain can only be placed in the zero ALU. It is possible to gain one
    LUT by allocating ALUs in odd numbers, but we will leave that for the
    future.

  - using CIN MUX to generate carry in logic is interesting, but we have
    not yet been able to get the vendor IDE to generate such a
    configuration to figure out which wires are used, so for now we are
    leaving the old behavior in logic with the allocation of a specialized
    head ALU.

Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
YRabbit 2025-08-27 00:17:55 +10:00 committed by GitHub
parent 0a7cbe1cd7
commit d966fc5dcb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 60 additions and 21 deletions

View File

@ -1368,4 +1368,5 @@ X(SEG_WIRES_TO_ISOLATE)
// routing params
X(NO_GP_CLOCK_ROUTING)
// gw5a alu
X(CIN_NETTYPE)

View File

@ -198,6 +198,7 @@ NPNR_PACKED_STRUCT(struct Extra_chip_data_POD {
static constexpr int32_t HAS_CLKDIV_HCLK = 64;
static constexpr int32_t HAS_PINCFG = 128;
static constexpr int32_t HAS_DFF67 = 256;
static constexpr int32_t HAS_CIN_MUX = 512;
});
} // namespace

View File

@ -24,6 +24,7 @@ CHIP_HAS_PLL_HCLK = 0x20
CHIP_HAS_CLKDIV_HCLK = 0x40
CHIP_HAS_PINCFG = 0x80
CHIP_HAS_DFF67 = 0x100
CHIP_HAS_CIN_MUX = 0x200
# Tile flags
TILE_I3C_CAPABLE_IO = 0x1
@ -1590,6 +1591,8 @@ def main():
chip_flags |= CHIP_HAS_PINCFG;
if "HAS_DFF67" in db.chip_flags:
chip_flags |= CHIP_HAS_DFF67;
if "HAS_CIN_MUX" in db.chip_flags:
chip_flags |= CHIP_HAS_CIN_MUX;
X = db.cols;
Y = db.rows;

View File

@ -342,6 +342,12 @@ bool GowinUtils::has_DFF67(void) const
return extra->chip_flags & Extra_chip_data_POD::HAS_DFF67;
}
bool GowinUtils::has_CIN_MUX(void) const
{
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_CIN_MUX;
}
bool GowinUtils::has_SP32(void)
{
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());

View File

@ -102,6 +102,9 @@ struct GowinUtils
// Logic cell structure
bool has_DFF67(void) const;
// ALU
bool has_CIN_MUX(void) const;
// DSP
inline int get_dsp_18_z(int z) const { return z & (~3); }
inline int get_dsp_9_idx(int z) const { return z & 3; }

View File

@ -1867,7 +1867,8 @@ struct GowinPacker
// ALU
// ===================================
// create ALU CIN block
std::unique_ptr<CellInfo> alu_add_cin_block(Context *ctx, CellInfo *head, NetInfo *cin_net)
std::unique_ptr<CellInfo> alu_add_cin_block(Context *ctx, CellInfo *head, NetInfo *cin_net, bool cin_is_vcc,
bool cin_is_gnd)
{
std::string name = head->name.str(ctx) + "_HEAD_ALULC";
IdString name_id = ctx->id(name);
@ -1880,13 +1881,13 @@ struct GowinPacker
cin_ci->addOutput(id_COUT);
cin_ci->connectPort(id_COUT, cout_net);
if (cin_net->name == ctx->id("$PACKER_GND")) {
if (cin_is_gnd) {
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")) {
if (cin_is_vcc) {
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());
@ -1902,6 +1903,7 @@ struct GowinPacker
cin_ci->addInput(id_I2);
cin_ci->connectPort(id_I2, ctx->nets.at(ctx->id("$PACKER_VCC")).get());
cin_ci->setParam(id_ALU_MODE, std::string("0")); // ADD
cin_ci->setParam(id_CIN_NETTYPE, Property("LOGIC"));
return cin_ci;
}
@ -1962,29 +1964,52 @@ struct GowinPacker
if (ctx->debug) {
log_info("ALU head found %s. CIN net is %s\n", ctx->nameOf(ci), ctx->nameOf(cin_net));
}
// always prepend first ALU with carry generator block
// three cases: CIN == 0, CIN == 1 and CIN == ?
new_cells.push_back(alu_add_cin_block(ctx, ci, cin_net));
CellInfo *cin_block_ci = new_cells.back().get();
// CIN block is the cluster root and is always placed in ALU0
// This is a possible place for further optimization
bool cin_is_vcc = cin_net->name == ctx->id("$PACKER_VCC");
bool cin_is_gnd = cin_net->name == ctx->id("$PACKER_GND");
bool cin_is_logic = !cin_is_vcc && !cin_is_gnd;
CellInfo *cin_block_ci;
int alu_chain_len;
// According to the documentation, GW5A can use CIN from
// logic using the input MUX, but in practice this has not
// yet been achieved. We are leaving the old mechanism in
// place for this case.
if ((!gwu.has_CIN_MUX()) || cin_is_logic) {
// prepend first ALU with carry generator block
// three cases: CIN == 0, CIN == 1 and CIN == ?
new_cells.push_back(alu_add_cin_block(ctx, ci, cin_net, cin_is_vcc, cin_is_gnd));
cin_block_ci = new_cells.back().get();
// CIN block is the cluster root and is always placed in ALU0
alu_chain_len = 1;
} else {
cin_block_ci = ci;
ci->disconnectPort(id_CIN);
if (cin_is_vcc) {
ci->setParam(id_CIN_NETTYPE, Property("VCC"));
} else {
ci->setParam(id_CIN_NETTYPE, Property("GND"));
}
alu_chain_len = 0;
}
cin_block_ci->cluster = cin_block_ci->name;
cin_block_ci->constr_z = BelZ::ALU0_Z;
cin_block_ci->constr_abs_z = true;
int alu_chain_len = 1;
while (true) {
// add to cluster
if (ctx->debug) {
log_info("Add ALU to the chain (len:%d): %s\n", alu_chain_len, ctx->nameOf(ci));
if (ci != cin_block_ci) {
// add to cluster
if (ctx->debug) {
log_info("Add ALU to the chain (len:%d): %s\n", alu_chain_len, ctx->nameOf(ci));
}
cin_block_ci->constr_children.push_back(ci);
NPNR_ASSERT(ci->cluster == ClusterId());
ci->cluster = cin_block_ci->name;
ci->constr_abs_z = false;
ci->constr_x = alu_chain_len / 6;
ci->constr_y = 0;
ci->constr_z = alu_chain_len % 6;
}
cin_block_ci->constr_children.push_back(ci);
NPNR_ASSERT(ci->cluster == ClusterId());
ci->cluster = cin_block_ci->name;
ci->constr_abs_z = false;
ci->constr_x = alu_chain_len / 6;
ci->constr_y = 0;
ci->constr_z = alu_chain_len % 6;
// XXX mode 0 - ADD
if (ci->params.at(id_ALU_MODE).as_int64() == 0) {
ci->renamePort(id_I3, id_I2);