| /* | 
 |  * Copyright 2020-2022 F4PGA Authors | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *     http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  * | 
 |  * SPDX-License-Identifier: Apache-2.0 | 
 |  */ | 
 | #include <algorithm> | 
 | #include <array> | 
 | #include <istream> | 
 | #include <iterator> | 
 | #include <map> | 
 | #include <memory> | 
 | #include <ostream> | 
 | #include <sstream> | 
 | #include <string> | 
 | #include <vector> | 
 |  | 
 | #include "clocks.h" | 
 | #include "kernel/log.h" | 
 | #include "kernel/register.h" | 
 | #include "kernel/rtlil.h" | 
 | #include "propagation.h" | 
 | #include "sdc_writer.h" | 
 | #include "set_clock_groups.h" | 
 | #include "set_false_path.h" | 
 | #include "set_max_delay.h" | 
 |  | 
 | USING_YOSYS_NAMESPACE | 
 |  | 
 | PRIVATE_NAMESPACE_BEGIN | 
 |  | 
 | struct ReadSdcCmd : public Frontend { | 
 |     ReadSdcCmd() : Frontend("sdc", "Read SDC file") {} | 
 |  | 
 |     void help() override | 
 |     { | 
 |         log("\n"); | 
 |         log("    read_sdc <filename>\n"); | 
 |         log("\n"); | 
 |         log("Read SDC file.\n"); | 
 |         log("\n"); | 
 |     } | 
 |  | 
 |     void execute(std::istream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *) override | 
 |     { | 
 |         if (args.size() < 2) { | 
 |             log_cmd_error("Missing script file.\n"); | 
 |         } | 
 |         log("\nReading clock constraints file(SDC)\n\n"); | 
 |         size_t argidx = 1; | 
 |         extra_args(f, filename, args, argidx); | 
 |         std::string content{std::istreambuf_iterator<char>(*f), std::istreambuf_iterator<char>()}; | 
 |         log("%s\n", content.c_str()); | 
 |         Tcl_Interp *interp = yosys_get_tcl_interp(); | 
 |         if (Tcl_EvalFile(interp, args[argidx].c_str()) != TCL_OK) { | 
 |             log_cmd_error("TCL interpreter returned an error: %s\n", Tcl_GetStringResult(interp)); | 
 |         } | 
 |     } | 
 | }; | 
 |  | 
 | struct WriteSdcCmd : public Backend { | 
 |     WriteSdcCmd(SdcWriter &sdc_writer) : Backend("sdc", "Write SDC file"), sdc_writer_(sdc_writer) {} | 
 |  | 
 |     void help() override | 
 |     { | 
 |         log("\n"); | 
 |         log("    write_sdc [-include_propagated_clocks] <filename>\n"); | 
 |         log("\n"); | 
 |         log("Write SDC file.\n"); | 
 |         log("\n"); | 
 |         log("    -include_propagated_clocks\n"); | 
 |         log("       Write out all propagated clocks"); | 
 |         log("\n"); | 
 |     } | 
 |  | 
 |     void execute(std::ostream *&f, std::string filename, std::vector<std::string> args, RTLIL::Design *design) override | 
 |     { | 
 |         size_t argidx; | 
 |         bool include_propagated = false; | 
 |         if (args.size() < 2) { | 
 |             log_cmd_error("Missing output file.\n"); | 
 |         } | 
 |         for (argidx = 1; argidx < args.size(); argidx++) { | 
 |             std::string arg = args[argidx]; | 
 |             if (arg == "-include_propagated_clocks" && argidx + 1 < args.size()) { | 
 |                 include_propagated = true; | 
 |                 continue; | 
 |             } | 
 |             break; | 
 |         } | 
 |         log("\nWriting out clock constraints file(SDC)\n"); | 
 |         extra_args(f, filename, args, argidx); | 
 |         sdc_writer_.WriteSdc(design, *f, include_propagated); | 
 |     } | 
 |  | 
 |     SdcWriter &sdc_writer_; | 
 | }; | 
 |  | 
 | struct CreateClockCmd : public Pass { | 
 |     CreateClockCmd() : Pass("create_clock", "Create clock object") {} | 
 |  | 
 |     void help() override | 
 |     { | 
 |         log("\n"); | 
 |         log("    create_clock [ -name clock_name ] -period period_value " | 
 |             "[-waveform <edge_list>] <target>\n"); | 
 |         log("Define a clock.\n"); | 
 |         log("If name is not specified then the name of the first target is " | 
 |             "selected as the clock's name.\n"); | 
 |         log("Period is expressed in nanoseconds.\n"); | 
 |         log("The waveform option specifies the duty cycle (the rising a " | 
 |             "falling edges) of the clock.\n"); | 
 |         log("It is specified as a list of two elements/time values: the first " | 
 |             "rising edge and the next falling edge.\n"); | 
 |         log("\n"); | 
 |     } | 
 |  | 
 |     void execute(std::vector<std::string> args, RTLIL::Design *design) override | 
 |     { | 
 |         size_t argidx; | 
 |         std::string name; | 
 |         bool is_waveform_specified(false); | 
 |         float rising_edge(0); | 
 |         float falling_edge(0); | 
 |         float period(0); | 
 |         if (args.size() < 4) { | 
 |             log_cmd_error("Incorrect number of arguments\n"); | 
 |         } | 
 |         for (argidx = 1; argidx < args.size(); argidx++) { | 
 |             std::string arg = args[argidx]; | 
 |             if (arg == "-add" && argidx + 1 < args.size()) { | 
 |                 continue; | 
 |             } | 
 |             if (arg == "-name" && argidx + 1 < args.size()) { | 
 |                 name = args[++argidx]; | 
 |                 continue; | 
 |             } | 
 |             if (arg == "-period" && argidx + 1 < args.size()) { | 
 |                 period = std::stof(args[++argidx]); | 
 |                 continue; | 
 |             } | 
 |             if (arg == "-waveform" && argidx + 1 < args.size()) { | 
 |                 std::string edges(args[++argidx]); | 
 |                 std::copy_if(edges.begin(), edges.end(), edges.begin(), [](char c) { return c != '{' or c != '}'; }); | 
 |                 std::stringstream ss(edges); | 
 |                 ss >> rising_edge >> falling_edge; | 
 |                 is_waveform_specified = true; | 
 |                 continue; | 
 |             } | 
 |             break; | 
 |         } | 
 |         if (period <= 0) { | 
 |             log_cmd_error("Incorrect period value\n"); | 
 |         } | 
 |         // Add "w:" prefix to selection arguments to enforce wire object | 
 |         // selection | 
 |         AddWirePrefix(args, argidx); | 
 |         extra_args(args, argidx, design); | 
 |         // If clock name is not specified then take the name of the first target | 
 |         std::vector<RTLIL::Wire *> selected_wires; | 
 |         for (auto module : design->modules()) { | 
 |             if (!design->selected(module)) { | 
 |                 continue; | 
 |             } | 
 |             for (auto wire : module->wires()) { | 
 |                 if (design->selected(module, wire)) { | 
 | #ifdef SDC_DEBUG | 
 |                     log("Selected wire %s\n", RTLIL::unescape_id(wire->name).c_str()); | 
 | #endif | 
 |                     selected_wires.push_back(wire); | 
 |                 } | 
 |             } | 
 |         } | 
 |         if (selected_wires.size() == 0) { | 
 |             log_cmd_error("Target selection is empty\n"); | 
 |         } | 
 |         if (name.empty()) { | 
 |             name = RTLIL::unescape_id(selected_wires.at(0)->name); | 
 |         } | 
 |         if (!is_waveform_specified) { | 
 |             rising_edge = 0; | 
 |             falling_edge = period / 2; | 
 |         } | 
 |         Clock::Add(name, selected_wires, period, rising_edge, falling_edge, Clock::EXPLICIT); | 
 |     } | 
 |  | 
 |     void AddWirePrefix(std::vector<std::string> &args, size_t argidx) | 
 |     { | 
 |         auto selection_begin = args.begin() + argidx; | 
 |         std::transform(selection_begin, args.end(), selection_begin, [](std::string &w) { return "w:" + w; }); | 
 |     } | 
 | }; | 
 |  | 
 | struct GetClocksCmd : public Pass { | 
 |     GetClocksCmd() : Pass("get_clocks", "Create clock object") {} | 
 |  | 
 |     void help() override | 
 |     { | 
 |         log("\n"); | 
 |         log("    get_clocks [-include_generated_clocks] [-of <nets>] " | 
 |             "[<patterns>]\n"); | 
 |         log("\n"); | 
 |         log("Returns all clocks in the design.\n"); | 
 |         log("\n"); | 
 |         log("    -include_generated_clocks\n"); | 
 |         log("        Include auto-generated clocks.\n"); | 
 |         log("\n"); | 
 |         log("    -of\n"); | 
 |         log("        Get clocks of these nets.\n"); | 
 |         log("\n"); | 
 |         log("    <pattern>\n"); | 
 |         log("        Pattern of clock names. Default are all clocks in the " | 
 |             "design.\n"); | 
 |         log("\n"); | 
 |     } | 
 |  | 
 |     std::vector<std::string> extract_list(const std::string &args) | 
 |     { | 
 |         std::vector<std::string> port_list; | 
 |         std::stringstream ss(args); | 
 |         std::istream_iterator<std::string> begin(ss); | 
 |         std::istream_iterator<std::string> end; | 
 |         std::copy(begin, end, std::back_inserter(port_list)); | 
 |         return port_list; | 
 |     } | 
 |  | 
 |     void execute(std::vector<std::string> args, RTLIL::Design *design) override | 
 |     { | 
 |  | 
 |         // Parse command arguments | 
 |         bool include_generated_clocks(false); | 
 |         std::vector<std::string> clocks_nets; | 
 |         size_t argidx(0); | 
 |  | 
 |         // Parse command switches | 
 |         for (argidx = 1; argidx < args.size(); argidx++) { | 
 |             std::string arg = args[argidx]; | 
 |             if (arg == "-include_generated_clocks") { | 
 |                 include_generated_clocks = true; | 
 |                 continue; | 
 |             } | 
 |             if (arg == "-of" and argidx + 1 < args.size()) { | 
 |                 clocks_nets = extract_list(args[++argidx]); | 
 | #ifdef SDC_DEBUG | 
 |                 for (auto clock_net : clocks_nets) { | 
 |                     log("Clock filter %s\n", clock_net.c_str()); | 
 |                 } | 
 | #endif | 
 |                 continue; | 
 |             } | 
 |             if (arg.size() > 0 and arg[0] == '-') { | 
 |                 log_cmd_error("Unknown option %s.\n", arg.c_str()); | 
 |             } | 
 |  | 
 |             break; | 
 |         } | 
 |  | 
 |         // Parse object patterns | 
 |         std::vector<std::string> clocks_list(args.begin() + argidx, args.end()); | 
 |  | 
 |         // Fetch clocks in the design | 
 |         std::map<std::string, RTLIL::Wire *> clocks(Clocks::GetClocks(design)); | 
 |         if (clocks.size() == 0) { | 
 |             log_warning("No clocks found in design\n"); | 
 |         } | 
 |  | 
 |         // Extract clocks into tcl list | 
 |         Tcl_Interp *interp = yosys_get_tcl_interp(); | 
 |         Tcl_Obj *tcl_list = Tcl_NewListObj(0, NULL); | 
 |         for (auto &clock : clocks) { | 
 |             // Skip propagated clocks (i.e. clock wires with the same parameters | 
 |             // as the master clocks they originate from | 
 |             if (Clock::IsPropagated(clock.second)) { | 
 |                 continue; | 
 |             } | 
 |             // Skip generated clocks if -include_generated_clocks is not specified | 
 |             if (Clock::IsGenerated(clock.second) and !include_generated_clocks) { | 
 |                 continue; | 
 |             } | 
 |             // Check if clock name is in the list of design clocks | 
 |             if (clocks_list.size() > 0 and std::find(clocks_list.begin(), clocks_list.end(), clock.first) == clocks_list.end()) { | 
 |                 continue; | 
 |             } | 
 |             // Check if clock wire is in the -of list | 
 |             if (clocks_nets.size() > 0 and std::find(clocks_nets.begin(), clocks_nets.end(), Clock::WireName(clock.second)) == clocks_nets.end()) { | 
 |                 continue; | 
 |             } | 
 |             auto &wire = clock.second; | 
 |             const char *name = RTLIL::id2cstr(wire->name); | 
 |             Tcl_Obj *name_obj = Tcl_NewStringObj(name, -1); | 
 |             Tcl_ListObjAppendElement(interp, tcl_list, name_obj); | 
 |         } | 
 |         Tcl_SetObjResult(interp, tcl_list); | 
 |     } | 
 | }; | 
 |  | 
 | struct PropagateClocksCmd : public Pass { | 
 |     PropagateClocksCmd() : Pass("propagate_clocks", "Propagate clock information") {} | 
 |  | 
 |     void help() override | 
 |     { | 
 |         log("\n"); | 
 |         log("    propagate_clocks\n"); | 
 |         log("\n"); | 
 |         log("Propagate clock information throughout the design.\n"); | 
 |         log("\n"); | 
 |     } | 
 |  | 
 |     void execute(std::vector<std::string> args, RTLIL::Design *design) override | 
 |     { | 
 |         if (args.size() > 1) { | 
 |             log_warning("Command accepts no arguments.\nAll will be ignored.\n"); | 
 |         } | 
 |         if (!design->top_module()) { | 
 |             log_cmd_error("No top module selected\n"); | 
 |         } | 
 |  | 
 |         std::array<std::unique_ptr<Propagation>, 2> passes{std::unique_ptr<Propagation>(new BufferPropagation(design, this)), | 
 |                                                            std::unique_ptr<Propagation>(new ClockDividerPropagation(design, this))}; | 
 |  | 
 |         log("Perform clock propagation\n"); | 
 |  | 
 |         for (auto &pass : passes) { | 
 |             pass->Run(); | 
 |         } | 
 |  | 
 |         Clocks::UpdateAbc9DelayTarget(design); | 
 |     } | 
 | }; | 
 |  | 
 | class SdcPlugin | 
 | { | 
 |   public: | 
 |     SdcPlugin() : write_sdc_cmd_(sdc_writer_), set_false_path_cmd_(sdc_writer_), set_max_delay_cmd_(sdc_writer_), set_clock_groups_cmd_(sdc_writer_) | 
 |     { | 
 |         log("Loaded SDC plugin\n"); | 
 |     } | 
 |  | 
 |     ReadSdcCmd read_sdc_cmd_; | 
 |     WriteSdcCmd write_sdc_cmd_; | 
 |     CreateClockCmd create_clock_cmd_; | 
 |     GetClocksCmd get_clocks_cmd_; | 
 |     PropagateClocksCmd propagate_clocks_cmd_; | 
 |     SetFalsePath set_false_path_cmd_; | 
 |     SetMaxDelay set_max_delay_cmd_; | 
 |     SetClockGroups set_clock_groups_cmd_; | 
 |  | 
 |   private: | 
 |     SdcWriter sdc_writer_; | 
 | } SdcPlugin; | 
 |  | 
 | PRIVATE_NAMESPACE_END |