risc-v Lookup

CSRs 参考riscv-privileged-20211203.pdf sstatus SPP: 在进入S mode之前hart处于什么mode。 用户态trap进入S mode,SPP被设置为0,其他情况为1。 如果SPP为0,执行SRET后,返回U mode。如果SPP为1,执行SRET后,返回S mode。随后SPP置0。 SIE: 1开启/0关闭 S mode的所有中断 in S mode。 在U mode时,SIE被忽略,S mode 的中断都是打开的。 SPIE: 当trap进入S mode,SIE会置0禁止S mode所有中断,SIE的旧值会保存到SPIE中。 执行SRET后,SIE被设置为SPIE中之前保存的旧值,SPIE置1。 stvec Supervisor Trap Vector Base Address Register, 保存S mode异常/中断的跳转地址。 MODE为0,直接跳转到BASE;MODE为1,跳转到BASE + cause * 4。 在linux entry.S中的做法为直接设置stvec为handle_exception地址,地址的后两位肯定是4bytes对齐的,所以为00。跳转到handle_exception后,分开处理中断、系统调用、异常。根据异常cause再跳转到不同的异常处理函数。 SIP SIE Supervisor Interrupt pending/enable Registers SIP中断挂起(待处理的中断)和SIE中断使能。每一位代表的中断与scause中的为每个中断分配的异常码一致。 当sstatus.SIE=1, SIP[x]=1, SIE[x]=1,表示系统能够处理某个中断。 SIP中有的位是只读的,不能通过直接写0来清除: SEIP is read-only in sip, and is set and cleared by the execution environment, typically through a platform-specific interrupt controller....

2023-05-26 · 1 min

Spi Subsystem

1. SPI协议介绍 CPOL:表示SPICLK的初始电平,0为低电平,1为高电平 CPHA:表示相位,即第一个还是第二个时钟沿采样数据,0为第一个时钟沿,1为第二个时钟沿 CPOL CPHA 模式 含义 0 0 0 SPICLK初始电平为低电平,在第一个时钟沿采样数据 0 1 1 SPICLK初始电平为低电平,在第二个时钟沿采样数据 1 0 2 SPICLK初始电平为高电平,在第一个时钟沿采样数据 1 1 3 SPICLK初始电平为高电平,在第二个时钟沿采样数据 我们常用的是模式0和模式3,因为它们都是在上升沿采样数据,不用去在乎时钟的初始电平是什么,只要在上升沿采集数据就行。 2. SPI driver 2.2 spi_controller include/linux/spi.h 2.3 spi_device include/linux/spi.h 2.4 spi_transfer、spi_message 在SPI子系统中,用spi_transfer结构体描述一个传输,用spi_message管理整个传输。可以构造多个spi_transfer结构体,把它们放入一个spi_message里面。 SPI传输时,发出N个字节,就可以同时得到N个字节。 即使只想读N个字节,也必须发出N个字节:可以发出0xff 即使只想发出N个字节,也会读到N个字节:可以忽略读到的数据。 3. SPI 设备树处理 SPI Master 必须的属性如下: address-cells:这个SPI Master下的SPI设备,需要多少个cell来表述它的片选引脚 size-cells:必须设置为0 compatible:根据它找到SPI Master驱动 可选的属性如下: cs-gpios:SPI Master可以使用多个GPIO当做片选,可以在这个属性列出那些GPIO num-cs:片选引脚总数 SPI Device 必须的属性如下: compatible:根据它找到SPI Device驱动 reg:用来表示它使用哪个片选引脚 spi-max-frequency:必选,该SPI设备支持的最大SPI时钟 可选的属性如下: spi-cpol:这是一个空属性(没有值),表示CPOL为1,即平时SPI时钟为低电平 spi-cpha:这是一个空属性(没有值),表示CPHA为1),即在时钟的第2个边沿采样数据 spi-cs-high:这是一个空属性(没有值),表示片选引脚高电平有效 spi-3wire:这是一个空属性(没有值),表示使用SPI 三线模式 spi-lsb-first:这是一个空属性(没有值),表示使用SPI传输数据时先传输最低位(LSB) spi-tx-bus-width:表示有几条MOSI引脚;没有这个属性时默认只有1条MOSI引脚 spi-rx-bus-width:表示有几条MISO引脚;没有这个属性时默认只有1条MISO引脚 spi-rx-delay-us:单位是毫秒,表示每次读传输后要延时多久 spi-tx-delay-us:单位是毫秒,表示每次写传输后要延时多久 示例:...

2023-05-22 · 2 min

Rust

1.2 Even Better TOML,支持 .toml 文件完整特性 Error Lens, 更好的获得错误展示 One Dark Pro, 非常好看的 VSCode 主题 CodeLLDB, Debugger 程序 1.3 Cargo cargo new hello_world tree cargo run is equal to cargo build + ./target/debug/world_hello 默认的是debug模式,编译器不会做任何的优化,编译速度快,运行慢。 高性能模式,生产发布模式: cargo run --release cargo build --release cargo check 检查编译能否通过 cargo.toml 项目数据描述文件 cargo.lock 项目依赖详细清单 在cargo.toml中定义依赖的三种方式: [dependencies] rand = "0.3" hammer = { version = "0.5.0"} color = { git = "https://github.com/bjz/color-rs" } geometry = { path = "crates/geometry" } 2....

2023-05-20 · 3 min

Linux Essentials

编译 使用 LLVM 编译内核 (需要安装 llvm 工具链): make mrproper make LLVM=1 ARCH=arm defconfig make LLVM=1 ARCH=arm Image -j12 生成 compile_commands.json: cd scripts/clang-tools/ ./gen_compile_commands.py -d ../.. # linux根目录 mv compile_commands.json ../.. NOT indexed include <asm/xxx.h> 先找 arch/xxx/include/xxx.h,没有的话就找/include/asm-generic/xxx.h 从 dts 中获取 regs 地址并映射到 virtual address: linux5.10: void __iomem devm_platform_get_and_ioremap_resource(struct platform_devicepdev, unsigned int index, struct resource **res) linux5.4: void __iomem devm_platform_ioremap_resource(struct platform_devicepdev, unsigned int index) 相当于 platform_get_resource + devm_request_mem_region + devm_ioremap linux 链表相关操作:...

2023-05-19 · 1 min

uCore_Chapter3 多道程序与分时多任务

分时多任务系统与抢占式调度 RISC-V架构中断 以内核所在的 S 特权级为例,中断屏蔽相应的 CSR 有 sstatus 和 sie 。sstatus 的 sie 为 S 特权级的中断使能,能够同时控制三种中断,如果将其清零则会将它们全部屏蔽。即使 sstatus.sie 置 1 ,还要看 sie 这个 CSR,它的三个字段 ssie/stie/seie 分别控制 S 特权级的软件中断、时钟中断和外部中断的中断使能。 当 Trap 发生时,sstatus.sie 会被保存在 sstatus.spie 字段中,同时 sstatus.sie 置零,这也就在 Trap 处理的过程中屏蔽了所有 S 特权级的中断; 当 Trap 处理完毕 sret 的时候, sstatus.sie 会恢复到 sstatus.spie 内的值。 也就是说,如果不去手动设置 sstatus CSR ,在只考虑 S 特权级中断的情况下,是不会出现 嵌套中断 (Nested Interrupt) 的。 嵌套中断可以分为两部分:在处理一个中断的过程中又被同特权级/高特权级中断所打断。默认情况下硬件会避免前一部分,也可以通过手动设置来允许前一部分的存在;而从上面介绍的规则可以知道,后一部分则是无论如何设置都不可避免的。 时钟中断与计时器 计数器保存在一个 64 位的 CSR mtime 另外一个 64 位的 CSR mtimecmp 的作用是:一旦计数器 mtime 的值超过了 mtimecmp,就会触发一次时钟中断。这使得我们可以方便的通过设置 mtimecmp 的值来决定下一次时钟中断何时触发。...

2023-05-17 · 2 min

Uart Subsystem

Reference https://www.kernel.org/doc/Documentation/serial/driver Introduction 波特率115200,bps每秒传输的bit数。 每一位1/115200秒,传输1byte需要10位(start, data, stop),那么每秒能传11520byte。 115200,8n1。8:data,n:校验位不用,1:停止位。 TTY体系中设备节点的差别 不关心终端是真实的还是虚拟的,都可以通过/dev/tty找到当前终端。 /dev/console 内核的打印信息可以通过cmdline来选择打印到哪个设备。 console=ttyS0 console=tty console=ttyS0时,/dev/console就是ttyS0 console=ttyN时,/dev/console就是/dev/ttyN console=tty时,/dev/console就是前台程序的虚拟终端 console=tty0时,/dev/console就是前台程序的虚拟终端 console有多个取值时,使用最后一个取值来判断。 /dev/tty 和/dev/tty0区别 /dev/tty表示当前进程的控制终端,也就是当前进程与用户交互的终端。 /dev/tty0则是当前所使用虚拟终端的一个别名 Linux串口应用编程 https://digilander.libero.it/robang/rubrica/serial.htm struct termios options; open("/dev/ttyS1", O_RDWR | O_NOCTTY | O_NDELAY)// O_NOCTTY: 不用作控制终端 O_NDELAY: 使I/O变成非阻塞模式 fcntl(fd, F_SETFL, 0): //读数据时,没有数据则阻塞等待 fcntl(fd, F_SETFL, FNDELAY): //读数据时不等待,没有数据就返回0 /* c_cflag: Control Options */ options.c_cflag |= (CLOCAL | CREAD); // 必须打开 Enable the receiver and set local mode options.c_cflag &= ~CSIZE; /* Mask the character size bits */ options....

2023-05-15 · 3 min

GPIO Subsystem

https://zhuanlan.zhihu.com/p/41942876 push pull推挽输出 推挽输出的最大特点是可以真正能真正的输出高电平和低电平,在两种电平下都具有驱动能力。 open drain开漏输出 open source开集输出 这两种输出的原理和特性基本是类似的,区别在于一个是使用MOS管,其中的"漏"指的就是MOS管的漏极;另一个使用三极管,其中的"集"指的就是MOS三极管的集电极。这两者其实都是和推挽输出相对应的输出模式,由于使用MOS管的情况较多,很多时候就用"开漏输出"这个词代替了开漏输出和开集输出。 开漏、开集输出最主要的特性就是高电平没有驱动能力,需要借助外部上拉电阻才能真正输出高电平。 Kernel doc: General Purpose Input/Output (GPIO) GPIO Driver Interface Controller Drivers: gpio_chip struct gpio_chip 抽象gpio controller。 gpiochip_add_data() or devm_gpiochip_add_data()接口用来注册gpio controller。gpiochip_remove()释放gpio controller。 gpiochip_is_request()在gpio controller driver中用于检测某个gpio是否被其他chip占用,没占用返回NULL,占用返回request时传入的string。 GPIO electrical configuration gpio_chip的.set_config回调用于设置: Debouncing Single-ended modes (open drain/open source) Pull up and pull down resistor enablement 这些属性可以在dts中指定,include/dt-bindings/gpio/gpio.h GPIO_PUSH_PULL GPIO_LINE_OPEN_SOURCE GPIO_OPEN_DRAIN … 可以设置为gpiochip_generic_config()会调用到pinctrl_gpio_set_config()->ops->pin_config_set GPIO drivers providing IRQs gpiod_to_irq(); // 传入gpio_desc,返回gpio的irq number(软件映射的,不是irq hw id) gpio_chip_hwgpio(); gc->to_irq(); rts_gpio_to_irq(); irq_linear_revmap(); Cascaded GPIO irqchips CHAINED CASCADED GPIO IRQCHIPS:挺多soc上是这种做法,打开CONFIG_GPIOLIB_IRQCHIP设置girq->parent_handler。gpio controller注册过程中通过irq_set_chained_handler设置中断处理函数,因此在中断处理函数中需要chained_irq_enter,chained_irq_exit。相当于级联中断处理器的做法。 static irqreturn_t foo_gpio_irq(int irq, void *data) /// 中断处理函数 chained_irq_enter(....

2023-05-12 · 2 min

CMA&DMA

Reserved-memory 参考:http://www.wowotech.net/memory_management/cma.html https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18841683/Linux+Reserved+Memory?view=blog /Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt 定义了 no-map 属性的,不会自动映射到虚拟地址,需要自行在 driver 中映射。 // dts reserved: buffer@0x38000000 { no-map; reg = <0x38000000 0x08000000>; }; /* Get reserved memory region from Device-tree */ np = of_parse_phandle(dev->of_node, "memory-region", 0); rc = of_address_to_resource(np, 0, &r); lp->paddr = r.start; lp->vaddr = memremap(r.start, resource_size(&r), MEMREMAP_WB); 定义"shared-dma-pool" 就可以创建 DMA memory pool,使用 DMA engine API 了。of_reserved_mem_device_init 中会帮我们创建映射。(DMA 在 of_reserved_mem_device_init 阶段会进行 memremap 同上,这个 remap 不是直接映射的方式)。 // dts reserved: buffer@0 { compatible = "shared-dma-pool"; no-map; reg = <0x0 0x70000000 0x0 0x10000000>; }; /* Initialize reserved memory resources */ rc = of_reserved_mem_device_init(dev); /* Allocate memory */ dma_set_coherent_mask(dev, 0xFFFFFFFF); lp->vaddr = dma_alloc_coherent(dev, ALLOC_SIZE, &lp->paddr, GFP_KERNEL); log:...

2023-05-10 · 4 min

Pinctrl Subsystem

/sys/kernel/debug/pinctrl 其他驱动调用pinctrl子系统: #include <linux/pinctrl/consumer.h> static struct pinctrl *xxx_pinctrl; struct pinctrl_state *default_state = NULL; // dts pinctrl-0 = <&state1> pinctrl-1 = <&state2> 1. /* 获取pin control state holder 的句柄 */ pinctrl = devm_pinctrl_get(dev); 2. /* 得到名字为state1和state2对应的pin state */ **struct** pinctrl_state * turnon_tes = pinctrl_lookup_state(pinctrl, "state1"); **struct** pinctrl_state * turnoff_tes = pinctrl_lookup_state(pinctrl, "state2"); 3. pinctrl_select_state(pinctrl, turnon_tes)。 devm_pinctrl_get(struct device *dev) //返回一个pinctrl句柄 pinctrl_get(struct device *dev) find_pinctrl(struct device *dev) // 查看是否device core已经创建了该pinctrl句柄 create_pinctrl(struct device *dev, struct pinctrl_dev *pctldev) pinctrl_dt_to_map(struct pinctrl *p, struct pinctrl_dev *pctldev) //从设备树中获取信息保存到pinctrl_map结构体中 for (state = 0; ; state++) { propname = kasprintf(GFP_KERNEL, "pinctrl-%d", state); //查找pinctrl-0,1,2属性 //size保存了pinctrl-0中phandle的个数,比如有的节点pinctrl-0 = <&x1, &x2> prop = of_find_property(np, propname, &size); list = prop->value; //list保存了phandle列表 //保存pinctrl-names index为state的name,比如pinctrl-names = "default" ret = of_property_read_string_index(np, "pinctrl-names", state, &statename); if (ret < 0) // 如果没有定义pinctrl-names属性,那么我们将pinctrl-0 pinctrl-1 pinctrl-2……中的那个ID取出来作为state name statename = prop->name + strlen("pinctrl-"); for (config = 0; config < size; config++) { phandle = be32_to_cpup(list++); np_config = of_find_node_by_phandle(phandle); // 找到pinctrl-x=<&x1, &x2>中的config节点 dt_to_map_one_config(p, pctldev, statename, np_config); np_pctldev = of_node_get(np_config); np_pctldev = of_get_next_parent(np_pctldev); // 找到pinctrl controller节点 pctldev = get_pinctrl_dev_from_of_node(np_pctldev); //找到pinctrl_dev //调用底层的callback函数处理pin configuration node。 ops->dt_node_to_map(pctldev, np_config, &map, &num_maps); ....

2023-05-10 · 2 min

GDB

Stepping step(s)/stepi(si) + <n>: 执行 n 行 c/assembly 代码, 会跳进函数。 next(n)/nexti(ni) + <n>: 执行 n 行 c/assembly 代码,不会跳进函数。 Running run/r: run code 直到遇到 breakpoint。程序跑完接着 run 会 restart 程序。 continue/c: 继续执行。 finish: run code 直到当前函数 return。 advance <location>: run code 直到指令到达。和设置 breakpoint 然后 Breakpoints break <location>: 设置断点,location 可以是内存地址"*0x7c00"或者名称"mon_backtrace", "monitor.c:71" break <location> if <condition>: 只有 condition 满足的时候才会 break。 cond <number> <condition>: 给某个断点增加 condition。 info breakpoints/b: 查看断点 delete <n>: 删除编号为 n 的断点 Watchpoints watch <expression>: 表达式值发生改变时,会停止执行指令。...

2023-05-08 · 2 min