VPR-7.0
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
read_sdc.c
Go to the documentation of this file.
1 #include <string.h>
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <limits.h>
5 #include "assert.h"
6 #include "util.h"
7 #include "vpr_types.h"
8 #include "globals.h"
9 #include "read_sdc.h"
10 #include "read_blif.h"
11 #include "path_delay.h"
12 #include "path_delay2.h"
13 #include "ReadOptions.h"
14 #include "slre.h"
15 
16 /***************************** Summary **********************************/
17 
18 /* Author: Michael Wainberg
19 
20 Looks for an SDC (Synopsys Design Constraints) file called <circuitname>.sdc
21 (unless overridden with --sdc_file <filename.sdc> on the command-line, in which
22 case it looks for that filename), and parses the timing constraints in that file.
23 If it doesn't find a file with that name, it uses default timing constraints
24 (which differ depending on whether the circuit has 0, 1, or multiple clocks).
25 
26 The primary routine, read_sdc, populates a container structure, g_sdc.
27 One of the two key output data structures within is g_sdc->constrained_clocks, which
28 associates each clock given a timing constraint with a name, fanout and whether
29 it is a netlist or virtual (external) clock. From this point on, the only clocks
30 we care about are the ones in this array. During timing analysis and data output,
31 clocks are accessed using the indexing of this array.
32 
33 The other key data structure is the "constraint matrix" g_sdc->domain_constraint, which
34 has a timing constraint for each pair (source and sink) of clock domains. These
35 generally come from finding the smallest difference between the posedges of the
36 two clocks over the LCM clock period ("edge counting" - see calculate_constraint()).
37 
38 Alternatively, entries in g_sdc->domain_constraint can come from a special-case, "override
39 constraint" (so named because it overrides the default behaviour of edge counting).
40 Override constraints can cut paths (set_clock_groups, set_false_path commands),
41 create a multicycle (set_multicycle_path) or even override a constraint with a user-
42 specified one (set_max_delay). These entries are stored temporarily in g_sdc->cc_constraints
43 (cc = clock to clock), which is freed once the timing_constraints echo file is
44 created during process_constraints().
45 
46 Flip-flop-level override constraints also exist and are stored in g_sdc->cf_constraints,
47 g_sdc->fc_constraints and g_sdc->ff_constraints (depending on whether the source, sink or neither
48 of the two is a clock domain).Unlike g_sdc->cc_constraints, they are placed on the timing
49 graph during timing analysis instead of going into g_sdc->domain_constraint, and are not
50 freed until the end of VPR's execution.
51 
52 I/O constraints from set_input_delay and set_output_delay are stored in constrained_
53 inputs and g_sdc->constrained_outputs. These associate each I/O in the netlist given a
54 constraint with the clock (often virtual, but could be in the netlist) it was
55 constrained on, and the delay through the I/O in that constraint.
56 
57 The remaining data structures are temporary and local to this file: netlist_clocks,
58 netlist_inputs and netlist_outputs, which are used to match names of clocks and I/Os
59 in the SDC file to those in the netlist; sdc_clocks, which stores info on clock periods
60 and offsets from create_clock commands and is the raw info used in edge counting; and
61 exclusive_groups, used when parsing set_clock_groups commands into g_sdc->cc_constraints. */
62 
63 /*********************** Externally-accessible variables **************************/
64 
66 
67 /****************** Types local to this module **************************/
68 
69 typedef struct s_sdc_clock {
70  char * name;
71  float period;
72  float rising_edge;
73  float falling_edge;
74 } t_sdc_clock;
75 /* Stores the name, period and offset of each constrained clock. */
76 
77 typedef struct s_sdc_exclusive_group {
78  char ** clock_names;
81 /* Used to temporarily separate clock names into exclusive groups when parsing the
82 command set_clock_groups -exclusive. */
83 
84 /****************** Variables local to this module **************************/
85 
86 static FILE *sdc;
87 t_sdc_clock * sdc_clocks = NULL; /* List of clock periods and offsets from create_clock commands */
88 
89 int num_netlist_clocks = 0; /* number of clocks in netlist */
90 char ** netlist_clocks; /* [0..num_netlist_clocks - 1] array of names of clocks in netlist */
91 
92 int num_netlist_ios = 0; /* number of clocks in netlist */
93 char ** netlist_ios; /* [0..num_netlist_clocks - 1] array of names of ios in netlist */
94 
95 /***************** Subroutines local to this module *************************/
96 
98 static void use_default_timing_constraints(void);
100 static boolean get_sdc_tok(char * buf);
101 static boolean is_number(char * ptr);
102 static int find_constrained_clock(char * ptr);
103 static float calculate_constraint(t_sdc_clock source_domain, t_sdc_clock sink_domain);
104 static void add_override_constraint(char ** from_list, int num_from, char ** to_list, int num_to,
105  float constraint, int num_multicycles, boolean domain_level_from, boolean domain_level_to,
106  boolean make_copies);
107 static int find_cc_constraint(char * source_clock_domain, char * sink_clock_domain);
108 static boolean regex_match (char *string, char *pattern);
109 static void count_netlist_ios_as_constrained_ios(char * clock_name, float io_delay);
110 static void free_io_constraint(t_io *& io_array, int num_ios);
111 static void free_clock_constraint(t_clock *& clock_array, int num_clocks);
112 
113 /********************* Subroutine definitions *******************************/
114 
115 void read_sdc(t_timing_inf timing_inf) {
116 
117  char buf[BUFSIZE];
118  int source_clock_domain, sink_clock_domain, iinput, ioutput, icc, isource, isink;
119  boolean found;
120 
121  /* Make sure we haven't called this subroutine before. */
122  assert(!g_sdc);
123 
124  /* Allocate container structure for SDC constraints. */
125  g_sdc = (t_timing_constraints *) my_calloc(1, sizeof(t_timing_constraints));
126 
127  /* Reset file line number. */
128  file_line_number = 0;
129 
130  /* If no SDC file is included or specified, or timing analysis is off,
131  use default behaviour of cutting paths between domains and optimizing each clock separately */
132 
133  if (!timing_inf.timing_analysis_enabled) {
134  vpr_printf(TIO_MESSAGE_INFO, "\n");
135  vpr_printf(TIO_MESSAGE_INFO, "Timing analysis off; using default timing constraints.\n");
137  return;
138  }
139 
140  if ((sdc = fopen(timing_inf.SDCFile, "r")) == NULL) {
141  vpr_printf(TIO_MESSAGE_INFO, "\n");
142  vpr_printf(TIO_MESSAGE_INFO, "SDC file '%s' blank or not found.\n", timing_inf.SDCFile);
144  return;
145  }
146 
147  /* Now we have an SDC file. */
148 
149  /* Count how many clocks and I/Os are in the netlist.
150  Store the names of each clock and each I/O in netlist_clocks and netlist_ios.
151  The only purpose of these two lists is to compare clock names in the SDC file against them.
152  As a result, they will be freed after the SDC file is parsed. */
154 
155  /* Parse the file line-by-line. */
156  found = FALSE;
157  while (my_fgets(buf, BUFSIZE, sdc) != NULL) {
158  if (get_sdc_tok(buf)) {
159  found = TRUE;
160  }
161  }
162  if (!found) { /* blank file or only comments found */
163  vpr_printf(TIO_MESSAGE_INFO, "\n");
164  vpr_printf(TIO_MESSAGE_INFO, "SDC file '%s' blank or not found.\n", timing_inf.SDCFile);
166  free(netlist_clocks);
167  free(netlist_ios);
168  return;
169  }
170 
171  fclose(sdc);
172 
173  /* Make sure that all virtual clocks referenced in g_sdc->constrained_inputs and g_sdc->constrained_outputs have been constrained. */
174  for (iinput = 0; iinput < g_sdc->num_constrained_inputs; iinput++) {
175  if ((find_constrained_clock(g_sdc->constrained_inputs[iinput].clock_name)) == -1) {
176  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] Input %s is associated with an unconstrained clock %s.\n",
177  g_sdc->constrained_inputs[iinput].file_line_number,
178  g_sdc->constrained_inputs[iinput].name,
179  g_sdc->constrained_inputs[iinput].clock_name);
180  exit(1);
181  }
182  }
183 
184  for (ioutput = 0; ioutput < g_sdc->num_constrained_outputs; ioutput++) {
185  if ((find_constrained_clock(g_sdc->constrained_outputs[ioutput].clock_name)) == -1) {
186  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] Output %s is associated with an unconstrained clock %s.\n",
187  g_sdc->constrained_inputs[iinput].file_line_number,
188  g_sdc->constrained_outputs[ioutput].name,
189  g_sdc->constrained_outputs[ioutput].clock_name);
190  exit(1);
191  }
192  }
193 
194  /* Make sure that all clocks referenced in g_sdc->cc_constraints have been constrained. */
195  for (icc = 0; icc < g_sdc->num_cc_constraints; icc++) {
196  for (isource = 0; isource < g_sdc->cc_constraints[icc].num_source; isource++) {
197  if ((find_constrained_clock(g_sdc->cc_constraints[icc].source_list[isource])) == -1) {
198  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] Token %s is not a constrained clock.\n",
199  g_sdc->cc_constraints[icc].file_line_number,
200  g_sdc->cc_constraints[icc].source_list[isource]);
201  exit(1);
202  }
203  }
204  for (isink = 0; isink < g_sdc->cc_constraints[icc].num_sink; isink++) {
205  if ((find_constrained_clock(g_sdc->cc_constraints[icc].sink_list[isink])) == -1) {
206  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] Token %s is not a constrained clock.\n",
207  g_sdc->cc_constraints[icc].file_line_number,
208  g_sdc->cc_constraints[icc].sink_list[isink]);
209  exit(1);
210  }
211  }
212  }
213 
214  /* Allocate matrix of timing constraints [0..g_sdc->num_constrained_clocks-1][0..g_sdc->num_constrained_clocks-1] and initialize to 0 */
215  g_sdc->domain_constraint = (float **) alloc_matrix(0, g_sdc->num_constrained_clocks-1, 0, g_sdc->num_constrained_clocks-1, sizeof(float));
216 
217  /* Based on the information from sdc_clocks, calculate constraints for all paths except ones with an override constraint. */
218  for (source_clock_domain = 0; source_clock_domain < g_sdc->num_constrained_clocks; source_clock_domain++) {
219  for (sink_clock_domain = 0; sink_clock_domain < g_sdc->num_constrained_clocks; sink_clock_domain++) {
220  if ((icc = find_cc_constraint(g_sdc->constrained_clocks[source_clock_domain].name, g_sdc->constrained_clocks[sink_clock_domain].name)) != -1) {
221  if (g_sdc->cc_constraints[icc].num_multicycles == 0) {
222  /* There's a special constraint from set_false_path, set_clock_groups
223  -exclusive or set_max_delay which overrides the default constraint. */
224  g_sdc->domain_constraint[source_clock_domain][sink_clock_domain] = g_sdc->cc_constraints[icc].constraint;
225  } else {
226  /* There's a special constraint from set_multicycle_path which overrides the default constraint.
227  This constraint = default constraint (obtained via edge counting) + (num_multicycles - 1) * period of sink clock domain. */
228  g_sdc->domain_constraint[source_clock_domain][sink_clock_domain] =
229  calculate_constraint(sdc_clocks[source_clock_domain], sdc_clocks[sink_clock_domain])
230  + (g_sdc->cc_constraints[icc].num_multicycles - 1) * sdc_clocks[sink_clock_domain].period;
231  }
232  } else {
233  /* There's no special override constraint. */
234  /* Calculate the constraint between clock domains by finding the smallest positive
235  difference between a posedge in the source domain and one in the sink domain. */
236  g_sdc->domain_constraint[source_clock_domain][sink_clock_domain] =
237  calculate_constraint(sdc_clocks[source_clock_domain], sdc_clocks[sink_clock_domain]);
238  }
239  }
240  }
241 
242  vpr_printf(TIO_MESSAGE_INFO, "\n");
243  vpr_printf(TIO_MESSAGE_INFO, "SDC file '%s' parsed successfully.\n",
244  timing_inf.SDCFile );
245  vpr_printf(TIO_MESSAGE_INFO, "%d clocks (including virtual clocks), %d inputs and %d outputs were constrained.\n",
247  vpr_printf(TIO_MESSAGE_INFO, "\n");
248 
249  /* Since all the information we need is stored in g_sdc->domain_constraint, g_sdc->constrained_clocks,
250  and constrained_ios, free other data structures used in this routine */
251  free(sdc_clocks);
252  free(netlist_clocks);
253  free(netlist_ios);
254  return;
255 }
256 
258 
259  int source_clock_domain, sink_clock_domain;
260 
261  /* Find all netlist clocks and add them as constrained clocks. */
263 
264  /* We'll use separate defaults for multi-clock and single-clock/combinational circuits. */
265 
266  if (g_sdc->num_constrained_clocks <= 1) {
267  /* Create one constrained clock with period 0... */
268  g_sdc->domain_constraint = (float **) alloc_matrix(0, 0, 0, 0, sizeof(float));
269  g_sdc->domain_constraint[0][0] = 0.;
270 
271  if (g_sdc->num_constrained_clocks == 0) {
272  /* We need to create a virtual clock to constrain I/Os on. */
273  g_sdc->num_constrained_clocks = 1;
274  g_sdc->constrained_clocks = (t_clock *) my_malloc(sizeof(t_clock));
275  g_sdc->constrained_clocks[0].name = my_strdup("virtual_io_clock");
277 
278  vpr_printf(TIO_MESSAGE_INFO, "\n");
279  vpr_printf(TIO_MESSAGE_INFO, "Defaulting to: constrain all %d inputs and %d outputs on a virtual external clock.\n",
281  vpr_printf(TIO_MESSAGE_INFO, "Optimize this virtual clock to run as fast as possible.\n");
282  } else {
283  vpr_printf(TIO_MESSAGE_INFO, "\n");
284  vpr_printf(TIO_MESSAGE_INFO, "Defaulting to: constrain all %d inputs and %d outputs on the netlist clock.\n",
286  vpr_printf(TIO_MESSAGE_INFO, "Optimize this clock to run as fast as possible.\n");
287  }
288 
289  /* Constrain all I/Os on the single constrained clock (whether real or virtual), with I/O delay 0. */
291 
292  } else { /* Multiclock circuit */
293 
294  /* Constrain all I/Os on a separate virtual clock. Cut paths between all netlist
295  clocks, but analyse all paths between the virtual I/O clock and netlist clocks
296  and optimize all clocks to go as fast as possible. */
297 
298  g_sdc->constrained_clocks = (t_clock *) my_realloc (g_sdc->constrained_clocks, ++g_sdc->num_constrained_clocks * sizeof(t_clock));
299  g_sdc->constrained_clocks[g_sdc->num_constrained_clocks - 1].name = my_strdup("virtual_io_clock");
302 
303  /* Allocate matrix of timing constraints [0..g_sdc->num_constrained_clocks-1][0..g_sdc->num_constrained_clocks-1] */
304  g_sdc->domain_constraint = (float **) alloc_matrix(0, g_sdc->num_constrained_clocks-1, 0, g_sdc->num_constrained_clocks-1, sizeof(float));
305 
306  for (source_clock_domain = 0; source_clock_domain < g_sdc->num_constrained_clocks; source_clock_domain++) {
307  for (sink_clock_domain = 0; sink_clock_domain < g_sdc->num_constrained_clocks; sink_clock_domain++) {
308  if (source_clock_domain == sink_clock_domain || source_clock_domain == g_sdc->num_constrained_clocks - 1
309  || sink_clock_domain == g_sdc->num_constrained_clocks - 1) {
310  g_sdc->domain_constraint[source_clock_domain][sink_clock_domain] = 0.;
311  } else {
312  g_sdc->domain_constraint[source_clock_domain][sink_clock_domain] = DO_NOT_ANALYSE;
313  }
314  }
315  }
316 
317  vpr_printf(TIO_MESSAGE_INFO, "\n");
318  vpr_printf(TIO_MESSAGE_INFO, "Defaulting to: constrain all %d inputs and %d outputs on a virtual external clock;\n",
320  vpr_printf(TIO_MESSAGE_INFO, "\tcut paths between netlist clock domains; and\n");
321  vpr_printf(TIO_MESSAGE_INFO, "\toptimize all clocks to run as fast as possible.\n");
322  }
323 }
324 
326 
327  /* Count how many clocks and I/Os are in the netlist.
328  Store the names of each clock and each I/O in netlist_clocks and netlist_ios. */
329 
330  int iblock, i, clock_net;
331  char * name;
332  boolean found;
333 
334  for (iblock = 0; iblock < num_logical_blocks; iblock++) {
335  if (logical_block[iblock].clock_net != OPEN) {
336  clock_net = logical_block[iblock].clock_net;
337  assert(clock_net != OPEN);
338  name = logical_block[clock_net].name;
339  /* Now that we've found a clock, let's see if we've counted it already */
340  found = FALSE;
341  for (i = 0; !found && i < num_netlist_clocks; i++) {
342  if (strcmp(netlist_clocks[i], name) == 0) {
343  found = TRUE;
344  }
345  }
346  if (!found) {
347  /* If we get here, the clock is new and so we dynamically grow the array netlist_clocks by one. */
348  netlist_clocks = (char **) my_realloc (netlist_clocks, ++num_netlist_clocks * sizeof(char *));
349  netlist_clocks[num_netlist_clocks - 1] = name;
350  }
351  } else if (logical_block[iblock].type == VPACK_INPAD || logical_block[iblock].type == VPACK_OUTPAD) {
352  name = logical_block[iblock].name;
353  /* Now that we've found an I/O, let's see if we've counted it already */
354  found = FALSE;
355  for (i = 0; !found && i < num_netlist_ios; i++) {
356  if (strcmp(netlist_ios[i], name) == 0) {
357  found = TRUE;
358  }
359  }
360  if (!found) {
361  /* If we get here, the I/O is new and so we dynamically grow the array netlist_ios by one. */
362  netlist_ios = (char **) my_realloc (netlist_ios, ++num_netlist_ios * sizeof(char *));
363  netlist_ios[num_netlist_ios - 1] = logical_block[iblock].type == VPACK_OUTPAD ? name + 4 : name;
364  /* the + 4 removes the prefix "out:" automatically prepended to outputs */
365  }
366  }
367  }
368 }
369 
371  /* Counts how many clocks are in the netlist, and adds them to the array g_sdc->constrained_clocks. */
372 
373  int iblock, i, clock_net;
374  char * name;
375  boolean found;
376 
377  g_sdc->num_constrained_clocks = 0;
378 
379  for (iblock = 0; iblock < num_logical_blocks; iblock++) {
380  if (logical_block[iblock].clock_net != OPEN) {
381  clock_net = logical_block[iblock].clock_net;
382  assert(clock_net != OPEN);
383  name = logical_block[clock_net].name;
384  /* Now that we've found a clock, let's see if we've counted it already */
385  found = FALSE;
386  for (i = 0; !found && i < g_sdc->num_constrained_clocks; i++) {
387  if (strcmp(g_sdc->constrained_clocks[i].name, name) == 0) {
388  found = TRUE;
389  }
390  }
391  if (!found) {
392  /* If we get here, the clock is new and so we dynamically grow the array g_sdc->constrained_clocks by one. */
393  g_sdc->constrained_clocks = (t_clock *) my_realloc (g_sdc->constrained_clocks, ++g_sdc->num_constrained_clocks * sizeof(t_clock));
394  g_sdc->constrained_clocks[g_sdc->num_constrained_clocks - 1].name = my_strdup(name);
396  /* Fanout will be filled out once the timing graph has been constructed. */
397  }
398  }
399  }
400 }
401 
402 static void count_netlist_ios_as_constrained_ios(char * clock_name, float io_delay) {
403  /* Count how many I/Os are in the netlist, adds them to the arrays g_sdc->constrained_inputs/
404  g_sdc->constrained_outputs with an I/O delay of 0 and constrains them to clock clock_name. */
405 
406  int iblock, iinput, ioutput;
407  char * name;
408  boolean found;
409 
410  for (iblock = 0; iblock < num_logical_blocks; iblock++) {
411  if (logical_block[iblock].type == VPACK_INPAD) {
412  name = logical_block[iblock].name;
413  /* Now that we've found an I/O, let's see if we've counted it already */
414  found = FALSE;
415  for (iinput = 0; !found && iinput < g_sdc->num_constrained_inputs; iinput++) {
416  if (strcmp(g_sdc->constrained_inputs[iinput].name, name) == 0) {
417  found = TRUE;
418  }
419  }
420  if (!found) {
421  /* If we get here, the input is new and so we add it to g_sdc->constrained_inputs. */
422  g_sdc->constrained_inputs = (t_io *) my_realloc (g_sdc->constrained_inputs, ++g_sdc->num_constrained_inputs * sizeof(t_io));
423  g_sdc->constrained_inputs[g_sdc->num_constrained_inputs - 1].name = my_strdup(name);
424  g_sdc->constrained_inputs[g_sdc->num_constrained_inputs - 1].clock_name = my_strdup(clock_name);
425  g_sdc->constrained_inputs[g_sdc->num_constrained_inputs - 1].delay = 0.;
426  }
427  } else if (logical_block[iblock].type == VPACK_OUTPAD) {
428  name = logical_block[iblock].name;
429  /* Now that we've found an I/O, let's see if we've counted it already */
430  found = FALSE;
431  for (ioutput = 0; !found && ioutput < g_sdc->num_constrained_outputs; ioutput++) {
432  if (strcmp(g_sdc->constrained_outputs[ioutput].name, name) == 0) {
433  found = TRUE;
434  }
435  }
436  if (!found) {
437  /* If we get here, the output is new and so we add it to g_sdc->constrained_outputs. */
438  g_sdc->constrained_outputs = (t_io *) my_realloc (g_sdc->constrained_outputs, ++g_sdc->num_constrained_outputs * sizeof(t_io));
439  g_sdc->constrained_outputs[g_sdc->num_constrained_outputs - 1].name = my_strdup(name + 4);
440  /* the + 4 removes the prefix "out:" automatically prepended to outputs */
441  g_sdc->constrained_outputs[g_sdc->num_constrained_outputs - 1].clock_name = my_strdup(clock_name);
442  g_sdc->constrained_outputs[g_sdc->num_constrained_outputs - 1].delay = 0.;
443  }
444  }
445  }
446 }
447 
448 static boolean get_sdc_tok(char * buf) {
449 /* Figures out which tokens are on this line and takes the appropriate actions.
450  Returns true if anything non-commented is found on this line. */
451 
452 #define SDC_TOKENS " \t\n{}[]" /* We can ignore braces. */
453 
454  char * ptr, ** from_list = NULL, ** to_list = NULL, * clock_name;
455  float clock_period, rising_edge, falling_edge, max_delay;
456  int iclock, iio, num_exclusive_groups = 0,
457  num_from = 0, num_to = 0, num_multicycles, i, j;
458  t_sdc_exclusive_group * exclusive_groups = NULL;
459  boolean found, domain_level_from = FALSE, domain_level_to = FALSE;
460 
461  /* my_strtok splits the string into tokens - little character arrays separated by the SDC_TOKENS
462  defined above. Throughout this code, ptr refers to the tokens we fetch, one at a time. The token
463  changes at each call of my_strtok. We call my_strtok with NULL as the first argument every time
464  AFTER the first, since this picks up tokenizing where we left off. We always wrap each call to
465  my_strtok with a check that ptr is non-null to avoid an exception from passing NULL into strcmp. */
466 
467 
468  if ((ptr = my_strtok(buf, SDC_TOKENS, sdc, buf)) == NULL) {/* blank line */
469  return FALSE;
470  }
471 
472  if (strcmp(ptr, "create_clock") == 0) {
473  /* Syntax: create_clock -period <float> [-waveform {rising_edge falling_edge}] <netlist clock list or regexes>
474  or create_clock -period <float> [-waveform {rising_edge falling_edge}] -name <virtual clock name>*/
475 
476  /* make sure clock has -period specified */
477  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || strcmp(ptr, "-period") != 0) {
478  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] Create_clock must be directly followed by '-period'.\n",
480  exit(1);
481  }
482 
483  /* Check if the token following -period is actually a number. */
484  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || !is_number(ptr)) {
485  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] A number must follow '-period'.\n",
487  exit(1);
488  }
489  clock_period = (float) strtod(ptr, NULL);
490 
491  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
492  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] Clock(s) not specified.\n",
494  exit(1);
495  }
496  if (strcmp(ptr, "-waveform") == 0) {
497 
498  /* Get the first float, which is the rising edge, and the second, which is the falling edge. */
499 
500  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || !is_number(ptr)) {
501  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] First token following '-waveform' should be rising edge, but is not a number.\n",
503  }
504  rising_edge = (float) strtod(ptr, NULL);
505 
506  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || !is_number(ptr)) {
507  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] Second token following '-waveform' should be falling edge, but is not a number.\n",
509  exit(1);
510  }
511  falling_edge = (float) strtod(ptr, NULL);
512 
513  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
514  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] Clock(s) not specified.\n",
516  exit(1);
517  } /* We need this extra call to my_strtok to advance the ptr to the right spot. */
518 
519  } else {
520  /* The clock's rising edge is by default at 0, and the falling edge is at the half-period. */
521  rising_edge = 0.;
522  falling_edge = clock_period / 2.0;
523  }
524 
525  if (strcmp(ptr, "-name") == 0) {
526  /* For external virtual clocks only (used with I/O constraints).
527  Only one virtual clock can be specified per line,
528  so make sure there's only one token left on this line. */
529 
530  /* Get the virtual clock name */
531  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
532  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] Virtual clock name not specified.\n",
534  exit(1);
535  } /* We need this extra call to my_strtok to advance the ptr to the right spot. */
536 
537  /* We've found a new clock! */
538 
539  /* Store the clock's name, period and edges in the local array sdc_clocks. */
540  sdc_clocks = (t_sdc_clock *) my_realloc(sdc_clocks, ++g_sdc->num_constrained_clocks * sizeof(t_sdc_clock));
541  sdc_clocks[g_sdc->num_constrained_clocks - 1].name = ptr;
542  sdc_clocks[g_sdc->num_constrained_clocks - 1].period = clock_period;
543  sdc_clocks[g_sdc->num_constrained_clocks - 1].rising_edge = rising_edge;
544  sdc_clocks[g_sdc->num_constrained_clocks - 1].falling_edge = falling_edge;
545 
546  /* Also store the clock's name, and the fact that it is not a netlist clock, in g_sdc->constrained_clocks. */
548  g_sdc->constrained_clocks[g_sdc->num_constrained_clocks - 1].name = my_strdup(ptr);
550  /* Fanout will be filled out once the timing graph has been constructed. */
551 
552  /* The next token should be NULL. If so, return; if not, print an error message and exit. */
553  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) != NULL) {
554  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] More than one virtual clock name is specified after '-name'.\n",
556  exit(1);
557  }
558 
559  } else {
560  /* Parse through to the end of the line. All that should be left on this line are one or more
561  * regular expressions denoting netlist clocks to be associated with this clock period. An array sdc_clocks will
562  * store the period and offset of each clock at the same index which that clock has in netlist_clocks. Later,
563  * after everything has been parsed, we take the information from this array to calculate the actual timing constraints
564  * which these periods and offsets imply, and put them in the matrix g_sdc->domain_constraint. */
565 
566  do {
567  /* See if the regular expression stored in ptr is legal and matches at least one clock net.
568  If it is not legal, it will fail during regex_match. We check for a match using boolean found. */
569  found = FALSE;
570  for (iclock = 0; iclock < num_netlist_clocks; iclock++) {
571  if (regex_match(netlist_clocks[iclock], ptr)) {
572  /* We've found a new clock! (Note that we can't store ptr as the clock's
573  name since it could be a regex, unlike the virtual clock case).*/
574  found = TRUE;
575 
576  /* Store the clock's name, period and edges in the local array sdc_clocks. */
577  sdc_clocks = (t_sdc_clock *) my_realloc(sdc_clocks, ++g_sdc->num_constrained_clocks * sizeof(t_sdc_clock));
578  sdc_clocks[g_sdc->num_constrained_clocks - 1].name = netlist_clocks[iclock];
579  sdc_clocks[g_sdc->num_constrained_clocks - 1].period = clock_period;
580  sdc_clocks[g_sdc->num_constrained_clocks - 1].rising_edge = rising_edge;
581  sdc_clocks[g_sdc->num_constrained_clocks - 1].falling_edge = falling_edge;
582 
583  /* Also store the clock's name, and the fact that it is a netlist clock, in g_sdc->constrained_clocks. */
587  /* Fanout will be filled out once the timing graph has been constructed. */
588  }
589  }
590 
591  if (!found) {
592  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] Clock name or regular expression does not correspond to any nets.\n",
594  vpr_printf(TIO_MESSAGE_ERROR, "If you'd like to create a virtual clock, use the '-name' keyword.\n");
595  exit(1);
596  }
597  } while ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) != NULL); /* Advance to the next token (or the end of the line). */
598  }
599 
600  /* Warn if the clock has non-50% duty cycle. */
601  if (fabs(rising_edge - falling_edge) - clock_period/2.0 > EPSILON) {
602  vpr_printf(TIO_MESSAGE_WARNING, "Clock %s does not have 50%% duty cycle.\n",
603  sdc_clocks[g_sdc->num_constrained_clocks - 1].name);
604  }
605 
606  return TRUE;
607 
608  } else if (strcmp(ptr, "set_clock_groups") == 0) {
609  /* Syntax: set_clock_groups -exclusive -group {<clock list or regexes>} -group {<clock list or regexes>} [-group {<clock list or regexes>} ...] */
610 
611  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || strcmp(ptr, "-exclusive") != 0) {
612  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_clock_groups must be directly followed by '-exclusive'.\n",
614  exit(1);
615  }
616 
617  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || strcmp(ptr, "-group") != 0) {
618  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_clock_groups '-exclusive' must be followed by lists of clock names or regular expressions each starting with the '-group' command.\n",
620  exit(1);
621  }
622 
623  /* Parse through to the end of the line. All that should be left on this line are a bunch of
624  -group commands, followed by groups of regexes. We need to ensure that paths are cut between
625  every clock matching a regex in one group and every clock matching a regex in any other group. */
626 
627  do {
628  ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf);
629 
630  /* Create a new entry in exclusive groups */
631  exclusive_groups = (t_sdc_exclusive_group *) my_realloc(
632  exclusive_groups, ++num_exclusive_groups * sizeof(t_sdc_exclusive_group));
633  exclusive_groups[num_exclusive_groups - 1].clock_names = NULL;
634  exclusive_groups[num_exclusive_groups - 1].num_clock_names = 0;
635  do {
636  /* Check the regex ptr against each netlist clock and add it to the clock_names list if it matches. */
637  found = FALSE;
638  for (iclock = 0; iclock < num_netlist_clocks; iclock++) {
639  if (regex_match(netlist_clocks[iclock], ptr)) {
640  found = TRUE;
641  exclusive_groups[num_exclusive_groups - 1].clock_names = (char **) my_realloc(
642  exclusive_groups[num_exclusive_groups - 1].clock_names, ++exclusive_groups[num_exclusive_groups - 1].num_clock_names * sizeof(char *));
643  exclusive_groups[num_exclusive_groups - 1].clock_names
644  [exclusive_groups[num_exclusive_groups - 1].num_clock_names - 1] =
645  my_strdup(netlist_clocks[iclock]);
646  }
647  }
648  if (!found) {
649  /* If no clocks matched, assume ptr is the name of a virtual clock and add it to the list.
650  (If it's not a virtual clock, we'll catch it later when we check all override constraints.) */
651  exclusive_groups[num_exclusive_groups - 1].clock_names = (char **) my_realloc(
652  exclusive_groups[num_exclusive_groups - 1].clock_names, ++exclusive_groups[num_exclusive_groups - 1].num_clock_names * sizeof(char *));
653  exclusive_groups[num_exclusive_groups - 1].clock_names
654  [exclusive_groups[num_exclusive_groups - 1].num_clock_names - 1] =
655  my_strdup(ptr);
656  }
657  } while ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) != NULL && strcmp(ptr, "-group") != 0);
658  } while (ptr);
659 
660  if (num_exclusive_groups < 2) {
661  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] At least two '-group' commands required.",
663  exit(1);
664  }
665 
666  /* Finally, create two DO_NOT_ANALYSE override constraints for each pair of entries
667  to cut paths bidirectionally between pairs of clock lists in different groups.
668  Set make_copies to TRUE because we have to use the lists of names in multiple
669  override constraints, and it's impossible to free them from multiple places at the
670  end without a whole lot of trouble. */
671 
672  for (i = 0; i < num_exclusive_groups; i++) {
673  for (j = 0; j < num_exclusive_groups; j++) {
674  if (i != j) {
675  add_override_constraint(exclusive_groups[i].clock_names, exclusive_groups[i].num_clock_names,
676  exclusive_groups[j].clock_names, exclusive_groups[j].num_clock_names, DO_NOT_ANALYSE, 0, TRUE, TRUE, TRUE);
677  }
678  }
679  }
680 
681  /* Now that we've copied all the clock name lists
682  (2 * num_exlusive_groups - 1) times, free the original lists. */
683  for (i = 0; i < num_exclusive_groups; i++) {
684  for (j = 0; j < exclusive_groups[i].num_clock_names; j++) {
685  free(exclusive_groups[i].clock_names[j]);
686  }
687  free(exclusive_groups[i].clock_names);
688  }
689  free (exclusive_groups);
690 
691  return TRUE;
692 
693  } else if (strcmp(ptr, "set_false_path") == 0) {
694  /* Syntax: set_false_path -from <clock list or regexes> -to <clock list or regexes> */
695 
696  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || strcmp(ptr, "-from") != 0 || (ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
697  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_false_path must be directly followed by '-from <clock/flip-flop_list>'.\n",
699  exit(1);
700  }
701 
702  if (strcmp(ptr, "get_clocks") == 0) {
703  domain_level_from = TRUE;
704  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
705  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_false_path must be directly followed by '-from <clock/flip-flop_list>'.\n",
707  exit(1);
708  }
709  }
710 
711  do {
712  /* Keep adding clock names to from_list until we hit the -to command. */
713  from_list = (char **) my_realloc(from_list, ++num_from * sizeof(char *));
714  from_list[num_from - 1] = my_strdup(ptr);
715 
716  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
717  /* We hit the end of the line before finding a -to. */
718  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_false_path requires '-to <clock/flip-flop_list>' after '-from <clock/flip-flop_list>'.\n",
720  exit(1);
721  }
722  } while (strcmp(ptr, "-to") != 0);
723 
724  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
725  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_false_path requires '-to <clock/flip-flop_list>' after '-from <clock/flip-flop_list>'.\n",
727  }
728 
729  if (strcmp(ptr, "get_clocks") == 0) {
730  domain_level_to = TRUE;
731  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
732  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_false_path must be directly followed by '-from <clock/flip-flop_list>'.\n",
734  exit(1);
735  }
736  }
737 
738  do {
739  /* Keep adding clock names to to_list until we hit the end of the line. */
740  to_list = (char **) my_realloc(to_list, ++num_to * sizeof(char *));
741  to_list[num_to - 1] = my_strdup(ptr);
742  } while ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) != NULL);
743 
744  /* Create a constraint between each element in from_list and each element in to_list with value DO_NOT_ANALYSE.
745  Set make_copies to false since, as we only need to use from_list and to_list once, we can just have the
746  override constraint entry point to those lists. */
747  add_override_constraint(from_list, num_from, to_list, num_to, DO_NOT_ANALYSE, 0, domain_level_from, domain_level_to, FALSE);
748 
749  /* Finally, set from_list and to_list to NULL since they're both
750  being pointed to by the override constraint entry we just created. */
751  from_list = NULL, to_list = NULL;
752 
753  return TRUE;
754 
755  } else if (strcmp(ptr, "set_max_delay") == 0) {
756  /* Syntax: set_max_delay <delay> -from <clock list or regexes> -to <clock list or regexes> */
757 
758  /* Basically the same as set_false_path above, except we get a specific delay value for the constraint. */
759 
760  /* check if the token following set_max_delay is actually a number*/
761  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || !is_number(ptr)) {
762  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] Token following set_max_delay should be a delay value, but is not a number.\n",
764  exit(1);
765  }
766  max_delay = (float) strtod(ptr, NULL);
767 
768  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || strcmp(ptr, "-from") != 0 || (ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
769  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_max_delay requires '-from <clock/flip-flop_list>' after max_delay.\n",
771  exit(1);
772  }
773 
774  if (strcmp(ptr, "get_clocks") == 0) {
775  domain_level_from = TRUE;
776  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
777  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_max_delay requires '-from <clock/flip-flop_list>' after max_delay.\n",
779  exit(1);
780  }
781  }
782 
783  do {
784  /* Keep adding clock names to from_list until we hit the -to command. */
785  from_list = (char **) my_realloc(from_list, ++num_from * sizeof(char *));
786  from_list[num_from - 1] = my_strdup(ptr);
787 
788  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
789  /* We hit the end of the line before finding a -to. */
790  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_max_delay requires '-to <clock/flip-flop_list>' after '-from <clock/flip-flop_list>'.\n",
792  exit(1);
793  }
794  } while (strcmp(ptr, "-to") != 0);
795 
796  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
797  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_max_delay requires '-to <clock/flip-flop_list>' after '-from <clock/flip-flop_list>'.\n",
799  }
800 
801  if (strcmp(ptr, "get_clocks") == 0) {
802  domain_level_to = TRUE;
803  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
804  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_max_delay requires '-to <clock/flip-flop_list>' after '-from <clock/flip-flop_list>'.\n",
806  exit(1);
807  }
808  }
809 
810  do {
811  /* Keep adding clock names to to_list until we hit the end of the line. */
812  to_list = (char **) my_realloc(to_list, ++num_to * sizeof(char *));
813  to_list[num_to - 1] = my_strdup(ptr);
814  } while ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) != NULL);
815 
816  /* Create a constraint between each element in from_list and each element in to_list with value max_delay. */
817  add_override_constraint(from_list, num_from, to_list, num_to, max_delay, 0, domain_level_from, domain_level_to, FALSE);
818 
819  /* Finally, set from_list and to_list to NULL since they're both
820  being pointed to by the override constraint entry we just created. */
821  from_list = NULL, to_list = NULL;
822 
823  return TRUE;
824 
825  } else if (strcmp(ptr, "set_multicycle_path") == 0) {
826  /* Syntax: set_multicycle_path -setup -from <clock list or regexes> -to <clock list or regexes> <num_multicycles> */
827 
828  /* Basically the same as set_false_path and set_max_delay above, except we have to calculate
829  the default value of the constraint (obtained via edge counting) first, and then set a
830  constraint equal to default constraint + (num_multicycles - 1) * period of sink clock domain. */
831 
832  /* check if the token following set_max_delay is actually a number*/
833  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || strcmp(ptr, "-setup") != 0) {
834  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_multicycle_path must be directly followed by '-setup'.\n",
836  exit(1);
837  }
838 
839  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || strcmp(ptr, "-from") != 0 || (ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
840  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_multicycle_path requires '-from <clock/flip-flop_list>' after '-setup'.\n",
842  exit(1);
843  }
844 
845  if (strcmp(ptr, "get_clocks") == 0) {
846  domain_level_from = TRUE;
847  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
848  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_multicycle_path '-setup' must be followed by '-from <clock/flip-flop_list>'.\n",
850  exit(1);
851  }
852  }
853 
854  do {
855  /* Keep adding clock names to from_list until we hit the -to command. */
856  from_list = (char **) my_realloc(from_list, ++num_from * sizeof(char *));
857  from_list[num_from - 1] = my_strdup(ptr);
858 
859  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
860  /* We hit the end of the line before finding a -to. */
861  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_multicycle_path requires '-to <clock/flip-flop_list>' after '-from <clock/flip-flop_list>'.\n",
863  exit(1);
864  }
865  } while (strcmp(ptr, "-to") != 0);
866 
867  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
868  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_multicycle_path requires '-to <clock/flip-flop_list>' after '-from <clock/flip-flop_list>'.\n",
870  }
871 
872  if (strcmp(ptr, "get_clocks") == 0) {
873  domain_level_to = TRUE;
874  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
875  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_multicycle_path requires '-to <clock/flip-flop_list>' after '-from <clock/flip-flop_list>'.\n",
877  exit(1);
878  }
879  }
880 
881  do {
882  /* Keep adding clock names to to_list until we hit a number (i.e. num_multicycles). */
883  to_list = (char **) my_realloc(to_list, ++num_to * sizeof(char *));
884  to_list[num_to - 1] = my_strdup(ptr);
885  } while (((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) != NULL) && !is_number(ptr));
886 
887  if (!ptr) {
888  /* We hit the end of the line before finding a number. */
889  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_multicycle_path requires num_multicycles after '-to <clock/flip-flop_list>'.\n",
891  exit(1);
892  }
893 
894  num_multicycles = (int) strtod(ptr, NULL);
895 
896  /* Create an override constraint between from and to. Unlike the previous two commands, set_multicycle_path requires
897  information about the periods and offsets of the clock domains which from and to, which we have to fill in at the end. */
898  add_override_constraint(from_list, num_from, to_list, num_to, HUGE_NEGATIVE_FLOAT /* irrelevant - never used */,
899  num_multicycles, domain_level_from, domain_level_to, FALSE);
900 
901  /* Finally, set from_list and to_list to NULL since they're both
902  being pointed to by the override constraint entry we just created. */
903  from_list = NULL, to_list = NULL;
904 
905  return TRUE;
906 
907  } else if (strcmp(ptr, "set_input_delay") == 0) {
908  /* Syntax: set_input_delay -clock <virtual or netlist clock> -max <max_delay> [get_ports {<I/O port list or regexes>}] */
909 
910  /* We want to assign virtual_clock to all input ports in port_list, and
911  set the input delay (from the external device to the FPGA) to max_delay. */
912 
913  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || strcmp(ptr, "-clock") != 0 || (ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
914  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_input_delay must be directly followed by '-clock <virtual or netlist clock name>'.\n",
916  exit(1);
917  }
918 
919  if (num_netlist_clocks == 1 && strcmp(ptr, "*") == 0) {
920  /* Allow the user to wildcard the clock name if there's only one clock (not standard SDC but very convenient). */
921  clock_name = netlist_clocks[0];
922  } else {
923  /* We have no way of error-checking whether this is an actual virtual clock until we finish parsing. */
924  clock_name = ptr;
925  }
926 
927  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || strcmp(ptr, "-max") != 0) {
928  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_input_delay '-clock <virtual or netlist clock name>' must be directly followed by '-max <maximum_input_delay>'.\n",
930  exit(1);
931  }
932 
933  /* check if the token following -max is actually a number*/
934  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || !is_number(ptr)) {
935  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] Token following '-max' should be a delay value, but is not a number.\n",
937  exit(1);
938  }
939  max_delay = (float) strtod(ptr, NULL);
940 
941  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || strcmp(ptr, "get_ports") != 0) {
942  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_input_delay requires a [get_ports {...}] command following '-max <max_input_delay>'.\n",
944  exit(1);
945  }
946 
947  /* Parse through to the end of the line. Add each regular expression match we find to the list of
948  constrained inputs and give each entry the virtual clock name and max_delay we've just parsed.
949  We have no way of error-checking whether these tokens correspond to actual input ports until later. */
950 
951  for (;;) {
952  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) { /* end of line */
953  return TRUE;
954  }
955 
956  found = FALSE;
957 
958  for (iio = 0; iio < num_netlist_ios; iio++) {
959  /* See if the regular expression stored in ptr is legal and matches at least one input port.
960  If it is not legal, it will fail during regex_match. We check for a match using boolean found. */
961  if (regex_match(netlist_ios[iio], ptr)) {
962  /* We've found a new input! */
963  g_sdc->num_constrained_inputs++;
964  found = TRUE;
965 
966  /* Fill in input information in the permanent array g_sdc->constrained_inputs. */
967  g_sdc->constrained_inputs = (t_io *) my_realloc (g_sdc->constrained_inputs, g_sdc->num_constrained_inputs * sizeof(t_io));
969  g_sdc->constrained_inputs[g_sdc->num_constrained_inputs - 1].clock_name = my_strdup(clock_name);
970  g_sdc->constrained_inputs[g_sdc->num_constrained_inputs - 1].delay = max_delay;
971  g_sdc->constrained_inputs[g_sdc->num_constrained_inputs - 1].file_line_number = file_line_number; /* global var */
972  }
973  }
974 
975  if (!found) {
976  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] Output name or regular expression \"%s\" does not correspond to any nets.\n",
977  file_line_number, ptr);
978  exit(1);
979  }
980  }
981 
982  } else if (strcmp(ptr, "set_output_delay") == 0) {
983  /* Syntax: set_output_delay -clock <virtual or netlist clock> -max <max_delay> [get_ports {<I/O port list or regexes>}] */
984 
985  /* We want to assign virtual_clock to all output ports in port_list, and
986  set the output delay (from the external device to the FPGA) to max_delay. */
987 
988  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || strcmp(ptr, "-clock") != 0 || (ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) {
989  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_output_delay must be directly followed by '-clock <virtual or netlist clock name>'.\n",
991  exit(1);
992  }
993 
994  if (num_netlist_clocks == 1 && strcmp(ptr, "*") == 0) {
995  /* Allow the user to wildcard the clock name if there's only one clock (not standard SDC but very convenient). */
996  clock_name = netlist_clocks[0];
997  } else {
998  /* We have no way of error-checking whether this is an actual virtual clock until we finish parsing. */
999  clock_name = ptr;
1000  }
1001 
1002  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || strcmp(ptr, "-max") != 0) {
1003  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_output_delay -clock <virtual or netlist clock name> must be directly followed by '-max <maximum_output_delay>'.\n",
1005  exit(1);
1006  }
1007 
1008  /* check if the token following -max is actually a number*/
1009  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || !is_number(ptr)) {
1010  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] Token following '-max' should be a delay value, but is not a number.\n",
1012  exit(1);
1013  }
1014  max_delay = (float) strtod(ptr, NULL);
1015 
1016  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL || strcmp(ptr, "get_ports") != 0) {
1017  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] set_output_delay requires a [get_ports {...}] command following '-max <max_output_delay>'.\n",
1019  exit(1);
1020  }
1021 
1022  /* Parse through to the end of the line. Add each regular expression match we find to the list of
1023  constrained outputs and give each entry the virtual clock name and max_delay we've just parsed.
1024  We have no way of error-checking whether these tokens correspond to actual output ports until later. */
1025 
1026  for (;;) {
1027  if ((ptr = my_strtok(NULL, SDC_TOKENS, sdc, buf)) == NULL) { /* end of line */
1028  return TRUE;
1029  }
1030 
1031  found = FALSE;
1032 
1033  for (iio = 0; iio < num_netlist_ios; iio++) {
1034  /* See if the regular expression stored in ptr is legal and matches at least one output port.
1035  If it is not legal, it will fail during regex_match. We check for a match using boolean found. */
1036  if (regex_match(netlist_ios[iio], ptr)) {
1037  /* We've found a new output! */
1038  g_sdc->num_constrained_outputs++;
1039  found = TRUE;
1040 
1041  /* Fill in output information in the permanent array g_sdc->constrained_outputs. */
1042  g_sdc->constrained_outputs = (t_io *) my_realloc (g_sdc->constrained_outputs, g_sdc->num_constrained_outputs * sizeof(t_io));
1044  g_sdc->constrained_outputs[g_sdc->num_constrained_outputs - 1].clock_name = my_strdup(clock_name);
1045  g_sdc->constrained_outputs[g_sdc->num_constrained_outputs - 1].delay = max_delay;
1046  g_sdc->constrained_outputs[g_sdc->num_constrained_outputs - 1].file_line_number = file_line_number; /* global var */
1047  }
1048  }
1049 
1050  if (!found) {
1051  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] Output name or regular expression \"%s\" does not correspond to any nets.\n",
1052  file_line_number, ptr);
1053  exit(1);
1054  }
1055  }
1056 
1057  } else {
1058  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] Incorrect or unsupported syntax near start of line.\n",
1060  exit(1);
1061  }
1062 }
1063 
1064 static boolean is_number(char * ptr) {
1065 /* Checks if the character array ptr represents a valid floating-point number. *
1066  * To return TRUE, all characters must be digits, although *
1067  * there can also be no more than one decimal point. */
1068  int i, len, num_decimal_points = 0;
1069  len = strlen(ptr);
1070  for (i = 0; i < len; i++) {
1071  if ((ptr[i] < '0' || ptr[i] > '9')) {
1072  if (ptr[i] != '.') {
1073  return FALSE;
1074  }
1075  num_decimal_points++;
1076  if (num_decimal_points > 1) {
1077  return FALSE;
1078  }
1079  }
1080  }
1081  return TRUE;
1082 }
1083 
1084 static int find_constrained_clock(char * ptr) {
1085 /* Given a string ptr, find whether it's the name of a clock in the array g_sdc->constrained_clocks. *
1086  * if it is, return the clock's index in g_sdc->constrained_clocks; if it's not, return -1. */
1087  int index;
1088  for (index = 0; index < g_sdc->num_constrained_clocks; index++) {
1089  if (strcmp(ptr, g_sdc->constrained_clocks[index].name) == 0) {
1090  return index;
1091  }
1092  }
1093  return -1;
1094 }
1095 
1096 static int find_cc_constraint(char * source_clock_name, char * sink_clock_name) {
1097  /* Given a pair of source and sink clock domains, find out if there's an override constraint between them.
1098  If there is, return the index in g_sdc->cc_constraints; if there is not, return -1. */
1099  int icc, isource, isink;
1100 
1101  for (icc = 0; icc < g_sdc->num_cc_constraints; icc++) {
1102  for (isource = 0; isource < g_sdc->cc_constraints[icc].num_source; isource++) {
1103  if (strcmp(g_sdc->cc_constraints[icc].source_list[isource], source_clock_name) == 0) {
1104  for (isink = 0; isink < g_sdc->cc_constraints[icc].num_sink; isink++) {
1105  if (strcmp(g_sdc->cc_constraints[icc].sink_list[isink], sink_clock_name) == 0) {
1106  return icc;
1107  }
1108  }
1109  }
1110  }
1111  }
1112  return -1;
1113 }
1114 
1115 static void add_override_constraint(char ** from_list, int num_from, char ** to_list, int num_to,
1116  float constraint, int num_multicycles, boolean domain_level_from, boolean domain_level_to,
1117  boolean make_copies) {
1118  /* Add a special-case constraint to override the default, calculated timing constraint,
1119  to one of four arrays depending on whether it's coming from/to a flip-flop or an entire clock domain.
1120 
1121  If make_copies is true, we make a copy of from_list and to_list for this override constraint entry;
1122  if false, we just set the override constraint entry to point to the existing list. The latter is
1123  more efficient, but it's almost impossible to free multiple identical pointers without freeing
1124  the same thing twice and causing an error. */
1125 
1126  t_override_constraint ** constraint_array;
1127  /* Because we are reallocating the array and possibly changing
1128  its address, we need to modify it through a reference. */
1129 
1130  int num_constraints, i;
1131 
1132  if (domain_level_from) {
1133  if (domain_level_to) { /* Clock-to-clock constraint */
1134  constraint_array = &g_sdc->cc_constraints;
1135  num_constraints = ++g_sdc->num_cc_constraints;
1136  } else { /* Clock-to-flipflop constraint */
1137  constraint_array = &g_sdc->cf_constraints;
1138  num_constraints = ++g_sdc->num_cf_constraints;
1139  }
1140  } else {
1141  if (domain_level_to) { /* Flipflop-to-clock constraint */
1142  constraint_array = &g_sdc->fc_constraints;
1143  num_constraints = ++g_sdc->num_fc_constraints;
1144  } else { /* Flipflop-to-flipflop constraint */
1145  constraint_array = &g_sdc->ff_constraints;
1146  num_constraints = ++g_sdc->num_ff_constraints;
1147  }
1148  }
1149 
1150  *constraint_array = (t_override_constraint *) my_realloc(*constraint_array, num_constraints * sizeof(t_override_constraint));
1151 
1152  if (make_copies) {
1153  /* Copy from_list and to_list to constraint_array[num_constraints - 1].source_list and .sink_list. */
1154  (*constraint_array)[num_constraints - 1].source_list = (char **) my_malloc(num_from * sizeof(char *));
1155  (*constraint_array)[num_constraints - 1].sink_list = (char **) my_malloc(num_to * sizeof(char *));
1156  for (i = 0; i < num_from; i++) {
1157  (*constraint_array)[num_constraints - 1].source_list[i] = my_strdup(from_list[i]);
1158  }
1159  for (i = 0; i < num_to; i++) {
1160  (*constraint_array)[num_constraints - 1].sink_list[i] = my_strdup(to_list[i]);
1161  }
1162  } else {
1163  /* Just set constraint array to point to from_list and to_list. */
1164  (*constraint_array)[num_constraints - 1].source_list = from_list;
1165  (*constraint_array)[num_constraints - 1].sink_list = to_list;
1166  }
1167  (*constraint_array)[num_constraints - 1].num_source = num_from;
1168  (*constraint_array)[num_constraints - 1].num_sink = num_to;
1169  (*constraint_array)[num_constraints - 1].constraint = constraint;
1170  (*constraint_array)[num_constraints - 1].num_multicycles = num_multicycles;
1171  (*constraint_array)[num_constraints - 1].file_line_number = file_line_number; /* global var */
1172 }
1173 
1174 static float calculate_constraint(t_sdc_clock source_domain, t_sdc_clock sink_domain) {
1175  /* Given information from the SDC file about the period and offset of two clocks, *
1176  * determine the implied setup-time constraint between them via edge counting. */
1177 
1178  int source_period, sink_period, source_rising_edge, sink_rising_edge, lcm_period, num_source_edges, num_sink_edges,
1179  * source_edges, * sink_edges, i, j, time, constraint_as_int;
1180  float constraint;
1181 
1182  /* If the source and sink domains have the same period and edges, the constraint is just the common clock period. */
1183  if (fabs(source_domain.period - sink_domain.period) < EPSILON &&
1184  fabs(source_domain.rising_edge - sink_domain.rising_edge) < EPSILON &&
1185  fabs(source_domain.falling_edge - sink_domain.falling_edge) < EPSILON) {
1186  return source_domain.period; /* or, equivalently, sink_domain.period */
1187  }
1188 
1189  /* If either period is 0, the constraint is 0. */
1190  if (source_domain.period < EPSILON || sink_domain.period < EPSILON) {
1191  return 0.;
1192  }
1193 
1194  /* Multiply periods and edges by 1000 and round down *
1195  * to the nearest integer, to avoid messy decimals. */
1196 
1197  source_period = static_cast<int>(source_domain.period * 1000);
1198  sink_period = static_cast<int>(sink_domain.period * 1000);
1199  source_rising_edge = static_cast<int>(source_domain.rising_edge * 1000);
1200  sink_rising_edge = static_cast<int>(sink_domain.rising_edge * 1000);
1201 
1202  /* If we get here, we have to use edge counting. Find the LCM of the two periods. *
1203  * This determines how long it takes before the pattern of the two clocks starts repeating. */
1204  for (lcm_period = 1; lcm_period % source_period != 0 || lcm_period % sink_period != 0; lcm_period++)
1205  ;
1206 
1207  /* Create an array of positive edges for each clock over one LCM clock period. */
1208 
1209  num_source_edges = lcm_period/source_period + 1;
1210  num_sink_edges = lcm_period/sink_period + 1;
1211 
1212  source_edges = (int *) my_malloc((num_source_edges + 1) * sizeof(int));
1213  sink_edges = (int *) my_malloc((num_sink_edges + 1) * sizeof(int));
1214 
1215  for (i = 0, time = source_rising_edge; i < num_source_edges + 1; i++) {
1216  source_edges[i] = time;
1217  time += source_period;
1218  }
1219 
1220  for (i = 0, time = sink_rising_edge; i < num_sink_edges + 1; i++) {
1221  sink_edges[i] = time;
1222  time += sink_period;
1223  }
1224 
1225  /* Compare every edge in source_edges with every edge in sink_edges. *
1226  * The lowest STRICTLY POSITIVE difference between a sink edge and a source edge *
1227  * gives us the set-up time constraint. */
1228 
1229  constraint_as_int = INT_MAX; /* constraint starts off at +ve infinity so that everything will be less than it */
1230 
1231  for (i = 0; i < num_source_edges + 1; i++) {
1232  for (j = 0; j < num_sink_edges + 1; j++) {
1233  if (sink_edges[j] > source_edges[i]) {
1234  constraint_as_int = std::min(constraint_as_int, sink_edges[j] - source_edges[i]);
1235  }
1236  }
1237  }
1238 
1239  /* Divide by 1000 again and turn the constraint back into a float, and clean up memory. */
1240 
1241  constraint = constraint_as_int / 1000.;
1242 
1243  free(source_edges);
1244  free(sink_edges);
1245 
1246  return constraint;
1247 }
1248 
1249 static boolean regex_match (char * string, char * regular_expression) {
1250  /* Given a string and a regular expression, return TRUE if there's a match,
1251  FALSE if not. Print an error and exit if regular_expression is invalid. */
1252 
1253  const char * error;
1254 
1255  assert(string && regular_expression);
1256 
1257  /* The regex library reports a match if regular_expression is a substring of string
1258  AND not equal to string. This is not appropriate for our purposes. For example,
1259  we'd get both "clock" and "clock2" matching the regular expression "clock".
1260  We have to manually return that there's no match in this special case. */
1261  if (strstr(string, regular_expression) && strcmp(string, regular_expression) != 0)
1262  return FALSE;
1263 
1264  if (strcmp(regular_expression, "*") == 0)
1265  return TRUE; /* The regex library hangs if it is fed "*" as a regular expression. */
1266 
1267  error = slre_match((enum slre_option) 0, regular_expression, string, strlen(string));
1268 
1269  if (!error)
1270  return TRUE;
1271  else if (strcmp(error, "No match") == 0)
1272  return FALSE;
1273  else {
1274  vpr_printf(TIO_MESSAGE_ERROR, "[SDC line %d] Error matching regular expression \"%s\".\n",
1275  file_line_number, regular_expression);
1276  exit(1);
1277  }
1278 }
1279 
1281  if (!g_sdc) return;
1282 
1284  /* Should already have been freed in process_constraints() */
1285 
1292  free_matrix(g_sdc->domain_constraint, 0, g_sdc->num_constrained_clocks - 1, 0, sizeof(float));
1293  free(g_sdc);
1294  g_sdc = NULL;
1295 }
1296 
1297 void free_override_constraint(t_override_constraint *& constraint_array, int num_constraints) {
1298  int i, j;
1299 
1300  if (!constraint_array) return;
1301 
1302  for (i = 0; i < num_constraints; i++) {
1303  for (j = 0; j < constraint_array[i].num_source; j++) {
1304  free(constraint_array[i].source_list[j]);
1305  constraint_array[i].source_list[j] = NULL;
1306  }
1307  for (j = 0; j < constraint_array[i].num_sink; j++) {
1308  free(constraint_array[i].sink_list[j]);
1309  constraint_array[i].sink_list[j] = NULL;
1310  }
1311  free(constraint_array[i].source_list);
1312  free(constraint_array[i].sink_list);
1313  }
1314  free(constraint_array);
1315  constraint_array = NULL;
1316 }
1317 
1318 static void free_io_constraint(t_io *& io_array, int num_ios) {
1319  int i;
1320 
1321  for (i = 0; i < num_ios; i++) {
1322  free(io_array[i].name);
1323  free(io_array[i].clock_name);
1324  }
1325  free(io_array);
1326  io_array = NULL;
1327 }
1328 
1329 static void free_clock_constraint(t_clock *& clock_array, int num_clocks) {
1330  int i;
1331 
1332  for (i = 0; i < num_clocks; i++) {
1333  free(clock_array[i].name);
1334  }
1335  free(clock_array);
1336  clock_array = NULL;
1337 }
static void count_netlist_clocks_as_constrained_clocks(void)
Definition: read_sdc.c:370
static void free_clock_constraint(t_clock *&clock_array, int num_clocks)
Definition: read_sdc.c:1329
void ** alloc_matrix(int nrmin, int nrmax, int ncmin, int ncmax, size_t elsize)
Definition: util.c:551
struct s_sdc_clock t_sdc_clock
void free_override_constraint(t_override_constraint *&constraint_array, int num_constraints)
Definition: read_sdc.c:1297
static void count_netlist_ios_as_constrained_ios(char *clock_name, float io_delay)
Definition: read_sdc.c:402
const char * slre_match(enum slre_option options, const char *re, const char *buf, int buf_len,...)
Definition: slre.c:646
void free_matrix(void *vptr, int nrmin, int nrmax, int ncmin, size_t elsize)
Definition: util.c:573
boolean is_netlist_clock
Definition: vpr_types.h:370
char * name
Definition: read_sdc.c:70
boolean timing_analysis_enabled
void * my_calloc(size_t nelem, size_t size)
Definition: util.c:132
slre_option
Definition: slre.h:80
float falling_edge
Definition: read_sdc.c:73
static FILE * sdc
Definition: read_sdc.c:86
#define DO_NOT_ANALYSE
Definition: path_delay.h:4
float ** domain_constraint
Definition: vpr_types.h:431
char ** netlist_ios
Definition: read_sdc.c:93
#define BUFSIZE
Definition: graphics.c:184
static boolean is_number(char *ptr)
Definition: read_sdc.c:1064
Definition: util.h:12
t_sdc_clock * sdc_clocks
Definition: read_sdc.c:87
char * name
Definition: vpr_types.h:377
#define min(a, b)
Definition: graphics.c:174
static boolean regex_match(char *string, char *pattern)
Definition: read_sdc.c:1249
t_override_constraint * fc_constraints
Definition: vpr_types.h:446
int num_netlist_clocks
Definition: read_sdc.c:89
void read_sdc(t_timing_inf timing_inf)
Definition: read_sdc.c:115
static void * my_malloc(int ibytes)
Definition: graphics.c:499
t_override_constraint * cf_constraints
Definition: vpr_types.h:443
int file_line_number
Definition: vpr_types.h:380
char * name
Definition: vpr_types.h:369
static boolean get_sdc_tok(char *buf)
Definition: read_sdc.c:448
static void add_override_constraint(char **from_list, int num_from, char **to_list, int num_to, float constraint, int num_multicycles, boolean domain_level_from, boolean domain_level_to, boolean make_copies)
Definition: read_sdc.c:1115
static void use_default_timing_constraints(void)
Definition: read_sdc.c:257
static float calculate_constraint(t_sdc_clock source_domain, t_sdc_clock sink_domain)
Definition: read_sdc.c:1174
void free_sdc_related_structs(void)
Definition: read_sdc.c:1280
char * my_strtok(char *ptr, const char *tokens, FILE *fp, char *buf)
Definition: util.c:472
static int find_constrained_clock(char *ptr)
Definition: read_sdc.c:1084
float delay
Definition: vpr_types.h:379
t_clock * constrained_clocks
Definition: vpr_types.h:429
#define EPSILON
Definition: vpr_types.h:83
static void free_io_constraint(t_io *&io_array, int num_ios)
Definition: read_sdc.c:1318
static void alloc_and_load_netlist_clocks_and_ios(void)
Definition: read_sdc.c:325
enum logical_block_types type
Definition: vpr_types.h:208
float period
Definition: read_sdc.c:71
struct s_sdc_exclusive_group t_sdc_exclusive_group
static void * my_realloc(void *memblk, int ibytes)
Definition: graphics.c:512
char * clock_name
Definition: vpr_types.h:378
static int find_cc_constraint(char *source_clock_domain, char *sink_clock_domain)
Definition: read_sdc.c:1096
t_io * constrained_outputs
Definition: vpr_types.h:437
int num_logical_blocks
Definition: globals.c:17
char * my_fgets(char *buf, int max_size, FILE *fp)
Definition: util.c:412
int num_netlist_ios
Definition: read_sdc.c:92
Definition: slre.c:50
int file_line_number
Definition: util.c:15
t_override_constraint * ff_constraints
Definition: vpr_types.h:449
t_override_constraint * cc_constraints
Definition: vpr_types.h:440
float rising_edge
Definition: read_sdc.c:72
t_timing_constraints * g_sdc
Definition: read_sdc.c:65
#define SDC_TOKENS
char * my_strdup(const char *str)
Definition: util.c:101
messagelogger vpr_printf
Definition: util.c:17
#define HUGE_NEGATIVE_FLOAT
Definition: vpr_types.h:80
t_io * constrained_inputs
Definition: vpr_types.h:434
char ** netlist_clocks
Definition: read_sdc.c:90
struct s_logical_block * logical_block
Definition: globals.c:20
Definition: util.h:12