Windows simulation support

This commit is contained in:
William D. Jones 2015-05-09 21:09:32 +08:00 committed by Sebastien Bourdeauducq
parent 99fb0d4619
commit fe6eef7069
4 changed files with 210 additions and 37 deletions

View 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)

View 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()

View 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) $^

157
vpi/ipc.c
View File

@ -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;
}