#include <assert.h>
#include <stdio.h>
#include <sys/types.h>
#include <time.h>
#include "util.h"
#include "vpr_types.h"
#include "vpr_utils.h"
#include "globals.h"
#include "place_and_route.h"
#include "place.h"
#include "read_place.h"
#include "route_export.h"
#include "draw.h"
#include "stats.h"
#include "check_route.h"
#include "rr_graph.h"
#include "path_delay.h"
#include "net_delay.h"
#include "timing_place.h"
#include "read_xml_arch_file.h"
#include "ReadOptions.h"
#include "route_common.h"
#include "place_macro.h"
#include "verilog_writer.h"
#include "power.h"

/* CLB PIN REMAP */
#include "place_clb_pin_remap.h"

#include "tileable_chan_details_builder.h"

/******************* Subroutines local to this module ************************/

static int binary_search_place_and_route(struct s_placer_opts placer_opts,
		char *place_file, char *net_file, char *arch_file, char *route_file,
		boolean full_stats, boolean verify_binary_search,
		struct s_annealing_sched annealing_sched,
		struct s_router_opts router_opts,
		struct s_det_routing_arch det_routing_arch, t_segment_inf * segment_inf,
		t_timing_inf timing_inf, t_chan_width_dist chan_width_dist,
		t_model *models, t_direct_inf *directs, int num_directs, float sram_area,
        /*Xifan TANG: Switch Segment Pattern Support*/
        t_swseg_pattern_inf* swseg_patterns);

static float comp_width(t_chan * chan, float x, float separation);

void post_place_sync(INP int L_num_blocks,
		INOUTP const struct s_block block_list[]);

void free_pb_data(t_pb *pb);

/************************* Subroutine Definitions ****************************/

void place_and_route(enum e_operation operation,
		struct s_placer_opts placer_opts, char *place_file, char *net_file,
		char *arch_file, char *route_file,
		struct s_annealing_sched annealing_sched,
		struct s_router_opts router_opts,
		struct s_det_routing_arch det_routing_arch, t_segment_inf * segment_inf,
		t_timing_inf timing_inf, t_chan_width_dist chan_width_dist,
		struct s_model *models,
		t_direct_inf *directs, int num_directs, float sram_area,
        /*Xifan TANG: Switch Segment Pattern Support*/
        t_swseg_pattern_inf* swseg_patterns) {

	/* This routine controls the overall placement and routing of a circuit. */
	char msg[BUFSIZE];
	int width_fac, i;
	boolean success, Fc_clipped;
	float **net_delay = NULL;
	t_slack * slacks = NULL;
	t_chunk net_delay_ch = {NULL, 0, NULL};

	/*struct s_linked_vptr *net_delay_chunk_list_head;*/
	t_ivec **clb_opins_used_locally = NULL; /* [0..num_blocks-1][0..num_class-1] */
	int max_pins_per_clb;
	clock_t begin, end;

	Fc_clipped = FALSE;

	max_pins_per_clb = 0;
	for (i = 0; i < num_types; i++) {
		if (type_descriptors[i].num_pins > max_pins_per_clb) {
			max_pins_per_clb = type_descriptors[i].num_pins;
		}
	}

	if (placer_opts.place_freq == PLACE_NEVER) {
		/* Read the placement from a file */
		read_place(place_file, net_file, arch_file, nx, ny, num_blocks, block);
		sync_grid_to_blocks(num_blocks, block, nx, ny, grid);
	} else {
		assert(
				(PLACE_ONCE == placer_opts.place_freq) || (PLACE_ALWAYS == placer_opts.place_freq));
		begin = clock();
		try_place(placer_opts, annealing_sched, chan_width_dist, router_opts,
				det_routing_arch, segment_inf, timing_inf, directs, num_directs);
		print_place(place_file, net_file, arch_file);
		end = clock();
#ifdef CLOCKS_PER_SEC
		vpr_printf(TIO_MESSAGE_INFO, "Placement took %g seconds.\n", (float)(end - begin) / CLOCKS_PER_SEC);
#else
		vpr_printf(TIO_MESSAGE_INFO, "Placement took %g seconds.\n", (float)(end - begin) / CLK_PER_SEC);
#endif
	}
	post_place_sync(num_blocks, block);

    /* Xifan TANG: PLACE_CLB_PIN_REMAP */
    if (TRUE == placer_opts.place_clb_pin_remap) {
      vpr_printf(TIO_MESSAGE_INFO, "Try remap CLB pins after placement...\n");
	  begin = clock();
      try_clb_pin_remap_after_placement(det_routing_arch,
                                        segment_inf, 
                                        timing_inf, 
                                        num_directs, 
                                        directs);
      end = clock();
	  update_screen(MAJOR, "CLB_PIN_REMAP", PLACEMENT, FALSE);
#ifdef CLOCKS_PER_SEC
      vpr_printf(TIO_MESSAGE_INFO, "CLB_PIN_REMAP took %g seconds.\n", (float)(end - begin) / CLOCKS_PER_SEC);
#else
      vpr_printf(TIO_MESSAGE_INFO, "CLB_PIN_REMAP took %g seconds.\n", (float)(end - begin) / CLK_PER_SEC);
#endif
    }
    /* END */
	begin = clock();


	fflush(stdout);

	if (!router_opts.doRouting)
		return;

	width_fac = router_opts.fixed_channel_width;

	/* If channel width not fixed, use binary search to find min W */
	if (NO_FIXED_CHANNEL_WIDTH == width_fac) {
		g_solution_inf.channel_width = binary_search_place_and_route(placer_opts, place_file, net_file,
				arch_file, route_file, router_opts.full_stats,
				router_opts.verify_binary_search, annealing_sched, router_opts,
				det_routing_arch, segment_inf, timing_inf, chan_width_dist,
				models, directs, num_directs,sram_area,
                /*Xifan TANG: Switch Segment Pattern Support*/
                swseg_patterns);
	} else {
		g_solution_inf.channel_width = width_fac;
		if (det_routing_arch.directionality == UNI_DIRECTIONAL) {
			if (width_fac % 2 != 0) {
				vpr_printf(TIO_MESSAGE_ERROR, "in pack_place_and_route.c: Given odd chan width (%d) for udsd architecture.\n",
						width_fac);
				exit(1);
			}
		}
		/* Other constraints can be left to rr_graph to check since this is one pass routing */

        /* Xifan Tang: W estimation for tileable routing architecture */
        /* Build the segment inf vector */
        std::vector<t_segment_inf> segment_vec;
        for (int iseg = 0; iseg < det_routing_arch.num_segment; ++iseg) {
          segment_vec.push_back(segment_inf[iseg]);
        }
    
        if (TRUE == router_opts.use_tileable_route_chan_width) {
          int adapted_W = adapt_to_tileable_route_chan_width(width_fac, segment_vec); 
          vpr_printf(TIO_MESSAGE_INFO, 
                     "Adapt routing channel width (%d) to be tileable: %d\n", 
                     width_fac, adapted_W);
          width_fac = adapted_W;
        }

		/* Allocate the major routing structures. */

		clb_opins_used_locally = alloc_route_structs();

		slacks = alloc_and_load_timing_graph(timing_inf);
		net_delay = alloc_net_delay(&net_delay_ch, clb_net,
					num_nets);

		success = try_route(width_fac, router_opts, det_routing_arch,
				segment_inf, timing_inf, net_delay, slacks, chan_width_dist,
				clb_opins_used_locally, &Fc_clipped, directs, num_directs,
                /*Xifan TANG: Switch Segment Pattern Support*/
                swseg_patterns);

		if (Fc_clipped) {
			vpr_printf(TIO_MESSAGE_WARNING, "Fc_output was too high and was clipped to full (maximum) connectivity.\n");
		}

		if (success == FALSE) {
			vpr_printf(TIO_MESSAGE_INFO, "Circuit is unrouteable with a channel width factor of %d.\n", width_fac);
			vpr_printf(TIO_MESSAGE_INFO, "\n");
			sprintf(msg, "Routing failed with a channel width factor of %d. ILLEGAL routing shown.", width_fac);
		}

		else {
			check_route(router_opts.route_type, det_routing_arch.num_switch, clb_opins_used_locally);
			get_serial_num();

			vpr_printf(TIO_MESSAGE_INFO, "Circuit successfully routed with a channel width factor of %d.\n", width_fac);
			vpr_printf(TIO_MESSAGE_INFO, "\n");

			routing_stats(router_opts.full_stats, router_opts.route_type,
					det_routing_arch.num_switch, segment_inf,
					det_routing_arch.num_segment, det_routing_arch.R_minW_nmos,
					det_routing_arch.R_minW_pmos,
					det_routing_arch.directionality,
					timing_inf.timing_analysis_enabled, net_delay, slacks, sram_area);

			print_route(route_file);

			if (getEchoEnabled() && isEchoFileEnabled(E_ECHO_ROUTING_SINK_DELAYS)) {
				print_sink_delays(getEchoFileName(E_ECHO_ROUTING_SINK_DELAYS));
			}

			sprintf(msg, "Routing succeeded with a channel width factor of %d.\n\n",
					width_fac);


		}

		init_draw_coords(max_pins_per_clb);
		update_screen(MAJOR, msg, ROUTING, timing_inf.timing_analysis_enabled);
		

		if (timing_inf.timing_analysis_enabled) {
			assert(slacks->slack);

			if (getEchoEnabled() && isEchoFileEnabled(E_ECHO_POST_FLOW_TIMING_GRAPH)) {
				print_timing_graph_as_blif (getEchoFileName(E_ECHO_POST_FLOW_TIMING_GRAPH),
						models);
			}

			if(GetPostSynthesisOption())
			  {
			    verilog_writer();
			  }

			free_timing_graph(slacks);

			assert(net_delay);
			free_net_delay(net_delay, &net_delay_ch);
		}

		fflush(stdout);
	}

	if (clb_opins_used_locally != NULL) {
		for (i = 0; i < num_blocks; i++) {
			free_ivec_vector(clb_opins_used_locally[i], 0,
					block[i].type->num_class - 1);
		}
		free(clb_opins_used_locally);
		clb_opins_used_locally = NULL;
	}

	/* Frees up all the data structure used in vpr_utils. */
	free_port_pin_from_blk_pin();
	free_blk_pin_from_port_pin();

	end = clock();
#ifdef CLOCKS_PER_SEC
	vpr_printf(TIO_MESSAGE_INFO, "Routing took %g seconds.\n", (float) (end - begin) / CLOCKS_PER_SEC);
#else
	vpr_printf(TIO_MESSAGE_INFO, "Routing took %g seconds.\n", (float)(end - begin) / CLK_PER_SEC);
#endif

	/*WMF: cleaning up memory usage */

	/*	if (g_heap_free_head)
		free(g_heap_free_head);
	if (g_trace_free_head)
		free(g_trace_free_head);
	if (g_linked_f_pointer_free_head)
		free(g_linked_f_pointer_free_head);*/
}

static int binary_search_place_and_route(struct s_placer_opts placer_opts,
		char *place_file, char *net_file, char *arch_file, char *route_file,
		boolean full_stats, boolean verify_binary_search,
		struct s_annealing_sched annealing_sched,
		struct s_router_opts router_opts,
		struct s_det_routing_arch det_routing_arch, t_segment_inf * segment_inf,
		t_timing_inf timing_inf, t_chan_width_dist chan_width_dist,
		t_model *models, t_direct_inf *directs, int num_directs, float sram_area,
        /*Xifan TANG: Switch Segment Pattern Support*/
        t_swseg_pattern_inf* swseg_patterns) {

	/* This routine performs a binary search to find the minimum number of      *
	 * tracks per channel required to successfully route a circuit, and returns *
	 * that minimum width_fac.                                                  */

	struct s_trace **best_routing; /* Saves the best routing found so far. */
	int current, low, high, final;
	int max_pins_per_clb, i;
	boolean success, prev_success, prev2_success, Fc_clipped = FALSE;
	char msg[BUFSIZE];
	float **net_delay = NULL;
	t_slack * slacks = NULL;

	t_chunk net_delay_ch = {NULL, 0, NULL};

	/*struct s_linked_vptr *net_delay_chunk_list_head;*/
	t_ivec **clb_opins_used_locally, **saved_clb_opins_used_locally;

	/* [0..num_blocks-1][0..num_class-1] */
	int attempt_count;
	int udsd_multiplier;
	int warnings;

	t_graph_type graph_type;

	/* Allocate the major routing structures. */

	if (router_opts.route_type == GLOBAL) {
		graph_type = GRAPH_GLOBAL;
    /* Xifan Tang: tileable undirectional rr_graph support */
	} else if (BI_DIRECTIONAL == det_routing_arch.directionality) {
        graph_type = GRAPH_BIDIR;
	} else if (UNI_DIRECTIONAL == det_routing_arch.directionality) {
         if (true == det_routing_arch.tileable) {
			graph_type = GRAPH_UNIDIR_TILEABLE;
         } else {
		    graph_type = GRAPH_UNIDIR;
         }
	}

	max_pins_per_clb = 0;
	for (i = 0; i < num_types; i++) {
		max_pins_per_clb = std::max(max_pins_per_clb, type_descriptors[i].num_pins);
	}

	clb_opins_used_locally = alloc_route_structs();
	best_routing = alloc_saved_routing(clb_opins_used_locally,
			&saved_clb_opins_used_locally);

	slacks = alloc_and_load_timing_graph(timing_inf);
	net_delay = alloc_net_delay(&net_delay_ch, clb_net, num_nets);

	/* UDSD by AY Start */
	if (det_routing_arch.directionality == BI_DIRECTIONAL)
		udsd_multiplier = 1;
	else
		udsd_multiplier = 2;
	/* UDSD by AY End */


	if (router_opts.fixed_channel_width != NO_FIXED_CHANNEL_WIDTH) {
		current = router_opts.fixed_channel_width + 5 * udsd_multiplier;
		low = router_opts.fixed_channel_width - 1 * udsd_multiplier;
	} else {
		current = max_pins_per_clb + max_pins_per_clb % 2; /* Binary search part */
        /* End */
		low = -1;
	}

    /* Build the segment inf vector */
    std::vector<t_segment_inf> segment_vec;
    for (int iseg = 0; iseg < det_routing_arch.num_segment; ++iseg) {
      segment_vec.push_back(segment_inf[iseg]);
    }

	/* Constraints must be checked to not break rr_graph generator */
	if (det_routing_arch.directionality == UNI_DIRECTIONAL) {
		if (current % 2 != 0) {
			vpr_printf(TIO_MESSAGE_ERROR, "in pack_place_and_route.c: Tried odd chan width (%d) for udsd architecture.\n",
					current);
			exit(1);
		}
	}

	else {
		if (det_routing_arch.Fs % 3) {
			vpr_printf(TIO_MESSAGE_ERROR, "Fs must be three in bidirectional mode.\n");
			exit(1);
		}
	}

	high = -1;
	final = -1;

	attempt_count = 0;

	while (final == -1) {
        /* Xifan Tang: W estimation for tileable routing architecture */
        if (TRUE == router_opts.use_tileable_route_chan_width) {
          int adapted_W = adapt_to_tileable_route_chan_width(current, segment_vec); 
          vpr_printf(TIO_MESSAGE_INFO, 
                     "Adapt routing channel width (%d) to be tileable: %d\n", 
                     current, adapted_W);
          current = adapted_W;
        }
        /* Do a early exit when the current equals to high or low, 
         * This means that the current W has been tried already. We just return a final value (high) 
         */
        if ( (current == high) || (current == low) ) {
          final = high;
	      break;
        }

		vpr_printf(TIO_MESSAGE_INFO, "Using low: %d, high: %d, current: %d\n", low, high, current);
		fflush(stdout);

		/* Check if the channel width is huge to avoid overflow.  Assume the *
		 * circuit is unroutable with the current router options if we're    *
		 * going to overflow.                                                */
		if (router_opts.fixed_channel_width != NO_FIXED_CHANNEL_WIDTH) {
			if (current > router_opts.fixed_channel_width * 4) {
				vpr_printf(TIO_MESSAGE_ERROR, "This circuit appears to be unroutable with the current router options. Last failed at %d.\n", low);
				vpr_printf(TIO_MESSAGE_INFO, "Aborting routing procedure.\n");
				exit(1);
			}
		} else {
			if (current > 1000) {
				vpr_printf(TIO_MESSAGE_ERROR, "This circuit requires a channel width above 1000, probably is not going to route.\n");
				vpr_printf(TIO_MESSAGE_INFO, "Aborting routing procedure.\n");
				exit(1);
			}
		}

		if ((current * 3) < det_routing_arch.Fs) {
			vpr_printf(TIO_MESSAGE_INFO, "Width factor is now below specified Fs. Stop search.\n");
			final = high;
			break;
		}

		if (placer_opts.place_freq == PLACE_ALWAYS) {
			placer_opts.place_chan_width = current;
			try_place(placer_opts, annealing_sched, chan_width_dist,
					router_opts, det_routing_arch, segment_inf, timing_inf,
					directs, num_directs);
            /* Xifan TANG: PLACE_CLB_PIN_REMAP */
            if (TRUE == placer_opts.place_clb_pin_remap) {
              vpr_printf(TIO_MESSAGE_INFO, "Try remap CLB pins after placement...\n");
              try_clb_pin_remap_after_placement(det_routing_arch,
                                                segment_inf, 
                                                timing_inf, 
                                                num_directs, 
                                                directs);
            }
            /* END */
		}
		success = try_route(current, router_opts, det_routing_arch, segment_inf,
				timing_inf, net_delay, slacks, chan_width_dist,
				clb_opins_used_locally, &Fc_clipped, directs, num_directs,
                /*Xifan TANG: Switch Segment Pattern Support*/
                swseg_patterns);
		attempt_count++;
		fflush(stdout);
#if 1
		if (success && (Fc_clipped == FALSE)) {
#else
			if (success
					&& (Fc_clipped == FALSE
							|| det_routing_arch.Fc_type == FRACTIONAL))
			{
#endif
			if (current == high) {
				/* Can't go any lower */
				final = current;
			}
			high = current;

			/* If Fc_output is too high, set to full connectivity but warn the user */
			if (Fc_clipped) {
				vpr_printf(TIO_MESSAGE_WARNING, "Fc_output was too high and was clipped to full (maximum) connectivity.\n");
			}

			/* If we're re-placing constantly, save placement in case it is best. */
#if 0
			if (placer_opts.place_freq == PLACE_ALWAYS)
			{
				print_place(place_file, net_file, arch_file);
			}
#endif

			/* Save routing in case it is best. */
			save_routing(best_routing, clb_opins_used_locally,
					saved_clb_opins_used_locally);

			if ((high - low) <= 1 * udsd_multiplier)
				final = high;

			if (low != -1) {
				current = (high + low) / 2;

			} else {
				current = high / 2; /* haven't found lower bound yet */
			}
		} else { /* last route not successful */
			if (success && Fc_clipped) {
				vpr_printf(TIO_MESSAGE_INFO, "Routing rejected, Fc_output was too high.\n");
				success = FALSE;
			}
			low = current;
			if (high != -1) {

				if ((high - low) <= 1 * udsd_multiplier)
					final = high;

				current = (high + low) / 2;

			} else {
				if (router_opts.fixed_channel_width != NO_FIXED_CHANNEL_WIDTH) {
					/* FOR Wneed = f(Fs) search */
					if (low < router_opts.fixed_channel_width + 30) {
						current = low + 5 * udsd_multiplier;
					} else {
						vpr_printf(TIO_MESSAGE_ERROR, "Aborting: Wneed = f(Fs) search found exceedingly large Wneed (at least %d).\n", low);
						exit(1);
					}
				} else {
					current = low * 2; /* Haven't found upper bound yet */
  
				}
			}
		}
		current = current + current % udsd_multiplier;
	}

	/* The binary search above occassionally does not find the minimum    *
	 * routeable channel width.  Sometimes a circuit that will not route  *
	 * in 19 channels will route in 18, due to router flukiness.  If      *  
	 * verify_binary_search is set, the code below will ensure that FPGAs *
	 * with channel widths of final-2 and final-3 wil not route           *  
	 * successfully.  If one does route successfully, the router keeps    *
	 * trying smaller channel widths until two in a row (e.g. 8 and 9)    *
	 * fail.                                                              */

	if (verify_binary_search) {

		vpr_printf(TIO_MESSAGE_INFO, "\n");
		vpr_printf(TIO_MESSAGE_INFO, "Verifying that binary search found min channel width...\n");

		prev_success = TRUE; /* Actually final - 1 failed, but this makes router */
		/* try final-2 and final-3 even if both fail: safer */
		prev2_success = TRUE;

		current = final - 2;

		while (prev2_success || prev_success) {
			if ((router_opts.fixed_channel_width != NO_FIXED_CHANNEL_WIDTH)
					&& (current < router_opts.fixed_channel_width)) {
				break;
			}
			fflush(stdout);
			if (current < 1)
				break;
			if (placer_opts.place_freq == PLACE_ALWAYS) {
				placer_opts.place_chan_width = current;
				try_place(placer_opts, annealing_sched, chan_width_dist,
						router_opts, det_routing_arch, segment_inf, timing_inf,
						directs, num_directs);
                /* Xifan TANG: PLACE_CLB_PIN_REMAP */
                if (TRUE == placer_opts.place_clb_pin_remap) {
                  vpr_printf(TIO_MESSAGE_INFO, "Try remap CLB pins after placement...\n");
                  try_clb_pin_remap_after_placement(det_routing_arch,
                                                    segment_inf, 
                                                    timing_inf, 
                                                    num_directs, 
                                                    directs);
                }
                /* END */
			}
			success = try_route(current, router_opts, det_routing_arch,
					segment_inf, timing_inf, net_delay, slacks,
					chan_width_dist, clb_opins_used_locally, &Fc_clipped, directs, num_directs,
                    /*Xifan TANG: Switch Segment Pattern Support*/
                    swseg_patterns);

			if (success && Fc_clipped == FALSE) {
				final = current;
				save_routing(best_routing, clb_opins_used_locally,
						saved_clb_opins_used_locally);

				if (placer_opts.place_freq == PLACE_ALWAYS) {
					print_place(place_file, net_file, arch_file);
				}
			}

			prev2_success = prev_success;
			prev_success = success;
			current--;
			if (det_routing_arch.directionality == UNI_DIRECTIONAL) {
				current--; /* width must be even */
			}
		}
	}

	/* End binary search verification. */
	/* Restore the best placement (if necessary), the best routing, and  *
	 * * the best channel widths for final drawing and statistics output.  */
	init_chan(final, chan_width_dist);
#if 0
	if (placer_opts.place_freq == PLACE_ALWAYS)
	{
		vpr_printf(TIO_MESSAGE_INFO, "Reading best placement back in.\n");
		placer_opts.place_chan_width = final;
		read_place(place_file, net_file, arch_file, placer_opts,
				router_opts, chan_width_dist, det_routing_arch,
				segment_inf, timing_inf);
	}
#endif
	free_rr_graph();

	build_rr_graph(graph_type, num_types, type_descriptors, nx, ny, grid,
			chan_width_x[0], NULL, 
            det_routing_arch.switch_block_type, det_routing_arch.Fs, 
            det_routing_arch.switch_block_sub_type, det_routing_arch.sub_Fs, 
            det_routing_arch.wire_opposite_side,
            det_routing_arch.num_segment,
			det_routing_arch.num_switch, segment_inf,
			det_routing_arch.global_route_switch,
			det_routing_arch.delayless_switch, timing_inf,
			det_routing_arch.wire_to_ipin_switch, router_opts.base_cost_type,
			directs, num_directs, FALSE,
			&warnings,
            /*Xifan TANG: Switch Segment Pattern Support*/
            det_routing_arch.num_swseg_pattern, swseg_patterns, TRUE, TRUE);

	restore_routing(best_routing, clb_opins_used_locally,
			saved_clb_opins_used_locally);
	check_route(router_opts.route_type, det_routing_arch.num_switch,
			clb_opins_used_locally);
	get_serial_num();
	if (Fc_clipped) {
		vpr_printf(TIO_MESSAGE_WARNING, "Best routing Fc_output too high, clipped to full (maximum) connectivity.\n");
	}
	vpr_printf(TIO_MESSAGE_INFO, "Best routing used a channel width factor of %d.\n", final);

	routing_stats(full_stats, router_opts.route_type,
			det_routing_arch.num_switch, segment_inf,
			det_routing_arch.num_segment, det_routing_arch.R_minW_nmos,
			det_routing_arch.R_minW_pmos, det_routing_arch.directionality,
			timing_inf.timing_analysis_enabled, net_delay, slacks, sram_area);

	print_route(route_file);

	if (getEchoEnabled() && isEchoFileEnabled(E_ECHO_ROUTING_SINK_DELAYS)) {
		print_sink_delays(getEchoFileName(E_ECHO_ROUTING_SINK_DELAYS));
	}

	init_draw_coords(max_pins_per_clb);
	sprintf(msg, "Routing succeeded with a channel width factor of %d.", final);
	update_screen(MAJOR, msg, ROUTING, timing_inf.timing_analysis_enabled);

	if (timing_inf.timing_analysis_enabled) {
		if (getEchoEnabled() && isEchoFileEnabled(E_ECHO_POST_FLOW_TIMING_GRAPH)) {
			print_timing_graph_as_blif (getEchoFileName(E_ECHO_POST_FLOW_TIMING_GRAPH), models);
		}
		
		if(GetPostSynthesisOption())
		  {
		    verilog_writer();
		  }

		free_timing_graph(slacks);
		free_net_delay(net_delay, &net_delay_ch);
	}
	
	for (i = 0; i < num_blocks; i++) {
		free_ivec_vector(clb_opins_used_locally[i], 0,
				block[i].type->num_class - 1);
	}
	free(clb_opins_used_locally);
	clb_opins_used_locally = NULL;

	free_saved_routing(best_routing, saved_clb_opins_used_locally);
	fflush(stdout);

	return (final);

}

void init_chan(int cfactor, t_chan_width_dist chan_width_dist) {

	/* Assigns widths to channels (in tracks).  Minimum one track          * 
	 * per channel.  io channels are io_rat * maximum in interior          * 
	 * tracks wide.  The channel distributions read from the architecture  *
	 * file are scaled by cfactor.                                         */

	float x, separation, chan_width_io;
	int nio, i;
	t_chan chan_x_dist, chan_y_dist;

	chan_width_io = chan_width_dist.chan_width_io;
	chan_x_dist = chan_width_dist.chan_x_dist;
	chan_y_dist = chan_width_dist.chan_y_dist;

	/* io channel widths */

	nio = (int) floor(cfactor * chan_width_io + 0.5);
	if (nio == 0)
		nio = 1; /* No zero width channels */

	chan_width_x[0] = chan_width_x[ny] = nio;
	chan_width_y[0] = chan_width_y[nx] = nio;

	if (ny > 1) {
		separation = 1. / (ny - 2.); /* Norm. distance between two channels. */
		x = 0.; /* This avoids div by zero if ny = 2. */
		chan_width_x[1] = (int) floor(
				cfactor * comp_width(&chan_x_dist, x, separation) + 0.5);

		/* No zero width channels */
		chan_width_x[1] = std::max(chan_width_x[1], 1);

		for (i = 1; i < ny - 1; i++) {
			x = (float) i / ((float) (ny - 2.));
			chan_width_x[i + 1] = (int) floor(
					cfactor * comp_width(&chan_x_dist, x, separation) + 0.5);
			chan_width_x[i + 1] = std::max(chan_width_x[i + 1], 1);
		}
	}

	if (nx > 1) {
		separation = 1. / (nx - 2.); /* Norm. distance between two channels. */
		x = 0.; /* Avoids div by zero if nx = 2. */
		chan_width_y[1] = (int) floor(
				cfactor * comp_width(&chan_y_dist, x, separation) + 0.5);

		chan_width_y[1] = std::max(chan_width_y[1], 1);

		for (i = 1; i < nx - 1; i++) {
			x = (float) i / ((float) (nx - 2.));
			chan_width_y[i + 1] = (int) floor(
					cfactor * comp_width(&chan_y_dist, x, separation) + 0.5);
			chan_width_y[i + 1] = std::max(chan_width_y[i + 1], 1);
		}
	}
#ifdef VERBOSE
	vpr_printf(TIO_MESSAGE_INFO, "\n");
	vpr_printf(TIO_MESSAGE_INFO, "chan_width_x:\n");
	for (i = 0; i <= ny; i++)
		vpr_printf(TIO_MESSAGE_INFO, "%d  ", chan_width_x[i]);
	vpr_printf(TIO_MESSAGE_INFO, "\n");
	vpr_printf(TIO_MESSAGE_INFO, "chan_width_y:\n");
	for (i = 0; i <= nx; i++)
		vpr_printf(TIO_MESSAGE_INFO, "%d  ", chan_width_y[i]);
	vpr_printf(TIO_MESSAGE_INFO, "\n");
#endif

}

static float comp_width(t_chan * chan, float x, float separation) {

	/* Return the relative channel density.  *chan points to a channel   *
	 * functional description data structure, and x is the distance      *   
	 * (between 0 and 1) we are across the chip.  separation is the      *   
	 * distance between two channels, in the 0 to 1 coordinate system.   */

	float val;

	switch (chan->type) {

	case UNIFORM:
		val = chan->peak;
		break;

	case GAUSSIAN:
		val = (x - chan->xpeak) * (x - chan->xpeak)
				/ (2 * chan->width * chan->width);
		val = chan->peak * exp(-val);
		val += chan->dc;
		break;

	case PULSE:
		val = (float) fabs((double) (x - chan->xpeak));
		if (val > chan->width / 2.) {
			val = 0;
		} else {
			val = chan->peak;
		}
		val += chan->dc;
		break;

	case DELTA:
		val = x - chan->xpeak;
		if (val > -separation / 2. && val <= separation / 2.)
			val = chan->peak;
		else
			val = 0.;
		val += chan->dc;
		break;

	default:
		vpr_printf(TIO_MESSAGE_ERROR, "in comp_width: Unknown channel type %d.\n", chan->type);
		exit(1);
		break;
	}

	return (val);
}

/* After placement, logical pins for blocks, and nets must be updated to correspond with physical pins of type */
/* This function should only be called once */
void post_place_sync(INP int L_num_blocks,
		INOUTP const struct s_block block_list[]) {
	int iblk, j, k, inet;
	t_type_ptr type;
	int max_num_block_pins;

	/* Go through each block */
	for (iblk = 0; iblk < L_num_blocks; ++iblk) {
		type = block[iblk].type;
		assert(type->num_pins % type->capacity == 0);
		max_num_block_pins = type->num_pins / type->capacity;
		/* Logical location and physical location is offset by z * max_num_block_pins */
		/* Sync blocks and nets */
		for (j = 0; j < max_num_block_pins; j++) {
			inet = block[iblk].nets[j];
			if (inet != OPEN && block[iblk].z > 0) {
				assert(
						block[iblk]. nets[j + block[iblk].z * max_num_block_pins] == OPEN);
				block[iblk].nets[j + block[iblk].z * max_num_block_pins] =
						block[iblk].nets[j];
				block[iblk].nets[j] = OPEN;
				for (k = 0; k <= clb_net[inet].num_sinks; k++) {
					if (clb_net[inet].node_block[k] == iblk && clb_net[inet]. node_block_pin[k] == j) {
						clb_net[inet].node_block_pin[k] = j
								+ block[iblk].z * max_num_block_pins;
						break;
					}
				}
				assert(k <= clb_net[inet].num_sinks);
			}
		}
	}
}

void free_pb_data(t_pb *pb) {
	int i, j;
	const t_pb_type *pb_type;
	t_rr_node *temp;

	if (pb == NULL || pb->name == NULL) {
		return;
	}

	pb_type = pb->pb_graph_node->pb_type;

	/* free existing rr graph for pb */
	if (pb->rr_graph) {
		temp = rr_node;
		rr_node = pb->rr_graph;
		num_rr_nodes = pb->pb_graph_node->total_pb_pins;
		free_rr_graph();
		rr_node = temp;
	}

	if (pb_type->num_modes > 0) {
		/* Free children of pb */
		for (i = 0; i < pb_type->modes[pb->mode].num_pb_type_children; i++) {
			for (j = 0; j < pb_type->modes[pb->mode].pb_type_children[i].num_pb;
					j++) {
				if (pb->child_pbs[i]) {
					free_pb_data(&pb->child_pbs[i][j]);
				}
			}
		}
	}

	/* Frees all the pb data structures.                                 */
	if (pb->name) {
		free(pb->name);
		if (pb->child_pbs) {
			free(pb->child_pbs);
		}
	}
}