3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-06-22 22:03:40 +00:00

Merge branch 'YosysHQ:main' into master

This commit is contained in:
Akash Levy 2024-04-08 12:26:40 -07:00 committed by GitHub
commit e3f633fae6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 1110 additions and 551 deletions

View file

@ -141,7 +141,7 @@ LIBS += -lrt
endif endif
endif endif
YOSYS_VER := 0.39+147 YOSYS_VER := 0.39+165
# 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
@ -628,6 +628,7 @@ $(eval $(call add_include_file,kernel/sigtools.h))
$(eval $(call add_include_file,kernel/timinginfo.h)) $(eval $(call add_include_file,kernel/timinginfo.h))
$(eval $(call add_include_file,kernel/utils.h)) $(eval $(call add_include_file,kernel/utils.h))
$(eval $(call add_include_file,kernel/yosys.h)) $(eval $(call add_include_file,kernel/yosys.h))
$(eval $(call add_include_file,kernel/yosys_common.h))
$(eval $(call add_include_file,kernel/yw.h)) $(eval $(call add_include_file,kernel/yw.h))
$(eval $(call add_include_file,libs/ezsat/ezsat.h)) $(eval $(call add_include_file,libs/ezsat/ezsat.h))
$(eval $(call add_include_file,libs/ezsat/ezminisat.h)) $(eval $(call add_include_file,libs/ezsat/ezminisat.h))

View file

@ -625,11 +625,11 @@ struct value : public expr_base<value<Bits>> {
value<Bits + 1> remainder; value<Bits + 1> remainder;
value<Bits + 1> dividend = sext<Bits + 1>(); value<Bits + 1> dividend = sext<Bits + 1>();
value<Bits + 1> divisor = other.template sext<Bits + 1>(); value<Bits + 1> divisor = other.template sext<Bits + 1>();
if (dividend.is_neg()) dividend = dividend.neg(); if (is_neg()) dividend = dividend.neg();
if (divisor.is_neg()) divisor = divisor.neg(); if (other.is_neg()) divisor = divisor.neg();
std::tie(quotient, remainder) = dividend.udivmod(divisor); std::tie(quotient, remainder) = dividend.udivmod(divisor);
if (dividend.is_neg() != divisor.is_neg()) quotient = quotient.neg(); if (is_neg() != other.is_neg()) quotient = quotient.neg();
if (dividend.is_neg()) remainder = remainder.neg(); if (is_neg()) remainder = remainder.neg();
return {quotient.template trunc<Bits>(), remainder.template trunc<Bits>()}; return {quotient.template trunc<Bits>(), remainder.template trunc<Bits>()};
} }
}; };
@ -1010,22 +1010,24 @@ struct observer {
// Default member initializers would make this a non-aggregate-type in C++11, so they are commented out. // Default member initializers would make this a non-aggregate-type in C++11, so they are commented out.
struct fmt_part { struct fmt_part {
enum { enum {
STRING = 0, LITERAL = 0,
INTEGER = 1, INTEGER = 1,
CHARACTER = 2, STRING = 2,
VLOG_TIME = 3, UNICHAR = 3,
VLOG_TIME = 4,
} type; } type;
// STRING type // LITERAL type
std::string str; std::string str;
// INTEGER/CHARACTER types // INTEGER/STRING/UNICHAR types
// + value<Bits> val; // + value<Bits> val;
// INTEGER/CHARACTER/VLOG_TIME types // INTEGER/STRING/VLOG_TIME types
enum { enum {
RIGHT = 0, RIGHT = 0,
LEFT = 1, LEFT = 1,
NUMERIC = 2,
} justify; // = RIGHT; } justify; // = RIGHT;
char padding; // = '\0'; char padding; // = '\0';
size_t width; // = 0; size_t width; // = 0;
@ -1033,7 +1035,14 @@ struct fmt_part {
// INTEGER type // INTEGER type
unsigned base; // = 10; unsigned base; // = 10;
bool signed_; // = false; bool signed_; // = false;
bool plus; // = false; enum {
MINUS = 0,
PLUS_MINUS = 1,
SPACE_MINUS = 2,
} sign; // = MINUS;
bool hex_upper; // = false;
bool show_base; // = false;
bool group; // = false;
// VLOG_TIME type // VLOG_TIME type
bool realtime; // = false; bool realtime; // = false;
@ -1049,11 +1058,12 @@ struct fmt_part {
// We might want to replace some of these bit() calls with direct // We might want to replace some of these bit() calls with direct
// chunk access if it turns out to be slow enough to matter. // chunk access if it turns out to be slow enough to matter.
std::string buf; std::string buf;
std::string prefix;
switch (type) { switch (type) {
case STRING: case LITERAL:
return str; return str;
case CHARACTER: { case STRING: {
buf.reserve(Bits/8); buf.reserve(Bits/8);
for (int i = 0; i < Bits; i += 8) { for (int i = 0; i < Bits; i += 8) {
char ch = 0; char ch = 0;
@ -1067,35 +1077,76 @@ struct fmt_part {
break; break;
} }
case UNICHAR: {
uint32_t codepoint = val.template get<uint32_t>();
if (codepoint >= 0x10000)
buf += (char)(0xf0 | (codepoint >> 18));
else if (codepoint >= 0x800)
buf += (char)(0xe0 | (codepoint >> 12));
else if (codepoint >= 0x80)
buf += (char)(0xc0 | (codepoint >> 6));
else
buf += (char)codepoint;
if (codepoint >= 0x10000)
buf += (char)(0x80 | ((codepoint >> 12) & 0x3f));
if (codepoint >= 0x800)
buf += (char)(0x80 | ((codepoint >> 6) & 0x3f));
if (codepoint >= 0x80)
buf += (char)(0x80 | ((codepoint >> 0) & 0x3f));
break;
}
case INTEGER: { case INTEGER: {
size_t width = Bits; bool negative = signed_ && val.is_neg();
if (negative) {
prefix = "-";
val = val.neg();
} else {
switch (sign) {
case MINUS: break;
case PLUS_MINUS: prefix = "+"; break;
case SPACE_MINUS: prefix = " "; break;
}
}
size_t val_width = Bits;
if (base != 10) { if (base != 10) {
width = 0; val_width = 1;
for (size_t index = 0; index < Bits; index++) for (size_t index = 0; index < Bits; index++)
if (val.bit(index)) if (val.bit(index))
width = index + 1; val_width = index + 1;
} }
if (base == 2) { if (base == 2) {
for (size_t i = width; i > 0; i--) if (show_base)
buf += (val.bit(i - 1) ? '1' : '0'); prefix += "0b";
for (size_t index = 0; index < val_width; index++) {
if (group && index > 0 && index % 4 == 0)
buf += '_';
buf += (val.bit(index) ? '1' : '0');
}
} else if (base == 8 || base == 16) { } else if (base == 8 || base == 16) {
if (show_base)
prefix += (base == 16) ? (hex_upper ? "0X" : "0x") : "0o";
size_t step = (base == 16) ? 4 : 3; size_t step = (base == 16) ? 4 : 3;
for (size_t index = 0; index < width; index += step) { for (size_t index = 0; index < val_width; index += step) {
if (group && index > 0 && index % (4 * step) == 0)
buf += '_';
uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2); uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2);
if (step == 4) if (step == 4)
value |= val.bit(index + 3) << 3; value |= val.bit(index + 3) << 3;
buf += "0123456789abcdef"[value]; buf += (hex_upper ? "0123456789ABCDEF" : "0123456789abcdef")[value];
} }
std::reverse(buf.begin(), buf.end());
} else if (base == 10) { } else if (base == 10) {
bool negative = signed_ && val.is_neg(); if (show_base)
if (negative) prefix += "0d";
val = val.neg();
if (val.is_zero()) if (val.is_zero())
buf += '0'; buf += '0';
value<(Bits > 4 ? Bits : 4)> xval = val.template zext<(Bits > 4 ? Bits : 4)>(); value<(Bits > 4 ? Bits : 4)> xval = val.template zext<(Bits > 4 ? Bits : 4)>();
size_t index = 0;
while (!xval.is_zero()) { while (!xval.is_zero()) {
if (group && index > 0 && index % 3 == 0)
buf += '_';
value<(Bits > 4 ? Bits : 4)> quotient, remainder; value<(Bits > 4 ? Bits : 4)> quotient, remainder;
if (Bits >= 4) if (Bits >= 4)
std::tie(quotient, remainder) = xval.udivmod(value<(Bits > 4 ? Bits : 4)>{10u}); std::tie(quotient, remainder) = xval.udivmod(value<(Bits > 4 ? Bits : 4)>{10u});
@ -1103,11 +1154,18 @@ struct fmt_part {
std::tie(quotient, remainder) = std::make_pair(value<(Bits > 4 ? Bits : 4)>{0u}, xval); std::tie(quotient, remainder) = std::make_pair(value<(Bits > 4 ? Bits : 4)>{0u}, xval);
buf += '0' + remainder.template trunc<4>().template get<uint8_t>(); buf += '0' + remainder.template trunc<4>().template get<uint8_t>();
xval = quotient; xval = quotient;
index++;
} }
if (negative || plus)
buf += negative ? '-' : '+';
std::reverse(buf.begin(), buf.end());
} else assert(false && "Unsupported base for fmt_part"); } else assert(false && "Unsupported base for fmt_part");
if (justify == NUMERIC && group && padding == '0') {
int group_size = base == 10 ? 3 : 4;
while (prefix.size() + buf.size() < width) {
if (buf.size() % (group_size + 1) == group_size)
buf += '_';
buf += '0';
}
}
std::reverse(buf.begin(), buf.end());
break; break;
} }
@ -1123,17 +1181,29 @@ struct fmt_part {
std::string str; std::string str;
assert(width == 0 || padding != '\0'); assert(width == 0 || padding != '\0');
if (justify == RIGHT && buf.size() < width) { if (prefix.size() + buf.size() < width) {
size_t pad_width = width - buf.size(); size_t pad_width = width - prefix.size() - buf.size();
if (padding == '0' && (buf.front() == '+' || buf.front() == '-')) { switch (justify) {
str += buf.front(); case LEFT:
buf.erase(0, 1); str += prefix;
} str += buf;
str += std::string(pad_width, padding); str += std::string(pad_width, padding);
break;
case RIGHT:
str += std::string(pad_width, padding);
str += prefix;
str += buf;
break;
case NUMERIC:
str += prefix;
str += std::string(pad_width, padding);
str += buf;
break;
}
} else {
str += prefix;
str += buf;
} }
str += buf;
if (justify == LEFT && buf.size() < width)
str += std::string(width - buf.size(), padding);
return str; return str;
} }
}; };

View file

@ -1070,7 +1070,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
f << stringf(";\n"); f << stringf(";\n");
return true; return true;
} }
if (cell->type == ID($_BUF_)) { if (cell->type == ID($_BUF_)) {
f << stringf("%s" "assign ", indent.c_str()); f << stringf("%s" "assign ", indent.c_str());
dump_sigspec(f, cell->getPort(ID::Y)); dump_sigspec(f, cell->getPort(ID::Y));
@ -2014,22 +2014,29 @@ void dump_sync_effect(std::ostream &f, std::string indent, const RTLIL::SigSpec
void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right) void dump_conn(std::ostream &f, std::string indent, const RTLIL::SigSpec &left, const RTLIL::SigSpec &right)
{ {
if (simple_lhs) { bool all_chunks_wires = true;
for (auto &chunk : left.chunks())
if (chunk.is_wire() && reg_wires.count(chunk.wire->name))
all_chunks_wires = false;
if (!simple_lhs && all_chunks_wires) {
f << stringf("%s" "assign ", indent.c_str());
dump_sigspec(f, left);
f << stringf(" = ");
dump_sigspec(f, right);
f << stringf(";\n");
} else {
int offset = 0; int offset = 0;
for (auto &chunk : left.chunks()) { for (auto &chunk : left.chunks()) {
f << stringf("%s" "assign ", indent.c_str()); if (chunk.is_wire() && reg_wires.count(chunk.wire->name))
f << stringf("%s" "always%s\n%s ", indent.c_str(), systemverilog ? "_comb" : " @*", indent.c_str());
else
f << stringf("%s" "assign ", indent.c_str());
dump_sigspec(f, chunk); dump_sigspec(f, chunk);
f << stringf(" = "); f << stringf(" = ");
dump_sigspec(f, right.extract(offset, GetSize(chunk))); dump_sigspec(f, right.extract(offset, GetSize(chunk)));
f << stringf(";\n"); f << stringf(";\n");
offset += GetSize(chunk); offset += GetSize(chunk);
} }
} else {
f << stringf("%s" "assign ", indent.c_str());
dump_sigspec(f, left);
f << stringf(" = ");
dump_sigspec(f, right);
f << stringf(";\n");
} }
} }
@ -2276,11 +2283,15 @@ void dump_module(std::ostream &f, std::string indent, RTLIL::Module *module)
active_initdata[sig[i]] = val[i]; active_initdata[sig[i]] = val[i];
} }
if (!module->processes.empty()) bool has_sync_rules = false;
log_warning("Module %s contains unmapped RTLIL processes. RTLIL processes\n" for (auto process : module->processes)
"can't always be mapped directly to Verilog always blocks. Unintended\n" if (!process.second->syncs.empty())
"changes in simulation behavior are possible! Use \"proc\" to convert\n" has_sync_rules = true;
"processes to logic networks and registers.\n", log_id(module)); if (has_sync_rules)
log_warning("Module %s contains RTLIL processes with sync rules. Such RTLIL "
"processes can't always be mapped directly to Verilog always blocks. "
"unintended changes in simulation behavior are possible! Use \"proc\" "
"to convert processes to logic networks and registers.\n", log_id(module));
f << stringf("\n"); f << stringf("\n");
for (auto it = module->processes.begin(); it != module->processes.end(); ++it) for (auto it = module->processes.begin(); it != module->processes.end(); ++it)

View file

@ -619,6 +619,48 @@ Finite state machines
Add a brief description of the ``$fsm`` cell type. Add a brief description of the ``$fsm`` cell type.
Coarse arithmetics
~~~~~~~~~~~~~~~~~~~~~
The ``$macc`` cell type represents a generalized multiply and accumulate operation. The cell is purely combinational. It outputs the result of summing up a sequence of products and other injected summands.
.. code-block::
Y = 0 +- a0factor1 * a0factor2 +- a1factor1 * a1factor2 +- ...
+ B[0] + B[1] + ...
The A port consists of concatenated pairs of multiplier inputs ("factors").
A zero length factor2 acts as a constant 1, turning factor1 into a simple summand.
In this pseudocode, ``u(foo)`` means an unsigned int that's foo bits long.
.. code-block::
struct A {
u(CONFIG.mul_info[0].factor1_len) a0factor1;
u(CONFIG.mul_info[0].factor2_len) a0factor2;
u(CONFIG.mul_info[1].factor1_len) a1factor1;
u(CONFIG.mul_info[1].factor2_len) a1factor2;
...
};
The cell's ``CONFIG`` parameter determines the layout of cell port ``A``.
The CONFIG parameter carries the following information:
.. code-block::
struct CONFIG {
u4 num_bits;
struct mul_info {
bool is_signed;
bool is_subtract;
u(num_bits) factor1_len;
u(num_bits) factor2_len;
}[num_ports];
};
B is an array of concatenated 1-bit-wide unsigned integers to also be summed up.
Specify rules Specify rules
~~~~~~~~~~~~~ ~~~~~~~~~~~~~
@ -1152,4 +1194,4 @@ file via ABC using the abc pass.
.. todo:: Add information about ``$lut`` and ``$sop`` cells. .. todo:: Add information about ``$lut`` and ``$sop`` cells.
.. todo:: Add information about ``$alu``, ``$macc``, ``$fa``, and ``$lcu`` cells. .. todo:: Add information about ``$alu``, ``$fa``, and ``$lcu`` cells.

View file

@ -790,7 +790,7 @@ struct AST_INTERNAL::ProcessGenerator
Fmt fmt; Fmt fmt;
fmt.parse_verilog(args, /*sformat_like=*/false, default_base, /*task_name=*/ast->str, current_module->name); fmt.parse_verilog(args, /*sformat_like=*/false, default_base, /*task_name=*/ast->str, current_module->name);
if (ast->str.substr(0, 8) == "$display") if (ast->str.substr(0, 8) == "$display")
fmt.append_string("\n"); fmt.append_literal("\n");
fmt.emit_rtlil(cell); fmt.emit_rtlil(cell);
} else if (!ast->str.empty()) { } else if (!ast->str.empty()) {
log_file_error(ast->filename, ast->location.first_line, "Found unsupported invocation of system task `%s'!\n", ast->str.c_str()); log_file_error(ast->filename, ast->location.first_line, "Found unsupported invocation of system task `%s'!\n", ast->str.c_str());

View file

@ -1079,7 +1079,7 @@ bool AstNode::simplify(bool const_fold, int stage, int width_hint, bool sign_hin
// when $display()/$write() functions are used in an initial block, print them during synthesis // when $display()/$write() functions are used in an initial block, print them during synthesis
Fmt fmt = processFormat(stage, /*sformat_like=*/false, default_base, /*first_arg_at=*/0, /*may_fail=*/true); Fmt fmt = processFormat(stage, /*sformat_like=*/false, default_base, /*first_arg_at=*/0, /*may_fail=*/true);
if (str.substr(0, 8) == "$display") if (str.substr(0, 8) == "$display")
fmt.append_string("\n"); fmt.append_literal("\n");
log("%s", fmt.render().c_str()); log("%s", fmt.render().c_str());
} }

View file

@ -19,7 +19,7 @@ Formatting of code
blank lines. blank lines.
- Otherwise stick to the Linux Kernel Coding Style: - Otherwise stick to the Linux Kernel Coding Style:
https://www.kernel.org/doc/Documentation/CodingStyle https://www.kernel.org/doc/Documentation/process/coding-style.rst
C++ Language C++ Language

View file

@ -22,9 +22,9 @@
USING_YOSYS_NAMESPACE USING_YOSYS_NAMESPACE
void Fmt::append_string(const std::string &str) { void Fmt::append_literal(const std::string &str) {
FmtPart part = {}; FmtPart part = {};
part.type = FmtPart::STRING; part.type = FmtPart::LITERAL;
part.str = str; part.str = str;
parts.push_back(part); parts.push_back(part);
} }
@ -42,11 +42,11 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) {
} else if (fmt.substr(i, 2) == "{{") { } else if (fmt.substr(i, 2) == "{{") {
part.str += '{'; part.str += '{';
++i; ++i;
} else if (fmt[i] == '}') } else if (fmt[i] == '}') {
log_assert(false && "Unexpected '}' in format string"); log_assert(false && "Unexpected '}' in format string");
else if (fmt[i] == '{') { } else if (fmt[i] == '{') {
if (!part.str.empty()) { if (!part.str.empty()) {
part.type = FmtPart::STRING; part.type = FmtPart::LITERAL;
parts.push_back(part); parts.push_back(part);
part = {}; part = {};
} }
@ -74,19 +74,24 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) {
part.sig = args.extract(0, arg_size); part.sig = args.extract(0, arg_size);
args.remove(0, arg_size); args.remove(0, arg_size);
if (fmt[i] == 'U') {
part.type = FmtPart::UNICHAR;
++i;
goto success;
}
if (fmt[i] == '>') if (fmt[i] == '>')
part.justify = FmtPart::RIGHT; part.justify = FmtPart::RIGHT;
else if (fmt[i] == '<') else if (fmt[i] == '<')
part.justify = FmtPart::LEFT; part.justify = FmtPart::LEFT;
else if (fmt[i] == '=')
part.justify = FmtPart::NUMERIC;
else else
log_assert(false && "Unexpected justification in format substitution"); log_assert(false && "Unexpected justification in format substitution");
if (++i == fmt.size()) if (++i == fmt.size())
log_assert(false && "Unexpected end in format substitution"); log_assert(false && "Unexpected end in format substitution");
if (fmt[i] == '0' || fmt[i] == ' ') part.padding = fmt[i];
part.padding = fmt[i];
else
log_assert(false && "Unexpected padding in format substitution");
if (++i == fmt.size()) if (++i == fmt.size())
log_assert(false && "Unexpected end in format substitution"); log_assert(false && "Unexpected end in format substitution");
@ -107,8 +112,12 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) {
} else if (fmt[i] == 'h') { } else if (fmt[i] == 'h') {
part.type = FmtPart::INTEGER; part.type = FmtPart::INTEGER;
part.base = 16; part.base = 16;
} else if (fmt[i] == 'H') {
part.type = FmtPart::INTEGER;
part.base = 16;
part.hex_upper = true;
} else if (fmt[i] == 'c') { } else if (fmt[i] == 'c') {
part.type = FmtPart::CHARACTER; part.type = FmtPart::STRING;
} else if (fmt[i] == 't') { } else if (fmt[i] == 't') {
part.type = FmtPart::VLOG_TIME; part.type = FmtPart::VLOG_TIME;
} else if (fmt[i] == 'r') { } else if (fmt[i] == 'r') {
@ -124,10 +133,29 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) {
log_assert(false && "Unexpected end in format substitution"); log_assert(false && "Unexpected end in format substitution");
if (part.type == FmtPart::INTEGER) { if (part.type == FmtPart::INTEGER) {
if (fmt[i] == '+') { if (fmt[i] == '-') {
part.plus = true; part.sign = FmtPart::MINUS;
if (++i == fmt.size()) if (++i == fmt.size())
log_assert(false && "Unexpected end in format substitution"); log_assert(false && "Unexpected end in format substitution");
} else if (fmt[i] == '+') {
part.sign = FmtPart::PLUS_MINUS;
if (++i == fmt.size())
log_assert(false && "Unexpected end in format substitution");
} else if (fmt[i] == ' ') {
part.sign = FmtPart::SPACE_MINUS;
if (++i == fmt.size())
log_assert(false && "Unexpected end in format substitution");
} else {
// also accept no sign character and treat like MINUS for compatibility
}
if (fmt[i] == '#') {
part.show_base = true;
++i;
}
if (fmt[i] == '_') {
part.group = true;
++i;
} }
if (fmt[i] == 'u') if (fmt[i] == 'u')
@ -140,6 +168,7 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) {
log_assert(false && "Unexpected end in format substitution"); log_assert(false && "Unexpected end in format substitution");
} }
success:
if (fmt[i] != '}') if (fmt[i] != '}')
log_assert(false && "Expected '}' after format substitution"); log_assert(false && "Expected '}' after format substitution");
@ -150,7 +179,7 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) {
} }
} }
if (!part.str.empty()) { if (!part.str.empty()) {
part.type = FmtPart::STRING; part.type = FmtPart::LITERAL;
parts.push_back(part); parts.push_back(part);
} }
} }
@ -161,7 +190,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const {
for (auto &part : parts) { for (auto &part : parts) {
switch (part.type) { switch (part.type) {
case FmtPart::STRING: case FmtPart::LITERAL:
for (char c : part.str) { for (char c : part.str) {
if (c == '{') if (c == '{')
fmt += "{{"; fmt += "{{";
@ -172,10 +201,15 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const {
} }
break; break;
case FmtPart::UNICHAR:
log_assert(part.sig.size() <= 32);
fmt += "{U}";
break;
case FmtPart::VLOG_TIME: case FmtPart::VLOG_TIME:
log_assert(part.sig.size() == 0); log_assert(part.sig.size() == 0);
YS_FALLTHROUGH YS_FALLTHROUGH
case FmtPart::CHARACTER: case FmtPart::STRING:
log_assert(part.sig.size() % 8 == 0); log_assert(part.sig.size() % 8 == 0);
YS_FALLTHROUGH YS_FALLTHROUGH
case FmtPart::INTEGER: case FmtPart::INTEGER:
@ -187,6 +221,8 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const {
fmt += '>'; fmt += '>';
else if (part.justify == FmtPart::LEFT) else if (part.justify == FmtPart::LEFT)
fmt += '<'; fmt += '<';
else if (part.justify == FmtPart::NUMERIC)
fmt += '=';
else log_abort(); else log_abort();
log_assert(part.width == 0 || part.padding != '\0'); log_assert(part.width == 0 || part.padding != '\0');
fmt += part.padding != '\0' ? part.padding : ' '; fmt += part.padding != '\0' ? part.padding : ' ';
@ -197,13 +233,18 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const {
case 2: fmt += 'b'; break; case 2: fmt += 'b'; break;
case 8: fmt += 'o'; break; case 8: fmt += 'o'; break;
case 10: fmt += 'd'; break; case 10: fmt += 'd'; break;
case 16: fmt += 'h'; break; case 16: fmt += part.hex_upper ? 'H' : 'h'; break;
default: log_abort(); default: log_abort();
} }
if (part.plus) switch (part.sign) {
fmt += '+'; case FmtPart::MINUS: fmt += '-'; break;
case FmtPart::PLUS_MINUS: fmt += '+'; break;
case FmtPart::SPACE_MINUS: fmt += ' '; break;
}
fmt += part.show_base ? "#" : "";
fmt += part.group ? "_" : "";
fmt += part.signed_ ? 's' : 'u'; fmt += part.signed_ ? 's' : 'u';
} else if (part.type == FmtPart::CHARACTER) { } else if (part.type == FmtPart::STRING) {
fmt += 'c'; fmt += 'c';
} else if (part.type == FmtPart::VLOG_TIME) { } else if (part.type == FmtPart::VLOG_TIME) {
if (part.realtime) if (part.realtime)
@ -299,12 +340,12 @@ void Fmt::apply_verilog_automatic_sizing_and_add(FmtPart &part)
part.width = places; part.width = places;
if (part.justify == FmtPart::RIGHT) { if (part.justify == FmtPart::RIGHT) {
append_string(gap); append_literal(gap);
parts.push_back(part); parts.push_back(part);
} else { } else {
part.justify = FmtPart::RIGHT; part.justify = FmtPart::RIGHT;
parts.push_back(part); parts.push_back(part);
append_string(gap); append_literal(gap);
} }
} }
} }
@ -355,7 +396,7 @@ void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_lik
part.str += module_name.str(); part.str += module_name.str();
} else { } else {
if (!part.str.empty()) { if (!part.str.empty()) {
part.type = FmtPart::STRING; part.type = FmtPart::LITERAL;
parts.push_back(part); parts.push_back(part);
part = {}; part = {};
} }
@ -375,7 +416,7 @@ void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_lik
part.justify = FmtPart::LEFT; part.justify = FmtPart::LEFT;
} else if (fmt[i] == '+') { } else if (fmt[i] == '+') {
// always show sign; not in IEEE 1800-2017 or verilator but iverilog has it // always show sign; not in IEEE 1800-2017 or verilator but iverilog has it
part.plus = true; part.sign = FmtPart::PLUS_MINUS;
} else break; } else break;
} }
if (i == fmt.size()) { if (i == fmt.size()) {
@ -408,11 +449,11 @@ void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_lik
part.type = FmtPart::INTEGER; part.type = FmtPart::INTEGER;
part.base = 16; part.base = 16;
} else if (fmt[i] == 'c' || fmt[i] == 'C') { } else if (fmt[i] == 'c' || fmt[i] == 'C') {
part.type = FmtPart::CHARACTER; part.type = FmtPart::STRING;
part.sig.extend_u0(8); part.sig.extend_u0(8);
// %10c and %010c not fully defined in IEEE 1800-2017 and do different things in iverilog // %10c and %010c not fully defined in IEEE 1800-2017 and do different things in iverilog
} else if (fmt[i] == 's' || fmt[i] == 'S') { } else if (fmt[i] == 's' || fmt[i] == 'S') {
part.type = FmtPart::CHARACTER; part.type = FmtPart::STRING;
if ((part.sig.size() % 8) != 0) if ((part.sig.size() % 8) != 0)
part.sig.extend_u0((part.sig.size() + 7) / 8 * 8); part.sig.extend_u0((part.sig.size() + 7) / 8 * 8);
// %10s and %010s not fully defined in IEEE 1800-2017 and do the same thing in iverilog // %10s and %010s not fully defined in IEEE 1800-2017 and do the same thing in iverilog
@ -435,12 +476,20 @@ void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_lik
log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with incomplete format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1); log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with incomplete format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1);
} }
if (part.padding == '\0') if (part.padding == '\0') {
part.padding = (has_leading_zero && part.justify == FmtPart::RIGHT) ? '0' : ' '; if (has_leading_zero && part.justify == FmtPart::RIGHT) {
part.padding = '0';
part.justify = FmtPart::NUMERIC;
} else {
part.padding = ' ';
}
}
if (part.type == FmtPart::INTEGER && part.base != 10 && part.plus) if (part.type == FmtPart::INTEGER && part.base != 10 && part.sign != FmtPart::MINUS)
log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with invalid format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1); log_file_error(fmtarg->filename, fmtarg->first_line, "System task `%s' called with invalid format specifier in argument %zu.\n", task_name.c_str(), fmtarg - args.begin() + 1);
if (part.base != 10)
part.signed_ = false;
if (part.type == FmtPart::INTEGER && !has_leading_zero) if (part.type == FmtPart::INTEGER && !has_leading_zero)
apply_verilog_automatic_sizing_and_add(part); apply_verilog_automatic_sizing_and_add(part);
else else
@ -449,12 +498,12 @@ void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_lik
} }
} }
if (!part.str.empty()) { if (!part.str.empty()) {
part.type = FmtPart::STRING; part.type = FmtPart::LITERAL;
parts.push_back(part); parts.push_back(part);
} }
} else { } else {
FmtPart part = {}; FmtPart part = {};
part.type = FmtPart::STRING; part.type = FmtPart::LITERAL;
part.str = arg->str; part.str = arg->str;
parts.push_back(part); parts.push_back(part);
} }
@ -474,7 +523,7 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
for (auto &part : parts) { for (auto &part : parts) {
switch (part.type) { switch (part.type) {
case FmtPart::STRING: case FmtPart::LITERAL:
for (char c : part.str) { for (char c : part.str) {
if (c == '%') if (c == '%')
fmt.str += "%%"; fmt.str += "%%";
@ -491,14 +540,13 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
args.push_back(arg); args.push_back(arg);
fmt.str += '%'; fmt.str += '%';
if (part.plus) if (part.sign == FmtPart::PLUS_MINUS || part.sign == FmtPart::SPACE_MINUS)
fmt.str += '+'; fmt.str += '+'; // treat space/minus as plus/minus
if (part.justify == FmtPart::LEFT) if (part.justify == FmtPart::LEFT)
fmt.str += '-'; fmt.str += '-';
if (part.width == 0) { if (part.width == 0) {
fmt.str += '0'; fmt.str += '0';
} else if (part.width > 0) { } else if (part.width > 0) {
log_assert(part.padding == ' ' || part.padding == '0');
if (part.base != 10 || part.padding == '0') if (part.base != 10 || part.padding == '0')
fmt.str += '0'; fmt.str += '0';
fmt.str += std::to_string(part.width); fmt.str += std::to_string(part.width);
@ -507,13 +555,13 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
case 2: fmt.str += 'b'; break; case 2: fmt.str += 'b'; break;
case 8: fmt.str += 'o'; break; case 8: fmt.str += 'o'; break;
case 10: fmt.str += 'd'; break; case 10: fmt.str += 'd'; break;
case 16: fmt.str += 'h'; break; case 16: fmt.str += 'h'; break; // treat uppercase hex as lowercase
default: log_abort(); default: log_abort();
} }
break; break;
} }
case FmtPart::CHARACTER: { case FmtPart::STRING: {
VerilogFmtArg arg; VerilogFmtArg arg;
arg.type = VerilogFmtArg::INTEGER; arg.type = VerilogFmtArg::INTEGER;
arg.sig = part.sig; arg.sig = part.sig;
@ -524,7 +572,6 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
fmt.str += '-'; fmt.str += '-';
if (part.sig.size() == 8) { if (part.sig.size() == 8) {
if (part.width > 0) { if (part.width > 0) {
log_assert(part.padding == '0' || part.padding == ' ');
if (part.padding == '0') if (part.padding == '0')
fmt.str += part.padding; fmt.str += part.padding;
fmt.str += std::to_string(part.width); fmt.str += std::to_string(part.width);
@ -533,7 +580,6 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
} else { } else {
log_assert(part.sig.size() % 8 == 0); log_assert(part.sig.size() % 8 == 0);
if (part.width > 0) { if (part.width > 0) {
log_assert(part.padding == ' '); // no zero padding
fmt.str += std::to_string(part.width); fmt.str += std::to_string(part.width);
} }
fmt.str += 's'; fmt.str += 's';
@ -541,6 +587,16 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
break; break;
} }
case FmtPart::UNICHAR: {
VerilogFmtArg arg;
arg.type = VerilogFmtArg::INTEGER;
arg.sig = part.sig.extract(0, 7); // only ASCII
args.push_back(arg);
fmt.str += "%c";
break;
}
case FmtPart::VLOG_TIME: { case FmtPart::VLOG_TIME: {
VerilogFmtArg arg; VerilogFmtArg arg;
arg.type = VerilogFmtArg::TIME; arg.type = VerilogFmtArg::TIME;
@ -549,11 +605,11 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
args.push_back(arg); args.push_back(arg);
fmt.str += '%'; fmt.str += '%';
if (part.plus) log_assert(part.sign == FmtPart::MINUS || part.sign == FmtPart::PLUS_MINUS);
if (part.sign == FmtPart::PLUS_MINUS)
fmt.str += '+'; fmt.str += '+';
if (part.justify == FmtPart::LEFT) if (part.justify == FmtPart::LEFT)
fmt.str += '-'; fmt.str += '-';
log_assert(part.padding == ' ' || part.padding == '0');
if (part.padding == '0' && part.width > 0) if (part.padding == '0' && part.width > 0)
fmt.str += '0'; fmt.str += '0';
fmt.str += std::to_string(part.width); fmt.str += std::to_string(part.width);
@ -599,24 +655,35 @@ void Fmt::emit_cxxrtl(std::ostream &os, std::string indent, std::function<void(c
os << indent << "buf += fmt_part { "; os << indent << "buf += fmt_part { ";
os << "fmt_part::"; os << "fmt_part::";
switch (part.type) { switch (part.type) {
case FmtPart::STRING: os << "STRING"; break; case FmtPart::LITERAL: os << "LITERAL"; break;
case FmtPart::INTEGER: os << "INTEGER"; break; case FmtPart::INTEGER: os << "INTEGER"; break;
case FmtPart::CHARACTER: os << "CHARACTER"; break; case FmtPart::STRING: os << "STRING"; break;
case FmtPart::UNICHAR: os << "UNICHAR"; break;
case FmtPart::VLOG_TIME: os << "VLOG_TIME"; break; case FmtPart::VLOG_TIME: os << "VLOG_TIME"; break;
} }
os << ", "; os << ", ";
os << escape_cxx_string(part.str) << ", "; os << escape_cxx_string(part.str) << ", ";
os << "fmt_part::"; os << "fmt_part::";
switch (part.justify) { switch (part.justify) {
case FmtPart::LEFT: os << "LEFT"; break; case FmtPart::LEFT: os << "LEFT"; break;
case FmtPart::RIGHT: os << "RIGHT"; break; case FmtPart::RIGHT: os << "RIGHT"; break;
case FmtPart::NUMERIC: os << "NUMERIC"; break;
} }
os << ", "; os << ", ";
os << "(char)" << (int)part.padding << ", "; os << "(char)" << (int)part.padding << ", ";
os << part.width << ", "; os << part.width << ", ";
os << part.base << ", "; os << part.base << ", ";
os << part.signed_ << ", "; os << part.signed_ << ", ";
os << part.plus << ", "; os << "fmt_part::";
switch (part.sign) {
case FmtPart::MINUS: os << "MINUS"; break;
case FmtPart::PLUS_MINUS: os << "PLUS_MINUS"; break;
case FmtPart::SPACE_MINUS: os << "SPACE_MINUS"; break;
}
os << ", ";
os << part.hex_upper << ", ";
os << part.show_base << ", ";
os << part.group << ", ";
os << part.realtime; os << part.realtime;
os << " }.render("; os << " }.render(";
emit_sig(part.sig); emit_sig(part.sig);
@ -631,19 +698,62 @@ std::string Fmt::render() const
for (auto &part : parts) { for (auto &part : parts) {
switch (part.type) { switch (part.type) {
case FmtPart::STRING: case FmtPart::LITERAL:
str += part.str; str += part.str;
break; break;
case FmtPart::UNICHAR: {
RTLIL::Const value = part.sig.as_const();
uint32_t codepoint = value.as_int();
if (codepoint >= 0x10000)
str += (char)(0xf0 | (codepoint >> 18));
else if (codepoint >= 0x800)
str += (char)(0xe0 | (codepoint >> 12));
else if (codepoint >= 0x80)
str += (char)(0xc0 | (codepoint >> 6));
else
str += (char)codepoint;
if (codepoint >= 0x10000)
str += (char)(0x80 | ((codepoint >> 12) & 0x3f));
if (codepoint >= 0x800)
str += (char)(0x80 | ((codepoint >> 6) & 0x3f));
if (codepoint >= 0x80)
str += (char)(0x80 | ((codepoint >> 0) & 0x3f));
break;
}
case FmtPart::INTEGER: case FmtPart::INTEGER:
case FmtPart::CHARACTER: case FmtPart::STRING:
case FmtPart::VLOG_TIME: { case FmtPart::VLOG_TIME: {
std::string buf; std::string buf;
std::string prefix;
if (part.type == FmtPart::INTEGER) { if (part.type == FmtPart::INTEGER) {
RTLIL::Const value = part.sig.as_const(); RTLIL::Const value = part.sig.as_const();
bool has_x = false, all_x = true, has_z = false, all_z = true;
for (State bit : value) {
if (bit == State::Sx)
has_x = true;
else
all_x = false;
if (bit == State::Sz)
has_z = true;
else
all_z = false;
}
if (!has_z && !has_x && part.signed_ && value[value.size() - 1]) {
prefix = "-";
value = RTLIL::const_neg(value, {}, part.signed_, {}, value.size() + 1);
} else {
switch (part.sign) {
case FmtPart::MINUS: break;
case FmtPart::PLUS_MINUS: prefix = "+"; break;
case FmtPart::SPACE_MINUS: prefix = " "; break;
}
}
if (part.base != 10) { if (part.base != 10) {
size_t minimum_size = 0; size_t minimum_size = 1;
for (size_t index = 0; index < (size_t)value.size(); index++) for (size_t index = 0; index < (size_t)value.size(); index++)
if (value[index] != State::S0) if (value[index] != State::S0)
minimum_size = index + 1; minimum_size = index + 1;
@ -651,10 +761,28 @@ std::string Fmt::render() const
} }
if (part.base == 2) { if (part.base == 2) {
buf = value.as_string(); if (part.show_base)
prefix += "0b";
for (size_t index = 0; index < (size_t)value.size(); index++) {
if (part.group && index > 0 && index % 4 == 0)
buf += '_';
RTLIL::State bit = value[index];
if (bit == State::Sx)
buf += 'x';
else if (bit == State::Sz)
buf += 'z';
else if (bit == State::S1)
buf += '1';
else /* if (bit == State::S0) */
buf += '0';
}
} else if (part.base == 8 || part.base == 16) { } else if (part.base == 8 || part.base == 16) {
if (part.show_base)
prefix += (part.base == 16) ? (part.hex_upper ? "0X" : "0x") : "0o";
size_t step = (part.base == 16) ? 4 : 3; size_t step = (part.base == 16) ? 4 : 3;
for (size_t index = 0; index < (size_t)value.size(); index += step) { for (size_t index = 0; index < (size_t)value.size(); index += step) {
if (part.group && index > 0 && index % (4 * step) == 0)
buf += '_';
RTLIL::Const subvalue = value.extract(index, min(step, value.size() - index)); RTLIL::Const subvalue = value.extract(index, min(step, value.size() - index));
bool has_x = false, all_x = true, has_z = false, all_z = true; bool has_x = false, all_x = true, has_z = false, all_z = true;
for (State bit : subvalue) { for (State bit : subvalue) {
@ -676,21 +804,11 @@ std::string Fmt::render() const
else if (has_z) else if (has_z)
buf += 'Z'; buf += 'Z';
else else
buf += "0123456789abcdef"[subvalue.as_int()]; buf += (part.hex_upper ? "0123456789ABCDEF" : "0123456789abcdef")[subvalue.as_int()];
} }
std::reverse(buf.begin(), buf.end());
} else if (part.base == 10) { } else if (part.base == 10) {
bool has_x = false, all_x = true, has_z = false, all_z = true; if (part.show_base)
for (State bit : value) { prefix += "0d";
if (bit == State::Sx)
has_x = true;
else
all_x = false;
if (bit == State::Sz)
has_z = true;
else
all_z = false;
}
if (all_x) if (all_x)
buf += 'x'; buf += 'x';
else if (all_z) else if (all_z)
@ -700,25 +818,29 @@ std::string Fmt::render() const
else if (has_z) else if (has_z)
buf += 'Z'; buf += 'Z';
else { else {
bool negative = part.signed_ && value[value.size() - 1]; log_assert(value.is_fully_def());
RTLIL::Const absvalue; if (value.is_fully_zero())
if (negative)
absvalue = RTLIL::const_neg(value, {}, part.signed_, {}, value.size() + 1);
else
absvalue = value;
log_assert(absvalue.is_fully_def());
if (absvalue.is_fully_zero())
buf += '0'; buf += '0';
while (!absvalue.is_fully_zero()) { size_t index = 0;
buf += '0' + RTLIL::const_mod(absvalue, 10, false, false, 4).as_int(); while (!value.is_fully_zero()) {
absvalue = RTLIL::const_div(absvalue, 10, false, false, absvalue.size()); if (part.group && index > 0 && index % 3 == 0)
buf += '_';
buf += '0' + RTLIL::const_mod(value, 10, false, false, 4).as_int();
value = RTLIL::const_div(value, 10, false, false, value.size());
index++;
} }
if (negative || part.plus)
buf += negative ? '-' : '+';
std::reverse(buf.begin(), buf.end());
} }
} else log_abort(); } else log_abort();
} else if (part.type == FmtPart::CHARACTER) { if (part.justify == FmtPart::NUMERIC && part.group && part.padding == '0') {
int group_size = part.base == 10 ? 3 : 4;
while (prefix.size() + buf.size() < part.width) {
if (buf.size() % (group_size + 1) == group_size)
buf += '_';
buf += '0';
}
}
std::reverse(buf.begin(), buf.end());
} else if (part.type == FmtPart::STRING) {
buf = part.sig.as_const().decode_string(); buf = part.sig.as_const().decode_string();
} else if (part.type == FmtPart::VLOG_TIME) { } else if (part.type == FmtPart::VLOG_TIME) {
// We only render() during initial, so time is always zero. // We only render() during initial, so time is always zero.
@ -726,17 +848,29 @@ std::string Fmt::render() const
} }
log_assert(part.width == 0 || part.padding != '\0'); log_assert(part.width == 0 || part.padding != '\0');
if (part.justify == FmtPart::RIGHT && buf.size() < part.width) { if (prefix.size() + buf.size() < part.width) {
size_t pad_width = part.width - buf.size(); size_t pad_width = part.width - prefix.size() - buf.size();
if (part.padding == '0' && (!buf.empty() && (buf.front() == '+' || buf.front() == '-'))) { switch (part.justify) {
str += buf.front(); case FmtPart::LEFT:
buf.erase(0, 1); str += prefix;
str += buf;
str += std::string(pad_width, part.padding);
break;
case FmtPart::RIGHT:
str += std::string(pad_width, part.padding);
str += prefix;
str += buf;
break;
case FmtPart::NUMERIC:
str += prefix;
str += std::string(pad_width, part.padding);
str += buf;
break;
} }
str += std::string(pad_width, part.padding); } else {
str += prefix;
str += buf;
} }
str += buf;
if (part.justify == FmtPart::LEFT && buf.size() < part.width)
str += std::string(part.width - buf.size(), part.padding);
break; break;
} }
} }

View file

@ -53,22 +53,24 @@ struct VerilogFmtArg {
// Must be kept in sync with `struct fmt_part` in backends/cxxrtl/runtime/cxxrtl/cxxrtl.h! // Must be kept in sync with `struct fmt_part` in backends/cxxrtl/runtime/cxxrtl/cxxrtl.h!
struct FmtPart { struct FmtPart {
enum { enum {
STRING = 0, LITERAL = 0,
INTEGER = 1, INTEGER = 1,
CHARACTER = 2, STRING = 2,
VLOG_TIME = 3, UNICHAR = 3,
VLOG_TIME = 4,
} type; } type;
// STRING type // LITERAL type
std::string str; std::string str;
// INTEGER/CHARACTER types // INTEGER/STRING/UNICHAR types
RTLIL::SigSpec sig; RTLIL::SigSpec sig;
// INTEGER/CHARACTER/VLOG_TIME types // INTEGER/STRING/VLOG_TIME types
enum { enum {
RIGHT = 0, RIGHT = 0,
LEFT = 1, LEFT = 1,
NUMERIC = 2,
} justify = RIGHT; } justify = RIGHT;
char padding = '\0'; char padding = '\0';
size_t width = 0; size_t width = 0;
@ -76,7 +78,14 @@ struct FmtPart {
// INTEGER type // INTEGER type
unsigned base = 10; unsigned base = 10;
bool signed_ = false; bool signed_ = false;
bool plus = false; enum {
MINUS = 0,
PLUS_MINUS = 1,
SPACE_MINUS = 2,
} sign = MINUS;
bool hex_upper = false;
bool show_base = false;
bool group = false;
// VLOG_TIME type // VLOG_TIME type
bool realtime = false; bool realtime = false;
@ -86,7 +95,7 @@ struct Fmt {
public: public:
std::vector<FmtPart> parts; std::vector<FmtPart> parts;
void append_string(const std::string &str); void append_literal(const std::string &str);
void parse_rtlil(const RTLIL::Cell *cell); void parse_rtlil(const RTLIL::Cell *cell);
void emit_rtlil(RTLIL::Cell *cell) const; void emit_rtlil(RTLIL::Cell *cell) const;

View file

@ -17,11 +17,11 @@
* *
*/ */
#include "kernel/yosys.h"
#ifndef LOG_H #ifndef LOG_H
#define LOG_H #define LOG_H
#include "kernel/yosys_common.h"
#include <time.h> #include <time.h>
#include <regex> #include <regex>
@ -449,4 +449,6 @@ void log_dump_args_worker(const char *p, T first, Args ... args)
YOSYS_NAMESPACE_END YOSYS_NAMESPACE_END
#include "kernel/yosys.h"
#endif #endif

View file

@ -17,11 +17,12 @@
* *
*/ */
#include "kernel/yosys.h"
#ifndef REGISTER_H #ifndef REGISTER_H
#define REGISTER_H #define REGISTER_H
#include "kernel/yosys_common.h"
#include "kernel/yosys.h"
YOSYS_NAMESPACE_BEGIN YOSYS_NAMESPACE_BEGIN
struct Pass struct Pass

View file

@ -17,11 +17,12 @@
* *
*/ */
#include "kernel/yosys.h"
#ifndef RTLIL_H #ifndef RTLIL_H
#define RTLIL_H #define RTLIL_H
#include "kernel/yosys_common.h"
#include "kernel/yosys.h"
YOSYS_NAMESPACE_BEGIN YOSYS_NAMESPACE_BEGIN
namespace RTLIL namespace RTLIL

View file

@ -39,323 +39,7 @@
#ifndef YOSYS_H #ifndef YOSYS_H
#define YOSYS_H #define YOSYS_H
#include <map> #include "kernel/yosys_common.h"
#include <set>
#include <tuple>
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
#include <unordered_map>
#include <unordered_set>
#include <initializer_list>
#include <stdexcept>
#include <memory>
#include <cmath>
#include <cstddef>
#include <sstream>
#include <fstream>
#include <istream>
#include <ostream>
#include <iostream>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <limits.h>
#include <sys/stat.h>
#include <errno.h>
#ifdef WITH_PYTHON
#include <Python.h>
#endif
#ifndef _YOSYS_
# error It looks like you are trying to build Yosys without the config defines set. \
When building Yosys with a custom make system, make sure you set all the \
defines the Yosys Makefile would set for your build configuration.
#endif
#ifdef YOSYS_ENABLE_TCL
# include <tcl.h>
# ifdef YOSYS_MXE_HACKS
extern Tcl_Command Tcl_CreateCommand(Tcl_Interp *interp, const char *cmdName, Tcl_CmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *deleteProc);
extern Tcl_Interp *Tcl_CreateInterp(void);
extern void Tcl_Preserve(ClientData data);
extern void Tcl_Release(ClientData clientData);
extern int Tcl_InterpDeleted(Tcl_Interp *interp);
extern void Tcl_DeleteInterp(Tcl_Interp *interp);
extern int Tcl_Eval(Tcl_Interp *interp, const char *script);
extern int Tcl_EvalFile(Tcl_Interp *interp, const char *fileName);
extern void Tcl_Finalize(void);
extern int Tcl_GetCommandInfo(Tcl_Interp *interp, const char *cmdName, Tcl_CmdInfo *infoPtr);
extern const char *Tcl_GetStringResult(Tcl_Interp *interp);
extern Tcl_Obj *Tcl_NewStringObj(const char *bytes, int length);
extern Tcl_Obj *Tcl_NewIntObj(int intValue);
extern Tcl_Obj *Tcl_NewListObj(int objc, Tcl_Obj *const objv[]);
extern Tcl_Obj *Tcl_ObjSetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *newValuePtr, int flags);
# endif
# undef CONST
# undef INLINE
#endif
#ifdef _WIN32
# undef NOMINMAX
# define NOMINMAX 1
# undef YY_NO_UNISTD_H
# define YY_NO_UNISTD_H 1
# include <windows.h>
# include <io.h>
# include <direct.h>
# define strtok_r strtok_s
# define strdup _strdup
# define snprintf _snprintf
# define getcwd _getcwd
# define mkdir _mkdir
# define popen _popen
# define pclose _pclose
# ifndef __MINGW32__
# define PATH_MAX MAX_PATH
# define isatty _isatty
# define fileno _fileno
# endif
// The following defines conflict with our identifiers:
# undef CONST
// `wingdi.h` defines a TRANSPARENT macro that conflicts with X(TRANSPARENT) entry in kernel/constids.inc
# undef TRANSPARENT
#endif
#ifndef PATH_MAX
# define PATH_MAX 4096
#endif
#define YOSYS_NAMESPACE Yosys
#define PRIVATE_NAMESPACE_BEGIN namespace {
#define PRIVATE_NAMESPACE_END }
#define YOSYS_NAMESPACE_BEGIN namespace Yosys {
#define YOSYS_NAMESPACE_END }
#define YOSYS_NAMESPACE_PREFIX Yosys::
#define USING_YOSYS_NAMESPACE using namespace Yosys;
#if defined(__GNUC__) || defined(__clang__)
# define YS_ATTRIBUTE(...) __attribute__((__VA_ARGS__))
#elif defined(_MSC_VER)
# define YS_ATTRIBUTE(...)
#else
# define YS_ATTRIBUTE(...)
#endif
#if defined(__GNUC__) || defined(__clang__)
# define YS_MAYBE_UNUSED __attribute__((__unused__))
#else
# define YS_MAYBE_UNUSED
#endif
#if __cplusplus >= 201703L
# define YS_FALLTHROUGH [[fallthrough]];
#elif defined(__clang__)
# define YS_FALLTHROUGH [[clang::fallthrough]];
#elif defined(__GNUC__)
# define YS_FALLTHROUGH [[gnu::fallthrough]];
#else
# define YS_FALLTHROUGH
#endif
YOSYS_NAMESPACE_BEGIN
// Note: All headers included in hashlib.h must be included
// outside of YOSYS_NAMESPACE before this or bad things will happen.
#ifdef HASHLIB_H
# undef HASHLIB_H
# include "kernel/hashlib.h"
#else
# include "kernel/hashlib.h"
# undef HASHLIB_H
#endif
using std::vector;
using std::string;
using std::tuple;
using std::pair;
using std::make_tuple;
using std::make_pair;
using std::get;
using std::min;
using std::max;
// A primitive shared string implementation that does not
// move its .c_str() when the object is copied or moved.
struct shared_str {
std::shared_ptr<string> content;
shared_str() { }
shared_str(string s) { content = std::shared_ptr<string>(new string(s)); }
shared_str(const char *s) { content = std::shared_ptr<string>(new string(s)); }
const char *c_str() const { return content->c_str(); }
const string &str() const { return *content; }
bool operator==(const shared_str &other) const { return *content == *other.content; }
unsigned int hash() const { return hashlib::hash_ops<std::string>::hash(*content); }
};
using hashlib::mkhash;
using hashlib::mkhash_init;
using hashlib::mkhash_add;
using hashlib::mkhash_xorshift;
using hashlib::hash_ops;
using hashlib::hash_cstr_ops;
using hashlib::hash_ptr_ops;
using hashlib::hash_obj_ops;
using hashlib::dict;
using hashlib::idict;
using hashlib::pool;
using hashlib::mfp;
namespace RTLIL {
struct IdString;
struct Const;
struct SigBit;
struct SigSpec;
struct Wire;
struct Cell;
struct Memory;
struct Process;
struct Module;
struct Design;
struct Monitor;
enum State : unsigned char;
}
namespace AST {
struct AstNode;
}
using RTLIL::IdString;
using RTLIL::Const;
using RTLIL::SigBit;
using RTLIL::SigSpec;
using RTLIL::Wire;
using RTLIL::Cell;
using RTLIL::Module;
using RTLIL::Design;
namespace hashlib {
template<> struct hash_ops<RTLIL::Wire*> : hash_obj_ops {};
template<> struct hash_ops<RTLIL::Cell*> : hash_obj_ops {};
template<> struct hash_ops<RTLIL::Memory*> : hash_obj_ops {};
template<> struct hash_ops<RTLIL::Process*> : hash_obj_ops {};
template<> struct hash_ops<RTLIL::Module*> : hash_obj_ops {};
template<> struct hash_ops<RTLIL::Design*> : hash_obj_ops {};
template<> struct hash_ops<RTLIL::Monitor*> : hash_obj_ops {};
template<> struct hash_ops<AST::AstNode*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Wire*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Cell*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Memory*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Process*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Module*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Design*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Monitor*> : hash_obj_ops {};
template<> struct hash_ops<const AST::AstNode*> : hash_obj_ops {};
}
void memhasher_on();
void memhasher_off();
void memhasher_do();
extern bool memhasher_active;
inline void memhasher() { if (memhasher_active) memhasher_do(); }
void yosys_banner();
int ceil_log2(int x) YS_ATTRIBUTE(const);
inline std::string vstringf(const char *fmt, va_list ap)
{
// For the common case of strings shorter than 128, save a heap
// allocation by using a stack allocated buffer.
const int kBufSize = 128;
char buf[kBufSize];
buf[0] = '\0';
va_list apc;
va_copy(apc, ap);
int n = vsnprintf(buf, kBufSize, fmt, apc);
va_end(apc);
if (n < kBufSize)
return std::string(buf);
std::string string;
char *str = NULL;
#if defined(_WIN32 )|| defined(__CYGWIN__)
int sz = 2 * kBufSize, rc;
while (1) {
va_copy(apc, ap);
str = (char*)realloc(str, sz);
rc = vsnprintf(str, sz, fmt, apc);
va_end(apc);
if (rc >= 0 && rc < sz)
break;
sz *= 2;
}
if (str != NULL) {
string = str;
free(str);
}
return string;
#else
if (vasprintf(&str, fmt, ap) < 0)
str = NULL;
if (str != NULL) {
string = str;
free(str);
}
return string;
#endif
}
std::string stringf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 1, 2));
inline std::string stringf(const char *fmt, ...)
{
std::string string;
va_list ap;
va_start(ap, fmt);
string = vstringf(fmt, ap);
va_end(ap);
return string;
}
int readsome(std::istream &f, char *s, int n);
std::string next_token(std::string &text, const char *sep = " \t\r\n", bool long_strings = false);
std::vector<std::string> split_tokens(const std::string &text, const char *sep = " \t\r\n");
bool patmatch(const char *pattern, const char *string);
#if !defined(YOSYS_DISABLE_SPAWN)
int run_command(const std::string &command, std::function<void(const std::string&)> process_line = std::function<void(const std::string&)>());
#endif
std::string get_base_tmpdir();
std::string make_temp_file(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX");
std::string make_temp_dir(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX");
bool check_file_exists(std::string filename, bool is_exec = false);
bool check_directory_exists(const std::string& dirname);
bool is_absolute_path(std::string filename);
void remove_directory(std::string dirname);
bool create_directory(const std::string& dirname);
std::string escape_filename_spaces(const std::string& filename);
template<typename T> int GetSize(const T &obj) { return obj.size(); }
inline int GetSize(RTLIL::Wire *wire);
extern int autoidx;
extern int yosys_xtrace;
YOSYS_NAMESPACE_END
#include "kernel/log.h" #include "kernel/log.h"
#include "kernel/rtlil.h" #include "kernel/rtlil.h"
@ -363,14 +47,6 @@ YOSYS_NAMESPACE_END
YOSYS_NAMESPACE_BEGIN YOSYS_NAMESPACE_BEGIN
using RTLIL::State;
using RTLIL::SigChunk;
using RTLIL::SigSig;
namespace hashlib {
template<> struct hash_ops<RTLIL::State> : hash_ops<int> {};
}
void yosys_setup(); void yosys_setup();
#ifdef WITH_PYTHON #ifdef WITH_PYTHON
@ -385,26 +61,6 @@ Tcl_Interp *yosys_get_tcl_interp();
extern RTLIL::Design *yosys_design; extern RTLIL::Design *yosys_design;
RTLIL::IdString new_id(std::string file, int line, std::string func);
RTLIL::IdString new_id_suffix(std::string file, int line, std::string func, std::string suffix);
#define NEW_ID \
YOSYS_NAMESPACE_PREFIX new_id(__FILE__, __LINE__, __FUNCTION__)
#define NEW_ID_SUFFIX(suffix) \
YOSYS_NAMESPACE_PREFIX new_id_suffix(__FILE__, __LINE__, __FUNCTION__, suffix)
// Create a statically allocated IdString object, using for example ID::A or ID($add).
//
// Recipe for Converting old code that is using conversion of strings like ID::A and
// "$add" for creating IdStrings: Run below SED command on the .cc file and then use for
// example "meld foo.cc foo.cc.orig" to manually compile errors, if necessary.
//
// sed -i.orig -r 's/"\\\\([a-zA-Z0-9_]+)"/ID(\1)/g; s/"(\$[a-zA-Z0-9_]+)"/ID(\1)/g;' <filename>
//
#define ID(_id) ([]() { const char *p = "\\" #_id, *q = p[1] == '$' ? p+1 : p; \
static const YOSYS_NAMESPACE_PREFIX RTLIL::IdString id(q); return id; })()
namespace ID = RTLIL::ID;
RTLIL::Design *yosys_get_design(); RTLIL::Design *yosys_get_design();
std::string proc_self_dirname(); std::string proc_self_dirname();
std::string proc_share_dirname(); std::string proc_share_dirname();

379
kernel/yosys_common.h Normal file
View file

@ -0,0 +1,379 @@
/* -*- c++ -*-
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2012 Claire Xenia Wolf <claire@yosyshq.com>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* 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 YOSYS_COMMON_H
#define YOSYS_COMMON_H
#include <map>
#include <set>
#include <tuple>
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
#include <unordered_map>
#include <unordered_set>
#include <initializer_list>
#include <stdexcept>
#include <memory>
#include <cmath>
#include <cstddef>
#include <sstream>
#include <fstream>
#include <istream>
#include <ostream>
#include <iostream>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <limits.h>
#include <sys/stat.h>
#include <errno.h>
#ifdef WITH_PYTHON
#include <Python.h>
#endif
#ifndef _YOSYS_
# error It looks like you are trying to build Yosys without the config defines set. \
When building Yosys with a custom make system, make sure you set all the \
defines the Yosys Makefile would set for your build configuration.
#endif
#ifdef YOSYS_ENABLE_TCL
# include <tcl.h>
# ifdef YOSYS_MXE_HACKS
extern Tcl_Command Tcl_CreateCommand(Tcl_Interp *interp, const char *cmdName, Tcl_CmdProc *proc, ClientData clientData, Tcl_CmdDeleteProc *deleteProc);
extern Tcl_Interp *Tcl_CreateInterp(void);
extern void Tcl_Preserve(ClientData data);
extern void Tcl_Release(ClientData clientData);
extern int Tcl_InterpDeleted(Tcl_Interp *interp);
extern void Tcl_DeleteInterp(Tcl_Interp *interp);
extern int Tcl_Eval(Tcl_Interp *interp, const char *script);
extern int Tcl_EvalFile(Tcl_Interp *interp, const char *fileName);
extern void Tcl_Finalize(void);
extern int Tcl_GetCommandInfo(Tcl_Interp *interp, const char *cmdName, Tcl_CmdInfo *infoPtr);
extern const char *Tcl_GetStringResult(Tcl_Interp *interp);
extern Tcl_Obj *Tcl_NewStringObj(const char *bytes, int length);
extern Tcl_Obj *Tcl_NewIntObj(int intValue);
extern Tcl_Obj *Tcl_NewListObj(int objc, Tcl_Obj *const objv[]);
extern Tcl_Obj *Tcl_ObjSetVar2(Tcl_Interp *interp, Tcl_Obj *part1Ptr, Tcl_Obj *part2Ptr, Tcl_Obj *newValuePtr, int flags);
# endif
# undef CONST
# undef INLINE
#endif
#ifdef _WIN32
# undef NOMINMAX
# define NOMINMAX 1
# undef YY_NO_UNISTD_H
# define YY_NO_UNISTD_H 1
# include <windows.h>
# include <io.h>
# include <direct.h>
# define strtok_r strtok_s
# define strdup _strdup
# define snprintf _snprintf
# define getcwd _getcwd
# define mkdir _mkdir
# define popen _popen
# define pclose _pclose
# ifndef __MINGW32__
# define PATH_MAX MAX_PATH
# define isatty _isatty
# define fileno _fileno
# endif
// The following defines conflict with our identifiers:
# undef CONST
// `wingdi.h` defines a TRANSPARENT macro that conflicts with X(TRANSPARENT) entry in kernel/constids.inc
# undef TRANSPARENT
#endif
#ifndef PATH_MAX
# define PATH_MAX 4096
#endif
#define YOSYS_NAMESPACE Yosys
#define PRIVATE_NAMESPACE_BEGIN namespace {
#define PRIVATE_NAMESPACE_END }
#define YOSYS_NAMESPACE_BEGIN namespace Yosys {
#define YOSYS_NAMESPACE_END }
#define YOSYS_NAMESPACE_PREFIX Yosys::
#define USING_YOSYS_NAMESPACE using namespace Yosys;
#if defined(__GNUC__) || defined(__clang__)
# define YS_ATTRIBUTE(...) __attribute__((__VA_ARGS__))
#elif defined(_MSC_VER)
# define YS_ATTRIBUTE(...)
#else
# define YS_ATTRIBUTE(...)
#endif
#if defined(__GNUC__) || defined(__clang__)
# define YS_MAYBE_UNUSED __attribute__((__unused__))
#else
# define YS_MAYBE_UNUSED
#endif
#if __cplusplus >= 201703L
# define YS_FALLTHROUGH [[fallthrough]];
#elif defined(__clang__)
# define YS_FALLTHROUGH [[clang::fallthrough]];
#elif defined(__GNUC__)
# define YS_FALLTHROUGH [[gnu::fallthrough]];
#else
# define YS_FALLTHROUGH
#endif
YOSYS_NAMESPACE_BEGIN
// Note: All headers included in hashlib.h must be included
// outside of YOSYS_NAMESPACE before this or bad things will happen.
#ifdef HASHLIB_H
# undef HASHLIB_H
# include "kernel/hashlib.h"
#else
# include "kernel/hashlib.h"
# undef HASHLIB_H
#endif
using std::vector;
using std::string;
using std::tuple;
using std::pair;
using std::make_tuple;
using std::make_pair;
using std::get;
using std::min;
using std::max;
// A primitive shared string implementation that does not
// move its .c_str() when the object is copied or moved.
struct shared_str {
std::shared_ptr<string> content;
shared_str() { }
shared_str(string s) { content = std::shared_ptr<string>(new string(s)); }
shared_str(const char *s) { content = std::shared_ptr<string>(new string(s)); }
const char *c_str() const { return content->c_str(); }
const string &str() const { return *content; }
bool operator==(const shared_str &other) const { return *content == *other.content; }
unsigned int hash() const { return hashlib::hash_ops<std::string>::hash(*content); }
};
using hashlib::mkhash;
using hashlib::mkhash_init;
using hashlib::mkhash_add;
using hashlib::mkhash_xorshift;
using hashlib::hash_ops;
using hashlib::hash_cstr_ops;
using hashlib::hash_ptr_ops;
using hashlib::hash_obj_ops;
using hashlib::dict;
using hashlib::idict;
using hashlib::pool;
using hashlib::mfp;
namespace RTLIL {
struct IdString;
struct Const;
struct SigBit;
struct SigSpec;
struct Wire;
struct Cell;
struct Memory;
struct Process;
struct Module;
struct Design;
struct Monitor;
struct Selection;
struct SigChunk;
enum State : unsigned char;
typedef std::pair<SigSpec, SigSpec> SigSig;
namespace ID {}
}
namespace AST {
struct AstNode;
}
using RTLIL::IdString;
using RTLIL::Const;
using RTLIL::SigBit;
using RTLIL::SigSpec;
using RTLIL::Wire;
using RTLIL::Cell;
using RTLIL::Module;
using RTLIL::Design;
using RTLIL::State;
using RTLIL::SigChunk;
using RTLIL::SigSig;
namespace hashlib {
template<> struct hash_ops<RTLIL::Wire*> : hash_obj_ops {};
template<> struct hash_ops<RTLIL::Cell*> : hash_obj_ops {};
template<> struct hash_ops<RTLIL::Memory*> : hash_obj_ops {};
template<> struct hash_ops<RTLIL::Process*> : hash_obj_ops {};
template<> struct hash_ops<RTLIL::Module*> : hash_obj_ops {};
template<> struct hash_ops<RTLIL::Design*> : hash_obj_ops {};
template<> struct hash_ops<RTLIL::Monitor*> : hash_obj_ops {};
template<> struct hash_ops<AST::AstNode*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Wire*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Cell*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Memory*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Process*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Module*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Design*> : hash_obj_ops {};
template<> struct hash_ops<const RTLIL::Monitor*> : hash_obj_ops {};
template<> struct hash_ops<const AST::AstNode*> : hash_obj_ops {};
}
void memhasher_on();
void memhasher_off();
void memhasher_do();
extern bool memhasher_active;
inline void memhasher() { if (memhasher_active) memhasher_do(); }
void yosys_banner();
int ceil_log2(int x) YS_ATTRIBUTE(const);
inline std::string vstringf(const char *fmt, va_list ap)
{
// For the common case of strings shorter than 128, save a heap
// allocation by using a stack allocated buffer.
const int kBufSize = 128;
char buf[kBufSize];
buf[0] = '\0';
va_list apc;
va_copy(apc, ap);
int n = vsnprintf(buf, kBufSize, fmt, apc);
va_end(apc);
if (n < kBufSize)
return std::string(buf);
std::string string;
char *str = NULL;
#if defined(_WIN32 )|| defined(__CYGWIN__)
int sz = 2 * kBufSize, rc;
while (1) {
va_copy(apc, ap);
str = (char*)realloc(str, sz);
rc = vsnprintf(str, sz, fmt, apc);
va_end(apc);
if (rc >= 0 && rc < sz)
break;
sz *= 2;
}
if (str != NULL) {
string = str;
free(str);
}
return string;
#else
if (vasprintf(&str, fmt, ap) < 0)
str = NULL;
if (str != NULL) {
string = str;
free(str);
}
return string;
#endif
}
std::string stringf(const char *fmt, ...) YS_ATTRIBUTE(format(printf, 1, 2));
inline std::string stringf(const char *fmt, ...)
{
std::string string;
va_list ap;
va_start(ap, fmt);
string = vstringf(fmt, ap);
va_end(ap);
return string;
}
int readsome(std::istream &f, char *s, int n);
std::string next_token(std::string &text, const char *sep = " \t\r\n", bool long_strings = false);
std::vector<std::string> split_tokens(const std::string &text, const char *sep = " \t\r\n");
bool patmatch(const char *pattern, const char *string);
#if !defined(YOSYS_DISABLE_SPAWN)
int run_command(const std::string &command, std::function<void(const std::string&)> process_line = std::function<void(const std::string&)>());
#endif
std::string get_base_tmpdir();
std::string make_temp_file(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX");
std::string make_temp_dir(std::string template_str = get_base_tmpdir() + "/yosys_XXXXXX");
bool check_file_exists(std::string filename, bool is_exec = false);
bool check_directory_exists(const std::string& dirname);
bool is_absolute_path(std::string filename);
void remove_directory(std::string dirname);
bool create_directory(const std::string& dirname);
std::string escape_filename_spaces(const std::string& filename);
template<typename T> int GetSize(const T &obj) { return obj.size(); }
inline int GetSize(RTLIL::Wire *wire);
extern int autoidx;
extern int yosys_xtrace;
RTLIL::IdString new_id(std::string file, int line, std::string func);
RTLIL::IdString new_id_suffix(std::string file, int line, std::string func, std::string suffix);
#define NEW_ID \
YOSYS_NAMESPACE_PREFIX new_id(__FILE__, __LINE__, __FUNCTION__)
#define NEW_ID_SUFFIX(suffix) \
YOSYS_NAMESPACE_PREFIX new_id_suffix(__FILE__, __LINE__, __FUNCTION__, suffix)
// Create a statically allocated IdString object, using for example ID::A or ID($add).
//
// Recipe for Converting old code that is using conversion of strings like ID::A and
// "$add" for creating IdStrings: Run below SED command on the .cc file and then use for
// example "meld foo.cc foo.cc.orig" to manually compile errors, if necessary.
//
// sed -i.orig -r 's/"\\\\([a-zA-Z0-9_]+)"/ID(\1)/g; s/"(\$[a-zA-Z0-9_]+)"/ID(\1)/g;' <filename>
//
#define ID(_id) ([]() { const char *p = "\\" #_id, *q = p[1] == '$' ? p+1 : p; \
static const YOSYS_NAMESPACE_PREFIX RTLIL::IdString id(q); return id; })()
namespace ID = RTLIL::ID;
namespace hashlib {
template<> struct hash_ops<RTLIL::State> : hash_ops<int> {};
}
YOSYS_NAMESPACE_END
#endif

View file

@ -36,7 +36,8 @@ struct cell_area_t {
struct statdata_t struct statdata_t
{ {
#define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \ #define STAT_INT_MEMBERS X(num_wires) X(num_wire_bits) X(num_pub_wires) X(num_pub_wire_bits) \
X(num_memories) X(num_memory_bits) X(num_cells) X(num_processes) X(num_ports) X(num_port_bits) X(num_memories) X(num_memory_bits) X(num_cells) \
X(num_processes)
#define STAT_NUMERIC_MEMBERS STAT_INT_MEMBERS X(area) #define STAT_NUMERIC_MEMBERS STAT_INT_MEMBERS X(area)
@ -90,6 +91,11 @@ struct statdata_t
for (auto wire : mod->selected_wires()) for (auto wire : mod->selected_wires())
{ {
if (wire->port_input || wire->port_output) {
num_ports++;
num_port_bits += wire->width;
}
if (wire->name.isPublic()) { if (wire->name.isPublic()) {
num_pub_wires++; num_pub_wires++;
num_pub_wire_bits += wire->width; num_pub_wire_bits += wire->width;
@ -239,6 +245,8 @@ struct statdata_t
log(" Number of wire bits: %6u\n", num_wire_bits); log(" Number of wire bits: %6u\n", num_wire_bits);
log(" Number of public wires: %6u\n", num_pub_wires); log(" Number of public wires: %6u\n", num_pub_wires);
log(" Number of public wire bits: %6u\n", num_pub_wire_bits); log(" Number of public wire bits: %6u\n", num_pub_wire_bits);
log(" Number of ports: %6u\n", num_ports);
log(" Number of port bits: %6u\n", num_port_bits);
log(" Number of memories: %6u\n", num_memories); log(" Number of memories: %6u\n", num_memories);
log(" Number of memory bits: %6u\n", num_memory_bits); log(" Number of memory bits: %6u\n", num_memory_bits);
log(" Number of processes: %6u\n", num_processes); log(" Number of processes: %6u\n", num_processes);
@ -284,6 +292,8 @@ struct statdata_t
log(" \"num_wire_bits\": %u,\n", num_wire_bits); log(" \"num_wire_bits\": %u,\n", num_wire_bits);
log(" \"num_pub_wires\": %u,\n", num_pub_wires); log(" \"num_pub_wires\": %u,\n", num_pub_wires);
log(" \"num_pub_wire_bits\": %u,\n", num_pub_wire_bits); log(" \"num_pub_wire_bits\": %u,\n", num_pub_wire_bits);
log(" \"num_ports\": %u,\n", num_ports);
log(" \"num_port_bits\": %u,\n", num_port_bits);
log(" \"num_memories\": %u,\n", num_memories); log(" \"num_memories\": %u,\n", num_memories);
log(" \"num_memory_bits\": %u,\n", num_memory_bits); log(" \"num_memory_bits\": %u,\n", num_memory_bits);
log(" \"num_processes\": %u,\n", num_processes); log(" \"num_processes\": %u,\n", num_processes);
@ -494,6 +504,8 @@ struct StatPass : public Pass {
design->scratchpad_set_int("stat.num_wire_bits", data.num_wire_bits); design->scratchpad_set_int("stat.num_wire_bits", data.num_wire_bits);
design->scratchpad_set_int("stat.num_pub_wires", data.num_pub_wires); design->scratchpad_set_int("stat.num_pub_wires", data.num_pub_wires);
design->scratchpad_set_int("stat.num_pub_wire_bits", data.num_pub_wire_bits); design->scratchpad_set_int("stat.num_pub_wire_bits", data.num_pub_wire_bits);
design->scratchpad_set_int("stat.num_ports", data.num_ports);
design->scratchpad_set_int("stat.num_port_bits", data.num_port_bits);
design->scratchpad_set_int("stat.num_memories", data.num_memories); design->scratchpad_set_int("stat.num_memories", data.num_memories);
design->scratchpad_set_int("stat.num_memory_bits", data.num_memory_bits); design->scratchpad_set_int("stat.num_memory_bits", data.num_memory_bits);
design->scratchpad_set_int("stat.num_processes", data.num_processes); design->scratchpad_set_int("stat.num_processes", data.num_processes);

View file

@ -35,3 +35,4 @@ $(eval $(call add_share_file,share,techlibs/common/abc9_map.v))
$(eval $(call add_share_file,share,techlibs/common/abc9_unmap.v)) $(eval $(call add_share_file,share,techlibs/common/abc9_unmap.v))
$(eval $(call add_share_file,share,techlibs/common/cmp2lcu.v)) $(eval $(call add_share_file,share,techlibs/common/cmp2lcu.v))
$(eval $(call add_share_file,share,techlibs/common/cmp2softlogic.v)) $(eval $(call add_share_file,share,techlibs/common/cmp2softlogic.v))
$(eval $(call add_share_file,share/choices,techlibs/common/choices/kogge-stone.v))

View file

@ -0,0 +1,54 @@
/*
* yosys -- Yosys Open SYnthesis Suite
*
* Copyright (C) 2024 Martin Povišer <povik@cutebit.org>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* 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.
*
*/
(* techmap_celltype = "$lcu" *)
module _80_lcu_kogge_stone (P, G, CI, CO);
parameter WIDTH = 2;
(* force_downto *)
input [WIDTH-1:0] P, G;
input CI;
(* force_downto *)
output [WIDTH-1:0] CO;
integer i, j;
(* force_downto *)
reg [WIDTH-1:0] p, g;
wire [1023:0] _TECHMAP_DO_ = "proc; opt -fast";
always @* begin
p = P;
g = G;
// in almost all cases CI will be constant zero
g[0] = g[0] | (p[0] & CI);
for (i = 0; i < $clog2(WIDTH); i = i + 1) begin
// iterate in reverse so we don't confuse a result from this stage and the previous
for (j = WIDTH - 1; j >= 2**i; j = j - 1) begin
g[j] = g[j] | p[j] & g[j - 2**i];
p[j] = p[j] & p[j - 2**i];
end
end
end
assign CO = g;
endmodule

View file

@ -902,18 +902,34 @@ endgenerate
endmodule endmodule
// -------------------------------------------------------- // --------------------------------------------------------
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
//-
//- $macc (A, B, Y)
//-
//- Multiply and accumulate.
//- A building block for summing any number of negated and unnegated signals
//- and arithmetic products of pairs of signals. Cell port A concatenates pairs
//- of signals to be multiplied together. When the second signal in a pair is zero
//- length, a constant 1 is used instead as the second factor. Cell port B
//- concatenates 1-bit-wide signals to also be summed, such as "carry in" in adders.
//- Typically created by the `alumacc` pass, which transforms $add and $mul
//- into $macc cells.
module \$macc (A, B, Y); module \$macc (A, B, Y);
parameter A_WIDTH = 0; parameter A_WIDTH = 0;
parameter B_WIDTH = 0; parameter B_WIDTH = 0;
parameter Y_WIDTH = 0; parameter Y_WIDTH = 0;
// CONFIG determines the layout of A, as explained below
parameter CONFIG = 4'b0000; parameter CONFIG = 4'b0000;
parameter CONFIG_WIDTH = 4; parameter CONFIG_WIDTH = 4;
input [A_WIDTH-1:0] A; // In the terms used for this cell, there's mixed meanings for the term "port". To disambiguate:
input [B_WIDTH-1:0] B; // A cell port is for example the A input (it is constructed in C++ as cell->setPort(ID::A, ...))
output reg [Y_WIDTH-1:0] Y; // Multiplier ports are pairs of multiplier inputs ("factors").
// If the second signal in such a pair is zero length, no multiplication is necessary, and the first signal is just added to the sum.
input [A_WIDTH-1:0] A; // Cell port A is the concatenation of all arithmetic ports
input [B_WIDTH-1:0] B; // Cell port B is the concatenation of single-bit unsigned signals to be also added to the sum
output reg [Y_WIDTH-1:0] Y; // Output sum
// Xilinx XSIM does not like $clog2() below.. // Xilinx XSIM does not like $clog2() below..
function integer my_clog2; function integer my_clog2;
@ -929,10 +945,42 @@ function integer my_clog2;
end end
endfunction endfunction
// Bits that a factor's length field in CONFIG per factor in cell port A
localparam integer num_bits = CONFIG[3:0] > 0 ? CONFIG[3:0] : 1; localparam integer num_bits = CONFIG[3:0] > 0 ? CONFIG[3:0] : 1;
// Number of multiplier ports
localparam integer num_ports = (CONFIG_WIDTH-4) / (2 + 2*num_bits); localparam integer num_ports = (CONFIG_WIDTH-4) / (2 + 2*num_bits);
// Minium bit width of an induction variable to iterate over all bits of cell port A
localparam integer num_abits = my_clog2(A_WIDTH) > 0 ? my_clog2(A_WIDTH) : 1; localparam integer num_abits = my_clog2(A_WIDTH) > 0 ? my_clog2(A_WIDTH) : 1;
// In this pseudocode, u(foo) means an unsigned int that's foo bits long.
// The CONFIG parameter carries the following information:
// struct CONFIG {
// u4 num_bits;
// struct port_field {
// bool is_signed;
// bool is_subtract;
// u(num_bits) factor1_len;
// u(num_bits) factor2_len;
// }[num_ports];
// };
// The A cell port carries the following information:
// struct A {
// u(CONFIG.port_field[0].factor1_len) port0factor1;
// u(CONFIG.port_field[0].factor2_len) port0factor2;
// u(CONFIG.port_field[1].factor1_len) port1factor1;
// u(CONFIG.port_field[1].factor2_len) port1factor2;
// ...
// };
// and log(sizeof(A)) is num_abits.
// No factor1 may have a zero length.
// A factor2 having a zero length implies factor2 is replaced with a constant 1.
// Additionally, B is an array of 1-bit-wide unsigned integers to also be summed up.
// Finally, we have:
// Y = port0factor1 * port0factor2 + port1factor1 * port1factor2 + ...
// * B[0] + B[1] + ...
function [2*num_ports*num_abits-1:0] get_port_offsets; function [2*num_ports*num_abits-1:0] get_port_offsets;
input [CONFIG_WIDTH-1:0] cfg; input [CONFIG_WIDTH-1:0] cfg;
integer i, cursor; integer i, cursor;

View file

@ -207,7 +207,7 @@ module _90_fa (A, B, C, X, Y);
endmodule endmodule
(* techmap_celltype = "$lcu" *) (* techmap_celltype = "$lcu" *)
module _90_lcu (P, G, CI, CO); module _90_lcu_brent_kung (P, G, CI, CO);
parameter WIDTH = 2; parameter WIDTH = 2;
(* force_downto *) (* force_downto *)

View file

@ -113,7 +113,31 @@ module EFX_GBUFCE(
endmodule endmodule
module EFX_RAM_5K( module EFX_RAM_5K
# (
parameter READ_WIDTH = 20,
parameter WRITE_WIDTH = 20,
localparam READ_ADDR_WIDTH =
(READ_WIDTH == 16) ? 8 : // 256x16
(READ_WIDTH == 8) ? 9 : // 512x8
(READ_WIDTH == 4) ? 10 : // 1024x4
(READ_WIDTH == 2) ? 11 : // 2048x2
(READ_WIDTH == 1) ? 12 : // 4096x1
(READ_WIDTH == 20) ? 8 : // 256x20
(READ_WIDTH == 10) ? 9 : // 512x10
(READ_WIDTH == 5) ? 10 : -1, // 1024x5
localparam WRITE_ADDR_WIDTH =
(WRITE_WIDTH == 16) ? 8 : // 256x16
(WRITE_WIDTH == 8) ? 9 : // 512x8
(WRITE_WIDTH == 4) ? 10 : // 1024x4
(WRITE_WIDTH == 2) ? 11 : // 2048x2
(WRITE_WIDTH == 1) ? 12 : // 4096x1
(WRITE_WIDTH == 20) ? 8 : // 256x20
(WRITE_WIDTH == 10) ? 9 : // 512x10
(WRITE_WIDTH == 5) ? 10 : -1 // 1024x5
)
(
input [WRITE_WIDTH-1:0] WDATA, input [WRITE_WIDTH-1:0] WDATA,
input [WRITE_ADDR_WIDTH-1:0] WADDR, input [WRITE_ADDR_WIDTH-1:0] WADDR,
input WE, input WE,
@ -126,8 +150,6 @@ module EFX_RAM_5K(
(* clkbuf_sink *) (* clkbuf_sink *)
input RCLK input RCLK
); );
parameter READ_WIDTH = 20;
parameter WRITE_WIDTH = 20;
parameter OUTPUT_REG = 1'b0; parameter OUTPUT_REG = 1'b0;
parameter RCLK_POLARITY = 1'b1; parameter RCLK_POLARITY = 1'b1;
parameter RE_POLARITY = 1'b1; parameter RE_POLARITY = 1'b1;
@ -155,25 +177,4 @@ module EFX_RAM_5K(
parameter INIT_11 = 256'h0000000000000000000000000000000000000000000000000000000000000000; parameter INIT_11 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_12 = 256'h0000000000000000000000000000000000000000000000000000000000000000; parameter INIT_12 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_13 = 256'h0000000000000000000000000000000000000000000000000000000000000000; parameter INIT_13 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
localparam READ_ADDR_WIDTH =
(READ_WIDTH == 16) ? 8 : // 256x16
(READ_WIDTH == 8) ? 9 : // 512x8
(READ_WIDTH == 4) ? 10 : // 1024x4
(READ_WIDTH == 2) ? 11 : // 2048x2
(READ_WIDTH == 1) ? 12 : // 4096x1
(READ_WIDTH == 20) ? 8 : // 256x20
(READ_WIDTH == 10) ? 9 : // 512x10
(READ_WIDTH == 5) ? 10 : -1; // 1024x5
localparam WRITE_ADDR_WIDTH =
(WRITE_WIDTH == 16) ? 8 : // 256x16
(WRITE_WIDTH == 8) ? 9 : // 512x8
(WRITE_WIDTH == 4) ? 10 : // 1024x4
(WRITE_WIDTH == 2) ? 11 : // 2048x2
(WRITE_WIDTH == 1) ? 12 : // 4096x1
(WRITE_WIDTH == 20) ? 8 : // 256x20
(WRITE_WIDTH == 10) ? 9 : // 512x10
(WRITE_WIDTH == 5) ? 10 : -1; // 1024x5
endmodule endmodule

View file

@ -16,7 +16,7 @@ for arch in ../../techlibs/*; do
done done
else else
echo -n "Test $path ->" echo -n "Test $path ->"
iverilog -t null -I$arch $path iverilog -t null -I$arch -g2005-sv $path
echo " ok" echo " ok"
fi fi
done done

View file

@ -25,6 +25,11 @@ T rand_int(T min = std::numeric_limits<T>::min(), T max = std::numeric_limits<T>
return dist(generator); return dist(generator);
} }
int64_t sext(size_t bits, uint64_t value)
{
return (int64_t)(value << (64 - bits)) >> (64 - bits);
}
struct BinaryOperationBase struct BinaryOperationBase
{ {
void tweak_input(uint64_t &a, uint64_t &b) {} void tweak_input(uint64_t &a, uint64_t &b) {}
@ -246,6 +251,106 @@ struct CtlzTest
} }
} ctlz; } ctlz;
struct UdivTest : BinaryOperationBase
{
UdivTest()
{
std::printf("Randomized tests for value::udivmod (div):\n");
test_binary_operation(*this);
}
uint64_t reference_impl(size_t bits, uint64_t a, uint64_t b)
{
return a / b;
}
template<size_t Bits>
cxxrtl::value<Bits> testing_impl(cxxrtl::value<Bits> a, cxxrtl::value<Bits> b)
{
return std::get<0>(a.udivmod(b));
}
void tweak_input(uint64_t &, uint64_t &b)
{
if (b == 0) b = 1; // Avoid divide by zero
}
} udiv;
struct UmodTest : BinaryOperationBase
{
UmodTest()
{
std::printf("Randomized tests for value::udivmod (mod):\n");
test_binary_operation(*this);
}
uint64_t reference_impl(size_t bits, uint64_t a, uint64_t b)
{
return a % b;
}
template<size_t Bits>
cxxrtl::value<Bits> testing_impl(cxxrtl::value<Bits> a, cxxrtl::value<Bits> b)
{
return std::get<1>(a.udivmod(b));
}
void tweak_input(uint64_t &, uint64_t &b)
{
if (b == 0) b = 1; // Avoid divide by zero
}
} umod;
struct SdivTest : BinaryOperationBase
{
SdivTest()
{
std::printf("Randomized tests for value::sdivmod (div):\n");
test_binary_operation(*this);
}
uint64_t reference_impl(size_t bits, uint64_t a, uint64_t b)
{
return (uint64_t)(sext(bits, a) / sext(bits, b));
}
template<size_t Bits>
cxxrtl::value<Bits> testing_impl(cxxrtl::value<Bits> a, cxxrtl::value<Bits> b)
{
return std::get<0>(a.sdivmod(b));
}
void tweak_input(uint64_t &, uint64_t &b)
{
if (b == 0) b = 1; // Avoid divide by zero
}
} sdiv;
struct SmodTest : BinaryOperationBase
{
SmodTest()
{
std::printf("Randomized tests for value::sdivmod (mod):\n");
test_binary_operation(*this);
}
uint64_t reference_impl(size_t bits, uint64_t a, uint64_t b)
{
return (uint64_t)(sext(bits, a) % sext(bits, b));
}
template<size_t Bits>
cxxrtl::value<Bits> testing_impl(cxxrtl::value<Bits> a, cxxrtl::value<Bits> b)
{
return std::get<1>(a.sdivmod(b));
}
void tweak_input(uint64_t &, uint64_t &b)
{
if (b == 0) b = 1; // Avoid divide by zero
}
} smod;
int main() int main()
{ {
} }

View file

@ -1,4 +1,14 @@
module RAM_9b1B ( module RAM_9b1B
#(
parameter INIT = 0,
parameter OPTION_INIT = "UNDEFINED",
parameter PORT_R_WIDTH = 9,
parameter PORT_W_WIDTH = 9,
parameter PORT_R_CLK_POL = 0,
parameter PORT_W_CLK_POL = 0,
parameter PORT_W_WR_EN_WIDTH = 1
)
(
input PORT_R_CLK, input PORT_R_CLK,
input [6:0] PORT_R_ADDR, input [6:0] PORT_R_ADDR,
output reg [PORT_R_WIDTH-1:0] PORT_R_RD_DATA, output reg [PORT_R_WIDTH-1:0] PORT_R_RD_DATA,
@ -8,14 +18,6 @@ module RAM_9b1B (
input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA
); );
parameter INIT = 0;
parameter OPTION_INIT = "UNDEFINED";
parameter PORT_R_WIDTH = 9;
parameter PORT_W_WIDTH = 9;
parameter PORT_R_CLK_POL = 0;
parameter PORT_W_CLK_POL = 0;
parameter PORT_W_WR_EN_WIDTH = 1;
reg [8:0] mem [0:15]; reg [8:0] mem [0:15];
integer i; integer i;

View file

@ -1,4 +1,11 @@
module RAM_WREN ( module RAM_WREN #(
parameter ABITS=4,
parameter WIDTH=8,
parameter PORT_A_WR_EN_WIDTH=1,
parameter PORT_A_WR_BE_WIDTH=0,
parameter OPTION_BYTESIZE=WIDTH,
parameter WB=OPTION_BYTESIZE
)(
input PORT_A_CLK, input PORT_A_CLK,
input [ABITS-1:0] PORT_A_ADDR, input [ABITS-1:0] PORT_A_ADDR,
input [WIDTH-1:0] PORT_A_WR_DATA, input [WIDTH-1:0] PORT_A_WR_DATA,
@ -7,13 +14,6 @@ module RAM_WREN (
input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE
); );
parameter ABITS=4;
parameter WIDTH=8;
parameter PORT_A_WR_EN_WIDTH=1;
parameter PORT_A_WR_BE_WIDTH=0;
parameter OPTION_BYTESIZE=WIDTH;
parameter WB=OPTION_BYTESIZE;
reg [WIDTH-1:0] mem [0:2**ABITS-1]; reg [WIDTH-1:0] mem [0:2**ABITS-1];
integer i; integer i;

View file

@ -2,13 +2,15 @@
// expect-rd-ports 1 // expect-rd-ports 1
// expect-rd-clk \clk // expect-rd-clk \clk
module ram2 (input clk, module ram2 #(
parameter SIZE = 5 // Address size
) (input clk,
input sel, input sel,
input we, input we,
input [SIZE-1:0] adr, input [SIZE-1:0] adr,
input [63:0] dat_i, input [63:0] dat_i,
output reg [63:0] dat_o); output reg [63:0] dat_o);
parameter SIZE = 5; // Address size
reg [63:0] mem [0:(1 << SIZE)-1]; reg [63:0] mem [0:(1 << SIZE)-1];
integer i; integer i;

View file

@ -1,2 +1,3 @@
*.log *.log
*.out *.out
*.err

View file

@ -0,0 +1 @@
test_cell -s 1711533949 -n 10 -map +/techmap.v -map +/choices/kogge-stone.v $lcu

View file

@ -1,6 +1,10 @@
/*.log /*.log
/*.out /*.out
/*.err
/run-test.mk /run-test.mk
/const_arst.v /const_arst.v
/const_sr.v /const_sr.v
/doubleslash.v /doubleslash.v
/roundtrip_proc_1.v
/roundtrip_proc_2.v
/assign_to_reg.v

View file

@ -0,0 +1,22 @@
# https://github.com/yosyshq/yosys/issues/2035
read_ilang <<END
module \top
wire width 1 input 0 \halfbrite
wire width 2 output 1 \r_on
process $1
assign \r_on [1:0] 2'00
assign \r_on [1:0] 2'11
switch \halfbrite [0]
case 1'1
assign \r_on [1] 1'0
end
end
end
END
proc_prune
write_verilog assign_to_reg.v
design -reset
logger -expect-no-warnings
read_verilog assign_to_reg.v