1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

パターンジェネレータを使用して任意のイーサネットフレームを出力する

Last updated at Posted at 2025-06-26

はじめに

今回はパターンジェネレータで任意のイーサネットフレームの出力を試してみました。
Renesas RAマイコンを搭載したEK-RA6M3にパターンジェネレータが出力したイーサネットフレームを入力して正しいイーサネットフレームとして認識させます。これを応用すればエラーとなるイーサネットフレームも入力させることができるようになります。
準備した環境は以下の通りです。

  1. EK-RA6M3 (評価ボード)
  2. e2 studio 2025-04 (統合開発環境)
  3. ADALM2000 (パターンジェネレータ)
  4. Scopy (ADALM2000制御ソフト)

Scopyにインポートするイーサネットフレームのパターンファイルの作成

最初のステップとして、Scopyにインポートするイーサネットフレームのパターンファイル作成します。ScopyのパターンジェネレータにはI2CUART等の波形をエミュレーションする設定が最初から備わっていますが、Ethernetの波形をエミュレーションするパターンはありません。そのため、パターンジェネレータのImport (任意のCSVを読み込んでパターンを生成する)を使用してイーサネットフレームの波形を生成します。イーサネットフレームの波形パターンを手打ちするのは面倒なので、Pythonスクリプトから生成します。

  • MIIインターフェースにおけるイーサネットフレームの波形パターン生成スクリプト
  • 受信に必要な端子であるRX_CLKRX_DVRXD0-3の波形パターンを生成します
  • MIIインターフェースなのでRX_CLKの周波数は25MHzになります
  • Scopyの周波数は50MHzに設定してください
generate_mii_waveform.py
import csv
import binascii

# --- User Configuration ---

# Sampling frequency to be set in Scopy's Pattern Generator (Hz)
# Set to the maximum supported frequency of your Scopy device.
SCOPY_FREQ = 50_000_000

# Clock frequency of the MII interface (Hz)
MII_CLK_FREQ = 25_000_000

# Output CSV filename
OUTPUT_CSV_FILENAME = "ethernet_frame_mii.csv"

# --- Ethernet Frame Parameters ---
DEST_MAC = "FF:FF:FF:FF:FF:FF"   # Destination MAC address
SRC_MAC = "00:1A:2B:3C:4D:5E"    # Source MAC address
ETH_TYPE = "0800"                  # Ethernet Type (0x0800: IPv4)
PAYLOAD_HEX = "00" * 46            # Payload data (hex string)

# --- Script Body ---

def mac_to_bytes(mac_string: str) -> bytes:
    """Converts a MAC address string to a byte sequence."""
    return bytes.fromhex(mac_string.replace(':', ''))

def hex_to_bytes(hex_string: str) -> bytes:
    """Converts a hexadecimal string to a byte sequence."""
    return bytes.fromhex(hex_string)

def calculate_fcs(frame_bytes: bytes) -> bytes:
    """Calculates the FCS (CRC-32) for an Ethernet frame."""
    crc = binascii.crc32(frame_bytes) & 0xFFFFFFFF
    return crc.to_bytes(4, byteorder='little')

def generate_frame_bytes() -> bytes:
    """Generates a complete Ethernet frame byte sequence."""
    preamble = b'\x55' * 7
    sfd = b'\xd5'
    dest_mac_bytes = mac_to_bytes(DEST_MAC)
    src_mac_bytes = mac_to_bytes(SRC_MAC)
    eth_type_bytes = hex_to_bytes(ETH_TYPE)
    payload_bytes = hex_to_bytes(PAYLOAD_HEX)
    frame_body_for_fcs = dest_mac_bytes + src_mac_bytes + eth_type_bytes + payload_bytes
    fcs_bytes = calculate_fcs(frame_body_for_fcs)
    return preamble + sfd + frame_body_for_fcs + fcs_bytes

def bytes_to_nibbles(byte_data: bytes) -> list[int]:
    """Converts a byte sequence to a list of nibbles for MII transmission."""
    nibbles = []
    for byte in byte_data:
        nibbles.append(byte & 0x0F)
        nibbles.append((byte >> 4) & 0x0F)
    return nibbles

def main():
    """Main process"""
    print("Generating Ethernet frame with 96-bit IPG for 50MHz Scopy setup...")

    nibble_stream = bytes_to_nibbles(generate_frame_bytes())
    
    with open(OUTPUT_CSV_FILENAME, 'w', newline='') as csvfile:
        writer = csv.writer(csvfile)
        
        # --- 1. Pre-frame Idle Period ---
        # Provide a stable idle period (24 MII clock cycles).
        idle_pattern = [0, 0, 0, 0, 0, 0]
        for _ in range(24):
            writer.writerow(idle_pattern) # CLK Low
            idle_pattern[0] = 1 # Change CLK to High
            writer.writerow(idle_pattern) # CLK High
            idle_pattern[0] = 0 # Reset CLK for next loop

        # --- 2. Ethernet Frame Data ---
        last_rxd_bits = [0, 0, 0, 0]
        for nibble in nibble_stream:
            rxd_bits = [(nibble >> i) & 1 for i in range(4)]
            # CLK Low: Set data. Stable for 20ns, ensuring 20ns setup time.
            writer.writerow([0, 1] + rxd_bits)
            # CLK High: Hold data. Stable for 20ns, ensuring 20ns hold time.
            writer.writerow([1, 1] + rxd_bits)
            last_rxd_bits = rxd_bits

        # --- Frame End: De-assert RX_DV cleanly (uses 1 clock cycle) ---
        # writer.writerow([0, 0] + last_rxd_bits)
        # writer.writerow([1, 0] + last_rxd_bits)
        
        # --- 3. Post-frame Idle Period (IPG = 96 bit times = 24 MII cycles) ---
        # We already used 1 cycle to de-assert DV, so we need 23 more cycles.
        for _ in range(23):
            writer.writerow(idle_pattern) # CLK Low
            idle_pattern[0] = 1 # Change CLK to High
            writer.writerow(idle_pattern) # CLK High
            idle_pattern[0] = 0 # Reset CLK for next loop

    print("-" * 50)
    print(f"CSV file '{OUTPUT_CSV_FILENAME}' was generated successfully.")
    print("This file includes a 96-bit (24 MII cycles) Inter-Packet Gap (IPG).")
    print("Please use a Scopy sample rate of 50 MHz.")
    print("-" * 50)


if __name__ == '__main__':
    main()
  • RMIIインターフェースにおけるイーサネットフレームの波形パターン生成スクリプト
  • 受信に必要な端子であるCRS_DVRXD0-1の波形パターンを生成します
  • パターンファイルでは50MHzのクロックを生成できないので、ScopyのパターンジェネレータにあるPatternClockを使用して50MHzのクロックを出力してください
  • Scopyの周波数は50MHzに設定してください
generate_rmii_waveform.py
import csv
import binascii

# --- User Configuration ---

# Sampling frequency to be set in Scopy's Pattern Generator (Hz)
# This MUST match the RMII REF_CLK frequency (typically 50MHz).
SCOPY_FREQ = 50_000_000

# Output CSV filename
OUTPUT_CSV_FILENAME = "ethernet_frame_rmii.csv"

# --- Ethernet Frame Parameters ---
DEST_MAC = "FF:FF:FF:FF:FF:FF"   # Destination MAC address
SRC_MAC = "00:1A:2B:3C:4D:5E"    # Source MAC address
ETH_TYPE = "0800"                  # Ethernet Type (0x0800: IPv4)
PAYLOAD_HEX = "00" * 46            # Payload data (hex string)

# --- Script Body ---

def mac_to_bytes(mac_string: str) -> bytes:
    """Converts a MAC address string to a byte sequence."""
    return bytes.fromhex(mac_string.replace(':', ''))

def hex_to_bytes(hex_string: str) -> bytes:
    """Converts a hexadecimal string to a byte sequence."""
    return bytes.fromhex(hex_string)

def calculate_fcs(frame_bytes: bytes) -> bytes:
    """Calculates the FCS (CRC-32) for an Ethernet frame."""
    crc = binascii.crc32(frame_bytes) & 0xFFFFFFFF
    return crc.to_bytes(4, byteorder='little')

def generate_frame_bytes() -> bytes:
    """Generates a complete Ethernet frame byte sequence."""
    preamble = b'\x55' * 7
    sfd = b'\xd5'
    dest_mac_bytes = mac_to_bytes(DEST_MAC)
    src_mac_bytes = mac_to_bytes(SRC_MAC)
    eth_type_bytes = hex_to_bytes(ETH_TYPE)
    payload_bytes = hex_to_bytes(PAYLOAD_HEX)
    frame_body_for_fcs = dest_mac_bytes + src_mac_bytes + eth_type_bytes + payload_bytes
    fcs_bytes = calculate_fcs(frame_body_for_fcs)
    return preamble + sfd + frame_body_for_fcs + fcs_bytes

def bytes_to_rmii_chunks(byte_data: bytes) -> list[int]:
    """Converts a byte sequence to a list of 2-bit chunks for RMII transmission."""
    chunks = []
    for byte in byte_data:
        # RMII transmits 2 bits per REF_CLK cycle, starting from LSB.
        # Cycle 1: bits 1, 0
        chunks.append((byte >> 0) & 0x03)
        # Cycle 2: bits 3, 2
        chunks.append((byte >> 2) & 0x03)
        # Cycle 3: bits 5, 4
        chunks.append((byte >> 4) & 0x03)
        # Cycle 4: bits 7, 6
        chunks.append((byte >> 6) & 0x03)
    return chunks

def main():
    """Main process"""
    print("Generating RMII Ethernet frame for 50MHz Scopy setup...")

    # Calculate IPG in clock cycles
    BIT_TIME_NS = 10  # For 100Mbps Ethernet
    IPG_BIT_TIMES = 96
    REF_CLK_PERIOD_NS = 1_000_000_000 / SCOPY_FREQ
    ipg_cycles = int((IPG_BIT_TIMES * BIT_TIME_NS) / REF_CLK_PERIOD_NS)
    print(f"Calculated IPG: {ipg_cycles} cycles for 50MHz REF_CLK.")

    rmii_stream = bytes_to_rmii_chunks(generate_frame_bytes())
    
    with open(OUTPUT_CSV_FILENAME, 'w', newline='') as csvfile:
        writer = csv.writer(csvfile)
        # The CSV file will be header-less.
        # Column order: CRS_DV, RXD0, RXD1

        # --- 1. Pre-frame Idle Period ---
        # CRS_DV is low. RXD is don't care, set to 0.
        idle_pattern = [0, 0, 0]
        for _ in range(ipg_cycles):
            writer.writerow(idle_pattern)

        # --- 2. Ethernet Frame Data ---
        # CRS_DV is high during frame reception.
        for chunk in rmii_stream:
            rxd0 = (chunk >> 0) & 1
            rxd1 = (chunk >> 1) & 1
            writer.writerow([1, rxd0, rxd1])

        # --- 3. Post-frame Idle Period (IPG) ---
        for _ in range(ipg_cycles):
            writer.writerow(idle_pattern)

    print("-" * 50)
    print(f"CSV file '{OUTPUT_CSV_FILENAME}' was generated successfully.")
    print("This file generates RMII patterns (CRS_DV, RXD0, RXD1) without a clock.")
    print("Please ensure an external 50MHz REF_CLK is supplied to the DUT.")
    print(f"Scopy sample rate MUST be set to {SCOPY_FREQ/1_000_000:.0f} MHz.")
    print("-" * 50)

if __name__ == '__main__':
    main()

Scopyにパターンファイルをインポート

次に、Scopyを起動してイーサネットフレームのパターンファイルをインポートします。
ADALM2000をホストPCに接続して、パターンジェネレータの画面を開きます。
右側の設定画面からPatternImportに設定して、Open fileから先ほど作成した波形パターンのCSVファイルを選択します。その後周波数を50MHzに設定して、Import selected channelsを押して適用します。
RMIIインターフェースを使用する場合は、CSVファイルにクロックの波形パターンがないので、別にPatternClockを50MHzに設定してください。

image.png
  • MIIインターフェースにおけるイーサネットフレームの波形パターン
image.png
  • RMIIインターフェースにおけるイーサネットフレームの波形パターン
image.png

ADALM2000の詳細な使い方を以下を参照してください。

イーサネットフレーム受信用の新規プロジェクトの作成

次に、e2 studioを起動してEK-RA6M3用のプロジェクトを作成します。
プロジェクトの作成方法について詳細な説明は省きますが、EK-RA6M3用のプロジェクトにFSPモジュールのr_etherを追加してください(プロジェクトの初期設定はRMIIインターフェースになっているので、MIIインターフェースを使用したい場合はr_ether_phySelect MII typeの設定と端子設定を変更してください)
下記が今回検証に使用したmainプログラムです。
プログラム起動時はEK-RA6M3のEthernetコネクタをホストPCと接続しておいてください、r_etherがリンクアップして送受信を開始するためのプロセスとして必要です、ただしその後はホストPCから飛んでくるイーサネットフレームを受信したくないので、Ethernet-PHYを絶縁モードに切り替えています。
リンク確立後のボーレートおよびモードは100Mbps全二重になることを確認してください、その他のボーレートとモードでは正常にイーサネットフレームを認識できない可能性があります。

hal_entry.c
#define ETHER_EXAMPLE_MAXIMUM_ETHERNET_FRAME_SIZE     (1514)
#define ETHER_PHY_REG_CONTROL   (0)

/* Receive data buffer */
static uint8_t gp_read_buffer[ETHER_EXAMPLE_MAXIMUM_ETHERNET_FRAME_SIZE] = {0};

void hal_entry(void)
{
    /* TODO: add your own code here */
    fsp_err_t err = FSP_SUCCESS;

    uint32_t reg_addr = 0;
    uint32_t data;

    /* Source MAC Address */
    uint32_t read_data_size = 0;

    R_BSP_MODULE_START(FSP_IP_EPTPC, 0);

    /* Open the ether instance with initial configuration. */
    err = R_ETHER_Open(&g_ether0_ctrl, &g_ether0_cfg);

    /* Handle any errors. This function should be defined by the user. */
    assert(FSP_SUCCESS == err);

    do
    {
        /* When the Ethernet link status read from the PHY-LSI Basic Status register is link-up,
         * Initializes the module and make auto negotiation. */
        err = R_ETHER_LinkProcess(&g_ether0_ctrl);
    } while (FSP_SUCCESS != err);

    reg_addr = ETHER_PHY_REG_CONTROL;
    R_ETHER_PHY_Read(g_ether0_ctrl.p_ether_cfg->p_ether_phy_instance->p_ctrl, reg_addr, &data);

    data = data | 0x400;
    R_ETHER_PHY_Write(g_ether0_ctrl.p_ether_cfg->p_ether_phy_instance->p_ctrl, reg_addr, data);

    R_BSP_SoftwareDelay(BSP_DELAY_UNITS_MILLISECONDS, 100);

    while (1)
    {
        err = R_ETHER_Read(&g_ether0_ctrl, (void *) gp_read_buffer, &read_data_size);
        if (FSP_SUCCESS == err)
        {
            __NOP();
        }
    }

#if BSP_TZ_SECURE_BUILD
    /* Enter non-secure code */
    R_BSP_NonSecureEnter();
#endif
}

イーサネットフレーム受信の動作確認

最初にADALM2000の各ピンをEK-RA6M3のピンヘッダに接続します。
検証プログラムを起動して、__NOP()にブレークポイントを貼ったら、ScopyのRunもしくはSignalを押してイーサネットフレームの波形パターンの出力を開始してください。
正常にイーサネットフレームを受信出来たらブレークするはずです。受信バッファの中身を確認してスクリプト通りのイーサネットフレームを受信しているか確認してください。
また、イーサネットフレームパターンのデータ部を1ニブル消去すると端数ビットフレームとして認識され、FCS部を改変するとCRCエラーフレームとして認識されるはずです。
もしイーサネットフレームを受信できない場合は、イーサネット用として設定している端子のPIDRレジスタを確認して波形が変化している(PIDRレジスタが変化している)ことを確認すると良いと思います。

image.png

さいごに

今回はパターンジェネレータで任意のイーサネットフレームの出力を試してみました。これを応用してパターンファイルを改変すれば、任意のエラーフレームを生成することもできます。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?