Windows simulation support
authorWilliam D. Jones <thor0505@comcast.net>
Sat, 9 May 2015 13:09:32 +0000 (21:09 +0800)
committerSebastien Bourdeauducq <sb@m-labs.hk>
Sat, 9 May 2015 13:09:52 +0000 (21:09 +0800)
migen/sim/generic.py
migen/sim/ipc.py
vpi/Makefile
vpi/ipc.c

index a5dd9214e42d9ed0a54e5d6458c8b202f15320ca..868719447336bb3145f3c021c96446053ad630f8 100644 (file)
@@ -1,4 +1,5 @@
 import warnings
+import sys
 
 from migen.fhdl.std import *
 from migen.fhdl.structure import _Fragment
@@ -25,6 +26,9 @@ class TopLevel:
         self.ios = {cd.clk, cd.rst}
 
     def get(self, sockaddr):
+        if sys.platform == "win32":
+            sockaddr = sockaddr[0]  # Get the IP address only
+
         template1 = """`timescale 1ns / 1ps
 
 module {top_name}();
@@ -83,7 +87,12 @@ class Simulator:
         if sim_runner is None:
             sim_runner = icarus.Runner()
         self.top_level = top_level
-        self.ipc = Initiator(sockaddr)
+        if sys.platform == "win32":
+            sockaddr = ("127.0.0.1", 50007)
+            self.ipc = Initiator(sockaddr)
+        else:
+            self.ipc = Initiator(sockaddr)
+
         self.sim_runner = sim_runner
 
         c_top = self.top_level.get(sockaddr)
@@ -217,3 +226,4 @@ class Simulator:
 def run_simulation(fragment, ncycles=None, vcd_name=None, **kwargs):
     with Simulator(fragment, TopLevel(vcd_name), icarus.Runner(**kwargs)) as s:
         s.run(ncycles)
+
index 04e394039b11a5f7e250a9ea0f5e36f54b373dc7..1fd6c865e13e4a394ea3c7dae2aa35fb47e423db 100644 (file)
@@ -3,12 +3,14 @@
 
 import socket
 import os
+import sys
+import struct
+
 
 #
 # Message classes
 #
 
-
 class Int32(int):
     pass
 
@@ -55,11 +57,11 @@ class MessageReadReply(Message):
 
 message_classes = [MessageTick, MessageGo, MessageWrite, MessageRead, MessageReadReply]
 
+
 #
 # Packing
 #
 
-
 def _pack_int(v):
     if v == 0:
         p = [1, 0]
@@ -78,6 +80,11 @@ def _pack_str(v):
     return p
 
 
+def _pack_int16(v):
+    return [v & 0xff,
+            (v & 0xff00) >> 8]
+
+
 def _pack_int32(v):
     return [
         v & 0xff,
@@ -100,13 +107,16 @@ def _pack(message):
             r += _pack_int32(value)
         else:
             raise TypeError
+    if sys.platform == "win32":
+        size = _pack_int16(len(r) + 2)  # size specifier adds to size
+        r = size + r
     return bytes(r)
 
+
 #
 # Unpacking
 #
 
-
 def _unpack_int(i, nchunks=None):
     v = 0
     power = 1
@@ -144,11 +154,11 @@ def _unpack(message):
         pvalues.append(v)
     return msgclass(*pvalues)
 
+
 #
 # I/O
 #
 
-
 class PacketTooLarge(Exception):
     pass
 
@@ -156,8 +166,11 @@ class PacketTooLarge(Exception):
 class Initiator:
     def __init__(self, sockaddr):
         self.sockaddr = sockaddr
-        self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
-        self._cleanup_file()
+        if sys.platform == "win32":
+            self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        else:
+            self.socket = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET)
+            self._cleanup_file()
         self.socket.bind(self.sockaddr)
         self.socket.listen(1)
 
@@ -175,11 +188,31 @@ class Initiator:
 
     def recv(self):
         maxlen = 2048
-        packet = self.conn.recv(maxlen)
-        if len(packet) < 1:
-            return None
-        if len(packet) >= maxlen:
-            raise PacketTooLarge
+        if sys.platform == "win32":
+            packet = self.conn.recv(maxlen)
+            if len(packet) < 1:
+                return None
+            if len(packet) >= maxlen:
+                raise PacketTooLarge
+            expected_size = struct.unpack("<H", packet[:2])[0]
+
+            # If full packet wasn't received, keep waiting!
+            if len(packet) < expected_size:
+                packet_frag = self.conn.recv(maxlen)
+                packet += packet_frag
+                if len(packet_frag) < 1:
+                    return None
+                if len(packet) >= maxlen:
+                    raise PacketTooLarge
+
+            # Discard the length from the packet - it's not needed anymore.
+            packet = packet[2:]
+        else:
+            packet = self.conn.recv(maxlen)
+            if len(packet) < 1:
+                return None
+            if len(packet) >= maxlen:
+                raise PacketTooLarge
         return _unpack(packet)
 
     def close(self):
@@ -187,6 +220,17 @@ class Initiator:
             self.conn.shutdown(socket.SHUT_RDWR)
             self.conn.close()
         if hasattr(self, "socket"):
-            self.socket.shutdown(socket.SHUT_RDWR)
-            self.socket.close()
-        self._cleanup_file()
+            if sys.platform == "win32":
+                # self.socket.shutdown(socket.SHUT_RDWR)
+                # Fails with WinError 10057:
+                # A request to send or receive data was disallowed because the
+                # socket is not connected and (when sending on a datagram
+                # socket using a sendto call) no address was supplied
+                # This error will cascade into WinError 10038, where
+                # Simulator.__exit__ didn't finish cleaning up and left an
+                # invalid socket.
+                self.socket.close()
+            else:
+                self.socket.shutdown(socket.SHUT_RDWR)
+                self.socket.close()
+                self._cleanup_file()
index 54ab175bae5d2906768f00840bd27c9ac5ce923a..5f4ed341a054b8dcec548d6ded33ef87697f4208 100644 (file)
@@ -2,6 +2,10 @@ INSTDIR = $(shell iverilog-vpi --install-dir)
 
 CFLAGS = -Wall -O2 $(CFLAGS_$@)
 VPI_CFLAGS := $(shell iverilog-vpi --cflags)
+# Define the below flags for a Windows build.
+# Make sure to run iverilog-vpi with -mingw and -ivl options if necessary!
+# i.e. iverilog-vpi -mingw=C:\msys64\mingw32 -ivl=C:\msys64\mingw32
+# MINGW_FLAGS=-lWs2_32
 
 OBJ=ipc.o main.o
 
@@ -11,7 +15,7 @@ all: migensim.vpi
        $(CC) $(CFLAGS) $(VPI_CFLAGS) -c $(INCDIRS) -o $@ $<
 
 migensim.vpi: $(OBJ)
-       iverilog-vpi --name=migensim $^
+       iverilog-vpi $(MINGW_FLAGS) --name=migensim $^
 
 install: migensim.vpi
        install -m755 -t $(INSTDIR) $^
index 8b1357c712bc025ed579c5da57a3fa6d0607ceb7..2b7dd0082bfb6f890a0512a00d8b430d2921dfb2 100644 (file)
--- a/vpi/ipc.c
+++ b/vpi/ipc.c
@@ -5,13 +5,21 @@
 
 #include <assert.h>
 #include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
 #include <unistd.h>
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 
+#ifdef _WIN32
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#define WIN_SOCKET_PORT "50007"
+#else
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+
+
 #include "ipc.h"
 
 struct ipc_softc {
@@ -26,22 +34,56 @@ struct ipc_softc *ipc_connect(const char *sockaddr,
        go_handler h_go, write_handler h_write, read_handler h_read, void *user)
 {
        struct ipc_softc *sc;
+#ifdef _WIN32
+       struct addrinfo hints, *my_addrinfo;
+       WSADATA wsaData;
+#else
        struct sockaddr_un addr;
-       
+#endif
+
        sc = malloc(sizeof(struct ipc_softc));
        if(!sc) return NULL;
-       
+
        sc->h_go = h_go;
        sc->h_write = h_write;
        sc->h_read = h_read;
        sc->user = user;
-       
+
+#ifdef _WIN32
+       /* Initialize Winsock. */
+       if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
+               free(sc);
+               return NULL;
+       }
+
+       memset(&hints, 0, sizeof(hints));
+       hints.ai_family = AF_INET;
+       hints.ai_socktype = SOCK_STREAM;
+       hints.ai_protocol = IPPROTO_TCP;
+
+       if(getaddrinfo(sockaddr, WIN_SOCKET_PORT, NULL, &my_addrinfo) != 0) {
+               free(sc);
+               return NULL;
+       }
+
+       sc->socket = socket(AF_INET, SOCK_STREAM, 0);
+       if(sc->socket < 0) {
+               free(sc);
+               return NULL;
+       }
+
+       if(connect(sc->socket, my_addrinfo->ai_addr, my_addrinfo->ai_addrlen) != 0) {
+               close(sc->socket);
+               free(sc);
+               return NULL;
+       }
+#else
        sc->socket = socket(AF_UNIX, SOCK_SEQPACKET, 0);
        if(sc->socket < 0) {
                free(sc);
                return NULL;
        }
-       
+
        addr.sun_family = AF_UNIX;
        strcpy(addr.sun_path, sockaddr);
        if(connect(sc->socket, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
@@ -49,7 +91,8 @@ struct ipc_softc *ipc_connect(const char *sockaddr,
                free(sc);
                return NULL;
        }
-       
+#endif
+
        return sc;
 }
 
@@ -57,6 +100,9 @@ void ipc_destroy(struct ipc_softc *sc)
 {
        close(sc->socket);
        free(sc);
+#ifdef _WIN32
+       WSACleanup();
+#endif
 }
 
 enum {
@@ -77,26 +123,69 @@ enum {
 int ipc_receive(struct ipc_softc *sc)
 {
        unsigned char buffer[MAX_LEN];
-       ssize_t l;
+       ssize_t l = 0;
        int i;
-       
+#ifdef _WIN32
+       int expected_num;
+       int received_num = 0;
+#endif
+
+#ifdef _WIN32
+       /* Initial recv. Includes a length identifier so that we wait
+        * until a full message is received. The length of the message
+        * includes the length identifier. */
+       while(received_num < 2) {
+               /* Ensure we wait to get the packet length,
+                * which requires two bytes, before continuing. */
+               received_num = recv(sc->socket, (char *)&buffer[l], \
+                       (MAX_LEN - received_num), 0);
+               l += received_num;
+
+               if(received_num == 0)
+                       return 2;
+               if((received_num < 0) || (received_num >= MAX_LEN) || \
+                       (l >= MAX_LEN))
+                       return 0;
+       }
+
+       expected_num = ((buffer[1] << 8) | (buffer[0]));
+       while(l < expected_num) {
+               /* received_num will never exceed MAX_LEN, unless
+                * recv is broken. */
+               received_num = recv(sc->socket, (char *)&buffer[l], \
+                       (MAX_LEN - received_num), 0);
+               l += received_num;
+
+               if(received_num == 0)
+                       return 2;
+               if((received_num < 0) || (received_num >= MAX_LEN) || \
+                       (l >= MAX_LEN))
+                       return 0;
+       } /* l is assumed to have the message length
+          * in the message unpacking code. */
+       i = 2; /* Skip the length identifier. */
+#else
+       /* On Unix, SOCK_SEQPACKET will take care of ensuring message
+        * boundaries are satisfied. */
        l = recv(sc->socket, buffer, MAX_LEN, 0);
        if(l == 0)
                return 2;
        if((l < 0) || (l >= MAX_LEN))
                return 0;
-       
-       i = 0;
+       i = 0; /* No length identifier, so we care about the entire buffer */
+#endif
+
        switch(buffer[i++]) {
                case MESSAGE_GO:
-                       assert(l == 1);
+                       assert((l - i) == 0);
+
                        return sc->h_go(sc->user);
                case MESSAGE_WRITE: {
                        char *name;
                        int nchunks;
                        unsigned char *chunks;
                        unsigned int chunk_index;
-                       
+
                        name = (char *)&buffer[i];
                        i += strlen(name) + 1;
                        assert((i+4) < l);
@@ -105,18 +194,18 @@ int ipc_receive(struct ipc_softc *sc)
                        nchunks = buffer[i++];
                        assert(i + nchunks == l);
                        chunks = (unsigned char *)&buffer[i];
-                       
+
                        return sc->h_write(name, chunk_index, nchunks, chunks, sc->user);
                }
                case MESSAGE_READ: {
                        char *name;
                        unsigned int name_index;
-                       
+
                        name = (char *)&buffer[i];
                        i += strlen(name) + 1;
                        assert((i+4) == l);
                        name_index = buffer[i] | buffer[i+1] << 8 | buffer[i+2] << 16 | buffer[i+3] << 24;
-                       
+
                        return sc->h_read(name, name_index, sc->user);
                }
                default:
@@ -126,13 +215,26 @@ int ipc_receive(struct ipc_softc *sc)
 
 int ipc_tick(struct ipc_softc *sc)
 {
-       char c;
        ssize_t l;
-       
+
+#ifdef _WIN32
+       char c[3];
+
+       c[0] = 3;
+       c[1] = 0;
+       c[2] = MESSAGE_TICK;
+       l = send(sc->socket, c, 3, 0);
+       if(l != 3)
+               return 0;
+#else
+       char c;
+
        c = MESSAGE_TICK;
        l = send(sc->socket, &c, 1, 0);
        if(l != 1)
                return 0;
+#endif
+
        return 1;
 }
 
@@ -141,17 +243,30 @@ int ipc_read_reply(struct ipc_softc *sc, int nchunks, const unsigned char *chunk
        int len;
        char buffer[MAX_LEN];
        ssize_t l;
-       
+
+#ifdef _WIN32
+       len = nchunks + 4;
+       assert(len < MAX_LEN);
+       assert(nchunks < 256);
+
+       buffer[0] = len & 0xFF;
+       buffer[1] = (0xFF00 & len) >> 8;
+       buffer[2] = MESSAGE_READ_REPLY;
+       buffer[3] = nchunks;
+       memcpy(&buffer[4], chunks, nchunks);
+#else
        len = nchunks + 2;
        assert(len < MAX_LEN);
        assert(nchunks < 256);
-       
+
        buffer[0] = MESSAGE_READ_REPLY;
        buffer[1] = nchunks;
        memcpy(&buffer[2], chunks, nchunks);
-       
+#endif
+
        l = send(sc->socket, buffer, len, 0);
        if(l != len)
                return 0;
        return 1;
 }
+