Support multiple synthesis widths
[nan-gate] / nangate.cc
1 #include "kernel/yosys.h"
2 #include "kernel/celltypes.h"
3 #include "erase_b2f_pm.h"
4 #include "dff_nan_pm.h"
5 #include "share_nan_pm.h"
6
7 USING_YOSYS_NAMESPACE
8 PRIVATE_NAMESPACE_BEGIN
9
10 static bool is_width(const string &arg) {
11 return arg == "-w" or arg == "-width" or arg == "--width";
12 }
13
14 struct NandToNaNWorker
15 {
16 int nand_count = 0, not_count = 0, b2f_count = 0, f2b_count = 0;
17 RTLIL::Design *design;
18 RTLIL::Module *module;
19 size_t width;
20
21 NandToNaNWorker(RTLIL::Design *design, RTLIL::Module *module, size_t width) :
22 design(design), module(module), width(width)
23 {
24 IdString b2f_cell("\\bit_to_fp" + std::to_string(width));
25 IdString nan_cell("\\nan_fp" + std::to_string(width));
26 IdString f2b_cell("\\fp" + std::to_string(width) + "_to_bit");
27
28 for (auto cell : module->selected_cells()) {
29 if (cell->type == "$_NAND_") {
30 b2f_count += 2;
31 nand_count += 1;
32 f2b_count += 1;
33 RTLIL::Cell *b2fA = module->addCell(NEW_ID, b2f_cell);
34 RTLIL::Cell *b2fB = module->addCell(NEW_ID, b2f_cell);
35 RTLIL::Cell *nan = module->addCell(NEW_ID, nan_cell);
36 RTLIL::Cell *f2b = module->addCell(NEW_ID, f2b_cell);
37 b2fA->attributes[ID(nan_b2f)] = width;
38 b2fB->attributes[ID(nan_b2f)] = width;
39 nan->attributes[ID(nan_cell)] = width;
40 f2b->attributes[ID(nan_f2b)] = width;
41 b2fA->setPort("\\A", cell->getPort("\\A"));
42 b2fA->setPort("\\Y", module->addWire(NEW_ID, width));
43 b2fB->setPort("\\A", cell->getPort("\\B"));
44 b2fB->setPort("\\Y", module->addWire(NEW_ID, width));
45 f2b->setPort("\\A", module->addWire(NEW_ID, width));
46 f2b->setPort("\\Y", cell->getPort("\\Y"));
47 nan->setPort("\\A", b2fA->getPort("\\Y"));
48 nan->setPort("\\B", b2fB->getPort("\\Y"));
49 nan->setPort("\\Y", f2b->getPort("\\A"));
50 module->swap_names(cell, nan);
51 module->remove(cell);
52 } else if (cell->type == "$_NOT_") {
53 b2f_count += 1;
54 not_count += 1;
55 f2b_count += 1;
56 RTLIL::Cell *b2f = module->addCell(NEW_ID, b2f_cell);
57 RTLIL::Cell *nan = module->addCell(NEW_ID, nan_cell);
58 RTLIL::Cell *f2b = module->addCell(NEW_ID, f2b_cell);
59 b2f->attributes[ID(nan_b2f)] = width;
60 nan->attributes[ID(nan_cell)] = width;
61 f2b->attributes[ID(nan_f2b)] = width;
62 b2f->setPort("\\A", cell->getPort("\\A"));
63 b2f->setPort("\\Y", module->addWire(NEW_ID, width));
64 f2b->setPort("\\A", module->addWire(NEW_ID, width));
65 f2b->setPort("\\Y", cell->getPort("\\Y"));
66 nan->setPort("\\A", b2f->getPort("\\Y"));
67 nan->setPort("\\B", b2f->getPort("\\Y"));
68 nan->setPort("\\Y", f2b->getPort("\\A"));
69 module->swap_names(cell, nan);
70 module->remove(cell);
71 }
72 }
73 }
74 };
75
76 struct NandToNaNPass : public Pass {
77 NandToNaNPass() : Pass("nand_to_nan") {}
78 void execute(vector<string> args, Design *design) override {
79 log_header(design, "Executing NAND_TO_NaN pass (implementing tom7 logic)\n");
80 log_push();
81 Pass::call(design, "read_verilog -lib -sv +/plugins/nangate/techlib.sv");
82 log_pop();
83
84 int width = 3;
85 size_t argidx = 1;
86 for (; argidx < args.size(); ++argidx) {
87 if (is_width(args[argidx]) and argidx + 1 < args.size()) {
88 try {
89 width = std::stoi(args[++argidx]);
90 } catch (...) {
91 cmd_error(args, argidx, "Invalid number");
92 }
93 continue;
94 }
95 break;
96 }
97
98 for (auto module : design->selected_modules()) {
99 log("Replacing NAND with NaN in module %s...\n", log_id(module));
100 NandToNaNWorker worker(design, module, width);
101 log("Replaced %d NAND gates and %d NOT gates.\n",
102 worker.nand_count, worker.not_count);
103 log("Inserted:\n nan_fp#: %5d\n bit_to_fp#: %5d\n fp#_to_bit: %5d\n",
104 worker.nand_count + worker.not_count,
105 worker.b2f_count, worker.f2b_count);
106 }
107 }
108 } NandToNaNPass;
109
110 struct DffToFpPass : public Pass {
111 DffToFpPass() : Pass("dff_nan") {}
112 void execute(vector<string> args, Design *design) override {
113 log_header(design, "Executing DFF_NaN pass (widening flipflops to hold floats)\n");
114
115 int width = 3;
116 size_t argidx = 1;
117 for (; argidx < args.size(); ++argidx) {
118 if (is_width(args[argidx]) and argidx + 1 < args.size()) {
119 try {
120 width = std::stoi(args[++argidx]);
121 } catch (...) {
122 cmd_error(args, argidx, "Invalid number");
123 }
124 }
125 }
126 extra_args(args, argidx, design, false);
127
128 IdString b2f_cell("\\bit_to_fp" + std::to_string(width));
129 IdString nan_cell("\\nan_fp" + std::to_string(width));
130 IdString f2b_cell("\\fp" + std::to_string(width) + "_to_bit");
131
132 for (auto module : design->selected_modules()) {
133 log(" Module %s\n", log_id(module));
134 dff_nan_pm pm(module, module->selected_cells());
135 pool<RTLIL::Cell*> dffs;
136 pm.run([&]() { dffs.insert(pm.st.dff); });
137 for (auto &dff : dffs) {
138 RTLIL::Cell *f2b = module->addCell(NEW_ID, f2b_cell);
139 f2b->attributes[ID(nan_f2b)] = width;
140 f2b->setPort("\\A", module->addWire(NEW_ID, width));
141 f2b->setPort("\\Y", dff->getPort("\\Q"));
142 RTLIL::Cell *b2f = module->addCell(NEW_ID, b2f_cell);
143 b2f->attributes[ID(nan_b2f)] = width;
144 b2f->setPort("\\A", dff->getPort("\\D"));
145 b2f->setPort("\\Y", module->addWire(NEW_ID, width));
146 for (int i = 0; i < width; i++) {
147 // @TODO: Support more DFF types
148 assert(dff->type == "$_DFF_P_");
149 RTLIL::Cell *new_ff = module->addCell(NEW_ID, "$_DFF_P_");
150 new_ff->setPort("\\C", dff->getPort("\\C"));
151 new_ff->setPort("\\D", b2f->getPort("\\Y")[i]);
152 new_ff->setPort("\\Q", f2b->getPort("\\A")[i]);
153 }
154 module->remove(dff);
155 }
156 log("Converted %d flip-flops to hold floats\n", GetSize(dffs));
157 }
158 }
159 } DffToFpPass;
160
161 struct EraseFpBitPass : public Pass {
162 EraseFpBitPass() : Pass("simplify_nan") {}
163 void execute(vector<string> args, Design *design) override {
164 log_header(design, "Executing SIMPLIFY_NaN pass (erasing useless conversion chains)\n");
165 (void) args;
166 for (auto module : design->selected_modules()) {
167 log("Simplifying NaN conversions in module %s\n", log_id(module));
168 erase_b2f_pm pm(module, module->selected_cells());
169 pool<RTLIL::Cell*> eraseCells;
170 pm.run([&]() {
171 module->connect(pm.st.base->getPort("\\A"), pm.st.target->getPort("\\Y"));
172 eraseCells.insert(pm.st.target);
173 });
174 for (auto cell : eraseCells) {
175 module->remove(cell);
176 }
177 log("Removed %d bit_to_fp# nodes\n", GetSize(eraseCells));
178 }
179 }
180 } EraseFpBitPass;
181
182 struct ShareNaN : public Pass {
183 ShareNaN() : Pass("share_nan") {}
184 void execute(vector <string> args, Design *design) override {
185 log_header(design, "Executing SHARE_NAN pass (merging conversion cells).\n");
186 (void)args;
187 for (auto module : design->selected_modules()) {
188 log("Module %s\n", log_id(module));
189 share_nan_pm pm(module, module->selected_cells());
190 mfp<RTLIL::Cell*> sharedCells;
191 pm.run([&]() { sharedCells.merge(pm.st.cvt_a, pm.st.cvt_b); });
192 int merged = 0;
193 for (auto &entry : sharedCells) {
194 auto &main = sharedCells.find(entry);
195 if (entry == main) continue;
196 merged += 1;
197 module->connect(main->getPort("\\Y"), entry->getPort("\\Y"));
198 module->remove(entry);
199 }
200 log("Merged %d conversion cells\n", merged);
201 }
202 }
203 } ShareNaNPass;
204
205 struct TechmapNaN : public Pass {
206 TechmapNaN() : Pass("techmap_nan", "techmap NaN gates") {}
207 void execute(vector<string>, Design *design) override {
208 Pass::call(design, "techmap -autoproc -extern -map +/plugins/nangate/techlib.sv");
209 }
210 } TechmapNaNPass;
211
212 struct SynthNaN : public Pass {
213 SynthNaN() : Pass("synth_nan", "synthesize to tom7 logic") {}
214 void help() override {
215 log("");
216 log(" synth_nan [options]\n\n");
217 log("");
218 log("This command synthesizes a design into NaN gates.");
219 log("");
220 log(" -top <module>");
221 log(" use the specified module as top module (default='top')");
222 log("");
223 log(" -width <width>");
224 log(" synthesize with a given floating-point with (default=3)");
225 log("");
226 log("Runs the equivalent of the following script:\n\n");
227 log(" synth [-top <module>]\n");
228 log(" abc -g NAND\n");
229 log(" nand_to_nan [-width <width>]\n");
230 log(" share_nan\n");
231 log(" dff_nan [-width <width>]\n");
232 log(" simplify_nan\n");
233 log(" clean\n");
234 log(" techmap_nan\n");
235 }
236 void execute(vector <string> args, Design *design) override {
237 string synth_args;
238 string width_args;
239 log_header(design, "Executing SYNTH_NaN pass (synthesizing to tom7 logic).\n");
240 log_push();
241
242 size_t argidx = 1;
243 for (; argidx < args.size(); ++argidx) {
244 if (args[argidx] == "-top" and argidx + 1 < args.size()) {
245 synth_args += " -top ";
246 synth_args += args[++argidx];
247 continue;
248 }
249 if (is_width(args[argidx]) and argidx + 1 < args.size()) {
250 width_args += " --width ";
251 width_args += args[++argidx];
252 continue;
253 }
254 break;
255 }
256 extra_args(args, argidx, design, false);
257
258 Pass::call(design, "synth" + synth_args);
259 Pass::call(design, "abc -g NAND");
260 Pass::call(design, "nand_to_nan" + width_args);
261 Pass::call(design, "share_nan");
262 Pass::call(design, "dff_nan" + width_args);
263 Pass::call(design, "simplify_nan");
264 Pass::call(design, "clean");
265 Pass::call(design, "techmap_nan");
266 log_pop();
267 }
268 } SynthNaNPass;
269
270 PRIVATE_NAMESPACE_END
271