// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved // ================================================================ // These are functions imported into BSV during Bluesim or Verilog simulation. // See C_Imports.bsv for the corresponding 'import BDPI' declarations. // There are several independent groups of functions below; the // groups are separated by heavy dividers ('// *******') // Below, 'dummy' args are not used, and are present only to appease // some Verilog simulators that are finicky about 0-arg functions. // ================================================================ // Includes from C library // General #include #include #include #include #include #include #include #include // For comms polling #include #include #include // For TCP #include // socket definitions #include // socket types #include // inet (3) funtions #include // To set non-blocking mode // ================================================================ // Includes for this project #include "C_Imported_Functions.h" // **************************************************************** // **************************************************************** // **************************************************************** // Functions for console I/O // ================================================================ // c_trygetchar() // Returns next input character (ASCII code) from the console. // Returns 0 if no input is available. uint8_t c_trygetchar (uint8_t dummy) { uint8_t ch; ssize_t n; struct pollfd x_pollfd; const int fd_stdin = 0; // ---------------- // Poll for input x_pollfd.fd = fd_stdin; x_pollfd.events = POLLRDNORM; x_pollfd.revents = 0; poll (& x_pollfd, 1, 1); // printf ("INFO: c_trygetchar: Polling for input\n"); if ((x_pollfd.revents & POLLRDNORM) == 0) { return 0; } // ---------------- // Input is available n = read (fd_stdin, & ch, 1); if (n == 1) { return ch; } else { if (n == 0) printf ("c_trygetchar: end of file\n"); return 0xFF; } } // ================================================================ // A small 'main' to test c_trygetchar() #ifdef TEST_TRYGETCHAR char message[] = "Hello World!\n"; int main (int argc, char *argv []) { uint8_t ch; int j; for (j = 0; j < strlen (message); j++) c_putchar (message[j]); printf ("Polling for input\n"); j = 0; while (1) { ch = c_trygetchar (); if (ch == 0xFF) break; if (ch != 0) printf ("Received character %0d 0x%0x '%c'\n", ch, ch, ch); else { printf ("\r%0d ", j); fflush (stdout); j++; sleep (1); } } return 0; } #endif // ================================================================ // c_putchar() // Writes character to stdout uint32_t c_putchar (uint8_t ch) { int status; uint32_t success = 0; if ((ch == 0) || (ch > 0x7F)) { // Discard non-printables success = 1; } else { if ((ch == '\n') || (' ' <= ch)) { status = fprintf (stdout, "%c", ch); if (status > 0) success = 1; } else { status = fprintf (stdout, "[\\%0d]", ch); if (status > 0) success = 1; } if (success == 1) { status = fflush (stdout); if (status != 0) success = 0; } } return success; } // **************************************************************** // **************************************************************** // **************************************************************** // Functions for reading in various parameters // ================================================================ // c_get_symbol_val () // Returns the value of a symbol (a memory address) from a symbol-table file. // The symbol-table file has a ' ' pair on each line. // Reads the whole symbol-table file on each call, // which is ok if it's not called often and the file is small. static char symbol_table_filename [] = "symbol_table.txt"; uint64_t c_get_symbol_val (char * symbol) { bool ok = false; uint64_t val = 0; FILE *fp = fopen (symbol_table_filename, "r"); if (fp == NULL) { fprintf (stderr, "c_get_symbol: could not open file: %s\n", symbol_table_filename); } else { int len = strlen (symbol); int linenum = 0; while (true) { char linebuf [1024], *p; p = fgets (linebuf, 1024, fp); if (p == NULL) { // EOF fprintf (stderr, "c_get_symbol: could not find value for symbol %s\n", symbol); break; } else { linenum++; int cmp = strncmp (symbol, linebuf, len); if (cmp != 0) // This symbol is not on this line continue; else { int n = sscanf (& (linebuf [len]), "%" SCNx64, & val); if ((n == EOF) || (n < 1)) { fprintf (stderr, "c_get_symbol: could not find value for symbol %s\n", symbol); fprintf (stderr, " on file '%s' line %d\n", symbol_table_filename, linenum); } else ok = true; break; } } } } return val; } // ---------------------------------------------------------------- // A small 'main' to test c_get_symbol_val() #ifdef TEST_GET_SYMBOL_VAL int main (int argc, char *argv []) { uint64_t x = c_get_symbol_val ("tohost"); fprintf (stdout, "tohost value is 0x%" PRIx64 "\n", x); } #endif // ================================================================ // Misc. parameters from a parameters file, if it exists. // Each non-empty line of the file should contain // param-name param-value comment // or # comment /* #define MAX_PARAMS 128 char sim_params_filename[] = "sim_params.txt"; static bool params_loaded = false; static uint64_t param_vals [MAX_PARAMS]; #define PARAM_LINEBUF_SIZE 1024 static void load_params (void) { for (uint32_t j = 0; j < MAX_PARAMS; j++) param_vals [j] = 0; FILE *fp = fopen ("sim_params.txt", "r"); if (fp == NULL) { fprintf (stdout, "INFO: no simulation parameters file: %s\n", sim_params_filename); return; } else { fprintf (stdout, "INFO: loading simulation parameters from file: %s\n", sim_params_filename); } while (true) { char linebuf [PARAM_LINEBUF_SIZE], *p; p = fgets (linebuf, PARAM_LINEBUF_SIZE, fp); if (p == NULL) // EOF break; // Skip leading blanks and tabs int index = 0; while ((index < PARAM_LINEBUF_SIZE) && ((linebuf [index] == ' ') || (linebuf [index] == '\t'))) index++; if (index == PARAM_LINEBUF_SIZE) break; if (linebuf [index] == '#') // comment line continue; // Read the parameter code uint32_t param; int n = sscanf (& linebuf [index], "%" SCNd32 "%n", & param, & index); if ((n == EOF) || (n < 1)) continue; // Read the parameter value (decimal or hex, depending on the parameter) uint64_t param_val; if (param == sim_param_watch_tohost) { int n = sscanf (& (linebuf [index]), "%" SCNd64, & param_val); } else if (param == sim_param_tohost_addr) { int n = sscanf (& (linebuf [index]), "%" SCNx64, & param_val); } else { int n = sscanf (& (linebuf [index]), "%" SCNd64, & param_val); } if ((n == EOF) || (n < 1)) continue; else if (param >= MAX_PARAMS) { fprintf (stderr, "INFO: ignoring out-of-bounds parameter code %0" PRId32 " (should be < %0d)\n", param, MAX_PARAMS); fprintf (stderr, " Value (decimal) = %0" PRId64, param_val); fprintf (stderr, " Value (hex) = 0x%0" PRIx64, param_val); } else { param_vals [param] = param_val; } } fclose (fp); } uint64_t c_get_param_val (uint32_t param) { if (! params_loaded) { load_params (); params_loaded = true; } uint64_t param_val = 0; if (param < MAX_PARAMS) param_val = param_vals [param]; return param_val; } */ // **************************************************************** // **************************************************************** // **************************************************************** // Functions for Tandem Verification trace file output. static char trace_file_name[] = "trace_data.dat"; static FILE *trace_file_stream; static uint64_t trace_file_size = 0; static uint64_t trace_file_writes = 0; #define BUFSIZE 1024 static uint8_t buf [BUFSIZE]; // ================================================================ // c_trace_file_open() // Open file for recording binary trace output. uint32_t c_trace_file_open (uint8_t dummy) { uint32_t success = 0; trace_file_stream = fopen ("trace_out.dat", "w"); if (trace_file_stream == NULL) { fprintf (stderr, "ERROR: c_trace_file_open: unable to open file '%s'.\n", trace_file_name); success = 0; } else { fprintf (stdout, "c_trace_file_stream: opened file '%s' for trace_data.\n", trace_file_name); success = 1; } return success; } // ================================================================ // c_trace_file_load_byte_in_buffer () // Write 8-bit 'data' into output buffer at byte offset 'j' uint32_t c_trace_file_load_byte_in_buffer (uint32_t j, uint8_t data) { uint32_t success = 0; if (j >= BUFSIZE) { fprintf (stderr, "ERROR: c_trace_file_load_byte_in_buffer: index (%0d) out of bounds (%0d)\n", j, BUFSIZE); success = 0; } else { buf [j] = data; success = 1; } return success; } // ================================================================ // c_trace_file_load_word64_in_buffer () // Write 64-bit 'data' into output buffer at 'byte_offset' uint32_t c_trace_file_load_word64_in_buffer (uint32_t byte_offset, uint64_t data) { uint32_t success = 0; if ((byte_offset + 7) >= BUFSIZE) { fprintf (stderr, "ERROR: c_trace_file_load_word64_in_buffer: index (%0d) out of bounds (%0d)\n", byte_offset, BUFSIZE); success = 0; } else { uint64_t *p = (uint64_t *) & (buf [byte_offset]); *p = data; success = 1; } return success; } // ================================================================ // c_trace_file_write_buffer() // Write out 'n' bytes from the already-loaded output buffer to the trace file. uint32_t c_trace_file_write_buffer (uint32_t n) { uint32_t success = 0; size_t n_written = fwrite (buf, 1, n, trace_file_stream); if (n_written != n) success = 0; else { trace_file_size += n; trace_file_writes += 1; success = 1; } return success; } // ================================================================ // c_trace_file_close() // Close the trace file. uint32_t c_trace_file_close (uint8_t dummy) { uint32_t success = 0; int status; if (trace_file_stream == NULL) success = 1; else { status = fclose (trace_file_stream); if (status != 0) { fprintf (stderr, "ERROR: c_trace_file_close: error in fclose()\n"); success = 0; } else { fprintf (stdout, "c_trace_file_stream: closed file '%s' for trace_data.\n", trace_file_name); fprintf (stdout, " Trace file writes: %0" PRId64 "\n", trace_file_writes); fprintf (stdout, " Trace file size: %0" PRId64 " bytes\n", trace_file_size); success = 1; } } return success; } // **************************************************************** // **************************************************************** // **************************************************************** // Functions for communication with remote debug client. // Acknowledgement: portions of TCP code adapted from example ECHOSERV // ECHOSERV // (c) Paul Griffiths, 1999 // http://www.paulgriffiths.net/program/c/echoserv.php // ================================================================ // The socket file descriptor static uint16_t port = 30000; static int connected_sockfd = 0; static FILE *logfile_fp = NULL; static char logfile_name [] = "debug_server_log.txt"; // ================================================================ // Connect to debug client as server on tcp_port. // Return fail/ok. uint8_t c_debug_client_connect (const uint16_t tcp_port) { int listen_sockfd; // listening socket struct sockaddr_in servaddr; // socket address structure struct linger linger; fprintf (stdout, "Awaiting remote debug client connection on tcp port %0d ...\n", tcp_port); // Create the listening socket if ( (listen_sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0 ) { fprintf (stderr, "ERROR: c_debug_client_connect: socket () failed\n"); return DMI_STATUS_ERR; } // Set linger to 0 (immediate exit on close) linger.l_onoff = 1; linger.l_linger = 0; setsockopt (listen_sockfd, SOL_SOCKET, SO_LINGER, & linger, sizeof (linger)); // Initialize socket address structure memset (& servaddr, 0, sizeof (servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl (INADDR_ANY); servaddr.sin_port = htons (tcp_port); // Bind socket addresss to listening socket if ( bind (listen_sockfd, (struct sockaddr *) & servaddr, sizeof (servaddr)) < 0 ) { fprintf (stderr, "ERROR: c_debug_client_connect: bind () failed\n"); return DMI_STATUS_ERR; } // Listen for connection if ( listen (listen_sockfd, 1) < 0 ) { fprintf (stderr, "ERROR: c_debug_client_connect: listen () failed\n"); return DMI_STATUS_ERR; } // Set listening socket to non-blocking int flags = fcntl (listen_sockfd, F_GETFL, 0); if (flags < 0) { fprintf (stderr, "ERROR: c_debug_client_connect: fcntl (F_GETFL) failed\n"); return DMI_STATUS_ERR; } flags = (flags |O_NONBLOCK); if (fcntl (listen_sockfd, F_SETFL, flags) < 0) { fprintf (stderr, "ERROR: c_debug_client_connect: fcntl (F_SETFL, O_NONBLOCK) failed\n"); return DMI_STATUS_ERR; } // Wait for a connection, accept() it while (true) { connected_sockfd = accept (listen_sockfd, NULL, NULL); if ((connected_sockfd < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK))) { sleep (1); } else if (connected_sockfd < 0) { fprintf (stderr, "ERROR: c_debug_client_connect: accept () failed\n"); return DMI_STATUS_ERR; } else break; } // Close the listening socket if (close (listen_sockfd) < 0) { perror ("ERROR: c_debug_client_connect: error in close (listen_sockfd)"); return DMI_STATUS_ERR; } fprintf (stdout, "Connected\n"); logfile_fp = fopen (logfile_name, "w"); if (logfile_fp != NULL) { fprintf (stdout, " Logfile for debug client transactions is '%s'\n", logfile_name); fprintf (logfile_fp, "CONNECTED on TCP port %0d\n", tcp_port); } else fprintf (stdout, " Unable to open logfile for debug client transactions: '%s'\n", logfile_name); return DMI_STATUS_OK; } // ================================================================ // Disconnect from debug client as server. // Return fail/ok. uint8_t c_debug_client_disconnect (uint8_t dummy) { uint8_t buf [128]; ssize_t n; fprintf (stdout, "Disconnected from remote debug client on port %0d\n", port); shutdown (connected_sockfd, SHUT_WR); // Drain remaining bytes arriving while (1) { n = recv (connected_sockfd, buf, 128, 0); if (n == 0) break; if ((n == -1) && (errno != EINTR)) break; } if (close (connected_sockfd) < 0) { perror ("ERROR: c_debug_client_disconnect:"); fprintf (stderr, " socket file descriptor: %0d\n", connected_sockfd); return DMI_STATUS_ERR; } if (logfile_fp != NULL) { fprintf (logfile_fp, "DISCONNECTED on port %0d\n", port); fclose (logfile_fp); } return DMI_STATUS_OK; } // ================================================================ // Receive 7-byte request from remote client // Result is: { status, data_b3, data_b2, data_b1, data_b0, addr_b1, addr_b0, op } static int command_num = 0; uint64_t c_debug_client_request_recv (uint8_t dummy) { uint64_t result = 0; uint8_t *p_result = (uint8_t *) & result; // ---------------- // First, poll to check if any data is available int fd = connected_sockfd; struct pollfd x_pollfd; x_pollfd.fd = fd; x_pollfd.events = POLLRDNORM; x_pollfd.revents = 0; int n = poll (& x_pollfd, 1, 0); if (n < 0) { perror ("ERROR: c_debug_client_request_recv (): poll () failed"); p_result [7] = DMI_STATUS_ERR; return result; } if ((x_pollfd.revents & POLLRDNORM) == 0) { // No byte available // sched_yield (); // Allow other threads to run. p_result [7] = DMI_STATUS_UNAVAIL; return result; } // ---------------- // Data is available; read the 7-byte request int data_size = 7; int n_recd = 0; int n_iters = 0; p_result [0] = DMI_STATUS_ERR; while (n_recd < data_size) { int n = read (fd, p_result + n_recd, (data_size - n_recd)); if ((n < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK)) { if (logfile_fp != NULL) { fprintf (logfile_fp, "ERROR: c_debug_client_request_recv () failed\n"); fprintf (logfile_fp, " Received %0d bytes so far (of %0d)\n", n_recd, data_size); fprintf (logfile_fp, " Data so far: 0x%0" PRIx64 "\n", result); fprintf (logfile_fp, " read (sock, ...) => %0d\n", n); } p_result [7] = DMI_STATUS_ERR; return result; } else if (n > 0) { n_recd += n; } n_iters++; if ((n_iters > 0) && ((n_iters % 1000000) == 0)) { if (logfile_fp != NULL) { fprintf (logfile_fp, "WARNING: c_debug_client_request_recv () stalled?\n"); fprintf (logfile_fp, " Received %0d bytes so far (of %0d)\n", n_recd, data_size); fprintf (logfile_fp, " Data so far: 0x%0" PRIx64 "\n", result); fprintf (logfile_fp, " %0d iterations so far\n", n_iters); } } } p_result [7] = DMI_STATUS_OK; if (logfile_fp != NULL) { uint8_t op = (result & 0xFF); uint16_t addr = ((result >> 8) & 0xFFFF); uint32_t data = ((result >> 24) & 0xFFFFFFFF); switch (op) { case DMI_OP_READ: { fprintf (logfile_fp, "C_to_S READ 0x%04x\n", addr); break; } case DMI_OP_WRITE: { fprintf (logfile_fp, "C_to_S WRITE 0x%04x 0x%08x\n", addr, data); break; } case DMI_OP_SHUTDOWN: { fprintf (logfile_fp, "C_to_S SHUTDOWN\n"); break; } case DMI_OP_START_COMMAND: { fprintf (logfile_fp, "C_to_S ======== START_COMMAND %0d\n", command_num); command_num++; break; } default: { fprintf (logfile_fp, "C_to_S ERROR: Unrecognized op %0d; ignored\n", op); fprintf (stderr, "ERROR: c_debug_client_request_recv: Unrecognized op %0d; ignored\n", op); } } fflush (logfile_fp); } return result; } // ================================================================ // Send 4-byte response 'data' to debug client. // Returns fail/ok status uint8_t c_debug_client_response_send (const uint32_t data) { int fd = connected_sockfd; int data_size = 4; // 4 bytes int n_sent = 0; int n_iters = 0; uint8_t *p_data = (uint8_t *) & data; while (n_sent < data_size) { int n = write (fd, p_data + n_sent, (data_size - n_sent)); if ((n < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK)) { if (logfile_fp != NULL) { fprintf (logfile_fp, "ERROR: c_debug_client_response_send (0x%08x) failed\n", data); fprintf (logfile_fp, " Sent %0d bytes so far (of %0d)\n", n_sent, data_size); fprintf (logfile_fp, " write (sock, ...) => %0d\n", n); } return DMI_STATUS_ERR; } else if (n > 0) { n_sent += n; } n_iters++; if ((n_iters > 0) && ((n_iters % 0x1000000) == 0)) { if (logfile_fp != NULL) { fprintf (logfile_fp, "WARNING: c_debug_client_response_send (0x%08x) stalled?\n", data); fprintf (logfile_fp, " Sent %0d bytes so far (of %0d)\n", n_sent, data_size); fprintf (logfile_fp, " %0d iterations so far\n", n_iters); } } } fsync (fd); if (logfile_fp != NULL) { fprintf (logfile_fp, "S_to_C 0x%08x\n", data); fflush (logfile_fp); } return DMI_STATUS_OK; } // ================================================================ // This 'main' procedure is for standalone testing of this C server code. // It listens on the server socket for a connection from Dsharp. // It simply prints out read/write commands from Dsharp, // and responds to read commands with an incrementing integer. #ifdef TEST_COMMS int main (int argc, char *argv []) { uint8_t status; uint64_t req; uint8_t *p_req = (uint8_t *) (& req); uint8_t req_op; uint16_t req_addr; uint32_t req_data; uint32_t rsp_data = 0; status = c_debug_client_connect (port); if (status != DMI_STATUS_OK) { return 1; } while (true) { req = c_debug_client_request_recv (0); status = p_req [7]; req_op = p_req [0]; req_addr = p_req [2]; req_addr = ((req_addr << 8) | p_req [1]); req_data = p_req [6]; req_data = ((req_data << 8) | p_req [5]); req_data = ((req_data << 8) | p_req [4]); req_data = ((req_data << 8) | p_req [3]); if (status == DMI_STATUS_UNAVAIL) { usleep (1000); continue; } if (status == DMI_STATUS_ERR) break; if (logfile_fp != NULL) fprintf (logfile_fp, "================\n"); if (req_op == DMI_OP_SHUTDOWN) { if (logfile_fp != NULL) fprintf (logfile_fp, "SHUTDOWN\n"); break; } else if (req_op == DMI_OP_READ) { if (logfile_fp != NULL) { fprintf (logfile_fp, "READ addr '%04x'\n", req_addr); fprintf (logfile_fp, " => sending response 0x%08x\n", rsp_data); } status = c_debug_client_response_send (rsp_data); if (status == DMI_STATUS_ERR) break; rsp_data++; } else if (req_op == DMI_OP_WRITE) { if (logfile_fp != NULL) fprintf (logfile_fp, "WRITE addr '%04x' data '%08x'\n", req_addr, req_data); } else { if (logfile_fp != NULL) fprintf (stderr, "ERROR: unknown command: %0d\n", req_op); continue; } } return 0; } #endif // **************************************************************** // **************************************************************** // ****************************************************************