Merge pull request #4045 from povik/upstream-ql-k6n10f

Upstream QuickLogic k6n10f flow
This commit is contained in:
Miodrag Milanović 2023-12-04 16:47:17 +01:00 committed by GitHub
commit 8738143880
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
61 changed files with 98395 additions and 138 deletions

View File

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

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

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

View File

@ -1,13 +1,43 @@
%_pm.h: passes/pmgen/pmgen.py %.pmg
$(P) mkdir -p pmgen && $(PYTHON_EXECUTABLE) $< -o $@ -p $(subst _pm.h,,$(notdir $@)) $(filter-out $<,$^)
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/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_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 "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,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

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

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

View File

@ -0,0 +1,120 @@
read_verilog <<EOF
module top(a, b, y, batch, clk);
parameter [31:0] OPER_WIDTH=8;
parameter [31:0] ACC_WIDTH=8;
input wire batch;
input wire clk;
input wire [OPER_WIDTH-1:0] a;
input wire [OPER_WIDTH-1:0] b;
output reg [ACC_WIDTH-1:0] y;
wire [ACC_WIDTH-1:0] ab = a * b;
always @(posedge clk) begin
if (batch)
y <= ab;
else
y <= ab + y;
end
endmodule
EOF
design -save ast
proc
wreduce
#equiv_opt -async2sync -map +/quicklogic/qlf_k6n10f/dsp_sim.v synth_quicklogic -family qlf_k6n10f
#design -load postopt
synth_quicklogic -family qlf_k6n10f
cd top
select -assert-count 1 t:QL_DSP2_MULTACC
select -assert-none t:QL_DSP2_MULTACC %n t:* %i
design -load ast
chparam -set OPER_WIDTH 18
chparam -set ACC_WIDTH 16
proc
wreduce
synth_quicklogic -family qlf_k6n10f
cd top
select -assert-count 1 t:QL_DSP2_MULTACC
select -assert-none t:QL_DSP2_MULTACC %n t:* %i
design -load ast
chparam -set OPER_WIDTH 19 # <-- too wide, shouldn't map to multacc
chparam -set ACC_WIDTH 16
proc
wreduce
synth_quicklogic -family qlf_k6n10f
cd top
select -assert-none t:QL_DSP2_MULTACC
select -assert-count 1 t:QL_DSP2_MULT
design -load ast
chparam -set OPER_WIDTH 16
chparam -set ACC_WIDTH 32
proc
synth_quicklogic -family qlf_k6n10f
read_verilog -sv <<EOF
module testbench(clk);
localparam OPER_WIDTH=16;
localparam ACC_WIDTH=32;
localparam VECTORLEN=16;
parameter PRIME1 = 237481091;
parameter PRIME2 = 1752239;
reg [OPER_WIDTH-1:0] a_vector [VECTORLEN-1:0];
reg [OPER_WIDTH-1:0] b_vector [VECTORLEN-1:0];
reg [ACC_WIDTH-1:0] y_vector [VECTORLEN-1:0];
reg [0:0] batch_vector [VECTORLEN-1:0];
integer j;
integer a_, b_, y_, batch_;
initial begin
y_ = 0;
a_ = 0;
b_ = 0;
y_vector[0] = 0;
for (j = 0; j < VECTORLEN; j = j + 1) begin
batch_ = (j % 4) == 0;
a_ = (a_ ^ (PRIME1 * j)) & ((1 << OPER_WIDTH) - 1);
b_ = (b_ ^ (PRIME2 * j)) & ((1 << OPER_WIDTH) - 1);
if (batch_)
y_ = a_ * b_;
else
y_ = a_ * b_ + y_;
a_vector[j] = a_;
b_vector[j] = b_;
y_vector[j + 1] = y_;
batch_vector[j] = batch_;
end
end
input wire clk;
wire batch = batch_vector[i];
wire [OPER_WIDTH-1:0] a = a_vector[i];
wire [OPER_WIDTH-1:0] b = b_vector[i];
wire [ACC_WIDTH-1:0] y_expected = y_vector[i];
wire [ACC_WIDTH-1:0] y;
top uut_top(
.batch(batch),
.clk(clk),
.a(a),
.b(b),
.y(y)
);
reg [7:0] i = 0;
always @(posedge clk) begin
if (i < VECTORLEN) begin
// FIXME: for some reason the first assert fails (despite comparing zero to zero)
if (i > 0)
assert(y == y_expected);
i <= i + 1;
end
end
endmodule
EOF
read_verilog +/quicklogic/qlf_k6n10f/dsp_sim.v
hierarchy -top testbench
proc
sim -assert -q -clock clk -n 20

View File

@ -0,0 +1,17 @@
read_verilog ../../common/fsm.v
hierarchy -top fsm
proc
flatten
equiv_opt -run :prove -map +/quicklogic/qlf_k6n10f/cells_sim.v -map +/quicklogic/common/cells_sim.v synth_quicklogic -family qlf_k6n10f
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
design -load postopt # load the post-opt design (otherwise equiv_opt loads the pre-opt design)
cd fsm # Constrain all select calls below inside the top module
select -assert-count 9 t:$lut
select -assert-count 6 t:sdffsre
select -assert-none t:$lut t:sdffsre %% t:* %D

View File

@ -0,0 +1,29 @@
read_verilog ../../common/latches.v
design -save read
hierarchy -top latchp
proc
equiv_opt -assert -async2sync -map +/quicklogic/qlf_k6n10f/cells_sim.v synth_quicklogic -family qlf_k6n10f
design -load postopt
cd latchp
select -assert-count 1 t:latchsre
select -assert-none t:latchsre %% t:* %D
design -load read
hierarchy -top latchn
proc
equiv_opt -assert -async2sync -map +/quicklogic/qlf_k6n10f/cells_sim.v synth_quicklogic -family qlf_k6n10f
design -load postopt
cd latchn
select -assert-count 1 t:latchnsre
select -assert-none t:latchnsre %% t:* %D
design -load read
hierarchy -top latchsr
proc
equiv_opt -assert -async2sync -map +/quicklogic/qlf_k6n10f/cells_sim.v synth_quicklogic -family qlf_k6n10f
design -load postopt
cd latchsr
select -assert-count 2 t:$lut
select -assert-count 1 t:latchnsre
select -assert-none t:$lut t:latchnsre %% t:* %D

View File

@ -0,0 +1,10 @@
read_verilog ../../common/logic.v
hierarchy -top top
proc
equiv_opt -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 top # Constrain all select calls below inside the top module
select -assert-count 9 t:$lut
select -assert-none t:$lut %% t:* %D

View File

@ -0,0 +1,462 @@
from __future__ import annotations
from dataclasses import dataclass
blockram_template = """# ======================================
log ** GENERATING TEST {top} WITH PARAMS{param_str}
design -reset; read_verilog -defer ../../common/blockram.v
chparam{param_str} {top}
hierarchy -top {top}
synth_quicklogic -family qlf_k6n10f -top {top}
"""
blockram_tests: "list[tuple[list[tuple[str, int]], str, list[str]]]" = [
# TDP36K = 1024x36bit RAM, 2048x18bit or 4096x9bit also work
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 36)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 18)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 9)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K"]),
# larger sizes need an extra ram
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 48)], "sync_ram_*dp", ["-assert-count 2 t:TDP36K"]),
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 36)], "sync_ram_*dp", ["-assert-count 2 t:TDP36K"]),
([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 18)], "sync_ram_*dp", ["-assert-count 2 t:TDP36K"]),
([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 10)], "sync_ram_*dp", ["-assert-count 2 t:TDP36K"]),
# 4096x20bit *can* fit in 3, albeit somewhat awkwardly
([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 20)], "sync_ram_*dp", ["-assert-min 3 t:TDP36K",
"-assert-max 4 t:TDP36K"]),
# smaller sizes can still fit in one, and assign the correct width (1, 2, 4, 8, 18 or 36)
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 32)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=36 %i"]),
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 24)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=36 %i"]),
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 18)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=18 %i"]),
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 9)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=9 %i"]),
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 16)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=18 %i"]),
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 9)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=9 %i"]),
([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 8)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=9 %i"]),
([("ADDRESS_WIDTH", 13), ("DATA_WIDTH", 4)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=4 %i"]),
([("ADDRESS_WIDTH", 14), ("DATA_WIDTH", 2)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=2 %i"]),
([("ADDRESS_WIDTH", 15), ("DATA_WIDTH", 1)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=1 %i"]),
# 2x asymmetric (1024x36bit write / 2048x18bit read or vice versa = 1TDP36K)
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 18), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 16), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 9), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 8), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
# 4x asymmetric (1024x36bit write / 4096x9bit read or vice versa = 1TDP36K)
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 4), ("SHIFT_VAL", 2)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 9), ("SHIFT_VAL", 2)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 8), ("SHIFT_VAL", 2)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
# can also use an extra TDP36K for higher width
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 16), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 8), ("SHIFT_VAL", 2)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 36), ("SHIFT_VAL", 1)], "sync_ram_sdp_w*r", ["-assert-count 2 t:TDP36K"]),
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 36), ("SHIFT_VAL", 2)], "sync_ram_sdp_w*r", ["-assert-count 4 t:TDP36K"]),
([("ADDRESS_WIDTH", 9), ("DATA_WIDTH", 36), ("SHIFT_VAL", 2)], "sync_ram_sdp_w*r", ["-assert-count 4 t:TDP36K"]),
# # SHIFT=0 should be identical to sync_ram_sdp
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 36), ("SHIFT_VAL", 0)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 18), ("SHIFT_VAL", 0)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K"]),
# asymmetric memories assign different port widths on a and b ports
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 18), ("SHIFT_VAL", 1)], "sync_ram_sdp_wwr", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=36 %i a:port_b_width=18 %i"]),
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 9), ("SHIFT_VAL", 1)], "sync_ram_sdp_wwr", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=18 %i a:port_b_width=9 %i"]),
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 9), ("SHIFT_VAL", 2)], "sync_ram_sdp_wwr", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=36 %i a:port_b_width=9 %i"]),
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 18), ("SHIFT_VAL", 1)], "sync_ram_sdp_wrr", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=18 %i a:port_b_width=36 %i"]),
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 9), ("SHIFT_VAL", 1)], "sync_ram_sdp_wrr", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=9 %i a:port_b_width=18 %i"]),
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 9), ("SHIFT_VAL", 2)], "sync_ram_sdp_wrr", ["-assert-count 1 t:TDP36K", "-assert-count 1 t:TDP36K a:port_a_width=9 %i a:port_b_width=36 %i"]),
# two disjoint 18K memories can share a single TDP36K
([("ADDRESS_WIDTH_A", 10), ("DATA_WIDTH_A", 18),
("ADDRESS_WIDTH_B", 10), ("DATA_WIDTH_B", 18)], "double_sync_ram_sdp", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH_A", 10), ("DATA_WIDTH_A", 16),
("ADDRESS_WIDTH_B", 11), ("DATA_WIDTH_B", 8)], "double_sync_ram_sdp", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH_A", 14), ("DATA_WIDTH_A", 1),
("ADDRESS_WIDTH_B", 11), ("DATA_WIDTH_B", 8)], "double_sync_ram_sdp", ["-assert-count 1 t:TDP36K"]),
# but only if data width is <= 18
([("ADDRESS_WIDTH_A", 9), ("DATA_WIDTH_A", 36),
("ADDRESS_WIDTH_B", 11), ("DATA_WIDTH_B", 9)], "double_sync_ram_sdp", ["-assert-count 2 t:TDP36K"]),
# also for tdp
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 18)], "double_sync_ram_tdp", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 16)], "double_sync_ram_tdp", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 8)], "double_sync_ram_tdp", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH", 12), ("DATA_WIDTH", 4)], "double_sync_ram_tdp", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH", 13), ("DATA_WIDTH", 2)], "double_sync_ram_tdp", ["-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH", 14), ("DATA_WIDTH", 1)], "double_sync_ram_tdp", ["-assert-count 1 t:TDP36K"]),
# still only if data width is <= 18
([("ADDRESS_WIDTH", 9), ("DATA_WIDTH", 36)], "double_sync_ram_tdp", ["-assert-count 2 t:TDP36K"]),
# sharing a TDP36K sets is_split=1
([("ADDRESS_WIDTH_A", 10), ("DATA_WIDTH_A", 18),
("ADDRESS_WIDTH_B", 10), ("DATA_WIDTH_B", 18)], "double_sync_ram_sdp", ["-assert-count 1 t:TDP36K a:is_split=1 %i"]),
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 18)], "double_sync_ram_tdp", ["-assert-count 1 t:TDP36K a:is_split=1 %i"]),
# an unshared TDP36K sets is_split=0
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 36)], "sync_ram_*dp", ["-assert-count 1 t:TDP36K a:is_split=0 %i"]),
([("ADDRESS_WIDTH", 11), ("DATA_WIDTH", 18)], "sync_ram_sdp_w*r", ["-assert-count 1 t:TDP36K a:is_split=0 %i"]),
# sharing a TDP36K sets correct port widths
([("ADDRESS_WIDTH_A", 10), ("DATA_WIDTH_A", 18), ("DATA_WIDTH_B", 18), ("ADDRESS_WIDTH_B", 10)], "double_sync_ram_sdp",
["-assert-count 1 t:TDP36K a:port_a1_width=18 %i a:port_a2_width=18 %i",
"-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH", 10), ("DATA_WIDTH", 18)], "double_sync_ram_tdp",
["-assert-count 1 t:TDP36K a:port_a1_width=18 %i a:port_a2_width=18 %i",
"-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH_A", 10), ("DATA_WIDTH_A", 16), ("DATA_WIDTH_B", 8), ("ADDRESS_WIDTH_B", 11)], "double_sync_ram_sdp",
["-assert-count 1 t:TDP36K a:port_a1_width=18 %i a:port_a2_width=9 %i "
+ "t:TDP36K a:port_a2_width=18 %i a:port_a1_width=9 %i %u",
"-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH_A", 12), ("DATA_WIDTH_A", 4), ("DATA_WIDTH_B", 12), ("ADDRESS_WIDTH_B", 10)], "double_sync_ram_sdp",
["-assert-count 1 t:TDP36K a:port_a1_width=4 %i a:port_a2_width=18 %i "
+ "t:TDP36K a:port_a2_width=4 %i a:port_a1_width=18 %i %u",
"-assert-count 1 t:TDP36K"]),
([("ADDRESS_WIDTH_A", 13), ("DATA_WIDTH_A", 2), ("DATA_WIDTH_B", 1), ("ADDRESS_WIDTH_B", 14)], "double_sync_ram_sdp",
["-assert-count 1 t:TDP36K a:port_a1_width=2 %i a:port_a2_width=1 %i "
+ "t:TDP36K a:port_a2_width=2 %i a:port_a1_width=1 %i %u",
"-assert-count 1 t:TDP36K"]),
]
sim_template = """\
design -stash synthesized
design -copy-from synthesized -as {top}_synth {top}
design -delete synthesized
read_verilog +/quicklogic/common/cells_sim.v +/quicklogic/qlf_k6n10f/cells_sim.v +/quicklogic/qlf_k6n10f/brams_sim.v +/quicklogic/qlf_k6n10f/sram1024x18_mem.v +/quicklogic/qlf_k6n10f/ufifo_ctl.v +/quicklogic/qlf_k6n10f/TDP18K_FIFO.v
read_verilog <<EOF
`define MEM_TEST_VECTOR {mem_test_vector}
`define UUT_SUBMODULE \\
{uut_submodule}
EOF
read_verilog -defer -formal mem_tb.v
chparam{param_str} -set VECTORLEN {vectorlen} TB
hierarchy -top TB -check
prep
log ** CHECKING SIMULATION FOR TEST {top} WITH PARAMS{param_str}
sim -clock clk -n {vectorlen} -assert
"""
sync_ram_sdp_submodule = """\
sync_ram_sdp_synth uut (\\
.clk(clk),\\
.address_in_r(ra_a),\\
.data_out(rq_a),\\
.write_enable(wce_a),\\
.address_in_w(wa_a),\\
.data_in(wd_a)\\
);\
"""
sync_ram_tdp_submodule = """\
sync_ram_tdp_synth uut (\\
.clk_a(clk),\\
.clk_b(clk),\\
.write_enable_a(wce_a),\\
.write_enable_b(wce_b),\\
.read_enable_a(rce_a),\\
.read_enable_b(rce_b),\\
.addr_a(ra_a),\\
.addr_b(ra_b),\\
.read_data_a(rq_a),\\
.read_data_b(rq_b),\\
.write_data_a(wd_a),\\
.write_data_b(wd_b)\\
);\
"""
sync_ram_sdp_wwr_submodule = """\
sync_ram_sdp_wwr_synth uut (\\
.clk_w(clk),\\
.clk_r(clk),\\
.write_enable(wce_a),\\
.data_in(wd_a),\\
.address_in_w(wa_a),\\
.address_in_r(ra_a),\\
.data_out(rq_a)\\
);\
"""
sync_ram_sdp_wrr_submodule = """\
sync_ram_sdp_wrr_synth uut (\\
.clk_w(clk),\\
.clk_r(clk),\\
.write_enable(wce_a),\\
.data_in(wd_a),\\
.address_in_w(wa_a),\\
.address_in_r(ra_a),\\
.data_out(rq_a)\\
);\
"""
double_sync_ram_sdp_submodule = """\
double_sync_ram_sdp_synth uut (\\
.clk_a(clk),\\
.write_enable_a(wce_a),\\
.address_in_w_a(wa_a),\\
.address_in_r_a(ra_a),\\
.data_in_a(wd_a),\\
.data_out_a(rq_a),\\
.clk_b(clk),\\
.write_enable_b(wce_b),\\
.address_in_w_b(wa_b),\\
.address_in_r_b(ra_b),\\
.data_in_b(wd_b),\\
.data_out_b(rq_b)\\
);\
"""
@dataclass
class TestClass:
params: dict[str, int]
top: str
assertions: list[str]
test_steps: None | list[dict[str, int]]
test_val_map = {
"rce_a": "rce_a_testvector",
"ra_a": "ra_a_testvector",
"rq_a": "rq_a_expected",
"wce_a": "wce_a_testvector",
"wa_a": "wa_a_testvector",
"wd_a": "wd_a_testvector",
"rce_b": "rce_b_testvector",
"ra_b": "ra_b_testvector",
"rq_b": "rq_b_expected",
"wce_b": "wce_b_testvector",
"wa_b": "wa_b_testvector",
"wd_b": "wd_b_testvector",
}
sim_tests: list[TestClass] = [
TestClass( # basic SDP test
# note that the common SDP model reads every cycle, but the testbench
# still uses the rce signal to check read assertions
params={"ADDRESS_WIDTH": 10, "DATA_WIDTH": 36},
top="sync_ram_sdp",
assertions=[],
test_steps=[
{"wce_a": 1, "wa_a": 0x0A, "wd_a": 0xdeadbeef},
{"wce_a": 1, "wa_a": 0xBA, "wd_a": 0x5a5a5a5a},
{"wce_a": 1, "wa_a": 0xFF, "wd_a": 0},
{"rce_a": 1, "ra_a": 0x0A},
{"rq_a": 0xdeadbeef},
{"rce_a": 1, "ra_a": 0xFF},
{"rq_a": 0},
]
),
TestClass( # SDP read before write
params={"ADDRESS_WIDTH": 4, "DATA_WIDTH": 16},
top="sync_ram_sdp",
assertions=[],
test_steps=[
{"wce_a": 1, "wa_a": 0xA, "wd_a": 0x1234},
{"wce_a": 1, "wa_a": 0xA, "wd_a": 0x5678, "rce_a": 1, "ra_a": 0xA},
{"rq_a": 0x1234, "rce_a": 1, "ra_a": 0xA},
{"rq_a": 0x5678},
]
),
TestClass( # basic TDP test
# note that the testbench uses ra and wa, while the common TDP model
# uses a shared address
params={"ADDRESS_WIDTH": 10, "DATA_WIDTH": 36},
top="sync_ram_tdp",
assertions=[],
test_steps=[
{"wce_a": 1, "ra_a": 0x0A, "wce_b": 1, "ra_b": 0xBA,
"wd_a": 0xdeadbeef, "wd_b": 0x5a5a5a5a},
{"wce_a": 1, "ra_a": 0xFF,
"wd_a": 0},
{"rce_a": 1, "ra_a": 0x0A, "rce_b": 1, "ra_b": 0x0A},
{"rq_a": 0xdeadbeef, "rq_b": 0xdeadbeef},
{"rce_a": 1, "ra_a": 0xFF, "rce_b": 1, "ra_b": 0xBA},
{"rq_a": 0, "rq_b": 0x5a5a5a5a},
]
),
TestClass( # TDP with truncation
params={"ADDRESS_WIDTH": 4, "DATA_WIDTH": 16},
top="sync_ram_tdp",
assertions=[],
test_steps=[
{"wce_a": 1, "ra_a": 0x0F, "wce_b": 1, "ra_b": 0xBA,
"wd_a": 0xdeadbeef, "wd_b": 0x5a5a5a5a},
{"wce_a": 1, "ra_a": 0xFF,
"wd_a": 0},
{"rce_a": 1, "ra_a": 0x0F, "rce_b": 1, "ra_b": 0x0A},
{"rq_a": 0, "rq_b": 0x00005a5a},
]
),
TestClass( # TDP read before write
# note that the testbench uses rce and wce, while the common TDP model
# uses a single enable for write, with reads on no write
params={"ADDRESS_WIDTH": 10, "DATA_WIDTH": 36},
top="sync_ram_tdp",
assertions=[],
test_steps=[
{"wce_a": 1, "ra_a": 0x0A, "wce_b": 1, "ra_b": 0xBA,
"wd_a": 0xdeadbeef, "wd_b": 0x5a5a5a5a},
{"wce_a": 1, "ra_a": 0xBA, "rce_b": 1, "ra_b": 0xBA,
"wd_a": 0xa5a5a5a5},
{ "rq_b": 0x5a5a5a5a},
{"rce_a": 1, "ra_a": 0x0A, "rce_b": 1, "ra_b": 0x0A},
{"rq_a": 0xdeadbeef, "rq_b": 0xdeadbeef},
{ "rce_b": 1, "ra_b": 0xBA},
{ "rq_b": 0xa5a5a5a5},
]
),
TestClass( # 2x wide write
params={"ADDRESS_WIDTH": 11, "DATA_WIDTH": 18, "SHIFT_VAL": 1},
top="sync_ram_sdp_wwr",
assertions=[],
test_steps=[
{"wce_a": 1, "wa_a": 0b0000000001, "wd_a": 0xdeadbeef},
{"rce_a": 0, "ra_a": 0b00000000010},
{"rq_a": 0xdead},
{"rce_a": 0, "ra_a": 0b00000000011},
{"rq_a": 0xbeef},
]
),
TestClass( # 4x wide write
params={"ADDRESS_WIDTH": 10, "DATA_WIDTH": 8, "SHIFT_VAL": 2},
top="sync_ram_sdp_wwr",
assertions=[],
test_steps=[
{"wce_a": 1, "wa_a": 0b000100001, "wd_a": 0xdeadbeef},
{"rce_a": 0, "ra_a": 0b00010000100},
{"rq_a": 0xde},
{"rce_a": 0, "ra_a": 0b00010000101},
{"rq_a": 0xad},
{"rce_a": 0, "ra_a": 0b00010000110},
{"rq_a": 0xbe},
{"rce_a": 0, "ra_a": 0b00010000111},
{"rq_a": 0xef},
]
),
TestClass( # 2x wide read
params={"ADDRESS_WIDTH": 11, "DATA_WIDTH": 18, "SHIFT_VAL": 1},
top="sync_ram_sdp_wrr",
assertions=[],
test_steps=[
{"wce_a": 1, "wa_a": 0b00000000010, "wd_a": 0xdead},
{"wce_a": 1, "wa_a": 0b00000000011, "wd_a": 0xbeef},
{"rce_a": 0, "ra_a": 0b0000000001},
{"rq_a": 0xdeadbeef},
]
),
TestClass( # 4x wide read
params={"ADDRESS_WIDTH": 10, "DATA_WIDTH": 8, "SHIFT_VAL": 2},
top="sync_ram_sdp_wrr",
assertions=[],
test_steps=[
{"wce_a": 1, "wa_a": 0b00010000100, "wd_a": 0xde},
{"wce_a": 1, "wa_a": 0b00010000101, "wd_a": 0xad},
{"wce_a": 1, "wa_a": 0b00010000110, "wd_a": 0xbe},
{"wce_a": 1, "wa_a": 0b00010000111, "wd_a": 0xef},
{"rce_a": 0, "ra_a": 0b000100001},
{"rq_a": 0xdeadbeef},
]
),
TestClass( # basic split SDP test
params={"ADDRESS_WIDTH_A": 10, "DATA_WIDTH_A": 16,
"ADDRESS_WIDTH_B": 10, "DATA_WIDTH_B": 16},
top="double_sync_ram_sdp",
assertions=[],
test_steps=[
{"wce_a": 1, "wa_a": 0x0A, "wce_b": 1, "wa_b": 0xBA,
"wd_a": 0x1234, "wd_b": 0x4567},
{"wce_a": 1, "wa_a": 0xFF, "wce_b": 1, "wa_b": 0x0A,
"wd_a": 0, "wd_b": 0xbeef},
{"rce_a": 1, "ra_a": 0x0A, "rce_b": 1, "ra_b": 0x0A},
{"rq_a": 0x1234, "rq_b": 0xbeef},
]
),
]
for (params, top, assertions) in blockram_tests:
sim_test = TestClass(
params=dict(params),
top=top,
assertions=assertions,
test_steps=None
)
sim_tests.append(sim_test)
i = 0
j = 0
max_j = 16
f = None
for sim_test in sim_tests:
# format params
param_str = ""
for (key, val) in sim_test.params.items():
param_str += f" -set {key} {val}"
# resolve top module wildcards
top_list = [sim_test.top]
if "*dp" in sim_test.top:
top_list += [
sim_test.top.replace("*dp", dp_sub) for dp_sub in ["sdp", "tdp"]
]
if "w*r" in sim_test.top:
top_list += [
sim_test.top.replace("w*r", wr_sub) for wr_sub in ["wwr", "wrr"]
]
if len(top_list) > 1:
top_list.pop(0)
# iterate over string substitutions
for top in top_list:
# limit number of tests per file to allow parallel make
if not f:
fn = f"t_mem{i}.ys"
f = open(fn, mode="w")
j = 0
# output yosys script test file
print(
blockram_template.format(param_str=param_str, top=top),
file=f
)
for assertion in sim_test.assertions:
print("log ** CHECKING CELL COUNTS FOR TEST {top} WITH PARAMS{param_str}".format(param_str=param_str, top=top), file=f)
print("select {}".format(assertion), file=f)
print("", file=f)
# prepare simulation tests
test_steps = sim_test.test_steps
if test_steps:
if top == "sync_ram_sdp":
uut_submodule = sync_ram_sdp_submodule
elif top == "sync_ram_tdp":
uut_submodule = sync_ram_tdp_submodule
elif top == "sync_ram_sdp_wwr":
uut_submodule = sync_ram_sdp_wwr_submodule
elif top == "sync_ram_sdp_wrr":
uut_submodule = sync_ram_sdp_wrr_submodule
elif top == "double_sync_ram_sdp":
uut_submodule = double_sync_ram_sdp_submodule
else:
raise NotImplementedError(f"missing submodule header for {top}")
mem_test_vector = ""
for step, test in enumerate(test_steps):
for key, val in test.items():
key = test_val_map[key]
mem_test_vector += f"\\\n{key}[{step}] = 'h{val:x};"
print(
sim_template.format(
top=top,
mem_test_vector=mem_test_vector,
uut_submodule=uut_submodule,
param_str=param_str,
vectorlen=len(test_steps) + 2
), file=f
)
# simulation counts for 2 tests
j += 1
# increment test counter
j += 1
if j >= max_j:
f = f.close()
i += 1
if f:
f.close()

View File

@ -0,0 +1,84 @@
module TB(input clk);
parameter ADDRESS_WIDTH = 10;
parameter ADDRESS_WIDTH_A = ADDRESS_WIDTH;
parameter ADDRESS_WIDTH_B = ADDRESS_WIDTH;
parameter DATA_WIDTH = 36;
parameter DATA_WIDTH_A = DATA_WIDTH;
parameter DATA_WIDTH_B = DATA_WIDTH;
parameter VECTORLEN = 16;
parameter SHIFT_VAL = 0;
// intentionally keep expected values at full width precision to allow testing
// of truncation
localparam MAX_WIDTH = 36;
reg rce_a_testvector [VECTORLEN-1:0];
reg [ADDRESS_WIDTH_A-1:0] ra_a_testvector [VECTORLEN-1:0];
reg [MAX_WIDTH-1:0] rq_a_expected [VECTORLEN-1:0];
reg wce_a_testvector [VECTORLEN-1:0];
reg [ADDRESS_WIDTH_A-1:0] wa_a_testvector [VECTORLEN-1:0];
reg [DATA_WIDTH_A-1:0] wd_a_testvector [VECTORLEN-1:0];
reg rce_b_testvector [VECTORLEN-1:0];
reg [ADDRESS_WIDTH_B-1:0] ra_b_testvector [VECTORLEN-1:0];
reg [MAX_WIDTH-1:0] rq_b_expected [VECTORLEN-1:0];
reg wce_b_testvector [VECTORLEN-1:0];
reg [ADDRESS_WIDTH_B-1:0] wa_b_testvector [VECTORLEN-1:0];
reg [DATA_WIDTH_B-1:0] wd_b_testvector [VECTORLEN-1:0];
reg [$clog2(VECTORLEN)-1:0] i = 0;
integer j;
initial begin
for (j = 0; j < VECTORLEN; j = j + 1) begin
rce_a_testvector[j] = 1'b0;
ra_a_testvector[j] = 10'h0;
wce_a_testvector[j] = 1'b0;
wa_a_testvector[j] = 10'h0;
rce_b_testvector[j] = 1'b0;
ra_b_testvector[j] = 10'h0;
wce_b_testvector[j] = 1'b0;
wa_b_testvector[j] = 10'h0;
end
`MEM_TEST_VECTOR
end
wire rce_a = rce_a_testvector[i];
wire [ADDRESS_WIDTH_A-1:0] ra_a = ra_a_testvector[i];
wire [MAX_WIDTH-1:0] rq_a_e = rq_a_expected[i];
wire [DATA_WIDTH_A-1:0] rq_a;
wire wce_a = wce_a_testvector[i];
wire [ADDRESS_WIDTH_A-1:0] wa_a = wa_a_testvector[i];
wire [DATA_WIDTH_A-1:0] wd_a = wd_a_testvector[i];
wire rce_b = rce_b_testvector[i];
wire [ADDRESS_WIDTH_B-1:0] ra_b = ra_b_testvector[i];
wire [MAX_WIDTH-1:0] rq_b_e = rq_b_expected[i];
wire [DATA_WIDTH_B-1:0] rq_b;
wire wce_b = wce_b_testvector[i];
wire [ADDRESS_WIDTH_B-1:0] wa_b = wa_b_testvector[i];
wire [DATA_WIDTH_B-1:0] wd_b = wd_b_testvector[i];
`UUT_SUBMODULE
always @(posedge clk) begin
if (i < VECTORLEN-1) begin
if (i > 0) begin
if($past(rce_a))
assert(rq_a == rq_a_e);
if($past(rce_b))
assert(rq_b == rq_b_e);
end
i <= i + 1;
end
end
endmodule

View File

@ -0,0 +1,50 @@
module top(clk);
parameter DEPTH_LOG2 = 10;
parameter WIDTH = 36;
parameter PRIME = 237481091;
localparam DEPTH = 2**DEPTH_LOG2;
input wire clk;
(* syn_ramstyle = "block_ram" *)
reg [WIDTH-1:0] mem [DEPTH-1:0];
integer i;
initial begin
for (i = 0; i < DEPTH; i = i + 1) begin
// Make up data by multiplying a large prime with the address,
// then cropping and retaining the lower bits
mem[i] = PRIME * i;
end
end
reg [DEPTH_LOG2-1:0] counter = 0;
reg done = 1'b0;
reg did_read = 1'b0;
reg [DEPTH_LOG2-1:0] read_addr;
reg [WIDTH-1:0] read_val;
always @(posedge clk) begin
if (!done) begin
did_read <= 1'b1;
read_addr <= counter;
read_val <= mem[counter];
end else begin
did_read <= 1'b0;
end
if (!done)
counter = counter + 1;
if (counter == 0)
done = 1;
end
wire [WIDTH-1:0] expect_val = PRIME * read_addr;
always @(posedge clk) begin
if (did_read) begin
$display("addr %x expected %x actual %x", read_addr, expect_val, read_val);
assert(read_val == expect_val);
end
end
endmodule

View File

@ -0,0 +1,14 @@
read_verilog -sv meminit.v
chparam -set DEPTH_LOG2 3 -set WIDTH 36
prep
opt_dff
prep -rdff
synth_quicklogic -family qlf_k6n10f -run map_bram:map_bram
select -assert-none t:$mem_v2 t:$mem
select -assert-count 1 t:TDP36K
select -assert-count 1 t:TDP36K a:is_split=0 %i
select -assert-count 1 t:TDP36K a:was_split_candidate=0 %i
read_verilog +/quicklogic/common/cells_sim.v +/quicklogic/qlf_k6n10f/cells_sim.v +/quicklogic/qlf_k6n10f/brams_sim.v +/quicklogic/qlf_k6n10f/sram1024x18_mem.v +/quicklogic/qlf_k6n10f/ufifo_ctl.v +/quicklogic/qlf_k6n10f/TDP18K_FIFO.v
prep
hierarchy -top top
sim -assert -q -n 12 -clock clk

View File

@ -0,0 +1,40 @@
read_verilog ../../common/mux.v
design -save read
hierarchy -top mux2
proc
equiv_opt -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 mux2 # Constrain all select calls below inside the top module
select -assert-count 1 t:$lut r:WIDTH=3 %i
select -assert-none t:$lut r:WIDTH=3 %i %% t:* %D
design -load read
hierarchy -top mux4
proc
equiv_opt -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 mux4 # Constrain all select calls below inside the top module
select -assert-count 1 t:$lut r:WIDTH=6 %i
select -assert-none t:$lut r:WIDTH=6 %i %% t:* %D
design -load read
hierarchy -top mux8
proc
equiv_opt -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 mux8 # Constrain all select calls below inside the top module
select -assert-count 2 t:$lut r:WIDTH=6 %i
select -assert-count 1 t:$lut r:WIDTH=3 %i
select -assert-none t:$lut r:WIDTH=6 r:WIDTH=3 %u %i %% t:* %D
design -load read
hierarchy -top mux16
proc
equiv_opt -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 mux16 # Constrain all select calls below inside the top module
select -assert-max 5 t:$lut r:WIDTH=6 %i # OOT flow does 2
select -assert-max 1 t:$lut r:WIDTH=3 %i # and here 1
select -assert-max 1 t:$lut r:WIDTH=4 r:WIDTH=5 %u %i
select -assert-none t:$lut r:WIDTH>2 %i %% t:* %D

View File

@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -eu
python3 mem_gen.py
source ../../../gen-tests-makefile.sh
run_tests --yosys-scripts --bash