#ifndef GOWIN_H
#define GOWIN_H

#include "nextpnr.h"

NEXTPNR_NAMESPACE_BEGIN

namespace {
// 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 is_lut(const CellInfo *cell) { return type_is_lut(cell->type); }
// Return true if a cell is a DFF
inline bool type_is_dff(IdString cell_type)
{
    return cell_type.in(id_DFF, id_DFFE, id_DFFN, id_DFFNE, id_DFFS, id_DFFSE, id_DFFNS, id_DFFNSE, id_DFFR, id_DFFRE,
                        id_DFFNR, id_DFFNRE, id_DFFP, id_DFFPE, id_DFFNP, id_DFFNPE, id_DFFC, id_DFFCE, id_DFFNC,
                        id_DFFNCE);
}
inline bool is_dff(const CellInfo *cell) { return type_is_dff(cell->type); }
// Return true if a cell is a ALU
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); }

// io
inline bool type_is_io(IdString cell_type) { return cell_type.in(id_IBUF, id_OBUF, id_IOBUF, id_TBUF); }
inline bool is_io(const CellInfo *cell) { return type_is_io(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); }

// MIPI
inline bool type_is_mipi(IdString cell_type) { return cell_type.in(id_MIPI_OBUF, id_MIPI_OBUF_A, id_MIPI_IBUF); }
inline bool is_mipi(const CellInfo *cell) { return type_is_mipi(cell->type); }

// I3C
inline bool type_is_i3c(IdString cell_type) { return cell_type.in(id_I3C_IOBUF); }
inline bool is_i3c(const CellInfo *cell) { return type_is_i3c(cell->type); }

// IOLOGIC input and output separately

inline bool type_is_iologico(IdString cell_type)
{
    return cell_type.in(id_ODDR, id_ODDRC, id_OSER4, id_OSER8, id_OSER10, id_OVIDEO, id_IOLOGICO_EMPTY);
}
inline bool is_iologico(const CellInfo *cell) { return type_is_iologico(cell->type); }

inline bool type_is_iologici(IdString cell_type)
{
    return cell_type.in(id_IDDR, id_IDDRC, id_IDES4, id_IDES8, id_IDES10, id_IVIDEO, id_IOLOGICI_EMPTY);
}
inline bool is_iologici(const CellInfo *cell) { return type_is_iologici(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, id_ROM16);
}
inline bool is_ssram(const CellInfo *cell) { return type_is_ssram(cell->type); }

// 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,
                        id_DPX9B);
}
inline bool is_bsram(const CellInfo *cell) { return type_is_bsram(cell->type); }

// Return true if a cell is a DSP
inline bool type_is_dsp(IdString cell_type)
{
    return cell_type.in(id_PADD9, id_PADD18, id_MULT9X9, id_MULT18X18, id_MULT36X36, id_ALU54D, id_MULTALU18X18,
                        id_MULTALU36X18, id_MULTADDALU18X18);
}
inline bool is_dsp(const CellInfo *cell) { return type_is_dsp(cell->type); }

// Return true if a cell is CLKDIV
inline bool type_is_clkdiv(IdString cell_type) { return cell_type == id_CLKDIV; }
inline bool is_clkdiv(const CellInfo *cell) { return type_is_clkdiv(cell->type); }

// Return true if a cell is CLKDIV2
inline bool type_is_clkdiv2(IdString cell_type) { return cell_type == id_CLKDIV2; }
inline bool is_clkdiv2(const CellInfo *cell) { return type_is_clkdiv2(cell->type); }

// Return true for HCLK Cells
inline bool is_hclk(const CellInfo *cell) { return type_is_clkdiv2(cell->type) || type_is_clkdiv(cell->type); }

// Return true if a cell is a UserFlash
inline bool type_is_userflash(IdString cell_type)
{
    return cell_type.in(id_FLASH96K, id_FLASH256K, id_FLASH608K, id_FLASH128K, id_FLASH64K, id_FLASH64K, id_FLASH64KZ,
                        id_FLASH96KA);
}
inline bool is_userflash(const CellInfo *cell) { return type_is_userflash(cell->type); }

// Return true if a cell is a PLL
inline bool type_is_pll(IdString cell_type) { return cell_type.in(id_rPLL, id_PLLVR); }
inline bool is_pll(const CellInfo *cell) { return type_is_pll(cell->type); }

// Return true if a cell is a EMCU
inline bool type_is_emcu(IdString cell_type) { return cell_type == id_EMCU; }
inline bool is_emcu(const CellInfo *cell) { return type_is_emcu(cell->type); }

// ==========================================
// extra data in the chip db
// ==========================================
NPNR_PACKED_STRUCT(struct Pad_extra_data_POD {
    int32_t pll_tile;
    int32_t pll_bel;
    int32_t pll_type;
});

NPNR_PACKED_STRUCT(struct Tile_extra_data_POD {
    int32_t class_id;
    int16_t io16_x_off;
    int16_t io16_y_off;
    int32_t tile_flags;
    // tile flags
    static constexpr int32_t TILE_I3C_CAPABLE_IO = 1;
});

NPNR_PACKED_STRUCT(struct Bottom_io_cnd_POD {
    int32_t wire_a_net;
    int32_t wire_b_net;
});

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 Spine_bel_POD {
    int32_t spine;
    int32_t bel_x;
    int32_t bel_y;
    int32_t bel_z;
});

NPNR_PACKED_STRUCT(struct Io_dlldly_bel_POD {
    int32_t io;
    int32_t dlldly;
});

NPNR_PACKED_STRUCT(struct Wire_bel_POD {
    int32_t pip_xy;
    int32_t pip_dst;
    int32_t pip_src;
    int32_t bel_x;
    int32_t bel_y;
    int32_t bel_z;
    int32_t side;
});

NPNR_PACKED_STRUCT(struct Segment_POD {
    int16_t x;
    int16_t seg_idx;
    int16_t min_x;
    int16_t min_y;
    int16_t max_x;
    int16_t max_y;
    int16_t top_row;
    int16_t bottom_row;
    uint32_t top_wire;
    uint32_t bottom_wire;
    RelSlice<uint32_t> top_gate_wire;
    RelSlice<uint32_t> bottom_gate_wire;
});

NPNR_PACKED_STRUCT(struct Constraint_POD {
    int32_t net;
    int32_t row;
    int32_t col;
    int32_t bel;
    int32_t iostd;
});

NPNR_PACKED_STRUCT(struct Extra_package_data_POD { RelSlice<Constraint_POD> cst; });

NPNR_PACKED_STRUCT(struct Extra_chip_data_POD {
    int32_t chip_flags;
    Bottom_io_POD bottom_io;
    RelSlice<IdString> diff_io_types;
    RelSlice<Spine_bel_POD> dqce_bels;
    RelSlice<Spine_bel_POD> dcs_bels;
    RelSlice<Wire_bel_POD> dhcen_bels;
    RelSlice<Io_dlldly_bel_POD> io_dlldly_bels;
    RelSlice<Segment_POD> segments;
    // chip flags
    static constexpr int32_t HAS_SP32 = 1;
    static constexpr int32_t NEED_SP_FIX = 2;
    static constexpr int32_t NEED_BSRAM_OUTREG_FIX = 4;
    static constexpr int32_t NEED_BLKSEL_FIX = 8;
    static constexpr int32_t HAS_BANDGAP = 16;
    static constexpr int32_t HAS_PLL_HCLK = 32;
    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

// Bels Z ranges. It is desirable that these numbers be synchronized with the chipdb generator
namespace BelZ {
enum
{
    LUT0_Z = 0,
    LUT7_Z = 14,
    MUX20_Z = 16,
    MUX21_Z = 18,
    MUX23_Z = 22,
    MUX27_Z = 29,
    ALU0_Z = 30, // :35, 6 ALU
    RAMW_Z = 36, // RAM16SDP4

    IOBA_Z = 50,
    IOBB_Z = 51, // +IOBC...IOBL

    IOLOGICA_Z = 70,
    IDES16_Z = 74,
    OSER16_Z = 75,

    BUFG_Z = 76, // : 81 reserve just in case
    BSRAM_Z = 100,

    OSC_Z = 274,
    PLL_Z = 275,
    GSR_Z = 276,
    VCC_Z = 277,
    VSS_Z = 278,
    BANDGAP_Z = 279,

    DQCE_Z = 280,  // : 286 reserve for 6 DQCEs
    DCS_Z = 286,   // : 288 reserve for 2 DCSs
    DHCEN_Z = 288, // : 298

    USERFLASH_Z = 298,

    EMCU_Z = 300,

    MIPIOBUF_Z = 301,
    MIPIIBUF_Z = 302,

    DLLDLY_Z = 303, // : 305 reserve for 2 DLLDLYs

    PINCFG_Z = 400,

    // The two least significant bits encode Z for 9-bit adders and
    // multipliers, if they are equal to 0, then we get Z of their common
    // 18-bit equivalent.
    DSP_Z = 509, // DSP

    DSP_0_Z = 511, // DSP macro 0
    PADD18_0_0_Z = 512,
    PADD9_0_0_Z = 512 + 1,
    PADD9_0_1_Z = 512 + 2,
    PADD18_0_1_Z = 516,
    PADD9_0_2_Z = 516 + 1,
    PADD9_0_3_Z = 516 + 2,

    MULT18X18_0_0_Z = 520,
    MULT9X9_0_0_Z = 520 + 1,
    MULT9X9_0_1_Z = 520 + 2,
    MULT18X18_0_1_Z = 524,
    MULT9X9_0_2_Z = 524 + 1,
    MULT9X9_0_3_Z = 524 + 2,

    ALU54D_0_Z = 524 + 3,
    MULTALU18X18_0_Z = 528,
    MULTALU36X18_0_Z = 528 + 1,
    MULTADDALU18X18_0_Z = 528 + 2,

    MULT36X36_Z = 528 + 3,

    DSP_1_Z = 543, // DSP macro 1
    PADD18_1_0_Z = 544,
    PADD9_1_0_Z = 544 + 1,
    PADD9_1_1_Z = 544 + 2,
    PADD18_1_1_Z = 548,
    PADD9_1_2_Z = 548 + 1,
    PADD9_1_3_Z = 548 + 2,

    MULT18X18_1_0_Z = 552,
    MULT9X9_1_0_Z = 552 + 1,
    MULT9X9_1_1_Z = 552 + 2,
    MULT18X18_1_1_Z = 556,
    MULT9X9_1_2_Z = 556 + 1,
    MULT9X9_1_3_Z = 556 + 2,

    ALU54D_1_Z = 556 + 3,
    MULTALU18X18_1_Z = 560,
    MULTALU36X18_1_Z = 560 + 1,
    MULTADDALU18X18_1_Z = 560 + 2,

    // HCLK Bels
    CLKDIV2_0_Z = 610,
    CLKDIV2_1_Z = 611,
    CLKDIV2_2_Z = 612,
    CLKDIV2_3_Z = 613,

    CLKDIV_0_Z = 620,
    CLKDIV_1_Z = 621,
    CLKDIV_2_Z = 622,
    CLKDIV_3_Z = 623
};
}

NEXTPNR_NAMESPACE_END
#endif
