// OpenSTA, Static Timing Analyzer // Copyright (c) 2025, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . // // The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. // // Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // This notice may not be removed or altered from any source distribution. %{ #include #include "sdf/SdfReaderPvt.hh" #include "sdf/SdfScanner.hh" #undef yylex #define yylex scanner->lex // warning: variable 'yynerrs_' set but not used #pragma GCC diagnostic ignored "-Wunused-but-set-variable" void sta::SdfParse::error(const location_type &loc, const std::string &msg) { reader->report()->fileError(164,reader->filename().c_str(), loc.begin.line,"%s",msg.c_str()); } %} %require "3.2" %skeleton "lalr1.cc" %debug %define api.namespace {sta} %locations %define api.location.file "SdfLocation.hh" %define parse.assert %parse-param { SdfScanner *scanner } %parse-param { SdfReader *reader } %define api.parser.class {SdfParse} // expected shift/reduce conflicts %expect 4 %union { char character; std::string *string; float number; float *number_ptr; int integer; sta::SdfTriple *triple; sta::SdfTripleSeq *delval_list; sta::SdfPortSpec *port_spec; const sta::Transition *transition; } %token DELAYFILE SDFVERSION DESIGN DATE VENDOR PROGRAM PVERSION %token DIVIDER VOLTAGE PROCESS TEMPERATURE TIMESCALE %token CELL CELLTYPE INSTANCE DELAY ABSOLUTE INCREMENTAL %token INTERCONNECT PORT DEVICE RETAIN %token IOPATH TIMINGCHECK %token SETUP HOLD SETUPHOLD RECOVERY REMOVAL RECREM WIDTH PERIOD SKEW NOCHANGE %token POSEDGE NEGEDGE COND CONDELSE %token QSTRING ID FNUMBER DNUMBER EXPR_OPEN_IOPATH EXPR_OPEN EXPR_ID_CLOSE %type FNUMBER NUMBER %type DNUMBER %type number_opt %type value triple %type delval_list %type QSTRING ID path port port_instance %type EXPR_OPEN_IOPATH EXPR_OPEN EXPR_ID_CLOSE %type port_spec port_tchk %type port_transition %type hchar // Used by error recovery. %destructor { delete $$; } QSTRING %start file %% file: '(' DELAYFILE header cells ')' {} ; header: header_stmt | header header_stmt ; // technically the ordering of these statements is fixed by the spec header_stmt: '(' SDFVERSION QSTRING ')' { delete $3; } | '(' DESIGN QSTRING ')' { delete $3; } | '(' DATE QSTRING ')' { delete $3; } | '(' VENDOR QSTRING ')' { delete $3; } | '(' PROGRAM QSTRING ')' { delete $3; } | '(' PVERSION QSTRING ')' { delete $3; } | '(' DIVIDER hchar ')' { reader->setDivider($3); } | '(' VOLTAGE triple ')' { reader->deleteTriple($3); } | '(' VOLTAGE NUMBER ')' | '(' VOLTAGE ')' // Illegal SDF (from OC). | '(' PROCESS QSTRING ')' { delete $3; } | '(' PROCESS ')' // Illegal SDF (from OC). | '(' TEMPERATURE NUMBER ')' | '(' TEMPERATURE triple ')' { reader->deleteTriple($3); } | '(' TEMPERATURE ')' // Illegal SDF (from OC). | '(' TIMESCALE NUMBER ID ')' { reader->setTimescale($3, $4); } ; hchar: '/' { $$ = '/'; } | '.' { $$ = '.'; } ; number_opt: { $$ = nullptr; } | NUMBER { $$ = new float($1); } ; cells: cell | cells cell ; cell: '(' CELL celltype cell_instance timing_specs ')' { reader->cellFinish(); } ; celltype: '(' CELLTYPE QSTRING ')' { reader->setCell($3); } ; cell_instance: '(' INSTANCE ')' { reader->setInstance(nullptr); } | '(' INSTANCE '*' ')' { reader->setInstanceWildcard(); } | '(' INSTANCE path ')' { reader->setInstance($3); } ; timing_specs: /* empty */ | timing_specs timing_spec ; timing_spec: '(' DELAY deltypes ')' | '(' TIMINGCHECK tchk_defs ')' ; deltypes: | deltypes deltype ; deltype: '(' ABSOLUTE { reader->setInIncremental(false); } del_defs ')' | '(' INCREMENTAL { reader->setInIncremental(true); } del_defs ')' ; del_defs: | del_defs del_def ; path: ID { $$ = reader->unescaped($1); } | path hchar ID { $$ = reader->makePath($1, reader->unescaped($3)); } ; del_def: '(' IOPATH port_spec port_instance retains delval_list ')' { reader->iopath($3, $4, $6, nullptr, false); } | '(' CONDELSE '(' IOPATH port_spec port_instance retains delval_list ')' ')' { reader->iopath($5, $6, $8, nullptr, true); } | '(' COND EXPR_OPEN_IOPATH port_spec port_instance retains delval_list ')' ')' { reader->iopath($4, $5, $7, $3, false); } | '(' INTERCONNECT port_instance port_instance delval_list ')' { reader->interconnect($3, $4, $5); } | '(' PORT port_instance delval_list ')' { reader->port($3, $4); } | '(' DEVICE delval_list ')' { reader->device($3); } | '(' DEVICE port_instance delval_list ')' { reader->device($3, $4); } ; retains: /* empty */ | retains retain ; retain: '(' RETAIN delval_list ')' { reader->deleteTripleSeq($3); } ; delval_list: value { $$ = reader->makeTripleSeq(); $$->push_back($1); } | delval_list value { $1->push_back($2); $$ = $1; } ; tchk_defs: | tchk_defs tchk_def ; tchk_def: '(' SETUP { reader->setInTimingCheck(true); } port_tchk port_tchk value ')' { reader->timingCheck(sta::TimingRole::setup(), $4, $5, $6); reader->setInTimingCheck(false); } | '(' HOLD { reader->setInTimingCheck(true); } port_tchk port_tchk value ')' { reader->timingCheck(sta::TimingRole::hold(), $4, $5, $6); reader->setInTimingCheck(false); } | '(' SETUPHOLD { reader->setInTimingCheck(true); } port_tchk port_tchk value value ')' { reader->timingCheckSetupHold($4, $5, $6, $7); reader->setInTimingCheck(false); } | '(' RECOVERY { reader->setInTimingCheck(true); } port_tchk port_tchk value ')' { reader->timingCheck(sta::TimingRole::recovery(),$4,$5,$6); reader->setInTimingCheck(false); } | '(' REMOVAL { reader->setInTimingCheck(true); } port_tchk port_tchk value ')' { reader->timingCheck(sta::TimingRole::removal(),$4,$5,$6); reader->setInTimingCheck(false); } | '(' RECREM { reader->setInTimingCheck(true); } port_tchk port_tchk value value ')' { reader->timingCheckRecRem($4, $5, $6, $7); reader->setInTimingCheck(false); } | '(' SKEW { reader->setInTimingCheck(true); } port_tchk port_tchk value ')' // Sdf skew clk/ref are reversed from liberty. { reader->timingCheck(sta::TimingRole::skew(),$5,$4,$6); reader->setInTimingCheck(false); } | '(' WIDTH { reader->setInTimingCheck(true); } port_tchk value ')' { reader->timingCheckWidth($4, $5); reader->setInTimingCheck(false); } | '(' PERIOD { reader->setInTimingCheck(true); } port_tchk value ')' { reader->timingCheckPeriod($4, $5); reader->setInTimingCheck(false); } | '(' NOCHANGE { reader->setInTimingCheck(true); } port_tchk port_tchk value value ')' { reader->timingCheckNochange($4, $5, $6, $7); reader->setInTimingCheck(false); } ; port: ID { $$ = reader->unescaped($1); } | ID '[' DNUMBER ']' { $$ = reader->makeBusName($1, $3); } ; port_instance: port | path hchar port { $$ = reader->makePath($1, $3); } ; port_spec: port_instance { $$=reader->makePortSpec(sta::Transition::riseFall(),$1,nullptr); } | '(' port_transition port_instance ')' { $$ = reader->makePortSpec($2, $3, nullptr); } ; port_transition: POSEDGE { $$ = sta::Transition::rise(); } | NEGEDGE { $$ = sta::Transition::fall(); } ; port_tchk: port_spec | '(' COND EXPR_ID_CLOSE { $$ = reader->makeCondPortSpec($3); } | '(' COND EXPR_OPEN port_transition port_instance ')' ')' { $$ = reader->makePortSpec($4, $5, $3); } ; value: '(' ')' { $$ = reader->makeTriple(); } | '(' NUMBER ')' { $$ = reader->makeTriple($2); } | '(' triple ')' { $$ = $2; } ; triple: NUMBER ':' number_opt ':' number_opt { float *fp = new float($1); $$ = reader->makeTriple(fp, $3, $5); } | number_opt ':' NUMBER ':' number_opt { float *fp = new float($3); $$ = reader->makeTriple($1, fp, $5); } | number_opt ':' number_opt ':' NUMBER { float *fp = new float($5); $$ = reader->makeTriple($1, $3, fp); } ; NUMBER: FNUMBER | DNUMBER { $$ = static_cast($1); } | '-' DNUMBER { $$ = static_cast(-$2); } ; %%