From 446ae57b75ecff6a1ad99457f8d305520ccfcaf0 Mon Sep 17 00:00:00 2001 From: Vamsi K Vytla Date: Fri, 15 Nov 2019 10:47:13 +0100 Subject: [PATCH] build/sim/modules: add XGMII 10Gbps ethernet module Used to simulate SoCs with XGMII 10Gbps ethernet and to do LiteEth verification --- litex/build/sim/core/modules/Makefile | 2 +- .../sim/core/modules/xgmii_ethernet/Makefile | 17 + .../sim/core/modules/xgmii_ethernet/tapcfg | 1 + .../modules/xgmii_ethernet/xgmii_ethernet.c | 387 ++++++++++++++++++ litex/build/sim/verilator.py | 3 +- 5 files changed, 408 insertions(+), 2 deletions(-) create mode 100644 litex/build/sim/core/modules/xgmii_ethernet/Makefile create mode 120000 litex/build/sim/core/modules/xgmii_ethernet/tapcfg create mode 100644 litex/build/sim/core/modules/xgmii_ethernet/xgmii_ethernet.c diff --git a/litex/build/sim/core/modules/Makefile b/litex/build/sim/core/modules/Makefile index fb770b4b..a0b721c5 100644 --- a/litex/build/sim/core/modules/Makefile +++ b/litex/build/sim/core/modules/Makefile @@ -1,4 +1,4 @@ -MODULES = ethernet serial2console serial2tcp clocker +MODULES = xgmii_ethernet ethernet serial2console serial2tcp clocker SHROBJS = $(MODULES:=.so) .PHONY: $(MODULES) diff --git a/litex/build/sim/core/modules/xgmii_ethernet/Makefile b/litex/build/sim/core/modules/xgmii_ethernet/Makefile new file mode 100644 index 00000000..39f54cd8 --- /dev/null +++ b/litex/build/sim/core/modules/xgmii_ethernet/Makefile @@ -0,0 +1,17 @@ +include ../variables.mak +CFLAGS += -Itapcfg/src/include + +all: $(OBJ_DIR)/xgmii_ethernet.so + +include ../rules.mak + +OBJS = $(addprefix $(OBJ_DIR)/, xgmii_ethernet.o tapcfg.o taplog.o) + +$(OBJ_DIR)/xgmii_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/xgmii_ethernet/tapcfg b/litex/build/sim/core/modules/xgmii_ethernet/tapcfg new file mode 120000 index 00000000..d0b6901b --- /dev/null +++ b/litex/build/sim/core/modules/xgmii_ethernet/tapcfg @@ -0,0 +1 @@ +../ethernet/tapcfg/ \ No newline at end of file diff --git a/litex/build/sim/core/modules/xgmii_ethernet/xgmii_ethernet.c b/litex/build/sim/core/modules/xgmii_ethernet/xgmii_ethernet.c new file mode 100644 index 00000000..45f7fc75 --- /dev/null +++ b/litex/build/sim/core/modules/xgmii_ethernet/xgmii_ethernet.c @@ -0,0 +1,387 @@ +#include +#include +#include +#include +#include "error.h" + +#include +#include +#include +#include +#include "tapcfg.h" +#include "modules.h" + +#define ETH_LEN 9000 +struct eth_packet_s { + char data[ETH_LEN]; + size_t len; + struct eth_packet_s *next; +}; + +#define DW_64 +struct session_s { + #ifdef DW_64 + unsigned long int tx; + unsigned long int rx; + #else + unsigned int tx; + unsigned int rx; + #endif + char tx_valid; + char rx_valid; + char rx_ready; + + #ifdef DW_64 + unsigned long int *tx_data; + unsigned long int *rx_data; + #else + unsigned int *tx_data; + unsigned int *rx_data; + #endif + + char *tx_ctl; + char terminate; + char preamble; + char *rx_ctl; + char *sys_clk; + + tapcfg_t *tapcfg; + int fd; + char databuf[ETH_LEN]; + char rx_state; + int datalen; + char inbuf[ETH_LEN]; + int inlen; + int insent; + struct eth_packet_s *ethpack; + struct event *ev; +}; + +static struct event_base *base=NULL; + +int litex_sim_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 litex_sim_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 xgmii_ethernet_start(void *b) +{ + base = (struct event_base *) b; + printf("[xgmii_ethernet] loaded (%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_READ) { + ep = malloc(sizeof(struct eth_packet_s)); + memset(ep, 0, sizeof(struct eth_packet_s)); + ep->len = tapcfg_read(s->tapcfg, ep->data, ETH_LEN); + 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 xgmii_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 = litex_sim_module_get_args(args, "interface", &c_tap); + { + if(RC_OK != ret) + goto out; + } + ret = litex_sim_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); + + 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 xgmii_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")) { + litex_sim_module_pads_get(pads, "rx_data", (void**)&s->rx_data); + litex_sim_module_pads_get(pads, "rx_ctl", (void**)&s->rx_ctl); + litex_sim_module_pads_get(pads, "tx_data", (void**)&s->tx_data); + litex_sim_module_pads_get(pads, "tx_ctl", (void**)&s->tx_ctl); + } + + if(!strcmp(plist->name, "sys_clk")) + litex_sim_module_pads_get(pads, "sys_clk", (void**)&s->sys_clk); + +out: + return ret; +} + +#ifdef DW_64 +char g_preamble = 0; +char g_dw = 64; +unsigned long int g_mask = 0xff; +unsigned long int g_idle = 0x0707070707070707; +#else +char g_preamble = 0; +char g_dw = 32; +unsigned int g_mask = 0xff; +unsigned int g_idle = 0x07070707; +#endif + +static int xgmii_ethernet_tick(void *sess) +{ + struct session_s *s = (struct session_s*)sess; + struct eth_packet_s *pep; + + if(*s->sys_clk == 0) { + s->preamble=0; + return RC_OK; + } + + #ifdef DW_64 + unsigned long int u; + #else + unsigned int u; + #endif + // XGMII stuff + u = *s->tx_data; + s->tx = u; + // printf("%16lx\t\t%x\n", u, *s->tx_ctl & g_mask); + if (u != g_idle) { + // printf("%16lx\t\t%x\n", u, *s->tx_ctl & g_mask); + // printf("preamble: %02x\n", g_preamble); + } + + if ((g_preamble == 0) && (*s->tx_ctl & g_mask) == 0x1) { + g_preamble = (g_dw == 64)? 2: 1; + } else if (g_preamble == 1) { + g_preamble = 2; + } else if (g_preamble == 2) { + if ((*s->tx_ctl & g_mask) != 0) { + // Intentionally ignoring errors for now (since we don't really have retransmission) + // So this means last word + // TODO: Check for end of frame mid word + for (int m = 0; m < (g_dw >> 3); m++) { + char mask = 1 << m; + if ((*s->tx_ctl & mask) == 0) + s->databuf[s->datalen++] = (char) ((u & (g_mask << (8*m))) >> (8*m)); + } + + // Enable for debugging + printf("Sending: \n"); + for(int i=0; i < s->datalen; printf("%02x ", s->databuf[i++] & 0xff)); + printf("\n%u\n", s->datalen); + printf("Sent %u\n", s->datalen); + tapcfg_write(s->tapcfg, s->databuf, s->datalen); + s->datalen=0; + g_preamble=0; + } else { + for (int i = 0; i < (g_dw >> 3); i++) { + assert(s->datalen <= ETH_LEN); + s->databuf[s->datalen++]= (char) ((u & (g_mask << (8 * i))) >> (8*i)); + } + } + } + + #ifdef DW_64 + unsigned long int local_data = 0x0707070707070707; + unsigned long int temp_data = 0; // This is here just to avoid an ugly cast later + #else + unsigned int local_data = 0x07070707; + unsigned int temp_data = 0; + #endif + char temp_ctl = 0; + char local_ctl = 0; + if(s->inlen) { + // printf("%x ", s->rx_state); + if (s->rx_state == 0) { + *s->rx_data = 0xd5555555555555fb; + *s->rx_ctl = 1; + s->rx_state = 1; + } else if ((s->rx_state == 1) && (s->insent + (g_dw >> 3) < s->inlen)) { + *s->rx_ctl = 0; + local_data = 0; + for (unsigned int i = 0; i < (g_dw >> 3); i++) { + temp_data = (unsigned char) s->inbuf[s->insent++]; + local_data |= (temp_data << (i << 3)); + } + *s->rx_data = local_data; + } else if ((s->rx_state == 1) && (s->insent + (g_dw >> 3) >= s->inlen)) { + // printf("%d, %d\n", s->insent, s->inlen); + local_data = 0; + for (unsigned int i = 0; i < (g_dw >> 3); i++) { + if (s->insent < s->inlen) { + temp_data = (unsigned char) s->inbuf[s->insent++]; + } else if (s->insent == s->inlen) { + temp_data = (unsigned char) 0xfd; + temp_ctl = 1; + s->insent++; + } else { + temp_data = (unsigned char) 0x07; + temp_ctl = 1; + s->insent++; + } + local_data |= (temp_data << (i << 3)); + local_ctl |= (temp_ctl << i); + //printf("%16lx %02x\n", local_data, local_ctl); + } + *s->rx_data = local_data; + *s->rx_ctl = local_ctl; + if (s->insent == s->inlen) + s->rx_state = 2; + else { + s->insent = 0; + s->inlen = 0; + s->rx_state = 0; + } + } else if (s->rx_state == 2) { + *s->rx_ctl = 0xff; + *s->rx_data = 0x07070707070707fd; + s->insent =0; + s->inlen = 0; + s->rx_state = 0; + } else { + *s->rx_ctl = 0xff; + *s->rx_data = local_data; + } + // printf("%x, %16lx, %x\n", s->rx_state, *s->rx_data, *s->rx_ctl); + } else { + *s->rx_ctl = 0xff; + *s->rx_data = local_data; + if(s->ethpack) { + memcpy(s->inbuf, s->ethpack->data, s->ethpack->len); + printf("Received: %ld\n", s->ethpack->len ); + for(int i=0; i< s->ethpack->len;) { + printf("%02x ", s->inbuf[i++] & 0xff); + if (i%8 == 0 && i > 0); + } + printf("\n"); + s->inlen = s->ethpack->len; + pep=s->ethpack->next; + free(s->ethpack); + s->ethpack=pep; + } + } + return RC_OK; +} + +static struct ext_module_s ext_mod = { + "xgmii_ethernet", + xgmii_ethernet_start, + xgmii_ethernet_new, + xgmii_ethernet_add_pads, + NULL, + xgmii_ethernet_tick +}; + +int litex_sim_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/verilator.py b/litex/build/sim/verilator.py index df664596..3f9b40fc 100644 --- a/litex/build/sim/verilator.py +++ b/litex/build/sim/verilator.py @@ -206,7 +206,8 @@ class SimVerilatorToolchain: # run if run: _compile_sim(build_name, verbose) - _run_sim(build_name, as_root=sim_config.has_module("ethernet")) + _run_sim(build_name, as_root=sim_config.has_module("ethernet") or + sim_config.has_module("xgmii_ethernet")) os.chdir("../../") -- 2.30.2