Copy ice40 into xc7
This commit is contained in:
parent
2e02f2d616
commit
d53658a079
902
xc7/arch.cc
Normal file
902
xc7/arch.cc
Normal 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
841
xc7/arch.h
Normal 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
162
xc7/arch_place.cc
Normal 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
154
xc7/arch_pybindings.cc
Normal 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
62
xc7/arch_pybindings.h
Normal 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
194
xc7/archdefs.h
Normal 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
6
xc7/benchmark/.gitignore
vendored
Normal 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
|
40
xc7/benchmark/hx8kdemo.pcf
Normal file
40
xc7/benchmark/hx8kdemo.pcf
Normal 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
139
xc7/benchmark/hx8kdemo.v
Normal 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
2977
xc7/benchmark/picorv32.v
Normal file
File diff suppressed because it is too large
Load Diff
241
xc7/benchmark/picosoc.v
Normal file
241
xc7/benchmark/picosoc.v
Normal 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
|
||||
|
91
xc7/benchmark/report.ipynb
Normal file
91
xc7/benchmark/report.ipynb
Normal 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
137
xc7/benchmark/simpleuart.v
Normal 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
579
xc7/benchmark/spimemio.v
Normal 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
999
xc7/bitstream.cc
Normal 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>> ¶ms, 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
34
xc7/bitstream.h
Normal 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
6
xc7/blinky.pcf
Normal 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
15
xc7/blinky.proj
Normal 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
8
xc7/blinky.sh
Executable 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
27
xc7/blinky.v
Normal 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
3
xc7/blinky.ys
Normal file
@ -0,0 +1,3 @@
|
||||
read_verilog blinky.v
|
||||
synth_ice40 -top blinky
|
||||
write_json blinky.json
|
25
xc7/blinky_tb.v
Normal file
25
xc7/blinky_tb.v
Normal 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
4
xc7/carry_tests/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
*.vcd
|
||||
*_out.v
|
||||
*.out
|
||||
|
9
xc7/carry_tests/counter.v
Normal file
9
xc7/carry_tests/counter.v
Normal 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
|
23
xc7/carry_tests/counter_tb.v
Normal file
23
xc7/carry_tests/counter_tb.v
Normal 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
10
xc7/carry_tests/test.pcf
Normal 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
9
xc7/carry_tests/test.sh
Executable 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
409
xc7/cells.cc
Normal 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
114
xc7/cells.h
Normal 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
287
xc7/chains.cc
Normal 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
27
xc7/chains.h
Normal 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
1280
xc7/chipdb.py
Normal file
File diff suppressed because it is too large
Load Diff
437
xc7/constids.inc
Normal file
437
xc7/constids.inc
Normal 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
238
xc7/delay.cc
Normal 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
84
xc7/family.cmake
Normal 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
766
xc7/gfx.cc
Normal 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
523
xc7/gfx.h
Normal 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
14
xc7/icebreaker.pcf
Normal 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
31
xc7/icebreaker.v
Normal 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
3
xc7/icebreaker.ys
Normal file
@ -0,0 +1,3 @@
|
||||
read_verilog icebreaker.v
|
||||
synth_ice40 -nocarry -top icebreaker
|
||||
write_json icebreaker.json
|
164
xc7/main.cc
Normal file
164
xc7/main.cc
Normal 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
907
xc7/pack.cc
Normal 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
2
xc7/pack_tests/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.vcd
|
||||
*_out.v
|
43
xc7/pack_tests/ffmodes.v
Normal file
43
xc7/pack_tests/ffmodes.v
Normal 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
27
xc7/pack_tests/locals.v
Normal 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
|
54
xc7/pack_tests/place_constr.v
Normal file
54
xc7/pack_tests/place_constr.v
Normal 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
10
xc7/pack_tests/test.pcf
Normal 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
16
xc7/pack_tests/test.sh
Executable 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
76
xc7/pcf.cc
Normal 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
34
xc7/pcf.h
Normal 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
15
xc7/picorv32.proj
Normal 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
7
xc7/picorv32.sh
Executable 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
42
xc7/picorv32_benchmark.py
Executable 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
31
xc7/picorv32_top.v
Normal 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
73
xc7/project.cc
Normal 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
6
xc7/resource/chipdb.rc
Normal 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
30
xc7/resource/embed.cc
Normal 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
5
xc7/resource/resource.h
Normal 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
357
xc7/tmfuzz.py
Normal 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--")
|
Loading…
Reference in New Issue
Block a user