| /* |
| Permission is hereby granted, free of charge, to any person |
| obtaining a copy of this software and associated documentation |
| files (the "Software"), to deal in the Software without |
| restriction, including without limitation the rights to use, |
| copy, modify, merge, publish, distribute, sublicense, and/or sell |
| copies of the Software, and to permit persons to whom the |
| Software is furnished to do so, subject to the following |
| conditions: |
| |
| The above copyright notice and this permission notice shall be |
| included in all copies or substantial portions of the Software. |
| |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
| OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
| HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
| WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
| FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
| OTHER DEALINGS IN THE SOFTWARE. |
| */ |
| |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdio.h> |
| #include "odin_globals.h" |
| #include "odin_util.h" |
| #include "read_blif.h" |
| #include "string_cache.h" |
| |
| #include "netlist_utils.h" |
| #include "odin_types.h" |
| #include "Hashtable.hpp" |
| #include "netlist_check.h" |
| #include "node_creation_library.h" |
| #include "simulate_blif.h" |
| #include "vtr_util.h" |
| #include "vtr_memory.h" |
| |
| #define TOKENS " \t\n" |
| #define GND_NAME "gnd" |
| #define VCC_NAME "vcc" |
| #define HBPAD_NAME "unconn" |
| |
| #define READ_BLIF_BUFFER 1048576 // 1MB |
| |
| long file_line_number; |
| int line_count; |
| |
| // Stores pin names of the form port[pin] |
| struct hard_block_pins{ |
| int count; |
| char **names; |
| // Maps name to index. |
| Hashtable *index; |
| }; |
| |
| // Stores port names, and their sizes. |
| struct hard_block_ports{ |
| char *signature; |
| int count; |
| int *sizes; |
| char **names; |
| // Maps portname to index. |
| Hashtable *index; |
| }; |
| |
| // Stores all information pertaining to a hard block model. (.model) |
| struct hard_block_model{ |
| char *name; |
| |
| hard_block_pins *inputs; |
| hard_block_pins *outputs; |
| |
| hard_block_ports *input_ports; |
| hard_block_ports *output_ports; |
| }; |
| |
| // A cache structure for models. |
| struct hard_block_models{ |
| hard_block_model **models; |
| int count; |
| // Maps name to model |
| Hashtable *index; |
| }; |
| |
| |
| netlist_t * blif_netlist; |
| bool static skip_reading_bit_map=false; |
| bool insert_global_clock; |
| |
| |
| void rb_create_top_driver_nets(const char *instance_name_prefix, Hashtable *output_nets_hash); |
| void rb_look_for_clocks();// not sure if this is needed |
| void add_top_input_nodes(FILE *file, Hashtable *output_nets_hash); |
| void rb_create_top_output_nodes(FILE *file); |
| int read_tokens (char *buffer, hard_block_models *models, FILE *file, Hashtable *output_nets_hash); |
| static void dum_parse (char *buffer, FILE *file); |
| void create_internal_node_and_driver(FILE *file, Hashtable *output_nets_hash); |
| operation_list assign_node_type_from_node_name(char * output_name);// function will decide the node->type of the given node |
| operation_list read_bit_map_find_unknown_gate(int input_count, nnode_t * node, FILE *file); |
| void create_latch_node_and_driver(FILE *file, Hashtable *output_nets_hash); |
| void create_hard_block_nodes(hard_block_models *models, FILE *file, Hashtable *output_nets_hash); |
| void hook_up_nets(Hashtable *output_nets_hash); |
| void hook_up_node(nnode_t *node, Hashtable *output_nets_hash); |
| char* search_clock_name(FILE *file); |
| void free_hard_block_model(hard_block_model *model); |
| char *get_hard_block_port_name(char *name); |
| long get_hard_block_pin_number(char *original_name); |
| static int compare_hard_block_pin_names(const void *p1, const void *p2); |
| hard_block_ports *get_hard_block_ports(char **pins, int count); |
| Hashtable *index_names(char **names, int count); |
| Hashtable *associate_names(char **names1, char **names2, int count); |
| void free_hard_block_pins(hard_block_pins *p); |
| void free_hard_block_ports(hard_block_ports *p); |
| |
| |
| hard_block_model *get_hard_block_model(char *name, hard_block_ports *ports, hard_block_models *models); |
| void add_hard_block_model(hard_block_model *m, hard_block_ports *ports, hard_block_models *models); |
| char *generate_hard_block_ports_signature(hard_block_ports *ports); |
| int verify_hard_block_ports_against_model(hard_block_ports *ports, hard_block_model *model); |
| hard_block_model *read_hard_block_model(char *name_subckt, hard_block_ports *ports, FILE *file); |
| |
| |
| void free_hard_block_models(hard_block_models *models); |
| |
| hard_block_models *create_hard_block_models(); |
| |
| int count_blif_lines(FILE *file); |
| |
| /* |
| * Reads a blif file with the given filename and produces |
| * a netlist which is referred to by the global variable |
| * "blif_netlist". |
| */ |
| netlist_t *read_blif() |
| { |
| insert_global_clock = true; |
| current_parse_file = 0; |
| blif_netlist = allocate_netlist(); |
| /*Opening the blif file */ |
| FILE *file = vtr::fopen (configuration.list_of_file_names[current_parse_file].c_str(), "r"); |
| if (file == NULL) |
| { |
| error_message(ARG_ERROR, -1, current_parse_file, "cannot open file: %s\n", configuration.list_of_file_names[current_parse_file].c_str()); |
| } |
| int num_lines = count_blif_lines(file); |
| |
| Hashtable *output_nets_hash = new Hashtable(); |
| |
| printf("Reading top level module\n"); fflush(stdout); |
| /* create the top level module */ |
| rb_create_top_driver_nets("top", output_nets_hash); |
| |
| /* Extracting the netlist by reading the blif file */ |
| printf("Reading blif netlist..."); fflush(stdout); |
| |
| file_line_number = 0; |
| line_count = 0; |
| int position = -1; |
| double time = wall_time(); |
| // A cache of hard block models indexed by name. As each one is read, it's stored here to be used again. |
| hard_block_models *models = create_hard_block_models(); |
| printf("\n"); |
| char buffer[READ_BLIF_BUFFER]; |
| while (vtr::fgets(buffer, READ_BLIF_BUFFER, file) && read_tokens(buffer, models, file, output_nets_hash)) |
| { // Print a progress bar indicating completeness. |
| position = print_progress_bar((++line_count)/(double)num_lines, position, 50, wall_time() - time); |
| } |
| free_hard_block_models(models); |
| /* Now look for high-level signals */ |
| rb_look_for_clocks(); |
| // We the estimate of completion is rough...make sure we end up at 100%. ;) |
| print_progress_bar(1.0, position, 50, wall_time() - time); |
| printf("-------------------------------------\n"); fflush(stdout); |
| |
| // Outputs netlist graph. |
| check_netlist(blif_netlist); |
| delete output_nets_hash; |
| fclose (file); |
| return blif_netlist; |
| } |
| |
| |
| |
| /*--------------------------------------------------------------------------------------------- |
| * (function: read_tokens) |
| * |
| * Parses the given line from the blif file. Returns true if there are more lines |
| * to read. |
| *-------------------------------------------------------------------------------------------*/ |
| int read_tokens (char *buffer, hard_block_models *models, FILE *file, Hashtable *output_nets_hash) |
| { |
| /* Figures out which, if any token is at the start of this line and * |
| * takes the appropriate action. */ |
| char *token = vtr::strtok (buffer, TOKENS, file, buffer); |
| |
| if (token) |
| { |
| if(skip_reading_bit_map && ((token[0] == '0') || (token[0] == '1') || (token[0] == '-'))) |
| { |
| dum_parse(buffer, file); |
| } |
| else |
| { |
| skip_reading_bit_map= false; |
| if (strcmp (token, ".inputs") == 0) |
| { |
| add_top_input_nodes(file, output_nets_hash);// create the top input nodes |
| } |
| else if (strcmp (token, ".outputs") == 0) |
| { |
| rb_create_top_output_nodes(file);// create the top output nodes |
| } |
| else if (strcmp (token, ".names") == 0) |
| { |
| create_internal_node_and_driver(file, output_nets_hash); |
| } |
| else if (strcmp(token,".latch") == 0) |
| { |
| create_latch_node_and_driver(file, output_nets_hash); |
| } |
| else if (strcmp(token,".subckt") == 0) |
| { |
| create_hard_block_nodes(models, file, output_nets_hash); |
| } |
| else if (strcmp(token,".end")==0) |
| { |
| // Marks the end of the main module of the blif |
| // Call function to hook up the nets |
| hook_up_nets(output_nets_hash); |
| return false; |
| } |
| else if (strcmp(token,".model")==0) |
| { |
| // Ignore models. |
| dum_parse(buffer, file); |
| } |
| } |
| } |
| return true; |
| } |
| |
| |
| /*--------------------------------------------------------------------------------------------- |
| * function:assign_node_type_from_node_name(char *) |
| This function tries to assign the node->type by looking at the name |
| Else return GENERIC |
| *-------------------------------------------------------------------------------------------*/ |
| operation_list assign_node_type_from_node_name(char * output_name) |
| { |
| //variable to extract the type |
| operation_list result = GENERIC; |
| |
| int start, end; |
| int length_string = strlen(output_name); |
| for(start = length_string-1; (start >= 0) && (output_name[start] != '^'); start--); |
| for(end = length_string-1; (end >= 0) && (output_name[end] != '~'); end-- ); |
| |
| if((start < end) && (end > 0)) |
| { |
| // Stores the extracted string |
| char *extracted_string = (char*)vtr::calloc(end-start+2, sizeof(char)); |
| int i, j; |
| for(i = start + 1, j = 0; i < end; i++, j++) |
| { |
| extracted_string[j] = output_name[i]; |
| } |
| |
| extracted_string[j]='\0'; |
| for(i=0; i<operation_list_END; i++) |
| { |
| if(!strcmp(extracted_string,operation_list_STR[i][ODIN_LONG_STRING]) |
| || !strcmp(extracted_string,operation_list_STR[i][ODIN_SHORT_STRING])) |
| { |
| result = static_cast<operation_list>(i); |
| break; |
| } |
| } |
| |
| vtr::free(extracted_string); |
| } |
| return result; |
| } |
| |
| /*--------------------------------------------------------------------------------------------- |
| * function:create_latch_node_and_driver |
| to create an ff node and driver from that node |
| format .latch <input> <output> [<type> <control/clock>] <initial val> |
| *-------------------------------------------------------------------------------------------*/ |
| void create_latch_node_and_driver(FILE *file, Hashtable *output_nets_hash) |
| { |
| /* Storing the names of the input and the final output in array names */ |
| char ** names = NULL; // Store the names of the tokens |
| int input_token_count = 0; /*to keep track whether controlling clock is specified or not */ |
| /*input_token_count=3 it is not and =5 it is */ |
| char *ptr = NULL; |
| |
| char buffer[READ_BLIF_BUFFER]; |
| while ((ptr = vtr::strtok (NULL, TOKENS, file, buffer)) != NULL) |
| { |
| input_token_count += 1; |
| names = (char**)vtr::realloc(names, (sizeof(char*))* (input_token_count)); |
| |
| names[input_token_count-1] = vtr::strdup(ptr); |
| } |
| |
| /* assigning the new_node */ |
| if(input_token_count != 5) |
| { |
| /* supported added for the ABC .latch output without control */ |
| if(input_token_count == 3) |
| { |
| input_token_count = 5; |
| names = (char**)vtr::realloc(names, sizeof(char*) * input_token_count); |
| |
| names[3] = search_clock_name(file); |
| names[4] = names[2]; |
| names[2] = vtr::strdup("re"); |
| } |
| else |
| { |
| std::string line = ""; |
| for(int i=0; i< input_token_count; i++) |
| { |
| line += names[i]; |
| line += " "; |
| } |
| |
| error_message(NETLIST_ERROR,file_line_number,current_parse_file, "This .latch Format not supported: <%s> \n\t required format :.latch <input> <output> [<type> <control/clock>] <initial val>", |
| line.c_str()); |
| } |
| } |
| |
| nnode_t *new_node = allocate_nnode(); |
| new_node->related_ast_node = NULL; |
| new_node->type = FF_NODE; |
| new_node->edge_type = edge_type_blif_enum(names[2]); |
| |
| /* Read in the initial value of the latch. |
| Possible values from a blif file are: |
| 0: LOW |
| 1: HIGH |
| 2: DON'T CARE |
| 3: UNKNOWN |
| |
| 2 and 3 are treated in the same way */ |
| int initial_value = atoi(names[4]); |
| if(initial_value == 0 || initial_value == 1){ |
| new_node->initial_value = initial_value; |
| new_node->has_initial_value = true; |
| } |
| |
| /* allocate the output pin (there is always one output pin) */ |
| allocate_more_output_pins(new_node, 1); |
| add_output_port_information(new_node, 1); |
| |
| /* allocate the input pin */ |
| allocate_more_input_pins(new_node,2);/* input[1] is clock */ |
| |
| /* add the port information */ |
| int i; |
| for(i = 0; i < 2; i++) |
| { |
| add_input_port_information(new_node,1); |
| } |
| |
| /* add names and type information to the created input pins */ |
| npin_t *new_pin = allocate_npin(); |
| new_pin->name = vtr::strdup(names[0]); |
| new_pin->type = INPUT; |
| add_input_pin_to_node(new_node, new_pin,0); |
| |
| new_pin = allocate_npin(); |
| new_pin->name = vtr::strdup(names[3]); |
| new_pin->type = INPUT; |
| add_input_pin_to_node(new_node, new_pin,1); |
| |
| /* add a name for the node, keeping the name of the node same as the output */ |
| new_node->name = make_full_ref_name(names[1],NULL, NULL, NULL,-1); |
| |
| /*add this node to blif_netlist as an ff (flip-flop) node */ |
| blif_netlist->ff_nodes = (nnode_t **)vtr::realloc(blif_netlist->ff_nodes, sizeof(nnode_t*)*(blif_netlist->num_ff_nodes+1)); |
| blif_netlist->ff_nodes[blif_netlist->num_ff_nodes++] = new_node; |
| new_node->file_number = current_parse_file; |
| new_node->line_number = line_count; |
| |
| /*add name information and a net(driver) for the output */ |
| nnet_t *new_net = allocate_nnet(); |
| new_net->name = new_node->name; |
| |
| new_pin = allocate_npin(); |
| new_pin->name = new_node->name; |
| new_pin->type = OUTPUT; |
| add_output_pin_to_node(new_node, new_pin, 0); |
| add_driver_pin_to_net(new_net, new_pin); |
| |
| output_nets_hash->add(new_node->name, new_net); |
| |
| /* Free the char** names */ |
| for (i = 0; i < input_token_count; i++) |
| vtr::free(names[i]); |
| |
| vtr::free(names); |
| vtr::free(ptr); |
| } |
| |
| /*--------------------------------------------------------------------------------------------- |
| * function: search_clock_name |
| to search the clock if the control in the latch |
| is not mentioned |
| *-------------------------------------------------------------------------------------------*/ |
| char* search_clock_name(FILE* file) |
| { |
| fpos_t pos; |
| int last_line = file_line_number; |
| fgetpos(file,&pos); |
| rewind(file); |
| |
| char *to_return = NULL; |
| char ** input_names = NULL; |
| int input_names_count = 0; |
| int found = 0; |
| while(!found) |
| { |
| char buffer[READ_BLIF_BUFFER]; |
| vtr::fgets(buffer,READ_BLIF_BUFFER,file); |
| |
| // not sure if this is needed |
| if(feof(file)) |
| break; |
| |
| char *ptr = NULL; |
| if((ptr = vtr::strtok(buffer, TOKENS, file, buffer))) |
| { |
| if(!strcmp(ptr,".end")) |
| break; |
| |
| if(!strcmp(ptr,".inputs")) |
| { |
| /* store the inputs in array of string */ |
| while((ptr = vtr::strtok (NULL, TOKENS, file, buffer))) |
| { |
| input_names = (char**)vtr::realloc(input_names,sizeof(char*) * (input_names_count + 1)); |
| input_names[input_names_count++] = vtr::strdup(ptr); |
| } |
| } |
| else if(!strcmp(ptr,".names") || !strcmp(ptr,".latch")) |
| { |
| while((ptr = vtr::strtok (NULL, TOKENS,file, buffer))) |
| { |
| int i; |
| for(i = 0; i < input_names_count; i++) |
| { |
| if(!strcmp(ptr,input_names[i])) |
| { |
| vtr::free(input_names[i]); |
| input_names[i] = input_names[--input_names_count]; |
| } |
| } |
| } |
| } |
| else if(input_names_count == 1) |
| { |
| found = 1; |
| } |
| } |
| } |
| file_line_number = last_line; |
| fsetpos(file,&pos); |
| |
| if (found) |
| { |
| to_return = input_names[0]; |
| } |
| else |
| { |
| to_return = vtr::strdup(DEFAULT_CLOCK_NAME); |
| for(int i = 0; i < input_names_count; i++) |
| { |
| if(input_names[i]) |
| { |
| vtr::free(input_names[i]); |
| } |
| } |
| } |
| |
| vtr::free(input_names); |
| |
| return to_return; |
| } |
| |
| |
| |
| /*--------------------------------------------------------------------------------------------- |
| * function:create_hard_block_nodes |
| to create the hard block nodes |
| *-------------------------------------------------------------------------------------------*/ |
| void create_hard_block_nodes(hard_block_models *models, FILE *file, Hashtable *output_nets_hash) |
| { |
| char buffer[READ_BLIF_BUFFER]; |
| char *subcircuit_name = vtr::strtok (NULL, TOKENS, file, buffer); |
| |
| /* storing the names on the formal-actual parameter */ |
| char *token; |
| int count = 0; |
| // Contains strings of the form port[pin]=port~pin |
| char **names_parameters = NULL; |
| while ((token = vtr::strtok (NULL, TOKENS, file, buffer)) != NULL) |
| { |
| names_parameters = (char**)vtr::realloc(names_parameters, sizeof(char*)*(count + 1)); |
| names_parameters[count++] = vtr::strdup(token); |
| } |
| |
| // Split the name parameters at the equals sign. |
| char **mappings = (char**)vtr::calloc(count, sizeof(char*)); |
| char **names = (char**)vtr::calloc(count, sizeof(char*)); |
| int i = 0; |
| for (i = 0; i < count; i++) |
| { |
| mappings[i] = vtr::strdup(strtok(names_parameters[i], "=")); |
| names[i] = vtr::strdup(strtok(NULL, "=")); |
| } |
| |
| // Associate mappings with their connections. |
| Hashtable *mapping_index = associate_names(mappings, names, count); |
| |
| // Sort the mappings. |
| qsort(mappings, count, sizeof(char *), compare_hard_block_pin_names); |
| |
| for(i = 0; i < count; i++) |
| vtr::free(names_parameters[i]); |
| |
| vtr::free(names_parameters); |
| |
| // Index the mappings in a hard_block_ports struct. |
| hard_block_ports *ports = get_hard_block_ports(mappings, count); |
| |
| for (i = 0; i < count; i++) |
| { |
| vtr::free(mappings[i]); |
| mappings[i] = NULL; |
| } |
| |
| vtr::free(mappings); |
| mappings = NULL; |
| |
| |
| // Look up the model in the models cache. |
| hard_block_model *model = NULL; |
| if ((subcircuit_name != NULL) && (!(model = get_hard_block_model(subcircuit_name, ports, models)))) |
| { |
| // If the model isn's present, scan ahead and find it. |
| model = read_hard_block_model(subcircuit_name, ports, file); |
| // Add it to the cache. |
| add_hard_block_model(model, ports, models); |
| } |
| |
| nnode_t *new_node = allocate_nnode(); |
| |
| // Name the node subcircuit_name~hard_block_number so that the name is unique. |
| static long hard_block_number = 0; |
| odin_sprintf(buffer, "%s~%ld", subcircuit_name, hard_block_number++); |
| new_node->name = make_full_ref_name(buffer, NULL, NULL, NULL,-1); |
| |
| // Determine the type of hard block. |
| char *subcircuit_name_prefix = vtr::strdup(subcircuit_name); |
| subcircuit_name_prefix[5] = '\0'; |
| if (!strcmp(subcircuit_name, "multiply") || !strcmp(subcircuit_name_prefix, "mult_")) |
| new_node->type = MULTIPLY; |
| else if (!strcmp(subcircuit_name, "adder") || !strcmp(subcircuit_name_prefix, "adder")) |
| new_node->type = ADD; |
| else if (!strcmp(subcircuit_name, "sub") || !strcmp(subcircuit_name_prefix, "sub")) |
| new_node->type = MINUS; |
| else |
| { |
| new_node->type = MEMORY; |
| } |
| vtr::free(subcircuit_name_prefix); |
| |
| /* Add input and output ports to the new node. */ |
| { |
| hard_block_ports *p; |
| p = model->input_ports; |
| for (i = 0; i < p->count; i++) |
| add_input_port_information(new_node, p->sizes[i]); |
| |
| p = model->output_ports; |
| for (i = 0; i < p->count; i++) |
| add_output_port_information(new_node, p->sizes[i]); |
| } |
| |
| // Allocate pins positions. |
| if (model->inputs->count > 0) |
| allocate_more_input_pins (new_node, model->inputs->count); |
| if (model->outputs->count > 0) |
| allocate_more_output_pins(new_node, model->outputs->count); |
| |
| // Add input pins. |
| for(i = 0; i < model->inputs->count; i++) |
| { |
| char *mapping = model->inputs->names[i]; |
| char *name = (char *)mapping_index->get(mapping); |
| |
| if (!name) |
| error_message(NETLIST_ERROR, file_line_number, current_parse_file, "Invalid hard block mapping: %s", mapping); |
| |
| npin_t *new_pin = allocate_npin(); |
| new_pin->name = vtr::strdup(name); |
| new_pin->type = INPUT; |
| new_pin->mapping = get_hard_block_port_name(mapping); |
| |
| add_input_pin_to_node(new_node, new_pin, i); |
| } |
| |
| // Add output pins, nets, and index each net. |
| for(i = 0; i < model->outputs->count; i++) |
| { |
| char *mapping = model->outputs->names[i]; |
| char *name = (char *)mapping_index->get(mapping); |
| |
| if (!name) error_message(NETLIST_ERROR, file_line_number, current_parse_file,"Invalid hard block mapping: %s", model->outputs->names[i]); |
| |
| npin_t *new_pin = allocate_npin(); |
| new_pin->name = vtr::strdup(name); |
| new_pin->type = OUTPUT; |
| new_pin->mapping = get_hard_block_port_name(mapping); |
| |
| add_output_pin_to_node(new_node, new_pin, i); |
| |
| nnet_t *new_net = allocate_nnet(); |
| new_net->name = vtr::strdup(name); |
| |
| add_driver_pin_to_net(new_net,new_pin); |
| |
| // Index the net by name. |
| output_nets_hash->add(name, new_net); |
| } |
| |
| // Create a fake ast node. |
| new_node->related_ast_node = (ast_node_t *)vtr::calloc(1, sizeof(ast_node_t)); |
| new_node->related_ast_node->children = (ast_node_t **)vtr::calloc(1,sizeof(ast_node_t *)); |
| new_node->related_ast_node->children[0] = (ast_node_t *)vtr::calloc(1, sizeof(ast_node_t)); |
| new_node->related_ast_node->children[0]->types.identifier = vtr::strdup(subcircuit_name); |
| |
| /*add this node to blif_netlist as an internal node */ |
| blif_netlist->internal_nodes = (nnode_t **)vtr::realloc(blif_netlist->internal_nodes, sizeof(nnode_t*) * (blif_netlist->num_internal_nodes + 1)); |
| blif_netlist->internal_nodes[blif_netlist->num_internal_nodes++] = new_node; |
| new_node->file_number = current_parse_file; |
| new_node->line_number = line_count; |
| |
| free_hard_block_ports(ports); |
| mapping_index->destroy_free_items(); |
| delete mapping_index; |
| vtr::free(names); |
| |
| |
| } |
| |
| /*--------------------------------------------------------------------------------------------- |
| * function:create_internal_node_and_driver |
| to create an internal node and driver from that node |
| *-------------------------------------------------------------------------------------------*/ |
| |
| void create_internal_node_and_driver(FILE *file, Hashtable *output_nets_hash) |
| { |
| /* Storing the names of the input and the final output in array names */ |
| char *ptr = NULL; |
| char **names = NULL; // stores the names of the input and the output, last name stored would be of the output |
| int input_count = 0; |
| char buffer[READ_BLIF_BUFFER]; |
| while ((ptr = vtr::strtok (NULL, TOKENS, file, buffer))) |
| { |
| names = (char**)vtr::realloc(names, sizeof(char*) * (input_count + 1)); |
| names[input_count++]= vtr::strdup(ptr); |
| } |
| |
| /* assigning the new_node */ |
| nnode_t *new_node = allocate_nnode(); |
| new_node->related_ast_node = NULL; |
| |
| /* gnd vcc unconn already created as top module so ignore them */ |
| if ( |
| !strcmp(names[input_count-1],"gnd") |
| || !strcmp(names[input_count-1],"vcc") |
| || !strcmp(names[input_count-1],"unconn") |
| ) |
| { |
| skip_reading_bit_map = true; |
| free_nnode(new_node); |
| } |
| else |
| { |
| /* assign the node type by seeing the name */ |
| operation_list node_type = (operation_list)assign_node_type_from_node_name(names[input_count-1]); |
| |
| if(node_type != GENERIC) |
| { |
| new_node->type = node_type; |
| skip_reading_bit_map = true; |
| } |
| /* Check for GENERIC type , change the node by reading the bit map */ |
| else if(node_type == GENERIC) |
| { |
| new_node->type = (operation_list)read_bit_map_find_unknown_gate(input_count-1, new_node, file); |
| skip_reading_bit_map = true; |
| } |
| |
| /* allocate the input pin (= input_count-1)*/ |
| if (input_count-1 > 0) // check if there is any input pins |
| { |
| allocate_more_input_pins(new_node, input_count-1); |
| |
| /* add the port information */ |
| if(new_node->type == MUX_2) |
| { |
| add_input_port_information(new_node, (input_count-1)/2); |
| add_input_port_information(new_node, (input_count-1)/2); |
| } |
| else |
| { |
| int i; |
| for(i = 0; i < input_count-1; i++) |
| add_input_port_information(new_node, 1); |
| } |
| } |
| |
| /* add names and type information to the created input pins */ |
| int i; |
| for(i = 0; i <= input_count-2; i++) |
| { |
| npin_t *new_pin = allocate_npin(); |
| new_pin->name = vtr::strdup(names[i]); |
| new_pin->type = INPUT; |
| add_input_pin_to_node(new_node, new_pin, i); |
| } |
| |
| /* add information for the intermediate VCC and GND node (appears in ABC )*/ |
| if(new_node->type == GND_NODE) |
| { |
| allocate_more_input_pins(new_node,1); |
| add_input_port_information(new_node, 1); |
| |
| npin_t *new_pin = allocate_npin(); |
| new_pin->name = vtr::strdup(GND_NAME); |
| new_pin->type = INPUT; |
| add_input_pin_to_node(new_node, new_pin,0); |
| } |
| |
| if(new_node->type == VCC_NODE) |
| { |
| allocate_more_input_pins(new_node,1); |
| add_input_port_information(new_node, 1); |
| |
| npin_t *new_pin = allocate_npin(); |
| new_pin->name = vtr::strdup(VCC_NAME); |
| new_pin->type = INPUT; |
| add_input_pin_to_node(new_node, new_pin,0); |
| } |
| |
| /* allocate the output pin (there is always one output pin) */ |
| allocate_more_output_pins(new_node, 1); |
| add_output_port_information(new_node, 1); |
| |
| /* add a name for the node, keeping the name of the node same as the output */ |
| new_node->name = make_full_ref_name(names[input_count-1],NULL, NULL, NULL,-1); |
| |
| /*add this node to blif_netlist as an internal node */ |
| blif_netlist->internal_nodes = (nnode_t**)vtr::realloc(blif_netlist->internal_nodes, sizeof(nnode_t*)*(blif_netlist->num_internal_nodes+1)); |
| blif_netlist->internal_nodes[blif_netlist->num_internal_nodes++] = new_node; |
| new_node->file_number = current_parse_file; |
| new_node->line_number = line_count; |
| |
| /*add name information and a net(driver) for the output */ |
| |
| npin_t *new_pin = allocate_npin(); |
| new_pin->name = new_node->name; |
| new_pin->type = OUTPUT; |
| |
| add_output_pin_to_node(new_node, new_pin, 0); |
| |
| nnet_t *new_net = allocate_nnet(); |
| new_net->name = new_node->name; |
| |
| add_driver_pin_to_net(new_net,new_pin); |
| |
| output_nets_hash->add(new_node->name, new_net); |
| |
| } |
| /* Free the char** names */ |
| for(int i = 0; i < input_count; i++) |
| vtr::free(names[i]); |
| |
| vtr::free(names); |
| } |
| |
| /* |
| *--------------------------------------------------------------------------------------------- |
| * function: read_bit_map_find_unknown_gate |
| read the bit map for simulation |
| *-------------------------------------------------------------------------------------------*/ |
| operation_list read_bit_map_find_unknown_gate(int input_count, nnode_t *node, FILE *file) |
| { |
| operation_list to_return = operation_list_END; |
| |
| fpos_t pos; |
| int last_line = file_line_number; |
| const char *One = "1"; |
| const char *Zero = "0"; |
| fgetpos(file,&pos); |
| |
| char **bit_map = NULL; |
| char *output_bit_map = NULL;// to distinguish whether for the bit_map output is 1 or 0 |
| int line_count_bitmap = 0; //stores the number of lines in a particular bit map |
| char buffer[READ_BLIF_BUFFER]; |
| |
| if(!input_count) |
| { |
| vtr::fgets (buffer, READ_BLIF_BUFFER, file); |
| |
| file_line_number = last_line; |
| fsetpos(file,&pos); |
| |
| char *ptr = vtr::strtok(buffer,"\t\n", file, buffer); |
| if(!ptr) |
| { |
| to_return = GND_NODE; |
| } |
| else if(!strcmp(ptr," 1")) |
| { |
| to_return = VCC_NODE; |
| } |
| else if(!strcmp(ptr," 0")) |
| { |
| to_return = GND_NODE; |
| } |
| else |
| { |
| to_return = VCC_NODE; |
| } |
| } |
| else |
| { |
| while(1) |
| { |
| vtr::fgets (buffer, READ_BLIF_BUFFER, file); |
| if(!(buffer[0] == '0' || buffer[0] == '1' || buffer[0] == '-')) |
| break; |
| |
| bit_map = (char**)vtr::realloc(bit_map,sizeof(char*) * (line_count_bitmap + 1)); |
| bit_map[line_count_bitmap++] = vtr::strdup(vtr::strtok(buffer,TOKENS, file, buffer)); |
| if (output_bit_map != NULL) vtr::free(output_bit_map); |
| output_bit_map = vtr::strdup(vtr::strtok(NULL,TOKENS, file, buffer)); |
| } |
| |
| oassert(output_bit_map); |
| |
| file_line_number = last_line; |
| fsetpos(file,&pos); |
| |
| /*Patern recognition for faster simulation*/ |
| if(!strcmp(output_bit_map, One)) |
| { |
| //On-gate recognition |
| //TODO move off-logic parts to appropriate code block |
| |
| vtr::free(output_bit_map); |
| output_bit_map = vtr::strdup(One); |
| node->generic_output = 1; |
| |
| /* Single line bit map : */ |
| if(line_count_bitmap == 1) |
| { |
| // GT |
| if(!strcmp(bit_map[0],"100")) |
| { |
| to_return = GT; |
| } |
| |
| // LT |
| else if(!strcmp(bit_map[0],"010")) |
| { |
| to_return = LT; |
| } |
| |
| /* LOGICAL_AND and LOGICAL_NAND for ABC*/ |
| else |
| { |
| int i; |
| for(i = 0; i < input_count && bit_map[0][i] == '1'; i++); |
| |
| if(i == input_count) |
| { |
| if (!strcmp(output_bit_map,"1")) |
| { |
| to_return = LOGICAL_AND; |
| } |
| else if (!strcmp(output_bit_map,"0")) |
| { |
| to_return = LOGICAL_NAND; |
| } |
| } |
| |
| /* BITWISE_NOT */ |
| if(!strcmp(bit_map[0],"0") && to_return == operation_list_END) |
| { |
| to_return = BITWISE_NOT; |
| } |
| /* LOGICAL_NOR and LOGICAL_OR for ABC */ |
| for(i = 0; i < input_count && bit_map[0][i] == '0'; i++); |
| |
| if(i == input_count && to_return == operation_list_END) |
| { |
| if (!strcmp(output_bit_map,"1")) |
| { |
| to_return = LOGICAL_NOR; |
| } |
| else if (!strcmp(output_bit_map,"0")) |
| { |
| to_return = LOGICAL_OR; |
| } |
| } |
| } |
| } |
| /* Assumption that bit map is in order when read from blif */ |
| else if(line_count_bitmap == 2) |
| { |
| /* LOGICAL_XOR */ |
| if((strcmp(bit_map[0],"01")==0) && (strcmp(bit_map[1],"10")==0)) |
| { |
| to_return = LOGICAL_XOR; |
| } |
| /* LOGICAL_XNOR */ |
| else if((strcmp(bit_map[0],"00")==0) && (strcmp(bit_map[1],"11")==0)) |
| { |
| to_return = LOGICAL_XNOR; |
| } |
| } |
| else if (line_count_bitmap == 4) |
| { |
| /* ADDER_FUNC */ |
| if ( |
| (!strcmp(bit_map[0],"001")) |
| && (!strcmp(bit_map[1],"010")) |
| && (!strcmp(bit_map[2],"100")) |
| && (!strcmp(bit_map[3],"111")) |
| ) |
| { |
| to_return = ADDER_FUNC; |
| } |
| /* CARRY_FUNC */ |
| else if( |
| (!strcmp(bit_map[0],"011")) |
| && (!strcmp(bit_map[1],"101")) |
| && (!strcmp(bit_map[2],"110")) |
| && (!strcmp(bit_map[3],"111")) |
| ) |
| { |
| to_return = CARRY_FUNC; |
| } |
| /* LOGICAL_XOR */ |
| else if( |
| (!strcmp(bit_map[0],"001")) |
| && (!strcmp(bit_map[1],"010")) |
| && (!strcmp(bit_map[2],"100")) |
| && (!strcmp(bit_map[3],"111")) |
| ) |
| { |
| to_return = LOGICAL_XOR; |
| } |
| /* LOGICAL_XNOR */ |
| else if( |
| (!strcmp(bit_map[0],"000")) |
| && (!strcmp(bit_map[1],"011")) |
| && (!strcmp(bit_map[2],"101")) |
| && (!strcmp(bit_map[3],"110")) |
| ) |
| { |
| to_return = LOGICAL_XNOR; |
| } |
| } |
| |
| |
| if(line_count_bitmap == input_count && to_return == operation_list_END) |
| { |
| /* LOGICAL_OR */ |
| int i; |
| for(i = 0; i < line_count_bitmap; i++) |
| { |
| if(bit_map[i][i] == '1') |
| { |
| int j; |
| for(j = 1; j < input_count; j++) |
| { |
| if(bit_map[i][(i+j)% input_count]!='-') |
| { |
| break; |
| } |
| } |
| |
| if(j != input_count) |
| { |
| break; |
| } |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| if(i == line_count_bitmap) |
| { |
| to_return = LOGICAL_OR; |
| } |
| else |
| { |
| |
| /* LOGICAL_NAND */ |
| for(i = 0; i < line_count_bitmap; i++) |
| { |
| if(bit_map[i][i]=='0') |
| { |
| int j; |
| for(j = 1; j < input_count; j++) |
| { |
| if(bit_map[i][(i+j)% input_count]!='-') |
| { |
| break; |
| } |
| } |
| |
| if(j != input_count) |
| { |
| break; |
| } |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| if(i == line_count_bitmap) |
| { |
| to_return = LOGICAL_NAND; |
| } |
| } |
| } |
| |
| /* MUX_2 */ |
| if(line_count_bitmap*2 == input_count && to_return == operation_list_END) |
| { |
| int i; |
| for(i = 0; i < line_count_bitmap; i++) |
| { |
| if((bit_map[i][i]=='1') && (bit_map[i][i+line_count_bitmap] =='1')) |
| { |
| int j; |
| for (j = 1; j < line_count_bitmap; j++) |
| { |
| if ( |
| (bit_map[i][ (i+j) % line_count_bitmap] != '-') |
| || (bit_map[i][((i+j) % line_count_bitmap) + line_count_bitmap] != '-') |
| ) |
| { |
| break; |
| } |
| } |
| |
| if(j != input_count) |
| { |
| break; |
| } |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| if(i == line_count_bitmap) |
| { |
| to_return = MUX_2; |
| } |
| } |
| } |
| else |
| { |
| //Off-gate recognition |
| //TODO |
| |
| vtr::free(output_bit_map); |
| output_bit_map = vtr::strdup(Zero); |
| node->generic_output = 0; |
| } |
| |
| /* assigning the bit_map to the node if it is GENERIC */ |
| if(to_return == operation_list_END) |
| { |
| node->bit_map = bit_map; |
| node->bit_map_line_count = line_count_bitmap; |
| to_return = GENERIC; |
| } |
| } |
| if(output_bit_map) |
| { |
| vtr::free(output_bit_map); |
| } |
| if(bit_map) |
| { |
| for(int i = 0; i < line_count_bitmap; i++) |
| { |
| vtr::free(bit_map[i]); |
| } |
| vtr::free(bit_map); |
| } |
| return to_return; |
| } |
| |
| /* |
| *--------------------------------------------------------------------------------------------- |
| * function: add_top_input_nodes |
| to add the top level inputs to the netlist |
| *-------------------------------------------------------------------------------------------*/ |
| static void build_top_input_node(const char *name_str, Hashtable *output_nets_hash) |
| { |
| char *temp_string = make_full_ref_name(name_str, NULL, NULL,NULL, -1); |
| |
| /* create a new top input node and net*/ |
| |
| nnode_t *new_node = allocate_nnode(); |
| |
| new_node->related_ast_node = NULL; |
| new_node->type = INPUT_NODE; |
| |
| /* add the name of the input variable */ |
| new_node->name = temp_string; |
| |
| new_node->file_number = current_parse_file; |
| new_node->line_number = line_count; |
| |
| /* allocate the pins needed */ |
| allocate_more_output_pins(new_node, 1); |
| add_output_port_information(new_node, 1); |
| |
| /* Create the pin connection for the net */ |
| npin_t *new_pin = allocate_npin(); |
| new_pin->name = vtr::strdup(temp_string); |
| new_pin->type = OUTPUT; |
| |
| /* hookup the pin, net, and node */ |
| add_output_pin_to_node(new_node, new_pin, 0); |
| |
| nnet_t *new_net = allocate_nnet(); |
| new_net->name = vtr::strdup(temp_string); |
| |
| add_driver_pin_to_net(new_net, new_pin); |
| |
| blif_netlist->top_input_nodes = (nnode_t**)vtr::realloc(blif_netlist->top_input_nodes, sizeof(nnode_t*)*(blif_netlist->num_top_input_nodes+1)); |
| blif_netlist->top_input_nodes[blif_netlist->num_top_input_nodes++] = new_node; |
| |
| //long sc_spot = sc_add_string(output_nets_sc, temp_string); |
| //if (output_nets_sc->data[sc_spot]) |
| //warning_message(NETLIST_ERROR,linenum,-1, "Net (%s) with the same name already created\n",temp_string); |
| |
| //output_nets_sc->data[sc_spot] = new_net; |
| |
| output_nets_hash->add(temp_string, new_net); |
| } |
| |
| void add_top_input_nodes(FILE *file, Hashtable *output_nets_hash) |
| { |
| /** |
| * insert a global clock for fall back. |
| * in case of undriven internal clocks, they will attach to the global clock |
| * this also fix the issue of constant verilog (no input) |
| * that cannot simulate due to empty input vector |
| */ |
| if(insert_global_clock) |
| { |
| insert_global_clock = false; |
| build_top_input_node(DEFAULT_CLOCK_NAME, output_nets_hash); |
| } |
| |
| char *ptr; |
| char buffer[READ_BLIF_BUFFER]; |
| while ((ptr = vtr::strtok (NULL, TOKENS, file, buffer))) |
| { |
| build_top_input_node(ptr, output_nets_hash); |
| } |
| } |
| |
| /*--------------------------------------------------------------------------------------------- |
| * function: create_top_output_nodes |
| to add the top level outputs to the netlist |
| *-------------------------------------------------------------------------------------------*/ |
| void rb_create_top_output_nodes(FILE *file) |
| { |
| char *ptr; |
| char buffer[READ_BLIF_BUFFER]; |
| |
| while ((ptr = vtr::strtok (NULL, TOKENS, file, buffer))) |
| { |
| char *temp_string = make_full_ref_name(ptr, NULL, NULL,NULL, -1);; |
| |
| /*add_a_fanout_pin_to_net((nnet_t*)output_nets_sc->data[sc_spot], new_pin);*/ |
| |
| /* create a new top output node and */ |
| nnode_t *new_node = allocate_nnode(); |
| new_node->related_ast_node = NULL; |
| new_node->type = OUTPUT_NODE; |
| |
| /* add the name of the output variable */ |
| new_node->name = temp_string; |
| |
| /* allocate the input pin needed */ |
| allocate_more_input_pins(new_node, 1); |
| add_input_port_information(new_node, 1); |
| |
| /* Create the pin connection for the net */ |
| npin_t *new_pin = allocate_npin(); |
| new_pin->name = temp_string; |
| /* hookup the pin, net, and node */ |
| add_input_pin_to_node(new_node, new_pin, 0); |
| |
| /*adding the node to the blif_netlist output nodes |
| add_node_to_netlist() function can also be used */ |
| blif_netlist->top_output_nodes = (nnode_t**)vtr::realloc(blif_netlist->top_output_nodes, sizeof(nnode_t*)*(blif_netlist->num_top_output_nodes+1)); |
| blif_netlist->top_output_nodes[blif_netlist->num_top_output_nodes++] = new_node; |
| new_node->file_number = current_parse_file; |
| new_node->line_number = line_count; |
| } |
| } |
| |
| |
| /*--------------------------------------------------------------------------------------------- |
| * (function: look_for_clocks) |
| *-------------------------------------------------------------------------------------------*/ |
| |
| void rb_look_for_clocks() |
| { |
| int i; |
| for (i = 0; i < blif_netlist->num_ff_nodes; i++) |
| { |
| if (blif_netlist->ff_nodes[i]->input_pins[1]->net->driver_pin->node->type != CLOCK_NODE) |
| { |
| blif_netlist->ff_nodes[i]->input_pins[1]->net->driver_pin->node->type = CLOCK_NODE; |
| } |
| } |
| |
| } |
| |
| /* |
| ---------------------------------------------------------------------------- |
| function: Creates the drivers for the top module |
| Top module is : |
| * Special as all inputs are actually drivers. |
| * Also make the 0 and 1 constant nodes at this point. |
| --------------------------------------------------------------------------- |
| */ |
| |
| void rb_create_top_driver_nets(const char *instance_name_prefix, Hashtable *output_nets_hash) |
| { |
| npin_t *new_pin; |
| /* create the constant nets */ |
| |
| /* ZERO net */ |
| /* description given for the zero net is same for other two */ |
| blif_netlist->zero_net = allocate_nnet(); // allocate memory to net pointer |
| blif_netlist->gnd_node = allocate_nnode(); // allocate memory to node pointer |
| blif_netlist->gnd_node->type = GND_NODE; // mark the type |
| allocate_more_output_pins(blif_netlist->gnd_node, 1);// alloacate 1 output pin pointer to this node |
| add_output_port_information(blif_netlist->gnd_node, 1);// add port info. this port has 1 pin ,till now number of port for this is one |
| new_pin = allocate_npin(); |
| add_output_pin_to_node(blif_netlist->gnd_node, new_pin, 0);// add this pin to output pin pointer array of this node |
| add_driver_pin_to_net(blif_netlist->zero_net,new_pin);// add this pin to net as driver pin |
| |
| /*ONE net*/ |
| blif_netlist->one_net = allocate_nnet(); |
| blif_netlist->vcc_node = allocate_nnode(); |
| blif_netlist->vcc_node->type = VCC_NODE; |
| allocate_more_output_pins(blif_netlist->vcc_node, 1); |
| add_output_port_information(blif_netlist->vcc_node, 1); |
| new_pin = allocate_npin(); |
| add_output_pin_to_node(blif_netlist->vcc_node, new_pin, 0); |
| add_driver_pin_to_net(blif_netlist->one_net, new_pin); |
| |
| /* Pad net */ |
| blif_netlist->pad_net = allocate_nnet(); |
| blif_netlist->pad_node = allocate_nnode(); |
| blif_netlist->pad_node->type = PAD_NODE; |
| allocate_more_output_pins(blif_netlist->pad_node, 1); |
| add_output_port_information(blif_netlist->pad_node, 1); |
| new_pin = allocate_npin(); |
| add_output_pin_to_node(blif_netlist->pad_node, new_pin, 0); |
| add_driver_pin_to_net(blif_netlist->pad_net, new_pin); |
| |
| /* CREATE the driver for the ZERO */ |
| blif_netlist->zero_net->name = make_full_ref_name(instance_name_prefix, NULL, NULL, zero_string, -1); |
| output_nets_hash->add(GND_NAME, blif_netlist->zero_net); |
| |
| /* CREATE the driver for the ONE and store twice */ |
| blif_netlist->one_net->name = make_full_ref_name(instance_name_prefix, NULL, NULL, one_string, -1); |
| output_nets_hash->add(VCC_NAME, blif_netlist->one_net); |
| |
| /* CREATE the driver for the PAD */ |
| blif_netlist->pad_net->name = make_full_ref_name(instance_name_prefix, NULL, NULL, pad_string, -1); |
| output_nets_hash->add(HBPAD_NAME, blif_netlist->pad_net); |
| |
| blif_netlist->vcc_node->name = vtr::strdup(VCC_NAME); |
| blif_netlist->gnd_node->name = vtr::strdup(GND_NAME); |
| blif_netlist->pad_node->name = vtr::strdup(HBPAD_NAME); |
| |
| } |
| |
| /*--------------------------------------------------------------------------------------------- |
| * (function: dum_parse) |
| *-------------------------------------------------------------------------------------------*/ |
| static void dum_parse (char *buffer, FILE *file) |
| { |
| /* Continue parsing to the end of this (possibly continued) line. */ |
| while (vtr::strtok (NULL, TOKENS, file, buffer)); |
| } |
| |
| |
| |
| /*--------------------------------------------------------------------------------------------- |
| * function: hook_up_nets() |
| * find the output nets and add the corresponding nets |
| *-------------------------------------------------------------------------------------------*/ |
| void hook_up_nets(Hashtable *output_nets_hash) |
| { |
| nnode_t **node_sets[] = {blif_netlist->internal_nodes, blif_netlist->ff_nodes, blif_netlist->top_output_nodes}; |
| int counts[] = {blif_netlist->num_internal_nodes, blif_netlist->num_ff_nodes, blif_netlist->num_top_output_nodes}; |
| int num_sets = 3; |
| |
| /* hook all the input pins in all the internal nodes to the net */ |
| int i; |
| for (i = 0; i < num_sets; i++) |
| { |
| int j; |
| for(j = 0; j < counts[i]; j++) |
| { |
| nnode_t *node = node_sets[i][j]; |
| hook_up_node(node, output_nets_hash); |
| } |
| } |
| } |
| |
| /* |
| * Connect the given node's input pins to their corresponding nets by |
| * looking each one up in the output_nets_sc. |
| */ |
| void hook_up_node(nnode_t *node, Hashtable *output_nets_hash) |
| { |
| int j; |
| for(j = 0; j < node->num_input_pins; j++) |
| { |
| npin_t *input_pin = node->input_pins[j]; |
| |
| nnet_t *output_net = (nnet_t *)output_nets_hash->get(input_pin->name); |
| |
| if(!output_net) |
| error_message(NETLIST_ERROR,file_line_number, current_parse_file, "Error: Could not hook up the pin %s: not available.", input_pin->name); |
| |
| add_fanout_pin_to_net(output_net, input_pin); |
| } |
| } |
| |
| /* |
| * Scans ahead in the given file to find the |
| * model for the hard block by the given name. |
| * Returns the file to its original position when finished. |
| */ |
| hard_block_model *read_hard_block_model(char *name_subckt, hard_block_ports *ports, FILE *file) |
| { |
| // Store the current position in the file. |
| fpos_t pos; |
| int last_line = file_line_number; |
| fgetpos(file,&pos); |
| |
| hard_block_model *model; |
| |
| while(1) { |
| model = NULL; |
| |
| // Search the file for .model followed buy the subcircuit name. |
| char buffer[READ_BLIF_BUFFER]; |
| while (vtr::fgets(buffer, READ_BLIF_BUFFER, file)) |
| { |
| char *token = vtr::strtok(buffer,TOKENS, file, buffer); |
| // match .model followed by the subcircuit name. |
| if (token && !strcmp(token,".model") && !strcmp(vtr::strtok(NULL,TOKENS, file, buffer), name_subckt)) |
| { |
| model = (hard_block_model *)vtr::calloc(1, sizeof(hard_block_model)); |
| model->name = vtr::strdup(name_subckt); |
| model->inputs = (hard_block_pins *)vtr::calloc(1, sizeof(hard_block_pins)); |
| model->inputs->count = 0; |
| model->inputs->names = NULL; |
| |
| model->outputs = (hard_block_pins *)vtr::calloc(1, sizeof(hard_block_pins)); |
| model->outputs->count = 0; |
| model->outputs->names = NULL; |
| |
| // Read the inputs and outputs. |
| while (vtr::fgets(buffer, READ_BLIF_BUFFER, file)) |
| { |
| char *first_word = vtr::strtok(buffer, TOKENS, file, buffer); |
| if(first_word) |
| { |
| if(!strcmp(first_word, ".inputs")) |
| { |
| char *name; |
| while ((name = vtr::strtok(NULL, TOKENS, file, buffer))) |
| { |
| model->inputs->names = (char **)vtr::realloc(model->inputs->names, sizeof(char *) * (model->inputs->count + 1)); |
| model->inputs->names[model->inputs->count++] = vtr::strdup(name); |
| } |
| } |
| else if(!strcmp(first_word, ".outputs")) |
| { |
| char *name; |
| while ((name = vtr::strtok(NULL, TOKENS, file, buffer))) |
| { |
| model->outputs->names = (char **)vtr::realloc(model->outputs->names, sizeof(char *) * (model->outputs->count + 1)); |
| model->outputs->names[model->outputs->count++] = vtr::strdup(name); |
| } |
| } |
| else if(!strcmp(first_word, ".end")) |
| { |
| break; |
| } |
| } |
| } |
| break; |
| } |
| } |
| |
| if(!model || feof(file)) |
| error_message(NETLIST_ERROR, last_line, current_parse_file, "A subcircuit model for '%s' with matching ports was not found.",name_subckt); |
| |
| // Sort the names. |
| qsort(model->inputs->names, model->inputs->count, sizeof(char *), compare_hard_block_pin_names); |
| qsort(model->outputs->names, model->outputs->count, sizeof(char *), compare_hard_block_pin_names); |
| |
| // Index the names. |
| model->inputs->index = index_names(model->inputs->names, model->inputs->count); |
| model->outputs->index = index_names(model->outputs->names, model->outputs->count); |
| |
| // Organise the names into ports. |
| model->input_ports = get_hard_block_ports(model->inputs->names, model->inputs->count); |
| model->output_ports = get_hard_block_ports(model->outputs->names, model->outputs->count); |
| |
| // Check that the model we've read matches the ports of the instance we are trying to match. |
| if (verify_hard_block_ports_against_model(ports, model)) |
| { |
| break; |
| } |
| else |
| { // If not, free it, and keep looking. |
| free_hard_block_model(model); |
| } |
| } |
| |
| // Restore the original position in the file. |
| file_line_number = last_line; |
| fsetpos(file,&pos); |
| |
| return model; |
| } |
| |
| /* |
| * Callback function for qsort which compares pin names |
| * of the form port_name[pin_number] primarily |
| * on the port_name, and on the pin_number if the port_names |
| * are identical. |
| */ |
| static int compare_hard_block_pin_names(const void *p1, const void *p2) |
| { |
| char *name1 = *(char * const *)p1; |
| char *name2 = *(char * const *)p2; |
| |
| char *port_name1 = get_hard_block_port_name(name1); |
| char *port_name2 = get_hard_block_port_name(name2); |
| int portname_difference = strcmp(port_name1, port_name2); |
| vtr::free(port_name1); |
| vtr::free(port_name2); |
| |
| // If the portnames are the same, compare the pin numbers. |
| if (!portname_difference) |
| { |
| int n1 = get_hard_block_pin_number(name1); |
| int n2 = get_hard_block_pin_number(name2); |
| return n1 - n2; |
| } |
| else |
| { |
| return portname_difference; |
| } |
| } |
| |
| /* |
| * Creates a hashtable index for an array of strings of |
| * the form names[i]=>i. |
| */ |
| Hashtable *index_names(char **names, int count) |
| { |
| Hashtable *index = new Hashtable(); |
| for (long i = 0; i < count; i++) |
| { |
| int *offset = (int *)vtr::calloc(1, sizeof(int)); |
| *offset = i; |
| index->add(names[i], offset); |
| } |
| return index; |
| } |
| |
| /* |
| * Create an associative index of names1[i]=>names2[i] |
| */ |
| Hashtable *associate_names(char **names1, char **names2, int count) |
| { |
| Hashtable *index = new Hashtable(); |
| for (long i = 0; i < count; i++) |
| index->add(names1[i], names2[i]); |
| |
| return index; |
| } |
| |
| |
| /* |
| * Organises the given strings representing pin names on a hard block |
| * model into ports, and indexes the ports by name. Returns the organised |
| * ports as a hard_block_ports struct. |
| */ |
| hard_block_ports *get_hard_block_ports(char **pins, int count) |
| { |
| // Count the input port sizes. |
| hard_block_ports *ports = (hard_block_ports *)vtr::calloc(1, sizeof(hard_block_ports)); |
| ports->count = 0; |
| ports->sizes = NULL; |
| ports->names = NULL; |
| char *prev_portname = NULL; |
| int i; |
| for (i = 0; i < count; i++) |
| { |
| char *portname = get_hard_block_port_name(pins[i]); |
| // Compare the part of the name before the "[" |
| if (!i || strcmp(prev_portname, portname)) |
| { |
| ports->sizes = (int *)vtr::realloc(ports->sizes, sizeof(int) * (ports->count + 1)); |
| ports->names = (char **)vtr::realloc(ports->names, sizeof(char *) * (ports->count + 1)); |
| |
| ports->sizes[ports->count] = 0; |
| ports->names[ports->count] = vtr::strdup(portname); |
| ports->count++; |
| |
| } |
| |
| if ( prev_portname != NULL ) |
| vtr::free(prev_portname); |
| |
| prev_portname = portname; |
| ports->sizes[ports->count-1]++; |
| } |
| |
| if ( prev_portname != NULL ) |
| vtr::free(prev_portname); |
| |
| ports->signature = generate_hard_block_ports_signature(ports); |
| ports->index = index_names(ports->names, ports->count); |
| |
| return ports; |
| } |
| |
| /* |
| * Check for inconsistencies between the hard block model and the ports found |
| * in the hard block instance. Returns false if differences are found. |
| */ |
| int verify_hard_block_ports_against_model(hard_block_ports *ports, hard_block_model *model) |
| { |
| hard_block_ports *port_sets[] = {model->input_ports, model->output_ports}; |
| int i; |
| for (i = 0; i < 2; i++) |
| { |
| hard_block_ports *p = port_sets[i]; |
| int j; |
| for (j = 0; j < p->count; j++) |
| { |
| // Look up each port from the model in "ports" |
| char *name = p->names[j]; |
| int size = p->sizes[j]; |
| int *idx = (int *)ports->index->get(name); |
| // Model port not specified in ports. |
| if (!idx) |
| { |
| //printf("Model port not specified in ports. %s\n", name); |
| return false; |
| } |
| |
| // Make sure they match in size. |
| int instance_size = ports->sizes[*idx]; |
| // Port sizes differ. |
| if (size != instance_size) |
| { |
| //printf("Port sizes differ. %s\n", name); |
| return false; |
| } |
| } |
| } |
| |
| hard_block_ports *in = model->input_ports; |
| hard_block_ports *out = model->output_ports; |
| int j; |
| for (j = 0; j < ports->count; j++) |
| { |
| // Look up each port from the subckt to make sure it appears in the model. |
| char *name = ports->names[j]; |
| int *in_idx = (int *)in->index->get(name); |
| int *out_idx = (int *)out->index->get(name); |
| // Port does not appear in the model. |
| if (!in_idx && !out_idx) |
| { |
| //printf("Port does not appear in the model. %s\n", name); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /* |
| * Generates string which represents the geometry of the given hard block ports. |
| */ |
| char *generate_hard_block_ports_signature(hard_block_ports *ports) |
| { |
| char buffer[READ_BLIF_BUFFER]; |
| buffer[0] = '\0'; |
| |
| strcat(buffer, "_"); |
| |
| int j; |
| for (j = 0; j < ports->count; j++) |
| { |
| char buffer1[READ_BLIF_BUFFER]; |
| odin_sprintf(buffer1, "%s_%d_", ports->names[j], ports->sizes[j]); |
| strcat(buffer, buffer1); |
| } |
| return vtr::strdup(buffer); |
| } |
| |
| /* |
| * Gets the text in the given string which occurs |
| * before the first instance of "[". The string is |
| * presumably of the form "port[pin_number]" |
| * |
| * The retuned string is strduped and must be freed. |
| * The original string is unaffected. |
| */ |
| char *get_hard_block_port_name(char *name) |
| { |
| name = vtr::strdup(name); |
| if (strchr(name,'[')) |
| return strtok(name,"["); |
| else |
| return name; |
| } |
| |
| /* |
| * Parses a port name of the form port[pin_number] |
| * and returns the pin number as a long. Returns -1 |
| * if there is no [pin_number] in the name. Throws an |
| * error if pin_number is not parsable as a long. |
| * |
| * The original string is unaffected. |
| */ |
| long get_hard_block_pin_number(char *original_name) |
| { |
| if (!strchr(original_name,'[')) |
| return -1; |
| |
| char *name = vtr::strdup(original_name); |
| strtok(name,"["); |
| char *endptr; |
| char *pin_number_string = strtok(NULL,"]"); |
| long pin_number = strtol(pin_number_string, &endptr, 10); |
| |
| if (pin_number_string == endptr) |
| error_message(NETLIST_ERROR,file_line_number, current_parse_file,"The given port name \"%s\" does not contain a valid pin number.", original_name); |
| |
| vtr::free(name); |
| |
| return pin_number; |
| } |
| |
| /* |
| * Adds the given model to the hard block model cache. |
| */ |
| void add_hard_block_model(hard_block_model *m, hard_block_ports *ports, hard_block_models *models) |
| { |
| if(models && m) |
| { |
| char needle[READ_BLIF_BUFFER] = { 0 }; |
| |
| if(m->name && ports && ports->signature) |
| sprintf(needle, "%s%s", m->name, ports->signature); |
| else if(m->name) |
| sprintf(needle, "%s", m->name); |
| else if(ports && ports->signature) |
| sprintf(needle, "%s", ports->signature); |
| |
| if(strlen(needle) > 0) |
| { |
| models->count += 1; |
| |
| models->models = (hard_block_model **)vtr::realloc(models->models, models->count * sizeof(hard_block_model *)); |
| models->models[models->count-1] = m; |
| models->index->add(needle, m); |
| } |
| } |
| } |
| |
| /* |
| * Looks up a hard block model by name. Returns null if the |
| * model is not found. |
| */ |
| hard_block_model *get_hard_block_model(char *name, hard_block_ports *ports, hard_block_models *models) |
| { |
| hard_block_model *to_return = NULL; |
| char needle[READ_BLIF_BUFFER] = { 0 }; |
| |
| if(name && ports && ports->signature) |
| sprintf(needle, "%s%s", name, ports->signature); |
| else if(name) |
| sprintf(needle, "%s", name); |
| else if(ports && ports->signature) |
| sprintf(needle, "%s", ports->signature); |
| |
| if(strlen(needle) > 0) |
| to_return = (hard_block_model *)models->index->get(needle); |
| |
| return to_return; |
| } |
| |
| /* |
| * Creates a new hard block model cache. |
| */ |
| hard_block_models *create_hard_block_models() |
| { |
| hard_block_models *m = (hard_block_models *)vtr::calloc(1, sizeof(hard_block_models)); |
| m->models = NULL; |
| m->count = 0; |
| m->index = new Hashtable(); |
| |
| return m; |
| } |
| |
| /* |
| * Counts the number of lines in the given blif file |
| * before a .end token is hit. |
| */ |
| int count_blif_lines(FILE *file) |
| { |
| int num_lines = 0; |
| char buffer[READ_BLIF_BUFFER]; |
| while (vtr::fgets(buffer, READ_BLIF_BUFFER, file)) |
| { |
| if (strstr(buffer, ".end")) |
| break; |
| num_lines++; |
| } |
| rewind(file); |
| return num_lines; |
| } |
| |
| /* |
| * Frees the hard block model cache, freeing |
| * all encapsulated hard block models. |
| */ |
| void free_hard_block_models(hard_block_models *models) |
| { |
| //does not delete the items in the hash |
| delete models->index; |
| int i; |
| for (i = 0; i < models->count; i++) |
| free_hard_block_model(models->models[i]); |
| |
| vtr::free(models->models); |
| vtr::free(models); |
| } |
| |
| |
| /* |
| * Frees a hard_block_model. |
| */ |
| void free_hard_block_model(hard_block_model *model) |
| { |
| free_hard_block_pins(model->inputs); |
| free_hard_block_pins(model->outputs); |
| |
| free_hard_block_ports(model->input_ports); |
| free_hard_block_ports(model->output_ports); |
| |
| vtr::free(model->name); |
| vtr::free(model); |
| } |
| |
| /* |
| * Frees hard_block_pins |
| */ |
| void free_hard_block_pins(hard_block_pins *p) |
| { |
| while (p->count--) |
| vtr::free(p->names[p->count]); |
| |
| vtr::free(p->names); |
| |
| p->index->destroy_free_items(); |
| delete p->index; |
| vtr::free(p); |
| } |
| |
| /* |
| * Frees hard_block_ports |
| */ |
| void free_hard_block_ports(hard_block_ports *p) |
| { |
| while(p->count--) |
| vtr::free(p->names[p->count]); |
| |
| vtr::free(p->signature); |
| vtr::free(p->names); |
| vtr::free(p->sizes); |
| |
| p->index->destroy_free_items(); |
| delete p->index; |
| vtr::free(p); |
| } |