himbaechel: Add discovery of uarch and chipdb
Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
parent
3cac90a30a
commit
565927dfcc
5
.github/ci/build_himbaechel.sh
vendored
5
.github/ci/build_himbaechel.sh
vendored
@ -12,7 +12,8 @@ function build_nextpnr {
|
|||||||
# We'd ideally use pypy3 for speed (as works locally), but the version
|
# We'd ideally use pypy3 for speed (as works locally), but the version
|
||||||
# our CI Ubuntu provides doesn't like some of the typing stuff
|
# our CI Ubuntu provides doesn't like some of the typing stuff
|
||||||
python3 ../himbaechel/uarch/example/example_arch_gen.py ./example.bba
|
python3 ../himbaechel/uarch/example/example_arch_gen.py ./example.bba
|
||||||
./bba/bbasm --l ./example.bba ./example.bin
|
mkdir -p share/himbaechel/example
|
||||||
|
./bba/bbasm --l ./example.bba share/himbaechel/example/example.bin
|
||||||
popd
|
popd
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,6 +23,6 @@ function run_tests {
|
|||||||
|
|
||||||
function run_archcheck {
|
function run_archcheck {
|
||||||
pushd build
|
pushd build
|
||||||
./nextpnr-himbaechel --uarch example --chipdb ./example.bin --test
|
./nextpnr-himbaechel --device EXAMPLE --test
|
||||||
popd
|
popd
|
||||||
}
|
}
|
||||||
|
@ -45,8 +45,188 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
|
#if defined(_WIN32)
|
||||||
|
#include <io.h>
|
||||||
|
#include <windows.h>
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <mach-o/dyld.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#else
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef __FreeBSD__
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_BEGIN
|
NEXTPNR_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
static std::string npnr_share_dirname;
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
bool check_file_exists(std::string filename, bool) { return _access(filename.c_str(), 0) == 0; }
|
||||||
|
#else
|
||||||
|
bool check_file_exists(std::string filename, bool is_exec)
|
||||||
|
{
|
||||||
|
return access(filename.c_str(), is_exec ? X_OK : F_OK) == 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__linux__) || defined(__CYGWIN__)
|
||||||
|
std::string proc_self_dirname()
|
||||||
|
{
|
||||||
|
char path[PATH_MAX];
|
||||||
|
ssize_t buflen = readlink("/proc/self/exe", path, sizeof(path));
|
||||||
|
if (buflen < 0) {
|
||||||
|
log_error("readlink(\"/proc/self/exe\") failed: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
while (buflen > 0 && path[buflen - 1] != '/')
|
||||||
|
buflen--;
|
||||||
|
return std::string(path, buflen);
|
||||||
|
}
|
||||||
|
#elif defined(__FreeBSD__)
|
||||||
|
std::string proc_self_dirname()
|
||||||
|
{
|
||||||
|
int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
|
||||||
|
size_t buflen;
|
||||||
|
char *buffer;
|
||||||
|
std::string path;
|
||||||
|
if (sysctl(mib, 4, NULL, &buflen, NULL, 0) != 0)
|
||||||
|
log_error("sysctl failed: %s\n", strerror(errno));
|
||||||
|
buffer = (char *)malloc(buflen);
|
||||||
|
if (buffer == NULL)
|
||||||
|
log_error("malloc failed: %s\n", strerror(errno));
|
||||||
|
if (sysctl(mib, 4, buffer, &buflen, NULL, 0) != 0)
|
||||||
|
log_error("sysctl failed: %s\n", strerror(errno));
|
||||||
|
while (buflen > 0 && buffer[buflen - 1] != '/')
|
||||||
|
buflen--;
|
||||||
|
path.assign(buffer, buflen);
|
||||||
|
free(buffer);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
#elif defined(__APPLE__)
|
||||||
|
std::string proc_self_dirname()
|
||||||
|
{
|
||||||
|
char *path = NULL;
|
||||||
|
uint32_t buflen = 0;
|
||||||
|
while (_NSGetExecutablePath(path, &buflen) != 0)
|
||||||
|
path = (char *)realloc((void *)path, buflen);
|
||||||
|
while (buflen > 0 && path[buflen - 1] != '/')
|
||||||
|
buflen--;
|
||||||
|
std::string str(path, buflen);
|
||||||
|
free(path);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
#elif defined(_WIN32)
|
||||||
|
std::string proc_self_dirname()
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
#ifdef __MINGW32__
|
||||||
|
char longpath[MAX_PATH + 1];
|
||||||
|
char shortpath[MAX_PATH + 1];
|
||||||
|
#else
|
||||||
|
WCHAR longpath[MAX_PATH + 1];
|
||||||
|
TCHAR shortpath[MAX_PATH + 1];
|
||||||
|
#endif
|
||||||
|
if (!GetModuleFileName(0, longpath, MAX_PATH + 1))
|
||||||
|
log_error("GetModuleFileName() failed.\n");
|
||||||
|
if (!GetShortPathName(longpath, shortpath, MAX_PATH + 1))
|
||||||
|
log_error("GetShortPathName() failed.\n");
|
||||||
|
while (shortpath[i] != 0)
|
||||||
|
i++;
|
||||||
|
while (i > 0 && shortpath[i - 1] != '/' && shortpath[i - 1] != '\\')
|
||||||
|
shortpath[--i] = 0;
|
||||||
|
std::string path;
|
||||||
|
for (i = 0; shortpath[i]; i++)
|
||||||
|
path += char(shortpath[i]);
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
#elif defined(EMSCRIPTEN) || defined(__wasm)
|
||||||
|
std::string proc_self_dirname() { return "/"; }
|
||||||
|
#elif defined(__OpenBSD__)
|
||||||
|
char npnr_path[PATH_MAX];
|
||||||
|
char *npnr_argv0;
|
||||||
|
|
||||||
|
std::string proc_self_dirname(void)
|
||||||
|
{
|
||||||
|
char buf[PATH_MAX + 1] = "", *path, *p;
|
||||||
|
// if case argv[0] contains a valid path, return it
|
||||||
|
if (strlen(npnr_path) > 0) {
|
||||||
|
p = strrchr(npnr_path, '/');
|
||||||
|
snprintf(buf, sizeof buf, "%*s/", (int)(npnr_path - p), npnr_path);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
// if argv[0] does not, reconstruct the path out of $PATH
|
||||||
|
path = strdup(getenv("PATH"));
|
||||||
|
if (!path)
|
||||||
|
log_error("getenv(\"PATH\") failed: %s\n", strerror(errno));
|
||||||
|
for (p = strtok(path, ":"); p; p = strtok(NULL, ":")) {
|
||||||
|
snprintf(buf, sizeof buf, "%s/%s", p, npnr_argv0);
|
||||||
|
if (access(buf, X_OK) == 0) {
|
||||||
|
*(strrchr(buf, '/') + 1) = '\0';
|
||||||
|
free(path);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(path);
|
||||||
|
log_error("Can't determine nextpnr executable path\n.");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#error "Don't know how to determine process executable base path!"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(EMSCRIPTEN) || defined(__wasm)
|
||||||
|
void init_share_dirname() { npnr_share_dirname = "/share/"; }
|
||||||
|
#else
|
||||||
|
void init_share_dirname()
|
||||||
|
{
|
||||||
|
std::string proc_self_path = proc_self_dirname();
|
||||||
|
#if defined(_WIN32) && !defined(nextpnr_WIN32_UNIX_DIR)
|
||||||
|
std::string proc_share_path = proc_self_path + "share\\";
|
||||||
|
if (check_file_exists(proc_share_path, true)) {
|
||||||
|
npnr_share_dirname = proc_share_path;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
proc_share_path = proc_self_path + "..\\share\\";
|
||||||
|
if (check_file_exists(proc_share_path, true)) {
|
||||||
|
npnr_share_dirname = proc_share_path;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
std::string proc_share_path = proc_self_path + "share/";
|
||||||
|
if (check_file_exists(proc_share_path, true)) {
|
||||||
|
npnr_share_dirname = proc_share_path;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
proc_share_path = proc_self_path + "../share/" + "nextpnr/";
|
||||||
|
if (check_file_exists(proc_share_path, true)) {
|
||||||
|
npnr_share_dirname = proc_share_path;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#ifdef nextpnr_DATDIR
|
||||||
|
proc_share_path = nextpnr_DATDIR "/";
|
||||||
|
if (check_file_exists(proc_share_path, true)) {
|
||||||
|
npnr_share_dirname = proc_share_path;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::string proc_share_dirname()
|
||||||
|
{
|
||||||
|
if (npnr_share_dirname.empty())
|
||||||
|
log_error("init_share_dirname: unable to determine share/ directory!\n");
|
||||||
|
return npnr_share_dirname;
|
||||||
|
}
|
||||||
|
|
||||||
struct no_separator : std::numpunct<char>
|
struct no_separator : std::numpunct<char>
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
@ -62,6 +242,12 @@ CommandHandler::CommandHandler(int argc, char **argv) : argc(argc), argv(argv)
|
|||||||
// the locale is broken in this system, so leave it as it is
|
// the locale is broken in this system, so leave it as it is
|
||||||
}
|
}
|
||||||
log_streams.clear();
|
log_streams.clear();
|
||||||
|
|
||||||
|
#if defined(__OpenBSD__)
|
||||||
|
// save the executable origin for proc_self_dirname()
|
||||||
|
npnr_argv0 = argv[0];
|
||||||
|
realpath(npnr_argv0, npnr_path);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CommandHandler::parseOptions()
|
bool CommandHandler::parseOptions()
|
||||||
|
@ -69,6 +69,12 @@ class CommandHandler
|
|||||||
std::ofstream logfile;
|
std::ofstream logfile;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Relative directory functions from Yosys
|
||||||
|
bool check_file_exists(std::string filename, bool is_exec);
|
||||||
|
void init_share_dirname();
|
||||||
|
std::string proc_self_dirname();
|
||||||
|
std::string proc_share_dirname();
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
|
||||||
#endif // COMMAND_H
|
#endif // COMMAND_H
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "nextpnr.h"
|
#include "nextpnr.h"
|
||||||
|
|
||||||
|
#include "command.h"
|
||||||
#include "placer1.h"
|
#include "placer1.h"
|
||||||
#include "placer_heap.h"
|
#include "placer_heap.h"
|
||||||
#include "router1.h"
|
#include "router1.h"
|
||||||
@ -33,66 +34,72 @@ NEXTPNR_NAMESPACE_BEGIN
|
|||||||
|
|
||||||
static const ChipInfoPOD *get_chip_info(const RelPtr<ChipInfoPOD> *ptr) { return ptr->get(); }
|
static const ChipInfoPOD *get_chip_info(const RelPtr<ChipInfoPOD> *ptr) { return ptr->get(); }
|
||||||
|
|
||||||
Arch::Arch(ArchArgs args)
|
Arch::Arch(ArchArgs args) : args(args)
|
||||||
{
|
{
|
||||||
|
HimbaechelArch *arch = HimbaechelArch::find_match(args.device);
|
||||||
|
if (!arch) {
|
||||||
|
std::string available = HimbaechelArch::list();
|
||||||
|
log_error("unable to load uarch for device '%s', included uarches: %s\n", args.device.c_str(),
|
||||||
|
available.c_str());
|
||||||
|
}
|
||||||
|
log_info("Using uarch '%s' for device '%s'\n", arch->name.c_str(), args.device.c_str());
|
||||||
|
this->args.uarch = arch->name;
|
||||||
|
uarch = arch->create(args.device, args.options);
|
||||||
|
// Load uarch
|
||||||
|
uarch->init_database(this);
|
||||||
|
if (!chip_info)
|
||||||
|
log_error("uarch didn't load any chipdb, probably a load_chipdb call was missing\n");
|
||||||
|
|
||||||
|
init_tiles();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Arch::load_chipdb(const std::string &path)
|
||||||
|
{
|
||||||
|
std::string db_path;
|
||||||
|
if (!args.chipdb_override.empty()) {
|
||||||
|
db_path = args.chipdb_override;
|
||||||
|
} else {
|
||||||
|
db_path = proc_share_dirname();
|
||||||
|
db_path += "/himbaechel/";
|
||||||
|
db_path += path;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
blob_file.open(args.chipdb);
|
blob_file.open(db_path);
|
||||||
if (args.chipdb.empty() || !blob_file.is_open())
|
if (db_path.empty() || !blob_file.is_open())
|
||||||
log_error("Unable to read chipdb %s\n", args.chipdb.c_str());
|
log_error("Unable to read chipdb %s\n", db_path.c_str());
|
||||||
const char *blob = reinterpret_cast<const char *>(blob_file.data());
|
const char *blob = reinterpret_cast<const char *>(blob_file.data());
|
||||||
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(blob));
|
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(blob));
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
log_error("Unable to read chipdb %s\n", args.chipdb.c_str());
|
log_error("Unable to read chipdb %s\n", db_path.c_str());
|
||||||
}
|
}
|
||||||
// Check consistency of blob
|
// Check consistency of blob
|
||||||
if (chip_info->magic != 0x00ca7ca7)
|
if (chip_info->magic != 0x00ca7ca7)
|
||||||
log_error("chipdb %s does not look like a valid himbächel database!\n", args.chipdb.c_str());
|
log_error("chipdb %s does not look like a valid himbächel database!\n", db_path.c_str());
|
||||||
std::string blob_uarch(chip_info->uarch.get());
|
std::string blob_uarch(chip_info->uarch.get());
|
||||||
if (blob_uarch != args.uarch)
|
if (blob_uarch != args.uarch)
|
||||||
log_error("database device uarch '%s' does not match selected device uarch '%s'.\n", blob_uarch.c_str(),
|
log_error("database device uarch '%s' does not match selected device uarch '%s'.\n", blob_uarch.c_str(),
|
||||||
args.uarch.c_str());
|
args.uarch.c_str());
|
||||||
// Load uarch
|
|
||||||
uarch = HimbaechelArch::create(args.uarch, args.options);
|
|
||||||
if (!uarch) {
|
|
||||||
std::string available = HimbaechelArch::list();
|
|
||||||
log_error("unable to load device uarch '%s', available options: %s\n", args.uarch.c_str(), available.c_str());
|
|
||||||
}
|
|
||||||
uarch->init_constids(this);
|
|
||||||
// Setup constids from database
|
// Setup constids from database
|
||||||
for (int i = 0; i < chip_info->extra_constids->bba_ids.ssize(); i++) {
|
for (int i = 0; i < chip_info->extra_constids->bba_ids.ssize(); i++) {
|
||||||
IdString::initialize_add(this, chip_info->extra_constids->bba_ids[i].get(),
|
IdString::initialize_add(this, chip_info->extra_constids->bba_ids[i].get(),
|
||||||
i + chip_info->extra_constids->known_id_count);
|
i + chip_info->extra_constids->known_id_count);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Arch::set_speed_grade(const std::string &speed)
|
||||||
|
{
|
||||||
|
if (speed.empty())
|
||||||
|
return;
|
||||||
// Select speed grade
|
// Select speed grade
|
||||||
if (args.speed.empty()) {
|
for (const auto &speed_data : chip_info->speed_grades) {
|
||||||
if (chip_info->speed_grades.ssize() == 0) {
|
if (IdString(speed_data.name) == id(speed)) {
|
||||||
// no timing information and no speed grade specified
|
speed_grade = &speed_data;
|
||||||
speed_grade = nullptr;
|
break;
|
||||||
} else if (chip_info->speed_grades.ssize() == 1) {
|
|
||||||
// speed grade not specified but only one available; use it
|
|
||||||
speed_grade = &(chip_info->speed_grades[0]);
|
|
||||||
} else {
|
|
||||||
std::string available_speeds = "";
|
|
||||||
for (const auto &speed_data : chip_info->speed_grades) {
|
|
||||||
if (!available_speeds.empty())
|
|
||||||
available_speeds += ", ";
|
|
||||||
available_speeds += IdString(speed_data.name).c_str(this);
|
|
||||||
}
|
|
||||||
log_error("Speed grade must be specified using --speed (available options: %s).\n",
|
|
||||||
available_speeds.c_str());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (const auto &speed_data : chip_info->speed_grades) {
|
|
||||||
if (IdString(speed_data.name) == id(args.speed)) {
|
|
||||||
speed_grade = &speed_data;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!speed_grade) {
|
|
||||||
log_error("Speed grade '%s' not found in database.\n", args.speed.c_str());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
init_tiles();
|
if (!speed_grade) {
|
||||||
|
log_error("Speed grade '%s' not found in database.\n", speed.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Arch::init_tiles()
|
void Arch::init_tiles()
|
||||||
|
@ -386,9 +386,8 @@ struct BelPinRange
|
|||||||
struct ArchArgs
|
struct ArchArgs
|
||||||
{
|
{
|
||||||
std::string uarch;
|
std::string uarch;
|
||||||
std::string chipdb;
|
std::string chipdb_override;
|
||||||
std::string device;
|
std::string device;
|
||||||
std::string speed;
|
|
||||||
dict<std::string, std::string> options;
|
dict<std::string, std::string> options;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -421,6 +420,9 @@ struct Arch : BaseArch<ArchRanges>
|
|||||||
Arch(ArchArgs args);
|
Arch(ArchArgs args);
|
||||||
~Arch(){};
|
~Arch(){};
|
||||||
|
|
||||||
|
void load_chipdb(const std::string &path);
|
||||||
|
void set_speed_grade(const std::string &speed);
|
||||||
|
|
||||||
void late_init();
|
void late_init();
|
||||||
|
|
||||||
// Database references
|
// Database references
|
||||||
|
@ -28,7 +28,7 @@ NEXTPNR_NAMESPACE_BEGIN
|
|||||||
void arch_wrap_python(py::module &m)
|
void arch_wrap_python(py::module &m)
|
||||||
{
|
{
|
||||||
using namespace PythonConversion;
|
using namespace PythonConversion;
|
||||||
py::class_<ArchArgs>(m, "ArchArgs").def_readwrite("chipdb", &ArchArgs::chipdb);
|
py::class_<ArchArgs>(m, "ArchArgs").def_readwrite("device", &ArchArgs::device);
|
||||||
|
|
||||||
py::class_<BelId>(m, "BelId").def_readwrite("index", &BelId::index);
|
py::class_<BelId>(m, "BelId").def_readwrite("index", &BelId::index);
|
||||||
|
|
||||||
|
@ -25,8 +25,6 @@ NEXTPNR_NAMESPACE_BEGIN
|
|||||||
|
|
||||||
void HimbaechelAPI::init(Context *ctx) { this->ctx = ctx; }
|
void HimbaechelAPI::init(Context *ctx) { this->ctx = ctx; }
|
||||||
|
|
||||||
void HimbaechelAPI::init_constids(Arch *arch) {}
|
|
||||||
|
|
||||||
std::vector<IdString> HimbaechelAPI::getCellTypes() const
|
std::vector<IdString> HimbaechelAPI::getCellTypes() const
|
||||||
{
|
{
|
||||||
std::vector<IdString> result;
|
std::vector<IdString> result;
|
||||||
@ -89,17 +87,17 @@ std::string HimbaechelArch::list()
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
std::unique_ptr<HimbaechelAPI> HimbaechelArch::create(const std::string &name,
|
|
||||||
const dict<std::string, std::string> &args)
|
HimbaechelArch *HimbaechelArch::find_match(const std::string &device)
|
||||||
{
|
{
|
||||||
HimbaechelArch *cursor = HimbaechelArch::list_head;
|
HimbaechelArch *cursor = HimbaechelArch::list_head;
|
||||||
while (cursor) {
|
while (cursor) {
|
||||||
if (cursor->name != name) {
|
if (!cursor->match_device(device)) {
|
||||||
cursor = cursor->list_next;
|
cursor = cursor->list_next;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
return cursor->create(args);
|
return cursor;
|
||||||
}
|
}
|
||||||
return {};
|
return nullptr;
|
||||||
}
|
}
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -58,8 +58,9 @@ struct PlacerHeapCfg;
|
|||||||
struct HimbaechelAPI
|
struct HimbaechelAPI
|
||||||
{
|
{
|
||||||
virtual void init(Context *ctx);
|
virtual void init(Context *ctx);
|
||||||
// If constids are being used, this is used to set them up early before loading the db blob
|
// If constids are being used, this is used to set them up early
|
||||||
virtual void init_constids(Arch *arch);
|
// then it is responsible for loading the db blob with arch->load_chipdb()
|
||||||
|
virtual void init_database(Arch *arch) = 0;
|
||||||
Context *ctx;
|
Context *ctx;
|
||||||
bool with_gui = false;
|
bool with_gui = false;
|
||||||
|
|
||||||
@ -116,10 +117,12 @@ struct HimbaechelArch
|
|||||||
std::string name;
|
std::string name;
|
||||||
HimbaechelArch(const std::string &name);
|
HimbaechelArch(const std::string &name);
|
||||||
~HimbaechelArch(){};
|
~HimbaechelArch(){};
|
||||||
virtual std::unique_ptr<HimbaechelAPI> create(const dict<std::string, std::string> &args) = 0;
|
virtual bool match_device(const std::string &device) = 0;
|
||||||
|
virtual std::unique_ptr<HimbaechelAPI> create(const std::string &device,
|
||||||
|
const dict<std::string, std::string> &args) = 0;
|
||||||
|
|
||||||
static std::string list();
|
static std::string list();
|
||||||
static std::unique_ptr<HimbaechelAPI> create(const std::string &name, const dict<std::string, std::string> &args);
|
static HimbaechelArch *find_match(const std::string &device);
|
||||||
};
|
};
|
||||||
|
|
||||||
NEXTPNR_NAMESPACE_END
|
NEXTPNR_NAMESPACE_END
|
||||||
|
@ -40,16 +40,19 @@ class HimbaechelCommandHandler : public CommandHandler
|
|||||||
po::options_description getArchOptions() override;
|
po::options_description getArchOptions() override;
|
||||||
};
|
};
|
||||||
|
|
||||||
HimbaechelCommandHandler::HimbaechelCommandHandler(int argc, char **argv) : CommandHandler(argc, argv) {}
|
HimbaechelCommandHandler::HimbaechelCommandHandler(int argc, char **argv) : CommandHandler(argc, argv)
|
||||||
|
{
|
||||||
|
init_share_dirname();
|
||||||
|
}
|
||||||
|
|
||||||
po::options_description HimbaechelCommandHandler::getArchOptions()
|
po::options_description HimbaechelCommandHandler::getArchOptions()
|
||||||
{
|
{
|
||||||
std::string all_uarches = HimbaechelArch::list();
|
std::string all_uarches = HimbaechelArch::list();
|
||||||
std::string uarch_help = stringf("himbächel micro-arch to use (available: %s)", all_uarches.c_str());
|
std::string uarch_help = stringf("himbächel micro-arch to use (available: %s)", all_uarches.c_str());
|
||||||
po::options_description specific("Architecture specific options");
|
po::options_description specific("Architecture specific options");
|
||||||
specific.add_options()("uarch", po::value<std::string>(), uarch_help.c_str());
|
specific.add_options()("device", po::value<std::string>(), "name of device to use");
|
||||||
specific.add_options()("chipdb", po::value<std::string>(), "path to chip database file");
|
specific.add_options()("chipdb", po::value<std::string>(), "override path to chip database file");
|
||||||
specific.add_options()("speed", po::value<std::string>(), "device speed grade");
|
specific.add_options()("list-uarch", "list included uarches");
|
||||||
specific.add_options()("vopt,o", po::value<std::vector<std::string>>(), "options to pass to the himbächel uarch");
|
specific.add_options()("vopt,o", po::value<std::vector<std::string>>(), "options to pass to the himbächel uarch");
|
||||||
|
|
||||||
return specific;
|
return specific;
|
||||||
@ -65,14 +68,19 @@ std::unique_ptr<Context> HimbaechelCommandHandler::createContext(dict<std::strin
|
|||||||
if (arch_name != "himbaechel")
|
if (arch_name != "himbaechel")
|
||||||
log_error("Unsupported architecture '%s'.\n", arch_name.c_str());
|
log_error("Unsupported architecture '%s'.\n", arch_name.c_str());
|
||||||
}
|
}
|
||||||
if (!vm.count("uarch"))
|
if (vm.count("list-uarch")) {
|
||||||
log_error("uarch must be specified\n");
|
std::string uarches = HimbaechelArch::list();
|
||||||
if (!vm.count("chipdb"))
|
log_info("Supported uarches: %s\n", uarches.c_str());
|
||||||
log_error("chip database path must be specified.\n");
|
exit(0);
|
||||||
chipArgs.uarch = vm["uarch"].as<std::string>();
|
}
|
||||||
chipArgs.chipdb = vm["chipdb"].as<std::string>();
|
if (!vm.count("device"))
|
||||||
if (vm.count("speed"))
|
log_error("device must be specified\n");
|
||||||
chipArgs.speed = vm["speed"].as<std::string>();
|
chipArgs.device = vm["device"].as<std::string>();
|
||||||
|
|
||||||
|
if (vm.count("chipdb")) {
|
||||||
|
chipArgs.chipdb_override = vm["chipdb"].as<std::string>();
|
||||||
|
}
|
||||||
|
|
||||||
if (vm.count("vopt")) {
|
if (vm.count("vopt")) {
|
||||||
std::vector<std::string> options = vm["vopt"].as<std::vector<std::string>>();
|
std::vector<std::string> options = vm["vopt"].as<std::vector<std::string>>();
|
||||||
for (const auto &opt : options) {
|
for (const auto &opt : options) {
|
||||||
|
@ -37,7 +37,13 @@ struct ExampleImpl : HimbaechelAPI
|
|||||||
static constexpr int K = 4;
|
static constexpr int K = 4;
|
||||||
|
|
||||||
~ExampleImpl(){};
|
~ExampleImpl(){};
|
||||||
void init_constids(Arch *arch) override { init_uarch_constids(arch); }
|
void init_database(Arch *arch) override
|
||||||
|
{
|
||||||
|
init_uarch_constids(arch);
|
||||||
|
arch->load_chipdb("example/example.bin");
|
||||||
|
arch->set_speed_grade("DEFAULT");
|
||||||
|
}
|
||||||
|
|
||||||
void init(Context *ctx) override
|
void init(Context *ctx) override
|
||||||
{
|
{
|
||||||
h.init(ctx);
|
h.init(ctx);
|
||||||
@ -134,7 +140,8 @@ struct ExampleImpl : HimbaechelAPI
|
|||||||
struct ExampleArch : HimbaechelArch
|
struct ExampleArch : HimbaechelArch
|
||||||
{
|
{
|
||||||
ExampleArch() : HimbaechelArch("example"){};
|
ExampleArch() : HimbaechelArch("example"){};
|
||||||
std::unique_ptr<HimbaechelAPI> create(const dict<std::string, std::string> &args)
|
bool match_device(const std::string &device) override { return device == "EXAMPLE"; }
|
||||||
|
std::unique_ptr<HimbaechelAPI> create(const std::string &device, const dict<std::string, std::string> &args)
|
||||||
{
|
{
|
||||||
return std::make_unique<ExampleImpl>();
|
return std::make_unique<ExampleImpl>();
|
||||||
}
|
}
|
||||||
|
@ -25,14 +25,14 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(chipdb_binaries)
|
set(chipdb_binaries)
|
||||||
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chipdb)
|
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/share/himbaechel/gowin)
|
||||||
foreach(device ${HIMBAECHEL_GOWIN_DEVICES})
|
foreach(device ${HIMBAECHEL_GOWIN_DEVICES})
|
||||||
if(NOT device IN_LIST ALL_HIMBAECHEL_GOWIN_DEVICES)
|
if(NOT device IN_LIST ALL_HIMBAECHEL_GOWIN_DEVICES)
|
||||||
message(FATAL_ERROR "Device ${device} is not a supported Gowin device")
|
message(FATAL_ERROR "Device ${device} is not a supported Gowin device")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
set(device_bba chipdb/chipdb-${device}.bba)
|
set(device_bba ${CMAKE_BINARY_DIR}/share/himbaechel/gowin/chipdb-${device}.bba)
|
||||||
set(device_bin chipdb/chipdb-${device}.bin)
|
set(device_bin ${CMAKE_BINARY_DIR}/share/himbaechel/gowin/chipdb-${device}.bin)
|
||||||
add_custom_command(
|
add_custom_command(
|
||||||
OUTPUT ${device_bin}
|
OUTPUT ${device_bin}
|
||||||
COMMAND ${apycula_Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/gowin_arch_gen.py -d ${device} -o ${device_bba}
|
COMMAND ${apycula_Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/gowin_arch_gen.py -d ${device} -o ${device_bba}
|
||||||
@ -48,5 +48,5 @@ foreach(device ${HIMBAECHEL_GOWIN_DEVICES})
|
|||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
add_custom_target(chipdb-himbaechel-gowin ALL DEPENDS ${chipdb_binaries})
|
add_custom_target(chipdb-himbaechel-gowin ALL DEPENDS ${chipdb_binaries})
|
||||||
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/chipdb/ DESTINATION share/nextpnr/himbaechel/gowin
|
install(DIRECTORY ${CMAKE_BINARY_DIR}/share/himbaechel/gowin DESTINATION share/nextpnr/himbaechel/gowin
|
||||||
PATTERN "*.bba" EXCLUDE)
|
PATTERN "*.bba" EXCLUDE)
|
||||||
|
@ -22,7 +22,7 @@ struct GowinImpl : HimbaechelAPI
|
|||||||
{
|
{
|
||||||
|
|
||||||
~GowinImpl(){};
|
~GowinImpl(){};
|
||||||
void init_constids(Arch *arch) override { init_uarch_constids(arch); }
|
void init_database(Arch *arch) override;
|
||||||
void init(Context *ctx) override;
|
void init(Context *ctx) override;
|
||||||
|
|
||||||
void pack() override;
|
void pack() override;
|
||||||
@ -64,12 +64,49 @@ struct GowinImpl : HimbaechelAPI
|
|||||||
struct GowinArch : HimbaechelArch
|
struct GowinArch : HimbaechelArch
|
||||||
{
|
{
|
||||||
GowinArch() : HimbaechelArch("gowin"){};
|
GowinArch() : HimbaechelArch("gowin"){};
|
||||||
std::unique_ptr<HimbaechelAPI> create(const dict<std::string, std::string> &args)
|
|
||||||
|
bool match_device(const std::string &device) override { return device.size() > 2 && device.substr(0, 2) == "GW"; }
|
||||||
|
|
||||||
|
std::unique_ptr<HimbaechelAPI> create(const std::string &device, const dict<std::string, std::string> &args)
|
||||||
{
|
{
|
||||||
return std::make_unique<GowinImpl>();
|
return std::make_unique<GowinImpl>();
|
||||||
}
|
}
|
||||||
} gowinrArch;
|
} gowinrArch;
|
||||||
|
|
||||||
|
void GowinImpl::init_database(Arch *arch)
|
||||||
|
{
|
||||||
|
init_uarch_constids(arch);
|
||||||
|
const ArchArgs &args = arch->args;
|
||||||
|
std::string family;
|
||||||
|
if (args.options.count("family")) {
|
||||||
|
family = args.options.at("family");
|
||||||
|
} else {
|
||||||
|
bool GW2 = args.device == "GW2A-LV18PG256C8/I7";
|
||||||
|
if (GW2) {
|
||||||
|
family = "GW2A-18";
|
||||||
|
} else {
|
||||||
|
std::regex devicere = std::regex("GW1N([SZ]?)[A-Z]*-(LV|UV|UX)([0-9])(C?).*");
|
||||||
|
std::smatch match;
|
||||||
|
if (!std::regex_match(args.device, match, devicere)) {
|
||||||
|
log_error("Invalid device %s\n", args.device.c_str());
|
||||||
|
}
|
||||||
|
family = stringf("GW1N%s-%s", match[1].str().c_str(), match[3].str().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
arch->load_chipdb(stringf("gowin/chipdb-%s.bin", family.c_str()));
|
||||||
|
|
||||||
|
// These fields go in the header of the output JSON file and can help
|
||||||
|
// gowin_pack support different architectures
|
||||||
|
arch->settings[arch->id("packer.arch")] = std::string("himbaechel/gowin");
|
||||||
|
arch->settings[arch->id("packer.chipdb")] = family;
|
||||||
|
|
||||||
|
chip = arch->id(family);
|
||||||
|
std::string pn = args.device;
|
||||||
|
partno = arch->id(pn);
|
||||||
|
arch->settings[arch->id("packer.partno")] = pn;
|
||||||
|
}
|
||||||
|
|
||||||
void GowinImpl::init(Context *ctx)
|
void GowinImpl::init(Context *ctx)
|
||||||
{
|
{
|
||||||
h.init(ctx);
|
h.init(ctx);
|
||||||
@ -78,24 +115,6 @@ void GowinImpl::init(Context *ctx)
|
|||||||
gwu.init(ctx);
|
gwu.init(ctx);
|
||||||
|
|
||||||
const ArchArgs &args = ctx->getArchArgs();
|
const ArchArgs &args = ctx->getArchArgs();
|
||||||
// These fields go in the header of the output JSON file and can help
|
|
||||||
// gowin_pack support different architectures
|
|
||||||
ctx->settings[ctx->id("packer.arch")] = std::string("himbaechel/gowin");
|
|
||||||
ctx->settings[ctx->id("packer.chipdb")] = args.chipdb;
|
|
||||||
|
|
||||||
if (!args.options.count("partno")) {
|
|
||||||
log_error("Partnumber (like --vopt partno=GW1NR-LV9QN88PC6/I5) must be specified.\n");
|
|
||||||
}
|
|
||||||
// GW1N-9C.xxx -> GW1N-9C
|
|
||||||
std::string chipdb = args.chipdb;
|
|
||||||
auto dot_pos = chipdb.find(".");
|
|
||||||
if (dot_pos != std::string::npos) {
|
|
||||||
chipdb.resize(dot_pos);
|
|
||||||
}
|
|
||||||
chip = ctx->id(chipdb);
|
|
||||||
std::string pn = args.options.at("partno");
|
|
||||||
partno = ctx->id(pn);
|
|
||||||
ctx->settings[ctx->id("packer.partno")] = pn;
|
|
||||||
|
|
||||||
// package and speed class
|
// package and speed class
|
||||||
std::regex speedre = std::regex("(.*)(C[0-9]/I[0-9])$");
|
std::regex speedre = std::regex("(.*)(C[0-9]/I[0-9])$");
|
||||||
@ -103,6 +122,7 @@ void GowinImpl::init(Context *ctx)
|
|||||||
|
|
||||||
IdString spd;
|
IdString spd;
|
||||||
IdString package_idx;
|
IdString package_idx;
|
||||||
|
std::string pn = args.device;
|
||||||
if (std::regex_match(pn, match, speedre)) {
|
if (std::regex_match(pn, match, speedre)) {
|
||||||
package_idx = ctx->id(match[1]);
|
package_idx = ctx->id(match[1]);
|
||||||
spd = ctx->id(match[2]);
|
spd = ctx->id(match[2]);
|
||||||
|
Loading…
Reference in New Issue
Block a user