Merge branch 'github/Blif', fast blif parser.
This commit is contained in:
commit
2cbb072e6c
|
@ -105,15 +105,38 @@ namespace {
|
|||
string name;
|
||||
unordered_map<string, Net::Direction> pins;
|
||||
vector<subckt> subcircuits;
|
||||
vector<pair<string, string> > aliases;
|
||||
bool operator<(model const & o) const{ return name < o.name; }
|
||||
};
|
||||
|
||||
std::vector<std::string> tokenize(std::string const & s){
|
||||
std::vector<std::string> 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
|
||||
|
@ -136,15 +159,35 @@ Cell * Blif::load ( string cellPath ) //, Cell *cell )
|
|||
bool hasName;
|
||||
|
||||
string line;
|
||||
vector<string> 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){
|
||||
vector<string> 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
|
||||
if(not current_names.empty()){
|
||||
if( current_names.size() != 4
|
||||
or current_names[2] != "1" or current_names[3] != "1"
|
||||
){
|
||||
ostringstream mess;
|
||||
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 << " ";
|
||||
}
|
||||
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<string, string>(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 +205,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,52 +230,74 @@ 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 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(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){
|
||||
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<Net::Direction::Code>(D | it->second);
|
||||
}
|
||||
else{
|
||||
models.back().pins.insert(pair<string, Net::Direction>(token, D));
|
||||
}
|
||||
}
|
||||
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<Net::Direction::Code>(D | it->second);
|
||||
}
|
||||
else{
|
||||
models.back().pins.insert(pair<string, Net::Direction>(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");
|
||||
}
|
||||
else if(state == SUBCKT){
|
||||
if(hasName){
|
||||
// Encountered a pin: need to be processed
|
||||
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<string, string>(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;
|
||||
hasName = true;
|
||||
ostringstream mess;
|
||||
mess << "Unable to parse the subckt pin specification <"
|
||||
<< token << ">\n";
|
||||
Error(mess.str());
|
||||
}
|
||||
}
|
||||
else{
|
||||
throw Error("Unexpected token\n");
|
||||
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();
|
||||
}
|
||||
|
@ -255,57 +318,92 @@ 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<Cell*> model_cells;
|
||||
for(auto M : models){
|
||||
design = framework->createCell(M.name);
|
||||
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<string> 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);
|
||||
}
|
||||
}
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
int i=0;
|
||||
for(auto & S : M.subcircuits){
|
||||
}
|
||||
// Second pass: every cell and its nets have already been created
|
||||
for(int i=0; i<models.size(); ++i){
|
||||
cmess2 << "Handling model " << models[i].name << endl;
|
||||
auto const & M = models[i];
|
||||
Cell * design = model_cells[i];
|
||||
for(int j=0; j<M.subcircuits.size(); ++j){
|
||||
auto & S = M.subcircuits[j];
|
||||
ostringstream subckt_name;
|
||||
subckt_name << "subckt_" << i;
|
||||
subckt_name << "subckt_" << j;
|
||||
Cell * cell = framework->getCell(S.cell, Catalog::State::Views, 0);
|
||||
if(cell == NULL){
|
||||
cerr << Warning("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){
|
||||
//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);
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue