<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://qiujiandong.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://qiujiandong.github.io/" rel="alternate" type="text/html" /><updated>2026-04-23T12:54:20+00:00</updated><id>https://qiujiandong.github.io/feed.xml</id><title type="html">Jiandong Qiu</title><subtitle>Jiandong&apos;s personal website</subtitle><author><name>Jiandong Qiu</name><email>qiujiandong1998@gmail.com</email><uri>https://qiujiandong.github.io</uri></author><entry><title type="html">RISC-V Vector优化Radix-2 FFT</title><link href="https://qiujiandong.github.io/risc-v/rvv-fft/" rel="alternate" type="text/html" title="RISC-V Vector优化Radix-2 FFT" /><published>2025-10-27T17:29:30+00:00</published><updated>2025-10-27T17:29:30+00:00</updated><id>https://qiujiandong.github.io/risc-v/rvv-fft</id><content type="html" xml:base="https://qiujiandong.github.io/risc-v/rvv-fft/"><![CDATA[<p>利用RVV优化FFT计算, 采用Radix-2方案.</p>

<h2 id="fft基础">FFT基础</h2>

<p>离散傅里叶变换：</p>

\[X[k] = \sum\limits_{n = 0}^{N - 1} {x[n]{e^{ - j{2\pi nk} \over N}}
= \sum\limits_{n = 0}^{N - 1} {x[n]W_N^{nk}} }\]

<p>快速傅里叶变换(FFT)常见的可以分为DIT/DIF，即按时域分解或者按频域分解。以按频域分解为例：</p>

<p><img src="/images/posts/rvv-fft/fft_dif.svg" alt="fft_dif" class="align-center" /></p>

<p>写出偶数频率项和奇数频率项：</p>

\[X[2r] = \sum\limits_{n = 0}^{N - 1} {x[n]W_N^{2nr}} =
\sum\limits_{n = 0}^{\frac{N}{2} - 1} {x[n]W_N^{2rn}} +
\sum\limits_{n = 0}^{\frac{N}{2} - 1}
{x[n + \frac{N}{2}]W_N^{2r[n + \frac{N}{2}]}} =
\sum\limits_{n = 0}^{\frac{N}{2} - 1} {(x[n] + x[n + \frac{N}{2}])W_{\frac{N}{2}}^{rn}}\]

\[X[2r + 1] = \sum\limits_{n = 0}^{\frac{N}{2} - 1}
{(x[n] - x[n + \frac{N}{2}])W_N^nW_{\frac{N}{2}}^{rn}}\]

<p>从这两个表达式来看，长度为N的DFT可以分解成两个长度为N/2的DFT.</p>

<p>而且从分解的形式看，每次都是将偶数部分和奇数部分拆分开,
结果自然就不会是连续的, 而是形成位倒序的排列,
由此也可以理解位倒序正是和radix-2息息相关，
如果是radix-3或者radix-4, 输出的顺序就不完全是位倒序了。</p>

<h2 id="向量化处理">向量化处理</h2>

<p>可以看到DIF FFT的计算中每个stage的FFT长度是逐级减小的,
这不太利于发挥SIMD指令的性能, 因此略微调整数据的摆放位置,
可以使得中间的计算结果都能连续存放。</p>

<p>原先的DIF FFT的计算过程有一个好处是可以原位计算,
但是经过如下的调整后, 就不能原位计算了, 但是可以实现中间每层的计算都以N/2的向量长度处理。</p>

<p><img src="/images/posts/rvv-fft/fft_rvv.svg" alt="fft_rvv" class="align-center" /></p>

<h2 id="实数fft">实数FFT</h2>

<p>先说结论，N点的实数FFT只两个N/4长度的序列就可以算出N/2点结果，而且因为完整的结果是共轭对称的,
所以一般剩下N/2点就不用写了。</p>

<h3 id="正变换">正变换</h3>

<p>实数序列x[n], n=0, 1, 2, …, N-1，可以根据索引值的奇偶性进行分组。</p>

<p>偶数索引值的数组成一个新的实数序列f[u]，u=0, 1, 2, …, N/2-1</p>

\[f[u] = x[2u]\]

<p>x[n]中奇数索引值的数组成新的实数序列g[u]，长度为N/2。</p>

\[g[u] = x[2u+1]\]

<p>把x[n]看作一个复数序列y[u]，偶数索引值的数作为实部，奇数索引值的数作为虚部。</p>

\[y[u] = f[u] + jg[u]\]

<p>两边都做离散傅里叶变换，根据线性性质可以得到：</p>

\[Y[r] = F[r] + jG[r]\]

<p>实数的离散傅里叶变换具有共轭对称性。</p>

\[X[r] = \overline {X[N - r]}\]

<p>只需要把公式代入下面的分析式中就可以轻松验证</p>

\[X[r] = \sum\limits_{n = 0}^{N - 1} {x[n]{e^{ -j2\pi \frac{rn}{N}}}}\]

<p>$F[r]$是$f[u]$的离散傅里叶变换结果，$G[r]$是$g[u]$的离散傅里叶变换结果,
$Y[r]$是$y[u]$的离散傅里叶变换结果。因为$f[u]$, $g[u]$是实数，所以：</p>

\[\overline {F[N/2 - r]}  = F[r]\]

\[\overline {G[N/2 - r]}  = G[r]\]

<p>根据这一信息，可以把$r=N/2-r$代入$Y[r]$中：</p>

\[\overline {Y[N/2 - r]}  = \overline {F[N/2 - r]}  +
\overline {jG[N/2 - r]}  = F[r] - jG[r]\]

<p>再联立两个方程，可以用$Y[r]$和$\overline {Y[N/2 - r]} $来表示$F[r]$和$G[r]$</p>

\[F[r] = {1 \over 2}\left( {Y[r] + \overline {Y[N/2 - r]} } \right)\]

\[G[r] = {j \over 2}\left( {\overline {Y[N/2 - r]}  - Y[r]} \right)\]

<p>到这里，把x[n]（N点实数）看作一个复数序列y[u]（N/2点复数）后, 可以计算y[u]的离散傅里叶变换，并且可以推算出x[n]中偶数索引的数组成的序列的离散傅里叶变换结果$F[r]$和奇数索引的数组成的序列的离散傅里叶变换结果$G[r]$。</p>

<p>再考虑到用DIT分解计算x[n]的离散傅里叶变换时，第一步就是把x[n]分成两个奇偶子序列，</p>

\[X[r] = \sum\limits_{n = 0}^{N/2 - 1} {x[2n]{e^{ - j2\pi {r2n} \over N}}}  +
\sum\limits_{n = 0}^{N/2 - 1} {x[2n + 1]{e^{ - j2\pi {r(2n + 1)} \over N}}}\]

\[X[r] = F[r] + \omega _N^rG[r],\omega _N^r = {e^{ - j2\pi {r \over {N/2}}}}\]

<p>这样就可以计算出x[r]的前N/2点的复数结果.</p>

<p>但是当$r=N/2$时，代进去会发现$X[N/2] = \overline {X[N/2]} $，只能知道X[N/2]处也是一个实数，这个奈奎斯特频点的值需要进一步推导：</p>

<p>N/2点的离散傅里叶变换具有周期性，即$F[r]=F[r+N/2]$, $G[r]=G[r+N/2]$</p>

\[X[r + N/2] = F[r + N/2] + \omega _N^{r + N/2}G[r + N/2] = F[r] - \omega _N^rG[r]\]

<p>所以：</p>

\[X[N/2] = F[0] - G[0]\]

<p>再结合$X[r]$的共轭对称性，可以得到</p>

\[X[r + N/2] = \overline {X[N/2 - r]} =
F[N/2 - r] - \omega _N^rG[N/2 -r] =
F[r] - \omega _N^rG[r]\]

<p>总结一下计算步骤, 先把实数序列当做复数序列算cfft $Y[r]$, 然后根据$Y[r]$ 计算出$F[r]$和$G[r]$,
再利用N/4长度的$F[r]$和$G[r]$计算$X[r]$, 其中$X[0]$和$X[N/2]$另外单独算.</p>

\[Y[r] = F[r] + jG[r]\]

\[F[r] = {1 \over 2}\left( {Y[r] + \overline {Y[N/2 - r]} } \right)\]

\[G[r] = {j \over 2}\left( {\overline {Y[N/2 - r]}  - Y[r]} \right)\]

\[X[r] = F[r] + \omega _N^rG[r]\]

\[X[N/2 - r] = \overline {F[r] - \omega _N^rG[r]}\]

<h3 id="逆变换">逆变换</h3>

<p>如果同样想用复数IFFT来计算实数IFFT（经过IFFT计算后结果为实数），这是一个相反的过程。</p>

\[Y[r] = F[r] + jG[r]\]

<p>目前已知$X[r]$，只需要用$X[r]$表示出$F[r]$和$G[r]$，就可以用IFFT计算出$Y[r]$了。</p>

\[X[r] = F[r] + \omega _N^rG[r]\]

\[X[r + N/2] = F[r] - \omega _N^rG[r]\]

<p>联立上面两个式子可以得到：</p>

\[F[r] = {1 \over 2}\left( {X[r] + X[r + N/2]} \right)\]

\[G[r] = \frac{\omega _N^{ - r}}{2}\left( {X[r] - X[r + N/2]} \right)\]

<p>$X[r]$具有共轭对称性，所以:</p>

\[X[r] = \overline {X[N - r]}\]

<p>计算IFFT分为两步，首先需要将长度为（N/2 + 1）的复数序列转换为长度为N/2的复数序列。</p>

\[F[r] = {1 \over 2}\left( {X[r] + \overline {X[N/2 - r]} } \right)\]

\[G[r] = \frac{\omega _N^{ - r}}{2}\left( {X[r] - \overline {X[N/2 - r]} } \right)\]

<p>观察对称性：</p>

\[F[N/2 - r] = {1 \over 2}\left( {X[N/2 - r] + \overline {X[r]} } \right)\]

\[G[N/2 - r] = \frac{\omega _N^{ - r}}{2}\left( {X[N/2 - r] - \overline {X[r]} } \right)\]

<p>所以在计算那种知道结果是实数的IFFT的时候，是可以类似计算FFT的时候一样,
同时计算$F[r]$和$F[N/2 - r]$的, 因为他们所用到的输入数据是一样的。</p>

<p>最后，如果FFT和IFFT都不归一化，一个序列调用FFT，再调用IFFT后会增大N倍，一般都会除以归一化因子N，有的放在FFT的计算上面，有的放在IFFT的计算上面，也有的拆分成两个$1/\sqrt{N}$，分别放在FFT和IFFT上。</p>

<p>一般情况下，信号经过FFT-IFFT loop会增大N倍，而如果用N/2点计算的话，就会增大N/2倍。所以如果想要让两种计算方式的结果一致，需要在第二种方式时把结果乘以2。</p>

<h2 id="参考资料">参考资料</h2>

<ul>
  <li><a href="https://zh.wikipedia.org/wiki/%E5%BA%93%E5%88%A9-%E5%9B%BE%E5%9F%BA%E5%BF%AB%E9%80%9F%E5%82%85%E9%87%8C%E5%8F%B6%E5%8F%98%E6%8D%A2%E7%AE%97%E6%B3%95">FFT wiki</a></li>
</ul>]]></content><author><name>Jiandong Qiu</name><email>qiujiandong1998@gmail.com</email><uri>https://qiujiandong.github.io</uri></author><category term="RISC-V" /><category term="RISC-V" /><category term="FFT" /><category term="RVV" /><summary type="html"><![CDATA[利用RVV优化FFT计算, 采用Radix-2方案.]]></summary></entry><entry><title type="html">openEuler RISC-V QEMU网络配置</title><link href="https://qiujiandong.github.io/risc-v/openeuler/qemu-net/" rel="alternate" type="text/html" title="openEuler RISC-V QEMU网络配置" /><published>2025-10-25T22:14:44+00:00</published><updated>2025-10-25T22:14:44+00:00</updated><id>https://qiujiandong.github.io/risc-v/openeuler/qemu-net</id><content type="html" xml:base="https://qiujiandong.github.io/risc-v/openeuler/qemu-net/"><![CDATA[<p>记录openEuler RISC-V QEMU虚拟机连接宿主机网络的方法.</p>

<p>openEuler的虚拟机镜像使用官网提供的即可, <a href="https://docs.openeuler.org/zh/docs/25.09/server/installation_upgrade/installation/risc_v_qemu.html">openEuler RISC-V虚拟机安装文档</a></p>

<p>关于如何连接外网, 网上已经有很多文章介绍了, 比如这些参考链接:</p>

<ul>
  <li><a href="https://wiki.qemu.org/Documentation/Networking">QEMU Networking</a></li>
  <li><a href="https://wiki.qemu.org/Documentation/Networking/NAT">QEMU Networking NAT</a></li>
  <li><a href="https://en.wikibooks.org/wiki/QEMU/Networking">QEMU Networking Wiki</a></li>
  <li><a href="https://embedded.pages.openeuler.org/master/developer_guide/debug/qemu/qemu_start.html#qemu-enable-net">openEuler Embedded QEMU Networking</a></li>
</ul>

<p>但这些文章看上去配置起来也都挺麻烦的, 所以我在这里记录一下我的配置方式.</p>

<h2 id="nat模式">NAT模式</h2>

<p>Network Address Translation, 官方提供的<code class="language-plaintext highlighter-rouge">start_vm.sh</code>中默认启用的是user mode network backend,
这个模式默认不能用<code class="language-plaintext highlighter-rouge">ping</code>来测试网络的连通性. 需要在宿主机上做一些额外的设置才行.
但用<code class="language-plaintext highlighter-rouge">curl</code>来测试网络的连通性是可以的.</p>

<p>这个NAT模式的额外开销比较多, 所以网络的性能也相对较差一些.</p>

<p>通过<code class="language-plaintext highlighter-rouge">qemu-system-riscv</code>的help帮助信息可以看到有如下的配置选项</p>

<pre><code class="language-txt">-netdev user,id=str[,ipv4=on|off][,net=addr[/mask]][,host=addr]
         [,ipv6=on|off][,ipv6-net=addr[/int]][,ipv6-host=addr]
         [,restrict=on|off][,hostname=host][,dhcpstart=addr]
         [,dns=addr][,ipv6-dns=addr][,dnssearch=domain][,domainname=domain]
         [,tftp=dir][,tftp-server-name=name][,bootfile=f][,hostfwd=rule][,guestfwd=rule][,smb=dir[,smbserver=addr]]
                configure a user mode network backend with ID 'str',
                its DHCP server and optional services
</code></pre>

<h2 id="桥接模式">桥接模式</h2>

<p>桥接模式顾名思义就是用宿主机上的网桥来转发虚拟机上的网络数据包,
但这个需要在宿主机上创建网桥, 然后设置<code class="language-plaintext highlighter-rouge">iptables</code>的转发. 感觉是有点麻烦的,
不过好在qemu提供来一个<code class="language-plaintext highlighter-rouge">qemu-bridge-helper</code>来帮助我们做这个工作.</p>

<p>关于网桥, 我发现我之前安装过<code class="language-plaintext highlighter-rouge">libvirt-daemon</code>, 所以有一个<code class="language-plaintext highlighter-rouge">virbr0</code>网桥已经有了,</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ip addr show virbr0 <span class="o">&amp;&amp;</span> brctl show virbr0
</code></pre></div></div>

<pre><code class="language-txt">4: virbr0: &lt;NO-CARRIER,BROADCAST,MULTICAST,UP&gt; mtu 1500 qdisc noqueue state DOWN group default qlen 1000
    link/ether 52:54:00:18:ec:ef brd ff:ff:ff:ff:ff:ff
    inet 192.168.122.1/24 brd 192.168.122.255 scope global virbr0
       valid_lft forever preferred_lft forever
bridge name     bridge id               STP enabled     interfaces
virbr0          8000.52540018ecef       yes
</code></pre>

<p>可能是因为我之前装过<code class="language-plaintext highlighter-rouge">gnome-boxes</code>吧, 所以创建网桥的步骤我这里可以省略,
只要安装过<code class="language-plaintext highlighter-rouge">libvirt-daemon</code>并启动来<code class="language-plaintext highlighter-rouge">libvirtd</code>服务应该就有这个网桥.
在启动qemu的时候, 直接指定<code class="language-plaintext highlighter-rouge">-netdev bridge</code>, <code class="language-plaintext highlighter-rouge">br=virbr0</code>,
QEMU就会启用<code class="language-plaintext highlighter-rouge">qemu-bridge-helper</code>来帮助我们做转发工作.</p>

<pre><code class="language-txt">-netdev bridge,id=str[,br=bridge][,helper=helper]
                configure a host TAP network backend with ID 'str' that is
                connected to a bridge (default=br0)
                using the program 'helper (default=/usr/local/bin/qemu-riscv64/libexec/qemu-bridge-helper)
</code></pre>

<p>根据QEMU安装位置的不同, 还需要提供一个<code class="language-plaintext highlighter-rouge">bridge.conf</code>文件</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cat</span> /usr/local/bin/qemu-riscv64/etc/qemu/bridge.conf
allow virbr0
</code></pre></div></div>

<p>然后用的是时候还需要加上管理员权限</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>qemu-system-riscv ...
</code></pre></div></div>

<p>用这种bridge模式的话, 网络性能相对更好, 而且也可以用<code class="language-plaintext highlighter-rouge">ping</code>来测试网络的连通性了.</p>

<h2 id="更新软件包">更新软件包</h2>

<p>更新软件包的时候可能会遇到这样的问题:</p>

<pre><code class="language-txt">SSL peer certificate or SSH remote key was not OK ...
</code></pre>

<p>在更新软件包之前需要先同步一下时间. 一开始可以先手动设置一下时间:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo date</span> <span class="nt">-s</span> <span class="s2">"2025-10-25 20:30:00"</span>
</code></pre></div></div>

<p>然后在可以连网更新软件包之后再同步一下时间:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>ntpdate ntp.aliyun.com
<span class="nb">sudo </span>timedatectl set-timezone Asia/Shanghai
</code></pre></div></div>

<p>也可以按照这里的文档来设置时间, <a href="https://docs.openeuler.org/zh/docs/21.03/docs/Administration/%E5%9F%BA%E7%A1%80%E9%85%8D%E7%BD%AE.html">基础设置</a>
具体如何更新软件包可以参考<a href="https://docs.openeuler.org/zh/docs/21.03/docs/Administration/%E4%BD%BF%E7%94%A8DNF%E7%AE%A1%E7%90%86%E8%BD%AF%E4%BB%B6%E5%8C%85.html">文档</a></p>]]></content><author><name>Jiandong Qiu</name><email>qiujiandong1998@gmail.com</email><uri>https://qiujiandong.github.io</uri></author><category term="RISC-V" /><category term="openEuler" /><category term="openEuler" /><category term="QEMU" /><category term="RISC-V" /><summary type="html"><![CDATA[记录openEuler RISC-V QEMU虚拟机连接宿主机网络的方法.]]></summary></entry><entry><title type="html">K230设置自动连接wifi</title><link href="https://qiujiandong.github.io/embedded/k230-wifi/" rel="alternate" type="text/html" title="K230设置自动连接wifi" /><published>2025-10-19T20:43:42+00:00</published><updated>2025-10-19T20:43:42+00:00</updated><id>https://qiujiandong.github.io/embedded/k230-wifi</id><content type="html" xml:base="https://qiujiandong.github.io/embedded/k230-wifi/"><![CDATA[<p>按照<a href="https://www.kendryte.com/k230_linux/zh/dev/app_develop_guide/user_develop/wifi_doc.html">K230 linux WiFi使用指南</a>
设置K230开发板的wifi密码. 感觉流程可以精简一些, 实现开机自动连接Wifi.</p>

<p>第一步, 启用无线网卡. <code class="language-plaintext highlighter-rouge">ifconfig</code> 或者 <code class="language-plaintext highlighter-rouge">ip</code> 命令都可以用, <code class="language-plaintext highlighter-rouge">ifconfig</code> 是相对较老的命令</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># ifconfig wlan0 up</span>
ip <span class="nb">link set </span>wlan0 up
</code></pre></div></div>

<p>第二步, 启动<code class="language-plaintext highlighter-rouge">wpa_supplicant</code>:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wpa_supplicant <span class="nt">-B</span> <span class="nt">-i</span> wlan0 <span class="nt">-c</span> /etc/wpa_supplicant.conf
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">-B</code>的作用是后台运行, <code class="language-plaintext highlighter-rouge">-i</code>指定无线网卡, <code class="language-plaintext highlighter-rouge">-c</code>指定配置文件.
和wifi相关的配置都可以在配置文件中写好.</p>

<p>所以在第二步前, 需要先准备一下配置文件. 嘉楠提供了默认的配置文件:</p>

<pre><code class="language-txt">ctrl_interface=/var/run/wpa_supplicant
ap_scan=1

network={
  key_mgmt=NONE
}
</code></pre>

<p>这里没有设置wifi的名称和密码, 只能用来连开放的无线网络.
如果要设置wifi的名称和密码的话, 可以先:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>wpa_passphrase wifi_test 12345678
<span class="nv">network</span><span class="o">={</span>
        <span class="nv">ssid</span><span class="o">=</span><span class="s2">"wifi_test"</span>
        <span class="c">#psk="12345678"</span>
        <span class="nv">psk</span><span class="o">=</span>5c86769b524f416c47ece4465c526cf24e20ce3e565618f4a081130ae22402cc
<span class="o">}</span>
</code></pre></div></div>

<p>用<code class="language-plaintext highlighter-rouge">wpa_passphrase</code>生成加密的密码, 这样可以避免wifi密码泄漏.</p>

<p>接着写配置文件的时候可以写多个wifi名称和密码, 并且可以指定优先级,
以应多多种场景:</p>

<pre><code class="language-txt">ctrl_interface=/var/run/wpa_supplicant
ap_scan=1

network={
    ssid="HomeWiFi"
    psk="home1234"
    priority=10
}

network={
    ssid="OfficeWiFi"
    psk="work5678"
    priority=5
}

network={
    ssid="MyPhone"
    psk="87654321"
    priority=1
}
</code></pre>

<p>有了<code class="language-plaintext highlighter-rouge">wpa_supplicant</code>的配置之后, 还需要启动<code class="language-plaintext highlighter-rouge">udhcpc</code>来获取ip地址:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>udhcpc <span class="nt">-i</span> wlan0 <span class="nt">-q</span>
</code></pre></div></div>

<hr />

<p>最后, 这整个流程也都可以写在一个脚本里, 比如<code class="language-plaintext highlighter-rouge">/etc/init.d/wifi_connect.sh</code>,
然后在系统启动的过程中调用这个脚本, 以实现开机自动启动:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">#!/bin/sh</span>
<span class="nv">WLAN_IFACE</span><span class="o">=</span><span class="s2">"wlan0"</span>
<span class="nv">CONF_FILE</span><span class="o">=</span><span class="s2">"/etc/wpa_supplicant.conf"</span>
ip <span class="nb">link set</span> <span class="nv">$WLAN_IFACE</span> up
wpa_supplicant <span class="nt">-B</span> <span class="nt">-i</span> <span class="nv">$WLAN_IFACE</span> <span class="nt">-c</span> <span class="nv">$CONF_FILE</span>
<span class="nb">sleep </span>3
udhcpc <span class="nt">-i</span> <span class="nv">$WLAN_IFACE</span> <span class="nt">-q</span>
</code></pre></div></div>

<p>然后在<code class="language-plaintext highlighter-rouge">/etc/init.d/rcS</code> 中的最后调用这个脚本:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>/etc/init.d/wifi_connect.sh &amp;
</code></pre></div></div>

<p>这样系统启动的时候就会自动连wifi啦.</p>]]></content><author><name>Jiandong Qiu</name><email>qiujiandong1998@gmail.com</email><uri>https://qiujiandong.github.io</uri></author><category term="Embedded" /><category term="wifi" /><category term="K230" /><category term="Embedded" /><summary type="html"><![CDATA[按照K230 linux WiFi使用指南 设置K230开发板的wifi密码. 感觉流程可以精简一些, 实现开机自动连接Wifi.]]></summary></entry><entry><title type="html">常量字符串末尾的换行符可以被优化</title><link href="https://qiujiandong.github.io/gcc/missing-linebrk/" rel="alternate" type="text/html" title="常量字符串末尾的换行符可以被优化" /><published>2025-10-16T22:35:28+00:00</published><updated>2025-10-16T22:35:28+00:00</updated><id>https://qiujiandong.github.io/gcc/missing-linebrk</id><content type="html" xml:base="https://qiujiandong.github.io/gcc/missing-linebrk/"><![CDATA[<p>C代码中带换行符的常量字符串, 编译器会将末尾的换行符优化掉, 节省一个换行符的空间.</p>

<p>今天遇到一个问题, 在<code class="language-plaintext highlighter-rouge">gcc</code>编译的时候, 常量字符串末尾的换行符<code class="language-plaintext highlighter-rouge">\n</code>没有出现在反汇编代码中.</p>

<p>代码非常简单:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span><span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="n">printf</span><span class="p">(</span><span class="s">"hello</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
  <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>objdump <span class="nt">-s</span> <span class="nt">-j</span> .rodata main

main:     file format elf64-x86-64

Contents of section .rodata:
 2000 01000200 68656c6c 6f00               ....hello.
</code></pre></div></div>

<p>从二进制的数据来看, <code class="language-plaintext highlighter-rouge">68</code>, <code class="language-plaintext highlighter-rouge">65</code>, <code class="language-plaintext highlighter-rouge">6c</code>, <code class="language-plaintext highlighter-rouge">6c</code>, <code class="language-plaintext highlighter-rouge">6f</code>对应的就是<code class="language-plaintext highlighter-rouge">hello</code>,
但是随后就是<code class="language-plaintext highlighter-rouge">00</code>表示字符串结尾了, <code class="language-plaintext highlighter-rouge">\n</code>对应的ASCII码是<code class="language-plaintext highlighter-rouge">0a</code>却没有出现.</p>

<p>后来通过反汇编的代码发现原因很简单, 就是<code class="language-plaintext highlighter-rouge">puts</code>函数能够自带换行符地打印输出,
在打印带换行符的常量字符串的时候, 直接用<code class="language-plaintext highlighter-rouge">puts</code>函数替换了<code class="language-plaintext highlighter-rouge">printf</code>,
从而节省了一个字符.</p>

<pre><code class="language-asm">0000000000001149 &lt;main&gt;:
    1149:	f3 0f 1e fa          	endbr64
    114d:	55                   	push   %rbp
    114e:	48 89 e5             	mov    %rsp,%rbp
    1151:	48 8d 05 ac 0e 00 00 	lea    0xeac(%rip),%rax        # 2004 &lt;_IO_stdin_used+0x4&gt;
    1158:	48 89 c7             	mov    %rax,%rdi
    115b:	e8 f0 fe ff ff       	call   1050 &lt;puts@plt&gt;
    1160:	b8 00 00 00 00       	mov    $0x0,%eax
    1165:	5d                   	pop    %rbp
    1166:	c3                   	ret

</code></pre>

<p>在这里不同编译器的行为可能都不一样, 可以在<a href="https://godbolt.org/z/seoG4naT8">这里</a>
换不同的编译器看结果.</p>]]></content><author><name>Jiandong Qiu</name><email>qiujiandong1998@gmail.com</email><uri>https://qiujiandong.github.io</uri></author><category term="GCC" /><category term="GCC" /><summary type="html"><![CDATA[C代码中带换行符的常量字符串, 编译器会将末尾的换行符优化掉, 节省一个换行符的空间.]]></summary></entry><entry><title type="html">rvv-bench测试框架</title><link href="https://qiujiandong.github.io/risc-v/rvv-bench/" rel="alternate" type="text/html" title="rvv-bench测试框架" /><published>2025-10-15T11:16:56+00:00</published><updated>2025-10-15T11:16:56+00:00</updated><id>https://qiujiandong.github.io/risc-v/rvv-bench</id><content type="html" xml:base="https://qiujiandong.github.io/risc-v/rvv-bench/"><![CDATA[<p>rvv-bench的测试框架设计得非常好, 一方面不依赖系统C库, 另一方面测试也比较严谨全面, 另外测试结果很容易在网页中显示.</p>

<p>上游仓库：</p>

<ul>
  <li><a href="https://github.com/camel-cdr/rvv-bench">camel-cdr/rvv-bench</a> 测试代码</li>
  <li><a href="https://github.com/camel-cdr/rvv-bench-results">camel-cdr/rvv-bench-results</a> 测试结果</li>
</ul>

<p>我fork之后的仓库</p>

<ul>
  <li><a href="https://github.com/qiujiandong/rvv-bench">qiujiandong/rvv-bench</a> 测试代码</li>
  <li><a href="https://github.com/qiujiandong/rvv-bench-results">qiujiandong/rvv-bench-results</a>
测试结果</li>
</ul>

<h2 id="编译">编译</h2>

<p>编译之前我调整了一下配置，可以参考这个commit: <a href="https://github.com/qiujiandong/rvv-bench/commit/3163e32b47ba552306cde20722f73ad24a9e2918">qiujiandong/rvv-bench commit:3163e32</a>
主要是</p>

<ul>
  <li>减小了测试数据量的大小，适合在嵌入式平台测试</li>
  <li>添加了<code class="language-plaintext highlighter-rouge">zvfh</code>的编译选项,</li>
  <li>增加了<code class="language-plaintext highlighter-rouge">USER_PERF_EVENT</code>的宏定义。</li>
</ul>

<p>然后利用<code class="language-plaintext highlighter-rouge">rvv-bench/bench</code>目录中的<code class="language-plaintext highlighter-rouge">Makefile</code>就可以完成编译。</p>

<p>编译完成后得到的是多个二进制文件，可以在Linux上独立测试：</p>

<pre><code class="language-txt">ascii_to_utf16
ascii_to_utf32
base64_encode
byteswap
chacha20
hist
LUT4
LUT6
mandelbrot
memcpy
memset
mergelines
poly1305
strlen
trans8x8e16
trans8x8e8
utf8_count
</code></pre>

<p>所有测试程序的输出都是javascript中的一个Object,
将所有测试结果保存到一个<code class="language-plaintext highlighter-rouge">data.js</code>文件中，写成一个<code class="language-plaintext highlighter-rouge">data</code>数组，便于后续显示.
类似如下的格式：</p>

<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// data.js</span>
<span class="kd">let</span> <span class="nx">data</span> <span class="o">=</span> <span class="p">[</span>
  <span class="p">{</span>
    <span class="na">title</span><span class="p">:</span> <span class="dl">"</span><span class="s2">ascii to utf16</span><span class="dl">"</span><span class="p">,</span>
    <span class="na">labels</span><span class="p">:</span> <span class="p">[</span>
      <span class="dl">"</span><span class="s2">0</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">scalar</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">scalar_autovec</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">rvv_ext_m1</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">rvv_ext_m2</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">rvv_ext_m4</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">rvv_vsseg_m1</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">rvv_vsseg_m2</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">rvv_vsseg_m4</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">rvv_vss_m1</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">rvv_vss_m2</span><span class="dl">"</span><span class="p">,</span>
      <span class="dl">"</span><span class="s2">rvv_vss_m4</span><span class="dl">"</span><span class="p">,</span>
    <span class="p">],</span>
    <span class="p">...</span>
  <span class="p">},</span>
<span class="p">...</span>
<span class="p">];</span>
</code></pre></div></div>

<h2 id="显示结果">显示结果</h2>

<p>对于结果数据的显示, 我做了一些优化。比如这里有一个<code class="language-plaintext highlighter-rouge">vl128dl128</code>的文件夹,
如果需要更新测试结果，只需要复制整个文件夹，然后更新<code class="language-plaintext highlighter-rouge">data.js</code>文件即可。</p>

<p><img src="/images/posts/rvv-bench/results.png" alt="results" class="align-center" /></p>

<h2 id="c代码解析">C代码解析</h2>

<h3 id="裸机版本">裸机版本</h3>

<p>从编译选项中带<code class="language-plaintext highlighter-rouge">-nostdlib</code>可以看出来，虽然是在linux上跑，但是这个测试代码是不依赖标准库的.
和标准库相关的内容都在<code class="language-plaintext highlighter-rouge">nolibc.h</code>中实现了，所以如果是需要移植到裸机上跑的话也很方便,
只需要对<code class="language-plaintext highlighter-rouge">nolibc.h</code>中的内容做裸机的适配即可。</p>

<h3 id="自定义测试函数">自定义测试函数</h3>

<p>如果需要测试跟memory大小相关的一些性能，也可以通过这个框架来测试.
比如<code class="language-plaintext highlighter-rouge">memcpy</code>, 有不同的实现版本，修改<code class="language-plaintext highlighter-rouge">memcpy.c</code>中的<code class="language-plaintext highlighter-rouge">IMPLS</code>宏可以控制需要测试的实现版本。</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="cp">#define IMPLS(f) \
    IFHOSTED(f(libc)) \
    f(musl) \
    f(scalar) \
    f(scalar_autovec) \
    MX(f, rvv) \
    MX(f, rvv_align_dest) \
    MX(f, rvv_align_src) \
    MX(f, rvv_align_dest_hybrid) \
    MX(f, rvv_vlmax) \
    MX(f, rvv_tail) \
    MX(f, rvv_128) \
</span></code></pre></div></div>

<h3 id="测cycle的方式">测cycle的方式</h3>

<p>在Linux上用户一般不能直接读cycle寄存器，但如果Linux配了<code class="language-plaintext highlighter-rouge">CONFIG_PERF_EVENTS=y</code>,
那么就可以通过<code class="language-plaintext highlighter-rouge">/proc/sys/kernel/perf_user_access</code>来控制访问cycle寄存器的权限。</p>

<p>从Linux Kernel的<a href="https://docs.kernel.org/admin-guide/sysctl/kernel.html?utm_source=chatgpt.com#perf-user-access-arm64-and-riscv-only">文档</a>
可以了解到：</p>

<pre><code class="language-txt">perf_user_access (arm64 and riscv only)
Controls user space access for reading perf event counters.

for arm64 The default value is 0 (access disabled).

When set to 1, user space can read performance monitor counter registers directly.

See Perf for more information.

for riscv When set to 0, user space access is disabled.

The default value is 1, user space can read performance monitor counter registers
through perf, any direct access without perf intervention will trigger an illegal
instruction.

When set to 2, which enables legacy mode (user space has direct access to cycle and
insret CSRs only). Note that this legacy value is deprecated and will be removed
once all user space applications are fixed.

Note that the time CSR is always directly accessible to all modes.
</code></pre>

<p>代码里有两个宏，<code class="language-plaintext highlighter-rouge">USE_PERF_EVENT</code> 和 <code class="language-plaintext highlighter-rouge">USE_PERF_EVENT_SLOW</code>.
其中<code class="language-plaintext highlighter-rouge">USE_PERF_EVENT</code>就是直接用<code class="language-plaintext highlighter-rouge">rdcycle</code>指令读cycle,
<code class="language-plaintext highlighter-rouge">USE_PERF_EVENT_SLOW</code>就是需要通过<code class="language-plaintext highlighter-rouge">perf_event_open</code>以文件操作的形式获取cycle.
文件操作肯定相对于直接读寄存器更慢一些。</p>

<p>这两种方式不管是哪一种都需要先通过系统调用<code class="language-plaintext highlighter-rouge">perf_event_open</code>先获取文件描述符,
才能测cycle。所以代码中有一部分绕过glibc库，直接通过<code class="language-plaintext highlighter-rouge">ecall</code>进行系统调用的做法。</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">__attribute__</span><span class="p">((</span><span class="kr">naked</span><span class="p">))</span>
<span class="k">static</span> <span class="kt">int</span>
<span class="nf">nolibc_perf_event_open</span><span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">ptr</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">__asm__</span> <span class="p">(</span>
        <span class="s">"li a1, 0</span><span class="se">\n</span><span class="s">"</span>
        <span class="s">"li a2, -1</span><span class="se">\n</span><span class="s">"</span>
        <span class="s">"li a3, -1</span><span class="se">\n</span><span class="s">"</span>
        <span class="s">"li a4, 0</span><span class="se">\n</span><span class="s">"</span>
        <span class="s">"li a7, 241</span><span class="se">\n</span><span class="s">"</span>
        <span class="s">"ecall</span><span class="se">\n</span><span class="s">"</span>
        <span class="s">"ret</span><span class="se">\n</span><span class="s">"</span>
    <span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>

<p>在Linux内核源码中有：</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// include/uapi/asm-generic/unistd.h</span>
<span class="cp">#define __NR_perf_event_open 241
</span></code></pre></div></div>

<p>所以需要给a7传入的值是241，也就是<code class="language-plaintext highlighter-rouge">perf_event_open</code>的系统调用号。</p>]]></content><author><name>Jiandong Qiu</name><email>qiujiandong1998@gmail.com</email><uri>https://qiujiandong.github.io</uri></author><category term="RISC-V" /><category term="RISC-V" /><category term="rvv" /><category term="benchmark" /><summary type="html"><![CDATA[rvv-bench的测试框架设计得非常好, 一方面不依赖系统C库, 另一方面测试也比较严谨全面, 另外测试结果很容易在网页中显示.]]></summary></entry><entry><title type="html">用wurblpt仿真ToF传感器</title><link href="https://qiujiandong.github.io/sensor/wurblpt/" rel="alternate" type="text/html" title="用wurblpt仿真ToF传感器" /><published>2025-10-12T23:21:36+00:00</published><updated>2025-10-12T23:21:36+00:00</updated><id>https://qiujiandong.github.io/sensor/wurblpt</id><content type="html" xml:base="https://qiujiandong.github.io/sensor/wurblpt/"><![CDATA[<p>介绍如何一步步从零开始基于<a href="https://github.com/marlam/wurblpt">WurblPT</a>仿真ToF传感器.</p>

<p>最开始是从这篇论文<a href="https://ieeexplore.ieee.org/document/7054461">Simulation of Time-of-Flight Sensors for Evaluation of Chip Layout Variants</a>
入手的, WurblPT有一个example用来仿真这个论文里的例子.</p>

<p>仓库依赖<code class="language-plaintext highlighter-rouge">tgd</code>, 可以先把这个仓库<a href="https://github.com/marlam/tgd">tgd</a> clone一下, 先编译这个</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>tgd
cmake <span class="nt">-B</span> build 
cmake <span class="nt">-B</span> build <span class="nt">-DCMAKE_INSTALL_PREFIX</span><span class="o">=</span><span class="nv">$HOME</span>/.local <span class="nt">-DCMAKE_EXPORT_COMPILE_COMMANDS</span><span class="o">=</span>ON <span class="nt">-DCMAKE_BUILD_TYPE</span><span class="o">=</span>Release <span class="nb">.</span>
<span class="nb">cd </span>build
<span class="nb">sudo </span>checkinstall
</code></pre></div></div>

<p>值得注意的有两个地方:</p>

<ol>
  <li>一定要编译Release模式, Release模式跑起来真的快很多.</li>
  <li>这种用源码编译的库我一般都放在自己的<code class="language-plaintext highlighter-rouge">.local</code>目录, 那样不容易污染系统环境.</li>
  <li>用<code class="language-plaintext highlighter-rouge">checkinstall</code>安装可以方便后续卸载, 但就是需要用到管理员权限,
卸载的时候也是通过dpkg卸载. 在安装的时候要记得看清楚安装的库的名字.</li>
</ol>

<p>安装完<code class="language-plaintext highlighter-rouge">tgd</code>之后就可以安装<code class="language-plaintext highlighter-rouge">libwurblpt</code>了, 安装的命令也是一样的.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>wurblpt/libwurblpt
cmake <span class="nt">-B</span> build <span class="nt">-DCMAKE_INSTALL_PREFIX</span><span class="o">=</span><span class="nv">$HOME</span>/.local <span class="nt">-DCMAKE_EXPORT_COMPILE_COMMANDS</span><span class="o">=</span>ON <span class="nt">-DCMAKE_BUILD_TYPE</span><span class="o">=</span>Release <span class="nb">.</span>
<span class="nb">cd </span>build
<span class="nb">sudo </span>checkinstall
</code></pre></div></div>

<p>最后再编译<code class="language-plaintext highlighter-rouge">wurblpt-tof-example</code>, 然后就可以跑ToF的仿真了.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">cd </span>wurblpt/wurblpt-tof-example
cmake <span class="nt">-B</span> build <span class="nt">-DCMAKE_INSTALL_PREFIX</span><span class="o">=</span><span class="nv">$HOME</span>/.local <span class="nt">-DCMAKE_EXPORT_COMPILE_COMMANDS</span><span class="o">=</span>ON <span class="nt">-DCMAKE_BUILD_TYPE</span><span class="o">=</span>Release <span class="nb">.</span>
<span class="nb">cd </span>build
make
./wurblpt-tof-example
</code></pre></div></div>

<p>仿真生成的都是<code class="language-plaintext highlighter-rouge">.tgd</code>后缀的数据, 可以用<code class="language-plaintext highlighter-rouge">qv</code>查看, 这个我也是从仓库的issue看到的,
<a href="https://github.com/marlam/wurblpt/issues/1">wurblpt#1</a>
我是通过<code class="language-plaintext highlighter-rouge">flatpak</code>安装的<code class="language-plaintext highlighter-rouge">qv</code>, 可以把要看的一个系列图片放在一个文件夹里,
然后用<code class="language-plaintext highlighter-rouge">qv</code>查看整个文件夹, 然后可以用方向键的左右来看每帧的结果.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>flatpak run de.marlam.qv result
</code></pre></div></div>

<p>如何将距离像转换为三维点云视图? <a href="https://marlam.de/wurblpt/features/simulation-of-time-of-flight-sensors/">这里</a>
只说了可以用距离像生成, 但是没有说具体怎么做. 而且<code class="language-plaintext highlighter-rouge">tgd</code>格式在python里的支持不是很好,
没法直接读取. <code class="language-plaintext highlighter-rouge">tgd</code>格式可以转<code class="language-plaintext highlighter-rouge">tiff</code>格式:<br />
比如像这样可以把<code class="language-plaintext highlighter-rouge">resutl.tgd</code>中通道0的数据转换称<code class="language-plaintext highlighter-rouge">tiff</code>格式.</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>tgd convert <span class="nt">-c</span> 0 result.tgd result.tiff
</code></pre></div></div>

<p>然后<code class="language-plaintext highlighter-rouge">tiff</code>格式的数据在python中就可以比较方便处理了. 利用<code class="language-plaintext highlighter-rouge">open3d</code>可以生成3D点云.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">tifffile</span> <span class="k">as</span> <span class="n">tiff</span>
<span class="kn">import</span> <span class="nn">open3d</span> <span class="k">as</span> <span class="n">o3d</span>
<span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="n">np</span>
<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="n">plt</span>

<span class="n">Z</span> <span class="o">=</span> <span class="n">tiff</span><span class="p">.</span><span class="n">imread</span><span class="p">(</span><span class="s">"out.tiff"</span><span class="p">)</span>
<span class="n">Z</span> <span class="o">=</span> <span class="mi">300</span> <span class="o">-</span> <span class="n">Z</span> <span class="o">*</span> <span class="mi">100</span>
<span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">meshgrid</span><span class="p">(</span><span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="n">Z</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">1</span><span class="p">]),</span> <span class="n">np</span><span class="p">.</span><span class="n">arange</span><span class="p">(</span><span class="n">Z</span><span class="p">.</span><span class="n">shape</span><span class="p">[</span><span class="mi">0</span><span class="p">])[::</span><span class="o">-</span><span class="mi">1</span><span class="p">])</span>

<span class="n">points</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">stack</span><span class="p">((</span><span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">Z</span><span class="p">),</span> <span class="n">axis</span><span class="o">=-</span><span class="mi">1</span><span class="p">).</span><span class="n">reshape</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>

<span class="n">norm</span> <span class="o">=</span> <span class="p">(</span><span class="n">Z</span> <span class="o">-</span> <span class="n">Z</span><span class="p">.</span><span class="nb">min</span><span class="p">())</span> <span class="o">/</span> <span class="p">(</span><span class="n">Z</span><span class="p">.</span><span class="nb">max</span><span class="p">()</span> <span class="o">+</span> <span class="mi">50</span> <span class="o">-</span> <span class="n">Z</span><span class="p">.</span><span class="nb">min</span><span class="p">())</span>
<span class="n">colors</span> <span class="o">=</span> <span class="n">plt</span><span class="p">.</span><span class="n">cm</span><span class="p">.</span><span class="n">jet</span><span class="p">(</span><span class="n">norm</span><span class="p">).</span><span class="n">reshape</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">4</span><span class="p">)[:,</span> <span class="p">:</span><span class="mi">3</span><span class="p">]</span>

<span class="n">pcd</span> <span class="o">=</span> <span class="n">o3d</span><span class="p">.</span><span class="n">geometry</span><span class="p">.</span><span class="n">PointCloud</span><span class="p">()</span>
<span class="n">pcd</span><span class="p">.</span><span class="n">points</span> <span class="o">=</span> <span class="n">o3d</span><span class="p">.</span><span class="n">utility</span><span class="p">.</span><span class="n">Vector3dVector</span><span class="p">(</span><span class="n">points</span><span class="p">)</span>
<span class="n">pcd</span><span class="p">.</span><span class="n">colors</span> <span class="o">=</span> <span class="n">o3d</span><span class="p">.</span><span class="n">utility</span><span class="p">.</span><span class="n">Vector3dVector</span><span class="p">(</span><span class="n">colors</span><span class="p">)</span>

<span class="n">min_bound</span> <span class="o">=</span> <span class="n">points</span><span class="p">.</span><span class="nb">min</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>
<span class="n">max_bound</span> <span class="o">=</span> <span class="n">points</span><span class="p">.</span><span class="nb">max</span><span class="p">(</span><span class="n">axis</span><span class="o">=</span><span class="mi">0</span><span class="p">)</span>

<span class="n">bbox_points</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">([</span>
    <span class="p">[</span><span class="n">min_bound</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">min_bound</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">min_bound</span><span class="p">[</span><span class="mi">2</span><span class="p">]],</span>
    <span class="p">[</span><span class="n">max_bound</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">min_bound</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">min_bound</span><span class="p">[</span><span class="mi">2</span><span class="p">]],</span>
    <span class="p">[</span><span class="n">max_bound</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">max_bound</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">min_bound</span><span class="p">[</span><span class="mi">2</span><span class="p">]],</span>
    <span class="p">[</span><span class="n">min_bound</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">max_bound</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">min_bound</span><span class="p">[</span><span class="mi">2</span><span class="p">]],</span>
    <span class="p">[</span><span class="n">min_bound</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">min_bound</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">max_bound</span><span class="p">[</span><span class="mi">2</span><span class="p">]],</span>
    <span class="p">[</span><span class="n">max_bound</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">min_bound</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">max_bound</span><span class="p">[</span><span class="mi">2</span><span class="p">]],</span>
    <span class="p">[</span><span class="n">max_bound</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">max_bound</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">max_bound</span><span class="p">[</span><span class="mi">2</span><span class="p">]],</span>
    <span class="p">[</span><span class="n">min_bound</span><span class="p">[</span><span class="mi">0</span><span class="p">],</span> <span class="n">max_bound</span><span class="p">[</span><span class="mi">1</span><span class="p">],</span> <span class="n">max_bound</span><span class="p">[</span><span class="mi">2</span><span class="p">]],</span>
<span class="p">])</span>

<span class="n">lines</span> <span class="o">=</span> <span class="p">[</span>
    <span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">],[</span><span class="mi">1</span><span class="p">,</span><span class="mi">2</span><span class="p">],[</span><span class="mi">2</span><span class="p">,</span><span class="mi">3</span><span class="p">],[</span><span class="mi">3</span><span class="p">,</span><span class="mi">0</span><span class="p">],</span>
    <span class="p">[</span><span class="mi">4</span><span class="p">,</span><span class="mi">5</span><span class="p">],[</span><span class="mi">5</span><span class="p">,</span><span class="mi">6</span><span class="p">],[</span><span class="mi">6</span><span class="p">,</span><span class="mi">7</span><span class="p">],[</span><span class="mi">7</span><span class="p">,</span><span class="mi">4</span><span class="p">],</span>
    <span class="p">[</span><span class="mi">0</span><span class="p">,</span><span class="mi">4</span><span class="p">],[</span><span class="mi">1</span><span class="p">,</span><span class="mi">5</span><span class="p">],[</span><span class="mi">2</span><span class="p">,</span><span class="mi">6</span><span class="p">],[</span><span class="mi">3</span><span class="p">,</span><span class="mi">7</span><span class="p">]</span> 
<span class="p">]</span>

<span class="n">colors_lines</span> <span class="o">=</span> <span class="p">[[</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">]</span> <span class="k">for</span> <span class="n">_</span> <span class="ow">in</span> <span class="n">lines</span><span class="p">]</span>

<span class="n">line_set</span> <span class="o">=</span> <span class="n">o3d</span><span class="p">.</span><span class="n">geometry</span><span class="p">.</span><span class="n">LineSet</span><span class="p">()</span>
<span class="n">line_set</span><span class="p">.</span><span class="n">points</span> <span class="o">=</span> <span class="n">o3d</span><span class="p">.</span><span class="n">utility</span><span class="p">.</span><span class="n">Vector3dVector</span><span class="p">(</span><span class="n">bbox_points</span><span class="p">)</span>
<span class="n">line_set</span><span class="p">.</span><span class="n">lines</span> <span class="o">=</span> <span class="n">o3d</span><span class="p">.</span><span class="n">utility</span><span class="p">.</span><span class="n">Vector2iVector</span><span class="p">(</span><span class="n">lines</span><span class="p">)</span>
<span class="n">line_set</span><span class="p">.</span><span class="n">colors</span> <span class="o">=</span> <span class="n">o3d</span><span class="p">.</span><span class="n">utility</span><span class="p">.</span><span class="n">Vector3dVector</span><span class="p">(</span><span class="n">colors_lines</span><span class="p">)</span>

<span class="n">render_option</span> <span class="o">=</span> <span class="n">o3d</span><span class="p">.</span><span class="n">visualization</span><span class="p">.</span><span class="n">RenderOption</span><span class="p">()</span>
<span class="n">render_option</span><span class="p">.</span><span class="n">background_color</span> <span class="o">=</span> <span class="n">np</span><span class="p">.</span><span class="n">array</span><span class="p">([</span><span class="mf">1.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">,</span> <span class="mf">1.0</span><span class="p">])</span>

<span class="n">o3d</span><span class="p">.</span><span class="n">visualization</span><span class="p">.</span><span class="n">draw_geometries</span><span class="p">([</span><span class="n">pcd</span><span class="p">,</span> <span class="n">line_set</span><span class="p">])</span>
</code></pre></div></div>]]></content><author><name>Jiandong Qiu</name><email>qiujiandong1998@gmail.com</email><uri>https://qiujiandong.github.io</uri></author><category term="sensor" /><category term="sensor" /><category term="sim" /><category term="wurblpt" /><summary type="html"><![CDATA[介绍如何一步步从零开始基于WurblPT仿真ToF传感器.]]></summary></entry><entry><title type="html">RISC-V中利用rdtime估计cycle数</title><link href="https://qiujiandong.github.io/risc-v/rdtime/" rel="alternate" type="text/html" title="RISC-V中利用rdtime估计cycle数" /><published>2025-10-10T00:17:24+00:00</published><updated>2025-10-10T00:17:24+00:00</updated><id>https://qiujiandong.github.io/risc-v/rdtime</id><content type="html" xml:base="https://qiujiandong.github.io/risc-v/rdtime/"><![CDATA[<p>在RISC-V Linux中, <code class="language-plaintext highlighter-rouge">rdtime</code>或者<code class="language-plaintext highlighter-rouge">rdcycle</code>可以用来获取和时间相关的统计量, 但是时钟源一般不同.</p>

<ul>
  <li><code class="language-plaintext highlighter-rouge">rdtime</code>采用的是较低精度的时钟源, 但是这个时钟是以恒定频率工作的，所以可以用来稳定计时。</li>
  <li><code class="language-plaintext highlighter-rouge">rdcycle</code>相对地，CPU的cycle所对应的时钟可能是动态变化的，但它也是用来精确衡量算力消耗的单位。</li>
</ul>

<p>假定CPU以恒定频率工作，那么<code class="language-plaintext highlighter-rouge">rdtime</code>和<code class="language-plaintext highlighter-rouge">rdcycle</code>就是呈固定的线性关系，因此可以由<code class="language-plaintext highlighter-rouge">rdtime</code>的结果来推算cycle数。</p>

<h2 id="测试代码">测试代码</h2>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// main.c</span>
<span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span>
<span class="k">extern</span> <span class="kt">unsigned</span> <span class="kt">long</span> <span class="nf">test</span><span class="p">();</span>

<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span> <span class="n">printf</span><span class="p">(</span><span class="s">"cycle: %lu</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">test</span><span class="p">());</span> <span class="p">}</span>
</code></pre></div></div>

<pre><code class="language-asm"># test.S
.global test

test:
    li      t0, 10000
    rdtime  t1
1:
    ld      a0, 0(sp)
    ld      a1, 8(sp)
    ld      a2, 16(sp)
    ld      a3, 24(sp)
    ld      a4, 32(sp)
    ld      a5, 40(sp)
    ld      a6, 48(sp)
    ld      a7, 56(sp)
    ld      s0, 64(sp)
    ld      s1, 72(sp)
    ld      s2, 80(sp)
    ld      s3, 88(sp)
    ld      s4, 96(sp)
    ld      s5, 104(sp)
    ld      s6, 112(sp)
    ld      s7, 120(sp)
    addi    t0, t0, -1
    bnez    t0, 1b
    rdtime  t0
    sub     a0, t0, t1
    ret

</code></pre>

<h2 id="编译">编译</h2>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>riscv64-unknown-linux-gnu-gcc <span class="nt">-march</span><span class="o">=</span>rv64imafdc <span class="nt">-mabi</span><span class="o">=</span>lp64d <span class="nt">-static</span> <span class="nt">-o</span> main main.c test.S
</code></pre></div></div>

<h2 id="测试">测试</h2>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./main
cycle: 2706
</code></pre></div></div>

<p>经过<code class="language-plaintext highlighter-rouge">rdtime</code>统计得到的结果是2706个计数值，那么<code class="language-plaintext highlighter-rouge">rdtime</code>的参考频率是多少？<br />
可以通过<code class="language-plaintext highlighter-rouge">timebase-frequency</code>的值来确定。</p>

<h2 id="结果分析">结果分析</h2>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> xxd /proc/device-tree/cpus/timebase-frequency
00000000: 019b fcc0                                ....
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">timebase-frequency</code>是以大端形式存放的，实际的值应该是 <code class="language-plaintext highlighter-rouge">27000000</code></p>

<p><img src="/images/posts/rdtime/timebase-frequency.png" alt="timebase" class="align-center" /></p>

<p>如果CPU是固定以1.6GHz工作的，那么就可以计算出实际的cycle数：</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;&gt;&gt;</span> 2706/27000000<span class="k">*</span>1600000000
160355.55555555556
</code></pre></div></div>

<p>从测试的结果可以看出，代码中大约执行160000次<code class="language-plaintext highlighter-rouge">ld</code>指令，推算得出大约耗时160355 cycle，结果也是比较合理的。</p>]]></content><author><name>Jiandong Qiu</name><email>qiujiandong1998@gmail.com</email><uri>https://qiujiandong.github.io</uri></author><category term="RISC-V" /><category term="rdtime" /><category term="RISC-V" /><summary type="html"><![CDATA[在RISC-V Linux中, rdtime或者rdcycle可以用来获取和时间相关的统计量, 但是时钟源一般不同.]]></summary></entry><entry><title type="html">Neovim自定义代码片段</title><link href="https://qiujiandong.github.io/neovim/snippets/" rel="alternate" type="text/html" title="Neovim自定义代码片段" /><published>2025-10-09T23:43:28+00:00</published><updated>2025-10-09T23:43:28+00:00</updated><id>https://qiujiandong.github.io/neovim/snippets</id><content type="html" xml:base="https://qiujiandong.github.io/neovim/snippets/"><![CDATA[<p>用<a href="https://github.com/L3MON4D3/LuaSnip">LuaSnip</a>可以实现自定义代码片段,
从而避免一些重复的typing.</p>

<hr />

<p>起因是这样的, 很多开源的代码中, 文件头部都有统一的关于License的注释, 比如:</p>

<pre><code class="language-txt">SPDX-License-Identifier: ...
</code></pre>

<p>还有一些Doxygen的文件头注释:</p>

<pre><code class="language-txt">/*! 
 * @file
 * @author Firstname Lastname
 * @version 1.0
 * @copyright Copyright (c) 2025
 */
</code></pre>

<p>这些代码片段都可以通过预先定义snippets来快速插入. 可以参考这个commit的设置:
<a href="https://github.com/qiujiandong/kickstart.nvim/commit/99bf05bcbc168d214e33cc98e06566299969d622">qiujiandong/kickstart.nvim:99bf05b</a></p>

<div class="language-lua highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">return</span> <span class="p">{</span>
  <span class="c1">-- C file header snippet</span>
  <span class="n">s</span><span class="p">(</span>
    <span class="s1">'header_apache'</span><span class="p">,</span>
    <span class="n">fmt</span><span class="p">(</span>
      <span class="s">[[
/**
 * @file {project}.c
 * @brief {description}
 * @author Jiandong Qiu
 * @date {}
 *
 * Copyright (c) {} Jiandong Qiu
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

]]</span><span class="p">,</span>
      <span class="p">{</span>
        <span class="n">project</span> <span class="o">=</span> <span class="n">i</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="s1">'ProjectName'</span><span class="p">),</span>
        <span class="n">description</span> <span class="o">=</span> <span class="n">i</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="s1">'Short description'</span><span class="p">),</span>
        <span class="n">f</span><span class="p">(</span><span class="k">function</span><span class="p">()</span>
          <span class="k">return</span> <span class="nb">os.date</span> <span class="s1">'%Y-%m-%d'</span>
        <span class="k">end</span><span class="p">),</span>
        <span class="nb">os.date</span> <span class="s1">'%Y'</span><span class="p">,</span>
      <span class="p">}</span>
    <span class="p">)</span>
  <span class="p">),</span>
<span class="p">}</span>
</code></pre></div></div>

<p>效果就是只需要输入<code class="language-plaintext highlighter-rouge">header_apache</code>, 就会自动插入上述的代码片段.</p>

<p>如果之后遇到开发中经常写的代码片段, 也都可以用类似的方式写一段snippets, 从而提高效率.</p>]]></content><author><name>Jiandong Qiu</name><email>qiujiandong1998@gmail.com</email><uri>https://qiujiandong.github.io</uri></author><category term="Neovim" /><category term="nvim" /><category term="snippets" /><summary type="html"><![CDATA[用LuaSnip可以实现自定义代码片段, 从而避免一些重复的typing.]]></summary></entry><entry><title type="html">GCC Undefined Behavior (UB)和有符号数溢出</title><link href="https://qiujiandong.github.io/gcc/signed-overflow/" rel="alternate" type="text/html" title="GCC Undefined Behavior (UB)和有符号数溢出" /><published>2025-10-06T23:41:38+00:00</published><updated>2025-10-06T23:41:38+00:00</updated><id>https://qiujiandong.github.io/gcc/signed-overflow</id><content type="html" xml:base="https://qiujiandong.github.io/gcc/signed-overflow/"><![CDATA[<p>GCC中有一些Undefined Behavior(UB), 在开启<code class="language-plaintext highlighter-rouge">-O2/O3</code>优化后, 可能导致意想不到的结果.</p>

<h2 id="实例">实例</h2>

<p>实际举个例子, 如下面的代码:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// main.c</span>
<span class="cp">#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdlib.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp">
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">10</span><span class="p">;</span> <span class="o">++</span><span class="n">i</span><span class="p">)</span> <span class="p">{</span>
    <span class="kt">int32_t</span> <span class="n">a</span> <span class="o">=</span> <span class="n">rand</span><span class="p">()</span> <span class="o">%</span> <span class="mh">0x10000</span><span class="p">;</span>
    <span class="kt">int16_t</span> <span class="n">b</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int16_t</span><span class="p">)((</span><span class="n">a</span> <span class="o">*</span> <span class="n">a</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="mi">16</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">b</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
      <span class="n">printf</span><span class="p">(</span><span class="s">"b &gt; 0, b = %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">b</span><span class="p">);</span>
    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
      <span class="n">printf</span><span class="p">(</span><span class="s">"b &lt; 0, b = %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">b</span><span class="p">);</span>
    <span class="p">}</span>
  <span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>

<p>采用不同的优化等级编译并运行:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ gcc <span class="nt">-O0</span> <span class="nt">-o</span> main main.c <span class="o">&amp;&amp;</span> ./main
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 4816
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 1279
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 23228
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 5248
b &lt; 0, b <span class="o">=</span> <span class="nt">-16997</span>
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 8648
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 21989
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 7907
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 970
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 15575
</code></pre></div></div>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ gcc <span class="nt">-O2</span> <span class="nt">-o</span> main main.c <span class="o">&amp;&amp;</span> ./main
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 4816
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 1279
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 23228
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 5248
b <span class="o">&gt;</span> 0, b <span class="o">=</span> <span class="nt">-16997</span>
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 8648
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 21989
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 7907
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 970
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 15575
</code></pre></div></div>

<p>不难发现, <code class="language-plaintext highlighter-rouge">O2</code>优化的结果中, <strong>有时候<code class="language-plaintext highlighter-rouge">b</code>打印出来明明是负数, 但判断的时候却是始终认为其是大于0的</strong>,
这就让人感觉非常匪夷所思了.</p>

<h2 id="原因分析">原因分析</h2>

<p><code class="language-plaintext highlighter-rouge">a</code>是一个[0, 65535)的随机数, <code class="language-plaintext highlighter-rouge">a * a</code>在<code class="language-plaintext highlighter-rouge">int32_t</code>类型的表示范围内有可能会溢出,
即超出<code class="language-plaintext highlighter-rouge">int32_t</code>的表示范围.<br />
而且编译器在开启<code class="language-plaintext highlighter-rouge">-O2/O3</code>优化的时候, 会假设不会有有符号数溢出, 因此<code class="language-plaintext highlighter-rouge">a * a</code>必定大于0.
在这样一个前提下, 下面关于<code class="language-plaintext highlighter-rouge">b</code>的判断就会始终认为<code class="language-plaintext highlighter-rouge">b</code>是大于0的.
但实际上在有符号数溢出的情况下, <code class="language-plaintext highlighter-rouge">b</code>的值是有可能小于0的, 这样一来就导致了上面实际测试中遇到的问题.</p>

<h2 id="解决办法">解决办法</h2>

<p>GCC有很多Undefined Behavior(UB), 而且有Undefined Behavior Sanitizer (UBSan) 可以在运行时发现这些隐患.
在编译的时候加上 <code class="language-plaintext highlighter-rouge">-fsanitize=undefined</code> 选项,
可以参考<a href="https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#index-fsanitize_003dundefined">GCC文档 -fsanitize=undefined</a>,
这样一来运行时就会有如下的错误提示:</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>❯ gcc <span class="nt">-O2</span> <span class="nt">-fsanitize</span><span class="o">=</span>undefined <span class="nt">-o</span> main main.c <span class="o">&amp;&amp;</span> ./main
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 4816
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 1279
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 23228
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 5248
main.c:30:30: runtime error: signed integer overflow: 56401 <span class="k">*</span> 56401 cannot be represented <span class="k">in </span><span class="nb">type</span> <span class="s1">'int'</span>
b &lt; 0, b <span class="o">=</span> <span class="nt">-16997</span>
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 8648
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 21989
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 7907
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 970
b <span class="o">&gt;</span> 0, b <span class="o">=</span> 15575
</code></pre></div></div>

<h2 id="参考连接">参考连接</h2>

<ul>
  <li><a href="https://wiki.gentoo.org/wiki/UndefinedBehaviorSanitizer">UndefinedBehaviorSanitizer (Gentoo wiki)</a></li>
  <li><a href="https://blogs.oracle.com/linux/post/improving-application-security-with-undefinedbehaviorsanitizer-ubsan-and-gcc">Improving Application Security with UndefinedBehaviorSanitizer (UBSan) and GCC (Oracle Linux Blog)</a></li>
</ul>]]></content><author><name>Jiandong Qiu</name><email>qiujiandong1998@gmail.com</email><uri>https://qiujiandong.github.io</uri></author><category term="GCC" /><category term="GCC" /><category term="UB" /><summary type="html"><![CDATA[GCC中有一些Undefined Behavior(UB), 在开启-O2/O3优化后, 可能导致意想不到的结果.]]></summary></entry><entry><title type="html">pragma pack() 用GCC编译时不接受宏参数</title><link href="https://qiujiandong.github.io/gcc/pragma-pack/" rel="alternate" type="text/html" title="pragma pack() 用GCC编译时不接受宏参数" /><published>2025-10-06T18:20:33+00:00</published><updated>2025-10-06T18:20:33+00:00</updated><id>https://qiujiandong.github.io/gcc/pragma-pack</id><content type="html" xml:base="https://qiujiandong.github.io/gcc/pragma-pack/"><![CDATA[<p><code class="language-plaintext highlighter-rouge">#pragma pack()</code>括号中的参数如果是一个宏, 那么在GCC和MSVC中处理的方式不一样.</p>

<h2 id="原因分析">原因分析</h2>

<p>使用GCC编译器时， <code class="language-plaintext highlighter-rouge">#pragma pack()</code> 中指定的对齐字节数, 不能通过宏来传递, 而MSVC则可以通过宏来传递。</p>

<h2 id="测试代码">测试代码</h2>

<p>定义了一个结构体<code class="language-plaintext highlighter-rouge">A</code>，在使用<code class="language-plaintext highlighter-rouge">#pragma pack(2)</code>时, <code class="language-plaintext highlighter-rouge">A</code>占6字节，如果不使用则占8字节。</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// main.c</span>
<span class="cp">#include</span> <span class="cpf">&lt;stdint.h&gt;</span><span class="cp">
#include</span> <span class="cpf">&lt;stdio.h&gt;</span><span class="cp">
</span>
<span class="cp">#if USE_MACRO
#define ALIGN 2
#pragma pack(ALIGN)
#else
#pragma pack(2)
#endif
</span><span class="k">typedef</span> <span class="k">struct</span> <span class="p">{</span>
    <span class="kt">uint16_t</span> <span class="n">a</span><span class="p">;</span>
    <span class="kt">uint32_t</span> <span class="n">b</span><span class="p">;</span>
<span class="p">}</span> <span class="n">A</span><span class="p">;</span>
<span class="cp">#pragma pack()
</span>
<span class="kt">int</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
    <span class="n">printf</span><span class="p">(</span><span class="s">"sizeof A: %u</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">A</span><span class="p">));</span>
    <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>

<h2 id="测试结果">测试结果</h2>

<pre><code class="language-txt">❯ gcc -o main -DUSE_MACRO=0 main.c &amp;&amp; ./main
sizeof A: 6
❯ gcc -o main -DUSE_MACRO=1 main.c &amp;&amp; ./main
main.c:7:14: warning: unknown action ‘ALIGN’ for ‘#pragma pack’ - ignored [-Wpragmas]
    7 | #pragma pack(ALIGN)
      |              ^~~~~
sizeof A: 8
</code></pre>

<p>测试后可以发现在不使用宏定义的情况下，结果是正确的,
但是如果使用宏定义，GCC编译时也会有相应的warning提示。</p>

<p>另外，如果在windows上采用MSVC编译器，即使使用宏定义，结果也会是正确的。</p>]]></content><author><name>Jiandong Qiu</name><email>qiujiandong1998@gmail.com</email><uri>https://qiujiandong.github.io</uri></author><category term="GCC" /><category term="GCC" /><category term="MSVC" /><summary type="html"><![CDATA[#pragma pack()括号中的参数如果是一个宏, 那么在GCC和MSVC中处理的方式不一样.]]></summary></entry></feed>