From: Sam Moore Date: Wed, 9 Apr 2014 15:41:58 +0000 (+0800) Subject: An interface for a FPU compiled from VHDL X-Git-Url: https://git.ucc.asn.au/?a=commitdiff_plain;h=1bab8e093f47967dc28a5a10030972407d7d0675;p=ipdf%2Fcode.git An interface for a FPU compiled from VHDL I have tested with the FPU from https://github.com/jop-devel/jop The way this works is really awful... 1. Make (or steal from jop) a VHDL FPU 2. Make (or steal from jop) a testbench that uses stdio to read operands and ops - The jop one reads from a generated file, but it is trivial to change to "STD_INPUT" and remove all the assertion tests. 3. Compile testbench into an executable with ghdl 4. Call VFPU::Start which does the following: - Create a socketpair(2) - fork(2) and in the child dup(2) stdin/stdout to the socketpair - Redirect stderr to /dev/null because the testbench generates 10^3 warning messages - in the child, exec(3) the testbench FPU 5. Call VFPU::Exec passing the desired operands and opcodes - The operands and opcodes are written as hex strings over the socketpair - The child executing the FPU program does its thing - The result is read back as a hex string 6. When finished, call VFPU::Halt to kill(2) the FPU I tried to get ghdl and gcc/g++ to make a static library to call ghdl_main instead of exec'ing things. But that didn't work. The fork and socketpair would have still been required for that approach anyway. TODO: - Make the interface (not the FPU) deal with arbitrary size registers - Wrap a Real type around it so we can #define REAL_SIMULATED_FPU I haven't put any of the VHDL in the git repo for obvious reasons. If I actually start messing with it, it will probably get a seperate repo. This may end up being a complete waste of time? Although potentially being able to run an arbitrary FPU albeit in a horrible manner does seem cool. --- diff --git a/src/Makefile b/src/Makefile index d7247ce..5d3ae00 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,7 +2,7 @@ ARCH := $(shell uname -i) CXX = g++ -std=gnu++0x -Wall -Werror -Wshadow -pedantic -g MAIN = main.o -OBJ = log.o document.o view.o screen.o +OBJ = log.o document.o view.o screen.o vfpu.o LIB_x86_64 = ../contrib/lib/libSDL2-2.0.so.0 -lGL LIB_i386 = ../contrib/lib32/libSDL2-2.0.so.0 -lGL LIB_unknown = $(LIB_x86_64) diff --git a/src/tests/vfpufloat.cpp b/src/tests/vfpufloat.cpp new file mode 100644 index 0000000..606ba5e --- /dev/null +++ b/src/tests/vfpufloat.cpp @@ -0,0 +1,14 @@ +#include "main.h" + +#include "vfpu.h" +using namespace std; + + +int main(int argc, char ** argv) +{ + VFPU::Start(); + float result = VFPU::Exec(25,10, VFPU::SUB); + printf("%f\n", result); + VFPU::Halt(); + return 0; +} diff --git a/src/vfpu.cpp b/src/vfpu.cpp new file mode 100644 index 0000000..67f5249 --- /dev/null +++ b/src/vfpu.cpp @@ -0,0 +1,125 @@ +#include "vfpu.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace VFPU +{ + +static const char g_fpu[] = "../vhdl/tb_fpu"; + +static bool g_running = false; +static int g_fpu_socket[2] = {-1,-1}; +static pid_t g_fpu_pid = -1; + +/** + * Starts the VFPU + * @returns 0 on success, errno of the failing function on failure + */ +int Start() +{ + assert(!g_running); + // create unix socket pair + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, g_fpu_socket) != 0) + return errno; + + + g_fpu_pid = fork(); + if (g_fpu_pid < 0) // error check + return errno; + + + // Child branch + if (g_fpu_pid == 0) + { + + // Remap stdio to the socket + dup2(g_fpu_socket[0],fileno(stdin)); + dup2(g_fpu_socket[0],fileno(stdout)); + dup2(open("/dev/null", O_APPEND), fileno(stderr)); //LALALA I AM NOT LISTENING TO YOUR STUPID ERRORS GHDL + + // Unbuffer things; buffers are a pain + setbuf(stdin, NULL); setbuf(stdout, NULL); setbuf(stderr, NULL); + + //fprintf(stderr, "Goodbye!\n"); + execl(g_fpu, g_fpu,NULL); + fprintf(stderr, "Uh oh! %s\n", strerror(errno)); // We will never see this if something goes wrong... oh dear + exit(errno); // Child exits here. + } + + // Parent branch + usleep(100); + g_running = true; // We are ready! + return 0; +} + +/** + * Halt the VFPU + */ +int Halt() +{ + assert(g_running); + // Tell the child to stop running the VHDL simulation + if (close(g_fpu_socket[1]) != 0) + return errno; + usleep(1000); + if (kill(g_fpu_pid, SIGKILL) != 0) + return errno; + g_running = false; + return 0; +} + +/** + * Tell the VFPU to execute an instruction, wait for it to finish, return the result + * TODO: Generalise for non 32bit Registers + */ +Register Exec(const Register & opa, const Register & opb, Opcode op) +{ + assert(g_running); + + // Copy floats into 32 bits (casting will alter the representation) + unsigned a; memcpy(&a, &opa, 8); + unsigned b; memcpy(&b, &opb, 8); + + + char buffer[BUFSIZ]; + int len = sprintf(buffer, "%08x\n%08x\n%03x\n",a, b, op); // This is... truly awful... why am I doing this + //fprintf(stderr, "Writing:\n%s", buffer); + + assert(len == 9+9+4); + assert(write(g_fpu_socket[1], buffer, len) == len); + //fprintf(stderr, "Wrote!\n"); + + len = read(g_fpu_socket[1], buffer, sizeof(buffer)); + assert(len == 9); + buffer[len] = '\0'; + + + unsigned result = 0x00000000; + for (int i = 0; i < len/2; ++i) + { + unsigned byte2; // cos its two bytes + sscanf(buffer+2*i, "%02x", &byte2); + result |= (byte2 << 8*(len/2-i-1)); + } + + //fprintf(stderr, "Buffer: %s\nResult: %08x\n", buffer, result); + + Register r; + memcpy(&r, &result, 8); // Amazing. + return r; +} + +} + + diff --git a/src/vfpu.h b/src/vfpu.h new file mode 100644 index 0000000..da130a4 --- /dev/null +++ b/src/vfpu.h @@ -0,0 +1,32 @@ +#ifndef _VFPU_H +#define _VFPU_H + +/** + * Implements a terrible and hacky interface to use a virtual FPU to do floating point operations + */ + +namespace VFPU +{ + extern int Start(); // Starts the VFPU + extern int Halt(); // Halts the VFPU + +/** + -- 000 = add, + -- 001 = substract, + -- 010 = multiply, + -- 011 = divide, + -- 100 = square root + -- 101 = unused + -- 110 = unused + -- 111 = unused + */ + typedef enum {ADD=0x000, SUB=0x001, MULT=0x010, DIV=0x011, SQRT=0x100} Opcode; + typedef float Register; + + extern Register Exec(const Register & a, const Register & b, Opcode op); + +} + +#endif //_VFPU_H + +