mirror of https://github.com/YosysHQ/nextpnr.git
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:
parent
30669eca60
commit
d8117e3cad
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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) {
|
||||||
|
|
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
|
|
@ -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')
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue