Merge branch 'YosysHQ:master' into master

This commit is contained in:
hakan-demirli 2024-01-07 14:08:35 +03:00 committed by GitHub
commit bcf1c7b879
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 369 additions and 81 deletions

View File

@ -141,7 +141,7 @@ LDLIBS += -lrt
endif
endif
YOSYS_VER := 0.36+42
YOSYS_VER := 0.36+67
# Note: We arrange for .gitcommit to contain the (short) commit hash in
# tarballs generated with git-archive(1) using .gitattributes. The git repo

View File

@ -2115,19 +2115,19 @@ struct CxxrtlWorker {
if (wire_type.type == WireType::MEMBER && edge_wires[wire])
f << indent << "prev_" << mangle(wire) << " = " << mangle(wire) << ";\n";
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))) {
for (auto &mem : mod_memories[module]) {
if (!writable_memories.count({module, mem.memid}))
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()) {
if (is_internal_cell(cell->type))
continue;
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";
@ -2146,7 +2146,7 @@ struct CxxrtlWorker {
if (!metadata_item.first.isPublic())
continue;
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;
}
f << indent << "{ " << escape_cxx_string(metadata_item.first.str().substr(1)) << ", ";
@ -2371,16 +2371,22 @@ struct CxxrtlWorker {
dump_eval_method(module);
f << indent << "}\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);
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) {
f << "\n";
f << indent << "void debug_info(debug_items &items, std::string path = \"\") override {\n";
dump_debug_info_method(module);
f << indent << "}\n";
f << "\n";
}
f << "\n";
f << indent << "static std::unique_ptr<" << mangle(module);
f << template_params(module, /*is_decl=*/false) << "> ";
f << "create(std::string name, metadata_map parameters, metadata_map attributes);\n";
@ -2457,8 +2463,18 @@ struct CxxrtlWorker {
f << indent << "};\n";
f << "\n";
f << indent << "void reset() override;\n";
f << "\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_eval) {
f << "\n";
@ -2490,24 +2506,20 @@ struct CxxrtlWorker {
f << indent << "bool " << mangle(module) << "::eval() {\n";
dump_eval_method(module);
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_eval) {
f << "\n";
f << indent << "void " << mangle(module) << "::debug_eval() {\n";
dump_debug_eval_method(module);
f << indent << "}\n";
f << "\n";
}
f << "\n";
f << indent << "CXXRTL_EXTREMELY_COLD\n";
f << indent << "void " << mangle(module) << "::debug_info(debug_items &items, std::string path) {\n";
dump_debug_info_method(module);
f << indent << "}\n";
f << "\n";
}
f << "\n";
}
void dump_design(RTLIL::Design *design)
@ -3267,6 +3279,8 @@ struct CxxrtlBackend : public Backend {
log(" wire<8> p_o_data;\n");
log("\n");
log(" bool eval() override;\n");
log(" template<class ObserverT>\n");
log(" bool commit(ObserverT &observer);\n");
log(" bool commit() override;\n");
log("\n");
log(" static std::unique_ptr<bb_p_debug>\n");

View File

@ -841,6 +841,27 @@ std::ostream &operator<<(std::ostream &os, const value_formatted<Bits> &vf)
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>
struct wire {
static constexpr size_t bits = Bits;
@ -875,8 +896,14 @@ struct wire {
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) {
observer.on_commit(curr.chunks, curr.data, next.data);
curr = next;
return true;
}
@ -950,12 +977,17 @@ struct memory {
write { index, val, mask, priority });
}
bool commit() {
// See the note for `wire::commit()`.
template<class ObserverT>
bool commit(ObserverT &observer) {
bool changed = false;
for (const write &entry : write_queue) {
value<Width> elem = data[entry.index];
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;
}
write_queue.clear();

View File

@ -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

View File

@ -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)
{
// 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) {
// check if a plausible struct member sss.mmmm
std::string sname;
if (name_has_dot(str, sname)) {
if (current_scope.count(str) > 0) {
auto item_node = current_scope[str];
if (item_node->type == AST_STRUCT_ITEM || item_node->type == AST_STRUCT || item_node->type == AST_UNION) {
if (!str.empty() && str[0] == '\\' && current_scope.count(str)) {
auto item_node = current_scope[str];
if (item_node->type == AST_STRUCT_ITEM || item_node->type == AST_STRUCT || item_node->type == AST_UNION) {
// Traverse any hierarchical path until the full name for the referenced struct/union is found.
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
auto range = make_struct_member_range(this, item_node);
newNode = new AstNode(AST_IDENTIFIER, range);
@ -4681,6 +4683,8 @@ void AstNode::expand_genblock(const std::string &prefix)
switch (child->type) {
case AST_WIRE:
case AST_MEMORY:
case AST_STRUCT:
case AST_UNION:
case AST_PARAMETER:
case AST_LOCALPARAM:
case AST_FUNCTION:

View File

@ -420,7 +420,7 @@ public:
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); }
};
pool()
constexpr pool()
{
}
@ -1062,6 +1062,10 @@ public:
const K *operator->() const { return &container[index]; }
};
constexpr idict()
{
}
int operator()(const K &key)
{
int hash = database.do_hash(key);
@ -1132,6 +1136,10 @@ class mfp
public:
typedef typename idict<K, 0, OPS>::const_iterator const_iterator;
constexpr mfp()
{
}
int operator()(const K &key) const
{
int i = database(key);

View File

@ -31,9 +31,6 @@ PRIVATE_NAMESPACE_BEGIN
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
typedef dict<RTLIL::IdString, RTLIL::Const> MergeableGroupKeyType;
@ -42,6 +39,8 @@ struct QlBramMergeWorker {
QlBramMergeWorker(RTLIL::Module* module) : module(module)
{
const RTLIL::IdString split_cell_type = ID($__QLF_TDP36K);
for (RTLIL::Cell* cell : module->selected_cells())
{
if(cell->type != split_cell_type) continue;
@ -125,6 +124,7 @@ struct QlBramMergeWorker {
void merge_brams(RTLIL::Cell* bram1, RTLIL::Cell* bram2)
{
const RTLIL::IdString merged_cell_type = ID($__QLF_TDP36K_MERGED);
// Create the new cell
RTLIL::Cell* merged = module->addCell(NEW_ID, merged_cell_type);

View File

@ -30,10 +30,6 @@ PRIVATE_NAMESPACE_BEGIN
// ============================================================================
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;
// ..........................................
@ -67,6 +63,11 @@ struct QlDspIORegs : public Pass {
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);
for (auto cell : module->cells()) {

View File

@ -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;
// 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
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.
SigMap sigmap;
@ -106,6 +74,38 @@ struct QlDspSimdPass : public Pass {
{
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
extra_args(a_Args, 1, a_Design);
@ -126,7 +126,7 @@ struct QlDspSimdPass : public Pass {
continue;
// Add to a group
const auto key = getDspConfig(cell);
const auto key = getDspConfig(cell, m_DspCfgPorts);
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.
DspConfig getDspConfig(RTLIL::Cell *a_Cell)
DspConfig getDspConfig(RTLIL::Cell *a_Cell, const std::vector<std::pair<IdString, IdString>> &dspCfgPorts)
{
DspConfig config;
for (const auto &it : m_DspCfgPorts) {
for (const auto &it : dspCfgPorts) {
auto port = it.first;
// Port unconnected

View File

@ -45,12 +45,12 @@ module top;
localparam W = 10;
typedef T U;
typedef logic [W-1:0] V;
struct packed {
typedef struct packed {
logic [W-1:0] x; // width 10
U y; // width 5
V z; // width 10
} shadow;
// This currently only works as long as long as shadow is not typedef'ed
} shadow_t;
shadow_t shadow;
always @(*) assert($bits(shadow.x) == 10);
always @(*) assert($bits(shadow.y) == 5);
always @(*) assert($bits(shadow.z) == 10);