Optimize the per-signal vcd_info structure
In a typical debug simulation run, a user tracing all the signals in the simulation may wind up creating millions of these. Rework the vcd_info structure to be more compact and possibly faster to scan.
This commit is contained in:
parent
94d75e0170
commit
76cc024f67
125
vpi/sys_lxt2.c
125
vpi/sys_lxt2.c
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2003-2009 Stephen Williams (steve@icarus.com)
|
||||
* Copyright (c) 2003-2010 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
|
||||
|
|
@ -45,19 +45,73 @@ static struct lxt2_wr_trace *dump_file = NULL;
|
|||
|
||||
static void* lxt2_thread(void*arg);
|
||||
|
||||
/*
|
||||
* Manage a table of all the dump-enabled vcd item. The cells of this
|
||||
* table are allocated incrementally, but are released all at once, so
|
||||
* we can allocate the items in chunks.
|
||||
*/
|
||||
struct vcd_info {
|
||||
vpiHandle item;
|
||||
vpiHandle cb;
|
||||
struct t_vpi_time time;
|
||||
struct lxt2_wr_symbol *sym;
|
||||
struct vcd_info *next;
|
||||
struct vcd_info *dmp_next;
|
||||
int scheduled;
|
||||
};
|
||||
|
||||
struct vcd_info_chunk {
|
||||
struct vcd_info_chunk*chunk_next;
|
||||
uint16_t chunk_fill;
|
||||
struct vcd_info data[0x10000];
|
||||
};
|
||||
|
||||
struct vcd_info_chunk*info_chunk_list = 0;
|
||||
|
||||
struct vcd_info*new_vcd_info(void)
|
||||
{
|
||||
struct vcd_info_chunk*cur_chunk = info_chunk_list;
|
||||
if (cur_chunk == 0 || cur_chunk->chunk_fill == 0xffff) {
|
||||
struct vcd_info_chunk*tmp = calloc(1, sizeof(struct vcd_info_chunk));
|
||||
tmp->chunk_fill = 0;
|
||||
tmp->chunk_next = cur_chunk;
|
||||
info_chunk_list = cur_chunk = tmp;
|
||||
return info_chunk_list->data + 0;
|
||||
}
|
||||
|
||||
struct vcd_info*ptr = cur_chunk->data + cur_chunk->chunk_fill;
|
||||
cur_chunk->chunk_fill += 1;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void functor_all_vcd_info( void (*fun) (struct vcd_info*info) )
|
||||
{
|
||||
struct vcd_info_chunk*cur;
|
||||
for (cur = info_chunk_list ; cur ; cur = cur->chunk_next) {
|
||||
int idx;
|
||||
struct vcd_info*ptr;
|
||||
for (idx=0, ptr=cur->data ; idx <= cur->chunk_fill ; idx += 1, ptr += 1)
|
||||
fun (ptr);
|
||||
}
|
||||
}
|
||||
|
||||
void delete_all_vcd_info(void)
|
||||
{
|
||||
while (info_chunk_list) {
|
||||
struct vcd_info_chunk*tmp = info_chunk_list->chunk_next;
|
||||
free(info_chunk_list);
|
||||
info_chunk_list = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Use this pointer to keep a list of vcd_info items that are
|
||||
* scheduled to be dumped. Use the VCD_INFO_ENDP value as a trailer
|
||||
* pointer instead of 0 so that individual vcd_info objects can tell
|
||||
* if they are in the list already, even if they are at the end of the
|
||||
* list.
|
||||
*/
|
||||
# define VCD_INFO_ENDP ((struct vcd_info*)1)
|
||||
static struct vcd_info *vcd_dmp_list = VCD_INFO_ENDP;
|
||||
static struct t_vpi_time vcd_dmp_time;
|
||||
|
||||
static struct vcd_info *vcd_list = NULL;
|
||||
static struct vcd_info *vcd_dmp_list = NULL;
|
||||
static PLI_UINT64 vcd_cur_time = 0;
|
||||
static int dump_is_off = 0;
|
||||
static long dump_limit = 0;
|
||||
|
|
@ -207,23 +261,17 @@ __inline__ static int dump_header_pending(void)
|
|||
*/
|
||||
static void vcd_checkpoint()
|
||||
{
|
||||
struct vcd_info*cur;
|
||||
|
||||
for (cur = vcd_list ; cur ; cur = cur->next)
|
||||
show_this_item(cur);
|
||||
functor_all_vcd_info( show_this_item );
|
||||
}
|
||||
|
||||
static void vcd_checkpoint_x()
|
||||
{
|
||||
struct vcd_info*cur;
|
||||
|
||||
for (cur = vcd_list ; cur ; cur = cur->next)
|
||||
show_this_item_x(cur);
|
||||
functor_all_vcd_info( show_this_item_x );
|
||||
}
|
||||
|
||||
static PLI_INT32 variable_cb_2(p_cb_data cause)
|
||||
{
|
||||
struct vcd_info* info = vcd_dmp_list;
|
||||
assert(cause->time->type == vpiSimTime);
|
||||
PLI_UINT64 now = timerec_to_time64(cause->time);
|
||||
|
||||
if (now != vcd_cur_time) {
|
||||
|
|
@ -231,12 +279,12 @@ static PLI_INT32 variable_cb_2(p_cb_data cause)
|
|||
vcd_cur_time = now;
|
||||
}
|
||||
|
||||
do {
|
||||
show_this_item(info);
|
||||
info->scheduled = 0;
|
||||
} while ((info = info->dmp_next) != 0);
|
||||
|
||||
vcd_dmp_list = 0;
|
||||
while (vcd_dmp_list != VCD_INFO_ENDP) {
|
||||
struct vcd_info* info = vcd_dmp_list;
|
||||
show_this_item(info);
|
||||
vcd_dmp_list = info->dmp_next;
|
||||
info->dmp_next = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -249,7 +297,7 @@ static PLI_INT32 variable_cb_1(p_cb_data cause)
|
|||
if (dump_is_full) return 0;
|
||||
if (dump_is_off) return 0;
|
||||
if (dump_header_pending()) return 0;
|
||||
if (info->scheduled) return 0;
|
||||
if (info->dmp_next) return 0;
|
||||
|
||||
if ((dump_limit > 0) && (ftell(dump_file->handle) > dump_limit)) {
|
||||
dump_is_full = 1;
|
||||
|
|
@ -258,14 +306,15 @@ static PLI_INT32 variable_cb_1(p_cb_data cause)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!vcd_dmp_list) {
|
||||
if (vcd_dmp_list == VCD_INFO_ENDP) {
|
||||
vcd_dmp_time.type = vpiSuppressTime;
|
||||
cb = *cause;
|
||||
cb.time = &vcd_dmp_time;
|
||||
cb.reason = cbReadOnlySynch;
|
||||
cb.cb_rtn = variable_cb_2;
|
||||
vpi_register_cb(&cb);
|
||||
}
|
||||
|
||||
info->scheduled = 1;
|
||||
info->dmp_next = vcd_dmp_list;
|
||||
vcd_dmp_list = info;
|
||||
|
||||
|
|
@ -291,8 +340,6 @@ static PLI_INT32 dumpvars_cb(p_cb_data cause)
|
|||
|
||||
static PLI_INT32 finish_cb(p_cb_data cause)
|
||||
{
|
||||
struct vcd_info *cur, *next;
|
||||
|
||||
if (finish_status != 0) return 0;
|
||||
|
||||
finish_status = 1;
|
||||
|
|
@ -303,11 +350,7 @@ static PLI_INT32 finish_cb(p_cb_data cause)
|
|||
}
|
||||
|
||||
vcd_work_terminate();
|
||||
for (cur = vcd_list ; cur ; cur = next) {
|
||||
next = cur->next;
|
||||
free(cur);
|
||||
}
|
||||
vcd_list = 0;
|
||||
delete_all_vcd_info();
|
||||
|
||||
vcd_scope_names_delete();
|
||||
nexus_ident_delete();
|
||||
|
|
@ -610,27 +653,23 @@ static void scan_item(unsigned depth, vpiHandle item, int skip)
|
|||
|
||||
if (nexus_id) set_nexus_ident(nexus_id, ident);
|
||||
|
||||
info = malloc(sizeof(*info));
|
||||
info = new_vcd_info();
|
||||
|
||||
info->time.type = vpiSimTime;
|
||||
info->item = item;
|
||||
info->sym = lxt2_wr_symbol_add(dump_file, ident,
|
||||
0 /* array rows */,
|
||||
vpi_get(vpiLeftRange, item),
|
||||
vpi_get(vpiRightRange, item),
|
||||
LXT2_WR_SYM_F_BITS);
|
||||
info->scheduled = 0;
|
||||
info->dmp_next = 0;
|
||||
|
||||
cb.time = &info->time;
|
||||
cb.time = 0;
|
||||
cb.user_data = (char*)info;
|
||||
cb.value = NULL;
|
||||
cb.obj = item;
|
||||
cb.reason = cbValueChange;
|
||||
cb.cb_rtn = variable_cb_1;
|
||||
|
||||
info->next = vcd_list;
|
||||
vcd_list = info;
|
||||
|
||||
info->cb = vpi_register_cb(&cb);
|
||||
|
||||
} else {
|
||||
|
|
@ -651,26 +690,22 @@ static void scan_item(unsigned depth, vpiHandle item, int skip)
|
|||
ident = strdup_sh(&name_heap, tmp);
|
||||
free(tmp);
|
||||
}
|
||||
info = malloc(sizeof(*info));
|
||||
info = new_vcd_info();
|
||||
|
||||
info->time.type = vpiSimTime;
|
||||
info->item = item;
|
||||
info->sym = lxt2_wr_symbol_add(dump_file, ident,
|
||||
0 /* array rows */,
|
||||
vpi_get(vpiSize, item)-1,
|
||||
0, LXT2_WR_SYM_F_DOUBLE);
|
||||
info->scheduled = 0;
|
||||
info->dmp_next = 0;
|
||||
|
||||
cb.time = &info->time;
|
||||
cb.time = 0;
|
||||
cb.user_data = (char*)info;
|
||||
cb.value = NULL;
|
||||
cb.obj = item;
|
||||
cb.reason = cbValueChange;
|
||||
cb.cb_rtn = variable_cb_1;
|
||||
|
||||
info->next = vcd_list;
|
||||
vcd_list = info;
|
||||
|
||||
info->cb = vpi_register_cb(&cb);
|
||||
|
||||
break;
|
||||
|
|
|
|||
Loading…
Reference in New Issue