mirror of https://github.com/YosysHQ/nextpnr.git
Gowin. Add GW5AST-138C chip. (#1631)
* Gowin. Add GW5AST-138C chip.
The ability to perform P&R for the largest GW5A series chip currently
available has been added, which has its own characteristics:
- the need to invert pin function configuration signals - these
signals are not part of the design, but are nextpnr command line
keys for specifying the activation of alternative pin functions such as
I2C;
- some clock PIPs are encoded not by fuses, but by applying VCC/GND to
special inputs. This is also not part of the design and is not a
dynamic clock selection primitive - it is simply an addition to the
fuses.
- added check for DFF and SSRAM placement in upper slots - prior to
this chip, SSRAM was not supported and there was no need for this
check.
- since the chip is divided into two parts in terms of the global
clock network, a flag is introduced to indicate which part the wire
belongs to. This is only requested for clock wires.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
* Gowin. Fix style.
Use C++ type cast.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
---------
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
This commit is contained in:
parent
8c6278170b
commit
b4da86edce
|
|
@ -20,7 +20,7 @@ add_nextpnr_himbaechel_microarchitecture(${uarch}
|
||||||
CORE_SOURCES ${SOURCES}
|
CORE_SOURCES ${SOURCES}
|
||||||
)
|
)
|
||||||
|
|
||||||
set(ALL_HIMBAECHEL_GOWIN_DEVICES GW1N-1 GW1NZ-1 GW1N-4 GW1N-9 GW1N-9C GW1NS-4 GW2A-18 GW2A-18C GW5A-25A)
|
set(ALL_HIMBAECHEL_GOWIN_DEVICES GW1N-1 GW1NZ-1 GW1N-4 GW1N-9 GW1N-9C GW1NS-4 GW2A-18 GW2A-18C GW5A-25A GW5AST-138C)
|
||||||
set(HIMBAECHEL_GOWIN_DEVICES ${ALL_HIMBAECHEL_GOWIN_DEVICES} CACHE STRING
|
set(HIMBAECHEL_GOWIN_DEVICES ${ALL_HIMBAECHEL_GOWIN_DEVICES} CACHE STRING
|
||||||
"Include support for these Gowin devices (available: ${ALL_HIMBAECHEL_GOWIN_DEVICES})")
|
"Include support for these Gowin devices (available: ${ALL_HIMBAECHEL_GOWIN_DEVICES})")
|
||||||
if (HIMBAECHEL_GOWIN_DEVICES STREQUAL "all")
|
if (HIMBAECHEL_GOWIN_DEVICES STREQUAL "all")
|
||||||
|
|
|
||||||
|
|
@ -1237,6 +1237,7 @@ X(FB_C)
|
||||||
X(BOTTOM_IO_PORT_A)
|
X(BOTTOM_IO_PORT_A)
|
||||||
X(BOTTOM_IO_PORT_B)
|
X(BOTTOM_IO_PORT_B)
|
||||||
X(IOLOGIC_DUMMY)
|
X(IOLOGIC_DUMMY)
|
||||||
|
X(SPINE_SELECT)
|
||||||
|
|
||||||
// User Flash
|
// User Flash
|
||||||
X(INUSEN)
|
X(INUSEN)
|
||||||
|
|
|
||||||
|
|
@ -1187,6 +1187,76 @@ struct GowinGlobalRouter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enable clocked spines by connecting magic wires to VCC/GND if necessary.
|
||||||
|
void enable_spines(void)
|
||||||
|
{
|
||||||
|
if (ctx->verbose) {
|
||||||
|
log_info("Check for spine select wires.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
NetInfo *vcc_net = ctx->nets.at(ctx->id("$PACKER_VCC")).get();
|
||||||
|
NetInfo *vss_net = ctx->nets.at(ctx->id("$PACKER_GND")).get();
|
||||||
|
|
||||||
|
std::unique_ptr<CellInfo> top_ci = gwu.create_cell(ctx->id("spine_select$top"), id_SPINE_SELECT);
|
||||||
|
top_ci->pseudo_cell = std::make_unique<RegionPlug>(Loc(0, 0, 0));
|
||||||
|
std::unique_ptr<CellInfo> bottom_ci = gwu.create_cell(ctx->id("spine_select$bottom"), id_SPINE_SELECT);
|
||||||
|
bottom_ci->pseudo_cell = std::make_unique<RegionPlug>(Loc(0, 0, 0));
|
||||||
|
|
||||||
|
pool<WireId> seen_spines;
|
||||||
|
dict<IdString, int> top_connections;
|
||||||
|
dict<IdString, int> bottom_connections;
|
||||||
|
|
||||||
|
for (auto &net : ctx->nets) {
|
||||||
|
const NetInfo *ni = net.second.get();
|
||||||
|
for (auto &wire : ni->wires) {
|
||||||
|
WireId spine = wire.first;
|
||||||
|
IdString spine_name = ctx->getWireName(spine)[1];
|
||||||
|
if (spine_name.str(ctx).rfind("SPINE", 0) == 0 && seen_spines.count(spine) == 0) {
|
||||||
|
seen_spines.insert(spine);
|
||||||
|
std::vector<std::pair<WireId, int>> wires;
|
||||||
|
if (gwu.get_spine_select_wire(spine, wires)) {
|
||||||
|
int sfx = 0; // To activate a single spine, it may be necessary to connect an unknown number of
|
||||||
|
// wires.
|
||||||
|
CellInfo *select_cell = top_ci.get();
|
||||||
|
auto connections = &top_connections;
|
||||||
|
if (gwu.wire_in_bottom_half(spine)) {
|
||||||
|
select_cell = bottom_ci.get();
|
||||||
|
connections = &bottom_connections;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto gate : wires) {
|
||||||
|
IdString port_name = ctx->idf("%s.%d", spine_name.c_str(ctx), sfx);
|
||||||
|
|
||||||
|
select_cell->addInput(port_name);
|
||||||
|
|
||||||
|
RegionPlug *rp = dynamic_cast<RegionPlug *>(select_cell->pseudo_cell.get());
|
||||||
|
rp->port_wires[port_name] = gate.first;
|
||||||
|
(*connections)[port_name] = gate.second;
|
||||||
|
++sfx;
|
||||||
|
if (ctx->verbose) {
|
||||||
|
log_info(" %s->%s\n", port_name.c_str(ctx), ctx->nameOfWire(gate.first));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// realy connect nets
|
||||||
|
if (!top_connections.empty()) {
|
||||||
|
for (auto conn : top_connections) {
|
||||||
|
top_ci->connectPort(conn.first, conn.second ? vcc_net : vss_net);
|
||||||
|
}
|
||||||
|
ctx->cells[top_ci->name] = std::move(top_ci);
|
||||||
|
}
|
||||||
|
if (!bottom_connections.empty()) {
|
||||||
|
for (auto conn : bottom_connections) {
|
||||||
|
bottom_ci->connectPort(conn.first, conn.second ? vcc_net : vss_net);
|
||||||
|
}
|
||||||
|
ctx->cells[bottom_ci->name] = std::move(bottom_ci);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Route all
|
// Route all
|
||||||
void run(void)
|
void run(void)
|
||||||
{
|
{
|
||||||
|
|
@ -1295,6 +1365,14 @@ struct GowinGlobalRouter
|
||||||
if (gwu.get_segments_count() != 0) {
|
if (gwu.get_segments_count() != 0) {
|
||||||
route_segmented(seg_nets);
|
route_segmented(seg_nets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// In some GW5 series chips, in addition to the mechanism for
|
||||||
|
// enabling/disabling individual clock spines using fuses, which is
|
||||||
|
// invisible to nextpnr, it is necessary to enable them by connecting
|
||||||
|
// some ports of the mysterious MUX to VSS/GND.
|
||||||
|
if (gwu.has_spine_enable_nets()) {
|
||||||
|
enable_spines();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,8 @@ void GowinImpl::init_database(Arch *arch)
|
||||||
std::regex devicere = std::regex("GW5A(T|ST)?-LV(25|60|138)[A-Z]*.*");
|
std::regex devicere = std::regex("GW5A(T|ST)?-LV(25|60|138)[A-Z]*.*");
|
||||||
std::smatch match;
|
std::smatch match;
|
||||||
if (std::regex_match(args.device, match, devicere)) {
|
if (std::regex_match(args.device, match, devicere)) {
|
||||||
family = stringf("GW5A%s-%sA", match[1].str().c_str(), match[2].str().c_str());
|
family = stringf("GW5A%s-%s%s", match[1].str().c_str(), match[2].str().c_str(),
|
||||||
|
match[2].str() == "25" ? "A" : "C");
|
||||||
} else {
|
} else {
|
||||||
std::regex devicere = std::regex("GW1N([SZ]?)[A-Z]*-(LV|UV|UX)([0-9])(C?).*");
|
std::regex devicere = std::regex("GW1N([SZ]?)[A-Z]*-(LV|UV|UX)([0-9])(C?).*");
|
||||||
std::smatch match;
|
std::smatch match;
|
||||||
|
|
@ -1012,6 +1013,12 @@ bool GowinImpl::slice_valid(int x, int y, int z) const
|
||||||
ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, 5 * 2 + 1)))) {
|
ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, 5 * 2 + 1)))) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (gwu.has_DFF67()) {
|
||||||
|
if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, 6 * 2 + 1))) ||
|
||||||
|
ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, 7 * 2 + 1)))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
// ALU/LUTs in slices 4, 5, 6, 7 are not allowed
|
// ALU/LUTs in slices 4, 5, 6, 7 are not allowed
|
||||||
for (int i = 4; i < 8; ++i) {
|
for (int i = 4; i < 8; ++i) {
|
||||||
if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, i * 2)))) {
|
if (ctx->getBoundBelCell(ctx->getBelByLocation(Loc(x, y, i * 2)))) {
|
||||||
|
|
|
||||||
|
|
@ -176,6 +176,14 @@ NPNR_PACKED_STRUCT(struct Segment_POD {
|
||||||
RelSlice<uint32_t> bottom_gate_wire;
|
RelSlice<uint32_t> bottom_gate_wire;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct SpineSelectWire_POD {
|
||||||
|
uint32_t spine;
|
||||||
|
int16_t x;
|
||||||
|
int16_t y;
|
||||||
|
uint32_t wire;
|
||||||
|
uint32_t vcc_gnd;
|
||||||
|
});
|
||||||
|
|
||||||
NPNR_PACKED_STRUCT(struct Constraint_POD {
|
NPNR_PACKED_STRUCT(struct Constraint_POD {
|
||||||
int32_t net;
|
int32_t net;
|
||||||
int32_t row;
|
int32_t row;
|
||||||
|
|
@ -196,6 +204,9 @@ NPNR_PACKED_STRUCT(struct Extra_chip_data_POD {
|
||||||
RelSlice<Wire_bel_POD> dhcen_bels;
|
RelSlice<Wire_bel_POD> dhcen_bels;
|
||||||
RelSlice<Io_dlldly_bel_POD> io_dlldly_bels;
|
RelSlice<Io_dlldly_bel_POD> io_dlldly_bels;
|
||||||
RelSlice<Segment_POD> segments;
|
RelSlice<Segment_POD> segments;
|
||||||
|
RelSlice<SpineSelectWire_POD> spine_select_wires_top;
|
||||||
|
RelSlice<SpineSelectWire_POD> spine_select_wires_bottom;
|
||||||
|
|
||||||
// chip flags
|
// chip flags
|
||||||
static constexpr int32_t HAS_SP32 = 1;
|
static constexpr int32_t HAS_SP32 = 1;
|
||||||
static constexpr int32_t NEED_SP_FIX = 2;
|
static constexpr int32_t NEED_SP_FIX = 2;
|
||||||
|
|
@ -209,6 +220,8 @@ NPNR_PACKED_STRUCT(struct Extra_chip_data_POD {
|
||||||
static constexpr int32_t HAS_CIN_MUX = 512;
|
static constexpr int32_t HAS_CIN_MUX = 512;
|
||||||
static constexpr int32_t NEED_BSRAM_RESET_FIX = 1024;
|
static constexpr int32_t NEED_BSRAM_RESET_FIX = 1024;
|
||||||
static constexpr int32_t NEED_SDP_FIX = 2048;
|
static constexpr int32_t NEED_SDP_FIX = 2048;
|
||||||
|
static constexpr int32_t NEED_CFGPINS_INVERSION = 4096;
|
||||||
|
static constexpr int32_t HAS_I2CCFG = 8192;
|
||||||
});
|
});
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
||||||
|
|
@ -15,21 +15,24 @@ from apycula import chipdb
|
||||||
BEL_FLAG_SIMPLE_IO = 0x100
|
BEL_FLAG_SIMPLE_IO = 0x100
|
||||||
|
|
||||||
# Wire flags
|
# Wire flags
|
||||||
WIRE_FLAG_CLOCK_GATE = 0x1
|
WIRE_FLAG_CLOCK_GATE = 0x1
|
||||||
|
WIRE_FLAG_BOTTOM_HALF = 0x2 # the wire is located in the bottom half of the chip
|
||||||
|
|
||||||
# Chip flags
|
# Chip flags
|
||||||
CHIP_HAS_SP32 = 0x1
|
CHIP_HAS_SP32 = 0x1
|
||||||
CHIP_NEED_SP_FIX = 0x2
|
CHIP_NEED_SP_FIX = 0x2
|
||||||
CHIP_NEED_BSRAM_OUTREG_FIX = 0x4
|
CHIP_NEED_BSRAM_OUTREG_FIX = 0x4
|
||||||
CHIP_NEED_BLKSEL_FIX = 0x8
|
CHIP_NEED_BLKSEL_FIX = 0x8
|
||||||
CHIP_HAS_BANDGAP = 0x10
|
CHIP_HAS_BANDGAP = 0x10
|
||||||
CHIP_HAS_PLL_HCLK = 0x20
|
CHIP_HAS_PLL_HCLK = 0x20
|
||||||
CHIP_HAS_CLKDIV_HCLK = 0x40
|
CHIP_HAS_CLKDIV_HCLK = 0x40
|
||||||
CHIP_HAS_PINCFG = 0x80
|
CHIP_HAS_PINCFG = 0x80
|
||||||
CHIP_HAS_DFF67 = 0x100
|
CHIP_HAS_DFF67 = 0x100
|
||||||
CHIP_HAS_CIN_MUX = 0x200
|
CHIP_HAS_CIN_MUX = 0x200
|
||||||
CHIP_NEED_BSRAM_RESET_FIX = 0x400
|
CHIP_NEED_BSRAM_RESET_FIX = 0x400
|
||||||
CHIP_NEED_SDP_FIX = 0x800
|
CHIP_NEED_SDP_FIX = 0x800
|
||||||
|
CHIP_NEED_CFGPINS_INVERSION = 0x1000
|
||||||
|
CHIP_HAS_I2CCFG = 0x2000
|
||||||
|
|
||||||
# Tile flags
|
# Tile flags
|
||||||
TILE_I3C_CAPABLE_IO = 0x1
|
TILE_I3C_CAPABLE_IO = 0x1
|
||||||
|
|
@ -265,6 +268,23 @@ class Segment(BBAStruct):
|
||||||
bba.slice(f"{context}_top_gate_wire", len(self.top_gate_wire))
|
bba.slice(f"{context}_top_gate_wire", len(self.top_gate_wire))
|
||||||
bba.slice(f"{context}_bottom_gate_wire", len(self.bottom_gate_wire))
|
bba.slice(f"{context}_bottom_gate_wire", len(self.bottom_gate_wire))
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SpineSelectWire(BBAStruct):
|
||||||
|
spine: IdString
|
||||||
|
x: int
|
||||||
|
y: int
|
||||||
|
wire: IdString
|
||||||
|
vcc_gnd: int
|
||||||
|
|
||||||
|
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||||
|
pass
|
||||||
|
def serialise(self, context: str, bba: BBAWriter):
|
||||||
|
bba.u32(self.spine.index)
|
||||||
|
bba.u16(self.x)
|
||||||
|
bba.u16(self.y)
|
||||||
|
bba.u32(self.wire.index)
|
||||||
|
bba.u32(self.vcc_gnd)
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class ChipExtraData(BBAStruct):
|
class ChipExtraData(BBAStruct):
|
||||||
strs: StringPool
|
strs: StringPool
|
||||||
|
|
@ -277,6 +297,8 @@ class ChipExtraData(BBAStruct):
|
||||||
dhcen_bels: list[WireBel] = field(default_factory = list)
|
dhcen_bels: list[WireBel] = field(default_factory = list)
|
||||||
io_dlldly_bels: list[IoBel] = field(default_factory = list)
|
io_dlldly_bels: list[IoBel] = field(default_factory = list)
|
||||||
segments: list[Segment] = field(default_factory = list)
|
segments: list[Segment] = field(default_factory = list)
|
||||||
|
spine_select_wires_top: list[SpineSelectWire] = field(default_factory = list)
|
||||||
|
spine_select_wires_bottom: list[SpineSelectWire] = field(default_factory = list)
|
||||||
|
|
||||||
def set_dcs_prefix(self, prefix: str):
|
def set_dcs_prefix(self, prefix: str):
|
||||||
self.dcs_prefix = self.strs.id(prefix)
|
self.dcs_prefix = self.strs.id(prefix)
|
||||||
|
|
@ -301,6 +323,7 @@ class ChipExtraData(BBAStruct):
|
||||||
|
|
||||||
def add_io_dlldly_bel(self, io: str, dlldly: str):
|
def add_io_dlldly_bel(self, io: str, dlldly: str):
|
||||||
self.io_dlldly_bels.append(IoBel(self.strs.id(io), self.strs.id(dlldly)))
|
self.io_dlldly_bels.append(IoBel(self.strs.id(io), self.strs.id(dlldly)))
|
||||||
|
|
||||||
def add_segment(self, x: int, seg_idx: int, min_x: int, min_y: int, max_x: int, max_y: int,
|
def add_segment(self, x: int, seg_idx: int, min_x: int, min_y: int, max_x: int, max_y: int,
|
||||||
top_row: int, bottom_row: int, top_wire: str, bottom_wire: str, top_gate_wire: list, bottom_gate_wire: list):
|
top_row: int, bottom_row: int, top_wire: str, bottom_wire: str, top_gate_wire: list, bottom_gate_wire: list):
|
||||||
new_seg = Segment(x, seg_idx, min_x, min_y, max_x, max_y, top_row, bottom_row,
|
new_seg = Segment(x, seg_idx, min_x, min_y, max_x, max_y, top_row, bottom_row,
|
||||||
|
|
@ -316,6 +339,12 @@ class ChipExtraData(BBAStruct):
|
||||||
new_seg.bottom_gate_wire.append(self.strs.id(''))
|
new_seg.bottom_gate_wire.append(self.strs.id(''))
|
||||||
self.segments.append(new_seg)
|
self.segments.append(new_seg)
|
||||||
|
|
||||||
|
def add_spine_select_wire_top(self, spine: str, x: int, y: int, wire: str, vcc_gnd: int):
|
||||||
|
self.spine_select_wires_top.append(SpineSelectWire(self.strs.id(spine), x, y, self.strs.id(wire), vcc_gnd))
|
||||||
|
|
||||||
|
def add_spine_select_wire_bottom(self, spine: str, x: int, y: int, wire: str, vcc_gnd: int):
|
||||||
|
self.spine_select_wires_bottom.append(SpineSelectWire(self.strs.id(spine), x, y, self.strs.id(wire), vcc_gnd))
|
||||||
|
|
||||||
def serialise_lists(self, context: str, bba: BBAWriter):
|
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||||
self.bottom_io.serialise_lists(f"{context}_bottom_io", bba)
|
self.bottom_io.serialise_lists(f"{context}_bottom_io", bba)
|
||||||
for i, t in enumerate(self.segments):
|
for i, t in enumerate(self.segments):
|
||||||
|
|
@ -338,6 +367,12 @@ class ChipExtraData(BBAStruct):
|
||||||
bba.label(f"{context}_segments")
|
bba.label(f"{context}_segments")
|
||||||
for i, t in enumerate(self.segments):
|
for i, t in enumerate(self.segments):
|
||||||
t.serialise(f"{context}_segment{i}", bba)
|
t.serialise(f"{context}_segment{i}", bba)
|
||||||
|
bba.label(f"{context}_spine_select_wires_top")
|
||||||
|
for i, t in enumerate(self.spine_select_wires_top):
|
||||||
|
t.serialise(f"{context}_spine_select_wire_top{i}", bba)
|
||||||
|
bba.label(f"{context}_spine_select_wires_bottom")
|
||||||
|
for i, t in enumerate(self.spine_select_wires_bottom):
|
||||||
|
t.serialise(f"{context}_spine_select_wire_bottom{i}", bba)
|
||||||
|
|
||||||
def serialise(self, context: str, bba: BBAWriter):
|
def serialise(self, context: str, bba: BBAWriter):
|
||||||
bba.u32(self.flags)
|
bba.u32(self.flags)
|
||||||
|
|
@ -349,6 +384,8 @@ class ChipExtraData(BBAStruct):
|
||||||
bba.slice(f"{context}_dhcen_bels", len(self.dhcen_bels))
|
bba.slice(f"{context}_dhcen_bels", len(self.dhcen_bels))
|
||||||
bba.slice(f"{context}_io_dlldly_bels", len(self.io_dlldly_bels))
|
bba.slice(f"{context}_io_dlldly_bels", len(self.io_dlldly_bels))
|
||||||
bba.slice(f"{context}_segments", len(self.segments))
|
bba.slice(f"{context}_segments", len(self.segments))
|
||||||
|
bba.slice(f"{context}_spine_select_wires_top", len(self.spine_select_wires_top))
|
||||||
|
bba.slice(f"{context}_spine_select_wires_bottom", len(self.spine_select_wires_bottom))
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class PackageExtraData(BBAStruct):
|
class PackageExtraData(BBAStruct):
|
||||||
|
|
@ -527,12 +564,17 @@ def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int):
|
||||||
tt.create_pip(src, dst, get_tm_class(db, src))
|
tt.create_pip(src, dst, get_tm_class(db, src))
|
||||||
|
|
||||||
# clock wires
|
# clock wires
|
||||||
|
# always mark clock wires with location flag
|
||||||
for dst, srcs in db.grid[y][x].clock_pips.items():
|
for dst, srcs in db.grid[y][x].clock_pips.items():
|
||||||
if not tt.has_wire(dst):
|
if not tt.has_wire(dst):
|
||||||
tt.create_wire(dst, "GLOBAL_CLK")
|
wire = tt.create_wire(dst, "GLOBAL_CLK")
|
||||||
|
if hasattr(db, "last_top_row") and y > db.last_top_row:
|
||||||
|
wire.flags |= WIRE_FLAG_BOTTOM_HALF
|
||||||
for src in srcs.keys():
|
for src in srcs.keys():
|
||||||
if not tt.has_wire(src):
|
if not tt.has_wire(src):
|
||||||
tt.create_wire(src, "GLOBAL_CLK")
|
wire = tt.create_wire(src, "GLOBAL_CLK")
|
||||||
|
if hasattr(db, "last_top_row") and y > db.last_top_row:
|
||||||
|
wire.flags |= WIRE_FLAG_BOTTOM_HALF
|
||||||
src_tm_class = get_tm_class(db, src)
|
src_tm_class = get_tm_class(db, src)
|
||||||
tt.create_pip(src, dst, src_tm_class)
|
tt.create_pip(src, dst, src_tm_class)
|
||||||
|
|
||||||
|
|
@ -1466,6 +1508,17 @@ def create_extra_data(chip: Chip, db: chipdb, chip_flags: int):
|
||||||
node.append(NodeWire(col, row, f'LB{idx}1'))
|
node.append(NodeWire(col, row, f'LB{idx}1'))
|
||||||
chip.add_node(node)
|
chip.add_node(node)
|
||||||
chip.add_node(lt_node)
|
chip.add_node(lt_node)
|
||||||
|
# create spine select wires
|
||||||
|
if hasattr(db, "spine_select_wires"):
|
||||||
|
if 'top' in db.spine_select_wires:
|
||||||
|
for spine, wire_desc in db.spine_select_wires['top'].items():
|
||||||
|
for y, x, wire, vcc_gnd in wire_desc:
|
||||||
|
chip.extra_data.add_spine_select_wire_top(spine, x, y, wire, vcc_gnd)
|
||||||
|
|
||||||
|
if 'bottom' in db.spine_select_wires:
|
||||||
|
for spine, wire_desc in db.spine_select_wires['bottom'].items():
|
||||||
|
for y, x, wire, vcc_gnd in wire_desc:
|
||||||
|
chip.extra_data.add_spine_select_wire_bottom(spine, x, y, wire, vcc_gnd)
|
||||||
|
|
||||||
def create_timing_info(chip: Chip, db: chipdb.Device):
|
def create_timing_info(chip: Chip, db: chipdb.Device):
|
||||||
def group_to_timingvalue(group):
|
def group_to_timingvalue(group):
|
||||||
|
|
@ -1636,6 +1689,10 @@ def main():
|
||||||
chip_flags |= CHIP_NEED_BSRAM_RESET_FIX;
|
chip_flags |= CHIP_NEED_BSRAM_RESET_FIX;
|
||||||
if "NEED_SDP_FIX" in db.chip_flags:
|
if "NEED_SDP_FIX" in db.chip_flags:
|
||||||
chip_flags |= CHIP_NEED_SDP_FIX;
|
chip_flags |= CHIP_NEED_SDP_FIX;
|
||||||
|
if "NEED_CFGPINS_INVERSION" in db.chip_flags:
|
||||||
|
chip_flags |= CHIP_NEED_CFGPINS_INVERSION;
|
||||||
|
if "CHIP_HAS_I2CCFG" in db.chip_flags:
|
||||||
|
chip_flags |= CHIP_HAS_I2CCFG;
|
||||||
|
|
||||||
X = db.cols;
|
X = db.cols;
|
||||||
Y = db.rows;
|
Y = db.rows;
|
||||||
|
|
|
||||||
|
|
@ -350,18 +350,51 @@ bool GowinUtils::has_BANDGAP(void)
|
||||||
return extra->chip_flags & Extra_chip_data_POD::HAS_BANDGAP;
|
return extra->chip_flags & Extra_chip_data_POD::HAS_BANDGAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GowinUtils::has_PINCFG(void)
|
bool GowinUtils::has_PINCFG(void) const
|
||||||
{
|
{
|
||||||
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
|
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_PINCFG;
|
return extra->chip_flags & Extra_chip_data_POD::HAS_PINCFG;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GowinUtils::need_CFGPINS_INVERSION(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::NEED_CFGPINS_INVERSION;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GowinUtils::has_I2CCFG(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_I2CCFG;
|
||||||
|
}
|
||||||
|
|
||||||
bool GowinUtils::has_DFF67(void) const
|
bool GowinUtils::has_DFF67(void) const
|
||||||
{
|
{
|
||||||
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
|
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_DFF67;
|
return extra->chip_flags & Extra_chip_data_POD::HAS_DFF67;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GowinUtils::has_spine_enable_nets(void) const
|
||||||
|
{
|
||||||
|
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
|
||||||
|
return extra->spine_select_wires_top.ssize() || extra->spine_select_wires_bottom.ssize();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GowinUtils::get_spine_select_wire(WireId spine, std::vector<std::pair<WireId, int>> &wires)
|
||||||
|
{
|
||||||
|
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
|
||||||
|
|
||||||
|
wires.clear();
|
||||||
|
for (auto &rec : wire_in_bottom_half(spine) ? extra->spine_select_wires_bottom : extra->spine_select_wires_top) {
|
||||||
|
if (IdString(rec.spine) == ctx->getWireName(spine)[1]) {
|
||||||
|
IdString tile = ctx->idf("X%dY%d", rec.x, rec.y);
|
||||||
|
IdStringList name = IdStringList::concat(tile, IdString(rec.wire));
|
||||||
|
wires.push_back(std::make_pair(ctx->getWireByName(name), rec.vcc_gnd));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !wires.empty();
|
||||||
|
}
|
||||||
|
|
||||||
bool GowinUtils::has_CIN_MUX(void) const
|
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());
|
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ static constexpr uint32_t FLAG_SIMPLE_IO = 0x100;
|
||||||
|
|
||||||
namespace WireFlags {
|
namespace WireFlags {
|
||||||
static constexpr uint32_t FLAG_CLOCK_GATE = 0x1;
|
static constexpr uint32_t FLAG_CLOCK_GATE = 0x1;
|
||||||
}
|
static constexpr uint32_t FLAG_BOTTOM_HALF = 0x2;
|
||||||
|
} // namespace WireFlags
|
||||||
|
|
||||||
struct GowinUtils
|
struct GowinUtils
|
||||||
{
|
{
|
||||||
|
|
@ -96,6 +97,12 @@ struct GowinUtils
|
||||||
return chip_wire_info(ctx->chip_info, wire).flags & WireFlags::FLAG_CLOCK_GATE;
|
return chip_wire_info(ctx->chip_info, wire).flags & WireFlags::FLAG_CLOCK_GATE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool wire_in_bottom_half(WireId wire) const
|
||||||
|
{
|
||||||
|
return chip_wire_info(ctx->chip_info, wire).flags & WireFlags::FLAG_BOTTOM_HALF;
|
||||||
|
}
|
||||||
|
bool get_spine_select_wire(WireId spine, std::vector<std::pair<WireId, int>> &);
|
||||||
|
|
||||||
// BSRAM
|
// BSRAM
|
||||||
bool has_SP32(void);
|
bool has_SP32(void);
|
||||||
bool need_SP_fix(void);
|
bool need_SP_fix(void);
|
||||||
|
|
@ -110,7 +117,9 @@ struct GowinUtils
|
||||||
bool has_BANDGAP(void);
|
bool has_BANDGAP(void);
|
||||||
|
|
||||||
// Pin function configuration via wires
|
// Pin function configuration via wires
|
||||||
bool has_PINCFG(void);
|
bool has_PINCFG(void) const;
|
||||||
|
bool need_CFGPINS_INVERSION(void) const;
|
||||||
|
bool has_I2CCFG(void) const;
|
||||||
|
|
||||||
// Logic cell structure
|
// Logic cell structure
|
||||||
bool has_DFF67(void) const;
|
bool has_DFF67(void) const;
|
||||||
|
|
@ -118,6 +127,9 @@ struct GowinUtils
|
||||||
// ALU
|
// ALU
|
||||||
bool has_CIN_MUX(void) const;
|
bool has_CIN_MUX(void) const;
|
||||||
|
|
||||||
|
// Clock MUX
|
||||||
|
bool has_spine_enable_nets(void) const;
|
||||||
|
|
||||||
// DSP
|
// DSP
|
||||||
inline int get_dsp_18_z(int z) const { return z & (~3); }
|
inline int get_dsp_18_z(int z) const { return z & (~3); }
|
||||||
inline int get_dsp_9_idx(int z) const { return z & 3; }
|
inline int get_dsp_9_idx(int z) const { return z & 3; }
|
||||||
|
|
|
||||||
|
|
@ -3959,10 +3959,15 @@ struct GowinPacker
|
||||||
|
|
||||||
auto pincfg_cell = std::make_unique<CellInfo>(ctx, id_PINCFG, id_PINCFG);
|
auto pincfg_cell = std::make_unique<CellInfo>(ctx, id_PINCFG, id_PINCFG);
|
||||||
|
|
||||||
for (int i = 0; i < 5; ++i) {
|
const int pin_cnt = gwu.has_I2CCFG() ? 5 : 4;
|
||||||
|
for (int i = 0; i < pin_cnt; ++i) {
|
||||||
IdString port = ctx->idf("UNK%d_VCC", i);
|
IdString port = ctx->idf("UNK%d_VCC", i);
|
||||||
pincfg_cell->addInput(port);
|
pincfg_cell->addInput(port);
|
||||||
pincfg_cell->connectPort(port, ctx->nets.at(ctx->id("$PACKER_VCC")).get());
|
if (i && gwu.need_CFGPINS_INVERSION()) {
|
||||||
|
pincfg_cell->connectPort(port, ctx->nets.at(ctx->id("$PACKER_GND")).get());
|
||||||
|
} else {
|
||||||
|
pincfg_cell->connectPort(port, ctx->nets.at(ctx->id("$PACKER_VCC")).get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ArchArgs &args = ctx->args;
|
const ArchArgs &args = ctx->args;
|
||||||
|
|
@ -3975,12 +3980,14 @@ struct GowinPacker
|
||||||
pincfg_cell->connectPort(id_SSPI, ctx->nets.at(ctx->id("$PACKER_GND")).get());
|
pincfg_cell->connectPort(id_SSPI, ctx->nets.at(ctx->id("$PACKER_GND")).get());
|
||||||
}
|
}
|
||||||
|
|
||||||
pincfg_cell->addInput(id_I2C);
|
if (gwu.has_I2CCFG()) {
|
||||||
if (args.options.count("i2c_as_gpio")) {
|
pincfg_cell->addInput(id_I2C);
|
||||||
pincfg_cell->connectPort(id_I2C, ctx->nets.at(ctx->id("$PACKER_VCC")).get());
|
if (args.options.count("i2c_as_gpio")) {
|
||||||
pincfg_cell->setParam(id_I2C, 1);
|
pincfg_cell->connectPort(id_I2C, ctx->nets.at(ctx->id("$PACKER_VCC")).get());
|
||||||
} else {
|
pincfg_cell->setParam(id_I2C, 1);
|
||||||
pincfg_cell->connectPort(id_I2C, ctx->nets.at(ctx->id("$PACKER_GND")).get());
|
} else {
|
||||||
|
pincfg_cell->connectPort(id_I2C, ctx->nets.at(ctx->id("$PACKER_GND")).get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ctx->cells[pincfg_cell->name] = std::move(pincfg_cell);
|
ctx->cells[pincfg_cell->name] = std::move(pincfg_cell);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue