mirror of
https://github.com/logicog/RTLPlayground.git
synced 2026-10-29 17:54:14 +08:00
Merge pull request #644 from vankxr/fix/nic-wrap
nic: Fix incorrect wrap handling of RX circular buffer
This commit is contained in:
18 files changed
+435
-117
No files matched your search
@@ -103,6 +103,7 @@ SRCS += \
|
|||||||
rtl837x_flash.c \
|
rtl837x_flash.c \
|
||||||
rtl837x_igmp.c \
|
rtl837x_igmp.c \
|
||||||
rtl837x_init.c \
|
rtl837x_init.c \
|
||||||
|
rtl837x_nic.c \
|
||||||
rtl837x_leds.c \
|
rtl837x_leds.c \
|
||||||
rtl837x_leds_dump.c \
|
rtl837x_leds_dump.c \
|
||||||
rtl837x_phy.c \
|
rtl837x_phy.c \
|
||||||
|
|||||||
@@ -257,7 +257,7 @@ void nic_setup(void) __banked
|
|||||||
|
|
||||||
// This sets the size of the RX buffer, the filling level is in 0x7874
|
// This sets the size of the RX buffer, the filling level is in 0x7874
|
||||||
// R7848-000004ff
|
// R7848-000004ff
|
||||||
REG_SET(RTL837X_REG_NIC_RXBUFF_RX, 0x4ff);
|
REG_SET(RTL837X_REG_NIC_RXBUFF_RX, RTL837X_NIC_RX_LAST_SLOT);
|
||||||
|
|
||||||
// R7844-000007fe
|
// R7844-000007fe
|
||||||
REG_SET(RTL837X_REG_NIC_BUFFSIZE_TX, 0x7fe);
|
REG_SET(RTL837X_REG_NIC_BUFFSIZE_TX, 0x7fe);
|
||||||
|
|||||||
@@ -34,6 +34,21 @@ Next, transfer the actual packet over by repeating the SFR function with a
|
|||||||
pointer to the frame on the ASIC directly after the frame header and a
|
pointer to the frame on the ASIC directly after the frame header and a
|
||||||
length as given by the length in the frame header + 7, again divided by 8.
|
length as given by the length in the frame header + 7, again divided by 8.
|
||||||
|
|
||||||
|
The RX ring's inclusive last slot is configured as `0x4ff`, giving a byte
|
||||||
|
range of `0x0000` through `0x27ff`. If the descriptor occupies the last slot
|
||||||
|
at `0x27f8`, its payload starts at `0x0000`, not `0x2800`. The RX copy code
|
||||||
|
normalizes that starting address before DMA. It relies on the hardware to
|
||||||
|
wrap transfers that start inside the ring and cross its end, as observed
|
||||||
|
on SWTGW218AS. Programming an initial address of `0x2800` does not wrap
|
||||||
|
and can instead read zeros or stale TX data outside the RX ring.
|
||||||
|
|
||||||
|
Frame lengths are checked against `UIP_CONF_BUFFER_SIZE` before copying.
|
||||||
|
DMA works in whole eight-byte units. `UIP_CONF_BUFFER_ALLOC_SIZE` rounds
|
||||||
|
the backing storage up to the next complete word: 1,560 bytes for the
|
||||||
|
1,556-byte logical frame limit. This padding allows a maximum-size frame
|
||||||
|
to be transferred directly without a separate tail copy. The logical
|
||||||
|
frame limit and advertised MSS are unchanged.
|
||||||
|
|
||||||
The received frame will have an RTL proprietary Ethernet frame type of
|
The received frame will have an RTL proprietary Ethernet frame type of
|
||||||
0x8899 (RRPC) where normally the frame type 0x0800 for IPv4 would be located.
|
0x8899 (RRPC) where normally the frame type 0x0800 for IPv4 would be located.
|
||||||
Further 6 bytes follow describing the frame, before the normal IPv4 data
|
Further 6 bytes follow describing the frame, before the normal IPv4 data
|
||||||
|
|||||||
+1
-1
@@ -142,7 +142,7 @@ extern __xdata bool stp_enabled;
|
|||||||
* advertises it as the System Name TLV) read it from here. */
|
* advertises it as the System Name TLV) read it from here. */
|
||||||
extern __xdata char hostname[24];
|
extern __xdata char hostname[24];
|
||||||
|
|
||||||
extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_SIZE+2];
|
extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_ALLOC_SIZE];
|
||||||
extern __xdata struct uip_eth_addr uip_ethaddr;
|
extern __xdata struct uip_eth_addr uip_ethaddr;
|
||||||
|
|
||||||
// Headers for calls in the common code area (HOME/BANK0)
|
// Headers for calls in the common code area (HOME/BANK0)
|
||||||
|
|||||||
+1
-1
@@ -28,7 +28,7 @@ extern __xdata uint8_t igmpEnabled;
|
|||||||
extern __xdata uint8_t cpuPort;
|
extern __xdata uint8_t cpuPort;
|
||||||
extern __xdata uint8_t sfr_data[4];
|
extern __xdata uint8_t sfr_data[4];
|
||||||
extern __xdata struct machine_runtime machine_detected;
|
extern __xdata struct machine_runtime machine_detected;
|
||||||
extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_SIZE + 2];
|
extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_ALLOC_SIZE];
|
||||||
|
|
||||||
__xdata uint16_t idx;
|
__xdata uint16_t idx;
|
||||||
#define IGMP_IP_HDR offsetof(struct igmp_pkt, hlen)
|
#define IGMP_IP_HDR offsetof(struct igmp_pkt, hlen)
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@ extern __code const struct machine machine;
|
|||||||
extern __xdata struct machine_runtime machine_detected;
|
extern __xdata struct machine_runtime machine_detected;
|
||||||
extern __xdata uint16_t management_vlan;
|
extern __xdata uint16_t management_vlan;
|
||||||
extern __xdata struct uip_eth_addr uip_ethaddr;
|
extern __xdata struct uip_eth_addr uip_ethaddr;
|
||||||
extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_SIZE + 2];
|
extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_ALLOC_SIZE];
|
||||||
|
|
||||||
/* 20-byte Actor/Partner information body (802.3ad 43.4.2.2), network byte order */
|
/* 20-byte Actor/Partner information body (802.3ad 43.4.2.2), network byte order */
|
||||||
struct lacp_info {
|
struct lacp_info {
|
||||||
|
|||||||
@@ -0,0 +1,90 @@
|
|||||||
|
#include "rtl837x_common.h"
|
||||||
|
#include "rtl837x_regs.h"
|
||||||
|
#include "rtl837x_sfr.h"
|
||||||
|
#include "rtl837x_nic.h"
|
||||||
|
|
||||||
|
extern __xdata uint8_t rx_headers[16];
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef RTLP_HOST_TEST
|
||||||
|
/* One RX DMA operation, starting inside the configured ring. The hardware
|
||||||
|
* wraps transfers that cross its end; the initial address must be normalized.
|
||||||
|
* Host tests supply a simulated DMA engine for this hardware boundary. */
|
||||||
|
bool nic_rx_dma(__xdata uint8_t *buffer, uint16_t ring_ptr, uint8_t words) __reentrant
|
||||||
|
{
|
||||||
|
uint16_t guard = 0;
|
||||||
|
|
||||||
|
SFR_NIC_DATA_U16LE = (uint16_t)buffer;
|
||||||
|
SFR_NIC_RING_U16LE = ring_ptr;
|
||||||
|
SFR_NIC_CTRL = words;
|
||||||
|
while (SFR_NIC_CTRL != 0) {
|
||||||
|
if (++guard == 0) {
|
||||||
|
print_string("NIC: RX transfer did not complete\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static bool nic_rx_copy(__xdata uint8_t *buffer, uint16_t ring_ptr, uint8_t words) __reentrant
|
||||||
|
{
|
||||||
|
if (ring_ptr > RTL837X_NIC_RX_RING_BYTES || (ring_ptr & 7))
|
||||||
|
return false;
|
||||||
|
if (ring_ptr == RTL837X_NIC_RX_RING_BYTES)
|
||||||
|
ring_ptr = 0;
|
||||||
|
|
||||||
|
return nic_rx_dma(buffer, ring_ptr, words);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool nic_rx_header(uint16_t ring_ptr) __reentrant
|
||||||
|
{
|
||||||
|
if (ring_ptr >= RTL837X_NIC_RX_RING_BYTES)
|
||||||
|
return false;
|
||||||
|
return nic_rx_copy(rx_headers, ring_ptr, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool nic_rx_packet(__xdata uint8_t *buffer, uint16_t ring_ptr) __reentrant
|
||||||
|
{
|
||||||
|
uint16_t len = ((uint16_t)rx_headers[5] << 8) | rx_headers[4];
|
||||||
|
|
||||||
|
if (!len || len > UIP_CONF_BUFFER_SIZE)
|
||||||
|
return false;
|
||||||
|
return nic_rx_copy(buffer, ring_ptr, (len + 7) >> 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transfers data in XMEM to the ASIC for transmission by the NIC
|
||||||
|
*/
|
||||||
|
void nic_tx_packet(__xdata uint8_t *buffer, uint16_t len, uint16_t ring_ptr)
|
||||||
|
{
|
||||||
|
uint16_t guard = 0;
|
||||||
|
|
||||||
|
SFR_NIC_DATA_U16LE = (uint16_t)(uintptr_t)buffer;
|
||||||
|
|
||||||
|
#ifdef RXTXDBG
|
||||||
|
print_string("TX: \n");
|
||||||
|
for (uint8_t i = 0; i < 100; i++) {
|
||||||
|
print_byte(uip_buf[i]);
|
||||||
|
write_char(' ');
|
||||||
|
}
|
||||||
|
write_char('\n');
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ring_ptr <<= 3;
|
||||||
|
ring_ptr |= 0x8000;
|
||||||
|
SFR_NIC_RING_U16LE = ring_ptr;
|
||||||
|
|
||||||
|
len += 0xf;
|
||||||
|
len >>= 3;
|
||||||
|
SFR_NIC_CTRL = len;
|
||||||
|
while (SFR_NIC_CTRL != 0) {
|
||||||
|
if (++guard == 0) {
|
||||||
|
print_string("NIC: TX transfer did not complete\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef RTL837X_NIC_H
|
||||||
|
#define RTL837X_NIC_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
// #define RXTXDBG 1
|
||||||
|
|
||||||
|
bool nic_rx_header(uint16_t ring_ptr) __reentrant;
|
||||||
|
bool nic_rx_packet(__xdata uint8_t *buffer, uint16_t ring_ptr) __reentrant;
|
||||||
|
|
||||||
|
/* buffer starts at the eight-byte NIC descriptor; len excludes that
|
||||||
|
* descriptor. ring_ptr is the ASIC TX position in eight-byte slots. */
|
||||||
|
void nic_tx_packet(__xdata uint8_t *buffer, uint16_t len, uint16_t ring_ptr);
|
||||||
|
|
||||||
|
/* RX DMA in eight-byte units. ring_ptr must start inside the configured
|
||||||
|
* RX ring; the hardware wraps transfers that cross its end. */
|
||||||
|
bool nic_rx_dma(__xdata uint8_t *buffer, uint16_t ring_ptr, uint8_t words) __reentrant;
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -147,6 +147,9 @@
|
|||||||
*/
|
*/
|
||||||
#define RTL837X_REG_NIC_BUFFSIZE_TX 0x7844
|
#define RTL837X_REG_NIC_BUFFSIZE_TX 0x7844
|
||||||
#define RTL837X_REG_NIC_RXBUFF_RX 0x7848
|
#define RTL837X_REG_NIC_RXBUFF_RX 0x7848
|
||||||
|
/* RX ring: inclusive final eight-byte slot, as programmed by nic_setup(). */
|
||||||
|
#define RTL837X_NIC_RX_LAST_SLOT 0x04ff
|
||||||
|
#define RTL837X_NIC_RX_RING_BYTES ((RTL837X_NIC_RX_LAST_SLOT + 1U) * 8U)
|
||||||
#define RTL837X_REG_NIC_RXCMD 0x784c
|
#define RTL837X_REG_NIC_RXCMD 0x784c
|
||||||
#define RTL837X_REG_NIC_TXCMD 0x7850
|
#define RTL837X_REG_NIC_TXCMD 0x7850
|
||||||
#define RTL837X_REG_RX_CTRL 0x785c
|
#define RTL837X_REG_RX_CTRL 0x785c
|
||||||
|
|||||||
+1
-1
@@ -30,7 +30,7 @@ extern __xdata struct machine_runtime machine_detected;
|
|||||||
|
|
||||||
extern __xdata struct uip_eth_addr uip_ethaddr;
|
extern __xdata struct uip_eth_addr uip_ethaddr;
|
||||||
|
|
||||||
extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_SIZE + 2];
|
extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_ALLOC_SIZE];
|
||||||
|
|
||||||
extern __xdata uint8_t cmd_buffer[CMD_BUF_SIZE];
|
extern __xdata uint8_t cmd_buffer[CMD_BUF_SIZE];
|
||||||
extern __xdata uint8_t cmd_words_len;
|
extern __xdata uint8_t cmd_words_len;
|
||||||
|
|||||||
+10
-108
@@ -2,7 +2,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
// #define REGDBG 1
|
// #define REGDBG 1
|
||||||
// #define RXTXDBG 1
|
|
||||||
|
|
||||||
#include "rtl837x_sfr.h"
|
#include "rtl837x_sfr.h"
|
||||||
#include "rtl837x_regs.h"
|
#include "rtl837x_regs.h"
|
||||||
@@ -12,6 +11,7 @@
|
|||||||
#include "rtl837x_phy.h"
|
#include "rtl837x_phy.h"
|
||||||
#include "rtl837x_port.h"
|
#include "rtl837x_port.h"
|
||||||
#include "rtl837x_trap.h"
|
#include "rtl837x_trap.h"
|
||||||
|
#include "rtl837x_nic.h"
|
||||||
#include "rtl837x_stp.h"
|
#include "rtl837x_stp.h"
|
||||||
#include "rtl837x_lacp.h"
|
#include "rtl837x_lacp.h"
|
||||||
#include "rtl837x_igmp.h"
|
#include "rtl837x_igmp.h"
|
||||||
@@ -123,8 +123,8 @@ __code const uint8_t * __code const hex = "0123456789abcdef";
|
|||||||
__xdata uint8_t flash_buf[FLASH_BUF_SIZE];
|
__xdata uint8_t flash_buf[FLASH_BUF_SIZE];
|
||||||
|
|
||||||
// NIC buffers for packet RX/TX
|
// NIC buffers for packet RX/TX
|
||||||
__xdata uint8_t rx_headers[16]; // Packet header(s) on RX
|
__xdata uint8_t rx_headers[16]; // RX descriptor
|
||||||
__xdata uint8_t uip_buf[UIP_CONF_BUFFER_SIZE+2];
|
__xdata uint8_t uip_buf[UIP_CONF_BUFFER_ALLOC_SIZE];
|
||||||
|
|
||||||
__xdata uint16_t rx_packet_vlan;
|
__xdata uint16_t rx_packet_vlan;
|
||||||
__xdata uint16_t management_vlan;
|
__xdata uint16_t management_vlan;
|
||||||
@@ -714,108 +714,6 @@ void get_random_32(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Transfer Network Interface RX data from the ASIC to the 8051 XMEM
|
|
||||||
* data will be stored in the rx_header structure
|
|
||||||
* len is the length of data to be transferred
|
|
||||||
*/
|
|
||||||
bool nic_rx_header(uint16_t ring_ptr)
|
|
||||||
{
|
|
||||||
uint16_t buffer = (uint16_t) &rx_headers[0];
|
|
||||||
uint16_t guard = 0;
|
|
||||||
|
|
||||||
SFR_NIC_DATA_U16LE = buffer;
|
|
||||||
SFR_NIC_RING_U16LE = ring_ptr;
|
|
||||||
SFR_NIC_CTRL = 1;
|
|
||||||
while (SFR_NIC_CTRL != 0) {
|
|
||||||
if (++guard == 0) {
|
|
||||||
print_string("NIC: RX header transfer did not complete\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Transfer Network Interface RX data from the ASIC to the 8051 XMEM
|
|
||||||
* the description of the packet must be in the rx_headers data structure
|
|
||||||
* data will be returned in the xmem buffer points to
|
|
||||||
* ring_ptr is the current position of the RX Ring on the ASIC side
|
|
||||||
*/
|
|
||||||
bool nic_rx_packet(uint16_t buffer, uint16_t ring_ptr)
|
|
||||||
{
|
|
||||||
uint16_t guard = 0;
|
|
||||||
|
|
||||||
SFR_NIC_DATA_U16LE = buffer;
|
|
||||||
SFR_NIC_RING_U16LE = ring_ptr;
|
|
||||||
|
|
||||||
uint16_t len = (((uint16_t)rx_headers[5]) << 8) | rx_headers[4];
|
|
||||||
len += 7;
|
|
||||||
len >>= 3;
|
|
||||||
#ifdef RXTXDBG
|
|
||||||
print_string(" len: ");
|
|
||||||
print_short(len);
|
|
||||||
#endif
|
|
||||||
SFR_NIC_CTRL = len;
|
|
||||||
while (SFR_NIC_CTRL != 0) {
|
|
||||||
if (++guard == 0) {
|
|
||||||
print_string("NIC: RX transfer did not complete\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Transfers data in XMEM to the ASIC for transmission by the nic
|
|
||||||
*/
|
|
||||||
void nic_tx_packet(uint16_t ring_ptr)
|
|
||||||
{
|
|
||||||
uint16_t len;
|
|
||||||
uint16_t guard = 0;
|
|
||||||
|
|
||||||
/* A frame that got a dot1Q tag was shifted forward over its padding, so it
|
|
||||||
* starts at uip_buf and carries the q_frame layout. One that did not keeps
|
|
||||||
* the padding in front and the nonq_frame layout, so the padding is skipped.
|
|
||||||
*/
|
|
||||||
if (frame_tagged) {
|
|
||||||
SFR_NIC_DATA_U16LE = (uint16_t) uip_buf;
|
|
||||||
len = FRAME_Q->len;
|
|
||||||
/*
|
|
||||||
(__xdata struct rtl_dot1q_frame *)uip_buf
|
|
||||||
#define FRAME (((__xdata struct rtl_dot1q_frame *)&uip_buf[0]).nonq_frame)*/
|
|
||||||
} else {
|
|
||||||
SFR_NIC_DATA_U16LE = (uint16_t) uip_buf + VLAN_TAG_SIZE;
|
|
||||||
len = FRAME->len;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef RXTXDBG
|
|
||||||
print_string("TX: \n");
|
|
||||||
for (uint8_t i = 0; i < 100; i++) {
|
|
||||||
print_byte(uip_buf[i]);
|
|
||||||
write_char(' ');
|
|
||||||
}
|
|
||||||
write_char('\n');
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ring_ptr <<= 3;
|
|
||||||
ring_ptr |= 0x8000;
|
|
||||||
SFR_NIC_RING_U16LE = ring_ptr;
|
|
||||||
|
|
||||||
len += 0xf;
|
|
||||||
len >>= 3;
|
|
||||||
SFR_NIC_CTRL = len;
|
|
||||||
while (SFR_NIC_CTRL != 0) {
|
|
||||||
if (++guard == 0) {
|
|
||||||
print_string("NIC: TX transfer did not complete\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* Read flash using the MMIO capabilities of the DW8051 core
|
/* Read flash using the MMIO capabilities of the DW8051 core
|
||||||
* Bank is < 0x3f and is the MSB
|
* Bank is < 0x3f and is the MSB
|
||||||
* addr gives the address in the bank
|
* addr gives the address in the bank
|
||||||
@@ -999,8 +897,12 @@ void tcpip_output(void)
|
|||||||
uint16_t ring_ptr = ((uint16_t)sfr_data[2]) << 8;
|
uint16_t ring_ptr = ((uint16_t)sfr_data[2]) << 8;
|
||||||
ring_ptr |= sfr_data[3];
|
ring_ptr |= sfr_data[3];
|
||||||
|
|
||||||
// Move data over from xmem buffer to ASIC side using DMA
|
// Move data over from xmem buffer to ASIC side using DMA. A VLAN-tagged
|
||||||
nic_tx_packet(ring_ptr);
|
// frame starts at uip_buf; an untagged frame retains four bytes of padding.
|
||||||
|
if (frame_tagged)
|
||||||
|
nic_tx_packet(uip_buf, FRAME_Q->len, ring_ptr);
|
||||||
|
else
|
||||||
|
nic_tx_packet(uip_buf + VLAN_TAG_SIZE, FRAME->len, ring_ptr);
|
||||||
|
|
||||||
// New position of the ring-pointer on the NIC-side indicates number of bytes transmitted
|
// New position of the ring-pointer on the NIC-side indicates number of bytes transmitted
|
||||||
reg_read_m(RTL837X_REG_NIC_TX_CURR_PKT);
|
reg_read_m(RTL837X_REG_NIC_TX_CURR_PKT);
|
||||||
@@ -1037,7 +939,7 @@ void handle_rx(void)
|
|||||||
write_char(' ');
|
write_char(' ');
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (!nic_rx_packet((uint16_t) &uip_buf[0], ring_ptr + 8)) {
|
if (!nic_rx_packet(uip_buf, ring_ptr + 8)) {
|
||||||
REG_SET(RTL837X_REG_NIC_RXCMD, 1);
|
REG_SET(RTL837X_REG_NIC_RXCMD, 1);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ TESTS = $(BUILDDIR)/test_cmd_editor \
|
|||||||
$(BUILDDIR)/test_page_json \
|
$(BUILDDIR)/test_page_json \
|
||||||
$(BUILDDIR)/test_httpd_tx \
|
$(BUILDDIR)/test_httpd_tx \
|
||||||
$(BUILDDIR)/test_tick_gate \
|
$(BUILDDIR)/test_tick_gate \
|
||||||
|
$(BUILDDIR)/test_nic_rx \
|
||||||
$(BUILDDIR)/test_lacp
|
$(BUILDDIR)/test_lacp
|
||||||
|
|
||||||
# The table tests link the register mock and one environment file; stub/
|
# The table tests link the register mock and one environment file; stub/
|
||||||
@@ -52,6 +53,11 @@ $(BUILDDIR)/test_port_tables: test_port_tables.c $(TABLE_ENV) hw_mock.h | $(BUIL
|
|||||||
$(BUILDDIR)/test_tick_gate: test_tick_gate.c support.c $(SRCROOT)/cmd_editor.c | $(BUILDDIR)
|
$(BUILDDIR)/test_tick_gate: test_tick_gate.c support.c $(SRCROOT)/cmd_editor.c | $(BUILDDIR)
|
||||||
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
|
# Exercise the production RX ring reader; only the hardware DMA edge is mocked.
|
||||||
|
# Discard unused TX functions so this RX fixture needs no TX register mocks.
|
||||||
|
$(BUILDDIR)/test_nic_rx: test_nic_rx.c $(SRCROOT)/rtl837x_nic.c $(SRCROOT)/rtl837x_nic.h | $(BUILDDIR)
|
||||||
|
$(CC) $(CFLAGS) -ffunction-sections -fdata-sections $(filter %.c,$^) -o $@ $(LDFLAGS) -Wl,--gc-sections
|
||||||
|
|
||||||
$(BUILDDIR)/test_page_json: test_page_json.c $(TABLE_ENV) hw_mock.h | $(BUILDDIR)
|
$(BUILDDIR)/test_page_json: test_page_json.c $(TABLE_ENV) hw_mock.h | $(BUILDDIR)
|
||||||
$(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS)
|
$(CC) $(CFLAGS) $(filter %.c,$^) -o $@ $(LDFLAGS)
|
||||||
|
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ void read_reg_timer(uint32_t *tmr) { *tmr = 0; }
|
|||||||
|
|
||||||
/* uip-conf.h asks uIP not to define the packet buffer: on the switch it lives
|
/* uip-conf.h asks uIP not to define the packet buffer: on the switch it lives
|
||||||
* at a fixed XDATA address, so the bench provides the storage itself. */
|
* at a fixed XDATA address, so the bench provides the storage itself. */
|
||||||
u8_t uip_buf[UIP_BUFSIZE + 2];
|
u8_t uip_buf[UIP_CONF_BUFFER_ALLOC_SIZE];
|
||||||
|
|
||||||
uint16_t strlen_x(const char *s) { return (uint16_t)strlen(s); }
|
uint16_t strlen_x(const char *s) { return (uint16_t)strlen(s); }
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
/* ---------- platform mocks ---------- */
|
/* ---------- platform mocks ---------- */
|
||||||
|
|
||||||
uint8_t uip_buf[UIP_CONF_BUFFER_SIZE + 2];
|
uint8_t uip_buf[UIP_CONF_BUFFER_ALLOC_SIZE];
|
||||||
uint16_t uip_len;
|
uint16_t uip_len;
|
||||||
struct uip_eth_addr uip_ethaddr = { .addr = {0x02,0x11,0x22,0x33,0x44,0x55} };
|
struct uip_eth_addr uip_ethaddr = { .addr = {0x02,0x11,0x22,0x33,0x44,0x55} };
|
||||||
const struct machine machine = { .min_port = 0, .max_port = 7,
|
const struct machine machine = { .min_port = 0, .max_port = 7,
|
||||||
|
|||||||
@@ -0,0 +1,277 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "rtl837x_common.h"
|
||||||
|
#include "rtl837x_regs.h"
|
||||||
|
#include "rtl837x_nic.h"
|
||||||
|
|
||||||
|
uint8_t rx_headers[16];
|
||||||
|
|
||||||
|
/* Model the DMA behavior reported on SWTGW218AS: a transfer starting inside
|
||||||
|
* the RX ring wraps at its end. An initial address outside the ring instead
|
||||||
|
* reads flat ASIC memory, including stale TX bytes at 0x2800. */
|
||||||
|
static uint8_t asic[0x8000];
|
||||||
|
static struct {
|
||||||
|
uint8_t before[16];
|
||||||
|
uint8_t data[UIP_CONF_BUFFER_ALLOC_SIZE];
|
||||||
|
uint8_t after[16];
|
||||||
|
} destination;
|
||||||
|
|
||||||
|
struct dma_request {
|
||||||
|
uint8_t *buffer;
|
||||||
|
uint16_t ring;
|
||||||
|
uint8_t words;
|
||||||
|
};
|
||||||
|
static struct dma_request requests[8];
|
||||||
|
static unsigned request_count, fail_request;
|
||||||
|
static int failures;
|
||||||
|
|
||||||
|
static void check(bool condition, const char *what)
|
||||||
|
{
|
||||||
|
if (!condition) {
|
||||||
|
printf(" FAIL: %s\n", what);
|
||||||
|
failures++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nic_rx_dma(uint8_t *buffer, uint16_t ring, uint8_t words)
|
||||||
|
{
|
||||||
|
size_t bytes = (size_t)words * 8;
|
||||||
|
unsigned call = ++request_count;
|
||||||
|
|
||||||
|
if (call <= sizeof(requests) / sizeof(requests[0]))
|
||||||
|
requests[call - 1] = (struct dma_request){ buffer, ring, words };
|
||||||
|
if (call == fail_request || !words || (size_t)ring + bytes > sizeof(asic))
|
||||||
|
return false;
|
||||||
|
if (ring < RTL837X_NIC_RX_RING_BYTES) {
|
||||||
|
for (size_t i = 0; i < bytes; i++)
|
||||||
|
buffer[i] = asic[((size_t)ring + i) % RTL837X_NIC_RX_RING_BYTES];
|
||||||
|
} else {
|
||||||
|
memcpy(buffer, asic + ring, bytes);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fixture_reset(void)
|
||||||
|
{
|
||||||
|
memset(asic, 0xd7, sizeof(asic));
|
||||||
|
memset(&destination, 0xa5, sizeof(destination));
|
||||||
|
memset(rx_headers, 0x6c, sizeof(rx_headers));
|
||||||
|
memset(requests, 0, sizeof(requests));
|
||||||
|
request_count = 0;
|
||||||
|
fail_request = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool filled(const uint8_t *data, size_t len, uint8_t value)
|
||||||
|
{
|
||||||
|
while (len--)
|
||||||
|
if (*data++ != value)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool guards_intact(void)
|
||||||
|
{
|
||||||
|
return filled(destination.before, sizeof(destination.before), 0xa5)
|
||||||
|
&& filled(destination.after, sizeof(destination.after), 0xa5);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool requests_start_in_rx_ring(void)
|
||||||
|
{
|
||||||
|
for (unsigned i = 0; i < request_count; i++) {
|
||||||
|
if (i >= sizeof(requests) / sizeof(requests[0]) || !requests[i].words
|
||||||
|
|| (requests[i].ring & 7)
|
||||||
|
|| requests[i].ring >= RTL837X_NIC_RX_RING_BYTES)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ring_write(uint16_t offset, const uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < len; i++)
|
||||||
|
asic[((size_t)offset + i) % RTL837X_NIC_RX_RING_BYTES] = data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put_descriptor(uint16_t offset, uint16_t len)
|
||||||
|
{
|
||||||
|
uint8_t descriptor[8] = { 0x03, 0x00, 0xb0, 0xc8, len, len >> 8, 0, 0 };
|
||||||
|
ring_write(offset, descriptor, sizeof(descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void expect_descriptor(uint16_t len)
|
||||||
|
{
|
||||||
|
uint8_t descriptor[8] = { 0x03, 0x00, 0xb0, 0xc8, len, len >> 8, 0, 0 };
|
||||||
|
check(!memcmp(rx_headers, descriptor, sizeof(descriptor)), "the first eight descriptor bytes stay intact");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pattern(uint8_t *data, size_t len)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < len; i++)
|
||||||
|
data[i] = (uint8_t)(i * 37 + (i >> 8) + 0x13);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void t_last_descriptor(void)
|
||||||
|
{
|
||||||
|
static const uint8_t root_bpdu[68] = {
|
||||||
|
0x01, 0x80, 0xc2, 0, 0, 0, 0xd0, 0xea, 0x11, 0x12, 0x34, 0x56,
|
||||||
|
0x88, 0x99, 0x04, 0, 0, 0, 0, 8,
|
||||||
|
0, 0x27, 0x42, 0x42, 3, 0, 0, 2, 2, 0x3c,
|
||||||
|
0x10, 0, 0xd0, 0xea, 0x11, 0x12, 0x34, 0x56, 0, 0, 0, 0,
|
||||||
|
0x10, 0, 0xd0, 0xea, 0x11, 0x12, 0x34, 0x56, 0x80, 0x0a,
|
||||||
|
0, 0, 0x14, 0, 2, 0, 0x0f, 0, 0
|
||||||
|
};
|
||||||
|
uint8_t stale_tx[72] = { 0 };
|
||||||
|
uint16_t last = RTL837X_NIC_RX_RING_BYTES - 8;
|
||||||
|
|
||||||
|
printf("[test] the last RX descriptor wraps its BPDU payload before DMA\n");
|
||||||
|
fixture_reset();
|
||||||
|
put_descriptor(last, sizeof(root_bpdu));
|
||||||
|
ring_write(0, root_bpdu, sizeof(root_bpdu));
|
||||||
|
memcpy(stale_tx, root_bpdu, sizeof(root_bpdu));
|
||||||
|
stale_tx[17] = 0x20;
|
||||||
|
stale_tx[19] = 1; /* retained TX destination mask, not ingress port 8 */
|
||||||
|
memcpy(asic + RTL837X_NIC_RX_RING_BYTES, stale_tx, sizeof(stale_tx));
|
||||||
|
check(nic_rx_header(last), "the final descriptor is readable");
|
||||||
|
expect_descriptor(sizeof(root_bpdu));
|
||||||
|
check(nic_rx_packet(destination.data, last + 8), "a payload address at the RX ring limit wraps to zero");
|
||||||
|
check(!memcmp(destination.data, root_bpdu, sizeof(root_bpdu)), "the complete upstream BPDU is read instead of stale TX bytes");
|
||||||
|
check(destination.data[19] == 8, "the upstream ingress tag survives the ring boundary");
|
||||||
|
check(request_count == 2 && requests[0].ring == last && requests[0].words == 1
|
||||||
|
&& requests[1].ring == 0 && requests[1].words == 9,
|
||||||
|
"descriptor and rounded payload DMA both start in the RX ring");
|
||||||
|
check(requests_start_in_rx_ring() && guards_intact(), "DMA starts inside the RX ring and preserves destination guards");
|
||||||
|
expect_descriptor(sizeof(root_bpdu));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void t_payload_layouts(void)
|
||||||
|
{
|
||||||
|
static const struct {
|
||||||
|
uint16_t descriptor, len;
|
||||||
|
} cases[] = {
|
||||||
|
{ 0x0100, 8 }, { 0x0100, 16 }, { 0x0100, 64 },
|
||||||
|
{ 0x0100, 68 }, { 0x0100, 72 },
|
||||||
|
{ RTL837X_NIC_RX_RING_BYTES - 16, 8 },
|
||||||
|
{ RTL837X_NIC_RX_RING_BYTES - 16, 16 },
|
||||||
|
{ RTL837X_NIC_RX_RING_BYTES - 16, 68 },
|
||||||
|
{ RTL837X_NIC_RX_RING_BYTES - 72, 64 },
|
||||||
|
{ RTL837X_NIC_RX_RING_BYTES - 48, 68 }
|
||||||
|
};
|
||||||
|
uint8_t frame[80];
|
||||||
|
|
||||||
|
printf("[test] payload DMA handles ordinary, exact-boundary, and crossing frames\n");
|
||||||
|
pattern(frame, sizeof(frame));
|
||||||
|
for (unsigned i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
|
||||||
|
size_t rounded = (cases[i].len + 7u) & ~7u;
|
||||||
|
fixture_reset();
|
||||||
|
put_descriptor(cases[i].descriptor, cases[i].len);
|
||||||
|
ring_write(cases[i].descriptor + 8, frame, rounded);
|
||||||
|
check(nic_rx_header(cases[i].descriptor), "the descriptor transfer completes");
|
||||||
|
check(nic_rx_packet(destination.data, cases[i].descriptor + 8), "the payload transfer completes");
|
||||||
|
check(!memcmp(destination.data, frame, rounded), "every payload and permitted padding byte reaches the correct destination offset");
|
||||||
|
check(filled(destination.data + rounded, sizeof(destination.data) - rounded, 0xa5), "DMA does not write beyond the rounded frame");
|
||||||
|
check(request_count == 2, "the payload uses one DMA request even when crossing the ring end");
|
||||||
|
check(requests_start_in_rx_ring() && guards_intact(), "crossing DMA starts inside the RX ring and preserves destination guards");
|
||||||
|
expect_descriptor(cases[i].len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void t_maximum_frames(void)
|
||||||
|
{
|
||||||
|
uint8_t frame[(UIP_CONF_BUFFER_SIZE + 7u) & ~7u];
|
||||||
|
static const uint16_t lengths[] = { 1, 7, 1549, 1550, 1551, 1552, 1553, 1554, 1555, 1556 };
|
||||||
|
|
||||||
|
printf("[test] rounded maximum frames fit without overwriting their descriptor\n");
|
||||||
|
pattern(frame, sizeof(frame));
|
||||||
|
for (unsigned layout = 0; layout < 3; layout++) {
|
||||||
|
uint16_t descriptor = layout == 0 ? 0x0100
|
||||||
|
: RTL837X_NIC_RX_RING_BYTES - (layout == 1 ? 16 : 8);
|
||||||
|
for (unsigned i = 0; i < sizeof(lengths) / sizeof(lengths[0]); i++) {
|
||||||
|
uint16_t len = lengths[i];
|
||||||
|
size_t rounded = (len + 7u) & ~7u;
|
||||||
|
fixture_reset();
|
||||||
|
put_descriptor(descriptor, len);
|
||||||
|
ring_write(descriptor + 8, frame, rounded);
|
||||||
|
check(nic_rx_header(descriptor), "the maximum-frame descriptor transfer completes");
|
||||||
|
check(nic_rx_packet(destination.data, descriptor + 8), "all supported frame lengths fit the destination");
|
||||||
|
check(!memcmp(destination.data, frame, rounded), "the full rounded frame is preserved");
|
||||||
|
check(filled(destination.data + rounded, sizeof(destination.data) - rounded, 0xa5), "rounded DMA writes no bytes beyond the available destination");
|
||||||
|
check(request_count == 2, "maximum frames use a single payload DMA request");
|
||||||
|
check(requests_start_in_rx_ring() && guards_intact(), "rounded DMA starts inside the RX ring and preserves destination guards");
|
||||||
|
expect_descriptor(len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void t_invalid_frames(void)
|
||||||
|
{
|
||||||
|
static const uint16_t invalid_lengths[] = { 0, UIP_CONF_BUFFER_SIZE + 1, 0xffff };
|
||||||
|
static const uint16_t invalid_pointers[] = { 1, 7, RTL837X_NIC_RX_RING_BYTES + 8, 0xfff8 };
|
||||||
|
|
||||||
|
printf("[test] invalid lengths and DMA addresses are rejected before transfer\n");
|
||||||
|
for (unsigned i = 0; i < sizeof(invalid_lengths) / sizeof(invalid_lengths[0]); i++) {
|
||||||
|
fixture_reset();
|
||||||
|
rx_headers[4] = invalid_lengths[i];
|
||||||
|
rx_headers[5] = invalid_lengths[i] >> 8;
|
||||||
|
check(!nic_rx_packet(destination.data, 0x0100), "unsupported or overflowing lengths are rejected");
|
||||||
|
check(!request_count && filled(destination.data, sizeof(destination.data), 0xa5) && guards_intact(),
|
||||||
|
"invalid frame lengths cause no DMA or destination changes");
|
||||||
|
}
|
||||||
|
for (unsigned i = 0; i < sizeof(invalid_pointers) / sizeof(invalid_pointers[0]); i++) {
|
||||||
|
fixture_reset();
|
||||||
|
rx_headers[4] = 68;
|
||||||
|
rx_headers[5] = 0;
|
||||||
|
check(!nic_rx_header(invalid_pointers[i]), "an invalid descriptor address is rejected");
|
||||||
|
check(!nic_rx_packet(destination.data, invalid_pointers[i]), "an invalid payload address is rejected");
|
||||||
|
check(!request_count && guards_intact(), "invalid addresses never reach DMA");
|
||||||
|
}
|
||||||
|
fixture_reset();
|
||||||
|
check(!nic_rx_header(RTL837X_NIC_RX_RING_BYTES) && !request_count,
|
||||||
|
"a descriptor at the ring limit is rejected rather than wrapped");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void t_dma_failure(void)
|
||||||
|
{
|
||||||
|
uint8_t frame[UIP_CONF_BUFFER_ALLOC_SIZE];
|
||||||
|
static const uint16_t lengths[] = { 68, UIP_CONF_BUFFER_SIZE };
|
||||||
|
|
||||||
|
printf("[test] failed descriptor and single-payload DMA transfers propagate failure\n");
|
||||||
|
fixture_reset();
|
||||||
|
fail_request = 1;
|
||||||
|
check(!nic_rx_header(0x0100) && request_count == 1, "descriptor DMA failure reaches the caller");
|
||||||
|
pattern(frame, sizeof(frame));
|
||||||
|
for (unsigned layout = 0; layout < 3; layout++) {
|
||||||
|
uint16_t descriptor = layout == 0 ? 0x0100
|
||||||
|
: RTL837X_NIC_RX_RING_BYTES - (layout == 1 ? 16 : 8);
|
||||||
|
for (unsigned i = 0; i < sizeof(lengths) / sizeof(lengths[0]); i++) {
|
||||||
|
uint16_t len = lengths[i];
|
||||||
|
fixture_reset();
|
||||||
|
put_descriptor(descriptor, len);
|
||||||
|
ring_write(descriptor + 8, frame, (len + 7u) & ~7u);
|
||||||
|
check(nic_rx_header(descriptor), "the descriptor precedes the injected payload failure");
|
||||||
|
fail_request = 2;
|
||||||
|
check(!nic_rx_packet(destination.data, descriptor + 8), "payload DMA failure reaches the caller");
|
||||||
|
check(request_count == fail_request, "a failed payload DMA is not retried or split");
|
||||||
|
check(filled(destination.data, sizeof(destination.data), 0xa5) && guards_intact(),
|
||||||
|
"an injected failure before DMA preserves the destination and guards");
|
||||||
|
expect_descriptor(len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
t_last_descriptor();
|
||||||
|
t_payload_layouts();
|
||||||
|
t_maximum_frames();
|
||||||
|
t_invalid_frames();
|
||||||
|
t_dma_failure();
|
||||||
|
if (failures) {
|
||||||
|
printf("%d NIC RX check(s) failed\n", failures);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
puts("all NIC RX checks passed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -132,6 +132,10 @@ typedef unsigned short uip_stats_t;
|
|||||||
*/
|
*/
|
||||||
#define UIP_CONF_BUFFER_SIZE 1556
|
#define UIP_CONF_BUFFER_SIZE 1556
|
||||||
|
|
||||||
|
/* Backing storage includes padding for the NIC's eight-byte DMA transfers.
|
||||||
|
* Keep UIP_CONF_BUFFER_SIZE as the logical frame/MSS limit. */
|
||||||
|
#define UIP_CONF_BUFFER_ALLOC_SIZE ((UIP_CONF_BUFFER_SIZE + 7U) & ~7U)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bytes of the buffer kept out of the advertised MSS.
|
* Bytes of the buffer kept out of the advertised MSS.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ static __code const uip_ipaddr_t all_zeroes_addr =
|
|||||||
#endif /* UIP_CONF_IPV6 */
|
#endif /* UIP_CONF_IPV6 */
|
||||||
|
|
||||||
#ifndef UIP_CONF_EXTERNAL_BUFFER
|
#ifndef UIP_CONF_EXTERNAL_BUFFER
|
||||||
u8_t uip_buf[UIP_BUFSIZE + 2]; /* The packet buffer that contains
|
u8_t uip_buf[UIP_CONF_BUFFER_ALLOC_SIZE]; /* The packet buffer that contains
|
||||||
incoming packets. */
|
incoming packets. */
|
||||||
#endif /* UIP_CONF_EXTERNAL_BUFFER */
|
#endif /* UIP_CONF_EXTERNAL_BUFFER */
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -419,7 +419,7 @@
|
|||||||
#endif /* UIP_CONF_BUFFER_EXTRA */
|
#endif /* UIP_CONF_BUFFER_EXTRA */
|
||||||
|
|
||||||
|
|
||||||
extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_SIZE+2];
|
extern __xdata uint8_t uip_buf[UIP_CONF_BUFFER_ALLOC_SIZE];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if statistics support should be compiled in.
|
* Determines if statistics support should be compiled in.
|
||||||
|
|||||||
Reference in new issue
Block a user