mirror of https://github.com/YosysHQ/yosys.git
cxxrtl: first pass of $print impl
This commit is contained in:
parent
202c3776e2
commit
095b093f4a
|
@ -518,6 +518,14 @@ struct value : public expr_base<value<Bits>> {
|
|||
return count;
|
||||
}
|
||||
|
||||
size_t chunks_used() const {
|
||||
for (size_t n = chunks; n > 0; n--) {
|
||||
if (data[n - 1] != 0)
|
||||
return n;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<bool Invert, bool CarryIn>
|
||||
std::pair<value<Bits>, bool /*CarryOut*/> alu(const value<Bits> &other) const {
|
||||
value<Bits> result;
|
||||
|
@ -575,6 +583,140 @@ struct value : public expr_base<value<Bits>> {
|
|||
result.data[result.chunks - 1] &= result.msb_mask;
|
||||
return result;
|
||||
}
|
||||
|
||||
// parallel to BigUnsigned::divideWithRemainder; quotient is stored in q,
|
||||
// *this is left with the remainder. See that function for commentary describing
|
||||
// how/why this works.
|
||||
void divideWithRemainder(const value<Bits> &b, value<Bits> &q) {
|
||||
assert(this != &q);
|
||||
|
||||
if (this == &b || &q == &b) {
|
||||
value<Bits> tmpB(b);
|
||||
divideWithRemainder(tmpB, q);
|
||||
return;
|
||||
}
|
||||
|
||||
q = value<Bits> {0u};
|
||||
|
||||
size_t blen = b.chunks_used();
|
||||
if (blen == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t len = chunks_used();
|
||||
if (len < blen) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t i, j, k;
|
||||
size_t i2;
|
||||
chunk_t temp;
|
||||
bool borrowIn, borrowOut;
|
||||
|
||||
size_t origLen = len;
|
||||
len++;
|
||||
chunk::type blk[len];
|
||||
std::copy(data, data + origLen, blk);
|
||||
blk[origLen] = 0;
|
||||
chunk::type subtractBuf[len];
|
||||
std::fill(subtractBuf, subtractBuf + len, 0);
|
||||
|
||||
size_t qlen = origLen - blen + 1;
|
||||
|
||||
i = qlen;
|
||||
while (i > 0) {
|
||||
i--;
|
||||
i2 = chunk::bits;
|
||||
while (i2 > 0) {
|
||||
i2--;
|
||||
for (j = 0, k = i, borrowIn = false; j <= blen; j++, k++) {
|
||||
temp = blk[k] - getShiftedBlock(b, j, i2);
|
||||
borrowOut = (temp > blk[k]);
|
||||
if (borrowIn) {
|
||||
borrowOut |= (temp == 0);
|
||||
temp--;
|
||||
}
|
||||
subtractBuf[k] = temp;
|
||||
borrowIn = borrowOut;
|
||||
}
|
||||
for (; k < origLen && borrowIn; k++) {
|
||||
borrowIn = (blk[k] == 0);
|
||||
subtractBuf[k] = blk[k] - 1;
|
||||
}
|
||||
if (!borrowIn) {
|
||||
q.data[i] |= (chunk::type(1) << i2);
|
||||
while (k > i) {
|
||||
k--;
|
||||
blk[k] = subtractBuf[k];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::copy(blk, blk + origLen, data);
|
||||
std::fill(blk + origLen, blk + chunks, 0);
|
||||
}
|
||||
|
||||
static chunk::type getShiftedBlock(const value<Bits> &num, size_t x, size_t y) {
|
||||
chunk::type part1 = (x == 0 || y == 0) ? 0 : (num.data[x - 1] >> (chunk::bits - y));
|
||||
chunk::type part2 = (x == num.chunks) ? 0 : (num.data[x] << y);
|
||||
return part1 | part2;
|
||||
}
|
||||
|
||||
// parallel to BigInteger::divideWithRemainder; quotient is stored in q,
|
||||
// *this is left with the remainder. See that function for commentary describing
|
||||
// how/why this works.
|
||||
void signedDivideWithRemainder(const value<Bits> &b, value<Bits> &q) {
|
||||
assert(this != &q);
|
||||
|
||||
if (this == &b || &q == &b) {
|
||||
value<Bits> tmpB(b);
|
||||
signedDivideWithRemainder(tmpB, q);
|
||||
return;
|
||||
}
|
||||
|
||||
if (b.is_zero()) {
|
||||
q = value<Bits>{0u};
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_zero()) {
|
||||
q = value<Bits>{0u};
|
||||
return;
|
||||
}
|
||||
|
||||
// BigInteger has a separate 'mag' to its sign. We don't, so the lazy
|
||||
// approach is to improvise said.
|
||||
auto mag = *this;
|
||||
bool neg = mag.is_neg();
|
||||
if (neg)
|
||||
mag = mag.neg();
|
||||
|
||||
auto bmag = b;
|
||||
bool bneg = bmag.is_neg();
|
||||
if (bneg)
|
||||
bmag = bmag.neg();
|
||||
|
||||
bool qneg = false;
|
||||
if (neg != b.is_neg()) {
|
||||
qneg = true;
|
||||
mag = mag.sub(value<Bits>{1u});
|
||||
}
|
||||
|
||||
mag.divideWithRemainder(bmag, q);
|
||||
|
||||
if (neg != bneg) {
|
||||
q = q.add(value<Bits>{1u});
|
||||
mag = bmag.sub(mag);
|
||||
mag = mag.sub(value<Bits>{1u});
|
||||
}
|
||||
|
||||
neg = bneg;
|
||||
|
||||
*this = neg ? mag.neg() : mag;
|
||||
if (qneg)
|
||||
q = q.neg();
|
||||
}
|
||||
};
|
||||
|
||||
// Expression template for a slice, usable as lvalue or rvalue, and composable with other expression templates here.
|
||||
|
@ -707,6 +849,101 @@ std::ostream &operator<<(std::ostream &os, const value<Bits> &val) {
|
|||
return os;
|
||||
}
|
||||
|
||||
template<size_t Bits>
|
||||
struct value_formatted {
|
||||
const value<Bits> &val;
|
||||
bool character;
|
||||
bool justify_left;
|
||||
char padding;
|
||||
int width;
|
||||
int base;
|
||||
bool signed_;
|
||||
bool lzero;
|
||||
bool plus;
|
||||
|
||||
value_formatted(const value<Bits> &val, bool character, bool justify_left, char padding, int width, int base, bool signed_, bool lzero, bool plus) :
|
||||
val(val), character(character), justify_left(justify_left), padding(padding), width(width), base(base), signed_(signed_), lzero(lzero), plus(plus) {}
|
||||
value_formatted(const value_formatted<Bits> &) = delete;
|
||||
value_formatted<Bits> &operator=(const value_formatted<Bits> &rhs) = delete;
|
||||
};
|
||||
|
||||
template<size_t Bits>
|
||||
std::ostream &operator<<(std::ostream &os, const value_formatted<Bits> &vf)
|
||||
{
|
||||
value<Bits> val = vf.val;
|
||||
|
||||
std::string buf;
|
||||
|
||||
// We might want to replace some of these bit() calls with direct
|
||||
// chunk access if it turns out to be slow enough to matter.
|
||||
|
||||
if (!vf.character) {
|
||||
size_t width = Bits;
|
||||
if (!vf.lzero && vf.base != 10) {
|
||||
width = 0;
|
||||
for (size_t index = 0; index < Bits; index++)
|
||||
if (val.bit(index))
|
||||
width = index + 1;
|
||||
}
|
||||
|
||||
if (vf.base == 2) {
|
||||
for (size_t i = width; i > 0; i--)
|
||||
buf += (val.bit(i - 1) ? '1' : '0');
|
||||
} else if (vf.base == 8 || vf.base == 16) {
|
||||
size_t step = (vf.base == 16) ? 4 : 3;
|
||||
for (size_t index = 0; index < width; index += step) {
|
||||
uint8_t value = val.bit(index) | (val.bit(index + 1) << 1) | (val.bit(index + 2) << 2);
|
||||
if (step == 4)
|
||||
value |= val.bit(index + 3) << 3;
|
||||
buf += "0123456789abcdef"[value];
|
||||
}
|
||||
std::reverse(buf.begin(), buf.end());
|
||||
} else if (vf.base == 10) {
|
||||
bool negative = vf.signed_ && val.is_neg();
|
||||
if (negative)
|
||||
val = val.neg();
|
||||
if (val.is_zero())
|
||||
buf += '0';
|
||||
// TODO: rigorously check our signed behaviour here
|
||||
while (!val.is_zero()) {
|
||||
value<Bits> quotient;
|
||||
val.signedDivideWithRemainder(value<Bits>{10u}, quotient);
|
||||
buf += '0' + val.template slice<3, 0>().val().template get<uint8_t>();
|
||||
val = quotient;
|
||||
}
|
||||
if (negative || vf.plus)
|
||||
buf += negative ? '-' : '+';
|
||||
std::reverse(buf.begin(), buf.end());
|
||||
} else assert(false);
|
||||
} else {
|
||||
buf.reserve(Bits/8);
|
||||
for (int i = 0; i < Bits; i += 8) {
|
||||
char ch = 0;
|
||||
for (int j = 0; j < 8 && i + j < int(Bits); j++)
|
||||
if (val.bit(i + j))
|
||||
ch |= 1 << j;
|
||||
if (ch != 0)
|
||||
buf.append({ch});
|
||||
}
|
||||
std::reverse(buf.begin(), buf.end());
|
||||
}
|
||||
|
||||
assert(vf.width == 0 || vf.padding != '\0');
|
||||
if (!vf.justify_left && buf.size() < vf.width) {
|
||||
size_t pad_width = vf.width - buf.size();
|
||||
if (vf.padding == '0' && (buf.front() == '+' || buf.front() == '-')) {
|
||||
os << buf.front();
|
||||
buf.erase(0, 1);
|
||||
}
|
||||
os << std::string(pad_width, vf.padding);
|
||||
}
|
||||
os << buf;
|
||||
if (vf.justify_left && buf.size() < vf.width)
|
||||
os << std::string(vf.width - buf.size(), vf.padding);
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
template<size_t Bits>
|
||||
struct wire {
|
||||
static constexpr size_t bits = Bits;
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include "kernel/celltypes.h"
|
||||
#include "kernel/mem.h"
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/fmt.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
@ -217,7 +218,7 @@ bool is_internal_cell(RTLIL::IdString type)
|
|||
|
||||
bool is_effectful_cell(RTLIL::IdString type)
|
||||
{
|
||||
return type.isPublic();
|
||||
return type.isPublic() || type == ID($print);
|
||||
}
|
||||
|
||||
bool is_cxxrtl_blackbox_cell(const RTLIL::Cell *cell)
|
||||
|
@ -1036,6 +1037,17 @@ struct CxxrtlWorker {
|
|||
f << ".val()";
|
||||
}
|
||||
|
||||
void dump_print(const RTLIL::Cell *cell)
|
||||
{
|
||||
Fmt fmt = {};
|
||||
fmt.parse_rtlil(cell);
|
||||
|
||||
// TODO: we may want to configure the output stream
|
||||
f << indent << "std::cout";
|
||||
fmt.emit_cxxrtl(f, [this](const RTLIL::SigSpec &sig) { dump_sigspec_rhs(sig); });
|
||||
f << ";\n";
|
||||
}
|
||||
|
||||
void dump_inlined_cells(const std::vector<const RTLIL::Cell*> &cells)
|
||||
{
|
||||
if (cells.empty()) {
|
||||
|
@ -1202,6 +1214,34 @@ struct CxxrtlWorker {
|
|||
f << " = ";
|
||||
dump_cell_expr(cell, for_debug);
|
||||
f << ";\n";
|
||||
// $print cell
|
||||
} else if (cell->type == ID($print)) {
|
||||
log_assert(!for_debug);
|
||||
f << indent << "if (";
|
||||
if (cell->getParam(ID::TRG_ENABLE).as_bool()) {
|
||||
f << '(';
|
||||
for (size_t i = 0; i < (size_t)cell->getParam(ID::TRG_WIDTH).as_int(); i++) {
|
||||
RTLIL::SigBit trg_bit = cell->getPort(ID::TRG)[i];
|
||||
trg_bit = sigmaps[trg_bit.wire->module](trg_bit);
|
||||
log_assert(trg_bit.wire);
|
||||
|
||||
if (i != 0)
|
||||
f << " || ";
|
||||
|
||||
if (cell->getParam(ID::TRG_POLARITY)[i] == State::S1)
|
||||
f << "posedge_";
|
||||
else
|
||||
f << "negedge_";
|
||||
f << mangle(trg_bit);
|
||||
}
|
||||
f << ") && ";
|
||||
}
|
||||
dump_sigspec_rhs(cell->getPort(ID::EN));
|
||||
f << " == value<1>{1u}) {\n";
|
||||
inc_indent();
|
||||
dump_print(cell);
|
||||
dec_indent();
|
||||
f << indent << "}\n";
|
||||
// Flip-flops
|
||||
} else if (is_ff_cell(cell->type)) {
|
||||
log_assert(!for_debug);
|
||||
|
@ -2601,6 +2641,16 @@ struct CxxrtlWorker {
|
|||
register_edge_signal(sigmap, cell->getPort(ID::CLK),
|
||||
cell->parameters[ID::CLK_POLARITY].as_bool() ? RTLIL::STp : RTLIL::STn);
|
||||
}
|
||||
|
||||
// $print cells may be triggered on posedge/negedge events.
|
||||
if (cell->type == ID($print) && cell->getParam(ID::TRG_ENABLE).as_bool()) {
|
||||
for (size_t i = 0; i < (size_t)cell->getParam(ID::TRG_WIDTH).as_int(); i++) {
|
||||
RTLIL::SigBit trg = cell->getPort(ID::TRG).extract(i, 1);
|
||||
if (is_valid_clock(trg))
|
||||
register_edge_signal(sigmap, trg,
|
||||
cell->parameters[ID::TRG_POLARITY][i] == RTLIL::S1 ? RTLIL::STp : RTLIL::STn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &mem : memories) {
|
||||
|
|
|
@ -29,7 +29,7 @@ void Fmt::append_string(const std::string &str) {
|
|||
parts.push_back(part);
|
||||
}
|
||||
|
||||
void Fmt::parse_rtlil(RTLIL::Cell *cell) {
|
||||
void Fmt::parse_rtlil(const RTLIL::Cell *cell) {
|
||||
std::string fmt = cell->getParam(ID(FORMAT)).decode_string();
|
||||
RTLIL::SigSpec args = cell->getPort(ID(ARGS));
|
||||
parts.clear();
|
||||
|
@ -465,6 +465,67 @@ std::vector<VerilogFmtArg> Fmt::emit_verilog() const
|
|||
return args;
|
||||
}
|
||||
|
||||
void Fmt::emit_cxxrtl(std::ostream &f, std::function<void(const RTLIL::SigSpec &)> emit_sig) const
|
||||
{
|
||||
for (auto &part : parts) {
|
||||
switch (part.type) {
|
||||
case FmtPart::STRING:
|
||||
f << " << \"";
|
||||
for (char c : part.str) {
|
||||
switch (c) {
|
||||
case '\\':
|
||||
YS_FALLTHROUGH
|
||||
case '"':
|
||||
f << '\\' << c;
|
||||
break;
|
||||
case '\a':
|
||||
f << "\\a";
|
||||
break;
|
||||
case '\b':
|
||||
f << "\\b";
|
||||
break;
|
||||
case '\f':
|
||||
f << "\\f";
|
||||
break;
|
||||
case '\n':
|
||||
f << "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
f << "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
f << "\\t";
|
||||
break;
|
||||
case '\v':
|
||||
f << "\\v";
|
||||
break;
|
||||
default:
|
||||
f << c;
|
||||
break;
|
||||
}
|
||||
}
|
||||
f << '"';
|
||||
break;
|
||||
|
||||
case FmtPart::INTEGER:
|
||||
case FmtPart::CHARACTER: {
|
||||
f << " << value_formatted<" << part.sig.size() << ">(";
|
||||
emit_sig(part.sig);
|
||||
f << ", " << (part.type == FmtPart::CHARACTER);
|
||||
f << ", " << (part.justify == FmtPart::LEFT);
|
||||
f << ", (char)" << (int)part.padding;
|
||||
f << ", " << part.width;
|
||||
f << ", " << part.base;
|
||||
f << ", " << part.signed_;
|
||||
f << ", " << part.lzero;
|
||||
f << ", " << part.plus;
|
||||
f << ')';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string Fmt::render() const
|
||||
{
|
||||
std::string str;
|
||||
|
|
|
@ -76,12 +76,14 @@ struct Fmt {
|
|||
|
||||
void append_string(const std::string &str);
|
||||
|
||||
void parse_rtlil(RTLIL::Cell *cell);
|
||||
void parse_rtlil(const RTLIL::Cell *cell);
|
||||
void emit_rtlil(RTLIL::Cell *cell) const;
|
||||
|
||||
void parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_like, int default_base, RTLIL::IdString task_name, RTLIL::IdString module_name);
|
||||
std::vector<VerilogFmtArg> emit_verilog() const;
|
||||
|
||||
void emit_cxxrtl(std::ostream &f, std::function<void(const RTLIL::SigSpec &)> emit_sig) const;
|
||||
|
||||
std::string render() const;
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue