2023-07-20 04:09:14 +02:00
|
|
|
#ifndef GOWIN_UTILS_H
|
|
|
|
|
#define GOWIN_UTILS_H
|
|
|
|
|
|
2024-10-02 22:36:36 +02:00
|
|
|
#include "design_utils.h"
|
2023-07-20 04:09:14 +02:00
|
|
|
#include "idstringlist.h"
|
|
|
|
|
#include "nextpnr_namespaces.h"
|
|
|
|
|
#include "nextpnr_types.h"
|
|
|
|
|
|
|
|
|
|
NEXTPNR_NAMESPACE_BEGIN
|
|
|
|
|
|
2023-08-08 02:57:45 +02:00
|
|
|
namespace BelFlags {
|
|
|
|
|
static constexpr uint32_t FLAG_SIMPLE_IO = 0x100;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-20 04:09:14 +02:00
|
|
|
struct GowinUtils
|
|
|
|
|
{
|
|
|
|
|
Context *ctx;
|
|
|
|
|
|
|
|
|
|
GowinUtils() {}
|
|
|
|
|
|
|
|
|
|
void init(Context *ctx) { this->ctx = ctx; }
|
|
|
|
|
|
2023-08-13 14:05:18 +02:00
|
|
|
// tile
|
|
|
|
|
IdString get_tile_class(int x, int y);
|
|
|
|
|
Loc get_tile_io16_offs(int x, int y);
|
2025-01-29 14:02:21 +01:00
|
|
|
bool get_i3c_capable(int x, int y);
|
Gowin. Add a router for segments. (#1456)
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>
2025-03-18 12:02:49 +01:00
|
|
|
inline Loc get_wire_loc(WireId wire) const
|
|
|
|
|
{
|
|
|
|
|
Loc loc;
|
|
|
|
|
tile_xy(ctx->chip_info, wire.tile, loc.x, loc.y);
|
|
|
|
|
return loc;
|
|
|
|
|
}
|
2023-08-13 14:05:18 +02:00
|
|
|
|
2023-07-20 04:09:14 +02:00
|
|
|
// pin functions: GCLKT_4, SSPI_CS, READY etc
|
2024-04-07 13:47:23 +02:00
|
|
|
IdStringList get_pin_funcs(BelId io_bel);
|
|
|
|
|
|
|
|
|
|
// PLL pads (type - CLKIN, FeedBack, etc)
|
|
|
|
|
BelId get_pll_bel(BelId io_bel, IdString type);
|
2023-08-08 02:57:45 +02:00
|
|
|
|
|
|
|
|
// 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);
|
2024-07-14 08:53:26 +02:00
|
|
|
BelId get_dqce_bel(IdString spine_name);
|
|
|
|
|
BelId get_dcs_bel(IdString spine_name);
|
2024-09-11 11:18:26 +02:00
|
|
|
BelId get_dhcen_bel(WireId hclkin_wire, IdString &side);
|
2025-03-19 08:41:35 +01:00
|
|
|
BelId get_dlldly_bel(BelId io_bel);
|
2023-08-08 02:57:45 +02:00
|
|
|
|
Gowin. Add a router for segments. (#1456)
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>
2025-03-18 12:02:49 +01:00
|
|
|
// 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;
|
|
|
|
|
|
2024-10-02 22:36:36 +02:00
|
|
|
// ports
|
|
|
|
|
inline bool port_used(CellInfo *cell, IdString port_name)
|
|
|
|
|
{
|
|
|
|
|
if (!nextpnr_himbaechel::port_used(cell, port_name)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
NetInfo *ni = cell->ports.at(port_name).net;
|
|
|
|
|
if (ni->driver.cell == nullptr) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return ni->users.entries() != 0;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-23 12:26:50 +02:00
|
|
|
// BSRAM
|
2024-07-06 11:14:43 +02:00
|
|
|
bool has_SP32(void);
|
2024-06-23 12:26:50 +02:00
|
|
|
bool need_SP_fix(void);
|
2024-06-25 10:27:00 +02:00
|
|
|
bool need_BSRAM_OUTREG_fix(void);
|
2024-06-28 00:15:50 +02:00
|
|
|
bool need_BLKSEL_fix(void);
|
2025-03-12 09:32:38 +01:00
|
|
|
bool has_PLL_HCLK(void);
|
|
|
|
|
bool has_CLKDIV_HCLK(void);
|
2024-06-23 12:26:50 +02:00
|
|
|
|
2024-07-06 11:14:43 +02:00
|
|
|
// Power saving
|
|
|
|
|
bool has_BANDGAP(void);
|
|
|
|
|
|
2024-03-18 13:08:52 +01:00
|
|
|
// DSP
|
|
|
|
|
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_18_idx(int z) const { return z & 4; }
|
|
|
|
|
inline int get_dsp_paired_9(int z) const { return (3 - get_dsp_9_idx(z)) | (z & (~3)); }
|
|
|
|
|
inline int get_dsp_mult_from_padd(int padd_z) const { return padd_z + 8; }
|
|
|
|
|
inline int get_dsp_padd_from_mult(int mult_z) const { return mult_z - 8; }
|
|
|
|
|
inline int get_dsp_next_macro(int z) const { return z + 32; }
|
|
|
|
|
inline int get_dsp(int z) const { return BelZ::DSP_Z; }
|
|
|
|
|
inline int get_dsp_macro(int z) const { return (z & 0x20) + BelZ::DSP_0_Z; }
|
|
|
|
|
inline int get_dsp_macro_num(int z) const { return (z & 0x20) >> 5; }
|
|
|
|
|
Loc get_dsp_next_9_in_chain(Loc from) const;
|
|
|
|
|
Loc get_dsp_next_macro_in_chain(Loc from) const;
|
|
|
|
|
Loc get_dsp_next_in_chain(Loc from, IdString dsp_type) const;
|
|
|
|
|
|
|
|
|
|
// check bus.
|
|
|
|
|
// This is necessary to find the head in the DSP chain - these buses are
|
|
|
|
|
// not switched in the hardware, but in software you can leave them
|
|
|
|
|
// unconnected or connect them to VCC or VSS, which is the same - as I
|
|
|
|
|
// already said, they are hard-wired and we are only discovering the fact
|
|
|
|
|
// that they are not connected to another DSP in the chain.
|
|
|
|
|
CellInfo *dsp_bus_src(const CellInfo *ci, const char *bus_prefix, int wire_num) const;
|
|
|
|
|
CellInfo *dsp_bus_dst(const CellInfo *ci, const char *bus_prefix, int wire_num) const;
|
|
|
|
|
|
2023-08-08 02:57:45 +02:00
|
|
|
bool is_diff_io_supported(IdString type);
|
2024-07-06 11:14:43 +02:00
|
|
|
bool has_bottom_io_cnds(void);
|
2023-08-08 02:57:45 +02:00
|
|
|
IdString get_bottom_io_wire_a_net(int8_t condition);
|
|
|
|
|
IdString get_bottom_io_wire_b_net(int8_t condition);
|
gowin: Himbaechel. Add GW1NZ-1 BSRAM.
The following primitives are implemented for the GW1NZ-1 chip:
* pROM - read only memory - (bitwidth: 1, 2, 4, 8, 16, 32).
* pROMX9 - read only memory - (bitwidth: 9, 18, 36).
* SDPB - semidual port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SDPX9B - semidual port - (bitwidth: 9, 18, 36).
* DPB - dual port - (bitwidth: 16).
* DPX9B - dual port - (bitwidth: 18).
* SP - single port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SPX9 - single port - (bitwidth: 9, 18, 36).
Also:
- The creation of databases for GW1NS-2 has been removed - this was not
planned to be supported in Himbaechel from the very beginning and
even examples were not created in apicula for this chip due to the
lack of boards with it on sale.
- It is temporarily prohibited to connect DFFs and LUTs into clusters
because for some reason this prevents the creation of images on lower
chips (placer cannot find the placement), although without these
clusters the images are quite working. Requires further research.
- Added creation of ALU with mode 0 - addition. Such an element is not
generated by Yosys, but it is a favorite vendor element and its
support here greatly simplifies the compilation of vendor netlists.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2023-11-20 03:27:56 +01:00
|
|
|
|
|
|
|
|
// wires
|
|
|
|
|
inline bool is_wire_type_default(IdString wire_type) { return wire_type == IdString(); }
|
2024-01-23 05:50:36 +01:00
|
|
|
// If wire is an important part of the global network (like SPINExx)
|
|
|
|
|
inline bool is_global_wire(WireId wire) const
|
|
|
|
|
{
|
|
|
|
|
return ctx->getWireName(wire)[1].in(
|
|
|
|
|
id_SPINE0, id_SPINE1, id_SPINE2, id_SPINE3, id_SPINE4, id_SPINE5, id_SPINE6, id_SPINE7, id_SPINE8,
|
|
|
|
|
id_SPINE9, id_SPINE10, id_SPINE11, id_SPINE12, id_SPINE13, id_SPINE14, id_SPINE15, id_SPINE16,
|
|
|
|
|
id_SPINE17, id_SPINE18, id_SPINE19, id_SPINE20, id_SPINE21, id_SPINE22, id_SPINE23, id_SPINE24,
|
|
|
|
|
id_SPINE25, id_SPINE26, id_SPINE27, id_SPINE28, id_SPINE29, id_SPINE30, id_SPINE31);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// pips
|
|
|
|
|
inline bool is_global_pip(PipId pip) const
|
|
|
|
|
{
|
|
|
|
|
return is_global_wire(ctx->getPipSrcWire(pip)) || is_global_wire(ctx->getPipDstWire(pip));
|
|
|
|
|
}
|
gowin: Himbaechel. Add BSRAM for all chips.
The following primitives are implemented for the GW1N-1, GW2A-18,
GW2AR-18C, GW1NSR-4C, GW1NR-9C, GW1NR-9 and GW1N-4 chips:
* pROM - read only memory - (bitwidth: 1, 2, 4, 8, 16, 32).
* pROMX9 - read only memory - (bitwidth: 9, 18, 36).
* SDPB - semidual port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SDPX9B - semidual port - (bitwidth: 9, 18, 36).
* DPB - dual port - (bitwidth: 16).
* DPX9B - dual port - (bitwidth: 18).
* SP - single port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SPX9 - single port - (bitwidth: 9, 18, 36).
For GW1NSR-4C and GW1NR-9 chips, SP/SPX9 primitives with data widths
of 32/36 bits are implemented using a pair of 16-bit wide
primitives.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2023-11-26 11:51:16 +01:00
|
|
|
|
Gowin. Add a router for segments. (#1456)
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>
2025-03-18 12:02:49 +01:00
|
|
|
inline bool is_segment_pip(PipId pip) const { return ctx->getWireType(ctx->getPipDstWire(pip)) == id_LW_TAP; }
|
|
|
|
|
|
2024-10-22 12:49:44 +02:00
|
|
|
// construct name
|
|
|
|
|
IdString create_aux_name(IdString main_name, int idx = 0, const char *str_suffix = "_aux$");
|
|
|
|
|
|
gowin: Himbaechel. Add BSRAM for all chips.
The following primitives are implemented for the GW1N-1, GW2A-18,
GW2AR-18C, GW1NSR-4C, GW1NR-9C, GW1NR-9 and GW1N-4 chips:
* pROM - read only memory - (bitwidth: 1, 2, 4, 8, 16, 32).
* pROMX9 - read only memory - (bitwidth: 9, 18, 36).
* SDPB - semidual port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SDPX9B - semidual port - (bitwidth: 9, 18, 36).
* DPB - dual port - (bitwidth: 16).
* DPX9B - dual port - (bitwidth: 18).
* SP - single port - (bitwidth: 1, 2, 4, 8, 16, 32).
* SPX9 - single port - (bitwidth: 9, 18, 36).
For GW1NSR-4C and GW1NR-9 chips, SP/SPX9 primitives with data widths
of 32/36 bits are implemented using a pair of 16-bit wide
primitives.
Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2023-11-26 11:51:16 +01:00
|
|
|
// make cell but do not include it in the list of chip cells.
|
|
|
|
|
std::unique_ptr<CellInfo> create_cell(IdString name, IdString type);
|
2024-08-03 15:57:22 +02:00
|
|
|
|
|
|
|
|
// HCLK
|
|
|
|
|
BelId get_clkdiv_for_clkdiv2(BelId clkdiv2_bel) const;
|
|
|
|
|
BelId get_other_hclk_clkdiv2(BelId clkdiv2_bel) const;
|
|
|
|
|
BelId get_other_hclk_clkdiv(BelId clkdiv_bel) const;
|
|
|
|
|
BelId get_clkdiv2_for_clkdiv(BelId clkdiv_bel) const;
|
|
|
|
|
IdStringList get_hclk_id(BelId hclk_bel) const; // use the upper CLKDIV2 (CLKDIV2_0 orCLKDIV2_2) as an id
|
|
|
|
|
|
|
|
|
|
// Find Bels connected to a bound cell
|
|
|
|
|
void find_connected_bels(const CellInfo *cell, IdString port, IdString dest_type, IdString dest_pin, int iter_limit,
|
|
|
|
|
std::vector<BelId> &candidates);
|
|
|
|
|
|
|
|
|
|
// Find a maximum bipartite matching
|
|
|
|
|
template <typename T1, typename T2> std::map<T1, T2> find_maximum_bipartite_matching(std::map<T1, std::set<T2>> &G)
|
|
|
|
|
{
|
|
|
|
|
std::map<int, T1> U;
|
|
|
|
|
std::map<int, T2> V;
|
|
|
|
|
std::map<T2, int> V_IDX;
|
|
|
|
|
std::vector<std::vector<int>> int_graph(G.size());
|
|
|
|
|
|
|
|
|
|
int u_idx = 0;
|
|
|
|
|
int v_idx = 0;
|
|
|
|
|
|
|
|
|
|
// Translate the input graph to an integer graph
|
|
|
|
|
for (auto row : G) {
|
|
|
|
|
U.insert(std::pair(u_idx, row.first));
|
|
|
|
|
for (auto v : row.second) {
|
|
|
|
|
if (V_IDX.find(v) == V_IDX.end()) {
|
|
|
|
|
V_IDX[v] = v_idx;
|
|
|
|
|
V[v_idx] = v;
|
|
|
|
|
v_idx++;
|
|
|
|
|
}
|
|
|
|
|
int_graph[u_idx].push_back(V_IDX[v]);
|
|
|
|
|
}
|
|
|
|
|
u_idx++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::vector<int> int_matching = kuhn_find_maximum_bipartite_matching(u_idx, v_idx, int_graph);
|
|
|
|
|
std::map<T1, T2> ret_matching;
|
|
|
|
|
|
|
|
|
|
int m_idx = 0;
|
|
|
|
|
for (auto val : int_matching) {
|
|
|
|
|
if (val >= 0) { // elements that are not matched have a value of -1
|
|
|
|
|
ret_matching[U[val]] = V[m_idx];
|
|
|
|
|
}
|
|
|
|
|
m_idx++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret_matching;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Find a maximum matching in a bipartite graph, g
|
|
|
|
|
std::vector<int> kuhn_find_maximum_bipartite_matching(int n, int k, std::vector<std::vector<int>> &g);
|
2023-07-20 04:09:14 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
NEXTPNR_NAMESPACE_END
|
|
|
|
|
|
|
|
|
|
#endif
|