DSP register inference rules for Nexus plus inference plugin enhancements Signed-off-by: Maciej Kurc <mkurc@antmicro.com>
diff --git a/dsp_ff-plugin/dsp_ff.cc b/dsp_ff-plugin/dsp_ff.cc index 6daf965..11555f8 100644 --- a/dsp_ff-plugin/dsp_ff.cc +++ b/dsp_ff-plugin/dsp_ff.cc
@@ -84,6 +84,9 @@ struct DspPortType { RTLIL::IdString name; + /// Range of port pins that have FFs (low to high, inclusive) + std::pair<int, int> bits; + /// A dict of associated cell ports indexed by their function (like "clk, "rst") /// along with the default value to connect when unused. dict<RTLIL::IdString, std::pair<RTLIL::IdString, RTLIL::Const>> assoc; @@ -192,6 +195,33 @@ return vec; }; + // Parses port name as "<name>[<hi>:<lo>]" or just "<name>" + auto parsePortName = [&](const std::string& str) { + const std::regex expr ("^(.*)\\[([0-9]+):([0-9]+)\\]"); + std::smatch match; + + std::tuple<std::string, int, int> data; + auto res = std::regex_match(str, match, expr); + if (res) { + data = std::make_tuple( + std::string(match[1]), + std::stoi(match[2]), + std::stoi(match[3]) + ); + + if ((std::get<2>(data) > std::get<1>(data)) || + std::get<2>(data) < 0 || std::get<1>(data) < 0) + { + log_error(" invalid port spec: '%s'\n", str.c_str()); + } + } + else { + data = std::make_tuple(str, -1, -1); + } + + return data; + }; + std::ifstream file (a_FileName); std::string line; @@ -203,8 +233,10 @@ std::vector<DspType> dspTypes; std::vector<FlopType> flopTypes; + std::vector<RTLIL::IdString> dspAliases; + std::vector<std::string> tok; - + // Parse the file while (1) { @@ -229,7 +261,7 @@ // DSP section if (fields[0] == "dsp") { - if (fields.size() != 2) { + if (fields.size() < 2) { log_error(" syntax error: '%s'\n", line.c_str()); } if (!tok.empty()) { @@ -239,6 +271,11 @@ dspTypes.resize(dspTypes.size() + 1);\ dspTypes.back().name = RTLIL::escape_id(fields[1]); + + dspAliases.clear(); + for (size_t i=2; i<fields.size(); ++i) { + dspAliases.push_back(RTLIL::escape_id(fields[i])); + } } else if (fields[0] == "enddsp") { if (fields.size() != 1) { @@ -247,7 +284,13 @@ if (tok.size() != 1 || tok.back() != "dsp") { log_error(" unexpected keyword '%s'\n", fields[0].c_str()); } - tok.pop_back(); + tok.pop_back(); + + const auto dspType = dspTypes.back(); + for (const auto& alias : dspAliases) { + dspTypes.push_back(dspType); + dspTypes.back().name = alias; + } } // DSP port section @@ -260,12 +303,18 @@ } tok.push_back(fields[0]); + auto spec = parsePortName(fields[1]); + auto& ports = dspTypes.back().ports; ports.resize(ports.size() + 1); - ports.back().name = RTLIL::escape_id(fields[1]); - ports.back().assoc.insert(std::make_pair(RTLIL::escape_id("clk"), std::make_pair(RTLIL::IdString(), RTLIL::Sx))); - ports.back().assoc.insert(std::make_pair(RTLIL::escape_id("rst"), std::make_pair(RTLIL::IdString(), RTLIL::Sx))); - ports.back().assoc.insert(std::make_pair(RTLIL::escape_id("ena"), std::make_pair(RTLIL::IdString(), RTLIL::Sx))); + ports.back().name = RTLIL::escape_id(std::get<0>(spec)); + ports.back().bits = std::make_pair(std::get<2>(spec), std::get<1>(spec)); + ports.back().assoc.insert(std::make_pair(RTLIL::escape_id("clk"), + std::make_pair(RTLIL::IdString(), RTLIL::Sx))); + ports.back().assoc.insert(std::make_pair(RTLIL::escape_id("rst"), + std::make_pair(RTLIL::IdString(), RTLIL::Sx))); + ports.back().assoc.insert(std::make_pair(RTLIL::escape_id("ena"), + std::make_pair(RTLIL::IdString(), RTLIL::Sx))); } else if (fields[0] == "endport") { if (fields.size() != 1) { @@ -289,11 +338,16 @@ flopTypes.resize(flopTypes.size() + 1); flopTypes.back().name = RTLIL::escape_id(fields[1]); - flopTypes.back().ports.insert(std::make_pair(RTLIL::escape_id("clk"), RTLIL::IdString())); - flopTypes.back().ports.insert(std::make_pair(RTLIL::escape_id("rst"), RTLIL::IdString())); - flopTypes.back().ports.insert(std::make_pair(RTLIL::escape_id("ena"), RTLIL::IdString())); - flopTypes.back().ports.insert(std::make_pair(RTLIL::escape_id("d"), RTLIL::IdString())); - flopTypes.back().ports.insert(std::make_pair(RTLIL::escape_id("q"), RTLIL::IdString())); + flopTypes.back().ports.insert(std::make_pair( + RTLIL::escape_id("clk"), RTLIL::IdString())); + flopTypes.back().ports.insert(std::make_pair( + RTLIL::escape_id("rst"), RTLIL::IdString())); + flopTypes.back().ports.insert(std::make_pair( + RTLIL::escape_id("ena"), RTLIL::IdString())); + flopTypes.back().ports.insert(std::make_pair( + RTLIL::escape_id("d"), RTLIL::IdString())); + flopTypes.back().ports.insert(std::make_pair( + RTLIL::escape_id("q"), RTLIL::IdString())); } else if (fields[0] == "endff") { if (fields.size() != 1) { @@ -397,6 +451,19 @@ flopTypes.back().ports[RTLIL::escape_id("q")] = RTLIL::escape_id(fields[1]); } + // Parameters that has to match for a flip-flop + else if (fields[0] == "match") { + if (fields.size() < 2) { + log_error(" syntax error: '%s'\n", line.c_str()); + } + if (tok.size() == 0 || tok.back() != "ff") { + log_error(" unexpected keyword '%s'\n", fields[0].c_str()); + } + + for (size_t i=1; i<fields.size(); ++i) { + flopTypes.back().params.matching.push_back(RTLIL::escape_id(fields[i])); + } + } // Parameters to set else if (fields[0] == "set") { if (fields.size() < 2) { @@ -469,15 +536,21 @@ } else { - log(" unexpected keyword '%s'\n", fields[0].c_str()); + log_error(" unexpected keyword '%s'\n", fields[0].c_str()); } } // Convert lists to maps for (const auto& it : dspTypes) { + if (m_DspTypes.count(it.name)) { + log_error(" duplicated rule for DSP '%s'\n", it.name.c_str()); + } m_DspTypes.insert(std::make_pair(it.name, it)); } for (const auto& it : flopTypes) { + if (m_FlopTypes.count(it.name)) { + log_error(" duplicated rule for flip-flop '%s'\n", it.name.c_str()); + } m_FlopTypes.insert(std::make_pair(it.name, it)); } } @@ -492,7 +565,14 @@ log(" ports:\n"); for (const auto& port : dsp.ports) { - log(" %s.%s\n", dsp.name.c_str(), port.name.c_str()); + + std::string range; + if (port.bits.first != -1 && port.bits.second != -1) { + range = stringf("[%d:%d]", port.bits.second, port.bits.first); + } + + log(" %s.%s%s\n", dsp.name.c_str(), port.name.c_str(), range.c_str()); + for (const auto& it : port.assoc) { log(" %.3s: %s\n", it.first.c_str(), !it.second.first.empty() ? it.second.first.c_str() : "<none>"); } @@ -529,6 +609,12 @@ } if (!ff.params.set.empty()) { + log(" params that must match:\n"); + for (const auto& it : ff.params.matching) { + log(" %s\n", it.c_str()); + } + } + if (!ff.params.set.empty()) { log(" set params:\n"); for (const auto& it : ff.params.set) { log(" %s=%s\n", it.first.c_str(), it.second.decode_string().c_str()); @@ -656,7 +742,7 @@ bool checkDspPort (RTLIL::Cell* a_Cell, const DspPortType& a_PortRule) { bool isOk = true; - // The cell parameters must not be set + // The cell register control parameters must not be set for (const auto& it : a_PortRule.params.set) { const auto curr = a_Cell->getParam(it.first); if (curr == it.second) { @@ -790,6 +876,7 @@ return isOk; } + /// Returns a string with either wire name or constant value for a SigBit static std::string sigBitName (const RTLIL::SigBit& a_SigBit) { if (a_SigBit.is_wire()) { RTLIL::Wire* w = a_SigBit.wire; @@ -816,6 +903,7 @@ a_Cell->type.c_str(), a_PortRule.name.c_str(), a_Cell->name.c_str()); // Check if the port can be used for FF integration + log_assert(a_Cell->output(a_PortRule.name) || a_Cell->input(a_PortRule.name)); if (!checkDspPort(a_Cell, a_PortRule)) { log_debug(" port check failed\n"); return; @@ -837,7 +925,12 @@ continue; } - log_assert(a_Cell->output(a_PortRule.name) || a_Cell->input(a_PortRule.name)); + // Skip bits out of the specified range + if ((a_PortRule.bits.first >= 0 && (int)i < a_PortRule.bits.first) || + (a_PortRule.bits.second >= 0 && (int)i > a_PortRule.bits.second)) + { + continue; + } pool<CellPin> others; @@ -952,6 +1045,11 @@ flops[i].second, flops[i].first->type.c_str(), flops[i].first->name.c_str()); } + else if ((a_PortRule.bits.first >= 0 && (int)i < a_PortRule.bits.first) || + (a_PortRule.bits.second >= 0 && (int)i > a_PortRule.bits.second)) + { + log_debug(" %2zu. (excluded)\n", i); + } else { log_debug(" %2zu. None\n", i); } @@ -1252,7 +1350,7 @@ } } - // No driver found + // No driver found. FIXME: Implement a cleaner way of indicating that return CellPin(nullptr, RTLIL::IdString(), -1); }
diff --git a/dsp_ff-plugin/nexus-dsp_rules.txt b/dsp_ff-plugin/nexus-dsp_rules.txt index 642f847..fa11c3f 100644 --- a/dsp_ff-plugin/nexus-dsp_rules.txt +++ b/dsp_ff-plugin/nexus-dsp_rules.txt
@@ -1,4 +1,4 @@ -dsp MULT36X36 # 36x36 mode +dsp MULT9X9 MULT18X18 MULT18X36 MULT36X36 port A clk CLK 0 rst RSTA 0 @@ -25,6 +25,127 @@ endport enddsp +dsp MULTPREADD9X9 MULTPREADD18X18 MULTADDSUB18X18 MULTADDSUB36X36 + port A + clk CLK 0 + rst RSTA 0 + ena CEA 1 + + set REGINPUTA=REGISTER + map GSR=GSR + endport + port B + clk CLK 0 + rst RSTB 0 + ena CEB 1 + + set REGINPUTB=REGISTER + map GSR=GSR + endport + port C + clk CLK 0 + rst RSTC 0 + ena CEC 1 + + set REGINPUTC=REGISTER + map GSR=GSR + endport + port Z + clk CLK 0 + rst RSTOUT 0 + ena CEOUT 1 + + set REGOUTPUT=REGISTER + map GSR=GSR + endport +enddsp + +# TODO: Uncomment when support for multiple port registers controlled by +# a common parameter is added: + +#dsp MULTADDSUB9X9WIDE +# port A0 +# clk CLK 0 +# rst RSTA0A1 0 +# ena CEA0A1 1 +# +# set REGINPUTAB0=REGISTER +# map GSR=GSR +# endport +# port A1 +# clk CLK 0 +# rst RSTA0A1 0 +# ena CEA0A1 1 +# +# set REGINPUTAB1=REGISTER +# map GSR=GSR +# endport +# port A2 +# clk CLK 0 +# rst RSTA2A3 0 +# ena CEA2A3 1 +# +# set REGINPUTAB2=REGISTER +# map GSR=GSR +# endport +# port A3 +# clk CLK 0 +# rst RSTA2A3 0 +# ena CEA2A3 1 +# +# set REGINPUTAB3=REGISTER +# map GSR=GSR +# endport +# port B0 +# clk CLK 0 +# rst RSTB0B1 0 +# ena CEB0B1 1 +# +# set REGINPUTAB0=REGISTER +# map GSR=GSR +# endport +# port B1 +# clk CLK 0 +# rst RSTB0B1 0 +# ena CEB0B1 1 +# +# set REGINPUTAB1=REGISTER +# map GSR=GSR +# endport +# port B2 +# clk CLK 0 +# rst RSTB2B3 0 +# ena CEB2B3 1 +# +# set REGINPUTAB2=REGISTER +# map GSR=GSR +# endport +# port B3 +# clk CLK 0 +# rst RSTB2B3 0 +# ena CEB2B3 1 +# +# set REGINPUTAB3=REGISTER +# map GSR=GSR +# endport +# port C +# clk CLK 0 +# rst RSTC0 +# ena CEC 1 +# +# set REGINPUTC=REGISTER +# map GSR=GSR +# endport +# port Z +# clk CLK 0 +# rst RSTOUT 0 +# ena CEOUT 1 +# +# set REGOUTPUT=REGISTER +# map GSR=GSR +# endport +#enddsp + ff FD1P3DX clk CK rst CD