16 KiB
名词解释
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_enable,1为使能,0为不使能 |
22 | peri | 外设控制器 |
23 | spi_sleep | SPI睡眠控制器 |
24 | reset_status | reset源的状态。wdt0~1_reset_sts和soft_reset_sts代表其各自的reset状态,1为reset,0为非reset,reset_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_mclk,i2c0~2的分频值为8位,由寄存器clk_th5控制。其中wdt0~1的分频值为8位,由寄存器clk_th6控制。
设置外设的时钟分频值就是向相应的位写入数据,获取外设的时钟分频值就是从相应的位读取数据。
时钟对应时钟源的设置与获取
sysctl_clock_[set | get]_clock_select
时钟 | 时钟源 | 意义 |
---|---|---|
pll0~2->pll_bypass0~2 | 0, 1 | 1,绕过对应pll;0,不绕过对应pll |
pll2->pll_ckin_sel2 | 0, 1, 2 | 0,IN0;1,PLL0;2,PLL1 |
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
- 如为PLL0,则设置寄存器clk_sel0的aclk_sel位为0,目的是为了让CPU时钟变为XTAL;
- 设置对应pll寄存器的pll_out_en位为0,目的是使对应pll输出失效;
- 设置对应pll寄存器的pll_pwrd位为0,目的是关闭对应的pll;
- 设置pll的新值,如为pll2则以pll2->pll_ckin_sel为源计算结果频率,如为其它pll则以IN0为源计算结果频率;
- 设置对应pll寄存器的pll_pwrd位为1,目的是开启对应的pll;
- 设置对应pll寄存器的pll_reset位为1,目的是重置对应的pll;
- 设置寄存器pll_lock里与pll对应的pll_slip_clear位为1,清空对应pll的slip;
- 设置对应pll寄存器的pll_out_en位为1,目的是开启对应pll的输出;
- 如为PLL0,则设置寄存器clk_sel0的aclk_sel位为1,目的是为了让CPU时钟变为PLL;
- 最终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的重置呢?其它外设为什么不记录呢?