3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-23 00:55:32 +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
YOSYS_VER := 0.39+147
YOSYS_VER := 0.39+165
# Note: We arrange for .gitcommit to contain the (short) commit hash in
# 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/utils.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,libs/ezsat/ezsat.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> dividend = sext<Bits + 1>();
value<Bits + 1> divisor = other.template sext<Bits + 1>();
if (dividend.is_neg()) dividend = dividend.neg();
if (divisor.is_neg()) divisor = divisor.neg();
if (is_neg()) dividend = dividend.neg();
if (other.is_neg()) divisor = divisor.neg();
std::tie(quotient, remainder) = dividend.udivmod(divisor);
if (dividend.is_neg() != divisor.is_neg()) quotient = quotient.neg();
if (dividend.is_neg()) remainder = remainder.neg();
if (is_neg() != other.is_neg()) quotient = quotient.neg();
if (is_neg()) remainder = remainder.neg();
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.
struct fmt_part {
enum {
STRING = 0,
LITERAL = 0,
INTEGER = 1,
CHARACTER = 2,
VLOG_TIME = 3,
STRING = 2,
UNICHAR = 3,
VLOG_TIME = 4,
} type;
// STRING type
// LITERAL type
std::string str;
// INTEGER/CHARACTER types
// INTEGER/STRING/UNICHAR types
// + value<Bits> val;
// INTEGER/CHARACTER/VLOG_TIME types
// INTEGER/STRING/VLOG_TIME types
enum {
RIGHT = 0,
LEFT = 1,
NUMERIC = 2,
} justify; // = RIGHT;
char padding; // = '\0';
size_t width; // = 0;
@ -1033,7 +1035,14 @@ struct fmt_part {
// INTEGER type
unsigned base; // = 10;
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
bool realtime; // = false;
@ -1049,11 +1058,12 @@ struct fmt_part {
// We might want to replace some of these bit() calls with direct
// chunk access if it turns out to be slow enough to matter.
std::string buf;
std::string prefix;
switch (type) {
case STRING:
case LITERAL:
return str;
case CHARACTER: {
case STRING: {
buf.reserve(Bits/8);
for (int i = 0; i < Bits; i += 8) {
char ch = 0;
@ -1067,35 +1077,76 @@ struct fmt_part {
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: {
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) {
width = 0;
val_width = 1;
for (size_t index = 0; index < Bits; index++)
if (val.bit(index))
width = index + 1;
val_width = index + 1;
}
if (base == 2) {
for (size_t i = width; i > 0; i--)
buf += (val.bit(i - 1) ? '1' : '0');
if (show_base)
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) {
if (show_base)
prefix += (base == 16) ? (hex_upper ? "0X" : "0x") : "0o";
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);
if (step == 4)
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) {
bool negative = signed_ && val.is_neg();
if (negative)
val = val.neg();
if (show_base)
prefix += "0d";
if (val.is_zero())
buf += '0';
value<(Bits > 4 ? Bits : 4)> xval = val.template zext<(Bits > 4 ? Bits : 4)>();
size_t index = 0;
while (!xval.is_zero()) {
if (group && index > 0 && index % 3 == 0)
buf += '_';
value<(Bits > 4 ? Bits : 4)> quotient, remainder;
if (Bits >= 4)
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);
buf += '0' + remainder.template trunc<4>().template get<uint8_t>();
xval = quotient;
index++;
}
if (negative || plus)
buf += negative ? '-' : '+';
std::reverse(buf.begin(), buf.end());
} 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;
}
@ -1123,17 +1181,29 @@ struct fmt_part {
std::string str;
assert(width == 0 || padding != '\0');
if (justify == RIGHT && buf.size() < width) {
size_t pad_width = width - buf.size();
if (padding == '0' && (buf.front() == '+' || buf.front() == '-')) {
str += buf.front();
buf.erase(0, 1);
}
str += std::string(pad_width, padding);
if (prefix.size() + buf.size() < width) {
size_t pad_width = width - prefix.size() - buf.size();
switch (justify) {
case LEFT:
str += prefix;
str += buf;
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;
}
};

View file

@ -1070,7 +1070,7 @@ bool dump_cell_expr(std::ostream &f, std::string indent, RTLIL::Cell *cell)
f << stringf(";\n");
return true;
}
if (cell->type == ID($_BUF_)) {
f << stringf("%s" "assign ", indent.c_str());
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)
{
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;
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);
f << stringf(" = ");
dump_sigspec(f, right.extract(offset, GetSize(chunk)));
f << stringf(";\n");
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];
}
if (!module->processes.empty())
log_warning("Module %s contains unmapped RTLIL processes. RTLIL processes\n"
"can't always be mapped directly to Verilog always blocks. Unintended\n"
"changes in simulation behavior are possible! Use \"proc\" to convert\n"
"processes to logic networks and registers.\n", log_id(module));
bool has_sync_rules = false;
for (auto process : module->processes)
if (!process.second->syncs.empty())
has_sync_rules = true;
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");
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.
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
~~~~~~~~~~~~~
@ -1152,4 +1194,4 @@ file via ABC using the abc pass.
.. 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.parse_verilog(args, /*sformat_like=*/false, default_base, /*task_name=*/ast->str, current_module->name);
if (ast->str.substr(0, 8) == "$display")
fmt.append_string("\n");
fmt.append_literal("\n");
fmt.emit_rtlil(cell);
} 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());

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
Fmt fmt = processFormat(stage, /*sformat_like=*/false, default_base, /*first_arg_at=*/0, /*may_fail=*/true);
if (str.substr(0, 8) == "$display")
fmt.append_string("\n");
fmt.append_literal("\n");
log("%s", fmt.render().c_str());
}

View file

@ -19,7 +19,7 @@ Formatting of code
blank lines.
- 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

View file

@ -22,9 +22,9 @@
USING_YOSYS_NAMESPACE
void Fmt::append_string(const std::string &str) {
void Fmt::append_literal(const std::string &str) {
FmtPart part = {};
part.type = FmtPart::STRING;
part.type = FmtPart::LITERAL;
part.str = str;
parts.push_back(part);
}
@ -42,11 +42,11 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) {
} else if (fmt.substr(i, 2) == "{{") {
part.str += '{';
++i;
} else if (fmt[i] == '}')
} else if (fmt[i] == '}') {
log_assert(false && "Unexpected '}' in format string");
else if (fmt[i] == '{') {
} else if (fmt[i] == '{') {
if (!part.str.empty()) {
part.type = FmtPart::STRING;
part.type = FmtPart::LITERAL;
parts.push_back(part);
part = {};
}
@ -74,19 +74,24 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) {
part.sig = args.extract(0, arg_size);
args.remove(0, arg_size);
if (fmt[i] == 'U') {
part.type = FmtPart::UNICHAR;
++i;
goto success;
}
if (fmt[i] == '>')
part.justify = FmtPart::RIGHT;
else if (fmt[i] == '<')
part.justify = FmtPart::LEFT;
else if (fmt[i] == '=')
part.justify = FmtPart::NUMERIC;
else
log_assert(false && "Unexpected justification in format substitution");
if (++i == fmt.size())
log_assert(false && "Unexpected end in format substitution");
if (fmt[i] == '0' || fmt[i] == ' ')
part.padding = fmt[i];
else
log_assert(false && "Unexpected padding in format substitution");
part.padding = fmt[i];
if (++i == fmt.size())
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') {
part.type = FmtPart::INTEGER;
part.base = 16;
} else if (fmt[i] == 'H') {
part.type = FmtPart::INTEGER;
part.base = 16;
part.hex_upper = true;
} else if (fmt[i] == 'c') {
part.type = FmtPart::CHARACTER;
part.type = FmtPart::STRING;
} else if (fmt[i] == 't') {
part.type = FmtPart::VLOG_TIME;
} 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");
if (part.type == FmtPart::INTEGER) {
if (fmt[i] == '+') {
part.plus = true;
if (fmt[i] == '-') {
part.sign = FmtPart::MINUS;
if (++i == fmt.size())
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')
@ -140,6 +168,7 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) {
log_assert(false && "Unexpected end in format substitution");
}
success:
if (fmt[i] != '}')
log_assert(false && "Expected '}' after format substitution");
@ -150,7 +179,7 @@ void Fmt::parse_rtlil(const RTLIL::Cell *cell) {
}
}
if (!part.str.empty()) {
part.type = FmtPart::STRING;
part.type = FmtPart::LITERAL;
parts.push_back(part);
}
}
@ -161,7 +190,7 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const {
for (auto &part : parts) {
switch (part.type) {
case FmtPart::STRING:
case FmtPart::LITERAL:
for (char c : part.str) {
if (c == '{')
fmt += "{{";
@ -172,10 +201,15 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const {
}
break;
case FmtPart::UNICHAR:
log_assert(part.sig.size() <= 32);
fmt += "{U}";
break;
case FmtPart::VLOG_TIME:
log_assert(part.sig.size() == 0);
YS_FALLTHROUGH
case FmtPart::CHARACTER:
case FmtPart::STRING:
log_assert(part.sig.size() % 8 == 0);
YS_FALLTHROUGH
case FmtPart::INTEGER:
@ -187,6 +221,8 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const {
fmt += '>';
else if (part.justify == FmtPart::LEFT)
fmt += '<';
else if (part.justify == FmtPart::NUMERIC)
fmt += '=';
else log_abort();
log_assert(part.width == 0 || part.padding != '\0');
fmt += part.padding != '\0' ? part.padding : ' ';
@ -197,13 +233,18 @@ void Fmt::emit_rtlil(RTLIL::Cell *cell) const {
case 2: fmt += 'b'; break;
case 8: fmt += 'o'; break;
case 10: fmt += 'd'; break;
case 16: fmt += 'h'; break;
case 16: fmt += part.hex_upper ? 'H' : 'h'; break;
default: log_abort();
}
if (part.plus)
fmt += '+';
switch (part.sign) {
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';
} else if (part.type == FmtPart::CHARACTER) {
} else if (part.type == FmtPart::STRING) {
fmt += 'c';
} else if (part.type == FmtPart::VLOG_TIME) {
if (part.realtime)
@ -299,12 +340,12 @@ void Fmt::apply_verilog_automatic_sizing_and_add(FmtPart &part)
part.width = places;
if (part.justify == FmtPart::RIGHT) {
append_string(gap);
append_literal(gap);
parts.push_back(part);
} else {
part.justify = FmtPart::RIGHT;
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();
} else {
if (!part.str.empty()) {
part.type = FmtPart::STRING;
part.type = FmtPart::LITERAL;
parts.push_back(part);
part = {};
}
@ -375,7 +416,7 @@ void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_lik
part.justify = FmtPart::LEFT;
} else if (fmt[i] == '+') {
// always show sign; not in IEEE 1800-2017 or verilator but iverilog has it
part.plus = true;
part.sign = FmtPart::PLUS_MINUS;
} else break;
}
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.base = 16;
} else if (fmt[i] == 'c' || fmt[i] == 'C') {
part.type = FmtPart::CHARACTER;
part.type = FmtPart::STRING;
part.sig.extend_u0(8);
// %10c and %010c not fully defined in IEEE 1800-2017 and do different things in iverilog
} else if (fmt[i] == 's' || fmt[i] == 'S') {
part.type = FmtPart::CHARACTER;
part.type = FmtPart::STRING;
if ((part.sig.size() % 8) != 0)
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
@ -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);
}
if (part.padding == '\0')
part.padding = (has_leading_zero && part.justify == FmtPart::RIGHT) ? '0' : ' ';
if (part.padding == '\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);
if (part.base != 10)
part.signed_ = false;
if (part.type == FmtPart::INTEGER && !has_leading_zero)
apply_verilog_automatic_sizing_and_add(part);
else
@ -449,12 +498,12 @@ void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_lik
}
}
if (!part.str.empty()) {
part.type = FmtPart::STRING;
part.type = FmtPart::LITERAL;
parts.push_back(part);
}
} else {
FmtPart part = {};
part.type = FmtPart::STRING;
part.type = FmtPart::LITERAL;
part.str = arg->str;
parts.push_back(part);
}
@ -474,7 +523,7 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
for (auto &part : parts) {
switch (part.type) {
case FmtPart::STRING:
case FmtPart::LITERAL:
for (char c : part.str) {
if (c == '%')
fmt.str += "%%";
@ -491,14 +540,13 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
args.push_back(arg);
fmt.str += '%';
if (part.plus)
fmt.str += '+';
if (part.sign == FmtPart::PLUS_MINUS || part.sign == FmtPart::SPACE_MINUS)
fmt.str += '+'; // treat space/minus as plus/minus
if (part.justify == FmtPart::LEFT)
fmt.str += '-';
if (part.width == 0) {
fmt.str += '0';
} else if (part.width > 0) {
log_assert(part.padding == ' ' || part.padding == '0');
if (part.base != 10 || part.padding == '0')
fmt.str += '0';
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 8: fmt.str += 'o'; 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();
}
break;
}
case FmtPart::CHARACTER: {
case FmtPart::STRING: {
VerilogFmtArg arg;
arg.type = VerilogFmtArg::INTEGER;
arg.sig = part.sig;
@ -524,7 +572,6 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
fmt.str += '-';
if (part.sig.size() == 8) {
if (part.width > 0) {
log_assert(part.padding == '0' || part.padding == ' ');
if (part.padding == '0')
fmt.str += part.padding;
fmt.str += std::to_string(part.width);
@ -533,7 +580,6 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
} else {
log_assert(part.sig.size() % 8 == 0);
if (part.width > 0) {
log_assert(part.padding == ' '); // no zero padding
fmt.str += std::to_string(part.width);
}
fmt.str += 's';
@ -541,6 +587,16 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
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: {
VerilogFmtArg arg;
arg.type = VerilogFmtArg::TIME;
@ -549,11 +605,11 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
args.push_back(arg);
fmt.str += '%';
if (part.plus)
log_assert(part.sign == FmtPart::MINUS || part.sign == FmtPart::PLUS_MINUS);
if (part.sign == FmtPart::PLUS_MINUS)
fmt.str += '+';
if (part.justify == FmtPart::LEFT)
fmt.str += '-';
log_assert(part.padding == ' ' || part.padding == '0');
if (part.padding == '0' && part.width > 0)
fmt.str += '0';
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 << "fmt_part::";
switch (part.type) {
case FmtPart::STRING: os << "STRING"; break;
case FmtPart::LITERAL: os << "LITERAL"; 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;
}
os << ", ";
os << escape_cxx_string(part.str) << ", ";
os << "fmt_part::";
switch (part.justify) {
case FmtPart::LEFT: os << "LEFT"; break;
case FmtPart::RIGHT: os << "RIGHT"; break;
case FmtPart::LEFT: os << "LEFT"; break;
case FmtPart::RIGHT: os << "RIGHT"; break;
case FmtPart::NUMERIC: os << "NUMERIC"; break;
}
os << ", ";
os << "(char)" << (int)part.padding << ", ";
os << part.width << ", ";
os << part.base << ", ";
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 << " }.render(";
emit_sig(part.sig);
@ -631,19 +698,62 @@ std::string Fmt::render() const
for (auto &part : parts) {
switch (part.type) {
case FmtPart::STRING:
case FmtPart::LITERAL:
str += part.str;
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::CHARACTER:
case FmtPart::STRING:
case FmtPart::VLOG_TIME: {
std::string buf;
std::string prefix;
if (part.type == FmtPart::INTEGER) {
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) {
size_t minimum_size = 0;
size_t minimum_size = 1;
for (size_t index = 0; index < (size_t)value.size(); index++)
if (value[index] != State::S0)
minimum_size = index + 1;
@ -651,10 +761,28 @@ std::string Fmt::render() const
}
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) {
if (part.show_base)
prefix += (part.base == 16) ? (part.hex_upper ? "0X" : "0x") : "0o";
size_t step = (part.base == 16) ? 4 : 3;
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));
bool has_x = false, all_x = true, has_z = false, all_z = true;
for (State bit : subvalue) {
@ -676,21 +804,11 @@ std::string Fmt::render() const
else if (has_z)
buf += 'Z';
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) {
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 (part.show_base)
prefix += "0d";
if (all_x)
buf += 'x';
else if (all_z)
@ -700,25 +818,29 @@ std::string Fmt::render() const
else if (has_z)
buf += 'Z';
else {
bool negative = part.signed_ && value[value.size() - 1];
RTLIL::Const absvalue;
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())
log_assert(value.is_fully_def());
if (value.is_fully_zero())
buf += '0';
while (!absvalue.is_fully_zero()) {
buf += '0' + RTLIL::const_mod(absvalue, 10, false, false, 4).as_int();
absvalue = RTLIL::const_div(absvalue, 10, false, false, absvalue.size());
size_t index = 0;
while (!value.is_fully_zero()) {
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 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();
} else if (part.type == FmtPart::VLOG_TIME) {
// 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');
if (part.justify == FmtPart::RIGHT && buf.size() < part.width) {
size_t pad_width = part.width - buf.size();
if (part.padding == '0' && (!buf.empty() && (buf.front() == '+' || buf.front() == '-'))) {
str += buf.front();
buf.erase(0, 1);
if (prefix.size() + buf.size() < part.width) {
size_t pad_width = part.width - prefix.size() - buf.size();
switch (part.justify) {
case FmtPart::LEFT:
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;
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -39,323 +39,7 @@
#ifndef YOSYS_H
#define YOSYS_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;
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/yosys_common.h"
#include "kernel/log.h"
#include "kernel/rtlil.h"
@ -363,14 +47,6 @@ YOSYS_NAMESPACE_END
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();
#ifdef WITH_PYTHON
@ -385,26 +61,6 @@ Tcl_Interp *yosys_get_tcl_interp();
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();
std::string proc_self_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
{
#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)
@ -90,6 +91,11 @@ struct statdata_t
for (auto wire : mod->selected_wires())
{
if (wire->port_input || wire->port_output) {
num_ports++;
num_port_bits += wire->width;
}
if (wire->name.isPublic()) {
num_pub_wires++;
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 public wires: %6u\n", num_pub_wires);
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 memory bits: %6u\n", num_memory_bits);
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_pub_wires\": %u,\n", num_pub_wires);
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_memory_bits\": %u,\n", num_memory_bits);
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_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_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_memory_bits", data.num_memory_bits);
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/cmp2lcu.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
// --------------------------------------------------------
// |---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);
parameter A_WIDTH = 0;
parameter B_WIDTH = 0;
parameter Y_WIDTH = 0;
// CONFIG determines the layout of A, as explained below
parameter CONFIG = 4'b0000;
parameter CONFIG_WIDTH = 4;
input [A_WIDTH-1:0] A;
input [B_WIDTH-1:0] B;
output reg [Y_WIDTH-1:0] Y;
// In the terms used for this cell, there's mixed meanings for the term "port". To disambiguate:
// A cell port is for example the A input (it is constructed in C++ as cell->setPort(ID::A, ...))
// 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..
function integer my_clog2;
@ -929,10 +945,42 @@ function integer my_clog2;
end
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;
// Number of multiplier ports
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;
// 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;
input [CONFIG_WIDTH-1:0] cfg;
integer i, cursor;

View file

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

View file

@ -113,7 +113,31 @@ module EFX_GBUFCE(
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_ADDR_WIDTH-1:0] WADDR,
input WE,
@ -126,8 +150,6 @@ module EFX_RAM_5K(
(* clkbuf_sink *)
input RCLK
);
parameter READ_WIDTH = 20;
parameter WRITE_WIDTH = 20;
parameter OUTPUT_REG = 1'b0;
parameter RCLK_POLARITY = 1'b1;
parameter RE_POLARITY = 1'b1;
@ -155,25 +177,4 @@ module EFX_RAM_5K(
parameter INIT_11 = 256'h0000000000000000000000000000000000000000000000000000000000000000;
parameter INIT_12 = 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

View file

@ -16,7 +16,7 @@ for arch in ../../techlibs/*; do
done
else
echo -n "Test $path ->"
iverilog -t null -I$arch $path
iverilog -t null -I$arch -g2005-sv $path
echo " ok"
fi
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);
}
int64_t sext(size_t bits, uint64_t value)
{
return (int64_t)(value << (64 - bits)) >> (64 - bits);
}
struct BinaryOperationBase
{
void tweak_input(uint64_t &a, uint64_t &b) {}
@ -246,6 +251,106 @@ struct CtlzTest
}
} 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()
{
}

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 [6:0] PORT_R_ADDR,
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
);
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];
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 [ABITS-1:0] PORT_A_ADDR,
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
);
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];
integer i;

View file

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

View file

@ -1,2 +1,3 @@
*.log
*.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
/*.out
/*.err
/run-test.mk
/const_arst.v
/const_sr.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