#include "vcd_lib.h" #include #include #include #include #include #include #include "buffer.h" size_t vcd_sizeof_signal_type(vcd_signal_type_t type) { switch (type) { case VCD_SIGNAL_TYPE_BOOL: return sizeof(int); case VCD_SIGNAL_TYPE_INT: return sizeof(int); case VCD_SIGNAL_TYPE_FLOAT: return sizeof(float); } } typedef struct vcd_signal { char *name; vcd_signal_type_t type; buffer_t *samples; } vcd_signal_t; vcd_signal_t *vcd_signal_alloc(const char *name, vcd_signal_type_t type, size_t initial_buffer_size) { vcd_signal_t *res = malloc_checked(sizeof *res); res->name = strdup_checked(name); res->type = type; res->samples = buffer_alloc(initial_buffer_size * vcd_sizeof_signal_type(type)); return res; } void vcd_signal_free(vcd_signal_t *signal) { assert (signal); buffer_free(signal->samples); free(signal->name); free(signal); } void vcd_add_samples(vcd_signal_t *signal, void *samples, size_t count) { assert (signal); assert (samples); buffer_write(signal->samples, samples, count * vcd_sizeof_signal_type(signal->type)); } const char *vcd_time_unit_repr(vcd_time_unit_t u) { const char *table[] = { "s", "ms", "us", "ns", "ps", "fs" }; assert (VCD_TIME_UNIT_S <= u && u <= VCD_TIME_UNIT_FS); return table[u]; } typedef struct vcd_file { char *filename; buffer_t *signals; vcd_time_unit_t time_unit; size_t time_unit_factor; } vcd_file_t; vcd_file_t *vcd_file_alloc(const char *filename, vcd_time_unit_t time_unit, size_t time_unit_factor) { assert (filename); vcd_file_t *vcd = malloc_checked(sizeof *vcd); vcd->filename = strdup_checked(filename); vcd->time_unit = time_unit; vcd->time_unit_factor = time_unit_factor; vcd->signals = buffer_alloc(10 * sizeof(vcd_signal_t)); return vcd; } void vcd_file_free(vcd_file_t *vcd) { assert (vcd); /* Free buffers. */ buffer_foreach (vcd_signal_t *, psig, vcd->signals) vcd_signal_free(*psig); buffer_free(vcd->signals); free(vcd->filename); free(vcd); } vcd_signal_t *vcd_file_lookup_signal(const vcd_file_t *vcd, const char *name) { assert (vcd); assert (name); vcd_signal_t *res = NULL; buffer_foreach (vcd_signal_t *, psig, vcd->signals) { if (strcmp((*psig)->name, name) == 0) { res = *psig; break; } } return res; } bool vcd_file_add_signal(const vcd_file_t *vcd, vcd_signal_t *signal) { assert (vcd); assert (signal); if (vcd_file_lookup_signal(vcd, signal->name)) return false; buffer_write(vcd->signals, &signal, sizeof signal); return true; } bool vcd_file_write(vcd_file_t *vcd) { FILE *f = fopen(vcd->filename, "w"); if (!f) return false; time_t current_time; time(¤t_time); fprintf(f, "$version Generated by vcd.c $end\n"); fprintf(f, "$date %s $end\n", ctime(¤t_time)); fprintf(f, "$timescale %zu %s $end\n", vcd->time_unit_factor, vcd_time_unit_repr(vcd->time_unit)); /* Dump signal declarations. */ fprintf(f, "$scope module Top $end\n"); buffer_foreach (vcd_signal_t *, psig, vcd->signals) { fprintf(f, "$var "); switch ((*psig)->type) { case VCD_SIGNAL_TYPE_BOOL: fprintf(f, "wire 1"); break; case VCD_SIGNAL_TYPE_INT: fprintf(f, "integer %zu", 8 * sizeof(int)); break; case VCD_SIGNAL_TYPE_FLOAT: fprintf(f, "real 32"); break; } fprintf(f, " %p %s $end\n", (*psig), (*psig)->name); } fprintf(f, "$upscope $end\n"); fprintf(f, "$enddefinitions\n"); /* Dump samples. */ fprintf(f, "$dumpvars\n"); /* We maintain a pointer to the current sample in each buffer. */ size_t signal_count = vcd->signals->occupancy / sizeof(vcd_signal_t *); unsigned char **psamples = calloc(signal_count, sizeof(unsigned char *)); assert (psamples); for (size_t i = 0; i < signal_count; i++) psamples[i] = ((vcd_signal_t **)vcd->signals->data)[i]->samples->data; /* We dump */ bool active = true; for (size_t step = 0; active; step++) { fprintf(f, "#%zu\n", step); active = false; for (size_t i = 0; i < signal_count; i++) { vcd_signal_t *sig = ((vcd_signal_t **)vcd->signals->data)[i]; if (psamples[i] < sig->samples->data + sig->samples->occupancy) { active = true; switch (sig->type) { case VCD_SIGNAL_TYPE_BOOL: fprintf(f, "%d%p\n", (*(int *)psamples[i] ? 1 : 0), sig); psamples[i] += sizeof(int); break; case VCD_SIGNAL_TYPE_INT: fprintf(f, "r%d %p\n", *(int *)psamples, sig); psamples[i] += sizeof(int); break; case VCD_SIGNAL_TYPE_FLOAT: fprintf(f, "r%.16g %p\n", *(float *)psamples, sig); psamples[i] += sizeof(float); break; } } } } free(psamples); fclose(f); return true; }