在大三下学期,我学习了“通信芯片设计基础”这门课。说实话,我最感兴趣的还是Verilog实践部分,对于模电学的不好的我,再去学PN结、mos管电路等等知识实在是遭罪。最后老师布置了一个很难的大作业,具体要求见下图:
布置的时候是5月底,同时还有其他几门课的实践要做,又因为这个作业的截止日期是6月18日,在不知道期末考试时间的情况下,暂时搁置没有做。等到6月初所有课程结课,知道6.15-6.20会把所有课考完的时候,才反应过来只能用复习周的时间来做了。一开始准备两个人合作着做,后来发现我们低估了这个东西的难度,而且两个人的进度难以同步。于是我从6月9日晚上开始,给自己两天的时间,全力投入这个实践(因为再耽误就来不及复习了),终于在6月12号的早上成功进行了仿真,得到了想要的结果,晚上写完了实验报告。虽然延迟了一天,但这种成就感是满满的(尤其是得知班上暂时没有别人做出来的时候)。
反正的确花了时间,也确确实实学到了东西。本来打算考试结束以后进一步完善,但是没有精力了。就以这个为开端,开始我的博客记录吧。下面介绍一下我的思路。
设计方案
按照我粗浅的理解,整个项目可以大致分为4个模块:地址学习表模块、查表模块、数据帧处理模块和MAC核通信模块。但这些模块不是完全相互独立的,这是按照功能所进行的大致区分。接下来我会一一介绍这些模块的设计方案。
地址学习表模块
在接触这个作业之前,除了在课堂上听老师说了一下哈希以外,我对地址学习表的认知仅限于在通信网络基础课上学到的知识,了解概念和逻辑但从未想过用某种语言去实现。因此,我最直观的想法就是用二维寄存器数组来实现,一个维度是序号(广度),另一个维度是内容(深度)。我也询问了ChatGPT如何用Verilog实现地址学习表,得到的回应是不仅可以使用二维寄存器数组,还可以“使用别的数据结构如链表、结构体和哈希表”来实现。联想到老师上课讲过的内容,我继续追问哈希表的具体情况,得到的回应是,不能直接实现哈希表,只能使用Verilog语言来模拟哈希表的功能。看完示例后,我坚定了使用二维寄存器数组构建地址学习表的想法。由于我本学期选修的科目是高级程序设计(数据结构),我对链表和结构体也有一定的了解。但经过查阅资料,发现Verilog并不支持链表和结构体,猜想可能是Verilog中没有指针的概念。
于是,我打算进一步学习哈希表。通过询问助教老师,明确了用Verilog语言实现哈希表的结构和逻辑:
实际运用时,需要先初始化所有表的所有项为0。当数据帧传入时,提取其源MAC地址,并记录传入数据帧的端口号。接着对源MAC地址进行Hash值计算。若Hash值对应地址的内容为空,就在两个表内分别写入发送数据帧的端口号和源MAC地址,并在老化时间表内赋予默认的老化时间time_decay。如果不为空,说明已经记录,但还需要更新老化时间,将默认老化时间写入老化时间表中Hash值对应地址,其他两个表内容不变。
对于老化时间,采用一个计数器进行计数,每过一定的时钟周期,所有不为0的表项的值都自减。直到减为0,记录该表项的Hash地址,删除其它两个表中地址相同的表项。
运用Hash算法,不仅起到了索引的作用,还对MAC地址进行了压缩和加密,没有开启MAC核中的CRC校验功能就完成了CRC加密。
查表模块
与写表的逻辑相似,查表也需要先提取数据帧中的目标MAC地址,然后进行Hash运算,以Hash值为地址直接访问三张表中对应的表项。如果内容不为空,就读出端口号,作为目标端口准备发送。如果目标MAC地址是全1序列(广播),就不查表,直接向所有端口发送数据帧。
数据帧处理模块
这个模块的内容包括从数据帧中提取源MAC地址、目标MAC地址和数据、将提取的地址和数据重新组合成新的数据帧发送等功能。实现的前提是学习以太网帧结构。大致的逻辑是:对于接收到的数据帧,记录其发送端口,并提取其源MAC地址。对于要发送的数据帧,提取其目的MAC地址,采用addr_fanzhuan任务重组数据帧的MAC地址部分。所以,核心是对MAC地址的提取。由于数据帧只有32位宽,但MAC地址有48位宽,因此在处理完数据帧的前置序列后,还需要通过打两拍的方式读取MAC地址。
对于4个端口我都分别采用了一个实例实现MAC地址的提取,提取完成后均会采用addr_rdy1/2/3/4置高位来标记提取完毕的端口。在上层模块中还有一个总的addr_rdy用来判断和示意引入哪一个端口的地址,当输入不使能后,就把原来置为高位的addr_rdy1/2/3/4再置于低位,这样能够确保每次只读入一个端口提取好的地址,从而良好地避免冲突。
MAC核通信模块
对于我个人而言,这是我最头疼的模块,需要面对庞大的开源MAC核代码。即使很明确逻辑,也很难在实例化时确保每一个端口都正确。在这里我想把它和发包函数一起讲一讲,因为涉及到一些以太网帧结构的知识,如果弄清楚将无法正确发包。
先来说说MAC核本身,大致可以分为输入区端口和输出区端口,分别管理着MAC核的输入数据和输出数据。如果要实现独立的收发,就需要在实例化时全部和定义的属于自己的端口连起来。读到这里可能有些疑惑,但可以先看看我的实例化示例:
如果要实现4个MAC回环测试模式,就需要把第一个核的输入区和第二个核的输入区连接、把第二个核的输出区和第三个核的输入区连接,以此类推。
可以很清楚的看到,同样是对第二个MAC核实例化,输入区输出区连线的改变直接影响着功能。
此外,为了实现地址学习、查表等功能,还需要把这些端口引入相应的模块中作为输入和输出。
接下来我分析一下用于测试的发包模块ephy。一开始我并没有注意到这个模块,因为它在所给范例的文件夹中藏得很深。后来真正测试时才发现,必须弄清楚这个模块的含义。该模块本身的输入是关于使能和同步的一些寄存器,重点是名为Rxd的端口,该模块主要采用Rxd进行变量的传输和通信。
在其内部编写的send_frame任务,传入的参数原本是数据帧总长度和数据data内容,但为了方便测试,我还加入了目标MAC地址以及源MAC地址作为传入参数。
需要注意的是premble_100M任务,它的内容是在Rxd端口输出一些固定的值。通过学习以太网帧结构,我了解到以太网帧最前面的8个字节的作用是同步,也印证了premble_100M是必须执行的。关于pad_100M,就是将目标MAC地址、源MAC地址和数据内容按需通过Rxd进行发送。其实在之前,我并没有接触过Verilog里的task。这次实践大大拓宽了我对Verilog的认识。
仿真验证平台搭建方案
我认为,TestBench文件的编写是不算困难的,只需要把该实例化的模块实例化就可以。关键问题在于发送数据帧的模块配置。在内容以外的另一个关键点就是用脚本进行仿真。
先来说说发送数据帧的模块配置。根据我在上一个部分的分析,该模块的关键是Rxd端口。如果在tb中只进行一次实例化,通过逻辑来实现对所选择MAC核的发送,是远远不如进行4个实例化,再把对应的端口连接起来来的直接和方便的,因为每个MAC核的端口没有数据结构管理,采用逻辑选择的模块反而是浪费资源。
我设置发送四个包,下一个包的目标MAC地址正好是前一个包的源MAC地址,这样能够清晰地看到对应关系。
关于用脚本进行Modelsim仿真,我也是和助教老师沟通学习,按照获得的模板,再自己查阅资料,最终编写出较为完善的运行脚本。我认为关键不在于脚本本身,而是对Modelsim甚至Vivado等软件的理解。我发现,与Modelsim的交互完全可以采用命令行来实现,这也激起了我对软件编写的思考。在编写脚本的过程中我也遇到了一些问题,比如需要获取所有需要编译的文件的地址,我自己编写了一个脚本用来获取其绝对地址(相对地址也可以),并生成一个文本文档来记录。总而言之,我从学习用脚本进行仿真的过程中学到了很多,不仅磨练了自己写代码的能力,而且拓宽了我的知识面,并且加深了我对语言、软件的理解。
仿真结果分析
我设置发送四个包,下一个包的目标MAC地址正好是前一个包的源MAC地址,这样能够清晰地看到对应关系。对于输入端口号,我采取了独热码编码,因此位宽为4。可以看到,成功地读取了每个端口发送数据帧的目的MAC地址和源MAC地址,并通过Hash算法计算出了地址,存入了对应的表。以第一次输入为例,记录输入端口号为4’h1,解析出的源MAC地址为48’h3333,先通过Hash算法算出Hash值为10’h275,再将源MAC地址写入mac_table[275],其值变为48’h3333,同时将输入端口号写入port_table[275],其值变为4’h1。以第一次输出为例,解析出目的MAC地址为48’h3333,先通过Hash算法算出Hash值为10’h275,然后直接查询mac_table[275],其值不为空,于是查询port_table[275],得到输出端口号4’h1。此时output_port_reg的值变为4’h1。
能看到数据按照输出端口被正确发送。如果放大看,还能看到整个数据包的传输过程:在前置序列后,先传输目的MAC地址,再传输源MAC地址,最后是数据内容。
心得体会
这次实验我真的学到了很多,非常感谢帮助我的两位助教学长给予我指导和帮助。我的代码编写能力和对Verilog语言的认识加深了很多。我意识到Verilog语言的局限性,也深刻感受到老师所说的“心中有电路”是什么感觉:开始写代码之前一定要弄清楚模块之间的逻辑。此外我对通信网络以及TCL语言有了更广泛更深入的认识和理解。虽然大作业很困难,我也参考了学长的代码,但是自己修改、编写模块并最终实现功能的过程让我充满了信心和兴趣,希望在以后的学习道路上也可以多一点这样的机会,并且做的越来越好。
写在最后
虽然以后可能不研究FPGA的通信方向,虽然时间很仓促,但是这个作业让我学到了很多。本人处于保研边缘,但是非常愿意深入研究某一方面,快要被拼命卷分数的同学卷死力()我希望像我这样的成绩不是特别优秀但是愿意钻研愿意主动学习新知识的同学能得到更多的机会,具体咋办我也不到捏,现在只能求求我能入围保研名单┭┮﹏┭┮