#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include "flp.h"
#include "package.h"
#include "temperature.h"
#include "temperature_block.h"
#include "temperature_grid.h"
#include "util.h"
#include "hotspot.h"
#include "microchannel.h"
#include "materials.h"
#define PRINT_GRID_TRANSIENT 1
void usage(int argc, char **argv)
{
fprintf(stdout, "Usage: %s -f <file> -p <file> [-o <file>] [-c <file>] [-d <file>] [options]\n", argv[0]);
fprintf(stdout, "A thermal simulator that reads power trace from a file and outputs temperatures.\n");
fprintf(stdout, "Options:(may be specified in any order, within \"[]\" means optional)\n");
fprintf(stdout, " -f <file>\tfloorplan input file (e.g. ev6.flp) - overridden by the\n");
fprintf(stdout, " \tlayer configuration file (e.g. layer.lcf) when the\n");
fprintf(stdout, " \tlatter is specified\n");
fprintf(stdout, " -p <file>\tpower trace input file (e.g. gcc.ptrace)\n");
fprintf(stdout, " [-o <file>]\ttransient temperature trace output file - if not provided, only\n");
fprintf(stdout, " \tsteady state temperatures are output to stdout\n");
fprintf(stdout, " [-c <file>]\tinput configuration parameters from file (e.g. hotspot.config)\n");
fprintf(stdout, " [-d <file>]\toutput configuration parameters to file\n");
fprintf(stdout, " [options]\tzero or more options of the form \"-<name> <value>\",\n");
fprintf(stdout, " \toverride the options from config file. e.g. \"-model_type block\" selects\n");
fprintf(stdout, " \tthe block model while \"-model_type grid\" selects the grid model\n");
fprintf(stdout, " [-detailed_3D <on/off]>\tHeterogeneous R-C assignments for specified layers. Requires a .lcf file to be specified\n");
}
void global_config_from_strs(global_config_t *config, str_pair *table, int size)
{
int idx;
if ((idx = get_str_index(table, size, "f")) >= 0) {
if(sscanf(table[idx].value, "%s", config->flp_file) != 1)
fatal("invalid format for configuration parameter flp_file\n");
} else {
strcpy(config->flp_file, NULLFILE);
}
if ((idx = get_str_index(table, size, "p")) >= 0) {
if(sscanf(table[idx].value, "%s", config->p_infile) != 1)
fatal("invalid format for configuration parameter p_infile\n");
} else {
fatal("required parameter p_infile missing. check usage\n");
}
if ((idx = get_str_index(table, size, "o")) >= 0) {
if(sscanf(table[idx].value, "%s", config->t_outfile) != 1)
fatal("invalid format for configuration parameter t_outfile\n");
} else {
strcpy(config->t_outfile, NULLFILE);
}
if ((idx = get_str_index(table, size, "c")) >= 0) {
if(sscanf(table[idx].value, "%s", config->config) != 1)
fatal("invalid format for configuration parameter config\n");
} else {
strcpy(config->config, NULLFILE);
}
if ((idx = get_str_index(table, size, "d")) >= 0) {
if(sscanf(table[idx].value, "%s", config->dump_config) != 1)
fatal("invalid format for configuration parameter dump_config\n");
} else {
strcpy(config->dump_config, NULLFILE);
}
if ((idx = get_str_index(table, size, "detailed_3D")) >= 0) {
if(sscanf(table[idx].value, "%s", config->detailed_3D) != 1)
fatal("invalid format for configuration parameter lc\n");
} else {
strcpy(config->detailed_3D, "off");
}
if ((idx = get_str_index(table, size, "use_microchannels")) >= 0) {
if(sscanf(table[idx].value, "%d", &config->use_microchannels) != 1)
fatal("invalid format for configuration parameter use_microchannels\n");
} else {
config->use_microchannels = 0;
}
if ((idx = get_str_index(table, size, "materials_file")) >= 0) {
if(sscanf(table[idx].value, "%s", config->materials_file) != 1)
fatal("invalid format for configuration parameter materials_file\n");
} else {
strcpy(config->materials_file, NULLFILE);
}
}
int global_config_to_strs(global_config_t *config, str_pair *table, int max_entries)
{
if (max_entries < 8)
fatal("not enough entries in table\n");
sprintf(table[0].name, "f");
sprintf(table[1].name, "p");
sprintf(table[2].name, "o");
sprintf(table[3].name, "c");
sprintf(table[4].name, "d");
sprintf(table[5].name, "detailed_3D");
sprintf(table[6].name, "use_microchannels");
sprintf(table[7].name, "materials_file");
sprintf(table[0].value, "%s", config->flp_file);
sprintf(table[1].value, "%s", config->p_infile);
sprintf(table[2].value, "%s", config->t_outfile);
sprintf(table[3].value, "%s", config->config);
sprintf(table[4].value, "%s", config->dump_config);
sprintf(table[5].value, "%s", config->detailed_3D);
sprintf(table[6].value, "%d", config->use_microchannels);
sprintf(table[7].value, "%s", config->materials_file);
return 8;
}
int read_names(FILE *fp, char **names)
{
char line[LINE_SIZE], temp[LINE_SIZE], *src;
int i;
do {
fgets(line, LINE_SIZE, fp);
if (feof(fp))
fatal("not enough names in trace file\n");
strcpy(temp, line);
src = strtok(temp, " \r\t\n");
} while (!src);
if(line[strlen(line)-1] != '\n')
fatal("line too long\n");
for(i=0,src=line; *src && i < MAX_UNITS; i++) {
if(!sscanf(src, "%s", names[i]))
fatal("invalid format of names\n");
src += strlen(names[i]);
while (isspace((int)*src))
src++;
}
if(*src && i == MAX_UNITS)
fatal("no. of units exceeded limit\n");
return i;
}
int read_vals(FILE *fp, double *vals)
{
char line[LINE_SIZE], temp[LINE_SIZE], *src;
int i;
do {
fgets(line, LINE_SIZE, fp);
if (feof(fp))
return 0;
strcpy(temp, line);
src = strtok(temp, " \r\t\n");
} while (!src);
if(line[strlen(line)-1] != '\n')
fatal("line too long\n");
for(i=0,src=line; *src && i < MAX_UNITS; i++) {
if(!sscanf(src, "%s", temp) || !sscanf(src, "%lf", &vals[i]))
fatal("invalid format of values\n");
src += strlen(temp);
while (isspace((int)*src))
src++;
}
if(*src && i == MAX_UNITS)
fatal("no. of entries exceeded limit\n");
return i;
}
void write_names(FILE *fp, char **names, int size)
{
int i;
for(i=0; i < size-1; i++)
fprintf(fp, "%s\t", names[i]);
fprintf(fp, "%s\n", names[i]);
}
void write_vals(FILE *fp, double *vals, int size)
{
int i;
for(i=0; i < size-1; i++)
fprintf(fp, "%.2f\t", vals[i]);
fprintf(fp, "%.2f\n", vals[i]);
}
char **alloc_names(int nr, int nc)
{
int i;
char **m;
m = (char **) calloc (nr, sizeof(char *));
assert(m != NULL);
m[0] = (char *) calloc (nr * nc, sizeof(char));
assert(m[0] != NULL);
for (i = 1; i < nr; i++)
m[i] = m[0] + nc * i;
return m;
}
void free_names(char **m)
{
free(m[0]);
free(m);
}
void print_dashed_line(int length) {
int i;
for(i = 0; i < length; i++)
printf("-");
printf("\n");
}
#if VERBOSE>1
void print_simulation_summary(thermal_config_t thermal_config, RC_model_t *model) {
if(model->type != GRID_MODEL)
return;
grid_model_t *grid_model = model->grid;
int i;
int nl = grid_model->n_layers;
int hsidx = nl - DEFAULT_PACK_LAYERS + LAYER_SINK;
int spidx = nl - DEFAULT_PACK_LAYERS + LAYER_SP;
int intidx = LAYER_INT;
int silidx = LAYER_SI;
printf("\n\nSimulation Summary:\n");
print_dashed_line(25);
printf("Ambient at %.2f K\n", thermal_config.ambient);
print_dashed_line(25);
for(i = grid_model->n_layers - 1; i >= 0; i--) {
if(i == hsidx)
printf("Heat Sink : %.2f mm\n", grid_model->layers[i].thickness * 1e3);
else if(i == spidx)
printf("Heat Spreader : %.2f mm\n", grid_model->layers[i].thickness * 1e3);
else if(i == intidx && !grid_model->has_lcf)
printf("TIM : %.2f um\n", grid_model->layers[i].thickness * 1e6);
else if(i == silidx && !grid_model->has_lcf)
printf(" Chip : %.2f um\n", grid_model->layers[i].thickness * 1e6);
else if(grid_model->has_lcf)
printf("Layer %d : %.2f um\n", grid_model->layers[i].no, grid_model->layers[i].thickness * 1e6);
else
fatal("Unexpected error in print_simulation_summary\n");
printf(" conductivity = %lf W/(m-K)\n", grid_model->layers[i].k);
printf(" vol. heat capacity = %lf J/(m^3-K)\n", grid_model->layers[i].sp);
if(grid_model->layers[i].has_power)
printf(" dissipates power\n");
if(grid_model->layers[i].is_microchannel)
printf(" microfluidic cooling layer\n");
print_dashed_line(25);
}
printf("\n\n");
}
#endif
int main(int argc, char **argv)
{
int i, j, idx, base = 0, count = 0, n = 0;
int num, size, lines = 0, do_transient = TRUE;
char **names;
double *vals;
FILE *pin, *tout = NULL;
flp_t *flp;
RC_model_t *model;
double *temp = NULL, *power;
double total_power = 0.0;
double *overall_power, *steady_temp;
thermal_config_t thermal_config;
microchannel_config_t *microchannel_config = malloc(sizeof(microchannel_config_t));
global_config_t global_config;
str_pair table[MAX_ENTRIES];
materials_list_t materials_list;
int natural = 0;
double avg_sink_temp = 0;
int natural_convergence = 0;
double r_convec_old;
int do_detailed_3D = FALSE;
int use_microchannels = FALSE;
if (!(argc >= 5 && argc % 2)) {
usage(argc, argv);
return 1;
}
printf("Parsing input files...\n");
size = parse_cmdline(table, MAX_ENTRIES, argc, argv);
global_config_from_strs(&global_config, table, size);
if(!strcmp(global_config.t_outfile, NULLFILE))
do_transient = FALSE;
if (strcmp(global_config.config, NULLFILE))
size += read_str_pairs(&table[size], MAX_ENTRIES, global_config.config);
size = str_pairs_remove_duplicates(table, size);
if(!strcmp(global_config.detailed_3D, "on")){
do_detailed_3D = TRUE;
}
else if(strcmp(global_config.detailed_3D, "off")){
do_detailed_3D = FALSE;
}
default_materials(&materials_list);
if(strncmp(global_config.materials_file, NULLFILE, STR_SIZE)) {
materials_add_from_file(&materials_list, global_config.materials_file);
}
thermal_config = default_thermal_config();
thermal_config_add_from_strs(&thermal_config, &materials_list, table, size);
use_microchannels = global_config.use_microchannels;
if(use_microchannels) {
*microchannel_config = default_microchannel_config();
microchannel_config_add_from_strs(microchannel_config, &materials_list, table, size);
}
else {
microchannel_config = NULL;
}
if (((idx = get_str_index(table, size, "package_model_used")) >= 0) && !(table[idx].value==0)) {
if (thermal_config.package_model_used) {
avg_sink_temp = thermal_config.ambient + SMALL_FOR_CONVEC;
natural = package_model(&thermal_config, table, size, avg_sink_temp);
if (thermal_config.r_convec<R_CONVEC_LOW || thermal_config.r_convec>R_CONVEC_HIGH)
printf("Warning: Heatsink convection resistance is not realistic, double-check your package settings...\n");
}
}
if (strcmp(global_config.dump_config, NULLFILE)) {
size = global_config_to_strs(&global_config, table, MAX_ENTRIES);
size += thermal_config_to_strs(&thermal_config, &table[size], MAX_ENTRIES-size);
if(use_microchannels)
size += microchannel_config_to_strs(microchannel_config, &table[size], MAX_ENTRIES-size);
dump_str_pairs(table, size, global_config.dump_config, "-");
}
if(strcmp(thermal_config.grid_layer_file, NULLFILE)) {
flp = NULL;
if(strcmp(global_config.flp_file, NULLFILE)) {
fprintf(stderr, "Warning: Layer Configuration File %s specified. Overriding floorplan file %s\n", thermal_config.grid_layer_file, global_config.flp_file);
}
}
else if(strcmp(global_config.flp_file, NULLFILE)) {
flp = read_flp(global_config.flp_file, FALSE, FALSE);
}
else {
fatal("Either LCF or FLP file must be specified\n");
}
model = alloc_RC_model(&thermal_config, flp, microchannel_config, &materials_list, do_detailed_3D, use_microchannels);
if (model->type != GRID_MODEL && do_detailed_3D)
fatal("-do_detailed_3D can only be used with -model_type grid\n");
if (model->type == GRID_MODEL && !model->grid->has_lcf && do_detailed_3D)
fatal("-do_detailed_3D can only be used in 3D mode (if a grid_layer_file is specified)\n");
if (use_microchannels && (model->type != GRID_MODEL || !do_detailed_3D))
fatal("-use_microchannels requires -model_type grid and do_detailed_3D on options\n");
if(model->type != GRID_MODEL && strcmp(model->config->grid_steady_file, NULLFILE)) {
warning("Ignoring -grid_steady_file because grid model is not being used\n");
strcpy(model->config->grid_steady_file, NULLFILE);
}
if(model->type != GRID_MODEL && strcmp(model->config->grid_transient_file, NULLFILE)) {
warning("Ignoring -grid_transient_file because grid model is not being used\n");
strcpy(model->config->grid_transient_file, NULLFILE);
}
#if VERBOSE > 1
print_simulation_summary(thermal_config, model);
#endif
printf("Creating thermal circuit...\n");
populate_R_model(model, flp);
if (do_transient)
populate_C_model(model, flp);
#if VERBOSE > 2
debug_print_model(model);
#endif
if (do_transient)
temp = hotspot_vector(model);
power = hotspot_vector(model);
steady_temp = hotspot_vector(model);
overall_power = hotspot_vector(model);
if (do_transient && strcmp(model->config->init_file, NULLFILE)) {
if (!model->config->dtm_used)
read_temp(model, temp, model->config->init_file, FALSE);
else
read_temp(model, temp, model->config->init_file, TRUE);
} else if (do_transient)
set_temp(model, temp, model->config->init_temp);
if (model->type == BLOCK_MODEL)
n = model->block->flp->n_units;
else if (model->type == GRID_MODEL) {
for(i=0; i < model->grid->n_layers; i++)
if (model->grid->layers[i].has_power)
n += model->grid->layers[i].flp->n_units;
} else
fatal("unknown model type\n");
if(!(pin = fopen(global_config.p_infile, "r")))
fatal("unable to open power trace input file\n");
if(do_transient && !(tout = fopen(global_config.t_outfile, "w")))
fatal("unable to open temperature trace file for output\n");
names = alloc_names(MAX_UNITS, STR_SIZE);
if(read_names(pin, names) != n)
fatal("no. of units in floorplan and trace file differ\n");
if (do_transient)
write_names(tout, names, n);
vals = dvector(MAX_UNITS);
while ((num=read_vals(pin, vals)) != 0) {
if(num != n)
fatal("invalid trace file format\n");
if (model->type == BLOCK_MODEL)
for(i=0; i < n; i++)
power[get_blk_index(flp, names[i])] = vals[i];
else
for(i=0, base=0, count=0; i < model->grid->n_layers; i++) {
if(model->grid->layers[i].has_power) {
for(j=0; j < model->grid->layers[i].flp->n_units; j++) {
idx = get_blk_index(model->grid->layers[i].flp, names[count+j]);
power[base+idx] = vals[count+j];
}
count += model->grid->layers[i].flp->n_units;
}
base += model->grid->layers[i].flp->n_units;
}
if (do_transient) {
if (natural) {
avg_sink_temp = calc_sink_temp(model, temp);
natural = package_model(model->config, table, size, avg_sink_temp);
populate_R_model(model, flp);
}
printf("Computing temperatures for t = %e...\n", lines*model->config->sampling_intvl);
if (model->type == BLOCK_MODEL || lines == 0)
compute_temp(model, power, temp, model->config->sampling_intvl);
else
compute_temp(model, power, NULL, model->config->sampling_intvl);
if(model->type == GRID_MODEL && strcmp(model->config->grid_transient_file, NULLFILE)) {
dump_transient_temp_grid(model->grid, lines, model->config->sampling_intvl, model->config->grid_transient_file);
}
if (model->type == BLOCK_MODEL)
for(i=0; i < n; i++)
vals[i] = temp[get_blk_index(flp, names[i])];
else
for(i=0, base=0, count=0; i < model->grid->n_layers; i++) {
if(model->grid->layers[i].has_power) {
for(j=0; j < model->grid->layers[i].flp->n_units; j++) {
idx = get_blk_index(model->grid->layers[i].flp, names[count+j]);
vals[count+j] = temp[base+idx];
}
count += model->grid->layers[i].flp->n_units;
}
base += model->grid->layers[i].flp->n_units;
}
write_vals(tout, vals, n);
}
if (model->type == BLOCK_MODEL)
for(i=0; i < n; i++)
overall_power[i] += power[i];
else
for(i=0, base=0; i < model->grid->n_layers; i++) {
if(model->grid->layers[i].has_power)
for(j=0; j < model->grid->layers[i].flp->n_units; j++)
overall_power[base+j] += power[base+j];
base += model->grid->layers[i].flp->n_units;
}
lines++;
}
if(!lines)
fatal("no power numbers in trace file\n");
if (model->type == BLOCK_MODEL)
for(i=0; i < n; i++) {
overall_power[i] /= lines;
total_power += overall_power[i];
}
else
for(i=0, base=0; i < model->grid->n_layers; i++) {
if(model->grid->layers[i].has_power)
for(j=0; j < model->grid->layers[i].flp->n_units; j++) {
overall_power[base+j] /= lines;
total_power += overall_power[base+j];
}
base += model->grid->layers[i].flp->n_units;
}
natural_convergence = 0;
if (natural) {
while (!natural_convergence) {
r_convec_old = model->config->r_convec;
steady_state_temp(model, overall_power, steady_temp);
avg_sink_temp = calc_sink_temp(model, steady_temp) + SMALL_FOR_CONVEC;
natural = package_model(model->config, table, size, avg_sink_temp);
populate_R_model(model, flp);
if (avg_sink_temp > MAX_SINK_TEMP)
fatal("too high power for a natural convection package -- possible thermal runaway\n");
if (fabs(model->config->r_convec-r_convec_old)<NATURAL_CONVEC_TOL)
natural_convergence = 1;
}
} else {
fprintf(stderr, "Computing steady-state temperatures...\n");
steady_state_temp(model, overall_power, steady_temp);
}
if (strcmp(model->config->steady_file, NULLFILE))
dump_temp(model, steady_temp, model->config->steady_file);
if (model->type == GRID_MODEL &&
strcmp(model->config->grid_steady_file, NULLFILE))
dump_steady_temp_grid(model->grid, model->config->grid_steady_file);
#if VERBOSE > 2
if (model->type == BLOCK_MODEL) {
if (do_transient) {
fprintf(stdout, "printing temp...\n");
dump_dvector(temp, model->block->n_nodes);
}
fprintf(stdout, "printing steady_temp...\n");
dump_dvector(steady_temp, model->block->n_nodes);
} else {
if (do_transient) {
fprintf(stdout, "printing temp...\n");
dump_dvector(temp, model->grid->total_n_blocks + EXTRA);
}
fprintf(stdout, "printing steady_temp...\n");
dump_dvector(steady_temp, model->grid->total_n_blocks + EXTRA);
}
#endif
fclose(pin);
if (do_transient)
fclose(tout);
if(!model->grid->has_lcf)
free_flp(flp, FALSE, FALSE);
delete_RC_model(model);
if (do_transient)
free_dvector(temp);
free_materials(&materials_list);
free_microchannel(microchannel_config);
free_dvector(power);
free_dvector(steady_temp);
free_dvector(overall_power);
free_names(names);
free_dvector(vals);
printf("Simulation complete.\n");
return 0;
}