| #include <stdio.h> | |
| #include <string.h> | |
| #include "util.h" | |
| #include "vpr_types.h" | |
| #include "globals.h" | |
| #include "path_delay.h" | |
| #include "path_delay2.h" | |
| #include "net_delay.h" | |
| #include "vpr_utils.h" | |
| #include <assert.h> | |
| #include "read_xml_arch_file.h" | |
| /****************** Timing graph Structure ************************************ | |
| * * | |
| * In the timing graph I create, input pads and constant generators have no * | |
| * inputs; everything else has inputs. Every input pad and output pad is * | |
| * represented by two tnodes -- an input pin and an output pin. For an input * | |
| * pad the input pin comes from off chip and has no fanin, while the output * | |
| * pin drives outpads and/or CBs. For output pads, the input node is driven * | |
| * by a CB or input pad, and the output node goes off chip and has no * | |
| * fanout (out-edges). I need two nodes to respresent things like pads * | |
| * because I mark all delay on tedges, not on tnodes. * | |
| * * | |
| * Every used (not OPEN) CB pin becomes a timing node. As well, every used * | |
| * subblock pin within a CB also becomes a timing node. Unused (OPEN) pins * | |
| * don't create any timing nodes. If a subblock is used in combinational mode * | |
| * (i.e. its clock pin is open), I just hook the subblock input tnodes to the * | |
| * subblock output tnode. If the subblock is used in sequential mode, I * | |
| * create two extra tnodes. One is just the subblock clock pin, which is * | |
| * connected to the subblock output. This means that FFs don't generate * | |
| * their output until their clock arrives. For global clocks coming from an * | |
| * input pad, the delay of the clock is 0, so the FFs generate their outputs * | |
| * at T = 0, as usual. For locally-generated or gated clocks, however, the * | |
| * clock will arrive later, and the FF output will be generated later. This * | |
| * lets me properly model things like ripple counters and gated clocks. The * | |
| * other extra node is the FF storage node (i.e. a sink), which connects to * | |
| * the subblock inputs and has no fanout. * | |
| * * | |
| * One other subblock that needs special attention is a constant generator. * | |
| * This has no used inputs, but its output is used. I create an extra tnode, * | |
| * a dummy input, in addition to the output pin tnode. The dummy tnode has * | |
| * no fanin. Since constant generators really generate their outputs at T = * | |
| * -infinity, I set the delay from the input tnode to the output to a large- * | |
| * magnitude negative number. This guarantees every block that needs the * | |
| * output of a constant generator sees it available very early. * | |
| * * | |
| * For this routine to work properly, subblocks whose outputs are unused must * | |
| * be completely empty -- all their input pins and their clock pin must be * | |
| * OPEN. Check_netlist checks the input netlist to guarantee this -- don't * | |
| * disable that check. * | |
| * * | |
| * NB: The discussion below is only relevant for circuits with multiple * | |
| * clocks. For circuits with a single clock, everything I do is exactly * | |
| * correct. * | |
| * * | |
| * A note about how I handle FFs: By hooking the clock pin up to the FF * | |
| * output, I properly model the time at which the FF generates its output. * | |
| * I don't do a completely rigorous job of modelling required arrival time at * | |
| * the FF input, however. I assume every FF and outpad needs its input at * | |
| * T = 0, which is when the earliest clock arrives. This can be conservative * | |
| * -- a fuller analysis would be to do a fast path analysis of the clock * | |
| * feeding each FF and subtract its earliest arrival time from the delay of * | |
| * the D signal to the FF input. This is too much work, so I'm not doing it. * | |
| * Alternatively, when one has N clocks, it might be better to just do N * | |
| * separate timing analyses, with only signals from FFs clocked on clock i * | |
| * being propagated forward on analysis i, and only FFs clocked on i being * | |
| * considered as sinks. This gives all the critical paths within clock * | |
| * domains, but ignores interactions. Instead, I assume all the clocks are * | |
| * more-or-less synchronized (they might be gated or locally-generated, but * | |
| * they all have the same frequency) and explore all interactions. Tough to * | |
| * say what's the better way. Since multiple clocks aren't important for my * | |
| * work, it's not worth bothering about much. * | |
| * * | |
| ******************************************************************************/ | |
| #define T_CONSTANT_GENERATOR -1000 /* Essentially -ve infinity */ | |
| /***************** Types local to this module ***************************/ | |
| enum e_subblock_pin_type | |
| { SUB_INPUT = 0, SUB_OUTPUT, SUB_CLOCK, NUM_SUB_PIN_TYPES }; | |
| /***************** Variables local to this module ***************************/ | |
| /* Variables for "chunking" the tedge memory. If the head pointer is NULL, * | |
| * no timing graph exists now. */ | |
| static struct s_linked_vptr *tedge_ch_list_head = NULL; | |
| static int tedge_ch_bytes_avail = 0; | |
| static char *tedge_ch_next_avail = NULL; | |
| static struct s_net *timing_nets = NULL; | |
| static int num_timing_nets = 0; | |
| /***************** Subroutines local to this module *************************/ | |
| static float **alloc_net_slack(); | |
| static void compute_net_slacks(float **net_slack); | |
| static void build_cb_tnodes(int iblk); | |
| static void alloc_and_load_tnodes(t_timing_inf timing_inf); | |
| static void alloc_and_load_tnodes_from_prepacked_netlist(float block_delay, float inter_cluster_net_delay); | |
| static void load_tnode(INP t_pb_graph_pin *pb_graph_pin, INP int iblock, INOUTP int *inode, INP t_timing_inf timing_inf); | |
| static void normalize_costs(float t_crit, long max_critical_input_paths, long max_critical_output_paths); | |
| static void print_primitive_as_blif (FILE *fpout, int iblk); | |
| /********************* Subroutine definitions *******************************/ | |
| float ** | |
| alloc_and_load_timing_graph(t_timing_inf timing_inf) | |
| { | |
| /* This routine builds the graph used for timing analysis. Every cb pin is a | |
| * timing node (tnode). The connectivity between pins is * | |
| * represented by timing edges (tedges). All delay is marked on edges, not * | |
| * on nodes. This routine returns an array that will store slack values: * | |
| * net_slack[0..num_nets-1][1..num_pins-1]. */ | |
| /* The are below are valid only for CBs, not pads. */ | |
| /* Array for mapping from a pin on a block to a tnode index. For pads, only * | |
| * the first two pin locations are used (input to pad is first, output of * | |
| * pad is second). For CLBs, all OPEN pins on the cb have their mapping * | |
| * set to OPEN so I won't use it by mistake. */ | |
| int num_sinks; | |
| float **net_slack; /* [0..num_nets-1][1..num_pins-1]. */ | |
| /************* End of variable declarations ********************************/ | |
| if(tedge_ch_list_head != NULL) | |
| { | |
| printf("Error in alloc_and_load_timing_graph:\n" | |
| "\tAn old timing graph still exists.\n"); | |
| exit(1); | |
| } | |
| num_timing_nets = num_nets; | |
| timing_nets = clb_net; | |
| alloc_and_load_tnodes(timing_inf); | |
| num_sinks = alloc_and_load_timing_graph_levels(); | |
| #ifdef CREATE_ECHO_FILES | |
| print_timing_graph("initial_timing_graph.echo"); | |
| #endif | |
| check_timing_graph(num_sinks); | |
| net_slack = alloc_net_slack(); | |
| return (net_slack); | |
| } | |
| float** alloc_and_load_pre_packing_timing_graph(float block_delay, float inter_cluster_net_delay, t_model *models) | |
| { | |
| /* This routine builds the graph used for timing analysis. Every technology mapped netlist pin is a | |
| * timing node (tnode). The connectivity between pins is * | |
| * represented by timing edges (tedges). All delay is marked on edges, not * | |
| * on nodes. This routine returns an array that will store slack values: * | |
| * net_slack[0..num_nets-1][1..num_pins-1]. */ | |
| /* The are below are valid only for CBs, not pads. */ | |
| /* Array for mapping from a pin on a block to a tnode index. For pads, only * | |
| * the first two pin locations are used (input to pad is first, output of * | |
| * pad is second). For CLBs, all OPEN pins on the cb have their mapping * | |
| * set to OPEN so I won't use it by mistake. */ | |
| int num_sinks; | |
| float **net_slack; | |
| /************* End of variable declarations ********************************/ | |
| if(tedge_ch_list_head != NULL) | |
| { | |
| printf("Error in alloc_and_load_timing_graph:\n" | |
| "\tAn old timing graph still exists.\n"); | |
| exit(1); | |
| } | |
| num_timing_nets = num_logical_nets; | |
| timing_nets = vpack_net; | |
| alloc_and_load_tnodes_from_prepacked_netlist(block_delay, inter_cluster_net_delay); | |
| num_sinks = alloc_and_load_timing_graph_levels(); | |
| net_slack = alloc_net_slack(); | |
| #ifdef CREATE_ECHO_FILES | |
| print_timing_graph("pre_packing_timing_graph.echo"); | |
| print_timing_graph_as_blif("pre_packing_timing_graph_as_blif.blif", models); | |
| #endif | |
| check_timing_graph(num_sinks); | |
| return net_slack; | |
| } | |
| static float ** | |
| alloc_net_slack() | |
| { | |
| /* Allocates the net_slack structure. Chunk allocated to save space. */ | |
| float **net_slack; /* [0..num_nets-1][1..num_pins-1] */ | |
| int inet, j; | |
| net_slack = (float **)my_malloc(num_timing_nets * sizeof(float *)); | |
| for(inet = 0; inet < num_timing_nets; inet++) | |
| { | |
| net_slack[inet] = (float *)my_chunk_malloc((timing_nets[inet].num_sinks + 1) * | |
| sizeof(float), &tedge_ch_list_head, | |
| &tedge_ch_bytes_avail, | |
| &tedge_ch_next_avail); | |
| for(j = 0; j <= timing_nets[inet].num_sinks; j++) { | |
| net_slack[inet][j] = UNDEFINED; | |
| } | |
| } | |
| return (net_slack); | |
| } | |
| void | |
| load_timing_graph_net_delays(float **net_delay) | |
| { | |
| /* Sets the delays of the inter-CLB nets to the values specified by * | |
| * net_delay[0..num_nets-1][1..num_pins-1]. These net delays should have * | |
| * been allocated and loaded with the net_delay routines. This routine * | |
| * marks the corresponding edges in the timing graph with the proper delay. */ | |
| int inet, ipin, inode; | |
| t_tedge *tedge; | |
| for(inet = 0; inet < num_timing_nets; inet++) | |
| { | |
| inode = net_to_driver_tnode[inet]; | |
| tedge = tnode[inode].out_edges; | |
| /* Note that the edges of a tnode corresponding to a CLB or INPAD opin must * | |
| * be in the same order as the pins of the net driven by the tnode. */ | |
| for(ipin = 1; ipin < (timing_nets[inet].num_sinks + 1); ipin++) | |
| tedge[ipin - 1].Tdel = net_delay[inet][ipin]; | |
| } | |
| } | |
| void | |
| free_timing_graph(float **net_slack) | |
| { | |
| /* Frees the timing graph data. */ | |
| if(tedge_ch_list_head == NULL) | |
| { | |
| printf("Error in free_timing_graph: No timing graph to free.\n"); | |
| exit(1); | |
| } | |
| free_chunk_memory(tedge_ch_list_head); | |
| free(tnode); | |
| free(net_to_driver_tnode); | |
| free_ivec_vector(tnodes_at_level, 0, num_tnode_levels - 1); | |
| free(net_slack); | |
| tedge_ch_list_head = NULL; | |
| tedge_ch_bytes_avail = 0; | |
| tedge_ch_next_avail = NULL; | |
| tnode = NULL; | |
| num_tnodes = 0; | |
| net_to_driver_tnode = NULL; | |
| tnodes_at_level = NULL; | |
| num_tnode_levels = 0; | |
| } | |
| void | |
| print_net_slack(char *fname, | |
| float **net_slack) | |
| { | |
| /* Prints the net slacks into a file. */ | |
| int inet, ipin; | |
| FILE *fp; | |
| fp = my_fopen(fname, "w", 0); | |
| fprintf(fp, "Net #\tSlacks\n\n"); | |
| for(inet = 0; inet < num_timing_nets; inet++) | |
| { | |
| fprintf(fp, "%5d", inet); | |
| for(ipin = 1; ipin < (timing_nets[inet].num_sinks + 1); ipin++) | |
| { | |
| fprintf(fp, "\t%g", net_slack[inet][ipin]); | |
| } | |
| fprintf(fp, "\n"); | |
| } | |
| } | |
| /* Count # of tnodes, allocates space, and loads the tnodes and its associated edges */ | |
| static void alloc_and_load_tnodes(t_timing_inf timing_inf) { | |
| int i, j, k; | |
| int inode; | |
| int num_nodes_in_block; | |
| int count; | |
| int iblock, irr_node; | |
| int inet, dport, dpin, dblock, dnode; | |
| int normalized_pin, normalization; | |
| t_pb_graph_pin *ipb_graph_pin; | |
| t_rr_node *local_rr_graph, *d_rr_graph; | |
| net_to_driver_tnode = my_malloc(num_timing_nets * sizeof(int)); | |
| for(i = 0; i < num_timing_nets; i++) { | |
| net_to_driver_tnode[i] = OPEN; | |
| } | |
| /* allocate space for tnodes */ | |
| num_tnodes = 0; | |
| for(i = 0; i < num_blocks; i++) { | |
| num_nodes_in_block = 0; | |
| for(j = 0; j < block[i].pb->pb_graph_node->total_pb_pins; j++) { | |
| if(block[i].pb->rr_graph[j].net_num != OPEN) { | |
| if(block[i].pb->rr_graph[j].pb_graph_pin->type == PB_PIN_INPAD || | |
| block[i].pb->rr_graph[j].pb_graph_pin->type == PB_PIN_OUTPAD || | |
| block[i].pb->rr_graph[j].pb_graph_pin->type == PB_PIN_SEQUENTIAL) { | |
| num_nodes_in_block += 2; | |
| } else { | |
| num_nodes_in_block++; | |
| } | |
| } | |
| } | |
| num_tnodes += num_nodes_in_block; | |
| } | |
| tnode = my_calloc(num_tnodes, sizeof(t_tnode)); | |
| /* load tnodes with all info except edge info */ | |
| /* populate tnode lookups for edge info */ | |
| inode = 0; | |
| for(i = 0; i < num_blocks; i++) { | |
| for(j = 0; j < block[i].pb->pb_graph_node->total_pb_pins; j++) { | |
| if(block[i].pb->rr_graph[j].net_num != OPEN) { | |
| assert(tnode[inode].pb_graph_pin == NULL); | |
| load_tnode(block[i].pb->rr_graph[j].pb_graph_pin, i, &inode, timing_inf); | |
| } | |
| } | |
| } | |
| assert(inode == num_tnodes); | |
| /* load edge delays */ | |
| for(i = 0; i < num_tnodes; i++) { | |
| /* 3 primary scenarios for edge delays | |
| 1. Point-to-point delays inside block | |
| 2. | |
| */ | |
| count = 0; | |
| iblock = tnode[i].block; | |
| switch (tnode[i].type) { | |
| case INPAD_OPIN: | |
| case INTERMEDIATE_NODE: | |
| case PRIMITIVE_OPIN: | |
| case FF_OPIN: | |
| case CB_IPIN: | |
| /* fanout is determined by intra-cluster connections */ | |
| /* Allocate space for edges */ | |
| irr_node = tnode[i].pb_graph_pin->pin_count_in_cluster; | |
| local_rr_graph = block[iblock].pb->rr_graph; | |
| ipb_graph_pin = local_rr_graph[irr_node].pb_graph_pin; | |
| if(ipb_graph_pin->parent_node->pb_type->max_internal_delay != UNDEFINED) { | |
| if(pb_max_internal_delay == UNDEFINED) { | |
| pb_max_internal_delay = ipb_graph_pin->parent_node->pb_type->max_internal_delay; | |
| pbtype_max_internal_delay = ipb_graph_pin->parent_node->pb_type; | |
| } else if(pb_max_internal_delay < ipb_graph_pin->parent_node->pb_type->max_internal_delay) { | |
| pb_max_internal_delay = ipb_graph_pin->parent_node->pb_type->max_internal_delay; | |
| pbtype_max_internal_delay = ipb_graph_pin->parent_node->pb_type; | |
| } | |
| } | |
| for(j = 0; j < block[iblock].pb->rr_graph[irr_node].num_edges; j++) { | |
| dnode = local_rr_graph[irr_node].edges[j]; | |
| if((local_rr_graph[dnode].prev_node == irr_node) && (j == local_rr_graph[dnode].prev_edge)) { | |
| count++; | |
| } | |
| } | |
| assert(count > 0); | |
| tnode[i].num_edges = count; | |
| tnode[i].out_edges = (t_tedge *) my_chunk_malloc(count * | |
| sizeof(t_tedge), | |
| &tedge_ch_list_head, | |
| &tedge_ch_bytes_avail, | |
| &tedge_ch_next_avail); | |
| /* Load edges */ | |
| count = 0; | |
| for(j = 0; j < local_rr_graph[irr_node].num_edges; j++) { | |
| dnode = local_rr_graph[irr_node].edges[j]; | |
| if((local_rr_graph[dnode].prev_node == irr_node) && (j == local_rr_graph[dnode].prev_edge)) { | |
| assert((ipb_graph_pin->output_edges[j]->num_output_pins == 1) && | |
| (local_rr_graph[ipb_graph_pin->output_edges[j]->output_pins[0]->pin_count_in_cluster].net_num == local_rr_graph[irr_node].net_num)); | |
| tnode[i].out_edges[count].Tdel = ipb_graph_pin->output_edges[j]->delay_max; | |
| tnode[i].out_edges[count].to_node = local_rr_graph[dnode].tnode->index; | |
| if(vpack_net[local_rr_graph[irr_node].net_num].is_const_gen == TRUE && tnode[i].type == PRIMITIVE_OPIN) { | |
| tnode[i].out_edges[count].Tdel = T_CONSTANT_GENERATOR; | |
| tnode[i].type = CONSTANT_GEN_SOURCE; | |
| } | |
| count++; | |
| } | |
| } | |
| assert(count > 0); | |
| break; | |
| case PRIMITIVE_IPIN: | |
| /* Pin info comes from pb_graph block delays | |
| */ | |
| irr_node = tnode[i].pb_graph_pin->pin_count_in_cluster; | |
| local_rr_graph = block[iblock].pb->rr_graph; | |
| ipb_graph_pin = local_rr_graph[irr_node].pb_graph_pin; | |
| tnode[i].num_edges = ipb_graph_pin->num_pin_timing; | |
| tnode[i].out_edges = (t_tedge *) my_chunk_malloc(ipb_graph_pin->num_pin_timing * | |
| sizeof(t_tedge), | |
| &tedge_ch_list_head, | |
| &tedge_ch_bytes_avail, | |
| &tedge_ch_next_avail); | |
| k = 0; | |
| for(j = 0; j < tnode[i].num_edges; j++) { | |
| /* Some outpins aren't used, ignore these. Only consider output pins that are used */ | |
| if(local_rr_graph[ipb_graph_pin->pin_timing[j]->pin_count_in_cluster].net_num != OPEN) { | |
| tnode[i].out_edges[k].Tdel = ipb_graph_pin->pin_timing_del_max[j]; | |
| tnode[i].out_edges[k].to_node = local_rr_graph[ipb_graph_pin->pin_timing[j]->pin_count_in_cluster].tnode->index; | |
| assert(tnode[i].out_edges[k].to_node != OPEN); | |
| k++; | |
| } | |
| } | |
| tnode[i].num_edges -= (j - k); /* remove unused edges */ | |
| if(tnode[i].num_edges == 0) { | |
| printf(ERRTAG "No timing information for pin %s.%s[%d]\n", tnode[i].pb_graph_pin->parent_node->pb_type->name, tnode[i].pb_graph_pin->port->name,tnode[i].pb_graph_pin->pin_number); | |
| exit(1); | |
| } | |
| break; | |
| case CB_OPIN: | |
| /* load up net info */ | |
| irr_node = tnode[i].pb_graph_pin->pin_count_in_cluster; | |
| local_rr_graph = block[iblock].pb->rr_graph; | |
| ipb_graph_pin = local_rr_graph[irr_node].pb_graph_pin; | |
| assert(local_rr_graph[irr_node].net_num != OPEN); | |
| inet = vpack_to_clb_net_mapping[local_rr_graph[irr_node].net_num]; | |
| assert(inet != OPEN); | |
| net_to_driver_tnode[inet] = i; | |
| tnode[i].num_edges = clb_net[inet].num_sinks; | |
| tnode[i].out_edges = (t_tedge *) my_chunk_malloc(clb_net[inet].num_sinks * | |
| sizeof(t_tedge), | |
| &tedge_ch_list_head, | |
| &tedge_ch_bytes_avail, | |
| &tedge_ch_next_avail); | |
| for(j = 1; j <= clb_net[inet].num_sinks; j++) { | |
| dblock = clb_net[inet].node_block[j]; | |
| normalization = block[dblock].type->num_pins / block[dblock].type->capacity; | |
| normalized_pin = clb_net[inet].node_block_pin[j] % normalization; | |
| d_rr_graph = block[dblock].pb->rr_graph; | |
| dpin = OPEN; | |
| dport = OPEN; | |
| count = 0; | |
| for(k = 0; k < block[dblock].pb->pb_graph_node->num_input_ports && dpin == OPEN; k++) { | |
| if(normalized_pin >= count && (count + block[dblock].pb->pb_graph_node->num_input_pins[k] > normalized_pin)) { | |
| dpin = normalized_pin - count; | |
| dport = k; | |
| break; | |
| } | |
| count += block[dblock].pb->pb_graph_node->num_input_pins[k]; | |
| } | |
| if(dpin == OPEN) { | |
| for(k = 0; k < block[dblock].pb->pb_graph_node->num_output_ports && dpin == OPEN; k++) { | |
| count += block[dblock].pb->pb_graph_node->num_output_pins[k]; | |
| } | |
| for(k = 0; k < block[dblock].pb->pb_graph_node->num_clock_ports && dpin == OPEN; k++) { | |
| if(normalized_pin >= count && (count + block[dblock].pb->pb_graph_node->num_clock_pins[k] > normalized_pin)) { | |
| dpin = normalized_pin - count; | |
| dport = k; | |
| } | |
| count += block[dblock].pb->pb_graph_node->num_clock_pins[k]; | |
| } | |
| assert(dpin != OPEN); | |
| assert(inet == vpack_to_clb_net_mapping[d_rr_graph[block[dblock].pb->pb_graph_node->clock_pins[dport][dpin].pin_count_in_cluster].net_num]); | |
| tnode[i].out_edges[j-1].to_node = d_rr_graph[block[dblock].pb->pb_graph_node->clock_pins[dport][dpin].pin_count_in_cluster].tnode->index; | |
| } else { | |
| assert(dpin != OPEN); | |
| assert(inet == vpack_to_clb_net_mapping[d_rr_graph[block[dblock].pb->pb_graph_node->input_pins[dport][dpin].pin_count_in_cluster].net_num]); | |
| /* delays are assigned post routing */ | |
| tnode[i].out_edges[j-1].to_node = d_rr_graph[block[dblock].pb->pb_graph_node->input_pins[dport][dpin].pin_count_in_cluster].tnode->index; | |
| } | |
| assert(inet != OPEN); | |
| } | |
| break; | |
| case OUTPAD_IPIN: | |
| case INPAD_SOURCE: | |
| case OUTPAD_SINK: | |
| case FF_SINK: | |
| case FF_SOURCE: | |
| case FF_IPIN: | |
| break; | |
| default: | |
| printf(ERRTAG "Consistency check failed: Unknown tnode type %d\n", tnode[i].type); | |
| assert(0); | |
| break; | |
| } | |
| } | |
| } | |
| /* Allocate timing graph for pre packed netlist | |
| Count number of tnodes first | |
| Then connect up tnodes with edges | |
| */ | |
| static void alloc_and_load_tnodes_from_prepacked_netlist(float block_delay, float inter_cluster_net_delay) { | |
| int i, j, k; | |
| t_model *model; | |
| t_model_ports *model_port; | |
| int inode, inet, iblock; | |
| int incr; | |
| int count; | |
| net_to_driver_tnode = my_malloc(num_logical_nets * sizeof(int)); | |
| for(i = 0; i < num_logical_nets; i++) { | |
| net_to_driver_tnode[i] = OPEN; | |
| } | |
| /* allocate space for tnodes */ | |
| num_tnodes = 0; | |
| for(i = 0; i < num_logical_blocks; i++) { | |
| model = logical_block[i].model; | |
| logical_block[i].clock_net_tnode = NULL; | |
| if(logical_block[i].type == VPACK_INPAD) { | |
| logical_block[i].output_net_tnodes = my_calloc(1, sizeof(t_tnode**)); | |
| num_tnodes += 2; | |
| } else if(logical_block[i].type == VPACK_OUTPAD) { | |
| logical_block[i].input_net_tnodes = my_calloc(1, sizeof(t_tnode**)); | |
| num_tnodes += 2; | |
| } else { | |
| if(logical_block[i].clock_net == OPEN) { | |
| incr = 1; | |
| } else { | |
| incr = 2; | |
| } | |
| j = 0; | |
| model_port = model->inputs; | |
| while(model_port) { | |
| if(model_port->is_clock == FALSE) { | |
| for(k = 0; k < model_port->size; k++) { | |
| if(logical_block[i].input_nets[j][k] != OPEN) { | |
| num_tnodes += incr; | |
| } | |
| } | |
| j++; | |
| } else { | |
| num_tnodes++; | |
| } | |
| model_port = model_port->next; | |
| } | |
| logical_block[i].input_net_tnodes = my_calloc(j, sizeof(t_tnode**)); | |
| j = 0; | |
| model_port = model->outputs; | |
| while(model_port) { | |
| for(k = 0; k < model_port->size; k++) { | |
| if(logical_block[i].output_nets[j][k] != OPEN) { | |
| num_tnodes += incr; | |
| } | |
| } | |
| j++; | |
| model_port = model_port->next; | |
| } | |
| logical_block[i].output_net_tnodes = my_calloc(j, sizeof(t_tnode**)); | |
| } | |
| } | |
| tnode = my_calloc(num_tnodes, sizeof(t_tnode)); | |
| for(i = 0; i < num_tnodes; i++) { | |
| tnode[i].index = i; | |
| } | |
| /* load tnodes, alloc edges for tnodes, load all known tnodes */ | |
| inode = 0; | |
| for(i = 0; i < num_logical_blocks; i++) { | |
| model = logical_block[i].model; | |
| if(logical_block[i].type == VPACK_INPAD) { | |
| logical_block[i].output_net_tnodes[0] = my_calloc(1, sizeof(t_tnode*)); | |
| logical_block[i].output_net_tnodes[0][0] = &tnode[inode]; | |
| net_to_driver_tnode[logical_block[i].output_nets[0][0]] = inode; | |
| tnode[inode].model_pin = 0; | |
| tnode[inode].model_port = 0; | |
| tnode[inode].block = i; | |
| tnode[inode].type = INPAD_OPIN; | |
| tnode[inode].num_edges = vpack_net[logical_block[i].output_nets[0][0]].num_sinks; | |
| tnode[inode].out_edges = | |
| (t_tedge *) my_chunk_malloc(tnode[inode].num_edges * | |
| sizeof(t_tedge), | |
| &tedge_ch_list_head, | |
| &tedge_ch_bytes_avail, | |
| &tedge_ch_next_avail); | |
| tnode[inode + 1].num_edges = 1; | |
| tnode[inode + 1].out_edges = | |
| (t_tedge *) my_chunk_malloc(1 * | |
| sizeof(t_tedge), | |
| &tedge_ch_list_head, | |
| &tedge_ch_bytes_avail, | |
| &tedge_ch_next_avail); | |
| tnode[inode + 1].out_edges->Tdel = 0; | |
| tnode[inode + 1].out_edges->to_node = inode; | |
| tnode[inode + 1].T_req = 0; | |
| tnode[inode + 1].T_arr = 0; | |
| tnode[inode + 1].type = INPAD_SOURCE; | |
| tnode[inode + 1].block = i; | |
| inode += 2; | |
| } else if (logical_block[i].type == VPACK_OUTPAD) { | |
| logical_block[i].input_net_tnodes[0] = my_calloc(1, sizeof(t_tnode*)); | |
| logical_block[i].input_net_tnodes[0][0] = &tnode[inode]; | |
| tnode[inode].model_pin = 0; | |
| tnode[inode].model_port = 0; | |
| tnode[inode].block = i; | |
| tnode[inode].type = OUTPAD_IPIN; | |
| tnode[inode].num_edges = 1; | |
| tnode[inode].out_edges = | |
| (t_tedge *) my_chunk_malloc(1 * | |
| sizeof(t_tedge), | |
| &tedge_ch_list_head, | |
| &tedge_ch_bytes_avail, | |
| &tedge_ch_next_avail); | |
| tnode[inode].out_edges->Tdel = 0; | |
| tnode[inode].out_edges->to_node = inode + 1; | |
| tnode[inode + 1].T_req = 0; | |
| tnode[inode + 1].T_arr = 0; | |
| tnode[inode + 1].type = OUTPAD_SINK; | |
| tnode[inode + 1].block = i; | |
| tnode[inode + 1].num_edges = 0; | |
| tnode[inode + 1].out_edges = NULL; | |
| tnode[inode + 1].index = inode + 1; | |
| inode += 2; | |
| } else { | |
| j = 0; | |
| model_port = model->outputs; | |
| count = 0; | |
| while(model_port) { | |
| logical_block[i].output_net_tnodes[j] = my_calloc(model_port->size, sizeof(t_tnode*)); | |
| for(k = 0; k < model_port->size; k++) { | |
| if(logical_block[i].output_nets[j][k] != OPEN) { | |
| count++; | |
| tnode[inode].model_pin = k; | |
| tnode[inode].model_port = j; | |
| tnode[inode].block = i; | |
| net_to_driver_tnode[logical_block[i].output_nets[j][k]] = inode; | |
| logical_block[i].output_net_tnodes[j][k] = &tnode[inode]; | |
| tnode[inode].num_edges = vpack_net[logical_block[i].output_nets[j][k]].num_sinks; | |
| tnode[inode].out_edges = | |
| (t_tedge *) my_chunk_malloc(tnode[inode].num_edges * | |
| sizeof(t_tedge), | |
| &tedge_ch_list_head, | |
| &tedge_ch_bytes_avail, | |
| &tedge_ch_next_avail); | |
| if (logical_block[i].clock_net == OPEN) { | |
| tnode[inode].type = PRIMITIVE_OPIN; | |
| inode++; | |
| } else { | |
| tnode[inode].type = FF_OPIN; | |
| tnode[inode + 1].num_edges = 1; | |
| tnode[inode + 1].out_edges = | |
| (t_tedge *) my_chunk_malloc(1 * | |
| sizeof(t_tedge), | |
| &tedge_ch_list_head, | |
| &tedge_ch_bytes_avail, | |
| &tedge_ch_next_avail); | |
| tnode[inode + 1].out_edges->to_node = inode; | |
| tnode[inode + 1].out_edges->Tdel = 0; | |
| tnode[inode + 1].type = FF_SOURCE; | |
| tnode[inode + 1].block = i; | |
| inode += 2; | |
| } | |
| } | |
| } | |
| j++; | |
| model_port = model_port->next; | |
| } | |
| j = 0; | |
| model_port = model->inputs; | |
| while(model_port) { | |
| if(model_port->is_clock == FALSE) { | |
| logical_block[i].input_net_tnodes[j] = my_calloc(model_port->size, sizeof(t_tnode*)); | |
| for(k = 0; k < model_port->size; k++) { | |
| if(logical_block[i].input_nets[j][k] != OPEN) { | |
| tnode[inode].model_pin = k; | |
| tnode[inode].model_port = j; | |
| tnode[inode].block = i; | |
| logical_block[i].input_net_tnodes[j][k] = &tnode[inode]; | |
| if (logical_block[i].clock_net == OPEN) { | |
| tnode[inode].type = PRIMITIVE_IPIN; | |
| tnode[inode].out_edges = | |
| (t_tedge *) my_chunk_malloc(count * | |
| sizeof(t_tedge), | |
| &tedge_ch_list_head, | |
| &tedge_ch_bytes_avail, | |
| &tedge_ch_next_avail); | |
| tnode[inode].num_edges = count; | |
| inode++; | |
| } else { | |
| tnode[inode].type = FF_IPIN; | |
| tnode[inode].num_edges = 1; | |
| tnode[inode].out_edges = | |
| (t_tedge *) my_chunk_malloc(1 * | |
| sizeof(t_tedge), | |
| &tedge_ch_list_head, | |
| &tedge_ch_bytes_avail, | |
| &tedge_ch_next_avail); | |
| tnode[inode].out_edges->to_node = inode + 1; | |
| tnode[inode].out_edges->Tdel = 0; | |
| tnode[inode + 1].type = FF_SINK; | |
| tnode[inode + 1].num_edges = 0; | |
| tnode[inode + 1].out_edges = NULL; | |
| inode += 2; | |
| } | |
| } | |
| } | |
| j++; | |
| } else { | |
| if(logical_block[i].clock_net != OPEN) { | |
| assert(logical_block[i].clock_net_tnode == NULL); | |
| logical_block[i].clock_net_tnode = &tnode[inode]; | |
| tnode[inode].block = i; | |
| tnode[inode].model_pin = 0; | |
| tnode[inode].model_port = 0; | |
| tnode[inode].num_edges = 0; | |
| tnode[inode].out_edges = NULL; | |
| tnode[inode].type = FF_SINK; | |
| inode++; | |
| } | |
| } | |
| model_port = model_port->next; | |
| } | |
| } | |
| } | |
| assert(inode == num_tnodes); | |
| /* load edge delays */ | |
| for(i = 0; i < num_tnodes; i++) { | |
| /* 3 primary scenarios for edge delays | |
| 1. Point-to-point delays inside block | |
| 2. | |
| */ | |
| count = 0; | |
| iblock = tnode[i].block; | |
| switch (tnode[i].type) { | |
| case INPAD_OPIN: | |
| case PRIMITIVE_OPIN: | |
| case FF_OPIN: | |
| /* fanout is determined by intra-cluster connections */ | |
| /* Allocate space for edges */ | |
| inet = logical_block[tnode[i].block].output_nets[tnode[i].model_port][tnode[i].model_pin]; | |
| assert(inet != OPEN); | |
| for(j = 1; j <= vpack_net[inet].num_sinks; j++) { | |
| if(vpack_net[inet].is_const_gen) { | |
| tnode[i].out_edges[j - 1].Tdel = T_CONSTANT_GENERATOR; | |
| tnode[i].type = CONSTANT_GEN_SOURCE; | |
| } else { | |
| tnode[i].out_edges[j - 1].Tdel = inter_cluster_net_delay; | |
| } | |
| assert(logical_block[vpack_net[inet].node_block[j]].input_net_tnodes[vpack_net[inet].node_block_port[j]][vpack_net[inet].node_block_pin[j]] != NULL); | |
| if(vpack_net[inet].is_global) { | |
| assert(logical_block[vpack_net[inet].node_block[j]].clock_net == inet); | |
| tnode[i].out_edges[j - 1].to_node = logical_block[vpack_net[inet].node_block[j]].clock_net_tnode->index; | |
| } else { | |
| tnode[i].out_edges[j - 1].to_node = logical_block[vpack_net[inet].node_block[j]].input_net_tnodes[vpack_net[inet].node_block_port[j]][vpack_net[inet].node_block_pin[j]]->index; | |
| } | |
| } | |
| assert(tnode[i].num_edges == vpack_net[inet].num_sinks); | |
| break; | |
| case PRIMITIVE_IPIN: | |
| model_port = logical_block[iblock].model->outputs; | |
| count = 0; | |
| j = 0; | |
| while(model_port) { | |
| for(k = 0; k < model_port->size; k++) { | |
| if(logical_block[iblock].output_nets[j][k] != OPEN) { | |
| tnode[i].out_edges[count].Tdel = block_delay; | |
| tnode[i].out_edges[count].to_node = logical_block[iblock].output_net_tnodes[j][k]->index; | |
| count++; | |
| } | |
| else { | |
| assert(logical_block[iblock].output_net_tnodes[j][k] == NULL); | |
| } | |
| } | |
| j++; | |
| model_port = model_port->next; | |
| } | |
| assert(count == tnode[i].num_edges); | |
| break; | |
| case OUTPAD_IPIN: | |
| case INPAD_SOURCE: | |
| case OUTPAD_SINK: | |
| case FF_SINK: | |
| case FF_SOURCE: | |
| case FF_IPIN: | |
| break; | |
| default: | |
| printf(ERRTAG "Consistency check failed: Unknown tnode type %d\n", tnode[i].type); | |
| assert(0); | |
| break; | |
| } | |
| } | |
| for(i = 0; i < num_logical_nets; i++) { | |
| assert(net_to_driver_tnode[i] != OPEN); | |
| } | |
| } | |
| static void load_tnode(INP t_pb_graph_pin *pb_graph_pin, INP int iblock, INOUTP int *inode, INP t_timing_inf timing_inf) { | |
| int i; | |
| i = *inode; | |
| tnode[i].pb_graph_pin = pb_graph_pin; | |
| tnode[i].block = iblock; | |
| tnode[i].index = i; | |
| block[iblock].pb->rr_graph[pb_graph_pin->pin_count_in_cluster].tnode = &tnode[i]; | |
| if(tnode[i].pb_graph_pin->parent_node->pb_type->blif_model == NULL) { | |
| assert(tnode[i].pb_graph_pin->type == PB_PIN_NORMAL); | |
| if(tnode[i].pb_graph_pin->parent_node->parent_pb_graph_node == NULL) { | |
| if(tnode[i].pb_graph_pin->port->type == IN_PORT) { | |
| tnode[i].type = CB_IPIN; | |
| } else { | |
| assert(tnode[i].pb_graph_pin->port->type == OUT_PORT); | |
| tnode[i].type = CB_OPIN; | |
| } | |
| } else { | |
| tnode[i].type = INTERMEDIATE_NODE; | |
| } | |
| } else { | |
| if(tnode[i].pb_graph_pin->type == PB_PIN_INPAD) { | |
| assert(tnode[i].pb_graph_pin->port->type == OUT_PORT); | |
| tnode[i].type = INPAD_OPIN; | |
| tnode[i + 1].num_edges = 1; | |
| tnode[i + 1].out_edges = | |
| (t_tedge *) my_chunk_malloc(1 * | |
| sizeof(t_tedge), | |
| &tedge_ch_list_head, | |
| &tedge_ch_bytes_avail, | |
| &tedge_ch_next_avail); | |
| tnode[i + 1].out_edges->Tdel = 0; | |
| tnode[i + 1].out_edges->to_node = i; | |
| tnode[i + 1].pb_graph_pin = NULL; | |
| tnode[i + 1].T_req = 0; | |
| tnode[i + 1].T_arr = 0; | |
| tnode[i + 1].type = INPAD_SOURCE; | |
| tnode[i + 1].block = iblock; | |
| tnode[i + 1].index = i + 1; | |
| (*inode)++; | |
| } else if(tnode[i].pb_graph_pin->type == PB_PIN_OUTPAD) { | |
| assert(tnode[i].pb_graph_pin->port->type == IN_PORT); | |
| tnode[i].type = OUTPAD_IPIN; | |
| tnode[i].num_edges = 1; | |
| tnode[i].out_edges = | |
| (t_tedge *) my_chunk_malloc(1 * | |
| sizeof(t_tedge), | |
| &tedge_ch_list_head, | |
| &tedge_ch_bytes_avail, | |
| &tedge_ch_next_avail); | |
| tnode[i].out_edges->Tdel = 0; | |
| tnode[i].out_edges->to_node = i + 1; | |
| tnode[i + 1].pb_graph_pin = NULL; | |
| tnode[i + 1].T_req = 0; | |
| tnode[i + 1].T_arr = 0; | |
| tnode[i + 1].type = OUTPAD_SINK; | |
| tnode[i + 1].block = iblock; | |
| tnode[i + 1].num_edges = 0; | |
| tnode[i + 1].out_edges = NULL; | |
| tnode[i + 1].index = i + 1; | |
| (*inode)++; | |
| } else if(tnode[i].pb_graph_pin->type == PB_PIN_SEQUENTIAL) { | |
| if(tnode[i].pb_graph_pin->port->type == IN_PORT) { | |
| tnode[i].type = FF_IPIN; | |
| tnode[i].num_edges = 1; | |
| tnode[i].out_edges = (t_tedge *) my_chunk_malloc(1 * | |
| sizeof(t_tedge), | |
| &tedge_ch_list_head, | |
| &tedge_ch_bytes_avail, | |
| &tedge_ch_next_avail); | |
| tnode[i].out_edges->Tdel = pb_graph_pin->tsu_tco; | |
| tnode[i].out_edges->to_node = i + 1; | |
| tnode[i + 1].pb_graph_pin = NULL; | |
| tnode[i + 1].T_req = 0; | |
| tnode[i + 1].T_arr = 0; | |
| tnode[i + 1].type = FF_SINK; | |
| tnode[i + 1].block = iblock; | |
| tnode[i + 1].num_edges = 0; | |
| tnode[i + 1].out_edges = NULL; | |
| tnode[i + 1].index = i + 1; | |
| } else { | |
| assert(tnode[i].pb_graph_pin->port->type == OUT_PORT); | |
| tnode[i].type = FF_OPIN; | |
| tnode[i + 1].num_edges = 1; | |
| tnode[i + 1].out_edges = (t_tedge *) my_chunk_malloc(1 * | |
| sizeof(t_tedge), | |
| &tedge_ch_list_head, | |
| &tedge_ch_bytes_avail, | |
| &tedge_ch_next_avail); | |
| tnode[i + 1].out_edges->Tdel = pb_graph_pin->tsu_tco; | |
| tnode[i + 1].out_edges->to_node = i; | |
| tnode[i + 1].pb_graph_pin = NULL; | |
| tnode[i + 1].T_req = 0; | |
| tnode[i + 1].T_arr = 0; | |
| tnode[i + 1].type = FF_SOURCE; | |
| tnode[i + 1].block = iblock; | |
| tnode[i + 1].index = i + 1; | |
| } | |
| (*inode)++; | |
| } else if (tnode[i].pb_graph_pin->type == PB_PIN_CLOCK) { | |
| tnode[i].type = FF_SINK; | |
| tnode[i].num_edges = 0; | |
| tnode[i].out_edges = NULL; | |
| } else { | |
| if(tnode[i].pb_graph_pin->port->type == IN_PORT) { | |
| assert(tnode[i].pb_graph_pin->type == PB_PIN_TERMINAL); | |
| tnode[i].type = PRIMITIVE_IPIN; | |
| } else { | |
| assert(tnode[i].pb_graph_pin->port->type == OUT_PORT); | |
| assert(tnode[i].pb_graph_pin->type == PB_PIN_TERMINAL); | |
| tnode[i].type = PRIMITIVE_OPIN; | |
| } | |
| } | |
| } | |
| (*inode)++; | |
| } | |
| void | |
| print_timing_graph(char *fname) | |
| { | |
| /* Prints the timing graph into a file. */ | |
| FILE *fp; | |
| int inode, iedge, ilevel, i; | |
| t_tedge *tedge; | |
| t_tnode_type itype; | |
| char *tnode_type_names[] = { "INPAD_SOURCE", "INPAD_OPIN", "OUTPAD_IPIN", | |
| "OUTPAD_SINK", "CB_IPIN", "CB_OPIN", "INTERMEDIATE_NODE", "PRIMITIVE_IPIN", "PRIMITIVE_OPIN", | |
| "FF_IPIN", "FF_OPIN", | |
| "FF_SINK", "FF_SOURCE", | |
| "CONSTANT_GEN_SOURCE" | |
| }; | |
| fp = my_fopen(fname, "w", 0); | |
| fprintf(fp, "num_tnodes: %d\n", num_tnodes); | |
| fprintf(fp, "Node #\tType\t\tipin\tiblk\t# edges\t" | |
| "Edges (to_node, Tdel)\n\n"); | |
| for(inode = 0; inode < num_tnodes; inode++) | |
| { | |
| fprintf(fp, "%d\t", inode); | |
| itype = tnode[inode].type; | |
| fprintf(fp, "%-15.15s\t", tnode_type_names[itype]); | |
| if(tnode[inode].pb_graph_pin != NULL) { | |
| fprintf(fp, "%d\t%d\t", tnode[inode].pb_graph_pin->pin_count_in_cluster, | |
| tnode[inode].block); | |
| } else { | |
| fprintf(fp, "%d\t", tnode[inode].block); | |
| } | |
| fprintf(fp, "%d\t", tnode[inode].num_edges); | |
| tedge = tnode[inode].out_edges; | |
| for(iedge = 0; iedge < tnode[inode].num_edges; iedge++) | |
| { | |
| fprintf(fp, "\t(%4d,%7.3g)", tedge[iedge].to_node, | |
| tedge[iedge].Tdel); | |
| } | |
| fprintf(fp, "\n"); | |
| } | |
| fprintf(fp, "\n\nnum_tnode_levels: %d\n", num_tnode_levels); | |
| for(ilevel = 0; ilevel < num_tnode_levels; ilevel++) | |
| { | |
| fprintf(fp, "\n\nLevel: %d Num_nodes: %d\nNodes:", ilevel, | |
| tnodes_at_level[ilevel].nelem); | |
| for(i = 0; i < tnodes_at_level[ilevel].nelem; i++) | |
| fprintf(fp, "\t%d", tnodes_at_level[ilevel].list[i]); | |
| } | |
| fprintf(fp, "\n"); | |
| fprintf(fp, "\n\nNet #\tNet_to_driver_tnode\n"); | |
| for(i = 0; i < num_nets; i++) | |
| fprintf(fp, "%4d\t%6d\n", i, net_to_driver_tnode[i]); | |
| fprintf(fp, "\n\nNode #\t\tT_arr\t\tT_req\n\n"); | |
| for(inode = 0; inode < num_tnodes; inode++) { | |
| fprintf(fp, "%d\t%12g\t%12g\n", inode, tnode[inode].T_arr, | |
| tnode[inode].T_req); | |
| } | |
| fclose(fp); | |
| } | |
| float | |
| load_net_slack(float **net_slack, | |
| float target_cycle_time) | |
| { | |
| /* Determines the slack of every source-sink pair of block pins in the * | |
| * circuit. The timing graph must have already been built. target_cycle_ * | |
| * time is the target delay for the circuit -- if 0, the target_cycle_time * | |
| * is set to the critical path found in the timing graph. This routine * | |
| * loads net_slack, and returns the current critical path delay. */ | |
| float T_crit, T_arr, Tdel, T_cycle, T_req; | |
| int inode, ilevel, num_at_level, i, num_edges, iedge, to_node; | |
| int total; | |
| t_tedge *tedge; | |
| /* Reset all arrival times to -ve infinity. Can't just set to zero or the * | |
| * constant propagation (constant generators work at -ve infinity) won't * | |
| * work. */ | |
| long max_critical_input_paths = 0; | |
| long max_critical_output_paths = 0; | |
| for(inode = 0; inode < num_tnodes; inode++) | |
| tnode[inode].T_arr = T_CONSTANT_GENERATOR; | |
| /* Compute all arrival times with a breadth-first analysis from inputs to * | |
| * outputs. Also compute critical path (T_crit). */ | |
| T_crit = 0.; | |
| /* Primary inputs arrive at T = 0. */ | |
| num_at_level = tnodes_at_level[0].nelem; | |
| for(i = 0; i < num_at_level; i++) | |
| { | |
| inode = tnodes_at_level[0].list[i]; | |
| tnode[inode].T_arr = 0.; | |
| } | |
| total = 0; | |
| for(ilevel = 0; ilevel < num_tnode_levels; ilevel++) | |
| { | |
| num_at_level = tnodes_at_level[ilevel].nelem; | |
| total += num_at_level; | |
| for(i = 0; i < num_at_level; i++) | |
| { | |
| inode = tnodes_at_level[ilevel].list[i]; | |
| if(i == 0) { | |
| tnode[i].num_critical_input_paths = 1; | |
| } | |
| T_arr = tnode[inode].T_arr; | |
| num_edges = tnode[inode].num_edges; | |
| tedge = tnode[inode].out_edges; | |
| T_crit = max(T_crit, T_arr); | |
| for(iedge = 0; iedge < num_edges; iedge++) | |
| { | |
| to_node = tedge[iedge].to_node; | |
| Tdel = tedge[iedge].Tdel; | |
| /* Update number of near critical paths entering tnode */ | |
| /*check for approximate equality*/ | |
| if (fabs(tnode[to_node].T_arr - (T_arr + Tdel)) < EQUAL_DEF){ | |
| tnode[to_node].num_critical_input_paths += tnode[inode].num_critical_input_paths; | |
| } | |
| else if (tnode[to_node].T_arr < T_arr + Tdel) { | |
| tnode[to_node].num_critical_input_paths = tnode[inode].num_critical_input_paths; | |
| } | |
| if (tnode[to_node].num_critical_input_paths > max_critical_input_paths) | |
| max_critical_input_paths = tnode[to_node].num_critical_input_paths; | |
| tnode[to_node].T_arr = | |
| max(tnode[to_node].T_arr, T_arr + Tdel); | |
| } | |
| } | |
| } | |
| assert(total == num_tnodes); | |
| if(target_cycle_time > 0.) /* User specified target cycle time */ | |
| T_cycle = target_cycle_time; | |
| else /* Otherwise, target = critical path */ | |
| T_cycle = T_crit; | |
| /* Compute the required arrival times with a backward breadth-first analysis * | |
| * from sinks (output pads, etc.) to primary inputs. */ | |
| for(ilevel = num_tnode_levels - 1; ilevel >= 0; ilevel--) | |
| { | |
| num_at_level = tnodes_at_level[ilevel].nelem; | |
| for(i = 0; i < num_at_level; i++) | |
| { | |
| inode = tnodes_at_level[ilevel].list[i]; | |
| num_edges = tnode[inode].num_edges; | |
| if(ilevel == 0) { | |
| assert(tnode[inode].type == INPAD_SOURCE || tnode[inode].type == FF_SOURCE || tnode[inode].type == CONSTANT_GEN_SOURCE); | |
| } else { | |
| assert(!(tnode[inode].type == INPAD_SOURCE || tnode[inode].type == FF_SOURCE || tnode[inode].type == CONSTANT_GEN_SOURCE)); | |
| } | |
| if(num_edges == 0) | |
| { /* sink */ | |
| assert(tnode[inode].type == OUTPAD_SINK || tnode[inode].type == FF_SINK); | |
| tnode[inode].T_req = T_cycle; | |
| tnode[inode].num_critical_output_paths = 1; | |
| } | |
| else | |
| { | |
| assert(!(tnode[inode].type == OUTPAD_SINK || tnode[inode].type == FF_SINK)); | |
| tedge = tnode[inode].out_edges; | |
| to_node = tedge[0].to_node; | |
| Tdel = tedge[0].Tdel; | |
| T_req = tnode[to_node].T_req - Tdel; | |
| tnode[inode].num_critical_output_paths = tnode[to_node].num_critical_output_paths; | |
| if (tnode[to_node].num_critical_output_paths > max_critical_output_paths) | |
| max_critical_output_paths = tnode[to_node].num_critical_output_paths; | |
| for(iedge = 1; iedge < num_edges; iedge++) | |
| { | |
| to_node = tedge[iedge].to_node; | |
| Tdel = tedge[iedge].Tdel; | |
| /* Update number of near critical paths affected by output of tnode */ | |
| /*check for approximate equality*/ | |
| if (fabs(tnode[to_node].T_req - Tdel - T_req) < EQUAL_DEF){ | |
| tnode[inode].num_critical_output_paths += tnode[to_node].num_critical_output_paths; | |
| } | |
| else if (tnode[to_node].T_req - Tdel < T_req) { | |
| tnode[inode].num_critical_output_paths = tnode[to_node].num_critical_output_paths; | |
| } | |
| if (tnode[to_node].num_critical_output_paths > max_critical_output_paths) | |
| max_critical_output_paths = tnode[to_node].num_critical_output_paths; | |
| T_req = | |
| min(T_req, | |
| tnode[to_node].T_req - Tdel); | |
| } | |
| tnode[inode].T_req = T_req; | |
| } | |
| } | |
| } | |
| normalize_costs(T_crit, max_critical_input_paths, max_critical_output_paths); | |
| compute_net_slacks(net_slack); | |
| return (T_crit); | |
| } | |
| static void | |
| compute_net_slacks(float **net_slack) | |
| { | |
| /* Puts the slack of each source-sink pair of block pins in net_slack. */ | |
| int inet, iedge, inode, to_node, num_edges; | |
| t_tedge *tedge; | |
| float T_arr, Tdel, T_req; | |
| for(inet = 0; inet < num_timing_nets; inet++) | |
| { | |
| inode = net_to_driver_tnode[inet]; | |
| T_arr = tnode[inode].T_arr; | |
| num_edges = tnode[inode].num_edges; | |
| tedge = tnode[inode].out_edges; | |
| for(iedge = 0; iedge < num_edges; iedge++) | |
| { | |
| to_node = tedge[iedge].to_node; | |
| Tdel = tedge[iedge].Tdel; | |
| T_req = tnode[to_node].T_req; | |
| net_slack[inet][iedge + 1] = T_req - T_arr - Tdel; | |
| } | |
| } | |
| } | |
| void | |
| print_critical_path(char *fname) | |
| { | |
| /* Prints out the critical path to a file. */ | |
| t_linked_int *critical_path_head, *critical_path_node; | |
| FILE *fp; | |
| int non_global_nets_on_crit_path, global_nets_on_crit_path; | |
| int tnodes_on_crit_path, inode, iblk, inet; | |
| t_tnode_type type; | |
| float total_net_delay, total_logic_delay, Tdel; | |
| critical_path_head = allocate_and_load_critical_path(); | |
| critical_path_node = critical_path_head; | |
| fp = my_fopen(fname, "w", 0); | |
| non_global_nets_on_crit_path = 0; | |
| global_nets_on_crit_path = 0; | |
| tnodes_on_crit_path = 0; | |
| total_net_delay = 0.; | |
| total_logic_delay = 0.; | |
| while(critical_path_node != NULL) | |
| { | |
| Tdel = | |
| print_critical_path_node(fp, critical_path_node); | |
| inode = critical_path_node->data; | |
| type = tnode[inode].type; | |
| tnodes_on_crit_path++; | |
| if(type == CB_OPIN) | |
| { | |
| get_tnode_block_and_output_net(inode, &iblk, &inet); | |
| if(!timing_nets[inet].is_global) | |
| non_global_nets_on_crit_path++; | |
| else | |
| global_nets_on_crit_path++; | |
| total_net_delay += Tdel; | |
| } | |
| else | |
| { | |
| total_logic_delay += Tdel; | |
| } | |
| critical_path_node = critical_path_node->next; | |
| } | |
| fprintf(fp, | |
| "\nTnodes on crit. path: %d Non-global nets on crit. path: %d." | |
| "\n", tnodes_on_crit_path, non_global_nets_on_crit_path); | |
| fprintf(fp, "Global nets on crit. path: %d.\n", global_nets_on_crit_path); | |
| fprintf(fp, "Total logic delay: %g (s) Total net delay: %g (s)\n", | |
| total_logic_delay, total_net_delay); | |
| printf("Nets on crit. path: %d normal, %d global.\n", | |
| non_global_nets_on_crit_path, global_nets_on_crit_path); | |
| printf("Total logic delay: %g (s) Total net delay: %g (s)\n", | |
| total_logic_delay, total_net_delay); | |
| fclose(fp); | |
| free_int_list(&critical_path_head); | |
| } | |
| t_linked_int * | |
| allocate_and_load_critical_path(void) | |
| { | |
| /* Finds the critical path and puts a list of the tnodes on the critical * | |
| * path in a linked list, from the path SOURCE to the path SINK. */ | |
| t_linked_int *critical_path_head, *curr_crit_node, *prev_crit_node; | |
| int inode, iedge, to_node, num_at_level, i, crit_node, num_edges; | |
| float min_slack, slack; | |
| t_tedge *tedge; | |
| num_at_level = tnodes_at_level[0].nelem; | |
| min_slack = HUGE_FLOAT; | |
| crit_node = OPEN; /* Stops compiler warnings. */ | |
| for(i = 0; i < num_at_level; i++) | |
| { /* Path must start at SOURCE (no inputs) */ | |
| inode = tnodes_at_level[0].list[i]; | |
| slack = tnode[inode].T_req - tnode[inode].T_arr; | |
| if(slack < min_slack) | |
| { | |
| crit_node = inode; | |
| min_slack = slack; | |
| } | |
| } | |
| critical_path_head = (t_linked_int *) my_malloc(sizeof(t_linked_int)); | |
| critical_path_head->data = crit_node; | |
| prev_crit_node = critical_path_head; | |
| num_edges = tnode[crit_node].num_edges; | |
| while(num_edges != 0) | |
| { /* Path will end at SINK (no fanout) */ | |
| curr_crit_node = (t_linked_int *) my_malloc(sizeof(t_linked_int)); | |
| prev_crit_node->next = curr_crit_node; | |
| tedge = tnode[crit_node].out_edges; | |
| min_slack = HUGE_FLOAT; | |
| for(iedge = 0; iedge < num_edges; iedge++) | |
| { | |
| to_node = tedge[iedge].to_node; | |
| slack = tnode[to_node].T_req - tnode[to_node].T_arr; | |
| if(slack < min_slack) | |
| { | |
| crit_node = to_node; | |
| min_slack = slack; | |
| } | |
| } | |
| curr_crit_node->data = crit_node; | |
| prev_crit_node = curr_crit_node; | |
| num_edges = tnode[crit_node].num_edges; | |
| } | |
| prev_crit_node->next = NULL; | |
| return (critical_path_head); | |
| } | |
| void | |
| get_tnode_block_and_output_net(int inode, | |
| int *iblk_ptr, | |
| int *inet_ptr) | |
| { | |
| /* Returns the index of the block that this tnode is part of. If the tnode * | |
| * is a CB_OPIN or INPAD_OPIN (i.e. if it drives a net), the net index is * | |
| * returned via inet_ptr. Otherwise inet_ptr points at OPEN. */ | |
| int inet, ipin, iblk; | |
| t_tnode_type tnode_type; | |
| iblk = tnode[inode].block; | |
| tnode_type = tnode[inode].type; | |
| if(tnode_type == CB_OPIN) | |
| { | |
| ipin = tnode[inode].pb_graph_pin->pin_count_in_cluster; | |
| inet = block[iblk].pb->rr_graph[ipin].net_num; | |
| assert(inet != OPEN); | |
| inet = vpack_to_clb_net_mapping[inet]; | |
| } | |
| else | |
| { | |
| inet = OPEN; | |
| } | |
| *iblk_ptr = iblk; | |
| *inet_ptr = inet; | |
| } | |
| void | |
| do_constant_net_delay_timing_analysis(t_timing_inf timing_inf, | |
| float constant_net_delay_value) | |
| { | |
| /* Does a timing analysis (simple) where it assumes that each net has a * | |
| * constant delay value. Used only when operation == TIMING_ANALYSIS_ONLY. */ | |
| struct s_linked_vptr *net_delay_chunk_list_head; | |
| float **net_delay, **net_slack; | |
| float T_crit; | |
| net_slack = alloc_and_load_timing_graph(timing_inf); | |
| net_delay = alloc_net_delay(&net_delay_chunk_list_head, timing_nets, num_timing_nets); | |
| load_constant_net_delay(net_delay, constant_net_delay_value, timing_nets, num_timing_nets); | |
| load_timing_graph_net_delays(net_delay); | |
| T_crit = load_net_slack(net_slack, 0); | |
| printf("\n"); | |
| printf("\nCritical Path: %g (s)\n", T_crit); | |
| #ifdef CREATE_ECHO_FILES | |
| print_critical_path("critical_path.echo"); | |
| print_timing_graph("timing_graph.echo"); | |
| print_net_slack("net_slack.echo", net_slack); | |
| print_net_delay(net_delay, "net_delay.echo", timing_nets, num_timing_nets); | |
| #endif /* CREATE_ECHO_FILES */ | |
| free_timing_graph(net_slack); | |
| free_net_delay(net_delay, &net_delay_chunk_list_head); | |
| } | |
| static void normalize_costs(float t_crit, long max_critical_input_paths, long max_critical_output_paths) { | |
| int i; | |
| for(i = 0; i < num_tnodes; i++) { | |
| tnode[i].normalized_slack = ((float)tnode[i].T_req - tnode[i].T_arr) / t_crit; | |
| tnode[i].normalized_T_arr = (float)tnode[i].T_arr/(float)t_crit; | |
| tnode[i].normalized_total_critical_paths = ((float)tnode[i].num_critical_input_paths + tnode[i].num_critical_output_paths) / ((float)max_critical_input_paths + max_critical_output_paths); | |
| } | |
| } | |
| void | |
| print_timing_graph_as_blif(char *fname, t_model *models) | |
| { | |
| struct s_model_ports *port; | |
| struct s_linked_vptr *p_io_removed; | |
| /* Prints out the critical path to a file. */ | |
| FILE *fp; | |
| int i, j; | |
| fp = my_fopen(fname, "w", 0); | |
| fprintf(fp, ".model %s\n", blif_circuit_name); | |
| fprintf(fp, ".inputs "); | |
| for(i = 0; i < num_logical_blocks; i++) { | |
| if(logical_block[i].type == VPACK_INPAD) { | |
| fprintf(fp, "\\\n%s ", logical_block[i].name); | |
| } | |
| } | |
| p_io_removed = circuit_p_io_removed; | |
| while(p_io_removed) { | |
| fprintf(fp, "\\\n%s ", (char *) p_io_removed->data_vptr); | |
| p_io_removed = p_io_removed->next; | |
| } | |
| fprintf(fp, "\n"); | |
| fprintf(fp, ".outputs "); | |
| for(i = 0; i < num_logical_blocks; i++) { | |
| if(logical_block[i].type == VPACK_OUTPAD) { | |
| /* Outputs have a "out:" prepended to them, must remove */ | |
| fprintf(fp, "\\\n%s ", &logical_block[i].name[4]); | |
| } | |
| } | |
| fprintf(fp, "\n"); | |
| fprintf(fp, ".names unconn\n"); | |
| fprintf(fp, " 0\n\n"); | |
| /* Print out primitives */ | |
| for(i = 0; i < num_logical_blocks; i++) { | |
| print_primitive_as_blif(fp, i); | |
| } | |
| /* Print out tnode connections */ | |
| for(i = 0; i < num_tnodes; i++) { | |
| if(tnode[i].type != PRIMITIVE_IPIN && tnode[i].type != FF_SOURCE && tnode[i].type != INPAD_SOURCE && tnode[i].type != OUTPAD_IPIN) { | |
| for(j = 0; j < tnode[i].num_edges; j++) { | |
| fprintf(fp, ".names tnode_%d tnode_%d\n", i, tnode[i].out_edges[j]); | |
| fprintf(fp, "1 1\n\n"); | |
| } | |
| } | |
| } | |
| fprintf(fp, ".end\n\n"); | |
| /* Print out .subckt models */ | |
| while(models) { | |
| fprintf(fp, ".model %s\n", models->name); | |
| fprintf(fp, ".inputs "); | |
| port = models->inputs; | |
| while(port) { | |
| if(port->size > 1) { | |
| for(j = 0; j < port->size; j++) { | |
| fprintf(fp, "%s[%d] ", port->name, j); | |
| } | |
| } else { | |
| fprintf(fp, "%s ", port->name); | |
| } | |
| port = port->next; | |
| } | |
| fprintf(fp, "\n"); | |
| fprintf(fp, ".outputs "); | |
| port = models->outputs; | |
| while(port) { | |
| if(port->size > 1) { | |
| for(j = 0; j < port->size; j++) { | |
| fprintf(fp, "%s[%d] ", port->name, j); | |
| } | |
| } else { | |
| fprintf(fp, "%s ", port->name); | |
| } | |
| port = port->next; | |
| } | |
| fprintf(fp, "\n.blackbox\n.end\n\n"); | |
| fprintf(fp, "\n\n"); | |
| models = models->next; | |
| } | |
| fclose(fp); | |
| } | |
| static void print_primitive_as_blif (FILE *fpout, int iblk) { | |
| int i, j; | |
| struct s_model_ports *port; | |
| struct s_linked_vptr *truth_table; | |
| t_rr_node *irr_graph; | |
| t_pb_graph_node *pb_graph_node; | |
| int node; | |
| /* Print primitives found in timing graph in blif format based on whether this is a logical primitive or a physical primitive */ | |
| if(logical_block[iblk].type == VPACK_INPAD) { | |
| if(logical_block[iblk].pb == NULL) { | |
| fprintf(fpout, ".names %s tnode_%d\n", logical_block[iblk].name, logical_block[iblk].output_net_tnodes[0][0]->index); | |
| } else { | |
| fprintf(fpout, ".names %s tnode_%d\n", logical_block[iblk].name, | |
| logical_block[iblk].pb->rr_graph[logical_block[iblk].pb->pb_graph_node->output_pins[0][0].pin_count_in_cluster].tnode->index); | |
| } | |
| fprintf(fpout, "1 1\n\n"); | |
| } else if(logical_block[iblk].type == VPACK_OUTPAD) { | |
| /* outputs have the symbol out: automatically prepended to it, must remove */ | |
| if(logical_block[iblk].pb == NULL) { | |
| fprintf(fpout, ".names tnode_%d %s\n", logical_block[iblk].input_net_tnodes[0][0]->index, &logical_block[iblk].name[4]); | |
| } else { | |
| /* avoid the out: from output pad naming */ | |
| fprintf(fpout, ".names tnode_%d %s\n", | |
| logical_block[iblk].pb->rr_graph[logical_block[iblk].pb->pb_graph_node->input_pins[0][0].pin_count_in_cluster].tnode->index, | |
| (logical_block[iblk].name + 4)); | |
| } | |
| fprintf(fpout, "1 1\n\n"); | |
| } else if(strcmp(logical_block[iblk].model->name, "latch") == 0) { | |
| fprintf(fpout, ".latch "); | |
| node = OPEN; | |
| if(logical_block[iblk].pb == NULL) { | |
| i = 0; | |
| port = logical_block[iblk].model->inputs; | |
| while(port) { | |
| if(!port->is_clock) { | |
| assert(port->size == 1); | |
| for(j = 0; j < port->size; j++) { | |
| if(logical_block[iblk].input_net_tnodes[i][j] != NULL) { | |
| fprintf(fpout, "tnode_%d ", logical_block[iblk].input_net_tnodes[i][j]->index); | |
| } else { | |
| assert(0); | |
| } | |
| } | |
| i++; | |
| } | |
| port = port->next; | |
| } | |
| assert(i == 1); | |
| i = 0; | |
| port = logical_block[iblk].model->outputs; | |
| while(port) { | |
| assert(port->size == 1); | |
| for(j = 0; j < port->size; j++) { | |
| if(logical_block[iblk].output_net_tnodes[i][j] != NULL) { | |
| node = logical_block[iblk].output_net_tnodes[i][j]->index; | |
| fprintf(fpout, "latch_%s re ", logical_block[iblk].name); | |
| } else { | |
| assert(0); | |
| } | |
| } | |
| i++; | |
| port = port->next; | |
| } | |
| assert(i == 1); | |
| i = 0; | |
| port = logical_block[iblk].model->inputs; | |
| while(port) { | |
| if(port->is_clock) { | |
| for(j = 0; j < port->size; j++) { | |
| if(logical_block[iblk].clock_net_tnode != NULL) { | |
| fprintf(fpout, "tnode_%d 0\n\n", logical_block[iblk].clock_net_tnode->index); | |
| } else { | |
| assert(0); | |
| } | |
| } | |
| i++; | |
| } | |
| port = port->next; | |
| } | |
| assert(i == 1); | |
| } else { | |
| irr_graph = logical_block[iblk].pb->rr_graph; | |
| assert(irr_graph[logical_block[iblk].pb->pb_graph_node->input_pins[0][0].pin_count_in_cluster].net_num != OPEN); | |
| fprintf(fpout, "tnode_%d ", irr_graph[logical_block[iblk].pb->pb_graph_node->input_pins[0][0].pin_count_in_cluster].tnode->index); | |
| node = irr_graph[logical_block[iblk].pb->pb_graph_node->output_pins[0][0].pin_count_in_cluster].tnode->index; | |
| fprintf(fpout, "latch_%s re ", logical_block[iblk].name); | |
| assert(irr_graph[logical_block[iblk].pb->pb_graph_node->clock_pins[0][0].pin_count_in_cluster].net_num != OPEN); | |
| fprintf(fpout, "tnode_%d 0\n\n", irr_graph[logical_block[iblk].pb->pb_graph_node->clock_pins[0][0].pin_count_in_cluster].tnode->index); | |
| } | |
| assert(node != OPEN); | |
| fprintf(fpout, ".names latch_%s tnode_%d\n", logical_block[iblk].name, node); | |
| fprintf(fpout, "1 1\n\n"); | |
| } else if(strcmp(logical_block[iblk].model->name, "names") == 0) { | |
| fprintf(fpout, ".names "); | |
| node = OPEN; | |
| if(logical_block[iblk].pb == NULL) { | |
| i = 0; | |
| port = logical_block[iblk].model->inputs; | |
| while(port) { | |
| assert(!port->is_clock); | |
| for(j = 0; j < port->size; j++) { | |
| if(logical_block[iblk].input_net_tnodes[i][j] != NULL) { | |
| fprintf(fpout, "tnode_%d ", logical_block[iblk].input_net_tnodes[i][j]->index); | |
| } else { | |
| break; | |
| } | |
| } | |
| i++; | |
| port = port->next; | |
| } | |
| assert(i == 1); | |
| i = 0; | |
| port = logical_block[iblk].model->outputs; | |
| while(port) { | |
| assert(port->size == 1); | |
| fprintf(fpout, "lut_%s\n", logical_block[iblk].name); | |
| node = logical_block[iblk].output_net_tnodes[0][0]->index; | |
| assert(node != OPEN); | |
| i++; | |
| port = port->next; | |
| } | |
| assert(i == 1); | |
| } else { | |
| irr_graph = logical_block[iblk].pb->rr_graph; | |
| assert(logical_block[iblk].pb->pb_graph_node->num_input_ports == 1); | |
| for(i = 0; i < logical_block[iblk].pb->pb_graph_node->num_input_pins[0]; i++) { | |
| if(irr_graph[logical_block[iblk].pb->pb_graph_node->input_pins[0][i].pin_count_in_cluster].net_num != OPEN) { | |
| fprintf(fpout, "tnode_%d ", irr_graph[logical_block[iblk].pb->pb_graph_node->input_pins[0][i].pin_count_in_cluster].tnode->index); | |
| } else { | |
| if(i > 0 && i < logical_block[iblk].pb->pb_graph_node->num_input_pins[0] - 1) { | |
| assert(irr_graph[logical_block[iblk].pb->pb_graph_node->input_pins[0][i + 1].pin_count_in_cluster].net_num == OPEN); | |
| } | |
| } | |
| } | |
| assert(logical_block[iblk].pb->pb_graph_node->num_output_ports == 1); | |
| assert(logical_block[iblk].pb->pb_graph_node->num_output_pins[0] == 1); | |
| fprintf(fpout, "lut_%s\n", logical_block[iblk].name); | |
| node = irr_graph[logical_block[iblk].pb->pb_graph_node->output_pins[0][0].pin_count_in_cluster].tnode->index; | |
| } | |
| assert(node != OPEN); | |
| truth_table = logical_block[iblk].truth_table; | |
| while(truth_table) { | |
| fprintf(fpout, "%s\n", (char*)truth_table->data_vptr); | |
| truth_table = truth_table->next; | |
| } | |
| fprintf(fpout, "\n"); | |
| fprintf(fpout, ".names lut_%s tnode_%d\n", logical_block[iblk].name, node); | |
| fprintf(fpout, "1 1\n\n"); | |
| } else { | |
| /* This is a standard .subckt blif structure */ | |
| fprintf(fpout, ".subckt %s ", logical_block[iblk].model->name); | |
| if(logical_block[iblk].pb == NULL) { | |
| i = 0; | |
| port = logical_block[iblk].model->inputs; | |
| while(port) { | |
| if(!port->is_clock) { | |
| for(j = 0; j < port->size; j++) { | |
| if(logical_block[iblk].input_net_tnodes[i][j] != NULL) { | |
| if(port->size > 1) { | |
| fprintf(fpout, "\\\n%s[%d]=tnode_%d ", port->name, j, logical_block[iblk].input_net_tnodes[i][j]->index); | |
| } else { | |
| fprintf(fpout, "\\\n%s=tnode_%d ", port->name, logical_block[iblk].input_net_tnodes[i][j]->index); | |
| } | |
| } else { | |
| if(port->size > 1) { | |
| fprintf(fpout, "\\\n%s[%d]=unconn ", port->name, j); | |
| } else { | |
| fprintf(fpout, "\\\n%s=unconn ", port->name); | |
| } | |
| } | |
| } | |
| i++; | |
| } | |
| port = port->next; | |
| } | |
| i = 0; | |
| port = logical_block[iblk].model->outputs; | |
| while(port) { | |
| for(j = 0; j < port->size; j++) { | |
| if(logical_block[iblk].output_net_tnodes[i][j] != NULL) { | |
| if(port->size > 1) { | |
| fprintf(fpout, "\\\n%s[%d]=%s ", port->name, j, vpack_net[logical_block[iblk].output_nets[i][j]].name); | |
| } else { | |
| fprintf(fpout, "\\\n%s=%s ", port->name, vpack_net[logical_block[iblk].output_nets[i][j]].name); | |
| } | |
| } else { | |
| if(port->size > 1) { | |
| fprintf(fpout, "\\\n%s[%d]=unconn_%d_%s_%d ", port->name, j, iblk, port->name, j); | |
| } else { | |
| fprintf(fpout, "\\\n%s=unconn_%d_%s_%d ", port->name, iblk, port->name); | |
| } | |
| } | |
| } | |
| i++; | |
| port = port->next; | |
| } | |
| i = 0; | |
| port = logical_block[iblk].model->inputs; | |
| while(port) { | |
| if(port->is_clock) { | |
| assert(port->size == 1); | |
| if(logical_block[iblk].clock_net_tnode != NULL) { | |
| fprintf(fpout, "\\\n%s=tnode_%d ", port->name, logical_block[iblk].clock_net_tnode->index); | |
| } else { | |
| fprintf(fpout, "\\\n%s=unconn ", port->name); | |
| } | |
| i++; | |
| } | |
| port = port->next; | |
| } | |
| fprintf(fpout, "\n\n"); | |
| i = 0; | |
| port = logical_block[iblk].model->outputs; | |
| while(port) { | |
| for(j = 0; j < port->size; j++) { | |
| if(logical_block[iblk].output_net_tnodes[i][j] != NULL) { | |
| fprintf(fpout, ".names %s tnode_%d\n", vpack_net[logical_block[iblk].output_nets[i][j]].name, logical_block[iblk].output_net_tnodes[i][j]->index); | |
| fprintf(fpout, "1 1\n\n"); | |
| } | |
| } | |
| i++; | |
| port = port->next; | |
| } | |
| } else { | |
| irr_graph = logical_block[iblk].pb->rr_graph; | |
| pb_graph_node = logical_block[iblk].pb->pb_graph_node; | |
| for(i = 0; i < pb_graph_node->num_input_ports; i++) { | |
| for(j = 0; j < pb_graph_node->num_input_pins[i]; j++) { | |
| if(irr_graph[pb_graph_node->input_pins[i][j].pin_count_in_cluster].net_num != OPEN) { | |
| if(pb_graph_node->num_input_pins[i] > 1) { | |
| fprintf(fpout, "\\\n%s[%d]=tnode_%d ", pb_graph_node->input_pins[i][j].port->name, j, irr_graph[pb_graph_node->input_pins[i][j].pin_count_in_cluster].tnode->index); | |
| } else { | |
| fprintf(fpout, "\\\n%s=tnode_%d ", pb_graph_node->input_pins[i][j].port->name, irr_graph[pb_graph_node->input_pins[i][j].pin_count_in_cluster].tnode->index); | |
| } | |
| } else { | |
| if(pb_graph_node->num_input_pins[i] > 1) { | |
| fprintf(fpout, "\\\n%s[%d]=unconn ", pb_graph_node->input_pins[i][j].port->name, j); | |
| } else { | |
| fprintf(fpout, "\\\n%s=unconn ", pb_graph_node->input_pins[i][j].port->name); | |
| } | |
| } | |
| } | |
| } | |
| for(i = 0; i < pb_graph_node->num_output_ports; i++) { | |
| for(j = 0; j < pb_graph_node->num_output_pins[i]; j++) { | |
| if(irr_graph[pb_graph_node->output_pins[i][j].pin_count_in_cluster].net_num != OPEN) { | |
| if(pb_graph_node->num_output_pins[i] > 1) { | |
| fprintf(fpout, "\\\n%s[%d]=%s ", pb_graph_node->output_pins[i][j].port->name, j, | |
| vpack_net[irr_graph[pb_graph_node->output_pins[i][j].pin_count_in_cluster].net_num].name); | |
| } else { | |
| fprintf(fpout, "\\\n%s=%s ", pb_graph_node->output_pins[i][j].port->name, | |
| vpack_net[irr_graph[pb_graph_node->output_pins[i][j].pin_count_in_cluster].tnode->index].name); | |
| } | |
| } else { | |
| if(pb_graph_node->num_output_pins[i] > 1) { | |
| fprintf(fpout, "\\\n%s[%d]=unconn ", pb_graph_node->output_pins[i][j].port->name, j); | |
| } else { | |
| fprintf(fpout, "\\\n%s=unconn ", pb_graph_node->output_pins[i][j].port->name); | |
| } | |
| } | |
| } | |
| } | |
| for(i = 0; i < pb_graph_node->num_clock_ports; i++) { | |
| for(j = 0; j < pb_graph_node->num_clock_pins[i]; j++) { | |
| if(irr_graph[pb_graph_node->clock_pins[i][j].pin_count_in_cluster].net_num != OPEN) { | |
| if(pb_graph_node->num_clock_pins[i] > 1) { | |
| fprintf(fpout, "\\\n%s[%d]=tnode_%d ", pb_graph_node->clock_pins[i][j].port->name, j, irr_graph[pb_graph_node->clock_pins[i][j].pin_count_in_cluster].tnode->index); | |
| } else { | |
| fprintf(fpout, "\\\n%s=tnode_%d ", pb_graph_node->clock_pins[i][j].port->name, irr_graph[pb_graph_node->clock_pins[i][j].pin_count_in_cluster].tnode->index); | |
| } | |
| } else { | |
| if(pb_graph_node->num_clock_pins[i] > 1) { | |
| fprintf(fpout, "\\\n%s[%d]=unconn ", pb_graph_node->clock_pins[i][j].port->name, j); | |
| } else { | |
| fprintf(fpout, "\\\n%s=unconn ", pb_graph_node->clock_pins[i][j].port->name); | |
| } | |
| } | |
| } | |
| } | |
| fprintf(fpout, "\n\n"); | |
| /* connect up output port names to output tnodes */ | |
| for(i = 0; i < pb_graph_node->num_output_ports; i++) { | |
| for(j = 0; j < pb_graph_node->num_output_pins[i]; j++) { | |
| if(irr_graph[pb_graph_node->output_pins[i][j].pin_count_in_cluster].net_num != OPEN) { | |
| fprintf(fpout, ".names %s tnode_%d\n", vpack_net[irr_graph[pb_graph_node->output_pins[i][j].pin_count_in_cluster].net_num].name, irr_graph[pb_graph_node->output_pins[i][j].pin_count_in_cluster].tnode->index); | |
| fprintf(fpout, "1 1\n\n"); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } |