computer_knowledge_notes/Hardware/RISC-V/k210/系统控制器.md

214 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#### 名词解释
##### PLL
锁相环(Phase Locked Loop)使电路上的时钟和某一外部时钟的相位同步。PLL的主要用途是作为频率合成器产生更宽范围的频率输出。也可以用作抖动滤波器或补充时钟网络的延迟。锁相环通过由PD(Phase Detector)、LF(Loop Filter)和VCO(Voltage Controlled Oscillator)三部分组成。
##### APB
一个总线协议,几乎成为一种标准的片上总线结构。主要用于低带宽的外设连接。
#### 系统控制器的寄存器
系统控制器是个外设它负责所有与系统相关的外设的设置。它的32个32位寄存器都映射在内存里。
| 编号 | 名称 | 描述 |
| ----- | ------------- | ------------------------------------------------------------ |
| 0 | git_id | Git短提交id。只读寄存器git的commit id共160位而它只能提供前32位所以叫"short commit id"。 |
| 1 | clk_freq | 系统时钟频率。只读寄存器。 |
| 2 | pll0 | PLL0控制器。可写其中clkr0,clkf0,clkod0用于计算输出频率其中clkr0,clkf0,clkod0,bwadj0用于控制输入频率其中pll_bypass0设置是否bypass pll0(1:bypass, 0:do not bypass)pll_out_en0位控制pll0的时钟使能pll_pwrd0控制pll0的启动(0:power off, 1:power on)pll_reset0用于重置pll0(0:不重置1:重置) |
| 3 | pll1 | PLL1控制器。可写其中clkr1,clkf1,clkod1用于计算输出频率其中clkr1,clkf1,clkod1,bwadj1用于控制输入频率其中其中pll_bypass1设置是否bypass pll1(1:bypass, 0:do not bypass)pll_out_en1位控制pll1的时钟使能pll_pwrd1控制pll1的启动(0:power off, 1:power on)pll_reset1用于重置pll1(0:不重置1:重置) |
| 4 | pll2 | PLL2控制器。可写其中clkr2,clkf2,clkod2用于计算输出频率其中clkr2,clkf2,clkod2,bwadj2用于控制输入频率其中其中pll_bypass2设置是否bypass pll2(1:bypass, 0:do not bypass)pll_out_en2位控制pll2的时钟使能其中pll_ckin_sel2将pll2设为时钟源pll_pwrd2控制pll2的启动(0:power off, 1:power on)pll_reset2用于重置pll2(0:不重置1:重置) |
| 5 | resv5 | 保留 |
| 6 | pll_lock | PLL锁测试器。其中pll_lock0~2均只读pll_lock0=3则pll0已锁pll_lock1=1则pll1已锁pll_lock2=1则pll2已锁。其中pll_lip_clear0~2用于清空对应pll的slip置1则清空对应pll的slip。 |
| 7 | rom_error | AXI ROM测试器 |
| 8 | clk_sel0 | 时钟选择控制器0。可写其中aclk_sel设置aclk为时钟源(0:IN0, 1:ACLK)其中aclk_divider_sel占用2位控制aclk的分频值其中apb0~2_clk_sel分别占用3位分别控制apb0~2的分频值其中spi3_clk_sel设置spi3为时钟源其中timer0~2_clk_sel分别设置timer0~2为时钟源其它为保留位。 |
| 9 | clk_sel1 | 时钟选择控制器1。可写仅第一位有效spi3_sample_clk_sel将spi3_sample设为时钟源。 |
| 10 | clk_en_cent | 中心时钟使能。可写每一位控制一个时钟使能共用6个位控制cpu, sram0~1, apb0~2其余位保留。其中apb0~2分别控制着若干外设。置1使能置0不使能。 |
| 11 | clk_en_peri | 外部时钟使能。可写每一位控制一个外设的时钟全能。置1使能置0不使能。 |
| 12 | soft_reset | 软重置。可写仅第一位有效。置1重置置0不重置。 |
| 13 | peri-reset | 外重置。可写每一位控制一个外设。置1重置置0不重置。 |
| 14 | clk_th0 | 时钟分频值控制器0。可写控制分频值宽度为4的外设的分频值控制的外设有5个sram0~1, ai, dvp, rom |
| 15 | clk_th1 | 时钟分频值控制器1。可写控制分频值宽度为8的外设的分频值控制的外设有4个spi0~3 |
| 16 | clk_th2 | 时钟分频值控制器2。可写控制分频值宽度为8的外设的分频值控制的外设有3个timer0~2剩余8位为保留位 |
| 17 | clk_th3 | 时钟分频值控制器3。可写控制分频值宽度为16的外设的分频值控制的外设有2个i2s0~1_clk_threshold |
| 18 | clk_th4 | 时钟分频值控制器4。可写控制2个分频值宽度为8的外设的分频值i2s0~1_mclk_threshold还控制1个分频值宽度为16的外设的分频值i2s2_clk_threshold |
| 19 | clk_th5 | 时钟分频值控制器5。可写控制分频值宽度为8的外设的分频值控制的外设有4个i2s2_mclk_threshold, i2c0~2_clk_threshold |
| 20 | clk_th6 | 时钟分频值控制器6。可写控制分频值宽度为8的外设的分频值控制的外设有2个wdt0~1_clk_threshold, 剩余16位为保留位 |
| 21 | misc | 杂项控制器。有1个1位的sip_dvp_data_enable1为使能0为不使能 |
| 22 | peri | 外设控制器 |
| 23 | spi_sleep | SPI睡眠控制器 |
| 24 | reset_status | reset源的状态。wdt0~1_reset_sts和soft_reset_sts代表其各自的reset状态1为reset0为非resetreset_sts_clr如写入1则清空reset状态。其余为保留位。 |
| 25 | dma_sel0 | DMA握手控制器0。可写控制dma_sel0~4共5个6位宽的channel其余2位保留 |
| 26 | dma_sel1 | DMA握手控制器1。可写控制dma_sel5这个6位宽的channel其余26位保留 |
| 27 | power_sel | IO电源模式选择控制器。可写power_mode_sel0~7共8个1位的项控制8种电源模式1为使能0为不使能其余24位保留 |
| 28-31 | resv28-resv31 | 保留 |
#### 系统控制器的功能
##### 外设时钟的使能或取消使能
`sysctl_clock_[enable | disable]`
共有三条总线(apb0, apb1, apb2),所有外设都分配在这三条总线上。在**clk_en_cent**寄存器上有3位分别控制这三条总线时钟的使能或取消使能。
共有三种类型的外设。一种外设是PLL它们的时钟使能由各自的寄存器控制(**pll0**, **pll1**, **pll2**)一种连在内部总线上它们是CPU、内存和外部总线它们的时钟使能由寄存器**clk_en_cent**控制;其它外设连在外部总线上,它们的时钟使能由**clk_en_peri**控制。
对于PLL或连在内部总线上的设备只要该外设对应的时钟使能位置1即可使能该设备对于连接在外部总线apb上的设备则需要先使能该外设所在的apb总线的时钟再使能该外设的时钟。
设备要取消使能只要将该设备的使能位置0即可。
##### 对应时钟分频值的设置与获取
`sysctl_clock_[set | get]_threshold`
不同的外设其分频值位宽是不一样的共有2、3、4、8、16五种情况。其中aclk_divider(2位)、apb0~2(各3位)由寄存器**clk_sel0**控制。其中sram0~1、ai、dvp、rom的分频值均为4位由寄存器**clk_th0**控制。其中spi0~3的分频值为8位由寄存器**clk_th1**控制。其中timer0~2的分频值为8位由寄存器**clk_th2**控制。其中i2s0~1的分频值为16位由寄存器**clk_th3**控制。其中i2s2的分频值为16位i2s0~1_mclk的分频值为8位由寄存器**clk_th4**控制。其中i2s2_mclki2c0~2的分频值为8位由寄存器**clk_th5**控制。其中wdt0~1的分频值为8位由寄存器**clk_th6**控制。
设置外设的时钟分频值就是向相应的位写入数据,获取外设的时钟分频值就是从相应的位读取数据。
##### 时钟对应时钟源的设置与获取
`sysctl_clock_[set | get]_clock_select`
| 时钟 | 时钟源 | 意义 |
| ------------------------------------ | ------- | -------------------------------------------------------- |
| pll0~2->pll_bypass0~2 | 0, 1 | 1绕过对应pll0不绕过对应pll |
| pll2->pll_ckin_sel2 | 0, 1, 2 | 0IN01PLL02PLL1 |
| clk_sel0寄存器的aclk, spi3, timer0~2 | 0, 1 | 1设置对应设备为时钟源0不设置对应设备为时钟源 |
| clk_sel1->spi3_sample_clk_sel | 0, 1 | 1设置spi3_sample为时钟源0不设置spi3_sample为时钟源 |
时钟源的设置就是为如上寄存器的相关位指定时钟源。
时钟源的获取就是从如上寄存器中的相应位获取对应的值。
##### 获取PLL的频率
`sysctl_pll_get_freq`
```
FOUT = FIN / NR * NF /OD # 输出频率 = 输入频率 / R分频器 * N计数器 / od
# nr = pll.clkr + 1
# nf = pll.clkf + 1
# od = pll.clkod + 1
# FIN : 如PLL0~1则为26000000UL如PLL2则从pll2.pll_ckin_sel2获取对应时钟源(可能是SOURCE_IN0, SOURCE_PLL0, SOURCE_PLL1)如SOURCE_IN0则为26000000UL,如SOURCE_PLL0~1则按上述公式再算一遍。
```
##### 获取各种设备的基本时钟频率
`sysctl_clock_get_freq`
| clock | divider | 频率 |
| -------------------------------------------------------- | ------- | ------------------------------------------------------------ |
| **IN0** | | 源频率是26000000UL结果频率与源频率相等 |
| directly under **PLL**时钟域(PLL0~2) | gated | 源频率是对应PLL的频率结果频率与源频率相等 |
| directly under **ACLK**时钟域(CPU, DMA, FFT, ACLK, HCLK) | | 从寄存器clk_sel0的aclk位取对应的值如为0表示没有选择aclk则源频率为IN0的频率如为1表示选择了aclk则源频率的算法为(PLL0的频率)/(2ULL << ACLK的时钟分频值)。结果频率与源频率相等 |
| under **ACLK**时钟域(SRAM0~1, ROM, DVP) | gated | 源频率是ACLK的时钟频率结果频率的算法为(源频率)/(设备对应的分频值 + 1) |
| under **ACLK**时钟域(ABP0~2) | even | 源频率是ACLK的时钟频率结果频率的算法为(源频率)/(设备对应的分频值 + 1) |
| under **AI**时钟域 | gated | 源频率是PLL1的时钟频率结果频率的算法为(源频率)/(AI的分频值 + 1) |
| under **I2S**时钟域(I2S0~2) | even | 源频率是PLL2的时钟频率结果频率的算法为(源频率)/(对应设备的分频值 + 1)/2 |
| under **WDT**时钟域(WDT0~1) | even | 源频率是IN0的时钟频率结果频率的算法为(源频率)/(对应设备的分频值 + 1)/2 |
| under **PLL0**时钟域(SPI0~2, I2C0~2) | even | 源频率是PLL0的时钟频率结果频率的算法为(源频率)/(对应设备的分频值 + 1)/2 |
| under **PLL0_SEL**时钟域(SPI3, TIMER0~2) | even | 从寄存器clk_sel0相应的位获取对应的值如为0表示没有选择对应设备的时钟则源频率为IN0的频率如为1表示选择了对应设备的时钟其源频率为PLL0的频率对于SPI3结果频率的算法是(源频率)/(SPI3的分频值 + 1)/2对于TIMER0~2结果频率的算法是(源频率)/(TIMER2的分频值 + 1)/2 |
| under **MISC**时钟域 | even | 无代码仅注释目前无任何意义 |
| under **APB0**时钟域(GPIO, UART1~3, FPIOA, SHA) | even | 源频率为APB0的时钟频率结果频率与源频率相等 |
| under **APB1**时钟域(AES, OTP, RTC) | even | 如为AES或OTP则源频率是APB1的时钟频率如为RTC则源频率是IN0的时钟频率结果频率与源频率相等 |
| under **APB2**时钟域 | even | Do nothing即源频率和结果频率均为0 |
##### 设备重置与获取重置状态
`sysctl_reset`, `sysctl_get_reset_status`
重置的过程就是将寄存器**soft_rest****peri_rest**中对应的位置1再置0
获取重置状态就是从寄存器**reset_status**中依次读取相应的位如某一位为1则表明该位对应的外设被重置了可以获取重置状态的外设有wdt1, wdt2, soft最后应该是通过给reset_sts_clr位置1来清空外设的重置状态
##### PLL的使能与禁用
`sysctl_pll_[enable | disable]`
寄存器**pll0~2**分别控制相应PLL的使能与禁用
使能的过程
```
sysctl->pll.pll_bypass = 0 // 不绕过pll
sysctl->pll.pll_pwd = 1 // 开启pll
// 重置pll
sysctl->pll.pll_reset = 0
sysctl->pll.pll_reset = 1
asm volatile("nop; nop");
sysctl->pll.pll_reset = 0
```
禁用的过程:
```
sysctl->pll.pll_bypass = 1 // 绕过pll
sysctl->pll.pll_pwrd = 0 // 关闭pll
```
##### 选择外设DMA通道握手信号
`sysctl_dma_select`
DMA一共有6个通道每个通道占6位。前5个通道在寄存器**dma_sel0**第6个通道在寄存器**dma_sel1**。每个通道可选择的外设有SSI, I2C, UART, AES, SHA, AI, FFT, I2S。
##### 设置spi0和dvp数据
`sysctl_set_spi0_dvp_data`
寄存器**misc**的spi_dvp_data_enable位置1则使能置0则不使能。
##### 设置FPIOA电源模式
`sysctl_set_power_mode`
电源模式共有两种3.3V和1.8V。寄存器**power_sel**共可选择8种power bank(即IO电源域编号)如电源模式选择1.8V则相应的power bank位置1如电源模式选择3.3V则相应的power bank位置0。
##### CPU频率的获取与设置
`sysctl_cpu_[get | set]_freq`
获取CPU频率固定为390000000.
设置CPU的频率实际上就是设置PLL0的频率以IN0为源频率以`(clk_sel0->aclk_divider_sel + 1) * 2 * freq`为结果频率计算出对应的pll寄存器相关位的值并进行设置。
##### 设置PLL的频率
`sysctl_pll_set_freq`
1. 如为PLL0则设置寄存器clk_sel0的aclk_sel位为0目的是为了让CPU时钟变为XTAL
2. 设置对应pll寄存器的pll_out_en位为0目的是使对应pll输出失效
3. 设置对应pll寄存器的pll_pwrd位为0目的是关闭对应的pll
4. 设置pll的新值如为pll2则以pll2->pll_ckin_sel为源计算结果频率如为其它pll则以IN0为源计算结果频率
5. 设置对应pll寄存器的pll_pwrd位为1目的是开启对应的pll
6. 设置对应pll寄存器的pll_reset位为1目的是重置对应的pll
7. 设置寄存器pll_lock里与pll对应的pll_slip_clear位为1清空对应pll的slip
8. 设置对应pll寄存器的pll_out_en位为1目的是开启对应pll的输出
9. 如为PLL0则设置寄存器clk_sel0的aclk_sel位为1目的是为了让CPU时钟变为PLL
10. 最终return第4步算出的结果频率。
##### 中断的使能与取消使能
`sysctl_[enable | disable]_irq`
通过设置**mie**和**mstatus**相应的位实现。
##### 获取从启动到现在所用的时间
`sysctl_get_time_us`
从**mcycle**获取CPU周期数从`sysctl_clock_get_freq()`获取CPU频率两者相除即为启动到现在所过的时间。
#### 待解决的问题
- 不知道PLL的具体作用。
- 不知道时钟的threshold的具体作用。
threshold在kendryte的编程指南里翻译为分频值似乎不是词典里翻译的阈值的意思。
- 外设重置可以理解为重置外设的寄存器,那么软重置是什么意思呢?
- 为什么重置状态寄存器只记录wdt0~1和soft的重置呢其它外设为什么不记录呢