Compare commits
53 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
2830340870 | ||
![]() |
75d684d032 | ||
![]() |
e2b3e7e86f | ||
![]() |
dbcc9b734f | ||
![]() |
5c142fc257 | ||
![]() |
6ee0098cf9 | ||
![]() |
b3658c47e0 | ||
![]() |
fb9471aced | ||
![]() |
1adda174de | ||
![]() |
a2e5ded5a6 | ||
![]() |
86e947e9d2 | ||
![]() |
9e2208adbe | ||
![]() |
3b1b549aa3 | ||
![]() |
1eac0528ab | ||
![]() |
3e6e0273dd | ||
![]() |
ea6cbf9804 | ||
![]() |
cb1f01f3a6 | ||
![]() |
7d84aefb7f | ||
![]() |
fdb2dd4c42 | ||
![]() |
2a41124378 | ||
![]() |
e6450a179b | ||
![]() |
94071078dc | ||
![]() |
005bffab48 | ||
![]() |
1fc8809e18 | ||
![]() |
91a88dda77 | ||
![]() |
0987d5a2b9 | ||
![]() |
ed6f6a4d98 | ||
![]() |
5fb63c6a0c | ||
![]() |
6cc8c2ee54 | ||
![]() |
f92728b826 | ||
![]() |
150428fe7b | ||
![]() |
5f85167f8c | ||
![]() |
4badd8bbbf | ||
![]() |
d41f13bde9 | ||
![]() |
63beb45950 | ||
![]() |
d09c901f67 | ||
![]() |
cb990e3731 | ||
![]() |
f027bc3cf9 | ||
![]() |
81a1f02dcc | ||
![]() |
ab29eab513 | ||
![]() |
b020a47bb8 | ||
![]() |
2f537d2ccc | ||
![]() |
86d93849c8 | ||
![]() |
757ed10047 | ||
![]() |
2b038faeb1 | ||
![]() |
de2d551035 | ||
![]() |
7f98c33d6b | ||
![]() |
19f5d24b79 | ||
![]() |
503fafd574 | ||
![]() |
8436191307 | ||
![]() |
0add621416 | ||
![]() |
4d46e57d12 | ||
![]() |
6cf832e9b0 |
@ -454,6 +454,9 @@ template <typename R> struct BaseArch : ArchAPI<R>
|
||||
// Flow methods
|
||||
virtual void assignArchInfo() override {};
|
||||
|
||||
// HACK
|
||||
virtual bool isPipInverting(PipId pip) const { return false; }
|
||||
|
||||
// --------------------------------------------------------------
|
||||
// These structures are used to provide default implementations of bel/wire/pip binding. Arches might want to
|
||||
// replace them with their own, for example to use faster access structures than dict. Arches might also
|
||||
|
@ -94,9 +94,9 @@ struct Router2
|
||||
// The notional location of the wire, to guarantee thread safety
|
||||
int16_t x = 0, y = 0;
|
||||
// Visit data
|
||||
PipId pip_fwd, pip_bwd;
|
||||
bool visited_fwd = false, visited_bwd = false;
|
||||
float cost_fwd = 0.0, cost_bwd = 0.0;
|
||||
PipId pip_fwd[2], pip_bwd[2];
|
||||
bool visited_fwd[2] = {false, false}, visited_bwd[2] = {false, false};
|
||||
float cost_fwd[2] = {0.0, 0.0}, cost_bwd[2] = {0.0, 0.0};
|
||||
};
|
||||
|
||||
Context *ctx;
|
||||
@ -240,11 +240,12 @@ struct Router2
|
||||
struct QueuedWire
|
||||
{
|
||||
|
||||
explicit QueuedWire(int wire = -1, WireScore score = WireScore{}, int randtag = 0)
|
||||
: wire(wire), score(score), randtag(randtag) {};
|
||||
explicit QueuedWire(int wire = -1, WireScore score = WireScore{}, bool inverted = false, int randtag = 0)
|
||||
: wire(wire), score(score), inverted(inverted), randtag(randtag) {};
|
||||
|
||||
int wire;
|
||||
WireScore score;
|
||||
bool inverted = false;
|
||||
int randtag = 0;
|
||||
|
||||
struct Greater
|
||||
@ -527,12 +528,18 @@ struct Router2
|
||||
void reset_wires(ThreadContext &t)
|
||||
{
|
||||
for (auto w : t.dirty_wires) {
|
||||
flat_wires[w].pip_fwd = PipId();
|
||||
flat_wires[w].pip_bwd = PipId();
|
||||
flat_wires[w].visited_fwd = false;
|
||||
flat_wires[w].visited_bwd = false;
|
||||
flat_wires[w].cost_fwd = 0.0;
|
||||
flat_wires[w].cost_bwd = 0.0;
|
||||
flat_wires[w].pip_fwd[0] = PipId();
|
||||
flat_wires[w].pip_fwd[1] = PipId();
|
||||
flat_wires[w].pip_bwd[0] = PipId();
|
||||
flat_wires[w].pip_bwd[1] = PipId();
|
||||
flat_wires[w].visited_fwd[0] = false;
|
||||
flat_wires[w].visited_fwd[1] = false;
|
||||
flat_wires[w].visited_bwd[0] = false;
|
||||
flat_wires[w].visited_bwd[1] = false;
|
||||
flat_wires[w].cost_fwd[0] = 0.0;
|
||||
flat_wires[w].cost_fwd[1] = 0.0;
|
||||
flat_wires[w].cost_bwd[0] = 0.0;
|
||||
flat_wires[w].cost_bwd[1] = 0.0;
|
||||
}
|
||||
t.dirty_wires.clear();
|
||||
}
|
||||
@ -563,32 +570,32 @@ struct Router2
|
||||
}
|
||||
|
||||
// Functions for marking wires as visited, and checking if they have already been visited
|
||||
void set_visited_fwd(ThreadContext &t, int wire, PipId pip, float cost)
|
||||
void set_visited_fwd(ThreadContext &t, int wire, PipId pip, float cost, bool inverted)
|
||||
{
|
||||
auto &wd = flat_wires.at(wire);
|
||||
if (!wd.visited_fwd && !wd.visited_bwd)
|
||||
if (!wd.visited_fwd[0] && !wd.visited_fwd[1] && !wd.visited_bwd[0] && !wd.visited_bwd[1])
|
||||
t.dirty_wires.push_back(wire);
|
||||
wd.pip_fwd = pip;
|
||||
wd.visited_fwd = true;
|
||||
wd.cost_fwd = cost;
|
||||
wd.pip_fwd[inverted] = pip;
|
||||
wd.visited_fwd[inverted] = true;
|
||||
wd.cost_fwd[inverted] = cost;
|
||||
}
|
||||
void set_visited_bwd(ThreadContext &t, int wire, PipId pip, float cost)
|
||||
void set_visited_bwd(ThreadContext &t, int wire, PipId pip, float cost, bool inverted)
|
||||
{
|
||||
auto &wd = flat_wires.at(wire);
|
||||
if (!wd.visited_fwd && !wd.visited_bwd)
|
||||
if (!wd.visited_fwd[0] && !wd.visited_fwd[1] && !wd.visited_bwd[0] && !wd.visited_bwd[1])
|
||||
t.dirty_wires.push_back(wire);
|
||||
wd.pip_bwd = pip;
|
||||
wd.visited_bwd = true;
|
||||
wd.cost_bwd = cost;
|
||||
wd.pip_bwd[inverted] = pip;
|
||||
wd.visited_bwd[inverted] = true;
|
||||
wd.cost_bwd[inverted] = cost;
|
||||
}
|
||||
|
||||
bool was_visited_fwd(int wire, float cost)
|
||||
bool was_visited_fwd(int wire, float cost, bool inverted)
|
||||
{
|
||||
return flat_wires.at(wire).visited_fwd && flat_wires.at(wire).cost_fwd <= cost;
|
||||
return flat_wires.at(wire).visited_fwd[inverted] && flat_wires.at(wire).cost_fwd[inverted] <= cost;
|
||||
}
|
||||
bool was_visited_bwd(int wire, float cost)
|
||||
bool was_visited_bwd(int wire, float cost, bool inverted)
|
||||
{
|
||||
return flat_wires.at(wire).visited_bwd && flat_wires.at(wire).cost_bwd <= cost;
|
||||
return flat_wires.at(wire).visited_bwd[inverted] && flat_wires.at(wire).cost_bwd[inverted] <= cost;
|
||||
}
|
||||
|
||||
float get_arc_crit(NetInfo *net, store_index<PortRef> i)
|
||||
@ -635,12 +642,13 @@ struct Router2
|
||||
// We have two modes:
|
||||
// 0. starting within a small range of existing routing
|
||||
// 1. expanding from all routing
|
||||
int mode = 0;
|
||||
int mode = 1;
|
||||
if (net->users.entries() < 4 || nd.wires.empty() || (crit > 0.95))
|
||||
mode = 1;
|
||||
|
||||
// This records the point where forwards and backwards routing met
|
||||
int midpoint_wire = -1;
|
||||
bool midpoint_inversion = false;
|
||||
int explored = 1;
|
||||
|
||||
for (; mode < 2; mode++) {
|
||||
@ -666,9 +674,11 @@ struct Router2
|
||||
int wire_idx = wire_to_idx.at(wire);
|
||||
base_score.togo_cost = get_togo_cost(net, i, wire_idx, dst_wire, false, crit_weight);
|
||||
t.fwd_queue.push(QueuedWire(wire_idx, base_score));
|
||||
set_visited_fwd(t, wire_idx, PipId(), 0.0);
|
||||
set_visited_fwd(t, wire_idx, PipId(), 0.0, false);
|
||||
};
|
||||
#ifndef ARCH_HIMBAECHEL
|
||||
auto &dst_data = flat_wires.at(dst_wire_idx);
|
||||
// TODO: does this break gatemate because of not knowing inversion state? (yes)
|
||||
// Look for nearby existing routing
|
||||
for (int dy = -cfg.bb_margin_y; dy <= cfg.bb_margin_y; dy++)
|
||||
for (int dx = -cfg.bb_margin_x; dx <= cfg.bb_margin_x; dx++) {
|
||||
@ -680,6 +690,7 @@ struct Router2
|
||||
seed_queue_fwd(wire);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mode == 0 && t.fwd_queue.size() < 4)
|
||||
continue;
|
||||
@ -688,7 +699,7 @@ struct Router2
|
||||
// Seed forwards with the source wire, if less than 8 existing wires added
|
||||
seed_queue_fwd(src_wire);
|
||||
} else {
|
||||
set_visited_fwd(t, src_wire_idx, PipId(), 0.0);
|
||||
set_visited_fwd(t, src_wire_idx, PipId(), 0.0, false);
|
||||
}
|
||||
}
|
||||
auto seed_queue_bwd = [&](WireId wire) {
|
||||
@ -698,7 +709,7 @@ struct Router2
|
||||
int wire_idx = wire_to_idx.at(wire);
|
||||
base_score.togo_cost = get_togo_cost(net, i, wire_idx, src_wire, true, crit_weight);
|
||||
t.bwd_queue.push(QueuedWire(wire_idx, base_score));
|
||||
set_visited_bwd(t, wire_idx, PipId(), 0.0);
|
||||
set_visited_bwd(t, wire_idx, PipId(), 0.0, false);
|
||||
};
|
||||
|
||||
// Seed backwards with the dest wire
|
||||
@ -717,11 +728,15 @@ struct Router2
|
||||
auto curr = t.fwd_queue.top();
|
||||
t.fwd_queue.pop();
|
||||
++explored;
|
||||
if (was_visited_bwd(curr.wire, std::numeric_limits<float>::max())) {
|
||||
if (was_visited_bwd(curr.wire, std::numeric_limits<float>::max(), curr.inverted)) {
|
||||
// Meet in the middle; done
|
||||
midpoint_wire = curr.wire;
|
||||
midpoint_inversion = curr.inverted;
|
||||
break;
|
||||
}
|
||||
if (was_visited_bwd(curr.wire, std::numeric_limits<float>::max(), !curr.inverted)) {
|
||||
ROUTE_LOG_DBG("fwd: met bwd with wrong polarity\n");
|
||||
}
|
||||
auto &curr_data = flat_wires.at(curr.wire);
|
||||
for (PipId dh : ctx->getPipsDownhill(curr_data.w)) {
|
||||
// Skip pips outside of box in bounding-box mode
|
||||
@ -736,7 +751,7 @@ struct Router2
|
||||
next_score.cost = curr.score.cost + score_wire_for_arc(net, i, phys_pin, next, dh, crit_weight);
|
||||
next_score.togo_cost =
|
||||
cfg.estimate_weight * get_togo_cost(net, i, next_idx, dst_wire, false, crit_weight);
|
||||
if (was_visited_fwd(next_idx, next_score.delay)) {
|
||||
if (was_visited_fwd(next_idx, next_score.delay, /*inverted=*/curr.inverted ^ ctx->isPipInverting(dh))) {
|
||||
// Don't expand the same node twice.
|
||||
continue;
|
||||
}
|
||||
@ -752,8 +767,8 @@ struct Router2
|
||||
continue;
|
||||
if (!thread_test_wire(t, nwd))
|
||||
continue; // thread safety issue
|
||||
set_visited_fwd(t, next_idx, dh, next_score.delay);
|
||||
t.fwd_queue.push(QueuedWire(next_idx, next_score, t.rng.rng()));
|
||||
set_visited_fwd(t, next_idx, dh, next_score.delay, curr.inverted ^ ctx->isPipInverting(dh));
|
||||
t.fwd_queue.push(QueuedWire(next_idx, next_score, curr.inverted ^ ctx->isPipInverting(dh), t.rng.rng()));
|
||||
}
|
||||
}
|
||||
if (!t.bwd_queue.empty()) {
|
||||
@ -762,12 +777,16 @@ struct Router2
|
||||
t.bwd_queue.pop();
|
||||
++explored;
|
||||
auto &curr_data = flat_wires.at(curr.wire);
|
||||
if (was_visited_fwd(curr.wire, std::numeric_limits<float>::max()) ||
|
||||
(const_mode && ctx->getWireConstantValue(curr_data.w) == net->constant_value)) {
|
||||
if (was_visited_fwd(curr.wire, std::numeric_limits<float>::max(), curr.inverted) ||
|
||||
(const_mode && ctx->getWireConstantValue(curr_data.w) == net->constant_value && !curr.inverted)) {
|
||||
// Meet in the middle; done
|
||||
midpoint_wire = curr.wire;
|
||||
midpoint_inversion = curr.inverted;
|
||||
break;
|
||||
}
|
||||
if (was_visited_fwd(curr.wire, std::numeric_limits<float>::max(), !curr.inverted)) {
|
||||
ROUTE_LOG_DBG("bwd: met fwd with wrong polarity\n");
|
||||
}
|
||||
// Don't allow the same wire to be bound to the same net with a different driving pip
|
||||
PipId bound_pip;
|
||||
auto fnd_wire = nd.wires.find(curr_data.w);
|
||||
@ -790,7 +809,7 @@ struct Router2
|
||||
? 0
|
||||
: cfg.estimate_weight * get_togo_cost(net, i, next_idx, src_wire,
|
||||
true, crit_weight);
|
||||
if (was_visited_bwd(next_idx, next_score.delay)) {
|
||||
if (was_visited_bwd(next_idx, next_score.delay, /*inverted=*/curr.inverted ^ ctx->isPipInverting(uh))) {
|
||||
// Don't expand the same node twice.
|
||||
continue;
|
||||
}
|
||||
@ -802,8 +821,8 @@ struct Router2
|
||||
continue;
|
||||
if (!thread_test_wire(t, nwd))
|
||||
continue; // thread safety issue
|
||||
set_visited_bwd(t, next_idx, uh, next_score.delay);
|
||||
t.bwd_queue.push(QueuedWire(next_idx, next_score, t.rng.rng()));
|
||||
set_visited_bwd(t, next_idx, uh, next_score.delay, curr.inverted ^ ctx->isPipInverting(uh));
|
||||
t.bwd_queue.push(QueuedWire(next_idx, next_score, curr.inverted ^ ctx->isPipInverting(uh), t.rng.rng()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -812,27 +831,29 @@ struct Router2
|
||||
}
|
||||
ArcRouteResult result = ARC_SUCCESS;
|
||||
if (midpoint_wire != -1) {
|
||||
ROUTE_LOG_DBG(" Routed (explored %d wires): ", explored);
|
||||
ROUTE_LOG_DBG(" Routed (explored %d wires):\n", explored);
|
||||
if (const_mode) {
|
||||
bind_pip_internal(nd, i, midpoint_wire, PipId());
|
||||
} else {
|
||||
int cursor_bwd = midpoint_wire;
|
||||
while (was_visited_fwd(cursor_bwd, std::numeric_limits<float>::max())) {
|
||||
PipId pip = flat_wires.at(cursor_bwd).pip_fwd;
|
||||
int inversion_bwd = midpoint_inversion;
|
||||
while (was_visited_fwd(cursor_bwd, std::numeric_limits<float>::max(), inversion_bwd)) {
|
||||
PipId pip = flat_wires.at(cursor_bwd).pip_fwd[inversion_bwd];
|
||||
if (pip == PipId() && cursor_bwd != src_wire_idx)
|
||||
break;
|
||||
bind_pip_internal(nd, i, cursor_bwd, pip);
|
||||
if (ctx->debug && !is_mt) {
|
||||
auto &wd = flat_wires.at(cursor_bwd);
|
||||
ROUTE_LOG_DBG(" fwd wire: %s (curr %d hist %f share %d)\n", ctx->nameOfWire(wd.w),
|
||||
wd.curr_cong - 1, wd.hist_cong_cost, nd.wires.at(wd.w).second);
|
||||
ROUTE_LOG_DBG(" fwd wire: %s (curr %d hist %f share %d inverted %d)\n", ctx->nameOfWire(wd.w),
|
||||
wd.curr_cong - 1, wd.hist_cong_cost, nd.wires.at(wd.w).second, inversion_bwd);
|
||||
}
|
||||
if (pip == PipId()) {
|
||||
break;
|
||||
}
|
||||
ROUTE_LOG_DBG(" fwd pip: %s (%d, %d)\n", ctx->nameOfPip(pip), ctx->getPipLocation(pip).x,
|
||||
ctx->getPipLocation(pip).y);
|
||||
ROUTE_LOG_DBG(" fwd pip: %s (%d, %d) (inverting %d)\n", ctx->nameOfPip(pip), ctx->getPipLocation(pip).x,
|
||||
ctx->getPipLocation(pip).y, ctx->isPipInverting(pip));
|
||||
cursor_bwd = wire_to_idx.at(ctx->getPipSrcWire(pip));
|
||||
inversion_bwd ^= ctx->isPipInverting(pip);
|
||||
}
|
||||
|
||||
while (cursor_bwd != src_wire_idx) {
|
||||
@ -857,19 +878,21 @@ struct Router2
|
||||
}
|
||||
|
||||
int cursor_fwd = midpoint_wire;
|
||||
while (was_visited_bwd(cursor_fwd, std::numeric_limits<float>::max())) {
|
||||
PipId pip = flat_wires.at(cursor_fwd).pip_bwd;
|
||||
int inversion_fwd = midpoint_inversion;
|
||||
while (was_visited_bwd(cursor_fwd, std::numeric_limits<float>::max(), inversion_fwd)) {
|
||||
PipId pip = flat_wires.at(cursor_fwd).pip_bwd[inversion_fwd];
|
||||
if (pip == PipId()) {
|
||||
break;
|
||||
}
|
||||
ROUTE_LOG_DBG(" bwd pip: %s (%d, %d)\n", ctx->nameOfPip(pip), ctx->getPipLocation(pip).x,
|
||||
ctx->getPipLocation(pip).y);
|
||||
ROUTE_LOG_DBG(" bwd pip: %s (%d, %d) (inverting %d)\n", ctx->nameOfPip(pip), ctx->getPipLocation(pip).x,
|
||||
ctx->getPipLocation(pip).y, ctx->isPipInverting(pip));
|
||||
cursor_fwd = wire_to_idx.at(ctx->getPipDstWire(pip));
|
||||
inversion_fwd ^= ctx->isPipInverting(pip);
|
||||
bind_pip_internal(nd, i, cursor_fwd, pip);
|
||||
if (ctx->debug && !is_mt) {
|
||||
auto &wd = flat_wires.at(cursor_fwd);
|
||||
ROUTE_LOG_DBG(" bwd wire: %s (curr %d hist %f share %d)\n", ctx->nameOfWire(wd.w),
|
||||
wd.curr_cong - 1, wd.hist_cong_cost, nd.wires.at(wd.w).second);
|
||||
ROUTE_LOG_DBG(" bwd wire: %s (curr %d hist %f share %d inverted %d)\n", ctx->nameOfWire(wd.w),
|
||||
wd.curr_cong - 1, wd.hist_cong_cost, nd.wires.at(wd.w).second, inversion_fwd);
|
||||
}
|
||||
}
|
||||
NPNR_ASSERT(cursor_fwd == dst_wire_idx);
|
||||
|
@ -662,6 +662,9 @@ struct Arch : BaseArch<ArchRanges>
|
||||
uarch->notifyPipChange(pip, nullptr);
|
||||
BaseArch::unbindPip(pip);
|
||||
}
|
||||
bool isPipInverting(PipId pip) const override {
|
||||
return uarch->isPipInverting(pip);
|
||||
}
|
||||
|
||||
// -------------------------------------------------
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
set(HIMBAECHEL_UARCHES "example;gowin;xilinx;ng-ultra")
|
||||
set(HIMBAECHEL_UARCHES "example;gowin;xilinx;ng-ultra;gatemate")
|
||||
foreach(uarch ${HIMBAECHEL_UARCHES})
|
||||
add_subdirectory(${family}/uarch/${uarch})
|
||||
aux_source_directory(${family}/uarch/${uarch} HM_UARCH_FILES)
|
||||
|
@ -130,6 +130,9 @@ struct HimbaechelAPI
|
||||
// For custom placer configuration
|
||||
virtual void configurePlacerHeap(PlacerHeapCfg &cfg) {};
|
||||
|
||||
// HACK
|
||||
virtual bool isPipInverting(PipId pip) const { return false; }
|
||||
|
||||
virtual ~HimbaechelAPI() {};
|
||||
};
|
||||
|
||||
|
38
himbaechel/uarch/gatemate/CMakeLists.txt
Normal file
38
himbaechel/uarch/gatemate/CMakeLists.txt
Normal file
@ -0,0 +1,38 @@
|
||||
message(STATUS "Configuring Himbaechel-GateMate uarch")
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
project(himbaechel-gatemate-chipdb NONE)
|
||||
|
||||
set(HIMBAECHEL_PEPPERCORN_PATH "" CACHE STRING
|
||||
"Path to a Project Peppercorn database scripts")
|
||||
|
||||
set(ALL_HIMBAECHE_GATEMATE_DEVICES CCGM1A1 CCGM1A2 CCGM1A4 CCGM1A9 CCGM1A16 CCGM1A25)
|
||||
set(HIMBAECHEL_GATEMATE_DEVICES "" CACHE STRING
|
||||
"Include support for these GateMate devices (available: ${ALL_HIMBAECHE_GATEMATE_DEVICES})")
|
||||
message(STATUS "Enabled Himbaechel-GateMate devices: ${HIMBAECHEL_GATEMATE_DEVICES}")
|
||||
|
||||
set(chipdb_binaries)
|
||||
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/share/himbaechel/gatemate)
|
||||
foreach(device ${HIMBAECHEL_GATEMATE_DEVICES})
|
||||
set(device_bba ${CMAKE_BINARY_DIR}/share/himbaechel/gatemate/chipdb-${device}.bba)
|
||||
set(device_bin ${CMAKE_BINARY_DIR}/share/himbaechel/gatemate/chipdb-${device}.bin)
|
||||
if("${HIMBAECHEL_PEPPERCORN_PATH}" STREQUAL "")
|
||||
message(SEND_ERROR "HIMBAECHEL_PEPPERCORN_PATH must be set to a Project Peppercorn checkout")
|
||||
endif()
|
||||
add_custom_command(
|
||||
OUTPUT ${device_bin}
|
||||
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/gen/arch_gen.py --device ${device} --bba ${device_bba} --lib ${HIMBAECHEL_PEPPERCORN_PATH}/gatemate
|
||||
COMMAND bbasm ${BBASM_ENDIAN_FLAG} ${device_bba} ${device_bin}.new
|
||||
# atomically update
|
||||
COMMAND ${CMAKE_COMMAND} -E rename ${device_bin}.new ${device_bin}
|
||||
DEPENDS
|
||||
bbasm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/gen/arch_gen.py
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/constids.inc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/gfxids.inc
|
||||
VERBATIM)
|
||||
list(APPEND chipdb_binaries ${device_bin})
|
||||
endforeach()
|
||||
|
||||
add_custom_target(chipdb-himbaechel-gatemate ALL DEPENDS ${chipdb_binaries})
|
||||
install(DIRECTORY ${CMAKE_BINARY_DIR}/share/himbaechel/gatemate/ DESTINATION share/nextpnr/himbaechel/gatemate
|
||||
PATTERN "*.bba" EXCLUDE)
|
197
himbaechel/uarch/gatemate/bitstream.cc
Normal file
197
himbaechel/uarch/gatemate/bitstream.cc
Normal file
@ -0,0 +1,197 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2024 The Project Peppercorn Authors.
|
||||
*
|
||||
* 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 <fstream>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include "config.h"
|
||||
#include "gatemate.h"
|
||||
|
||||
#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc"
|
||||
#include "himbaechel_constids.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
namespace {
|
||||
|
||||
struct BitstreamBackend
|
||||
{
|
||||
Context *ctx;
|
||||
GateMateImpl *uarch;
|
||||
const std::string &device;
|
||||
std::ostream &out;
|
||||
|
||||
BitstreamBackend(Context *ctx, GateMateImpl *uarch, const std::string &device, std::ostream &out)
|
||||
: ctx(ctx), uarch(uarch), device(device), out(out) {};
|
||||
|
||||
std::vector<bool> int_to_bitvector(int val, int size)
|
||||
{
|
||||
std::vector<bool> bv;
|
||||
for (int i = 0; i < size; i++) {
|
||||
bv.push_back((val & (1 << i)) != 0);
|
||||
}
|
||||
return bv;
|
||||
}
|
||||
|
||||
std::vector<bool> str_to_bitvector(std::string str, int size)
|
||||
{
|
||||
std::vector<bool> bv;
|
||||
bv.resize(size, 0);
|
||||
for (int i = 0; i < int(str.size()); i++) {
|
||||
char c = str.at((str.size() - i) - 1);
|
||||
NPNR_ASSERT(c == '0' || c == '1');
|
||||
bv.at(i) = (c == '1');
|
||||
}
|
||||
return bv;
|
||||
}
|
||||
|
||||
CfgLoc getConfigLoc(int tile)
|
||||
{
|
||||
auto ti = *uarch->tile_extra_data(tile);
|
||||
CfgLoc loc;
|
||||
loc.die = ti.die;
|
||||
loc.x = ti.bit_x;
|
||||
loc.y = ti.bit_y;
|
||||
return loc;
|
||||
}
|
||||
|
||||
void write_bitstream()
|
||||
{
|
||||
ChipConfig cc;
|
||||
cc.chip_name = device;
|
||||
cc.configs[0].add_word("GPIO.BANK_E1", int_to_bitvector(1, 1));
|
||||
cc.configs[0].add_word("GPIO.BANK_E2", int_to_bitvector(1, 1));
|
||||
cc.configs[0].add_word("GPIO.BANK_N1", int_to_bitvector(1, 1));
|
||||
cc.configs[0].add_word("GPIO.BANK_N2", int_to_bitvector(1, 1));
|
||||
cc.configs[0].add_word("GPIO.BANK_S1", int_to_bitvector(1, 1));
|
||||
cc.configs[0].add_word("GPIO.BANK_S2", int_to_bitvector(1, 1));
|
||||
cc.configs[0].add_word("GPIO.BANK_W1", int_to_bitvector(1, 1));
|
||||
cc.configs[0].add_word("GPIO.BANK_W2", int_to_bitvector(1, 1));
|
||||
for (auto &cell : ctx->cells) {
|
||||
CfgLoc loc = getConfigLoc(cell.second.get()->bel.tile);
|
||||
auto ¶ms = cell.second.get()->params;
|
||||
switch (cell.second->type.index) {
|
||||
case id_CC_IBUF.index:
|
||||
case id_CC_TOBUF.index:
|
||||
case id_CC_OBUF.index:
|
||||
case id_CC_IOBUF.index:
|
||||
case id_CC_LVDS_IBUF.index:
|
||||
case id_CC_LVDS_TOBUF.index:
|
||||
case id_CC_LVDS_OBUF.index:
|
||||
case id_CC_LVDS_IOBUF.index:
|
||||
for (auto &p : params) {
|
||||
cc.tiles[loc].add_word(stringf("GPIO.%s", p.first.c_str(ctx)), p.second.as_bits());
|
||||
}
|
||||
break;
|
||||
case id_CPE.index: {
|
||||
int id = uarch->tile_extra_data(cell.second.get()->bel.tile)->prim_id;
|
||||
for (auto &p : params) {
|
||||
cc.tiles[loc].add_word(stringf("CPE%d.%s", id, p.first.c_str(ctx)), p.second.as_bits());
|
||||
}
|
||||
} break;
|
||||
case id_BUFG.index:
|
||||
{
|
||||
Loc l = ctx->getBelLocation(cell.second->bel);
|
||||
cc.configs[0].add_word(stringf("GLBOUT.GLB%d_EN",l.z), int_to_bitvector(1,1));
|
||||
}
|
||||
break;
|
||||
case id_PLL.index:
|
||||
{
|
||||
Loc l = ctx->getBelLocation(cell.second->bel);
|
||||
cc.configs[0].add_word(stringf("PLL%d.CLK_OUT_EN",l.z-4), int_to_bitvector(1,1));
|
||||
cc.configs[0].add_word(stringf("PLL%d.PLL_EN",l.z-4), int_to_bitvector(1,1));
|
||||
cc.configs[0].add_word(stringf("PLL%d.PLL_RST",l.z-4), int_to_bitvector(1,1));
|
||||
for (auto &p : params) {
|
||||
cc.configs[0].add_word(stringf("PLL%d.%s", l.z-4, p.first.c_str(ctx)), p.second.as_bits());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case id_USR_RSTN.index:
|
||||
break;
|
||||
default:
|
||||
log_error("Unhandled cell %s of type %s\n", cell.second.get()->name.c_str(ctx),
|
||||
cell.second->type.c_str(ctx));
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &net : ctx->nets) {
|
||||
NetInfo *ni = net.second.get();
|
||||
if (ni->wires.empty())
|
||||
continue;
|
||||
std::set<std::string> nets;
|
||||
for (auto &w : ni->wires) {
|
||||
if (w.second.pip != PipId()) {
|
||||
PipId pip = w.second.pip;
|
||||
const auto extra_data = *reinterpret_cast<const GateMatePipExtraDataPOD *>(
|
||||
chip_pip_info(ctx->chip_info, pip).extra_data.get());
|
||||
if (extra_data.type == PipExtra::PIP_EXTRA_MUX && (extra_data.flags & MUX_VISIBLE)) {
|
||||
IdString name = IdString(extra_data.name);
|
||||
CfgLoc loc = getConfigLoc(pip.tile);
|
||||
std::string word = name.c_str(ctx);
|
||||
if (extra_data.flags & MUX_CONFIG) {
|
||||
cc.configs[loc.die].add_word(word, int_to_bitvector(extra_data.value, extra_data.bits));
|
||||
} else {
|
||||
int id = uarch->tile_extra_data(pip.tile)->prim_id;
|
||||
if (boost::starts_with(word, "IM."))
|
||||
boost::replace_all(word, "IM.", stringf("IM%d.", id));
|
||||
else if (boost::starts_with(word, "OM."))
|
||||
boost::replace_all(word, "OM.", stringf("OM%d.", id));
|
||||
else if (boost::starts_with(word, "CPE."))
|
||||
boost::replace_all(word, "CPE.", stringf("CPE%d.", id));
|
||||
else if (boost::starts_with(word, "IOES."))
|
||||
boost::replace_all(word, "IOES.", stringf("IOES%d.", id));
|
||||
else if (boost::starts_with(word, "LES."))
|
||||
boost::replace_all(word, "LES.", stringf("LES%d.", id));
|
||||
else if (boost::starts_with(word, "BES."))
|
||||
boost::replace_all(word, "BES.", stringf("BES%d.", id));
|
||||
else if (boost::starts_with(word, "RES."))
|
||||
boost::replace_all(word, "RES.", stringf("RES%d.", id));
|
||||
else if (boost::starts_with(word, "TES."))
|
||||
boost::replace_all(word, "TES.", stringf("TES%d.", id));
|
||||
cc.tiles[loc].add_word(word, int_to_bitvector(extra_data.value, extra_data.bits));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
out << cc;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void GateMateImpl::write_bitstream(const std::string &device, const std::string &filename)
|
||||
{
|
||||
std::ofstream out(filename);
|
||||
if (!out)
|
||||
log_error("failed to open file %s for writing (%s)\n", filename.c_str(), strerror(errno));
|
||||
|
||||
BitstreamBackend be(ctx, this, device, out);
|
||||
be.write_bitstream();
|
||||
}
|
||||
|
||||
bool GateMateImpl::read_bitstream(const std::string &device, const std::string &filename)
|
||||
{
|
||||
std::ifstream in(filename);
|
||||
if (!in)
|
||||
log_error("failed to open file %s for reading (%s)\n", filename.c_str(), strerror(errno));
|
||||
return false;
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
211
himbaechel/uarch/gatemate/ccf.cc
Normal file
211
himbaechel/uarch/gatemate/ccf.cc
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2024 The Project Peppercorn Authors.
|
||||
*
|
||||
* 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 <boost/algorithm/string.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
|
||||
#include "extra_data.h"
|
||||
#include "himbaechel_api.h"
|
||||
#include "log.h"
|
||||
#include "nextpnr.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "gatemate.h"
|
||||
|
||||
#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc"
|
||||
#include "himbaechel_constids.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
struct GateMateCCFReader
|
||||
{
|
||||
Context *ctx;
|
||||
std::istream ∈
|
||||
int lineno;
|
||||
dict<IdString, Property> defaults;
|
||||
|
||||
GateMateCCFReader(Context *ctx, std::istream &in) : ctx(ctx), in(in) {};
|
||||
|
||||
std::string strip_quotes(const std::string &str)
|
||||
{
|
||||
if (str.at(0) == '"') {
|
||||
if (str.back() != '"') {
|
||||
log_error("expected '\"' at end of string '%s' (on line %d).\n", str.c_str(), lineno);
|
||||
}
|
||||
return str.substr(1, str.size() - 2);
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
};
|
||||
|
||||
void parse_params(std::vector<std::string> ¶ms, bool is_default, dict<IdString, Property> *props)
|
||||
{
|
||||
for (auto param : params) {
|
||||
std::vector<std::string> expr;
|
||||
boost::split(expr, param, boost::is_any_of("="));
|
||||
if (expr.size() != 2)
|
||||
log_error("each parameter must be in form NAME=VALUE (on line %d)\n", lineno);
|
||||
|
||||
std::string name = expr.at(0);
|
||||
boost::algorithm::trim(name);
|
||||
boost::algorithm::to_upper(name);
|
||||
std::string value = strip_quotes(expr.at(1));
|
||||
boost::algorithm::trim(value);
|
||||
boost::algorithm::to_upper(value);
|
||||
|
||||
if (name == "LOC") {
|
||||
if (is_default)
|
||||
log_error("Value '%s' can not be defined for default GPIO in line %d.\n", name.c_str(), lineno);
|
||||
if (ctx->get_package_pin_bel(ctx->id(value)) == BelId())
|
||||
log_error("Unknown location '%s' used in line %d.\n", value.c_str(), lineno);
|
||||
props->emplace(id_LOC, Property(value));
|
||||
} else if (name == "SCHMITT_TRIGGER" || name == "PULLUP" || name == "PULLDOWN" || name == "KEEPER" ||
|
||||
name == "FF_IBF" || name == "FF_OBF" || name == "LVDS_BOOST" || name == "LVDS_RTERM") {
|
||||
if (value == "TRUE") {
|
||||
props->emplace(ctx->id(name.c_str()), Property(Property::State::S1));
|
||||
} else if (value == "FALSE") {
|
||||
props->emplace(ctx->id(name.c_str()), Property(Property::State::S0));
|
||||
} else
|
||||
log_error("Uknown value '%s' for parameter '%s' in line %d, must be TRUE or FALSE.\n",
|
||||
value.c_str(), name.c_str(), lineno);
|
||||
} else if (name == "SLEW") {
|
||||
if (value == "FAST" || value == "SLOW") {
|
||||
props->emplace(ctx->id(name.c_str()), Property(value));
|
||||
} else
|
||||
log_error("Uknown value '%s' for parameter '%s' in line %d, must be SLOW or FAST.\n", value.c_str(),
|
||||
name.c_str(), lineno);
|
||||
} else if (name == "DRIVE") {
|
||||
try {
|
||||
int drive = boost::lexical_cast<int>(value.c_str());
|
||||
if (drive == 3 || drive == 6 || drive == 9 || drive == 12) {
|
||||
props->emplace(ctx->id(name.c_str()), Property(drive, 2));
|
||||
} else
|
||||
log_error("Parameter '%s' must have value 3,6,9 or 12 in line %d.\n", name.c_str(), lineno);
|
||||
} catch (boost::bad_lexical_cast const &) {
|
||||
log_error("Parameter '%s' must be number in line %d.\n", name.c_str(), lineno);
|
||||
}
|
||||
} else if (name == "DELAY_IBF" || name == "DELAY_OBF") {
|
||||
try {
|
||||
int delay = boost::lexical_cast<int>(value.c_str());
|
||||
if (delay >= 0 && delay <= 15) {
|
||||
props->emplace(ctx->id(name.c_str()), Property(delay, 4));
|
||||
} else
|
||||
log_error("Parameter '%s' must have value from 0 to 15 in line %d.\n", name.c_str(), lineno);
|
||||
} catch (boost::bad_lexical_cast const &) {
|
||||
log_error("Parameter '%s' must be number in line %d.\n", name.c_str(), lineno);
|
||||
}
|
||||
} else {
|
||||
log_error("Uknown parameter name '%s' in line %d.\n", name.c_str(), lineno);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
log_info("Parsing CCF file..\n");
|
||||
|
||||
std::string line;
|
||||
std::string linebuf;
|
||||
defaults.clear();
|
||||
|
||||
auto isempty = [](const std::string &str) {
|
||||
return std::all_of(str.begin(), str.end(), [](char c) { return isblank(c) || c == '\r' || c == '\n'; });
|
||||
};
|
||||
lineno = 0;
|
||||
while (std::getline(in, line)) {
|
||||
++lineno;
|
||||
// Both // and # are considered start of comment
|
||||
size_t com_start = line.find("//");
|
||||
if (com_start != std::string::npos)
|
||||
line = line.substr(0, com_start);
|
||||
com_start = line.find('#');
|
||||
if (com_start != std::string::npos)
|
||||
line = line.substr(0, com_start);
|
||||
if (isempty(line))
|
||||
continue;
|
||||
linebuf += line;
|
||||
|
||||
size_t pos = linebuf.find(';');
|
||||
// Need to concatenate lines until there is closing ; sign
|
||||
while (pos != std::string::npos) {
|
||||
std::string content = linebuf.substr(0, pos);
|
||||
|
||||
std::vector<std::string> params;
|
||||
boost::split(params, content, boost::is_any_of("|"));
|
||||
std::string command = params.at(0);
|
||||
|
||||
std::stringstream ss(command);
|
||||
std::vector<std::string> words;
|
||||
std::string tmp;
|
||||
while (ss >> tmp)
|
||||
words.push_back(tmp);
|
||||
std::string type = words.at(0);
|
||||
|
||||
boost::algorithm::to_lower(type);
|
||||
if (type == "default_gpio") {
|
||||
if (words.size() != 1)
|
||||
log_error("line with default_GPIO should not contain only parameters (in line %d).\n", lineno);
|
||||
params.erase(params.begin());
|
||||
parse_params(params, true, &defaults);
|
||||
|
||||
} else if (type == "net" || type == "pin_in" || type == "pin_out" || type == "pin_inout") {
|
||||
if (words.size() < 3 || words.size() > 5)
|
||||
log_error("pin definition line not properly formed (in line %d).\n", lineno);
|
||||
std::string pin_name = strip_quotes(words.at(1));
|
||||
|
||||
// put back other words and use them as parameters
|
||||
std::stringstream ss;
|
||||
for (size_t i = 2; i < words.size(); i++)
|
||||
ss << words.at(i);
|
||||
params[0] = ss.str();
|
||||
|
||||
IdString cellname = ctx->id(pin_name);
|
||||
if (ctx->cells.count(cellname)) {
|
||||
CellInfo *cell = ctx->cells.at(cellname).get();
|
||||
for (auto p : defaults)
|
||||
cell->params[p.first] = p.second;
|
||||
parse_params(params, false, &cell->params);
|
||||
} else
|
||||
log_warning("Pad with name '%s' not found in netlist.\n", pin_name.c_str());
|
||||
} else {
|
||||
log_error("unknown type '%s' in line %d.\n", type.c_str(), lineno);
|
||||
}
|
||||
|
||||
linebuf = linebuf.substr(pos + 1);
|
||||
pos = linebuf.find(';');
|
||||
}
|
||||
}
|
||||
if (!isempty(linebuf))
|
||||
log_error("unexpected end of CCF file\n");
|
||||
}
|
||||
};
|
||||
|
||||
void GateMateImpl::parse_ccf(const std::string &filename)
|
||||
{
|
||||
std::ifstream in(filename);
|
||||
if (!in)
|
||||
log_error("failed to open CCF file '%s'\n", filename.c_str());
|
||||
GateMateCCFReader reader(ctx, in);
|
||||
reader.run();
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
246
himbaechel/uarch/gatemate/config.cc
Normal file
246
himbaechel/uarch/gatemate/config.cc
Normal file
@ -0,0 +1,246 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2024 The Project Peppercorn Authors.
|
||||
*
|
||||
* 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 "config.h"
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <iomanip>
|
||||
#include <set>
|
||||
#include "log.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
#define fmt(x) (static_cast<const std::ostringstream &>(std::ostringstream() << x).str())
|
||||
|
||||
inline std::string to_string(const std::vector<bool> &bv)
|
||||
{
|
||||
std::ostringstream os;
|
||||
for (auto bit : boost::adaptors::reverse(bv))
|
||||
os << (bit ? '1' : '0');
|
||||
return os.str();
|
||||
}
|
||||
|
||||
inline std::istream &operator>>(std::istream &in, std::vector<bool> &bv)
|
||||
{
|
||||
bv.clear();
|
||||
std::string s;
|
||||
in >> s;
|
||||
for (auto c : boost::adaptors::reverse(s)) {
|
||||
assert((c == '0') || (c == '1'));
|
||||
bv.push_back((c == '1'));
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
// Skip whitespace, optionally including newlines
|
||||
inline void skip_blank(std::istream &in, bool nl = false)
|
||||
{
|
||||
int c = in.peek();
|
||||
while (in && (((c == ' ') || (c == '\t')) || (nl && ((c == '\n') || (c == '\r'))))) {
|
||||
in.get();
|
||||
c = in.peek();
|
||||
}
|
||||
}
|
||||
// Return true if end of line (or file)
|
||||
inline bool skip_check_eol(std::istream &in)
|
||||
{
|
||||
skip_blank(in, false);
|
||||
if (!in)
|
||||
return false;
|
||||
int c = in.peek();
|
||||
// Comments count as end of line
|
||||
if (c == '#') {
|
||||
in.get();
|
||||
c = in.peek();
|
||||
while (in && c != EOF && c != '\n') {
|
||||
in.get();
|
||||
c = in.peek();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return (c == EOF || c == '\n');
|
||||
}
|
||||
|
||||
// Skip past blank lines and comments
|
||||
inline void skip(std::istream &in)
|
||||
{
|
||||
skip_blank(in, true);
|
||||
while (in && (in.peek() == '#')) {
|
||||
// Skip comment line
|
||||
skip_check_eol(in);
|
||||
skip_blank(in, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if at the end of a record (or file)
|
||||
inline bool skip_check_eor(std::istream &in)
|
||||
{
|
||||
skip(in);
|
||||
int c = in.peek();
|
||||
return (c == EOF || c == '.');
|
||||
}
|
||||
|
||||
// Return true if at the end of file
|
||||
inline bool skip_check_eof(std::istream &in)
|
||||
{
|
||||
skip(in);
|
||||
int c = in.peek();
|
||||
return (c == EOF);
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const ConfigWord &cw)
|
||||
{
|
||||
out << cw.name << " " << to_string(cw.value) << std::endl;
|
||||
return out;
|
||||
}
|
||||
|
||||
std::istream &operator>>(std::istream &in, ConfigWord &cw)
|
||||
{
|
||||
in >> cw.name;
|
||||
in >> cw.value;
|
||||
return in;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const TileConfig &tc)
|
||||
{
|
||||
for (const auto &cword : tc.cwords)
|
||||
out << cword;
|
||||
return out;
|
||||
}
|
||||
|
||||
std::istream &operator>>(std::istream &in, TileConfig &tc)
|
||||
{
|
||||
tc.cwords.clear();
|
||||
while (!skip_check_eor(in)) {
|
||||
ConfigWord w;
|
||||
in >> w;
|
||||
tc.cwords.push_back(w);
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
void TileConfig::add_word(const std::string &name, const std::vector<bool> &value) { cwords.push_back({name, value}); }
|
||||
|
||||
std::string TileConfig::to_string() const
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << *this;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
TileConfig TileConfig::from_string(const std::string &str)
|
||||
{
|
||||
std::stringstream ss(str);
|
||||
TileConfig tc;
|
||||
ss >> tc;
|
||||
return tc;
|
||||
}
|
||||
|
||||
bool TileConfig::empty() const { return cwords.empty(); }
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const ChipConfig &cc)
|
||||
{
|
||||
out << ".device " << cc.chip_name << std::endl << std::endl;
|
||||
for (const auto &config : cc.configs) {
|
||||
if (!config.second.empty()) {
|
||||
out << ".config " << config.first << " " << std::endl;
|
||||
out << config.second;
|
||||
out << std::endl;
|
||||
}
|
||||
}
|
||||
for (const auto &tile : cc.tiles) {
|
||||
if (!tile.second.empty()) {
|
||||
out << ".tile " << tile.first.die << " " << tile.first.x << " " << tile.first.y << std::endl;
|
||||
out << tile.second;
|
||||
out << std::endl;
|
||||
}
|
||||
}
|
||||
for (const auto &bram : cc.brams) {
|
||||
if (!bram.second.empty()) {
|
||||
out << ".bram " << bram.first.die << " " << bram.first.x << " " << bram.first.y << std::endl;
|
||||
out << bram.second;
|
||||
out << std::endl;
|
||||
}
|
||||
}
|
||||
for (const auto &bram : cc.bram_data) {
|
||||
if (!bram.second.empty()) {
|
||||
out << ".bram_init " << bram.first.die << " " << bram.first.x << " " << bram.first.y << std::endl;
|
||||
std::ios_base::fmtflags f(out.flags());
|
||||
for (size_t i = 0; i < bram.second.size(); i++) {
|
||||
out << std::setw(2) << std::setfill('0') << std::hex << (int)bram.second.at(i);
|
||||
if (i % 32 == 31)
|
||||
out << std::endl;
|
||||
else
|
||||
out << " ";
|
||||
}
|
||||
out.flags(f);
|
||||
out << std::endl;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::istream &operator>>(std::istream &in, ChipConfig &cc)
|
||||
{
|
||||
while (!skip_check_eof(in)) {
|
||||
std::string verb;
|
||||
in >> verb;
|
||||
if (verb == ".device") {
|
||||
in >> cc.chip_name;
|
||||
} else if (verb == ".config") {
|
||||
int die;
|
||||
in >> die;
|
||||
TileConfig tc;
|
||||
in >> tc;
|
||||
cc.configs.emplace(die, tc);
|
||||
} else if (verb == ".tile") {
|
||||
CfgLoc loc;
|
||||
in >> loc.die;
|
||||
in >> loc.x;
|
||||
in >> loc.y;
|
||||
TileConfig tc;
|
||||
in >> tc;
|
||||
cc.tiles.emplace(loc, tc);
|
||||
} else if (verb == ".bram") {
|
||||
CfgLoc loc;
|
||||
in >> loc.die;
|
||||
in >> loc.x;
|
||||
in >> loc.y;
|
||||
TileConfig tc;
|
||||
in >> tc;
|
||||
cc.brams.emplace(loc, tc);
|
||||
} else if (verb == ".bram_init") {
|
||||
CfgLoc loc;
|
||||
in >> loc.die;
|
||||
in >> loc.x;
|
||||
in >> loc.y;
|
||||
std::ios_base::fmtflags f(in.flags());
|
||||
while (!skip_check_eor(in)) {
|
||||
uint16_t value;
|
||||
in >> std::hex >> value;
|
||||
cc.bram_data[loc].push_back(value);
|
||||
}
|
||||
in.flags(f);
|
||||
} else {
|
||||
log_error("unrecognised config entry %s\n", verb.c_str());
|
||||
}
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
89
himbaechel/uarch/gatemate/config.h
Normal file
89
himbaechel/uarch/gatemate/config.h
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2024 The Project Peppercorn Authors.
|
||||
*
|
||||
* 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 GATEMATE_CONFIG_H
|
||||
#define GATEMATE_CONFIG_H
|
||||
|
||||
#include <map>
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
struct ConfigWord
|
||||
{
|
||||
std::string name;
|
||||
std::vector<bool> value;
|
||||
inline bool operator==(const ConfigWord &other) const { return other.name == name && other.value == value; }
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const ConfigWord &cw);
|
||||
|
||||
std::istream &operator>>(std::istream &in, ConfigWord &cw);
|
||||
|
||||
struct TileConfig
|
||||
{
|
||||
std::vector<ConfigWord> cwords;
|
||||
|
||||
void add_word(const std::string &name, const std::vector<bool> &value);
|
||||
|
||||
std::string to_string() const;
|
||||
static TileConfig from_string(const std::string &str);
|
||||
|
||||
bool empty() const;
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const TileConfig &tc);
|
||||
|
||||
std::istream &operator>>(std::istream &in, TileConfig &ce);
|
||||
|
||||
struct CfgLoc
|
||||
{
|
||||
int die;
|
||||
int x;
|
||||
int y;
|
||||
|
||||
inline bool operator==(const CfgLoc &other) const { return other.die == die && other.x == x && other.y == y; }
|
||||
inline bool operator!=(const CfgLoc &other) const { return other.die != die || x != other.x || y == other.y; }
|
||||
|
||||
inline bool operator<(const CfgLoc &other) const
|
||||
{
|
||||
return die < other.die ||
|
||||
((die == other.die && y < other.y) || (die == other.die && y == other.y && x < other.x));
|
||||
}
|
||||
};
|
||||
|
||||
class ChipConfig
|
||||
{
|
||||
public:
|
||||
std::string chip_name;
|
||||
std::string chip_package;
|
||||
std::map<CfgLoc, TileConfig> tiles;
|
||||
std::map<CfgLoc, TileConfig> brams;
|
||||
std::map<int, TileConfig> configs;
|
||||
|
||||
// Block RAM initialisation
|
||||
std::map<CfgLoc, std::vector<uint8_t>> bram_data;
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const ChipConfig &cc);
|
||||
std::istream &operator>>(std::istream &in, ChipConfig &cc);
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
167
himbaechel/uarch/gatemate/constids.inc
Normal file
167
himbaechel/uarch/gatemate/constids.inc
Normal file
@ -0,0 +1,167 @@
|
||||
X(CPE)
|
||||
X(CPE_BIG)
|
||||
X(CPE_SML)
|
||||
|
||||
X(RAM_I1)
|
||||
X(RAM_I2)
|
||||
X(IN1)
|
||||
X(IN2)
|
||||
X(IN3)
|
||||
X(IN4)
|
||||
X(IN5)
|
||||
X(IN6)
|
||||
X(IN7)
|
||||
X(IN8)
|
||||
X(CLK)
|
||||
X(EN)
|
||||
X(SR)
|
||||
X(CINX)
|
||||
X(PINX)
|
||||
X(CINY1)
|
||||
X(PINY1)
|
||||
X(CINY2)
|
||||
X(PINY2)
|
||||
X(OUT1)
|
||||
X(OUT2)
|
||||
X(RAM_O1)
|
||||
X(RAM_O2)
|
||||
X(COUTX)
|
||||
X(POUTX)
|
||||
X(COUTY1)
|
||||
X(POUTY1)
|
||||
X(COUTY2)
|
||||
X(POUTY2)
|
||||
|
||||
X(GPIO)
|
||||
|
||||
X(CC_IBUF)
|
||||
X(CC_OBUF)
|
||||
X(CC_TOBUF)
|
||||
X(CC_IOBUF)
|
||||
X(CC_LVDS_IBUF)
|
||||
X(CC_LVDS_OBUF)
|
||||
X(CC_LVDS_TOBUF)
|
||||
X(CC_LVDS_IOBUF)
|
||||
X(CC_IDDR)
|
||||
X(CC_ODDR)
|
||||
X(I)
|
||||
X(O)
|
||||
X(IO)
|
||||
X(DI)
|
||||
X(DO)
|
||||
X(OE)
|
||||
X(Y)
|
||||
X(A)
|
||||
X(T)
|
||||
X(I_P)
|
||||
X(I_N)
|
||||
X(O_P)
|
||||
X(O_N)
|
||||
X(IO_P)
|
||||
X(IO_N)
|
||||
|
||||
X(OUT3)
|
||||
X(OUT4)
|
||||
|
||||
X(LOC)
|
||||
X(PIN_NAME)
|
||||
X(PIN_NAME_P)
|
||||
X(PIN_NAME_N)
|
||||
X(V_IO)
|
||||
X(PULLUP)
|
||||
X(PULLDOWN)
|
||||
X(KEEPER)
|
||||
X(SCHMITT_TRIGGER)
|
||||
X(DELAY_IBF)
|
||||
X(FF_IBF)
|
||||
X(DRIVE)
|
||||
X(SLEW)
|
||||
X(DELAY_OBF)
|
||||
X(FF_OBF)
|
||||
X(LVDS_RTERM)
|
||||
X(LVDS_BOOST)
|
||||
|
||||
X(OE_ENABLE)
|
||||
X(OUT_SIGNAL)
|
||||
X(INPUT_ENABLE)
|
||||
X(OUT23_14_SEL)
|
||||
X(LVDS_EN)
|
||||
X(LVDS_IE)
|
||||
|
||||
X(CC_L2T4)
|
||||
X(CC_L2T5)
|
||||
X(CC_LUT1)
|
||||
X(CC_LUT2)
|
||||
X(I0)
|
||||
X(I1)
|
||||
X(I2)
|
||||
X(I3)
|
||||
X(I4)
|
||||
X(INIT)
|
||||
X(INIT_L00)
|
||||
X(INIT_L01)
|
||||
X(INIT_L02)
|
||||
X(INIT_L03)
|
||||
X(INIT_L10)
|
||||
X(INIT_L11)
|
||||
X(INIT_L20)
|
||||
X(INIT_L30)
|
||||
X(O1)
|
||||
X(O2)
|
||||
|
||||
X(CC_DFF)
|
||||
X(CPE_DFF)
|
||||
X(Q)
|
||||
X(D)
|
||||
X(R)
|
||||
X(S)
|
||||
X(FF_INIT)
|
||||
X(2D_IN)
|
||||
X(SR_VAL)
|
||||
X(SR_INV)
|
||||
X(EN_INV)
|
||||
X(CLK_INV)
|
||||
X(EN_SR)
|
||||
|
||||
X(CC_BUFG)
|
||||
X(BUFG)
|
||||
|
||||
X(CC_MX2)
|
||||
X(CC_MX4)
|
||||
X(FUNCTION)
|
||||
X(D0)
|
||||
X(D1)
|
||||
X(D2)
|
||||
X(D3)
|
||||
X(S0)
|
||||
X(S1)
|
||||
|
||||
X(CC_PLL)
|
||||
X(CC_PLL_ADV)
|
||||
X(PLL)
|
||||
X(CLK_REF)
|
||||
X(USR_CLK_REF)
|
||||
X(USR_SEL_A_B)
|
||||
X(CLK_FEEDBACK)
|
||||
X(USR_LOCKED_STDY_RST)
|
||||
X(CLK0)
|
||||
X(CLK90)
|
||||
X(CLK180)
|
||||
X(CLK270)
|
||||
X(CLK_REF_OUT)
|
||||
X(USR_PLL_LOCKED_STDY)
|
||||
X(USR_PLL_LOCKED)
|
||||
X(REF_CLK)
|
||||
X(OUT_CLK)
|
||||
X(PERF_MD)
|
||||
X(LOCK_REQ)
|
||||
X(CLK270_DOUB)
|
||||
X(CLK180_DOUB)
|
||||
X(LOW_JITTER)
|
||||
X(CI_FILTER_CONST)
|
||||
X(CP_FILTER_CONST)
|
||||
X(PLL_CFG_A)
|
||||
X(PLL_CFG_B)
|
||||
|
||||
X(CC_USR_RSTN)
|
||||
X(USR_RSTN)
|
58
himbaechel/uarch/gatemate/extra_data.h
Normal file
58
himbaechel/uarch/gatemate/extra_data.h
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2024 The Project Peppercorn Authors.
|
||||
*
|
||||
* 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 GATEMATE_EXTRA_DATA_H
|
||||
#define GATEMATE_EXTRA_DATA_H
|
||||
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
NPNR_PACKED_STRUCT(struct GateMateTileExtraDataPOD {
|
||||
uint8_t die;
|
||||
uint8_t bit_x;
|
||||
uint8_t bit_y;
|
||||
uint8_t prim_id;
|
||||
});
|
||||
|
||||
NPNR_PACKED_STRUCT(struct GateMatePipExtraDataPOD {
|
||||
int32_t name;
|
||||
uint8_t bits;
|
||||
uint8_t value;
|
||||
uint8_t flags;
|
||||
uint8_t type;
|
||||
});
|
||||
|
||||
enum MuxFlags
|
||||
{
|
||||
MUX_INVERT = 1,
|
||||
MUX_VISIBLE = 2,
|
||||
MUX_CONFIG = 4,
|
||||
MUX_CPE_INV = 8,
|
||||
};
|
||||
|
||||
enum PipExtra
|
||||
{
|
||||
PIP_EXTRA_MUX = 1,
|
||||
PIP_EXTRA_CPE = 2,
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
288
himbaechel/uarch/gatemate/gatemate.cc
Normal file
288
himbaechel/uarch/gatemate/gatemate.cc
Normal file
@ -0,0 +1,288 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2024 The Project Peppercorn Authors.
|
||||
*
|
||||
* 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 "gatemate.h"
|
||||
#include "design_utils.h"
|
||||
|
||||
#define GEN_INIT_CONSTIDS
|
||||
#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc"
|
||||
|
||||
#include "himbaechel_constids.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
GateMateImpl::~GateMateImpl() {};
|
||||
|
||||
void GateMateImpl::init_database(Arch *arch)
|
||||
{
|
||||
const ArchArgs &args = arch->args;
|
||||
init_uarch_constids(arch);
|
||||
arch->load_chipdb(stringf("gatemate/chipdb-%s.bin", args.device.c_str()));
|
||||
arch->set_package("FBGA324");
|
||||
arch->set_speed_grade("DEFAULT");
|
||||
}
|
||||
|
||||
void GateMateImpl::init(Context *ctx) { HimbaechelAPI::init(ctx); }
|
||||
|
||||
delay_t GateMateImpl::estimateDelay(WireId src, WireId dst) const
|
||||
{
|
||||
int sx, sy, dx, dy;
|
||||
tile_xy(ctx->chip_info, src.tile, sx, sy);
|
||||
tile_xy(ctx->chip_info, dst.tile, dx, dy);
|
||||
|
||||
return 100 * (std::abs(dx - sx) / 4 + std::abs(dy - sy) / 4 + 2);
|
||||
}
|
||||
|
||||
bool GateMateImpl::isBelLocationValid(BelId bel, bool explain_invalid) const
|
||||
{
|
||||
CellInfo *cell = ctx->getBoundBelCell(bel);
|
||||
if (cell == nullptr) {
|
||||
return true;
|
||||
}
|
||||
if (ctx->getBelType(bel) == id_CPE) {
|
||||
Loc loc = ctx->getBelLocation(bel);
|
||||
int x = loc.x - 2;
|
||||
int y = loc.y - 2;
|
||||
if (x < 2 || x > 167)
|
||||
return false;
|
||||
if (y < 2 || y > 127)
|
||||
return false;
|
||||
if (x == 1 && y == 66)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void updateLUT(Context *ctx, CellInfo *cell, IdString port, IdString init)
|
||||
{
|
||||
if (cell->params.count(init) == 0) return;
|
||||
unsigned init_val = int_or_default(cell->params, init);
|
||||
WireId pin_wire = ctx->getBelPinWire(cell->bel, port);
|
||||
for (PipId pip : ctx->getPipsUphill(pin_wire)) {
|
||||
if (!ctx->getBoundPipNet(pip))
|
||||
continue;
|
||||
const auto extra_data = *reinterpret_cast<const GateMatePipExtraDataPOD *>(
|
||||
chip_pip_info(ctx->chip_info, pip).extra_data.get());
|
||||
if (!extra_data.name)
|
||||
continue;
|
||||
if (extra_data.type == PipExtra::PIP_EXTRA_MUX && (extra_data.flags & MUX_CPE_INV)) {
|
||||
if (port.in(id_IN1,id_IN3,id_IN5,id_IN7))
|
||||
init_val = (init_val & 0b1010) >> 1 | (init_val & 0b0101) << 1;
|
||||
else
|
||||
init_val = (init_val & 0b0011) << 2 | (init_val & 0b1100) >> 2;
|
||||
cell->params[init] = Property(init_val, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateINV(Context *ctx, CellInfo *cell, IdString port)
|
||||
{
|
||||
if (cell->params.count(port) == 0) return;
|
||||
unsigned init_val = int_or_default(cell->params, port);
|
||||
WireId pin_wire = ctx->getBelPinWire(cell->bel, port);
|
||||
for (PipId pip : ctx->getPipsUphill(pin_wire)) {
|
||||
if (!ctx->getBoundPipNet(pip))
|
||||
continue;
|
||||
const auto extra_data = *reinterpret_cast<const GateMatePipExtraDataPOD *>(
|
||||
chip_pip_info(ctx->chip_info, pip).extra_data.get());
|
||||
if (!extra_data.name)
|
||||
continue;
|
||||
if (extra_data.type == PipExtra::PIP_EXTRA_MUX && (extra_data.flags & MUX_CPE_INV)) {
|
||||
cell->params[port] = Property(3 - init_val, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateSR_INV(Context *ctx, CellInfo *cell, IdString port, IdString param)
|
||||
{
|
||||
if (cell->params.count(param) == 0) return;
|
||||
unsigned init_val = int_or_default(cell->params, param);
|
||||
WireId pin_wire = ctx->getBelPinWire(cell->bel, port);
|
||||
for (PipId pip : ctx->getPipsUphill(pin_wire)) {
|
||||
if (!ctx->getBoundPipNet(pip))
|
||||
continue;
|
||||
const auto extra_data = *reinterpret_cast<const GateMatePipExtraDataPOD *>(
|
||||
chip_pip_info(ctx->chip_info, pip).extra_data.get());
|
||||
if (!extra_data.name)
|
||||
continue;
|
||||
if (extra_data.type == PipExtra::PIP_EXTRA_MUX && (extra_data.flags & MUX_CPE_INV)) {
|
||||
cell->params[param] = Property(3 - init_val, 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateMUX_INV(Context *ctx, CellInfo *cell, IdString port, IdString param, int bit)
|
||||
{
|
||||
unsigned init_val = int_or_default(cell->params, param);
|
||||
WireId pin_wire = ctx->getBelPinWire(cell->bel, port);
|
||||
for (PipId pip : ctx->getPipsUphill(pin_wire)) {
|
||||
if (!ctx->getBoundPipNet(pip))
|
||||
continue;
|
||||
const auto extra_data = *reinterpret_cast<const GateMatePipExtraDataPOD *>(
|
||||
chip_pip_info(ctx->chip_info, pip).extra_data.get());
|
||||
if (!extra_data.name)
|
||||
continue;
|
||||
if (extra_data.type == PipExtra::PIP_EXTRA_MUX && (extra_data.flags & MUX_CPE_INV)) {
|
||||
int old = (init_val >> bit) & 1;
|
||||
int val = (init_val & (~(1 << bit) & 0xf)) | ((!old) << bit);
|
||||
cell->params[param] = Property(val, 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GateMateImpl::postRoute()
|
||||
{
|
||||
ctx->assignArchInfo();
|
||||
log_break();
|
||||
log_info("Resources spent on routing:\n");
|
||||
for (auto &net : ctx->nets) {
|
||||
NetInfo *ni = net.second.get();
|
||||
for (auto &w : ni->wires) {
|
||||
if (w.second.pip != PipId()) {
|
||||
const auto extra_data = *reinterpret_cast<const GateMatePipExtraDataPOD *>(
|
||||
chip_pip_info(ctx->chip_info, w.second.pip).extra_data.get());
|
||||
if (!extra_data.name)
|
||||
continue;
|
||||
if (extra_data.type == PipExtra::PIP_EXTRA_CPE) {
|
||||
IdStringList id = ctx->getPipName(w.second.pip);
|
||||
BelId bel = ctx->getBelByName(IdStringList::concat(id[0], id_CPE));
|
||||
if (!ctx->getBoundBelCell(bel)) {
|
||||
CellInfo *cell = ctx->createCell(ctx->id(ctx->nameOfBel(bel)), id_CPE);
|
||||
ctx->bindBel(bel, cell, PlaceStrength::STRENGTH_FIXED);
|
||||
if (IdString(extra_data.name) == id_RAM_O2) {
|
||||
// Propagate IN1 to O2 and RAM_O2
|
||||
cell->params[id_INIT_L00] = Property(0b1010, 4);
|
||||
cell->params[id_INIT_L10] = Property(0b1010, 4);
|
||||
cell->params[id_O2] = Property(0b11, 2);
|
||||
cell->params[id_RAM_O2] = Property(1, 1);
|
||||
} else if (IdString(extra_data.name) == id_RAM_O1) {
|
||||
// Propagate IN1 to O1 and RAM_O1
|
||||
cell->params[id_INIT_L00] = Property(0b1010, 4);
|
||||
cell->params[id_INIT_L10] = Property(0b1010, 4);
|
||||
cell->params[id_INIT_L20] = Property(0b1010, 4);
|
||||
cell->params[id_O1] = Property(0b11, 2);
|
||||
cell->params[id_RAM_O1] = Property(1, 1);
|
||||
} else if (IdString(extra_data.name) == id_RAM_I1) {
|
||||
cell->params[id_RAM_I1] = Property(1, 1);
|
||||
} else if (IdString(extra_data.name) == id_RAM_I2) {
|
||||
cell->params[id_RAM_I2] = Property(1, 1);
|
||||
} else {
|
||||
log_error("Issue adding pass trough signal for %s.\n",IdString(extra_data.name).c_str(ctx));
|
||||
}
|
||||
} else
|
||||
log_error("Issue adding pass trough signal.\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (auto &cell : ctx->cells) {
|
||||
if (cell.second->type == id_CPE) {
|
||||
// if LUT part used
|
||||
uint8_t func = int_or_default(cell.second->params, id_FUNCTION, 0);
|
||||
if (func != 4) {
|
||||
updateLUT(ctx, cell.second.get(), id_IN1, id_INIT_L00);
|
||||
updateLUT(ctx, cell.second.get(), id_IN2, id_INIT_L00);
|
||||
updateLUT(ctx, cell.second.get(), id_IN3, id_INIT_L01);
|
||||
updateLUT(ctx, cell.second.get(), id_IN4, id_INIT_L01);
|
||||
} else {
|
||||
updateMUX_INV(ctx, cell.second.get(), id_IN1, id_INIT_L11, 0);
|
||||
updateMUX_INV(ctx, cell.second.get(), id_IN2, id_INIT_L11, 1);
|
||||
updateMUX_INV(ctx, cell.second.get(), id_IN3, id_INIT_L11, 2);
|
||||
updateMUX_INV(ctx, cell.second.get(), id_IN4, id_INIT_L11, 3);
|
||||
}
|
||||
|
||||
updateLUT(ctx, cell.second.get(), id_IN5, id_INIT_L02);
|
||||
updateLUT(ctx, cell.second.get(), id_IN6, id_INIT_L02);
|
||||
updateLUT(ctx, cell.second.get(), id_IN7, id_INIT_L03);
|
||||
updateLUT(ctx, cell.second.get(), id_IN8, id_INIT_L03);
|
||||
updateINV(ctx, cell.second.get(), id_CLK);
|
||||
updateINV(ctx, cell.second.get(), id_EN);
|
||||
bool set = int_or_default(cell.second->params, id_EN_SR, 0) == 1;
|
||||
if (set)
|
||||
updateSR_INV(ctx, cell.second.get(), id_SR, id_S);
|
||||
else
|
||||
updateSR_INV(ctx, cell.second.get(), id_SR, id_R);
|
||||
}
|
||||
}
|
||||
|
||||
print_utilisation(ctx);
|
||||
|
||||
const ArchArgs &args = ctx->args;
|
||||
if (args.options.count("out")) {
|
||||
write_bitstream(args.device, args.options.at("out"));
|
||||
}
|
||||
}
|
||||
|
||||
void GateMateImpl::postPlace()
|
||||
{
|
||||
ctx->assignArchInfo();
|
||||
}
|
||||
|
||||
|
||||
void GateMateImpl::setupArchContext()
|
||||
{
|
||||
const ArchArgs &args = ctx->args;
|
||||
if (args.options.count("read")) {
|
||||
if (!read_bitstream(args.device, args.options.at("read")))
|
||||
log_error("Loading bitstream failed.\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Bel bucket functions
|
||||
IdString GateMateImpl::getBelBucketForCellType(IdString cell_type) const
|
||||
{
|
||||
if (cell_type.in(id_CC_IBUF, id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF, id_CC_LVDS_IBUF, id_CC_LVDS_TOBUF,
|
||||
id_CC_LVDS_OBUF, id_CC_LVDS_IOBUF))
|
||||
return id_GPIO;
|
||||
else
|
||||
return cell_type;
|
||||
}
|
||||
|
||||
bool GateMateImpl::isValidBelForCellType(IdString cell_type, BelId bel) const
|
||||
{
|
||||
IdString bel_type = ctx->getBelType(bel);
|
||||
if (bel_type == id_GPIO)
|
||||
return cell_type.in(id_CC_IBUF, id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF, id_CC_LVDS_IBUF, id_CC_LVDS_TOBUF,
|
||||
id_CC_LVDS_OBUF, id_CC_LVDS_IOBUF);
|
||||
else
|
||||
return (bel_type == cell_type);
|
||||
}
|
||||
|
||||
const GateMateTileExtraDataPOD *GateMateImpl::tile_extra_data(int tile) const
|
||||
{
|
||||
return reinterpret_cast<const GateMateTileExtraDataPOD *>(ctx->chip_info->tile_insts[tile].extra_data.get());
|
||||
}
|
||||
|
||||
struct GateMateArch : HimbaechelArch
|
||||
{
|
||||
GateMateArch() : HimbaechelArch("gatemate") {};
|
||||
bool match_device(const std::string &device) override
|
||||
{
|
||||
return device.size() > 6 && device.substr(0, 6) == "CCGM1A";
|
||||
}
|
||||
std::unique_ptr<HimbaechelAPI> create(const std::string &device, const dict<std::string, std::string> &args)
|
||||
{
|
||||
return std::make_unique<GateMateImpl>();
|
||||
}
|
||||
} gateMateArch;
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
72
himbaechel/uarch/gatemate/gatemate.h
Normal file
72
himbaechel/uarch/gatemate/gatemate.h
Normal file
@ -0,0 +1,72 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2024 The Project Peppercorn Authors.
|
||||
*
|
||||
* 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 HIMBAECHEL_GATEMATE_H
|
||||
#define HIMBAECHEL_GATEMATE_H
|
||||
|
||||
#include "extra_data.h"
|
||||
#include "himbaechel_api.h"
|
||||
#include "log.h"
|
||||
#include "nextpnr.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "himbaechel_helpers.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
struct GateMateImpl : HimbaechelAPI
|
||||
{
|
||||
~GateMateImpl();
|
||||
void init_database(Arch *arch) override;
|
||||
|
||||
void init(Context *ctx) override;
|
||||
void setupArchContext() override;
|
||||
|
||||
void pack() override;
|
||||
|
||||
void postPlace() override;
|
||||
void postRoute() override;
|
||||
|
||||
bool isBelLocationValid(BelId bel, bool explain_invalid = false) const override;
|
||||
delay_t estimateDelay(WireId src, WireId dst) const override;
|
||||
|
||||
void drawBel(std::vector<GraphicElement> &g, GraphicElement::style_t style, IdString bel_type, Loc loc) override;
|
||||
|
||||
void write_bitstream(const std::string &device, const std::string &filename);
|
||||
bool read_bitstream(const std::string &device, const std::string &filename);
|
||||
|
||||
bool checkPipAvail(PipId pip) const override { return blocked_pips.count(pip) == 0; }
|
||||
bool checkPipAvailForNet(PipId pip, const NetInfo *net) const override { return checkPipAvail(pip); };
|
||||
|
||||
void parse_ccf(const std::string &filename);
|
||||
|
||||
IdString getBelBucketForCellType(IdString cell_type) const;
|
||||
bool isValidBelForCellType(IdString cell_type, BelId bel) const;
|
||||
|
||||
bool isPipInverting(PipId pip) const override {
|
||||
const auto &extra_data = *reinterpret_cast<const GateMatePipExtraDataPOD*>(chip_pip_info(ctx->chip_info, pip).extra_data.get());
|
||||
return extra_data.type == PipExtra::PIP_EXTRA_MUX && (extra_data.flags & MUX_INVERT);
|
||||
}
|
||||
const GateMateTileExtraDataPOD *tile_extra_data(int tile) const;
|
||||
|
||||
pool<PipId> blocked_pips;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
#endif
|
260
himbaechel/uarch/gatemate/gen/arch_gen.py
Normal file
260
himbaechel/uarch/gatemate/gen/arch_gen.py
Normal file
@ -0,0 +1,260 @@
|
||||
#
|
||||
# nextpnr -- Next Generation Place and Route
|
||||
#
|
||||
# Copyright (C) 2024 The Project Peppercorn Authors.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
import os
|
||||
from os import path
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
sys.path.append(path.join(path.dirname(__file__), "../../.."))
|
||||
from himbaechel_dbgen.chip import *
|
||||
|
||||
PIP_EXTRA_MUX = 1
|
||||
PIP_EXTRA_CPE = 2
|
||||
|
||||
MUX_INVERT = 1
|
||||
MUX_VISIBLE = 2
|
||||
MUX_CONFIG = 4
|
||||
MUX_CPE_INV = 8
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--lib", help="Project Peppercorn python database script path", type=str, required=True)
|
||||
parser.add_argument("--device", help="name of device to export", type=str, required=True)
|
||||
parser.add_argument("--bba", help="bba file to write", type=str, required=True)
|
||||
args = parser.parse_args()
|
||||
|
||||
sys.path.append(os.path.expanduser(args.lib))
|
||||
sys.path += args.lib
|
||||
import chip
|
||||
import die
|
||||
|
||||
@dataclass
|
||||
class TileExtraData(BBAStruct):
|
||||
die : int = 0
|
||||
bit_x: int = 0
|
||||
bit_y: int = 0
|
||||
prim_id : int = 0
|
||||
|
||||
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||
pass
|
||||
def serialise(self, context: str, bba: BBAWriter):
|
||||
bba.u8(self.die)
|
||||
bba.u8(self.bit_x)
|
||||
bba.u8(self.bit_y)
|
||||
bba.u8(self.prim_id)
|
||||
|
||||
@dataclass
|
||||
class PipExtraData(BBAStruct):
|
||||
pip_type: int
|
||||
name: IdString
|
||||
bits: int = 0
|
||||
value: int = 0
|
||||
invert: int = 0
|
||||
|
||||
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||
pass
|
||||
def serialise(self, context: str, bba: BBAWriter):
|
||||
bba.u32(self.name.index)
|
||||
bba.u8(self.bits)
|
||||
bba.u8(self.value)
|
||||
bba.u8(self.invert)
|
||||
bba.u8(self.pip_type)
|
||||
|
||||
def set_timings(ch):
|
||||
speed = "DEFAULT"
|
||||
tmg = ch.set_speed_grades([speed])
|
||||
|
||||
lut = ch.timing.add_cell_variant(speed, "CPE")
|
||||
lut.add_comb_arc("IN1", "OUT1", TimingValue(455, 459))
|
||||
lut.add_comb_arc("IN2", "OUT1", TimingValue(450, 457))
|
||||
lut.add_comb_arc("IN3", "OUT1", TimingValue(427, 430))
|
||||
lut.add_comb_arc("IN4", "OUT1", TimingValue(423, 424))
|
||||
lut.add_comb_arc("IN5", "OUT1", TimingValue(416, 418))
|
||||
lut.add_comb_arc("IN6", "OUT1", TimingValue(413, 422))
|
||||
lut.add_comb_arc("IN7", "OUT1", TimingValue(372, 374))
|
||||
lut.add_comb_arc("IN8", "OUT1", TimingValue(275, 385))
|
||||
|
||||
lut.add_comb_arc("IN1", "OUT2", TimingValue(479, 484))
|
||||
lut.add_comb_arc("IN2", "OUT2", TimingValue(471, 488))
|
||||
lut.add_comb_arc("IN3", "OUT2", TimingValue(446, 449))
|
||||
lut.add_comb_arc("IN4", "OUT2", TimingValue(443, 453))
|
||||
|
||||
dff = ch.timing.add_cell_variant(speed, "CPE_DFF")
|
||||
dff.add_setup_hold("CLK", "IN1", ClockEdge.RISING, TimingValue(60), TimingValue(50))
|
||||
dff.add_clock_out("CLK", "OUT1", ClockEdge.RISING, TimingValue(60))
|
||||
dff.add_clock_out("CLK", "OUT2", ClockEdge.RISING, TimingValue(60))
|
||||
|
||||
def main():
|
||||
# Range needs to be +1, but we are adding +2 more to coordinates, since
|
||||
# they are starting from -2 instead of zero required for nextpnr
|
||||
dev = chip.get_device(args.device)
|
||||
ch = Chip("gatemate", args.device, dev.max_col() + 3, dev.max_row() + 3)
|
||||
# Init constant ids
|
||||
ch.strs.read_constids(path.join(path.dirname(__file__), "..", "constids.inc"))
|
||||
ch.read_gfxids(path.join(path.dirname(__file__), "..", "gfxids.inc"))
|
||||
|
||||
for type_name in die.get_tile_type_list():
|
||||
tt = ch.create_tile_type(type_name)
|
||||
for group in sorted(die.get_groups_for_type(type_name)):
|
||||
tt.create_group(group.name, group.type)
|
||||
for wire in sorted(die.get_endpoints_for_type(type_name)):
|
||||
tt.create_wire(wire.name, wire.type)
|
||||
for prim in sorted(die.get_primitives_for_type(type_name)):
|
||||
bel = tt.create_bel(prim.name, prim.type, prim.z)
|
||||
for pin in sorted(die.get_primitive_pins(prim.type)):
|
||||
tt.add_bel_pin(bel, pin.name, die.get_pin_connection_name(prim,pin), pin.dir)
|
||||
for mux in sorted(die.get_mux_connections_for_type(type_name)):
|
||||
pp = tt.create_pip(mux.src, mux.dst)
|
||||
mux_flags = MUX_INVERT if mux.invert else 0
|
||||
mux_flags |= MUX_VISIBLE if mux.visible else 0
|
||||
mux_flags |= MUX_CONFIG if mux.config else 0
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_MUX, ch.strs.id(mux.name), mux.bits, mux.value, mux_flags)
|
||||
if "CPE" in type_name:
|
||||
pp = tt.create_pip("CPE.IN1", "CPE.RAM_O2")
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_CPE,ch.strs.id("RAM_O2"))
|
||||
pp = tt.create_pip("CPE.IN1", "CPE.RAM_O1")
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_CPE,ch.strs.id("RAM_O1"))
|
||||
pp = tt.create_pip("CPE.RAM_I1", "CPE.OUT1")
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_CPE,ch.strs.id("RAM_I1"))
|
||||
pp = tt.create_pip("CPE.RAM_I2", "CPE.OUT2")
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_CPE,ch.strs.id("RAM_I2"))
|
||||
#pp = tt.create_pip("CPE.CINX", "CPE.COUTX")
|
||||
#pp.extra_data = PipExtraData(PIP_EXTRA_CPE,ch.strs.id("COUTX"))
|
||||
#pp = tt.create_pip("CPE.PINX", "CPE.POUTX")
|
||||
#pp.extra_data = PipExtraData(PIP_EXTRA_CPE,ch.strs.id("POUTX"))
|
||||
#pp = tt.create_pip("CPE.CINY1", "CPE.COUTY1")
|
||||
#pp.extra_data = PipExtraData(PIP_EXTRA_CPE,ch.strs.id("COUTY1"))
|
||||
#pp = tt.create_pip("CPE.PINY1", "CPE.POUTY1")
|
||||
#pp.extra_data = PipExtraData(PIP_EXTRA_CPE,ch.strs.id("POUTY1"))
|
||||
#pp = tt.create_pip("CPE.CINY2", "CPE.COUTY2")
|
||||
#pp.extra_data = PipExtraData(PIP_EXTRA_CPE,ch.strs.id("COUTY2"))
|
||||
#pp = tt.create_pip("CPE.PINY2", "CPE.POUTY2")
|
||||
#pp.extra_data = PipExtraData(PIP_EXTRA_CPE,ch.strs.id("POUTY2"))
|
||||
for i in range(1,9):
|
||||
tt.create_wire(f"CPE.V_IN{i}", "CPE_VIRTUAL_WIRE")
|
||||
pp = tt.create_pip(f"CPE.V_IN{i}", f"CPE.IN{i}")
|
||||
pp = tt.create_pip(f"CPE.V_IN{i}", f"CPE.IN{i}")
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_MUX, ch.strs.id(f"CPE.IN{i}_INV"), 1, i, MUX_CPE_INV | MUX_INVERT)
|
||||
tt.create_wire("CPE.V_CLK", "CPE_VIRTUAL_WIRE")
|
||||
pp = tt.create_pip("CPE.V_CLK", "CPE.CLK")
|
||||
pp = tt.create_pip("CPE.V_CLK", "CPE.CLK")
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_MUX, ch.strs.id("CPE.CLK_INV"), 1, 1, MUX_CPE_INV| MUX_INVERT)
|
||||
tt.create_wire("CPE.V_EN", "CPE_VIRTUAL_WIRE")
|
||||
pp = tt.create_pip("CPE.V_EN", "CPE.EN")
|
||||
pp = tt.create_pip("CPE.V_EN", "CPE.EN")
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_MUX, ch.strs.id("CPE.EN_INV"), 1, 1, MUX_CPE_INV| MUX_INVERT)
|
||||
tt.create_wire("CPE.V_SR", "CPE_VIRTUAL_WIRE")
|
||||
pp = tt.create_pip("CPE.V_SR", "CPE.SR")
|
||||
pp = tt.create_pip("CPE.V_SR", "CPE.SR")
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_MUX, ch.strs.id("CPE.SR_INV"), 1, 1, MUX_CPE_INV| MUX_INVERT)
|
||||
if "GPIO" in type_name:
|
||||
tt.create_wire("GPIO.OUT_D1", "WIRE_INTERNAL")
|
||||
tt.create_wire("GPIO.OUT_D2", "WIRE_INTERNAL")
|
||||
#tt.create_wire("GPIO.OUT_Q1", "WIRE_INTERNAL")
|
||||
#tt.create_wire("GPIO.OUT_Q2", "WIRE_INTERNAL")
|
||||
#tt.create_wire("GPIO.OUT_CLK","WIRE_INTERNAL")
|
||||
#tt.create_wire("GPIO.CLK_INT","WIRE_INTERNAL")
|
||||
|
||||
pp = tt.create_pip("GPIO.OUT1", "GPIO.OUT_D1")
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.OUT1_4"), 1, 0, MUX_VISIBLE)
|
||||
pp = tt.create_pip("GPIO.OUT4", "GPIO.OUT_D1")
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.OUT1_4"), 1, 1, MUX_VISIBLE)
|
||||
|
||||
pp = tt.create_pip("GPIO.OUT2", "GPIO.OUT_D2")
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.OUT2_3"), 1, 0, MUX_VISIBLE)
|
||||
pp = tt.create_pip("GPIO.OUT3", "GPIO.OUT_D2")
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.OUT2_3"), 1, 1, MUX_VISIBLE)
|
||||
|
||||
pp = tt.create_pip("GPIO.OUT_D1","GPIO.DO")
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.OUT23_14_SEL"), 1, 0, MUX_VISIBLE)
|
||||
pp = tt.create_pip("GPIO.OUT_D2","GPIO.DO")
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.OUT23_14_SEL"), 1, 1, MUX_VISIBLE)
|
||||
|
||||
|
||||
pp = tt.create_pip("GPIO.OUT2","GPIO.OE")
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.OE_SIGNAL"), 2, 1, MUX_VISIBLE)
|
||||
pp = tt.create_pip("GPIO.OUT3","GPIO.OE")
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.OE_SIGNAL"), 2, 2, MUX_VISIBLE)
|
||||
pp = tt.create_pip("GPIO.OUT4","GPIO.OE")
|
||||
pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.OE_SIGNAL"), 2, 3, MUX_VISIBLE)
|
||||
|
||||
#pp = tt.create_pip("GPIO.OUT4", "GPIO.CLK_INT")
|
||||
#pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.CLK_1_4"), 1, 0, MUX_VISIBLE)
|
||||
#pp = tt.create_pip("GPIO.OUT1", "GPIO.CLK_INT")
|
||||
#pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.CLK_1_4"), 1, 1, MUX_VISIBLE)
|
||||
|
||||
#pp = tt.create_pip("GPIO.CLK_INT", "GPIO.OUT_CLK")
|
||||
#pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.SEL_OUT_CLOCK"), 1, 1, MUX_VISIBLE)
|
||||
#pp = tt.create_pip("GPIO.CLOCK1", "GPIO.OUT_CLK")
|
||||
#pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.OUT_CLOCK"), 2, 0, MUX_VISIBLE)
|
||||
#pp = tt.create_pip("GPIO.CLOCK2", "GPIO.OUT_CLK")
|
||||
#pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.OUT_CLOCK"), 2, 1, MUX_VISIBLE)
|
||||
#pp = tt.create_pip("GPIO.CLOCK3", "GPIO.OUT_CLK")
|
||||
#pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.OUT_CLOCK"), 2, 2, MUX_VISIBLE)
|
||||
#pp = tt.create_pip("GPIO.CLOCK4", "GPIO.OUT_CLK")
|
||||
#pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.OUT_CLOCK"), 2, 3, MUX_VISIBLE)
|
||||
|
||||
|
||||
#tt.create_wire("GPIO.IN_D1", "WIRE_INTERNAL")
|
||||
#tt.create_wire("GPIO.IN_D2", "WIRE_INTERNAL")
|
||||
#tt.create_wire("GPIO.IN_Q1", "WIRE_INTERNAL")
|
||||
#tt.create_wire("GPIO.IN_Q2", "WIRE_INTERNAL")
|
||||
#tt.create_wire("GPIO.IN_CLK","WIRE_INTERNAL")
|
||||
|
||||
#pp = tt.create_pip("GPIO.CLK_INT", "GPIO.IN_CLK")
|
||||
#pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.SEL_IN_CLOCK"), 1, 1, MUX_VISIBLE)
|
||||
#pp = tt.create_pip("GPIO.CLOCK1", "GPIO.IN_CLK")
|
||||
#pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.IN_CLOCK"), 2, 0, MUX_VISIBLE)
|
||||
#pp = tt.create_pip("GPIO.CLOCK2", "GPIO.IN_CLK")
|
||||
#pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.IN_CLOCK"), 2, 1, MUX_VISIBLE)
|
||||
#pp = tt.create_pip("GPIO.CLOCK3", "GPIO.IN_CLK")
|
||||
#pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.IN_CLOCK"), 2, 2, MUX_VISIBLE)
|
||||
#pp = tt.create_pip("GPIO.CLOCK4", "GPIO.IN_CLK")
|
||||
#pp.extra_data = PipExtraData(PIP_EXTRA_MUX,ch.strs.id("GPIO.IN_CLOCK"), 2, 3, MUX_VISIBLE)
|
||||
|
||||
tt.create_pip("GPIO.DI", "GPIO.IN1")
|
||||
tt.create_pip("GPIO.DI", "GPIO.IN2")
|
||||
|
||||
# Setup tile grid
|
||||
for x in range(dev.max_col() + 3):
|
||||
for y in range(dev.max_row() + 3):
|
||||
ti = ch.set_tile_type(x, y, dev.get_tile_type(x - 2,y - 2))
|
||||
tileinfo = dev.get_tile_info(x - 2,y - 2)
|
||||
ti.extra_data = TileExtraData(tileinfo.die, tileinfo.bit_x, tileinfo.bit_y, tileinfo.prim_index)
|
||||
|
||||
# Create nodes between tiles
|
||||
for _,nodes in dev.get_connections():
|
||||
node = []
|
||||
for conn in sorted(nodes):
|
||||
conn.name = conn.name.replace("CPE.IN", "CPE.V_IN")
|
||||
conn.name = conn.name.replace("CPE.CLK", "CPE.V_CLK")
|
||||
conn.name = conn.name.replace("CPE.EN", "CPE.V_EN")
|
||||
conn.name = conn.name.replace("CPE.SR", "CPE.V_SR")
|
||||
node.append(NodeWire(conn.x + 2, conn.y + 2, conn.name))
|
||||
ch.add_node(node)
|
||||
set_timings(ch)
|
||||
|
||||
for package in dev.get_packages():
|
||||
pkg = ch.create_package(package)
|
||||
for pad in sorted(dev.get_package_pads(package)):
|
||||
pkg.create_pad(pad.name, f"X{pad.x+2}Y{pad.y+2}", pad.bel, pad.function, pad.bank)
|
||||
|
||||
ch.write_bba(args.bba)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
75
himbaechel/uarch/gatemate/gfx.cc
Normal file
75
himbaechel/uarch/gatemate/gfx.cc
Normal file
@ -0,0 +1,75 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2024 The Project Peppercorn Authors.
|
||||
*
|
||||
* 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 "gatemate.h"
|
||||
|
||||
#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc"
|
||||
#define HIMBAECHEL_GFXIDS "uarch/gatemate/gfxids.inc"
|
||||
#define HIMBAECHEL_UARCH gatemate
|
||||
|
||||
#include "himbaechel_constids.h"
|
||||
#include "himbaechel_gfxids.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
void GateMateImpl::drawBel(std::vector<GraphicElement> &g, GraphicElement::style_t style, IdString bel_type, Loc loc)
|
||||
{
|
||||
GraphicElement el;
|
||||
el.type = GraphicElement::TYPE_BOX;
|
||||
el.style = style;
|
||||
switch (bel_type.index) {
|
||||
case id_CPE.index:
|
||||
el.x1 = loc.x + 0.70;
|
||||
el.x2 = el.x1 + 0.20;
|
||||
el.y1 = loc.y + 0.55;
|
||||
el.y2 = el.y1 + 0.40;
|
||||
g.push_back(el);
|
||||
break;
|
||||
case id_GPIO.index:
|
||||
el.x1 = loc.x + 0.20;
|
||||
el.x2 = el.x1 + 0.60;
|
||||
el.y1 = loc.y + 0.20;
|
||||
el.y2 = el.y1 + 0.60;
|
||||
g.push_back(el);
|
||||
break;
|
||||
case id_BUFG.index:
|
||||
el.x1 = loc.x + 0.15 + loc.z * 0.20;
|
||||
el.x2 = el.x1 + 0.15;
|
||||
el.y1 = loc.y + 0.10;
|
||||
el.y2 = el.y1 + 0.30;
|
||||
g.push_back(el);
|
||||
break;
|
||||
case id_PLL.index:
|
||||
el.x1 = loc.x + 0.15 + (loc.z - 4) * 0.20;
|
||||
el.x2 = el.x1 + 0.15;
|
||||
el.y1 = loc.y + 0.60;
|
||||
el.y2 = el.y1 + 0.30;
|
||||
g.push_back(el);
|
||||
break;
|
||||
case id_USR_RSTN.index:
|
||||
el.x1 = loc.x + 0.20;
|
||||
el.x2 = el.x1 + 0.20;
|
||||
el.y1 = loc.y + 0.20;
|
||||
el.y2 = el.y1 + 0.20;
|
||||
g.push_back(el);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
0
himbaechel/uarch/gatemate/gfxids.inc
Normal file
0
himbaechel/uarch/gatemate/gfxids.inc
Normal file
629
himbaechel/uarch/gatemate/pack.cc
Normal file
629
himbaechel/uarch/gatemate/pack.cc
Normal file
@ -0,0 +1,629 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2024 The Project Peppercorn Authors.
|
||||
*
|
||||
* 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 <boost/algorithm/string.hpp>
|
||||
|
||||
#include "pack.h"
|
||||
|
||||
#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc"
|
||||
#include "himbaechel_constids.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
void GateMatePacker::disconnect_if_gnd(CellInfo *cell, IdString input)
|
||||
{
|
||||
NetInfo *net = cell->getPort(input);
|
||||
if (!net)
|
||||
return;
|
||||
if (net->name.in(ctx->id("$PACKER_GND"))) {
|
||||
cell->disconnectPort(input);
|
||||
}
|
||||
}
|
||||
|
||||
void GateMatePacker::pack_io()
|
||||
{
|
||||
// Trim nextpnr IOBs - assume IO buffer insertion has been done in synthesis
|
||||
for (auto &port : ctx->ports) {
|
||||
if (!ctx->cells.count(port.first))
|
||||
log_error("Port '%s' doesn't seem to have a corresponding top level IO\n", ctx->nameOf(port.first));
|
||||
CellInfo *ci = ctx->cells.at(port.first).get();
|
||||
|
||||
PortRef top_port;
|
||||
top_port.cell = nullptr;
|
||||
bool is_npnr_iob = false;
|
||||
|
||||
if (ci->type == ctx->id("$nextpnr_ibuf") || ci->type == ctx->id("$nextpnr_iobuf")) {
|
||||
// Might have an input buffer connected to it
|
||||
is_npnr_iob = true;
|
||||
NetInfo *o = ci->getPort(id_O);
|
||||
if (o == nullptr)
|
||||
;
|
||||
else if (o->users.entries() > 1)
|
||||
log_error("Top level pin '%s' has multiple input buffers\n", ctx->nameOf(port.first));
|
||||
else if (o->users.entries() == 1)
|
||||
top_port = *o->users.begin();
|
||||
}
|
||||
if (ci->type == ctx->id("$nextpnr_obuf") || ci->type == ctx->id("$nextpnr_iobuf")) {
|
||||
// Might have an output buffer connected to it
|
||||
is_npnr_iob = true;
|
||||
NetInfo *i = ci->getPort(id_I);
|
||||
if (i != nullptr && i->driver.cell != nullptr) {
|
||||
if (top_port.cell != nullptr)
|
||||
log_error("Top level pin '%s' has multiple input/output buffers\n", ctx->nameOf(port.first));
|
||||
top_port = i->driver;
|
||||
}
|
||||
// Edge case of a bidirectional buffer driving an output pin
|
||||
if (i->users.entries() > 2) {
|
||||
log_error("Top level pin '%s' has illegal buffer configuration\n", ctx->nameOf(port.first));
|
||||
} else if (i->users.entries() == 2) {
|
||||
if (top_port.cell != nullptr)
|
||||
log_error("Top level pin '%s' has illegal buffer configuration\n", ctx->nameOf(port.first));
|
||||
for (auto &usr : i->users) {
|
||||
if (usr.cell->type == ctx->id("$nextpnr_obuf") || usr.cell->type == ctx->id("$nextpnr_iobuf"))
|
||||
continue;
|
||||
top_port = usr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!is_npnr_iob)
|
||||
log_error("Port '%s' doesn't seem to have a corresponding top level IO (internal cell type mismatch)\n",
|
||||
ctx->nameOf(port.first));
|
||||
|
||||
if (top_port.cell == nullptr) {
|
||||
log_info("Trimming port '%s' as it is unused.\n", ctx->nameOf(port.first));
|
||||
} else {
|
||||
// Copy attributes to real IO buffer
|
||||
for (auto &attrs : ci->attrs)
|
||||
top_port.cell->attrs[attrs.first] = attrs.second;
|
||||
for (auto ¶ms : ci->params) {
|
||||
IdString key = params.first;
|
||||
if (top_port.cell->type.in(id_CC_LVDS_IBUF, id_CC_LVDS_OBUF, id_CC_LVDS_TOBUF, id_CC_LVDS_IOBUF)) {
|
||||
if (top_port.port.in(id_I_P, id_O_P, id_IO_P))
|
||||
key = id_PIN_NAME_P;
|
||||
if (top_port.port.in(id_I_N, id_O_N, id_IO_N))
|
||||
key = id_PIN_NAME_N;
|
||||
}
|
||||
if (top_port.cell->params.count(key)) {
|
||||
if (top_port.cell->params[key] != params.second) {
|
||||
std::string val = params.second.is_string ? params.second.as_string()
|
||||
: std::to_string(params.second.as_int64());
|
||||
log_warning("Overriding parameter '%s' with value '%s' for cell '%s'.\n", key.c_str(ctx),
|
||||
val.c_str(), ctx->nameOf(top_port.cell));
|
||||
}
|
||||
}
|
||||
top_port.cell->params[key] = params.second;
|
||||
}
|
||||
|
||||
// Make sure that top level net is set correctly
|
||||
port.second.net = top_port.cell->ports.at(top_port.port).net;
|
||||
}
|
||||
// Now remove the nextpnr-inserted buffer
|
||||
ci->disconnectPort(id_I);
|
||||
ci->disconnectPort(id_O);
|
||||
ctx->cells.erase(port.first);
|
||||
}
|
||||
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_CC_IBUF, id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF, id_CC_LVDS_IBUF, id_CC_LVDS_OBUF,
|
||||
id_CC_LVDS_TOBUF, id_CC_LVDS_IOBUF))
|
||||
continue;
|
||||
|
||||
bool is_lvds = ci.type.in(id_CC_LVDS_IBUF, id_CC_LVDS_OBUF, id_CC_LVDS_TOBUF, id_CC_LVDS_IOBUF);
|
||||
|
||||
std::string loc = str_or_default(ci.params, is_lvds ? id_PIN_NAME_P : id_PIN_NAME, "UNPLACED");
|
||||
if (ci.params.count(id_LOC)) {
|
||||
std::string new_loc = str_or_default(ci.params, id_LOC, "UNPLACED");
|
||||
if (loc != "UNPLACED" && loc != new_loc)
|
||||
log_warning("Overriding location of cell '%s' from '%s' with '%s'\n", ctx->nameOf(&ci), loc.c_str(),
|
||||
new_loc.c_str());
|
||||
loc = new_loc;
|
||||
}
|
||||
|
||||
if (loc == "UNPLACED")
|
||||
log_warning("IO signal name '%s' is not defined in CCF file and will be auto-placed.\n", ctx->nameOf(&ci));
|
||||
|
||||
disconnect_if_gnd(&ci, id_T);
|
||||
if (ci.type == id_CC_TOBUF && !ci.getPort(id_T))
|
||||
ci.type = id_CC_OBUF;
|
||||
if (ci.type == id_CC_LVDS_TOBUF && !ci.getPort(id_T))
|
||||
ci.type = id_CC_LVDS_OBUF;
|
||||
|
||||
std::vector<IdString> keys;
|
||||
for (auto &p : ci.params) {
|
||||
|
||||
if (p.first.in(id_PIN_NAME, id_PIN_NAME_P, id_PIN_NAME_N)) {
|
||||
if (ctx->get_package_pin_bel(ctx->id(p.second.as_string())) == BelId())
|
||||
log_error("Unknown %s '%s' for cell '%s'.\n", p.first.c_str(ctx), p.second.as_string().c_str(),
|
||||
ci.name.c_str(ctx));
|
||||
keys.push_back(p.first);
|
||||
continue;
|
||||
}
|
||||
if (p.first.in(id_V_IO, id_LOC)) {
|
||||
keys.push_back(p.first);
|
||||
continue;
|
||||
}
|
||||
if (ci.type.in(id_CC_IBUF, id_CC_IOBUF) &&
|
||||
p.first.in(id_PULLUP, id_PULLDOWN, id_KEEPER, id_SCHMITT_TRIGGER, id_DELAY_IBF, id_FF_IBF))
|
||||
continue;
|
||||
if (ci.type.in(id_CC_TOBUF) && p.first.in(id_PULLUP, id_PULLDOWN, id_KEEPER))
|
||||
continue;
|
||||
if (ci.type.in(id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF) &&
|
||||
p.first.in(id_DRIVE, id_SLEW, id_DELAY_OBF, id_FF_OBF))
|
||||
continue;
|
||||
if (ci.type.in(id_CC_LVDS_IBUF, id_CC_LVDS_IOBUF) && p.first.in(id_LVDS_RTERM, id_DELAY_IBF, id_FF_IBF))
|
||||
continue;
|
||||
if (ci.type.in(id_CC_LVDS_OBUF, id_CC_LVDS_TOBUF, id_CC_LVDS_IOBUF) &&
|
||||
p.first.in(id_LVDS_BOOST, id_DELAY_OBF, id_FF_OBF))
|
||||
continue;
|
||||
log_warning("Removing unsupported parameter '%s' for type '%s'.\n", p.first.c_str(ctx), ci.type.c_str(ctx));
|
||||
keys.push_back(p.first);
|
||||
}
|
||||
if (ci.params.count(id_SLEW)) {
|
||||
std::string val = str_or_default(ci.params, id_SLEW, "UNDEFINED");
|
||||
if (val == "UNDEFINED")
|
||||
keys.push_back(id_SLEW);
|
||||
else if (val == "FAST")
|
||||
ci.params[id_SLEW] = Property(Property::State::S1);
|
||||
else if (val == "SLOW")
|
||||
ci.params[id_SLEW] = Property(Property::State::S0);
|
||||
else
|
||||
log_error("Unknown value '%s' for SLEW parameter of '%s' cell.\n", val.c_str(), ci.name.c_str(ctx));
|
||||
}
|
||||
if (is_lvds) {
|
||||
std::string p_pin = str_or_default(ci.params, id_PIN_NAME_P, "UNPLACED");
|
||||
std::string n_pin = str_or_default(ci.params, id_PIN_NAME_N, "UNPLACED");
|
||||
if (p_pin == "UNPLACED" || n_pin == "UNPLACED")
|
||||
log_error("Both LVDS pins must be set to a valid locations.\n");
|
||||
if (p_pin.substr(0, 6) != n_pin.substr(0, 6) || p_pin[7] != n_pin[7])
|
||||
log_error("Both LVDS pads '%s' and '%s' do not match.\n", p_pin.c_str(), n_pin.c_str());
|
||||
if (p_pin[6] != 'A')
|
||||
log_error("Both LVDS positive pad must be from type A.\n");
|
||||
if (n_pin[6] != 'B')
|
||||
log_error("Both LVDS negative pad must be from type B.\n");
|
||||
}
|
||||
for (auto key : keys)
|
||||
ci.params.erase(key);
|
||||
|
||||
if ((ci.params.count(id_KEEPER) + ci.params.count(id_PULLUP) + ci.params.count(id_PULLDOWN)) > 1)
|
||||
log_error("PULLUP, PULLDOWN and KEEPER are mutually exclusive parameters.\n");
|
||||
|
||||
if (is_lvds)
|
||||
ci.params[id_LVDS_EN] = Property(Property::State::S1);
|
||||
|
||||
// DELAY_IBF and DELAY_OBF must be set depending of type
|
||||
// Also we need to enable input/output
|
||||
if (ci.type.in(id_CC_IBUF, id_CC_IOBUF, id_CC_LVDS_IBUF, id_CC_LVDS_IOBUF)) {
|
||||
ci.params[id_DELAY_IBF] = Property(1 << int_or_default(ci.params, id_DELAY_IBF, 0), 16);
|
||||
if (is_lvds)
|
||||
ci.params[id_LVDS_IE] = Property(Property::State::S1);
|
||||
else
|
||||
ci.params[id_INPUT_ENABLE] = Property(Property::State::S1);
|
||||
}
|
||||
if (ci.type.in(id_CC_OBUF, id_CC_TOBUF, id_CC_IOBUF, id_CC_LVDS_OBUF, id_CC_LVDS_TOBUF, id_CC_LVDS_IOBUF)) {
|
||||
ci.params[id_DELAY_OBF] = Property(1 << int_or_default(ci.params, id_DELAY_OBF, 0), 16);
|
||||
ci.params[id_OE_ENABLE] = Property(Property::State::S1);
|
||||
}
|
||||
if (ci.params.count(id_DRIVE)) {
|
||||
int val = int_or_default(ci.params, id_DRIVE, 0);
|
||||
if (val != 3 && val != 6 && val != 9 && val != 12)
|
||||
log_error("Unsupported value '%d' for DRIVE parameter of '%s' cell.\n", val, ci.name.c_str(ctx));
|
||||
ci.params[id_DRIVE] = Property((val - 3) / 3, 2);
|
||||
}
|
||||
for (auto &p : ci.params) {
|
||||
if (p.first.in(id_PULLUP, id_PULLDOWN, id_KEEPER, id_SCHMITT_TRIGGER, id_FF_OBF, id_FF_IBF, id_LVDS_RTERM,
|
||||
id_LVDS_BOOST)) {
|
||||
int val = int_or_default(ci.params, p.first, 0);
|
||||
if (val != 0 && val != 1)
|
||||
log_error("Unsupported value '%d' for %s parameter of '%s' cell.\n", val, p.first.c_str(ctx),
|
||||
ci.name.c_str(ctx));
|
||||
ci.params[p.first] = Property(val, 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Disconnect PADs
|
||||
ci.disconnectPort(id_IO);
|
||||
ci.disconnectPort(id_I);
|
||||
ci.disconnectPort(id_O);
|
||||
ci.disconnectPort(id_IO_P);
|
||||
ci.disconnectPort(id_IO_N);
|
||||
ci.disconnectPort(id_I_P);
|
||||
ci.disconnectPort(id_I_N);
|
||||
ci.disconnectPort(id_O_P);
|
||||
ci.disconnectPort(id_O_N);
|
||||
|
||||
// Remap ports to GPIO bel
|
||||
ci.renamePort(id_A, id_DO);
|
||||
ci.renamePort(id_Y, id_DI);
|
||||
ci.renamePort(id_T, id_OE);
|
||||
|
||||
NetInfo *do_net = ci.getPort(id_DO);
|
||||
if (do_net) {
|
||||
if (do_net->name.in(ctx->id("$PACKER_GND"), ctx->id("$PACKER_VCC"))) {
|
||||
ci.params[id_OUT23_14_SEL] =
|
||||
Property(do_net->name == ctx->id("$PACKER_VCC") ? Property::State::S1 : Property::State::S0);
|
||||
ci.disconnectPort(id_DO);
|
||||
} else {
|
||||
ci.params[id_OUT_SIGNAL] = Property(Property::State::S1);
|
||||
}
|
||||
}
|
||||
if (!loc.empty()) {
|
||||
BelId bel = ctx->get_package_pin_bel(ctx->id(loc));
|
||||
if (bel == BelId())
|
||||
log_error("Unable to constrain IO '%s', device does not have a pin named '%s'\n", ci.name.c_str(ctx),
|
||||
loc.c_str());
|
||||
log_info(" Constraining '%s' to pad '%s'\n", ci.name.c_str(ctx), loc.c_str());
|
||||
if (!ctx->checkBelAvail(bel)) {
|
||||
log_error("Can't place %s at %s because it's already taken by %s\n", ctx->nameOf(&ci),
|
||||
ctx->nameOfBel(bel), ctx->nameOf(ctx->getBoundBelCell(bel)));
|
||||
}
|
||||
ctx->bindBel(bel, &ci, PlaceStrength::STRENGTH_FIXED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GateMatePacker::pack_cpe()
|
||||
{
|
||||
log_info("Packing CPEs..\n");
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_CC_L2T4, id_CC_L2T5, id_CC_LUT2, id_CC_LUT1))
|
||||
continue;
|
||||
if (ci.type == id_CC_L2T5) {
|
||||
ci.renamePort(id_I0, id_IN5);
|
||||
ci.renamePort(id_I1, id_IN6);
|
||||
ci.renamePort(id_I2, id_IN7);
|
||||
ci.renamePort(id_I3, id_IN8);
|
||||
|
||||
ci.renamePort(id_I4, id_IN1);
|
||||
ci.renamePort(id_O, id_OUT1);
|
||||
ci.params[id_INIT_L00] = Property(0b1010, 4);
|
||||
ci.params[id_INIT_L10] = Property(0b1010, 4);
|
||||
ci.params[id_O1] = Property(0b11, 2);
|
||||
} else {
|
||||
ci.renamePort(id_I0, id_IN1);
|
||||
ci.renamePort(id_I1, id_IN2);
|
||||
ci.renamePort(id_I2, id_IN3);
|
||||
ci.renamePort(id_I3, id_IN4);
|
||||
ci.renamePort(id_O, id_OUT1);
|
||||
ci.params[id_O1] = Property(0b11, 2);
|
||||
ci.params[id_INIT_L20] = Property(0b1010, 4);
|
||||
if (ci.type.in(id_CC_LUT1, id_CC_LUT2)) {
|
||||
uint8_t val = int_or_default(ci.params, id_INIT, 0);
|
||||
if (ci.type == id_CC_LUT1)
|
||||
val = val << 2 | val;
|
||||
ci.params[id_INIT_L00] = Property(val, 4);
|
||||
ci.unsetParam(id_INIT);
|
||||
ci.params[id_INIT_L10] = Property(0b1010, 4);
|
||||
}
|
||||
}
|
||||
ci.type = id_CPE;
|
||||
}
|
||||
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_CC_MX2, id_CC_MX4))
|
||||
continue;
|
||||
|
||||
ci.renamePort(id_D0, id_IN1);
|
||||
ci.renamePort(id_D1, id_IN2);
|
||||
ci.renamePort(id_S0, id_IN6);
|
||||
ci.renamePort(id_Y, id_OUT1);
|
||||
// Only for CC_MX4
|
||||
ci.renamePort(id_D2, id_IN3);
|
||||
ci.renamePort(id_D3, id_IN4);
|
||||
ci.renamePort(id_S1, id_IN8);
|
||||
|
||||
uint8_t select = 0;
|
||||
uint8_t invert = 0;
|
||||
for(int i=0;i<4;i++) {
|
||||
NetInfo *net = ci.getPort(ctx->idf("IN%d",i+1));
|
||||
if (net) {
|
||||
if (net->name.in(ctx->id("$PACKER_GND"),ctx->id("$PACKER_VCC"))) {
|
||||
if (net->name == ctx->id("$PACKER_VCC"))
|
||||
invert |= 1<< i;
|
||||
ci.disconnectPort(ctx->idf("IN%d",i+1));
|
||||
} else {
|
||||
select |= 1 << i;
|
||||
}
|
||||
}
|
||||
}
|
||||
ci.params[id_FUNCTION] = Property(0b100, 3);
|
||||
ci.params[id_INIT_L02] = Property(0b1100, 4); // IN6
|
||||
if (ci.type == id_CC_MX4)
|
||||
ci.params[id_INIT_L03] = Property(0b1100, 4); // IN8
|
||||
ci.params[id_INIT_L10] = Property(select, 4); // Selection bits
|
||||
ci.params[id_INIT_L11] = Property(invert, 4); // Inversion bits
|
||||
ci.params[id_INIT_L20] = Property(0b1100, 4); // Always D1
|
||||
ci.params[id_O1] = Property(0b11, 2);
|
||||
ci.type = id_CPE;
|
||||
}
|
||||
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_CC_DFF))
|
||||
continue;
|
||||
ci.renamePort(id_Q, id_OUT2);
|
||||
ci.params[id_O2] = Property(0b00, 2);
|
||||
ci.params[id_2D_IN] = Property(1, 1);
|
||||
NetInfo *d_net = ci.getPort(id_D);
|
||||
if (d_net->name == ctx->id("$PACKER_GND")) {
|
||||
ci.params[id_INIT_L00] = Property(0b0000, 4);
|
||||
ci.disconnectPort(id_D);
|
||||
} else if (d_net->name == ctx->id("$PACKER_VCC")) {
|
||||
ci.params[id_INIT_L00] = Property(0b1111, 4);
|
||||
ci.disconnectPort(id_D);
|
||||
} else {
|
||||
ci.params[id_INIT_L00] = Property(0b1010, 4);
|
||||
}
|
||||
ci.params[id_INIT_L10] = Property(0b1010, 4);
|
||||
ci.renamePort(id_D, id_IN1);
|
||||
|
||||
NetInfo *en_net = ci.getPort(id_EN);
|
||||
bool invert = int_or_default(ci.params, id_EN_INV, 0) == 1;
|
||||
if (en_net) {
|
||||
if (en_net->name == ctx->id("$PACKER_GND")) {
|
||||
ci.params[id_EN] = Property(invert ? 0b11 : 0b00, 2);
|
||||
ci.disconnectPort(id_EN);
|
||||
} else if (en_net->name == ctx->id("$PACKER_VCC")) {
|
||||
ci.params[id_EN] = Property(invert ? 0b00 : 0b11, 2);
|
||||
ci.disconnectPort(id_EN);
|
||||
} else {
|
||||
ci.params[id_EN] = Property(invert ? 0b01 : 0b10, 2);
|
||||
}
|
||||
} else {
|
||||
ci.params[id_EN] = Property(invert ? 0b11 : 0b00, 2);
|
||||
}
|
||||
ci.unsetParam(id_EN_INV);
|
||||
|
||||
NetInfo *clk_net = ci.getPort(id_CLK);
|
||||
invert = int_or_default(ci.params, id_CLK_INV, 0) == 1;
|
||||
if (clk_net) {
|
||||
if (clk_net->name == ctx->id("$PACKER_GND")) {
|
||||
ci.params[id_CLK] = Property(invert ? 0b11 : 0b00, 2);
|
||||
ci.disconnectPort(id_CLK);
|
||||
} else if (clk_net->name == ctx->id("$PACKER_VCC")) {
|
||||
ci.params[id_CLK] = Property(invert ? 0b00 : 0b11, 2);
|
||||
ci.disconnectPort(id_CLK);
|
||||
} else {
|
||||
ci.params[id_CLK] = Property(invert ? 0b01 : 0b10, 2);
|
||||
}
|
||||
} else {
|
||||
ci.params[id_CLK] = Property(invert ? 0b11 : 0b00, 2);
|
||||
}
|
||||
ci.unsetParam(id_CLK_INV);
|
||||
|
||||
NetInfo *sr_net = ci.getPort(id_SR);
|
||||
invert = int_or_default(ci.params, id_SR_INV, 0) == 1;
|
||||
int sr_val = int_or_default(ci.params, id_SR_VAL, 0) == 1;
|
||||
if (sr_net) {
|
||||
if (sr_net->name == ctx->id("$PACKER_GND")) {
|
||||
if (invert)
|
||||
log_error("Invalid DFF configuration\n.");
|
||||
ci.params[id_R] = Property(0b11, 2);
|
||||
ci.params[id_S] = Property(0b11, 2);
|
||||
ci.disconnectPort(id_SR);
|
||||
} else if (sr_net->name == ctx->id("$PACKER_VCC")) {
|
||||
if (!invert)
|
||||
log_error("Invalid DFF configuration\n.");
|
||||
ci.params[id_R] = Property(0b11, 2);
|
||||
ci.params[id_S] = Property(0b11, 2);
|
||||
ci.disconnectPort(id_SR);
|
||||
} else {
|
||||
if (sr_val) {
|
||||
ci.params[id_R] = Property(0b11, 2);
|
||||
ci.params[id_S] = Property(invert ? 0b01 : 0b10, 2);
|
||||
ci.params[id_EN_SR] = Property(0b1, 1);
|
||||
} else {
|
||||
ci.params[id_R] = Property(invert ? 0b01 : 0b10, 2);
|
||||
ci.params[id_S] = Property(0b11, 2);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (invert)
|
||||
log_error("Invalid DFF configuration\n.");
|
||||
ci.params[id_R] = Property(0b11, 2);
|
||||
ci.params[id_S] = Property(0b11, 2);
|
||||
}
|
||||
ci.unsetParam(id_SR_VAL);
|
||||
ci.unsetParam(id_SR_INV);
|
||||
|
||||
if (ci.params.count(id_INIT) && ci.params[id_INIT].is_fully_def()) {
|
||||
bool init = int_or_default(ci.params, id_INIT, 0) == 1;
|
||||
if (init)
|
||||
ci.params[id_FF_INIT] = Property(0b11, 2);
|
||||
else
|
||||
ci.params[id_FF_INIT] = Property(0b10, 2);
|
||||
ci.unsetParam(id_INIT);
|
||||
} else {
|
||||
ci.unsetParam(id_INIT);
|
||||
}
|
||||
ci.timing_index = ctx->get_cell_timing_idx(id_CPE_DFF);
|
||||
ci.type = id_CPE;
|
||||
}
|
||||
}
|
||||
|
||||
void GateMatePacker::pack_bufg()
|
||||
{
|
||||
log_info("Packing BUFGs..\n");
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_CC_BUFG))
|
||||
continue;
|
||||
ci.type = id_BUFG;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename KeyType> double double_or_default(const dict<KeyType, Property> &ct, const KeyType &key, double def = 0)
|
||||
{
|
||||
auto found = ct.find(key);
|
||||
if (found == ct.end())
|
||||
return def;
|
||||
else {
|
||||
if (found->second.is_string) {
|
||||
try {
|
||||
return std::stod(found->second.as_string());
|
||||
} catch (std::invalid_argument &e) {
|
||||
log_error("Expecting numeric value but got '%s'.\n", found->second.as_string().c_str());
|
||||
}
|
||||
} else
|
||||
return double(found->second.as_int64());
|
||||
}
|
||||
};
|
||||
|
||||
void GateMatePacker::pack_pll()
|
||||
{
|
||||
log_info("Packing PLLss..\n");
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_CC_PLL))
|
||||
continue;
|
||||
|
||||
disconnect_if_gnd(&ci, id_CLK_REF);
|
||||
disconnect_if_gnd(&ci, id_USR_CLK_REF);
|
||||
disconnect_if_gnd(&ci, id_CLK_FEEDBACK);
|
||||
disconnect_if_gnd(&ci, id_USR_LOCKED_STDY_RST);
|
||||
|
||||
ci.params[id_LOCK_REQ] = Property(int_or_default(ci.params, id_LOCK_REQ, 1),1);
|
||||
ci.params[id_CLK180_DOUB] = Property(int_or_default(ci.params, id_CLK180_DOUB, 0),1);
|
||||
ci.params[id_CLK270_DOUB] = Property(int_or_default(ci.params, id_CLK270_DOUB, 0),1);
|
||||
std::string mode = str_or_default(ci.params, id_PERF_MD, "SPEED");
|
||||
boost::algorithm::to_upper(mode);
|
||||
int perf_md;
|
||||
if (mode == "LOWPOWER") perf_md = 1;
|
||||
else if (mode == "ECONOMY") perf_md = 2;
|
||||
else if (mode == "SPEED") perf_md = 3;
|
||||
else log_error("Unknown PERF_MD parameter value '%s' for cell %s.\n", mode.c_str(), ci.name.c_str(ctx));
|
||||
|
||||
|
||||
double ref_clk = double_or_default(ci.params, id_REF_CLK, 0.0);
|
||||
if (ref_clk <= 0 || ref_clk > 125)
|
||||
log_error("REF_CLK parameter is out of range (0,125.00].\n");
|
||||
|
||||
double max_freq = 0.0;
|
||||
switch(perf_md) {
|
||||
case 1: max_freq = 250.00; break;
|
||||
case 2: max_freq = 312.50; break;
|
||||
case 3: max_freq = 416.75; break;
|
||||
}
|
||||
|
||||
double out_clk = double_or_default(ci.params, id_OUT_CLK, 0.0);
|
||||
if (out_clk <= 0 || out_clk > max_freq)
|
||||
log_error("OUT_CLK parameter is out of range (0,%.2lf].\n", max_freq);
|
||||
|
||||
// For 10 MHz - > 25 MHz
|
||||
ci.params[ctx->id("CFG_A.AO_SW")] = Property(0b01000,5);
|
||||
ci.params[ctx->id("CFG_A.CI_FILTER_CONST")] = Property(0b00010,5);
|
||||
ci.params[ctx->id("CFG_A.COARSE_TUNE")] = Property(0b100,3);
|
||||
ci.params[ctx->id("CFG_A.CP_FILTER_CONST")] = Property(0b00100,5);
|
||||
ci.params[ctx->id("CFG_A.EN_COARSE_TUNE")] = Property(0b1,1);
|
||||
ci.params[ctx->id("CFG_A.FAST_LOCK")] = Property(0b1,1);
|
||||
ci.params[ctx->id("CFG_A.FILTER_SHIFT")] = Property(0b10,2);
|
||||
ci.params[ctx->id("CFG_A.FINE_TUNE")] = Property(0b00011001000,11);
|
||||
ci.params[ctx->id("CFG_A.K")] = Property(0b000000000001,12);
|
||||
ci.params[ctx->id("CFG_A.M1")] = Property(0b000010,6);
|
||||
ci.params[ctx->id("CFG_A.M2")] = Property(0b0000001001,10);
|
||||
ci.params[ctx->id("CFG_A.N1")] = Property(0b001001,6);
|
||||
ci.params[ctx->id("CFG_A.N2")] = Property(0b0000001010,10);
|
||||
ci.params[ctx->id("CFG_A.PDIV0_MUX")] = Property(0b1,1);
|
||||
ci.params[ctx->id("CFG_A.PDIV1_SEL")] = Property(0b1,1);
|
||||
ci.params[ctx->id("CFG_A.SAR_LIMIT")] = Property(0b010,3);
|
||||
|
||||
// Remove all not propagated parameters
|
||||
ci.unsetParam(id_PERF_MD);
|
||||
ci.unsetParam(id_REF_CLK);
|
||||
ci.unsetParam(id_OUT_CLK);
|
||||
ci.unsetParam(id_LOW_JITTER);
|
||||
ci.unsetParam(id_CI_FILTER_CONST);
|
||||
ci.unsetParam(id_CP_FILTER_CONST);
|
||||
//ci.unsetParam(id_PLL_CFG_A);
|
||||
//ci.unsetParam(id_PLL_CFG_B);
|
||||
|
||||
ci.type = id_PLL;
|
||||
}
|
||||
}
|
||||
|
||||
void GateMatePacker::pack_misc()
|
||||
{
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo &ci = *cell.second;
|
||||
if (!ci.type.in(id_CC_USR_RSTN))
|
||||
continue;
|
||||
ci.type = id_USR_RSTN;
|
||||
}
|
||||
}
|
||||
|
||||
void GateMatePacker::pack_constants()
|
||||
{
|
||||
log_info("Packing constants..\n");
|
||||
// Replace constants with LUTs
|
||||
const dict<IdString, Property> vcc_params = {{id_INIT_L20, Property(0b1111, 4)}, {id_O1, Property(0b11, 2)}};
|
||||
const dict<IdString, Property> gnd_params = {{id_INIT_L20, Property(0b0000, 4)}, {id_O1, Property(0b11, 2)}};
|
||||
|
||||
h.replace_constants(CellTypePort(id_CPE, id_OUT1), CellTypePort(id_CPE, id_OUT1), vcc_params, gnd_params);
|
||||
}
|
||||
|
||||
void GateMatePacker::remove_constants()
|
||||
{
|
||||
log_info("Removing constants..\n");
|
||||
auto fnd_cell = ctx->cells.find(ctx->id("$PACKER_VCC_DRV"));
|
||||
if (fnd_cell != ctx->cells.end()) {
|
||||
auto fnd_net = ctx->nets.find(ctx->id("$PACKER_VCC"));
|
||||
if (fnd_net != ctx->nets.end() && fnd_net->second->users.entries() == 0) {
|
||||
BelId bel = (*fnd_cell).second.get()->bel;
|
||||
if (bel != BelId())
|
||||
ctx->unbindBel(bel);
|
||||
ctx->cells.erase(fnd_cell);
|
||||
ctx->nets.erase(fnd_net);
|
||||
log_info(" Removed unused VCC cell\n");
|
||||
}
|
||||
}
|
||||
fnd_cell = ctx->cells.find(ctx->id("$PACKER_GND_DRV"));
|
||||
if (fnd_cell != ctx->cells.end()) {
|
||||
auto fnd_net = ctx->nets.find(ctx->id("$PACKER_GND"));
|
||||
if (fnd_net != ctx->nets.end() && fnd_net->second->users.entries() == 0) {
|
||||
BelId bel = (*fnd_cell).second.get()->bel;
|
||||
if (bel != BelId())
|
||||
ctx->unbindBel(bel);
|
||||
ctx->cells.erase(fnd_cell);
|
||||
ctx->nets.erase(fnd_net);
|
||||
log_info(" Removed unused GND cell\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GateMateImpl::pack()
|
||||
{
|
||||
const ArchArgs &args = ctx->args;
|
||||
if (args.options.count("ccf")) {
|
||||
parse_ccf(args.options.at("ccf"));
|
||||
}
|
||||
|
||||
GateMatePacker packer(ctx, this);
|
||||
packer.pack_constants();
|
||||
packer.pack_io();
|
||||
packer.pack_pll();
|
||||
packer.pack_bufg();
|
||||
packer.pack_misc();
|
||||
packer.pack_cpe();
|
||||
packer.remove_constants();
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
48
himbaechel/uarch/gatemate/pack.h
Normal file
48
himbaechel/uarch/gatemate/pack.h
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2024 The Project Peppercorn Authors.
|
||||
*
|
||||
* 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 GATEMATE_PACK_H
|
||||
#define GATEMATE_PACK_H
|
||||
|
||||
#include "gatemate.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
struct GateMatePacker
|
||||
{
|
||||
GateMatePacker(Context *ctx, GateMateImpl *uarch) : ctx(ctx), uarch(uarch) { h.init(ctx); };
|
||||
|
||||
void pack_io();
|
||||
void pack_cpe();
|
||||
void pack_bufg();
|
||||
void pack_pll();
|
||||
void pack_misc();
|
||||
void pack_constants();
|
||||
|
||||
void disconnect_if_gnd(CellInfo *cell, IdString input);
|
||||
void remove_constants();
|
||||
|
||||
Context *ctx;
|
||||
GateMateImpl *uarch;
|
||||
|
||||
HimbaechelHelpers h;
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
#endif
|
2
tests
2
tests
@ -1 +1 @@
|
||||
Subproject commit d2d5b69dd34b1a9a1e4e9466599ed18e9254fee9
|
||||
Subproject commit 00c55a9eb9ea2e062b51fe0d64741412b185d95d
|
Loading…
Reference in New Issue
Block a user