dfflibmap: Refactor to use dfflegalize internally.

This commit is contained in:
Marcelina Kościelnicka 2020-07-02 18:22:43 +02:00
parent 68babb2ae4
commit 7ed9d18907
5 changed files with 212 additions and 210 deletions

View File

@ -115,7 +115,7 @@ static bool parse_pin(LibertyAst *cell, LibertyAst *attr, std::string &pin_name,
return false;
}
static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval, bool prepare_mode)
static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has_reset, bool rstpol, bool rstval)
{
LibertyAst *best_cell = nullptr;
std::map<std::string, char> best_cell_ports;
@ -222,21 +222,12 @@ static void find_cell(LibertyAst *ast, IdString cell_type, bool clkpol, bool has
if (best_cell != nullptr) {
log(" cell %s (%sinv, pins=%d, area=%.2f) is a direct match for cell type %s.\n",
best_cell->args[0].c_str(), best_cell_noninv ? "non" : "", best_cell_pins, best_cell_area, cell_type.c_str());
if (prepare_mode) {
cell_mappings[cell_type].cell_name = cell_type;
cell_mappings[cell_type].ports["C"] = 'C';
if (has_reset)
cell_mappings[cell_type].ports["R"] = 'R';
cell_mappings[cell_type].ports["D"] = 'D';
cell_mappings[cell_type].ports["Q"] = 'Q';
} else {
cell_mappings[cell_type].cell_name = RTLIL::escape_id(best_cell->args[0]);
cell_mappings[cell_type].ports = best_cell_ports;
}
cell_mappings[cell_type].cell_name = RTLIL::escape_id(best_cell->args[0]);
cell_mappings[cell_type].ports = best_cell_ports;
}
}
static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool setpol, bool clrpol, bool prepare_mode)
static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool setpol, bool clrpol)
{
LibertyAst *best_cell = nullptr;
std::map<std::string, char> best_cell_ports;
@ -339,141 +330,12 @@ static void find_cell_sr(LibertyAst *ast, IdString cell_type, bool clkpol, bool
if (best_cell != nullptr) {
log(" cell %s (%sinv, pins=%d, area=%.2f) is a direct match for cell type %s.\n",
best_cell->args[0].c_str(), best_cell_noninv ? "non" : "", best_cell_pins, best_cell_area, cell_type.c_str());
if (prepare_mode) {
cell_mappings[cell_type].cell_name = cell_type;
cell_mappings[cell_type].ports["C"] = 'C';
cell_mappings[cell_type].ports["S"] = 'S';
cell_mappings[cell_type].ports["R"] = 'R';
cell_mappings[cell_type].ports["D"] = 'D';
cell_mappings[cell_type].ports["Q"] = 'Q';
} else {
cell_mappings[cell_type].cell_name = RTLIL::escape_id(best_cell->args[0]);
cell_mappings[cell_type].ports = best_cell_ports;
}
cell_mappings[cell_type].cell_name = RTLIL::escape_id(best_cell->args[0]);
cell_mappings[cell_type].ports = best_cell_ports;
}
}
static bool expand_cellmap_worker(std::string from, std::string to, std::string inv)
{
if (cell_mappings.count(to) > 0)
return false;
log(" create mapping for %s from mapping for %s.\n", to.c_str(), from.c_str());
cell_mappings[to].cell_name = cell_mappings[from].cell_name;
cell_mappings[to].ports = cell_mappings[from].ports;
for (auto &it : cell_mappings[to].ports) {
char cmp_ch = it.second;
if ('a' <= cmp_ch && cmp_ch <= 'z')
cmp_ch -= 'a' - 'A';
if (inv.find(cmp_ch) == std::string::npos)
continue;
if ('a' <= it.second && it.second <= 'z')
it.second -= 'a' - 'A';
else if ('A' <= it.second && it.second <= 'Z')
it.second += 'a' - 'A';
}
return true;
}
static bool expand_cellmap(std::string pattern, std::string inv)
{
std::vector<std::pair<std::string, std::string>> from_to_list;
bool return_status = false;
for (auto &it : cell_mappings) {
std::string from = it.first.str(), to = it.first.str();
if (from.size() != pattern.size())
continue;
for (size_t i = 0; i < from.size(); i++) {
if (pattern[i] == '*') {
to[i] = from[i] == 'P' ? 'N' :
from[i] == 'N' ? 'P' :
from[i] == '1' ? '0' :
from[i] == '0' ? '1' : '*';
} else
if (pattern[i] != '?' && pattern[i] != from[i])
goto pattern_failed;
}
from_to_list.push_back(std::pair<std::string, std::string>(from, to));
pattern_failed:;
}
for (auto &it : from_to_list)
return_status = return_status || expand_cellmap_worker(it.first, it.second, inv);
return return_status;
}
static void map_sr_to_arst(IdString from, IdString to)
{
if (!cell_mappings.count(from) || cell_mappings.count(to) > 0)
return;
char from_clk_pol = from[8];
char from_set_pol = from[9];
char from_clr_pol = from[10];
char to_clk_pol = to[6];
char to_rst_pol = to[7];
char to_rst_val = to[8];
log_assert(from_clk_pol == to_clk_pol);
log_assert(to_rst_pol == from_set_pol && to_rst_pol == from_clr_pol);
log(" create mapping for %s from mapping for %s.\n", to.c_str(), from.c_str());
cell_mappings[to].cell_name = cell_mappings[from].cell_name;
cell_mappings[to].ports = cell_mappings[from].ports;
for (auto &it : cell_mappings[to].ports)
{
bool is_set_pin = it.second == 'S' || it.second == 's';
bool is_clr_pin = it.second == 'R' || it.second == 'r';
if (!is_set_pin && !is_clr_pin)
continue;
if ((to_rst_val == '0' && is_set_pin) || (to_rst_val == '1' && is_clr_pin))
{
// this is the unused set/clr pin -- deactivate it
if (is_set_pin)
it.second = (from_set_pol == 'P') == (it.second == 'S') ? '0' : '1';
else
it.second = (from_clr_pol == 'P') == (it.second == 'R') ? '0' : '1';
}
else
{
// this is the used set/clr pin -- rename it to 'reset'
if (it.second == 'S')
it.second = 'R';
if (it.second == 's')
it.second = 'r';
}
}
}
static void map_adff_to_dff(IdString from, IdString to)
{
if (!cell_mappings.count(from) || cell_mappings.count(to) > 0)
return;
char from_clk_pol = from[6];
char from_rst_pol = from[7];
char to_clk_pol = to[6];
log_assert(from_clk_pol == to_clk_pol);
log(" create mapping for %s from mapping for %s.\n", to.c_str(), from.c_str());
cell_mappings[to].cell_name = cell_mappings[from].cell_name;
cell_mappings[to].ports = cell_mappings[from].ports;
for (auto &it : cell_mappings[to].ports) {
if (it.second == 'S' || it.second == 'R')
it.second = from_rst_pol == 'P' ? '0' : '1';
if (it.second == 's' || it.second == 'r')
it.second = from_rst_pol == 'P' ? '1' : '0';
}
}
static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare_mode)
static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module)
{
log("Mapping DFF cells in module `%s':\n", module->name.c_str());
@ -499,7 +361,7 @@ static void dfflibmap(RTLIL::Design *design, RTLIL::Module *module, bool prepare
module->remove(cell);
cell_mapping &cm = cell_mappings[cell_type];
RTLIL::Cell *new_cell = module->addCell(cell_name, prepare_mode ? cm.cell_name : cm.cell_name);
RTLIL::Cell *new_cell = module->addCell(cell_name, cm.cell_name);
new_cell->set_src_attribute(src);
@ -552,7 +414,7 @@ struct DfflibmapPass : public Pass {
void help() override
{
log("\n");
log(" dfflibmap [-prepare] -liberty <file> [selection]\n");
log(" dfflibmap [-prepare] [-map-only] [-info] -liberty <file> [selection]\n");
log("\n");
log("Map internal flip-flop cells to the flip-flop cells in the technology\n");
log("library specified in the given liberty file.\n");
@ -562,15 +424,27 @@ struct DfflibmapPass : public Pass {
log("\n");
log("When called with -prepare, this command will convert the internal FF cells\n");
log("to the internal cell types that best match the cells found in the given\n");
log("liberty file.\n");
log("liberty file, but won't actually map them to the target cells.\n");
log("\n");
log("When called with -map-only, this command will only map internal cell\n");
log("types that are already of exactly the right type to match the target\n");
log("cells, leaving remaining internal cells untouched.\n");
log("\n");
log("When called with -info, this command will only print the target cell\n");
log("list, along with their associated internal cell types, and the arguments");
log("that would be passed to the dfflegalize pass. The design will not be\n");
log("changed.\n");
log("\n");
}
void execute(std::vector<std::string> args, RTLIL::Design *design) override
{
log_header(design, "Executing DFFLIBMAP pass (mapping DFF cells to sequential cells from liberty file).\n");
log_push();
std::string liberty_file;
bool prepare_mode = false;
bool map_only_mode = false;
bool info_mode = false;
size_t argidx;
for (argidx = 1; argidx < args.size(); argidx++)
@ -585,10 +459,28 @@ struct DfflibmapPass : public Pass {
prepare_mode = true;
continue;
}
if (arg == "-map-only") {
map_only_mode = true;
continue;
}
if (arg == "-info") {
info_mode = true;
continue;
}
break;
}
extra_args(args, argidx, design);
int modes = 0;
if (prepare_mode)
modes++;
if (map_only_mode)
modes++;
if (info_mode)
modes++;
if (modes > 1)
log_cmd_error("Only one of -prepare, -map-only, or -info options should be given!\n");
if (liberty_file.empty())
log_cmd_error("Missing `-liberty liberty_file' option!\n");
@ -599,74 +491,49 @@ struct DfflibmapPass : public Pass {
LibertyParser libparser(f);
f.close();
find_cell(libparser.ast, ID($_DFF_N_), false, false, false, false, prepare_mode);
find_cell(libparser.ast, ID($_DFF_P_), true, false, false, false, prepare_mode);
find_cell(libparser.ast, ID($_DFF_N_), false, false, false, false);
find_cell(libparser.ast, ID($_DFF_P_), true, false, false, false);
find_cell(libparser.ast, ID($_DFF_NN0_), false, true, false, false, prepare_mode);
find_cell(libparser.ast, ID($_DFF_NN1_), false, true, false, true, prepare_mode);
find_cell(libparser.ast, ID($_DFF_NP0_), false, true, true, false, prepare_mode);
find_cell(libparser.ast, ID($_DFF_NP1_), false, true, true, true, prepare_mode);
find_cell(libparser.ast, ID($_DFF_PN0_), true, true, false, false, prepare_mode);
find_cell(libparser.ast, ID($_DFF_PN1_), true, true, false, true, prepare_mode);
find_cell(libparser.ast, ID($_DFF_PP0_), true, true, true, false, prepare_mode);
find_cell(libparser.ast, ID($_DFF_PP1_), true, true, true, true, prepare_mode);
find_cell(libparser.ast, ID($_DFF_NN0_), false, true, false, false);
find_cell(libparser.ast, ID($_DFF_NN1_), false, true, false, true);
find_cell(libparser.ast, ID($_DFF_NP0_), false, true, true, false);
find_cell(libparser.ast, ID($_DFF_NP1_), false, true, true, true);
find_cell(libparser.ast, ID($_DFF_PN0_), true, true, false, false);
find_cell(libparser.ast, ID($_DFF_PN1_), true, true, false, true);
find_cell(libparser.ast, ID($_DFF_PP0_), true, true, true, false);
find_cell(libparser.ast, ID($_DFF_PP1_), true, true, true, true);
find_cell_sr(libparser.ast, ID($_DFFSR_NNN_), false, false, false, prepare_mode);
find_cell_sr(libparser.ast, ID($_DFFSR_NNP_), false, false, true, prepare_mode);
find_cell_sr(libparser.ast, ID($_DFFSR_NPN_), false, true, false, prepare_mode);
find_cell_sr(libparser.ast, ID($_DFFSR_NPP_), false, true, true, prepare_mode);
find_cell_sr(libparser.ast, ID($_DFFSR_PNN_), true, false, false, prepare_mode);
find_cell_sr(libparser.ast, ID($_DFFSR_PNP_), true, false, true, prepare_mode);
find_cell_sr(libparser.ast, ID($_DFFSR_PPN_), true, true, false, prepare_mode);
find_cell_sr(libparser.ast, ID($_DFFSR_PPP_), true, true, true, prepare_mode);
// try to implement as many cells as possible just by inverting
// the SET and RESET pins. If necessary, implement cell types
// by inverting both D and Q. Only invert clock pins if there
// is no other way of implementing the cell.
while (1)
{
if (expand_cellmap("$_DFF_?*?_", "R") ||
expand_cellmap("$_DFFSR_?*?_", "S") ||
expand_cellmap("$_DFFSR_??*_", "R"))
continue;
if (expand_cellmap("$_DFF_??*_", "DQ"))
continue;
if (expand_cellmap("$_DFF_*_", "C") ||
expand_cellmap("$_DFF_*??_", "C") ||
expand_cellmap("$_DFFSR_*??_", "C"))
continue;
break;
}
map_sr_to_arst(ID($_DFFSR_NNN_), ID($_DFF_NN0_));
map_sr_to_arst(ID($_DFFSR_NNN_), ID($_DFF_NN1_));
map_sr_to_arst(ID($_DFFSR_NPP_), ID($_DFF_NP0_));
map_sr_to_arst(ID($_DFFSR_NPP_), ID($_DFF_NP1_));
map_sr_to_arst(ID($_DFFSR_PNN_), ID($_DFF_PN0_));
map_sr_to_arst(ID($_DFFSR_PNN_), ID($_DFF_PN1_));
map_sr_to_arst(ID($_DFFSR_PPP_), ID($_DFF_PP0_));
map_sr_to_arst(ID($_DFFSR_PPP_), ID($_DFF_PP1_));
map_adff_to_dff(ID($_DFF_NN0_), ID($_DFF_N_));
map_adff_to_dff(ID($_DFF_NN1_), ID($_DFF_N_));
map_adff_to_dff(ID($_DFF_NP0_), ID($_DFF_N_));
map_adff_to_dff(ID($_DFF_NP1_), ID($_DFF_N_));
map_adff_to_dff(ID($_DFF_PN0_), ID($_DFF_P_));
map_adff_to_dff(ID($_DFF_PN1_), ID($_DFF_P_));
map_adff_to_dff(ID($_DFF_PP0_), ID($_DFF_P_));
map_adff_to_dff(ID($_DFF_PP1_), ID($_DFF_P_));
find_cell_sr(libparser.ast, ID($_DFFSR_NNN_), false, false, false);
find_cell_sr(libparser.ast, ID($_DFFSR_NNP_), false, false, true);
find_cell_sr(libparser.ast, ID($_DFFSR_NPN_), false, true, false);
find_cell_sr(libparser.ast, ID($_DFFSR_NPP_), false, true, true);
find_cell_sr(libparser.ast, ID($_DFFSR_PNN_), true, false, false);
find_cell_sr(libparser.ast, ID($_DFFSR_PNP_), true, false, true);
find_cell_sr(libparser.ast, ID($_DFFSR_PPN_), true, true, false);
find_cell_sr(libparser.ast, ID($_DFFSR_PPP_), true, true, true);
log(" final dff cell mappings:\n");
logmap_all();
for (auto module : design->selected_modules())
if (!module->get_blackbox_attribute())
dfflibmap(design, module, prepare_mode);
if (!map_only_mode) {
std::string dfflegalize_cmd = "dfflegalize";
for (auto it : cell_mappings)
dfflegalize_cmd += stringf(" -cell %s 01", it.first.c_str());
dfflegalize_cmd += " t:$_DFF* t:$_SDFF*";
if (info_mode) {
log("dfflegalize command line: %s\n", dfflegalize_cmd.c_str());
} else {
Pass::call(design, dfflegalize_cmd);
}
}
if (!prepare_mode && !info_mode) {
for (auto module : design->selected_modules())
if (!module->get_blackbox_attribute())
dfflibmap(design, module);
}
log_pop();
cell_mappings.clear();
}
} DfflibmapPass;

View File

@ -5,6 +5,6 @@ for x in *.lib; do
echo "Running $x.."
echo "read_verilog small.v" > test.ys
echo "synth -top small" >> test.ys
echo "dfflibmap -liberty ${x}" >> test.ys
echo "dfflibmap -info -liberty ${x}" >> test.ys
../../yosys -ql ${x%.lib}.log -s test.ys
done

View File

@ -0,0 +1,22 @@
module dffn(input CLK, D, output reg Q, output QN);
always @(negedge CLK)
Q <= D;
assign QN = ~Q;
endmodule
module dffsr(input CLK, D, CLEAR, PRESET, output reg Q, output QN);
always @(posedge CLK, posedge CLEAR, posedge PRESET)
if (CLEAR)
Q <= 0;
else if (PRESET)
Q <= 1;
else
Q <= D;
assign QN = ~Q;
endmodule

View File

@ -0,0 +1,55 @@
library(test) {
/* D-type flip-flop with asynchronous reset and preset */
cell (dffn) {
area : 6;
ff("IQ", "IQN") {
next_state : "D";
clocked_on : "!CLK";
}
pin(D) {
direction : input;
}
pin(CLK) {
direction : input;
}
pin(Q) {
direction: output;
function : "IQ";
}
pin(QN) {
direction: output;
function : "IQN";
}
}
cell (dffsr) {
area : 6;
ff("IQ", "IQN") {
next_state : "D";
clocked_on : "CLK";
clear : "CLEAR";
preset : "PRESET";
clear_preset_var1 : L;
clear_preset_var2 : L;
}
pin(D) {
direction : input;
}
pin(CLK) {
direction : input;
}
pin(CLEAR) {
direction : input;
}
pin(PRESET) {
direction : input;
}
pin(Q) {
direction: output;
function : "IQ";
}
pin(QN) {
direction: output;
function : "IQN";
}
}
}

View File

@ -0,0 +1,58 @@
read_verilog -icells <<EOT
module top(input C, D, S, R, output [9:0] Q);
$_DFF_P_ ff0 (.C(C), .D(D), .Q(Q[0]));
$_DFF_PP0_ ff1 (.C(C), .D(D), .R(R), .Q(Q[1]));
$_DFF_PP1_ ff2 (.C(C), .D(D), .R(R), .Q(Q[2]));
$_DFFSR_PPP_ ff3 (.C(C), .D(D), .R(R), .S(S), .Q(Q[3]));
$_DFFSR_NNN_ ff4 (.C(C), .D(D), .R(R), .S(S), .Q(Q[4]));
assign Q[9:5] = ~Q[4:0];
endmodule
EOT
simplemap
design -save orig
#equiv_opt -map dfflibmap-sim.v -assert -multiclock dfflibmap -liberty dfflibmap.lib
#equiv_opt -map dfflibmap-sim.v -assert -multiclock dfflibmap -prepare -liberty dfflibmap.lib
dfflibmap -prepare -liberty dfflibmap.lib
equiv_opt -map dfflibmap-sim.v -assert -multiclock dfflibmap -map-only -liberty dfflibmap.lib
design -load orig
dfflibmap -liberty dfflibmap.lib
clean
select -assert-count 4 t:$_NOT_
select -assert-count 1 t:dffn
select -assert-count 4 t:dffsr
select -assert-none t:dffn t:dffsr t:$_NOT_ %% %n t:* %i
design -load orig
dfflibmap -prepare -liberty dfflibmap.lib
select -assert-count 9 t:$_NOT_
select -assert-count 1 t:$_DFF_N_
select -assert-count 4 t:$_DFFSR_PPP_
select -assert-none t:$_DFF_N_ t:$_DFFSR_PPP_ t:$_NOT_ %% %n t:* %i
design -load orig
dfflibmap -map-only -liberty dfflibmap.lib
select -assert-count 5 t:$_NOT_
select -assert-count 0 t:dffn
select -assert-count 1 t:dffsr
design -load orig
dfflibmap -prepare -liberty dfflibmap.lib
dfflibmap -map-only -liberty dfflibmap.lib
clean
select -assert-count 4 t:$_NOT_
select -assert-count 1 t:dffn
select -assert-count 4 t:dffsr
select -assert-none t:dffn t:dffsr t:$_NOT_ %% %n t:* %i