Skip to content

Commit

Permalink
himbaechel: Add discovery of uarch and chipdb
Browse files Browse the repository at this point in the history
Signed-off-by: gatecat <[email protected]>
  • Loading branch information
gatecat committed Sep 15, 2023
1 parent 3cac90a commit 565927d
Show file tree
Hide file tree
Showing 12 changed files with 332 additions and 94 deletions.
5 changes: 3 additions & 2 deletions .github/ci/build_himbaechel.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

Expand All @@ -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
}
186 changes: 186 additions & 0 deletions common/kernel/command.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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()
Expand Down
6 changes: 6 additions & 0 deletions common/kernel/command.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
87 changes: 47 additions & 40 deletions himbaechel/arch.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "log.h"
#include "nextpnr.h"

#include "command.h"
#include "placer1.h"
#include "placer_heap.h"
#include "router1.h"
Expand All @@ -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()
Expand Down
6 changes: 4 additions & 2 deletions himbaechel/arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion himbaechel/arch_pybindings.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
Loading

0 comments on commit 565927d

Please sign in to comment.