Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ice40/ecp5: add support for both 1364.1 and Synplify/LSE RAM/ROM attributes #1603

Merged
merged 8 commits into from Apr 10, 2020
42 changes: 35 additions & 7 deletions passes/memory/memory_bram.cc
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand All @@ -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());
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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() << "\"";

Expand Down Expand Up @@ -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;
Expand All @@ -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() << "\"";

Expand Down Expand Up @@ -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");
Expand Down
119 changes: 113 additions & 6 deletions passes/memory/memory_map.cc
Expand Up @@ -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;
Expand Down Expand Up @@ -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("\\WR_CLK");
RTLIL::Const clocks_pol = cell->parameters["\\WR_CLK_POLARITY"];
Expand Down Expand Up @@ -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())
Expand All @@ -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>
whitequark marked this conversation as resolved.
Show resolved Hide resolved
}
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;

Expand Down
62 changes: 62 additions & 0 deletions techlibs/ecp5/brams.txt
Expand Up @@ -37,16 +37,78 @@ bram $__ECP5_DP16KD
clkpol 2 3
endbram

# The syn_* attributes are described in:
# https://www.latticesemi.com/-/media/LatticeSemi/Documents/Tutorials/AK/LatticeDiamondTutorial311.ashx
attr_icase 1

match $__ECP5_PDPW16KD
# implicitly requested RAM or ROM
attribute !syn_ramstyle syn_ramstyle=auto
attribute !syn_romstyle syn_romstyle=auto
attribute !ram_block
attribute !rom_block
attribute !logic_block
min bits 2048
min efficiency 5
shuffle_enable A
make_transp
or_next_if_better
endmatch

match $__ECP5_PDPW16KD
# explicitly requested RAM
attribute syn_ramstyle=block_ram ram_block
attribute !syn_romstyle
attribute !rom_block
attribute !logic_block
min wports 1
shuffle_enable A
make_transp
or_next_if_better
endmatch

match $__ECP5_PDPW16KD
# explicitly requested ROM
attribute syn_romstyle=ebr rom_block
attribute !syn_ramstyle
attribute !ram_block
attribute !logic_block
max wports 0
shuffle_enable A
make_transp
or_next_if_better
endmatch

match $__ECP5_DP16KD
# implicitly requested RAM or ROM
attribute !syn_ramstyle syn_ramstyle=auto
attribute !syn_romstyle syn_romstyle=auto
attribute !ram_block
attribute !rom_block
attribute !logic_block
min bits 2048
min efficiency 5
shuffle_enable A
or_next_if_better
endmatch

match $__ECP5_DP16KD
# explicitly requested RAM
attribute syn_ramstyle=block_ram ram_block
attribute !syn_romstyle
attribute !rom_block
attribute !logic_block
min wports 1
shuffle_enable A
or_next_if_better
endmatch

match $__ECP5_DP16KD
# explicitly requested ROM
attribute syn_romstyle=ebr rom_block
attribute !syn_ramstyle
attribute !ram_block
attribute !logic_block
max wports 0
shuffle_enable A
endmatch
9 changes: 9 additions & 0 deletions techlibs/ecp5/lutrams.txt
Expand Up @@ -11,7 +11,16 @@ bram $__TRELLIS_DPR16X4
clkpol 0 2
endbram

# The syn_* attributes are described in:
# https://www.latticesemi.com/-/media/LatticeSemi/Documents/Tutorials/AK/LatticeDiamondTutorial311.ashx
attr_icase 1

match $__TRELLIS_DPR16X4
attribute !syn_ramstyle syn_ramstyle=auto syn_ramstyle=distributed
attribute !syn_romstyle syn_romstyle=auto
attribute !ram_block
attribute !rom_block
attribute !logic_block
make_outreg
min wports 1
endmatch
4 changes: 3 additions & 1 deletion techlibs/ecp5/synth_ecp5.cc
Expand Up @@ -279,7 +279,9 @@ struct SynthEcp5Pass : public ScriptPass
if (check_label("map_ffram"))
{
run("opt -fast -mux_undef -undriven -fine");
run("memory_map");
run("memory_map -iattr -attr !ram_block -attr !rom_block -attr logic_block "
"-attr syn_ramstyle=auto -attr syn_ramstyle=registers "
"-attr syn_romstyle=auto -attr syn_romstyle=logic");
run("opt -undriven -fine");
}

Expand Down