gowin: Himbaechel. Add OSER10 and OVIDEO
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
parent
dfb701b5ab
commit
3a073540c2
@ -1091,6 +1091,7 @@ X(XD4)
|
|||||||
X(XD5)
|
X(XD5)
|
||||||
|
|
||||||
// HCLK wires
|
// HCLK wires
|
||||||
|
X(HCLK)
|
||||||
X(HCLK_OUT0)
|
X(HCLK_OUT0)
|
||||||
X(HCLK_OUT1)
|
X(HCLK_OUT1)
|
||||||
X(HCLK_OUT2)
|
X(HCLK_OUT2)
|
||||||
|
@ -49,8 +49,8 @@ struct GowinGlobalRouter
|
|||||||
|
|
||||||
IdString src_type = ctx->getWireType(ctx->getPipSrcWire(pip));
|
IdString src_type = ctx->getWireType(ctx->getPipSrcWire(pip));
|
||||||
IdString dst_type = ctx->getWireType(ctx->getPipDstWire(pip));
|
IdString dst_type = ctx->getWireType(ctx->getPipDstWire(pip));
|
||||||
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, id_HCLK);
|
||||||
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, id_HCLK);
|
||||||
|
|
||||||
bool res = (src_valid && dst_valid) || (src_valid && is_local(dst_type)) || (is_local(src_type) && dst_valid);
|
bool res = (src_valid && dst_valid) || (src_valid && is_local(dst_type)) || (is_local(src_type) && dst_valid);
|
||||||
if (ctx->debug && res && false) {
|
if (ctx->debug && res && false) {
|
||||||
|
@ -38,10 +38,6 @@ struct GowinImpl : HimbaechelAPI
|
|||||||
|
|
||||||
bool isValidBelForCellType(IdString cell_type, BelId bel) const override;
|
bool isValidBelForCellType(IdString cell_type, BelId bel) const override;
|
||||||
|
|
||||||
// placer hits
|
|
||||||
void notifyBelChange(BelId bel, CellInfo *cell) override;
|
|
||||||
bool checkBelAvail(BelId bel) const override;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
HimbaechelHelpers h;
|
HimbaechelHelpers h;
|
||||||
GowinUtils gwu;
|
GowinUtils gwu;
|
||||||
@ -182,13 +178,13 @@ void GowinImpl::postRoute()
|
|||||||
if (is_iologic(ci) && !ci->type.in(id_ODDR, id_ODDRC, id_IDDR, id_IDDRC)) {
|
if (is_iologic(ci) && !ci->type.in(id_ODDR, id_ODDRC, id_IDDR, id_IDDRC)) {
|
||||||
if (visited_hclk_users.find(ci->name) == visited_hclk_users.end()) {
|
if (visited_hclk_users.find(ci->name) == visited_hclk_users.end()) {
|
||||||
// mark FCLK<-HCLK connections
|
// mark FCLK<-HCLK connections
|
||||||
ci->setAttr(id_IOLOGIC_FCLK, Property("UNKNOWN"));
|
|
||||||
const NetInfo *h_net = ci->getPort(id_FCLK);
|
const NetInfo *h_net = ci->getPort(id_FCLK);
|
||||||
if (h_net) {
|
if (h_net) {
|
||||||
for (auto &user : h_net->users) {
|
for (auto &user : h_net->users) {
|
||||||
if (user.port != id_FCLK) {
|
if (user.port != id_FCLK) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
user.cell->setAttr(id_IOLOGIC_FCLK, Property("UNKNOWN"));
|
||||||
visited_hclk_users.insert(user.cell->name);
|
visited_hclk_users.insert(user.cell->name);
|
||||||
// XXX Based on the implementation, perhaps a function
|
// XXX Based on the implementation, perhaps a function
|
||||||
// is needed to get Pip from a Wire
|
// is needed to get Pip from a Wire
|
||||||
@ -395,27 +391,6 @@ bool GowinImpl::slice_valid(int x, int y, int z) const
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// placer hits
|
|
||||||
void GowinImpl::notifyBelChange(BelId bel, CellInfo *cell)
|
|
||||||
{
|
|
||||||
if (cell != nullptr) {
|
|
||||||
// OSER8 took both IOLOGIC bels in the tile
|
|
||||||
if (cell->type == id_OSER8) {
|
|
||||||
Loc loc = ctx->getBelLocation(bel);
|
|
||||||
inactive_bels.insert(ctx->getBelByLocation(get_pair_iologic_bel(loc)));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// the unbind is about to happen
|
|
||||||
CellInfo *ci = ctx->getBoundBelCell(bel);
|
|
||||||
// OSER8 took both IOLOGIC bels in the tile
|
|
||||||
if (ci->type == id_OSER8) {
|
|
||||||
Loc loc = ctx->getBelLocation(bel);
|
|
||||||
inactive_bels.erase(ctx->getBelByLocation(get_pair_iologic_bel(loc)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GowinImpl::checkBelAvail(BelId bel) const { return inactive_bels.find(bel) == inactive_bels.end(); }
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
@ -5,6 +5,60 @@
|
|||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
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); }
|
||||||
|
|
||||||
|
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); }
|
||||||
|
|
||||||
|
inline bool type_is_iologic(IdString cell_type)
|
||||||
|
{
|
||||||
|
return cell_type.in(id_ODDR, id_ODDRC, id_OSER4, id_OSER8, id_OSER10, id_OVIDEO);
|
||||||
|
}
|
||||||
|
inline bool is_iologic(const CellInfo *cell) { return type_is_iologic(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); }
|
||||||
|
inline bool is_ssram(const CellInfo *cell) { return type_is_ssram(cell->type); }
|
||||||
|
|
||||||
|
// extra data in the chip db
|
||||||
|
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 Extra_chip_data_POD {
|
||||||
|
Bottom_io_POD bottom_io;
|
||||||
|
RelSlice<IdString> diff_io_types;
|
||||||
|
});
|
||||||
|
|
||||||
|
} // 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
|
||||||
namespace BelZ {
|
namespace BelZ {
|
||||||
enum
|
enum
|
||||||
@ -30,101 +84,5 @@ enum
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace BelFlags {
|
|
||||||
static constexpr uint32_t FLAG_SIMPLE_IO = 0x100;
|
|
||||||
}
|
|
||||||
|
|
||||||
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); }
|
|
||||||
|
|
||||||
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); }
|
|
||||||
|
|
||||||
inline bool type_is_iologic(IdString cell_type) { return cell_type.in(id_ODDR, id_ODDRC, id_OSER4, id_OSER8); }
|
|
||||||
inline bool is_iologic(const CellInfo *cell) { return type_is_iologic(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); }
|
|
||||||
inline bool is_ssram(const CellInfo *cell) { return type_is_ssram(cell->type); }
|
|
||||||
|
|
||||||
// extra data in the chip db
|
|
||||||
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 Extra_chip_data_POD {
|
|
||||||
Bottom_io_POD bottom_io;
|
|
||||||
RelSlice<IdString> diff_io_types;
|
|
||||||
});
|
|
||||||
|
|
||||||
inline bool is_diff_io_supported(const ChipInfoPOD *chip, IdString type)
|
|
||||||
{
|
|
||||||
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(chip->extra_data.get());
|
|
||||||
for (auto &dtype : extra->diff_io_types) {
|
|
||||||
if (IdString(dtype) == type) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(chip->extra_data.get());
|
|
||||||
return IdString(extra->bottom_io.conditions[condition].wire_a_net);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline IdString get_bottom_io_wire_b_net(const ChipInfoPOD *chip, int8_t condition)
|
|
||||||
{
|
|
||||||
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(chip->extra_data.get());
|
|
||||||
return IdString(extra->bottom_io.conditions[condition].wire_b_net);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bels and pips
|
|
||||||
inline bool is_simple_io_bel(const ChipInfoPOD *chip, BelId bel)
|
|
||||||
{
|
|
||||||
return chip_bel_info(chip, bel).flags & BelFlags::FLAG_SIMPLE_IO;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline Loc get_pair_iologic_bel(Loc loc)
|
|
||||||
{
|
|
||||||
loc.z = BelZ::IOLOGICA_Z + (1 - (loc.z - BelZ::IOLOGICA_Z));
|
|
||||||
return loc;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,9 +1,14 @@
|
|||||||
#include "gowin_utils.h"
|
|
||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "nextpnr.h"
|
#include "nextpnr.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
#define HIMBAECHEL_CONSTIDS "uarch/gowin/constids.inc"
|
||||||
|
#include "himbaechel_constids.h"
|
||||||
|
#include "himbaechel_helpers.h"
|
||||||
|
|
||||||
|
#include "gowin.h"
|
||||||
|
#include "gowin_utils.h"
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
// pin functions: GCLKT_4, SSPI_CS, READY etc
|
// pin functions: GCLKT_4, SSPI_CS, READY etc
|
||||||
@ -22,4 +27,50 @@ IdStringList GowinUtils::get_pin_funcs(BelId bel)
|
|||||||
return IdStringList();
|
return IdStringList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GowinUtils::is_simple_io_bel(BelId bel)
|
||||||
|
{
|
||||||
|
return chip_bel_info(ctx->chip_info, bel).flags & BelFlags::FLAG_SIMPLE_IO;
|
||||||
|
}
|
||||||
|
|
||||||
|
Loc GowinUtils::get_pair_iologic_bel(Loc loc)
|
||||||
|
{
|
||||||
|
loc.z = BelZ::IOLOGICA_Z + (1 - (loc.z - BelZ::IOLOGICA_Z));
|
||||||
|
return loc;
|
||||||
|
}
|
||||||
|
|
||||||
|
BelId GowinUtils::get_io_bel_from_iologic(BelId bel)
|
||||||
|
{
|
||||||
|
Loc loc = ctx->getBelLocation(bel);
|
||||||
|
loc.z = BelZ::IOBA_Z + loc.z - BelZ::IOLOGICA_Z;
|
||||||
|
return ctx->getBelByLocation(loc);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GowinUtils::is_diff_io_supported(IdString type)
|
||||||
|
{
|
||||||
|
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
|
||||||
|
for (auto &dtype : extra->diff_io_types) {
|
||||||
|
if (IdString(dtype) == type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GowinUtils::have_bottom_io_cnds(void)
|
||||||
|
{
|
||||||
|
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
|
||||||
|
return extra->bottom_io.conditions.size() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
IdString GowinUtils::get_bottom_io_wire_a_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_a_net);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -7,6 +7,10 @@
|
|||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
namespace BelFlags {
|
||||||
|
static constexpr uint32_t FLAG_SIMPLE_IO = 0x100;
|
||||||
|
}
|
||||||
|
|
||||||
struct GowinUtils
|
struct GowinUtils
|
||||||
{
|
{
|
||||||
Context *ctx;
|
Context *ctx;
|
||||||
@ -17,6 +21,16 @@ struct GowinUtils
|
|||||||
|
|
||||||
// pin functions: GCLKT_4, SSPI_CS, READY etc
|
// pin functions: GCLKT_4, SSPI_CS, READY etc
|
||||||
IdStringList get_pin_funcs(BelId bel);
|
IdStringList get_pin_funcs(BelId bel);
|
||||||
|
|
||||||
|
// Bels and pips
|
||||||
|
bool is_simple_io_bel(BelId bel);
|
||||||
|
Loc get_pair_iologic_bel(Loc loc);
|
||||||
|
BelId get_io_bel_from_iologic(BelId bel);
|
||||||
|
|
||||||
|
bool is_diff_io_supported(IdString type);
|
||||||
|
bool have_bottom_io_cnds(void);
|
||||||
|
IdString get_bottom_io_wire_a_net(int8_t condition);
|
||||||
|
IdString get_bottom_io_wire_b_net(int8_t condition);
|
||||||
};
|
};
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include "himbaechel_helpers.h"
|
#include "himbaechel_helpers.h"
|
||||||
|
|
||||||
#include "gowin.h"
|
#include "gowin.h"
|
||||||
|
#include "gowin_utils.h"
|
||||||
#include "pack.h"
|
#include "pack.h"
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
@ -16,8 +17,13 @@ struct GowinPacker
|
|||||||
{
|
{
|
||||||
Context *ctx;
|
Context *ctx;
|
||||||
HimbaechelHelpers h;
|
HimbaechelHelpers h;
|
||||||
|
GowinUtils gwu;
|
||||||
|
|
||||||
GowinPacker(Context *ctx) : ctx(ctx) { h.init(ctx); }
|
GowinPacker(Context *ctx) : ctx(ctx)
|
||||||
|
{
|
||||||
|
h.init(ctx);
|
||||||
|
gwu.init(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
// ===================================
|
// ===================================
|
||||||
// IO
|
// IO
|
||||||
@ -60,7 +66,7 @@ struct GowinPacker
|
|||||||
|
|
||||||
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)) {
|
if (!gwu.have_bottom_io_cnds()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!ci.type.in(id_OBUF, id_TBUF, id_IOBUF)) {
|
if (!ci.type.in(id_OBUF, id_TBUF, id_IOBUF)) {
|
||||||
@ -82,10 +88,10 @@ struct GowinPacker
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
IdString wire_a_net = get_bottom_io_wire_a_net(ctx->chip_info, cnd);
|
IdString wire_a_net = gwu.get_bottom_io_wire_a_net(cnd);
|
||||||
connect_io_wire(id_BOTTOM_IO_PORT_A, wire_a_net);
|
connect_io_wire(id_BOTTOM_IO_PORT_A, wire_a_net);
|
||||||
|
|
||||||
IdString wire_b_net = get_bottom_io_wire_b_net(ctx->chip_info, cnd);
|
IdString wire_b_net = gwu.get_bottom_io_wire_b_net(cnd);
|
||||||
connect_io_wire(id_BOTTOM_IO_PORT_B, wire_b_net);
|
connect_io_wire(id_BOTTOM_IO_PORT_B, wire_b_net);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,7 +174,7 @@ 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 (is_simple_io_bel(ctx->chip_info, io_bel)) {
|
if (gwu.is_simple_io_bel(io_bel)) {
|
||||||
config_simple_io(ci);
|
config_simple_io(ci);
|
||||||
}
|
}
|
||||||
make_iob_nets(ci);
|
make_iob_nets(ci);
|
||||||
@ -281,7 +287,7 @@ struct GowinPacker
|
|||||||
if (!is_diffio(&ci)) {
|
if (!is_diffio(&ci)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (!is_diff_io_supported(ctx->chip_info, ci.type)) {
|
if (!gwu.is_diff_io_supported(ci.type)) {
|
||||||
log_error("%s is not supported\n", ci.type.c_str(ctx));
|
log_error("%s is not supported\n", ci.type.c_str(ctx));
|
||||||
}
|
}
|
||||||
cells_to_remove.push_back(ci.name);
|
cells_to_remove.push_back(ci.name);
|
||||||
@ -332,7 +338,7 @@ struct GowinPacker
|
|||||||
IdString out_port = id_Q0;
|
IdString out_port = id_Q0;
|
||||||
IdString tx_port = id_Q1;
|
IdString tx_port = id_Q1;
|
||||||
|
|
||||||
CellInfo *out_iob = net_only_drives(ctx, ci.ports.at(out_port).net, is_iob, id_I);
|
CellInfo *out_iob = net_only_drives(ctx, ci.ports.at(out_port).net, is_iob, id_I, true);
|
||||||
NPNR_ASSERT(out_iob != nullptr && out_iob->bel != BelId());
|
NPNR_ASSERT(out_iob != nullptr && out_iob->bel != BelId());
|
||||||
BelId iob_bel = out_iob->bel;
|
BelId iob_bel = out_iob->bel;
|
||||||
|
|
||||||
@ -368,11 +374,15 @@ struct GowinPacker
|
|||||||
out_iob->disconnectPort(id_I);
|
out_iob->disconnectPort(id_I);
|
||||||
ci.disconnectPort(out_port);
|
ci.disconnectPort(out_port);
|
||||||
set_daaj_nets(ci, iob_bel);
|
set_daaj_nets(ci, iob_bel);
|
||||||
config_bottom_row(*out_iob, ctx->getBelLocation(iob_bel), Bottom_io_POD::DDR);
|
|
||||||
|
Loc io_loc = ctx->getBelLocation(iob_bel);
|
||||||
|
if (io_loc.y == ctx->getGridDimY() - 1) {
|
||||||
|
config_bottom_row(*out_iob, io_loc, Bottom_io_POD::DDR);
|
||||||
|
}
|
||||||
|
|
||||||
// if Q1 is connected then disconnect it too
|
// if Q1 is connected then disconnect it too
|
||||||
if (port_used(&ci, tx_port)) {
|
if (port_used(&ci, tx_port)) {
|
||||||
NPNR_ASSERT(out_iob == net_only_drives(ctx, ci.ports.at(tx_port).net, is_iob, id_OEN));
|
NPNR_ASSERT(out_iob == net_only_drives(ctx, ci.ports.at(tx_port).net, is_iob, id_OEN, true));
|
||||||
nets_to_remove.push_back(ci.getPort(tx_port)->name);
|
nets_to_remove.push_back(ci.getPort(tx_port)->name);
|
||||||
out_iob->disconnectPort(id_OEN);
|
out_iob->disconnectPort(id_OEN);
|
||||||
ci.disconnectPort(tx_port);
|
ci.disconnectPort(tx_port);
|
||||||
@ -380,6 +390,51 @@ struct GowinPacker
|
|||||||
make_iob_nets(*out_iob);
|
make_iob_nets(*out_iob);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pack_single_output_iol(CellInfo &ci, std::vector<IdString> &cells_to_remove,
|
||||||
|
std::vector<IdString> &nets_to_remove)
|
||||||
|
{
|
||||||
|
IdString out_port = id_Q;
|
||||||
|
|
||||||
|
CellInfo *out_iob = net_only_drives(ctx, ci.ports.at(out_port).net, is_iob, id_I, true);
|
||||||
|
NPNR_ASSERT(out_iob != nullptr && out_iob->bel != BelId());
|
||||||
|
BelId iob_bel = out_iob->bel;
|
||||||
|
|
||||||
|
BelId l_bel = get_iologic_bel(out_iob);
|
||||||
|
if (l_bel == BelId()) {
|
||||||
|
log_error("Can't place IOLOGIC %s at %s\n", ctx->nameOf(&ci), ctx->nameOfBel(iob_bel));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctx->checkBelAvail(l_bel)) {
|
||||||
|
log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci),
|
||||||
|
ctx->nameOfBel(l_bel), ctx->nameOf(ctx->getBoundBelCell(l_bel)));
|
||||||
|
}
|
||||||
|
ctx->bindBel(l_bel, &ci, PlaceStrength::STRENGTH_LOCKED);
|
||||||
|
std::string out_mode;
|
||||||
|
switch (ci.type.hash()) {
|
||||||
|
case ID_OVIDEO:
|
||||||
|
out_mode = "VIDEORX";
|
||||||
|
break;
|
||||||
|
case ID_OSER10:
|
||||||
|
out_mode = "ODDRX5";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ci.setParam(ctx->id("OUTMODE"), out_mode);
|
||||||
|
|
||||||
|
// mark IOB as used by IOLOGIC
|
||||||
|
out_iob->setParam(id_IOLOGIC_IOB, 1);
|
||||||
|
// disconnect Q output: it is wired internally
|
||||||
|
nets_to_remove.push_back(ci.getPort(out_port)->name);
|
||||||
|
out_iob->disconnectPort(id_I);
|
||||||
|
ci.disconnectPort(out_port);
|
||||||
|
set_daaj_nets(ci, iob_bel);
|
||||||
|
|
||||||
|
Loc io_loc = ctx->getBelLocation(iob_bel);
|
||||||
|
if (io_loc.y == ctx->getGridDimY() - 1) {
|
||||||
|
config_bottom_row(*out_iob, io_loc, Bottom_io_POD::DDR);
|
||||||
|
}
|
||||||
|
make_iob_nets(*out_iob);
|
||||||
|
}
|
||||||
|
|
||||||
IdString create_aux_iologic_name(IdString main_name, int idx = 0)
|
IdString create_aux_iologic_name(IdString main_name, int idx = 0)
|
||||||
{
|
{
|
||||||
std::string sfx("");
|
std::string sfx("");
|
||||||
@ -391,9 +446,11 @@ struct GowinPacker
|
|||||||
|
|
||||||
BelId get_aux_iologic_bel(const CellInfo &ci)
|
BelId get_aux_iologic_bel(const CellInfo &ci)
|
||||||
{
|
{
|
||||||
return ctx->getBelByLocation(get_pair_iologic_bel(ctx->getBelLocation(ci.bel)));
|
return ctx->getBelByLocation(gwu.get_pair_iologic_bel(ctx->getBelLocation(ci.bel)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_diff_io(BelId bel) { return ctx->getBoundBelCell(bel)->attrs.count(id_DIFF_TYPE) != 0; }
|
||||||
|
|
||||||
void create_aux_iologic_cells(CellInfo &ci)
|
void create_aux_iologic_cells(CellInfo &ci)
|
||||||
{
|
{
|
||||||
if (ci.type.in(id_ODDR, id_ODDRC, id_OSER4)) {
|
if (ci.type.in(id_ODDR, id_ODDRC, id_OSER4)) {
|
||||||
@ -401,16 +458,23 @@ struct GowinPacker
|
|||||||
}
|
}
|
||||||
IdString aux_name = create_aux_iologic_name(ci.name);
|
IdString aux_name = create_aux_iologic_name(ci.name);
|
||||||
BelId bel = get_aux_iologic_bel(ci);
|
BelId bel = get_aux_iologic_bel(ci);
|
||||||
|
BelId io_bel = gwu.get_io_bel_from_iologic(bel);
|
||||||
|
if (!ctx->checkBelAvail(io_bel)) {
|
||||||
|
if (!is_diff_io(io_bel)) {
|
||||||
|
log_error("Can't place %s at %s because of %s\n", ctx->nameOf(&ci), ctx->nameOfBel(bel),
|
||||||
|
ctx->nameOf(ctx->getBoundBelCell(io_bel)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ctx->createCell(aux_name, id_IOLOGIC_DUMMY);
|
ctx->createCell(aux_name, id_IOLOGIC_DUMMY);
|
||||||
CellInfo *aux = ctx->cells[aux_name].get();
|
CellInfo *aux = ctx->cells[aux_name].get();
|
||||||
aux->addInput(id_PCLK);
|
aux->addInput(id_PCLK);
|
||||||
ci.copyPortTo(id_PCLK, ctx->cells.at(aux_name).get(), id_PCLK);
|
ci.copyPortTo(id_PCLK, ctx->cells.at(aux_name).get(), id_PCLK);
|
||||||
aux->addInput(id_FCLK);
|
|
||||||
ci.copyPortTo(id_FCLK, ctx->cells.at(aux_name).get(), id_FCLK);
|
|
||||||
aux->addInput(id_RESET);
|
aux->addInput(id_RESET);
|
||||||
ci.copyPortTo(id_RESET, ctx->cells.at(aux_name).get(), id_RESET);
|
ci.copyPortTo(id_RESET, ctx->cells.at(aux_name).get(), id_RESET);
|
||||||
ctx->cells.at(aux_name)->setParam(ctx->id("OUTMODE"), Property("DDRENABLE"));
|
ctx->cells.at(aux_name)->setParam(ctx->id("OUTMODE"), Property("DDRENABLE"));
|
||||||
ctx->cells.at(aux_name)->setAttr(ctx->id("IOLOGIC_TYPE"), Property("DUMMY"));
|
ctx->cells.at(aux_name)->setAttr(ctx->id("IOLOGIC_TYPE"), Property("DUMMY"));
|
||||||
|
ctx->cells.at(aux_name)->setAttr(ctx->id("MAIN_CELL"), Property(ci.name.str(ctx)));
|
||||||
ctx->bindBel(bel, aux, PlaceStrength::STRENGTH_LOCKED);
|
ctx->bindBel(bel, aux, PlaceStrength::STRENGTH_LOCKED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,6 +496,11 @@ struct GowinPacker
|
|||||||
create_aux_iologic_cells(ci);
|
create_aux_iologic_cells(ci);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (ci.type.in(id_OVIDEO, id_OSER10)) {
|
||||||
|
pack_single_output_iol(ci, cells_to_remove, nets_to_remove);
|
||||||
|
create_aux_iologic_cells(ci);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto cell : cells_to_remove) {
|
for (auto cell : cells_to_remove) {
|
||||||
@ -957,13 +1026,29 @@ struct GowinPacker
|
|||||||
{
|
{
|
||||||
handle_constants();
|
handle_constants();
|
||||||
pack_iobs();
|
pack_iobs();
|
||||||
|
ctx->check();
|
||||||
|
|
||||||
pack_diff_iobs();
|
pack_diff_iobs();
|
||||||
|
ctx->check();
|
||||||
|
|
||||||
pack_iologic();
|
pack_iologic();
|
||||||
|
ctx->check();
|
||||||
|
|
||||||
pack_gsr();
|
pack_gsr();
|
||||||
|
ctx->check();
|
||||||
|
|
||||||
pack_wideluts();
|
pack_wideluts();
|
||||||
|
ctx->check();
|
||||||
|
|
||||||
pack_alus();
|
pack_alus();
|
||||||
|
ctx->check();
|
||||||
|
|
||||||
constrain_lutffs();
|
constrain_lutffs();
|
||||||
|
ctx->check();
|
||||||
|
|
||||||
pack_pll();
|
pack_pll();
|
||||||
|
ctx->check();
|
||||||
|
|
||||||
pack_ram16sdp4();
|
pack_ram16sdp4();
|
||||||
|
|
||||||
ctx->fixupHierarchy();
|
ctx->fixupHierarchy();
|
||||||
|
Loading…
Reference in New Issue
Block a user