Compare commits

...

43 Commits

Author SHA1 Message Date
Robert O'Callahan 6b5db348e4
Merge 0d3cd5d6c8 into a16fc9b4f3 2025-11-06 21:05:29 +01:00
Emil J a16fc9b4f3
Merge pull request #5467 from YosysHQ/emil/liberty-unquoted-expressions
libparse: support unquoted expressions
2025-11-06 19:45:17 +01:00
Emil J 3a23d4458e
Merge pull request #5470 from YosysHQ/emil/unit-test-makefile
Makefile: clean unit test on clean, ensure prepared to fix parallelism
2025-11-06 19:05:57 +01:00
Miodrag Milanovic dc9a787025 Fix out of tree clean 2025-11-06 14:28:28 +01:00
Emil J. Tywoniak 2bf7aac9d1 Makefile: clean unit test on clean, ensure prepared to fix parallelism 2025-11-06 13:59:14 +01:00
Emil J. Tywoniak fdcc4c1507 libparse: remove leftover comments 2025-11-06 13:30:09 +01:00
Emil J. Tywoniak 90553267b0 libparse: fix quoting and negedge in filterlib -verilogsim 2025-11-05 14:13:58 +01:00
Emil J. Tywoniak 504b668ea6 libparse: fix verilogsim negedge 2025-11-05 13:49:05 +01:00
Emil J. Tywoniak b0a3d6a3e7 libparse: fix up tests since liberty expression parsing now normalizes the form of these expressions 2025-11-05 13:06:12 +01:00
Emil J. Tywoniak bf29f6dc11 libparse: tolerate closing quotes in expression parsing 2025-11-05 13:06:09 +01:00
Emil J. Tywoniak 4fac7a1b20 libparse: fix space before closing paren in expressions 2025-11-05 13:05:56 +01:00
Emil J. Tywoniak 547e254a9b libparse: parse expressions in filterlib 2025-11-05 13:05:56 +01:00
Emil J. Tywoniak 66d8fc5c28 libparse: quirk-compatibility for unquoted boolean expression strings 2025-11-05 11:00:25 +01:00
Robert O'Callahan 0d3cd5d6c8 Implement SigSpec::updhash() using a relaxed atomic for thread-safety 2025-10-31 11:53:39 +00:00
Robert O'Callahan 45017e19ec Make SigSpec::unpack() non-const 2025-10-31 11:53:39 +00:00
Robert O'Callahan 9a2fd4c31b Switch the SigSpec packed representation to allow just a single chunk that's inline in the SigSpec.
Single-chunk SigSpecs are very common and this avoids a heap allocation. It also simplifies
some algorithms.
2025-10-31 11:53:39 +00:00
Robert O'Callahan 1af5d4f2db Use chunks iterator for SigSpec::extract() 2025-10-31 11:53:39 +00:00
Robert O'Callahan ab525643a7 Don't reset the hash when unpacking, instead clear the hash whenever bits are modified 2025-10-31 11:53:39 +00:00
Robert O'Callahan a1f7d6c9bf Use size() instead of direct access to width_ in many places 2025-10-31 11:53:39 +00:00
Robert O'Callahan dbb8354996 Remove unnecessary pack() from SigSpec::extend_u0() 2025-10-31 11:53:39 +00:00
Robert O'Callahan d314c47a55 Simplify SigSpec::as_bit() 2025-10-31 11:53:39 +00:00
Robert O'Callahan be530bef73 Instead of using packing and hashing to compute SigSpec ordering and equality, just use the width and chunkwise comparisons
This avoids having to pack and compute hashes, and generally results in a
simpler ordering.
2025-10-31 11:53:39 +00:00
Robert O'Callahan 973e8a3928 Build a temporary SigChunk list in the iterator in the cases where that's needed 2025-10-31 11:53:39 +00:00
Robert O'Callahan 8c9dd3209a Make SigSpec conversion to vector of SigChunk use chunks iterator 2025-10-31 11:53:39 +00:00
Robert O'Callahan 4672127610 Make SigSpec::parse_rhs use is_chunk to avoid direct access to chunks_ 2025-10-31 11:53:39 +00:00
Robert O'Callahan 11a91af920 Make SigSpec::to_sigbit_pool use chunk iterator 2025-10-31 11:53:39 +00:00
Robert O'Callahan 8cb7cd7ac1 Make SigSpec::to_sigbit_set use chunk iterator 2025-10-31 11:53:39 +00:00
Robert O'Callahan 82f86164d3 Use SigSpec::try_as_const in some places 2025-10-31 11:53:39 +00:00
Robert O'Callahan 0d45d9cc6e Make SigSpec::is_one_hot use try_as_const 2025-10-31 11:53:39 +00:00
Robert O'Callahan a0e9e2d364 Fix try_as_const/as_wire/as_chunk 2025-10-31 11:53:39 +00:00
Robert O'Callahan 000c081965 Add try_as_const and use the const iterator a bit more 2025-10-31 11:53:38 +00:00
Robert O'Callahan 04a6dbc562 Make SigSpec::has_marked_bits use chunk iterator 2025-10-31 11:53:38 +00:00
Robert O'Callahan 7bd6b4f287 Make SigSpec::has_const(State) use chunk iterator 2025-10-31 11:53:38 +00:00
Robert O'Callahan 213d665ae1 Make SigSpec::has_const use chunk iterator 2025-10-31 11:53:38 +00:00
Robert O'Callahan 5c8f9f14ca Make SigSpec::is_fully_undef use chunk iterator 2025-10-31 11:53:38 +00:00
Robert O'Callahan b2de56cae2 Make SigSpec::is_fully_def use chunk iterator 2025-10-31 11:53:38 +00:00
Robert O'Callahan 8a88acd9b8 Make SigSpec::is_fully_ones use chunk iterator 2025-10-31 11:53:38 +00:00
Robert O'Callahan 58dbf75885 Make SigSpec::is_fully_zero use chunk iterator 2025-10-31 11:53:38 +00:00
Robert O'Callahan 20e64ee17b Make is_fully_const use chunk iterator 2025-10-31 11:53:38 +00:00
Robert O'Callahan bf4cfbd72d Make SigSpec::is_wire/is_chunk/is_fully_const use chunk iterator 2025-10-31 11:53:38 +00:00
Robert O'Callahan fd7b4f4a8b Make SigSpec::updhash() use chunk iterator 2025-10-31 11:53:38 +00:00
Robert O'Callahan 37e4c2e8f8 Make SigSpec::chunks() return an object that can be iterated over without packing the SigSpec 2025-10-31 11:53:38 +00:00
Robert O'Callahan c4f3e61339 Make Module stop accessing internals of SigSpec 2025-10-29 18:32:10 +00:00
21 changed files with 1028 additions and 678 deletions

View File

@ -95,14 +95,14 @@ TARGETS = $(PROGRAM_PREFIX)yosys$(EXE) $(PROGRAM_PREFIX)yosys-config
PRETTY = 1
SMALL = 0
# Unit test
UNITESTPATH := tests/unit
all: top-all
YOSYS_SRC := $(dir $(firstword $(MAKEFILE_LIST)))
VPATH := $(YOSYS_SRC)
# Unit test
UNITESTPATH := $(YOSYS_SRC)/tests/unit
export CXXSTD ?= c++17
CXXFLAGS := $(CXXFLAGS) -Wall -Wextra -ggdb -I. -I"$(YOSYS_SRC)" -MD -MP -D_YOSYS_ -fPIC -I$(PREFIX)/include
LIBS := $(LIBS) -lstdc++ -lm
@ -1135,7 +1135,7 @@ DOC_TARGET ?= html
docs: docs/prep
$(Q) $(MAKE) -C docs $(DOC_TARGET)
clean: clean-py
clean: clean-py clean-unit-test
rm -rf share
rm -f $(OBJS) $(GENFILES) $(TARGETS) $(EXTRA_TARGETS) $(EXTRA_OBJS)
rm -f kernel/version_*.o kernel/version_*.cc
@ -1150,7 +1150,7 @@ clean: clean-py
rm -f tests/svinterfaces/*.log_stdout tests/svinterfaces/*.log_stderr tests/svinterfaces/dut_result.txt tests/svinterfaces/reference_result.txt tests/svinterfaces/a.out tests/svinterfaces/*_syn.v tests/svinterfaces/*.diff
rm -f tests/tools/cmp_tbdata
rm -f $(addsuffix /run-test.mk,$(MK_TEST_DIRS))
-$(MAKE) -C docs clean
-$(MAKE) -C $(YOSYS_SRC)/docs clean
rm -rf docs/util/__pycache__
rm -f libyosys.so
@ -1162,7 +1162,7 @@ clean-py:
rm -rf kernel/*.pyh
clean-abc:
$(MAKE) -C abc DEP= clean
$(MAKE) -C $(YOSYS_SRC)/abc DEP= clean
rm -f $(PROGRAM_PREFIX)yosys-abc$(EXE) $(PROGRAM_PREFIX)yosys-libabc.a abc/abc-[0-9a-f]* abc/libabc-[0-9a-f]*.a .git-abc-submodule-hash
mrproper: clean

View File

@ -121,7 +121,8 @@ void RTLIL_BACKEND::dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig, boo
dump_sigchunk(f, sig.as_chunk(), autoint);
} else {
f << stringf("{ ");
for (const auto& chunk : reversed(sig.chunks())) {
auto chunks = sig.chunks();
for (const auto& chunk : reversed(chunks)) {
dump_sigchunk(f, chunk, false);
f << stringf(" ");
}

View File

@ -382,8 +382,9 @@ void dump_sigspec(std::ostream &f, const RTLIL::SigSpec &sig)
dump_sigchunk(f, sig.as_chunk());
} else {
f << stringf("{ ");
for (auto it = sig.chunks().rbegin(); it != sig.chunks().rend(); ++it) {
if (it != sig.chunks().rbegin())
auto chunks = sig.chunks();
for (auto it = chunks.rbegin(); it != chunks.rend(); ++it) {
if (it != chunks.rbegin())
f << stringf(", ");
dump_sigchunk(f, *it, true);
}

File diff suppressed because it is too large Load Diff

View File

@ -1223,9 +1223,10 @@ struct RTLIL::SigSpecConstIterator
typedef RTLIL::SigBit& reference;
const RTLIL::SigSpec *sig_p;
RTLIL::SigBit bit;
int index;
inline const RTLIL::SigBit &operator*() const;
inline const RTLIL::SigBit &operator*();
inline bool operator!=(const RTLIL::SigSpecConstIterator &other) const { return index != other.index; }
inline bool operator==(const RTLIL::SigSpecIterator &other) const { return index == other.index; }
inline void operator++() { index++; }
@ -1234,33 +1235,83 @@ struct RTLIL::SigSpecConstIterator
struct RTLIL::SigSpec
{
private:
int width_;
Hasher::hash_t hash_;
std::vector<RTLIL::SigChunk> chunks_; // LSB at index 0
std::vector<RTLIL::SigBit> bits_; // LSB at index 0
enum Representation : char {
CHUNK,
BITS,
};
// An AtomicHash is either clear or a nonzero integer.
struct AtomicHash {
// Create an initially clear value.
AtomicHash() : atomic_(0) {}
AtomicHash(const AtomicHash &rhs) : atomic_(rhs.load()) {}
AtomicHash &operator=(const AtomicHash &rhs) { store(rhs.load()); return *this; }
// Read the hash. Returns nullopt if the hash is clear.
std::optional<Hasher::hash_t> read() const {
Hasher::hash_t value = load();
if (value == 0)
return std::nullopt;
return value;
}
// Set the hash. If the value is already set, then the new value must
// equal the current value.
void set(Hasher::hash_t value) const {
log_assert(value != 0);
Hasher::hash_t old = const_cast<std::atomic<Hasher::hash_t>&>(atomic_)
.exchange(value, std::memory_order_relaxed);
log_assert(old == 0 || old == value);
}
void clear() { store(0); }
private:
int load() const { return atomic_.load(std::memory_order_relaxed); }
void store(Hasher::hash_t value) const {
const_cast<std::atomic<Hasher::hash_t>&>(atomic_).store(value, std::memory_order_relaxed);
}
void pack() const;
void unpack() const;
void updhash() const;
std::atomic<Hasher::hash_t> atomic_;
};
inline bool packed() const {
return bits_.empty();
Representation rep_;
AtomicHash hash_;
union {
RTLIL::SigChunk chunk_;
std::vector<RTLIL::SigBit> bits_; // LSB at index 0
};
void init_empty_bits() {
rep_ = BITS;
new (&bits_) std::vector<RTLIL::SigBit>;
}
inline void inline_unpack() const {
if (!chunks_.empty())
void unpack();
inline void inline_unpack() {
if (rep_ == CHUNK)
unpack();
}
// Only used by Module::remove(const pool<Wire*> &wires)
// but cannot be more specific as it isn't yet declared
friend struct RTLIL::Module;
Hasher::hash_t updhash() const;
void destroy() {
if (rep_ == CHUNK)
chunk_.~SigChunk();
else
bits_.~vector();
}
friend struct Chunks;
public:
SigSpec() : width_(0), hash_(0) {}
SigSpec() { init_empty_bits(); }
SigSpec(std::initializer_list<RTLIL::SigSpec> parts);
SigSpec(const SigSpec &value) = default;
SigSpec(SigSpec &&value) = default;
SigSpec(const SigSpec &value) : rep_(value.rep_), hash_(value.hash_) {
if (value.rep_ == CHUNK)
new (&chunk_) RTLIL::SigChunk(value.chunk_);
else
new (&bits_) std::vector<RTLIL::SigBit>(value.bits_);
}
SigSpec(SigSpec &&value) : rep_(value.rep_), hash_(value.hash_) {
if (value.rep_ == CHUNK)
new (&chunk_) RTLIL::SigChunk(std::move(value.chunk_));
else
new (&bits_) std::vector<RTLIL::SigBit>(std::move(value.bits_));
}
SigSpec(const RTLIL::Const &value);
SigSpec(RTLIL::Const &&value);
SigSpec(const RTLIL::SigChunk &chunk);
@ -1276,24 +1327,135 @@ public:
SigSpec(const pool<RTLIL::SigBit> &bits);
SigSpec(const std::set<RTLIL::SigBit> &bits);
explicit SigSpec(bool bit);
~SigSpec() {
destroy();
}
SigSpec &operator=(const SigSpec &rhs) = default;
SigSpec &operator=(SigSpec &&rhs) = default;
SigSpec &operator=(const SigSpec &rhs) {
destroy();
rep_ = rhs.rep_;
hash_ = rhs.hash_;
if (rep_ == CHUNK)
new (&chunk_) RTLIL::SigChunk(rhs.chunk_);
else
new (&bits_) std::vector<RTLIL::SigBit>(rhs.bits_);
return *this;
}
SigSpec &operator=(SigSpec &&rhs) {
destroy();
rep_ = rhs.rep_;
hash_ = rhs.hash_;
if (rep_ == CHUNK)
new (&chunk_) RTLIL::SigChunk(std::move(rhs.chunk_));
else
new (&bits_) std::vector<RTLIL::SigBit>(std::move(rhs.bits_));
return *this;
}
inline const std::vector<RTLIL::SigChunk> &chunks() const { pack(); return chunks_; }
inline const std::vector<RTLIL::SigBit> &bits() const { inline_unpack(); return bits_; }
struct Chunks {
Chunks(const SigSpec &spec) : spec(spec) {}
struct const_iterator {
using iterator_category = std::forward_iterator_tag;
using value_type = const SigChunk &;
using difference_type = std::ptrdiff_t;
using pointer = const SigChunk *;
using reference = const SigChunk &;
inline int size() const { return width_; }
inline bool empty() const { return width_ == 0; }
const SigSpec &spec;
int bit_index;
SigChunk chunk;
inline RTLIL::SigBit &operator[](int index) { inline_unpack(); return bits_.at(index); }
inline const RTLIL::SigBit &operator[](int index) const { inline_unpack(); return bits_.at(index); }
const_iterator(const SigSpec &spec) : spec(spec) {
bit_index = 0;
if (spec.rep_ == BITS)
next_chunk_bits();
}
void next_chunk_bits();
const SigChunk &operator*() {
if (spec.rep_ == CHUNK)
return spec.chunk_;
return chunk;
};
const SigChunk *operator->() { return &**this; }
const_iterator &operator++() {
bit_index += (**this).width;
if (spec.rep_ == BITS)
next_chunk_bits();
return *this;
}
bool operator==(const const_iterator &rhs) const { return bit_index == rhs.bit_index; }
bool operator!=(const const_iterator &rhs) const { return !(*this == rhs); }
};
const_iterator begin() const { return const_iterator(spec); }
const_iterator end() const {
const_iterator it(spec);
it.bit_index = spec.size();
return it;
}
// Later we should deprecate these and remove their in-tree calls,
// so we can eventually remove chunk_vector.
std::vector<RTLIL::SigChunk>::const_reverse_iterator rbegin() {
ensure_chunk_vector();
return chunk_vector.rbegin();
}
std::vector<RTLIL::SigChunk>::const_reverse_iterator rend() {
ensure_chunk_vector();
return chunk_vector.rend();
}
int size() {
ensure_chunk_vector();
return chunk_vector.size();
}
int size() const {
int result = 0;
for (const SigChunk &_: *this)
++result;
return result;
}
const SigChunk &at(int index) {
ensure_chunk_vector();
return chunk_vector.at(index);
}
operator const std::vector<RTLIL::SigChunk>&() {
ensure_chunk_vector();
return chunk_vector;
}
private:
void ensure_chunk_vector() {
if (spec.size() > 0 && chunk_vector.empty()) {
for (const RTLIL::SigChunk &c : *this)
chunk_vector.push_back(c);
}
}
const SigSpec &spec;
std::vector<RTLIL::SigChunk> chunk_vector;
};
friend struct Chunks::const_iterator;
inline Chunks chunks() const { return {*this}; }
inline const SigSpec &bits() const { return *this; }
inline int size() const { return rep_ == CHUNK ? chunk_.width : GetSize(bits_); }
inline bool empty() const { return size() == 0; };
inline RTLIL::SigBit &operator[](int index) { inline_unpack(); hash_.clear(); return bits_.at(index); }
inline RTLIL::SigBit operator[](int index) const {
if (rep_ == CHUNK) {
if (index < 0 || index >= chunk_.width)
throw std::out_of_range("SigSpec::operator[]");
if (chunk_.wire)
return RTLIL::SigBit(chunk_.wire, chunk_.offset + index);
return RTLIL::SigBit(chunk_.data[index]);
}
return bits_.at(index);
}
inline RTLIL::SigSpecIterator begin() { RTLIL::SigSpecIterator it; it.sig_p = this; it.index = 0; return it; }
inline RTLIL::SigSpecIterator end() { RTLIL::SigSpecIterator it; it.sig_p = this; it.index = width_; return it; }
inline RTLIL::SigSpecIterator end() { RTLIL::SigSpecIterator it; it.sig_p = this; it.index = size(); return it; }
inline RTLIL::SigSpecConstIterator begin() const { RTLIL::SigSpecConstIterator it; it.sig_p = this; it.index = 0; return it; }
inline RTLIL::SigSpecConstIterator end() const { RTLIL::SigSpecConstIterator it; it.sig_p = this; it.index = width_; return it; }
inline RTLIL::SigSpecConstIterator end() const { RTLIL::SigSpecConstIterator it; it.sig_p = this; it.index = size(); return it; }
void sort();
void sort_and_unify();
@ -1325,10 +1487,14 @@ public:
RTLIL::SigSpec extract(const RTLIL::SigSpec &pattern, const RTLIL::SigSpec *other = NULL) const;
RTLIL::SigSpec extract(const pool<RTLIL::SigBit> &pattern, const RTLIL::SigSpec *other = NULL) const;
RTLIL::SigSpec extract(int offset, int length = 1) const;
RTLIL::SigSpec extract_end(int offset) const { return extract(offset, width_ - offset); }
RTLIL::SigSpec extract_end(int offset) const { return extract(offset, size() - offset); }
RTLIL::SigBit lsb() const { log_assert(width_); return (*this)[0]; };
RTLIL::SigBit msb() const { log_assert(width_); return (*this)[width_ - 1]; };
void rewrite_wires(std::function<void(RTLIL::Wire*& wire)> rewrite);
RTLIL::SigBit lsb() const { log_assert(size()); return (*this)[0]; };
RTLIL::SigBit msb() const { log_assert(size()); return (*this)[size() - 1]; };
RTLIL::SigBit front() const { return (*this)[0]; }
RTLIL::SigBit back() const { return (*this)[size() - 1]; }
void append(const RTLIL::SigSpec &signal);
inline void append(Wire *wire) { append(RTLIL::SigSpec(wire)); }
@ -1351,7 +1517,7 @@ public:
bool is_wire() const;
bool is_chunk() const;
inline bool is_bit() const { return width_ == 1; }
inline bool is_bit() const { return size() == 1; }
bool known_driver() const;
@ -1388,6 +1554,9 @@ public:
int as_int_saturating(bool is_signed = false) const;
std::string as_string() const;
// Returns std::nullopt if there are any non-constant bits. Returns an empty
// Const if this has zero width.
std::optional<RTLIL::Const> try_as_const() const;
RTLIL::Const as_const() const;
RTLIL::Wire *as_wire() const;
RTLIL::SigChunk as_chunk() const;
@ -1405,11 +1574,19 @@ public:
static bool parse_sel(RTLIL::SigSpec &sig, RTLIL::Design *design, RTLIL::Module *module, std::string str);
static bool parse_rhs(const RTLIL::SigSpec &lhs, RTLIL::SigSpec &sig, RTLIL::Module *module, std::string str);
operator std::vector<RTLIL::SigChunk>() const { return chunks(); }
operator std::vector<RTLIL::SigBit>() const { return bits(); }
const RTLIL::SigBit &at(int offset, const RTLIL::SigBit &defval) { return offset < width_ ? (*this)[offset] : defval; }
operator std::vector<RTLIL::SigChunk>() const;
operator std::vector<RTLIL::SigBit>() const { return to_sigbit_vector(); }
const RTLIL::SigBit &at(int offset, const RTLIL::SigBit &defval) { return offset < size() ? (*this)[offset] : defval; }
[[nodiscard]] Hasher hash_into(Hasher h) const { if (!hash_) updhash(); h.eat(hash_); return h; }
[[nodiscard]] Hasher hash_into(Hasher h) const {
Hasher::hash_t val;
if (std::optional<Hasher::hash_t> current = hash_.read())
val = *current;
else
val = updhash();
h.eat(val);
return h;
}
#ifndef NDEBUG
void check(Module *mod = nullptr) const;
@ -2314,13 +2491,15 @@ inline RTLIL::SigBit &RTLIL::SigSpecIterator::operator*() const {
return (*sig_p)[index];
}
inline const RTLIL::SigBit &RTLIL::SigSpecConstIterator::operator*() const {
return (*sig_p)[index];
inline const RTLIL::SigBit &RTLIL::SigSpecConstIterator::operator*() {
bit = (*sig_p)[index];
return bit;
}
inline RTLIL::SigBit::SigBit(const RTLIL::SigSpec &sig) {
log_assert(sig.size() == 1 && sig.chunks().size() == 1);
*this = SigBit(sig.chunks().front());
log_assert(sig.size() == 1);
auto it = sig.chunks().begin();
*this = SigBit(*it);
}
template<typename T>

View File

@ -277,14 +277,26 @@ inline int ceil_log2(int x)
#endif
}
template <typename T>
auto reversed(T& container) {
struct reverse_view {
reverse_view(T& container) : container(container) {}
auto begin() const { return container.rbegin(); }
auto end() const { return container.rend(); }
T& container;
};
return reverse_view{container};
}
template <typename T>
auto reversed(const T& container) {
struct reverse_view {
const T& cont;
auto begin() const { return cont.rbegin(); }
auto end() const { return cont.rend(); }
};
return reverse_view{container};
struct reverse_view {
reverse_view(const T& container) : container(container) {}
auto begin() const { return container.rbegin(); }
auto end() const { return container.rend(); }
const T& container;
};
return reverse_view{container};
}
YOSYS_NAMESPACE_END

View File

@ -278,11 +278,12 @@ struct ShowWorker
std::vector<std::string> label_pieces;
int bitpos = sig.size()-1;
for (int rep, chunk_idx = ((int) sig.chunks().size()) - 1; chunk_idx >= 0; chunk_idx -= rep) {
const RTLIL::SigChunk &c = sig.chunks().at(chunk_idx);
RTLIL::SigSpec::Chunks sig_chunks = sig.chunks();
for (int rep, chunk_idx = ((int) sig_chunks.size()) - 1; chunk_idx >= 0; chunk_idx -= rep) {
const RTLIL::SigChunk &c = sig_chunks.at(chunk_idx);
// Find the number of times this chunk is repeating
for (rep = 1; chunk_idx - rep >= 0 && c == sig.chunks().at(chunk_idx - rep); rep++);
for (rep = 1; chunk_idx - rep >= 0 && c == sig_chunks.at(chunk_idx - rep); rep++);
int cl, cr;
cl = c.offset + c.width - 1;

View File

@ -1428,13 +1428,13 @@ void reintegrate(RTLIL::Module *module, bool dff_mode)
// Copy connections (and rename) from mapped_mod to module
for (auto conn : mapped_mod->connections()) {
if (!conn.first.is_fully_const()) {
auto chunks = conn.first.chunks();
std::vector<RTLIL::SigChunk> chunks = conn.first.chunks();
for (auto &c : chunks)
c.wire = module->wires_.at(remap_name(c.wire->name));
conn.first = std::move(chunks);
}
if (!conn.second.is_fully_const()) {
auto chunks = conn.second.chunks();
std::vector<RTLIL::SigChunk> chunks = conn.second.chunks();
for (auto &c : chunks)
if (c.wire)
c.wire = module->wires_.at(remap_name(c.wire->name));

View File

@ -127,7 +127,7 @@ static bool parse_next_state(const LibertyAst *cell, const LibertyAst *attr, std
return false;
}
auto pin_names = pool<std::string>{};
auto pin_names = std::unordered_set<std::string>{};
tree.get_pin_names(pin_names);
// from the `ff` block, we know the flop output signal name for loopback.
@ -156,7 +156,7 @@ static bool parse_next_state(const LibertyAst *cell, const LibertyAst *attr, std
auto pins = std::vector<std::string>(pin_names.begin(), pin_names.end());
int lut = 0;
for (int n = 0; n < 8; n++) {
auto values = dict<std::string, bool>{};
auto values = std::unordered_map<std::string, bool>{};
values.insert(std::make_pair(pins[0], (n & 1) == 1));
values.insert(std::make_pair(pins[1], (n & 2) == 2));
values.insert(std::make_pair(ff_output, (n & 4) == 4));

View File

@ -25,9 +25,22 @@
#include <fstream>
#include <iostream>
#include <sstream>
#include <algorithm>
#ifndef FILTERLIB
#ifdef FILTERLIB
#undef log_assert
void log_assert(bool cond) {
if (!cond)
fprintf(stderr, "Unspecified assertion failed\n");
}
void warn(std::string str) {
std::cerr << str;
}
#else
#include "kernel/log.h"
void warn(std::string str) {
Yosys::log_formatted_warning("", str);
}
#endif
using namespace Yosys;
@ -162,13 +175,15 @@ void LibertyAst::dump(FILE *f, sieve &blacklist, sieve &whitelist, std::string i
fprintf(f, " ;\n");
}
#ifndef FILTERLIB
// binary operators excluding ' '
bool LibertyExpression::is_nice_binop(char c) {
bool LibertyExpression::char_is_nice_binop(char c) {
return c == '*' || c == '&' || c == '^' || c == '+' || c == '|';
}
bool LibertyExpression::is_binop() {
return kind == AND || kind == OR || kind == XOR;
}
// https://matklad.github.io/2020/04/13/simple-but-powerful-pratt-parsing.html
LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
if (s.empty())
@ -177,7 +192,7 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
char c = s.peek();
auto lhs = LibertyExpression{};
while (isspace(c)) {
while (isspace(c) || c == '"') {
if (s.empty())
return lhs;
s.next();
@ -191,7 +206,9 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
s.next();
lhs = parse(s);
if (s.peek() != ')') {
log_warning("expected ')' instead of '%c' while parsing Liberty expression '%s'\n", s.peek(), s.full_expr());
std::stringstream ss;
ss << "expected ')' instead of " << s.peek() << " while parsing Liberty expression '" << s.full_expr() << "'\n";
warn(ss.str());
return lhs;
}
s.next();
@ -200,10 +217,11 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
lhs.kind = Kind::NOT;
lhs.children.push_back(parse(s, 7));
} else {
log_warning("unrecognised character '%c' while parsing Liberty expression '%s'\n", c, s.full_expr());
std::stringstream ss;
ss << "unrecognised character " << c << " while parsing Liberty expression " << s.full_expr() << "\n";
warn(ss.str());
return lhs;
}
while (true) {
if (s.empty())
break;
@ -246,9 +264,10 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
s.next();
c = s.peek();
}
if (is_nice_binop(c)) {
if (char_is_nice_binop(c) || c == ')' || c == '\'' || c == '\"') {
// We found a real binop, so this space wasn't an AND
// and we just discard it as meaningless whitespace
// Tail operators also imply this isn't an AND
continue;
}
} else {
@ -286,7 +305,7 @@ LibertyExpression LibertyExpression::parse(Lexer &s, int min_prio) {
return lhs;
}
void LibertyExpression::get_pin_names(pool<std::string>& names) {
void LibertyExpression::get_pin_names(std::unordered_set<std::string>& names) {
if (kind == Kind::PIN) {
names.insert(name);
} else {
@ -295,7 +314,7 @@ void LibertyExpression::get_pin_names(pool<std::string>& names) {
}
}
bool LibertyExpression::eval(dict<std::string, bool>& values) {
bool LibertyExpression::eval(std::unordered_map<std::string, bool>& values) {
bool result = false;
switch (kind) {
case Kind::AND:
@ -324,7 +343,7 @@ bool LibertyExpression::eval(dict<std::string, bool>& values) {
return false;
}
std::string LibertyExpression::str(int indent)
std::string LibertyExpression::sexpr_str(int indent)
{
std::string prefix;
switch (kind) {
@ -355,16 +374,55 @@ std::string LibertyExpression::str(int indent)
if (!first) {
prefix += "\n" + std::string(indent + add_indent, ' ');
}
prefix += child.str(indent + add_indent);
prefix += child.sexpr_str(indent + add_indent);
first = false;
}
prefix += ")";
return prefix;
}
#endif
std::string LibertyExpression::vlog_str()
{
std::string prefix;
if (kind != PIN)
prefix += "(";
if (is_binop()) {
log_assert(children.size() == 2);
prefix += children[0].vlog_str();
switch (kind) {
case AND:
prefix += "&";
break;
case OR:
prefix += "|";
break;
case XOR:
prefix += "^";
break;
default:
log_assert(false);
}
prefix += children[1].vlog_str();
} else {
switch (kind) {
case NOT:
log_assert(children.size() == 1);
prefix += "~";
prefix += children[0].vlog_str();
break;
case PIN:
prefix += name;
break;
default:
log_assert(false);
}
}
if (kind != PIN)
prefix += ")";
return prefix;
}
int LibertyParser::lexer(std::string &str)
int LibertyParser::lexer_inner(std::string &str)
{
int c;
@ -390,11 +448,9 @@ int LibertyParser::lexer(std::string &str)
if (str == "+" || str == "-") {
/* Single operator is not an identifier */
// fprintf(stderr, "LEX: char >>%s<<\n", str.c_str());
return str[0];
}
else {
// fprintf(stderr, "LEX: identifier >>%s<<\n", str.c_str());
return 'v';
}
}
@ -402,24 +458,25 @@ int LibertyParser::lexer(std::string &str)
// if it wasn't an identifer, number of array range,
// maybe it's a string?
if (c == '"') {
f.consume(1);
size_t i = 0;
while (true) {
c = f.peek(i);
line += (c == '\n');
if (c != '"')
if (c != '"' && c != EOF)
i += 1;
else
break;
}
str.clear();
#ifdef FILTERLIB
f.unget();
str.append(f.buffered_data(), f.buffered_data() + i + 2);
f.consume(i + 2);
#else
str.append(f.buffered_data(), f.buffered_data() + i);
f.consume(i + 1);
str.append(f.buffered_data(), f.buffered_data() + i + 1);
// Usage in filterlib is expected to retain quotes
// but yosys expects to get unquoted
#ifdef FILTERLIB
str = "\"" + str + "\"";
#endif
f.consume(i + 2);
return 'v';
}
@ -442,13 +499,12 @@ int LibertyParser::lexer(std::string &str)
return lexer(str);
}
f.unget();
// fprintf(stderr, "LEX: char >>/<<\n");
return '/'; // a single '/' charater.
}
// check for a backslash
if (c == '\\') {
c = f.get();
c = f.get();
if (c == '\r')
c = f.get();
if (c == '\n') {
@ -467,14 +523,22 @@ int LibertyParser::lexer(std::string &str)
// anything else, such as ';' will get passed
// through as literal items.
// if (c >= 32 && c < 255)
// fprintf(stderr, "LEX: char >>%c<<\n", c);
// else
// fprintf(stderr, "LEX: char %d\n", c);
return c;
}
int LibertyParser::lexer(std::string &str)
{
int ret = lexer_inner(str);
// if (ret >= 32 && ret < 255) {
// fprintf(stdout, "LEX: ret >>%c<<\n", ret);
// } else if (ret == 'v') {
// fprintf(stdout, "LEX: ret v str %s\n", str.c_str());
// } else {
// fprintf(stdout, "LEX: ret %d\n", ret);
// }
return ret;
}
void LibertyParser::report_unexpected_token(int tok)
{
std::string eReport;
@ -545,6 +609,25 @@ void LibertyParser::parse_vector_range(int tok)
}
}
// Consume into out_str any string-ish tokens, seperated with spaces
// to cope with abuse of the underdefined spec by real world PDKs
// enabled by proprietary implementations.
// Sorry.
int LibertyParser::consume_wrecked_str(int tok, std::string& out_str) {
std::string str = "";
while (tok != ';' && tok != EOF && tok != 'n') {
out_str += " ";
if (tok == 'v')
out_str += str;
else
out_str += tok;
tok = lexer(str);
}
if (tok == EOF)
error("wrecked string EOF");
return tok;
}
LibertyAst *LibertyParser::parse(bool top_level)
{
std::string str;
@ -591,7 +674,14 @@ LibertyAst *LibertyParser::parse(bool top_level)
if (tok == '[') {
parse_vector_range(tok);
tok = lexer(str);
} else {
// Hack for when an expression string is unquoted
tok = consume_wrecked_str(tok, ast->value);
}
} else if (tok == '(') {
// Hack for when an expression string is unquoted and starts with
// parentheses
tok = consume_wrecked_str(tok, ast->value);
}
while (tok == '+' || tok == '-' || tok == '*' || tok == '/' || tok == '!') {
ast->value += tok;
@ -601,7 +691,7 @@ LibertyAst *LibertyParser::parse(bool top_level)
ast->value += str;
tok = lexer(str);
}
// In a liberty file, all key : value pairs should end in ';'
// However, there are some liberty files in the wild that
// just have a newline. We'll be kind and accept a newline
@ -621,11 +711,11 @@ LibertyAst *LibertyParser::parse(bool top_level)
continue;
if (tok == ')')
break;
if (tok == '[')
{
parse_vector_range(tok);
continue;
continue;
}
if (tok == 'n')
continue;
@ -727,42 +817,13 @@ const LibertyAst *find_non_null(const LibertyAst *node, const char *name)
std::string func2vl(std::string str)
{
for (size_t pos = str.find_first_of("\" \t"); pos != std::string::npos; pos = str.find_first_of("\" \t")) {
char c_left = pos > 0 ? str[pos-1] : ' ';
char c_right = pos+1 < str.size() ? str[pos+1] : ' ';
if (std::string("\" \t*+").find(c_left) != std::string::npos)
str.erase(pos, 1);
else if (std::string("\" \t*+").find(c_right) != std::string::npos)
str.erase(pos, 1);
else
str[pos] = '*';
}
std::vector<size_t> group_start;
for (size_t pos = 0; pos < str.size(); pos++) {
if (str[pos] == '(')
group_start.push_back(pos);
if (str[pos] == ')' && group_start.size() > 0) {
if (pos+1 < str.size() && str[pos+1] == '\'') {
std::string group = str.substr(group_start.back(), pos-group_start.back()+1);
str[group_start.back()] = '~';
str.replace(group_start.back()+1, group.size(), group);
pos++;
}
group_start.pop_back();
}
if (str[pos] == '\'' && pos > 0) {
size_t start = str.find_last_of("()'*+^&| ", pos-1)+1;
std::string group = str.substr(start, pos-start);
str[start] = '~';
str.replace(start+1, group.size(), group);
}
if (str[pos] == '*')
str[pos] = '&';
if (str[pos] == '+')
str[pos] = '|';
}
auto helper = LibertyExpression::Lexer(str);
return LibertyExpression::parse(helper).vlog_str();
}
std::string vlog_identifier(std::string str)
{
str.erase(std::remove(str.begin(), str.end(), '\"'), str.end());
return str;
}
@ -772,11 +833,13 @@ void event2vl(const LibertyAst *ast, std::string &edge, std::string &expr)
expr.clear();
if (ast != NULL) {
expr = func2vl(ast->value);
if (expr.size() > 0 && expr[0] == '~')
edge = "negedge " + expr.substr(1);
auto helper = LibertyExpression::Lexer(ast->value);
auto parsed = LibertyExpression::parse(helper);
expr = parsed.vlog_str();
if (parsed.kind == LibertyExpression::Kind::NOT)
edge = "negedge " + parsed.children[0].vlog_str();
else
edge = "posedge " + expr;
edge = "posedge " + parsed.vlog_str();
}
}
@ -806,13 +869,13 @@ void gen_verilogsim_cell(const LibertyAst *ast)
return;
CHECK_NV(ast->args.size(), == 1);
printf("module %s (", ast->args[0].c_str());
printf("module %s (", vlog_identifier(ast->args[0]).c_str());
bool first = true;
for (auto child : ast->children) {
if (child->id != "pin")
continue;
CHECK_NV(child->args.size(), == 1);
printf("%s%s", first ? "" : ", ", child->args[0].c_str());
printf("%s%s", first ? "" : ", ", vlog_identifier(child->args[0]).c_str());
first = false;
}
printf(");\n");
@ -823,7 +886,7 @@ void gen_verilogsim_cell(const LibertyAst *ast)
printf(" reg ");
first = true;
for (auto arg : child->args) {
printf("%s%s", first ? "" : ", ", arg.c_str());
printf("%s%s", first ? "" : ", ", vlog_identifier(arg).c_str());
first = false;
}
printf(";\n");
@ -835,9 +898,10 @@ void gen_verilogsim_cell(const LibertyAst *ast)
CHECK_NV(child->args.size(), == 1);
const LibertyAst *dir = find_non_null(child, "direction");
const LibertyAst *func = child->find("function");
printf(" %s %s;\n", dir->value.c_str(), child->args[0].c_str());
std::string var = vlog_identifier(child->args[0]);
printf(" %s %s;\n", dir->value.c_str(), var.c_str());
if (func != NULL)
printf(" assign %s = %s; // %s\n", child->args[0].c_str(), func2vl(func->value).c_str(), func->value.c_str());
printf(" assign %s = %s; // %s\n", var.c_str(), func2vl(func->value).c_str(), func->value.c_str());
}
for (auto child : ast->children)
@ -845,8 +909,8 @@ void gen_verilogsim_cell(const LibertyAst *ast)
if (child->id != "ff" || child->args.size() != 2)
continue;
std::string iq_var = child->args[0];
std::string iqn_var = child->args[1];
std::string iq_var = vlog_identifier(child->args[0]);
std::string iqn_var = vlog_identifier(child->args[1]);
std::string clock_edge, clock_expr;
event2vl(child->find("clocked_on"), clock_edge, clock_expr);
@ -909,8 +973,8 @@ void gen_verilogsim_cell(const LibertyAst *ast)
if (child->id != "latch" || child->args.size() != 2)
continue;
std::string iq_var = child->args[0];
std::string iqn_var = child->args[1];
std::string iq_var = vlog_identifier(child->args[0]);
std::string iqn_var = vlog_identifier(child->args[1]);
std::string enable_edge, enable_expr;
event2vl(child->find("enable"), enable_edge, enable_expr);

View File

@ -63,7 +63,7 @@ namespace Yosys
}
std::string pin() {
auto length = s.find_first_of("\t()'!^*& +|");
auto length = s.find_first_of("\t()'!^*& +|\"");
if (length == std::string::npos) {
// nothing found so use size of s
length = s.size();
@ -91,11 +91,13 @@ namespace Yosys
LibertyExpression() : kind(Kind::EMPTY) {}
static LibertyExpression parse(Lexer &s, int min_prio = 0);
void get_pin_names(pool<std::string>& names);
bool eval(dict<std::string, bool>& values);
std::string str(int indent = 0);
void get_pin_names(std::unordered_set<std::string>& names);
bool eval(std::unordered_map<std::string, bool>& values);
std::string sexpr_str(int indent = 0);
std::string vlog_str();
private:
static bool is_nice_binop(char c);
static bool char_is_nice_binop(char c);
bool is_binop();
};
class LibertyInputStream {
@ -170,10 +172,12 @@ namespace Yosys
'n': newline
anything else is a single character.
*/
int lexer_inner(std::string &str);
int lexer(std::string &str);
void report_unexpected_token(int tok);
void parse_vector_range(int tok);
int consume_wrecked_str(int tok, std::string& out_str);
LibertyAst *parse(bool top_level);
void error() const;
void error(const std::string &str) const;

View File

@ -2,5 +2,5 @@ module XNOR2X1 (B, A, Y);
input B;
input A;
output Y;
assign Y = !(B&!A|!B&A); // "!(B&!A|!B&A)"
assign Y = (~((B&(~A))|((~B)&A))); // "!(B&!A|!B&A)"
endmodule

View File

@ -5,7 +5,7 @@ library(dff) {
area : 1;
ff("IQ", "IQN") {
next_state : "(D)";
clocked_on : "CLK";
clocked_on : (CLK);
}
pin(D) {
direction : input;
@ -15,7 +15,7 @@ library(dff) {
}
pin(Q) {
direction: output;
function : "IQ";
function : IQ;
}
}

View File

@ -3,7 +3,7 @@ library(dff) {
area : 1 ;
ff("IQ", "IQN") {
next_state : "(D)" ;
clocked_on : "CLK" ;
clocked_on : ( CLK ) ;
}
pin(D) {
direction : input ;
@ -13,7 +13,7 @@ library(dff) {
}
pin(Q) {
direction : output ;
function : "IQ" ;
function : IQ ;
}
}
}

View File

@ -1,12 +1,12 @@
module dff (D, CLK, Q);
reg "IQ", "IQN";
reg IQ, IQN;
input D;
input CLK;
output Q;
assign Q = IQ; // "IQ"
assign Q = IQ; // IQ
always @(posedge CLK) begin
// "(D)"
"IQ" <= (D);
"IQN" <= ~((D));
IQ <= D;
IQN <= ~(D);
end
endmodule

View File

@ -1,13 +1,13 @@
module inv (A, Y);
input A;
output Y;
assign Y = ~A; // "A'"
assign Y = (~A); // "A'"
endmodule
module tri_inv (A, S, Z);
input A;
input S;
output Z;
assign Z = ~A; // "A'"
assign Z = (~A); // "A'"
endmodule
module buffer (A, Y);
input A;
@ -18,29 +18,29 @@ module nand2 (A, B, Y);
input A;
input B;
output Y;
assign Y = ~(A&B); // "(A * B)'"
assign Y = (~(A&B)); // "(A * B)'"
endmodule
module nor2 (A, B, Y);
input A;
input B;
output Y;
assign Y = ~(A|B); // "(A + B)'"
assign Y = (~(A|B)); // "(A + B)'"
endmodule
module xor2 (A, B, Y);
input A;
input B;
output Y;
assign Y = (A&~B)|(~A&B); // "(A *B') + (A' * B)"
assign Y = ((A&(~B))|((~A)&B)); // "(A *B') + (A' * B)"
endmodule
module imux2 (A, B, S, Y);
input A;
input B;
input S;
output Y;
assign Y = ~(&(A&S)|(B&~S)&); // "( (A * S) + (B * S') )'"
assign Y = (~((A&S)|(B&(~S)))); // "( (A * S) + (B * S') )'"
endmodule
module dff (D, CLK, RESET, PRESET, Q, QN);
reg "IQ", "IQN";
reg IQ, IQN;
input D;
input CLK;
input RESET;
@ -51,26 +51,26 @@ module dff (D, CLK, RESET, PRESET, Q, QN);
assign QN = IQN; // "IQN"
always @(posedge CLK, posedge RESET, posedge PRESET) begin
if ((RESET) && (PRESET)) begin
"IQ" <= 0;
"IQN" <= 0;
IQ <= 0;
IQN <= 0;
end
else if (RESET) begin
"IQ" <= 0;
"IQN" <= 1;
IQ <= 0;
IQN <= 1;
end
else if (PRESET) begin
"IQ" <= 1;
"IQN" <= 0;
IQ <= 1;
IQN <= 0;
end
else begin
// "D"
"IQ" <= D;
"IQN" <= ~(D);
IQ <= D;
IQN <= ~(D);
end
end
endmodule
module latch (D, G, Q, QN);
reg "IQ", "IQN";
reg IQ, IQN;
input D;
input G;
output Q;
@ -79,8 +79,8 @@ module latch (D, G, Q, QN);
assign QN = IQN; // "IQN"
always @* begin
if (G) begin
"IQ" <= D;
"IQN" <= ~(D);
IQ <= D;
IQN <= ~(D);
end
end
endmodule
@ -89,14 +89,14 @@ module aoi211 (A, B, C, Y);
input B;
input C;
output Y;
assign Y = ~((A&B)|C); // "((A * B) + C)'"
assign Y = (~((A&B)|C)); // "((A * B) + C)'"
endmodule
module oai211 (A, B, C, Y);
input A;
input B;
input C;
output Y;
assign Y = ~((A|B)&C); // "((A + B) * C)'"
assign Y = (~((A|B)&C)); // "((A + B) * C)'"
endmodule
module halfadder (A, B, C, Y);
input A;
@ -104,7 +104,7 @@ module halfadder (A, B, C, Y);
output C;
assign C = (A&B); // "(A * B)"
output Y;
assign Y = (A&~B)|(~A&B); // "(A *B') + (A' * B)"
assign Y = ((A&(~B))|((~A)&B)); // "(A *B') + (A' * B)"
endmodule
module fulladder (A, B, CI, CO, Y);
input A;

View File

@ -0,0 +1,60 @@
library(dff_unquoted) {
cell (dff1) {
area : 1;
ff("IQ", "IQN") {
next_state : !D;
clocked_on : (CLK);
}
pin(D) {
direction : input;
}
pin(CLK) {
direction : input;
}
pin(Q) {
direction: output;
function : IQ;
}
}
cell (dff2) {
area : 1;
ff(IQ, IQN) {
next_state : D';
clocked_on : CLK;
}
pin(D) {
direction : input;
}
pin(CLK) {
direction : input;
}
pin(Q) {
direction: output;
function : "IQ";
}
}
cell (dffe) {
area : 6;
ff("IQ", "IQN") {
next_state : (D&EN) | (IQ&!EN);
clocked_on : !CLK;
}
pin(D) {
direction : input;
}
pin(EN) {
direction : input;
}
pin(CLK) {
direction : input;
}
pin(Q) {
direction: output;
function : "IQ";
}
pin(QN) {
direction: output;
function : "IQN";
}
}
}

View File

@ -0,0 +1,60 @@
library(dff_unquoted) {
cell(dff1) {
area : 1 ;
ff("IQ", "IQN") {
next_state : !D ;
clocked_on : ( CLK ) ;
}
pin(D) {
direction : input ;
}
pin(CLK) {
direction : input ;
}
pin(Q) {
direction : output ;
function : IQ ;
}
}
cell(dff2) {
area : 1 ;
ff(IQ, IQN) {
next_state : D ' ;
clocked_on : CLK ;
}
pin(D) {
direction : input ;
}
pin(CLK) {
direction : input ;
}
pin(Q) {
direction : output ;
function : "IQ" ;
}
}
cell(dffe) {
area : 6 ;
ff("IQ", "IQN") {
next_state : ( D & EN ) | ( IQ & ! EN ) ;
clocked_on : !CLK ;
}
pin(D) {
direction : input ;
}
pin(EN) {
direction : input ;
}
pin(CLK) {
direction : input ;
}
pin(Q) {
direction : output ;
function : "IQ" ;
}
pin(QN) {
direction : output ;
function : "IQN" ;
}
}
}

View File

@ -0,0 +1,39 @@
module dff1 (D, CLK, Q);
reg IQ, IQN;
input D;
input CLK;
output Q;
assign Q = IQ; // IQ
always @(posedge CLK) begin
// !D
IQ <= (~D);
IQN <= ~((~D));
end
endmodule
module dff2 (D, CLK, Q);
reg IQ, IQN;
input D;
input CLK;
output Q;
assign Q = IQ; // "IQ"
always @(posedge CLK) begin
// D '
IQ <= (~D);
IQN <= ~((~D));
end
endmodule
module dffe (D, EN, CLK, Q, QN);
reg IQ, IQN;
input D;
input EN;
input CLK;
output Q;
assign Q = IQ; // "IQ"
output QN;
assign QN = IQN; // "IQN"
always @(negedge CLK) begin
// ( D & EN ) | ( IQ & ! EN )
IQ <= ((D&EN)|(IQ&(~EN)));
IQN <= ~(((D&EN)|(IQ&(~EN))));
end
endmodule

View File

@ -30,11 +30,11 @@ TESTS := $(addprefix $(BINTEST)/, $(basename $(ALLTESTFILE:%Test.cc=%Test.o)))
all: prepare $(TESTS) run-tests
$(BINTEST)/%: $(OBJTEST)/%.o
$(BINTEST)/%: $(OBJTEST)/%.o | prepare
$(CXX) -L$(ROOTPATH) $(RPATH) $(LINKFLAGS) -o $@ $^ $(LIBS) \
$(GTEST_LDFLAGS) $(EXTRAFLAGS)
$(OBJTEST)/%.o: $(basename $(subst $(OBJTEST),.,%)).cc
$(OBJTEST)/%.o: $(basename $(subst $(OBJTEST),.,%)).cc | prepare
$(CXX) -o $@ -c -I$(ROOTPATH) $(CPPFLAGS) $(CXXFLAGS) $(GTEST_CXXFLAGS) $^
.PHONY: prepare run-tests clean

View File

@ -13,7 +13,7 @@ namespace RTLIL {
void checkAll(std::initializer_list<std::string> expressions, std::string expected) {
for (const auto& e : expressions) {
auto helper = LibertyExpression::Lexer(e);
auto tree_s = LibertyExpression::parse(helper).str();
auto tree_s = LibertyExpression::parse(helper).sexpr_str();
EXPECT_EQ(tree_s, expected);
}
}
@ -82,6 +82,11 @@ namespace RTLIL {
}, "(and (pin \"x\")\n"
" (not (pin \"y\")))"
);
checkAll({
"( D & EN )",
}, "(and (pin \"D\")\n"
" (pin \"EN\"))"
);
}
}