diff --git a/migen/sim/generic.py b/migen/sim/generic.py index a5dd9214e..868719447 100644 --- a/migen/sim/generic.py +++ b/migen/sim/generic.py @@ -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) + diff --git a/migen/sim/ipc.py b/migen/sim/ipc.py index 04e394039..1fd6c865e 100644 --- a/migen/sim/ipc.py +++ b/migen/sim/ipc.py @@ -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("= 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() diff --git a/vpi/Makefile b/vpi/Makefile index 54ab175ba..5f4ed341a 100644 --- a/vpi/Makefile +++ b/vpi/Makefile @@ -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) $^ diff --git a/vpi/ipc.c b/vpi/ipc.c index 8b1357c71..2b7dd0082 100644 --- a/vpi/ipc.c +++ b/vpi/ipc.c @@ -5,13 +5,21 @@ #include #include -#include -#include #include #include #include #include +#ifdef _WIN32 +#include +#include +#define WIN_SOCKET_PORT "50007" +#else +#include +#include +#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; } +