一、总体架构

在FPGA上实现EtherCAT主站,主要涉及以下几个关键模块的设计和实现:

  1. 物理层接口模块
  • 该模块负责与外部的以太网物理层(PHY)芯片进行通信,完成数据的发送和接收。通常使用标准的以太网物理层接口,如MII(Media Independent Interface)或RMII(Reduced Media Independent Interface)。
  • 实现数据的串并转换:将从MAC层接收的并行数据转换为串行数据发送到PHY芯片,同时将从PHY芯片接收的串行数据转换为并行数据送往MAC层。
  1. MAC层模块
  • 处理以太网帧的封装和解封装。它接收来自物理层的数据,解析以太网帧头,检查目的MAC地址是否为本机地址,如果是则提取有效载荷,否则将其丢弃。
  • 对于发送的数据,它会添加以太网帧头(包括源MAC地址、目的MAC地址、以太网类型等),并将其传递给物理层进行发送。
  1. EtherCAT处理模块
  • 专门负责EtherCAT协议的处理,包括解析EtherCAT帧的头部信息,根据EtherCAT命令(如NMT命令、SDO服务、PDO交换等)进行相应的操作。
  • 该模块还负责与从站进行数据交换,根据从站的配置和状态,管理数据的读写操作,以及处理从站的响应。
  1. 应用层模块
  • 作为用户与EtherCAT系统的接口,接收用户的控制信息并将其转换为EtherCAT协议的命令和数据,同时将从EtherCAT处理模块接收的数据提供给用户应用程序。

二、关键模块的实现细节

(一)物理层接口模块

module ETH_PHY_Interface (
    input wire clk,
    input wire rst,
    input wire [31:0] tx_data,
    input wire tx_valid,
    output wire tx_ready,
    output wire [31:0] rx_data,
    output wire rx_valid,
    output wire mii_tx_clk,
    output wire mii_tx_en,
    output wire [3:0] mii_txd,
    input wire mii_rx_clk,
    input wire mii_rx_dv,
    input wire [3:0] mii_rxd
);

    // 发送逻辑
    reg [3:0] tx_shift_reg;
    reg tx_en_reg;
    always @(posedge clk) begin
        if (rst) begin
            tx_en_reg <= 1'b0;
            tx_shift_reg <= 4'b0;
        end else if (tx_valid && tx_ready) begin
            tx_en_reg <= 1'b1;
            tx_shift_reg <= tx_data[31:28];
        end else if (tx_en_reg) begin
            tx_shift_reg <= {tx_shift_reg[2:0], 1'b0};
        end else begin
            tx_en_reg <= 1'b0;
        end
    end

    assign mii_tx_en = tx_en_reg;
    assign mii_txd = tx_shift_reg;
    assign tx_ready = ~tx_en_reg;

    // 接收逻辑
    reg [31:0] rx_shift_reg;
    reg rx_valid_reg;
    always @(posedge mii_rx_clk) begin
        if (rst) begin
            rx_valid_reg <= 1'b0;
            rx_shift_reg <= 32'b0;
        end else if (mii_rx_dv) begin
            rx_shift_reg <= {rx_shift_reg[27:0], mii_rxd};
            if (rx_shift_reg[3:0] == 4'hF) begin
                rx_valid_reg <= 1'b1;
            end else begin
                rx_valid_reg <= 1'b0;
            end
        end else begin
            rx_valid_reg <= 1'b0;
        end
    end

    assign rx_data = rx_shift_reg;
    assign mii_tx_clk = clk;

endmodule

  • tx_validtx_ready 用于控制数据的发送,当 tx_valid 为高且 tx_ready 为高时开始发送数据。
  • 使用 tx_shift_reg 进行数据的串行化,将并行数据转换为串行数据发送。
  • rx_valid 表示接收数据的有效性,rx_shift_reg 用于将接收到的串行数据组合为并行数据。

(二)MAC层模块

module ETH_MAC_Layer (
    input wire clk,
    input wire rst,
    input wire [31:0] upper_layer_tx_data,
    input wire upper_layer_tx_valid,
    output wire upper_layer_tx_ready,
    output wire [31:0] upper_layer_rx_data,
    output wire upper_layer_rx_valid,
    input wire [31:0] phy_rx_data,
    input wire phy_rx_valid,
    output wire phy_tx_data,
    output wire phy_tx_valid,
    input wire phy_tx_ready,
    input wire [47:0] local_mac_addr
);

    // 接收逻辑
    reg [47:0] dst_mac_addr;
    reg [47:0] src_mac_addr;
    reg [15:0] ethertype;
    reg [31:0] payload_data;
    reg frame_valid;

    always @(posedge clk) begin
        if (rst) begin
            frame_valid <= 1'b0;
        end else if (phy_rx_valid) begin
            dst_mac_addr <= phy_rx_data[47:0];
            src_mac_addr <= phy_rx_data[95:48];
            ethertype <= phy_rx_data[111:96];
            payload_data <= phy_rx_data[159:128];
            frame_valid <= 1'b1;
        end else if (upper_layer_rx_ready) begin
            frame_valid <= 1'b0;
        end
    end

    assign upper_layer_rx_data = payload_data;
    assign upper_layer_rx_valid = frame_valid && (dst_mac_addr == local_mac_addr);

    // 发送逻辑
    reg [31:0] frame_buffer;
    reg [11:0] frame_index;
    always @(posedge clk) begin
        if (rst) begin
            phy_tx_valid <= 1'b0;
            frame_index <= 12'b0;
        end else if (upper_layer_tx_valid && phy_tx_ready) begin
            src_mac_addr <= local_mac_addr;
            // 假设目的MAC地址固定
            dst_mac_addr <= 48'hFFFFFFFFFFFF;
            ethertype <= 16'h88A4; // EtherCAT的以太网类型
            frame_buffer[47:0] <= dst_mac_addr;
            frame_buffer[95:48] <= src_mac_addr;
            frame_buffer[111:96] <= ethertype;
            frame_buffer[127:112] <= 12'h000; // 长度,可根据实际修改
            frame_buffer[159:128] <= upper_layer_tx_data;
            phy_tx_valid <= 1'b1;
            frame_index <= frame_index + 1;
        end else if (phy_tx_valid && phy_tx_ready) begin
            frame_index <= frame_index + 1;
        end else begin
            phy_tx_valid <= 1'b0;
        end
    end

    assign phy_tx_data = frame_buffer[31:0];
    assign upper_layer_tx_ready = phy_tx_ready;

endmodule

  • 在接收逻辑中,解析以太网帧头,提取目的MAC地址、源MAC地址、以太网类型和有效载荷。
  • 仅当接收帧的目的MAC地址与本地MAC地址匹配时,将有效载荷传递给上层,并置 upper_layer_rx_valid 为高。
  • 在发送逻辑中,构建以太网帧,添加本地MAC地址作为源地址,设置目的MAC地址和以太网类型为EtherCAT的类型,然后将上层数据封装为以太网帧发送。

(三)EtherCAT处理模块

module EtherCAT_Handler (
    input wire clk,
    input wire rst,
    input wire [31:0] mac_rx_data,
    input wire mac_rx_valid,
    output wire mac_tx_data,
    output wire mac_tx_valid,
    input wire mac_tx_ready,
    output wire [31:0] app_layer_rx_data,
    output wire app_layer_rx_valid,
    input wire app_layer_rx_ready,
    input wire [31:0] app_layer_tx_data,
    input wire app_layer_tx_valid,
    output wire app_layer_tx_ready,
    output wire [7:0] nmt_state
);

    // EtherCAT帧解析逻辑
    reg [7:0] command;
    reg [15:0] index;
    reg [15:0] address;
    reg [31:0] ec_data;
    reg ec_frame_valid;

    always @(posedge clk) begin
        if (rst) begin
            ec_frame_valid <= 1'b0;
        end else if (mac_rx_valid) begin
            command <= mac_rx_data[7:0];
            index <= mac_rx_data[23:8];
            address <= mac_rx_data[39:24];
            ec_data <= mac_rx_data;
            ec_frame_valid <= 1'b1;
        end else if (app_layer_rx_ready) begin
            ec_frame_valid <= 1'b0;
        end
    end

    // 命令处理逻辑
    always @(posedge clk) begin
        if (rst) begin
            nmt_state <= 8'h01; // 初始NMT状态,例如初始化
        end else if (ec_frame_valid) begin
            case (command)
                8'h01: begin
                    // 处理NMT命令,如启动、停止、重置从站
                    // 可根据具体命令更新nmt_state
                end
                8'h11: begin
                    // 处理SDO读请求
                    // 进行相应的SDO读操作
                end
                8'h23: begin
                    // 处理SDO写请求
                    // 进行相应的SDO写操作
                end
                // 处理其他EtherCAT命令
                default: begin
                    // 处理其他命令
                end
            endcase
        end
    end

    assign app_layer_rx_data = ec_data;
    assign app_layer_rx_valid = ec_frame_valid;

    // 数据发送逻辑
    always @(posedge clk) begin
        if (rst) begin
            mac_tx_valid <= 1'b0;
        end else if (app_layer_tx_valid && mac_tx_ready) begin
            // 封装EtherCAT帧
            mac_tx_valid <= 1'b1;
        end else if (mac_tx_valid && mac_tx_ready) begin
            mac_tx_valid <= 1'b0;
        end
    end

    assign mac_tx_data = app_layer_tx_data;
    assign app_layer_tx_ready = mac_tx_ready;

endmodule

  • 解析EtherCAT帧的命令、索引和地址信息。
  • 根据不同的EtherCAT命令(如NMT命令、SDO读写命令等)进行相应的处理,通过 case 语句实现不同命令的逻辑分支。
  • 将应用层发送的数据封装为EtherCAT帧发送给MAC层。

(四)应用层模块

module Application_Layer (
    input wire clk,
    input wire rst,
    input wire [31:0] ec_handler_rx_data,
    input wire ec_handler_rx_valid,
    output wire ec_handler_tx_data,
    output wire ec_handler_tx_valid,
    input wire ec_handler_tx_ready,
    input wire [31:0] user_data_in,
    input wire user_data_in_valid,
    output wire user_data_in_ready,
    output wire [31:0] user_data_out,
    output wire user_data_out_valid,
    input wire user_data_out_ready
);

    // 从EtherCAT处理模块接收数据并传递给用户应用程序
    assign user_data_out = ec_handler_rx_data;
    assign user_data_out_valid = ec_handler_rx_valid;

    // 从用户应用程序接收数据并发送给EtherCAT处理模块
    assign ec_handler_tx_data = user_data_in;
    assign ec_handler_tx_valid = user_data_in_valid;
    assign user_data_in_ready = ec_handler_tx_ready;

endmodule

  • 该模块实现应用程序和EtherCAT处理模块之间的数据交换,将EtherCAT处理模块的数据传递给用户应用程序,将用户应用程序的数据发送给EtherCAT处理模块。

三、模块间的连接

将上述各个模块连接起来,构成一个完整的EtherCAT主站系统:

module EtherCAT_Master (
    input wire clk,
    input wire rst,
    output wire mii_tx_clk,
    output wire mii_tx_en,
    output wire [3:0] mii_txd,
    input wire mii_rx_clk,
    input wire mii_rx_dv,
    input wire [3:0] mii_rxd,
    input wire [47:0] local_mac_addr
);

    wire [31:0] phy_rx_data;
    wire phy_rx_valid;
    wire phy_tx_data;
    wire phy_tx_valid;
    wire phy_tx_ready;
    wire [31:0] mac_rx_data;
    wire mac_rx_valid;
    wire mac_tx_data;
    wire mac_tx_valid;
    wire mac_tx_ready;
    wire [31:0] ec_handler_rx_data;
    wire ec_handler_rx_valid;
    wire ec_handler_tx_data;
    wire ec_handler_tx_valid;
    wire ec_handler_tx_ready;
    wire [31:0] app_layer_rx_data;
    wire app_layer_rx_valid;
    wire app_layer_tx_data;
    wire app_layer_tx_valid;
    wire app_layer_tx_ready;
    wire [7:0] nmt_state;

    ETH_PHY_Interface phy_interface (
      .clk(clk),
      .rst(rst),
      .tx_data(phy_tx_data),
      .tx_valid(phy_tx_valid),
      .tx_ready(phy_tx_ready),
      .rx_data(phy_rx_data),
      .rx_valid(phy_rx_valid),
      .mii_tx_clk(mii_tx_clk),
      .mii_tx_en(mii_tx_en),
      .mii_txd(mii_txd),
      .mii_rx_clk(mii_rx_clk),
      .mii_rx_dv(mii_rx_dv),
      .mii_rxd(mii_rxd)
    );

    ETH_MAC_Layer mac_layer (
      .clk(clk),
      .rst(rst),
      .upper_layer_tx_data(mac_tx_data),
      .upper_layer_tx_valid(mac_tx_valid),
      .upper_layer_tx_ready(mac_tx_ready),
      .upper_layer_rx_data(mac_rx_data),
      .upper_layer_rx_valid(mac_rx_valid),
      .phy_rx_data(phy_rx_data),
      .phy_rx_valid(phy_rx_valid),
      .phy_tx_data(phy_tx_data),
      .phy_tx_valid(phy_tx_valid),
      .phy_tx_ready(phy_tx_ready),
      .local_mac_addr(local_mac_addr)
    );

    EtherCAT_Handler ethercat_handler (
      .clk(clk),
      .rst(rst),
      .mac_rx_data(mac_rx_data),
      .mac_rx_valid(mac_rx_valid),
      .mac_tx_data(mac_tx_data),
      .mac_tx_valid(mac_tx_valid),
      .mac_tx_ready(mac_tx_ready),
      .app_layer_rx_data(app_layer_rx_data),
      .app_layer_rx_valid(app_layer_rx_valid),
      .app_layer_rx_ready(app_layer_tx_valid),
      .app_layer_tx_data(app_layer_tx_data),
      .app_layer_tx_valid(app_layer_tx_valid),
      .app_layer_tx_ready(app_layer_tx_ready),
      .nmt_state(nmt_state)
    );

    Application_Layer app_layer (
      .clk(clk),
      .rst(rst),
      .ec_handler_rx_data(ec_handler_rx_data),
      .ec_handler_rx_valid(ec_handler_rx_valid),
      .ec_handler_tx_data(ec_handler_tx_data),
      .ec_handler_tx_valid(ec_handler_tx_valid),
      .ec_handler_tx_ready(ec_handler_tx_ready),
      .user_data_in(app_layer_tx_data),
      .user_data_in_valid(app_layer_tx_valid),
      .user_data_in_ready(app_layer_tx_ready),
      .user_data_out(app_layer_rx_data),
      .user_data_out_valid(app_layer_rx_valid),
      .user_data_out_ready(ec_handler_rx_valid)
    );

endmodule

  • 该顶层模块将物理层接口、MAC层、EtherCAT处理模块和应用层模块连接在一起,实现了数据在不同模块间的传递和处理。

四、测试与验证

  1. 仿真测试
  • 使用仿真工具(如ModelSim)对各个模块进行单独的功能测试,例如:
  • 对物理层接口模块,发送测试数据,验证数据的串并转换是否正确。
  • 对MAC层模块,输入不同的以太网帧,检查帧的解析和封装是否正确。
  • 对EtherCAT处理模块,发送不同的EtherCAT命令,验证命令处理是否正确。
  1. 硬件测试
  • 将生成的比特流