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: Emit Makefile
|
|
|
|
|
//
|
2019-11-08 04:33:59 +01:00
|
|
|
// Code available from: https://verilator.org
|
2006-08-26 13:35:28 +02:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2025-01-01 14:30:25 +01:00
|
|
|
// Copyright 2004-2025 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
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2022-08-05 11:56:57 +02:00
|
|
|
#include "V3EmitMk.h"
|
|
|
|
|
|
|
|
|
|
#include "V3EmitCBase.h"
|
2020-08-15 15:43:53 +02:00
|
|
|
#include "V3HierBlock.h"
|
2015-02-27 02:40:45 +01:00
|
|
|
#include "V3Os.h"
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2024-10-01 03:42:36 +02:00
|
|
|
// Groups adjacent files in a list, evenly distributing sum of scores
|
|
|
|
|
class EmitGroup final {
|
|
|
|
|
public:
|
|
|
|
|
struct FileOrConcatenatedFilesList final {
|
|
|
|
|
const std::string m_filename; // Filename or output group filename if grouping
|
|
|
|
|
std::vector<std::string> m_concatenatedFilenames; // Grouped filenames if grouping
|
|
|
|
|
|
|
|
|
|
bool isConcatenatingFile() const { return !m_concatenatedFilenames.empty(); }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct FilenameWithScore final {
|
|
|
|
|
const std::string m_filename; // Input filename
|
|
|
|
|
const uint64_t m_score; // Input file complexity
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
private:
|
|
|
|
|
// Data of a single work unit used in `singleConcatenatedFilesList()`.
|
|
|
|
|
struct WorkList final {
|
|
|
|
|
uint64_t m_totalScore = 0; // Sum of scores of included files
|
|
|
|
|
std::vector<FilenameWithScore> m_files; // Included filenames
|
|
|
|
|
int m_bucketsNum
|
|
|
|
|
= 0; // Number of buckets assigned for this list. Used only in concatenable lists.
|
|
|
|
|
bool m_isConcatenable = true; // Indicated whether files on this list can be concatenated.
|
|
|
|
|
const int m_dbgId; // Work list ID for debugging.
|
|
|
|
|
|
|
|
|
|
WorkList() = delete;
|
|
|
|
|
WorkList(int id)
|
|
|
|
|
: m_dbgId{id} {}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// MAIN PARAMETERS
|
|
|
|
|
|
|
|
|
|
// Minimum number of input files required to perform concatenation.
|
|
|
|
|
// Concatenation of a small number of files does not give any performance advantages.
|
|
|
|
|
// The value has been chosen arbitrarily, most likely could be larger.
|
|
|
|
|
static constexpr size_t MIN_FILES_COUNT = 16;
|
|
|
|
|
|
|
|
|
|
// Concatenation of only a few files most likely does not increase performance.
|
|
|
|
|
// The value has been chosen arbitrarily.
|
|
|
|
|
static constexpr size_t MIN_FILES_PER_BUCKET = 4;
|
|
|
|
|
|
|
|
|
|
// MEMBERS
|
|
|
|
|
|
|
|
|
|
const std::vector<FilenameWithScore>
|
|
|
|
|
m_inputFiles; // List of filenames from initial AstCFile list
|
|
|
|
|
std::vector<FileOrConcatenatedFilesList>
|
|
|
|
|
m_outputFiles; // Output list of files and group files
|
|
|
|
|
const uint64_t m_totalScore; // Sum of file scores
|
|
|
|
|
const std::string m_groupFilePrefix; // Prefix for output group filenames
|
|
|
|
|
std::vector<WorkList> m_workLists; // Lists of small enough files
|
|
|
|
|
std::unique_ptr<std::ofstream> m_logp; // Dump file
|
|
|
|
|
std::vector<WorkList*> m_concatenableListsByDescSize; // Lists sorted by size, descending
|
|
|
|
|
|
|
|
|
|
EmitGroup(std::vector<FilenameWithScore> inputFiles, uint64_t totalScore,
|
|
|
|
|
std::string groupFilePrefix)
|
|
|
|
|
: m_inputFiles{std::move(inputFiles)}
|
|
|
|
|
, m_totalScore{totalScore}
|
|
|
|
|
, m_groupFilePrefix{groupFilePrefix} {}
|
|
|
|
|
|
|
|
|
|
// Debug logging: prints scores histogram
|
|
|
|
|
void dumpLogScoreHistogram(std::ostream& os) {
|
|
|
|
|
constexpr int MAX_BAR_LENGTH = 80;
|
|
|
|
|
constexpr int MAX_INTERVALS_NUM = 60;
|
|
|
|
|
|
|
|
|
|
// All scores arranged in ascending order.
|
|
|
|
|
std::vector<uint64_t> sortedScores;
|
|
|
|
|
sortedScores.reserve(m_inputFiles.size());
|
|
|
|
|
std::transform(m_inputFiles.begin(), m_inputFiles.end(), std::back_inserter(sortedScores),
|
|
|
|
|
[](const FilenameWithScore& inputFile) { return inputFile.m_score; });
|
|
|
|
|
std::sort(sortedScores.begin(), sortedScores.end());
|
|
|
|
|
|
|
|
|
|
const int64_t topScore = sortedScores.back();
|
2025-01-25 23:52:27 +01:00
|
|
|
os << "Top score: " << topScore << '\n';
|
2024-10-01 03:42:36 +02:00
|
|
|
const int maxScoreWidth = std::to_string(topScore).length();
|
|
|
|
|
|
|
|
|
|
const int64_t intervalsNum = std::min<int64_t>(topScore + 1, MAX_INTERVALS_NUM);
|
|
|
|
|
|
|
|
|
|
struct Interval final {
|
|
|
|
|
uint64_t m_lowerBound = 0;
|
|
|
|
|
int m_size = 0;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
std::vector<Interval> intervals;
|
|
|
|
|
intervals.resize(intervalsNum);
|
|
|
|
|
|
|
|
|
|
intervals[0].m_lowerBound = 0;
|
|
|
|
|
for (int i = 1; i < intervalsNum; i++) {
|
|
|
|
|
intervals[i].m_lowerBound = (topScore + 1) * i / intervalsNum + 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const uint64_t score : sortedScores) {
|
|
|
|
|
const unsigned int ivIdx = score * intervalsNum / (topScore + 1);
|
|
|
|
|
++intervals[ivIdx].m_size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int topIntervalSize = 0;
|
|
|
|
|
for (const Interval& iv : intervals)
|
|
|
|
|
topIntervalSize = std::max(topIntervalSize, iv.m_size);
|
|
|
|
|
|
2025-01-25 23:52:27 +01:00
|
|
|
os << "Input files' scores histogram:\n";
|
2024-10-01 03:42:36 +02:00
|
|
|
|
|
|
|
|
for (const Interval& iv : intervals) {
|
|
|
|
|
const int scaledSize = iv.m_size * (MAX_BAR_LENGTH + 1) / topIntervalSize;
|
|
|
|
|
std::string line = " |" + std::string(scaledSize, '#');
|
|
|
|
|
|
|
|
|
|
os << std::setw(maxScoreWidth) << iv.m_lowerBound << line << " " << iv.m_size << '\n';
|
|
|
|
|
}
|
|
|
|
|
os << std::setw(maxScoreWidth) << (topScore + 1) << '\n';
|
2025-01-25 23:52:27 +01:00
|
|
|
os << '\n';
|
2024-10-01 03:42:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PRIVATE METHODS
|
|
|
|
|
|
|
|
|
|
// Debug logging: dumps Work Lists and their lists of files
|
|
|
|
|
void dumpWorkLists(std::ostream& os) {
|
2025-01-25 23:52:27 +01:00
|
|
|
os << "Initial Work Lists with their concatenation eligibility status:\n";
|
2024-10-01 03:42:36 +02:00
|
|
|
for (const WorkList& list : m_workLists) {
|
|
|
|
|
os << "+ [" << (list.m_isConcatenable ? 'x' : ' ') << "] Work List #" << list.m_dbgId
|
|
|
|
|
<< " (num of files: " << list.m_files.size()
|
|
|
|
|
<< "; total score: " << list.m_totalScore << ")\n";
|
|
|
|
|
if (debug() >= 6 || list.m_files.size() < 4) {
|
|
|
|
|
// Log all files
|
|
|
|
|
for (const FilenameWithScore& file : list.m_files)
|
|
|
|
|
os << "| + " << file.m_filename << " (score: " << file.m_score << ")\n";
|
|
|
|
|
} else {
|
|
|
|
|
// Log only first and last file
|
|
|
|
|
const FilenameWithScore& first = list.m_files.front();
|
|
|
|
|
const FilenameWithScore& last = list.m_files.back();
|
|
|
|
|
os << "| + " << first.m_filename << " (score: " << first.m_score << ")\n";
|
|
|
|
|
os << "| | (... " << (list.m_files.size() - 2) << " files ...)\n";
|
|
|
|
|
os << "| + " << last.m_filename << " (score: " << last.m_score << ")\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-01-25 23:52:27 +01:00
|
|
|
os << '\n';
|
2024-10-01 03:42:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Debug logging: dumps list of output files. List of grouped files is additionally printed
|
|
|
|
|
// for each concatenating file.
|
|
|
|
|
void dumpOutputList(std::ostream& os) const {
|
2025-01-25 23:52:27 +01:00
|
|
|
os << "List of output files after execution of concatenation:\n";
|
2024-10-01 03:42:36 +02:00
|
|
|
|
|
|
|
|
for (const FileOrConcatenatedFilesList& entry : m_outputFiles) {
|
|
|
|
|
if (entry.isConcatenatingFile()) {
|
|
|
|
|
os << "+ " << entry.m_filename << " (concatenating file)\n";
|
|
|
|
|
for (const string& f : entry.m_concatenatedFilenames) {
|
|
|
|
|
os << "| + " << f << '\n';
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
os << "+ " << entry.m_filename << '\n';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Called when the concatenation is aborted, creates an identity mapping
|
|
|
|
|
bool fallbackNoGrouping(size_t inputFilesCount) {
|
|
|
|
|
const int totalBucketsNum = v3Global.opt.outputGroups();
|
|
|
|
|
// Return early if there's nothing to do.
|
|
|
|
|
bool groupingRedundant = false;
|
2025-01-25 23:52:27 +01:00
|
|
|
if (inputFilesCount < MIN_FILES_COUNT
|
|
|
|
|
&& inputFilesCount <= static_cast<size_t>(totalBucketsNum)) {
|
|
|
|
|
UINFO(4, "File concatenation skipped: Too few files (" << m_inputFiles.size() << " < "
|
|
|
|
|
<< MIN_FILES_COUNT << ")\n");
|
2024-10-01 03:42:36 +02:00
|
|
|
groupingRedundant = true;
|
|
|
|
|
}
|
|
|
|
|
if (inputFilesCount < (MIN_FILES_PER_BUCKET * totalBucketsNum)) {
|
|
|
|
|
UINFO(4, "File concatenation skipped: Too few files per bucket ("
|
|
|
|
|
<< m_inputFiles.size() << " < " << MIN_FILES_PER_BUCKET << " - "
|
2025-01-25 23:52:27 +01:00
|
|
|
<< totalBucketsNum << ")\n");
|
2024-10-01 03:42:36 +02:00
|
|
|
groupingRedundant = true;
|
|
|
|
|
}
|
|
|
|
|
if (!groupingRedundant) return false;
|
|
|
|
|
|
|
|
|
|
m_outputFiles.reserve(m_inputFiles.size());
|
|
|
|
|
for (const FilenameWithScore& filename : m_inputFiles) {
|
|
|
|
|
m_outputFiles.push_back({std::move(filename.m_filename), {}});
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void createWorkLists() {
|
|
|
|
|
// Create initial Work Lists.
|
|
|
|
|
|
|
|
|
|
const int totalBucketsNum = v3Global.opt.outputGroups();
|
|
|
|
|
// Input files with a score exceeding this value are excluded from concatenation.
|
|
|
|
|
const uint64_t concatenableFileMaxScore = m_totalScore / totalBucketsNum / 2;
|
|
|
|
|
|
|
|
|
|
V3Stats::addStat("Concatenation max score", concatenableFileMaxScore);
|
|
|
|
|
int nextWorkListId = 0;
|
|
|
|
|
|
2025-01-25 23:52:27 +01:00
|
|
|
if (m_logp) *m_logp << "Input files with their concatenation eligibility status:\n";
|
2024-10-01 03:42:36 +02:00
|
|
|
for (const FilenameWithScore& inputFile : m_inputFiles) {
|
|
|
|
|
const bool fileIsConcatenable = (inputFile.m_score <= concatenableFileMaxScore);
|
|
|
|
|
if (m_logp)
|
|
|
|
|
*m_logp << " + [" << (fileIsConcatenable ? 'x' : ' ') << "] "
|
2025-01-25 23:52:27 +01:00
|
|
|
<< inputFile.m_filename << " (score: " << inputFile.m_score << ")\n";
|
2024-10-01 03:42:36 +02:00
|
|
|
V3Stats::addStatSum(fileIsConcatenable ? "Concatenation total grouped score"
|
|
|
|
|
: "Concatenation total non-grouped score",
|
|
|
|
|
inputFile.m_score);
|
|
|
|
|
// Add new list if the last list's concatenability does not match the inputFile's
|
|
|
|
|
// concatenability
|
|
|
|
|
if (m_workLists.empty() || m_workLists.back().m_isConcatenable != fileIsConcatenable) {
|
|
|
|
|
m_workLists.push_back(WorkList{nextWorkListId++});
|
|
|
|
|
m_workLists.back().m_isConcatenable = fileIsConcatenable;
|
|
|
|
|
}
|
|
|
|
|
// Add inputFile to the last list
|
|
|
|
|
WorkList& list = m_workLists.back();
|
|
|
|
|
list.m_files.push_back({inputFile.m_filename, inputFile.m_score});
|
|
|
|
|
list.m_totalScore += inputFile.m_score;
|
|
|
|
|
}
|
2025-01-25 23:52:27 +01:00
|
|
|
if (m_logp) *m_logp << '\n';
|
2024-10-01 03:42:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void assignBuckets(uint64_t concatenableFilesTotalScore) {
|
|
|
|
|
// Assign buckets to lists
|
|
|
|
|
|
|
|
|
|
const size_t totalBucketsNum = v3Global.opt.outputGroups();
|
|
|
|
|
|
|
|
|
|
// More concatenable lists than buckets. Exclude lists with lowest number of files.
|
|
|
|
|
// Does not happen very often due to files being already filtered out by comparison of
|
|
|
|
|
// their score to ConcatenableFilesMaxScore.
|
|
|
|
|
if (m_concatenableListsByDescSize.size() > totalBucketsNum) {
|
|
|
|
|
// Debugging: Log which work lists will be kept
|
|
|
|
|
if (m_logp) {
|
|
|
|
|
*m_logp << "More Work Lists than buckets; "
|
2025-01-25 23:52:27 +01:00
|
|
|
"Work Lists with statuses indicating whether the list will be kept:\n";
|
2024-10-01 03:42:36 +02:00
|
|
|
// Only lists that will be kept. List that will be removed are logged below.
|
|
|
|
|
std::for_each(m_concatenableListsByDescSize.begin(),
|
|
|
|
|
m_concatenableListsByDescSize.begin() + totalBucketsNum,
|
|
|
|
|
[&](WorkList* listp) {
|
|
|
|
|
*m_logp << "+ [x] Work List #" << listp->m_dbgId
|
|
|
|
|
<< " (num of files: " << listp->m_files.size()
|
|
|
|
|
<< "; total score: " << listp->m_totalScore << ")\n";
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// NOTE: Not just debug logging - notice `isConcatenable` assignment in the loop.
|
|
|
|
|
std::for_each(m_concatenableListsByDescSize.begin() + totalBucketsNum,
|
|
|
|
|
m_concatenableListsByDescSize.end(), [&](WorkList* listp) {
|
|
|
|
|
listp->m_isConcatenable = false;
|
|
|
|
|
|
|
|
|
|
if (m_logp)
|
|
|
|
|
*m_logp << "+ [ ] Work List #" << listp->m_dbgId
|
|
|
|
|
<< " (num of files: " << listp->m_files.size()
|
|
|
|
|
<< "; total score: " << listp->m_totalScore << ")\n";
|
|
|
|
|
});
|
2025-01-25 23:52:27 +01:00
|
|
|
if (m_logp) *m_logp << '\n';
|
2024-10-01 03:42:36 +02:00
|
|
|
|
|
|
|
|
m_concatenableListsByDescSize.resize(totalBucketsNum);
|
|
|
|
|
// Recalculate stats
|
|
|
|
|
concatenableFilesTotalScore = 0;
|
|
|
|
|
for (WorkList* listp : m_concatenableListsByDescSize) {
|
|
|
|
|
concatenableFilesTotalScore += listp->m_totalScore;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const uint64_t idealBucketScore = concatenableFilesTotalScore / totalBucketsNum;
|
|
|
|
|
|
|
|
|
|
V3Stats::addStat("Concatenation ideal bucket score", idealBucketScore);
|
|
|
|
|
|
2025-01-25 23:52:27 +01:00
|
|
|
if (m_logp) *m_logp << "Buckets assigned to Work Lists:\n";
|
2024-10-01 03:42:36 +02:00
|
|
|
int availableBuckets = v3Global.opt.outputGroups();
|
|
|
|
|
for (WorkList* listp : m_concatenableListsByDescSize) {
|
|
|
|
|
if (availableBuckets > 0) {
|
|
|
|
|
listp->m_bucketsNum = std::min(
|
|
|
|
|
availableBuckets, std::max<int>(1, listp->m_totalScore / idealBucketScore));
|
|
|
|
|
availableBuckets -= listp->m_bucketsNum;
|
|
|
|
|
if (m_logp)
|
|
|
|
|
*m_logp << "+ [" << std::setw(2) << listp->m_bucketsNum << "] Work List #"
|
|
|
|
|
<< listp->m_dbgId << '\n';
|
|
|
|
|
} else {
|
|
|
|
|
// Out of buckets. Instead of recalculating everything just exclude the list.
|
|
|
|
|
listp->m_isConcatenable = false;
|
|
|
|
|
if (m_logp)
|
|
|
|
|
*m_logp << "+ [ 0] Work List #" << std::left << std::setw(4) << listp->m_dbgId
|
|
|
|
|
<< std::right << " (excluding from concatenation)\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-01-25 23:52:27 +01:00
|
|
|
if (m_logp) *m_logp << '\n';
|
2024-10-01 03:42:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void buildOutputList() {
|
|
|
|
|
// Assign files to buckets and build final list of files
|
|
|
|
|
|
|
|
|
|
// At this point the workLists contains concatenatable file lists separated by one or more
|
|
|
|
|
// non-concatenable file lists. Each concatenatable list has N buckets (where N > 0)
|
|
|
|
|
// assigned to it, which have to be filled with files from this list. Ideally, sum of file
|
|
|
|
|
// scores in every bucket should be the same.
|
|
|
|
|
int concatenatedFileId = 0;
|
|
|
|
|
for (WorkList& list : m_workLists) {
|
|
|
|
|
if (!list.m_isConcatenable) {
|
|
|
|
|
for (FilenameWithScore& file : list.m_files) {
|
|
|
|
|
m_outputFiles.push_back({std::move(file.m_filename), {}});
|
|
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ideal bucket score limited to buckets and score of the current Work List.
|
|
|
|
|
const uint64_t listIdealBucketScore = list.m_totalScore / list.m_bucketsNum;
|
|
|
|
|
|
|
|
|
|
auto fileIt = list.m_files.begin();
|
|
|
|
|
for (int i = 0; i < list.m_bucketsNum; ++i) {
|
|
|
|
|
FileOrConcatenatedFilesList bucket{v3Global.opt.prefix() + "_" + m_groupFilePrefix
|
|
|
|
|
+ std::to_string(concatenatedFileId++),
|
|
|
|
|
{}};
|
|
|
|
|
|
|
|
|
|
uint64_t bucketScore = 0;
|
|
|
|
|
|
|
|
|
|
for (; fileIt != list.m_files.end(); ++fileIt) {
|
|
|
|
|
const uint64_t diffNow
|
|
|
|
|
= std::abs((int64_t)(listIdealBucketScore - bucketScore));
|
|
|
|
|
const uint64_t diffIfAdded = std::abs(
|
|
|
|
|
(int64_t)(listIdealBucketScore - bucketScore - fileIt->m_score));
|
|
|
|
|
if (bucketScore == 0 || fileIt->m_score == 0 || diffNow > diffIfAdded) {
|
|
|
|
|
// Bucket score will be better with the file in it.
|
|
|
|
|
bucketScore += fileIt->m_score;
|
|
|
|
|
bucket.m_concatenatedFilenames.push_back(std::move(fileIt->m_filename));
|
|
|
|
|
} else {
|
|
|
|
|
// Best possible bucket score reached, process next bucket.
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-31 19:38:53 +01:00
|
|
|
const bool lastBucketAndLeftovers
|
|
|
|
|
= (i + 1 == list.m_bucketsNum) && (fileIt != list.m_files.end());
|
|
|
|
|
if (bucket.m_concatenatedFilenames.size() > 1 || lastBucketAndLeftovers) {
|
|
|
|
|
m_outputFiles.push_back(std::move(bucket));
|
|
|
|
|
} else if (bucket.m_concatenatedFilenames.size() == 1) {
|
2024-10-01 03:42:36 +02:00
|
|
|
// Unwrap the bucket if it contains only one file.
|
|
|
|
|
m_outputFiles.push_back(
|
|
|
|
|
{std::move(bucket.m_concatenatedFilenames.front()), {}});
|
|
|
|
|
}
|
|
|
|
|
// Most likely no bucket will be empty in normal situations. If it happen the
|
|
|
|
|
// bucket will just be dropped.
|
|
|
|
|
}
|
|
|
|
|
for (; fileIt != list.m_files.end(); ++fileIt) {
|
|
|
|
|
// The Work List is out of buckets, but some files were left.
|
|
|
|
|
// Add them to the last bucket.
|
2024-10-31 19:38:53 +01:00
|
|
|
UASSERT(m_outputFiles.back().isConcatenatingFile(),
|
|
|
|
|
"Cannot add leftover files to a single file");
|
2024-10-01 03:42:36 +02:00
|
|
|
m_outputFiles.back().m_concatenatedFilenames.push_back(fileIt->m_filename);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void assertFilesSame() const {
|
|
|
|
|
auto ifIt = m_inputFiles.begin();
|
|
|
|
|
auto ofIt = m_outputFiles.begin();
|
|
|
|
|
while (ifIt != m_inputFiles.end() && ofIt != m_outputFiles.end()) {
|
|
|
|
|
if (ofIt->isConcatenatingFile()) {
|
|
|
|
|
for (const string& ocf : ofIt->m_concatenatedFilenames) {
|
|
|
|
|
UASSERT(ifIt != m_inputFiles.end(),
|
|
|
|
|
"More output files than input files. First extra file: " << ocf);
|
|
|
|
|
UASSERT(ifIt->m_filename == ocf,
|
|
|
|
|
"Name mismatch: " << ifIt->m_filename << " != " << ocf);
|
|
|
|
|
++ifIt;
|
|
|
|
|
}
|
|
|
|
|
++ofIt;
|
|
|
|
|
} else {
|
|
|
|
|
UASSERT(ifIt->m_filename == ofIt->m_filename,
|
|
|
|
|
"Name mismatch: " << ifIt->m_filename << " != " << ofIt->m_filename);
|
|
|
|
|
++ifIt;
|
|
|
|
|
++ofIt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
UASSERT(ifIt == m_inputFiles.end(),
|
|
|
|
|
"More input files than input files. First extra file: " << ifIt->m_filename);
|
|
|
|
|
UASSERT(ofIt == m_outputFiles.end(),
|
|
|
|
|
"More output files than input files. First extra file: " << ofIt->m_filename);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void process() {
|
2025-01-25 23:52:27 +01:00
|
|
|
UINFO(4, __FUNCTION__ << " group file prefix: " << m_groupFilePrefix << '\n');
|
|
|
|
|
UINFO(5, "Number of input files: " << m_inputFiles.size() << '\n');
|
|
|
|
|
UINFO(5, "Total score: " << m_totalScore << '\n');
|
2024-10-01 03:42:36 +02:00
|
|
|
|
|
|
|
|
const int totalBucketsNum = v3Global.opt.outputGroups();
|
2025-01-25 23:52:27 +01:00
|
|
|
UINFO(5, "Number of buckets: " << totalBucketsNum << '\n');
|
2024-10-01 03:42:36 +02:00
|
|
|
UASSERT(totalBucketsNum > 0, "More than 0 buckets required");
|
|
|
|
|
|
|
|
|
|
if (fallbackNoGrouping(m_inputFiles.size())) return;
|
|
|
|
|
|
2025-01-25 23:52:27 +01:00
|
|
|
if (debug() >= 6 || dumpLevel() >= 6) {
|
2024-10-01 03:42:36 +02:00
|
|
|
const string filename = v3Global.debugFilename("outputgroup") + ".txt";
|
2025-01-25 23:52:27 +01:00
|
|
|
UINFO(5, "Dumping " << filename << endl);
|
2024-10-01 03:42:36 +02:00
|
|
|
m_logp = std::unique_ptr<std::ofstream>{V3File::new_ofstream(filename)};
|
2025-03-24 00:51:54 +01:00
|
|
|
if (m_logp->fail()) v3fatal("Can't write file: " << filename);
|
2024-10-01 03:42:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_logp) dumpLogScoreHistogram(*m_logp);
|
|
|
|
|
|
|
|
|
|
createWorkLists();
|
|
|
|
|
|
|
|
|
|
// Collect stats and mark lists with only one file as non-concatenable
|
|
|
|
|
size_t concatenableFilesCount = 0;
|
|
|
|
|
int64_t concatenableFilesTotalScore = 0;
|
|
|
|
|
|
|
|
|
|
for (WorkList& list : m_workLists) {
|
|
|
|
|
if (!list.m_isConcatenable) continue;
|
|
|
|
|
|
|
|
|
|
// "Concatenation" of a single file is pointless
|
|
|
|
|
if (list.m_files.size() == 1) {
|
|
|
|
|
list.m_isConcatenable = false;
|
|
|
|
|
UINFO(5, "Excluding from concatenation: Work List contains only one file: "
|
|
|
|
|
"Work List #"
|
|
|
|
|
<< list.m_dbgId << endl);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
concatenableFilesCount += list.m_files.size();
|
|
|
|
|
concatenableFilesTotalScore += list.m_totalScore;
|
|
|
|
|
// This vector is sorted below
|
|
|
|
|
m_concatenableListsByDescSize.push_back(&list);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (m_logp) dumpWorkLists(*m_logp);
|
|
|
|
|
|
|
|
|
|
// Check concatenation conditions again using more precise data
|
|
|
|
|
if (fallbackNoGrouping(concatenableFilesCount)) return;
|
|
|
|
|
|
|
|
|
|
std::sort(m_concatenableListsByDescSize.begin(), m_concatenableListsByDescSize.end(),
|
|
|
|
|
[](const WorkList* ap, const WorkList* bp) {
|
|
|
|
|
// Sort in descending order by number of files
|
|
|
|
|
if (ap->m_files.size() != bp->m_files.size())
|
|
|
|
|
return (ap->m_files.size() > bp->m_files.size());
|
|
|
|
|
// As a fallback sort in ascending order by totalSize. This makes lists
|
|
|
|
|
// with higher score more likely to be excluded.
|
|
|
|
|
return bp->m_totalScore > ap->m_totalScore;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
assignBuckets(concatenableFilesTotalScore);
|
|
|
|
|
|
|
|
|
|
buildOutputList();
|
|
|
|
|
|
|
|
|
|
if (m_logp) dumpOutputList(*m_logp);
|
|
|
|
|
assertFilesSame();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
static std::vector<FileOrConcatenatedFilesList>
|
|
|
|
|
singleConcatenatedFilesList(std::vector<FilenameWithScore> inputFiles, uint64_t totalScore,
|
|
|
|
|
std::string groupFilePrefix) {
|
|
|
|
|
EmitGroup group{std::move(inputFiles), totalScore, groupFilePrefix};
|
|
|
|
|
group.process();
|
|
|
|
|
return group.m_outputFiles;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2022-10-12 11:19:21 +02:00
|
|
|
// ######################################################################
|
|
|
|
|
// Emit statements and expressions
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class EmitMk final {
|
2024-10-01 03:42:36 +02:00
|
|
|
using FileOrConcatenatedFilesList = EmitGroup::FileOrConcatenatedFilesList;
|
|
|
|
|
using FilenameWithScore = EmitGroup::FilenameWithScore;
|
|
|
|
|
|
2025-01-25 19:35:56 +01:00
|
|
|
// MEMBERS
|
|
|
|
|
double m_putClassCount = 0; // Number of classEntries printed
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
public:
|
|
|
|
|
// METHODS
|
2009-01-21 22:56:50 +01:00
|
|
|
|
2024-10-01 03:42:36 +02:00
|
|
|
static void emitConcatenatingFile(const FileOrConcatenatedFilesList& entry) {
|
|
|
|
|
UASSERT(entry.isConcatenatingFile(), "Passed entry does not represent concatenating file");
|
|
|
|
|
|
|
|
|
|
V3OutCFile concatenatingFile{v3Global.opt.makeDir() + "/" + entry.m_filename + ".cpp"};
|
|
|
|
|
concatenatingFile.putsHeader();
|
|
|
|
|
for (const string& file : entry.m_concatenatedFilenames) {
|
|
|
|
|
concatenatingFile.puts("#include \"" + file + ".cpp\"\n");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2009-12-03 03:15:56 +01:00
|
|
|
void putMakeClassEntry(V3OutMkFile& of, const string& name) {
|
2020-04-15 13:58:34 +02:00
|
|
|
of.puts("\t" + V3Os::filenameNonDirExt(name) + " \\\n");
|
2025-01-25 19:35:56 +01:00
|
|
|
++m_putClassCount;
|
2009-12-03 03:15:56 +01:00
|
|
|
}
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
void emitClassMake() {
|
2024-10-01 03:42:36 +02:00
|
|
|
std::vector<FileOrConcatenatedFilesList> vmClassesSlowList;
|
|
|
|
|
std::vector<FileOrConcatenatedFilesList> vmClassesFastList;
|
|
|
|
|
if (v3Global.opt.outputGroups() > 0) {
|
|
|
|
|
std::vector<FilenameWithScore> slowFiles;
|
|
|
|
|
std::vector<FilenameWithScore> fastFiles;
|
|
|
|
|
uint64_t slowTotalScore = 0;
|
|
|
|
|
uint64_t fastTotalScore = 0;
|
|
|
|
|
|
|
|
|
|
for (AstNodeFile* nodep = v3Global.rootp()->filesp(); nodep;
|
|
|
|
|
nodep = VN_AS(nodep->nextp(), NodeFile)) {
|
|
|
|
|
const AstCFile* const cfilep = VN_CAST(nodep, CFile);
|
|
|
|
|
if (cfilep && cfilep->source() && cfilep->support() == false) {
|
|
|
|
|
std::vector<FilenameWithScore>& files = cfilep->slow() ? slowFiles : fastFiles;
|
|
|
|
|
uint64_t& totalScore = cfilep->slow() ? slowTotalScore : fastTotalScore;
|
|
|
|
|
|
|
|
|
|
totalScore += cfilep->complexityScore();
|
|
|
|
|
files.push_back(
|
|
|
|
|
{V3Os::filenameNonDirExt(cfilep->name()), cfilep->complexityScore()});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
vmClassesSlowList = EmitGroup::singleConcatenatedFilesList(
|
|
|
|
|
std::move(slowFiles), slowTotalScore, "vm_classes_Slow_");
|
|
|
|
|
vmClassesFastList = EmitGroup::singleConcatenatedFilesList(
|
|
|
|
|
std::move(fastFiles), fastTotalScore, "vm_classes_");
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
// Generate the makefile
|
2022-11-20 19:11:01 +01:00
|
|
|
V3OutMkFile of{v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "_classes.mk"};
|
2019-05-19 22:13:13 +02:00
|
|
|
of.putsHeader();
|
2020-04-15 13:58:34 +02:00
|
|
|
of.puts("# DESCR"
|
|
|
|
|
"IPTION: Verilator output: Make include file with class lists\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("#\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
of.puts("# This file lists generated Verilated files, for including "
|
|
|
|
|
"in higher level makefiles.\n");
|
|
|
|
|
of.puts("# See " + v3Global.opt.prefix() + ".mk" + " for the caller.\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
|
|
|
|
|
of.puts("\n### Switches...\n");
|
2020-08-16 21:10:43 +02:00
|
|
|
of.puts("# C11 constructs required? 0/1 (always on now)\n");
|
|
|
|
|
of.puts("VM_C11 = 1\n");
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
of.puts("# Timing enabled? 0/1\n");
|
|
|
|
|
of.puts("VM_TIMING = ");
|
|
|
|
|
of.puts(v3Global.usesTiming() ? "1" : "0");
|
|
|
|
|
of.puts("\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("# Coverage output mode? 0/1 (from --coverage)\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
of.puts("VM_COVERAGE = ");
|
|
|
|
|
of.puts(v3Global.opt.coverage() ? "1" : "0");
|
|
|
|
|
of.puts("\n");
|
2020-03-08 14:03:29 +01:00
|
|
|
of.puts("# Parallel builds? 0/1 (from --output-split)\n");
|
|
|
|
|
of.puts("VM_PARALLEL_BUILDS = ");
|
2020-05-25 22:12:34 +02:00
|
|
|
of.puts(v3Global.useParallelBuild() ? "1" : "0");
|
2020-04-15 13:58:34 +02:00
|
|
|
of.puts("\n");
|
2025-03-07 16:41:29 +01:00
|
|
|
of.puts("# Tracing output mode? 0/1 (from --trace/--trace-fst/--trace-saif)\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
of.puts("VM_TRACE = ");
|
|
|
|
|
of.puts(v3Global.opt.trace() ? "1" : "0");
|
|
|
|
|
of.puts("\n");
|
2022-05-20 17:02:43 +02:00
|
|
|
of.puts("# Tracing output mode in VCD format? 0/1 (from --trace)\n");
|
|
|
|
|
of.puts("VM_TRACE_VCD = ");
|
|
|
|
|
of.puts(v3Global.opt.trace() && v3Global.opt.traceFormat().vcd() ? "1" : "0");
|
|
|
|
|
of.puts("\n");
|
2020-10-11 03:17:39 +02:00
|
|
|
of.puts("# Tracing output mode in FST format? 0/1 (from --trace-fst)\n");
|
|
|
|
|
of.puts("VM_TRACE_FST = ");
|
|
|
|
|
of.puts(v3Global.opt.trace() && v3Global.opt.traceFormat().fst() ? "1" : "0");
|
|
|
|
|
of.puts("\n");
|
2025-03-07 16:41:29 +01:00
|
|
|
of.puts("# Tracing output mode in SAIF format? 0/1 (from --trace-saif)\n");
|
|
|
|
|
of.puts("VM_TRACE_SAIF = ");
|
|
|
|
|
of.puts(v3Global.opt.trace() && v3Global.opt.traceFormat().saif() ? "1" : "0");
|
|
|
|
|
of.puts("\n");
|
2006-08-26 13:35:28 +02:00
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("\n### Object file lists...\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
for (int support = 0; support < 3; ++support) {
|
2021-07-11 16:42:32 +02:00
|
|
|
for (const bool& slow : {false, true}) {
|
2020-04-15 13:58:34 +02:00
|
|
|
if (support == 2) {
|
|
|
|
|
of.puts("# Global classes, need linked once per executable");
|
|
|
|
|
} else if (support) {
|
|
|
|
|
of.puts("# Generated support classes");
|
|
|
|
|
} else {
|
|
|
|
|
of.puts("# Generated module classes");
|
|
|
|
|
}
|
|
|
|
|
if (slow) {
|
|
|
|
|
of.puts(", non-fast-path, compile with low/medium optimization\n");
|
|
|
|
|
} else {
|
|
|
|
|
of.puts(", fast-path, compile with highest optimization\n");
|
|
|
|
|
}
|
2025-01-25 19:35:56 +01:00
|
|
|
const string targetVar = (support == 2 ? "VM_GLOBAL"s
|
|
|
|
|
: support == 1 ? "VM_SUPPORT"s
|
|
|
|
|
: "VM_CLASSES"s)
|
|
|
|
|
+ (slow ? "_SLOW" : "_FAST");
|
|
|
|
|
m_putClassCount = 0;
|
|
|
|
|
of.puts(targetVar + " += \\\n");
|
2020-08-15 15:43:53 +02:00
|
|
|
if (support == 2 && v3Global.opt.hierChild()) {
|
|
|
|
|
// Do nothing because VM_GLOBAL is necessary per executable. Top module will
|
|
|
|
|
// have them.
|
|
|
|
|
} else if (support == 2 && !slow) {
|
2019-05-19 22:13:13 +02:00
|
|
|
putMakeClassEntry(of, "verilated.cpp");
|
2021-02-22 03:25:21 +01:00
|
|
|
if (v3Global.dpi()) putMakeClassEntry(of, "verilated_dpi.cpp");
|
|
|
|
|
if (v3Global.opt.vpi()) putMakeClassEntry(of, "verilated_vpi.cpp");
|
|
|
|
|
if (v3Global.opt.savable()) putMakeClassEntry(of, "verilated_save.cpp");
|
|
|
|
|
if (v3Global.opt.coverage()) putMakeClassEntry(of, "verilated_cov.cpp");
|
2019-05-19 22:13:13 +02:00
|
|
|
if (v3Global.opt.trace()) {
|
2020-03-02 03:39:23 +01:00
|
|
|
putMakeClassEntry(of, v3Global.opt.traceSourceBase() + "_c.cpp");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2022-12-04 23:30:51 +01:00
|
|
|
if (v3Global.usesProbDist()) putMakeClassEntry(of, "verilated_probdist.cpp");
|
Timing support (#3363)
Adds timing support to Verilator. It makes it possible to use delays,
event controls within processes (not just at the start), wait
statements, and forks.
Building a design with those constructs requires a compiler that
supports C++20 coroutines (GCC 10, Clang 5).
The basic idea is to have processes and tasks with delays/event controls
implemented as C++20 coroutines. This allows us to suspend and resume
them at any time.
There are five main runtime classes responsible for managing suspended
coroutines:
* `VlCoroutineHandle`, a wrapper over C++20's `std::coroutine_handle`
with move semantics and automatic cleanup.
* `VlDelayScheduler`, for coroutines suspended by delays. It resumes
them at a proper simulation time.
* `VlTriggerScheduler`, for coroutines suspended by event controls. It
resumes them if its corresponding trigger was set.
* `VlForkSync`, used for syncing `fork..join` and `fork..join_any`
blocks.
* `VlCoroutine`, the return type of all verilated coroutines. It allows
for suspending a stack of coroutines (normally, C++ coroutines are
stackless).
There is a new visitor in `V3Timing.cpp` which:
* scales delays according to the timescale,
* simplifies intra-assignment timing controls and net delays into
regular timing controls and assignments,
* simplifies wait statements into loops with event controls,
* marks processes and tasks with timing controls in them as
suspendable,
* creates delay, trigger scheduler, and fork sync variables,
* transforms timing controls and fork joins into C++ awaits
There are new functions in `V3SchedTiming.cpp` (used by `V3Sched.cpp`)
that integrate static scheduling with timing. This involves providing
external domains for variables, so that the necessary combinational
logic gets triggered after coroutine resumption, as well as statements
that need to be injected into the design eval function to perform this
resumption at the correct time.
There is also a function that transforms forked processes into separate
functions.
See the comments in `verilated_timing.h`, `verilated_timing.cpp`,
`V3Timing.cpp`, and `V3SchedTiming.cpp`, as well as the internals
documentation for more details.
Signed-off-by: Krzysztof Bieganski <kbieganski@antmicro.com>
2022-08-22 14:26:32 +02:00
|
|
|
if (v3Global.usesTiming()) putMakeClassEntry(of, "verilated_timing.cpp");
|
2024-05-17 16:38:34 +02:00
|
|
|
if (v3Global.useRandomizeMethods())
|
|
|
|
|
putMakeClassEntry(of, "verilated_random.cpp");
|
2023-10-21 14:53:56 +02:00
|
|
|
putMakeClassEntry(of, "verilated_threads.cpp");
|
2022-03-25 20:46:50 +01:00
|
|
|
if (v3Global.opt.usesProfiler()) {
|
|
|
|
|
putMakeClassEntry(of, "verilated_profiler.cpp");
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
} else if (support == 2 && slow) {
|
2024-10-01 03:42:36 +02:00
|
|
|
} else if (support == 0 && v3Global.opt.outputGroups() > 0) {
|
|
|
|
|
const std::vector<FileOrConcatenatedFilesList>& list
|
|
|
|
|
= slow ? vmClassesSlowList : vmClassesFastList;
|
|
|
|
|
for (const FileOrConcatenatedFilesList& entry : list) {
|
2025-01-25 19:35:56 +01:00
|
|
|
if (entry.isConcatenatingFile()) emitConcatenatingFile(entry);
|
2024-10-01 03:42:36 +02:00
|
|
|
putMakeClassEntry(of, entry.m_filename);
|
|
|
|
|
}
|
2020-04-15 13:58:34 +02:00
|
|
|
} else {
|
|
|
|
|
for (AstNodeFile* nodep = v3Global.rootp()->filesp(); nodep;
|
2021-10-22 14:56:48 +02:00
|
|
|
nodep = VN_AS(nodep->nextp(), NodeFile)) {
|
2021-11-13 19:50:44 +01:00
|
|
|
const AstCFile* const cfilep = VN_CAST(nodep, CFile);
|
2020-04-15 13:58:34 +02:00
|
|
|
if (cfilep && cfilep->source() && cfilep->slow() == (slow != 0)
|
|
|
|
|
&& cfilep->support() == (support != 0)) {
|
2019-09-27 09:44:23 +02:00
|
|
|
putMakeClassEntry(of, cfilep->name());
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
of.puts("\n");
|
2025-01-25 19:35:56 +01:00
|
|
|
V3Stats::addStat("Makefile targets, " + targetVar, m_putClassCount);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
of.puts("\n");
|
|
|
|
|
of.putsHeader();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void emitOverallMake() {
|
2019-05-19 22:13:13 +02:00
|
|
|
// Generate the makefile
|
2022-11-20 19:11:01 +01:00
|
|
|
V3OutMkFile of{v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + ".mk"};
|
2019-05-19 22:13:13 +02:00
|
|
|
of.putsHeader();
|
2020-04-15 13:58:34 +02:00
|
|
|
of.puts("# DESCR"
|
|
|
|
|
"IPTION: Verilator output: "
|
|
|
|
|
"Makefile for building Verilated archive or executable\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("#\n");
|
|
|
|
|
of.puts("# Execute this makefile from the object directory:\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
of.puts("# make -f " + v3Global.opt.prefix() + ".mk" + "\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("\n");
|
|
|
|
|
|
|
|
|
|
if (v3Global.opt.exe()) {
|
2020-04-15 13:58:34 +02:00
|
|
|
of.puts("default: " + v3Global.opt.exeName() + "\n");
|
2021-11-14 15:39:31 +01:00
|
|
|
} else if (!v3Global.opt.libCreate().empty()) {
|
|
|
|
|
of.puts("default: lib" + v3Global.opt.libCreate() + "\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
} else {
|
2023-12-23 15:31:10 +01:00
|
|
|
of.puts("default: lib" + v3Global.opt.prefix() + "\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
|
|
|
|
of.puts("\n### Constants...\n");
|
2024-07-26 15:35:37 +02:00
|
|
|
of.puts("# Perl executable (from $PERL, defaults to 'perl' if not set)\n");
|
2023-09-26 21:42:15 +02:00
|
|
|
of.puts("PERL = " + V3OutFormatter::quoteNameControls(V3Options::getenvPERL()) + "\n");
|
2024-07-26 15:35:37 +02:00
|
|
|
of.puts("# Python3 executable (from $PYTHON3, defaults to 'python3' if not set)\n");
|
|
|
|
|
of.puts("PYTHON3 = " + V3OutFormatter::quoteNameControls(V3Options::getenvPYTHON3())
|
|
|
|
|
+ "\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("# Path to Verilator kit (from $VERILATOR_ROOT)\n");
|
2023-09-26 21:42:15 +02:00
|
|
|
of.puts("VERILATOR_ROOT = "
|
|
|
|
|
+ V3OutFormatter::quoteNameControls(V3Options::getenvVERILATOR_ROOT()) + "\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("# SystemC include directory with systemc.h (from $SYSTEMC_INCLUDE)\n");
|
2024-07-14 17:39:45 +02:00
|
|
|
of.puts("SYSTEMC_INCLUDE ?= "s + V3Options::getenvSYSTEMC_INCLUDE() + "\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("# SystemC library directory with libsystemc.a (from $SYSTEMC_LIBDIR)\n");
|
2024-07-14 17:39:45 +02:00
|
|
|
of.puts("SYSTEMC_LIBDIR ?= "s + V3Options::getenvSYSTEMC_LIBDIR() + "\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
|
2020-05-29 00:51:46 +02:00
|
|
|
// Only check it if we really need the value
|
2021-03-08 03:05:15 +01:00
|
|
|
if (v3Global.opt.systemC() && !V3Options::systemCFound()) {
|
2020-05-29 00:51:46 +02:00
|
|
|
v3fatal("Need $SYSTEMC_INCLUDE in environment or when Verilator configured,\n"
|
|
|
|
|
"and need $SYSTEMC_LIBDIR in environment or when Verilator configured\n"
|
|
|
|
|
"Probably System-C isn't installed, see http://www.systemc.org\n");
|
|
|
|
|
}
|
|
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("\n### Switches...\n");
|
2021-07-08 01:12:52 +02:00
|
|
|
of.puts("# C++ code coverage 0/1 (from --prof-c)\n");
|
2024-07-14 17:39:45 +02:00
|
|
|
of.puts("VM_PROFC = "s + ((v3Global.opt.profC()) ? "1" : "0") + "\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("# SystemC output mode? 0/1 (from --sc)\n");
|
2024-07-14 17:39:45 +02:00
|
|
|
of.puts("VM_SC = "s + ((v3Global.opt.systemC()) ? "1" : "0") + "\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("# Legacy or SystemC output mode? 0/1 (from --sc)\n");
|
2024-07-14 17:39:45 +02:00
|
|
|
of.puts("VM_SP_OR_SC = $(VM_SC)\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("# Deprecated\n");
|
2024-07-14 17:39:45 +02:00
|
|
|
of.puts("VM_PCLI = "s + (v3Global.opt.systemC() ? "0" : "1") + "\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
of.puts(
|
|
|
|
|
"# Deprecated: SystemC architecture to find link library path (from $SYSTEMC_ARCH)\n");
|
2024-07-14 17:39:45 +02:00
|
|
|
of.puts("VM_SC_TARGET_ARCH = "s + V3Options::getenvSYSTEMC_ARCH() + "\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
|
|
|
|
|
of.puts("\n### Vars...\n");
|
|
|
|
|
of.puts("# Design prefix (from --prefix)\n");
|
2024-07-14 17:39:45 +02:00
|
|
|
of.puts("VM_PREFIX = "s + v3Global.opt.prefix() + "\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("# Module prefix (from --prefix)\n");
|
2024-07-14 17:39:45 +02:00
|
|
|
of.puts("VM_MODPREFIX = "s + v3Global.opt.modPrefix() + "\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
|
|
|
|
|
of.puts("# User CFLAGS (from -CFLAGS on Verilator command line)\n");
|
|
|
|
|
of.puts("VM_USER_CFLAGS = \\\n");
|
2024-05-17 16:38:34 +02:00
|
|
|
const std::string solver = V3Options::getenvVERILATOR_SOLVER();
|
|
|
|
|
if (v3Global.useRandomizeMethods() && solver != "")
|
|
|
|
|
of.puts("\t-DVM_SOLVER_DEFAULT='\"" + V3OutFormatter::quoteNameControls(solver)
|
|
|
|
|
+ "\"' \\\n");
|
2021-11-14 15:39:31 +01:00
|
|
|
if (!v3Global.opt.libCreate().empty()) of.puts("\t-fPIC \\\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
const V3StringList& cFlags = v3Global.opt.cFlags();
|
2020-08-16 18:54:32 +02:00
|
|
|
for (const string& i : cFlags) of.puts("\t" + i + " \\\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("\n");
|
|
|
|
|
|
|
|
|
|
of.puts("# User LDLIBS (from -LDFLAGS on Verilator command line)\n");
|
|
|
|
|
of.puts("VM_USER_LDLIBS = \\\n");
|
|
|
|
|
const V3StringList& ldLibs = v3Global.opt.ldLibs();
|
2020-08-16 18:54:32 +02:00
|
|
|
for (const string& i : ldLibs) of.puts("\t" + i + " \\\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("\n");
|
|
|
|
|
|
|
|
|
|
V3StringSet dirs;
|
|
|
|
|
of.puts("# User .cpp files (from .cpp's on Verilator command line)\n");
|
|
|
|
|
of.puts("VM_USER_CLASSES = \\\n");
|
|
|
|
|
const V3StringSet& cppFiles = v3Global.opt.cppFiles();
|
2020-08-16 17:43:49 +02:00
|
|
|
for (const auto& cppfile : cppFiles) {
|
2024-07-14 17:31:32 +02:00
|
|
|
of.puts("\t" + V3Os::filenameNonDirExt(cppfile) + " \\\n");
|
2024-09-22 22:47:10 +02:00
|
|
|
const string dir
|
|
|
|
|
= V3Os::filenameRelativePath(V3Os::filenameDir(cppfile), v3Global.opt.makeDir());
|
2020-02-04 05:21:56 +01:00
|
|
|
dirs.insert(dir);
|
2019-05-19 22:13:13 +02:00
|
|
|
}
|
2024-09-22 22:47:10 +02:00
|
|
|
dirs.insert(V3Os::filenameRelativePath(".", v3Global.opt.makeDir()));
|
|
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("\n");
|
|
|
|
|
|
|
|
|
|
of.puts("# User .cpp directories (from .cpp's on Verilator command line)\n");
|
|
|
|
|
of.puts("VM_USER_DIR = \\\n");
|
2020-08-16 17:43:49 +02:00
|
|
|
for (const auto& i : dirs) of.puts("\t" + i + " \\\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("\n");
|
|
|
|
|
|
|
|
|
|
of.puts("\n### Default rules...\n");
|
|
|
|
|
of.puts("# Include list of all generated classes\n");
|
2020-04-15 13:58:34 +02:00
|
|
|
of.puts("include " + v3Global.opt.prefix() + "_classes.mk\n");
|
2020-08-15 15:43:53 +02:00
|
|
|
if (v3Global.opt.hierTop()) {
|
|
|
|
|
of.puts("# Include rules for hierarchical blocks\n");
|
|
|
|
|
of.puts("include " + v3Global.opt.prefix() + "_hier.mk\n");
|
|
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("# Include global rules\n");
|
|
|
|
|
of.puts("include $(VERILATOR_ROOT)/include/verilated.mk\n");
|
|
|
|
|
|
|
|
|
|
if (v3Global.opt.exe()) {
|
|
|
|
|
of.puts("\n### Executable rules... (from --exe)\n");
|
|
|
|
|
of.puts("VPATH += $(VM_USER_DIR)\n");
|
|
|
|
|
of.puts("\n");
|
2022-11-29 15:02:39 +01:00
|
|
|
}
|
2019-05-19 22:13:13 +02:00
|
|
|
|
2024-07-24 12:40:39 +02:00
|
|
|
const string compilerIncludePch
|
|
|
|
|
= v3Global.opt.compilerIncludes().empty() ? "" : "$(VK_PCH_H).fast.gch";
|
|
|
|
|
const string compilerIncludeFlag
|
|
|
|
|
= v3Global.opt.compilerIncludes().empty() ? "" : "$(VK_PCH_I_FAST)";
|
2022-11-29 15:02:39 +01:00
|
|
|
for (const string& cppfile : cppFiles) {
|
2024-07-14 17:31:32 +02:00
|
|
|
const string basename = V3Os::filenameNonDirExt(cppfile);
|
2022-11-29 15:02:39 +01:00
|
|
|
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
|
2024-07-24 12:40:39 +02:00
|
|
|
of.puts(basename + ".o: " + cppfile + " " + compilerIncludePch + "\n");
|
|
|
|
|
|
|
|
|
|
// NOLINTNEXTLINE(performance-inefficient-string-concatenation)
|
|
|
|
|
of.puts("\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) "
|
|
|
|
|
+ compilerIncludeFlag + " -c -o $@ $<\n");
|
2022-11-29 15:02:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (v3Global.opt.exe()) {
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("\n### Link rules... (from --exe)\n");
|
2023-12-23 15:31:10 +01:00
|
|
|
// let default rule depend on '{prefix}__ALL.a', for compatibility
|
2020-04-15 13:58:34 +02:00
|
|
|
of.puts(v3Global.opt.exeName()
|
2020-08-15 15:43:53 +02:00
|
|
|
+ ": $(VK_USER_OBJS) $(VK_GLOBAL_OBJS) $(VM_PREFIX)__ALL.a $(VM_HIER_LIBS)\n");
|
2020-05-29 00:51:46 +02:00
|
|
|
of.puts("\t$(LINK) $(LDFLAGS) $^ $(LOADLIBES) $(LDLIBS) $(LIBS) $(SC_LIBS) -o $@\n");
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("\n");
|
2023-12-23 15:31:10 +01:00
|
|
|
} else if (!v3Global.opt.libCreate().empty()) {
|
2022-04-12 13:04:31 +02:00
|
|
|
const string libCreateDeps = "$(VK_OBJS) $(VK_USER_OBJS) $(VK_GLOBAL_OBJS) "
|
|
|
|
|
+ v3Global.opt.libCreate() + ".o $(VM_HIER_LIBS)";
|
2021-11-14 15:39:31 +01:00
|
|
|
of.puts("\n### Library rules from --lib-create\n");
|
2020-08-15 15:43:53 +02:00
|
|
|
// The rule to create .a is defined in verilated.mk, so just define dependency here.
|
2021-11-14 15:39:31 +01:00
|
|
|
of.puts(v3Global.opt.libCreateName(false) + ": " + libCreateDeps + "\n");
|
2019-10-09 12:47:26 +02:00
|
|
|
of.puts("\n");
|
2020-08-29 13:56:06 +02:00
|
|
|
if (v3Global.opt.hierChild()) {
|
|
|
|
|
// Hierarchical child does not need .so because hierTop() will create .so from .a
|
2021-11-14 15:39:31 +01:00
|
|
|
of.puts("lib" + v3Global.opt.libCreate() + ": " + v3Global.opt.libCreateName(false)
|
|
|
|
|
+ "\n");
|
2020-08-29 13:56:06 +02:00
|
|
|
} else {
|
2021-11-14 15:39:31 +01:00
|
|
|
of.puts(v3Global.opt.libCreateName(true) + ": " + libCreateDeps + "\n");
|
2020-08-29 13:56:06 +02:00
|
|
|
// Linker on mac emits an error if all symbols are not found here,
|
|
|
|
|
// but some symbols that are referred as "DPI-C" can not be found at this moment.
|
|
|
|
|
// So add dynamic_lookup
|
|
|
|
|
of.puts("ifeq ($(shell uname -s),Darwin)\n");
|
|
|
|
|
of.puts("\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -undefined "
|
2020-08-31 14:22:31 +02:00
|
|
|
"dynamic_lookup -shared -flat_namespace -o $@ $^\n");
|
2020-08-29 13:56:06 +02:00
|
|
|
of.puts("else\n");
|
|
|
|
|
of.puts(
|
|
|
|
|
"\t$(OBJCACHE) $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(OPT_FAST) -shared -o $@ $^\n");
|
|
|
|
|
of.puts("endif\n");
|
|
|
|
|
of.puts("\n");
|
2021-11-14 15:39:31 +01:00
|
|
|
of.puts("lib" + v3Global.opt.libCreate() + ": " + v3Global.opt.libCreateName(false)
|
|
|
|
|
+ " " + v3Global.opt.libCreateName(true) + "\n");
|
2020-08-29 13:56:06 +02:00
|
|
|
}
|
2023-12-23 15:31:10 +01:00
|
|
|
} else {
|
|
|
|
|
const string libname = "lib" + v3Global.opt.prefix() + ".a";
|
|
|
|
|
of.puts("\n### Library rules (default lib mode)\n");
|
|
|
|
|
// The rule to create .a is defined in verilated.mk, so just define dependency here.
|
|
|
|
|
of.puts(libname + ": $(VK_OBJS) $(VK_USER_OBJS) $(VM_HIER_LIBS)\n");
|
|
|
|
|
of.puts("libverilated.a: $(VK_GLOBAL_OBJS)\n");
|
|
|
|
|
// let default rule depend on '{prefix}__ALL.a', for compatibility
|
|
|
|
|
of.puts("lib" + v3Global.opt.prefix() + ": " + libname
|
|
|
|
|
+ " libverilated.a $(VM_PREFIX)__ALL.a\n");
|
2019-10-09 12:47:26 +02:00
|
|
|
}
|
|
|
|
|
|
2019-05-19 22:13:13 +02:00
|
|
|
of.puts("\n");
|
|
|
|
|
of.putsHeader();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
|
|
|
|
|
2019-10-18 01:44:10 +02:00
|
|
|
explicit EmitMk() {
|
2019-05-19 22:13:13 +02:00
|
|
|
emitClassMake();
|
|
|
|
|
emitOverallMake();
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-11-17 01:56:16 +01:00
|
|
|
virtual ~EmitMk() = default;
|
2006-08-26 13:35:28 +02:00
|
|
|
};
|
|
|
|
|
|
2020-08-15 15:43:53 +02:00
|
|
|
//######################################################################
|
2025-01-25 19:35:56 +01:00
|
|
|
|
2020-11-19 03:32:16 +01:00
|
|
|
class EmitMkHierVerilation final {
|
2020-08-15 15:43:53 +02:00
|
|
|
const V3HierBlockPlan* const m_planp;
|
|
|
|
|
const string m_makefile; // path of this makefile
|
|
|
|
|
void emitCommonOpts(V3OutMkFile& of) const {
|
|
|
|
|
const string cwd = V3Os::filenameRealPath(".");
|
|
|
|
|
of.puts("# Verilation of hierarchical blocks are executed in this directory\n");
|
|
|
|
|
of.puts("VM_HIER_RUN_DIR := " + cwd + "\n");
|
|
|
|
|
of.puts("# Common options for hierarchical blocks\n");
|
2022-09-18 16:32:43 +02:00
|
|
|
const string fullpath_bin = V3Os::filenameRealPath(v3Global.opt.buildDepBin());
|
2021-01-12 04:42:14 +01:00
|
|
|
const string verilator_wrapper = V3Os::filenameDir(fullpath_bin) + "/verilator";
|
|
|
|
|
of.puts("VM_HIER_VERILATOR := " + verilator_wrapper + "\n");
|
2020-08-15 15:43:53 +02:00
|
|
|
of.puts("VM_HIER_INPUT_FILES := \\\n");
|
|
|
|
|
const V3StringList& vFiles = v3Global.opt.vFiles();
|
2020-08-16 18:54:32 +02:00
|
|
|
for (const string& i : vFiles) of.puts("\t" + V3Os::filenameRealPath(i) + " \\\n");
|
2020-08-15 15:43:53 +02:00
|
|
|
of.puts("\n");
|
|
|
|
|
const V3StringSet& libraryFiles = v3Global.opt.libraryFiles();
|
|
|
|
|
of.puts("VM_HIER_VERILOG_LIBS := \\\n");
|
2020-08-16 18:54:32 +02:00
|
|
|
for (const string& i : libraryFiles) {
|
|
|
|
|
of.puts("\t" + V3Os::filenameRealPath(i) + " \\\n");
|
2020-08-15 15:43:53 +02:00
|
|
|
}
|
|
|
|
|
of.puts("\n");
|
|
|
|
|
}
|
|
|
|
|
void emitLaunchVerilator(V3OutMkFile& of, const string& argsFile) const {
|
|
|
|
|
of.puts("\t@$(MAKE) -C $(VM_HIER_RUN_DIR) -f " + m_makefile
|
|
|
|
|
+ " hier_launch_verilator \\\n");
|
|
|
|
|
of.puts("\t\tVM_HIER_LAUNCH_VERILATOR_ARGSFILE=\"" + argsFile + "\"\n");
|
|
|
|
|
}
|
|
|
|
|
void emit(V3OutMkFile& of) const {
|
|
|
|
|
of.puts("# Hierarchical Verilation -*- Makefile -*-\n");
|
|
|
|
|
of.puts("# DESCR"
|
2022-12-23 17:32:38 +01:00
|
|
|
"IPTION: Verilator output: Makefile for hierarchical Verilation\n");
|
2020-08-15 15:43:53 +02:00
|
|
|
of.puts("#\n");
|
|
|
|
|
of.puts("# The main makefile " + v3Global.opt.prefix() + ".mk calls this makefile\n");
|
|
|
|
|
of.puts("\n");
|
|
|
|
|
of.puts("ifndef VM_HIER_VERILATION_INCLUDED\n");
|
|
|
|
|
of.puts("VM_HIER_VERILATION_INCLUDED = 1\n\n");
|
|
|
|
|
|
|
|
|
|
of.puts(".SUFFIXES:\n");
|
|
|
|
|
of.puts(".PHONY: hier_build hier_verilation hier_launch_verilator\n");
|
|
|
|
|
|
|
|
|
|
of.puts("# Libraries of hierarchical blocks\n");
|
|
|
|
|
of.puts("VM_HIER_LIBS := \\\n");
|
2020-08-31 14:22:31 +02:00
|
|
|
const V3HierBlockPlan::HierVector blocks
|
|
|
|
|
= m_planp->hierBlocksSorted(); // leaf comes first
|
|
|
|
|
// List in order of leaf-last order so that linker can resolve dependency
|
2024-02-25 23:12:13 +01:00
|
|
|
for (const auto& block : vlstd::reverse_view(blocks)) {
|
2024-07-14 15:34:54 +02:00
|
|
|
of.puts("\t" + block->hierLibFilename(true) + " \\\n");
|
2020-08-15 15:43:53 +02:00
|
|
|
}
|
|
|
|
|
of.puts("\n");
|
|
|
|
|
|
|
|
|
|
// Build hierarchical libraries as soon as possible to get maximum parallelism
|
|
|
|
|
of.puts("hier_build: $(VM_HIER_LIBS) " + v3Global.opt.prefix() + ".mk\n");
|
|
|
|
|
of.puts("\t$(MAKE) -f " + v3Global.opt.prefix() + ".mk\n");
|
|
|
|
|
of.puts("hier_verilation: " + v3Global.opt.prefix() + ".mk\n");
|
|
|
|
|
emitCommonOpts(of);
|
|
|
|
|
|
|
|
|
|
// Instead of direct execute of "cd $(VM_HIER_RUN_DIR) && $(VM_HIER_VERILATOR)",
|
|
|
|
|
// call via make to get message of "Entering directory" and "Leaving directory".
|
|
|
|
|
// This will make some editors and IDEs happy when viewing a logfile.
|
|
|
|
|
of.puts("# VM_HIER_LAUNCH_VERILATOR_ARGSFILE must be passed as a command argument\n");
|
|
|
|
|
of.puts("hier_launch_verilator:\n");
|
|
|
|
|
of.puts("\t$(VM_HIER_VERILATOR) -f $(VM_HIER_LAUNCH_VERILATOR_ARGSFILE)\n");
|
|
|
|
|
|
|
|
|
|
// Top level module
|
|
|
|
|
{
|
2024-07-14 15:34:54 +02:00
|
|
|
const string argsFile = v3Global.hierPlanp()->topCommandArgsFilename(false);
|
2020-08-15 15:43:53 +02:00
|
|
|
of.puts("\n# Verilate the top module\n");
|
|
|
|
|
of.puts(v3Global.opt.prefix()
|
|
|
|
|
+ ".mk: $(VM_HIER_INPUT_FILES) $(VM_HIER_VERILOG_LIBS) ");
|
|
|
|
|
of.puts(V3Os::filenameNonDir(argsFile) + " ");
|
2024-07-14 15:34:54 +02:00
|
|
|
for (const auto& itr : *m_planp) of.puts(itr.second->hierWrapperFilename(true) + " ");
|
2020-08-15 15:43:53 +02:00
|
|
|
of.puts("\n");
|
|
|
|
|
emitLaunchVerilator(of, argsFile);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Rules to process hierarchical blocks
|
|
|
|
|
of.puts("\n# Verilate hierarchical blocks\n");
|
2022-07-19 12:23:27 +02:00
|
|
|
for (const V3HierBlock* const blockp : m_planp->hierBlocksSorted()) {
|
|
|
|
|
const string prefix = blockp->hierPrefix();
|
2024-07-14 15:34:54 +02:00
|
|
|
const string argsFilename = blockp->commandArgsFilename(false);
|
|
|
|
|
of.puts(blockp->hierGeneratedFilenames(true));
|
2020-08-15 15:43:53 +02:00
|
|
|
of.puts(": $(VM_HIER_INPUT_FILES) $(VM_HIER_VERILOG_LIBS) ");
|
2024-07-14 15:34:54 +02:00
|
|
|
of.puts(V3Os::filenameNonDir(argsFilename) + " ");
|
2022-07-19 12:23:27 +02:00
|
|
|
const V3HierBlock::HierBlockSet& children = blockp->children();
|
2020-08-15 15:43:53 +02:00
|
|
|
for (V3HierBlock::HierBlockSet::const_iterator child = children.begin();
|
|
|
|
|
child != children.end(); ++child) {
|
2024-07-14 15:34:54 +02:00
|
|
|
of.puts((*child)->hierWrapperFilename(true) + " ");
|
2020-08-15 15:43:53 +02:00
|
|
|
}
|
|
|
|
|
of.puts("\n");
|
2024-07-14 15:34:54 +02:00
|
|
|
emitLaunchVerilator(of, argsFilename);
|
2020-08-15 15:43:53 +02:00
|
|
|
|
|
|
|
|
// Rule to build lib*.a
|
2024-07-14 15:34:54 +02:00
|
|
|
of.puts(blockp->hierLibFilename(true));
|
2020-08-15 15:43:53 +02:00
|
|
|
of.puts(": ");
|
2024-07-14 15:34:54 +02:00
|
|
|
of.puts(blockp->hierMkFilename(true));
|
2020-08-15 15:43:53 +02:00
|
|
|
of.puts(" ");
|
|
|
|
|
for (V3HierBlock::HierBlockSet::const_iterator child = children.begin();
|
|
|
|
|
child != children.end(); ++child) {
|
2024-07-14 15:34:54 +02:00
|
|
|
of.puts((*child)->hierLibFilename(true));
|
2020-08-15 15:43:53 +02:00
|
|
|
of.puts(" ");
|
|
|
|
|
}
|
2024-07-14 15:34:54 +02:00
|
|
|
of.puts("\n\t$(MAKE) -f " + blockp->hierMkFilename(false) + " -C " + prefix);
|
2020-08-15 15:43:53 +02:00
|
|
|
of.puts(" VM_PREFIX=" + prefix);
|
|
|
|
|
of.puts("\n\n");
|
|
|
|
|
}
|
|
|
|
|
of.puts("endif # Guard\n");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
explicit EmitMkHierVerilation(const V3HierBlockPlan* planp)
|
2020-08-16 15:55:36 +02:00
|
|
|
: m_planp{planp}
|
|
|
|
|
, m_makefile{v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + "_hier.mk"} {
|
2022-11-20 19:11:01 +01:00
|
|
|
V3OutMkFile of{m_makefile};
|
2020-08-15 15:43:53 +02:00
|
|
|
emit(of);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2006-08-26 13:35:28 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Gate class functions
|
|
|
|
|
|
2019-10-18 01:44:10 +02:00
|
|
|
void V3EmitMk::emitmk() {
|
2020-04-15 13:58:34 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2021-11-26 23:55:36 +01:00
|
|
|
const EmitMk emitter;
|
2006-08-26 13:35:28 +02:00
|
|
|
}
|
2020-08-15 15:43:53 +02:00
|
|
|
|
|
|
|
|
void V3EmitMk::emitHierVerilation(const V3HierBlockPlan* planp) {
|
|
|
|
|
UINFO(2, __FUNCTION__ << ": " << endl);
|
2020-12-23 21:21:33 +01:00
|
|
|
EmitMkHierVerilation{planp};
|
2020-08-15 15:43:53 +02:00
|
|
|
}
|