mirror of https://github.com/YosysHQ/nextpnr.git
Gatemate FPGA initial support (#1473)
* Initial code for GateMate * Initial work on forming bitstream * Add CCF parsing * Use CCF to set IO location * Propagate errors * Restructure code * Add support for reading from config * Start adding infrastructure for reading bitstream * Fix script * GPIO initial work * Add IN1->RAM_O2 propagation * Fixed typo * Cleanup * More parameter checks * Add LVDS support * Cleanup * Keep just used connections for now * Naive lut tree CPE pack * Naive pack CC_DFF * pack DFF fixes * Handle MUX flags * Fix DFF pack * Prevent pass trough issues * Cleanup * Use device wrapper class * Update due to API changes * Use pin connection aliases * Start work on BUFG support * Fix CC_L2T5 pack * Add CPE input inverters * Constrain routes to have correct inversion state * Add clock inversion pip * Added MX2 and MX4 support * Fix script * BUFG support * debug print if route found with wrong polarity * Some CC_DFF improvements * Create reproducible chip database * Simplify inversion of special signals * Few more DFF features * Add forgotten virtual port renames * Handle muxes with constant inputs * Allow inversion for muxes * cleanup * DFF input can be constant * init DFF only when needed * cleanup * Add basic PLL support * Add some timings * Add USR_RSTN support * Display few more primitives * Use pass trough signals to validate architecture data * Use extra tile information from chip database * Updates needed for a build system changes * Implement SB_DRIVE support * Properly named configuration bits * autogenerated constids.inc * small fix * Initial code for CPE halfs * Some cleanup * make sure FFs are compatible * reverted due to db change * Merge DFF where applicable * memory allocation issue * fix * better MX2 * ram_i handling * Cleanup MX4 * Support latches * compare L_D flag as well * Move virtual pips * Naive addf pack * carry chains grouping * Keep chip database reproducible * split addf vectors * Block CPEs when GPIO is used * Prepare placement code * RAM_I/RAM_O rewrite * fix ram_i/o index * Display RAM and add new primitives * PLL wip code * CC_PLL_ADV packing * PLL handling cleanup * Add PLL comments * Keep only high fan-out BUFG * Add skeleton for tests * Utilize move_ram_o * GPIO wip * GPIO wip * PLL fixes * cleanup * FF_OBF support * Handle FF_IBF * Make SLEW FAST if not defined as in latest p_r * Make sure FF_OBF only driving GPIO * Moved pll calc into separate file * IDDR handling and started ODDR * Route DDR input for CC_ODDR * Notify error in case ODDR or IDDR are used but not with I/O pin * cleanup for CC_USR_RSTN * Extract proper RAM location for bitstream * Code cleanup * Allow auto place of pads * Use clock source flag * Configure GPIO clock signals * Handle conflicting clk * Use BUGF in proper order * Connected CLK, works without but good for debugging * CC_CFG_CTRL placement * Group RAM data 40 bytes per row * Write BRAM content * RAM wip * Use relative constraints from chipdb * fix broken build * Memory wip * Handle custom clock for memories * Support FIFO * optimize move_ram_io * Fix SR signal handling acorrding to findings * set placer beta * Pre place what we can * Revert "debug print if route found with wrong polarity" This reverts commitcf9ded2f18. * Revert "Constrain routes to have correct inversion state" This reverts commit795c284d48. * Remove virtual pips * Implement post processing inversion * ADDF add ability to route additional CO * Merge two ADDFs in one CPE * Added TODO * clangformat * Cleanup * Add serdes handling in config file * Cleanup * Cleanup * Cleanup * Fix in PLL handling * Fixed ADDF edge case * No need for this * Fix latch * Sanity checks * Support CC_BRAM_20K merge * Start creating testing environment * LVDS fixes * Add connection helper * Cleanup * Fix tabs * Formatting fix * Remove optimization tests for now * remove read_bitstream * removed .c_str() * Removed config parsing * using snake_case * Use bool_or_default where applicable * refactored bitstream write code * Add allow-unconstrained option * Update DFF related messages * Add clock constraint propagation --------- Co-authored-by: Lofty <dan.ravensloft@gmail.com>
This commit is contained in:
parent
7a3a43e150
commit
d6483adb4d
|
|
@ -64,7 +64,7 @@ else()
|
||||||
|
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(HIMBAECHEL_UARCHES example gowin xilinx ng-ultra)
|
set(HIMBAECHEL_UARCHES example gowin xilinx ng-ultra gatemate)
|
||||||
|
|
||||||
set(HIMBAECHEL_UARCH "" CACHE STRING "Microarchitectures for nextpnr-himbaechel build")
|
set(HIMBAECHEL_UARCH "" CACHE STRING "Microarchitectures for nextpnr-himbaechel build")
|
||||||
set_property(CACHE HIMBAECHEL_UARCH PROPERTY STRINGS ${HIMBAECHEL_UARCHES})
|
set_property(CACHE HIMBAECHEL_UARCH PROPERTY STRINGS ${HIMBAECHEL_UARCHES})
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
set(SOURCES
|
||||||
|
bitstream.cc
|
||||||
|
ccf.cc
|
||||||
|
cells.cc
|
||||||
|
config.cc
|
||||||
|
config.h
|
||||||
|
constids.inc
|
||||||
|
extra_data.h
|
||||||
|
gatemate.cc
|
||||||
|
gatemate.h
|
||||||
|
gfx.cc
|
||||||
|
gfxids.inc
|
||||||
|
pack.cc
|
||||||
|
pack.h
|
||||||
|
pll.cc
|
||||||
|
)
|
||||||
|
|
||||||
|
set(TEST_SOURCES
|
||||||
|
tests/lut.cc
|
||||||
|
tests/testing.cc
|
||||||
|
tests/main.cc
|
||||||
|
)
|
||||||
|
|
||||||
|
add_nextpnr_himbaechel_microarchitecture(${uarch}
|
||||||
|
CORE_SOURCES ${SOURCES}
|
||||||
|
TEST_SOURCES ${TEST_SOURCES}
|
||||||
|
)
|
||||||
|
|
||||||
|
set(HIMBAECHEL_PEPPERCORN_PATH "" CACHE STRING
|
||||||
|
"Path to a Project Peppercorn database")
|
||||||
|
if (NOT HIMBAECHEL_PEPPERCORN_PATH)
|
||||||
|
message(FATAL_ERROR "HIMBAECHEL_PEPPERCORN_PATH must be set to a Project Peppercorn checkout")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
#set(ALL_HIMBAECHE_GATEMATE_DEVICES CCGM1A1 CCGM1A2 CCGM1A4 CCGM1A9 CCGM1A16 CCGM1A25)
|
||||||
|
set(ALL_HIMBAECHE_GATEMATE_DEVICES CCGM1A1)
|
||||||
|
set(HIMBAECHEL_GATEMATE_DEVICES ${ALL_HIMBAECHE_GATEMATE_DEVICES} CACHE STRING
|
||||||
|
"Include support for these GateMate devices (available: ${ALL_HIMBAECHE_GATEMATE_DEVICES})")
|
||||||
|
message(STATUS "Enabled Himbaechel-GateMate devices: ${HIMBAECHEL_GATEMATE_DEVICES}")
|
||||||
|
|
||||||
|
foreach (device ${HIMBAECHEL_GATEMATE_DEVICES})
|
||||||
|
if (NOT device IN_LIST ALL_HIMBAECHE_GATEMATE_DEVICES)
|
||||||
|
message(FATAL_ERROR "Device ${device} is not a supported GateMate device")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_bba_produce_command(
|
||||||
|
TARGET nextpnr-himbaechel-gatemate-bba
|
||||||
|
COMMAND ${Python3_EXECUTABLE}
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/gen/arch_gen.py
|
||||||
|
--lib ${HIMBAECHEL_PEPPERCORN_PATH}/gatemate
|
||||||
|
--device ${device}
|
||||||
|
--bba ${CMAKE_CURRENT_BINARY_DIR}/chipdb-${device}.bba.new
|
||||||
|
OUTPUT
|
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/chipdb-${device}.bba
|
||||||
|
INPUTS
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/gen/arch_gen.py
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/constids.inc
|
||||||
|
${CMAKE_CURRENT_SOURCE_DIR}/gfxids.inc
|
||||||
|
)
|
||||||
|
|
||||||
|
add_bba_compile_command(
|
||||||
|
TARGET nextpnr-himbaechel-gatemate-chipdb
|
||||||
|
OUTPUT himbaechel/gatemate/chipdb-${device}.bin
|
||||||
|
INPUT ${CMAKE_CURRENT_BINARY_DIR}/chipdb-${device}.bba
|
||||||
|
MODE binary
|
||||||
|
)
|
||||||
|
endforeach()
|
||||||
|
|
@ -0,0 +1,252 @@
|
||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
#include "gatemate_util.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) {};
|
||||||
|
|
||||||
|
const GateMateTileExtraDataPOD *tile_extra_data(int tile) const
|
||||||
|
{
|
||||||
|
return reinterpret_cast<const GateMateTileExtraDataPOD *>(ctx->chip_info->tile_insts[tile].extra_data.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
CfgLoc get_config_loc(int tile)
|
||||||
|
{
|
||||||
|
auto ti = *tile_extra_data(tile);
|
||||||
|
CfgLoc loc;
|
||||||
|
loc.die = ti.die;
|
||||||
|
loc.x = ti.bit_x;
|
||||||
|
loc.y = ti.bit_y;
|
||||||
|
return loc;
|
||||||
|
}
|
||||||
|
|
||||||
|
CfgLoc get_ram_config_loc(int tile)
|
||||||
|
{
|
||||||
|
auto ti = *tile_extra_data(tile);
|
||||||
|
CfgLoc loc;
|
||||||
|
loc.die = ti.die;
|
||||||
|
loc.x = (ti.bit_x - 17) / 16;
|
||||||
|
loc.y = (ti.bit_y - 1) / 8;
|
||||||
|
return loc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void export_connection(ChipConfig &cc, PipId 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 = get_config_loc(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 = 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));
|
||||||
|
if (boost::starts_with(word, "SB_DRIVE.")) {
|
||||||
|
Loc l;
|
||||||
|
tile_xy(ctx->chip_info, pip.tile, l.x, l.y);
|
||||||
|
l.z = 0;
|
||||||
|
BelId cpe_bel = ctx->getBelByLocation(l);
|
||||||
|
// Only if switchbox is inside core (same as sharing location with CPE)
|
||||||
|
if (cpe_bel != BelId() && ctx->getBelType(cpe_bel).in(id_CPE_HALF_L, id_CPE_HALF_U)) {
|
||||||
|
// Convert coordinates into in-tile coordinates
|
||||||
|
int xt = ((l.x - 2 - 1) + 16) % 8;
|
||||||
|
int yt = ((l.y - 2 - 1) + 16) % 8;
|
||||||
|
// Bitstream data for certain SB_DRIVES is located in other tiles
|
||||||
|
switch (word[14]) {
|
||||||
|
case '3':
|
||||||
|
if (xt >= 4) {
|
||||||
|
loc.x -= 2;
|
||||||
|
word[14] = '1';
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case '4':
|
||||||
|
if (yt >= 4) {
|
||||||
|
loc.y -= 2;
|
||||||
|
word[14] = '2';
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case '1':
|
||||||
|
if (xt <= 3) {
|
||||||
|
loc.x += 2;
|
||||||
|
word[14] = '3';
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case '2':
|
||||||
|
if (yt <= 3) {
|
||||||
|
loc.y += 2;
|
||||||
|
word[14] = '4';
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cc.tiles[loc].add_word(word, int_to_bitvector(extra_data.value, extra_data.bits));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = get_config_loc(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_HALF_U.index:
|
||||||
|
case id_CPE_HALF_L.index: {
|
||||||
|
int id = 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);
|
||||||
|
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_RAM.index: {
|
||||||
|
CfgLoc loc = get_ram_config_loc(cell.second.get()->bel.tile);
|
||||||
|
auto &bram = cc.brams[loc];
|
||||||
|
for (auto &p : params) {
|
||||||
|
std::string name = p.first.c_str(ctx);
|
||||||
|
if (boost::starts_with(name, "RAM_cfg"))
|
||||||
|
bram.add_word(name, p.second.as_bits());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_fifo = params.count(id_RAM_cfg_fifo_sync_enable) | params.count(id_RAM_cfg_fifo_async_enable);
|
||||||
|
if (!is_fifo) {
|
||||||
|
auto &bram_data = cc.bram_data[loc];
|
||||||
|
bram_data = std::vector<uint8_t>(5120);
|
||||||
|
for (int i = 0; i < 128; i++) {
|
||||||
|
for (int j = 0; j < 40; j++) {
|
||||||
|
bram_data[i * 40 + j] = extract_bits(params, ctx->idf("INIT_%02X", i), j * 8, 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
case id_USR_RSTN.index:
|
||||||
|
case id_CFG_CTRL.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;
|
||||||
|
for (auto &w : ni->wires) {
|
||||||
|
if (w.second.pip != PipId())
|
||||||
|
export_connection(cc, w.second.pip);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
@ -0,0 +1,215 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
GateMateImpl *uarch;
|
||||||
|
std::istream ∈
|
||||||
|
int lineno;
|
||||||
|
dict<IdString, Property> defaults;
|
||||||
|
|
||||||
|
GateMateCCFReader(Context *ctx, GateMateImpl *uarch, std::istream &in) : ctx(ctx), uarch(uarch), 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);
|
||||||
|
if (!uarch->available_pads.count(ctx->id(value)))
|
||||||
|
log_error("Pad '%s' used in line %d not available.\n", value.c_str(), lineno);
|
||||||
|
props->emplace(id_LOC, Property(value));
|
||||||
|
uarch->available_pads.erase(ctx->id(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), Property(Property::State::S1));
|
||||||
|
} else if (value == "FALSE") {
|
||||||
|
props->emplace(ctx->id(name), 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), 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), 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), 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, this, in);
|
||||||
|
reader.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* 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 "nextpnr.h"
|
||||||
|
#include "pack.h"
|
||||||
|
|
||||||
|
#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc"
|
||||||
|
#include "himbaechel_constids.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
CellInfo *GateMatePacker::create_cell_ptr(IdString type, IdString name)
|
||||||
|
{
|
||||||
|
CellInfo *cell = ctx->createCell(name, type);
|
||||||
|
|
||||||
|
auto add_port = [&](const IdString id, PortType dir) {
|
||||||
|
cell->ports[id].name = id;
|
||||||
|
cell->ports[id].type = dir;
|
||||||
|
};
|
||||||
|
if (type.in(id_CPE_HALF, id_CPE_HALF_U, id_CPE_HALF_L)) {
|
||||||
|
add_port(id_I1, PORT_IN);
|
||||||
|
add_port(id_I2, PORT_IN);
|
||||||
|
add_port(id_I3, PORT_IN);
|
||||||
|
add_port(id_I4, PORT_IN);
|
||||||
|
add_port(id_RAM_I, PORT_IN);
|
||||||
|
add_port(id_OUT, PORT_OUT);
|
||||||
|
add_port(id_RAM_O, PORT_OUT);
|
||||||
|
add_port(id_EN, PORT_IN);
|
||||||
|
add_port(id_CLK, PORT_IN);
|
||||||
|
add_port(id_SR, PORT_IN);
|
||||||
|
if (type == id_CPE_HALF_L) {
|
||||||
|
add_port(id_COUTY1, PORT_OUT);
|
||||||
|
}
|
||||||
|
} else if (type.in(id_CC_BUFG)) {
|
||||||
|
add_port(id_I, PORT_IN);
|
||||||
|
add_port(id_O, PORT_OUT);
|
||||||
|
} else {
|
||||||
|
log_error("Trying to create unknown cell type %s\n", type.c_str(ctx));
|
||||||
|
}
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &out, const ConfigWord &cw)
|
||||||
|
{
|
||||||
|
out << cw.name << " " << to_string(cw.value) << std::endl;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &out, const TileConfig &tc)
|
||||||
|
{
|
||||||
|
for (const auto &cword : tc.cwords)
|
||||||
|
out << cword;
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TileConfig::add_word(const std::string &name, const std::vector<bool> &value)
|
||||||
|
{
|
||||||
|
if (!added.count(name)) {
|
||||||
|
cwords.push_back({name, value});
|
||||||
|
added.emplace(name, value);
|
||||||
|
} else {
|
||||||
|
auto val = added.at(name);
|
||||||
|
if (val != value)
|
||||||
|
log_error("Trying to add value to already assigned word %s\n", name.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string TileConfig::to_string() const
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << *this;
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
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 &ser : cc.serdes) {
|
||||||
|
if (!ser.second.empty()) {
|
||||||
|
out << ".serdes " << ser.first << " " << std::endl;
|
||||||
|
out << ser.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 % 40 == 39)
|
||||||
|
out << std::endl;
|
||||||
|
else
|
||||||
|
out << " ";
|
||||||
|
}
|
||||||
|
out.flags(f);
|
||||||
|
out << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* 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);
|
||||||
|
|
||||||
|
struct TileConfig
|
||||||
|
{
|
||||||
|
std::vector<ConfigWord> cwords;
|
||||||
|
std::map<std::string, std::vector<bool>> added;
|
||||||
|
|
||||||
|
void add_word(const std::string &name, const std::vector<bool> &value);
|
||||||
|
|
||||||
|
std::string to_string() const;
|
||||||
|
|
||||||
|
bool empty() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &out, const TileConfig &tc);
|
||||||
|
|
||||||
|
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> serdes;
|
||||||
|
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);
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct GateMateBelPinConstraintPOD {
|
||||||
|
int32_t name;
|
||||||
|
int16_t constr_x;
|
||||||
|
int16_t constr_y;
|
||||||
|
int16_t constr_z;
|
||||||
|
int16_t dummy;
|
||||||
|
});
|
||||||
|
|
||||||
|
NPNR_PACKED_STRUCT(struct GateMateBelExtraDataPOD { RelSlice<GateMateBelPinConstraintPOD> constraints; });
|
||||||
|
|
||||||
|
enum MuxFlags
|
||||||
|
{
|
||||||
|
MUX_INVERT = 1,
|
||||||
|
MUX_VISIBLE = 2,
|
||||||
|
MUX_CONFIG = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum PipExtra
|
||||||
|
{
|
||||||
|
PIP_EXTRA_MUX = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum CPEFunction
|
||||||
|
{
|
||||||
|
C_ADDF = 1,
|
||||||
|
C_ADDF2 = 2,
|
||||||
|
C_MULT = 3,
|
||||||
|
C_MX4 = 4,
|
||||||
|
C_EN_CIN = 5,
|
||||||
|
C_CONCAT = 6,
|
||||||
|
C_ADDCIN = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum ClusterPlacement
|
||||||
|
{
|
||||||
|
PLACE_DB_CONSTR = 32,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PllCfgRecord
|
||||||
|
{
|
||||||
|
double weight;
|
||||||
|
double f_core;
|
||||||
|
double f_dco;
|
||||||
|
double f_core_delta;
|
||||||
|
double core_weight;
|
||||||
|
int32_t K;
|
||||||
|
int32_t N1;
|
||||||
|
int32_t N2;
|
||||||
|
int32_t M1;
|
||||||
|
int32_t M2;
|
||||||
|
int32_t PDIV1;
|
||||||
|
};
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,419 @@
|
||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
#include "placer_heap.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);
|
||||||
|
for (const auto &pad : ctx->package_info->pads) {
|
||||||
|
available_pads.emplace(IdString(pad.package_pin));
|
||||||
|
BelId bel = ctx->getBelByName(IdStringList::concat(IdString(pad.tile), IdString(pad.bel)));
|
||||||
|
bel_to_pad.emplace(bel, &pad);
|
||||||
|
}
|
||||||
|
for (auto bel : ctx->getBels()) {
|
||||||
|
auto *ptr = bel_extra_data(bel);
|
||||||
|
std::map<IdString, const GateMateBelPinConstraintPOD *> pins;
|
||||||
|
for (const auto &p : ptr->constraints)
|
||||||
|
pins.emplace(IdString(p.name), &p);
|
||||||
|
pin_to_constr.emplace(bel, pins);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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).in(id_CPE_HALF, id_CPE_HALF_L, id_CPE_HALF_U)) {
|
||||||
|
Loc loc = ctx->getBelLocation(bel);
|
||||||
|
const CellInfo *adj_half = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(loc.x, loc.y, loc.z == 1 ? 0 : 1)));
|
||||||
|
if (adj_half) {
|
||||||
|
const auto &half_data = fast_cell_info.at(cell->flat_index);
|
||||||
|
if (half_data.dff_used) {
|
||||||
|
const auto &adj_data = fast_cell_info.at(adj_half->flat_index);
|
||||||
|
if (adj_data.dff_used) {
|
||||||
|
if (adj_data.ff_config != half_data.ff_config)
|
||||||
|
return false;
|
||||||
|
if (adj_data.ff_en != half_data.ff_en)
|
||||||
|
return false;
|
||||||
|
if (adj_data.ff_clk != half_data.ff_clk)
|
||||||
|
return false;
|
||||||
|
if (adj_data.ff_sr != half_data.ff_sr)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Loc GateMateImpl::getRelativeConstraint(Loc &root_loc, IdString id) const
|
||||||
|
{
|
||||||
|
Loc child_loc;
|
||||||
|
BelId root_bel = ctx->getBelByLocation(root_loc);
|
||||||
|
if (pin_to_constr.count(root_bel)) {
|
||||||
|
auto &constr = pin_to_constr.at(root_bel);
|
||||||
|
if (constr.count(id)) {
|
||||||
|
auto &p = constr.at(id);
|
||||||
|
child_loc.x = root_loc.x + p->constr_x;
|
||||||
|
child_loc.y = root_loc.y + p->constr_y;
|
||||||
|
child_loc.z = p->constr_z;
|
||||||
|
} else {
|
||||||
|
log_error("Constrain info not available for pin.\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log_error("Bel info not available for constraints.\n");
|
||||||
|
}
|
||||||
|
return child_loc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GateMateImpl::getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc,
|
||||||
|
std::vector<std::pair<CellInfo *, BelId>> &placement) const
|
||||||
|
{
|
||||||
|
for (auto child : cluster->constr_children) {
|
||||||
|
Loc child_loc;
|
||||||
|
if (child->constr_z >= PLACE_DB_CONSTR) {
|
||||||
|
child_loc = getRelativeConstraint(root_loc, IdString(child->constr_z - PLACE_DB_CONSTR));
|
||||||
|
} else {
|
||||||
|
child_loc.x = root_loc.x + child->constr_x;
|
||||||
|
child_loc.y = root_loc.y + child->constr_y;
|
||||||
|
child_loc.z = child->constr_abs_z ? child->constr_z : (root_loc.z + child->constr_z);
|
||||||
|
}
|
||||||
|
BelId child_bel = ctx->getBelByLocation(child_loc);
|
||||||
|
if (child_bel == BelId() || !this->isValidBelForCellType(child->type, child_bel))
|
||||||
|
return false;
|
||||||
|
placement.emplace_back(child, child_bel);
|
||||||
|
if (!getChildPlacement(child, child_loc, placement))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GateMateImpl::getClusterPlacement(ClusterId cluster, BelId root_bel,
|
||||||
|
std::vector<std::pair<CellInfo *, BelId>> &placement) const
|
||||||
|
{
|
||||||
|
CellInfo *root_cell = get_cluster_root(ctx, cluster);
|
||||||
|
placement.clear();
|
||||||
|
NPNR_ASSERT(root_bel != BelId());
|
||||||
|
Loc root_loc = ctx->getBelLocation(root_bel);
|
||||||
|
if (root_cell->constr_abs_z) {
|
||||||
|
// Coerce root to absolute z constraint
|
||||||
|
root_loc.z = root_cell->constr_z;
|
||||||
|
root_bel = ctx->getBelByLocation(root_loc);
|
||||||
|
if (root_bel == BelId() || !this->isValidBelForCellType(root_cell->type, root_bel))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
placement.emplace_back(root_cell, root_bel);
|
||||||
|
return getChildPlacement(root_cell, root_loc, placement);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GateMateImpl::need_inversion(CellInfo *cell, IdString port)
|
||||||
|
{
|
||||||
|
PortRef sink;
|
||||||
|
sink.cell = cell;
|
||||||
|
sink.port = port;
|
||||||
|
|
||||||
|
NetInfo *net_info = cell->getPort(port);
|
||||||
|
if (!net_info)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
WireId src_wire = ctx->getNetinfoSourceWire(net_info);
|
||||||
|
WireId dst_wire = ctx->getNetinfoSinkWire(net_info, sink, 0);
|
||||||
|
|
||||||
|
if (src_wire == WireId())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
WireId cursor = dst_wire;
|
||||||
|
bool invert = false;
|
||||||
|
while (cursor != WireId() && cursor != src_wire) {
|
||||||
|
auto it = net_info->wires.find(cursor);
|
||||||
|
|
||||||
|
if (it == net_info->wires.end())
|
||||||
|
break;
|
||||||
|
|
||||||
|
PipId pip = it->second.pip;
|
||||||
|
if (pip == PipId())
|
||||||
|
break;
|
||||||
|
|
||||||
|
invert ^= ctx->isPipInverting(pip);
|
||||||
|
cursor = ctx->getPipSrcWire(pip);
|
||||||
|
}
|
||||||
|
|
||||||
|
return invert;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GateMateImpl::update_cpe_lt(CellInfo *cell, IdString port, IdString init)
|
||||||
|
{
|
||||||
|
unsigned init_val = int_or_default(cell->params, init);
|
||||||
|
bool invert = need_inversion(cell, port);
|
||||||
|
if (invert) {
|
||||||
|
if (port.in(id_IN1, id_IN3))
|
||||||
|
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 GateMateImpl::update_cpe_inv(CellInfo *cell, IdString port, IdString param)
|
||||||
|
{
|
||||||
|
unsigned init_val = int_or_default(cell->params, param);
|
||||||
|
bool invert = need_inversion(cell, port);
|
||||||
|
if (invert) {
|
||||||
|
cell->params[param] = Property(3 - init_val, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GateMateImpl::update_cpe_mux(CellInfo *cell, IdString port, IdString param, int bit)
|
||||||
|
{
|
||||||
|
// Mux inversion data is contained in other CPE half
|
||||||
|
Loc l = ctx->getBelLocation(cell->bel);
|
||||||
|
CellInfo *cell_l = ctx->getBoundBelCell(ctx->getBelByLocation(Loc(l.x, l.y, 1)));
|
||||||
|
unsigned init_val = int_or_default(cell_l->params, param);
|
||||||
|
bool invert = need_inversion(cell, port);
|
||||||
|
if (invert) {
|
||||||
|
int old = (init_val >> bit) & 1;
|
||||||
|
int val = (init_val & (~(1 << bit) & 0xf)) | ((!old) << bit);
|
||||||
|
cell_l->params[param] = Property(val, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GateMateImpl::rename_param(CellInfo *cell, IdString name, IdString new_name, int width)
|
||||||
|
{
|
||||||
|
if (cell->params.count(name)) {
|
||||||
|
cell->params[new_name] = Property(int_or_default(cell->params, name, 0), width);
|
||||||
|
cell->unsetParam(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GateMateImpl::prePlace() { assign_cell_info(); }
|
||||||
|
|
||||||
|
void GateMateImpl::postPlace()
|
||||||
|
{
|
||||||
|
ctx->assignArchInfo();
|
||||||
|
for (auto &cell : ctx->cells) {
|
||||||
|
if (cell.second->type.in(id_CPE_HALF, id_CPE_HALF_U, id_CPE_HALF_L)) {
|
||||||
|
Loc l = ctx->getBelLocation(cell.second->bel);
|
||||||
|
if (l.z == 0) { // CPE_HALF_U
|
||||||
|
if (cell.second->params.count(id_C_O) && int_or_default(cell.second->params, id_C_O, 0) == 0)
|
||||||
|
cell.second->params[id_C_2D_IN] = Property(1, 1);
|
||||||
|
rename_param(cell.second.get(), id_C_O, id_C_O2, 2);
|
||||||
|
rename_param(cell.second.get(), id_C_RAM_I, id_C_RAM_I2, 1);
|
||||||
|
rename_param(cell.second.get(), id_C_RAM_O, id_C_RAM_O2, 1);
|
||||||
|
cell.second->type = id_CPE_HALF_U;
|
||||||
|
} else { // CPE_HALF_L
|
||||||
|
if (!cell.second->params.count(id_INIT_L20))
|
||||||
|
cell.second->params[id_INIT_L20] = Property(0b1100, 4);
|
||||||
|
rename_param(cell.second.get(), id_C_O, id_C_O1, 2);
|
||||||
|
rename_param(cell.second.get(), id_INIT_L00, id_INIT_L02, 4);
|
||||||
|
rename_param(cell.second.get(), id_INIT_L01, id_INIT_L03, 4);
|
||||||
|
rename_param(cell.second.get(), id_INIT_L10, id_INIT_L11, 4);
|
||||||
|
rename_param(cell.second.get(), id_C_RAM_I, id_C_RAM_I1, 1);
|
||||||
|
rename_param(cell.second.get(), id_C_RAM_O, id_C_RAM_O1, 1);
|
||||||
|
cell.second->type = id_CPE_HALF_L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GateMateImpl::postRoute()
|
||||||
|
{
|
||||||
|
ctx->assignArchInfo();
|
||||||
|
// Update configuration bits based on signal inversion
|
||||||
|
for (auto &cell : ctx->cells) {
|
||||||
|
if (cell.second->type.in(id_CPE_HALF_U)) {
|
||||||
|
uint8_t func = int_or_default(cell.second->params, id_C_FUNCTION, 0);
|
||||||
|
if (func != C_MX4) {
|
||||||
|
update_cpe_lt(cell.second.get(), id_IN1, id_INIT_L00);
|
||||||
|
update_cpe_lt(cell.second.get(), id_IN2, id_INIT_L00);
|
||||||
|
update_cpe_lt(cell.second.get(), id_IN3, id_INIT_L01);
|
||||||
|
update_cpe_lt(cell.second.get(), id_IN4, id_INIT_L01);
|
||||||
|
} else {
|
||||||
|
update_cpe_mux(cell.second.get(), id_IN1, id_INIT_L11, 0);
|
||||||
|
update_cpe_mux(cell.second.get(), id_IN2, id_INIT_L11, 1);
|
||||||
|
update_cpe_mux(cell.second.get(), id_IN3, id_INIT_L11, 2);
|
||||||
|
update_cpe_mux(cell.second.get(), id_IN4, id_INIT_L11, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (cell.second->type.in(id_CPE_HALF_L)) {
|
||||||
|
update_cpe_lt(cell.second.get(), id_IN1, id_INIT_L02);
|
||||||
|
update_cpe_lt(cell.second.get(), id_IN2, id_INIT_L02);
|
||||||
|
update_cpe_lt(cell.second.get(), id_IN3, id_INIT_L03);
|
||||||
|
update_cpe_lt(cell.second.get(), id_IN4, id_INIT_L03);
|
||||||
|
}
|
||||||
|
if (cell.second->type.in(id_CPE_HALF_U, id_CPE_HALF_L)) {
|
||||||
|
update_cpe_inv(cell.second.get(), id_CLK, id_C_CPE_CLK);
|
||||||
|
update_cpe_inv(cell.second.get(), id_EN, id_C_CPE_EN);
|
||||||
|
bool set = int_or_default(cell.second->params, id_C_EN_SR, 0) == 1;
|
||||||
|
if (set)
|
||||||
|
update_cpe_inv(cell.second.get(), id_SR, id_C_CPE_SET);
|
||||||
|
else
|
||||||
|
update_cpe_inv(cell.second.get(), id_SR, id_C_CPE_RES);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Sanity check
|
||||||
|
for (auto &c : ctx->cells) {
|
||||||
|
CellInfo *cell = c.second.get();
|
||||||
|
if (!cell->type.in(id_CPE_HALF_U, id_CPE_HALF_L)) {
|
||||||
|
for (auto port : cell->ports) {
|
||||||
|
if (need_inversion(cell, port.first)) {
|
||||||
|
log_error("Unhandled cell '%s' of type '%s' port '%s'\n", cell->name.c_str(ctx),
|
||||||
|
cell->type.c_str(ctx), port.first.c_str(ctx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print_utilisation(ctx);
|
||||||
|
|
||||||
|
const ArchArgs &args = ctx->args;
|
||||||
|
if (args.options.count("out")) {
|
||||||
|
write_bitstream(args.device, args.options.at("out"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GateMateImpl::configurePlacerHeap(PlacerHeapCfg &cfg)
|
||||||
|
{
|
||||||
|
cfg.beta = 0.5;
|
||||||
|
cfg.placeAllAtOnce = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GateMateImpl::assign_cell_info()
|
||||||
|
{
|
||||||
|
fast_cell_info.resize(ctx->cells.size());
|
||||||
|
for (auto &cell : ctx->cells) {
|
||||||
|
CellInfo *ci = cell.second.get();
|
||||||
|
auto &fc = fast_cell_info.at(ci->flat_index);
|
||||||
|
if (ci->type.in(id_CPE_HALF, id_CPE_HALF_U, id_CPE_HALF_L)) {
|
||||||
|
fc.signal_used = int_or_default(ci->params, id_C_O, -1);
|
||||||
|
fc.ff_en = ci->getPort(id_EN);
|
||||||
|
fc.ff_clk = ci->getPort(id_CLK);
|
||||||
|
fc.ff_sr = ci->getPort(id_SR);
|
||||||
|
fc.ff_config = 0;
|
||||||
|
if (fc.signal_used == 0) {
|
||||||
|
fc.ff_config |= int_or_default(ci->params, id_C_CPE_EN, 0);
|
||||||
|
fc.ff_config <<= 2;
|
||||||
|
fc.ff_config |= int_or_default(ci->params, id_C_CPE_CLK, 0);
|
||||||
|
fc.ff_config <<= 2;
|
||||||
|
fc.ff_config |= int_or_default(ci->params, id_C_CPE_RES, 0);
|
||||||
|
fc.ff_config <<= 2;
|
||||||
|
fc.ff_config |= int_or_default(ci->params, id_C_CPE_SET, 0);
|
||||||
|
fc.ff_config <<= 2;
|
||||||
|
fc.ff_config |= int_or_default(ci->params, id_C_EN_SR, 0);
|
||||||
|
fc.ff_config <<= 1;
|
||||||
|
fc.ff_config |= int_or_default(ci->params, id_C_L_D, 0);
|
||||||
|
fc.ff_config <<= 1;
|
||||||
|
fc.ff_config |= int_or_default(ci->params, id_FF_INIT, 0);
|
||||||
|
fc.dff_used = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 if (cell_type.in(id_CPE_HALF_U, id_CPE_HALF_L, id_CPE_HALF))
|
||||||
|
return id_CPE_HALF;
|
||||||
|
else
|
||||||
|
return cell_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
BelBucketId GateMateImpl::getBelBucketForBel(BelId bel) const
|
||||||
|
{
|
||||||
|
IdString bel_type = ctx->getBelType(bel);
|
||||||
|
if (bel_type.in(id_CPE_HALF_U, id_CPE_HALF_L))
|
||||||
|
return id_CPE_HALF;
|
||||||
|
return bel_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 if (bel_type == id_CPE_HALF_U)
|
||||||
|
return cell_type.in(id_CPE_HALF_U, id_CPE_HALF);
|
||||||
|
else if (bel_type == id_CPE_HALF_L)
|
||||||
|
return cell_type.in(id_CPE_HALF_L, id_CPE_HALF);
|
||||||
|
else
|
||||||
|
return (bel_type == cell_type);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GateMateImpl::isPipInverting(PipId pip) const
|
||||||
|
{
|
||||||
|
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 GateMateBelExtraDataPOD *GateMateImpl::bel_extra_data(BelId bel) const
|
||||||
|
{
|
||||||
|
return reinterpret_cast<const GateMateBelExtraDataPOD *>(chip_bel_info(ctx->chip_info, bel).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
|
||||||
|
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
* 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 pack() override;
|
||||||
|
|
||||||
|
void prePlace() 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;
|
||||||
|
|
||||||
|
bool getClusterPlacement(ClusterId cluster, BelId root_bel,
|
||||||
|
std::vector<std::pair<CellInfo *, BelId>> &placement) const override;
|
||||||
|
|
||||||
|
IdString getBelBucketForCellType(IdString cell_type) const override;
|
||||||
|
bool isValidBelForCellType(IdString cell_type, BelId bel) const override;
|
||||||
|
BelBucketId getBelBucketForBel(BelId bel) const override;
|
||||||
|
|
||||||
|
Loc getRelativeConstraint(Loc &root_loc, IdString id) const;
|
||||||
|
|
||||||
|
void configurePlacerHeap(PlacerHeapCfg &cfg) override;
|
||||||
|
|
||||||
|
bool isPipInverting(PipId pip) const override;
|
||||||
|
|
||||||
|
std::set<IdString> available_pads;
|
||||||
|
std::map<BelId, const PadInfoPOD *> bel_to_pad;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool getChildPlacement(const BaseClusterInfo *cluster, Loc root_loc,
|
||||||
|
std::vector<std::pair<CellInfo *, BelId>> &placement) const;
|
||||||
|
|
||||||
|
void write_bitstream(const std::string &device, const std::string &filename);
|
||||||
|
|
||||||
|
void parse_ccf(const std::string &filename);
|
||||||
|
|
||||||
|
void assign_cell_info();
|
||||||
|
bool need_inversion(CellInfo *cell, IdString port);
|
||||||
|
void update_cpe_lt(CellInfo *cell, IdString port, IdString init);
|
||||||
|
void update_cpe_inv(CellInfo *cell, IdString port, IdString param);
|
||||||
|
void update_cpe_mux(CellInfo *cell, IdString port, IdString param, int bit);
|
||||||
|
void rename_param(CellInfo *cell, IdString name, IdString new_name, int width);
|
||||||
|
|
||||||
|
const GateMateBelExtraDataPOD *bel_extra_data(BelId bel) const;
|
||||||
|
|
||||||
|
struct GateMateCellInfo
|
||||||
|
{
|
||||||
|
// slice info
|
||||||
|
const NetInfo *ff_en = nullptr, *ff_clk = nullptr, *ff_sr = nullptr;
|
||||||
|
int ff_config = 0;
|
||||||
|
int signal_used = -1;
|
||||||
|
bool dff_used = false;
|
||||||
|
};
|
||||||
|
std::vector<GateMateCellInfo> fast_cell_info;
|
||||||
|
std::map<BelId, std::map<IdString, const GateMateBelPinConstraintPOD *>> pin_to_constr;
|
||||||
|
};
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,84 @@
|
||||||
|
/*
|
||||||
|
* 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_UTIL_H
|
||||||
|
#define GATEMATE_UTIL_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <set>
|
||||||
|
#include <string>
|
||||||
|
#include "nextpnr.h"
|
||||||
|
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename KeyType>
|
||||||
|
int extract_bits(const dict<KeyType, Property> &ct, const KeyType &key, int start, int bits, int def = 0)
|
||||||
|
{
|
||||||
|
Property extr = get_or_default(ct, key, Property()).extract(start, bits);
|
||||||
|
|
||||||
|
if (extr.is_string) {
|
||||||
|
try {
|
||||||
|
return std::stoi(extr.as_string());
|
||||||
|
} catch (std::invalid_argument &e) {
|
||||||
|
log_error("Expecting numeric value but got '%s'.\n", extr.as_string().c_str());
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
return extr.as_int64();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::vector<std::vector<T>> splitNestedVector(const std::vector<std::vector<T>> &input, size_t maxSize = 8)
|
||||||
|
{
|
||||||
|
std::vector<std::vector<T>> result;
|
||||||
|
|
||||||
|
for (const auto &inner : input) {
|
||||||
|
size_t i = 0;
|
||||||
|
while (i < inner.size()) {
|
||||||
|
size_t end = std::min(i + maxSize, inner.size());
|
||||||
|
result.emplace_back(inner.begin() + i, inner.begin() + end);
|
||||||
|
i = end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,200 @@
|
||||||
|
#
|
||||||
|
# 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
|
||||||
|
|
||||||
|
MUX_INVERT = 1
|
||||||
|
MUX_VISIBLE = 2
|
||||||
|
MUX_CONFIG = 4
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BelPinConstraint(BBAStruct):
|
||||||
|
index: int
|
||||||
|
pin_name: IdString
|
||||||
|
constr_x: int = 0
|
||||||
|
constr_y: int = 0
|
||||||
|
constr_z: int = 0
|
||||||
|
|
||||||
|
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||||
|
pass
|
||||||
|
def serialise(self, context: str, bba: BBAWriter):
|
||||||
|
bba.u32(self.pin_name.index)
|
||||||
|
bba.u16(self.constr_x)
|
||||||
|
bba.u16(self.constr_y)
|
||||||
|
bba.u16(self.constr_z)
|
||||||
|
bba.u16(0)
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class BelExtraData(BBAStruct):
|
||||||
|
constraints: list[BelPinConstraint] = field(default_factory = list)
|
||||||
|
|
||||||
|
def add_constraints(self, pin: IdString, x: int, y: int, z:int):
|
||||||
|
item = BelPinConstraint(len(self.constraints),pin,x,y,z)
|
||||||
|
self.constraints.append(item)
|
||||||
|
|
||||||
|
def serialise_lists(self, context: str, bba: BBAWriter):
|
||||||
|
self.constraints.sort(key=lambda p: p.pin_name.index)
|
||||||
|
bba.label(f"{context}_constraints")
|
||||||
|
for i, t in enumerate(self.constraints):
|
||||||
|
t.serialise(f"{context}_constraint{i}", bba)
|
||||||
|
pass
|
||||||
|
def serialise(self, context: str, bba: BBAWriter):
|
||||||
|
bba.slice(f"{context}_constraints", len(self.constraints))
|
||||||
|
|
||||||
|
def set_timings(ch):
|
||||||
|
speed = "DEFAULT"
|
||||||
|
tmg = ch.set_speed_grades([speed])
|
||||||
|
|
||||||
|
lut = ch.timing.add_cell_variant(speed, "CPE_HALF_L")
|
||||||
|
lut.add_comb_arc("IN1", "OUT", TimingValue(416, 418)) # IN5 to OUT1
|
||||||
|
lut.add_comb_arc("IN2", "OUT", TimingValue(413, 422)) # IN6 to OUT1
|
||||||
|
lut.add_comb_arc("IN3", "OUT", TimingValue(372, 374)) # IN7 to OUT1
|
||||||
|
lut.add_comb_arc("IN4", "OUT", TimingValue(275, 385)) # IN8 to OUT1
|
||||||
|
|
||||||
|
lut = ch.timing.add_cell_variant(speed, "CPE_HALF_U")
|
||||||
|
lut.add_comb_arc("IN1", "OUT", TimingValue(479, 484)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN2", "OUT", TimingValue(471, 488)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN3", "OUT", TimingValue(446, 449)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN4", "OUT", TimingValue(443, 453)) # to OUT2
|
||||||
|
|
||||||
|
lut = ch.timing.add_cell_variant(speed, "CPE_HALF")
|
||||||
|
lut.add_comb_arc("IN1", "OUT", TimingValue(479, 484)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN2", "OUT", TimingValue(471, 488)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN3", "OUT", TimingValue(446, 449)) # to OUT2
|
||||||
|
lut.add_comb_arc("IN4", "OUT", TimingValue(443, 453)) # to OUT2
|
||||||
|
|
||||||
|
dff = ch.timing.add_cell_variant(speed, "CPE_DFF")
|
||||||
|
dff.add_setup_hold("CLK", "IN1", ClockEdge.RISING, TimingValue(60), TimingValue(50))
|
||||||
|
dff.add_setup_hold("CLK", "IN2", ClockEdge.RISING, TimingValue(60), TimingValue(50))
|
||||||
|
dff.add_setup_hold("CLK", "IN3", ClockEdge.RISING, TimingValue(60), TimingValue(50))
|
||||||
|
dff.add_setup_hold("CLK", "IN4", ClockEdge.RISING, TimingValue(60), TimingValue(50))
|
||||||
|
dff.add_clock_out("CLK", "OUT", 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 sorted(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)
|
||||||
|
if "GPIO" in type_name:
|
||||||
|
tt.create_wire("GPIO.DI", "CPE_VIRTUAL_WIRE")
|
||||||
|
for prim in sorted(die.get_primitives_for_type(type_name)):
|
||||||
|
bel = tt.create_bel(prim.name, prim.type, prim.z)
|
||||||
|
extra = BelExtraData()
|
||||||
|
for constr in sorted(die.get_pins_constraint(type_name, prim.name, prim.type)):
|
||||||
|
extra.add_constraints(ch.strs.id(constr.name),constr.rel_x,constr.rel_y,0 if constr.pin_num==2 else 1)
|
||||||
|
bel.extra_data = extra
|
||||||
|
if prim.name == "GPIO":
|
||||||
|
tt.add_bel_pin(bel, "DI", "GPIO.DI", PinType.INPUT)
|
||||||
|
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)
|
||||||
|
if mux.name:
|
||||||
|
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 "GPIO" in type_name:
|
||||||
|
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):
|
||||||
|
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, pad.flags)
|
||||||
|
|
||||||
|
ch.write_bba(args.bba)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
@ -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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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_HALF_L.index:
|
||||||
|
el.x1 = loc.x + 0.20;
|
||||||
|
el.x2 = el.x1 + 0.20;
|
||||||
|
el.y1 = loc.y + 0.25;
|
||||||
|
el.y2 = el.y1 + 0.20;
|
||||||
|
g.push_back(el);
|
||||||
|
break;
|
||||||
|
case id_CPE_HALF_U.index:
|
||||||
|
el.x1 = loc.x + 0.20;
|
||||||
|
el.x2 = el.x1 + 0.20;
|
||||||
|
el.y1 = loc.y + 0.55;
|
||||||
|
el.y2 = el.y1 + 0.20;
|
||||||
|
g.push_back(el);
|
||||||
|
break;
|
||||||
|
case id_RAM.index:
|
||||||
|
el.x1 = loc.x + 0.70;
|
||||||
|
el.x2 = el.x1 + 0.20;
|
||||||
|
el.y1 = loc.y + 0.20;
|
||||||
|
el.y2 = el.y1 + 15.60;
|
||||||
|
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
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,78 @@
|
||||||
|
/*
|
||||||
|
* 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_io_sel();
|
||||||
|
void pack_cpe();
|
||||||
|
void pack_addf();
|
||||||
|
void pack_bufg();
|
||||||
|
void sort_bufg();
|
||||||
|
void insert_pll_bufg();
|
||||||
|
void pack_pll();
|
||||||
|
void pack_misc();
|
||||||
|
void pack_constants();
|
||||||
|
void pack_ram();
|
||||||
|
|
||||||
|
void remove_constants();
|
||||||
|
void remove_not_used();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void dff_to_cpe(CellInfo *dff, CellInfo *cpe);
|
||||||
|
void insert_bufg(CellInfo *cell, IdString port);
|
||||||
|
void disconnect_if_gnd(CellInfo *cell, IdString input);
|
||||||
|
void pll_out(CellInfo *cell, IdString origPort, Loc fixed);
|
||||||
|
|
||||||
|
PllCfgRecord get_pll_settings(double f_ref, double f_core, int mode, int low_jitter, bool pdiv0_mux, bool feedback);
|
||||||
|
|
||||||
|
CellInfo *move_ram_i(CellInfo *cell, IdString origPort, bool place = true);
|
||||||
|
CellInfo *move_ram_o(CellInfo *cell, IdString origPort, bool place = true);
|
||||||
|
CellInfo *move_ram_i_fixed(CellInfo *cell, IdString origPort, Loc fixed);
|
||||||
|
CellInfo *move_ram_o_fixed(CellInfo *cell, IdString origPort, Loc fixed);
|
||||||
|
CellInfo *move_ram_io(CellInfo *cell, IdString iPort, IdString oPort, bool place = true);
|
||||||
|
uint8_t ram_ctrl_signal(CellInfo *cell, IdString port, bool alt);
|
||||||
|
uint8_t ram_clk_signal(CellInfo *cell, IdString port);
|
||||||
|
bool is_gpio_valid_dff(CellInfo *dff);
|
||||||
|
BelId get_bank_cpe(int bank);
|
||||||
|
// Cell creating
|
||||||
|
CellInfo *create_cell_ptr(IdString type, IdString name);
|
||||||
|
void flush_cells();
|
||||||
|
void pack_ram_cell(CellInfo &ci, CellInfo *cell, int num, bool is_split);
|
||||||
|
|
||||||
|
pool<IdString> packed_cells;
|
||||||
|
std::map<NetInfo *, int> global_signals;
|
||||||
|
|
||||||
|
Context *ctx;
|
||||||
|
GateMateImpl *uarch;
|
||||||
|
|
||||||
|
HimbaechelHelpers h;
|
||||||
|
};
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
#endif
|
||||||
|
|
@ -0,0 +1,352 @@
|
||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
USING_NEXTPNR_NAMESPACE;
|
||||||
|
|
||||||
|
double calculate_delta_stepsize(double num)
|
||||||
|
{
|
||||||
|
std::ostringstream out;
|
||||||
|
out << std::fixed << std::setprecision(4) << num;
|
||||||
|
std::string str = out.str();
|
||||||
|
switch (4 - (str.size() - str.find_last_not_of('0') - 1)) {
|
||||||
|
case 4:
|
||||||
|
return 0.0001;
|
||||||
|
case 3:
|
||||||
|
return 0.001;
|
||||||
|
case 2:
|
||||||
|
return 0.01;
|
||||||
|
case 1:
|
||||||
|
return 0.1;
|
||||||
|
default:
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int getDCO_optimized_value(int f_dco_min, int f_dco_max, double f_dco)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
optimization parameters
|
||||||
|
DCO_OPT:
|
||||||
|
1: Optimized to lower DCO frequency.
|
||||||
|
2: Optimized to the middle of the DCO range (DEFAULT)
|
||||||
|
3: Optimized to upper DCO frequency.
|
||||||
|
*/
|
||||||
|
const int DCO_OPT = 2; // 1: to lower DCO, 2: to DCO middle range, 3: to upper DCO, default = 2
|
||||||
|
const int DCO_FREQ_OPT_FACTOR = 24; // proportional factor btw. frequency match and dco optimization
|
||||||
|
|
||||||
|
switch (DCO_OPT) {
|
||||||
|
case 1:
|
||||||
|
return round(abs(f_dco - f_dco_min)) * DCO_FREQ_OPT_FACTOR;
|
||||||
|
case 3:
|
||||||
|
return round(abs(f_dco - f_dco_max)) * DCO_FREQ_OPT_FACTOR;
|
||||||
|
default: {
|
||||||
|
int f_dco_middle = (f_dco_max + f_dco_min) / 2; // Middle of DCO Range in MHz
|
||||||
|
return round(abs(f_dco - f_dco_middle)) * DCO_FREQ_OPT_FACTOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_M1_M2(double f_core, double f_core_delta, PllCfgRecord &setting, double max_input_freq)
|
||||||
|
{
|
||||||
|
// precise output frequency preference, highest priority
|
||||||
|
// the larger value the higher priority
|
||||||
|
const int OUT_FREQ_OPT_FACTOR = 4200000;
|
||||||
|
|
||||||
|
std::vector<PllCfgRecord> res_arr;
|
||||||
|
for (int M1 = 1; M1 <= 64; M1++) {
|
||||||
|
for (int M2 = 1; M2 <= 1024; M2++) {
|
||||||
|
if (((M1 * M2) % 2) != 0) {
|
||||||
|
// M1*M2-->even number(except one);
|
||||||
|
// this is important to ensure 90 degree phase shifting of clock output
|
||||||
|
if (M1 * M2 != 1)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
double f_core_local = setting.f_dco / (2 * setting.PDIV1 * M1 * M2);
|
||||||
|
if (f_core_local > max_input_freq / 4)
|
||||||
|
continue; // f_core max limit
|
||||||
|
if ((f_core_local - f_core) < -f_core_delta)
|
||||||
|
break; // lower limit jump to parent loop
|
||||||
|
if (abs(f_core_local - f_core) > f_core_delta)
|
||||||
|
continue; // reg limit continue
|
||||||
|
if (setting.f_dco / setting.PDIV1 > max_input_freq)
|
||||||
|
continue; // M1 input freq limit
|
||||||
|
if ((setting.f_dco / setting.PDIV1) / M1 > max_input_freq / 2)
|
||||||
|
continue; // M2 input freq limit
|
||||||
|
|
||||||
|
PllCfgRecord tmp;
|
||||||
|
tmp.f_core = f_core_local;
|
||||||
|
tmp.M1 = M1;
|
||||||
|
tmp.M2 = M2;
|
||||||
|
tmp.weight = abs(f_core_local - f_core) * OUT_FREQ_OPT_FACTOR;
|
||||||
|
res_arr.push_back(tmp);
|
||||||
|
|
||||||
|
// frequency match
|
||||||
|
if (abs(f_core_local - f_core) <= f_core_delta) {
|
||||||
|
auto it = std::min_element(
|
||||||
|
res_arr.begin(), res_arr.end(),
|
||||||
|
[](const PllCfgRecord &a, const PllCfgRecord &b) { return a.weight < b.weight; });
|
||||||
|
size_t index = std::distance(res_arr.begin(), it);
|
||||||
|
setting.M1 = res_arr[index].M1;
|
||||||
|
setting.M2 = res_arr[index].M2;
|
||||||
|
setting.f_core = res_arr[index].f_core;
|
||||||
|
setting.f_core_delta = abs(res_arr[index].f_core - f_core);
|
||||||
|
setting.core_weight = res_arr[index].weight + setting.M1 + setting.M2 + setting.N1 + setting.N2 +
|
||||||
|
setting.K + setting.PDIV1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_DCO_ext_feedback(double f_core, double f_ref, PllCfgRecord &setting, int f_dco_min, int f_dco_max,
|
||||||
|
double f_dco, double max_input_freq)
|
||||||
|
{
|
||||||
|
std::vector<PllCfgRecord> res_arr;
|
||||||
|
|
||||||
|
for (int M1 = 1; M1 <= 64; M1++) {
|
||||||
|
for (int M2 = 1; M2 <= 1024; M2++) {
|
||||||
|
if (((M1 * M2) % 2) != 0) {
|
||||||
|
// M1*M2-->even number(except one);
|
||||||
|
// this is important to ensure 90 degree phase shifting of clock output
|
||||||
|
if (M1 * M2 != 1)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
double f_dco_local = (f_ref / setting.K) * 2 * setting.PDIV1 * setting.N1 * setting.N2 * M1 * M2;
|
||||||
|
if (f_dco_local > f_dco_max)
|
||||||
|
break; // upper limit
|
||||||
|
if ((f_dco_local < f_dco_min) || (f_dco_local > f_dco_max))
|
||||||
|
continue; // dco out of range limit
|
||||||
|
if (f_dco_local / setting.PDIV1 > max_input_freq)
|
||||||
|
continue; // M1 input freq limit
|
||||||
|
if ((f_dco_local / setting.PDIV1) / M1 > max_input_freq / 2)
|
||||||
|
continue; // M2 input freq limit
|
||||||
|
|
||||||
|
PllCfgRecord tmp;
|
||||||
|
tmp.f_dco = f_dco_local;
|
||||||
|
tmp.M1 = M1;
|
||||||
|
tmp.M2 = M2;
|
||||||
|
tmp.weight = f_dco_local + getDCO_optimized_value(f_dco_min, f_dco_max, f_dco_local);
|
||||||
|
res_arr.push_back(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = std::min_element(res_arr.begin(), res_arr.end(),
|
||||||
|
[](const PllCfgRecord &a, const PllCfgRecord &b) { return a.weight < b.weight; });
|
||||||
|
size_t index = std::distance(res_arr.begin(), it);
|
||||||
|
setting.M1 = res_arr[index].M1;
|
||||||
|
setting.M2 = res_arr[index].M2;
|
||||||
|
setting.f_dco = res_arr[index].f_dco;
|
||||||
|
}
|
||||||
|
}; // namespace
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
PllCfgRecord GateMatePacker::get_pll_settings(double f_ref, double f_core, int mode, int low_jitter, bool pdiv0_mux,
|
||||||
|
bool feedback)
|
||||||
|
{
|
||||||
|
const int MATCH_LIMIT = 10; // only for low jitter = false
|
||||||
|
// frequency tolerance for low jitter = false, the maximum frequency deviation in MHz from f_core
|
||||||
|
const double DELTA_LIMIT = 0.1;
|
||||||
|
|
||||||
|
double max_input_freq;
|
||||||
|
int f_dco_min, f_dco_max, pdiv1_min;
|
||||||
|
double f_dco;
|
||||||
|
std::vector<PllCfgRecord> pll_cfg_arr;
|
||||||
|
PllCfgRecord pll_cfg_rec;
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case 1: {
|
||||||
|
// low power
|
||||||
|
f_dco_min = 500;
|
||||||
|
f_dco_max = 1000;
|
||||||
|
pdiv1_min = 1;
|
||||||
|
max_input_freq = 1000;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 2: {
|
||||||
|
// economy
|
||||||
|
f_dco_min = 1000;
|
||||||
|
f_dco_max = 2000;
|
||||||
|
pdiv1_min = 2;
|
||||||
|
max_input_freq = 1250;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
// initialize as speed
|
||||||
|
f_dco_min = 1250;
|
||||||
|
f_dco_max = 2500;
|
||||||
|
pdiv1_min = 2;
|
||||||
|
max_input_freq = 1666.67;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
double f_core_par = f_core;
|
||||||
|
|
||||||
|
if (f_ref > 50)
|
||||||
|
log_warning("The PLL input frequency is outside the specified frequency (max 50 MHz ) range\n");
|
||||||
|
|
||||||
|
if (pdiv0_mux && feedback) {
|
||||||
|
double res;
|
||||||
|
if (modf(f_core / f_ref, &res) != 0)
|
||||||
|
log_warning("In this PLL mode f_core can only be greater and multiple of f_ref\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdiv0_mux) {
|
||||||
|
if (f_core > max_input_freq / 4) {
|
||||||
|
f_core = max_input_freq / 4;
|
||||||
|
log_warning("Frequency out of range; PLL max output frequency for mode: %d: %.5f MHz\n", mode,
|
||||||
|
max_input_freq / 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pll_cfg_rec.K = 1;
|
||||||
|
pll_cfg_rec.N1 = 1;
|
||||||
|
pll_cfg_rec.N2 = 1;
|
||||||
|
pll_cfg_rec.M1 = 1;
|
||||||
|
pll_cfg_rec.M2 = 1;
|
||||||
|
pll_cfg_rec.PDIV1 = 2;
|
||||||
|
pll_cfg_rec.f_dco = 0;
|
||||||
|
pll_cfg_rec.f_core_delta = std::numeric_limits<double>::max();
|
||||||
|
pll_cfg_rec.f_core = pll_cfg_rec.f_dco / (2 * pll_cfg_rec.PDIV1);
|
||||||
|
pll_cfg_rec.core_weight = std::numeric_limits<double>::max();
|
||||||
|
pll_cfg_arr.push_back(pll_cfg_rec);
|
||||||
|
|
||||||
|
double f_core_delta_stepsize = calculate_delta_stepsize(f_core);
|
||||||
|
|
||||||
|
int K = 1;
|
||||||
|
int K_max = low_jitter ? 1 : 1024;
|
||||||
|
int match_cnt = 0;
|
||||||
|
int match_delta_cnt = 0;
|
||||||
|
while (K <= K_max) {
|
||||||
|
for (int N1 = 1; N1 <= 64; N1++) {
|
||||||
|
for (int N2 = 1; N2 <= 1024; N2++) {
|
||||||
|
for (int PDIV1 = pdiv1_min; PDIV1 <= 2; PDIV1++) {
|
||||||
|
if (feedback) {
|
||||||
|
// extern feedback
|
||||||
|
int f_core_local = (f_ref / K) * N1 * N2;
|
||||||
|
if (f_core_local > max_input_freq / 4)
|
||||||
|
continue;
|
||||||
|
if (abs(f_core - f_core_local) < pll_cfg_arr[0].f_core_delta) {
|
||||||
|
// search for best frequency match
|
||||||
|
pll_cfg_arr[0].f_core = f_core_local;
|
||||||
|
pll_cfg_arr[0].f_core_delta = abs(f_core - f_core_local);
|
||||||
|
pll_cfg_arr[0].K = K;
|
||||||
|
pll_cfg_arr[0].N1 = N1;
|
||||||
|
pll_cfg_arr[0].N2 = N2;
|
||||||
|
pll_cfg_arr[0].PDIV1 = PDIV1;
|
||||||
|
}
|
||||||
|
if (pll_cfg_arr[0].f_core_delta == 0) {
|
||||||
|
get_DCO_ext_feedback(f_core, f_ref, pll_cfg_arr[0], f_dco_min, f_dco_max, f_dco,
|
||||||
|
max_input_freq);
|
||||||
|
log_info("PLL fout= %.4f MHz (fout error %.5f%% of requested %.4f MHz)\n",
|
||||||
|
pll_cfg_arr[0].f_core,
|
||||||
|
100 - (100 * std::min(pll_cfg_arr[0].f_core, f_core_par) /
|
||||||
|
std::max(pll_cfg_arr[0].f_core, f_core_par)),
|
||||||
|
f_core);
|
||||||
|
return pll_cfg_arr[0];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
f_dco = (f_ref / K) * PDIV1 * N1 * N2;
|
||||||
|
|
||||||
|
if ((f_dco <= f_dco_min) || (f_dco > f_dco_max))
|
||||||
|
continue; // DCO out of range
|
||||||
|
if (f_dco == 0)
|
||||||
|
continue; // DCO = 0
|
||||||
|
if (f_dco / PDIV1 > max_input_freq)
|
||||||
|
continue; // N1 input freq limit
|
||||||
|
if ((f_dco / PDIV1) / N1 > max_input_freq / 2)
|
||||||
|
continue; // N2 input freq limit
|
||||||
|
if (int(f_core) > int(f_dco / (2 * PDIV1)))
|
||||||
|
continue; // > f_core max
|
||||||
|
|
||||||
|
pll_cfg_rec.f_core = 0;
|
||||||
|
pll_cfg_rec.f_dco = f_dco;
|
||||||
|
pll_cfg_rec.f_core_delta = std::numeric_limits<double>::max();
|
||||||
|
pll_cfg_rec.K = K;
|
||||||
|
pll_cfg_rec.N1 = N1;
|
||||||
|
pll_cfg_rec.N2 = N2;
|
||||||
|
pll_cfg_rec.PDIV1 = PDIV1;
|
||||||
|
pll_cfg_rec.M1 = 0;
|
||||||
|
pll_cfg_rec.M2 = 0;
|
||||||
|
pll_cfg_rec.core_weight = std::numeric_limits<double>::max();
|
||||||
|
|
||||||
|
if (!pdiv0_mux) {
|
||||||
|
// f_core = f_dco/2
|
||||||
|
pll_cfg_rec.M1 = 1;
|
||||||
|
pll_cfg_rec.M2 = 1;
|
||||||
|
pll_cfg_rec.f_core = pll_cfg_rec.f_dco / 2;
|
||||||
|
pll_cfg_rec.core_weight = abs(pll_cfg_rec.f_core - f_core);
|
||||||
|
} else {
|
||||||
|
// default clock path
|
||||||
|
// calculate M1, M2 then calculate weight for intern loop feedback
|
||||||
|
bool found = false;
|
||||||
|
double f_core_delta = 0;
|
||||||
|
while (f_core_delta < (round((max_input_freq / 4) - f_ref / K))) {
|
||||||
|
get_M1_M2(f_core, f_core_delta, pll_cfg_rec, max_input_freq);
|
||||||
|
if (pll_cfg_rec.f_core != 0 && pll_cfg_rec.f_core_delta <= f_core_delta) {
|
||||||
|
// best result
|
||||||
|
pll_cfg_rec.core_weight +=
|
||||||
|
pll_cfg_rec.f_dco + getDCO_optimized_value(f_dco_min, f_dco_max, f_dco);
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
if (found)
|
||||||
|
break; // best result found
|
||||||
|
f_core_delta += f_core_delta_stepsize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pll_cfg_arr.push_back(pll_cfg_rec);
|
||||||
|
if (pll_cfg_rec.f_core_delta == 0)
|
||||||
|
match_cnt++;
|
||||||
|
if (pll_cfg_rec.f_core_delta < DELTA_LIMIT)
|
||||||
|
match_delta_cnt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
K++;
|
||||||
|
// only with low_jitter false
|
||||||
|
if (match_cnt > MATCH_LIMIT)
|
||||||
|
break;
|
||||||
|
if ((match_cnt == 0) && (match_delta_cnt > MATCH_LIMIT))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (feedback) {
|
||||||
|
// extern feedback if not exact match pick the best here
|
||||||
|
get_DCO_ext_feedback(f_core, f_ref, pll_cfg_arr[0], f_dco_min, f_dco_max, f_dco, max_input_freq);
|
||||||
|
}
|
||||||
|
auto it =
|
||||||
|
std::min_element(pll_cfg_arr.begin(), pll_cfg_arr.end(), [](const PllCfgRecord &a, const PllCfgRecord &b) {
|
||||||
|
return a.core_weight < b.core_weight;
|
||||||
|
});
|
||||||
|
PllCfgRecord val = pll_cfg_arr.at(std::distance(pll_cfg_arr.begin(), it));
|
||||||
|
log_info("PLL fout= %.4f MHz (fout error %.5f%% of requested %.4f MHz)\n", val.f_core,
|
||||||
|
100 - (100 * std::min(val.f_core, f_core_par) / std::max(val.f_core, f_core_par)), f_core);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* 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 <vector>
|
||||||
|
#include "command.h"
|
||||||
|
#include "testing.h"
|
||||||
|
#include "uarch/gatemate/pack.h"
|
||||||
|
#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc"
|
||||||
|
#include "himbaechel_constids.h"
|
||||||
|
|
||||||
|
USING_NEXTPNR_NAMESPACE
|
||||||
|
|
||||||
|
TEST_F(GateMateTest, pack_constants)
|
||||||
|
{
|
||||||
|
GateMatePacker packer(ctx, impl);
|
||||||
|
packer.pack_constants();
|
||||||
|
ASSERT_EQ(ctx->cells.size(), 2LU);
|
||||||
|
packer.remove_constants();
|
||||||
|
ASSERT_EQ(ctx->cells.size(), 0LU);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* 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 <vector>
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
USING_NEXTPNR_NAMESPACE
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
// TODO: Remove for delivery, useful while checking tests
|
||||||
|
log_streams.push_back(std::make_pair(&std::cerr, LogLevel::LOG_MSG));
|
||||||
|
::testing::InitGoogleTest(&argc, argv);
|
||||||
|
return RUN_ALL_TESTS();
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,210 @@
|
||||||
|
/*
|
||||||
|
* 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 "testing.h"
|
||||||
|
#include <vector>
|
||||||
|
#include "command.h"
|
||||||
|
#include "uarch/gatemate/pack.h"
|
||||||
|
#define HIMBAECHEL_CONSTIDS "uarch/gatemate/constids.inc"
|
||||||
|
#include "himbaechel_constids.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
void GateMateTest::SetUp()
|
||||||
|
{
|
||||||
|
init_share_dirname();
|
||||||
|
chipArgs.device = "CCGM1A1";
|
||||||
|
ctx = new Context(chipArgs);
|
||||||
|
ctx->uarch->init(ctx);
|
||||||
|
ctx->late_init();
|
||||||
|
impl = (GateMateImpl *)(ctx->uarch.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void GateMateTest::TearDown() { delete ctx; }
|
||||||
|
|
||||||
|
CellInfo *GateMateTest::create_cell_ptr(IdString type, std::string name)
|
||||||
|
{
|
||||||
|
CellInfo *cell = ctx->createCell(ctx->id(name), type);
|
||||||
|
|
||||||
|
auto add_port = [&](const IdString id, PortType dir) {
|
||||||
|
cell->ports[id].name = id;
|
||||||
|
cell->ports[id].type = dir;
|
||||||
|
};
|
||||||
|
switch (type.index) {
|
||||||
|
case id_CC_IBUF.index:
|
||||||
|
add_port(id_I, PORT_IN);
|
||||||
|
add_port(id_Y, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_OBUF.index:
|
||||||
|
add_port(id_A, PORT_IN);
|
||||||
|
add_port(id_O, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_TOBUF.index:
|
||||||
|
add_port(id_A, PORT_IN);
|
||||||
|
add_port(id_T, PORT_IN);
|
||||||
|
add_port(id_O, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_IOBUF.index:
|
||||||
|
add_port(id_A, PORT_IN);
|
||||||
|
add_port(id_T, PORT_IN);
|
||||||
|
add_port(id_Y, PORT_OUT);
|
||||||
|
add_port(id_IO, PORT_INOUT);
|
||||||
|
break;
|
||||||
|
case id_CC_LVDS_IBUF.index:
|
||||||
|
add_port(id_I_P, PORT_IN);
|
||||||
|
add_port(id_I_N, PORT_IN);
|
||||||
|
add_port(id_Y, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_LVDS_OBUF.index:
|
||||||
|
add_port(id_A, PORT_IN);
|
||||||
|
add_port(id_O_P, PORT_OUT);
|
||||||
|
add_port(id_O_N, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_LVDS_TOBUF.index:
|
||||||
|
add_port(id_A, PORT_IN);
|
||||||
|
add_port(id_T, PORT_IN);
|
||||||
|
add_port(id_O_P, PORT_OUT);
|
||||||
|
add_port(id_O_N, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_LVDS_IOBUF.index:
|
||||||
|
add_port(id_A, PORT_IN);
|
||||||
|
add_port(id_T, PORT_IN);
|
||||||
|
add_port(id_Y, PORT_OUT);
|
||||||
|
add_port(id_IO_P, PORT_INOUT);
|
||||||
|
add_port(id_IO_N, PORT_INOUT);
|
||||||
|
break;
|
||||||
|
case id_CC_IDDR.index:
|
||||||
|
add_port(id_D, PORT_IN);
|
||||||
|
add_port(id_CLK, PORT_IN);
|
||||||
|
add_port(id_Q0, PORT_OUT);
|
||||||
|
add_port(id_Q1, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_ODDR.index:
|
||||||
|
add_port(id_D0, PORT_IN);
|
||||||
|
add_port(id_D1, PORT_IN);
|
||||||
|
add_port(id_CLK, PORT_IN);
|
||||||
|
add_port(id_DDR, PORT_IN);
|
||||||
|
add_port(id_Q, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_DFF.index:
|
||||||
|
add_port(id_D, PORT_IN);
|
||||||
|
add_port(id_CLK, PORT_IN);
|
||||||
|
add_port(id_EN, PORT_IN);
|
||||||
|
add_port(id_SR, PORT_IN);
|
||||||
|
add_port(id_Q, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_DLT.index:
|
||||||
|
add_port(id_D, PORT_IN);
|
||||||
|
add_port(id_G, PORT_IN);
|
||||||
|
add_port(id_SR, PORT_IN);
|
||||||
|
add_port(id_Q, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_L2T4.index:
|
||||||
|
add_port(id_I0, PORT_IN);
|
||||||
|
add_port(id_I1, PORT_IN);
|
||||||
|
add_port(id_I2, PORT_IN);
|
||||||
|
add_port(id_I3, PORT_IN);
|
||||||
|
add_port(id_O, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_L2T5.index:
|
||||||
|
add_port(id_I0, PORT_IN);
|
||||||
|
add_port(id_I1, PORT_IN);
|
||||||
|
add_port(id_I2, PORT_IN);
|
||||||
|
add_port(id_I3, PORT_IN);
|
||||||
|
add_port(id_I4, PORT_IN);
|
||||||
|
add_port(id_O, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_LUT1.index:
|
||||||
|
add_port(id_I0, PORT_IN);
|
||||||
|
add_port(id_O, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_LUT2.index:
|
||||||
|
add_port(id_I0, PORT_IN);
|
||||||
|
add_port(id_I1, PORT_IN);
|
||||||
|
add_port(id_O, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_MX2.index:
|
||||||
|
add_port(id_D0, PORT_IN);
|
||||||
|
add_port(id_D1, PORT_IN);
|
||||||
|
add_port(id_S0, PORT_IN);
|
||||||
|
add_port(id_Y, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_MX4.index:
|
||||||
|
add_port(id_D0, PORT_IN);
|
||||||
|
add_port(id_D1, PORT_IN);
|
||||||
|
add_port(id_D2, PORT_IN);
|
||||||
|
add_port(id_D3, PORT_IN);
|
||||||
|
add_port(id_S0, PORT_IN);
|
||||||
|
add_port(id_S1, PORT_IN);
|
||||||
|
add_port(id_Y, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_ADDF.index:
|
||||||
|
add_port(id_A, PORT_IN);
|
||||||
|
add_port(id_B, PORT_IN);
|
||||||
|
add_port(id_CI, PORT_IN);
|
||||||
|
add_port(id_CO, PORT_OUT);
|
||||||
|
add_port(id_S, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_BUFG.index:
|
||||||
|
add_port(id_I, PORT_IN);
|
||||||
|
add_port(id_O, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_USR_RSTN.index:
|
||||||
|
add_port(id_USR_RSTN, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_PLL_ADV.index:
|
||||||
|
add_port(id_USR_SEL_A_B, PORT_IN);
|
||||||
|
[[fallthrough]];
|
||||||
|
case id_CC_PLL.index:
|
||||||
|
add_port(id_CLK_REF, PORT_IN);
|
||||||
|
add_port(id_USR_CLK_REF, PORT_IN);
|
||||||
|
add_port(id_CLK_FEEDBACK, PORT_IN);
|
||||||
|
add_port(id_USR_LOCKED_STDY_RST, PORT_IN);
|
||||||
|
add_port(id_USR_PLL_LOCKED_STDY, PORT_OUT);
|
||||||
|
add_port(id_USR_PLL_LOCKED, PORT_OUT);
|
||||||
|
add_port(id_CLK0, PORT_OUT);
|
||||||
|
add_port(id_CLK90, PORT_OUT);
|
||||||
|
add_port(id_CLK180, PORT_OUT);
|
||||||
|
add_port(id_CLK270, PORT_OUT);
|
||||||
|
add_port(id_CLK_REF_OUT, PORT_OUT);
|
||||||
|
break;
|
||||||
|
case id_CC_CFG_CTRL.index:
|
||||||
|
for (int i = 0; i < 8; i++)
|
||||||
|
add_port(ctx->idf("DATA[%d]", i), PORT_IN);
|
||||||
|
add_port(id_CLK, PORT_IN);
|
||||||
|
add_port(id_EN, PORT_IN);
|
||||||
|
add_port(id_RECFG, PORT_IN);
|
||||||
|
add_port(id_VALID, PORT_IN);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
log_error("Trying to create unknown cell type %s\n", type.c_str(ctx));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GateMateTest::direct_connect(CellInfo *o_cell, IdString o_port, CellInfo *i_cell, IdString i_port)
|
||||||
|
{
|
||||||
|
NetInfo *net = ctx->createNet(ctx->idf("%s_%s", o_cell->name.c_str(ctx), o_port.c_str(ctx)));
|
||||||
|
o_cell->connectPort(o_port, net);
|
||||||
|
i_cell->connectPort(i_port, net);
|
||||||
|
}
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* 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_TESTING_H
|
||||||
|
#define GATEMATE_TESTING_H
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "nextpnr.h"
|
||||||
|
#include "uarch/gatemate/gatemate.h"
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class GateMateTest : public ::testing::Test
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
virtual void SetUp() override;
|
||||||
|
|
||||||
|
virtual void TearDown() override;
|
||||||
|
|
||||||
|
CellInfo *create_cell_ptr(IdString type, std::string name);
|
||||||
|
void direct_connect(CellInfo *o_cell, IdString o_port, CellInfo *i_cell, IdString i_port);
|
||||||
|
|
||||||
|
ArchArgs chipArgs;
|
||||||
|
Context *ctx;
|
||||||
|
GateMateImpl *impl;
|
||||||
|
};
|
||||||
|
|
||||||
|
NEXTPNR_NAMESPACE_END
|
||||||
|
#endif
|
||||||
Loading…
Reference in New Issue