1930 lines
69 KiB
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;
|
|
}
|
|
|
|
|