2012-04-13 03:08:20 +02:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
2006-08-26 13:35:28 +02:00
|
|
|
//*************************************************************************
|
|
|
|
|
// DESCRIPTION: Verilator: File stream wrapper that understands indentation
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2022-01-01 14:26:40 +01:00
|
|
|
// Copyright 2003-2022 by Wilson Snyder. This program is free software; you
|
2020-03-21 16:24:24 +01:00
|
|
|
// can redistribute it and/or modify it under the terms of either the GNU
|
2009-05-04 23:07:57 +02:00
|
|
|
// Lesser General Public License Version 3 or the Perl Artistic License
|
|
|
|
|
// Version 2.0.
|
2020-03-21 16:24:24 +01:00
|
|
|
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
2019-10-05 02:17:11 +02:00
|
|
|
|
2006-12-18 20:20:45 +01:00
|
|
|
#include "config_build.h"
|
|
|
|
|
#include "verilatedos.h"
|
2018-10-14 19:43:24 +02:00
|
|
|
|
|
|
|
|
#include "V3File.h"
|
2022-08-05 11:56:57 +02:00
|
|
|
|
|
|
|
|
#include "V3Ast.h"
|
|
|
|
|
#include "V3Global.h"
|
2018-10-14 19:43:24 +02:00
|
|
|
#include "V3Os.h"
|
2019-12-24 01:00:17 +01:00
|
|
|
#include "V3String.h"
|
2018-10-14 19:43:24 +02:00
|
|
|
|
2010-01-25 01:00:34 +01:00
|
|
|
#include <cerrno>
|
2018-10-14 13:04:18 +02:00
|
|
|
#include <cstdarg>
|
2010-01-20 13:15:51 +01:00
|
|
|
#include <fcntl.h>
|
2006-08-26 13:35:28 +02:00
|
|
|
#include <iomanip>
|
2010-01-20 13:15:51 +01:00
|
|
|
#include <map>
|
2018-10-14 13:04:18 +02:00
|
|
|
#include <memory>
|
2022-08-05 11:56:57 +02:00
|
|
|
|
2018-10-14 13:04:18 +02:00
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/types.h>
|
2010-01-20 13:15:51 +01:00
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
// clang-format off
|
2014-03-25 01:19:43 +01:00
|
|
|
#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
|
2010-01-20 13:15:51 +01:00
|
|
|
# define INFILTER_PIPE // Allow pipe filtering. Needs fork()
|
|
|
|
|
#endif
|
|
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
#ifdef HAVE_STAT_NSEC // i.e. Linux 2.6, from configure
|
|
|
|
|
# define VL_STAT_CTIME_NSEC(stat) ((stat).st_ctim.tv_nsec) // Nanoseconds
|
|
|
|
|
# define VL_STAT_MTIME_NSEC(stat) ((stat).st_mtim.tv_nsec) // Nanoseconds
|
2017-10-14 02:34:36 +02:00
|
|
|
#else
|
|
|
|
|
# define VL_STAT_CTIME_NSEC(stat) (0)
|
|
|
|
|
# define VL_STAT_MTIME_NSEC(stat) (0)
|
|
|
|
|
#endif
|
|
|
|
|
|
2010-01-20 13:15:51 +01:00
|
|
|
#ifdef INFILTER_PIPE
|
|
|
|
|
# include <sys/wait.h>
|
|
|
|
|
#endif
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2019-12-28 17:44:24 +01:00
|
|
|
#if defined(_WIN32) || defined(__MINGW32__)
|
|
|
|
|
# include <io.h> // open, read, write, close
|
|
|
|
|
#endif
|
2020-04-15 13:58:34 +02:00
|
|
|
// clang-format on
|
2019-12-28 17:44:24 +01:00
|
|
|
|
2010-04-07 02:20:44 +02:00
|
|
|
// If change this code, run a test with the below size set very small
|
2022-01-08 18:01:39 +01:00
|
|
|
// #define INFILTER_IPC_BUFSIZ 16
|
2020-08-16 20:19:12 +02:00
|
|
|
constexpr int INFILTER_IPC_BUFSIZ = (64 * 1024); // For debug, try this as a small number
|
|
|
|
|
constexpr int INFILTER_CACHE_MAX = (64 * 1024); // Maximum bytes to cache if same file read twice
|
2010-01-20 13:15:51 +01:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// V3File Internal state
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class V3FileDependImp final {
|
2006-08-26 13:35:28 +02:00
|
|
|
// TYPES
|
2020-11-19 03:32:16 +01:00
|
|
|
class DependFile final {
|
2019-05-19 22:13:13 +02:00
|
|
|
// A single file
|
2021-11-26 23:55:36 +01:00
|
|
|
const bool m_target; // True if write, else read
|
2020-11-15 21:40:35 +01:00
|
|
|
bool m_exists = true;
|
2021-11-26 23:55:36 +01:00
|
|
|
const string m_filename; // Filename
|
2020-04-15 13:58:34 +02:00
|
|
|
struct stat m_stat; // Stat information
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
2019-05-19 22:13:13 +02:00
|
|
|
DependFile(const string& filename, bool target)
|
2020-08-16 15:55:36 +02:00
|
|
|
: m_target{target}
|
|
|
|
|
, m_filename{filename} {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_stat.st_ctime = 0;
|
|
|
|
|
m_stat.st_mtime = 0;
|
|
|
|
|
}
|
2020-11-17 01:56:16 +01:00
|
|
|
~DependFile() = default;
|
2019-05-19 22:13:13 +02:00
|
|
|
const string& filename() const { return m_filename; }
|
|
|
|
|
bool target() const { return m_target; }
|
2019-10-18 01:44:10 +02:00
|
|
|
bool exists() const { return m_exists; }
|
2019-05-19 22:13:13 +02:00
|
|
|
off_t size() const { return m_stat.st_size; }
|
|
|
|
|
ino_t ino() const { return m_stat.st_ino; }
|
|
|
|
|
time_t cstime() const { return m_stat.st_ctime; } // Seconds
|
|
|
|
|
time_t cnstime() const { return VL_STAT_CTIME_NSEC(m_stat); } // Nanoseconds
|
|
|
|
|
time_t mstime() const { return m_stat.st_mtime; } // Seconds
|
|
|
|
|
time_t mnstime() const { return VL_STAT_MTIME_NSEC(m_stat); } // Nanoseconds
|
|
|
|
|
void loadStats() {
|
|
|
|
|
if (!m_stat.st_mtime) {
|
2021-06-21 00:32:57 +02:00
|
|
|
const string fn = filename();
|
|
|
|
|
const int err = stat(fn.c_str(), &m_stat);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (err != 0) {
|
2019-01-05 10:58:14 +01:00
|
|
|
memset(&m_stat, 0, sizeof(m_stat));
|
|
|
|
|
m_stat.st_mtime = 1;
|
2019-10-18 01:44:10 +02:00
|
|
|
m_exists = false;
|
2019-01-05 10:58:14 +01:00
|
|
|
// Not an error... This can occur due to `line directives in the .vpp files
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(1, "-Info: File not statable: " << filename() << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
bool operator<(const DependFile& rhs) const { return filename() < rhs.filename(); }
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// MEMBERS
|
2018-02-02 03:24:41 +01:00
|
|
|
std::set<string> m_filenameSet; // Files generated (elim duplicates)
|
|
|
|
|
std::set<DependFile> m_filenameList; // Files sourced/generated
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
static string stripQuotes(const string& in) {
|
2019-05-19 22:13:13 +02:00
|
|
|
string pretty = in;
|
|
|
|
|
string::size_type pos;
|
2018-10-14 04:28:59 +02:00
|
|
|
while ((pos = pretty.find('\"')) != string::npos) pretty.replace(pos, 1, "_");
|
|
|
|
|
while ((pos = pretty.find('\n')) != string::npos) pretty.replace(pos, 1, "_");
|
2019-05-19 22:13:13 +02:00
|
|
|
return pretty;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
|
|
|
|
// ACCESSOR METHODS
|
|
|
|
|
void addSrcDepend(const string& filename) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_filenameSet.find(filename) == m_filenameSet.end()) {
|
2020-04-06 00:30:46 +02:00
|
|
|
// cppcheck-suppress stlFindInsert // cppcheck 1.90 bug
|
2019-05-19 22:13:13 +02:00
|
|
|
m_filenameSet.insert(filename);
|
2020-04-15 13:58:34 +02:00
|
|
|
DependFile df(filename, false);
|
2019-05-19 22:13:13 +02:00
|
|
|
df.loadStats(); // Get size now, in case changes during the run
|
|
|
|
|
m_filenameList.insert(df);
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
void addTgtDepend(const string& filename) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_filenameSet.find(filename) == m_filenameSet.end()) {
|
2020-04-06 00:30:46 +02:00
|
|
|
// cppcheck-suppress stlFindInsert // cppcheck 1.90 bug
|
2019-05-19 22:13:13 +02:00
|
|
|
m_filenameSet.insert(filename);
|
2018-08-25 15:52:45 +02:00
|
|
|
m_filenameList.insert(DependFile(filename, true));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
void writeDepend(const string& filename);
|
2019-10-18 01:44:10 +02:00
|
|
|
std::vector<string> getAllDeps() const;
|
2018-10-15 00:39:33 +02:00
|
|
|
void writeTimes(const string& filename, const string& cmdlineIn);
|
|
|
|
|
bool checkTimes(const string& filename, const string& cmdlineIn);
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
V3FileDependImp dependImp; // Depend implementation class
|
2006-08-26 13:35:28 +02:00
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// V3FileDependImp
|
|
|
|
|
|
|
|
|
|
inline void V3FileDependImp::writeDepend(const string& filename) {
|
2021-07-12 00:42:01 +02:00
|
|
|
const std::unique_ptr<std::ofstream> ofp{V3File::new_ofstream(filename)};
|
2020-04-15 13:58:34 +02:00
|
|
|
if (ofp->fail()) v3fatal("Can't write " << filename);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-08-16 17:43:49 +02:00
|
|
|
for (const DependFile& i : m_filenameList) {
|
|
|
|
|
if (i.target()) *ofp << i.filename() << " ";
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
*ofp << " : ";
|
|
|
|
|
*ofp << v3Global.opt.bin();
|
|
|
|
|
*ofp << " ";
|
|
|
|
|
|
2020-08-16 17:43:49 +02:00
|
|
|
for (const DependFile& i : m_filenameList) {
|
|
|
|
|
if (!i.target()) *ofp << i.filename() << " ";
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2006-08-31 17:29:15 +02:00
|
|
|
|
2020-11-19 03:03:23 +01:00
|
|
|
*ofp << '\n';
|
2006-08-31 17:29:15 +02:00
|
|
|
|
|
|
|
|
if (v3Global.opt.makePhony()) {
|
2020-11-19 03:03:23 +01:00
|
|
|
*ofp << '\n';
|
2020-08-16 17:43:49 +02:00
|
|
|
for (const DependFile& i : m_filenameList) {
|
2020-11-19 03:03:23 +01:00
|
|
|
if (!i.target()) *ofp << i.filename() << ":\n";
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-31 17:29:15 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-18 01:44:10 +02:00
|
|
|
inline std::vector<string> V3FileDependImp::getAllDeps() const {
|
|
|
|
|
std::vector<string> r;
|
2020-11-11 04:10:38 +01:00
|
|
|
for (const auto& itr : m_filenameList) {
|
|
|
|
|
if (!itr.target() && itr.exists()) r.push_back(itr.filename());
|
2019-10-18 01:44:10 +02:00
|
|
|
}
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
inline void V3FileDependImp::writeTimes(const string& filename, const string& cmdlineIn) {
|
2021-07-12 00:42:01 +02:00
|
|
|
const std::unique_ptr<std::ofstream> ofp{V3File::new_ofstream(filename)};
|
2020-04-15 13:58:34 +02:00
|
|
|
if (ofp->fail()) v3fatal("Can't write " << filename);
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2021-06-21 00:32:57 +02:00
|
|
|
const string cmdline = stripQuotes(cmdlineIn);
|
2020-04-15 13:58:34 +02:00
|
|
|
*ofp << "# DESCR"
|
2020-11-19 03:03:23 +01:00
|
|
|
<< "IPTION: Verilator output: Timestamp data for --skip-identical. Delete at will.\n";
|
|
|
|
|
*ofp << "C \"" << cmdline << "\"\n";
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
for (std::set<DependFile>::iterator iter = m_filenameList.begin();
|
|
|
|
|
iter != m_filenameList.end(); ++iter) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Read stats of files we create after we're done making them
|
2019-09-09 13:50:21 +02:00
|
|
|
// (except for this file, of course)
|
2021-11-13 19:50:44 +01:00
|
|
|
DependFile* const dfp = const_cast<DependFile*>(&(*iter));
|
2019-05-19 22:13:13 +02:00
|
|
|
V3Options::fileNfsFlush(dfp->filename());
|
|
|
|
|
dfp->loadStats();
|
|
|
|
|
off_t showSize = iter->size();
|
|
|
|
|
ino_t showIno = iter->ino();
|
|
|
|
|
if (dfp->filename() == filename) {
|
2020-04-15 13:58:34 +02:00
|
|
|
showSize = 0;
|
|
|
|
|
showIno = 0; // We're writing it, so need to ignore it
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
*ofp << (iter->target() ? "T" : "S") << " ";
|
|
|
|
|
*ofp << " " << std::setw(8) << showSize;
|
|
|
|
|
*ofp << " " << std::setw(8) << showIno;
|
|
|
|
|
*ofp << " " << std::setw(11) << iter->cstime();
|
|
|
|
|
*ofp << " " << std::setw(11) << iter->cnstime();
|
|
|
|
|
*ofp << " " << std::setw(11) << iter->mstime();
|
|
|
|
|
*ofp << " " << std::setw(11) << iter->mnstime();
|
|
|
|
|
*ofp << " \"" << iter->filename() << "\"";
|
2020-11-19 03:03:23 +01:00
|
|
|
*ofp << '\n';
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool V3FileDependImp::checkTimes(const string& filename, const string& cmdlineIn) {
|
2021-07-12 00:42:01 +02:00
|
|
|
const std::unique_ptr<std::ifstream> ifp{V3File::new_ifstream_nodepend(filename)};
|
2006-08-26 13:35:28 +02:00
|
|
|
if (ifp->fail()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, " --check-times failed: no input " << filename << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
return false;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2009-12-03 02:09:13 +01:00
|
|
|
{
|
2021-06-21 00:32:57 +02:00
|
|
|
const string ignore = V3Os::getline(*ifp);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (ignore.empty()) { /*used*/
|
|
|
|
|
}
|
2009-12-03 02:09:13 +01:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
{
|
2020-04-15 13:58:34 +02:00
|
|
|
char chkDir;
|
|
|
|
|
*ifp >> chkDir;
|
|
|
|
|
char quote;
|
|
|
|
|
*ifp >> quote;
|
2021-06-21 00:32:57 +02:00
|
|
|
const string chkCmdline = V3Os::getline(*ifp, '"');
|
|
|
|
|
const string cmdline = stripQuotes(cmdlineIn);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (cmdline != chkCmdline) {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, " --check-times failed: different command line\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (!ifp->eof()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
char chkDir;
|
|
|
|
|
*ifp >> chkDir;
|
|
|
|
|
off_t chkSize;
|
|
|
|
|
*ifp >> chkSize;
|
|
|
|
|
ino_t chkIno;
|
|
|
|
|
*ifp >> chkIno;
|
2019-05-19 22:13:13 +02:00
|
|
|
if (ifp->eof()) break; // Needed to read final whitespace before found eof
|
2020-04-15 13:58:34 +02:00
|
|
|
time_t chkCstime;
|
|
|
|
|
*ifp >> chkCstime;
|
|
|
|
|
time_t chkCnstime;
|
|
|
|
|
*ifp >> chkCnstime;
|
|
|
|
|
time_t chkMstime;
|
|
|
|
|
*ifp >> chkMstime;
|
|
|
|
|
time_t chkMnstime;
|
|
|
|
|
*ifp >> chkMnstime;
|
|
|
|
|
char quote;
|
|
|
|
|
*ifp >> quote;
|
2021-06-21 00:32:57 +02:00
|
|
|
const string chkFilename = V3Os::getline(*ifp, '"');
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
V3Options::fileNfsFlush(chkFilename);
|
2018-10-15 00:39:33 +02:00
|
|
|
// NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
|
2019-05-19 22:13:13 +02:00
|
|
|
struct stat chkStat;
|
2021-06-21 00:32:57 +02:00
|
|
|
const int err = stat(chkFilename.c_str(), &chkStat);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (err != 0) {
|
|
|
|
|
UINFO(2, " --check-times failed: missing " << chkFilename << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
// UINFO(9," got d="<<chkDir<<" s="<<chkSize<<" ct="<<chkCstime<<"."
|
2018-03-10 22:32:04 +01:00
|
|
|
// <<chkCnstime<<" mt="<<chkMstime<<"."<<chkMnstime<<" fn = "<<chkFilename<<endl);
|
2020-04-15 13:58:34 +02:00
|
|
|
// UINFO(9," nowSt s="<<chkStat.st_size<<" mt="<<chkStat.st_mtime<<"
|
|
|
|
|
// ct="<<chkStat.st_ctime<<" fn = "<<chkFilename<<endl);
|
|
|
|
|
if (filename != chkFilename) { // Other then the .dat file itself, as we were writing it
|
|
|
|
|
// at the time...
|
2019-05-19 22:13:13 +02:00
|
|
|
// We'd like this rule:
|
2020-04-15 13:58:34 +02:00
|
|
|
// if (!(chkStat.st_size == chkSize
|
2019-05-19 22:13:13 +02:00
|
|
|
// && chkStat.st_mtime == chkMstime) {
|
|
|
|
|
// However NFS messes us up, as there might be some data outstanding when
|
|
|
|
|
// we determined the original size. For safety, we know the creation time
|
|
|
|
|
// must be within a few second window... call it 20 sec.
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!(chkStat.st_size >= chkSize && chkStat.st_ino == chkIno
|
|
|
|
|
&& chkStat.st_ctime == chkCstime && VL_STAT_CTIME_NSEC(chkStat) == chkCnstime
|
2019-05-19 22:13:13 +02:00
|
|
|
&& chkStat.st_mtime <= (chkMstime + 20)
|
|
|
|
|
// Not comparing chkMnstime
|
2020-04-15 13:58:34 +02:00
|
|
|
)) {
|
|
|
|
|
UINFO(2, " --check-times failed: out-of-date "
|
|
|
|
|
<< chkFilename << "; " << chkStat.st_size << "=?" << chkSize << " "
|
|
|
|
|
<< chkStat.st_ctime << "." << VL_STAT_CTIME_NSEC(chkStat) << "=?"
|
|
|
|
|
<< chkCstime << "." << chkCnstime << " " << chkStat.st_mtime << "."
|
|
|
|
|
<< VL_STAT_MTIME_NSEC(chkStat) << "=?" << chkMstime << "."
|
|
|
|
|
<< chkMnstime << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// V3File
|
|
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
void V3File::addSrcDepend(const string& filename) { dependImp.addSrcDepend(filename); }
|
|
|
|
|
void V3File::addTgtDepend(const string& filename) { dependImp.addTgtDepend(filename); }
|
|
|
|
|
void V3File::writeDepend(const string& filename) { dependImp.writeDepend(filename); }
|
|
|
|
|
std::vector<string> V3File::getAllDeps() { return dependImp.getAllDeps(); }
|
2018-10-15 00:39:33 +02:00
|
|
|
void V3File::writeTimes(const string& filename, const string& cmdlineIn) {
|
|
|
|
|
dependImp.writeTimes(filename, cmdlineIn);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-10-15 00:39:33 +02:00
|
|
|
bool V3File::checkTimes(const string& filename, const string& cmdlineIn) {
|
|
|
|
|
return dependImp.checkTimes(filename, cmdlineIn);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2019-11-01 02:17:05 +01:00
|
|
|
void V3File::createMakeDirFor(const string& filename) {
|
|
|
|
|
if (filename != VL_DEV_NULL
|
|
|
|
|
// If doesn't start with makeDir then some output file user requested
|
2020-04-15 13:58:34 +02:00
|
|
|
&& filename.substr(0, v3Global.opt.makeDir().length() + 1)
|
|
|
|
|
== v3Global.opt.makeDir() + "/") {
|
2019-11-01 02:17:05 +01:00
|
|
|
createMakeDir();
|
|
|
|
|
}
|
|
|
|
|
}
|
2008-01-31 14:50:06 +01:00
|
|
|
void V3File::createMakeDir() {
|
|
|
|
|
static bool created = false;
|
|
|
|
|
if (!created) {
|
2019-05-19 22:13:13 +02:00
|
|
|
created = true;
|
|
|
|
|
V3Os::createDir(v3Global.opt.makeDir());
|
2021-02-22 03:25:21 +01:00
|
|
|
if (v3Global.opt.hierTop()) V3Os::createDir(v3Global.opt.hierTopDataDir());
|
2008-01-31 14:50:06 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-20 13:15:51 +01:00
|
|
|
//######################################################################
|
2019-10-05 13:54:14 +02:00
|
|
|
// VInFilterImp
|
2010-01-20 13:15:51 +01:00
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class VInFilterImp final {
|
2021-03-13 00:10:45 +01:00
|
|
|
using StrList = VInFilter::StrList;
|
2010-01-20 13:15:51 +01:00
|
|
|
|
2021-03-12 23:26:53 +01:00
|
|
|
std::map<const std::string, std::string> m_contentsMap; // Cache of file contents
|
2020-08-16 15:55:36 +02:00
|
|
|
bool m_readEof = false; // Received EOF on read
|
2010-01-20 13:15:51 +01:00
|
|
|
#ifdef INFILTER_PIPE
|
2020-08-16 15:55:36 +02:00
|
|
|
pid_t m_pid = 0; // fork() process id
|
2010-01-20 13:15:51 +01:00
|
|
|
#else
|
2021-12-06 14:15:58 +01:00
|
|
|
int m_pid = 0; // fork() process id - always zero as disabled
|
2010-01-20 13:15:51 +01:00
|
|
|
#endif
|
2020-08-16 15:55:36 +02:00
|
|
|
bool m_pidExited = false;
|
|
|
|
|
int m_pidStatus = 0;
|
|
|
|
|
int m_writeFd = 0; // File descriptor TO filter
|
|
|
|
|
int m_readFd = 0; // File descriptor FROM filter
|
2010-01-20 13:15:51 +01:00
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// METHODS
|
2018-05-14 12:50:47 +02:00
|
|
|
VL_DEBUG_FUNC; // Declare debug()
|
2010-01-20 13:15:51 +01:00
|
|
|
|
2010-04-07 02:20:44 +02:00
|
|
|
bool readContents(const string& filename, StrList& outl) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_pid) {
|
|
|
|
|
return readContentsFilter(filename, outl);
|
|
|
|
|
} else {
|
|
|
|
|
return readContentsFile(filename, outl);
|
|
|
|
|
}
|
2010-01-20 13:15:51 +01:00
|
|
|
}
|
2010-04-07 02:20:44 +02:00
|
|
|
bool readContentsFile(const string& filename, StrList& outl) {
|
2021-06-21 00:32:57 +02:00
|
|
|
const int fd = open(filename.c_str(), O_RDONLY);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (fd < 0) return false;
|
2019-05-19 22:13:13 +02:00
|
|
|
m_readEof = false;
|
|
|
|
|
readBlocks(fd, -1, outl);
|
|
|
|
|
close(fd);
|
|
|
|
|
return true;
|
2010-01-20 13:15:51 +01:00
|
|
|
}
|
2010-04-07 02:20:44 +02:00
|
|
|
bool readContentsFilter(const string& filename, StrList& outl) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (filename != "" || outl.empty()) {} // Prevent unused
|
2010-01-20 13:15:51 +01:00
|
|
|
#ifdef INFILTER_PIPE
|
2020-04-15 13:58:34 +02:00
|
|
|
writeFilter("read \"" + filename + "\"\n");
|
2021-06-21 00:32:57 +02:00
|
|
|
const string line = readFilterLine();
|
2019-05-19 22:13:13 +02:00
|
|
|
if (line.find("Content-Length") != string::npos) {
|
|
|
|
|
int len = 0;
|
|
|
|
|
sscanf(line.c_str(), "Content-Length: %d\n", &len);
|
|
|
|
|
readBlocks(m_readFd, len, outl);
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (line != "") v3error("--pipe-filter protocol error, unexpected: " << line);
|
2019-05-19 22:13:13 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2010-01-20 13:15:51 +01:00
|
|
|
#else
|
2019-05-19 22:13:13 +02:00
|
|
|
v3fatalSrc("--pipe-filter not implemented on this platform");
|
|
|
|
|
return false;
|
2010-01-20 13:15:51 +01:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2011-08-05 03:58:45 +02:00
|
|
|
// cppcheck-suppress functionConst
|
2010-01-20 13:15:51 +01:00
|
|
|
void checkFilter(bool hang) {
|
|
|
|
|
#ifdef INFILTER_PIPE
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!m_pidExited && waitpid(m_pid, &m_pidStatus, hang ? 0 : WNOHANG)) {
|
|
|
|
|
UINFO(1, "--pipe-filter: Exited, status "
|
|
|
|
|
<< m_pidStatus << " exit=" << WEXITSTATUS(m_pidStatus) << " err"
|
|
|
|
|
<< strerror(errno) << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_readEof = true;
|
|
|
|
|
m_pidExited = true;
|
|
|
|
|
}
|
2010-01-20 13:15:51 +01:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-26 21:13:36 +01:00
|
|
|
void readBlocks(int fd, int size, StrList& outl) {
|
2019-05-19 22:13:13 +02:00
|
|
|
char buf[INFILTER_IPC_BUFSIZ];
|
|
|
|
|
ssize_t sizegot = 0;
|
2020-04-15 13:58:34 +02:00
|
|
|
while (!m_readEof && (size < 0 || size > sizegot)) {
|
2019-05-19 22:13:13 +02:00
|
|
|
ssize_t todo = INFILTER_IPC_BUFSIZ;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (size > 0 && size < todo) todo = size;
|
2019-05-19 22:13:13 +02:00
|
|
|
errno = 0;
|
2021-06-21 00:32:57 +02:00
|
|
|
const ssize_t got = read(fd, buf, todo);
|
2020-04-15 13:58:34 +02:00
|
|
|
// UINFO(9,"RD GOT g "<< got<<" e "<<errno<<" "<<strerror(errno)<<endl);
|
|
|
|
|
// usleep(50*1000);
|
|
|
|
|
if (got > 0) {
|
2019-05-19 22:13:13 +02:00
|
|
|
outl.push_back(string(buf, got));
|
|
|
|
|
sizegot += got;
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (errno == EINTR || errno == EAGAIN
|
2010-01-25 02:53:24 +01:00
|
|
|
#ifdef EWOULDBLOCK
|
2020-04-15 13:58:34 +02:00
|
|
|
|| errno == EWOULDBLOCK
|
2010-01-25 02:53:24 +01:00
|
|
|
#endif
|
2020-04-15 13:58:34 +02:00
|
|
|
) {
|
|
|
|
|
checkFilter(false);
|
|
|
|
|
V3Os::u_sleep(1000);
|
|
|
|
|
continue;
|
|
|
|
|
} else {
|
|
|
|
|
m_readEof = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2010-01-20 13:15:51 +01:00
|
|
|
}
|
2015-10-04 19:11:32 +02:00
|
|
|
// cppcheck-suppress unusedFunction unusedPrivateFunction
|
2010-01-20 13:15:51 +01:00
|
|
|
string readFilterLine() {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Slow, but we don't need it much
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(9, "readFilterLine\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
string line;
|
|
|
|
|
while (!m_readEof) {
|
|
|
|
|
StrList outl;
|
|
|
|
|
readBlocks(m_readFd, 1, outl);
|
2021-06-21 00:32:57 +02:00
|
|
|
const string onechar = listString(outl);
|
2019-05-19 22:13:13 +02:00
|
|
|
line += onechar;
|
|
|
|
|
if (onechar == "\n") {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (line == "\n") {
|
|
|
|
|
line = "";
|
|
|
|
|
continue;
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(6, "filter-line-in: " << line);
|
2019-05-19 22:13:13 +02:00
|
|
|
return line;
|
2010-01-20 13:15:51 +01:00
|
|
|
}
|
2015-10-04 19:11:32 +02:00
|
|
|
// cppcheck-suppress unusedFunction unusedPrivateFunction
|
2010-01-20 13:15:51 +01:00
|
|
|
void writeFilter(const string& out) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (debug() >= 6) {
|
|
|
|
|
UINFO(6, "filter-out: " << out);
|
|
|
|
|
if (out[out.length() - 1] != '\n') cout << endl;
|
|
|
|
|
}
|
|
|
|
|
if (!m_pid) {
|
|
|
|
|
v3error("--pipe-filter: write to closed file\n");
|
|
|
|
|
m_readEof = true;
|
|
|
|
|
stop();
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
unsigned offset = 0;
|
2020-04-15 13:58:34 +02:00
|
|
|
while (!m_readEof && out.length() > offset) {
|
2019-05-19 22:13:13 +02:00
|
|
|
errno = 0;
|
2021-06-21 00:32:57 +02:00
|
|
|
const int got = write(m_writeFd, (out.c_str()) + offset, out.length() - offset);
|
2020-04-15 13:58:34 +02:00
|
|
|
// UINFO(9,"WR GOT g "<< got<<" e "<<errno<<" "<<strerror(errno)<<endl);
|
|
|
|
|
// usleep(50*1000);
|
|
|
|
|
if (got > 0) {
|
|
|
|
|
offset += got;
|
|
|
|
|
} else if (errno == EINTR || errno == EAGAIN
|
2010-01-25 02:53:24 +01:00
|
|
|
#ifdef EWOULDBLOCK
|
2020-04-15 13:58:34 +02:00
|
|
|
|| errno == EWOULDBLOCK
|
2010-01-25 02:53:24 +01:00
|
|
|
#endif
|
2020-04-15 13:58:34 +02:00
|
|
|
) {
|
|
|
|
|
checkFilter(false);
|
|
|
|
|
V3Os::u_sleep(1000);
|
|
|
|
|
continue;
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
2010-01-20 13:15:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Start the filter
|
|
|
|
|
void start(const string& command) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (command == "") {
|
2019-05-19 22:13:13 +02:00
|
|
|
m_pid = 0; // Disabled
|
|
|
|
|
} else {
|
|
|
|
|
startFilter(command);
|
|
|
|
|
}
|
2010-01-20 13:15:51 +01:00
|
|
|
}
|
|
|
|
|
void startFilter(const string& command) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (command == "") {} // Prevent Unused
|
2010-01-20 13:15:51 +01:00
|
|
|
#ifdef INFILTER_PIPE
|
2020-04-16 03:47:37 +02:00
|
|
|
int fd_stdin[2];
|
|
|
|
|
int fd_stdout[2];
|
2022-07-13 17:01:03 +02:00
|
|
|
constexpr int P_RD = 0;
|
|
|
|
|
constexpr int P_WR = 1;
|
2019-05-19 22:13:13 +02:00
|
|
|
|
|
|
|
|
if (pipe(fd_stdin) != 0 || pipe(fd_stdout) != 0) {
|
2020-04-15 13:58:34 +02:00
|
|
|
v3fatal("--pipe-filter: Can't pipe: " << strerror(errno));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
if (fd_stdin[P_RD] <= 2 || fd_stdin[P_WR] <= 2 || fd_stdout[P_RD] <= 2
|
|
|
|
|
|| fd_stdout[P_WR] <= 2) {
|
2019-05-19 22:13:13 +02:00
|
|
|
// We'd have to rearrange all of the FD usages in this case.
|
|
|
|
|
// Too unlikely; verilator isn't a daemon.
|
|
|
|
|
v3fatal("--pipe-filter: stdin/stdout closed before pipe opened\n");
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(1, "--pipe-filter: /bin/sh -c " << command << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
|
2021-06-21 00:32:57 +02:00
|
|
|
const pid_t pid = fork();
|
2020-04-15 13:58:34 +02:00
|
|
|
if (pid < 0) v3fatal("--pipe-filter: fork failed: " << strerror(errno));
|
2019-05-19 22:13:13 +02:00
|
|
|
if (pid == 0) { // Child
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(6, "In child\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
close(fd_stdin[P_WR]);
|
|
|
|
|
dup2(fd_stdin[P_RD], 0);
|
|
|
|
|
close(fd_stdout[P_RD]);
|
|
|
|
|
dup2(fd_stdout[P_WR], 1);
|
|
|
|
|
// And stderr comes from parent
|
2010-01-20 13:15:51 +01:00
|
|
|
|
2020-08-15 16:12:55 +02:00
|
|
|
execl("/bin/sh", "sh", "-c", command.c_str(), static_cast<char*>(nullptr));
|
2019-05-19 22:13:13 +02:00
|
|
|
// Don't use v3fatal, we don't share the common structures any more
|
|
|
|
|
fprintf(stderr, "--pipe-filter: exec failed: %s\n", strerror(errno));
|
2019-10-27 22:34:04 +01:00
|
|
|
_exit(1);
|
2020-04-15 13:58:34 +02:00
|
|
|
} else { // Parent
|
|
|
|
|
UINFO(6, "In parent, child pid " << pid << " stdin " << fd_stdin[P_WR] << "->"
|
|
|
|
|
<< fd_stdin[P_RD] << " stdout " << fd_stdout[P_WR]
|
|
|
|
|
<< "->" << fd_stdout[P_RD] << endl);
|
2019-05-19 22:13:13 +02:00
|
|
|
m_pid = pid;
|
|
|
|
|
m_pidExited = false;
|
|
|
|
|
m_pidStatus = 0;
|
|
|
|
|
m_writeFd = fd_stdin[P_WR];
|
|
|
|
|
m_readFd = fd_stdout[P_RD];
|
|
|
|
|
m_readEof = false;
|
|
|
|
|
|
|
|
|
|
close(fd_stdin[P_RD]);
|
|
|
|
|
close(fd_stdout[P_WR]);
|
|
|
|
|
|
|
|
|
|
int flags = fcntl(m_readFd, F_GETFL, 0);
|
|
|
|
|
fcntl(m_readFd, F_SETFL, flags | O_NONBLOCK);
|
|
|
|
|
|
|
|
|
|
flags = fcntl(m_writeFd, F_GETFL, 0);
|
|
|
|
|
fcntl(m_writeFd, F_SETFL, flags | O_NONBLOCK);
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(6, "startFilter complete\n");
|
2010-01-20 13:15:51 +01:00
|
|
|
#else
|
2019-05-19 22:13:13 +02:00
|
|
|
v3fatalSrc("--pipe-filter not implemented on this platform");
|
2010-01-20 13:15:51 +01:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void stop() {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (m_pid) stopFilter();
|
2010-01-20 13:15:51 +01:00
|
|
|
}
|
|
|
|
|
void stopFilter() {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(6, "Stopping filter process\n");
|
2010-01-20 13:15:51 +01:00
|
|
|
#ifdef INFILTER_PIPE
|
2019-05-19 22:13:13 +02:00
|
|
|
close(m_writeFd);
|
|
|
|
|
checkFilter(true);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (!WIFEXITED(m_pidStatus) || WEXITSTATUS(m_pidStatus) != 0) {
|
2019-05-19 22:13:13 +02:00
|
|
|
v3fatal("--pipe-filter returned bad status");
|
|
|
|
|
}
|
|
|
|
|
m_pid = 0;
|
|
|
|
|
close(m_readFd);
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(6, "Closed\n");
|
2010-01-20 13:15:51 +01:00
|
|
|
#else
|
2019-05-19 22:13:13 +02:00
|
|
|
v3fatalSrc("--pipe-filter not implemented on this platform");
|
2010-01-20 13:15:51 +01:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected:
|
2019-10-05 13:54:14 +02:00
|
|
|
friend class VInFilter;
|
2010-01-20 13:15:51 +01:00
|
|
|
// Read file contents and return it
|
2010-04-07 02:20:44 +02:00
|
|
|
bool readWholefile(const string& filename, StrList& outl) {
|
2020-08-16 17:43:49 +02:00
|
|
|
const auto it = m_contentsMap.find(filename);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (it != m_contentsMap.end()) {
|
|
|
|
|
outl.push_back(it->second);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
if (!readContents(filename, outl)) return false;
|
|
|
|
|
if (listSize(outl) < INFILTER_CACHE_MAX) {
|
|
|
|
|
// Cache small files (only to save space)
|
|
|
|
|
// It's quite common to `include "timescale" thousands of times
|
2019-07-15 02:59:56 +02:00
|
|
|
// This isn't so important if it's just an open(), but filtering can be slow
|
2020-12-19 00:24:47 +01:00
|
|
|
m_contentsMap.emplace(filename, listString(outl));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
return true;
|
2010-01-20 13:15:51 +01:00
|
|
|
}
|
2020-12-23 21:21:33 +01:00
|
|
|
static size_t listSize(const StrList& sl) {
|
2019-05-19 22:13:13 +02:00
|
|
|
size_t out = 0;
|
2020-08-16 17:43:49 +02:00
|
|
|
for (const string& i : sl) out += i.length();
|
2019-05-19 22:13:13 +02:00
|
|
|
return out;
|
2010-04-07 02:20:44 +02:00
|
|
|
}
|
2020-12-23 21:21:33 +01:00
|
|
|
static string listString(const StrList& sl) {
|
2019-05-19 22:13:13 +02:00
|
|
|
string out;
|
2020-08-16 17:43:49 +02:00
|
|
|
for (const string& i : sl) out += i;
|
2019-05-19 22:13:13 +02:00
|
|
|
return out;
|
2010-04-07 02:20:44 +02:00
|
|
|
}
|
2010-01-20 13:15:51 +01:00
|
|
|
// CONSTRUCTORS
|
2020-08-16 15:55:36 +02:00
|
|
|
explicit VInFilterImp(const string& command) { start(command); }
|
2019-10-05 13:54:14 +02:00
|
|
|
~VInFilterImp() { stop(); }
|
2010-01-20 13:15:51 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
2019-10-05 13:54:14 +02:00
|
|
|
// VInFilter
|
2010-01-20 13:15:51 +01:00
|
|
|
// Just dispatch to the implementation
|
|
|
|
|
|
2019-10-05 13:54:14 +02:00
|
|
|
VInFilter::VInFilter(const string& command) { m_impp = new VInFilterImp(command); }
|
2020-04-15 13:58:34 +02:00
|
|
|
VInFilter::~VInFilter() {
|
2020-08-15 16:12:55 +02:00
|
|
|
if (m_impp) VL_DO_CLEAR(delete m_impp, m_impp = nullptr);
|
2020-04-15 13:58:34 +02:00
|
|
|
}
|
2010-01-20 13:15:51 +01:00
|
|
|
|
2019-10-05 13:54:14 +02:00
|
|
|
bool VInFilter::readWholefile(const string& filename, VInFilter::StrList& outl) {
|
2010-01-20 13:15:51 +01:00
|
|
|
if (!m_impp) v3fatalSrc("readWholefile on invalid filter");
|
2010-04-07 02:20:44 +02:00
|
|
|
return m_impp->readWholefile(filename, outl);
|
2010-01-20 13:15:51 +01:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
2010-01-07 16:50:23 +01:00
|
|
|
// V3OutFormatter: A class for printing to a file, with automatic indentation of C++ code.
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2012-03-20 20:57:29 +01:00
|
|
|
V3OutFormatter::V3OutFormatter(const string& filename, V3OutFormatter::Language lang)
|
2020-08-16 15:55:36 +02:00
|
|
|
: m_filename{filename}
|
|
|
|
|
, m_lang{lang} {
|
2016-09-14 04:53:09 +02:00
|
|
|
m_blockIndent = v3Global.opt.decoration() ? 4 : 1;
|
2020-04-15 13:58:34 +02:00
|
|
|
m_commaWidth = v3Global.opt.decoration() ? 50 : 150;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
2020-06-02 05:16:02 +02:00
|
|
|
string V3OutFormatter::indentSpaces(int num) {
|
2022-07-13 17:15:21 +02:00
|
|
|
// Indent the specified number of spaces.
|
|
|
|
|
if (num <= 0) return std::string{};
|
|
|
|
|
return std::string(std::min<size_t>(num, MAXSPACE), ' ');
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2022-01-10 00:11:24 +01:00
|
|
|
bool V3OutFormatter::tokenMatch(const char* cp, const char* cmp) {
|
|
|
|
|
while (*cmp && *cmp == *cp) {
|
2020-04-15 13:58:34 +02:00
|
|
|
++cp;
|
|
|
|
|
++cmp;
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
if (*cmp) return false;
|
|
|
|
|
if (*cp && !isspace(*cp)) return false;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-10 00:11:24 +01:00
|
|
|
bool V3OutFormatter::tokenStart(const char* cp) {
|
|
|
|
|
return (tokenMatch(cp, "begin") || tokenMatch(cp, "case") || tokenMatch(cp, "casex")
|
|
|
|
|
|| tokenMatch(cp, "casez") || tokenMatch(cp, "class") || tokenMatch(cp, "function")
|
|
|
|
|
|| tokenMatch(cp, "interface") || tokenMatch(cp, "module") || tokenMatch(cp, "package")
|
|
|
|
|
|| tokenMatch(cp, "task"));
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-07 16:50:23 +01:00
|
|
|
bool V3OutFormatter::tokenEnd(const char* cp) {
|
2022-01-10 00:11:24 +01:00
|
|
|
return (tokenMatch(cp, "end") || tokenMatch(cp, "endcase") || tokenMatch(cp, "endclass")
|
|
|
|
|
|| tokenMatch(cp, "endfunction") || tokenMatch(cp, "endinterface")
|
|
|
|
|
|| tokenMatch(cp, "endmodule") || tokenMatch(cp, "endpackage")
|
|
|
|
|
|| tokenMatch(cp, "endtask"));
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2019-11-10 02:35:12 +01:00
|
|
|
int V3OutFormatter::endLevels(const char* strg) {
|
2019-05-19 22:13:13 +02:00
|
|
|
int levels = m_indentLevel;
|
2006-08-26 13:35:28 +02:00
|
|
|
{
|
2019-05-19 22:13:13 +02:00
|
|
|
const char* cp = strg;
|
|
|
|
|
while (isspace(*cp)) cp++;
|
|
|
|
|
switch (*cp) {
|
|
|
|
|
case '\n': // Newlines.. No need for whitespace before it
|
2019-10-19 02:28:59 +02:00
|
|
|
return 0;
|
2019-05-19 22:13:13 +02:00
|
|
|
case '#': // Preproc directive
|
2019-10-19 02:28:59 +02:00
|
|
|
return 0;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
// label/public/private: Deindent by 2 spaces
|
|
|
|
|
const char* mp = cp;
|
2020-04-15 13:58:34 +02:00
|
|
|
for (; isalnum(*mp); mp++) {}
|
|
|
|
|
if (mp[0] == ':' && mp[1] != ':') return (levels - m_blockIndent / 2);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
// We want "} else {" to be one level to the left of normal
|
2020-04-15 13:58:34 +02:00
|
|
|
for (const char* cp = strg; *cp; cp++) {
|
2019-05-19 22:13:13 +02:00
|
|
|
switch (*cp) {
|
|
|
|
|
case '}':
|
2020-04-15 13:58:34 +02:00
|
|
|
case ')': levels -= m_blockIndent; break;
|
2019-05-19 22:13:13 +02:00
|
|
|
case '<':
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_lang == LA_XML) {
|
2019-05-19 22:13:13 +02:00
|
|
|
if (cp[1] == '/') levels -= m_blockIndent;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case 'e':
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_lang == LA_VERILOG && tokenEnd(cp)) levels -= m_blockIndent;
|
2019-05-19 22:13:13 +02:00
|
|
|
break;
|
|
|
|
|
case '\t':
|
2020-04-15 13:58:34 +02:00
|
|
|
case ' ': break; // Continue
|
|
|
|
|
default: return levels; // Letter
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2019-10-19 02:28:59 +02:00
|
|
|
return levels;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2019-11-10 02:35:12 +01:00
|
|
|
void V3OutFormatter::puts(const char* strg) {
|
2022-06-10 13:26:33 +02:00
|
|
|
if (!v3Global.opt.decoration()) {
|
|
|
|
|
putsOutput(strg);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2021-06-04 16:00:13 +02:00
|
|
|
if (m_prependIndent && strg[0] != '\n') {
|
2019-10-05 03:10:53 +02:00
|
|
|
putsNoTracking(indentSpaces(endLevels(strg)));
|
2019-05-19 22:13:13 +02:00
|
|
|
m_prependIndent = false;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
bool wordstart = true;
|
2018-10-08 03:23:45 +02:00
|
|
|
bool equalsForBracket = false; // Looking for "= {"
|
2020-04-15 13:58:34 +02:00
|
|
|
for (const char* cp = strg; *cp; cp++) {
|
2018-08-25 15:52:45 +02:00
|
|
|
putcNoTracking(*cp);
|
2022-01-10 00:11:24 +01:00
|
|
|
if (isalpha(*cp)) {
|
|
|
|
|
if (wordstart && m_lang == LA_VERILOG && tokenStart(cp)) indentInc();
|
|
|
|
|
if (wordstart && m_lang == LA_VERILOG && tokenEnd(cp)) indentDec();
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
switch (*cp) {
|
|
|
|
|
case '\n':
|
|
|
|
|
m_lineno++;
|
|
|
|
|
wordstart = true;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (cp[1] == '\0') {
|
|
|
|
|
// Add the indent later, may be a indentInc/indentDec
|
|
|
|
|
// called between now and then
|
|
|
|
|
m_prependIndent = true;
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
|
|
|
|
m_prependIndent = false;
|
2020-04-15 13:58:34 +02:00
|
|
|
putsNoTracking(indentSpaces(endLevels(cp + 1)));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
break;
|
2020-04-15 13:58:34 +02:00
|
|
|
case ' ': wordstart = true; break;
|
|
|
|
|
case '\t': wordstart = true; break;
|
2021-05-30 00:46:15 +02:00
|
|
|
case '"':
|
|
|
|
|
wordstart = false;
|
|
|
|
|
m_inStringLiteral = !m_inStringLiteral;
|
|
|
|
|
break;
|
2019-09-25 01:07:22 +02:00
|
|
|
case '/':
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_lang == LA_C || m_lang == LA_VERILOG) {
|
2021-05-30 00:46:15 +02:00
|
|
|
if (cp > strg && cp[-1] == '/' && !m_inStringLiteral) {
|
2019-09-25 01:07:22 +02:00
|
|
|
// Output ignoring contents to EOL
|
|
|
|
|
cp++;
|
|
|
|
|
while (*cp && cp[1] && cp[1] != '\n') putcNoTracking(*cp++);
|
2019-10-05 04:54:17 +02:00
|
|
|
if (*cp) putcNoTracking(*cp);
|
2019-09-25 01:07:22 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
2019-05-19 22:13:13 +02:00
|
|
|
case '{':
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_lang == LA_C && (equalsForBracket || m_bracketLevel)) {
|
2018-10-08 03:23:45 +02:00
|
|
|
// Break up large code inside "= { ..."
|
2020-04-15 13:58:34 +02:00
|
|
|
m_parenVec.push(m_indentLevel
|
|
|
|
|
* m_blockIndent); // Line up continuation with block+1
|
2018-10-08 03:23:45 +02:00
|
|
|
++m_bracketLevel;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
indentInc();
|
|
|
|
|
break;
|
|
|
|
|
case '}':
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_bracketLevel > 0) {
|
2018-10-08 03:23:45 +02:00
|
|
|
m_parenVec.pop();
|
|
|
|
|
--m_bracketLevel;
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
indentDec();
|
|
|
|
|
break;
|
|
|
|
|
case '(':
|
|
|
|
|
indentInc();
|
2022-06-10 13:26:33 +02:00
|
|
|
// Line up continuation with open paren, plus one indent
|
|
|
|
|
m_parenVec.push(m_column);
|
2019-05-19 22:13:13 +02:00
|
|
|
break;
|
|
|
|
|
case ')':
|
|
|
|
|
if (!m_parenVec.empty()) m_parenVec.pop();
|
|
|
|
|
indentDec();
|
|
|
|
|
break;
|
|
|
|
|
case '<':
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_lang == LA_XML) {
|
|
|
|
|
if (cp[1] == '/') {
|
|
|
|
|
// Zero as the > will result in net decrease by one
|
|
|
|
|
} else if (cp[1] == '!' || cp[1] == '?') {
|
|
|
|
|
indentInc(); // net same indent
|
|
|
|
|
} else {
|
|
|
|
|
indentInc(); // net increase by one
|
|
|
|
|
indentInc();
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case '>':
|
2020-04-15 13:58:34 +02:00
|
|
|
if (m_lang == LA_XML) {
|
2019-05-19 22:13:13 +02:00
|
|
|
indentDec();
|
2020-04-15 13:58:34 +02:00
|
|
|
if (cp > strg && cp[-1] == '/') indentDec(); // < ..... /> stays same level
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
break;
|
2020-04-15 13:58:34 +02:00
|
|
|
default: wordstart = false; break;
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2018-10-08 03:23:45 +02:00
|
|
|
|
|
|
|
|
switch (*cp) {
|
2020-04-15 13:58:34 +02:00
|
|
|
case '=': equalsForBracket = true; break;
|
|
|
|
|
case ' ': break;
|
|
|
|
|
default: equalsForBracket = false; break;
|
2018-10-08 03:23:45 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-25 15:52:45 +02:00
|
|
|
void V3OutFormatter::putBreakExpr() {
|
2006-08-26 13:35:28 +02:00
|
|
|
if (!m_parenVec.empty()) putBreak();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add a line break if too wide
|
2018-08-25 15:52:45 +02:00
|
|
|
void V3OutFormatter::putBreak() {
|
2022-06-10 13:26:33 +02:00
|
|
|
if (!v3Global.opt.decoration()) return;
|
2006-08-26 13:35:28 +02:00
|
|
|
if (!m_nobreak) {
|
2020-04-15 13:58:34 +02:00
|
|
|
// char s[1000]; sprintf(s, "{%d,%d}", m_column, m_parenVec.top()); putsNoTracking(s);
|
2019-05-19 22:13:13 +02:00
|
|
|
if (exceededWidth()) {
|
|
|
|
|
putcNoTracking('\n');
|
2019-10-05 03:10:53 +02:00
|
|
|
if (!m_parenVec.empty()) putsNoTracking(indentSpaces(m_parenVec.top()));
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-11 00:59:48 +01:00
|
|
|
void V3OutFormatter::putsQuoted(const string& strg) {
|
2009-05-08 19:16:19 +02:00
|
|
|
// Quote \ and " for use inside C programs
|
|
|
|
|
// Don't use to quote a filename for #include - #include doesn't \ escape.
|
|
|
|
|
putcNoTracking('"');
|
2021-06-21 00:32:57 +02:00
|
|
|
const string quoted = quoteNameControls(strg);
|
2020-08-16 18:54:32 +02:00
|
|
|
for (const char c : quoted) putcNoTracking(c);
|
2009-05-08 19:16:19 +02:00
|
|
|
putcNoTracking('"');
|
|
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
void V3OutFormatter::putsNoTracking(const string& strg) {
|
2022-06-10 13:26:33 +02:00
|
|
|
if (!v3Global.opt.decoration()) {
|
|
|
|
|
putsOutput(strg.c_str());
|
|
|
|
|
return;
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
// Don't track {}'s, probably because it's a $display format string
|
2020-08-16 18:54:32 +02:00
|
|
|
for (const char c : strg) putcNoTracking(c);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2018-08-25 15:52:45 +02:00
|
|
|
void V3OutFormatter::putcNoTracking(char chr) {
|
2022-06-10 13:26:33 +02:00
|
|
|
if (!v3Global.opt.decoration()) {
|
|
|
|
|
putcOutput(chr);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2006-08-26 13:35:28 +02:00
|
|
|
switch (chr) {
|
|
|
|
|
case '\n':
|
2019-05-19 22:13:13 +02:00
|
|
|
m_lineno++;
|
|
|
|
|
m_column = 0;
|
|
|
|
|
m_nobreak = true;
|
|
|
|
|
break;
|
2020-04-15 13:58:34 +02:00
|
|
|
case '\t': m_column = ((m_column + 9) / 8) * 8; break;
|
2006-08-26 13:35:28 +02:00
|
|
|
case ' ':
|
|
|
|
|
case '(':
|
|
|
|
|
case '|':
|
2020-04-15 13:58:34 +02:00
|
|
|
case '&': m_column++; break;
|
2006-08-26 13:35:28 +02:00
|
|
|
default:
|
2019-05-19 22:13:13 +02:00
|
|
|
m_column++;
|
|
|
|
|
m_nobreak = false;
|
|
|
|
|
break;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2018-08-25 15:52:45 +02:00
|
|
|
putcOutput(chr);
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2019-05-30 00:41:03 +02:00
|
|
|
string V3OutFormatter::quoteNameControls(const string& namein, V3OutFormatter::Language lang) {
|
|
|
|
|
// Encode control chars into output-appropriate escapes
|
2019-05-17 03:44:01 +02:00
|
|
|
// Reverse is V3Parse::deQuote
|
|
|
|
|
string out;
|
2020-04-15 13:58:34 +02:00
|
|
|
if (lang == LA_XML) {
|
2019-05-30 00:41:03 +02:00
|
|
|
// Encode chars into XML string
|
2020-08-16 18:54:32 +02:00
|
|
|
for (const char c : namein) {
|
|
|
|
|
if (c == '"') {
|
2019-05-30 00:41:03 +02:00
|
|
|
out += string(""");
|
2020-08-16 18:54:32 +02:00
|
|
|
} else if (c == '\'') {
|
2019-05-30 00:41:03 +02:00
|
|
|
out += string("'");
|
2020-08-16 18:54:32 +02:00
|
|
|
} else if (c == '<') {
|
2019-05-30 00:41:03 +02:00
|
|
|
out += string("<");
|
2020-08-16 18:54:32 +02:00
|
|
|
} else if (c == '>') {
|
2019-05-30 00:41:03 +02:00
|
|
|
out += string(">");
|
2020-08-16 18:54:32 +02:00
|
|
|
} else if (c == '&') {
|
2019-05-30 00:41:03 +02:00
|
|
|
out += string("&");
|
2020-08-16 18:54:32 +02:00
|
|
|
} else if (isprint(c)) {
|
|
|
|
|
out += c;
|
2019-05-30 00:41:03 +02:00
|
|
|
} else {
|
2021-03-06 16:33:43 +01:00
|
|
|
out += string("&#") + cvtToStr((unsigned int)(c & 0xff)) + ";";
|
2019-05-30 00:41:03 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Encode control chars into C style escapes
|
2020-08-16 18:54:32 +02:00
|
|
|
for (const char c : namein) {
|
|
|
|
|
if (c == '\\' || c == '"') {
|
|
|
|
|
out += string("\\") + c;
|
|
|
|
|
} else if (c == '\n') {
|
2019-05-30 00:41:03 +02:00
|
|
|
out += "\\n";
|
2020-08-16 18:54:32 +02:00
|
|
|
} else if (c == '\r') {
|
2019-05-30 00:41:03 +02:00
|
|
|
out += "\\r";
|
2020-08-16 18:54:32 +02:00
|
|
|
} else if (c == '\t') {
|
2019-05-30 00:41:03 +02:00
|
|
|
out += "\\t";
|
2020-08-16 18:54:32 +02:00
|
|
|
} else if (isprint(c)) {
|
|
|
|
|
out += c;
|
2019-05-30 00:41:03 +02:00
|
|
|
} else {
|
|
|
|
|
// This will also cover \a etc
|
2021-06-21 00:32:57 +02:00
|
|
|
const string octal = string("\\") + cvtToStr((c >> 6) & 3) + cvtToStr((c >> 3) & 7)
|
|
|
|
|
+ cvtToStr(c & 7);
|
2019-05-30 00:41:03 +02:00
|
|
|
out += octal;
|
|
|
|
|
}
|
2019-05-17 03:44:01 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
// Simple wrappers
|
|
|
|
|
|
2019-11-10 02:35:12 +01:00
|
|
|
void V3OutFormatter::printf(const char* fmt...) {
|
2021-03-06 16:33:43 +01:00
|
|
|
constexpr size_t bufsize = 5000;
|
|
|
|
|
char sbuff[bufsize];
|
2006-08-26 13:35:28 +02:00
|
|
|
va_list ap;
|
2019-05-19 22:13:13 +02:00
|
|
|
va_start(ap, fmt);
|
2021-03-06 16:33:43 +01:00
|
|
|
VL_VSNPRINTF(sbuff, bufsize, fmt, ap);
|
2006-08-26 13:35:28 +02:00
|
|
|
va_end(ap);
|
|
|
|
|
this->puts(sbuff);
|
|
|
|
|
}
|
|
|
|
|
|
2010-01-07 16:50:23 +01:00
|
|
|
//######################################################################
|
|
|
|
|
// V3OutFormatter: A class for printing to a file, with automatic indentation of C++ code.
|
|
|
|
|
|
2012-03-20 20:57:29 +01:00
|
|
|
V3OutFile::V3OutFile(const string& filename, V3OutFormatter::Language lang)
|
2022-07-04 16:23:31 +02:00
|
|
|
: V3OutFormatter{filename, lang}
|
|
|
|
|
, m_bufferp{new std::array<char, WRITE_BUFFER_SIZE_BYTES>{}} {
|
2020-08-15 16:12:55 +02:00
|
|
|
if ((m_fp = V3File::new_fopen_w(filename)) == nullptr) {
|
|
|
|
|
v3fatal("Cannot write " << filename);
|
|
|
|
|
}
|
2010-01-07 16:50:23 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
V3OutFile::~V3OutFile() {
|
2022-07-04 16:23:31 +02:00
|
|
|
writeBlock();
|
|
|
|
|
|
2010-01-07 16:50:23 +01:00
|
|
|
if (m_fp) fclose(m_fp);
|
2020-08-15 16:12:55 +02:00
|
|
|
m_fp = nullptr;
|
2010-01-07 16:50:23 +01:00
|
|
|
}
|
2017-02-09 13:43:43 +01:00
|
|
|
|
|
|
|
|
void V3OutFile::putsForceIncs() {
|
|
|
|
|
const V3StringList& forceIncs = v3Global.opt.forceIncs();
|
2020-08-16 18:54:32 +02:00
|
|
|
for (const string& i : forceIncs) { puts("#include \"" + i + "\"\n"); }
|
2017-02-09 13:43:43 +01:00
|
|
|
}
|
2019-10-06 19:24:21 +02:00
|
|
|
|
2019-12-24 01:00:17 +01:00
|
|
|
void V3OutCFile::putsGuard() {
|
|
|
|
|
UASSERT(!m_guard, "Already called putsGuard in emit file");
|
|
|
|
|
m_guard = true;
|
2021-03-04 03:57:07 +01:00
|
|
|
string var = VString::upcase(string("VERILATED_") + V3Os::filenameNonDir(filename()) + "_");
|
2020-11-11 04:10:38 +01:00
|
|
|
for (char& c : var) {
|
|
|
|
|
if (!isalnum(c)) c = '_';
|
2019-12-24 01:00:17 +01:00
|
|
|
}
|
|
|
|
|
puts("\n#ifndef " + var + "\n");
|
|
|
|
|
puts("#define " + var + " // guard\n");
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-06 19:24:21 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// VIdProtect
|
|
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class VIdProtectImp final {
|
2019-10-06 19:24:21 +02:00
|
|
|
// MEMBERS
|
2021-03-12 23:26:53 +01:00
|
|
|
std::map<const std::string, std::string> m_nameMap; // Map of old name into new name
|
|
|
|
|
std::unordered_set<std::string> m_newIdSet; // Which new names exist
|
2019-10-06 19:24:21 +02:00
|
|
|
protected:
|
|
|
|
|
// CONSTRUCTORS
|
|
|
|
|
friend class VIdProtect;
|
2020-04-15 13:58:34 +02:00
|
|
|
static VIdProtectImp& singleton() {
|
|
|
|
|
static VIdProtectImp s;
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-06 19:24:21 +02:00
|
|
|
public:
|
|
|
|
|
VIdProtectImp() {
|
|
|
|
|
passthru("this");
|
Introduce model interface class, make $root part or Syms (#3036)
This patch implements #3032. Verilator creates a module representing the
SystemVerilog $root scope (V3LinkLevel::wrapTop). Until now, this was
called the "TOP" module, which also acted as the user instantiated model
class. Syms used to hold a pointer to this root module, but hold
instances of any submodule. This patch renames this root scope module
from "TOP" to "$root", and introduces a separate model class which is
now an interface class. As the root module is no longer the user
interface class, it can now be made an instance of Syms, just like any
other submodule. This allows absolute references into the root module to
avoid an additional pointer indirection resulting in a potential speedup
(about 1.5% on OpenTitan). The model class now also contains all non
design specific generated code (e.g.: eval loops, trace config, etc),
which additionally simplifies Verilator internals.
Please see the updated documentation for the model interface changes.
2021-06-21 16:30:20 +02:00
|
|
|
passthru("TOP");
|
2021-06-13 15:33:11 +02:00
|
|
|
passthru("vlSelf");
|
2019-10-06 19:24:21 +02:00
|
|
|
passthru("vlSymsp");
|
|
|
|
|
}
|
2020-11-17 01:56:16 +01:00
|
|
|
~VIdProtectImp() = default;
|
2019-10-06 19:24:21 +02:00
|
|
|
// METHODS
|
|
|
|
|
string passthru(const string& old) {
|
|
|
|
|
if (!v3Global.opt.protectIds()) return old;
|
2020-08-16 17:43:49 +02:00
|
|
|
const auto it = m_nameMap.find(old);
|
2019-10-06 19:24:21 +02:00
|
|
|
if (it != m_nameMap.end()) {
|
|
|
|
|
// No way to go back and correct the older crypt name
|
2020-04-15 13:58:34 +02:00
|
|
|
UASSERT(old == it->second,
|
|
|
|
|
"Passthru request for '" + old + "' after already --protect-ids of it.");
|
|
|
|
|
} else {
|
2020-12-19 00:24:47 +01:00
|
|
|
m_nameMap.emplace(old, old);
|
2019-10-06 19:24:21 +02:00
|
|
|
m_newIdSet.insert(old);
|
|
|
|
|
}
|
|
|
|
|
return old;
|
|
|
|
|
}
|
|
|
|
|
string protectIf(const string& old, bool doIt) {
|
|
|
|
|
if (!v3Global.opt.protectIds() || old.empty() || !doIt) return old;
|
2020-08-16 17:43:49 +02:00
|
|
|
const auto it = m_nameMap.find(old);
|
2020-08-16 20:55:46 +02:00
|
|
|
if (it != m_nameMap.end()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
return it->second;
|
2020-08-16 20:55:46 +02:00
|
|
|
} else {
|
2019-10-06 19:24:21 +02:00
|
|
|
string out;
|
|
|
|
|
if (v3Global.opt.debugProtect()) {
|
|
|
|
|
// This lets us see the symbol being protected to debug cases
|
|
|
|
|
// where e.g. the definition is protect() but reference is
|
|
|
|
|
// missing a protect()
|
2020-04-15 13:58:34 +02:00
|
|
|
out = "PS" + old;
|
2019-10-06 19:24:21 +02:00
|
|
|
} else {
|
2020-04-15 13:58:34 +02:00
|
|
|
VHashSha256 digest(v3Global.opt.protectKeyDefaulted());
|
2019-10-06 19:24:21 +02:00
|
|
|
digest.insert(old);
|
|
|
|
|
// Add "PS" prefix (Protect Symbols) as cannot start symbol with number
|
2020-04-15 13:58:34 +02:00
|
|
|
out = "PS" + digest.digestSymbol();
|
2019-10-06 19:24:21 +02:00
|
|
|
// See if we can shrink the digest symbol to something smaller
|
|
|
|
|
for (size_t len = 6; len < out.size() - 3; len += 3) {
|
2021-06-21 00:32:57 +02:00
|
|
|
const string tryout = out.substr(0, len);
|
2019-10-06 19:24:21 +02:00
|
|
|
if (m_newIdSet.find(tryout) == m_newIdSet.end()) {
|
|
|
|
|
out = tryout;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-19 00:24:47 +01:00
|
|
|
m_nameMap.emplace(old, out);
|
2019-10-06 19:24:21 +02:00
|
|
|
m_newIdSet.insert(out);
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
string protectWordsIf(const string& old, bool doIt) {
|
2021-06-13 15:33:11 +02:00
|
|
|
// Split at " " (for traces), "." (for scopes), "->", "(", "&", ")" (for self pointers)
|
2019-10-06 19:24:21 +02:00
|
|
|
if (!(doIt && v3Global.opt.protectIds())) return old;
|
|
|
|
|
string out;
|
|
|
|
|
string::size_type start = 0;
|
|
|
|
|
// space, ., ->
|
2020-04-04 04:31:54 +02:00
|
|
|
while (true) {
|
2019-10-06 19:24:21 +02:00
|
|
|
// When C++11, use find_if and lambda
|
|
|
|
|
string::size_type pos = string::npos;
|
2020-04-04 04:31:54 +02:00
|
|
|
string separator;
|
2020-04-15 13:58:34 +02:00
|
|
|
trySep(old, start, " ", pos /*ref*/, separator /*ref*/);
|
|
|
|
|
trySep(old, start, ".", pos /*ref*/, separator /*ref*/);
|
|
|
|
|
trySep(old, start, "->", pos /*ref*/, separator /*ref*/);
|
2021-06-13 15:33:11 +02:00
|
|
|
trySep(old, start, "(", pos /*ref*/, separator /*ref*/);
|
|
|
|
|
trySep(old, start, "&", pos /*ref*/, separator /*ref*/);
|
|
|
|
|
trySep(old, start, ")", pos /*ref*/, separator /*ref*/);
|
2019-10-06 19:24:21 +02:00
|
|
|
if (pos == string::npos) break;
|
2020-04-15 13:58:34 +02:00
|
|
|
out += protectIf(old.substr(start, pos - start), true) + separator;
|
2019-10-06 19:24:21 +02:00
|
|
|
start = pos + separator.length();
|
|
|
|
|
}
|
|
|
|
|
out += protectIf(old.substr(start), true);
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
void writeMapFile(const string& filename) const {
|
2020-04-15 13:58:34 +02:00
|
|
|
V3OutXmlFile of(filename);
|
2019-10-06 19:24:21 +02:00
|
|
|
of.putsHeader();
|
2020-04-15 13:58:34 +02:00
|
|
|
of.puts("<!-- DESCR"
|
|
|
|
|
"IPTION: Verilator output: XML representation of netlist -->\n");
|
2019-10-06 19:24:21 +02:00
|
|
|
of.puts("<verilator_id_map>\n");
|
|
|
|
|
{
|
2020-11-11 04:10:38 +01:00
|
|
|
for (const auto& itr : m_nameMap) {
|
|
|
|
|
of.puts("<map from=\"" + itr.second + "\" to=\"" + itr.first + "\"/>\n");
|
2019-10-06 19:24:21 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
of.puts("</verilator_id_map>\n");
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
|
2019-10-06 19:24:21 +02:00
|
|
|
private:
|
|
|
|
|
void trySep(const string& old, string::size_type start, const string& trySep,
|
|
|
|
|
string::size_type& posr, string& separatorr) {
|
2021-11-26 23:55:36 +01:00
|
|
|
const string::size_type trypos = old.find(trySep, start);
|
2019-10-06 19:24:21 +02:00
|
|
|
if (trypos != string::npos) {
|
|
|
|
|
if (posr == string::npos || (posr > trypos)) {
|
|
|
|
|
posr = trypos;
|
|
|
|
|
separatorr = trySep;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
string VIdProtect::protectIf(const string& old, bool doIt) {
|
|
|
|
|
return VIdProtectImp::singleton().protectIf(old, doIt);
|
|
|
|
|
}
|
|
|
|
|
string VIdProtect::protectWordsIf(const string& old, bool doIt) {
|
|
|
|
|
return VIdProtectImp::singleton().protectWordsIf(old, doIt);
|
|
|
|
|
}
|
|
|
|
|
void VIdProtect::writeMapFile(const string& filename) {
|
|
|
|
|
VIdProtectImp::singleton().writeMapFile(filename);
|
|
|
|
|
}
|