From 4035a6cb46e2205a01e945a73398e4adb5f99f33 Mon Sep 17 00:00:00 2001 From: Gabriel Gouvine Date: Sun, 12 Apr 2015 16:24:29 +0200 Subject: [PATCH 1/4] Supports several models in a Blif file --- crlcore/src/ccore/blif/BlifParser.cpp | 35 +++++++++++++++++---------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/crlcore/src/ccore/blif/BlifParser.cpp b/crlcore/src/ccore/blif/BlifParser.cpp index f130fd52..39304629 100644 --- a/crlcore/src/ccore/blif/BlifParser.cpp +++ b/crlcore/src/ccore/blif/BlifParser.cpp @@ -228,6 +228,7 @@ Cell * Blif::load ( string cellPath ) //, Cell *cell ) throw Error("Unexpected token after model name\n"); else{ models.back().name = token; + cmess2 << "Processing model <" << token << ">" << endl; hasName = true; } } @@ -255,13 +256,11 @@ Cell * Blif::load ( string cellPath ) //, Cell *cell ) } */ - if(models.size() > 1){ - cerr << Warning("Several models in the file; only the last was open\n"); - } - - Cell * design = NULL; + // Two passes: first create the cells and their nets, then create the internals + std::vector model_cells; for(auto M : models){ - design = framework->createCell(M.name); + Cell * design = framework->createCell(M.name); + model_cells.push_back(design); addSupplyNets(design); unordered_set net_names; @@ -282,14 +281,18 @@ Cell * Blif::load ( string cellPath ) //, Cell *cell ) new_net->setDirection( it->second ); } } - - int i=0; - for(auto & S : M.subcircuits){ + } + // Second pass: every cell and its nets have already been created + for(int i=0; igetCell(S.cell, Catalog::State::Views, 0); if(cell == NULL){ - cerr << Warning("Cell " + S.cell + "to instanciate hasn't been found\n"); + cerr << Error("Cell " + S.cell + " to instanciate hasn't been found\n"); continue; } Instance* instance = Instance::create( design, subckt_name.str(), cell); @@ -304,8 +307,14 @@ Cell * Blif::load ( string cellPath ) //, Cell *cell ) ++i; } } - - return design; + cmess2 << "BLIF file loaded" << endl; + if(model_cells.empty()){ + throw Error("No model found in the file\n"); + } + else if(model_cells.size() > 1){ + cerr << Warning("Several models found: returned the first one\n"); + } + return model_cells[0]; } } From 7d5338c9bec761f91f136d736242d5656f72b169 Mon Sep 17 00:00:00 2001 From: Gabriel Gouvine Date: Mon, 13 Apr 2015 14:13:03 +0200 Subject: [PATCH 2/4] Support for aliases defined with .names construct --- crlcore/src/ccore/blif/BlifParser.cpp | 130 +++++++++++++++++--------- 1 file changed, 86 insertions(+), 44 deletions(-) diff --git a/crlcore/src/ccore/blif/BlifParser.cpp b/crlcore/src/ccore/blif/BlifParser.cpp index 39304629..8e3b7b20 100644 --- a/crlcore/src/ccore/blif/BlifParser.cpp +++ b/crlcore/src/ccore/blif/BlifParser.cpp @@ -105,6 +105,7 @@ namespace { string name; unordered_map pins; vector subcircuits; + vector > aliases; bool operator<(model const & o) const{ return name < o.name; } }; @@ -136,15 +137,36 @@ Cell * Blif::load ( string cellPath ) //, Cell *cell ) bool hasName; string line; + vector current_names; while(getline(ccell, line)){ istringstream linestream(line); string before_comment; getline(linestream, before_comment, '#'); istringstream tokens(before_comment); string token; - while(tokens>>token){ + if(tokens >> token){ assert(not token.empty()); if(token[0] == '.'){ + // Process finished .names statement + if(not current_names.empty()){ + if( current_names.size() != 4 + or current_names[2] != "1" or current_names[3] != "1" + ){ + ostringstream mess; + mess << "Name statement is not an alias and will be ignored " + << "(map to standard cells for functions and tie cells for constants)\n"; + for(string const & S: current_names){ + mess << S << " "; + } + mess << "\n"; + cerr << Warning(mess.str()); + } + else{ + // Name statement is an alias: the second signal will map to the first + models.back().aliases.push_back(pair(current_names[0], current_names[1])); + } + current_names.clear(); + } if(token == ".model"){ if(state != EXT) throw Error("Nested model are not supported\n"); @@ -162,13 +184,11 @@ Cell * Blif::load ( string cellPath ) //, Cell *cell ) models.back().subcircuits.push_back(subckt()); } else if(token == ".names"){ - cerr << Warning("BLIF names are ignored\n"); if(state == EXT) throw Error("Names without an enclosing model are not supported\n"); if(state == MODEL and not hasName) throw Error("Model has no name\n"); state = NAMES; - hasName = false; } else if(token == ".latch"){ throw Error("Latch constructs are not understood by the parser\n"); @@ -189,53 +209,68 @@ Cell * Blif::load ( string cellPath ) //, Cell *cell ) state = EXT; } else{ - throw Error("Unexpected control token\n"); + ostringstream mess; + mess << "Unknown control token <" << token << ">\n"; + throw Error(mess.str()); } + } - else{ // Either a pin or an input/output definition - if(state == INPUTS or state == OUTPUTS){ - auto it = models.back().pins.find(token); - Net::Direction D = (state == INPUTS)? Net::Direction::DirIn : Net::Direction::DirOut; - if(it != models.back().pins.end()){ - it->second = static_cast(D | it->second); - } - else{ - models.back().pins.insert(pair(token, D)); - } - } - else if(state == SUBCKT){ - if(hasName){ - // Encountered a pin: need to be processed - istringstream token_stream(token); - string before_space, after_space; - getline(token_stream, before_space, '='); - getline(token_stream, after_space, '='); - if(token_stream){ - Error("Encountered more than one '=' in token"); - } - models.back().subcircuits.back().pins.push_back(pair(before_space, after_space)); - } - else{ - models.back().subcircuits.back().cell = token; - hasName = true; - } - } - else if(state == NAMES){ - // TODO; now just ignored - } - else if(state == MODEL){ - if(hasName) - throw Error("Unexpected token after model name\n"); - else{ - models.back().name = token; - cmess2 << "Processing model <" << token << ">" << endl; - hasName = true; - } + else if(state == NAMES){ + // Part of a cover for a logic function + current_names.push_back(token); + } + else{ + ostringstream mess; + mess << "Encountered a non-control token at the beginning of a line: <" << token << ">\n"; + throw Error(mess.str()); + } + } + // Process all tokens after the control + while(tokens >> token){ + assert(not token.empty()); + // Either a pin or an input/output definition + if(state == INPUTS or state == OUTPUTS){ + auto it = models.back().pins.find(token); + Net::Direction D = (state == INPUTS)? Net::Direction::DirIn : Net::Direction::DirOut; + if(it != models.back().pins.end()){ + it->second = static_cast(D | it->second); } else{ - throw Error("Unexpected token\n"); + models.back().pins.insert(pair(token, D)); } } + else if(state == SUBCKT){ + if(hasName){ + // Encountered a pin: need to be processed + istringstream token_stream(token); + string before_space, after_space; + getline(token_stream, before_space, '='); + getline(token_stream, after_space, '='); + if(token_stream){ + Error("Encountered more than one '=' in token"); + } + models.back().subcircuits.back().pins.push_back(pair(before_space, after_space)); + } + else{ + models.back().subcircuits.back().cell = token; + hasName = true; + } + } + else if(state == NAMES){ + current_names.push_back(token); + } + else if(state == MODEL){ + if(hasName) + throw Error("Unexpected token after model name\n"); + else{ + models.back().name = token; + cmess2 << "Processing model <" << token << ">" << endl; + hasName = true; + } + } + else{ + throw Error("Unexpected token\n"); + } } line.clear(); } @@ -268,6 +303,10 @@ Cell * Blif::load ( string cellPath ) //, Cell *cell ) for(auto const & P : S.pins){ net_names.insert(P.second); } + for(auto const & A : M.aliases){ + net_names.insert(A.first); + net_names.insert(A.second); + } } for(auto const & P : M.pins){ net_names.insert(P.first); @@ -306,6 +345,9 @@ Cell * Blif::load ( string cellPath ) //, Cell *cell ) //connectSupplyNets(instance, design); ++i; } + for(auto alias : M.aliases){ + design->getNet( alias.first )->merge(design->getNet( alias.second )); + } } cmess2 << "BLIF file loaded" << endl; if(model_cells.empty()){ From d7f9e5cb8737d3b5bfc13c702a806f7f7c00f428 Mon Sep 17 00:00:00 2001 From: Gabriel Gouvine Date: Mon, 13 Apr 2015 16:49:06 +0200 Subject: [PATCH 3/4] Handling aliases of non-instantiated nets For example $true and $false in Yosys --- crlcore/src/ccore/blif/BlifParser.cpp | 52 ++++++++++++++++++--------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/crlcore/src/ccore/blif/BlifParser.cpp b/crlcore/src/ccore/blif/BlifParser.cpp index 8e3b7b20..b2c3840f 100644 --- a/crlcore/src/ccore/blif/BlifParser.cpp +++ b/crlcore/src/ccore/blif/BlifParser.cpp @@ -153,7 +153,7 @@ Cell * Blif::load ( string cellPath ) //, Cell *cell ) or current_names[2] != "1" or current_names[3] != "1" ){ ostringstream mess; - mess << "Name statement is not an alias and will be ignored " + mess << ".names statement is not an alias and will be ignored " << "(map to standard cells for functions and tie cells for constants)\n"; for(string const & S: current_names){ mess << S << " "; @@ -295,34 +295,38 @@ Cell * Blif::load ( string cellPath ) //, Cell *cell ) std::vector model_cells; for(auto M : models){ Cell * design = framework->createCell(M.name); + if(design == NULL){ + throw Error("Model " + M.name + " is NULL\n"); + } model_cells.push_back(design); addSupplyNets(design); unordered_set net_names; for(auto const & S : M.subcircuits){ for(auto const & P : S.pins){ - net_names.insert(P.second); + net_names.insert(P.second); } for(auto const & A : M.aliases){ - net_names.insert(A.first); - net_names.insert(A.second); + net_names.insert(A.first); + net_names.insert(A.second); } } for(auto const & P : M.pins){ - net_names.insert(P.first); + net_names.insert(P.first); } for(string const & N : net_names){ - Net* new_net = Net::create( design, N ); - auto it = M.pins.find(N); - if(it != M.pins.end()){ - new_net->setExternal( true ); - new_net->setDirection( it->second ); - } + Net* new_net = Net::create( design, N ); + auto it = M.pins.find(N); + if(it != M.pins.end()){ + new_net->setExternal( true ); + new_net->setDirection( it->second ); + } } } // Second pass: every cell and its nets have already been created for(int i=0; igetCell(S.cell, Catalog::State::Views, 0); if(cell == NULL){ - cerr << Error("Cell " + S.cell + " to instanciate hasn't been found\n"); - continue; + throw Error("Cell <" + S.cell + "> to instanciate hasn't been found\n"); } + //cmess2 << "Creating instance <" << subckt_name.str() << "> of <" << S.cell << "> in <" << models[i].name << ">" << endl; Instance* instance = Instance::create( design, subckt_name.str(), cell); for(auto & P : S.pins){ Net* internalNet = cell->getNet( P.first ); Net* externalNet = design->getNet( P.second ); - assert(internalNet != NULL and externalNet != NULL); + assert(internalNet != NULL and externalNet != NULL and instance != NULL); instance->getPlug( internalNet )->setNet( externalNet ); } //connectSupplyNets(instance, design); - ++i; } + // Merge the aliased nets for(auto alias : M.aliases){ - design->getNet( alias.first )->merge(design->getNet( alias.second )); + //cmess2 << "Merging nets <" << alias.first << "> and <" << alias.second << ">" << endl; + Net * first_net = design->getNet( alias.first ); + Net * second_net = design->getNet( alias.second ); + if(first_net == NULL or second_net == NULL){ + ostringstream mess; + mess << "Trying to create an alias for non-instantiated nets:"; + if(first_net == NULL) + mess << " <" << alias.first << ">"; + if(second_net == NULL) + mess << " <" << alias.second << ">"; + mess << ", will be ignored\n"; + cerr << Warning(mess.str()); + continue; + } + if(!first_net->isExternal()) + swap(first_net, second_net); // If only one net is external, it needs to be the first + first_net->merge(second_net); } } cmess2 << "BLIF file loaded" << endl; From 575b6c923a897f364c5863d61d9547d8923187d6 Mon Sep 17 00:00:00 2001 From: Gabriel Gouvine Date: Mon, 13 Apr 2015 17:38:51 +0200 Subject: [PATCH 4/4] Performance improvements for the BLIF parser Got rid of streams --- crlcore/src/ccore/blif/BlifParser.cpp | 55 ++++++++++++++++++++------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/crlcore/src/ccore/blif/BlifParser.cpp b/crlcore/src/ccore/blif/BlifParser.cpp index b2c3840f..96512d35 100644 --- a/crlcore/src/ccore/blif/BlifParser.cpp +++ b/crlcore/src/ccore/blif/BlifParser.cpp @@ -109,12 +109,34 @@ namespace { bool operator<(model const & o) const{ return name < o.name; } }; + std::vector tokenize(std::string const & s){ + std::vector ret; + auto b_it = s.begin(); + while(b_it != s.end()){ + // Remove whitespace + while(b_it != s.end() && (*b_it == ' ' or *b_it == '\t')) + ++b_it; + // Find the end of the token + auto e_it = b_it; + while(e_it != s.end() && *e_it != ' ' && *e_it != '\t' and *e_it != '#') + ++e_it; + // Create the token + if(e_it != b_it) + ret.push_back(std::string(b_it, e_it)); + // Handle comments + if(e_it != s.end() && *e_it == '#') + e_it = s.end(); + b_it = e_it; + } + std::reverse(ret.begin(), ret.end()); + return ret; + } + } // End of anonymous namespace. namespace CRL { - // // Can only parse simple, netlist BLIF files generated by Yosys // Ignores all ".names" and uses only the .subckt, .model, .input and .output @@ -140,11 +162,10 @@ Cell * Blif::load ( string cellPath ) //, Cell *cell ) vector current_names; while(getline(ccell, line)){ istringstream linestream(line); - string before_comment; - getline(linestream, before_comment, '#'); - istringstream tokens(before_comment); - string token; - if(tokens >> token){ + vector tokens = tokenize(line); + if(not tokens.empty()){ + string const token = tokens.back(); + tokens.pop_back(); assert(not token.empty()); if(token[0] == '.'){ // Process finished .names statement @@ -226,7 +247,9 @@ Cell * Blif::load ( string cellPath ) //, Cell *cell ) } } // Process all tokens after the control - while(tokens >> token){ + while(not tokens.empty()){ + string const token = tokens.back(); + tokens.pop_back(); assert(not token.empty()); // Either a pin or an input/output definition if(state == INPUTS or state == OUTPUTS){ @@ -242,14 +265,18 @@ Cell * Blif::load ( string cellPath ) //, Cell *cell ) else if(state == SUBCKT){ if(hasName){ // Encountered a pin: need to be processed - istringstream token_stream(token); - string before_space, after_space; - getline(token_stream, before_space, '='); - getline(token_stream, after_space, '='); - if(token_stream){ - Error("Encountered more than one '=' in token"); + auto equal_pos = token.find_first_of('='); + if(equal_pos+1 < token.size()){ + string before_space = token.substr(0, equal_pos); + string after_space = token.substr(equal_pos+1, string::npos); + models.back().subcircuits.back().pins.push_back(pair(before_space, after_space)); + } + else{ + ostringstream mess; + mess << "Unable to parse the subckt pin specification <" + << token << ">\n"; + Error(mess.str()); } - models.back().subcircuits.back().pins.push_back(pair(before_space, after_space)); } else{ models.back().subcircuits.back().cell = token;