blob: 543d3a92636889d2d91175add80db9a067144691 [file] [log] [blame]
#include "catch.hpp"
#include "vpr_api.h"
#include "vtr_util.h"
#include "rr_metadata.h"
#include "fasm.h"
#include "fasm_utils.h"
#include "arch_util.h"
#include "rr_graph_writer.h"
#include <sstream>
#include <fstream>
#include <regex>
#include <cmath>
#include <algorithm>
static constexpr const char kArchFile[] = "test_fasm_arch.xml";
static constexpr const char kRrGraphFile[] = "test_fasm_rrgraph.xml";
namespace {
// ============================================================================
using Catch::Matchers::Equals;
using Catch::Matchers::StartsWith;
using Catch::Matchers::Contains;
class RegexMatcher : public Catch::MatcherBase<std::string> {
std::string m_RegexStr;
std::regex m_Regex;
public:
RegexMatcher (const std::string& a_Regex) :
m_RegexStr(a_Regex), m_Regex(a_Regex) {}
bool match(const std::string& str) const override {
std::smatch smatch;
return std::regex_match(str, smatch, m_Regex);
}
virtual std::string describe() const override {
return "does not match the regex '" + m_RegexStr + "'";
}
};
inline RegexMatcher MatchesRegex (const std::string& a_Regex) {
return RegexMatcher(a_Regex);
}
// ============================================================================
TEST_CASE("find_tags", "[fasm]") {
const std::string feature ("{prefix}CLB_{loc}_{site}");
auto tags = fasm::find_tags_in_feature (feature);
REQUIRE (tags.size() == 3);
REQUIRE (std::find(tags.begin(), tags.end(), "prefix") != tags.end());
REQUIRE (std::find(tags.begin(), tags.end(), "loc") != tags.end());
REQUIRE (std::find(tags.begin(), tags.end(), "site") != tags.end());
}
TEST_CASE("substitute_tags_correct", "[fasm]") {
const std::string feature ("{prefix}CLB_{loc}_{site}");
const std::map<const std::string, std::string> tags = {
{"prefix", "L"},
{"loc", "X7Y8"},
{"site", "SLICE"}
};
auto result = fasm::substitute_tags(feature, tags);
REQUIRE(result.compare("LCLB_X7Y8_SLICE") == 0);
}
TEST_CASE("substitute_tags_undef", "[fasm]") {
const std::string feature ("{prefix}CLB_{loc}_{site}");
const std::map<const std::string, std::string> tags = {
{"loc", "X7Y8"},
{"site", "SLICE"}
};
REQUIRE_THROWS(fasm::substitute_tags(feature, tags));
}
// ============================================================================
/*
VPR may rearrange LUT inputs and modify its content accordingly. This function
compares two LUT contents through applying every possible bit permutation. If
one matches then those two LUT contents are considered to define the same LUT
*/
bool match_lut_init(const std::string& a_BlifInit, const std::string& a_FasmInit) {
// Strings differ in length, no match possible
if (a_BlifInit.length() != a_FasmInit.length()) {
return false;
}
// Skip "nn'b" at the beginning
const std::string blifInit = a_BlifInit.substr(4);
const std::string fasmInit = a_FasmInit.substr(4);
// LUT bits
size_t N = (size_t)log2f(blifInit.length());
// Decode indices of individual ones.
std::vector<int> blifOnes;
std::vector<int> fasmOnes;
for (size_t i=0; i<blifInit.length(); ++i) {
size_t bit = blifInit.length() - 1 - i;
if (blifInit[bit] == '1') {
blifOnes.push_back(i);
}
if (fasmInit[bit] == '1') {
fasmOnes.push_back(i);
}
}
// Must have the same number of ones. If not the no match possible.
if (blifOnes.size() != fasmOnes.size()) {
return false;
}
// Initialize bit indices for permutations
std::vector<int> indices(N);
for (size_t i=0; i<N; ++i) {
indices[i] = i;
}
// Check every possible permutation of LUT address bits
do {
// Compare ones positions
bool match = true;
for (size_t i=0; i<blifOnes.size(); ++i) {
// Remap position according to current index permutation
size_t fasmOne = 0;
for (size_t k=0; k<indices.size(); ++k) {
if (blifOnes[i] & (1<<k)) {
fasmOne |= (1<<indices[k]);
}
}
// If doesn't match then no need to check further.
if (std::find(fasmOnes.begin(), fasmOnes.end(), fasmOne) == fasmOnes.end())
{
match = false;
break;
}
}
// Got a match
if (match) {
return true;
}
} while (std::next_permutation(indices.begin(), indices.end()));
return false;
}
TEST_CASE("match_lut_init", "[fasm]") {
CHECK(match_lut_init("16'b0000000011111111", "16'b0000111100001111"));
}
TEST_CASE("fasm_integration_test", "[fasm]") {
{
t_vpr_setup vpr_setup;
t_arch arch;
t_options options;
const char *argv[] = {
"test_vpr",
kArchFile,
"wire.eblif",
"--route_chan_width",
"100",
};
vpr_init(sizeof(argv)/sizeof(argv[0]), argv,
&options, &vpr_setup, &arch);
bool flow_succeeded = vpr_flow(vpr_setup, arch);
REQUIRE(flow_succeeded == true);
auto &device_ctx = g_vpr_ctx.mutable_device();
for(size_t inode = 0; inode < device_ctx.rr_nodes.size(); ++inode) {
for(t_edge_size iedge = 0; iedge < device_ctx.rr_nodes[inode].num_edges(); ++iedge) {
auto sink_inode = device_ctx.rr_nodes[inode].edge_sink_node(iedge);
auto switch_id = device_ctx.rr_nodes[inode].edge_switch(iedge);
vpr::add_rr_edge_metadata(inode, sink_inode, switch_id,
"fasm_features", vtr::string_fmt("%d_%d_%zu",
inode, sink_inode, switch_id));
}
}
write_rr_graph(kRrGraphFile, vpr_setup.Segments);
vpr_free_all(arch, vpr_setup);
}
t_vpr_setup vpr_setup;
t_arch arch;
t_options options;
const char *argv[] = {
"test_vpr",
kArchFile,
"wire.eblif",
"--route_chan_width",
"100",
"--read_rr_graph",
kRrGraphFile,
};
vpr_init(sizeof(argv)/sizeof(argv[0]), argv,
&options, &vpr_setup, &arch);
vpr_setup.PackerOpts.doPacking = STAGE_LOAD;
vpr_setup.PlacerOpts.doPlacement = STAGE_LOAD;
vpr_setup.RouterOpts.doRouting = STAGE_LOAD;
vpr_setup.AnalysisOpts.doAnalysis = STAGE_SKIP;
bool flow_succeeded = vpr_flow(vpr_setup, arch);
REQUIRE(flow_succeeded == true);
std::stringstream fasm_string;
fasm::FasmWriterVisitor visitor(fasm_string);
NetlistWalker nl_walker(visitor);
nl_walker.walk();
// Write fasm to file
fasm_string.seekg(0);
std::ofstream fasm_file("output.fasm", std::ios_base::out);
while(fasm_string) {
std::string line;
std::getline(fasm_string, line);
fasm_file << line << std::endl;
}
// Load LUTs from the BLIF (comments)
std::vector<std::string> lut_defs;
std::string lut_def;
std::ifstream blif_file("wire.eblif", std::ios_base::in);
while (blif_file) {
std::string line;
std::getline(blif_file, line);
if (!line.length()) {
continue;
}
if (line.find("#") != std::string::npos) {
auto init_pos = line.find("#");
lut_def = line.substr(init_pos+2);
continue;
}
if (line.find(".names") != std::string::npos) {
REQUIRE(lut_def.length() > 0);
lut_defs.push_back(lut_def);
//fprintf(stderr, "'%s'\n", lut_def.c_str());
lut_def = "";
continue;
}
}
// Verify fasm
fasm_string.clear();
fasm_string.seekg(0);
std::set<std::tuple<int, int, short>> routing_edges;
std::set<std::tuple<int, int>> occupied_locs;
bool found_lut5 = false;
bool found_lut6 = false;
bool found_mux1 = false;
bool found_mux2 = false;
bool found_mux3 = false;
while(fasm_string) {
// Should see something like:
// CLB.FLE0_X1Y1.N2_LUT5
// PLB.FLE1_X2Y1.LUT5_1.LUT[31:0]=32'b00000000000000010000000000000000
// FLE0_X3Y1.OUT_MUX.LUT
// SLICE.FLE1_X4Y1.DISABLE_FF
// CLB.FLE0_X1Y3.LUT6[63:0]=64'b0000000000000000000000000000000100000000000000000000000000000000
// 3634_3690_0
std::string line;
std::getline(fasm_string, line);
if(line == "") {
continue;
}
// Look for muxes
{
std::smatch m;
if (std::regex_match(line, m, std::regex(".*FLE\\d?_X\\d+Y\\d+.OUT_MUX.LUT$"))) {
found_mux1 = true;
}
if (std::regex_match(line, m, std::regex(".*FLE\\d?_X\\d+Y\\d+.DISABLE_FF$"))) {
found_mux2 = true;
}
if (std::regex_match(line, m, std::regex(".*FLE\\d?_X\\d+Y\\d+.IN\\d{1}$"))) {
found_mux3 = true;
}
}
if(line.find("FLE") != std::string::npos) {
// Check correlation with top-level prefixes with X coordinates
// as defined in the architecture
if(line.find("_X1") != std::string::npos) {
CHECK_THAT(line, StartsWith("CLB"));
}
if(line.find("_X2") != std::string::npos) {
CHECK_THAT(line, StartsWith("PLB"));
}
if(line.find("_X3") != std::string::npos) {
// X3 has no top-level prefix, so the intermediate one is the
// first
CHECK_THAT(line, StartsWith("FLE"));
}
if(line.find("_X4") != std::string::npos) {
CHECK_THAT(line, StartsWith("SLICE"));
}
// Check presence of LOC prefix substitutions
CHECK_THAT(line, MatchesRegex(".*X\\d+Y\\d+.*"));
// Extract loc from tag
std::smatch locMatch;
REQUIRE(std::regex_match(line, locMatch, std::regex(".*X(\\d+)Y(\\d+).*")));
REQUIRE(locMatch.size() == 3);
int loc_x = vtr::atoi(locMatch[1].str());
int loc_y = vtr::atoi(locMatch[2].str());
occupied_locs.insert(std::make_tuple(loc_x, loc_y));
// Check presence of ALUT_{LR} with substituted L/R
if(line.find("ALUT") != std::string::npos) {
CHECK_THAT(line, MatchesRegex(".*ALUT_[LR]{1}.*"));
}
// Check correct substitution of "" and "_SING"
if (loc_y == 1) {
CHECK_THAT(line, Contains("_SING"));
}
else {
CHECK_THAT(line, !Contains("_SING"));
}
// Check that all tags were substituted
CHECK_THAT(line, !Contains("{") && !Contains("}"));
// Check LUT
auto pos = line.find("LUT[");
if(pos != std::string::npos) {
auto str = line.substr(pos);
std::regex regex("LUT\\[31:0\\]=(32'b[01]{32})$");
std::smatch match;
if (std::regex_match(str, match, regex)) {
bool found = false;
for (auto itr=lut_defs.begin(); itr!=lut_defs.end(); itr++) {
if (match_lut_init(*itr, match[1].str())) {
found = true;
lut_defs.erase(itr);
break;
}
}
if (!found) {
FAIL_CHECK("LUT definition '" << match[1] << "' not found in the BLIF file!");
} else {
found_lut5 = true;
}
}
else {
FAIL_CHECK("LUT definition '" << line << "' is malformed!");
}
}
// Check LUT6
pos = line.find("LUT6[");
if(pos != std::string::npos) {
auto str = line.substr(pos);
std::regex regex("LUT6\\[63:0\\]=(64'b[01]{64})$");
std::smatch match;
if (std::regex_match(str, match, regex)) {
bool found = false;
for (auto itr=lut_defs.begin(); itr!=lut_defs.end(); itr++) {
if (match_lut_init(*itr, match[1].str())) {
found = true;
lut_defs.erase(itr);
break;
}
}
if (!found) {
FAIL_CHECK("LUT definition '" << match[1] << "' not found in the BLIF file!");
} else {
found_lut6 = true;
}
}
else {
FAIL_CHECK("LUT definition '" << line << "' is malformed!");
}
}
} else {
auto parts = vtr::split(line, "_");
REQUIRE(parts.size() == 3);
auto src_inode = vtr::atoi(parts[0]);
auto sink_inode = vtr::atoi(parts[1]);
auto switch_id = vtr::atoi(parts[2]);
auto ret = routing_edges.insert(std::make_tuple(src_inode, sink_inode, switch_id));
CHECK(ret.second == true);
}
}
// Verify occupied grid LOCs
const auto & place_ctx = g_vpr_ctx.placement();
for (const auto& loc: place_ctx.block_locs) {
// Do not consider "IOB" tiles. They do not have fasm features
// defined in the arch.
if (loc.loc.x < 1 || loc.loc.x > 4)
continue;
if (loc.loc.y < 1 || loc.loc.y > 4)
continue;
auto iter = occupied_locs.find(std::make_tuple(loc.loc.x, loc.loc.y));
if (iter == occupied_locs.end()) {
FAIL_CHECK("X" << loc.loc.x << "Y" << loc.loc.y << " not emitted!");
}
}
// Verify routes
const auto & route_ctx = g_vpr_ctx.routing();
for(const auto &trace : route_ctx.trace) {
const t_trace *head = trace.head;
while(head != nullptr) {
const t_trace *next = head->next;
if(next != nullptr && head->iswitch != OPEN) {
const auto next_inode = next->index;
auto iter = routing_edges.find(std::make_tuple(head->index, next_inode, head->iswitch));
if (iter == routing_edges.end()) {
FAIL_CHECK("source: " << head->index << " sink: " << next_inode << " switch:" << head->iswitch);
}
}
head = next;
}
}
// Verify that all LUTs defined in the BLIF file ended up in fasm
CHECK(lut_defs.size() == 0);
CHECK(found_lut5);
CHECK(found_lut6);
CHECK(found_mux1);
CHECK(found_mux2);
CHECK(found_mux3);
vpr_free_all(arch, vpr_setup);
}
} // namespace