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
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,6 +166,9 @@ class PacketTooLarge(Exception):
class Initiator:
def __init__(self, sockaddr):
self.sockaddr = sockaddr
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)
@ -175,6 +188,26 @@ class Initiator:
def recv(self):
maxlen = 2048
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
@ -187,6 +220,17 @@ class Initiator:
self.conn.shutdown(socket.SHUT_RDWR)
self.conn.close()
if hasattr(self, "socket"):
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) $^

127
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,7 +34,12 @@ 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;
@ -36,6 +49,35 @@ struct ipc_softc *ipc_connect(const char *sockaddr,
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);
@ -49,6 +91,7 @@ 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,19 +123,62 @@ 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; /* No length identifier, so we care about the entire buffer */
#endif
i = 0;
switch(buffer[i++]) {
case MESSAGE_GO:
assert(l == 1);
assert((l - i) == 0);
return sc->h_go(sc->user);
case MESSAGE_WRITE: {
char *name;
@ -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;
}
@ -142,6 +244,17 @@ int ipc_read_reply(struct ipc_softc *sc, int nchunks, const unsigned char *chunk
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);
@ -149,9 +262,11 @@ int ipc_read_reply(struct ipc_softc *sc, int nchunks, const unsigned char *chunk
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;
}