add qlf_k6n10f architecture + bram inference

(Copied from QuickLogic Yosys plugin repo)
This commit is contained in:
N. Engelhardt 2023-11-27 09:42:40 +01:00 committed by Martin Povišer
parent 98769010af
commit 48c1fdc33d
13 changed files with 90338 additions and 21 deletions

View File

@ -1,4 +1,6 @@
OBJS += techlibs/quicklogic/synth_quicklogic.o
OBJS += techlibs/quicklogic/ql-bram-merge.o
OBJS += techlibs/quicklogic/quicklogic_eqn.o
$(eval $(call add_share_file,share/quicklogic/common,techlibs/quicklogic/common/cells_sim.v))
@ -10,3 +12,13 @@ $(eval $(call add_share_file,share/quicklogic/pp3,techlibs/quicklogic/pp3/cells_
$(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_final_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))

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

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,376 @@
// 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 *)
(* blackbox *)
(* 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,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,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,457 @@
// 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
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 *)
(* port_a_width = PORT_A_WIDTH *)
(* port_b_width = PORT_B_WIDTH *)
TDP36K _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
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 _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,100 @@
/*
* 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
struct QuicklogicEqnPass : public Pass {
QuicklogicEqnPass() : Pass("quicklogic_eqn", "Quicklogic: Calculate equations for luts") {}
void help() override
{
log("\n");
log(" quicklogic_eqn [selection]\n");
log("\n");
log("Calculate equations for luts since bitstream generator depends on it.\n");
log("\n");
}
Const init2eqn(Const init, int inputs)
{
std::string init_bits = init.as_string();
const char *names[] = {"I0", "I1", "I2", "I3", "I4"};
std::string eqn;
int width = (int)pow(2, inputs);
for (int i = 0; i < width; i++) {
if (init_bits[width - 1 - i] == '1') {
eqn += "(";
for (int j = 0; j < inputs; j++) {
if (i & (1 << j))
eqn += names[j];
else
eqn += std::string("~") + names[j];
if (j != (inputs - 1))
eqn += "*";
}
eqn += ")+";
}
}
if (eqn.empty())
return Const("0");
eqn = eqn.substr(0, eqn.length() - 1);
return Const(eqn);
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing Quicklogic_EQN pass (calculate equations for luts).\n");
extra_args(args, args.size(), design);
int cnt = 0;
for (auto module : design->selected_modules()) {
for (auto cell : module->selected_cells()) {
if (cell->type == ID(LUT1)) {
cell->setParam(ID(EQN), init2eqn(cell->getParam(ID::INIT), 1));
cnt++;
}
if (cell->type == ID(LUT2)) {
cell->setParam(ID(EQN), init2eqn(cell->getParam(ID::INIT), 2));
cnt++;
}
if (cell->type == ID(LUT3)) {
cell->setParam(ID(EQN), init2eqn(cell->getParam(ID::INIT), 3));
cnt++;
}
if (cell->type == ID(LUT4)) {
cell->setParam(ID(EQN), init2eqn(cell->getParam(ID::INIT), 4));
cnt++;
}
if (cell->type == ID(LUT5)) {
cell->setParam(ID(EQN), init2eqn(cell->getParam(ID::INIT), 5));
cnt++;
}
}
}
log_header(design, "Updated %d of LUT* elements with equation.\n", cnt);
}
} QuicklogicEqnPass;
PRIVATE_NAMESPACE_END

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,17 @@ 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(" -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");
@ -61,7 +74,7 @@ struct SynthQuickLogicPass : public ScriptPass {
}
string top_opt, blif_file, edif_file, family, currmodule, verilog_file, lib_path;
bool abc9;
bool abc9, inferAdder, nobram, bramTypes;
void clear_flags() override
{
@ -72,12 +85,26 @@ struct SynthQuickLogicPass : public ScriptPass {
currmodule = "";
family = "pp3";
abc9 = true;
inferAdder = true;
nobram = false;
bramTypes = false;
lib_path = "+/quicklogic/";
}
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++)
@ -114,6 +141,18 @@ struct SynthQuickLogicPass : public ScriptPass {
abc9 = false;
continue;
}
if (args[argidx] == "-nocarry") {
inferAdder = false;
continue;
}
if (args[argidx] == "-nobram") {
nobram = true;
continue;
}
if (args[argidx] == "-bramtypes") {
bramTypes = true;
continue;
}
break;
}
extra_args(args, argidx, design);
@ -121,7 +160,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) {
@ -144,14 +183,22 @@ struct SynthQuickLogicPass : public ScriptPass {
}
if (check_label("begin")) {
run(stringf("read_verilog -lib -specify +/quicklogic/common/cells_sim.v +/quicklogic/%s/cells_sim.v", family.c_str()));
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());
}
run(read_simlibs);
run(stringf("hierarchy -check %s", help_mode ? "-top <top>" : top_opt.c_str()));
}
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");
@ -176,6 +223,71 @@ struct SynthQuickLogicPass : public ScriptPass {
run("opt_clean");
}
if (check_label("map_bram", "(for qlf_k6n10f, skip if -no_bram)") && (help_mode || family == "qlf_k6n10f")) {
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");
run("techmap -map " + lib_path + family + "/brams_final_map.v");
if (help_mode) {
run("chtype -set TDP36K_<mode> t:TDP36K a:<mode>", "(if -bram_types)");
}
else if (bramTypes) {
for (int a_dwidth : {1, 2, 4, 9, 18, 36})
for (int b_dwidth: {1, 2, 4, 9, 18, 36}) {
run(stringf("chtype -set TDP36K_BRAM_A_X%d_B_X%d_nonsplit t:TDP36K a:is_inferred=0 %%i "
"a:is_fifo=0 %%i a:port_a_dwidth=%d %%i a:port_b_dwidth=%d %%i",
a_dwidth, b_dwidth, a_dwidth, b_dwidth));
run(stringf("chtype -set TDP36K_FIFO_ASYNC_A_X%d_B_X%d_nonsplit t:TDP36K a:is_inferred=0 %%i "
"a:is_fifo=1 %%i a:sync_fifo=0 %%i a:port_a_dwidth=%d %%i a:port_b_dwidth=%d %%i",
a_dwidth, b_dwidth, a_dwidth, b_dwidth));
run(stringf("chtype -set TDP36K_FIFO_SYNC_A_X%d_B_X%d_nonsplit t:TDP36K a:is_inferred=0 %%i "
"a:is_fifo=1 %%i a:sync_fifo=1 %%i a:port_a_dwidth=%d %%i a:port_b_dwidth=%d %%i",
a_dwidth, b_dwidth, a_dwidth, b_dwidth));
}
for (int a1_dwidth : {1, 2, 4, 9, 18})
for (int b1_dwidth: {1, 2, 4, 9, 18})
for (int a2_dwidth : {1, 2, 4, 9, 18})
for (int b2_dwidth: {1, 2, 4, 9, 18}) {
run(stringf("chtype -set TDP36K_BRAM_A1_X%d_B1_X%d_A2_X%d_B2_X%d_split t:TDP36K a:is_inferred=0 %%i "
"a:is_split=1 %%i a:is_fifo=0 %%i "
"a:port_a1_dwidth=%d %%i a:port_b1_dwidth=%d %%i a:port_a2_dwidth=%d %%i a:port_b2_dwidth=%d %%i",
a1_dwidth, b1_dwidth, a2_dwidth, b2_dwidth, a1_dwidth, b1_dwidth, a2_dwidth, b2_dwidth));
run(stringf("chtype -set TDP36K_FIFO_ASYNC_A1_X%d_B1_X%d_A2_X%d_B2_X%d_split t:TDP36K a:is_inferred=0 %%i "
"a:is_split=1 %%i a:is_fifo=1 %%i a:sync_fifo=0 %%i "
"a:port_a1_dwidth=%d %%i a:port_b1_dwidth=%d %%i a:port_a2_dwidth=%d %%i a:port_b2_dwidth=%d %%i",
a1_dwidth, b1_dwidth, a2_dwidth, b2_dwidth, a1_dwidth, b1_dwidth, a2_dwidth, b2_dwidth));
run(stringf("chtype -set TDP36K_FIFO_SYNC_A1_X%d_B1_X%d_A2_X%d_B2_X%d_split t:TDP36K a:is_inferred=0 %%i "
"a:is_split=1 %%i a:is_fifo=1 %%i a:sync_fifo=1 %%i "
"a:port_a1_dwidth=%d %%i a:port_b1_dwidth=%d %%i a:port_a2_dwidth=%d %%i a:port_b2_dwidth=%d %%i",
a1_dwidth, b1_dwidth, a2_dwidth, b2_dwidth, a1_dwidth, b1_dwidth, a2_dwidth, b2_dwidth));
}
for (int a_width : {1, 2, 4, 9, 18, 36})
for (int b_width: {1, 2, 4, 9, 18, 36}) {
run(stringf("chtype -set TDP36K_BRAM_A_X%d_B_X%d_nonsplit t:TDP36K a:is_inferred=1 %%i "
"a:port_a_width=%d %%i a:port_b_width=%d %%i",
a_width, b_width, a_width, b_width));
}
for (int a1_width : {1, 2, 4, 9, 18})
for (int b1_width: {1, 2, 4, 9, 18})
for (int a2_width : {1, 2, 4, 9, 18})
for (int b2_width: {1, 2, 4, 9, 18}) {
run(stringf("chtype -set TDP36K_BRAM_A1_X%d_B1_X%d_A2_X%d_B2_X%d_split t:TDP36K a:is_inferred=1 %%i "
"a:port_a1_width=%d %%i a:port_b1_width=%d %%i a:port_a2_width=%d %%i a:port_b2_width=%d %%i",
a1_width, b1_width, a2_width, b2_width, a1_width, b1_width, a2_width, b2_width));
}
}
}
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 "
@ -185,36 +297,65 @@ 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)");
}
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("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(stringf("read_verilog -lib -specify -icells +/quicklogic/%s/abc9_model.v", family.c_str()));
run(stringf("techmap -map +/quicklogic/%s/abc9_map.v", family.c_str()));
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(stringf("techmap -map +/quicklogic/%s/abc9_unmap.v", family.c_str()));
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")) {
@ -224,14 +365,18 @@ 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");