Merge updated master into krys/docs

This commit is contained in:
Krystine Sherwin 2023-12-13 10:17:11 +13:00
commit afe8eff790
No known key found for this signature in database
116 changed files with 26611 additions and 716 deletions

30
.github/workflows/wasi.yml vendored Normal file
View File

@ -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
.gitignore vendored
View File

@ -1,5 +1,6 @@
*.o
*.d
*.dwo
.*.swp
*.gch
*.gcda

View File

@ -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

View File

@ -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

View File

@ -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";

View File

@ -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.

View File

@ -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;

View File

@ -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);

View File

@ -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" {

View File

@ -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) {
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++)
// First add to `count` as if the chunk is zero
count += (n == 0 ? Bits % chunk::bits : chunk::bits);
// 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;
}
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::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});
}
std::copy(blk, blk + origLen, data);
return {quotient, /*remainder=*/dividend};
}
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>

View File

@ -19,7 +19,7 @@
#ifndef CXXRTL_VCD_H
#define CXXRTL_VCD_H
#include <backends/cxxrtl/cxxrtl.h>
#include <cxxrtl/cxxrtl.h>
namespace cxxrtl {

View File

@ -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
if not got_topt:
if not check_witness:
skip_steps = max(skip_steps, t)
num_steps = max(num_steps, t+1)
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, 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):
filename = outywfile.replace("%", index)
print_msg("Writing trace to Yosys witness file: %s" % (filename))
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()
print_msg("Status: %s" % retstatus)
sys.exit(0 if retstatus == "PASSED" else 1)
if not incremental:
print_msg("Status: %s" % retstatus)
sys.exit(0 if retstatus == "PASSED" else 1)

View File

@ -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)

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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 = "^"; }

View File

@ -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);

View File

@ -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;

View File

@ -140,6 +140,7 @@ X(nomem2reg)
X(nomeminit)
X(nosync)
X(nowrshmsk)
X(no_ram)
X(no_rw_check)
X(O)
X(OFFSET)

View File

@ -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");

View File

@ -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;

View File

@ -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; }

View File

@ -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;

View File

@ -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));

View File

@ -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;

View File

@ -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)); }

View File

@ -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,8 +566,11 @@ void MemMapping::determine_style() {
return;
}
}
if (mem.get_bool_attribute(ID::logic_block))
kind = RamKind::Logic;
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.

View File

@ -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)

View File

@ -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();

View File

@ -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

View File

@ -82,22 +82,17 @@ 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++) {
SigSpec slice = old_a.extract(i*const_factor, const_factor);
new_a.append(slice);
new_a.append(padding);
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())
new_b.append(State::S0);

View File

@ -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());
}
}
}

View File

@ -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,
A, // multiplicand
B, // multiplier(scanned)
Y // result
if (!lowpower)
CreateBoothMult(module,
A, // multiplicand
B, // multiplier(scanned)
Y, // result
is_signed
);
else /* signed multiplier */
CreateBoothSMult(module,
A, // multiplicand
B, // multiplier(scanned)
Y // result (sized)
else
CreateBoothLowpowerMult(module,
A, // multiplicand
B, // multiplier(scanned)
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,16 +468,15 @@ 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
ppij_vec.append(Bur4d_msb("row_dec_red", X[x_sz - 1], two_int, s_int));
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(State::S1);
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);
}
void DebugDumpAlignPP(std::vector<std::vector<RTLIL::Wire *>> &aligned_pp)
@ -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,218 +1051,88 @@ 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++;
// 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)
);
module->connect(fa_carry[0][1], q1_carry_out);
}
// 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]
);
}
// 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]
);
// step case: 2nd and rest of rows. (fa_row_ix == 1...n)
// special because these are driven by a decoder and prior fa.
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_L", fa_row_ix)),
PPij[((fa_row_ix + 1) * dec_count) + dec_count - 1]);
// 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]);
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},
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]
);
}
}
// 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) {
// 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)),
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++;
// 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) {
int fa_row_ix = cpa_ix / 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 {
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 op;
BuildHa(cpa_name, fa_sum_n[(fa_row_count - 1) * fa_count + cpa_ix - offset + 2], ci, op,
cpa_carry[cpa_ix - offset]);
module->connect(Z[cpa_ix], op);
}
// 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
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));
}
//
// 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_";
for (int cpa_ix = fa_row_count * 2; cpa_ix < z_sz; cpa_ix++) {
int offset = fa_row_count * 2;
std::string cpa_name = stringf("cpa_%d", cpa_ix - offset);
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);
SigBit ci = (cpa_ix == offset) ? cori_n_int[enc_count - 1] : cpa_carry[cpa_ix - offset - 1];
SigBit op;
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);
}
}
};
@ -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,8 +1158,17 @@ 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++) {
break;
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;
}

View File

@ -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
{

View File

@ -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))

View File

@ -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

View File

@ -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") {

View File

@ -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") {

View File

@ -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") {

View File

@ -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");

View File

@ -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),

1
techlibs/quicklogic/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/*_pm.h

View File

@ -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))

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
/bram_types_sim.v

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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")) {
run("techmap");
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");
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("opt_expr -mux_undef");
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("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()));
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,4 +1,4 @@
read_verilog ../common/latches.v
read_verilog ../../common/latches.v
design -save read
hierarchy -top latchp

View File

@ -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

View File

@ -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

View File

@ -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.'"

View File

@ -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

View File

@ -0,0 +1 @@
t_*.ys

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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