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

news2024/11/23 11:28:20

从零开始 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、功能细节分析

  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干货分享!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/746006.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Android Java代码与JNI交互 JNI访问Java构造方法(九)

🔥 Android Studio 版本 🔥 🔥 创建包含JNI的类 JNIConstructorClass.java 🔥 package com.cmake.ndk1.jni;import com.cmake.ndk1.model.Animal;public class JNIConstructorClass {static {System.loadLibrary("constructor-class-lib");}public native…

深度学习——CNN卷积神经网络

基本概念 概述 卷积神经网络&#xff08;Convolutional Neural Network&#xff0c;CNN&#xff09;是一种深度学习中常用于处理具有网格结构数据的神经网络模型。它在计算机视觉领域广泛应用于图像分类、目标检测、图像生成等任务。 核心思想 CNN 的核心思想是通过利用局部…

环形链表(快慢指针)

给你一个链表的头节点 head &#xff0c;判断链表中是否有环。如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&#xff08;索…

C++ | 继承

目录 前言 一、继承的基本概念与使用 1、继承的概念 2、继承的定义 3、继承的访问限定符与继承方式 二、基类与派生类之间的赋值转换&#xff08;切片&#xff09; 三、继承中的作用域 1、继承中的作用域 2、隐藏&#xff08;重定义&#xff09; 四、派生类的默认构…

知识付费小程序怎么做

知识付费小程序是一种通过在线平台提供知识和教育内容的应用程序。下面将详细介绍其功能&#xff1a; 1. 音频视频课程&#xff1a; 知识付费小程序提供了丰富的音频和视频课程&#xff0c;在这些课程中&#xff0c;用户可以通过观看或听取专业讲师的讲解来学习各种知识领域。…

【文章系列解读】Nerf

1. Nerf NeRF: Representing Scenes as Neural Radiance Fields for View Synthesis 2020年8月3日 &#xff08;0&#xff09;总结 NeRF工作的过程可以分成两部分&#xff1a;三维重建和渲染。&#xff08;1&#xff09;三维重建部分本质上是一个2D到3D的建模过程&#xff…

Java习题之实现平方根(sqrt)函数

目录 前言 二分查找 牛顿迭代法 总结 &#x1f381;博主介绍&#xff1a;博客名为tq02&#xff0c;已学C语言、JavaSE&#xff0c;目前学了MySQL和JavaWed &#x1f3a5;学习专栏&#xff1a; C语言 JavaSE MySQL基础 &#x1f384;博主链接&#xff1a;tq02的…

【第四章 flutter-初识flutter】

文章目录 一、目录结构二、创建一个flutter项目三、创建自定义组件四、Container组件 就是divalignment 内容对齐方式decoration 类似border 为BoxDecoration的类 五、Text属性六、image组件总结、 一、目录结构 android、ios各自平台的资源文件 lib 项目目录 linux macos PC平…

Linux宝塔Mysql读写分离配置,两台服务器,服务器存在多个库

Linux宝塔Mysql读写分离配置&#xff0c;两台服务器&#xff0c;服务器存在多个库 一、主库操作 #登录数据库&#xff0c;用root登录方便&#xff0c;用其他账号会提示权限不足&#xff0c;需要登录root给予权限 mysql -u root -p 密码#创建一个账号&#xff0c;供从库用该账…

电商企业需要部署WMS仓储管理系统吗

随着电子商务行业的迅速发展&#xff0c;电商企业面临着日益增长的订单量和复杂的物流流程。为了提高仓储管理的效率和准确性&#xff0c;许多电商企业开始考虑部署WMS仓储管理系统。然而&#xff0c;是否真的需要部署WMS仓储管理系统&#xff0c;仍然是一个值得探讨的问题。本…

vLLM大模型推理加速方案原理(PagedAttention)

一、vLLM 简介 vLLM 用于大模型并行推理加速&#xff0c;核心是 PagedAttention 算法&#xff0c;官网为&#xff1a;https://vllm.ai/。 vLLM 主要特性&#xff1a; 先进的服务吞吐量通过 PagedAttention 对注意力 key 和 value 进行内存管理对传入请求的批处理针对 CUDA 内…

纯代码和低代码的本质区别

一、前言 纯代码和低代码是现代软件开发中两种不同的方法。 纯代码需要专业的编程技能&#xff0c;掌握编程语言、算法和数据结构等专业知识。而低代码则是一种新兴的开发方法&#xff0c;它大大降低了对编程技能的要求&#xff0c;让非技术人员也能够创建应用程序。随着低代码…

【SpringBoot】--03.数据访问、基础特性(外部化和内部外配置、整合JUnit)

文章目录 SpringBoot3-数据访问1.整合SSM场景1.1创建SSM整合项目1.2配置数据源1.3配置MyBatis1.4CRUD编写 2.自动配置原理3.扩展&#xff1a;整合其他数据源3.1 Druid 数据源 SpringBoot3-基础特性1. SpringApplication1.1 自定义 banner1.2.自定义 SpringApplication1.3Fluent…

nvm 管理node 环境配置

nvm安装&#xff1a; nvm&#xff08;Node Version Manager&#xff09;是一个用来管理node版本的工具。我们之所以需要使用node&#xff0c;是因为我们需要使用node中的npm(Node Package Manager)&#xff0c;使用npm的目的是为了能够方便的管理一些前端开发的包&#xff01;…

ColorOS凭什么夺冠?

摘要&#xff1a;五大主流安卓系统流畅度PK&#xff0c;谁的体验最好&#xff1f; 评价一款手机&#xff0c;你最先看的是什么&#xff1f; 是处理器平台&#xff1f;CPU核心频率&#xff1f;还是内存配置&#xff1f; 虽然这些硬件参数能够清晰地反映几款不同配置手机之间的性…

20230712-----阻塞IO驱动按键控制LED灯的亮灭

驱动程序 #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> #include <linux/device.h> #include <linux/cdev.h> #include <linux/slab.h> #include <linux/uaccess.h> #include <linux/of.h> #in…

TeeChart for.NET Crack

TeeChart for.NET Crack TeeChart for.NET为各种图表需求提供了图表控件&#xff0c;包括金融、科学和统计等重要的垂直领域。它可以处理您的数据&#xff0c;在各种平台上无缝创建信息丰富、引人入胜的图表&#xff0c;包括Windows窗体、WPF、带有HTML5/Javascript渲染的ASP.N…

敢不敢和AI比猜拳?能赢算我输----基于手势识别的AI猜拳游戏【含python源码+PyqtUI界面+原理详解】-python手势识别 深度学习实战项目

功能演示 摘要&#xff1a;手势识别是一种通过技术手段识别视频图像中人物手势的技术。本文详细介绍了手势识别实现的技术原理&#xff0c;同时基于python与pyqt开发了一款带UI界面的基于手势识别的猜拳游戏。手势识别采用了mediapipe的深度学习算法进行手掌检测与手部的关键点…

字符设备驱动开发(最初方式)

目录&#xff1a; 1.字符设备驱动简介2.字符设备驱动开发步骤2.1. 驱动模块的加载与卸载2.2. Makefile的编写2.3.字符设备的注册与注销2.3.1.设备号的组成2.3.2.设备号的分配 2.4.具体操作函数的实现2.4.1.进行打开和关闭操作2.4.2.对chrdev进行读写操作 3.具体程序的实现3.1.驱…

第十一章——使用类

运算符重载 运算符重载是一种形式的C多态。之前介绍过的函数重载&#xff08;定义多个名称相同但特征标不同的函数&#xff09;让程序员能够用同名的函数来完成相同的基本操作&#xff0c;即使这些操作被用于不同的数据类型。 运算符重载将重载的概念扩展到运算符上&#xff0…