Merge pull request #4474 from tony-min-1/mchp

Add PolarFire FPGA support
This commit is contained in:
N. Engelhardt 2024-07-29 15:28:44 +02:00 committed by GitHub
commit 9f869b265c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 4620 additions and 0 deletions

View File

@ -879,6 +879,7 @@ endif
+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/arch/microchip && bash run-test.sh $(SEEDOPT)
+cd tests/rpc && bash run-test.sh
+cd tests/memfile && bash run-test.sh
+cd tests/verilog && bash run-test.sh

View File

@ -37,6 +37,17 @@ $(eval $(call add_extra_objs,passes/pmgen/xilinx_dsp_cascade_pm.h))
# --------------------------------------
OBJS += passes/pmgen/microchip_dsp.o
GENFILES += passes/pmgen/microchip_dsp_pm.h
GENFILES += passes/pmgen/microchip_dsp_CREG_pm.h
GENFILES += passes/pmgen/microchip_dsp_cascade_pm.h
passes/pmgen/microchip_dsp.o: passes/pmgen/microchip_dsp_pm.h passes/pmgen/microchip_dsp_CREG_pm.h passes/pmgen/microchip_dsp_cascade_pm.h
$(eval $(call add_extra_objs,passes/pmgen/microchip_dsp_pm.h))
$(eval $(call add_extra_objs,passes/pmgen/microchip_dsp_CREG_pm.h))
$(eval $(call add_extra_objs,passes/pmgen/microchip_dsp_cascade_pm.h))
# --------------------------------------
OBJS += passes/pmgen/peepopt.o
GENFILES += passes/pmgen/peepopt_pm.h
passes/pmgen/peepopt.o: passes/pmgen/peepopt_pm.h

View File

@ -0,0 +1,362 @@
/*
ISC License
Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
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/sigtools.h"
#include "kernel/yosys.h"
#include <deque>
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
#include "passes/pmgen/microchip_dsp_CREG_pm.h"
#include "passes/pmgen/microchip_dsp_cascade_pm.h"
#include "passes/pmgen/microchip_dsp_pm.h"
void microchip_dsp_pack(microchip_dsp_pm &pm)
{
auto &st = pm.st_microchip_dsp_pack;
log("Analysing %s.%s for Microchip MACC_PA packing.\n", log_id(pm.module), log_id(st.dsp));
Cell *cell = st.dsp;
// pack pre-adder
if (st.preAdderStatic) {
SigSpec &pasub = cell->connections_.at(ID(PASUB));
log(" static PASUB preadder %s (%s)\n", log_id(st.preAdderStatic), log_id(st.preAdderStatic->type));
bool D_SIGNED = st.preAdderStatic->getParam(ID::B_SIGNED).as_bool();
bool B_SIGNED = st.preAdderStatic->getParam(ID::A_SIGNED).as_bool();
st.sigB.extend_u0(18, B_SIGNED);
st.sigD.extend_u0(18, D_SIGNED);
if (st.moveBtoA) {
cell->setPort(ID::A, st.sigA); // if pre-adder feeds into A, original sigB will be moved to port A
}
cell->setPort(ID::B, st.sigB);
cell->setPort(ID::D, st.sigD);
// MACC_PA supports both addition and subtraction with the pre-adder.
// Affects the sign of the 'D' port.
if (st.preAdderStatic->type == ID($add))
pasub[0] = State::S0;
else if (st.preAdderStatic->type == ID($sub))
pasub[0] = State::S1;
else
log_assert(!"strange pre-adder type");
pm.autoremove(st.preAdderStatic);
}
// pack post-adder
if (st.postAdderStatic) {
log(" postadder %s (%s)\n", log_id(st.postAdderStatic), log_id(st.postAdderStatic->type));
SigSpec &sub = cell->connections_.at(ID(SUB));
// Post-adder in MACC_PA also supports subtraction
// Determines the sign of the output from the multiplier.
if (st.postAdderStatic->type == ID($add))
sub[0] = State::S0;
else if (st.postAdderStatic->type == ID($sub))
sub[0] = State::S1;
else
log_assert(!"strange post-adder type");
if (st.useFeedBack) {
cell->setPort(ID(CDIN_FDBK_SEL), {State::S0, State::S1});
} else {
st.sigC.extend_u0(48, st.postAdderStatic->getParam(ID::A_SIGNED).as_bool());
cell->setPort(ID::C, st.sigC);
}
pm.autoremove(st.postAdderStatic);
}
// pack registers
if (st.clock != SigBit()) {
cell->setPort(ID::CLK, st.clock);
// function to absorb a register
auto f = [&pm, cell](SigSpec &A, Cell *ff, IdString ceport, IdString rstport, IdString bypass) {
// input/output ports
SigSpec D = ff->getPort(ID::D);
SigSpec Q = pm.sigmap(ff->getPort(ID::Q));
if (!A.empty())
A.replace(Q, D);
if (rstport != IdString()) {
if (ff->type.in(ID($sdff), ID($sdffe))) {
SigSpec srst = ff->getPort(ID::SRST);
bool rstpol_n = !ff->getParam(ID::SRST_POLARITY).as_bool();
// active low sync rst
cell->setPort(rstport, rstpol_n ? srst : pm.module->Not(NEW_ID, srst));
} else if (ff->type.in(ID($adff), ID($adffe))) {
SigSpec arst = ff->getPort(ID::ARST);
bool rstpol_n = !ff->getParam(ID::ARST_POLARITY).as_bool();
// active low async rst
cell->setPort(rstport, rstpol_n ? arst : pm.module->Not(NEW_ID, arst));
} else {
// active low async/sync rst
cell->setPort(rstport, State::S1);
}
}
if (ff->type.in(ID($dffe), ID($sdffe), ID($adffe))) {
SigSpec ce = ff->getPort(ID::EN);
bool cepol = ff->getParam(ID::EN_POLARITY).as_bool();
// enables are all active high
cell->setPort(ceport, cepol ? ce : pm.module->Not(NEW_ID, ce));
} else {
// enables are all active high
cell->setPort(ceport, State::S1);
}
// bypass set to 0
cell->setPort(bypass, State::S0);
for (auto c : Q.chunks()) {
auto it = c.wire->attributes.find(ID::init);
if (it == c.wire->attributes.end())
continue;
for (int i = c.offset; i < c.offset + c.width; i++) {
log_assert(it->second[i] == State::S0 || it->second[i] == State::Sx);
it->second[i] = State::Sx;
}
}
};
// NOTE: flops are not autoremoved because it is possible that they
// are only partially absorbed into DSP, or have fanouts.
if (st.ffA) {
SigSpec A = cell->getPort(ID::A);
if (st.ffA) {
f(A, st.ffA, ID(A_EN), ID(A_SRST_N), ID(A_BYPASS));
}
pm.add_siguser(A, cell);
cell->setPort(ID::A, A);
}
if (st.ffB) {
SigSpec B = cell->getPort(ID::B);
if (st.ffB) {
f(B, st.ffB, ID(B_EN), ID(B_SRST_N), ID(B_BYPASS));
}
pm.add_siguser(B, cell);
cell->setPort(ID::B, B);
}
if (st.ffD) {
SigSpec D = cell->getPort(ID::D);
if (st.ffD->type.in(ID($adff), ID($adffe))) {
f(D, st.ffD, ID(D_EN), ID(D_ARST_N), ID(D_BYPASS));
} else {
f(D, st.ffD, ID(D_EN), ID(D_SRST_N), ID(D_BYPASS));
}
pm.add_siguser(D, cell);
cell->setPort(ID::D, D);
}
if (st.ffP) {
SigSpec P; // unused
f(P, st.ffP, ID(P_EN), ID(P_SRST_N), ID(P_BYPASS));
st.ffP->connections_.at(ID::Q).replace(st.sigP, pm.module->addWire(NEW_ID, GetSize(st.sigP)));
}
log(" clock: %s (%s)\n", log_signal(st.clock), "posedge");
if (st.ffA)
log(" \t ffA:%s\n", log_id(st.ffA));
if (st.ffB)
log(" \t ffB:%s\n", log_id(st.ffB));
if (st.ffD)
log(" \t ffD:%s\n", log_id(st.ffD));
if (st.ffP)
log(" \t ffP:%s\n", log_id(st.ffP));
}
log("\n");
SigSpec P = st.sigP;
if (GetSize(P) < 48)
P.append(pm.module->addWire(NEW_ID, 48 - GetSize(P)));
cell->setPort(ID::P, P);
pm.blacklist(cell);
}
// For packing cascaded DSPs
void microchip_dsp_packC(microchip_dsp_CREG_pm &pm)
{
auto &st = pm.st_microchip_dsp_packC;
log_debug("Analysing %s.%s for Microchip DSP packing (REG_C).\n", log_id(pm.module), log_id(st.dsp));
log_debug("ffC: %s\n", log_id(st.ffC, "--"));
Cell *cell = st.dsp;
if (st.clock != SigBit()) {
cell->setPort(ID::CLK, st.clock);
// same function as above, used for the last CREG we need to absorb
auto f = [&pm, cell](SigSpec &A, Cell *ff, IdString ceport, IdString rstport, IdString bypass) {
// input/output ports
SigSpec D = ff->getPort(ID::D);
SigSpec Q = pm.sigmap(ff->getPort(ID::Q));
if (!A.empty())
A.replace(Q, D);
if (rstport != IdString()) {
if (ff->type.in(ID($sdff), ID($sdffe))) {
SigSpec srst = ff->getPort(ID::SRST);
bool rstpol_n = !ff->getParam(ID::SRST_POLARITY).as_bool();
// active low sync rst
cell->setPort(rstport, rstpol_n ? srst : pm.module->Not(NEW_ID, srst));
} else if (ff->type.in(ID($adff), ID($adffe))) {
SigSpec arst = ff->getPort(ID::ARST);
bool rstpol_n = !ff->getParam(ID::ARST_POLARITY).as_bool();
// active low async rst
cell->setPort(rstport, rstpol_n ? arst : pm.module->Not(NEW_ID, arst));
} else {
// active low async/sync rst
cell->setPort(rstport, State::S1);
}
}
if (ff->type.in(ID($dffe), ID($sdffe), ID($adffe))) {
SigSpec ce = ff->getPort(ID::EN);
bool cepol = ff->getParam(ID::EN_POLARITY).as_bool();
// enables are all active high
cell->setPort(ceport, cepol ? ce : pm.module->Not(NEW_ID, ce));
} else {
// enables are all active high
cell->setPort(ceport, State::S1);
}
// bypass set to 0
cell->setPort(bypass, State::S0);
for (auto c : Q.chunks()) {
auto it = c.wire->attributes.find(ID::init);
if (it == c.wire->attributes.end())
continue;
for (int i = c.offset; i < c.offset + c.width; i++) {
log_assert(it->second[i] == State::S0 || it->second[i] == State::Sx);
it->second[i] = State::Sx;
}
}
};
if (st.ffC) {
SigSpec C = cell->getPort(ID::C);
if (st.ffC->type.in(ID($adff), ID($adffe))) {
f(C, st.ffC, ID(C_EN), ID(C_ARST_N), ID(C_BYPASS));
} else {
f(C, st.ffC, ID(C_EN), ID(C_SRST_N), ID(C_BYPASS));
}
pm.add_siguser(C, cell);
cell->setPort(ID::C, C);
}
log(" clock: %s (%s)", log_signal(st.clock), "posedge");
if (st.ffC)
log(" ffC:%s", log_id(st.ffC));
log("\n");
}
pm.blacklist(cell);
}
struct MicrochipDspPass : public Pass {
MicrochipDspPass() : Pass("microchip_dsp", "MICROCHIP: pack resources into DSPs") {}
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" microchip_dsp [options] [selection]\n");
log("\n");
log("Pack input registers 'A', 'B', 'C', and 'D' (with optional enable/reset),\n");
log("output register 'P' (with optional enable/reset), pre-adder and/or post-adder into\n");
log("Microchip DSP resources.\n");
log("\n");
log("Multiply-accumulate operations using the post-adder with feedback on the 'C'\n");
log("input will be folded into the DSP. In this scenario only, the 'C' input can be\n");
log("used to override the current accumulation result with a new value. This will\n");
log("be added to the multiplier result to form the next accumulation result.\n");
log("\n");
log("Use of the dedicated 'PCOUT' -> 'PCIN' cascade path is detected for 'P' -> 'C'\n");
log("connections (optionally, where 'P' is right-shifted by 17-bits and used as an\n");
log("input to the post-adder. This pattern is common for summing partial products to\n");
log("implement wide multipliers). Cascade chains are limited to a mazimum length \n");
log("of 24 cells, corresponding to PolarFire (pf) devices.\n");
log("\n");
log("This pass is a no-op if the scratchpad variable 'microchip_dsp.multonly' is set\n");
log("to 1.\n");
log("\n");
log("\n");
log(" -family {polarfire}\n");
log(" select the family to target\n");
log(" default: polarfire\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing MICROCHIP_DSP pass (pack resources into DSPs).\n");
std::string family = "polarfire";
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if ((args[argidx] == "-family") && argidx + 1 < args.size()) {
family = args[++argidx];
continue;
}
break;
}
extra_args(args, argidx, design);
for (auto module : design->selected_modules()) {
if (design->scratchpad_get_bool("microchip_dsp.multonly"))
continue;
{
// For more details on PolarFire MACC_PA, consult
// the "PolarFire FPGA Macro Library Guide"
// Main pattern matching step to capture a DSP cell.
// Match for pre-adder, post-adder, as well as
// registers 'A', 'B', 'D', and 'P'. Additionally,
// check for an accumulator pattern based on whether
// a post-adder and PREG are both present AND
// if PREG feeds into this post-adder.
microchip_dsp_pm pm(module, module->selected_cells());
pm.run_microchip_dsp_pack(microchip_dsp_pack);
}
// Separating out CREG packing is necessary since there
// is no guarantee that the cell ordering corresponds
// to the "expected" case (i.e. the order in which
// they appear in the source). There existed the possibility
// where a register got packed as a CREG into a
// downstream DSP that should have otherwise been a
// PREG of an upstream DSP that had not been visited
// yet
{
microchip_dsp_CREG_pm pm(module, module->selected_cells());
pm.run_microchip_dsp_packC(microchip_dsp_packC);
}
// Lastly, identify and utilise PCOUT -> PCIN chains
{
microchip_dsp_cascade_pm pm(module, module->selected_cells());
pm.run_microchip_dsp_cascade();
}
}
}
} MicrochipDspPass;
PRIVATE_NAMESPACE_END

View File

@ -0,0 +1,439 @@
// ISC License
//
// Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
//
// 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.
// This file describes the main pattern matcher setup (of three total) that
// forms the `microchip_dsp` pass described in microchip_dsp.cc
// At a high level, it works as follows:
// ( 1) Starting from a DSP cell. Capture DSP configurations as states
// ( 2) Match for pre-adder
// ( 3) Match for post-adder
// ( 4) Match register 'A', 'B', 'D', 'P'
// ( 5) If post-adder and PREG both present, check if PREG feeds into post-adder.
// This indicates an accumulator situation like the ASCII diagram below:
// +--------------------------------+
// |_________ |
// | /-------\ +----+ |
// +----+ +-| post- |___|PREG|---+ 'P'
// |MULT|------ | adder | +----+
// +----+ \-------/
pattern microchip_dsp_pack
state <SigBit> clock
state <SigSpec> sigA sigB sigC sigD sigP
state <Cell*> ffA ffB ffD ffP
state <Cell*> preAdderStatic postAdderStatic
state <bool> moveBtoA useFeedBack
// static ports, used to detect dsp configuration
state <SigSpec> bypassA bypassB bypassC bypassD bypassP
state <SigSpec> bypassPASUB
// Variables used for subpatterns
state <SigSpec> argQ argD
udata <bool> allowAsync
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
udata <Cell*> dff
udata <Cell*> u_preAdderStatic u_postAdderStatic
udata <IdString> u_postAddAB
state <IdString> postAddAB
// (1) Starting from a DSP cell
match dsp
select dsp->type.in(\MACC_PA)
endmatch
// detect existing signals connected to DSP
// detect configuration ports
code sigA sigB sigC sigD clock sigP
//helper function to remove unused bits
auto unextend = [](const SigSpec &sig) {
int i;
for (i = GetSize(sig)-1; i > 0; i--)
if (sig[i] != sig[i-1])
break;
// Do not remove non-const sign bit
if (sig[i].wire)
++i;
return sig.extract(0, i);
};
//unextend to remove unused bits
sigA = unextend(port(dsp, \A));
sigB = unextend(port(dsp, \B));
//update signals
sigC = port(dsp, \C, SigSpec());
sigD = port(dsp, \D, SigSpec());
SigSpec P = port(dsp, \P);
// Only care about bits that are used
int i;
for (i = GetSize(P)-1; i >= 0; i--)
if (nusers(P[i]) > 1)
break;
i++;
log_assert(nusers(P.extract_end(i)) <= 1);
// This sigP could have no users if downstream sinks (e.g. $add) is
// narrower than $mul result, for example
if (i == 0)
reject;
sigP = P.extract(0, i);
clock = port(dsp, \CLK, SigBit());
endcode
// capture static configuration ports
code bypassA bypassB bypassC bypassD bypassPASUB bypassP
bypassA = port(dsp, \A_BYPASS, SigSpec());
bypassB = port(dsp, \B_BYPASS, SigSpec());
bypassC = port(dsp, \C_BYPASS, SigSpec());
bypassD = port(dsp, \D_BYPASS, SigSpec());
bypassPASUB = port(dsp, \PASUB_BYPASS, SigSpec());
bypassP = port(dsp, \P_BYPASS, SigSpec());
endcode
// (2) Match for pre-adder
//
code sigA sigB sigD preAdderStatic moveBtoA
subpattern(preAddMatching);
preAdderStatic = u_preAdderStatic;
moveBtoA = false;
if (preAdderStatic) {
if (port(preAdderStatic, \Y) == sigA)
{
//used for packing
moveBtoA = true;
// sigA should be the input to the multiplier without the preAdd. sigB and sigD should be
//the preAdd inputs. If our "A" input into the multiplier is from the preAdd (not sigA), then
// we basically swap it.
sigA = sigB;
}
// port B of preAdderStatic must be mapped to port D of DSP for subtraction
sigD = port(preAdderStatic, \B);
sigB = port(preAdderStatic, \A);
}
endcode
// (3) Match for post-adder
//
code postAdderStatic sigP sigC
u_postAdderStatic = nullptr;
subpattern(postAddMatching);
postAdderStatic = u_postAdderStatic;
if (postAdderStatic) {
//sigC will be whichever input to the postAdder that is NOT from the multiplier
// u_postAddAB is the input to the postAdder from the multiplier
sigC = port(postAdderStatic, u_postAddAB == \A ? \B : \A);
sigP = port(postAdderStatic, \Y);
}
endcode
// (4) Matching registers
//
// 'A' input for REG_A
code argQ bypassA sigA clock ffA
if (bypassA.is_fully_ones()){
argQ = sigA;
allowAsync = false;
subpattern(in_dffe);
if (dff) {
ffA = dff;
clock = dffclock;
sigA = dffD;
}
}
endcode
// 'B' input for REG_B
code argQ bypassB sigB clock ffB
if (bypassB.is_fully_ones()){
argQ = sigB;
allowAsync = false;
subpattern(in_dffe);
if (dff) {
ffB = dff;
clock = dffclock;
sigB = dffD;
}
}
endcode
// 'D' input for REG_D
code argQ bypassP sigD clock ffD
if (bypassD.is_fully_ones()){
argQ = sigD;
allowAsync = true;
subpattern(in_dffe);
if (dff) {
ffD = dff;
clock = dffclock;
sigD = dffD;
}
}
endcode
// 'P' output for REG_P
code argD ffP sigP clock bypassP
if (bypassP.is_fully_ones() && nusers(sigP) == 2) {
argD = sigP;
allowAsync = false;
subpattern(out_dffe);
if (dff) {
ffP = dff;
clock = dffclock;
sigP = dffQ;
}
}
endcode
// (5) If post-adder and PREG both present, check if PREG feeds into post-adder via port C.
// This indicates an accumulator situation. Port C can be freed
// +--------------------------------+
// |_________ |
// | /-------\ +----+ |
// +----+ +-| post- |___|PREG|---+ 'P'
// |MULT|------ | adder | +----+
// +----+ \-------/
code useFeedBack
useFeedBack = false;
if (postAdderStatic && ffP) {
if (sigC == sigP) {
useFeedBack = true;
}
}
endcode
// if any cells are absorbed, invoke the callback function
code
if (preAdderStatic || postAdderStatic)
accept;
if (ffA || ffB || ffD || ffP)
accept;
endcode
// #######################
// Subpattern for matching against post-adder
// Match 'P' output that exclusively drives one of two inputs to an $add
// cell (post-adder).
// The other input to the adder is assumed to come in from the 'C' input
subpattern postAddMatching
arg sigP
match postAdd
select postAdd->type.in($add, $sub)
select GetSize(port(postAdd, \Y)) <= 48
// AB is the port that connects MUL to ADD
choice <IdString> AB {\A, \B}
select nusers(port(postAdd, AB)) == 2
// has one input coming from multiplier
index <SigBit> port(postAdd, AB)[0] === sigP[0]
filter GetSize(port(postAdd, AB)) >= GetSize(sigP)
filter port(postAdd, AB).extract(0, GetSize(sigP)) == sigP
// Check that remainder of AB is a sign- or zero-extension
filter port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(sigP[GetSize(sigP)-1], GetSize(port(postAdd, AB))-GetSize(sigP)) || port(postAdd, AB).extract_end(GetSize(sigP)) == SigSpec(State::S0, GetSize(port(postAdd, AB))-GetSize(sigP))
set postAddAB AB
// optional
endmatch
code
if (postAdd)
{
if (postAdd->type.in(ID($sub)) && postAddAB == \A) {
// if $sub, the multiplier output must match to $sub.B, otherwise no match
} else {
u_postAddAB = postAddAB;
u_postAdderStatic = postAdd;
}
}
endcode
// #######################
// Subpattern for matching against pre-adder
// support static PASUB only
subpattern preAddMatching
arg sigA sigB sigD bypassB bypassD bypassPASUB
code
u_preAdderStatic = nullptr;
// Ensure that preAdder not already used
// Assume we can inspect port D to see if its all zeros.
if (!(sigD.empty() || sigD.is_fully_zero())) reject;
if (!bypassB.is_fully_ones()) reject;
if (!bypassD.is_fully_ones()) reject;
if (!bypassPASUB.is_fully_ones()) reject;
endcode
match preAdd
// can handle add or sub
select preAdd->type.in($add, $sub)
// Output has to be 18 bits or less, and only has single fanout
select GetSize(port(preAdd, \Y)) <= 18
select nusers(port(preAdd, \Y)) == 2
// Adder inputs must be 18 bits or less
select GetSize(port(preAdd, \A)) <= 18
select GetSize(port(preAdd, \B)) <= 18
// Output feeds into one of multiplier input
filter port(preAdd, \Y) == sigB || port(preAdd, \Y) == sigA
// optional
endmatch
code
if (preAdd)
{
u_preAdderStatic = preAdd;
}
endcode
// #######################
// Subpattern for matching against input registers, based on knowledge of the
// 'Q' input.
subpattern in_dffe
arg argQ clock
code
dff = nullptr;
if (argQ.empty())
reject;
for (const auto &c : argQ.chunks()) {
// Abandon matches when 'Q' is a constant
if (!c.wire)
reject;
// Abandon matches when 'Q' has the keep attribute set
if (c.wire->get_bool_attribute(\keep))
reject;
// Abandon matches when 'Q' has a non-zero init attribute set
Const init = c.wire->attributes.at(\init, Const());
if (!init.empty())
for (auto b : init.extract(c.offset, c.width))
if (b != State::Sx && b != State::S0)
reject;
}
endcode
match ff
// reg D has async rst
// reg A, B has sync rst
select ff->type.in($dff, $dffe, $sdff, $sdffe, $adff, $adffe)
// does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
// it is possible that only part of a dff output matches argQ
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \Q)[offset] === argQ[0]
// Check that the rest of argQ is present
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
// only consider async rst flops when flag is set
filter !ff->type.in($adff, $adffe) || allowAsync
// clock must be consistent
filter clock == SigBit() || port(ff, \CLK)[0] == clock
endmatch
code argQ
// Check that reset value, if present, is fully 0.
bool noResetFlop = ff->type.in($dff, $dffe);
bool srstZero = ff->type.in($sdff, $sdffe) && param(ff, \SRST_VALUE).is_fully_zero();
bool arstZero = ff->type.in($adff, $adffe) && param(ff, \ARST_VALUE).is_fully_zero();
bool resetLegal = noResetFlop || srstZero || arstZero;
if (resetLegal)
{
SigSpec Q = port(ff, \Q);
dff = ff;
dffclock = port(ff, \CLK);
dffD = argQ;
SigSpec D = port(ff, \D);
argQ = Q;
dffD.replace(argQ, D);
}
endcode
// #######################
subpattern out_dffe
arg argD argQ clock
code
dff = nullptr;
for (auto c : argD.chunks())
// Abandon matches when 'D' has the keep attribute set
if (c.wire->get_bool_attribute(\keep))
reject;
endcode
match ff
select ff->type.in($dff, $dffe, $sdff, $sdffe)
// does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \D)[offset] === argD[0]
// Check that the rest of argD is present
filter GetSize(port(ff, \D)) >= offset + GetSize(argD)
filter port(ff, \D).extract(offset, GetSize(argD)) == argD
filter clock == SigBit() || port(ff, \CLK)[0] == clock
endmatch
code argQ
SigSpec D = port(ff, \D);
SigSpec Q = port(ff, \Q);
argQ = argD;
argQ.replace(D, Q);
// Abandon matches when 'Q' has a non-zero init attribute set
for (auto c : argQ.chunks()) {
Const init = c.wire->attributes.at(\init, Const());
if (!init.empty())
for (auto b : init.extract(c.offset, c.width))
if (b != State::Sx && b != State::S0)
reject;
}
dff = ff;
dffQ = argQ;
dffclock = port(ff, \CLK);
endcode

View File

@ -0,0 +1,168 @@
// ISC License
//
// Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
//
// 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.
// This file describes the second of three pattern matcher setups that
// forms the `microchip_dsp` pass described in microchip_dsp.cc
// At a high level, it works as follows:
// (1) Starting from a DSP cell that (a) doesn't have a CREG already,
// and (b) uses the 'C' port
// (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
// (attached to at most two $mux cells that implement clock-enable or
// reset functionality, using a subpattern discussed below)
// Notes:
// - Running CREG packing after microchip_dsp_pack is necessary since there is no
// guarantee that the cell ordering corresponds to the "expected" case (i.e.
// the order in which they appear in the source) thus the possiblity existed
// where a register got packed as a CREG into a downstream DSP, while it should
// have otherwise been a PREG of an upstream DSP that had not been visited.
// yet.
// - The reason this is separated out from the microchip_dsp.pmg file is
// for efficiency --- each *.pmg file creates a class of the same basename,
// which when constructed, creates a custom database tailored to the
// pattern(s) contained within. Since the pattern in this file must be
// executed after the pattern contained in microchip_dsp.pmg, it is necessary
// to reconstruct this database. Separating the two patterns into
// independent files causes two smaller, more specific, databases.
pattern microchip_dsp_packC
udata <std::function<SigSpec(const SigSpec&)>> unextend
state <SigBit> clock
state <SigSpec> sigC sigP
state <Cell*> ffC
// Variables used for subpatterns
state <SigSpec> argQ argD
state <int> ffoffset
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
udata <Cell*> dff
// (1) Starting from a DSP cell that (a) doesn't have a CREG already,
// and (b) uses the 'C' port
match dsp
select dsp->type.in(\MACC_PA)
select port(dsp, \C_BYPASS, SigSpec()).is_fully_ones()
select nusers(port(dsp, \C, SigSpec())) > 1
endmatch
code sigC sigP clock
//helper function to remove unused bits
unextend = [](const SigSpec &sig) {
int i;
for (i = GetSize(sig)-1; i > 0; i--)
if (sig[i] != sig[i-1])
break;
// Do not remove non-const sign bit
if (sig[i].wire)
++i;
return sig.extract(0, i);
};
sigC = unextend(port(dsp, \C, SigSpec()));
SigSpec P = port(dsp, \P);
// Only care about those bits that are used
int i;
for (i = GetSize(P)-1; i >= 0; i--)
if (nusers(P[i]) > 1)
break;
i++;
log_assert(nusers(P.extract_end(i)) <= 1);
sigP = P.extract(0, i);
clock = port(dsp, \CLK, SigBit());
endcode
// (2) Match the driver of the 'C' input to a possible $dff cell (CREG)
// (attached to at most two $mux cells that implement clock-enable or
// reset functionality, using the in_dffe subpattern)
code argQ ffC sigC clock
argQ = sigC;
subpattern(in_dffe);
if (dff) {
ffC = dff;
clock = dffclock;
sigC = dffD;
}
endcode
code
if (ffC)
accept;
endcode
// #######################
// Subpattern for matching against input registers, based on knowledge of the
// 'Q' input.
subpattern in_dffe
arg argQ clock
code
dff = nullptr;
if (argQ.empty())
reject;
for (const auto &c : argQ.chunks()) {
// Abandon matches when 'Q' is a constant
if (!c.wire)
reject;
// Abandon matches when 'Q' has the keep attribute set
if (c.wire->get_bool_attribute(\keep))
reject;
// Abandon matches when 'Q' has a non-zero init attribute set
Const init = c.wire->attributes.at(\init, Const());
if (!init.empty())
for (auto b : init.extract(c.offset, c.width))
if (b != State::Sx && b != State::S0)
reject;
}
endcode
match ff
select ff->type.in($dff, $dffe, $sdff, $sdffe, $adff, $adffe)
// does not support clock inversion
select param(ff, \CLK_POLARITY).as_bool()
slice offset GetSize(port(ff, \D))
index <SigBit> port(ff, \Q)[offset] === argQ[0]
// Check that the rest of argQ is present
filter GetSize(port(ff, \Q)) >= offset + GetSize(argQ)
filter port(ff, \Q).extract(offset, GetSize(argQ)) == argQ
filter clock == SigBit() || port(ff, \CLK)[0] == clock
endmatch
code argQ
// Check that reset value, if present, is fully 0.
bool noResetFlop = ff->type.in($dff, $dffe);
bool srstZero = ff->type.in($sdff, $sdffe) && param(ff, \SRST_VALUE).is_fully_zero();
bool arstZero = ff->type.in($adff, $adffe) && param(ff, \ARST_VALUE).is_fully_zero();
bool resetLegal = noResetFlop || srstZero || arstZero;
if (resetLegal)
{
SigSpec Q = port(ff, \Q);
dff = ff;
dffclock = port(ff, \CLK);
dffD = argQ;
SigSpec D = port(ff, \D);
argQ = Q;
dffD.replace(argQ, D);
}
endcode

View File

@ -0,0 +1,236 @@
// ISC License
//
// Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
//
// 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.
// This file describes the third of three pattern matcher setups that
// forms the `microchip_dsp` pass described in microchip_dsp.cc
// At a high level, it works as follows:
// (1) Starting from a DSP cell that
// (a) CDIN_FDBK_SEL is set to default "00"
// (b) doesn't already use the 'PCOUT' port
// (2) Match another DSP cell that
// (a) does not have the CREG enabled,
// (b) 'C' port is driven by the 'P' output of the previous DSP cell
// (c) has its 'PCIN' port unused
// (3) Recursively go to (2) until no more matches possible, keeping track
// of the longest possible chain found
// (4) The longest chain is then divided into chunks of no more than
// MAX_DSP_CASCADE in length (to prevent long cascades that exceed the
// height of a DSP column) with each DSP in each chunk being rewritten
// to use [ABP]COUT -> [ABP]CIN cascading as appropriate
pattern microchip_dsp_cascade
udata <std::function<SigSpec(const SigSpec&)>> unextend
udata <vector<std::tuple<Cell*,int>>> chain longest_chain
udata <std::set<Cell*>> visited
state <Cell*> next
state <SigSpec> clock
// Variables used for subpatterns
state <SigSpec> argQ argD
state <int> ffoffset
udata <SigSpec> dffD dffQ
udata <SigBit> dffclock
udata <Cell*> dff
// Maximum of 24 cascaded blocks
code
#define MAX_DSP_CASCADE 24
endcode
// NOTE: Chain vector
// +--------+ +--------+
// | first |----> | next | ----> ...
// +--------+ +--------+
// first.COUT cascades to next.CIN, so on and so forth
// Helper function to remove unused bits
code
unextend = [](const SigSpec &sig) {
int i;
for (i = GetSize(sig)-1; i > 0; i--)
if (sig[i] != sig[i-1])
break;
// Do not remove non-const sign bit
if (sig[i].wire)
++i;
return sig.extract(0, i);
};
endcode
// (1) Starting from a DSP cell that
// (a) CDIN_FDBK_SEL is set to default "00"
// (b) doesn't already use the 'PCOUT' port
match first
select first->type.in(\MACC_PA) && port(first, \CDIN_FDBK_SEL, Const(0, 2)) == Const::from_string("00")
select nusers(port(first, \CDOUT, SigSpec())) <= 1
endmatch
// (4) The longest chain is then divided into chunks of no more than
// MAX_DSP_CASCADE in length (to prevent long cascades that exceed the
// height of a DSP column) with each DSP in each chunk being rewritten
// to use [ABP]COUT -> [ABP]CIN cascading as appropriate
code
visited.clear();
visited.insert(first);
longest_chain.clear();
chain.emplace_back(first, -1);
subpattern(tail);
finally
// longest cascade chain has been found with DSP "first" being the head of the chain
// do some post processing
chain.pop_back();
visited.clear();
log_assert(chain.empty());
if (GetSize(longest_chain) > 1) {
Cell *dsp = std::get<0>(longest_chain.front());
Cell *dsp_pcin;
int SHIFT = -1;
for (int i = 1; i < GetSize(longest_chain); i++) {
log_assert(dsp->type.in(\MACC_PA));
std::tie(dsp_pcin,SHIFT) = longest_chain[i];
// Chain length exceeds the maximum cascade length, must split it up
if (i % MAX_DSP_CASCADE > 0) {
Wire *cascade = module->addWire(NEW_ID, 48);
// zero port C and move wire to cascade
dsp_pcin->setPort(ID(C), Const(0, 48));
dsp_pcin->setPort(ID(CDIN), cascade);
dsp->setPort(ID(CDOUT), cascade);
// Configure wire to cascade the dsps
add_siguser(cascade, dsp_pcin);
add_siguser(cascade, dsp);
// configure mux to use cascade for signal E
SigSpec cdin_fdbk_sel = port(dsp_pcin, \CDIN_FDBK_SEL, Const(0, 2));
cdin_fdbk_sel[1] = State::S1;
dsp_pcin->setPort(\CDIN_FDBK_SEL, cdin_fdbk_sel);
// check if shifting is required for wide multiplier implmentation
if (SHIFT == 17)
{
dsp_pcin->setPort(\ARSHFT17, State::S1);
}
log_debug("PCOUT -> PCIN cascade for %s -> %s\n", log_id(dsp), log_id(dsp_pcin));
} else {
log_debug(" Blocking %s -> %s cascade (exceeds max: %d)\n", log_id(dsp), log_id(dsp_pcin), MAX_DSP_CASCADE);
}
dsp = dsp_pcin;
}
accept;
}
endcode
// ------------------------------------------------------------------
subpattern tail
arg first
arg next
// (2) Match another DSP cell that
// (a) does not have the CREG enabled,
// (b) 'C' port is driven by the 'P' output of the previous DSP cell
// (c) has its 'PCIN' port unused
match nextP
// find candidates where nextP.C port is driven (maybe partially) by chain's tail DSP.P port
// and with no registers in between (since cascade path cannot be pipelined)
// reg C must not be used
select port(nextP, \C_BYPASS, SigSpec()).is_fully_ones()
// must be same DSP type
select nextP->type.in(\MACC_PA)
// port C should be driven by something
select nusers(port(nextP, \C, SigSpec())) > 1
// CIN must be unused
select nusers(port(nextP, \PCIN, SigSpec())) == 0
// should not have internal feedback connection
select port(nextP, \CDIN_FDBK_SEL, SigSpec()).is_fully_zero()
// SHIFT should be unused
select port(nextP, \ARSHFT17_BYPASS).is_fully_ones()
select port(nextP, \ARSHFT17).is_fully_zero()
select nusers(port(nextP, \ARSHFT17, SigSpec())) == 0
// current DSP cell can be cascaded with the back of the cascade chain
// index <SigBit> port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[0] || port(nextP, \C)[0] === port(std::get<0>(chain.back()), \P)[17]
filter port(nextP, \C)[0] == port(std::get<0>(chain.back()), \P)[0] || port(nextP, \C)[0] == port(std::get<0>(chain.back()), \P)[17]
// semioptional
optional
endmatch
code next
next = nextP;
// keep DSP type consistent in the chain
// currently since we only have one type anyways, this line is always false
if (next && next->type != first->type) reject;
// break infinite recursion when there's a combinational loop
if (visited.count(next) > 0) reject;
endcode
// (3) Recursively go to (2) until no more matches possible, recording the
// longest possible chain
code
if (next) {
SigSpec driver_sigP = port(std::get<0>(chain.back()), \P);
int shift = 0;
if (port(next, \C)[0] == port(std::get<0>(chain.back()), \P)[17]) shift = 17;
chain.emplace_back(next, shift);
visited.insert(next);
SigSpec sigC = unextend(port(next, \C));
// Make sure driverDSP.P === DSP.C
if (GetSize(sigC) + shift <= GetSize(driver_sigP) && driver_sigP.extract(shift, GetSize(sigC)) == sigC)
{
subpattern(tail);
}
} else {
if (GetSize(chain) > GetSize(longest_chain))
longest_chain = chain;
}
finally
if (next)
{
visited.erase(next);
chain.pop_back();
}
endcode

View File

@ -0,0 +1,191 @@
# ISC License
#
# Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
#
# 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.
# LSRAM true dual-port
ram block $__LSRAM_TDP_ {
# Cost of a given cell is assumed to be:
# (cost-widthscale) + [widthscale * (used_bits/14)]
cost 129;
# INIT is supported
init any;
# port A and port B are allowed to have different widths, but they MUST have
# WIDTH values of the same set.
# Example: Port A has a Data Width of 1. Then Port B's Data Width must be either
# 1, 2, 4, 8, or 16 (both values are in the 'WIDTH_1' set).
# WIDTH_1 = {1, 2, 4, 8, 16}
# WIDTH_2 = {5, 10, 20}
# "byte" specifies how many data bits correspond to one write enable bit.
# "byte" must be larger than width, or width must be a multipler of "byte"
# if "byte" > WIDTH, a single enable wire is inferred
# otherwise, WIDTH/byte number of enable wires are inferred
#
# WIDTH = {1, 2, 4, 5, 8, 10} requires 1 enable wire
# WIDTH = {16, 20} requires 2 enable wire
option "WIDTH_CONFIG" "REGULAR" {
# Data-Width| Address bits
# 1 | 14
# 2 | 13
# 4 | 12
# 8 | 11
# 16 | 10
# 14 address bits
abits 14;
widths 1 2 4 8 16 per_port;
byte 8;
}
option "WIDTH_CONFIG" "ALIGN" {
# Data-Width| Address bits
# 5 | 12
# 10 | 11
# 20 | 10
# Quick "hack" to fix address bit alignment by setting address bits to 12.
# If abits=14, tool will think there are 14 bits for width=5, 13 bits for width=10, 12 bits for width=20
# THe LSRAM_map.v file detects if this option is being used, and adjusts the address port alignments accordingly.
abits 12;
widths 5 10 20 per_port;
byte 10;
}
port srsw "A" "B" {
# read & write width must be same
width tied;
# clock polarity is rising
clock posedge;
# A/B read-enable
rden;
# initial value of read port data (not supported)
rdinit none;
# write modes (<A/B>_WMODE)
# 1. Simple Write: read-data port holds prev value (similar to "NO_CHANGE" for RAMB18E1)
# 2. Feed-through: read-data port takes new write value (similar to "WRITE_FIRST" for RAMB18E1)
# 3. Read-Before-Write: read-data port holds old value while being written (similar to "READ_FIRST" for RAMB18E1)
portoption "WRITE_MODE" "NO_CHANGE" {
# Read-write interaction
rdwr no_change;
# Write transparency:
# For write ports, define behaviour when another synchronous read port
# reads from the same memory cell that said write port is writing to at the same time.
wrtrans all old;
}
portoption "WRITE_MODE" "WRITE_FIRST" {
# bits corresponding to high A/B_WEN are updated
rdwr new_only;
wrtrans all new;
}
portoption "WRITE_MODE" "READ_FIRST" {
rdwr old;
wrtrans all old;
}
# generate params to indicate if read or write is used for each port
optional_rw;
}
}
# two-port configuration
ram block $__LSRAM_SDP_ {
# since two-port configuration is dedicated for wide-read/write,
# we want to prioritize this configuration over TDP to avoid tool picking multiple TDP RAMs
# inplace of a single SDP RAM for wide read/write. This means the cost of a single SDP should
# be less than 2 TDP.
cost 129;
init any;
option "WIDTH_CONFIG" "REGULAR" {
# Data-Width| Address bits
# 1 | 14
# 2 | 13
# 4 | 12
# 8 | 11
# 16 | 10
# 32 | 9
abits 14;
widths 1 2 4 8 16 32 per_port;
# width = 32, byte-write size is 8, ignore other widths
byte 8;
}
option "WIDTH_CONFIG" "ALIGN" {
# Data-Width| Address bits
# 5 | 12
# 10 | 11
# 20 | 10
# 40 | 9
# Same trick as TSP RAM for alignment
abits 12;
widths 5 10 20 40 per_port;
byte 10;
}
port sw "W" {
# only consider wide write
option "WIDTH_CONFIG" "REGULAR" width 32;
option "WIDTH_CONFIG" "ALIGN" width 40;
clock posedge;
# only simple write supported for two-port mode
wrtrans all old;
optional;
}
port sr "R" {
option "WIDTH_CONFIG" "REGULAR" width 32;
option "WIDTH_CONFIG" "ALIGN" width 40;
clock posedge;
rden;
rdinit none;
optional;
}
}

View File

@ -0,0 +1,320 @@
/*
ISC License
Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
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.
*/
// See document PolarFire Family Fabric User Guide
// section 4.1 for port list.
//LSRAM true dual-port
module $__LSRAM_TDP_ (...);
parameter INIT = 0;
parameter ADDR_BITS = 14;
parameter OPTION_WIDTH_CONFIG = "A";
parameter PORT_A_WIDTH = 1;
parameter PORT_A_WR_EN_WIDTH = 1;
parameter PORT_A_RD_USED = 0;
parameter PORT_A_WR_USED = 0;
parameter PORT_A_OPTION_WRITE_MODE = "NO_CHANGE";
parameter PORT_B_WIDTH = 1;
parameter PORT_B_WR_EN_WIDTH = 1;
parameter PORT_B_RD_USED = 0;
parameter PORT_B_WR_USED = 0;
parameter PORT_B_OPTION_WRITE_MODE = "NO_CHANGE";
input PORT_A_CLK;
input PORT_A_RD_EN;
input [ADDR_BITS-1:0] PORT_A_ADDR;
input [PORT_A_WIDTH-1:0] PORT_A_WR_DATA;
input [PORT_A_WR_EN_WIDTH-1:0] PORT_A_WR_EN;
output [PORT_A_WIDTH-1:0] PORT_A_RD_DATA;
input PORT_B_CLK;
input PORT_B_RD_EN;
input [ADDR_BITS-1:0] PORT_B_ADDR;
input [PORT_B_WIDTH-1:0] PORT_B_WR_DATA;
input [PORT_B_WR_EN_WIDTH-1:0] PORT_B_WR_EN;
output [PORT_B_WIDTH-1:0] PORT_B_RD_DATA;
`include "brams_defs.vh"
// address wires
wire [ADDR_BITS-1:0] A_address;
wire [ADDR_BITS-1:0] B_address;
assign A_address = (OPTION_WIDTH_CONFIG == "REGULAR") ? PORT_A_ADDR : {PORT_A_ADDR, 2'b00};
assign B_address = (OPTION_WIDTH_CONFIG == "REGULAR") ? PORT_B_ADDR : {PORT_B_ADDR, 2'b00};
// if port is not used, set block sel to 0 to disable it (read-data output is set to 0)
parameter PORT_A_RD_USED = 0;
parameter PORT_A_WR_USED = 0;
wire [2:0] A_BLK_SEL = (PORT_A_RD_USED == 1 || PORT_A_WR_USED == 1) ? 3'b111 : 3'b000;
wire [2:0] B_BLK_SEL = (PORT_B_RD_USED == 1 || PORT_B_WR_USED == 1) ? 3'b111 : 3'b000;
// wires for write data
generate
wire [19:0] A_write_data;
wire [19:0] B_write_data;
if (PORT_A_WIDTH == 16) begin
assign A_write_data[7:0] = PORT_A_WR_DATA[7:0];
assign A_write_data[17:10] = PORT_A_WR_DATA[15:8];
assign A_write_data[9:8] = 2'b0;
assign A_write_data[19:18] = 2'b0;
end else begin
assign A_write_data[PORT_A_WIDTH-1:0] = PORT_A_WR_DATA;
end
if (PORT_B_WIDTH == 16) begin
assign B_write_data[7:0] = PORT_B_WR_DATA[7:0];
assign B_write_data[17:10] = PORT_B_WR_DATA[15:8];
assign B_write_data[9:8] = 2'b0;
assign B_write_data[19:18] = 2'b0;
end else begin
assign B_write_data[PORT_B_WIDTH-1:0] = PORT_B_WR_DATA;
end
endgenerate
// wires for read data
wire [19:0] A_read_data;
assign PORT_A_RD_DATA = A_read_data[PORT_A_WIDTH-1:0];
wire [19:0] B_read_data;
assign PORT_B_RD_DATA = B_read_data[PORT_B_WIDTH-1:0];
// byte-write enables
wire [1:0] A_write_EN = (PORT_A_WR_EN_WIDTH == 1) ? {1'b0, PORT_A_WR_EN} : PORT_A_WR_EN;
wire [1:0] B_write_EN = (PORT_B_WR_EN_WIDTH == 1) ? {1'b0, PORT_B_WR_EN} : PORT_B_WR_EN;
// port width
wire [2:0] A_width = (PORT_A_WIDTH == 1) ? 3'b000 :
(PORT_A_WIDTH == 2) ? 3'b001 :
(PORT_A_WIDTH == 4 || PORT_A_WIDTH == 5) ? 3'b010 :
(PORT_A_WIDTH == 8 || PORT_A_WIDTH == 10) ? 3'b011 : 3'b100;
wire [2:0] B_width = (PORT_B_WIDTH == 1) ? 3'b000 :
(PORT_B_WIDTH == 2) ? 3'b001 :
(PORT_B_WIDTH == 4 || PORT_B_WIDTH == 5) ? 3'b010 :
(PORT_B_WIDTH == 8 || PORT_B_WIDTH == 10) ? 3'b011 : 3'b100;
// write modes
wire [1:0] A_write_mode = PORT_A_OPTION_WRITE_MODE == "NO_CHANGE" ? 2'b00 :
PORT_A_OPTION_WRITE_MODE == "WRITE_FIRST" ? 2'b01 : 2'b10;
wire [1:0] B_write_mode = PORT_B_OPTION_WRITE_MODE == "NO_CHANGE" ? 2'b00 :
PORT_B_OPTION_WRITE_MODE == "WRITE_FIRST" ? 2'b01 : 2'b10;
RAM1K20 #(
`PARAMS_INIT_LSRAM
) _TECHMAP_REPLACE_ (
// port A
.A_ADDR(A_address),
.A_BLK_EN(A_BLK_SEL),
.A_CLK(PORT_A_CLK),
.A_DIN(A_write_data),
.A_DOUT(A_read_data),
.A_WEN(A_write_EN),
.A_REN(PORT_A_RD_EN),
.A_WIDTH(A_width),
.A_WMODE(A_write_mode),
.A_BYPASS(1'b1),
.A_DOUT_EN(1'b1),
.A_DOUT_SRST_N(1'b1),
.A_DOUT_ARST_N(1'b1),
// port B
.B_ADDR(B_address),
.B_BLK_EN(B_BLK_SEL),
.B_CLK(PORT_B_CLK),
.B_DIN(B_write_data),
.B_DOUT(B_read_data),
.B_WEN(B_write_EN),
.B_REN(PORT_B_RD_EN),
.B_WIDTH(B_width),
.B_WMODE(B_write_mode),
.B_BYPASS(1'b1),
.B_DOUT_EN(1'b1),
.B_DOUT_SRST_N(1'b1),
.B_DOUT_ARST_N(1'b1),
// Disable ECC for TDP
.ECC_EN(1'b0),
.ECC_BYPASS(1'b1),
.BUSY_FB(1'b0)
);
endmodule
// single dual port configuration
module $__LSRAM_SDP_ (...);
parameter INIT = 0;
parameter OPTION_WIDTH_CONFIG = "REGULAR";
parameter ADDR_BITS = 14;
parameter PORT_W_WIDTH = 1;
parameter PORT_W_WR_EN_WIDTH = 4;
parameter PORT_W_USED = 1;
parameter PORT_R_WIDTH = 1;
parameter PORT_R_USED = 0;
input PORT_W_CLK;
input [ADDR_BITS-1:0] PORT_W_ADDR;
input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA;
input [PORT_W_WR_EN_WIDTH-1:0] PORT_W_WR_EN;
input PORT_R_CLK;
input PORT_R_RD_EN;
input [ADDR_BITS-1:0] PORT_R_ADDR;
output [PORT_R_WIDTH-1:0] PORT_R_RD_DATA;
input PORT_R_RD_SRST;
`include "brams_defs.vh"
// address wires
wire [ADDR_BITS-1:0] A_address;
wire [ADDR_BITS-1:0] B_address;
assign A_address = (OPTION_WIDTH_CONFIG == "REGULAR") ? PORT_R_ADDR : {PORT_R_ADDR, 2'b00};
assign B_address = (OPTION_WIDTH_CONFIG == "REGULAR") ? PORT_W_ADDR : {PORT_W_ADDR, 2'b00};
// if port is not used, set block sel to 0 to disable it (read-data output is set to 0)
// port A is for read, port B for write
parameter PORT_W_USED = 0;
parameter PORT_R_USED = 0;
wire [2:0] A_BLK_SEL = (PORT_R_USED == 1) ? 3'b111 : 3'b000;
wire [2:0] B_BLK_SEL = (PORT_W_USED == 1) ? 3'b111 : 3'b000;
// read/write data & write enables
// Currently support only wide write, width = {32, 40}
generate
wire [19:0] A_write_data;
wire [19:0] B_write_data;
wire [1:0] A_write_EN;
wire [1:0] B_write_EN;
// write port (A provides MSB)
if (PORT_W_WIDTH == 32) begin
assign B_write_data[3:0] = PORT_W_WR_DATA[3:0];
assign B_write_data[8:5] = PORT_W_WR_DATA[7:4];
assign B_write_data[13:10] = PORT_W_WR_DATA[11:8];
assign B_write_data[18:15] = PORT_W_WR_DATA[15:12];
assign B_write_data[4] = 1'b0;
assign B_write_data[9] = 1'b0;
assign B_write_data[14] = 1'b0;
assign B_write_data[19] = 1'b0;
assign A_write_data[3:0] = PORT_W_WR_DATA[19:16];
assign A_write_data[8:5] = PORT_W_WR_DATA[23:20];
assign A_write_data[13:10] = PORT_W_WR_DATA[27:24];
assign A_write_data[18:15] = PORT_W_WR_DATA[31:28];
assign A_write_data[4] = 1'b0;
assign A_write_data[9] = 1'b0;
assign A_write_data[14] = 1'b0;
assign A_write_data[19] = 1'b0;
end else if (PORT_W_WIDTH == 40) begin
assign B_write_data = PORT_W_WR_DATA[19:0];
assign A_write_data = PORT_W_WR_DATA[39:20];
end
// byte-write enables
assign A_write_EN = PORT_W_WR_EN[1:0];
assign B_write_EN = PORT_W_WR_EN[3:2];
// read ports (A provides MSB)
wire [19:0] A_read_data;
wire [19:0] B_read_data;
if (PORT_R_WIDTH == 32) begin
assign PORT_R_RD_DATA[3:0] = B_read_data[3:0];
assign PORT_R_RD_DATA[8:5] = B_read_data[7:4];
assign PORT_R_RD_DATA[13:10] = B_read_data[11:8];
assign PORT_R_RD_DATA[18:15] = B_read_data[15:12];
assign PORT_R_RD_DATA[19:16] = A_read_data[3:0];
assign PORT_R_RD_DATA[23:20] = A_read_data[8:5];
assign PORT_R_RD_DATA[27:24] = A_read_data[13:10];
assign PORT_R_RD_DATA[31:28] = A_read_data[18:15];
end else if (PORT_R_WIDTH == 40) begin
assign PORT_R_RD_DATA[19:0] = B_read_data[19:0];
assign PORT_R_RD_DATA[39:20] = A_read_data[19:0];
end
endgenerate
// port width
wire [2:0] A_width = (PORT_R_WIDTH == 1) ? 3'b000 :
(PORT_R_WIDTH == 2) ? 3'b001 :
(PORT_R_WIDTH == 4 || PORT_R_WIDTH == 5) ? 3'b010 :
(PORT_R_WIDTH == 8 || PORT_R_WIDTH == 10) ? 3'b011 :
(PORT_R_WIDTH == 16 || PORT_R_WIDTH == 20) ? 3'b100 : 3'b101;
wire [2:0] B_width = (PORT_W_WIDTH == 1) ? 3'b000 :
(PORT_W_WIDTH == 2) ? 3'b001 :
(PORT_W_WIDTH == 4 || PORT_W_WIDTH == 5) ? 3'b010 :
(PORT_W_WIDTH == 8 || PORT_W_WIDTH == 10) ? 3'b011 :
(PORT_W_WIDTH == 16 || PORT_W_WIDTH == 20) ? 3'b100 : 3'b101;
// write modes
wire [1:0] A_write_mode = 2'b00;
wire [1:0] B_write_mode = 2'b00;
RAM1K20 #(
`PARAMS_INIT_LSRAM
) _TECHMAP_REPLACE_ (
// port A - read
.A_ADDR(A_address),
.A_BLK_EN(A_BLK_SEL),
.A_CLK(PORT_R_CLK),
.A_DIN(A_write_data),
.A_DOUT(A_read_data),
.A_WEN(A_write_EN),
.A_REN(PORT_R_RD_EN),
.A_WIDTH(A_width),
.A_WMODE(A_write_mode),
.A_BYPASS(1'b1),
.A_DOUT_EN(1'b1),
.A_DOUT_SRST_N(1'b1),
.A_DOUT_ARST_N(1'b1),
// port B - write
.B_ADDR(B_address),
.B_BLK_EN(B_BLK_SEL),
.B_CLK(PORT_W_CLK),
.B_DIN(B_write_data),
.B_DOUT(B_read_data),
.B_WEN(B_write_EN),
.B_REN(PORT_R_RD_EN),
.B_WIDTH(B_width),
.B_WMODE(B_write_mode),
.B_BYPASS(1'b1),
.B_DOUT_EN(1'b1),
.B_DOUT_SRST_N(1'b1),
.B_DOUT_ARST_N(1'b1),
// Disable ECC for SDP
.ECC_EN(1'b0),
.ECC_BYPASS(1'b1),
.BUSY_FB(1'b0)
);
endmodule

View File

@ -0,0 +1,31 @@
# ISC License
#
# Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
#
# 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.
OBJS += techlibs/microchip/synth_microchip.o
OBJS += techlibs/microchip/microchip_dffopt.o
$(eval $(call add_share_file,share/microchip,techlibs/microchip/arith_map.v))
$(eval $(call add_share_file,share/microchip,techlibs/microchip/cells_map.v))
$(eval $(call add_share_file,share/microchip,techlibs/microchip/cells_sim.v))
$(eval $(call add_share_file,share/microchip,techlibs/microchip/polarfire_dsp_map.v))
$(eval $(call add_share_file,share/microchip,techlibs/microchip/brams_defs.vh))
$(eval $(call add_share_file,share/microchip,techlibs/microchip/LSRAM_map.v))
$(eval $(call add_share_file,share/microchip,techlibs/microchip/LSRAM.txt))
$(eval $(call add_share_file,share/microchip,techlibs/microchip/uSRAM_map.v))
$(eval $(call add_share_file,share/microchip,techlibs/microchip/uSRAM.txt))

View File

@ -0,0 +1,105 @@
/*
ISC License
Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
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.
*/
// Based on Macro Library for PolarFire https://coredocs.s3.amazonaws.com/Libero/2021_2/Tool/pf_mlg.pdf
// NOTE: prefix module names with \$__ so that mapping prioritizes these cells over internal Yosys cells
(* techmap_celltype = "$_MUX4_" *)
module \$__microchip_MUX4_ (A, B, C, D, S, T, Y);
input A, B, C, D, S, T;
output Y;
MX4 _TECHMAP_REPLACE_.MUX4(.D3(D), .D2(C), .D1(B), .D0(A), .S1(T), .S0(S), .Y(Y));
endmodule
(* techmap_celltype = "$reduce_xor" *)
module \$__microchip_XOR8_ (A, Y);
parameter A_SIGNED = 1;
parameter A_WIDTH = 8;
parameter Y_WIDTH = 1;
input [A_WIDTH-1:0] A;
output [Y_WIDTH-1:0] Y;
// check if mapping should proceed
generate
if (A_WIDTH != 8 || A_SIGNED || Y_WIDTH != 1) begin
wire _TECHMAP_FAIL_ = 1;
end
endgenerate
XOR8 _TECHMAP_REPLACE_.XOR8 (.A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]), .E(A[4]), .F(A[5]), .G(A[6]), .H(A[7]), .Y(Y));
endmodule
(* techmap_celltype = "$alu" *)
module \$__SF2_ALU (A, B, CI, BI, X, Y, CO);
parameter A_SIGNED = 0;
parameter B_SIGNED = 0;
parameter A_WIDTH = 1;
parameter B_WIDTH = 1;
parameter Y_WIDTH = 1;
(* 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] AA, BB;
\$pos #(.A_SIGNED(A_SIGNED), .A_WIDTH(A_WIDTH), .Y_WIDTH(Y_WIDTH)) A_conv (.A(A), .Y(AA));
\$pos #(.A_SIGNED(B_SIGNED), .A_WIDTH(B_WIDTH), .Y_WIDTH(Y_WIDTH)) B_conv (.A(B), .Y(BB));
(* force_downto *)
wire [Y_WIDTH-1:0] C = {CO, CI};
genvar i;
generate for (i = 0; i < Y_WIDTH; i = i + 1) begin:slice
ARI1 #(
// See section 1.4 of PolarFire Macro Library
// G = F1 = A[i] & (B[i]^BI)
// Y = F0 = A[i]^B[i]^BI
// P = Y
// ADCB
.INIT(20'b 01_11_0010_1000_1001_0110)
) carry (
.A(1'b0),
.B(AA[i]),
.C(BB[i]),
.D(BI),
.FCI(C[i]),
.Y(X[i]),
.S(Y[i]),
.FCO(CO[i])
);
end endgenerate
endmodule

View File

@ -0,0 +1,69 @@
/*
ISC License
Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
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.
*/
`define PARAMS_INIT_LSRAM \
.INIT0(slice_init_LSRAM(00)), \
.INIT1(slice_init_LSRAM(01)), \
.INIT2(slice_init_LSRAM(02)), \
.INIT3(slice_init_LSRAM(03)), \
.INIT4(slice_init_LSRAM(04)), \
.INIT5(slice_init_LSRAM(05)), \
.INIT6(slice_init_LSRAM(06)), \
.INIT7(slice_init_LSRAM(07)), \
.INIT8(slice_init_LSRAM(08)), \
.INIT9(slice_init_LSRAM(09)), \
.INIT10(slice_init_LSRAM(10)), \
.INIT11(slice_init_LSRAM(11)), \
.INIT12(slice_init_LSRAM(12)), \
.INIT13(slice_init_LSRAM(13)), \
.INIT14(slice_init_LSRAM(14)), \
.INIT15(slice_init_LSRAM(15)), \
.INIT16(slice_init_LSRAM(16)), \
.INIT17(slice_init_LSRAM(17)), \
.INIT18(slice_init_LSRAM(18)), \
.INIT19(slice_init_LSRAM(19))
`define PARAMS_INIT_uSRAM \
.INIT0(slice_init_uSRAM(00)), \
.INIT1(slice_init_uSRAM(01)), \
.INIT2(slice_init_uSRAM(02)), \
.INIT3(slice_init_uSRAM(03)), \
.INIT4(slice_init_uSRAM(04)), \
.INIT5(slice_init_uSRAM(05)), \
.INIT6(slice_init_uSRAM(06)), \
.INIT7(slice_init_uSRAM(07)), \
.INIT8(slice_init_uSRAM(08)), \
.INIT9(slice_init_uSRAM(09)), \
.INIT10(slice_init_uSRAM(10)), \
.INIT11(slice_init_uSRAM(11)) \
// Helper function for initializing the LSRAM
function [1023:0] slice_init_LSRAM;
input integer slice_idx;
integer i;
for (i = 0; i < 1024; i = i + 1)
slice_init_LSRAM[i] = INIT[(slice_idx * 1024 + i)];
endfunction
// Helper function for initializing the uSRAM
function [63:0] slice_init_uSRAM;
input integer slice_idx;
integer i;
for (i = 0; i < 64; i = i + 1)
slice_init_uSRAM[i] = INIT[(slice_idx * 64 + i)];
endfunction

View File

@ -0,0 +1,104 @@
/*
ISC License
Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
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.
*/
// DFFs
module \$_DFFE_PN0P_ (input D, C, R, E, output Q);
SLE _TECHMAP_REPLACE_ (.D(D), .CLK(C), .EN(E), .ALn(R), .ADn(1'b1), .SLn(1'b1), .SD(1'b0), .LAT(1'b0), .Q(Q));
endmodule
module \$_DFFE_PN1P_ (input D, C, R, E, output Q);
SLE _TECHMAP_REPLACE_ (.D(D), .CLK(C), .EN(E), .ALn(R), .ADn(1'b0), .SLn(1'b1), .SD(1'b0), .LAT(1'b0), .Q(Q));
endmodule
// for sync set/reset registers, we can pass them into ABC9. So we need to follow the simplification idiom
// and map to intermediate cell types
module \$_SDFFCE_PN0P_ (input D, C, R, E, output Q);
MICROCHIP_SYNC_RESET_DFF _TECHMAP_REPLACE_ (.D(D), .CLK(C), .Reset(R), .En(E), .Q(Q));
endmodule
module \$_SDFFCE_PN1P_ (input D, C, R, E, output Q);
MICROCHIP_SYNC_SET_DFF _TECHMAP_REPLACE_ (.D(D), .CLK(C), .Set(R), .En(E), .Q(Q));
endmodule
// LATCHES
module \$_DLATCH_PN0_ (input D, R, E, output Q);
SLE _TECHMAP_REPLACE_ (.D(D), .CLK(E), .EN(1'b1), .ALn(R), .ADn(1'b1), .SLn(1'b1), .SD(1'b0), .LAT(1'b1), .Q(Q));
endmodule
module \$_DLATCH_PN1_ (input D, R, E, output Q);
SLE _TECHMAP_REPLACE_ (.D(D), .CLK(E), .EN(1'b1), .ALn(R), .ADn(1'b0), .SLn(1'b1), .SD(1'b0), .LAT(1'b1), .Q(Q));
endmodule
module \$_DLATCH_P_ (input D, E, output Q);
SLE _TECHMAP_REPLACE_ (.D(D), .CLK(E), .EN(1'b1), .ALn(1'b1), .ADn(1'b0), .SLn(1'b1), .SD(1'b0), .LAT(1'b1), .Q(Q));
endmodule
// map intermediate flops to SLE
`ifdef FINAL_MAP
module MICROCHIP_SYNC_SET_DFF(
input D,
input CLK,
input Set,
input En,
output Q);
SLE _TECHMAP_REPLACE_ (.D(D), .CLK(CLK), .EN(En), .ALn(1'b1), .ADn(1'b0), .SLn(Set), .SD(1'b1), .LAT(1'b0), .Q(Q));
endmodule
module MICROCHIP_SYNC_RESET_DFF(
input D,
input CLK,
input Reset,
input En,
output Q);
SLE _TECHMAP_REPLACE_ (.D(D), .CLK(CLK), .EN(En), .ALn(1'b1), .ADn(1'b0), .SLn(Reset), .SD(1'b0), .LAT(1'b0), .Q(Q));
endmodule
`endif
// LUT
`ifndef NO_LUT
module \$lut (A, Y);
parameter WIDTH = 0;
parameter LUT = 0;
(* force_downto *)
input [WIDTH-1:0] A;
output Y;
generate
if (WIDTH == 1) begin
CFG1 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Y(Y), .A(A[0]));
end else
if (WIDTH == 2) begin
CFG2 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Y(Y), .A(A[0]), .B(A[1]));
end else
if (WIDTH == 3) begin
CFG3 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Y(Y), .A(A[0]), .B(A[1]), .C(A[2]));
end else
if (WIDTH == 4) begin
CFG4 #(.INIT(LUT)) _TECHMAP_REPLACE_ (.Y(Y), .A(A[0]), .B(A[1]), .C(A[2]), .D(A[3]));
end else begin
wire _TECHMAP_FAIL_ = 1;
end
endgenerate
endmodule
`endif

View File

@ -0,0 +1,719 @@
/*
ISC License
Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
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.
*/
// Macro Library for PolarFire https://coredocs.s3.amazonaws.com/Libero/2021_2/Tool/pf_mlg.pdf
module AND2 (
input A, B,
output Y
);
assign Y = A & B;
endmodule
module AND3 (
input A, B, C,
output Y
);
assign Y = A & B & C;
endmodule
module AND4 (
input A, B, C, D,
output Y
);
assign Y = A & B & C & D;
endmodule
(* abc9_lut=1 *)
module CFG1 (
output Y,
input A
);
parameter [1:0] INIT = 2'h0;
assign Y = INIT >> A;
specify
(A => Y) = 127;
endspecify
endmodule
(* abc9_lut=1 *)
module CFG2 (
output Y,
input A,
input B
);
parameter [3:0] INIT = 4'h0;
assign Y = INIT >> {B, A};
specify
(A => Y) = 238;
(B => Y) = 127;
endspecify
endmodule
(* abc9_lut=1 *)
module CFG3 (
output Y,
input A,
input B,
input C
);
parameter [7:0] INIT = 8'h0;
assign Y = INIT >> {C, B, A};
specify
(A => Y) = 407;
(B => Y) = 238;
(C => Y) = 127;
endspecify
endmodule
(* abc9_lut=1 *)
module CFG4 (
output Y,
input A,
input B,
input C,
input D
);
parameter [15:0] INIT = 16'h0;
assign Y = INIT >> {D, C, B, A};
specify
(A => Y) = 472;
(B => Y) = 407;
(C => Y) = 238;
(D => Y) = 127;
endspecify
endmodule
module BUFF (
input A,
output Y
);
assign Y = A;
endmodule
module BUFD (
input A,
output Y
);
assign Y = A;
endmodule
module CLKINT (
input A,
(* clkbuf_driver *)
output Y
);
assign Y = A;
endmodule
module CLKINT_PRESERVE (
input A,
(* clkbuf_driver *)
output Y
);
assign Y = A;
endmodule
module GCLKINT (
input A, EN,
(* clkbuf_driver *)
output Y
);
assign Y = A & EN;
endmodule
module RCLKINT (
input A,
(* clkbuf_driver *)
output Y
);
assign Y = A;
endmodule
module RGCLKINT (
input A, EN,
(* clkbuf_driver *)
output Y
);
assign Y = A & EN;
endmodule
// sequential elements
// MICROCHIP_SYNC_SET_DFF and MICROCHIP_SYNC_RESET_DFF are intermediate cell types to implement the simplification idiom for abc9 flow
// see: https://yosyshq.readthedocs.io/projects/yosys/en/latest/yosys_internals/extending_yosys/abc_flow.html
(* abc9_flop, lib_whitebox *)
module MICROCHIP_SYNC_SET_DFF(
input D,
input CLK,
input Set,
input En,
output reg Q);
parameter [0:0] INIT = 1'b0; // unused
always @(posedge CLK) begin
if (En == 1) begin
if (Set == 0)
Q <= 1;
else
Q <= D;
end
end
specify
$setup(D , posedge CLK &&& En && Set, 0); // neg setup not supported?
$setup(En, posedge CLK, 109);
$setup(Set, posedge CLK &&& En, 404);
if (En && !Set) (posedge CLK => (Q : 1'b1)) = 303;
if (En && Set) (posedge CLK => (Q : D)) = 303;
endspecify
endmodule
(* abc9_flop, lib_whitebox *)
module MICROCHIP_SYNC_RESET_DFF(
input D,
input CLK,
input Reset,
input En,
output reg Q);
parameter [0:0] INIT = 1'b0; // unused
always @(posedge CLK) begin
if (En == 1) begin
if (Reset == 0)
Q <= 0;
else
Q <= D;
end
end
specify
$setup(D , posedge CLK &&& En && Reset, 0); // neg setup not supported?
$setup(En, posedge CLK, 109);
$setup(Reset, posedge CLK &&& En, 404);
if (En && !Reset) (posedge CLK => (Q : 1'b0)) = 303;
if (En && Reset) (posedge CLK => (Q : D)) = 303;
endspecify
endmodule
module SLE (
output Q,
input ADn,
input ALn,
(* clkbuf_sink *)
input CLK,
input D,
input LAT,
input SD,
input EN,
input SLn
);
reg q_latch, q_ff;
always @(posedge CLK, negedge ALn) begin
if (!ALn) begin
q_ff <= !ADn;
end else if (EN) begin
if (!SLn)
q_ff <= SD;
else
q_ff <= D;
end
end
always @* begin
if (!ALn) begin
q_latch <= !ADn;
end else if (CLK && EN) begin
if (!SLn)
q_ff <= SD;
else
q_ff <= D;
end
end
assign Q = LAT ? q_latch : q_ff;
endmodule
(* abc9_box, lib_whitebox *)
module ARI1 (
(* abc9_carry *)
input FCI,
(* abc9_carry *)
output FCO,
input A, B, C, D,
output Y, S
);
parameter [19:0] INIT = 20'h0;
wire [2:0] Fsel = {D, C, B};
wire F0 = INIT[Fsel];
wire F1 = INIT[8 + Fsel];
wire Yout = A ? F1 : F0;
assign Y = Yout;
assign S = FCI ^ Yout;
wire G = INIT[16] ? (INIT[17] ? F1 : F0) : INIT[17];
wire P = INIT[19] ? 1'b1 : (INIT[18] ? Yout : 1'b0);
assign FCO = P ? FCI : G;
specify
//pin to pin path delay
(A => Y ) = 472;
(B => Y ) = 407;
(C => Y ) = 238;
(D => Y ) = 127;
(A => S ) = 572;
(B => S ) = 507;
(C => S ) = 338;
(D => S ) = 227;
(FCI => S ) = 100;
(A => FCO ) = 522;
(B => FCO ) = 457;
(C => FCO ) = 288;
(D => FCO ) = 177;
(FCI => FCO ) = 50;
endspecify
endmodule
(* blackbox *)
module GCLKBUF (
(* iopad_external_pin *)
input PAD,
input EN,
(* clkbuf_driver *)
output Y
);
endmodule
(* blackbox *)
module GCLKBUF_DIFF (
(* iopad_external_pin *)
input PADP,
(* iopad_external_pin *)
input PADN,
input EN,
(* clkbuf_driver *)
output Y
);
endmodule
module INV (
input A,
output Y
);
assign Y = !A;
endmodule
module INVD (
input A,
output Y
);
assign Y = !A;
endmodule
module MX2 (
input A, B, S,
output Y
);
assign Y = S ? B : A;
endmodule
module MX4 (
input D0, D1, D2, D3, S0, S1,
output Y
);
assign Y = S1 ? (S0 ? D3 : D2) : (S0 ? D1 : D0);
endmodule
module NAND2 (
input A, B,
output Y
);
assign Y = !(A & B);
endmodule
module NAND3 (
input A, B, C,
output Y
);
assign Y = !(A & B & C);
endmodule
module NAND4 (
input A, B, C, D,
output Y
);
assign Y = !(A & B & C & D);
endmodule
module NOR2 (
input A, B,
output Y
);
assign Y = !(A | B);
endmodule
module NOR3 (
input A, B, C,
output Y
);
assign Y = !(A | B | C);
endmodule
module NOR4 (
input A, B, C, D,
output Y
);
assign Y = !(A | B | C | D);
endmodule
module OR2 (
input A, B,
output Y
);
assign Y = A | B;
endmodule
module OR3 (
input A, B, C,
output Y
);
assign Y = A | B | C;
endmodule
module OR4 (
input A, B, C, D,
output Y
);
assign Y = A | B | C | D;
endmodule
module XOR2 (
input A, B,
output Y
);
assign Y = A ^ B;
endmodule
module XOR3 (
input A, B, C,
output Y
);
assign Y = A ^ B ^ C;
endmodule
module XOR4 (
input A, B, C, D,
output Y
);
assign Y = A ^ B ^ C ^ D;
endmodule
module XOR8 (
input A, B, C, D, E, F, G, H,
output Y
);
assign Y = A ^ B ^ C ^ D ^ E ^ F ^ G ^ H;
endmodule
// module UJTAG
module BIBUF (
input D,
input E,
(* iopad_external_pin *)
inout PAD,
output Y
);
parameter IOSTD = "";
assign PAD = E ? D : 1'bz;
assign Y = PAD;
endmodule
(* blackbox *)
module BIBUF_DIFF (
input D,
input E,
(* iopad_external_pin *)
inout PADP,
(* iopad_external_pin *)
inout PADN,
output Y
);
parameter IOSTD = "";
endmodule
module CLKBIBUF (
input D,
input E,
(* iopad_external_pin *)
inout PAD,
(* clkbuf_driver *)
output Y
);
parameter IOSTD = "";
assign PAD = E ? D : 1'bz;
assign Y = PAD;
endmodule
module CLKBUF (
(* iopad_external_pin *)
input PAD,
(* clkbuf_driver *)
output Y
);
parameter IOSTD = "";
assign Y = PAD;
specify
(PAD => Y) = 50;
endspecify
endmodule
(* blackbox *)
module CLKBUF_DIFF (
(* iopad_external_pin *)
input PADP,
(* iopad_external_pin *)
input PADN,
(* clkbuf_driver *)
output Y
);
parameter IOSTD = "";
endmodule
module INBUF (
(* iopad_external_pin *)
input PAD,
output Y
);
parameter IOSTD = "";
assign Y = PAD;
endmodule
(* blackbox *)
module INBUF_DIFF (
(* iopad_external_pin *)
input PADP,
(* iopad_external_pin *)
input PADN,
output Y
);
parameter IOSTD = "";
endmodule
module OUTBUF (
input D,
(* iopad_external_pin *)
output PAD
);
parameter IOSTD = "";
assign PAD = D;
endmodule
(* blackbox *)
module OUTBUF_DIFF (
input D,
(* iopad_external_pin *)
output PADP,
(* iopad_external_pin *)
output PADN
);
parameter IOSTD = "";
endmodule
module TRIBUFF (
input D,
input E,
(* iopad_external_pin *)
output PAD
);
parameter IOSTD = "";
assign PAD = E ? D : 1'bz;
endmodule
(* blackbox *)
module TRIBUFF_DIFF (
input D,
input E,
(* iopad_external_pin *)
output PADP,
(* iopad_external_pin *)
output PADN
);
parameter IOSTD = "";
endmodule
(* blackbox *)
module MACC_PA (
input DOTP,
input SIMD,
input OVFL_CARRYOUT_SEL,
input CLK,
input AL_N,
input [17:0] A,
input A_BYPASS,
input A_SRST_N,
input A_EN,
input [17:0] B,
input B_BYPASS,
input B_SRST_N,
input B_EN,
input [17:0] D,
input D_BYPASS,
input D_ARST_N,
input D_SRST_N,
input D_EN,
input CARRYIN,
input [47:0] C,
input C_BYPASS,
input C_ARST_N,
input C_SRST_N,
input C_EN,
input [47:0] CDIN,
output [47:0] P,
output OVFL_CARRYOUT,
input P_BYPASS,
input P_SRST_N,
input P_EN,
output [47:0] CDOUT,
input PASUB,
input PASUB_BYPASS,
input PASUB_AD_N,
input PASUB_SL_N,
input PASUB_SD_N,
input PASUB_EN,
input [1:0] CDIN_FDBK_SEL,
input CDIN_FDBK_SEL_BYPASS,
input [1:0] CDIN_FDBK_SEL_AD_N,
input CDIN_FDBK_SEL_SL_N,
input [1:0] CDIN_FDBK_SEL_SD_N,
input CDIN_FDBK_SEL_EN,
input ARSHFT17,
input ARSHFT17_BYPASS,
input ARSHFT17_AD_N,
input ARSHFT17_SL_N,
input ARSHFT17_SD_N,
input ARSHFT17_EN,
input SUB,
input SUB_BYPASS,
input SUB_AD_N,
input SUB_SL_N,
input SUB_SD_N,
input SUB_EN
);
endmodule
(* blackbox *)
module RAM1K20 (
input [13:0] A_ADDR,
input [2:0] A_BLK_EN,
input A_CLK,
input [19:0] A_DIN,
output [19:0] A_DOUT,
input [1:0] A_WEN,
input A_REN,
input [2:0] A_WIDTH,
input [1:0] A_WMODE,
input A_BYPASS,
input A_DOUT_EN,
input A_DOUT_SRST_N,
input A_DOUT_ARST_N,
input [13:0] B_ADDR,
input [2:0] B_BLK_EN,
input B_CLK,
input [19:0] B_DIN,
output [19:0] B_DOUT,
input [1:0] B_WEN,
input B_REN,
input [2:0] B_WIDTH,
input [1:0] B_WMODE,
input B_BYPASS,
input B_DOUT_EN,
input B_DOUT_SRST_N,
input B_DOUT_ARST_N,
input ECC_EN,
input ECC_BYPASS,
output SB_CORRECT,
output DB_DETECT,
input BUSY_FB,
output ACCESS_BUSY
);
parameter INIT0 = 1024'h0;
parameter INIT1 = 1024'h0;
parameter INIT2 = 1024'h0;
parameter INIT3 = 1024'h0;
parameter INIT4 = 1024'h0;
parameter INIT5 = 1024'h0;
parameter INIT6 = 1024'h0;
parameter INIT7 = 1024'h0;
parameter INIT8 = 1024'h0;
parameter INIT9 = 1024'h0;
parameter INIT10 = 1024'h0;
parameter INIT11 = 1024'h0;
parameter INIT12 = 1024'h0;
parameter INIT13 = 1024'h0;
parameter INIT14 = 1024'h0;
parameter INIT15 = 1024'h0;
parameter INIT16 = 1024'h0;
parameter INIT17 = 1024'h0;
parameter INIT18 = 1024'h0;
parameter INIT19 = 1024'h0;
endmodule
(* blackbox *)
module RAM64x12 (
input R_CLK,
input [5:0] R_ADDR,
input R_ADDR_BYPASS,
input R_ADDR_EN,
input R_ADDR_SL_N,
input R_ADDR_SD,
input R_ADDR_AL_N,
input R_ADDR_AD_N,
input BLK_EN,
output [11:0] R_DATA,
input R_DATA_BYPASS,
input R_DATA_EN,
input R_DATA_SL_N,
input R_DATA_SD,
input R_DATA_AL_N,
input R_DATA_AD_N,
input W_CLK,
input [5:0] W_ADDR,
input [11:0]W_DATA,
input W_EN,
input BUSY_FB,
output ACCESS_BUSY
);
parameter INIT0 = 64'h0;
parameter INIT1 = 64'h0;
parameter INIT2 = 64'h0;
parameter INIT3 = 64'h0;
parameter INIT4 = 64'h0;
parameter INIT5 = 64'h0;
parameter INIT6 = 64'h0;
parameter INIT7 = 64'h0;
parameter INIT8 = 64'h0;
parameter INIT9 = 64'h0;
parameter INIT10 = 64'h0;
parameter INIT11 = 64'h0;
endmodule

View File

@ -0,0 +1,343 @@
/*
ISC License
Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
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/sigtools.h"
#include "kernel/yosys.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
typedef std::pair<Const, std::vector<SigBit>> LutData;
// Compute a LUT implementing (select ^ select_inv) ? alt_data : data. Returns true if successful.
bool merge_lut(LutData &result, const LutData &data, const LutData select, bool select_inv, SigBit alt_data, int max_lut_size)
{
// First, gather input signals -- insert new signals at the beginning
// of the vector, so they don't disturb the likely-critical D LUT input
// timings.
result.second = data.second;
// D lut inputs initially start at 0.
int idx_data = 0;
// Now add the control input LUT inputs.
std::vector<int> idx_sel;
for (auto bit : select.second) {
int idx = -1;
for (int i = 0; i < GetSize(result.second); i++)
if (result.second[i] == bit)
idx = i;
if (idx == -1) {
idx = 0;
// Insert new signal at the beginning and bump all indices.
result.second.insert(result.second.begin(), bit);
idx_data++;
for (int &sidx : idx_sel)
sidx++;
}
idx_sel.push_back(idx);
}
// Insert the Q signal, if any, to the slowest input -- it will have
// no problem meeting timing.
// This is to emulate CLK_EN, where output data is retained
int idx_alt = -1;
if (alt_data.wire) {
// Check if we already have it.
for (int i = 0; i < GetSize(result.second); i++)
if (result.second[i] == alt_data)
idx_alt = i;
// If not, add it.
if (idx_alt == -1) {
idx_alt = 0;
result.second.insert(result.second.begin(), alt_data);
idx_data++;
for (int &sidx : idx_sel)
sidx++;
}
}
// If LUT would be too large, bail.
if (GetSize(result.second) > max_lut_size)
return false;
// Okay, we're doing it — compute the LUT mask.
result.first = Const(0, 1 << GetSize(result.second));
for (int i = 0; i < GetSize(result.first); i++) {
int sel_lut_idx = 0;
for (int j = 0; j < GetSize(select.second); j++)
if (i & 1 << idx_sel[j])
sel_lut_idx |= 1 << j;
bool select_val = (select.first.bits[sel_lut_idx] == State::S1);
bool new_bit;
if (select_val ^ select_inv) {
// Use alt_data.
if (alt_data.wire)
new_bit = (i & 1 << idx_alt) != 0;
else
new_bit = alt_data.data == State::S1;
} else {
// Use original LUT.
int lut_idx = i >> idx_data & ((1 << GetSize(data.second)) - 1);
new_bit = data.first.bits[lut_idx] == State::S1;
}
result.first.bits[i] = new_bit ? State::S1 : State::S0;
}
return true;
}
struct MicrochipDffOptPass : public Pass {
MicrochipDffOptPass() : Pass("microchip_dffopt", "MICROCHIP: optimize FF control signal usage") {}
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" microchip_dffopt [options] [selection]\n");
log("\n");
log("Converts hardware clock enable and set/reset signals on FFs to emulation\n");
log("using LUTs, if doing so would improve area. Operates on post-techmap LUT, DFF\n");
log("cells. \n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing MICROCHIP_DFFOPT pass (optimize FF control signal usage).\n");
size_t argidx = 1;
int max_lut_size = 4;
extra_args(args, argidx, design);
for (auto module : design->selected_modules()) {
log("Optimizing FFs in %s.\n", log_id(module));
SigMap sigmap(module);
dict<SigBit, pair<LutData, Cell *>> bit_to_lut;
dict<SigBit, int> bit_uses;
// Gather LUTs.
for (auto cell : module->selected_cells()) {
for (auto port : cell->connections())
for (auto bit : port.second)
bit_uses[sigmap(bit)]++;
if (cell->get_bool_attribute(ID::keep))
continue;
if (cell->type == ID(INV)) {
SigBit sigout = sigmap(cell->getPort(ID::Y));
SigBit sigin = sigmap(cell->getPort(ID::A));
bit_to_lut[sigout] = make_pair(LutData(Const(1, 2), {sigin}), cell); // INIT = 01
} else if (cell->type.in(ID(CFG1), ID(CFG2), ID(CFG3), ID(CFG4))) {
SigBit sigout = sigmap(cell->getPort(ID::Y));
const Const &init = cell->getParam(ID::INIT);
std::vector<SigBit> sigin;
sigin.push_back(sigmap(cell->getPort(ID(A))));
if (cell->type == ID(CFG1))
goto lut_sigin_done;
sigin.push_back(sigmap(cell->getPort(ID(B))));
if (cell->type == ID(CFG2))
goto lut_sigin_done;
sigin.push_back(sigmap(cell->getPort(ID(C))));
if (cell->type == ID(CFG3))
goto lut_sigin_done;
sigin.push_back(sigmap(cell->getPort(ID(D))));
lut_sigin_done:
bit_to_lut[sigout] = make_pair(LutData(init, sigin), cell);
}
}
for (auto wire : module->wires())
if (wire->port_output || wire->port_input)
for (int i = 0; i < GetSize(wire); i++)
bit_uses[sigmap(SigBit(wire, i))]++;
// Iterate through FFs.
for (auto cell : module->selected_cells()) {
if (!cell->type.in(ID(SLE))) // not a SLE
continue;
if (cell->getPort(ID(LAT)).is_fully_ones()) // skip latch
continue;
if (cell->get_bool_attribute(ID::keep)) // keep attribute
continue;
if (!cell->getPort(ID(ALn)).is_fully_ones()) // async FF
continue;
const bool hasSyncLoad = cell->getPort(ID(SLn)).is_wire();
const bool has_s = hasSyncLoad && cell->getPort(ID(SD)).is_fully_ones();
const bool has_r = hasSyncLoad && cell->getPort(ID(SD)).is_fully_zero();
// SLE cannot have both synchronous set and reset implemented at the same time
log_assert(!(has_s && has_r));
// Don't bother if D has more than one use.
SigBit sig_D = sigmap(cell->getPort(ID::D));
if (bit_uses[sig_D] > 2)
continue;
// Find the D LUT.
auto it_D = bit_to_lut.find(sig_D);
if (it_D == bit_to_lut.end())
continue;
LutData lut_d = it_D->second.first;
Cell *cell_d = it_D->second.second;
LutData lut_d_post_ce;
LutData lut_d_post_s;
LutData lut_d_post_r;
bool worthy_post_ce = false;
bool worthy_post_s = false;
bool worthy_post_r = false;
// First, unmap CE.
SigBit sig_Q = sigmap(cell->getPort(ID::Q));
SigBit sig_CE = sigmap(cell->getPort(ID(EN)));
LutData lut_ce = LutData(Const(2, 2), {sig_CE}); // INIT = 10
auto it_CE = bit_to_lut.find(sig_CE);
if (it_CE != bit_to_lut.end())
lut_ce = it_CE->second.first;
if (sig_CE.wire) {
// Merge CE LUT and D LUT into one. If it cannot be done, nothing to do about this FF.
if (!merge_lut(lut_d_post_ce, lut_d, lut_ce, true, sig_Q, max_lut_size))
continue;
// If this gets rid of a CE LUT, it's worth it. If not, it still may be worth it, if we can remove set/reset
// as well.
if (it_CE != bit_to_lut.end())
worthy_post_ce = true;
} else if (sig_CE.data != State::S1) {
// Strange. Should not happen in a reasonable flow, so bail.
log_assert(false); // This DFF is always off
continue;
} else {
lut_d_post_ce = lut_d;
}
// Second, unmap S, if any.
lut_d_post_s = lut_d_post_ce;
if (has_s) {
SigBit sig_S = sigmap(cell->getPort(ID(SLn)));
LutData lut_s = LutData(Const(2, 2), {sig_S}); // INIT = 10
bool inv_s = true; // active low
auto it_S = bit_to_lut.find(sig_S);
if (it_S != bit_to_lut.end())
lut_s = it_S->second.first;
if (sig_S.wire) {
// Merge S LUT and D LUT into one. If it cannot be done, try to at least merge CE.
if (!merge_lut(lut_d_post_s, lut_d_post_ce, lut_s, inv_s, SigBit(State::S1), max_lut_size))
goto unmap;
// If this gets rid of an S LUT, it's worth it.
if (it_S != bit_to_lut.end())
worthy_post_s = true;
} else if (sig_S.data != (inv_s ? State::S1 : State::S0)) {
// Strange. Should not happen in a reasonable flow, so bail.
log_assert(false); // DFF is always in set mode
continue;
}
}
// Third, unmap R, if any.
lut_d_post_r = lut_d_post_s;
if (has_r) {
SigBit sig_R = sigmap(cell->getPort(ID(SLn)));
LutData lut_r = LutData(Const(2, 2), {sig_R}); // INIT = 10
bool inv_r = true; // active low
auto it_R = bit_to_lut.find(sig_R);
if (it_R != bit_to_lut.end())
lut_r = it_R->second.first;
if (sig_R.wire) {
// Merge R LUT and D LUT into one. If it cannot be done, try to at least merge CE/S.
if (!merge_lut(lut_d_post_r, lut_d_post_s, lut_r, inv_r, SigBit(State::S0), max_lut_size))
goto unmap;
// If this gets rid of an S LUT, it's worth it.
if (it_R != bit_to_lut.end())
worthy_post_r = true;
} else if (sig_R.data != (inv_r ? State::S1 : State::S0)) {
// Strange. Should not happen in a reasonable flow, so bail.
log_assert(false); // DFF is always in reset mode
continue;
}
}
unmap:
// SLE cannot have both synchronous set and reset implemented at the same time
log_assert(!(worthy_post_r && worthy_post_s));
LutData final_lut;
if (worthy_post_r) {
final_lut = lut_d_post_r;
} else if (worthy_post_s) {
final_lut = lut_d_post_s;
} else if (worthy_post_ce) {
final_lut = lut_d_post_ce;
} else {
// Nothing to do here.
continue;
}
std::string ports;
if (worthy_post_r)
ports += " + R";
if (worthy_post_s)
ports += " + S";
if (worthy_post_ce)
ports += " + CE";
log(" Merging D%s LUTs for %s/%s (%d -> %d)\n", ports.c_str(), log_id(cell), log_id(sig_Q.wire),
GetSize(lut_d.second), GetSize(final_lut.second));
// Okay, we're doing it. Unmap ports.
if ((has_s && worthy_post_s) || worthy_post_r) {
cell->setPort(ID(SLn), Const(1, 1));
}
// if we made it this far, clk enable is always merged into D
cell->setPort(ID(EN), Const(1, 1));
// Create the new LUT.
Cell *lut_cell = nullptr;
switch (GetSize(final_lut.second)) {
case 1:
lut_cell = module->addCell(NEW_ID, ID(CFG1));
break;
case 2:
lut_cell = module->addCell(NEW_ID, ID(CFG2));
break;
case 3:
lut_cell = module->addCell(NEW_ID, ID(CFG3));
break;
case 4:
lut_cell = module->addCell(NEW_ID, ID(CFG4));
break;
default:
log_assert(!"unknown lut size");
}
lut_cell->attributes = cell_d->attributes;
Wire *lut_out = module->addWire(NEW_ID);
lut_cell->setParam(ID::INIT, final_lut.first);
cell->setPort(ID::D, lut_out);
lut_cell->setPort(ID::Y, lut_out);
lut_cell->setPort(ID(A), final_lut.second[0]);
if (GetSize(final_lut.second) >= 2)
lut_cell->setPort(ID(B), final_lut.second[1]);
if (GetSize(final_lut.second) >= 3)
lut_cell->setPort(ID(C), final_lut.second[2]);
if (GetSize(final_lut.second) >= 4)
lut_cell->setPort(ID(D), final_lut.second[3]);
}
}
}
} MicrochipDffOptPass;
PRIVATE_NAMESPACE_END

View File

@ -0,0 +1,95 @@
/*
ISC License
Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
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.
*/
module \$__MUL18X18 (input [17:0] A, input [17:0] B, output [35:0] Y);
parameter A_SIGNED = 0;
parameter B_SIGNED = 0;
parameter A_WIDTH = 0;
parameter B_WIDTH = 0;
parameter Y_WIDTH = 0;
wire [47:0] P_48;
// For pin descriptions, see Section 9 of PolarFire FPGA Macro Library Guide:
// https://coredocs.s3.amazonaws.com/Libero/2021_2/Tool/pf_mlg.pdf
MACC_PA _TECHMAP_REPLACE_ (
.DOTP(1'b0),
.SIMD(1'b0),
.OVFL_CARRYOUT_SEL(1'b0),
.AL_N(1'b1),
.A(A),
.A_BYPASS(1'b1),
.A_SRST_N(1'b1),
.A_EN(1'b1),
.B(B),
.B_BYPASS(1'b1),
.B_SRST_N(1'b1),
.B_EN(1'b1),
.D(18'b0),
.D_BYPASS(1'b1),
.D_ARST_N(1'b1),
.D_SRST_N(1'b1),
.D_EN(1'b1),
.CARRYIN(1'b0),
.C(48'b0),
.C_BYPASS(1'b1),
.C_ARST_N(1'b1),
.C_SRST_N(1'b1),
.C_EN(1'b1),
.P(P_48),
.P_BYPASS(1'b1),
.P_SRST_N(1'b1),
.P_EN(1'b1),
.PASUB(1'b0),
.PASUB_BYPASS(1'b1),
.PASUB_AD_N(1'b0),
.PASUB_SL_N(1'b1),
.PASUB_SD_N(1'b0),
.PASUB_EN(1'b1),
.CDIN_FDBK_SEL(2'b00),
.CDIN_FDBK_SEL_BYPASS(1'b1),
.CDIN_FDBK_SEL_AD_N(2'b00),
.CDIN_FDBK_SEL_SL_N(1'b1),
.CDIN_FDBK_SEL_SD_N(2'b00),
.CDIN_FDBK_SEL_EN(1'b1),
.ARSHFT17(1'b0),
.ARSHFT17_BYPASS(1'b1),
.ARSHFT17_AD_N(1'b0),
.ARSHFT17_SL_N(1'b1),
.ARSHFT17_SD_N(1'b0),
.ARSHFT17_EN(1'b1),
.SUB(1'b0),
.SUB_BYPASS(1'b1),
.SUB_AD_N(1'b0),
.SUB_SL_N(1'b1),
.SUB_SD_N(1'b0),
.SUB_EN(1'b1)
);
assign Y = P_48;
endmodule

View File

@ -0,0 +1,553 @@
/*
ISC License
Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
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/celltypes.h"
#include "kernel/log.h"
#include "kernel/register.h"
#include "kernel/rtlil.h"
USING_YOSYS_NAMESPACE
PRIVATE_NAMESPACE_BEGIN
struct SynthMicrochipPass : public ScriptPass {
SynthMicrochipPass() : ScriptPass("synth_microchip", "synthesis for Microchip FPGAs") {}
void help() override
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" synth_microchip [options]\n");
log("\n");
log("This command runs synthesis for Microchip FPGAs. This command creates \n");
log("netlists that are compatible with Microchip PolarFire devices. \n");
log("\n");
log(" -top <module>\n");
log(" use the specified module as the top module\n");
log("\n");
log(" -family <family>\n");
log(" Run synthesis for the specified Microchip architecture. \n");
log(" Generate the synthesis netlist for the specified family.\n");
log(" supported values:\n");
log(" - polarfire: PolarFire\n");
log("\n");
log(" -edif <file>\n");
log(" Write the design to the specified edif file. Writing of an output file\n");
log(" is omitted if this parameter is not specified.\n");
log("\n");
log(" -blif <file>\n");
log(" Write the design to the specified BLIF file. Writing of an output file\n");
log(" is omitted if this parameter is not specified.\n");
log("\n");
log(" -vlog <file>\n");
log(" write the design to the specified Verilog file. writing of an output\n");
log(" file is omitted if this parameter is not specified.\n");
log(" -nobram\n");
log(" Do not use block RAM cells in output netlist\n");
log("\n");
log(" -nocarry\n");
log(" Do not use ARI1 cells in output netlist\n");
log("\n");
log(" -nodsp\n");
log(" Do not use MATH blocks to implement multipliers and associated logic\n");
log("\n");
log(" -noiopad\n");
log(" Disable I/O buffer insertion (useful for hierarchical or \n");
log(" out-of-context flows)\n");
log("\n");
log(" -noclkbuf\n");
log(" Disable automatic clock buffer insertion\n");
log("\n");
log(" -run <from_label>:<to_label>\n");
log(" Only run the commands between the labels (see below). an empty\n");
log(" 'from_label' is synonymous to 'begin', and empty 'to_label' is\n");
log(" synonymous to the end of the command list.\n");
log("\n");
log(" -noflatten\n");
log(" do not flatten design before synthesis\n");
log("\n");
log(" -dff\n");
log(" Run 'abc'/'abc9' with -dff option\n");
log("\n");
log(" -retime\n");
log(" Run 'abc' with '-D 1' option to enable flip-flop retiming.\n");
log(" implies -dff.\n");
log("\n");
log(" -noabc9\n");
log(" Use classic ABC flow instead of ABC9\n");
log("\n");
log(" -discard-ffinit\n");
log(" discard FF init value instead of emitting an error\n");
log("\n");
log("\n");
log("The following commands are executed by this synthesis command:\n");
help_script();
log("\n");
}
std::string top_opt, edif_file, blif_file, vlog_file, family;
bool flatten, retime, noiopad, noclkbuf, nobram, nocarry, nowidelut, nodsp;
bool abc9, dff;
bool discard_ffinit;
int lut_size;
// debug dump switches
bool debug_memory, debug_carry;
void clear_flags() override
{
top_opt = "-auto-top";
edif_file.clear();
blif_file.clear();
vlog_file.clear();
family = "polarfire";
flatten = true;
retime = false;
noiopad = false;
noclkbuf = false;
nocarry = false;
nobram = false;
nowidelut = false;
nodsp = false;
abc9 = true;
dff = false;
lut_size = 4;
discard_ffinit = false;
debug_memory = false;
debug_carry = false;
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
std::string run_from, run_to;
clear_flags();
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++) {
if (args[argidx] == "-top" && argidx + 1 < args.size()) {
top_opt = "-top " + args[++argidx];
continue;
}
if ((args[argidx] == "-family" || args[argidx] == "-arch") && argidx + 1 < args.size()) {
family = args[++argidx];
continue;
}
if (args[argidx] == "-edif" && argidx + 1 < args.size()) {
edif_file = args[++argidx];
continue;
}
if (args[argidx] == "-blif" && argidx + 1 < args.size()) {
blif_file = args[++argidx];
continue;
}
if (args[argidx] == "-vlog" && argidx + 1 < args.size()) {
vlog_file = args[++argidx];
continue;
}
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] == "-noflatten") {
flatten = false;
continue;
}
if (args[argidx] == "-retime") {
dff = true;
retime = true;
continue;
}
if (args[argidx] == "-nocarry") {
nocarry = true;
continue;
}
if (args[argidx] == "-nowidelut") {
nowidelut = true;
continue;
}
if (args[argidx] == "-iopad") {
continue;
}
if (args[argidx] == "-noiopad") {
noiopad = true;
continue;
}
if (args[argidx] == "-noclkbuf") {
noclkbuf = true;
continue;
}
if (args[argidx] == "-nocarry") {
nocarry = true;
continue;
}
if (args[argidx] == "-nobram") {
nobram = true;
continue;
}
if (args[argidx] == "-noabc9") {
abc9 = false;
continue;
}
if (args[argidx] == "-nodsp") {
nodsp = true;
continue;
}
if (args[argidx] == "-dff") {
dff = true;
continue;
}
if (args[argidx] == "-debug_memory") {
debug_memory = true;
continue;
}
if (args[argidx] == "-debug_carry") {
debug_carry = true;
continue;
}
if (args[argidx] == "-discard-ffinit") {
discard_ffinit = true;
continue;
}
break;
}
extra_args(args, argidx, design);
if (family == "polarfire") {
lut_size = 4;
} else {
log_cmd_error("Invalid Microchip -family setting: '%s'.\n", family.c_str());
}
if (!design->full_selection())
log_cmd_error("This command only operates on fully selected designs!\n");
if (abc9 && retime)
log_cmd_error("-retime option not currently compatible with -abc9!\n");
log_header(design, "Executing SYNTH_MICROCHIP pass.\n");
log_push();
run_script(design, run_from, run_to);
log_pop();
}
void script() override
{
std::string lut_size_s = std::to_string(lut_size);
if (help_mode)
lut_size_s = "[4]";
if (check_label("begin")) {
std::string read_args;
read_args += " -lib -specify +/microchip/cells_sim.v";
run("read_verilog" + read_args);
run(stringf("hierarchy -check %s", top_opt.c_str()));
}
if (check_label("prepare")) {
run("proc");
if (flatten || help_mode)
run("flatten", "(with '-flatten')");
if (active_design)
active_design->scratchpad_unset("tribuf.added_something");
run("tribuf -logic");
if (noiopad && active_design && active_design->scratchpad_get_bool("tribuf.added_something"))
log_error("Tristate buffers are unsupported without the '-iopad' option.\n");
run("deminout");
run("opt_expr");
run("opt_clean");
run("check");
run("opt -nodffe -nosdff");
run("fsm");
run("opt");
run("wreduce");
run("peepopt");
run("opt_clean");
}
if (check_label("map_dsp", "(skip if '-nodsp')")) {
if (!nodsp || help_mode) {
run("memory_dff"); // microchip_dsp will merge registers, reserve memory port registers first
if (help_mode)
run("techmap -map +/mul2dsp.v -map +/microchip/{family}_dsp_map.v {options}");
else if (family == "polarfire") // Microchip - map multipliers to DSP
run("techmap -map +/mul2dsp.v -map +/microchip/polarfire_dsp_map.v -D DSP_A_MAXWIDTH=18 -D DSP_B_MAXWIDTH=18 "
"-D DSP_A_MAXWIDTH_PARTIAL=18 " // Partial multipliers are intentionally
// limited to 18x18 in order to take
// advantage of the (PCOUT >> 17) -> PCIN
// dedicated cascade chain capability
"-D DSP_A_MINWIDTH=2 -D DSP_B_MINWIDTH=2 " // Blocks Nx1 multipliers
"-D DSP_Y_MINWIDTH=9 "
"-D DSP_SIGNEDONLY=1 -D DSP_NAME=$__MUL18X18");
run("select a:mul2dsp");
run("setattr -unset mul2dsp");
run("opt_expr -fine");
run("wreduce");
run("select -clear");
if (help_mode)
run("microchip_dsp -family <family>");
else if (family == "polarfire") // Microchip - absorb cells into DSP
run("microchip_dsp -family " + family);
run("chtype -set $mul t:$__soft_mul");
}
}
if (check_label("coarse")) {
run("techmap -map +/cmp2lut.v -map +/cmp2lcu.v -D LUT_WIDTH=" + lut_size_s);
run("alumacc");
run("share");
run("opt");
run("memory -nomap");
run("opt_clean");
if (discard_ffinit || help_mode)
run("attrmap -remove init", "(only if -discard-ffinit)");
}
if (check_label("map_memory")) {
std::string params = "";
std::string LSRAM_map = "+/microchip/LSRAM_map.v";
std::string uSRAM_map = "+/microchip/uSRAM_map.v";
if (debug_memory)
run("write_verilog -noexpr memory_map_pre.vm");
if (help_mode) {
params = " [...]";
} else {
if (family == "polarfire") {
// cost of a single bit for memory lowered to soft logic
params += " -logic-cost-rom 0.015625";
params += " -lib +/microchip/LSRAM.txt";
params += " -lib +/microchip/uSRAM.txt";
LSRAM_map = "+/microchip/LSRAM_map.v";
uSRAM_map = "+/microchip/uSRAM_map.v";
}
if (nobram)
params += " -no-auto-block";
}
// transform memories into intermediate cells
// Cost based transformation. The cost is assigned by us for each cell.
run("memory_libmap" + params);
if (debug_memory)
run("write_verilog -noexpr memory_map_libmap.vm");
// map intermediate cells to actual RAM macros
// NOTE: order doesnt matter here
run("techmap -map " + LSRAM_map);
run("techmap -map " + uSRAM_map);
if (debug_memory)
run("write_verilog -noexpr memory_map_final.vm");
}
if (check_label("map_ffram")) {
run("opt -fast -full");
// blast unmapped RAM to flops or LUTs
run("memory_map");
}
if (check_label("fine")) {
run("opt -full");
if (debug_carry)
run("write_verilog -noexpr ARI1_cells.vm");
if (!nocarry) {
// converts $mux -> $_MUX_ to allow muxcover to work
run("simplemap t:$mux");
// converts $and/$or/$xor to gate representation for extract_reduce to work
run("simplemap t:$xor"); // only mapping reduce_xor
// mapping based on Yosys internal gates
if (debug_carry)
run("write_verilog -noexpr ARI1_pre.vm");
// collapse $_AND_/$_OR_/$_XOR_ chains into reduction cells
run("extract_reduce");
if (debug_carry)
run("write_verilog -noexpr ARI1_extract_reduce.vm");
// pack mux trees into $_MUX4_
run("muxcover -nodecode -mux4=220");
if (debug_carry)
run("write_verilog -noexpr ARI1_muxcover.vm");
run("techmap -map +/microchip/arith_map.v");
if (debug_carry)
run("write_verilog -noexpr ARI1_post.vm");
}
// convert all remaining cells to gates
run("techmap -map +/techmap.v");
run("opt -fast");
}
if (check_label("map_cells")) {
// Needs to be done before logic optimization, so that inverters (inserted
// here because of negative-polarity output enable) are handled.
if (help_mode || !noiopad) {
run("iopadmap -bits -inpad INBUF Y:PAD -outpad OUTBUF D:PAD -toutpad TRIBUFF E:D:PAD -tinoutpad BIBUF E:Y:D:PAD",
"(unless -noiobs)");
}
std::string techmap_args = "-map +/techmap.v -map +/microchip/cells_map.v";
run("techmap " + techmap_args);
run("clean");
}
if (check_label("map_ffs")) {
// dfflegalize : Converts FFs to types supported by the target
// this can convert less capable cells into more capable cells (e.g. dff -> dffe)
// Based on PolarFire® FPGA Macro Library Guide
// D-flop:
// active high enable
// active low clear or active low set
// Latch:
// active low clear or active low set
// SLE (can implement D-flop/Latch):
// active high EN
// active low async load (set/reset) with static load configuration via ADn (Q = ~ADn)
// active low sync load (set/reset) with static load configuration via SD
// static latch configuration bit
// init not supported
// Yosys internal cell description
// see: https://yosyshq.readthedocs.io/projects/yosys/en/latest/yosys_internals/formats/cell_library.html
// see: common/simcells.v
// $_DFF_[NP]_ (regular dff)
// $_DFFE_[NP][NP]_ (enable)
// $_DFF_[NP][NP][01]_ (async reset to 0/1)
// $_DFFE_[NP][NP][01][NP]_ (async reset to 0/1 + enable)
// $_ALDFF_[NP][NP]_ (async load)
// $_ALDFFE_[NP][NP][NP]_ (async load + enable)
// $_DFFSR_[NP][NP][NP]_ (async set & reset)
// $_DFFSRE_[NP][NP][NP][NP]_ (async set & reset + enable)
// $_SDFF_[NP][NP][01]_ (sync reset to 0/1)
// $_SDFFE_[NP][NP][01][NP]_ (sync reset to 0/1 + enable, reset prioritize over enable)
// $_SDFFCE_[NP][NP][01][NP]_ (sync reset to 0/1 + enable, enable prioritize over reset)
// $_SR_[NP][NP]_ (set/reset latch)
// $_DLATCH_[NP]_ (D-latch)
// $_DLATCH_[NP][NP][01]_ (D-latch + reset to 0/1)
// $_DLATCHSR_[NP][NP][NP]_ (D-latch + set + reset)
if (family == "polarfire") {
std::string params = "";
// D-flop with async reset and enable
// posedge CLK, active low reset to 1 or 0, active high EN
params += " -cell $_DFFE_PN?P_ x";
// D-flop with sync reset and enable, enable takes priority over reset
// posedge CLK, active low reset to 1 or 0, active high EN
params += " -cell $_SDFFCE_PN?P_ x";
// D-latch + reset to 0/1
// posedge CLK, active low reset to 1 or 0
params += " -cell $_DLATCH_PN?_ x";
run("dfflegalize" + params, "(Converts FFs to supported types)");
}
if (abc9 || help_mode) {
if (dff || help_mode)
run("zinit -all w:* t:$_SDFFCE_*", "('-dff' only)");
run("techmap -D NO_LUT -map +/microchip/cells_map.v", "('-abc9' only)");
}
}
if (check_label("map_luts")) {
run("opt_expr -mux_undef -noclkinv");
if (help_mode)
run("abc -luts 2:2,3,6:5[,10,20] [-dff] [-D 1]", "(option for '-nowidelut', '-dff', '-retime')");
else if (abc9) {
std::string abc9_opts;
// for the if command in abc to specify wire delay between adjacent LUTs (default = 0)
// NOTE: should not have 0 wire delay between LUTs,
// otherwise abc might use LUT2+LUT3 instead of single LUT4
abc9_opts += " -W 300";
if (nowidelut)
abc9_opts += stringf(" -maxlut %d", lut_size);
if (dff)
abc9_opts += " -dff";
run("abc9" + abc9_opts);
} else {
std::string abc_opts = " -lut " + lut_size_s;
if (dff)
abc_opts += " -dff";
if (retime)
abc_opts += " -D 1";
run("abc" + abc_opts);
}
run("clean");
if (help_mode || !abc9)
run("techmap -D NO_LUT -map +/microchip/cells_map.v", "(only if not '-abc9')");
std::string techmap_args = "-map +/microchip/cells_map.v -D FINAL_MAP";
techmap_args += " -D LUT_WIDTH=" + lut_size_s;
run("techmap " + techmap_args);
if (help_mode || lut_size == 4)
run("microchip_dffopt");
}
run("clkbufmap -buf CLKINT Y:A -inpad CLKBUF Y:PAD");
run("clean -purge");
if (check_label("check")) {
run("hierarchy -check");
run("stat");
run("check -noinit");
run("blackbox =A:whitebox");
}
if (check_label("edif")) {
if (!edif_file.empty() || help_mode)
run(stringf("write_edif -pvector bra %s", edif_file.c_str()));
}
if (check_label("blif")) {
if (!blif_file.empty() || help_mode)
run(stringf("write_blif %s", blif_file.c_str()));
}
if (check_label("vlog"))
{
if (!vlog_file.empty() || help_mode)
run(stringf("write_verilog %s", help_mode ? "<file-name>" : vlog_file.c_str()));
}
}
} SynthMicrochipPass;
PRIVATE_NAMESPACE_END

View File

@ -0,0 +1,69 @@
# ISC License
#
# Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
#
# 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.
# asynchronous read
ram block $__uSRAM_AR_ {
#(LSRAM cost)/3
cost 43;
# INIT supported
init any;
abits 6;
widths 12 per_port;
# single write enable wire
port sw "W" {
clock posedge;
# collision not supported, but write takes precedence and read data is invalid while writing to
# the same address
wrtrans all new;
optional;
}
port ar "R" {
optional;
}
}
# synchronous read
# NOTE: synchronous read can be realized by the address pipeline register or data pipeline register.
# This assumes address is synchronized
ram block $__uSRAM_SR_ {
cost 42;
init any;
abits 6;
widths 12 per_port;
port sw "W" {
clock posedge;
# collision not supported
wrtrans all new;
optional;
}
port sr "R" {
clock posedge;
rden;
rdinit none;
optional;
}
}

View File

@ -0,0 +1,126 @@
/*
ISC License
Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
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.
*/
// See document PolarFire Family Fabric User Guide
// section 4.2 for port list.
// Asynchronous read
module $__uSRAM_AR_ (...);
parameter INIT = 0;
parameter ADDR_BITS = 6;
parameter PORT_W_WIDTH = 12;
parameter PORT_R_WIDTH = 12;
parameter PORT_R_USED = 0;
parameter PORT_W_USED = 0;
input PORT_W_CLK;
input [ADDR_BITS-1:0] PORT_W_ADDR;
input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA;
input PORT_W_WR_EN;
input [ADDR_BITS-1:0] PORT_R_ADDR;
output [PORT_R_WIDTH-1:0] PORT_R_RD_DATA;
`include "brams_defs.vh"
RAM64x12 #(
`PARAMS_INIT_uSRAM
) _TECHMAP_REPLACE_ (
.R_ADDR(PORT_R_ADDR),
.R_ADDR_BYPASS(1'b1),
.R_ADDR_EN(1'b0),
.R_ADDR_SL_N(1'b1),
.R_ADDR_SD(1'b0),
.R_ADDR_AL_N(1'b1),
.R_ADDR_AD_N(1'b0),
.BLK_EN(PORT_R_USED ? 1'b1 : 1'b0),
.R_DATA(PORT_R_RD_DATA),
.R_DATA_BYPASS(1'b1),
.R_DATA_EN(1'b0),
.R_DATA_SL_N(1'b1),
.R_DATA_SD(1'b0),
.R_DATA_AL_N(1'b1),
.R_DATA_AD_N(1'b0),
.W_CLK(PORT_W_CLK),
.W_ADDR(PORT_W_ADDR),
.W_DATA(PORT_W_WR_DATA),
.W_EN(PORT_W_WR_EN),
.BUSY_FB(1'b0)
);
endmodule
// Synchronous read
module $__uSRAM_SR_ (...);
parameter INIT = 0;
parameter ADDR_BITS = 6;
parameter PORT_W_WIDTH = 12;
parameter PORT_R_WIDTH = 12;
parameter PORT_R_USED = 0;
parameter PORT_W_USED = 0;
input PORT_W_CLK;
input [ADDR_BITS-1:0] PORT_W_ADDR;
input [PORT_W_WIDTH-1:0] PORT_W_WR_DATA;
input PORT_W_WR_EN;
// Read port clock and enable signal
// that async read uSRAM doesn't have
input PORT_R_CLK;
input PORT_R_RD_EN;
input [ADDR_BITS-1:0] PORT_R_ADDR;
output [PORT_R_WIDTH-1:0] PORT_R_RD_DATA;
`include "brams_defs.vh"
RAM64x12 #(
`PARAMS_INIT_uSRAM
) _TECHMAP_REPLACE_ (
.R_CLK(PORT_R_CLK),
.R_ADDR(PORT_R_ADDR),
.R_ADDR_BYPASS(1'b0),
.R_ADDR_EN(PORT_R_RD_EN),
.R_ADDR_SL_N(1'b1),
.R_ADDR_SD(1'b0),
.R_ADDR_AL_N(1'b1),
.R_ADDR_AD_N(1'b0),
.BLK_EN(PORT_R_USED ? 1'b1 : 1'b0),
.R_DATA(PORT_R_RD_DATA),
.R_DATA_BYPASS(1'b1),
.R_DATA_EN(1'b0),
.R_DATA_SL_N(1'b1),
.R_DATA_SD(1'b0),
.R_DATA_AL_N(1'b1),
.R_DATA_AD_N(1'b0),
.W_CLK(PORT_W_CLK),
.W_ADDR(PORT_W_ADDR),
.W_DATA(PORT_W_WR_DATA),
.W_EN(PORT_W_WR_EN),
.BUSY_FB(1'b0)
);
endmodule

4
tests/arch/microchip/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*.log
/run-test.mk
*.vm

View File

@ -0,0 +1,76 @@
# ISC License
#
# Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
#
# 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.
# active low async reset with enable
read_verilog <<EOT
module top(
input clk,
input en,
input rst,
input D,
output reg Q
);
always @(posedge clk, negedge rst) begin
if (rst == 0) begin
Q <= 1;
end else if(en) begin
Q <= D;
end
end
endmodule
EOT
synth_microchip -top top -family polarfire -noiopad
select -assert-count 1 t:SLE
select -assert-count 1 t:CLKBUF
select -assert-none t:SLE t:CLKBUF %% t:* %D
design -reset
read_verilog -D NO_INIT ../common/dffs.v
synth_microchip -top dff -family polarfire -noiopad
select -assert-count 1 t:SLE
select -assert-count 1 t:CLKBUF
select -assert-none t:SLE t:CLKBUF %% t:* %D
design -reset
read_verilog -D NO_INIT ../common/dffs.v
synth_microchip -top dffe -family polarfire -noiopad
select -assert-count 1 t:SLE
select -assert-count 1 t:CLKBUF
select -assert-none t:SLE t:CLKBUF %% t:* %D
design -reset
read_verilog -D NO_INIT ../common/adffs.v
synth_microchip -top adff -family polarfire -noiopad
select -assert-count 1 t:SLE
select -assert-count 1 t:CLKBUF
select -assert-count 1 t:CFG1
select -assert-none t:SLE t:CLKBUF t:CFG1 %% t:* %D
design -reset
read_verilog -D NO_INIT ../common/adffs.v
synth_microchip -top adffn -family polarfire -noiopad
select -assert-count 1 t:SLE
select -assert-count 1 t:CLKBUF
select -assert-none t:SLE t:CLKBUF %% t:* %D
design -reset
read_verilog -D NO_INIT ../common/adffs.v
synth_microchip -top dffs -family polarfire -noiopad
select -assert-count 1 t:SLE
select -assert-count 1 t:CLKBUF
select -assert-count 1 t:CFG1
select -assert-none t:SLE t:CLKBUF t:CFG1 %% t:* %D

View File

@ -0,0 +1,42 @@
# ISC License
#
# Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
#
# 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.
# reset can be merged into D LUT
read_verilog <<EOT
module dff_opt(
input clk,
input [1:0] D_comb,
input [1:0] EN_comb,
input [1:0] RST_comb,
output bar
);
reg foo;
assign bar = foo;
always@(posedge clk) begin
if (&RST_comb) begin
foo <= 0;
end else begin
foo <= &D_comb;
end
end
endmodule
EOT
synth_microchip -top dff_opt -family polarfire -noiopad
select -assert-count 1 t:SLE
select -assert-count 1 t:CFG4
select -assert-count 1 t:CLKBUF
select -assert-none t:SLE t:CFG4 t:CLKBUF %% t:* %D

211
tests/arch/microchip/dsp.ys Normal file
View File

@ -0,0 +1,211 @@
# ISC License
#
# Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
#
# 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.
# pre-adder
design -reset
read_verilog <<EOT
module pre_adder(
input signed [5:0] in_A,
input signed [4:0] in_B,
input signed [4:0] in_D,
output [11:0] out_Y
);
assign out_Y = in_A * (in_B + in_D);
endmodule
EOT
synth_microchip -top pre_adder -family polarfire -noiopad
select -assert-count 1 t:MACC_PA
select -assert-none t:MACC_PA %% t:* %D
# post-adder
design -reset
read_verilog <<EOT
module post_adder(
input signed[17:0] in_A,
input signed [17:0] in_B,
input signed [17:0] in_C,
output signed [35:0] out_Y
);
assign out_Y = (in_B*in_A)+in_C;
endmodule
EOT
synth_microchip -top post_adder -family polarfire -noiopad
select -assert-count 1 t:MACC_PA
select -assert-none t:MACC_PA %% t:* %D
# pre-adder + post-adder
design -reset
read_verilog <<EOT
module pre_post_adder(
input signed[5:0] in_A,
input signed [4:0] in_B,
input signed [11:0] in_C,
input signed [4:0] in_D,
output signed [12:0] out_Y
);
assign out_Y = ((in_D + in_B)*in_A)+in_C;
endmodule
EOT
synth_microchip -top pre_post_adder -family polarfire -noiopad
select -assert-count 1 t:MACC_PA
select -assert-none t:MACC_PA %% t:* %D
# multiply accumulate
design -reset
read_verilog <<EOT
module mac(
input clk,
input signed [4:0] in_A,
input signed [4:0] in_B,
input signed [4:0] in_D,
input srst_P,
output reg signed [11:0] out_P
);
always@(posedge clk) begin
if (~srst_P) begin
out_P <= 12'h000;
end else begin
out_P <= in_A * (in_B + in_D) + out_P;
end
end
endmodule
EOT
synth_microchip -top mac -family polarfire -noiopad
select -assert-count 1 t:MACC_PA
select -assert-none t:MACC_PA %% t:* %D
# cascade
design -reset
read_verilog <<EOT
module cas(
input signed [5:0] in_A,
input signed [4:0] in_B,
input signed [4:0] in_D,
input signed [4:0] casA,
input signed [4:0] casB,
output signed [11:0] out_P
);
wire signed [9:0] cascade;
assign cascade = casA * casB;
assign out_P = in_A * (in_B + in_D) + cascade;
endmodule
EOT
synth_microchip -top cas -family polarfire -noiopad
select -assert-count 2 t:MACC_PA
select -assert-none t:MACC_PA %% t:* %D
# carryout
design -reset
read_verilog <<EOT
module carryout (cout,out,a, b,c);
parameter n = 6;
parameter k = 2;
output reg [k*(n+1)-1:0] out;
output reg cout;
input [n:0] a;
input [n:0] b;
input [n-1:0] c;
always @(*)
begin
{cout,out} = a * b + c;
end
endmodule
EOT
synth_microchip -top carryout -family polarfire -noiopad
select -assert-count 1 t:MACC_PA
select -assert-none t:MACC_PA %% t:* %D
# pipeline registers
design -reset
read_verilog <<EOT
module pipeline(
input clk,
input srst_A,
input srst_B,
input srst_D,
input srst_P,
input arst_D,
input srst_C,
input signed [5:0] in_A,
input signed [4:0] in_B,
input signed [4:0] in_C,
input signed [4:0] in_D,
output reg [11:0] out_P
);
wire srst_A_N;
wire srst_B_N;
wire srst_C_N;
wire srst_D_N;
wire srst_P_N;
assign srst_A_N = ~srst_A;
assign srst_B_N = ~srst_B;
assign srst_C_N = ~srst_C;
assign srst_D_N = ~srst_D;
assign srst_P_N = ~srst_P;
reg signed [5:0] reg_A;
reg signed [4:0] reg_B;
reg signed [4:0] reg_C;
reg signed [4:0] reg_D;
always@(posedge clk) begin // sync reset A
// if (~srst_A_N) begin
if (srst_A_N) begin
reg_A = 6'b000000;
end else begin
reg_A = in_A;
end
end
always@(posedge clk) begin // sync reset B
if (srst_B_N) begin
reg_B = 5'b00000;
end else begin
reg_B = in_B;
end
end
always@(posedge clk, negedge arst_D) begin // async reset D
if (~arst_D) begin
reg_D = 5'b00000;
end else begin
reg_D = in_D;
end
end
always@(posedge clk) begin // sync reset C
if (srst_C_N) begin
reg_C = 5'b00000;
end else begin
reg_C = in_C;
end
end
// sync reset P
always@(posedge clk) begin
if (srst_P_N) begin
out_P = 12'h000;
end else begin
out_P = reg_A * (reg_B + reg_D) + reg_C;
end
end
endmodule
EOT
synth_microchip -top pipeline -family polarfire -noiopad
select -assert-count 1 t:MACC_PA
select -assert-none t:MACC_PA %% t:* %D

View File

@ -0,0 +1,51 @@
# ISC License
#
# Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
#
# 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.
# regular unsigned multiply
read_verilog ../common/mul.v
chparam -set X_WIDTH 8 -set Y_WIDTH 8 -set A_WIDTH 16
hierarchy -top top
proc
synth_microchip -family polarfire -noiopad
cd top # Constrain all select calls below inside the top module
select -assert-count 1 t:MACC_PA
select -assert-none t:MACC_PA %% t:* %D
# regular signed multiply
design -reset
read_verilog <<EOT
module signed_mult(
input signed [17:0] in_A,
input signed [17:0] in_B,
output signed [35:0] out_Y
);
assign out_Y = in_A * in_B;
endmodule
EOT
synth_microchip -top signed_mult -family polarfire -noiopad
select -assert-count 1 t:MACC_PA
select -assert-none t:MACC_PA %% t:* %D
# wide multiply
design -reset
read_verilog ../common/mul.v
chparam -set X_WIDTH 30 -set Y_WIDTH 16 -set A_WIDTH 46
hierarchy -top top
proc
synth_microchip -family polarfire -noiopad
cd top # Constrain all select calls below inside the top module
select -assert-count 2 t:MACC_PA
select -assert-none t:MACC_PA %% t:* %D

View File

@ -0,0 +1,50 @@
# ISC License
#
# Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
#
# 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.
read_verilog <<EOT
module ram_SDP(data,waddr,we,clk,q);
parameter d_width = 32;
parameter addr_width = 8;
parameter mem_depth = 256;
input [d_width-1:0] data;
input [addr_width-1:0] waddr;
input we, clk;
output reg [d_width-1:0] q;
reg [d_width-1:0] mem [mem_depth-1:0];
always @(posedge clk) begin
if (we) begin
mem[waddr] <= data;
end else begin
q <= mem[waddr];
end
end
endmodule
EOT
synth_microchip -top ram_SDP -family polarfire -noiopad
select -assert-count 1 t:RAM1K20
select -assert-count 1 t:CFG1
select -assert-none t:RAM1K20 t:CFG1 %% t:* %D
# very similar to ram_SDP.v, except read enable is always active
design -reset
read_verilog ../common/blockram.v
hierarchy -top sync_ram_sdp
chparam -set DATA_WIDTH 32 -set ADDRESS_WIDTH 8
synth_microchip -top sync_ram_sdp -family polarfire -noiopad
select -assert-count 1 t:RAM1K20
select -assert-none t:RAM1K20 %% t:* %D

View File

@ -0,0 +1,64 @@
# ISC License
#
# Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
#
# 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.
read_verilog <<EOT
module ram_TDP (clka,clkb,wea,addra,dataina,qa,web,addrb,datainb,qb);
parameter addr_width = 10;
parameter data_width = 2;
input clka,clkb,wea,web;
input [data_width - 1 : 0] dataina,datainb;
input [addr_width - 1 : 0] addra,addrb;
output reg [data_width - 1 : 0] qa,qb;
reg [addr_width - 1 : 0] addra_reg, addrb_reg;
reg [data_width - 1 : 0] mem [(2**addr_width) - 1 : 0];
always @ (posedge clka)
begin
addra_reg <= addra;
if(wea) begin
mem[addra] <= dataina;
qa <= dataina;
end else begin
qa <= mem[addra];
end
end
always @ (posedge clkb)
begin
addrb_reg <= addrb;
if(web) begin
mem[addrb] <= datainb;
qb <= datainb;
end else begin
qb <= mem[addrb];
end
end
endmodule
EOT
synth_microchip -top ram_TDP -family polarfire -noiopad
select -assert-count 1 t:RAM1K20
select -assert-none t:RAM1K20 %% t:* %D
# similar to ram_TDP.v, but different write mode and read_enable=~write_enable
design -reset
read_verilog ../common/blockram.v
hierarchy -top sync_ram_tdp
chparam -set DATA_WIDTH 2 -set ADDRESS_WIDTH 10
synth_microchip -top sync_ram_tdp -family polarfire -noiopad
select -assert-count 1 t:RAM1K20
select -assert-count 2 t:CFG1
select -assert-none t:RAM1K20 t:CFG1 %% t:* %D

View File

@ -0,0 +1,28 @@
# ISC License
#
# Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
#
# 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.
read_verilog <<EOT
module reduce(
input [7:0] data,
output Y
);
assign Y = ^data;
endmodule
EOT
synth_microchip -top reduce -family polarfire -noiopad
select -assert-count 1 t:XOR8
select -assert-none t:XOR8 %% t:* %D

View File

@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -eu
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

@ -0,0 +1,22 @@
# ISC License
#
# Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
#
# 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.
read_verilog ../common/blockram.v
hierarchy -top sync_ram_sp
chparam -set DATA_WIDTH 20 -set ADDRESS_WIDTH 10
synth_microchip -top sync_ram_sp -family polarfire -noiopad
select -assert-count 1 t:RAM1K20
select -assert-none t:RAM1K20 %% t:* %D

View File

@ -0,0 +1,40 @@
# ISC License
#
# Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
#
# 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.
read_verilog <<EOT
module uram_ar(data,waddr,we,clk,q);
parameter d_width = 12;
parameter addr_width = 2;
parameter mem_depth = 12;
input [d_width-1:0] data;
input [addr_width-1:0] waddr;
input we, clk;
output [d_width-1:0] q;
reg [d_width-1:0] mem [mem_depth-1:0];
assign q = mem[waddr];
always @(posedge clk) begin
if (we)
mem[waddr] <= data;
end
endmodule
EOT
synth_microchip -top uram_ar -family polarfire -noiopad
select -assert-count 1 t:RAM64x12
select -assert-none t:RAM64x12 %% t:* %D

View File

@ -0,0 +1,45 @@
# ISC License
#
# Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
#
# 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.
read_verilog <<EOT
module uram_sr(clk, wr, raddr, din, waddr, dout);
input clk;
input [11:0] din;
input wr;
input [5:0] waddr, raddr;
output [11:0] dout;
reg [5:0] raddr_reg;
reg [11:0] mem [0:63];
assign dout = mem[raddr_reg];
integer i;
initial begin
for (i = 0; i < 64; i = i + 1) begin
mem[i] = 12'hfff;
end
end
always@(posedge clk) begin
raddr_reg <= raddr;
if(wr)
mem[waddr]<= din;
end
endmodule
EOT
synth_microchip -top uram_sr -family polarfire -noiopad
select -assert-count 1 t:RAM64x12
select -assert-none t:RAM64x12 %% t:* %D

View File

@ -0,0 +1,41 @@
# ISC License
#
# Copyright (C) 2024 Microchip Technology Inc. and its subsidiaries
#
# 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.
read_verilog <<EOT
module widemux(
input [3:0] data,
input S0,
input S1,
output Y
);
assign Y = S1 ? (S0 ? data[3] : data[1]) : (S0 ? data[2] : data[0]);
endmodule
EOT
synth_microchip -top widemux -family polarfire -noiopad
select -assert-count 1 t:MX4
select -assert-none t:MX4 %% t:* %D
# RTL style is different here forming a different structure
read_verilog ../common/mux.v
design -save read
hierarchy -top mux4
proc
equiv_opt -assert -map +/microchip/cells_sim.v synth_microchip -top mux4 -family polarfire -noiopad
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:CFG3
select -assert-none t:CFG3 %% t:* %D