Skip to content

Commit

Permalink
Gowin. Add a router for segments.
Browse files Browse the repository at this point in the history
Gowin chips have an interesting mechanism - wires that run vertically
through several rows (at least 10) in each column of the chip. In each
row a particular wire has branches to the left and right, covering on
average 4 neighboring cells in the row. For lack of a better term, I
further call such a wire a segment.

So a segment can provide a direct connection in a local rectangle. There
are no special restrictions on the sinks, so segment networks can be
used for ClockEnable, LocalSetReset, as well as for LUT and DFF inputs.

The sources are not so simple - the sources can be the upper or lower
end of the segment, which in theory can lead to unfortunate consequences
if the signal is applied from both ends.

The matter is complicated by the fact that there are default
connections, i.e. in the absence of any set fuse the segment input is
still connected to something (VCC for example) and to disable the unused
end of the segment you need to set a special combination of fuses.

Taking into account which end of which segment is used is one of the
tasks of this router. In addition, segment ends can physically coincide
with PLL, DSP and BSRAM inputs, which can also lead to unexpected
effects. Some of these things are tracked when generating the base, some
in this router, some when packing in gowin_pack.

Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
  • Loading branch information
yrabbit committed Feb 17, 2025
1 parent f3a5024 commit 008f65c
Show file tree
Hide file tree
Showing 7 changed files with 722 additions and 12 deletions.
5 changes: 5 additions & 0 deletions himbaechel/uarch/gowin/constids.inc
Original file line number Diff line number Diff line change
Expand Up @@ -1346,3 +1346,8 @@ X(TREG_LSR_NET)
X(NOIOBFF)
X(IOBFF)

// segments
X(LW_TAP)
X(LW_TAP_0)
X(LW_BRANCH)
X(SEG_WIRES_TO_ISOLATE)
531 changes: 520 additions & 11 deletions himbaechel/uarch/gowin/globals.cc

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion himbaechel/uarch/gowin/gowin.cc
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,8 @@ void GowinImpl::init(Context *ctx)
// We do not allow the use of global wires that bypass a special router.
bool GowinImpl::checkPipAvail(PipId pip) const
{
return (ctx->getWireConstantValue(ctx->getPipSrcWire(pip)) != IdString()) || (!gwu.is_global_pip(pip));
return (ctx->getWireConstantValue(ctx->getPipSrcWire(pip)) != IdString()) ||
(!(gwu.is_global_pip(pip) || gwu.is_segment_pip(pip)));
}

void GowinImpl::pack()
Expand Down
16 changes: 16 additions & 0 deletions himbaechel/uarch/gowin/gowin.h
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,21 @@ NPNR_PACKED_STRUCT(struct Wire_bel_POD {
int32_t side;
});

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

NPNR_PACKED_STRUCT(struct Constraint_POD {
int32_t net;
int32_t row;
Expand All @@ -162,6 +177,7 @@ NPNR_PACKED_STRUCT(struct Extra_chip_data_POD {
RelSlice<Spine_bel_POD> dqce_bels;
RelSlice<Spine_bel_POD> dcs_bels;
RelSlice<Wire_bel_POD> dhcen_bels;
RelSlice<Segment_POD> segments;
// chip flags
static constexpr int32_t HAS_SP32 = 1;
static constexpr int32_t NEED_SP_FIX = 2;
Expand Down
79 changes: 79 additions & 0 deletions himbaechel/uarch/gowin/gowin_arch_gen.py
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,44 @@ def serialise(self, context: str, bba: BBAWriter):
bba.u32(self.bel_z)
bba.u32(self.hclk_side.index)

# segment column description
@dataclass
class Segment(BBAStruct):
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: IdString
bottom_wire: IdString
top_gate_wire: list[IdString] = field(default_factory = list)
bottom_gate_wire: list[IdString] = field(default_factory = list)

def serialise_lists(self, context: str, bba: BBAWriter):
bba.label(f"{context}_top_gate_wire")
for i, wire in enumerate(self.top_gate_wire):
bba.u32(wire.index)
bba.label(f"{context}_bottom_gate_wire")
for i, wire in enumerate(self.bottom_gate_wire):
bba.u32(wire.index)

def serialise(self, context: str, bba: BBAWriter):
bba.u16(self.x)
bba.u16(self.seg_idx)
bba.u16(self.min_x)
bba.u16(self.min_y)
bba.u16(self.max_x)
bba.u16(self.max_y)
bba.u16(self.top_row)
bba.u16(self.bottom_row)
bba.u32(self.top_wire.index)
bba.u32(self.bottom_wire.index)
bba.slice(f"{context}_top_gate_wire", len(self.top_gate_wire))
bba.slice(f"{context}_bottom_gate_wire", len(self.bottom_gate_wire))

@dataclass
class ChipExtraData(BBAStruct):
strs: StringPool
Expand All @@ -209,6 +247,7 @@ class ChipExtraData(BBAStruct):
dqce_bels: list[SpineBel] = field(default_factory = list)
dcs_bels: list[SpineBel] = field(default_factory = list)
dhcen_bels: list[WireBel] = field(default_factory = list)
segments: list[Segment] = field(default_factory = list)

def create_bottom_io(self):
self.bottom_io = BottomIO()
Expand All @@ -228,8 +267,25 @@ def add_dqce_bel(self, spine: str, x: int, y: int, z: int):
def add_dcs_bel(self, spine: str, x: int, y: int, z: int):
self.dcs_bels.append(SpineBel(self.strs.id(spine), x, y, z))

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):
new_seg = Segment(x, seg_idx, min_x, min_y, max_x, max_y, top_row, bottom_row,
self.strs.id(top_wire), self.strs.id(bottom_wire),
[self.strs.id(top_gate_wire[0])], [self.strs.id(bottom_gate_wire[0])])
if top_gate_wire[1]:
new_seg.top_gate_wire.append(self.strs.id(top_gate_wire[1]))
else:
new_seg.top_gate_wire.append(self.strs.id(''))
if bottom_gate_wire[1]:
new_seg.bottom_gate_wire.append(self.strs.id(bottom_gate_wire[1]))
else:
new_seg.bottom_gate_wire.append(self.strs.id(''))
self.segments.append(new_seg)

def serialise_lists(self, context: str, bba: BBAWriter):
self.bottom_io.serialise_lists(f"{context}_bottom_io", bba)
for i, t in enumerate(self.segments):
t.serialise_lists(f"{context}_segment{i}", bba)
bba.label(f"{context}_diff_io_types")
for i, diff_io_type in enumerate(self.diff_io_types):
bba.u32(diff_io_type.index)
Expand All @@ -242,6 +298,9 @@ def serialise_lists(self, context: str, bba: BBAWriter):
bba.label(f"{context}_dhcen_bels")
for i, t in enumerate(self.dhcen_bels):
t.serialise(f"{context}_dhcen_bel{i}", bba)
bba.label(f"{context}_segments")
for i, t in enumerate(self.segments):
t.serialise(f"{context}_segment{i}", bba)

def serialise(self, context: str, bba: BBAWriter):
bba.u32(self.flags)
Expand All @@ -250,6 +309,7 @@ def serialise(self, context: str, bba: BBAWriter):
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))
bba.slice(f"{context}_segments", len(self.segments))

@dataclass
class PackageExtraData(BBAStruct):
Expand Down Expand Up @@ -400,6 +460,8 @@ def create_switch_matrix(tt: TileType, db: chipdb, x: int, y: int):
def get_wire_type(name):
if name in {'XD0', 'XD1', 'XD2', 'XD3', 'XD4', 'XD5',}:
return "X0"
if name in {'LT00', 'LT10', 'LT20', 'LT30', 'LT02', 'LT13'}:
return "LW_TAP"
return ""

for dst, srcs in db.grid[y][x].pips.items():
Expand Down Expand Up @@ -1282,6 +1344,23 @@ def create_extra_data(chip: Chip, db: chipdb, chip_flags: int):
# create spine->dcs bel map
for spine, bel in dcs_bels.items():
chip.extra_data.add_dcs_bel(spine, bel[0], bel[1], bel[2])
# create segments
if hasattr(db, "segments"):
for y_x_idx, seg in db.segments.items():
_, x, idx = y_x_idx
chip.extra_data.add_segment(x, idx, seg['min_x'], seg['min_y'], seg['max_x'], seg['max_y'],
seg['top_row'], seg['bottom_row'], seg['top_wire'], seg['bottom_wire'],
seg['top_gate_wire'], seg['bottom_gate_wire'])
# add segment nodes
lt_node = [NodeWire(x, seg['top_row'], seg['top_wire'])]
lt_node.append(NodeWire(x, seg['bottom_row'], seg['bottom_wire']))
for row in range(seg['min_y'], seg['max_y'] + 1):
lt_node.append(NodeWire(x, row, f'LT0{1 + (idx // 4) * 3}'))
node = [NodeWire(x, row, f'LBO{idx // 4}')]
for col in range(seg['min_x'], seg['max_x'] + 1):
node.append(NodeWire(col, row, f'LB{idx}1'))
chip.add_node(node)
chip.add_node(lt_node)

def create_timing_info(chip: Chip, db: chipdb.Device):
def group_to_timingvalue(group):
Expand Down
84 changes: 84 additions & 0 deletions himbaechel/uarch/gowin/gowin_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,90 @@

NEXTPNR_NAMESPACE_BEGIN

// Segments
int GowinUtils::get_segments_count(void) const
{
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
return extra->segments.ssize();
}

void GowinUtils::get_segment_region(int s_i, int &seg_idx, int &x, int &min_x, int &min_y, int &max_x, int &max_y) const
{
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
seg_idx = extra->segments[s_i].seg_idx;
x = extra->segments[s_i].x;
min_x = extra->segments[s_i].min_x;
min_y = extra->segments[s_i].min_y;
max_x = extra->segments[s_i].max_x;
max_y = extra->segments[s_i].max_y;
}

void GowinUtils::get_segment_wires_loc(int s_i, Loc &top, Loc &bottom) const
{
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
top.x = bottom.x = extra->segments[s_i].x;
top.y = extra->segments[s_i].top_row;
bottom.y = extra->segments[s_i].bottom_row;
}

void GowinUtils::get_segment_wires(int s_i, WireId &top, WireId &bottom) const
{
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
Loc top_loc, bottom_loc;
get_segment_wires_loc(s_i, top_loc, bottom_loc);

IdString tile = ctx->idf("X%dY%d", top_loc.x, top_loc.y);
IdStringList name = IdStringList::concat(tile, IdString(extra->segments[s_i].top_wire));
top = ctx->getWireByName(name);
tile = ctx->idf("X%dY%d", bottom_loc.x, bottom_loc.y);
name = IdStringList::concat(tile, IdString(extra->segments[s_i].bottom_wire));
bottom = ctx->getWireByName(name);
}

void GowinUtils::get_segment_top_gate_wires(int s_i, WireId &wire0, WireId &wire1) const
{
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
Loc top_loc, bottom_loc;
get_segment_wires_loc(s_i, top_loc, bottom_loc);

IdString tile = ctx->idf("X%dY%d", top_loc.x, top_loc.y);
IdStringList name;
wire0 = WireId();
IdString wire_name = IdString(extra->segments[s_i].top_gate_wire[0]);
if (wire_name != IdString()) {
name = IdStringList::concat(tile, wire_name);
wire0 = ctx->getWireByName(name);
}
wire1 = WireId();
wire_name = IdString(extra->segments[s_i].top_gate_wire[1]);
if (wire_name != IdString()) {
name = IdStringList::concat(tile, wire_name);
wire1 = ctx->getWireByName(name);
}
}

void GowinUtils::get_segment_bottom_gate_wires(int s_i, WireId &wire0, WireId &wire1) const
{
const Extra_chip_data_POD *extra = reinterpret_cast<const Extra_chip_data_POD *>(ctx->chip_info->extra_data.get());
Loc top_loc, bottom_loc;
get_segment_wires_loc(s_i, top_loc, bottom_loc);

IdString tile = ctx->idf("X%dY%d", bottom_loc.x, bottom_loc.y);
IdStringList name;
wire0 = WireId();
IdString wire_name = IdString(extra->segments[s_i].bottom_gate_wire[0]);
if (wire_name != IdString()) {
name = IdStringList::concat(tile, wire_name);
wire0 = ctx->getWireByName(name);
}
wire1 = WireId();
wire_name = IdString(extra->segments[s_i].bottom_gate_wire[1]);
if (wire_name != IdString()) {
name = IdStringList::concat(tile, wire_name);
wire1 = ctx->getWireByName(name);
}
}

// tile extra data
IdString GowinUtils::get_tile_class(int x, int y)
{
Expand Down
16 changes: 16 additions & 0 deletions himbaechel/uarch/gowin/gowin_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ struct GowinUtils
IdString get_tile_class(int x, int y);
Loc get_tile_io16_offs(int x, int y);
bool get_i3c_capable(int x, int y);
inline Loc get_wire_loc(WireId wire) const
{
Loc loc;
tile_xy(ctx->chip_info, wire.tile, loc.x, loc.y);
return loc;
}

// pin functions: GCLKT_4, SSPI_CS, READY etc
IdStringList get_pin_funcs(BelId io_bel);
Expand All @@ -39,6 +45,14 @@ struct GowinUtils
BelId get_dcs_bel(IdString spine_name);
BelId get_dhcen_bel(WireId hclkin_wire, IdString &side);

// Segments
int get_segments_count(void) const;
void get_segment_region(int s_i, int &seg_idx, int &x, int &min_x, int &min_y, int &max_x, int &max_y) const;
void get_segment_wires_loc(int s_i, Loc &top, Loc &bottom) const;
void get_segment_wires(int s_i, WireId &top, WireId &bottom) const;
void get_segment_top_gate_wires(int s_i, WireId &wire0, WireId &wire1) const;
void get_segment_bottom_gate_wires(int s_i, WireId &wire0, WireId &wire1) const;

// ports
inline bool port_used(CellInfo *cell, IdString port_name)
{
Expand Down Expand Up @@ -108,6 +122,8 @@ struct GowinUtils
return is_global_wire(ctx->getPipSrcWire(pip)) || is_global_wire(ctx->getPipDstWire(pip));
}

inline bool is_segment_pip(PipId pip) const { return ctx->getWireType(ctx->getPipDstWire(pip)) == id_LW_TAP; }

// construct name
IdString create_aux_name(IdString main_name, int idx = 0, const char *str_suffix = "_aux$");

Expand Down

0 comments on commit 008f65c

Please sign in to comment.