cisen / blog

Time waits for no one.
132 stars 20 forks source link

tock 源码相关 #840

Open cisen opened 4 years ago

cisen commented 4 years ago

总结

let ipc = kernel::ipc::IPC::new(board_kernel, &memory_allocation_capability);

    let launchxl = Platform {
        console,
        gpio,
        led,
        button,
        alarm,
        rng,
        i2c_master,
        ipc,
    };

    let chip = static_init!(cc26x2::chip::Cc26X2, cc26x2::chip::Cc26X2::new(HFREQ));

    extern "C" {
        /// Beginning of the ROM region containing app images.
        static _sapps: u8;
    }

    kernel::procs::load_processes(
        board_kernel,
        chip,
        &_sapps as *const u8,
        &mut APP_MEMORY,
        &mut PROCESSES,
        FAULT_RESPONSE,
        &process_management_capability,
    );

    board_kernel.kernel_loop(&launchxl, chip, Some(&launchxl.ipc), &main_loop_capability);

流程

编译安装

raspberry

architecture

process_memory_layout

processram

tock-stack

问答

文件系统在哪里?

没有线程?

进程是单线程的?

如何跟底层硬件链接?

什么叫MPU?

为什么回调比闭包好?又什么区别?

进程的回调是什么?为什么是这种结构?

进程是如何到指令的?

  1. app会被rust打包成elf文件(此时代码已经被编译为对应cpu的机器diamond)
  2. 使用elftab转换为tab文件
  3. 使用tockloader根据主板安装进各个板子指定的内存地址
  4. 启动系统,读取app的头,识别.txt,.data等执行对应的指令并执行

kernel是什么?代码在哪里?

kernel是先创建process在创建kernel?

capsules是什么?

进程在哪里创建和初始化的?

多线程是如何调用多核cpu的?

线程包含哪些必要的内存参数?

线程是如何切换的?

创建一个进程需要什么?

每个硬件比如led都占用一个线程?

现在每块板会创建多少个进程?

进程的数量是固定的,app是不固定的?

如何确定一段内存里面有几个app?

syscall做什么的?有什么用?

systick有什么用?怎么用?

tock系统是如何注入systick_handler的?

arch下面的SysTick有什么用?如何跟systick_handler关联

如何将systick和process联合在一起?

Cortex-M3 事实上有两个栈指针寄存器,分别为:

它们可以分别存储不同的值,且可以通过寄存器名 MSP 和 PSP 直接读写。寄存器名 SP 指代其中哪一个取决于当前 CONTROL 寄存器的 bit[1]。

tock是如何注入app?

app的开发流程是什么?

  1. 借助libtock引入timer/led等硬件驱动编写程序,如果不需要调硬件也可以不引入libtock-rs
  2. 编写完使用libtock-rs写好的make脚本编译出.tab文件,实际上还是使用cargo build --release --target=thumbv7em-none-eabi构建出应用(就是.bin文件,只是没带后缀),然后使用elf2tab转化为tab文件
  3. 使用libtock-rs自带的make将app 烧录进主板,或者tockloader也可以将tab文件烧录进主板

系统的启动/关闭/重启是如何在哪里实现的?

tock能否通过不重启来加载和启动app?就是添加一个flash进程把app写入flash

目前实现了哪些协议?

MUC内部实现了数据链路层?

IEEE802154的实现原理是什么?

TASKS_TXEN 0x000 Enable RADIO in TX mode  
TASKS_RXEN 0x004 Enable RADIO in RX mode  
TASKS_START 0x008 Start RADIO  
TASKS_STOP 0x00C Stop RADIO  
TASKS_DISABLE 0x010 Disable RADIO

IP协议是如何调用数据链路层的?

网络调用的原理是什么?

tock是如何调用cortex的MAC层进行通信的?

IEEE 802.15.4相关

IPC通信的原理是什么?

用户进程是如何跟内核进程通信的?

普通进程是如何订阅硬件的?比如button

进程死循环怎么处理?

如何知道硬件button按下了?

hard_fault是什么?

NVIC是在哪里跟GPIO绑在一起的?

第一次中断时如何插入的?

GPIO的中断是在哪里开始执行的?

nrf52的nvic在哪里?

什么是VolatileCell?

fired的实现在哪里?

fired调用的schedule函数在哪里?

fired的执行流程是什么?

GPIO中断的执行流程是什么?

上面的中断如何跟drive的subscribe关联的?

如何知道触发的中断类型的?

中断的初始化流程

zero_bss是做什么的?

内存布局时如何确定的?

如何读取和识别elf/tab文件的?

为什么要编译为机器代码而不是执行?

ids是如何被rust读取和解析的?

tockloader是如何安装程序的?

tock的程序不用在该系统里面编译?

tock的内存分页是如何做的?

tock写入页的流程是什么?

  1. 具体实现在chips\sam4l\src\flashcalw.rs,入口时write_page函数
  2. write_page函数复制缓存一份将要被写的数据到self.buffer,修改状态为WriteUnlocking(开始写操作),然后执行lock_page_region触发中断
  3. 执行中断函数handle_interrupt,匹配到状态WriteUnlocking,触发WriteErasing进行清空那一页的数据,并继续触发中断
  4. 中断匹配到WriteErasing,执行write_to_page_buffer将page的数据写入缓存中,写的实现是找到page的地址,然后进行指针偏移复制。设置状态为WriteWriting,并触发写入保护中断
  5. 中断匹配到WriteWriting,说明已经写完,清空缓存,设置状态为Ready,并执行写完成的kernel flash 回调。完成写操作 注意的是:
    • 这里的写都是根据PAGE_SIZE一次写入的,也就是根据PAGE_SIZE对齐的
    • flash的中断注入是在nvic::HFLASHC => flashcalw::FLASH_CONTROLLER.handle_interrupt(),

capsules\src\nonvolatile_to_pages.rs和capsules\src\nonvolatile_storage_driver.rs的功能分别是什么?

内核的页目录在哪里?

内核是如何分配内存给各个app的?

安装app和安装内核的openocd命令有何不同?

tock系统的安装流程是怎样的?

最后生成的.bin文件是怎样生成的?入口在哪里?

系统文件elf是怎样生成的?

进程是在哪里开始执行app里面的代码的?

process的callback是什么?

进程的执行在哪里?

Ring buffer跟task的组合有什么作用?

app是被插入PC寄存器队列的,kernel又是死循环的,芯片是单线程的,那app是如何在kernel循环中被执行的?

执行完用户app的代码,是如何切回kernel进程的?

内核态和用户态是如何切换的?

systick_handler的作用是什么?

arch\cortex-m\src\syscall.rsswitch_to_process做了什么?

一个process有多个taskQueue,一个taskQueue又有多个task?

用户进程app内申请的heap和stack是如何分配和隔离的?

libtock的timer是什么?怎么实现的?

capsules/src/virtual_alarm.rs是什么?有什么用?

libtock-rs的DRIVER_NUMBER是每个驱动固定的值?

// driver_number见capsules\src\driver.rs
// timer
const DRIVER_NUMBER: usize = 0x00000;
// temperature
const DRIVER_NUMBER: usize = 0x60000;
// led
const DRIVER_NUMBER: usize = 0x00002;

libtock里面使用SVC 1/2/3这种来调用内核硬件,这个1/2/3如何解决并发问题?

收到SVC后,在哪里开始调用硬件?

时钟如何倒计时的?

tock将时间处理分为时钟(alarm)和时间(time),各有什么区别?

NVIC硬件调用逻辑

  1. libtock-rs执行汇编触发NVIC中断:
    pub unsafe fn subscribe(
    major: usize,
    minor: usize,
    cb: *const unsafe extern "C" fn(usize, usize, usize, usize),
    ud: usize,
    ) -> isize {
    // 将结果code放到res返回
    let res;
    // // 将结果code放到res返回
    // 将major(driver_number)参数放入R0寄存器,minor(subscribe_number)放到R1寄存器
    // 回调放到R2,用户数据放到R3
    // driver_number见capsules\src\driver.rs
    llvm_asm!("svc 1" : "={r0}"(res)
                 : "{r0}"(major) "{r1}"(minor) "{r2}"(cb) "{r3}"(ud)
                 : "memory"
                 : "volatile");
    res
    }
  2. tock中断执行svc_handler将R0寄存器SYSCALL_FIRED设置为1
  3. 继续内核循环do_process,通过process.switch_to();发现SYSCALL_FIRED是1,需要切换到syscall
  4. switch_to_process函数读取寄存器的参数和arguments_to_syscall函数获得syscall的类型
  5. 继续内核循环do_process根据syscall的类型比如:Syscall::SUBSCRIBE和传入的driver_num,进程id创建订阅回调,完成订阅对应硬件的capsule

NVIC订阅如何返回硬件结果?调用是在哪里?

时钟是在哪里初始化的?如何设置的?

cpu如何知道那个中断被订阅了?比如时钟中断,怎样使能时钟中断?

components/capsules/chips的驱动有何区别?

COMMAND,SUBSCRIBE,ALLOW有什么区别?

如何输入调试信息

测试项目tests文件在哪里?

像print这种在本机的terminal输出信息的是如何实现的?

栈的生长方向是如何确定的?

栈的大小是如何确定的?

data的大小是如何确定的?

heap和stack之间隔着data,app_heap_start是如何确定的?默认是怎样的?

app的启动链接脚本是如何在哪里启动的?

grant的是做什么用的?为什么是1024?为什么放到内存顶部?

let ctr_ptr = process.grant_ptr(self.grant_num) as mut mut T; // If the pointer at that location is NULL then the grant // memory needs to be allocated. let new_grant = if (ctr_ptr).is_null() { process .alloc(size_of::(), align_of::()) .map(|root_arr| { let root_ptr = root_arr.as_mut_ptr() as mut T; // Initialize the grant contents using ptr::write, to // ensure that we don't try to drop the contents of // uninitialized memory when T implements Drop. write(root_ptr, Default::default()); // Record the location in the grant pointer. write_volatile(ctr_ptr, root_ptr); root_ptr }) } else { Some(*ctr_ptr) };



**app rust代码里面申请的堆和栈是如何映射到系统的内存分配的?**
- [rust内部只使用栈分配](https://github.com/cisen/blog/issues/763)
- rust只使用栈,堆需要用户自己申请,而自己申请只能调用tock系统的syscall(4)来申请堆

**虚拟内存跟进程有何关系?sp指针指向的虚拟内存还是真实内存?**
- 内存控制的关键[MPU](https://github.com/cisen/blog/issues/978)
- https://www.cnblogs.com/shijingjing07/p/5611579.html
- tock没有虚拟内存,sp指针指向真实内存

**sp指针代表栈指针?**
- 是的

**mpu是如何判断野指针访问内存的?**
- https://blog.csdn.net/u010961173/article/details/102380900
- 操作系统可以为这些域分配更多的属性:访问权限、cache和写缓存。存储器基于当时的处理器模式(管理模式或用户模式)可以设定这些区域的访问权限为读/写、只读和不可访问。
- 当处理器访问主存的一个域时,MPU比较该域的访问权限属性和当时的处理器模式。如果请求符合域的访问标准,则MPU允许内核读/写主存;如果存储器请求不符号域的访问标准,将产生一个异常信号。

**访问权限、cache和写缓存分别是什么意思?**
- https://blog.csdn.net/ldld1717/article/details/50557129
到2000年,DRAM部件每片的容量到达256Mbit,随机访问速率在30MHz左右。微处理器每秒需要访问存储器几百兆次。如果处理器速率远高于存储器,那么只能借助Cache才能满足其全部性能。
Cache存储器是一个容量小但存取速度非常快的存储器,它保存最近用到的存储器数据拷贝。对于程序员来说,Cache是透明的。它自动决定保存哪些数据、覆盖哪些数据。现在Cache通常与处理器在同一芯片上实现。Cache能够发挥作用是因为程序具有局部性特性。所谓局部性就是指,在任何特定的时间,微处理器趋于对相同区域的数据(如堆栈)多次执行相同的指令(如循环)。
Cache经常与写缓存器(write buffer)一起使用。写缓存器是一个非常小的先进先出(FIFO)存储器,位于处理器核与主存之间。使用写缓存的目的是,将处理器核和Cache从较慢的主存写操作中解脱出来。当CPU向主存储器做写入操作时,它先将数据写入到写缓存区中,由于写缓存器的速度很高,这种写入操作的速度也将很高。写缓存区在CPU空闲时,以较低的速度将数据写入到主存储器中相应的位置。
通过引入Cache和写缓存区,存储系统的性能得到了很大的提高,但同时也带来了一些问题。比如,由于数据将存在于系统中的不同的物理位置,可能造成数据的不一致性;由于写缓存区的优化作用,可能有些写操作的执行顺序不是用户期望的顺序,从而造成操作错误。

**tock没有MMU,有实现4G虚拟内存和内存映射到物理内存吗?**
- tock不支持虚拟内存,因此才需要每个app都编写`layout_generic.ld`声明内存布局
- tock同时不支持多进程,IPC通信也只是为了方便进程驱动和进程交换数据

**不支持虚拟内存,rust创建变量的时候是如何知道目标栈地址?sp指针?**
- 创建app的时候还要编写`.cargo\config`的rustflags,使用c的link-arg传递参数给连接器,用-Tlayout.ld指定连接脚本声明内存布局
- ARM处理器针对不同的模式,共有 6 个堆栈指针(SP),其中用户模式和系统模式共用一个SP,每种异常模式都有各自专用的R13寄存器(SP)。它们通常指向各模式所对应的专用堆栈,也就是ARM处理器允许用户程序有六个不同的堆栈空间。这些堆栈指针分别为R13、R13_svc、R13_abt、R13_und、R13_irq、R13_fiq
- 是的,app的栈其实就用户模式的sp指针,就是R13,因此只要在汇编种设置好sp指针的地址,编译器就会顺着设置变量
- 因为堆是需要用户手动通过系统模式的系统调用申请的,否则会导致mpu报错

[包括寄存器的所有信息nRF52832_PS_v1.4.pdf](https://github.com/cisen/blog/files/5106125/nRF52832_PS_v1.4.pdf)
cisen commented 4 years ago

kernel

包括功能

systick

cisen commented 4 years ago

tockloader

问答

tockloader是如何将bin文件写进硬件的?

程序进程的数据结构:

全局静态区,文字常量区,程序代码区是从内存地址分配的角度来描述的。

cisen commented 4 years ago

调试

# mac 
brew tap ARMmbed/homebrew-formulae && brew update && brew install arm-none-eabi-gcc
$ cd tock/boards/nrf52dk/jtag
# 重要侦听2331端口
$ ./jdbserver_pca10040.sh
$ JLinkGDBServer -device nrf52 -speed 1200 -if swd -AutoConnect 1 -port 2331
arm-none-eabi-gdb -x gdbinit_pca10040.jlink
# debian
sudo apt install gdb-arm-none-eabi gdb-multiarch
{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "nRF52-DK",
            "type": "gdb",
            "request": "attach",
            "executable": "${workspaceRoot}/boards/nordic/nrf52dk/target/thumbv7em-none-eabi/release/nrf52dk",
            "target": "localhost:2331",
            "remote": true,
            "cwd": "${workspaceRoot}",
            "gdbpath": "gdb-multiarch",
            "autorun": [
                "monitor reset",
                "monitor speed auto",
                "b reset_handler"
            ]
        }
    ]
}
cisen commented 4 years ago

改bug

跟踪

总结

这样才对?

alarms: | alarm 2 | | alarm 1 | before: prev/now | when | | cur_alarm | after: prev/now(fake)| cur_alarm | | | now(acturelly)

alarm=16779325, now=2109, prev=16776056, nowWrapPrew=4278193349, alarmWrapPrev=3269

```rust
// has_expired函数
fn has_expired(alarm: u32, now: u32, prev: u32) -> bool {
    now.wrapping_sub(prev) >= alarm.wrapping_sub(prev)
    if (now as i32).wrapping_sub(prev as i32) < 0
        || (alarm as i32).wrapping_sub(prev as i32) < 0
    {
        panic!("now={}, alarm={}, prev={}", now, alarm, prev);
    }
}
- - - 如果alarm1跟alarm2间距很大,出现设置alarm2的时候alarm1到期了还没执行的概率就很小
- - - 如果时钟中断随机设置,也很有可能出现alarm1跟alarm2很接近的情况,就会出现alarm1要等很久才能被捕获

## 问题翻译
这个计时器的问题跟这些最新的issue的是一样的。
**前提**
我开始注意到这个问题是整合RTT的时候,通过syscall的console打印了一堆信息。app有些时候会被冻结,似乎在永远等待console驱动程序返回“写完成”回调。因为debug的输出也会被冻结,我不是百分百肯定。但是通过LED的闪来调试确实发现这个问题。

在virtual_alarm capsule中,有很多针对某个参考的相互比较时间戳的检查(比如“now”)

第一个检查,如果新的时钟设置的到期时间戳when在当前已设置时钟的到期时间戳之前(这种情况时钟应该更新到期时间戳到when)
tock/capsules/src/virtual_alarm.rs
Lines 85 to 91 in d52a619
```rust
 let cur_alarm = self.mux.alarm.get_alarm(); 
 let now = self.now(); 

 if cur_alarm.wrapping_sub(now) > when.wrapping_sub(now) { 
     self.mux.prev.set(self.mux.alarm.now()); 
     self.mux.alarm.set_alarm(when); 
 } 

第二个检查,如果一个alarm跟now相比已经到期了(alarm是设定值,now是时钟动态的当前值,prev是前一个设定值,when是下一个设定值) tock/capsules/src/virtual_alarm.rs Lines 131 to 133 in d52a619

 fn has_expired(alarm: u32, now: u32, prev: u32) -> bool { 
     now.wrapping_sub(prev) >= alarm.wrapping_sub(prev) 
 } 

我不确定wrapping_sub算法的逻辑含义是什么,但是这似乎在保证 cur_alarm> = now && when> = now(在第一种情况下),以及now> = prev && alarm> = prev(在第二种情况)。 否则,由于时间戳是无符号整数,因此如果其中一个参数稍微小于now(在第一种情况下)或prev(在第二种情况下),则wrapping_sub算法将产生非常大的数字。

然而,这些假设在现在的代码种是不会出现的,不管通过插入一些紧急语句却很容易生成示例。

第一种情况,下面的代码很容易让我的app出现panic

if (cur_alarm as i32).wrapping_sub(now as i32) < 0
    || (when as i32).wrapping_sub(now as i32) < 0
{
    panic!("cur_alarm={}, when={}, now={}", cur_alarm, when, now);
}
Kernel panic at [...]/tock/capsules/src/virtual_alarm.rs:91:
    "cur_alarm=10351, when=10356, now=10353"

第二种情况发生panics的频率要少得多,但是我仍然可以在某个时候触发它(您可以看到systicks的数字要大得多)。

if (now as i32).wrapping_sub(prev as i32) < 0
    || (alarm as i32).wrapping_sub(prev as i32) < 0
{
    panic!("now={}, alarm={}, prev={}", now, alarm, prev);
}
Kernel panic at [...]/tock/capsules/src/virtual_alarm.rs:141:
    "now=7392136, alarm=7392134, prev=7392135"

在我观察到的两种panic中,参考点都在其他两者之间,从而使比较“翻转”。

例如,在第二种情况下,alarm = 7392134 <prev = 7392135 <now = 7392136,但是代码却得到尚未到期的结论(这似乎是错误的)。

请注意,alarm capsule中有相同的功能。 tock/capsules/src/alarm.rs Lines 161 to 163 in d52a619

 fn has_expired(alarm: u32, now: u32, prev: u32) -> bool { 
     now.wrapping_sub(prev) >= alarm.wrapping_sub(prev) 
 } 

无论如何,我不确定逻辑到底应该是什么。 看来在i32格式上进行比较会更准确。 virtual_alarm的多路复用器还可以更好地跟踪过去是否触发了哪些alarm或“只是发生了”(即发生在过去的2 ^ 31个ticks中?),这与#1496和#1499有关。 或确保上一个alarm确实早于任何其他alarm(与第二次panic不同)。

我还想知道“参考点”是否在此比较中带来了任何价值,而不是在i32上进行wrapping_sub,如下所示:

fn has_expired(alarm: u32, now: u32) -> bool {
    (now as i32).wrapping_sub(alarm as i32) >= 0
}

最终,ticks会回绕,但是在这些比较中,我认为应该将时间戳假定为“足够接近”(这样i32会更准确)。 当然,如果您按2 ^ 31个ticks的顺序等待,那么所有关于比较逻辑正确性的押注都会被取消。

我还认为,更好的输入数据结构会有所帮助,例如,使用“ Timestamp”和“ Duration”类型代替无处不在的u32类型。

无论如何,似乎在这里非常需要更多的单元测试(即#1512)来模拟alarm muxer 在各种情况下的行为。

关于此问题的一些更新:通过运行libtock-rs中的blink_random示例,可以在virtual_alarm.rs中的has_expired中产生以下情况。

Kernel panic at [...]/tock/capsules/src/virtual_alarm.rs:141:
    "now=1291, alarm=16778507, prev=16775234"

当转换为十六进制时,now= 0x50b,alarm= 0x100050b,prev= 0xfff842。 因此,似乎systick只有24位(cc#1413),并且alarm实际在逻辑上应等于now

在那种情况下,我们得到now.wrapping_sub(prev)= 0xff000cc9alarm.wrapping_sub(prev)= 0xcc9,因此has_expired函数能正确地返回alarm已过期,尽管它会错误地得出now值: now= 1290

1496 修复virtual_uart 种的transmit_buffer 函数在异步回调种的bug

tock/capsules/src/virtual_uart.rs Line 353 in ad676ee

 self.mux.do_next_op(); 

有传输请求时调用do_op。 如果没有待处理的请求,则可以立即处理。 如果失败,则发出带有错误的callback_done回调: tock/capsules/src/virtual_uart.rs Line 190 in ba11fd9

 client.transmitted_buffer(rbuf.unwrap(), 0, rcode); 

我们已经同意,出于竞争条件/数据完整性的原因,不应在调用期间发生回调。 如果立即处理了对虚拟UART的发送调用且失败了,则将同步触发回调。 相反,这应该使用延迟调用来异步发出回调。

1499 添加alarm的测试

此拉取请求增加了对schedule timer的支持,其中提供的systick counter实际上位于过去。 这对于将timer设置为很短的时间很有用。 如果在一个应用程序中同时调度计时器,就会发生这种情况,想象两个LED同时以500和250ms的间隔闪烁。 一个延迟将在第二个停止后开始,并且会任意缩短。 这很容易导致(不可避免的)情况,即设置为alarm的systick count其实已经结束。

此PR通过firing alarm(如果已结束)来解决此问题。

1651 VirtualMuxAlarm 错误

使用VirtualMuxAlarm设置alarm时,参考点(self.mux.prev)与alarm一起设置。 tock/capsules/src/virtual_alarm.rs Lines 84 to 95 in f30961f

 if enabled > 0 { 
     let cur_alarm = self.mux.alarm.get_alarm(); 
     let now = self.now(); 

     if cur_alarm.wrapping_sub(now) > when.wrapping_sub(now) { 
         self.mux.prev.set(self.mux.alarm.now()); 
         self.mux.alarm.set_alarm(when); 
     } 
 } else { 
     self.mux.prev.set(self.mux.alarm.now()); 
     self.mux.alarm.set_alarm(when); 
 } 

然后,该上一个参考点用于确定多路复用的alarms中的alarm是否已过期。 tock/capsules/src/virtual_alarm.rs Line 149 in f30961f

 .filter(|cur| cur.armed.get() && has_expired(cur.when.get(), now, prev)) 

问题 问题在于,在VirtualMuxAlarm :: set_alarm内部无法保证cur_alarm> = now。 我们可能会处在:已经设置了alarm1,正在设置alarm2,而alarm1已经在过去(但尚未触发)的情况。

> in VirtualMuxAlarm::set_alarm
alarms:       | alarm 1   |      | alarm 2
before:  prev | cur_alarm | now  | when(设置1)
after:        |           | prev | cur_alarm(设置2)

在这种情况下,由于新的prev就在alarm1之后,因此下次调用MuxAlarm :: fired时,alarm1将不会被视为过期-而是需要花费ticks轮回的时间才能判断时钟已经到期。

这在Nordic上的Segger RTT调试中尤其明显,该调试器将计时器设置为将来非常接近(100us)。 到其他代码运行时,这可能已经过去了。 tock/capsules/src/segger_rtt.rs Line 243 in f30961f

 let interval = (100 as u32) * <A::Frequency>::frequency() / 1000000; 

但是使用virtual alarms的所有capsule都会受到影响。

可观察到的结果是alarm客户端(在本例中为Segger RTT)永远等待(对于Segger RTT,内核调试和控制台似乎冻结)。

解决方案 我认为在设置新alarm时移动prev是不正确的。 仅应在MuxAlarm :: fired函数中对其进行更新,在该函数中实际上会触发所有过期的alarm。 这再次表明32位时间戳的“三向比较”容易出错,因为实现起来很复杂,难以理解,很难知道何时可以安全地更新参考点,等等。我认为这是 关于在与时间相关的胶囊和HIL中具有真正单调(即64位)时间戳的另一个论点(如先前在https://groups.google.com/d/msg/tock-dev/CWecIZq7jqg/RrWySQLbBgAJ中讨论的)。

delay_ms (1) blocks the process (and maybe the whole alarm system) on stm boards

问答

rtc时钟是如何设置时间的?

alarm的CAPTURE/COMPARE 模式是什么?

VirtualMuxAlarm是什么?

self.now和alarm.now有何区别?

cur_alarm,now,when,prev分别是什么?

Timer和Count两种模式有何区别?

既然MuxAlarm.virtual_alarms[0]=VirtualMuxAlarm,就是MuxAlarm是VirtualMuxAlarm的父,那VirtualMuxAlarm是如何被push进去virtual_alarms的?

系统的所有时钟都是在MuxAlarm下进行管理的?

AlarmDriver跟VirtualMuxAlarm有什么关系?有什么区别?

每次订阅一次就创建一个virtualAlarm?

app_alarm是什么?

一个app只能setTimeout一次?

可以通过创建多个timeDriver实现多个setTimeout?

寄存器

CC: Capture compare

CCXE: Capture/Compare x output enable

CCXNE:Capture/Compare 1 complementary output enable

CCXNP:Capture/Compare x complementary output polarity

OPM:The one-pulse mode (OPM)

TI: Timer

TIx: x Timer number,TIx Timer X,the timer channel inputs, TIx inputs.

IT: internal Trigger

ITx:internal Trigger x

TI1FP1:Timer Input 1 Filtered Priority channel 1

ED: Edge Detector:

FDTS: Frequency of Division Timer Clock:The FDTS clock signal is derived from the timer clock signal, and the CKD[1:0] control bit-field sets the ratio between these two clock signals.

fCK_INT: Frequency clock of internal

fDTS = FDTS

TIxF_ED:TIx Edge Detector

ETRF:External Trigger input

Internal Trigger 0 (ITR0).

Internal Trigger 1 (ITR1).

Internal Trigger 2 (ITR2).

Internal Trigger 3 (ITR3).

TI1 Edge Detector (TI1F_ED)

Filtered Timer Input 1 (TI1FP1)

Filtered Timer Input 2 (TI2FP2)

External Trigger input (ETRF)

trigger signal (TRGI)

- 16777216 = 2^24B÷bai1024÷1024=16MB
- 在`chips\nrf52\src\adc.rs`写着:`/// Capture and compare value. Sample rate is 16 MHz/CC`
- 如果您使用nRF52840的RTC,则对于32KHz的频率,溢出频率为2 ^ 24,因此为512秒,即仅超过8分钟。

capsules/src/virtual_alarm.rs

alarm=16779325, now=2109, prev=16776056, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779355, now=2139, prev=16776086, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779392, now=2176, prev=16776123, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779425, now=2209, prev=16776156, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779463, now=2247, prev=16776194, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779356, now=2140, prev=16776087, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779415, now=2199, prev=16776146, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779430, now=2214, prev=16776161, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779469, now=2253, prev=16776200, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779505, now=2289, prev=16776236, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779409, now=2193, prev=16776140, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779444, now=2228, prev=16776175, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779453, now=2237, prev=16776184, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779475, now=2259, prev=16776206, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779542, now=2326, prev=16776273, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779460, now=2244, prev=16776191, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779485, now=2269, prev=16776216, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779515, now=2299, prev=16776246, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779605, now=2389, prev=16776336, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779646, now=2430, prev=16776377, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779576, now=2360, prev=16776307, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779618, now=2402, prev=16776349, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779682, now=2466, prev=16776413, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779745, now=2529, prev=16776476, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779810, now=2594, prev=16776541, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779759, now=2543, prev=16776490, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779778, now=2562, prev=16776509, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779831, now=2615, prev=16776562, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779868, now=2652, prev=16776599, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779885, now=2669, prev=16776616, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779810, now=2594, prev=16776541, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779826, now=2610, prev=16776557, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779914, now=2698, prev=16776645, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779985, now=2769, prev=16776716, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779999, now=2783, prev=16776730, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779932, now=2716, prev=16776663, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16779997, now=2781, prev=16776728, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16780031, now=2815, prev=16776762, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16780095, now=2879, prev=16776826, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16780141, now=2925, prev=16776872, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16780020, now=2804, prev=16776751, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16780031, now=2815, prev=16776762, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16780073, now=2857, prev=16776804, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16780127, now=2911, prev=16776858, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16780142, now=2926, prev=16776873, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16780091, now=2875, prev=16776822, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16780162, now=2946, prev=16776893, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16780279, now=3063, prev=16777010, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16780325, now=3109, prev=16777056, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16780358, now=3142, prev=16777089, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16780365, now=3149, prev=16777096, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16780254, now=3038, prev=16776985, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16780288, now=3072, prev=16777019, nowWrapPrew=4278193349, alarmWrapPrev=3269 alarm=16780307, now=3091, prev=16777038, nowWrapPrew=4278193349, alarmWrapPrev=3269

cisen commented 4 years ago

进程总结

问答

tbf中,创建进程的核心参数是什么?

cisen commented 3 years ago

URAT 相关

问答

各个芯片不同的针脚定义在哪里?

寄存器有什么用?

如何发送一大波数据?如何分批发送

发送流程是怎样的?

write_buffer和tx_buffer有什么关系?

console是如何初始化的?

cisen commented 3 years ago

kernel 机会查找

关键文件

cisen commented 2 years ago

安装到swerv

问答

需要准备什么?

需要sw暴露什么接口?只要内存分布就够了?

如何编译riscv版本?

cd boards/hail/
make