diff --git a/litex/boards/targets/sim.py b/litex/boards/targets/sim.py index 0f0d657d0..50cac545d 100755 --- a/litex/boards/targets/sim.py +++ b/litex/boards/targets/sim.py @@ -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 index 000000000..655a7bc07 --- /dev/null +++ b/litex/build/sim/config.py @@ -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 index 000000000..0388cdda0 --- /dev/null +++ b/litex/build/sim/core/Makefile @@ -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 index 000000000..1a81b8fad --- /dev/null +++ b/litex/build/sim/core/error.h @@ -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 index 000000000..ba8cdb6a0 --- /dev/null +++ b/litex/build/sim/core/libdylib.c @@ -0,0 +1,366 @@ +#include +#include +#include +#include +#include + +#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 + +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 + +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 index 000000000..a92a3b05e --- /dev/null +++ b/litex/build/sim/core/libdylib.h @@ -0,0 +1,118 @@ +#ifndef LIBDYLIB_H +#define LIBDYLIB_H + +#define LIBDYLIB_VERSION 0x020000 +#define LIBDYLIB_VERSION_STR "2.0.0" + +#include +#include + +#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 index 000000000..8d7d30a6b --- /dev/null +++ b/litex/build/sim/core/modules.c @@ -0,0 +1,164 @@ +/* Copyright (C) 2017 LambdaConcept */ + +#include +#include +#include +#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 index 000000000..1bfabdf6d --- /dev/null +++ b/litex/build/sim/core/modules.h @@ -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 index 000000000..fb770b4bb --- /dev/null +++ b/litex/build/sim/core/modules/Makefile @@ -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 index 000000000..3fd45b454 --- /dev/null +++ b/litex/build/sim/core/modules/clocker/Makefile @@ -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 index 000000000..080ebf314 --- /dev/null +++ b/litex/build/sim/core/modules/clocker/clocker.c @@ -0,0 +1,113 @@ +#include +#include +#include +#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 index 000000000..211b76d5e --- /dev/null +++ b/litex/build/sim/core/modules/ethernet/Makefile @@ -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 index 000000000..90d3f513c --- /dev/null +++ b/litex/build/sim/core/modules/ethernet/ethernet.c @@ -0,0 +1,314 @@ +#include +#include +#include +#include "error.h" + +#include +#include +#include +#include +#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 index 000000000..7cbe265c8 --- /dev/null +++ b/litex/build/sim/core/modules/rules.mak @@ -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 index 000000000..7d752baa5 --- /dev/null +++ b/litex/build/sim/core/modules/serial2console/Makefile @@ -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 index 000000000..850a56a8a --- /dev/null +++ b/litex/build/sim/core/modules/serial2console/serial2console.c @@ -0,0 +1,208 @@ +#include +#include +#include +#include "error.h" +#include +#include +#include +#include +#include + +#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 index 000000000..403644496 --- /dev/null +++ b/litex/build/sim/core/modules/serial2tcp/Makefile @@ -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 index 000000000..fbe539de6 --- /dev/null +++ b/litex/build/sim/core/modules/serial2tcp/serial2tcp.c @@ -0,0 +1,300 @@ +#include +#include +#include +#include "error.h" +#include +#include +#include +#include + +#include +#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 index 000000000..764417645 --- /dev/null +++ b/litex/build/sim/core/modules/variables.mak @@ -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 index 000000000..232bfeeb4 --- /dev/null +++ b/litex/build/sim/core/pads.c @@ -0,0 +1,79 @@ +/* Copyright (C) 2017 LambdaConcept */ + +#include +#include +#include +#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 index 000000000..8385cb2ef --- /dev/null +++ b/litex/build/sim/core/pads.h @@ -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 index 000000000..a969f1f13 --- /dev/null +++ b/litex/build/sim/core/parse.c @@ -0,0 +1,339 @@ +/* Copyright (C) 2017 LambdaConcept */ + +#include +#include +#include +#include +#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 index 000000000..9c87380c1 --- /dev/null +++ b/litex/build/sim/core/sim.c @@ -0,0 +1,244 @@ +/* Copyright (C) 2017 LambdaConcept */ + +#include +#include +#include +#include +#ifndef _WIN32 +#include +# ifdef _XOPEN_SOURCE_EXTENDED +# include +# endif +#include +#endif +#include +#include "error.h" +#include "modules.h" +#include "pads.h" +#include "veril.h" + +#include +#include +#include + +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 index 000000000..54d5f5ad1 --- /dev/null +++ b/litex/build/sim/core/tinydir.h @@ -0,0 +1,804 @@ +/* +Copyright (c) 2013-2016, tinydir authors: +- Cong Xu +- Lautis Sun +- Baudouin Feildel +- Andargor +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 +#include +#include +#ifdef _MSC_VER +# define WIN32_LEAN_AND_MEAN +# include +# include +# pragma warning(push) +# pragma warning (disable : 4996) +#else +# include +# include +# include +# include +#endif +#ifdef __MINGW32__ +# include +#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 +#define _TINYDIR_PATH_MAX MAX_PATH +#elif defined __linux__ +#include +#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 +#endif +#if _BSD_SOURCE || _SVID_SOURCE || \ + (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700) +# define _TINYDIR_HAS_DIRFD +# include +#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 +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 index 000000000..cd5b2db09 --- /dev/null +++ b/litex/build/sim/core/veril.cpp @@ -0,0 +1,41 @@ +/* Copyright (C) 2017 LambdaConcept */ + +#include +#include +#include +#include "Vdut.h" +#include "Vdut.h" +#include "verilated.h" +#include "verilated_vcd_c.h" +#include + + +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 index 000000000..ce27a32ca --- /dev/null +++ b/litex/build/sim/core/veril.h @@ -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 index 8d0791bef..000000000 --- a/litex/build/sim/dut_tb.cpp +++ /dev/null @@ -1,492 +0,0 @@ -// This file is Copyright (c) 2015 Florent Kermarrec -// License: BSD -#include "Vdut.h" -#include "verilated.h" -#include "verilated_vcd_c.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -/* 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; itick%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); -} diff --git a/litex/build/sim/platform.py b/litex/build/sim/platform.py index a86d1d08c..f76532a4c 100644 --- a/litex/build/sim/platform.py +++ b/litex/build/sim/platform.py @@ -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) diff --git a/litex/build/sim/verilator.py b/litex/build/sim/verilator.py index 5335d987b..594eaa857 100644 --- a/litex/build/sim/verilator.py +++ b/litex/build/sim/verilator.py @@ -1,4 +1,5 @@ # This file is Copyright (c) 2015-2016 Florent Kermarrec +# 2017 Pierre-Olivier Vauboin # 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) +def _generate_sim_h_struct(name, index, siglist): + content = '' - ios = """ -#define SYS_CLK dut->{sys_clk} -""".format(sys_clk=io_name("sys_clk")) + 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' - 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) + return content -def _build_sim(platform, vns, build_name, include_paths, serial, verbose): +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 +#include +#include +#include "Vdut.h" +#include +#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) -""".format( - disable_warnings="-Wno-fatal", - include=include) + +def _generate_sim_config(config): + content = config.get_json() + tools.write_to_file("sim_config.js", content) + + +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)