从零开始 verilog 以太网交换机(六)帧处理单元设计与实现
🔈声明:
😃博主主页:王_嘻嘻的CSDN主页
🧨 从零开始 verilog 以太网交换机系列专栏:点击这里
🔑未经作者允许,禁止转载,侵权必删
🚩关注本专题的朋友们可以收获一个经典交换机设计的全流程,包括设计与验证(FPGA);以太网MAC的基础知识。新手朋友们还将获得一个具有竞争力的项目经历,后续整个工程和代码下载链接也都会放在csdn和公众号内
本章将继续进行帧处理单元设计与实现,交换机完整的架构可以参考:从零开始 verilog 以太网交换机(一)架构分析。
1、帧处理单元功能
在前级帧合路完成后,数据就来到交换机最为核心的模块——帧处理单元,在本单元中,需要完成MAC头的解析,利用之前完成的转发表模块,对source mac address进行学习,并查询数据帧的destination mac address对应哪个port,并对该帧数据进行标注,并按数据流发送到后级的队列控制器中。
注意:
- 转发表模块的内容可参考从零开始 verilog 以太网交换机(四)以太网转发表的设计与实现;
- 转发表中使用到的hash key暂时用destination mac address低位来代替,在之后switch v2的版本中会进一步完善hash算法;
- 当前设计还较为简单,输出的数据流暂不考虑反压;
2、帧处理单元接口
帧处理单元接口如下表所示,包括前级帧合路单元的state fifo和data fifo接口,端口转发表的请求-响应接口以及给后级队列管理器的数据流接口。
3、帧处理单元实现细节
如前文谈到的,帧处理单元最为重要的功能就是确定数据帧该去往何处,在该单元中是通过目的mac address信息,并通过目的mac address查找端口映射表得到,同时对源mac address进行逆向学习,维护交换机内的端口映射表。
当然,除了确定数据帧的端口号之外,在帧处理单元还可以完成各种对数据帧本身处理的操作,不仅限于L2层,不过在v1的交换机版本中,暂时没有其它功能,在v2中我们会增加更多处理。
3.1、功能细节分析
- 将数据帧的source mac发送给转发表模块进行端口号和mac address的逆向学习;
- 当dest mac address=48‘hFFFFFF时,发送广播包;
- 将dest mac address发送到转发表中查找对应的端口号,若找到则将portid记录,若查找失败则发送广播包(广播包不再发送到原端口);
- 在向后级传送数据时,将portid和frame length一并携带传输;
3.2、帧处理单元核心电路设计
上图为帧处理单元的核心状态机,共分为5个状态:
- IDLE:空闲,等待数据帧;
- RD_HDR:从data fifo中读取MAC header,解析关键field;
- LEARN:将source mac address进行逆向学习过程:
- SEARCH:根据dest mac address搜索portid过程;
- TRANS:将所获得的所有信息组包,并向下级发送的过程;
当前级的帧合路模块中存放完整的数据帧后,帧处理单元将开始状态机的跳转,首先从前级的data fifo中读取14或18Byte的Mac header,得到source address、dest address和frame type(v1版本中frame type暂时没有左右),并根据前级的state fifo中的length计算得到data fifo中还需读取的payload长度。
然后进行source mac address和接收portid的逆向学习,当lut模块返回ack时代表学习成功,返回nak时代表lut同样的hash key已存放了两个表项或者,lut已满,学习失败。
在SEARCH状态时,根据dest mac address决定是否需要查表得到对应portid,若dest address=48‘hFFFFFF,则该数据帧为广播数据帧,不需要查表,直接向后级发送;若dest address!=48’hFFFFFF,则需要进行查表,lut若返回ack,代表查询成功,会将portid一并返回;若返回nak,代表查询失败,数据帧也需要广播处理。
最后将porid,数据帧length、mac header和data fifo中剩余payload一起发送到后级的队列控制器中。
3.3、帧处理单元代码
Verilog代码放在下面,Testbench就不展示了,有需要的可以等专题结束后在资源中下载,或者去我的公众号获得链接。
module frame_process(
clk,
rst_n,
//frame_mux interface
frame_mux_ptr_fifo_empty,
frame_mux_ptr_fifo_rd,
frame_mux_ptr_fifo_dout,
frame_mux_data_fifo_rd,
frame_mux_data_fifo_dout,
//hash_lut interface
hash_lut_source,
hash_lut_req,
hash_lut_mac,
hash_lut_hash,
hash_lut_portmap,
hash_lut_ack,
hash_lut_nak,
hash_lut_result,
//queue_controller interface
queue_sof,
queue_dv,
queue_dout
);
parameter MAC_HEADER_LEN = 5'd14;
input clk;
input rst_n;
input frame_mux_ptr_fifo_empty;
output frame_mux_ptr_fifo_rd;
input [15:0] frame_mux_ptr_fifo_dout;
output frame_mux_data_fifo_rd;
input [7:0] frame_mux_data_fifo_dout;
output hash_lut_source;
output hash_lut_req;
output [47:0] hash_lut_mac;
output [9:0] hash_lut_hash;
output [15:0] hash_lut_portmap;
input hash_lut_ack;
input hash_lut_nak;
input [15:0] hash_lut_result;
output queue_sof;
output queue_dv;
output [7:0] queue_dout;
wire frame_mux_ptr_fifo_rd;
reg frame_mux_data_fifo_rd;
reg queue_sof;
reg queue_dv;
reg [7:0] queue_dout;
localparam IDLE = 5'b00001;
localparam RD_HEADER = 5'b00010;
localparam LEARN = 5'b00100;
localparam SEARCH = 5'b01000;
localparam TRANS = 5'b10000;
//fsm state
reg [4:0] cur_state;
reg [4:0] nxt_state;
reg [10:0] frame_pyld_cnt; //数据帧长度计数
reg [4:0] frame_header_cnt; //数据帧mac地址及类型操作计数
wire trans_mac_header_over; //frame_info_cnt清零标志位
//frame decode
wire [3:0] dec_portid;
wire [10:0] dec_frame_len;
//frame info
reg [47:0] src_mac; //源MAC地址
reg [47:0] dst_mac; //目的MAC地址
reg [15:0] frame_type; //数据帧类型
reg [3:0] queue_portmap; //输出给后级队列的端口映射位图(本设计中仅使用4个port)
wire broadcast; //广播指示寄存器
wire [11:0] frame_length;
//=============================FSM==============================
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
cur_state[4:0] <= IDLE;
else
cur_state[4:0] <= nxt_state[4:0];
end
always @(*)begin
case(cur_state[4:0])
IDLE: nxt_state[4:0] = (!frame_mux_ptr_fifo_empty) ? RD_HEADER : IDLE;
RD_HEADER: nxt_state[4:0] = (frame_header_cnt[4:0]==MAC_HEADER_LEN-1) ? LEARN : RD_HEADER;
LEARN: nxt_state[4:0] = (hash_lut_ack | hash_lut_nak) ? broadcast ? TRANS : SEARCH : LEARN;
SEARCH: nxt_state[4:0] = (hash_lut_ack | hash_lut_nak) ? TRANS : SEARCH;
TRANS: nxt_state[4:0] = (frame_pyld_cnt[10:0]==11'b1) ? IDLE : TRANS;
default: nxt_state[4:0] = IDLE;
endcase
end
//===========================frame decode=========================
assign dec_portid[3:0] = frame_mux_ptr_fifo_dout[14:11];
assign dec_frame_len[10:0] = frame_mux_ptr_fifo_dout[10:0];
//==============================cnt===============================
always @(posedge clk or negedge rst_n)begin
if(!rst_n)begin
frame_pyld_cnt[10:0] <= 11'b0;
frame_header_cnt[4:0] <= 5'b0;
end
else begin
case(cur_state[4:0])
IDLE:begin
frame_pyld_cnt[10:0] <= dec_frame_len[10:0] - MAC_HEADER_LEN;
frame_header_cnt[4:0] <= 5'b0;
end
RD_HEADER:
frame_header_cnt[4:0] <= frame_header_cnt[4:0] + 1'b1;
TRANS:begin
frame_pyld_cnt[10:0] <= (frame_header_cnt[4:0]==MAC_HEADER_LEN+2'd1) ? frame_pyld_cnt[10:0]-1'b1 : frame_pyld_cnt[10:0];
frame_header_cnt[4:0] <= (frame_header_cnt[4:0] < (MAC_HEADER_LEN+2'd1) ) ? frame_header_cnt[4:0] + 1'b1 : frame_header_cnt[4:0];
end
default:begin
frame_pyld_cnt[10:0] <= frame_pyld_cnt[10:0];
frame_header_cnt[4:0] <= 5'b0;
end
endcase
end
end
assign frame_over = (cur_state[4:0]==TRANS) & (frame_pyld_cnt[10:0]==11'b1);
//========================frame_mux fifo rd========================
assign frame_mux_ptr_fifo_rd = frame_over;
assign frame_mux_data_fifo_rd = (cur_state[4:0]==RD_HEADER) | ((cur_state[4:0]==TRANS) & trans_mac_header_over);
//===========================addr register=======================
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
dst_mac[47:0] <= 48'b0;
else if( (cur_state[4:0]==RD_HEADER) & (frame_header_cnt[4:0]<5'd6) )
dst_mac[47:0] <= {dst_mac[39:0],frame_mux_data_fifo_dout[7:0]}; //将目的MAC地址移位寄存
else if( (cur_state[4:0]==TRANS) & (frame_header_cnt[4:0]>=5'd2 & frame_header_cnt[4:0]<5'd8) )
dst_mac[47:0] <= {dst_mac[39:0],8'b0}; //将目的MAC地址输出
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
src_mac[47:0] <= 48'b0;
else if( (cur_state[4:0]==RD_HEADER) & (frame_header_cnt[4:0]>=5'd6 & frame_header_cnt[4:0]<5'd12) )
src_mac[47:0] <= {src_mac[39:0],frame_mux_data_fifo_dout[7:0]};
else if( (cur_state[4:0]==TRANS) & (frame_header_cnt[4:0]>=5'd8 & frame_header_cnt[4:0]<5'd14) )
src_mac[47:0] <= {src_mac[39:0],8'b0};
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
frame_type[15:0] <= 16'b0;
else if( (cur_state[4:0]==RD_HEADER) & (frame_header_cnt[4:0] >= 5'd12) )
frame_type[15:0] <= {frame_type[7:0],frame_mux_data_fifo_dout[7:0]};
else if( (cur_state[4:0]==TRANS) & (frame_header_cnt[4:0] >= 5'd14) )
frame_type[15:0] <= {frame_type[7:0],8'b0};
end
assign frame_length[11:0] = dec_frame_len[10:0] + 11'd2;
//==========================hash lut===============================
assign hash_lut_portmap[15:0] = {12'b0,dec_portid[3:0]};
assign hash_lut_req = (cur_state[4:0]==LEARN) | (cur_state[4:0]==SEARCH);
assign hash_lut_source = cur_state[4:0]==LEARN;
assign hash_lut_mac[47:0] = (cur_state[4:0]==LEARN) ? src_mac[47:0] : dst_mac[47:0];
assign hash_lut_hash[9:0] = hash_lut_mac[9:0];
assign broadcast = (dst_mac[47:0]==48'hffff_ffff);
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
queue_portmap[3:0] <= 4'b0;
else if( (cur_state[4:0]==SEARCH) & hash_lut_ack)
queue_portmap[3:0] <= hash_lut_result[3:0];
else if( ((cur_state[4:0]==SEARCH) & hash_lut_nak) | broadcast) //广播帧
queue_portmap[3:0] <= ~hash_lut_portmap[3:0];
end
//===============================queue text================================
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
queue_sof <= 1'b0;
else if( (cur_state[4:0]==TRANS) & (~queue_dv))
queue_sof <= 1'b1;
else
queue_sof <= 1'b0;
end
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
queue_dv <= 1'b0;
else if(cur_state[4:0]==TRANS)
queue_dv <= 1'b1;
else
queue_dv <= 1'b0;
end
assign trans_mac_header_over = (cur_state[4:0]==TRANS) & (frame_header_cnt[4:0]==MAC_HEADER_LEN+5'd1);
always @(posedge clk or negedge rst_n)begin
if(!rst_n)
queue_dout[7:0] <= 8'b0;
else if( (cur_state[4:0]==TRANS) & (~trans_mac_header_over) )begin
if(frame_header_cnt[3:0]==4'd0)
queue_dout[7:0] <= {frame_length[11:8],queue_portmap[3:0]};
else if(frame_header_cnt[3:0]==4'd1)
queue_dout[7:0] <= frame_length[7:0];
else if(frame_header_cnt[3:0]<4'd8)
queue_dout[7:0] <= dst_mac[47:40];
else if(frame_header_cnt[3:0]<4'd14)
queue_dout[7:0] <= src_mac[47:40];
else
queue_dout[7:0] <= frame_type[15:8];
end
else if(cur_state[4:0]==TRANS)
queue_dout[7:0] <= frame_mux_data_fifo_dout[7:0];
end
endmodule
搜索关注我的微信公众号【IC墨鱼仔】,获取我的更多IC干货分享!