mirror of
https://github.com/YosysHQ/yosys
synced 2025-06-23 06:13:41 +00:00
Merge branch 'YosysHQ:main' into master
This commit is contained in:
commit
e3f633fae6
29 changed files with 1110 additions and 551 deletions
3
Makefile
3
Makefile
|
@ -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))
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
306
kernel/fmt.cc
306
kernel/fmt.cc
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
25
kernel/fmt.h
25
kernel/fmt.h
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
346
kernel/yosys.h
346
kernel/yosys.h
|
@ -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
379
kernel/yosys_common.h
Normal 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
|
|
@ -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);
|
||||||
|
|
|
@ -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))
|
||||||
|
|
54
techlibs/common/choices/kogge-stone.v
Normal file
54
techlibs/common/choices/kogge-stone.v
Normal 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
|
|
@ -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;
|
||||||
|
|
|
@ -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 *)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
1
tests/simple/.gitignore
vendored
1
tests/simple/.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
*.log
|
*.log
|
||||||
*.out
|
*.out
|
||||||
|
*.err
|
||||||
|
|
1
tests/techmap/kogge-stone.ys
Normal file
1
tests/techmap/kogge-stone.ys
Normal file
|
@ -0,0 +1 @@
|
||||||
|
test_cell -s 1711533949 -n 10 -map +/techmap.v -map +/choices/kogge-stone.v $lcu
|
4
tests/verilog/.gitignore
vendored
4
tests/verilog/.gitignore
vendored
|
@ -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
|
||||||
|
|
22
tests/verilog/assign_to_reg.ys
Normal file
22
tests/verilog/assign_to_reg.ys
Normal 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
|
Loading…
Add table
Add a link
Reference in a new issue