• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nickg / nvc / 13741295555

08 Mar 2025 07:54PM UTC coverage: 92.319% (+0.08%) from 92.236%
13741295555

push

github

nickg
Pass mir_context_t around explicitly

33 of 38 new or added lines in 3 files covered. (86.84%)

542 existing lines in 5 files now uncovered.

68074 of 73738 relevant lines covered (92.32%)

433184.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.88
/src/rt/shell.c
1
//
2
//  Copyright (C) 2011-2025  Nick Gasson
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 3 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
15
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
//
17

18
#include "util.h"
19
#include "common.h"
20
#include "diag.h"
21
#include "hash.h"
22
#include "printer.h"
23
#include "rt/assert.h"
24
#include "rt/model.h"
25
#include "rt/structs.h"
26
#include "shell.h"
27
#include "tree.h"
28
#include "type.h"
29

30
#include <assert.h>
31
#include <errno.h>
32
#include <inttypes.h>
33
#include <stdarg.h>
34
#include <stdio.h>
35
#include <stdlib.h>
36
#include <string.h>
37
#include <unistd.h>
38

39
#include <readline/readline.h>
40
#include <readline/history.h>
41

42
#undef DLLEXPORT
43
#include <tcl.h>
44

45
#define TIME_BUFSZ 32
46

47
#if TCL_MAJOR_VERSION < 9
48
typedef int Tcl_Size;
49
#endif
50

51
typedef struct {
52
   const char     *name;
53
   Tcl_ObjCmdProc *fn;
54
   const char     *help;
55
} shell_cmd_t;
56

57
typedef enum {
58
   SHELL_SIGNAL,
59
   SHELL_REGION,
60
} object_kind_t;
61

62
typedef struct {
63
   object_kind_t kind;
64
   ident_t       name;
65
   ident_t       path;
66
} shell_object_t;
67

68
typedef struct {
69
   shell_object_t  obj;
70
   rt_signal_t    *signal;
71
   print_func_t   *printer;
72
   rt_watch_t     *watch;
73
   tcl_shell_t    *owner;
74
} shell_signal_t;
75

76
typedef struct {
77
   shell_object_t  obj;
78
   rt_scope_t     *scope;
79
} shell_region_t;
80

81
typedef char *(*get_line_fn_t)(tcl_shell_t *);
82

83
typedef struct _tcl_shell {
84
   char            *prompt;
85
   Tcl_Interp      *interp;
86
   shell_cmd_t     *cmds;
87
   size_t           ncmds;
88
   size_t           cmdalloc;
89
   rt_model_t      *model;
90
   tree_t           top;
91
   rt_scope_t      *root;
92
   shell_signal_t  *signals;
93
   unsigned         nsignals;
94
   shell_region_t  *regions;
95
   unsigned         nregions;
96
   hash_t          *namemap;
97
   jit_t           *jit;
98
   int64_t          now_var;
99
   unsigned         deltas_var;
100
   printer_t       *printer;
101
   get_line_fn_t    getline;
102
   shell_handler_t  handler;
103
   bool             quit;
104
   char            *datadir;
105
} tcl_shell_t;
106

107
static __thread tcl_shell_t *rl_shell = NULL;
108

109
__attribute__((format(printf, 2, 3)))
110
static int tcl_error(tcl_shell_t *sh, const char *fmt, ...)
4✔
111
{
112
   va_list ap;
4✔
113
   va_start(ap, fmt);
4✔
114
   char *buf LOCAL = color_vasprintf(fmt, ap);
4✔
115
   va_end(ap);
4✔
116

117
   Tcl_SetObjResult(sh->interp, Tcl_NewStringObj(buf, -1));
4✔
118
   return TCL_ERROR;
4✔
119
}
120

121
static void tcl_dict_put(tcl_shell_t *sh, Tcl_Obj *dict, const char *key,
14✔
122
                         Tcl_Obj *value)
123
{
124
   Tcl_DictObjPut(sh->interp, dict, Tcl_NewStringObj(key, -1), value);
14✔
125
}
14✔
126

127
static Tcl_Obj *tcl_ident_string(ident_t ident)
29✔
128
{
129
   return Tcl_NewStringObj(istr(ident), ident_len(ident));
29✔
130
}
131

UNCOV
132
static int syntax_error(tcl_shell_t *sh, Tcl_Obj *const objv[])
×
133
{
UNCOV
134
   return tcl_error(sh, "syntax error, enter $bold$help %s$$ for usage",
×
135
                    Tcl_GetString(objv[0]));
136
}
137

138
__attribute__((format(printf, 2, 3)))
139
static void shell_printf(tcl_shell_t *sh, const char *fmt, ...)
9✔
140
{
141
   va_list ap;
9✔
142
   va_start(ap, fmt);
9✔
143

144
   if (sh->handler.stdout_write != NULL) {
9✔
145
      char *buf LOCAL = color_vasprintf(fmt, ap);
6✔
146
      (*sh->handler.stdout_write)(buf, strlen(buf), sh->handler.context);
3✔
147
   }
148
   else
149
      wrapped_vprintf(fmt, ap);
6✔
150

151
   va_end(ap);
9✔
152
}
9✔
153

154
static bool shell_has_model(tcl_shell_t *sh)
83✔
155
{
156
   if (sh->model == NULL) {
83✔
UNCOV
157
      tcl_error(sh, "no simulation loaded, elaborate first with the "
×
158
                "$bold$-e$$ command or pass the top-level design unit "
159
                "as a command line argument");
UNCOV
160
      return false;
×
161
   }
162

163
   return true;
164
}
165

166
static void shell_next_time_step(rt_model_t *m, void *user)
5✔
167
{
168
   tcl_shell_t *sh = user;
5✔
169
   assert(sh->handler.next_time_step != NULL);
5✔
170

171
   uint64_t now = model_now(m, NULL);
5✔
172
   (*sh->handler.next_time_step)(now, sh->handler.context);
5✔
173

174
   model_set_global_cb(sh->model, RT_NEXT_TIME_STEP, shell_next_time_step, sh);
5✔
175
}
5✔
176

177
static void shell_create_model(tcl_shell_t *sh)
21✔
178
{
179
   assert(sh->model == NULL);
21✔
180

181
   sh->model = model_new(sh->jit, NULL);
21✔
182
   create_scope(sh->model, sh->top, NULL);
21✔
183

184
   if (sh->handler.next_time_step != NULL)
21✔
185
      model_set_global_cb(sh->model, RT_NEXT_TIME_STEP,
3✔
186
                          shell_next_time_step, sh);
187

188
   model_reset(sh->model);
21✔
189

190
   if ((sh->root = find_scope(sh->model, tree_stmt(sh->top, 0))) == NULL)
21✔
191
      fatal_trace("cannot find root scope");
192
}
21✔
193

194
static void shell_update_now(tcl_shell_t *sh)
43✔
195
{
196
   sh->now_var = model_now(sh->model, &sh->deltas_var);
43✔
197

198
   Tcl_UpdateLinkedVar(sh->interp, "now");
43✔
199
   Tcl_UpdateLinkedVar(sh->interp, "deltas");
43✔
200
}
43✔
201

202
static bool shell_get_printer(tcl_shell_t *sh, shell_signal_t *ss)
36✔
203
{
204
   if (ss->printer == NULL)
36✔
205
      ss->printer = printer_for(sh->printer, tree_type(ss->signal->where));
18✔
206

207
   if (ss->printer == NULL) {
36✔
UNCOV
208
      tcl_error(sh, "cannot display type %s",
×
UNCOV
209
                type_pp(tree_type(ss->signal->where)));
×
UNCOV
210
      return false;
×
211
   }
212

213
   return true;
214
}
215

216
static void shell_add_cmd(tcl_shell_t *sh, const char *name, Tcl_ObjCmdProc fn,
480✔
217
                          const char *help)
218
{
219
   shell_cmd_t cmd = { name, fn, help };
480✔
220

221
   if (sh->cmdalloc == sh->ncmds) {
480✔
222
      sh->cmdalloc = MAX(sh->cmdalloc * 2, 16);
30✔
223
      sh->cmds = xrealloc_array(sh->cmds, sh->cmdalloc, sizeof(shell_cmd_t));
30✔
224
   }
225

226
   sh->cmds[sh->ncmds++] = cmd;
480✔
227

228
   Tcl_CreateObjCommand(sh->interp, name, fn, sh, NULL);
480✔
229
}
480✔
230

231
static void shell_event_cb(uint64_t now, rt_signal_t *s, rt_watch_t *w,
5✔
232
                           void *user)
233
{
234
   shell_signal_t *ss = user;
5✔
235
   shell_handler_t *h = &(ss->owner->handler);
5✔
236

237
   if (h->signal_update != NULL) {
5✔
238
      const char *enc = print_signal(ss->printer, ss->signal, PRINT_F_ENCODE);
5✔
239
      (*h->signal_update)(ss->obj.path, now, s, enc, h->context);
5✔
240
   }
241
}
5✔
242

243
static void watch_signal(shell_signal_t *ss)
5✔
244
{
245
   ss->watch = watch_new(ss->owner->model, shell_event_cb, ss,
5✔
246
                         WATCH_POSTPONED, 1);
247
   model_set_event_cb(ss->owner->model, ss->signal, ss->watch);
5✔
248
}
5✔
249

250
static void recreate_objects(tcl_shell_t *sh, rt_scope_t *scope,
8✔
251
                             shell_signal_t **sptr, shell_region_t **rptr)
252
{
253
   shell_region_t *r = (*rptr)++;
8✔
254
   assert(r->obj.name == ident_downcase(tree_ident(scope->where)));
8✔
255
   r->scope = scope;
8✔
256

257
   for (int i = 0; i < scope->signals.count; i++) {
19✔
258
      shell_signal_t *ss = (*sptr)++;
11✔
259
      ss->signal = scope->signals.items[i];
11✔
260
      assert(ss->obj.name == ident_downcase(tree_ident(ss->signal->where)));
11✔
261

262
      if (ss->watch != NULL)
11✔
263
         watch_signal(ss);
1✔
264
   }
265

266
   for (int i = 0; i < scope->aliases.count; i++) {
12✔
267
      rt_alias_t *a = scope->aliases.items[i];
4✔
268

269
      shell_signal_t *ss = (*sptr)++;
4✔
270
      assert(ss->obj.name == ident_downcase(tree_ident(a->where)));
4✔
271
      ss->signal = a->signal;
4✔
272

273
      if (ss->watch != NULL)
4✔
UNCOV
274
         watch_signal(ss);
×
275
   }
276

277
   for (int i = 0; i < scope->children.count; i++)
12✔
278
      recreate_objects(sh, scope->children.items[i], sptr, rptr);
4✔
279
}
8✔
280

281
const char *next_option(int *pos, int objc, Tcl_Obj *const objv[])
54✔
282
{
283
   if (*pos >= objc)
54✔
284
      return NULL;
285

286
   const char *opt = Tcl_GetString(objv[*pos]);
53✔
287
   if (opt[0] != '-')
53✔
288
      return NULL;
289

290
   (*pos)++;
5✔
291
   return opt;
5✔
292
}
293

294
static shell_object_t *get_object(tcl_shell_t *sh, const char *name)
40✔
295
{
296
   shell_object_t *obj = hash_get(sh->namemap, ident_new(name));
40✔
297
   if (obj == NULL)
40✔
298
      tcl_error(sh, "cannot find name '%s'", name);
1✔
299

300
   return obj;
40✔
301
}
302

303
static shell_signal_t *get_signal(tcl_shell_t *sh, const char *name)
35✔
304
{
305
   shell_object_t *obj = get_object(sh, name);
35✔
306
   if (obj == NULL)
35✔
307
      return NULL;
308
   else if (obj->kind != SHELL_SIGNAL) {
35✔
UNCOV
309
      tcl_error(sh, "'%s' is not a signal", name);
×
UNCOV
310
      return NULL;
×
311
   }
312

313
   return container_of(obj, shell_signal_t, obj);
314
}
315

316
static const char restart_help[] =
317
   "Restart the simulation";
318

319
static int shell_cmd_restart(ClientData cd, Tcl_Interp *interp,
4✔
320
                             int objc, Tcl_Obj *const objv[])
321
{
322
   tcl_shell_t *sh = cd;
4✔
323

324
   if (!shell_has_model(sh))
4✔
325
      return TCL_ERROR;
326

327
   model_free(sh->model);
4✔
328
   sh->model = NULL;
4✔
329

330
   jit_reset(sh->jit);
4✔
331

332
   clear_vhdl_assert();
4✔
333
   for (vhdl_severity_t s = SEVERITY_NOTE; s <= SEVERITY_FAILURE; s++)
20✔
334
      set_vhdl_assert_enable(s, true);
16✔
335

336
   shell_create_model(sh);
4✔
337

338
   shell_signal_t *wptr = sh->signals;
4✔
339
   shell_region_t *rptr = sh->regions;
4✔
340
   recreate_objects(sh, sh->root, &wptr, &rptr);
4✔
341
   assert(wptr == sh->signals + sh->nsignals);
4✔
342
   assert(rptr == sh->regions + sh->nregions);
4✔
343

344
   shell_update_now(sh);
4✔
345

346
   if (sh->handler.restart_sim != NULL)
4✔
347
      (*sh->handler.restart_sim)(sh->handler.context);
1✔
348

349
   return TCL_OK;
350
}
351

352
static const char run_help[] =
353
   "Start or resume the simulation";
354

355
static int shell_cmd_run(ClientData cd, Tcl_Interp *interp,
22✔
356
                         int objc, Tcl_Obj *const objv[])
357
{
358
   tcl_shell_t *sh = cd;
22✔
359
   static bool sim_running = false;
22✔
360

361
   if (!shell_has_model(sh))
22✔
362
      return TCL_ERROR;
363
   else if (sim_running)
22✔
UNCOV
364
      return tcl_error(sh, "simulation already running");
×
365

366
   uint64_t stop_time = UINT64_MAX;
22✔
367
   if (objc == 3) {
22✔
368
      Tcl_WideInt base;
5✔
369
      int error = Tcl_GetWideIntFromObj(interp, objv[1], &base);
5✔
370
      if (error != TCL_OK || base <= 0)
5✔
UNCOV
371
         return tcl_error(sh, "invalid time");
×
372

373
      const char *unit = Tcl_GetString(objv[2]);
5✔
374

375
      uint64_t mult;
5✔
376
      if      (strcmp(unit, "fs") == 0) mult = 1;
5✔
377
      else if (strcmp(unit, "ps") == 0) mult = 1000;
5✔
378
      else if (strcmp(unit, "ns") == 0) mult = 1000000;
5✔
UNCOV
379
      else if (strcmp(unit, "us") == 0) mult = 1000000000;
×
UNCOV
380
      else if (strcmp(unit, "ms") == 0) mult = 1000000000000;
×
381
      else
UNCOV
382
         return tcl_error(sh, "invalid time unit %s", unit);
×
383

384
      stop_time = model_now(sh->model, NULL) + (base * mult);
5✔
385
   }
386
   else if (objc != 1)
17✔
UNCOV
387
      return tcl_error(sh, "usage: $bold$run [time units]$$");
×
388

389

390
   sim_running = true;
22✔
391
   model_run(sh->model, stop_time);
22✔
392
   sim_running = false;
22✔
393

394
   shell_update_now(sh);
22✔
395

396
   return TCL_OK;
22✔
397
}
398

399
static const char find_help[] =
400
   "Find signals and other objects in the design\n"
401
   "\n"
402
   "Syntax:\n"
403
   "  find signals [options] <pattern>\n"
404
   "  find regions [options] <pattern>\n"
405
   "\n"
406
   "Options:\n"
407
   "  -r, -recursive\tInclude subregions in wildcard search.\n"
408
   "\n"
409
   "Examples:\n"
410
   "  find signals -r /*\tList all signals in the design\n"
411
   "  find signals /uut/x*\tAll signals in instance UUT that start with X\n"
412
   "  find regions -r *\tList all regions in the design\n";
413

414
static int shell_cmd_find(ClientData cd, Tcl_Interp *interp,
12✔
415
                          int objc, Tcl_Obj *const objv[])
416
{
417
   tcl_shell_t *sh = cd;
12✔
418

419
   enum { SIGNALS, REGIONS } what;
12✔
420
   if (!shell_has_model(sh))
12✔
421
      return TCL_ERROR;
422
   else if (objc < 3)
12✔
UNCOV
423
      goto usage;
×
424
   else if (strcmp(Tcl_GetString(objv[1]), "signals") == 0)
12✔
425
      what = SIGNALS;
UNCOV
426
   else if (strcmp(Tcl_GetString(objv[1]), "regions") == 0)
×
427
      what = REGIONS;
428
   else
UNCOV
429
      goto usage;
×
430

431
   int pos = 2;
12✔
432
   for (const char *opt; (opt = next_option(&pos, objc, objv)); ) {
12✔
UNCOV
433
      if (strcmp(opt, "-recursive") == 0 || strcmp(opt, "-r") == 0) {
×
434
         // Always recursive for now...
435
      }
436
      else
UNCOV
437
         goto usage;
×
438
   }
439

440
   const char *glob = Tcl_GetString(objv[pos]);
12✔
441
   Tcl_Obj *result = Tcl_NewListObj(0, NULL);
12✔
442

443
   switch (what) {
12✔
444
   case SIGNALS:
445
      {
446
         for (int i = 0; i < sh->nsignals; i++) {
33✔
447
            if (!ident_glob(sh->signals[i].obj.path, glob, -1))
21✔
UNCOV
448
               continue;
×
449

450
            Tcl_Obj *obj = tcl_ident_string(sh->signals[i].obj.path);
21✔
451
            Tcl_ListObjAppendElement(interp, result, obj);
21✔
452
         }
453
      }
454
      break;
455

456
   case REGIONS:
457
      {
458
         for (int i = 0; i < sh->nregions; i++) {
×
UNCOV
459
            if (!ident_glob(sh->regions[i].obj.path, glob, -1))
×
UNCOV
460
               continue;
×
461

UNCOV
462
            Tcl_Obj *obj = tcl_ident_string(sh->regions[i].obj.path);
×
UNCOV
463
            Tcl_ListObjAppendElement(interp, result, obj);
×
464
         }
465
         break;
466
      }
467
      break;
468
   }
469

470
   Tcl_SetObjResult(interp, result);
12✔
471
   return TCL_OK;
12✔
472

UNCOV
473
 usage:
×
UNCOV
474
   return syntax_error(sh, objv);
×
475
}
476

477
static const char elaborate_help[] =
478
   "Obsolete command which does nothing.\n";
479

480
static int shell_cmd_elaborate(ClientData cd, Tcl_Interp *interp,
×
481
                               int objc, Tcl_Obj *const objv[])
482
{
483
   tcl_shell_t *sh = cd;
×
484
   return tcl_error(sh, "the elaborate/vsim command has been removed, "
×
485
                    "elaborate the design first with \"nvc -e top --do ...\" "
486
                    "or \"nvc -e top -i\"");
487
}
488

489
static const char examine_help[] =
490
   "Display current value of one of more signals\n"
491
   "\n"
492
   "Syntax:\n"
493
   "  examine [options] <name>...\n"
494
   "\n"
495
   "Note \"exa\" is an alias of this command.\n"
496
   "\n"
497
   "Options:\n"
498
   "  -radix <type>\tFormat as hexadecimal, decimal, or binary.\n"
499
   "  -<radix>\tAlias of \"-radix <radix>\".\n"
500
   "\n"
501
   "Examples:\n"
502
   "  examine /uut/foo\n"
503
   "  exa -hex sig\n";
504

505
static bool parse_radix(const char *str, print_flags_t *flags)
5✔
506
{
507
   if (strcmp(str, "binary") == 0 || strcmp(str, "bin") == 0
5✔
508
       || strcmp(str, "b") == 0) {
4✔
509
      *flags &= ~PRINT_F_RADIX;
1✔
510
      *flags |= PRINT_F_BIN;
1✔
511
      return true;
1✔
512
   }
513
   else if (strcmp(str, "-hexadecimal") == 0 || strcmp(str, "hex") == 0
4✔
514
            || strcmp(str, "h") == 0) {
1✔
515
      *flags &= ~PRINT_F_RADIX;
3✔
516
      *flags |= PRINT_F_HEX;
3✔
517
      return true;
3✔
518
   }
519
   else
520
      return false;
521
}
522

523
static int shell_cmd_examine(ClientData cd, Tcl_Interp *interp,
27✔
524
                             int objc, Tcl_Obj *const objv[])
525
{
526
   tcl_shell_t *sh = cd;
27✔
527

528
   if (!shell_has_model(sh))
27✔
529
      return TCL_ERROR;
530

531
   print_flags_t flags = 0;
27✔
532
   int pos = 1;
27✔
533
   for (const char *opt; (opt = next_option(&pos, objc, objv)); ) {
31✔
534
      if (parse_radix(opt + 1, &flags))
4✔
535
         continue;
3✔
536
      else if (strcmp(opt, "-radix") == 0 && pos + 1 < objc) {
1✔
537
         const char *arg = Tcl_GetString(objv[pos++]);
1✔
538
         if (!parse_radix(arg, &flags))
1✔
UNCOV
539
            goto usage;
×
540
      }
541
      else
UNCOV
542
         goto usage;
×
543
   }
544

545
   if (pos == objc)
27✔
UNCOV
546
      goto usage;
×
547

548
   const int count = objc - pos;
27✔
549
   Tcl_Obj *single[1], **result = single;
27✔
550

551
   if (count > 1)
27✔
552
      result = xmalloc_array(count, sizeof(Tcl_Obj *));
1✔
553

554
   for (int i = 0; pos < objc; pos++, i++) {
55✔
555
      const char *name = Tcl_GetString(objv[pos]);
28✔
556
      shell_signal_t *ss = get_signal(sh, name);
28✔
557
      if (ss == NULL)
28✔
558
         return TCL_ERROR;
559

560
      if (!shell_get_printer(sh, ss))
28✔
561
         return TCL_ERROR;
562

563
      const char *str = print_signal(ss->printer, ss->signal, flags);
28✔
564
      result[i] = Tcl_NewStringObj(str, -1);
28✔
565
   }
566

567
   if (count > 1) {
27✔
568
      Tcl_Obj *list = Tcl_NewListObj(count, result);
1✔
569
      Tcl_SetObjResult(interp, list);
1✔
570
      free(result);
1✔
571
   }
572
   else
573
      Tcl_SetObjResult(interp, result[0]);
26✔
574

575
   return TCL_OK;
576

UNCOV
577
 usage:
×
UNCOV
578
   return syntax_error(sh, objv);
×
579
}
580

581
static const char describe_help[] =
582
   "Return information about an object or region\n"
583
   "\n"
584
   "Syntax:\n"
585
   "  describe <name>...\n"
586
   "\n"
587
   "Examples:\n"
588
   "  describe /uut/foo\n"
589
   "  describe /uut\n";
590

591
static int shell_cmd_describe(ClientData cd, Tcl_Interp *interp,
5✔
592
                              int objc, Tcl_Obj *const objv[])
593
{
594
   tcl_shell_t *sh = cd;
5✔
595

596
   if (!shell_has_model(sh))
5✔
597
      return TCL_ERROR;
598

599
   int pos = 1;
5✔
600
   for (const char *opt; (opt = next_option(&pos, objc, objv)); ) {
5✔
UNCOV
601
      goto usage;
×
602
   }
603

604
   if (pos == objc)
5✔
UNCOV
605
      goto usage;
×
606

607
   const int count = objc - pos;
5✔
608
   Tcl_Obj *single[1], **result = single;
5✔
609

610
   if (count > 1)
5✔
UNCOV
611
      result = xmalloc_array(count, sizeof(Tcl_Obj *));
×
612

613
   for (int i = 0; pos < objc; pos++, i++) {
9✔
614
      const char *name = Tcl_GetString(objv[pos]);
5✔
615
      shell_object_t *obj = get_object(sh, name);
5✔
616
      if (obj == NULL)
5✔
617
         return TCL_ERROR;
618

619
      Tcl_Obj *d = result[i] = Tcl_NewDictObj();
4✔
620

621
      tcl_dict_put(sh, d, "name", tcl_ident_string(obj->name));
4✔
622
      tcl_dict_put(sh, d, "path", tcl_ident_string(obj->path));
4✔
623

624
      switch (obj->kind) {
4✔
625
      case SHELL_SIGNAL:
2✔
626
         {
627
            tcl_dict_put(sh, d, "kind", Tcl_NewStringObj("signal", -1));
2✔
628

629
            shell_signal_t *ss = container_of(obj, shell_signal_t, obj);
2✔
630
            type_t type = tree_type(ss->signal->where);
2✔
631
            tcl_dict_put(sh, d, "type", Tcl_NewStringObj(type_pp(type), -1));
2✔
632
         }
633
         break;
2✔
634
      case SHELL_REGION:
2✔
635
         tcl_dict_put(sh, d, "kind", Tcl_NewStringObj("region", -1));
2✔
636
         break;
2✔
637
      }
638
   }
639

640
   if (count > 1) {
4✔
UNCOV
641
      Tcl_Obj *list = Tcl_NewListObj(count, result);
×
UNCOV
642
      Tcl_SetObjResult(interp, list);
×
UNCOV
643
      free(result);
×
644
   }
645
   else
646
      Tcl_SetObjResult(interp, result[0]);
4✔
647

648
   return TCL_OK;
649

650
 usage:
×
651
   return syntax_error(sh, objv);
×
652
}
653

654
static const char force_help[] =
655
   "Force the value of a signal\n"
656
   "\n"
657
   "Syntax:\n"
658
   "  force [<signal> <value>]\n"
659
   "\n"
660
   "Value can be either an enumeration literal ('1', true), an integer "
661
   "(42, 0), or a bit string literal (\"10111\") and must be appropriate "
662
   "for the signal type. Without arguments lists all currently forced "
663
   "signals.\n"
664
   "\n"
665
   "Examples:\n"
666
   "  force /uut/foo '1'\n"
667
   "  force /bitvec \"10011\"\n";
668

669
static int shell_cmd_force(ClientData cd, Tcl_Interp *interp,
6✔
670
                           int objc, Tcl_Obj *const objv[])
671
{
672
   tcl_shell_t *sh = cd;
6✔
673

674
   if (!shell_has_model(sh))
6✔
675
      return TCL_ERROR;
676
   else if (objc != 3 && objc != 1)
6✔
UNCOV
677
      return syntax_error(sh, objv);
×
678

679
   if (objc == 1) {
6✔
680
      for (int i = 0; i < sh->nsignals; i++) {
4✔
681
         shell_signal_t *ss = &(sh->signals[i]);
3✔
682
         if (!(ss->signal->nexus.flags & NET_F_FORCED))
3✔
UNCOV
683
            continue;
×
684

685
         if (!shell_get_printer(sh, ss))
3✔
UNCOV
686
            return TCL_ERROR;
×
687

688
         const size_t nbytes = ss->signal->shared.size;
3✔
689
         uint8_t *value LOCAL = xmalloc(nbytes);
6✔
690
         get_forcing_value(ss->signal, value);
3✔
691

692
         shell_printf(sh, "force %s %s\n", istr(ss->obj.path),
3✔
693
                      print_raw(ss->printer, value, nbytes, 0));
694
      }
695

696
      return TCL_OK;
697
   }
698

699
   const char *signame = Tcl_GetString(objv[1]);
5✔
700
   const char *valstr = Tcl_GetString(objv[2]);
5✔
701

702
   shell_signal_t *ss = get_signal(sh, signame);
5✔
703
   if (ss == NULL)
5✔
704
      return TCL_ERROR;
705

706
   type_t type = tree_type(ss->signal->where);
5✔
707

708
   parsed_value_t value;
5✔
709
   if (!parse_value(type, valstr, &value))
5✔
710
      return tcl_error(sh, "value '%s' is not valid for type %s",
1✔
711
                       valstr, type_pp(type));
712

713
   if (type_is_scalar(type))
4✔
714
      force_signal(sh->model, ss->signal, &value.integer, 0, 1);
2✔
715
   else if (type_is_character_array(type)) {
2✔
716
      const int width = signal_width(ss->signal);
2✔
717
      if (value.enums->count != width) {
2✔
718
         tcl_error(sh, "expected %d elements for signal %s but have %d", width,
1✔
719
                   signame, value.enums->count);
720
         free(value.enums);
1✔
721
         return TCL_ERROR;
1✔
722
      }
723

724
      force_signal(sh->model, ss->signal, value.enums->values, 0, width);
1✔
725
      free(value.enums);
1✔
726
   }
727
   else
UNCOV
728
      return tcl_error(sh, "cannot force signals of type %s", type_pp(type));
×
729

730
   return TCL_OK;
731
}
732

733
static const char noforce_help[] =
734
   "Stop forcing the value of signals\n"
735
   "\n"
736
   "Syntax:\n"
737
   "  noforce <signal>...\n"
738
   "  noforce *\n"
739
   "\n"
740
   "The second form stops forcing all currently forced signals.\n"
741
   "\n"
742
   "Examples:\n"
743
   "  noforce /uut/foo /baz\n";
744

745
static int shell_cmd_noforce(ClientData cd, Tcl_Interp *interp,
3✔
746
                             int objc, Tcl_Obj *const objv[])
747
{
748
   tcl_shell_t *sh = cd;
3✔
749

750
   if (!shell_has_model(sh))
3✔
751
      return TCL_ERROR;
752
   else if (objc == 1)
3✔
UNCOV
753
      return syntax_error(sh, objv);
×
754

755
   for (int i = 1; i < objc; i++) {
5✔
756
      const char *signame = Tcl_GetString(objv[i]);
3✔
757
      if (strcmp(signame, "*") == 0) {
3✔
758
         for (int i = 0; i < sh->nsignals; i++) {
4✔
759
            shell_signal_t *ss = &(sh->signals[i]);
3✔
760
            if (ss->signal->nexus.flags & NET_F_FORCED)
3✔
761
               release_signal(sh->model, ss->signal, 0,
2✔
762
                              signal_width(ss->signal));
2✔
763
         }
764
      }
765
      else {
766
         shell_signal_t *ss = get_signal(sh, signame);
2✔
767
         if (ss == NULL)
2✔
768
            return TCL_ERROR;
769

770
         if (!(ss->signal->nexus.flags & NET_F_FORCED))
2✔
771
            return tcl_error(sh, "signal %s is not forced", signame);
1✔
772

773
         release_signal(sh->model, ss->signal, 0, signal_width(ss->signal));
1✔
774
      }
775
   }
776

777
   return TCL_OK;
778
}
779

780
static const char add_help[] =
781
   "Add signals and other objects to the display\n"
782
   "\n"
783
   "Syntax:\n"
784
   "  add wave [options] <name>...\n"
785
   "\n"
786
   "Options:\n"
787
   "  -r, -recursive\tInclude subregions in wildcard search.\n"
788
   "\n"
789
   "Examples:\n"
790
   "  add wave /*\tAdd all signals to waveform\n";
791

792
static int shell_cmd_add(ClientData cd, Tcl_Interp *interp,
4✔
793
                         int objc, Tcl_Obj *const objv[])
794
{
795
   tcl_shell_t *sh = cd;
4✔
796
   char **globs LOCAL = NULL;
8✔
797

798
   if (objc < 3 || strcmp(Tcl_GetString(objv[1]), "wave") != 0)
4✔
UNCOV
799
      goto usage;
×
800
   else if (!shell_has_model(sh))
4✔
801
      return TCL_ERROR;
802

803
   int pos = 2;
4✔
804
   for (const char *opt; (opt = next_option(&pos, objc, objv)); ) {
4✔
UNCOV
805
      if (strcmp(opt, "-recursive") == 0 || strcmp(opt, "-r") == 0) {
×
806
         // Always recursive for now...
807
      }
808
      else
UNCOV
809
         goto usage;
×
810
   }
811

812
   const int nglobs = objc - pos;
4✔
813
   globs = xmalloc_array(nglobs, sizeof(char *));
4✔
814
   for (int i = 0; i < nglobs; i++)
9✔
815
      globs[i] = Tcl_GetString(objv[pos++]);
5✔
816

817
   for (int i = 0; i < sh->nsignals; i++) {
16✔
818
      shell_signal_t *ss = &(sh->signals[i]);
12✔
819

820
      bool match = false;
12✔
821
      for (int j = 0; j < nglobs; j++)
27✔
822
         match |= ident_glob(ss->obj.path, globs[j], -1);
15✔
823

824
      if (!match || !shell_get_printer(sh, ss))
12✔
825
         continue;
7✔
826

827
      if (sh->handler.add_wave != NULL) {
5✔
828
         const char *enc =
5✔
829
            print_signal(ss->printer, ss->signal, PRINT_F_ENCODE);
5✔
830
         (*sh->handler.add_wave)(ss->obj.path, enc, sh->handler.context);
5✔
831
      }
832

833
      if (ss->watch == NULL)
5✔
834
         watch_signal(ss);
4✔
835
   }
836

837
   return TCL_OK;
838

UNCOV
839
 usage:
×
UNCOV
840
   return syntax_error(sh, objv);
×
841
}
842

843
static const char quit_help[] =
844
   "Obsolete command which does nothing.\n";
845

UNCOV
846
static int shell_cmd_quit(ClientData cd, Tcl_Interp *interp,
×
847
                          int objc, Tcl_Obj *const objv[])
848
{
UNCOV
849
   tcl_shell_t *sh = cd;
×
UNCOV
850
   return tcl_error(sh, "the quit command has been removed");
×
851
}
852

853
static const char exit_help[] =
854
   "Exit the simulator and return a status code\n"
855
   "\n"
856
   "Syntax:\n"
857
   "  exit [-code <integer>]\n"
858
   "\n"
859
   "Options:\n"
860
   "  -code <integer>\tStatus code to return to shell.\n";
861

862
static int shell_cmd_exit(ClientData cd, Tcl_Interp *interp,
1✔
863
                          int objc, Tcl_Obj *const objv[])
864
{
865
   tcl_shell_t *sh = cd;
1✔
866

867
   int pos = 1, status = EXIT_SUCCESS;
1✔
868
   for (const char *opt; (opt = next_option(&pos, objc, objv)); ) {
2✔
869
      if (strcmp(opt, "-code") == 0 && pos < objc)
1✔
870
         status = atoi(Tcl_GetString(objv[pos++]));
1✔
871
      else
872
         goto usage;
×
873
   }
874

875
   if (pos != objc)
1✔
UNCOV
876
      goto usage;
×
877

878
   if (sh->handler.exit != NULL)
1✔
879
      (*sh->handler.exit)(status, sh->handler.context);
1✔
880

881
   Tcl_Exit(status);
1✔
882

UNCOV
883
 usage:
×
UNCOV
884
   return syntax_error(sh, objv);
×
885
}
886

887
static const char help_help[] =
888
   "Display list of commands or detailed help\n"
889
   "\n"
890
   "Use $bold$help <command>$$ to display detailed usage of a particular\n"
891
   "command.\n";
892

UNCOV
893
static int shell_cmd_help(ClientData cd, Tcl_Interp *interp,
×
894
                          int objc, Tcl_Obj *const objv[])
895
{
UNCOV
896
   tcl_shell_t *sh = cd;
×
897

UNCOV
898
   if (objc == 2) {
×
UNCOV
899
      const char *which = Tcl_GetString(objv[1]);
×
UNCOV
900
      for (int i = 0; i < sh->ncmds; i++) {
×
UNCOV
901
         if (strcmp(sh->cmds[i].name, which) == 0) {
×
UNCOV
902
            shell_printf(sh, "%s", sh->cmds[i].help);
×
UNCOV
903
            return TCL_OK;
×
904
         }
905
      }
906

UNCOV
907
      return tcl_error(sh, "invalid command '%s'", which);
×
908
   }
UNCOV
909
   else if (objc != 1)
×
UNCOV
910
      return tcl_error(sh, "syntax error, try $bold$help$$");
×
911

912
   shell_printf(sh, "List of supported commands:\n");
×
913

UNCOV
914
   for (shell_cmd_t *c = sh->cmds; c < sh->cmds + sh->ncmds; c++) {
×
UNCOV
915
      const int linelen = strchrnul(c->help, '\n') - c->help;
×
UNCOV
916
      shell_printf(sh, "  $bold$%-16s$$%.*s\n", c->name, linelen, c->help);
×
917
   }
918

UNCOV
919
   shell_printf(sh, "\n");
×
UNCOV
920
   shell_printf(sh, "Use $bold$help <command>$$ for detailed usage "
×
921
                "of a particular command. Standard TCL commands are "
922
                "also accepted.\n");
923

UNCOV
924
   return TCL_OK;
×
925
}
926

927
static const char copyright_help[] = "Display copyright information";
928

UNCOV
929
static int shell_cmd_copyright(ClientData cd, Tcl_Interp *interp,
×
930
                               int objc, Tcl_Obj *const objv[])
931
{
UNCOV
932
   Tcl_Channel channel = Tcl_GetStdChannel(TCL_STDOUT);
×
933

UNCOV
934
   extern char copy_string[];
×
UNCOV
935
   Tcl_WriteChars(channel, copy_string, -1);
×
936
   Tcl_WriteChars(channel, "\n", 1);
×
UNCOV
937
   Tcl_Flush(channel);
×
938

UNCOV
939
   return TCL_OK;
×
940
}
941

942
static const char echo_help[] = "Display value of arguments";
943

944
static int shell_cmd_echo(ClientData cd, Tcl_Interp *interp,
1✔
945
                          int objc, Tcl_Obj *const objv[])
946
{
947
   Tcl_Channel channel = Tcl_GetStdChannel(TCL_STDOUT);
1✔
948

949
   for (int i = 1; i < objc; i++) {
3✔
950
      if (i > 1) Tcl_WriteChars(channel, " ", 1);
2✔
951
      Tcl_WriteObj(channel, objv[i]);
2✔
952
   }
953

954
   Tcl_WriteChars(channel, "\n", 1);
1✔
955
   Tcl_Flush(channel);
1✔
956

957
   return TCL_OK;
1✔
958
}
959

UNCOV
960
static char *shell_list_generator(const char *script, const char *text,
×
961
                                  int state, int prefix)
962
{
UNCOV
963
   static Tcl_Obj *list = NULL;
×
UNCOV
964
   static int index, len;
×
UNCOV
965
   static Tcl_Size max;
×
966

UNCOV
967
   if (!state) {
×
UNCOV
968
      if (Tcl_Eval(rl_shell->interp, script) != TCL_OK)
×
969
         return NULL;
970

UNCOV
971
      list = Tcl_GetObjResult(rl_shell->interp);
×
972

UNCOV
973
      if (Tcl_ListObjLength(rl_shell->interp, list, &max) != TCL_OK)
×
974
         return NULL;
975

UNCOV
976
      index = 0;
×
UNCOV
977
      len = strlen(text);
×
978
   }
979

980
   while (index < max) {
×
UNCOV
981
      Tcl_Obj *obj;
×
UNCOV
982
      if (Tcl_ListObjIndex(rl_shell->interp, list, index++, &obj) != TCL_OK)
×
UNCOV
983
         return NULL;
×
984

UNCOV
985
      const char *str = Tcl_GetString(obj);
×
UNCOV
986
      if (strncmp(str, text + prefix, len - prefix) == 0) {
×
UNCOV
987
         if (prefix == 0)
×
UNCOV
988
            return xstrdup(str);
×
989
         else {
UNCOV
990
            assert(len >= prefix);
×
991
            const size_t complen = strlen(str);
×
992
            char *buf = xmalloc(prefix + complen + 1);
×
UNCOV
993
            memcpy(buf, text, prefix);
×
UNCOV
994
            memcpy(buf + prefix, str, complen + 1);
×
UNCOV
995
            return buf;
×
996
         }
997
      }
998
   }
999

1000
   return NULL;
1001
}
1002

UNCOV
1003
static char *shell_command_generator(const char *text, int state)
×
1004
{
UNCOV
1005
   return shell_list_generator("info commands", text, state, 0);
×
1006
}
1007

1008
static char *shell_variable_generator(const char *text, int state)
×
1009
{
1010
   return shell_list_generator("info vars", text, state, 1);
×
1011
}
1012

UNCOV
1013
static char **shell_tab_completion(const char *text, int start, int end)
×
1014
{
1015
   rl_attempted_completion_over = 0;
×
1016

1017
   if (text[0] == '$')
×
1018
      return rl_completion_matches(text, shell_variable_generator);
×
1019

1020
   // Determine if we are completing a TCL command or not
UNCOV
1021
   int pos = start - 1;
×
1022
   for (; pos >= 0 && isspace_iso88591(rl_line_buffer[pos]); pos--);
×
1023

1024
   if (pos == -1 || rl_line_buffer[pos] == '[')
×
UNCOV
1025
      return rl_completion_matches(text, shell_command_generator);
×
1026

1027
   return NULL;
1028
}
1029

UNCOV
1030
static char *shell_completing_get_line(tcl_shell_t *sh)
×
1031
{
1032
   rl_attempted_completion_function = shell_tab_completion;
×
UNCOV
1033
   rl_completer_quote_characters = "\"'";
×
UNCOV
1034
   rl_completer_word_break_characters = " \t\r\n[]{}";
×
UNCOV
1035
   rl_special_prefixes = "$";
×
UNCOV
1036
   rl_shell = sh;
×
1037

UNCOV
1038
   char *buf = readline(sh->prompt);
×
UNCOV
1039
   if ((buf != NULL) && (*buf != '\0'))
×
1040
      add_history(buf);
×
1041

1042
   rl_shell = NULL;
×
1043
   return buf;
×
1044
}
1045

1046

1047
static char *shell_raw_get_line(tcl_shell_t *sh)
69✔
1048
{
1049
   fputs(sh->prompt, stdout);
69✔
1050
   fflush(stdout);
69✔
1051

1052
   LOCAL_TEXT_BUF tb = tb_new();
138✔
1053

1054
   size_t off = 0;
69✔
1055
   for (;;) {
1,311✔
1056
      int ch = fgetc(stdin);
1,380✔
1057
      fputc(ch, stdout);
1,380✔
1058
      switch (ch) {
1,380✔
1059
      case EOF:
1060
         return (off > 0) ? tb_claim(tb) : NULL;
1061
      case '\n':
66✔
1062
         return tb_claim(tb);
66✔
1063
      default:
1,311✔
1064
         tb_append(tb, ch);
1,311✔
1065
      }
1066
   }
1067
}
1068

1069
void shell_print_banner(tcl_shell_t *sh)
3✔
1070
{
1071
   extern const char version_string[];
3✔
1072
   shell_printf(sh, "\n");
3✔
1073

1074
   if (sh->handler.stdout_write == NULL)
3✔
1075
      print_centred(version_string);
3✔
1076
   else
UNCOV
1077
      shell_printf(sh, "\t%s", version_string);
×
1078

1079
   static const char blurb[] =
3✔
1080
      "\n\nThis program comes with ABSOLUTELY NO WARRANTY. This is free "
1081
      "software, and you are welcome to redistribute it under certain "
1082
      "conditions; type $bold$copyright$$ for details.\n\n"
1083
      "Type $bold$help$$ for a list of supported commands.\n\n";
1084

1085
   shell_printf(sh, blurb);
3✔
1086
}
3✔
1087

1088
static int compare_shell_cmd(const void *a, const void *b)
1,770✔
1089
{
1090
   return strcmp(((shell_cmd_t *)a)->name, ((shell_cmd_t *)b)->name);
1,770✔
1091
}
1092

1093
tcl_shell_t *shell_new(jit_t *jit)
30✔
1094
{
1095
   tcl_shell_t *sh = xcalloc(sizeof(tcl_shell_t));
30✔
1096
#ifdef RL_VERSION_MAJOR
1097
   sh->prompt   = color_asprintf("\001$+cyan$\002%%\001$$\002 ");
30✔
1098
#else
1099
   sh->prompt   = color_asprintf("$+cyan$%%$$ ");
1100
#endif
1101
   sh->interp   = Tcl_CreateInterp();
30✔
1102
   sh->jit      = jit;
30✔
1103
   sh->printer  = printer_new();
30✔
1104

1105
   if (isatty(fileno(stdin)))
30✔
UNCOV
1106
      sh->getline = shell_completing_get_line;
×
1107
   else
1108
      sh->getline = shell_raw_get_line;
30✔
1109

1110
   if (Tcl_Init(sh->interp) != 0)
30✔
1111
      fatal("%s", Tcl_GetStringResult(sh->interp));
×
1112

1113
   Tcl_LinkVar(sh->interp, "now", (char *)&sh->now_var,
30✔
1114
               TCL_LINK_WIDE_INT | TCL_LINK_READ_ONLY);
1115
   Tcl_LinkVar(sh->interp, "deltas", (char *)&sh->deltas_var,
30✔
1116
               TCL_LINK_UINT | TCL_LINK_READ_ONLY);
1117

1118
   {
1119
      LOCAL_TEXT_BUF tb = tb_new();
30✔
1120
      get_data_dir(tb);
30✔
1121
      sh->datadir = tb_claim(tb);
30✔
1122
   }
1123
   Tcl_LinkVar(sh->interp, "nvc_dataDir", (char *)&sh->datadir,
30✔
1124
               TCL_LINK_READ_ONLY | TCL_LINK_STRING);
1125

1126
   atexit(Tcl_Finalize);
30✔
1127

1128
   Tcl_DeleteCommand(sh->interp, "exit");
30✔
1129

1130
   shell_add_cmd(sh, "help", shell_cmd_help, help_help);
30✔
1131
   shell_add_cmd(sh, "exit", shell_cmd_exit, exit_help);
30✔
1132
   shell_add_cmd(sh, "copyright", shell_cmd_copyright, copyright_help);
30✔
1133
   shell_add_cmd(sh, "find", shell_cmd_find, find_help);
30✔
1134
   shell_add_cmd(sh, "run", shell_cmd_run, run_help);
30✔
1135
   shell_add_cmd(sh, "restart", shell_cmd_restart, restart_help);
30✔
1136
   shell_add_cmd(sh, "elaborate", shell_cmd_elaborate, elaborate_help);
30✔
1137
   shell_add_cmd(sh, "vsim", shell_cmd_elaborate, elaborate_help);
30✔
1138
   shell_add_cmd(sh, "examine", shell_cmd_examine, examine_help);
30✔
1139
   shell_add_cmd(sh, "exa", shell_cmd_examine, examine_help);
30✔
1140
   shell_add_cmd(sh, "add", shell_cmd_add, add_help);
30✔
1141
   shell_add_cmd(sh, "quit", shell_cmd_quit, quit_help);
30✔
1142
   shell_add_cmd(sh, "force", shell_cmd_force, force_help);
30✔
1143
   shell_add_cmd(sh, "noforce", shell_cmd_noforce, noforce_help);
30✔
1144
   shell_add_cmd(sh, "echo", shell_cmd_echo, echo_help);
30✔
1145
   shell_add_cmd(sh, "describe", shell_cmd_describe, describe_help);
30✔
1146

1147
   qsort(sh->cmds, sh->ncmds, sizeof(shell_cmd_t), compare_shell_cmd);
30✔
1148

1149
   return sh;
30✔
1150
}
1151

1152
void shell_free(tcl_shell_t *sh)
29✔
1153
{
1154
   if (sh->model != NULL) {
29✔
1155
      model_free(sh->model);
17✔
1156
      hash_free(sh->namemap);
17✔
1157
      free(sh->signals);
17✔
1158
      free(sh->regions);
17✔
1159
   }
1160

1161
   printer_free(sh->printer);
29✔
1162
   Tcl_DeleteInterp(sh->interp);
29✔
1163

1164
   free(sh->datadir);
29✔
1165
   free(sh->prompt);
29✔
1166
   free(sh->cmds);
29✔
1167
   free(sh);
29✔
1168
}
29✔
1169

1170
bool shell_eval(tcl_shell_t *sh, const char *script, const char **result)
115✔
1171
{
1172
   const int code = Tcl_Eval(sh->interp, script);
115✔
1173

1174
   switch (code) {
114✔
1175
   case TCL_OK:
77✔
1176
      if (result != NULL)
77✔
1177
         *result = Tcl_GetStringResult(sh->interp);
77✔
1178
      return true;
1179
   case TCL_ERROR:
37✔
1180
      {
1181
         const char *info = Tcl_GetVar(sh->interp, "::errorInfo", 0);
37✔
1182
         if (info != NULL && *info != '\n')
37✔
1183
            errorf("%s", info);
37✔
1184

1185
         *result = Tcl_GetStringResult(sh->interp);
37✔
1186
         if (info == NULL && *result != NULL && **result != '\0')
37✔
1187
            errorf("%s", *result);
×
1188
      }
1189
      return false;
UNCOV
1190
   default:
×
UNCOV
1191
      warnf("Tcl_Eval returned unknown code %d", code);
×
UNCOV
1192
      return false;
×
1193
   }
1194
}
1195

1196
static void count_objects(rt_scope_t *scope, unsigned *nsignals,
23✔
1197
                          unsigned *nregions)
1198
{
1199
   *nsignals += scope->signals.count + scope->aliases.count;
23✔
1200
   *nregions += 1;
23✔
1201

1202
   for (int i = 0; i < scope->children.count;i ++)
29✔
1203
      count_objects(scope->children.items[i], nsignals, nregions);
6✔
1204
}
23✔
1205

1206
static void recurse_objects(tcl_shell_t *sh, rt_scope_t *scope,
23✔
1207
                            text_buf_t *path, shell_signal_t **sptr,
1208
                            shell_region_t **rptr)
1209
{
1210
   const int base = tb_len(path);
23✔
1211

1212
   shell_region_t *r = (*rptr)++;
23✔
1213
   r->scope = scope;
23✔
1214
   r->obj.kind = SHELL_REGION;
23✔
1215
   r->obj.name = ident_downcase(tree_ident(scope->where));
23✔
1216
   r->obj.path = ident_new(tb_get(path));
23✔
1217

1218
   hash_put(sh->namemap, r->obj.path, &(r->obj));
23✔
1219

1220
   for (int i = 0; i < scope->signals.count; i++) {
58✔
1221
      shell_signal_t *ss = (*sptr)++;
35✔
1222
      ss->signal = scope->signals.items[i];
35✔
1223
      ss->obj.kind = SHELL_SIGNAL;
35✔
1224
      ss->obj.name = ident_downcase(tree_ident(ss->signal->where));
35✔
1225
      ss->owner = sh;
35✔
1226

1227
      tb_istr(path, ss->obj.name);
35✔
1228
      ss->obj.path = ident_new(tb_get(path));
35✔
1229
      tb_trim(path, base);
35✔
1230

1231
      hash_put(sh->namemap, ss->obj.path, &(ss->obj));
35✔
1232
   }
1233

1234
   for (int i = 0; i < scope->aliases.count; i++) {
28✔
1235
      rt_alias_t *a = scope->aliases.items[i];
5✔
1236

1237
      shell_signal_t *ss = (*sptr)++;
5✔
1238
      ss->signal = a->signal;
5✔
1239
      ss->obj.kind = SHELL_SIGNAL;
5✔
1240
      ss->obj.name = ident_downcase(tree_ident(a->where));
5✔
1241
      ss->owner = sh;
5✔
1242

1243
      tb_istr(path, ss->obj.name);
5✔
1244
      ss->obj.path = ident_new(tb_get(path));
5✔
1245
      tb_trim(path, base);
5✔
1246

1247
      hash_put(sh->namemap, ss->obj.path, &(ss->obj));
5✔
1248
   }
1249

1250
   for (int i = 0; i < scope->children.count; i++) {
29✔
1251
      rt_scope_t *child = scope->children.items[i];
6✔
1252
      ident_t name = ident_downcase(tree_ident(child->where));
6✔
1253
      tb_istr(path, name);
6✔
1254
      tb_append(path, '/');
6✔
1255
      recurse_objects(sh, child, path, sptr, rptr);
6✔
1256
      tb_trim(path, base);
6✔
1257
   }
1258
}
23✔
1259

1260
void shell_reset(tcl_shell_t *sh, tree_t top)
17✔
1261
{
1262
   jit_reset(sh->jit);
17✔
1263

1264
   sh->top = top;
17✔
1265

1266
   shell_create_model(sh);
17✔
1267

1268
   sh->nsignals = sh->nregions = 0;
17✔
1269
   count_objects(sh->root, &sh->nsignals, &sh->nregions);
17✔
1270

1271
   sh->signals = xcalloc_array(sh->nsignals, sizeof(shell_signal_t));
17✔
1272
   sh->regions = xcalloc_array(sh->nregions, sizeof(shell_region_t));
17✔
1273
   sh->namemap = hash_new(1 + sh->nsignals * 2);
17✔
1274

1275
   text_buf_t *path = tb_new();
17✔
1276
   shell_signal_t *sptr = sh->signals;
17✔
1277
   shell_region_t *rptr = sh->regions;
17✔
1278
   tb_cat(path, "/");
17✔
1279
   recurse_objects(sh, sh->root, path, &sptr, &rptr);
17✔
1280
   assert(sptr == sh->signals + sh->nsignals);
17✔
1281
   assert(rptr == sh->regions + sh->nregions);
17✔
1282
   tb_free(path);
17✔
1283

1284
   shell_update_now(sh);
17✔
1285

1286
   if (sh->handler.start_sim != NULL)
17✔
1287
      (*sh->handler.start_sim)(tree_ident(top), sh->handler.context);
2✔
1288
}
17✔
1289

1290
void shell_interact(tcl_shell_t *sh)
3✔
1291
{
1292
   shell_print_banner(sh);
3✔
1293

1294
   char *line;
3✔
1295
   while (!sh->quit && (line = (*sh->getline)(sh))) {
69✔
1296
      const char *result = NULL;
66✔
1297
      if (shell_eval(sh, line, &result) && *result != '\0')
66✔
1298
         color_printf("$+black$%s$$\n", result);
6✔
1299

1300
      free(line);
66✔
1301
   }
1302
}
3✔
1303

1304
bool shell_do(tcl_shell_t *sh, const char *file)
12✔
1305
{
1306
   const int code = Tcl_EvalFile(sh->interp, file);
12✔
1307

1308
   switch (code) {
12✔
1309
   case TCL_OK:
1310
      return true;
1311
   case TCL_ERROR:
3✔
1312
      {
1313
         const char *info = Tcl_GetVar(sh->interp, "::errorInfo", 0);
3✔
1314
         if (info != NULL && *info != '\n')
3✔
1315
            errorf("%s", info);
3✔
1316
         else {
UNCOV
1317
            const char *str = Tcl_GetStringResult(sh->interp);
×
UNCOV
1318
            if (str != NULL && *str != '\0')
×
UNCOV
1319
               errorf("%s", str);
×
1320
         }
1321

1322
         return false;
1323
      }
UNCOV
1324
   default:
×
UNCOV
1325
      warnf("Tcl_Eval returned unknown code %d", code);
×
UNCOV
1326
      return false;
×
1327
   }
1328
}
1329

1330
static int shell_redirect_close(ClientData cd, Tcl_Interp *interp)
26✔
1331
{
1332
   return EINVAL;
26✔
1333
}
1334

1335
static void shell_redirect_watch(ClientData cd, int mask)
26✔
1336
{
1337
}
26✔
1338

1339
static int shell_redirect_output(ClientData cd, const char *buf, int nchars,
5✔
1340
                                 int *error)
1341
{
1342
   tcl_shell_t *sh = untag_pointer(cd, tcl_shell_t);
5✔
1343
   switch (pointer_tag(cd)) {
5✔
1344
   case 0:
2✔
1345
      (*sh->handler.stdout_write)(buf, nchars, sh->handler.context);
2✔
1346
      break;
2✔
1347
   case 1:
2✔
1348
      (*sh->handler.stderr_write)(buf, nchars, sh->handler.context);
2✔
1349
      break;
2✔
1350
   case 2:
1✔
1351
      (*sh->handler.backchannel_write)(buf, nchars, sh->handler.context);
1✔
1352
      break;
1✔
UNCOV
1353
   default:
×
1354
      fatal_trace("invalid channel number %"PRIiPTR, pointer_tag(cd));
1355
   }
1356

1357
   return nchars;
5✔
1358
}
1359

1360
static const Tcl_ChannelType redirect_funcs = {
1361
   .typeName = "redirect",
1362
   .version = TCL_CHANNEL_VERSION_5,
1363
   .closeProc = shell_redirect_close,
1364
   .watchProc = shell_redirect_watch,
1365
   .outputProc = shell_redirect_output,
1366
};
1367

1368
void shell_set_handler(tcl_shell_t *sh, const shell_handler_t *h)
12✔
1369
{
1370
   sh->handler = *h;
12✔
1371

1372
   if (h->stdout_write != NULL) {
12✔
1373
      Tcl_Channel chan = Tcl_CreateChannel(&redirect_funcs, "redirect0",
30✔
1374
                                           tag_pointer(sh, 0), TCL_WRITABLE);
10✔
1375
      Tcl_SetChannelOption(NULL, chan, "-translation", "lf");
10✔
1376
      Tcl_SetChannelOption(NULL, chan, "-buffering", "line");
10✔
1377
      Tcl_SetChannelOption(NULL, chan, "-encoding", "utf-8");
10✔
1378

1379
      Tcl_RegisterChannel(sh->interp, chan);
10✔
1380
      Tcl_SetStdChannel(chan, TCL_STDOUT);
10✔
1381
   }
1382

1383
   if (h->stderr_write != NULL) {
12✔
1384
      Tcl_Channel chan = Tcl_CreateChannel(&redirect_funcs, "redirect1",
24✔
1385
                                           tag_pointer(sh, 1), TCL_WRITABLE);
8✔
1386
      Tcl_SetChannelOption(NULL, chan, "-translation", "lf");
8✔
1387
      Tcl_SetChannelOption(NULL, chan, "-buffering", "none");
8✔
1388
      Tcl_SetChannelOption(NULL, chan, "-encoding", "utf-8");
8✔
1389

1390
      Tcl_RegisterChannel(sh->interp, chan);
8✔
1391
      Tcl_SetStdChannel(chan, TCL_STDERR);
8✔
1392
   }
1393

1394
   if (h->backchannel_write != NULL) {
12✔
1395
      Tcl_Channel chan = Tcl_CreateChannel(&redirect_funcs, "backchannel",
24✔
1396
                                           tag_pointer(sh, 2), TCL_WRITABLE);
8✔
1397
      Tcl_SetChannelOption(NULL, chan, "-translation", "lf");
8✔
1398
      Tcl_SetChannelOption(NULL, chan, "-buffering", "full");
8✔
1399
      Tcl_SetChannelOption(NULL, chan, "-encoding", "utf-8");
8✔
1400

1401
      Tcl_RegisterChannel(sh->interp, chan);
8✔
1402
   }
1403
}
12✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc