3
0
Fork 0
mirror of https://github.com/YosysHQ/yosys synced 2025-04-23 09:05:32 +00:00

Merge pull request #1603 from whitequark/ice40-ram_style

ice40/ecp5: add support for both 1364.1 and Synplify/LSE RAM/ROM attributes
This commit is contained in:
whitequark 2020-04-10 14:51:01 +00:00 committed by GitHub
commit 93ef516d91
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 837 additions and 51 deletions

View file

@ -137,9 +137,26 @@ struct rules_t
vector<vector<std::tuple<bool,IdString,Const>>> attributes;
};
bool attr_icase;
dict<IdString, vector<bram_t>> brams;
vector<match_t> matches;
std::string map_case(std::string value) const
{
if (attr_icase) {
for (char &c : value)
c = tolower(c);
}
return value;
}
RTLIL::Const map_case(RTLIL::Const value) const
{
if (value.flags & RTLIL::CONST_FLAG_STRING)
return map_case(value.decode_string());
return value;
}
std::ifstream infile;
vector<string> tokens;
vector<string> labels;
@ -337,7 +354,7 @@ struct rules_t
IdString key = RTLIL::escape_id(tokens[idx].substr(c1, c2));
Const val = c2 != std::string::npos ? tokens[idx].substr(c2+1) : RTLIL::Const(1);
data.attributes.back().emplace_back(exists, key, val);
data.attributes.back().emplace_back(exists, key, map_case(val));
}
continue;
}
@ -351,6 +368,7 @@ struct rules_t
rewrite_filename(filename);
infile.open(filename);
linecount = 0;
attr_icase = false;
if (infile.fail())
log_error("Can't open rules file `%s'.\n", filename.c_str());
@ -360,6 +378,11 @@ struct rules_t
if (!labels.empty())
syntax_error();
if (GetSize(tokens) == 2 && tokens[0] == "attr_icase") {
attr_icase = atoi(tokens[1].c_str());
continue;
}
if (tokens[0] == "bram") {
parse_bram();
continue;
@ -843,7 +866,7 @@ grow_read_ports:;
}
else if (!exists)
continue;
if (it->second != value)
if (rules.map_case(it->second) != value)
continue;
found = true;
break;
@ -855,7 +878,7 @@ grow_read_ports:;
ss << "!";
IdString key = std::get<1>(sums.front());
ss << log_id(key);
const Const &value = std::get<2>(sums.front());
const Const &value = rules.map_case(std::get<2>(sums.front()));
if (exists && value != Const(1))
ss << "=\"" << value.decode_string() << "\"";
@ -1167,7 +1190,7 @@ void handle_cell(Cell *cell, const rules_t &rules)
}
else if (!exists)
continue;
if (it->second != value)
if (rules.map_case(it->second) != value)
continue;
found = true;
break;
@ -1179,7 +1202,7 @@ void handle_cell(Cell *cell, const rules_t &rules)
ss << "!";
IdString key = std::get<1>(sums.front());
ss << log_id(key);
const Const &value = std::get<2>(sums.front());
const Const &value = rules.map_case(std::get<2>(sums.front()));
if (exists && value != Const(1))
ss << "=\"" << value.decode_string() << "\"";
@ -1252,8 +1275,13 @@ struct MemoryBramPass : public Pass {
log("The given rules file describes the available resources and how they should be\n");
log("used.\n");
log("\n");
log("The rules file contains a set of block ram description and a sequence of match\n");
log("rules. A block ram description looks like this:\n");
log("The rules file contains configuration options, a set of block ram description\n");
log("and a sequence of match rules.\n");
log("\n");
log("The option 'attr_icase' configures how attribute values are matched. The value 0\n");
log("means case-sensitive, 1 means case-insensitive.\n");
log("\n");
log("A block ram description looks like this:\n");
log("\n");
log(" bram RAMB1024X32 # name of BRAM cell\n");
log(" init 1 # set to '1' if BRAM can be initialized\n");

View file

@ -28,11 +28,32 @@ PRIVATE_NAMESPACE_BEGIN
struct MemoryMapWorker
{
bool attr_icase = false;
dict<RTLIL::IdString, std::vector<RTLIL::Const>> attributes;
RTLIL::Design *design;
RTLIL::Module *module;
std::map<std::pair<RTLIL::SigSpec, RTLIL::SigSpec>, RTLIL::SigBit> decoder_cache;
MemoryMapWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module) {}
std::string map_case(std::string value) const
{
if (attr_icase) {
for (char &c : value)
c = tolower(c);
}
return value;
}
RTLIL::Const map_case(RTLIL::Const value) const
{
if (value.flags & RTLIL::CONST_FLAG_STRING)
return map_case(value.decode_string());
return value;
}
std::string genid(RTLIL::IdString name, std::string token1 = "", int i = -1, std::string token2 = "", int j = -1, std::string token3 = "", int k = -1, std::string token4 = "")
{
std::stringstream sstr;
@ -98,6 +119,36 @@ struct MemoryMapWorker
return;
}
// check if attributes allow us to infer FFRAM for this cell
for (const auto &attr : attributes) {
if (cell->attributes.count(attr.first)) {
const auto &cell_attr = cell->attributes[attr.first];
if (attr.second.empty()) {
log("Not mapping memory cell %s in module %s (attribute %s is set).\n",
cell->name.c_str(), module->name.c_str(), attr.first.c_str());
return;
}
bool found = false;
for (auto &value : attr.second) {
if (map_case(cell_attr) == map_case(value)) {
found = true;
break;
}
}
if (!found) {
if (cell_attr.flags & RTLIL::CONST_FLAG_STRING) {
log("Not mapping memory cell %s in module %s (attribute %s is set to \"%s\").\n",
cell->name.c_str(), module->name.c_str(), attr.first.c_str(), cell_attr.decode_string().c_str());
} else {
log("Not mapping memory cell %s in module %s (attribute %s is set to %d).\n",
cell->name.c_str(), module->name.c_str(), attr.first.c_str(), cell_attr.as_int());
}
return;
}
}
}
// all write ports must share the same clock
RTLIL::SigSpec clocks = cell->getPort(ID::WR_CLK);
RTLIL::Const clocks_pol = cell->parameters[ID::WR_CLK_POLARITY];
@ -339,7 +390,7 @@ struct MemoryMapWorker
module->remove(cell);
}
MemoryMapWorker(RTLIL::Design *design, RTLIL::Module *module) : design(design), module(module)
void run()
{
std::vector<RTLIL::Cell*> cells;
for (auto cell : module->selected_cells())
@ -356,17 +407,73 @@ struct MemoryMapPass : public Pass {
{
// |---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|---v---|
log("\n");
log(" memory_map [selection]\n");
log(" memory_map [options] [selection]\n");
log("\n");
log("This pass converts multiport memory cells as generated by the memory_collect\n");
log("pass to word-wide DFFs and address decoders.\n");
log("\n");
log(" -attr !<name>\n");
log(" do not map memories that have attribute <name> set.\n");
log("\n");
log(" -attr <name>[=<value>]\n");
log(" for memories that have attribute <name> set, only map them if its value\n");
log(" is a string <value> (if specified), or an integer 1 (otherwise). if this\n");
log(" option is specified multiple times, map the memory if the attribute is\n");
log(" to any of the values.\n");
log("\n");
log(" -iattr\n");
log(" for -attr, ignore case of <value>.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE {
void execute(std::vector<std::string> args, RTLIL::Design *design) YS_OVERRIDE
{
bool attr_icase = false;
dict<RTLIL::IdString, std::vector<RTLIL::Const>> attributes;
log_header(design, "Executing MEMORY_MAP pass (converting $mem cells to logic and flip-flops).\n");
extra_args(args, 1, design);
for (auto mod : design->selected_modules())
MemoryMapWorker(design, mod);
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
{
if (args[argidx] == "-attr" && argidx + 1 < args.size())
{
std::string attr_arg = args[++argidx];
std::string name;
RTLIL::Const value;
size_t eq_at = attr_arg.find('=');
if (eq_at != std::string::npos) {
name = attr_arg.substr(0, eq_at);
value = attr_arg.substr(eq_at + 1);
} else {
name = attr_arg;
value = RTLIL::Const(1);
}
if (attr_arg.size() > 1 && attr_arg[0] == '!') {
if (value != RTLIL::Const(1)) {
--argidx;
break; // we don't support -attr !<name>=<value>
}
attributes[RTLIL::escape_id(name.substr(1))].clear();
} else {
attributes[RTLIL::escape_id(name)].push_back(value);
}
continue;
}
if (args[argidx] == "-iattr")
{
attr_icase = true;
continue;
}
break;
}
extra_args(args, argidx, design);
for (auto mod : design->selected_modules()) {
MemoryMapWorker worker(design, mod);
worker.attr_icase = attr_icase;
worker.attributes = attributes;
worker.run();
}
}
} MemoryMapPass;