514 lines
12 KiB
C++
514 lines
12 KiB
C++
/*
|
|
* Copyright (c) 2003-2007 Stephen Williams (steve@icarus.com)
|
|
*
|
|
* This source code is free software; you can redistribute it
|
|
* and/or modify it in source code form under the terms of the GNU
|
|
* General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
|
|
*/
|
|
|
|
/*
|
|
* This file provides a simple command line debugger for the vvp
|
|
* runtime. It is a means to interact with the user running the
|
|
* simulation.
|
|
*/
|
|
|
|
# include "config.h"
|
|
|
|
# include "vpi_priv.h"
|
|
# include "vthread.h"
|
|
# include "schedule.h"
|
|
# include <stdio.h>
|
|
# include <ctype.h>
|
|
#ifdef HAVE_READLINE_READLINE_H
|
|
# include <readline/readline.h>
|
|
#endif
|
|
#ifdef HAVE_READLINE_HISTORY_H
|
|
# include <readline/history.h>
|
|
#endif
|
|
# include <string.h>
|
|
# include <stdlib.h>
|
|
#ifdef HAVE_MALLOC_H
|
|
# include <malloc.h>
|
|
#endif
|
|
|
|
struct __vpiScope*stop_current_scope = 0;
|
|
bool stop_is_finish; /* When set, $stop acts like $finish (set in main.cc). */
|
|
|
|
#ifndef USE_READLINE
|
|
static char* readline_stub(const char*prompt)
|
|
{
|
|
char buf[256];
|
|
|
|
if (prompt && prompt[0]) {
|
|
fputs(prompt, stdout);
|
|
fflush(stdout);
|
|
}
|
|
|
|
if (fgets(buf, sizeof(buf), stdin)) {
|
|
char*nl = buf + strlen(buf);
|
|
while (nl > buf && isspace(nl[-1])) {
|
|
nl -= 1;
|
|
nl[0] = 0;
|
|
}
|
|
|
|
return strdup(buf);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
#define readline(x) readline_stub(x)
|
|
#endif
|
|
|
|
|
|
static bool interact_flag = true;
|
|
|
|
static void cmd_call(unsigned argc, char*argv[])
|
|
{
|
|
struct __vpiHandle**table;
|
|
unsigned ntable;
|
|
|
|
if (stop_current_scope == 0) {
|
|
vpip_make_root_iterator(table, ntable);
|
|
|
|
} else {
|
|
table = stop_current_scope->intern;
|
|
ntable = stop_current_scope->nintern;
|
|
}
|
|
|
|
/* This is an array of vpiHandles, for passing to the created
|
|
command. */
|
|
unsigned vpi_argc = argc - 1;
|
|
vpiHandle*vpi_argv = (vpiHandle*)calloc(vpi_argc, sizeof(vpiHandle));
|
|
vpiHandle*vpi_free = (vpiHandle*)calloc(vpi_argc, sizeof(vpiHandle));
|
|
|
|
unsigned errors = 0;
|
|
|
|
for (unsigned idx = 0 ; idx < vpi_argc ; idx += 1) {
|
|
vpiHandle handle = 0;
|
|
bool add_to_free_list = false;
|
|
|
|
/* Detect the special case that the argument is the
|
|
.(dot) string. This represents the handle for the
|
|
current scope. */
|
|
if (stop_current_scope && (strcmp(argv[idx+1], ".") == 0))
|
|
handle = &stop_current_scope->base;
|
|
|
|
/* Is the argument a quoted string? */
|
|
if (handle == 0 && argv[idx+1][0] == '"') {
|
|
char*tmp = strdup(argv[idx+1]);
|
|
tmp[strlen(tmp)-1] = 0;
|
|
|
|
/* Create a temporary vpiStringConst to pass as a
|
|
handle. Make it temporary so that memory is
|
|
reclaimed after the call is completed. */
|
|
handle = vpip_make_string_const(strdup(tmp+1), false);
|
|
add_to_free_list = true;
|
|
free(tmp);
|
|
}
|
|
|
|
/* Is the argument a decimal constant? */
|
|
if (handle == 0
|
|
&& strspn(argv[idx+1],"0123456789") == strlen(argv[idx+1])) {
|
|
handle = vpip_make_dec_const(strtol(argv[idx+1],0,10));
|
|
add_to_free_list = true;
|
|
}
|
|
|
|
/* Try to find the vpiHandle within this scope that has
|
|
the name in argv[idx+2]. Look in the current scope. */
|
|
|
|
for (unsigned tmp = 0 ; (tmp < ntable)&& !handle ; tmp += 1) {
|
|
struct __vpiScope*scope;
|
|
const char*name;
|
|
|
|
switch (table[tmp]->vpi_type->type_code) {
|
|
|
|
case vpiModule:
|
|
case vpiFunction:
|
|
case vpiTask:
|
|
case vpiNamedBegin:
|
|
case vpiNamedFork:
|
|
scope = (struct __vpiScope*) table[idx];
|
|
if (strcmp(scope->name, argv[idx+1]) == 0)
|
|
handle = table[tmp];
|
|
break;
|
|
|
|
case vpiReg:
|
|
case vpiNet:
|
|
case vpiParameter:
|
|
name = vpi_get_str(vpiName,table[tmp]);
|
|
if (strcmp(argv[idx+1], name) == 0)
|
|
handle = table[tmp];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (handle == 0) {
|
|
printf("call error: I don't know how to"
|
|
" pass %s to %s\n", argv[idx+1], argv[0]);
|
|
errors += 1;
|
|
}
|
|
|
|
vpi_argv[idx] = handle;
|
|
if (add_to_free_list)
|
|
vpi_free[idx] = handle;
|
|
else
|
|
vpi_free[idx] = 0;
|
|
|
|
}
|
|
|
|
/* If there are no errors so far, then make up a call to the
|
|
vpi task and execute that call. Free the call structure
|
|
when we finish. */
|
|
if (errors == 0) {
|
|
vpiHandle call_handle = vpip_build_vpi_call(argv[0], 0, 0, 0,
|
|
vpi_argc, vpi_argv,
|
|
1, 0);
|
|
if (call_handle == 0)
|
|
goto out;
|
|
|
|
vpip_execute_vpi_call(0, call_handle);
|
|
vpi_free_object(call_handle);
|
|
}
|
|
|
|
out:
|
|
for (unsigned idx = 0 ; idx < vpi_argc ; idx += 1) {
|
|
if (vpi_free[idx])
|
|
vpi_free_object(vpi_free[idx]);
|
|
}
|
|
|
|
free(vpi_argv);
|
|
free(vpi_free);
|
|
}
|
|
|
|
static void cmd_cont(unsigned, char*[])
|
|
{
|
|
interact_flag = false;
|
|
}
|
|
|
|
static void cmd_finish(unsigned, char*[])
|
|
{
|
|
interact_flag = false;
|
|
schedule_finish(0);
|
|
}
|
|
|
|
static void cmd_help(unsigned, char*[]);
|
|
|
|
static void cmd_list(unsigned, char*[])
|
|
{
|
|
struct __vpiHandle**table;
|
|
unsigned ntable;
|
|
|
|
if (stop_current_scope == 0) {
|
|
vpip_make_root_iterator(table, ntable);
|
|
|
|
} else {
|
|
table = stop_current_scope->intern;
|
|
ntable = stop_current_scope->nintern;
|
|
}
|
|
|
|
printf("%u items in this scope:\n", ntable);
|
|
for (unsigned idx = 0 ; idx < ntable ; idx += 1) {
|
|
|
|
struct __vpiScope*scope;
|
|
struct __vpiSignal*sig;
|
|
|
|
switch (table[idx]->vpi_type->type_code) {
|
|
case vpiModule:
|
|
scope = (struct __vpiScope*) table[idx];
|
|
printf("module : %s\n", scope->name);
|
|
break;
|
|
|
|
case vpiTask:
|
|
scope = (struct __vpiScope*) table[idx];
|
|
printf("task : %s\n", scope->name);
|
|
break;
|
|
|
|
case vpiFunction:
|
|
scope = (struct __vpiScope*) table[idx];
|
|
printf("function: %s\n", scope->name);
|
|
break;
|
|
|
|
case vpiNamedBegin:
|
|
scope = (struct __vpiScope*) table[idx];
|
|
printf("block : %s\n", scope->name);
|
|
break;
|
|
|
|
case vpiNamedFork:
|
|
scope = (struct __vpiScope*) table[idx];
|
|
printf("fork : %s\n", scope->name);
|
|
break;
|
|
|
|
case vpiParameter:
|
|
printf("param : %s\n", vpi_get_str(vpiName, table[idx]));
|
|
break;
|
|
|
|
case vpiReg:
|
|
sig = (struct __vpiSignal*) table[idx];
|
|
if ((sig->msb == 0) && (sig->lsb == 0))
|
|
printf("reg : %s%s\n",
|
|
vpi_get_str(vpiName, table[idx]),
|
|
sig->signed_flag? "signed " : "");
|
|
else
|
|
printf("reg : %s%s[%d:%d]\n",
|
|
vpi_get_str(vpiName, table[idx]),
|
|
sig->signed_flag? "signed " : "",
|
|
sig->msb, sig->lsb);
|
|
break;
|
|
|
|
case vpiNet:
|
|
sig = (struct __vpiSignal*) table[idx];
|
|
if ((sig->msb == 0) && (sig->lsb == 0))
|
|
printf("net : %s%s\n",
|
|
vpi_get_str(vpiName, table[idx]),
|
|
sig->signed_flag? "signed " : "");
|
|
else
|
|
printf("net : %s%s[%d:%d]\n",
|
|
vpi_get_str(vpiName, table[idx]),
|
|
sig->signed_flag? "signed " : "",
|
|
sig->msb, sig->lsb);
|
|
break;
|
|
|
|
default:
|
|
printf("%8d: <vpi handle>\n",
|
|
table[idx]->vpi_type->type_code);
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
static void cmd_load(unsigned argc, char*argv[])
|
|
{
|
|
unsigned idx;
|
|
|
|
for (idx = 1 ; idx < argc ; idx += 1) {
|
|
printf("Loading module %s...\n", argv[idx]);
|
|
vpip_load_module(argv[idx]);
|
|
}
|
|
}
|
|
|
|
static void cmd_pop(unsigned, char*[])
|
|
{
|
|
if (stop_current_scope != 0)
|
|
stop_current_scope = stop_current_scope->scope;
|
|
}
|
|
|
|
static void cmd_push(unsigned argc, char* argv[])
|
|
{
|
|
|
|
for (unsigned idx = 1 ; idx < argc ; idx += 1) {
|
|
struct __vpiHandle**table;
|
|
unsigned ntable;
|
|
|
|
struct __vpiScope*child = 0;
|
|
|
|
if (stop_current_scope) {
|
|
table = stop_current_scope->intern;
|
|
ntable = stop_current_scope->nintern;
|
|
} else {
|
|
vpip_make_root_iterator(table, ntable);
|
|
}
|
|
|
|
child = 0;
|
|
unsigned tmp;
|
|
for (tmp = 0 ; tmp < ntable ; tmp += 1) {
|
|
if (table[tmp]->vpi_type->type_code != vpiModule)
|
|
continue;
|
|
|
|
struct __vpiScope*cp = (struct __vpiScope*) table[tmp];
|
|
|
|
/* This is a scope, and the name matches, then
|
|
report that I found the child. */
|
|
if (strcmp(cp->name, argv[idx]) == 0) {
|
|
child = cp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (child == 0) {
|
|
printf("Scope %s not found.\n", argv[idx]);
|
|
return;
|
|
}
|
|
|
|
stop_current_scope = child;
|
|
}
|
|
}
|
|
|
|
static void cmd_time(unsigned, char*[])
|
|
{
|
|
unsigned long ticks = schedule_simtime();
|
|
printf("%lu ticks\n", ticks);
|
|
}
|
|
|
|
static void cmd_where(unsigned, char*[])
|
|
{
|
|
struct __vpiScope*cur = stop_current_scope;
|
|
|
|
while (cur) {
|
|
switch (cur->base.vpi_type->type_code) {
|
|
case vpiModule:
|
|
printf("module %s\n",
|
|
cur->name);
|
|
break;
|
|
default:
|
|
printf("scope (%d) %s;\n",
|
|
cur->base.vpi_type->type_code,
|
|
cur->name);
|
|
break;
|
|
}
|
|
|
|
cur = cur->scope;
|
|
}
|
|
}
|
|
|
|
static void cmd_unknown(unsigned, char*argv[])
|
|
{
|
|
printf("Unknown command: %s\n", argv[0]);
|
|
printf("Try the help command to get a summary\n"
|
|
"of available commands.\n");
|
|
}
|
|
|
|
static struct {
|
|
const char*name;
|
|
void (*proc)(unsigned argc, char*argv[]);
|
|
const char*summary;
|
|
} cmd_table[] = {
|
|
{ "cd", &cmd_push,
|
|
"Synonym for push."},
|
|
{ "cont", &cmd_cont,
|
|
"Resume (continue) the simulation"},
|
|
{ "finish", &cmd_finish,
|
|
"Finish the simulation."},
|
|
{ "help", &cmd_help,
|
|
"Get help."},
|
|
{ "list", &cmd_list,
|
|
"List items in the current scope."},
|
|
{ "load", &cmd_load,
|
|
"Load a VPI module, a la vvp -m."},
|
|
{ "ls", &cmd_list,
|
|
"Shorthand for \"list\"."},
|
|
{ "pop", &cmd_pop,
|
|
"Pop one scope from the scope stack."},
|
|
{ "push", &cmd_push,
|
|
"Descend into the named scope."},
|
|
{ "time", &cmd_time,
|
|
"Print the current simulation time."},
|
|
{ "where", &cmd_where,
|
|
"Show current scope, and scope hierarchy stack."},
|
|
{ 0, &cmd_unknown, 0}
|
|
};
|
|
|
|
static void cmd_help(unsigned argc, char*argv[])
|
|
{
|
|
printf("Commands can be from the following table of base commands,\n"
|
|
"or can be invocations of system tasks/functions.\n\n");
|
|
for (unsigned idx = 0 ; cmd_table[idx].name != 0 ; idx += 1) {
|
|
printf("%-8s - %s\n", cmd_table[idx].name, cmd_table[idx].summary);
|
|
}
|
|
|
|
printf("\nIf the command name starts with a '$' character, it\n"
|
|
"is taken to be the name of a system task, and a call is\n"
|
|
"built up and executed.\n");
|
|
}
|
|
|
|
|
|
static void invoke_command(char*txt)
|
|
{
|
|
unsigned argc = 0;
|
|
char**argv = new char*[strlen(txt)/2];
|
|
|
|
/* Chop the line into words. */
|
|
for (char*cp = txt+strspn(txt, " ")
|
|
; *cp; cp += strspn(cp, " ")) {
|
|
argv[argc] = cp;
|
|
|
|
if (cp[0] == '"') {
|
|
char*tmp = strchr(cp+1, '"');
|
|
if (tmp == 0) {
|
|
printf("Missing close-quote: %s\n", cp);
|
|
delete[]argv;
|
|
return;
|
|
}
|
|
|
|
cp = tmp + 1;
|
|
|
|
} else {
|
|
cp += strcspn(cp, " ");
|
|
}
|
|
|
|
if (*cp) *cp++ = 0;
|
|
|
|
argc += 1;
|
|
}
|
|
|
|
|
|
/* Look up the command, using argv[0] as the key. */
|
|
if (argc > 0) {
|
|
|
|
if (argv[0][0] == '$') {
|
|
cmd_call(argc, argv);
|
|
|
|
} else {
|
|
unsigned idx;
|
|
for (idx = 0 ; cmd_table[idx].name ; idx += 1)
|
|
if (strcmp(cmd_table[idx].name, argv[0]) == 0)
|
|
break;
|
|
|
|
cmd_table[idx].proc (argc, argv);
|
|
}
|
|
|
|
}
|
|
|
|
delete[]argv;
|
|
}
|
|
|
|
void stop_handler(int rc)
|
|
{
|
|
/* The user may be running in a non-interactive environment, so
|
|
* they want $stop and <Control-C> to be the same as $finish. */
|
|
if (stop_is_finish) {
|
|
schedule_finish(0);
|
|
return;
|
|
}
|
|
|
|
vpi_mcd_printf(1,"** VVP Stop(%d) **\n", rc);
|
|
vpi_mcd_printf(1,"** Current simulation time is %" TIME_FMT "u ticks.\n",
|
|
schedule_simtime());
|
|
|
|
interact_flag = true;
|
|
while (interact_flag) {
|
|
char*input = readline("> ");
|
|
if (input == 0)
|
|
break;
|
|
|
|
|
|
/* Advance to the first input character. */
|
|
char*first = input;
|
|
while (*first && isspace(*first))
|
|
first += 1;
|
|
|
|
if (first[0] != 0) {
|
|
#ifdef HAVE_READLINE_HISTORY_H
|
|
add_history(first);
|
|
#endif
|
|
invoke_command(first);
|
|
}
|
|
|
|
free(input);
|
|
}
|
|
|
|
vpi_mcd_printf(1,"** Continue **\n");
|
|
}
|
|
|