#include #include #include #include #include #include #include #include "sdc_common.hpp" #include "sdc_error.hpp" namespace sdcparse { /* * Functions for create_clock */ void sdc_create_clock_set_period(Callback& callback, const Lexer& lexer, CreateClock& sdc_create_clock, double period) { if(!std::isnan(sdc_create_clock.period)) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Can only define a single clock period.\n"); } else { sdc_create_clock.period = period; } } void sdc_create_clock_set_name(Callback& callback, const Lexer& lexer, CreateClock& sdc_create_clock, const std::string& name) { if(!sdc_create_clock.name.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Can only define a single clock name.\n"); } else { sdc_create_clock.name = name; } } void sdc_create_clock_set_waveform(Callback& callback, const Lexer& lexer, CreateClock& sdc_create_clock, double rise_edge, double fall_edge) { if(!std::isnan(sdc_create_clock.rise_edge) || !std::isnan(sdc_create_clock.fall_edge)) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Can only define a single waveform.\n"); } else { sdc_create_clock.rise_edge = rise_edge; sdc_create_clock.fall_edge = fall_edge; } } void sdc_create_clock_add_targets(Callback& callback, const Lexer& lexer, CreateClock& sdc_create_clock, StringGroup target_group) { assert(target_group.type == StringGroupType::STRING); if(!sdc_create_clock.targets.strings.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Can only define a single set of targets for clock creation. " "If you want to define multiple targets specify them as a list (e.g. \"{target1 target2}\").\n"); } sdc_create_clock.targets = target_group; } void add_sdc_create_clock(Callback& callback, const Lexer& lexer, CreateClock& sdc_create_clock) { /* * Error Checking */ //Must have a clock period if(std::isnan(sdc_create_clock.period)) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must define clock period.\n"); } //Must have either a target (if a netlist clock), or a name (if a virtual clock) if(sdc_create_clock.targets.strings.size() == 0 && sdc_create_clock.name.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must define either a target (for netlist clock) or a name (for virtual clock).\n"); } //Currently we do not support defining clock names that differ from the netlist target name if(sdc_create_clock.targets.strings.size() != 0 && !sdc_create_clock.name.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Currently custom names for netlist clocks are unsupported, remove '-name' option or create a virtual clock.\n"); } /* * Set defaults */ //Determine default rise/fall time for waveform if(std::isnan(sdc_create_clock.rise_edge) && std::isnan(sdc_create_clock.fall_edge)) { sdc_create_clock.rise_edge = 0.0; sdc_create_clock.fall_edge = sdc_create_clock.period / 2; } assert(!std::isnan(sdc_create_clock.rise_edge)); assert(!std::isnan(sdc_create_clock.fall_edge)); //Determine if clock is virtual or not if(sdc_create_clock.targets.strings.size() == 0 && !sdc_create_clock.name.empty()) { //Have a virtual target if there is a name AND no target strings sdc_create_clock.is_virtual = true; } else { assert(sdc_create_clock.targets.strings.size() > 0); //Have a real target so this is not a virtual clock sdc_create_clock.is_virtual = false; } /* * Add command */ callback.create_clock(sdc_create_clock); } /* * Functions for set_input_delay/set_output_delay */ void sdc_set_io_delay_set_clock(Callback& callback, const Lexer& lexer, SetIoDelay& sdc_set_io_delay, const std::string& clock_name) { if(!sdc_set_io_delay.clock_name.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Can only specify a single clock\n"); } sdc_set_io_delay.clock_name = clock_name; } void sdc_set_io_delay_set_value(Callback& callback, const Lexer& lexer, SetIoDelay& sdc_set_io_delay, double value) { if(!std::isnan(sdc_set_io_delay.delay)) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Delay value can only specified once.\n"); } sdc_set_io_delay.delay = value; } void sdc_set_io_delay_set_max(Callback& callback, const Lexer& lexer, SetIoDelay& sdc_set_io_delay) { if (sdc_set_io_delay.is_max) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "-max can only be specified once.\n"); } sdc_set_io_delay.is_max = true; } void sdc_set_io_delay_set_min(Callback& callback, const Lexer& lexer, SetIoDelay& sdc_set_io_delay) { if (sdc_set_io_delay.is_min) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "-min can only be specified once.\n"); } sdc_set_io_delay.is_min = true; } void sdc_set_io_delay_set_ports(Callback& callback, const Lexer& lexer, SetIoDelay& sdc_set_io_delay, StringGroup ports) { assert(ports.type == StringGroupType::PORT); if(!sdc_set_io_delay.target_ports.strings.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Currently only a single get_ports command is supported.\n"); } sdc_set_io_delay.target_ports = ports; } void add_sdc_set_io_delay(Callback& callback, const Lexer& lexer, SetIoDelay& sdc_set_io_delay) { /* * Error checks */ if(sdc_set_io_delay.clock_name.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify clock name.\n"); } if(std::isnan(sdc_set_io_delay.delay)) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify delay value.\n"); } if(sdc_set_io_delay.target_ports.strings.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify target ports using get_ports.\n"); } /* * Add command */ callback.set_io_delay(sdc_set_io_delay); } /* * Functions for set_clock_groups */ void sdc_set_clock_groups_set_type(Callback& callback, const Lexer& lexer, SetClockGroups& sdc_set_clock_groups, ClockGroupsType type) { if(sdc_set_clock_groups.type != ClockGroupsType::NONE) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Can only specify a single clock groups relation type (e.g. '-exclusive')\n"); } sdc_set_clock_groups.type = type; } void sdc_set_clock_groups_add_group(Callback& /*callback*/, const Lexer& /*lexer*/, SetClockGroups& sdc_set_clock_groups, StringGroup clock_group) { assert(clock_group.type == StringGroupType::CLOCK || clock_group.type == StringGroupType::STRING); //Duplicate and insert the clock group sdc_set_clock_groups.clock_groups.push_back(clock_group); } void add_sdc_set_clock_groups(Callback& callback, const Lexer& lexer, SetClockGroups& sdc_set_clock_groups) { /* * Error checks */ if(sdc_set_clock_groups.type == ClockGroupsType::NONE) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify clock relation type as '-exclusive'.\n"); } if(sdc_set_clock_groups.clock_groups.size() < 2) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify at least 2 clock groups.\n"); } /* * Add command */ callback.set_clock_groups(sdc_set_clock_groups); } /* * Functions for set_false_path */ void sdc_set_false_path_add_to_from_group(Callback& callback, const Lexer& lexer, SetFalsePath& sdc_set_false_path, StringGroup group, FromToType to_from_dir) { assert(group.type == StringGroupType::CLOCK || group.type == StringGroupType::STRING); //Error checking if(to_from_dir == FromToType::FROM) { //Check that we haven't already defined the from path if(!sdc_set_false_path.from.strings.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-from' option is supported.\n"); } } else { assert(to_from_dir == FromToType::TO); //Check that we haven't already defined the from path if(!sdc_set_false_path.to.strings.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-to' option is supported.\n"); } } //Add the clock group if(to_from_dir == FromToType::FROM) { sdc_set_false_path.from = group; } else { assert(to_from_dir == FromToType::TO); sdc_set_false_path.to = group; } } void add_sdc_set_false_path(Callback& callback, const Lexer& /*lexer*/, SetFalsePath& sdc_set_false_path) { /* * Error checks */ /* * Add command */ callback.set_false_path(sdc_set_false_path); } /* * Functions for set_max_delay/set_min_delay */ void sdc_set_min_max_delay_set_value(Callback& callback, const Lexer& lexer, SetMinMaxDelay& sdc_set_min_max_delay, double min_max_delay) { if(!std::isnan(sdc_set_min_max_delay.value)) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify max delay value only once.\n"); } sdc_set_min_max_delay.value = min_max_delay; } void sdc_set_min_max_delay_add_to_from_group(Callback& callback, const Lexer& lexer, SetMinMaxDelay& sdc_set_min_max_delay, StringGroup group, FromToType to_from_dir) { assert(group.type == StringGroupType::CLOCK || group.type == StringGroupType::STRING); //Error checking if(to_from_dir == FromToType::FROM) { //Check that we haven't already defined the from path if(!sdc_set_min_max_delay.from.strings.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-from' option is supported.\n"); } } else { assert(to_from_dir == FromToType::TO); //Check that we haven't already defined the from path if(!sdc_set_min_max_delay.to.strings.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-to' option is supported.\n"); } } //Add the clock group if(to_from_dir == FromToType::FROM) { sdc_set_min_max_delay.from = group; } else { assert(to_from_dir == FromToType::TO); sdc_set_min_max_delay.to = group; } } void add_sdc_set_min_max_delay(Callback& callback, const Lexer& lexer, SetMinMaxDelay& sdc_set_min_max_delay) { /* * Error checks */ if(std::isnan(sdc_set_min_max_delay.value)) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify the max delay value.\n"); } /* * Add command */ callback.set_min_max_delay(sdc_set_min_max_delay); } /* * Functions for set_multicycle_path */ void sdc_set_multicycle_path_set_setup(Callback& callback, const Lexer& lexer, SetMulticyclePath& sdc_set_multicycle_path) { if(sdc_set_multicycle_path.is_setup) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "'-setup' should only be specified once.\n"); } sdc_set_multicycle_path.is_setup = true; } void sdc_set_multicycle_path_set_hold(Callback& callback, const Lexer& lexer, SetMulticyclePath& sdc_set_multicycle_path) { if(sdc_set_multicycle_path.is_hold) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "'-hold' should only be specified once.\n"); } sdc_set_multicycle_path.is_hold = true; } void sdc_set_multicycle_path_set_mcp_value(Callback& callback, const Lexer& lexer, SetMulticyclePath& sdc_set_multicycle_path, int mcp_value) { if(sdc_set_multicycle_path.mcp_value != UNINITIALIZED_INT) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify multicycle path value only once.\n"); } sdc_set_multicycle_path.mcp_value = mcp_value; } void sdc_set_multicycle_path_add_to_from_group(Callback& callback, const Lexer& lexer, SetMulticyclePath& sdc_set_multicycle_path, StringGroup group, FromToType to_from_dir) { assert(group.type == StringGroupType::CLOCK || group.type == StringGroupType::STRING || group.type == StringGroupType::PIN); //Error checking if(to_from_dir == FromToType::FROM) { //Check that we haven't already defined the from path if(!sdc_set_multicycle_path.from.strings.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-from' option is supported.\n"); } } else { assert(to_from_dir == FromToType::TO); //Check that we haven't already defined the from path if(!sdc_set_multicycle_path.to.strings.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-to' option is supported.\n"); } } //Add the clock group if(to_from_dir == FromToType::FROM) { sdc_set_multicycle_path.from = group; } else { assert(to_from_dir == FromToType::TO); sdc_set_multicycle_path.to = group; } } void add_sdc_set_multicycle_path(Callback& callback, const Lexer& lexer, SetMulticyclePath& sdc_set_multicycle_path) { /* * Error checks */ if(sdc_set_multicycle_path.mcp_value == UNINITIALIZED_INT) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify the multicycle path value.\n"); } /* * Add command */ callback.set_multicycle_path(sdc_set_multicycle_path); } /* * Functions for set_clock_uncertainty */ void sdc_set_clock_uncertainty_set_setup(Callback& callback, const Lexer& lexer, SetClockUncertainty& sdc_set_clock_uncertainty) { if(sdc_set_clock_uncertainty.is_setup) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "'-setup' should only be specified once.\n"); } sdc_set_clock_uncertainty.is_setup = true; } void sdc_set_clock_uncertainty_set_hold(Callback& callback, const Lexer& lexer, SetClockUncertainty& sdc_set_clock_uncertainty) { if(sdc_set_clock_uncertainty.is_hold) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "'-hold' should only be specified once.\n"); } sdc_set_clock_uncertainty.is_hold = true; } void sdc_set_clock_uncertainty_set_value(Callback& callback, const Lexer& lexer, SetClockUncertainty& sdc_set_clock_uncertainty, float value) { if(!std::isnan(sdc_set_clock_uncertainty.value)) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify clock uncertainty value only once.\n"); } sdc_set_clock_uncertainty.value = value; } void sdc_set_clock_uncertainty_add_to_from_group(Callback& callback, const Lexer& lexer, SetClockUncertainty& sdc_set_clock_uncertainty, StringGroup group, FromToType to_from_dir) { assert(group.type == StringGroupType::CLOCK || group.type == StringGroupType::STRING); //Error checking if(to_from_dir == FromToType::FROM) { //Check that we haven't already defined the from path if(!sdc_set_clock_uncertainty.from.strings.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-from' option is supported.\n"); } } else { assert(to_from_dir == FromToType::TO); //Check that we haven't already defined the from path if(!sdc_set_clock_uncertainty.to.strings.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-to' option is supported.\n"); } } //Add the clock group if(to_from_dir == FromToType::FROM) { sdc_set_clock_uncertainty.from = group; } else { assert(to_from_dir == FromToType::TO); sdc_set_clock_uncertainty.to = group; } } void add_sdc_set_clock_uncertainty(Callback& callback, const Lexer& lexer, SetClockUncertainty& sdc_set_clock_uncertainty) { /* * Error checks */ if(std::isnan(sdc_set_clock_uncertainty.value)) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify the clock uncertainty value.\n"); } /* * Add command */ callback.set_clock_uncertainty(sdc_set_clock_uncertainty); } /* * Functions for set_clock_latency */ void sdc_set_clock_latency_set_type(Callback& callback, const Lexer& lexer, SetClockLatency& sdc_set_clock_latency, ClockLatencyType type) { //Error checking if(sdc_set_clock_latency.type != ClockLatencyType::NONE) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "The latency type (e.g. '-source') can only be specified once.\n"); } sdc_set_clock_latency.type = type; } void sdc_set_clock_latency_early(Callback& callback, const Lexer& lexer, SetClockLatency& sdc_set_clock_latency) { //Error checking if(sdc_set_clock_latency.is_early) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "The '-early' option can only be specified once.\n"); } sdc_set_clock_latency.is_early = true; } void sdc_set_clock_latency_late(Callback& callback, const Lexer& lexer, SetClockLatency& sdc_set_clock_latency) { //Error checking if(sdc_set_clock_latency.is_late) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "The '-late' option can only be specified once.\n"); } sdc_set_clock_latency.is_late = true; } void sdc_set_clock_latency_set_value(Callback& callback, const Lexer& lexer, SetClockLatency& sdc_set_clock_latency, float value) { if(!std::isnan(sdc_set_clock_latency.value)) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "The clock latency value can only be specified once.\n"); } sdc_set_clock_latency.value = value; } void sdc_set_clock_latency_set_clocks(Callback& callback, const Lexer& lexer, SetClockLatency& sdc_set_clock_latency, StringGroup target_clocks) { if(target_clocks.type != StringGroupType::CLOCK) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Target clocks must be specified with 'get_clocks'.\n"); } sdc_set_clock_latency.target_clocks = target_clocks; } void add_sdc_set_clock_latency(Callback& callback, const Lexer& lexer, SetClockLatency& sdc_set_clock_latency) { /* * Error checks */ if(sdc_set_clock_latency.type != ClockLatencyType::SOURCE) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify the clock latency type (e.g. '-source').\n"); } if(std::isnan(sdc_set_clock_latency.value)) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify the clock latency value.\n"); } if(sdc_set_clock_latency.target_clocks.strings.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify target clocks.\n"); } /* * Add command */ callback.set_clock_latency(sdc_set_clock_latency); } /* * Functions for set_disable_timing */ void sdc_set_disable_timing_add_to_from_group(Callback& callback, const Lexer& lexer, SetDisableTiming& sdc_set_disable_timing, StringGroup group, FromToType to_from_dir) { //Error checking if (group.type != StringGroupType::PIN && group.type != StringGroupType::STRING) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only get_pins supported with set_disable_timing.\n"); } if(to_from_dir == FromToType::FROM) { //Check that we haven't already defined the from path if(!sdc_set_disable_timing.from.strings.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-from' option is supported.\n"); } } else { assert(to_from_dir == FromToType::TO); //Check that we haven't already defined the from path if(!sdc_set_disable_timing.to.strings.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single '-to' option is supported.\n"); } } //Add the clock group if(to_from_dir == FromToType::FROM) { sdc_set_disable_timing.from = group; } else { assert(to_from_dir == FromToType::TO); sdc_set_disable_timing.to = group; } } void add_sdc_set_disable_timing(Callback& callback, const Lexer& /*lexer*/, SetDisableTiming& sdc_set_disable_timing) { /* * Error checks */ /* * Add command */ callback.set_disable_timing(sdc_set_disable_timing); } /* * Functions for set_timing_derate */ void sdc_set_timing_derate_early(Callback& callback, const Lexer& lexer, SetTimingDerate& sdc_set_timing_derate) { if(sdc_set_timing_derate.is_early) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "-early should only be specified once.\n"); } sdc_set_timing_derate.is_early = true; } void sdc_set_timing_derate_late(Callback& callback, const Lexer& lexer, SetTimingDerate& sdc_set_timing_derate) { if(sdc_set_timing_derate.is_late) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "-late should only be specified once.\n"); } sdc_set_timing_derate.is_late = true; } void sdc_set_timing_derate_target_type(Callback& callback, const Lexer& lexer, SetTimingDerate& sdc_set_timing_derate, TimingDerateTargetType target_type) { if(target_type == TimingDerateTargetType::NET) { if(sdc_set_timing_derate.derate_nets) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "-net_delay should only be specified once.\n"); } else { sdc_set_timing_derate.derate_nets = true; } } else if (target_type == TimingDerateTargetType::CELL) { if(sdc_set_timing_derate.derate_cells) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "-cell_delay should only be specified once.\n"); } else { sdc_set_timing_derate.derate_cells = true; } } } void sdc_set_timing_derate_value(Callback& callback, const Lexer& lexer, SetTimingDerate& sdc_set_timing_derate, float value) { if(!std::isnan(sdc_set_timing_derate.value)) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single derate value per set_timing_derate command is allowed.\n"); } if(sdc_set_timing_derate.value < 0) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Timing derate values must be positive.\n"); } sdc_set_timing_derate.value = value; } void sdc_set_timing_derate_targets(Callback& callback, const Lexer& lexer, SetTimingDerate& sdc_set_timing_derate, StringGroup targets) { if(targets.type != StringGroupType::CELL) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only get_cells is supported with set_timing_derate.\n"); } if(!sdc_set_timing_derate.cell_targets.strings.empty()) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Only a single get_cells call is supported with set_timing_derate.\n"); } sdc_set_timing_derate.cell_targets = targets; } void add_sdc_set_timing_derate(Callback& callback, const Lexer& lexer, SetTimingDerate& sdc_set_timing_derate) { /* * Error checks */ if(std::isnan(sdc_set_timing_derate.value)) { sdc_error_wrap(callback, lexer.lineno(), lexer.text(), "Must specify timing derate value\n"); } /* * Defaults */ if(!sdc_set_timing_derate.derate_nets && !sdc_set_timing_derate.derate_cells) { //If unspecified, both cells and nets are derated sdc_set_timing_derate.derate_nets = true; sdc_set_timing_derate.derate_cells = true; } /* * Add command */ callback.set_timing_derate(sdc_set_timing_derate); } /* * Functions for string_group */ StringGroup make_sdc_string_group(StringGroupType type, const std::string& string) { //Convenience function for converting a single string into a group StringGroup sdc_string_group(type); sdc_string_group.strings.push_back(string); return sdc_string_group; } void sdc_string_group_add_string(StringGroup& sdc_string_group, const std::string& string) { //Insert the new string sdc_string_group.strings.push_back(string); } void sdc_string_group_add_strings(StringGroup& sdc_string_group, const StringGroup& string_group_to_add) { for(const auto& string : string_group_to_add.strings) { sdc_string_group_add_string(sdc_string_group, string); } } char* strdup(const char* src) { if(src == NULL) { return NULL; } size_t len = std::strlen(src); //Number of char's excluding null terminator //Space for [0..len] chars char* new_str = (char*) std::malloc((len+1)*sizeof(*src)); assert(new_str != NULL); //Copy chars from [0..len], i.e. src[len] should be null terminator std::memcpy(new_str, src, len+1); return new_str; } char* strndup(const char* src, size_t len) { if(src == NULL) { return NULL; } //Space for [0..len] chars char* new_str = (char*) std::malloc((len+1)*sizeof(*src)); assert(new_str != NULL); //Copy chars from [0..len-1] std::memcpy(new_str, src, len); //Add the null terminator new_str[len] = '\0'; return new_str; } } //namespace