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:
parent
7b533a032d
commit
08a14e8cf1
|
@ -1,5 +1,5 @@
|
|||
include ../variables.mak
|
||||
MODULES = xgmii_ethernet ethernet serial2console serial2tcp clocker spdeeprom
|
||||
MODULES = xgmii_ethernet ethernet serial2console serial2tcp clocker spdeeprom gmii_ethernet
|
||||
|
||||
.PHONY: $(MODULES)
|
||||
all: $(MODULES)
|
||||
|
|
|
@ -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 $@ $<
|
|
@ -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;
|
||||
}
|
|
@ -235,9 +235,9 @@ class SimVerilatorToolchain:
|
|||
raise OSError(msg)
|
||||
_compile_sim(build_name, verbose)
|
||||
run_as_root = False
|
||||
if sim_config.has_module("ethernet"):
|
||||
run_as_root = True
|
||||
if sim_config.has_module("xgmii_ethernet"):
|
||||
if sim_config.has_module("ethernet") \
|
||||
or sim_config.has_module("xgmii_ethernet") \
|
||||
or sim_config.has_module("gmii_ethernet"):
|
||||
run_as_root = True
|
||||
_run_sim(build_name, as_root=run_as_root, interactive=interactive)
|
||||
|
||||
|
|
|
@ -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 SDRAMPHYModel
|
||||
|
||||
from liteeth.phy.gmii import LiteEthPHYGMII
|
||||
from liteeth.phy.xgmii import LiteEthPHYXGMII
|
||||
from liteeth.phy.model import LiteEthPHYModel
|
||||
from liteeth.mac import LiteEthMAC
|
||||
|
@ -76,6 +77,14 @@ _io = [
|
|||
Subsignal("tx_data", Pins(64)),
|
||||
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,
|
||||
Subsignal("scl", Pins(1)),
|
||||
Subsignal("sda_out", Pins(1)),
|
||||
|
@ -185,6 +194,8 @@ class SimSoC(SoCCore):
|
|||
self.submodules.ethphy = LiteEthPHYModel(self.platform.request("eth", 0))
|
||||
elif ethernet_phy_model == "xgmii":
|
||||
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:
|
||||
raise ValueError("Unknown Ethernet PHY model:", ethernet_phy_model)
|
||||
# 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-verbosity", default=0, help="Set SDRAM checker verbosity")
|
||||
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("--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)")
|
||||
|
@ -360,6 +371,8 @@ def main():
|
|||
sim_config.add_module("ethernet", "eth", args={"interface": "tap0", "ip": args.remote_ip})
|
||||
elif args.ethernet_phy_model == "xgmii":
|
||||
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:
|
||||
raise ValueError("Unknown Ethernet PHY model: " + args.ethernet_phy_model)
|
||||
|
||||
|
|
Loading…
Reference in New Issue