| #include <cstring> |
| #include <cstdio> |
| #include <cstdlib> |
| #include <cstdarg> |
| #include <cassert> |
| #include <cmath> |
| #include <memory> |
| |
| #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 |