从零开始 verilog 以太网交换机(六)帧处理单元设计与实现

从零开始 verilog 以太网交换机(六)帧处理单元设计与实现



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

  本章将继续进行帧处理单元设计与实现,交换机完整的架构可以参考:从零开始 verilog 以太网交换机(一)架构分析


1、帧处理单元功能

  在前级帧合路完成后,数据就来到交换机最为核心的模块——帧处理单元,在本单元中,需要完成MAC头的解析,利用之前完成的转发表模块,对source mac address进行学习,并查询数据帧的destination mac address对应哪个port,并对该帧数据进行标注,并按数据流发送到后级的队列控制器中。

注意:



2、帧处理单元接口

  帧处理单元接口如下表所示,包括前级帧合路单元的state fifo和data fifo接口,端口转发表的请求-响应接口以及给后级队列管理器的数据流接口。

在这里插入图片描述


3、帧处理单元实现细节

  如前文谈到的,帧处理单元最为重要的功能就是确定数据帧该去往何处,在该单元中是通过目的mac address信息,并通过目的mac address查找端口映射表得到,同时对源mac address进行逆向学习,维护交换机内的端口映射表。
  当然,除了确定数据帧的端口号之外,在帧处理单元还可以完成各种对数据帧本身处理的操作,不仅限于L2层,不过在v1的交换机版本中,暂时没有其它功能,在v2中我们会增加更多处理。


3.1、功能细节分析

  1. 将数据帧的source mac发送给转发表模块进行端口号和mac address的逆向学习;
  2. 当dest mac address=48‘hFFFFFF时,发送广播包;
  3. 将dest mac address发送到转发表中查找对应的端口号,若找到则将portid记录,若查找失败则发送广播包(广播包不再发送到原端口);
  4. 在向后级传送数据时,将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干货分享!

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现一个简单的交换机,可以采用以下步骤: 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 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值