Gowin. Implement MIPI IO.
Adds output (MIPI_OBUF and MIPI_OBUF_A) and input (MIPI_IBUF) primitives to allow the use of “real” MIPI (not emulation) ports capable of operating in both HS and LP modes. Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
parent
1623243d50
commit
17089994f1
@ -1313,6 +1313,16 @@ X(LSREN)
|
|||||||
// EMCU
|
// EMCU
|
||||||
X(EMCU)
|
X(EMCU)
|
||||||
|
|
||||||
|
// MIPI
|
||||||
|
X(IL)
|
||||||
|
X(OH)
|
||||||
|
X(OL)
|
||||||
|
X(OENB)
|
||||||
|
X(MIPI_IBUF)
|
||||||
|
X(MIPI_OBUF)
|
||||||
|
X(MIPI_OBUF_A)
|
||||||
|
X(MODESEL)
|
||||||
|
|
||||||
// Register placement options
|
// Register placement options
|
||||||
X(IREG_IN_IOB)
|
X(IREG_IN_IOB)
|
||||||
X(OREG_IN_IOB)
|
X(OREG_IN_IOB)
|
||||||
|
@ -243,6 +243,10 @@ struct GowinGlobalRouter
|
|||||||
bool driver_is_dqce(const PortRef &driver) { return CellTypePort(driver) == CellTypePort(id_DQCE, id_CLKOUT); }
|
bool driver_is_dqce(const PortRef &driver) { return CellTypePort(driver) == CellTypePort(id_DQCE, id_CLKOUT); }
|
||||||
bool driver_is_dcs(const PortRef &driver) { return CellTypePort(driver) == CellTypePort(id_DCS, id_CLKOUT); }
|
bool driver_is_dcs(const PortRef &driver) { return CellTypePort(driver) == CellTypePort(id_DCS, id_CLKOUT); }
|
||||||
bool driver_is_dhcen(const PortRef &driver) { return CellTypePort(driver) == CellTypePort(id_DHCEN, id_CLKOUT); }
|
bool driver_is_dhcen(const PortRef &driver) { return CellTypePort(driver) == CellTypePort(id_DHCEN, id_CLKOUT); }
|
||||||
|
bool driver_is_mipi(const PortRef &driver)
|
||||||
|
{
|
||||||
|
return CellTypePort(driver) == CellTypePort(id_IOBUF, id_O) && driver.cell->params.count(id_MIPI_IBUF);
|
||||||
|
}
|
||||||
bool driver_is_clksrc(const PortRef &driver)
|
bool driver_is_clksrc(const PortRef &driver)
|
||||||
{
|
{
|
||||||
// dedicated pins
|
// dedicated pins
|
||||||
@ -506,7 +510,7 @@ struct GowinGlobalRouter
|
|||||||
NPNR_ASSERT(net_before_dhcen != nullptr);
|
NPNR_ASSERT(net_before_dhcen != nullptr);
|
||||||
|
|
||||||
PortRef driver = net_before_dhcen->driver;
|
PortRef driver = net_before_dhcen->driver;
|
||||||
NPNR_ASSERT_MSG(driver_is_buf(driver) || driver_is_clksrc(driver),
|
NPNR_ASSERT_MSG(driver_is_buf(driver) || driver_is_clksrc(driver) || driver_is_mipi(driver),
|
||||||
stringf("The input source for %s is not a clock.", ctx->nameOf(dhcen_ci)).c_str());
|
stringf("The input source for %s is not a clock.", ctx->nameOf(dhcen_ci)).c_str());
|
||||||
|
|
||||||
IdString port;
|
IdString port;
|
||||||
@ -519,8 +523,14 @@ struct GowinGlobalRouter
|
|||||||
WireId src = ctx->getBelPinWire(driver.cell->bel, port);
|
WireId src = ctx->getBelPinWire(driver.cell->bel, port);
|
||||||
|
|
||||||
std::vector<PipId> path;
|
std::vector<PipId> path;
|
||||||
RouteResult route_result = route_direct_net(
|
RouteResult route_result;
|
||||||
net, [&](PipId pip, WireId src_wire) { return global_pip_filter(pip, src); }, src, &path);
|
if (driver_is_mipi(driver)) {
|
||||||
|
route_result = route_direct_net(net, [&](PipId pip, WireId src_wire) { return true; }, src, &path);
|
||||||
|
} else {
|
||||||
|
route_result = route_direct_net(
|
||||||
|
net, [&](PipId pip, WireId src_wire) { return global_pip_filter(pip, src); }, src, &path);
|
||||||
|
}
|
||||||
|
|
||||||
if (route_result == NOT_ROUTED) {
|
if (route_result == NOT_ROUTED) {
|
||||||
log_error("Can't route the %s network.\n", ctx->nameOf(net));
|
log_error("Can't route the %s network.\n", ctx->nameOf(net));
|
||||||
}
|
}
|
||||||
@ -557,6 +567,9 @@ struct GowinGlobalRouter
|
|||||||
hw_dhcen->setAttr(id_DHCEN_USED, 1);
|
hw_dhcen->setAttr(id_DHCEN_USED, 1);
|
||||||
dhcen_ci->copyPortTo(id_CE, hw_dhcen, id_CE);
|
dhcen_ci->copyPortTo(id_CE, hw_dhcen, id_CE);
|
||||||
}
|
}
|
||||||
|
if (driver_is_mipi(driver)) {
|
||||||
|
ctx->bindWire(src, net_before_dhcen, STRENGTH_LOCKED);
|
||||||
|
}
|
||||||
|
|
||||||
// connect all users to upper level net
|
// connect all users to upper level net
|
||||||
std::vector<PortRef> users;
|
std::vector<PortRef> users;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#include <regex>
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
#include "himbaechel_api.h"
|
#include "himbaechel_api.h"
|
||||||
#include "himbaechel_helpers.h"
|
#include "himbaechel_helpers.h"
|
||||||
@ -110,7 +110,8 @@ struct GowinArch : HimbaechelArch
|
|||||||
|
|
||||||
bool match_device(const std::string &device) override { return device.size() > 2 && device.substr(0, 2) == "GW"; }
|
bool match_device(const std::string &device) override { return device.size() > 2 && device.substr(0, 2) == "GW"; }
|
||||||
|
|
||||||
std::unique_ptr<HimbaechelAPI> create(const std::string &device, const dict<std::string, std::string> &args) override
|
std::unique_ptr<HimbaechelAPI> create(const std::string &device,
|
||||||
|
const dict<std::string, std::string> &args) override
|
||||||
{
|
{
|
||||||
return std::make_unique<GowinImpl>();
|
return std::make_unique<GowinImpl>();
|
||||||
}
|
}
|
||||||
@ -639,6 +640,9 @@ IdString GowinImpl::getBelBucketForCellType(IdString cell_type) const
|
|||||||
if (cell_type.in(id_IBUF, id_OBUF)) {
|
if (cell_type.in(id_IBUF, id_OBUF)) {
|
||||||
return id_IOB;
|
return id_IOB;
|
||||||
}
|
}
|
||||||
|
if (cell_type.in(id_MIPI_OBUF, id_MIPI_OBUF_A)) {
|
||||||
|
return id_MIPI_OBUF;
|
||||||
|
}
|
||||||
if (type_is_lut(cell_type)) {
|
if (type_is_lut(cell_type)) {
|
||||||
return id_LUT4;
|
return id_LUT4;
|
||||||
}
|
}
|
||||||
@ -676,6 +680,9 @@ bool GowinImpl::isValidBelForCellType(IdString cell_type, BelId bel) const
|
|||||||
if (bel_type == id_IOB) {
|
if (bel_type == id_IOB) {
|
||||||
return cell_type.in(id_IBUF, id_OBUF);
|
return cell_type.in(id_IBUF, id_OBUF);
|
||||||
}
|
}
|
||||||
|
if (bel_type == id_MIPI_OBUF) {
|
||||||
|
return cell_type.in(id_MIPI_OBUF, id_MIPI_OBUF_A);
|
||||||
|
}
|
||||||
if (bel_type == id_LUT4) {
|
if (bel_type == id_LUT4) {
|
||||||
return type_is_lut(cell_type);
|
return type_is_lut(cell_type);
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,10 @@ inline bool type_is_diffio(IdString cell_type)
|
|||||||
}
|
}
|
||||||
inline bool is_diffio(const CellInfo *cell) { return type_is_diffio(cell->type); }
|
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); }
|
||||||
|
|
||||||
// IOLOGIC input and output separately
|
// IOLOGIC input and output separately
|
||||||
|
|
||||||
inline bool type_is_iologico(IdString cell_type)
|
inline bool type_is_iologico(IdString cell_type)
|
||||||
@ -199,6 +203,9 @@ enum
|
|||||||
|
|
||||||
EMCU_Z = 300,
|
EMCU_Z = 300,
|
||||||
|
|
||||||
|
MIPIOBUF_Z = 301,
|
||||||
|
MIPIIBUF_Z = 302,
|
||||||
|
|
||||||
// The two least significant bits encode Z for 9-bit adders and
|
// 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
|
// multipliers, if they are equal to 0, then we get Z of their common
|
||||||
// 18-bit equivalent.
|
// 18-bit equivalent.
|
||||||
|
@ -55,9 +55,11 @@ DHCEN_Z = 288 # : 298
|
|||||||
|
|
||||||
USERFLASH_Z = 298
|
USERFLASH_Z = 298
|
||||||
|
|
||||||
|
|
||||||
EMCU_Z = 300
|
EMCU_Z = 300
|
||||||
|
|
||||||
|
MIPIOBUF_Z = 301
|
||||||
|
MIPIIBUF_Z = 302
|
||||||
|
|
||||||
DSP_Z = 509
|
DSP_Z = 509
|
||||||
|
|
||||||
DSP_0_Z = 511 # DSP macro 0
|
DSP_0_Z = 511 # DSP macro 0
|
||||||
@ -580,6 +582,23 @@ def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int):
|
|||||||
tt.add_bel_pin(bel, port, wire, PinType.OUTPUT)
|
tt.add_bel_pin(bel, port, wire, PinType.OUTPUT)
|
||||||
else:
|
else:
|
||||||
tt.add_bel_pin(bel, port, wire, PinType.INPUT)
|
tt.add_bel_pin(bel, port, wire, PinType.INPUT)
|
||||||
|
elif func == 'mipi_obuf':
|
||||||
|
bel = tt.create_bel('MIPI_OBUF', 'MIPI_OBUF', MIPIOBUF_Z)
|
||||||
|
elif func == 'mipi_ibuf':
|
||||||
|
bel = tt.create_bel('MIPI_IBUF', 'MIPI_IBUF', MIPIIBUF_Z)
|
||||||
|
wire = desc['HSREN']
|
||||||
|
if not tt.has_wire(wire):
|
||||||
|
tt.create_wire(wire)
|
||||||
|
tt.add_bel_pin(bel, 'HSREN', wire, PinType.INPUT)
|
||||||
|
wire = 'MIPIOL'
|
||||||
|
if not tt.has_wire(wire):
|
||||||
|
tt.create_wire(wire)
|
||||||
|
tt.add_bel_pin(bel, 'OL', wire, PinType.OUTPUT)
|
||||||
|
for i in range(2):
|
||||||
|
wire = f'MIPIEN{i}'
|
||||||
|
if not tt.has_wire(wire):
|
||||||
|
tt.create_wire(wire)
|
||||||
|
tt.add_bel_pin(bel, f'MIPIEN{i}', wire, PinType.INPUT)
|
||||||
elif func == 'buf':
|
elif func == 'buf':
|
||||||
for buf_type, wires in desc.items():
|
for buf_type, wires in desc.items():
|
||||||
for i, wire in enumerate(wires):
|
for i, wire in enumerate(wires):
|
||||||
|
@ -266,6 +266,14 @@ struct GowinPacker
|
|||||||
iob_n->disconnectPort(id_OEN);
|
iob_n->disconnectPort(id_OEN);
|
||||||
iob_p->disconnectPort(id_OEN);
|
iob_p->disconnectPort(id_OEN);
|
||||||
ci.movePortTo(id_OEN, iob_p, id_OEN);
|
ci.movePortTo(id_OEN, iob_p, id_OEN);
|
||||||
|
|
||||||
|
// MIPI
|
||||||
|
if (ci.params.count(id_MIPI_OBUF)) {
|
||||||
|
iob_p->setParam(id_MIPI_OBUF, 1);
|
||||||
|
iob_n->setParam(id_MIPI_OBUF, 1);
|
||||||
|
ci.movePortTo(id_IB, iob_n, id_I);
|
||||||
|
iob_p->copyPortTo(id_OEN, iob_n, id_OEN);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
iob_p->disconnectPort(id_I);
|
iob_p->disconnectPort(id_I);
|
||||||
ci.movePortTo(id_I, iob_p, id_I);
|
ci.movePortTo(id_I, iob_p, id_I);
|
||||||
@ -300,6 +308,152 @@ struct GowinPacker
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ===================================
|
||||||
|
// MIPI IO
|
||||||
|
// ===================================
|
||||||
|
void pack_mipi(void)
|
||||||
|
{
|
||||||
|
log_info("Pack MIPI IOs...\n");
|
||||||
|
std::vector<std::unique_ptr<CellInfo>> new_cells;
|
||||||
|
|
||||||
|
for (auto &cell : ctx->cells) {
|
||||||
|
CellInfo &ci = *cell.second;
|
||||||
|
if (!is_mipi(&ci)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch (ci.type.hash()) {
|
||||||
|
case ID_MIPI_OBUF_A: /* fallt-hrough */
|
||||||
|
case ID_MIPI_OBUF: {
|
||||||
|
// check for MIPI-capable pin
|
||||||
|
CellInfo *out_iob = net_only_drives(ctx, ci.ports.at(id_O).net, is_iob, id_I, true);
|
||||||
|
if (out_iob == nullptr || out_iob->bel == BelId()) {
|
||||||
|
log_error("MIPI %s is not connected to the output pin or the pin is not constrained.\n",
|
||||||
|
ctx->nameOf(&ci));
|
||||||
|
}
|
||||||
|
BelId iob_bel = out_iob->bel;
|
||||||
|
Loc iob_loc = ctx->getBelLocation(iob_bel);
|
||||||
|
iob_loc.z = BelZ::MIPIOBUF_Z;
|
||||||
|
BelId mipi_bel = ctx->getBelByLocation(iob_loc);
|
||||||
|
if (mipi_bel == BelId()) {
|
||||||
|
log_error("Can't place MIPI %s at X%dY%d/IOBA.\n", ctx->nameOf(&ci), iob_loc.x, iob_loc.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ci.type == id_MIPI_OBUF_A) {
|
||||||
|
// if serialization is used then IL and input of serializator must be in the same network
|
||||||
|
NetInfo *i_net = ci.getPort(id_I);
|
||||||
|
NetInfo *il_net = ci.getPort(id_IL);
|
||||||
|
if (i_net != il_net) {
|
||||||
|
if (i_net != nullptr && is_iologico(i_net->driver.cell)) {
|
||||||
|
if (i_net->driver.cell->getPort(id_D0) != ci.getPort(id_IL)) {
|
||||||
|
log_error("MIPI %s port IL and IOLOGIC %s port D0 are in differrent networks!\n",
|
||||||
|
ctx->nameOf(&ci), ctx->nameOf(i_net->driver.cell));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log_error("MIPI %s ports IL and I are in differrent networks!\n", ctx->nameOf(&ci));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ci.disconnectPort(id_IL);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->bindBel(mipi_bel, &ci, PlaceStrength::STRENGTH_LOCKED);
|
||||||
|
|
||||||
|
// Create TBUF with additional input IB
|
||||||
|
IdString mipi_tbuf_name = gwu.create_aux_name(ci.name);
|
||||||
|
new_cells.push_back(gwu.create_cell(mipi_tbuf_name, id_TLVDS_TBUF));
|
||||||
|
|
||||||
|
CellInfo *mipi_tbuf = new_cells.back().get();
|
||||||
|
mipi_tbuf->addInput(id_I);
|
||||||
|
mipi_tbuf->addInput(id_IB);
|
||||||
|
mipi_tbuf->addOutput(id_O);
|
||||||
|
mipi_tbuf->addOutput(id_OB);
|
||||||
|
mipi_tbuf->addInput(id_OEN);
|
||||||
|
ci.movePortTo(id_I, mipi_tbuf, id_I);
|
||||||
|
ci.movePortTo(id_IB, mipi_tbuf, id_IB);
|
||||||
|
ci.movePortTo(id_O, mipi_tbuf, id_O);
|
||||||
|
ci.movePortTo(id_OB, mipi_tbuf, id_OB);
|
||||||
|
ci.movePortTo(id_MODESEL, mipi_tbuf, id_OEN);
|
||||||
|
|
||||||
|
mipi_tbuf->setParam(id_MIPI_OBUF, 1);
|
||||||
|
} break;
|
||||||
|
case ID_MIPI_IBUF: {
|
||||||
|
// check for MIPI-capable pin A
|
||||||
|
CellInfo *in_iob = net_only_drives(ctx, ci.ports.at(id_IO).net, is_iob, id_I);
|
||||||
|
if (in_iob == nullptr || in_iob->bel == BelId()) {
|
||||||
|
log_error("MIPI %s IO is not connected to the input pin or the pin is not constrained.\n",
|
||||||
|
ctx->nameOf(&ci));
|
||||||
|
}
|
||||||
|
// check A IO placing
|
||||||
|
BelId iob_bel = in_iob->bel;
|
||||||
|
Loc iob_loc = ctx->getBelLocation(iob_bel);
|
||||||
|
if (iob_loc.z != BelZ::IOBA_Z) {
|
||||||
|
log_error("MIPI %s IO pin must be connected to the A IO pin.\n", ctx->nameOf(&ci));
|
||||||
|
}
|
||||||
|
|
||||||
|
iob_loc.z = BelZ::MIPIIBUF_Z;
|
||||||
|
BelId mipi_bel = ctx->getBelByLocation(iob_loc);
|
||||||
|
if (mipi_bel == BelId()) {
|
||||||
|
log_error("Can't place MIPI %s at X%dY%d/IOBA.\n", ctx->nameOf(&ci), iob_loc.x, iob_loc.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for MIPI-capable pin B
|
||||||
|
CellInfo *inb_iob = net_only_drives(ctx, ci.ports.at(id_IOB).net, is_iob, id_I);
|
||||||
|
if (inb_iob == nullptr || inb_iob->bel == BelId()) {
|
||||||
|
log_error("MIPI %s IOB is not connected to the input pin or the pin is not constrained.\n",
|
||||||
|
ctx->nameOf(&ci));
|
||||||
|
}
|
||||||
|
// check B IO placing
|
||||||
|
BelId iobb_bel = inb_iob->bel;
|
||||||
|
Loc iobb_loc = ctx->getBelLocation(iobb_bel);
|
||||||
|
if (iobb_loc.z != BelZ::IOBB_Z || iobb_loc.x != iob_loc.x || iobb_loc.y != iob_loc.y) {
|
||||||
|
log_error("MIPI %s IOB pin must be connected to the B IO pin.\n", ctx->nameOf(&ci));
|
||||||
|
}
|
||||||
|
// MIPI IBUF uses next pair of IOs too
|
||||||
|
Loc iob_next_loc(iob_loc);
|
||||||
|
++iob_next_loc.x;
|
||||||
|
iob_next_loc.z = BelZ::IOBA_Z;
|
||||||
|
CellInfo *inc_iob = ctx->getBoundBelCell(ctx->getBelByLocation(iob_next_loc));
|
||||||
|
iob_next_loc.z = BelZ::IOBB_Z;
|
||||||
|
CellInfo *other_cell_b = ctx->getBoundBelCell(ctx->getBelByLocation(iob_next_loc));
|
||||||
|
if (inc_iob != nullptr || other_cell_b != nullptr) {
|
||||||
|
log_error("MIPI %s cannot be placed in same IO with %s.\n", ctx->nameOf(&ci),
|
||||||
|
inc_iob == nullptr ? ctx->nameOf(other_cell_b) : ctx->nameOf(inc_iob));
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->bindBel(mipi_bel, &ci, PlaceStrength::STRENGTH_LOCKED);
|
||||||
|
|
||||||
|
// reconnect wires
|
||||||
|
// A
|
||||||
|
ci.disconnectPort(id_IO);
|
||||||
|
in_iob->disconnectPort(id_I);
|
||||||
|
ci.movePortTo(id_I, in_iob, id_I);
|
||||||
|
ci.movePortTo(id_OH, in_iob, id_O);
|
||||||
|
in_iob->disconnectPort(id_OEN);
|
||||||
|
ci.movePortTo(id_OEN, in_iob, id_OEN);
|
||||||
|
// B
|
||||||
|
ci.disconnectPort(id_IO);
|
||||||
|
inb_iob->disconnectPort(id_I);
|
||||||
|
ci.movePortTo(id_IB, inb_iob, id_I);
|
||||||
|
ci.movePortTo(id_OB, inb_iob, id_O);
|
||||||
|
inb_iob->disconnectPort(id_OEN);
|
||||||
|
ci.movePortTo(id_OENB, inb_iob, id_OEN);
|
||||||
|
// MIPI enable (?)
|
||||||
|
ci.addInput(ctx->id("MIPIEN0"));
|
||||||
|
ci.connectPort(ctx->id("MIPIEN0"), ctx->nets.at(ctx->id("$PACKER_GND")).get());
|
||||||
|
ci.addInput(ctx->id("MIPIEN1"));
|
||||||
|
ci.connectPort(ctx->id("MIPIEN1"), ctx->nets.at(ctx->id("$PACKER_VCC")).get());
|
||||||
|
|
||||||
|
in_iob->setParam(id_MIPI_IBUF, 1);
|
||||||
|
inb_iob->setParam(id_MIPI_IBUF, 1);
|
||||||
|
} break;
|
||||||
|
default:
|
||||||
|
log_error("MIPI %s is not implemented.\n", ci.type.c_str(ctx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto &ncell : new_cells) {
|
||||||
|
ctx->cells[ncell->name] = std::move(ncell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void pack_diff_iobs(void)
|
void pack_diff_iobs(void)
|
||||||
{
|
{
|
||||||
log_info("Pack diff IOBs...\n");
|
log_info("Pack diff IOBs...\n");
|
||||||
@ -432,7 +586,8 @@ struct GowinPacker
|
|||||||
}
|
}
|
||||||
// mark IOB as used by IOLOGIC
|
// mark IOB as used by IOLOGIC
|
||||||
out_iob->setParam(id_IOLOGIC_IOB, 1);
|
out_iob->setParam(id_IOLOGIC_IOB, 1);
|
||||||
check_iologic_placement(ci, ctx->getBelLocation(iob_bel), out_iob->params.count(id_DIFF_TYPE));
|
check_iologic_placement(ci, ctx->getBelLocation(iob_bel),
|
||||||
|
out_iob->params.count(id_DIFF_TYPE) || out_iob->params.count(id_MIPI_OBUF));
|
||||||
|
|
||||||
if (!ctx->checkBelAvail(l_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),
|
log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci),
|
||||||
@ -471,6 +626,20 @@ struct GowinPacker
|
|||||||
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);
|
||||||
|
} else { // disconnect TXx ports, ignore these nets
|
||||||
|
switch (ci.type.hash()) {
|
||||||
|
case ID_OSER8:
|
||||||
|
ci.disconnectPort(id_TX3);
|
||||||
|
ci.disconnectPort(id_TX2); /* fall-through */
|
||||||
|
case ID_OSER4:
|
||||||
|
ci.disconnectPort(id_TX1);
|
||||||
|
ci.disconnectPort(id_TX0);
|
||||||
|
break;
|
||||||
|
case ID_ODDR: /* fall-through */
|
||||||
|
case ID_ODDRC: /* fall-through */
|
||||||
|
ci.disconnectPort(id_TX);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
make_iob_nets(*out_iob);
|
make_iob_nets(*out_iob);
|
||||||
}
|
}
|
||||||
@ -489,7 +658,8 @@ struct GowinPacker
|
|||||||
}
|
}
|
||||||
// mark IOB as used by IOLOGIC
|
// mark IOB as used by IOLOGIC
|
||||||
out_iob->setParam(id_IOLOGIC_IOB, 1);
|
out_iob->setParam(id_IOLOGIC_IOB, 1);
|
||||||
check_iologic_placement(ci, ctx->getBelLocation(iob_bel), out_iob->params.count(id_DIFF_TYPE));
|
check_iologic_placement(ci, ctx->getBelLocation(iob_bel),
|
||||||
|
out_iob->params.count(id_DIFF_TYPE) || out_iob->params.count(id_MIPI_OBUF));
|
||||||
|
|
||||||
if (!ctx->checkBelAvail(l_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),
|
log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci),
|
||||||
@ -534,6 +704,11 @@ struct GowinPacker
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool is_diff_io(BelId bel) { return ctx->getBoundBelCell(bel)->params.count(id_DIFF_TYPE) != 0; }
|
bool is_diff_io(BelId bel) { return ctx->getBoundBelCell(bel)->params.count(id_DIFF_TYPE) != 0; }
|
||||||
|
bool is_mipi_io(BelId bel)
|
||||||
|
{
|
||||||
|
return ctx->getBoundBelCell(bel)->params.count(id_MIPI_IBUF) ||
|
||||||
|
ctx->getBoundBelCell(bel)->params.count(id_MIPI_OBUF);
|
||||||
|
}
|
||||||
|
|
||||||
CellInfo *create_aux_iologic_cell(CellInfo &ci, IdString mode, bool io16 = false, int idx = 0)
|
CellInfo *create_aux_iologic_cell(CellInfo &ci, IdString mode, bool io16 = false, int idx = 0)
|
||||||
{
|
{
|
||||||
@ -545,7 +720,7 @@ struct GowinPacker
|
|||||||
BelId bel = get_aux_iologic_bel(ci);
|
BelId bel = get_aux_iologic_bel(ci);
|
||||||
BelId io_bel = gwu.get_io_bel_from_iologic(bel);
|
BelId io_bel = gwu.get_io_bel_from_iologic(bel);
|
||||||
if (!ctx->checkBelAvail(io_bel)) {
|
if (!ctx->checkBelAvail(io_bel)) {
|
||||||
if (!is_diff_io(io_bel)) {
|
if (!(is_diff_io(io_bel) || is_mipi_io(io_bel))) {
|
||||||
log_error("Can't place %s at %s because of a conflict with another IO %s\n", ctx->nameOf(&ci),
|
log_error("Can't place %s at %s because of a conflict with another IO %s\n", ctx->nameOf(&ci),
|
||||||
ctx->nameOfBel(bel), ctx->nameOf(ctx->getBoundBelCell(io_bel)));
|
ctx->nameOfBel(bel), ctx->nameOf(ctx->getBoundBelCell(io_bel)));
|
||||||
}
|
}
|
||||||
@ -609,7 +784,8 @@ struct GowinPacker
|
|||||||
}
|
}
|
||||||
// mark IOB as used by IOLOGIC
|
// mark IOB as used by IOLOGIC
|
||||||
in_iob->setParam(id_IOLOGIC_IOB, 1);
|
in_iob->setParam(id_IOLOGIC_IOB, 1);
|
||||||
check_iologic_placement(ci, ctx->getBelLocation(iob_bel), in_iob->params.count(id_DIFF_TYPE));
|
check_iologic_placement(ci, ctx->getBelLocation(iob_bel),
|
||||||
|
in_iob->params.count(id_DIFF_TYPE) || in_iob->params.count(id_MIPI_IBUF));
|
||||||
|
|
||||||
if (!ctx->checkBelAvail(l_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),
|
log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci),
|
||||||
@ -3932,6 +4108,9 @@ struct GowinPacker
|
|||||||
pack_iobs();
|
pack_iobs();
|
||||||
ctx->check();
|
ctx->check();
|
||||||
|
|
||||||
|
pack_mipi();
|
||||||
|
ctx->check();
|
||||||
|
|
||||||
pack_diff_iobs();
|
pack_diff_iobs();
|
||||||
ctx->check();
|
ctx->check();
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user