mirror of https://github.com/YosysHQ/yosys.git
Merge updated master into krys/docs
This commit is contained in:
commit
afe8eff790
|
@ -0,0 +1,30 @@
|
|||
name: WASI Build
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
wasi:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Build
|
||||
run: |
|
||||
WASI_SDK=wasi-sdk-19.0
|
||||
WASI_SDK_URL=https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-19/wasi-sdk-19.0-linux.tar.gz
|
||||
if ! [ -d ${WASI_SDK} ]; then curl -L ${WASI_SDK_URL} | tar xzf -; fi
|
||||
|
||||
mkdir -p build
|
||||
cat > build/Makefile.conf <<END
|
||||
export PATH := $(pwd)/${WASI_SDK}/bin:${PATH}
|
||||
WASI_SYSROOT := $(pwd)/${WASI_SDK}/share/wasi-sysroot
|
||||
|
||||
CONFIG := wasi
|
||||
PREFIX := /
|
||||
|
||||
ENABLE_TCL := 0
|
||||
ENABLE_READLINE := 0
|
||||
ENABLE_PLUGINS := 0
|
||||
ENABLE_ZLIB := 0
|
||||
END
|
||||
|
||||
make -C build -f ../Makefile CXX=clang -j$(nproc)
|
|
@ -1,5 +1,6 @@
|
|||
*.o
|
||||
*.d
|
||||
*.dwo
|
||||
.*.swp
|
||||
*.gch
|
||||
*.gcda
|
||||
|
|
42
CHANGELOG
42
CHANGELOG
|
@ -2,9 +2,49 @@
|
|||
List of major changes and improvements between releases
|
||||
=======================================================
|
||||
|
||||
Yosys 0.34 .. Yosys 0.35-dev
|
||||
Yosys 0.36 .. Yosys 0.37-dev
|
||||
--------------------------
|
||||
|
||||
Yosys 0.35 .. Yosys 0.36
|
||||
--------------------------
|
||||
* New commands and options
|
||||
- Added option "--" to pass arguments down to tcl when using -c option.
|
||||
- Added ability on MacOS and Windows to pass options after arguments on cli.
|
||||
- Added option "-cmp2softlogic" to synth_lattice.
|
||||
- Added option "-lowpower" to "booth" pass.
|
||||
|
||||
* QuickLogic support
|
||||
- Added "K6N10f" support.
|
||||
- Added "-nodsp", "-nocarry", "-nobram" and "-bramtypes" options to
|
||||
"synth_quicklogic" pass.
|
||||
- Added "ql_bram_merge" pass to merge 18K BRAM cells into TDP36K.
|
||||
- Added "ql_bram_types" pass to change TDP36K depending on configuration.
|
||||
- Added "ql_dsp_io_regs" pass to update QL_DSP2 depending on configuration.
|
||||
- Added "ql_dsp_macc" pass to infer multiplier-accumulator DSP cells.
|
||||
- Added "ql_dsp_simd" pass to merge DSP pairs to operate in SIMD mode.
|
||||
|
||||
* ECP5,iCE40 and Gowin support
|
||||
- Enabled abc9 by default, added "-noabc9" option to disable.
|
||||
|
||||
* MachXO3 support
|
||||
- Quality of results improvements.
|
||||
- Enabled "booth" pass by default for it in "synth_lattice".
|
||||
|
||||
* Various
|
||||
- Improved "peepopt" by adding shiftadd pattern support.
|
||||
- Added "--incremental" mode to smtbmc.
|
||||
|
||||
Yosys 0.34 .. Yosys 0.35
|
||||
--------------------------
|
||||
* Various
|
||||
- Improvements on "peepopt" shiftmul matcher.
|
||||
- Improvements on "ram_style" attributes handling.
|
||||
|
||||
* Verific support
|
||||
- Improved static elaboration for VHDL and mixed HDL designs.
|
||||
- Expose "hdlname" attribute with original module name.
|
||||
- Expose "architecture" attribute with VHDL architecture name.
|
||||
|
||||
Yosys 0.33 .. Yosys 0.34
|
||||
--------------------------
|
||||
* New commands and options
|
||||
|
|
25
Makefile
25
Makefile
|
@ -141,7 +141,7 @@ LDLIBS += -lrt
|
|||
endif
|
||||
endif
|
||||
|
||||
YOSYS_VER := 0.34+55
|
||||
YOSYS_VER := 0.36+13
|
||||
|
||||
# Note: We arrange for .gitcommit to contain the (short) commit hash in
|
||||
# tarballs generated with git-archive(1) using .gitattributes. The git repo
|
||||
|
@ -157,7 +157,7 @@ endif
|
|||
OBJS = kernel/version_$(GIT_REV).o
|
||||
|
||||
bumpversion:
|
||||
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 4a1b559.. | wc -l`/;" Makefile
|
||||
sed -i "/^YOSYS_VER := / s/+[0-9][0-9]*$$/+`git log --oneline 8f07a0d.. | wc -l`/;" Makefile
|
||||
|
||||
# set 'ABCREV = default' to use abc/ as it is
|
||||
#
|
||||
|
@ -321,11 +321,11 @@ AR = $(WASI_SDK)/bin/ar
|
|||
RANLIB = $(WASI_SDK)/bin/ranlib
|
||||
WASIFLAGS := --sysroot $(WASI_SDK)/share/wasi-sysroot $(WASIFLAGS)
|
||||
endif
|
||||
CXXFLAGS := $(WASIFLAGS) -std=$(CXXSTD) -Os $(filter-out -fPIC,$(CXXFLAGS))
|
||||
CXXFLAGS := $(WASIFLAGS) -std=$(CXXSTD) -Os -D_WASI_EMULATED_PROCESS_CLOCKS $(filter-out -fPIC,$(CXXFLAGS))
|
||||
LDFLAGS := $(WASIFLAGS) -Wl,-z,stack-size=1048576 $(filter-out -rdynamic,$(LDFLAGS))
|
||||
LDLIBS := $(filter-out -lrt,$(LDLIBS))
|
||||
LDLIBS := -lwasi-emulated-process-clocks $(filter-out -lrt,$(LDLIBS))
|
||||
ABCMKARGS += AR="$(AR)" RANLIB="$(RANLIB)"
|
||||
ABCMKARGS += ARCHFLAGS="$(WASIFLAGS) -DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING -DABC_NO_RLIMIT -Wno-c++11-narrowing"
|
||||
ABCMKARGS += ARCHFLAGS="$(WASIFLAGS) -D_WASI_EMULATED_PROCESS_CLOCKS -DABC_USE_STDINT_H -DABC_NO_DYNAMIC_LINKING -DABC_NO_RLIMIT -Wno-c++11-narrowing"
|
||||
ABCMKARGS += OPTFLAGS="-Os"
|
||||
EXE = .wasm
|
||||
|
||||
|
@ -647,12 +647,12 @@ $(eval $(call add_include_file,frontends/ast/ast.h))
|
|||
$(eval $(call add_include_file,frontends/ast/ast_binding.h))
|
||||
$(eval $(call add_include_file,frontends/blif/blifparse.h))
|
||||
$(eval $(call add_include_file,backends/rtlil/rtlil_backend.h))
|
||||
$(eval $(call add_include_file,backends/cxxrtl/cxxrtl.h))
|
||||
$(eval $(call add_include_file,backends/cxxrtl/cxxrtl_vcd.h))
|
||||
$(eval $(call add_include_file,backends/cxxrtl/cxxrtl_capi.cc))
|
||||
$(eval $(call add_include_file,backends/cxxrtl/cxxrtl_capi.h))
|
||||
$(eval $(call add_include_file,backends/cxxrtl/cxxrtl_vcd_capi.cc))
|
||||
$(eval $(call add_include_file,backends/cxxrtl/cxxrtl_vcd_capi.h))
|
||||
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/cxxrtl.h))
|
||||
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/cxxrtl_vcd.h))
|
||||
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.cc))
|
||||
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi.h))
|
||||
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.cc))
|
||||
$(eval $(call add_include_file,backends/cxxrtl/runtime/cxxrtl/capi/cxxrtl_capi_vcd.h))
|
||||
|
||||
OBJS += kernel/driver.o kernel/register.o kernel/rtlil.o kernel/log.o kernel/calc.o kernel/yosys.o
|
||||
OBJS += kernel/binding.o
|
||||
|
@ -880,7 +880,8 @@ endif
|
|||
+cd tests/arch/gowin && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/arch/intel_alm && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/arch/nexus && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/arch/quicklogic && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/arch/quicklogic/pp3 && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/arch/quicklogic/qlf_k6n10f && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/arch/gatemate && bash run-test.sh $(SEEDOPT)
|
||||
+cd tests/rpc && bash run-test.sh
|
||||
+cd tests/memfile && bash run-test.sh
|
||||
|
|
|
@ -2536,7 +2536,7 @@ struct CxxrtlWorker {
|
|||
f << "#define " << include_guard << "\n";
|
||||
f << "\n";
|
||||
if (top_module != nullptr && debug_info) {
|
||||
f << "#include <backends/cxxrtl/cxxrtl_capi.h>\n";
|
||||
f << "#include <cxxrtl/capi/cxxrtl_capi.h>\n";
|
||||
f << "\n";
|
||||
f << "#ifdef __cplusplus\n";
|
||||
f << "extern \"C\" {\n";
|
||||
|
@ -2554,7 +2554,7 @@ struct CxxrtlWorker {
|
|||
}
|
||||
f << "#ifdef __cplusplus\n";
|
||||
f << "\n";
|
||||
f << "#include <backends/cxxrtl/cxxrtl.h>\n";
|
||||
f << "#include <cxxrtl/cxxrtl.h>\n";
|
||||
f << "\n";
|
||||
f << "using namespace cxxrtl;\n";
|
||||
f << "\n";
|
||||
|
@ -2573,17 +2573,17 @@ struct CxxrtlWorker {
|
|||
if (split_intf)
|
||||
f << "#include \"" << intf_filename << "\"\n";
|
||||
else
|
||||
f << "#include <backends/cxxrtl/cxxrtl.h>\n";
|
||||
f << "#include <cxxrtl/cxxrtl.h>\n";
|
||||
if (has_prints)
|
||||
f << "#include <iostream>\n";
|
||||
f << "\n";
|
||||
f << "#if defined(CXXRTL_INCLUDE_CAPI_IMPL) || \\\n";
|
||||
f << " defined(CXXRTL_INCLUDE_VCD_CAPI_IMPL)\n";
|
||||
f << "#include <backends/cxxrtl/cxxrtl_capi.cc>\n";
|
||||
f << "#include <cxxrtl/capi/cxxrtl_capi.cc>\n";
|
||||
f << "#endif\n";
|
||||
f << "\n";
|
||||
f << "#if defined(CXXRTL_INCLUDE_VCD_CAPI_IMPL)\n";
|
||||
f << "#include <backends/cxxrtl/cxxrtl_vcd_capi.cc>\n";
|
||||
f << "#include <cxxrtl/capi/cxxrtl_capi_vcd.cc>\n";
|
||||
f << "#endif\n";
|
||||
f << "\n";
|
||||
f << "using namespace cxxrtl_yosys;\n";
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
This directory contains the runtime components of CXXRTL and should be placed on the include path
|
||||
when building the simulation using the `-I${YOSYS}/backends/cxxrtl/runtime` option. These components
|
||||
are not used in the Yosys binary; they are only built as a part of the simulation binary.
|
||||
|
||||
The interfaces declared in `cxxrtl_capi*.h` contain the stable C API. These interfaces will not be
|
||||
changed in backward-incompatible ways unless no other option is available, and any breaking changes
|
||||
will be made in a way that causes the downstream code to fail in a visible way. The ABI of these
|
||||
interfaces is considered stable as well, and it will not use features complicating its use via
|
||||
libraries such as libffi or ctypes.
|
||||
|
||||
The implementations in `cxxrtl_capi*.cc` are considered private; they are still placed in the include
|
||||
path to enable build-system-less builds (where the CXXRTL runtime component is included in the C++
|
||||
file of the simulation toplevel).
|
||||
|
||||
The interfaces declared in `cxxrtl*.h` (without `capi`) are unstable and may change without notice.
|
||||
|
||||
For clarity, all of the files in this directory and its subdirectories have unique names regardless
|
||||
of the directory where they are placed.
|
|
@ -16,10 +16,10 @@
|
|||
*
|
||||
*/
|
||||
|
||||
// This file is a part of the CXXRTL C API. It should be used together with `cxxrtl_capi.h`.
|
||||
// This file is a part of the CXXRTL C API. It should be used together with `cxxrtl/capi/cxxrtl_capi.h`.
|
||||
|
||||
#include <backends/cxxrtl/cxxrtl.h>
|
||||
#include <backends/cxxrtl/cxxrtl_capi.h>
|
||||
#include <cxxrtl/capi/cxxrtl_capi.h>
|
||||
#include <cxxrtl/cxxrtl.h>
|
||||
|
||||
struct _cxxrtl_handle {
|
||||
std::unique_ptr<cxxrtl::module> module;
|
|
@ -16,10 +16,10 @@
|
|||
*
|
||||
*/
|
||||
|
||||
// This file is a part of the CXXRTL C API. It should be used together with `cxxrtl_vcd_capi.h`.
|
||||
// This file is a part of the CXXRTL C API. It should be used together with `cxxrtl/capi/cxxrtl_capi_vcd.h`.
|
||||
|
||||
#include <backends/cxxrtl/cxxrtl_vcd.h>
|
||||
#include <backends/cxxrtl/cxxrtl_vcd_capi.h>
|
||||
#include <cxxrtl/capi/cxxrtl_capi_vcd.h>
|
||||
#include <cxxrtl/cxxrtl_vcd.h>
|
||||
|
||||
extern const cxxrtl::debug_items &cxxrtl_debug_items_from_handle(cxxrtl_handle handle);
|
||||
|
|
@ -16,8 +16,8 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#ifndef CXXRTL_VCD_CAPI_H
|
||||
#define CXXRTL_VCD_CAPI_H
|
||||
#ifndef CXXRTL_CAPI_VCD_H
|
||||
#define CXXRTL_CAPI_VCD_H
|
||||
|
||||
// This file is a part of the CXXRTL C API. It should be used together with `cxxrtl_vcd_capi.cc`.
|
||||
//
|
||||
|
@ -27,7 +27,7 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <backends/cxxrtl/cxxrtl_capi.h>
|
||||
#include <cxxrtl/capi/cxxrtl_capi.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
|
@ -39,7 +39,8 @@
|
|||
#include <functional>
|
||||
#include <sstream>
|
||||
|
||||
#include <backends/cxxrtl/cxxrtl_capi.h>
|
||||
// `cxxrtl::debug_item` has to inherit from `cxxrtl_object` to satisfy strict aliasing requirements.
|
||||
#include <cxxrtl/capi/cxxrtl_capi.h>
|
||||
|
||||
#ifndef __has_attribute
|
||||
# define __has_attribute(x) 0
|
||||
|
@ -507,25 +508,18 @@ struct value : public expr_base<value<Bits>> {
|
|||
size_t count = 0;
|
||||
for (size_t n = 0; n < chunks; n++) {
|
||||
chunk::type x = data[chunks - 1 - n];
|
||||
if (x == 0) {
|
||||
// First add to `count` as if the chunk is zero
|
||||
count += (n == 0 ? Bits % chunk::bits : chunk::bits);
|
||||
} else {
|
||||
// This loop implements the find first set idiom as recognized by LLVM.
|
||||
for (; x != 0; count++)
|
||||
// If the chunk isn't zero, correct the `count` value and return
|
||||
if (x != 0) {
|
||||
for (; x != 0; count--)
|
||||
x >>= 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
@ -584,82 +578,36 @@ struct value : public expr_base<value<Bits>> {
|
|||
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;
|
||||
std::pair<value<Bits>, value<Bits>> udivmod(value<Bits> divisor) const {
|
||||
value<Bits> quotient;
|
||||
value<Bits> dividend = *this;
|
||||
if (dividend.ucmp(divisor))
|
||||
return {/*quotient=*/value<Bits>{0u}, /*remainder=*/dividend};
|
||||
int64_t divisor_shift = divisor.ctlz() - dividend.ctlz();
|
||||
assert(divisor_shift >= 0);
|
||||
divisor = divisor.shl(value<Bits>{(chunk::type) divisor_shift});
|
||||
for (size_t step = 0; step <= divisor_shift; step++) {
|
||||
quotient = quotient.shl(value<Bits>{1u});
|
||||
if (!dividend.ucmp(divisor)) {
|
||||
dividend = dividend.sub(divisor);
|
||||
quotient.set_bit(0, true);
|
||||
}
|
||||
divisor = divisor.shr(value<Bits>{1u});
|
||||
}
|
||||
return {quotient, /*remainder=*/dividend};
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
std::pair<value<Bits>, value<Bits>> sdivmod(const value<Bits> &other) const {
|
||||
value<Bits + 1> quotient;
|
||||
value<Bits + 1> remainder;
|
||||
value<Bits + 1> dividend = sext<Bits + 1>();
|
||||
value<Bits + 1> divisor = other.template sext<Bits + 1>();
|
||||
if (dividend.is_neg()) dividend = dividend.neg();
|
||||
if (divisor.is_neg()) divisor = divisor.neg();
|
||||
std::tie(quotient, remainder) = dividend.udivmod(divisor);
|
||||
if (dividend.is_neg() != divisor.is_neg()) quotient = quotient.neg();
|
||||
if (dividend.is_neg()) remainder = remainder.neg();
|
||||
return {quotient.template trunc<Bits>(), remainder.template trunc<Bits>()};
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -848,9 +796,12 @@ std::ostream &operator<<(std::ostream &os, const value_formatted<Bits> &vf)
|
|||
if (val.is_zero())
|
||||
buf += '0';
|
||||
while (!val.is_zero()) {
|
||||
value<Bits> quotient;
|
||||
val.divideWithRemainder(value<Bits>{10u}, quotient);
|
||||
buf += '0' + val.template trunc<(Bits > 4 ? 4 : Bits)>().val().template get<uint8_t>();
|
||||
value<Bits> quotient, remainder;
|
||||
if (Bits >= 4)
|
||||
std::tie(quotient, remainder) = val.udivmod(value<Bits>{10u});
|
||||
else
|
||||
std::tie(quotient, remainder) = std::make_pair(value<Bits>{0u}, val);
|
||||
buf += '0' + remainder.template trunc<(Bits > 4 ? 4 : Bits)>().val().template get<uint8_t>();
|
||||
val = quotient;
|
||||
}
|
||||
if (negative || vf.plus)
|
||||
|
@ -1734,35 +1685,23 @@ CXXRTL_ALWAYS_INLINE
|
|||
std::pair<value<BitsY>, value<BitsY>> divmod_uu(const value<BitsA> &a, const value<BitsB> &b) {
|
||||
constexpr size_t Bits = max(BitsY, max(BitsA, BitsB));
|
||||
value<Bits> quotient;
|
||||
value<Bits> remainder;
|
||||
value<Bits> dividend = a.template zext<Bits>();
|
||||
value<Bits> divisor = b.template zext<Bits>();
|
||||
if (dividend.ucmp(divisor))
|
||||
return {/*quotient=*/value<BitsY> { 0u }, /*remainder=*/dividend.template trunc<BitsY>()};
|
||||
uint32_t divisor_shift = dividend.ctlz() - divisor.ctlz();
|
||||
divisor = divisor.shl(value<32> { divisor_shift });
|
||||
for (size_t step = 0; step <= divisor_shift; step++) {
|
||||
quotient = quotient.shl(value<1> { 1u });
|
||||
if (!dividend.ucmp(divisor)) {
|
||||
dividend = dividend.sub(divisor);
|
||||
quotient.set_bit(0, true);
|
||||
}
|
||||
divisor = divisor.shr(value<1> { 1u });
|
||||
}
|
||||
return {quotient.template trunc<BitsY>(), /*remainder=*/dividend.template trunc<BitsY>()};
|
||||
std::tie(quotient, remainder) = dividend.udivmod(divisor);
|
||||
return {quotient.template trunc<BitsY>(), remainder.template trunc<BitsY>()};
|
||||
}
|
||||
|
||||
template<size_t BitsY, size_t BitsA, size_t BitsB>
|
||||
CXXRTL_ALWAYS_INLINE
|
||||
std::pair<value<BitsY>, value<BitsY>> divmod_ss(const value<BitsA> &a, const value<BitsB> &b) {
|
||||
value<BitsA + 1> ua = a.template sext<BitsA + 1>();
|
||||
value<BitsB + 1> ub = b.template sext<BitsB + 1>();
|
||||
if (ua.is_neg()) ua = ua.neg();
|
||||
if (ub.is_neg()) ub = ub.neg();
|
||||
value<BitsY> y, r;
|
||||
std::tie(y, r) = divmod_uu<BitsY>(ua, ub);
|
||||
if (a.is_neg() != b.is_neg()) y = y.neg();
|
||||
if (a.is_neg()) r = r.neg();
|
||||
return {y, r};
|
||||
constexpr size_t Bits = max(BitsY, max(BitsA, BitsB));
|
||||
value<Bits> quotient;
|
||||
value<Bits> remainder;
|
||||
value<Bits> dividend = a.template sext<Bits>();
|
||||
value<Bits> divisor = b.template sext<Bits>();
|
||||
std::tie(quotient, remainder) = dividend.sdivmod(divisor);
|
||||
return {quotient.template trunc<BitsY>(), remainder.template trunc<BitsY>()};
|
||||
}
|
||||
|
||||
template<size_t BitsY, size_t BitsA, size_t BitsB>
|
|
@ -19,7 +19,7 @@
|
|||
#ifndef CXXRTL_VCD_H
|
||||
#define CXXRTL_VCD_H
|
||||
|
||||
#include <backends/cxxrtl/cxxrtl.h>
|
||||
#include <cxxrtl/cxxrtl.h>
|
||||
|
||||
namespace cxxrtl {
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
|
||||
import os, sys, getopt, re, bisect
|
||||
import os, sys, getopt, re, bisect, json
|
||||
##yosys-sys-path##
|
||||
from smtio import SmtIo, SmtOpts, MkVcd
|
||||
from ywio import ReadWitness, WriteWitness, WitnessValues
|
||||
|
@ -56,6 +56,7 @@ binarymode = False
|
|||
keep_going = False
|
||||
check_witness = False
|
||||
detect_loops = False
|
||||
incremental = None
|
||||
so = SmtOpts()
|
||||
|
||||
|
||||
|
@ -185,6 +186,9 @@ def help():
|
|||
check if states are unique in temporal induction counter examples
|
||||
(this feature is experimental and incomplete)
|
||||
|
||||
--incremental
|
||||
run in incremental mode (experimental)
|
||||
|
||||
""" + so.helpmsg())
|
||||
|
||||
def usage():
|
||||
|
@ -196,7 +200,7 @@ try:
|
|||
opts, args = getopt.getopt(sys.argv[1:], so.shortopts + "t:higcm:", so.longopts +
|
||||
["help", "final-only", "assume-skipped=", "smtc=", "cex=", "aig=", "aig-noheader", "yw=", "btorwit=", "presat",
|
||||
"dump-vcd=", "dump-yw=", "dump-vlogtb=", "vlogtb-top=", "dump-smtc=", "dump-all", "noinfo", "append=",
|
||||
"smtc-init", "smtc-top=", "noinit", "binary", "keep-going", "check-witness", "detect-loops"])
|
||||
"smtc-init", "smtc-top=", "noinit", "binary", "keep-going", "check-witness", "detect-loops", "incremental"])
|
||||
except:
|
||||
usage()
|
||||
|
||||
|
@ -282,6 +286,9 @@ for o, a in opts:
|
|||
check_witness = True
|
||||
elif o == "--detect-loops":
|
||||
detect_loops = True
|
||||
elif o == "--incremental":
|
||||
from smtbmc_incremental import Incremental
|
||||
incremental = Incremental()
|
||||
elif so.handle(o, a):
|
||||
pass
|
||||
else:
|
||||
|
@ -290,7 +297,7 @@ for o, a in opts:
|
|||
if len(args) != 1:
|
||||
usage()
|
||||
|
||||
if sum([tempind, gentrace, covermode]) > 1:
|
||||
if sum([tempind, gentrace, covermode, incremental is not None]) > 1:
|
||||
usage()
|
||||
|
||||
constr_final_start = None
|
||||
|
@ -444,8 +451,10 @@ if noinfo and vcdfile is None and vlogtbfile is None and outconstr is None:
|
|||
smt.produce_models = False
|
||||
|
||||
def print_msg(msg):
|
||||
print("%s %s" % (smt.timestamp(), msg))
|
||||
sys.stdout.flush()
|
||||
if incremental:
|
||||
incremental.print_msg(msg)
|
||||
else:
|
||||
print("%s %s" % (smt.timestamp(), msg), flush=True)
|
||||
|
||||
print_msg("Solver: %s" % (so.solver))
|
||||
|
||||
|
@ -640,10 +649,9 @@ if aimfile is not None:
|
|||
num_steps = max(num_steps, step+2)
|
||||
step += 1
|
||||
|
||||
if inywfile is not None:
|
||||
if not got_topt:
|
||||
skip_steps = 0
|
||||
num_steps = 0
|
||||
def ywfile_constraints(inywfile, constr_assumes, map_steps=None, skip_x=False):
|
||||
if map_steps is None:
|
||||
map_steps = {}
|
||||
|
||||
with open(inywfile, "r") as f:
|
||||
inyw = ReadWitness(f)
|
||||
|
@ -662,10 +670,14 @@ if inywfile is not None:
|
|||
addr_re = re.compile(r'\\\[[0-9]+\]$')
|
||||
bits_re = re.compile(r'[01?]*$')
|
||||
|
||||
max_t = -1
|
||||
|
||||
for t, step in inyw.steps():
|
||||
present_signals, missing = step.present_signals(inyw.sigmap)
|
||||
for sig in present_signals:
|
||||
bits = step[sig]
|
||||
if skip_x:
|
||||
bits = bits.replace('x', '?')
|
||||
if not bits_re.match(bits):
|
||||
raise ValueError("unsupported bit value in Yosys witness file")
|
||||
|
||||
|
@ -684,7 +696,7 @@ if inywfile is not None:
|
|||
if common_end <= common_offset:
|
||||
continue
|
||||
|
||||
smt_expr = smt.witness_net_expr(topmod, f"s{t}", wire)
|
||||
smt_expr = smt.witness_net_expr(topmod, f"s{map_steps.get(t, t)}", wire)
|
||||
|
||||
if not smt_bool:
|
||||
slice_high = common_end - offset - 1
|
||||
|
@ -714,7 +726,7 @@ if inywfile is not None:
|
|||
for mem in smt_mems[sig.memory_path]:
|
||||
width, size, bv = mem["width"], mem["size"], mem["statebv"]
|
||||
|
||||
smt_expr = smt.net_expr(topmod, f"s{t}", mem["smtpath"])
|
||||
smt_expr = smt.net_expr(topmod, f"s{map_steps.get(t, t)}", mem["smtpath"])
|
||||
|
||||
if bv:
|
||||
word_low = sig.memory_addr * width
|
||||
|
@ -738,11 +750,21 @@ if inywfile is not None:
|
|||
|
||||
smt_constr = "(= %s #b%s)" % (smt_expr, bit_slice)
|
||||
constr_assumes[t].append((inywfile, smt_constr))
|
||||
max_t = t
|
||||
|
||||
return max_t
|
||||
|
||||
if inywfile is not None:
|
||||
if not got_topt:
|
||||
skip_steps = 0
|
||||
num_steps = 0
|
||||
|
||||
max_t = ywfile_constraints(inywfile, constr_assumes)
|
||||
|
||||
if not got_topt:
|
||||
if not check_witness:
|
||||
skip_steps = max(skip_steps, t)
|
||||
num_steps = max(num_steps, t+1)
|
||||
skip_steps = max(skip_steps, max_t)
|
||||
num_steps = max(num_steps, max_t+1)
|
||||
|
||||
if btorwitfile is not None:
|
||||
with open(btorwitfile, "r") as f:
|
||||
|
@ -841,7 +863,7 @@ if btorwitfile is not None:
|
|||
skip_steps = step
|
||||
num_steps = step+1
|
||||
|
||||
def collect_mem_trace_data(steps_start, steps_stop, vcd=None):
|
||||
def collect_mem_trace_data(steps, vcd=None):
|
||||
mem_trace_data = dict()
|
||||
|
||||
for mempath in sorted(smt.hiermems(topmod)):
|
||||
|
@ -849,16 +871,16 @@ def collect_mem_trace_data(steps_start, steps_stop, vcd=None):
|
|||
|
||||
expr_id = list()
|
||||
expr_list = list()
|
||||
for i in range(steps_start, steps_stop):
|
||||
for seq, i in enumerate(steps):
|
||||
for j in range(rports):
|
||||
expr_id.append(('R', i-steps_start, j, 'A'))
|
||||
expr_id.append(('R', i-steps_start, j, 'D'))
|
||||
expr_id.append(('R', seq, j, 'A'))
|
||||
expr_id.append(('R', seq, j, 'D'))
|
||||
expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "R%dA" % j))
|
||||
expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "R%dD" % j))
|
||||
for j in range(wports):
|
||||
expr_id.append(('W', i-steps_start, j, 'A'))
|
||||
expr_id.append(('W', i-steps_start, j, 'D'))
|
||||
expr_id.append(('W', i-steps_start, j, 'M'))
|
||||
expr_id.append(('W', seq, j, 'A'))
|
||||
expr_id.append(('W', seq, j, 'D'))
|
||||
expr_id.append(('W', seq, j, 'M'))
|
||||
expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dA" % j))
|
||||
expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dD" % j))
|
||||
expr_list.append(smt.mem_expr(topmod, "s%d" % i, mempath, "W%dM" % j))
|
||||
|
@ -943,14 +965,14 @@ def collect_mem_trace_data(steps_start, steps_stop, vcd=None):
|
|||
netpath[-1] += "<%0*x>" % ((len(addr)+3) // 4, int_addr)
|
||||
vcd.add_net([topmod] + netpath, width)
|
||||
|
||||
for i in range(steps_start, steps_stop):
|
||||
for seq, i in enumerate(steps):
|
||||
if i not in mem_trace_data:
|
||||
mem_trace_data[i] = list()
|
||||
mem_trace_data[i].append((netpath, int_addr, "".join(tdata[i-steps_start])))
|
||||
mem_trace_data[i].append((netpath, int_addr, "".join(tdata[seq])))
|
||||
|
||||
return mem_trace_data
|
||||
|
||||
def write_vcd_trace(steps_start, steps_stop, index):
|
||||
def write_vcd_trace(steps, index, seq_time=False):
|
||||
filename = vcdfile.replace("%", index)
|
||||
print_msg("Writing trace to VCD file: %s" % (filename))
|
||||
|
||||
|
@ -971,10 +993,10 @@ def write_vcd_trace(steps_start, steps_stop, index):
|
|||
vcd.add_clock([topmod] + netpath, edge)
|
||||
path_list.append(netpath)
|
||||
|
||||
mem_trace_data = collect_mem_trace_data(steps_start, steps_stop, vcd)
|
||||
mem_trace_data = collect_mem_trace_data(steps, vcd)
|
||||
|
||||
for i in range(steps_start, steps_stop):
|
||||
vcd.set_time(i)
|
||||
for seq, i in enumerate(steps):
|
||||
vcd.set_time(seq if seq_time else i)
|
||||
value_list = smt.get_net_bin_list(topmod, path_list, "s%d" % i)
|
||||
for path, value in zip(path_list, value_list):
|
||||
vcd.set_net([topmod] + path, value)
|
||||
|
@ -982,7 +1004,14 @@ def write_vcd_trace(steps_start, steps_stop, index):
|
|||
for path, addr, value in mem_trace_data[i]:
|
||||
vcd.set_net([topmod] + path, value)
|
||||
|
||||
vcd.set_time(steps_stop)
|
||||
if seq_time:
|
||||
end_time = len(steps)
|
||||
elif steps:
|
||||
end_time = steps[-1] + 1
|
||||
else:
|
||||
end_time = 0
|
||||
|
||||
vcd.set_time(end_time)
|
||||
|
||||
def detect_state_loop(steps_start, steps_stop):
|
||||
print_msg(f"Checking for loops in found induction counter example")
|
||||
|
@ -1027,7 +1056,7 @@ def escape_identifier(identifier):
|
|||
|
||||
|
||||
|
||||
def write_vlogtb_trace(steps_start, steps_stop, index):
|
||||
def write_vlogtb_trace(steps, index):
|
||||
filename = vlogtbfile.replace("%", index)
|
||||
print_msg("Writing trace to Verilog testbench: %s" % (filename))
|
||||
|
||||
|
@ -1092,7 +1121,7 @@ def write_vlogtb_trace(steps_start, steps_stop, index):
|
|||
print(" initial begin", file=f)
|
||||
|
||||
regs = sorted(smt.hiernets(vlogtb_topmod, regs_only=True))
|
||||
regvals = smt.get_net_bin_list(vlogtb_topmod, regs, vlogtb_state.replace("@@step_idx@@", str(steps_start)))
|
||||
regvals = smt.get_net_bin_list(vlogtb_topmod, regs, vlogtb_state.replace("@@step_idx@@", str(steps[0])))
|
||||
|
||||
print("`ifndef VERILATOR", file=f)
|
||||
print(" #1;", file=f)
|
||||
|
@ -1107,7 +1136,7 @@ def write_vlogtb_trace(steps_start, steps_stop, index):
|
|||
anyconsts = sorted(smt.hieranyconsts(vlogtb_topmod))
|
||||
for info in anyconsts:
|
||||
if info[3] is not None:
|
||||
modstate = smt.net_expr(vlogtb_topmod, vlogtb_state.replace("@@step_idx@@", str(steps_start)), info[0])
|
||||
modstate = smt.net_expr(vlogtb_topmod, vlogtb_state.replace("@@step_idx@@", str(steps[0])), info[0])
|
||||
value = smt.bv2bin(smt.get("(|%s| %s)" % (info[1], modstate)))
|
||||
print(" UUT.%s = %d'b%s;" % (".".join(escape_identifier(info[0] + [info[3]])), len(value), value), file=f);
|
||||
|
||||
|
@ -1117,7 +1146,7 @@ def write_vlogtb_trace(steps_start, steps_stop, index):
|
|||
|
||||
addr_expr_list = list()
|
||||
data_expr_list = list()
|
||||
for i in range(steps_start, steps_stop):
|
||||
for i in steps:
|
||||
for j in range(rports):
|
||||
addr_expr_list.append(smt.mem_expr(vlogtb_topmod, vlogtb_state.replace("@@step_idx@@", str(i)), mempath, "R%dA" % j))
|
||||
data_expr_list.append(smt.mem_expr(vlogtb_topmod, vlogtb_state.replace("@@step_idx@@", str(i)), mempath, "R%dD" % j))
|
||||
|
@ -1138,7 +1167,7 @@ def write_vlogtb_trace(steps_start, steps_stop, index):
|
|||
print("", file=f)
|
||||
anyseqs = sorted(smt.hieranyseqs(vlogtb_topmod))
|
||||
|
||||
for i in range(steps_start, steps_stop):
|
||||
for i in steps:
|
||||
pi_names = [[name] for name, _ in primary_inputs if name not in clock_inputs]
|
||||
pi_values = smt.get_net_bin_list(vlogtb_topmod, pi_names, vlogtb_state.replace("@@step_idx@@", str(i)))
|
||||
|
||||
|
@ -1170,14 +1199,14 @@ def write_vlogtb_trace(steps_start, steps_stop, index):
|
|||
print(" end", file=f)
|
||||
print(" always @(posedge clock) begin", file=f)
|
||||
|
||||
print(" genclock <= cycle < %d;" % (steps_stop-1), file=f)
|
||||
print(" genclock <= cycle < %d;" % (steps[-1]), file=f)
|
||||
print(" cycle <= cycle + 1;", file=f)
|
||||
print(" end", file=f)
|
||||
|
||||
print("endmodule", file=f)
|
||||
|
||||
|
||||
def write_constr_trace(steps_start, steps_stop, index):
|
||||
def write_constr_trace(steps, index):
|
||||
filename = outconstr.replace("%", index)
|
||||
print_msg("Writing trace to constraints file: %s" % (filename))
|
||||
|
||||
|
@ -1194,7 +1223,7 @@ def write_constr_trace(steps_start, steps_stop, index):
|
|||
constr_prefix = smtctop[1] + "."
|
||||
|
||||
if smtcinit:
|
||||
steps_start = steps_stop - 1
|
||||
steps = [steps[-1]]
|
||||
|
||||
with open(filename, "w") as f:
|
||||
primary_inputs = list()
|
||||
|
@ -1203,13 +1232,13 @@ def write_constr_trace(steps_start, steps_stop, index):
|
|||
width = smt.modinfo[constr_topmod].wsize[name]
|
||||
primary_inputs.append((name, width))
|
||||
|
||||
if steps_start == 0 or smtcinit:
|
||||
if steps[0] == 0 or smtcinit:
|
||||
print("initial", file=f)
|
||||
else:
|
||||
print("state %d" % steps_start, file=f)
|
||||
print("state %d" % steps[0], file=f)
|
||||
|
||||
regnames = sorted(smt.hiernets(constr_topmod, regs_only=True))
|
||||
regvals = smt.get_net_list(constr_topmod, regnames, constr_state.replace("@@step_idx@@", str(steps_start)))
|
||||
regvals = smt.get_net_list(constr_topmod, regnames, constr_state.replace("@@step_idx@@", str(steps[0])))
|
||||
|
||||
for name, val in zip(regnames, regvals):
|
||||
print("assume (= [%s%s] %s)" % (constr_prefix, ".".join(name), val), file=f)
|
||||
|
@ -1220,7 +1249,7 @@ def write_constr_trace(steps_start, steps_stop, index):
|
|||
|
||||
addr_expr_list = list()
|
||||
data_expr_list = list()
|
||||
for i in range(steps_start, steps_stop):
|
||||
for i in steps:
|
||||
for j in range(rports):
|
||||
addr_expr_list.append(smt.mem_expr(constr_topmod, constr_state.replace("@@step_idx@@", str(i)), mempath, "R%dA" % j))
|
||||
data_expr_list.append(smt.mem_expr(constr_topmod, constr_state.replace("@@step_idx@@", str(i)), mempath, "R%dD" % j))
|
||||
|
@ -1236,7 +1265,7 @@ def write_constr_trace(steps_start, steps_stop, index):
|
|||
for addr, data in addr_data.items():
|
||||
print("assume (= (select [%s%s] %s) %s)" % (constr_prefix, ".".join(mempath), addr, data), file=f)
|
||||
|
||||
for k in range(steps_start, steps_stop):
|
||||
for k in steps:
|
||||
if not smtcinit:
|
||||
print("", file=f)
|
||||
print("state %d" % k, file=f)
|
||||
|
@ -1247,11 +1276,14 @@ def write_constr_trace(steps_start, steps_stop, index):
|
|||
for name, val in zip(pi_names, pi_values):
|
||||
print("assume (= [%s%s] %s)" % (constr_prefix, ".".join(name), val), file=f)
|
||||
|
||||
def write_yw_trace(steps_start, steps_stop, index, allregs=False):
|
||||
def write_yw_trace(steps, index, allregs=False, filename=None):
|
||||
if filename is None:
|
||||
if outywfile is None:
|
||||
return
|
||||
filename = outywfile.replace("%", index)
|
||||
print_msg("Writing trace to Yosys witness file: %s" % (filename))
|
||||
|
||||
mem_trace_data = collect_mem_trace_data(steps_start, steps_stop)
|
||||
mem_trace_data = collect_mem_trace_data(steps)
|
||||
|
||||
with open(filename, "w") as f:
|
||||
inits, seqs, clocks, mems = smt.hierwitness(topmod, allregs)
|
||||
|
@ -1295,10 +1327,10 @@ def write_yw_trace(steps_start, steps_stop, index, allregs=False):
|
|||
sig = yw.add_sig(word_path, overlap_start, overlap_end - overlap_start, True)
|
||||
mem_init_values.append((sig, overlap_bits.replace("x", "?")))
|
||||
|
||||
for k in range(steps_start, steps_stop):
|
||||
for i, k in enumerate(steps):
|
||||
step_values = WitnessValues()
|
||||
|
||||
if k == steps_start:
|
||||
if not i:
|
||||
for sig, value in mem_init_values:
|
||||
step_values[sig] = value
|
||||
sigs = inits + seqs
|
||||
|
@ -1314,17 +1346,24 @@ def write_yw_trace(steps_start, steps_stop, index, allregs=False):
|
|||
|
||||
|
||||
def write_trace(steps_start, steps_stop, index, allregs=False):
|
||||
if steps_stop is None:
|
||||
steps = steps_start
|
||||
seq_time = True
|
||||
else:
|
||||
steps = list(range(steps_start, steps_stop))
|
||||
seq_time = False
|
||||
|
||||
if vcdfile is not None:
|
||||
write_vcd_trace(steps_start, steps_stop, index)
|
||||
write_vcd_trace(steps, index, seq_time=seq_time)
|
||||
|
||||
if vlogtbfile is not None:
|
||||
write_vlogtb_trace(steps_start, steps_stop, index)
|
||||
write_vlogtb_trace(steps, index)
|
||||
|
||||
if outconstr is not None:
|
||||
write_constr_trace(steps_start, steps_stop, index)
|
||||
write_constr_trace(steps, index)
|
||||
|
||||
if outywfile is not None:
|
||||
write_yw_trace(steps_start, steps_stop, index, allregs)
|
||||
write_yw_trace(steps, index, allregs)
|
||||
|
||||
|
||||
def print_failed_asserts_worker(mod, state, path, extrainfo, infomap, infokey=()):
|
||||
|
@ -1596,7 +1635,11 @@ def smt_check_sat(expected=["sat", "unsat"]):
|
|||
smt_forall_assert()
|
||||
return smt.check_sat(expected=expected)
|
||||
|
||||
if tempind:
|
||||
|
||||
if incremental:
|
||||
incremental.mainloop()
|
||||
|
||||
elif tempind:
|
||||
retstatus = "FAILED"
|
||||
skip_counter = step_size
|
||||
for step in range(num_steps, -1, -1):
|
||||
|
@ -1954,5 +1997,6 @@ else: # not tempind, covermode
|
|||
smt.write("(exit)")
|
||||
smt.wait()
|
||||
|
||||
if not incremental:
|
||||
print_msg("Status: %s" % retstatus)
|
||||
sys.exit(0 if retstatus == "PASSED" else 1)
|
||||
|
|
|
@ -0,0 +1,389 @@
|
|||
from collections import defaultdict
|
||||
import json
|
||||
import typing
|
||||
from functools import partial
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
import smtbmc
|
||||
else:
|
||||
import sys
|
||||
|
||||
smtbmc = sys.modules["__main__"]
|
||||
|
||||
|
||||
class InteractiveError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Incremental:
|
||||
def __init__(self):
|
||||
self.traceidx = 0
|
||||
|
||||
self.state_set = set()
|
||||
self.map_cache = {}
|
||||
|
||||
self._cached_hierwitness = {}
|
||||
self._witness_index = None
|
||||
|
||||
self._yw_constraints = {}
|
||||
|
||||
def setup(self):
|
||||
generic_assert_map = smtbmc.get_assert_map(
|
||||
smtbmc.topmod, "state", smtbmc.topmod
|
||||
)
|
||||
self.inv_generic_assert_map = {
|
||||
tuple(data[1:]): key for key, data in generic_assert_map.items()
|
||||
}
|
||||
assert len(self.inv_generic_assert_map) == len(generic_assert_map)
|
||||
|
||||
def print_json(self, **kwargs):
|
||||
print(json.dumps(kwargs), flush=True)
|
||||
|
||||
def print_msg(self, msg):
|
||||
self.print_json(msg=msg)
|
||||
|
||||
def get_cached_assert(self, step, name):
|
||||
try:
|
||||
assert_map = self.map_cache[step]
|
||||
except KeyError:
|
||||
assert_map = self.map_cache[step] = smtbmc.get_assert_map(
|
||||
smtbmc.topmod, f"s{step}", smtbmc.topmod
|
||||
)
|
||||
return assert_map[self.inv_generic_assert_map[name]][0]
|
||||
|
||||
def arg_step(self, cmd, declare=False, name="step", optional=False):
|
||||
step = cmd.get(name, None)
|
||||
if step is None and optional:
|
||||
return None
|
||||
if not isinstance(step, int):
|
||||
if optional:
|
||||
raise InteractiveError(f"{name} must be an integer")
|
||||
else:
|
||||
raise InteractiveError(f"integer {name} argument required")
|
||||
if declare and step in self.state_set:
|
||||
raise InteractiveError(f"step {step} already declared")
|
||||
if not declare and step not in self.state_set:
|
||||
raise InteractiveError(f"step {step} not declared")
|
||||
return step
|
||||
|
||||
def expr_arg_len(self, expr, min_len, max_len=-1):
|
||||
if max_len == -1:
|
||||
max_len = min_len
|
||||
arg_len = len(expr) - 1
|
||||
|
||||
if min_len is not None and arg_len < min_len:
|
||||
if min_len == max_len:
|
||||
raise (
|
||||
f"{json.dumps(expr[0])} expression must have "
|
||||
f"{min_len} argument{'s' if min_len != 1 else ''}"
|
||||
)
|
||||
else:
|
||||
raise (
|
||||
f"{json.dumps(expr[0])} expression must have at least "
|
||||
f"{min_len} argument{'s' if min_len != 1 else ''}"
|
||||
)
|
||||
if max_len is not None and arg_len > max_len:
|
||||
raise (
|
||||
f"{json.dumps(expr[0])} expression can have at most "
|
||||
f"{min_len} argument{'s' if max_len != 1 else ''}"
|
||||
)
|
||||
|
||||
def expr_step(self, expr, smt_out):
|
||||
self.expr_arg_len(expr, 1)
|
||||
step = expr[1]
|
||||
if step not in self.state_set:
|
||||
raise InteractiveError(f"step {step} not declared")
|
||||
smt_out.append(f"s{step}")
|
||||
return "module", smtbmc.topmod
|
||||
|
||||
def expr_mod_constraint(self, expr, smt_out):
|
||||
self.expr_arg_len(expr, 1)
|
||||
position = len(smt_out)
|
||||
smt_out.append(None)
|
||||
arg_sort = self.expr(expr[1], smt_out, required_sort=["module", None])
|
||||
module = arg_sort[1]
|
||||
suffix = expr[0][3:]
|
||||
smt_out[position] = f"(|{module}{suffix}| "
|
||||
smt_out.append(")")
|
||||
return "Bool"
|
||||
|
||||
def expr_mod_constraint2(self, expr, smt_out):
|
||||
self.expr_arg_len(expr, 2)
|
||||
|
||||
position = len(smt_out)
|
||||
smt_out.append(None)
|
||||
arg_sort = self.expr(expr[1], smt_out, required_sort=["module", None])
|
||||
smt_out.append(" ")
|
||||
self.expr(expr[2], smt_out, required_sort=arg_sort)
|
||||
module = arg_sort[1]
|
||||
suffix = expr[0][3:]
|
||||
smt_out[position] = f"(|{module}{suffix}| "
|
||||
smt_out.append(")")
|
||||
return "Bool"
|
||||
|
||||
def expr_not(self, expr, smt_out):
|
||||
self.expr_arg_len(expr, 1)
|
||||
|
||||
smt_out.append("(not ")
|
||||
self.expr(expr[1], smt_out, required_sort="Bool")
|
||||
smt_out.append(")")
|
||||
return "Bool"
|
||||
|
||||
def expr_eq(self, expr, smt_out):
|
||||
self.expr_arg_len(expr, 2)
|
||||
|
||||
smt_out.append("(= ")
|
||||
arg_sort = self.expr(expr[1], smt_out)
|
||||
if (
|
||||
smtbmc.smt.unroll
|
||||
and isinstance(arg_sort, (list, tuple))
|
||||
and arg_sort[0] == "module"
|
||||
):
|
||||
raise InteractiveError("state equality not supported in unroll mode")
|
||||
|
||||
smt_out.append(" ")
|
||||
self.expr(expr[2], smt_out, required_sort=arg_sort)
|
||||
smt_out.append(")")
|
||||
return "Bool"
|
||||
|
||||
def expr_andor(self, expr, smt_out):
|
||||
if len(expr) == 1:
|
||||
smt_out.push({"and": "true", "or": "false"}[expr[0]])
|
||||
elif len(expr) == 2:
|
||||
arg_sort = self.expr(expr[1], smt_out)
|
||||
if arg_sort != "Bool":
|
||||
raise InteractiveError(
|
||||
f"arguments of {json.dumps(expr[0])} must have sort Bool"
|
||||
)
|
||||
else:
|
||||
sep = f"({expr[0]} "
|
||||
for arg in expr[1:]:
|
||||
smt_out.append(sep)
|
||||
sep = " "
|
||||
self.expr(arg, smt_out, required_sort="Bool")
|
||||
smt_out.append(")")
|
||||
return "Bool"
|
||||
|
||||
def expr_yw(self, expr, smt_out):
|
||||
if len(expr) == 2:
|
||||
name = None
|
||||
step = expr[1]
|
||||
elif len(expr) == 3:
|
||||
name = expr[1]
|
||||
step = expr[2]
|
||||
|
||||
if step not in self.state_set:
|
||||
raise InteractiveError(f"step {step} not declared")
|
||||
|
||||
if name not in self._yw_constraints:
|
||||
raise InteractiveError(f"no yw file loaded as name {name!r}")
|
||||
|
||||
constraints = self._yw_constraints[name].get(step, [])
|
||||
|
||||
if len(constraints) == 0:
|
||||
smt_out.append("true")
|
||||
elif len(constraints) == 1:
|
||||
smt_out.append(constraints[0])
|
||||
else:
|
||||
sep = "(and "
|
||||
for constraint in constraints:
|
||||
smt_out.append(sep)
|
||||
sep = " "
|
||||
smt_out.append(constraint)
|
||||
smt_out.append(")")
|
||||
|
||||
return "Bool"
|
||||
|
||||
def expr_label(self, expr, smt_out):
|
||||
if len(expr) != 3:
|
||||
raise InteractiveError(f'expected ["!", label, sub_expr], got {expr!r}')
|
||||
label = expr[1]
|
||||
subexpr = expr[2]
|
||||
|
||||
if not isinstance(label, str):
|
||||
raise InteractiveError(f"expression label has to be a string")
|
||||
|
||||
smt_out.append("(! ")
|
||||
smt_out.appedd(label)
|
||||
smt_out.append(" ")
|
||||
|
||||
sort = self.expr(subexpr, smt_out)
|
||||
|
||||
smt_out.append(")")
|
||||
|
||||
return sort
|
||||
|
||||
expr_handlers = {
|
||||
"step": expr_step,
|
||||
"mod_h": expr_mod_constraint,
|
||||
"mod_is": expr_mod_constraint,
|
||||
"mod_i": expr_mod_constraint,
|
||||
"mod_a": expr_mod_constraint,
|
||||
"mod_u": expr_mod_constraint,
|
||||
"mod_t": expr_mod_constraint2,
|
||||
"not": expr_not,
|
||||
"and": expr_andor,
|
||||
"or": expr_andor,
|
||||
"=": expr_eq,
|
||||
"yw": expr_yw,
|
||||
"!": expr_label,
|
||||
}
|
||||
|
||||
def expr(self, expr, smt_out, required_sort=None):
|
||||
if not isinstance(expr, (list, tuple)) or not expr:
|
||||
raise InteractiveError(
|
||||
f"expression must be a non-empty JSON array, found: {json.dumps(expr)}"
|
||||
)
|
||||
name = expr[0]
|
||||
|
||||
handler = self.expr_handlers.get(name)
|
||||
if handler:
|
||||
sort = handler(self, expr, smt_out)
|
||||
|
||||
if required_sort is not None:
|
||||
if isinstance(required_sort, (list, tuple)):
|
||||
if (
|
||||
not isinstance(sort, (list, tuple))
|
||||
or len(sort) != len(required_sort)
|
||||
or any(
|
||||
r is not None and r != s
|
||||
for r, s in zip(required_sort, sort)
|
||||
)
|
||||
):
|
||||
raise InteractiveError(
|
||||
f"required sort {json.dumps(required_sort)} found sort {json.dumps(sort)}"
|
||||
)
|
||||
return sort
|
||||
raise InteractiveError(f"unknown expression {json.dumps(expr[0])}")
|
||||
|
||||
def expr_smt(self, expr, required_sort):
|
||||
smt_out = []
|
||||
self.expr(expr, smt_out, required_sort=required_sort)
|
||||
out = "".join(smt_out)
|
||||
return out
|
||||
|
||||
def cmd_new_step(self, cmd):
|
||||
step = self.arg_step(cmd, declare=True)
|
||||
self.state_set.add(step)
|
||||
smtbmc.smt_state(step)
|
||||
|
||||
def cmd_assert(self, cmd):
|
||||
name = cmd.get("cmd")
|
||||
|
||||
assert_fn = {
|
||||
"assert_antecedent": smtbmc.smt_assert_antecedent,
|
||||
"assert_consequent": smtbmc.smt_assert_consequent,
|
||||
"assert": smtbmc.smt_assert,
|
||||
}[name]
|
||||
|
||||
assert_fn(self.expr_smt(cmd.get("expr"), "Bool"))
|
||||
|
||||
def cmd_push(self, cmd):
|
||||
smtbmc.smt_push()
|
||||
|
||||
def cmd_pop(self, cmd):
|
||||
smtbmc.smt_pop()
|
||||
|
||||
def cmd_check(self, cmd):
|
||||
return smtbmc.smt_check_sat()
|
||||
|
||||
def cmd_design_hierwitness(self, cmd=None):
|
||||
allregs = (cmd is None) or bool(cmd.get("allreges", False))
|
||||
if self._cached_hierwitness[allregs] is not None:
|
||||
return self._cached_hierwitness[allregs]
|
||||
inits, seqs, clocks, mems = smtbmc.smt.hierwitness(smtbmc.topmod, allregs)
|
||||
self._cached_hierwitness[allregs] = result = dict(
|
||||
inits=inits, seqs=seqs, clocks=clocks, mems=mems
|
||||
)
|
||||
return result
|
||||
|
||||
def cmd_write_yw_trace(self, cmd):
|
||||
steps = cmd.get("steps")
|
||||
allregs = bool(cmd.get("allregs", False))
|
||||
|
||||
if steps is None:
|
||||
steps = sorted(self.state_set)
|
||||
|
||||
path = cmd.get("path")
|
||||
|
||||
smtbmc.write_yw_trace(steps, self.traceidx, allregs=allregs, filename=path)
|
||||
|
||||
if path is None:
|
||||
self.traceidx += 1
|
||||
|
||||
def cmd_read_yw_trace(self, cmd):
|
||||
steps = cmd.get("steps")
|
||||
path = cmd.get("path")
|
||||
name = cmd.get("name")
|
||||
skip_x = cmd.get("skip_x", False)
|
||||
if path is None:
|
||||
raise InteractiveError("path required")
|
||||
|
||||
constraints = defaultdict(list)
|
||||
|
||||
if steps is None:
|
||||
steps = sorted(self.state_set)
|
||||
|
||||
map_steps = {i: int(j) for i, j in enumerate(steps)}
|
||||
|
||||
smtbmc.ywfile_constraints(path, constraints, map_steps=map_steps, skip_x=skip_x)
|
||||
|
||||
self._yw_constraints[name] = {
|
||||
map_steps.get(i, i): [smtexpr for cexfile, smtexpr in constraint_list]
|
||||
for i, constraint_list in constraints.items()
|
||||
}
|
||||
|
||||
def cmd_ping(self, cmd):
|
||||
return cmd
|
||||
|
||||
cmd_handlers = {
|
||||
"new_step": cmd_new_step,
|
||||
"assert": cmd_assert,
|
||||
"assert_antecedent": cmd_assert,
|
||||
"assert_consequent": cmd_assert,
|
||||
"push": cmd_push,
|
||||
"pop": cmd_pop,
|
||||
"check": cmd_check,
|
||||
"design_hierwitness": cmd_design_hierwitness,
|
||||
"write_yw_trace": cmd_write_yw_trace,
|
||||
"read_yw_trace": cmd_read_yw_trace,
|
||||
"ping": cmd_ping,
|
||||
}
|
||||
|
||||
def handle_command(self, cmd):
|
||||
if not isinstance(cmd, dict) or "cmd" not in cmd:
|
||||
raise InteractiveError('object with "cmd" key required')
|
||||
|
||||
name = cmd.get("cmd", None)
|
||||
|
||||
handler = self.cmd_handlers.get(name)
|
||||
if handler:
|
||||
return handler(self, cmd)
|
||||
else:
|
||||
raise InteractiveError(f"unknown command: {name}")
|
||||
|
||||
def mainloop(self):
|
||||
self.setup()
|
||||
while True:
|
||||
try:
|
||||
cmd = input().strip()
|
||||
if not cmd or cmd.startswith("#") or cmd.startswith("//"):
|
||||
continue
|
||||
try:
|
||||
cmd = json.loads(cmd)
|
||||
except json.decoder.JSONDecodeError as e:
|
||||
self.print_json(err=f"invalid JSON: {e}")
|
||||
continue
|
||||
except EOFError:
|
||||
break
|
||||
|
||||
try:
|
||||
result = self.handle_command(cmd)
|
||||
except InteractiveError as e:
|
||||
self.print_json(err=str(e))
|
||||
continue
|
||||
except Exception as e:
|
||||
self.print_json(err=f"internal error: {e}")
|
||||
raise
|
||||
else:
|
||||
self.print_json(ok=result)
|
|
@ -33,10 +33,14 @@ def cli():
|
|||
Display a Yosys witness trace in a human readable format.
|
||||
""")
|
||||
@click.argument("input", type=click.File("r"))
|
||||
def display(input):
|
||||
@click.option("--skip-x", help="Treat x bits as unassigned.", is_flag=True)
|
||||
def display(input, skip_x):
|
||||
click.echo(f"Reading Yosys witness trace {input.name!r}...")
|
||||
inyw = ReadWitness(input)
|
||||
|
||||
if skip_x:
|
||||
inyw.skip_x()
|
||||
|
||||
def output():
|
||||
|
||||
yield click.style("*** RTLIL bit-order below may differ from source level declarations ***", fg="red")
|
||||
|
@ -91,7 +95,11 @@ If two or more inputs are provided they will be concatenated together into the o
|
|||
@click.option("--append", "-p", type=int, multiple=True,
|
||||
help="Number of steps (+ve or -ve) to append to end of input trace. "
|
||||
+"Can be defined multiple times, following the same order as input traces. ")
|
||||
def yw2yw(inputs, output, append):
|
||||
@click.option("--skip-x", help="Leave input x bits unassigned.", is_flag=True)
|
||||
def yw2yw(inputs, output, append, skip_x):
|
||||
if len(inputs) == 0:
|
||||
raise click.ClickException(f"no inputs specified")
|
||||
|
||||
outyw = WriteWitness(output, "yosys-witness yw2yw")
|
||||
join_inputs = len(inputs) > 1
|
||||
inyws = {}
|
||||
|
@ -129,12 +137,12 @@ def yw2yw(inputs, output, append):
|
|||
click.echo(f"Copying yosys witness trace from {input.name!r} to {output.name!r}...")
|
||||
|
||||
if first_witness:
|
||||
outyw.step(init_values)
|
||||
outyw.step(init_values, skip_x=skip_x)
|
||||
else:
|
||||
outyw.step(inyw.first_step())
|
||||
outyw.step(inyw.first_step(), skip_x=skip_x)
|
||||
|
||||
for t, values in inyw.steps(1):
|
||||
outyw.step(values)
|
||||
outyw.step(values, skip_x=skip_x)
|
||||
|
||||
click.echo(f" copied {t + 1} time steps.")
|
||||
first_witness = False
|
||||
|
@ -174,7 +182,8 @@ This requires a Yosys witness AIGER map file as generated by 'write_aiger -ywmap
|
|||
@click.argument("input", type=click.File("r"))
|
||||
@click.argument("mapfile", type=click.File("r"))
|
||||
@click.argument("output", type=click.File("w"))
|
||||
def aiw2yw(input, mapfile, output):
|
||||
@click.option("--skip-x", help="Leave input x bits unassigned.", is_flag=True)
|
||||
def aiw2yw(input, mapfile, output, skip_x):
|
||||
input_name = input.name
|
||||
click.echo(f"Converting AIGER witness trace {input_name!r} to Yosys witness trace {output.name!r}...")
|
||||
click.echo(f"Using Yosys witness AIGER map file {mapfile.name!r}")
|
||||
|
@ -245,7 +254,7 @@ def aiw2yw(input, mapfile, output):
|
|||
|
||||
values[bit] = v
|
||||
|
||||
outyw.step(values)
|
||||
outyw.step(values, skip_x=skip_x)
|
||||
|
||||
outyw.end_trace()
|
||||
|
||||
|
|
|
@ -351,11 +351,14 @@ class WriteWitness:
|
|||
self.out.name("steps")
|
||||
self.out.begin_array()
|
||||
|
||||
def step(self, values):
|
||||
def step(self, values, skip_x=False):
|
||||
if not self.header_written:
|
||||
self.write_header()
|
||||
|
||||
self.out.value({"bits": values.pack(self.sigmap)})
|
||||
packed = values.pack(self.sigmap)
|
||||
if skip_x:
|
||||
packed = packed.replace('x', '?')
|
||||
self.out.value({"bits": packed})
|
||||
|
||||
self.t += 1
|
||||
|
||||
|
@ -390,6 +393,9 @@ class ReadWitness:
|
|||
|
||||
self.bits = [step["bits"] for step in data["steps"]]
|
||||
|
||||
def skip_x(self):
|
||||
self.bits = [step.replace('x', '?') for step in self.bits]
|
||||
|
||||
def init_step(self):
|
||||
return self.step(0)
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
from pyosys import libyosys as ys
|
||||
import libyosys as ys
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
|
|
@ -658,11 +658,20 @@ void AstNode::dumpVlog(FILE *f, std::string indent) const
|
|||
if (0) { case AST_NEG: txt = "-"; }
|
||||
if (0) { case AST_LOGIC_NOT: txt = "!"; }
|
||||
if (0) { case AST_SELFSZ: txt = "@selfsz@"; }
|
||||
if (0) { case AST_TO_SIGNED: txt = "signed'"; }
|
||||
if (0) { case AST_TO_UNSIGNED: txt = "unsigned'"; }
|
||||
fprintf(f, "%s(", txt.c_str());
|
||||
children[0]->dumpVlog(f, "");
|
||||
fprintf(f, ")");
|
||||
break;
|
||||
|
||||
case AST_CAST_SIZE:
|
||||
children[0]->dumpVlog(f, "");
|
||||
fprintf(f, "'(");
|
||||
children[1]->dumpVlog(f, "");
|
||||
fprintf(f, ")");
|
||||
break;
|
||||
|
||||
if (0) { case AST_BIT_AND: txt = "&"; }
|
||||
if (0) { case AST_BIT_OR: txt = "|"; }
|
||||
if (0) { case AST_BIT_XOR: txt = "^"; }
|
||||
|
|
|
@ -1740,7 +1740,8 @@ RTLIL::SigSpec AstNode::genRTLIL(int width_hint, bool sign_hint)
|
|||
if (width_hint < 0)
|
||||
detectSignWidth(width_hint, sign_hint);
|
||||
RTLIL::SigSpec left = children[0]->genRTLIL(width_hint, sign_hint);
|
||||
RTLIL::SigSpec right = children[1]->genRTLIL();
|
||||
// for $shift and $shiftx, the second operand can be negative
|
||||
RTLIL::SigSpec right = children[1]->genRTLIL(-1, type == AST_SHIFT || type == AST_SHIFTX);
|
||||
int width = width_hint > 0 ? width_hint : left.size();
|
||||
is_signed = children[0]->is_signed;
|
||||
return binop2rtlil(this, type_name, width, left, right);
|
||||
|
|
|
@ -115,7 +115,9 @@ void msg_func(msg_type_t msg_type, const char *message_id, linefile_type linefil
|
|||
|
||||
if (log_verific_callback) {
|
||||
string full_message = stringf("%s%s\n", message_prefix.c_str(), message.c_str());
|
||||
log_verific_callback(int(msg_type), message_id, LineFile::GetFileName(linefile), LineFile::GetLineNo(linefile), full_message.c_str());
|
||||
log_verific_callback(int(msg_type), message_id, LineFile::GetFileName(linefile),
|
||||
linefile ? linefile->GetLeftLine() : 0, linefile ? linefile->GetLeftCol() : 0,
|
||||
linefile ? linefile->GetRightLine() : 0, linefile ? linefile->GetRightCol() : 0, full_message.c_str());
|
||||
} else {
|
||||
if (msg_type == VERIFIC_ERROR || msg_type == VERIFIC_WARNING || msg_type == VERIFIC_PROGRAM_ERROR)
|
||||
log_warning_noprefix("%s%s\n", message_prefix.c_str(), message.c_str());
|
||||
|
@ -126,7 +128,7 @@ void msg_func(msg_type_t msg_type, const char *message_id, linefile_type linefil
|
|||
verific_error_msg = message;
|
||||
}
|
||||
|
||||
void set_verific_logging(void (*cb)(int msg_type, const char *message_id, const char* file_path, unsigned int line_no, const char *msg))
|
||||
void set_verific_logging(void (*cb)(int msg_type, const char *message_id, const char* file_path, unsigned int left_line, unsigned int left_col, unsigned int right_line, unsigned int right_col, const char *msg))
|
||||
{
|
||||
Message::SetConsoleOutput(0);
|
||||
Message::RegisterCallBackMsg(msg_func);
|
||||
|
@ -262,6 +264,9 @@ static const std::string verific_unescape(const char *value)
|
|||
|
||||
void VerificImporter::import_attributes(dict<RTLIL::IdString, RTLIL::Const> &attributes, DesignObj *obj, Netlist *nl)
|
||||
{
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
MapIter mi;
|
||||
Att *attr;
|
||||
|
||||
|
@ -1345,7 +1350,12 @@ void VerificImporter::import_netlist(RTLIL::Design *design, Netlist *nl, std::ma
|
|||
wire->start_offset = min(portbus->LeftIndex(), portbus->RightIndex());
|
||||
wire->upto = portbus->IsUp();
|
||||
import_attributes(wire->attributes, portbus, nl);
|
||||
|
||||
SetIter si ;
|
||||
Port *port ;
|
||||
FOREACH_PORT_OF_PORTBUS(portbus, si, port) {
|
||||
import_attributes(wire->attributes, port->GetNet(), nl);
|
||||
break;
|
||||
}
|
||||
bool portbus_input = portbus->GetDir() == DIR_INOUT || portbus->GetDir() == DIR_IN;
|
||||
if (portbus_input)
|
||||
wire->port_input = true;
|
||||
|
|
|
@ -140,6 +140,7 @@ X(nomem2reg)
|
|||
X(nomeminit)
|
||||
X(nosync)
|
||||
X(nowrshmsk)
|
||||
X(no_ram)
|
||||
X(no_rw_check)
|
||||
X(O)
|
||||
X(OFFSET)
|
||||
|
|
|
@ -51,40 +51,51 @@
|
|||
|
||||
#if !defined(_WIN32) || defined(__MINGW32__)
|
||||
# include <unistd.h>
|
||||
#else
|
||||
#endif
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
|
||||
char *optarg;
|
||||
int optind = 1, optcur = 1;
|
||||
int optind = 1, optcur = 1, optopt = 0;
|
||||
int getopt(int argc, char **argv, const char *optstring)
|
||||
{
|
||||
if (optind >= argc || argv[optind][0] != '-')
|
||||
if (optind >= argc)
|
||||
return -1;
|
||||
|
||||
if (argv[optind][0] != '-' || argv[optind][1] == 0) {
|
||||
optopt = 1;
|
||||
optarg = argv[optind++];
|
||||
return optopt;
|
||||
}
|
||||
|
||||
bool takes_arg = false;
|
||||
int opt = argv[optind][optcur];
|
||||
optopt = argv[optind][optcur];
|
||||
|
||||
if (optopt == '-') {
|
||||
++optind;
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; optstring[i]; i++)
|
||||
if (opt == optstring[i] && optstring[i + 1] == ':')
|
||||
if (optopt == optstring[i] && optstring[i + 1] == ':')
|
||||
takes_arg = true;
|
||||
|
||||
if (!takes_arg) {
|
||||
if (argv[optind][++optcur] == 0)
|
||||
optind++, optcur = 1;
|
||||
return opt;
|
||||
return optopt;
|
||||
}
|
||||
|
||||
if (argv[optind][++optcur]) {
|
||||
optarg = argv[optind++] + optcur;
|
||||
optcur = 1;
|
||||
return opt;
|
||||
return optopt;
|
||||
}
|
||||
|
||||
optarg = argv[++optind];
|
||||
optind++, optcur = 1;
|
||||
return opt;
|
||||
return optopt;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
|
||||
#ifdef EMSCRIPTEN
|
||||
# include <sys/stat.h>
|
||||
|
@ -215,6 +226,7 @@ int main(int argc, char **argv)
|
|||
std::string backend_command = "auto";
|
||||
std::vector<std::string> vlog_defines;
|
||||
std::vector<std::string> passes_commands;
|
||||
std::vector<std::string> frontend_files;
|
||||
std::vector<std::string> plugin_filenames;
|
||||
std::string output_filename = "";
|
||||
std::string scriptfile = "";
|
||||
|
@ -509,6 +521,9 @@ int main(int argc, char **argv)
|
|||
case 'C':
|
||||
run_tcl_shell = true;
|
||||
break;
|
||||
case '\001':
|
||||
frontend_files.push_back(optarg);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Run '%s -h' for help.\n", argv[0]);
|
||||
exit(1);
|
||||
|
@ -561,17 +576,33 @@ int main(int argc, char **argv)
|
|||
run_pass(vdef_cmd);
|
||||
}
|
||||
|
||||
while (optind < argc)
|
||||
if (run_frontend(argv[optind++], frontend_command))
|
||||
if (scriptfile.empty() || !scriptfile_tcl) {
|
||||
// Without a TCL script, arguments following '--' are also treated as frontend files
|
||||
for (int i = optind; i < argc; ++i)
|
||||
frontend_files.push_back(argv[i]);
|
||||
}
|
||||
|
||||
for (auto it = frontend_files.begin(); it != frontend_files.end(); ++it) {
|
||||
if (run_frontend((*it).c_str(), frontend_command))
|
||||
run_shell = false;
|
||||
}
|
||||
|
||||
if (!topmodule.empty())
|
||||
run_pass("hierarchy -top " + topmodule);
|
||||
|
||||
if (!scriptfile.empty()) {
|
||||
if (scriptfile_tcl) {
|
||||
#ifdef YOSYS_ENABLE_TCL
|
||||
if (Tcl_EvalFile(yosys_get_tcl_interp(), scriptfile.c_str()) != TCL_OK)
|
||||
int tcl_argc = argc - optind;
|
||||
std::vector<Tcl_Obj*> script_args;
|
||||
Tcl_Interp *interp = yosys_get_tcl_interp();
|
||||
for (int i = optind; i < argc; ++i)
|
||||
script_args.push_back(Tcl_NewStringObj(argv[i], strlen(argv[i])));
|
||||
|
||||
Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argc", 4), NULL, Tcl_NewIntObj(tcl_argc), 0);
|
||||
Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv", 4), NULL, Tcl_NewListObj(tcl_argc, script_args.data()), 0);
|
||||
Tcl_ObjSetVar2(interp, Tcl_NewStringObj("argv0", 5), NULL, Tcl_NewStringObj(scriptfile.c_str(), scriptfile.length()), 0);
|
||||
|
||||
if (Tcl_EvalFile(interp, scriptfile.c_str()) != TCL_OK)
|
||||
log_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(yosys_get_tcl_interp()));
|
||||
#else
|
||||
log_error("Can't exectue TCL script: this version of yosys is not built with TCL support enabled.\n");
|
||||
|
|
|
@ -326,6 +326,16 @@ void Fmt::parse_verilog(const std::vector<VerilogFmtArg> &args, bool sformat_lik
|
|||
break;
|
||||
}
|
||||
|
||||
case VerilogFmtArg::TIME: {
|
||||
FmtPart part = {};
|
||||
part.type = FmtPart::TIME;
|
||||
part.realtime = arg->realtime;
|
||||
part.padding = ' ';
|
||||
part.width = 20;
|
||||
parts.push_back(part);
|
||||
break;
|
||||
}
|
||||
|
||||
case VerilogFmtArg::STRING: {
|
||||
if (arg == args.begin() || !sformat_like) {
|
||||
const auto fmtarg = arg;
|
||||
|
|
|
@ -371,7 +371,7 @@ class dict
|
|||
}
|
||||
|
||||
public:
|
||||
class const_iterator : public std::iterator<std::forward_iterator_tag, std::pair<K, T>>
|
||||
class const_iterator
|
||||
{
|
||||
friend class dict;
|
||||
protected:
|
||||
|
@ -379,6 +379,11 @@ public:
|
|||
int index;
|
||||
const_iterator(const dict *ptr, int index) : ptr(ptr), index(index) { }
|
||||
public:
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef std::pair<K, T> value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef std::pair<K, T>* pointer;
|
||||
typedef std::pair<K, T>& reference;
|
||||
const_iterator() { }
|
||||
const_iterator operator++() { index--; return *this; }
|
||||
const_iterator operator+=(int amt) { index -= amt; return *this; }
|
||||
|
@ -389,7 +394,7 @@ public:
|
|||
const std::pair<K, T> *operator->() const { return &ptr->entries[index].udata; }
|
||||
};
|
||||
|
||||
class iterator : public std::iterator<std::forward_iterator_tag, std::pair<K, T>>
|
||||
class iterator
|
||||
{
|
||||
friend class dict;
|
||||
protected:
|
||||
|
@ -397,6 +402,11 @@ public:
|
|||
int index;
|
||||
iterator(dict *ptr, int index) : ptr(ptr), index(index) { }
|
||||
public:
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef std::pair<K, T> value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef std::pair<K, T>* pointer;
|
||||
typedef std::pair<K, T>& reference;
|
||||
iterator() { }
|
||||
iterator operator++() { index--; return *this; }
|
||||
iterator operator+=(int amt) { index -= amt; return *this; }
|
||||
|
@ -800,7 +810,7 @@ protected:
|
|||
}
|
||||
|
||||
public:
|
||||
class const_iterator : public std::iterator<std::forward_iterator_tag, K>
|
||||
class const_iterator
|
||||
{
|
||||
friend class pool;
|
||||
protected:
|
||||
|
@ -808,6 +818,11 @@ public:
|
|||
int index;
|
||||
const_iterator(const pool *ptr, int index) : ptr(ptr), index(index) { }
|
||||
public:
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef K value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef K* pointer;
|
||||
typedef K& reference;
|
||||
const_iterator() { }
|
||||
const_iterator operator++() { index--; return *this; }
|
||||
bool operator==(const const_iterator &other) const { return index == other.index; }
|
||||
|
@ -816,7 +831,7 @@ public:
|
|||
const K *operator->() const { return &ptr->entries[index].udata; }
|
||||
};
|
||||
|
||||
class iterator : public std::iterator<std::forward_iterator_tag, K>
|
||||
class iterator
|
||||
{
|
||||
friend class pool;
|
||||
protected:
|
||||
|
@ -824,6 +839,11 @@ public:
|
|||
int index;
|
||||
iterator(pool *ptr, int index) : ptr(ptr), index(index) { }
|
||||
public:
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef K value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef K* pointer;
|
||||
typedef K& reference;
|
||||
iterator() { }
|
||||
iterator operator++() { index--; return *this; }
|
||||
bool operator==(const iterator &other) const { return index == other.index; }
|
||||
|
@ -1021,7 +1041,7 @@ class idict
|
|||
pool<K, OPS> database;
|
||||
|
||||
public:
|
||||
class const_iterator : public std::iterator<std::forward_iterator_tag, K>
|
||||
class const_iterator
|
||||
{
|
||||
friend class idict;
|
||||
protected:
|
||||
|
@ -1029,6 +1049,11 @@ public:
|
|||
int index;
|
||||
const_iterator(const idict &container, int index) : container(container), index(index) { }
|
||||
public:
|
||||
typedef std::forward_iterator_tag iterator_category;
|
||||
typedef K value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef K* pointer;
|
||||
typedef K& reference;
|
||||
const_iterator() { }
|
||||
const_iterator operator++() { index++; return *this; }
|
||||
bool operator==(const const_iterator &other) const { return index == other.index; }
|
||||
|
|
|
@ -59,7 +59,7 @@ bool log_quiet_warnings = false;
|
|||
int log_verbose_level;
|
||||
string log_last_error;
|
||||
void (*log_error_atexit)() = NULL;
|
||||
void (*log_verific_callback)(int msg_type, const char *message_id, const char* file_path, unsigned int line_no, const char *msg) = NULL;
|
||||
void (*log_verific_callback)(int msg_type, const char *message_id, const char* file_path, unsigned int left_line, unsigned int left_col, unsigned int right_line, unsigned int right_col, const char *msg) = NULL;
|
||||
|
||||
int log_make_debug = 0;
|
||||
int log_force_debug = 0;
|
||||
|
|
|
@ -131,8 +131,8 @@ void log_header(RTLIL::Design *design, const char *format, ...) YS_ATTRIBUTE(for
|
|||
void log_warning(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
|
||||
void log_experimental(const char *format, ...) YS_ATTRIBUTE(format(printf, 1, 2));
|
||||
|
||||
void set_verific_logging(void (*cb)(int msg_type, const char *message_id, const char* file_path, unsigned int line_no, const char *msg));
|
||||
extern void (*log_verific_callback)(int msg_type, const char *message_id, const char* file_path, unsigned int line_no, const char *msg);
|
||||
void set_verific_logging(void (*cb)(int msg_type, const char *message_id, const char* file_path, unsigned int left_line, unsigned int left_col, unsigned int right_line, unsigned int right_col, const char *msg));
|
||||
extern void (*log_verific_callback)(int msg_type, const char *message_id, const char* file_path, unsigned int left_line, unsigned int left_col, unsigned int right_line, unsigned int right_col, const char *msg);
|
||||
|
||||
// Log with filename to report a problem in a source file.
|
||||
void log_file_warning(const std::string &filename, int lineno, const char *format, ...) YS_ATTRIBUTE(format(printf, 3, 4));
|
||||
|
|
|
@ -148,6 +148,9 @@ void Mem::emit() {
|
|||
for (int j = 0; j < (1 << wr_ports[i].wide_log2); j++)
|
||||
wr_port_xlat.push_back(i);
|
||||
for (auto &port : rd_ports) {
|
||||
for (auto attr: port.attributes)
|
||||
if (!cell->has_attribute(attr.first))
|
||||
cell->attributes.insert(attr);
|
||||
if (port.cell) {
|
||||
module->remove(port.cell);
|
||||
port.cell = nullptr;
|
||||
|
@ -210,6 +213,9 @@ void Mem::emit() {
|
|||
cell->setPort(ID::RD_ADDR, rd_addr);
|
||||
cell->setPort(ID::RD_DATA, rd_data);
|
||||
for (auto &port : wr_ports) {
|
||||
for (auto attr: port.attributes)
|
||||
if (!cell->has_attribute(attr.first))
|
||||
cell->attributes.insert(attr);
|
||||
if (port.cell) {
|
||||
module->remove(port.cell);
|
||||
port.cell = nullptr;
|
||||
|
@ -246,6 +252,9 @@ void Mem::emit() {
|
|||
cell->setPort(ID::WR_ADDR, wr_addr);
|
||||
cell->setPort(ID::WR_DATA, wr_data);
|
||||
for (auto &init : inits) {
|
||||
for (auto attr: init.attributes)
|
||||
if (!cell->has_attribute(attr.first))
|
||||
cell->attributes.insert(attr);
|
||||
if (init.cell) {
|
||||
module->remove(init.cell);
|
||||
init.cell = nullptr;
|
||||
|
|
|
@ -803,8 +803,14 @@ struct RTLIL::SigBit
|
|||
unsigned int hash() const;
|
||||
};
|
||||
|
||||
struct RTLIL::SigSpecIterator : public std::iterator<std::input_iterator_tag, RTLIL::SigSpec>
|
||||
struct RTLIL::SigSpecIterator
|
||||
{
|
||||
typedef std::input_iterator_tag iterator_category;
|
||||
typedef RTLIL::SigBit value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef RTLIL::SigBit* pointer;
|
||||
typedef RTLIL::SigBit& reference;
|
||||
|
||||
RTLIL::SigSpec *sig_p;
|
||||
int index;
|
||||
|
||||
|
@ -814,8 +820,14 @@ struct RTLIL::SigSpecIterator : public std::iterator<std::input_iterator_tag, RT
|
|||
inline void operator++() { index++; }
|
||||
};
|
||||
|
||||
struct RTLIL::SigSpecConstIterator : public std::iterator<std::input_iterator_tag, RTLIL::SigSpec>
|
||||
struct RTLIL::SigSpecConstIterator
|
||||
{
|
||||
typedef std::input_iterator_tag iterator_category;
|
||||
typedef RTLIL::SigBit value_type;
|
||||
typedef ptrdiff_t difference_type;
|
||||
typedef RTLIL::SigBit* pointer;
|
||||
typedef RTLIL::SigBit& reference;
|
||||
|
||||
const RTLIL::SigSpec *sig_p;
|
||||
int index;
|
||||
|
||||
|
@ -921,6 +933,9 @@ public:
|
|||
RTLIL::SigSpec extract(int offset, int length = 1) const;
|
||||
RTLIL::SigSpec extract_end(int offset) const { return extract(offset, width_ - offset); }
|
||||
|
||||
RTLIL::SigBit lsb() const { log_assert(width_); return (*this)[0]; };
|
||||
RTLIL::SigBit msb() const { log_assert(width_); return (*this)[width_ - 1]; };
|
||||
|
||||
void append(const RTLIL::SigSpec &signal);
|
||||
inline void append(Wire *wire) { append(RTLIL::SigSpec(wire)); }
|
||||
inline void append(const RTLIL::SigChunk &chunk) { append(RTLIL::SigSpec(chunk)); }
|
||||
|
|
|
@ -481,18 +481,58 @@ void MemMapping::dump_config(MemConfig &cfg) {
|
|||
}
|
||||
}
|
||||
|
||||
std::pair<bool, Const> search_for_attribute(Mem mem, IdString attr) {
|
||||
// priority of attributes:
|
||||
// 1. attributes on memory itself
|
||||
// 2. attributes on a read or write port
|
||||
// 3. attributes on data signal of a read or write port
|
||||
// 4. attributes on address signal of a read or write port
|
||||
|
||||
if (mem.has_attribute(attr))
|
||||
return std::make_pair(true, mem.attributes.at(attr));
|
||||
|
||||
for (auto &port: mem.rd_ports)
|
||||
if (port.has_attribute(attr))
|
||||
return std::make_pair(true, port.attributes.at(attr));
|
||||
for (auto &port: mem.wr_ports)
|
||||
if (port.has_attribute(attr))
|
||||
return std::make_pair(true, port.attributes.at(attr));
|
||||
|
||||
for (auto &port: mem.rd_ports)
|
||||
for (SigBit bit: port.data)
|
||||
if (bit.is_wire() && bit.wire->has_attribute(attr))
|
||||
return std::make_pair(true, bit.wire->attributes.at(attr));
|
||||
for (auto &port: mem.wr_ports)
|
||||
for (SigBit bit: port.data)
|
||||
if (bit.is_wire() && bit.wire->has_attribute(attr))
|
||||
return std::make_pair(true, bit.wire->attributes.at(attr));
|
||||
|
||||
for (auto &port: mem.rd_ports)
|
||||
for (SigBit bit: port.addr)
|
||||
if (bit.is_wire() && bit.wire->has_attribute(attr))
|
||||
return std::make_pair(true, bit.wire->attributes.at(attr));
|
||||
for (auto &port: mem.wr_ports)
|
||||
for (SigBit bit: port.addr)
|
||||
if (bit.is_wire() && bit.wire->has_attribute(attr))
|
||||
return std::make_pair(true, bit.wire->attributes.at(attr));
|
||||
|
||||
return std::make_pair(false, Const());
|
||||
}
|
||||
|
||||
// Go through memory attributes to determine user-requested mapping style.
|
||||
void MemMapping::determine_style() {
|
||||
kind = RamKind::Auto;
|
||||
style = "";
|
||||
if (mem.get_bool_attribute(ID::lram)) {
|
||||
auto find_attr = search_for_attribute(mem, ID::lram);
|
||||
if (find_attr.first && find_attr.second.as_bool()) {
|
||||
kind = RamKind::Huge;
|
||||
log("found attribute 'lram' on memory %s.%s, forced mapping to huge RAM\n", log_id(mem.module->name), log_id(mem.memid));
|
||||
return;
|
||||
}
|
||||
for (auto attr: {ID::ram_block, ID::rom_block, ID::ram_style, ID::rom_style, ID::ramstyle, ID::romstyle, ID::syn_ramstyle, ID::syn_romstyle}) {
|
||||
if (mem.has_attribute(attr)) {
|
||||
Const val = mem.attributes.at(attr);
|
||||
find_attr = search_for_attribute(mem, attr);
|
||||
if (find_attr.first) {
|
||||
Const val = find_attr.second;
|
||||
if (val == 1) {
|
||||
kind = RamKind::NotLogic;
|
||||
log("found attribute '%s = 1' on memory %s.%s, disabled mapping to FF\n", log_id(attr), log_id(mem.module->name), log_id(mem.memid));
|
||||
|
@ -526,9 +566,12 @@ void MemMapping::determine_style() {
|
|||
return;
|
||||
}
|
||||
}
|
||||
if (mem.get_bool_attribute(ID::logic_block))
|
||||
for (auto attr: {ID::logic_block, ID::no_ram}){
|
||||
find_attr = search_for_attribute(mem, attr);
|
||||
if (find_attr.first && find_attr.second.as_bool())
|
||||
kind = RamKind::Logic;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine whether the memory can be mapped entirely to soft logic.
|
||||
bool MemMapping::determine_logic_ok() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
%_pm.h: passes/pmgen/pmgen.py %.pmg
|
||||
$(P) mkdir -p passes/pmgen && $(PYTHON_EXECUTABLE) $< -o $@ -p $(subst _pm.h,,$(notdir $@)) $(filter-out $<,$^)
|
||||
$(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $< -o $@ -p $(subst _pm.h,,$(notdir $@)) $(filter-out $<,$^)
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
|
@ -44,6 +44,7 @@ $(eval $(call add_extra_objs,passes/pmgen/peepopt_pm.h))
|
|||
|
||||
PEEPOPT_PATTERN = passes/pmgen/peepopt_shiftmul_right.pmg
|
||||
PEEPOPT_PATTERN += passes/pmgen/peepopt_shiftmul_left.pmg
|
||||
PEEPOPT_PATTERN += passes/pmgen/peepopt_shiftadd.pmg
|
||||
PEEPOPT_PATTERN += passes/pmgen/peepopt_muldiv.pmg
|
||||
|
||||
passes/pmgen/peepopt_pm.h: passes/pmgen/pmgen.py $(PEEPOPT_PATTERN)
|
||||
|
|
|
@ -48,6 +48,9 @@ struct PeepoptPass : public Pass {
|
|||
log(" Analogously, replace A<<(B*C) with appropriate selection of\n");
|
||||
log(" output bits from A<<(B<<K). (left variant)\n");
|
||||
log("\n");
|
||||
log(" * shiftadd - Replace A>>(B+D) with (A'>>D)>>(B) where D is constant and\n");
|
||||
log(" A' is derived from A by padding or cutting inaccessible bits.\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
|
@ -72,6 +75,7 @@ struct PeepoptPass : public Pass {
|
|||
|
||||
pm.setup(module->selected_cells());
|
||||
|
||||
pm.run_shiftadd();
|
||||
pm.run_shiftmul_right();
|
||||
pm.run_shiftmul_left();
|
||||
pm.run_muldiv();
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
pattern shiftadd
|
||||
//
|
||||
// Transforms add/sub+shift pairs that result from expressions such as data[s*W +C +:W2]
|
||||
// specifically something like: out[W2-1:0] = data >> (s*W +C)
|
||||
// will be transformed into: out[W2-1:0] = (data >> C) >> (s*W)
|
||||
// this can then be optimized using peepopt_shiftmul_right.pmg
|
||||
//
|
||||
|
||||
match shift
|
||||
select shift->type.in($shift, $shiftx, $shr)
|
||||
filter !port(shift, \B).empty()
|
||||
endmatch
|
||||
|
||||
// the right shift amount
|
||||
state <SigSpec> shift_amount
|
||||
// log2 scale factor in interpreting of shift_amount
|
||||
// due to zero padding on the shift cell's B port
|
||||
state <int> log2scale
|
||||
// zeros at the MSB position make it unsigned
|
||||
state <bool> msb_zeros
|
||||
|
||||
code shift_amount log2scale msb_zeros
|
||||
shift_amount = port(shift, \B);
|
||||
|
||||
log2scale = 0;
|
||||
while (shift_amount[0] == State::S0) {
|
||||
shift_amount.remove(0);
|
||||
if (shift_amount.empty()) reject;
|
||||
log2scale++;
|
||||
}
|
||||
|
||||
msb_zeros = 0;
|
||||
while (shift_amount.bits().back() == State::S0) {
|
||||
msb_zeros = true;
|
||||
shift_amount.remove(GetSize(shift_amount) - 1);
|
||||
if (shift_amount.empty()) reject;
|
||||
}
|
||||
endcode
|
||||
|
||||
state <bool> var_signed
|
||||
state <SigSpec> var_signal
|
||||
// offset: signed constant value c in data[var+c +:W1] (constant shift-right amount)
|
||||
state <int> offset
|
||||
|
||||
match add
|
||||
// either data[var+c +:W1] or data[var-c +:W1]
|
||||
select add->type.in($add, $sub)
|
||||
index <SigSpec> port(add, \Y) === shift_amount
|
||||
|
||||
// one must be constant, the other is variable
|
||||
choice <IdString> constport {\A, \B}
|
||||
select !port(add, constport).empty()
|
||||
select port(add, constport).is_fully_const()
|
||||
define <IdString> varport (constport == \A ? \B : \A)
|
||||
|
||||
// if a value of var is able to wrap the output, the transformation might give wrong results
|
||||
// an addition/substraction can at most flip one more bit than the largest operand (the carry bit)
|
||||
// as long as the output can show this bit, no wrap should occur (assuming all signed-ness make sense)
|
||||
select ( GetSize(port(add, \Y)) > max(GetSize(port(add, \A)), GetSize(port(add, \B))) )
|
||||
|
||||
define <bool> varport_A (varport == \A)
|
||||
define <bool> is_sub add->type.in($sub)
|
||||
|
||||
define <bool> constport_signed param(add, !varport_A ? \A_SIGNED : \B_SIGNED).as_bool()
|
||||
define <bool> varport_signed param(add, varport_A ? \A_SIGNED : \B_SIGNED).as_bool();
|
||||
define <bool> offset_negative ((port(add, constport).bits().back() == State::S1) ^ (is_sub && varport_A))
|
||||
|
||||
// checking some value boundaries as well:
|
||||
// data[...-c +:W1] is fine for +/-var (pad at LSB, all data still accessible)
|
||||
// data[...+c +:W1] is only fine for +var(add) and var unsigned
|
||||
// (+c cuts lower C bits, making them inaccessible, a signed var could try to access them)
|
||||
// either its an add or the variable port is A (it must be positive)
|
||||
select (add->type.in($add) || varport == \A)
|
||||
|
||||
// -> data[var+c +:W1] (with var signed) is illegal
|
||||
filter !(!offset_negative && varport_signed)
|
||||
|
||||
// state-variables are assigned at the end only:
|
||||
// shift the log2scale offset in-front of add to get true value: (var+c)<<N -> (var<<N)+(c<<N)
|
||||
set offset ( (port(add, constport).as_int(constport_signed) << log2scale) * ( (is_sub && varport_A) ? -1 : 1 ) )
|
||||
|
||||
set var_signed varport_signed
|
||||
set var_signal add->getPort(varport)
|
||||
endmatch
|
||||
|
||||
code
|
||||
{
|
||||
// positive constant offset with a signed variable (index) cannot be handled
|
||||
// the above filter should get rid of this case but 'offset' is calculated differently
|
||||
// due to limitations of state-variables in pmgen
|
||||
// it should only differ if previous passes create invalid data
|
||||
log_assert(!(offset>0 && var_signed));
|
||||
|
||||
did_something = true;
|
||||
log("shiftadd pattern in %s: shift=%s, add/sub=%s, offset: %d\n", \
|
||||
log_id(module), log_id(shift), log_id(add), offset);
|
||||
|
||||
SigSpec old_a = port(shift, \A), new_a;
|
||||
if(offset<0) {
|
||||
// data >> (...-c) transformed to {data, c'X} >> (...)
|
||||
SigSpec padding( (shift->type.in($shiftx) ? State::Sx : State::S0), -offset );
|
||||
new_a.append(padding);
|
||||
new_a.append(old_a);
|
||||
} else {
|
||||
// data >> (...+c) transformed to data[MAX:c] >> (...)
|
||||
new_a.append(old_a.extract_end(offset));
|
||||
|
||||
}
|
||||
|
||||
SigSpec new_b = {var_signal, SigSpec(State::S0, log2scale)};
|
||||
if (msb_zeros || !var_signed)
|
||||
new_b.append(State::S0);
|
||||
|
||||
shift->setPort(\A, new_a);
|
||||
shift->setParam(\A_WIDTH, GetSize(new_a));
|
||||
shift->setPort(\B, new_b);
|
||||
shift->setParam(\B_WIDTH, GetSize(new_b));
|
||||
blacklist(add);
|
||||
accept;
|
||||
}
|
||||
endcode
|
|
@ -82,21 +82,16 @@ code
|
|||
int new_const_factor = 1 << factor_bits;
|
||||
SigSpec padding(State::Sx, new_const_factor-const_factor);
|
||||
SigSpec old_a = port(shift, \A), new_a;
|
||||
int trunc = 0;
|
||||
|
||||
if (GetSize(old_a) % const_factor != 0) {
|
||||
trunc = const_factor - GetSize(old_a) % const_factor;
|
||||
old_a.append(SigSpec(State::Sx, trunc));
|
||||
}
|
||||
|
||||
for (int i = 0; i*const_factor < GetSize(old_a); i++) {
|
||||
if ((i+1)*const_factor < GetSize(old_a)) {
|
||||
SigSpec slice = old_a.extract(i*const_factor, const_factor);
|
||||
new_a.append(slice);
|
||||
new_a.append(padding);
|
||||
} else {
|
||||
new_a.append(old_a.extract_end(i*const_factor));
|
||||
}
|
||||
}
|
||||
|
||||
if (trunc > 0)
|
||||
new_a.remove(GetSize(new_a)-trunc, trunc);
|
||||
|
||||
SigSpec new_b = {mul_din, SigSpec(State::S0, factor_bits)};
|
||||
if (param(shift, \B_SIGNED).as_bool())
|
||||
|
|
|
@ -775,6 +775,30 @@ struct SimInstance
|
|||
return did_something;
|
||||
}
|
||||
|
||||
static void log_source(RTLIL::AttrObject *src)
|
||||
{
|
||||
for (auto src : src->get_strpool_attribute(ID::src))
|
||||
log(" %s\n", src.c_str());
|
||||
}
|
||||
|
||||
void log_cell_w_hierarchy(std::string opening_verbiage, RTLIL::Cell *cell)
|
||||
{
|
||||
log_assert(cell->module == module);
|
||||
bool has_src = cell->has_attribute(ID::src);
|
||||
log("%s %s%s\n", opening_verbiage.c_str(),
|
||||
log_id(cell), has_src ? " at" : "");
|
||||
log_source(cell);
|
||||
|
||||
struct SimInstance *sim = this;
|
||||
while (sim->instance) {
|
||||
has_src = sim->instance->has_attribute(ID::src);
|
||||
log(" in instance %s of module %s%s\n", log_id(sim->instance),
|
||||
log_id(sim->instance->type), has_src ? " at" : "");
|
||||
log_source(sim->instance);
|
||||
sim = sim->parent;
|
||||
}
|
||||
}
|
||||
|
||||
void update_ph3(bool check_assertions)
|
||||
{
|
||||
for (auto &it : ff_database)
|
||||
|
@ -876,10 +900,11 @@ struct SimInstance
|
|||
log("Assumption %s.%s (%s) failed.\n", hiername().c_str(), log_id(cell), label.c_str());
|
||||
|
||||
if (cell->type == ID($assert) && en == State::S1 && a != State::S1) {
|
||||
log_cell_w_hierarchy("Failed assertion", cell);
|
||||
if (shared->serious_asserts)
|
||||
log_error("Assert %s.%s (%s) failed.\n", hiername().c_str(), log_id(cell), label.c_str());
|
||||
log_error("Assertion %s.%s (%s) failed.\n", hiername().c_str(), log_id(cell), label.c_str());
|
||||
else
|
||||
log_warning("Assert %s.%s (%s) failed.\n", hiername().c_str(), log_id(cell), label.c_str());
|
||||
log_warning("Assertion %s.%s (%s) failed.\n", hiername().c_str(), log_id(cell), label.c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,6 +66,8 @@ struct BoothPassWorker {
|
|||
RTLIL::Module *module;
|
||||
SigMap sigmap;
|
||||
int booth_counter;
|
||||
bool lowpower = false;
|
||||
bool mapped_cpa = false;
|
||||
|
||||
BoothPassWorker(RTLIL::Module *module) : module(module), sigmap(module) { booth_counter = 0; }
|
||||
|
||||
|
@ -184,6 +186,24 @@ struct BoothPassWorker {
|
|||
cor_o = module->AndGate(NEW_ID_SUFFIX(name), pp1_nor_pp0, cori_i);
|
||||
}
|
||||
|
||||
void BuildBitwiseFa(Module *mod, std::string name, const SigSpec &sig_a, const SigSpec &sig_b,
|
||||
const SigSpec &sig_c, const SigSpec &sig_x, const SigSpec &sig_y,
|
||||
const std::string &src = "")
|
||||
{
|
||||
// We can't emit a single wide full-adder cell here since
|
||||
// there would typically be feedback loops involving the cells'
|
||||
// input and output ports, and Yosys doesn't cope well with
|
||||
// those
|
||||
log_assert(sig_a.size() == sig_b.size());
|
||||
log_assert(sig_a.size() == sig_c.size());
|
||||
log_assert(sig_a.size() == sig_x.size());
|
||||
log_assert(sig_a.size() == sig_y.size());
|
||||
|
||||
for (int i = 0; i < sig_a.size(); i++)
|
||||
mod->addFa(stringf("%s[%d]", name.c_str(), i), sig_a[i], sig_b[i],
|
||||
sig_c[i], sig_x[i], sig_y[i], src);
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
for (auto cell : module->selected_cells()) {
|
||||
|
@ -258,17 +278,19 @@ struct BoothPassWorker {
|
|||
}
|
||||
log_assert(GetSize(Y) == required_op_size);
|
||||
|
||||
if (!is_signed) /* unsigned multiplier */
|
||||
CreateBoothUMult(module,
|
||||
if (!lowpower)
|
||||
CreateBoothMult(module,
|
||||
A, // multiplicand
|
||||
B, // multiplier(scanned)
|
||||
Y // result
|
||||
Y, // result
|
||||
is_signed
|
||||
);
|
||||
else /* signed multiplier */
|
||||
CreateBoothSMult(module,
|
||||
else
|
||||
CreateBoothLowpowerMult(module,
|
||||
A, // multiplicand
|
||||
B, // multiplier(scanned)
|
||||
Y // result (sized)
|
||||
Y, // result
|
||||
is_signed
|
||||
);
|
||||
|
||||
module->remove(cell);
|
||||
|
@ -276,25 +298,54 @@ struct BoothPassWorker {
|
|||
}
|
||||
}
|
||||
|
||||
SigSig WallaceSum(int width, std::vector<SigSpec> summands)
|
||||
{
|
||||
for (auto &s : summands)
|
||||
s.extend_u0(width);
|
||||
|
||||
while (summands.size() > 2) {
|
||||
std::vector<SigSpec> new_summands;
|
||||
int i;
|
||||
for (i = 0; i < (int) summands.size() - 2; i += 3) {
|
||||
SigSpec x = module->addWire(NEW_ID, width);
|
||||
SigSpec y = module->addWire(NEW_ID, width);
|
||||
BuildBitwiseFa(module, NEW_ID.str(), summands[i], summands[i + 1],
|
||||
summands[i + 2], x, y);
|
||||
new_summands.push_back(y);
|
||||
new_summands.push_back({x.extract(0, width - 1), State::S0});
|
||||
}
|
||||
|
||||
new_summands.insert(new_summands.begin(), summands.begin() + i, summands.end());
|
||||
|
||||
std::swap(summands, new_summands);
|
||||
}
|
||||
|
||||
if (!summands.size())
|
||||
return SigSig(SigSpec(width, State::S0), SigSpec(width, State::S0));
|
||||
else if (summands.size() == 1)
|
||||
return SigSig(summands[0], SigSpec(width, State::S0));
|
||||
else
|
||||
return SigSig(summands[0], summands[1]);
|
||||
}
|
||||
|
||||
/*
|
||||
Build Unsigned Multiplier.
|
||||
Build Multiplier.
|
||||
-------------------------
|
||||
Create a booth unsigned multiplier.
|
||||
Uses a generic booth multiplier with
|
||||
extra row of decoders and extended multiplier
|
||||
Uses a generic booth multiplier
|
||||
*/
|
||||
|
||||
void CreateBoothUMult(RTLIL::Module *module,
|
||||
void CreateBoothMult(RTLIL::Module *module,
|
||||
SigSpec X, // multiplicand
|
||||
SigSpec Y, // multiplier
|
||||
SigSpec Z)
|
||||
SigSpec Z,
|
||||
bool is_signed)
|
||||
{ // result
|
||||
int x_sz = X.size(), z_sz = Z.size();
|
||||
int z_sz = Z.size();
|
||||
|
||||
SigSpec one_int, two_int, s_int, sb_int;
|
||||
int encoder_count = 0;
|
||||
|
||||
BuildBoothUMultEncoders(Y, one_int, two_int, s_int, sb_int, module, encoder_count);
|
||||
BuildBoothMultEncoders(Y, one_int, two_int, s_int, sb_int, module, encoder_count, is_signed);
|
||||
|
||||
// Build the decoder rows
|
||||
// format of each Partial product to be passed to CSA
|
||||
|
@ -308,43 +359,24 @@ struct BoothPassWorker {
|
|||
|
||||
// Row 0: special case 1. Format S/.S.S.C.Data
|
||||
SigSpec ppij_row_0;
|
||||
BuildBoothUMultDecoderRow0(module, X, s_int, sb_int, one_int, two_int, ppij_row_0);
|
||||
BuildBoothMultDecoderRow0(module, X, s_int, sb_int, one_int, two_int, ppij_row_0, is_signed);
|
||||
|
||||
// data, shift, sign
|
||||
ppij_int.push_back(std::make_tuple(ppij_row_0, 0, s_int[0]));
|
||||
|
||||
for (int i = 1; i < encoder_count - 2; i++) {
|
||||
for (int i = 1; i < encoder_count; i++) {
|
||||
// format 1,S.Data.shift = encoder_ix*2,sign = sb_int[i]
|
||||
SigSpec ppij_row_n;
|
||||
|
||||
BuildBoothUMultDecoderRowN(module,
|
||||
BuildBoothMultDecoderRowN(module,
|
||||
X, // multiplicand
|
||||
one_int[i], two_int[i], s_int[i], sb_int[i], ppij_row_n, i,
|
||||
false, // include sign
|
||||
false // include constant
|
||||
is_signed
|
||||
);
|
||||
// data, shift, sign
|
||||
ppij_int.push_back(std::make_tuple(ppij_row_n, i * 2, s_int[i]));
|
||||
}
|
||||
|
||||
// Build second to last row
|
||||
// format S/,Data + sign bit
|
||||
SigSpec ppij_row_em1;
|
||||
BuildBoothUMultDecoderRowN(module, X, one_int[encoder_count - 2], two_int[encoder_count - 2], s_int[encoder_count - 2],
|
||||
sb_int[encoder_count - 2], ppij_row_em1, encoder_count - 2,
|
||||
false, // include sign
|
||||
true // no constant
|
||||
);
|
||||
ppij_int.push_back(std::make_tuple(ppij_row_em1, (encoder_count - 2) * 2, s_int[encoder_count - 2]));
|
||||
// Build last row
|
||||
// format Data + sign bit
|
||||
SigSpec ppij_row_e;
|
||||
BuildBoothUMultDecoderRowN(module, X, one_int[encoder_count - 1], two_int[encoder_count - 1], s_int[encoder_count - 1],
|
||||
sb_int[encoder_count - 1], ppij_row_e, encoder_count - 1,
|
||||
true, // no sign
|
||||
true // no constant
|
||||
);
|
||||
ppij_int.push_back(std::make_tuple(ppij_row_e, (encoder_count - 1) * 2, s_int[encoder_count - 1]));
|
||||
|
||||
// Debug dump out partial products
|
||||
// DebugDumpPP(ppij_int);
|
||||
|
@ -358,35 +390,34 @@ struct BoothPassWorker {
|
|||
for (int i = 0; i < encoder_count + 1; i++)
|
||||
aligned_pp[i].extend_u0(z_sz);
|
||||
|
||||
AlignPP(x_sz, z_sz, ppij_int, aligned_pp);
|
||||
AlignPP(z_sz, ppij_int, aligned_pp);
|
||||
|
||||
// Debug: dump out aligned partial products.
|
||||
// Later on yosys will clean up unused constants
|
||||
// DebugDumpAlignPP(aligned_pp);
|
||||
|
||||
SigSpec s_vec;
|
||||
SigSpec c_vec;
|
||||
std::vector<std::vector<RTLIL::Cell *>> debug_csa_trees;
|
||||
|
||||
debug_csa_trees.resize(z_sz);
|
||||
|
||||
BuildCSATree(module, aligned_pp, s_vec, c_vec, debug_csa_trees);
|
||||
SigSig wtree_sum = WallaceSum(z_sz, aligned_pp);
|
||||
|
||||
// Debug code: Dump out the csa trees
|
||||
// DumpCSATrees(debug_csa_trees);
|
||||
// Build the CPA to do the final accumulation.
|
||||
BuildCPA(module, s_vec, c_vec, Z);
|
||||
log_assert(wtree_sum.second[0] == State::S0);
|
||||
if (mapped_cpa)
|
||||
BuildCPA(module, wtree_sum.first, {State::S0, wtree_sum.second.extract_end(1)}, Z);
|
||||
else
|
||||
module->addAdd(NEW_ID, wtree_sum.first, {wtree_sum.second.extract_end(1), State::S0}, Z);
|
||||
}
|
||||
|
||||
/*
|
||||
Build Row 0 of decoders
|
||||
*/
|
||||
|
||||
void BuildBoothUMultDecoderRow0(RTLIL::Module *module,
|
||||
void BuildBoothMultDecoderRow0(RTLIL::Module *module,
|
||||
SigSpec X, // multiplicand
|
||||
SigSpec s_int, SigSpec sb_int, SigSpec one_int,
|
||||
SigSpec two_int, SigSpec &ppij_vec)
|
||||
SigSpec two_int, SigSpec &ppij_vec, bool is_signed)
|
||||
{
|
||||
(void)sb_int;
|
||||
(void)module;
|
||||
int x_sz = GetSize(X);
|
||||
SigBit ppij;
|
||||
|
@ -399,21 +430,32 @@ struct BoothPassWorker {
|
|||
ppij_vec.append(Bur4d_n(stringf("row0_dec_%d", i), X[i], X[i - 1],
|
||||
one_int[0], two_int[0], s_int[0]));
|
||||
|
||||
|
||||
// The redundant bit. Duplicate decoding of last bit.
|
||||
ppij_vec.append(Bur4d_msb("row0_dec_msb", X[x_sz - 1], two_int[0], s_int[0]));
|
||||
if (!is_signed) {
|
||||
ppij_vec.append(Bur4d_msb("row0_dec_msb", X.msb(), two_int[0], s_int[0]));
|
||||
} else {
|
||||
ppij_vec.append(Bur4d_n("row0_dec_msb", X.msb(), X.msb(),
|
||||
one_int[0], two_int[0], s_int[0]));
|
||||
}
|
||||
|
||||
// append the sign bits
|
||||
ppij_vec.append(s_int[0]);
|
||||
ppij_vec.append(s_int[0]);
|
||||
ppij_vec.append(sb_int[0]);
|
||||
if (is_signed) {
|
||||
SigBit e = module->XorGate(NEW_ID, s_int[0], module->AndGate(NEW_ID, X.msb(), module->OrGate(NEW_ID, two_int[0], one_int[0])));
|
||||
ppij_vec.append({module->NotGate(NEW_ID, e), e, e});
|
||||
} else {
|
||||
// append the sign bits
|
||||
ppij_vec.append({module->NotGate(NEW_ID, s_int[0]), s_int[0], s_int[0]});
|
||||
}
|
||||
}
|
||||
|
||||
// Build a generic row of decoders.
|
||||
|
||||
void BuildBoothUMultDecoderRowN(RTLIL::Module *module,
|
||||
void BuildBoothMultDecoderRowN(RTLIL::Module *module,
|
||||
SigSpec X, // multiplicand
|
||||
SigSpec one_int, SigSpec two_int, SigSpec s_int, SigSpec sb_int,
|
||||
SigSpec &ppij_vec, int row_ix, bool no_sign, bool no_constant)
|
||||
SigSpec &ppij_vec, int row_ix,
|
||||
bool is_signed)
|
||||
{
|
||||
(void)module;
|
||||
int x_sz = GetSize(X);
|
||||
|
@ -426,15 +468,14 @@ struct BoothPassWorker {
|
|||
ppij_vec.append(Bur4d_n(stringf("row_%d_dec_%d", row_ix, i), X[i], X[i - 1],
|
||||
one_int, two_int, s_int));
|
||||
|
||||
// redundant bit
|
||||
if (!is_signed) { // redundant bit
|
||||
ppij_vec.append(Bur4d_msb("row_dec_red", X[x_sz - 1], two_int, s_int));
|
||||
} else {
|
||||
ppij_vec.append(Bur4d_n(stringf("row_%d_dec_msb", row_ix), X[x_sz - 1], X[x_sz - 1],
|
||||
one_int, two_int, s_int));
|
||||
}
|
||||
|
||||
// sign bit
|
||||
if (!no_sign) // if no sign is false then make a sign bit
|
||||
ppij_vec.append(sb_int);
|
||||
|
||||
// constant bit
|
||||
if (!no_constant) // if non constant is false make a constant bit
|
||||
ppij_vec.append(!is_signed ? sb_int[0] : module->XorGate(NEW_ID, sb_int, module->AndGate(NEW_ID, X.msb(), module->OrGate(NEW_ID, two_int, one_int))));
|
||||
ppij_vec.append(State::S1);
|
||||
}
|
||||
|
||||
|
@ -591,7 +632,7 @@ struct BoothPassWorker {
|
|||
Pad out rows with zeros and left the opt pass clean them up.
|
||||
|
||||
*/
|
||||
void AlignPP(int x_sz, int z_sz, std::vector<std::tuple<SigSpec, int, SigBit>> &ppij_int,
|
||||
void AlignPP(int z_sz, std::vector<std::tuple<SigSpec, int, SigBit>> &ppij_int,
|
||||
std::vector<SigSpec> &aligned_pp)
|
||||
{
|
||||
unsigned aligned_pp_ix = aligned_pp.size() - 1;
|
||||
|
@ -611,12 +652,10 @@ struct BoothPassWorker {
|
|||
// in first column of the last partial product
|
||||
// which is at index corresponding to size of multiplicand
|
||||
{
|
||||
int prior_row_idx = get<1>(ppij_int[aligned_pp_ix - 1]);
|
||||
SigBit prior_row_sign = get<2>(ppij_int[aligned_pp_ix - 1]);
|
||||
//if (prior_row_sign) {
|
||||
log_assert(aligned_pp_ix < aligned_pp.size());
|
||||
log_assert(x_sz - 1 < (int)(aligned_pp[aligned_pp_ix].size()));
|
||||
aligned_pp[aligned_pp_ix][x_sz - 1] = prior_row_sign;
|
||||
//}
|
||||
if (prior_row_idx < z_sz)
|
||||
aligned_pp[aligned_pp_ix][prior_row_idx] = prior_row_sign;
|
||||
}
|
||||
|
||||
for (int row_ix = aligned_pp_ix - 1; row_ix >= 0; row_ix--) {
|
||||
|
@ -813,12 +852,12 @@ struct BoothPassWorker {
|
|||
}
|
||||
}
|
||||
|
||||
void BuildBoothUMultEncoders(SigSpec Y, SigSpec &one_int, SigSpec &two_int,
|
||||
SigSpec &s_int, SigSpec &sb_int, RTLIL::Module *module, int &encoder_ix)
|
||||
void BuildBoothMultEncoders(SigSpec Y, SigSpec &one_int, SigSpec &two_int,
|
||||
SigSpec &s_int, SigSpec &sb_int, RTLIL::Module *module, int &encoder_ix, bool is_signed)
|
||||
{
|
||||
int y_sz = GetSize(Y);
|
||||
|
||||
for (int y_ix = 0; y_ix < y_sz;) {
|
||||
for (int y_ix = 0; y_ix < (!is_signed ? y_sz : y_sz - 1);) {
|
||||
std::string enc_name = stringf("bur_enc_%d", encoder_ix);
|
||||
|
||||
two_int.append(module->addWire(NEW_ID_SUFFIX(stringf("two_int_%d", encoder_ix)), 1));
|
||||
|
@ -844,7 +883,7 @@ struct BoothPassWorker {
|
|||
bool need_padded_cell = false;
|
||||
|
||||
if (y_ix > y_sz - 1) {
|
||||
y0 = State::S0;
|
||||
y0 = is_signed ? Y.msb() : State::S0;
|
||||
need_padded_cell = false;
|
||||
} else {
|
||||
y0 = Y[y_ix];
|
||||
|
@ -853,7 +892,7 @@ struct BoothPassWorker {
|
|||
|
||||
if (y_ix > y_sz - 1) {
|
||||
need_padded_cell = false;
|
||||
y1 = State::S0;
|
||||
y1 = is_signed ? Y.msb() : State::S0;
|
||||
} else {
|
||||
y1 = Y[y_ix];
|
||||
y_ix++;
|
||||
|
@ -861,10 +900,10 @@ struct BoothPassWorker {
|
|||
|
||||
if (y_ix > y_sz - 1) {
|
||||
need_padded_cell = false;
|
||||
y2 = State::S0;
|
||||
y2 = is_signed ? Y.msb() : State::S0;
|
||||
} else {
|
||||
if (y_ix == y_sz - 1)
|
||||
need_padded_cell = true;
|
||||
need_padded_cell = !is_signed;
|
||||
else
|
||||
need_padded_cell = false;
|
||||
y2 = Y[y_ix];
|
||||
|
@ -902,12 +941,15 @@ struct BoothPassWorker {
|
|||
}
|
||||
|
||||
/*
|
||||
Signed Multiplier
|
||||
Low-power Multiplier
|
||||
*/
|
||||
void CreateBoothSMult(RTLIL::Module *module, SigSpec X, SigSpec Y, SigSpec Z)
|
||||
void CreateBoothLowpowerMult(RTLIL::Module *module, SigSpec X, SigSpec Y, SigSpec Z, bool is_signed)
|
||||
{ // product
|
||||
int x_sz = X.size(), y_sz = Y.size(), z_sz = Z.size();
|
||||
|
||||
if (!is_signed)
|
||||
log_error("Low-power Booth architecture is only supported on signed multipliers.\n");
|
||||
|
||||
unsigned enc_count = (y_sz / 2) + (((y_sz % 2) != 0) ? 1 : 0);
|
||||
int dec_count = x_sz + 1;
|
||||
|
||||
|
@ -1009,219 +1051,89 @@ struct BoothPassWorker {
|
|||
PPij[((encoder_ix - 1) * dec_count) + dec_count - 1], unused_op);
|
||||
}
|
||||
|
||||
//
|
||||
// instantiate the quadrant 1 cell. This is the upper right
|
||||
// quadrant which can be realized using non-booth encoded logic.
|
||||
//
|
||||
SigBit pp0_o_int, pp1_o_int, nxj_o_int, q1_carry_out;
|
||||
|
||||
BuildBoothQ1("icb_booth_q1_",
|
||||
negi_n_int[0], // negi
|
||||
cori_n_int[0], // cori
|
||||
X[0], X[1], Y[0], Y[1],
|
||||
nxj_o_int, q1_carry_out, pp0_o_int, pp1_o_int);
|
||||
|
||||
module->connect(Z[0], pp0_o_int);
|
||||
module->connect(Z[1], pp1_o_int);
|
||||
module->connect(nxj[(0 * dec_count) + 2], nxj_o_int);
|
||||
|
||||
//
|
||||
// sum up the partial products
|
||||
//
|
||||
int fa_el_ix = 0;
|
||||
int fa_row_ix = 0;
|
||||
// use 1 d arrays (2d cannot have variable sized indices)
|
||||
SigSpec fa_sum_n(State::S0, fa_row_count * fa_count);
|
||||
SigSpec fa_carry_n(State::S0, fa_row_count * fa_count);
|
||||
std::vector<SigSpec> fa_sum;
|
||||
std::vector<SigSpec> fa_carry;
|
||||
|
||||
for (fa_row_ix = 0; fa_row_ix < fa_row_count; fa_row_ix++) {
|
||||
for (fa_el_ix = 0; fa_el_ix < fa_count; fa_el_ix++) {
|
||||
fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix] =
|
||||
module->addWire(NEW_ID_SUFFIX(stringf("fa_sum_n_%d_%d", fa_row_ix, fa_el_ix)), 1);
|
||||
fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix] =
|
||||
module->addWire(NEW_ID_SUFFIX(stringf("fa_carry_n_%d_%d", fa_row_ix, fa_el_ix)), 1);
|
||||
}
|
||||
fa_sum.push_back(module->addWire(NEW_ID_SUFFIX(stringf("fa_sum_%d", fa_row_ix)), fa_count));
|
||||
fa_carry.push_back(module->addWire(NEW_ID_SUFFIX(stringf("fa_carry_%d", fa_row_ix)), fa_count));
|
||||
}
|
||||
|
||||
// full adder creation
|
||||
std::string bfa_name;
|
||||
std::string exc_inv_name;
|
||||
for (fa_row_ix = 0; fa_row_ix < fa_row_count; fa_row_ix++) {
|
||||
for (fa_el_ix = 0; fa_el_ix < fa_count; fa_el_ix++) {
|
||||
// base case: 1st row. Inputs from decoders
|
||||
// Note in rest of tree inputs from prior addition and a decoder
|
||||
if (fa_row_ix == 0) {
|
||||
// beginning
|
||||
// base case:
|
||||
// first two cells: have B input hooked to 0.
|
||||
if (fa_el_ix == 0) {
|
||||
// quadrant 1: we hard code these using non-booth
|
||||
fa_el_ix++;
|
||||
|
||||
}
|
||||
// step case
|
||||
else if (fa_el_ix >= 2 && fa_el_ix <= x_sz) {
|
||||
// middle (2...x_sz cells)
|
||||
module->addFa(NEW_ID_SUFFIX(stringf("bfa_0_step_%d_%d_L", fa_row_ix, fa_el_ix)),
|
||||
/* A */ PPij[(0 * dec_count) + fa_el_ix],
|
||||
/* B */ PPij[(1 * dec_count) + fa_el_ix - 2],
|
||||
/* C */ fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix - 1],
|
||||
/* X */ fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix],
|
||||
/* Y */ fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix]
|
||||
// base case: 1st row: Inputs from decoders
|
||||
// 1st row exception: two localized inverters due to sign extension structure
|
||||
SigBit d08_inv = module->NotGate(NEW_ID_SUFFIX("bfa_0_exc_inv1"), PPij[(0 * dec_count) + dec_count - 1]);
|
||||
SigBit d18_inv = module->NotGate(NEW_ID_SUFFIX("bfa_0_exc_inv2"), PPij[(1 * dec_count) + dec_count - 1]);
|
||||
BuildBitwiseFa(module, NEW_ID_SUFFIX("fa_row_0").str(),
|
||||
/* A */ {State::S0, d08_inv, PPij[(0 * dec_count) + x_sz], PPij.extract((0 * dec_count) + 2, x_sz - 1)},
|
||||
/* B */ {State::S1, d18_inv, PPij.extract((1 * dec_count), x_sz)},
|
||||
/* C */ fa_carry[0].extract(1, x_sz + 2),
|
||||
/* X */ fa_carry[0].extract(2, x_sz + 2),
|
||||
/* Y */ fa_sum[0].extract(2, x_sz + 2)
|
||||
);
|
||||
}
|
||||
// end 3 cells: x_sz+1.2.3
|
||||
//
|
||||
else {
|
||||
// fa_el_ix = x_sz+1
|
||||
module->addFa(NEW_ID_SUFFIX(stringf("bfa_0_se_0_%d_%d_L", fa_row_ix, fa_el_ix)),
|
||||
/* A */ PPij[(0 * dec_count) + x_sz],
|
||||
/* B */ PPij[(1 * dec_count) + fa_el_ix - 2],
|
||||
/* C */ fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix - 1],
|
||||
/* X */ fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix],
|
||||
/* Y */ fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix]
|
||||
);
|
||||
|
||||
// exception:invert ppi
|
||||
fa_el_ix++;
|
||||
SigBit d08_inv = module->NotGate(NEW_ID_SUFFIX(stringf("bfa_0_exc_inv1_%d_%d_L", fa_row_ix, fa_el_ix)),
|
||||
PPij[(0 * dec_count) + dec_count - 1]);
|
||||
|
||||
SigBit d18_inv = module->NotGate(NEW_ID_SUFFIX(stringf("bfa_0_exc_inv2_%d_%d_L", fa_row_ix, fa_el_ix)),
|
||||
PPij[(1 * dec_count) + dec_count - 1]);
|
||||
|
||||
module->addFa(NEW_ID_SUFFIX(stringf("bfa_0_se_1_%d_%d_L", fa_row_ix, fa_el_ix)),
|
||||
/* A */ d08_inv,
|
||||
/* B */ d18_inv,
|
||||
/* C */ fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix - 1],
|
||||
/* X */ fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix],
|
||||
/* Y */ fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix]
|
||||
);
|
||||
|
||||
// sign extension
|
||||
fa_el_ix++;
|
||||
|
||||
module->addFa(NEW_ID_SUFFIX(stringf("bfa_0_se_2_%d_%d_L", fa_row_ix, fa_el_ix)),
|
||||
/* A */ State::S0,
|
||||
/* B */ State::S1,
|
||||
/* C */ fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix - 1],
|
||||
/* X */ fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix],
|
||||
/* Y */ fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix]
|
||||
);
|
||||
}
|
||||
}
|
||||
module->connect(fa_carry[0][1], q1_carry_out);
|
||||
|
||||
// step case: 2nd and rest of rows. (fa_row_ix == 1...n)
|
||||
// special because these are driven by a decoder and prior fa.
|
||||
else {
|
||||
// beginning
|
||||
if (fa_el_ix == 0) {
|
||||
// first two cells: have B input hooked to 0.
|
||||
// column is offset by row_ix*2
|
||||
|
||||
module->addFa(NEW_ID_SUFFIX(stringf("bfa_base_%d_%d_L", fa_row_ix, fa_el_ix)),
|
||||
/* A */ fa_sum_n[(fa_row_ix - 1) * fa_count + 2],
|
||||
/* B */ State::S0,
|
||||
/* C */ cori_n_int[fa_row_ix],
|
||||
/* X */ fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix],
|
||||
/* Y */ fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix]
|
||||
);
|
||||
fa_el_ix++;
|
||||
|
||||
module->addFa(NEW_ID_SUFFIX(stringf("bfa_base_%d_%d_L", fa_row_ix, fa_el_ix)),
|
||||
/* A */ fa_sum_n[(fa_row_ix - 1) * fa_count + 3], // from prior full adder row
|
||||
/* B */ State::S0,
|
||||
/* C */ fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix - 1],
|
||||
/* X */ fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix],
|
||||
/* Y */ fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix]
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
else if (fa_el_ix >= 2 && fa_el_ix <= x_sz + 1) {
|
||||
// middle (2...x_sz+1 cells)
|
||||
module->addFa(NEW_ID_SUFFIX(stringf("bfa_step_%d_%d_L", fa_row_ix, fa_el_ix)),
|
||||
/* A */ fa_sum_n[(fa_row_ix - 1) * fa_count + fa_el_ix + 2],
|
||||
/* B */ PPij[(fa_row_ix + 1) * dec_count + fa_el_ix - 2],
|
||||
/* C */ fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix - 1],
|
||||
/* X */ fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix],
|
||||
/* Y */ fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix]
|
||||
);
|
||||
}
|
||||
|
||||
else if (fa_el_ix > x_sz + 1) {
|
||||
for (fa_row_ix = 1; fa_row_ix < fa_row_count; fa_row_ix++) {
|
||||
// end two bits: sign extension
|
||||
SigBit d_inv = module->NotGate(NEW_ID_SUFFIX(stringf("bfa_se_inv_%d_%d_L", fa_row_ix, fa_el_ix)),
|
||||
SigBit d_inv = module->NotGate(NEW_ID_SUFFIX(stringf("bfa_se_inv_%d_L", fa_row_ix)),
|
||||
PPij[((fa_row_ix + 1) * dec_count) + dec_count - 1]);
|
||||
|
||||
module->addFa(NEW_ID_SUFFIX(stringf("bfa_se_%d_%d_L", fa_row_ix, fa_el_ix)),
|
||||
/* A */ fa_carry_n[((fa_row_ix - 1) * fa_count) + fa_count - 1],
|
||||
/* B */ d_inv,
|
||||
/* C */ fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix - 1],
|
||||
/* X */ fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix],
|
||||
/* Y */ fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix]
|
||||
);
|
||||
fa_el_ix++;
|
||||
BuildBitwiseFa(module, NEW_ID_SUFFIX(stringf("fa_row_%d", fa_row_ix)).str(),
|
||||
/* A */ {State::S0, fa_carry[fa_row_ix - 1][fa_count - 1], fa_sum[fa_row_ix - 1].extract(2, x_sz + 2)},
|
||||
/* B */ {State::S1, d_inv, PPij.extract((fa_row_ix + 1) * dec_count, x_sz), State::S0, State::S0},
|
||||
|
||||
// sign extension
|
||||
module->addFa(NEW_ID_SUFFIX(stringf("bfa_se_%d_%d_L", fa_row_ix, fa_el_ix)),
|
||||
/* A */ State::S0,
|
||||
/* B */ State::S1,
|
||||
/* C */ fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix - 1],
|
||||
/* X */ fa_carry_n[(fa_row_ix * fa_count) + fa_el_ix],
|
||||
/* Y */ fa_sum_n[(fa_row_ix * fa_count) + fa_el_ix]
|
||||
/* C */ {fa_carry[fa_row_ix].extract(0, x_sz + 3), cori_n_int[fa_row_ix]},
|
||||
/* X */ fa_carry[fa_row_ix],
|
||||
/* Y */ fa_sum[fa_row_ix]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// instantiate the cpa
|
||||
SigSpec cpa_carry;
|
||||
if (z_sz > fa_row_count * 2)
|
||||
cpa_carry = module->addWire(NEW_ID_SUFFIX("cpa_carry"), z_sz - fa_row_count * 2);
|
||||
|
||||
for (int cix = 0; cix < z_sz; cix++)
|
||||
cpa_carry.append(module->addWire(NEW_ID_SUFFIX(stringf("cpa_carry_%d", cix)), 1));
|
||||
|
||||
for (int cpa_ix = 0; cpa_ix < z_sz; cpa_ix++) {
|
||||
// The end case where we pass the last two summands
|
||||
// from prior row directly to product output
|
||||
// without using a cpa cell. This is always
|
||||
// 0,1 index of prior fa row
|
||||
if (cpa_ix <= fa_row_count * 2 - 1) {
|
||||
for (int cpa_ix = 0; cpa_ix < fa_row_count * 2; cpa_ix += 2) {
|
||||
int fa_row_ix = cpa_ix / 2;
|
||||
module->connect(Z.extract(cpa_ix, 2), fa_sum[fa_row_ix].extract(0, 2));
|
||||
}
|
||||
|
||||
module->addBufGate(NEW_ID_SUFFIX(stringf("pp_buf_%d_driven_by_fa_row_%d", cpa_ix, fa_row_ix)),
|
||||
fa_sum_n[(fa_row_ix * fa_count) + 0], Z[cpa_ix]);
|
||||
|
||||
cpa_ix++;
|
||||
module->addBufGate(NEW_ID_SUFFIX(stringf("pp_buf_%d_driven_by_fa_row_%d", cpa_ix, fa_row_ix)),
|
||||
fa_sum_n[(fa_row_ix * fa_count) + 1], Z[cpa_ix]);
|
||||
} else {
|
||||
for (int cpa_ix = fa_row_count * 2; cpa_ix < z_sz; cpa_ix++) {
|
||||
int offset = fa_row_count * 2;
|
||||
bool base_case = cpa_ix - offset == 0 ? true : false;
|
||||
std::string cpa_name = stringf("cpa_%d", cpa_ix - offset);
|
||||
|
||||
SigBit ci;
|
||||
if (base_case)
|
||||
ci = cori_n_int[enc_count - 1];
|
||||
else
|
||||
ci = cpa_carry[cpa_ix - offset - 1];
|
||||
|
||||
SigBit ci = (cpa_ix == offset) ? cori_n_int[enc_count - 1] : cpa_carry[cpa_ix - offset - 1];
|
||||
SigBit op;
|
||||
BuildHa(cpa_name, fa_sum_n[(fa_row_count - 1) * fa_count + cpa_ix - offset + 2], ci, op,
|
||||
cpa_carry[cpa_ix - offset]);
|
||||
BuildHa(cpa_name, fa_sum[fa_row_count - 1][cpa_ix - offset + 2], ci, op, cpa_carry[cpa_ix - offset]);
|
||||
module->connect(Z[cpa_ix], op);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// instantiate the quadrant 1 cell. This is the upper right
|
||||
// quadrant which can be realized using non-booth encoded logic.
|
||||
//
|
||||
std::string q1_name = "icb_booth_q1_";
|
||||
|
||||
SigBit pp0_o_int;
|
||||
SigBit pp1_o_int;
|
||||
SigBit nxj_o_int;
|
||||
SigBit cor_o_int;
|
||||
|
||||
BuildBoothQ1(q1_name,
|
||||
negi_n_int[0], // negi
|
||||
cori_n_int[0], // cori
|
||||
|
||||
X[0], X[1], Y[0], Y[1],
|
||||
|
||||
nxj_o_int, cor_o_int, pp0_o_int, pp1_o_int);
|
||||
|
||||
module->connect(fa_sum_n[(0 * fa_count) + 0], pp0_o_int);
|
||||
module->connect(fa_sum_n[(0 * fa_count) + 1], pp1_o_int);
|
||||
module->connect(fa_carry_n[(0 * fa_count) + 1], cor_o_int);
|
||||
module->connect(nxj[(0 * dec_count) + 2], nxj_o_int);
|
||||
}
|
||||
};
|
||||
|
||||
struct BoothPass : public Pass {
|
||||
|
@ -1232,21 +1144,13 @@ struct BoothPass : public Pass {
|
|||
log("\n");
|
||||
log(" booth [selection]\n");
|
||||
log("\n");
|
||||
log("This pass replaces multiplier cells with an implementation based on the Booth\n");
|
||||
log("algorithm. It operates on $mul cells whose width of operands is at least 4x4\n");
|
||||
log("and whose width of result is at least 8. The detailed architecture is selected\n");
|
||||
log("from two options based on the signedness of the operands to the $mul cell.\n");
|
||||
log("This pass replaces multiplier cells with a radix-4 Booth-encoded implementation.\n");
|
||||
log("It operates on $mul cells whose width of operands is at least 4x4 and whose\n");
|
||||
log("width of result is at least 8.\n");
|
||||
log("\n");
|
||||
log("See the references below for the description of the architectures.\n");
|
||||
log("\n");
|
||||
log("Signed-multiplier architecture:\n");
|
||||
log("Y. J. Chang, Y. C. Cheng, S. C. Liao and C. H. Hsiao, \"A Low Power Radix-4 Booth\n");
|
||||
log("Multiplier With Pre-Encoded Mechanism,\" in IEEE Access, vol. 8, pp. 114842-114853,\n");
|
||||
log("2020, doi: 10.1109/ACCESS.2020.3003684\n");
|
||||
log("\n");
|
||||
log("Unsigned-multiplier architecture:\n");
|
||||
log("G. W. Bewick, \"Fast Multiplication: Algorithms and Implementations,\" PhD Thesis,\n");
|
||||
log("Department of Electrical Engineering, Stanford University, 1994\n");
|
||||
log(" -lowpower\n");
|
||||
log(" use an alternative low-power architecture for the generated multiplier\n");
|
||||
log(" (signed multipliers only)\n");
|
||||
log("\n");
|
||||
}
|
||||
void execute(vector<string> args, RTLIL::Design *design) override
|
||||
|
@ -1254,7 +1158,16 @@ struct BoothPass : public Pass {
|
|||
log_header(design, "Executing BOOTH pass (map to Booth multipliers).\n");
|
||||
|
||||
size_t argidx;
|
||||
bool mapped_cpa = false;
|
||||
bool lowpower = false;
|
||||
for (argidx = 1; argidx < args.size(); argidx++) {
|
||||
if (args[argidx] == "-mapped_cpa")
|
||||
// Have an undocumented option which helps with multiplier
|
||||
// verification using specialized tools (AMulet2 in particular)
|
||||
mapped_cpa = true;
|
||||
else if (args[argidx] == "-lowpower")
|
||||
lowpower = true;
|
||||
else
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
@ -1264,6 +1177,8 @@ struct BoothPass : public Pass {
|
|||
for (auto mod : design->selected_modules()) {
|
||||
if (!mod->has_processes_warn()) {
|
||||
BoothPassWorker worker(mod);
|
||||
worker.mapped_cpa = mapped_cpa;
|
||||
worker.lowpower = lowpower;
|
||||
worker.run();
|
||||
total += worker.booth_counter;
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ USING_YOSYS_NAMESPACE
|
|||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
struct SynthAchronixPass : public ScriptPass {
|
||||
SynthAchronixPass() : ScriptPass("synth_achronix", "synthesis for Acrhonix Speedster22i FPGAs.") { }
|
||||
SynthAchronixPass() : ScriptPass("synth_achronix", "synthesis for Achronix Speedster22i FPGAs.") { }
|
||||
|
||||
void help() override
|
||||
{
|
||||
|
|
|
@ -34,3 +34,4 @@ $(eval $(call add_share_file,share,techlibs/common/abc9_model.v))
|
|||
$(eval $(call add_share_file,share,techlibs/common/abc9_map.v))
|
||||
$(eval $(call add_share_file,share,techlibs/common/abc9_unmap.v))
|
||||
$(eval $(call add_share_file,share,techlibs/common/cmp2lcu.v))
|
||||
$(eval $(call add_share_file,share,techlibs/common/cmp2softlogic.v))
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
module constgtge(C, A, B, Y);
|
||||
parameter A_WIDTH = 0;
|
||||
parameter B_WIDTH = 0;
|
||||
|
||||
(* force_downto *)
|
||||
input [A_WIDTH-1:0] A;
|
||||
(* force_downto *)
|
||||
input [B_WIDTH-1:0] B;
|
||||
output Y;
|
||||
input C;
|
||||
|
||||
wire [A_WIDTH:0] ch;
|
||||
genvar n;
|
||||
generate
|
||||
if (B_WIDTH > A_WIDTH) begin
|
||||
// Fail
|
||||
end else begin
|
||||
assign ch[0] = C;
|
||||
for (n = 0; n < A_WIDTH; n = n + 1) begin
|
||||
if (n < B_WIDTH) begin
|
||||
assign ch[n + 1] = B[n] ? (ch[n] && A[n]) : (ch[n] || A[n]);
|
||||
end else begin
|
||||
assign ch[n + 1] = ch[n] || A[n];
|
||||
end
|
||||
end
|
||||
assign Y = ch[A_WIDTH];
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
||||
|
||||
module constltle(C, A, B, Y);
|
||||
parameter A_WIDTH = 0;
|
||||
parameter B_WIDTH = 0;
|
||||
|
||||
(* force_downto *)
|
||||
input [A_WIDTH-1:0] A;
|
||||
(* force_downto *)
|
||||
input [B_WIDTH-1:0] B;
|
||||
output Y;
|
||||
input C;
|
||||
|
||||
wire [A_WIDTH:0] ch;
|
||||
genvar n;
|
||||
generate
|
||||
if (B_WIDTH > A_WIDTH) begin
|
||||
// Fail
|
||||
end else begin
|
||||
assign ch[0] = C;
|
||||
for (n = 0; n < A_WIDTH; n = n + 1) begin
|
||||
if (n < B_WIDTH) begin
|
||||
assign ch[n + 1] = !B[n] ? (ch[n] && !A[n]) : (ch[n] || !A[n]);
|
||||
end else begin
|
||||
assign ch[n + 1] = ch[n] && !A[n];
|
||||
end
|
||||
end
|
||||
assign Y = ch[A_WIDTH];
|
||||
end
|
||||
endgenerate
|
||||
endmodule
|
||||
|
||||
(* techmap_celltype = "$ge $gt $le $lt" *)
|
||||
module _map_const_cmp_(A, B, Y);
|
||||
parameter A_WIDTH = 0;
|
||||
parameter B_WIDTH = 0;
|
||||
parameter Y_WIDTH = 0;
|
||||
parameter A_SIGNED = 0;
|
||||
parameter B_SIGNED = 0;
|
||||
|
||||
(* force_downto *)
|
||||
input [A_WIDTH-1:0] A;
|
||||
(* force_downto *)
|
||||
input [B_WIDTH-1:0] B;
|
||||
(* force_downto *)
|
||||
output [Y_WIDTH-1:0] Y;
|
||||
|
||||
parameter _TECHMAP_CELLTYPE_ = "";
|
||||
|
||||
parameter _TECHMAP_CONSTMSK_A_ = 0;
|
||||
parameter _TECHMAP_CONSTVAL_A_ = 0;
|
||||
parameter _TECHMAP_CONSTMSK_B_ = 0;
|
||||
parameter _TECHMAP_CONSTVAL_B_ = 0;
|
||||
|
||||
wire [1023:0] _TECHMAP_DO_ = "opt -fast;";
|
||||
|
||||
wire [A_WIDTH:0] ch;
|
||||
|
||||
genvar n;
|
||||
generate
|
||||
if (Y_WIDTH != 1 || A_SIGNED || B_SIGNED)
|
||||
wire _TECHMAP_FAIL_ = 1;
|
||||
else if (&_TECHMAP_CONSTMSK_A_) begin
|
||||
if (A_WIDTH > B_WIDTH)
|
||||
wire _TECHMAP_FAIL_ = 1;
|
||||
else if (_TECHMAP_CELLTYPE_ == "$lt" || _TECHMAP_CELLTYPE_ == "$le")
|
||||
constgtge #(.A_WIDTH(B_WIDTH), .B_WIDTH(A_WIDTH))
|
||||
_TECHMAP_REPLACE_(.A(B), .B(A), .Y(Y),
|
||||
.C(_TECHMAP_CELLTYPE_ == "$lt"));
|
||||
else
|
||||
constltle #(.A_WIDTH(B_WIDTH), .B_WIDTH(A_WIDTH))
|
||||
_TECHMAP_REPLACE_(.A(B), .B(A), .Y(Y),
|
||||
.C(_TECHMAP_CELLTYPE_ == "$gt"));
|
||||
end else if (&_TECHMAP_CONSTMSK_B_) begin
|
||||
if (B_WIDTH > A_WIDTH)
|
||||
wire _TECHMAP_FAIL_ = 1;
|
||||
else if (_TECHMAP_CELLTYPE_ == "$lt" || _TECHMAP_CELLTYPE_ == "$le")
|
||||
constltle #(.A_WIDTH(A_WIDTH), .B_WIDTH(B_WIDTH))
|
||||
_TECHMAP_REPLACE_(.A(A), .B(B), .Y(Y),
|
||||
.C(_TECHMAP_CELLTYPE_ == "$le"));
|
||||
else
|
||||
constgtge #(.A_WIDTH(A_WIDTH), .B_WIDTH(B_WIDTH))
|
||||
_TECHMAP_REPLACE_(.A(A), .B(B), .Y(Y),
|
||||
.C(_TECHMAP_CELLTYPE_ == "$ge"));
|
||||
end else
|
||||
wire _TECHMAP_FAIL_ = 1;
|
||||
endgenerate
|
||||
|
||||
endmodule
|
|
@ -93,8 +93,8 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
log(" -abc2\n");
|
||||
log(" run two passes of 'abc' for slightly improved logic density\n");
|
||||
log("\n");
|
||||
log(" -abc9\n");
|
||||
log(" use new ABC9 flow (EXPERIMENTAL)\n");
|
||||
log(" -noabc9\n");
|
||||
log(" disable use of new ABC9 flow\n");
|
||||
log("\n");
|
||||
log(" -vpr\n");
|
||||
log(" generate an output netlist (and BLIF file) suitable for VPR\n");
|
||||
|
@ -137,7 +137,7 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
retime = false;
|
||||
abc2 = false;
|
||||
vpr = false;
|
||||
abc9 = false;
|
||||
abc9 = true;
|
||||
iopad = false;
|
||||
nodsp = false;
|
||||
no_rw_check = false;
|
||||
|
@ -224,7 +224,11 @@ struct SynthEcp5Pass : public ScriptPass
|
|||
continue;
|
||||
}
|
||||
if (args[argidx] == "-abc9") {
|
||||
abc9 = true;
|
||||
// removed, ABC9 is on by default.
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-noabc9") {
|
||||
abc9 = false;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-iopad") {
|
||||
|
|
|
@ -78,8 +78,8 @@ struct SynthGowinPass : public ScriptPass
|
|||
log(" -noalu\n");
|
||||
log(" do not use ALU cells\n");
|
||||
log("\n");
|
||||
log(" -abc9\n");
|
||||
log(" use new ABC9 flow (EXPERIMENTAL)\n");
|
||||
log(" -noabc9\n");
|
||||
log(" disable use of new ABC9 flow\n");
|
||||
log("\n");
|
||||
log(" -no-rw-check\n");
|
||||
log(" marks all recognized read ports as \"return don't-care value on\n");
|
||||
|
@ -106,7 +106,7 @@ struct SynthGowinPass : public ScriptPass
|
|||
nodffe = false;
|
||||
nolutram = false;
|
||||
nowidelut = false;
|
||||
abc9 = false;
|
||||
abc9 = true;
|
||||
noiopads = false;
|
||||
noalu = false;
|
||||
no_rw_check = false;
|
||||
|
@ -170,7 +170,11 @@ struct SynthGowinPass : public ScriptPass
|
|||
continue;
|
||||
}
|
||||
if (args[argidx] == "-abc9") {
|
||||
abc9 = true;
|
||||
// removed, ABC9 is on by default.
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-noabc9") {
|
||||
abc9 = false;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-noiopads") {
|
||||
|
|
|
@ -106,8 +106,8 @@ struct SynthIce40Pass : public ScriptPass
|
|||
log(" generate an output netlist (and BLIF file) suitable for VPR\n");
|
||||
log(" (this feature is experimental and incomplete)\n");
|
||||
log("\n");
|
||||
log(" -abc9\n");
|
||||
log(" use new ABC9 flow (EXPERIMENTAL)\n");
|
||||
log(" -noabc9\n");
|
||||
log(" disable use of new ABC9 flow\n");
|
||||
log("\n");
|
||||
log(" -flowmap\n");
|
||||
log(" use FlowMap LUT techmapping instead of abc (EXPERIMENTAL)\n");
|
||||
|
@ -144,7 +144,7 @@ struct SynthIce40Pass : public ScriptPass
|
|||
noabc = false;
|
||||
abc2 = false;
|
||||
vpr = false;
|
||||
abc9 = false;
|
||||
abc9 = true;
|
||||
flowmap = false;
|
||||
device_opt = "hx";
|
||||
no_rw_check = false;
|
||||
|
@ -235,7 +235,11 @@ struct SynthIce40Pass : public ScriptPass
|
|||
continue;
|
||||
}
|
||||
if (args[argidx] == "-abc9") {
|
||||
abc9 = true;
|
||||
// removed, ABC9 is on by default.
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-noabc9") {
|
||||
abc9 = false;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-dff") {
|
||||
|
|
|
@ -127,6 +127,10 @@ struct SynthLatticePass : public ScriptPass
|
|||
log(" read/write collision\" (same result as setting the no_rw_check\n");
|
||||
log(" attribute on all memories).\n");
|
||||
log("\n");
|
||||
log(" -cmp2softlogic\n");
|
||||
log(" implement constant comparisons in soft logic, do not involve\n");
|
||||
log(" hard carry chains\n");
|
||||
log("\n");
|
||||
log("\n");
|
||||
log("The following commands are executed by this synthesis command:\n");
|
||||
help_script();
|
||||
|
@ -135,6 +139,7 @@ struct SynthLatticePass : public ScriptPass
|
|||
|
||||
string top_opt, edif_file, json_file, family;
|
||||
bool noccu2, nodffe, nobram, nolutram, nowidelut, asyncprld, flatten, dff, retime, abc2, abc9, iopad, nodsp, no_rw_check, have_dsp;
|
||||
bool cmp2softlogic;
|
||||
string postfix, arith_map, brams_map, dsp_map;
|
||||
|
||||
void clear_flags() override
|
||||
|
@ -162,6 +167,7 @@ struct SynthLatticePass : public ScriptPass
|
|||
brams_map = "";
|
||||
dsp_map = "";
|
||||
have_dsp = false;
|
||||
cmp2softlogic = false;
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
|
@ -263,6 +269,10 @@ struct SynthLatticePass : public ScriptPass
|
|||
no_rw_check = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-cmp2softlogic") {
|
||||
cmp2softlogic = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
@ -343,6 +353,8 @@ struct SynthLatticePass : public ScriptPass
|
|||
run("peepopt");
|
||||
run("opt_clean");
|
||||
run("share");
|
||||
if (cmp2softlogic)
|
||||
run("techmap -map +/cmp2softlogic.v");
|
||||
run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4");
|
||||
run("opt_expr");
|
||||
run("opt_clean");
|
||||
|
@ -350,6 +362,8 @@ struct SynthLatticePass : public ScriptPass
|
|||
run("techmap -map +/mul2dsp.v -map +/lattice/dsp_map" + dsp_map + ".v -D DSP_A_MAXWIDTH=18 -D DSP_B_MAXWIDTH=18 -D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 -D DSP_NAME=$__MUL18X18", "(unless -nodsp)");
|
||||
run("chtype -set $mul t:$__soft_mul", "(unless -nodsp)");
|
||||
}
|
||||
if (family == "xo3" || help_mode)
|
||||
run("booth", "(only if '-family xo3')");
|
||||
run("alumacc");
|
||||
run("opt");
|
||||
run("memory -nomap" + no_rw_check_opt);
|
||||
|
@ -373,7 +387,7 @@ struct SynthLatticePass : public ScriptPass
|
|||
{
|
||||
run("opt -fast -mux_undef -undriven -fine");
|
||||
run("memory_map");
|
||||
run("opt -undriven -fine");
|
||||
run("opt -undriven -fine -mux_undef");
|
||||
}
|
||||
|
||||
if (check_label("map_gates"))
|
||||
|
@ -409,6 +423,7 @@ struct SynthLatticePass : public ScriptPass
|
|||
dfflegalize_args += " -cell $_DLATCH_?_ x";
|
||||
}
|
||||
run("dfflegalize" + dfflegalize_args, "($_ALDFF_*_ only if -asyncprld, $_DLATCH_* only if not -asyncprld, $_*DFFE_* only if not -nodffe)");
|
||||
run("opt_merge");
|
||||
if ((abc9 && dff) || help_mode)
|
||||
run("zinit -all w:* t:$_DFF_?_ t:$_DFFE_??_ t:$_SDFF*", "(only if -abc9 and -dff)");
|
||||
run("techmap -D NO_LUT -map +/lattice/cells_map.v");
|
||||
|
|
|
@ -134,8 +134,8 @@ DP16K #(
|
|||
.INITVAL_3D($sformatf("0x%080x", init_slice('h3d))),
|
||||
.INITVAL_3E($sformatf("0x%080x", init_slice('h3e))),
|
||||
.INITVAL_3F($sformatf("0x%080x", init_slice('h3f))),
|
||||
.DATA_WIDTH_A($sformatf("X%d", PORT_A_WIDTH)),
|
||||
.DATA_WIDTH_B($sformatf("X%d", PORT_B_WIDTH)),
|
||||
.DATA_WIDTH_A($sformatf("X%0d", PORT_A_WIDTH)),
|
||||
.DATA_WIDTH_B($sformatf("X%0d", PORT_B_WIDTH)),
|
||||
.OUTREG_A("BYPASSED"),
|
||||
.OUTREG_B("BYPASSED"),
|
||||
.RESETMODE_A(PORT_A_OPTION_RESETMODE),
|
||||
|
@ -298,8 +298,8 @@ PDPSC16K #(
|
|||
.INITVAL_3D($sformatf("0x%080x", init_slice('h3d))),
|
||||
.INITVAL_3E($sformatf("0x%080x", init_slice('h3e))),
|
||||
.INITVAL_3F($sformatf("0x%080x", init_slice('h3f))),
|
||||
.DATA_WIDTH_W($sformatf("X%d", PORT_W_WIDTH)),
|
||||
.DATA_WIDTH_R($sformatf("X%d", PORT_R_WIDTH)),
|
||||
.DATA_WIDTH_W($sformatf("X%0d", PORT_W_WIDTH)),
|
||||
.DATA_WIDTH_R($sformatf("X%0d", PORT_R_WIDTH)),
|
||||
.OUTREG("BYPASSED"),
|
||||
.RESETMODE(PORT_R_OPTION_RESETMODE),
|
||||
.ASYNC_RST_RELEASE(PORT_R_OPTION_RESETMODE),
|
||||
|
@ -389,8 +389,8 @@ PDP16K #(
|
|||
.INITVAL_3D($sformatf("0x%080x", init_slice('h3d))),
|
||||
.INITVAL_3E($sformatf("0x%080x", init_slice('h3e))),
|
||||
.INITVAL_3F($sformatf("0x%080x", init_slice('h3f))),
|
||||
.DATA_WIDTH_W($sformatf("X%d", PORT_W_WIDTH)),
|
||||
.DATA_WIDTH_R($sformatf("X%d", PORT_R_WIDTH)),
|
||||
.DATA_WIDTH_W($sformatf("X%0d", PORT_W_WIDTH)),
|
||||
.DATA_WIDTH_R($sformatf("X%0d", PORT_R_WIDTH)),
|
||||
.OUTREG("BYPASSED"),
|
||||
.RESETMODE(PORT_R_OPTION_RESETMODE),
|
||||
.ASYNC_RST_RELEASE(PORT_R_OPTION_RESETMODE),
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
/*_pm.h
|
|
@ -1,13 +1,43 @@
|
|||
techlibs/quicklogic/qlf_k6n10f/bram_types_sim.v: techlibs/quicklogic/qlf_k6n10f/generate_bram_types_sim.py
|
||||
$(P) mkdir -p $(dir $@) && $(PYTHON_EXECUTABLE) $^ $@
|
||||
|
||||
OBJS += techlibs/quicklogic/synth_quicklogic.o
|
||||
OBJS += techlibs/quicklogic/ql_bram_merge.o
|
||||
OBJS += techlibs/quicklogic/ql_bram_types.o
|
||||
OBJS += techlibs/quicklogic/ql_dsp_simd.o
|
||||
OBJS += techlibs/quicklogic/ql_dsp_io_regs.o
|
||||
|
||||
$(eval $(call add_share_file,share/quicklogic,techlibs/quicklogic/pp3_ffs_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic,techlibs/quicklogic/pp3_lut_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic,techlibs/quicklogic/pp3_latches_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic,techlibs/quicklogic/pp3_cells_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic,techlibs/quicklogic/cells_sim.v))
|
||||
$(eval $(call add_share_file,share/quicklogic,techlibs/quicklogic/lut_sim.v))
|
||||
$(eval $(call add_share_file,share/quicklogic,techlibs/quicklogic/pp3_cells_sim.v))
|
||||
# --------------------------------------
|
||||
|
||||
$(eval $(call add_share_file,share/quicklogic,techlibs/quicklogic/abc9_model.v))
|
||||
$(eval $(call add_share_file,share/quicklogic,techlibs/quicklogic/abc9_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic,techlibs/quicklogic/abc9_unmap.v))
|
||||
OBJS += techlibs/quicklogic/ql_dsp_macc.o
|
||||
GENFILES += techlibs/quicklogic/ql_dsp_macc_pm.h techlibs/quicklogic/qlf_k6n10f/bram_types_sim.v
|
||||
techlibs/quicklogic/ql_dsp_macc.o: techlibs/quicklogic/ql_dsp_macc_pm.h
|
||||
$(eval $(call add_extra_objs,techlibs/quicklogic/ql_dsp_macc_pm.h))
|
||||
|
||||
# --------------------------------------
|
||||
|
||||
$(eval $(call add_share_file,share/quicklogic/common,techlibs/quicklogic/common/cells_sim.v))
|
||||
|
||||
$(eval $(call add_share_file,share/quicklogic/pp3,techlibs/quicklogic/pp3/ffs_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/pp3,techlibs/quicklogic/pp3/lut_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/pp3,techlibs/quicklogic/pp3/latches_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/pp3,techlibs/quicklogic/pp3/cells_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/pp3,techlibs/quicklogic/pp3/cells_sim.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/pp3,techlibs/quicklogic/pp3/abc9_model.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/pp3,techlibs/quicklogic/pp3/abc9_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/pp3,techlibs/quicklogic/pp3/abc9_unmap.v))
|
||||
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/arith_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/libmap_brams.txt))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/libmap_brams_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/brams_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/brams_sim.v))
|
||||
$(eval $(call add_gen_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/bram_types_sim.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/cells_sim.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/ffs_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/dsp_sim.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/dsp_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/dsp_final_map.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/TDP18K_FIFO.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/ufifo_ctl.v))
|
||||
$(eval $(call add_share_file,share/quicklogic/qlf_k6n10f,techlibs/quicklogic/qlf_k6n10f/sram1024x18_mem.v))
|
|
@ -1,76 +0,0 @@
|
|||
(* abc9_lut=1, lib_whitebox *)
|
||||
module LUT1 (
|
||||
output O,
|
||||
input I0
|
||||
);
|
||||
parameter [1:0] INIT = 0;
|
||||
parameter EQN = "(I0)";
|
||||
|
||||
// These timings are for PolarPro 3E; other families will need updating.
|
||||
specify
|
||||
(I0 => O) = 698; // FS -> FZ
|
||||
endspecify
|
||||
|
||||
assign O = I0 ? INIT[1] : INIT[0];
|
||||
endmodule
|
||||
|
||||
// TZ TSL TAB
|
||||
(* abc9_lut=2, lib_whitebox *)
|
||||
module LUT2 (
|
||||
output O,
|
||||
input I0, I1
|
||||
);
|
||||
parameter [3:0] INIT = 4'h0;
|
||||
parameter EQN = "(I0)";
|
||||
|
||||
// These timings are for PolarPro 3E; other families will need updating.
|
||||
specify
|
||||
(I0 => O) = 1251; // TAB -> TZ
|
||||
(I1 => O) = 1406; // TSL -> TZ
|
||||
endspecify
|
||||
|
||||
wire [1:0] s1 = I1 ? INIT[3:2] : INIT[1:0];
|
||||
assign O = I0 ? s1[1] : s1[0];
|
||||
endmodule
|
||||
|
||||
(* abc9_lut=2, lib_whitebox *)
|
||||
module LUT3 (
|
||||
output O,
|
||||
input I0, I1, I2
|
||||
);
|
||||
parameter [7:0] INIT = 8'h0;
|
||||
parameter EQN = "(I0)";
|
||||
|
||||
// These timings are for PolarPro 3E; other families will need updating.
|
||||
specify
|
||||
(I0 => O) = 1251; // TAB -> TZ
|
||||
(I1 => O) = 1406; // TSL -> TZ
|
||||
(I2 => O) = 1699; // ('TA1', 'TA2', 'TB1', 'TB2') -> TZ
|
||||
endspecify
|
||||
|
||||
wire [3:0] s2 = I2 ? INIT[7:4] : INIT[3:0];
|
||||
wire [1:0] s1 = I1 ? s2[3:2] : s2[1:0];
|
||||
assign O = I0 ? s1[1] : s1[0];
|
||||
endmodule
|
||||
|
||||
(* abc9_lut=4, lib_whitebox *)
|
||||
module LUT4 (
|
||||
output O,
|
||||
input I0, I1, I2, I3
|
||||
);
|
||||
parameter [15:0] INIT = 16'h0;
|
||||
parameter EQN = "(I0)";
|
||||
|
||||
// These timings are for PolarPro 3E; other families will need updating.
|
||||
specify
|
||||
(I0 => O) = 995; // TBS -> CZ
|
||||
(I1 => O) = 1437; // ('TAB', 'BAB') -> CZ
|
||||
(I2 => O) = 1593; // ('TSL', 'BSL') -> CZ
|
||||
(I3 => O) = 1887; // ('TA1', 'TA2', 'TB1', 'TB2', 'BA1', 'BA2', 'BB1', 'BB2') -> CZ
|
||||
endspecify
|
||||
|
||||
wire [7:0] s3 = I3 ? INIT[15:8] : INIT[7:0];
|
||||
wire [3:0] s2 = I2 ? s3[7:4] : s3[3:0];
|
||||
wire [1:0] s1 = I1 ? s2[3:2] : s2[1:0];
|
||||
assign O = I0 ? s1[1] : s1[0];
|
||||
endmodule
|
|
@ -327,3 +327,80 @@ module qlal4s3b_cell_macro (
|
|||
);
|
||||
|
||||
endmodule
|
||||
|
||||
(* abc9_lut=1, lib_whitebox *)
|
||||
module LUT1 (
|
||||
output O,
|
||||
input I0
|
||||
);
|
||||
parameter [1:0] INIT = 0;
|
||||
parameter EQN = "(I0)";
|
||||
|
||||
// These timings are for PolarPro 3E; other families will need updating.
|
||||
specify
|
||||
(I0 => O) = 698; // FS -> FZ
|
||||
endspecify
|
||||
|
||||
assign O = I0 ? INIT[1] : INIT[0];
|
||||
endmodule
|
||||
|
||||
// TZ TSL TAB
|
||||
(* abc9_lut=2, lib_whitebox *)
|
||||
module LUT2 (
|
||||
output O,
|
||||
input I0, I1
|
||||
);
|
||||
parameter [3:0] INIT = 4'h0;
|
||||
parameter EQN = "(I0)";
|
||||
|
||||
// These timings are for PolarPro 3E; other families will need updating.
|
||||
specify
|
||||
(I0 => O) = 1251; // TAB -> TZ
|
||||
(I1 => O) = 1406; // TSL -> TZ
|
||||
endspecify
|
||||
|
||||
wire [1:0] s1 = I1 ? INIT[3:2] : INIT[1:0];
|
||||
assign O = I0 ? s1[1] : s1[0];
|
||||
endmodule
|
||||
|
||||
(* abc9_lut=2, lib_whitebox *)
|
||||
module LUT3 (
|
||||
output O,
|
||||
input I0, I1, I2
|
||||
);
|
||||
parameter [7:0] INIT = 8'h0;
|
||||
parameter EQN = "(I0)";
|
||||
|
||||
// These timings are for PolarPro 3E; other families will need updating.
|
||||
specify
|
||||
(I0 => O) = 1251; // TAB -> TZ
|
||||
(I1 => O) = 1406; // TSL -> TZ
|
||||
(I2 => O) = 1699; // ('TA1', 'TA2', 'TB1', 'TB2') -> TZ
|
||||
endspecify
|
||||
|
||||
wire [3:0] s2 = I2 ? INIT[7:4] : INIT[3:0];
|
||||
wire [1:0] s1 = I1 ? s2[3:2] : s2[1:0];
|
||||
assign O = I0 ? s1[1] : s1[0];
|
||||
endmodule
|
||||
|
||||
(* abc9_lut=4, lib_whitebox *)
|
||||
module LUT4 (
|
||||
output O,
|
||||
input I0, I1, I2, I3
|
||||
);
|
||||
parameter [15:0] INIT = 16'h0;
|
||||
parameter EQN = "(I0)";
|
||||
|
||||
// These timings are for PolarPro 3E; other families will need updating.
|
||||
specify
|
||||
(I0 => O) = 995; // TBS -> CZ
|
||||
(I1 => O) = 1437; // ('TAB', 'BAB') -> CZ
|
||||
(I2 => O) = 1593; // ('TSL', 'BSL') -> CZ
|
||||
(I3 => O) = 1887; // ('TA1', 'TA2', 'TB1', 'TB2', 'BA1', 'BA2', 'BB1', 'BB2') -> CZ
|
||||
endspecify
|
||||
|
||||
wire [7:0] s3 = I3 ? INIT[15:8] : INIT[7:0];
|
||||
wire [3:0] s2 = I2 ? s3[7:4] : s3[3:0];
|
||||
wire [1:0] s1 = I1 ? s2[3:2] : s2[1:0];
|
||||
assign O = I0 ? s1[1] : s1[0];
|
||||
endmodule
|
|
@ -0,0 +1,216 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2023 N. Engelhardt <nak@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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
// ============================================================================
|
||||
|
||||
|
||||
|
||||
struct QlBramMergeWorker {
|
||||
|
||||
const RTLIL::IdString split_cell_type = ID($__QLF_TDP36K);
|
||||
const RTLIL::IdString merged_cell_type = ID($__QLF_TDP36K_MERGED);
|
||||
|
||||
// can be used to record parameter values that have to match on both sides
|
||||
typedef dict<RTLIL::IdString, RTLIL::Const> MergeableGroupKeyType;
|
||||
|
||||
RTLIL::Module *module;
|
||||
dict<MergeableGroupKeyType, pool<RTLIL::Cell*>> mergeable_groups;
|
||||
|
||||
QlBramMergeWorker(RTLIL::Module* module) : module(module)
|
||||
{
|
||||
for (RTLIL::Cell* cell : module->selected_cells())
|
||||
{
|
||||
if(cell->type != split_cell_type) continue;
|
||||
if(!cell->hasParam(ID(OPTION_SPLIT))) continue;
|
||||
if(cell->getParam(ID(OPTION_SPLIT)) != RTLIL::Const(1, 32)) continue;
|
||||
mergeable_groups[get_key(cell)].insert(cell);
|
||||
}
|
||||
}
|
||||
|
||||
static MergeableGroupKeyType get_key(RTLIL::Cell* cell)
|
||||
{
|
||||
MergeableGroupKeyType key;
|
||||
// For now, there are no restrictions on which cells can be merged
|
||||
(void) cell;
|
||||
return key;
|
||||
}
|
||||
|
||||
const dict<RTLIL::IdString, RTLIL::IdString>& param_map(bool second)
|
||||
{
|
||||
static const dict<RTLIL::IdString, RTLIL::IdString> bram1_map = {
|
||||
{ ID(INIT), ID(INIT1) },
|
||||
{ ID(PORT_A_WIDTH), ID(PORT_A1_WIDTH) },
|
||||
{ ID(PORT_B_WIDTH), ID(PORT_B1_WIDTH) },
|
||||
{ ID(PORT_A_WR_BE_WIDTH), ID(PORT_A1_WR_BE_WIDTH) },
|
||||
{ ID(PORT_B_WR_BE_WIDTH), ID(PORT_B1_WR_BE_WIDTH) }
|
||||
};
|
||||
static const dict<RTLIL::IdString, RTLIL::IdString> bram2_map = {
|
||||
{ ID(INIT), ID(INIT2) },
|
||||
{ ID(PORT_A_WIDTH), ID(PORT_A2_WIDTH) },
|
||||
{ ID(PORT_B_WIDTH), ID(PORT_B2_WIDTH) },
|
||||
{ ID(PORT_A_WR_BE_WIDTH), ID(PORT_A2_WR_BE_WIDTH) },
|
||||
{ ID(PORT_B_WR_BE_WIDTH), ID(PORT_B2_WR_BE_WIDTH) }
|
||||
};
|
||||
|
||||
if(second)
|
||||
return bram2_map;
|
||||
else
|
||||
return bram1_map;
|
||||
}
|
||||
|
||||
const dict<RTLIL::IdString, RTLIL::IdString>& port_map(bool second)
|
||||
{
|
||||
static const dict<RTLIL::IdString, RTLIL::IdString> bram1_map = {
|
||||
{ ID(PORT_A_CLK), ID(PORT_A1_CLK) },
|
||||
{ ID(PORT_B_CLK), ID(PORT_B1_CLK) },
|
||||
{ ID(PORT_A_CLK_EN), ID(PORT_A1_CLK_EN) },
|
||||
{ ID(PORT_B_CLK_EN), ID(PORT_B1_CLK_EN) },
|
||||
{ ID(PORT_A_ADDR), ID(PORT_A1_ADDR) },
|
||||
{ ID(PORT_B_ADDR), ID(PORT_B1_ADDR) },
|
||||
{ ID(PORT_A_WR_DATA), ID(PORT_A1_WR_DATA) },
|
||||
{ ID(PORT_B_WR_DATA), ID(PORT_B1_WR_DATA) },
|
||||
{ ID(PORT_A_WR_EN), ID(PORT_A1_WR_EN) },
|
||||
{ ID(PORT_B_WR_EN), ID(PORT_B1_WR_EN) },
|
||||
{ ID(PORT_A_WR_BE), ID(PORT_A1_WR_BE) },
|
||||
{ ID(PORT_B_WR_BE), ID(PORT_B1_WR_BE) },
|
||||
{ ID(PORT_A_RD_DATA), ID(PORT_A1_RD_DATA) },
|
||||
{ ID(PORT_B_RD_DATA), ID(PORT_B1_RD_DATA) }
|
||||
};
|
||||
static const dict<RTLIL::IdString, RTLIL::IdString> bram2_map = {
|
||||
{ ID(PORT_A_CLK), ID(PORT_A2_CLK) },
|
||||
{ ID(PORT_B_CLK), ID(PORT_B2_CLK) },
|
||||
{ ID(PORT_A_CLK_EN), ID(PORT_A2_CLK_EN) },
|
||||
{ ID(PORT_B_CLK_EN), ID(PORT_B2_CLK_EN) },
|
||||
{ ID(PORT_A_ADDR), ID(PORT_A2_ADDR) },
|
||||
{ ID(PORT_B_ADDR), ID(PORT_B2_ADDR) },
|
||||
{ ID(PORT_A_WR_DATA), ID(PORT_A2_WR_DATA) },
|
||||
{ ID(PORT_B_WR_DATA), ID(PORT_B2_WR_DATA) },
|
||||
{ ID(PORT_A_WR_EN), ID(PORT_A2_WR_EN) },
|
||||
{ ID(PORT_B_WR_EN), ID(PORT_B2_WR_EN) },
|
||||
{ ID(PORT_A_WR_BE), ID(PORT_A2_WR_BE) },
|
||||
{ ID(PORT_B_WR_BE), ID(PORT_B2_WR_BE) },
|
||||
{ ID(PORT_A_RD_DATA), ID(PORT_A2_RD_DATA) },
|
||||
{ ID(PORT_B_RD_DATA), ID(PORT_B2_RD_DATA) }
|
||||
};
|
||||
|
||||
if(second)
|
||||
return bram2_map;
|
||||
else
|
||||
return bram1_map;
|
||||
}
|
||||
|
||||
void merge_brams(RTLIL::Cell* bram1, RTLIL::Cell* bram2)
|
||||
{
|
||||
|
||||
// Create the new cell
|
||||
RTLIL::Cell* merged = module->addCell(NEW_ID, merged_cell_type);
|
||||
log_debug("Merging split BRAM cells %s and %s -> %s\n", log_id(bram1->name), log_id(bram2->name), log_id(merged->name));
|
||||
|
||||
for (auto &it : param_map(false))
|
||||
{
|
||||
if(bram1->hasParam(it.first))
|
||||
merged->setParam(it.second, bram1->getParam(it.first));
|
||||
}
|
||||
for (auto &it : param_map(true))
|
||||
{
|
||||
if(bram2->hasParam(it.first))
|
||||
merged->setParam(it.second, bram2->getParam(it.first));
|
||||
}
|
||||
|
||||
for (auto &it : port_map(false))
|
||||
{
|
||||
if (bram1->hasPort(it.first))
|
||||
merged->setPort(it.second, bram1->getPort(it.first));
|
||||
else
|
||||
log_error("Can't find port %s on cell %s!\n", log_id(it.first), log_id(bram1->name));
|
||||
}
|
||||
for (auto &it : port_map(true))
|
||||
{
|
||||
if (bram2->hasPort(it.first))
|
||||
merged->setPort(it.second, bram2->getPort(it.first));
|
||||
else
|
||||
log_error("Can't find port %s on cell %s!\n", log_id(it.first), log_id(bram2->name));
|
||||
}
|
||||
merged->attributes = bram1->attributes;
|
||||
for (auto attr: bram2->attributes)
|
||||
if (!merged->has_attribute(attr.first))
|
||||
merged->attributes.insert(attr);
|
||||
|
||||
// Remove the old cells
|
||||
module->remove(bram1);
|
||||
module->remove(bram2);
|
||||
|
||||
}
|
||||
|
||||
void merge_bram_groups()
|
||||
{
|
||||
for (auto &it : mergeable_groups)
|
||||
{
|
||||
while (it.second.size() > 1)
|
||||
{
|
||||
merge_brams(it.second.pop(), it.second.pop());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct QlBramMergePass : public Pass {
|
||||
|
||||
QlBramMergePass() : Pass("ql_bram_merge", "Infers QuickLogic k6n10f BRAM pairs that can operate independently") {}
|
||||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" ql_bram_merge [selection]\n");
|
||||
log("\n");
|
||||
log(" This pass identifies k6n10f 18K BRAM cells and packs pairs of them together\n");
|
||||
log(" into a TDP36K cell operating in split mode\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(design, "Executing QL_BRAM_MERGE pass.\n");
|
||||
|
||||
size_t argidx = 1;
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (RTLIL::Module* module : design->selected_modules())
|
||||
{
|
||||
QlBramMergeWorker worker(module);
|
||||
worker.merge_bram_groups();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} QlBramMergePass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,165 @@
|
|||
/*
|
||||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2023 N. Engelhardt <nak@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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
// ============================================================================
|
||||
|
||||
|
||||
struct QlBramTypesPass : public Pass {
|
||||
|
||||
QlBramTypesPass() : Pass("ql_bram_types", "Change TDP36K type to subtypes") {}
|
||||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" ql_bram_types [selection]\n");
|
||||
log("\n");
|
||||
log(" This pass changes the type of TDP36K cells to different types based on the\n");
|
||||
log(" configuration of the cell.\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
int width_for_mode(int mode){
|
||||
// 1: mode = 3'b101;
|
||||
// 2: mode = 3'b110;
|
||||
// 4: mode = 3'b100;
|
||||
// 8,9: mode = 3'b001;
|
||||
// 16, 18: mode = 3'b010;
|
||||
// 32, 36: mode = 3'b011;
|
||||
switch (mode)
|
||||
{
|
||||
case 1:
|
||||
return 9;
|
||||
case 2:
|
||||
return 18;
|
||||
case 3:
|
||||
return 36;
|
||||
case 4:
|
||||
return 4;
|
||||
case 5:
|
||||
return 1;
|
||||
case 6:
|
||||
return 2;
|
||||
default:
|
||||
log_error("Invalid mode: %x", mode);
|
||||
}
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
log_header(design, "Executing QL_BRAM_TYPES pass.\n");
|
||||
|
||||
size_t argidx = 1;
|
||||
extra_args(args, argidx, design);
|
||||
|
||||
for (RTLIL::Module* module : design->selected_modules())
|
||||
for (RTLIL::Cell* cell: module->selected_cells())
|
||||
{
|
||||
if (cell->type != ID(TDP36K) || !cell->hasParam(ID(MODE_BITS)))
|
||||
continue;
|
||||
|
||||
RTLIL::Const mode_bits = cell->getParam(ID(MODE_BITS));
|
||||
|
||||
bool split = mode_bits.extract(80).as_bool();
|
||||
|
||||
bool FMODE1_i = mode_bits.extract(13).as_bool();
|
||||
bool FMODE2_i = mode_bits.extract(54).as_bool();
|
||||
if (FMODE1_i != FMODE2_i) {
|
||||
log_debug("Can't change type of mixed use TDP36K block: FMODE1_i = %s, FMODE2_i = %s\n", FMODE1_i ? "true" : "false", FMODE2_i ? "true" : "false");
|
||||
continue;
|
||||
}
|
||||
bool is_fifo = FMODE1_i;
|
||||
|
||||
bool SYNC_FIFO1_i = mode_bits.extract(0).as_bool();
|
||||
bool SYNC_FIFO2_i = mode_bits.extract(41).as_bool();
|
||||
if (SYNC_FIFO1_i != SYNC_FIFO2_i) {
|
||||
log_debug("Can't change type of mixed use TDP36K block: SYNC_FIFO1_i = %s, SYNC_FIFO2_i = %s\n", SYNC_FIFO1_i ? "true" : "false", SYNC_FIFO2_i ? "true" : "false");
|
||||
continue;
|
||||
}
|
||||
bool sync_fifo = SYNC_FIFO1_i;
|
||||
|
||||
int RMODE_A1_i = mode_bits.extract(1, 3).as_int();
|
||||
int RMODE_B1_i = mode_bits.extract(4, 3).as_int();
|
||||
int WMODE_A1_i = mode_bits.extract(7, 3).as_int();
|
||||
int WMODE_B1_i = mode_bits.extract(10, 3).as_int();
|
||||
|
||||
int RMODE_A2_i = mode_bits.extract(42, 3).as_int();
|
||||
int RMODE_B2_i = mode_bits.extract(45, 3).as_int();
|
||||
int WMODE_A2_i = mode_bits.extract(48, 3).as_int();
|
||||
int WMODE_B2_i = mode_bits.extract(51, 3).as_int();
|
||||
|
||||
// TODO: should these be a warning or an error?
|
||||
if (RMODE_A1_i != WMODE_A1_i) {
|
||||
log_warning("Can't change type of misconfigured TDP36K block: Port A1 configured with read width = %d different from write width = %d\n", width_for_mode(RMODE_A1_i), width_for_mode(WMODE_A1_i));
|
||||
continue;
|
||||
}
|
||||
if (RMODE_B1_i != WMODE_B1_i) {
|
||||
log_warning("Can't change type of misconfigured TDP36K block: Port B1 configured with read width = %d different from write width = %d\n", width_for_mode(RMODE_B1_i), width_for_mode(WMODE_B1_i));
|
||||
continue;
|
||||
}
|
||||
if (RMODE_A2_i != WMODE_A2_i) {
|
||||
log_warning("Can't change type of misconfigured TDP36K block: Port A2 configured with read width = %d different from write width = %d\n", width_for_mode(RMODE_A2_i), width_for_mode(WMODE_A2_i));
|
||||
continue;
|
||||
}
|
||||
if (RMODE_B2_i != WMODE_B2_i) {
|
||||
log_warning("Can't change type of misconfigured TDP36K block: Port B2 configured with read width = %d different from write width = %d\n", width_for_mode(RMODE_B2_i), width_for_mode(WMODE_B2_i));
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: For nonsplit blocks, should RMODE_A1_i == RMODE_A2_i etc be checked/enforced?
|
||||
|
||||
std::string type = "TDP36K";
|
||||
if (is_fifo) {
|
||||
type += "_FIFO_";
|
||||
if (sync_fifo)
|
||||
type += "SYNC_";
|
||||
else
|
||||
type += "ASYNC_";
|
||||
} else
|
||||
type += "_BRAM_";
|
||||
|
||||
if (split) {
|
||||
type += stringf("A1_X%d_", width_for_mode(RMODE_A1_i));
|
||||
type += stringf("B1_X%d_", width_for_mode(RMODE_B1_i));
|
||||
type += stringf("A2_X%d_", width_for_mode(RMODE_A2_i));
|
||||
type += stringf("B2_X%d_", width_for_mode(RMODE_B2_i));
|
||||
type += "split";
|
||||
} else {
|
||||
type += stringf("A_X%d_", width_for_mode(RMODE_A1_i));
|
||||
type += stringf("B_X%d_", width_for_mode(RMODE_B1_i));
|
||||
type += "nonsplit";
|
||||
}
|
||||
|
||||
cell->type = RTLIL::escape_id(type);
|
||||
log_debug("Changed type of memory cell %s to %s\n", log_id(cell->name), log_id(cell->type));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} QlBramMergePass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* Copyright 2020-2022 F4PGA Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
#define MODE_BITS_REGISTER_INPUTS_ID 92
|
||||
#define MODE_BITS_OUTPUT_SELECT_START_ID 81
|
||||
#define MODE_BITS_OUTPUT_SELECT_WIDTH 3
|
||||
|
||||
// ============================================================================
|
||||
|
||||
struct QlDspIORegs : public Pass {
|
||||
const std::vector<IdString> ports2del_mult = {ID(load_acc), ID(subtract), ID(acc_fir), ID(dly_b),
|
||||
ID(saturate_enable), ID(shift_right), ID(round)};
|
||||
const std::vector<IdString> ports2del_mult_acc = {ID(acc_fir), ID(dly_b)};
|
||||
|
||||
SigMap sigmap;
|
||||
|
||||
// ..........................................
|
||||
|
||||
QlDspIORegs() : Pass("ql_dsp_io_regs", "change types of QL_DSP2 depending on configuration") {}
|
||||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" ql_dsp_io_regs [options] [selection]\n");
|
||||
log("\n");
|
||||
log("This pass looks for QL_DSP2 cells and changes their cell type depending on their\n");
|
||||
log("configuration.\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> a_Args, RTLIL::Design *a_Design) override
|
||||
{
|
||||
log_header(a_Design, "Executing QL_DSP_IO_REGS pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < a_Args.size(); argidx++) {
|
||||
break;
|
||||
}
|
||||
extra_args(a_Args, argidx, a_Design);
|
||||
|
||||
for (auto module : a_Design->selected_modules()) {
|
||||
ql_dsp_io_regs_pass(module);
|
||||
}
|
||||
}
|
||||
|
||||
void ql_dsp_io_regs_pass(RTLIL::Module *module)
|
||||
{
|
||||
sigmap.set(module);
|
||||
|
||||
for (auto cell : module->cells()) {
|
||||
if (cell->type != ID(QL_DSP2))
|
||||
continue;
|
||||
|
||||
// If the cell does not have the "is_inferred" attribute set
|
||||
// then don't touch it.
|
||||
if (!cell->get_bool_attribute(ID(is_inferred)))
|
||||
continue;
|
||||
|
||||
// Get DSP configuration
|
||||
for (auto cfg_port : {ID(register_inputs), ID(output_select)})
|
||||
if (!cell->hasPort(cfg_port) || !sigmap(cell->getPort(cfg_port)).is_fully_const())
|
||||
log_error("Missing or non-constant '%s' port on DSP cell %s\n",
|
||||
log_id(cfg_port), log_id(cell));
|
||||
int reg_in_i = sigmap(cell->getPort(ID(register_inputs))).as_int();
|
||||
int out_sel_i = sigmap(cell->getPort(ID(output_select))).as_int();
|
||||
|
||||
// Get the feedback port
|
||||
if (!cell->hasPort(ID(feedback)))
|
||||
log_error("Missing 'feedback' port on %s", log_id(cell));
|
||||
SigSpec feedback = sigmap(cell->getPort(ID(feedback)));
|
||||
|
||||
// Check the top two bits on 'feedback' to be constant zero.
|
||||
// That's what we are expecting from inference.
|
||||
if (feedback.extract(1, 2) != SigSpec(0, 2))
|
||||
log_error("Unexpected feedback configuration on %s\n", log_id(cell));
|
||||
|
||||
// Build new type name
|
||||
std::string new_type = "\\QL_DSP2_MULT";
|
||||
|
||||
// Decide if we should be deleting the clock port
|
||||
bool del_clk = true;
|
||||
|
||||
switch (out_sel_i) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 5:
|
||||
case 7:
|
||||
del_clk = false;
|
||||
new_type += "ACC";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (reg_in_i) {
|
||||
del_clk = false;
|
||||
new_type += "_REGIN";
|
||||
}
|
||||
|
||||
if (out_sel_i > 3) {
|
||||
del_clk = false;
|
||||
new_type += "_REGOUT";
|
||||
}
|
||||
|
||||
// Set new type name
|
||||
cell->type = RTLIL::IdString(new_type);
|
||||
|
||||
std::vector<std::string> ports2del;
|
||||
|
||||
if (del_clk)
|
||||
cell->unsetPort(ID(clk));
|
||||
|
||||
switch (out_sel_i) {
|
||||
case 0:
|
||||
case 4:
|
||||
case 6:
|
||||
for (auto port : ports2del_mult)
|
||||
cell->unsetPort(port);
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
case 5:
|
||||
case 7:
|
||||
for (auto port : ports2del_mult_acc)
|
||||
cell->unsetPort(port);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} QlDspIORegs;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Copyright 2020-2022 F4PGA Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
*/
|
||||
|
||||
#include "kernel/sigtools.h"
|
||||
#include "kernel/yosys.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
#include "techlibs/quicklogic/ql_dsp_macc_pm.h"
|
||||
|
||||
// ============================================================================
|
||||
|
||||
static void create_ql_macc_dsp(ql_dsp_macc_pm &pm)
|
||||
{
|
||||
auto &st = pm.st_ql_dsp_macc;
|
||||
|
||||
// Get port widths
|
||||
size_t a_width = GetSize(st.mul->getPort(ID(A)));
|
||||
size_t b_width = GetSize(st.mul->getPort(ID(B)));
|
||||
size_t z_width = GetSize(st.ff->getPort(ID(Q)));
|
||||
|
||||
size_t min_width = std::min(a_width, b_width);
|
||||
size_t max_width = std::max(a_width, b_width);
|
||||
|
||||
// Signed / unsigned
|
||||
bool ab_signed = st.mul->getParam(ID(A_SIGNED)).as_bool();
|
||||
log_assert(ab_signed == st.mul->getParam(ID(B_SIGNED)).as_bool());
|
||||
|
||||
// Determine DSP type or discard if too narrow / wide
|
||||
RTLIL::IdString type;
|
||||
size_t tgt_a_width;
|
||||
size_t tgt_b_width;
|
||||
size_t tgt_z_width;
|
||||
|
||||
string cell_base_name = "dsp_t1";
|
||||
string cell_size_name = "";
|
||||
string cell_cfg_name = "";
|
||||
string cell_full_name = "";
|
||||
|
||||
if (min_width <= 2 && max_width <= 2 && z_width <= 4) {
|
||||
log_debug("\trejected: too narrow (%zd %zd %zd)\n", min_width, max_width, z_width);
|
||||
return;
|
||||
} else if (min_width <= 9 && max_width <= 10 && z_width <= 19) {
|
||||
cell_size_name = "_10x9x32";
|
||||
tgt_a_width = 10;
|
||||
tgt_b_width = 9;
|
||||
tgt_z_width = 19;
|
||||
} else if (min_width <= 18 && max_width <= 20 && z_width <= 38) {
|
||||
cell_size_name = "_20x18x64";
|
||||
tgt_a_width = 20;
|
||||
tgt_b_width = 18;
|
||||
tgt_z_width = 38;
|
||||
} else {
|
||||
log_debug("\trejected: too wide (%zd %zd %zd)\n", min_width, max_width, z_width);
|
||||
return;
|
||||
}
|
||||
|
||||
type = RTLIL::escape_id(cell_base_name + cell_size_name + "_cfg_ports");
|
||||
log("Inferring MACC %zux%zu->%zu as %s from:\n", a_width, b_width, z_width, log_id(type));
|
||||
|
||||
for (auto cell : {st.mul, st.add, st.mux, st.ff})
|
||||
if (cell)
|
||||
log(" %s (%s)\n", log_id(cell), log_id(cell->type));
|
||||
|
||||
// Add the DSP cell
|
||||
RTLIL::Cell *cell = pm.module->addCell(NEW_ID, type);
|
||||
|
||||
// Set attributes
|
||||
cell->set_bool_attribute(ID(is_inferred), true);
|
||||
|
||||
// Get input/output data signals
|
||||
RTLIL::SigSpec sig_a, sig_b, sig_z;
|
||||
sig_a = st.mul->getPort(ID(A));
|
||||
sig_b = st.mul->getPort(ID(B));
|
||||
sig_z = st.output_registered ? st.ff->getPort(ID(Q)) : st.ff->getPort(ID(D));
|
||||
|
||||
if (a_width < b_width)
|
||||
std::swap(sig_a, sig_b);
|
||||
|
||||
// Connect input data ports, sign extend / pad with zeros
|
||||
sig_a.extend_u0(tgt_a_width, ab_signed);
|
||||
sig_b.extend_u0(tgt_b_width, ab_signed);
|
||||
cell->setPort(ID(a_i), sig_a);
|
||||
cell->setPort(ID(b_i), sig_b);
|
||||
|
||||
// Connect output data port, pad if needed
|
||||
if ((size_t) GetSize(sig_z) < tgt_z_width) {
|
||||
auto *wire = pm.module->addWire(NEW_ID, tgt_z_width - GetSize(sig_z));
|
||||
sig_z.append(wire);
|
||||
}
|
||||
cell->setPort(ID(z_o), sig_z);
|
||||
|
||||
// Connect clock, reset and enable
|
||||
cell->setPort(ID(clock_i), st.ff->getPort(ID(CLK)));
|
||||
|
||||
RTLIL::SigSpec rst;
|
||||
RTLIL::SigSpec ena;
|
||||
|
||||
if (st.ff->hasPort(ID(ARST))) {
|
||||
if (st.ff->getParam(ID(ARST_POLARITY)).as_int() != 1) {
|
||||
rst = pm.module->Not(NEW_ID, st.ff->getPort(ID(ARST)));
|
||||
} else {
|
||||
rst = st.ff->getPort(ID(ARST));
|
||||
}
|
||||
} else {
|
||||
rst = RTLIL::SigSpec(RTLIL::S0);
|
||||
}
|
||||
|
||||
if (st.ff->hasPort(ID(EN))) {
|
||||
if (st.ff->getParam(ID(EN_POLARITY)).as_int() != 1) {
|
||||
ena = pm.module->Not(NEW_ID, st.ff->getPort(ID(EN)));
|
||||
} else {
|
||||
ena = st.ff->getPort(ID(EN));
|
||||
}
|
||||
} else {
|
||||
ena = RTLIL::SigSpec(RTLIL::S1);
|
||||
}
|
||||
|
||||
cell->setPort(ID(reset_i), rst);
|
||||
cell->setPort(ID(load_acc_i), ena);
|
||||
|
||||
// Insert feedback_i control logic used for clearing / loading the accumulator
|
||||
if (st.mux_in_pattern) {
|
||||
RTLIL::SigSpec sig_s = st.mux->getPort(ID(S));
|
||||
|
||||
// Depending on the mux port ordering insert inverter if needed
|
||||
log_assert(st.mux_ab.in(ID(A), ID(B)));
|
||||
if (st.mux_ab == ID(A))
|
||||
sig_s = pm.module->Not(NEW_ID, sig_s);
|
||||
|
||||
// Assemble the full control signal for the feedback_i port
|
||||
RTLIL::SigSpec sig_f;
|
||||
sig_f.append(sig_s);
|
||||
sig_f.append(RTLIL::S0);
|
||||
sig_f.append(RTLIL::S0);
|
||||
cell->setPort(ID(feedback_i), sig_f);
|
||||
}
|
||||
// No acc clear/load
|
||||
else {
|
||||
cell->setPort(ID(feedback_i), RTLIL::SigSpec(RTLIL::S0, 3));
|
||||
}
|
||||
|
||||
// Connect control ports
|
||||
cell->setPort(ID(unsigned_a_i), RTLIL::SigSpec(ab_signed ? RTLIL::S0 : RTLIL::S1));
|
||||
cell->setPort(ID(unsigned_b_i), RTLIL::SigSpec(ab_signed ? RTLIL::S0 : RTLIL::S1));
|
||||
|
||||
// Connect config bits
|
||||
cell->setPort(ID(saturate_enable_i), RTLIL::SigSpec(RTLIL::S0));
|
||||
cell->setPort(ID(shift_right_i), RTLIL::SigSpec(RTLIL::S0, 6));
|
||||
cell->setPort(ID(round_i), RTLIL::SigSpec(RTLIL::S0));
|
||||
cell->setPort(ID(register_inputs_i), RTLIL::SigSpec(RTLIL::S0));
|
||||
// 3 - output post acc; 1 - output pre acc
|
||||
cell->setPort(ID(output_select_i), RTLIL::Const(st.output_registered ? 1 : 3, 3));
|
||||
|
||||
bool subtract = (st.add->type == ID($sub));
|
||||
cell->setPort(ID(subtract_i), RTLIL::SigSpec(subtract ? RTLIL::S1 : RTLIL::S0));
|
||||
|
||||
// Mark the cells for removal
|
||||
pm.autoremove(st.mul);
|
||||
pm.autoremove(st.add);
|
||||
if (st.mux != nullptr) {
|
||||
pm.autoremove(st.mux);
|
||||
}
|
||||
pm.autoremove(st.ff);
|
||||
}
|
||||
|
||||
struct QlDspMacc : public Pass {
|
||||
QlDspMacc() : Pass("ql_dsp_macc", "infer QuickLogic multiplier-accumulator DSP cells") {}
|
||||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" ql_dsp_macc [selection]\n");
|
||||
log("\n");
|
||||
log("This pass looks for a multiply-accumulate pattern based on which it infers a\n");
|
||||
log("QuickLogic DSP cell.\n");
|
||||
log("\n");
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> a_Args, RTLIL::Design *a_Design) override
|
||||
{
|
||||
log_header(a_Design, "Executing QL_DSP_MACC pass.\n");
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < a_Args.size(); argidx++) {
|
||||
break;
|
||||
}
|
||||
extra_args(a_Args, argidx, a_Design);
|
||||
|
||||
for (auto module : a_Design->selected_modules())
|
||||
ql_dsp_macc_pm(module, module->selected_cells()).run_ql_dsp_macc(create_ql_macc_dsp);
|
||||
}
|
||||
|
||||
} QlDspMacc;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1,77 @@
|
|||
pattern ql_dsp_macc
|
||||
// Rough sketch: (mux is optional)
|
||||
//
|
||||
// /-----------------------\
|
||||
// | |
|
||||
// \ / |
|
||||
// mul ----> add -----> mux -----> ff -+---->
|
||||
// | /\
|
||||
// | |
|
||||
// --------------
|
||||
|
||||
state <IdString> add_ba
|
||||
state <IdString> mux_ab
|
||||
|
||||
// Is the output taken from before or after the FF?
|
||||
state <bool> output_registered
|
||||
|
||||
// Is there a mux in the pattern?
|
||||
state <bool> mux_in_pattern
|
||||
|
||||
code mux_in_pattern
|
||||
mux_in_pattern = false;
|
||||
branch;
|
||||
mux_in_pattern = true;
|
||||
endcode
|
||||
|
||||
// The multiplier is at the center of our pattern
|
||||
match mul
|
||||
select mul->type.in($mul)
|
||||
// It has either two or three consumers depending on whether there's a mux
|
||||
// in the pattern or not
|
||||
select nusers(port(mul, \Y)) <= 3
|
||||
filter nusers(port(mul, \Y)) == (mux_in_pattern ? 3 : 2)
|
||||
endmatch
|
||||
|
||||
code output_registered
|
||||
output_registered = false;
|
||||
branch;
|
||||
output_registered = true;
|
||||
endcode
|
||||
|
||||
match add
|
||||
select add->type.in($add, $sub)
|
||||
choice <IdString> AB {\A, \B}
|
||||
define <IdString> BA (AB == \A ? \B : \A)
|
||||
// One input to the adder is fed by the multiplier
|
||||
index <SigSpec> port(add, AB) === port(mul, \Y)
|
||||
// Save the other input port, it needs to be fed by the flip-flop
|
||||
set add_ba BA
|
||||
// Adder has either two or three consumers; it will have three consumers
|
||||
// IFF there's no mux in the pattern and the multiplier-accumulator result
|
||||
// is taken unregistered
|
||||
filter nusers(port(add, \Y)) == (!mux_in_pattern && !output_registered ? 3 : 2)
|
||||
endmatch
|
||||
|
||||
match mux
|
||||
if mux_in_pattern
|
||||
select mux->type.in($mux)
|
||||
choice <IdString> AB {\A, \B}
|
||||
define <IdString> BA (AB == \A ? \B : \A)
|
||||
index <SigSpec> port(mux, AB) === port(mul, \Y)
|
||||
index <SigSpec> port(mux, BA) === port(add, \Y)
|
||||
filter nusers(port(mux, \Y)) == (output_registered ? 2 : 3)
|
||||
set mux_ab AB
|
||||
endmatch
|
||||
|
||||
match ff
|
||||
select ff->type.in($dff, $adff, $dffe, $adffe)
|
||||
select param(ff, \CLK_POLARITY).as_bool()
|
||||
index <SigSpec> port(ff, \D) === mux_in_pattern ? port(mux, \Y) : port(add, \Y);
|
||||
index <SigSpec> port(ff, \Q) === port(add, add_ba)
|
||||
filter nusers(port(ff, \Q)) == (output_registered ? 3 : 2)
|
||||
endmatch
|
||||
|
||||
code
|
||||
accept;
|
||||
endcode
|
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* Copyright 2020-2022 F4PGA Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "kernel/log.h"
|
||||
#include "kernel/register.h"
|
||||
#include "kernel/rtlil.h"
|
||||
#include "kernel/sigtools.h"
|
||||
|
||||
USING_YOSYS_NAMESPACE
|
||||
PRIVATE_NAMESPACE_BEGIN
|
||||
|
||||
|
||||
// ============================================================================
|
||||
|
||||
struct QlDspSimdPass : public Pass {
|
||||
|
||||
QlDspSimdPass() : Pass("ql_dsp_simd", "merge QuickLogic K6N10f DSP pairs to operate in SIMD mode") {}
|
||||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" ql_dsp_simd [selection]\n");
|
||||
log("\n");
|
||||
log("This pass identifies K6N10f DSP cells with identical configuration and pack pairs\n");
|
||||
log("of them together into other DSP cells that can perform SIMD operation.\n");
|
||||
}
|
||||
|
||||
// ..........................................
|
||||
|
||||
/// Describes DSP config unique to a whole DSP cell
|
||||
struct DspConfig {
|
||||
// Port connections
|
||||
dict<RTLIL::IdString, RTLIL::SigSpec> connections;
|
||||
|
||||
DspConfig() = default;
|
||||
|
||||
DspConfig(const DspConfig &ref) = default;
|
||||
DspConfig(DspConfig &&ref) = default;
|
||||
|
||||
unsigned int hash() const { return connections.hash(); }
|
||||
|
||||
bool operator==(const DspConfig &ref) const { return connections == ref.connections; }
|
||||
};
|
||||
|
||||
// ..........................................
|
||||
|
||||
// DSP control and config ports to consider and how to map them to ports
|
||||
// of the target DSP cell
|
||||
const std::vector<std::pair<IdString, IdString>> m_DspCfgPorts = {
|
||||
std::make_pair(ID(clock_i), ID(clk)),
|
||||
std::make_pair(ID(reset_i), ID(reset)),
|
||||
std::make_pair(ID(feedback_i), ID(feedback)),
|
||||
std::make_pair(ID(load_acc_i), ID(load_acc)),
|
||||
std::make_pair(ID(unsigned_a_i), ID(unsigned_a)),
|
||||
std::make_pair(ID(unsigned_b_i), ID(unsigned_b)),
|
||||
std::make_pair(ID(subtract_i), ID(subtract)),
|
||||
std::make_pair(ID(output_select_i), ID(output_select)),
|
||||
std::make_pair(ID(saturate_enable_i), ID(saturate_enable)),
|
||||
std::make_pair(ID(shift_right_i), ID(shift_right)),
|
||||
std::make_pair(ID(round_i), ID(round)),
|
||||
std::make_pair(ID(register_inputs_i), ID(register_inputs))
|
||||
};
|
||||
|
||||
const int m_ModeBitsSize = 80;
|
||||
|
||||
// DSP data ports and how to map them to ports of the target DSP cell
|
||||
const std::vector<std::pair<IdString, IdString>> m_DspDataPorts = {
|
||||
std::make_pair(ID(a_i), ID(a)),
|
||||
std::make_pair(ID(b_i), ID(b)),
|
||||
std::make_pair(ID(acc_fir_i), ID(acc_fir)),
|
||||
std::make_pair(ID(z_o), ID(z)),
|
||||
std::make_pair(ID(dly_b_o), ID(dly_b))
|
||||
};
|
||||
|
||||
// DSP parameters
|
||||
const std::vector<std::string> m_DspParams = {"COEFF_3", "COEFF_2", "COEFF_1", "COEFF_0"};
|
||||
|
||||
// Source DSP cell type (SISD)
|
||||
const IdString m_SisdDspType = ID(dsp_t1_10x9x32);
|
||||
|
||||
// Target DSP cell types for the SIMD mode
|
||||
const IdString m_SimdDspType = ID(QL_DSP2);
|
||||
|
||||
/// Temporary SigBit to SigBit helper map.
|
||||
SigMap sigmap;
|
||||
|
||||
// ..........................................
|
||||
|
||||
void execute(std::vector<std::string> a_Args, RTLIL::Design *a_Design) override
|
||||
{
|
||||
log_header(a_Design, "Executing QL_DSP_SIMD pass.\n");
|
||||
|
||||
// Parse args
|
||||
extra_args(a_Args, 1, a_Design);
|
||||
|
||||
// Process modules
|
||||
for (auto module : a_Design->selected_modules()) {
|
||||
// Setup the SigMap
|
||||
sigmap.set(module);
|
||||
|
||||
// Assemble DSP cell groups
|
||||
dict<DspConfig, std::vector<RTLIL::Cell *>> groups;
|
||||
for (auto cell : module->selected_cells()) {
|
||||
// Check if this is a DSP cell we are looking for (type starts with m_SisdDspType)
|
||||
if (cell->type != m_SisdDspType)
|
||||
continue;
|
||||
|
||||
// Skip if it has the (* keep *) attribute set
|
||||
if (cell->has_keep_attr())
|
||||
continue;
|
||||
|
||||
// Add to a group
|
||||
const auto key = getDspConfig(cell);
|
||||
groups[key].push_back(cell);
|
||||
}
|
||||
|
||||
std::vector<Cell *> cellsToRemove;
|
||||
|
||||
// Map cell pairs to the target DSP SIMD cell
|
||||
for (const auto &it : groups) {
|
||||
const auto &group = it.second;
|
||||
const auto &config = it.first;
|
||||
|
||||
// Ensure an even number
|
||||
size_t count = group.size();
|
||||
if (count & 1)
|
||||
count--;
|
||||
|
||||
// Map SIMD pairs
|
||||
for (size_t i = 0; i < count; i += 2) {
|
||||
Cell *dsp_a = group[i];
|
||||
Cell *dsp_b = group[i + 1];
|
||||
|
||||
// Create the new cell
|
||||
Cell *simd = module->addCell(NEW_ID, m_SimdDspType);
|
||||
|
||||
log(" SIMD: %s (%s) + %s (%s) => %s (%s)\n", log_id(dsp_a), log_id(dsp_a->type),
|
||||
log_id(dsp_b), log_id(dsp_b->type), log_id(simd), log_id(simd->type));
|
||||
|
||||
// Check if the target cell is known (important to know
|
||||
// its port widths)
|
||||
if (!simd->known())
|
||||
log_error(" The target cell type '%s' is not known!", log_id(simd));
|
||||
|
||||
// Connect common ports
|
||||
for (const auto &it : m_DspCfgPorts)
|
||||
simd->setPort(it.first, config.connections.at(it.second));
|
||||
|
||||
// Connect data ports
|
||||
for (const auto &it : m_DspDataPorts) {
|
||||
size_t width;
|
||||
bool isOutput;
|
||||
|
||||
std::tie(width, isOutput) = getPortInfo(simd, it.second);
|
||||
|
||||
auto getConnection = [&](const RTLIL::Cell *cell) {
|
||||
RTLIL::SigSpec sigspec;
|
||||
if (cell->hasPort(it.first)) {
|
||||
const auto &sig = cell->getPort(it.first);
|
||||
sigspec.append(sig);
|
||||
}
|
||||
|
||||
int padding = width / 2 - sigspec.bits().size();
|
||||
|
||||
if (padding) {
|
||||
if (!isOutput)
|
||||
sigspec.append(RTLIL::SigSpec(RTLIL::Sx, padding));
|
||||
else
|
||||
sigspec.append(module->addWire(NEW_ID, padding));
|
||||
}
|
||||
return sigspec;
|
||||
};
|
||||
|
||||
RTLIL::SigSpec sigspec;
|
||||
sigspec.append(getConnection(dsp_a));
|
||||
sigspec.append(getConnection(dsp_b));
|
||||
simd->setPort(it.second, sigspec);
|
||||
}
|
||||
|
||||
// Concatenate FIR coefficient parameters into the single
|
||||
// MODE_BITS parameter
|
||||
Const mode_bits;
|
||||
for (const auto &it : m_DspParams) {
|
||||
auto val_a = dsp_a->getParam(it);
|
||||
auto val_b = dsp_b->getParam(it);
|
||||
|
||||
mode_bits.bits.insert(mode_bits.end(), val_a.begin(), val_a.end());
|
||||
mode_bits.bits.insert(mode_bits.end(), val_b.begin(), val_b.end());
|
||||
}
|
||||
|
||||
// Enable the fractured mode by connecting the control
|
||||
// port.
|
||||
simd->setPort(ID(f_mode), State::S1);
|
||||
simd->setParam(ID(MODE_BITS), mode_bits);
|
||||
log_assert(mode_bits.size() == m_ModeBitsSize);
|
||||
|
||||
// Handle the "is_inferred" attribute. If one of the fragments
|
||||
// is not inferred mark the whole DSP as not inferred
|
||||
bool is_inferred_a = dsp_a->get_bool_attribute(ID(is_inferred));
|
||||
bool is_inferred_b = dsp_b->get_bool_attribute(ID(is_inferred));
|
||||
simd->set_bool_attribute(ID(is_inferred), is_inferred_a && is_inferred_b);
|
||||
|
||||
// Mark DSP parts for removal
|
||||
cellsToRemove.push_back(dsp_a);
|
||||
cellsToRemove.push_back(dsp_b);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove old cells
|
||||
for (auto cell : cellsToRemove)
|
||||
module->remove(cell);
|
||||
}
|
||||
}
|
||||
|
||||
// ..........................................
|
||||
|
||||
/// Looks up port width and direction in the cell definition and returns it.
|
||||
/// Returns (0, false) if it cannot be determined.
|
||||
std::pair<size_t, bool> getPortInfo(RTLIL::Cell *a_Cell, RTLIL::IdString a_Port)
|
||||
{
|
||||
if (!a_Cell->known()) {
|
||||
return std::make_pair(0, false);
|
||||
}
|
||||
|
||||
// Get the module defining the cell (the previous condition ensures
|
||||
// that the pointers are valid)
|
||||
RTLIL::Module *mod = a_Cell->module->design->module(a_Cell->type);
|
||||
if (mod == nullptr) {
|
||||
return std::make_pair(0, false);
|
||||
}
|
||||
|
||||
// Get the wire representing the port
|
||||
RTLIL::Wire *wire = mod->wire(a_Port);
|
||||
if (wire == nullptr) {
|
||||
return std::make_pair(0, false);
|
||||
}
|
||||
|
||||
return std::make_pair(wire->width, wire->port_output);
|
||||
}
|
||||
|
||||
/// Given a DSP cell populates and returns a DspConfig struct for it.
|
||||
DspConfig getDspConfig(RTLIL::Cell *a_Cell)
|
||||
{
|
||||
DspConfig config;
|
||||
|
||||
for (const auto &it : m_DspCfgPorts) {
|
||||
auto port = it.first;
|
||||
|
||||
// Port unconnected
|
||||
if (!a_Cell->hasPort(port))
|
||||
continue;
|
||||
|
||||
config.connections[port] = sigmap(a_Cell->getPort(port));
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
} QlDspSimdPass;
|
||||
|
||||
PRIVATE_NAMESPACE_END
|
|
@ -0,0 +1 @@
|
|||
/bram_types_sim.v
|
|
@ -0,0 +1,344 @@
|
|||
// Copyright 2020-2022 F4PGA Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
`default_nettype wire
|
||||
module TDP18K_FIFO (
|
||||
RMODE_A_i,
|
||||
RMODE_B_i,
|
||||
WMODE_A_i,
|
||||
WMODE_B_i,
|
||||
WEN_A_i,
|
||||
WEN_B_i,
|
||||
REN_A_i,
|
||||
REN_B_i,
|
||||
CLK_A_i,
|
||||
CLK_B_i,
|
||||
BE_A_i,
|
||||
BE_B_i,
|
||||
ADDR_A_i,
|
||||
ADDR_B_i,
|
||||
WDATA_A_i,
|
||||
WDATA_B_i,
|
||||
RDATA_A_o,
|
||||
RDATA_B_o,
|
||||
EMPTY_o,
|
||||
EPO_o,
|
||||
EWM_o,
|
||||
UNDERRUN_o,
|
||||
FULL_o,
|
||||
FMO_o,
|
||||
FWM_o,
|
||||
OVERRUN_o,
|
||||
FLUSH_ni,
|
||||
FMODE_i,
|
||||
);
|
||||
parameter SYNC_FIFO_i = 1'b0;
|
||||
parameter POWERDN_i = 1'b0;
|
||||
parameter SLEEP_i = 1'b0;
|
||||
parameter PROTECT_i = 1'b0;
|
||||
parameter UPAF_i = 11'b0;
|
||||
parameter UPAE_i = 11'b0;
|
||||
parameter [18*1024-1:0] INIT_i = 18431'bx;
|
||||
|
||||
input wire [2:0] RMODE_A_i;
|
||||
input wire [2:0] RMODE_B_i;
|
||||
input wire [2:0] WMODE_A_i;
|
||||
input wire [2:0] WMODE_B_i;
|
||||
input wire WEN_A_i;
|
||||
input wire WEN_B_i;
|
||||
input wire REN_A_i;
|
||||
input wire REN_B_i;
|
||||
(* clkbuf_sink *)
|
||||
input wire CLK_A_i;
|
||||
(* clkbuf_sink *)
|
||||
input wire CLK_B_i;
|
||||
input wire [1:0] BE_A_i;
|
||||
input wire [1:0] BE_B_i;
|
||||
input wire [13:0] ADDR_A_i;
|
||||
input wire [13:0] ADDR_B_i;
|
||||
input wire [17:0] WDATA_A_i;
|
||||
input wire [17:0] WDATA_B_i;
|
||||
output reg [17:0] RDATA_A_o;
|
||||
output reg [17:0] RDATA_B_o;
|
||||
output wire EMPTY_o;
|
||||
output wire EPO_o;
|
||||
output wire EWM_o;
|
||||
output wire UNDERRUN_o;
|
||||
output wire FULL_o;
|
||||
output wire FMO_o;
|
||||
output wire FWM_o;
|
||||
output wire OVERRUN_o;
|
||||
input wire FLUSH_ni;
|
||||
input wire FMODE_i;
|
||||
reg [17:0] wmsk_a;
|
||||
reg [17:0] wmsk_b;
|
||||
wire [8:0] addr_a;
|
||||
wire [8:0] addr_b;
|
||||
reg [4:0] addr_a_d;
|
||||
reg [4:0] addr_b_d;
|
||||
wire [17:0] ram_rdata_a;
|
||||
wire [17:0] ram_rdata_b;
|
||||
reg [17:0] aligned_wdata_a;
|
||||
reg [17:0] aligned_wdata_b;
|
||||
wire ren_o;
|
||||
wire [10:0] ff_raddr;
|
||||
wire [10:0] ff_waddr;
|
||||
wire [13:0] ram_addr_a;
|
||||
wire [13:0] ram_addr_b;
|
||||
wire [3:0] ram_waddr_a;
|
||||
wire [3:0] ram_waddr_b;
|
||||
wire initn;
|
||||
wire smux_rclk;
|
||||
wire smux_wclk;
|
||||
wire real_fmode;
|
||||
wire [3:0] raw_fflags;
|
||||
reg [1:0] fifo_rmode;
|
||||
reg [1:0] fifo_wmode;
|
||||
wire smux_clk_a;
|
||||
wire smux_clk_b;
|
||||
wire ram_ren_a;
|
||||
wire ram_ren_b;
|
||||
wire ram_wen_a;
|
||||
wire ram_wen_b;
|
||||
wire cen_a;
|
||||
wire cen_b;
|
||||
wire cen_a_n;
|
||||
wire cen_b_n;
|
||||
wire ram_wen_a_n;
|
||||
wire ram_wen_b_n;
|
||||
localparam MODE_9 = 3'b001;
|
||||
always @(*) begin
|
||||
fifo_rmode = (RMODE_B_i == MODE_9 ? 2'b10 : 2'b01);
|
||||
fifo_wmode = (WMODE_A_i == MODE_9 ? 2'b10 : 2'b01);
|
||||
end
|
||||
assign smux_clk_a = CLK_A_i;
|
||||
assign smux_clk_b = CLK_B_i;
|
||||
assign real_fmode = FMODE_i;
|
||||
assign ram_ren_b = real_fmode ? ren_o : REN_B_i;
|
||||
assign ram_wen_a = FMODE_i ? ~FULL_o & WEN_A_i : WEN_A_i;
|
||||
assign ram_ren_a = FMODE_i ? 0 : REN_A_i;
|
||||
assign ram_wen_b = FMODE_i ? 1'b0 : WEN_B_i;
|
||||
assign cen_b = ram_ren_b | ram_wen_b;
|
||||
assign cen_a = ram_ren_a | ram_wen_a;
|
||||
assign ram_waddr_b = real_fmode ? {ff_raddr[0], 3'b000} : ADDR_B_i[3:0];
|
||||
assign ram_waddr_a = real_fmode ? {ff_waddr[0], 3'b000} : ADDR_A_i[3:0];
|
||||
assign ram_addr_b = real_fmode ? {ff_raddr[10:0], 3'h0} : {ADDR_B_i[13:4], addr_b_d[3:0]};
|
||||
assign ram_addr_a = real_fmode ? {ff_waddr[10:0], 3'h0} : {ADDR_A_i[13:4], addr_a_d[3:0]};
|
||||
always @(posedge CLK_A_i) addr_a_d[3:0] <= ADDR_A_i[3:0];
|
||||
always @(posedge CLK_B_i) addr_b_d[3:0] <= ADDR_B_i[3:0];
|
||||
assign cen_a_n = ~cen_a;
|
||||
assign ram_wen_a_n = ~ram_wen_a;
|
||||
assign cen_b_n = ~cen_b;
|
||||
assign ram_wen_b_n = ~ram_wen_b;
|
||||
|
||||
sram1024x18 #(
|
||||
.init(INIT_i)
|
||||
) uram(
|
||||
.clk_a(smux_clk_a),
|
||||
.cen_a(cen_a_n),
|
||||
.wen_a(ram_wen_a_n),
|
||||
.addr_a(ram_addr_a[13:4]),
|
||||
.wmsk_a(wmsk_a),
|
||||
.wdata_a(aligned_wdata_a),
|
||||
.rdata_a(ram_rdata_a),
|
||||
.clk_b(smux_clk_b),
|
||||
.cen_b(cen_b_n),
|
||||
.wen_b(ram_wen_b_n),
|
||||
.addr_b(ram_addr_b[13:4]),
|
||||
.wmsk_b(wmsk_b),
|
||||
.wdata_b(aligned_wdata_b),
|
||||
.rdata_b(ram_rdata_b)
|
||||
);
|
||||
fifo_ctl #(
|
||||
.ADDR_WIDTH(11),
|
||||
.FIFO_WIDTH(2),
|
||||
.DEPTH(6)
|
||||
) fifo_ctl(
|
||||
.rclk(smux_clk_b),
|
||||
.rst_R_n(FLUSH_ni),
|
||||
.wclk(smux_clk_a),
|
||||
.rst_W_n(FLUSH_ni),
|
||||
.ren(REN_B_i),
|
||||
.wen(ram_wen_a),
|
||||
.sync(SYNC_FIFO_i),
|
||||
.rmode(fifo_rmode),
|
||||
.wmode(fifo_wmode),
|
||||
.ren_o(ren_o),
|
||||
.fflags({FULL_o, FMO_o, FWM_o, OVERRUN_o, EMPTY_o, EPO_o, EWM_o, UNDERRUN_o}),
|
||||
.raddr(ff_raddr),
|
||||
.waddr(ff_waddr),
|
||||
.upaf(UPAF_i),
|
||||
.upae(UPAE_i)
|
||||
);
|
||||
localparam MODE_1 = 3'b101;
|
||||
localparam MODE_18 = 3'b010;
|
||||
localparam MODE_2 = 3'b110;
|
||||
localparam MODE_4 = 3'b100;
|
||||
always @(*) begin : WDATA_MODE_SEL
|
||||
if (ram_wen_a == 1) begin
|
||||
case (WMODE_A_i)
|
||||
MODE_18: begin
|
||||
aligned_wdata_a = WDATA_A_i;
|
||||
{wmsk_a[17], wmsk_a[15:8]} = (FMODE_i ? 9'h000 : (BE_A_i[1] ? 9'h000 : 9'h1ff));
|
||||
{wmsk_a[16], wmsk_a[7:0]} = (FMODE_i ? 9'h000 : (BE_A_i[0] ? 9'h000 : 9'h1ff));
|
||||
end
|
||||
MODE_9: begin
|
||||
aligned_wdata_a = {{2 {WDATA_A_i[16]}}, {2 {WDATA_A_i[7:0]}}};
|
||||
{wmsk_a[17], wmsk_a[15:8]} = (ram_waddr_a[3] ? 9'h000 : 9'h1ff);
|
||||
{wmsk_a[16], wmsk_a[7:0]} = (ram_waddr_a[3] ? 9'h1ff : 9'h000);
|
||||
end
|
||||
MODE_4: begin
|
||||
aligned_wdata_a = {2'b00, {4 {WDATA_A_i[3:0]}}};
|
||||
wmsk_a[17:16] = 2'b00;
|
||||
wmsk_a[15:12] = (ram_waddr_a[3:2] == 2'b11 ? 4'h0 : 4'hf);
|
||||
wmsk_a[11:8] = (ram_waddr_a[3:2] == 2'b10 ? 4'h0 : 4'hf);
|
||||
wmsk_a[7:4] = (ram_waddr_a[3:2] == 2'b01 ? 4'h0 : 4'hf);
|
||||
wmsk_a[3:0] = (ram_waddr_a[3:2] == 2'b00 ? 4'h0 : 4'hf);
|
||||
end
|
||||
MODE_2: begin
|
||||
aligned_wdata_a = {2'b00, {8 {WDATA_A_i[1:0]}}};
|
||||
wmsk_a[17:16] = 2'b00;
|
||||
wmsk_a[15:14] = (ram_waddr_a[3:1] == 3'b111 ? 2'h0 : 2'h3);
|
||||
wmsk_a[13:12] = (ram_waddr_a[3:1] == 3'b110 ? 2'h0 : 2'h3);
|
||||
wmsk_a[11:10] = (ram_waddr_a[3:1] == 3'b101 ? 2'h0 : 2'h3);
|
||||
wmsk_a[9:8] = (ram_waddr_a[3:1] == 3'b100 ? 2'h0 : 2'h3);
|
||||
wmsk_a[7:6] = (ram_waddr_a[3:1] == 3'b011 ? 2'h0 : 2'h3);
|
||||
wmsk_a[5:4] = (ram_waddr_a[3:1] == 3'b010 ? 2'h0 : 2'h3);
|
||||
wmsk_a[3:2] = (ram_waddr_a[3:1] == 3'b001 ? 2'h0 : 2'h3);
|
||||
wmsk_a[1:0] = (ram_waddr_a[3:1] == 3'b000 ? 2'h0 : 2'h3);
|
||||
end
|
||||
MODE_1: begin
|
||||
aligned_wdata_a = {2'b00, {16 {WDATA_A_i[0]}}};
|
||||
wmsk_a = 18'h0ffff;
|
||||
wmsk_a[{1'b0, ram_waddr_a[3:0]}] = 0;
|
||||
end
|
||||
default: wmsk_a = 18'h3ffff;
|
||||
endcase
|
||||
end
|
||||
else begin
|
||||
aligned_wdata_a = 18'h00000;
|
||||
wmsk_a = 18'h3ffff;
|
||||
end
|
||||
if (ram_wen_b == 1)
|
||||
case (WMODE_B_i)
|
||||
MODE_18: begin
|
||||
aligned_wdata_b = WDATA_B_i;
|
||||
{wmsk_b[17], wmsk_b[15:8]} = (BE_B_i[1] ? 9'h000 : 9'h1ff);
|
||||
{wmsk_b[16], wmsk_b[7:0]} = (BE_B_i[0] ? 9'h000 : 9'h1ff);
|
||||
end
|
||||
MODE_9: begin
|
||||
aligned_wdata_b = {{2 {WDATA_B_i[16]}}, {2 {WDATA_B_i[7:0]}}};
|
||||
{wmsk_b[17], wmsk_b[15:8]} = (ram_waddr_b[3] ? 9'h000 : 9'h1ff);
|
||||
{wmsk_b[16], wmsk_b[7:0]} = (ram_waddr_b[3] ? 9'h1ff : 9'h000);
|
||||
end
|
||||
MODE_4: begin
|
||||
aligned_wdata_b = {2'b00, {4 {WDATA_B_i[3:0]}}};
|
||||
wmsk_b[17:16] = 2'b00;
|
||||
wmsk_b[15:12] = (ram_waddr_b[3:2] == 2'b11 ? 4'h0 : 4'hf);
|
||||
wmsk_b[11:8] = (ram_waddr_b[3:2] == 2'b10 ? 4'h0 : 4'hf);
|
||||
wmsk_b[7:4] = (ram_waddr_b[3:2] == 2'b01 ? 4'h0 : 4'hf);
|
||||
wmsk_b[3:0] = (ram_waddr_b[3:2] == 2'b00 ? 4'h0 : 4'hf);
|
||||
end
|
||||
MODE_2: begin
|
||||
aligned_wdata_b = {2'b00, {8 {WDATA_B_i[1:0]}}};
|
||||
wmsk_b[17:16] = 2'b00;
|
||||
wmsk_b[15:14] = (ram_waddr_b[3:1] == 3'b111 ? 2'h0 : 2'h3);
|
||||
wmsk_b[13:12] = (ram_waddr_b[3:1] == 3'b110 ? 2'h0 : 2'h3);
|
||||
wmsk_b[11:10] = (ram_waddr_b[3:1] == 3'b101 ? 2'h0 : 2'h3);
|
||||
wmsk_b[9:8] = (ram_waddr_b[3:1] == 3'b100 ? 2'h0 : 2'h3);
|
||||
wmsk_b[7:6] = (ram_waddr_b[3:1] == 3'b011 ? 2'h0 : 2'h3);
|
||||
wmsk_b[5:4] = (ram_waddr_b[3:1] == 3'b010 ? 2'h0 : 2'h3);
|
||||
wmsk_b[3:2] = (ram_waddr_b[3:1] == 3'b001 ? 2'h0 : 2'h3);
|
||||
wmsk_b[1:0] = (ram_waddr_b[3:1] == 3'b000 ? 2'h0 : 2'h3);
|
||||
end
|
||||
MODE_1: begin
|
||||
aligned_wdata_b = {2'b00, {16 {WDATA_B_i[0]}}};
|
||||
wmsk_b = 18'h0ffff;
|
||||
wmsk_b[{1'b0, ram_waddr_b[3:0]}] = 0;
|
||||
end
|
||||
default: wmsk_b = 18'h3ffff;
|
||||
endcase
|
||||
else begin
|
||||
aligned_wdata_b = 18'b000000000000000000;
|
||||
wmsk_b = 18'h3ffff;
|
||||
end
|
||||
end
|
||||
always @(*) begin : RDATA_A_MODE_SEL
|
||||
case (RMODE_A_i)
|
||||
default: RDATA_A_o = 18'h00000;
|
||||
MODE_18: RDATA_A_o = ram_rdata_a;
|
||||
MODE_9: begin
|
||||
{RDATA_A_o[17], RDATA_A_o[15:8]} = 9'h000;
|
||||
{RDATA_A_o[16], RDATA_A_o[7:0]} = (ram_addr_a[3] ? {ram_rdata_a[17], ram_rdata_a[15:8]} : {ram_rdata_a[16], ram_rdata_a[7:0]});
|
||||
end
|
||||
MODE_4: begin
|
||||
RDATA_A_o[17:4] = 14'h0000;
|
||||
case (ram_addr_a[3:2])
|
||||
3: RDATA_A_o[3:0] = ram_rdata_a[15:12];
|
||||
2: RDATA_A_o[3:0] = ram_rdata_a[11:8];
|
||||
1: RDATA_A_o[3:0] = ram_rdata_a[7:4];
|
||||
0: RDATA_A_o[3:0] = ram_rdata_a[3:0];
|
||||
endcase
|
||||
end
|
||||
MODE_2: begin
|
||||
RDATA_A_o[17:2] = 16'h0000;
|
||||
case (ram_addr_a[3:1])
|
||||
7: RDATA_A_o[1:0] = ram_rdata_a[15:14];
|
||||
6: RDATA_A_o[1:0] = ram_rdata_a[13:12];
|
||||
5: RDATA_A_o[1:0] = ram_rdata_a[11:10];
|
||||
4: RDATA_A_o[1:0] = ram_rdata_a[9:8];
|
||||
3: RDATA_A_o[1:0] = ram_rdata_a[7:6];
|
||||
2: RDATA_A_o[1:0] = ram_rdata_a[5:4];
|
||||
1: RDATA_A_o[1:0] = ram_rdata_a[3:2];
|
||||
0: RDATA_A_o[1:0] = ram_rdata_a[1:0];
|
||||
endcase
|
||||
end
|
||||
MODE_1: begin
|
||||
RDATA_A_o[17:1] = 17'h00000;
|
||||
RDATA_A_o[0] = ram_rdata_a[ram_addr_a[3:0]];
|
||||
end
|
||||
endcase
|
||||
end
|
||||
always @(*)
|
||||
case (RMODE_B_i)
|
||||
default: RDATA_B_o = 18'h15566;
|
||||
MODE_18: RDATA_B_o = ram_rdata_b;
|
||||
MODE_9: begin
|
||||
{RDATA_B_o[17], RDATA_B_o[15:8]} = 9'b000000000;
|
||||
{RDATA_B_o[16], RDATA_B_o[7:0]} = (ram_addr_b[3] ? {ram_rdata_b[17], ram_rdata_b[15:8]} : {ram_rdata_b[16], ram_rdata_b[7:0]});
|
||||
end
|
||||
MODE_4:
|
||||
case (ram_addr_b[3:2])
|
||||
3: RDATA_B_o[3:0] = ram_rdata_b[15:12];
|
||||
2: RDATA_B_o[3:0] = ram_rdata_b[11:8];
|
||||
1: RDATA_B_o[3:0] = ram_rdata_b[7:4];
|
||||
0: RDATA_B_o[3:0] = ram_rdata_b[3:0];
|
||||
endcase
|
||||
MODE_2:
|
||||
case (ram_addr_b[3:1])
|
||||
7: RDATA_B_o[1:0] = ram_rdata_b[15:14];
|
||||
6: RDATA_B_o[1:0] = ram_rdata_b[13:12];
|
||||
5: RDATA_B_o[1:0] = ram_rdata_b[11:10];
|
||||
4: RDATA_B_o[1:0] = ram_rdata_b[9:8];
|
||||
3: RDATA_B_o[1:0] = ram_rdata_b[7:6];
|
||||
2: RDATA_B_o[1:0] = ram_rdata_b[5:4];
|
||||
1: RDATA_B_o[1:0] = ram_rdata_b[3:2];
|
||||
0: RDATA_B_o[1:0] = ram_rdata_b[1:0];
|
||||
endcase
|
||||
MODE_1: RDATA_B_o[0] = ram_rdata_b[{1'b0, ram_addr_b[3:0]}];
|
||||
endcase
|
||||
endmodule
|
||||
`default_nettype none
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2020-2022 F4PGA Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
(* techmap_celltype = "$alu" *)
|
||||
module _80_quicklogic_alu (A, B, CI, BI, X, Y, CO);
|
||||
parameter A_SIGNED = 0;
|
||||
parameter B_SIGNED = 0;
|
||||
parameter A_WIDTH = 2;
|
||||
parameter B_WIDTH = 2;
|
||||
parameter Y_WIDTH = 2;
|
||||
parameter _TECHMAP_CONSTVAL_CI_ = 0;
|
||||
parameter _TECHMAP_CONSTMSK_CI_ = 0;
|
||||
|
||||
(* force_downto *)
|
||||
input [A_WIDTH-1:0] A;
|
||||
(* force_downto *)
|
||||
input [B_WIDTH-1:0] B;
|
||||
(* force_downto *)
|
||||
output [Y_WIDTH-1:0] X, Y;
|
||||
|
||||
input CI, BI;
|
||||
(* force_downto *)
|
||||
output [Y_WIDTH-1:0] CO;
|
||||
|
||||
|
||||
wire _TECHMAP_FAIL_ = Y_WIDTH <= 2;
|
||||
|
||||
(* force_downto *)
|
||||
wire [Y_WIDTH-1:0] A_buf, B_buf;
|
||||
\$pos #(.A_SIGNED(A_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(Y_WIDTH)) A_conv (.A(A), .Y(A_buf));
|
||||
\$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) B_conv (.A(B), .Y(B_buf));
|
||||
|
||||
(* force_downto *)
|
||||
wire [Y_WIDTH-1:0] AA = A_buf;
|
||||
(* force_downto *)
|
||||
wire [Y_WIDTH-1:0] BB = BI ? ~B_buf : B_buf;
|
||||
|
||||
genvar i;
|
||||
wire co;
|
||||
|
||||
(* force_downto *)
|
||||
//wire [Y_WIDTH-1:0] C = {CO, CI};
|
||||
wire [Y_WIDTH:0] C;
|
||||
(* force_downto *)
|
||||
wire [Y_WIDTH-1:0] S = {AA ^ BB};
|
||||
assign CO[Y_WIDTH-1:0] = C[Y_WIDTH:1];
|
||||
//assign CO[Y_WIDTH-1] = co;
|
||||
|
||||
generate
|
||||
adder_carry intermediate_adder (
|
||||
.cin ( ),
|
||||
.cout (C[0]),
|
||||
.p (1'b0),
|
||||
.g (CI),
|
||||
.sumout ()
|
||||
);
|
||||
endgenerate
|
||||
genvar i;
|
||||
generate if (Y_WIDTH > 2) begin
|
||||
for (i = 0; i < Y_WIDTH-2; i = i + 1) begin:slice
|
||||
adder_carry my_adder (
|
||||
.cin (C[i]),
|
||||
.g (AA[i]),
|
||||
.p (S[i]),
|
||||
.cout (C[i+1]),
|
||||
.sumout (Y[i])
|
||||
);
|
||||
end
|
||||
end endgenerate
|
||||
generate
|
||||
adder_carry final_adder (
|
||||
.cin (C[Y_WIDTH-2]),
|
||||
.cout (),
|
||||
.p (1'b0),
|
||||
.g (1'b0),
|
||||
.sumout (co)
|
||||
);
|
||||
endgenerate
|
||||
|
||||
assign Y[Y_WIDTH-2] = S[Y_WIDTH-2] ^ co;
|
||||
assign C[Y_WIDTH-1] = S[Y_WIDTH-2] ? co : AA[Y_WIDTH-2];
|
||||
assign Y[Y_WIDTH-1] = S[Y_WIDTH-1] ^ C[Y_WIDTH-1];
|
||||
assign C[Y_WIDTH] = S[Y_WIDTH-1] ? C[Y_WIDTH-1] : AA[Y_WIDTH-1];
|
||||
|
||||
assign X = S;
|
||||
endmodule
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,375 @@
|
|||
// Copyright 2020-2022 F4PGA Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
`timescale 1ps/1ps
|
||||
|
||||
`default_nettype none
|
||||
(* abc9_lut=1 *)
|
||||
module LUT1(output wire O, input wire I0);
|
||||
parameter [1:0] INIT = 0;
|
||||
assign O = I0 ? INIT[1] : INIT[0];
|
||||
specify
|
||||
(I0 => O) = 74;
|
||||
endspecify
|
||||
endmodule
|
||||
|
||||
(* abc9_lut=2 *)
|
||||
module LUT2(output wire O, input wire I0, I1);
|
||||
parameter [3:0] INIT = 0;
|
||||
wire [ 1: 0] s1 = I1 ? INIT[ 3: 2] : INIT[ 1: 0];
|
||||
assign O = I0 ? s1[1] : s1[0];
|
||||
specify
|
||||
(I0 => O) = 116;
|
||||
(I1 => O) = 74;
|
||||
endspecify
|
||||
endmodule
|
||||
|
||||
(* abc9_lut=3 *)
|
||||
module LUT3(output wire O, input wire I0, I1, I2);
|
||||
parameter [7:0] INIT = 0;
|
||||
wire [ 3: 0] s2 = I2 ? INIT[ 7: 4] : INIT[ 3: 0];
|
||||
wire [ 1: 0] s1 = I1 ? s2[ 3: 2] : s2[ 1: 0];
|
||||
assign O = I0 ? s1[1] : s1[0];
|
||||
specify
|
||||
(I0 => O) = 162;
|
||||
(I1 => O) = 116;
|
||||
(I2 => O) = 174;
|
||||
endspecify
|
||||
endmodule
|
||||
|
||||
(* abc9_lut=3 *)
|
||||
module LUT4(output wire O, input wire I0, I1, I2, I3);
|
||||
parameter [15:0] INIT = 0;
|
||||
wire [ 7: 0] s3 = I3 ? INIT[15: 8] : INIT[ 7: 0];
|
||||
wire [ 3: 0] s2 = I2 ? s3[ 7: 4] : s3[ 3: 0];
|
||||
wire [ 1: 0] s1 = I1 ? s2[ 3: 2] : s2[ 1: 0];
|
||||
assign O = I0 ? s1[1] : s1[0];
|
||||
specify
|
||||
(I0 => O) = 201;
|
||||
(I1 => O) = 162;
|
||||
(I2 => O) = 116;
|
||||
(I3 => O) = 74;
|
||||
endspecify
|
||||
endmodule
|
||||
|
||||
(* abc9_lut=3 *)
|
||||
module LUT5(output wire O, input wire I0, I1, I2, I3, I4);
|
||||
parameter [31:0] INIT = 0;
|
||||
wire [15: 0] s4 = I4 ? INIT[31:16] : INIT[15: 0];
|
||||
wire [ 7: 0] s3 = I3 ? s4[15: 8] : s4[ 7: 0];
|
||||
wire [ 3: 0] s2 = I2 ? s3[ 7: 4] : s3[ 3: 0];
|
||||
wire [ 1: 0] s1 = I1 ? s2[ 3: 2] : s2[ 1: 0];
|
||||
assign O = I0 ? s1[1] : s1[0];
|
||||
specify
|
||||
(I0 => O) = 228;
|
||||
(I1 => O) = 189;
|
||||
(I2 => O) = 143;
|
||||
(I3 => O) = 100;
|
||||
(I4 => O) = 55;
|
||||
endspecify
|
||||
endmodule
|
||||
|
||||
(* abc9_lut=5 *)
|
||||
module LUT6(output wire O, input wire I0, I1, I2, I3, I4, I5);
|
||||
parameter [63:0] INIT = 0;
|
||||
wire [31: 0] s5 = I5 ? INIT[63:32] : INIT[31: 0];
|
||||
wire [15: 0] s4 = I4 ? s5[31:16] : s5[15: 0];
|
||||
wire [ 7: 0] s3 = I3 ? s4[15: 8] : s4[ 7: 0];
|
||||
wire [ 3: 0] s2 = I2 ? s3[ 7: 4] : s3[ 3: 0];
|
||||
wire [ 1: 0] s1 = I1 ? s2[ 3: 2] : s2[ 1: 0];
|
||||
assign O = I0 ? s1[1] : s1[0];
|
||||
specify
|
||||
(I0 => O) = 251;
|
||||
(I1 => O) = 212;
|
||||
(I2 => O) = 166;
|
||||
(I3 => O) = 123;
|
||||
(I4 => O) = 77;
|
||||
(I5 => O) = 43;
|
||||
endspecify
|
||||
endmodule
|
||||
|
||||
(* abc9_flop, lib_whitebox *)
|
||||
module sh_dff(
|
||||
output reg Q,
|
||||
input wire D,
|
||||
(* clkbuf_sink *)
|
||||
input wire C
|
||||
);
|
||||
|
||||
initial Q = 1'b0;
|
||||
always @(posedge C)
|
||||
Q <= D;
|
||||
|
||||
specify
|
||||
(posedge C => (Q +: D)) = 0;
|
||||
$setuphold(posedge C, D, 0, 0);
|
||||
endspecify
|
||||
|
||||
endmodule
|
||||
|
||||
(* abc9_box, lib_whitebox *)
|
||||
(* keep *)
|
||||
module adder_carry(
|
||||
output wire sumout,
|
||||
(* abc9_carry *)
|
||||
output wire cout,
|
||||
input wire p,
|
||||
input wire g,
|
||||
(* abc9_carry *)
|
||||
input wire cin
|
||||
);
|
||||
assign sumout = p ^ cin;
|
||||
assign cout = p ? cin : g;
|
||||
|
||||
specify
|
||||
(p => sumout) = 35;
|
||||
(g => sumout) = 35;
|
||||
(cin => sumout) = 40;
|
||||
(p => cout) = 67;
|
||||
(g => cout) = 65;
|
||||
(cin => cout) = 69;
|
||||
endspecify
|
||||
|
||||
endmodule
|
||||
|
||||
(* abc9_flop, lib_whitebox *)
|
||||
module dff(
|
||||
output reg Q,
|
||||
input wire D,
|
||||
(* clkbuf_sink *)
|
||||
input wire C
|
||||
);
|
||||
initial Q = 1'b0;
|
||||
|
||||
always @(posedge C)
|
||||
Q <= D;
|
||||
|
||||
specify
|
||||
(posedge C=>(Q+:D)) = 285;
|
||||
$setuphold(posedge C, D, 56, 0);
|
||||
endspecify
|
||||
|
||||
endmodule
|
||||
|
||||
(* abc9_flop, lib_whitebox *)
|
||||
module dffn(
|
||||
output reg Q,
|
||||
input wire D,
|
||||
(* clkbuf_sink *)
|
||||
input wire C
|
||||
);
|
||||
initial Q = 1'b0;
|
||||
|
||||
always @(negedge C)
|
||||
Q <= D;
|
||||
|
||||
specify
|
||||
(negedge C=>(Q+:D)) = 285;
|
||||
$setuphold(negedge C, D, 56, 0);
|
||||
endspecify
|
||||
|
||||
endmodule
|
||||
|
||||
(* abc9_flop, lib_whitebox *)
|
||||
module dffsre(
|
||||
output reg Q,
|
||||
input wire D,
|
||||
(* clkbuf_sink *)
|
||||
input wire C,
|
||||
input wire E,
|
||||
input wire R,
|
||||
input wire S
|
||||
);
|
||||
initial Q = 1'b0;
|
||||
|
||||
always @(posedge C or negedge S or negedge R)
|
||||
if (!R)
|
||||
Q <= 1'b0;
|
||||
else if (!S)
|
||||
Q <= 1'b1;
|
||||
else if (E)
|
||||
Q <= D;
|
||||
|
||||
specify
|
||||
(posedge C => (Q +: D)) = 280;
|
||||
(R => Q) = 0;
|
||||
(S => Q) = 0;
|
||||
$setuphold(posedge C, D, 56, 0);
|
||||
$setuphold(posedge C, E, 32, 0);
|
||||
$setuphold(posedge C, R, 0, 0);
|
||||
$setuphold(posedge C, S, 0, 0);
|
||||
$recrem(posedge R, posedge C, 0, 0);
|
||||
$recrem(posedge S, posedge C, 0, 0);
|
||||
endspecify
|
||||
|
||||
endmodule
|
||||
|
||||
(* abc9_flop, lib_whitebox *)
|
||||
module dffnsre(
|
||||
output reg Q,
|
||||
input wire D,
|
||||
(* clkbuf_sink *)
|
||||
input wire C,
|
||||
input wire E,
|
||||
input wire R,
|
||||
input wire S
|
||||
);
|
||||
initial Q = 1'b0;
|
||||
|
||||
always @(negedge C or negedge S or negedge R)
|
||||
if (!R)
|
||||
Q <= 1'b0;
|
||||
else if (!S)
|
||||
Q <= 1'b1;
|
||||
else if (E)
|
||||
Q <= D;
|
||||
|
||||
specify
|
||||
(negedge C => (Q +: D)) = 280;
|
||||
(R => Q) = 0;
|
||||
(S => Q) = 0;
|
||||
$setuphold(negedge C, D, 56, 0);
|
||||
$setuphold(negedge C, E, 32, 0);
|
||||
$setuphold(negedge C, R, 0, 0);
|
||||
$setuphold(negedge C, S, 0, 0);
|
||||
$recrem(posedge R, negedge C, 0, 0);
|
||||
$recrem(posedge S, negedge C, 0, 0);
|
||||
endspecify
|
||||
|
||||
endmodule
|
||||
|
||||
(* abc9_flop, lib_whitebox *)
|
||||
module sdffsre(
|
||||
output reg Q,
|
||||
input wire D,
|
||||
(* clkbuf_sink *)
|
||||
input wire C,
|
||||
input wire E,
|
||||
input wire R,
|
||||
input wire S
|
||||
);
|
||||
initial Q = 1'b0;
|
||||
|
||||
always @(posedge C)
|
||||
if (!R)
|
||||
Q <= 1'b0;
|
||||
else if (!S)
|
||||
Q <= 1'b1;
|
||||
else if (E)
|
||||
Q <= D;
|
||||
|
||||
specify
|
||||
(posedge C => (Q +: D)) = 280;
|
||||
$setuphold(posedge C, D, 56, 0);
|
||||
$setuphold(posedge C, R, 32, 0);
|
||||
$setuphold(posedge C, S, 0, 0);
|
||||
$setuphold(posedge C, E, 0, 0);
|
||||
endspecify
|
||||
|
||||
endmodule
|
||||
|
||||
(* abc9_flop, lib_whitebox *)
|
||||
module sdffnsre(
|
||||
output reg Q,
|
||||
input wire D,
|
||||
(* clkbuf_sink *)
|
||||
input wire C,
|
||||
input wire E,
|
||||
input wire R,
|
||||
input wire S
|
||||
);
|
||||
initial Q = 1'b0;
|
||||
|
||||
always @(negedge C)
|
||||
if (!R)
|
||||
Q <= 1'b0;
|
||||
else if (!S)
|
||||
Q <= 1'b1;
|
||||
else if (E)
|
||||
Q <= D;
|
||||
|
||||
specify
|
||||
(negedge C => (Q +: D)) = 280;
|
||||
$setuphold(negedge C, D, 56, 0);
|
||||
$setuphold(negedge C, R, 32, 0);
|
||||
$setuphold(negedge C, S, 0, 0);
|
||||
$setuphold(negedge C, E, 0, 0);
|
||||
endspecify
|
||||
|
||||
endmodule
|
||||
|
||||
(* abc9_flop, lib_whitebox *)
|
||||
module latchsre (
|
||||
output reg Q,
|
||||
input wire S,
|
||||
input wire R,
|
||||
input wire D,
|
||||
input wire G,
|
||||
input wire E
|
||||
);
|
||||
initial Q = 1'b0;
|
||||
|
||||
always @*
|
||||
begin
|
||||
if (!R)
|
||||
Q <= 1'b0;
|
||||
else if (!S)
|
||||
Q <= 1'b1;
|
||||
else if (E && G)
|
||||
Q <= D;
|
||||
end
|
||||
|
||||
specify
|
||||
(posedge G => (Q +: D)) = 0;
|
||||
$setuphold(posedge G, D, 0, 0);
|
||||
$setuphold(posedge G, E, 0, 0);
|
||||
$setuphold(posedge G, R, 0, 0);
|
||||
$setuphold(posedge G, S, 0, 0);
|
||||
endspecify
|
||||
|
||||
endmodule
|
||||
|
||||
(* abc9_flop, lib_whitebox *)
|
||||
module latchnsre (
|
||||
output reg Q,
|
||||
input wire S,
|
||||
input wire R,
|
||||
input wire D,
|
||||
input wire G,
|
||||
input wire E
|
||||
);
|
||||
initial Q = 1'b0;
|
||||
|
||||
always @*
|
||||
begin
|
||||
if (!R)
|
||||
Q <= 1'b0;
|
||||
else if (!S)
|
||||
Q <= 1'b1;
|
||||
else if (E && !G)
|
||||
Q <= D;
|
||||
end
|
||||
|
||||
specify
|
||||
(negedge G => (Q +: D)) = 0;
|
||||
$setuphold(negedge G, D, 0, 0);
|
||||
$setuphold(negedge G, E, 0, 0);
|
||||
$setuphold(negedge G, R, 0, 0);
|
||||
$setuphold(negedge G, S, 0, 0);
|
||||
endspecify
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
// Copyright 2020-2022 F4PGA Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
module dsp_t1_20x18x64_cfg_ports (
|
||||
input [19:0] a_i,
|
||||
input [17:0] b_i,
|
||||
input [ 5:0] acc_fir_i,
|
||||
output [37:0] z_o,
|
||||
output [17:0] dly_b_o,
|
||||
|
||||
input clock_i,
|
||||
input reset_i,
|
||||
|
||||
input [2:0] feedback_i,
|
||||
input load_acc_i,
|
||||
input unsigned_a_i,
|
||||
input unsigned_b_i,
|
||||
|
||||
input [2:0] output_select_i,
|
||||
input saturate_enable_i,
|
||||
input [5:0] shift_right_i,
|
||||
input round_i,
|
||||
input subtract_i,
|
||||
input register_inputs_i
|
||||
);
|
||||
|
||||
parameter [19:0] COEFF_0 = 20'd0;
|
||||
parameter [19:0] COEFF_1 = 20'd0;
|
||||
parameter [19:0] COEFF_2 = 20'd0;
|
||||
parameter [19:0] COEFF_3 = 20'd0;
|
||||
|
||||
QL_DSP2 # (
|
||||
.MODE_BITS ({COEFF_3, COEFF_2, COEFF_1, COEFF_0})
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.a (a_i),
|
||||
.b (b_i),
|
||||
.acc_fir (acc_fir_i),
|
||||
.z (z_o),
|
||||
.dly_b (dly_b_o),
|
||||
|
||||
.clk (clock_i),
|
||||
.reset (reset_i),
|
||||
|
||||
.feedback (feedback_i),
|
||||
.load_acc (load_acc_i),
|
||||
.unsigned_a (unsigned_a_i),
|
||||
.unsigned_b (unsigned_b_i),
|
||||
|
||||
.f_mode (1'b0), // No fracturation
|
||||
.output_select (output_select_i),
|
||||
.saturate_enable (saturate_enable_i),
|
||||
.shift_right (shift_right_i),
|
||||
.round (round_i),
|
||||
.subtract (subtract_i),
|
||||
.register_inputs (register_inputs_i)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
module dsp_t1_10x9x32_cfg_ports (
|
||||
input [ 9:0] a_i,
|
||||
input [ 8:0] b_i,
|
||||
input [ 5:0] acc_fir_i,
|
||||
output [18:0] z_o,
|
||||
output [ 8:0] dly_b_o,
|
||||
|
||||
(* clkbuf_sink *)
|
||||
input clock_i,
|
||||
input reset_i,
|
||||
|
||||
input [2:0] feedback_i,
|
||||
input load_acc_i,
|
||||
input unsigned_a_i,
|
||||
input unsigned_b_i,
|
||||
|
||||
input [2:0] output_select_i,
|
||||
input saturate_enable_i,
|
||||
input [5:0] shift_right_i,
|
||||
input round_i,
|
||||
input subtract_i,
|
||||
input register_inputs_i
|
||||
);
|
||||
|
||||
parameter [9:0] COEFF_0 = 10'd0;
|
||||
parameter [9:0] COEFF_1 = 10'd0;
|
||||
parameter [9:0] COEFF_2 = 10'd0;
|
||||
parameter [9:0] COEFF_3 = 10'd0;
|
||||
|
||||
wire [37:0] z;
|
||||
wire [17:0] dly_b;
|
||||
|
||||
QL_DSP2 # (
|
||||
.MODE_BITS ({10'd0, COEFF_3,
|
||||
10'd0, COEFF_2,
|
||||
10'd0, COEFF_1,
|
||||
10'd0, COEFF_0})
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.a ({10'd0, a_i}),
|
||||
.b ({ 9'd0, b_i}),
|
||||
.acc_fir (acc_fir_i),
|
||||
.z (z),
|
||||
.dly_b (dly_b),
|
||||
|
||||
.clk (clock_i),
|
||||
.reset (reset_i),
|
||||
|
||||
.feedback (feedback_i),
|
||||
.load_acc (load_acc_i),
|
||||
.unsigned_a (unsigned_a_i),
|
||||
.unsigned_b (unsigned_b_i),
|
||||
|
||||
.f_mode (1'b1), // Enable fractuation, Use the lower half
|
||||
.output_select (output_select_i),
|
||||
.saturate_enable (saturate_enable_i),
|
||||
.shift_right (shift_right_i),
|
||||
.round (round_i),
|
||||
.subtract (subtract_i),
|
||||
.register_inputs (register_inputs_i)
|
||||
);
|
||||
|
||||
assign z_o = z[18:0];
|
||||
assign dly_b_o = dly_b_o[8:0];
|
||||
|
||||
endmodule
|
||||
|
||||
module dsp_t1_20x18x64_cfg_params (
|
||||
input [19:0] a_i,
|
||||
input [17:0] b_i,
|
||||
input [ 5:0] acc_fir_i,
|
||||
output [37:0] z_o,
|
||||
output [17:0] dly_b_o,
|
||||
|
||||
input clock_i,
|
||||
input reset_i,
|
||||
|
||||
input [2:0] feedback_i,
|
||||
input load_acc_i,
|
||||
input unsigned_a_i,
|
||||
input unsigned_b_i,
|
||||
input subtract_i
|
||||
);
|
||||
|
||||
parameter [19:0] COEFF_0 = 20'd0;
|
||||
parameter [19:0] COEFF_1 = 20'd0;
|
||||
parameter [19:0] COEFF_2 = 20'd0;
|
||||
parameter [19:0] COEFF_3 = 20'd0;
|
||||
|
||||
parameter [2:0] OUTPUT_SELECT = 3'd0;
|
||||
parameter [0:0] SATURATE_ENABLE = 1'd0;
|
||||
parameter [5:0] SHIFT_RIGHT = 6'd0;
|
||||
parameter [0:0] ROUND = 1'd0;
|
||||
parameter [0:0] REGISTER_INPUTS = 1'd0;
|
||||
|
||||
QL_DSP3 # (
|
||||
.MODE_BITS ({
|
||||
REGISTER_INPUTS,
|
||||
ROUND,
|
||||
SHIFT_RIGHT,
|
||||
SATURATE_ENABLE,
|
||||
OUTPUT_SELECT,
|
||||
1'b0, // Not fractured
|
||||
COEFF_3,
|
||||
COEFF_2,
|
||||
COEFF_1,
|
||||
COEFF_0
|
||||
})
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.a (a_i),
|
||||
.b (b_i),
|
||||
.acc_fir (acc_fir_i),
|
||||
.z (z_o),
|
||||
.dly_b (dly_b_o),
|
||||
|
||||
.clk (clock_i),
|
||||
.reset (reset_i),
|
||||
|
||||
.feedback (feedback_i),
|
||||
.load_acc (load_acc_i),
|
||||
.unsigned_a (unsigned_a_i),
|
||||
.unsigned_b (unsigned_b_i),
|
||||
.subtract (subtract_i)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
module dsp_t1_10x9x32_cfg_params (
|
||||
input [ 9:0] a_i,
|
||||
input [ 8:0] b_i,
|
||||
input [ 5:0] acc_fir_i,
|
||||
output [18:0] z_o,
|
||||
output [ 8:0] dly_b_o,
|
||||
|
||||
(* clkbuf_sink *)
|
||||
input clock_i,
|
||||
input reset_i,
|
||||
|
||||
input [2:0] feedback_i,
|
||||
input load_acc_i,
|
||||
input unsigned_a_i,
|
||||
input unsigned_b_i,
|
||||
input subtract_i
|
||||
);
|
||||
|
||||
parameter [9:0] COEFF_0 = 10'd0;
|
||||
parameter [9:0] COEFF_1 = 10'd0;
|
||||
parameter [9:0] COEFF_2 = 10'd0;
|
||||
parameter [9:0] COEFF_3 = 10'd0;
|
||||
|
||||
parameter [2:0] OUTPUT_SELECT = 3'd0;
|
||||
parameter [0:0] SATURATE_ENABLE = 1'd0;
|
||||
parameter [5:0] SHIFT_RIGHT = 6'd0;
|
||||
parameter [0:0] ROUND = 1'd0;
|
||||
parameter [0:0] REGISTER_INPUTS = 1'd0;
|
||||
|
||||
wire [37:0] z;
|
||||
wire [17:0] dly_b;
|
||||
|
||||
QL_DSP3 # (
|
||||
.MODE_BITS ({
|
||||
REGISTER_INPUTS,
|
||||
ROUND,
|
||||
SHIFT_RIGHT,
|
||||
SATURATE_ENABLE,
|
||||
OUTPUT_SELECT,
|
||||
1'b1, // Fractured
|
||||
10'd0, COEFF_3,
|
||||
10'd0, COEFF_2,
|
||||
10'd0, COEFF_1,
|
||||
10'd0, COEFF_0
|
||||
})
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.a ({10'd0, a_i}),
|
||||
.b ({ 9'd0, b_i}),
|
||||
.acc_fir (acc_fir_i),
|
||||
.z (z),
|
||||
.dly_b (dly_b),
|
||||
|
||||
.clk (clock_i),
|
||||
.reset (reset_i),
|
||||
|
||||
.feedback (feedback_i),
|
||||
.load_acc (load_acc_i),
|
||||
.unsigned_a (unsigned_a_i),
|
||||
.unsigned_b (unsigned_b_i),
|
||||
.subtract (subtract_i)
|
||||
);
|
||||
|
||||
assign z_o = z[18:0];
|
||||
assign dly_b_o = dly_b_o[8:0];
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,102 @@
|
|||
// Copyright 2020-2022 F4PGA Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
module \$__QL_MUL20X18 (input [19:0] A, input [17:0] B, output [37:0] Y);
|
||||
parameter A_SIGNED = 0;
|
||||
parameter B_SIGNED = 0;
|
||||
parameter A_WIDTH = 0;
|
||||
parameter B_WIDTH = 0;
|
||||
parameter Y_WIDTH = 0;
|
||||
|
||||
wire [19:0] a;
|
||||
wire [17:0] b;
|
||||
wire [37:0] z;
|
||||
|
||||
assign a = (A_WIDTH == 20) ? A :
|
||||
(A_SIGNED) ? {{(20 - A_WIDTH){A[A_WIDTH-1]}}, A} :
|
||||
{{(20 - A_WIDTH){1'b0}}, A};
|
||||
|
||||
assign b = (B_WIDTH == 18) ? B :
|
||||
(B_SIGNED) ? {{(18 - B_WIDTH){B[B_WIDTH-1]}}, B} :
|
||||
{{(18 - B_WIDTH){1'b0}}, B};
|
||||
|
||||
(* is_inferred=1 *)
|
||||
dsp_t1_20x18x64_cfg_ports _TECHMAP_REPLACE_ (
|
||||
.a_i (a),
|
||||
.b_i (b),
|
||||
.acc_fir_i (6'd0),
|
||||
.z_o (z),
|
||||
|
||||
.feedback_i (3'd0),
|
||||
.load_acc_i (1'b0),
|
||||
.unsigned_a_i (!A_SIGNED),
|
||||
.unsigned_b_i (!B_SIGNED),
|
||||
|
||||
.output_select_i (3'd0),
|
||||
.saturate_enable_i (1'b0),
|
||||
.shift_right_i (6'd0),
|
||||
.round_i (1'b0),
|
||||
.subtract_i (1'b0),
|
||||
.register_inputs_i (1'b0)
|
||||
);
|
||||
|
||||
assign Y = z;
|
||||
|
||||
endmodule
|
||||
|
||||
module \$__QL_MUL10X9 (input [9:0] A, input [8:0] B, output [18:0] Y);
|
||||
parameter A_SIGNED = 0;
|
||||
parameter B_SIGNED = 0;
|
||||
parameter A_WIDTH = 0;
|
||||
parameter B_WIDTH = 0;
|
||||
parameter Y_WIDTH = 0;
|
||||
|
||||
wire [ 9:0] a;
|
||||
wire [ 8:0] b;
|
||||
wire [18:0] z;
|
||||
|
||||
assign a = (A_WIDTH == 10) ? A :
|
||||
(A_SIGNED) ? {{(10 - A_WIDTH){A[A_WIDTH-1]}}, A} :
|
||||
{{(10 - A_WIDTH){1'b0}}, A};
|
||||
|
||||
assign b = (B_WIDTH == 9) ? B :
|
||||
(B_SIGNED) ? {{( 9 - B_WIDTH){B[B_WIDTH-1]}}, B} :
|
||||
{{( 9 - B_WIDTH){1'b0}}, B};
|
||||
|
||||
(* is_inferred=1 *)
|
||||
dsp_t1_10x9x32_cfg_ports _TECHMAP_REPLACE_ (
|
||||
.a_i (a),
|
||||
.b_i (b),
|
||||
.acc_fir_i (6'd0),
|
||||
.z_o (z),
|
||||
|
||||
.feedback_i (3'd0),
|
||||
.load_acc_i (1'b0),
|
||||
.unsigned_a_i (!A_SIGNED),
|
||||
.unsigned_b_i (!B_SIGNED),
|
||||
|
||||
.output_select_i (3'd0),
|
||||
.saturate_enable_i (1'b0),
|
||||
.shift_right_i (6'd0),
|
||||
.round_i (1'b0),
|
||||
.subtract_i (1'b0),
|
||||
.register_inputs_i (1'b0)
|
||||
);
|
||||
|
||||
|
||||
assign Y = z;
|
||||
|
||||
endmodule
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,133 @@
|
|||
// Copyright 2020-2022 F4PGA Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// DFF, asynchronous set/reset, enable
|
||||
module \$_DFFSRE_PNNP_ (C, S, R, E, D, Q);
|
||||
input C;
|
||||
input S;
|
||||
input R;
|
||||
input E;
|
||||
input D;
|
||||
output Q;
|
||||
dffsre _TECHMAP_REPLACE_ (.Q(Q), .D(D), .C(C), .E(E), .R(R), .S(S));
|
||||
endmodule
|
||||
|
||||
module \$_DFFSRE_NNNP_ (C, S, R, E, D, Q);
|
||||
input C;
|
||||
input S;
|
||||
input R;
|
||||
input E;
|
||||
input D;
|
||||
output Q;
|
||||
dffnsre _TECHMAP_REPLACE_ (.Q(Q), .D(D), .C(C), .E(E), .R(R), .S(S));
|
||||
endmodule
|
||||
|
||||
// DFF, synchronous set or reset, enable
|
||||
module \$_SDFFE_PN0P_ (D, C, R, E, Q);
|
||||
input D;
|
||||
input C;
|
||||
input R;
|
||||
input E;
|
||||
output Q;
|
||||
sdffsre _TECHMAP_REPLACE_ (.Q(Q), .D(D), .C(C), .E(E), .R(R), .S(1'b1));
|
||||
endmodule
|
||||
|
||||
module \$_SDFFE_PN1P_ (D, C, R, E, Q);
|
||||
input D;
|
||||
input C;
|
||||
input R;
|
||||
input E;
|
||||
output Q;
|
||||
sdffsre _TECHMAP_REPLACE_ (.Q(Q), .D(D), .C(C), .E(E), .R(1'b1), .S(R));
|
||||
endmodule
|
||||
|
||||
module \$_SDFFE_NN0P_ (D, C, R, E, Q);
|
||||
input D;
|
||||
input C;
|
||||
input R;
|
||||
input E;
|
||||
output Q;
|
||||
sdffnsre _TECHMAP_REPLACE_ (.Q(Q), .D(D), .C(C), .E(E), .R(R), .S(1'b1));
|
||||
endmodule
|
||||
|
||||
module \$_SDFFE_NN1P_ (D, C, R, E, Q);
|
||||
input D;
|
||||
input C;
|
||||
input R;
|
||||
input E;
|
||||
output Q;
|
||||
sdffnsre _TECHMAP_REPLACE_ (.Q(Q), .D(D), .C(C), .E(E), .R(1'b1), .S(R));
|
||||
endmodule
|
||||
|
||||
// Latch, no set/reset, no enable
|
||||
module \$_DLATCH_P_ (input E, D, output Q);
|
||||
latchsre _TECHMAP_REPLACE_ (.D(D), .Q(Q), .E(1'b1), .G(E), .R(1'b1), .S(1'b1));
|
||||
endmodule
|
||||
|
||||
module \$_DLATCH_N_ (input E, D, output Q);
|
||||
latchnsre _TECHMAP_REPLACE_ (.D(D), .Q(Q), .E(1'b1), .G(E), .R(1'b1), .S(1'b1));
|
||||
endmodule
|
||||
|
||||
// Latch with async set and reset and enable
|
||||
module \$_DLATCHSR_PPP_ (input E, S, R, D, output Q);
|
||||
latchsre _TECHMAP_REPLACE_ (.D(D), .Q(Q), .E(1'b1), .G(E), .R(!R), .S(!S));
|
||||
endmodule
|
||||
|
||||
module \$_DLATCHSR_NPP_ (input E, S, R, D, output Q);
|
||||
latchnsre _TECHMAP_REPLACE_ (.D(D), .Q(Q), .E(1'b1), .G(E), .R(!R), .S(!S));
|
||||
endmodule
|
||||
|
||||
module \$__SHREG_DFF_P_ (D, Q, C);
|
||||
input D;
|
||||
input C;
|
||||
output Q;
|
||||
|
||||
parameter DEPTH = 2;
|
||||
|
||||
reg [DEPTH-2:0] q;
|
||||
|
||||
genvar i;
|
||||
generate for (i = 0; i < DEPTH; i = i + 1) begin: slice
|
||||
|
||||
// First in chain
|
||||
generate if (i == 0) begin
|
||||
sh_dff #() shreg_beg (
|
||||
.Q(q[i]),
|
||||
.D(D),
|
||||
.C(C)
|
||||
);
|
||||
end endgenerate
|
||||
// Middle in chain
|
||||
generate if (i > 0 && i != DEPTH-1) begin
|
||||
sh_dff #() shreg_mid (
|
||||
.Q(q[i]),
|
||||
.D(q[i-1]),
|
||||
.C(C)
|
||||
);
|
||||
end endgenerate
|
||||
// Last in chain
|
||||
generate if (i == DEPTH-1) begin
|
||||
sh_dff #() shreg_end (
|
||||
.Q(Q),
|
||||
.D(q[i-1]),
|
||||
.C(C)
|
||||
);
|
||||
end endgenerate
|
||||
end: slice
|
||||
endgenerate
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,248 @@
|
|||
import sys
|
||||
from datetime import datetime, timezone
|
||||
|
||||
def generate(filename):
|
||||
with open(filename, "w") as f:
|
||||
f.write("// **AUTOGENERATED FILE** **DO NOT EDIT**\n")
|
||||
f.write(f"// Generated by {sys.argv[0]} at {datetime.now(timezone.utc)}\n")
|
||||
|
||||
f.write("`timescale 1ns /10ps\n")
|
||||
for a_width in [1,2,4,9,18,36]:
|
||||
for b_width in [1,2,4,9,18,36]:
|
||||
f.write(f"""
|
||||
module TDP36K_BRAM_A_X{a_width}_B_X{b_width}_nonsplit (
|
||||
RESET_ni,
|
||||
WEN_A1_i, WEN_B1_i,
|
||||
REN_A1_i, REN_B1_i,
|
||||
CLK_A1_i, CLK_B1_i,
|
||||
BE_A1_i, BE_B1_i,
|
||||
ADDR_A1_i, ADDR_B1_i,
|
||||
WDATA_A1_i, WDATA_B1_i,
|
||||
RDATA_A1_o, RDATA_B1_o,
|
||||
FLUSH1_i,
|
||||
WEN_A2_i, WEN_B2_i,
|
||||
REN_A2_i, REN_B2_i,
|
||||
CLK_A2_i, CLK_B2_i,
|
||||
BE_A2_i, BE_B2_i,
|
||||
ADDR_A2_i, ADDR_B2_i,
|
||||
WDATA_A2_i, WDATA_B2_i,
|
||||
RDATA_A2_o, RDATA_B2_o,
|
||||
FLUSH2_i
|
||||
);
|
||||
|
||||
parameter [80:0] MODE_BITS = 81'd0;
|
||||
parameter [1024*36-1:0] RAM_INIT = 36864'bx;
|
||||
|
||||
input wire RESET_ni;
|
||||
input wire WEN_A1_i, WEN_B1_i;
|
||||
input wire REN_A1_i, REN_B1_i;
|
||||
input wire WEN_A2_i, WEN_B2_i;
|
||||
input wire REN_A2_i, REN_B2_i;
|
||||
|
||||
(* clkbuf_sink *)
|
||||
input wire CLK_A1_i;
|
||||
(* clkbuf_sink *)
|
||||
input wire CLK_B1_i;
|
||||
(* clkbuf_sink *)
|
||||
input wire CLK_A2_i;
|
||||
(* clkbuf_sink *)
|
||||
input wire CLK_B2_i;
|
||||
|
||||
input wire [ 1:0] BE_A1_i, BE_B1_i;
|
||||
input wire [14:0] ADDR_A1_i, ADDR_B1_i;
|
||||
input wire [17:0] WDATA_A1_i, WDATA_B1_i;
|
||||
output wire [17:0] RDATA_A1_o, RDATA_B1_o;
|
||||
|
||||
input wire FLUSH1_i;
|
||||
|
||||
input wire [ 1:0] BE_A2_i, BE_B2_i;
|
||||
input wire [13:0] ADDR_A2_i, ADDR_B2_i;
|
||||
input wire [17:0] WDATA_A2_i, WDATA_B2_i;
|
||||
output wire [17:0] RDATA_A2_o, RDATA_B2_o;
|
||||
|
||||
input wire FLUSH2_i;
|
||||
|
||||
TDP36K #(.MODE_BITS(MODE_BITS), .RAM_INIT(RAM_INIT)) bram (
|
||||
.RESET_ni (RESET_ni),
|
||||
.WEN_A1_i (WEN_A1_i), .WEN_B1_i (WEN_B1_i),
|
||||
.REN_A1_i (REN_A1_i), .REN_B1_i (REN_B1_i),
|
||||
.CLK_A1_i (CLK_A1_i), .CLK_B1_i (CLK_B1_i),
|
||||
.BE_A1_i (BE_A1_i), .BE_B1_i (BE_B1_i),
|
||||
.ADDR_A1_i (ADDR_A1_i), .ADDR_B1_i (ADDR_B1_i),
|
||||
.WDATA_A1_i (WDATA_A1_i), .WDATA_B1_i (WDATA_B1_i),
|
||||
.RDATA_A1_o (RDATA_A1_o), .RDATA_B1_o (RDATA_B1_o),
|
||||
.FLUSH1_i (FLUSH1_i),
|
||||
.WEN_A2_i (WEN_A2_i), .WEN_B2_i (WEN_B2_i),
|
||||
.REN_A2_i (REN_A2_i), .REN_B2_i (REN_B2_i),
|
||||
.CLK_A2_i (CLK_A2_i), .CLK_B2_i (CLK_B2_i),
|
||||
.BE_A2_i (BE_A2_i), .BE_B2_i (BE_B2_i),
|
||||
.ADDR_A2_i (ADDR_A2_i), .ADDR_B2_i (ADDR_B2_i),
|
||||
.WDATA_A2_i (WDATA_A2_i), .WDATA_B2_i (WDATA_B2_i),
|
||||
.RDATA_A2_o (RDATA_A2_o), .RDATA_B2_o (RDATA_B2_o),
|
||||
.FLUSH2_i (FLUSH2_i)
|
||||
);
|
||||
|
||||
`ifdef SDF_SIM
|
||||
specify
|
||||
(negedge RESET_ni => (RDATA_A1_o +: WDATA_A1_i)) = 0;
|
||||
(negedge RESET_ni => (RDATA_B1_o +: WDATA_B1_i)) = 0;
|
||||
(negedge RESET_ni => (RDATA_A2_o +: WDATA_A2_i)) = 0;
|
||||
(negedge RESET_ni => (RDATA_B2_o +: WDATA_B2_i)) = 0;
|
||||
(posedge CLK_A1_i => (RDATA_A1_o +: WDATA_A1_i)) = 0;
|
||||
(posedge CLK_B1_i => (RDATA_B1_o +: WDATA_B1_i)) = 0;
|
||||
(posedge CLK_A2_i => (RDATA_A2_o +: WDATA_A2_i)) = 0;
|
||||
(posedge CLK_B2_i => (RDATA_B2_o +: WDATA_B2_i)) = 0;
|
||||
$setuphold(posedge CLK_A1_i, RESET_ni, 0, 0);
|
||||
$setuphold(posedge CLK_A1_i, FLUSH1_i, 0, 0);
|
||||
$setuphold(posedge CLK_A1_i, WEN_A1_i, 0, 0);
|
||||
$setuphold(posedge CLK_A1_i, REN_A1_i, 0, 0);
|
||||
$setuphold(posedge CLK_A1_i, BE_A1_i, 0, 0);
|
||||
$setuphold(posedge CLK_A1_i, ADDR_A1_i, 0, 0);
|
||||
$setuphold(posedge CLK_A1_i, WDATA_A1_i, 0, 0);
|
||||
$setuphold(posedge CLK_B1_i, RESET_ni, 0, 0);
|
||||
$setuphold(posedge CLK_B1_i, WEN_B1_i, 0, 0);
|
||||
$setuphold(posedge CLK_B1_i, REN_B1_i, 0, 0);
|
||||
$setuphold(posedge CLK_B1_i, BE_B1_i, 0, 0);
|
||||
$setuphold(posedge CLK_B1_i, ADDR_B1_i, 0, 0);
|
||||
$setuphold(posedge CLK_B1_i, WDATA_B1_i, 0, 0);
|
||||
$setuphold(posedge CLK_A2_i, RESET_ni, 0, 0);
|
||||
$setuphold(posedge CLK_A2_i, FLUSH2_i, 0, 0);
|
||||
$setuphold(posedge CLK_A2_i, WEN_A2_i, 0, 0);
|
||||
$setuphold(posedge CLK_A2_i, REN_A2_i, 0, 0);
|
||||
$setuphold(posedge CLK_A2_i, BE_A2_i, 0, 0);
|
||||
$setuphold(posedge CLK_A2_i, ADDR_A2_i, 0, 0);
|
||||
$setuphold(posedge CLK_A2_i, WDATA_A2_i, 0, 0);
|
||||
$setuphold(posedge CLK_B2_i, RESET_ni, 0, 0);
|
||||
$setuphold(posedge CLK_B2_i, WEN_B2_i, 0, 0);
|
||||
$setuphold(posedge CLK_B2_i, REN_B2_i, 0, 0);
|
||||
$setuphold(posedge CLK_B2_i, BE_B2_i, 0, 0);
|
||||
$setuphold(posedge CLK_B2_i, ADDR_B2_i, 0, 0);
|
||||
$setuphold(posedge CLK_B2_i, WDATA_B2_i, 0, 0);
|
||||
endspecify
|
||||
`endif
|
||||
endmodule
|
||||
""")
|
||||
|
||||
for a1_width in [1,2,4,9,18]:
|
||||
for b1_width in [1,2,4,9,18]:
|
||||
for a2_width in [1,2,4,9,18]:
|
||||
for b2_width in [1,2,4,9,18]:
|
||||
f.write(f"""
|
||||
module TDP36K_BRAM_A1_X{a1_width}_B1_X{b1_width}_A2_X{a2_width}_B2_X{b2_width}_split (
|
||||
RESET_ni,
|
||||
WEN_A1_i, WEN_B1_i,
|
||||
REN_A1_i, REN_B1_i,
|
||||
CLK_A1_i, CLK_B1_i,
|
||||
BE_A1_i, BE_B1_i,
|
||||
ADDR_A1_i, ADDR_B1_i,
|
||||
WDATA_A1_i, WDATA_B1_i,
|
||||
RDATA_A1_o, RDATA_B1_o,
|
||||
FLUSH1_i,
|
||||
WEN_A2_i, WEN_B2_i,
|
||||
REN_A2_i, REN_B2_i,
|
||||
CLK_A2_i, CLK_B2_i,
|
||||
BE_A2_i, BE_B2_i,
|
||||
ADDR_A2_i, ADDR_B2_i,
|
||||
WDATA_A2_i, WDATA_B2_i,
|
||||
RDATA_A2_o, RDATA_B2_o,
|
||||
FLUSH2_i
|
||||
);
|
||||
|
||||
parameter [80:0] MODE_BITS = 81'd0;
|
||||
parameter [1024*36-1:0] RAM_INIT = 36864'bx;
|
||||
|
||||
input wire RESET_ni;
|
||||
input wire WEN_A1_i, WEN_B1_i;
|
||||
input wire REN_A1_i, REN_B1_i;
|
||||
input wire WEN_A2_i, WEN_B2_i;
|
||||
input wire REN_A2_i, REN_B2_i;
|
||||
|
||||
(* clkbuf_sink *)
|
||||
input wire CLK_A1_i;
|
||||
(* clkbuf_sink *)
|
||||
input wire CLK_B1_i;
|
||||
(* clkbuf_sink *)
|
||||
input wire CLK_A2_i;
|
||||
(* clkbuf_sink *)
|
||||
input wire CLK_B2_i;
|
||||
|
||||
input wire [ 1:0] BE_A1_i, BE_B1_i;
|
||||
input wire [14:0] ADDR_A1_i, ADDR_B1_i;
|
||||
input wire [17:0] WDATA_A1_i, WDATA_B1_i;
|
||||
output wire [17:0] RDATA_A1_o, RDATA_B1_o;
|
||||
|
||||
input wire FLUSH1_i;
|
||||
|
||||
input wire [ 1:0] BE_A2_i, BE_B2_i;
|
||||
input wire [13:0] ADDR_A2_i, ADDR_B2_i;
|
||||
input wire [17:0] WDATA_A2_i, WDATA_B2_i;
|
||||
output wire [17:0] RDATA_A2_o, RDATA_B2_o;
|
||||
|
||||
input wire FLUSH2_i;
|
||||
|
||||
TDP36K #(.MODE_BITS(MODE_BITS), .RAM_INIT(RAM_INIT)) bram (
|
||||
.RESET_ni (RESET_ni),
|
||||
.WEN_A1_i (WEN_A1_i), .WEN_B1_i (WEN_B1_i),
|
||||
.REN_A1_i (REN_A1_i), .REN_B1_i (REN_B1_i),
|
||||
.CLK_A1_i (CLK_A1_i), .CLK_B1_i (CLK_B1_i),
|
||||
.BE_A1_i (BE_A1_i), .BE_B1_i (BE_B1_i),
|
||||
.ADDR_A1_i (ADDR_A1_i), .ADDR_B1_i (ADDR_B1_i),
|
||||
.WDATA_A1_i (WDATA_A1_i), .WDATA_B1_i (WDATA_B1_i),
|
||||
.RDATA_A1_o (RDATA_A1_o), .RDATA_B1_o (RDATA_B1_o),
|
||||
.FLUSH1_i (FLUSH1_i),
|
||||
.WEN_A2_i (WEN_A2_i), .WEN_B2_i (WEN_B2_i),
|
||||
.REN_A2_i (REN_A2_i), .REN_B2_i (REN_B2_i),
|
||||
.CLK_A2_i (CLK_A2_i), .CLK_B2_i (CLK_B2_i),
|
||||
.BE_A2_i (BE_A2_i), .BE_B2_i (BE_B2_i),
|
||||
.ADDR_A2_i (ADDR_A2_i), .ADDR_B2_i (ADDR_B2_i),
|
||||
.WDATA_A2_i (WDATA_A2_i), .WDATA_B2_i (WDATA_B2_i),
|
||||
.RDATA_A2_o (RDATA_A2_o), .RDATA_B2_o (RDATA_B2_o),
|
||||
.FLUSH2_i (FLUSH2_i)
|
||||
);
|
||||
|
||||
`ifdef SDF_SIM
|
||||
specify
|
||||
(negedge RESET_ni => (RDATA_A1_o +: WDATA_A1_i)) = 0;
|
||||
(negedge RESET_ni => (RDATA_B1_o +: WDATA_B1_i)) = 0;
|
||||
(negedge RESET_ni => (RDATA_A2_o +: WDATA_A2_i)) = 0;
|
||||
(negedge RESET_ni => (RDATA_B2_o +: WDATA_B2_i)) = 0;
|
||||
(posedge CLK_A1_i => (RDATA_A1_o +: WDATA_A1_i)) = 0;
|
||||
(posedge CLK_B1_i => (RDATA_B1_o +: WDATA_B1_i)) = 0;
|
||||
(posedge CLK_A2_i => (RDATA_A2_o +: WDATA_A2_i)) = 0;
|
||||
(posedge CLK_B2_i => (RDATA_B2_o +: WDATA_B2_i)) = 0;
|
||||
$setuphold(posedge CLK_A1_i, RESET_ni, 0, 0);
|
||||
$setuphold(posedge CLK_A1_i, FLUSH1_i, 0, 0);
|
||||
$setuphold(posedge CLK_A1_i, WEN_A1_i, 0, 0);
|
||||
$setuphold(posedge CLK_A1_i, REN_A1_i, 0, 0);
|
||||
$setuphold(posedge CLK_A1_i, BE_A1_i, 0, 0);
|
||||
$setuphold(posedge CLK_A1_i, ADDR_A1_i, 0, 0);
|
||||
$setuphold(posedge CLK_A1_i, WDATA_A1_i, 0, 0);
|
||||
$setuphold(posedge CLK_B1_i, RESET_ni, 0, 0);
|
||||
$setuphold(posedge CLK_B1_i, WEN_B1_i, 0, 0);
|
||||
$setuphold(posedge CLK_B1_i, REN_B1_i, 0, 0);
|
||||
$setuphold(posedge CLK_B1_i, BE_B1_i, 0, 0);
|
||||
$setuphold(posedge CLK_B1_i, ADDR_B1_i, 0, 0);
|
||||
$setuphold(posedge CLK_B1_i, WDATA_B1_i, 0, 0);
|
||||
$setuphold(posedge CLK_A2_i, RESET_ni, 0, 0);
|
||||
$setuphold(posedge CLK_A2_i, FLUSH2_i, 0, 0);
|
||||
$setuphold(posedge CLK_A2_i, WEN_A2_i, 0, 0);
|
||||
$setuphold(posedge CLK_A2_i, REN_A2_i, 0, 0);
|
||||
$setuphold(posedge CLK_A2_i, BE_A2_i, 0, 0);
|
||||
$setuphold(posedge CLK_A2_i, ADDR_A2_i, 0, 0);
|
||||
$setuphold(posedge CLK_A2_i, WDATA_A2_i, 0, 0);
|
||||
$setuphold(posedge CLK_B2_i, RESET_ni, 0, 0);
|
||||
$setuphold(posedge CLK_B2_i, WEN_B2_i, 0, 0);
|
||||
$setuphold(posedge CLK_B2_i, REN_B2_i, 0, 0);
|
||||
$setuphold(posedge CLK_B2_i, BE_B2_i, 0, 0);
|
||||
$setuphold(posedge CLK_B2_i, ADDR_B2_i, 0, 0);
|
||||
$setuphold(posedge CLK_B2_i, WDATA_B2_i, 0, 0);
|
||||
endspecify
|
||||
`endif
|
||||
endmodule
|
||||
""")
|
||||
|
||||
if __name__ == "__main__":
|
||||
filename = "bram_types_sim.v"
|
||||
if len(sys.argv) > 1:
|
||||
filename = sys.argv[1]
|
||||
generate(filename)
|
|
@ -0,0 +1,22 @@
|
|||
ram block $__QLF_TDP36K {
|
||||
init any;
|
||||
byte 9;
|
||||
option "SPLIT" 0 {
|
||||
abits 15;
|
||||
widths 1 2 4 9 18 36 per_port;
|
||||
}
|
||||
option "SPLIT" 1 {
|
||||
abits 14;
|
||||
widths 1 2 4 9 18 per_port;
|
||||
}
|
||||
cost 65;
|
||||
port srsw "A" "B" {
|
||||
width tied;
|
||||
clock posedge;
|
||||
# wen causes read even when ren is low
|
||||
# map clken = wen || ren
|
||||
clken;
|
||||
wrbe_separate;
|
||||
rdwr old;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,483 @@
|
|||
// Copyright 2020-2022 F4PGA Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
module \$__QLF_TDP36K (PORT_A_CLK, PORT_A_ADDR, PORT_A_WR_DATA, PORT_A_WR_EN, PORT_A_WR_BE, PORT_A_CLK_EN, PORT_A_RD_DATA,
|
||||
PORT_B_CLK, PORT_B_ADDR, PORT_B_WR_DATA, PORT_B_WR_EN, PORT_B_WR_BE, PORT_B_CLK_EN, PORT_B_RD_DATA);
|
||||
|
||||
parameter INIT = 0;
|
||||
|
||||
parameter OPTION_SPLIT = 0;
|
||||
|
||||
parameter PORT_A_WIDTH = 1;
|
||||
parameter PORT_A_WR_BE_WIDTH = 1;
|
||||
|
||||
parameter PORT_B_WIDTH = 1;
|
||||
parameter PORT_B_WR_BE_WIDTH = 1;
|
||||
|
||||
input PORT_A_CLK;
|
||||
input [14:0] PORT_A_ADDR;
|
||||
input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA;
|
||||
input PORT_A_WR_EN;
|
||||
input [PORT_A_WR_BE_WIDTH-1:0] PORT_A_WR_BE;
|
||||
input PORT_A_CLK_EN;
|
||||
output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA;
|
||||
|
||||
input PORT_B_CLK;
|
||||
input [14:0] PORT_B_ADDR;
|
||||
input [PORT_B_WIDTH-1:0] PORT_B_WR_DATA;
|
||||
input PORT_B_WR_EN;
|
||||
input [PORT_B_WR_BE_WIDTH-1:0] PORT_B_WR_BE;
|
||||
input PORT_B_CLK_EN;
|
||||
output [PORT_B_WIDTH-1:0] PORT_B_RD_DATA;
|
||||
|
||||
|
||||
// Fixed mode settings
|
||||
localparam [ 0:0] SYNC_FIFO1_i = 1'd0;
|
||||
localparam [ 0:0] FMODE1_i = 1'd0;
|
||||
localparam [ 0:0] POWERDN1_i = 1'd0;
|
||||
localparam [ 0:0] SLEEP1_i = 1'd0;
|
||||
localparam [ 0:0] PROTECT1_i = 1'd0;
|
||||
localparam [11:0] UPAE1_i = 12'd10;
|
||||
localparam [11:0] UPAF1_i = 12'd10;
|
||||
|
||||
localparam [ 0:0] SYNC_FIFO2_i = 1'd0;
|
||||
localparam [ 0:0] FMODE2_i = 1'd0;
|
||||
localparam [ 0:0] POWERDN2_i = 1'd0;
|
||||
localparam [ 0:0] SLEEP2_i = 1'd0;
|
||||
localparam [ 0:0] PROTECT2_i = 1'd0;
|
||||
localparam [10:0] UPAE2_i = 11'd10;
|
||||
localparam [10:0] UPAF2_i = 11'd10;
|
||||
|
||||
// Width mode function
|
||||
function [2:0] mode;
|
||||
input integer width;
|
||||
case (width)
|
||||
1: mode = 3'b101;
|
||||
2: mode = 3'b110;
|
||||
4: mode = 3'b100;
|
||||
8,9: mode = 3'b001;
|
||||
16, 18: mode = 3'b010;
|
||||
32, 36: mode = 3'b011;
|
||||
default: mode = 3'b000;
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
function [36863:0] pack_init;
|
||||
integer i;
|
||||
reg [35:0] ri;
|
||||
for (i = 0; i < (OPTION_SPLIT ? 512 : 1024); i = i + 1) begin
|
||||
ri = INIT[i*36 +: 36];
|
||||
pack_init[i*36 +: 36] = {ri[35], ri[26], ri[34:27], ri[25:18],
|
||||
ri[17], ri[8], ri[16:9], ri[7:0]};
|
||||
end
|
||||
if (OPTION_SPLIT)
|
||||
pack_init[36863:18432] = 18432'bx;
|
||||
endfunction
|
||||
|
||||
wire REN_A1_i;
|
||||
wire REN_A2_i;
|
||||
|
||||
wire REN_B1_i;
|
||||
wire REN_B2_i;
|
||||
|
||||
wire WEN_A1_i;
|
||||
wire WEN_A2_i;
|
||||
|
||||
wire WEN_B1_i;
|
||||
wire WEN_B2_i;
|
||||
|
||||
wire [1:0] BE_A1_i;
|
||||
wire [1:0] BE_A2_i;
|
||||
|
||||
wire [1:0] BE_B1_i;
|
||||
wire [1:0] BE_B2_i;
|
||||
|
||||
wire [14:0] ADDR_A1_i;
|
||||
wire [13:0] ADDR_A2_i;
|
||||
|
||||
wire [14:0] ADDR_B1_i;
|
||||
wire [13:0] ADDR_B2_i;
|
||||
|
||||
wire [17:0] WDATA_A1_i;
|
||||
wire [17:0] WDATA_A2_i;
|
||||
|
||||
wire [17:0] WDATA_B1_i;
|
||||
wire [17:0] WDATA_B2_i;
|
||||
|
||||
wire [17:0] RDATA_A1_o;
|
||||
wire [17:0] RDATA_A2_o;
|
||||
|
||||
wire [17:0] RDATA_B1_o;
|
||||
wire [17:0] RDATA_B2_o;
|
||||
|
||||
|
||||
// Set port width mode (In non-split mode A2/B2 is not active. Set same values anyway to match previous behavior.)
|
||||
localparam [ 2:0] RMODE_A1_i = mode(PORT_A_WIDTH);
|
||||
localparam [ 2:0] WMODE_A1_i = mode(PORT_A_WIDTH);
|
||||
localparam [ 2:0] RMODE_A2_i = mode(PORT_A_WIDTH);
|
||||
localparam [ 2:0] WMODE_A2_i = mode(PORT_A_WIDTH);
|
||||
|
||||
localparam [ 2:0] RMODE_B1_i = mode(PORT_B_WIDTH);
|
||||
localparam [ 2:0] WMODE_B1_i = mode(PORT_B_WIDTH);
|
||||
localparam [ 2:0] RMODE_B2_i = mode(PORT_B_WIDTH);
|
||||
localparam [ 2:0] WMODE_B2_i = mode(PORT_B_WIDTH);
|
||||
|
||||
assign REN_A1_i = PORT_A_CLK_EN;
|
||||
assign WEN_A1_i = PORT_A_CLK_EN & PORT_A_WR_EN;
|
||||
assign {BE_A2_i, BE_A1_i} = PORT_A_WR_BE;
|
||||
|
||||
assign REN_B1_i = PORT_B_CLK_EN;
|
||||
assign WEN_B1_i = PORT_B_CLK_EN & PORT_B_WR_EN;
|
||||
assign {BE_B2_i, BE_B1_i} = PORT_B_WR_BE;
|
||||
|
||||
case (PORT_A_WIDTH)
|
||||
9: assign { WDATA_A1_i[16], WDATA_A1_i[7:0] } = PORT_A_WR_DATA;
|
||||
18: assign { WDATA_A1_i[17], WDATA_A1_i[15:8], WDATA_A1_i[16], WDATA_A1_i[7:0] } = PORT_A_WR_DATA;
|
||||
36: assign { WDATA_A2_i[17], WDATA_A2_i[15:8], WDATA_A2_i[16], WDATA_A2_i[7:0], WDATA_A1_i[17], WDATA_A1_i[15:8], WDATA_A1_i[16], WDATA_A1_i[7:0]} = PORT_A_WR_DATA;
|
||||
default: assign WDATA_A1_i = PORT_A_WR_DATA; // 1,2,4
|
||||
endcase
|
||||
|
||||
case (PORT_B_WIDTH)
|
||||
9: assign { WDATA_B1_i[16], WDATA_B1_i[7:0] } = PORT_B_WR_DATA;
|
||||
18: assign { WDATA_B1_i[17], WDATA_B1_i[15:8], WDATA_B1_i[16], WDATA_B1_i[7:0] } = PORT_B_WR_DATA;
|
||||
36: assign { WDATA_B2_i[17], WDATA_B2_i[15:8], WDATA_B2_i[16], WDATA_B2_i[7:0], WDATA_B1_i[17], WDATA_B1_i[15:8], WDATA_B1_i[16], WDATA_B1_i[7:0]} = PORT_B_WR_DATA;
|
||||
default: assign WDATA_B1_i = PORT_B_WR_DATA; // 1,2,4
|
||||
endcase
|
||||
|
||||
case (PORT_A_WIDTH)
|
||||
9: assign PORT_A_RD_DATA = { RDATA_A1_o[16], RDATA_A1_o[7:0] };
|
||||
18: assign PORT_A_RD_DATA = { RDATA_A1_o[17], RDATA_A1_o[15:8], RDATA_A1_o[16], RDATA_A1_o[7:0] };
|
||||
36: assign PORT_A_RD_DATA = { RDATA_A2_o[17], RDATA_A2_o[15:8], RDATA_A2_o[16], RDATA_A2_o[7:0], RDATA_A1_o[17], RDATA_A1_o[15:8], RDATA_A1_o[16], RDATA_A1_o[7:0]};
|
||||
default: assign PORT_A_RD_DATA = RDATA_A1_o; // 1,2,4
|
||||
endcase
|
||||
|
||||
case (PORT_B_WIDTH)
|
||||
9: assign PORT_B_RD_DATA = { RDATA_B1_o[16], RDATA_B1_o[7:0] };
|
||||
18: assign PORT_B_RD_DATA = { RDATA_B1_o[17], RDATA_B1_o[15:8], RDATA_B1_o[16], RDATA_B1_o[7:0] };
|
||||
36: assign PORT_B_RD_DATA = { RDATA_B2_o[17], RDATA_B2_o[15:8], RDATA_B2_o[16], RDATA_B2_o[7:0], RDATA_B1_o[17], RDATA_B1_o[15:8], RDATA_B1_o[16], RDATA_B1_o[7:0]};
|
||||
default: assign PORT_B_RD_DATA = RDATA_B1_o; // 1,2,4
|
||||
endcase
|
||||
|
||||
defparam _TECHMAP_REPLACE_.MODE_BITS = { 1'b0,
|
||||
UPAF2_i, UPAE2_i, PROTECT2_i, SLEEP2_i, POWERDN2_i, FMODE2_i, WMODE_B2_i, WMODE_A2_i, RMODE_B2_i, RMODE_A2_i, SYNC_FIFO2_i,
|
||||
UPAF1_i, UPAE1_i, PROTECT1_i, SLEEP1_i, POWERDN1_i, FMODE1_i, WMODE_B1_i, WMODE_A1_i, RMODE_B1_i, RMODE_A1_i, SYNC_FIFO1_i
|
||||
};
|
||||
|
||||
(* is_inferred = 1 *)
|
||||
(* is_split = 0 *)
|
||||
(* was_split_candidate = OPTION_SPLIT *)
|
||||
(* port_a_width = PORT_A_WIDTH *)
|
||||
(* port_b_width = PORT_B_WIDTH *)
|
||||
TDP36K #(
|
||||
.RAM_INIT(pack_init()),
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.RESET_ni(1'b1),
|
||||
|
||||
.CLK_A1_i(PORT_A_CLK),
|
||||
.ADDR_A1_i(PORT_A_ADDR),
|
||||
.WEN_A1_i(WEN_A1_i),
|
||||
.BE_A1_i(BE_A1_i),
|
||||
.WDATA_A1_i(WDATA_A1_i),
|
||||
.REN_A1_i(REN_A1_i),
|
||||
.RDATA_A1_o(RDATA_A1_o),
|
||||
|
||||
.CLK_A2_i(PORT_A_CLK),
|
||||
.ADDR_A2_i(PORT_A_ADDR[13:0]),
|
||||
.WEN_A2_i(WEN_A1_i),
|
||||
.BE_A2_i(BE_A2_i),
|
||||
.WDATA_A2_i(WDATA_A2_i),
|
||||
.REN_A2_i(REN_A1_i),
|
||||
.RDATA_A2_o(RDATA_A2_o),
|
||||
|
||||
.CLK_B1_i(PORT_B_CLK),
|
||||
.ADDR_B1_i(PORT_B_ADDR),
|
||||
.WEN_B1_i(WEN_B1_i),
|
||||
.BE_B1_i(BE_B1_i),
|
||||
.WDATA_B1_i(WDATA_B1_i),
|
||||
.REN_B1_i(REN_B1_i),
|
||||
.RDATA_B1_o(RDATA_B1_o),
|
||||
|
||||
.CLK_B2_i(PORT_B_CLK),
|
||||
.ADDR_B2_i(PORT_B_ADDR[13:0]),
|
||||
.WEN_B2_i(WEN_B1_i),
|
||||
.BE_B2_i(BE_B2_i),
|
||||
.WDATA_B2_i(WDATA_B2_i),
|
||||
.REN_B2_i(REN_B1_i),
|
||||
.RDATA_B2_o(RDATA_B2_o),
|
||||
|
||||
.FLUSH1_i(1'b0),
|
||||
.FLUSH2_i(1'b0)
|
||||
);
|
||||
|
||||
endmodule
|
||||
|
||||
|
||||
module \$__QLF_TDP36K_MERGED (...);
|
||||
|
||||
parameter INIT1 = 0;
|
||||
|
||||
parameter PORT_A1_WIDTH = 1;
|
||||
parameter PORT_B1_WIDTH = 1;
|
||||
|
||||
parameter PORT_A1_WR_BE_WIDTH = 1;
|
||||
parameter PORT_B1_WR_BE_WIDTH = 1;
|
||||
|
||||
input PORT_A1_CLK;
|
||||
input [14:0] PORT_A1_ADDR;
|
||||
input [PORT_A1_WIDTH-1:0] PORT_A1_WR_DATA;
|
||||
input PORT_A1_WR_EN;
|
||||
input [PORT_A1_WR_BE_WIDTH-1:0] PORT_A1_WR_BE;
|
||||
input PORT_A1_CLK_EN;
|
||||
output [PORT_A1_WIDTH-1:0] PORT_A1_RD_DATA;
|
||||
|
||||
input PORT_B1_CLK;
|
||||
input [14:0] PORT_B1_ADDR;
|
||||
input [PORT_B1_WIDTH-1:0] PORT_B1_WR_DATA;
|
||||
input PORT_B1_WR_EN;
|
||||
input [PORT_B1_WR_BE_WIDTH-1:0] PORT_B1_WR_BE;
|
||||
input PORT_B1_CLK_EN;
|
||||
output [PORT_B1_WIDTH-1:0] PORT_B1_RD_DATA;
|
||||
|
||||
parameter INIT2 = 0;
|
||||
|
||||
parameter PORT_A2_WIDTH = 1;
|
||||
parameter PORT_B2_WIDTH = 1;
|
||||
parameter PORT_A2_WR_BE_WIDTH = 1;
|
||||
parameter PORT_B2_WR_BE_WIDTH = 1;
|
||||
|
||||
input PORT_A2_CLK;
|
||||
input [14:0] PORT_A2_ADDR;
|
||||
input [PORT_A2_WIDTH-1:0] PORT_A2_WR_DATA;
|
||||
input PORT_A2_WR_EN;
|
||||
input [PORT_A2_WR_BE_WIDTH-1:0] PORT_A2_WR_BE;
|
||||
input PORT_A2_CLK_EN;
|
||||
output [PORT_A2_WIDTH-1:0] PORT_A2_RD_DATA;
|
||||
|
||||
input PORT_B2_CLK;
|
||||
input [14:0] PORT_B2_ADDR;
|
||||
input [PORT_B2_WIDTH-1:0] PORT_B2_WR_DATA;
|
||||
input PORT_B2_WR_EN;
|
||||
input [PORT_B2_WR_BE_WIDTH-1:0] PORT_B2_WR_BE;
|
||||
input PORT_B2_CLK_EN;
|
||||
output [PORT_B2_WIDTH-1:0] PORT_B2_RD_DATA;
|
||||
|
||||
|
||||
// Fixed mode settings
|
||||
localparam [ 0:0] SYNC_FIFO1_i = 1'd0;
|
||||
localparam [ 0:0] FMODE1_i = 1'd0;
|
||||
localparam [ 0:0] POWERDN1_i = 1'd0;
|
||||
localparam [ 0:0] SLEEP1_i = 1'd0;
|
||||
localparam [ 0:0] PROTECT1_i = 1'd0;
|
||||
localparam [11:0] UPAE1_i = 12'd10;
|
||||
localparam [11:0] UPAF1_i = 12'd10;
|
||||
|
||||
localparam [ 0:0] SYNC_FIFO2_i = 1'd0;
|
||||
localparam [ 0:0] FMODE2_i = 1'd0;
|
||||
localparam [ 0:0] POWERDN2_i = 1'd0;
|
||||
localparam [ 0:0] SLEEP2_i = 1'd0;
|
||||
localparam [ 0:0] PROTECT2_i = 1'd0;
|
||||
localparam [10:0] UPAE2_i = 11'd10;
|
||||
localparam [10:0] UPAF2_i = 11'd10;
|
||||
|
||||
// Width mode function
|
||||
function [2:0] mode;
|
||||
input integer width;
|
||||
case (width)
|
||||
1: mode = 3'b101;
|
||||
2: mode = 3'b110;
|
||||
4: mode = 3'b100;
|
||||
8,9: mode = 3'b001;
|
||||
16, 18: mode = 3'b010;
|
||||
default: mode = 3'b000;
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
function [36863:0] pack_init;
|
||||
integer i;
|
||||
reg [35:0] ri;
|
||||
for (i = 0; i < 1024; i = i + 1) begin
|
||||
ri = {INIT2[i*18 +: 18], INIT1[i*18 +: 18]};
|
||||
pack_init[i*36 +: 36] = {ri[35], ri[26], ri[34:27], ri[25:18], ri[17], ri[8], ri[16:9], ri[7:0]};
|
||||
end
|
||||
endfunction
|
||||
|
||||
wire REN_A1_i;
|
||||
wire REN_A2_i;
|
||||
|
||||
wire REN_B1_i;
|
||||
wire REN_B2_i;
|
||||
|
||||
wire WEN_A1_i;
|
||||
wire WEN_A2_i;
|
||||
|
||||
wire WEN_B1_i;
|
||||
wire WEN_B2_i;
|
||||
|
||||
wire [1:0] BE_A1_i;
|
||||
wire [1:0] BE_A2_i;
|
||||
|
||||
wire [1:0] BE_B1_i;
|
||||
wire [1:0] BE_B2_i;
|
||||
|
||||
wire [14:0] ADDR_A1_i;
|
||||
wire [13:0] ADDR_A2_i;
|
||||
|
||||
wire [14:0] ADDR_B1_i;
|
||||
wire [13:0] ADDR_B2_i;
|
||||
|
||||
wire [17:0] WDATA_A1_i;
|
||||
wire [17:0] WDATA_A2_i;
|
||||
|
||||
wire [17:0] WDATA_B1_i;
|
||||
wire [17:0] WDATA_B2_i;
|
||||
|
||||
wire [17:0] RDATA_A1_o;
|
||||
wire [17:0] RDATA_A2_o;
|
||||
|
||||
wire [17:0] RDATA_B1_o;
|
||||
wire [17:0] RDATA_B2_o;
|
||||
|
||||
|
||||
// Set port width mode (In non-split mode A2/B2 is not active. Set same values anyway to match previous behavior.)
|
||||
localparam [ 2:0] RMODE_A1_i = mode(PORT_A1_WIDTH);
|
||||
localparam [ 2:0] WMODE_A1_i = mode(PORT_A1_WIDTH);
|
||||
localparam [ 2:0] RMODE_B1_i = mode(PORT_B1_WIDTH);
|
||||
localparam [ 2:0] WMODE_B1_i = mode(PORT_B1_WIDTH);
|
||||
|
||||
localparam [ 2:0] RMODE_A2_i = mode(PORT_A2_WIDTH);
|
||||
localparam [ 2:0] WMODE_A2_i = mode(PORT_A2_WIDTH);
|
||||
localparam [ 2:0] RMODE_B2_i = mode(PORT_B2_WIDTH);
|
||||
localparam [ 2:0] WMODE_B2_i = mode(PORT_B2_WIDTH);
|
||||
|
||||
assign REN_A1_i = PORT_A1_CLK_EN;
|
||||
assign WEN_A1_i = PORT_A1_CLK_EN & PORT_A1_WR_EN;
|
||||
assign BE_A1_i = PORT_A1_WR_BE;
|
||||
|
||||
assign REN_B1_i = PORT_B1_CLK_EN;
|
||||
assign WEN_B1_i = PORT_B1_CLK_EN & PORT_B1_WR_EN;
|
||||
assign BE_B1_i = PORT_B1_WR_BE;
|
||||
|
||||
assign REN_A2_i = PORT_A2_CLK_EN;
|
||||
assign WEN_A2_i = PORT_A2_CLK_EN & PORT_A2_WR_EN;
|
||||
assign BE_A2_i = PORT_A2_WR_BE;
|
||||
|
||||
assign REN_B2_i = PORT_B2_CLK_EN;
|
||||
assign WEN_B2_i = PORT_B2_CLK_EN & PORT_B2_WR_EN;
|
||||
assign BE_B2_i = PORT_B2_WR_BE;
|
||||
|
||||
assign ADDR_A1_i = PORT_A1_ADDR;
|
||||
assign ADDR_B1_i = PORT_B1_ADDR;
|
||||
assign ADDR_A2_i = PORT_A2_ADDR;
|
||||
assign ADDR_B2_i = PORT_B2_ADDR;
|
||||
|
||||
case (PORT_A1_WIDTH)
|
||||
9: assign { WDATA_A1_i[16], WDATA_A1_i[7:0] } = PORT_A1_WR_DATA;
|
||||
18: assign { WDATA_A1_i[17], WDATA_A1_i[15:8], WDATA_A1_i[16], WDATA_A1_i[7:0] } = PORT_A1_WR_DATA;
|
||||
default: assign WDATA_A1_i = PORT_A1_WR_DATA; // 1,2,4,8,16
|
||||
endcase
|
||||
|
||||
case (PORT_B1_WIDTH)
|
||||
9: assign { WDATA_B1_i[16], WDATA_B1_i[7:0] } = PORT_B1_WR_DATA;
|
||||
18: assign { WDATA_B1_i[17], WDATA_B1_i[15:8], WDATA_B1_i[16], WDATA_B1_i[7:0] } = PORT_B1_WR_DATA;
|
||||
default: assign WDATA_B1_i = PORT_B1_WR_DATA; // 1,2,4,8,16
|
||||
endcase
|
||||
|
||||
case (PORT_A1_WIDTH)
|
||||
9: assign PORT_A1_RD_DATA = { RDATA_A1_o[16], RDATA_A1_o[7:0] };
|
||||
18: assign PORT_A1_RD_DATA = { RDATA_A1_o[17], RDATA_A1_o[15:8], RDATA_A1_o[16], RDATA_A1_o[7:0] };
|
||||
default: assign PORT_A1_RD_DATA = RDATA_A1_o; // 1,2,4,8,16
|
||||
endcase
|
||||
|
||||
case (PORT_B1_WIDTH)
|
||||
9: assign PORT_B1_RD_DATA = { RDATA_B1_o[16], RDATA_B1_o[7:0] };
|
||||
18: assign PORT_B1_RD_DATA = { RDATA_B1_o[17], RDATA_B1_o[15:8], RDATA_B1_o[16], RDATA_B1_o[7:0] };
|
||||
default: assign PORT_B1_RD_DATA = RDATA_B1_o; // 1,2,4,8,16
|
||||
endcase
|
||||
|
||||
case (PORT_A2_WIDTH)
|
||||
9: assign { WDATA_A2_i[16], WDATA_A2_i[7:0] } = PORT_A2_WR_DATA;
|
||||
18: assign { WDATA_A2_i[17], WDATA_A2_i[15:8], WDATA_A2_i[16], WDATA_A2_i[7:0] } = PORT_A2_WR_DATA;
|
||||
default: assign WDATA_A2_i = PORT_A2_WR_DATA; // 1,2,4,8,16
|
||||
endcase
|
||||
|
||||
case (PORT_B2_WIDTH)
|
||||
9: assign { WDATA_B2_i[16], WDATA_B2_i[7:0] } = PORT_B2_WR_DATA;
|
||||
18: assign { WDATA_B2_i[17], WDATA_B2_i[15:8], WDATA_B2_i[16], WDATA_B2_i[7:0] } = PORT_B2_WR_DATA;
|
||||
default: assign WDATA_B2_i = PORT_B2_WR_DATA; // 1,2,4,8,16
|
||||
endcase
|
||||
|
||||
case (PORT_A2_WIDTH)
|
||||
9: assign PORT_A2_RD_DATA = { RDATA_A2_o[16], RDATA_A2_o[7:0] };
|
||||
18: assign PORT_A2_RD_DATA = { RDATA_A2_o[17], RDATA_A2_o[15:8], RDATA_A2_o[16], RDATA_A2_o[7:0] };
|
||||
default: assign PORT_A2_RD_DATA = RDATA_A2_o; // 1,2,4,8,16
|
||||
endcase
|
||||
|
||||
case (PORT_B2_WIDTH)
|
||||
9: assign PORT_B2_RD_DATA = { RDATA_B2_o[16], RDATA_B2_o[7:0] };
|
||||
18: assign PORT_B2_RD_DATA = { RDATA_B2_o[17], RDATA_B2_o[15:8], RDATA_B2_o[16], RDATA_B2_o[7:0] };
|
||||
default: assign PORT_B2_RD_DATA = RDATA_B2_o; // 1,2,4,8,16
|
||||
endcase
|
||||
|
||||
defparam _TECHMAP_REPLACE_.MODE_BITS = {1'b1,
|
||||
UPAF2_i, UPAE2_i, PROTECT2_i, SLEEP2_i, POWERDN2_i, FMODE2_i, WMODE_B2_i, WMODE_A2_i, RMODE_B2_i, RMODE_A2_i, SYNC_FIFO2_i,
|
||||
UPAF1_i, UPAE1_i, PROTECT1_i, SLEEP1_i, POWERDN1_i, FMODE1_i, WMODE_B1_i, WMODE_A1_i, RMODE_B1_i, RMODE_A1_i, SYNC_FIFO1_i
|
||||
};
|
||||
|
||||
(* is_inferred = 1 *)
|
||||
(* is_split = 1 *)
|
||||
(* port_a1_width = PORT_A1_WIDTH *)
|
||||
(* port_a2_width = PORT_A2_WIDTH *)
|
||||
(* port_b1_width = PORT_B1_WIDTH *)
|
||||
(* port_b2_width = PORT_B2_WIDTH *)
|
||||
TDP36K #(
|
||||
.RAM_INIT(pack_init()),
|
||||
) _TECHMAP_REPLACE_ (
|
||||
.RESET_ni(1'b1),
|
||||
.WDATA_A1_i(WDATA_A1_i),
|
||||
.WDATA_A2_i(WDATA_A2_i),
|
||||
.RDATA_A1_o(RDATA_A1_o),
|
||||
.RDATA_A2_o(RDATA_A2_o),
|
||||
.ADDR_A1_i(ADDR_A1_i),
|
||||
.ADDR_A2_i(ADDR_A2_i),
|
||||
.CLK_A1_i(PORT_A1_CLK),
|
||||
.CLK_A2_i(PORT_A2_CLK),
|
||||
.REN_A1_i(REN_A1_i),
|
||||
.REN_A2_i(REN_A2_i),
|
||||
.WEN_A1_i(WEN_A1_i),
|
||||
.WEN_A2_i(WEN_A2_i),
|
||||
.BE_A1_i(BE_A1_i),
|
||||
.BE_A2_i(BE_A2_i),
|
||||
|
||||
.WDATA_B1_i(WDATA_B1_i),
|
||||
.WDATA_B2_i(WDATA_B2_i),
|
||||
.RDATA_B1_o(RDATA_B1_o),
|
||||
.RDATA_B2_o(RDATA_B2_o),
|
||||
.ADDR_B1_i(ADDR_B1_i),
|
||||
.ADDR_B2_i(ADDR_B2_i),
|
||||
.CLK_B1_i(PORT_B1_CLK),
|
||||
.CLK_B2_i(PORT_B2_CLK),
|
||||
.REN_B1_i(REN_B1_i),
|
||||
.REN_B2_i(REN_B2_i),
|
||||
.WEN_B1_i(WEN_B1_i),
|
||||
.WEN_B2_i(WEN_B2_i),
|
||||
.BE_B1_i(BE_B1_i),
|
||||
.BE_B2_i(BE_B2_i),
|
||||
|
||||
.FLUSH1_i(1'b0),
|
||||
.FLUSH2_i(1'b0)
|
||||
);
|
||||
|
||||
endmodule
|
|
@ -0,0 +1,64 @@
|
|||
`default_nettype none
|
||||
module sram1024x18 (
|
||||
clk_a,
|
||||
cen_a,
|
||||
wen_a,
|
||||
addr_a,
|
||||
wmsk_a,
|
||||
wdata_a,
|
||||
rdata_a,
|
||||
clk_b,
|
||||
cen_b,
|
||||
wen_b,
|
||||
addr_b,
|
||||
wmsk_b,
|
||||
wdata_b,
|
||||
rdata_b
|
||||
);
|
||||
parameter [1024*18-1:0] init = 18431'bx;
|
||||
(* clkbuf_sink *)
|
||||
input wire clk_a;
|
||||
input wire cen_a;
|
||||
input wire wen_a;
|
||||
input wire [9:0] addr_a;
|
||||
input wire [17:0] wmsk_a;
|
||||
input wire [17:0] wdata_a;
|
||||
output reg [17:0] rdata_a;
|
||||
(* clkbuf_sink *)
|
||||
input wire clk_b;
|
||||
input wire cen_b;
|
||||
input wire wen_b;
|
||||
input wire [9:0] addr_b;
|
||||
input wire [17:0] wmsk_b;
|
||||
input wire [17:0] wdata_b;
|
||||
output reg [17:0] rdata_b;
|
||||
reg [17:0] ram [1023:0];
|
||||
integer i;
|
||||
initial begin
|
||||
for (i = 0; i < 1024; i = i + 1) begin
|
||||
ram[i] = init[18*i +: 18];
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk_a) begin
|
||||
if (!cen_a) begin
|
||||
if (!wen_a)
|
||||
for (i = 0; i < 18; i++) begin
|
||||
if (!wmsk_a[i]) ram[addr_a][i] <= wdata_a[i];
|
||||
end
|
||||
rdata_a <= ram[addr_a];
|
||||
end
|
||||
end
|
||||
|
||||
always @(posedge clk_b) begin
|
||||
if (!cen_b) begin
|
||||
if (!wen_b)
|
||||
for (i = 0; i < 18; i++) begin
|
||||
if (!wmsk_b[i]) ram[addr_b][i] <= wdata_b[i];
|
||||
end
|
||||
rdata_b <= ram[addr_b];
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,620 @@
|
|||
// Copyright 2020-2022 F4PGA Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
`default_nettype wire
|
||||
module fifo_ctl (
|
||||
raddr,
|
||||
waddr,
|
||||
fflags,
|
||||
ren_o,
|
||||
sync,
|
||||
rmode,
|
||||
wmode,
|
||||
rclk,
|
||||
rst_R_n,
|
||||
wclk,
|
||||
rst_W_n,
|
||||
ren,
|
||||
wen,
|
||||
upaf,
|
||||
upae
|
||||
);
|
||||
parameter ADDR_WIDTH = 11;
|
||||
parameter FIFO_WIDTH = 3'd2;
|
||||
parameter DEPTH = 6;
|
||||
output wire [ADDR_WIDTH - 1:0] raddr;
|
||||
output wire [ADDR_WIDTH - 1:0] waddr;
|
||||
output wire [7:0] fflags;
|
||||
output wire ren_o;
|
||||
input wire sync;
|
||||
input wire [1:0] rmode;
|
||||
input wire [1:0] wmode;
|
||||
(* clkbuf_sink *)
|
||||
input wire rclk;
|
||||
input wire rst_R_n;
|
||||
(* clkbuf_sink *)
|
||||
input wire wclk;
|
||||
input wire rst_W_n;
|
||||
input wire ren;
|
||||
input wire wen;
|
||||
input wire [ADDR_WIDTH - 1:0] upaf;
|
||||
input wire [ADDR_WIDTH - 1:0] upae;
|
||||
localparam ADDR_PLUS_ONE = ADDR_WIDTH + 1;
|
||||
reg [ADDR_WIDTH:0] pushtopop1;
|
||||
reg [ADDR_WIDTH:0] pushtopop2;
|
||||
reg [ADDR_WIDTH:0] poptopush1;
|
||||
reg [ADDR_WIDTH:0] poptopush2;
|
||||
wire [ADDR_WIDTH:0] pushtopop0;
|
||||
wire [ADDR_WIDTH:0] poptopush0;
|
||||
wire [ADDR_WIDTH:0] smux_poptopush;
|
||||
wire [ADDR_WIDTH:0] smux_pushtopop;
|
||||
assign smux_poptopush = (sync ? poptopush0 : poptopush2);
|
||||
assign smux_pushtopop = (sync ? pushtopop0 : pushtopop2);
|
||||
always @(posedge rclk or negedge rst_R_n)
|
||||
if (~rst_R_n) begin
|
||||
pushtopop1 <= 'h0;
|
||||
pushtopop2 <= 'h0;
|
||||
end
|
||||
else begin
|
||||
pushtopop1 = pushtopop0;
|
||||
pushtopop2 = pushtopop1;
|
||||
end
|
||||
always @(posedge wclk or negedge rst_W_n)
|
||||
if (~rst_W_n) begin
|
||||
poptopush1 <= 'h0;
|
||||
poptopush2 <= 'h0;
|
||||
end
|
||||
else begin
|
||||
poptopush1 <= poptopush0;
|
||||
poptopush2 <= poptopush1;
|
||||
end
|
||||
fifo_push #(
|
||||
.ADDR_WIDTH(ADDR_WIDTH),
|
||||
.DEPTH(DEPTH)
|
||||
) u_fifo_push(
|
||||
.wclk(wclk),
|
||||
.wen(wen),
|
||||
.rst_n(rst_W_n),
|
||||
.rmode(rmode),
|
||||
.wmode(wmode),
|
||||
.gcout(pushtopop0),
|
||||
.gcin(smux_poptopush),
|
||||
.ff_waddr(waddr),
|
||||
.pushflags(fflags[7:4]),
|
||||
.upaf(upaf)
|
||||
);
|
||||
fifo_pop #(
|
||||
.ADDR_WIDTH(ADDR_WIDTH),
|
||||
.FIFO_WIDTH(FIFO_WIDTH),
|
||||
.DEPTH(DEPTH)
|
||||
) u_fifo_pop(
|
||||
.rclk(rclk),
|
||||
.ren_in(ren),
|
||||
.rst_n(rst_R_n),
|
||||
.rmode(rmode),
|
||||
.wmode(wmode),
|
||||
.ren_o(ren_o),
|
||||
.gcout(poptopush0),
|
||||
.gcin(smux_pushtopop),
|
||||
.out_raddr(raddr),
|
||||
.popflags(fflags[3:0]),
|
||||
.upae(upae)
|
||||
);
|
||||
endmodule
|
||||
module fifo_push (
|
||||
pushflags,
|
||||
gcout,
|
||||
ff_waddr,
|
||||
rst_n,
|
||||
wclk,
|
||||
wen,
|
||||
rmode,
|
||||
wmode,
|
||||
gcin,
|
||||
upaf
|
||||
);
|
||||
parameter ADDR_WIDTH = 11;
|
||||
parameter DEPTH = 6;
|
||||
output wire [3:0] pushflags;
|
||||
output wire [ADDR_WIDTH:0] gcout;
|
||||
output wire [ADDR_WIDTH - 1:0] ff_waddr;
|
||||
input rst_n;
|
||||
(* clkbuf_sink *)
|
||||
input wclk;
|
||||
input wen;
|
||||
input [1:0] rmode;
|
||||
input [1:0] wmode;
|
||||
input [ADDR_WIDTH:0] gcin;
|
||||
input [ADDR_WIDTH - 1:0] upaf;
|
||||
localparam ADDR_PLUS_ONE = ADDR_WIDTH + 1;
|
||||
reg full_next;
|
||||
reg full;
|
||||
reg paf_next;
|
||||
reg paf;
|
||||
reg fmo;
|
||||
reg fmo_next;
|
||||
reg overflow;
|
||||
reg p1;
|
||||
reg p2;
|
||||
reg f1;
|
||||
reg f2;
|
||||
reg q1;
|
||||
reg q2;
|
||||
reg [1:0] gmode;
|
||||
reg [ADDR_WIDTH:0] waddr;
|
||||
reg [ADDR_WIDTH:0] raddr;
|
||||
reg [ADDR_WIDTH:0] gcout_reg;
|
||||
reg [ADDR_WIDTH:0] gcout_next;
|
||||
reg [ADDR_WIDTH:0] raddr_next;
|
||||
reg [ADDR_WIDTH - 1:0] paf_thresh;
|
||||
wire overflow_next;
|
||||
wire [ADDR_WIDTH:0] waddr_next;
|
||||
wire [ADDR_WIDTH:0] gc8out_next;
|
||||
wire [ADDR_WIDTH - 1:0] gc16out_next;
|
||||
wire [ADDR_WIDTH - 2:0] gc32out_next;
|
||||
wire [ADDR_WIDTH:0] tmp;
|
||||
wire [ADDR_WIDTH:0] next_count;
|
||||
wire [ADDR_WIDTH:0] count;
|
||||
wire [ADDR_WIDTH:0] fbytes;
|
||||
genvar i;
|
||||
assign next_count = fbytes - (waddr_next >= raddr_next ? waddr_next - raddr_next : (~raddr_next + waddr_next) + 1);
|
||||
assign count = fbytes - (waddr >= raddr ? waddr - raddr : (~raddr + waddr) + 1);
|
||||
assign fbytes = 1 << (DEPTH + 5);
|
||||
always @(*) begin
|
||||
paf_thresh = wmode[1] ? upaf : (wmode[0] ? upaf << 1 : upaf << 2);
|
||||
end
|
||||
always @(*)
|
||||
case (wmode)
|
||||
2'h0, 2'h1, 2'h2: begin
|
||||
full_next = (wen ? f1 : f2);
|
||||
fmo_next = (wen ? p1 : p2);
|
||||
paf_next = (wen ? q1 : q2);
|
||||
end
|
||||
default: begin
|
||||
full_next = 1'b0;
|
||||
fmo_next = 1'b0;
|
||||
paf_next = 1'b0;
|
||||
end
|
||||
endcase
|
||||
always @(*) begin : PUSH_FULL_FLAGS
|
||||
f1 = 1'b0;
|
||||
f2 = 1'b0;
|
||||
p1 = 1'b0;
|
||||
p2 = 1'b0;
|
||||
q1 = next_count < {1'b0, paf_thresh};
|
||||
q2 = count < {1'b0, paf_thresh};
|
||||
case (wmode)
|
||||
2'h0:
|
||||
case (DEPTH)
|
||||
3'h6: begin
|
||||
f1 = {~waddr_next[11], waddr_next[10:2]} == raddr_next[11:2];
|
||||
f2 = {~waddr[11], waddr[10:2]} == raddr_next[11:2];
|
||||
p1 = ((waddr_next[10:2] + 1) & 9'h1ff) == raddr_next[10:2];
|
||||
p2 = ((waddr[10:2] + 1) & 9'h1ff) == raddr_next[10:2];
|
||||
end
|
||||
3'h5: begin
|
||||
f1 = {~waddr_next[10], waddr_next[9:2]} == raddr_next[10:2];
|
||||
f2 = {~waddr[10], waddr[9:2]} == raddr_next[10:2];
|
||||
p1 = ((waddr_next[9:2] + 1) & 8'hff) == raddr_next[9:2];
|
||||
p2 = ((waddr[9:2] + 1) & 8'hff) == raddr_next[9:2];
|
||||
end
|
||||
3'h4: begin
|
||||
f1 = {~waddr_next[9], waddr_next[8:2]} == raddr_next[9:2];
|
||||
f2 = {~waddr[9], waddr[8:2]} == raddr_next[9:2];
|
||||
p1 = ((waddr_next[8:2] + 1) & 7'h7f) == raddr_next[8:2];
|
||||
p2 = ((waddr[8:2] + 1) & 7'h7f) == raddr_next[8:2];
|
||||
end
|
||||
3'h3: begin
|
||||
f1 = {~waddr_next[8], waddr_next[7:2]} == raddr_next[8:2];
|
||||
f2 = {~waddr[8], waddr[7:2]} == raddr_next[8:2];
|
||||
p1 = ((waddr_next[7:2] + 1) & 6'h3f) == raddr_next[7:2];
|
||||
p2 = ((waddr[7:2] + 1) & 6'h3f) == raddr_next[7:2];
|
||||
end
|
||||
3'h2: begin
|
||||
f1 = {~waddr_next[7], waddr_next[6:2]} == raddr_next[7:2];
|
||||
f2 = {~waddr[7], waddr[6:2]} == raddr_next[7:2];
|
||||
p1 = ((waddr_next[6:2] + 1) & 5'h1f) == raddr_next[6:2];
|
||||
p2 = ((waddr[6:2] + 1) & 5'h1f) == raddr_next[6:2];
|
||||
end
|
||||
3'h1: begin
|
||||
f1 = {~waddr_next[6], waddr_next[5:2]} == raddr_next[6:2];
|
||||
f2 = {~waddr[6], waddr[5:2]} == raddr_next[6:2];
|
||||
p1 = ((waddr_next[5:2] + 1) & 4'hf) == raddr_next[5:2];
|
||||
p2 = ((waddr[5:2] + 1) & 4'hf) == raddr_next[5:2];
|
||||
end
|
||||
3'h0: begin
|
||||
f1 = {~waddr_next[5], waddr_next[4:2]} == raddr_next[5:2];
|
||||
f2 = {~waddr[5], waddr[4:2]} == raddr_next[5:2];
|
||||
p1 = ((waddr_next[4:2] + 1) & 3'h7) == raddr_next[4:2];
|
||||
p2 = ((waddr[4:2] + 1) & 3'h7) == raddr_next[4:2];
|
||||
end
|
||||
3'h7: begin
|
||||
f1 = {~waddr_next[ADDR_WIDTH], waddr_next[ADDR_WIDTH - 1:2]} == raddr_next[ADDR_WIDTH:2];
|
||||
f2 = {~waddr[ADDR_WIDTH], waddr[ADDR_WIDTH - 1:2]} == raddr_next[ADDR_WIDTH:2];
|
||||
p1 = ((waddr_next[ADDR_WIDTH - 1:2] + 1) & {ADDR_WIDTH - 2 {1'b1}}) == raddr_next[ADDR_WIDTH - 1:2];
|
||||
p2 = ((waddr[ADDR_WIDTH - 1:2] + 1) & {ADDR_WIDTH - 2 {1'b1}}) == raddr_next[ADDR_WIDTH - 1:2];
|
||||
end
|
||||
endcase
|
||||
2'h1:
|
||||
case (DEPTH)
|
||||
3'h6: begin
|
||||
f1 = {~waddr_next[11], waddr_next[10:1]} == raddr_next[11:1];
|
||||
f2 = {~waddr[11], waddr[10:1]} == raddr_next[11:1];
|
||||
p1 = ((waddr_next[10:1] + 1) & 10'h3ff) == raddr_next[10:1];
|
||||
p2 = ((waddr[10:1] + 1) & 10'h3ff) == raddr_next[10:1];
|
||||
end
|
||||
3'h5: begin
|
||||
f1 = {~waddr_next[10], waddr_next[9:1]} == raddr_next[10:1];
|
||||
f2 = {~waddr[10], waddr[9:1]} == raddr_next[10:1];
|
||||
p1 = ((waddr_next[9:1] + 1) & 9'h1ff) == raddr_next[9:1];
|
||||
p2 = ((waddr[9:1] + 1) & 9'h1ff) == raddr_next[9:1];
|
||||
end
|
||||
3'h4: begin
|
||||
f1 = {~waddr_next[9], waddr_next[8:1]} == raddr_next[9:1];
|
||||
f2 = {~waddr[9], waddr[8:1]} == raddr_next[9:1];
|
||||
p1 = ((waddr_next[8:1] + 1) & 8'hff) == raddr_next[8:1];
|
||||
p2 = ((waddr[8:1] + 1) & 8'hff) == raddr_next[8:1];
|
||||
end
|
||||
3'h3: begin
|
||||
f1 = {~waddr_next[8], waddr_next[7:1]} == raddr_next[8:1];
|
||||
f2 = {~waddr[8], waddr[7:1]} == raddr_next[8:1];
|
||||
p1 = ((waddr_next[7:1] + 1) & 7'h7f) == raddr_next[7:1];
|
||||
p2 = ((waddr[7:1] + 1) & 7'h7f) == raddr_next[7:1];
|
||||
end
|
||||
3'h2: begin
|
||||
f1 = {~waddr_next[7], waddr_next[6:1]} == raddr_next[7:1];
|
||||
f2 = {~waddr[7], waddr[6:1]} == raddr_next[7:1];
|
||||
p1 = ((waddr_next[6:1] + 1) & 6'h3f) == raddr_next[6:1];
|
||||
p2 = ((waddr[6:1] + 1) & 6'h3f) == raddr_next[6:1];
|
||||
end
|
||||
3'h1: begin
|
||||
f1 = {~waddr_next[6], waddr_next[5:1]} == raddr_next[6:1];
|
||||
f2 = {~waddr[6], waddr[5:1]} == raddr_next[6:1];
|
||||
p1 = ((waddr_next[5:1] + 1) & 5'h1f) == raddr_next[5:1];
|
||||
p2 = ((waddr[5:1] + 1) & 5'h1f) == raddr_next[5:1];
|
||||
end
|
||||
3'h0: begin
|
||||
f1 = {~waddr_next[5], waddr_next[4:1]} == raddr_next[5:1];
|
||||
f2 = {~waddr[5], waddr[4:1]} == raddr_next[5:1];
|
||||
p1 = ((waddr_next[4:1] + 1) & 4'hf) == raddr_next[4:1];
|
||||
p2 = ((waddr[4:1] + 1) & 4'hf) == raddr_next[4:1];
|
||||
end
|
||||
3'h7: begin
|
||||
f1 = {~waddr_next[ADDR_WIDTH], waddr_next[ADDR_WIDTH - 1:1]} == raddr_next[ADDR_WIDTH:1];
|
||||
f2 = {~waddr[ADDR_WIDTH], waddr[ADDR_WIDTH - 1:1]} == raddr_next[ADDR_WIDTH:1];
|
||||
p1 = ((waddr_next[ADDR_WIDTH - 1:1] + 1) & {ADDR_WIDTH - 1 {1'b1}}) == raddr_next[ADDR_WIDTH - 1:1];
|
||||
p2 = ((waddr[ADDR_WIDTH - 1:1] + 1) & {ADDR_WIDTH - 1 {1'b1}}) == raddr_next[ADDR_WIDTH - 1:1];
|
||||
end
|
||||
endcase
|
||||
2'h2:
|
||||
case (DEPTH)
|
||||
3'h6: begin
|
||||
f1 = {~waddr_next[11], waddr_next[10:0]} == raddr_next[11:0];
|
||||
f2 = {~waddr[11], waddr[10:0]} == raddr_next[11:0];
|
||||
p1 = ((waddr_next[10:0] + 1) & 11'h7ff) == raddr_next[10:0];
|
||||
p2 = ((waddr[10:0] + 1) & 11'h7ff) == raddr_next[10:0];
|
||||
end
|
||||
3'h5: begin
|
||||
f1 = {~waddr_next[10], waddr_next[9:0]} == raddr_next[10:0];
|
||||
f2 = {~waddr[10], waddr[9:0]} == raddr_next[10:0];
|
||||
p1 = ((waddr_next[9:0] + 1) & 10'h3ff) == raddr_next[9:0];
|
||||
p2 = ((waddr[9:0] + 1) & 10'h3ff) == raddr_next[9:0];
|
||||
end
|
||||
3'h4: begin
|
||||
f1 = {~waddr_next[9], waddr_next[8:0]} == raddr_next[9:0];
|
||||
f2 = {~waddr[9], waddr[8:0]} == raddr_next[9:0];
|
||||
p1 = ((waddr_next[8:0] + 1) & 9'h1ff) == raddr_next[8:0];
|
||||
p2 = ((waddr[8:0] + 1) & 9'h1ff) == raddr_next[8:0];
|
||||
end
|
||||
3'h3: begin
|
||||
f1 = {~waddr_next[8], waddr_next[7:0]} == raddr_next[8:0];
|
||||
f2 = {~waddr[8], waddr[7:0]} == raddr_next[8:0];
|
||||
p1 = ((waddr_next[7:0] + 1) & 8'hff) == raddr_next[7:0];
|
||||
p2 = ((waddr[7:0] + 1) & 8'hff) == raddr_next[7:0];
|
||||
end
|
||||
3'h2: begin
|
||||
f1 = {~waddr_next[7], waddr_next[6:0]} == raddr_next[7:0];
|
||||
f2 = {~waddr[7], waddr[6:0]} == raddr_next[7:0];
|
||||
p1 = ((waddr_next[6:0] + 1) & 7'h7f) == raddr_next[6:0];
|
||||
p2 = ((waddr[6:0] + 1) & 7'h7f) == raddr_next[6:0];
|
||||
end
|
||||
3'h1: begin
|
||||
f1 = {~waddr_next[6], waddr_next[5:0]} == raddr_next[6:0];
|
||||
f2 = {~waddr[6], waddr[5:0]} == raddr_next[6:0];
|
||||
p1 = ((waddr_next[5:0] + 1) & 6'h3f) == raddr_next[5:0];
|
||||
p2 = ((waddr[5:0] + 1) & 6'h3f) == raddr_next[5:0];
|
||||
end
|
||||
3'h0: begin
|
||||
f1 = {~waddr_next[5], waddr_next[4:0]} == raddr_next[5:0];
|
||||
f2 = {~waddr[5], waddr[4:0]} == raddr_next[5:0];
|
||||
p1 = ((waddr_next[4:0] + 1) & 5'h1f) == raddr_next[4:0];
|
||||
p2 = ((waddr[4:0] + 1) & 5'h1f) == raddr_next[4:0];
|
||||
end
|
||||
3'h7: begin
|
||||
f1 = {~waddr_next[ADDR_WIDTH], waddr_next[ADDR_WIDTH - 1:0]} == raddr_next[ADDR_WIDTH:0];
|
||||
f2 = {~waddr[ADDR_WIDTH], waddr[ADDR_WIDTH - 1:0]} == raddr_next[ADDR_WIDTH:0];
|
||||
p1 = ((waddr_next[ADDR_WIDTH - 1:0] + 1) & {ADDR_WIDTH {1'b1}}) == raddr_next[ADDR_WIDTH - 1:0];
|
||||
p2 = ((waddr[ADDR_WIDTH - 1:0] + 1) & {ADDR_WIDTH {1'b1}}) == raddr_next[ADDR_WIDTH - 1:0];
|
||||
end
|
||||
endcase
|
||||
2'h3: begin
|
||||
f1 = 1'b0;
|
||||
f2 = 1'b0;
|
||||
p1 = 1'b0;
|
||||
p2 = 1'b0;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
always @(*)
|
||||
case (wmode)
|
||||
2'h0: gmode = 2'h0;
|
||||
2'h1: gmode = (rmode == 2'h0 ? 2'h0 : 2'h1);
|
||||
2'h2: gmode = (rmode == 2'h2 ? 2'h2 : rmode);
|
||||
2'h3: gmode = 2'h3;
|
||||
endcase
|
||||
assign gc8out_next = (waddr_next >> 1) ^ waddr_next;
|
||||
assign gc16out_next = (waddr_next >> 2) ^ (waddr_next >> 1);
|
||||
assign gc32out_next = (waddr_next >> 3) ^ (waddr_next >> 2);
|
||||
always @(*)
|
||||
if (wen)
|
||||
case (gmode)
|
||||
2'h2: gcout_next = gc8out_next;
|
||||
2'h1: gcout_next = {1'b0, gc16out_next};
|
||||
2'h0: gcout_next = {2'b00, gc32out_next};
|
||||
default: gcout_next = {ADDR_PLUS_ONE {1'b0}};
|
||||
endcase
|
||||
else
|
||||
gcout_next = {ADDR_PLUS_ONE {1'b0}};
|
||||
always @(posedge wclk or negedge rst_n)
|
||||
if (~rst_n) begin
|
||||
full <= 1'b0;
|
||||
fmo <= 1'b0;
|
||||
paf <= 1'b0;
|
||||
raddr <= {ADDR_PLUS_ONE {1'b0}};
|
||||
end
|
||||
else begin
|
||||
full <= full_next;
|
||||
fmo <= fmo_next;
|
||||
paf <= paf_next;
|
||||
case (gmode)
|
||||
0: raddr <= raddr_next & {{ADDR_WIDTH - 1 {1'b1}}, 2'b00};
|
||||
1: raddr <= raddr_next & {{ADDR_WIDTH {1'b1}}, 1'b0};
|
||||
2: raddr <= raddr_next & {ADDR_WIDTH + 1 {1'b1}};
|
||||
3: raddr <= 12'h000;
|
||||
endcase
|
||||
end
|
||||
assign overflow_next = full & wen;
|
||||
always @(posedge wclk or negedge rst_n)
|
||||
if (~rst_n)
|
||||
overflow <= 1'b0;
|
||||
else if (wen == 1'b1)
|
||||
overflow <= overflow_next;
|
||||
always @(posedge wclk or negedge rst_n)
|
||||
if (~rst_n) begin
|
||||
waddr <= {ADDR_WIDTH + 1 {1'b0}};
|
||||
gcout_reg <= {ADDR_WIDTH + 1 {1'b0}};
|
||||
end
|
||||
else if (wen == 1'b1) begin
|
||||
waddr <= waddr_next;
|
||||
gcout_reg <= gcout_next;
|
||||
end
|
||||
assign gcout = gcout_reg;
|
||||
generate
|
||||
for (i = 0; i < (ADDR_WIDTH + 1); i = i + 1) begin : genblk1
|
||||
assign tmp[i] = ^(gcin >> i);
|
||||
end
|
||||
endgenerate
|
||||
always @(*)
|
||||
case (gmode)
|
||||
2'h0: raddr_next = {tmp[ADDR_WIDTH - 2:0], 2'b00} & {{ADDR_WIDTH - 1 {1'b1}}, 2'b00};
|
||||
2'h1: raddr_next = {tmp[ADDR_WIDTH - 1:0], 1'b0} & {{ADDR_WIDTH {1'b1}}, 1'b0};
|
||||
2'h2: raddr_next = {tmp[ADDR_WIDTH:0]} & {ADDR_WIDTH + 1 {1'b1}};
|
||||
default: raddr_next = {ADDR_WIDTH + 1 {1'b0}};
|
||||
endcase
|
||||
assign ff_waddr = waddr[ADDR_WIDTH - 1:0];
|
||||
assign pushflags = {full, fmo, paf, overflow};
|
||||
assign waddr_next = waddr + (wmode == 2'h0 ? 'h4 : (wmode == 2'h1 ? 'h2 : 'h1));
|
||||
endmodule
|
||||
module fifo_pop (
|
||||
ren_o,
|
||||
popflags,
|
||||
out_raddr,
|
||||
gcout,
|
||||
rst_n,
|
||||
rclk,
|
||||
ren_in,
|
||||
rmode,
|
||||
wmode,
|
||||
gcin,
|
||||
upae
|
||||
);
|
||||
parameter ADDR_WIDTH = 11;
|
||||
parameter FIFO_WIDTH = 3'd2;
|
||||
parameter DEPTH = 6;
|
||||
output wire ren_o;
|
||||
output wire [3:0] popflags;
|
||||
output reg [ADDR_WIDTH - 1:0] out_raddr;
|
||||
output wire [ADDR_WIDTH:0] gcout;
|
||||
input rst_n;
|
||||
(* clkbuf_sink *)
|
||||
input rclk;
|
||||
input ren_in;
|
||||
input [1:0] rmode;
|
||||
input [1:0] wmode;
|
||||
input [ADDR_WIDTH:0] gcin;
|
||||
input [ADDR_WIDTH - 1:0] upae;
|
||||
localparam ADDR_PLUS_ONE = ADDR_WIDTH + 1;
|
||||
reg empty;
|
||||
reg epo;
|
||||
reg pae;
|
||||
reg underflow;
|
||||
reg e1;
|
||||
reg e2;
|
||||
reg o1;
|
||||
reg o2;
|
||||
reg q1;
|
||||
reg q2;
|
||||
reg [1:0] bwl_sel;
|
||||
reg [1:0] gmode;
|
||||
reg [ADDR_WIDTH - 1:0] ff_raddr;
|
||||
reg [ADDR_WIDTH:0] waddr;
|
||||
reg [ADDR_WIDTH:0] raddr;
|
||||
reg [ADDR_WIDTH:0] gcout_reg;
|
||||
reg [ADDR_WIDTH:0] gcout_next;
|
||||
reg [ADDR_WIDTH:0] waddr_next;
|
||||
reg [ADDR_WIDTH - 1:0] pae_thresh;
|
||||
wire ren_out;
|
||||
wire empty_next;
|
||||
wire pae_next;
|
||||
wire epo_next;
|
||||
wire [ADDR_WIDTH - 2:0] gc32out_next;
|
||||
wire [ADDR_WIDTH - 1:0] gc16out_next;
|
||||
wire [ADDR_WIDTH:0] gc8out_next;
|
||||
wire [ADDR_WIDTH:0] raddr_next;
|
||||
wire [ADDR_WIDTH - 1:0] ff_raddr_next;
|
||||
wire [ADDR_WIDTH:0] tmp;
|
||||
wire [ADDR_PLUS_ONE:0] next_count;
|
||||
wire [ADDR_PLUS_ONE:0] count;
|
||||
wire [ADDR_PLUS_ONE:0] fbytes;
|
||||
genvar i;
|
||||
assign next_count = waddr - raddr_next;
|
||||
assign count = waddr - raddr;
|
||||
assign fbytes = 1 << (DEPTH + 5);
|
||||
always @(*) pae_thresh = rmode[1] ? upae : (rmode[0] ? upae << 1 : upae << 2);
|
||||
assign ren_out = (empty ? 1'b1 : ren_in);
|
||||
always @(*)
|
||||
case (rmode)
|
||||
2'h0: gmode = 2'h0;
|
||||
2'h1: gmode = (wmode == 2'h0 ? 2'h0 : 2'h1);
|
||||
2'h2: gmode = (wmode == 2'h2 ? 2'h2 : wmode);
|
||||
2'h3: gmode = 2'h3;
|
||||
endcase
|
||||
always @(*) begin
|
||||
e1 = 1'b0;
|
||||
e2 = 1'b0;
|
||||
o1 = 1'b0;
|
||||
o2 = 1'b0;
|
||||
q1 = next_count < {1'b0, pae_thresh};
|
||||
q2 = count < {1'b0, pae_thresh};
|
||||
case (rmode)
|
||||
2'h0: begin
|
||||
e1 = raddr_next[ADDR_WIDTH:2] == waddr_next[ADDR_WIDTH:2];
|
||||
e2 = raddr[ADDR_WIDTH:2] == waddr_next[ADDR_WIDTH:2];
|
||||
o1 = (raddr_next[ADDR_WIDTH:2] + 1) == waddr_next[ADDR_WIDTH:2];
|
||||
o2 = (raddr[ADDR_WIDTH:2] + 1) == waddr_next[ADDR_WIDTH:2];
|
||||
end
|
||||
2'h1: begin
|
||||
e1 = raddr_next[ADDR_WIDTH:1] == waddr_next[ADDR_WIDTH:1];
|
||||
e2 = raddr[ADDR_WIDTH:1] == waddr_next[ADDR_WIDTH:1];
|
||||
o1 = (raddr_next[ADDR_WIDTH:1] + 1) == waddr_next[ADDR_WIDTH:1];
|
||||
o2 = (raddr[ADDR_WIDTH:1] + 1) == waddr_next[ADDR_WIDTH:1];
|
||||
end
|
||||
2'h2: begin
|
||||
e1 = raddr_next[ADDR_WIDTH:0] == waddr_next[ADDR_WIDTH:0];
|
||||
e2 = raddr[ADDR_WIDTH:0] == waddr_next[ADDR_WIDTH:0];
|
||||
o1 = (raddr_next[ADDR_WIDTH:0] + 1) == waddr_next[ADDR_WIDTH:0];
|
||||
o2 = (raddr[ADDR_WIDTH:0] + 1) == waddr_next[11:0];
|
||||
end
|
||||
2'h3: begin
|
||||
e1 = 1'b0;
|
||||
e2 = 1'b0;
|
||||
o1 = 1'b0;
|
||||
o2 = 1'b0;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
assign empty_next = (ren_in & !empty ? e1 : e2);
|
||||
assign epo_next = (ren_in & !empty ? o1 : o2);
|
||||
assign pae_next = (ren_in & !empty ? q1 : q2);
|
||||
always @(posedge rclk or negedge rst_n)
|
||||
if (~rst_n) begin
|
||||
empty <= 1'b1;
|
||||
pae <= 1'b1;
|
||||
epo <= 1'b0;
|
||||
end
|
||||
else begin
|
||||
empty <= empty_next;
|
||||
pae <= pae_next;
|
||||
epo <= epo_next;
|
||||
end
|
||||
assign gc8out_next = (raddr_next >> 1) ^ raddr_next;
|
||||
assign gc16out_next = (raddr_next >> 2) ^ (raddr_next >> 1);
|
||||
assign gc32out_next = (raddr_next >> 3) ^ (raddr_next >> 2);
|
||||
always @(*)
|
||||
if (ren_in)
|
||||
case (gmode)
|
||||
2'h2: gcout_next = gc8out_next;
|
||||
2'h1: gcout_next = {1'b0, gc16out_next};
|
||||
2'h0: gcout_next = {2'b00, gc32out_next};
|
||||
default: gcout_next = 'h0;
|
||||
endcase
|
||||
else
|
||||
gcout_next = 'h0;
|
||||
always @(posedge rclk or negedge rst_n)
|
||||
if (~rst_n)
|
||||
waddr <= 12'h000;
|
||||
else
|
||||
waddr <= waddr_next;
|
||||
always @(posedge rclk or negedge rst_n)
|
||||
if (~rst_n) begin
|
||||
underflow <= 1'b0;
|
||||
bwl_sel <= 2'h0;
|
||||
gcout_reg <= 12'h000;
|
||||
end
|
||||
else if (ren_in) begin
|
||||
underflow <= empty;
|
||||
if (!empty) begin
|
||||
bwl_sel <= raddr_next[1:0];
|
||||
gcout_reg <= gcout_next;
|
||||
end
|
||||
end
|
||||
generate
|
||||
for (i = 0; i < (ADDR_WIDTH + 1); i = i + 1) begin : genblk1
|
||||
assign tmp[i] = ^(gcin >> i);
|
||||
end
|
||||
endgenerate
|
||||
always @(*)
|
||||
case (gmode)
|
||||
2'h0: waddr_next = {tmp[ADDR_WIDTH - 2:0], 2'b00} & {{ADDR_WIDTH - 1 {1'b1}}, 2'b00};
|
||||
2'h1: waddr_next = {tmp[ADDR_WIDTH - 1:0], 1'b0} & {{ADDR_WIDTH {1'b1}}, 1'b0};
|
||||
2'h2: waddr_next = {tmp[ADDR_WIDTH:0]} & {ADDR_PLUS_ONE {1'b1}};
|
||||
default: waddr_next = {ADDR_PLUS_ONE {1'b0}};
|
||||
endcase
|
||||
assign ff_raddr_next = ff_raddr + (rmode == 2'h0 ? 'h4 : (rmode == 2'h1 ? 'h2 : 'h1));
|
||||
assign raddr_next = raddr + (rmode == 2'h0 ? 'h4 : (rmode == 2'h1 ? 'h2 : 'h1));
|
||||
always @(posedge rclk or negedge rst_n)
|
||||
if (~rst_n)
|
||||
ff_raddr <= 1'sb0;
|
||||
else if (empty & ~empty_next)
|
||||
ff_raddr <= raddr_next[ADDR_WIDTH - 1:0];
|
||||
else if ((ren_in & !empty) & ~empty_next)
|
||||
ff_raddr <= ff_raddr_next;
|
||||
always @(posedge rclk or negedge rst_n)
|
||||
if (~rst_n)
|
||||
raddr <= 12'h000;
|
||||
else if (ren_in & !empty)
|
||||
raddr <= raddr_next;
|
||||
always @(*)
|
||||
case (FIFO_WIDTH)
|
||||
3'h2: out_raddr = {ff_raddr[ADDR_WIDTH - 1:1], bwl_sel[0]};
|
||||
3'h4: out_raddr = {ff_raddr[ADDR_WIDTH - 1:2], bwl_sel};
|
||||
default: out_raddr = ff_raddr[ADDR_WIDTH - 1:0];
|
||||
endcase
|
||||
assign ren_o = ren_out;
|
||||
assign gcout = gcout_reg;
|
||||
assign popflags = {empty, epo, pae, underflow};
|
||||
endmodule
|
||||
`default_nettype none
|
|
@ -2,6 +2,7 @@
|
|||
* yosys -- Yosys Open SYnthesis Suite
|
||||
*
|
||||
* Copyright (C) 2021 QuickLogic Corp.
|
||||
* Copyright 2020-2022 F4PGA Authors
|
||||
*
|
||||
* Permission to use, copy, modify, and/or distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
|
@ -30,6 +31,7 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
|
||||
void help() override
|
||||
{
|
||||
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
|
||||
log("\n");
|
||||
log(" synth_quicklogic [options]\n");
|
||||
log("This command runs synthesis for QuickLogic FPGAs\n");
|
||||
|
@ -42,6 +44,21 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
log(" generate the synthesis netlist for the specified family.\n");
|
||||
log(" supported values:\n");
|
||||
log(" - pp3: PolarPro 3 \n");
|
||||
log(" - qlf_k6n10f: K6N10f\n");
|
||||
log("\n");
|
||||
log(" -nodsp\n");
|
||||
log(" do not use dsp_t1_* to implement multipliers and associated logic\n");
|
||||
log(" (qlf_k6n10f only).\n");
|
||||
log("\n");
|
||||
log(" -nocarry\n");
|
||||
log(" do not use adder_carry cells in output netlist.\n");
|
||||
log("\n");
|
||||
log(" -nobram\n");
|
||||
log(" do not use block RAM cells in output netlist.\n");
|
||||
log("\n");
|
||||
log(" -bramtypes\n");
|
||||
log(" Emit specialized BRAM cells for particular address and data width\n");
|
||||
log(" configurations.\n");
|
||||
log("\n");
|
||||
log(" -blif <file>\n");
|
||||
log(" write the design to the specified BLIF file. writing of an output file\n");
|
||||
|
@ -60,27 +77,51 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
log("\n");
|
||||
}
|
||||
|
||||
string top_opt, blif_file, family, currmodule, verilog_file;
|
||||
bool abc9;
|
||||
string top_opt, blif_file, edif_file, family, currmodule, verilog_file, lib_path;
|
||||
bool abc9, inferAdder, nobram, bramTypes, dsp;
|
||||
|
||||
void clear_flags() override
|
||||
{
|
||||
top_opt = "-auto-top";
|
||||
blif_file = "";
|
||||
edif_file = "";
|
||||
verilog_file = "";
|
||||
currmodule = "";
|
||||
family = "pp3";
|
||||
abc9 = true;
|
||||
inferAdder = true;
|
||||
nobram = false;
|
||||
bramTypes = false;
|
||||
lib_path = "+/quicklogic/";
|
||||
dsp = true;
|
||||
}
|
||||
|
||||
void set_scratchpad_defaults(RTLIL::Design *design) {
|
||||
lib_path = design->scratchpad_get_string("ql.lib_path", lib_path);
|
||||
if (lib_path.back() != '/')
|
||||
lib_path += "/";
|
||||
inferAdder = !design->scratchpad_get_bool("ql.nocarry", false);
|
||||
nobram = design->scratchpad_get_bool("ql.nobram", false);
|
||||
bramTypes = design->scratchpad_get_bool("ql.bramtypes", false);
|
||||
}
|
||||
|
||||
void execute(std::vector<std::string> args, RTLIL::Design *design) override
|
||||
{
|
||||
string run_from, run_to;
|
||||
clear_flags();
|
||||
set_scratchpad_defaults(design);
|
||||
|
||||
size_t argidx;
|
||||
for (argidx = 1; argidx < args.size(); argidx++)
|
||||
{
|
||||
if (args[argidx] == "-run" && argidx+1 < args.size()) {
|
||||
size_t pos = args[argidx+1].find(':');
|
||||
if (pos == std::string::npos)
|
||||
break;
|
||||
run_from = args[++argidx].substr(0, pos);
|
||||
run_to = args[argidx].substr(pos+1);
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-top" && argidx+1 < args.size()) {
|
||||
top_opt = "-top " + args[++argidx];
|
||||
continue;
|
||||
|
@ -101,6 +142,22 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
abc9 = false;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nocarry" || args[argidx] == "-no_adder") {
|
||||
inferAdder = false;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nobram" || args[argidx] == "-no_bram") {
|
||||
nobram = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-bramtypes" || args[argidx] == "-bram_types") {
|
||||
bramTypes = true;
|
||||
continue;
|
||||
}
|
||||
if (args[argidx] == "-nodsp" || args[argidx] == "-no_dsp") {
|
||||
dsp = false;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
extra_args(args, argidx, design);
|
||||
|
@ -108,7 +165,7 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
if (!design->full_selection())
|
||||
log_cmd_error("This command only operates on fully selected designs!\n");
|
||||
|
||||
if (family != "pp3")
|
||||
if (family != "pp3" && family != "qlf_k6n10f")
|
||||
log_cmd_error("Invalid family specified: '%s'\n", family.c_str());
|
||||
|
||||
if (abc9 && design->scratchpad_get_int("abc9.D", 0) == 0) {
|
||||
|
@ -126,16 +183,29 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
|
||||
void script() override
|
||||
{
|
||||
if (help_mode) {
|
||||
family = "<family>";
|
||||
}
|
||||
|
||||
if (check_label("begin")) {
|
||||
run(stringf("read_verilog -lib -specify +/quicklogic/cells_sim.v +/quicklogic/%s_cells_sim.v", family.c_str()));
|
||||
run("read_verilog -lib -specify +/quicklogic/lut_sim.v");
|
||||
std::string read_simlibs = stringf("read_verilog -lib -specify %scommon/cells_sim.v %s%s/cells_sim.v", lib_path.c_str(), lib_path.c_str(), family.c_str());
|
||||
if (family == "qlf_k6n10f") {
|
||||
read_simlibs += stringf(" %sqlf_k6n10f/brams_sim.v", lib_path.c_str());
|
||||
if (bramTypes)
|
||||
read_simlibs += stringf(" %sqlf_k6n10f/bram_types_sim.v", lib_path.c_str());
|
||||
if (dsp)
|
||||
read_simlibs += stringf(" %sqlf_k6n10f/dsp_sim.v", lib_path.c_str());
|
||||
}
|
||||
run(read_simlibs);
|
||||
run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
|
||||
}
|
||||
|
||||
if (check_label("coarse")) {
|
||||
if (check_label("prepare")) {
|
||||
run("proc");
|
||||
run("flatten");
|
||||
run("tribuf -logic");
|
||||
if (help_mode || family == "pp3") {
|
||||
run("tribuf -logic", " (for pp3)");
|
||||
}
|
||||
run("deminout");
|
||||
run("opt_expr");
|
||||
run("opt_clean");
|
||||
|
@ -147,6 +217,24 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
run("peepopt");
|
||||
run("opt_clean");
|
||||
run("share");
|
||||
}
|
||||
|
||||
if (check_label("map_dsp", "(for qlf_k6n10f, skip if -nodsp)")
|
||||
&& ((dsp && family == "qlf_k6n10f") || help_mode)) {
|
||||
run("wreduce t:$mul");
|
||||
run("ql_dsp_macc");
|
||||
|
||||
run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=20 -D DSP_B_MAXWIDTH=18 -D DSP_A_MINWIDTH=11 -D DSP_B_MINWIDTH=10 -D DSP_NAME=$__QL_MUL20X18");
|
||||
run("techmap -map +/mul2dsp.v -D DSP_A_MAXWIDTH=10 -D DSP_B_MAXWIDTH=9 -D DSP_A_MINWIDTH=4 -D DSP_B_MINWIDTH=4 -D DSP_NAME=$__QL_MUL10X9");
|
||||
run("chtype -set $mul t:$__soft_mul");
|
||||
|
||||
run("techmap -map " + lib_path + family + "/dsp_map.v -D USE_DSP_CFG_PARAMS=0");
|
||||
run("ql_dsp_simd");
|
||||
run("techmap -map " + lib_path + family + "/dsp_final_map.v");
|
||||
run("ql_dsp_io_regs");
|
||||
}
|
||||
|
||||
if (check_label("coarse")) {
|
||||
run("techmap -map +/cmp2lut.v -D LUT_WIDTH=4");
|
||||
run("opt_expr");
|
||||
run("opt_clean");
|
||||
|
@ -157,6 +245,17 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
run("opt_clean");
|
||||
}
|
||||
|
||||
if (check_label("map_bram", "(for qlf_k6n10f, skip if -no_bram)")
|
||||
&& (family == "qlf_k6n10f" || help_mode)) {
|
||||
run("memory_libmap -lib " + lib_path + family + "/libmap_brams.txt");
|
||||
run("ql_bram_merge");
|
||||
run("techmap -map " + lib_path + family + "/libmap_brams_map.v");
|
||||
run("techmap -autoproc -map " + lib_path + family + "/brams_map.v");
|
||||
|
||||
if (bramTypes || help_mode)
|
||||
run("ql_bram_types", "(if -bramtypes)");
|
||||
}
|
||||
|
||||
if (check_label("map_ffram")) {
|
||||
run("opt -fast -mux_undef -undriven -fine");
|
||||
run("memory_map -iattr -attr !ram_block -attr !rom_block -attr logic_block "
|
||||
|
@ -166,36 +265,67 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
}
|
||||
|
||||
if (check_label("map_gates")) {
|
||||
if (inferAdder && family == "qlf_k6n10f") {
|
||||
run("techmap -map +/techmap.v -map " + lib_path + family + "/arith_map.v", "(unless -no_adder)");
|
||||
} else {
|
||||
run("techmap");
|
||||
}
|
||||
run("opt -fast");
|
||||
run("muxcover -mux8 -mux4");
|
||||
if (help_mode || family == "pp3") {
|
||||
run("muxcover -mux8 -mux4", "(for pp3)");
|
||||
}
|
||||
}
|
||||
|
||||
if (check_label("map_ffs")) {
|
||||
run("opt_expr");
|
||||
if (help_mode) {
|
||||
run("shregmap -minlen <min> -maxlen <max>", "(for qlf_k6n10f)");
|
||||
run("dfflegalize -cell <supported FF types>");
|
||||
run("techmap -map " + lib_path + family + "/cells_map.v", "(for pp3)");
|
||||
run("techmap -map " + lib_path + family + "/ffs_map.v", "(for ql_k6n10f)");
|
||||
}
|
||||
if (family == "pp3") {
|
||||
run("dfflegalize -cell $_DFFSRE_PPPP_ 0 -cell $_DLATCH_?_ x");
|
||||
|
||||
run(stringf("techmap -map +/quicklogic/%s_cells_map.v -map +/quicklogic/%s_ffs_map.v", family.c_str(), family.c_str()));
|
||||
|
||||
run("techmap -map " + lib_path + family + "/cells_map.v -map " + lib_path + family + "/ffs_map.v");
|
||||
run("opt_expr -mux_undef");
|
||||
} else if (family == "qlf_k6n10f") {
|
||||
run("shregmap -minlen 8 -maxlen 20");
|
||||
// FIXME: Apparently dfflegalize leaves around $_DLATCH_[NP]_ even if
|
||||
// not in the allowed set. As a workaround we put them in the allowed
|
||||
// set explicitly and map them later to $_DLATCHSR_[NP]NN_.
|
||||
run("dfflegalize -cell $_DFFSRE_?NNP_ 0 -cell $_DLATCHSR_?NN_ 0 -cell $_DLATCH_?_ 0" " -cell $_SDFFE_?N?P_ 0");
|
||||
run("techmap -map " + lib_path + family + "/ffs_map.v");
|
||||
}
|
||||
run("opt");
|
||||
}
|
||||
|
||||
if (check_label("map_luts")) {
|
||||
run(stringf("techmap -map +/quicklogic/%s_latches_map.v", family.c_str()));
|
||||
if (check_label("map_luts", "(for pp3)") && (help_mode || family == "pp3")) {
|
||||
run("techmap -map " + lib_path + family + "/latches_map.v");
|
||||
if (abc9) {
|
||||
run("read_verilog -lib -specify -icells +/quicklogic/abc9_model.v");
|
||||
run("techmap -map +/quicklogic/abc9_map.v");
|
||||
run("read_verilog -lib -specify -icells " + lib_path + family + "/abc9_model.v");
|
||||
run("techmap -map " + lib_path + family + "/abc9_map.v");
|
||||
run("abc9 -maxlut 4 -dff");
|
||||
run("techmap -map +/quicklogic/abc9_unmap.v");
|
||||
run("techmap -map " + lib_path + family + "/abc9_unmap.v");
|
||||
} else {
|
||||
run("abc -luts 1,2,2,4 -dress");
|
||||
}
|
||||
run("clean");
|
||||
}
|
||||
|
||||
if (check_label("map_cells")) {
|
||||
run(stringf("techmap -map +/quicklogic/%s_lut_map.v", family.c_str()));
|
||||
if (check_label("map_luts", "(for qlf_k6n10f)") && (help_mode || family == "qlf_k6n10f")) {
|
||||
if (abc9) {
|
||||
run("abc9 -maxlut 6");
|
||||
} else {
|
||||
run("abc -lut 6 -dress");
|
||||
}
|
||||
run("clean");
|
||||
run("opt_lut");
|
||||
}
|
||||
|
||||
if (check_label("map_cells", "(for pp3)") && (help_mode || family == "pp3")) {
|
||||
run("techmap -map " + lib_path + family + "/lut_map.v");
|
||||
run("clean");
|
||||
run("opt_lut");
|
||||
}
|
||||
|
||||
if (check_label("check")) {
|
||||
|
@ -205,26 +335,30 @@ struct SynthQuickLogicPass : public ScriptPass {
|
|||
run("check -noinit");
|
||||
}
|
||||
|
||||
if (check_label("iomap")) {
|
||||
if (check_label("iomap", "(for pp3)") && (family == "pp3" || help_mode)) {
|
||||
run("clkbufmap -inpad ckpad Q:P");
|
||||
run("iopadmap -bits -outpad outpad A:P -inpad inpad Q:P -tinoutpad bipad EN:Q:A:P A:top");
|
||||
}
|
||||
|
||||
if (check_label("finalize")) {
|
||||
run("setundef -zero -params -undriven");
|
||||
run("hilomap -hicell logic_1 A -locell logic_0 A -singleton A:top");
|
||||
if (help_mode || family == "pp3") {
|
||||
run("setundef -zero -params -undriven", "(for pp3)");
|
||||
}
|
||||
if (family == "pp3" || !edif_file.empty()) {
|
||||
run("hilomap -hicell logic_1 A -locell logic_0 A -singleton A:top", "(for pp3 or if -edif)");
|
||||
}
|
||||
run("opt_clean -purge");
|
||||
run("check");
|
||||
run("blackbox =A:whitebox");
|
||||
}
|
||||
|
||||
if (check_label("blif")) {
|
||||
if (check_label("blif", "(if -blif)")) {
|
||||
if (!blif_file.empty() || help_mode) {
|
||||
run(stringf("write_blif -attr -param %s %s", top_opt.c_str(), blif_file.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
if (check_label("verilog")) {
|
||||
if (check_label("verilog", "(if -verilog)")) {
|
||||
if (!verilog_file.empty() || help_mode) {
|
||||
run(stringf("write_verilog -noattr -nohex %s", help_mode ? "<file-name>" : verilog_file.c_str()));
|
||||
}
|
||||
|
|
|
@ -45,6 +45,113 @@ module sync_ram_sdp #(parameter DATA_WIDTH=8, ADDRESS_WIDTH=10)
|
|||
endmodule // sync_ram_sdp
|
||||
|
||||
|
||||
module sync_ram_sdp_wwr #(parameter DATA_WIDTH=8, ADDRESS_WIDTH=10, SHIFT_VAL=1) // wd=16, wa=9
|
||||
(
|
||||
input wire clk_w, clk_r, write_enable,
|
||||
input wire [WORD-1:0] data_in,
|
||||
input wire [ADDRESS_WIDTH_W-1:0] address_in_w,
|
||||
input wire [ADDRESS_WIDTH-1:0] address_in_r,
|
||||
output wire [DATA_WIDTH-1:0] data_out
|
||||
);
|
||||
|
||||
localparam ADDRESS_WIDTH_W = ADDRESS_WIDTH-SHIFT_VAL;
|
||||
localparam BYTE = DATA_WIDTH;
|
||||
localparam WORD = DATA_WIDTH<<SHIFT_VAL;
|
||||
localparam DEPTH = 2**ADDRESS_WIDTH_W;
|
||||
localparam SUB_DEPTH = 2**SHIFT_VAL;
|
||||
|
||||
reg [BYTE-1:0] data_out_r;
|
||||
reg [BYTE-1:0] memory [0:DEPTH-1];
|
||||
|
||||
integer i;
|
||||
always @(posedge clk_w) begin
|
||||
for (i=0; i<SUB_DEPTH; i=i+1)
|
||||
if (write_enable)
|
||||
memory[{address_in_w, i}] <= data_in[i*BYTE+:BYTE];
|
||||
end
|
||||
|
||||
always @(posedge clk_r) begin
|
||||
data_out_r <= memory[address_in_r];
|
||||
end
|
||||
|
||||
assign data_out = data_out_r;
|
||||
|
||||
endmodule // sync_ram_sdp_wwr
|
||||
|
||||
|
||||
module sync_ram_sdp_wrr #(parameter DATA_WIDTH=8, ADDRESS_WIDTH=10, SHIFT_VAL=1) // rd=16, ra=9
|
||||
(
|
||||
input wire clk_w, clk_r, write_enable,
|
||||
input wire [DATA_WIDTH-1:0] data_in,
|
||||
input wire [ADDRESS_WIDTH-1:0] address_in_w,
|
||||
input wire [ADDRESS_WIDTH_R-1:0] address_in_r,
|
||||
output wire [WORD-1:0] data_out
|
||||
);
|
||||
localparam ADDRESS_WIDTH_R = ADDRESS_WIDTH-SHIFT_VAL;
|
||||
localparam BYTE = DATA_WIDTH;
|
||||
localparam WORD = BYTE<<SHIFT_VAL;
|
||||
localparam DEPTH = 2**ADDRESS_WIDTH;
|
||||
localparam SUB_DEPTH = 2**SHIFT_VAL;
|
||||
|
||||
reg [WORD-1:0] data_out_r;
|
||||
reg [BYTE-1:0] memory [0:DEPTH-1];
|
||||
|
||||
always @(posedge clk_w) begin
|
||||
if (write_enable)
|
||||
memory[address_in_w] <= data_in;
|
||||
end
|
||||
|
||||
integer i;
|
||||
always @(posedge clk_r) begin
|
||||
for (i=0; i<SUB_DEPTH; i=i+1)
|
||||
data_out_r[i*BYTE+:BYTE] <= memory[{address_in_r, i}];
|
||||
end
|
||||
|
||||
assign data_out = data_out_r;
|
||||
|
||||
endmodule // sync_ram_sdp_wrr
|
||||
|
||||
|
||||
module double_sync_ram_sdp #(parameter DATA_WIDTH_A=8, ADDRESS_WIDTH_A=10, DATA_WIDTH_B=8, ADDRESS_WIDTH_B=10)
|
||||
(
|
||||
input wire write_enable_a, clk_a,
|
||||
input wire [DATA_WIDTH_A-1:0] data_in_a,
|
||||
input wire [ADDRESS_WIDTH_A-1:0] address_in_r_a, address_in_w_a,
|
||||
output wire [DATA_WIDTH_A-1:0] data_out_a,
|
||||
|
||||
input wire write_enable_b, clk_b,
|
||||
input wire [DATA_WIDTH_B-1:0] data_in_b,
|
||||
input wire [ADDRESS_WIDTH_B-1:0] address_in_r_b, address_in_w_b,
|
||||
output wire [DATA_WIDTH_B-1:0] data_out_b
|
||||
);
|
||||
|
||||
sync_ram_sdp #(
|
||||
.DATA_WIDTH(DATA_WIDTH_A),
|
||||
.ADDRESS_WIDTH(ADDRESS_WIDTH_A)
|
||||
) a_ram (
|
||||
.write_enable(write_enable_a),
|
||||
.clk(clk_a),
|
||||
.data_in(data_in_a),
|
||||
.address_in_r(address_in_r_a),
|
||||
.address_in_w(address_in_w_a),
|
||||
.data_out(data_out_a)
|
||||
);
|
||||
|
||||
sync_ram_sdp #(
|
||||
.DATA_WIDTH(DATA_WIDTH_B),
|
||||
.ADDRESS_WIDTH(ADDRESS_WIDTH_B)
|
||||
) b_ram (
|
||||
.write_enable(write_enable_b),
|
||||
.clk(clk_b),
|
||||
.data_in(data_in_b),
|
||||
.address_in_r(address_in_r_b),
|
||||
.address_in_w(address_in_w_b),
|
||||
.data_out(data_out_b)
|
||||
);
|
||||
|
||||
endmodule // double_sync_ram_sdp
|
||||
|
||||
|
||||
module sync_ram_tdp #(parameter DATA_WIDTH=8, ADDRESS_WIDTH=10)
|
||||
(input wire clk_a, clk_b,
|
||||
input wire write_enable_a, write_enable_b,
|
||||
|
@ -74,3 +181,68 @@ module sync_ram_tdp #(parameter DATA_WIDTH=8, ADDRESS_WIDTH=10)
|
|||
|
||||
endmodule // sync_ram_tdp
|
||||
|
||||
module double_sync_ram_tdp #(parameter DATA_WIDTH=8, ADDRESS_WIDTH=10)
|
||||
(
|
||||
input wire clk_a_0,
|
||||
input wire write_enable_a_0, read_enable_a_0,
|
||||
input wire [DATA_WIDTH-1:0] write_data_a_0,
|
||||
input wire [ADDRESS_WIDTH-1:0] addr_a_0,
|
||||
output wire [DATA_WIDTH-1:0] read_data_a_0,
|
||||
|
||||
input wire clk_a_1,
|
||||
input wire write_enable_a_1, read_enable_a_1,
|
||||
input wire [DATA_WIDTH-1:0] write_data_a_1,
|
||||
input wire [ADDRESS_WIDTH-1:0] addr_a_1,
|
||||
output wire [DATA_WIDTH-1:0] read_data_a_1,
|
||||
|
||||
input wire clk_b_0,
|
||||
input wire write_enable_b_0, read_enable_b_0,
|
||||
input wire [DATA_WIDTH-1:0] write_data_b_0,
|
||||
input wire [ADDRESS_WIDTH-1:0] addr_b_0,
|
||||
output wire [DATA_WIDTH-1:0] read_data_b_0,
|
||||
|
||||
input wire clk_b_1,
|
||||
input wire write_enable_b_1, read_enable_b_1,
|
||||
input wire [DATA_WIDTH-1:0] write_data_b_1,
|
||||
input wire [ADDRESS_WIDTH-1:0] addr_b_1,
|
||||
output wire [DATA_WIDTH-1:0] read_data_b_1
|
||||
);
|
||||
|
||||
sync_ram_tdp #(
|
||||
.DATA_WIDTH(DATA_WIDTH),
|
||||
.ADDRESS_WIDTH(ADDRESS_WIDTH)
|
||||
) ram_0 (
|
||||
.clk_a(clk_a_0),
|
||||
.clk_b(clk_b_0),
|
||||
.write_enable_a(write_enable_a_0),
|
||||
.write_enable_b(write_enable_b_0),
|
||||
.read_enable_a(read_enable_a_0),
|
||||
.read_enable_b(read_enable_b_0),
|
||||
.write_data_a(write_data_a_0),
|
||||
.write_data_b(write_data_b_0),
|
||||
.addr_a(addr_a_0),
|
||||
.addr_b(addr_b_0),
|
||||
.read_data_a(read_data_a_0),
|
||||
.read_data_b(read_data_b_0)
|
||||
);
|
||||
|
||||
sync_ram_tdp #(
|
||||
.DATA_WIDTH(DATA_WIDTH),
|
||||
.ADDRESS_WIDTH(ADDRESS_WIDTH)
|
||||
) ram_1 (
|
||||
.clk_a(clk_a_1),
|
||||
.clk_b(clk_b_1),
|
||||
.write_enable_a(write_enable_a_1),
|
||||
.write_enable_b(write_enable_b_1),
|
||||
.read_enable_a(read_enable_a_1),
|
||||
.read_enable_b(read_enable_b_1),
|
||||
.write_data_a(write_data_a_1),
|
||||
.write_data_b(write_data_b_1),
|
||||
.addr_a(addr_a_1),
|
||||
.addr_b(addr_b_1),
|
||||
.read_data_a(read_data_a_1),
|
||||
.read_data_b(read_data_b_1)
|
||||
);
|
||||
|
||||
endmodule // double_sync_ram_tdp
|
||||
|
||||
|
|
|
@ -4,6 +4,9 @@ proc
|
|||
equiv_opt -assert -map +/ecp5/cells_sim.v synth_ecp5 # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd top # Constrain all select calls below inside the top module
|
||||
select -assert-count 10 t:LUT4
|
||||
select -assert-none t:LUT4 %% t:* %D
|
||||
select -assert-min 25 t:LUT4
|
||||
select -assert-max 26 t:LUT4
|
||||
select -assert-count 10 t:PFUMX
|
||||
select -assert-count 6 t:L6MUX21
|
||||
select -assert-none t:LUT4 t:PFUMX t:L6MUX21 %% t:* %D
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ flatten
|
|||
equiv_opt -assert -multiclock -map +/ecp5/cells_sim.v synth_ecp5 # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd top # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:LUT4
|
||||
select -assert-count 4 t:CCU2C
|
||||
select -assert-count 8 t:TRELLIS_FF
|
||||
select -assert-none t:CCU2C t:TRELLIS_FF %% t:* %D
|
||||
select -assert-none t:LUT4 t:CCU2C t:TRELLIS_FF %% t:* %D
|
||||
|
|
|
@ -6,10 +6,11 @@ equiv_opt -assert -multiclock -map +/gowin/cells_sim.v synth_gowin # equivalency
|
|||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd top # Constrain all select calls below inside the top module
|
||||
|
||||
select -assert-count 1 t:LUT1
|
||||
select -assert-count 8 t:DFFC
|
||||
select -assert-count 8 t:ALU
|
||||
select -assert-count 1 t:GND
|
||||
select -assert-count 1 t:VCC
|
||||
select -assert-count 2 t:IBUF
|
||||
select -assert-count 8 t:OBUF
|
||||
select -assert-none t:DFFC t:ALU t:GND t:VCC t:IBUF t:OBUF %% t:* %D
|
||||
select -assert-none t:LUT1 t:DFFC t:ALU t:GND t:VCC t:IBUF t:OBUF %% t:* %D
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
read_verilog init.v
|
||||
read_verilog -lib +/gowin/cells_sim.v
|
||||
read_verilog -lib -specify +/gowin/cells_sim.v
|
||||
design -save read
|
||||
|
||||
proc
|
||||
|
|
|
@ -32,10 +32,17 @@ proc
|
|||
equiv_opt -assert -map +/gowin/cells_sim.v synth_gowin # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd mux8 # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:LUT1
|
||||
select -assert-count 10 t:LUT3
|
||||
select -assert-count 1 t:LUT4
|
||||
select -assert-count 5 t:MUX2_LUT5
|
||||
select -assert-count 2 t:MUX2_LUT6
|
||||
select -assert-count 1 t:MUX2_LUT7
|
||||
select -assert-count 11 t:IBUF
|
||||
select -assert-count 1 t:OBUF
|
||||
select -assert-count 1 t:GND
|
||||
|
||||
select -assert-none t:LUT* t:MUX2_LUT6 t:MUX2_LUT5 t:IBUF t:OBUF %% t:* %D
|
||||
select -assert-none t:LUT* t:MUX2_LUT7 t:MUX2_LUT6 t:MUX2_LUT5 t:IBUF t:OBUF t:GND %% t:* %D
|
||||
|
||||
design -load read
|
||||
hierarchy -top mux16
|
||||
|
|
|
@ -3,7 +3,7 @@ hierarchy -top top
|
|||
equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd top # Constrain all select calls below inside the top module
|
||||
select -assert-count 11 t:SB_LUT4
|
||||
select -assert-count 10 t:SB_LUT4
|
||||
select -assert-count 6 t:SB_CARRY
|
||||
select -assert-none t:SB_LUT4 t:SB_CARRY %% t:* %D
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ proc
|
|||
equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd mux4 # Constrain all select calls below inside the top module
|
||||
select -assert-count 2 t:SB_LUT4
|
||||
select -assert-count 3 t:SB_LUT4
|
||||
|
||||
select -assert-none t:SB_LUT4 %% t:* %D
|
||||
|
||||
|
@ -25,7 +25,7 @@ proc
|
|||
equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd mux8 # Constrain all select calls below inside the top module
|
||||
select -assert-count 5 t:SB_LUT4
|
||||
select -assert-count 6 t:SB_LUT4
|
||||
|
||||
select -assert-none t:SB_LUT4 %% t:* %D
|
||||
|
||||
|
@ -35,7 +35,7 @@ proc
|
|||
equiv_opt -assert -map +/ice40/cells_sim.v synth_ice40 # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd mux16 # Constrain all select calls below inside the top module
|
||||
select -assert-min 11 t:SB_LUT4
|
||||
select -assert-max 12 t:SB_LUT4
|
||||
select -assert-min 13 t:SB_LUT4
|
||||
select -assert-max 14 t:SB_LUT4
|
||||
|
||||
select -assert-none t:SB_LUT4 %% t:* %D
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
read_verilog ../common/add_sub.v
|
||||
read_verilog ../../common/add_sub.v
|
||||
hierarchy -top top
|
||||
equiv_opt -assert -map +/quicklogic/lut_sim.v -map +/quicklogic/pp3_cells_sim.v synth_quicklogic -family pp3 # equivalency check
|
||||
equiv_opt -assert -map +/quicklogic/pp3/cells_sim.v synth_quicklogic -family pp3 # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd top # Constrain all select calls below inside the top module
|
||||
select -assert-count 2 t:LUT2
|
|
@ -1,9 +1,9 @@
|
|||
read_verilog ../common/adffs.v
|
||||
read_verilog ../../common/adffs.v
|
||||
design -save read
|
||||
|
||||
hierarchy -top adff
|
||||
proc
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/pp3_cells_sim.v -map +/quicklogic/cells_sim.v synth_quicklogic # equivalency check
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/pp3/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd adff # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:dffepc
|
||||
|
@ -19,7 +19,7 @@ select -assert-none t:dffepc t:logic_0 t:logic_1 t:inpad t:outpad t:ckpad %% t:*
|
|||
design -load read
|
||||
hierarchy -top adffn
|
||||
proc
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/pp3_cells_sim.v -map +/quicklogic/cells_sim.v -map +/quicklogic/lut_sim.v synth_quicklogic # equivalency check
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/pp3/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd adffn # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:LUT1
|
||||
|
@ -36,7 +36,7 @@ select -assert-none t:LUT1 t:dffepc t:logic_0 t:logic_1 t:inpad t:outpad t:ckpad
|
|||
design -load read
|
||||
hierarchy -top dffs
|
||||
proc
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/pp3_cells_sim.v -map +/quicklogic/cells_sim.v -map +/quicklogic/lut_sim.v synth_quicklogic # equivalency check
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/pp3/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd dffs # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:LUT2
|
||||
|
@ -53,7 +53,7 @@ select -assert-none t:LUT2 t:dffepc t:logic_0 t:logic_1 t:inpad t:outpad t:ckpad
|
|||
design -load read
|
||||
hierarchy -top ndffnr
|
||||
proc
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/pp3_cells_sim.v -map +/quicklogic/cells_sim.v -map +/quicklogic/lut_sim.v synth_quicklogic # equivalency check
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/pp3/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd ndffnr # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:LUT1
|
|
@ -1,8 +1,8 @@
|
|||
read_verilog ../common/counter.v
|
||||
read_verilog ../../common/counter.v
|
||||
hierarchy -top top
|
||||
proc
|
||||
flatten
|
||||
equiv_opt -assert -multiclock -map +/quicklogic/pp3_cells_sim.v -map +/quicklogic/cells_sim.v -map +/quicklogic/lut_sim.v synth_quicklogic # equivalency check
|
||||
equiv_opt -assert -multiclock -map +/quicklogic/pp3/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd top # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:LUT1
|
|
@ -1,11 +1,11 @@
|
|||
read_verilog ../common/dffs.v
|
||||
read_verilog ../../common/dffs.v
|
||||
rename dff my_dff # Work around conflicting module names between test and vendor cells
|
||||
rename dffe my_dffe
|
||||
design -save read
|
||||
|
||||
hierarchy -top my_dff
|
||||
proc
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/pp3_cells_sim.v -map +/quicklogic/cells_sim.v synth_quicklogic # equivalency check
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/pp3/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd my_dff # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:ckpad
|
||||
|
@ -20,7 +20,7 @@ select -assert-none t:ckpad t:dffepc t:inpad t:logic_0 t:logic_1 t:outpad %% t:*
|
|||
design -load read
|
||||
hierarchy -top my_dffe
|
||||
proc
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/pp3_cells_sim.v -map +/quicklogic/cells_sim.v synth_quicklogic # equivalency check
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/pp3/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd my_dffe # Constrain all select calls below inside the top module
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
read_verilog ../common/fsm.v
|
||||
read_verilog ../../common/fsm.v
|
||||
hierarchy -top fsm
|
||||
proc
|
||||
flatten
|
||||
|
||||
equiv_opt -run :prove -map +/quicklogic/pp3_cells_sim.v -map +/quicklogic/cells_sim.v -map +/quicklogic/lut_sim.v synth_quicklogic
|
||||
equiv_opt -run :prove -map +/quicklogic/pp3/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic
|
||||
async2sync
|
||||
miter -equiv -make_assert -flatten gold gate miter
|
||||
sat -verify -prove-asserts -show-public -set-at 1 in_reset 1 -seq 20 -prove-skip 1 miter
|
|
@ -1,4 +1,4 @@
|
|||
read_verilog ../common/latches.v
|
||||
read_verilog ../../common/latches.v
|
||||
design -save read
|
||||
|
||||
hierarchy -top latchp
|
|
@ -1,7 +1,7 @@
|
|||
read_verilog ../common/logic.v
|
||||
read_verilog ../../common/logic.v
|
||||
hierarchy -top top
|
||||
proc
|
||||
equiv_opt -assert -map +/quicklogic/pp3_cells_sim.v -map +/quicklogic/cells_sim.v -map +/quicklogic/lut_sim.v synth_quicklogic # equivalency check
|
||||
equiv_opt -assert -map +/quicklogic/pp3/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd top # Constrain all select calls below inside the top module
|
||||
|
|
@ -1,9 +1,9 @@
|
|||
read_verilog ../common/mux.v
|
||||
read_verilog ../../common/mux.v
|
||||
design -save read
|
||||
|
||||
hierarchy -top mux2
|
||||
proc
|
||||
equiv_opt -assert -map +/quicklogic/pp3_cells_sim.v -map +/quicklogic/cells_sim.v -map +/quicklogic/lut_sim.v synth_quicklogic # equivalency check
|
||||
equiv_opt -assert -map +/quicklogic/pp3/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd mux2 # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:LUT3
|
||||
|
@ -15,7 +15,7 @@ select -assert-none t:LUT3 t:inpad t:outpad %% t:* %D
|
|||
design -load read
|
||||
hierarchy -top mux4
|
||||
proc
|
||||
equiv_opt -assert -map +/quicklogic/pp3_cells_sim.v -map +/quicklogic/cells_sim.v -map +/quicklogic/lut_sim.v synth_quicklogic # equivalency check
|
||||
equiv_opt -assert -map +/quicklogic/pp3/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd mux4 # Constrain all select calls below inside the top module
|
||||
select -assert-count 3 t:LUT3
|
||||
|
@ -27,7 +27,7 @@ select -assert-none t:LUT3 t:inpad t:outpad %% t:* %D
|
|||
design -load read
|
||||
hierarchy -top mux8
|
||||
proc
|
||||
equiv_opt -assert -map +/quicklogic/pp3_cells_sim.v -map +/quicklogic/cells_sim.v -map +/quicklogic/lut_sim.v synth_quicklogic # equivalency check
|
||||
equiv_opt -assert -map +/quicklogic/pp3/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd mux8 # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:LUT1
|
||||
|
@ -41,7 +41,7 @@ select -assert-none t:LUT1 t:LUT3 t:mux4x0 t:inpad t:outpad %% t:* %D
|
|||
design -load read
|
||||
hierarchy -top mux16
|
||||
proc
|
||||
equiv_opt -assert -map +/quicklogic/pp3_cells_sim.v -map +/quicklogic/cells_sim.v -map +/quicklogic/lut_sim.v synth_quicklogic # equivalency check
|
||||
equiv_opt -assert -map +/quicklogic/pp3/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd mux16 # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:LUT3
|
|
@ -1,4 +1,4 @@
|
|||
#!/usr/bin/env bash
|
||||
set -eu
|
||||
source ../../gen-tests-makefile.sh
|
||||
source ../../../gen-tests-makefile.sh
|
||||
run_tests --yosys-scripts --bash --yosys-args "-w 'Yosys has only limited support for tri-state logic at the moment.'"
|
|
@ -1,10 +1,10 @@
|
|||
read_verilog ../common/tribuf.v
|
||||
read_verilog ../../common/tribuf.v
|
||||
hierarchy -top tristate
|
||||
proc
|
||||
tribuf
|
||||
flatten
|
||||
synth
|
||||
equiv_opt -assert -map +/quicklogic/pp3_cells_sim.v -map +/quicklogic/cells_sim.v -map +/simcells.v synth_quicklogic # equivalency check
|
||||
equiv_opt -assert -map +/quicklogic/pp3/cells_sim.v -map +/quicklogic/common/cells_sim.v -map +/simcells.v synth_quicklogic # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd tristate # Constrain all select calls below inside the top module
|
||||
select -assert-count 2 t:inpad
|
|
@ -0,0 +1 @@
|
|||
t_*.ys
|
|
@ -0,0 +1,8 @@
|
|||
read_verilog ../../common/add_sub.v
|
||||
hierarchy -top top
|
||||
equiv_opt -assert -map +/quicklogic/qlf_k6n10f/cells_sim.v synth_quicklogic -family qlf_k6n10f # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd top # Constrain all select calls below inside the top module
|
||||
select -assert-count 9 t:$lut # OOT flow has 8
|
||||
select -assert-count 8 t:adder_carry
|
||||
select -assert-none t:$lut t:adder_carry %% t:* %D
|
|
@ -0,0 +1,48 @@
|
|||
read_verilog ../../common/adffs.v
|
||||
design -save read
|
||||
|
||||
hierarchy -top adff
|
||||
proc
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd adff # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:$lut r:WIDTH=1 %i
|
||||
select -assert-none r:WIDTH>1
|
||||
select -assert-count 1 t:dffsre
|
||||
|
||||
select -assert-none t:$lut t:dffsre %% t:* %D
|
||||
|
||||
|
||||
design -load read
|
||||
hierarchy -top adffn
|
||||
proc
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd adffn # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:dffsre
|
||||
|
||||
select -assert-none t:dffsre %% t:* %D
|
||||
|
||||
|
||||
design -load read
|
||||
hierarchy -top dffs
|
||||
proc
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd dffs # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:$lut r:WIDTH=1 %i
|
||||
select -assert-none r:WIDTH>1
|
||||
select -assert-count 1 t:sdffsre
|
||||
|
||||
select -assert-none t:$lut t:sdffsre %% t:* %D
|
||||
|
||||
|
||||
design -load read
|
||||
hierarchy -top ndffnr
|
||||
proc
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd ndffnr # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:sdffnsre
|
||||
|
||||
select -assert-none t:sdffnsre %% t:* %D
|
|
@ -0,0 +1,12 @@
|
|||
read_verilog ../../common/counter.v
|
||||
hierarchy -top top
|
||||
proc
|
||||
flatten
|
||||
equiv_opt -assert -multiclock -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd top # Constrain all select calls below inside the top module
|
||||
select -assert-count 4 t:$lut
|
||||
select -assert-count 8 t:adder_carry
|
||||
select -assert-count 8 t:dffsre
|
||||
|
||||
select -assert-none t:$lut t:adder_carry t:dffsre %% t:* %D
|
|
@ -0,0 +1,21 @@
|
|||
read_verilog ../../common/dffs.v
|
||||
rename dff my_dff # Work around conflicting module names between test and vendor cells
|
||||
rename dffe my_dffe
|
||||
design -save read
|
||||
|
||||
hierarchy -top my_dff
|
||||
proc
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd my_dff # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:sdffsre
|
||||
select -assert-none t:sdffsre %% t:* %D
|
||||
|
||||
design -load read
|
||||
hierarchy -top my_dffe
|
||||
proc
|
||||
equiv_opt -async2sync -assert -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f # equivalency check
|
||||
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
|
||||
cd my_dffe # Constrain all select calls below inside the top module
|
||||
select -assert-count 1 t:sdffsre
|
||||
select -assert-none t:sdffsre %% t:* %D
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue