一个懂 SystemVerilog、会拼位宽、能显示
12'hABC的程序员计算器。开源了。
做芯片这行,日常工作里少不了跟进制、位宽、bit mask 打交道。读 AXI 总线地址、手算寄存器域段、验证 CRC 结果——随时都要切进制、移位、拼位宽。
这么多年,我用过的计算器也不少了。Windows 自带的、各种 APP、甚至命令行里的——要么只能十进制,要么把 HEX/BIN 当附加功能草草了事。每次从波形里看到一个值,想算一下它的十进制偏移量,还得先手动记下"这个信号是 12 位的"。
没有一个是从硬件工程师的视角来设计的。所以我自己写了一个。叫 BinCalcX。
下面详细介绍一下它的功能,以及一些平时你可能不会注意到的使用小技巧。
一、SystemVerilog 字面量显示
打开 BinCalcX,最先看到的不是按钮,是右边的四行 RPN 堆栈寄存器:
T 64'h0000_0000_0000_0000
Z 64'h0000_0000_0000_0000
Y 64'h0000_0000_0000_0000
X 12'hABC
每一位都带着 位宽前缀和进制标识:12'hABC、8'b1101_0101、32'd1000。写 Verilog / SystemVerilog 的人再熟悉不过了——直接复制粘贴进代码,不需要自己加前缀。
HEX 显示还会按 4 位一组自动加空格(0000 0000 0000 0000),BIN 显示按 8 位一组加空格(byte 对齐),大位宽下也不会看花眼。
这和"在普通计算器里切到 HEX、看到一个孤零零的 ABC、然后在心里默念’这是 12 位的’"——是完全不同的体验。
技巧 1:选中 HEX / DEC / OCT 任意一个字段里的文字,直接
Ctrl+C就能复制对应进制的数值。如果是 BIN 模式,复制的是不带空格的完整二进制串。如果 bit grid 上有拖拽选中的位域,Ctrl+C复制的是这个 slice 的值——在写#define GPIO_MODE_MASK 0x...的时候极其方便。
技巧 2:
Ctrl+V粘贴时,支持 C 风格前缀(0x、0b、0o),也支持在数字中间加分隔符——0xDEAD_BEEF和0b1010_0101都能正确解析。还支持开头带+/-符号,粘贴一个负数会自动转成当前位宽的补码。从代码里复制一个常量过来,什么都不用改。
二、逐寄存器位宽 & 硬件操作
这是 BinCalcX 跟所有其他计算器最根本的区别:每一个寄存器(T / Z / Y / X)都有自己独立的位宽,范围 1 到 64。
为什么这很重要?
硬件里,不同信号的位宽是不一样的。一个 12-bit 的 address 和一个 4-bit 的 burst length 拼接在一起,是一条 16-bit 的总线。传统计算器只能记住一个全局位宽,一旦你切换去算另一个域段,之前那个位宽信息就丢了。
BinCalcX 里,X 寄存器位宽是 12,Y 寄存器位宽是 4,然后做 concatenation:
X = 12'hABC (宽度 12)
Y = 4'h3 (宽度 4)
→ 按 {,}(concatenate)
X = 16'h3ABC (宽度 16,结果位宽 = Y宽度 + X宽度)
{,} 操作符就是 SystemVerilog 里的 {Y, X},自动按各自位宽拼接。还有一个 {N} 复制操作:
X = 8'hFF (宽度 8)
Y = 32'd4 (Y 的值作为重复次数)
→ 按 {N}(replicate)
X = 32'hFFFF_FFFF (宽度 32,X 被复制了 4 次)
写 testbench 生成全 1 的 mask、或者给一个 field 填充重复 pattern 时,这俩操作省掉了大量手工计算。
三个改变 X 位宽的方式
- 顶部的位宽按钮 W8 / W16 / W32 / W64:这是最常用的,一下就切到常见总线位宽
- 在 bit grid 上拖拽选取一段位域,然后按
{,}或{N}:选区先被提取为 X(宽度 =hi - lo + 1,值 shift down 到 LSB 对齐),然后执行拼接/复制操作 - {,} 拼接操作的结果位宽:等于两个寄存器的位宽之和
技巧 3:W8/W16/W32/W64 按钮只改变 X 的位宽,不会影响 Y、Z、T。这在做"先把一个 field 提取出来(X=12-bit),再跟另一个 field 拼接(Y=4-bit)“这种多步操作时非常重要——你不会不小心把 Y 的位宽也重置了。
技巧 4:输入数字时,如果超过当前位宽能表示的最大值,按键会被静默拒绝——不会 wrap 回 0,不会截断。比如 8-bit 下你狂按
9,超过 255 之后再按一个数字都输不进去。这防止了手滑多输导致的值错乱。
技巧 5:做完
{,}或{N}之后,X 的位宽可能变得很窄(比如拼接结果是 16 位)。此时如果你开始输入新数字,X 的位宽会自动恢复到 W 选择器的值(比如 64)——新输入用大位宽,不受上一次窄结果的影响。
技巧 6:bit grid 上有拖拽选区时,直接按
{,}或{N}按钮会先把选中的位域提取为 X(覆盖 X 的值和位宽),然后再执行拼接/复制操作。这是一个快捷操作:比如先拖选 address 的[11:0],按一下{,},选中的 12-bit field 就作为新的 X 跟 Y 拼在一起了——不需要先 Enter 再操作。
技巧 7:CLx 清除 X 但保留它的位宽;CLR 把所有四个寄存器清零,位宽重置为顶部 W 选择器当前设置的值(默认 64)。写完一个域段的计算、要开始下一组完全无关的计算时,用 CLR 清干净。
三、64 格 Bit Grid——电子草稿纸
顶部的 bit grid 是 4 行 × 16 列,一共 64 个格子,覆盖 64-bit 的全部位。
设计决策:格子里不写 0/1
传统 bit 显示在每个格子里写 0 或 1,但 64 个格子挤在一起时,数字会变得很小,而且 0 和 1 视觉上差别不大,扫一眼很难看清 pattern。
BinCalcX 的方案是格子内不填文字,纯靠明暗:亮起来的格子(bit=1)用当前进制对应的主题色填充,暗的格子(bit=0)是一个淡灰色边框。眼睛扫过去,亮色的分布就是 bit pattern——跟看逻辑分析仪或者波形图的思路一致。
颜色锚定:切换进制,一看就知道在哪
每种进制有一套独立的主题色,不是只在标签上变色——整个界面里跟"当前活跃数据"相关的东西全部统一着色:
| 进制 | 主题色 | 着色的元素 |
|---|---|---|
| HEX | 绿色 | HEX 字段、bit grid 亮格、ENTER 键、X 寄存器 |
| DEC | 蓝色 | DEC 字段、bit grid 亮格、ENTER 键、X 寄存器 |
| OCT | 琥珀色 | OCT 字段、bit grid 亮格、ENTER 键、X 寄存器 |
| BIN | 紫色 | bit grid 亮格、ENTER 键、X 寄存器 |
你在 HEX 下操作,整个界面的"信号色"就是绿的;切到 DEC,一切变成蓝的——视觉上不可能搞混当前进制。这个设计借鉴了示波器上不同通道的颜色逻辑。
Nibble 分组 & Byte 边界
每 4 个 bit 之间有一个 6px 的额外间距,每 8 个 bit(一个 byte)用交替的底色阴影区分。这样扫一眼就能定位到一个具体的 byte 或者 nibble。
顶部还有灰色的 nibble 轴标签——63、59、55、51……标记了每个 nibble 的最高位——方便做域段提取时数 bit 位置。
超出位宽的格子是灰的
64 个格子始终全部显示,但超出当前 X 寄存器位宽的格子会灰色淡出。这像一个硬件截断的视觉提示:当前只关心低 12 位,高 52 位不存在。
技巧 8:鼠标悬浮在任意格子上,底部状态栏显示
Bit: 37 | Mask: 0x0000_0020_0000_0000。不用心算 1 « 37 等于多少,直接复制 mask。状态信息会在鼠标移开后 2.2 秒自动清除,不会常驻占用空间。
技巧 9:拖拽选取(按下鼠标左键在 grid 上滑动)是最常用的操作之一。选中一段位域后:
- 状态栏实时显示 slice 的 DEC / HEX / BIN 值(如
Bits 15..8 = 255 0xFF 0b11111111)- X 寄存器同步显示这个 slice 的 SV 字面量(如
8'hFF)Ctrl+C复制这个 slice 的值(当前进制格式)Enter把它 push 到 Y(X 不变)Esc取消选择 这对应的就是日常里"看一眼寄存器的[15:8]域段是什么值”——拖一下,数字就出来了。
技巧 10:Bit grid 支持全键盘操作。方向键移动焦点环(一个高亮轮廓),空格键翻转当前聚焦的 bit。调一个
#define MASK (1 << 7)的时候,键盘按几下方向键再按空格——比鼠标点快得多。
技巧 11:连续两次
Esc:第一次取消 bit grid 上的选区(如果有的话),第二次才是 CLR 全部清零。这个防止了误操作——你正在看一个 slice,不小心碰到 Esc,不会把整个栈清掉。
技巧 12:在 grid 空白区域(格子之间的缝隙、边距)点击左键,也会取消当前选区——鼠标党不想伸手去按 Esc 的快捷操作。
技巧 13:bit grid 上的选区不受切换进制影响——你在 HEX 下拖选了
[15:8],切到 BIN 下这些格子仍然高亮。选区锁定的是 bit 位置,不是数值。
四、四个进制同时可见
Bit grid 下方一行排开三个活跃显示字段:HEX、OCT、DEC,加上最右边的 CHR(ASCII 字符解码),旁边还有 Signed 模式复选框。
当前激活的进制字段高亮(带主题色背景),但三个进制的值同时可见,不需要来回切换去对照。在 HEX 下输入一个地址,DEC 和 OCT 的等效值就在旁边。
CHR 字段
固定显示 8 个字符,对应 64-bit 从高到低每一个字节的 ASCII 解码。可打印字符直接显示,不可打印的(NUL、控制字符等)显示为居中的 ·。字段宽度锁定,不会因为内容变化而跳动错位。
技巧 14:写 C 的字符串常量或者调 UART 输出时,CHR 字段直接告诉你这个 64-bit 值对应的 8 个字符是什么——ASCII table 不用查了。
技巧 15:Signed 模式是纯显示层的功能——它不影响内部存储的值,只改变显示时的符号解释。同一个
0xFF,Unsigned 下显示255,Signed、位宽 8 下显示-1。这个开关可以随时切换,不影响计算。
五、RPN 堆栈——习惯了就回不去
BinCalcX 采用 HP 计算器风格的 RPN(逆波兰)逻辑,4 级堆栈 T·Z·Y·X。
RPN 怎么用?
没有等号键。输入一个数字,按 Enter 把它 push 上栈,再输入第二个数,然后按操作符:
12 ENTER 5 + → 17 (计算 12+5)
FF XOR 0F → F0 (在 HEX 下,直接对 X 和输入值做 XOR)
200 ENTER 100 + (位宽 8) → 44 (300 & 0xFF = 44,自动 mask)
为什么用 RPN?
做了硬件验证的同事对 stack 操作有天生的亲切感——这不就是 push/pop 吗。RPN 最大的好处:
- 不需要括号:
(3+5)*(7-2)在 RPN 里是3 ENTER 5 + 7 ENTER 2 - *,中间结果一直在栈上 - 中间结果可见:每一步的结果都在 X 寄存器里,T/Z/Y 三级栈保留之前的上下文
- 跟硬件思维一致:你推一个值上去,做操作,结果落在 X——跟 ALU 的数据流是一个方向
栈操作一览
| 操作 | 按键 / 快捷键 | 效果 |
|---|---|---|
| Enter | Enter 键或按钮 | 复制 X 到 Y,原 Y→Z,原 Z→T,原 T 丢弃 |
| Roll Down (Rv) | R 键 |
栈循环下移:T→Z, Z→Y, Y→X, X→T |
| Swap (X↔Y) | S 键 |
交换 X 和 Y,各自的位宽也交换 |
| CLx | Delete 键 | X 清零,保留位宽 |
| CLR | Esc | 全部清零,位宽重置为 W 选择器当前位宽 |
技巧 16:在 BIT 模式下,界面没有输入字段——所有输入通过 bit grid 点击或键盘来进行。此时
Ctrl+C复制的是完整二进制串(无空格分隔)。
技巧 17:
R和S键是纯键盘操作,按一下就能在栈上轮转或交换。习惯以后,做连续多步计算时,键盘敲击节奏非常顺畅——跟用文本编辑器一样。
六、运算符 & 其他功能
算术 & 逻辑
| 操作 | 按钮 | 快捷键 | 说明 |
|---|---|---|---|
| 加/减/乘/除/模 | + - * / MOD |
对应符号键 | Y op X → X,结果位宽 = max(Y位宽, X位宽) |
| AND / OR / XOR | And Or Xor |
& | ^ |
按位逻辑,Y op X |
| NOT | Not |
~ 或 ! |
对 X 按位取反,一元操作 |
移位
有两种移位,功能不同,不要搞混:
| 操作 | 按钮 | 快捷键 | 行为 |
|---|---|---|---|
| SHL / SHR | << >> |
< > 或 ( ) |
二元移位:Y 是被移位的值,X 是移位量;Y « X |
| ◀ / ▶ | 小三角按钮 | 无(鼠标点击) | 原地单步移位:X 左移或右移 1 位,盖写 X |
技巧 18:◀ ▶ 原地移位在 Signed 模式下,右移是算术移位(符号扩展),Unsigned 模式下是逻辑移位(补零)。这和 SystemVerilog 里
>>>vs>>的语义一致。比如 8-bit Signed 下0x80(-128)按 ▶ 变成0xC0(-64),保持负数。注意左移 ◀ 永远是逻辑移位(直接截断高位),不受 Signed 影响——0x80 << 1在 8-bit 下永远是0x00,不像硬件里的带符号左移。
关于二元移位 SHL/SHR:
<<>>是把 Y 移 X 位,结果位宽保持 Y 的位宽。如果移位量 X ≥ Y 的位宽,结果直接为 0(移位量被 Y 的宽度 clamp,不会做超宽移位)。
关于除法和取模:除 0 或模 0 不会报错,结果直接返回 0。不是 bug,是刻意为之——验证环境里除数偶尔为 0 是正常情况,弹个报错还不如静默返回 0 然后继续算。
其他操作
| 操作 | 快捷键 | 说明 |
|---|---|---|
| Negate (+/-) | N 或 _ |
对 X 做二进制补码取反,在位宽内 |
| Backspace | Backspace | 删除最后输入的一位数字 |
七、键盘快捷键完整清单
所有操作都能在键盘上完成,手不用移开:
| 按键 | 功能 |
|---|---|
0–9, A–F |
输入数字(BIN 模式下只接受 0/1) |
+ - * / % |
算术运算 |
& | ^ |
AND / OR / XOR |
~ 或 ! |
NOT |
< ( |
左移(SHL) |
> ) |
右移(SHR) |
R |
Roll Down |
S |
Swap X↔Y |
N 或 _ |
Negate |
| Enter | ENTER(或有选区时 push slice→Y) |
| Backspace | 删除一位 |
| Esc | 取消选区 / 全清(CLR) |
| Delete | CLx |
← ↑ ↓ → |
在 bit grid 上移动焦点 |
| Space | 翻转焦点 bit |
Ctrl+C |
复制(当前值或选区) |
Ctrl+V |
粘贴(支持 0x/0b/0o 前缀) |
Ctrl+T |
切换置顶模式 |
八、主题 & 外观
- 深色主题:底色
#1E1E1E,和 VS Code 默认主题完全一致。写 RTL 的时候开在编辑器旁边,不会有任何违和感。 - 浅色主题:写文档需要截图时一键切过去。
- 等宽字体:所有数字和标签用 monospace,多行数值严格对齐。
- 紧凑布局:窗口尺寸做了上限(大约 484×444),常年挂在角落不占屏。
Ctrl+T置顶:看 Verdi/Debussy 波形时让计算器浮在最上面,不用来回 Alt+Tab。- 偏好持久化:主题、进制、位宽、Signed 模式、置顶状态——关掉重开全部自动恢复。
九、开源
- 基于 Qt 6 (Widgets) + C++17,MVC 架构,代码量不大,结构清楚
- Windows 下载 zip 解压即用
- Linux
qmake6 && make -j - MIT 协议,随便用,随便改
GitHub:github.com/himingway/BinCalcX
做芯片设计这么多年,一直在用别人写的工具。有些很好用,有些凑合用,但始终缺一个真正从硬件视角出发的计算器。于是自己动手写了一个。现在把它开源出来,希望能帮到有同样需求的同行。
欢迎 Star ⭐,也欢迎提 issue 和 PR。