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>
This commit is contained in:
YRabbit 2025-11-06 18:17:05 +10:00 committed by GitHub
parent 30669eca60
commit d8117e3cad
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 100 additions and 7 deletions

View File

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

View File

@ -80,6 +80,7 @@ struct GowinCstReader
constrained_cells.insert(std::make_pair(cellId, belId)); constrained_cells.insert(std::make_pair(cellId, belId));
} }
}; };
pool<IdString> adc_ios; // bus#/X#Y#
log_info("Reading constraints...\n"); log_info("Reading constraints...\n");
try { try {
@ -95,6 +96,7 @@ struct GowinCstReader
std::regex hclkre = std::regex hclkre =
std::regex("INS_LOC +\"([^\"]+)\" +(TOP|RIGHT|BOTTOM|LEFT)SIDE\\[([0,1])\\] *;*[\\s\\S]*"); 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 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::smatch match, match_attr, match_pinloc;
std::string line, pinlines[2]; std::string line, pinlines[2];
std::vector<IdStringList> constrained_clkdivs; std::vector<IdStringList> constrained_clkdivs;
@ -104,7 +106,8 @@ struct GowinCstReader
ioport, ioport,
insloc, insloc,
clock, clock,
hclk hclk,
adc
} cst_type; } cst_type;
while (!in.eof()) { while (!in.eof()) {
@ -123,10 +126,14 @@ struct GowinCstReader
if (std::regex_match(line, match, hclkre)) { if (std::regex_match(line, match, hclkre)) {
cst_type = hclk; cst_type = hclk;
} else { } else {
if ((!line.empty()) && (line.rfind("//", 0) == std::string::npos)) { if (std::regex_match(line, match, adcre)) {
log_warning("Invalid constraint: %s\n", line.c_str()); 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]); IdString net = ctx->id(match[1]);
auto it = ctx->cells.find(net); 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)); log_info("Cell %s not found\n", net.c_str(ctx));
continue; continue;
} }
switch (cst_type) { 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=# case clock: { // CLOCK name BUFG|S=#
std::string which_clock = match[2]; std::string which_clock = match[2];
std::string lw = match[4]; std::string lw = match[4];
@ -258,6 +280,25 @@ struct GowinCstReader
for (auto &cell : constrained_cells) { 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()); 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; return true;
} catch (log_execution_error_exception) { } 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 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); } 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 // Return true if a cell is a EMCU
inline bool type_is_emcu(IdString cell_type) { return cell_type == id_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); } inline bool is_emcu(const CellInfo *cell) { return type_is_emcu(cell->type); }
@ -254,6 +258,8 @@ enum
PINCFG_Z = 400, PINCFG_Z = 400,
ADC_Z = 401,
// The two least significant bits encode Z for 9-bit adders and // 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 // multipliers, if they are equal to 0, then we get Z of their common
// 18-bit equivalent. // 18-bit equivalent.

View File

@ -75,7 +75,8 @@ MIPIIBUF_Z = 302
DLLDLY_Z = 303 # : 305 reserve for 2 DLLDLYs DLLDLY_Z = 303 # : 305 reserve for 2 DLLDLYs
PINCFG_Z = 400 # PINCFG_Z = 400
ADC_Z = 401
DSP_Z = 509 DSP_Z = 509
@ -463,7 +464,7 @@ def create_nodes(chip: Chip, db: chipdb):
for i in range(5): for i in range(5):
nodes.append([NodeWire(x, y, f'COUT{i}'), nodes.append([NodeWire(x, y, f'COUT{i}'),
NodeWire(x, y, f'CIN{i + 1}')]); 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'): 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'), nodes.append([NodeWire(x, y, f'CIN0'),
NodeWire(x - 1, y, f'COUT5')]) 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(): for pin, wire in desc['inputs'].items():
tt.create_wire(wire, "PLL_I") tt.create_wire(wire, "PLL_I")
tt.add_bel_pin(pll, pin, wire, PinType.INPUT) 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': elif func == 'gnd_source':
# GND is the logic low level generator # GND is the logic low level generator
tt.create_wire('VSS', 'GND', const_value = 'VSS') 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 // HCLK -- CLKDIV and CLKDIV2 for now
// =================================== // ===================================
@ -4557,6 +4585,9 @@ struct GowinPacker
pack_pll(); pack_pll();
ctx->check(); ctx->check();
pack_adc();
ctx->check();
pack_bsram(); pack_bsram();
ctx->check(); ctx->check();