litex_sim: add GMII verilator module and add support in litex_sim

Signed-off-by: Leon Schuermann <leon@is.currently.online>
This commit is contained in:
Leon Schuermann 2021-08-08 14:40:37 +02:00
parent 7b533a032d
commit 08a14e8cf1
5 changed files with 608 additions and 5 deletions

View File

@ -1,5 +1,5 @@
include ../variables.mak include ../variables.mak
MODULES = xgmii_ethernet ethernet serial2console serial2tcp clocker spdeeprom MODULES = xgmii_ethernet ethernet serial2console serial2tcp clocker spdeeprom gmii_ethernet
.PHONY: $(MODULES) .PHONY: $(MODULES)
all: $(MODULES) all: $(MODULES)

View File

@ -0,0 +1,20 @@
include ../../variables.mak
UNAME_S := $(shell uname -s)
include $(SRC_DIR)/modules/rules.mak
CFLAGS += -I$(TAPCFG_DIRECTORY)/src/include
OBJS = $(MOD).o tapcfg.o taplog.o
$(MOD).so: $(OBJS)
ifeq ($(UNAME_S),Darwin)
$(CC) $(LDFLAGS) -o $@ $^
else
$(CC) $(LDFLAGS) -Wl,-soname,$@ -o $@ $^
endif
tapcfg.o: $(TAPCFG_DIRECTORY)/src/lib/tapcfg.c
$(CC) $(CFLAGS) -c -o $@ $<
taplog.o: $(TAPCFG_DIRECTORY)/src/lib/taplog.c
$(CC) $(CFLAGS) -c -o $@ $<

View File

@ -0,0 +1,570 @@
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "error.h"
#include <event2/listener.h>
#include <event2/util.h>
#include <event2/event.h>
#include <json-c/json.h>
#include <zlib.h>
#include "tapcfg.h"
#include "modules.h"
// ---------- SETTINGS ---------- //
// Ethernet MTU. Must be >= MIN_ETH_LEN.
#define ETH_LEN 9000
// MAC address for the host's TAP interface
static const char macadr[6] = {0xaa, 0xb6, 0x24, 0x69, 0x77, 0x21};
// Debug (print to stderr) invalid bus states
#define GMII_TX_DEBUG_INVAL_SIGNAL
// Hex-dump transmitted (Sim -> TAP) packets to stderr
//#define GMII_TX_DEBUG
// Hex-dump received (TAP -> Sim) packets to stderr
//#define GMII_RX_DEBUG
// ------------------------------ //
#define MIN_ETH_LEN 60
// RX incoming (TAP -> Sim) Ethernet packet queue structs
typedef struct eth_packet_queue {
// Does not contain the trailing CRC32 checksum
uint8_t data[ETH_LEN];
size_t len;
struct eth_packet_queue *next;
} eth_packet_queue_t;
typedef struct gmii_state {
// ---------- SIMULATION & BUS STATE ----------
// GMII bus signals
//
// Receive (TAP -> SIM)
uint8_t *rx_data_signal;
bool *rx_dv_signal;
bool *rx_er_signal;
// Transmit (Sim -> TAP)
uint8_t *tx_data_signal;
uint8_t *tx_en_signal;
uint8_t *tx_er_signal;
// Collision detection and carrier sensing currently not implemented
// bool *rx_col_signal;
// bool *rx_cs_signal;
// RX clock signal and edge state
bool *rx_clk;
clk_edge_state_t rx_clk_edge;
// TX clock signal and edge state
// current only gigabit clock supported
bool *tx_clk;
clk_edge_state_t tx_clk_edge;
// ---------- GLOBAL STATE --------
tapcfg_t *tapcfg;
int tap_fd;
// ---------- TX (Sim -> TAP) STATE ---------
// Packet currently being transmitted over the GMII bus (Sim -> TAP).
uint8_t current_tx_pkt[ETH_LEN];
size_t current_tx_len;
bool prev_tx_en;
bool current_tx_abrt;
bool current_tx_drop_warning;
uint8_t current_tx_preamble_state;
// ---------- RX (TAP -> Sim) STATE ---------
// Packet currently being received over the GMII bus (TAP -> Sim). Packets
// copied here are already removed from the TAP incoming queue. Fields are
// valid if current_rx_len != 0. This field includes the CRC32 checksum.
uint8_t current_rx_pkt[ETH_LEN + sizeof(uint32_t)];
uint8_t current_rx_preamble_state;
size_t current_rx_len;
size_t current_rx_progress;
// Linked list of pending RX (TAP -> Sim) packets. `tail` is only valid when
// head != NULL.
eth_packet_queue_t *pending_rx_pkt_head;
eth_packet_queue_t *pending_rx_pkt_tail;
struct event *ev;
} gmii_ethernet_state_t;
// Shared libevent state, set on module init
static struct event_base *base = NULL;
/**
* Advance the RX (TAP -> Sim) state machine, producing a new bus snapshot
*
* This method must be called on the rising clock edge. It will produce a GMII
* bus word which needs to be presented to the device.
*
* This function will detect pending RX packets in the queue and remove them
* accordingly. Thus it is important that this function will be called on every
* rising clock edge, regardless of whether a packet is currently being
* transmitted.
*/
static void gmii_ethernet_rx_adv(gmii_ethernet_state_t *s,
uint64_t time_ps) {
// Check whether we are currently transmitting a packet over the GMII
// interface (i.e. whether there are still bytes left in the packet input
// buffer)
if (s->current_rx_len) {
// TODO:
// assert s->current_rx_progress < s->current_rx_len
// There are bytes to send, transfer the preamble, start of frame
// character and data onto the bus.
if (s->current_rx_preamble_state < 8) {
// Transmit 56 bits (7 bytes) of 0x55(preamble), followed by 1 byte
// 0xD5 (start of frame)
switch (s->current_rx_preamble_state) {
case 7:
*s->rx_data_signal = 0xD5;
break;
default:
*s->rx_data_signal = 0x55;
}
*s->rx_dv_signal = true;
*s->rx_er_signal = false;
s->current_rx_preamble_state++;
} else if (s->current_rx_progress < s->current_rx_len) {
*s->rx_data_signal = s->current_rx_pkt[s->current_rx_progress++];
*s->rx_dv_signal = true; // Data on the bus is valid
*s->rx_er_signal = false; // No receive error in this data word
} else {
// Finished transmitting, reset progress and length to zero.
//
// This cannot be combined with the branch above to ensure that we
// have at least one cycle where rx_dv is deasserted.
s->current_rx_preamble_state = 0;
s->current_rx_progress = 0;
s->current_rx_len = 0;
*s->rx_data_signal = 0;
*s->rx_dv_signal = 0;
*s->rx_er_signal = 0;
}
} else {
// No packet to transmit, indicate the bus is idle by deasserting `data
// valid` (`rx_dv_signal`)
*s->rx_data_signal = 0;
*s->rx_dv_signal = false;
*s->rx_er_signal = false;
}
if (!s->current_rx_len) {
// No packet is currently in transit (or one has just completed
// reception). Check if there is an outstanding packet from the TAP
// interface and copy it into the input buffer
if (s->pending_rx_pkt_head) {
eth_packet_queue_t* popped_rx_pkt;
// CRITICAL REGION {
// Advance the pending packets queue, removing the copied
// packet and freeing its allocated memory.
popped_rx_pkt = s->pending_rx_pkt_head;
s->pending_rx_pkt_head = s->pending_rx_pkt_head->next;
// } CRITICAL REGION
// Determine the maximum length to copy. We must not copy
// beyond the length of s->current_rx_pkt and need to
// reserve at least 4 bytes for the CRC32 to be appended.
size_t copy_len =
(popped_rx_pkt->len
<= sizeof(s->current_rx_pkt) - sizeof(uint32_t))
? popped_rx_pkt->len
: sizeof(s->current_rx_pkt) - sizeof(uint32_t);
// Copy the packet into the buffer
memcpy(s->current_rx_pkt, popped_rx_pkt->data, copy_len);
// Calculate the CRC32 checksum and append it to the
// packet data. This uses the original packet's length. If
// the original packet didn't fit into the buffer, the CRC
// is going to be wrong and thus the packet being cut off
// can be detected.
uint32_t crc = crc32(0, popped_rx_pkt->data, popped_rx_pkt->len);
s->current_rx_pkt[copy_len + 3] = (crc >> 24) & 0xFF;
s->current_rx_pkt[copy_len + 2] = (crc >> 16) & 0xFF;
s->current_rx_pkt[copy_len + 1] = (crc >> 8) & 0xFF;
s->current_rx_pkt[copy_len + 0] = (crc >> 0) & 0xFF;
#ifdef GMII_RX_DEBUG
fprintf(stderr, "\n----------------------------------\n"
"Received packet with %ld bytes\n", copy_len);
for (size_t i = 0; i < copy_len; i++) {
fprintf(stderr, "%02x", s->current_rx_pkt[i] & 0xff);
if (i != 0 && (i + 1) % 16 == 0) {
fprintf(stderr, "\n");
} else if (i != 0 && (i + 1) % 8 == 0) {
fprintf(stderr, " ");
}
}
fprintf(stderr, "\n----------------------------------\n");
#endif
// Set the packet length (including CRC32) and thus
// indicate that a packet is ready to be transmitted over
// the GMII interface
s->current_rx_len = copy_len + sizeof(uint32_t);
// Release the packet data memory
free(popped_rx_pkt);
}
}
}
/**
* Advance the TX (Sim -> TAP) state machine based on a GMII bus word
*
* This method must be called on a rising clock edge (when the device has placed
* a new GMII bus word).
*
* This function will detect frames sent by the device and place them on the TAP
* network interface.
*/
static void gmii_ethernet_tx_adv(gmii_ethernet_state_t *s, uint64_t time_ps) {
// Check whether the device is currently transmitting a new packet or
// continuing an old transmission based on the previous tx_en_signal value
if (s->prev_tx_en == false && *s->tx_en_signal == true) {
// New transmission, reset the current packet length and transmission
// abort state
s->current_tx_len = 0;
s->current_tx_preamble_state = 0;
s->current_tx_abrt = false;
s->current_tx_drop_warning = false;
}
if (s->current_tx_abrt) {
// The current transmission has experienced an error. Wait for this
// condition to be reset on the next new initiated transmission.
} else if (*s->tx_en_signal == true && *s->tx_er_signal == true) {
// Error condition on the bus, can't meaningfully handle. Abort
// transmission.
fprintf(stderr, "[gmii_ethernet]: TX error %02x %u %u\n",
*s->tx_data_signal, *s->tx_en_signal, *s->tx_er_signal);
} else if (*s->tx_en_signal == true
&& s->current_tx_len == ETH_LEN
&& !s->current_tx_drop_warning) {
// Data on bus is valid and transmission has not previously experienced
// an error condition, but no space left for the packet. Warn the user
// once per such error in a single transmission.
s->current_tx_drop_warning = true;
fprintf(stderr, "[gmii_ethernet]: TX ETH_LEN reached, "
"dropping frame data. Check the MTU.\n");
} else if (*s->tx_en_signal == true && s->current_tx_len < ETH_LEN) {
// Data valid, transmission has not previously experienced an error
// condition and sufficient space left in the buffer. Expect preamble or
// valid data.
// Read in the preamble. If it doesn't match, report and error and abort
// the current transmission.
bool preamble_error = false;
if (s->current_tx_preamble_state < 7) {
// Expect 56 bits (7 bytes) of 0x55
if (*s->tx_data_signal == 0x55) {
s->current_tx_preamble_state++;
} else {
preamble_error = true;
}
} else if (s->current_tx_preamble_state < 8) {
// Expect 8 bits (1 byte) of 0xD5
if (*s->tx_data_signal == 0xD5) {
s->current_tx_preamble_state++;
} else {
preamble_error = true;
}
} else {
s->current_tx_pkt[s->current_tx_len++] = *s->tx_data_signal;
}
if (preamble_error) {
fprintf(stderr, "[gmii_ethernet]: TX preamble error! %u %02x\n",
s->current_tx_preamble_state, *s->tx_data_signal);
s->current_tx_abrt = true;
}
}
if (s->prev_tx_en == true
&& *s->tx_en_signal == false
&& s->current_tx_len >= 1
&& !s->current_tx_abrt) {
// Falling edge on tx_en, the frame has been transmitted into the
// buffer. Transmit over the TAP interface.
// Length without frame check sequence
size_t pkt_len =
(s->current_tx_len > 3) ? s->current_tx_len - 4 : 0;
#ifdef GMII_TX_DEBUG
fprintf(stderr, "\n----------------------------------\n"
"Transmitted packet with %ld bytes\n", pkt_len);
for (size_t i = 0; i < pkt_len; i++) {
fprintf(stderr, "%02x", s->current_tx_pkt[i] & 0xff);
if (i != 0 && (i + 1) % 16 == 0) {
fprintf(stderr, "\n");
} else if (i != 0 && (i + 1) % 8 == 0) {
fprintf(stderr, " ");
}
}
fprintf(stderr, "\n----------------------------------\n");
#endif
if (s->current_tx_len < 4) {
fprintf(stderr, "[gmii_ethernet]: TX packet too short to contain "
"frame check sequence\n");
} else {
uint32_t crc = crc32(0, s->current_tx_pkt, pkt_len);
if (!((s->current_tx_pkt[pkt_len + 0] == ((crc >> 0) & 0xFF))
&& (s->current_tx_pkt[pkt_len + 1] == ((crc >> 8) & 0xFF))
&& (s->current_tx_pkt[pkt_len + 2] == ((crc >> 16) & 0xFF))
&& (s->current_tx_pkt[pkt_len + 3] == ((crc >> 24) & 0xFF))))
{
fprintf(stderr, "[gmii_ethernet]: TX packet FCS mismatch. "
"Expected: %08x. Actual: %08x.\n", crc,
(uint32_t) s->current_tx_pkt[pkt_len + 0] << 0
| (uint32_t) s->current_tx_pkt[pkt_len + 1] << 8
| (uint32_t) s->current_tx_pkt[pkt_len + 2] << 16
| (uint32_t) s->current_tx_pkt[pkt_len + 3] << 24);
}
}
tapcfg_write(s->tapcfg, s->current_tx_pkt, pkt_len);
}
// Store the previous tx_en_signal for edge detection
s->prev_tx_en = *s->tx_en_signal;
}
static int gmii_ethernet_tick(void *state, uint64_t time_ps) {
gmii_ethernet_state_t *s = (gmii_ethernet_state_t*) state;
if (clk_pos_edge(&s->tx_clk_edge, *s->tx_clk)) {
gmii_ethernet_tx_adv(s, time_ps);
}
if (clk_pos_edge(&s->rx_clk_edge, *s->rx_clk)) {
gmii_ethernet_rx_adv(s, time_ps);
}
return RC_OK;
}
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, "[gmii_ethernet]: error parsing json arg: %s\n", args);
ret = RC_JSERROR;
goto out;
}
if (!json_object_is_type(jsobj, json_type_object)) {
fprintf(stderr, "[gmii_ethernet]: 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, "[gmii_ethernet]: 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 = RC_OK;
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 event_handler(int tap_fd, short event, void *arg) {
gmii_ethernet_state_t *s = arg;
// Expect a new TAP packet if the socket has become readable
if (event & EV_READ) {
eth_packet_queue_t *rx_pkt =
malloc(sizeof(eth_packet_queue_t));
// Read the TAP packet into the buffer, extending its length
// to the minimum required Ethernet frame length if necessary.
int read_len = tapcfg_read(s->tapcfg, rx_pkt->data, ETH_LEN);
if (read_len < 0) {
// An error occured while reading from the TAP interface,
// report, free the packet and abort.
fprintf(stderr, "[gmii_ethernet]: TAP read error %d\n", read_len);
free(rx_pkt);
return;
} else if (read_len < MIN_ETH_LEN) {
// To avoid leaking any data, set the packet's contents
// after the proper received length to zero.
memset(&rx_pkt->data[read_len], 0, MIN_ETH_LEN - read_len);
rx_pkt->len = MIN_ETH_LEN;
} else {
// A packet larger than the minimum Ethernet frame length
// has been read.
rx_pkt->len = read_len;
}
// Packet is inserted into the back of the queue, thus no next
// packet.
rx_pkt->next = NULL;
// CRITICAL REGION {
// Append the received packet to the packet queue
if (!s->pending_rx_pkt_head) {
s->pending_rx_pkt_head = rx_pkt;
s->pending_rx_pkt_tail = rx_pkt;
} else {
s->pending_rx_pkt_tail->next = rx_pkt;
s->pending_rx_pkt_tail = rx_pkt;
}
// } CRITICAL REGION
}
}
static int gmii_ethernet_add_pads(void *state, struct pad_list_s *plist) {
int ret = RC_OK;
gmii_ethernet_state_t *s = (gmii_ethernet_state_t*) state;
struct pad_s *pads;
if (!state || !plist) {
ret = RC_INVARG;
goto out;
}
pads = plist->pads;
if (!strcmp(plist->name, "gmii_eth")) {
litex_sim_module_pads_get(pads, "rx_data", (void**) &s->rx_data_signal);
litex_sim_module_pads_get(pads, "rx_dv", (void**) &s->rx_dv_signal);
litex_sim_module_pads_get(pads, "rx_er", (void**) &s->rx_er_signal);
litex_sim_module_pads_get(pads, "tx_data", (void**) &s->tx_data_signal);
litex_sim_module_pads_get(pads, "tx_en", (void**) &s->tx_en_signal);
litex_sim_module_pads_get(pads, "tx_er", (void**) &s->tx_er_signal);
}
if (!strcmp(plist->name, "sys_clk")) {
// TODO: currently the single sys_clk signal is used for both the RX and
// TX GMII clock signals. This should be changed.
litex_sim_module_pads_get(pads, "sys_clk", (void**)&s->rx_clk);
s->tx_clk = s->rx_clk;
}
out:
return ret;
}
static int gmii_ethernet_start(void *b) {
base = (struct event_base *) b;
printf("[gmii_ethernet] loaded (%p)\n", base);
return RC_OK;
}
static int gmii_ethernet_new(void **state, char *args) {
int ret = RC_OK;
char *c_tap = NULL;
char *c_tap_ip = NULL;
gmii_ethernet_state_t *s = NULL;
struct timeval tv = {10, 0};
if (!state) {
ret = RC_INVARG;
goto out;
}
s = (gmii_ethernet_state_t*)malloc(sizeof(gmii_ethernet_state_t));
if (!s) {
ret = RC_NOENMEM;
goto out;
}
memset(s, 0, sizeof(gmii_ethernet_state_t));
ret = litex_sim_module_get_args(args, "interface", &c_tap);
if (ret != RC_OK) {
goto out;
}
ret = litex_sim_module_get_args(args, "ip", &c_tap_ip);
if (ret != RC_OK) {
goto out;
}
s->tapcfg = tapcfg_init();
tapcfg_start(s->tapcfg, c_tap, 0);
s->tap_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->tap_fd, EV_READ | EV_PERSIST, event_handler, s);
event_add(s->ev, &tv);
out:
*state = (void*) s;
return ret;
}
static struct ext_module_s ext_mod = {
"gmii_ethernet",
gmii_ethernet_start,
gmii_ethernet_new,
gmii_ethernet_add_pads,
NULL,
gmii_ethernet_tick
};
int litex_sim_ext_module_init(int (*register_module)(struct ext_module_s *)) {
int ret = RC_OK;
// Initiate calculation of zlib's CRC32 lookup table such that multithreaded
// calls to crc32() are safe.
get_crc_table();
ret = register_module(&ext_mod);
return ret;
}

View File

@ -235,9 +235,9 @@ class SimVerilatorToolchain:
raise OSError(msg) raise OSError(msg)
_compile_sim(build_name, verbose) _compile_sim(build_name, verbose)
run_as_root = False run_as_root = False
if sim_config.has_module("ethernet"): if sim_config.has_module("ethernet") \
run_as_root = True or sim_config.has_module("xgmii_ethernet") \
if sim_config.has_module("xgmii_ethernet"): or sim_config.has_module("gmii_ethernet"):
run_as_root = True run_as_root = True
_run_sim(build_name, as_root=run_as_root, interactive=interactive) _run_sim(build_name, as_root=run_as_root, interactive=interactive)

View File

@ -30,6 +30,7 @@ from litedram.modules import parse_spd_hexdump
from litedram.phy.model import sdram_module_nphases, get_sdram_phy_settings from litedram.phy.model import sdram_module_nphases, get_sdram_phy_settings
from litedram.phy.model import SDRAMPHYModel from litedram.phy.model import SDRAMPHYModel
from liteeth.phy.gmii import LiteEthPHYGMII
from liteeth.phy.xgmii import LiteEthPHYXGMII from liteeth.phy.xgmii import LiteEthPHYXGMII
from liteeth.phy.model import LiteEthPHYModel from liteeth.phy.model import LiteEthPHYModel
from liteeth.mac import LiteEthMAC from liteeth.mac import LiteEthMAC
@ -76,6 +77,14 @@ _io = [
Subsignal("tx_data", Pins(64)), Subsignal("tx_data", Pins(64)),
Subsignal("tx_ctl", Pins(8)), Subsignal("tx_ctl", Pins(8)),
), ),
("gmii_eth", 0,
Subsignal("rx_data", Pins(8)),
Subsignal("rx_dv", Pins(1)),
Subsignal("rx_er", Pins(1)),
Subsignal("tx_data", Pins(8)),
Subsignal("tx_en", Pins(1)),
Subsignal("tx_er", Pins(1)),
),
("i2c", 0, ("i2c", 0,
Subsignal("scl", Pins(1)), Subsignal("scl", Pins(1)),
Subsignal("sda_out", Pins(1)), Subsignal("sda_out", Pins(1)),
@ -185,6 +194,8 @@ class SimSoC(SoCCore):
self.submodules.ethphy = LiteEthPHYModel(self.platform.request("eth", 0)) self.submodules.ethphy = LiteEthPHYModel(self.platform.request("eth", 0))
elif ethernet_phy_model == "xgmii": elif ethernet_phy_model == "xgmii":
self.submodules.ethphy = LiteEthPHYXGMII(None, self.platform.request("xgmii_eth", 0), model=True) self.submodules.ethphy = LiteEthPHYXGMII(None, self.platform.request("xgmii_eth", 0), model=True)
elif ethernet_phy_model == "gmii":
self.submodules.ethphy = LiteEthPHYGMII(None, self.platform.request("gmii_eth", 0), model=True)
else: else:
raise ValueError("Unknown Ethernet PHY model:", ethernet_phy_model) raise ValueError("Unknown Ethernet PHY model:", ethernet_phy_model)
# Ethernet MAC # Ethernet MAC
@ -306,7 +317,7 @@ def sim_args(parser):
parser.add_argument("--sdram-from-spd-dump", default=None, help="Generate SDRAM module based on data from SPD EEPROM dump") parser.add_argument("--sdram-from-spd-dump", default=None, help="Generate SDRAM module based on data from SPD EEPROM dump")
parser.add_argument("--sdram-verbosity", default=0, help="Set SDRAM checker verbosity") parser.add_argument("--sdram-verbosity", default=0, help="Set SDRAM checker verbosity")
parser.add_argument("--with-ethernet", action="store_true", help="Enable Ethernet support") parser.add_argument("--with-ethernet", action="store_true", help="Enable Ethernet support")
parser.add_argument("--ethernet-phy-model", default="sim", help="Ethernet PHY to simulate (sim, xgmii)") parser.add_argument("--ethernet-phy-model", default="sim", help="Ethernet PHY to simulate (sim, xgmii, gmii)")
parser.add_argument("--with-etherbone", action="store_true", help="Enable Etherbone support") parser.add_argument("--with-etherbone", action="store_true", help="Enable Etherbone support")
parser.add_argument("--local-ip", default="192.168.1.50", help="Local IP address of SoC (default=192.168.1.50)") parser.add_argument("--local-ip", default="192.168.1.50", help="Local IP address of SoC (default=192.168.1.50)")
parser.add_argument("--remote-ip", default="192.168.1.100", help="Remote IP address of TFTP server (default=192.168.1.100)") parser.add_argument("--remote-ip", default="192.168.1.100", help="Remote IP address of TFTP server (default=192.168.1.100)")
@ -360,6 +371,8 @@ def main():
sim_config.add_module("ethernet", "eth", args={"interface": "tap0", "ip": args.remote_ip}) sim_config.add_module("ethernet", "eth", args={"interface": "tap0", "ip": args.remote_ip})
elif args.ethernet_phy_model == "xgmii": elif args.ethernet_phy_model == "xgmii":
sim_config.add_module("xgmii_ethernet", "xgmii_eth", args={"interface": "tap0", "ip": args.remote_ip}) sim_config.add_module("xgmii_ethernet", "xgmii_eth", args={"interface": "tap0", "ip": args.remote_ip})
elif args.ethernet_phy_model == "gmii":
sim_config.add_module("gmii_ethernet", "gmii_eth", args={"interface": "tap0", "ip": args.remote_ip})
else: else:
raise ValueError("Unknown Ethernet PHY model: " + args.ethernet_phy_model) raise ValueError("Unknown Ethernet PHY model: " + args.ethernet_phy_model)