从零开始学RISC:第十二篇
乘法器
非常简单。
1234567module MUL( input logic [31:0] src1, input logic [31:0] src2, output logic [63:0] out) assign out = src1 * src2;endmodule
结束。
我们肯定不能这样子实现乘法器——即使这种写法能被综合为DSP模块。为什么?因为乘法实在是太太太长了,时间太久。如果将这么一大坨逻辑塞在EX级,时钟频率一定会非常难看。
怎么办?我们将乘法拆分开,在每个时钟周期实现一部分。这样子,可以稍微改善一点时序。
四级流水线乘法器 MUL.sv
首先思考一下:多周期的流水线乘法器会带来哪些额外的时序控制与竞争冒险?
首先是模块要给出信号,来表示自己“是否完成当前运算”以及“是否能接受新的运算”。此外,在执行时,需要将乘法指令用到的寄存器与写回的目标寄存器记住,否则当指令在旁流水线的乘法模块执行时,结果尚未算出,但后面一条指令需要用到结果,这样就必须阻塞流水线。再如,计算后写回时,如果写回的目标寄存器与目前执行完毕的指令写回寄存器一致,则应 ...
从零开始学RISC:第十一篇
存取控制模块:再进化
在 从零开始学RISC:第十篇 中,设计了LoadStoreUnit来处理各类存取指令,但将同一个模块复用了两遍。这样会带来额外的逻辑开销。
最好的办法是将其拆分为两个模块:一个放在MEM级,负责存储;另一个放在WB级,负责读取。
存储控制模块 StoreUnit.sv
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576// StoreUnit模块 - 专门用于MEM级的Store操作// 负责处理SB/SH/SW的字节对齐和写使能生成module StoreUnit ( input logic [ 3:0] sl_type, // 存取类型 input logic [31:0] addr, // 地址(用于计算字节偏移) input logic [31:0] store_dat ...
2025年度回顾
2025年的最后一天,回顾一下这一年吧。
目录
1月
终于考完研了!好好放松一下!岩神启动~
毕设先研究一下吧。当然,玩好才是最重要的!(^_−)☆
和好朋友们过生日、打麻将、鹅鸭杀
光速推完《星之少女与六华的姐妹》。艺术风格不错,奈何剧情实在不太行。
2月
出发去哈尔滨!南方大土豆的雪国之旅。志愿者很不容易啊。
推完《星之终途》,Key的小短篇很精致。
3月
初试顺利通过了!全力冲刺复试。半导体物理好难啊 >_<
推完《近月少女的礼仪》,露娜Sama带我走吧
《闰跃之年》也很有意思,设计独特的跳跳乐。
来吧,高潮一战就在眼前!
4月
考完试了可就要让我休息休息了。
推完《少女理论及其周边》,梅丽尔可爱捏~
推完《月影魅像—解放之羽》,抛开立绘不谈,妹药的作品还是一如既往的对味。
推完《9-nine-》系列,超赞的剧本与系统设计。META元素很有特色,后劲 很 大。妹妹是天!
还和好基友一起打通了《双影奇境》。我玩过的最棒的双人合作游戏。
结果给室友的拯救者清灰时把电脑搞坏了,开不了机 TuT 还好我买了新电脑,可以给他用新的。
毕设自然也没落下,把Klippe ...
TCP、UDP与拥塞控制算法
前言
在 Windows11开启BBR2拥塞控制算法 中,将Windows的TCP拥塞控制算法从CUBIC换成了BBR2。TCP 拥塞控制到底在控制什么?这些算法有什么不同?
事实上,TCP一直在问自己:我应该同时在网络里“放多少数据”才不会把路堵死?一个名叫“拥塞窗口”(Congestion Window)的参数被设定,用于限制发送端能发送的最大数据量。大的cwnd会使得吞吐更高,但也更容易造成排队和丢包;更小的cwnd则会使得延迟更低,但是链路可能跑不满。
拥塞控制算法的本质,其实就是如何动态调整cwnd。如果看起来网络很空,那就增大一些cwnd,使得吞吐量提高;网络如果太“拥堵”,那就减小一点cwnd,使得延迟降低一点。
CUBIC:很平庸,也很稳
CUBIC像是“先加速、再减速、再加速”,目标是让 cwnd 在大窗口区间增长更快。
CUBIC 用一个三次函数来控制 cwnd 变化:
丢包后,cwnd 会降到某个值
然后增长会逐渐加速
接近上一次发生丢包时的窗口(Wmax)附近,增长会变慢(更谨慎)
超过 Wmax 后,再加速探测更高带宽
那么代价是什么呢?CUBIC是Los ...
自用音乐音量均衡脚本
前言
不同歌曲的音量大小不同,如果直接导入游戏做自定义电台的话很容易出现音乐时大时小的问题。因此,需要先将音量均衡。
傻瓜脚本
需要安装好ffmpeg且添加到PATH
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798import jsonimport reimport subprocessfrom pathlib import PathTARGET_I = -14TARGET_TP = -1.5TARGET_LRA = 12FFMPEG = "ffmpeg" # 或写 "ffmpeg.exe"def run(cmd): # ✅ 强制按 UTF-8 解码,避免 GBK 解码炸裂 p = subprocess.run ...
Windows11开启BBR2拥塞控制算法
前言
群里吹水时看到群友讨论相同配置的Windows网速和Linux网速哪个更快,得出结论是Linux获胜,毕竟Linux的TCP拥塞控制算法更好,采用的是BBR算法,而Windows一直是老古董CUBIC。
不过,在Windows11 22H2之后,也可以为TCP开启BBR2的拥塞控制算法了——尽管有一些小小的问题。
开启BBR2
首先查看自己电脑的默认控制算法:
123456789101112╭── PowerShell False 0ms [21:21]╰─ ~❯ Get-NetTCPSetting | Select SettingName, CongestionProviderSettingName CongestionProvider----------- ------------------AutomaticInternetCustom CUBICDatacenterCustom CUBICCompat NewRenoDatacenter CUBICIntern ...
最棒的VCD波形查看器:Surfer
前言
之前 折腾了半天GTKWave,还是调不好。高分辨率屏幕看没有优化缩放的波形,眼睛都要瞎了。
直到群友给我推荐了Surfer。居然还有在线Demo:Surfer Online
使用效果
可以看到,界面字体全部采用等宽字体,看信号也更加方便,不会IIlIlIIlIl分不清。
你甚至可以参考官方文档,用现成提供的指令集解码器,亦或是自己写,从而解析指令码,这样子就不用在内部引出ASCII:
这玩意甚至还有VSCode插件,虽然是WASM编译版。
真要挑它的缺点,可能就一个不支持模拟波形吧。还好做数字方向的看不到这些,哈哈。
自编译
源代码托管在Gitlab上,我拉了一份到自己的库,并对RV32格式译码器做了修改,将其寄存器显示修改为x0/x1这种,便于查看。
从零开始学RISC:第十篇
让我访问!
对于L-Type和S-Type,我们只支持了lw/sw,它们的兄弟姐妹lb/lbu/lh/lhu/sb/sh还没支持。
我们需要准备一个模块专门用来处理这些非字节存取。在前面ID级传出的sl_type信号终于能派上用场了。
这里有个小技巧:定义存取类型时,用高位来分辨是读还是写,剩下的读写类型一定程度上可以合并。
存取控制模块 LoadStoreUnit.sv
考虑到数据的存取都在MEM级完成,我们需要将模块放置在MEM级。我们要传入来自EX级的待存数据,对其进行适当移位,好将数据存放到合适位置,并根据dram_we和sl_type来将1位写使能dram_we转换为4位的按位写使能dram_we_strbe。
对于读出的数据,还要判断是有符号还是无符号读取。
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848 ...
从零开始学RISC:第九篇
综合器说好,那才是真好
模块综合
写出的HDL代码需要送入综合器中,生成实际的门电路。综合器会在这个过程中推测你写的代码,并帮你进行一定的优化,最终生成文件。
这里选择开源的Yosys进行综合。官方没有发布适用于Windows的二进制可执行文件,但是Python的WASM库有,虽然是给WebAssembly环境准备的,需要电脑上有wasmtime才行。编写一个综合脚本:
1234567891011121314151617181920212223242526272829303132333435363738# 读取所有模块(不需要 -formal)read_slang ./user/src/include/defines.svhread_slang ./user/src/PC.svread_slang ./user/src/IROM.svread_slang ./user/src/PR_IF_ID.svread_slang ./user/src/Decoder.svread_slang ./user/src/imm_extender.svread_slang ./user/src/Regi ...
从零开始学RISC:第八篇
前递:快人一步
激烈的斗争
我们做完了吗?还没完。
在 之前 的测试里,程序没有任何数据冒险——在实际运行中这几乎是不可能的。我们简单修改一点:
1234567addi x1,x0,1addi x2,x0,2addi x3,x0,3addi x4,x0,4addi x1,x0,7addi x2,x0,21add x7,x1,x2 # 加入数据冒险
然后运行。发生了什么?我们期望看到x7输出的是x1和x2变化后相加的值,应当为28。但是计算结果输出了错误的3!
这就是最经典的“数据冒险”。我们看看发生了什么。
123456789 x1 x2 x7 IF ID EX MEM WB-------------------------------------------------------------------------------1 1 2 x x1=72 1 2 x x2=21 x1=73 1 2 x ...



![[忆语游心] 生化危机4-重制版](https://webpn.esing.dev/img/re4_0_260502_1947_8tvn.jpg)

