从零开始 verilog 以太网交换机(五)帧合路单元的设计与实现

从零开始 verilog 以太网交换机(五)帧合路单元的设计与实现



🔈声明:
😃博主主页:王_嘻嘻的CSDN主页
🧨 从零开始 verilog 以太网交换机系列专栏:点击这里
🔑未经作者允许,禁止转载,侵权必删
🚩关注本专题的朋友们可以收获一个经典交换机设计的全流程,包括设计与验证(FPGA);以太网MAC的基础知识。新手朋友们还将获得一个具有竞争力的项目经历,后续整个工程和代码下载链接也都会放在csdn和公众号内

  本章将开始进行帧合路单元的设计,其负责将多个mac controller的帧合并为一路进行后续处理

  交换机完整的架构可以参考:从零开始 verilog 以太网交换机(一)架构分析


1、以太网帧合路功能

  交换机的核心功能是 n端口⬅➡n端口的数据包传输,除了前一章谈到的帧转发表查询功能外,还会有其他的帧处理功能需要实现在交换机中。显然不可能每个端口都拥有一个独立的帧处理单元,通常只有一个帧处理单元通过帧合路将n个端口的数据帧合并为一路,再进入帧处理单元,最后根据目的端口号,进行分路,分发到对应端口上。

  所以帧合路单元的作用本质上就是一个arbiter + mux,但是需要规划好单元的主频,否则在最开始的合路阶段就可能来不及处理多路的数据帧。因为MII接口支持10Mbps和100Mbps两种速率,交换机支持4端口,所以,帧合路最低需要400Mbps的处理速度,为了便于后续升级,我们在此采用500MHz的时钟频率,最高能支持4Gbps的数据传输


2、帧合路单元接口

  帧合路单元接口较为简单,只有mac_r的4组fifo输入,以及帧合并后的一组fifo输出,由于mac_r的接口相同,这里直接采用二维数组的方式来进行接口的设计。

在这里插入图片描述


3、帧合路单元实现细节

3.1、功能细节分析

  帧合路单元整体实现比较简单,通过仲裁器选出一路mac_r,并对该mac的一帧数据进行检错,去除CRC-32的校验码,并填上交换机端口号,以便转发表进行后续处理;最后把处理合路后的数据帧存入sfifo中。

  综上,总体功能为以下两点:

  • 仲裁
  • 帧处理

  在第一版的交换机中,我们直接采用round robin arbiter作为仲裁器,帧处理操作也仅有检错、端口号、CRC的处理;
  之后的版本中,该模块会采用优先级可量化配置的仲裁期,帧处理操作也会更为丰富,复杂

①:轮询仲裁器:按照一定的顺序轮流分配资源,以保证公平性和效率。


3.2、核心电路设计

请添加图片描述

  帧合路单元架构如上图所示,理论上后级转发电路也能满足4Gbps的数据传输速率,所以frame mux只要能存储处理一次最大帧过程中,所可能来的数据帧即可。

  最大帧1518B处理过程中,最多可能来24个最小帧,我们以2^n计算,将state fifo设置成16 x 32;将data fifo设置成8 x 2048;

  此外,端口号将直接填在state fifo中的保留位,目前为3-bits,后续如果需要可以扩展。


3.3、帧合路单元代码

  Verilog代码将放在下面,Testbench就不展示了,有需要的可以等专题结束后在资源中下载,或者去我的公众号获得链接

  在实现上,我们直接调用了round robin arbiter的IP,就不展开介绍了,感兴趣的朋友可以自行搜索资料了解。


module frame_mux(
clk,rst_n,
mac_rx_state_fifo_empty,
mac_rx_state_fifo_rd,
mac_rx_state_fifo_dout,
mac_rx_data_fifo_rd,
mac_rx_data_fifo_dout,
frame_mux_state_fifo_empty,
frame_mux_state_fifo_rd,
frame_mux_state_fifo_dout,
frame_mux_data_fifo_rd,
frame_mux_data_fifo_dout
);

parameter       PORT_NUM    =   4;


`define         FRAME_LEN_RANGE     10:0
`define         FRAME_LEN_ERR_BIT   11
`define         FRAME_CRC_ERR_BIT   12
`define         FRAME_PORT_RANGE    15:13


input                           clk;
input                           rst_n;

input   [PORT_NUM-1:0]          mac_rx_state_fifo_empty;
output  [PORT_NUM-1:0]          mac_rx_state_fifo_rd;
input   [PORT_NUM-1:0] [15:0]   mac_rx_state_fifo_dout;
output  [PORT_NUM-1:0]          mac_rx_data_fifo_rd;
input   [PORT_NUM-1:0] [7:0]    mac_rx_data_fifo_dout;

output                          frame_mux_state_fifo_empty;
input                           frame_mux_state_fifo_rd;
output  [15:0]                  frame_mux_state_fifo_dout;
input                           frame_mux_data_fifo_rd;
output  [7:0]                   frame_mux_data_fifo_dout;



//rrarb
wire    [PORT_NUM-1:0]          mac_r_req;
wire                            mac_r_grant_vld;
wire    [PORT_NUM-1:0]          mac_r_grant;
wire                            mac_r_switch_to_next;

//demux
wire    [15:0]                  mux_state_dout;
wire    [7:0]                   mux_data_dout;

//frame field
wire                            err_frame;
wire    [10:0]                  frame_len;
wire    [2:0]                   frame_port_idx;
wire                            frame_vld;
//mar fifo operation
reg     [10:0]                  mac_data_fifo_pop_cnt; 
wire                            frame_mux_state_fifo_wr;
/*------------------------------------------------------------
rrarb choose one mac
------------------------------------------------------------*/
assign mac_r_req[PORT_NUM-1:0] = mac_rx_state_fifo_empty;


rrarb_ff x_rrarb_mac_r(
    .clk(clk),
    .rst_n(rst_n),
    .req(mac_r_req[PORT_NUM-1:0]),
    .grant(mac_r_grant_vld),
    .grant_ff(mac_r_grant[PORT_NUM-1:0]),
    .grant_vld(),
    .switch_to_next(mac_r_switch_to_next)
);


one_hot_mux_2d 
    x_one_hot_mux_mac_state(
    .din(mac_rx_state_fifo_dout),
    .sel(mac_r_grant[PORT_NUM-1:0]),
    .dout(mux_state_dout[15:0]),
    .err()
);

one_hot_mux_2d 
    x_one_hot_mux_mac_data(
    .din(mac_rx_data_fifo_dout),
    .sel(mac_r_grant[PORT_NUM-1:0]),
    .dout(mux_data_dout[7:0]),
    .err()
);



assign err_frame = mac_r_grant_vld ? mux_state_dout[`FRAME_LEN_ERR_BIT] | mux_state_dout[`FRAME_CRC_ERR_BIT]
                                   : 1'b0 ; 

assign frame_len[10:0] = mux_state_dout[`FRAME_LEN_RANGE]-11'd4;   //ignore crc 4B
assign frame_vld = mac_r_grant_vld & (~err_frame);

always @(posedge clk or negedge rst_n)begin
    if(!rst_n)
        mac_data_fifo_pop_cnt[10:0] <= 11'b0;
    else if( (mac_data_fifo_pop_cnt[10:0]==frame_len[10:0]) | (~frame_vld) )
        mac_data_fifo_pop_cnt[10:0] <= 11'b0;
    else if(frame_vld)
        mac_data_fifo_pop_cnt[10:0] <= mac_data_fifo_pop_cnt[10:0] + 11'b1;
end

assign frame_mux_state_fifo_wr = (mac_data_fifo_pop_cnt[10:0]==frame_len[10:0]) & frame_vld;


assign frame_port_idx[2:0] = (mac_r_grant[PORT_NUM-1:0]=={{(PORT_NUM-1){1'b0}},1'b1})    ? 3'd0 :
                             (mac_r_grant[PORT_NUM-1:0]=={{(PORT_NUM-1){1'b0}},1'b1}<<1) ? 3'd1 :  
                             (mac_r_grant[PORT_NUM-1:0]=={{(PORT_NUM-1){1'b0}},1'b1}<<2) ? 3'd2 :
                             (mac_r_grant[PORT_NUM-1:0]=={{(PORT_NUM-1){1'b0}},1'b1}<<3) ? 3'd3 : 3'd0;

sfifo #(
    .DEPTH(32),
    .WIDTH(16)
    )x_state_fifo(
   .clk(clk),
   .data_in({frame_port_idx[2:0],mux_state_dout[12:0]}),
   .data_out(frame_mux_state_fifo_dout[15:0]),
   .empty_n(),
   .empty(frame_mux_state_fifo_empty),
   .full_n(),
   .full(),
   .rd_en(frame_mux_state_fifo_rd),
   .rst_n(rst_n),
   .wr_en(frame_mux_state_fifo_wr),
   .almost_full_n(),
   .almost_empty_n()
   );


sfifo #(
    .DEPTH(2048),
    .WIDTH(8)
    )x_data_fifo(
   .clk(clk),
   .data_in(mux_data_dout[7:0]),
   .data_out(frame_mux_data_fifo_dout[7:0]),
   .empty_n(),
   .empty(),
   .full_n(),
   .full(),
   .rd_en(frame_mux_data_fifo_rd),
   .rst_n(rst_n),
   .wr_en(mac_r_grant_vld),
   .almost_full_n(),
   .almost_empty_n()
   );



endmodule




搜索关注我的微信公众号【IC墨鱼仔】,获取我的更多IC干货分享!

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
实现一个简单的交换机,可以采用以下步骤: 1. 设计交换机的流水线结构,例如采用三级流水线:接收数据包、查找目的地址、转发数据包。 2. 实现交换机的输入输出端口,包括以太网端口和控制端口。 3. 设计交换机的查找表,用于存储MAC地址和对应的端口信息,可以采用SRAM或者CAM实现。 4. 实现交换机的流水线逻辑,包括接收数据包、查找目的地址、转发数据包等。 5. 测试交换机的性能和正确性,可以通过模拟数据包的发送和接收来进行测试。 以下是一个简单的Verilog代码示例,实现了一个具有4个以太网端口和1个控制端口的交换机: ``` module switch( input clk, input rst, input [47:0] in_data, // 输入数据包 input [3:0] in_port, // 输入端口 output reg [47:0] out_data, // 输出数据包 output reg [3:0] out_port, // 输出端口 input [47:0] mac_table [0:255], // MAC地址表 input [7:0] mac_port [0:255], // MAC地址对应的端口表 input [7:0] ctrl, // 控制端口 output reg [7:0] status // 状态输出 ); // 定义状态机状态 parameter IDLE = 0; parameter RECEIVE = 1; parameter FORWARD = 2; // 定义状态机变量 reg [1:0] state; reg [47:0] dest_mac; reg [3:0] dest_port; // 接收数据包 always @(posedge clk) begin if (rst) begin state <= IDLE; dest_mac <= 48'h000000000000; dest_port <= 4'h0; out_port <= 4'h0; out_data <= 48'h000000000000; status <= 8'h00; end else begin case(state) IDLE: begin if (in_port == ctrl) begin state <= RECEIVE; end end RECEIVE: begin dest_mac <= in_data[47:0]; state <= FORWARD; end FORWARD: begin if (mac_table[dest_mac[23:16]][47:0] == dest_mac) begin dest_port <= mac_port[dest_mac[23:16]][3:0]; end out_port <= dest_port; out_data <= in_data; status <= dest_port; state <= IDLE; end endcase end end endmodule ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值