|
VPR-6.0
|
This graph shows which files directly or indirectly include this file:Go to the source code of this file.
Functions | |
| boolean | IsTimingEnabled (INP t_options Options) |
| void | SetupVPR (INP t_options Options, INP boolean TimingEnabled, OUTP struct s_file_name_opts *FileNameOpts, OUTP t_arch *Arch, OUTP enum e_operation *Operation, OUTP t_model **user_models, OUTP t_model **library_models, OUTP struct s_packer_opts *PackerOpts, OUTP struct s_placer_opts *PlacerOpts, OUTP struct s_annealing_sched *AnnealSched, OUTP struct s_router_opts *RouterOpts, OUTP struct s_det_routing_arch *RoutingArch, OUTP t_segment_inf **Segments, OUTP t_timing_inf *Timing, OUTP boolean *ShowGraphics, OUTP int *GraphPause) |
| void | CheckSetup (INP enum e_operation Operation, INP struct s_placer_opts PlacerOpts, INP struct s_annealing_sched AnnealSched, INP struct s_router_opts RouterOpts, INP struct s_det_routing_arch RoutingArch, INP t_segment_inf *Segments, INP t_timing_inf Timing, INP t_chan_width_dist Chans) |
| void | CheckArch (INP t_arch Arch, INP boolean TimingEnabled) |
| void | CheckOptions (INP t_options Options, INP boolean TimingEnabled) |
| void | ShowSetup (INP t_options Options, INP t_arch Arch, INP boolean TimingEnabled, INP enum e_operation Operation, INP struct s_file_name_opts FileNameOpts, INP struct s_placer_opts PlacerOpts, INP struct s_annealing_sched AnnealSched, INP struct s_router_opts RouterOpts, INP struct s_det_routing_arch RoutingArch, INP t_segment_inf *Segments, INP t_timing_inf Timing) |
Definition at line 19 of file CheckArch.c.
{
CheckSwitches(Arch, TimingEnabled);
CheckSegments(Arch);
}
Here is the call graph for this function:
Here is the caller graph for this function:Checks that options don't conflict and that options aren't specified that may conflict.
Definition at line 14 of file CheckOptions.c.
{
boolean TimingPlacer;
boolean TimingRouter;
boolean default_flow;
default_flow = (Options.Count[OT_ROUTE] == 0 && Options.Count[OT_PLACE] == 0 && Options.Count[OT_PACK] == 0 && Options.Count[OT_TIMING_ANALYZE_ONLY_WITH_NET_DELAY] == 0);
const struct s_TokenPair *Cur;
enum e_OptionBaseToken Yes;
/* Check that all filenames were given */
if((NULL == Options.CircuitName) ||
(NULL == Options.ArchFile))
{
printf(ERRTAG "Not enough args. Need at least 'vpr "
"<archfile> <circuit_name>'\n");
exit(1);
}
/* Check that options aren't over specified */
Cur = OptionBaseTokenList;
while(Cur->Str)
{
if(Options.Count[Cur->Enum] > 1)
{
printf(ERRTAG "Parameter '%s' was specified more than "
"once on command line.\n", Cur->Str);
exit(1);
}
++Cur;
}
/* Todo: Add in checks for packer */
/* Check for conflicting parameters and determine if placer and
* router are on. */
if(Options.Count[OT_TIMING_ANALYZE_ONLY_WITH_NET_DELAY] && (Options.Count[OT_PACK] || Options.Count[OT_PLACE] || Options.Count[OT_ROUTE]))
{
printf(ERRTAG "'cluster'/'route'/'place', and "
"'timing_analysis_only_with_net_delay' are mutually "
"exclusive flags\n");
exit(1);
}
/* If placing and timing is enabled, default to a timing placer */
TimingPlacer = ((Options.Count[OT_PLACE] || default_flow) && TimingEnabled);
if(Options.Count[OT_PLACE_ALGORITHM] > 0)
{
if((PATH_TIMING_DRIVEN_PLACE != Options.PlaceAlgorithm) &&
(NET_TIMING_DRIVEN_PLACE != Options.PlaceAlgorithm))
{
/* Turn off the timing placer if they request a different placer */
TimingPlacer = FALSE;
}
}
/* If routing and timing is enabled, default to a timing router */
TimingRouter = ((Options.Count[OT_ROUTE] || default_flow) && TimingEnabled);
if(Options.Count[OT_ROUTER_ALGORITHM] > 0)
{
if(TIMING_DRIVEN != Options.RouterAlgorithm)
{
/* Turn off the timing router if they request a different router */
TimingRouter = FALSE;
}
}
Yes = OT_BASE_UNKNOWN;
if(Options.Count[OT_SEED] > 0)
{
Yes = OT_SEED;
}
if(Options.Count[OT_INNER_NUM] > 0)
{
Yes = OT_INNER_NUM;
}
if(Options.Count[OT_INIT_T] > 0)
{
Yes = OT_INIT_T;
}
if(Options.Count[OT_ALPHA_T] > 0)
{
Yes = OT_ALPHA_T;
}
if(Options.Count[OT_EXIT_T] > 0)
{
Yes = OT_EXIT_T;
}
if(Options.Count[OT_FIX_PINS] > 0)
{
Yes = OT_FIX_PINS;
}
if(Options.Count[OT_PLACE_ALGORITHM] > 0)
{
Yes = OT_PLACE_ALGORITHM;
}
if(Options.Count[OT_PLACE_COST_TYPE] > 0)
{
Yes = OT_PLACE_COST_TYPE;
}
if(Options.Count[OT_PLACE_COST_EXP] > 0)
{
Yes = OT_PLACE_COST_EXP;
}
if(Options.Count[OT_PLACE_CHAN_WIDTH] > 0)
{
Yes = OT_PLACE_CHAN_WIDTH;
}
if(Options.Count[OT_NUM_REGIONS] > 0)
{
Yes = OT_NUM_REGIONS;
}
if(Options.Count[OT_ENABLE_TIMING_COMPUTATIONS] > 0)
{
Yes = OT_ENABLE_TIMING_COMPUTATIONS;
}
if(Options.Count[OT_BLOCK_DIST] > 0)
{
Yes = OT_BLOCK_DIST;
}
/* Make sure if place is off none of those options were given */
if((Options.Count[OT_PLACE] == 0) && !default_flow && (Yes < OT_BASE_UNKNOWN))
{
Cur = OptionBaseTokenList;
while(Cur->Str)
{
if(Yes == Cur->Enum)
{
printf(ERRTAG
"Option '%s' is not allowed when placement is "
"not run.\n", Cur->Str);
exit(1);
}
++Cur;
}
}
Yes = OT_BASE_UNKNOWN;
if(Options.Count[OT_TIMING_TRADEOFF] > 0)
{
Yes = OT_TIMING_TRADEOFF;
}
if(Options.Count[OT_RECOMPUTE_CRIT_ITER] > 0)
{
Yes = OT_RECOMPUTE_CRIT_ITER;
}
if(Options.Count[OT_INNER_LOOP_RECOMPUTE_DIVIDER] > 0)
{
Yes = OT_INNER_LOOP_RECOMPUTE_DIVIDER;
}
if(Options.Count[OT_TD_PLACE_EXP_FIRST] > 0)
{
Yes = OT_TD_PLACE_EXP_FIRST;
}
if(Options.Count[OT_TD_PLACE_EXP_LAST] > 0)
{
Yes = OT_TD_PLACE_EXP_LAST;
}
/* Make sure if place is off none of those options were given */
if((FALSE == TimingPlacer) && (Yes < OT_BASE_UNKNOWN))
{
Cur = OptionBaseTokenList;
while(Cur->Str)
{
if(Yes == Cur->Enum)
{
printf(ERRTAG
"Option '%s' is not allowed when timing placement is "
"not used.\n", Cur->Str);
exit(1);
}
++Cur;
}
}
Yes = OT_BASE_UNKNOWN;
if(Options.Count[OT_ROUTE_TYPE] > 0)
{
Yes = OT_ROUTE_TYPE;
}
if(Options.Count[OT_ROUTE_CHAN_WIDTH] > 0)
{
Yes = OT_ROUTE_CHAN_WIDTH;
}
if(Options.Count[OT_ROUTER_ALGORITHM] > 0)
{
Yes = OT_ROUTER_ALGORITHM;
}
if(Options.Count[OT_MAX_ROUTER_ITERATIONS] > 0)
{
Yes = OT_MAX_ROUTER_ITERATIONS;
}
if(Options.Count[OT_INITIAL_PRES_FAC] > 0)
{
Yes = OT_INITIAL_PRES_FAC;
}
if(Options.Count[OT_FIRST_ITER_PRES_FAC] > 0)
{
Yes = OT_FIRST_ITER_PRES_FAC;
}
if(Options.Count[OT_PRES_FAC_MULT] > 0)
{
Yes = OT_PRES_FAC_MULT;
}
if(Options.Count[OT_ACC_FAC] > 0)
{
Yes = OT_ACC_FAC;
}
if(Options.Count[OT_BB_FACTOR] > 0)
{
Yes = OT_BB_FACTOR;
}
if(Options.Count[OT_BASE_COST_TYPE] > 0)
{
Yes = OT_BASE_COST_TYPE;
}
if(Options.Count[OT_BEND_COST] > 0)
{
Yes = OT_BEND_COST;
}
if(Options.Count[OT_BASE_COST_TYPE] > 0)
{
Yes = OT_BASE_COST_TYPE;
}
if(Options.Count[OT_ASTAR_FAC] > 0)
{
Yes = OT_ASTAR_FAC;
}
Yes = OT_BASE_UNKNOWN;
if(Options.Count[OT_MAX_CRITICALITY] > 0)
{
Yes = OT_MAX_CRITICALITY;
}
if(Options.Count[OT_CRITICALITY_EXP] > 0)
{
Yes = OT_CRITICALITY_EXP;
}
/* Make sure if timing router is off none of those options were given */
if((FALSE == TimingRouter) && (Yes < OT_BASE_UNKNOWN))
{
Cur = OptionBaseTokenList;
while(Cur->Str)
{
if(Yes == Cur->Enum)
{
printf(ERRTAG
"Option '%s' is not allowed when timing router is "
"not used.\n", Cur->Str);
exit(1);
}
++Cur;
}
}
}
Here is the caller graph for this function:| void CheckSetup | ( | INP enum e_operation | Operation, |
| INP struct s_placer_opts | PlacerOpts, | ||
| INP struct s_annealing_sched | AnnealSched, | ||
| INP struct s_router_opts | RouterOpts, | ||
| INP struct s_det_routing_arch | RoutingArch, | ||
| INP t_segment_inf * | Segments, | ||
| INP t_timing_inf | Timing, | ||
| INP t_chan_width_dist | Chans | ||
| ) |
Definition at line 11 of file CheckSetup.c.
{
int i;
int Tmp;
if((NONLINEAR_CONG == PlacerOpts.place_cost_type) &&
(!PlacerOpts.doPlacement || !RouterOpts.doRouting) &&
(PLACE_ALWAYS == PlacerOpts.place_freq))
{
printf(ERRTAG "Replacing using the nonlinear congestion option "
"for each channel width makes sense only for full "
"place and route.\n");
exit(1);
}
if((NONLINEAR_CONG == PlacerOpts.place_cost_type) &&
(BOUNDING_BOX_PLACE != PlacerOpts.place_algorithm))
{
/* Note that this may work together, but I have not tested it */
printf(ERRTAG
"Cannot use non-linear placement only supported with "
"bounding box placement\n");
exit(1);
}
if((GLOBAL == RouterOpts.route_type) &&
(TIMING_DRIVEN == RouterOpts.router_algorithm))
{
printf(ERRTAG "The global router does not support timing-drvien "
"routing.\n");
exit(1);
}
if((GLOBAL == RouterOpts.route_type) &&
(BOUNDING_BOX_PLACE != PlacerOpts.place_algorithm))
{
/* Works, but very weird. Can't optimize timing well, since you're
* not doing proper architecture delay modelling. */
printf(WARNTAG
"Using global routing with timing-driven placement. "
"This is allowed, but strange, and circuit speed will suffer.\n");
}
if((FALSE == Timing.timing_analysis_enabled) &&
((PlacerOpts.place_algorithm == NET_TIMING_DRIVEN_PLACE) ||
(PlacerOpts.place_algorithm == PATH_TIMING_DRIVEN_PLACE)))
{
/* May work, not tested */
printf(ERRTAG
"Timing analysis must be enabled for timing-driven placement\n");
exit(1);
}
if(!PlacerOpts.doPlacement && (USER == PlacerOpts.pad_loc_type))
{
printf(ERRTAG "A pad location file requires that placement is enabled.\n");
exit(1);
}
if(RouterOpts.doRouting)
{
if((TIMING_DRIVEN == RouterOpts.router_algorithm) &&
(FALSE == Timing.timing_analysis_enabled))
{
printf(ERRTAG
"Cannot perform timing-driven routing when timing "
"analysis is disabled.\n");
exit(1);
}
if((FALSE == Timing.timing_analysis_enabled) &&
(DEMAND_ONLY != RouterOpts.base_cost_type))
{
printf(ERRTAG
"base_cost_type must be demand_only when timing "
"analysis is disabled.\n");
exit(1);
}
}
if((TIMING_ANALYSIS_ONLY == Operation) &&
(FALSE == Timing.timing_analysis_enabled))
{
printf(ERRTAG
"-timing_analyze_only_with_net_delay option requires "
"that timing analysis not be disabled.\n");
exit(1);
}
if((NONLINEAR_CONG == PlacerOpts.place_cost_type) &&
((PlacerOpts.num_regions > nx) || (PlacerOpts.num_regions > ny)))
{
printf(ERRTAG "Cannot use more regions than clbs in "
"placement cost function.\n");
exit(1);
}
if(DETAILED == RouterOpts.route_type)
{
if((Chans.chan_x_dist.type != UNIFORM) ||
(Chans.chan_y_dist.type != UNIFORM) ||
(Chans.chan_x_dist.peak != Chans.chan_y_dist.peak) ||
(Chans.chan_x_dist.peak != Chans.chan_width_io))
{
printf(ERRTAG "Detailed routing currently only supported "
"on FPGAs with all channels of equal width.\n");
exit(1);
}
}
for(i = 0; i < RoutingArch.num_segment; ++i)
{
Tmp = Segments[i].opin_switch;
if(FALSE == switch_inf[Tmp].buffered)
{
printf(ERRTAG "opin_switch (#%d) of segment type #%d "
"is not buffered.\n", Tmp, i);
exit(1);
}
}
if(UNI_DIRECTIONAL == RoutingArch.directionality)
{
if((RouterOpts.fixed_channel_width != NO_FIXED_CHANNEL_WIDTH) &&
(RouterOpts.fixed_channel_width % 2 > 0))
{
printf(ERRTAG
"Routing channel width must be even for unidirectional\n");
exit(1);
}
if((PlacerOpts.place_chan_width != NO_FIXED_CHANNEL_WIDTH) &&
(PlacerOpts.place_chan_width % 2 > 0))
{
printf(ERRTAG
"Place channel width must be even for unidirectional\n");
exit(1);
}
}
}
Here is the caller graph for this function:Determines whether timing analysis should be on or off. Unless otherwise specified, always default to timing.
Definition at line 814 of file SetupVPR.c.
{
/* First priority to the '--timing_analysis' flag */
if(Options.Count[OT_TIMING_ANALYSIS])
{
return Options.TimingAnalysis;
}
return TRUE;
}
Here is the caller graph for this function:| void SetupVPR | ( | INP t_options | Options, |
| INP boolean | TimingEnabled, | ||
| OUTP struct s_file_name_opts * | FileNameOpts, | ||
| OUTP t_arch * | Arch, | ||
| OUTP enum e_operation * | Operation, | ||
| OUTP t_model ** | user_models, | ||
| OUTP t_model ** | library_models, | ||
| OUTP struct s_packer_opts * | PackerOpts, | ||
| OUTP struct s_placer_opts * | PlacerOpts, | ||
| OUTP struct s_annealing_sched * | AnnealSched, | ||
| OUTP struct s_router_opts * | RouterOpts, | ||
| OUTP struct s_det_routing_arch * | RoutingArch, | ||
| OUTP t_segment_inf ** | Segments, | ||
| OUTP t_timing_inf * | Timing, | ||
| OUTP boolean * | ShowGraphics, | ||
| OUTP int * | GraphPause | ||
| ) |
Sets VPR parameters and defaults. Does not do any error checking as this should have been done by the various input checkers
Definition at line 45 of file SetupVPR.c.
{
int i, j, len;
/* init default filenames */
if(Options.BlifFile == NULL) {
len = strlen(Options.CircuitName) + 6; /* circuit_name.blif/0*/
if(Options.OutFilePrefix != NULL) {
len += strlen(Options.OutFilePrefix);
}
Options.BlifFile = my_calloc(len,sizeof(char));
if(Options.OutFilePrefix == NULL) {
sprintf(Options.BlifFile, "%s.blif", Options.CircuitName);
} else {
sprintf(Options.BlifFile, "%s%s.blif", Options.OutFilePrefix, Options.CircuitName);
}
}
if(Options.NetFile == NULL) {
len = strlen(Options.CircuitName) + 5; /* circuit_name.net/0*/
if(Options.OutFilePrefix != NULL) {
len += strlen(Options.OutFilePrefix);
}
Options.NetFile = my_calloc(len,sizeof(char));
if(Options.OutFilePrefix == NULL) {
sprintf(Options.NetFile, "%s.net", Options.CircuitName);
} else {
sprintf(Options.NetFile, "%s%s.net", Options.OutFilePrefix, Options.CircuitName);
}
}
if(Options.PlaceFile == NULL) {
len = strlen(Options.CircuitName) + 7; /* circuit_name.place/0*/
if(Options.OutFilePrefix != NULL) {
len += strlen(Options.OutFilePrefix);
}
Options.PlaceFile = my_calloc(len,sizeof(char));
if(Options.OutFilePrefix == NULL) {
sprintf(Options.PlaceFile, "%s.place", Options.CircuitName);
} else {
sprintf(Options.PlaceFile, "%s%s.place", Options.OutFilePrefix, Options.CircuitName);
}
}
if(Options.RouteFile == NULL) {
len = strlen(Options.CircuitName) + 7; /* circuit_name.route/0*/
if(Options.OutFilePrefix != NULL) {
len += strlen(Options.OutFilePrefix);
}
Options.RouteFile = my_calloc(len,sizeof(char));
if(Options.OutFilePrefix == NULL) {
sprintf(Options.RouteFile, "%s.route", Options.CircuitName);
} else {
sprintf(Options.RouteFile, "%s%s.route", Options.OutFilePrefix, Options.CircuitName);
}
}
FileNameOpts->CircuitName = Options.CircuitName;
FileNameOpts->ArchFile = Options.ArchFile;
FileNameOpts->BlifFile = Options.BlifFile;
FileNameOpts->NetFile = Options.NetFile;
FileNameOpts->PlaceFile = Options.PlaceFile;
FileNameOpts->RouteFile = Options.RouteFile;
FileNameOpts->OutFilePrefix = Options.OutFilePrefix;
SetupOperation(Options, Operation);
SetupPlacerOpts(Options, TimingEnabled, PlacerOpts);
SetupAnnealSched(Options, AnnealSched);
SetupRouterOpts(Options, TimingEnabled, RouterOpts);
XmlReadArch(Options.ArchFile, TimingEnabled, Arch,
&type_descriptors, &num_types);
*user_models = Arch->models;
*library_models = Arch->model_library;
/* TODO: this is inelegant, I should be populating this information in XmlReadArch */
EMPTY_TYPE = NULL;
FILL_TYPE = NULL;
IO_TYPE = NULL;
for(i = 0; i < num_types; i++) {
if(strcmp(type_descriptors[i].name, "<EMPTY>") == 0) {
EMPTY_TYPE = &type_descriptors[i];
} else if(strcmp(type_descriptors[i].name, "io") == 0) {
IO_TYPE = &type_descriptors[i];
} else {
for(j = 0; j < type_descriptors[i].num_grid_loc_def; j++) {
if(type_descriptors[i].grid_loc_def[j].grid_loc_type == FILL) {
assert(FILL_TYPE == NULL);
FILL_TYPE = &type_descriptors[i];
}
}
}
}
assert(EMPTY_TYPE != NULL && FILL_TYPE != NULL && IO_TYPE != NULL);
*Segments = Arch->Segments;
RoutingArch->num_segment = Arch->num_segments;
SetupSwitches(*Arch, RoutingArch, Arch->Switches, Arch->num_switches);
SetupRoutingArch(*Arch, RoutingArch);
SetupTiming(Options, *Arch, TimingEnabled, *Operation,
*PlacerOpts, *RouterOpts, Timing);
SetupPackerOpts(Options, TimingEnabled, *Arch, Options.NetFile, PackerOpts);
/* init global variables */
OutFilePrefix = Options.OutFilePrefix;
grid_logic_tile_area = Arch->grid_logic_tile_area;
ipin_mux_trans_size = Arch->ipin_mux_trans_size;
/* Set seed for pseudo-random placement, default seed to 1 */
PlacerOpts->seed = 1;
if(Options.Count[OT_SEED])
{
PlacerOpts->seed = Options.Seed;
}
my_srandom(PlacerOpts->seed);
printf("Building complex block graph \n");
alloc_and_load_all_pb_graphs();
#ifdef DUMP_PB_GRAPH
echo_pb_graph("pb_graph.echo");
#endif
*GraphPause = 1; /* DEFAULT */
if(Options.Count[OT_AUTO])
{
*GraphPause = Options.GraphPause;
}
#ifdef NO_GRAPHICS
*ShowGraphics = FALSE; /* DEFAULT */
#else /* NO_GRAPHICS */
*ShowGraphics = TRUE; /* DEFAULT */
if(Options.Count[OT_NODISP])
{
*ShowGraphics = FALSE;
}
#endif /* NO_GRAPHICS */
#ifdef CREATE_ECHO_FILES
EchoArch("arch.echo", type_descriptors, num_types, Arch);
#endif
}
Here is the call graph for this function:
Here is the caller graph for this function:| void ShowSetup | ( | INP t_options | Options, |
| INP t_arch | Arch, | ||
| INP boolean | TimingEnabled, | ||
| INP enum e_operation | Operation, | ||
| INP struct s_file_name_opts | FileNameOpts, | ||
| INP struct s_placer_opts | PlacerOpts, | ||
| INP struct s_annealing_sched | AnnealSched, | ||
| INP struct s_router_opts | RouterOpts, | ||
| INP struct s_det_routing_arch | RoutingArch, | ||
| INP t_segment_inf * | Segments, | ||
| INP t_timing_inf | Timing | ||
| ) |
Definition at line 24 of file ShowSetup.c.
{
int i, j, num_p_inputs, num_p_outputs;
int *num_blocks_type;
num_blocks_type = my_calloc(num_types, sizeof(int));
printf("Timing analysis: %s\n", (TimingEnabled ? "ON" : "OFF"));
printf("\n");
printf("Circuit netlist file: %s\n", FileNameOpts.NetFile);
printf("Circuit placement file: %s\n", FileNameOpts.PlaceFile);
printf("Circuit routing file: %s\n", FileNameOpts.RouteFile);
ShowOperation(Operation);
printf("Placer: %s\n", (PlacerOpts.doPlacement ? "ENABLED" : "DISABLED"));
printf("Router: %s\n", (RouterOpts.doRouting ? "ENABLED" : "DISABLED"));
if(PlacerOpts.doPlacement) {
ShowPlacerOpts(Options, PlacerOpts, AnnealSched);
}
if(RouterOpts.doRouting)
{
ShowRouterOpts(RouterOpts);
}
if(DETAILED == RouterOpts.route_type)
ShowRoutingArch(RoutingArch);
printf("\n");
printf("Netlist num_nets: %d\n", num_nets);
printf("Netlist num_blocks: %d\n", num_blocks);
for(i = 0; i < num_types; i++)
{
num_blocks_type[i] = 0;
}
/* Count I/O input and output pads */
num_p_inputs = 0;
num_p_outputs = 0;
for(i = 0; i < num_blocks; i++)
{
num_blocks_type[block[i].type->index]++;
if(block[i].type == IO_TYPE)
{
for(j = 0; j < IO_TYPE->num_pins; j++)
{
if(block[i].nets[j] != OPEN)
{
if(IO_TYPE->
class_inf[IO_TYPE->pin_class[j]].
type == DRIVER)
{
num_p_inputs++;
}
else
{
assert(IO_TYPE->
class_inf[IO_TYPE->
pin_class[j]].
type == RECEIVER);
num_p_outputs++;
}
}
}
}
}
for(i = 0; i < num_types; i++)
{
if(IO_TYPE != &type_descriptors[i]) {
printf("Netlist %s blocks: %d\n", type_descriptors[i].name, num_blocks_type[i]);
}
}
/* Print out each block separately instead */
printf("Netlist inputs pins: %d\n", num_p_inputs);
printf("Netlist output pins: %d\n", num_p_outputs);
printf("\n");
free(num_blocks_type);
}
Here is the call graph for this function:
Here is the caller graph for this function: