在 FPGA 上实现路由器(3)

前言 又半个月过去了,在写了上篇系列博文之后也是做了很多新的更改。上次做的主要是关于性能方面的提升,怎么提高频率,从而达到比较大的流量,而这段时间做的则是功能,做实现 RIP 协议和转发表的动态更新。 软件部分 软件部分目前是用 C 代码写的,用 Xilinx SDK 提供的各个 AXI 外设的驱动和PS自己的驱动,实现了所需要的,RIP协议的处理,转发表的更新和统计信息的读取。 实际上做的时候比较粗暴,主要是通过三种 AXI 外设与硬件部分进行交互:AXI Stream FIFO,AXI GPIO 和 AXI BRAM Controller 。其中 AXI Stream FIFO 是用来接收和发送需要CPU处理的以太网帧的,AXI GPIO则是用来读取统计的信息,AXI BRAM Controller 是用来读写转发表的。最后在顶层设计中把这些外设连接起来。 硬件部分 硬件部分还是继续之前的部分往下写,添加了统计信息,直接暴露出去,让 CPU 走 AXI GPIO 读,因为不需要很高的精确度;转发表本身,一开始想的是自己写一些接口转换,后来发现,直接用 True Dual Port RAM 然后把一个 port 暴露给 AXI BRAM Controller 即可,免去了各种麻烦,PS可以直接进行修改,不需要额外的工作。 最终效果 为了测试这套东西是否正常工作,就开了两个 Arch Linux 的虚拟机,分别 Bridge 到两个千兆的 USB 网卡上,都连到 FPGA 上。然后在两边都配上了 BIRD ,配置 RIP 和一些路由,确实能更新硬件的转发表,并两边的 RIP 可以学习到对方的路由。

Read More

每周分享第 26 期

Grafana 6.2 发布 https://grafana.com/blog/2019/05/22/grafana-v6.2-released/ Program Synthesis Talk by Paul Zhu https://paulz.me/talk/program-synthesis/ VSCode 摸鱼插件 https://github.com/cteams/Thief-Book 编译期 C++ 计算器 https://www.zhihu.com/question/28582706/answer/691444859 一个在线的 tomasulo 算法实现 https://tomasulo.harrychen.xyz/ Safari Technology Preview 83 发布,含 WebAuthN 支持 https://webkit.org/blog/8967/release-notes-for-safari-technology-preview-83/ Markdown -> 微信 https://github.com/lyricat/wechat-format Rust 的 Code Coverage 库 https://github.com/xd009642/tarpaulin

Read More

IP 和 UDP Checksum 的增量更新问题

之前在写 IP Checksum 的增量更新,就是当 TTL -= 1 的时候,Checksum 应该增加 0x0100 ,但是这样会有问题,在于,如果按照原来的 IP Checksum 计算方法,是不会出现 0xFFFF 的(求和,进位,然后取反写入),这种加法就有可能出现 0xFFFF 。于是翻阅了相关的RFC: 首先是 RFC 1141 相关部分: unsigned long sum; ipptr->ttl--; /* decrement ttl */ sum = ipptr->Checksum + 0x100; /* increment checksum high byte*/ ipptr->Checksum = (sum + (sum>>16)) /* add carry */ 这也是比较直接能想到的一种方法,但是会出现刚才提到的问题。于是 RFC 1624 纠正了这个问题: Although this equation appears to work, there are boundary conditions under which it produces a result which differs from the one obtained by checksum computation from scratch.

Read More

每周分享第 25 期

关掉各种 Intel BUG 的 mitigation https://t.me/one_real_world/1517 GCC 10 支持用 MMX 模拟 SSE http://www.phoronix.com/scan.php?page=news_item&px=GCC-10-Emulating-MMX-With-SSE Minecraft Earth 发布 https://www.minecraft.net/en-us/article/new-game--minecraft-earth# Python 官方 format 工具 https://github.com/python/black JS Binary AST Proposal https://github.com/tc39/proposal-binary-ast 用 Rust 实现的 ld https://github.com/aep/elfkit Verilog -> Minecraft https://github.com/itsFrank/MinecraftHDL Rust 实现的 光栅化输出 https://github.com/ecumene/rust-sloth/ Nokia 的 Rust 内存 profiler https://github.com/nokia/memory-profiler Linux 5.2 更新的 Logitech Wireless Device 驱动 正好能用上 http://www.phoronix.com/scan.php?page=news_item&px=Better-Logitech-Linux-5.2

Read More

Nginx 反代到 HTTPS 上游

这次遇到一个需求,要反代到不在内网的地址,为了保证安全,还是得上 HTTPS ,所以尝试了一下怎么给 upstream 配置自签名 HTTPS 证书的验证。 upstream subpath { server 4.3.2.1:4321; } server { listen 443 ssl; server_name test.example.com; location /abc { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_ssl_trusted_certificate /path/to/self_signed_cert.crt; proxy_ssl_name 1.2.3.4; // to override server name checking proxy_ssl_verify on; proxy_ssl_depth 2; proxy_ssl_reuse on; proxy_pass https://subpath; } } 可以用 openssl 获得自签名的 cert : echo | openssl s_client -showcerts -connect 4.3.2.1:4321 2>/dev/null | \ openssl x509 -text > /path/to/self_signed_cert.crt ref: https://stackoverflow.

Read More

每周分享第 24 期

在线波形绘制 https://wavedrom.com/editor.html Python 波形绘制 https://github.com/wallento/wavedrompy GDB 8.3 发布 http://www.phoronix.com/scan.php?page=news_item&px=GDB-8.3-Debugger-Released 命令行自动搜索 StackOverflow https://github.com/WindSoilder/hors 从 C 到 Rust 的翻译 https://github.com/immunant/c2rust SHA-1 碰撞攻击新进展 https://www.zdnet.com/article/sha-1-collision-attacks-are-now-actually-practical-and-a-looming-danger/ macOS 配置 AD 管理员组 https://derflounder.wordpress.com/2011/02/17/adding-groups-from-your-directory-service-to-your-macs-admin-group/ ssh 后自动安装并打开 code-server 同步配置 https://github.com/cdr/sshcode HTTP Sunset 头 https://tools.ietf.org/html/rfc8594

Read More

在 FPGA 上实现路由器(2)

前言 月初的时候,有了一个完整可用的路由器(上一篇系列博文),但当时测了一下速度,只有几十 Mb/s ,只要往上提就会失效,得 reset 才能继续。当时也先没管性能的事情,先把和 OS 交互的部分做了。现在又回头来做性能调优。 之前,逻辑部分的主频只有 10 MHz ,这自然不行,不提高肯定做不到千兆。于是试着把主频拉高, FIFO 加大,然后遇到了很多问题,慢慢修复了,学到了很多新知识,目前也接近千兆的水平了吧,贴图: TCP 测速: UDP 测速: 测试环境是 macOS 虚拟机外打虚拟机内,走网桥把虚拟机和一个 USB 网卡接起来,然后从另一个 USB 网卡打到路由器。 尝试 700Mb/s 接下来讲讲,在这个过程中遇到了什么问题,怎么解决的。第一个是速度过快就会挂,这肯定是丢包逻辑没写对,后来在仿真里开够了时间,于是就找到了一个 BUG ,其实就是一行的修复。接着就是提高主频,但大家也知道,CPU不能随便超频,由于各种延迟的原因,比如 Setup 时间,如果超了一个时钟周期的时间,本来应该下个周期就得到新数据的,结果到了下下周期才有,那有的状态可能就乱了,我目前遇到的也主要就是这个问题。 于是就对着 Timing 里汇报的各种问题修啊修,发现了很多以前没有注意到的问题,它们不影响功能,但是会让逻辑变慢。第一个问题是 High Fanout ,以上就是说一个输出接到了很多输入,这看起来没啥问题,但数设课上也讲过,每个门的输入输出电流是有限制的,例如按书上的数据,一个门输出只能带十个门,更多只能级联一层。级联的话,延迟自然就高了。后来发现,这里的原因是,开了一个大的数组,但是没有变成 RAM ,综合出了几千个逻辑单元,自然是出问题。解决方法很简单,用 xpm_memory_tpdram 即可。这样一搞,主频就能上 200MHz 了。 这个时候测了一下,发现 UDP 能打到 700Mb/s 了,TCP 由于丢包率比较高,只有 400Mb/s ,距离预期还有一段距离。于是继续进行优化。 向 900Mb/s 进发 要继续提速,自然要提高主频。下一个主频目标就是 250MHz 。随着提高主频,时序的要求也会更高,自然也出现了新的问题。 这次的问题主要在于,一个路径上逻辑门数过多,多的有7到10个,每一步零点几到一点几纳秒,叠起来4纳秒哪里够用。于是把一些不需要依赖条件的逻辑挪到条件外面,这样就减少了一些路径的依赖。 解决了这个以后,现在的 WNS (Worst Negative Slack)只剩下 0.6 ns 了。这时候的问题一部分还是来自于逻辑门过多,但这个时候就没这么简单了,只能继续细化流水线,打一拍,这样才能把延迟降下来。 虽然 Timing 没有完全解决,但还是写进了 FPGA 中。幸好工作一切正常,就得到了上面那个图片的结果,接近千兆的速度了。

Read More

每周分享第 23 期

VS Code Remote https://code.visualstudio.com/blogs/2019/05/02/remote-development Rust 命令行看图片 https://github.com/atanunq/viu GCC 9.1 发布 https://readhacker.news/s/42Ruk Rust 的 Unikernel https://github.com/hermitcore/libhermit-rs Rust 的 QRCode 库 https://www.reddit.com/r/rust/comments/bk7z2x/announcing_bardecoder_a_qr_detector_and_decoder/ 鲁迅说过搜索引擎 http://cx.luxunmuseum.com.cn/ JIT 的 golang REPL https://github.com/gijit/gi Github 私有 Package Repo https://github.com/features/package-registry

Read More

给 Rocket Chip 挂接串口外设

前沿 最近在给 rCore 添加 Rocket Chip 支持。下面讲讲最近做了哪些工作,遇到了哪些坑,都是怎么解决的。 踩坑过程 Rocket Chip 运行代码 首先分析了一下已有的代码和工作方式,这个 Rocket Chip (ucb-bar/fpga-zynq)的设计大概是这样的:在 PS 上通过 fesvr 向 Rocket Chip 写入程序。Rocket Chip 本身暴露出一个 TSI ,一个串口的调试接口,通过 Zynq Adapter 挂到了 PS 下的 AXI 总线,暴露出若干个寄存器,大概如下: /** * Address Map * 0x00 - serial out FIFO data * 0x04 - serial out FIFO data available (words) * 0x08 - serial in FIFO data * 0x0C - serial in FIFO space available (words) * 0x10 - system reset * 0x20 - req FIFO data * 0x24 - req FIFO data available (words) * 0x28 - data FIFO data * 0x2C - data FIFO data available (words) * 0x30 - resp FIFO data * 0x34 - resp FIFO space available (words) * 0x38 - nsectors * 0x3C - max request length */ 前面的是调试接口,后面的是 block device 和 network ,我们暂时还没有用到这些 UCB BAR 做的私货。在 Vivado 中,地址 Offset 是 0x43C00000 ,所以代码中就这样访问对应的物理地址:

Read More