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
|
||||
# our CI Ubuntu provides doesn't like some of the typing stuff
|
||||
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
|
||||
}
|
||||
|
||||
@ -22,6 +23,6 @@ function run_tests {
|
||||
|
||||
function run_archcheck {
|
||||
pushd build
|
||||
./nextpnr-himbaechel --uarch example --chipdb ./example.bin --test
|
||||
./nextpnr-himbaechel --device EXAMPLE --test
|
||||
popd
|
||||
}
|
||||
|
@ -45,8 +45,188 @@
|
||||
#include "util.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
|
||||
|
||||
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>
|
||||
{
|
||||
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
|
||||
}
|
||||
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()
|
||||
|
@ -69,6 +69,12 @@ class CommandHandler
|
||||
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
|
||||
|
||||
#endif // COMMAND_H
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "log.h"
|
||||
#include "nextpnr.h"
|
||||
|
||||
#include "command.h"
|
||||
#include "placer1.h"
|
||||
#include "placer_heap.h"
|
||||
#include "router1.h"
|
||||
@ -33,66 +34,72 @@ NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
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 {
|
||||
blob_file.open(args.chipdb);
|
||||
if (args.chipdb.empty() || !blob_file.is_open())
|
||||
log_error("Unable to read chipdb %s\n", args.chipdb.c_str());
|
||||
blob_file.open(db_path);
|
||||
if (db_path.empty() || !blob_file.is_open())
|
||||
log_error("Unable to read chipdb %s\n", db_path.c_str());
|
||||
const char *blob = reinterpret_cast<const char *>(blob_file.data());
|
||||
chip_info = get_chip_info(reinterpret_cast<const RelPtr<ChipInfoPOD> *>(blob));
|
||||
} 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
|
||||
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());
|
||||
if (blob_uarch != args.uarch)
|
||||
log_error("database device uarch '%s' does not match selected device uarch '%s'.\n", blob_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
|
||||
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(),
|
||||
i + chip_info->extra_constids->known_id_count);
|
||||
}
|
||||
}
|
||||
|
||||
void Arch::set_speed_grade(const std::string &speed)
|
||||
{
|
||||
if (speed.empty())
|
||||
return;
|
||||
// Select speed grade
|
||||
if (args.speed.empty()) {
|
||||
if (chip_info->speed_grades.ssize() == 0) {
|
||||
// no timing information and no speed grade specified
|
||||
speed_grade = nullptr;
|
||||
} 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());
|
||||
for (const auto &speed_data : chip_info->speed_grades) {
|
||||
if (IdString(speed_data.name) == id(speed)) {
|
||||
speed_grade = &speed_data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
init_tiles();
|
||||
if (!speed_grade) {
|
||||
log_error("Speed grade '%s' not found in database.\n", speed.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void Arch::init_tiles()
|
||||
|
@ -386,9 +386,8 @@ struct BelPinRange
|
||||
struct ArchArgs
|
||||
{
|
||||
std::string uarch;
|
||||
std::string chipdb;
|
||||
std::string chipdb_override;
|
||||
std::string device;
|
||||
std::string speed;
|
||||
dict<std::string, std::string> options;
|
||||
};
|
||||
|
||||
@ -421,6 +420,9 @@ struct Arch : BaseArch<ArchRanges>
|
||||
Arch(ArchArgs args);
|
||||
~Arch(){};
|
||||
|
||||
void load_chipdb(const std::string &path);
|
||||
void set_speed_grade(const std::string &speed);
|
||||
|
||||
void late_init();
|
||||
|
||||
// Database references
|
||||
|
@ -28,7 +28,7 @@ NEXTPNR_NAMESPACE_BEGIN
|
||||
void arch_wrap_python(py::module &m)
|
||||
{
|
||||
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);
|
||||
|
||||
|
@ -25,8 +25,6 @@ NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
void HimbaechelAPI::init(Context *ctx) { this->ctx = ctx; }
|
||||
|
||||
void HimbaechelAPI::init_constids(Arch *arch) {}
|
||||
|
||||
std::vector<IdString> HimbaechelAPI::getCellTypes() const
|
||||
{
|
||||
std::vector<IdString> result;
|
||||
@ -89,17 +87,17 @@ std::string HimbaechelArch::list()
|
||||
}
|
||||
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;
|
||||
while (cursor) {
|
||||
if (cursor->name != name) {
|
||||
if (!cursor->match_device(device)) {
|
||||
cursor = cursor->list_next;
|
||||
continue;
|
||||
}
|
||||
return cursor->create(args);
|
||||
return cursor;
|
||||
}
|
||||
return {};
|
||||
return nullptr;
|
||||
}
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
@ -58,8 +58,9 @@ struct PlacerHeapCfg;
|
||||
struct HimbaechelAPI
|
||||
{
|
||||
virtual void init(Context *ctx);
|
||||
// If constids are being used, this is used to set them up early before loading the db blob
|
||||
virtual void init_constids(Arch *arch);
|
||||
// If constids are being used, this is used to set them up early
|
||||
// then it is responsible for loading the db blob with arch->load_chipdb()
|
||||
virtual void init_database(Arch *arch) = 0;
|
||||
Context *ctx;
|
||||
bool with_gui = false;
|
||||
|
||||
@ -116,10 +117,12 @@ struct HimbaechelArch
|
||||
std::string name;
|
||||
HimbaechelArch(const std::string &name);
|
||||
~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::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
|
||||
|
@ -40,16 +40,19 @@ class HimbaechelCommandHandler : public CommandHandler
|
||||
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()
|
||||
{
|
||||
std::string all_uarches = HimbaechelArch::list();
|
||||
std::string uarch_help = stringf("himbächel micro-arch to use (available: %s)", all_uarches.c_str());
|
||||
po::options_description specific("Architecture specific options");
|
||||
specific.add_options()("uarch", po::value<std::string>(), uarch_help.c_str());
|
||||
specific.add_options()("chipdb", po::value<std::string>(), "path to chip database file");
|
||||
specific.add_options()("speed", po::value<std::string>(), "device speed grade");
|
||||
specific.add_options()("device", po::value<std::string>(), "name of device to use");
|
||||
specific.add_options()("chipdb", po::value<std::string>(), "override path to chip database file");
|
||||
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");
|
||||
|
||||
return specific;
|
||||
@ -65,14 +68,19 @@ std::unique_ptr<Context> HimbaechelCommandHandler::createContext(dict<std::strin
|
||||
if (arch_name != "himbaechel")
|
||||
log_error("Unsupported architecture '%s'.\n", arch_name.c_str());
|
||||
}
|
||||
if (!vm.count("uarch"))
|
||||
log_error("uarch must be specified\n");
|
||||
if (!vm.count("chipdb"))
|
||||
log_error("chip database path must be specified.\n");
|
||||
chipArgs.uarch = vm["uarch"].as<std::string>();
|
||||
chipArgs.chipdb = vm["chipdb"].as<std::string>();
|
||||
if (vm.count("speed"))
|
||||
chipArgs.speed = vm["speed"].as<std::string>();
|
||||
if (vm.count("list-uarch")) {
|
||||
std::string uarches = HimbaechelArch::list();
|
||||
log_info("Supported uarches: %s\n", uarches.c_str());
|
||||
exit(0);
|
||||
}
|
||||
if (!vm.count("device"))
|
||||
log_error("device must be specified\n");
|
||||
chipArgs.device = vm["device"].as<std::string>();
|
||||
|
||||
if (vm.count("chipdb")) {
|
||||
chipArgs.chipdb_override = vm["chipdb"].as<std::string>();
|
||||
}
|
||||
|
||||
if (vm.count("vopt")) {
|
||||
std::vector<std::string> options = vm["vopt"].as<std::vector<std::string>>();
|
||||
for (const auto &opt : options) {
|
||||
|
@ -37,7 +37,13 @@ struct ExampleImpl : HimbaechelAPI
|
||||
static constexpr int K = 4;
|
||||
|
||||
~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
|
||||
{
|
||||
h.init(ctx);
|
||||
@ -134,7 +140,8 @@ struct ExampleImpl : HimbaechelAPI
|
||||
struct ExampleArch : HimbaechelArch
|
||||
{
|
||||
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>();
|
||||
}
|
||||
|
@ -25,14 +25,14 @@ else()
|
||||
endif()
|
||||
|
||||
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})
|
||||
if(NOT device IN_LIST ALL_HIMBAECHEL_GOWIN_DEVICES)
|
||||
message(FATAL_ERROR "Device ${device} is not a supported Gowin device")
|
||||
endif()
|
||||
|
||||
set(device_bba chipdb/chipdb-${device}.bba)
|
||||
set(device_bin chipdb/chipdb-${device}.bin)
|
||||
set(device_bba ${CMAKE_BINARY_DIR}/share/himbaechel/gowin/chipdb-${device}.bba)
|
||||
set(device_bin ${CMAKE_BINARY_DIR}/share/himbaechel/gowin/chipdb-${device}.bin)
|
||||
add_custom_command(
|
||||
OUTPUT ${device_bin}
|
||||
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()
|
||||
|
||||
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)
|
||||
|
@ -22,7 +22,7 @@ struct GowinImpl : HimbaechelAPI
|
||||
{
|
||||
|
||||
~GowinImpl(){};
|
||||
void init_constids(Arch *arch) override { init_uarch_constids(arch); }
|
||||
void init_database(Arch *arch) override;
|
||||
void init(Context *ctx) override;
|
||||
|
||||
void pack() override;
|
||||
@ -64,12 +64,49 @@ struct GowinImpl : HimbaechelAPI
|
||||
struct GowinArch : HimbaechelArch
|
||||
{
|
||||
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>();
|
||||
}
|
||||
} 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)
|
||||
{
|
||||
h.init(ctx);
|
||||
@ -78,24 +115,6 @@ void GowinImpl::init(Context *ctx)
|
||||
gwu.init(ctx);
|
||||
|
||||
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
|
||||
std::regex speedre = std::regex("(.*)(C[0-9]/I[0-9])$");
|
||||
@ -103,6 +122,7 @@ void GowinImpl::init(Context *ctx)
|
||||
|
||||
IdString spd;
|
||||
IdString package_idx;
|
||||
std::string pn = args.device;
|
||||
if (std::regex_match(pn, match, speedre)) {
|
||||
package_idx = ctx->id(match[1]);
|
||||
spd = ctx->id(match[2]);
|
||||
|
Loading…
Reference in New Issue
Block a user