编程作业中的学术诚信

本文是我自己对 Academic Integrity at MIT: Writing Code 的非官方中文翻译。本文已经得到了官方的邮件授权。 编写代码 Writing Code 与学术写作类似,当你在做课程项目的时候,如果使用了或者改编了其他人开发的代码,你必须要引用代码的来源。你可以在代码注释中引用代码来源。这些注释不仅保护了他人的劳动成果,也会帮助你理解代码和调试。 Writing code is similar to academic writing in that when you use or adapt code developed by someone else as part of your project, you must cite your source. However, instead of quoting or paraphrasing a source, you include an inline comment in the code. These comments not only ensure you are giving proper credit, but help with code understanding and debugging.

Read More

Archive 中 COMMON 符号的链接问题

背景 最近看到一个 issue: irssi 1.4.1 fails to build on darwin arm64,它的现象是,链接的时候会报错: Undefined symbols for architecture arm64: "_current_theme", referenced from: _format_get_text_theme in libfe_common_core.a(formats.c.o) _format_get_text in libfe_common_core.a(formats.c.o) _strip_codes in libfe_common_core.a(formats.c.o) _format_send_as_gui_flags in libfe_common_core.a(formats.c.o) _window_print_daychange in libfe_common_core.a(fe-windows.c.o) _printformat_module_dest_charargs in libfe_common_core.a(printtext.c.o) _printformat_module_gui_args in libfe_common_core.a(printtext.c.o) ... "_default_formats", referenced from: _format_find_tag in libfe_common_core.a(formats.c.o) _format_get_text_theme_args in libfe_common_core.a(formats.c.o) _printformat_module_dest_args in libfe_common_core.a(printtext.c.o) _printformat_module_gui_args in libfe_common_core.a(printtext.c.o) ld: symbol(s) not found for architecture arm64 代码 themes.c 定义了这两个全局变量: THEME_REC *current_theme; GHashTable *default_formats; 并且 themes.

Read More

NVIDIA 驱动和 CUDA 安装速查

背景 最近在 Ubuntu 上配置 NVIDIA 驱动和 CUDA 环境的次数比较多,在此总结一下整个流程,作为教程供大家学习。 配置 NVIDIA APT 源 Ubuntu 源有自带的 NVIDIA 驱动版本,但这里我们要使用 NVIDIA 的 APT 源。首先,我们要访问 https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=x86_64&Distribution=Ubuntu&target_version=20.04&target_type=deb_network,在网页中选择我们的系统,例如: Operating System: Linux Architecture: x86_64 Distribution: Ubuntu Version: 20.04 Installer Type: deb (network) 此时,下面就会显示一些命令,复制下来执行: wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-keyring_1.0-1_all.deb sudo dpkg -i cuda-keyring_1.0-1_all.deb sudo apt-get update 最后一步的 sudo apt-get -y install cuda 可以不着急安装,我们在后面再来讨论 CUDA 版本的问题。 NVIDIA 驱动 配置好源以后,接下来,我们就要安装 NVIDIA 驱动了。首先,我们要选取一个 NVIDIA 版本,选择的标准如下: 驱动版本需要支持所使用的显卡 驱动版本需要支持所使用的 CUDA 版本 这些信息在网络上都可以查到,也可以参考 NVIDIA 驱动和 CUDA 版本信息速查。 假如我们已经选择了要安装 470.

Read More

切换 ConnectX-4 为以太网模式

背景 最近在给机房配置网络,遇到一个需求,就是想要把 ConnectX-4 当成以太网卡用,它既支持 Infiniband,又支持 Ethernet,只不过默认是 Infiniband 模式,所以需要用 mlxconfig 工具来做这个切换。 切换方法 在 Using mlxconfig 文档中,写了如何切换网卡为 Infiniband 模式: $ mlxconfig -d /dev/mst/mt4103_pci_cr0 set LINK_TYPE_P1=1 LINK_TYPE_P2=1 Device #1: ---------- Device type: ConnectX3Pro PCI device: /dev/mst/mt4103_pci_cr0 Configurations: Next Boot New LINK_TYPE_P1 ETH(2) IB(1) LINK_TYPE_P2 ETH(2) IB(1) Apply new Configuration? ? (y/n) [n] : y Applying... Done! -I- Please reboot machine to load new configurations. 那么,我们只需要反其道而行之,设置模式为 ETH(2) 即可。 MST 安装 要使用 mlxconfig,就需要安装 MFT(Mellanox Firmware Tools)。我们用的是 Debian bookworm,于是要下载 DEB:

Read More

rsyslog 收集远程日志

背景 最近在运维的时候发现网络设备(如交换机)有一个远程发送日志的功能,即可以通过 syslog udp 协议发送日志到指定的服务器。为此,可以在服务器上运行 rsyslog 并收集日志。 rsyslog 配置 默认的 rsyslog 配置是收集系统本地的配置,因此我们需要编写一个 rsyslog 配置,用于收集远程的日志。 首先复制 /etc/rsyslog.conf 到 /etc/rsyslog-remote.conf,然后修改: 注释掉 imuxsock 和 imklog 相关的 module 加载 去掉 imudp 和 imtcp 相关的注释,这样就会监听在相应的端口上 修改 $WorkDirectory,例如 $WorkDirectory /var/spool/rsyslog-remote,防止与已有的 rsyslog 冲突 注释 $IncludeConfig,防止引入了不必要的配置 注释所有已有的 RULES 下面的配置 添加如下配置: $template FromIp,"/var/log/rsyslog-remote/%FROMHOST-IP%.log" *.* ?FromIp 这样,就会按照来源的 IP 地址进行分类,然后都写入到 /var/log/rsyslog-remote/x.x.x.x.log 文件里。 systemd service 最后,写一个 systemd service 让它自动启动: [Unit] ConditionPathExists=/etc/rsyslog-remote.conf Description=Remote Syslog Service [Service] Type=simple PIDFile=/var/run/rsyslogd-remote.pid ExecStart=/usr/sbin/rsyslogd -n -f /etc/rsyslog-remote.conf -i /var/run/rsyslogd-remote.

Read More

「教学」Wishbone 总线协议

背景 最近在研究如何把 Wishbone 总线协议引入计算机组成原理课程,因此趁此机会学习了一下 Wishbone 的协议。 总线 总线是什么?总线通常用于连接 CPU 和外设,为了更好的兼容性和可复用性,会想到能否设计一个统一的协议,其中 CPU 实现的是发起请求的一方(又称为 master),外设实现的是接收请求的一方(又称为 slave),那么如果要添加外设、或者替换 CPU 实现,都会变得比较简单,减少了许多适配的工作量。 那么,我们来思考一下,一个总线协议需要包括哪些内容?对于 CPU 来说,程序会读写内存,读写内存就需要以下几个信号传输到内存: 地址(addr):例如 32 位处理器就是 32 位地址,或者按照内存的大小计算地址线的宽度 数据(w_data 和 r_data):分别是写数据和读数据,宽度通常为 32 位 或 64 位,也就是一个时钟周期可以传输的数据量 读还是写(we):高表示写,低表示读 字节有效(be):例如为了实现单字节写,虽然 w_data 可能是 32 位宽,但是实际写入的是其中的一个字节 除了请求的内容以外,为了表示 CPU 想要发送请求,还需要添加 valid 信号:高表示发送请求,低表示不发送请求。很多时候,外设的速度比较慢,可能无法保证每个周期都可以处理请求,因此外设可以提供一个 ready 信号:当 valid=1 && ready=1 的时候,发送并处理请求;当 valid=1 && ready=0 的时候,表示外设还没有准备好,此时 CPU 需要一直保持 valid=1 不变,等到外设准备好后,valid=1 && ready=1 请求生效。 简单总结一下上面的需求,可以得到 master 和 slave 端分别的信号列表。这次,我们在命名的时候用 _o 表示输出、_i 表示输入,可以得到 master 端(CPU 端)的信号:

Read More

Nix Cookbook

背景 最近在尝试 NixOS 和在 macOS 上跑 Nix,下面记录一些我在使用过程中遇到的一些小问题和解决思路。 NixOS 全局配置 NixOS 的全局配置路径:/etc/nixos/configuration.nix 和 /etc/nixos/hardware-configuration.nix 应用更新后的全局配置: nixos-rebuild switch # or nixos-rebuild switch --upgrade 应用 Flakes 配置文件并显示变化: #!/usr/bin/env python3 import os user = os.getenv("USER") home = f"/nix/var/nix/profiles/" old = home + os.readlink(f"{home}system") os.system("sudo nixos-rebuild switch --flake .") new = home + os.readlink(f"{home}system") os.system(f"nix store diff-closures {old} {new}") 更新大版本 如果要更新 NixOS 21.11 到 22.05: nix-channel --list nix-channel --add https://nixos.org/channels/nixos-22.05 nixos nixos-rebuild switch --upgrade 可以考虑改或者不改 /etc/nixos/configuration.

Read More

在 libvirt 中运行 RISC-V 虚拟机

背景 我在 libvirt 中跑了几个 KVM 加速的虚拟机,然后突发奇想,既然 libvirt 背后是 qemu,然后 qemu 是支持跨指令集的,那是否可以让 libvirt 来运行 RISC-V 架构的虚拟机?经过一番搜索,发现可以跑 ARM:How To: Running Fedora-ARM under QEMU,既然如此,我们也可以试试用 libvirt 来运行 RV64 虚拟机。 准备 rootfs 第一步是根据 Debian 的文档 Creating a riscv64 chroot 来创建 rootfs,然后再用 virt-make-fs 来打包。 首先是用 mmdebstrap 来生成一个 chroot: $ sudo mkdir -p /tmp/riscv64-chroot $ sudo apt install mmdebstrap qemu-user-static binfmt-support debian-ports-archive-keyring $ sudo mmdebstrap --architectures=riscv64 --include="debian-ports-archive-keyring" sid /tmp/riscv64-chroot "deb http://deb.debian.org/debian-ports sid main" "deb http://deb.debian.org/debian-ports unreleased main" 进入 chroot 以后,进行一些配置:

Read More

「教学」异步 SRAM 时序

背景 在一些场合里,我们会使用异步的(即没有时钟信号的)外部 SRAM 来存储数据,而我们经常使用的很多外部接口都是同步接口(即有时钟信号的接口),比如 SPI 和 I2C 等等,UART 虽然是异步,但是它速度很低,不怎么需要考虑时序的问题。所以在 FPGA 上编写一个正确的异步 SRAM 控制器是具有一定的挑战的。 寄存器时序 考虑到读者可能已经不记得寄存器的时序了,这里首先来复习一下 setup 和 hold 的概念。如果你已经比较熟悉了,可以直接阅读下一节。 寄存器在时钟的上升沿(下图的 a)进行采样,为了保证采样的稳定性,输入引脚 D 需要在时钟上升沿之前 \(t_{su}\) 的时刻(下图的 b)到时钟上升沿之后 \(t_h\) 的时刻(下图的 c)保持稳定,输出引脚 Q 会在时钟上升沿之后 \(t_{cko}\) 的时刻(下图的 d)变化: 接口 首先我们来看看异步 SRAM 的接口。下文中,采用 IS61WV102416BLL-10TLI 和 AS7C34098A-10TCN 作为例子: 可以看到,它有 20 位的地址,16 位的数据,若干个控制信号,同时只能进行读或者写(简称 1RW)。它没有时钟信号,所以是异步 SRAM。 时序 对于一个同步接口,我们通常只需要给一个满足时钟周期的时钟,然后通过约束文件保证 setup 和 hold 条件满足即可。但是对于异步接口,由于输出的时候没有时钟,我们需要更小心地完成这件事情。 读时序 首先来看一下比较简单的读时序: 可以看到地址和数据的关系:首先是地址需要稳定 \(t_{RC}\) 的时间,那么数据合法的范围是地址稳定的初始时刻加上 \(t_{AA}\),到地址稳定的结束时刻加上 \(t_{OH}\)。我们再来看一下这几个时间的范围: 首先可以看到读周期时间 \(t_{RC}\) 至少是 10ns,这对应了型号中最后的数字,这表示了这个 SRAM 最快的读写速度。比较有意思的是 \(t_{AA}\) 最多是 10ns,刚好和 \(t_{RC}\) 的最小值相等。

Read More

「教学」ACE 缓存一致性协议

背景 最近几天分析了 TileLink 的缓存一致性协议部分内容,见TileLink 总线协议分析,趁此机会研究一下之前尝试过研究,但是因为缺少一些基础知识而弃坑的 ACE 协议分析。 下面主要参考了 IHI0022E 的版本,也就是 AXI4 对应的 ACE 版本。 回顾 首先回顾一下一个缓存一致性协议需要支持哪些操作。对于较上一级 Cache 来说,它需要这么几件事情: 读或写 miss 的时候,需要请求这个缓存行的数据,并且更新自己的状态,比如读取到 Shared,写入到 Modified 等。 写入一个 valid && !dirty 的缓存行的时候,需要升级自己的状态,比如从 Shared 到 Modified。 需要 evict 一个 valid && dirty 的缓存行的时候,需要把 dirty 数据写回,并且降级自己的状态,比如 Modified -> Shared/Invalid。如果需要 evict 一个 valid && !dirty 的缓存行,可以选择通知,也可以选择不通知下一级。 收到 snoop 请求的时候,需要返回当前的缓存数据,并且更新状态。 需要一个方法来通知下一级 Cache/Interconnect,告诉它第一和第二步完成了。 如果之前看过我的 TileLink 分析,那么上面的这些操作对应到 TileLink 就是: 读或写 miss 的时候,需要请求这个缓存行的数据(发送 AcquireBlock,等待 GrantData),并且更新自己的状态,比如读取到 Shared,写入到 Modified 等。 写入一个 valid && !

Read More