一、总体架构
在FPGA上实现EtherCAT主站,主要涉及以下几个关键模块的设计和实现:
- 物理层接口模块:
- 该模块负责与外部的以太网物理层(PHY)芯片进行通信,完成数据的发送和接收。通常使用标准的以太网物理层接口,如MII(Media Independent Interface)或RMII(Reduced Media Independent Interface)。
- 实现数据的串并转换:将从MAC层接收的并行数据转换为串行数据发送到PHY芯片,同时将从PHY芯片接收的串行数据转换为并行数据送往MAC层。
- MAC层模块:
- 处理以太网帧的封装和解封装。它接收来自物理层的数据,解析以太网帧头,检查目的MAC地址是否为本机地址,如果是则提取有效载荷,否则将其丢弃。
- 对于发送的数据,它会添加以太网帧头(包括源MAC地址、目的MAC地址、以太网类型等),并将其传递给物理层进行发送。
- EtherCAT处理模块:
- 专门负责EtherCAT协议的处理,包括解析EtherCAT帧的头部信息,根据EtherCAT命令(如NMT命令、SDO服务、PDO交换等)进行相应的操作。
- 该模块还负责与从站进行数据交换,根据从站的配置和状态,管理数据的读写操作,以及处理从站的响应。
- 应用层模块:
- 作为用户与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_valid
和tx_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处理模块和应用层模块连接在一起,实现了数据在不同模块间的传递和处理。
四、测试与验证
- 仿真测试:
- 使用仿真工具(如ModelSim)对各个模块进行单独的功能测试,例如:
- 对物理层接口模块,发送测试数据,验证数据的串并转换是否正确。
- 对MAC层模块,输入不同的以太网帧,检查帧的解析和封装是否正确。
- 对EtherCAT处理模块,发送不同的EtherCAT命令,验证命令处理是否正确。
- 硬件测试:
- 将生成的比特流