fabulous: Add a viaduct uarch
Signed-off-by: gatecat <gatecat@ds0.me>
This commit is contained in:
parent
b9b16eaa53
commit
f423055390
@ -1,4 +1,4 @@
|
||||
set(VIADUCT_UARCHES "example" "okami")
|
||||
set(VIADUCT_UARCHES "example" "okami" "fabulous")
|
||||
foreach(uarch ${VIADUCT_UARCHES})
|
||||
aux_source_directory(${family}/viaduct/${uarch} UARCH_FILES)
|
||||
foreach(target ${family_targets})
|
||||
|
88
generic/viaduct/fabulous/constids.inc
Normal file
88
generic/viaduct/fabulous/constids.inc
Normal file
@ -0,0 +1,88 @@
|
||||
X(FABULOUS_LC)
|
||||
X(FABULOUS_COMB)
|
||||
X(FABULOUS_FF)
|
||||
|
||||
X(SET_NORESET)
|
||||
X(ASYNC_SR)
|
||||
X(NEG_CLK)
|
||||
X(FF)
|
||||
X(LATCH_NOFF)
|
||||
|
||||
X(IO_1_bidirectional_frame_config_pass)
|
||||
X(InPass4_frame_config)
|
||||
X(OutPass4_frame_config)
|
||||
X(RegFile_32x4)
|
||||
X(MULADD)
|
||||
X(MUX8LUT_frame_config)
|
||||
|
||||
X(CLK)
|
||||
X(I)
|
||||
X(T)
|
||||
X(O)
|
||||
X(Q)
|
||||
X(Ci)
|
||||
X(Co)
|
||||
|
||||
X(X0Y0)
|
||||
|
||||
X(REG_CLK)
|
||||
X(LUT_CLK)
|
||||
X(global_clock)
|
||||
|
||||
X(Global_Clock)
|
||||
X(O2Q)
|
||||
|
||||
X(WRITE_DATA)
|
||||
X(WRITE_ADDRESS)
|
||||
X(READ_DATA)
|
||||
X(READ_ADDRESS)
|
||||
X(DSP_DATA_OUT)
|
||||
X(DSP_DATA_IN)
|
||||
X(DSP_CLR)
|
||||
|
||||
X(carry_in)
|
||||
X(carry_out)
|
||||
|
||||
X(LUTFF)
|
||||
X(LUTFF_E)
|
||||
X(LUTFF_SR)
|
||||
X(LUTFF_SS)
|
||||
X(LUTFF_ESR)
|
||||
X(LUTFF_ESS)
|
||||
X(LUTFF_R)
|
||||
X(LUTFF_S)
|
||||
X(LUTFF_ER)
|
||||
X(LUTFF_ES)
|
||||
|
||||
X(LUTFF_N)
|
||||
X(LUTFF_NE)
|
||||
X(LUTFF_NSR)
|
||||
X(LUTFF_NSS)
|
||||
X(LUTFF_NESR)
|
||||
X(LUTFF_NESS)
|
||||
X(LUTFF_NR)
|
||||
X(LUTFF_NS)
|
||||
X(LUTFF_NER)
|
||||
X(LUTFF_NES)
|
||||
|
||||
X(clr)
|
||||
|
||||
X(__disconnected)
|
||||
X(INIT)
|
||||
|
||||
X(E)
|
||||
X(C)
|
||||
X(D)
|
||||
X(S)
|
||||
X(R)
|
||||
X(SR)
|
||||
X(EN)
|
||||
|
||||
X(BEL)
|
||||
X(PAD)
|
||||
X(LUT1)
|
||||
|
||||
X(BelBegin)
|
||||
X(BelEnd)
|
||||
X(GlobalClk)
|
||||
X(CFG)
|
102
generic/viaduct/fabulous/fab_cfg.h
Normal file
102
generic/viaduct/fabulous/fab_cfg.h
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2021-22 gatecat <gatecat@ds0.me>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifndef FAB_CFG_H
|
||||
#define FAB_CFG_H
|
||||
|
||||
#include "fab_defs.h"
|
||||
#include "hashlib.h"
|
||||
#include "idstring.h"
|
||||
#include "nextpnr_assertions.h"
|
||||
#include "nextpnr_namespaces.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
/*
|
||||
This set of structures is designed to enumerate the different configurable options for a fabulous architecture,
|
||||
affecting the packer etc...
|
||||
*/
|
||||
|
||||
struct ControlSetConfig
|
||||
{
|
||||
/*
|
||||
CLB signal routing masks for fast validity checking
|
||||
for each unique CLK/CE/SR input to a CLB, add an entry to this vector, and set the bits to 1 for each ff that
|
||||
signal can drive for a CLB with 8 FFs and 2 clocks split at halfway, the first entry would be 0x0F and the second
|
||||
0xF0
|
||||
*/
|
||||
std::vector<route_mask_t> routing = {0b11111111}; // default 1 shared between all
|
||||
bool have_signal = true;
|
||||
bool can_invert = false;
|
||||
};
|
||||
|
||||
struct LogicConfig
|
||||
{
|
||||
// ** Core CLB config
|
||||
unsigned lc_per_clb = 8; // number of logic cells per clb
|
||||
bool split_lc = false; // whether to represent SLICE as a single bel or separate lut+ff (latter important if ff and
|
||||
// lut can be used separately)
|
||||
|
||||
// ** LUT config
|
||||
unsigned lut_k = 4; // base number of inputs for lookup table
|
||||
enum LutType
|
||||
{
|
||||
SINGLE_LUT,
|
||||
// ...
|
||||
} lut_type = LutType::SINGLE_LUT; // different types of fracturable LUT structure
|
||||
|
||||
enum LutCascade
|
||||
{
|
||||
NO_CASCADE,
|
||||
// ...
|
||||
} lut_casc = LutCascade::NO_CASCADE; // different types of cascading between LUTs
|
||||
|
||||
// TODO: other features we might want to represent...
|
||||
// TODO: fracLUT/FF/mux/carry output sharing matrices
|
||||
|
||||
// ** Carry config
|
||||
enum CarryType
|
||||
{
|
||||
NO_CARRY, // no carry chain
|
||||
HA_PRE_LUT, // half addder before LUT (classic fabulous LC)
|
||||
PG_POST_LUT, // prop/gen logic after a fractured LUT
|
||||
FA_POST_LUT, // full adder after a fractured LUT
|
||||
} carry_type = CarryType::HA_PRE_LUT;
|
||||
int carry_lut_frac = -1; // how the LUT is fractured for PG_POST_LUT/FA_POST_LUT, if the LUT fracturing is different
|
||||
// (or only supported) for carry modes and not in general
|
||||
|
||||
// ** FF config
|
||||
unsigned ff_per_lc = 1; // number of flipflops per logic cell
|
||||
uint32_t dedi_ff_input = 0; // mask of flipflops in a LC that have dedicated inputs
|
||||
uint32_t dedi_ff_output = 0; // mask of flipflops in a LC that have dedicated outputs
|
||||
|
||||
ControlSetConfig clk, sr, en; // flipflop control set routing
|
||||
};
|
||||
|
||||
struct FabricConfig
|
||||
{
|
||||
LogicConfig clb;
|
||||
// DSP cascading, BRAM, IP rules, IO, clocking ...
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
39
generic/viaduct/fabulous/fab_defs.h
Normal file
39
generic/viaduct/fabulous/fab_defs.h
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2022 gatecat <gatecat@ds0.me>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FAB_DEFS_H
|
||||
#define FAB_DEFS_H
|
||||
|
||||
#include <cstdint>
|
||||
#include "nextpnr_namespaces.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
/*
|
||||
This defines some compile-time maximums for the fabulous arch
|
||||
*/
|
||||
|
||||
static constexpr unsigned MAX_LUTK = 6; // max number of LUT inputs
|
||||
|
||||
typedef uint64_t route_mask_t; // the width of this type defines the max number of FFs in a CLB (for defining control
|
||||
// set route patterns)
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
175
generic/viaduct/fabulous/fabric_parsing.h
Normal file
175
generic/viaduct/fabulous/fabric_parsing.h
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2022 gatecat <gatecat@ds0.me>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FABULOUS_PARSING_H
|
||||
#define FABULOUS_PARSING_H
|
||||
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include "idstring.h"
|
||||
#include "nextpnr_assertions.h"
|
||||
#include "nextpnr_namespaces.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
struct BaseCtx;
|
||||
|
||||
// Lightweight NIH string_view
|
||||
struct parser_view
|
||||
{
|
||||
char *m_ptr;
|
||||
size_t m_length;
|
||||
parser_view() : m_ptr(nullptr), m_length(0){};
|
||||
explicit parser_view(std::string &str) : m_ptr(&(str[0])), m_length(str.size()){};
|
||||
parser_view(char *ptr, size_t length) : m_ptr(ptr), m_length(length){};
|
||||
|
||||
static constexpr size_t npos = std::numeric_limits<size_t>::max();
|
||||
char operator[](size_t idx)
|
||||
{
|
||||
NPNR_ASSERT(idx < m_length);
|
||||
return m_ptr[idx];
|
||||
}
|
||||
|
||||
size_t size() const { return m_length; }
|
||||
|
||||
bool empty() const { return m_length == 0; }
|
||||
|
||||
parser_view substr(size_t start, size_t length = npos)
|
||||
{
|
||||
NPNR_ASSERT(start <= m_length);
|
||||
if (length == npos)
|
||||
length = m_length - start;
|
||||
NPNR_ASSERT(length <= m_length);
|
||||
return parser_view(m_ptr + start, length);
|
||||
}
|
||||
|
||||
size_t find(char tok) const
|
||||
{
|
||||
for (size_t i = 0; i < m_length; i++)
|
||||
if (m_ptr[i] == tok)
|
||||
return i;
|
||||
return npos;
|
||||
}
|
||||
|
||||
IdString to_id(const BaseCtx *ctx)
|
||||
{
|
||||
// This isn't really ideal, let's hope one day we can move to C++20 and have proper string_views instead :3
|
||||
char tmp = m_ptr[m_length];
|
||||
m_ptr[m_length] = '\0';
|
||||
IdString id = IdString(ctx, m_ptr);
|
||||
m_ptr[m_length] = tmp;
|
||||
return id;
|
||||
}
|
||||
|
||||
long to_int()
|
||||
{
|
||||
// This isn't really ideal, let's hope one day we can move to C++20 and have proper string_views instead :3
|
||||
char tmp = m_ptr[m_length];
|
||||
m_ptr[m_length] = '\0';
|
||||
long l = strtol(m_ptr, nullptr, 0);
|
||||
m_ptr[m_length] = tmp;
|
||||
return l;
|
||||
}
|
||||
|
||||
parser_view strip(const std::string &ws = " \r\n\t")
|
||||
{
|
||||
char *ptr = m_ptr;
|
||||
size_t length = m_length;
|
||||
while (length > 0) { // strip front
|
||||
if (ws.find(*ptr) == std::string::npos) // not whitespace
|
||||
break;
|
||||
ptr++;
|
||||
length--;
|
||||
}
|
||||
while (length > 0) { // strip back
|
||||
if (ws.find(ptr[length - 1]) == std::string::npos) // not whitespace
|
||||
break;
|
||||
length--;
|
||||
}
|
||||
return parser_view(ptr, length);
|
||||
}
|
||||
|
||||
char back()
|
||||
{
|
||||
NPNR_ASSERT(m_length > 0);
|
||||
return m_ptr[m_length - 1];
|
||||
}
|
||||
|
||||
parser_view back(size_t count)
|
||||
{
|
||||
NPNR_ASSERT(count <= m_length);
|
||||
return parser_view(m_ptr + (m_length - count), count);
|
||||
}
|
||||
|
||||
bool starts_with(const std::string &st)
|
||||
{
|
||||
if (m_length < st.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < st.length(); i++)
|
||||
if (m_ptr[i] != st[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
std::pair<parser_view, parser_view> split(char delim) const
|
||||
{
|
||||
size_t pos = find(delim);
|
||||
NPNR_ASSERT(pos != npos);
|
||||
return std::make_pair(parser_view(m_ptr, pos), parser_view(m_ptr + pos + 1, m_length - (pos + 1)));
|
||||
}
|
||||
};
|
||||
|
||||
struct CsvParser
|
||||
{
|
||||
explicit CsvParser(std::istream &in) : in(in){};
|
||||
std::istream ∈
|
||||
std::string buf;
|
||||
parser_view view;
|
||||
bool fetch_next_line()
|
||||
{
|
||||
while (!in.eof()) {
|
||||
std::getline(in, buf);
|
||||
view = parser_view(buf).strip();
|
||||
size_t end_pos = view.find('#');
|
||||
if (end_pos != parser_view::npos)
|
||||
view = view.substr(0, end_pos);
|
||||
view = view.strip();
|
||||
if (!view.empty())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
parser_view next_field()
|
||||
{
|
||||
size_t next_delim = view.find(',');
|
||||
if (next_delim == parser_view::npos) {
|
||||
parser_view result = view.substr(0, next_delim);
|
||||
view = parser_view();
|
||||
return result;
|
||||
} else {
|
||||
parser_view result = view.substr(0, next_delim);
|
||||
view = view.substr(next_delim + 1);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
396
generic/viaduct/fabulous/fabulous.cc
Normal file
396
generic/viaduct/fabulous/fabulous.cc
Normal file
@ -0,0 +1,396 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2021-22 gatecat <gatecat@ds0.me>
|
||||
*
|
||||
* 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 "fabric_parsing.h"
|
||||
#include "log.h"
|
||||
#include "nextpnr.h"
|
||||
#include "util.h"
|
||||
#include "viaduct_api.h"
|
||||
#include "viaduct_helpers.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#define GEN_INIT_CONSTIDS
|
||||
#define VIADUCT_CONSTIDS "viaduct/fabulous/constids.inc"
|
||||
#include "viaduct_constids.h"
|
||||
|
||||
#include "fab_cfg.h"
|
||||
#include "fab_defs.h"
|
||||
#include "fasm.h"
|
||||
#include "pack.h"
|
||||
#include "validity_check.h"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
namespace {
|
||||
struct FabulousImpl : ViaductAPI
|
||||
{
|
||||
FabulousImpl(const dict<std::string, std::string> &args)
|
||||
{
|
||||
for (auto a : args) {
|
||||
if (a.first == "fasm")
|
||||
fasm_file = a.second;
|
||||
else
|
||||
log_error("unrecognised fabulous option '%s'\n", a.first.c_str());
|
||||
}
|
||||
}
|
||||
~FabulousImpl(){};
|
||||
void init(Context *ctx) override
|
||||
{
|
||||
init_uarch_constids(ctx);
|
||||
ViaductAPI::init(ctx);
|
||||
h.init(ctx);
|
||||
fab_root = get_env_var("FAB_ROOT", ", set it to the fabulous build output or project path");
|
||||
if (boost::filesystem::exists(fab_root + "/.FABulous"))
|
||||
is_new_fab = true;
|
||||
else
|
||||
is_new_fab = false;
|
||||
log_info("Detected FABulous %s format project.\n", is_new_fab ? "2.0" : "1.0");
|
||||
// To consider: a faster serialised form of the device data (like bba that other arches use) so we don't have to
|
||||
// go through the whole csv parsing malarkey each time
|
||||
blk_trk = std::make_unique<BlockTracker>(ctx, cfg);
|
||||
is_new_fab ? init_bels_v2() : init_bels_v1();
|
||||
init_pips();
|
||||
}
|
||||
|
||||
void pack() override { fabulous_pack(ctx, cfg); }
|
||||
|
||||
void postRoute() override
|
||||
{
|
||||
if (!fasm_file.empty())
|
||||
fabulous_write_fasm(ctx, cfg, fasm_file);
|
||||
}
|
||||
|
||||
void prePlace() override { assign_cell_info(); }
|
||||
bool isBelLocationValid(BelId bel) const override { return blk_trk->check_validity(bel, cfg, cell_tags); }
|
||||
|
||||
private:
|
||||
FabricConfig cfg; // TODO: non-default config
|
||||
ViaductHelpers h;
|
||||
|
||||
WireId global_clk_wire;
|
||||
|
||||
std::string fasm_file;
|
||||
|
||||
std::unique_ptr<BlockTracker> blk_trk;
|
||||
|
||||
std::string get_env_var(const std::string &name, const std::string &prompt = "")
|
||||
{
|
||||
const char *var = getenv(name.c_str());
|
||||
if (var == nullptr)
|
||||
log_error("environment variable '%s' is not set%s\n", name.c_str(), prompt.c_str());
|
||||
return std::string(var);
|
||||
}
|
||||
|
||||
std::ifstream open_data_rel(const std::string &postfix)
|
||||
{
|
||||
const std::string filename(fab_root + postfix);
|
||||
std::ifstream in(filename);
|
||||
if (!in)
|
||||
log_error("failed to open data file '%s' (is FAB_ROOT set correctly?)\n", filename.c_str());
|
||||
return in;
|
||||
}
|
||||
|
||||
std::string fab_root;
|
||||
bool is_new_fab;
|
||||
|
||||
pool<IdString> warned_beltypes;
|
||||
|
||||
void add_pseudo_pip(WireId src, WireId dst, IdString pip_type)
|
||||
{
|
||||
const auto &src_data = ctx->wire_info(src);
|
||||
IdStringList pip_name = IdStringList::concat(ctx->getWireName(src), ctx->getWireName(dst));
|
||||
ctx->addPip(pip_name, pip_type, src, dst, ctx->getDelayFromNS(0.05), Loc(src_data.x, src_data.y, 0));
|
||||
}
|
||||
|
||||
void handle_bel_ports(BelId bel, IdString tile, IdString bel_type, const std::vector<parser_view> &ports)
|
||||
{
|
||||
// TODO: improve the scalability here as we support more bel types
|
||||
IdString idx = ctx->getBelName(bel)[1];
|
||||
Loc loc = ctx->getBelLocation(bel);
|
||||
if (bel_type == id_IO_1_bidirectional_frame_config_pass) {
|
||||
for (parser_view p : ports) {
|
||||
IdString port_id = p.to_id(ctx);
|
||||
WireId port_wire = get_wire(tile, port_id, ctx->idf("W_IO_%s", port_id.c_str(ctx)));
|
||||
IdString pin = p.back(1).to_id(ctx);
|
||||
ctx->addBelPin(bel, pin, port_wire, pin.in(id_I, id_T) ? PORT_IN : PORT_OUT);
|
||||
}
|
||||
} else if (bel_type.in(id_InPass4_frame_config, id_OutPass4_frame_config)) {
|
||||
for (parser_view p : ports) {
|
||||
IdString port_id = p.to_id(ctx);
|
||||
WireId port_wire = get_wire(tile, port_id, port_id);
|
||||
IdString pin = p.back(2).to_id(ctx);
|
||||
ctx->addBelPin(bel, pin, port_wire, bel_type == id_OutPass4_frame_config ? PORT_IN : PORT_OUT);
|
||||
}
|
||||
} else if (bel_type == id_RegFile_32x4) {
|
||||
WireId clk_wire = get_wire(tile, id_CLK, id_REG_CLK);
|
||||
ctx->addBelInput(bel, id_CLK, clk_wire);
|
||||
add_pseudo_pip(global_clk_wire, clk_wire, id_global_clock);
|
||||
for (parser_view p : ports) {
|
||||
IdString port_id = p.to_id(ctx);
|
||||
// TODO: nicer way of determining port type?
|
||||
if (p[0] == 'D') {
|
||||
ctx->addBelInput(bel, port_id, get_wire(tile, port_id, id_WRITE_DATA));
|
||||
} else if (p[0] == 'W') {
|
||||
ctx->addBelInput(bel, port_id, get_wire(tile, port_id, id_WRITE_ADDRESS));
|
||||
} else if (p[1] == 'D') {
|
||||
ctx->addBelOutput(bel, port_id, get_wire(tile, port_id, id_READ_DATA));
|
||||
} else {
|
||||
ctx->addBelInput(bel, port_id, get_wire(tile, port_id, id_READ_ADDRESS));
|
||||
}
|
||||
}
|
||||
} else if (bel_type == id_MULADD) {
|
||||
// TODO: do DSPs need a clock too like regfiles?
|
||||
for (parser_view p : ports) {
|
||||
IdString port_id = p.to_id(ctx);
|
||||
if (p[0] == 'Q') {
|
||||
ctx->addBelOutput(bel, port_id, get_wire(tile, port_id, id_DSP_DATA_OUT));
|
||||
} else if (port_id == id_clr) {
|
||||
ctx->addBelInput(bel, port_id, get_wire(tile, port_id, id_DSP_CLR));
|
||||
} else {
|
||||
ctx->addBelInput(bel, port_id, get_wire(tile, port_id, id_DSP_DATA_IN));
|
||||
}
|
||||
}
|
||||
} else if (bel_type == id_MUX8LUT_frame_config) {
|
||||
for (parser_view p : ports) {
|
||||
IdString port_id = p.to_id(ctx);
|
||||
ctx->addBelPin(bel, port_id, get_wire(tile, port_id, ctx->idf("LUTMUX_%s", port_id.c_str(ctx))),
|
||||
p[0] == 'M' ? PORT_OUT : PORT_IN);
|
||||
}
|
||||
} else if (bel_type == id_FABULOUS_LC) {
|
||||
// TODO: split LC mode, LUT permutation pseudo-switchbox, LUT thru pseudo-pips
|
||||
WireId clk_wire = get_wire(tile, ctx->idf("L%s_CLK", idx.c_str(ctx)), id_LUT_CLK);
|
||||
ctx->addBelInput(bel, id_CLK, clk_wire);
|
||||
add_pseudo_pip(global_clk_wire, clk_wire, id_global_clock);
|
||||
blk_trk->set_bel_type(bel, BelFlags::BLOCK_CLB, BelFlags::FUNC_LC_COMB, loc.z);
|
||||
for (parser_view p : ports) {
|
||||
IdString port_id = p.to_id(ctx);
|
||||
WireId port_wire = get_wire(tile, port_id, ctx->idf("LUT_%s", port_id.c_str(ctx)));
|
||||
// TODO: more robust port name handling
|
||||
if (p[3] == 'S' || p[3] == 'E' || p[3] == 'I') { // set/reset, enable, LUT input
|
||||
ctx->addBelInput(bel, p.substr(3).to_id(ctx), port_wire);
|
||||
} else if (p[3] == 'O') { // LUT otuput
|
||||
ctx->addBelOutput(bel, p.substr(3, 1).to_id(ctx), port_wire);
|
||||
} else if (p[3] == 'C') { // carry chain
|
||||
if (p[4] == 'i') {
|
||||
ctx->addBelInput(bel, id_Ci, port_wire);
|
||||
} else {
|
||||
NPNR_ASSERT(p[4] == 'o');
|
||||
ctx->addBelOutput(bel, id_Co, port_wire);
|
||||
}
|
||||
} else {
|
||||
log_error("don't know what to do with LC port '%s'\n", port_id.c_str(ctx));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// ...
|
||||
if (!warned_beltypes.count(bel_type) && !ports.empty()) {
|
||||
log_warning("don't know how to handle ports for bel type '%s'\n", bel_type.c_str(ctx));
|
||||
warned_beltypes.insert(bel_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_global_clock()
|
||||
{
|
||||
// TODO: how do we extend this to more complex clocking topologies?
|
||||
BelId global_clk_bel =
|
||||
ctx->addBel(IdStringList::concat(ctx->id("X0Y0"), id_CLK), id_Global_Clock, Loc(0, 0, 0), true, false);
|
||||
global_clk_wire = ctx->addWire(IdStringList::concat(ctx->id("X0Y0"), id_CLK), id_CLK, 0, 0);
|
||||
ctx->addBelOutput(global_clk_bel, id_CLK, global_clk_wire);
|
||||
}
|
||||
|
||||
// TODO: this is for legacy fabulous only, the new code path can be a lot simpler
|
||||
void init_bels_v1()
|
||||
{
|
||||
std::ifstream in = open_data_rel("/npnroutput/bel.txt");
|
||||
CsvParser csv(in);
|
||||
init_global_clock();
|
||||
while (csv.fetch_next_line()) {
|
||||
IdString tile = csv.next_field().to_id(ctx);
|
||||
int bel_x = csv.next_field().substr(1).to_int();
|
||||
int bel_y = csv.next_field().substr(1).to_int();
|
||||
auto bel_idx = csv.next_field();
|
||||
IdString bel_type = csv.next_field().to_id(ctx);
|
||||
NPNR_ASSERT(bel_idx.size() == 1);
|
||||
int bel_z = bel_idx[0] - 'A';
|
||||
NPNR_ASSERT(bel_z >= 0 && bel_z < 26);
|
||||
/*
|
||||
In the future we will need to handle optionally splitting SLICEs into separate LUT/COMB and FF bels
|
||||
This is the preferred approach in nextpnr for arches where the LUT and FF can be used separately of
|
||||
each other (e.g. there is a way of routing the LUT and FF outputs individually, and some extra
|
||||
optional FF input).
|
||||
While this isn't yet the standard fabulous SLICE, it should be considered as a future option in fabulous.
|
||||
*/
|
||||
Loc loc(bel_x, bel_y, bel_z);
|
||||
BelId bel = ctx->addBel(IdStringList::concat(tile, bel_idx.to_id(ctx)), bel_type, loc, false, false);
|
||||
std::vector<parser_view> ports;
|
||||
parser_view port;
|
||||
while (!(port = csv.next_field()).empty()) {
|
||||
ports.push_back(port);
|
||||
}
|
||||
handle_bel_ports(bel, tile, bel_type, ports);
|
||||
}
|
||||
postprocess_bels();
|
||||
}
|
||||
|
||||
void init_bels_v2()
|
||||
{
|
||||
std::ifstream in = open_data_rel("/.FABulous/bel.v2.txt");
|
||||
CsvParser csv(in);
|
||||
init_global_clock();
|
||||
BelId curr_bel;
|
||||
while (csv.fetch_next_line()) {
|
||||
IdString cmd = csv.next_field().to_id(ctx);
|
||||
if (cmd == id_BelBegin) {
|
||||
IdString tile = csv.next_field().to_id(ctx);
|
||||
auto bel_idx = csv.next_field();
|
||||
IdString bel_type = csv.next_field().to_id(ctx);
|
||||
NPNR_ASSERT(bel_idx.size() == 1);
|
||||
int bel_z = bel_idx[0] - 'A';
|
||||
NPNR_ASSERT(bel_z >= 0 && bel_z < 26);
|
||||
Loc loc = tile_loc(tile);
|
||||
curr_bel = ctx->addBel(IdStringList::concat(tile, bel_idx.to_id(ctx)), bel_type,
|
||||
Loc(loc.x, loc.y, bel_z), false, false);
|
||||
} else if (cmd.in(id_I, id_O)) {
|
||||
IdString port = csv.next_field().to_id(ctx);
|
||||
auto wire_name = csv.next_field().split('.');
|
||||
WireId wire =
|
||||
get_wire(wire_name.first.to_id(ctx), wire_name.second.to_id(ctx), wire_name.second.to_id(ctx));
|
||||
ctx->addBelPin(curr_bel, port, wire, cmd == id_O ? PORT_OUT : PORT_IN);
|
||||
} else if (cmd == id_GlobalClk) {
|
||||
IdStringList bel_name = ctx->getBelName(curr_bel);
|
||||
WireId clk_wire = get_wire(bel_name[0], ctx->idf("%s_CLK", bel_name[1].c_str(ctx)), id_REG_CLK);
|
||||
ctx->addBelInput(curr_bel, id_CLK, clk_wire);
|
||||
add_pseudo_pip(global_clk_wire, clk_wire, id_global_clock);
|
||||
} else if (cmd == id_CFG) {
|
||||
// TODO...
|
||||
} else if (cmd == id_BelEnd) {
|
||||
curr_bel = BelId();
|
||||
} else if (cmd != IdString()) {
|
||||
log_error("unsupported command %s in definition of bel %s\n", cmd.c_str(ctx),
|
||||
curr_bel == BelId() ? "<none>" : ctx->nameOfBel(curr_bel));
|
||||
}
|
||||
}
|
||||
postprocess_bels();
|
||||
}
|
||||
|
||||
void postprocess_bels()
|
||||
{
|
||||
// This does some post-processing on bels to make them useful for nextpnr place-and-route regardless of the code
|
||||
// path that creates them. In the future, splitting muxes and creating split LCs would be done here, too
|
||||
for (auto bel : ctx->getBels()) {
|
||||
auto &data = ctx->bel_info(bel);
|
||||
if (data.type == id_FABULOUS_LC) {
|
||||
if (!data.pins.count(id_Q)) {
|
||||
// Add a Q pseudo-pin and pseudo-pip from Q to O
|
||||
WireId o_wire = ctx->getBelPinWire(bel, id_O);
|
||||
IdString q_name = ctx->idf("%s_Q", data.name[1].c_str(ctx));
|
||||
WireId q_wire = get_wire(data.name[0], q_name, q_name);
|
||||
ctx->addBelOutput(bel, id_Q, q_wire);
|
||||
// Pseudo-pip for FF mode
|
||||
add_pseudo_pip(q_wire, o_wire, id_O2Q);
|
||||
}
|
||||
} else if (data.type == id_IO_1_bidirectional_frame_config_pass) {
|
||||
if (!data.pins.count(id_PAD)) {
|
||||
// Add a PAD pseudo-pin for the top level
|
||||
ctx->addBelInout(bel, id_PAD,
|
||||
get_wire(data.name[0], ctx->idf("PAD_%s", data.name[1].c_str(ctx)), id_PAD));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_pips()
|
||||
{
|
||||
std::ifstream in = open_data_rel(is_new_fab ? "/.FABulous/pips.txt" : "/npnroutput/pips.txt");
|
||||
CsvParser csv(in);
|
||||
while (csv.fetch_next_line()) {
|
||||
IdString src_tile = csv.next_field().to_id(ctx);
|
||||
IdString src_port = csv.next_field().to_id(ctx);
|
||||
IdString dst_tile = csv.next_field().to_id(ctx);
|
||||
IdString dst_port = csv.next_field().to_id(ctx);
|
||||
int delay = csv.next_field().to_int();
|
||||
IdString pip_name = csv.next_field().to_id(ctx);
|
||||
WireId src_wire = get_wire(src_tile, src_port, src_port);
|
||||
WireId dst_wire = get_wire(dst_tile, dst_port, dst_port);
|
||||
ctx->addPip(IdStringList::concat(src_tile, pip_name), pip_name, src_wire, dst_wire,
|
||||
ctx->getDelayFromNS(0.01 * delay), tile_loc(src_tile));
|
||||
}
|
||||
}
|
||||
|
||||
// Fast lookup of tile names to XY pairs
|
||||
dict<IdString, Loc> tile2loc;
|
||||
Loc tile_loc(IdString tile)
|
||||
{
|
||||
if (!tile2loc.count(tile)) {
|
||||
std::string tile_name = tile.str(ctx);
|
||||
parser_view view(tile_name);
|
||||
NPNR_ASSERT(view[0] == 'X');
|
||||
size_t ypos = view.find('Y');
|
||||
NPNR_ASSERT(ypos != parser_view::npos);
|
||||
int x = view.substr(1, ypos - 1).to_int();
|
||||
int y = view.substr(ypos + 1).to_int();
|
||||
tile2loc[tile] = Loc(x, y, 0);
|
||||
}
|
||||
return tile2loc.at(tile);
|
||||
}
|
||||
|
||||
// Create a wire if it doesn't exist, otherwise just return it
|
||||
WireId get_wire(IdString tile, IdString wire, IdString type)
|
||||
{
|
||||
// Create a wire name by using the built-in IdStringList mechanism to store a (tile, wire) pair
|
||||
// this way we don't store a full string in memory of every concatenated wire name, reducing the memory
|
||||
// footprint and start time significantly beyond the ~1k LUT scale
|
||||
auto wire_name = IdStringList::concat(tile, wire);
|
||||
auto found = ctx->wire_by_name.find(wire_name);
|
||||
if (found != ctx->wire_by_name.end())
|
||||
return found->second;
|
||||
// doesn't exist
|
||||
Loc loc = tile_loc(tile);
|
||||
return ctx->addWire(wire_name, type, loc.x, loc.y);
|
||||
}
|
||||
|
||||
CellTagger cell_tags;
|
||||
void assign_cell_info()
|
||||
{
|
||||
for (auto &cell : ctx->cells) {
|
||||
cell_tags.assign_for(ctx, cfg, cell.second.get());
|
||||
}
|
||||
}
|
||||
void notifyBelChange(BelId bel, CellInfo *cell)
|
||||
{
|
||||
CellInfo *old = ctx->getBoundBelCell(bel);
|
||||
blk_trk->update_bel(bel, old, cell);
|
||||
}
|
||||
};
|
||||
|
||||
struct FabulousArch : ViaductArch
|
||||
{
|
||||
FabulousArch() : ViaductArch("fabulous"){};
|
||||
std::unique_ptr<ViaductAPI> create(const dict<std::string, std::string> &args)
|
||||
{
|
||||
return std::make_unique<FabulousImpl>(args);
|
||||
}
|
||||
} fabulousArch;
|
||||
} // namespace
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
191
generic/viaduct/fabulous/fasm.cc
Normal file
191
generic/viaduct/fabulous/fasm.cc
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2021-22 gatecat <gatecat@ds0.me>
|
||||
*
|
||||
* 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 "fasm.h"
|
||||
#include "log.h"
|
||||
#include "nextpnr.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
#include <fstream>
|
||||
|
||||
#define VIADUCT_CONSTIDS "viaduct/fabulous/constids.inc"
|
||||
#include "viaduct_constids.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
namespace {
|
||||
struct FabFasmWriter
|
||||
{
|
||||
FabFasmWriter(const Context *ctx, const FabricConfig &cfg, const std::string &filename)
|
||||
: ctx(ctx), cfg(cfg), out(filename)
|
||||
{
|
||||
if (!out)
|
||||
log_error("failed to open fasm file '%s' for writing\n", filename.c_str());
|
||||
}
|
||||
std::string format_name(IdStringList name)
|
||||
{
|
||||
std::string result;
|
||||
for (IdString entry : name) {
|
||||
if (!result.empty())
|
||||
result += ".";
|
||||
result += entry.str(ctx);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
void write_pip(PipId pip)
|
||||
{
|
||||
auto &data = ctx->pip_info(pip);
|
||||
if (data.type.in(id_global_clock, id_O2Q))
|
||||
return; // pseudo-pips with no underlying bitstream bits
|
||||
// write pip name but with '.' instead of '/' for separator
|
||||
out << format_name(data.name) << std::endl;
|
||||
}
|
||||
void write_routing(const NetInfo *net)
|
||||
{
|
||||
std::vector<PipId> sorted_pips;
|
||||
for (auto &w : net->wires)
|
||||
if (w.second.pip != PipId())
|
||||
sorted_pips.push_back(w.second.pip);
|
||||
std::sort(sorted_pips.begin(), sorted_pips.end());
|
||||
out << stringf("# routing for net '%s'\n", ctx->nameOf(net)) << std::endl;
|
||||
for (auto pip : sorted_pips)
|
||||
write_pip(pip);
|
||||
out << std::endl;
|
||||
}
|
||||
|
||||
std::string prefix;
|
||||
|
||||
// Write a FASM bitvector; optionally inverting the values in the process
|
||||
void write_vector(const std::string &name, const std::vector<bool> &value, bool invert = false)
|
||||
{
|
||||
out << prefix << name << " = " << int(value.size()) << "'b";
|
||||
for (auto bit : boost::adaptors::reverse(value))
|
||||
out << ((bit ^ invert) ? '1' : '0');
|
||||
out << std::endl;
|
||||
}
|
||||
// Write a FASM bitvector given an integer value
|
||||
void write_int_vector(const std::string &name, uint64_t value, int width, bool invert = false)
|
||||
{
|
||||
std::vector<bool> bits(width, false);
|
||||
for (int i = 0; i < width; i++)
|
||||
bits[i] = (value & (1ULL << i)) != 0;
|
||||
write_vector(name, bits, invert);
|
||||
}
|
||||
// Write an int vector param
|
||||
void write_int_vector_param(const CellInfo *cell, const std::string &name, uint64_t defval, int width,
|
||||
bool invert = false)
|
||||
{
|
||||
uint64_t value = int_or_default(cell->params, ctx->id(name), defval);
|
||||
std::vector<bool> bits(width, false);
|
||||
for (int i = 0; i < width; i++)
|
||||
bits[i] = (value & (1ULL << i)) != 0;
|
||||
write_vector(stringf("%s[%d:0]", name.c_str(), width - 1), bits, invert);
|
||||
}
|
||||
|
||||
void write_bool(const CellInfo *cell, const std::string &name)
|
||||
{
|
||||
if (bool_or_default(cell->params, ctx->id(name))) {
|
||||
out << prefix << name << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void write_logic(const CellInfo *lc)
|
||||
{
|
||||
prefix = format_name(ctx->getBelName(lc->bel)) + ".";
|
||||
if (lc->type.in(id_FABULOUS_LC, id_FABULOUS_COMB)) {
|
||||
write_int_vector_param(lc, "INIT", 0U, 1U << cfg.clb.lut_k); // todo lut depermute and thru
|
||||
}
|
||||
if (lc->type == id_FABULOUS_LC) {
|
||||
write_bool(lc, "FF");
|
||||
}
|
||||
if (lc->type.in(id_FABULOUS_LC, id_FABULOUS_FF)) {
|
||||
write_bool(lc, "SET_NORESET");
|
||||
write_bool(lc, "NEG_CLK");
|
||||
write_bool(lc, "NEG_EN");
|
||||
write_bool(lc, "NEG_SR");
|
||||
write_bool(lc, "ASYNC_SR");
|
||||
}
|
||||
}
|
||||
|
||||
void write_io(const CellInfo *io)
|
||||
{
|
||||
write_bool(io, "INPUT_USED");
|
||||
write_bool(io, "OUTPUT_USED");
|
||||
write_bool(io, "ENABLE_USED");
|
||||
}
|
||||
|
||||
void write_generic_cell(const CellInfo *ci)
|
||||
{
|
||||
prefix = format_name(ctx->getBelName(ci->bel)) + ".";
|
||||
for (auto ¶m : ci->params) {
|
||||
// TODO: better parameter type auto-detection
|
||||
if (param.second.is_string) {
|
||||
// enum type parameter
|
||||
out << prefix << param.first.c_str(ctx) << "." << param.second.str << std::endl;
|
||||
} else if (param.second.str.size() == 1) {
|
||||
// boolean type parameter
|
||||
if (param.second.intval != 0)
|
||||
out << prefix << param.first.c_str(ctx) << std::endl;
|
||||
} else {
|
||||
// vector type parameter
|
||||
int msb = int(param.second.str.size()) - 1;
|
||||
out << prefix << param.first.c_str(ctx) << "[" << msb << ":0] = ";
|
||||
for (auto bit : boost::adaptors::reverse(param.second.str))
|
||||
out << bit;
|
||||
out << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write_cell(const CellInfo *ci)
|
||||
{
|
||||
out << stringf("# config for cell '%s'\n", ctx->nameOf(ci)) << std::endl;
|
||||
if (ci->type.in(id_FABULOUS_COMB, id_FABULOUS_FF, id_FABULOUS_LC))
|
||||
write_logic(ci);
|
||||
else if (ci->type == id_IO_1_bidirectional_frame_config_pass)
|
||||
write_io(ci);
|
||||
else
|
||||
write_generic_cell(ci);
|
||||
// TODO: other cell types
|
||||
out << std::endl;
|
||||
}
|
||||
|
||||
void write_fasm()
|
||||
{
|
||||
for (const auto &net : ctx->nets)
|
||||
write_routing(net.second.get());
|
||||
for (const auto &cell : ctx->cells)
|
||||
write_cell(cell.second.get());
|
||||
}
|
||||
|
||||
const Context *ctx;
|
||||
const FabricConfig &cfg;
|
||||
std::ofstream out;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void fabulous_write_fasm(const Context *ctx, const FabricConfig &cfg, const std::string &filename)
|
||||
{
|
||||
FabFasmWriter wr(ctx, cfg, filename);
|
||||
wr.write_fasm();
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
32
generic/viaduct/fabulous/fasm.h
Normal file
32
generic/viaduct/fabulous/fasm.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2021-22 gatecat <gatecat@ds0.me>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FABULOUS_FASM_H
|
||||
#define FABULOUS_FASM_H
|
||||
|
||||
#include "fab_cfg.h"
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
void fabulous_write_fasm(const Context *ctx, const FabricConfig &cfg, const std::string &filename);
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
253
generic/viaduct/fabulous/pack.cc
Normal file
253
generic/viaduct/fabulous/pack.cc
Normal file
@ -0,0 +1,253 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2021-22 gatecat <gatecat@ds0.me>
|
||||
*
|
||||
* 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 "pack.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
#define VIADUCT_CONSTIDS "viaduct/fabulous/constids.inc"
|
||||
#include "viaduct_constids.h"
|
||||
#include "viaduct_helpers.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
namespace {
|
||||
struct FabulousPacker
|
||||
{
|
||||
Context *ctx;
|
||||
const FabricConfig &cfg;
|
||||
ViaductHelpers h;
|
||||
|
||||
dict<IdString, unsigned> lut_types;
|
||||
std::vector<IdString> lut_inputs;
|
||||
|
||||
FabulousPacker(Context *ctx, const FabricConfig &cfg) : ctx(ctx), cfg(cfg)
|
||||
{
|
||||
// Set up some structures for faster lookups
|
||||
for (unsigned i = 0; i < cfg.clb.lut_k; i++) {
|
||||
lut_types[ctx->idf("LUT%d", i + 1)] = i + 1;
|
||||
lut_inputs.push_back(ctx->idf("I%d", i));
|
||||
}
|
||||
h.init(ctx);
|
||||
}
|
||||
|
||||
void pack_luts()
|
||||
{
|
||||
// Pack LUTs into FABULOUS_COMB (split-LUTFF mode) or FABULOUS_LC (packed-LUTFF mode)
|
||||
// TODO: fracturable LUT handling
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo *ci = cell.second.get();
|
||||
auto fnd_lut = lut_types.find(ci->type);
|
||||
if (fnd_lut == lut_types.end())
|
||||
continue;
|
||||
unsigned lut_n = fnd_lut->second;
|
||||
// convert to the necessary type
|
||||
ci->type = cfg.clb.split_lc ? id_FABULOUS_COMB : id_FABULOUS_LC;
|
||||
// add disconnected unused inputs
|
||||
for (unsigned i = 0; i < cfg.clb.lut_k; i++)
|
||||
if (!ci->ports.count(lut_inputs.at(i)))
|
||||
ci->addInput(lut_inputs.at(i));
|
||||
// replicate the INIT value so the unused MSBs become don't-cares
|
||||
auto inst_init = get_or_default(ci->params, id_INIT, Property(0));
|
||||
unsigned orig_init_len = 1U << lut_n, prim_len = 1U << cfg.clb.lut_k;
|
||||
Property new_init(0, prim_len);
|
||||
for (unsigned i = 0; i < prim_len; i += orig_init_len) {
|
||||
auto chunk = inst_init.extract(0, orig_init_len);
|
||||
for (unsigned j = 0; j < orig_init_len; j++)
|
||||
new_init.str.at(i + j) = chunk.str.at(j);
|
||||
}
|
||||
new_init.update_intval();
|
||||
ci->params[id_INIT] = new_init;
|
||||
}
|
||||
}
|
||||
|
||||
// Two-stage flipflop packing. First convert all the random primitives into a much easier-to-handle FABULOUS_FF
|
||||
// Then for split-LC mode, cluster it to connected LUTs; separate-LC mode, pack it into a connected or new LC
|
||||
|
||||
void prepare_ffs()
|
||||
{
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo *ci = cell.second.get();
|
||||
const std::string &type_str = ci->type.str(ctx);
|
||||
if (type_str.size() < 5 || type_str.substr(0, 5) != "LUTFF")
|
||||
continue;
|
||||
ci->type = id_FABULOUS_FF;
|
||||
// parse config string and unify
|
||||
size_t idx = 5;
|
||||
if (idx < type_str.size() && type_str.at(idx) == '_')
|
||||
++idx;
|
||||
// clock inversion
|
||||
if (idx < type_str.size() && type_str.at(idx) == 'N') {
|
||||
ci->params[id_NEG_CLK] = 1;
|
||||
++idx;
|
||||
} else {
|
||||
ci->params[id_NEG_CLK] = 0;
|
||||
}
|
||||
// clock enable
|
||||
if (idx < type_str.size() && type_str.at(idx) == 'E')
|
||||
++idx;
|
||||
if (ci->ports.count(id_E))
|
||||
ci->renamePort(id_E, id_EN);
|
||||
else
|
||||
ci->addInput(id_EN); // autocreate emtpy enable port if enable missing or unused
|
||||
// sr presence and type
|
||||
std::string srt = type_str.substr(idx);
|
||||
if (srt == "S") {
|
||||
ci->params[id_SET_NORESET] = 1;
|
||||
ci->params[id_ASYNC_SR] = 1;
|
||||
} else if (srt == "R") {
|
||||
ci->params[id_SET_NORESET] = 0;
|
||||
ci->params[id_ASYNC_SR] = 1;
|
||||
} else if (srt == "SS") {
|
||||
ci->params[id_SET_NORESET] = 1;
|
||||
ci->params[id_ASYNC_SR] = 0;
|
||||
} else if (srt == "SR" || srt == "") {
|
||||
ci->params[id_SET_NORESET] = 0;
|
||||
ci->params[id_ASYNC_SR] = 0;
|
||||
} else {
|
||||
NPNR_ASSERT_FALSE("unhandled FF type");
|
||||
}
|
||||
if (ci->ports.count(id_S))
|
||||
ci->renamePort(id_S, id_SR);
|
||||
else if (ci->ports.count(id_R))
|
||||
ci->renamePort(id_R, id_SR);
|
||||
if (!ci->ports.count(id_SR))
|
||||
ci->addInput(id_SR); // autocreate emtpy enable port if enable missing or unused
|
||||
}
|
||||
}
|
||||
|
||||
void pack_ffs()
|
||||
{
|
||||
pool<IdString> to_delete;
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo *ci = cell.second.get();
|
||||
if (ci->type != id_FABULOUS_FF)
|
||||
continue;
|
||||
NetInfo *d = ci->getPort(id_D);
|
||||
if (!d || !d->driver.cell)
|
||||
continue;
|
||||
CellInfo *drv = d->driver.cell;
|
||||
if (drv->type != (cfg.clb.split_lc ? id_FABULOUS_COMB : id_FABULOUS_LC) || d->driver.port != id_O)
|
||||
continue;
|
||||
if (!cfg.clb.split_lc && d->users.entries() > 1)
|
||||
continue; // TODO: could also resolve by duplicating LUT
|
||||
if (drv->cluster != ClusterId()) {
|
||||
// TODO: actually we can pack these often, we just have to be more careful to check control sets
|
||||
continue;
|
||||
}
|
||||
// we can pack them together
|
||||
if (cfg.clb.split_lc) {
|
||||
// create/modify cluster and add constraints. copy from an arch where we do this already...
|
||||
NPNR_ASSERT_FALSE("unimplemented");
|
||||
} else {
|
||||
to_delete.insert(ci->name);
|
||||
// this connection is packed inside the LC
|
||||
ci->disconnectPort(id_D);
|
||||
drv->disconnectPort(id_O);
|
||||
// move other ports/params
|
||||
ci->movePortTo(id_CLK, drv, id_CLK);
|
||||
ci->movePortTo(id_SR, drv, id_SR);
|
||||
ci->movePortTo(id_EN, drv, id_EN);
|
||||
ci->movePortTo(id_O, drv, id_Q);
|
||||
drv->params[id_NEG_CLK] = ci->params[id_NEG_CLK];
|
||||
drv->params[id_ASYNC_SR] = ci->params[id_ASYNC_SR];
|
||||
drv->params[id_SET_NORESET] = ci->params[id_SET_NORESET];
|
||||
drv->params[id_FF] = 1;
|
||||
for (auto &attr : ci->attrs)
|
||||
drv->attrs[attr.first] = attr.second;
|
||||
}
|
||||
}
|
||||
for (auto del : to_delete)
|
||||
ctx->cells.erase(del);
|
||||
if (!cfg.clb.split_lc) {
|
||||
// convert remaining ffs to their own lc
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo *ci = cell.second.get();
|
||||
if (ci->type != id_FABULOUS_FF)
|
||||
continue;
|
||||
ci->type = id_FABULOUS_LC;
|
||||
ci->renamePort(id_D, lut_inputs.at(0));
|
||||
ci->renamePort(id_O, id_Q);
|
||||
// configure LUT as a thru
|
||||
Property init(1U << cfg.clb.lut_k);
|
||||
for (unsigned i = 0; i < (1U << cfg.clb.lut_k); i += 2) {
|
||||
init.str[i] = Property::S0;
|
||||
init.str[i + 1] = Property::S1;
|
||||
}
|
||||
init.update_intval();
|
||||
ci->params[id_INIT] = init;
|
||||
ci->params[id_FF] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update_bel_attrs()
|
||||
{
|
||||
// This new arch uses the new IdStringList system with a / separator
|
||||
// old fabulous arches used a dot separator in bel names
|
||||
// update old attributes for maximum cross-compat
|
||||
for (auto &cell : ctx->cells) {
|
||||
CellInfo *ci = cell.second.get();
|
||||
if (!ci->attrs.count(id_BEL))
|
||||
continue;
|
||||
std::string &bel = ci->attrs.at(id_BEL).str;
|
||||
if (bel.find('/') != std::string::npos) // new style
|
||||
continue;
|
||||
size_t dot_pos = bel.find('.');
|
||||
if (dot_pos != std::string::npos)
|
||||
bel[dot_pos] = '/';
|
||||
}
|
||||
}
|
||||
|
||||
void handle_constants()
|
||||
{
|
||||
const dict<IdString, Property> vcc_params = {{id_INIT, Property(0x3, 2)}};
|
||||
const dict<IdString, Property> gnd_params = {{id_INIT, Property(0x0, 2)}};
|
||||
h.replace_constants(CellTypePort(id_LUT1, id_O), CellTypePort(id_LUT1, id_O), vcc_params, gnd_params);
|
||||
}
|
||||
|
||||
void handle_io()
|
||||
{
|
||||
// As per the preferred approach for new nextpnr flows, we require IO to be inserted by Yosys
|
||||
// pre-place-and-route, or just manually instantiated
|
||||
const pool<CellTypePort> top_ports{
|
||||
CellTypePort(id_IO_1_bidirectional_frame_config_pass, id_PAD),
|
||||
};
|
||||
h.remove_nextpnr_iobs(top_ports);
|
||||
}
|
||||
|
||||
void run()
|
||||
{
|
||||
update_bel_attrs();
|
||||
handle_constants();
|
||||
handle_io();
|
||||
pack_luts();
|
||||
prepare_ffs();
|
||||
pack_ffs();
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
void fabulous_pack(Context *ctx, const FabricConfig &cfg)
|
||||
{
|
||||
FabulousPacker packer(ctx, cfg);
|
||||
packer.run();
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
32
generic/viaduct/fabulous/pack.h
Normal file
32
generic/viaduct/fabulous/pack.h
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2021-22 gatecat <gatecat@ds0.me>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef FABULOUS_PACK_H
|
||||
#define FABULOUS_PACK_H
|
||||
|
||||
#include "fab_cfg.h"
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
void fabulous_pack(Context *ctx, const FabricConfig &cfg);
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
201
generic/viaduct/fabulous/validity_check.cc
Normal file
201
generic/viaduct/fabulous/validity_check.cc
Normal file
@ -0,0 +1,201 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2021-22 gatecat <gatecat@ds0.me>
|
||||
*
|
||||
* 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 "validity_check.h"
|
||||
#include "log.h"
|
||||
#include "util.h"
|
||||
|
||||
#define VIADUCT_CONSTIDS "viaduct/fabulous/constids.inc"
|
||||
#include "viaduct_constids.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
CLBState::CLBState(const LogicConfig &cfg)
|
||||
{
|
||||
// TODO: more than one per LC if in split-SLICE mode with fracturable LUTs
|
||||
lc_comb = std::make_unique<CellInfo *[]>(cfg.lc_per_clb);
|
||||
if (cfg.split_lc) {
|
||||
ff = std::make_unique<CellInfo *[]>(cfg.lc_per_clb * cfg.ff_per_lc);
|
||||
}
|
||||
// TODO: mux
|
||||
}
|
||||
|
||||
void CellTagger::assign_for(const Context *ctx, const FabricConfig &cfg, const CellInfo *ci)
|
||||
{
|
||||
if (int(data.size()) <= ci->flat_index)
|
||||
data.resize(ci->flat_index + 1);
|
||||
auto &t = data.at(ci->flat_index);
|
||||
// Use the same logic to handle both packed and split LC modes
|
||||
if (ci->type.in(id_FABULOUS_COMB, id_FABULOUS_LC)) {
|
||||
unsigned lut_input_count = 0;
|
||||
for (unsigned i = 0; i < cfg.clb.lut_k; i++)
|
||||
if (ci->getPort(ctx->idf("I%d", i)))
|
||||
lut_input_count = i + 1;
|
||||
t.comb.lut_inputs = SSOArray<IdString, MAX_LUTK>(lut_input_count, IdString());
|
||||
for (unsigned i = 0; i < lut_input_count; i++) {
|
||||
const NetInfo *sig = ci->getPort(ctx->idf("I%d", i));
|
||||
t.comb.lut_inputs[i] = sig ? sig->name : IdString();
|
||||
}
|
||||
t.comb.carry_used = false; // TODO
|
||||
t.comb.lut_out = ci->getPort(id_O);
|
||||
}
|
||||
if (ci->type.in(id_FABULOUS_FF, id_FABULOUS_LC)) {
|
||||
if (ci->type == id_FABULOUS_FF || bool_or_default(ci->params, id_FF)) {
|
||||
t.ff.ff_used = true;
|
||||
auto get_ctrlsig = [&](IdString name) {
|
||||
const NetInfo *sig = ci->getPort(name);
|
||||
bool invert = sig && bool_or_default(ci->params, ctx->idf("NEG_%s", name.c_str(ctx)));
|
||||
return ControlSig(sig ? sig->name : id___disconnected, invert);
|
||||
};
|
||||
t.ff.clk = get_ctrlsig(id_CLK);
|
||||
t.ff.sr = get_ctrlsig(id_SR);
|
||||
t.ff.en = get_ctrlsig(id_EN);
|
||||
t.ff.async = bool_or_default(ci->params, id_ASYNC_SR);
|
||||
t.ff.latch = bool_or_default(ci->params, id_LATCH_NOFF);
|
||||
t.ff.d = ci->getPort(id_D);
|
||||
t.ff.q = ci->getPort(id_Q);
|
||||
} else {
|
||||
t.ff.ff_used = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BlockTracker::set_bel_type(BelId bel, BelFlags::BlockType block, BelFlags::FuncType func, uint8_t index)
|
||||
{
|
||||
Loc loc = ctx->getBelLocation(bel);
|
||||
if (int(tiles.size()) <= loc.y)
|
||||
tiles.resize(loc.y + 1);
|
||||
auto &row = tiles.at(loc.y);
|
||||
if (int(row.size()) <= loc.x)
|
||||
row.resize(loc.x + 1);
|
||||
auto &tile = row.at(loc.x);
|
||||
if (block == BelFlags::BLOCK_CLB) {
|
||||
if (!tile.clb)
|
||||
tile.clb = std::make_unique<CLBState>(cfg.clb);
|
||||
}
|
||||
if (int(bel_data.size()) <= bel.index)
|
||||
bel_data.resize(bel.index + 1);
|
||||
auto &flags = bel_data.at(bel.index);
|
||||
flags.block = block;
|
||||
flags.func = func;
|
||||
flags.index = index;
|
||||
}
|
||||
|
||||
void BlockTracker::update_bel(BelId bel, CellInfo *old_cell, CellInfo *new_cell)
|
||||
{
|
||||
if (bel.index >= int(bel_data.size()))
|
||||
return; // some kind of bel not being tracked
|
||||
auto flags = bel_data.at(bel.index);
|
||||
if (flags.block == BelFlags::BLOCK_OTHER)
|
||||
return; // no structures to update
|
||||
Loc loc = ctx->getBelLocation(bel);
|
||||
if (loc.y >= int(tiles.size()))
|
||||
return; // some kind of bel not being tracked
|
||||
const auto &row = tiles.at(loc.y);
|
||||
if (loc.x >= int(row.size()))
|
||||
return; // some kind of bel not being tracked
|
||||
const auto &entry = row.at(loc.x);
|
||||
if (flags.block == BelFlags::BLOCK_CLB) {
|
||||
NPNR_ASSERT(entry.clb);
|
||||
// TODO: incremental validity check updates might care about this in the future, hence keeping it in the
|
||||
// interface for now
|
||||
NPNR_UNUSED(old_cell);
|
||||
if (flags.func == BelFlags::FUNC_LC_COMB)
|
||||
entry.clb->lc_comb[flags.index] = new_cell;
|
||||
else if (flags.func == BelFlags::FUNC_FF)
|
||||
entry.clb->ff[flags.index] = new_cell;
|
||||
else if (flags.func == BelFlags::FUNC_MUX)
|
||||
entry.clb->mux[flags.index] = new_cell;
|
||||
}
|
||||
}
|
||||
|
||||
bool CLBState::check_validity(const LogicConfig &cfg, const CellTagger &cell_data)
|
||||
{
|
||||
SSOArray<ControlSig, 2> used_clk(cfg.clk.routing.size()), used_sr(cfg.sr.routing.size()),
|
||||
used_en(cfg.en.routing.size());
|
||||
auto check_ctrlsig = [&](unsigned idx, ControlSig actual, const ControlSetConfig &ctrl,
|
||||
SSOArray<ControlSig, 2> &used) {
|
||||
// see if we have an already-matching signal available
|
||||
for (unsigned i = 0; i < ctrl.routing.size(); i++) {
|
||||
// doesn't route to this pin
|
||||
if (((ctrl.routing.at(i) >> unsigned(idx)) & 0x1U) == 0)
|
||||
continue;
|
||||
if (used[i] == actual)
|
||||
return true;
|
||||
}
|
||||
// see if we have a free slot available
|
||||
for (unsigned i = 0; i < ctrl.routing.size(); i++) {
|
||||
// doesn't route to this pin
|
||||
if (((ctrl.routing.at(i) >> unsigned(idx)) & 0x1U) == 0)
|
||||
continue;
|
||||
if (used[i] != ControlSig())
|
||||
continue;
|
||||
used[i] = actual;
|
||||
return true;
|
||||
}
|
||||
// no option available
|
||||
return false;
|
||||
};
|
||||
for (unsigned z = 0; z < cfg.lc_per_clb; z++) {
|
||||
// flipflop control set checking
|
||||
if (cfg.split_lc) {
|
||||
NPNR_ASSERT_FALSE("unimplemented!"); // TODO
|
||||
} else {
|
||||
NPNR_ASSERT(cfg.ff_per_lc == 1); // split-slice mode must be used for more
|
||||
const CellInfo *lc = lc_comb[z];
|
||||
if (!lc)
|
||||
continue;
|
||||
auto &lct = cell_data.get(lc);
|
||||
if (lct.ff.ff_used) {
|
||||
// check shared control signals
|
||||
if (!check_ctrlsig(z, lct.ff.clk, cfg.clk, used_clk))
|
||||
return false;
|
||||
if (cfg.en.have_signal && !check_ctrlsig(z, lct.ff.en, cfg.en, used_en))
|
||||
return false;
|
||||
if (cfg.sr.have_signal && !check_ctrlsig(z, lct.ff.sr, cfg.sr, used_sr))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO: other checks...
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BlockTracker::check_validity(BelId bel, const FabricConfig &cfg, const CellTagger &cell_data)
|
||||
{
|
||||
if (bel.index >= int(bel_data.size()))
|
||||
return true; // some kind of bel not being tracked
|
||||
auto flags = bel_data.at(bel.index);
|
||||
if (flags.block == BelFlags::BLOCK_OTHER)
|
||||
return true; // no structures to update
|
||||
Loc loc = ctx->getBelLocation(bel);
|
||||
if (loc.y >= int(tiles.size()))
|
||||
return true; // some kind of bel not being tracked
|
||||
const auto &row = tiles.at(loc.y);
|
||||
if (loc.x >= int(row.size()))
|
||||
return true; // some kind of bel not being tracked
|
||||
const auto &entry = row.at(loc.x);
|
||||
if (flags.block == BelFlags::BLOCK_CLB) {
|
||||
return entry.clb->check_validity(cfg.clb, cell_data);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
127
generic/viaduct/fabulous/validity_check.h
Normal file
127
generic/viaduct/fabulous/validity_check.h
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
* nextpnr -- Next Generation Place and Route
|
||||
*
|
||||
* Copyright (C) 2021-22 gatecat <gatecat@ds0.me>
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifndef VALIDITY_CHECKING_H
|
||||
#define VALIDITY_CHECKING_H
|
||||
|
||||
#include "fab_cfg.h"
|
||||
#include "fab_defs.h"
|
||||
#include "sso_array.h"
|
||||
|
||||
#include "nextpnr.h"
|
||||
|
||||
NEXTPNR_NAMESPACE_BEGIN
|
||||
|
||||
// The validity checking engine for the fabulous configurable CLB
|
||||
|
||||
// data that we tag onto cells for fast lookup, so we aren't doing slow hash map accesses in the inner-loop-critical
|
||||
// validity checking code
|
||||
struct ControlSig
|
||||
{
|
||||
ControlSig() : net(), invert(false){};
|
||||
ControlSig(IdString net, bool invert) : net(net), invert(invert){};
|
||||
IdString net;
|
||||
bool invert;
|
||||
bool operator==(const ControlSig &other) const { return net == other.net && invert == other.invert; }
|
||||
bool operator!=(const ControlSig &other) const { return net != other.net || invert != other.invert; }
|
||||
};
|
||||
|
||||
struct CellTags
|
||||
{
|
||||
struct
|
||||
{
|
||||
SSOArray<IdString, MAX_LUTK> lut_inputs; // for checking fracturable LUTs
|
||||
bool carry_used = false;
|
||||
const NetInfo *lut_out = nullptr;
|
||||
// ...
|
||||
} comb; // data for LUTs, or the LUT part of combined LUT+FF cells
|
||||
struct
|
||||
{
|
||||
ControlSig clk, sr, en;
|
||||
bool ff_used = false;
|
||||
bool async = false;
|
||||
bool latch = false;
|
||||
const NetInfo *d = nullptr, *q = nullptr;
|
||||
} ff; // data for FFs, or the FF part of combined LUT+FF cells
|
||||
};
|
||||
|
||||
// map between cell and tags, using the flat_index that viaduct defines for this purpose
|
||||
struct CellTagger
|
||||
{
|
||||
std::vector<CellTags> data;
|
||||
const CellTags &get(const CellInfo *ci) const { return data.at(ci->flat_index); }
|
||||
void assign_for(const Context *ctx, const FabricConfig &cfg, const CellInfo *ci);
|
||||
};
|
||||
|
||||
// we need to add some extra data to CLB bels to track what they do, so we can update CLBState accordingly
|
||||
struct BelFlags
|
||||
{
|
||||
enum BlockType : uint8_t
|
||||
{
|
||||
BLOCK_OTHER,
|
||||
BLOCK_CLB,
|
||||
// ...
|
||||
} block = BlockType::BLOCK_OTHER;
|
||||
enum FuncType : uint8_t
|
||||
{
|
||||
FUNC_LC_COMB,
|
||||
FUNC_FF,
|
||||
FUNC_MUX,
|
||||
FUNC_OTHER,
|
||||
} func;
|
||||
uint8_t index;
|
||||
// ...
|
||||
};
|
||||
|
||||
// state of a CLB, for fast bel->cell lookup
|
||||
// TODO: add valid/dirty tracking for incremental validity re-checking, important once we have bigger/more complex CLBs
|
||||
// (cf. xilinx/intel arches in nextpnr)
|
||||
struct CLBState
|
||||
{
|
||||
explicit CLBState(const LogicConfig &cfg);
|
||||
// In combined-LC mode (LC bel contains LUT and FF), this indexes the entire LC bel to cell. In separate mode, this
|
||||
// indexes the combinational part (LUT or LUT+carry only).
|
||||
std::unique_ptr<CellInfo *[]> lc_comb;
|
||||
// In split-LC mode only, this maps FF bel (in CLB) index to cell
|
||||
std::unique_ptr<CellInfo *[]> ff;
|
||||
// If there is (a) separate mux bel(s), map them to cells
|
||||
std::unique_ptr<CellInfo *[]> mux;
|
||||
bool check_validity(const LogicConfig &cfg, const CellTagger &cell_data);
|
||||
};
|
||||
|
||||
struct BlockTracker
|
||||
{
|
||||
Context *ctx;
|
||||
const FabricConfig &cfg;
|
||||
std::vector<BelFlags> bel_data;
|
||||
|
||||
BlockTracker(Context *ctx, const FabricConfig &cfg) : ctx(ctx), cfg(cfg){};
|
||||
void set_bel_type(BelId bel, BelFlags::BlockType block, BelFlags::FuncType func, uint8_t index);
|
||||
void update_bel(BelId bel, CellInfo *old_cell, CellInfo *new_cell);
|
||||
struct TileData
|
||||
{
|
||||
std::unique_ptr<CLBState> clb;
|
||||
// ...
|
||||
};
|
||||
std::vector<std::vector<TileData>> tiles;
|
||||
bool check_validity(BelId bel, const FabricConfig &cfg, const CellTagger &cell_data);
|
||||
};
|
||||
|
||||
NEXTPNR_NAMESPACE_END
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user