Copy ice40 into xc7

This commit is contained in:
Eddie Hung 2018-08-11 14:35:49 -07:00
parent 2e02f2d616
commit d53658a079
59 changed files with 13809 additions and 0 deletions

902
xc7/arch.cc Normal file
View File

@ -0,0 +1,902 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include <algorithm>
#include <cmath>
#include "cells.h"
#include "gfx.h"
#include "log.h"
#include "nextpnr.h"
#include "placer1.h"
#include "router1.h"
#include "util.h"
NEXTPNR_NAMESPACE_BEGIN
// -----------------------------------------------------------------------
void IdString::initialize_arch(const BaseCtx *ctx)
{
#define X(t) initialize_add(ctx, #t, ID_##t);
#include "constids.inc"
#undef X
}
// -----------------------------------------------------------------------
static const ChipInfoPOD *get_chip_info(const RelPtr<ChipInfoPOD> *ptr) { return ptr->get(); }
#if defined(_MSC_VER)
void load_chipdb();
#endif
Arch::Arch(ArchArgs args) : args(args)
{
#if defined(_MSC_VER)
load_chipdb();
#endif
#ifdef ICE40_HX1K_ONLY
if (args.type == ArchArgs::HX1K) {
fast_part = true;
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_1k));
} else {
log_error("Unsupported iCE40 chip type.\n");
}
#else
if (args.type == ArchArgs::LP384) {
fast_part = false;
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_384));
} else if (args.type == ArchArgs::LP1K || args.type == ArchArgs::HX1K) {
fast_part = args.type == ArchArgs::HX1K;
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_1k));
} else if (args.type == ArchArgs::UP5K) {
fast_part = false;
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_5k));
} else if (args.type == ArchArgs::LP8K || args.type == ArchArgs::HX8K) {
fast_part = args.type == ArchArgs::HX8K;
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(chipdb_blob_8k));
} else {
log_error("Unsupported iCE40 chip type.\n");
}
#endif
package_info = nullptr;
for (int i = 0; i < chip_info->num_packages; i++) {
if (chip_info->packages_data[i].name.get() == args.package) {
package_info = &(chip_info->packages_data[i]);
break;
}
}
if (package_info == nullptr)
log_error("Unsupported package '%s'.\n", args.package.c_str());
bel_carry.resize(chip_info->num_bels);
bel_to_cell.resize(chip_info->num_bels);
wire_to_net.resize(chip_info->num_wires);
pip_to_net.resize(chip_info->num_pips);
switches_locked.resize(chip_info->num_switches);
}
// -----------------------------------------------------------------------
std::string Arch::getChipName() const
{
#ifdef ICE40_HX1K_ONLY
if (args.type == ArchArgs::HX1K) {
return "Lattice LP1K";
} else {
log_error("Unsupported iCE40 chip type.\n");
}
#else
if (args.type == ArchArgs::LP384) {
return "Lattice LP384";
} else if (args.type == ArchArgs::LP1K) {
return "Lattice LP1K";
} else if (args.type == ArchArgs::HX1K) {
return "Lattice HX1K";
} else if (args.type == ArchArgs::UP5K) {
return "Lattice UP5K";
} else if (args.type == ArchArgs::LP8K) {
return "Lattice LP8K";
} else if (args.type == ArchArgs::HX8K) {
return "Lattice HX8K";
} else {
log_error("Unknown chip\n");
}
#endif
}
// -----------------------------------------------------------------------
IdString Arch::archArgsToId(ArchArgs args) const
{
if (args.type == ArchArgs::LP384)
return id("lp384");
if (args.type == ArchArgs::LP1K)
return id("lp1k");
if (args.type == ArchArgs::HX1K)
return id("hx1k");
if (args.type == ArchArgs::UP5K)
return id("up5k");
if (args.type == ArchArgs::LP8K)
return id("lp8k");
if (args.type == ArchArgs::HX8K)
return id("hx8k");
return IdString();
}
// -----------------------------------------------------------------------
BelId Arch::getBelByName(IdString name) const
{
BelId ret;
if (bel_by_name.empty()) {
for (int i = 0; i < chip_info->num_bels; i++)
bel_by_name[id(chip_info->bel_data[i].name.get())] = i;
}
auto it = bel_by_name.find(name);
if (it != bel_by_name.end())
ret.index = it->second;
return ret;
}
BelId Arch::getBelByLocation(Loc loc) const
{
BelId bel;
if (bel_by_loc.empty()) {
for (int i = 0; i < chip_info->num_bels; i++) {
BelId b;
b.index = i;
bel_by_loc[getBelLocation(b)] = i;
}
}
auto it = bel_by_loc.find(loc);
if (it != bel_by_loc.end())
bel.index = it->second;
return bel;
}
BelRange Arch::getBelsByTile(int x, int y) const
{
// In iCE40 chipdb bels at the same tile are consecutive and dense z ordinates
// are used
BelRange br;
br.b.cursor = Arch::getBelByLocation(Loc(x, y, 0)).index;
br.e.cursor = br.b.cursor;
if (br.e.cursor != -1) {
while (br.e.cursor < chip_info->num_bels && chip_info->bel_data[br.e.cursor].x == x &&
chip_info->bel_data[br.e.cursor].y == y)
br.e.cursor++;
}
return br;
}
PortType Arch::getBelPinType(BelId bel, IdString pin) const
{
NPNR_ASSERT(bel != BelId());
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();
if (num_bel_wires < 7) {
for (int i = 0; i < num_bel_wires; i++) {
if (bel_wires[i].port == pin.index)
return PortType(bel_wires[i].type);
}
} else {
int b = 0, e = num_bel_wires - 1;
while (b <= e) {
int i = (b + e) / 2;
if (bel_wires[i].port == pin.index)
return PortType(bel_wires[i].type);
if (bel_wires[i].port > pin.index)
e = i - 1;
else
b = i + 1;
}
}
return PORT_INOUT;
}
WireId Arch::getBelPinWire(BelId bel, IdString pin) const
{
WireId ret;
NPNR_ASSERT(bel != BelId());
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();
if (num_bel_wires < 7) {
for (int i = 0; i < num_bel_wires; i++) {
if (bel_wires[i].port == pin.index) {
ret.index = bel_wires[i].wire_index;
break;
}
}
} else {
int b = 0, e = num_bel_wires - 1;
while (b <= e) {
int i = (b + e) / 2;
if (bel_wires[i].port == pin.index) {
ret.index = bel_wires[i].wire_index;
break;
}
if (bel_wires[i].port > pin.index)
e = i - 1;
else
b = i + 1;
}
}
return ret;
}
std::vector<IdString> Arch::getBelPins(BelId bel) const
{
std::vector<IdString> ret;
NPNR_ASSERT(bel != BelId());
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();
for (int i = 0; i < num_bel_wires; i++)
ret.push_back(IdString(bel_wires[i].port));
return ret;
}
// -----------------------------------------------------------------------
WireId Arch::getWireByName(IdString name) const
{
WireId ret;
if (wire_by_name.empty()) {
for (int i = 0; i < chip_info->num_wires; i++)
wire_by_name[id(chip_info->wire_data[i].name.get())] = i;
}
auto it = wire_by_name.find(name);
if (it != wire_by_name.end())
ret.index = it->second;
return ret;
}
IdString Arch::getWireType(WireId wire) const
{
NPNR_ASSERT(wire != WireId());
switch (chip_info->wire_data[wire.index].type) {
case WireInfoPOD::WIRE_TYPE_NONE:
return IdString();
case WireInfoPOD::WIRE_TYPE_GLB2LOCAL:
return id("GLB2LOCAL");
case WireInfoPOD::WIRE_TYPE_GLB_NETWK:
return id("GLB_NETWK");
case WireInfoPOD::WIRE_TYPE_LOCAL:
return id("LOCAL");
case WireInfoPOD::WIRE_TYPE_LUTFF_IN:
return id("LUTFF_IN");
case WireInfoPOD::WIRE_TYPE_LUTFF_IN_LUT:
return id("LUTFF_IN_LUT");
case WireInfoPOD::WIRE_TYPE_LUTFF_LOUT:
return id("LUTFF_LOUT");
case WireInfoPOD::WIRE_TYPE_LUTFF_OUT:
return id("LUTFF_OUT");
case WireInfoPOD::WIRE_TYPE_LUTFF_COUT:
return id("LUTFF_COUT");
case WireInfoPOD::WIRE_TYPE_LUTFF_GLOBAL:
return id("LUTFF_GLOBAL");
case WireInfoPOD::WIRE_TYPE_CARRY_IN_MUX:
return id("CARRY_IN_MUX");
case WireInfoPOD::WIRE_TYPE_SP4_V:
return id("SP4_V");
case WireInfoPOD::WIRE_TYPE_SP4_H:
return id("SP4_H");
case WireInfoPOD::WIRE_TYPE_SP12_V:
return id("SP12_V");
case WireInfoPOD::WIRE_TYPE_SP12_H:
return id("SP12_H");
}
return IdString();
}
// -----------------------------------------------------------------------
PipId Arch::getPipByName(IdString name) const
{
PipId ret;
if (pip_by_name.empty()) {
for (int i = 0; i < chip_info->num_pips; i++) {
PipId pip;
pip.index = i;
pip_by_name[getPipName(pip)] = i;
}
}
auto it = pip_by_name.find(name);
if (it != pip_by_name.end())
ret.index = it->second;
return ret;
}
IdString Arch::getPipName(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
#if 1
int x = chip_info->pip_data[pip.index].x;
int y = chip_info->pip_data[pip.index].y;
std::string src_name = chip_info->wire_data[chip_info->pip_data[pip.index].src].name.get();
std::replace(src_name.begin(), src_name.end(), '/', '.');
std::string dst_name = chip_info->wire_data[chip_info->pip_data[pip.index].dst].name.get();
std::replace(dst_name.begin(), dst_name.end(), '/', '.');
return id("X" + std::to_string(x) + "/Y" + std::to_string(y) + "/" + src_name + ".->." + dst_name);
#else
return id(chip_info->pip_data[pip.index].name.get());
#endif
}
// -----------------------------------------------------------------------
BelId Arch::getPackagePinBel(const std::string &pin) const
{
for (int i = 0; i < package_info->num_pins; i++) {
if (package_info->pins[i].name.get() == pin) {
BelId id;
id.index = package_info->pins[i].bel_index;
return id;
}
}
return BelId();
}
std::string Arch::getBelPackagePin(BelId bel) const
{
for (int i = 0; i < package_info->num_pins; i++) {
if (package_info->pins[i].bel_index == bel.index) {
return std::string(package_info->pins[i].name.get());
}
}
return "";
}
// -----------------------------------------------------------------------
GroupId Arch::getGroupByName(IdString name) const
{
for (auto g : getGroups())
if (getGroupName(g) == name)
return g;
return GroupId();
}
IdString Arch::getGroupName(GroupId group) const
{
std::string suffix;
switch (group.type) {
case GroupId::TYPE_FRAME:
suffix = "tile";
break;
case GroupId::TYPE_MAIN_SW:
suffix = "main_sw";
break;
case GroupId::TYPE_LOCAL_SW:
suffix = "local_sw";
break;
case GroupId::TYPE_LC0_SW:
suffix = "lc0_sw";
break;
case GroupId::TYPE_LC1_SW:
suffix = "lc1_sw";
break;
case GroupId::TYPE_LC2_SW:
suffix = "lc2_sw";
break;
case GroupId::TYPE_LC3_SW:
suffix = "lc3_sw";
break;
case GroupId::TYPE_LC4_SW:
suffix = "lc4_sw";
break;
case GroupId::TYPE_LC5_SW:
suffix = "lc5_sw";
break;
case GroupId::TYPE_LC6_SW:
suffix = "lc6_sw";
break;
case GroupId::TYPE_LC7_SW:
suffix = "lc7_sw";
break;
default:
return IdString();
}
return id("X" + std::to_string(group.x) + "/Y" + std::to_string(group.y) + "/" + suffix);
}
std::vector<GroupId> Arch::getGroups() const
{
std::vector<GroupId> ret;
for (int y = 0; y < chip_info->height; y++) {
for (int x = 0; x < chip_info->width; x++) {
TileType type = chip_info->tile_grid[y * chip_info->width + x];
if (type == TILE_NONE)
continue;
GroupId group;
group.type = GroupId::TYPE_FRAME;
group.x = x;
group.y = y;
// ret.push_back(group);
group.type = GroupId::TYPE_MAIN_SW;
ret.push_back(group);
group.type = GroupId::TYPE_LOCAL_SW;
ret.push_back(group);
if (type == TILE_LOGIC) {
group.type = GroupId::TYPE_LC0_SW;
ret.push_back(group);
group.type = GroupId::TYPE_LC1_SW;
ret.push_back(group);
group.type = GroupId::TYPE_LC2_SW;
ret.push_back(group);
group.type = GroupId::TYPE_LC3_SW;
ret.push_back(group);
group.type = GroupId::TYPE_LC4_SW;
ret.push_back(group);
group.type = GroupId::TYPE_LC5_SW;
ret.push_back(group);
group.type = GroupId::TYPE_LC6_SW;
ret.push_back(group);
group.type = GroupId::TYPE_LC7_SW;
ret.push_back(group);
}
}
}
return ret;
}
std::vector<BelId> Arch::getGroupBels(GroupId group) const
{
std::vector<BelId> ret;
return ret;
}
std::vector<WireId> Arch::getGroupWires(GroupId group) const
{
std::vector<WireId> ret;
return ret;
}
std::vector<PipId> Arch::getGroupPips(GroupId group) const
{
std::vector<PipId> ret;
return ret;
}
std::vector<GroupId> Arch::getGroupGroups(GroupId group) const
{
std::vector<GroupId> ret;
return ret;
}
// -----------------------------------------------------------------------
bool Arch::getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const
{
const auto &driver = net_info->driver;
if (driver.port == id_COUT && sink.port == id_CIN) {
auto driver_loc = getBelLocation(driver.cell->bel);
auto sink_loc = getBelLocation(sink.cell->bel);
if (driver_loc.y == sink_loc.y)
budget = 0;
else
switch (args.type) {
#ifndef ICE40_HX1K_ONLY
case ArchArgs::HX8K:
#endif
case ArchArgs::HX1K:
budget = 190;
break;
#ifndef ICE40_HX1K_ONLY
case ArchArgs::LP384:
case ArchArgs::LP1K:
case ArchArgs::LP8K:
budget = 290;
break;
case ArchArgs::UP5K:
budget = 560;
break;
#endif
default:
log_error("Unsupported iCE40 chip type.\n");
}
return true;
}
return false;
}
// -----------------------------------------------------------------------
bool Arch::place()
{
Placer1Cfg cfg;
cfg.constraintWeight = placer_constraintWeight;
return placer1(getCtx(), cfg);
}
bool Arch::route()
{
Router1Cfg cfg;
return router1(getCtx(), cfg);
}
// -----------------------------------------------------------------------
DecalXY Arch::getBelDecal(BelId bel) const
{
DecalXY decalxy;
decalxy.decal.type = DecalId::TYPE_BEL;
decalxy.decal.index = bel.index;
decalxy.decal.active = bel_to_cell.at(bel.index) != nullptr;
return decalxy;
}
DecalXY Arch::getWireDecal(WireId wire) const
{
DecalXY decalxy;
decalxy.decal.type = DecalId::TYPE_WIRE;
decalxy.decal.index = wire.index;
decalxy.decal.active = wire_to_net.at(wire.index) != nullptr;
return decalxy;
}
DecalXY Arch::getPipDecal(PipId pip) const
{
DecalXY decalxy;
decalxy.decal.type = DecalId::TYPE_PIP;
decalxy.decal.index = pip.index;
decalxy.decal.active = pip_to_net.at(pip.index) != nullptr;
return decalxy;
};
DecalXY Arch::getGroupDecal(GroupId group) const
{
DecalXY decalxy;
decalxy.decal.type = DecalId::TYPE_GROUP;
decalxy.decal.index = (group.type << 16) | (group.x << 8) | (group.y);
decalxy.decal.active = true;
return decalxy;
};
std::vector<GraphicElement> Arch::getDecalGraphics(DecalId decal) const
{
std::vector<GraphicElement> ret;
if (decal.type == DecalId::TYPE_GROUP) {
int type = (decal.index >> 16) & 255;
int x = (decal.index >> 8) & 255;
int y = decal.index & 255;
if (type == GroupId::TYPE_FRAME) {
GraphicElement el;
el.type = GraphicElement::TYPE_LINE;
el.style = GraphicElement::STYLE_FRAME;
el.x1 = x + 0.01, el.x2 = x + 0.02, el.y1 = y + 0.01, el.y2 = y + 0.01;
ret.push_back(el);
el.x1 = x + 0.01, el.x2 = x + 0.01, el.y1 = y + 0.01, el.y2 = y + 0.02;
ret.push_back(el);
el.x1 = x + 0.99, el.x2 = x + 0.98, el.y1 = y + 0.01, el.y2 = y + 0.01;
ret.push_back(el);
el.x1 = x + 0.99, el.x2 = x + 0.99, el.y1 = y + 0.01, el.y2 = y + 0.02;
ret.push_back(el);
el.x1 = x + 0.99, el.x2 = x + 0.98, el.y1 = y + 0.99, el.y2 = y + 0.99;
ret.push_back(el);
el.x1 = x + 0.99, el.x2 = x + 0.99, el.y1 = y + 0.99, el.y2 = y + 0.98;
ret.push_back(el);
el.x1 = x + 0.01, el.x2 = x + 0.02, el.y1 = y + 0.99, el.y2 = y + 0.99;
ret.push_back(el);
el.x1 = x + 0.01, el.x2 = x + 0.01, el.y1 = y + 0.99, el.y2 = y + 0.98;
ret.push_back(el);
}
if (type == GroupId::TYPE_MAIN_SW) {
GraphicElement el;
el.type = GraphicElement::TYPE_BOX;
el.style = GraphicElement::STYLE_FRAME;
el.x1 = x + main_swbox_x1;
el.x2 = x + main_swbox_x2;
el.y1 = y + main_swbox_y1;
el.y2 = y + main_swbox_y2;
ret.push_back(el);
}
if (type == GroupId::TYPE_LOCAL_SW) {
GraphicElement el;
el.type = GraphicElement::TYPE_BOX;
el.style = GraphicElement::STYLE_FRAME;
el.x1 = x + local_swbox_x1;
el.x2 = x + local_swbox_x2;
el.y1 = y + local_swbox_y1;
el.y2 = y + local_swbox_y2;
ret.push_back(el);
}
if (GroupId::TYPE_LC0_SW <= type && type <= GroupId::TYPE_LC7_SW) {
GraphicElement el;
el.type = GraphicElement::TYPE_BOX;
el.style = GraphicElement::STYLE_FRAME;
el.x1 = x + lut_swbox_x1;
el.x2 = x + lut_swbox_x2;
el.y1 = y + logic_cell_y1 + logic_cell_pitch * (type - GroupId::TYPE_LC0_SW);
el.y2 = y + logic_cell_y2 + logic_cell_pitch * (type - GroupId::TYPE_LC0_SW);
ret.push_back(el);
}
}
if (decal.type == DecalId::TYPE_WIRE) {
int n = chip_info->wire_data[decal.index].num_segments;
const WireSegmentPOD *p = chip_info->wire_data[decal.index].segments.get();
GraphicElement::style_t style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
for (int i = 0; i < n; i++)
gfxTileWire(ret, p[i].x, p[i].y, GfxTileWireId(p[i].index), style);
}
if (decal.type == DecalId::TYPE_PIP) {
const PipInfoPOD &p = chip_info->pip_data[decal.index];
GraphicElement::style_t style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_HIDDEN;
gfxTilePip(ret, p.x, p.y, GfxTileWireId(p.src_seg), GfxTileWireId(p.dst_seg), style);
}
if (decal.type == DecalId::TYPE_BEL) {
BelId bel;
bel.index = decal.index;
auto bel_type = getBelType(bel);
if (bel_type == id_ICESTORM_LC) {
GraphicElement el;
el.type = GraphicElement::TYPE_BOX;
el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 +
(chip_info->bel_data[bel.index].z) * logic_cell_pitch;
el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 +
(chip_info->bel_data[bel.index].z) * logic_cell_pitch;
ret.push_back(el);
}
if (bel_type == id_SB_IO) {
GraphicElement el;
el.type = GraphicElement::TYPE_BOX;
el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 +
(4 * chip_info->bel_data[bel.index].z) * logic_cell_pitch;
el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 +
(4 * chip_info->bel_data[bel.index].z + 3) * logic_cell_pitch;
ret.push_back(el);
}
if (bel_type == id_ICESTORM_RAM) {
for (int i = 0; i < 2; i++) {
GraphicElement el;
el.type = GraphicElement::TYPE_BOX;
el.style = decal.active ? GraphicElement::STYLE_ACTIVE : GraphicElement::STYLE_INACTIVE;
el.x1 = chip_info->bel_data[bel.index].x + logic_cell_x1;
el.x2 = chip_info->bel_data[bel.index].x + logic_cell_x2;
el.y1 = chip_info->bel_data[bel.index].y + logic_cell_y1 + i;
el.y2 = chip_info->bel_data[bel.index].y + logic_cell_y2 + i + 7 * logic_cell_pitch;
ret.push_back(el);
}
}
}
return ret;
}
// -----------------------------------------------------------------------
bool Arch::getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const
{
for (int i = 0; i < chip_info->num_timing_cells; i++) {
const auto &tc = chip_info->cell_timing[i];
if (tc.type == cell->type.index) {
for (int j = 0; j < tc.num_paths; j++) {
const auto &path = tc.path_delays[j];
if (path.from_port == fromPort.index && path.to_port == toPort.index) {
if (fast_part)
delay.delay = path.fast_delay;
else
delay.delay = path.slow_delay;
return true;
}
}
break;
}
}
return false;
}
// Get the port class, also setting clockPort to associated clock if applicable
TimingPortClass Arch::getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockPort) const
{
if (cell->type == id_ICESTORM_LC) {
if (port == id_CLK)
return TMG_CLOCK_INPUT;
if (port == id_CIN)
return TMG_COMB_INPUT;
if (port == id_COUT || port == id_LO)
return TMG_COMB_OUTPUT;
if (cell->lcInfo.dffEnable) {
clockPort = id_CLK;
if (port == id_O)
return TMG_REGISTER_OUTPUT;
else
return TMG_REGISTER_INPUT;
} else {
if (port == id_O)
return TMG_COMB_OUTPUT;
else
return TMG_COMB_INPUT;
}
} else if (cell->type == id_ICESTORM_RAM) {
if (port == id_RCLK || port == id_WCLK)
return TMG_CLOCK_INPUT;
if (port.str(this)[0] == 'R')
clockPort = id_RCLK;
else
clockPort = id_WCLK;
if (cell->ports.at(port).type == PORT_OUT)
return TMG_REGISTER_OUTPUT;
else
return TMG_REGISTER_INPUT;
} else if (cell->type == id_ICESTORM_DSP || cell->type == id_ICESTORM_SPRAM) {
clockPort = id_CLK;
if (port == id_CLK)
return TMG_CLOCK_INPUT;
else if (cell->ports.at(port).type == PORT_OUT)
return TMG_REGISTER_OUTPUT;
else
return TMG_REGISTER_INPUT;
} else if (cell->type == id_SB_IO) {
if (port == id_D_IN_0 || port == id_D_IN_1)
return TMG_STARTPOINT;
if (port == id_D_OUT_0 || port == id_D_OUT_1 || port == id_OUTPUT_ENABLE)
return TMG_ENDPOINT;
return TMG_IGNORE;
} else if (cell->type == id_ICESTORM_PLL) {
if (port == id_PLLOUT_A || port == id_PLLOUT_B)
return TMG_GEN_CLOCK;
return TMG_IGNORE;
} else if (cell->type == id_ICESTORM_LFOSC) {
if (port == id_CLKLF)
return TMG_GEN_CLOCK;
return TMG_IGNORE;
} else if (cell->type == id_ICESTORM_HFOSC) {
if (port == id_CLKHF)
return TMG_GEN_CLOCK;
return TMG_IGNORE;
} else if (cell->type == id_SB_GB) {
if (port == id_GLOBAL_BUFFER_OUTPUT)
return TMG_COMB_OUTPUT;
return TMG_COMB_INPUT;
} else if (cell->type == id_SB_WARMBOOT) {
return TMG_ENDPOINT;
}
log_error("no timing info for port '%s' of cell type '%s'\n", port.c_str(this), cell->type.c_str(this));
}
bool Arch::isGlobalNet(const NetInfo *net) const
{
if (net == nullptr)
return false;
return net->driver.cell != nullptr && net->driver.port == id_GLOBAL_BUFFER_OUTPUT;
}
// Assign arch arg info
void Arch::assignArchInfo()
{
for (auto &net : getCtx()->nets) {
NetInfo *ni = net.second.get();
if (isGlobalNet(ni))
ni->is_global = true;
ni->is_enable = false;
ni->is_reset = false;
for (auto usr : ni->users) {
if (is_enable_port(this, usr))
ni->is_enable = true;
if (is_reset_port(this, usr))
ni->is_reset = true;
}
}
for (auto &cell : getCtx()->cells) {
CellInfo *ci = cell.second.get();
assignCellInfo(ci);
}
}
void Arch::assignCellInfo(CellInfo *cell)
{
cell->belType = cell->type;
if (cell->type == id_ICESTORM_LC) {
cell->lcInfo.dffEnable = bool_or_default(cell->params, id_DFF_ENABLE);
cell->lcInfo.carryEnable = bool_or_default(cell->params, id_CARRY_ENABLE);
cell->lcInfo.negClk = bool_or_default(cell->params, id_NEG_CLK);
cell->lcInfo.clk = get_net_or_empty(cell, id_CLK);
cell->lcInfo.cen = get_net_or_empty(cell, id_CEN);
cell->lcInfo.sr = get_net_or_empty(cell, id_SR);
cell->lcInfo.inputCount = 0;
if (get_net_or_empty(cell, id_I0))
cell->lcInfo.inputCount++;
if (get_net_or_empty(cell, id_I1))
cell->lcInfo.inputCount++;
if (get_net_or_empty(cell, id_I2))
cell->lcInfo.inputCount++;
if (get_net_or_empty(cell, id_I3))
cell->lcInfo.inputCount++;
}
}
NEXTPNR_NAMESPACE_END

841
xc7/arch.h Normal file
View File

@ -0,0 +1,841 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef NEXTPNR_H
#error Include "arch.h" via "nextpnr.h" only.
#endif
NEXTPNR_NAMESPACE_BEGIN
/**** Everything in this section must be kept in sync with chipdb.py ****/
template <typename T> struct RelPtr
{
int32_t offset;
// void set(const T *ptr) {
// offset = reinterpret_cast<const char*>(ptr) -
// reinterpret_cast<const char*>(this);
// }
const T *get() const { return reinterpret_cast<const T *>(reinterpret_cast<const char *>(this) + offset); }
const T &operator[](size_t index) const { return get()[index]; }
const T &operator*() const { return *(get()); }
const T *operator->() const { return get(); }
};
NPNR_PACKED_STRUCT(struct BelWirePOD {
int32_t port;
int32_t type;
int32_t wire_index;
});
NPNR_PACKED_STRUCT(struct BelInfoPOD {
RelPtr<char> name;
int32_t type;
int32_t num_bel_wires;
RelPtr<BelWirePOD> bel_wires;
int8_t x, y, z;
int8_t padding_0;
});
NPNR_PACKED_STRUCT(struct BelPortPOD {
int32_t bel_index;
int32_t port;
});
NPNR_PACKED_STRUCT(struct PipInfoPOD {
enum PipFlags : uint32_t
{
FLAG_NONE = 0,
FLAG_ROUTETHRU = 1,
FLAG_NOCARRY = 2
};
// RelPtr<char> name;
int32_t src, dst;
int32_t fast_delay;
int32_t slow_delay;
int8_t x, y;
int16_t src_seg, dst_seg;
int16_t switch_mask;
int32_t switch_index;
PipFlags flags;
});
NPNR_PACKED_STRUCT(struct WireSegmentPOD {
int8_t x, y;
int16_t index;
});
NPNR_PACKED_STRUCT(struct WireInfoPOD {
enum WireType : int8_t
{
WIRE_TYPE_NONE = 0,
WIRE_TYPE_GLB2LOCAL = 1,
WIRE_TYPE_GLB_NETWK = 2,
WIRE_TYPE_LOCAL = 3,
WIRE_TYPE_LUTFF_IN = 4,
WIRE_TYPE_LUTFF_IN_LUT = 5,
WIRE_TYPE_LUTFF_LOUT = 6,
WIRE_TYPE_LUTFF_OUT = 7,
WIRE_TYPE_LUTFF_COUT = 8,
WIRE_TYPE_LUTFF_GLOBAL = 9,
WIRE_TYPE_CARRY_IN_MUX = 10,
WIRE_TYPE_SP4_V = 11,
WIRE_TYPE_SP4_H = 12,
WIRE_TYPE_SP12_V = 13,
WIRE_TYPE_SP12_H = 14
};
RelPtr<char> name;
int32_t num_uphill, num_downhill;
RelPtr<int32_t> pips_uphill, pips_downhill;
int32_t num_bel_pins;
RelPtr<BelPortPOD> bel_pins;
int32_t num_segments;
RelPtr<WireSegmentPOD> segments;
int32_t fast_delay;
int32_t slow_delay;
int8_t x, y, z;
WireType type;
});
NPNR_PACKED_STRUCT(struct PackagePinPOD {
RelPtr<char> name;
int32_t bel_index;
});
NPNR_PACKED_STRUCT(struct PackageInfoPOD {
RelPtr<char> name;
int32_t num_pins;
RelPtr<PackagePinPOD> pins;
});
enum TileType : uint32_t
{
TILE_NONE = 0,
TILE_LOGIC = 1,
TILE_IO = 2,
TILE_RAMB = 3,
TILE_RAMT = 4,
TILE_DSP0 = 5,
TILE_DSP1 = 6,
TILE_DSP2 = 7,
TILE_DSP3 = 8,
TILE_IPCON = 9
};
NPNR_PACKED_STRUCT(struct ConfigBitPOD { int8_t row, col; });
NPNR_PACKED_STRUCT(struct ConfigEntryPOD {
RelPtr<char> name;
int32_t num_bits;
RelPtr<ConfigBitPOD> bits;
});
NPNR_PACKED_STRUCT(struct TileInfoPOD {
int8_t cols, rows;
int16_t num_config_entries;
RelPtr<ConfigEntryPOD> entries;
});
static const int max_switch_bits = 5;
NPNR_PACKED_STRUCT(struct SwitchInfoPOD {
int32_t num_bits;
int32_t bel;
int8_t x, y;
ConfigBitPOD cbits[max_switch_bits];
});
NPNR_PACKED_STRUCT(struct IerenInfoPOD {
int8_t iox, ioy, ioz;
int8_t ierx, iery, ierz;
});
NPNR_PACKED_STRUCT(struct BitstreamInfoPOD {
int32_t num_switches, num_ierens;
RelPtr<TileInfoPOD> tiles_nonrouting;
RelPtr<SwitchInfoPOD> switches;
RelPtr<IerenInfoPOD> ierens;
});
NPNR_PACKED_STRUCT(struct BelConfigEntryPOD {
RelPtr<char> entry_name;
RelPtr<char> cbit_name;
int8_t x, y;
int16_t padding;
});
// Stores mapping between bel parameters and config bits,
// for extra cells where this mapping is non-trivial
NPNR_PACKED_STRUCT(struct BelConfigPOD {
int32_t bel_index;
int32_t num_entries;
RelPtr<BelConfigEntryPOD> entries;
});
NPNR_PACKED_STRUCT(struct CellPathDelayPOD {
int32_t from_port;
int32_t to_port;
int32_t fast_delay;
int32_t slow_delay;
});
NPNR_PACKED_STRUCT(struct CellTimingPOD {
int32_t type;
int32_t num_paths;
RelPtr<CellPathDelayPOD> path_delays;
});
NPNR_PACKED_STRUCT(struct ChipInfoPOD {
int32_t width, height;
int32_t num_bels, num_wires, num_pips;
int32_t num_switches, num_belcfgs, num_packages;
int32_t num_timing_cells;
RelPtr<BelInfoPOD> bel_data;
RelPtr<WireInfoPOD> wire_data;
RelPtr<PipInfoPOD> pip_data;
RelPtr<TileType> tile_grid;
RelPtr<BitstreamInfoPOD> bits_info;
RelPtr<BelConfigPOD> bel_config;
RelPtr<PackageInfoPOD> packages_data;
RelPtr<CellTimingPOD> cell_timing;
});
#if defined(_MSC_VER)
extern const char *chipdb_blob_384;
extern const char *chipdb_blob_1k;
extern const char *chipdb_blob_5k;
extern const char *chipdb_blob_8k;
#else
extern const char chipdb_blob_384[];
extern const char chipdb_blob_1k[];
extern const char chipdb_blob_5k[];
extern const char chipdb_blob_8k[];
#endif
/************************ End of chipdb section. ************************/
struct BelIterator
{
int cursor;
BelIterator operator++()
{
cursor++;
return *this;
}
BelIterator operator++(int)
{
BelIterator prior(*this);
cursor++;
return prior;
}
bool operator!=(const BelIterator &other) const { return cursor != other.cursor; }
bool operator==(const BelIterator &other) const { return cursor == other.cursor; }
BelId operator*() const
{
BelId ret;
ret.index = cursor;
return ret;
}
};
struct BelRange
{
BelIterator b, e;
BelIterator begin() const { return b; }
BelIterator end() const { return e; }
};
// -----------------------------------------------------------------------
struct BelPinIterator
{
const BelPortPOD *ptr = nullptr;
void operator++() { ptr++; }
bool operator!=(const BelPinIterator &other) const { return ptr != other.ptr; }
BelPin operator*() const
{
BelPin ret;
ret.bel.index = ptr->bel_index;
ret.pin = ptr->port;
return ret;
}
};
struct BelPinRange
{
BelPinIterator b, e;
BelPinIterator begin() const { return b; }
BelPinIterator end() const { return e; }
};
// -----------------------------------------------------------------------
struct WireIterator
{
int cursor = -1;
void operator++() { cursor++; }
bool operator!=(const WireIterator &other) const { return cursor != other.cursor; }
WireId operator*() const
{
WireId ret;
ret.index = cursor;
return ret;
}
};
struct WireRange
{
WireIterator b, e;
WireIterator begin() const { return b; }
WireIterator end() const { return e; }
};
// -----------------------------------------------------------------------
struct AllPipIterator
{
int cursor = -1;
void operator++() { cursor++; }
bool operator!=(const AllPipIterator &other) const { return cursor != other.cursor; }
PipId operator*() const
{
PipId ret;
ret.index = cursor;
return ret;
}
};
struct AllPipRange
{
AllPipIterator b, e;
AllPipIterator begin() const { return b; }
AllPipIterator end() const { return e; }
};
// -----------------------------------------------------------------------
struct PipIterator
{
const int *cursor = nullptr;
void operator++() { cursor++; }
bool operator!=(const PipIterator &other) const { return cursor != other.cursor; }
PipId operator*() const
{
PipId ret;
ret.index = *cursor;
return ret;
}
};
struct PipRange
{
PipIterator b, e;
PipIterator begin() const { return b; }
PipIterator end() const { return e; }
};
struct ArchArgs
{
enum ArchArgsTypes
{
NONE,
LP384,
LP1K,
LP8K,
HX1K,
HX8K,
UP5K
} type = NONE;
std::string package;
};
struct Arch : BaseCtx
{
bool fast_part;
const ChipInfoPOD *chip_info;
const PackageInfoPOD *package_info;
mutable std::unordered_map<IdString, int> bel_by_name;
mutable std::unordered_map<IdString, int> wire_by_name;
mutable std::unordered_map<IdString, int> pip_by_name;
mutable std::unordered_map<Loc, int> bel_by_loc;
std::vector<bool> bel_carry;
std::vector<CellInfo *> bel_to_cell;
std::vector<NetInfo *> wire_to_net;
std::vector<NetInfo *> pip_to_net;
std::vector<NetInfo *> switches_locked;
ArchArgs args;
Arch(ArchArgs args);
std::string getChipName() const;
IdString archId() const { return id("ice40"); }
ArchArgs archArgs() const { return args; }
IdString archArgsToId(ArchArgs args) const;
// -------------------------------------------------
int getGridDimX() const { return 34; }
int getGridDimY() const { return 34; }
int getTileBelDimZ(int, int) const { return 8; }
int getTilePipDimZ(int, int) const { return 1; }
// -------------------------------------------------
BelId getBelByName(IdString name) const;
IdString getBelName(BelId bel) const
{
NPNR_ASSERT(bel != BelId());
return id(chip_info->bel_data[bel.index].name.get());
}
uint32_t getBelChecksum(BelId bel) const { return bel.index; }
void bindBel(BelId bel, CellInfo *cell, PlaceStrength strength)
{
NPNR_ASSERT(bel != BelId());
NPNR_ASSERT(bel_to_cell[bel.index] == nullptr);
bel_to_cell[bel.index] = cell;
bel_carry[bel.index] = (cell->type == id_ICESTORM_LC && cell->lcInfo.carryEnable);
cell->bel = bel;
cell->belStrength = strength;
refreshUiBel(bel);
}
void unbindBel(BelId bel)
{
NPNR_ASSERT(bel != BelId());
NPNR_ASSERT(bel_to_cell[bel.index] != nullptr);
bel_to_cell[bel.index]->bel = BelId();
bel_to_cell[bel.index]->belStrength = STRENGTH_NONE;
bel_to_cell[bel.index] = nullptr;
bel_carry[bel.index] = false;
refreshUiBel(bel);
}
bool checkBelAvail(BelId bel) const
{
NPNR_ASSERT(bel != BelId());
return bel_to_cell[bel.index] == nullptr;
}
CellInfo *getBoundBelCell(BelId bel) const
{
NPNR_ASSERT(bel != BelId());
return bel_to_cell[bel.index];
}
CellInfo *getConflictingBelCell(BelId bel) const
{
NPNR_ASSERT(bel != BelId());
return bel_to_cell[bel.index];
}
BelRange getBels() const
{
BelRange range;
range.b.cursor = 0;
range.e.cursor = chip_info->num_bels;
return range;
}
Loc getBelLocation(BelId bel) const
{
Loc loc;
loc.x = chip_info->bel_data[bel.index].x;
loc.y = chip_info->bel_data[bel.index].y;
loc.z = chip_info->bel_data[bel.index].z;
return loc;
}
BelId getBelByLocation(Loc loc) const;
BelRange getBelsByTile(int x, int y) const;
bool getBelGlobalBuf(BelId bel) const { return chip_info->bel_data[bel.index].type == ID_SB_GB; }
IdString getBelType(BelId bel) const
{
NPNR_ASSERT(bel != BelId());
return IdString(chip_info->bel_data[bel.index].type);
}
WireId getBelPinWire(BelId bel, IdString pin) const;
PortType getBelPinType(BelId bel, IdString pin) const;
std::vector<IdString> getBelPins(BelId bel) const;
// -------------------------------------------------
WireId getWireByName(IdString name) const;
IdString getWireName(WireId wire) const
{
NPNR_ASSERT(wire != WireId());
return id(chip_info->wire_data[wire.index].name.get());
}
IdString getWireType(WireId wire) const;
uint32_t getWireChecksum(WireId wire) const { return wire.index; }
void bindWire(WireId wire, NetInfo *net, PlaceStrength strength)
{
NPNR_ASSERT(wire != WireId());
NPNR_ASSERT(wire_to_net[wire.index] == nullptr);
wire_to_net[wire.index] = net;
net->wires[wire].pip = PipId();
net->wires[wire].strength = strength;
refreshUiWire(wire);
}
void unbindWire(WireId wire)
{
NPNR_ASSERT(wire != WireId());
NPNR_ASSERT(wire_to_net[wire.index] != nullptr);
auto &net_wires = wire_to_net[wire.index]->wires;
auto it = net_wires.find(wire);
NPNR_ASSERT(it != net_wires.end());
auto pip = it->second.pip;
if (pip != PipId()) {
pip_to_net[pip.index] = nullptr;
switches_locked[chip_info->pip_data[pip.index].switch_index] = nullptr;
}
net_wires.erase(it);
wire_to_net[wire.index] = nullptr;
refreshUiWire(wire);
}
bool checkWireAvail(WireId wire) const
{
NPNR_ASSERT(wire != WireId());
return wire_to_net[wire.index] == nullptr;
}
NetInfo *getBoundWireNet(WireId wire) const
{
NPNR_ASSERT(wire != WireId());
return wire_to_net[wire.index];
}
NetInfo *getConflictingWireNet(WireId wire) const
{
NPNR_ASSERT(wire != WireId());
return wire_to_net[wire.index];
}
DelayInfo getWireDelay(WireId wire) const
{
DelayInfo delay;
NPNR_ASSERT(wire != WireId());
if (fast_part)
delay.delay = chip_info->wire_data[wire.index].fast_delay;
else
delay.delay = chip_info->wire_data[wire.index].slow_delay;
return delay;
}
BelPinRange getWireBelPins(WireId wire) const
{
BelPinRange range;
NPNR_ASSERT(wire != WireId());
range.b.ptr = chip_info->wire_data[wire.index].bel_pins.get();
range.e.ptr = range.b.ptr + chip_info->wire_data[wire.index].num_bel_pins;
return range;
}
WireRange getWires() const
{
WireRange range;
range.b.cursor = 0;
range.e.cursor = chip_info->num_wires;
return range;
}
// -------------------------------------------------
PipId getPipByName(IdString name) const;
void bindPip(PipId pip, NetInfo *net, PlaceStrength strength)
{
NPNR_ASSERT(pip != PipId());
NPNR_ASSERT(pip_to_net[pip.index] == nullptr);
NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] == nullptr);
pip_to_net[pip.index] = net;
switches_locked[chip_info->pip_data[pip.index].switch_index] = net;
WireId dst;
dst.index = chip_info->pip_data[pip.index].dst;
NPNR_ASSERT(wire_to_net[dst.index] == nullptr);
wire_to_net[dst.index] = net;
net->wires[dst].pip = pip;
net->wires[dst].strength = strength;
refreshUiPip(pip);
refreshUiWire(dst);
}
void unbindPip(PipId pip)
{
NPNR_ASSERT(pip != PipId());
NPNR_ASSERT(pip_to_net[pip.index] != nullptr);
NPNR_ASSERT(switches_locked[chip_info->pip_data[pip.index].switch_index] != nullptr);
WireId dst;
dst.index = chip_info->pip_data[pip.index].dst;
NPNR_ASSERT(wire_to_net[dst.index] != nullptr);
wire_to_net[dst.index] = nullptr;
pip_to_net[pip.index]->wires.erase(dst);
pip_to_net[pip.index] = nullptr;
switches_locked[chip_info->pip_data[pip.index].switch_index] = nullptr;
refreshUiPip(pip);
refreshUiWire(dst);
}
bool checkPipAvail(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
auto &pi = chip_info->pip_data[pip.index];
auto &si = chip_info->bits_info->switches[pi.switch_index];
if (switches_locked[pi.switch_index] != nullptr)
return false;
if (pi.flags & PipInfoPOD::FLAG_ROUTETHRU) {
NPNR_ASSERT(si.bel >= 0);
if (bel_to_cell[si.bel] != nullptr)
return false;
}
if (pi.flags & PipInfoPOD::FLAG_NOCARRY) {
NPNR_ASSERT(si.bel >= 0);
if (bel_carry[si.bel])
return false;
}
return true;
}
NetInfo *getBoundPipNet(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
return pip_to_net[pip.index];
}
NetInfo *getConflictingPipNet(PipId pip) const
{
NPNR_ASSERT(pip != PipId());
return switches_locked[chip_info->pip_data[pip.index].switch_index];
}
AllPipRange getPips() const
{
AllPipRange range;
range.b.cursor = 0;
range.e.cursor = chip_info->num_pips;
return range;
}
Loc getPipLocation(PipId pip) const
{
Loc loc;
loc.x = chip_info->pip_data[pip.index].x;
loc.y = chip_info->pip_data[pip.index].y;
loc.z = 0;
return loc;
}
IdString getPipName(PipId pip) const;
IdString getPipType(PipId pip) const { return IdString(); }
uint32_t getPipChecksum(PipId pip) const { return pip.index; }
WireId getPipSrcWire(PipId pip) const
{
WireId wire;
NPNR_ASSERT(pip != PipId());
wire.index = chip_info->pip_data[pip.index].src;
return wire;
}
WireId getPipDstWire(PipId pip) const
{
WireId wire;
NPNR_ASSERT(pip != PipId());
wire.index = chip_info->pip_data[pip.index].dst;
return wire;
}
DelayInfo getPipDelay(PipId pip) const
{
DelayInfo delay;
NPNR_ASSERT(pip != PipId());
if (fast_part)
delay.delay = chip_info->pip_data[pip.index].fast_delay;
else
delay.delay = chip_info->pip_data[pip.index].slow_delay;
return delay;
}
PipRange getPipsDownhill(WireId wire) const
{
PipRange range;
NPNR_ASSERT(wire != WireId());
range.b.cursor = chip_info->wire_data[wire.index].pips_downhill.get();
range.e.cursor = range.b.cursor + chip_info->wire_data[wire.index].num_downhill;
return range;
}
PipRange getPipsUphill(WireId wire) const
{
PipRange range;
NPNR_ASSERT(wire != WireId());
range.b.cursor = chip_info->wire_data[wire.index].pips_uphill.get();
range.e.cursor = range.b.cursor + chip_info->wire_data[wire.index].num_uphill;
return range;
}
PipRange getWireAliases(WireId wire) const
{
PipRange range;
NPNR_ASSERT(wire != WireId());
range.b.cursor = nullptr;
range.e.cursor = nullptr;
return range;
}
BelId getPackagePinBel(const std::string &pin) const;
std::string getBelPackagePin(BelId bel) const;
// -------------------------------------------------
GroupId getGroupByName(IdString name) const;
IdString getGroupName(GroupId group) const;
std::vector<GroupId> getGroups() const;
std::vector<BelId> getGroupBels(GroupId group) const;
std::vector<WireId> getGroupWires(GroupId group) const;
std::vector<PipId> getGroupPips(GroupId group) const;
std::vector<GroupId> getGroupGroups(GroupId group) const;
// -------------------------------------------------
delay_t estimateDelay(WireId src, WireId dst) const;
delay_t predictDelay(const NetInfo *net_info, const PortRef &sink) const;
delay_t getDelayEpsilon() const { return 20; }
delay_t getRipupDelayPenalty() const { return 200; }
float getDelayNS(delay_t v) const { return v * 0.001; }
uint32_t getDelayChecksum(delay_t v) const { return v; }
bool getBudgetOverride(const NetInfo *net_info, const PortRef &sink, delay_t &budget) const;
// -------------------------------------------------
bool pack();
bool place();
bool route();
// -------------------------------------------------
std::vector<GraphicElement> getDecalGraphics(DecalId decal) const;
DecalXY getBelDecal(BelId bel) const;
DecalXY getWireDecal(WireId wire) const;
DecalXY getPipDecal(PipId pip) const;
DecalXY getGroupDecal(GroupId group) const;
// -------------------------------------------------
// Get the delay through a cell from one port to another, returning false
// if no path exists
bool getCellDelay(const CellInfo *cell, IdString fromPort, IdString toPort, DelayInfo &delay) const;
// Get the port class, also setting clockDomain if applicable
TimingPortClass getPortTimingClass(const CellInfo *cell, IdString port, IdString &clockDomain) const;
// Return true if a port is a net
bool isGlobalNet(const NetInfo *net) const;
// -------------------------------------------------
// Perform placement validity checks, returning false on failure (all
// implemented in arch_place.cc)
// Whether or not a given cell can be placed at a given Bel
// This is not intended for Bel type checks, but finer-grained constraints
// such as conflicting set/reset signals, etc
bool isValidBelForCell(CellInfo *cell, BelId bel) const;
// Return true whether all Bels at a given location are valid
bool isBelLocationValid(BelId bel) const;
// Helper function for above
bool logicCellsCompatible(const CellInfo** it, const size_t size) const;
// -------------------------------------------------
// Assign architecure-specific arguments to nets and cells, which must be
// called between packing or further
// netlist modifications, and validity checks
void assignArchInfo();
void assignCellInfo(CellInfo *cell);
// -------------------------------------------------
BelPin getIOBSharingPLLPin(BelId pll, IdString pll_pin) const
{
auto wire = getBelPinWire(pll, pll_pin);
for (auto src_bel : getWireBelPins(wire)) {
if (getBelType(src_bel.bel) == id_SB_IO && src_bel.pin == id_D_IN_0) {
return src_bel;
}
}
NPNR_ASSERT_FALSE("Expected PLL pin to share an output with an SB_IO D_IN_{0,1}");
}
float placer_constraintWeight = 10;
};
void ice40DelayFuzzerMain(Context *ctx);
NEXTPNR_NAMESPACE_END

162
xc7/arch_place.cc Normal file
View File

@ -0,0 +1,162 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "cells.h"
#include "nextpnr.h"
#include "util.h"
#include <boost/range/iterator_range.hpp>
NEXTPNR_NAMESPACE_BEGIN
bool Arch::logicCellsCompatible(const CellInfo** it, const size_t size) const
{
bool dffs_exist = false, dffs_neg = false;
const NetInfo *cen = nullptr, *clk = nullptr, *sr = nullptr;
int locals_count = 0;
for (auto cell : boost::make_iterator_range(it, it+size)) {
NPNR_ASSERT(cell->belType == id_ICESTORM_LC);
if (cell->lcInfo.dffEnable) {
if (!dffs_exist) {
dffs_exist = true;
cen = cell->lcInfo.cen;
clk = cell->lcInfo.clk;
sr = cell->lcInfo.sr;
if (cen != nullptr && !cen->is_global)
locals_count++;
if (clk != nullptr && !clk->is_global)
locals_count++;
if (sr != nullptr && !sr->is_global)
locals_count++;
if (cell->lcInfo.negClk) {
dffs_neg = true;
}
} else {
if (cen != cell->lcInfo.cen)
return false;
if (clk != cell->lcInfo.clk)
return false;
if (sr != cell->lcInfo.sr)
return false;
if (dffs_neg != cell->lcInfo.negClk)
return false;
}
}
locals_count += cell->lcInfo.inputCount;
}
return locals_count <= 32;
}
bool Arch::isBelLocationValid(BelId bel) const
{
if (getBelType(bel) == id_ICESTORM_LC) {
std::array<const CellInfo *, 8> bel_cells;
size_t num_cells = 0;
Loc bel_loc = getBelLocation(bel);
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
CellInfo *ci_other = getBoundBelCell(bel_other);
if (ci_other != nullptr)
bel_cells[num_cells++] = ci_other;
}
return logicCellsCompatible(bel_cells.data(), num_cells);
} else {
CellInfo *ci = getBoundBelCell(bel);
if (ci == nullptr)
return true;
else
return isValidBelForCell(ci, bel);
}
}
bool Arch::isValidBelForCell(CellInfo *cell, BelId bel) const
{
if (cell->type == id_ICESTORM_LC) {
NPNR_ASSERT(getBelType(bel) == id_ICESTORM_LC);
std::array<const CellInfo *, 8> bel_cells;
size_t num_cells = 0;
Loc bel_loc = getBelLocation(bel);
for (auto bel_other : getBelsByTile(bel_loc.x, bel_loc.y)) {
CellInfo *ci_other = getBoundBelCell(bel_other);
if (ci_other != nullptr && bel_other != bel)
bel_cells[num_cells++] = ci_other;
}
bel_cells[num_cells++] = cell;
return logicCellsCompatible(bel_cells.data(), num_cells);
} else if (cell->type == id_SB_IO) {
// Do not allow placement of input SB_IOs on blocks where there a PLL is outputting to.
// Find shared PLL by looking for driving bel siblings from D_IN_0
// that are a PLL clock output.
auto wire = getBelPinWire(bel, id_D_IN_0);
IdString pll_bel_pin;
BelId pll_bel;
for (auto pin : getWireBelPins(wire)) {
if (pin.pin == id_PLLOUT_A || pin.pin == id_PLLOUT_B) {
pll_bel = pin.bel;
pll_bel_pin = pin.pin;
break;
}
}
// Is there a PLL that shares this IO buffer?
if (pll_bel.index != -1) {
auto pll_cell = getBoundBelCell(pll_bel);
// Is a PLL placed in this PLL bel?
if (pll_cell != nullptr) {
// Is the shared port driving a net?
auto pi = pll_cell->ports[pll_bel_pin];
if (pi.net != nullptr) {
// Are we perhaps a PAD INPUT Bel that can be placed here?
if (pll_cell->attrs[id("BEL_PAD_INPUT")] == getBelName(bel).str(this)) {
return true;
}
return false;
}
}
}
return getBelPackagePin(bel) != "";
} else if (cell->type == id_SB_GB) {
NPNR_ASSERT(cell->ports.at(id_GLOBAL_BUFFER_OUTPUT).net != nullptr);
const NetInfo *net = cell->ports.at(id_GLOBAL_BUFFER_OUTPUT).net;
IdString glb_net = getWireName(getBelPinWire(bel, id_GLOBAL_BUFFER_OUTPUT));
int glb_id = std::stoi(std::string("") + glb_net.str(this).back());
if (net->is_reset && net->is_enable)
return false;
else if (net->is_reset)
return (glb_id % 2) == 0;
else if (net->is_enable)
return (glb_id % 2) == 1;
else
return true;
} else {
// TODO: IO cell clock checks
return true;
}
}
NEXTPNR_NAMESPACE_END

154
xc7/arch_pybindings.cc Normal file
View File

@ -0,0 +1,154 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef NO_PYTHON
#include "arch_pybindings.h"
#include "nextpnr.h"
#include "pybindings.h"
NEXTPNR_NAMESPACE_BEGIN
void arch_wrap_python()
{
using namespace PythonConversion;
class_<ArchArgs>("ArchArgs").def_readwrite("type", &ArchArgs::type);
enum_<decltype(std::declval<ArchArgs>().type)>("iCE40Type")
.value("NONE", ArchArgs::NONE)
.value("LP384", ArchArgs::LP384)
.value("LP1K", ArchArgs::LP1K)
.value("LP8K", ArchArgs::LP8K)
.value("HX1K", ArchArgs::HX1K)
.value("HX8K", ArchArgs::HX8K)
.value("UP5K", ArchArgs::UP5K)
.export_values();
class_<BelId>("BelId").def_readwrite("index", &BelId::index);
class_<WireId>("WireId").def_readwrite("index", &WireId::index);
class_<PipId>("PipId").def_readwrite("index", &PipId::index);
class_<BelPin>("BelPin").def_readwrite("bel", &BelPin::bel).def_readwrite("pin", &BelPin::pin);
auto arch_cls = class_<Arch, Arch *, bases<BaseCtx>, boost::noncopyable>("Arch", init<ArchArgs>());
auto ctx_cls = class_<Context, Context *, bases<Arch>, boost::noncopyable>("Context", no_init)
.def("checksum", &Context::checksum)
.def("pack", &Context::pack)
.def("place", &Context::place)
.def("route", &Context::route);
fn_wrapper_1a<Context, decltype(&Context::getBelType), &Context::getBelType, conv_to_str<IdString>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelType");
fn_wrapper_1a<Context, decltype(&Context::checkBelAvail), &Context::checkBelAvail, pass_through<bool>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "checkBelAvail");
fn_wrapper_1a<Context, decltype(&Context::getBelChecksum), &Context::getBelChecksum, pass_through<uint32_t>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelChecksum");
fn_wrapper_3a_v<Context, decltype(&Context::bindBel), &Context::bindBel, conv_from_str<BelId>,
addr_and_unwrap<CellInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindBel");
fn_wrapper_1a_v<Context, decltype(&Context::unbindBel), &Context::unbindBel, conv_from_str<BelId>>::def_wrap(
ctx_cls, "unbindBel");
fn_wrapper_1a<Context, decltype(&Context::getBoundBelCell), &Context::getBoundBelCell, deref_and_wrap<CellInfo>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBoundBelCell");
fn_wrapper_1a<Context, decltype(&Context::getConflictingBelCell), &Context::getConflictingBelCell,
deref_and_wrap<CellInfo>, conv_from_str<BelId>>::def_wrap(ctx_cls, "getConflictingBelCell");
fn_wrapper_0a<Context, decltype(&Context::getBels), &Context::getBels, wrap_context<BelRange>>::def_wrap(ctx_cls,
"getBels");
fn_wrapper_2a<Context, decltype(&Context::getBelPinWire), &Context::getBelPinWire, conv_to_str<WireId>,
conv_from_str<BelId>, conv_from_str<IdString>>::def_wrap(ctx_cls, "getBelPinWire");
fn_wrapper_1a<Context, decltype(&Context::getWireBelPins), &Context::getWireBelPins, wrap_context<BelPinRange>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireBelPins");
fn_wrapper_1a<Context, decltype(&Context::getWireChecksum), &Context::getWireChecksum, pass_through<uint32_t>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireChecksum");
fn_wrapper_3a_v<Context, decltype(&Context::bindWire), &Context::bindWire, conv_from_str<WireId>,
addr_and_unwrap<NetInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindWire");
fn_wrapper_1a_v<Context, decltype(&Context::unbindWire), &Context::unbindWire, conv_from_str<WireId>>::def_wrap(
ctx_cls, "unbindWire");
fn_wrapper_1a<Context, decltype(&Context::checkWireAvail), &Context::checkWireAvail, pass_through<bool>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "checkWireAvail");
fn_wrapper_1a<Context, decltype(&Context::getBoundWireNet), &Context::getBoundWireNet, deref_and_wrap<NetInfo>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "getBoundWireNet");
fn_wrapper_1a<Context, decltype(&Context::getConflictingWireNet), &Context::getConflictingWireNet,
deref_and_wrap<NetInfo>, conv_from_str<WireId>>::def_wrap(ctx_cls, "getConflictingWireNet");
fn_wrapper_0a<Context, decltype(&Context::getWires), &Context::getWires, wrap_context<WireRange>>::def_wrap(
ctx_cls, "getWires");
fn_wrapper_0a<Context, decltype(&Context::getPips), &Context::getPips, wrap_context<AllPipRange>>::def_wrap(
ctx_cls, "getPips");
fn_wrapper_1a<Context, decltype(&Context::getPipChecksum), &Context::getPipChecksum, pass_through<uint32_t>,
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipChecksum");
fn_wrapper_3a_v<Context, decltype(&Context::bindPip), &Context::bindPip, conv_from_str<PipId>,
addr_and_unwrap<NetInfo>, pass_through<PlaceStrength>>::def_wrap(ctx_cls, "bindPip");
fn_wrapper_1a_v<Context, decltype(&Context::unbindPip), &Context::unbindPip, conv_from_str<PipId>>::def_wrap(
ctx_cls, "unbindPip");
fn_wrapper_1a<Context, decltype(&Context::checkPipAvail), &Context::checkPipAvail, pass_through<bool>,
conv_from_str<PipId>>::def_wrap(ctx_cls, "checkPipAvail");
fn_wrapper_1a<Context, decltype(&Context::getBoundPipNet), &Context::getBoundPipNet, deref_and_wrap<NetInfo>,
conv_from_str<PipId>>::def_wrap(ctx_cls, "getBoundPipNet");
fn_wrapper_1a<Context, decltype(&Context::getConflictingPipNet), &Context::getConflictingPipNet,
deref_and_wrap<NetInfo>, conv_from_str<PipId>>::def_wrap(ctx_cls, "getConflictingPipNet");
fn_wrapper_1a<Context, decltype(&Context::getPipsDownhill), &Context::getPipsDownhill, wrap_context<PipRange>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsDownhill");
fn_wrapper_1a<Context, decltype(&Context::getPipsUphill), &Context::getPipsUphill, wrap_context<PipRange>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "getPipsUphill");
fn_wrapper_1a<Context, decltype(&Context::getWireAliases), &Context::getWireAliases, wrap_context<PipRange>,
conv_from_str<WireId>>::def_wrap(ctx_cls, "getWireAliases");
fn_wrapper_1a<Context, decltype(&Context::getPipSrcWire), &Context::getPipSrcWire, conv_to_str<WireId>,
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipSrcWire");
fn_wrapper_1a<Context, decltype(&Context::getPipDstWire), &Context::getPipDstWire, conv_to_str<WireId>,
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDstWire");
fn_wrapper_1a<Context, decltype(&Context::getPipDelay), &Context::getPipDelay, pass_through<DelayInfo>,
conv_from_str<PipId>>::def_wrap(ctx_cls, "getPipDelay");
fn_wrapper_1a<Context, decltype(&Context::getPackagePinBel), &Context::getPackagePinBel, conv_to_str<BelId>,
pass_through<std::string>>::def_wrap(ctx_cls, "getPackagePinBel");
fn_wrapper_1a<Context, decltype(&Context::getBelPackagePin), &Context::getBelPackagePin, pass_through<std::string>,
conv_from_str<BelId>>::def_wrap(ctx_cls, "getBelPackagePin");
fn_wrapper_0a<Context, decltype(&Context::getChipName), &Context::getChipName, pass_through<std::string>>::def_wrap(
ctx_cls, "getChipName");
fn_wrapper_0a<Context, decltype(&Context::archId), &Context::archId, conv_to_str<IdString>>::def_wrap(ctx_cls,
"archId");
typedef std::unordered_map<IdString, std::unique_ptr<CellInfo>> CellMap;
typedef std::unordered_map<IdString, std::unique_ptr<NetInfo>> NetMap;
readonly_wrapper<Context, decltype(&Context::cells), &Context::cells, wrap_context<CellMap &>>::def_wrap(ctx_cls,
"cells");
readonly_wrapper<Context, decltype(&Context::nets), &Context::nets, wrap_context<NetMap &>>::def_wrap(ctx_cls,
"nets");
WRAP_RANGE(Bel, conv_to_str<BelId>);
WRAP_RANGE(Wire, conv_to_str<WireId>);
WRAP_RANGE(AllPip, conv_to_str<PipId>);
WRAP_RANGE(Pip, conv_to_str<PipId>);
WRAP_MAP_UPTR(CellMap, "IdCellMap");
WRAP_MAP_UPTR(NetMap, "IdNetMap");
}
NEXTPNR_NAMESPACE_END
#endif // NO_PYTHON

62
xc7/arch_pybindings.h Normal file
View File

@ -0,0 +1,62 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef ARCH_PYBINDINGS_H
#define ARCH_PYBINDINGS_H
#ifndef NO_PYTHON
#include "nextpnr.h"
#include "pybindings.h"
#include "pywrappers.h"
NEXTPNR_NAMESPACE_BEGIN
namespace PythonConversion {
template <> struct string_converter<BelId>
{
BelId from_str(Context *ctx, std::string name) { return ctx->getBelByName(ctx->id(name)); }
std::string to_str(Context *ctx, BelId id)
{
if (id == BelId())
throw bad_wrap();
return ctx->getBelName(id).str(ctx);
}
};
template <> struct string_converter<WireId>
{
WireId from_str(Context *ctx, std::string name) { return ctx->getWireByName(ctx->id(name)); }
std::string to_str(Context *ctx, WireId id) { return ctx->getWireName(id).str(ctx); }
};
template <> struct string_converter<PipId>
{
PipId from_str(Context *ctx, std::string name) { return ctx->getPipByName(ctx->id(name)); }
std::string to_str(Context *ctx, PipId id) { return ctx->getPipName(id).str(ctx); }
};
} // namespace PythonConversion
NEXTPNR_NAMESPACE_END
#endif
#endif

194
xc7/archdefs.h Normal file
View File

@ -0,0 +1,194 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef NEXTPNR_H
#error Include "archdefs.h" via "nextpnr.h" only.
#endif
NEXTPNR_NAMESPACE_BEGIN
typedef int delay_t;
struct DelayInfo
{
delay_t delay = 0;
delay_t minRaiseDelay() const { return delay; }
delay_t maxRaiseDelay() const { return delay; }
delay_t minFallDelay() const { return delay; }
delay_t maxFallDelay() const { return delay; }
delay_t minDelay() const { return delay; }
delay_t maxDelay() const { return delay; }
DelayInfo operator+(const DelayInfo &other) const
{
DelayInfo ret;
ret.delay = this->delay + other.delay;
return ret;
}
};
// -----------------------------------------------------------------------
enum ConstIds
{
ID_NONE
#define X(t) , ID_##t
#include "constids.inc"
#undef X
};
#define X(t) static constexpr auto id_##t = IdString(ID_##t);
#include "constids.inc"
#undef X
struct BelId
{
int32_t index = -1;
bool operator==(const BelId &other) const { return index == other.index; }
bool operator!=(const BelId &other) const { return index != other.index; }
};
struct WireId
{
int32_t index = -1;
bool operator==(const WireId &other) const { return index == other.index; }
bool operator!=(const WireId &other) const { return index != other.index; }
};
struct PipId
{
int32_t index = -1;
bool operator==(const PipId &other) const { return index == other.index; }
bool operator!=(const PipId &other) const { return index != other.index; }
};
struct GroupId
{
enum : int8_t
{
TYPE_NONE,
TYPE_FRAME,
TYPE_MAIN_SW,
TYPE_LOCAL_SW,
TYPE_LC0_SW,
TYPE_LC1_SW,
TYPE_LC2_SW,
TYPE_LC3_SW,
TYPE_LC4_SW,
TYPE_LC5_SW,
TYPE_LC6_SW,
TYPE_LC7_SW
} type = TYPE_NONE;
int8_t x = 0, y = 0;
bool operator==(const GroupId &other) const { return (type == other.type) && (x == other.x) && (y == other.y); }
bool operator!=(const GroupId &other) const { return (type != other.type) || (x != other.x) || (y == other.y); }
};
struct DecalId
{
enum : int8_t
{
TYPE_NONE,
TYPE_BEL,
TYPE_WIRE,
TYPE_PIP,
TYPE_GROUP
} type = TYPE_NONE;
int32_t index = -1;
bool active = false;
bool operator==(const DecalId &other) const { return (type == other.type) && (index == other.index); }
bool operator!=(const DecalId &other) const { return (type != other.type) || (index != other.index); }
};
struct ArchNetInfo
{
bool is_global = false;
bool is_reset = false, is_enable = false;
};
struct NetInfo;
struct ArchCellInfo
{
IdString belType;
union
{
struct
{
bool dffEnable;
bool carryEnable;
bool negClk;
int inputCount;
const NetInfo *clk, *cen, *sr;
} lcInfo;
};
};
NEXTPNR_NAMESPACE_END
namespace std {
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX BelId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX BelId &bel) const noexcept { return hash<int>()(bel.index); }
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX WireId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX WireId &wire) const noexcept
{
return hash<int>()(wire.index);
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX PipId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX PipId &pip) const noexcept { return hash<int>()(pip.index); }
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX GroupId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX GroupId &group) const noexcept
{
std::size_t seed = 0;
boost::hash_combine(seed, hash<int>()(group.type));
boost::hash_combine(seed, hash<int>()(group.x));
boost::hash_combine(seed, hash<int>()(group.y));
return seed;
}
};
template <> struct hash<NEXTPNR_NAMESPACE_PREFIX DecalId>
{
std::size_t operator()(const NEXTPNR_NAMESPACE_PREFIX DecalId &decal) const noexcept
{
std::size_t seed = 0;
boost::hash_combine(seed, hash<int>()(decal.type));
boost::hash_combine(seed, hash<int>()(decal.index));
return seed;
}
};
} // namespace std

6
xc7/benchmark/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
hx8kdemo.log
hx8kdemo.blif
hx8kdemo.json
hx8kdemo_[an][0-9].asc
hx8kdemo_[an][0-9].log
report_[an][0-9].txt

View File

@ -0,0 +1,40 @@
# Pinout for the iCE40-HX8K Breakout Board
set_io clk J3
set_io flash_csb R12
set_io flash_clk R11
set_io flash_io0 P12
set_io flash_io1 P11
# for QSPI mode the flash chip on the iCE40-HX8K Breakout Board
# must be replaced with one that supports QSPI and the IO2 and IO3
# pins must be soldered to T9 and P8 (center on J3)
set_io flash_io2 T9
set_io flash_io3 P8
set_io ser_tx B12
set_io ser_rx B10
# left on J3
set_io debug_ser_tx T1
set_io debug_ser_rx R3
# right on J3
set_io debug_flash_csb T15
set_io debug_flash_clk R16
set_io debug_flash_io0 N12
set_io debug_flash_io1 P13
set_io debug_flash_io2 T13
set_io debug_flash_io3 T14
set_io leds[7] B5 # D9
set_io leds[6] B4 # D8
set_io leds[5] A2 # D7
set_io leds[4] A1 # D6
set_io leds[3] C5 # D5
set_io leds[2] C4 # D4
set_io leds[1] B3 # D3
set_io leds[0] C3 # D2

139
xc7/benchmark/hx8kdemo.v Normal file
View File

@ -0,0 +1,139 @@
/*
* PicoSoC - A simple example SoC using PicoRV32
*
* Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
module hx8kdemo (
input clk,
output ser_tx,
input ser_rx,
output [7:0] leds,
output flash_csb,
output flash_clk,
inout flash_io0,
inout flash_io1,
inout flash_io2,
inout flash_io3,
output debug_ser_tx,
output debug_ser_rx,
output debug_flash_csb,
output debug_flash_clk,
output debug_flash_io0,
output debug_flash_io1,
output debug_flash_io2,
output debug_flash_io3
);
reg [5:0] reset_cnt = 0;
wire resetn = &reset_cnt;
always @(posedge clk) begin
reset_cnt <= reset_cnt + !resetn;
end
wire flash_io0_oe, flash_io0_do, flash_io0_di;
wire flash_io1_oe, flash_io1_do, flash_io1_di;
wire flash_io2_oe, flash_io2_do, flash_io2_di;
wire flash_io3_oe, flash_io3_do, flash_io3_di;
SB_IO #(
.PIN_TYPE(6'b 1010_01),
.PULLUP(1'b 0)
) flash_io_buf [3:0] (
.PACKAGE_PIN({flash_io3, flash_io2, flash_io1, flash_io0}),
.OUTPUT_ENABLE({flash_io3_oe, flash_io2_oe, flash_io1_oe, flash_io0_oe}),
.D_OUT_0({flash_io3_do, flash_io2_do, flash_io1_do, flash_io0_do}),
.D_IN_0({flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di})
);
wire iomem_valid;
reg iomem_ready;
wire [3:0] iomem_wstrb;
wire [31:0] iomem_addr;
wire [31:0] iomem_wdata;
reg [31:0] iomem_rdata;
reg [31:0] gpio;
assign leds = gpio;
always @(posedge clk) begin
if (!resetn) begin
gpio <= 0;
end else begin
iomem_ready <= 0;
if (iomem_valid && !iomem_ready && iomem_addr[31:24] == 8'h 03) begin
iomem_ready <= 1;
iomem_rdata <= gpio;
if (iomem_wstrb[0]) gpio[ 7: 0] <= iomem_wdata[ 7: 0];
if (iomem_wstrb[1]) gpio[15: 8] <= iomem_wdata[15: 8];
if (iomem_wstrb[2]) gpio[23:16] <= iomem_wdata[23:16];
if (iomem_wstrb[3]) gpio[31:24] <= iomem_wdata[31:24];
end
end
end
picosoc soc (
.clk (clk ),
.resetn (resetn ),
.ser_tx (ser_tx ),
.ser_rx (ser_rx ),
.flash_csb (flash_csb ),
.flash_clk (flash_clk ),
.flash_io0_oe (flash_io0_oe),
.flash_io1_oe (flash_io1_oe),
.flash_io2_oe (flash_io2_oe),
.flash_io3_oe (flash_io3_oe),
.flash_io0_do (flash_io0_do),
.flash_io1_do (flash_io1_do),
.flash_io2_do (flash_io2_do),
.flash_io3_do (flash_io3_do),
.flash_io0_di (flash_io0_di),
.flash_io1_di (flash_io1_di),
.flash_io2_di (flash_io2_di),
.flash_io3_di (flash_io3_di),
.irq_5 (1'b0 ),
.irq_6 (1'b0 ),
.irq_7 (1'b0 ),
.iomem_valid (iomem_valid ),
.iomem_ready (iomem_ready ),
.iomem_wstrb (iomem_wstrb ),
.iomem_addr (iomem_addr ),
.iomem_wdata (iomem_wdata ),
.iomem_rdata (iomem_rdata )
);
assign debug_ser_tx = ser_tx;
assign debug_ser_rx = ser_rx;
assign debug_flash_csb = flash_csb;
assign debug_flash_clk = flash_clk;
assign debug_flash_io0 = flash_io0_di;
assign debug_flash_io1 = flash_io1_di;
assign debug_flash_io2 = flash_io2_di;
assign debug_flash_io3 = flash_io3_di;
endmodule

2977
xc7/benchmark/picorv32.v Normal file

File diff suppressed because it is too large Load Diff

241
xc7/benchmark/picosoc.v Normal file
View File

@ -0,0 +1,241 @@
/*
* PicoSoC - A simple example SoC using PicoRV32
*
* Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
`ifndef PICORV32_REGS
`ifdef PICORV32_V
`error "picosoc.v must be read before picorv32.v!"
`endif
`define PICORV32_REGS picosoc_regs
`endif
module picosoc (
input clk,
input resetn,
output iomem_valid,
input iomem_ready,
output [ 3:0] iomem_wstrb,
output [31:0] iomem_addr,
output [31:0] iomem_wdata,
input [31:0] iomem_rdata,
input irq_5,
input irq_6,
input irq_7,
output ser_tx,
input ser_rx,
output flash_csb,
output flash_clk,
output flash_io0_oe,
output flash_io1_oe,
output flash_io2_oe,
output flash_io3_oe,
output flash_io0_do,
output flash_io1_do,
output flash_io2_do,
output flash_io3_do,
input flash_io0_di,
input flash_io1_di,
input flash_io2_di,
input flash_io3_di
);
parameter integer MEM_WORDS = 256;
parameter [31:0] STACKADDR = (4*MEM_WORDS); // end of memory
parameter [31:0] PROGADDR_RESET = 32'h 0010_0000; // 1 MB into flash
reg [31:0] irq;
wire irq_stall = 0;
wire irq_uart = 0;
always @* begin
irq = 0;
irq[3] = irq_stall;
irq[4] = irq_uart;
irq[5] = irq_5;
irq[6] = irq_6;
irq[7] = irq_7;
end
wire mem_valid;
wire mem_instr;
wire mem_ready;
wire [31:0] mem_addr;
wire [31:0] mem_wdata;
wire [3:0] mem_wstrb;
wire [31:0] mem_rdata;
wire spimem_ready;
wire [31:0] spimem_rdata;
reg ram_ready;
wire [31:0] ram_rdata;
assign iomem_valid = mem_valid && (mem_addr[31:24] > 8'h 01);
assign iomem_wstrb = mem_wstrb;
assign iomem_addr = mem_addr;
assign iomem_wdata = mem_wdata;
wire spimemio_cfgreg_sel = mem_valid && (mem_addr == 32'h 0200_0000);
wire [31:0] spimemio_cfgreg_do;
wire simpleuart_reg_div_sel = mem_valid && (mem_addr == 32'h 0200_0004);
wire [31:0] simpleuart_reg_div_do;
wire simpleuart_reg_dat_sel = mem_valid && (mem_addr == 32'h 0200_0008);
wire [31:0] simpleuart_reg_dat_do;
wire simpleuart_reg_dat_wait;
assign mem_ready = (iomem_valid && iomem_ready) || spimem_ready || ram_ready || spimemio_cfgreg_sel ||
simpleuart_reg_div_sel || (simpleuart_reg_dat_sel && !simpleuart_reg_dat_wait);
assign mem_rdata = (iomem_valid && iomem_ready) ? iomem_rdata : spimem_ready ? spimem_rdata : ram_ready ? ram_rdata :
spimemio_cfgreg_sel ? spimemio_cfgreg_do : simpleuart_reg_div_sel ? simpleuart_reg_div_do :
simpleuart_reg_dat_sel ? simpleuart_reg_dat_do : 32'h 0000_0000;
picorv32 #(
.STACKADDR(STACKADDR),
.PROGADDR_RESET(PROGADDR_RESET),
.PROGADDR_IRQ(32'h 0000_0000),
.BARREL_SHIFTER(1),
.COMPRESSED_ISA(1),
.ENABLE_MUL(1),
.ENABLE_DIV(1),
.ENABLE_IRQ(1),
.ENABLE_IRQ_QREGS(0)
) cpu (
.clk (clk ),
.resetn (resetn ),
.mem_valid (mem_valid ),
.mem_instr (mem_instr ),
.mem_ready (mem_ready ),
.mem_addr (mem_addr ),
.mem_wdata (mem_wdata ),
.mem_wstrb (mem_wstrb ),
.mem_rdata (mem_rdata ),
.irq (irq )
);
spimemio spimemio (
.clk (clk),
.resetn (resetn),
.valid (mem_valid && mem_addr >= 4*MEM_WORDS && mem_addr < 32'h 0200_0000),
.ready (spimem_ready),
.addr (mem_addr[23:0]),
.rdata (spimem_rdata),
.flash_csb (flash_csb ),
.flash_clk (flash_clk ),
.flash_io0_oe (flash_io0_oe),
.flash_io1_oe (flash_io1_oe),
.flash_io2_oe (flash_io2_oe),
.flash_io3_oe (flash_io3_oe),
.flash_io0_do (flash_io0_do),
.flash_io1_do (flash_io1_do),
.flash_io2_do (flash_io2_do),
.flash_io3_do (flash_io3_do),
.flash_io0_di (flash_io0_di),
.flash_io1_di (flash_io1_di),
.flash_io2_di (flash_io2_di),
.flash_io3_di (flash_io3_di),
.cfgreg_we(spimemio_cfgreg_sel ? mem_wstrb : 4'b 0000),
.cfgreg_di(mem_wdata),
.cfgreg_do(spimemio_cfgreg_do)
);
simpleuart simpleuart (
.clk (clk ),
.resetn (resetn ),
.ser_tx (ser_tx ),
.ser_rx (ser_rx ),
.reg_div_we (simpleuart_reg_div_sel ? mem_wstrb : 4'b 0000),
.reg_div_di (mem_wdata),
.reg_div_do (simpleuart_reg_div_do),
.reg_dat_we (simpleuart_reg_dat_sel ? mem_wstrb[0] : 1'b 0),
.reg_dat_re (simpleuart_reg_dat_sel && !mem_wstrb),
.reg_dat_di (mem_wdata),
.reg_dat_do (simpleuart_reg_dat_do),
.reg_dat_wait(simpleuart_reg_dat_wait)
);
always @(posedge clk)
ram_ready <= mem_valid && !mem_ready && mem_addr < 4*MEM_WORDS;
picosoc_mem #(.WORDS(MEM_WORDS)) memory (
.clk(clk),
.wen((mem_valid && !mem_ready && mem_addr < 4*MEM_WORDS) ? mem_wstrb : 4'b0),
.addr(mem_addr[23:2]),
.wdata(mem_wdata),
.rdata(ram_rdata)
);
endmodule
// Implementation note:
// Replace the following two modules with wrappers for your SRAM cells.
module picosoc_regs (
input clk, wen,
input [5:0] waddr,
input [5:0] raddr1,
input [5:0] raddr2,
input [31:0] wdata,
output [31:0] rdata1,
output [31:0] rdata2
);
reg [31:0] regs [0:31];
always @(posedge clk)
if (wen) regs[waddr[4:0]] <= wdata;
assign rdata1 = regs[raddr1[4:0]];
assign rdata2 = regs[raddr2[4:0]];
endmodule
module picosoc_mem #(
parameter integer WORDS = 256
) (
input clk,
input [3:0] wen,
input [21:0] addr,
input [31:0] wdata,
output reg [31:0] rdata
);
reg [31:0] mem [0:WORDS-1];
always @(posedge clk) begin
rdata <= mem[addr];
if (wen[0]) mem[addr][ 7: 0] <= wdata[ 7: 0];
if (wen[1]) mem[addr][15: 8] <= wdata[15: 8];
if (wen[2]) mem[addr][23:16] <= wdata[23:16];
if (wen[3]) mem[addr][31:24] <= wdata[31:24];
end
endmodule

View File

@ -0,0 +1,91 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"%matplotlib inline\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import subprocess, re\n",
"\n",
"gitrev = subprocess.getoutput(\"git rev-parse --short HEAD\")\n",
"\n",
"data_a = np.zeros((10, 2))\n",
"data_n = np.zeros((10, 2))\n",
"\n",
"for i in range(10):\n",
" try:\n",
" with open(\"report_a%d.txt\" % i, \"r\") as f:\n",
" for line in f:\n",
" if line.startswith(\"Total path delay:\"):\n",
" data_a[i, 0] = float(line.split()[3])\n",
" except:\n",
" data_a[i, 0] = 1.0\n",
" \n",
" try:\n",
" with open(\"report_n%d.txt\" % i, \"r\") as f:\n",
" for line in f:\n",
" if line.startswith(\"Total path delay:\"):\n",
" data_n[i, 0] = float(line.split()[3])\n",
" except:\n",
" data_n[i, 0] = 1.0\n",
" \n",
" with open(\"hx8kdemo_a%d.log\" % i, \"r\") as f:\n",
" for line in f:\n",
" match = re.match(r\"real\\s+(\\d+)m(\\d+)\", line)\n",
" if match:\n",
" data_a[i, 1] = float(match.group(1)) + float(match.group(2))/60\n",
" \n",
" with open(\"hx8kdemo_n%d.log\" % i, \"r\") as f:\n",
" for line in f:\n",
" match = re.match(r\"real\\s+(\\d+)m(\\d+)\", line)\n",
" if match:\n",
" data_n[i, 1] = float(match.group(1)) + float(match.group(2))/60\n",
"\n",
"plt.figure(figsize=(9,3))\n",
"plt.title(\"nextpnr -- ice40/benchmark/ -- %s\" % gitrev)\n",
"plt.bar(np.arange(10), data_a[:, 0], color='blue')\n",
"plt.bar(15+np.arange(10), data_n[:, 0], color='red')\n",
"plt.ylabel('Longest path (ns)')\n",
"plt.xticks([5, 20], [\"arachne-pnr\", \"nextpnr\"])\n",
"plt.xlim(-2, 27)\n",
"plt.show()\n",
"\n",
"plt.figure(figsize=(9,3))\n",
"plt.title(\"nextpnr -- ice40/benchmark/ -- %s\" % gitrev)\n",
"plt.bar(np.arange(10), data_a[:, 1], color='blue')\n",
"plt.bar(15+np.arange(10), data_n[:, 1], color='red')\n",
"plt.ylabel('Runtime (minutes)')\n",
"plt.xticks([5, 20], [\"arachne-pnr\", \"nextpnr\"])\n",
"plt.xlim(-2, 27)\n",
"plt.show()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.5.2"
}
},
"nbformat": 4,
"nbformat_minor": 1
}

137
xc7/benchmark/simpleuart.v Normal file
View File

@ -0,0 +1,137 @@
/*
* PicoSoC - A simple example SoC using PicoRV32
*
* Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
module simpleuart (
input clk,
input resetn,
output ser_tx,
input ser_rx,
input [3:0] reg_div_we,
input [31:0] reg_div_di,
output [31:0] reg_div_do,
input reg_dat_we,
input reg_dat_re,
input [31:0] reg_dat_di,
output [31:0] reg_dat_do,
output reg_dat_wait
);
reg [31:0] cfg_divider;
reg [3:0] recv_state;
reg [31:0] recv_divcnt;
reg [7:0] recv_pattern;
reg [7:0] recv_buf_data;
reg recv_buf_valid;
reg [9:0] send_pattern;
reg [3:0] send_bitcnt;
reg [31:0] send_divcnt;
reg send_dummy;
assign reg_div_do = cfg_divider;
assign reg_dat_wait = reg_dat_we && (send_bitcnt || send_dummy);
assign reg_dat_do = recv_buf_valid ? recv_buf_data : ~0;
always @(posedge clk) begin
if (!resetn) begin
cfg_divider <= 1;
end else begin
if (reg_div_we[0]) cfg_divider[ 7: 0] <= reg_div_di[ 7: 0];
if (reg_div_we[1]) cfg_divider[15: 8] <= reg_div_di[15: 8];
if (reg_div_we[2]) cfg_divider[23:16] <= reg_div_di[23:16];
if (reg_div_we[3]) cfg_divider[31:24] <= reg_div_di[31:24];
end
end
always @(posedge clk) begin
if (!resetn) begin
recv_state <= 0;
recv_divcnt <= 0;
recv_pattern <= 0;
recv_buf_data <= 0;
recv_buf_valid <= 0;
end else begin
recv_divcnt <= recv_divcnt + 1;
if (reg_dat_re)
recv_buf_valid <= 0;
case (recv_state)
0: begin
if (!ser_rx)
recv_state <= 1;
recv_divcnt <= 0;
end
1: begin
if (2*recv_divcnt > cfg_divider) begin
recv_state <= 2;
recv_divcnt <= 0;
end
end
10: begin
if (recv_divcnt > cfg_divider) begin
recv_buf_data <= recv_pattern;
recv_buf_valid <= 1;
recv_state <= 0;
end
end
default: begin
if (recv_divcnt > cfg_divider) begin
recv_pattern <= {ser_rx, recv_pattern[7:1]};
recv_state <= recv_state + 1;
recv_divcnt <= 0;
end
end
endcase
end
end
assign ser_tx = send_pattern[0];
always @(posedge clk) begin
if (reg_div_we)
send_dummy <= 1;
send_divcnt <= send_divcnt + 1;
if (!resetn) begin
send_pattern <= ~0;
send_bitcnt <= 0;
send_divcnt <= 0;
send_dummy <= 1;
end else begin
if (send_dummy && !send_bitcnt) begin
send_pattern <= ~0;
send_bitcnt <= 15;
send_divcnt <= 0;
send_dummy <= 0;
end else
if (reg_dat_we && !send_bitcnt) begin
send_pattern <= {1'b1, reg_dat_di[7:0], 1'b0};
send_bitcnt <= 10;
send_divcnt <= 0;
end else
if (send_divcnt > cfg_divider && send_bitcnt) begin
send_pattern <= {1'b1, send_pattern[9:1]};
send_bitcnt <= send_bitcnt - 1;
send_divcnt <= 0;
end
end
end
endmodule

579
xc7/benchmark/spimemio.v Normal file
View File

@ -0,0 +1,579 @@
/*
* PicoSoC - A simple example SoC using PicoRV32
*
* Copyright (C) 2017 Clifford Wolf <clifford@clifford.at>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
module spimemio (
input clk, resetn,
input valid,
output ready,
input [23:0] addr,
output reg [31:0] rdata,
output flash_csb,
output flash_clk,
output flash_io0_oe,
output flash_io1_oe,
output flash_io2_oe,
output flash_io3_oe,
output flash_io0_do,
output flash_io1_do,
output flash_io2_do,
output flash_io3_do,
input flash_io0_di,
input flash_io1_di,
input flash_io2_di,
input flash_io3_di,
input [3:0] cfgreg_we,
input [31:0] cfgreg_di,
output [31:0] cfgreg_do
);
reg xfer_resetn;
reg din_valid;
wire din_ready;
reg [7:0] din_data;
reg [3:0] din_tag;
reg din_cont;
reg din_qspi;
reg din_ddr;
reg din_rd;
wire dout_valid;
wire [7:0] dout_data;
wire [3:0] dout_tag;
reg [23:0] buffer;
reg [23:0] rd_addr;
reg rd_valid;
reg rd_wait;
reg rd_inc;
assign ready = valid && (addr == rd_addr) && rd_valid;
wire jump = valid && !ready && (addr != rd_addr+4) && rd_valid;
reg softreset;
reg config_en; // cfgreg[31]
reg config_ddr; // cfgreg[22]
reg config_qspi; // cfgreg[21]
reg config_cont; // cfgreg[20]
reg [3:0] config_dummy; // cfgreg[19:16]
reg [3:0] config_oe; // cfgreg[11:8]
reg config_csb; // cfgreg[5]
reg config_clk; // cfgref[4]
reg [3:0] config_do; // cfgreg[3:0]
assign cfgreg_do[31] = config_en;
assign cfgreg_do[30:23] = 0;
assign cfgreg_do[22] = config_ddr;
assign cfgreg_do[21] = config_qspi;
assign cfgreg_do[20] = config_cont;
assign cfgreg_do[19:16] = config_dummy;
assign cfgreg_do[15:12] = 0;
assign cfgreg_do[11:8] = {flash_io3_oe, flash_io2_oe, flash_io1_oe, flash_io0_oe};
assign cfgreg_do[7:6] = 0;
assign cfgreg_do[5] = flash_csb;
assign cfgreg_do[4] = flash_clk;
assign cfgreg_do[3:0] = {flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di};
always @(posedge clk) begin
softreset <= !config_en || cfgreg_we;
if (!resetn) begin
softreset <= 1;
config_en <= 1;
config_csb <= 0;
config_clk <= 0;
config_oe <= 0;
config_do <= 0;
config_ddr <= 0;
config_qspi <= 0;
config_cont <= 0;
config_dummy <= 8;
end else begin
if (cfgreg_we[0]) begin
config_csb <= cfgreg_di[5];
config_clk <= cfgreg_di[4];
config_do <= cfgreg_di[3:0];
end
if (cfgreg_we[1]) begin
config_oe <= cfgreg_di[11:8];
end
if (cfgreg_we[2]) begin
config_ddr <= cfgreg_di[22];
config_qspi <= cfgreg_di[21];
config_cont <= cfgreg_di[20];
config_dummy <= cfgreg_di[19:16];
end
if (cfgreg_we[3]) begin
config_en <= cfgreg_di[31];
end
end
end
wire xfer_csb;
wire xfer_clk;
wire xfer_io0_oe;
wire xfer_io1_oe;
wire xfer_io2_oe;
wire xfer_io3_oe;
wire xfer_io0_do;
wire xfer_io1_do;
wire xfer_io2_do;
wire xfer_io3_do;
reg xfer_io0_90;
reg xfer_io1_90;
reg xfer_io2_90;
reg xfer_io3_90;
always @(negedge clk) begin
xfer_io0_90 <= xfer_io0_do;
xfer_io1_90 <= xfer_io1_do;
xfer_io2_90 <= xfer_io2_do;
xfer_io3_90 <= xfer_io3_do;
end
assign flash_csb = config_en ? xfer_csb : config_csb;
assign flash_clk = config_en ? xfer_clk : config_clk;
assign flash_io0_oe = config_en ? xfer_io0_oe : config_oe[0];
assign flash_io1_oe = config_en ? xfer_io1_oe : config_oe[1];
assign flash_io2_oe = config_en ? xfer_io2_oe : config_oe[2];
assign flash_io3_oe = config_en ? xfer_io3_oe : config_oe[3];
assign flash_io0_do = config_en ? (config_ddr ? xfer_io0_90 : xfer_io0_do) : config_do[0];
assign flash_io1_do = config_en ? (config_ddr ? xfer_io1_90 : xfer_io1_do) : config_do[1];
assign flash_io2_do = config_en ? (config_ddr ? xfer_io2_90 : xfer_io2_do) : config_do[2];
assign flash_io3_do = config_en ? (config_ddr ? xfer_io3_90 : xfer_io3_do) : config_do[3];
wire xfer_dspi = din_ddr && !din_qspi;
wire xfer_ddr = din_ddr && din_qspi;
spimemio_xfer xfer (
.clk (clk ),
.resetn (xfer_resetn ),
.din_valid (din_valid ),
.din_ready (din_ready ),
.din_data (din_data ),
.din_tag (din_tag ),
.din_cont (din_cont ),
.din_dspi (xfer_dspi ),
.din_qspi (din_qspi ),
.din_ddr (xfer_ddr ),
.din_rd (din_rd ),
.dout_valid (dout_valid ),
.dout_data (dout_data ),
.dout_tag (dout_tag ),
.flash_csb (xfer_csb ),
.flash_clk (xfer_clk ),
.flash_io0_oe (xfer_io0_oe ),
.flash_io1_oe (xfer_io1_oe ),
.flash_io2_oe (xfer_io2_oe ),
.flash_io3_oe (xfer_io3_oe ),
.flash_io0_do (xfer_io0_do ),
.flash_io1_do (xfer_io1_do ),
.flash_io2_do (xfer_io2_do ),
.flash_io3_do (xfer_io3_do ),
.flash_io0_di (flash_io0_di),
.flash_io1_di (flash_io1_di),
.flash_io2_di (flash_io2_di),
.flash_io3_di (flash_io3_di)
);
reg [3:0] state;
always @(posedge clk) begin
xfer_resetn <= 1;
din_valid <= 0;
if (!resetn || softreset) begin
state <= 0;
xfer_resetn <= 0;
rd_valid <= 0;
din_tag <= 0;
din_cont <= 0;
din_qspi <= 0;
din_ddr <= 0;
din_rd <= 0;
end else begin
if (dout_valid && dout_tag == 1) buffer[ 7: 0] <= dout_data;
if (dout_valid && dout_tag == 2) buffer[15: 8] <= dout_data;
if (dout_valid && dout_tag == 3) buffer[23:16] <= dout_data;
if (dout_valid && dout_tag == 4) begin
rdata <= {dout_data, buffer};
rd_addr <= rd_inc ? rd_addr + 4 : addr;
rd_valid <= 1;
rd_wait <= rd_inc;
rd_inc <= 1;
end
if (valid)
rd_wait <= 0;
case (state)
0: begin
din_valid <= 1;
din_data <= 8'h ff;
din_tag <= 0;
if (din_ready) begin
din_valid <= 0;
state <= 1;
end
end
1: begin
if (dout_valid) begin
xfer_resetn <= 0;
state <= 2;
end
end
2: begin
din_valid <= 1;
din_data <= 8'h ab;
din_tag <= 0;
if (din_ready) begin
din_valid <= 0;
state <= 3;
end
end
3: begin
if (dout_valid) begin
xfer_resetn <= 0;
state <= 4;
end
end
4: begin
rd_inc <= 0;
din_valid <= 1;
din_tag <= 0;
case ({config_ddr, config_qspi})
2'b11: din_data <= 8'h ED;
2'b01: din_data <= 8'h EB;
2'b10: din_data <= 8'h BB;
2'b00: din_data <= 8'h 03;
endcase
if (din_ready) begin
din_valid <= 0;
state <= 5;
end
end
5: begin
if (valid && !ready) begin
din_valid <= 1;
din_tag <= 0;
din_data <= addr[23:16];
din_qspi <= config_qspi;
din_ddr <= config_ddr;
if (din_ready) begin
din_valid <= 0;
state <= 6;
end
end
end
6: begin
din_valid <= 1;
din_tag <= 0;
din_data <= addr[15:8];
if (din_ready) begin
din_valid <= 0;
state <= 7;
end
end
7: begin
din_valid <= 1;
din_tag <= 0;
din_data <= addr[7:0];
if (din_ready) begin
din_valid <= 0;
din_data <= 0;
state <= config_qspi || config_ddr ? 8 : 9;
end
end
8: begin
din_valid <= 1;
din_tag <= 0;
din_data <= config_cont ? 8'h A5 : 8'h FF;
if (din_ready) begin
din_rd <= 1;
din_data <= config_dummy;
din_valid <= 0;
state <= 9;
end
end
9: begin
din_valid <= 1;
din_tag <= 1;
if (din_ready) begin
din_valid <= 0;
state <= 10;
end
end
10: begin
din_valid <= 1;
din_data <= 8'h 00;
din_tag <= 2;
if (din_ready) begin
din_valid <= 0;
state <= 11;
end
end
11: begin
din_valid <= 1;
din_tag <= 3;
if (din_ready) begin
din_valid <= 0;
state <= 12;
end
end
12: begin
if (!rd_wait || valid) begin
din_valid <= 1;
din_tag <= 4;
if (din_ready) begin
din_valid <= 0;
state <= 9;
end
end
end
endcase
if (jump) begin
rd_inc <= 0;
rd_valid <= 0;
xfer_resetn <= 0;
if (config_cont) begin
state <= 5;
end else begin
state <= 4;
din_qspi <= 0;
din_ddr <= 0;
end
din_rd <= 0;
end
end
end
endmodule
module spimemio_xfer (
input clk, resetn,
input din_valid,
output din_ready,
input [7:0] din_data,
input [3:0] din_tag,
input din_cont,
input din_dspi,
input din_qspi,
input din_ddr,
input din_rd,
output dout_valid,
output [7:0] dout_data,
output [3:0] dout_tag,
output reg flash_csb,
output reg flash_clk,
output reg flash_io0_oe,
output reg flash_io1_oe,
output reg flash_io2_oe,
output reg flash_io3_oe,
output reg flash_io0_do,
output reg flash_io1_do,
output reg flash_io2_do,
output reg flash_io3_do,
input flash_io0_di,
input flash_io1_di,
input flash_io2_di,
input flash_io3_di
);
reg [7:0] obuffer;
reg [7:0] ibuffer;
reg [3:0] count;
reg [3:0] dummy_count;
reg xfer_cont;
reg xfer_dspi;
reg xfer_qspi;
reg xfer_ddr;
reg xfer_ddr_q;
reg xfer_rd;
reg [3:0] xfer_tag;
reg [3:0] xfer_tag_q;
reg [7:0] next_obuffer;
reg [7:0] next_ibuffer;
reg [3:0] next_count;
reg fetch;
reg next_fetch;
reg last_fetch;
always @(posedge clk) begin
xfer_ddr_q <= xfer_ddr;
xfer_tag_q <= xfer_tag;
end
assign din_ready = din_valid && resetn && next_fetch;
assign dout_valid = (xfer_ddr_q ? fetch && !last_fetch : next_fetch && !fetch) && resetn;
assign dout_data = ibuffer;
assign dout_tag = xfer_tag_q;
always @* begin
flash_io0_oe = 0;
flash_io1_oe = 0;
flash_io2_oe = 0;
flash_io3_oe = 0;
flash_io0_do = 0;
flash_io1_do = 0;
flash_io2_do = 0;
flash_io3_do = 0;
next_obuffer = obuffer;
next_ibuffer = ibuffer;
next_count = count;
next_fetch = 0;
if (dummy_count == 0) begin
casez ({xfer_ddr, xfer_qspi, xfer_dspi})
3'b 000: begin
flash_io0_oe = 1;
flash_io0_do = obuffer[7];
if (flash_clk) begin
next_obuffer = {obuffer[6:0], 1'b 0};
next_count = count - |count;
end else begin
next_ibuffer = {ibuffer[6:0], flash_io1_di};
end
next_fetch = (next_count == 0);
end
3'b 01?: begin
flash_io0_oe = !xfer_rd;
flash_io1_oe = !xfer_rd;
flash_io2_oe = !xfer_rd;
flash_io3_oe = !xfer_rd;
flash_io0_do = obuffer[4];
flash_io1_do = obuffer[5];
flash_io2_do = obuffer[6];
flash_io3_do = obuffer[7];
if (flash_clk) begin
next_obuffer = {obuffer[3:0], 4'b 0000};
next_count = count - {|count, 2'b00};
end else begin
next_ibuffer = {ibuffer[3:0], flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di};
end
next_fetch = (next_count == 0);
end
3'b 11?: begin
flash_io0_oe = !xfer_rd;
flash_io1_oe = !xfer_rd;
flash_io2_oe = !xfer_rd;
flash_io3_oe = !xfer_rd;
flash_io0_do = obuffer[4];
flash_io1_do = obuffer[5];
flash_io2_do = obuffer[6];
flash_io3_do = obuffer[7];
next_obuffer = {obuffer[3:0], 4'b 0000};
next_ibuffer = {ibuffer[3:0], flash_io3_di, flash_io2_di, flash_io1_di, flash_io0_di};
next_count = count - {|count, 2'b00};
next_fetch = (next_count == 0);
end
3'b ??1: begin
flash_io0_oe = !xfer_rd;
flash_io1_oe = !xfer_rd;
flash_io0_do = obuffer[6];
flash_io1_do = obuffer[7];
if (flash_clk) begin
next_obuffer = {obuffer[5:0], 2'b 00};
next_count = count - {|count, 1'b0};
end else begin
next_ibuffer = {ibuffer[5:0], flash_io1_di, flash_io0_di};
end
next_fetch = (next_count == 0);
end
endcase
end
end
always @(posedge clk) begin
if (!resetn) begin
fetch <= 1;
last_fetch <= 1;
flash_csb <= 1;
flash_clk <= 0;
count <= 0;
dummy_count <= 0;
xfer_tag <= 0;
xfer_cont <= 0;
xfer_dspi <= 0;
xfer_qspi <= 0;
xfer_ddr <= 0;
xfer_rd <= 0;
end else begin
fetch <= next_fetch;
last_fetch <= xfer_ddr ? fetch : 1;
if (dummy_count) begin
flash_clk <= !flash_clk && !flash_csb;
dummy_count <= dummy_count - flash_clk;
end else
if (count) begin
flash_clk <= !flash_clk && !flash_csb;
obuffer <= next_obuffer;
ibuffer <= next_ibuffer;
count <= next_count;
end
if (din_valid && din_ready) begin
flash_csb <= 0;
flash_clk <= 0;
count <= 8;
dummy_count <= din_rd ? din_data : 0;
obuffer <= din_data;
xfer_tag <= din_tag;
xfer_cont <= din_cont;
xfer_dspi <= din_dspi;
xfer_qspi <= din_qspi;
xfer_ddr <= din_ddr;
xfer_rd <= din_rd;
end
end
end
endmodule

999
xc7/bitstream.cc Normal file
View File

@ -0,0 +1,999 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "bitstream.h"
#include <cctype>
#include <vector>
#include "cells.h"
#include "log.h"
NEXTPNR_NAMESPACE_BEGIN
inline TileType tile_at(const Context *ctx, int x, int y)
{
return ctx->chip_info->tile_grid[y * ctx->chip_info->width + x];
}
const ConfigEntryPOD &find_config(const TileInfoPOD &tile, const std::string &name)
{
for (int i = 0; i < tile.num_config_entries; i++) {
if (std::string(tile.entries[i].name.get()) == name) {
return tile.entries[i];
}
}
NPNR_ASSERT_FALSE_STR("unable to find config bit " + name);
}
std::tuple<int8_t, int8_t, int8_t> get_ieren(const BitstreamInfoPOD &bi, int8_t x, int8_t y, int8_t z)
{
for (int i = 0; i < bi.num_ierens; i++) {
auto ie = bi.ierens[i];
if (ie.iox == x && ie.ioy == y && ie.ioz == z) {
return std::make_tuple(ie.ierx, ie.iery, ie.ierz);
}
}
// No pin at this location
return std::make_tuple(-1, -1, -1);
};
bool get_config(const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg, const std::string &name,
int index = -1)
{
const ConfigEntryPOD &cfg = find_config(ti, name);
if (index == -1) {
for (int i = 0; i < cfg.num_bits; i++) {
return tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col);
}
} else {
return tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col);
}
return false;
}
void set_config(const TileInfoPOD &ti, std::vector<std::vector<int8_t>> &tile_cfg, const std::string &name, bool value,
int index = -1)
{
const ConfigEntryPOD &cfg = find_config(ti, name);
if (index == -1) {
for (int i = 0; i < cfg.num_bits; i++) {
int8_t &cbit = tile_cfg.at(cfg.bits[i].row).at(cfg.bits[i].col);
if (cbit && !value)
log_error("clearing already set config bit %s\n", name.c_str());
cbit = value;
}
} else {
int8_t &cbit = tile_cfg.at(cfg.bits[index].row).at(cfg.bits[index].col);
cbit = value;
if (cbit && !value)
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 invert 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)
{
auto found = cell->params.find(param);
if (found != cell->params.end())
return std::stoi(found->second);
else
return defval;
}
std::string get_param_str_or_def(const CellInfo *cell, const IdString param, std::string defval = "")
{
auto found = cell->params.find(param);
if (found != cell->params.end())
return found->second;
else
return defval;
}
char get_hexdigit(int i) { return std::string("0123456789ABCDEF").at(i); }
static const BelConfigPOD &get_ec_config(const ChipInfoPOD *chip, BelId bel)
{
for (int i = 0; i < chip->num_belcfgs; i++) {
if (chip->bel_config[i].bel_index == bel.index)
return chip->bel_config[i];
}
NPNR_ASSERT_FALSE("failed to find bel config");
}
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,
bool value, std::string prefix)
{
const ChipInfoPOD *chip = ctx->chip_info;
for (int i = 0; i < cell_cbits.num_entries; i++) {
const auto &cbit = cell_cbits.entries[i];
if (cbit.entry_name.get() == name) {
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), prefix + cbit.cbit_name.get(), value);
return;
}
}
NPNR_ASSERT_FALSE_STR("failed to config extra cell config bit " + name);
}
void configure_extra_cell(chipconfig_t &config, const Context *ctx, CellInfo *cell,
const std::vector<std::pair<std::string, int>> &params, bool string_style, std::string prefix)
{
const ChipInfoPOD *chip = ctx->chip_info;
const auto &bc = get_ec_config(chip, cell->bel);
for (auto p : params) {
std::vector<bool> value;
if (string_style) {
// Lattice's weird string style params, not sure if
// prefixes other than 0b should be supported, only 0b features in docs
std::string raw = get_param_str_or_def(cell, ctx->id(p.first), "0b0");
assert(raw.substr(0, 2) == "0b");
raw = raw.substr(2);
value.resize(raw.length());
for (int i = 0; i < (int)raw.length(); i++) {
if (raw[i] == '1') {
value[(raw.length() - 1) - i] = 1;
} else {
assert(raw[i] == '0');
value[(raw.length() - 1) - i] = 0;
}
}
} else {
int ival = get_param_or_def(cell, ctx->id(p.first), 0);
for (int i = 0; i < p.second; i++)
value.push_back((ival >> i) & 0x1);
}
value.resize(p.second);
if (p.second == 1) {
set_ec_cbit(config, ctx, bc, p.first, value.at(0), prefix);
} else {
for (int i = 0; i < p.second; i++) {
set_ec_cbit(config, ctx, bc, p.first + "_" + std::to_string(i), value.at(i), prefix);
}
}
}
}
std::string tagTileType(TileType &tile)
{
if (tile == TILE_NONE)
return "";
switch (tile) {
case TILE_LOGIC:
return ".logic_tile";
break;
case TILE_IO:
return ".io_tile";
break;
case TILE_RAMB:
return ".ramb_tile";
break;
case TILE_RAMT:
return ".ramt_tile";
break;
case TILE_DSP0:
return ".dsp0_tile";
break;
case TILE_DSP1:
return ".dsp1_tile";
break;
case TILE_DSP2:
return ".dsp2_tile";
break;
case TILE_DSP3:
return ".dsp3_tile";
break;
case TILE_IPCON:
return ".ipcon_tile";
break;
default:
NPNR_ASSERT(false);
}
}
static BelPin get_one_bel_pin(const Context *ctx, WireId wire)
{
auto pins = ctx->getWireBelPins(wire);
NPNR_ASSERT(pins.begin() != pins.end());
return *pins.begin();
}
// Permute LUT init value given map (LUT input -> ext input)
unsigned permute_lut(unsigned orig_init, const std::unordered_map<int, int> &input_permute)
{
unsigned new_init = 0;
for (int i = 0; i < 16; i++) {
int permute_address = 0;
for (int j = 0; j < 4; j++) {
if ((i >> j) & 0x1)
permute_address |= (1 << input_permute.at(j));
}
if ((orig_init >> i) & 0x1) {
new_init |= (1 << permute_address);
}
}
return new_init;
}
void write_asc(const Context *ctx, std::ostream &out)
{
static const std::vector<int> lut_perm = {
4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0,
};
// [y][x][row][col]
const ChipInfoPOD &ci = *ctx->chip_info;
const BitstreamInfoPOD &bi = *ci.bits_info;
chipconfig_t config;
config.resize(ci.height);
for (int y = 0; y < ci.height; y++) {
config.at(y).resize(ci.width);
for (int x = 0; x < ci.width; x++) {
TileType tile = tile_at(ctx, x, y);
int rows = bi.tiles_nonrouting[tile].rows;
int cols = bi.tiles_nonrouting[tile].cols;
config.at(y).at(x).resize(rows, std::vector<int8_t>(cols));
}
}
out << ".comment from next-pnr" << std::endl;
switch (ctx->args.type) {
case ArchArgs::LP384:
out << ".device 384" << std::endl;
break;
case ArchArgs::HX1K:
case ArchArgs::LP1K:
out << ".device 1k" << std::endl;
break;
case ArchArgs::HX8K:
case ArchArgs::LP8K:
out << ".device 8k" << std::endl;
break;
case ArchArgs::UP5K:
out << ".device 5k" << std::endl;
break;
default:
NPNR_ASSERT_FALSE("unsupported device type\n");
}
// Set pips
for (auto pip : ctx->getPips()) {
if (ctx->pip_to_net[pip.index] != nullptr) {
const PipInfoPOD &pi = ci.pip_data[pip.index];
const SwitchInfoPOD &swi = bi.switches[pi.switch_index];
int sw_bel_idx = swi.bel;
if (sw_bel_idx >= 0) {
const BelInfoPOD &beli = ci.bel_data[sw_bel_idx];
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC];
BelId sw_bel;
sw_bel.index = sw_bel_idx;
NPNR_ASSERT(ctx->getBelType(sw_bel) == id_ICESTORM_LC);
if (ci.wire_data[ctx->getPipDstWire(pip).index].type == WireInfoPOD::WIRE_TYPE_LUTFF_IN_LUT)
continue; // Permutation pips
BelPin output = get_one_bel_pin(ctx, ctx->getPipDstWire(pip));
NPNR_ASSERT(output.bel == sw_bel && output.pin == id_O);
unsigned lut_init;
WireId permWire;
for (auto permPip : ctx->getPipsUphill(ctx->getPipSrcWire(pip))) {
if (ctx->getBoundPipNet(permPip) != nullptr) {
permWire = ctx->getPipSrcWire(permPip);
}
}
NPNR_ASSERT(permWire != WireId());
std::string dName = ci.wire_data[permWire.index].name.get();
switch (dName.back()) {
case '0':
lut_init = 2;
break;
case '1':
lut_init = 4;
break;
case '2':
lut_init = 16;
break;
case '3':
lut_init = 256;
break;
default:
NPNR_ASSERT_FALSE("bad feedthru LUT input");
}
std::vector<bool> lc(20, false);
for (int i = 0; i < 16; i++) {
if ((lut_init >> i) & 0x1)
lc.at(lut_perm.at(i)) = true;
}
for (int i = 0; i < 20; i++)
set_config(ti, config.at(beli.y).at(beli.x), "LC_" + std::to_string(beli.z), lc.at(i), i);
} else {
for (int i = 0; i < swi.num_bits; i++) {
bool val = (pi.switch_mask & (1 << ((swi.num_bits - 1) - i))) != 0;
int8_t &cbit = config.at(swi.y).at(swi.x).at(swi.cbits[i].row).at(swi.cbits[i].col);
if (bool(cbit) != 0)
NPNR_ASSERT(false);
cbit = val;
}
}
}
}
std::unordered_set<Loc> sb_io_used_by_pll;
std::unordered_set<Loc> sb_io_used_by_io;
// Set logic cell config
for (auto &cell : ctx->cells) {
BelId bel = cell.second.get()->bel;
if (bel == BelId()) {
std::cout << "Found unplaced cell " << cell.first.str(ctx) << " while generating bitstream!" << std::endl;
continue;
}
if (cell.second->type == ctx->id("ICESTORM_LC")) {
const BelInfoPOD &beli = ci.bel_data[bel.index];
int x = beli.x, y = beli.y, z = beli.z;
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC];
unsigned lut_init = get_param_or_def(cell.second.get(), ctx->id("LUT_INIT"));
bool neg_clk = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK"));
bool dff_enable = get_param_or_def(cell.second.get(), ctx->id("DFF_ENABLE"));
bool async_sr = get_param_or_def(cell.second.get(), ctx->id("ASYNC_SR"));
bool set_noreset = get_param_or_def(cell.second.get(), ctx->id("SET_NORESET"));
bool carry_enable = get_param_or_def(cell.second.get(), ctx->id("CARRY_ENABLE"));
std::vector<bool> lc(20, false);
// Discover permutation
std::unordered_map<int, int> input_perm;
std::set<int> unused;
for (int i = 0; i < 4; i++)
unused.insert(i);
for (int i = 0; i < 4; i++) {
WireId lut_wire = ctx->getBelPinWire(bel, IdString(ID_I0 + i));
for (auto pip : ctx->getPipsUphill(lut_wire)) {
if (ctx->getBoundPipNet(pip) != nullptr) {
std::string name = ci.wire_data[ctx->getPipSrcWire(pip).index].name.get();
switch (name.back()) {
case '0':
input_perm[i] = 0;
unused.erase(0);
break;
case '1':
input_perm[i] = 1;
unused.erase(1);
break;
case '2':
input_perm[i] = 2;
unused.erase(2);
break;
case '3':
input_perm[i] = 3;
unused.erase(3);
break;
default:
NPNR_ASSERT_FALSE("failed to determine LUT permutation");
}
break;
}
}
}
for (int i = 0; i < 4; i++) {
if (!input_perm.count(i)) {
NPNR_ASSERT(!unused.empty());
input_perm[i] = *(unused.begin());
unused.erase(input_perm[i]);
}
}
lut_init = permute_lut(lut_init, input_perm);
for (int i = 0; i < 16; i++) {
if ((lut_init >> i) & 0x1)
lc.at(lut_perm.at(i)) = true;
}
lc.at(8) = carry_enable;
lc.at(9) = dff_enable;
lc.at(18) = set_noreset;
lc.at(19) = async_sr;
for (int i = 0; i < 20; i++)
set_config(ti, config.at(y).at(x), "LC_" + std::to_string(z), lc.at(i), i);
if (dff_enable)
set_config(ti, config.at(y).at(x), "NegClk", neg_clk);
bool carry_const = get_param_or_def(cell.second.get(), ctx->id("CIN_CONST"));
bool carry_set = get_param_or_def(cell.second.get(), ctx->id("CIN_SET"));
if (carry_const) {
if (!ctx->force)
NPNR_ASSERT(z == 0);
set_config(ti, config.at(y).at(x), "CarryInSet", carry_set);
}
} else if (cell.second->type == ctx->id("SB_IO")) {
const BelInfoPOD &beli = ci.bel_data[bel.index];
int x = beli.x, y = beli.y, z = beli.z;
sb_io_used_by_io.insert(Loc(x, y, z));
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
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 pullup = get_param_or_def(cell.second.get(), ctx->id("PULLUP"));
for (int i = 0; i < 6; i++) {
bool val = (pin_type >> i) & 0x01;
set_config(ti, config.at(y).at(x), "IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i), val);
}
set_config(ti, config.at(y).at(x), "NegClk", neg_trigger);
auto ieren = get_ieren(bi, x, y, z);
int iex, iey, iez;
std::tie(iex, iey, iez) = ieren;
NPNR_ASSERT(iez != -1);
bool input_en = false;
if ((ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_0).index] != nullptr) ||
(ctx->wire_to_net[ctx->getBelPinWire(bel, id_D_IN_1).index] != nullptr)) {
input_en = true;
}
if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), !input_en);
set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
} else {
set_config(ti, config.at(iey).at(iex), "IoCtrl.IE_" + std::to_string(iez), input_en);
set_config(ti, config.at(iey).at(iex), "IoCtrl.REN_" + std::to_string(iez), !pullup);
}
if (ctx->args.type == ArchArgs::UP5K) {
if (iez == 0) {
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_39", !pullup);
} else if (iez == 1) {
set_config(ti, config.at(iey).at(iex), "IoCtrl.cf_bit_35", !pullup);
}
}
} else if (cell.second->type == ctx->id("SB_GB")) {
// no cell config bits
} else if (cell.second->type == ctx->id("ICESTORM_RAM")) {
const BelInfoPOD &beli = ci.bel_data[bel.index];
int x = beli.x, y = beli.y;
const TileInfoPOD &ti_ramt = bi.tiles_nonrouting[TILE_RAMT];
const TileInfoPOD &ti_ramb = bi.tiles_nonrouting[TILE_RAMB];
if (!(ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K)) {
set_config(ti_ramb, config.at(y).at(x), "RamConfig.PowerUp", true);
}
bool negclk_r = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK_R"));
bool negclk_w = get_param_or_def(cell.second.get(), ctx->id("NEG_CLK_W"));
int write_mode = get_param_or_def(cell.second.get(), ctx->id("WRITE_MODE"));
int read_mode = get_param_or_def(cell.second.get(), ctx->id("READ_MODE"));
set_config(ti_ramb, config.at(y).at(x), "NegClk", negclk_w);
set_config(ti_ramt, config.at(y + 1).at(x), "NegClk", negclk_r);
set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_0", write_mode & 0x1);
set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_1", write_mode & 0x2);
set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_2", read_mode & 0x1);
set_config(ti_ramt, config.at(y + 1).at(x), "RamConfig.CBIT_3", read_mode & 0x2);
} else if (cell.second->type == ctx->id("SB_WARMBOOT") || cell.second->type == ctx->id("ICESTORM_LFOSC")) {
// No config needed
} else if (cell.second->type == ctx->id("ICESTORM_SPRAM")) {
const BelInfoPOD &beli = ci.bel_data[bel.index];
int x = beli.x, y = beli.y, z = beli.z;
NPNR_ASSERT(ctx->args.type == ArchArgs::UP5K);
if (x == 0 && y == 0) {
const TileInfoPOD &ti_ipcon = bi.tiles_nonrouting[TILE_IPCON];
if (z == 1) {
set_config(ti_ipcon, config.at(1).at(0), "IpConfig.CBIT_0", true);
} else if (z == 2) {
set_config(ti_ipcon, config.at(1).at(0), "IpConfig.CBIT_1", true);
} else {
NPNR_ASSERT(false);
}
} else if (x == 25 && y == 0) {
const TileInfoPOD &ti_ipcon = bi.tiles_nonrouting[TILE_IPCON];
if (z == 3) {
set_config(ti_ipcon, config.at(1).at(25), "IpConfig.CBIT_0", true);
} else if (z == 4) {
set_config(ti_ipcon, config.at(1).at(25), "IpConfig.CBIT_1", true);
} else {
NPNR_ASSERT(false);
}
}
} else if (cell.second->type == ctx->id("ICESTORM_DSP")) {
const std::vector<std::pair<std::string, int>> mac16_params = {{"C_REG", 1},
{"A_REG", 1},
{"B_REG", 1},
{"D_REG", 1},
{"TOP_8x8_MULT_REG", 1},
{"BOT_8x8_MULT_REG", 1},
{"PIPELINE_16x16_MULT_REG1", 1},
{"PIPELINE_16x16_MULT_REG2", 1},
{"TOPOUTPUT_SELECT", 2},
{"TOPADDSUB_LOWERINPUT", 2},
{"TOPADDSUB_UPPERINPUT", 1},
{"TOPADDSUB_CARRYSELECT", 2},
{"BOTOUTPUT_SELECT", 2},
{"BOTADDSUB_LOWERINPUT", 2},
{"BOTADDSUB_UPPERINPUT", 1},
{"BOTADDSUB_CARRYSELECT", 2},
{"MODE_8x8", 1},
{"A_SIGNED", 1},
{"B_SIGNED", 1}};
configure_extra_cell(config, ctx, cell.second.get(), mac16_params, false, std::string("IpConfig."));
} else if (cell.second->type == ctx->id("ICESTORM_HFOSC")) {
const std::vector<std::pair<std::string, int>> hfosc_params = {{"CLKHF_DIV", 2}, {"TRIM_EN", 1}};
configure_extra_cell(config, ctx, cell.second.get(), hfosc_params, true, 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;
// Get IO Bel that this PLL port goes through by finding sibling
// Bel driving the same wire via PIN_D_IN_0.
auto wire = ctx->getBelPinWire(cell.second->bel, port.second.name);
BelId io_bel;
for (auto pin : ctx->getWireBelPins(wire)) {
if (pin.pin == id_D_IN_0) {
io_bel = pin.bel;
break;
}
}
NPNR_ASSERT(io_bel.index != -1);
auto io_bel_loc = ctx->getBelLocation(io_bel);
// Check that this SB_IO is either unused or just used as an output.
if (sb_io_used_by_io.count(io_bel_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(io_bel_loc);
// Get IE/REN config location (cf. http://www.clifford.at/icestorm/io_tile.html)
auto ieren = get_ieren(bi, io_bel_loc.x, io_bel_loc.y, io_bel_loc.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_bel_loc.y).at(io_bel_loc.x),
"IOB_" + std::to_string(io_bel_loc.z) + ".PINTYPE_0", true);
}
} else {
NPNR_ASSERT(false);
}
}
// Set config bits in unused IO and RAM
for (auto bel : ctx->getBels()) {
if (ctx->bel_to_cell[bel.index] == nullptr && ctx->getBelType(bel) == id_SB_IO) {
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
const BelInfoPOD &beli = ci.bel_data[bel.index];
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);
int iex, iey, iez;
std::tie(iex, iey, iez) = ieren;
if (iez != -1) {
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);
}
} else if (ctx->bel_to_cell[bel.index] == nullptr && ctx->getBelType(bel) == id_ICESTORM_RAM) {
const BelInfoPOD &beli = ci.bel_data[bel.index];
int x = beli.x, y = beli.y;
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_RAMB];
if ((ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K)) {
set_config(ti, config.at(y).at(x), "RamConfig.PowerUp", true);
}
}
}
// Set other config bits
for (int y = 0; y < ci.height; y++) {
for (int x = 0; x < ci.width; x++) {
TileType tile = tile_at(ctx, x, y);
const TileInfoPOD &ti = bi.tiles_nonrouting[tile];
// set all ColBufCtrl bits (FIXME)
bool setColBufCtrl = true;
if (ctx->args.type == ArchArgs::LP1K || ctx->args.type == ArchArgs::HX1K) {
if (tile == TILE_RAMB || tile == TILE_RAMT) {
setColBufCtrl = (y == 3 || y == 5 || y == 11 || y == 13);
} else {
setColBufCtrl = (y == 4 || y == 5 || y == 12 || y == 13);
}
} else if (ctx->args.type == ArchArgs::LP8K || ctx->args.type == ArchArgs::HX8K) {
setColBufCtrl = (y == 8 || y == 9 || y == 24 || y == 25);
} else if (ctx->args.type == ArchArgs::UP5K) {
setColBufCtrl = (y == 4 || y == 5 || y == 14 || y == 15 || y == 26 || y == 27);
}
if (setColBufCtrl) {
set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_0", true);
set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_1", true);
set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_2", true);
set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_3", true);
set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_4", true);
set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_5", true);
set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_6", true);
set_config(ti, config.at(y).at(x), "ColBufCtrl.glb_netwk_7", true);
}
// Weird UltraPlus bits
if (tile == TILE_DSP0 || tile == TILE_DSP1 || tile == TILE_DSP2 || tile == TILE_DSP3 ||
tile == TILE_IPCON) {
if (ctx->args.type == ArchArgs::UP5K && x == 25 && y == 14) {
// Mystery bits not set in this one tile
} else {
for (int lc_idx = 0; lc_idx < 8; lc_idx++) {
static const std::vector<int> ip_dsp_lut_perm = {
4, 14, 15, 5, 6, 16, 17, 7, 3, 13, 12, 2, 1, 11, 10, 0,
};
for (int i = 0; i < 16; i++)
set_config(ti, config.at(y).at(x), "LC_" + std::to_string(lc_idx), ((i % 8) >= 4),
ip_dsp_lut_perm.at(i));
if (tile == TILE_IPCON)
set_config(ti, config.at(y).at(x),
"Cascade.IPCON_LC0" + std::to_string(lc_idx) + "_inmux02_5", true);
else
set_config(ti, config.at(y).at(x),
"Cascade.MULT" + std::to_string(int(tile - TILE_DSP0)) + "_LC0" +
std::to_string(lc_idx) + "_inmux02_5",
true);
}
}
}
}
}
// Write config out
for (int y = 0; y < ci.height; y++) {
for (int x = 0; x < ci.width; x++) {
TileType tile = tile_at(ctx, x, y);
if (tile == TILE_NONE)
continue;
out << tagTileType(tile);
out << " " << x << " " << y << std::endl;
for (auto row : config.at(y).at(x)) {
for (auto col : row) {
if (col == 1)
out << "1";
else
out << "0";
}
out << std::endl;
}
out << std::endl;
}
}
// Write RAM init data
for (auto &cell : ctx->cells) {
if (cell.second->bel != BelId()) {
if (cell.second->type == ctx->id("ICESTORM_RAM")) {
const BelInfoPOD &beli = ci.bel_data[cell.second->bel.index];
int x = beli.x, y = beli.y;
out << ".ram_data " << x << " " << y << std::endl;
for (int w = 0; w < 16; w++) {
std::vector<bool> bits(256);
std::string init =
get_param_str_or_def(cell.second.get(), ctx->id(std::string("INIT_") + get_hexdigit(w)));
NPNR_ASSERT(init != "");
for (size_t i = 0; i < init.size(); i++) {
bool val = (init.at((init.size() - 1) - i) == '1');
bits.at(i) = val;
}
for (int i = bits.size() - 4; i >= 0; i -= 4) {
int c = bits.at(i) + (bits.at(i + 1) << 1) + (bits.at(i + 2) << 2) + (bits.at(i + 3) << 3);
out << char(std::tolower(get_hexdigit(c)));
}
out << std::endl;
}
out << std::endl;
}
}
}
// Write symbols
// const bool write_symbols = 1;
for (auto wire : ctx->getWires()) {
NetInfo *net = ctx->getBoundWireNet(wire);
if (net != nullptr)
out << ".sym " << wire.index << " " << net->name.str(ctx) << std::endl;
}
}
void read_config(Context *ctx, std::istream &in, chipconfig_t &config)
{
constexpr size_t line_buf_size = 65536;
char buffer[line_buf_size];
int tile_x = -1, tile_y = -1, line_nr = -1;
while (1) {
in.getline(buffer, line_buf_size);
if (buffer[0] == '.') {
line_nr = -1;
const char *tok = strtok(buffer, " \t\r\n");
if (!strcmp(tok, ".device")) {
std::string config_device = strtok(nullptr, " \t\r\n");
std::string expected;
switch (ctx->args.type) {
case ArchArgs::LP384:
expected = "384";
break;
case ArchArgs::HX1K:
case ArchArgs::LP1K:
expected = "1k";
break;
case ArchArgs::HX8K:
case ArchArgs::LP8K:
expected = "8k";
break;
case ArchArgs::UP5K:
expected = "5k";
break;
default:
log_error("unsupported device type\n");
}
if (expected != config_device)
log_error("device type does not match\n");
} else if (!strcmp(tok, ".io_tile") || !strcmp(tok, ".logic_tile") || !strcmp(tok, ".ramb_tile") ||
!strcmp(tok, ".ramt_tile") || !strcmp(tok, ".ipcon_tile") || !strcmp(tok, ".dsp0_tile") ||
!strcmp(tok, ".dsp1_tile") || !strcmp(tok, ".dsp2_tile") || !strcmp(tok, ".dsp3_tile")) {
line_nr = 0;
tile_x = atoi(strtok(nullptr, " \t\r\n"));
tile_y = atoi(strtok(nullptr, " \t\r\n"));
TileType tile = tile_at(ctx, tile_x, tile_y);
if (tok != tagTileType(tile))
log_error("Wrong tile type for specified position\n");
} else if (!strcmp(tok, ".extra_bit")) {
/*
int b = atoi(strtok(nullptr, " \t\r\n"));
int x = atoi(strtok(nullptr, " \t\r\n"));
int y = atoi(strtok(nullptr, " \t\r\n"));
std::tuple<int, int, int> key(b, x, y);
extra_bits.insert(key);
*/
} else if (!strcmp(tok, ".sym")) {
int wireIndex = atoi(strtok(nullptr, " \t\r\n"));
const char *name = strtok(nullptr, " \t\r\n");
IdString netName = ctx->id(name);
if (ctx->nets.find(netName) == ctx->nets.end()) {
std::unique_ptr<NetInfo> created_net = std::unique_ptr<NetInfo>(new NetInfo);
created_net->name = netName;
ctx->nets[netName] = std::move(created_net);
}
WireId wire;
wire.index = wireIndex;
ctx->bindWire(wire, ctx->nets.at(netName).get(), STRENGTH_WEAK);
}
} else if (line_nr >= 0 && strlen(buffer) > 0) {
if (line_nr > int(config.at(tile_y).at(tile_x).size() - 1))
log_error("Invalid data in input asc file");
for (int i = 0; buffer[i] == '0' || buffer[i] == '1'; i++)
config.at(tile_y).at(tile_x).at(line_nr).at(i) = (buffer[i] == '1') ? 1 : 0;
line_nr++;
}
if (in.eof())
break;
}
}
bool read_asc(Context *ctx, std::istream &in)
{
try {
// [y][x][row][col]
const ChipInfoPOD &ci = *ctx->chip_info;
const BitstreamInfoPOD &bi = *ci.bits_info;
chipconfig_t config;
config.resize(ci.height);
for (int y = 0; y < ci.height; y++) {
config.at(y).resize(ci.width);
for (int x = 0; x < ci.width; x++) {
TileType tile = tile_at(ctx, x, y);
int rows = bi.tiles_nonrouting[tile].rows;
int cols = bi.tiles_nonrouting[tile].cols;
config.at(y).at(x).resize(rows, std::vector<int8_t>(cols));
}
}
read_config(ctx, in, config);
// Set pips
for (auto pip : ctx->getPips()) {
const PipInfoPOD &pi = ci.pip_data[pip.index];
const SwitchInfoPOD &swi = bi.switches[pi.switch_index];
bool isUsed = true;
for (int i = 0; i < swi.num_bits; i++) {
bool val = (pi.switch_mask & (1 << ((swi.num_bits - 1) - i))) != 0;
int8_t cbit = config.at(swi.y).at(swi.x).at(swi.cbits[i].row).at(swi.cbits[i].col);
isUsed &= !(bool(cbit) ^ val);
}
if (isUsed) {
NetInfo *net = ctx->wire_to_net[pi.dst];
WireId wire;
wire.index = pi.dst;
ctx->unbindWire(wire);
ctx->bindPip(pip, net, STRENGTH_WEAK);
}
}
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) == id_ICESTORM_LC) {
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_LOGIC];
const BelInfoPOD &beli = ci.bel_data[bel.index];
int x = beli.x, y = beli.y, z = beli.z;
std::vector<bool> lc(20, false);
bool isUsed = false;
for (int i = 0; i < 20; i++) {
lc.at(i) = get_config(ti, config.at(y).at(x), "LC_" + std::to_string(z), i);
isUsed |= lc.at(i);
}
bool neg_clk = get_config(ti, config.at(y).at(x), "NegClk");
isUsed |= neg_clk;
bool carry_set = get_config(ti, config.at(y).at(x), "CarryInSet");
isUsed |= carry_set;
if (isUsed) {
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
IdString name = created->name;
ctx->cells[name] = std::move(created);
ctx->bindBel(bel, ctx->cells[name].get(), STRENGTH_WEAK);
// TODO: Add port mapping to nets and assign values of properties
}
}
if (ctx->getBelType(bel) == id_SB_IO) {
const TileInfoPOD &ti = bi.tiles_nonrouting[TILE_IO];
const BelInfoPOD &beli = ci.bel_data[bel.index];
int x = beli.x, y = beli.y, z = beli.z;
bool isUsed = false;
for (int i = 0; i < 6; i++) {
isUsed |= get_config(ti, config.at(y).at(x),
"IOB_" + std::to_string(z) + ".PINTYPE_" + std::to_string(i));
}
bool neg_trigger = get_config(ti, config.at(y).at(x), "NegClk");
isUsed |= neg_trigger;
if (isUsed) {
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_IO"));
IdString name = created->name;
ctx->cells[name] = std::move(created);
ctx->bindBel(bel, ctx->cells[name].get(), STRENGTH_WEAK);
// TODO: Add port mapping to nets and assign values of properties
}
}
}
// Add cells that are without change in initial state of configuration
for (auto &net : ctx->nets) {
for (auto w : net.second->wires) {
if (w.second.pip == PipId()) {
WireId wire = w.first;
for (auto belpin : ctx->getWireBelPins(wire)) {
if (ctx->checkBelAvail(belpin.bel)) {
if (ctx->getBelType(belpin.bel) == id_ICESTORM_LC) {
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
IdString name = created->name;
ctx->cells[name] = std::move(created);
ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
// TODO: Add port mapping to nets
}
if (ctx->getBelType(belpin.bel) == id_SB_IO) {
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_IO"));
IdString name = created->name;
ctx->cells[name] = std::move(created);
ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
// TODO: Add port mapping to nets
}
if (ctx->getBelType(belpin.bel) == id_SB_GB) {
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_GB"));
IdString name = created->name;
ctx->cells[name] = std::move(created);
ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
// TODO: Add port mapping to nets
}
if (ctx->getBelType(belpin.bel) == id_SB_WARMBOOT) {
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("SB_WARMBOOT"));
IdString name = created->name;
ctx->cells[name] = std::move(created);
ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
// TODO: Add port mapping to nets
}
if (ctx->getBelType(belpin.bel) == id_ICESTORM_LFOSC) {
std::unique_ptr<CellInfo> created = create_ice_cell(ctx, ctx->id("ICESTORM_LFOSC"));
IdString name = created->name;
ctx->cells[name] = std::move(created);
ctx->bindBel(belpin.bel, ctx->cells[name].get(), STRENGTH_WEAK);
// TODO: Add port mapping to nets
}
}
}
}
}
}
for (auto &cell : ctx->cells) {
if (cell.second->bel != BelId()) {
for (auto &port : cell.second->ports) {
IdString pin = port.first;
WireId wire = ctx->getBelPinWire(cell.second->bel, pin);
if (wire != WireId()) {
NetInfo *net = ctx->getBoundWireNet(wire);
if (net != nullptr) {
port.second.net = net;
PortRef ref;
ref.cell = cell.second.get();
ref.port = port.second.name;
if (port.second.type == PORT_OUT)
net->driver = ref;
else
net->users.push_back(ref);
}
}
}
}
}
return true;
} catch (log_execution_error_exception) {
return false;
}
}
NEXTPNR_NAMESPACE_END

34
xc7/bitstream.h Normal file
View File

@ -0,0 +1,34 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef ICE40_BITSTREAM_H
#define ICE40_BITSTREAM_H
#include <iostream>
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
void write_asc(const Context *ctx, std::ostream &out);
bool read_asc(Context *ctx, std::istream &in);
NEXTPNR_NAMESPACE_END
#endif

6
xc7/blinky.pcf Normal file
View File

@ -0,0 +1,6 @@
set_io led1 99
set_io led2 98
set_io led3 97
set_io led4 96
set_io led5 95
set_io clki 21

15
xc7/blinky.proj Normal file
View File

@ -0,0 +1,15 @@
{
"project": {
"version": "1",
"name": "blinky",
"arch": {
"name": "ice40",
"type": "hx1k",
"package": "tq144"
},
"input": {
"json": "blinky.json",
"pcf": "blinky.pcf"
}
}
}

8
xc7/blinky.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash
set -ex
yosys blinky.ys
../nextpnr-ice40 --json blinky.json --pcf blinky.pcf --asc blinky.asc
icepack blinky.asc blinky.bin
icebox_vlog blinky.asc > blinky_chip.v
iverilog -o blinky_tb blinky_chip.v blinky_tb.v
vvp -N ./blinky_tb

27
xc7/blinky.v Normal file
View File

@ -0,0 +1,27 @@
module blinky (
input clki,
output led1,
output led2,
output led3,
output led4,
output led5
);
SB_GB clk_gb (
.USER_SIGNAL_TO_GLOBAL_BUFFER(clki),
.GLOBAL_BUFFER_OUTPUT(clk)
);
localparam BITS = 5;
localparam LOG2DELAY = 21;
reg [BITS+LOG2DELAY-1:0] counter = 0;
reg [BITS-1:0] outcnt;
always @(posedge clk) begin
counter <= counter + 1;
outcnt <= counter >> LOG2DELAY;
end
assign {led1, led2, led3, led4, led5} = outcnt ^ (outcnt >> 1);
endmodule

3
xc7/blinky.ys Normal file
View File

@ -0,0 +1,3 @@
read_verilog blinky.v
synth_ice40 -top blinky
write_json blinky.json

25
xc7/blinky_tb.v Normal file
View File

@ -0,0 +1,25 @@
module blinky_tb;
reg clk;
always #5 clk = (clk === 1'b0);
wire led1, led2, led3, led4, led5;
chip uut (
.io_0_8_1(clk),
.io_13_12_1(led1),
.io_13_12_0(led2),
.io_13_11_1(led3),
.io_13_11_0(led4),
.io_13_9_1(led5)
);
initial begin
// $dumpfile("blinky_tb.vcd");
// $dumpvars(0, blinky_tb);
repeat (10) begin
repeat (900000) @(posedge clk);
$display(led1, led2, led3, led4, led5);
end
$finish;
end
endmodule

4
xc7/carry_tests/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*.vcd
*_out.v
*.out

View File

@ -0,0 +1,9 @@
module top(input clk, cen, rst, ina, inb, output outa, outb, outc, outd);
reg [15:0] ctr = 0;
always @(posedge clk)
ctr <= ctr + 1'b1;
assign {outa, outb, outc, outd} = ctr[15:12];
endmodule

View File

@ -0,0 +1,23 @@
module counter_tb;
reg clk;
always #5 clk = (clk === 1'b0);
wire outa, outb, outc, outd;
chip uut (
.clk(clk),
.cen(1'b1),
.rst(1'b0),
.outa(outa),
.outb(outb),
.outc(outc),
.outd(outd)
);
initial begin
$dumpfile("counter_tb.vcd");
$dumpvars(0, counter_tb);
repeat (100000) @(posedge clk);
$finish;
end
endmodule

10
xc7/carry_tests/test.pcf Normal file
View File

@ -0,0 +1,10 @@
set_io clk 1
set_io cen 2
set_io rst 3
set_io ina 4
set_io inb 7
set_io outa 8
set_io outb 9
set_io outc 10
set_io outd 11

9
xc7/carry_tests/test.sh Executable file
View File

@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -ex
NAME=${1%.v}
yosys -p "synth_ice40 -top top; write_json ${NAME}.json" $1
../../nextpnr-ice40 --json ${NAME}.json --pcf test.pcf --asc ${NAME}.asc --verbose
icebox_vlog -p test.pcf -L ${NAME}.asc > ${NAME}_out.v
iverilog -o ${NAME}_sim.out ${NAME}_tb.v ${NAME}_out.v
vvp ${NAME}_sim.out

409
xc7/cells.cc Normal file
View File

@ -0,0 +1,409 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "cells.h"
#include "design_utils.h"
#include "log.h"
NEXTPNR_NAMESPACE_BEGIN
void add_port(const Context *ctx, CellInfo *cell, std::string name, PortType dir)
{
IdString id = ctx->id(name);
cell->ports[id] = PortInfo{id, nullptr, dir};
}
std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::string name)
{
static int auto_idx = 0;
std::unique_ptr<CellInfo> new_cell = std::unique_ptr<CellInfo>(new CellInfo());
if (name.empty()) {
new_cell->name = ctx->id("$nextpnr_" + type.str(ctx) + "_" + std::to_string(auto_idx++));
} else {
new_cell->name = ctx->id(name);
}
new_cell->type = type;
if (type == ctx->id("ICESTORM_LC")) {
new_cell->params[ctx->id("LUT_INIT")] = "0";
new_cell->params[ctx->id("NEG_CLK")] = "0";
new_cell->params[ctx->id("CARRY_ENABLE")] = "0";
new_cell->params[ctx->id("DFF_ENABLE")] = "0";
new_cell->params[ctx->id("SET_NORESET")] = "0";
new_cell->params[ctx->id("ASYNC_SR")] = "0";
new_cell->params[ctx->id("CIN_CONST")] = "0";
new_cell->params[ctx->id("CIN_SET")] = "0";
add_port(ctx, new_cell.get(), "I0", PORT_IN);
add_port(ctx, new_cell.get(), "I1", PORT_IN);
add_port(ctx, new_cell.get(), "I2", PORT_IN);
add_port(ctx, new_cell.get(), "I3", PORT_IN);
add_port(ctx, new_cell.get(), "CIN", PORT_IN);
add_port(ctx, new_cell.get(), "CLK", PORT_IN);
add_port(ctx, new_cell.get(), "CEN", PORT_IN);
add_port(ctx, new_cell.get(), "SR", PORT_IN);
add_port(ctx, new_cell.get(), "LO", PORT_OUT);
add_port(ctx, new_cell.get(), "O", PORT_OUT);
add_port(ctx, new_cell.get(), "COUT", PORT_OUT);
} else if (type == ctx->id("SB_IO")) {
new_cell->params[ctx->id("PIN_TYPE")] = "0";
new_cell->params[ctx->id("PULLUP")] = "0";
new_cell->params[ctx->id("NEG_TRIGGER")] = "0";
new_cell->params[ctx->id("IOSTANDARD")] = "SB_LVCMOS";
add_port(ctx, new_cell.get(), "PACKAGE_PIN", PORT_INOUT);
add_port(ctx, new_cell.get(), "LATCH_INPUT_VALUE", PORT_IN);
add_port(ctx, new_cell.get(), "CLOCK_ENABLE", PORT_IN);
add_port(ctx, new_cell.get(), "INPUT_CLK", PORT_IN);
add_port(ctx, new_cell.get(), "OUTPUT_CLK", PORT_IN);
add_port(ctx, new_cell.get(), "OUTPUT_ENABLE", PORT_IN);
add_port(ctx, new_cell.get(), "D_OUT_0", PORT_IN);
add_port(ctx, new_cell.get(), "D_OUT_1", PORT_IN);
add_port(ctx, new_cell.get(), "D_IN_0", PORT_OUT);
add_port(ctx, new_cell.get(), "D_IN_1", PORT_OUT);
} else if (type == ctx->id("ICESTORM_RAM")) {
new_cell->params[ctx->id("NEG_CLK_W")] = "0";
new_cell->params[ctx->id("NEG_CLK_R")] = "0";
new_cell->params[ctx->id("WRITE_MODE")] = "0";
new_cell->params[ctx->id("READ_MODE")] = "0";
add_port(ctx, new_cell.get(), "RCLK", PORT_IN);
add_port(ctx, new_cell.get(), "RCLKE", PORT_IN);
add_port(ctx, new_cell.get(), "RE", PORT_IN);
add_port(ctx, new_cell.get(), "WCLK", PORT_IN);
add_port(ctx, new_cell.get(), "WCLKE", PORT_IN);
add_port(ctx, new_cell.get(), "WE", PORT_IN);
for (int i = 0; i < 16; i++) {
add_port(ctx, new_cell.get(), "WDATA_" + std::to_string(i), PORT_IN);
add_port(ctx, new_cell.get(), "MASK_" + std::to_string(i), PORT_IN);
add_port(ctx, new_cell.get(), "RDATA_" + std::to_string(i), PORT_OUT);
}
for (int i = 0; i < 11; i++) {
add_port(ctx, new_cell.get(), "RADDR_" + std::to_string(i), PORT_IN);
add_port(ctx, new_cell.get(), "WADDR_" + std::to_string(i), PORT_IN);
}
} else if (type == ctx->id("ICESTORM_LFOSC")) {
add_port(ctx, new_cell.get(), "CLKLFEN", PORT_IN);
add_port(ctx, new_cell.get(), "CLKLFPU", PORT_IN);
add_port(ctx, new_cell.get(), "CLKLF", PORT_OUT);
add_port(ctx, new_cell.get(), "CLKLF_FABRIC", PORT_OUT);
} else if (type == ctx->id("ICESTORM_HFOSC")) {
new_cell->params[ctx->id("CLKHF_DIV")] = "0b00";
new_cell->params[ctx->id("TRIM_EN")] = "0b0";
add_port(ctx, new_cell.get(), "CLKHFEN", PORT_IN);
add_port(ctx, new_cell.get(), "CLKHFPU", PORT_IN);
add_port(ctx, new_cell.get(), "CLKHF", PORT_OUT);
add_port(ctx, new_cell.get(), "CLKHF_FABRIC", PORT_OUT);
for (int i = 0; i < 10; i++)
add_port(ctx, new_cell.get(), "TRIM" + std::to_string(i), PORT_IN);
} else if (type == ctx->id("SB_GB")) {
add_port(ctx, new_cell.get(), "USER_SIGNAL_TO_GLOBAL_BUFFER", PORT_IN);
add_port(ctx, new_cell.get(), "GLOBAL_BUFFER_OUTPUT", PORT_OUT);
} else if (type == ctx->id("ICESTORM_SPRAM")) {
add_port(ctx, new_cell.get(), "WREN", PORT_IN);
add_port(ctx, new_cell.get(), "CHIPSELECT", PORT_IN);
add_port(ctx, new_cell.get(), "CLOCK", PORT_IN);
add_port(ctx, new_cell.get(), "STANDBY", PORT_IN);
add_port(ctx, new_cell.get(), "SLEEP", PORT_IN);
add_port(ctx, new_cell.get(), "POWEROFF", PORT_IN);
for (int i = 0; i < 16; i++) {
add_port(ctx, new_cell.get(), "DATAIN_" + std::to_string(i), PORT_IN);
add_port(ctx, new_cell.get(), "DATAOUT_" + std::to_string(i), PORT_OUT);
}
for (int i = 0; i < 14; i++) {
add_port(ctx, new_cell.get(), "ADDRESS_" + std::to_string(i), PORT_IN);
}
for (int i = 0; i < 4; i++) {
add_port(ctx, new_cell.get(), "MASKWREN_" + std::to_string(i), PORT_IN);
}
} else if (type == ctx->id("ICESTORM_DSP")) {
new_cell->params[ctx->id("NEG_TRIGGER")] = "0";
new_cell->params[ctx->id("C_REG")] = "0";
new_cell->params[ctx->id("A_REG")] = "0";
new_cell->params[ctx->id("B_REG")] = "0";
new_cell->params[ctx->id("D_REG")] = "0";
new_cell->params[ctx->id("TOP_8x8_MULT_REG")] = "0";
new_cell->params[ctx->id("BOT_8x8_MULT_REG")] = "0";
new_cell->params[ctx->id("PIPELINE_16x16_MULT_REG1")] = "0";
new_cell->params[ctx->id("PIPELINE_16x16_MULT_REG2")] = "0";
new_cell->params[ctx->id("TOPOUTPUT_SELECT")] = "0";
new_cell->params[ctx->id("TOPADDSUB_LOWERINPUT")] = "0";
new_cell->params[ctx->id("TOPADDSUB_UPPERINPUT")] = "0";
new_cell->params[ctx->id("TOPADDSUB_CARRYSELECT")] = "0";
new_cell->params[ctx->id("BOTOUTPUT_SELECT")] = "0";
new_cell->params[ctx->id("BOTADDSUB_LOWERINPUT")] = "0";
new_cell->params[ctx->id("BOTADDSUB_UPPERINPUT")] = "0";
new_cell->params[ctx->id("BOTADDSUB_CARRYSELECT")] = "0";
new_cell->params[ctx->id("MODE_8x8")] = "0";
new_cell->params[ctx->id("A_SIGNED")] = "0";
new_cell->params[ctx->id("B_SIGNED")] = "0";
add_port(ctx, new_cell.get(), "CLK", PORT_IN);
add_port(ctx, new_cell.get(), "CE", PORT_IN);
for (int i = 0; i < 16; i++) {
add_port(ctx, new_cell.get(), "C_" + std::to_string(i), PORT_IN);
add_port(ctx, new_cell.get(), "A_" + std::to_string(i), PORT_IN);
add_port(ctx, new_cell.get(), "B_" + std::to_string(i), PORT_IN);
add_port(ctx, new_cell.get(), "D_" + std::to_string(i), PORT_IN);
}
add_port(ctx, new_cell.get(), "AHOLD", PORT_IN);
add_port(ctx, new_cell.get(), "BHOLD", PORT_IN);
add_port(ctx, new_cell.get(), "CHOLD", PORT_IN);
add_port(ctx, new_cell.get(), "DHOLD", PORT_IN);
add_port(ctx, new_cell.get(), "IRSTTOP", PORT_IN);
add_port(ctx, new_cell.get(), "IRSTBOT", PORT_IN);
add_port(ctx, new_cell.get(), "ORSTTOP", PORT_IN);
add_port(ctx, new_cell.get(), "ORSTBOT", PORT_IN);
add_port(ctx, new_cell.get(), "OLOADTOP", PORT_IN);
add_port(ctx, new_cell.get(), "OLOADBOT", PORT_IN);
add_port(ctx, new_cell.get(), "ADDSUBTOP", PORT_IN);
add_port(ctx, new_cell.get(), "ADDSUBBOT", PORT_IN);
add_port(ctx, new_cell.get(), "OHOLDTOP", PORT_IN);
add_port(ctx, new_cell.get(), "OHOLDBOT", PORT_IN);
add_port(ctx, new_cell.get(), "CI", PORT_IN);
add_port(ctx, new_cell.get(), "ACCUMCI", PORT_IN);
add_port(ctx, new_cell.get(), "SIGNEXTIN", PORT_IN);
for (int i = 0; i < 32; i++) {
add_port(ctx, new_cell.get(), "O_" + std::to_string(i), PORT_OUT);
}
add_port(ctx, new_cell.get(), "CO", PORT_OUT);
add_port(ctx, new_cell.get(), "ACCUMCO", 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 {
log_error("unable to create iCE40 cell of type %s", type.c_str(ctx));
}
return new_cell;
}
void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff)
{
lc->params[ctx->id("LUT_INIT")] = lut->params[ctx->id("LUT_INIT")];
replace_port(lut, ctx->id("I0"), lc, ctx->id("I0"));
replace_port(lut, ctx->id("I1"), lc, ctx->id("I1"));
replace_port(lut, ctx->id("I2"), lc, ctx->id("I2"));
replace_port(lut, ctx->id("I3"), lc, ctx->id("I3"));
if (no_dff) {
replace_port(lut, ctx->id("O"), lc, ctx->id("O"));
lc->params[ctx->id("DFF_ENABLE")] = "0";
}
}
void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut)
{
lc->params[ctx->id("DFF_ENABLE")] = "1";
std::string config = dff->type.str(ctx).substr(6);
auto citer = config.begin();
replace_port(dff, ctx->id("C"), lc, ctx->id("CLK"));
if (citer != config.end() && *citer == 'N') {
lc->params[ctx->id("NEG_CLK")] = "1";
++citer;
} else {
lc->params[ctx->id("NEG_CLK")] = "0";
}
if (citer != config.end() && *citer == 'E') {
replace_port(dff, ctx->id("E"), lc, ctx->id("CEN"));
++citer;
}
if (citer != config.end()) {
if ((config.end() - citer) >= 2) {
char c = *(citer++);
NPNR_ASSERT(c == 'S');
lc->params[ctx->id("ASYNC_SR")] = "0";
} else {
lc->params[ctx->id("ASYNC_SR")] = "1";
}
if (*citer == 'S') {
citer++;
replace_port(dff, ctx->id("S"), lc, ctx->id("SR"));
lc->params[ctx->id("SET_NORESET")] = "1";
} else {
NPNR_ASSERT(*citer == 'R');
citer++;
replace_port(dff, ctx->id("R"), lc, ctx->id("SR"));
lc->params[ctx->id("SET_NORESET")] = "0";
}
}
NPNR_ASSERT(citer == config.end());
if (pass_thru_lut) {
lc->params[ctx->id("LUT_INIT")] = "2";
replace_port(dff, ctx->id("D"), lc, ctx->id("I0"));
}
replace_port(dff, ctx->id("Q"), lc, ctx->id("O"));
}
void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio)
{
if (nxio->type == ctx->id("$nextpnr_ibuf")) {
sbio->params[ctx->id("PIN_TYPE")] = "1";
auto pu_attr = nxio->attrs.find(ctx->id("PULLUP"));
if (pu_attr != nxio->attrs.end())
sbio->params[ctx->id("PULLUP")] = pu_attr->second;
replace_port(nxio, ctx->id("O"), sbio, ctx->id("D_IN_0"));
} else if (nxio->type == ctx->id("$nextpnr_obuf")) {
sbio->params[ctx->id("PIN_TYPE")] = "25";
replace_port(nxio, ctx->id("I"), sbio, ctx->id("D_OUT_0"));
} else if (nxio->type == ctx->id("$nextpnr_iobuf")) {
// N.B. tristate will be dealt with below
sbio->params[ctx->id("PIN_TYPE")] = "25";
replace_port(nxio, ctx->id("I"), sbio, ctx->id("D_OUT_0"));
replace_port(nxio, ctx->id("O"), sbio, ctx->id("D_IN_0"));
} else {
NPNR_ASSERT(false);
}
NetInfo *donet = sbio->ports.at(ctx->id("D_OUT_0")).net;
CellInfo *tbuf = net_driven_by(
ctx, donet, [](const Context *ctx, const CellInfo *cell) { return cell->type == ctx->id("$_TBUF_"); },
ctx->id("Y"));
if (tbuf) {
sbio->params[ctx->id("PIN_TYPE")] = "41";
replace_port(tbuf, ctx->id("A"), sbio, ctx->id("D_OUT_0"));
replace_port(tbuf, ctx->id("E"), sbio, ctx->id("OUTPUT_ENABLE"));
ctx->nets.erase(donet->name);
if (!donet->users.empty())
log_error("unsupported tristate IO pattern for IO buffer '%s', "
"instantiate SB_IO manually to ensure correct behaviour\n",
nxio->name.c_str(ctx));
ctx->cells.erase(tbuf->name);
}
}
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)
{
if (port.cell == nullptr)
return false;
if (is_ff(ctx, port.cell))
return port.port == ctx->id("C");
if (port.cell->type == ctx->id("ICESTORM_LC"))
return port.port == ctx->id("CLK");
if (is_ram(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_RAM"))
return port.port == ctx->id("RCLK") || port.port == ctx->id("WCLK") || port.port == ctx->id("RCLKN") ||
port.port == ctx->id("WCLKN");
if (is_sb_mac16(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_DSP"))
return port.port == ctx->id("CLK");
return false;
}
bool is_reset_port(const BaseCtx *ctx, const PortRef &port)
{
if (port.cell == nullptr)
return false;
if (is_ff(ctx, port.cell))
return port.port == ctx->id("R") || port.port == ctx->id("S");
if (port.cell->type == ctx->id("ICESTORM_LC"))
return port.port == ctx->id("SR");
if (is_sb_mac16(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_DSP"))
return port.port == ctx->id("IRSTTOP") || port.port == ctx->id("IRSTBOT") || port.port == ctx->id("ORSTTOP") ||
port.port == ctx->id("ORSTBOT");
return false;
}
bool is_enable_port(const BaseCtx *ctx, const PortRef &port)
{
if (port.cell == nullptr)
return false;
if (is_ff(ctx, port.cell))
return port.port == ctx->id("E");
if (port.cell->type == ctx->id("ICESTORM_LC"))
return port.port == ctx->id("CEN");
// FIXME
// if (is_sb_mac16(ctx, port.cell) || port.cell->type == ctx->id("ICESTORM_DSP"))
// return port.port == ctx->id("CE");
return false;
}
NEXTPNR_NAMESPACE_END

114
xc7/cells.h Normal file
View File

@ -0,0 +1,114 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "nextpnr.h"
#ifndef ICE40_CELLS_H
#define ICE40_CELLS_H
NEXTPNR_NAMESPACE_BEGIN
// Create a standard iCE40 cell and return it
// Name will be automatically assigned if not specified
std::unique_ptr<CellInfo> create_ice_cell(Context *ctx, IdString type, std::string name = "");
// Return true if a cell is a LUT
inline bool is_lut(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_LUT4"); }
// Return true if a cell is a flipflop
inline bool is_ff(const BaseCtx *ctx, const CellInfo *cell)
{
return cell->type == ctx->id("SB_DFF") || cell->type == ctx->id("SB_DFFE") || cell->type == ctx->id("SB_DFFSR") ||
cell->type == ctx->id("SB_DFFR") || cell->type == ctx->id("SB_DFFSS") || cell->type == ctx->id("SB_DFFS") ||
cell->type == ctx->id("SB_DFFESR") || cell->type == ctx->id("SB_DFFER") ||
cell->type == ctx->id("SB_DFFESS") || cell->type == ctx->id("SB_DFFES") ||
cell->type == ctx->id("SB_DFFN") || cell->type == ctx->id("SB_DFFNE") ||
cell->type == ctx->id("SB_DFFNSR") || cell->type == ctx->id("SB_DFFNR") ||
cell->type == ctx->id("SB_DFFNSS") || cell->type == ctx->id("SB_DFFNS") ||
cell->type == ctx->id("SB_DFFNESR") || cell->type == ctx->id("SB_DFFNER") ||
cell->type == ctx->id("SB_DFFNESS") || cell->type == ctx->id("SB_DFFNES");
}
inline bool is_carry(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_CARRY"); }
inline bool is_lc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("ICESTORM_LC"); }
// Return true if a cell is a SB_IO
inline bool is_sb_io(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_IO"); }
// Return true if a cell is a global buffer
inline bool is_gbuf(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_GB"); }
// Return true if a cell is a RAM
inline bool is_ram(const BaseCtx *ctx, const CellInfo *cell)
{
return cell->type == ctx->id("SB_RAM40_4K") || cell->type == ctx->id("SB_RAM40_4KNR") ||
cell->type == ctx->id("SB_RAM40_4KNW") || cell->type == ctx->id("SB_RAM40_4KNRNW");
}
inline bool is_sb_lfosc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_LFOSC"); }
inline bool is_sb_hfosc(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_HFOSC"); }
inline bool is_sb_spram(const BaseCtx *ctx, const CellInfo *cell) { return cell->type == ctx->id("SB_SPRAM256KA"); }
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");
}
inline bool is_sb_pll40_pad(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");
}
uint8_t sb_pll40_type(const BaseCtx *ctx, const CellInfo *cell);
// 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
// can be reconnected
void lut_to_lc(const Context *ctx, CellInfo *lut, CellInfo *lc, bool no_dff = true);
// Convert a SB_DFFx primitive to (part of) an ICESTORM_LC, setting parameters
// and reconnecting signals as necessary. If pass_thru_lut is True, the LUT will
// be configured as pass through and D connected to I0, otherwise D will be
// ignored
void dff_to_lc(const Context *ctx, CellInfo *dff, CellInfo *lc, bool pass_thru_lut = false);
// Convert a nextpnr IO buffer to a SB_IO
void nxio_to_sb(Context *ctx, CellInfo *nxio, CellInfo *sbio);
// Return true if a port is a clock port
bool is_clock_port(const BaseCtx *ctx, const PortRef &port);
// Return true if a port is a reset port
bool is_reset_port(const BaseCtx *ctx, const PortRef &port);
// Return true if a port is a clock enable port
bool is_enable_port(const BaseCtx *ctx, const PortRef &port);
NEXTPNR_NAMESPACE_END
#endif

287
xc7/chains.cc Normal file
View File

@ -0,0 +1,287 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "chains.h"
#include <algorithm>
#include <vector>
#include "cells.h"
#include "design_utils.h"
#include "log.h"
#include "place_common.h"
#include "util.h"
NEXTPNR_NAMESPACE_BEGIN
struct CellChain
{
std::vector<CellInfo *> cells;
};
// Generic chain finder
template <typename F1, typename F2, typename F3>
std::vector<CellChain> find_chains(const Context *ctx, F1 cell_type_predicate, F2 get_previous, F3 get_next,
size_t min_length = 2)
{
std::set<IdString> chained;
std::vector<CellChain> chains;
for (auto cell : sorted(ctx->cells)) {
if (chained.find(cell.first) != chained.end())
continue;
CellInfo *ci = cell.second;
if (cell_type_predicate(ctx, ci)) {
CellInfo *start = ci;
CellInfo *prev_start = ci;
while (prev_start != nullptr) {
start = prev_start;
prev_start = get_previous(ctx, start);
}
CellChain chain;
CellInfo *end = start;
while (end != nullptr) {
chain.cells.push_back(end);
end = get_next(ctx, end);
}
if (chain.cells.size() >= min_length) {
chains.push_back(chain);
for (auto c : chain.cells)
chained.insert(c->name);
}
}
}
return chains;
}
class ChainConstrainer
{
private:
Context *ctx;
// Split a carry chain into multiple legal chains
std::vector<CellChain> split_carry_chain(CellChain &carryc)
{
bool start_of_chain = true;
std::vector<CellChain> chains;
std::vector<const CellInfo *> tile;
const int max_length = (ctx->chip_info->height - 2) * 8 - 2;
auto curr_cell = carryc.cells.begin();
while (curr_cell != carryc.cells.end()) {
CellInfo *cell = *curr_cell;
if (tile.size() >= 8) {
tile.clear();
}
if (start_of_chain) {
tile.clear();
chains.emplace_back();
start_of_chain = false;
if (cell->ports.at(ctx->id("CIN")).net) {
// CIN is not constant and not part of a chain. Must feed in from fabric
CellInfo *feedin = make_carry_feed_in(cell, cell->ports.at(ctx->id("CIN")));
chains.back().cells.push_back(feedin);
tile.push_back(feedin);
}
}
tile.push_back(cell);
chains.back().cells.push_back(cell);
bool split_chain = (!ctx->logicCellsCompatible(tile.data(), tile.size())) || (int(chains.back().cells.size()) > max_length);
if (split_chain) {
CellInfo *passout = make_carry_pass_out(cell->ports.at(ctx->id("COUT")));
tile.pop_back();
chains.back().cells.back() = passout;
start_of_chain = true;
} else {
NetInfo *carry_net = cell->ports.at(ctx->id("COUT")).net;
bool at_end = (curr_cell == carryc.cells.end() - 1);
if (carry_net != nullptr && (carry_net->users.size() > 1 || at_end)) {
if (carry_net->users.size() > 2 ||
(net_only_drives(ctx, carry_net, is_lc, ctx->id("I3"), false) !=
net_only_drives(ctx, carry_net, is_lc, ctx->id("CIN"), false)) ||
(at_end && !net_only_drives(ctx, carry_net, is_lc, ctx->id("I3"), true))) {
CellInfo *passout = make_carry_pass_out(cell->ports.at(ctx->id("COUT")));
chains.back().cells.push_back(passout);
tile.push_back(passout);
start_of_chain = true;
}
}
++curr_cell;
}
}
return chains;
}
// Insert a logic cell to legalise a COUT->fabric connection
CellInfo *make_carry_pass_out(PortInfo &cout_port)
{
NPNR_ASSERT(cout_port.net != nullptr);
std::unique_ptr<CellInfo> lc = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
lc->params[ctx->id("LUT_INIT")] = "65280"; // 0xff00: O = I3
lc->params[ctx->id("CARRY_ENABLE")] = "1";
lc->ports.at(ctx->id("O")).net = cout_port.net;
std::unique_ptr<NetInfo> co_i3_net(new NetInfo());
co_i3_net->name = ctx->id(lc->name.str(ctx) + "$I3");
co_i3_net->driver = cout_port.net->driver;
PortRef i3_r;
i3_r.port = ctx->id("I3");
i3_r.cell = lc.get();
co_i3_net->users.push_back(i3_r);
PortRef o_r;
o_r.port = ctx->id("O");
o_r.cell = lc.get();
cout_port.net->driver = o_r;
lc->ports.at(ctx->id("I3")).net = co_i3_net.get();
cout_port.net = co_i3_net.get();
IdString co_i3_name = co_i3_net->name;
NPNR_ASSERT(ctx->nets.find(co_i3_name) == ctx->nets.end());
ctx->nets[co_i3_name] = std::move(co_i3_net);
IdString name = lc->name;
ctx->assignCellInfo(lc.get());
ctx->cells[lc->name] = std::move(lc);
return ctx->cells[name].get();
}
// Insert a logic cell to legalise a CIN->fabric connection
CellInfo *make_carry_feed_in(CellInfo *cin_cell, PortInfo &cin_port)
{
NPNR_ASSERT(cin_port.net != nullptr);
std::unique_ptr<CellInfo> lc = create_ice_cell(ctx, ctx->id("ICESTORM_LC"));
lc->params[ctx->id("CARRY_ENABLE")] = "1";
lc->params[ctx->id("CIN_CONST")] = "1";
lc->params[ctx->id("CIN_SET")] = "1";
lc->ports.at(ctx->id("I1")).net = cin_port.net;
cin_port.net->users.erase(std::remove_if(cin_port.net->users.begin(), cin_port.net->users.end(),
[cin_cell, cin_port](const PortRef &usr) {
return usr.cell == cin_cell && usr.port == cin_port.name;
}));
PortRef i1_ref;
i1_ref.cell = lc.get();
i1_ref.port = ctx->id("I1");
lc->ports.at(ctx->id("I1")).net->users.push_back(i1_ref);
std::unique_ptr<NetInfo> out_net(new NetInfo());
out_net->name = ctx->id(lc->name.str(ctx) + "$O");
PortRef drv_ref;
drv_ref.port = ctx->id("COUT");
drv_ref.cell = lc.get();
out_net->driver = drv_ref;
lc->ports.at(ctx->id("COUT")).net = out_net.get();
PortRef usr_ref;
usr_ref.port = cin_port.name;
usr_ref.cell = cin_cell;
out_net->users.push_back(usr_ref);
cin_cell->ports.at(cin_port.name).net = out_net.get();
IdString out_net_name = out_net->name;
NPNR_ASSERT(ctx->nets.find(out_net_name) == ctx->nets.end());
ctx->nets[out_net_name] = std::move(out_net);
IdString name = lc->name;
ctx->assignCellInfo(lc.get());
ctx->cells[lc->name] = std::move(lc);
return ctx->cells[name].get();
}
void process_carries()
{
std::vector<CellChain> carry_chains =
find_chains(ctx, [](const Context *ctx, const CellInfo *cell) { return is_lc(ctx, cell); },
[](const Context *ctx, const
CellInfo *cell) {
CellInfo *carry_prev =
net_driven_by(ctx, cell->ports.at(ctx->id("CIN")).net, is_lc, ctx->id("COUT"));
if (carry_prev != nullptr)
return carry_prev;
/*CellInfo *i3_prev = net_driven_by(ctx, cell->ports.at(ctx->id("I3")).net, is_lc,
ctx->id("COUT")); if (i3_prev != nullptr) return i3_prev;*/
return (CellInfo *)nullptr;
},
[](const Context *ctx, const CellInfo *cell) {
CellInfo *carry_next = net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_lc,
ctx->id("CIN"), false);
if (carry_next != nullptr)
return carry_next;
/*CellInfo *i3_next =
net_only_drives(ctx, cell->ports.at(ctx->id("COUT")).net, is_lc, ctx->id("I3"),
false); if (i3_next != nullptr) return i3_next;*/
return (CellInfo *)nullptr;
});
std::unordered_set<IdString> chained;
for (auto &base_chain : carry_chains) {
for (auto c : base_chain.cells)
chained.insert(c->name);
}
// Any cells not in chains, but with carry enabled, must also be put in a single-carry chain
// for correct processing
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (chained.find(cell.first) == chained.end() && is_lc(ctx, ci) &&
bool_or_default(ci->params, ctx->id("CARRY_ENABLE"))) {
CellChain sChain;
sChain.cells.push_back(ci);
chained.insert(cell.first);
carry_chains.push_back(sChain);
}
}
std::vector<CellChain> all_chains;
// Chain splitting
for (auto &base_chain : carry_chains) {
if (ctx->verbose) {
log_info("Found carry chain: \n");
for (auto entry : base_chain.cells)
log_info(" %s\n", entry->name.c_str(ctx));
log_info("\n");
}
std::vector<CellChain> split_chains = split_carry_chain(base_chain);
for (auto &chain : split_chains) {
all_chains.push_back(chain);
}
}
// Actual chain placement
for (auto &chain : all_chains) {
if (ctx->verbose)
log_info("Placing carry chain starting at '%s'\n", chain.cells.front()->name.c_str(ctx));
// Place carry chain
chain.cells.at(0)->constr_abs_z = true;
chain.cells.at(0)->constr_z = 0;
for (int i = 1; i < int(chain.cells.size()); i++) {
chain.cells.at(i)->constr_x = 0;
chain.cells.at(i)->constr_y = (i / 8);
chain.cells.at(i)->constr_z = i % 8;
chain.cells.at(i)->constr_abs_z = true;
chain.cells.at(i)->constr_parent = chain.cells.at(0);
chain.cells.at(0)->constr_children.push_back(chain.cells.at(i));
}
}
}
public:
ChainConstrainer(Context *ctx) : ctx(ctx){};
void constrain_chains() { process_carries(); }
};
void constrain_chains(Context *ctx)
{
log_info("Constraining chains...\n");
ChainConstrainer(ctx).constrain_chains();
}
NEXTPNR_NAMESPACE_END

27
xc7/chains.h Normal file
View File

@ -0,0 +1,27 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
// This finds chains, inserts LCs to legalise them as needed, and sets relative constraints as appropriate
void constrain_chains(Context *ctx);
NEXTPNR_NAMESPACE_END

1280
xc7/chipdb.py Normal file

File diff suppressed because it is too large Load Diff

437
xc7/constids.inc Normal file
View File

@ -0,0 +1,437 @@
// pin and port names
X(I0)
X(I1)
X(I2)
X(I3)
X(O)
X(LO)
X(CIN)
X(COUT)
X(CEN)
X(CLK)
X(SR)
X(MASK_0)
X(MASK_1)
X(MASK_2)
X(MASK_3)
X(MASK_4)
X(MASK_5)
X(MASK_6)
X(MASK_7)
X(MASK_8)
X(MASK_9)
X(MASK_10)
X(MASK_11)
X(MASK_12)
X(MASK_13)
X(MASK_14)
X(MASK_15)
X(RDATA_0)
X(RDATA_1)
X(RDATA_2)
X(RDATA_3)
X(RDATA_4)
X(RDATA_5)
X(RDATA_6)
X(RDATA_7)
X(RDATA_8)
X(RDATA_9)
X(RDATA_10)
X(RDATA_11)
X(RDATA_12)
X(RDATA_13)
X(RDATA_14)
X(RDATA_15)
X(WDATA_0)
X(WDATA_1)
X(WDATA_2)
X(WDATA_3)
X(WDATA_4)
X(WDATA_5)
X(WDATA_6)
X(WDATA_7)
X(WDATA_8)
X(WDATA_9)
X(WDATA_10)
X(WDATA_11)
X(WDATA_12)
X(WDATA_13)
X(WDATA_14)
X(WDATA_15)
X(WADDR_0)
X(WADDR_1)
X(WADDR_2)
X(WADDR_3)
X(WADDR_4)
X(WADDR_5)
X(WADDR_6)
X(WADDR_7)
X(WADDR_8)
X(WADDR_9)
X(WADDR_10)
X(RADDR_0)
X(RADDR_1)
X(RADDR_2)
X(RADDR_3)
X(RADDR_4)
X(RADDR_5)
X(RADDR_6)
X(RADDR_7)
X(RADDR_8)
X(RADDR_9)
X(RADDR_10)
X(WCLK)
X(WCLKE)
X(WE)
X(RCLK)
X(RCLKE)
X(RE)
X(PACKAGE_PIN)
X(LATCH_INPUT_VALUE)
X(CLOCK_ENABLE)
X(INPUT_CLK)
X(OUTPUT_CLK)
X(OUTPUT_ENABLE)
X(D_OUT_0)
X(D_OUT_1)
X(D_IN_0)
X(D_IN_1)
X(USER_SIGNAL_TO_GLOBAL_BUFFER)
X(GLOBAL_BUFFER_OUTPUT)
X(REFERENCECLK)
X(EXTFEEDBACK)
X(DYNAMICDELAY_0)
X(DYNAMICDELAY_1)
X(DYNAMICDELAY_2)
X(DYNAMICDELAY_3)
X(DYNAMICDELAY_4)
X(DYNAMICDELAY_5)
X(DYNAMICDELAY_6)
X(DYNAMICDELAY_7)
X(LOCK)
X(PLLOUT_A)
X(PLLOUT_B)
X(BYPASS)
X(RESETB)
X(LATCHINPUTVALUE)
X(SDO)
X(SDI)
X(SCLK)
X(BOOT)
X(S0)
X(S1)
X(ADDSUBBOT)
X(ADDSUBTOP)
X(AHOLD)
X(A_0)
X(A_1)
X(A_10)
X(A_11)
X(A_12)
X(A_13)
X(A_14)
X(A_15)
X(A_2)
X(A_3)
X(A_4)
X(A_5)
X(A_6)
X(A_7)
X(A_8)
X(A_9)
X(BHOLD)
X(B_0)
X(B_1)
X(B_10)
X(B_11)
X(B_12)
X(B_13)
X(B_14)
X(B_15)
X(B_2)
X(B_3)
X(B_4)
X(B_5)
X(B_6)
X(B_7)
X(B_8)
X(B_9)
X(CE)
X(CHOLD)
X(CI)
X(CO)
X(C_0)
X(C_1)
X(C_10)
X(C_11)
X(C_12)
X(C_13)
X(C_14)
X(C_15)
X(C_2)
X(C_3)
X(C_4)
X(C_5)
X(C_6)
X(C_7)
X(C_8)
X(C_9)
X(DHOLD)
X(D_0)
X(D_1)
X(D_10)
X(D_11)
X(D_12)
X(D_13)
X(D_14)
X(D_15)
X(D_2)
X(D_3)
X(D_4)
X(D_5)
X(D_6)
X(D_7)
X(D_8)
X(D_9)
X(IRSTBOT)
X(IRSTTOP)
X(OHOLDBOT)
X(OHOLDTOP)
X(OLOADBOT)
X(OLOADTOP)
X(ORSTBOT)
X(ORSTTOP)
X(O_0)
X(O_1)
X(O_10)
X(O_11)
X(O_12)
X(O_13)
X(O_14)
X(O_15)
X(O_16)
X(O_17)
X(O_18)
X(O_19)
X(O_2)
X(O_20)
X(O_21)
X(O_22)
X(O_23)
X(O_24)
X(O_25)
X(O_26)
X(O_27)
X(O_28)
X(O_29)
X(O_3)
X(O_30)
X(O_31)
X(O_4)
X(O_5)
X(O_6)
X(O_7)
X(O_8)
X(O_9)
X(CLKHF)
X(CLKHFEN)
X(CLKHFPU)
X(CLKHF_FABRIC)
X(TRIM0)
X(TRIM1)
X(TRIM2)
X(TRIM3)
X(TRIM4)
X(TRIM5)
X(TRIM6)
X(TRIM7)
X(TRIM8)
X(TRIM9)
X(CLKLF)
X(CLKLFEN)
X(CLKLFPU)
X(CLKLF_FABRIC)
X(I2CIRQ)
X(I2CWKUP)
X(SBACKO)
X(SBADRI0)
X(SBADRI1)
X(SBADRI2)
X(SBADRI3)
X(SBADRI4)
X(SBADRI5)
X(SBADRI6)
X(SBADRI7)
X(SBCLKI)
X(SBDATI0)
X(SBDATI1)
X(SBDATI2)
X(SBDATI3)
X(SBDATI4)
X(SBDATI5)
X(SBDATI6)
X(SBDATI7)
X(SBDATO0)
X(SBDATO1)
X(SBDATO2)
X(SBDATO3)
X(SBDATO4)
X(SBDATO5)
X(SBDATO6)
X(SBDATO7)
X(SBRWI)
X(SBSTBI)
X(SCLI)
X(SCLO)
X(SCLOE)
X(SDAI)
X(SDAO)
X(SDAOE)
X(MCSNO0)
X(MCSNO1)
X(MCSNO2)
X(MCSNO3)
X(MCSNOE0)
X(MCSNOE1)
X(MCSNOE2)
X(MCSNOE3)
X(MI)
X(MO)
X(MOE)
X(SCKI)
X(SCKO)
X(SCKOE)
X(SCSNI)
X(SI)
X(SO)
X(SOE)
X(SPIIRQ)
X(SPIWKUP)
X(PU_ENB)
X(WEAK_PU_ENB)
X(LEDDADDR0)
X(LEDDADDR1)
X(LEDDADDR2)
X(LEDDADDR3)
X(LEDDCLK)
X(LEDDCS)
X(LEDDDAT0)
X(LEDDDAT1)
X(LEDDDAT2)
X(LEDDDAT3)
X(LEDDDAT4)
X(LEDDDAT5)
X(LEDDDAT6)
X(LEDDDAT7)
X(LEDDDEN)
X(LEDDEXE)
X(LEDDON)
X(PWMOUT0)
X(PWMOUT1)
X(PWMOUT2)
X(CURREN)
X(RGB0PWM)
X(RGB1PWM)
X(RGB2PWM)
X(RGBLEDEN)
X(RGB0)
X(RGB1)
X(RGB2)
X(ADDRESS_0)
X(ADDRESS_1)
X(ADDRESS_10)
X(ADDRESS_11)
X(ADDRESS_12)
X(ADDRESS_13)
X(ADDRESS_2)
X(ADDRESS_3)
X(ADDRESS_4)
X(ADDRESS_5)
X(ADDRESS_6)
X(ADDRESS_7)
X(ADDRESS_8)
X(ADDRESS_9)
X(CHIPSELECT)
X(CLOCK)
X(DATAIN_0)
X(DATAIN_1)
X(DATAIN_10)
X(DATAIN_11)
X(DATAIN_12)
X(DATAIN_13)
X(DATAIN_14)
X(DATAIN_15)
X(DATAIN_2)
X(DATAIN_3)
X(DATAIN_4)
X(DATAIN_5)
X(DATAIN_6)
X(DATAIN_7)
X(DATAIN_8)
X(DATAIN_9)
X(DATAOUT_0)
X(DATAOUT_1)
X(DATAOUT_10)
X(DATAOUT_11)
X(DATAOUT_12)
X(DATAOUT_13)
X(DATAOUT_14)
X(DATAOUT_15)
X(DATAOUT_2)
X(DATAOUT_3)
X(DATAOUT_4)
X(DATAOUT_5)
X(DATAOUT_6)
X(DATAOUT_7)
X(DATAOUT_8)
X(DATAOUT_9)
X(MASKWREN_0)
X(MASKWREN_1)
X(MASKWREN_2)
X(MASKWREN_3)
X(POWEROFF)
X(SLEEP)
X(STANDBY)
X(WREN)
// cell and bel types
X(ICESTORM_LC)
X(ICESTORM_RAM)
X(SB_IO)
X(SB_GB)
X(ICESTORM_PLL)
X(SB_WARMBOOT)
X(ICESTORM_DSP)
X(ICESTORM_HFOSC)
X(ICESTORM_LFOSC)
X(SB_I2C)
X(SB_SPI)
X(IO_I3C)
X(SB_LEDDA_IP)
X(SB_RGBA_DRV)
X(ICESTORM_SPRAM)
// cell parameters
X(DFF_ENABLE)
X(CARRY_ENABLE)
X(NEG_CLK)

238
xc7/delay.cc Normal file
View File

@ -0,0 +1,238 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "nextpnr.h"
#include "router1.h"
NEXTPNR_NAMESPACE_BEGIN
#define NUM_FUZZ_ROUTES 100000
void ice40DelayFuzzerMain(Context *ctx)
{
std::vector<WireId> srcWires, dstWires;
for (int i = 0; i < ctx->chip_info->num_wires; i++) {
WireId wire;
wire.index = i;
switch (ctx->chip_info->wire_data[i].type) {
case WireInfoPOD::WIRE_TYPE_LUTFF_OUT:
srcWires.push_back(wire);
break;
case WireInfoPOD::WIRE_TYPE_LUTFF_IN_LUT:
dstWires.push_back(wire);
break;
default:
break;
}
}
ctx->shuffle(srcWires);
ctx->shuffle(dstWires);
int index = 0;
int cnt = 0;
while (cnt < NUM_FUZZ_ROUTES) {
if (index >= int(srcWires.size()) || index >= int(dstWires.size())) {
index = 0;
ctx->shuffle(srcWires);
ctx->shuffle(dstWires);
}
WireId src = srcWires[index];
WireId dst = dstWires[index++];
std::unordered_map<WireId, PipId> route;
#if NUM_FUZZ_ROUTES <= 1000
if (!ctx->getActualRouteDelay(src, dst, nullptr, &route, false))
continue;
#else
if (!ctx->getActualRouteDelay(src, dst, nullptr, &route, true))
continue;
#endif
WireId cursor = dst;
delay_t delay = 0;
while (1) {
delay += ctx->getWireDelay(cursor).maxDelay();
printf("%s %d %d %s %s %d %d\n", cursor == dst ? "dst" : "src",
int(ctx->chip_info->wire_data[cursor.index].x), int(ctx->chip_info->wire_data[cursor.index].y),
ctx->getWireType(cursor).c_str(ctx), ctx->getWireName(cursor).c_str(ctx), int(delay),
int(ctx->estimateDelay(cursor, dst)));
if (cursor == src)
break;
PipId pip = route.at(cursor);
delay += ctx->getPipDelay(pip).maxDelay();
cursor = ctx->getPipSrcWire(pip);
}
cnt++;
if (cnt % 100 == 0)
fprintf(stderr, "Fuzzed %d arcs.\n", cnt);
}
}
namespace {
struct model_params_t
{
int neighbourhood;
int model0_offset;
int model0_norm1;
int model1_offset;
int model1_norm1;
int model1_norm2;
int model1_norm3;
int model2_offset;
int model2_linear;
int model2_sqrt;
int delta_local;
int delta_lutffin;
int delta_sp4;
int delta_sp12;
static const model_params_t &get(const ArchArgs& args)
{
static const model_params_t model_hx8k = {588, 129253, 8658, 118333, 23915, -73105, 57696,
-86797, 89, 3706, -316, -575, -158, -296};
static const model_params_t model_lp8k = {867, 206236, 11043, 191910, 31074, -95972, 75739,
-309793, 30, 11056, -474, -856, -363, -536};
static const model_params_t model_up5k = {1761, 305798, 16705, 296830, 24430, -40369, 33038,
-162662, 94, 4705, -1099, -1761, -418, -838};
if (args.type == ArchArgs::HX1K || args.type == ArchArgs::HX8K)
return model_hx8k;
if (args.type == ArchArgs::LP384 || args.type == ArchArgs::LP1K || args.type == ArchArgs::LP8K)
return model_lp8k;
if (args.type == ArchArgs::UP5K)
return model_up5k;
NPNR_ASSERT(0);
}
};
} // namespace
delay_t Arch::estimateDelay(WireId src, WireId dst) const
{
NPNR_ASSERT(src != WireId());
int x1 = chip_info->wire_data[src.index].x;
int y1 = chip_info->wire_data[src.index].y;
int z1 = chip_info->wire_data[src.index].z;
int type = chip_info->wire_data[src.index].type;
NPNR_ASSERT(dst != WireId());
int x2 = chip_info->wire_data[dst.index].x;
int y2 = chip_info->wire_data[dst.index].y;
int z2 = chip_info->wire_data[dst.index].z;
int dx = abs(x2 - x1);
int dy = abs(y2 - y1);
const model_params_t &p = model_params_t::get(args);
delay_t v = p.neighbourhood;
if (dx > 1 || dy > 1)
v = (p.model0_offset + p.model0_norm1 * (dx + dy)) / 128;
if (dx == 0 && dy == 0) {
if (type == WireInfoPOD::WIRE_TYPE_LOCAL)
v += p.delta_local;
if (type == WireInfoPOD::WIRE_TYPE_LUTFF_IN || type == WireInfoPOD::WIRE_TYPE_LUTFF_IN_LUT)
v += (z1 == z2) ? p.delta_lutffin : 0;
}
if (type == WireInfoPOD::WIRE_TYPE_SP4_V || type == WireInfoPOD::WIRE_TYPE_SP4_H)
v += p.delta_sp4;
if (type == WireInfoPOD::WIRE_TYPE_SP12_V || type == WireInfoPOD::WIRE_TYPE_SP12_H)
v += p.delta_sp12;
return v;
}
delay_t Arch::predictDelay(const NetInfo *net_info, const PortRef &sink) const
{
const auto &driver = net_info->driver;
auto driver_loc = getBelLocation(driver.cell->bel);
auto sink_loc = getBelLocation(sink.cell->bel);
if (driver.port == id_COUT) {
if (driver_loc.y == sink_loc.y)
return 0;
return 250;
}
int dx = abs(sink_loc.x - driver_loc.x);
int dy = abs(sink_loc.y - driver_loc.y);
const model_params_t &p = model_params_t::get(args);
if (dx <= 1 && dy <= 1)
return p.neighbourhood;
#if 1
// Model #0
return (p.model0_offset + p.model0_norm1 * (dx + dy)) / 128;
#else
float norm1 = dx + dy;
float dx2 = dx * dx;
float dy2 = dy * dy;
float norm2 = sqrtf(dx2 + dy2);
float dx3 = dx2 * dx;
float dy3 = dy2 * dy;
float norm3 = powf(dx3 + dy3, 1.0 / 3.0);
// Model #1
float v = p.model1_offset;
v += p.model1_norm1 * norm1;
v += p.model1_norm2 * norm2;
v += p.model1_norm3 * norm3;
v /= 128;
// Model #2
v = p.model2_offset + p.model2_linear * v + p.model2_sqrt * sqrtf(v);
v /= 128;
return v;
#endif
}
NEXTPNR_NAMESPACE_END

84
xc7/family.cmake Normal file
View File

@ -0,0 +1,84 @@
if(ICE40_HX1K_ONLY)
set(devices 1k)
foreach (target ${family_targets})
target_compile_definitions(${target} PRIVATE ICE40_HX1K_ONLY=1)
endforeach (target)
else()
set(devices 384 1k 5k 8k)
endif()
set(DB_PY ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdb.py)
set(ICEBOX_ROOT "/usr/local/share/icebox" CACHE STRING "icebox location root")
file(MAKE_DIRECTORY ice40/chipdbs/)
add_library(ice40_chipdb OBJECT ice40/chipdbs/)
target_compile_definitions(ice40_chipdb PRIVATE NEXTPNR_NAMESPACE=nextpnr_${family})
target_include_directories(ice40_chipdb PRIVATE ${family}/)
if (MSVC)
target_sources(ice40_chipdb PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/ice40/resource/embed.cc)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/ice40/resources/chipdb.rc PROPERTIES LANGUAGE RC)
foreach (dev ${devices})
if (dev EQUAL "5k")
set(OPT_FAST "")
set(OPT_SLOW --slow ${ICEBOX_ROOT}/timings_up5k.txt)
elseif(dev EQUAL "384")
set(OPT_FAST "")
set(OPT_SLOW --slow ${ICEBOX_ROOT}/timings_lp384.txt)
else()
set(OPT_FAST --fast ${ICEBOX_ROOT}/timings_hx${dev}.txt)
set(OPT_SLOW --slow ${ICEBOX_ROOT}/timings_lp${dev}.txt)
endif()
set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt)
set(DEV_CC_BBA_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bba)
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bin)
set(DEV_CONSTIDS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/constids.inc)
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_CONSTIDS_INC} -g ${DEV_GFXH} ${OPT_FAST} ${OPT_SLOW} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}
DEPENDS ${DEV_TXT_DB} ${DB_PY}
)
add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND bbasm ${DEV_CC_BBA_DB} ${DEV_CC_DB}
DEPENDS bbasm ${DEV_CC_BBA_DB}
)
target_sources(ice40_chipdb PRIVATE ${DEV_CC_DB})
set_source_files_properties(${DEV_CC_DB} PROPERTIES HEADER_FILE_ONLY TRUE)
foreach (target ${family_targets})
target_sources(${target} PRIVATE $<TARGET_OBJECTS:ice40_chipdb> ${CMAKE_CURRENT_SOURCE_DIR}/ice40/resource/chipdb.rc)
endforeach (target)
endforeach (dev)
else()
target_compile_options(ice40_chipdb PRIVATE -g0 -O0 -w)
foreach (dev ${devices})
if (dev EQUAL "5k")
set(OPT_FAST "")
set(OPT_SLOW --slow ${ICEBOX_ROOT}/timings_up5k.txt)
elseif(dev EQUAL "384")
set(OPT_FAST "")
set(OPT_SLOW --slow ${ICEBOX_ROOT}/timings_lp384.txt)
else()
set(OPT_FAST --fast ${ICEBOX_ROOT}/timings_hx${dev}.txt)
set(OPT_SLOW --slow ${ICEBOX_ROOT}/timings_lp${dev}.txt)
endif()
set(DEV_TXT_DB ${ICEBOX_ROOT}/chipdb-${dev}.txt)
set(DEV_CC_BBA_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.bba)
set(DEV_CC_DB ${CMAKE_CURRENT_SOURCE_DIR}/ice40/chipdbs/chipdb-${dev}.cc)
set(DEV_CONSTIDS_INC ${CMAKE_CURRENT_SOURCE_DIR}/ice40/constids.inc)
set(DEV_GFXH ${CMAKE_CURRENT_SOURCE_DIR}/ice40/gfx.h)
add_custom_command(OUTPUT ${DEV_CC_BBA_DB}
COMMAND ${PYTHON_EXECUTABLE} ${DB_PY} -p ${DEV_CONSTIDS_INC} -g ${DEV_GFXH} ${OPT_FAST} ${OPT_SLOW} ${DEV_TXT_DB} > ${DEV_CC_BBA_DB}.new
COMMAND mv ${DEV_CC_BBA_DB}.new ${DEV_CC_BBA_DB}
DEPENDS ${DEV_TXT_DB} ${DB_PY}
)
add_custom_command(OUTPUT ${DEV_CC_DB}
COMMAND bbasm --c ${DEV_CC_BBA_DB} ${DEV_CC_DB}.new
COMMAND mv ${DEV_CC_DB}.new ${DEV_CC_DB}
DEPENDS bbasm ${DEV_CC_BBA_DB}
)
target_sources(ice40_chipdb PRIVATE ${DEV_CC_DB})
foreach (target ${family_targets})
target_sources(${target} PRIVATE $<TARGET_OBJECTS:ice40_chipdb>)
endforeach (target)
endforeach (dev)
endif()

766
xc7/gfx.cc Normal file
View File

@ -0,0 +1,766 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "gfx.h"
NEXTPNR_NAMESPACE_BEGIN
void gfxTileWire(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId id, GraphicElement::style_t style)
{
GraphicElement el;
el.type = GraphicElement::TYPE_LINE;
el.style = style;
// Horizontal Span-4 Wires
if (id >= TILE_WIRE_SP4_H_L_36 && id <= TILE_WIRE_SP4_H_L_47) {
int idx = (id - TILE_WIRE_SP4_H_L_36) + 48;
float y1 = y + 1.0 - (0.03 + 0.0025 * (60 - (idx ^ 1)));
float y2 = y + 1.0 - (0.03 + 0.0025 * (60 - idx));
el.x1 = x;
el.x2 = x + 0.01;
el.y1 = y1;
el.y2 = y1;
g.push_back(el);
el.x1 = x + 0.01;
el.x2 = x + 0.02;
el.y1 = y1;
el.y2 = y2;
g.push_back(el);
el.x1 = x + 0.02;
el.x2 = x + 0.9;
el.y1 = y2;
el.y2 = y2;
g.push_back(el);
el.x1 = x + main_swbox_x1 + 0.0025 * (idx + 35);
el.x2 = el.x1;
el.y1 = y2;
el.y2 = y + main_swbox_y2;
g.push_back(el);
}
if (id >= TILE_WIRE_SP4_H_R_0 && id <= TILE_WIRE_SP4_H_R_47) {
int idx = id - TILE_WIRE_SP4_H_R_0;
float y1 = y + 1.0 - (0.03 + 0.0025 * (60 - idx));
float y2 = y + 1.0 - (0.03 + 0.0025 * (60 - (idx ^ 1)));
float y3 = y + 1.0 - (0.03 + 0.0025 * (60 - (idx ^ 1) - 12));
if (idx >= 12) {
el.x1 = x;
el.x2 = x + 0.01;
el.y1 = y1;
el.y2 = y1;
g.push_back(el);
el.x1 = x + 0.01;
el.x2 = x + 0.02;
el.y1 = y1;
el.y2 = y2;
g.push_back(el);
}
el.x1 = x + 0.02;
el.x2 = x + 0.9;
el.y1 = y2;
el.y2 = y2;
g.push_back(el);
el.x1 = x + 0.9;
el.x2 = x + 1.0;
el.y1 = y2;
el.y2 = y3;
g.push_back(el);
el.x1 = x + main_swbox_x1 + 0.0025 * ((idx ^ 1) + 35);
el.x2 = el.x1;
el.y1 = y2;
el.y2 = y + main_swbox_y2;
g.push_back(el);
}
// Vertical Span-4 Wires
if (id >= TILE_WIRE_SP4_V_T_36 && id <= TILE_WIRE_SP4_V_T_47) {
int idx = (id - TILE_WIRE_SP4_V_T_36) + 48;
float x1 = x + 0.03 + 0.0025 * (60 - (idx ^ 1));
float x2 = x + 0.03 + 0.0025 * (60 - idx);
el.y1 = y + 1.00;
el.y2 = y + 0.99;
el.x1 = x1;
el.x2 = x1;
g.push_back(el);
el.y1 = y + 0.99;
el.y2 = y + 0.98;
el.x1 = x1;
el.x2 = x2;
g.push_back(el);
el.y1 = y + 0.98;
el.y2 = y + 0.10;
el.x1 = x2;
el.x2 = x2;
g.push_back(el);
el.y1 = y + 1.0 - (0.03 + 0.0025 * (270 - idx));
el.y2 = el.y1;
el.x1 = x2;
el.x2 = x + main_swbox_x1;
g.push_back(el);
}
if (id >= TILE_WIRE_SP4_V_B_0 && id <= TILE_WIRE_SP4_V_B_47) {
int idx = id - TILE_WIRE_SP4_V_B_0;
float x1 = x + 0.03 + 0.0025 * (60 - idx);
float x2 = x + 0.03 + 0.0025 * (60 - (idx ^ 1));
float x3 = x + 0.03 + 0.0025 * (60 - (idx ^ 1) - 12);
if (idx >= 12) {
el.y1 = y + 1.00;
el.y2 = y + 0.99;
el.x1 = x1;
el.x2 = x1;
g.push_back(el);
el.y1 = y + 0.99;
el.y2 = y + 0.98;
el.x1 = x1;
el.x2 = x2;
g.push_back(el);
}
el.y1 = y + 0.98;
el.y2 = y + 0.10;
el.x1 = x2;
el.x2 = x2;
g.push_back(el);
el.y1 = y + 0.10;
el.y2 = y;
el.x1 = x2;
el.x2 = x3;
g.push_back(el);
el.y1 = y + 1.0 - (0.03 + 0.0025 * (145 - (idx ^ 1)));
el.y2 = el.y1;
el.x1 = x;
el.x2 = x2;
g.push_back(el);
el.y1 = y + 1.0 - (0.03 + 0.0025 * (270 - (idx ^ 1)));
el.y2 = el.y1;
el.x1 = x2;
el.x2 = x + main_swbox_x1;
g.push_back(el);
}
// Horizontal Span-12 Wires
if (id >= TILE_WIRE_SP12_H_L_22 && id <= TILE_WIRE_SP12_H_L_23) {
int idx = (id - TILE_WIRE_SP12_H_L_22) + 24;
float y1 = y + 1.0 - (0.03 + 0.0025 * (90 - (idx ^ 1)));
float y2 = y + 1.0 - (0.03 + 0.0025 * (90 - idx));
el.x1 = x;
el.x2 = x + 0.01;
el.y1 = y1;
el.y2 = y1;
g.push_back(el);
el.x1 = x + 0.01;
el.x2 = x + 0.02;
el.y1 = y1;
el.y2 = y2;
g.push_back(el);
el.x1 = x + 0.02;
el.x2 = x + 0.98333;
el.y1 = y2;
el.y2 = y2;
g.push_back(el);
el.x1 = x + main_swbox_x1 + 0.0025 * (idx + 5);
el.x2 = el.x1;
el.y1 = y2;
el.y2 = y + main_swbox_y2;
g.push_back(el);
}
if (id >= TILE_WIRE_SP12_H_R_0 && id <= TILE_WIRE_SP12_H_R_23) {
int idx = id - TILE_WIRE_SP12_H_R_0;
float y1 = y + 1.0 - (0.03 + 0.0025 * (90 - idx));
float y2 = y + 1.0 - (0.03 + 0.0025 * (90 - (idx ^ 1)));
float y3 = y + 1.0 - (0.03 + 0.0025 * (90 - (idx ^ 1) - 2));
if (idx >= 2) {
el.x1 = x;
el.x2 = x + 0.01;
el.y1 = y1;
el.y2 = y1;
g.push_back(el);
el.x1 = x + 0.01;
el.x2 = x + 0.02;
el.y1 = y1;
el.y2 = y2;
g.push_back(el);
}
el.x1 = x + 0.02;
el.x2 = x + 0.98333;
el.y1 = y2;
el.y2 = y2;
g.push_back(el);
el.x1 = x + 0.98333;
el.x2 = x + 1.0;
el.y1 = y2;
el.y2 = y3;
g.push_back(el);
el.x1 = x + main_swbox_x1 + 0.0025 * ((idx ^ 1) + 5);
el.x2 = el.x1;
el.y1 = y2;
el.y2 = y + main_swbox_y2;
g.push_back(el);
}
// Vertical Right Span-4
if (id >= TILE_WIRE_SP4_R_V_B_0 && id <= TILE_WIRE_SP4_R_V_B_47) {
int idx = id - TILE_WIRE_SP4_R_V_B_0;
float y1 = y + 1.0 - (0.03 + 0.0025 * (145 - (idx ^ 1)));
el.y1 = y1;
el.y2 = y1;
el.x1 = x + main_swbox_x2;
el.x2 = x + 1.0;
g.push_back(el);
}
// Vertical Span-12 Wires
if (id >= TILE_WIRE_SP12_V_T_22 && id <= TILE_WIRE_SP12_V_T_23) {
int idx = (id - TILE_WIRE_SP12_V_T_22) + 24;
float x1 = x + 0.03 + 0.0025 * (90 - (idx ^ 1));
float x2 = x + 0.03 + 0.0025 * (90 - idx);
el.y1 = y + 1.00;
el.y2 = y + 0.99;
el.x1 = x1;
el.x2 = x1;
g.push_back(el);
el.y1 = y + 0.99;
el.y2 = y + 0.98;
el.x1 = x1;
el.x2 = x2;
g.push_back(el);
el.y1 = y + 0.98;
el.y2 = y + 0.01667;
el.x1 = x2;
el.x2 = x2;
g.push_back(el);
el.y1 = y + 1.0 - (0.03 + 0.0025 * (300 - idx));
el.y2 = el.y1;
el.x1 = x2;
el.x2 = x + main_swbox_x1;
g.push_back(el);
}
if (id >= TILE_WIRE_SP12_V_B_0 && id <= TILE_WIRE_SP12_V_B_23) {
int idx = id - TILE_WIRE_SP12_V_B_0;
float x1 = x + 0.03 + 0.0025 * (90 - idx);
float x2 = x + 0.03 + 0.0025 * (90 - (idx ^ 1));
float x3 = x + 0.03 + 0.0025 * (90 - (idx ^ 1) - 2);
if (idx >= 2) {
el.y1 = y + 1.00;
el.y2 = y + 0.99;
el.x1 = x1;
el.x2 = x1;
g.push_back(el);
el.y1 = y + 0.99;
el.y2 = y + 0.98;
el.x1 = x1;
el.x2 = x2;
g.push_back(el);
}
el.y1 = y + 0.98;
el.y2 = y + 0.01667;
el.x1 = x2;
el.x2 = x2;
g.push_back(el);
el.y1 = y + 0.01667;
el.y2 = y;
el.x1 = x2;
el.x2 = x3;
g.push_back(el);
el.y1 = y + 1.0 - (0.03 + 0.0025 * (300 - (idx ^ 1)));
el.y2 = el.y1;
el.x1 = x2;
el.x2 = x + main_swbox_x1;
g.push_back(el);
}
// Global2Local
if (id >= TILE_WIRE_GLB2LOCAL_0 && id <= TILE_WIRE_GLB2LOCAL_3) {
int idx = id - TILE_WIRE_GLB2LOCAL_0;
el.x1 = x + main_swbox_x1 + 0.005 * (idx + 5);
el.x2 = el.x1;
el.y1 = y + main_swbox_y1;
el.y2 = el.y1 - 0.02;
g.push_back(el);
}
// GlobalNets
if (id >= TILE_WIRE_GLB_NETWK_0 && id <= TILE_WIRE_GLB_NETWK_7) {
int idx = id - TILE_WIRE_GLB_NETWK_0;
el.x1 = x + main_swbox_x1 - 0.05;
el.x2 = x + main_swbox_x1;
el.y1 = y + main_swbox_y1 + 0.005 * (13 - idx);
el.y2 = el.y1;
g.push_back(el);
}
// Neighbours
if (id >= TILE_WIRE_NEIGH_OP_BNL_0 && id <= TILE_WIRE_NEIGH_OP_TOP_7) {
int idx = id - TILE_WIRE_NEIGH_OP_BNL_0;
el.y1 = y + main_swbox_y2 - (0.0025 * (idx + 10) + 0.01 * (idx / 8));
el.y2 = el.y1;
el.x1 = x + main_swbox_x1 - 0.05;
el.x2 = x + main_swbox_x1;
g.push_back(el);
}
// Local Tracks
if (id >= TILE_WIRE_LOCAL_G0_0 && id <= TILE_WIRE_LOCAL_G3_7) {
int idx = id - TILE_WIRE_LOCAL_G0_0;
el.x1 = x + main_swbox_x2;
el.x2 = x + local_swbox_x1;
float yoff = y + (local_swbox_y1 + local_swbox_y2) / 2 - 0.005 * 16 - 0.075;
el.y1 = yoff + 0.005 * idx + 0.05 * (idx / 8);
el.y2 = el.y1;
g.push_back(el);
}
// LC Inputs
if (id >= TILE_WIRE_LUTFF_0_IN_0 && id <= TILE_WIRE_LUTFF_7_IN_3) {
int idx = id - TILE_WIRE_LUTFF_0_IN_0;
int z = idx / 4;
int input = idx % 4;
el.x1 = x + local_swbox_x2;
el.x2 = x + lut_swbox_x1;
el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * input) + z * logic_cell_pitch;
el.y2 = el.y1;
g.push_back(el);
}
if (id >= TILE_WIRE_LUTFF_0_IN_0_LUT && id <= TILE_WIRE_LUTFF_7_IN_3_LUT) {
int idx = id - TILE_WIRE_LUTFF_0_IN_0_LUT;
int z = idx / 4;
int input = idx % 4;
el.x1 = x + lut_swbox_x2;
el.x2 = x + logic_cell_x1;
el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * input) + z * logic_cell_pitch;
el.y2 = el.y1;
g.push_back(el);
}
// LC Outputs
if (id >= TILE_WIRE_LUTFF_0_OUT && id <= TILE_WIRE_LUTFF_7_OUT) {
int idx = id - TILE_WIRE_LUTFF_0_OUT;
float y1 = y + 1.0 - (0.03 + 0.0025 * (152 + idx));
el.y1 = y1;
el.y2 = y1;
el.x1 = x + main_swbox_x2;
el.x2 = x + 0.97 + 0.0025 * (7 - idx);
g.push_back(el);
el.y1 = y1;
el.y2 = y + (logic_cell_y1 + logic_cell_y2) / 2 + idx * logic_cell_pitch;
el.x1 = el.x2;
g.push_back(el);
el.y1 = el.y2;
el.x1 = x + logic_cell_x2;
g.push_back(el);
}
// LC Control
if (id >= TILE_WIRE_LUTFF_GLOBAL_CEN && id <= TILE_WIRE_LUTFF_GLOBAL_S_R) {
int idx = id - TILE_WIRE_LUTFF_GLOBAL_CEN;
el.x1 = x + main_swbox_x2 - 0.005 * (idx + 5);
el.x2 = el.x1;
el.y1 = y + main_swbox_y1;
el.y2 = el.y1 - 0.005 * (idx + 2);
g.push_back(el);
el.y1 = el.y2;
el.x2 = x + logic_cell_x2 - 0.005 * (2 - idx + 5);
g.push_back(el);
el.y2 = y + logic_cell_y1;
el.x1 = el.x2;
g.push_back(el);
for (int i = 0; i < 7; i++) {
el.y1 = y + logic_cell_y2 + i * logic_cell_pitch;
el.y2 = y + logic_cell_y1 + (i + 1) * logic_cell_pitch;
g.push_back(el);
}
}
// LC Cascade
if (id >= TILE_WIRE_LUTFF_0_LOUT && id <= TILE_WIRE_LUTFF_6_LOUT) {
int idx = id - TILE_WIRE_LUTFF_0_LOUT;
el.x1 = x + logic_cell_x1 + 0.005 * 5;
el.x2 = el.x1;
el.y1 = y + logic_cell_y2 + idx * logic_cell_pitch;
el.y2 = y + logic_cell_y1 + (idx + 1) * logic_cell_pitch;
g.push_back(el);
}
// Carry Chain
if (id >= TILE_WIRE_LUTFF_0_COUT && id <= TILE_WIRE_LUTFF_7_COUT) {
int idx = id - TILE_WIRE_LUTFF_0_COUT;
el.x1 = x + logic_cell_x1 + 0.005 * 3;
el.x2 = el.x1;
el.y1 = y + logic_cell_y2 + idx * logic_cell_pitch;
el.y2 = y + (idx < 7 ? logic_cell_y1 + (idx + 1) * logic_cell_pitch : 1.0);
g.push_back(el);
}
if (id == TILE_WIRE_CARRY_IN) {
el.x1 = x + logic_cell_x1 + 0.005 * 3;
el.x2 = el.x1;
el.y1 = y;
el.y2 = y + 0.01;
g.push_back(el);
}
if (id == TILE_WIRE_CARRY_IN_MUX) {
el.x1 = x + logic_cell_x1 + 0.005 * 3;
el.x2 = el.x1;
el.y1 = y + 0.02;
el.y2 = y + logic_cell_y1;
g.push_back(el);
}
}
static bool getWireXY_main(GfxTileWireId id, float &x, float &y)
{
// Horizontal Span-4 Wires
if (id >= TILE_WIRE_SP4_H_L_36 && id <= TILE_WIRE_SP4_H_L_47) {
int idx = (id - TILE_WIRE_SP4_H_L_36) + 48;
x = main_swbox_x1 + 0.0025 * (idx + 35);
y = main_swbox_y2;
return true;
}
if (id >= TILE_WIRE_SP4_H_R_0 && id <= TILE_WIRE_SP4_H_R_47) {
int idx = id - TILE_WIRE_SP4_H_R_0;
x = main_swbox_x1 + 0.0025 * ((idx ^ 1) + 35);
y = main_swbox_y2;
return true;
}
// Vertical Span-4 Wires
if (id >= TILE_WIRE_SP4_V_T_36 && id <= TILE_WIRE_SP4_V_T_47) {
int idx = (id - TILE_WIRE_SP4_V_T_36) + 48;
y = 1.0 - (0.03 + 0.0025 * (270 - idx));
x = main_swbox_x1;
return true;
}
if (id >= TILE_WIRE_SP4_V_B_0 && id <= TILE_WIRE_SP4_V_B_47) {
int idx = id - TILE_WIRE_SP4_V_B_0;
y = 1.0 - (0.03 + 0.0025 * (270 - (idx ^ 1)));
x = main_swbox_x1;
return true;
}
// Horizontal Span-12 Wires
if (id >= TILE_WIRE_SP12_H_L_22 && id <= TILE_WIRE_SP12_H_L_23) {
int idx = (id - TILE_WIRE_SP12_H_L_22) + 24;
x = main_swbox_x1 + 0.0025 * (idx + 5);
y = main_swbox_y2;
return true;
}
if (id >= TILE_WIRE_SP12_H_R_0 && id <= TILE_WIRE_SP12_H_R_23) {
int idx = id - TILE_WIRE_SP12_H_R_0;
x = main_swbox_x1 + 0.0025 * ((idx ^ 1) + 5);
y = main_swbox_y2;
return true;
}
// Vertical Right Span-4
if (id >= TILE_WIRE_SP4_R_V_B_0 && id <= TILE_WIRE_SP4_R_V_B_47) {
int idx = id - TILE_WIRE_SP4_R_V_B_0;
y = 1.0 - (0.03 + 0.0025 * (145 - (idx ^ 1)));
x = main_swbox_x2;
return true;
}
// Vertical Span-12 Wires
if (id >= TILE_WIRE_SP12_V_T_22 && id <= TILE_WIRE_SP12_V_T_23) {
int idx = (id - TILE_WIRE_SP12_V_T_22) + 24;
y = 1.0 - (0.03 + 0.0025 * (300 - idx));
x = main_swbox_x1;
return true;
}
if (id >= TILE_WIRE_SP12_V_B_0 && id <= TILE_WIRE_SP12_V_B_23) {
int idx = id - TILE_WIRE_SP12_V_B_0;
y = 1.0 - (0.03 + 0.0025 * (300 - (idx ^ 1)));
x = main_swbox_x1;
return true;
}
// Global2Local
if (id >= TILE_WIRE_GLB2LOCAL_0 && id <= TILE_WIRE_GLB2LOCAL_3) {
int idx = id - TILE_WIRE_GLB2LOCAL_0;
x = main_swbox_x1 + 0.005 * (idx + 5);
y = main_swbox_y1;
return true;
}
// GlobalNets
if (id >= TILE_WIRE_GLB_NETWK_0 && id <= TILE_WIRE_GLB_NETWK_7) {
int idx = id - TILE_WIRE_GLB_NETWK_0;
x = main_swbox_x1;
y = main_swbox_y1 + 0.005 * (13 - idx);
return true;
}
// Neighbours
if (id >= TILE_WIRE_NEIGH_OP_BNL_0 && id <= TILE_WIRE_NEIGH_OP_TOP_7) {
int idx = id - TILE_WIRE_NEIGH_OP_BNL_0;
y = main_swbox_y2 - (0.0025 * (idx + 10) + 0.01 * (idx / 8));
x = main_swbox_x1;
return true;
}
// Local Tracks
if (id >= TILE_WIRE_LOCAL_G0_0 && id <= TILE_WIRE_LOCAL_G3_7) {
int idx = id - TILE_WIRE_LOCAL_G0_0;
float yoff = (local_swbox_y1 + local_swbox_y2) / 2 - 0.005 * 16 - 0.075;
x = main_swbox_x2;
y = yoff + 0.005 * idx + 0.05 * (idx / 8);
return true;
}
// LC Outputs
if (id >= TILE_WIRE_LUTFF_0_OUT && id <= TILE_WIRE_LUTFF_7_OUT) {
int idx = id - TILE_WIRE_LUTFF_0_OUT;
y = 1.0 - (0.03 + 0.0025 * (152 + idx));
x = main_swbox_x2;
return true;
}
// LC Control
if (id >= TILE_WIRE_LUTFF_GLOBAL_CEN && id <= TILE_WIRE_LUTFF_GLOBAL_S_R) {
int idx = id - TILE_WIRE_LUTFF_GLOBAL_CEN;
x = main_swbox_x2 - 0.005 * (idx + 5);
y = main_swbox_y1;
return true;
}
return false;
}
static bool getWireXY_local(GfxTileWireId id, float &x, float &y)
{
if (id >= TILE_WIRE_LOCAL_G0_0 && id <= TILE_WIRE_LOCAL_G3_7) {
int idx = id - TILE_WIRE_LOCAL_G0_0;
float yoff = (local_swbox_y1 + local_swbox_y2) / 2 - 0.005 * 16 - 0.075;
x = local_swbox_x1;
y = yoff + 0.005 * idx + 0.05 * (idx / 8);
return true;
}
if (id >= TILE_WIRE_LUTFF_0_IN_0 && id <= TILE_WIRE_LUTFF_7_IN_3) {
int idx = id - TILE_WIRE_LUTFF_0_IN_0;
int z = idx / 4;
int input = idx % 4;
x = local_swbox_x2;
y = (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * input) + z * logic_cell_pitch;
return true;
}
return false;
}
void pipGfx(std::vector<GraphicElement> &g, int x, int y, float x1, float y1, float x2, float y2, float swx1,
float swy1, float swx2, float swy2, GraphicElement::style_t style)
{
float tx = 0.5 * (x1 + x2);
float ty = 0.5 * (y1 + y2);
GraphicElement el;
el.type = GraphicElement::TYPE_ARROW;
el.style = style;
if (fabsf(x1 - swx1) < 0.001 && fabsf(x2 - swx1) < 0.001) {
tx = x1 + 0.25 * fabsf(y1 - y2);
goto edge_pip;
}
if (fabsf(x1 - swx2) < 0.001 && fabsf(x2 - swx2) < 0.001) {
tx = x1 - 0.25 * fabsf(y1 - y2);
goto edge_pip;
}
if (fabsf(y1 - swy1) < 0.001 && fabsf(y2 - swy1) < 0.001) {
ty = y1 + 0.25 * fabsf(x1 - x2);
goto edge_pip;
}
if (fabsf(y1 - swy1) < 0.001 && fabsf(y2 - swy1) < 0.001) {
ty = y1 + 0.25 * fabsf(x1 - x2);
goto edge_pip;
}
el.x1 = x + x1;
el.y1 = y + y1;
el.x2 = x + x2;
el.y2 = y + y2;
g.push_back(el);
return;
edge_pip:
el.x1 = x + x1;
el.y1 = y + y1;
el.x2 = x + tx;
el.y2 = y + ty;
g.push_back(el);
el.x1 = x + tx;
el.y1 = y + ty;
el.x2 = x + x2;
el.y2 = y + y2;
g.push_back(el);
}
void gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src, GfxTileWireId dst,
GraphicElement::style_t style)
{
float x1, y1, x2, y2;
if (getWireXY_main(src, x1, y1) && getWireXY_main(dst, x2, y2)) {
pipGfx(g, x, y, x1, y1, x2, y2, main_swbox_x1, main_swbox_y1, main_swbox_x2, main_swbox_y2, style);
return;
}
if (getWireXY_local(src, x1, y1) && getWireXY_local(dst, x2, y2)) {
pipGfx(g, x, y, x1, y1, x2, y2, local_swbox_x1, local_swbox_y1, local_swbox_x2, local_swbox_y2, style);
return;
}
if (TILE_WIRE_LUTFF_0_IN_0_LUT <= src && src <= TILE_WIRE_LUTFF_7_IN_3_LUT && TILE_WIRE_LUTFF_0_OUT <= dst &&
dst <= TILE_WIRE_LUTFF_7_OUT) {
int lut_idx = (src - TILE_WIRE_LUTFF_0_IN_0_LUT) / 4;
int in_idx = (src - TILE_WIRE_LUTFF_0_IN_0_LUT) % 4;
GraphicElement el;
el.type = GraphicElement::TYPE_ARROW;
el.style = style;
el.x1 = x + logic_cell_x1;
el.x2 = x + logic_cell_x2;
el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * in_idx) + lut_idx * logic_cell_pitch;
el.y2 = y + (logic_cell_y1 + logic_cell_y2) / 2 + lut_idx * logic_cell_pitch;
g.push_back(el);
return;
}
if (TILE_WIRE_LUTFF_0_IN_0 <= src && src <= TILE_WIRE_LUTFF_7_IN_3 && TILE_WIRE_LUTFF_0_IN_0_LUT <= dst &&
dst <= TILE_WIRE_LUTFF_7_IN_3_LUT) {
int lut_idx = (src - TILE_WIRE_LUTFF_0_IN_0) / 4;
int in_idx = (src - TILE_WIRE_LUTFF_0_IN_0) % 4;
int out_idx = (dst - TILE_WIRE_LUTFF_0_IN_0_LUT) % 4;
GraphicElement el;
el.type = GraphicElement::TYPE_ARROW;
el.style = style;
el.x1 = x + lut_swbox_x1;
el.x2 = x + lut_swbox_x2;
el.y1 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * in_idx) + lut_idx * logic_cell_pitch;
el.y2 = y + (logic_cell_y1 + logic_cell_y2) / 2 - 0.0075 + (0.005 * out_idx) + lut_idx * logic_cell_pitch;
g.push_back(el);
return;
}
if (src == TILE_WIRE_CARRY_IN && dst == TILE_WIRE_CARRY_IN_MUX) {
GraphicElement el;
el.type = GraphicElement::TYPE_ARROW;
el.style = style;
el.x1 = x + logic_cell_x1 + 0.005 * 3;
el.x2 = el.x1;
el.y1 = y + 0.01;
el.y2 = y + 0.02;
g.push_back(el);
return;
}
}
NEXTPNR_NAMESPACE_END

523
xc7/gfx.h Normal file
View File

@ -0,0 +1,523 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef GFX_H
#define GFX_H
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
const float main_swbox_x1 = 0.35;
const float main_swbox_x2 = 0.60;
const float main_swbox_y1 = 0.05;
const float main_swbox_y2 = 0.73;
const float local_swbox_x1 = 0.63;
const float local_swbox_x2 = 0.73;
const float local_swbox_y1 = 0.05;
const float local_swbox_y2 = 0.55;
const float lut_swbox_x1 = 0.76;
const float lut_swbox_x2 = 0.80;
const float logic_cell_x1 = 0.83;
const float logic_cell_x2 = 0.95;
const float logic_cell_y1 = 0.05;
const float logic_cell_y2 = 0.10;
const float logic_cell_pitch = 0.0625;
enum GfxTileWireId
{
TILE_WIRE_GLB2LOCAL_0,
TILE_WIRE_GLB2LOCAL_1,
TILE_WIRE_GLB2LOCAL_2,
TILE_WIRE_GLB2LOCAL_3,
TILE_WIRE_GLB_NETWK_0,
TILE_WIRE_GLB_NETWK_1,
TILE_WIRE_GLB_NETWK_2,
TILE_WIRE_GLB_NETWK_3,
TILE_WIRE_GLB_NETWK_4,
TILE_WIRE_GLB_NETWK_5,
TILE_WIRE_GLB_NETWK_6,
TILE_WIRE_GLB_NETWK_7,
TILE_WIRE_LOCAL_G0_0,
TILE_WIRE_LOCAL_G0_1,
TILE_WIRE_LOCAL_G0_2,
TILE_WIRE_LOCAL_G0_3,
TILE_WIRE_LOCAL_G0_4,
TILE_WIRE_LOCAL_G0_5,
TILE_WIRE_LOCAL_G0_6,
TILE_WIRE_LOCAL_G0_7,
TILE_WIRE_LOCAL_G1_0,
TILE_WIRE_LOCAL_G1_1,
TILE_WIRE_LOCAL_G1_2,
TILE_WIRE_LOCAL_G1_3,
TILE_WIRE_LOCAL_G1_4,
TILE_WIRE_LOCAL_G1_5,
TILE_WIRE_LOCAL_G1_6,
TILE_WIRE_LOCAL_G1_7,
TILE_WIRE_LOCAL_G2_0,
TILE_WIRE_LOCAL_G2_1,
TILE_WIRE_LOCAL_G2_2,
TILE_WIRE_LOCAL_G2_3,
TILE_WIRE_LOCAL_G2_4,
TILE_WIRE_LOCAL_G2_5,
TILE_WIRE_LOCAL_G2_6,
TILE_WIRE_LOCAL_G2_7,
TILE_WIRE_LOCAL_G3_0,
TILE_WIRE_LOCAL_G3_1,
TILE_WIRE_LOCAL_G3_2,
TILE_WIRE_LOCAL_G3_3,
TILE_WIRE_LOCAL_G3_4,
TILE_WIRE_LOCAL_G3_5,
TILE_WIRE_LOCAL_G3_6,
TILE_WIRE_LOCAL_G3_7,
TILE_WIRE_LUTFF_0_IN_0,
TILE_WIRE_LUTFF_0_IN_1,
TILE_WIRE_LUTFF_0_IN_2,
TILE_WIRE_LUTFF_0_IN_3,
TILE_WIRE_LUTFF_1_IN_0,
TILE_WIRE_LUTFF_1_IN_1,
TILE_WIRE_LUTFF_1_IN_2,
TILE_WIRE_LUTFF_1_IN_3,
TILE_WIRE_LUTFF_2_IN_0,
TILE_WIRE_LUTFF_2_IN_1,
TILE_WIRE_LUTFF_2_IN_2,
TILE_WIRE_LUTFF_2_IN_3,
TILE_WIRE_LUTFF_3_IN_0,
TILE_WIRE_LUTFF_3_IN_1,
TILE_WIRE_LUTFF_3_IN_2,
TILE_WIRE_LUTFF_3_IN_3,
TILE_WIRE_LUTFF_4_IN_0,
TILE_WIRE_LUTFF_4_IN_1,
TILE_WIRE_LUTFF_4_IN_2,
TILE_WIRE_LUTFF_4_IN_3,
TILE_WIRE_LUTFF_5_IN_0,
TILE_WIRE_LUTFF_5_IN_1,
TILE_WIRE_LUTFF_5_IN_2,
TILE_WIRE_LUTFF_5_IN_3,
TILE_WIRE_LUTFF_6_IN_0,
TILE_WIRE_LUTFF_6_IN_1,
TILE_WIRE_LUTFF_6_IN_2,
TILE_WIRE_LUTFF_6_IN_3,
TILE_WIRE_LUTFF_7_IN_0,
TILE_WIRE_LUTFF_7_IN_1,
TILE_WIRE_LUTFF_7_IN_2,
TILE_WIRE_LUTFF_7_IN_3,
TILE_WIRE_LUTFF_0_IN_0_LUT,
TILE_WIRE_LUTFF_0_IN_1_LUT,
TILE_WIRE_LUTFF_0_IN_2_LUT,
TILE_WIRE_LUTFF_0_IN_3_LUT,
TILE_WIRE_LUTFF_1_IN_0_LUT,
TILE_WIRE_LUTFF_1_IN_1_LUT,
TILE_WIRE_LUTFF_1_IN_2_LUT,
TILE_WIRE_LUTFF_1_IN_3_LUT,
TILE_WIRE_LUTFF_2_IN_0_LUT,
TILE_WIRE_LUTFF_2_IN_1_LUT,
TILE_WIRE_LUTFF_2_IN_2_LUT,
TILE_WIRE_LUTFF_2_IN_3_LUT,
TILE_WIRE_LUTFF_3_IN_0_LUT,
TILE_WIRE_LUTFF_3_IN_1_LUT,
TILE_WIRE_LUTFF_3_IN_2_LUT,
TILE_WIRE_LUTFF_3_IN_3_LUT,
TILE_WIRE_LUTFF_4_IN_0_LUT,
TILE_WIRE_LUTFF_4_IN_1_LUT,
TILE_WIRE_LUTFF_4_IN_2_LUT,
TILE_WIRE_LUTFF_4_IN_3_LUT,
TILE_WIRE_LUTFF_5_IN_0_LUT,
TILE_WIRE_LUTFF_5_IN_1_LUT,
TILE_WIRE_LUTFF_5_IN_2_LUT,
TILE_WIRE_LUTFF_5_IN_3_LUT,
TILE_WIRE_LUTFF_6_IN_0_LUT,
TILE_WIRE_LUTFF_6_IN_1_LUT,
TILE_WIRE_LUTFF_6_IN_2_LUT,
TILE_WIRE_LUTFF_6_IN_3_LUT,
TILE_WIRE_LUTFF_7_IN_0_LUT,
TILE_WIRE_LUTFF_7_IN_1_LUT,
TILE_WIRE_LUTFF_7_IN_2_LUT,
TILE_WIRE_LUTFF_7_IN_3_LUT,
TILE_WIRE_LUTFF_0_LOUT,
TILE_WIRE_LUTFF_1_LOUT,
TILE_WIRE_LUTFF_2_LOUT,
TILE_WIRE_LUTFF_3_LOUT,
TILE_WIRE_LUTFF_4_LOUT,
TILE_WIRE_LUTFF_5_LOUT,
TILE_WIRE_LUTFF_6_LOUT,
TILE_WIRE_LUTFF_0_OUT,
TILE_WIRE_LUTFF_1_OUT,
TILE_WIRE_LUTFF_2_OUT,
TILE_WIRE_LUTFF_3_OUT,
TILE_WIRE_LUTFF_4_OUT,
TILE_WIRE_LUTFF_5_OUT,
TILE_WIRE_LUTFF_6_OUT,
TILE_WIRE_LUTFF_7_OUT,
TILE_WIRE_LUTFF_0_COUT,
TILE_WIRE_LUTFF_1_COUT,
TILE_WIRE_LUTFF_2_COUT,
TILE_WIRE_LUTFF_3_COUT,
TILE_WIRE_LUTFF_4_COUT,
TILE_WIRE_LUTFF_5_COUT,
TILE_WIRE_LUTFF_6_COUT,
TILE_WIRE_LUTFF_7_COUT,
TILE_WIRE_LUTFF_GLOBAL_CEN,
TILE_WIRE_LUTFF_GLOBAL_CLK,
TILE_WIRE_LUTFF_GLOBAL_S_R,
TILE_WIRE_CARRY_IN,
TILE_WIRE_CARRY_IN_MUX,
TILE_WIRE_NEIGH_OP_BNL_0,
TILE_WIRE_NEIGH_OP_BNL_1,
TILE_WIRE_NEIGH_OP_BNL_2,
TILE_WIRE_NEIGH_OP_BNL_3,
TILE_WIRE_NEIGH_OP_BNL_4,
TILE_WIRE_NEIGH_OP_BNL_5,
TILE_WIRE_NEIGH_OP_BNL_6,
TILE_WIRE_NEIGH_OP_BNL_7,
TILE_WIRE_NEIGH_OP_BNR_0,
TILE_WIRE_NEIGH_OP_BNR_1,
TILE_WIRE_NEIGH_OP_BNR_2,
TILE_WIRE_NEIGH_OP_BNR_3,
TILE_WIRE_NEIGH_OP_BNR_4,
TILE_WIRE_NEIGH_OP_BNR_5,
TILE_WIRE_NEIGH_OP_BNR_6,
TILE_WIRE_NEIGH_OP_BNR_7,
TILE_WIRE_NEIGH_OP_BOT_0,
TILE_WIRE_NEIGH_OP_BOT_1,
TILE_WIRE_NEIGH_OP_BOT_2,
TILE_WIRE_NEIGH_OP_BOT_3,
TILE_WIRE_NEIGH_OP_BOT_4,
TILE_WIRE_NEIGH_OP_BOT_5,
TILE_WIRE_NEIGH_OP_BOT_6,
TILE_WIRE_NEIGH_OP_BOT_7,
TILE_WIRE_NEIGH_OP_LFT_0,
TILE_WIRE_NEIGH_OP_LFT_1,
TILE_WIRE_NEIGH_OP_LFT_2,
TILE_WIRE_NEIGH_OP_LFT_3,
TILE_WIRE_NEIGH_OP_LFT_4,
TILE_WIRE_NEIGH_OP_LFT_5,
TILE_WIRE_NEIGH_OP_LFT_6,
TILE_WIRE_NEIGH_OP_LFT_7,
TILE_WIRE_NEIGH_OP_RGT_0,
TILE_WIRE_NEIGH_OP_RGT_1,
TILE_WIRE_NEIGH_OP_RGT_2,
TILE_WIRE_NEIGH_OP_RGT_3,
TILE_WIRE_NEIGH_OP_RGT_4,
TILE_WIRE_NEIGH_OP_RGT_5,
TILE_WIRE_NEIGH_OP_RGT_6,
TILE_WIRE_NEIGH_OP_RGT_7,
TILE_WIRE_NEIGH_OP_TNL_0,
TILE_WIRE_NEIGH_OP_TNL_1,
TILE_WIRE_NEIGH_OP_TNL_2,
TILE_WIRE_NEIGH_OP_TNL_3,
TILE_WIRE_NEIGH_OP_TNL_4,
TILE_WIRE_NEIGH_OP_TNL_5,
TILE_WIRE_NEIGH_OP_TNL_6,
TILE_WIRE_NEIGH_OP_TNL_7,
TILE_WIRE_NEIGH_OP_TNR_0,
TILE_WIRE_NEIGH_OP_TNR_1,
TILE_WIRE_NEIGH_OP_TNR_2,
TILE_WIRE_NEIGH_OP_TNR_3,
TILE_WIRE_NEIGH_OP_TNR_4,
TILE_WIRE_NEIGH_OP_TNR_5,
TILE_WIRE_NEIGH_OP_TNR_6,
TILE_WIRE_NEIGH_OP_TNR_7,
TILE_WIRE_NEIGH_OP_TOP_0,
TILE_WIRE_NEIGH_OP_TOP_1,
TILE_WIRE_NEIGH_OP_TOP_2,
TILE_WIRE_NEIGH_OP_TOP_3,
TILE_WIRE_NEIGH_OP_TOP_4,
TILE_WIRE_NEIGH_OP_TOP_5,
TILE_WIRE_NEIGH_OP_TOP_6,
TILE_WIRE_NEIGH_OP_TOP_7,
TILE_WIRE_SP4_V_B_0,
TILE_WIRE_SP4_V_B_1,
TILE_WIRE_SP4_V_B_2,
TILE_WIRE_SP4_V_B_3,
TILE_WIRE_SP4_V_B_4,
TILE_WIRE_SP4_V_B_5,
TILE_WIRE_SP4_V_B_6,
TILE_WIRE_SP4_V_B_7,
TILE_WIRE_SP4_V_B_8,
TILE_WIRE_SP4_V_B_9,
TILE_WIRE_SP4_V_B_10,
TILE_WIRE_SP4_V_B_11,
TILE_WIRE_SP4_V_B_12,
TILE_WIRE_SP4_V_B_13,
TILE_WIRE_SP4_V_B_14,
TILE_WIRE_SP4_V_B_15,
TILE_WIRE_SP4_V_B_16,
TILE_WIRE_SP4_V_B_17,
TILE_WIRE_SP4_V_B_18,
TILE_WIRE_SP4_V_B_19,
TILE_WIRE_SP4_V_B_20,
TILE_WIRE_SP4_V_B_21,
TILE_WIRE_SP4_V_B_22,
TILE_WIRE_SP4_V_B_23,
TILE_WIRE_SP4_V_B_24,
TILE_WIRE_SP4_V_B_25,
TILE_WIRE_SP4_V_B_26,
TILE_WIRE_SP4_V_B_27,
TILE_WIRE_SP4_V_B_28,
TILE_WIRE_SP4_V_B_29,
TILE_WIRE_SP4_V_B_30,
TILE_WIRE_SP4_V_B_31,
TILE_WIRE_SP4_V_B_32,
TILE_WIRE_SP4_V_B_33,
TILE_WIRE_SP4_V_B_34,
TILE_WIRE_SP4_V_B_35,
TILE_WIRE_SP4_V_B_36,
TILE_WIRE_SP4_V_B_37,
TILE_WIRE_SP4_V_B_38,
TILE_WIRE_SP4_V_B_39,
TILE_WIRE_SP4_V_B_40,
TILE_WIRE_SP4_V_B_41,
TILE_WIRE_SP4_V_B_42,
TILE_WIRE_SP4_V_B_43,
TILE_WIRE_SP4_V_B_44,
TILE_WIRE_SP4_V_B_45,
TILE_WIRE_SP4_V_B_46,
TILE_WIRE_SP4_V_B_47,
TILE_WIRE_SP4_V_T_36,
TILE_WIRE_SP4_V_T_37,
TILE_WIRE_SP4_V_T_38,
TILE_WIRE_SP4_V_T_39,
TILE_WIRE_SP4_V_T_40,
TILE_WIRE_SP4_V_T_41,
TILE_WIRE_SP4_V_T_42,
TILE_WIRE_SP4_V_T_43,
TILE_WIRE_SP4_V_T_44,
TILE_WIRE_SP4_V_T_45,
TILE_WIRE_SP4_V_T_46,
TILE_WIRE_SP4_V_T_47,
TILE_WIRE_SP4_R_V_B_0,
TILE_WIRE_SP4_R_V_B_1,
TILE_WIRE_SP4_R_V_B_2,
TILE_WIRE_SP4_R_V_B_3,
TILE_WIRE_SP4_R_V_B_4,
TILE_WIRE_SP4_R_V_B_5,
TILE_WIRE_SP4_R_V_B_6,
TILE_WIRE_SP4_R_V_B_7,
TILE_WIRE_SP4_R_V_B_8,
TILE_WIRE_SP4_R_V_B_9,
TILE_WIRE_SP4_R_V_B_10,
TILE_WIRE_SP4_R_V_B_11,
TILE_WIRE_SP4_R_V_B_12,
TILE_WIRE_SP4_R_V_B_13,
TILE_WIRE_SP4_R_V_B_14,
TILE_WIRE_SP4_R_V_B_15,
TILE_WIRE_SP4_R_V_B_16,
TILE_WIRE_SP4_R_V_B_17,
TILE_WIRE_SP4_R_V_B_18,
TILE_WIRE_SP4_R_V_B_19,
TILE_WIRE_SP4_R_V_B_20,
TILE_WIRE_SP4_R_V_B_21,
TILE_WIRE_SP4_R_V_B_22,
TILE_WIRE_SP4_R_V_B_23,
TILE_WIRE_SP4_R_V_B_24,
TILE_WIRE_SP4_R_V_B_25,
TILE_WIRE_SP4_R_V_B_26,
TILE_WIRE_SP4_R_V_B_27,
TILE_WIRE_SP4_R_V_B_28,
TILE_WIRE_SP4_R_V_B_29,
TILE_WIRE_SP4_R_V_B_30,
TILE_WIRE_SP4_R_V_B_31,
TILE_WIRE_SP4_R_V_B_32,
TILE_WIRE_SP4_R_V_B_33,
TILE_WIRE_SP4_R_V_B_34,
TILE_WIRE_SP4_R_V_B_35,
TILE_WIRE_SP4_R_V_B_36,
TILE_WIRE_SP4_R_V_B_37,
TILE_WIRE_SP4_R_V_B_38,
TILE_WIRE_SP4_R_V_B_39,
TILE_WIRE_SP4_R_V_B_40,
TILE_WIRE_SP4_R_V_B_41,
TILE_WIRE_SP4_R_V_B_42,
TILE_WIRE_SP4_R_V_B_43,
TILE_WIRE_SP4_R_V_B_44,
TILE_WIRE_SP4_R_V_B_45,
TILE_WIRE_SP4_R_V_B_46,
TILE_WIRE_SP4_R_V_B_47,
TILE_WIRE_SP4_H_L_36,
TILE_WIRE_SP4_H_L_37,
TILE_WIRE_SP4_H_L_38,
TILE_WIRE_SP4_H_L_39,
TILE_WIRE_SP4_H_L_40,
TILE_WIRE_SP4_H_L_41,
TILE_WIRE_SP4_H_L_42,
TILE_WIRE_SP4_H_L_43,
TILE_WIRE_SP4_H_L_44,
TILE_WIRE_SP4_H_L_45,
TILE_WIRE_SP4_H_L_46,
TILE_WIRE_SP4_H_L_47,
TILE_WIRE_SP4_H_R_0,
TILE_WIRE_SP4_H_R_1,
TILE_WIRE_SP4_H_R_2,
TILE_WIRE_SP4_H_R_3,
TILE_WIRE_SP4_H_R_4,
TILE_WIRE_SP4_H_R_5,
TILE_WIRE_SP4_H_R_6,
TILE_WIRE_SP4_H_R_7,
TILE_WIRE_SP4_H_R_8,
TILE_WIRE_SP4_H_R_9,
TILE_WIRE_SP4_H_R_10,
TILE_WIRE_SP4_H_R_11,
TILE_WIRE_SP4_H_R_12,
TILE_WIRE_SP4_H_R_13,
TILE_WIRE_SP4_H_R_14,
TILE_WIRE_SP4_H_R_15,
TILE_WIRE_SP4_H_R_16,
TILE_WIRE_SP4_H_R_17,
TILE_WIRE_SP4_H_R_18,
TILE_WIRE_SP4_H_R_19,
TILE_WIRE_SP4_H_R_20,
TILE_WIRE_SP4_H_R_21,
TILE_WIRE_SP4_H_R_22,
TILE_WIRE_SP4_H_R_23,
TILE_WIRE_SP4_H_R_24,
TILE_WIRE_SP4_H_R_25,
TILE_WIRE_SP4_H_R_26,
TILE_WIRE_SP4_H_R_27,
TILE_WIRE_SP4_H_R_28,
TILE_WIRE_SP4_H_R_29,
TILE_WIRE_SP4_H_R_30,
TILE_WIRE_SP4_H_R_31,
TILE_WIRE_SP4_H_R_32,
TILE_WIRE_SP4_H_R_33,
TILE_WIRE_SP4_H_R_34,
TILE_WIRE_SP4_H_R_35,
TILE_WIRE_SP4_H_R_36,
TILE_WIRE_SP4_H_R_37,
TILE_WIRE_SP4_H_R_38,
TILE_WIRE_SP4_H_R_39,
TILE_WIRE_SP4_H_R_40,
TILE_WIRE_SP4_H_R_41,
TILE_WIRE_SP4_H_R_42,
TILE_WIRE_SP4_H_R_43,
TILE_WIRE_SP4_H_R_44,
TILE_WIRE_SP4_H_R_45,
TILE_WIRE_SP4_H_R_46,
TILE_WIRE_SP4_H_R_47,
TILE_WIRE_SP12_V_B_0,
TILE_WIRE_SP12_V_B_1,
TILE_WIRE_SP12_V_B_2,
TILE_WIRE_SP12_V_B_3,
TILE_WIRE_SP12_V_B_4,
TILE_WIRE_SP12_V_B_5,
TILE_WIRE_SP12_V_B_6,
TILE_WIRE_SP12_V_B_7,
TILE_WIRE_SP12_V_B_8,
TILE_WIRE_SP12_V_B_9,
TILE_WIRE_SP12_V_B_10,
TILE_WIRE_SP12_V_B_11,
TILE_WIRE_SP12_V_B_12,
TILE_WIRE_SP12_V_B_13,
TILE_WIRE_SP12_V_B_14,
TILE_WIRE_SP12_V_B_15,
TILE_WIRE_SP12_V_B_16,
TILE_WIRE_SP12_V_B_17,
TILE_WIRE_SP12_V_B_18,
TILE_WIRE_SP12_V_B_19,
TILE_WIRE_SP12_V_B_20,
TILE_WIRE_SP12_V_B_21,
TILE_WIRE_SP12_V_B_22,
TILE_WIRE_SP12_V_B_23,
TILE_WIRE_SP12_V_T_22,
TILE_WIRE_SP12_V_T_23,
TILE_WIRE_SP12_H_R_0,
TILE_WIRE_SP12_H_R_1,
TILE_WIRE_SP12_H_R_2,
TILE_WIRE_SP12_H_R_3,
TILE_WIRE_SP12_H_R_4,
TILE_WIRE_SP12_H_R_5,
TILE_WIRE_SP12_H_R_6,
TILE_WIRE_SP12_H_R_7,
TILE_WIRE_SP12_H_R_8,
TILE_WIRE_SP12_H_R_9,
TILE_WIRE_SP12_H_R_10,
TILE_WIRE_SP12_H_R_11,
TILE_WIRE_SP12_H_R_12,
TILE_WIRE_SP12_H_R_13,
TILE_WIRE_SP12_H_R_14,
TILE_WIRE_SP12_H_R_15,
TILE_WIRE_SP12_H_R_16,
TILE_WIRE_SP12_H_R_17,
TILE_WIRE_SP12_H_R_18,
TILE_WIRE_SP12_H_R_19,
TILE_WIRE_SP12_H_R_20,
TILE_WIRE_SP12_H_R_21,
TILE_WIRE_SP12_H_R_22,
TILE_WIRE_SP12_H_R_23,
TILE_WIRE_SP12_H_L_22,
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 gfxTilePip(std::vector<GraphicElement> &g, int x, int y, GfxTileWireId src, GfxTileWireId dst,
GraphicElement::style_t style);
NEXTPNR_NAMESPACE_END
#endif // GFX_H

14
xc7/icebreaker.pcf Normal file
View File

@ -0,0 +1,14 @@
set_io led1 27
set_io led2 25
set_io led3 21
set_io led4 23
set_io led5 26
set_io ledr 11
set_io ledg 37
set_io clki 35
set_io btn1 20
set_io btn2 19
set_io btn3 18
set_io btn_n 10

31
xc7/icebreaker.v Normal file
View File

@ -0,0 +1,31 @@
module icebreaker (
input clki,
input btn1,
input btn2,
input btn3,
input btn_n,
output led1,
output led2,
output led3,
output led4,
output led5,
output ledr,
output ledg,
);
wire clk;
SB_GB clk_gb(.USER_SIGNAL_TO_GLOBAL_BUFFER(clki), .GLOBAL_BUFFER_OUTPUT(clk));
localparam BITS = 5;
localparam LOG2DELAY = 22;
reg [BITS+LOG2DELAY-1:0] counter = 0;
reg [BITS-1:0] outcnt;
always @(posedge clk) begin
counter <= counter + 1;
outcnt <= counter >> LOG2DELAY;
end
assign {led1, led2, led3, led4, led5} = outcnt ^ (outcnt >> 1);
assign {ledr, ledg} = ~(!btn_n + btn1 + btn2 + btn3);
endmodule

3
xc7/icebreaker.ys Normal file
View File

@ -0,0 +1,3 @@
read_verilog icebreaker.v
synth_ice40 -nocarry -top icebreaker
write_json icebreaker.json

164
xc7/main.cc Normal file
View File

@ -0,0 +1,164 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifdef MAIN_EXECUTABLE
#include <fstream>
#include "bitstream.h"
#include "command.h"
#include "design_utils.h"
#include "jsonparse.h"
#include "log.h"
#include "pcf.h"
#include "timing.h"
USING_NEXTPNR_NAMESPACE
class Ice40CommandHandler : public CommandHandler
{
public:
Ice40CommandHandler(int argc, char **argv);
virtual ~Ice40CommandHandler(){};
std::unique_ptr<Context> createContext() override;
void setupArchContext(Context *ctx) override;
void validate() override;
void customAfterLoad(Context *ctx) override;
void customBitstream(Context *ctx) override;
protected:
po::options_description getArchOptions();
};
Ice40CommandHandler::Ice40CommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {}
po::options_description Ice40CommandHandler::getArchOptions()
{
po::options_description specific("Architecture specific options");
#ifdef ICE40_HX1K_ONLY
specific.add_options()("hx1k", "set device type to iCE40HX1K");
#else
specific.add_options()("lp384", "set device type to iCE40LP384");
specific.add_options()("lp1k", "set device type to iCE40LP1K");
specific.add_options()("lp8k", "set device type to iCE40LP8K");
specific.add_options()("hx1k", "set device type to iCE40HX1K");
specific.add_options()("hx8k", "set device type to iCE40HX8K");
specific.add_options()("up5k", "set device type to iCE40UP5K");
#endif
specific.add_options()("package", po::value<std::string>(), "set device package");
specific.add_options()("pcf", po::value<std::string>(), "PCF constraints file to ingest");
specific.add_options()("asc", po::value<std::string>(), "asc bitstream file to write");
specific.add_options()("read", po::value<std::string>(), "asc bitstream file to read");
specific.add_options()("tmfuzz", "run path delay estimate fuzzer");
return specific;
}
void Ice40CommandHandler::validate()
{
conflicting_options(vm, "read", "json");
if ((vm.count("lp384") + vm.count("lp1k") + vm.count("lp8k") + vm.count("hx1k") + vm.count("hx8k") +
vm.count("up5k")) > 1)
log_error("Only one device type can be set\n");
}
void Ice40CommandHandler::customAfterLoad(Context *ctx)
{
if (vm.count("pcf")) {
std::string filename = vm["pcf"].as<std::string>();
std::ifstream pcf(filename);
if (!apply_pcf(ctx, filename, pcf))
log_error("Loading PCF failed.\n");
}
}
void Ice40CommandHandler::customBitstream(Context *ctx)
{
if (vm.count("asc")) {
std::string filename = vm["asc"].as<std::string>();
std::ofstream f(filename);
write_asc(ctx, f);
}
}
void Ice40CommandHandler::setupArchContext(Context *ctx)
{
if (vm.count("tmfuzz"))
ice40DelayFuzzerMain(ctx);
if (vm.count("read")) {
std::string filename = vm["read"].as<std::string>();
std::ifstream f(filename);
if (!read_asc(ctx, f))
log_error("Loading ASC failed.\n");
}
}
std::unique_ptr<Context> Ice40CommandHandler::createContext()
{
if (vm.count("lp384")) {
chipArgs.type = ArchArgs::LP384;
chipArgs.package = "qn32";
}
if (vm.count("lp1k")) {
chipArgs.type = ArchArgs::LP1K;
chipArgs.package = "tq144";
}
if (vm.count("lp8k")) {
chipArgs.type = ArchArgs::LP8K;
chipArgs.package = "ct256";
}
if (vm.count("hx1k")) {
chipArgs.type = ArchArgs::HX1K;
chipArgs.package = "tq144";
}
if (vm.count("hx8k")) {
chipArgs.type = ArchArgs::HX8K;
chipArgs.package = "ct256";
}
if (vm.count("up5k")) {
chipArgs.type = ArchArgs::UP5K;
chipArgs.package = "sg48";
}
if (chipArgs.type == ArchArgs::NONE) {
chipArgs.type = ArchArgs::HX1K;
chipArgs.package = "tq144";
}
#ifdef ICE40_HX1K_ONLY
if (chipArgs.type != ArchArgs::HX1K) {
log_error("This version of nextpnr-ice40 is built with HX1K-support only.\n");
}
#endif
if (vm.count("package"))
chipArgs.package = vm["package"].as<std::string>();
return std::unique_ptr<Context>(new Context(chipArgs));
}
int main(int argc, char *argv[])
{
Ice40CommandHandler handler(argc, argv);
return handler.exec();
}
#endif

907
xc7/pack.cc Normal file
View File

@ -0,0 +1,907 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
* Copyright (C) 2018 Serge Bazanski <q3k@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include <algorithm>
#include <iterator>
#include <unordered_set>
#include "cells.h"
#include "chains.h"
#include "design_utils.h"
#include "log.h"
#include "util.h"
NEXTPNR_NAMESPACE_BEGIN
// Pack LUTs and LUT-FF pairs
static void pack_lut_lutffs(Context *ctx)
{
log_info("Packing LUT-FFs..\n");
std::unordered_set<IdString> packed_cells;
std::vector<std::unique_ptr<CellInfo>> new_cells;
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (ctx->verbose)
log_info("cell '%s' is of type '%s'\n", ci->name.c_str(ctx), ci->type.c_str(ctx));
if (is_lut(ctx, ci)) {
std::unique_ptr<CellInfo> packed = create_ice_cell(ctx, ctx->id("ICESTORM_LC"), ci->name.str(ctx) + "_LC");
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(packed->attrs, packed->attrs.begin()));
packed_cells.insert(ci->name);
if (ctx->verbose)
log_info("packed cell %s into %s\n", ci->name.c_str(ctx), packed->name.c_str(ctx));
// See if we can pack into a DFF
// TODO: LUT cascade
NetInfo *o = ci->ports.at(ctx->id("O")).net;
CellInfo *dff = net_only_drives(ctx, o, is_ff, ctx->id("D"), true);
auto lut_bel = ci->attrs.find(ctx->id("BEL"));
bool packed_dff = false;
if (dff) {
if (ctx->verbose)
log_info("found attached dff %s\n", dff->name.c_str(ctx));
auto dff_bel = dff->attrs.find(ctx->id("BEL"));
if (lut_bel != ci->attrs.end() && dff_bel != dff->attrs.end() && lut_bel->second != dff_bel->second) {
// Locations don't match, can't pack
} else {
lut_to_lc(ctx, ci, packed.get(), false);
dff_to_lc(ctx, dff, packed.get(), false);
ctx->nets.erase(o->name);
if (dff_bel != dff->attrs.end())
packed->attrs[ctx->id("BEL")] = dff_bel->second;
packed_cells.insert(dff->name);
if (ctx->verbose)
log_info("packed cell %s into %s\n", dff->name.c_str(ctx), packed->name.c_str(ctx));
packed_dff = true;
}
}
if (!packed_dff) {
lut_to_lc(ctx, ci, packed.get(), true);
}
new_cells.push_back(std::move(packed));
}
}
for (auto pcell : packed_cells) {
ctx->cells.erase(pcell);
}
for (auto &ncell : new_cells) {
ctx->cells[ncell->name] = std::move(ncell);
}
}
// Pack FFs not packed as LUTFFs
static void pack_nonlut_ffs(Context *ctx)
{
log_info("Packing non-LUT FFs..\n");
std::unordered_set<IdString> packed_cells;
std::vector<std::unique_ptr<CellInfo>> new_cells;
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (is_ff(ctx, ci)) {
std::unique_ptr<CellInfo> packed =
create_ice_cell(ctx, ctx->id("ICESTORM_LC"), ci->name.str(ctx) + "_DFFLC");
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(packed->attrs, packed->attrs.begin()));
if (ctx->verbose)
log_info("packed cell %s into %s\n", ci->name.c_str(ctx), packed->name.c_str(ctx));
packed_cells.insert(ci->name);
dff_to_lc(ctx, ci, packed.get(), true);
new_cells.push_back(std::move(packed));
}
}
for (auto pcell : packed_cells) {
ctx->cells.erase(pcell);
}
for (auto &ncell : new_cells) {
ctx->cells[ncell->name] = std::move(ncell);
}
}
static bool net_is_constant(const Context *ctx, NetInfo *net, bool &value)
{
if (net == nullptr)
return false;
if (net->name == ctx->id("$PACKER_GND_NET") || net->name == ctx->id("$PACKER_VCC_NET")) {
value = (net->name == ctx->id("$PACKER_VCC_NET"));
return true;
} else {
return false;
}
}
// Pack carry logic
static void pack_carries(Context *ctx)
{
log_info("Packing carries..\n");
std::unordered_set<IdString> exhausted_cells;
std::unordered_set<IdString> packed_cells;
std::vector<std::unique_ptr<CellInfo>> new_cells;
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (is_carry(ctx, ci)) {
packed_cells.insert(cell.first);
CellInfo *carry_ci_lc;
bool ci_value;
bool ci_const = net_is_constant(ctx, ci->ports.at(ctx->id("CI")).net, ci_value);
if (ci_const) {
carry_ci_lc = nullptr;
} else {
carry_ci_lc = net_only_drives(ctx, ci->ports.at(ctx->id("CI")).net, is_lc, ctx->id("I3"), false);
}
std::set<IdString> i0_matches, i1_matches;
NetInfo *i0_net = ci->ports.at(ctx->id("I0")).net;
NetInfo *i1_net = ci->ports.at(ctx->id("I1")).net;
// Find logic cells connected to both I0 and I1
if (i0_net) {
for (auto usr : i0_net->users) {
if (is_lc(ctx, usr.cell) && usr.port == ctx->id("I1")) {
if (ctx->cells.find(usr.cell->name) != ctx->cells.end() &&
exhausted_cells.find(usr.cell->name) == exhausted_cells.end()) {
// This clause stops us double-packing cells
i0_matches.insert(usr.cell->name);
if (!i1_net) {
// I1 is don't care when disconnected, duplicate I0
i1_matches.insert(usr.cell->name);
}
}
}
}
}
if (i1_net) {
for (auto usr : i1_net->users) {
if (is_lc(ctx, usr.cell) && usr.port == ctx->id("I2")) {
if (ctx->cells.find(usr.cell->name) != ctx->cells.end() &&
exhausted_cells.find(usr.cell->name) == exhausted_cells.end()) {
// This clause stops us double-packing cells
i1_matches.insert(usr.cell->name);
if (!i0_net) {
// I0 is don't care when disconnected, duplicate I1
i0_matches.insert(usr.cell->name);
}
}
}
}
}
std::set<IdString> carry_lcs;
std::set_intersection(i0_matches.begin(), i0_matches.end(), i1_matches.begin(), i1_matches.end(),
std::inserter(carry_lcs, carry_lcs.end()));
CellInfo *carry_lc = nullptr;
if (carry_ci_lc && carry_lcs.find(carry_ci_lc->name) != carry_lcs.end()) {
carry_lc = carry_ci_lc;
} else {
// No LC to pack into matching I0/I1, insert a new one
std::unique_ptr<CellInfo> created_lc =
create_ice_cell(ctx, ctx->id("ICESTORM_LC"), cell.first.str(ctx) + "$CARRY");
carry_lc = created_lc.get();
created_lc->ports.at(ctx->id("I1")).net = i0_net;
if (i0_net) {
PortRef pr;
pr.cell = created_lc.get();
pr.port = ctx->id("I1");
i0_net->users.push_back(pr);
}
created_lc->ports.at(ctx->id("I2")).net = i1_net;
if (i1_net) {
PortRef pr;
pr.cell = created_lc.get();
pr.port = ctx->id("I2");
i1_net->users.push_back(pr);
}
new_cells.push_back(std::move(created_lc));
}
carry_lc->params[ctx->id("CARRY_ENABLE")] = "1";
replace_port(ci, ctx->id("CI"), carry_lc, ctx->id("CIN"));
replace_port(ci, ctx->id("CO"), carry_lc, ctx->id("COUT"));
if (i0_net) {
auto &i0_usrs = i0_net->users;
i0_usrs.erase(std::remove_if(i0_usrs.begin(), i0_usrs.end(), [ci, ctx](const PortRef &pr) {
return pr.cell == ci && pr.port == ctx->id("I0");
}));
}
if (i1_net) {
auto &i1_usrs = i1_net->users;
i1_usrs.erase(std::remove_if(i1_usrs.begin(), i1_usrs.end(), [ci, ctx](const PortRef &pr) {
return pr.cell == ci && pr.port == ctx->id("I1");
}));
}
// Check for constant driver on CIN
if (carry_lc->ports.at(ctx->id("CIN")).net != nullptr) {
IdString cin_net = carry_lc->ports.at(ctx->id("CIN")).net->name;
if (cin_net == ctx->id("$PACKER_GND_NET") || cin_net == ctx->id("$PACKER_VCC_NET")) {
carry_lc->params[ctx->id("CIN_CONST")] = "1";
carry_lc->params[ctx->id("CIN_SET")] = cin_net == ctx->id("$PACKER_VCC_NET") ? "1" : "0";
carry_lc->ports.at(ctx->id("CIN")).net = nullptr;
auto &cin_users = ctx->nets.at(cin_net)->users;
cin_users.erase(
std::remove_if(cin_users.begin(), cin_users.end(), [carry_lc, ctx](const PortRef &pr) {
return pr.cell == carry_lc && pr.port == ctx->id("CIN");
}));
}
}
exhausted_cells.insert(carry_lc->name);
}
}
for (auto pcell : packed_cells) {
ctx->cells.erase(pcell);
}
for (auto &ncell : new_cells) {
ctx->cells[ncell->name] = std::move(ncell);
}
}
// "Pack" RAMs
static void pack_ram(Context *ctx)
{
log_info("Packing RAMs..\n");
std::unordered_set<IdString> packed_cells;
std::vector<std::unique_ptr<CellInfo>> new_cells;
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (is_ram(ctx, ci)) {
std::unique_ptr<CellInfo> packed =
create_ice_cell(ctx, ctx->id("ICESTORM_RAM"), ci->name.str(ctx) + "_RAM");
packed_cells.insert(ci->name);
for (auto param : ci->params)
packed->params[param.first] = param.second;
packed->params[ctx->id("NEG_CLK_W")] =
std::to_string(ci->type == ctx->id("SB_RAM40_4KNW") || ci->type == ctx->id("SB_RAM40_4KNRNW"));
packed->params[ctx->id("NEG_CLK_R")] =
std::to_string(ci->type == ctx->id("SB_RAM40_4KNR") || ci->type == ctx->id("SB_RAM40_4KNRNW"));
packed->type = ctx->id("ICESTORM_RAM");
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("RCLKN"))
newname = "RCLK";
else if (pi.name == ctx->id("WCLKN"))
newname = "WCLK";
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
}
new_cells.push_back(std::move(packed));
}
}
for (auto pcell : packed_cells) {
ctx->cells.erase(pcell);
}
for (auto &ncell : new_cells) {
ctx->cells[ncell->name] = std::move(ncell);
}
}
// Merge a net into a constant net
static void set_net_constant(const Context *ctx, NetInfo *orig, NetInfo *constnet, bool constval)
{
orig->driver.cell = nullptr;
for (auto user : orig->users) {
if (user.cell != nullptr) {
CellInfo *uc = user.cell;
if (ctx->verbose)
log_info("%s user %s\n", orig->name.c_str(ctx), uc->name.c_str(ctx));
if ((is_lut(ctx, uc) || is_lc(ctx, uc) || is_carry(ctx, uc)) && (user.port.str(ctx).at(0) == 'I') &&
!constval) {
uc->ports[user.port].net = nullptr;
} else if ((is_sb_mac16(ctx, uc) || uc->type == ctx->id("ICESTORM_DSP")) &&
(user.port != ctx->id("CLK") &&
((constval && user.port == ctx->id("CE")) || (!constval && user.port != ctx->id("CE"))))) {
uc->ports[user.port].net = nullptr;
} else if (is_ram(ctx, uc) && !constval && user.port != ctx->id("RCLK") && user.port != ctx->id("RCLKN") &&
user.port != ctx->id("WCLK") && user.port != ctx->id("WCLKN") && user.port != ctx->id("RCLKE") &&
user.port != ctx->id("WCLKE")) {
uc->ports[user.port].net = nullptr;
} else {
uc->ports[user.port].net = constnet;
constnet->users.push_back(user);
}
}
}
orig->users.clear();
}
// Pack constants (simple implementation)
static void pack_constants(Context *ctx)
{
log_info("Packing constants..\n");
std::unique_ptr<CellInfo> gnd_cell = create_ice_cell(ctx, ctx->id("ICESTORM_LC"), "$PACKER_GND");
gnd_cell->params[ctx->id("LUT_INIT")] = "0";
std::unique_ptr<NetInfo> gnd_net = std::unique_ptr<NetInfo>(new NetInfo);
gnd_net->name = ctx->id("$PACKER_GND_NET");
gnd_net->driver.cell = gnd_cell.get();
gnd_net->driver.port = ctx->id("O");
gnd_cell->ports.at(ctx->id("O")).net = gnd_net.get();
std::unique_ptr<CellInfo> vcc_cell = create_ice_cell(ctx, ctx->id("ICESTORM_LC"), "$PACKER_VCC");
vcc_cell->params[ctx->id("LUT_INIT")] = "1";
std::unique_ptr<NetInfo> vcc_net = std::unique_ptr<NetInfo>(new NetInfo);
vcc_net->name = ctx->id("$PACKER_VCC_NET");
vcc_net->driver.cell = vcc_cell.get();
vcc_net->driver.port = ctx->id("O");
vcc_cell->ports.at(ctx->id("O")).net = vcc_net.get();
std::vector<IdString> dead_nets;
bool gnd_used = false;
for (auto net : sorted(ctx->nets)) {
NetInfo *ni = net.second;
if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("GND")) {
IdString drv_cell = ni->driver.cell->name;
set_net_constant(ctx, ni, gnd_net.get(), false);
gnd_used = true;
dead_nets.push_back(net.first);
ctx->cells.erase(drv_cell);
} else if (ni->driver.cell != nullptr && ni->driver.cell->type == ctx->id("VCC")) {
IdString drv_cell = ni->driver.cell->name;
set_net_constant(ctx, ni, vcc_net.get(), true);
dead_nets.push_back(net.first);
ctx->cells.erase(drv_cell);
}
}
if (gnd_used) {
ctx->cells[gnd_cell->name] = std::move(gnd_cell);
ctx->nets[gnd_net->name] = std::move(gnd_net);
}
// Vcc cell always inserted for now, as it may be needed during carry legalisation (TODO: trim later if actually
// never used?)
ctx->cells[vcc_cell->name] = std::move(vcc_cell);
ctx->nets[vcc_net->name] = std::move(vcc_net);
for (auto dn : dead_nets) {
ctx->nets.erase(dn);
}
}
static bool is_nextpnr_iob(Context *ctx, CellInfo *cell)
{
return cell->type == ctx->id("$nextpnr_ibuf") || cell->type == ctx->id("$nextpnr_obuf") ||
cell->type == ctx->id("$nextpnr_iobuf");
}
// Pack IO buffers
static void pack_io(Context *ctx)
{
std::unordered_set<IdString> packed_cells;
std::vector<std::unique_ptr<CellInfo>> new_cells;
log_info("Packing IOs..\n");
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (is_nextpnr_iob(ctx, ci)) {
CellInfo *sb = nullptr;
if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) {
sb = net_only_drives(ctx, ci->ports.at(ctx->id("O")).net, is_sb_io, ctx->id("PACKAGE_PIN"), true, ci);
} else if (ci->type == ctx->id("$nextpnr_obuf")) {
sb = net_only_drives(ctx, ci->ports.at(ctx->id("I")).net, is_sb_io, ctx->id("PACKAGE_PIN"), true, ci);
}
if (sb != nullptr) {
// Trivial case, SB_IO used. Just destroy the net and the
// iobuf
log_info("%s feeds SB_IO %s, removing %s %s.\n", ci->name.c_str(ctx), sb->name.c_str(ctx),
ci->type.c_str(ctx), ci->name.c_str(ctx));
NetInfo *net = sb->ports.at(ctx->id("PACKAGE_PIN")).net;
if (net != nullptr) {
ctx->nets.erase(net->name);
sb->ports.at(ctx->id("PACKAGE_PIN")).net = nullptr;
}
if (ci->type == ctx->id("$nextpnr_iobuf")) {
NetInfo *net2 = ci->ports.at(ctx->id("I")).net;
if (net2 != nullptr) {
ctx->nets.erase(net2->name);
}
}
} else {
// Create a SB_IO buffer
std::unique_ptr<CellInfo> ice_cell =
create_ice_cell(ctx, ctx->id("SB_IO"), ci->name.str(ctx) + "$sb_io");
nxio_to_sb(ctx, ci, ice_cell.get());
new_cells.push_back(std::move(ice_cell));
sb = new_cells.back().get();
}
packed_cells.insert(ci->name);
std::copy(ci->attrs.begin(), ci->attrs.end(), std::inserter(sb->attrs, sb->attrs.begin()));
}
}
for (auto pcell : packed_cells) {
ctx->cells.erase(pcell);
}
for (auto &ncell : new_cells) {
ctx->cells[ncell->name] = std::move(ncell);
}
}
// Return true if a port counts as "logic" for global promotion
static bool is_logic_port(BaseCtx *ctx, const PortRef &port)
{
if (is_clock_port(ctx, port) || is_reset_port(ctx, port) || is_enable_port(ctx, port))
return false;
return !is_sb_io(ctx, port.cell) && !is_sb_pll40(ctx, port.cell) && !is_sb_pll40_pad(ctx, port.cell) &&
port.cell->type != ctx->id("SB_GB");
}
static void insert_global(Context *ctx, NetInfo *net, bool is_reset, bool is_cen, bool is_logic)
{
std::string glb_name = net->name.str(ctx) + std::string("_$glb_") + (is_reset ? "sr" : (is_cen ? "ce" : "clk"));
std::unique_ptr<CellInfo> gb = create_ice_cell(ctx, ctx->id("SB_GB"), "$gbuf_" + glb_name);
gb->ports[ctx->id("USER_SIGNAL_TO_GLOBAL_BUFFER")].net = net;
PortRef pr;
pr.cell = gb.get();
pr.port = ctx->id("USER_SIGNAL_TO_GLOBAL_BUFFER");
net->users.push_back(pr);
pr.cell = gb.get();
pr.port = ctx->id("GLOBAL_BUFFER_OUTPUT");
std::unique_ptr<NetInfo> glbnet = std::unique_ptr<NetInfo>(new NetInfo());
glbnet->name = ctx->id(glb_name);
glbnet->driver = pr;
gb->ports[ctx->id("GLOBAL_BUFFER_OUTPUT")].net = glbnet.get();
std::vector<PortRef> keep_users;
for (auto user : net->users) {
if (is_clock_port(ctx, user) || (is_reset && is_reset_port(ctx, user)) ||
(is_cen && is_enable_port(ctx, user)) || (is_logic && is_logic_port(ctx, user))) {
user.cell->ports[user.port].net = glbnet.get();
glbnet->users.push_back(user);
} else {
keep_users.push_back(user);
}
}
net->users = keep_users;
ctx->nets[glbnet->name] = std::move(glbnet);
ctx->cells[gb->name] = std::move(gb);
}
// Simple global promoter (clock only)
static void promote_globals(Context *ctx)
{
log_info("Promoting globals..\n");
const int logic_fanout_thresh = 15;
const int enable_fanout_thresh = 5;
std::map<IdString, int> clock_count, reset_count, cen_count, logic_count;
for (auto net : sorted(ctx->nets)) {
NetInfo *ni = net.second;
if (ni->driver.cell != nullptr && !ctx->isGlobalNet(ni)) {
clock_count[net.first] = 0;
reset_count[net.first] = 0;
cen_count[net.first] = 0;
for (auto user : ni->users) {
if (is_clock_port(ctx, user))
clock_count[net.first]++;
if (is_reset_port(ctx, user))
reset_count[net.first]++;
if (is_enable_port(ctx, user))
cen_count[net.first]++;
if (is_logic_port(ctx, user))
logic_count[net.first]++;
}
}
}
int prom_globals = 0, prom_resets = 0, prom_cens = 0, prom_logics = 0;
int gbs_available = 8;
for (auto &cell : ctx->cells)
if (is_gbuf(ctx, cell.second.get()))
--gbs_available;
while (prom_globals < gbs_available) {
auto global_clock = std::max_element(clock_count.begin(), clock_count.end(),
[](const std::pair<IdString, int> &a, const std::pair<IdString, int> &b) {
return a.second < b.second;
});
auto global_reset = std::max_element(reset_count.begin(), reset_count.end(),
[](const std::pair<IdString, int> &a, const std::pair<IdString, int> &b) {
return a.second < b.second;
});
auto global_cen = std::max_element(cen_count.begin(), cen_count.end(),
[](const std::pair<IdString, int> &a, const std::pair<IdString, int> &b) {
return a.second < b.second;
});
auto global_logic = std::max_element(logic_count.begin(), logic_count.end(),
[](const std::pair<IdString, int> &a, const std::pair<IdString, int> &b) {
return a.second < b.second;
});
if (global_clock->second == 0 && prom_logics < 4 && global_logic->second > logic_fanout_thresh &&
(global_logic->second > global_cen->second || prom_cens >= 4) &&
(global_logic->second > global_reset->second || prom_resets >= 4)) {
NetInfo *logicnet = ctx->nets[global_logic->first].get();
insert_global(ctx, logicnet, false, false, true);
++prom_globals;
++prom_logics;
clock_count.erase(logicnet->name);
reset_count.erase(logicnet->name);
cen_count.erase(logicnet->name);
logic_count.erase(logicnet->name);
} else if (global_reset->second > global_clock->second && prom_resets < 4) {
NetInfo *rstnet = ctx->nets[global_reset->first].get();
insert_global(ctx, rstnet, true, false, false);
++prom_globals;
++prom_resets;
clock_count.erase(rstnet->name);
reset_count.erase(rstnet->name);
cen_count.erase(rstnet->name);
logic_count.erase(rstnet->name);
} else if (global_cen->second > global_clock->second && prom_cens < 4 &&
global_cen->second > enable_fanout_thresh) {
NetInfo *cennet = ctx->nets[global_cen->first].get();
insert_global(ctx, cennet, false, true, false);
++prom_globals;
++prom_cens;
clock_count.erase(cennet->name);
reset_count.erase(cennet->name);
cen_count.erase(cennet->name);
logic_count.erase(cennet->name);
} else if (global_clock->second != 0) {
NetInfo *clknet = ctx->nets[global_clock->first].get();
insert_global(ctx, clknet, false, false, false);
++prom_globals;
clock_count.erase(clknet->name);
reset_count.erase(clknet->name);
cen_count.erase(clknet->name);
logic_count.erase(clknet->name);
} else {
break;
}
}
}
// spliceLUT adds a pass-through LUT LC between the given cell's output port
// and either all users or only non_LUT users.
static std::unique_ptr<CellInfo> spliceLUT(Context *ctx, CellInfo *ci, IdString portId, bool onlyNonLUTs)
{
auto port = ci->ports[portId];
NPNR_ASSERT(port.net != nullptr);
// Create pass-through LUT.
std::unique_ptr<CellInfo> pt = create_ice_cell(ctx, ctx->id("ICESTORM_LC"),
ci->name.str(ctx) + "$nextpnr_" + portId.str(ctx) + "_lut_through");
pt->params[ctx->id("LUT_INIT")] = "65280"; // output is always I3
// Create LUT output net.
std::unique_ptr<NetInfo> out_net = std::unique_ptr<NetInfo>(new NetInfo);
out_net->name = ctx->id(ci->name.str(ctx) + "$nextnr_" + portId.str(ctx) + "_lut_through_net");
out_net->driver.cell = pt.get();
out_net->driver.port = ctx->id("O");
pt->ports.at(ctx->id("O")).net = out_net.get();
// New users of the original cell's port
std::vector<PortRef> new_users;
for (const auto &user : port.net->users) {
if (onlyNonLUTs && user.cell->type == ctx->id("ICESTORM_LC")) {
new_users.push_back(user);
continue;
}
// Rewrite pointer into net in user.
user.cell->ports[user.port].net = out_net.get();
// Add user to net.
PortRef pr;
pr.cell = user.cell;
pr.port = user.port;
out_net->users.push_back(pr);
}
// Add LUT to new users.
PortRef pr;
pr.cell = pt.get();
pr.port = ctx->id("I3");
new_users.push_back(pr);
pt->ports.at(ctx->id("I3")).net = port.net;
// Replace users of the original net.
port.net->users = new_users;
ctx->nets[out_net->name] = std::move(out_net);
return pt;
}
// Pack special functions
static void pack_special(Context *ctx)
{
log_info("Packing special functions..\n");
std::unordered_set<IdString> packed_cells;
std::vector<std::unique_ptr<CellInfo>> new_cells;
for (auto cell : sorted(ctx->cells)) {
CellInfo *ci = cell.second;
if (is_sb_lfosc(ctx, ci)) {
std::unique_ptr<CellInfo> packed =
create_ice_cell(ctx, ctx->id("ICESTORM_LFOSC"), ci->name.str(ctx) + "_OSC");
packed_cells.insert(ci->name);
replace_port(ci, ctx->id("CLKLFEN"), packed.get(), ctx->id("CLKLFEN"));
replace_port(ci, ctx->id("CLKLFPU"), packed.get(), ctx->id("CLKLFPU"));
if (/*bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))*/ true) { // FIXME
replace_port(ci, ctx->id("CLKLF"), packed.get(), ctx->id("CLKLF_FABRIC"));
} else {
replace_port(ci, ctx->id("CLKLF"), packed.get(), ctx->id("CLKLF"));
}
new_cells.push_back(std::move(packed));
} else if (is_sb_hfosc(ctx, ci)) {
std::unique_ptr<CellInfo> packed =
create_ice_cell(ctx, ctx->id("ICESTORM_HFOSC"), ci->name.str(ctx) + "_OSC");
packed_cells.insert(ci->name);
packed->params[ctx->id("CLKHF_DIV")] = str_or_default(ci->params, ctx->id("CLKHF_DIV"), "0b00");
replace_port(ci, ctx->id("CLKHFEN"), packed.get(), ctx->id("CLKHFEN"));
replace_port(ci, ctx->id("CLKHFPU"), packed.get(), ctx->id("CLKHFPU"));
if (/*bool_or_default(ci->attrs, ctx->id("ROUTE_THROUGH_FABRIC"))*/ true) { // FIXME
replace_port(ci, ctx->id("CLKHF"), packed.get(), ctx->id("CLKHF_FABRIC"));
} else {
replace_port(ci, ctx->id("CLKHF"), packed.get(), ctx->id("CLKHF"));
}
new_cells.push_back(std::move(packed));
} else if (is_sb_spram(ctx, ci)) {
std::unique_ptr<CellInfo> packed =
create_ice_cell(ctx, ctx->id("ICESTORM_SPRAM"), ci->name.str(ctx) + "_RAM");
packed_cells.insert(ci->name);
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);
}
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
}
new_cells.push_back(std::move(packed));
} else if (is_sb_mac16(ctx, ci)) {
std::unique_ptr<CellInfo> packed =
create_ice_cell(ctx, ctx->id("ICESTORM_DSP"), ci->name.str(ctx) + "_DSP");
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;
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);
}
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
}
new_cells.push_back(std::move(packed));
} else if (is_sb_pll40(ctx, ci)) {
bool is_pad = is_sb_pll40_pad(ctx, ci);
bool is_core = !is_pad;
std::unique_ptr<CellInfo> packed =
create_ice_cell(ctx, ctx->id("ICESTORM_PLL"), ci->name.str(ctx) + "_PLL");
packed->attrs[ctx->id("TYPE")] = ci->type.str(ctx);
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));
NetInfo *pad_packagepin_net = nullptr;
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";
if (pi.name == ctx->id("PACKAGEPIN")) {
if (!is_pad) {
log_error(" PLL '%s' has a PACKAGEPIN but is not a PAD PLL", ci->name.c_str(ctx));
} else {
// We drop this port and instead place the PLL adequately below.
pad_packagepin_net = port.second.net;
NPNR_ASSERT(pad_packagepin_net != nullptr);
continue;
}
}
if (pi.name == ctx->id("REFERENCECLK")) {
if (!is_core)
log_error(" PLL '%s' has a REFERENCECLK but is not a CORE PLL", ci->name.c_str(ctx));
}
replace_port(ci, ctx->id(pi.name.c_str(ctx)), packed.get(), ctx->id(newname));
}
// If PLL is not constrained already, do that - we need this
// information to then constrain the LOCK LUT.
BelId pll_bel;
bool constrained = false;
if (packed->attrs.find(ctx->id("BEL")) == packed->attrs.end()) {
for (auto bel : ctx->getBels()) {
if (ctx->getBelType(bel) != id_ICESTORM_PLL)
continue;
// A PAD PLL must have its' PACKAGEPIN on the SB_IO that's shared
// with PLLOUT_A.
if (is_pad) {
auto pll_sb_io_belpin = ctx->getIOBSharingPLLPin(bel, id_PLLOUT_A);
NPNR_ASSERT(pad_packagepin_net != nullptr);
auto pll_packagepin_driver = pad_packagepin_net->driver;
NPNR_ASSERT(pll_packagepin_driver.cell != nullptr);
if (pll_packagepin_driver.cell->type != ctx->id("SB_IO")) {
log_error(" PLL '%s' has a PACKAGEPIN driven by "
"an %s, should be directly connected to an input SB_IO\n",
ci->name.c_str(ctx), pll_packagepin_driver.cell->type.c_str(ctx));
}
auto packagepin_cell = pll_packagepin_driver.cell;
auto packagepin_bel_name = packagepin_cell->attrs.find(ctx->id("BEL"));
if (packagepin_bel_name == packagepin_cell->attrs.end()) {
log_error(" PLL '%s' PACKAGEPIN SB_IO '%s' is unconstrained\n", ci->name.c_str(ctx),
packagepin_cell->name.c_str(ctx));
}
auto packagepin_bel = ctx->getBelByName(ctx->id(packagepin_bel_name->second));
if (pll_sb_io_belpin.bel != packagepin_bel) {
log_error(" PLL '%s' PACKAGEPIN is connected to pin %s, can only be pin %s\n",
ci->name.c_str(ctx), ctx->getBelPackagePin(packagepin_bel).c_str(),
ctx->getBelPackagePin(pll_sb_io_belpin.bel).c_str());
}
if (pad_packagepin_net->users.size() != 1) {
log_error(" PLL '%s' clock input '%s' can only drive PLL\n", ci->name.c_str(ctx),
pad_packagepin_net->name.c_str(ctx));
}
// Set an attribute about this PLL's PAD SB_IO.
packed->attrs[ctx->id("BEL_PAD_INPUT")] = packagepin_bel_name->second;
// Remove the connection from the SB_IO to the PLL.
packagepin_cell->ports.erase(pll_packagepin_driver.port);
}
log_info(" constrained '%s' to %s\n", packed->name.c_str(ctx), ctx->getBelName(bel).c_str(ctx));
packed->attrs[ctx->id("BEL")] = ctx->getBelName(bel).str(ctx);
pll_bel = bel;
constrained = true;
}
if (!constrained) {
log_error(" could not constrain '%s' to any PLL Bel\n", packed->name.c_str(ctx));
}
}
// Delete the original PACKAGEPIN net if needed.
if (pad_packagepin_net != nullptr) {
for (auto user : pad_packagepin_net->users) {
user.cell->ports.erase(user.port);
}
ctx->nets.erase(pad_packagepin_net->name);
pad_packagepin_net = nullptr;
}
// The LOCK signal on iCE40 PLLs goes through the neigh_op_bnl_1 wire.
// In practice, this means the LOCK signal can only directly reach LUT
// inputs.
// If we have a net connected to LOCK, make sure it only drives LUTs.
auto port = packed->ports[ctx->id("LOCK")];
if (port.net != nullptr) {
bool found_lut = false;
bool all_luts = true;
unsigned int lut_count = 0;
for (const auto &user : port.net->users) {
NPNR_ASSERT(user.cell != nullptr);
if (user.cell->type == ctx->id("ICESTORM_LC")) {
found_lut = true;
lut_count++;
} else {
all_luts = false;
}
}
if (found_lut && all_luts) {
// Every user is a LUT, carry on now.
} else if (found_lut && !all_luts && lut_count < 8) {
// Strategy: create a pass-through LUT, move all non-LUT users behind it.
log_info(" LUT strategy for %s: move non-LUT users to new LUT\n", port.name.c_str(ctx));
auto pt = spliceLUT(ctx, packed.get(), port.name, true);
new_cells.push_back(std::move(pt));
} else {
// Strategy: create a pass-through LUT, move every user behind it.
log_info(" LUT strategy for %s: move all users to new LUT\n", port.name.c_str(ctx));
auto pt = spliceLUT(ctx, packed.get(), port.name, false);
new_cells.push_back(std::move(pt));
}
// Find wire that will be driven by this port.
const auto pll_out_wire = ctx->getBelPinWire(pll_bel, port.name);
NPNR_ASSERT(pll_out_wire.index != -1);
// Now, constrain all LUTs on the output of the signal to be at
// the correct Bel relative to the PLL Bel.
int x = ctx->chip_info->wire_data[pll_out_wire.index].x;
int y = ctx->chip_info->wire_data[pll_out_wire.index].y;
int z = 0;
for (const auto &user : port.net->users) {
NPNR_ASSERT(user.cell != nullptr);
NPNR_ASSERT(user.cell->type == ctx->id("ICESTORM_LC"));
// TODO(q3k): handle when the Bel might be already the
// target of another constraint.
NPNR_ASSERT(z < 8);
auto target_bel = ctx->getBelByLocation(Loc(x, y, z++));
auto target_bel_name = ctx->getBelName(target_bel).str(ctx);
user.cell->attrs[ctx->id("BEL")] = target_bel_name;
log_info(" constrained '%s' to %s\n", user.cell->name.c_str(ctx), target_bel_name.c_str());
}
}
new_cells.push_back(std::move(packed));
}
}
for (auto pcell : packed_cells) {
ctx->cells.erase(pcell);
}
for (auto &ncell : new_cells) {
ctx->cells[ncell->name] = std::move(ncell);
}
}
// Main pack function
bool Arch::pack()
{
Context *ctx = getCtx();
try {
log_break();
pack_constants(ctx);
promote_globals(ctx);
pack_io(ctx);
pack_lut_lutffs(ctx);
pack_nonlut_ffs(ctx);
pack_carries(ctx);
pack_ram(ctx);
pack_special(ctx);
ctx->assignArchInfo();
constrain_chains(ctx);
ctx->assignArchInfo();
log_info("Checksum: 0x%08x\n", ctx->checksum());
return true;
} catch (log_execution_error_exception) {
return false;
}
}
NEXTPNR_NAMESPACE_END

2
xc7/pack_tests/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.vcd
*_out.v

43
xc7/pack_tests/ffmodes.v Normal file
View File

@ -0,0 +1,43 @@
module top(input clk, cen, rst, ina, inb, output reg outa, outb, outc, outd);
reg temp0 = 1'b0, temp1 = 1'b0;
initial outa = 1'b0;
initial outb = 1'b0;
initial outc = 1'b0;
initial outd = 1'b0;
always @(posedge clk)
if (cen)
if(rst)
temp0 <= 1'b0;
else
temp0 <= ina;
always @(negedge clk)
if (ina)
if(rst)
temp1 <= 1'b1;
else
temp1 <= inb;
always @(posedge clk or posedge rst)
if(rst)
outa <= 1'b0;
else
outa <= temp0;
always @(posedge clk)
outb <= temp1;
always @(negedge clk)
outc <= temp0;
always @(negedge clk or posedge rst)
if (rst)
outd <= 1'b1;
else
outd <= temp1;
endmodule

27
xc7/pack_tests/locals.v Normal file
View File

@ -0,0 +1,27 @@
module top(input clk, cen, rst, ina, inb, output outa, outb, outc, outd);
reg [31:0] temp = 0;
integer i;
always @(posedge clk)
begin
if (cen) begin
if (rst) begin
temp <= 0;
end else begin
temp[0] <= ina;
temp[1] <= inb;
for (i = 2; i < 32; i++) begin
temp[i] <= temp[(i + 3) % 32] ^ temp[(i + 30) % 32] ^ temp[(i + 4) % 16] ^ temp[(i + 2) % 32];
end
end
end
end
assign outa = temp[3];
assign outb = temp[5];
assign outc = temp[9];
assign outd = temp[15];
endmodule

View File

@ -0,0 +1,54 @@
module top(input clk, cen, rst, ina, inb, output outa, outb, outc, outd);
wire temp0, temp1;
(* BEL="1_1_lc0" *)
SB_LUT4 #(
.LUT_INIT(2'b01)
) lut0 (
.I3(),
.I2(),
.I1(),
.I0(ina),
.O(temp0)
);
(* BEL="1_3_lc0" *)
SB_LUT4 #(
.LUT_INIT(2'b01)
) lut1 (
.I3(),
.I2(),
.I1(),
.I0(inb),
.O(temp1)
);
(* BEL="1_1_lc0" *)
SB_DFF ff0 (
.C(clk),
.D(temp1),
.Q(outa)
);
(* BEL="1_1_lc7" *)
SB_DFF ff1 (
.C(clk),
.D(inb),
.Q(outb)
);
(* BEL="1_6_lc7" *)
SB_DFF ff2 (
.C(clk),
.D(temp1),
.Q(outc)
);
assign outd = 1'b0;
endmodule

10
xc7/pack_tests/test.pcf Normal file
View File

@ -0,0 +1,10 @@
set_io clk 1
set_io cen 2
set_io rst 3
set_io ina 4
set_io inb 7
set_io outa 8
set_io outb 9
set_io outc 10
set_io outd 11

16
xc7/pack_tests/test.sh Executable file
View File

@ -0,0 +1,16 @@
#!/usr/bin/env bash
set -ex
NAME=${1%.v}
yosys -p "synth_ice40 -nocarry -top top; write_json ${NAME}.json" $1
../../nextpnr-ice40 --json ${NAME}.json --pcf test.pcf --asc ${NAME}.asc
icebox_vlog -p test.pcf ${NAME}.asc > ${NAME}_out.v
yosys -p "read_verilog +/ice40/cells_sim.v;\
rename chip gate;\
read_verilog $1;\
rename top gold;\
hierarchy;\
proc;\
clk2fflogic;\
miter -equiv -flatten -ignore_gold_x -make_outputs -make_outcmp gold gate miter;\
sat -dump_vcd equiv_${NAME}.vcd -verify-no-timeout -timeout 60 -seq 50 -prove trigger 0 -prove-skip 1 -show-inputs -show-outputs miter" ${NAME}_out.v

76
xc7/pcf.cc Normal file
View File

@ -0,0 +1,76 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "pcf.h"
#include <sstream>
#include "log.h"
NEXTPNR_NAMESPACE_BEGIN
// Read a w
// Apply PCF constraints to a pre-packing design
bool apply_pcf(Context *ctx, std::string filename, std::istream &in)
{
try {
if (!in)
log_error("failed to open PCF file\n");
std::string line;
while (std::getline(in, line)) {
size_t cstart = line.find("#");
if (cstart != std::string::npos)
line = line.substr(0, cstart);
std::stringstream ss(line);
std::vector<std::string> words;
std::string tmp;
while (ss >> tmp)
words.push_back(tmp);
if (words.size() == 0)
continue;
std::string cmd = words.at(0);
if (cmd == "set_io") {
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 pin = words.at(args_end + 1);
auto fnd_cell = ctx->cells.find(ctx->id(cell));
if (fnd_cell == ctx->cells.end()) {
log_warning("unmatched pcf constraint %s\n", cell.c_str());
} else {
BelId pin_bel = ctx->getPackagePinBel(pin);
if (pin_bel == BelId())
log_error("package does not have a pin named %s\n", pin.c_str());
fnd_cell->second->attrs[ctx->id("BEL")] = ctx->getBelName(pin_bel).str(ctx);
log_info("constrained '%s' to bel '%s'\n", cell.c_str(),
fnd_cell->second->attrs[ctx->id("BEL")].c_str());
}
} else {
log_error("unsupported pcf command '%s'\n", cmd.c_str());
}
}
ctx->settings.emplace(ctx->id("project/input/pcf"), filename);
return true;
} catch (log_execution_error_exception) {
return false;
}
}
NEXTPNR_NAMESPACE_END

34
xc7/pcf.h Normal file
View File

@ -0,0 +1,34 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Clifford Wolf <clifford@symbioticeda.com>
* Copyright (C) 2018 David Shah <david@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#ifndef PCF_H
#define PCF_H
#include <iostream>
#include "nextpnr.h"
NEXTPNR_NAMESPACE_BEGIN
// Apply PCF constraints to a pre-packing design
bool apply_pcf(Context *ctx, std::string filename, std::istream &in);
NEXTPNR_NAMESPACE_END
#endif // ROUTE_H

15
xc7/picorv32.proj Normal file
View File

@ -0,0 +1,15 @@
{
"project": {
"version": "1",
"name": "picorv32",
"arch": {
"name": "ice40",
"type": "hx8k",
"package": "ct256"
},
"input": {
"json": "picorv32.json",
"pcf": "icebreaker.pcf"
}
}
}

7
xc7/picorv32.sh Executable file
View File

@ -0,0 +1,7 @@
#!/bin/bash
set -ex
rm -f picorv32.v
wget https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v
yosys -p 'synth_ice40 -json picorv32.json -top top' picorv32.v picorv32_top.v
../nextpnr-ice40 --hx8k --asc picorv32.asc --json picorv32.json
icetime -d hx8k -t picorv32.asc

42
xc7/picorv32_benchmark.py Executable file
View File

@ -0,0 +1,42 @@
#!/usr/bin/env python3
import os, sys, threading
from os import path
import subprocess
import re
num_runs = 8
if not path.exists("picorv32.json"):
subprocess.run(["wget", "https://raw.githubusercontent.com/cliffordwolf/picorv32/master/picorv32.v"], check=True)
subprocess.run(["yosys", "-q", "-p", "synth_ice40 -json picorv32.json -top top", "picorv32.v", "picorv32_top.v"], check=True)
fmax = {}
if not path.exists("picorv32_work"):
os.mkdir("picorv32_work")
threads = []
for i in range(num_runs):
def runner(run):
ascfile = "picorv32_work/picorv32_s{}.asc".format(run)
if path.exists(ascfile):
os.remove(ascfile)
result = subprocess.run(["../nextpnr-ice40", "--hx8k", "--seed", str(run), "--json", "picorv32.json", "--asc", ascfile, "--freq", "70"], stderr=subprocess.DEVNULL, stdout=subprocess.DEVNULL)
if result.returncode != 0:
print("Run {} failed!".format(run))
else:
icetime_res = subprocess.check_output(["icetime", "-d", "hx8k", ascfile])
fmax_m = re.search(r'\(([0-9.]+) MHz\)', icetime_res.decode('utf-8'))
fmax[run] = float(fmax_m.group(1))
threads.append(threading.Thread(target=runner, args=[i+1]))
for t in threads: t.start()
for t in threads: t.join()
fmax_min = min(fmax.values())
fmax_max = max(fmax.values())
fmax_avg = sum(fmax.values()) / len(fmax)
print("{}/{} runs passed".format(len(fmax), num_runs))
print("icetime: min = {} MHz, avg = {} MHz, max = {} MHz".format(fmax_min, fmax_avg, fmax_max))

31
xc7/picorv32_top.v Normal file
View File

@ -0,0 +1,31 @@
module top (
input clk, resetn,
output trap,
output mem_valid,
output mem_instr,
input mem_ready,
output [31:0] mem_addr,
output [31:0] mem_wdata,
output [ 3:0] mem_wstrb,
input [31:0] mem_rdata
);
picorv32 #(
.ENABLE_COUNTERS(0),
.TWO_STAGE_SHIFT(0),
.CATCH_MISALIGN(0),
.CATCH_ILLINSN(0)
) cpu (
.clk (clk ),
.resetn (resetn ),
.trap (trap ),
.mem_valid(mem_valid),
.mem_instr(mem_instr),
.mem_ready(mem_ready),
.mem_addr (mem_addr ),
.mem_wdata(mem_wdata),
.mem_wstrb(mem_wstrb),
.mem_rdata(mem_rdata)
);
endmodule

73
xc7/project.cc Normal file
View File

@ -0,0 +1,73 @@
/*
* nextpnr -- Next Generation Place and Route
*
* Copyright (C) 2018 Miodrag Milanovic <miodrag@symbioticeda.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "project.h"
#include <boost/filesystem/convenience.hpp>
#include <fstream>
#include "log.h"
#include "pcf.h"
NEXTPNR_NAMESPACE_BEGIN
void ProjectHandler::saveArch(Context *ctx, pt::ptree &root, std::string path)
{
root.put("project.arch.package", ctx->archArgs().package);
if (ctx->settings.find(ctx->id("project/input/pcf")) != ctx->settings.end()) {
std::string fn = ctx->settings[ctx->id("project/input/pcf")];
root.put("project.input.pcf", make_relative(fn, path).string());
}
}
std::unique_ptr<Context> ProjectHandler::createContext(pt::ptree &root)
{
ArchArgs chipArgs;
std::string arch_type = root.get<std::string>("project.arch.type");
if (arch_type == "lp384") {
chipArgs.type = ArchArgs::LP384;
}
if (arch_type == "lp1k") {
chipArgs.type = ArchArgs::LP1K;
}
if (arch_type == "lp8k") {
chipArgs.type = ArchArgs::LP8K;
}
if (arch_type == "hx1k") {
chipArgs.type = ArchArgs::HX1K;
}
if (arch_type == "hx8k") {
chipArgs.type = ArchArgs::HX8K;
}
if (arch_type == "up5k") {
chipArgs.type = ArchArgs::UP5K;
}
chipArgs.package = root.get<std::string>("project.arch.package");
return std::unique_ptr<Context>(new Context(chipArgs));
}
void ProjectHandler::loadArch(Context *ctx, pt::ptree &root, std::string path)
{
auto input = root.get_child("project").get_child("input");
boost::filesystem::path pcf = boost::filesystem::path(path) / input.get<std::string>("pcf");
std::ifstream f(pcf.string());
if (!apply_pcf(ctx, input.get<std::string>("pcf"), f))
log_error("Loading PCF failed.\n");
}
NEXTPNR_NAMESPACE_END

6
xc7/resource/chipdb.rc Normal file
View File

@ -0,0 +1,6 @@
#include "resource.h"
IDR_CHIPDB_384 BINARYFILE "..\chipdbs\chipdb-384.bin"
IDR_CHIPDB_1K BINARYFILE "..\chipdbs\chipdb-1k.bin"
IDR_CHIPDB_5K BINARYFILE "..\chipdbs\chipdb-5k.bin"
IDR_CHIPDB_8K BINARYFILE "..\chipdbs\chipdb-8k.bin"

30
xc7/resource/embed.cc Normal file
View File

@ -0,0 +1,30 @@
#include <cstdio>
#include <windows.h>
#include "nextpnr.h"
#include "resource.h"
NEXTPNR_NAMESPACE_BEGIN
const char *chipdb_blob_384;
const char *chipdb_blob_1k;
const char *chipdb_blob_5k;
const char *chipdb_blob_8k;
const char *LoadFileInResource(int name, int type, DWORD &size)
{
HMODULE handle = ::GetModuleHandle(NULL);
HRSRC rc = ::FindResource(handle, MAKEINTRESOURCE(name), MAKEINTRESOURCE(type));
HGLOBAL rcData = ::LoadResource(handle, rc);
size = ::SizeofResource(handle, rc);
return static_cast<const char *>(::LockResource(rcData));
}
void load_chipdb()
{
DWORD size = 0;
chipdb_blob_384 = LoadFileInResource(IDR_CHIPDB_384, BINARYFILE, size);
chipdb_blob_1k = LoadFileInResource(IDR_CHIPDB_1K, BINARYFILE, size);
chipdb_blob_5k = LoadFileInResource(IDR_CHIPDB_5K, BINARYFILE, size);
chipdb_blob_8k = LoadFileInResource(IDR_CHIPDB_8K, BINARYFILE, size);
}
NEXTPNR_NAMESPACE_END

5
xc7/resource/resource.h Normal file
View File

@ -0,0 +1,5 @@
#define BINARYFILE 256
#define IDR_CHIPDB_384 101
#define IDR_CHIPDB_1K 102
#define IDR_CHIPDB_5K 103
#define IDR_CHIPDB_8K 104

357
xc7/tmfuzz.py Normal file
View File

@ -0,0 +1,357 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ../nextpnr-ice40 --hx8k --tmfuzz > tmfuzz_hx8k.txt
# ../nextpnr-ice40 --lp8k --tmfuzz > tmfuzz_lp8k.txt
# ../nextpnr-ice40 --up5k --tmfuzz > tmfuzz_up5k.txt
import numpy as np
import matplotlib.pyplot as plt
from collections import defaultdict
device = "hx8k"
# device = "lp8k"
# device = "up5k"
sel_src_type = "LUTFF_OUT"
sel_dst_type = "LUTFF_IN_LUT"
#%% Read fuzz data
src_dst_pairs = defaultdict(lambda: 0)
delay_data = list()
all_delay_data = list()
delay_map_sum = np.zeros((41, 41))
delay_map_sum2 = np.zeros((41, 41))
delay_map_count = np.zeros((41, 41))
same_tile_delays = list()
neighbour_tile_delays = list()
type_delta_data = dict()
with open("tmfuzz_%s.txt" % device, "r") as f:
for line in f:
line = line.split()
if line[0] == "dst":
dst_xy = (int(line[1]), int(line[2]))
dst_type = line[3]
dst_wire = line[4]
src_xy = (int(line[1]), int(line[2]))
src_type = line[3]
src_wire = line[4]
delay = int(line[5])
estdelay = int(line[6])
all_delay_data.append((delay, estdelay))
src_dst_pairs[src_type, dst_type] += 1
dx = dst_xy[0] - src_xy[0]
dy = dst_xy[1] - src_xy[1]
if src_type == sel_src_type and dst_type == sel_dst_type:
if dx == 0 and dy == 0:
same_tile_delays.append(delay)
elif abs(dx) <= 1 and abs(dy) <= 1:
neighbour_tile_delays.append(delay)
else:
delay_data.append((delay, estdelay, dx, dy, 0, 0, 0))
relx = 20 + dst_xy[0] - src_xy[0]
rely = 20 + dst_xy[1] - src_xy[1]
if (0 <= relx <= 40) and (0 <= rely <= 40):
delay_map_sum[relx, rely] += delay
delay_map_sum2[relx, rely] += delay*delay
delay_map_count[relx, rely] += 1
if dst_type == sel_dst_type:
if src_type not in type_delta_data:
type_delta_data[src_type] = list()
type_delta_data[src_type].append((dx, dy, delay))
delay_data = np.array(delay_data)
all_delay_data = np.array(all_delay_data)
max_delay = np.max(delay_data[:, 0:2])
mean_same_tile_delays = np.mean(neighbour_tile_delays)
mean_neighbour_tile_delays = np.mean(neighbour_tile_delays)
print("Avg same tile delay: %.2f (%.2f std, N=%d)" % \
(mean_same_tile_delays, np.std(same_tile_delays), len(same_tile_delays)))
print("Avg neighbour tile delay: %.2f (%.2f std, N=%d)" % \
(mean_neighbour_tile_delays, np.std(neighbour_tile_delays), len(neighbour_tile_delays)))
#%% Apply simple low-weight bluring to fill gaps
for i in range(0):
neigh_sum = np.zeros((41, 41))
neigh_sum2 = np.zeros((41, 41))
neigh_count = np.zeros((41, 41))
for x in range(41):
for y in range(41):
for p in range(-1, 2):
for q in range(-1, 2):
if p == 0 and q == 0:
continue
if 0 <= (x+p) <= 40:
if 0 <= (y+q) <= 40:
neigh_sum[x, y] += delay_map_sum[x+p, y+q]
neigh_sum2[x, y] += delay_map_sum2[x+p, y+q]
neigh_count[x, y] += delay_map_count[x+p, y+q]
delay_map_sum += 0.1 * neigh_sum
delay_map_sum2 += 0.1 * neigh_sum2
delay_map_count += 0.1 * neigh_count
delay_map = delay_map_sum / delay_map_count
delay_map_std = np.sqrt(delay_map_count*delay_map_sum2 - delay_map_sum**2) / delay_map_count
#%% Print src-dst-pair summary
print("Src-Dst-Type pair summary:")
for cnt, src, dst in sorted([(v, k[0], k[1]) for k, v in src_dst_pairs.items()]):
print("%20s %20s %5d%s" % (src, dst, cnt, " *" if src == sel_src_type and dst == sel_dst_type else ""))
print()
#%% Plot estimate vs actual delay
plt.figure(figsize=(8, 3))
plt.title("Estimate vs Actual Delay")
plt.plot(all_delay_data[:, 0], all_delay_data[:, 1], ".")
plt.plot(delay_data[:, 0], delay_data[:, 1], ".")
plt.plot([0, max_delay], [0, max_delay], "k")
plt.ylabel("Estimated Delay")
plt.xlabel("Actual Delay")
plt.grid()
plt.show()
#%% Plot delay heatmap and std dev heatmap
plt.figure(figsize=(9, 3))
plt.subplot(121)
plt.title("Actual Delay Map")
plt.imshow(delay_map)
plt.colorbar()
plt.subplot(122)
plt.title("Standard Deviation")
plt.imshow(delay_map_std)
plt.colorbar()
plt.show()
#%% Generate Model #0
def nonlinearPreprocessor0(dx, dy):
dx, dy = abs(dx), abs(dy)
values = [1.0]
values.append(dx + dy)
return np.array(values)
A = np.zeros((41*41, len(nonlinearPreprocessor0(0, 0))))
b = np.zeros(41*41)
index = 0
for x in range(41):
for y in range(41):
if delay_map_count[x, y] > 0:
A[index, :] = nonlinearPreprocessor0(x-20, y-20)
b[index] = delay_map[x, y]
index += 1
model0_params, _, _, _ = np.linalg.lstsq(A, b)
print("Model #0 parameters:", model0_params)
model0_map = np.zeros((41, 41))
for x in range(41):
for y in range(41):
v = np.dot(model0_params, nonlinearPreprocessor0(x-20, y-20))
model0_map[x, y] = v
plt.figure(figsize=(9, 3))
plt.subplot(121)
plt.title("Model #0 Delay Map")
plt.imshow(model0_map)
plt.colorbar()
plt.subplot(122)
plt.title("Model #0 Error Map")
plt.imshow(model0_map - delay_map)
plt.colorbar()
plt.show()
for i in range(delay_data.shape[0]):
dx = delay_data[i, 2]
dy = delay_data[i, 3]
delay_data[i, 4] = np.dot(model0_params, nonlinearPreprocessor0(dx, dy))
plt.figure(figsize=(8, 3))
plt.title("Model #0 vs Actual Delay")
plt.plot(delay_data[:, 0], delay_data[:, 4], ".")
plt.plot(delay_map.flat, model0_map.flat, ".")
plt.plot([0, max_delay], [0, max_delay], "k")
plt.ylabel("Model #0 Delay")
plt.xlabel("Actual Delay")
plt.grid()
plt.show()
print("In-sample RMS error: %f" % np.sqrt(np.nanmean((delay_map - model0_map)**2)))
print("Out-of-sample RMS error: %f" % np.sqrt(np.nanmean((delay_data[:, 0] - delay_data[:, 4])**2)))
print()
#%% Generate Model #1
def nonlinearPreprocessor1(dx, dy):
dx, dy = abs(dx), abs(dy)
values = [1.0]
values.append(dx + dy) # 1-norm
values.append((dx**2 + dy**2)**(1/2)) # 2-norm
values.append((dx**3 + dy**3)**(1/3)) # 3-norm
return np.array(values)
A = np.zeros((41*41, len(nonlinearPreprocessor1(0, 0))))
b = np.zeros(41*41)
index = 0
for x in range(41):
for y in range(41):
if delay_map_count[x, y] > 0:
A[index, :] = nonlinearPreprocessor1(x-20, y-20)
b[index] = delay_map[x, y]
index += 1
model1_params, _, _, _ = np.linalg.lstsq(A, b)
print("Model #1 parameters:", model1_params)
model1_map = np.zeros((41, 41))
for x in range(41):
for y in range(41):
v = np.dot(model1_params, nonlinearPreprocessor1(x-20, y-20))
model1_map[x, y] = v
plt.figure(figsize=(9, 3))
plt.subplot(121)
plt.title("Model #1 Delay Map")
plt.imshow(model1_map)
plt.colorbar()
plt.subplot(122)
plt.title("Model #1 Error Map")
plt.imshow(model1_map - delay_map)
plt.colorbar()
plt.show()
for i in range(delay_data.shape[0]):
dx = delay_data[i, 2]
dy = delay_data[i, 3]
delay_data[i, 5] = np.dot(model1_params, nonlinearPreprocessor1(dx, dy))
plt.figure(figsize=(8, 3))
plt.title("Model #1 vs Actual Delay")
plt.plot(delay_data[:, 0], delay_data[:, 5], ".")
plt.plot(delay_map.flat, model1_map.flat, ".")
plt.plot([0, max_delay], [0, max_delay], "k")
plt.ylabel("Model #1 Delay")
plt.xlabel("Actual Delay")
plt.grid()
plt.show()
print("In-sample RMS error: %f" % np.sqrt(np.nanmean((delay_map - model1_map)**2)))
print("Out-of-sample RMS error: %f" % np.sqrt(np.nanmean((delay_data[:, 0] - delay_data[:, 5])**2)))
print()
#%% Generate Model #2
def nonlinearPreprocessor2(v):
return np.array([1, v, np.sqrt(v)])
A = np.zeros((41*41, len(nonlinearPreprocessor2(0))))
b = np.zeros(41*41)
index = 0
for x in range(41):
for y in range(41):
if delay_map_count[x, y] > 0:
A[index, :] = nonlinearPreprocessor2(model1_map[x, y])
b[index] = delay_map[x, y]
index += 1
model2_params, _, _, _ = np.linalg.lstsq(A, b)
print("Model #2 parameters:", model2_params)
model2_map = np.zeros((41, 41))
for x in range(41):
for y in range(41):
v = np.dot(model1_params, nonlinearPreprocessor1(x-20, y-20))
v = np.dot(model2_params, nonlinearPreprocessor2(v))
model2_map[x, y] = v
plt.figure(figsize=(9, 3))
plt.subplot(121)
plt.title("Model #2 Delay Map")
plt.imshow(model2_map)
plt.colorbar()
plt.subplot(122)
plt.title("Model #2 Error Map")
plt.imshow(model2_map - delay_map)
plt.colorbar()
plt.show()
for i in range(delay_data.shape[0]):
dx = delay_data[i, 2]
dy = delay_data[i, 3]
delay_data[i, 6] = np.dot(model2_params, nonlinearPreprocessor2(delay_data[i, 5]))
plt.figure(figsize=(8, 3))
plt.title("Model #2 vs Actual Delay")
plt.plot(delay_data[:, 0], delay_data[:, 6], ".")
plt.plot(delay_map.flat, model2_map.flat, ".")
plt.plot([0, max_delay], [0, max_delay], "k")
plt.ylabel("Model #2 Delay")
plt.xlabel("Actual Delay")
plt.grid()
plt.show()
print("In-sample RMS error: %f" % np.sqrt(np.nanmean((delay_map - model2_map)**2)))
print("Out-of-sample RMS error: %f" % np.sqrt(np.nanmean((delay_data[:, 0] - delay_data[:, 6])**2)))
print()
#%% Generate deltas for different source net types
type_deltas = dict()
print("Delay deltas for different src types:")
for src_type in sorted(type_delta_data.keys()):
deltas = list()
for dx, dy, delay in type_delta_data[src_type]:
dx = abs(dx)
dy = abs(dy)
if dx > 1 or dy > 1:
est = model0_params[0] + model0_params[1] * (dx + dy)
else:
est = mean_neighbour_tile_delays
deltas.append(delay - est)
print("%15s: %8.2f (std %6.2f)" % (\
src_type, np.mean(deltas), np.std(deltas)))
type_deltas[src_type] = np.mean(deltas)
#%% Print C defs of model parameters
print("--snip--")
print("%d, %d, %d," % (mean_neighbour_tile_delays, 128 * model0_params[0], 128 * model0_params[1]))
print("%d, %d, %d, %d," % (128 * model1_params[0], 128 * model1_params[1], 128 * model1_params[2], 128 * model1_params[3]))
print("%d, %d, %d," % (128 * model2_params[0], 128 * model2_params[1], 128 * model2_params[2]))
print("%d, %d, %d, %d" % (type_deltas["LOCAL"], type_deltas["LUTFF_IN"], \
(type_deltas["SP4_H"] + type_deltas["SP4_V"]) / 2,
(type_deltas["SP12_H"] + type_deltas["SP12_V"]) / 2))
print("--snap--")