SICP_Chapter1 Building Abstractions with Functions

1.2 Elements of Programming 1.2.6 The Non-Pure Print Function >>> print(print(1), print(2)) 1 2 None None 注意 print(1)和 print(2)的返回值是 None。 1.4 Designning Functions 1.4.1 Documentation docstring。在函数名后在``````中描述函数信息。第一行用来描述函数,接着空一行,接着可以描述参数等。 help(pressure)可以查看函数帮助信息。 >>> def pressure(v, t, n): """Compute the pressure in pascals of an ideal gas. Applies the ideal gas law: http://en.wikipedia.org/wiki/Ideal_gas_law v -- volume of gas, in cubic meters t -- absolute temperature in degrees kelvin n -- particles of gas """ k = 1....

2024-01-22 · 2 min

xv6_chapter3 Page tables

3.1 Paging hardware xv6 runs on Sv39 RISC-V, 使用低39位来表示虚拟内存, 高25位没有使用。 39位中27位用作index来寻找PTE(Page table entry), 低12位表示在某个页表中的偏移地址, 正好对应4KB。每个PTE包含44bits的PPN(physical page number)和一些控制位。 实际的RISC-V CPU翻译虚拟地址到物理地址使用了三层。每层page table存储512个PTE,分别使用9个bit来索引。上一层的一个PTE的PPN对应下一层Page table地址。所以总共有512*512*512=2^27 PTE。每个pte占8bytes,所以需要占用的内存最多是2^30=1G, 因为没有访问到的pte是不会分配pagetable的,所以实际占用的内存会更少。 每个CPU需要把顶层的page directory物理地址加载到 satp 寄存器中, 第一个Page Directory的地址是已知的。 然后通过L2索引到第一个Page directory的PTE,读出PTE的PPN, 即第二个Page directory的起始物理地址。再根据L1索引到第二个Page directory的PTE, 以此类推。 只有最后一级pte会设置除了PTE_V以外的其他位,其他层级的pte只设置PTE_V。 3.2 Kernel address space // TODO: replace this image 上图PHYSTOP为0x88000000, 见memlayout.h QEMU模拟RAM从0x80000000物理地址开始,至多到0x80000000+128*1024*1024=0x88000000,xv6称这个地址为PHYSTOP。 Kernel使用RAM和device registers是直接映射的,虚拟地址和物理地址相等。 不过有一部分kernel虚拟地址不是直接映射的: Trampoline page. 在虚拟地址的最顶部。这边有意思的是物理内存中的trampoline code被映射到了两个地方,一个对应直接映射的虚拟内存中的kernel text,另一个是虚拟地址最顶部地址的一个page size。有关Trampoline page请参考第四章。 Kernel stack pages. 每个进程都有自己的kernel stack。如果访问超过了自己的kernel stack。会有guard page保护,guard page的PTE valid位置为0,导致访问异常。 3.3 Code: creating an address space TLB....

2024-01-11 · 2 min

xv6_chapter2 Operating system organization

2.1 Abstracting physical resources 2.2 User mode, supervisor mode, and system calls 2.3 Kernel Organization monolithic kernel vs microkernel xv6 和 Unix-like OS 属于 monolithic kernel. 2.4 Code: xv6 organization kernel 接口都在 def.h 中声明. 2.5 Process overview 进程的地址空间: RISC-V 64 只使用 64 位地址中的 39 位, 所以用户空间和内核空间各占 2^39 字节. xv6 只使用 39 位地址中的 38 位, 所以最大地址为 2^38 - 1 = 0x3fffffffff, MAXVA 在 riscv.h 中定义. 每个进程都有两个栈, 一个是内核栈, 一个是用户栈. 2.6 Code: starting xv6, the first process and system call bootloader 把 xv6 kernel code 加载到内存 0x80000000 地址, 首先执行的代码为entry....

2024-01-04 · 2 min

xv6_lab2 System calls

Trace 实现系统调用int trace(int mask);, 当调用mask中包含的系统调用号,打印出来。 在Makefile中增加trace用户程序的编译 UPROGS=\ ... $U/_trace 在user/user.h中增加函数声明 int trace(int); 在usys.pl中增加user space trace函数的入口。可以看到user space调用的系统调用, 是由这个脚本生成的函数。 以trace为例, 提供了trace函数的入口.global trace, 随后将定义在syscall.h中的SYS_trace编号存入寄存器a7, 通过ecall命令进入内核态。 sub entry { my $name = shift; print ".global $name\n"; print "${name}:\n"; print " li a7, SYS_${name}\n"; print " ecall\n"; print " ret\n"; } entry("trace"); 在syscall.c的syscall函数中通过获取a7寄存器中的编号,找到我们添加的系统调用函数,sys_trace。 函数具体实现如下: // 在proc结构体中增加mask成员 struct proc { //... int mask; } // 将user space传入的mask,传递给当前进程的mask变量 uint64 sys_trace(void) { if(argint(0, &myproc()->mask) < 0) return -1; return 0; } // 随后执行的系统调用number如果 (1 << num == mask), 则打印 syscall(void) { // ....

2024-01-04 · 1 min

xv6_chapter1 Operating system interfaces

XV6 实现的所有系统调用: int fork() //Create a process, return child’s PID. void exit(int status) //Terminate the current process; status reported to wait(). No return. int wait(int *status) //Wait for a child to exit; exit status in *status; returns child PID. int kill(int pid) //Terminate process PID. Returns 0, or -1 for error. int getpid() //Return the current process’s PID. int sleep(int n) //Pause for n clock ticks. int exec(char *file, char *argv[]) //Load a file and execute it with arguments; only returns if error....

2023-12-30 · 3 min

Zephyr -- Pinctrl Subsystem

Device Tree Pinctrl controller节点: 所有可选的支持属性可以查阅/dts/bindings/pinctrl/pincfg-node.yaml,支持配置上下拉,驱动能力等等。具体支持属性要参考soc的yaml文件/dts/bindings/pinctrl/xxx.yaml 当设备节点调用perip0_default的时候,group1~N都会被apply。 /* board-pinctrl.dtsi */ #include <vnd-soc-pkgxx.h> &pinctrl { /* Node with pin configuration for default state */ periph0_default: periph0_default { group1 { /* Mappings: PERIPH0_SIGA -> PX0, PERIPH0_SIGC -> PZ1 */ pinmux = <PERIPH0_SIGA_PX0>, <PERIPH0_SIGC_PZ1>; /* Pins PX0 and PZ1 have pull-up enabled */ bias-pull-up; }; ... groupN { /* Mappings: PERIPH0_SIGB -> PY7 */ pinmux = <PERIPH0_SIGB_PY7>; }; }; }; 使用pinctrl的设备节点: &uart0 { pinctrl-0 = <&uart0_default>; pinctrl-1 = <&uart0_sleep>; pinctrl-names = "default", "sleep"; }; 默认支持default, sleep两种属性,也可以自定义属性,比如slow, fast,这样需要在具体driver中自定义PINCTRL_STATE_XXX, 比如...

2023-12-25 · 3 min

Zephyr -- System Call

C Prototype 系统调用函数必须以__syscall开头,在include/或SYSCALL_INCLUDE_DIRS目录下被声明。 scripts/build/parse_syscalls.py会parse__syscall这个标记,并且函数有以下限制: 参数不能传入数组,比如int foo[] or int foo[12], 必须用int *foo代替。 函数指针不能正常解析,需要先对函数指针typedef,再传入。 定义了系统调用的xxx.h头文件必须要文件末尾加上同名的#include <syscall/xxx.h>。 Invocation Context 调用上下文 如果定义了CONFIG_USERSPACE,所有的system call APIs都会直接调用对应的implementation function。 如果定义了__ZEPHYR_SUPERVISOR__,表示所有code都在supervisor mode下运行,直接调用对应的implementation function。 如果定义了__ZEPHYR_USER__ Implementation Detail 实现细节 scripts/build/gencalls.py会把parse到的system calls放进/build/include/generated/syscall_list.h中。以K_SYSCALL_前缀加上API。 所有可以调用的system calls被保存在syscall_dispatch.c中的_k_syscall_table中。 system calls的API实现被保存在了/build/include/generated/syscalls/xxx.h 这些文件被/include/xxx.h文件最后include进去,完成函数的定义。e.g // include/i2c.h __syscall int i2c_configure(const struct device *dev, uint32_t dev_config); //声明 static inline int z_impl_i2c_configure(const struct device *dev, uint32_t dev_config) { const struct i2c_driver_api *api = (const struct i2c_driver_api *)dev->api; return api->configure(dev, dev_config); } //... #include <syscalls/i2c....

2023-12-18 · 1 min

Zephyr -- SPI framework

DeviceTree spi0: spi@f0020000 { compatible = "snps,designware-spi"; #address-cells = <1>; #size-cells = <0>; reg = <0xf0020000 0x100>; interrupts = <40 1>; pinctrl-0 = <&spi2_default>; pinctrl-1 = <&spi2_sleep>; pinctrl-names = "default", "sleep"; fifo-depth = <32>; // 必须参数 clocks = <&sysclk>; // 和下面clock-frequency二选一,优先从&sysclk中获取clock-frequency clock-frequency = <200000000>; cs-gpios = <&gpio0 18 GPIO_ACTIVE_LOW>; // 用gpio代替cs,可选参数 status = "disabled"; slow@0 { // spi设备 compatible = "test-spi-loopback-slow"; reg = <0>; spi-max-frequency = <500000>; }; fast@0 { // @x 是指第几根片选 compatible = "test-spi-loopback-fast"; reg = <0>; // 0表示spi master controller,1表示spi slave controller spi-max-frequency = <1000000>; }; }; Consumer(应用层和其他driver如何调用spi接口) 方法1:...

2023-12-15 · 3 min

Zephyr -- I2C framework

Device Tree Consumer(应用层和其他driver如何调用i2c接口) 方法1: 设备树API // 获取i2c device node id #define I2C_DEV DT_COMPAT_GET_ANY_STATUS_OKAY(test_i2c) // i2c deivce的compatible,不是i2c bus // 通过I2C_DT_SPEC_GET宏从设备树获得i2c_dt_spec结构体 struct i2c_dt_spec { const struct device *bus; uint16_t addr; }; #define I2C_DT_SPEC_GET_ON_I2C(node_id) \ .bus = DEVICE_DT_GET(DT_BUS(node_id)), \ .addr = DT_REG_ADDR(node_id) static struct i2c_dt_spec i2c = I2C_DT_SPEC_GET(I2C_DEV); 可以调用i2c_xxx_dt进行i2c传输了 static inline int i2c_write_dt(const struct i2c_dt_spec *spec, const uint8_t *buf, uint32_t num_bytes) static inline int i2c_read_dt(const struct i2c_dt_spec *spec, uint8_t *buf, uint32_t num_bytes) static inline int i2c_write_read_dt(const struct i2c_dt_spec *spec, const void *write_buf, size_t num_write, void *read_buf, size_t num_read) 方法2:...

2023-12-15 · 3 min

Vim, Tmux, and Vim plugins

VIM Basic Command-line :e {name of file} open file for editing :ls show open buffers :help {topic} open help :help :w opens help for the :w command :help w opens help for the w movement Movement Movements in Vim are also called “nouns”. Basic movement: hjkl (left, down, up, right) Words: w (next word), b (beginning of word), e (end of word) Lines: 0 (beginning of line), ^ (first non-blank character), $ (end of line) Screen: H (top of screen), M (middle of screen), L (bottom of screen) Scroll: Ctrl-u (up), Ctrl-d (down) File: gg (beginning of file), G (end of file) Line numbers: :{number}<CR> or {number}G (line {number}) Misc: % (corresponding item) Find: f{character}, t{character}, F{character}, T{character} find/to forward/backward {character} on the current line , / ; for navigating matches Search: /{regex}, n / N for navigating matches Edits Vim’s editing commands are also called “verbs”...

2023-12-13 · 4 min