Gowin. Add DHCEN primitive. (#1349)
* Gowin. Add DHCEN primitive. This primitive allows you to dynamically turn off and turn on the networks of high-speed clocks. This is done tracking the routes to the sinks and if the route passes through a special HCLK MUX (this may be the input MUX or the output MUX, as well as the interbank MUX), then the control signal of this MUX is used. Signed-off-by: YRabbit <rabbit@yrabbit.cyou> * Gowin. Change the DHCEN binding Use the entire PIP instead of a wire - avoids normalisation and may also be useful in the future when calculating clock stuff. Signed-off-by: YRabbit <rabbit@yrabbit.cyou> --------- Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
parent
8d0f52fbf9
commit
ff7b8535bc
@ -1269,10 +1269,12 @@ X(BUFG)
|
||||
X(CLOCK)
|
||||
X(DQCE)
|
||||
X(DCS)
|
||||
X(DCS_MODE)
|
||||
X(DQCE_PIP)
|
||||
X(DHCEN_USED)
|
||||
X(DCS_USED)
|
||||
X(SELFORCE)
|
||||
X(DHCEN)
|
||||
X(DCS_MODE)
|
||||
|
||||
//HCLK Bels
|
||||
X(CLKDIV)
|
||||
|
@ -134,7 +134,8 @@ struct GowinGlobalRouter
|
||||
|
||||
// Dedicated backwards BFS routing for global networks
|
||||
template <typename Tfilt>
|
||||
bool backwards_bfs_route(NetInfo *net, WireId src, WireId dst, int iter_limit, bool strict, Tfilt pip_filter)
|
||||
bool backwards_bfs_route(NetInfo *net, WireId src, WireId dst, int iter_limit, bool strict, Tfilt pip_filter,
|
||||
std::vector<PipId> *path = nullptr)
|
||||
{
|
||||
// Queue of wires to visit
|
||||
std::queue<WireId> visit;
|
||||
@ -208,6 +209,9 @@ struct GowinGlobalRouter
|
||||
break;
|
||||
}
|
||||
ctx->bindPip(pip, net, STRENGTH_LOCKED);
|
||||
if (path != nullptr) {
|
||||
path->push_back(pip);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
@ -225,6 +229,7 @@ struct GowinGlobalRouter
|
||||
bool driver_is_buf(const PortRef &driver) { return CellTypePort(driver) == CellTypePort(id_BUFG, id_O); }
|
||||
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_dhcen(const PortRef &driver) { return CellTypePort(driver) == CellTypePort(id_DHCEN, id_CLKOUT); }
|
||||
bool driver_is_clksrc(const PortRef &driver)
|
||||
{
|
||||
// dedicated pins
|
||||
@ -276,7 +281,9 @@ struct GowinGlobalRouter
|
||||
ROUTED_ALL
|
||||
};
|
||||
|
||||
RouteResult route_direct_net(NetInfo *net, WireId aux_src = WireId(), bool DCS_pips = false, bool DQCE_pips = false)
|
||||
template <typename Tfilter>
|
||||
RouteResult route_direct_net(NetInfo *net, Tfilter pip_filter, WireId aux_src = WireId(),
|
||||
std::vector<PipId> *path = nullptr)
|
||||
{
|
||||
WireId src;
|
||||
src = aux_src == WireId() ? ctx->getNetinfoSourceWire(net) : aux_src;
|
||||
@ -297,21 +304,9 @@ struct GowinGlobalRouter
|
||||
ctx->nameOf(usr.port));
|
||||
}
|
||||
bool bfs_res;
|
||||
if (DCS_pips) {
|
||||
bfs_res = backwards_bfs_route(net, src, dst, 1000000, false, [&](PipId pip) {
|
||||
return (is_relaxed_sink(usr) || global_DCS_pip_filter(pip));
|
||||
});
|
||||
} else {
|
||||
if (DQCE_pips) {
|
||||
bfs_res = backwards_bfs_route(net, src, dst, 1000000, false, [&](PipId pip) {
|
||||
return (is_relaxed_sink(usr) || global_DQCE_pip_filter(pip));
|
||||
});
|
||||
} else {
|
||||
bfs_res = backwards_bfs_route(net, src, dst, 1000000, false, [&](PipId pip) {
|
||||
return (is_relaxed_sink(usr) || global_pip_filter(pip));
|
||||
});
|
||||
}
|
||||
}
|
||||
bfs_res = backwards_bfs_route(
|
||||
net, src, dst, 1000000, false, [&](PipId pip) { return (is_relaxed_sink(usr) || pip_filter(pip)); },
|
||||
path);
|
||||
if (bfs_res) {
|
||||
routed = routed == ROUTED_PARTIALLY ? routed : ROUTED_ALL;
|
||||
} else {
|
||||
@ -345,7 +340,8 @@ struct GowinGlobalRouter
|
||||
src = ctx->getBelPinWire(driver.cell->bel, driver.port);
|
||||
}
|
||||
|
||||
RouteResult route_result = route_direct_net(net, src, false, true);
|
||||
RouteResult route_result = route_direct_net(
|
||||
net, [&](PipId pip) { return global_DQCE_pip_filter(pip); }, src);
|
||||
if (route_result == NOT_ROUTED) {
|
||||
log_error("Can't route the %s network.\n", ctx->nameOf(net));
|
||||
}
|
||||
@ -422,7 +418,8 @@ struct GowinGlobalRouter
|
||||
src = ctx->getBelPinWire(driver.cell->bel, driver.port);
|
||||
}
|
||||
|
||||
RouteResult route_result = route_direct_net(net, src, true);
|
||||
RouteResult route_result = route_direct_net(
|
||||
net, [&](PipId pip) { return global_DCS_pip_filter(pip); }, src);
|
||||
if (route_result == NOT_ROUTED) {
|
||||
log_error("Can't route the %s network.\n", ctx->nameOf(net));
|
||||
}
|
||||
@ -487,6 +484,84 @@ struct GowinGlobalRouter
|
||||
ctx->cells.erase(dcs_ci->name);
|
||||
}
|
||||
|
||||
void route_dhcen_net(NetInfo *net)
|
||||
{
|
||||
// route net after dhcen source of CLKIN net
|
||||
CellInfo *dhcen_ci = net->driver.cell;
|
||||
|
||||
NetInfo *net_before_dhcen = dhcen_ci->getPort(id_CLKIN);
|
||||
NPNR_ASSERT(net_before_dhcen != nullptr);
|
||||
|
||||
PortRef driver = net_before_dhcen->driver;
|
||||
NPNR_ASSERT_MSG(driver_is_buf(driver) || driver_is_clksrc(driver),
|
||||
stringf("The input source for %s is not a clock.", ctx->nameOf(dhcen_ci)).c_str());
|
||||
|
||||
IdString port;
|
||||
// use BUF input if there is one
|
||||
if (driver_is_buf(driver)) {
|
||||
port = id_I;
|
||||
} else {
|
||||
port = driver.port;
|
||||
}
|
||||
WireId src = ctx->getBelPinWire(driver.cell->bel, port);
|
||||
|
||||
std::vector<PipId> path;
|
||||
RouteResult route_result = route_direct_net(
|
||||
net, [&](PipId pip) { return global_pip_filter(pip); }, src, &path);
|
||||
if (route_result == NOT_ROUTED) {
|
||||
log_error("Can't route the %s network.\n", ctx->nameOf(net));
|
||||
}
|
||||
if (route_result == ROUTED_PARTIALLY) {
|
||||
log_error("It was not possible to completely route the %s net using only global resources. This is not "
|
||||
"allowed for dhcen managed networks.\n",
|
||||
ctx->nameOf(net));
|
||||
}
|
||||
|
||||
// In networks controlled by dhcen we disable/enable only HCLK - if
|
||||
// there are ordinary cells among the sinks, then they are not affected
|
||||
// by this primitive.
|
||||
for (PipId pip : path) {
|
||||
// move to upper level net
|
||||
ctx->unbindPip(pip);
|
||||
ctx->bindPip(pip, net_before_dhcen, STRENGTH_LOCKED);
|
||||
|
||||
WireId dst = ctx->getPipDstWire(pip);
|
||||
IdString side;
|
||||
BelId dhcen_bel = gwu.get_dhcen_bel(dst, side);
|
||||
if (dhcen_bel == BelId()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// One pseudo dhcen can be implemented as several hardware dhcen.
|
||||
// Here we find suitable hardware dhcens.
|
||||
CellInfo *hw_dhcen = ctx->getBoundBelCell(dhcen_bel);
|
||||
if (ctx->debug) {
|
||||
log_info(" use %s wire and %s bel for '%s' hw cell.\n", ctx->nameOfWire(dst),
|
||||
ctx->nameOfBel(dhcen_bel), ctx->nameOf(hw_dhcen));
|
||||
}
|
||||
|
||||
// The control network must connect the CE inputs of all hardware dhcens.
|
||||
hw_dhcen->setAttr(id_DHCEN_USED, 1);
|
||||
dhcen_ci->copyPortTo(id_CE, hw_dhcen, id_CE);
|
||||
}
|
||||
|
||||
// connect all users to upper level net
|
||||
std::vector<PortRef> users;
|
||||
for (auto &cell_port : net->users) {
|
||||
users.push_back(cell_port);
|
||||
}
|
||||
for (PortRef &user : users) {
|
||||
user.cell->disconnectPort(user.port);
|
||||
user.cell->connectPort(user.port, net_before_dhcen);
|
||||
}
|
||||
|
||||
// remove the virtual dhcen
|
||||
dhcen_ci->disconnectPort(id_CLKOUT);
|
||||
dhcen_ci->disconnectPort(id_CLKIN);
|
||||
dhcen_ci->disconnectPort(id_CE);
|
||||
ctx->cells.erase(dhcen_ci->name);
|
||||
}
|
||||
|
||||
void route_buffered_net(NetInfo *net)
|
||||
{
|
||||
// a) route net after buf using the buf input as source
|
||||
@ -496,7 +571,8 @@ struct GowinGlobalRouter
|
||||
NetInfo *net_before_buf = buf_ci->getPort(id_I);
|
||||
NPNR_ASSERT(net_before_buf != nullptr);
|
||||
|
||||
RouteResult route_result = route_direct_net(net, src);
|
||||
RouteResult route_result = route_direct_net(
|
||||
net, [&](PipId pip) { return global_pip_filter(pip); }, src);
|
||||
if (route_result == NOT_ROUTED || route_result == ROUTED_PARTIALLY) {
|
||||
log_error("Can't route the %s net. It might be worth removing the BUFG buffer flag.\n", ctx->nameOf(net));
|
||||
}
|
||||
@ -516,7 +592,7 @@ struct GowinGlobalRouter
|
||||
|
||||
void route_clk_net(NetInfo *net)
|
||||
{
|
||||
RouteResult route_result = route_direct_net(net);
|
||||
RouteResult route_result = route_direct_net(net, [&](PipId pip) { return global_pip_filter(pip); });
|
||||
if (route_result != NOT_ROUTED) {
|
||||
log_info(" '%s' net was routed using global resources %s.\n", ctx->nameOf(net),
|
||||
route_result == ROUTED_ALL ? "only" : "partially");
|
||||
@ -527,7 +603,7 @@ struct GowinGlobalRouter
|
||||
{
|
||||
log_info("Routing globals...\n");
|
||||
|
||||
std::vector<IdString> dqce_nets, dcs_nets, buf_nets, clk_nets;
|
||||
std::vector<IdString> dhcen_nets, dqce_nets, dcs_nets, buf_nets, clk_nets;
|
||||
|
||||
// Determining the priority of network routing
|
||||
for (auto &net : ctx->nets) {
|
||||
@ -550,12 +626,25 @@ struct GowinGlobalRouter
|
||||
} else {
|
||||
if (driver_is_dcs(ni->driver)) {
|
||||
dcs_nets.push_back(net.first);
|
||||
} else {
|
||||
if (driver_is_dhcen(ni->driver)) {
|
||||
dhcen_nets.push_back(net.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nets with DHCEN
|
||||
for (IdString net_name : dhcen_nets) {
|
||||
NetInfo *ni = ctx->nets.at(net_name).get();
|
||||
if (ctx->verbose) {
|
||||
log_info("route dhcen net '%s'\n", ctx->nameOf(ni));
|
||||
}
|
||||
route_dhcen_net(ni);
|
||||
}
|
||||
|
||||
// nets with DQCE
|
||||
for (IdString net_name : dqce_nets) {
|
||||
NetInfo *ni = ctx->nets.at(net_name).get();
|
||||
|
@ -116,12 +116,23 @@ NPNR_PACKED_STRUCT(struct Spine_bel_POD {
|
||||
int32_t bel_z;
|
||||
});
|
||||
|
||||
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 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;
|
||||
// chip flags
|
||||
static constexpr int32_t HAS_SP32 = 1;
|
||||
static constexpr int32_t NEED_SP_FIX = 2;
|
||||
@ -162,10 +173,12 @@ enum
|
||||
VSS_Z = 278,
|
||||
BANDGAP_Z = 279,
|
||||
|
||||
DQCE_Z = 280, // : 286 reserve for 6 DQCEs
|
||||
DCS_Z = 286, // : 288 reserve for 2 DCSs
|
||||
|
||||
USERFLASH_Z = 288,
|
||||
DQCE_Z = 280, // : 286 reserve for 6 DQCEs
|
||||
DCS_Z = 286, // : 288 reserve for 2 DCSs
|
||||
DHCEN_Z = 288, // : 298
|
||||
|
||||
USERFLASH_Z = 298,
|
||||
|
||||
// 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
|
||||
|
@ -51,8 +51,10 @@ 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
|
||||
|
||||
USERFLASH_Z = 288
|
||||
|
||||
DSP_Z = 509
|
||||
|
||||
@ -167,6 +169,28 @@ class SpineBel(BBAStruct):
|
||||
bba.u32(self.bel_y)
|
||||
bba.u32(self.bel_z)
|
||||
|
||||
# wire -> bel for DHCEN bels
|
||||
@dataclass
|
||||
class WireBel(BBAStruct):
|
||||
pip_xy: IdString
|
||||
pip_dst: IdString
|
||||
pip_src: IdString
|
||||
bel_x: int
|
||||
bel_y: int
|
||||
bel_z: int
|
||||
hclk_side: IdString
|
||||
|
||||
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||
pass
|
||||
def serialise(self, context: str, bba: BBAWriter):
|
||||
bba.u32(self.pip_xy.index)
|
||||
bba.u32(self.pip_dst.index)
|
||||
bba.u32(self.pip_src.index)
|
||||
bba.u32(self.bel_x)
|
||||
bba.u32(self.bel_y)
|
||||
bba.u32(self.bel_z)
|
||||
bba.u32(self.hclk_side.index)
|
||||
|
||||
@dataclass
|
||||
class ChipExtraData(BBAStruct):
|
||||
strs: StringPool
|
||||
@ -175,6 +199,7 @@ class ChipExtraData(BBAStruct):
|
||||
diff_io_types: list[IdString] = field(default_factory = list)
|
||||
dqce_bels: list[SpineBel] = field(default_factory = list)
|
||||
dcs_bels: list[SpineBel] = field(default_factory = list)
|
||||
dhcen_bels: list[WireBel] = field(default_factory = list)
|
||||
|
||||
def create_bottom_io(self):
|
||||
self.bottom_io = BottomIO()
|
||||
@ -185,6 +210,9 @@ class ChipExtraData(BBAStruct):
|
||||
def add_diff_io_type(self, diff_type: str):
|
||||
self.diff_io_types.append(self.strs.id(diff_type))
|
||||
|
||||
def add_dhcen_bel(self, pip_xy: str, pip_dst: str, pip_src, x: int, y: int, z: int, side: str):
|
||||
self.dhcen_bels.append(WireBel(self.strs.id(pip_xy), self.strs.id(pip_dst), self.strs.id(pip_src), x, y, z, self.strs.id(side)))
|
||||
|
||||
def add_dqce_bel(self, spine: str, x: int, y: int, z: int):
|
||||
self.dqce_bels.append(SpineBel(self.strs.id(spine), x, y, z))
|
||||
|
||||
@ -202,6 +230,9 @@ class ChipExtraData(BBAStruct):
|
||||
bba.label(f"{context}_dcs_bels")
|
||||
for i, t in enumerate(self.dcs_bels):
|
||||
t.serialise(f"{context}_dcs_bel{i}", bba)
|
||||
bba.label(f"{context}_dhcen_bels")
|
||||
for i, t in enumerate(self.dhcen_bels):
|
||||
t.serialise(f"{context}_dhcen_bel{i}", bba)
|
||||
|
||||
def serialise(self, context: str, bba: BBAWriter):
|
||||
bba.u32(self.flags)
|
||||
@ -209,6 +240,7 @@ class ChipExtraData(BBAStruct):
|
||||
bba.slice(f"{context}_diff_io_types", len(self.diff_io_types))
|
||||
bba.slice(f"{context}_dqce_bels", len(self.dqce_bels))
|
||||
bba.slice(f"{context}_dcs_bels", len(self.dcs_bels))
|
||||
bba.slice(f"{context}_dhcen_bels", len(self.dhcen_bels))
|
||||
|
||||
@dataclass
|
||||
class PadExtraData(BBAStruct):
|
||||
@ -425,6 +457,9 @@ dqce_bels = {}
|
||||
# map spine -> dcs bel
|
||||
dcs_bels = {}
|
||||
|
||||
# map HCLKIN wire -> dhcen bel
|
||||
dhcen_bels = {}
|
||||
|
||||
def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int):
|
||||
if (y, x) not in db.extra_func:
|
||||
return
|
||||
@ -453,6 +488,16 @@ def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int):
|
||||
tt.create_wire(wire)
|
||||
bel = tt.create_bel("BANDGAP", "BANDGAP", z = BANDGAP_Z)
|
||||
tt.add_bel_pin(bel, "BGEN", wire, PinType.INPUT)
|
||||
elif func == 'dhcen':
|
||||
for idx, dhcen in enumerate(desc):
|
||||
wire = dhcen['ce']
|
||||
if not tt.has_wire(wire):
|
||||
tt.create_wire(wire)
|
||||
bel_z = DHCEN_Z + idx
|
||||
bel = tt.create_bel(f"DHCEN{idx}", "DHCEN", z = bel_z)
|
||||
tt.add_bel_pin(bel, "CE", wire, PinType.INPUT)
|
||||
pip_xy, pip_dst, pip_src, side = dhcen['pip']
|
||||
dhcen_bels[pip_xy, pip_dst, pip_src] = (x, y, bel_z, side)
|
||||
elif func == 'dqce':
|
||||
for idx in range(6):
|
||||
bel_z = DQCE_Z + idx
|
||||
@ -1166,6 +1211,9 @@ def create_extra_data(chip: Chip, db: chipdb, chip_flags: int):
|
||||
chip.extra_data.add_bottom_io_cnd(net_a, net_b)
|
||||
for diff_type in db.diff_io_types:
|
||||
chip.extra_data.add_diff_io_type(diff_type)
|
||||
# create hclk wire->dhcen bel map
|
||||
for pip, bel in dhcen_bels.items():
|
||||
chip.extra_data.add_dhcen_bel(pip[0], pip[1], pip[2], bel[0], bel[1], bel[2], bel[3])
|
||||
# create spine->dqce bel map
|
||||
for spine, bel in dqce_bels.items():
|
||||
chip.extra_data.add_dqce_bel(spine, bel[0], bel[1], bel[2])
|
||||
|
@ -87,6 +87,22 @@ BelId GowinUtils::get_dcs_bel(IdString spine_name)
|
||||
return BelId();
|
||||
}
|
||||
|
||||
BelId GowinUtils::get_dhcen_bel(WireId hclkin_wire, IdString &side)
|
||||
{
|
||||
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
|
||||
for (auto &wire_bel : extra->dhcen_bels) {
|
||||
IdString dst = IdString(wire_bel.pip_dst);
|
||||
IdString src = IdString(wire_bel.pip_src);
|
||||
IdStringList pip = IdStringList::concat(IdStringList::concat(IdString(wire_bel.pip_xy), dst), src);
|
||||
WireId wire = ctx->getPipDstWire(ctx->getPipByName(pip));
|
||||
if (wire == hclkin_wire) {
|
||||
side = IdString(wire_bel.side);
|
||||
return ctx->getBelByLocation(Loc(wire_bel.bel_x, wire_bel.bel_y, wire_bel.bel_z));
|
||||
}
|
||||
}
|
||||
return BelId();
|
||||
}
|
||||
|
||||
bool GowinUtils::is_simple_io_bel(BelId bel)
|
||||
{
|
||||
return chip_bel_info(ctx->chip_info, bel).flags & BelFlags::FLAG_SIMPLE_IO;
|
||||
|
@ -35,6 +35,7 @@ struct GowinUtils
|
||||
BelId get_io_bel_from_iologic(BelId bel);
|
||||
BelId get_dqce_bel(IdString spine_name);
|
||||
BelId get_dcs_bel(IdString spine_name);
|
||||
BelId get_dhcen_bel(WireId hclkin_wire, IdString &side);
|
||||
|
||||
// BSRAM
|
||||
bool has_SP32(void);
|
||||
|
@ -3076,6 +3076,36 @@ struct GowinPacker
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Create DHCENs
|
||||
// =========================================
|
||||
void pack_dhcens()
|
||||
{
|
||||
// Allocate all available dhcen bels; we will find out which of them
|
||||
// will actually be used during the routing process.
|
||||
bool grab_bels = false;
|
||||
for (auto &cell : ctx->cells) {
|
||||
auto &ci = *cell.second;
|
||||
if (ci.type == id_DHCEN) {
|
||||
ci.pseudo_cell = std::make_unique<RegionPlug>(Loc(0, 0, 0));
|
||||
grab_bels = true;
|
||||
}
|
||||
}
|
||||
if (grab_bels) {
|
||||
// sane message if new primitives are used with old bases
|
||||
auto buckets = ctx->getBelBuckets();
|
||||
NPNR_ASSERT_MSG(std::find(buckets.begin(), buckets.end(), id_DHCEN) != buckets.end(),
|
||||
"There are no DHCEN bels to use.");
|
||||
int i = 0;
|
||||
for (auto &bel : ctx->getBelsInBucket(ctx->getBelBucketForCellType(id_DHCEN))) {
|
||||
IdString dhcen_name = ctx->idf("$PACKER_DHCEN_%d", ++i);
|
||||
CellInfo *dhcen = ctx->createCell(dhcen_name, id_DHCEN);
|
||||
dhcen->addInput(id_CE);
|
||||
ctx->bindBel(bel, dhcen, STRENGTH_LOCKED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =========================================
|
||||
// Enable UserFlash
|
||||
// =========================================
|
||||
@ -3218,6 +3248,9 @@ struct GowinPacker
|
||||
pack_buffered_nets();
|
||||
ctx->check();
|
||||
|
||||
pack_dhcens();
|
||||
ctx->check();
|
||||
|
||||
pack_userflash();
|
||||
ctx->check();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user