From 207aaea29b9bfc33004b242fa8c546c659f0a102 Mon Sep 17 00:00:00 2001 From: Cassie Jones Date: Fri, 6 Mar 2020 22:53:14 +0100 Subject: [PATCH] Support multiple synthesis widths The original NaN gates paper describes 3-bit and 4-bit floating point formats. The original synthesis was hard-coded for 3-bit synthesis, but technically 5-bit floats are the smallest legal floating point values. Allowing for different synthesis provides opportunities to compare the different results. --- example/example.ys | 2 +- nangate.cc | 151 +++++++++++++++++++++++++++++++-------------- techlib.sv | 104 ++++++++++++++++++++++++++----- 3 files changed, 193 insertions(+), 64 deletions(-) diff --git a/example/example.ys b/example/example.ys index ff9f6b2..b1c5e52 100644 --- a/example/example.ys +++ b/example/example.ys @@ -1,5 +1,5 @@ plugin -i nangate read_verilog -sv example/example.sv -synth_nan -top hello +synth_nan -top hello -width 5 show hello # synth_ice40 -noflatten -top hello diff --git a/nangate.cc b/nangate.cc index 00859f7..673e294 100644 --- a/nangate.cc +++ b/nangate.cc @@ -7,33 +7,42 @@ USING_YOSYS_NAMESPACE PRIVATE_NAMESPACE_BEGIN +static bool is_width(const string &arg) { + return arg == "-w" or arg == "-width" or arg == "--width"; +} + struct NandToNaNWorker { int nand_count = 0, not_count = 0, b2f_count = 0, f2b_count = 0; RTLIL::Design *design; RTLIL::Module *module; + size_t width; - NandToNaNWorker(RTLIL::Design *design, RTLIL::Module *module) : - design(design), module(module) + NandToNaNWorker(RTLIL::Design *design, RTLIL::Module *module, size_t width) : + design(design), module(module), width(width) { + IdString b2f_cell("\\bit_to_fp" + std::to_string(width)); + IdString nan_cell("\\nan_fp" + std::to_string(width)); + IdString f2b_cell("\\fp" + std::to_string(width) + "_to_bit"); + for (auto cell : module->selected_cells()) { if (cell->type == "$_NAND_") { b2f_count += 2; nand_count += 1; f2b_count += 1; - RTLIL::Cell *b2fA = module->addCell(NEW_ID, "\\bit_to_fp3"); - RTLIL::Cell *b2fB = module->addCell(NEW_ID, "\\bit_to_fp3"); - RTLIL::Cell *nan = module->addCell(NEW_ID, "\\nan_fp3"); - RTLIL::Cell *f2b = module->addCell(NEW_ID, "\\fp3_to_bit"); - b2fA->attributes[ID(nan_b2f)] = 3; - b2fB->attributes[ID(nan_b2f)] = 3; - nan->attributes[ID(nan_cell)] = 3; - f2b->attributes[ID(nan_f2b)] = 3; + RTLIL::Cell *b2fA = module->addCell(NEW_ID, b2f_cell); + RTLIL::Cell *b2fB = module->addCell(NEW_ID, b2f_cell); + RTLIL::Cell *nan = module->addCell(NEW_ID, nan_cell); + RTLIL::Cell *f2b = module->addCell(NEW_ID, f2b_cell); + b2fA->attributes[ID(nan_b2f)] = width; + b2fB->attributes[ID(nan_b2f)] = width; + nan->attributes[ID(nan_cell)] = width; + f2b->attributes[ID(nan_f2b)] = width; b2fA->setPort("\\A", cell->getPort("\\A")); - b2fA->setPort("\\Y", module->addWire(NEW_ID, 3)); + b2fA->setPort("\\Y", module->addWire(NEW_ID, width)); b2fB->setPort("\\A", cell->getPort("\\B")); - b2fB->setPort("\\Y", module->addWire(NEW_ID, 3)); - f2b->setPort("\\A", module->addWire(NEW_ID, 3)); + b2fB->setPort("\\Y", module->addWire(NEW_ID, width)); + f2b->setPort("\\A", module->addWire(NEW_ID, width)); f2b->setPort("\\Y", cell->getPort("\\Y")); nan->setPort("\\A", b2fA->getPort("\\Y")); nan->setPort("\\B", b2fB->getPort("\\Y")); @@ -44,15 +53,15 @@ struct NandToNaNWorker b2f_count += 1; not_count += 1; f2b_count += 1; - RTLIL::Cell *b2f = module->addCell(NEW_ID, "\\bit_to_fp3"); - RTLIL::Cell *nan = module->addCell(NEW_ID, "\\nan_fp3"); - RTLIL::Cell *f2b = module->addCell(NEW_ID, "\\fp3_to_bit"); - b2f->attributes[ID(nan_b2f)] = 3; - nan->attributes[ID(nan_cell)] = 3; - f2b->attributes[ID(nan_f2b)] = 3; + RTLIL::Cell *b2f = module->addCell(NEW_ID, b2f_cell); + RTLIL::Cell *nan = module->addCell(NEW_ID, nan_cell); + RTLIL::Cell *f2b = module->addCell(NEW_ID, f2b_cell); + b2f->attributes[ID(nan_b2f)] = width; + nan->attributes[ID(nan_cell)] = width; + f2b->attributes[ID(nan_f2b)] = width; b2f->setPort("\\A", cell->getPort("\\A")); - b2f->setPort("\\Y", module->addWire(NEW_ID, 3)); - f2b->setPort("\\A", module->addWire(NEW_ID, 3)); + b2f->setPort("\\Y", module->addWire(NEW_ID, width)); + f2b->setPort("\\A", module->addWire(NEW_ID, width)); f2b->setPort("\\Y", cell->getPort("\\Y")); nan->setPort("\\A", b2f->getPort("\\Y")); nan->setPort("\\B", b2f->getPort("\\Y")); @@ -72,39 +81,69 @@ struct NandToNaNPass : public Pass { Pass::call(design, "read_verilog -lib -sv +/plugins/nangate/techlib.sv"); log_pop(); - (void) args; + int width = 3; + size_t argidx = 1; + for (; argidx < args.size(); ++argidx) { + if (is_width(args[argidx]) and argidx + 1 < args.size()) { + try { + width = std::stoi(args[++argidx]); + } catch (...) { + cmd_error(args, argidx, "Invalid number"); + } + continue; + } + break; + } + for (auto module : design->selected_modules()) { log("Replacing NAND with NaN in module %s...\n", log_id(module)); - NandToNaNWorker worker(design, module); + NandToNaNWorker worker(design, module, width); log("Replaced %d NAND gates and %d NOT gates.\n", worker.nand_count, worker.not_count); - log("Inserted:\n nan_fp3: %5d\n bit_to_fp3: %5d\n fp3_to_bit: %5d\n", + log("Inserted:\n nan_fp#: %5d\n bit_to_fp#: %5d\n fp#_to_bit: %5d\n", worker.nand_count + worker.not_count, worker.b2f_count, worker.f2b_count); } } } NandToNaNPass; -struct DffToFp3Pass : public Pass { - DffToFp3Pass() : Pass("dff_nan") {} +struct DffToFpPass : public Pass { + DffToFpPass() : Pass("dff_nan") {} void execute(vector args, Design *design) override { log_header(design, "Executing DFF_NaN pass (widening flipflops to hold floats)\n"); - (void) args; + + int width = 3; + size_t argidx = 1; + for (; argidx < args.size(); ++argidx) { + if (is_width(args[argidx]) and argidx + 1 < args.size()) { + try { + width = std::stoi(args[++argidx]); + } catch (...) { + cmd_error(args, argidx, "Invalid number"); + } + } + } + extra_args(args, argidx, design, false); + + IdString b2f_cell("\\bit_to_fp" + std::to_string(width)); + IdString nan_cell("\\nan_fp" + std::to_string(width)); + IdString f2b_cell("\\fp" + std::to_string(width) + "_to_bit"); + for (auto module : design->selected_modules()) { log(" Module %s\n", log_id(module)); dff_nan_pm pm(module, module->selected_cells()); pool dffs; pm.run([&]() { dffs.insert(pm.st.dff); }); for (auto &dff : dffs) { - RTLIL::Cell *f2b = module->addCell(NEW_ID, "\\fp3_to_bit"); - f2b->attributes[ID(nan_f2b)] = 3; - f2b->setPort("\\A", module->addWire(NEW_ID, 3)); + RTLIL::Cell *f2b = module->addCell(NEW_ID, f2b_cell); + f2b->attributes[ID(nan_f2b)] = width; + f2b->setPort("\\A", module->addWire(NEW_ID, width)); f2b->setPort("\\Y", dff->getPort("\\Q")); - RTLIL::Cell *b2f = module->addCell(NEW_ID, "\\bit_to_fp3"); - b2f->attributes[ID(nan_b2f)] = 3; + RTLIL::Cell *b2f = module->addCell(NEW_ID, b2f_cell); + b2f->attributes[ID(nan_b2f)] = width; b2f->setPort("\\A", dff->getPort("\\D")); - b2f->setPort("\\Y", module->addWire(NEW_ID, 3)); - for (int i = 0; i < 3; i++) { + b2f->setPort("\\Y", module->addWire(NEW_ID, width)); + for (int i = 0; i < width; i++) { // @TODO: Support more DFF types assert(dff->type == "$_DFF_P_"); RTLIL::Cell *new_ff = module->addCell(NEW_ID, "$_DFF_P_"); @@ -117,7 +156,7 @@ struct DffToFp3Pass : public Pass { log("Converted %d flip-flops to hold floats\n", GetSize(dffs)); } } -} DffToFp3Pass; +} DffToFpPass; struct EraseFpBitPass : public Pass { EraseFpBitPass() : Pass("simplify_nan") {} @@ -135,7 +174,7 @@ struct EraseFpBitPass : public Pass { for (auto cell : eraseCells) { module->remove(cell); } - log("Removed %d bit_to_fp3 nodes\n", GetSize(eraseCells)); + log("Removed %d bit_to_fp# nodes\n", GetSize(eraseCells)); } } } EraseFpBitPass; @@ -144,7 +183,7 @@ struct ShareNaN : public Pass { ShareNaN() : Pass("share_nan") {} void execute(vector args, Design *design) override { log_header(design, "Executing SHARE_NAN pass (merging conversion cells).\n"); - (void) args; + (void)args; for (auto module : design->selected_modules()) { log("Module %s\n", log_id(module)); share_nan_pm pm(module, module->selected_cells()); @@ -173,34 +212,54 @@ struct TechmapNaN : public Pass { struct SynthNaN : public Pass { SynthNaN() : Pass("synth_nan", "synthesize to tom7 logic") {} void help() override { - log("synth_nan [options]\n\n"); + log(""); + log(" synth_nan [options]\n\n"); + log(""); + log("This command synthesizes a design into NaN gates."); + log(""); + log(" -top "); + log(" use the specified module as top module (default='top')"); + log(""); + log(" -width "); + log(" synthesize with a given floating-point with (default=3)"); + log(""); log("Runs the equivalent of the following script:\n\n"); log(" synth [-top ]\n"); log(" abc -g NAND\n"); - log(" nand_to_nan\n"); + log(" nand_to_nan [-width ]\n"); log(" share_nan\n"); - log(" dff_nan\n"); + log(" dff_nan [-width ]\n"); log(" simplify_nan\n"); log(" clean\n"); log(" techmap_nan\n"); } void execute(vector args, Design *design) override { string synth_args; + string width_args; log_header(design, "Executing SYNTH_NaN pass (synthesizing to tom7 logic).\n"); log_push(); - for (size_t i = 0; i < args.size(); i++) { - if (args[i] == "-top") { + + size_t argidx = 1; + for (; argidx < args.size(); ++argidx) { + if (args[argidx] == "-top" and argidx + 1 < args.size()) { synth_args += " -top "; - synth_args += args[i+1]; - i += 1; + synth_args += args[++argidx]; continue; } + if (is_width(args[argidx]) and argidx + 1 < args.size()) { + width_args += " --width "; + width_args += args[++argidx]; + continue; + } + break; } + extra_args(args, argidx, design, false); + Pass::call(design, "synth" + synth_args); Pass::call(design, "abc -g NAND"); - Pass::call(design, "nand_to_nan"); + Pass::call(design, "nand_to_nan" + width_args); Pass::call(design, "share_nan"); - Pass::call(design, "dff_nan"); + Pass::call(design, "dff_nan" + width_args); Pass::call(design, "simplify_nan"); Pass::call(design, "clean"); Pass::call(design, "techmap_nan"); diff --git a/techlib.sv b/techlib.sv index 4f4717a..36328c5 100644 --- a/techlib.sv +++ b/techlib.sv @@ -1,13 +1,8 @@ -`define POS0 3'b000 -`define NEG0 3'b100 -`define POS1 3'b001 -`define NEG1 3'b101 -`define PINF 3'b010 -`define NINF 3'b110 -`define PNAN 3'b011 -`define NNAN 3'b111 -`define ANAN 3'b?11 -`define ANYF 3'b??? +`define FP3_PINF 3'b010 +`define FP3_NINF 3'b110 +`define FP3_PNAN 3'b011 +`define FP3_ANAN 3'b?11 +`define FP3_ANYF 3'b??? // A NaN gate! Computes `Inf - max(x+y, -Inf)`. // See http://tom7.org/nand/ @@ -19,26 +14,101 @@ module nan_fp3( always begin casez ({A, B}) - {`ANYF, `ANAN}, {`ANAN, `ANYF}: Y = `PINF; - {`PINF, `NINF}, {`NINF, `PINF}: Y = `PINF; - {`ANYF, `PINF}, {`PINF, `ANYF}: Y = `PNAN; - default: Y = `PINF; + {`FP3_ANYF, `FP3_ANAN}, {`FP3_ANAN, `FP3_ANYF}: Y = `FP3_PINF; + {`FP3_PINF, `FP3_NINF}, {`FP3_NINF, `FP3_PINF}: Y = `FP3_PINF; + {`FP3_ANYF, `FP3_PINF}, {`FP3_PINF, `FP3_ANYF}: Y = `FP3_PNAN; + default: Y = `FP3_PINF; endcase end endmodule // Convert a bit into an fp3 module bit_to_fp3(input A, output [2:0] Y); -assign Y = A ? `PNAN : `PINF; +assign Y = A ? `FP3_PNAN : `FP3_PINF; endmodule // Convert an fp3 into a bit module fp3_to_bit(input [2:0] A, output reg Y); always begin - case (A) - `PNAN, `NNAN: Y = 1'b1; + casez (A) + `FP3_ANAN: Y = 1'b1; default: Y = 1'b0; endcase end endmodule +`define FP4_PINF 4'b0100 +`define FP4_NINF 4'b1100 +`define FP4_PNAN 4'b0111 +`define FP4_1NAN 4'b?101 +`define FP4_2NAN 4'b?11? +`define FP4_ANYF 4'b???? + +module nan_fp4( + input [3:0] A, + input [3:0] B, + output reg [3:0] Y +); + +always begin + casez ({A, B}) + {`FP4_ANYF, `FP4_1NAN}, {`FP4_1NAN, `FP4_ANYF}: Y = `FP4_PINF; + {`FP4_ANYF, `FP4_2NAN}, {`FP4_2NAN, `FP4_ANYF}: Y = `FP4_PINF; + {`FP4_PINF, `FP4_NINF}, {`FP4_NINF, `FP4_PINF}: Y = `FP4_PINF; + {`FP4_ANYF, `FP4_PINF}, {`FP4_PINF, `FP4_ANYF}: Y = `FP4_PNAN; + default: Y = `FP4_PINF; + endcase +end +endmodule + +module bit_to_fp4(input A, output [3:0] Y); +assign Y = A ? `FP4_PNAN : `FP4_PINF; +endmodule + +module fp4_to_bit(input [3:0] A, output reg Y); +always begin + casez (A) + `FP4_1NAN: Y = 1'b1; + `FP4_2NAN: Y = 1'b1; + default: Y = 1'b0; + endcase +end +endmodule + +`define FP5_PINF 5'b01100 +`define FP5_NINF 5'b11100 +`define FP5_PNAN 5'b01111 +`define FP5_1NAN 5'b?1101 +`define FP5_2NAN 5'b?111? +`define FP5_ANYF 5'b????? + +module nan_fp5( + input [4:0] A, + input [4:0] B, + output reg [4:0] Y +); + +always begin + casez ({A, B}) + {`FP5_ANYF, `FP5_1NAN}, {`FP5_1NAN, `FP5_ANYF}: Y = `FP5_PINF; + {`FP5_ANYF, `FP5_2NAN}, {`FP5_2NAN, `FP5_ANYF}: Y = `FP5_PINF; + {`FP5_PINF, `FP5_NINF}, {`FP5_NINF, `FP5_PINF}: Y = `FP5_PINF; + {`FP5_ANYF, `FP5_PINF}, {`FP5_PINF, `FP5_ANYF}: Y = `FP5_PNAN; + default: Y = `FP5_PINF; + endcase +end +endmodule + +module bit_to_fp5(input A, output [4:0] Y); +assign Y = A ? `FP5_PNAN : `FP5_PINF; +endmodule + +module fp5_to_bit(input [4:0] A, output reg Y); +always begin + casez (A) + `FP5_1NAN: Y = 1'b1; + `FP5_2NAN: Y = 1'b1; + default: Y = 1'b0; + endcase +end +endmodule -- 2.47.0