Files
Toooba/src_Testbench/Top/C_Imported_Functions.c
2019-03-26 14:49:40 -04:00

826 lines
22 KiB
C

// 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 <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <inttypes.h>
#include <string.h>
#include <errno.h>
// For comms polling
#include <sys/types.h>
#include <poll.h>
#include <sched.h>
// For TCP
#include <sys/socket.h> // socket definitions
#include <sys/types.h> // socket types
#include <arpa/inet.h> // inet (3) funtions
#include <fcntl.h> // 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 '<symbol> <value-in-hex>' 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
// ****************************************************************
// ****************************************************************
// ****************************************************************