1 /****************************************************************************
2     Copyright (C) 1987-2015 by Jeffery P. Hansen
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License along
15     with this program; if not, write to the Free Software Foundation, Inc.,
16     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 ****************************************************************************/
18 
19 #include <stdlib.h>
20 
21 #include "tkgate.h"
22 
23 
24 /*****************************************************************************
25  *
26  * Helping function to clear all breakpoints and reclaim allocated memory.
27  * Used in conjunction with Hash_flush().
28  *
29  *****************************************************************************/
breakpoint_flush(HashElem * e,Hash * h)30 static void breakpoint_flush(HashElem *e,Hash *h)
31 {
32   SBreakPoint *b = (SBreakPoint*) HashElem_obj(e);
33   delete_SBreakPoint(b);
34 }
35 
36 /*****************************************************************************
37  *
38  * Helping function used with qsort() to sort breakpoints by their ID value.
39  *
40  *****************************************************************************/
breakpoint_compare(const void * vA,const void * vB)41 static int breakpoint_compare(const void *vA,const void *vB)
42 {
43   const SBreakPoint *A = *(SBreakPoint**) vA;
44   const SBreakPoint *B = *(SBreakPoint**) vB;
45 
46   return A->bp_id - B->bp_id;
47 }
48 
49 /*****************************************************************************
50  *
51  * Generates a breakpoint file and loads it into the simulator.
52  *
53  * Parameters:
54  *     bp			Breakpoint to be set.
55  *
56  *
57  * Creates a script file to implement the specified breakpoint and loads the
58  * breakpoint as if it were a script file.  A breakpoint can be any expression
59  * in terms of nets in the user circuit and can optionally be prepended by
60  * the 'negedge' or 'posedge' keyword.  It can also optionally have a '#' followed
61  * by a number at the end.  The [*] in the net declaration/assignment means that
62  * the bit width of the declared net will match the bitwidth of the right-hand
63  * side expression.  The is a tkgate extension to verilog intended only for use
64  * with tkgate breakpoints.
65  *
66  * Some examples of the scripts generated from various breakpoint commands.
67  *
68  * Command: $break 1 (W + E)
69  *
70  *    wire [*] BP$1 = (W + E);
71  *    always @(BP$1) $tkg$break(1,BP$1);
72  *
73  * Command: $break 2 posedge (W > E)
74  *
75  *    wire [*] BP$2 = W > E;
76  *    always @(posedge BP$2) #10 $tkg$break(2,BP$2);
77  *
78  * Command: $break 3 (W > E) #10
79  *
80  *    wire [*] BP$3 = W > E;
81  *    always @(BP$3) #10 $tkg$break(3,BP$3);
82  *
83  * Command: $break 4 #10
84  *
85  *    always #10 $tkg$break(4,1'b1);
86  *
87  *****************************************************************************/
SBreakPoint_enable(SBreakPoint * bp)88 void SBreakPoint_enable(SBreakPoint *bp)
89 {
90   char fileName[STRMAX];
91   char expression[STRMAX];
92   const char *p;
93   const char *edge;
94   char *q;
95   int delay = -1;
96   int isDelayOnly;
97   FILE *f;
98   char x;
99 
100 
101   p = bp->bp_condition;
102 
103   while (*p && isspace(*p)) p++;
104 
105   if (strncmp(p,"posedge",7) == 0 && !isalnum(p[7])) {
106     edge = "posedge";
107     p += 7;
108   } else if (strncmp(p,"negedge",7) == 0 && !isalnum(p[7])) {
109     edge = "negedge";
110     p += 7;
111   } else
112     edge = "";
113 
114   strcpy(expression,p);
115   q = strchr(expression,'#');
116   if (q) {
117     /* Make sure there is no tailing junk by trying to scan an additional character */
118     if (sscanf(q,"#%d %c",&delay,&x) == 1) {
119       *q = 0;
120     } else if (sscanf(q,"#(%d) %c",&delay,&x) == 1) {
121       *q = 0;
122     } else
123       delay = -1;
124   }
125 
126   isDelayOnly = (sscanf(expression,"%c",&x) == 0);
127 
128   /*
129    * Empty breakpoint
130    */
131   if (isDelayOnly && delay < 0.0) {
132     BrkPtTable_error(TkGate.circuit->c_breakpoints,bp->bp_id);
133     return;
134   }
135 
136 
137   getSimTempFile(fileName);
138   f = fopen(fileName,"w");
139   if (!f) {
140     message(MC_MSGLOG|MC_URGENT,"Failed to create temorary file '%s' for breakpoint.",fileName);
141     BrkPtTable_error(TkGate.circuit->c_breakpoints,bp->bp_id);
142     return;
143   }
144 
145   if (isDelayOnly) {
146     fprintf(f,"always #%d $tkg$reportbreak(%d, 1'b1);\n",delay,bp->bp_id);
147   } else {
148     fprintf(f,"wire [*] \\BP$%d = %s;\n", bp->bp_id, expression);
149     if (delay > 0)
150       fprintf(f,"always #0 @(%s \\BP$%d ) #%d $tkg$reportbreak(%d, \\BP$%d );\n",
151 	      edge, bp->bp_id, delay, bp->bp_id, bp->bp_id);
152     else
153       fprintf(f,"always #0 @(%s \\BP$%d ) $tkg$reportbreak(%d, \\BP$%d );\n",
154 	      edge, bp->bp_id, bp->bp_id, bp->bp_id);
155   }
156 
157   fclose(f);
158 
159   sendSimCmd("$script break:%d %s -delete -silent -breakpt", bp->bp_id, fileName);
160 }
161 
162 /*****************************************************************************
163  *
164  * Create a new breapoint object
165  *
166  *****************************************************************************/
new_SBreakPoint(int idx,int state,const char * condition)167 SBreakPoint *new_SBreakPoint(int idx,int state, const char *condition)
168 {
169   SBreakPoint *bp = OM_MALLOC(SBreakPoint);
170 
171   bp->bp_id = idx;
172   bp->bp_state = state;
173   bp->bp_condition = ob_strdup(condition);
174 
175   return bp;
176 }
177 
178 /*****************************************************************************
179  *
180  * Delete a breapoint object and reclaim its memory.
181  *
182  *****************************************************************************/
delete_SBreakPoint(SBreakPoint * bp)183 void delete_SBreakPoint(SBreakPoint *bp)
184 {
185   ob_free(bp->bp_condition);
186   ob_free(bp);
187 }
188 
189 
190 /*****************************************************************************
191  *
192  * Insert breakpoint with the specified id
193  *
194  *****************************************************************************/
BrkPtTable_insert(NHash * bpm,int id,const char * condition)195 int BrkPtTable_insert(NHash *bpm,int id,const char *condition)
196 {
197   SBreakPoint *bp;
198 
199   while (NHash_find(bpm,id))
200     id++;
201 
202   bp = new_SBreakPoint(id,BPS_GO,condition);
203   NHash_insert(bpm,id,bp);
204 
205   if (TkGate.circuit->simulator.active)
206     SBreakPoint_enable(bp);
207 
208   return id;
209 }
210 
211 /*****************************************************************************
212  *
213  *  Delete breakpoint at an index (not id number)
214  *
215  *****************************************************************************/
BrkPtTable_delete(NHash * bpm,int id)216 void BrkPtTable_delete(NHash *bpm,int id)
217 {
218   SBreakPoint *bp = NHash_find(bpm,id);
219   if (!bp) return;
220 
221   NHash_remove(bpm,id);
222 
223   if (TkGate.circuit->simulator.active)
224     sendSimCmd("$delscript break:%d",id);
225 }
226 
BrkPtTable_enable(NHash * bpm,int id)227 void BrkPtTable_enable(NHash *bpm,int id)
228 {
229   SBreakPoint *bp = NHash_find(bpm,id);
230   if (!bp) return;
231 
232   if (TkGate.circuit->simulator.active) {
233     if (bp->bp_state == BPS_INVALID || bp->bp_state == BPS_IGNORE) {
234       ob_touch(bp);
235       bp->bp_state = BPS_GO;
236       SBreakPoint_enable(bp);
237     }
238     DoTcl("Breakpoint::setState %d %d",bp->bp_id,bp->bp_state);
239   } else {
240     ob_touch(bp);
241     bp->bp_state = BPS_STANDBY;
242     DoTcl("Breakpoint::setState %d %d",bp->bp_id,bp->bp_state);
243   }
244 }
245 
BrkPtTable_disable(NHash * bpm,int id)246 void BrkPtTable_disable(NHash *bpm,int id)
247 {
248   SBreakPoint *bp = NHash_find(bpm,id);
249   if (!bp) return;
250 
251   if (TkGate.circuit->simulator.active) {
252     sendSimCmd("$delscript break:%d",bp->bp_id);
253   }
254 
255   ob_touch(bp);
256   bp->bp_state = BPS_IGNORE;
257   DoTcl("Breakpoint::setState %d %d",bp->bp_id,bp->bp_state);
258 }
259 
260 
261 /*****************************************************************************
262  *
263  * Send all registered breakpoints to simulator.
264  *
265  *****************************************************************************/
BrkPtTable_sendAll(NHash * bpm)266 void BrkPtTable_sendAll(NHash *bpm)
267 {
268   if (TkGate.circuit->simulator.active) {
269     HashElem *he;
270 
271     for (he = Hash_first(bpm);he;he = Hash_next(bpm,he)) {
272       SBreakPoint *bp = (SBreakPoint*)HashElem_obj(he);
273 
274       if (bp->bp_state != BPS_IGNORE) {
275 	SBreakPoint_enable(bp);
276 	bp->bp_state = BPS_GO;
277 	DoTcl("Breakpoint::setState %d %d",bp->bp_id,bp->bp_state);
278       }
279     }
280   }
281 }
282 
283 /*****************************************************************************
284  *
285  *  Show a breakpoint as having been activated.
286  *
287  *****************************************************************************/
BrkPtTable_activate(NHash * bpm,int id,const char * value)288 void BrkPtTable_activate(NHash *bpm,int id,const char *value)
289 {
290   char s_id[STRMAX];
291   SBreakPoint *bp = NHash_find(bpm,id);
292   if (!bp) return;
293 
294   ob_touch(bp);
295   bp->bp_state = BPS_STOP;
296   sprintf(s_id,"%d",id);
297   DoTclL("Breakpoint::seeBreak",s_id,value,NULL);
298 }
299 
300 /*****************************************************************************
301  *
302  * Mark a breakpoint as having an error.
303  *
304  *****************************************************************************/
BrkPtTable_error(NHash * bpm,int id)305 void BrkPtTable_error(NHash *bpm,int id)
306 {
307   SBreakPoint *bp = NHash_find(bpm,id);
308   if (!bp) return;
309 
310   ob_touch(bp);
311   bp->bp_state = BPS_INVALID;
312   DoTcl("Breakpoint::seeError %d",id);
313 }
314 
315 /*****************************************************************************
316  *
317  * Clear the 'active' mark on all breakpoints.
318  *
319  *****************************************************************************/
BrkPtTable_clearStop(NHash * bpm)320 void BrkPtTable_clearStop(NHash *bpm)
321 {
322   HashElem *he;
323 
324   for (he = Hash_first(bpm);he;he = Hash_next(bpm,he)) {
325     SBreakPoint *bp = (SBreakPoint*)HashElem_obj(he);
326 
327     if (TkGate.circuit->simulator.active) {
328       if (bp->bp_state == BPS_STOP) {
329 	ob_touch(bp);
330 	bp->bp_state = BPS_GO;
331 	DoTcl("Breakpoint::seeGo %d",bp->bp_id);
332       }
333     } else {
334       if (bp->bp_state == BPS_STOP || bp->bp_state == BPS_GO) {
335 	ob_touch(bp);
336 	bp->bp_state = BPS_STANDBY;
337 	DoTcl("Breakpoint::seeStandby %d",bp->bp_id);
338       }
339     }
340   }
341 }
342 
343 /*****************************************************************************
344  *
345  * Flush all breakpoints from C registry and from tcl spreadsheet
346  *
347  *****************************************************************************/
BrkPtTable_flush(NHash * bpm)348 void BrkPtTable_flush(NHash *bpm)
349 {
350   Hash_flush(bpm, breakpoint_flush);
351   DoTcl("Breakpoint::flush");
352 }
353 
354 /*****************************************************************************
355  *
356  * Load all registered breakpoints into the tcl spreadsheet.
357  *
358  *****************************************************************************/
BrkPtTable_loadInterface(NHash * bpm)359 void BrkPtTable_loadInterface(NHash *bpm)
360 {
361   HashElem *he;
362   SBreakPoint **bplist;
363   int n = Hash_numElems(bpm);
364   int i;
365 
366   if (n == 0) return;
367   bplist = (SBreakPoint**) malloc(sizeof(SBreakPoint*)*n);
368 
369   i = 0;
370   for (he = Hash_first(bpm);he;he = Hash_next(bpm,he), i++) {
371     SBreakPoint *bp = (SBreakPoint*)HashElem_obj(he);
372     bplist[i] = bp;
373   }
374 
375   qsort(bplist,n,sizeof(SBreakPoint*),breakpoint_compare);
376 
377   for (i = 0;i < n;i++) {
378     char id_str[STRMAX], state_str[STRMAX];
379     int state;
380 
381     sprintf(id_str,"%d",bplist[i]->bp_id);
382 
383     state = bplist[i]->bp_state;
384 
385     if (!TkGate.circuit->simulator.active) {
386       if (state == BPS_GO || state == BPS_STOP)
387 	state = BPS_STANDBY;
388     }
389 
390     sprintf(state_str,"%d",state);
391     DoTclL("Breakpoint::addEntry",id_str,state_str,bplist[i]->bp_condition,NULL);
392   }
393 
394 
395   free(bplist);
396 }
397