Compare commits

...

4 Commits

Author SHA1 Message Date
Kelvin Chung 1ad7b655db
Merge de38bd34fb into d8117e3cad 2025-11-06 10:31:17 +00:00
YRabbit d8117e3cad
Gowin. Implement ADC. (#1597)
ADC support for GW5A-25 chips has been added.

The inputs of this primitive are fixed and do not require routing,
although they can be switched dynamically.

The .CST file also specifies the pins used as signal sources for the
bus0 and bus1 ADC buses.

Signed-off-by: YRabbit <rabbit@yrabbit.cyou>
2025-11-06 09:17:05 +01:00
Kelvin Chung de38bd34fb Change the iter count based timeout 2025-05-23 12:19:40 +01:00
Kelvin Chung 496df84b3b timeout for router1 2025-05-23 10:31:36 +01:00
7 changed files with 114 additions and 9 deletions

View File

@ -397,6 +397,8 @@ po::options_description CommandHandler::getGeneralOptions()
general.add_options()("parallel-refine", "use new experimental parallelised engine for placement refinement");
#endif
general.add_options()("router1-timeout", po::value<int>(), "Timeout for router1 in iteration count (default: 0, no timeout)");
general.add_options()("router2-heatmap", po::value<std::string>(),
"prefix for router2 resource congestion heatmaps");
@ -539,6 +541,10 @@ void CommandHandler::setupContext(Context *ctx)
if (vm.count("parallel-refine"))
ctx->settings[ctx->id("placerHeap/parallelRefine")] = true;
if (vm.count("router1-timeout")) {
ctx->settings[ctx->id("router1/maxIterCnt")] = std::to_string(std::max(0, vm["router1-timeout"].as<int>()));
}
if (vm.count("router2-heatmap"))
ctx->settings[ctx->id("router2/heatmap")] = vm["router2-heatmap"].as<std::string>();
if (vm.count("tmg-ripup") || vm.count("router2-tmg-ripup"))

View File

@ -1146,7 +1146,7 @@ NEXTPNR_NAMESPACE_BEGIN
Router1Cfg::Router1Cfg(Context *ctx)
{
maxIterCnt = ctx->setting<int>("router1/maxIterCnt", 200);
maxIterCnt = ctx->setting<int>("router1/maxIterCnt", 0);
cleanupReroute = ctx->setting<bool>("router1/cleanupReroute", true);
fullCleanupReroute = ctx->setting<bool>("router1/fullCleanupReroute", true);
useEstimate = ctx->setting<bool>("router1/useEstimate", true);
@ -1199,11 +1199,17 @@ bool router1(Context *ctx, const Router1Cfg &cfg)
last_arcs_with_ripup = router.arcs_with_ripup;
last_arcs_without_ripup = router.arcs_without_ripup;
ctx->yield();
#ifndef NDEBUG
router.check();
#endif
}
if (cfg.maxIterCnt){
if (iter_cnt > cfg.maxIterCnt) {
log_error("Max iteration count reached, stopping routing.\n");
}
}
if (ctx->debug)
log("-- %d --\n", iter_cnt);

View File

@ -1038,6 +1038,9 @@ X(RPLLA)
X(PLLVR)
X(PLLA)
// ADC
X(ADC)
// primitive attributes
X(INIT)
X(FF_USED)

View File

@ -80,6 +80,7 @@ struct GowinCstReader
constrained_cells.insert(std::make_pair(cellId, belId));
}
};
pool<IdString> adc_ios; // bus#/X#Y#
log_info("Reading constraints...\n");
try {
@ -95,6 +96,7 @@ struct GowinCstReader
std::regex hclkre =
std::regex("INS_LOC +\"([^\"]+)\" +(TOP|RIGHT|BOTTOM|LEFT)SIDE\\[([0,1])\\] *;*[\\s\\S]*");
std::regex clockre = std::regex("CLOCK_LOC +\"([^\"]+)\" +BUF([GS])(\\[([0-7])\\])?[^;]*;.*[\\s\\S]*");
std::regex adcre = std::regex("USE_ADC_SRC +bus([0-9]) +IO([TRBL])([0-9]+) *;.*[\\s\\S]*");
std::smatch match, match_attr, match_pinloc;
std::string line, pinlines[2];
std::vector<IdStringList> constrained_clkdivs;
@ -104,7 +106,8 @@ struct GowinCstReader
ioport,
insloc,
clock,
hclk
hclk,
adc
} cst_type;
while (!in.eof()) {
@ -123,10 +126,14 @@ struct GowinCstReader
if (std::regex_match(line, match, hclkre)) {
cst_type = hclk;
} else {
if ((!line.empty()) && (line.rfind("//", 0) == std::string::npos)) {
log_warning("Invalid constraint: %s\n", line.c_str());
if (std::regex_match(line, match, adcre)) {
cst_type = adc;
} else {
if ((!line.empty()) && (line.rfind("//", 0) == std::string::npos)) {
log_warning("Invalid constraint: %s\n", line.c_str());
}
continue;
}
continue;
}
}
}
@ -135,11 +142,26 @@ struct GowinCstReader
IdString net = ctx->id(match[1]);
auto it = ctx->cells.find(net);
if (cst_type != clock && it == ctx->cells.end()) {
if (cst_type != clock && cst_type != adc && it == ctx->cells.end()) {
log_info("Cell %s not found\n", net.c_str(ctx));
continue;
}
switch (cst_type) {
case adc: { // USE_ADC_SRC bus# IOLOC
int col = std::stoi(match[3]);
int row = 1; // Top
std::string side = match[2].str();
if (side == "R") {
row = col;
col = ctx->getGridDimX();
} else if (side == "B") {
row = ctx->getGridDimY();
} else if (side == "L") {
row = col;
col = 1;
}
adc_ios.insert(ctx->idf("%d/X%dY%d", std::stoi(match[1]), row - 1, col - 1));
} break;
case clock: { // CLOCK name BUFG|S=#
std::string which_clock = match[2];
std::string lw = match[4];
@ -258,6 +280,25 @@ struct GowinCstReader
for (auto &cell : constrained_cells) {
log_info("Cell %s is constrained to %s\n", cell.first.c_str(ctx), cell.second.str(ctx).c_str());
}
if (!adc_ios.empty()) {
log_info("ADC iobufs:\n");
for (auto &bus_io : adc_ios) {
log_info(" bus %s\n", bus_io.c_str(ctx));
}
}
}
if (!adc_ios.empty()) {
for (auto &cell : ctx->cells) {
auto &ci = *cell.second;
if (is_adc(&ci)) {
int idx = 0;
for (auto &bus_io : adc_ios) {
ci.setAttr(ctx->idf("ADC_IO_%d", idx), bus_io.str(ctx));
++idx;
}
}
}
}
return true;
} catch (log_execution_error_exception) {

View File

@ -100,6 +100,10 @@ inline bool is_userflash(const CellInfo *cell) { return type_is_userflash(cell->
inline bool type_is_pll(IdString cell_type) { return cell_type.in(id_rPLL, id_PLLVR); }
inline bool is_pll(const CellInfo *cell) { return type_is_pll(cell->type); }
// Return true if a cell is a ADC
inline bool type_is_adc(IdString cell_type) { return cell_type.in(id_ADC); }
inline bool is_adc(const CellInfo *cell) { return type_is_adc(cell->type); }
// Return true if a cell is a EMCU
inline bool type_is_emcu(IdString cell_type) { return cell_type == id_EMCU; }
inline bool is_emcu(const CellInfo *cell) { return type_is_emcu(cell->type); }
@ -254,6 +258,8 @@ enum
PINCFG_Z = 400,
ADC_Z = 401,
// 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
// 18-bit equivalent.

View File

@ -75,7 +75,8 @@ MIPIIBUF_Z = 302
DLLDLY_Z = 303 # : 305 reserve for 2 DLLDLYs
PINCFG_Z = 400 #
PINCFG_Z = 400
ADC_Z = 401
DSP_Z = 509
@ -463,7 +464,7 @@ def create_nodes(chip: Chip, db: chipdb):
for i in range(5):
nodes.append([NodeWire(x, y, f'COUT{i}'),
NodeWire(x, y, f'CIN{i + 1}')]);
# gobal carry chain
# global carry chain
if x > 1 and chip.tile_type_at(x - 1, y).extra_data.tile_class == chip.strs.id('LOGIC'):
nodes.append([NodeWire(x, y, f'CIN0'),
NodeWire(x - 1, y, f'COUT5')])
@ -788,6 +789,17 @@ def create_extra_funcs(tt: TileType, db: chipdb, x: int, y: int):
for pin, wire in desc['inputs'].items():
tt.create_wire(wire, "PLL_I")
tt.add_bel_pin(pll, pin, wire, PinType.INPUT)
elif func == 'adc':
pll = tt.create_bel("ADC", "ADC", z = ADC_Z)
for pin, wire in desc['outputs'].items():
tt.create_wire(wire, "ADC_O")
tt.add_bel_pin(pll, pin, wire, PinType.OUTPUT)
for pin, wire in desc['inputs'].items():
if pin == 'CLK' or pin == 'MDRP_CLK':
tt.create_wire(wire, "TILE_CLK")
else:
tt.create_wire(wire, "ADC_I")
tt.add_bel_pin(pll, pin, wire, PinType.INPUT)
elif func == 'gnd_source':
# GND is the logic low level generator
tt.create_wire('VSS', 'GND', const_value = 'VSS')

View File

@ -4073,6 +4073,34 @@ struct GowinPacker
}
}
// ===================================
// ADC
// ===================================
void pack_adc(void)
{
log_info("Pack ADC...\n");
for (auto &cell : ctx->cells) {
auto &ci = *cell.second;
if (is_adc(&ci)) {
for (int i = 0; i < 14; ++i) {
if (i < 2) {
ci.renamePort(ctx->idf("MDRP_OPCODE[%d]", i), ctx->idf("MDRP_OPCODE%d", i));
}
if (i < 3) {
ci.renamePort(ctx->idf("VSENCTL[%d]", i), ctx->idf("VSENCTL%d", i));
}
if (i < 8) {
ci.renamePort(ctx->idf("MDRP_WDATA[%d]", i), ctx->idf("MDRP_WDATA%d", i));
ci.renamePort(ctx->idf("MDRP_RDATA[%d]", i), ctx->idf("MDRP_RDATA%d", i));
}
ci.renamePort(ctx->idf("ADCVALUE[%d]", i), ctx->idf("ADCVALUE%d", i));
}
}
}
}
// ===================================
// HCLK -- CLKDIV and CLKDIV2 for now
// ===================================
@ -4557,6 +4585,9 @@ struct GowinPacker
pack_pll();
ctx->check();
pack_adc();
ctx->check();
pack_bsram();
ctx->check();