从零开始 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干货分享!