ext/bridge.c in origen_sim-0.15.0 vs ext/bridge.c in origen_sim-0.16.0

- old
+ new

@@ -8,13 +8,15 @@ #include <stdint.h> #include <stdlib.h> #include <stdio.h> #include <stdbool.h> #include <string.h> +#include <stdarg.h> #define MAX_NUMBER_PINS 2000 #define MAX_WAVE_EVENTS 50 +#define MAX_TRANSACTION_ERRORS 128 typedef struct Pin { char *name; vpiHandle data; // A handle to the driver data register vpiHandle drive; // A handle to the driver drive enable register @@ -40,42 +42,63 @@ Event events[MAX_WAVE_EVENTS]; Pin *active_pins[MAX_NUMBER_PINS]; int active_pin_count; } Wave; -static uint64_t period_in_ps; -static long repeat = 0; +// Used to record a miscompare event that is used by OrigenSim to work out the actual data +// from a failed register read transaction +typedef struct Miscompare { + char *pin_name; + unsigned long long cycle; + int expected; + int received; +} Miscompare; + +static Miscompare miscompares[MAX_TRANSACTION_ERRORS]; +static int transaction_error_count = 0; +static bool transaction_open = false; +static int match_loop_error_count = 0; +static bool match_loop_open = false; +static uint64_t period_in_simtime_units; +static unsigned long repeat = 0; static Pin pins[MAX_NUMBER_PINS]; static int number_of_pins = 0; // Allocate space for a unique wave for each pin, in reality it will be much less static Wave drive_waves[MAX_NUMBER_PINS]; static int number_of_drive_waves = 0; static Wave compare_waves[MAX_NUMBER_PINS]; static int number_of_compare_waves = 0; static int runtime_errors = 0; static int log_messages = 0; +static int error_count = 0; +static int max_errors = 100; +static unsigned long long cycle_count = 0; +static bool max_errors_exceeded = false; +static bool max_errors_exceeded_during_transaction = false; -static void bridge_set_period(char*); -static void bridge_define_pin(char*, char*, char*, char*); -static void bridge_define_wave(char*, char*, char*); -static void bridge_cycle(void); -static void bridge_drive_pin(char*, char*); -static void bridge_compare_pin(char*, char*); -static void bridge_capture_pin(char*); -static void bridge_stop_capture_pin(char*); -static void bridge_dont_care_pin(char*); -static void bridge_register_wave_events(void); -static void bridge_register_wave_event(int, int, int, uint64_t); -static void bridge_enable_drive_wave(Pin*); -static void bridge_disable_drive_wave(Pin*); -static void bridge_enable_compare_wave(Pin*); -static void bridge_disable_compare_wave(Pin*); -static void bridge_clear_waves_and_pins(void); -static bool bridge_is_drive_whole_cycle(Pin*); +static void set_period(char*); +static void define_pin(char*, char*, char*, char*); +static void define_wave(char*, char*, char*); +static void cycle(void); +static void drive_pin(char*, char*); +static void compare_pin(char*, char*); +static void capture_pin(char*); +static void stop_capture_pin(char*); +static void dont_care_pin(char*); +static void register_wave_events(void); +static void register_wave_event(int, int, int, uint64_t); +static void enable_drive_wave(Pin*); +static void disable_drive_wave(Pin*); +static void enable_compare_wave(Pin*); +static void disable_compare_wave(Pin*); +static void clear_waves_and_pins(void); +static bool is_drive_whole_cycle(Pin*); +static void origen_log(int, const char*, ...); static void end_simulation(void); +static void on_max_errors_exceeded(void); -static void bridge_define_pin(char * name, char * pin_ix, char * drive_wave_ix, char * compare_wave_ix) { +static void define_pin(char * name, char * pin_ix, char * drive_wave_ix, char * compare_wave_ix) { int index = atoi(pin_ix); Pin *pin = &pins[index]; number_of_pins += 1; (*pin).name = malloc(strlen(name) + 1); @@ -96,11 +119,11 @@ strcat(data, ".data"); (*pin).data = vpi_handle_by_name(data, NULL); free(data); if (!(*pin).data) { - vpi_printf("WARNING: Your DUT defines pin '%s', however it is not present in the testbench and will be ignored\n", (*pin).name); + origen_log(LOG_WARNING, "Your DUT defines pin '%s', however it is not present in the testbench and will be ignored", (*pin).name); (*pin).present = false; } else { (*pin).present = true; } @@ -130,11 +153,11 @@ free(driver); } -static void bridge_define_wave(char * index, char * compare, char * events) { +static void define_wave(char * index, char * compare, char * events) { int ix = atoi(index); Wave * wave; if (compare[0] == '0') { wave = &drive_waves[ix]; @@ -163,11 +186,11 @@ free(myevents); (*wave).active_pin_count = 0; } -static void bridge_register_wave_events() { +static void register_wave_events() { for (int i = 0; i < number_of_drive_waves; i++) { if (drive_waves[i].active_pin_count) { int x = 0; @@ -177,11 +200,11 @@ time = drive_waves[i].events[x].time; // TODO: May save some time by calling directly at time 0 //if (time == 0) { //} else { - bridge_register_wave_event(i, x, 0, time); + register_wave_event(i, x, 0, time); //} x++; } } } @@ -197,11 +220,11 @@ time = compare_waves[i].events[x].time; // TODO: May save some time by calling directly at time 0 //if (time == 0) { //} else { - bridge_register_wave_event(i, x, 1, time); + register_wave_event(i, x, 1, time); //} x++; } } } @@ -210,24 +233,24 @@ /// Enables the drive condition of the given pin. /// This is done by adding the pin to the wave's active pin list, if the wave has /// at least one pin in its list, the necessary callbacks will get triggered on every /// cycle to implement the required waveform. -static void bridge_enable_drive_wave(Pin * pin) { +static void enable_drive_wave(Pin * pin) { Wave *wave = &drive_waves[(*pin).drive_wave]; (*wave).active_pins[(*wave).active_pin_count] = pin; (*pin).drive_wave_pos = (*wave).active_pin_count; (*wave).active_pin_count += 1; } -static void bridge_disable_drive_wave(Pin * pin) { +static void disable_drive_wave(Pin * pin) { Wave *wave = &drive_waves[(*pin).drive_wave]; if ((*wave).active_pin_count == 0) { - vpi_printf("Wanted to disable drive on pin %i, but its drive wave has no active pins!\n", (*pin).index); + origen_log(LOG_ERROR, "Wanted to disable drive on pin %i, but its drive wave has no active pins!", (*pin).index); end_simulation(); } // If pin is last, we can clear it by just decrementing the active pin counter if ((*pin).drive_wave_pos != (*wave).active_pin_count - 1) { @@ -239,19 +262,19 @@ } (*wave).active_pin_count -= 1; } -static void bridge_enable_compare_wave(Pin * pin) { +static void enable_compare_wave(Pin * pin) { Wave *wave = &compare_waves[(*pin).compare_wave]; (*wave).active_pins[(*wave).active_pin_count] = pin; (*pin).compare_wave_pos = (*wave).active_pin_count; (*wave).active_pin_count += 1; } -static void bridge_disable_compare_wave(Pin * pin) { +static void disable_compare_wave(Pin * pin) { Wave *wave = &compare_waves[(*pin).compare_wave]; // If pin is last, we can clear it by just decrementing the active pin counter if ((*pin).compare_wave_pos != (*wave).active_pin_count - 1) { // Otherwise we can remove it by overwriting it with the current last pin in the @@ -263,29 +286,29 @@ (*wave).active_pin_count -= 1; } -static void bridge_clear_waves_and_pins() { +static void clear_waves_and_pins() { for (int i = 0; i < number_of_pins; i++) { Pin *pin = &pins[i]; free((*pin).name); } number_of_pins = 0; number_of_drive_waves = 0; number_of_compare_waves = 0; } -static void bridge_set_period(char * p_in_ps_str) { - uint64_t p = (uint64_t) strtol(p_in_ps_str, NULL, 10); - period_in_ps = p; - bridge_clear_waves_and_pins(); +static void set_period(char * p_in_simtime_units_str) { + uint64_t p = (uint64_t) strtol(p_in_simtime_units_str, NULL, 10); + period_in_simtime_units = p; + clear_waves_and_pins(); } -static bool bridge_is_drive_whole_cycle(Pin * pin) { +static bool is_drive_whole_cycle(Pin * pin) { Wave *wave = &drive_waves[(*pin).drive_wave]; // If drive wave only has one event if ((*wave).events[1].data == 'T') { // Return true if the single event specifies drive for the whole cycle @@ -296,11 +319,11 @@ } } /// Immediately drives the given pin to the given value -static void bridge_drive_pin(char * index, char * val) { +static void drive_pin(char * index, char * val) { Pin *pin = &pins[atoi(index)]; s_vpi_value v = {vpiIntVal, {0}}; if ((*pin).present) { // Apply the data value to the pin's driver @@ -314,28 +337,28 @@ // If it is already driving the wave will already be setup if ((*pin).previous_state != 1) { // If the drive is for the whole cycle, then we can enable it here // and don't need a callback - if (bridge_is_drive_whole_cycle(pin)) { + if (is_drive_whole_cycle(pin)) { v.value.integer = 1; vpi_put_value((*pin).drive, &v, NULL, vpiNoDelay); } else { - bridge_enable_drive_wave(pin); + enable_drive_wave(pin); } if ((*pin).previous_state == 2) { - bridge_disable_compare_wave(pin); + disable_compare_wave(pin); } (*pin).previous_state = 1; } } } /// Immediately sets the given pin to compare against the given value -static void bridge_compare_pin(char * index, char * val) { +static void compare_pin(char * index, char * val) { Pin *pin = &pins[atoi(index)]; s_vpi_value v = {vpiIntVal, {0}}; if ((*pin).present) { // Apply the data value to the pin's driver, don't enable compare yet, @@ -348,40 +371,40 @@ // Register it as actively comparing with it's wave // If it is already comparing the wave will already be setup if ((*pin).previous_state != 2) { - bridge_enable_compare_wave(pin); + enable_compare_wave(pin); if ((*pin).previous_state == 1) { - if (!bridge_is_drive_whole_cycle(pin)) { - bridge_disable_drive_wave(pin); + if (!is_drive_whole_cycle(pin)) { + disable_drive_wave(pin); } } (*pin).previous_state = 2; } } } /// Immediately sets the given pin to capture by registering it for compare /// but with its capture flag set -static void bridge_capture_pin(char * index) { +static void capture_pin(char * index) { Pin *pin = &pins[atoi(index)]; (*pin).capture_en = true; - bridge_compare_pin(index, "0"); + compare_pin(index, "0"); } /// Immediately sets the given pin to stop capture by clearing its capture flag -static void bridge_stop_capture_pin(char * index) { +static void stop_capture_pin(char * index) { Pin *pin = &pins[atoi(index)]; (*pin).capture_en = false; } /// Immediately sets the given pin to don't compare -static void bridge_dont_care_pin(char * index) { +static void dont_care_pin(char * index) { Pin *pin = &pins[atoi(index)]; s_vpi_value v = {vpiIntVal, {0}}; if ((*pin).present) { // Disable drive and compare on the pin's driver @@ -389,25 +412,25 @@ vpi_put_value((*pin).drive, &v, NULL, vpiNoDelay); vpi_put_value((*pin).compare, &v, NULL, vpiNoDelay); if ((*pin).previous_state != 0) { if ((*pin).previous_state == 1) { - if (!bridge_is_drive_whole_cycle(pin)) { - bridge_disable_drive_wave(pin); + if (!is_drive_whole_cycle(pin)) { + disable_drive_wave(pin); } } if ((*pin).previous_state == 2) { - bridge_disable_compare_wave(pin); + disable_compare_wave(pin); } (*pin).previous_state = 0; } } } -/// Callback handler to implement the events registered by bridge_register_wave_event -PLI_INT32 bridge_apply_wave_event_cb(p_cb_data data) { +/// Callback handler to implement the events registered by register_wave_event +PLI_INT32 apply_wave_event_cb(p_cb_data data) { s_vpi_value v = {vpiIntVal, {0}}; s_vpi_value v2 = {vpiIntVal, {0}}; int * wave_ix = (int*)(&(data->user_data[0])); int * event_ix = (int*)(&(data->user_data[sizeof(int)])); @@ -425,11 +448,11 @@ break; case 'X' : d = 0; break; default : - vpi_printf("ERROR: Unknown compare event: %c\n", (*wave).events[*event_ix].data); + origen_log(LOG_ERROR, "Unknown compare event: %c", (*wave).events[*event_ix].data); runtime_errors += 1; end_simulation(); return 1; } @@ -467,11 +490,11 @@ case 'X' : d = 0; on = 0; break; default : - vpi_printf("ERROR: Unknown drive event: %c\n", (*wave).events[*event_ix].data); + origen_log(LOG_ERROR, "Unknown drive event: %c\n", (*wave).events[*event_ix].data); runtime_errors += 1; end_simulation(); return 1; } @@ -494,11 +517,11 @@ return 0; } /// Registers a callback to apply the given wave during this cycle -static void bridge_register_wave_event(int wave_ix, int event_ix, int compare, uint64_t delay_in_ps) { +static void register_wave_event(int wave_ix, int event_ix, int compare, uint64_t delay_in_simtime_units) { s_cb_data call; s_vpi_time time; // This will get freed by the callback char * user_data = (char *) malloc((sizeof(int) * 3)); @@ -511,15 +534,15 @@ *d1 = event_ix; *d2 = compare; time.type = vpiSimTime; - time.high = (uint32_t)(delay_in_ps >> 32); - time.low = (uint32_t)(delay_in_ps); + time.high = (uint32_t)(delay_in_simtime_units >> 32); + time.low = (uint32_t)(delay_in_simtime_units); call.reason = cbAfterDelay; - call.cb_rtn = bridge_apply_wave_event_cb; + call.cb_rtn = apply_wave_event_cb; call.obj = 0; call.time = &time; call.value = 0; call.user_data = user_data; @@ -532,26 +555,51 @@ client_put("READY!\n"); return bridge_wait_for_msg(NULL); } +/// Send output to the Origen log, this will be automatically timestamped to the simulation and +/// sprintf type arguments can be supplied when calling: +/// +/// origen_log(LOG_ERROR, "Wanted to disable drive on pin %i, but its drive wave has no active pins!", (*pin).index); +/// origen_log(LOG_INFO, "Something to tell you about"); +static void origen_log(int type, const char * fmt, ...) { + s_vpi_time now; + int max_msg_len = 2048; + char msg[max_msg_len]; + va_list aptr; + + now.type = vpiSimTime; + vpi_get_time(0, &now); + + va_start(aptr, fmt); + vsprintf(msg, fmt, aptr); + va_end(aptr); + + vpi_printf("!%d![%u,%u] %s\n", type, now.high, now.low, msg); +}; + + /// Waits and responds to instructions from Origen (to set pin states). /// When Origen requests a cycle, time will be advanced and this func will be called again. PLI_INT32 bridge_wait_for_msg(p_cb_data data) { UNUSED(data); int max_msg_len = 1024; char msg[max_msg_len]; char comment[128]; int err; + int timescale; + int type; char *opcode, *arg1, *arg2, *arg3, *arg4; vpiHandle handle; s_vpi_value v; while(1) { err = client_get(max_msg_len, msg); if (err) { + // Don't send to the Origen log since Origen may not be there vpi_printf("ERROR: Failed to receive from Origen!\n"); end_simulation(); return 1; } if (runtime_errors) { @@ -567,215 +615,321 @@ char* orig_msg = calloc(strlen(msg)+1, sizeof(char)); strcpy(orig_msg, msg); opcode = strtok(msg, "^"); - switch(*opcode) { - // Define pin - // 0^pin_name^pin_index^drive_wave_ix^capture_wave_ix - // - // 0 for the wave indexes means the default, custom waves - // must therefore start from index 1 - // - // The pin index must be uniquely assigned to the pin by the caller - // and must be less than MAX_NUMBER_PINS - // - // 0^tdi^12^0^0 - case '0' : - arg1 = strtok(NULL, "^"); - arg2 = strtok(NULL, "^"); - arg3 = strtok(NULL, "^"); - arg4 = strtok(NULL, "^"); - //DEBUG("Define Pin: %s, %s, %s, %s\n", arg1, arg2, arg3, arg4); - bridge_define_pin(arg1, arg2, arg3, arg4); - break; - // Set Period - // 1^100000 - case '1' : - arg1 = strtok(NULL, "^"); - bridge_set_period(arg1); - break; - // Drive Pin - // 2^pin_index^data - // - // 2^12^0 - // 2^12^1 - case '2' : - arg1 = strtok(NULL, "^"); - arg2 = strtok(NULL, "^"); - //DEBUG("Drive Pin: %s, %s\n", arg1, arg2); - bridge_drive_pin(arg1, arg2); - break; - // Cycle - // 3^number_of_cycles - // - // 3^1 - // 3^65535 - case '3' : - arg1 = strtok(NULL, "^"); - repeat = strtol(arg1, NULL, 10); - if (repeat) { - repeat = repeat - 1; - } - bridge_cycle(); - return 0; - // Compare Pin - // 4^pin_index^data - // - // 4^14^0 - // 4^14^1 - case '4' : - arg1 = strtok(NULL, "^"); - arg2 = strtok(NULL, "^"); - bridge_compare_pin(arg1, arg2); - break; - // Don't Care Pin - // 5^pin_index - // - // 5^14 - case '5' : - arg1 = strtok(NULL, "^"); - bridge_dont_care_pin(arg1); - break; - // Define wave - // 6^wave_index^compare^events - // - // 0 for the wave indexes means the default, custom waves - // must therefore start from index 1 - // - // 0 for the compare parameter means it is a drive wave, 1 means it is - // for when the pin is in compare mode - // - // Some example events are shown below: - // - // 6^1^0^0_D_25_0_50_D_75_0 // Drive at 0ns, off at 25ns, drive at 50ns, off at 75ns - case '6' : - arg1 = strtok(NULL, "^"); - arg2 = strtok(NULL, "^"); - arg3 = strtok(NULL, "^"); - //DEBUG("Define Wave: %s, %s, %s\n", arg1, arg2, arg3); - bridge_define_wave(arg1, arg2, arg3); - break; - // Sync-up - // 7^ - case '7' : - client_put("OK!\n"); - break; - // Complete - // 8^ - case '8' : - end_simulation(); - return 0; - // Peek - // Returns the current value of the given net - // - // 9^origen.debug.errors - case '9' : - arg1 = strtok(NULL, "^"); - handle = vpi_handle_by_name(arg1, NULL); - if (handle) { - //v.format = vpiDecStrVal; // Seems important to set this before get - v.format = vpiBinStrVal; - vpi_get_value(handle, &v); - //DEBUG("%s\n", v.value.str); - sprintf(msg, "%s\n", v.value.str); - client_put(msg); - } else { - client_put("FAIL\n"); - } - break; - // Set Pattern Name - // a^atd_ramp_25mhz - case 'a' : - handle = vpi_handle_by_name(ORIGEN_SIM_TESTBENCH_CAT("debug.pattern"), NULL); - arg1 = strtok(NULL, "^"); + if (!max_errors_exceeded || (max_errors_exceeded && ( + // When max_errors_exceeded, only continue to process the following opcodes. + // These are the ones required to enable a controlled shutdown driven by the main Origen process + // and also any that return data to Origen so that the main process does not get blocked: + // Sync-up End Simulation Peek Flush Log + *opcode == '7' || *opcode == '8' || *opcode == '9' || *opcode == 'j' || *opcode == 'k' || + // Get version Get timescale Read reg trans Get cycle count + *opcode == 'i' || *opcode == 'l' || *opcode == 'n' || *opcode == 'o' + ))) { + switch(*opcode) { + // Define pin + // 0^pin_name^pin_index^drive_wave_ix^capture_wave_ix + // + // 0 for the wave indexes means the default, custom waves + // must therefore start from index 1 + // + // The pin index must be uniquely assigned to the pin by the caller + // and must be less than MAX_NUMBER_PINS + // + // 0^tdi^12^0^0 + case '0' : + arg1 = strtok(NULL, "^"); + arg2 = strtok(NULL, "^"); + arg3 = strtok(NULL, "^"); + arg4 = strtok(NULL, "^"); + //DEBUG("Define Pin: %s, %s, %s, %s\n", arg1, arg2, arg3, arg4); + define_pin(arg1, arg2, arg3, arg4); + break; + // Set Period + // 1^100000 + case '1' : + arg1 = strtok(NULL, "^"); + set_period(arg1); + break; + // Drive Pin + // 2^pin_index^data + // + // 2^12^0 + // 2^12^1 + case '2' : + arg1 = strtok(NULL, "^"); + arg2 = strtok(NULL, "^"); + //DEBUG("Drive Pin: %s, %s\n", arg1, arg2); + drive_pin(arg1, arg2); + break; + // Cycle + // 3^number_of_cycles + // + // 3^1 + // 3^65535 + case '3' : + arg1 = strtok(NULL, "^"); + repeat = strtol(arg1, NULL, 10); + if (repeat) { + repeat = repeat - 1; + } + cycle(); + return 0; + // Compare Pin + // 4^pin_index^data + // + // 4^14^0 + // 4^14^1 + case '4' : + arg1 = strtok(NULL, "^"); + arg2 = strtok(NULL, "^"); + compare_pin(arg1, arg2); + break; + // Don't Care Pin + // 5^pin_index + // + // 5^14 + case '5' : + arg1 = strtok(NULL, "^"); + dont_care_pin(arg1); + break; + // Define wave + // 6^wave_index^compare^events + // + // 0 for the wave indexes means the default, custom waves + // must therefore start from index 1 + // + // 0 for the compare parameter means it is a drive wave, 1 means it is + // for when the pin is in compare mode + // + // Some example events are shown below: + // + // 6^1^0^0_D_25_0_50_D_75_0 // Drive at 0ns, off at 25ns, drive at 50ns, off at 75ns + case '6' : + arg1 = strtok(NULL, "^"); + arg2 = strtok(NULL, "^"); + arg3 = strtok(NULL, "^"); + //DEBUG("Define Wave: %s, %s, %s\n", arg1, arg2, arg3); + define_wave(arg1, arg2, arg3); + break; + // Sync-up + // 7^ + case '7' : + client_put("OK!\n"); + break; + // Complete + // 8^ + case '8' : + end_simulation(); + return 0; + // Peek + // Returns the current value of the given net + // + // 9^origen.debug.errors + case '9' : + arg1 = strtok(NULL, "^"); + handle = vpi_handle_by_name(arg1, NULL); + if (handle) { + //v.format = vpiDecStrVal; // Seems important to set this before get + v.format = vpiBinStrVal; + vpi_get_value(handle, &v); + //DEBUG("%s\n", v.value.str); + sprintf(msg, "%s\n", v.value.str); + client_put(msg); + } else { + client_put("FAIL\n"); + } + break; + // Set Pattern Name + // a^atd_ramp_25mhz + case 'a' : + handle = vpi_handle_by_name(ORIGEN_SIM_TESTBENCH_CAT("debug.pattern"), NULL); + arg1 = strtok(NULL, "^"); - v.format = vpiStringVal; - v.value.str = arg1; - vpi_put_value(handle, &v, NULL, vpiNoDelay); - break; - // Poke - // Sets the given value on the given net, the number should be - // given as a decimal string - // - // b^origen.debug.errors^15 - case 'b' : - arg1 = strtok(NULL, "^"); - arg2 = strtok(NULL, "^"); - handle = vpi_handle_by_name(arg1, NULL); - if (handle) { - v.format = vpiDecStrVal; - v.value.str = arg2; + v.format = vpiStringVal; + v.value.str = arg1; vpi_put_value(handle, &v, NULL, vpiNoDelay); - } - break; - // Set Comment - // c^0^Some comment about the pattern - case 'c' : - arg1 = strtok(NULL, "^"); - arg2 = strtok(NULL, "^"); + break; + // Poke + // Sets the given value on the given net, the number should be + // given as a decimal string + // + // b^origen.debug.errors^15 + case 'b' : + arg1 = strtok(NULL, "^"); + arg2 = strtok(NULL, "^"); + handle = vpi_handle_by_name(arg1, NULL); + if (handle) { + v.format = vpiDecStrVal; + v.value.str = arg2; + vpi_put_value(handle, &v, NULL, vpiNoDelay); + } + break; + // Set Comment + // c^0^Some comment about the pattern + case 'c' : + arg1 = strtok(NULL, "^"); + arg2 = strtok(NULL, "^"); - strcpy(comment, ORIGEN_SIM_TESTBENCH_CAT("debug.comments")); - strcat(comment, arg1); + strcpy(comment, ORIGEN_SIM_TESTBENCH_CAT("debug.comments")); + strcat(comment, arg1); - handle = vpi_handle_by_name(comment, NULL); + handle = vpi_handle_by_name(comment, NULL); - v.format = vpiStringVal; - v.value.str = arg2; - vpi_put_value(handle, &v, NULL, vpiNoDelay); - break; - // Log all messages - // d^1 Turn logging on - // d^0 Turn logging off - case 'd' : - arg1 = strtok(NULL, "^"); - log_messages = atoi(arg1); - break; - // Capture Pin - // e^pin_index - // - // e^14 - case 'e' : - arg1 = strtok(NULL, "^"); - bridge_capture_pin(arg1); - break; - // Sync enable - case 'f' : - handle = vpi_handle_by_name(ORIGEN_SIM_TESTBENCH_CAT("pins.sync"), NULL); - v.format = vpiDecStrVal; - v.value.str = "1"; - vpi_put_value(handle, &v, NULL, vpiNoDelay); - break; - // Sync disable - case 'g' : - handle = vpi_handle_by_name(ORIGEN_SIM_TESTBENCH_CAT("pins.sync"), NULL); - v.format = vpiDecStrVal; - v.value.str = "0"; - vpi_put_value(handle, &v, NULL, vpiNoDelay); - break; - // Stop Capture Pin - // h^pin_index - // - // h^14 - case 'h' : - arg1 = strtok(NULL, "^"); - bridge_stop_capture_pin(arg1); - break; - // Get version, returns the version of OrigenSim the DUT object was compiled with - // i^ - case 'i' : - client_put(ORIGEN_SIM_VERSION"\n"); - break; - // Flush - case 'j' : - vpi_flush(); - break; - default : - vpi_printf("ERROR: Illegal message received from Origen: %s\n", orig_msg); - runtime_errors += 1; - end_simulation(); - return 1; + v.format = vpiStringVal; + v.value.str = arg2; + vpi_put_value(handle, &v, NULL, vpiNoDelay); + break; + // Log all messages + // d^1 Turn logging on + // d^0 Turn logging off + case 'd' : + arg1 = strtok(NULL, "^"); + log_messages = atoi(arg1); + break; + // Capture Pin + // e^pin_index + // + // e^14 + case 'e' : + arg1 = strtok(NULL, "^"); + capture_pin(arg1); + break; + // Sync enable + case 'f' : + handle = vpi_handle_by_name(ORIGEN_SIM_TESTBENCH_CAT("pins.sync"), NULL); + v.format = vpiDecStrVal; + v.value.str = "1"; + vpi_put_value(handle, &v, NULL, vpiNoDelay); + break; + // Sync disable + case 'g' : + handle = vpi_handle_by_name(ORIGEN_SIM_TESTBENCH_CAT("pins.sync"), NULL); + v.format = vpiDecStrVal; + v.value.str = "0"; + vpi_put_value(handle, &v, NULL, vpiNoDelay); + break; + // Stop Capture Pin + // h^pin_index + // + // h^14 + case 'h' : + arg1 = strtok(NULL, "^"); + stop_capture_pin(arg1); + break; + // Get version, returns the version of OrigenSim the DUT object was compiled with + // i^ + case 'i' : + client_put(ORIGEN_SIM_VERSION"\n"); + break; + // Flush + case 'j' : + vpi_flush(); + break; + // Log message + // k^2^A message to output to the console/log + case 'k' : + arg1 = strtok(NULL, "^"); + arg2 = strtok(NULL, "^"); + type = atoi(arg1); + origen_log(type, arg2); + break; + // Get timescale, returns a number that maps as follows: + // -15 - fs + // -14 - 10fs + // -13 - 100fs + // -12 - ps + // -11 - 10ps + // -10 - 100ps + // -9 - ns + // -8 - 10ns + // -7 - 100ns + // -6 - us + // -5 - 10us + // -4 - 100us + // -3 - ms + // -2 - 10ms + // -1 - 100ms + // 0 - s + // 1 - 10s + // 2 - 100s + // l^ + case 'l' : + timescale = vpi_get(vpiTimeUnit, 0); + sprintf(msg, "%d\n", timescale); + client_put(msg); + break; + // Set max_errors + // m^10 + case 'm' : + arg1 = strtok(NULL, "^"); + max_errors = atoi(arg1); + break; + // Read reg transaction + // n^1 - Start transaction + // n^0 - Stop transaction + case 'n' : + arg1 = strtok(NULL, "^"); + + if (*arg1 == '1') { + transaction_error_count = 0; + transaction_open = true; + } else { + // Send Origen the error data + sprintf(msg, "%d,%d\n", transaction_error_count, MAX_TRANSACTION_ERRORS); + client_put(msg); + for (int i = 0; i < transaction_error_count; i++) { + Miscompare *m = &miscompares[i]; + + sprintf(msg, "%s,%llu,%d,%d\n", (*m).pin_name, (*m).cycle, (*m).expected, (*m).received); + client_put(msg); + + free((*m).pin_name); + } + transaction_open = false; + if (max_errors_exceeded_during_transaction) { + on_max_errors_exceeded(); + } + } + break; + // Get cycle count + // o^ + case 'o' : + sprintf(msg, "%llu\n", cycle_count); + client_put(msg); + break; + // Set cycle count + // p^100 + case 'p' : + arg1 = strtok(NULL, "^"); + cycle_count = atoll(arg1); + break; + // Match loop + // q^1 - Start match loop + // q^0 - Stop match loop + case 'q' : + arg1 = strtok(NULL, "^"); + + if (*arg1 == '1') { + match_loop_error_count = 0; + match_loop_open = true; + } else { + match_loop_open = false; + } + break; + default : + origen_log(LOG_ERROR, "Illegal message received from Origen: %s", orig_msg); + runtime_errors += 1; + end_simulation(); + return 1; + } + } else { + // Simulation has been aborted but not told to end yet by Origen + repeat = 0; + set_period("1"); + cycle(); } free(orig_msg); } } @@ -788,46 +942,141 @@ handle = vpi_handle_by_name(ORIGEN_SIM_TESTBENCH_CAT("finish"), NULL); v.format = vpiDecStrVal; v.value.str = "1"; vpi_put_value(handle, &v, NULL, vpiNoDelay); // Corner case during testing, the timeset may not have been set yet - bridge_set_period("1000"); + set_period("1"); // Do a cycle so that the simulation sees the edge on origen.finish - bridge_cycle(); + cycle(); } -PLI_INT32 bridge_cycle_cb(p_cb_data data) { +PLI_INT32 cycle_cb(p_cb_data data) { UNUSED(data); repeat = repeat - 1; - bridge_cycle(); + cycle(); return 0; } /// Registers a callback after a cycle period, the main server loop should unblock /// after calling this to allow the simulation to proceed for a cycle -static void bridge_cycle() { +static void cycle() { s_cb_data call; s_vpi_time time; time.type = vpiSimTime; - time.high = (uint32_t)(period_in_ps >> 32); - time.low = (uint32_t)(period_in_ps); + time.high = (uint32_t)(period_in_simtime_units >> 32); + time.low = (uint32_t)(period_in_simtime_units); + cycle_count++; + call.reason = cbAfterDelay; call.obj = 0; call.time = &time; call.value = 0; call.user_data = 0; //DEBUG("REPEAT: %d\n", repeat); if (repeat) { - call.cb_rtn = bridge_cycle_cb; + call.cb_rtn = cycle_cb; } else { call.cb_rtn = bridge_wait_for_msg; } vpi_free_object(vpi_register_cb(&call)); - bridge_register_wave_events(); + register_wave_events(); +} + +static void on_max_errors_exceeded() { + // This will cause the simulation to stop processing messages from Origen + max_errors_exceeded = true; + // And this let's the Origen process know that we have stopped processing + origen_log(LOG_ERROR, "!MAX_ERROR_ABORT!"); +} + +/// Called every time a miscompare event occurs, 3 args will be passed in: +/// the pin name, expected data, actual data +PLI_INT32 bridge_on_miscompare(PLI_BYTE8 * user_dat) { + char *pin_name; + int expected; + int received; + s_vpi_value val; + vpiHandle handle; + + if (match_loop_open) { + match_loop_error_count++; + + handle = vpi_handle_by_name(ORIGEN_SIM_TESTBENCH_CAT("debug.match_errors"), NULL); + val.format = vpiIntVal; + val.value.integer = match_loop_error_count; + vpi_put_value(handle, &val, NULL, vpiNoDelay); + + } else { + vpiHandle callh = vpi_handle(vpiSysTfCall, 0); + vpiHandle argv = vpi_iterate(vpiArgument, callh); + vpiHandle arg; + + arg = vpi_scan(argv); + val.format = vpiStringVal; + vpi_get_value(arg, &val); + pin_name = val.value.str; + + arg = vpi_scan(argv); + val.format = vpiIntVal; + vpi_get_value(arg, &val); + expected = val.value.integer; + + arg = vpi_scan(argv); + val.format = vpiIntVal; + vpi_get_value(arg, &val); + received = val.value.integer; + + vpi_free_object(argv); + + origen_log(LOG_ERROR, "Miscompare on pin %s, expected %d received %d", pin_name, expected, received); + + error_count++; + + handle = vpi_handle_by_name(ORIGEN_SIM_TESTBENCH_CAT("debug.errors"), NULL); + val.format = vpiIntVal; + val.value.integer = error_count; + vpi_put_value(handle, &val, NULL, vpiNoDelay); + + if (error_count > max_errors) { + // If a transaction is currently open hold off aborting until after that has completed + // to enable a proper error message to be generated for it + if (transaction_open) { + max_errors_exceeded_during_transaction = true; + } else { + on_max_errors_exceeded(); + } + } + + // Store all errors during a transaction + if (transaction_open) { + if (transaction_error_count < MAX_TRANSACTION_ERRORS) { + Miscompare *miscompare = &miscompares[transaction_error_count]; + + (*miscompare).pin_name = malloc(strlen(pin_name) + 1); + strcpy((*miscompare).pin_name, pin_name); + (*miscompare).cycle = cycle_count; + (*miscompare).expected = expected; + (*miscompare).received = received; + } + transaction_error_count++; + } + } + return 0; +} + +/// Defines which functions are callable from Verilog as system tasks +void bridge_register_system_tasks() { + s_vpi_systf_data tf_data; + + tf_data.type = vpiSysTask; + tf_data.tfname = "$bridge_on_miscompare"; + tf_data.calltf = bridge_on_miscompare; + tf_data.compiletf = 0; + vpi_register_systf(&tf_data); }