mirror of https://github.com/YosysHQ/yosys.git
Merge branch 'YosysHQ:master' into master
This commit is contained in:
commit
bcf1c7b879
2
Makefile
2
Makefile
|
@ -141,7 +141,7 @@ LDLIBS += -lrt
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
YOSYS_VER := 0.36+42
|
YOSYS_VER := 0.36+67
|
||||||
|
|
||||||
# Note: We arrange for .gitcommit to contain the (short) commit hash in
|
# Note: We arrange for .gitcommit to contain the (short) commit hash in
|
||||||
# tarballs generated with git-archive(1) using .gitattributes. The git repo
|
# tarballs generated with git-archive(1) using .gitattributes. The git repo
|
||||||
|
|
|
@ -2115,19 +2115,19 @@ struct CxxrtlWorker {
|
||||||
if (wire_type.type == WireType::MEMBER && edge_wires[wire])
|
if (wire_type.type == WireType::MEMBER && edge_wires[wire])
|
||||||
f << indent << "prev_" << mangle(wire) << " = " << mangle(wire) << ";\n";
|
f << indent << "prev_" << mangle(wire) << " = " << mangle(wire) << ";\n";
|
||||||
if (wire_type.is_buffered())
|
if (wire_type.is_buffered())
|
||||||
f << indent << "if (" << mangle(wire) << ".commit()) changed = true;\n";
|
f << indent << "if (" << mangle(wire) << ".commit(observer)) changed = true;\n";
|
||||||
}
|
}
|
||||||
if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
|
if (!module->get_bool_attribute(ID(cxxrtl_blackbox))) {
|
||||||
for (auto &mem : mod_memories[module]) {
|
for (auto &mem : mod_memories[module]) {
|
||||||
if (!writable_memories.count({module, mem.memid}))
|
if (!writable_memories.count({module, mem.memid}))
|
||||||
continue;
|
continue;
|
||||||
f << indent << "if (" << mangle(&mem) << ".commit()) changed = true;\n";
|
f << indent << "if (" << mangle(&mem) << ".commit(observer)) changed = true;\n";
|
||||||
}
|
}
|
||||||
for (auto cell : module->cells()) {
|
for (auto cell : module->cells()) {
|
||||||
if (is_internal_cell(cell->type))
|
if (is_internal_cell(cell->type))
|
||||||
continue;
|
continue;
|
||||||
const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
|
const char *access = is_cxxrtl_blackbox_cell(cell) ? "->" : ".";
|
||||||
f << indent << "if (" << mangle(cell) << access << "commit()) changed = true;\n";
|
f << indent << "if (" << mangle(cell) << access << "commit(observer)) changed = true;\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
f << indent << "return changed;\n";
|
f << indent << "return changed;\n";
|
||||||
|
@ -2146,7 +2146,7 @@ struct CxxrtlWorker {
|
||||||
if (!metadata_item.first.isPublic())
|
if (!metadata_item.first.isPublic())
|
||||||
continue;
|
continue;
|
||||||
if (metadata_item.second.size() > 64 && (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) == 0) {
|
if (metadata_item.second.size() > 64 && (metadata_item.second.flags & RTLIL::CONST_FLAG_STRING) == 0) {
|
||||||
f << indent << "/* attribute " << metadata_item.first.str().substr(1) << " is over 64 bits wide */";
|
f << indent << "/* attribute " << metadata_item.first.str().substr(1) << " is over 64 bits wide */\n";
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
f << indent << "{ " << escape_cxx_string(metadata_item.first.str().substr(1)) << ", ";
|
f << indent << "{ " << escape_cxx_string(metadata_item.first.str().substr(1)) << ", ";
|
||||||
|
@ -2371,16 +2371,22 @@ struct CxxrtlWorker {
|
||||||
dump_eval_method(module);
|
dump_eval_method(module);
|
||||||
f << indent << "}\n";
|
f << indent << "}\n";
|
||||||
f << "\n";
|
f << "\n";
|
||||||
f << indent << "bool commit() override {\n";
|
f << indent << "template<class ObserverT>\n";
|
||||||
|
f << indent << "bool commit(ObserverT &observer) {\n";
|
||||||
dump_commit_method(module);
|
dump_commit_method(module);
|
||||||
f << indent << "}\n";
|
f << indent << "}\n";
|
||||||
f << "\n";
|
f << "\n";
|
||||||
|
f << indent << "bool commit() override {\n";
|
||||||
|
f << indent << indent << "null_observer observer;\n";
|
||||||
|
f << indent << indent << "return commit<>(observer);\n";
|
||||||
|
f << indent << "}\n";
|
||||||
if (debug_info) {
|
if (debug_info) {
|
||||||
|
f << "\n";
|
||||||
f << indent << "void debug_info(debug_items &items, std::string path = \"\") override {\n";
|
f << indent << "void debug_info(debug_items &items, std::string path = \"\") override {\n";
|
||||||
dump_debug_info_method(module);
|
dump_debug_info_method(module);
|
||||||
f << indent << "}\n";
|
f << indent << "}\n";
|
||||||
f << "\n";
|
|
||||||
}
|
}
|
||||||
|
f << "\n";
|
||||||
f << indent << "static std::unique_ptr<" << mangle(module);
|
f << indent << "static std::unique_ptr<" << mangle(module);
|
||||||
f << template_params(module, /*is_decl=*/false) << "> ";
|
f << template_params(module, /*is_decl=*/false) << "> ";
|
||||||
f << "create(std::string name, metadata_map parameters, metadata_map attributes);\n";
|
f << "create(std::string name, metadata_map parameters, metadata_map attributes);\n";
|
||||||
|
@ -2457,8 +2463,18 @@ struct CxxrtlWorker {
|
||||||
f << indent << "};\n";
|
f << indent << "};\n";
|
||||||
f << "\n";
|
f << "\n";
|
||||||
f << indent << "void reset() override;\n";
|
f << indent << "void reset() override;\n";
|
||||||
|
f << "\n";
|
||||||
f << indent << "bool eval() override;\n";
|
f << indent << "bool eval() override;\n";
|
||||||
f << indent << "bool commit() override;\n";
|
f << "\n";
|
||||||
|
f << indent << "template<class ObserverT>\n";
|
||||||
|
f << indent << "bool commit(ObserverT &observer) {\n";
|
||||||
|
dump_commit_method(module);
|
||||||
|
f << indent << "}\n";
|
||||||
|
f << "\n";
|
||||||
|
f << indent << "bool commit() override {\n";
|
||||||
|
f << indent << indent << "null_observer observer;\n";
|
||||||
|
f << indent << indent << "return commit<>(observer);\n";
|
||||||
|
f << indent << "}\n";
|
||||||
if (debug_info) {
|
if (debug_info) {
|
||||||
if (debug_eval) {
|
if (debug_eval) {
|
||||||
f << "\n";
|
f << "\n";
|
||||||
|
@ -2490,24 +2506,20 @@ struct CxxrtlWorker {
|
||||||
f << indent << "bool " << mangle(module) << "::eval() {\n";
|
f << indent << "bool " << mangle(module) << "::eval() {\n";
|
||||||
dump_eval_method(module);
|
dump_eval_method(module);
|
||||||
f << indent << "}\n";
|
f << indent << "}\n";
|
||||||
f << "\n";
|
|
||||||
f << indent << "bool " << mangle(module) << "::commit() {\n";
|
|
||||||
dump_commit_method(module);
|
|
||||||
f << indent << "}\n";
|
|
||||||
f << "\n";
|
|
||||||
if (debug_info) {
|
if (debug_info) {
|
||||||
if (debug_eval) {
|
if (debug_eval) {
|
||||||
|
f << "\n";
|
||||||
f << indent << "void " << mangle(module) << "::debug_eval() {\n";
|
f << indent << "void " << mangle(module) << "::debug_eval() {\n";
|
||||||
dump_debug_eval_method(module);
|
dump_debug_eval_method(module);
|
||||||
f << indent << "}\n";
|
f << indent << "}\n";
|
||||||
f << "\n";
|
|
||||||
}
|
}
|
||||||
|
f << "\n";
|
||||||
f << indent << "CXXRTL_EXTREMELY_COLD\n";
|
f << indent << "CXXRTL_EXTREMELY_COLD\n";
|
||||||
f << indent << "void " << mangle(module) << "::debug_info(debug_items &items, std::string path) {\n";
|
f << indent << "void " << mangle(module) << "::debug_info(debug_items &items, std::string path) {\n";
|
||||||
dump_debug_info_method(module);
|
dump_debug_info_method(module);
|
||||||
f << indent << "}\n";
|
f << indent << "}\n";
|
||||||
f << "\n";
|
|
||||||
}
|
}
|
||||||
|
f << "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
void dump_design(RTLIL::Design *design)
|
void dump_design(RTLIL::Design *design)
|
||||||
|
@ -3267,6 +3279,8 @@ struct CxxrtlBackend : public Backend {
|
||||||
log(" wire<8> p_o_data;\n");
|
log(" wire<8> p_o_data;\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" bool eval() override;\n");
|
log(" bool eval() override;\n");
|
||||||
|
log(" template<class ObserverT>\n");
|
||||||
|
log(" bool commit(ObserverT &observer);\n");
|
||||||
log(" bool commit() override;\n");
|
log(" bool commit() override;\n");
|
||||||
log("\n");
|
log("\n");
|
||||||
log(" static std::unique_ptr<bb_p_debug>\n");
|
log(" static std::unique_ptr<bb_p_debug>\n");
|
||||||
|
|
|
@ -841,6 +841,27 @@ std::ostream &operator<<(std::ostream &os, const value_formatted<Bits> &vf)
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An object that can be passed to a `commit()` method in order to produce a replay log of every state change in
|
||||||
|
// the simulation.
|
||||||
|
struct observer {
|
||||||
|
// Called when the `commit()` method for a wire is about to update the `chunks` chunks at `base` with `chunks` chunks
|
||||||
|
// at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to the wire chunk count and
|
||||||
|
// `base` points to the first chunk.
|
||||||
|
virtual void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value) = 0;
|
||||||
|
|
||||||
|
// Called when the `commit()` method for a memory is about to update the `chunks` chunks at `&base[chunks * index]`
|
||||||
|
// with `chunks` chunks at `value` that have a different bit pattern. It is guaranteed that `chunks` is equal to
|
||||||
|
// the memory element chunk count and `base` points to the first chunk of the first element of the memory.
|
||||||
|
virtual void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The `null_observer` class has the same interface as `observer`, but has no invocation overhead, since its methods
|
||||||
|
// are final and have no implementation. This allows the observer feature to be zero-cost when not in use.
|
||||||
|
struct null_observer final: observer {
|
||||||
|
void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value) override {}
|
||||||
|
void on_commit(size_t chunks, const chunk_t *base, const chunk_t *value, size_t index) override {}
|
||||||
|
};
|
||||||
|
|
||||||
template<size_t Bits>
|
template<size_t Bits>
|
||||||
struct wire {
|
struct wire {
|
||||||
static constexpr size_t bits = Bits;
|
static constexpr size_t bits = Bits;
|
||||||
|
@ -875,8 +896,14 @@ struct wire {
|
||||||
next.template set<IntegerT>(other);
|
next.template set<IntegerT>(other);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool commit() {
|
// This method intentionally takes a mandatory argument (to make it more difficult to misuse in
|
||||||
|
// black box implementations, leading to missed observer events). It is generic over its argument
|
||||||
|
// to make sure the `on_commit` call is devirtualized. This is somewhat awkward but lets us keep
|
||||||
|
// a single implementation for both this method and the one in `memory`.
|
||||||
|
template<class ObserverT>
|
||||||
|
bool commit(ObserverT &observer) {
|
||||||
if (curr != next) {
|
if (curr != next) {
|
||||||
|
observer.on_commit(curr.chunks, curr.data, next.data);
|
||||||
curr = next;
|
curr = next;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -950,12 +977,17 @@ struct memory {
|
||||||
write { index, val, mask, priority });
|
write { index, val, mask, priority });
|
||||||
}
|
}
|
||||||
|
|
||||||
bool commit() {
|
// See the note for `wire::commit()`.
|
||||||
|
template<class ObserverT>
|
||||||
|
bool commit(ObserverT &observer) {
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
for (const write &entry : write_queue) {
|
for (const write &entry : write_queue) {
|
||||||
value<Width> elem = data[entry.index];
|
value<Width> elem = data[entry.index];
|
||||||
elem = elem.update(entry.val, entry.mask);
|
elem = elem.update(entry.val, entry.mask);
|
||||||
changed |= (data[entry.index] != elem);
|
if (data[entry.index] != elem) {
|
||||||
|
observer.on_commit(value<Width>::chunks, data[0].data, elem.data, entry.index);
|
||||||
|
changed |= true;
|
||||||
|
}
|
||||||
data[entry.index] = elem;
|
data[entry.index] = elem;
|
||||||
}
|
}
|
||||||
write_queue.clear();
|
write_queue.clear();
|
||||||
|
|
|
@ -0,0 +1,229 @@
|
||||||
|
/*
|
||||||
|
* yosys -- Yosys Open SYnthesis Suite
|
||||||
|
*
|
||||||
|
* Copyright (C) 2023 Catherine <whitequark@whitequark.org>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||||
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||||
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||||
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||||
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||||
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||||
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CXXRTL_TIME_H
|
||||||
|
#define CXXRTL_TIME_H
|
||||||
|
|
||||||
|
#include <cinttypes>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <cxxrtl/cxxrtl.h>
|
||||||
|
|
||||||
|
namespace cxxrtl {
|
||||||
|
|
||||||
|
// A timestamp or a difference in time, stored as a 96-bit number of femtoseconds (10e-15 s). The dynamic range and
|
||||||
|
// resolution of this format can represent any VCD timestamp within 136 years, without the need for a timescale.
|
||||||
|
class time {
|
||||||
|
public:
|
||||||
|
static constexpr size_t bits = 96; // 3 chunks
|
||||||
|
|
||||||
|
private:
|
||||||
|
static constexpr value<bits> resolution = value<bits> {
|
||||||
|
chunk_t(1000000000000000ull & 0xffffffffull), chunk_t(1000000000000000ull >> 32), 0u
|
||||||
|
};
|
||||||
|
static constexpr size_t resolution_digits = 15;
|
||||||
|
|
||||||
|
// Signed number of femtoseconds from the beginning of time.
|
||||||
|
value<bits> raw;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr time() {}
|
||||||
|
|
||||||
|
explicit constexpr time(const value<bits> &raw) : raw(raw) {}
|
||||||
|
explicit operator const value<bits> &() const { return raw; }
|
||||||
|
|
||||||
|
static constexpr time maximum() {
|
||||||
|
return time(value<bits> { 0xffffffffu, 0xffffffffu, 0x7fffffffu });
|
||||||
|
}
|
||||||
|
|
||||||
|
time(int32_t secs, int64_t femtos) {
|
||||||
|
value<32> secs_val;
|
||||||
|
secs_val.set((uint32_t)secs);
|
||||||
|
value<64> femtos_val;
|
||||||
|
femtos_val.set((uint64_t)femtos);
|
||||||
|
raw = secs_val.sext<bits>().mul<bits>(resolution).add(femtos_val.sext<bits>());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_zero() const {
|
||||||
|
return raw.is_zero();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extracts the sign of the value.
|
||||||
|
bool is_negative() const {
|
||||||
|
return raw.is_neg();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extracts the absolute number of whole seconds. Negative if the value is negative.
|
||||||
|
int32_t secs() const {
|
||||||
|
return raw.sdivmod(resolution).first.trunc<32>().get<uint32_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extracts the absolute number of femtoseconds in the fractional second. Negative if the value is negative.
|
||||||
|
int64_t femtos() const {
|
||||||
|
return raw.sdivmod(resolution).second.trunc<64>().get<uint64_t>();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const time &other) const {
|
||||||
|
return raw == other.raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const time &other) const {
|
||||||
|
return raw != other.raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>(const time &other) const {
|
||||||
|
return other.raw.scmp(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator>=(const time &other) const {
|
||||||
|
return !raw.scmp(other.raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<(const time &other) const {
|
||||||
|
return raw.scmp(other.raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator<=(const time &other) const {
|
||||||
|
return !other.raw.scmp(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
time operator+(const time &other) const {
|
||||||
|
return time(raw.add(other.raw));
|
||||||
|
}
|
||||||
|
|
||||||
|
time &operator+=(const time &other) {
|
||||||
|
*this = *this + other;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
time operator-() const {
|
||||||
|
return time(raw.neg());
|
||||||
|
}
|
||||||
|
|
||||||
|
time operator-(const time &other) const {
|
||||||
|
return *this + (-other);
|
||||||
|
}
|
||||||
|
|
||||||
|
time &operator-=(const time &other) {
|
||||||
|
*this = *this - other;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
operator std::string() const {
|
||||||
|
char buf[38]; // len(f"-{2**64}.{10**15-1}") + 1 == 38
|
||||||
|
int32_t secs = this->secs();
|
||||||
|
int64_t femtos = this->femtos();
|
||||||
|
snprintf(buf, sizeof(buf), "%s%" PRIi32 ".%015" PRIi64,
|
||||||
|
is_negative() ? "-" : "", secs >= 0 ? secs : -secs, femtos >= 0 ? femtos : -femtos);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if __cplusplus >= 201603L
|
||||||
|
[[nodiscard("ignoring parse errors")]]
|
||||||
|
#endif
|
||||||
|
bool parse(const std::string &str) {
|
||||||
|
enum {
|
||||||
|
parse_sign_opt,
|
||||||
|
parse_integral,
|
||||||
|
parse_fractional,
|
||||||
|
} state = parse_sign_opt;
|
||||||
|
bool negative = false;
|
||||||
|
int32_t integral = 0;
|
||||||
|
int64_t fractional = 0;
|
||||||
|
size_t frac_digits = 0;
|
||||||
|
for (auto chr : str) {
|
||||||
|
switch (state) {
|
||||||
|
case parse_sign_opt:
|
||||||
|
state = parse_integral;
|
||||||
|
if (chr == '+' || chr == '-') {
|
||||||
|
negative = (chr == '-');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* fallthrough */
|
||||||
|
case parse_integral:
|
||||||
|
if (chr >= '0' && chr <= '9') {
|
||||||
|
integral *= 10;
|
||||||
|
integral += chr - '0';
|
||||||
|
} else if (chr == '.') {
|
||||||
|
state = parse_fractional;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case parse_fractional:
|
||||||
|
if (chr >= '0' && chr <= '9' && frac_digits < resolution_digits) {
|
||||||
|
fractional *= 10;
|
||||||
|
fractional += chr - '0';
|
||||||
|
frac_digits++;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (frac_digits == 0)
|
||||||
|
return false;
|
||||||
|
while (frac_digits++ < resolution_digits)
|
||||||
|
fractional *= 10;
|
||||||
|
*this = negative ? -time { integral, fractional} : time { integral, fractional };
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Out-of-line definition required until C++17.
|
||||||
|
constexpr value<time::bits> time::resolution;
|
||||||
|
|
||||||
|
std::ostream &operator<<(std::ostream &os, const time &val) {
|
||||||
|
os << (std::string)val;
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
// These literals are (confusingly) compatible with the ones from `std::chrono`: the `std::chrono` literals do not
|
||||||
|
// have an underscore (e.g. 1ms) and the `cxxrtl::time` literals do (e.g. 1_ms). This syntactic difference is
|
||||||
|
// a requirement of the C++ standard. Despite being compatible the literals should not be mixed in the same namespace.
|
||||||
|
namespace time_literals {
|
||||||
|
|
||||||
|
time operator""_s(unsigned long long seconds) {
|
||||||
|
return time { (int32_t)seconds, 0 };
|
||||||
|
}
|
||||||
|
|
||||||
|
time operator""_ms(unsigned long long milliseconds) {
|
||||||
|
return time { 0, (int64_t)milliseconds * 1000000000000 };
|
||||||
|
}
|
||||||
|
|
||||||
|
time operator""_us(unsigned long long microseconds) {
|
||||||
|
return time { 0, (int64_t)microseconds * 1000000000 };
|
||||||
|
}
|
||||||
|
|
||||||
|
time operator""_ns(unsigned long long nanoseconds) {
|
||||||
|
return time { 0, (int64_t)nanoseconds * 1000000 };
|
||||||
|
}
|
||||||
|
|
||||||
|
time operator""_ps(unsigned long long picoseconds) {
|
||||||
|
return time { 0, (int64_t)picoseconds * 1000 };
|
||||||
|
}
|
||||||
|
|
||||||
|
time operator""_fs(unsigned long long femtoseconds) {
|
||||||
|
return time { 0, (int64_t)femtoseconds };
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -226,17 +226,6 @@ void AstNode::annotateTypedEnums(AstNode *template_node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool name_has_dot(const std::string &name, std::string &struct_name)
|
|
||||||
{
|
|
||||||
// check if plausible struct member name \sss.mmm
|
|
||||||
std::string::size_type pos;
|
|
||||||
if (name.substr(0, 1) == "\\" && (pos = name.find('.', 0)) != std::string::npos) {
|
|
||||||
struct_name = name.substr(0, pos);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static AstNode *make_range(int left, int right, bool is_signed = false)
|
static AstNode *make_range(int left, int right, bool is_signed = false)
|
||||||
{
|
{
|
||||||
// generate a pre-validated range node for a fixed signal range.
|
// generate a pre-validated range node for a fixed signal range.
|
||||||
|
@ -2185,11 +2174,24 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
|
||||||
|
|
||||||
if (type == AST_IDENTIFIER && !basic_prep) {
|
if (type == AST_IDENTIFIER && !basic_prep) {
|
||||||
// check if a plausible struct member sss.mmmm
|
// check if a plausible struct member sss.mmmm
|
||||||
std::string sname;
|
if (!str.empty() && str[0] == '\\' && current_scope.count(str)) {
|
||||||
if (name_has_dot(str, sname)) {
|
auto item_node = current_scope[str];
|
||||||
if (current_scope.count(str) > 0) {
|
if (item_node->type == AST_STRUCT_ITEM || item_node->type == AST_STRUCT || item_node->type == AST_UNION) {
|
||||||
auto item_node = current_scope[str];
|
// Traverse any hierarchical path until the full name for the referenced struct/union is found.
|
||||||
if (item_node->type == AST_STRUCT_ITEM || item_node->type == AST_STRUCT || item_node->type == AST_UNION) {
|
std::string sname;
|
||||||
|
bool found_sname = false;
|
||||||
|
for (std::string::size_type pos = 0; (pos = str.find('.', pos)) != std::string::npos; pos++) {
|
||||||
|
sname = str.substr(0, pos);
|
||||||
|
if (current_scope.count(sname)) {
|
||||||
|
auto stype = current_scope[sname]->type;
|
||||||
|
if (stype == AST_WIRE || stype == AST_PARAMETER || stype == AST_LOCALPARAM) {
|
||||||
|
found_sname = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found_sname) {
|
||||||
// structure member, rewrite this node to reference the packed struct wire
|
// structure member, rewrite this node to reference the packed struct wire
|
||||||
auto range = make_struct_member_range(this, item_node);
|
auto range = make_struct_member_range(this, item_node);
|
||||||
newNode = new AstNode(AST_IDENTIFIER, range);
|
newNode = new AstNode(AST_IDENTIFIER, range);
|
||||||
|
@ -4681,6 +4683,8 @@ void AstNode::expand_genblock(const std::string &prefix)
|
||||||
switch (child->type) {
|
switch (child->type) {
|
||||||
case AST_WIRE:
|
case AST_WIRE:
|
||||||
case AST_MEMORY:
|
case AST_MEMORY:
|
||||||
|
case AST_STRUCT:
|
||||||
|
case AST_UNION:
|
||||||
case AST_PARAMETER:
|
case AST_PARAMETER:
|
||||||
case AST_LOCALPARAM:
|
case AST_LOCALPARAM:
|
||||||
case AST_FUNCTION:
|
case AST_FUNCTION:
|
||||||
|
|
|
@ -420,7 +420,7 @@ public:
|
||||||
operator const_iterator() const { return const_iterator(ptr, index); }
|
operator const_iterator() const { return const_iterator(ptr, index); }
|
||||||
};
|
};
|
||||||
|
|
||||||
dict()
|
constexpr dict()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -855,7 +855,7 @@ public:
|
||||||
operator const_iterator() const { return const_iterator(ptr, index); }
|
operator const_iterator() const { return const_iterator(ptr, index); }
|
||||||
};
|
};
|
||||||
|
|
||||||
pool()
|
constexpr pool()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1062,6 +1062,10 @@ public:
|
||||||
const K *operator->() const { return &container[index]; }
|
const K *operator->() const { return &container[index]; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
constexpr idict()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
int operator()(const K &key)
|
int operator()(const K &key)
|
||||||
{
|
{
|
||||||
int hash = database.do_hash(key);
|
int hash = database.do_hash(key);
|
||||||
|
@ -1132,6 +1136,10 @@ class mfp
|
||||||
public:
|
public:
|
||||||
typedef typename idict<K, 0, OPS>::const_iterator const_iterator;
|
typedef typename idict<K, 0, OPS>::const_iterator const_iterator;
|
||||||
|
|
||||||
|
constexpr mfp()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
int operator()(const K &key) const
|
int operator()(const K &key) const
|
||||||
{
|
{
|
||||||
int i = database(key);
|
int i = database(key);
|
||||||
|
|
|
@ -31,9 +31,6 @@ PRIVATE_NAMESPACE_BEGIN
|
||||||
|
|
||||||
struct QlBramMergeWorker {
|
struct QlBramMergeWorker {
|
||||||
|
|
||||||
const RTLIL::IdString split_cell_type = ID($__QLF_TDP36K);
|
|
||||||
const RTLIL::IdString merged_cell_type = ID($__QLF_TDP36K_MERGED);
|
|
||||||
|
|
||||||
// can be used to record parameter values that have to match on both sides
|
// can be used to record parameter values that have to match on both sides
|
||||||
typedef dict<RTLIL::IdString, RTLIL::Const> MergeableGroupKeyType;
|
typedef dict<RTLIL::IdString, RTLIL::Const> MergeableGroupKeyType;
|
||||||
|
|
||||||
|
@ -42,6 +39,8 @@ struct QlBramMergeWorker {
|
||||||
|
|
||||||
QlBramMergeWorker(RTLIL::Module* module) : module(module)
|
QlBramMergeWorker(RTLIL::Module* module) : module(module)
|
||||||
{
|
{
|
||||||
|
const RTLIL::IdString split_cell_type = ID($__QLF_TDP36K);
|
||||||
|
|
||||||
for (RTLIL::Cell* cell : module->selected_cells())
|
for (RTLIL::Cell* cell : module->selected_cells())
|
||||||
{
|
{
|
||||||
if(cell->type != split_cell_type) continue;
|
if(cell->type != split_cell_type) continue;
|
||||||
|
@ -125,6 +124,7 @@ struct QlBramMergeWorker {
|
||||||
|
|
||||||
void merge_brams(RTLIL::Cell* bram1, RTLIL::Cell* bram2)
|
void merge_brams(RTLIL::Cell* bram1, RTLIL::Cell* bram2)
|
||||||
{
|
{
|
||||||
|
const RTLIL::IdString merged_cell_type = ID($__QLF_TDP36K_MERGED);
|
||||||
|
|
||||||
// Create the new cell
|
// Create the new cell
|
||||||
RTLIL::Cell* merged = module->addCell(NEW_ID, merged_cell_type);
|
RTLIL::Cell* merged = module->addCell(NEW_ID, merged_cell_type);
|
||||||
|
|
|
@ -30,10 +30,6 @@ PRIVATE_NAMESPACE_BEGIN
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
struct QlDspIORegs : public Pass {
|
struct QlDspIORegs : public Pass {
|
||||||
const std::vector<IdString> ports2del_mult = {ID(load_acc), ID(subtract), ID(acc_fir), ID(dly_b),
|
|
||||||
ID(saturate_enable), ID(shift_right), ID(round)};
|
|
||||||
const std::vector<IdString> ports2del_mult_acc = {ID(acc_fir), ID(dly_b)};
|
|
||||||
|
|
||||||
SigMap sigmap;
|
SigMap sigmap;
|
||||||
|
|
||||||
// ..........................................
|
// ..........................................
|
||||||
|
@ -67,6 +63,11 @@ struct QlDspIORegs : public Pass {
|
||||||
|
|
||||||
void ql_dsp_io_regs_pass(RTLIL::Module *module)
|
void ql_dsp_io_regs_pass(RTLIL::Module *module)
|
||||||
{
|
{
|
||||||
|
static const std::vector<IdString> ports2del_mult = {ID(load_acc), ID(subtract), ID(acc_fir), ID(dly_b),
|
||||||
|
ID(saturate_enable), ID(shift_right), ID(round)};
|
||||||
|
static const std::vector<IdString> ports2del_mult_acc = {ID(acc_fir), ID(dly_b)};
|
||||||
|
|
||||||
|
|
||||||
sigmap.set(module);
|
sigmap.set(module);
|
||||||
|
|
||||||
for (auto cell : module->cells()) {
|
for (auto cell : module->cells()) {
|
||||||
|
|
|
@ -60,43 +60,11 @@ struct QlDspSimdPass : public Pass {
|
||||||
|
|
||||||
// ..........................................
|
// ..........................................
|
||||||
|
|
||||||
// DSP control and config ports to consider and how to map them to ports
|
|
||||||
// of the target DSP cell
|
|
||||||
const std::vector<std::pair<IdString, IdString>> m_DspCfgPorts = {
|
|
||||||
std::make_pair(ID(clock_i), ID(clk)),
|
|
||||||
std::make_pair(ID(reset_i), ID(reset)),
|
|
||||||
std::make_pair(ID(feedback_i), ID(feedback)),
|
|
||||||
std::make_pair(ID(load_acc_i), ID(load_acc)),
|
|
||||||
std::make_pair(ID(unsigned_a_i), ID(unsigned_a)),
|
|
||||||
std::make_pair(ID(unsigned_b_i), ID(unsigned_b)),
|
|
||||||
std::make_pair(ID(subtract_i), ID(subtract)),
|
|
||||||
std::make_pair(ID(output_select_i), ID(output_select)),
|
|
||||||
std::make_pair(ID(saturate_enable_i), ID(saturate_enable)),
|
|
||||||
std::make_pair(ID(shift_right_i), ID(shift_right)),
|
|
||||||
std::make_pair(ID(round_i), ID(round)),
|
|
||||||
std::make_pair(ID(register_inputs_i), ID(register_inputs))
|
|
||||||
};
|
|
||||||
|
|
||||||
const int m_ModeBitsSize = 80;
|
const int m_ModeBitsSize = 80;
|
||||||
|
|
||||||
// DSP data ports and how to map them to ports of the target DSP cell
|
|
||||||
const std::vector<std::pair<IdString, IdString>> m_DspDataPorts = {
|
|
||||||
std::make_pair(ID(a_i), ID(a)),
|
|
||||||
std::make_pair(ID(b_i), ID(b)),
|
|
||||||
std::make_pair(ID(acc_fir_i), ID(acc_fir)),
|
|
||||||
std::make_pair(ID(z_o), ID(z)),
|
|
||||||
std::make_pair(ID(dly_b_o), ID(dly_b))
|
|
||||||
};
|
|
||||||
|
|
||||||
// DSP parameters
|
// DSP parameters
|
||||||
const std::vector<std::string> m_DspParams = {"COEFF_3", "COEFF_2", "COEFF_1", "COEFF_0"};
|
const std::vector<std::string> m_DspParams = {"COEFF_3", "COEFF_2", "COEFF_1", "COEFF_0"};
|
||||||
|
|
||||||
// Source DSP cell type (SISD)
|
|
||||||
const IdString m_SisdDspType = ID(dsp_t1_10x9x32);
|
|
||||||
|
|
||||||
// Target DSP cell types for the SIMD mode
|
|
||||||
const IdString m_SimdDspType = ID(QL_DSP2);
|
|
||||||
|
|
||||||
/// Temporary SigBit to SigBit helper map.
|
/// Temporary SigBit to SigBit helper map.
|
||||||
SigMap sigmap;
|
SigMap sigmap;
|
||||||
|
|
||||||
|
@ -106,6 +74,38 @@ struct QlDspSimdPass : public Pass {
|
||||||
{
|
{
|
||||||
log_header(a_Design, "Executing QL_DSP_SIMD pass.\n");
|
log_header(a_Design, "Executing QL_DSP_SIMD pass.\n");
|
||||||
|
|
||||||
|
// DSP control and config ports to consider and how to map them to ports
|
||||||
|
// of the target DSP cell
|
||||||
|
static const std::vector<std::pair<IdString, IdString>> m_DspCfgPorts = {
|
||||||
|
std::make_pair(ID(clock_i), ID(clk)),
|
||||||
|
std::make_pair(ID(reset_i), ID(reset)),
|
||||||
|
std::make_pair(ID(feedback_i), ID(feedback)),
|
||||||
|
std::make_pair(ID(load_acc_i), ID(load_acc)),
|
||||||
|
std::make_pair(ID(unsigned_a_i), ID(unsigned_a)),
|
||||||
|
std::make_pair(ID(unsigned_b_i), ID(unsigned_b)),
|
||||||
|
std::make_pair(ID(subtract_i), ID(subtract)),
|
||||||
|
std::make_pair(ID(output_select_i), ID(output_select)),
|
||||||
|
std::make_pair(ID(saturate_enable_i), ID(saturate_enable)),
|
||||||
|
std::make_pair(ID(shift_right_i), ID(shift_right)),
|
||||||
|
std::make_pair(ID(round_i), ID(round)),
|
||||||
|
std::make_pair(ID(register_inputs_i), ID(register_inputs))
|
||||||
|
};
|
||||||
|
|
||||||
|
// DSP data ports and how to map them to ports of the target DSP cell
|
||||||
|
static const std::vector<std::pair<IdString, IdString>> m_DspDataPorts = {
|
||||||
|
std::make_pair(ID(a_i), ID(a)),
|
||||||
|
std::make_pair(ID(b_i), ID(b)),
|
||||||
|
std::make_pair(ID(acc_fir_i), ID(acc_fir)),
|
||||||
|
std::make_pair(ID(z_o), ID(z)),
|
||||||
|
std::make_pair(ID(dly_b_o), ID(dly_b))
|
||||||
|
};
|
||||||
|
|
||||||
|
// Source DSP cell type (SISD)
|
||||||
|
static const IdString m_SisdDspType = ID(dsp_t1_10x9x32);
|
||||||
|
|
||||||
|
// Target DSP cell types for the SIMD mode
|
||||||
|
static const IdString m_SimdDspType = ID(QL_DSP2);
|
||||||
|
|
||||||
// Parse args
|
// Parse args
|
||||||
extra_args(a_Args, 1, a_Design);
|
extra_args(a_Args, 1, a_Design);
|
||||||
|
|
||||||
|
@ -126,7 +126,7 @@ struct QlDspSimdPass : public Pass {
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Add to a group
|
// Add to a group
|
||||||
const auto key = getDspConfig(cell);
|
const auto key = getDspConfig(cell, m_DspCfgPorts);
|
||||||
groups[key].push_back(cell);
|
groups[key].push_back(cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,11 +255,11 @@ struct QlDspSimdPass : public Pass {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given a DSP cell populates and returns a DspConfig struct for it.
|
/// Given a DSP cell populates and returns a DspConfig struct for it.
|
||||||
DspConfig getDspConfig(RTLIL::Cell *a_Cell)
|
DspConfig getDspConfig(RTLIL::Cell *a_Cell, const std::vector<std::pair<IdString, IdString>> &dspCfgPorts)
|
||||||
{
|
{
|
||||||
DspConfig config;
|
DspConfig config;
|
||||||
|
|
||||||
for (const auto &it : m_DspCfgPorts) {
|
for (const auto &it : dspCfgPorts) {
|
||||||
auto port = it.first;
|
auto port = it.first;
|
||||||
|
|
||||||
// Port unconnected
|
// Port unconnected
|
||||||
|
|
|
@ -45,12 +45,12 @@ module top;
|
||||||
localparam W = 10;
|
localparam W = 10;
|
||||||
typedef T U;
|
typedef T U;
|
||||||
typedef logic [W-1:0] V;
|
typedef logic [W-1:0] V;
|
||||||
struct packed {
|
typedef struct packed {
|
||||||
logic [W-1:0] x; // width 10
|
logic [W-1:0] x; // width 10
|
||||||
U y; // width 5
|
U y; // width 5
|
||||||
V z; // width 10
|
V z; // width 10
|
||||||
} shadow;
|
} shadow_t;
|
||||||
// This currently only works as long as long as shadow is not typedef'ed
|
shadow_t shadow;
|
||||||
always @(*) assert($bits(shadow.x) == 10);
|
always @(*) assert($bits(shadow.x) == 10);
|
||||||
always @(*) assert($bits(shadow.y) == 5);
|
always @(*) assert($bits(shadow.y) == 5);
|
||||||
always @(*) assert($bits(shadow.z) == 10);
|
always @(*) assert($bits(shadow.z) == 10);
|
||||||
|
|
Loading…
Reference in New Issue