Time Subsystem

CONFIG_GENERIC_CLOCKEVENTS:新的时间子系统 以下选项三选一: CONFIG_HZ_PERIODIC:无论何时,都启用用周期性的tick,即便是在系统idle的时候。 CONFIG_NO_HZ_IDLE:在系统idle的时候,停掉周期性tick。会同时enable NO_HZ_COMMON。 CONFIG_NO_HZ_FULL:即便在非idle的状态下,也就是说cpu上还运行在task,也可能会停掉tick。会同时enable NO_HZ_COMMON。 CONFIG_HIGH_RES_TIMERS:高精度timer。 如果配置了高精度timer,或者配置了NO_HZ_COMMON的选项,那么一定需要配置CONFIG_TICK_ONESHOT,表示系统支持支持one-shot类型的tick device。 sysfs接口 cd /sys/bus/clocksource/devices/clocksource0 cat current_clocksource: 查看当前的clocksource cat available_clocksource: 查看可用的clocksource echo xxx > current_clocksource: 设置clocksource cd /sys/bus/clockevents/devices/clockevent0 cat current_device: 查看当前的clockevent Clocksource Clockevent

2023-05-08 · 1 min

Interrupt Subsystem

IRQ domain http://www.wowotech.net/linux_kenrel/irq-domain.html 1. 向系统注册irq domain interrupt controller初始化的过程中,注册irq domain irq_domain_add_linear(struct device_node *of_node, unsigned int size, const struct irq_domain_ops *ops, void *host_data) 2. 为irq domain创建映射 在各个硬件外设的驱动初始化过程中,创建HW interrupt ID和IRQ number的映射关系。 方法1:irq_create_mapping(struct irq_domain *host, irq_hw_number_t hwirq); 比如drivers/clocksource/timer-riscv.c中irq_create_mapping(domain, RV_IRQ_TIMER);直接将hw id(RV_IRQ_TIMER)传入, 创建hw id和irq number的映射。 irq_create_mapping(domain, hwirq); irq_create_mapping_affinity(); irq_domain_alloc_descs(); // 创建hw id和irq number的映射 irq_domain_associate(); domain->ops->map(); //调用到interrupt controller的map函数 方法2:irq_of_parse_and_map. 需要在设备树中指定hw id。 比如drivers/irqchip/irq-realtek-plic.c中irq_of_parse_and_map irq_of_parse_and_map(struct device_node *dev, int index); of_irq_parse_one(dev, index, &oirq); // 解析设备树 irq_create_of_mapping(&oirq); irq_create_fwspec_mapping(); irq_create_mapping(domain, hwirq); // 最终还是调用到irq_create_mapping 方法3:外设driver中直接platform_get_irq...

2023-05-08 · 3 min

uCore_Chapter1 应用程序与基本执行环境

Notes make build # 仅编译 make run # 编译+运行qemu make run LOG=trace # 其他选项可以看Makefile make clean # rm build/ make debug # 编译+运行gdb调试 每次在make run之前,尽量先执行make clean以删除缓存,特别是在切换ch分支之后。 退出 qemu 的方法 如果是正常推出,uCore 会自动关闭 qemu,但如果 os 跑飞了,我们不能通过 Ctrl + C 来推出。此时可以先按下 Ctrl+A ,再按下 X 来退出 Qemu。 ch1 流程 // entry.S _entry la sp, boot_stack_top //设置堆栈 call main // main.c clean_bss(); printf("hello wrold!\n"); consputc(); console_putchar(int c); sbi_call(SBI_CONSOLE_PUTCHAR, c, 0, 0); 怎么用gdb调试?`file kernel`然后? Makefile流程分析 根据make run 打印的信息:...

2023-04-25 · 1 min

uCore_Chapter2 批处理系统

make -C user clean # 在os目录,相当于cd user;make clean;cd .. make clean # 或者在user目录 git checkout ch2 make user BASE=1 CHAPTER=2 make run make test BASE=1 # make test 会完成 make user 和 make run 两个步骤(自动设置 CHAPTER) 流程 main(); printf("hello wrold!\n"); trap_init(); // 设置中断/异常处理地址 w_stvec((uint64)uservec & ~0x3); //把uservec地址传入,uservec在trampoline.S中定义 asm volatile("csrw stvec, %0" : : "r"(x)); // 设置stvec CSR loader_init(); run_next_app(); load_app(); usertrapret(trapframe, (uint64)boot_stack_top); w_sepc(trapframe->epc); r_sstatus(); w_sstatus(); userret(); sret // 返回到sepc中的值,0x80400000第一个app 应用程序系统调用ecall进入内核异常处理过程 // 进入应用程序 exit(MAGIC); syscall(SYS_exit, code); __syscall1(n, long(1234)); __asm_syscall("r"(a7), "0"(a0)); ecall //通过ecall 进入uservec uservec usertrap(); // ld t0, 16(a0) jr t0 r_scause(); csrr %0, scause // 应用层调用了ecall指令,所以scause自动被设置为8 syscall(); //在uservec中应用层传入eid到寄存器a7,这里读a7来判断是什么system call id = trapframe->a7; usertrapret(); // 这里回到第九行一样,循环,执行第二个应用程序 分析下uservec,注意:这里只是把stvec设置为uservec地址,并不会执行uservec下的代码,要等U mode的中断/异常到来时才会从uservec开始执行。...

2023-04-25 · 1 min

U-Boot启动流程

Uboot编译流程 https://blog.csdn.net/ooonebook/article/details/53000893 编译生成的文件: 具体可以参考Uboot Makefile u-boot Makefile u-boot.cfg: $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.autoconf $(@) cfg: u-boot.cfg prepare2: prepare3 outputmakefile cfg prepare1: prepare2$(version_h) $(timestamp_h) $(dt_h) $(env_h) include/config/auto.conf archprepare: prepare1 scripts_basic prepare0: archprepare prepare:prepare0 scripts: scripts_basic scripts_dtc include/config/auto.conf $(u-boot-dirs): prepare scripts $(sort $(u-boot-init) $(u-boot-main)): $(u-boot-dirs) u-boot-init := $(head-y) u-boot-main := $(libs-y) u-boot-keep-syms-lto := keep-syms-lto.o u-boot.lds: $(LDSCRIPT) prepare u-boot: $(u-boot-init) $(u-boot-main) $(u-boot-keep-syms-lto) u-boot.lds u-boot-dtb.bin: u-boot-nodtb.bin dts/dt.dtb u-boot-nodtb.bin: u-boot dts/dt.dtb: u-boot u-boot.srec: u-boot u-boot.bin: u-boot-dtb.bin u-boot.sym: u-boot System....

2023-04-19 · 4 min

I2C Subsystem

用户层测试指令 # 检测当前系统有几组i2c总线 i2cdetect -l # 查看i2c-0接口上的设备 i2cdetect -y -a 0 # Force scanning of non-regular addresses i2cdetect -y -r 0 # 读取指定设备的全部寄存器的值 i2cdump -f -y 0 0x68 # 读取指定i2c设备的某个寄存器的值,如下读取i2c-0地址为0x68器件中的0x01寄存器 i2cget -f -y 0 0x68 0x01 # 写入指定i2c设备的某个寄存器的值,如下写入i2c-0地址为0x68器件中的0x01寄存器值为0x6f i2cset -f -y 0 0x68 0x01 0x6f # 写入i2c-0地址为0x50的eeprom,从偏移为0x64地址读8个byte。 i2ctransfer -f -y 0 w1@0x50 0x64 r8 I2C 基础知识 写操作 主芯片要发出一个start信号 然后发出一个设备地址(用来确定是往哪一个芯片写数据),方向(读/写,0表示写,1表示读) 从设备回应(用来确定这个设备是否存在),然后就可以传输数据 主设备发送一个字节数据给从设备,并等待回应 每传输一字节数据,接收方要有一个回应信号(确定数据是否接受完成),然后再传输下一个数据。 数据发送完之后,主芯片就会发送一个停止信号。 下图:白色背景表示"主→从",灰色背景表示"从→主" 读操作 I2C信号 I2C协议中数据传输的单位是字节,也就是8位。但是要用到9个时钟:前面8个时钟用来传输8数据,第9个时钟用来传输回应信号。传输时,先传输最高位(MSB)。 开始信号(S):SCL为高电平时,SDA山高电平向低电平跳变,开始传送数据。 结束信号(P):SCL为高电平时,SDA由低电平向高电平跳变,结束传送数据。 响应信号(ACK):接收器在接收到8位数据后,在第9个时钟周期,拉低SDA SDA上传输的数据必须在SCL为高电平期间保持稳定,SDA上的数据只能在SCL为低电平期间变化 I2C协议信号如下:...

2023-04-13 · 4 min

Buildroot

Buildroot User Manual https://buildroot.org/downloads/manual/manual.html https://www.cnblogs.com/fuzidage/p/12049442.html Chapter 4 Buildroot quick start make menuconfig make make menuconfig 进入选择菜单,可以选择编译 kernel, bootloader, rootfs。 编译完成后内容会放到output目录下,其中: image: 包括 kernel image, bootloader and root filesystem images build: host: staging: 软链接,指向/host/<toolchain>/sysroot target: 就是目标板的文件系统,和 staging 相比,developing files(header, etc.)被省略了,binaries are stripped, 去除了 debug info。 PARTⅡ User Guide Chapter 6 Buildroot configuration 6.1 Cross-compilation toolchain Buildroot 提供两种 toolchain: internal toolchain backend, Buildroot toolchain in menuconfig. external toolchain backend. External toolchain in menuconfig....

2023-04-13 · 10 min

PWM Subsystem

PWM 子系统 PWM原理 利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,其本质是一种对模拟信号电平进行数字编码的方法。在嵌入式设备中,PWM多用于控制马达、LED、振动器等模拟器件。 脉冲周期(T),单位是时间,ns, us ,ms。 脉冲频率(f),单位是赫兹(Hz),与脉冲周期成倒数关系,f=1/T。 脉冲宽度(W),简称“脉宽”,是脉冲高电平持续的时间。单位是时间,ns, us, ms。 占空比(D),脉宽除以脉冲周期的值。 W = ton T = ton + toff = 1/f D = ton / (ton+ toff) = ton / T PWM consumer /* include/linux/pwm.h */ int pwm_config(struct pwm_device *pwm, int duty_ns, int period_ns); int pwm_enable(struct pwm_device *pwm); void pwm_disable(struct pwm_device *pwm); int pwm_apply_state(struct pwm_device *pwm, const struct pwm_state *state); pwm_config,用于控制PWM输出信号的频率和占空比,其中频率是以周期(period_ns)的形式配置的,占空比是以有效时间(duty_ns)的形式配置的。 pwm_enable/pwm_disable,用于控制PWM信号输出与否。 pwm_apply_state需要定义一个pwm_state,可以一下子修改period/duty_cycle/polarity/enabled。 上面的API都以struct pwm_device类型的指针为操作句柄,该指针抽象了一个PWM设备,那么怎么获得PWM句柄呢?使用如下的API: /* include/linux/pwm.h */ struct pwm_device *pwm_get(struct device *dev, const char *con_id); struct pwm_device *of_pwm_get(struct device_node *np, const char *con_id); void pwm_put(struct pwm_device *pwm); struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id); struct pwm_device *devm_of_pwm_get(struct device *dev, struct device_node *np, const char *con_id); void devm_pwm_put(struct device *dev, struct pwm_device *pwm); pwm_get/devm_pwm_get,从指定设备(dev)的DTS节点中,获得对应的PWM句柄。可以通过con_id指定一个名称,或者会获取和该设备绑定的第一个PWM句柄。设备的DTS文件需要用这样的格式指定所使用的PWM device:...

2023-03-21 · 3 min

正则表达式

References RegexOne github正则表达式 需要转义的特殊字符: * . ? + $ ^ [ ] ( ) { } | \ / Tutorials Lesson 1 The 123s \d any single digit character \D any single non-digit character Lesson 2: The Dot . any single character \. period Lesson 3: Matching specific characters [abc] only a, b, c single character Lesson 4: Excluding specific characters [^abc] not a, b or c Lesson 5: Character ranges [a-z] characters a to z...

2023-01-12 · 2 min

RISC-V手册 第十章 RISC-V特权架构

10.1 导言 到目前为止,本书主要关注RISC-V 对通用计算的支持:我们引入的所有指令都在用户模式(User mode 应用程序的代码在此模式下运行)下可用。本章介绍两种新的权限模式:运行最可信的代码的机器模式(machine mode),以及为Linux,FreeBSD 和Windows 等操作系统提供支持的监管者模式(supervisor mode)。 图10.1是RISC-V 特权指令的图形表示,图10.2列出了这些指令的操作码。显然,特权架构添加的指令非常少。作为替代,几个新的控制状态寄存器(CSR)显示了附加的功能。 10.2 简单嵌入式系统的机器模式 机器模式(缩写为M模式, M-mode)是 RISC-V中(hart hardware thread,硬件线程)可以执行的最高权限模式。程)可以执行的最高权限模式。在M模式下运行的hart对内存,I/O和一些对于启动和配置系统来说必要的底层功能有着完全的使用权。因此它是唯一所有标准RISC-V处理器都必须实现的权限模式。实际上简单的 RISC-V微控制器仅支持 M模式。 机器模式最重要的特性是拦截和处理异常的能力。RISC-V将异常分为两类。一类是同步异常。另一类是中断,它是与指令流异步的外部事件,比如鼠标的单击。 在 M模式运行期间可能发生的同步异常有五种: 访问错误异常:当物理内存的地址不支持访问类型时发生(例如尝试写入 ROM)。 断点异常:在执行 ebreak指令,或者地址或数据与调试触发器匹配时发生。 环境调用异常:在执行 ecall指令时发生。 非法指令异常:在译码阶段发现无效操作码时发生。 非对齐地址异常:在有效地址不能被访问大小整除时发生,例如地址为0x12的amoadd.w。 有三种标准的中断源:软件、时钟和外部来源。软件中断通过向内存映射寄存器中存数来触发。 10.3 机器模式下的异常处理 八个控制状态寄存器(CSR)是机器模式下异常处理的必要部分: mtvec (Machine Trap Vector) 它保存发生异常时处理器需要跳转到的地址。 mepc (Machine Exception PC) 它指向发生异常的指令。 mcause (Machine Exception Cause) 它指示发生异常的种类。 mie (Machine Interrupt Enable) 它指出处理器目前能处理和必须忽略的中断。 mip (Machine Interrupt Pending) 它列出目前正准备处理的中断。 mtval (Machine Trap Value) 它保存了陷入 trap 的附加信息:地址例外中出错的地址、发生非法指令例外的指令本身,对于其他异常,它的值为 0。 mscratch (Machine Scratch) 它暂时存放一个字大小的数据。 mstatus (Machine Status) 它保存全局中断使能,以及许多其他的状态,如图10....

2023-01-09 · 2 min