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(PLLA)
|
||||
|
||||
// ADC
|
||||
X(ADC)
|
||||
|
||||
// primitive attributes
|
||||
X(INIT)
|
||||
X(FF_USED)
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue