ice40: Implement emitting PLLs

This commit is contained in:
Sergiusz Bazanski 2018-07-22 13:42:07 +01:00
parent 139f7e0903
commit 2b1f7875bb
12 changed files with 275 additions and 17 deletions

View File

@ -173,6 +173,9 @@ struct Loc
bool operator==(const Loc &other) const { return (x == other.x) && (y == other.y) && (z == other.z); } bool operator==(const Loc &other) const { return (x == other.x) && (y == other.y) && (z == other.z); }
bool operator!=(const Loc &other) const { return (x != other.x) || (y != other.y) || (z == other.z); } bool operator!=(const Loc &other) const { return (x != other.x) || (y != other.y) || (z == other.z); }
Loc(int x, int y, int z) : x(x), y(y), z(z) {}
Loc() {}
}; };
NEXTPNR_NAMESPACE_END NEXTPNR_NAMESPACE_END

View File

@ -210,6 +210,7 @@ struct Router
else else
overtimeRevisitCnt++; overtimeRevisitCnt++;
} }
QueuedWire next_qw; QueuedWire next_qw;
next_qw.wire = next_wire; next_qw.wire = next_wire;

View File

@ -400,6 +400,7 @@ void FPGAViewWidget::paintGL()
// Calculate world thickness to achieve a screen 1px/1.1px line. // Calculate world thickness to achieve a screen 1px/1.1px line.
float thick1Px = mouseToWorldCoordinates(1, 0).x(); float thick1Px = mouseToWorldCoordinates(1, 0).x();
float thick11Px = mouseToWorldCoordinates(1.1, 0).x(); float thick11Px = mouseToWorldCoordinates(1.1, 0).x();
float thick2Px = mouseToWorldCoordinates(2, 0).x();
// Draw grid. // Draw grid.
auto grid = LineShaderData(); auto grid = LineShaderData();
@ -418,7 +419,7 @@ void FPGAViewWidget::paintGL()
for (int i = 0; i < 8; i++) for (int i = 0; i < 8; i++)
lineShader_.draw(rendererData_->highlighted[i], colors_.highlight[i], thick11Px, matrix); lineShader_.draw(rendererData_->highlighted[i], colors_.highlight[i], thick11Px, matrix);
lineShader_.draw(rendererData_->selected, colors_.selected, thick11Px, matrix); lineShader_.draw(rendererData_->selected, colors_.selected, thick2Px, matrix);
rendererDataLock_.unlock(); rendererDataLock_.unlock();
} }

View File

@ -331,11 +331,12 @@ WireId Arch::getBelPinWire(BelId bel, PortPin pin) const
int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires; int num_bel_wires = chip_info->bel_data[bel.index].num_bel_wires;
const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get(); const BelWirePOD *bel_wires = chip_info->bel_data[bel.index].bel_wires.get();
for (int i = 0; i < num_bel_wires; i++) for (int i = 0; i < num_bel_wires; i++) {
if (bel_wires[i].port == pin) { if (bel_wires[i].port == pin) {
ret.index = bel_wires[i].wire_index; ret.index = bel_wires[i].wire_index;
break; break;
} }
}
return ret; return ret;
} }

View File

@ -74,23 +74,33 @@ void set_config(const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cf
for (int i = 0; i < cfg.num_bits; i++) { for (int i = 0; i < cfg.num_bits; i++) {
int8_t &cbit = tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col); int8_t &cbit = tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col);
if (cbit && !value) if (cbit && !value)
log_error("clearing already set config bit %s", name.c_str()); log_error("clearing already set config bit %s\n", name.c_str());
cbit = value; cbit = value;
} }
} else { } else {
int8_t &cbit = tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col); int8_t &cbit = tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col);
cbit = value; cbit = value;
if (cbit && !value) if (cbit && !value)
log_error("clearing already set config bit %s[%d]", name.c_str(), index); log_error("clearing already set config bit %s[%d]\n", name.c_str(), index);
}
}
// Set an IE_{EN,REN} logical bit in a tile config. Logical means enabled.
// On {HX,LP}1K devices these bits are active low, so we need to inver them.
void set_ie_bit_logical(const Context *ctx, const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg, const std::string &name, bool value) {
if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
set_config(ti, tile_cfg, name, !value);
} else {
set_config(ti, tile_cfg, name, value);
} }
} }
int get_param_or_def(const CellInfo *cell, const IdString param, int defval = 0) int get_param_or_def(const CellInfo *cell, const IdString param, int defval = 0)
{ {
auto found = cell->params.find(param); auto found = cell->params.find(param);
if (found != cell->params.end()) if (found != cell->params.end()) {
return std::stoi(found->second); return std::stoi(found->second);
else } else
return defval; return defval;
} }
@ -117,7 +127,7 @@ static const BelConfigPOD &get_ec_config(const ChipInfoPOD *chip, BelId bel)
typedef std::vector<std::vector<std::vector<std::vector<int8_t>>>> chipconfig_t; typedef std::vector<std::vector<std::vector<std::vector<int8_t>>>> chipconfig_t;
static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfigPOD &cell_cbits, std::string name, static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfigPOD &cell_cbits, std::string name,
bool value) bool value, std::string prefix)
{ {
const ChipInfoPOD *chip = ctx->chip_info; const ChipInfoPOD *chip = ctx->chip_info;
@ -125,7 +135,7 @@ static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfi
const auto &cbit = cell_cbits.entries[i]; const auto &cbit = cell_cbits.entries[i];
if (cbit.entry_name.get() == name) { if (cbit.entry_name.get() == name) {
const auto &ti = chip->bits_info->tiles_nonrouting[tile_at(ctx, cbit.x, cbit.y)]; const auto &ti = chip->bits_info->tiles_nonrouting[tile_at(ctx, cbit.x, cbit.y)];
set_config(ti, config.at(cbit.y).at(cbit.x), std::string("IpConfig.") + cbit.cbit_name.get(), value); set_config(ti, config.at(cbit.y).at(cbit.x), prefix + cbit.cbit_name.get(), value);
return; return;
} }
} }
@ -133,7 +143,7 @@ static void set_ec_cbit(chipconfig_t &config, const Context *ctx, const BelConfi
} }
void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *cell, void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *cell,
const std::vector<std::pair<std::string, int>> &params, bool string_style) const std::vector<std::pair<std::string, int>> &params, bool string_style, std::string prefix)
{ {
const ChipInfoPOD *chip = ctx->chip_info; const ChipInfoPOD *chip = ctx->chip_info;
const auto &bc = get_ec_config(chip, cell->bel); const auto &bc = get_ec_config(chip, cell->bel);
@ -163,10 +173,10 @@ void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *ce
value.resize(p.second); value.resize(p.second);
if (p.second == 1) { if (p.second == 1) {
set_ec_cbit(config, ctx, bc, p.first, value.at(0)); set_ec_cbit(config, ctx, bc, p.first, value.at(0), prefix);
} else { } else {
for (int i = 0; i < p.second; i++) { for (int i = 0; i < p.second; i++) {
set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i)); set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i), prefix);
} }
} }
} }
@ -258,8 +268,13 @@ void write_asc(const Context *ctx, std::ostream &out)
} }
} }
} }
std::unordered_set<Loc> sb_io_used_by_pll;
std::unordered_set<Loc> sb_io_used_by_user;
// Set logic cell config // Set logic cell config
for (auto &cell : ctx->cells) { for (auto &cell : ctx->cells) {
BelId bel = cell.second.get()->bel; BelId bel = cell.second.get()->bel;
if (bel == BelId()) { if (bel == BelId()) {
std::cout << "Found unplaced cell " << cell.first.str(ctx) << " while generating bitstream!" << std::endl; std::cout << "Found unplaced cell " << cell.first.str(ctx) << " while generating bitstream!" << std::endl;
@ -304,6 +319,7 @@ void write_asc(const Context *ctx, std::ostream &out)
} else if (cell.second->type == ctx->id("SB_IO")) { } else if (cell.second->type == ctx->id("SB_IO")) {
const BelInfoPOD &beli = ci.bel_data[bel.index]; const BelInfoPOD &beli = ci.bel_data[bel.index];
int x = beli.x, y = beli.y, z = beli.z; int x = beli.x, y = beli.y, z = beli.z;
sb_io_used_by_user.insert(Loc(x,y,z));
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO]; const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
unsigned pin_type = get_param_or_def(cell.second.get(), ctx->id("PIN_TYPE")); unsigned pin_type = get_param_or_def(cell.second.get(), ctx->id("PIN_TYPE"));
bool neg_trigger = get_param_or_def(cell.second.get(), ctx->id("NEG_TRIGGER")); bool neg_trigger = get_param_or_def(cell.second.get(), ctx->id("NEG_TRIGGER"));
@ -405,7 +421,79 @@ void write_asc(const Context *ctx, std::ostream &out)
{"MODE_8x8", 1}, {"MODE_8x8", 1},
{"A_SIGNED", 1}, {"A_SIGNED", 1},
{"B_SIGNED", 1}}; {"B_SIGNED", 1}};
configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false); configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false, std::string("IpConfig."));
} else if (cell.second->type == ctx->id("ICESTORM_PLL")) {
const std::vector<std::pair<std::string, int>> pll_params = {
{"DELAY_ADJMODE_FB", 1}, {"DELAY_ADJMODE_REL", 1},
{"DIVF", 7}, {"DIVQ", 3}, {"DIVR", 4},
{"FDA_FEEDBACK", 4}, {"FDA_RELATIVE", 4},
{"FEEDBACK_PATH", 3}, {"FILTER_RANGE", 3},
{"PLLOUT_SELECT_A", 2}, {"PLLOUT_SELECT_B", 2},
{"PLLTYPE", 3}, {"SHIFTREG_DIV_MODE", 1}, {"TEST_MODE", 1}
};
configure_extra_cell(config, ctx, cell.second.get(), pll_params, false, std::string("PLL."));
// Configure the SB_IOs that the clock outputs are going through.
for (auto &port : cell.second->ports) {
// If this port is not a PLLOUT port, ignore it.
if (port.second.name != ctx->id("PLLOUT_A") && port.second.name != ctx->id("PLLOUT_B"))
continue;
// If the output is not driving any net, ignore it.
if (port.second.net == nullptr)
continue;
// Find SB_IO Bel in net that's driving a wire. That's the one
// we're routing the signal through.
// TODO(q3k): Is there a nicer way to do this?
bool found = false;
// For every wire in the PLLOUT net...
for (auto wp : port.second.net->wires) {
// ... get its' uphill bel ...
auto bel = ctx->getBelPinUphill(wp.first).bel;
if (bel == BelId()) {
continue;
}
// ... and check if it's an SB_IO.
if (ctx->getBelType(bel) != TYPE_SB_IO)
continue;
// Check that this SB_IO is either unused or just used as an output.
const BelInfoPOD &io_beli = ci.bel_data[bel.index];
auto io_loc = Loc(io_beli.x, io_beli.y, io_beli.z);
if (sb_io_used_by_user.count(io_loc)) {
log_error("SB_IO '%s' already in use, cannot route PLL through\n",
ctx->getBelName(bel).c_str(ctx));
}
sb_io_used_by_pll.insert(Loc(io_beli.x, io_beli.y, io_beli.z));
// Get IE/REN config location (cf. http://www.clifford.at/icestorm/io_tile.html)
auto ieren = get_ieren(bi, io_beli.x, io_beli.y, io_beli.z);
int iex, iey, iez;
std::tie(iex, iey, iez) = ieren;
NPNR_ASSERT(iez != -1);
// Write config.
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
// Enable input buffer and disable pull-up resistor in block
// (this is used by the PLL).
set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
// PINTYPE[0] passes the PLL through to the fabric.
set_config(ti, config.at(io_beli.y).at(io_beli.x), "IOB_" + std::to_string(io_beli.z) + ".PINTYPE_0", true);
found = true;
break;
}
if (!found) {
log_error("Could not find SB_IO forwarding PLL '%s' %s signal\n",
cell.second->name.c_str(ctx), port.second.name.c_str(ctx));
}
}
} else { } else {
NPNR_ASSERT(false); NPNR_ASSERT(false);
} }
@ -416,14 +504,16 @@ void write_asc(const Context *ctx, std::ostream &out)
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO]; const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
const BelInfoPOD &beli = ci.bel_data[bel.index]; const BelInfoPOD &beli = ci.bel_data[bel.index];
int x = beli.x, y = beli.y, z = beli.z; int x = beli.x, y = beli.y, z = beli.z;
if (sb_io_used_by_pll.count(Loc(x, y, z))) {
continue;
}
auto ieren = get_ieren(bi, x, y, z); auto ieren = get_ieren(bi, x, y, z);
int iex, iey, iez; int iex, iey, iez;
std::tie(iex, iey, iez) = ieren; std::tie(iex, iey, iez) = ieren;
if (iez != -1) { if (iez != -1) {
if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) { set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true);
set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), true); set_ie_bit_logical(ctx, ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), false);
}
} }
} else if (ctx->bel_to_cell[bel.index] == IdString() && ctx->getBelType(bel) == TYPE_ICESTORM_RAM) { } else if (ctx->bel_to_cell[bel.index] == IdString() && ctx->getBelType(bel) == TYPE_ICESTORM_RAM) {
const BelInfoPOD &beli = ci.bel_data[bel.index]; const BelInfoPOD &beli = ci.bel_data[bel.index];

View File

@ -207,6 +207,40 @@ std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::stri
add_port(ctx, new_cell.get(), "ACCUMCO", PORT_OUT); add_port(ctx, new_cell.get(), "ACCUMCO", PORT_OUT);
add_port(ctx, new_cell.get(), "SIGNEXTOUT", PORT_OUT); add_port(ctx, new_cell.get(), "SIGNEXTOUT", PORT_OUT);
} else if (type == ctx->id("ICESTORM_PLL")) {
new_cell->params[ctx->id("DELAY_ADJMODE_FB")] = "0";
new_cell->params[ctx->id("DELAY_ADJMODE_REL")] = "0";
new_cell->params[ctx->id("DIVF")] = "0";
new_cell->params[ctx->id("DIVQ")] = "0";
new_cell->params[ctx->id("DIVR")] = "0";
new_cell->params[ctx->id("FDA_FEEDBACK")] = "0";
new_cell->params[ctx->id("FDA_RELATIVE")] = "0";
new_cell->params[ctx->id("FEEDBACK_PATH")] = "0";
new_cell->params[ctx->id("FILTER_RANGE")] = "0";
new_cell->params[ctx->id("PLLOUT_SELECT_A")] = "0";
new_cell->params[ctx->id("PLLOUT_SELECT_B")] = "0";
new_cell->params[ctx->id("PLLTYPE")] = "0";
new_cell->params[ctx->id("SHIFTREG_DIVMODE")] = "0";
new_cell->params[ctx->id("TEST_MODE")] = "0";
add_port(ctx, new_cell.get(), "BYPASS", PORT_IN);
add_port(ctx, new_cell.get(), "DYNAMICDELAY", PORT_IN);
add_port(ctx, new_cell.get(), "EXTFEEDBACK", PORT_IN);
add_port(ctx, new_cell.get(), "LATCHINPUTVALUE", PORT_IN);
add_port(ctx, new_cell.get(), "REFERENCECLK", PORT_IN);
add_port(ctx, new_cell.get(), "RESETB", PORT_IN);
add_port(ctx, new_cell.get(), "SCLK", PORT_IN);
add_port(ctx, new_cell.get(), "SDI", PORT_IN);
add_port(ctx, new_cell.get(), "SDI", PORT_OUT);
add_port(ctx, new_cell.get(), "LOCK", PORT_OUT);
add_port(ctx, new_cell.get(), "PLLOUT_A", PORT_OUT);
add_port(ctx, new_cell.get(), "PLLOUT_B", PORT_OUT);
} else { } else {
log_error("unable to create iCE40 cell of type %s", type.c_str(ctx)); log_error("unable to create iCE40 cell of type %s", type.c_str(ctx));
} }
@ -312,6 +346,21 @@ void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio)
} }
} }
uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell)
{
if (cell->type == ctx->id("SB_PLL40_PAD"))
return 2;
if (cell->type == ctx->id("SB_PLL40_2_PAD"))
return 4;
if (cell->type == ctx->id("SB_PLL40_2F_PAD"))
return 5;
if (cell->type == ctx->id("SB_PLL40_CORE"))
return 3;
if (cell->type == ctx->id("SB_PLL40_2F_CORE"))
return 7;
NPNR_ASSERT(0);
}
bool is_clock_port(const BaseCtx *ctx, const PortRef &port) bool is_clock_port(const BaseCtx *ctx, const PortRef &port)
{ {
if (port.cell == nullptr) if (port.cell == nullptr)

View File

@ -71,6 +71,15 @@ inline bool is_sb_spram(const BaseCtx *ctx, const CellInfo *cell) { return cell-
inline bool is_sb_mac16(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_MAC16"); } inline bool is_sb_mac16(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_MAC16"); }
inline bool is_sb_pll40(const BaseCtx *ctx, const CellInfo *cell)
{
return cell->type == ctx->id("SB_PLL40_PAD") || cell->type == ctx->id("SB_PLL40_2_PAD") ||
cell->type == ctx->id("SB_PLL40_2F_PAD") || cell->type == ctx->id("SB_PLL40_CORE") ||
cell->type == ctx->id("SB_PLL40_2F_CORE");
}
uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell);
// Convert a SB_LUT primitive to (part of) an ICESTORM_LC, swapping ports // Convert a SB_LUT primitive to (part of) an ICESTORM_LC, swapping ports
// as needed. Set no_dff if a DFF is not being used, so that the output // as needed. Set no_dff if a DFF is not being used, so that the output
// can be reconnected // can be reconnected

View File

@ -184,6 +184,8 @@ def wire_type(name):
wt = "LOCAL" wt = "LOCAL"
elif name in ("WCLK", "WCLKE", "WE", "RCLK", "RCLKE", "RE"): elif name in ("WCLK", "WCLKE", "WE", "RCLK", "RCLKE", "RE"):
wt = "LOCAL" wt = "LOCAL"
elif name in ("PLLOUT_A", "PLLOUT_B"):
wt = "LOCAL"
if wt is None: if wt is None:
print("No type for wire: %s (%s)" % (longname, name), file=sys.stderr) print("No type for wire: %s (%s)" % (longname, name), file=sys.stderr)
@ -591,6 +593,43 @@ def is_ec_output(ec_entry):
if "glb_netwk_" in wirename: return True if "glb_netwk_" in wirename: return True
return False return False
def is_ec_pll_clock_output(ec, ec_entry):
return ec[0] == 'PLL' and ec_entry[0] in ('PLLOUT_A', 'PLLOUT_B')
def add_pll_clock_output(bel, ec, ec_entry):
#print('add_pll_clock_output', ec, ec_entry)
pll_x, pll_y, pll_z = ec[1], ec[2], ec[3]
port = ec_entry[0]
io_x, io_y, io_z = ec_entry[1]
io_z = int(io_z)
global num_wires
wire_idx = num_wires
num_wires = num_wires + 1
wire_xy[wire_idx] = [(pll_x, pll_y)]
wire_names_r[wire_idx] = (pll_x, pll_y, port)
wire_names[(pll_x, pll_y, port)] = wire_idx
wire_segments[wire_idx] = {
(pll_x, pll_y): port,
(io_x, io_y): 'PLLIN',
}
wire_downhill_belports[wire_idx] = {(bel, port),}
bel_wires[bel].append((wire_idx, port))
io_wire = wire_names[(io_x, io_y, 'io_{}/D_IN_0'.format(io_z))]
wire_downhill[wire_idx] = {io_wire,}
if io_wire not in wire_uphill:
wire_uphill[io_wire] = set()
wire_uphill[io_wire].add(wire_idx)
switches.append((io_x, io_y, 0, []))
switchnum = len(switches) - 1
pip_xy[(wire_idx, io_wire)] = (io_x, io_y, 0, switchnum)
def add_bel_ec(ec): def add_bel_ec(ec):
ectype, x, y, z = ec ectype, x, y, z = ec
bel = len(bel_name) bel = len(bel_name)
@ -605,6 +644,8 @@ def add_bel_ec(ec):
add_bel_output(bel, wire_names[entry[1]], entry[0]) add_bel_output(bel, wire_names[entry[1]], entry[0])
else: else:
add_bel_input(bel, wire_names[entry[1]], entry[0]) add_bel_input(bel, wire_names[entry[1]], entry[0])
elif is_ec_pll_clock_output(ec, entry):
add_pll_clock_output(bel, ec, entry)
else: else:
extra_cell_config[bel].append(entry) extra_cell_config[bel].append(entry)

View File

@ -464,7 +464,11 @@ enum GfxTileWireId
TILE_WIRE_SP12_H_R_23, TILE_WIRE_SP12_H_R_23,
TILE_WIRE_SP12_H_L_22, TILE_WIRE_SP12_H_L_22,
TILE_WIRE_SP12_H_L_23 TILE_WIRE_SP12_H_L_23,
TILE_WIRE_PLLIN,
TILE_WIRE_PLLOUT_A,
TILE_WIRE_PLLOUT_B
}; };
void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style); void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style);

View File

@ -591,6 +591,38 @@ static void pack_special(Context *ctx)
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname)); replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
} }
new_cells.push_back(std::move(packed)); new_cells.push_back(std::move(packed));
} else if (is_sb_pll40(ctx, ci)) {
std::unique_ptr<CellInfo> packed =
create_ice_cell(ctx, ctx->id("ICESTORM_PLL"), ci->name.str(ctx) + "_PLL");
packed_cells.insert(ci->name);
for (auto attr : ci->attrs)
packed->attrs[attr.first] = attr.second;
for (auto param : ci->params)
packed->params[param.first] = param.second;
auto feedback_path = packed->params[ctx->id("FEEDBACK_PATH")];
packed->params[ctx->id("FEEDBACK_PATH")] = feedback_path == "DELAY" ? "0" :
feedback_path == "SIMPLE" ? "1" :
feedback_path == "PHASE_AND_DELAY" ? "2" :
feedback_path == "EXTERNAL" ? "6" : feedback_path;
packed->params[ctx->id("PLLTYPE")] = std::to_string(sb_pll40_type(ctx, ci));
for (auto port : ci->ports) {
PortInfo &pi = port.second;
std::string newname = pi.name.str(ctx);
size_t bpos = newname.find('[');
if (bpos != std::string::npos) {
newname = newname.substr(0, bpos) + "_" + newname.substr(bpos + 1, (newname.size() - bpos) - 2);
}
if (pi.name == ctx->id("PLLOUTCOREA"))
newname = "PLLOUT_A";
if (pi.name == ctx->id("PLLOUTCOREB"))
newname = "PLLOUT_B";
if (pi.name == ctx->id("PLLOUTCORE"))
newname = "PLLOUT_A";
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
}
new_cells.push_back(std::move(packed));
} }
} }

View File

@ -62,6 +62,31 @@ bool apply_pcf(Context *ctx, std::istream &in)
log_info("constrained '%s' to bel '%s'\n", cell.c_str(), log_info("constrained '%s' to bel '%s'\n", cell.c_str(),
fnd_cell->second->attrs[ctx->id("BEL")].c_str()); fnd_cell->second->attrs[ctx->id("BEL")].c_str());
} }
} else if (cmd == "set_loc") {
size_t args_end = 1;
while (args_end < words.size() && words.at(args_end).at(0) == '-')
args_end++;
std::string cell = words.at(args_end);
std::string x = words.at(args_end + 1);
std::string y = words.at(args_end + 2);
std::string z = words.at(args_end + 3);
auto fnd_cell = ctx->cells.find(ctx->id(cell));
if (fnd_cell == ctx->cells.end()) {
log_error("unmatched pcf constraint %s\n", cell.c_str());
}
Loc loc;
loc.x = std::stoi(x);
loc.y = std::stoi(y);
loc.z = std::stoi(z);
auto bel = ctx->getBelByLocation(loc);
if (bel == BelId()) {
log_error("constrain '%s': unknown bel\n", line.c_str());
}
fnd_cell->second->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx);
log_info("constrained '%s' to bel '%s'\n", cell.c_str(), ctx->getBelName(bel).c_str(ctx));
} else { } else {
log_error("unsupported pcf command '%s'\n", cmd.c_str()); log_error("unsupported pcf command '%s'\n", cmd.c_str());
} }

View File

@ -118,6 +118,8 @@ X(DYNAMICDELAY_5)
X(DYNAMICDELAY_6) X(DYNAMICDELAY_6)
X(DYNAMICDELAY_7) X(DYNAMICDELAY_7)
X(LOCK) X(LOCK)
X(PLLOUT_A)
X(PLLOUT_B)
X(BYPASS) X(BYPASS)
X(RESETB) X(RESETB)
X(LATCHINPUTVALUE) X(LATCHINPUTVALUE)