litex/build/sim: introduce new simulator with modules support (thanks lambdaconcept)
authorPierre-Olivier Vauboin <po@lambdaconcept>
Wed, 28 Jun 2017 14:10:34 +0000 (16:10 +0200)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Wed, 28 Jun 2017 14:14:13 +0000 (16:14 +0200)
29 files changed:
litex/boards/targets/sim.py
litex/build/sim/config.py [new file with mode: 0644]
litex/build/sim/core/Makefile [new file with mode: 0644]
litex/build/sim/core/error.h [new file with mode: 0644]
litex/build/sim/core/libdylib.c [new file with mode: 0644]
litex/build/sim/core/libdylib.h [new file with mode: 0644]
litex/build/sim/core/modules.c [new file with mode: 0644]
litex/build/sim/core/modules.h [new file with mode: 0644]
litex/build/sim/core/modules/Makefile [new file with mode: 0644]
litex/build/sim/core/modules/clocker/Makefile [new file with mode: 0644]
litex/build/sim/core/modules/clocker/clocker.c [new file with mode: 0644]
litex/build/sim/core/modules/ethernet/Makefile [new file with mode: 0644]
litex/build/sim/core/modules/ethernet/ethernet.c [new file with mode: 0644]
litex/build/sim/core/modules/rules.mak [new file with mode: 0644]
litex/build/sim/core/modules/serial2console/Makefile [new file with mode: 0644]
litex/build/sim/core/modules/serial2console/serial2console.c [new file with mode: 0644]
litex/build/sim/core/modules/serial2tcp/Makefile [new file with mode: 0644]
litex/build/sim/core/modules/serial2tcp/serial2tcp.c [new file with mode: 0644]
litex/build/sim/core/modules/variables.mak [new file with mode: 0644]
litex/build/sim/core/pads.c [new file with mode: 0644]
litex/build/sim/core/pads.h [new file with mode: 0644]
litex/build/sim/core/parse.c [new file with mode: 0644]
litex/build/sim/core/sim.c [new file with mode: 0644]
litex/build/sim/core/tinydir.h [new file with mode: 0644]
litex/build/sim/core/veril.cpp [new file with mode: 0644]
litex/build/sim/core/veril.h [new file with mode: 0644]
litex/build/sim/dut_tb.cpp [deleted file]
litex/build/sim/platform.py
litex/build/sim/verilator.py

index 0f0d657d06c045e2a419b20cf94d190e70fb706e..50cac545dca8cd459415cf944f6504d6598116c0 100755 (executable)
@@ -20,6 +20,8 @@ from litedram.core.controller import ControllerSettings
 from liteeth.phy.model import LiteEthPHYModel
 from liteeth.core.mac import LiteEthMAC
 
+from litex.build.sim.config import SimConfig
+
 class BaseSoC(SoCSDRAM):
     def __init__(self, **kwargs):
         platform = sim.Platform()
@@ -92,10 +94,15 @@ def main():
                         help="enable Ethernet support")
     args = parser.parse_args()
 
+    scfg = SimConfig(default_clk="sys_clk")
+    scfg.add_module("serial2console", "serial")
+    if args.with_ethernet:
+        scfg.add_module('ethernet', "eth", args={"interface": "tap1", "ip": "192.168.1.100"})
+
     cls = MiniSoC if args.with_ethernet else BaseSoC
     soc = cls(**soc_sdram_argdict(args))
     builder = Builder(soc, **builder_argdict(args))
-    builder.build()
+    builder.build(sim_config=scfg)
 
 
 if __name__ == "__main__":
diff --git a/litex/build/sim/config.py b/litex/build/sim/config.py
new file mode 100644 (file)
index 0000000..655a7bc
--- /dev/null
@@ -0,0 +1,42 @@
+import json
+
+class SimConfig():
+    def __init__(self, default_clk=None):
+        self.modules = []
+        self.default_clk = default_clk
+        if default_clk:
+            self.add_clocker(default_clk)
+
+    def _format_interfaces(self, interfaces):
+        if not isinstance(interfaces, list):
+            interfaces = [interfaces]
+        new = []
+        for it in interfaces:
+            obj = it
+            if isinstance(it, tuple):
+                name, index = it
+                obj = {"name": name, "index": index}
+            new.append(obj)
+        return new
+
+    def add_clocker(self, clk):
+        self.add_module("clocker", [], clocks=clk, tickfirst=True)
+
+    def add_module(self, name, interfaces, clocks=None, args=None, tickfirst=False):
+        interfaces = self._format_interfaces(interfaces)
+        if clocks:
+            interfaces += self._format_interfaces(clocks)
+        else:
+            interfaces += [self.default_clk]
+        newmod = {
+            "module": name,
+            "interface": interfaces,
+        }
+        if args:
+            newmod.update({"args": args})
+        if tickfirst:
+            newmod.update({"tickfirst": tickfirst})
+        self.modules.append(newmod)
+
+    def get_json(self):
+        return json.dumps(self.modules, indent=4)
diff --git a/litex/build/sim/core/Makefile b/litex/build/sim/core/Makefile
new file mode 100644 (file)
index 0000000..0388cdd
--- /dev/null
@@ -0,0 +1,40 @@
+include variables.mak
+
+CC = gcc
+CFLAGS = -Wall -O3 -ggdb
+LDFLAGS = -lpthread -ljson-c -lm -lstdc++ -ldl -levent
+
+SRC_DIR ?= .
+INC_DIR ?= .
+MOD_DIR = $(SRC_DIR)/modules
+export OBJ_DIR = $(abspath obj_dir)
+
+SRCS_SIM_ABSPATH = $(wildcard $(SRC_DIR)/*.c)
+SRCS_SIM = $(notdir $(SRCS_SIM_ABSPATH))
+SRCS_SIM_CPP = dut_init.cpp $(SRC_DIR)/veril.cpp
+OBJS_SIM = $(SRCS_SIM:.c=.o)
+
+all: modules sim
+
+mkdir:
+       mkdir -p $(OBJ_DIR)
+
+$(OBJS_SIM): %.o: $(SRC_DIR)/%.c
+       $(CC) -c $(CFLAGS) -o $(OBJ_DIR)/$@ $<
+
+.PHONY: sim
+sim: mkdir $(OBJS_SIM)
+       verilator -Wno-fatal -O3 --cc dut.v --exe \
+               $(SRCS_SIM_CPP) $(OBJS_SIM) \
+               -CFLAGS "$(CFLAGS) -I$(SRC_DIR)" \
+               -LDFLAGS "$(LDFLAGS)" \
+               -trace $(INC_DIR)
+       make -j -C $(OBJ_DIR) -f Vdut.mk Vdut
+
+.PHONY: modules
+modules: mkdir
+       $(MAKE) -C $(MOD_DIR)
+
+.PHONY: clean
+clean:
+       rm -rf $(OBJ_DIR)
diff --git a/litex/build/sim/core/error.h b/litex/build/sim/core/error.h
new file mode 100644 (file)
index 0000000..1a81b8f
--- /dev/null
@@ -0,0 +1,14 @@
+/* Copyright (C) 2017 LambdaConcept */
+
+#ifndef __ERROR_H_
+#define __ERROR_H_
+
+#define RC_OK 0
+#define RC_ERROR -1
+#define RC_INVARG -2
+#define RC_NOENMEM -3
+#define RC_JSERROR -4
+
+#define eprintf(format, ...) fprintf (stderr, "%s:%d "format, __FILE__, __LINE__,  ##__VA_ARGS__)
+
+#endif
diff --git a/litex/build/sim/core/libdylib.c b/litex/build/sim/core/libdylib.c
new file mode 100644 (file)
index 0000000..ba8cdb6
--- /dev/null
@@ -0,0 +1,366 @@
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "libdylib.h"
+
+#ifdef LIBDYLIB_CXX
+using libdylib::dylib_ref;
+namespace libdylib {
+#endif
+struct dylib_data {
+    void *handle;
+    const char *path;
+    bool dyn_path; // true if path should be freed
+    bool freed;
+    bool is_self;
+};
+#ifdef LIBDYLIB_CXX
+}
+#endif
+
+LIBDYLIB_DEFINE(const void*, get_handle)(dylib_ref lib)
+{
+    return lib->handle;
+}
+
+LIBDYLIB_DEFINE(const char*, get_path)(dylib_ref lib)
+{
+    return lib->path;
+}
+
+#define ERR_MAX_SIZE 2048
+static char last_err[ERR_MAX_SIZE];
+static bool last_err_set = 0;
+static void set_last_error(const char *s)
+{
+    if (!s)
+        s = "NULL error";
+    last_err_set = 1;
+    strncpy(last_err, s, ERR_MAX_SIZE);
+}
+
+static dylib_ref dylib_ref_alloc (void *handle, const char *path)
+{
+    if (handle == NULL)
+        return NULL;
+    dylib_ref ref = NULL;
+    ref = (dylib_ref)malloc(sizeof(*ref));
+    ref->handle = handle;
+    ref->path = path;
+    ref->dyn_path = false;
+    ref->freed = false;
+    ref->is_self = false;
+    return ref;
+}
+
+/*
+static dylib_ref dylib_ref_alloc_dynamic (void *handle, char *path)
+{
+    dylib_ref ref = dylib_ref_alloc(handle, path);
+    if (ref == NULL)
+        return NULL;
+    ref->dyn_path = true;
+    return ref;
+}
+*/
+static void dylib_ref_free (dylib_ref ref)
+{
+    if (ref == NULL)
+        return;
+    if (ref->freed)
+        return;
+    ref->handle = NULL;
+    if (ref->dyn_path)
+        free((char*)ref->path);
+    ref->freed = true;
+    free((void*)ref);
+}
+
+static void platform_set_last_error();
+static void *platform_raw_open (const char *path);
+static void *platform_raw_open_self();
+static bool platform_raw_close (void *handle);
+static void *platform_raw_lookup (void *handle, const char *symbol);
+
+#define check_null_arg(arg, msg, ret) if (arg == NULL) {set_last_error(msg); return ret; }
+#define check_null_handle(handle, ret) check_null_arg(handle, "NULL library handle", ret)
+#define check_null_path(path, ret) check_null_arg(path, "NULL library path", ret)
+
+#if defined(LIBDYLIB_UNIX)
+#include <dlfcn.h>
+
+static void platform_set_last_error()
+{
+    set_last_error(dlerror());
+}
+
+static void *platform_raw_open (const char *path)
+{
+    return (void*)dlopen(path, RTLD_LOCAL | RTLD_NOW);
+}
+
+static void *platform_raw_open_self()
+{
+  //return (void*)RTLD_SELF;
+  return NULL;
+}
+
+static bool platform_raw_close (void *handle)
+{
+    return dlclose(handle) == 0;
+}
+
+static void *platform_raw_lookup (void *handle, const char *symbol)
+{
+    return dlsym(handle, symbol);
+}
+
+// end LIBDYLIB_UNIX
+#elif defined(LIBDYLIB_WINDOWS)
+#include <windows.h>
+
+static void platform_set_last_error()
+{
+    // Based on http://stackoverflow.com/questions/1387064
+    DWORD code = GetLastError();
+    if (!code)
+        set_last_error(NULL);
+    else
+    {
+        LPSTR buf = NULL;
+        size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+            NULL, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&buf, 0, NULL);
+        set_last_error((const char*)buf);
+        LocalFree(buf);
+    }
+}
+
+static void *platform_raw_open (const char *path)
+{
+    return (void*)LoadLibrary(path);
+}
+
+static void *platform_raw_open_self()
+{
+    return (void*)GetModuleHandle(NULL);
+}
+
+static bool platform_raw_close (void *handle)
+{
+    return FreeLibrary((HMODULE)handle);
+}
+
+static void *platform_raw_lookup (void *handle, const char *symbol)
+{
+    return (void*)GetProcAddress((HMODULE)handle, symbol);
+}
+
+// end LIBDYLIB_WINDOWS
+#else
+#error "unrecognized platform"
+#endif
+
+// All platforms
+
+LIBDYLIB_DEFINE(dylib_ref, open)(const char *path)
+{
+    check_null_path(path, NULL);
+    dylib_ref lib = dylib_ref_alloc(platform_raw_open(path), path);
+    if (lib == NULL)
+        platform_set_last_error();
+    return lib;
+}
+
+LIBDYLIB_DEFINE(dylib_ref, open_self)()
+{
+    dylib_ref lib = dylib_ref_alloc(platform_raw_open_self(), NULL);
+    lib->is_self = true;
+    return lib;
+}
+
+LIBDYLIB_DEFINE(bool, close)(dylib_ref lib)
+{
+    check_null_handle(lib, 0);
+    if (lib->is_self)
+    {
+        dylib_ref_free(lib);
+        return true;
+    }
+    bool ret = platform_raw_close((void*)lib->handle);
+    if (!ret)
+        platform_set_last_error();
+    else
+        dylib_ref_free(lib);
+    return ret;
+}
+
+LIBDYLIB_DEFINE(void*, lookup)(dylib_ref lib, const char *symbol)
+{
+    check_null_handle(lib, NULL);
+    void *ret = platform_raw_lookup((void*)lib->handle, symbol);
+    if (ret == NULL)
+        platform_set_last_error();
+    return ret;
+}
+
+LIBDYLIB_DEFINE(dylib_ref, open_list)(const char *path, ...)
+{
+    va_list args;
+    va_start(args, path);
+    dylib_ref ret = LIBDYLIB_NAME(va_open_list)(path, args);
+    va_end(args);
+    return ret;
+}
+
+LIBDYLIB_DEFINE(dylib_ref, va_open_list)(const char *path, va_list args)
+{
+    const char *curpath = path;
+    dylib_ref ret = NULL;
+    while (curpath)
+    {
+        ret = LIBDYLIB_NAME(open)(curpath);
+        if (ret)
+            break;
+        curpath = va_arg(args, const char*);
+    }
+    return ret;
+}
+
+const char *locate_patterns[] =
+#if defined(LIBDYLIB_APPLE)
+    {"lib%s.dylib", "%s.framework/%s", "%s.dylib", "lib%s.so", "%s.so"}
+#elif defined(LIBDYLIB_LINUX)
+    {"lib%s.so", "%s.so"}
+#elif defined(LIBDYLIB_WINDOWS)
+    {"%s.dll", "lib%s.dll"}
+#else
+    #warning "Falling back to default open_locate patterns"
+    {"lib%s.so", "%s.so"}
+#endif
+;
+
+char *simple_format(const char *pattern, const char *str)
+{
+    size_t i_in = 0,
+           i_out = 0,
+           len_p = strlen(pattern),
+           len_s = strlen(str),
+           len_out = len_p;
+    {
+        const char *tmp = pattern;
+        while ((tmp = strstr(tmp, "%s")))
+        {
+            len_out += len_s - 2;
+            ++tmp;
+        }
+    }
+    char *out = (char*)malloc((len_out + 1) * sizeof(char));
+    while (i_in < len_p)
+    {
+        if (pattern[i_in] == '%' && pattern[i_in + 1] == 's')
+        {
+            strcpy(out + i_out, str);
+            i_in += 2;
+            i_out += len_s;
+        }
+        else if (pattern[i_in] == '%' && pattern[i_in + 1] == '%')
+        {
+            out[i_out++] = '%';
+            i_in += 2;
+        }
+        else
+        {
+            out[i_out++] = pattern[i_in++];
+        }
+    }
+    out[len_out] = 0;
+    return out;
+}
+
+LIBDYLIB_DEFINE(dylib_ref, open_locate)(const char *name)
+{
+    dylib_ref lib = NULL;
+    size_t i;
+    for (i = 0; i < (sizeof(locate_patterns) / sizeof(locate_patterns[0])); ++i)
+    {
+        char *path = simple_format(locate_patterns[i], name);
+        lib = LIBDYLIB_NAME(open)(path);
+        if (lib != NULL)
+            break;
+        else
+            free(path);
+    }
+    if (lib == NULL)
+        lib = LIBDYLIB_NAME(open)(name);
+    return lib;
+}
+
+LIBDYLIB_DEFINE(bool, bind)(dylib_ref lib, const char *symbol, void **dest)
+{
+    *dest = LIBDYLIB_NAME(lookup)(lib, symbol);
+    return *dest != 0;
+}
+
+LIBDYLIB_DEFINE(bool, find)(dylib_ref lib, const char *symbol)
+{
+    return LIBDYLIB_NAME(lookup)(lib, symbol) != NULL;
+}
+
+LIBDYLIB_DEFINE(bool, find_any)(dylib_ref lib, ...)
+{
+    va_list args;
+    va_start(args, lib);
+    bool ret = LIBDYLIB_NAME(va_find_any)(lib, args);
+    va_end(args);
+    return ret;
+}
+LIBDYLIB_DEFINE(bool, va_find_any)(dylib_ref lib, va_list args)
+{
+    const char *cursym = NULL;
+    bool ret = 0;
+    while (!ret && (cursym = va_arg(args, const char*)))
+    {
+        if (LIBDYLIB_NAME(lookup)(lib, cursym))
+            ret = 1;
+    }
+    return ret;
+}
+LIBDYLIB_DEFINE(bool, find_all)(dylib_ref lib, ...)
+{
+    va_list args;
+    va_start(args, lib);
+    bool ret = LIBDYLIB_NAME(va_find_all)(lib, args);
+    va_end(args);
+    return ret;
+}
+LIBDYLIB_DEFINE(bool, va_find_all)(dylib_ref lib, va_list args)
+{
+    const char *cursym = NULL;
+    bool ret = 1;
+    while (ret && (cursym = va_arg(args, const char*)))
+    {
+        if (!LIBDYLIB_NAME(lookup)(lib, cursym))
+            ret = 0;
+    }
+    return ret;
+}
+
+LIBDYLIB_DEFINE(const char*, last_error)()
+{
+    if (!last_err_set)
+        return NULL;
+    return last_err;
+}
+
+LIBDYLIB_DEFINE(int, get_version)()
+{
+    return LIBDYLIB_VERSION;
+}
+
+LIBDYLIB_DEFINE(const char*, get_version_str())
+{
+    return LIBDYLIB_VERSION_STR;
+}
diff --git a/litex/build/sim/core/libdylib.h b/litex/build/sim/core/libdylib.h
new file mode 100644 (file)
index 0000000..a92a3b0
--- /dev/null
@@ -0,0 +1,118 @@
+#ifndef LIBDYLIB_H
+#define LIBDYLIB_H
+
+#define LIBDYLIB_VERSION 0x020000
+#define LIBDYLIB_VERSION_STR "2.0.0"
+
+#include <stdarg.h>
+#include <stdbool.h>
+
+#if !defined(LIBDYLIB_UNIX) && (defined(__APPLE__) || defined(__linux__) || defined(__UNIX__))
+    #define LIBDYLIB_UNIX
+#elif !defined(LIBDYLIB_WINDOWS) && defined(WIN32)
+    #define LIBDYLIB_WINDOWS
+#endif
+#if !defined(LIBDYLIB_UNIX) && !defined(LIBDYLIB_WINDOWS)
+    #error "Could not detect platform - try defining LIBDYLIB_UNIX or LIBDYLIB_WINDOWS appropriately"
+#endif
+#if defined(LIBDYLIB_UNIX)
+    #if defined(__APPLE__)
+        #ifndef LIBDYLIB_APPLE
+            #define LIBDYLIB_APPLE
+        #endif
+    #elif defined(__linux__) || defined(__linux)
+        #ifndef LIBDYLIB_LINUX
+            #define LIBDYLIB_LINUX
+        #endif
+    #endif
+#endif
+
+#ifdef LIBDYLIB_UNIX
+    #define LIBDYLIB_EXPORT __attribute__((visibility("default")))
+#else
+    #define LIBDYLIB_EXPORT __declspec(dllexport)
+#endif
+
+#ifdef __cplusplus
+    #ifndef LIBDYLIB_C
+        #define LIBDYLIB_CXX
+    #endif
+#else
+    #ifdef LIBDYLIB_CXX
+        #error "Can't build as C++ with a C compiler"
+    #else
+        #define LIBDYLIB_C
+    #endif
+#endif
+
+#ifdef LIBDYLIB_C
+    #define LIBDYLIB_NAME(name) libdylib_##name
+    #define LIBDYLIB_DECLARE(type, name) LIBDYLIB_EXPORT type libdylib_##name
+    #define LIBDYLIB_DEFINE(type, name) type libdylib_##name
+#else
+    #define LIBDYLIB_NAME(name) libdylib::name
+    #define LIBDYLIB_DECLARE(type, name) LIBDYLIB_EXPORT type name
+    #define LIBDYLIB_DEFINE(type, name) type libdylib::name
+#endif
+
+#ifdef LIBDYLIB_CXX
+namespace libdylib {
+#endif
+
+    typedef struct dylib_data* dylib_ref;
+    LIBDYLIB_DECLARE(const void*, get_handle)(dylib_ref lib);
+    LIBDYLIB_DECLARE(const char*, get_path)(dylib_ref lib);
+
+    // attempt to load a dynamic library from a path
+    // return a library handle or NULL
+    LIBDYLIB_DECLARE(dylib_ref, open)(const char *path);
+
+    // return a handle to the current executable
+    LIBDYLIB_DECLARE(dylib_ref, open_self)();
+
+    // close the specified dynamic library
+    // returns 1 on success, 0 on failure
+    LIBDYLIB_DECLARE(bool, close)(dylib_ref lib);
+
+    // attempt to load a dynamic library from all paths given
+    // return a library handle of the first successfully-loaded library, or NULL if none were successfully loaded
+    // NOTE: the last argument must be NULL
+    LIBDYLIB_DECLARE(dylib_ref, open_list)(const char *path, ...);
+    LIBDYLIB_DECLARE(dylib_ref, va_open_list)(const char *path, va_list args);
+
+    // attempt to load a dynamic library using platform-specific prefixes/suffixes
+    // e.g. open_locate("foo") would attempt to open libfoo.so and foo.so on Linux
+    LIBDYLIB_DECLARE(dylib_ref, open_locate)(const char *name);
+
+    // return the address of a symbol in a library, or NULL if the symbol does not exist
+    LIBDYLIB_DECLARE(void*, lookup)(dylib_ref lib, const char *symbol);
+
+    // set the contents of dest to the result of lookup(lib, symbol) and returns 1,
+    // or set dest to NULL and returns 0 if the symbol was not found
+    LIBDYLIB_DECLARE(bool, bind)(dylib_ref lib, const char *symbol, void **dest);
+    // helper macros - note that dest is a simple pointer, NOT a pointer to a pointer
+    #define LIBDYLIB_BIND(lib, symbol, dest) LIBDYLIB_NAME(bind)(lib, symbol, (void**)&dest)
+    #define LIBDYLIB_BINDNAME(lib, name) LIBDYLIB_BIND(lib, #name, name)
+
+    // check for the existence of a symbol in a library
+    LIBDYLIB_DECLARE(bool, find)(dylib_ref lib, const char *symbol);
+
+    // check for the existence of any or all specified symbols in a library, respectively
+    // NOTE: the last argument must be NULL (0 should not be relied on)
+    LIBDYLIB_DECLARE(bool, find_any)(dylib_ref lib, ...);
+    LIBDYLIB_DECLARE(bool, va_find_any)(dylib_ref lib, va_list args);
+    LIBDYLIB_DECLARE(bool, find_all)(dylib_ref lib, ...);
+    LIBDYLIB_DECLARE(bool, va_find_all)(dylib_ref lib, va_list args);
+
+    // returns the last error message set by libdylib functions, or NULL
+    LIBDYLIB_DECLARE(const char*, last_error)();
+
+    // return compiled version information
+    LIBDYLIB_DECLARE(int, get_version)();
+    LIBDYLIB_DECLARE(const char*, get_version_str)();
+
+#ifdef LIBDYLIB_CXX
+}
+#endif
+
+#endif /* LIBDYLIB_H */
diff --git a/litex/build/sim/core/modules.c b/litex/build/sim/core/modules.c
new file mode 100644 (file)
index 0000000..8d7d30a
--- /dev/null
@@ -0,0 +1,164 @@
+/* Copyright (C) 2017 LambdaConcept */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "tinydir.h"
+#include "error.h"
+#include "libdylib.h"
+#include "modules.h"
+
+#ifdef _MSC_VER
+#define LIBEXT "dll"
+#else
+#define LIBEXT "so"
+#endif
+
+static struct ext_module_list_s *modlist=NULL;
+
+
+
+
+int lambdasim_register_ext_module(struct ext_module_s *mod)
+{
+  int ret=RC_OK;
+  struct ext_module_list_s *ml=NULL;
+  
+  if(!mod)
+  {
+    eprintf("Invalid arguments\n");
+    ret=RC_INVARG;
+    goto out;
+  }
+  ml=( struct ext_module_list_s *)malloc(sizeof( struct ext_module_list_s));
+  if(NULL == ml)
+  {
+    ret = RC_NOENMEM;
+    eprintf("Not enough memory\n");
+    goto out;
+  }
+  memset(ml, 0, sizeof( struct ext_module_list_s));
+  ml->module = mod;
+  ml->next = modlist;
+  modlist = ml;
+
+out:
+  
+  return ret;
+}
+
+int lambdasim_load_ext_modules(struct ext_module_list_s **mlist)
+{
+  int ret = RC_OK;
+  tinydir_dir dir;
+  tinydir_file file;
+  dylib_ref lib;
+  int (*lambdasim_ext_module_init)(int (*reg)(struct ext_module_s *));
+  char name[100];
+  if (tinydir_open(&dir, "./modules/") == -1)
+  {
+    ret = RC_ERROR;
+    eprintf("Error opening file");
+    goto out;
+  }
+  if(modlist)
+  {
+    ret=RC_ERROR;
+    eprintf("modules already loaded !\n");
+    goto out;
+  }
+  while(dir.has_next)
+  {
+    if(-1 == tinydir_readfile(&dir, &file))
+    {
+      ret = RC_ERROR;
+      eprintf("Can't get file \n");
+    }
+
+    if(!strcmp(file.extension, LIBEXT))
+    {
+      sprintf(name, "./modules/%s", file.name);
+      lib = libdylib_open(name);
+      if(!lib)
+      {
+       ret = RC_ERROR;
+       eprintf("Can't load library %s\n", libdylib_last_error());
+       goto out;
+      }
+      
+      if(!libdylib_find(lib, "lambdasim_ext_module_init"))
+      {
+       ret = RC_ERROR;
+       eprintf("Module has no lambdasim_ext_module_init function\n");
+       goto out;
+      }
+      LIBDYLIB_BINDNAME(lib, lambdasim_ext_module_init);
+      if(!lambdasim_ext_module_init)
+      {
+       ret = RC_ERROR;
+       eprintf("Can't bind %s\n", libdylib_last_error());
+       goto out;
+      }
+      ret = lambdasim_ext_module_init(lambdasim_register_ext_module);
+      if(RC_OK != ret)
+      {
+       goto out;
+      }
+    }
+    if(-1 == tinydir_next(&dir))
+    {
+      eprintf("Error getting next file\n");
+      ret = RC_ERROR;
+      goto out;
+    }
+  }
+  *mlist = modlist;
+out:
+  tinydir_close(&dir);
+  return ret;
+}
+
+int lambdasim_find_ext_module(struct ext_module_list_s *first, char *name , struct ext_module_list_s **found)
+{
+  struct ext_module_list_s *list = NULL;
+  int ret=RC_OK;
+  
+  if(!first || !name || !found)
+  {
+    ret = RC_INVARG;
+    eprintf("Invalid arg\n");
+    goto out;
+  }
+
+  for(list = first; list; list=list->next)
+  {
+    if(!strcmp(name, list->module->name))
+      break;
+  }
+out:
+  *found = list;
+  return ret;
+}
+
+
+int lambdasim_find_module(struct module_s *first, char *name , struct module_s **found)
+{
+  struct module_s *list = NULL;
+  int ret=RC_OK;
+  
+  if(!first || !name || !found)
+  {
+    ret = RC_INVARG;
+    eprintf("Invalid arg\n");
+    goto out;
+  }
+
+  for(list = first; list; list=list->next)
+  {
+    if(!strcmp(name, list->name))
+      break;
+  }
+out:
+  *found = list;
+  return ret;
+}
diff --git a/litex/build/sim/core/modules.h b/litex/build/sim/core/modules.h
new file mode 100644 (file)
index 0000000..1bfabdf
--- /dev/null
@@ -0,0 +1,43 @@
+/* Copyright (C) 2017 LambdaConcept */
+
+#ifndef __MODULE_H_
+#define __MODULE_H_
+
+#include "pads.h"
+
+struct interface_s {
+  char *name;
+  int index;
+};
+
+struct module_s {
+  char *name;
+  struct interface_s *iface;
+  size_t niface;
+  char tickfirst;
+  char *args;
+  struct module_s *next;
+};
+
+
+
+struct ext_module_s {
+  char *name;
+  int (*start)(void *);
+  int (*new_sess)(void **, char *);
+  int (*add_pads)(void *, struct pad_list_s *);
+  int (*close)(void*);
+  int (*tick)(void*);
+};
+
+struct ext_module_list_s {
+  struct ext_module_s *module;
+  struct ext_module_list_s *next;
+};
+
+
+int lambdasim_file_to_module_list(char *filename, struct module_s **mod);
+int lambdasim_load_ext_modules(struct ext_module_list_s **mlist);
+int lambdasim_find_ext_module(struct ext_module_list_s *first, char *name , struct ext_module_list_s **found);
+
+#endif
diff --git a/litex/build/sim/core/modules/Makefile b/litex/build/sim/core/modules/Makefile
new file mode 100644 (file)
index 0000000..fb770b4
--- /dev/null
@@ -0,0 +1,14 @@
+MODULES = ethernet serial2console serial2tcp clocker
+SHROBJS = $(MODULES:=.so)
+
+.PHONY: $(MODULES)
+all: $(MODULES)
+
+$(MODULES): %:
+       $(MAKE) -C $@
+
+.PHONY: clean
+clean:
+       for module in $(MODULES); do \
+               $(MAKE) -C $$module $@; \
+       done
diff --git a/litex/build/sim/core/modules/clocker/Makefile b/litex/build/sim/core/modules/clocker/Makefile
new file mode 100644 (file)
index 0000000..3fd45b4
--- /dev/null
@@ -0,0 +1,5 @@
+include ../variables.mak
+
+all: $(OBJ_DIR)/clocker.so
+
+include ../rules.mak
diff --git a/litex/build/sim/core/modules/clocker/clocker.c b/litex/build/sim/core/modules/clocker/clocker.c
new file mode 100644 (file)
index 0000000..080ebf3
--- /dev/null
@@ -0,0 +1,113 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "error.h"
+#include "modules.h"
+
+struct session_s {
+  char *sys_clk;
+};
+
+static int lambdasim_module_pads_get( struct pad_s *pads, char *name, void **signal)
+{
+  int ret;
+  void *sig=NULL;
+  int i;
+
+  if(!pads || !name || !signal)
+  {
+    ret=RC_INVARG;
+    goto out;
+  }
+  i = 0;
+  while(pads[i].name)
+  {
+    if(!strcmp(pads[i].name, name))
+    {
+      sig=(void*)pads[i].signal;
+      break;
+    }
+    i++;
+  }
+
+out:
+  *signal=sig;
+  return ret;
+}
+
+static int clocker_start()
+{
+  printf("Loaded !\n");
+  return RC_OK;
+}
+
+static int clocker_new(void **sess, char *args)
+{
+  int ret=RC_OK;
+
+  struct session_s *s=NULL;
+
+  if(!sess)
+  {
+    ret = RC_INVARG;
+    goto out;
+  }
+
+  s=(struct session_s*)malloc(sizeof(struct session_s));
+  if(!s)
+  {
+    ret=RC_NOENMEM;
+    goto out;
+  }
+  memset(s, 0, sizeof(struct session_s));
+out:
+  *sess=(void*)s;
+  return ret;
+  
+}
+
+static int clocker_add_pads(void *sess, struct pad_list_s *plist)
+{
+  int ret=RC_OK;
+  struct session_s *s=(struct session_s*)sess;
+  struct pad_s *pads;
+  if(!sess || !plist)
+  {
+    ret = RC_INVARG;
+    goto out;
+  }
+  pads = plist->pads;
+  
+  if(!strcmp(plist->name, "sys_clk"))  
+  {
+    lambdasim_module_pads_get(pads, "sys_clk", (void**)&s->sys_clk);
+  }
+
+  *s->sys_clk=0;
+out:
+  return ret;
+  
+}
+
+static int clocker_tick(void *sess)
+{
+  struct session_s *s=(struct session_s*)sess;
+  *s->sys_clk = ~(*s->sys_clk);
+  return 0;
+}
+
+static struct ext_module_s ext_mod = {
+  "clocker",
+  clocker_start,
+  clocker_new,
+  clocker_add_pads,
+  NULL,
+  clocker_tick
+};
+
+int lambdasim_ext_module_init(int (*register_module)(struct ext_module_s *))
+{
+  int ret=RC_OK;
+  ret = register_module(&ext_mod);
+  return ret;
+}
diff --git a/litex/build/sim/core/modules/ethernet/Makefile b/litex/build/sim/core/modules/ethernet/Makefile
new file mode 100644 (file)
index 0000000..211b76d
--- /dev/null
@@ -0,0 +1,17 @@
+include ../variables.mak
+CFLAGS += -Itapcfg/src/include
+
+all: $(OBJ_DIR)/ethernet.so
+
+include ../rules.mak
+
+OBJS = $(addprefix $(OBJ_DIR)/, ethernet.o tapcfg.o taplog.o)
+
+$(OBJ_DIR)/ethernet.so: $(OBJS)
+       $(CC) $(LDFLAGS) -Wl,-soname,$@ -o $@ $^
+
+$(OBJ_DIR)/tapcfg.o: tapcfg/src/lib/tapcfg.c
+       $(CC) $(CFLAGS) -c -o $@ $<
+
+$(OBJ_DIR)/taplog.o: tapcfg/src/lib/taplog.c
+       $(CC) $(CFLAGS) -c -o $@ $<
diff --git a/litex/build/sim/core/modules/ethernet/ethernet.c b/litex/build/sim/core/modules/ethernet/ethernet.c
new file mode 100644 (file)
index 0000000..90d3f51
--- /dev/null
@@ -0,0 +1,314 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "error.h"
+
+#include <event2/listener.h>
+#include <event2/util.h>
+#include <event2/event.h>
+#include <json-c/json.h>
+#include "tapcfg.h"
+#include "modules.h"
+
+struct eth_packet_s {
+  char data[2000];
+  size_t len;
+  struct eth_packet_s *next;
+};
+
+struct session_s {
+  char *tx;
+  char *tx_valid;
+  char *tx_ready;
+  char *rx;
+  char *rx_valid;
+  char *rx_ready;
+  char *sys_clk;
+  tapcfg_t *tapcfg;
+  int fd;
+  char databuf[2000];
+  int datalen;
+  char inbuf[2000];
+  int inlen;
+  int insent;
+  struct eth_packet_s *ethpack;
+  struct event *ev;
+};
+
+
+static struct event_base *base=NULL;
+
+int lambdasim_module_get_args( char *args, char *arg, char **val)
+{
+  int ret=RC_OK;
+  json_object *jsobj=NULL;
+  json_object *obj=NULL;
+  char *value=NULL;
+  int r;
+
+  jsobj = json_tokener_parse(args);
+  if(NULL==jsobj)
+  {
+    fprintf(stderr, "Error parsing json arg: %s \n", args);
+    ret=RC_JSERROR;
+    goto out;
+  }
+  if(!json_object_is_type(jsobj, json_type_object))
+  {
+    fprintf(stderr, "Arg must be type object! : %s \n", args);
+    ret=RC_JSERROR;
+    goto out;
+  }
+  obj=NULL;
+  r = json_object_object_get_ex(jsobj, arg, &obj);
+  if(!r)
+  {
+    fprintf(stderr, "Could not find object: \"%s\" (%s)\n", arg, args);
+    ret=RC_JSERROR;
+    goto out;
+  }
+  value=strdup(json_object_get_string(obj));
+
+out:
+  *val = value;
+  return ret;
+}
+
+
+static int lambdasim_module_pads_get( struct pad_s *pads, char *name, void **signal)
+{
+  int ret;
+  void *sig=NULL;
+  int i;
+
+  if(!pads || !name || !signal)
+  {
+    ret=RC_INVARG;
+    goto out;
+  }
+  i = 0;
+  while(pads[i].name)
+  {
+    if(!strcmp(pads[i].name, name))
+    {
+      sig=(void*)pads[i].signal;
+      break;
+    }
+    i++;
+  }
+
+out:
+  *signal=sig;
+  return ret;
+}
+
+static int ethernet_start(void *b)
+{
+  base=(struct event_base *)b;
+  printf("Loaded eth %p!\n", base);
+  return RC_OK;
+}
+
+
+
+void event_handler(int fd, short event, void *arg)
+{
+  struct  session_s *s=(struct session_s*)arg;
+  struct eth_packet_s *ep;
+  struct eth_packet_s *tep;
+  
+  
+  
+  if (event & EV_TIMEOUT) {
+    //printf("timeout\n");
+  } else if (event & EV_READ) {
+    ep = malloc(sizeof(struct eth_packet_s));
+    memset(ep, 0, sizeof(struct eth_packet_s));
+    ep->len = tapcfg_read(s->tapcfg, ep->data, 2000);
+    if(ep->len < 60)
+    {
+      ep->len = 60;
+    }
+    
+    if(!s->ethpack)
+    {
+      s->ethpack = ep;
+    } else {
+      for(tep=s->ethpack; tep->next; tep=tep->next);
+      tep->next = ep;
+    }
+  }
+}
+static const char macadr[6] = {0xaa, 0xb6, 0x24, 0x69, 0x77, 0x21};
+
+static int ethernet_new(void **sess, char *args)
+{
+  int ret=RC_OK;
+  char *c_tap=NULL;
+  char *c_tap_ip=NULL;
+  struct session_s *s=NULL;
+  struct timeval tv={10, 0};
+  if(!sess)
+  {
+    ret = RC_INVARG;
+    goto out;
+  }
+
+  s=(struct session_s*)malloc(sizeof(struct session_s));
+  if(!s)
+  {
+    ret=RC_NOENMEM;
+    goto out;
+  }
+  memset(s, 0, sizeof(struct session_s));
+
+  ret = lambdasim_module_get_args(args, "interface", &c_tap);
+  {
+    if(RC_OK != ret)
+    {
+      goto out;
+    }
+  }
+  ret = lambdasim_module_get_args(args, "ip", &c_tap_ip);
+  {
+    if(RC_OK != ret)
+    {
+      goto out;
+    }
+  }
+
+  s->tapcfg = tapcfg_init();
+  tapcfg_start(s->tapcfg, c_tap, 0);
+  s->fd = tapcfg_get_fd(s->tapcfg);
+  tapcfg_iface_set_hwaddr(s->tapcfg, macadr, 6);
+  tapcfg_iface_set_ipv4(s->tapcfg, c_tap_ip, 24);
+  tapcfg_iface_set_status(s->tapcfg, TAPCFG_STATUS_ALL_UP);
+  free(c_tap);
+  free(c_tap_ip);
+  printf("FT:%d\n", s->fd);
+  printf("ETHERNET MODULE NEW CALLED\n");
+
+  s->ev = event_new(base, s->fd, EV_READ | EV_PERSIST, event_handler, s);
+  event_add(s->ev, &tv);
+  
+out:
+  *sess=(void*)s;
+  return ret;
+  
+}
+
+static int ethernet_add_pads(void *sess, struct pad_list_s *plist)
+{
+  int ret=RC_OK;
+  struct session_s *s=(struct session_s*)sess;
+  struct pad_s *pads;
+  if(!sess || !plist)
+  {
+    ret = RC_INVARG;
+    goto out;
+  }
+  pads = plist->pads;
+  if(!strcmp(plist->name, "eth"))
+  {
+    lambdasim_module_pads_get(pads, "sink_data", (void**)&s->rx);
+    lambdasim_module_pads_get(pads, "sink_valid", (void**)&s->rx_valid);
+    lambdasim_module_pads_get(pads, "sink_ready", (void**)&s->rx_ready);
+    lambdasim_module_pads_get(pads, "source_data", (void**)&s->tx);
+    lambdasim_module_pads_get(pads, "source_valid", (void**)&s->tx_valid);
+    lambdasim_module_pads_get(pads, "source_ready", (void**)&s->tx_ready);
+  }
+  
+  if(!strcmp(plist->name, "sys_clk"))  
+  {
+    lambdasim_module_pads_get(pads, "sys_clk", (void**)&s->sys_clk);
+  }
+
+out:
+  return ret;
+  
+}
+static int ethernet_tick(void *sess)
+{
+  char c;
+  struct session_s *s=(struct session_s*)sess;
+  struct eth_packet_s *pep;
+  
+  if(*s->sys_clk == 0)
+  {
+    return RC_OK;
+  }
+
+
+  *s->tx_ready = 1;
+  if(*s->tx_valid == 1)
+  {
+    c = *s->tx;
+   //printf("%02x ", (unsigned char)c);
+    s->databuf[s->datalen++]=c;
+  } else {
+    if(s->datalen)
+    {
+      //printf("send fini\n");
+      tapcfg_write(s->tapcfg, s->databuf, s->datalen);
+      s->datalen=0;
+    }
+  }
+  
+  *s->rx_valid=0;
+  if(s->inlen)
+  {
+    *s->rx_valid=1;
+    *s->rx = s->inbuf[s->insent++];
+    //printf("%02x ", (unsigned char)*s->rx);
+    //printf("%d", *s->rx_ready);
+    if(s->insent == s->inlen)
+    {
+      //printf("recv fini\n");
+      s->insent =0;
+      s->inlen = 0;
+    }
+  }
+  else
+  {
+    if(s->ethpack)
+    {
+      memcpy(s->inbuf, s->ethpack->data, s->ethpack->len);
+      s->inlen = s->ethpack->len;
+      pep=s->ethpack->next;
+      free(s->ethpack);
+      s->ethpack=pep;
+    }
+  }
+  /*
+  else
+  {
+  if(tapcfg_wait_readable(s->tapcfg, 0))
+    {
+      memset(s->inbuf, 0, 2000);
+      s->inlen =  tapcfg_read(s->tapcfg, s->inbuf, 2000);
+      
+      if(s->inlen < 60)
+      {
+       s->inlen = 60;
+      }
+    }
+  }
+  */
+  return RC_OK;
+}
+
+static struct ext_module_s ext_mod = {
+  "ethernet",
+  ethernet_start,
+  ethernet_new,
+  ethernet_add_pads,
+  NULL,
+  ethernet_tick
+};
+
+int lambdasim_ext_module_init(int (*register_module)(struct ext_module_s *))
+{
+  int ret=RC_OK;
+  ret = register_module(&ext_mod);
+  return ret;
+}
diff --git a/litex/build/sim/core/modules/rules.mak b/litex/build/sim/core/modules/rules.mak
new file mode 100644 (file)
index 0000000..7cbe265
--- /dev/null
@@ -0,0 +1,9 @@
+$(OBJ_DIR)/%.o: %.c
+       $(CC) -c $(CFLAGS) -I../.. -o $@ $<
+
+$(OBJ_DIR)/%.so: $(OBJ_DIR)/%.o
+       $(CC) $(LDFLAGS) -Wl,-soname,$@ -o $@ $<
+
+.PHONY: clean
+clean:
+       rm -f $(OBJ_DIR)/*.o $(OBJ_DIR)/*.so
diff --git a/litex/build/sim/core/modules/serial2console/Makefile b/litex/build/sim/core/modules/serial2console/Makefile
new file mode 100644 (file)
index 0000000..7d752ba
--- /dev/null
@@ -0,0 +1,5 @@
+include ../variables.mak
+
+all: $(OBJ_DIR)/serial2console.so
+
+include ../rules.mak
diff --git a/litex/build/sim/core/modules/serial2console/serial2console.c b/litex/build/sim/core/modules/serial2console/serial2console.c
new file mode 100644 (file)
index 0000000..850a56a
--- /dev/null
@@ -0,0 +1,208 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "error.h"
+#include <unistd.h>
+#include <event2/listener.h>
+#include <event2/util.h>
+#include <event2/event.h>
+#include <termios.h>
+
+#include "modules.h"
+
+struct session_s {
+  char *tx;
+  char *tx_valid;
+  char *tx_ready;
+  char *rx;
+  char *rx_valid;
+  char *rx_ready;
+  char *sys_clk;
+  struct event *ev;
+  char databuf[2048];
+  int data_start;
+  int datalen;
+};
+
+struct event_base *base;
+static int lambdasim_module_pads_get( struct pad_s *pads, char *name, void **signal)
+{
+  int ret;
+  void *sig=NULL;
+  int i;
+
+  if(!pads || !name || !signal)
+  {
+    ret=RC_INVARG;
+    goto out;
+  }
+  i = 0;
+  while(pads[i].name)
+  {
+    if(!strcmp(pads[i].name, name))
+    {
+      sig=(void*)pads[i].signal;
+      break;
+    }
+    i++;
+  }
+
+out:
+  *signal=sig;
+  return ret;
+}
+
+
+
+void set_conio_terminal_mode(void)
+{
+  struct termios new_termios;
+
+  tcgetattr(0, &new_termios);
+  cfmakeraw(&new_termios);
+  tcsetattr(0, TCSANOW, &new_termios);
+}
+
+
+
+static int serial2console_start(void *b)
+{
+  base =(struct event_base *)b;
+  printf("Loaded %p!\n", base);
+  // set_conio_terminal_mode();
+  return RC_OK;
+}
+
+void read_handler(int fd, short event, void *arg)
+{
+  struct session_s *s= (struct session_s*)arg;
+  char buffer[1024];
+  ssize_t read_len;
+  
+  int i;
+  read_len = read(fd, buffer, 1024);
+  for(i = 0; i < read_len; i++)
+  {
+    s->databuf[(s->data_start +  s->datalen ) % 2048] = buffer[i];
+    s->datalen++;
+  }
+}
+
+static void event_handler(int fd, short event, void *arg)
+{
+  //printf("hit\n");
+  if (event & EV_READ)
+  {
+    read_handler(fd, event, arg);
+  }
+}
+
+
+static int serial2console_new(void **sess, char *args)
+{
+  int ret=RC_OK;
+  struct timeval tv = {1, 0};
+  struct session_s *s=NULL;
+
+  if(!sess)
+  {
+    ret = RC_INVARG;
+    goto out;
+  }
+  
+  s=(struct session_s*)malloc(sizeof(struct session_s));
+  if(!s)
+  {
+    ret=RC_NOENMEM;
+    goto out;
+  }
+  memset(s, 0, sizeof(struct session_s));
+  s->ev = event_new(base, fileno(stdin), EV_READ | EV_PERSIST , event_handler, s);
+  event_add(s->ev, &tv);
+  
+out:
+  *sess=(void*)s;
+  return ret;
+  
+}
+
+static int serial2console_add_pads(void *sess, struct pad_list_s *plist)
+{
+  int ret=RC_OK;
+  struct session_s *s=(struct session_s*)sess;
+  struct pad_s *pads;
+  if(!sess || !plist)
+  {
+    ret = RC_INVARG;
+    goto out;
+  }
+  pads = plist->pads;
+  if(!strcmp(plist->name, "serial"))
+  {
+    lambdasim_module_pads_get(pads, "sink_data", (void**)&s->rx);
+    lambdasim_module_pads_get(pads, "sink_valid", (void**)&s->rx_valid);
+    lambdasim_module_pads_get(pads, "sink_ready", (void**)&s->rx_ready);
+    lambdasim_module_pads_get(pads, "source_data", (void**)&s->tx);
+    lambdasim_module_pads_get(pads, "source_valid", (void**)&s->tx_valid);
+    lambdasim_module_pads_get(pads, "source_ready", (void**)&s->tx_ready);
+  }
+  
+  if(!strcmp(plist->name, "sys_clk"))  
+  {
+    lambdasim_module_pads_get(pads, "sys_clk", (void**)&s->sys_clk);
+
+  }
+
+out:
+  return ret;
+  
+}
+static int serial2console_tick(void *sess)
+{
+
+  struct session_s *s=(struct session_s*)sess;
+  if(*s->sys_clk == 0)
+  {
+    return RC_OK;
+  }
+  *s->tx_ready = 1;
+  if(*s->tx_valid)
+  {
+    if(*s->tx == '\n')
+    {
+      printf("\r");
+    }
+    printf("%c", *s->tx);
+    fflush(stdout);
+  }
+
+
+
+  *s->rx_valid=0;
+  if(s->datalen)
+  {
+    
+    *s->rx=s->databuf[s->data_start];
+    s->data_start = (s->data_start + 1) % 2048;
+    s->datalen--;
+    *s->rx_valid=1;
+  }
+  
+  return RC_OK;
+}
+
+static struct ext_module_s ext_mod = {
+  "serial2console",
+  serial2console_start,
+  serial2console_new,
+  serial2console_add_pads,
+  NULL,
+  serial2console_tick
+};
+
+int lambdasim_ext_module_init(int (*register_module)(struct ext_module_s *))
+{
+  int ret=RC_OK;
+  ret = register_module(&ext_mod);
+  return ret;
+}
diff --git a/litex/build/sim/core/modules/serial2tcp/Makefile b/litex/build/sim/core/modules/serial2tcp/Makefile
new file mode 100644 (file)
index 0000000..4036444
--- /dev/null
@@ -0,0 +1,5 @@
+include ../variables.mak
+
+all: $(OBJ_DIR)/serial2tcp.so
+
+include ../rules.mak
diff --git a/litex/build/sim/core/modules/serial2tcp/serial2tcp.c b/litex/build/sim/core/modules/serial2tcp/serial2tcp.c
new file mode 100644 (file)
index 0000000..fbe539d
--- /dev/null
@@ -0,0 +1,300 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "error.h"
+#include <unistd.h>
+#include <event2/listener.h>
+#include <event2/util.h>
+#include <event2/event.h>
+
+#include <json-c/json.h>
+#include "modules.h"
+
+struct session_s {
+  char *tx;
+  char *tx_valid;
+  char *tx_ready;
+  char *rx;
+  char *rx_valid;
+  char *rx_ready;
+  char *sys_clk;
+  struct event *ev;
+  char databuf[2048];
+  int data_start;
+  int datalen;
+  int fd;
+};
+
+struct event_base *base;
+
+
+int lambdasim_module_get_args( char *args, char *arg, char **val)
+{
+  int ret=RC_OK;
+  json_object *jsobj=NULL;
+  json_object *obj=NULL;
+  char *value=NULL;
+  int r;
+
+  jsobj = json_tokener_parse(args);
+  if(NULL==jsobj)
+  {
+    fprintf(stderr, "Error parsing json arg: %s \n", args);
+    ret=RC_JSERROR;
+    goto out;
+  }
+  if(!json_object_is_type(jsobj, json_type_object))
+  {
+    fprintf(stderr, "Arg must be type object! : %s \n", args);
+    ret=RC_JSERROR;
+    goto out;
+  }
+  obj=NULL;
+  r = json_object_object_get_ex(jsobj, arg, &obj);
+  if(!r)
+  {
+    fprintf(stderr, "Could not find object: \"%s\" (%s)\n", arg, args);
+    ret=RC_JSERROR;
+    goto out;
+  }
+  value=strdup(json_object_get_string(obj));
+
+out:
+  *val = value;
+  return ret;
+}
+
+
+
+static int lambdasim_module_pads_get( struct pad_s *pads, char *name, void **signal)
+{
+  int ret;
+  void *sig=NULL;
+  int i;
+
+  if(!pads || !name || !signal)
+  {
+    ret=RC_INVARG;
+    goto out;
+  }
+  i = 0;
+  while(pads[i].name)
+  {
+    if(!strcmp(pads[i].name, name))
+    {
+      sig=(void*)pads[i].signal;
+      break;
+    }
+    i++;
+  }
+
+out:
+  *signal=sig;
+  return ret;
+}
+
+
+
+static int serial2tcp_start(void *b)
+{
+  base =(struct event_base *)b;
+  printf("Loaded %p!\n", base);
+  return RC_OK;
+}
+
+void read_handler(int fd, short event, void *arg)
+{
+  struct session_s *s= (struct session_s*)arg;
+  char buffer[1024];
+  ssize_t read_len;
+  
+  int i;
+  
+  read_len = read(fd, buffer, 1024);
+  for(i = 0; i < read_len; i++)
+  {
+    s->databuf[(s->data_start +  s->datalen ) % 2048] = buffer[i];
+    s->datalen++;
+  }
+}
+
+static void event_handler(int fd, short event, void *arg)
+{
+  //printf("hit\n");
+  if (event & EV_READ)
+  {
+    read_handler(fd, event, arg);
+  }
+}
+
+
+
+
+static void accept_conn_cb(struct evconnlistener *listener, evutil_socket_t fd, struct sockaddr *address, int socklen,  void *ctx)
+{
+  struct session_s *s= (struct session_s*)ctx;
+  struct timeval tv = {1, 0};
+  
+  s->fd = fd;
+  s->ev = event_new(base, fd, EV_READ | EV_PERSIST , event_handler, s);
+  event_add(s->ev, &tv);  
+}
+
+static void
+accept_error_cb(struct evconnlistener *listener, void *ctx)
+{
+  struct event_base *base = evconnlistener_get_base(listener);
+  eprintf("ERRROR\n");
+
+  event_base_loopexit(base, NULL);
+}
+
+static int serial2tcp_new(void **sess, char *args)
+{
+  int ret=RC_OK;
+  struct session_s *s=NULL;
+  char *cport=NULL;
+  int port;
+  struct evconnlistener *listener;
+  struct sockaddr_in sin;
+
+  
+  if(!sess)
+  {
+    ret = RC_INVARG;
+    goto out;
+  }
+
+
+  ret = lambdasim_module_get_args(args, "port", &cport);
+  {
+    if(RC_OK != ret)
+    {
+      goto out;
+    }
+  }
+
+  printf("Found port %s\n", cport);
+  sscanf(cport, "%d", &port);
+  free(cport);
+  if(!port)
+  {
+    ret=RC_ERROR;
+    fprintf(stderr, "Invalid port selected!\n");
+    goto out;
+  }
+  
+  
+  s=(struct session_s*)malloc(sizeof(struct session_s));
+  if(!s)
+  {
+    ret=RC_NOENMEM;
+    goto out;
+  }
+  memset(s, 0, sizeof(struct session_s));
+
+  memset(&sin, 0, sizeof(sin));
+  sin.sin_family = AF_INET;
+  sin.sin_addr.s_addr = htonl(0);
+  sin.sin_port = htons(port);
+  listener = evconnlistener_new_bind(base, accept_conn_cb, s,  LEV_OPT_CLOSE_ON_FREE|LEV_OPT_REUSEABLE, -1, (struct sockaddr*)&sin, sizeof(sin));
+  if (!listener)
+  {
+    ret=RC_ERROR;
+    eprintf("Can't bind port %d\n!\n", port);
+    goto out;
+  }
+  evconnlistener_set_error_cb(listener, accept_error_cb);
+  
+  
+  
+  
+out:
+  *sess=(void*)s;
+  return ret;
+  
+}
+
+static int serial2tcp_add_pads(void *sess, struct pad_list_s *plist)
+{
+  int ret=RC_OK;
+  struct session_s *s=(struct session_s*)sess;
+  struct pad_s *pads;
+  if(!sess || !plist)
+  {
+    ret = RC_INVARG;
+    goto out;
+  }
+  pads = plist->pads;
+  if(!strcmp(plist->name, "serial"))
+  {
+    lambdasim_module_pads_get(pads, "sink_data", (void**)&s->rx);
+    lambdasim_module_pads_get(pads, "sink_valid", (void**)&s->rx_valid);
+    lambdasim_module_pads_get(pads, "sink_ready", (void**)&s->rx_ready);
+    lambdasim_module_pads_get(pads, "source_data", (void**)&s->tx);
+    lambdasim_module_pads_get(pads, "source_valid", (void**)&s->tx_valid);
+    lambdasim_module_pads_get(pads, "source_ready", (void**)&s->tx_ready);
+  }
+  
+  if(!strcmp(plist->name, "sys_clk"))  
+  {
+    lambdasim_module_pads_get(pads, "sys_clk", (void**)&s->sys_clk);
+
+  }
+
+out:
+  return ret;
+  
+}
+static int serial2tcp_tick(void *sess)
+{
+  char c;
+  int ret=RC_OK;
+  
+  struct session_s *s=(struct session_s*)sess;
+  if(*s->sys_clk == 0)
+  {
+    return RC_OK;
+  }
+
+  
+  *s->tx_ready = 1;
+  if(s->fd && *s->tx_valid)
+  {
+    c = *s->tx;
+    if(-1 ==write(s->fd, &c, 1))
+    {
+      eprintf("Error writing on socket\n");
+      ret = RC_ERROR;
+      goto out;
+    }
+  }
+  
+  *s->rx_valid=0;
+  if(s->datalen)
+  {
+    *s->rx=s->databuf[s->data_start];
+    s->data_start = (s->data_start + 1) % 2048;
+    s->datalen--;
+    *s->rx_valid=1;
+  }
+  
+out:
+  return ret;
+}
+
+static struct ext_module_s ext_mod = {
+  "serial2tcp",
+  serial2tcp_start,
+  serial2tcp_new,
+  serial2tcp_add_pads,
+  NULL,
+  serial2tcp_tick
+};
+
+int lambdasim_ext_module_init(int (*register_module)(struct ext_module_s *))
+{
+  int ret=RC_OK;
+  ret = register_module(&ext_mod);
+  return ret;
+}
diff --git a/litex/build/sim/core/modules/variables.mak b/litex/build/sim/core/modules/variables.mak
new file mode 100644 (file)
index 0000000..7644176
--- /dev/null
@@ -0,0 +1,5 @@
+CC = gcc
+CFLAGS = -Wall -O3 -ggdb -fPIC -Werror
+LDFLAGS = -levent -shared -fPIC
+
+OBJ_DIR ?= .
diff --git a/litex/build/sim/core/pads.c b/litex/build/sim/core/pads.c
new file mode 100644 (file)
index 0000000..232bfee
--- /dev/null
@@ -0,0 +1,79 @@
+/* Copyright (C) 2017 LambdaConcept */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "error.h"
+#include "pads.h"
+
+static struct pad_list_s *padlist=NULL;
+int lambdasim_register_pads(struct pad_s *pads, char *interface_name, int index)
+{
+  int ret = RC_OK;
+
+  struct pad_list_s *pl=NULL;
+  if(!pads || !interface_name)
+  {
+    ret = RC_INVARG;
+    eprintf("Invalid argument\n");
+    goto out;    
+  }
+
+  pl = (struct pad_list_s *)malloc(sizeof(struct pad_list_s));
+  if(NULL == pl)
+  {
+    ret = RC_NOENMEM;
+    eprintf("Not enough mem\n");
+    goto out;
+  }
+
+  memset(pl, 0, sizeof(struct pad_list_s));
+
+  pl->index = index; /* Do we really need it ?*/
+  pl->name = strdup(interface_name);
+  pl->pads = pads;
+
+  pl->next = padlist;
+  padlist = pl;
+  
+out:
+  return ret;
+}
+
+int lambdasim_pads_get_list(struct pad_list_s **plist)
+{
+  int ret=RC_OK;
+
+  
+  if(!plist)
+  {
+    ret = RC_INVARG;
+    eprintf("Invalid argument\n");
+    goto out;
+  }
+
+  *plist = padlist;
+out:
+  return ret;
+}
+
+int lambdasim_pads_find(struct pad_list_s *first, char *name, int index, struct pad_list_s **found)
+{
+  struct pad_list_s *list = NULL;
+  int ret=RC_OK;
+  if(!first || !name || !found)
+  {
+    ret = RC_INVARG;
+    eprintf("Invalid arg\n");
+    goto out;
+  }
+
+  for(list = first; list; list=list->next)
+  {
+    if(!strcmp(name, list->name) && (list->index == index))
+      break;
+  }
+out:
+  *found = list;
+  return ret;
+}
diff --git a/litex/build/sim/core/pads.h b/litex/build/sim/core/pads.h
new file mode 100644 (file)
index 0000000..8385cb2
--- /dev/null
@@ -0,0 +1,28 @@
+/* Copyright (C) 2017 LambdaConcept */
+
+#ifndef __PADS_H_
+#define __PADS_H_
+
+struct pad_s {
+  char *name;
+  size_t len;
+  void *signal;
+};
+
+struct pad_list_s {
+  char *name;
+  struct pad_s *pads;
+  int index;
+  struct pad_list_s *next;
+};
+
+int lambdasim_pads_get_list(struct pad_list_s **plist);
+int lambdasim_pads_find(struct pad_list_s *first, char *name, int index,  struct pad_list_s **found);
+  
+#ifdef __cplusplus
+extern "C" int lambdasim_register_pads(struct pad_s *pads, char *interface_name, int index);
+#else
+int lambdasim_register_pads(struct pad_s *pads, char *interface_name, int index);
+#endif
+
+#endif
diff --git a/litex/build/sim/core/parse.c b/litex/build/sim/core/parse.c
new file mode 100644 (file)
index 0000000..a969f1f
--- /dev/null
@@ -0,0 +1,339 @@
+/* Copyright (C) 2017 LambdaConcept */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <json-c/json.h>
+#include "error.h"
+#include "modules.h"
+
+static int file_to_js(char *filename, json_object **obj)
+{
+  int ret = RC_OK;
+  struct json_tokener *tok=NULL;
+  json_object *pobj=NULL;
+  enum json_tokener_error jerr;
+  FILE *in=NULL;
+  int linenum=0;
+  char *lineptr=NULL;
+  size_t len, len2;
+
+  if(!filename || !obj)
+  {
+    ret=RC_INVARG;
+    goto out;
+  }
+
+  in = fopen(filename, "r");
+  if(!in)
+  {
+    eprintf("Can't open configuration file: %s\n", filename);
+    ret= RC_ERROR;
+    goto out;
+  }
+
+  tok = json_tokener_new();
+  if(!tok)
+  {
+    ret=RC_ERROR;
+    eprintf("Can't create new tokener\n");
+    goto out;
+  }
+
+  do
+  {
+    linenum++;
+    len=32768;
+    len2 = getline(&lineptr, &len, in);
+    if(len2 == -1)
+    {
+      ret=RC_ERROR;
+      eprintf("End of file !\n");
+      goto out;
+    }
+    pobj = json_tokener_parse_ex(tok, lineptr, len2);
+    if((jerr = json_tokener_get_error(tok)) == json_tokener_success)
+    {
+      break;
+    }
+
+    if((jerr = json_tokener_get_error(tok)) != json_tokener_continue)
+    {
+      fprintf(stderr, "ERROR in %s:\n", filename);
+      fprintf(stderr, "line:%d:\n%s",linenum, lineptr);
+      jerr = json_tokener_get_error(tok);
+      fprintf(stderr, "json parse error: %s\n", json_tokener_error_desc(jerr));
+      goto out;
+    }
+    free(lineptr);
+    lineptr=NULL;
+  }while(1);
+
+  *obj=pobj;
+  pobj=NULL;
+out:
+
+  if(pobj)
+  {
+    json_object_put(pobj);
+  }
+  if(tok)
+  {
+    json_tokener_free(tok);
+  }
+  if(lineptr)
+  {
+    free(lineptr);
+  }
+  if(in)
+  {
+    fclose(in);
+  }
+  return ret;
+}
+
+static int json_to_interface_list(json_object *interface, struct interface_s **iface)
+{
+  int ret=RC_OK;
+  int n, i;
+  json_object *obj;
+  json_object *name;
+  json_object *index;
+  
+  struct interface_s *t_iface=NULL;
+
+  if(!interface || !iface)
+  {
+    ret = RC_INVARG;
+    eprintf("Invalid argument\n");
+    goto out;
+  }
+  
+  if(!json_object_is_type(interface, json_type_array))
+  {
+    ret=RC_JSERROR;
+    eprintf("Interface must be an array\n");
+    goto out;
+  }
+
+  n = json_object_array_length(interface);
+  t_iface = (struct interface_s *)malloc(sizeof(struct interface_s) * (n + 1));
+  if(!t_iface)
+  {
+    ret=  RC_NOENMEM;
+    eprintf("Not enough memory\n");
+    goto out;
+  }
+  memset(t_iface, 0,sizeof(struct interface_s) * (n + 1));
+
+  for(i = 0; i < n; i++)
+  {
+    obj = json_object_array_get_idx(interface, i);
+    if(json_object_is_type(obj, json_type_object))
+    {
+      if(!json_object_object_get_ex(obj, "name", &name))
+      {
+       ret=RC_JSERROR;
+       eprintf("Module interface must have a name (%s)!\n", json_object_to_json_string(obj));
+       goto out;
+      }
+      t_iface[i].name = strdup(json_object_get_string(name));
+
+      if(json_object_object_get_ex(obj, "index", &index))
+      {
+       if(!json_object_is_type(index, json_type_int))
+       {
+         ret = RC_JSERROR;
+         eprintf("Interface Index must be an int ! (%s)\n", json_object_to_json_string(obj));
+       }
+       t_iface[i].index = json_object_get_int(index);
+      }
+    }
+    if(json_object_is_type(obj, json_type_string))
+    {
+      t_iface[i].name = strdup(json_object_get_string(obj));
+    }
+  }
+  *iface = t_iface;
+  t_iface = NULL;
+out:
+  if(t_iface)
+  {
+    free(t_iface);
+  }
+  return ret;
+}
+
+static int module_list_free(struct module_s *mod)
+{
+  int ret=RC_OK; 
+  struct module_s *mnext;
+  int i;
+  while(mod)
+  {
+    mnext = mod->next;
+    if(mod->iface)
+    {
+      for(i = 0; i < mod->niface; i++)
+      {
+       if(mod->iface[i].name)
+       {
+         free(mod->iface[i].name);
+       }
+      }
+      free(mod->iface);
+      if(mod->name)
+      {
+       free(mod->name);
+      }
+      if(mod->args)
+      {
+       free(mod->args);
+      }
+    }
+    free(mod);
+    mod = mnext;
+  }
+  return ret;
+}
+
+static int json_to_module_list(json_object *obj, struct module_s **mod)
+{
+  struct module_s *m=NULL;
+  struct module_s *first=NULL;
+
+  json_object *tobj;
+  int ret=RC_OK;
+  int i, n, len;
+  json_object *name;
+  json_object *args;
+  json_object *interface;
+  json_object *tickfirst;
+
+  if(!obj || !mod)
+  {
+    ret = RC_INVARG;
+    eprintf("Wrong arguments\n");
+    goto out;
+  }
+
+  if(!json_object_is_type(obj, json_type_array))
+  {
+    ret=RC_JSERROR;
+    eprintf("Config file must be an array\n");
+    goto out;
+  }
+
+  n = json_object_array_length(obj);
+  for(i = 0; i < n; i++)
+  {
+    tobj = json_object_array_get_idx(obj, i);
+    if(!json_object_object_get_ex(tobj, "module", &name))
+    {
+      ret=RC_JSERROR;
+      eprintf("expected \"module\" in object (%s)\n", json_object_to_json_string(tobj));
+      goto out;
+    }
+
+    if(!json_object_object_get_ex(tobj, "interface", &interface))
+    {
+      ret=RC_JSERROR;
+      eprintf("expected \"interface\" in object (%s)\n", json_object_to_json_string(tobj));
+      goto out;
+    }
+
+    args = NULL;
+    json_object_object_get_ex(tobj, "args", &args);
+
+    tickfirst=NULL;
+    json_object_object_get_ex(tobj, "tickfirst", &tickfirst);
+        
+
+    if(m)
+    {
+      m->next=(struct module_s *)malloc(sizeof(struct module_s));
+      m=m->next;
+    }
+    else
+    {
+      m=(struct module_s *)malloc(sizeof(struct module_s));
+    }
+    if(!m)
+    {
+      ret = RC_NOENMEM;
+      eprintf("Not enough memory\n");
+      goto out;
+    }
+    if(!first)
+    {
+      first = m;
+    }
+    memset(m, 0, sizeof(struct module_s));
+    ret = json_to_interface_list(interface, &m->iface);
+    if(RC_OK != ret)
+    {
+      goto out;
+    }
+    len = 0;
+    
+    while(m->iface[len++].name);
+    m->niface= len-1;
+    m->name = strdup(json_object_get_string(name));
+    if(args)
+    {
+      m->args = strdup(json_object_to_json_string(args));
+    }
+    if(tickfirst)
+    {
+      m->tickfirst = json_object_get_boolean(tickfirst);
+    }
+  }
+  *mod = first;
+  first=NULL;
+
+out:
+  if(first)
+  {
+    module_list_free(first);
+  }
+  return ret;
+}
+
+int lambdasim_file_to_module_list(char *filename, struct module_s **mod)
+{
+  struct module_s *m=NULL;
+  json_object *obj=NULL;
+  int ret=RC_OK;
+
+  if(!filename || !mod)
+  {
+    ret = RC_INVARG;
+    eprintf("Invalid argument\n");
+    goto out;
+  }
+
+  ret = file_to_js(filename, &obj);
+  if(RC_OK != ret)
+  {
+    goto out;
+  }
+
+  ret = json_to_module_list(obj, &m);
+  if(RC_OK != ret)
+  {
+    goto out;
+  }
+
+  *mod = m;
+  m = NULL;
+out:
+  if(m)
+  {
+    module_list_free(m);
+  }
+  if(obj)
+  {
+    json_object_put(obj);
+  }
+  return ret;
+}
diff --git a/litex/build/sim/core/sim.c b/litex/build/sim/core/sim.c
new file mode 100644 (file)
index 0000000..9c87380
--- /dev/null
@@ -0,0 +1,244 @@
+/* Copyright (C) 2017 LambdaConcept */
+
+#include <string.h>
+#include <errno.h>
+#include <stdio.h>
+#include <signal.h>
+#ifndef _WIN32
+#include <netinet/in.h>
+# ifdef _XOPEN_SOURCE_EXTENDED
+#  include <arpa/inet.h>
+# endif
+#include <sys/socket.h>
+#endif
+#include <stdlib.h>
+#include "error.h"
+#include "modules.h"
+#include "pads.h"
+#include "veril.h"
+
+#include <event2/listener.h>
+#include <event2/util.h>
+#include <event2/event.h>
+
+void lambdasim_init(void **out);
+
+
+struct session_list_s {
+  void *session;
+  char tickfirst;
+  struct ext_module_s *module;
+  struct session_list_s *next;
+};
+
+struct session_list_s *sesslist=NULL;
+
+static int lambdasim_initialize_all(void **dut, void *base)
+{
+  struct module_s *ml=NULL;
+  struct module_s *mli=NULL;
+  struct ext_module_list_s *mlist=NULL;
+  struct ext_module_list_s *pmlist=NULL;
+  //struct ext_module_list_s *mlisti=NULL;
+  struct pad_list_s *plist=NULL;
+  struct pad_list_s *pplist=NULL;
+  struct session_list_s *slist=NULL;
+  void *vdut=NULL;
+  int i;
+  int ret = RC_OK;
+  
+  /* Load external modules */
+  ret = lambdasim_load_ext_modules(&mlist);
+  if(RC_OK != ret)
+  {
+    goto out;
+  }
+  for(pmlist = mlist; pmlist; pmlist=pmlist->next)
+  {
+    if(pmlist->module->start)
+    {
+      pmlist->module->start(base);
+    }
+  }
+
+  
+  /* Load configuration */
+  ret = lambdasim_file_to_module_list("sim_config.js", &ml);
+  if(RC_OK != ret)
+  {
+    goto out;
+  }
+  /* Init generated */
+  lambdasim_init(&vdut);
+  
+  /* Get pads from generated */
+  ret = lambdasim_pads_get_list(&plist);
+  if(RC_OK != ret)
+  {
+    goto out;
+  }
+  
+  for(mli = ml; mli; mli=mli->next)
+  {
+
+    /* Find the module in the external module */
+    pmlist = NULL;
+    ret = lambdasim_find_ext_module(mlist, mli->name, &pmlist );
+    if(RC_OK != ret)
+    {
+      goto out;
+    }
+    if(NULL == pmlist)
+    {
+      eprintf("Could not find module %s\n", mli->name);
+      continue;
+    }
+    
+    slist=(struct session_list_s *)malloc(sizeof(struct session_list_s));
+    if(NULL == slist)
+    {
+      ret = RC_NOENMEM;
+      goto out;
+    }
+    memset(slist, 0, sizeof(struct session_list_s));
+
+    slist->tickfirst = mli->tickfirst;
+    slist->module = pmlist->module;
+    slist->next = sesslist;
+    ret = pmlist->module->new_sess(&slist->session, mli->args);
+    if(RC_OK != ret)
+    {
+      goto out;
+    }
+    sesslist = slist;
+    
+    /* For each interface */
+    for(i = 0; i < mli->niface; i++)
+    {
+      /*Find the pads */
+      pplist=NULL;
+      ret = lambdasim_pads_find(plist, mli->iface[i].name, mli->iface[i].index, &pplist);
+      if(RC_OK != ret)
+      {
+       goto out;
+      }
+      if(NULL == pplist)
+      {
+       eprintf("Could not find interface %s with index %d\n", mli->iface[i].name, mli->iface[i].index);
+       continue;
+      }
+      ret = pmlist->module->add_pads(slist->session, pplist);
+      if(RC_OK != ret)
+      {
+       goto out;
+      } 
+    }
+  }
+  *dut = vdut;
+out:
+  return ret;
+}
+int lambdasim_sort_session()
+{
+  struct session_list_s *s;
+  struct session_list_s *sprev=sesslist;
+  
+  if(!sesslist->next)
+  {
+    return RC_OK;
+  }
+  
+  for(s = sesslist->next; s; s=s->next)
+  {
+    if(s->tickfirst)
+    {
+      sprev->next = s->next;
+      s->next = sesslist;
+      sesslist=s;
+      s=sprev;
+      continue;
+    }
+    sprev = s;
+  }
+
+  return RC_OK;
+  
+}
+struct event *ev;
+static void cb(int sock, short which, void *arg)
+{
+  struct session_list_s *s;
+  void *vdut=arg;
+  struct timeval tv;
+  tv.tv_sec = 0;
+  tv.tv_usec = 0;
+  int i;
+  
+  
+  //lambdasim_eval(vdut);
+  for(i = 0; i < 1000; i++)
+  {
+    for(s = sesslist; s; s=s->next)
+    {
+      if(s->tickfirst)
+       s->module->tick(s->session);
+    }
+    lambdasim_eval(vdut);
+    for(s = sesslist; s; s=s->next)
+    {
+      if(!s->tickfirst)
+       s->module->tick(s->session);
+    }
+  }
+  //lambdasim_eval(vdut);
+  
+  
+  if (!evtimer_pending(ev, NULL)) {
+    event_del(ev);
+    evtimer_add(ev, &tv);
+  }
+}  
+
+int main()
+{
+
+  void *vdut=NULL;
+  struct event_base *base=NULL;
+  struct timeval tv;
+
+  int ret;
+  
+#ifdef _WIN32
+  WSADATA wsa_data;
+  WSAStartup(0x0201, &wsa_data);
+#endif
+
+
+  base = event_base_new();
+  if(!base)
+  {
+    eprintf("Can't allocate base\n");
+    ret=RC_ERROR;
+    goto out;
+  }
+  
+  if(RC_OK != (ret = lambdasim_initialize_all(&vdut, base)))
+  {
+    goto out;
+  }
+  
+  if(RC_OK != (ret = lambdasim_sort_session()))
+  {
+    goto out;
+  }
+  
+  
+  tv.tv_sec = 0;
+  tv.tv_usec = 0;
+  ev = event_new(base, -1, EV_PERSIST, cb, vdut);
+  event_add(ev, &tv);
+  event_base_dispatch(base);
+out:
+  return ret;
+}
diff --git a/litex/build/sim/core/tinydir.h b/litex/build/sim/core/tinydir.h
new file mode 100644 (file)
index 0000000..54d5f5a
--- /dev/null
@@ -0,0 +1,804 @@
+/*
+Copyright (c) 2013-2016, tinydir authors:
+- Cong Xu
+- Lautis Sun
+- Baudouin Feildel
+- Andargor <andargor@yahoo.com>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef TINYDIR_H
+#define TINYDIR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if ((defined _UNICODE) && !(defined UNICODE))
+#define UNICODE
+#endif
+
+#if ((defined UNICODE) && !(defined _UNICODE))
+#define _UNICODE
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _MSC_VER
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# include <tchar.h>
+# pragma warning(push)
+# pragma warning (disable : 4996)
+#else
+# include <dirent.h>
+# include <libgen.h>
+# include <sys/stat.h>
+# include <stddef.h>
+#endif
+#ifdef __MINGW32__
+# include <tchar.h>
+#endif
+
+
+/* types */
+
+/* Windows UNICODE wide character support */
+#if defined _MSC_VER || defined __MINGW32__
+#define _tinydir_char_t TCHAR
+#define TINYDIR_STRING(s) _TEXT(s)
+#define _tinydir_strlen _tcslen
+#define _tinydir_strcpy _tcscpy
+#define _tinydir_strcat _tcscat
+#define _tinydir_strcmp _tcscmp
+#define _tinydir_strrchr _tcsrchr
+#define _tinydir_strncmp _tcsncmp
+#else
+#define _tinydir_char_t char
+#define TINYDIR_STRING(s) s
+#define _tinydir_strlen strlen
+#define _tinydir_strcpy strcpy
+#define _tinydir_strcat strcat
+#define _tinydir_strcmp strcmp
+#define _tinydir_strrchr strrchr
+#define _tinydir_strncmp strncmp
+#endif
+
+#if (defined _MSC_VER || defined __MINGW32__)
+#include <windows.h>
+#define _TINYDIR_PATH_MAX MAX_PATH
+#elif defined  __linux__
+#include <linux/limits.h>
+#define _TINYDIR_PATH_MAX PATH_MAX
+#else
+#define _TINYDIR_PATH_MAX 4096
+#endif
+
+#ifdef _MSC_VER
+/* extra chars for the "\\*" mask */
+# define _TINYDIR_PATH_EXTRA 2
+#else
+# define _TINYDIR_PATH_EXTRA 0
+#endif
+
+#define _TINYDIR_FILENAME_MAX 256
+
+#if (defined _MSC_VER || defined __MINGW32__)
+#define _TINYDIR_DRIVE_MAX 3
+#endif
+
+#ifdef _MSC_VER
+# define _TINYDIR_FUNC static __inline
+#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
+# define _TINYDIR_FUNC static __inline__
+#else
+# define _TINYDIR_FUNC static inline
+#endif
+
+/* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */
+#ifdef TINYDIR_USE_READDIR_R
+
+/* readdir_r is a POSIX-only function, and may not be available under various
+ * environments/settings, e.g. MinGW. Use readdir fallback */
+#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\
+       _POSIX_SOURCE
+# define _TINYDIR_HAS_READDIR_R
+#endif
+#if _POSIX_C_SOURCE >= 200112L
+# define _TINYDIR_HAS_FPATHCONF
+# include <unistd.h>
+#endif
+#if _BSD_SOURCE || _SVID_SOURCE || \
+       (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
+# define _TINYDIR_HAS_DIRFD
+# include <sys/types.h>
+#endif
+#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\
+       defined _PC_NAME_MAX
+# define _TINYDIR_USE_FPATHCONF
+#endif
+#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\
+       !(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX)
+# define _TINYDIR_USE_READDIR
+#endif
+
+/* Use readdir by default */
+#else
+# define _TINYDIR_USE_READDIR
+#endif
+
+/* MINGW32 has two versions of dirent, ASCII and UNICODE*/
+#ifndef _MSC_VER
+#if (defined __MINGW32__) && (defined _UNICODE)
+#define _TINYDIR_DIR _WDIR
+#define _tinydir_dirent _wdirent
+#define _tinydir_opendir _wopendir
+#define _tinydir_readdir _wreaddir
+#define _tinydir_closedir _wclosedir
+#else
+#define _TINYDIR_DIR DIR
+#define _tinydir_dirent dirent
+#define _tinydir_opendir opendir
+#define _tinydir_readdir readdir
+#define _tinydir_closedir closedir
+#endif
+#endif
+
+/* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and _TINYDIR_FREE. */
+#if    defined(_TINYDIR_MALLOC) &&  defined(_TINYDIR_FREE)
+#elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE)
+#else
+#error "Either define both alloc and free or none of them!"
+#endif
+
+#if !defined(_TINYDIR_MALLOC)
+       #define _TINYDIR_MALLOC(_size) malloc(_size)
+       #define _TINYDIR_FREE(_ptr)    free(_ptr)
+#endif /* !defined(_TINYDIR_MALLOC) */
+
+typedef struct tinydir_file
+{
+       _tinydir_char_t path[_TINYDIR_PATH_MAX];
+       _tinydir_char_t name[_TINYDIR_FILENAME_MAX];
+       _tinydir_char_t *extension;
+       int is_dir;
+       int is_reg;
+
+#ifndef _MSC_VER
+#ifdef __MINGW32__
+       struct _stat _s;
+#else
+       struct stat _s;
+#endif
+#endif
+} tinydir_file;
+
+typedef struct tinydir_dir
+{
+       _tinydir_char_t path[_TINYDIR_PATH_MAX];
+       int has_next;
+       size_t n_files;
+
+       tinydir_file *_files;
+#ifdef _MSC_VER
+       HANDLE _h;
+       WIN32_FIND_DATA _f;
+#else
+       _TINYDIR_DIR *_d;
+       struct _tinydir_dirent *_e;
+#ifndef _TINYDIR_USE_READDIR
+       struct _tinydir_dirent *_ep;
+#endif
+#endif
+} tinydir_dir;
+
+
+/* declarations */
+
+_TINYDIR_FUNC
+int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path);
+_TINYDIR_FUNC
+int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path);
+_TINYDIR_FUNC
+void tinydir_close(tinydir_dir *dir);
+
+_TINYDIR_FUNC
+int tinydir_next(tinydir_dir *dir);
+_TINYDIR_FUNC
+int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file);
+_TINYDIR_FUNC
+int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i);
+_TINYDIR_FUNC
+int tinydir_open_subdir_n(tinydir_dir *dir, size_t i);
+
+_TINYDIR_FUNC
+int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path);
+_TINYDIR_FUNC
+void _tinydir_get_ext(tinydir_file *file);
+_TINYDIR_FUNC
+int _tinydir_file_cmp(const void *a, const void *b);
+#ifndef _MSC_VER
+#ifndef _TINYDIR_USE_READDIR
+_TINYDIR_FUNC
+size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp);
+#endif
+#endif
+
+
+/* definitions*/
+
+_TINYDIR_FUNC
+int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
+{
+#ifndef _MSC_VER
+#ifndef _TINYDIR_USE_READDIR
+       int error;
+       int size;       /* using int size */
+#endif
+#else
+       _tinydir_char_t path_buf[_TINYDIR_PATH_MAX];
+#endif
+       _tinydir_char_t *pathp;
+
+       if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0)
+       {
+               errno = EINVAL;
+               return -1;
+       }
+       if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
+       {
+               errno = ENAMETOOLONG;
+               return -1;
+       }
+
+       /* initialise dir */
+       dir->_files = NULL;
+#ifdef _MSC_VER
+       dir->_h = INVALID_HANDLE_VALUE;
+#else
+       dir->_d = NULL;
+#ifndef _TINYDIR_USE_READDIR
+       dir->_ep = NULL;
+#endif
+#endif
+       tinydir_close(dir);
+
+       _tinydir_strcpy(dir->path, path);
+       /* Remove trailing slashes */
+       pathp = &dir->path[_tinydir_strlen(dir->path) - 1];
+       while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/')))
+       {
+               *pathp = TINYDIR_STRING('\0');
+               pathp++;
+       }
+#ifdef _MSC_VER
+       _tinydir_strcpy(path_buf, dir->path);
+       _tinydir_strcat(path_buf, TINYDIR_STRING("\\*"));
+       dir->_h = FindFirstFileEx(path_buf, FindExInfoStandard, &dir->_f, FindExSearchNameMatch, NULL, 0);
+       if (dir->_h == INVALID_HANDLE_VALUE)
+       {
+               errno = ENOENT;
+#else
+       dir->_d = _tinydir_opendir(path);
+       if (dir->_d == NULL)
+       {
+#endif
+               goto bail;
+       }
+
+       /* read first file */
+       dir->has_next = 1;
+#ifndef _MSC_VER
+#ifdef _TINYDIR_USE_READDIR
+       dir->_e = _tinydir_readdir(dir->_d);
+#else
+       /* allocate dirent buffer for readdir_r */
+       size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */
+       if (size == -1) return -1;
+       dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size);
+       if (dir->_ep == NULL) return -1;
+
+       error = readdir_r(dir->_d, dir->_ep, &dir->_e);
+       if (error != 0) return -1;
+#endif
+       if (dir->_e == NULL)
+       {
+               dir->has_next = 0;
+       }
+#endif
+
+       return 0;
+
+bail:
+       tinydir_close(dir);
+       return -1;
+}
+
+_TINYDIR_FUNC
+int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path)
+{
+       /* Count the number of files first, to pre-allocate the files array */
+       size_t n_files = 0;
+       if (tinydir_open(dir, path) == -1)
+       {
+               return -1;
+       }
+       while (dir->has_next)
+       {
+               n_files++;
+               if (tinydir_next(dir) == -1)
+               {
+                       goto bail;
+               }
+       }
+       tinydir_close(dir);
+
+       if (tinydir_open(dir, path) == -1)
+       {
+               return -1;
+       }
+
+       dir->n_files = 0;
+       dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files);
+       if (dir->_files == NULL)
+       {
+               goto bail;
+       }
+       while (dir->has_next)
+       {
+               tinydir_file *p_file;
+               dir->n_files++;
+
+               p_file = &dir->_files[dir->n_files - 1];
+               if (tinydir_readfile(dir, p_file) == -1)
+               {
+                       goto bail;
+               }
+
+               if (tinydir_next(dir) == -1)
+               {
+                       goto bail;
+               }
+
+               /* Just in case the number of files has changed between the first and
+               second reads, terminate without writing into unallocated memory */
+               if (dir->n_files == n_files)
+               {
+                       break;
+               }
+       }
+
+       qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
+
+       return 0;
+
+bail:
+       tinydir_close(dir);
+       return -1;
+}
+
+_TINYDIR_FUNC
+void tinydir_close(tinydir_dir *dir)
+{
+       if (dir == NULL)
+       {
+               return;
+       }
+
+       memset(dir->path, 0, sizeof(dir->path));
+       dir->has_next = 0;
+       dir->n_files = 0;
+       _TINYDIR_FREE(dir->_files);
+       dir->_files = NULL;
+#ifdef _MSC_VER
+       if (dir->_h != INVALID_HANDLE_VALUE)
+       {
+               FindClose(dir->_h);
+       }
+       dir->_h = INVALID_HANDLE_VALUE;
+#else
+       if (dir->_d)
+       {
+               _tinydir_closedir(dir->_d);
+       }
+       dir->_d = NULL;
+       dir->_e = NULL;
+#ifndef _TINYDIR_USE_READDIR
+       _TINYDIR_FREE(dir->_ep);
+       dir->_ep = NULL;
+#endif
+#endif
+}
+
+_TINYDIR_FUNC
+int tinydir_next(tinydir_dir *dir)
+{
+       if (dir == NULL)
+       {
+               errno = EINVAL;
+               return -1;
+       }
+       if (!dir->has_next)
+       {
+               errno = ENOENT;
+               return -1;
+       }
+
+#ifdef _MSC_VER
+       if (FindNextFile(dir->_h, &dir->_f) == 0)
+#else
+#ifdef _TINYDIR_USE_READDIR
+       dir->_e = _tinydir_readdir(dir->_d);
+#else
+       if (dir->_ep == NULL)
+       {
+               return -1;
+       }
+       if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0)
+       {
+               return -1;
+       }
+#endif
+       if (dir->_e == NULL)
+#endif
+       {
+               dir->has_next = 0;
+#ifdef _MSC_VER
+               if (GetLastError() != ERROR_SUCCESS &&
+                       GetLastError() != ERROR_NO_MORE_FILES)
+               {
+                       tinydir_close(dir);
+                       errno = EIO;
+                       return -1;
+               }
+#endif
+       }
+
+       return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
+{
+       if (dir == NULL || file == NULL)
+       {
+               errno = EINVAL;
+               return -1;
+       }
+#ifdef _MSC_VER
+       if (dir->_h == INVALID_HANDLE_VALUE)
+#else
+       if (dir->_e == NULL)
+#endif
+       {
+               errno = ENOENT;
+               return -1;
+       }
+       if (_tinydir_strlen(dir->path) +
+               _tinydir_strlen(
+#ifdef _MSC_VER
+                       dir->_f.cFileName
+#else
+                       dir->_e->d_name
+#endif
+               ) + 1 + _TINYDIR_PATH_EXTRA >=
+               _TINYDIR_PATH_MAX)
+       {
+               /* the path for the file will be too long */
+               errno = ENAMETOOLONG;
+               return -1;
+       }
+       if (_tinydir_strlen(
+#ifdef _MSC_VER
+                       dir->_f.cFileName
+#else
+                       dir->_e->d_name
+#endif
+               ) >= _TINYDIR_FILENAME_MAX)
+       {
+               errno = ENAMETOOLONG;
+               return -1;
+       }
+
+       _tinydir_strcpy(file->path, dir->path);
+       _tinydir_strcat(file->path, TINYDIR_STRING("/"));
+       _tinydir_strcpy(file->name,
+#ifdef _MSC_VER
+               dir->_f.cFileName
+#else
+               dir->_e->d_name
+#endif
+       );
+       _tinydir_strcat(file->path, file->name);
+#ifndef _MSC_VER
+#ifdef __MINGW32__
+       if (_tstat(
+#else
+       if (stat(
+#endif
+               file->path, &file->_s) == -1)
+       {
+               return -1;
+       }
+#endif
+       _tinydir_get_ext(file);
+
+       file->is_dir =
+#ifdef _MSC_VER
+               !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
+#else
+               S_ISDIR(file->_s.st_mode);
+#endif
+       file->is_reg =
+#ifdef _MSC_VER
+               !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
+               (
+                       !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
+                       !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+                       !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
+#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
+                       !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
+#endif
+#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
+                       !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
+#endif
+                       !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
+                       !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
+#else
+               S_ISREG(file->_s.st_mode);
+#endif
+
+       return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
+{
+       if (dir == NULL || file == NULL)
+       {
+               errno = EINVAL;
+               return -1;
+       }
+       if (i >= dir->n_files)
+       {
+               errno = ENOENT;
+               return -1;
+       }
+
+       memcpy(file, &dir->_files[i], sizeof(tinydir_file));
+       _tinydir_get_ext(file);
+
+       return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
+{
+       _tinydir_char_t path[_TINYDIR_PATH_MAX];
+       if (dir == NULL)
+       {
+               errno = EINVAL;
+               return -1;
+       }
+       if (i >= dir->n_files || !dir->_files[i].is_dir)
+       {
+               errno = ENOENT;
+               return -1;
+       }
+
+       _tinydir_strcpy(path, dir->_files[i].path);
+       tinydir_close(dir);
+       if (tinydir_open_sorted(dir, path) == -1)
+       {
+               return -1;
+       }
+
+       return 0;
+}
+
+/* Open a single file given its path */
+_TINYDIR_FUNC
+int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
+{
+       tinydir_dir dir;
+       int result = 0;
+       int found = 0;
+       _tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX];
+       _tinydir_char_t file_name_buf[_TINYDIR_FILENAME_MAX];
+       _tinydir_char_t *dir_name;
+       _tinydir_char_t *base_name;
+#if (defined _MSC_VER || defined __MINGW32__)
+       _tinydir_char_t drive_buf[_TINYDIR_DRIVE_MAX];
+       _tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX];
+#endif
+
+       if (file == NULL || path == NULL || _tinydir_strlen(path) == 0)
+       {
+               errno = EINVAL;
+               return -1;
+       }
+       if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
+       {
+               errno = ENAMETOOLONG;
+               return -1;
+       }
+
+       /* Get the parent path */
+#if (defined _MSC_VER || defined __MINGW32__)
+#if ((defined _MSC_VER) && (_MSC_VER >= 1400))
+               _tsplitpath_s(
+                       path,
+                       drive_buf, _TINYDIR_DRIVE_MAX,
+                       dir_name_buf, _TINYDIR_FILENAME_MAX,
+                       file_name_buf, _TINYDIR_FILENAME_MAX,
+                       ext_buf, _TINYDIR_FILENAME_MAX);
+#else
+               _tsplitpath(
+                       path,
+                       drive_buf,
+                       dir_name_buf,
+                       file_name_buf,
+                       ext_buf);
+#endif
+
+/* _splitpath_s not work fine with only filename and widechar support */
+#ifdef _UNICODE
+               if (drive_buf[0] == L'\xFEFE')
+                       drive_buf[0] = '\0';
+               if (dir_name_buf[0] == L'\xFEFE')
+                       dir_name_buf[0] = '\0';
+#endif
+
+       if (errno)
+       {
+               errno = EINVAL;
+               return -1;
+       }
+       /* Emulate the behavior of dirname by returning "." for dir name if it's
+       empty */
+       if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0')
+       {
+               _tinydir_strcpy(dir_name_buf, TINYDIR_STRING("."));
+       }
+       /* Concatenate the drive letter and dir name to form full dir name */
+       _tinydir_strcat(drive_buf, dir_name_buf);
+       dir_name = drive_buf;
+       /* Concatenate the file name and extension to form base name */
+       _tinydir_strcat(file_name_buf, ext_buf);
+       base_name = file_name_buf;
+#else
+       _tinydir_strcpy(dir_name_buf, path);
+       dir_name = dirname(dir_name_buf);
+       _tinydir_strcpy(file_name_buf, path);
+       base_name =basename(file_name_buf);
+#endif
+
+       /* Open the parent directory */
+       if (tinydir_open(&dir, dir_name) == -1)
+       {
+               return -1;
+       }
+
+       /* Read through the parent directory and look for the file */
+       while (dir.has_next)
+       {
+               if (tinydir_readfile(&dir, file) == -1)
+               {
+                       result = -1;
+                       goto bail;
+               }
+               if (_tinydir_strcmp(file->name, base_name) == 0)
+               {
+                       /* File found */
+                       found = 1;
+                       break;
+               }
+               tinydir_next(&dir);
+       }
+       if (!found)
+       {
+               result = -1;
+               errno = ENOENT;
+       }
+
+bail:
+       tinydir_close(&dir);
+       return result;
+}
+
+_TINYDIR_FUNC
+void _tinydir_get_ext(tinydir_file *file)
+{
+       _tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.'));
+       if (period == NULL)
+       {
+               file->extension = &(file->name[_tinydir_strlen(file->name)]);
+       }
+       else
+       {
+               file->extension = period + 1;
+       }
+}
+
+_TINYDIR_FUNC
+int _tinydir_file_cmp(const void *a, const void *b)
+{
+       const tinydir_file *fa = (const tinydir_file *)a;
+       const tinydir_file *fb = (const tinydir_file *)b;
+       if (fa->is_dir != fb->is_dir)
+       {
+               return -(fa->is_dir - fb->is_dir);
+       }
+       return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
+}
+
+#ifndef _MSC_VER
+#ifndef _TINYDIR_USE_READDIR
+/*
+The following authored by Ben Hutchings <ben@decadent.org.uk>
+from https://womble.decadent.org.uk/readdir_r-advisory.html
+*/
+/* Calculate the required buffer size (in bytes) for directory      *
+* entries read from the given directory handle.  Return -1 if this  *
+* this cannot be done.                                              *
+*                                                                   *
+* This code does not trust values of NAME_MAX that are less than    *
+* 255, since some systems (including at least HP-UX) incorrectly    *
+* define it to be a smaller value.                                  */
+_TINYDIR_FUNC
+size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp)
+{
+       long name_max;
+       size_t name_end;
+       /* parameter may be unused */
+       (void)dirp;
+
+#if defined _TINYDIR_USE_FPATHCONF
+       name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
+       if (name_max == -1)
+#if defined(NAME_MAX)
+               name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
+#else
+               return (size_t)(-1);
+#endif
+#elif defined(NAME_MAX)
+       name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
+#else
+#error "buffer size for readdir_r cannot be determined"
+#endif
+       name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1;
+       return (name_end > sizeof(struct _tinydir_dirent) ?
+               name_end : sizeof(struct _tinydir_dirent));
+}
+#endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+# if defined (_MSC_VER)
+# pragma warning(pop)
+# endif
+
+#endif
diff --git a/litex/build/sim/core/veril.cpp b/litex/build/sim/core/veril.cpp
new file mode 100644 (file)
index 0000000..cd5b2db
--- /dev/null
@@ -0,0 +1,41 @@
+/* Copyright (C) 2017 LambdaConcept */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "Vdut.h"
+#include "Vdut.h"
+#include "verilated.h"
+#include "verilated_vcd_c.h"
+#include <verilated.h>
+
+
+VerilatedVcdC* tfp;
+
+extern "C" void lambdasim_eval(void *vdut)
+{
+  Vdut *dut = (Vdut*)vdut;
+  dut->eval();
+}
+
+extern "C" void lambdasim_init_tracer(void *vdut)
+{
+  Vdut *dut = (Vdut*)vdut;
+  Verilated::traceEverOn(true);
+  tfp = new VerilatedVcdC;
+  dut->trace(tfp, 99);
+  tfp->open("dut.vcd");
+}
+
+extern "C" void lambdasim_tracer_dump()
+{
+  static unsigned int ticks=0;
+  tfp->dump(ticks++);
+}
+
+
+vluint64_t main_time = 0;
+double sc_time_stamp()
+{
+  return main_time;
+}
diff --git a/litex/build/sim/core/veril.h b/litex/build/sim/core/veril.h
new file mode 100644 (file)
index 0000000..ce27a32
--- /dev/null
@@ -0,0 +1,16 @@
+/* Copyright (C) 2017 LambdaConcept */
+
+#ifndef __VERIL_H_
+#define __VERIL_H_
+
+#ifdef __cplusplus
+extern "C" void lambdasim_eval(void *vdut);
+extern "C" void lambdasim_init_tracer(void *vdut);
+extern "C" void lambdasim_tracer_dump();
+#else
+void lambdasim_eval(void *vdut);
+void lambdasim_init_tracer(void *vdut);
+void lambdasim_tracer_dump();
+#endif
+
+#endif
diff --git a/litex/build/sim/dut_tb.cpp b/litex/build/sim/dut_tb.cpp
deleted file mode 100644 (file)
index 8d0791b..0000000
+++ /dev/null
@@ -1,492 +0,0 @@
-// This file is Copyright (c) 2015 Florent Kermarrec <florent@enjoy-digital.fr>
-// License: BSD
-#include "Vdut.h"
-#include "verilated.h"
-#include "verilated_vcd_c.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/socket.h>
-#include <sys/ioctl.h>
-#include <termios.h>
-#include <sys/poll.h>
-
-#include <linux/if.h>
-#include <linux/if_tun.h>
-
-#include <SDL/SDL.h>
-
-/* ios */
-
-#ifdef SERIAL_SOURCE_VALID
-#define WITH_SERIAL
-#endif
-
-#ifdef ETH_SOURCE_VALID
-#define WITH_ETH
-#endif
-
-#ifdef VGA_DE
-#define WITH_VGA
-#endif
-
-#define MAX(a,b) (((a)>(b))?(a):(b))
-#define MIN(a,b) (((a)<(b))?(a):(b))
-
-int trace = 0;
-
-vluint64_t main_time = 0;
-double sc_time_stamp()
-{
-       return main_time;
-}
-
-Vdut* dut;
-VerilatedVcdC* tfp;
-
-/* Sim struct */
-struct sim {
-       bool run;
-
-       unsigned int tick;
-       clock_t start;
-       clock_t end;
-       float speed;
-
-#ifdef WITH_SERIAL_PTY
-       char serial_dev[64];
-       int serial_fd;
-       unsigned char serial_rx_data;
-       unsigned char serial_tx_data;
-#endif
-#ifdef WITH_ETH
-       const char *eth_dev;
-       const char *eth_tap;
-       int eth_fd;
-       unsigned char eth_txbuffer[2048];
-       unsigned char eth_rxbuffer[2048];
-       int eth_txbuffer_len;
-       int eth_rxbuffer_len;
-       int eth_rxbuffer_pos;
-       int eth_last_source_valid;
-#endif
-};
-
-/* Serial functions */
-#ifndef WITH_SERIAL_PTY
-struct termios orig_termios;
-
-void reset_terminal_mode(void)
-{
-       tcsetattr(0, TCSANOW, &orig_termios);
-}
-
-void set_conio_terminal_mode(void)
-{
-       struct termios new_termios;
-
-       /* take two copies - one for now, one for later */
-       tcgetattr(0, &orig_termios);
-       memcpy(&new_termios, &orig_termios, sizeof(new_termios));
-
-       /* register cleanup handler, and set the new terminal mode */
-       atexit(reset_terminal_mode);
-       cfmakeraw(&new_termios);
-       tcsetattr(0, TCSANOW, &new_termios);
-}
-
-int kbhit(void)
-{
-       struct timeval tv = { 0L, 0L };
-       fd_set fds;
-       FD_ZERO(&fds);
-       FD_SET(0, &fds);
-       return select(1, &fds, NULL, NULL, &tv);
-}
-
-int getch(void)
-{
-       int r;
-       unsigned char c;
-       if((r = read(0, &c, sizeof(c))) < 0) {
-               return r;
-       } else {
-               return c;
-       }
-}
-#endif
-
-/* Ethernet functions */
-/* create tap:
-     openvpn --mktun --dev tap0
-     ifconfig tap0 192.168.0.14 up
-     mknod /dev/net/tap0 c 10 200
-   delete tap:
-     openvpn --rmtun --dev tap0 */
-#ifdef WITH_ETH
-void eth_init(struct sim *s, const char *dev, const char*tap)
-{
-       s->eth_txbuffer_len = 0;
-       s->eth_rxbuffer_len = 0;
-       s->eth_rxbuffer_pos = 0;
-       s->eth_last_source_valid = 0;
-       s->eth_dev = dev;
-       s->eth_tap = tap;
-}
-
-void eth_open(struct sim *s)
-{
-
-       struct ifreq ifr;
-       s->eth_fd = open (s->eth_dev, O_RDWR);
-       if(s->eth_fd < 0) {
-               fprintf(stderr, " Could not open dev %s\n", s->eth_dev);
-               return;
-       }
-
-       memset(&ifr, 0, sizeof(ifr));
-       ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
-       strncpy(ifr.ifr_name, s->eth_tap, IFNAMSIZ);
-
-       if(ioctl(s->eth_fd, TUNSETIFF, (void *) &ifr) < 0) {
-               fprintf(stderr, " Could not set %s\n", s->eth_tap);
-               close(s->eth_fd);
-       }
-       return;
-}
-
-int eth_close(struct sim *s)
-{
-       if(s->eth_fd < 0)
-               close(s->eth_fd);
-}
-
-void eth_write(struct sim *s, unsigned char *buf, int len)
-{
-       write(s->eth_fd, buf, len);
-}
-
-int eth_read(struct sim *s, unsigned char *buf)
-{
-
-       struct pollfd fds[1];
-       int n;
-       int len;
-
-       fds[0].fd = s->eth_fd;
-       fds[0].events = POLLIN;
-
-       n = poll(fds, 1, 0);
-       if((n > 0) && ((fds[0].revents & POLLIN) == POLLIN)) {
-               len = read(s->eth_fd, buf, 1532);
-       } else {
-               len = 0;
-       }
-       return len;
-}
-#endif
-
-/* VGA functions */
-#ifdef WITH_VGA
-
-SDL_Surface *screen;
-SDL_Event event;
-
-void vga_set_pixel(SDL_Surface *screen, int x, int y, char r, char g, char b)
-{
-       unsigned int *pixmem32;
-       unsigned int color;
-
-       color = SDL_MapRGB(screen->format, r, g, b);
-       pixmem32 = (unsigned int*) screen->pixels  + y*640 + x;
-       *pixmem32 = color;
-}
-
-int vga_init(struct sim *s) {
-       if(SDL_Init(SDL_INIT_VIDEO) < 0) return 1;
-       if(!(screen = SDL_SetVideoMode(640, 480+1, 32, SDL_HWSURFACE))) {
-               SDL_Quit();
-               return 1;
-       }
-       return 0;
-}
-
-int x;
-int y;
-int frame;
-int hsync_wait_de = 1;
-int vsync_wait_de = 1;
-
-void vga_service(struct sim *s) {
-       int i;
-       if(VGA_HSYNC == 1 && hsync_wait_de == 0) {
-               x = 0;
-               y++;
-               hsync_wait_de = 1;
-       }
-       if(VGA_VSYNC == 1 && vsync_wait_de == 0) {
-               y = 0;
-               vsync_wait_de = 1;
-               for(i=0; i<frame; i++)
-                       vga_set_pixel(screen, i%640, 480, 255, 255, 255);
-               frame++;
-               if(SDL_MUSTLOCK(screen))
-                       SDL_UnlockSurface(screen);
-               SDL_Flip(screen);
-               if(SDL_MUSTLOCK(screen))
-                       SDL_LockSurface(screen);
-       }
-       if(VGA_DE == 1) {
-               hsync_wait_de = 0;
-               vsync_wait_de = 0;
-               vga_set_pixel(screen, x, y, VGA_R, VGA_G, VGA_B);
-               x++;
-       }
-
-       if(s->tick%1000 == 0) {
-               while(SDL_PollEvent(&event)) {
-                       switch (event.type) {
-                               case SDL_QUIT:
-                                       s->run = false;
-                                       break;
-                               case SDL_KEYDOWN:
-                                       s->run = false;
-                                       break;
-                       }
-               }
-       }
-}
-
-int vga_close(struct sim *s) {
-       SDL_Quit();
-}
-
-#endif
-
-
-#ifndef WITH_SERIAL_PTY
-int console_service(struct sim *s)
-{
-       /* fpga --> console */
-       SERIAL_SOURCE_READY = 1;
-       if(SERIAL_SOURCE_VALID == 1) {
-               if(SERIAL_SOURCE_DATA == '\n')
-                       putchar('\r');
-               putchar(SERIAL_SOURCE_DATA);
-               fflush(stdout);
-       }
-
-       /* console --> fpga */
-       SERIAL_SINK_VALID = 0;
-       if(s->tick%(1000) == 0) {
-               if(kbhit()) {
-                       char c = getch();
-                       if(c == 27 && !kbhit()) {
-                               printf("\r\n");
-                               return -1;
-                       } else {
-                               SERIAL_SINK_VALID = 1;
-                               SERIAL_SINK_DATA = c;
-                       }
-               }
-       }
-       return 0;
-}
-#else
-void console_init(struct sim *s)
-{
-       FILE *f;
-       f = fopen("/tmp/simserial","r");
-       fscanf(f, "%[^\n]", s->serial_dev);
-       fclose(f);
-       return;
-}
-
-void console_open(struct sim *s)
-{
-       s->serial_fd = open(s->serial_dev, O_RDWR);
-       if(s->serial_fd < 0) {
-               fprintf(stderr, " Could not open dev %s\n", s->serial_dev);
-               return;
-       }
-       return;
-}
-
-int console_close(struct sim *s)
-{
-       if(s->serial_fd < 0)
-               close(s->serial_fd);
-}
-
-void console_write(struct sim *s, unsigned char *buf, int len)
-{
-       write(s->serial_fd, buf, len);
-}
-
-int console_read(struct sim *s, unsigned char *buf)
-{
-       struct pollfd fds[1];
-       int n;
-       int len;
-
-       fds[0].fd = s->serial_fd;
-       fds[0].events = POLLIN;
-
-       n = poll(fds, 1, 0);
-       if((n > 0) && ((fds[0].revents & POLLIN) == POLLIN)) {
-               len = read(s->serial_fd, buf, 1);
-       } else {
-               len = 0;
-       }
-       return len;
-}
-
-int console_service(struct sim *s)
-{
-       /* fpga --> console */
-       SERIAL_SOURCE_READY = 1;
-       if(SERIAL_SOURCE_VALID == 1) {
-               s->serial_tx_data = SERIAL_SOURCE_DATA;
-               console_write(s, &(s->serial_tx_data), 1);
-       }
-
-       /* console --> fpga */
-       SERIAL_SINK_VALID = 0;
-       if(console_read(s, &(s->serial_rx_data)))
-       {
-               SERIAL_SINK_VALID = 1;
-               SERIAL_SINK_DATA = s->serial_rx_data;
-       }
-       return 0;
-}
-#endif
-
-#ifdef WITH_ETH
-int ethernet_service(struct sim *s) {
-       /* fpga --> tap */
-       ETH_SOURCE_READY = 1;
-       if(ETH_SOURCE_VALID == 1) {
-               s->eth_txbuffer[s->eth_txbuffer_len] = ETH_SOURCE_DATA;
-               s->eth_txbuffer_len++;
-       } else {
-               if(s->eth_last_source_valid) {
-                       eth_write(s, s->eth_txbuffer, s->eth_txbuffer_len);
-                       s->eth_txbuffer_len = 0;
-               }
-       }
-       s->eth_last_source_valid = ETH_SOURCE_VALID;
-
-       /* tap --> fpga */
-       if(s->eth_rxbuffer_len == 0) {
-               ETH_SINK_VALID = 0;
-               s->eth_rxbuffer_pos = 0;
-               s->eth_rxbuffer_len = eth_read(s, s->eth_rxbuffer);
-       } else {
-               if(s->eth_rxbuffer_pos < MAX(s->eth_rxbuffer_len, 60)) {
-                       ETH_SINK_VALID = 1;
-                       ETH_SINK_DATA = s->eth_rxbuffer[s->eth_rxbuffer_pos];
-                       s->eth_rxbuffer_pos++;
-               } else {
-                       ETH_SINK_VALID = 0;
-                       s->eth_rxbuffer_len = 0;
-                       memset(s->eth_rxbuffer, 0, 1532);
-               }
-       }
-}
-#endif
-
-void sim_tick(struct sim *s)
-{
-       SYS_CLK = s->tick%2;
-       dut->eval();
-       if(trace)
-               tfp->dump(s->tick);
-       s->tick++;
-}
-
-void sim_init(struct sim *s)
-{
-       int i;
-       s->tick = 0;
-#ifdef SYS_RST
-       SYS_RST = 1;
-       SYS_CLK = 0;
-       for (i=0; i<8; i++)
-               sim_tick(s);
-       SYS_RST = 0;
-#endif
-       s->start = clock();
-}
-
-int main(int argc, char **argv, char **env)
-{
-       float speed;
-
-#ifndef WITH_SERIAL_PTY
-       set_conio_terminal_mode();
-#endif
-
-       Verilated::commandArgs(argc, argv);
-       dut = new Vdut;
-
-       Verilated::traceEverOn(true);
-       tfp = new VerilatedVcdC;
-       dut->trace(tfp, 99);
-       tfp->open("dut.vcd");
-
-       struct sim s;
-       sim_init(&s);
-
-#ifdef WITH_SERIAL_PTY
-       console_init(&s);
-       console_open(&s);
-#endif
-
-#ifdef WITH_ETH
-       eth_init(&s, "/dev/net/tap0", "tap0"); // XXX get this from /tmp/simethernet
-       eth_open(&s);
-#endif
-
-#ifdef WITH_VGA
-       if(vga_init(&s)) return 1;
-#endif
-
-       s.run = true;
-       while(s.run) {
-               sim_tick(&s);
-               if(SYS_CLK) {
-#ifdef WITH_SERIAL
-                       if(console_service(&s) != 0)
-                               s.run = false;
-#endif
-#ifdef WITH_ETH
-                       ethernet_service(&s);
-#endif
-#ifdef WITH_VGA
-                       vga_service(&s);
-#endif
-               }
-       }
-       s.end = clock();
-
-       speed = (s.tick/2)/((s.end-s.start)/CLOCKS_PER_SEC);
-
-       printf("average speed: %3.3f MHz\n\r", speed/1000000);
-
-       tfp->close();
-
-#ifdef WITH_SERIAL_PTY
-       console_close(&s);
-#endif
-#ifdef WITH_ETH
-       eth_close(&s);
-#endif
-#ifdef WITH_VGA
-       vga_close(&s);
-#endif
-
-       exit(0);
-}
index a86d1d08c2b6d069941a19eb9b394e3104699517..f76532a4c9bd2e578e6de22afb773ff6a1b1e850 100644 (file)
@@ -1,3 +1,5 @@
+from litex.gen.fhdl.structure import Signal
+from litex.gen.genlib.record import Record
 from litex.build.generic_platform import GenericPlatform
 from litex.build.sim import common, verilator
 
@@ -5,11 +7,29 @@ from litex.build.sim import common, verilator
 class SimPlatform(GenericPlatform):
     def __init__(self, *args, toolchain="verilator", **kwargs):
         GenericPlatform.__init__(self, *args, **kwargs)
+        self.sim_requested = []
         if toolchain == "verilator":
             self.toolchain = verilator.SimVerilatorToolchain()
         else:
             raise ValueError("Unknown toolchain")
 
+    def request(self, name, number=None):
+        index = ""
+        if number is not None:
+            index = str(number)
+        obj = GenericPlatform.request(self, name, number=number)
+        siglist = []
+        if isinstance(obj, Signal):
+            siglist.append((name, obj.nbits, name))
+        elif isinstance(obj, Record):
+            for subsignal, dummy in obj.iter_flat():
+                subfname = subsignal.backtrace[-1][0]
+                prefix = "{}{}_".format(name, index)
+                subname = subfname.split(prefix)[1]
+                siglist.append((subname, subsignal.nbits, subfname))
+        self.sim_requested.append((name, index, siglist))
+        return obj
+
     def get_verilog(self, *args, special_overrides=dict(), **kwargs):
         so = dict(common.sim_special_overrides)
         so.update(special_overrides)
index 5335d987b02d84bab4560c0c64bd87f0ab2259ba..594eaa857b8bab7b5faf5c48adcb8933f896989b 100644 (file)
@@ -1,4 +1,5 @@
 # This file is Copyright (c) 2015-2016 Florent Kermarrec <florent@enjoy-digital.fr>
+#                            2017 Pierre-Olivier Vauboin <po@lambdaconcept.com>
 # License: BSD
 
 import os
@@ -10,116 +11,106 @@ from litex.build.generic_platform import *
 
 
 sim_directory = os.path.abspath(os.path.dirname(__file__))
+core_directory = os.path.join(sim_directory, 'core')
 
 
-def _build_tb(platform, vns, serial, template):
-    def io_name(resource, subsignal=None):
-        res = platform.lookup_request(resource)
-        if subsignal is not None:
-            res = getattr(res, subsignal)
-        return vns.get_name(res)
-
-    ios = """
-#define SYS_CLK dut->{sys_clk}
-""".format(sys_clk=io_name("sys_clk"))
-
-    if serial == "pty":
-        ios += "#define WITH_SERIAL_PTY"
-    elif serial == "console":
-        pass
-    else:
-        raise ValueError
-    try:
-        ios += """
-#define SERIAL_SOURCE_VALID dut->{serial_source_valid}
-#define SERIAL_SOURCE_READY dut->{serial_source_ready}
-#define SERIAL_SOURCE_DATA  dut->{serial_source_data}
-
-#define SERIAL_SINK_VALID dut->{serial_sink_valid}
-#define SERIAL_SINK_READY dut->{serial_sink_ready}
-#define SERIAL_SINK_DATA  dut->{serial_sink_data}
-""".format(
-    serial_source_valid=io_name("serial", "source_valid"),
-    serial_source_ready=io_name("serial", "source_ready"),
-    serial_source_data=io_name("serial", "source_data"),
-
-    serial_sink_valid=io_name("serial", "sink_valid"),
-    serial_sink_ready=io_name("serial", "sink_ready"),
-    serial_sink_data=io_name("serial", "sink_data"),
-    )
-    except:
-        pass
-
-    try:
-        ios += """
-#define ETH_SOURCE_VALID dut->{eth_source_valid}
-#define ETH_SOURCE_READY dut->{eth_source_ready}
-#define ETH_SOURCE_DATA  dut->{eth_source_data}
-
-#define ETH_SINK_VALID dut->{eth_sink_valid}
-#define ETH_SINK_READY dut->{eth_sink_ready}
-#define ETH_SINK_DATA  dut->{eth_sink_data}
-""".format(
-    eth_source_valid=io_name("eth", "source_valid"),
-    eth_source_ready=io_name("eth", "source_ready"),
-    eth_source_data=io_name("eth", "source_data"),
-
-    eth_sink_valid=io_name("eth", "sink_valid"),
-    eth_sink_ready=io_name("eth", "sink_ready"),
-    eth_sink_data=io_name("eth", "sink_data"),
-    )
-    except:
-        pass
-
-    try:
-        ios += """
-#define VGA_DE dut->{vga_de}
-#define VGA_HSYNC dut->{vga_hsync}
-#define VGA_VSYNC dut->{vga_vsync}
-#define VGA_R dut->{vga_r}
-#define VGA_G dut->{vga_g}
-#define VGA_B dut->{vga_b}
-""".format(
-    vga_de=io_name("vga", "de"),
-    vga_hsync=io_name("vga", "hsync"),
-    vga_vsync=io_name("vga", "vsync"),
-    vga_r=io_name("vga", "r"),
-    vga_g=io_name("vga", "g"),
-    vga_b=io_name("vga", "b"),
-    )
-    except:
-        pass
-
-    content = ""
-    f = open(template, "r")
-    done = False
-    for l in f:
-        content += l
-        if "/* ios */" in l and not done:
-            content += ios
-            done = True
-
-    f.close()
-    tools.write_to_file("dut_tb.cpp", content)
-
-
-def _build_sim(platform, vns, build_name, include_paths, serial, verbose):
+def _generate_sim_h_struct(name, index, siglist):
+    content = ''
+
+    content += 'struct pad_s {}{}[] = {{\n'.format(name, index)
+    for signame, sigbits, dummy in siglist:
+        content += '    {{ (char*)"{}", {}, NULL }},\n'.format(signame, sigbits)
+    content += '    { NULL, 0, NULL }\n'
+    content += '};\n\n'
+
+    return content
+
+
+def _generate_sim_h(platform):
+    content = """\
+#ifndef __SIM_CORE_H_
+#define __SIM_CORE_H_
+#include "pads.h"
+
+"""
+    for args in platform.sim_requested:
+        content += _generate_sim_h_struct(*args)
+
+    content += """\
+#ifndef __cplusplus
+void lambdasim_init(void **out);
+#endif
+
+#endif /* __SIM_CORE_H_ */
+"""
+    tools.write_to_file("dut_header.h", content)
+
+
+def _generate_sim_cpp_struct(name, index, siglist):
+    content = ''
+
+    for i, (signame, sigbits, sigfname) in enumerate(siglist):
+        content += '    {}{}[{}].signal = &dut->{};\n'.format(name, index, i, sigfname)
+
+    idx_int = 0 if not index else int(index)
+    content += '    lambdasim_register_pads({}{}, (char*)"{}", {});\n\n'.format(name, index, name, idx_int)
+
+    return content
+
+
+def _generate_sim_cpp(platform):
+    content = """\
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "Vdut.h"
+#include <verilated.h>
+#include "dut_header.h"
+
+extern "C" void lambdasim_init(void **out)
+{
+    Vdut *dut;
+
+    dut = new Vdut;
+
+"""
+    for args in platform.sim_requested:
+        content += _generate_sim_cpp_struct(*args)
+
+    content += """\
+    *out=dut;
+}
+"""
+    tools.write_to_file("dut_init.cpp", content)
+
+
+def _generate_sim_variables(include_paths):
     include = ""
     for path in include_paths:
         include += "-I"+path+" "
 
-    build_script_contents = """# Autogenerated by LiteX
-    rm -rf obj_dir/
-verilator {disable_warnings} -O3 --cc dut.v --exe dut_tb.cpp -LDFLAGS "-lpthread -lSDL" -trace {include}
-make -j -C obj_dir/ -f Vdut.mk Vdut
+    content = """\
+SRC_DIR = {}
+INC_DIR = {}
+""".format(core_directory, include)
+    tools.write_to_file("variables.mak", content)
+
+
+def _generate_sim_config(config):
+    content = config.get_json()
+    tools.write_to_file("sim_config.js", content)
+
 
-""".format(
-    disable_warnings="-Wno-fatal",
-    include=include)
+def _build_sim(platform, build_name, verbose):
+    makefile = os.path.join(core_directory, 'Makefile')
+    build_script_contents = """\
+rm -rf obj_dir/
+make -C . -f {}
+mkdir -p modules && cp obj_dir/*.so modules
+""".format(makefile)
     build_script_file = "build_" + build_name + ".sh"
     tools.write_to_file(build_script_file, build_script_contents, force_unix=True)
 
-    _build_tb(platform, vns, serial, os.path.join(sim_directory, "dut_tb.cpp"))
     p = subprocess.Popen(["bash", build_script_file], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
     output, _ = p.communicate()
     output = output.decode('utf-8')
@@ -134,7 +125,8 @@ make -j -C obj_dir/ -f Vdut.mk Vdut
 
 
 def _run_sim(build_name):
-    run_script_contents = """obj_dir/Vdut
+    run_script_contents = """\
+sudo obj_dir/Vdut
 """
     run_script_file = "run_" + build_name + ".sh"
     tools.write_to_file(run_script_file, run_script_contents, force_unix=True)
@@ -145,8 +137,9 @@ def _run_sim(build_name):
 
 class SimVerilatorToolchain:
     def build(self, platform, fragment, build_dir="build", build_name="top",
-            toolchain_path=None, serial="console", run=True, verbose=True):
-        tools.mkdir_noerror(build_dir)
+            toolchain_path=None, serial="console", run=True, verbose=True,
+            sim_config=None):
+        os.makedirs(build_dir, exist_ok=True)
         os.chdir(build_dir)
 
         if not isinstance(fragment, _Fragment):
@@ -163,7 +156,12 @@ class SimVerilatorToolchain:
             if path not in include_paths:
                 include_paths.append(path)
         include_paths += platform.verilog_include_paths
-        _build_sim(platform, v_output.ns, build_name, include_paths, serial, verbose)
+        _generate_sim_h(platform)
+        _generate_sim_cpp(platform)
+        _generate_sim_variables(include_paths)
+        if sim_config:
+            _generate_sim_config(sim_config)
+        _build_sim(platform, build_name, verbose)
 
         if run:
             _run_sim(build_name)