Skip to content

Commit

Permalink
bugfix(phy_tester): fixed pytest start execution
Browse files Browse the repository at this point in the history
other minor fixes
  • Loading branch information
kostaond committed Sep 17, 2024
1 parent 933433f commit 41f75fe
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 36 deletions.
15 changes: 10 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ GPATH
**/test_apps/**/sdkconfig.old

# Example project files
**/examples/**/sdkconfig
**/examples/**/sdkconfig.old
**/examples/**/build
**/examples/**/managed_components
**/**examples/**/sdkconfig
**/**examples/**/sdkconfig.old
**/**examples/**/build
**/**examples/**/managed_components

# Doc build artifacts
docs/_build/
Expand Down Expand Up @@ -80,4 +80,9 @@ dependencies.lock
docs/html

# component hash file generated by idf
**/.component_hash
**/.component_hash

# PHY tester build realted files [IDF-11145]
phy_tester/managed_components
phy_tester/sdkconfig
phy_tester/sdkconfig.old
2 changes: 1 addition & 1 deletion phy_tester/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ or run automatic test to identify Ethernet related issue of your board:
1) Configure PHY and Ethernet component based on your actual board's needs in sdkconfig.
2) Build.
3) Find your network interface (NIC) name the DUT is connected to (e.g. use `ip` command).
4) Run `pytest --target=esp32 --eth_nic=YOUR_NIC_NAME`
4) Run `pytest -s --embedded-services esp,idf --tb=no --add-target-as-marker y -m esp32 --eth-nic YOUR_NIC_NAME` (change target and NIC per your setup)

Note: you need `pytest` installed, see [pytest in ESP-IDF](https://docs.espressif.com/projects/esp-idf/en/stable/esp32/contribute/esp-idf-tests-with-pytest.html) for more information.

Expand Down
13 changes: 13 additions & 0 deletions phy_tester/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import pytest
from _pytest.fixtures import FixtureRequest

@pytest.fixture(scope='session')
def eth_nic(request: FixtureRequest) -> str:
return request.config.getoption('eth_nic') or ''

def pytest_addoption(parser: pytest.Parser) -> None:
idf_group = parser.getgroup('idf')
idf_group.addoption(
'--eth-nic',
help='Network interface (NIC) name the DUT is connected to',
)
16 changes: 9 additions & 7 deletions phy_tester/eth_error_msg.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,17 @@ def print_eth_init_fail_msg(cls) -> None:
cls.print_yellow('=================================================================================')
cls.print_yellow(f'Ethernet initialization failed!')
cls.print_yellow(f'------------------------------')
cls.print_yellow(f'Possible root causes:')
cls.print_yellow(f'1) RMII REF CLK mode incorrectly configured if emac initialization timeouts. Does ESP32 outputs the RMII CLK? Or is RMII CLK '
'provided externally by PHY or oscillator?')
cls.print_yellow(f'2) If external RMII CLK is used, measure the clock signal at ESP32 REF RMII CLK input pin using oscilloscope with sufficient '
cls.print_yellow(f'Check the above DUT log and investigate possible root causes:')
cls.print_yellow(f'1) If EMAC errors:')
cls.print_yellow(f' a) RMII REF CLK mode incorrectly configured if emac initialization timeouts.')
cls.print_yellow(f' Does ESP32 outputs the RMII CLK? Or is RMII CLK provided externally by PHY or oscillator?')
cls.print_yellow(f' b) If external RMII CLK is used, measure the clock signal at ESP32 REF RMII CLK input pin using oscilloscope with sufficient '
'bandwidth. There must be 50 MHz square wave signal.')
# TODO make it target dependent
cls.print_yellow(f'3) Make sure programmer/monitor device correctly handles “nDTR”/”nRST” and associated transistors which are connected to GPIO0.')

cls.print_yellow(f'4) If PHY error reported: check if bootstrap (PHY address) is set correctly or set to "auto" in the code.')
cls.print_yellow(f' c) Make sure programmer/monitor device correctly handles “nDTR”/”nRST” and associated transistors which are connected to GPIO0.')
cls.print_yellow(f'2) If PHY errors:')
cls.print_yellow(f' a) Check if bootstrap (PHY address) is set correctly or set to "auto" in the code (menuconfig).')
cls.print_yellow(f' b) Cross-check with schematics if MDIO and MDC GPIOs are correctly configured.')
cls.print_yellow('=================================================================================')

@classmethod
Expand Down
16 changes: 12 additions & 4 deletions phy_tester/main/eth_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/
#include "freertos/FreeRTOS.h"
#include "freertos/event_groups.h"
#include "esp_rom_sys.h"
#include "esp_event.h"
#include "esp_check.h"
#include "esp_log.h"
Expand Down Expand Up @@ -190,11 +191,18 @@ esp_err_t loopback_far_end_en(esp_eth_handle_t *eth_handle, phy_id_t phy_id, boo

return ESP_FAIL;
}
esp_eth_ioctl(eth_handle, ETH_CMD_WRITE_PHY_REG, &reg);
reg_val_exp = reg_val;
esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, &reg);
if (reg_val_exp != reg_val) {
// it was observed that e.g. IP101 needs to be commanded multiple time to take it effect
uint8_t attempt = 0;
do {
esp_eth_ioctl(eth_handle, ETH_CMD_WRITE_PHY_REG, &reg);
reg_val_exp = reg_val;
esp_eth_ioctl(eth_handle, ETH_CMD_READ_PHY_REG, &reg);
attempt++;
} while (reg_val_exp != reg_val && attempt < 10);

if (attempt >= 10) {
ESP_LOGE(TAG, "error to configure far-end loopback");
ESP_LOGE(TAG, "expected reg. val 0x%lx, actual 0x%lx", reg_val_exp, reg_val);
return ESP_FAIL;
}

Expand Down
1 change: 1 addition & 0 deletions phy_tester/sdkconfig.defaults
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
CONFIG_ETHERNET_RX_TASK_STACK_SIZE=4096
48 changes: 29 additions & 19 deletions phy_tester/pytest_eth_phy.py → phy_tester/test_eth_phy.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
green = '\033[32m'
italics = '\033[3m'
yellow = '\033[33m'
cyan = '\033[36m'


class EthTestIntf(object):
Expand Down Expand Up @@ -116,11 +117,16 @@ def send_recv_cmp(self, mac: str, payload_len: int=1010) -> bool:

def traffic_gen(self, mac: str, count: int, payload_len: int, data_pattern_hex: str, interval: float, pipe_rcv:connection.Connection) -> None:
with self.configure_eth_if() as so:
payload = bytes.fromhex(data_pattern_hex) * int((payload_len + 1) / len(bytes.fromhex(data_pattern_hex)))
eth_frame = Ether(dst=mac, src=so.getsockname()[4], type=self.eth_type) / raw(payload[0:payload_len])
payload = bytearray.fromhex(data_pattern_hex) * int((payload_len + 1) / len(bytearray.fromhex(data_pattern_hex)))
i = 0
cnt = 0
try:
while pipe_rcv.poll() is not True:
payload[0] = cnt
cnt += 1
if cnt > 255:
cnt = 0
eth_frame = Ether(dst=mac, src=so.getsockname()[4], type=self.eth_type) / raw(payload[0:payload_len])
so.send(raw(eth_frame))
sleep(interval)
if count != -1:
Expand All @@ -131,7 +137,7 @@ def traffic_gen(self, mac: str, count: int, payload_len: int, data_pattern_hex:
raise e


def test_loopback_server(dut: Dut, eth_if: EthTestIntf, mac: str) -> str:
def _test_loopback_server(dut: Dut, eth_if: EthTestIntf, mac: str) -> str:
ret = 'PASS'
eth_type_hex = hex(eth_if.eth_type)
dut.write(f'loop-server -f {eth_type_hex} -t 2000\n')
Expand All @@ -148,7 +154,7 @@ def test_loopback_server(dut: Dut, eth_if: EthTestIntf, mac: str) -> str:
return ret


def test_farend_loopback(dut: Dut, eth_if: EthTestIntf, mac: str) -> str:
def _test_farend_loopback(dut: Dut, eth_if: EthTestIntf, mac: str) -> str:
ret = 'PASS'
dut.write('farend-loop-en -e\n')
try:
Expand All @@ -174,19 +180,19 @@ def test_farend_loopback(dut: Dut, eth_if: EthTestIntf, mac: str) -> str:
return ret


def test_nearend_loopback(dut: Dut, count: int) -> str:
def _test_nearend_loopback(dut: Dut, count: int) -> str:
ret = 'PASS'
dut.write(f'loop-test -s 640 -c {count}\n')
try:
dut.expect_exact('Link Up')
dut.expect_exact(f'looped frames: {count}, rx errors: 0', timeout=4)
dut.expect_exact(f'looped frames: {count}, rx errors: 0', timeout=10)
except: # noqa
logging.error('near-end loop back failed')
ret = 'FAIL'
return ret


def test_dut_rx(dut: Dut, eth_if: EthTestIntf, mac: str) -> str:
def _test_dut_rx(dut: Dut, eth_if: EthTestIntf, mac: str) -> str:
ret = 'PASS'
eth_type_hex = hex(eth_if.eth_type)
dut.write(f'loop-server -v -f {eth_type_hex} -t 2000\n')
Expand Down Expand Up @@ -221,7 +227,7 @@ def test_dut_rx(dut: Dut, eth_if: EthTestIntf, mac: str) -> str:
return ret


def test_dut_tx(dut: Dut, eth_if: EthTestIntf, count: int) -> str:
def _test_dut_tx(dut: Dut, eth_if: EthTestIntf, count: int) -> str:
ret = 'PASS'
with eth_if.configure_eth_if() as so:
so.settimeout(5)
Expand Down Expand Up @@ -309,7 +315,7 @@ def ethernet_phy_test(dut: Dut, test_pc_nic: str) -> None:

dut.expect_exact('Steps to Test Ethernet PHY')

res_dict = {'loopback server': test_loopback_server(dut, target_if, 'ff:ff:ff:ff:ff:ff')}
res_dict = {'loopback server': _test_loopback_server(dut, target_if, 'ff:ff:ff:ff:ff:ff')}
# Loopback server results
if res_dict['loopback server'] == 'PASS':
tx_path = green
Expand All @@ -321,15 +327,15 @@ def ethernet_phy_test(dut: Dut, test_pc_nic: str) -> None:
print(f'loopback server: {tx_path}{res_dict["loopback server"]}{norm}')
draw_result(tx_path, rx_path, tx_path, rx_path, False, True)

if res_dict['loopback server'] == 'FAIL':
if True is True: #res_dict['loopback server'] == 'FAIL':
logging.error('loopback server test failed!')
logging.info('Running additional tests to try to isolate the problem...')

res_dict.update({'DUT tx': test_dut_tx(dut, target_if, 1)})
res_dict.update({'DUT rx': test_dut_rx(dut, target_if, 'ff:ff:ff:ff:ff:ff')})
res_dict.update({'DUT tx': _test_dut_tx(dut, target_if, 1)})
res_dict.update({'DUT rx': _test_dut_rx(dut, target_if, 'ff:ff:ff:ff:ff:ff')})

res_dict.update({'far-end loopback': test_farend_loopback(dut, target_if, 'ff:ff:ff:ff:ff:ff')})
res_dict.update({'near-end loopback': test_nearend_loopback(dut, 2)})
res_dict.update({'far-end loopback': _test_farend_loopback(dut, target_if, 'ff:ff:ff:ff:ff:ff')})
res_dict.update({'near-end loopback': _test_nearend_loopback(dut, 5)})

# DUT Tx/Rx results
if res_dict['DUT tx'] == 'PASS':
Expand Down Expand Up @@ -408,16 +414,19 @@ def ethernet_phy_test(dut: Dut, test_pc_nic: str) -> None:
if rj45_rx_fail or rj45_tx_fail:
EthFailMsg.print_rj45_fail_msg(rj45_rx_fail, rj45_tx_fail)

print('\nThe test finished! `Final problem isolation` should show you the most probable location of issue. However, go over the full log to see '
'additional details and to fully understand each tested scenario.')
print(f'\n{cyan}The test finished! `Final problem isolation` should show you the most probable location of issue. However, go over the full log to see '
f'additional details and to fully understand each tested scenario.{norm}')

print('\nScript run:')


@pytest.mark.esp32
@pytest.mark.esp32p4
@pytest.mark.parametrize('target', [
'esp32',
'esp32p4',
], indirect=True)
def test_esp_ethernet(dut: Dut,
eth_nic: str) -> None:
print(eth_nic)
ethernet_phy_test(dut, eth_nic)


Expand All @@ -430,7 +439,7 @@ def test_esp_ethernet(dut: Dut,
parser = argparse.ArgumentParser(description='Ethernet PHY tester helper script')
parser.add_argument('--eth_nic', default='',
help='Name of the test PC Ethernet NIC connected to DUT. If the option '
'is omitted, script uses the first identified Ethernet interface.')
'is omitted, script automatically uses the first identified Ethernet interface.')

subparsers = parser.add_subparsers(help='Commands', dest='command')

Expand Down Expand Up @@ -466,3 +475,4 @@ def test_esp_ethernet(dut: Dut,
tx_proc.join()
if tx_proc.exitcode is None:
tx_proc.terminate()

0 comments on commit 41f75fe

Please sign in to comment.