OpenFPGA/vpr7_x2p/vpr/SRC/fpga_x2p/verilog/verilog_sdc.c

1930 lines
69 KiB
C

/***********************************/
/* SPICE Modeling for VPR */
/* Xifan TANG, EPFL/LSI */
/***********************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <assert.h>
#include <sys/stat.h>
#include <unistd.h>
/* Include vpr structs*/
#include "util.h"
#include "physical_types.h"
#include "vpr_types.h"
#include "globals.h"
#include "rr_graph_util.h"
#include "rr_graph.h"
#include "rr_graph2.h"
#include "route_common.h"
#include "vpr_utils.h"
/* Include SPICE support headers*/
#include "linkedlist.h"
#include "fpga_x2p_types.h"
#include "fpga_x2p_utils.h"
#include "fpga_x2p_backannotate_utils.h"
#include "fpga_x2p_mux_utils.h"
#include "fpga_x2p_pbtypes_utils.h"
#include "fpga_x2p_bitstream_utils.h"
#include "fpga_x2p_rr_graph_utils.h"
#include "fpga_x2p_globals.h"
/* Include Verilog support headers*/
#include "verilog_global.h"
#include "verilog_utils.h"
#include "verilog_routing.h"
#include "verilog_tcl_utils.h"
#include "verilog_sdc_pb_types.h"
#include "verilog_sdc.h"
/* options for report timing */
typedef struct s_sdc_opts t_sdc_opts;
struct s_sdc_opts {
char* sdc_dir;
boolean constrain_sbs;
boolean constrain_cbs;
boolean constrain_pbs;
boolean constrain_routing_channels;
boolean break_loops;
boolean break_loops_mux;
};
float get_switch_sdc_tmax (t_switch_inf* cur_switch_inf) {
return cur_switch_inf->R * cur_switch_inf->Cout + cur_switch_inf->Tdel;
}
float get_routing_seg_sdc_tmax (t_segment_inf* cur_seg) {
return cur_seg->Rmetal * cur_seg->Cmetal;
}
boolean is_rr_node_to_be_disable_for_analysis(t_rr_node* cur_rr_node) {
/* Conditions to enable timing analysis for a node
* 1st condition: it have a valid vpack_net_number
* 2nd condition: it is not an parasitic net
* 3rd condition: it is not a global net
*/
if ( (OPEN != cur_rr_node->vpack_net_num)
&& (FALSE == cur_rr_node->is_parasitic_net)
&& (FALSE == vpack_net[cur_rr_node->vpack_net_num].is_global)
&& (FALSE == vpack_net[cur_rr_node->vpack_net_num].is_const_gen) ){
return FALSE;
}
return TRUE;
}
/* TO avoid combinational loops caused by memories
* We disable all the timing paths starting from an output of memory cell
*/
void verilog_generate_sdc_break_loop_sram(FILE* fp,
t_sram_orgz_info* cur_sram_orgz_info) {
t_spice_model* mem_model = NULL;
int iport, ipin;
char* port_name = NULL;
int num_output_ports = 0;
t_spice_model_port** output_ports = NULL;
/* Check */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler!\n",
__FILE__, __LINE__);
exit(1);
}
get_sram_orgz_info_mem_model(cur_sram_orgz_info, &mem_model);
assert (NULL != mem_model);
/* Find the output ports of mem_model */
output_ports = find_spice_model_ports(mem_model, SPICE_MODEL_PORT_OUTPUT, &num_output_ports, TRUE);
for (iport = 0; iport < num_output_ports; iport++) {
for (ipin = 0; ipin < output_ports[iport]->size; ipin++) {
if (TRUE == mem_model->dump_explicit_port_map) {
port_name = output_ports[iport]->lib_name;
} else {
port_name = output_ports[iport]->prefix;
}
/* Disable the timing for all the memory cells */
fprintf(fp,
"set_disable_timing [get_pins -filter \"name == %s",
port_name);
if (1 < output_ports[iport]->size) {
fprintf(fp, "[%d]", ipin);
}
fprintf(fp, "\" ");
fprintf(fp,
"-of [get_cells -hier -filter \"ref_lib_cell_name == %s\"]]\n",
mem_model->name);
}
}
/* Free */
my_free(output_ports);
return;
}
/* Statisitcs for input sizes and structures of MUXes
* used in FPGA architecture
* Disable timing starting from any MUX outputs
*/
void verilog_generate_sdc_break_loop_mux(FILE* fp,
int num_switch,
t_switch_inf* switches,
t_spice* spice,
t_det_routing_arch* routing_arch) {
/* We have linked list whichs stores spice model information of multiplexer*/
t_llist* muxes_head = NULL;
t_llist* temp = NULL;
t_spice_mux_model* cur_spice_mux_model = NULL;
int num_input_ports = 0;
t_spice_model_port** input_ports = NULL;
int num_output_ports = 0;
t_spice_model_port** output_ports = NULL;
char* SPC_cell_suffix = "_SPC";
/* Check */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler!\n",
__FILE__, __LINE__);
exit(1);
}
/* Alloc the muxes*/
muxes_head = stats_spice_muxes(num_switch, switches, spice, routing_arch);
/* Print mux netlist one by one*/
temp = muxes_head;
while(temp) {
assert(NULL != temp->dptr);
cur_spice_mux_model = (t_spice_mux_model*)(temp->dptr);
input_ports = find_spice_model_ports(cur_spice_mux_model->spice_model, SPICE_MODEL_PORT_INPUT, &num_input_ports, TRUE);
output_ports = find_spice_model_ports(cur_spice_mux_model->spice_model, SPICE_MODEL_PORT_OUTPUT, &num_output_ports, TRUE);
assert(1 == num_input_ports);
assert(1 == num_input_ports);
/* Check the Input port size */
if ( (NULL != cur_spice_mux_model->spice_model->verilog_netlist)
&& (cur_spice_mux_model->size != input_ports[0]->size)) {
vpr_printf(TIO_MESSAGE_ERROR,
"(File:%s,[LINE%d])User-defined MUX SPICE MODEL(%s) size(%d) unmatch with the architecture needs(%d)!\n",
__FILE__, __LINE__, cur_spice_mux_model->spice_model->name, input_ports[0]->size,cur_spice_mux_model->size);
exit(1);
}
/* Use the user defined ports, Bypass LUT MUXes */
if (SPICE_MODEL_MUX == cur_spice_mux_model->spice_model->type) {
fprintf(fp,
"set_disable_timing [get_pins -filter \"name =~ %s*\" ",
output_ports[0]->prefix);
fprintf(fp,
"-of [get_cells -hier -filter \"ref_lib_cell_name == %s\"]]\n",
gen_verilog_one_mux_module_name(cur_spice_mux_model->spice_model, cur_spice_mux_model->size));
/* For SPC cells*/
fprintf(fp,
"set_disable_timing [get_pins -filter \"name =~ %s*\" ",
output_ports[0]->prefix);
fprintf(fp,
"-of [get_cells -hier -filter \"ref_lib_cell_name =~ %s%s*\"]]\n",
gen_verilog_one_mux_module_name(cur_spice_mux_model->spice_model, cur_spice_mux_model->size),
SPC_cell_suffix);
}
/* Free */
my_free(output_ports);
my_free(input_ports);
/* Move on to the next*/
temp = temp->next;
}
/* remember to free the linked list*/
free_muxes_llist(muxes_head);
return;
}
void verilog_generate_sdc_clock_period(t_sdc_opts sdc_opts,
float critical_path_delay) {
FILE* fp = NULL;
char* fname = my_strcat(sdc_opts.sdc_dir, sdc_clock_period_file_name);
t_llist* temp = NULL;
t_spice_model_port* temp_port = NULL;
int ipin;
float clock_period = 10.;
int iport;
int num_clock_ports = 0;
t_spice_model_port** clock_port = NULL;
vpr_printf(TIO_MESSAGE_INFO,
"Generating SDC for constraining clocks in P&R flow: %s ...\n",
fname);
/* Print the muxes netlist*/
fp = fopen(fname, "w");
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Failure in create SDC constraints %s",
__FILE__, __LINE__, fname);
exit(1);
}
/* Generate the descriptions*/
dump_verilog_sdc_file_header(fp, "Clock contraints for PnR");
/* Create clock */
/* Get clock port from the global port */
get_fpga_x2p_global_all_clock_ports(global_ports_head, &num_clock_ports, &clock_port);
/* Print comments */
fprintf(fp,
"##################################################\n");
fprintf(fp,
"### Create clock #\n");
fprintf(fp,
"##################################################\n");
/* Create a clock */
for (iport = 0; iport < num_clock_ports; iport++) {
fprintf(fp, "create_clock ");
if (NULL != strstr(clock_port[iport]->prefix,"prog")) {
fprintf(fp, "%s -period 100 -waveform {0 50}\n",
clock_port[iport]->prefix,
critical_path_delay, critical_path_delay/2);
}
else {
fprintf(fp, "%s -period %.4g -waveform {0 %.4g}\n",
clock_port[iport]->prefix,
critical_path_delay, critical_path_delay/2);
}
}
/* Find the global clock ports */
temp = global_ports_head;
while (NULL != temp) {
/* Get the port */
temp_port = (t_spice_model_port*)(temp->dptr);
/* We only care clock ports */
if (SPICE_MODEL_PORT_CLOCK == temp_port->type) {
/* Go to next */
temp = temp->next;
continue;
}
for (ipin = 0; ipin < temp_port->size; ipin++) {
fprintf(fp,
"create_clock -name {%s[%d]} -period %.2g -waveform {0.00 %.2g} [list [get_ports {%s[%d]}]]\n",
temp_port->prefix, ipin,
clock_period, clock_period/2,
temp_port->prefix, ipin);
fprintf(fp,
"set_drive 0 %s[%d]\n",
temp_port->prefix, ipin);
}
/* Go to next */
temp = temp->next;
}
/* close file */
fclose(fp);
return;
}
void verilog_generate_sdc_break_loop_sb(FILE* fp,
int LL_nx, int LL_ny) {
int ix, iy;
t_sb* cur_sb_info = NULL;
int side, itrack;
/* Check the file handler */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler for SDC generation",
__FILE__, __LINE__);
exit(1);
}
/* Go for each SB */
for (ix = 0; ix < (LL_nx + 1); ix++) {
for (iy = 0; iy < (LL_ny + 1); iy++) {
cur_sb_info = &(sb_info[ix][iy]);
for (side = 0; side < cur_sb_info->num_sides; side++) {
for (itrack = 0; itrack < cur_sb_info->chan_width[side]; itrack++) {
assert((CHANX == cur_sb_info->chan_rr_node[side][itrack]->type)
||(CHANY == cur_sb_info->chan_rr_node[side][itrack]->type));
/* We only care the output port and it should indicate a SB mux */
if ( (OUT_PORT != cur_sb_info->chan_rr_node_direction[side][itrack])
|| (FALSE != check_drive_rr_node_imply_short(*cur_sb_info, cur_sb_info->chan_rr_node[side][itrack], side))) {
continue;
}
/* Bypass if we have only 1 driving node */
if (1 == cur_sb_info->chan_rr_node[side][itrack]->num_drive_rr_nodes) {
continue;
}
/* Disable timing here */
set_disable_timing_one_sb_output(fp, cur_sb_info,
cur_sb_info->chan_rr_node[side][itrack]);
}
}
}
}
return;
}
void verilog_generate_sdc_break_loops(t_sram_orgz_info* cur_sram_orgz_info,
t_sdc_opts sdc_opts,
int LL_nx, int LL_ny,
int num_switch,
t_switch_inf* switches,
t_spice* spice,
t_det_routing_arch* routing_arch) {
FILE* fp = NULL;
char* fname = my_strcat(sdc_opts.sdc_dir, sdc_break_loop_file_name);
vpr_printf(TIO_MESSAGE_INFO,
"Generating SDC for breaking combinational loops in P&R flow: %s ...\n",
fname);
/* Create file handler */
fp = fopen(fname, "w");
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Failure in create SDC constraints %s",
__FILE__, __LINE__, fname);
exit(1);
}
/* Generate the descriptions*/
dump_verilog_sdc_file_header(fp, "Break Combinational Loops for PnR");
/* 1. Break loops from Memory Cells */
verilog_generate_sdc_break_loop_sram(fp, cur_sram_orgz_info);
/* 2. Break loops from Multiplexer Output */
if (TRUE == sdc_opts.break_loops_mux) {
verilog_generate_sdc_break_loop_mux(fp, num_switch, switches, spice, routing_arch);
}
/* 3. Break loops from any SB output */
verilog_generate_sdc_break_loop_sb(fp, LL_nx, LL_ny);
/* Close the file*/
fclose(fp);
/* Free strings */
my_free(fname);
return;
}
/* Constrain a path within a Switch block,
* If this indicates a metal wire, we constraint to be 0 delay
*/
void verilog_generate_sdc_constrain_one_sb_path(FILE* fp,
t_sb* cur_sb_info,
t_rr_node* src_rr_node,
t_rr_node* des_rr_node,
float tmax) {
/* Check the file handler */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler for SDC generation",
__FILE__, __LINE__);
exit(1);
}
/* Check */
assert ((OPIN == src_rr_node->type)
||(CHANX == src_rr_node->type)
||(CHANY == src_rr_node->type));
assert ((CHANX == des_rr_node->type)
||(CHANY == des_rr_node->type));
fprintf(fp, "set_max_delay");
fprintf(fp, " -from ");
fprintf(fp, "%s/",
gen_verilog_one_sb_instance_name(cur_sb_info));
dump_verilog_one_sb_routing_pin(fp, cur_sb_info, src_rr_node);
fprintf(fp, " -to ");
fprintf(fp, "%s/",
gen_verilog_one_sb_instance_name(cur_sb_info));
dump_verilog_one_sb_chan_pin(fp, cur_sb_info, des_rr_node, OUT_PORT);
/* If src_node == des_node, this is a metal wire */
fprintf(fp, " %.2g", tmax);
fprintf(fp, "\n");
return;
}
void verilog_generate_sdc_constrain_one_sb_mux(FILE* fp,
t_sb* cur_sb_info,
t_rr_node* wire_rr_node) {
int iedge, switch_id;
float switch_delay = 0.;
/* Check the file handler */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler for SDC generation",
__FILE__, __LINE__);
exit(1);
}
assert( ( CHANX == wire_rr_node->type )
|| ( CHANY == wire_rr_node->type ));
/* Find the starting points */
for (iedge = 0; iedge < wire_rr_node->num_drive_rr_nodes; iedge++) {
/* Get the switch delay */
switch_id = wire_rr_node->drive_switches[iedge];
switch_delay = get_switch_sdc_tmax (&(switch_inf[switch_id]));
/* Constrain a path */
verilog_generate_sdc_constrain_one_sb_path(fp, cur_sb_info,
wire_rr_node->drive_rr_nodes[iedge],
wire_rr_node,
switch_delay);
}
return;
}
/* Constrain a path within a Switch block,
* If this indicates a metal wire, we constraint to be 0 delay
*/
void verilog_generate_sdc_constrain_one_cb_path(FILE* fp,
t_cb* cur_cb_info,
t_rr_node* src_rr_node,
t_rr_node* des_rr_node,
int des_rr_node_grid_side,
float tmax) {
/* Check the file handler */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler for SDC generation",
__FILE__, __LINE__);
exit(1);
}
/* Check */
assert ((INC_DIRECTION == src_rr_node->direction)
||(DEC_DIRECTION == src_rr_node->direction));
assert ((CHANX == src_rr_node->type)
||(CHANY == src_rr_node->type));
assert (IPIN == des_rr_node->type);
fprintf(fp, "set_max_delay");
fprintf(fp, " -from ");
fprintf(fp, "%s/",
gen_verilog_one_cb_instance_name(cur_cb_info));
fprintf(fp, "%s",
gen_verilog_routing_channel_one_midout_name( cur_cb_info,
src_rr_node->ptc_num));
fprintf(fp, " -to ");
fprintf(fp, "%s/",
gen_verilog_one_cb_instance_name(cur_cb_info));
dump_verilog_grid_side_pin_with_given_index(fp, IPIN, /* This is an output of a connection box */
des_rr_node->ptc_num,
des_rr_node_grid_side,
des_rr_node->xlow,
des_rr_node->ylow,
FALSE);
/* If src_node == des_node, this is a metal wire */
fprintf(fp, " %.2g", tmax);
fprintf(fp, "\n");
return;
}
/* Constrain the inputs and outputs of SBs, with the Switch delays */
void verilog_generate_sdc_constrain_sbs(t_sram_orgz_info* cur_sram_orgz_info,
t_sdc_opts sdc_opts,
int LL_nx, int LL_ny,
int num_switch,
t_switch_inf* switches,
t_spice* spice) {
FILE* fp = NULL;
int ix, iy;
int side, itrack;
t_sb* cur_sb_info = NULL;
char* fname = my_strcat(sdc_opts.sdc_dir, sdc_constrain_sb_file_name);
vpr_printf(TIO_MESSAGE_INFO,
"Generating SDC for constraining Switch Blocks in P&R flow: %s ...\n",
fname);
/* Print the muxes netlist*/
fp = fopen(fname, "w");
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Failure in create SDC constraints %s",
__FILE__, __LINE__, fname);
exit(1);
}
/* Generate the descriptions*/
dump_verilog_sdc_file_header(fp, "Constrain Switch Blocks for PnR");
/* We start from a SB[x][y] */
for (ix = 0; ix < (LL_nx + 1); ix++) {
for (iy = 0; iy < (LL_ny + 1); iy++) {
cur_sb_info = &(sb_info[ix][iy]);
for (side = 0; side < cur_sb_info->num_sides; side++) {
for (itrack = 0; itrack < cur_sb_info->chan_width[side]; itrack++) {
assert((CHANX == cur_sb_info->chan_rr_node[side][itrack]->type)
||(CHANY == cur_sb_info->chan_rr_node[side][itrack]->type));
/* We only care the output port and it should indicate a SB mux */
if (OUT_PORT != cur_sb_info->chan_rr_node_direction[side][itrack]) {
continue;
}
/* Constrain thru wires */
if (FALSE != check_drive_rr_node_imply_short(*cur_sb_info, cur_sb_info->chan_rr_node[side][itrack], side)) {
/* Set the max, min delay to 0? */
verilog_generate_sdc_constrain_one_sb_path(fp, cur_sb_info,
cur_sb_info->chan_rr_node[side][itrack],
cur_sb_info->chan_rr_node[side][itrack],
0.);
continue;
}
/* This is a MUX, constrain all the paths from an input to an output */
verilog_generate_sdc_constrain_one_sb_mux(fp, cur_sb_info,
cur_sb_info->chan_rr_node[side][itrack]);
}
}
}
}
/* Close the file*/
fclose(fp);
/* Free strings */
my_free(fname);
return;
}
void verilog_generate_sdc_constrain_one_cb(FILE* fp,
t_cb* cur_cb_info) {
int side, side_cnt;
int inode, iedge, switch_id;
float switch_delay = 0.;
/* Check the file handler */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler for SDC generation",
__FILE__, __LINE__);
exit(1);
}
side_cnt = 0;
/* Print the ports of grids*/
/* only check ipin_rr_nodes of cur_cb_info */
for (side = 0; side < cur_cb_info->num_sides; side++) {
/* Bypass side with zero IPINs*/
if (0 == cur_cb_info->num_ipin_rr_nodes[side]) {
continue;
}
side_cnt++;
assert(0 < cur_cb_info->num_ipin_rr_nodes[side]);
assert(NULL != cur_cb_info->ipin_rr_node[side]);
for (inode = 0; inode < cur_cb_info->num_ipin_rr_nodes[side]; inode++) {
for (iedge = 0; iedge < cur_cb_info->ipin_rr_node[side][inode]->num_drive_rr_nodes; iedge++) {
/* Get the switch delay */
switch_id = cur_cb_info->ipin_rr_node[side][inode]->drive_switches[iedge];
switch_delay = get_switch_sdc_tmax (&(switch_inf[switch_id]));
/* Print each INPUT Pins of a grid */
verilog_generate_sdc_constrain_one_cb_path(fp, cur_cb_info,
cur_cb_info->ipin_rr_node[side][inode]->drive_rr_nodes[iedge],
cur_cb_info->ipin_rr_node[side][inode],
cur_cb_info->ipin_rr_node_grid_side[side][inode],
switch_delay);
}
}
}
/* Make sure only 2 sides of IPINs are printed */
assert((1 == side_cnt)||(2 == side_cnt));
return;
}
/* Constrain the inputs and outputs of Connection Blocks, with the Switch delays */
void verilog_generate_sdc_constrain_cbs(t_sram_orgz_info* cur_sram_orgz_info,
t_sdc_opts sdc_opts,
int LL_nx, int LL_ny,
int num_switch,
t_switch_inf* switches,
t_spice* spice) {
FILE* fp = NULL;
int ix, iy;
char* fname = my_strcat(sdc_opts.sdc_dir, sdc_constrain_cb_file_name);
vpr_printf(TIO_MESSAGE_INFO,
"Generating SDC for constraining Connection Blocks in P&R flow: %s ...\n",
fname);
/* Print the muxes netlist*/
fp = fopen(fname, "w");
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Failure in create SDC constraints %s",
__FILE__, __LINE__, fname);
exit(1);
}
/* Generate the descriptions*/
dump_verilog_sdc_file_header(fp, "Constrain Connection Blocks for PnR");
/* Connection Boxes */
/* X - channels [1...nx][0..ny]*/
for (iy = 0; iy < (LL_ny + 1); iy++) {
for (ix = 1; ix < (LL_nx + 1); ix++) {
if ((TRUE == is_cb_exist(CHANX, ix, iy))
&&(0 < count_cb_info_num_ipin_rr_nodes(cbx_info[ix][iy]))) {
verilog_generate_sdc_constrain_one_cb(fp, &(cbx_info[ix][iy]));
}
}
}
/* Y - channels [1...ny][0..nx]*/
for (ix = 0; ix < (LL_nx + 1); ix++) {
for (iy = 1; iy < (LL_ny + 1); iy++) {
if ((TRUE == is_cb_exist(CHANY, ix, iy))
&&(0 < count_cb_info_num_ipin_rr_nodes(cby_info[ix][iy]))) {
verilog_generate_sdc_constrain_one_cb(fp, &(cby_info[ix][iy]));
}
}
}
/* Close the file*/
fclose(fp);
/* Free strings */
my_free(fname);
return;
}
void verilog_generate_sdc_constrain_one_chan(FILE* fp,
t_rr_type chan_type,
int x, int y,
t_arch arch,
int LL_num_rr_nodes, t_rr_node* LL_rr_node,
t_ivec*** LL_rr_node_indices,
t_rr_indexed_data* LL_rr_indexed_data) {
int chan_width = 0;
t_rr_node** chan_rr_nodes = NULL;
int cost_index, iseg, itrack;
/* Check the file handler */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler for SDC generation",
__FILE__, __LINE__);
exit(1);
}
/* Collect rr_nodes for Tracks for chanx[ix][iy] */
chan_rr_nodes = get_chan_rr_nodes(&chan_width, chan_type, x, y,
LL_num_rr_nodes, LL_rr_node, LL_rr_node_indices);
for (itrack = 0; itrack < chan_width; itrack++) {
fprintf(fp, "set_max_delay");
fprintf(fp, " -from ");
fprintf(fp, "%s/in%d",
gen_verilog_one_routing_channel_instance_name(chan_type, x, y),
itrack);
fprintf(fp, " -to ");
fprintf(fp, "%s/out%d",
gen_verilog_one_routing_channel_instance_name(chan_type, x, y),
itrack);
/* Find the segment delay ! */
cost_index = chan_rr_nodes[itrack]->cost_index;
iseg = LL_rr_indexed_data[cost_index].seg_index;
/* Check */
assert((!(iseg < 0))&&(iseg < arch.num_segments));
fprintf(fp, " %.2g", get_routing_seg_sdc_tmax(&(arch.Segments[iseg])));
fprintf(fp, "\n");
fprintf(fp, "set_max_delay");
fprintf(fp, " -from ");
fprintf(fp, "%s/in%d",
gen_verilog_one_routing_channel_instance_name(chan_type, x, y),
itrack);
fprintf(fp, " -to ");
fprintf(fp, "%s/mid_out%d",
gen_verilog_one_routing_channel_instance_name(chan_type, x, y),
itrack);
/* Check */
fprintf(fp, " %.2g", get_routing_seg_sdc_tmax(&(arch.Segments[iseg])));
fprintf(fp, "\n");
}
/* Free */
my_free(chan_rr_nodes);
return;
}
void verilog_generate_sdc_disable_one_unused_chan(FILE* fp,
t_rr_type chan_type,
int x, int y,
int LL_num_rr_nodes, t_rr_node* LL_rr_node,
t_ivec*** LL_rr_node_indices,
t_rr_indexed_data* LL_rr_indexed_data) {
int chan_width = 0;
t_rr_node** chan_rr_nodes = NULL;
int itrack;
/* Check the file handler */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler for SDC generation",
__FILE__, __LINE__);
exit(1);
}
/* Print comments */
fprintf(fp,
"##################################################\n");
fprintf(fp,
"### Disable Timing for an %s[%d][%d] ###\n",
convert_chan_type_to_string(chan_type),
x, y);
fprintf(fp,
"##################################################\n");
/* Collect rr_nodes for Tracks for chanx[ix][iy] */
chan_rr_nodes = get_chan_rr_nodes(&chan_width, chan_type, x, y,
LL_num_rr_nodes, LL_rr_node, LL_rr_node_indices);
for (itrack = 0; itrack < chan_width; itrack++) {
/* We disable the timing of the input and output of a routing track,
* when it is not mapped to a net or it is a parasitic net
*/
if (FALSE == is_rr_node_to_be_disable_for_analysis(chan_rr_nodes[itrack])) {
continue;
}
fprintf(fp, "set_disable_timing ");
fprintf(fp, "%s/in%d",
gen_verilog_one_routing_channel_instance_name(chan_type, x, y),
itrack);
fprintf(fp, "\n");
fprintf(fp, "set_disable_timing ");
fprintf(fp, "%s/out%d",
gen_verilog_one_routing_channel_instance_name(chan_type, x, y),
itrack);
fprintf(fp, "\n");
fprintf(fp, "set_disable_timing ");
fprintf(fp, "%s/mid_out%d",
gen_verilog_one_routing_channel_instance_name(chan_type, x, y),
itrack);
fprintf(fp, "\n");
}
/* Free */
my_free(chan_rr_nodes);
return;
}
/* Constrain the inputs and outputs of Connection Blocks, with the Switch delays */
void verilog_generate_sdc_constrain_routing_channels(t_sram_orgz_info* cur_sram_orgz_info,
t_sdc_opts sdc_opts,
t_arch arch,
int LL_nx, int LL_ny,
int LL_num_rr_nodes, t_rr_node* LL_rr_node,
t_ivec*** LL_rr_node_indices,
t_rr_indexed_data* LL_rr_indexed_data,
t_spice* spice) {
FILE* fp = NULL;
int ix, iy;
char* fname = my_strcat(sdc_opts.sdc_dir, sdc_constrain_routing_chan_file_name);
vpr_printf(TIO_MESSAGE_INFO,
"Generating SDC for constraining Routing Channels in P&R flow: %s ...\n",
fname);
/* Print the muxes netlist*/
fp = fopen(fname, "w");
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Failure in create SDC constraints %s",
__FILE__, __LINE__, fname);
exit(1);
}
/* Generate the descriptions*/
dump_verilog_sdc_file_header(fp, "Constrain Routing Channels for PnR");
/* Routing channels */
/* X - channels [1...nx][0..ny]*/
for (iy = 0; iy < (LL_ny + 1); iy++) {
for (ix = 1; ix < (LL_nx + 1); ix++) {
verilog_generate_sdc_constrain_one_chan(fp, CHANX, ix, iy, arch,
LL_num_rr_nodes, LL_rr_node,
LL_rr_node_indices, LL_rr_indexed_data);
}
}
/* Y - channels [1...ny][0..nx]*/
for (ix = 0; ix < (LL_nx + 1); ix++) {
for (iy = 1; iy < (LL_ny + 1); iy++) {
verilog_generate_sdc_constrain_one_chan(fp, CHANY, ix, iy, arch,
LL_num_rr_nodes, LL_rr_node,
LL_rr_node_indices, LL_rr_indexed_data);
}
}
/* Close the file*/
fclose(fp);
/* Free strings */
my_free(fname);
return;
}
/* Disable the timing for all the global port
* Except the clock ports
*/
void verilog_generate_sdc_disable_global_ports(FILE* fp) {
t_llist* temp = global_ports_head;
t_spice_model_port* cur_port = NULL;
/* Check the file handler */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler for SDC generation",
__FILE__, __LINE__);
exit(1);
}
/* Print comments */
fprintf(fp,
"##################################################\n");
fprintf(fp,
"### Disable Timing for global ports ###\n");
fprintf(fp,
"##################################################\n");
while (NULL != temp) {
/* Get the port */
cur_port = (t_spice_model_port*)(temp->dptr);
/* Only focus on the non-clock ports */
if ( (SPICE_MODEL_PORT_CLOCK == cur_port->type)
&& (FALSE == cur_port->is_prog) ) {
/* Go to the next */
temp = temp->next;
continue;
}
/* Output disable timing command */
fprintf(fp,
"set_disable_timing %s\n",
cur_port->prefix);
/* Go to the next */
temp = temp->next;
}
return;
}
/* Disable the timing for SRAM outputs */
void verilog_generate_sdc_disable_sram_orgz(FILE* fp,
t_sram_orgz_info* cur_sram_orgz_info) {
/* Check the file handler */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler for SDC generation",
__FILE__, __LINE__);
exit(1);
}
/* Print comments */
fprintf(fp,
"##################################################\n");
fprintf(fp,
"### Disable Timing for configuration memories ###\n");
fprintf(fp,
"##################################################\n");
verilog_generate_sdc_break_loop_sram(fp, cur_sram_orgz_info);
return;
}
void verilog_generate_sdc_disable_unused_sbs_muxs(FILE* fp, int LL_nx, int LL_ny) {
int ix, iy, side, itrack, imux;
t_rr_node* cur_rr_node;
t_sb* cur_sb_info;
for (ix = 0; ix < (LL_nx + 1); ix++) {
for (iy = 0; iy < (LL_ny + 1); iy++) {
cur_sb_info = &(sb_info[ix][iy]);
/* Print comments */
fprintf(fp,
"########################################################\n");
fprintf(fp,
"### Disable Timing for MUXES in Switch block[%d][%d] ###\n",
ix, iy);
fprintf(fp,
"########################################################\n");
for (side = 0; side < cur_sb_info->num_sides; side++) {
for (itrack = 0; itrack < cur_sb_info->chan_width[side]; itrack++) {
if (OUT_PORT == cur_sb_info->chan_rr_node_direction[side][itrack]) {
cur_rr_node = cur_sb_info->chan_rr_node[side][itrack];
for (imux = 0 ; imux < cur_rr_node-> fan_in; imux++) {
if (imux == cur_rr_node->id_path) {
fprintf(fp, "#"); // comments out if the node is active
}
//if(cur_rr_node->name_mux == NULL) assert (NULL != cur_rr_node->name_mux);
fprintf(fp, "set_disable_timing %s[%d]\n",
cur_rr_node->name_mux, imux);
}
}
}
}
}
}
return;
}
void verilog_generate_sdc_disable_unused_cbs_muxs(FILE* fp) {
int ix, iy, iside, inode, imux;
t_cb* cur_cb_info;
t_rr_node* cur_rr_node;
for (iy = 0; iy < (ny + 1); iy++) {
for (ix = 1; ix < (nx + 1); ix++) {
if (0 < count_cb_info_num_ipin_rr_nodes(cbx_info[ix][iy])) {
cur_cb_info = &(cbx_info[ix][iy]);
/* Print comments */
fprintf(fp,
"##############################################################\n");
fprintf(fp,
"### Disable Timing for MUXES in Connection block X[%d][%d] ###\n",
ix, iy);
fprintf(fp,
"##############################################################\n");
for (iside = 0; iside < cur_cb_info->num_sides; iside++) {
for (inode = 0; inode < cur_cb_info->num_ipin_rr_nodes[iside]; inode++) {
cur_rr_node = cur_cb_info->ipin_rr_node[iside][inode];
for (imux = 0 ; imux < cur_rr_node-> fan_in; imux++) {
if (imux == cur_rr_node->id_path) {
fprintf(fp, "#"); // comments out if the node is active
}
fprintf(fp, "set_disable_timing %s[%d]\n",
cur_rr_node->name_mux, imux);
}
}
}
}
}
}
for (iy = 1; iy < (ny + 1); iy++) {
for (ix = 0; ix < (nx + 1); ix++) {
if (0 < count_cb_info_num_ipin_rr_nodes(cby_info[ix][iy])) {
cur_cb_info = &(cby_info[ix][iy]);
/* Print comments */
fprintf(fp,
"##############################################################\n");
fprintf(fp,
"### Disable Timing for MUXES in Connection block Y[%d][%d] ###\n",
ix, iy);
fprintf(fp,
"##############################################################\n");
for (iside = 0; iside < cur_cb_info->num_sides; iside++) {
for (inode = 0; inode < cur_cb_info->num_ipin_rr_nodes[iside]; inode++) {
cur_rr_node = cur_cb_info->ipin_rr_node[iside][inode];
for (imux = 0 ; imux < cur_rr_node-> fan_in; imux++) {
if (imux == cur_rr_node->id_path) {
fprintf(fp, "#"); // comments out if the node is active
}
fprintf(fp, "set_disable_timing %s[%d]\n",
cur_rr_node->name_mux, imux);
}
}
}
}
}
}
return;
}
void verilog_generate_sdc_disable_unused_sbs(FILE* fp,
int LL_nx, int LL_ny,
int num_switch,
t_switch_inf* switches,
t_spice* spice) {
int ix, iy;
int side, itrack, inode;
t_sb* cur_sb_info = NULL;
/* Check the file handler */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler for SDC generation",
__FILE__, __LINE__);
exit(1);
}
/* We start from a SB[x][y] */
for (ix = 0; ix < (LL_nx + 1); ix++) {
for (iy = 0; iy < (LL_ny + 1); iy++) {
cur_sb_info = &(sb_info[ix][iy]);
/* Print comments */
fprintf(fp,
"##################################################\n");
fprintf(fp,
"### Disable Timing for an Switch block[%d][%d] ###\n",
ix, iy);
fprintf(fp,
"##################################################\n");
for (side = 0; side < cur_sb_info->num_sides; side++) {
/* Disable Channel inputs and outputs*/
for (itrack = 0; itrack < cur_sb_info->chan_width[side]; itrack++) {
assert((CHANX == cur_sb_info->chan_rr_node[side][itrack]->type)
||(CHANY == cur_sb_info->chan_rr_node[side][itrack]->type));
if (FALSE == is_rr_node_to_be_disable_for_analysis(cur_sb_info->chan_rr_node[side][itrack])) {
continue;
}
fprintf(fp, "set_disable_timing ");
fprintf(fp, "%s/",
gen_verilog_one_sb_instance_name(cur_sb_info));
dump_verilog_one_sb_chan_pin(fp, cur_sb_info,
cur_sb_info->chan_rr_node[side][itrack],
cur_sb_info->chan_rr_node_direction[side][itrack]);
fprintf(fp, "\n");
}
/* Disable OPINs*/
for (inode = 0; inode < cur_sb_info->num_opin_rr_nodes[side]; inode++) {
assert (OPIN == cur_sb_info->opin_rr_node[side][inode]->type);
if (FALSE == is_rr_node_to_be_disable_for_analysis(cur_sb_info->opin_rr_node[side][inode])) {
continue;
}
fprintf(fp, "set_disable_timing ");
fprintf(fp, "%s/",
gen_verilog_one_sb_instance_name(cur_sb_info));
dump_verilog_one_sb_routing_pin(fp, cur_sb_info,
cur_sb_info->opin_rr_node[side][inode]);
fprintf(fp, "\n");
}
}
}
}
return;
}
void verilog_generate_sdc_disable_one_unused_cb(FILE* fp,
t_cb* cur_cb_info) {
int side, side_cnt;
int inode;
/* Check the file handler */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler for SDC generation",
__FILE__, __LINE__);
exit(1);
}
/* Print comments */
fprintf(fp,
"##################################################\n");
fprintf(fp,
"### Disable Timing for an %s[%d][%d] ###\n",
convert_cb_type_to_string(cur_cb_info->type),
cur_cb_info->x, cur_cb_info->y);
fprintf(fp,
"##################################################\n");
side_cnt = 0;
/* Print the ports of grids*/
/* only check ipin_rr_nodes of cur_cb_info */
for (side = 0; side < cur_cb_info->num_sides; side++) {
/* Bypass side with zero IPINs*/
if (0 == cur_cb_info->num_ipin_rr_nodes[side]) {
continue;
}
side_cnt++;
assert(0 < cur_cb_info->num_ipin_rr_nodes[side]);
assert(NULL != cur_cb_info->ipin_rr_node[side]);
for (inode = 0; inode < cur_cb_info->num_ipin_rr_nodes[side]; inode++) {
if (FALSE == is_rr_node_to_be_disable_for_analysis(cur_cb_info->ipin_rr_node[side][inode])) {
continue;
}
fprintf(fp, "set_disable_timing ");
fprintf(fp, "%s/",
gen_verilog_one_cb_instance_name(cur_cb_info));
dump_verilog_grid_side_pin_with_given_index(fp, IPIN,
cur_cb_info->ipin_rr_node[side][inode]->ptc_num,
cur_cb_info->ipin_rr_node_grid_side[side][inode],
cur_cb_info->ipin_rr_node[side][inode]->xlow,
cur_cb_info->ipin_rr_node[side][inode]->ylow,
FALSE); /* Do not specify direction of port */
fprintf(fp, "\n");
}
}
return;
}
void verilog_generate_sdc_disable_unused_cbs(FILE* fp,
int LL_nx, int LL_ny,
int num_switch,
t_switch_inf* switches,
t_spice* spice) {
int ix, iy;
/* Check the file handler */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler for SDC generation",
__FILE__, __LINE__);
exit(1);
}
/* Connection Boxes */
/* X - channels [1...nx][0..ny]*/
for (iy = 0; iy < (LL_ny + 1); iy++) {
for (ix = 1; ix < (LL_nx + 1); ix++) {
if ((TRUE == is_cb_exist(CHANX, ix, iy))
&&(0 < count_cb_info_num_ipin_rr_nodes(cbx_info[ix][iy]))) {
verilog_generate_sdc_disable_one_unused_cb(fp, &(cbx_info[ix][iy]));
}
}
}
/* Y - channels [1...ny][0..nx]*/
for (ix = 0; ix < (LL_nx + 1); ix++) {
for (iy = 1; iy < (LL_ny + 1); iy++) {
if ((TRUE == is_cb_exist(CHANY, ix, iy))
&&(0 < count_cb_info_num_ipin_rr_nodes(cby_info[ix][iy]))) {
verilog_generate_sdc_disable_one_unused_cb(fp, &(cby_info[ix][iy]));
}
}
}
return;
}
/* Constrain the inputs and outputs of Connection Blocks, with the Switch delays */
void verilog_generate_sdc_disable_unused_routing_channels(FILE* fp,
t_arch arch,
int LL_nx, int LL_ny,
int LL_num_rr_nodes, t_rr_node* LL_rr_node,
t_ivec*** LL_rr_node_indices,
t_rr_indexed_data* LL_rr_indexed_data) {
int ix, iy;
/* Check the file handler */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler for SDC generation",
__FILE__, __LINE__);
exit(1);
}
/* Routing channels */
/* X - channels [1...nx][0..ny]*/
for (iy = 0; iy < (LL_ny + 1); iy++) {
for (ix = 1; ix < (LL_nx + 1); ix++) {
verilog_generate_sdc_disable_one_unused_chan(fp, CHANX, ix, iy,
LL_num_rr_nodes, LL_rr_node,
LL_rr_node_indices, LL_rr_indexed_data);
}
}
/* Y - channels [1...ny][0..nx]*/
for (ix = 0; ix < (LL_nx + 1); ix++) {
for (iy = 1; iy < (LL_ny + 1); iy++) {
verilog_generate_sdc_disable_one_unused_chan(fp, CHANY, ix, iy,
LL_num_rr_nodes, LL_rr_node,
LL_rr_node_indices, LL_rr_indexed_data);
}
}
return;
}
/* Go recursively in the hierarchy
* and disable all the pb_types
*/
void rec_verilog_generate_sdc_disable_unused_pb_types(FILE* fp,
char* prefix,
t_pb_type* cur_pb_type) {
int ipb;
int mode_index;
char* pass_on_prefix = NULL;
/* Skip print the level for the top-level pb_type,
* it has been printed outside
*/
if (NULL == cur_pb_type->parent_mode) {
pass_on_prefix = my_strdup(prefix);
} else {
/* Special prefix for primitive node*/
/* generate pass_on_prefix */
pass_on_prefix = (char*) my_malloc(sizeof(char) *
( strlen(prefix) + 1
+ strlen(cur_pb_type->name) + 1 + 1));
sprintf(pass_on_prefix, "%s/%s*",
prefix, cur_pb_type->name);
/* Disable everything in this pb_type
* Use the spice_model_name of current pb_type
*/
fprintf(fp, "set_disable_timing ");
/* Print top-level hierarchy */
fprintf(fp, "%s/*",
pass_on_prefix);
fprintf(fp, "\n");
}
/* Return if this is the primitive pb_type */
if (TRUE == is_primitive_pb_type(cur_pb_type)) {
return;
}
/* Go recursively */
mode_index = find_pb_type_physical_mode_index(*cur_pb_type);
for (ipb = 0; ipb < cur_pb_type->modes[mode_index].num_pb_type_children; ipb++) {
rec_verilog_generate_sdc_disable_unused_pb_types(fp, pass_on_prefix,
&(cur_pb_type->modes[mode_index].pb_type_children[ipb]));
}
/* Free */
my_free(pass_on_prefix);
return;
}
/* This block is totally unused.
* We just go through each pb_type and disable all the ports using wildcards
*/
void verilog_generate_sdc_disable_one_unused_grid(FILE* fp,
t_type_ptr cur_grid_type,
int block_x, int block_y,
int block_z) {
char* prefix = NULL;
/* Check the file handler */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler for SDC generation",
__FILE__, __LINE__);
exit(1);
}
/* Build prefix, which is the top-level hierarchy */
prefix = (char*) my_malloc(sizeof(char) *
( strlen(gen_verilog_one_grid_instance_name(block_x, block_y))
+ 1
+ strlen(gen_verilog_one_phy_block_instance_name(cur_grid_type, block_z))
+ 2));
sprintf(prefix, "%s/%s",
gen_verilog_one_grid_instance_name(block_x, block_y),
gen_verilog_one_phy_block_instance_name(cur_grid_type, block_z));
/* Print comments */
fprintf(fp,
"####################################################\n");
fprintf(fp,
"### Disable Timing for an unused Grid[%d][%d][%d] ###\n",
block_x, block_y, block_z);
fprintf(fp,
"#####################################################\n");
/* Disable everything under this level */
fprintf(fp, "set_disable_timing ");
fprintf(fp, "%s/*", prefix);
fprintf(fp, "\n");
/* Go recursively in the pb_type hierarchy */
rec_verilog_generate_sdc_disable_unused_pb_types(fp, prefix,
cur_grid_type->pb_type);
/* Free */
my_free(prefix);
return;
}
/* The block is used for mapping logic circuits
* But typically, only part of the logic resources are used.
* This function will search the local_rr_graph of a phy_pb of the block
* And disable the unused resources in a SDC format
*/
void verilog_generate_sdc_disable_one_unused_block(FILE* fp,
t_block* cur_block) {
int inode;
t_phy_pb* cur_phy_pb = NULL;
/* Check the file handler */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler for SDC generation",
__FILE__, __LINE__);
exit(1);
}
/* Get the phy_pb */
cur_phy_pb = (t_phy_pb*)(cur_block->phy_pb);
/* Print comments */
fprintf(fp,
"####################################################\n");
fprintf(fp,
"### Disable Timing for a mapped Grid[%d][%d][%d] ###\n",
cur_block->x, cur_block->y, cur_block->z);
fprintf(fp,
"####################################################\n");
/* Search every nodes in the local_rr_graph */
for (inode = 0; inode < cur_phy_pb->rr_graph->num_rr_nodes; inode++) {
/* Focus on the SOURCE and SINK rr_nodes */
if ((SOURCE != cur_phy_pb->rr_graph->rr_node[inode].type)
&& (SINK != cur_phy_pb->rr_graph->rr_node[inode].type)) {
continue;
}
/* Identify if the rr_node is usused */
if (FALSE == is_rr_node_to_be_disable_for_analysis(&(cur_phy_pb->rr_graph->rr_node[inode]))) {
continue;
}
/* Get the pb_graph_pin */
assert (NULL != cur_phy_pb->rr_graph->rr_node[inode].pb_graph_pin);
/* Disable the timing of this node */
fprintf(fp, "set_disable_timing ");
/* Print top-level hierarchy */
fprintf(fp, "%s/",
gen_verilog_one_grid_instance_name(cur_block->x, cur_block->y));
fprintf(fp, "%s/",
gen_verilog_one_phy_block_instance_name(cur_block->type, cur_block->z));
fprintf(fp, "%s",
gen_verilog_one_pb_graph_pin_full_name_in_hierarchy(cur_phy_pb->rr_graph->rr_node[inode].pb_graph_pin));
fprintf(fp, "\n");
}
return;
}
void verilog_generate_sdc_disable_unused_grids(FILE* fp,
int LL_nx, int LL_ny,
t_grid_tile** LL_grid,
t_block* LL_block) {
int ix, iy, iblk;
int blk_id;
t_type_ptr type = NULL;
boolean* grid_usage = NULL;
/* Check the file handler */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler for SDC generation",
__FILE__, __LINE__);
exit(1);
}
for (ix = 0; ix < (LL_nx + 2); ix++) {
for (iy = 0; iy < (LL_ny + 2); iy++) {
type = LL_grid[ix][iy].type;
/* bypass EMPTY type */
if ((NULL == type) || (EMPTY_TYPE == type)) {
continue;
}
/* Allocate usage and initialize to unused */
grid_usage = (boolean*) my_calloc(type->capacity, sizeof(boolean));
for (iblk = 0; iblk < type->capacity; iblk++) {
grid_usage[iblk] = FALSE;
}
/* Print comments */
fprintf(fp,
"#######################################\n");
fprintf(fp,
"### Disable Timing for Grid[%d][%d] ###\n",
ix, iy);
fprintf(fp,
"#######################################\n");
/* For used grid, find the unused rr_node in the local rr_graph and disable the pb_graph_pin */
for (iblk = 0; iblk < LL_grid[ix][iy].usage; iblk++) {
blk_id = LL_grid[ix][iy].blocks[iblk];
assert( (OPEN < LL_block[blk_id].z) && (LL_block[blk_id].z < type->capacity) );
/* Label the grid_usage */
grid_usage[LL_block[blk_id].z] = TRUE;
verilog_generate_sdc_disable_one_unused_block(fp,
&(LL_block[blk_id]));
}
/* For unused grid, disable all the pins in the physical_pb_type */
for (iblk = 0; iblk < type->capacity; iblk++) {
/* Bypass used blocks */
if (TRUE == grid_usage[iblk]) {
continue;
}
verilog_generate_sdc_disable_one_unused_grid(fp,
LL_grid[ix][iy].type,
ix, iy,
iblk);
continue;
}
}
}
/* Free */
my_free(grid_usage);
return;
}
void verilog_generate_sdc_disable_unused_grids_muxs(FILE* fp,
int LL_nx, int LL_ny,
t_grid_tile** LL_grid,
t_block* LL_block) {
int ix, iy, iblk, itype, i_num_rr_nodes, i_fan_in;
int blk_id;
t_type_ptr type;
t_phy_pb* cur_phy_pb;
t_rr_graph* cur_rr_graph;
t_rr_node* cur_rr_node;
char* grid_instance_name=NULL;
char* grid_sub_instance_name=NULL;
char* grid_prefix=NULL;
for (ix = 1; ix < (LL_nx + 1); ix++) {
for (iy = 1; iy < (LL_ny + 1); iy++) {
type = LL_grid[ix][iy].type;
/* Print comments */
fprintf(fp,
"###########################################\n");
fprintf(fp,
"### Disable Timing for Grid[%d][%d] MUXES ###\n",
ix, iy);
fprintf(fp,
"###########################################\n");
grid_instance_name = (char *) my_malloc(sizeof(char) * strlen(gen_verilog_one_grid_instance_name(ix, iy)) + 1);
grid_instance_name = gen_verilog_one_grid_instance_name(ix, iy);
for (iblk = 0; iblk < LL_grid[ix][iy].usage; iblk++) {
blk_id = LL_grid[ix][iy].blocks[iblk];
grid_sub_instance_name = gen_verilog_one_phy_block_instance_name(type, LL_block[blk_id].z);
grid_prefix = (char *) my_malloc(sizeof(char) * (strlen(grid_instance_name) + 1 + strlen(grid_sub_instance_name) + 1));
sprintf (grid_prefix, "%s/%s", grid_instance_name, grid_sub_instance_name);
cur_phy_pb = (t_phy_pb*) LL_block[blk_id].phy_pb;
if (NULL != cur_phy_pb) {
cur_rr_graph = cur_phy_pb->rr_graph;
for (itype = 0; itype < num_types; itype++){
if (FILL_TYPE == &type_descriptors[itype]){
dump_sdc_one_clb_muxes(fp, grid_prefix, cur_rr_graph, type_descriptors[itype].pb_graph_head);
}
}
}
my_free(grid_sub_instance_name);
my_free(grid_prefix);
}
my_free(grid_instance_name);
}
}
return;
}
/* Head function of the recursive generation of the sdc constraints
* on the muxes inside of the CLBs */
void dump_sdc_one_clb_muxes(FILE* fp,
char* grid_instance_name,
t_rr_graph* rr_graph,
t_pb_graph_node* pb_graph_head) {
/* Give head of the pb_graph to the recursive function*/
dump_sdc_rec_one_pb_muxes(fp, grid_instance_name, rr_graph, pb_graph_head);
return;
}
/* Recursive function going to the leaf nodes of the graph and dumping
* the sdc constraints for the current node. We use the id present inside
* of the rr_graph to comment the active path and the fan_in and name
* inside of the pb_graph to dump the name of the port we need to disable*/
void dump_sdc_rec_one_pb_muxes(FILE* fp,
char* grid_instance_name,
t_rr_graph* rr_graph,
t_pb_graph_node* cur_pb_graph_node) {
int mode_index;
int ipb, jpb, child_mode_index;
t_pb_type* cur_pb_type = NULL;
cur_pb_type = cur_pb_graph_node->pb_type;
if (TRUE == is_primitive_pb_type(cur_pb_type )) {
return;
}
mode_index = find_pb_type_physical_mode_index(*cur_pb_type);
for(ipb = 0; ipb < cur_pb_type->modes[mode_index].num_pb_type_children; ipb++) {
for(jpb = 0; jpb < cur_pb_type->modes[mode_index].pb_type_children[ipb].num_pb; jpb++) {
dump_sdc_rec_one_pb_muxes(fp, grid_instance_name, rr_graph,
&(cur_pb_graph_node->child_pb_graph_nodes[mode_index][ipb][jpb]));
}
}
dump_sdc_pb_graph_node_muxes(fp, grid_instance_name, rr_graph,
cur_pb_graph_node);
return;
}
void dump_sdc_pb_graph_node_muxes (FILE* fp,
char* grid_instance_name,
t_rr_graph* rr_graph,
t_pb_graph_node* pb_graph_node) {
int i_pin, i_port;
// Input pins
for (i_port = 0; i_port< pb_graph_node->num_input_ports; i_port++) {
for (i_pin = 0; i_pin < pb_graph_node->num_input_pins[i_port]; i_pin++) {
dump_sdc_pb_graph_pin_muxes (fp, grid_instance_name, rr_graph, pb_graph_node->input_pins[i_port][i_pin]);
}
}
// Output pins
for (i_port = 0; i_port< pb_graph_node->num_output_ports; i_port++) {
for (i_pin = 0; i_pin < pb_graph_node->num_output_pins[i_port]; i_pin++) {
dump_sdc_pb_graph_pin_muxes (fp, grid_instance_name, rr_graph, pb_graph_node->output_pins[i_port][i_pin]);
}
}
// Clock pins
for (i_port = 0; i_port< pb_graph_node->num_clock_ports; i_port++) {
for (i_pin = 0; i_pin < pb_graph_node->num_clock_pins[i_port]; i_pin++) {
dump_sdc_pb_graph_pin_muxes (fp, grid_instance_name, rr_graph, pb_graph_node->clock_pins[i_port][i_pin]);
}
}
return;
}
void dump_sdc_pb_graph_pin_muxes (FILE* fp,
char* grid_instance_name,
t_rr_graph* rr_graph,
t_pb_graph_pin pb_graph_pin) {
int i_fan_in, datapath_id, fan_in;
int level_changing = 0;
int mode_index;
int num_mux_input;
t_spice_model* mux_spice_model;
t_rr_node cur_node = rr_graph->rr_node[pb_graph_pin.rr_node_index_physical_pb];
t_pb_type* cur_pb_type = pb_graph_pin.parent_node->pb_type;
int cur_mode_index = find_pb_type_physical_mode_index(*cur_pb_type);
t_mode* cur_mode = cur_pb_type->modes;
t_interconnect* cur_interc;
/* There are three types of interconnection: same level, going down a level, going up a level
* Since we check the fan_in, we need to get the right input edge mode */
if (0 == pb_graph_pin.num_input_edges || 0 == pb_graph_pin.num_output_edges) {
return;
}
if (pb_graph_pin.input_edges[cur_mode_index]->interconnect->parent_mode != pb_graph_pin.output_edges[cur_mode_index]->interconnect->parent_mode) {
if (pb_graph_pin.input_edges[cur_mode_index]->interconnect->parent_mode != cur_mode) {
cur_mode = pb_graph_pin.input_edges[cur_mode_index]->interconnect->parent_mode;
level_changing = 1;
}
}
find_interc_fan_in_des_pb_graph_pin(&pb_graph_pin, cur_mode, &cur_interc, &fan_in);
if (0 == fan_in || 1 == fan_in) {
return; /* Returns if there is no mux */
}
/* Handle DEFAULT PATH ID */
datapath_id = cur_node.id_path;
if (DEFAULT_PATH_ID == datapath_id) {
mux_spice_model = cur_interc->spice_model;
datapath_id = get_mux_default_path_id(mux_spice_model, fan_in, datapath_id);
/* Either the default is the last pin or the num 0. If fan_in was 0, we wouldn't be here
* so if the next condition works, datapath_id is actually 1 too far */
if (fan_in == datapath_id) {
datapath_id --;
}
} else {
assert((DEFAULT_PATH_ID < datapath_id)&&(datapath_id < fan_in));
}
for (i_fan_in=0 ; i_fan_in < fan_in ; i_fan_in++) {
if (i_fan_in == datapath_id) {
fprintf(fp, "#");
}
if (0 == level_changing) {
fprintf(fp, "set_disable_timing ");
fprintf(fp, "%s/%s%s/in[%d]\n", grid_instance_name,
gen_verilog_one_pb_graph_pin_full_name_in_hierarchy_parent_node(cur_node.pb_graph_pin),
pb_graph_pin.name_mux, i_fan_in);
}
if (1 == level_changing) {
fprintf(fp, "set_disable_timing ");
fprintf(fp, "%s/%s%s/in[%d]\n", grid_instance_name,
gen_verilog_one_pb_graph_pin_full_name_in_hierarchy_grand_parent_node(cur_node.pb_graph_pin),
pb_graph_pin.name_mux, i_fan_in);
}
// Hierarchical dumping. Might be broken if extending the software hence going through a more direct method.
//fprintf(fp, "set_disable_timing [get_pins -filter \"hierarchical_name =");
//fprintf(fp, "~ *%s/in[%d]\" -of_objects [get_cells -hier -filter ",
// pb_graph_pin->name_mux, i_fan_in);
//printf("%s", pb_graph_pin->name_mux);
//fprintf(fp, "\"hierarchical_name =~ %s*\"]]",
// grid_instance_name);
// Might need to comment here the name of the verilog pin connected to ease the debugging
//fprintf(fp, "\n");
}
return;
}
/* Generate SDC constaints for inputs and outputs
* We consider the top module in formal verification purpose here
* which is easier
*/
void verilog_generate_sdc_input_output_delays(FILE* fp,
char* circuit_name,
float critical_path_delay) {
int iopad_idx, iblock, iport;
int found_mapped_inpad;
char* port_name = NULL;
int num_clock_ports = 0;
t_spice_model_port** clock_port = NULL;
/* Check the file handler */
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Invalid file handler for SDC generation",
__FILE__, __LINE__);
exit(1);
}
/* Get clock port from the global port */
get_fpga_x2p_global_op_clock_ports(global_ports_head, &num_clock_ports, &clock_port);
/* Print comments */
fprintf(fp,
"##################################################\n");
fprintf(fp,
"### Create clock #\n");
fprintf(fp,
"##################################################\n");
/* Create a clock */
for (iport = 0; iport < num_clock_ports; iport++) {
fprintf(fp, "create_clock ");
fprintf(fp, "%s -period %.4g -waveform {0 %.4g}\n",
clock_port[iport]->prefix,
critical_path_delay, critical_path_delay/2);
}
/* Print comments */
fprintf(fp,
"##################################################\n");
fprintf(fp,
"### Create input and output delays #\n");
fprintf(fp,
"##################################################\n");
fprintf(fp, "set input_pins \"\"\n");
fprintf(fp, "set output_pins \"\"\n");
assert(NULL != iopad_verilog_model);
for (iopad_idx = 0; iopad_idx < iopad_verilog_model->cnt; iopad_idx++) {
/* Find if this inpad is mapped to a logical block */
found_mapped_inpad = 0;
/* Malloc and assign port_name */
port_name = gen_verilog_top_module_io_port_prefix(gio_inout_prefix, iopad_verilog_model->prefix);
/* Find the linked logical block */
for (iblock = 0; iblock < num_logical_blocks; iblock++) {
/* Bypass OUTPAD: donot put any voltage stimuli */
/* Make sure We find the correct logical block !*/
if ((iopad_verilog_model == logical_block[iblock].mapped_spice_model)
&&(iopad_idx == logical_block[iblock].mapped_spice_model_index)) {
/* Output PAD only need a short connection */
if (VPACK_OUTPAD == logical_block[iblock].type) {
fprintf(fp, "set_output_delay ");
fprintf(fp, "-clock ");
/*for (iport = 0; iport < num_clock_ports; iport++) {
fprintf(fp, "%s ",
clock_port[iport]->prefix);
}*/
fprintf(fp, "[get_clocks] ");
fprintf(fp, "-max %.4g ",
critical_path_delay);
dump_verilog_generic_port_no_repeat(fp, VERILOG_PORT_CONKT,
port_name,
iopad_idx, iopad_idx);
fprintf(fp, "\n");
found_mapped_inpad = 1;
fprintf(fp, "append output_pins \"%s[%d] \"\n",port_name ,iopad_idx);
break;
}
/* Input PAD may drive a clock net or a constant generator */
assert(VPACK_INPAD == logical_block[iblock].type);
/* clock net or constant generator should be disabled in timing analysis */
if (TRUE == logical_block[iblock].is_clock) {
break;
}
fprintf(fp, "set_input_delay ");
fprintf(fp, "-clock ");
/*for (iport = 0; iport < num_clock_ports; iport++) {
fprintf(fp, "%s ",
clock_port[iport]->prefix);
}*/
fprintf(fp, "[get_clocks] ");
fprintf(fp, "-max 0 ");
dump_verilog_generic_port_no_repeat(fp, VERILOG_PORT_CONKT,
port_name,
iopad_idx, iopad_idx);
fprintf(fp, "\n");
found_mapped_inpad = 1;
fprintf(fp, "append input_pins \"%s[%d] \"\n",port_name ,iopad_idx);
}
}
assert((0 == found_mapped_inpad)||(1 == found_mapped_inpad));
/* If we find one iopad already, we finished in this round here */
if (1 == found_mapped_inpad) {
/* Free */
my_free(port_name);
continue;
}
/* if we cannot find any mapped inpad from tech.-mapped netlist, set the disable timing! */
fprintf(fp, "set_disable_timing ");
dump_verilog_generic_port_no_repeat(fp, VERILOG_PORT_CONKT,
port_name,
iopad_idx, iopad_idx);
fprintf(fp, "\n");
/* Free */
my_free(port_name);
}
/* Free */
my_free(clock_port);
return;
}
void verilog_generate_sdc_pnr(t_sram_orgz_info* cur_sram_orgz_info,
char* sdc_dir,
t_arch arch,
t_det_routing_arch* routing_arch,
int LL_num_rr_nodes, t_rr_node* LL_rr_node,
t_ivec*** LL_rr_node_indices,
t_rr_indexed_data* LL_rr_indexed_data,
int LL_nx, int LL_ny, t_grid_tile** LL_grid,
t_syn_verilog_opts fpga_verilog_opts) {
t_sdc_opts sdc_opts;
/* Initialize */
sdc_opts.sdc_dir = my_strdup(sdc_dir);
sdc_opts.constrain_pbs = TRUE;
sdc_opts.constrain_routing_channels = TRUE;
sdc_opts.constrain_sbs = TRUE;
sdc_opts.constrain_cbs = TRUE;
sdc_opts.break_loops = TRUE;
sdc_opts.break_loops_mux = FALSE; /* By default, we turn it off to avoid a overkill */
/* Part 1. Constrain clock cycles */
verilog_generate_sdc_clock_period(sdc_opts, pow(10,9)*arch.spice->spice_params.stimulate_params.vpr_crit_path_delay);
/* Part 2. Output Design Constraints for breaking loops */
if (TRUE == sdc_opts.break_loops) {
verilog_generate_sdc_break_loops(cur_sram_orgz_info, sdc_opts,
LL_nx, LL_ny,
routing_arch->num_switch, switch_inf,
arch.spice,
routing_arch);
}
/* Part 3. Output routing constraints for Switch Blocks */
if (TRUE == sdc_opts.constrain_sbs) {
verilog_generate_sdc_constrain_sbs(cur_sram_orgz_info, sdc_opts,
LL_nx, LL_ny,
routing_arch->num_switch, switch_inf,
arch.spice);
}
/* Part 4. Output routing constraints for Connection Blocks */
if (TRUE == sdc_opts.constrain_cbs) {
verilog_generate_sdc_constrain_cbs(cur_sram_orgz_info, sdc_opts,
LL_nx, LL_ny,
routing_arch->num_switch, switch_inf,
arch.spice);
}
/* Part 5. Output routing constraints for Connection Blocks */
if (TRUE == sdc_opts.constrain_routing_channels) {
verilog_generate_sdc_constrain_routing_channels(cur_sram_orgz_info, sdc_opts, arch,
LL_nx, LL_ny,
LL_num_rr_nodes, LL_rr_node,
LL_rr_node_indices, LL_rr_indexed_data,
arch.spice);
}
/* Part 6. Output routing constraints for Programmable blocks */
if (TRUE == sdc_opts.constrain_pbs) {
verilog_generate_sdc_constrain_pb_types(cur_sram_orgz_info,
sdc_dir);
}
return;
}
/* Output a SDC file to constrain a FPGA mapped with a benchmark */
void verilog_generate_sdc_analysis(t_sram_orgz_info* cur_sram_orgz_info,
char* sdc_dir,
char* circuit_name,
t_arch arch,
t_det_routing_arch* routing_arch,
int LL_num_rr_nodes, t_rr_node* LL_rr_node,
t_ivec*** LL_rr_node_indices,
t_rr_indexed_data* LL_rr_indexed_data,
int LL_nx, int LL_ny, t_grid_tile** LL_grid,
t_block* LL_block,
t_syn_verilog_opts fpga_verilog_opts) {
FILE* fp = NULL;
char* fname = my_strcat(sdc_dir, sdc_analysis_file_name);
vpr_printf(TIO_MESSAGE_INFO,
"Generating SDC for Timing/Power analysis on the mapped FPGA: %s ...\n",
fname);
/* Create file handler */
fp = fopen(fname, "w");
if (NULL == fp) {
vpr_printf(TIO_MESSAGE_ERROR,
"(FILE:%s,LINE[%d])Failure in create SDC constraints %s",
__FILE__, __LINE__, fname);
exit(1);
}
/* Generate the descriptions*/
dump_verilog_sdc_file_header(fp, "Constrain for Timing/Power analysis on the mapped FPGA");
/* Create clock and set input/output delays */
verilog_generate_sdc_input_output_delays(fp, circuit_name,
arch.spice->spice_params.stimulate_params.vpr_crit_path_delay);
/* Disable the timing for global ports */
verilog_generate_sdc_disable_global_ports(fp);
/* Disable the timing for configuration cells */
verilog_generate_sdc_disable_sram_orgz(fp, cur_sram_orgz_info);
/* Disable timing for un-used resources */
/* Apply to Routing Channels */
verilog_generate_sdc_disable_unused_routing_channels(fp, arch, LL_nx, LL_ny,
LL_num_rr_nodes, LL_rr_node,
LL_rr_node_indices, LL_rr_indexed_data);
/* Apply to Connection blocks */
verilog_generate_sdc_disable_unused_cbs(fp, LL_nx, LL_ny,
routing_arch->num_switch, switch_inf,
arch.spice);
verilog_generate_sdc_disable_unused_cbs_muxs(fp);
/* Apply to Switch blocks */
verilog_generate_sdc_disable_unused_sbs(fp, LL_nx, LL_ny,
routing_arch->num_switch, switch_inf,
arch.spice);
verilog_generate_sdc_disable_unused_sbs_muxs(fp, LL_nx, LL_ny);
/* Apply to Grids */
verilog_generate_sdc_disable_unused_grids(fp, LL_nx, LL_ny, LL_grid, LL_block);
verilog_generate_sdc_disable_unused_grids_muxs(fp, LL_nx, LL_ny, LL_grid, LL_block);
/* Close the file*/
fclose(fp);
/* Free strings */
my_free(fname);
return;
}