上一篇文章對xloader_entry進行了分析,看到其中調用的第一個标号就是sys_init,本文就對這個标号對應的代碼段進行粗略的分析,這裡我也還有好多沒有搞明白的,就先留着,日後慢慢明白,先把自己目前能夠看明白的東西記下來。
另外,需要說明的是,像sys_init以及後續還要講的ddr_init之類的代碼是與體系結構高度相關的,由于我的文章針對的是spearplus開發闆的,是以,上面提到的所有寄存器的位址以及功能說明要需要參考相應的user manual文檔。
與本文代碼對應的文檔是spearplus 600 preliminary user manual。
好, 下面就開始看代碼。sys_init代碼段位于xloader目錄下的pll/spear_pll.s代碼中,該代碼段的主要功能是對系統的時鐘頻率以及 工作方式進行初始化,并初始化外設時鐘等,主要對gmac ethernet、uart1、uart2以及smi(serial flash controller)等外設進行了初始化。
對代碼的整個說明以注釋的方式跟源檔案一起貼在這裡,其中還有很多不明白的地方,希望以後能夠慢慢明白:
1 /* preserve8 */
2
3
4
5 /*;----------------------------------------------------------------------------------- */
6 /*
7 include include/mmu946t.s
8 include include/arm.equ
9 */
10 #include "splus_pll.h"
11
12
13 #define misc_base 0xfca80000
14 #define phy_ctr_reg 0xa4
15 #define periphclk_cfg 0x28
16 #define periph1_clken 0x2c
17 #define periph1_rst 0x38
18 #define amba_clk_cfg 0x24
19 #define gmac_synth_clk 0x68
20 #define gmac_ctr_reg 0xa8
21 #define pll1_freq 0xa600010f
22 #define pll1_cntl_reg 0x00000008
23 #define pll1_frq_reg 0x0000000c
24
25 #define txclk_synth 0x00000008 /*enable synth. clock */
26 #define pll2_freq 0x8500010f
27 #define pll2_cntl_reg 0x00000014
28 #define pll2_frq_reg 0x00000018
29
30
31 #define sysctl_base 0xfca00000
32 #define scctrl 0x00000000
33 #define scpllctrl 0x00000014
34 #define plltim 0x1ffffff
35
36 #define smi_base 0xfc000000
37 #define smi_cr1 0x00
38
39 /* control reg 1 */
40 #define bank_en 0x0000000f /* enables all banks */
41 #define dsel_time 0x00000050 /* deselect time 5+1 smi_ck periods */
42 #define prescal3 0x00000300 /* ahb_ck prescaling value */
43 #define prescal5 0x00000500 /* ahb_ck prescaling value */
44 #define prescala 0x00000a00 /* ahb_ck prescaling value */
45 #define prescalf 0x00000f00 /* ahb_ck prescaling value */
46 #define prescal9 0x00000900 /* ahb_ck prescaling value */
47 #define sw_mode 0x10000000 /* enables sw mode */
48 #define wb_mode 0x20000000 /* write burst mode */
49 #define fast_mode 0x00008000 /* fast mode */
50
51
52 #define arm1_we 0x00000001
53 #define arm1 0x00000002
54 #define arm2 0x00000004
55 #define uart1 0x00000008
56 #define uart2 0x00000010
57 #define ssp1 0x00000020
58 #define ssp2 0x00000040
59 #define i2c 0x00000080
60 #define jpeg 0x00000100
61 #define fsmc 0x00000200
62 #define firda 0x00000400
63 #define gpt4 0x00000800
64 #define gpt5 0x00001000
65 #define gpio4 0x00002000
66 #define ssp3 0x00004000
67 #define ad 0x00008000
68 #define gpt3 0x00010000
69 #define rtc 0x00020000
70 #define gpio3 0x00040000
71 #define dma 0x00080000
72 #define rom 0x00100000
73 #define smi 0x00200000
74 #define clcd 0x00400000
75 #define gmac 0x00800000
76 #define usbdev 0x01000000
77 #define usbhost1 0x02000000
78 #define usbhost2 0x04000000
79 #define ddr_ctrl 0x08000000
80 #define ram_wrapper 0x10000000
81 #define ddr_core 0x20000000
82
83 #define pll_mode_non_dithered_m_mask 0xff000000
84 #define pll_mode_non_dithered_m_shift 24
85 #define pll_value_m_mask 0xffff0000
86 #define pll_value_m_shift 16
87 #define pll_value_p_mask 0x00000700
88 #define pll_value_p_shift 8
89 #define pll_value_n_mask 0x0000000f
90 #define pll_value_n_shift 0
91 #define synth_xmask 0xffff0000
92 #define synth_ymask 0x0000ffff
93
94
95 /* area init, code, readonly
96
97 ;-----------------------------------------------------------------------------------
98
99 ; ************************************************
100 ; * routine to initialize the system controller.
101 ; ************************************************
102 */
103
104
105 .global sys_init
106 sys_init:
107
108 /* system pll init */
109
/* 0xfc880010是wdogris寄存器的位址。
* 該寄存器是一個隻讀的寄存器,當讀出來的内容最低位為1時,
* 表示由于看門狗計數器到達了0而引發了一次中斷,否則該位為0。
* 此處一直到114行代碼斷的作用是判斷是否是由于看門狗引發的
* 中斷而導緻了sys_init的調用,如果是,則直接跳轉到
* normal_mode标号處執行代碼,而不需要對時鐘進行初始化了。
*/
110 ldr r1, =0xfc880010
111 ldr r3,[r1, #0x0 ] /* 讀取wdogris寄存器的内容 */
112 and r3,r3,#0x1 /* 隻取讀取内容的最低位,該位就表明了目前看門狗的狀态 */
113 cmp r3,#0x1 /* 看看最低位是否為1,如果是,則表明此時是由看門狗引發的中斷
114 beq normal_mode * 不需要重新進行時鐘初始化,直接跳轉到normal_mode就行。
*/
115
/* 對系統工作模式進行設定,将系統設定為slow模式
* 其中,sysctl_base==0xfca00000
* scctrl==0x00000000
116 /* setting sysctl to slow mode */
117 ldr r1, =sysctl_base /* system controler registers的基位址 */
118 ldr r3,[r1, #scctrl ] /* 讀入scctrl寄存器的内容 */
119 orr r2,r3,#0x00000002 /* 将scctrl寄存器的低三位設定成'b01x(x表示任意值),
* 當系統reset後,初始時scctrl的低三位為'b001。
* 當scctrl低三位的值為'b01x時,系統将被設定成slow模式。
120 str r2,[r1, #scctrl] /* 儲存設定之後的值到scctrl寄存器 */
121
122 /* setting 100us pll timer */
123 ldr r4, =plltim /* plltim==0x01ffffff */
124 movs r2,r4,lsl #0x3 /* 由于scpllctrl寄存器的[27:3]位是儲存plltime的,
* 是以這裡将plltim左移三位,用來設定scpllctrl中的plltime。
*/
125 str r2,[r1, #scpllctrl] /* 将移位之後的plltim的值儲存到scpllctrl寄存器 */
126
127 /*
128 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
129 ; programming pll1
130 ; ldr r1,=misc_base
131 ; ldr r2,=pll1_freq
132 ; str r2,[r1, #pll1_frq_reg]
133
134
135 /* ; programming pll1 */
136 ldr r1,=misc_base /* misc_base==0xfca80000,是各種寄存器的基位址 */
137
138 /* ; program m value */
139 ldr r2,[r1, #pll1_frq_reg] /* ; read the pll1_frequency register*/
/* 距misc_base偏移pll1_frq_reg==0x0000000c處
* 是pll1_frq寄存器。此處,首先加載pll1_frq寄存器的值
*/
140 ldr r7,=pll_mode_non_dithered_m_mask /* pll_mode_non_dithered_m_mask==0xff000000 */
141 mvn r7,r7 /* 将r7的值按位反轉,也就是r7==0x00ffffff */
142 and r2,r2,r7 /*; mask the m value */ /* 将從pll1_frq寄存器中讀出的值的高8位清零,
* 這高8位要替換成自己定義的值
*/
143 ldr r4,=config_sys_pll1_m_value /* config_sys_pll1_m_value==166==0xa6,
* 這個值定義在spear_pll.h檔案中,當我們修改該頭檔案中的
* config_sys_pll1_m_value的時候,
* 就會在初始化的時候改變pll1_frq寄存器中的高8位的值。
144 mov r4,r4, lsl #pll_mode_non_dithered_m_shift /* ; shift and set the m value */
/* pll_mode_non_dithered_m_shift==24
* 這裡是将config_sys_pll1_m_value的值左移24位,
* 使其有效位位于32位字中的高8位。
145 orr r2,r2,r4 /* 将pll1_frq寄存器中讀出的值的高8位設定成spear_pll.h中定義的值 */
146 str r2,[r1, #pll1_frq_reg] /* ; load the m value*/ /* 将設定儲存到pll1_frq寄存器中 */
147
/* 148-155行所做的事情與139-146行所做的事情類似
* 隻不過此處設定的是p值,而不是m值
148 ldr r2,[r1, #pll1_frq_reg]
149 ldr r7,=pll_value_p_mask
150 mvn r7,r7
151 and r2,r2,r7
152 ldr r4,=config_sys_pll1_p_value
153 mov r4,r4, lsl #pll_value_p_shift
154 orr r2,r2,r4
155 str r2,[r1, #pll1_frq_reg]
156
/* 157-164行所做的事情與139-146行所做的事情類似
* 隻不過此處設定的是n值,而不是m值
157 ldr r2,[r1, #pll1_frq_reg]
158 ldr r7,=pll_value_n_mask
159 mvn r7,r7
160 and r2,r2,r7
161 ldr r4,=config_sys_pll1_n_value
162 mov r4,r4, lsl #pll_value_n_shift
163 orr r2,r2,r4
164 str r2,[r1, #pll1_frq_reg]
165
166
/* 從這裡開始設定pll1_ctr寄存器, pll1_cntl_reg==0x00000008
* 我猜應該是先diable再enable使得之前對pll的設定生效。
167 /*; power down : pll1 ctrl programming */
168 ldr r2,=0x1c0a /* pll1_ctr寄存器的第2位清零,disable pll */
169 str r2,[r1, #pll1_cntl_reg]
170 /*;enable pll1 */
171 ldr r2,=0x1c0e /* pll1_ctr寄存器的第2位置位,enable pll */
172 str r2,[r1, #pll1_cntl_reg]
173 /* ;strobe */ /* 這一段沒明白什麼意思... */
174 ldr r2,=0x1c06
175 str r2,[r1, #pll1_cntl_reg]
176 ldr r2,=0x1c0e
177 str r2,[r1, #pll1_cntl_reg]
178 /*;wait for lock */
/* pll1_ctr寄存器的最低位是一個隻讀位,當它為0的時候
* 表示pll處于unlock狀态,當它為1的時候表示pll處于lock狀态
* 此處,是在等待,知道pll處于lock狀态。
179 pll1_lock_1:
180 ldr r2,[r1,#pll1_cntl_reg] /* 讀取pll1_ctr寄存器的值 */
181 and r2,r2,#0x1 /* 屏蔽除最低位之外的所有位 */
182 cmp r2,#0x1 /* 看看最低位是否為1,為1則表明已經處于lock狀态了 */
183 bne pll1_lock_1 /* 否則繼續等待,知道最低位為1 */
/* 以下代碼所做的工作類似與上面的代碼
* 隻不過上面的代碼是對pll1進行初始化,
* 而下面的代碼是對pll2進行初始化。原因可能是spearplus這個開發闆具有雙arm核。
184 /*
185 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
186 ; programming pll2
187 */
188 ldr r1,=misc_base
189 ldr r2,=pll2_freq
190 str r2,[r1, #pll2_frq_reg]
191
192 /*
193 ; power down : pll2 ctrl programming
194 */
195 ldr r2,=0x1c0a
196 str r2,[r1, #pll2_cntl_reg]
197 /* ;enable pll1*/
198 ldr r2,=0x1c0e
199 str r2,[r1, #pll2_cntl_reg]
200 /*;strobe */
201 ldr r2,=0x1c06
202 str r2,[r1, #pll2_cntl_reg]
203 ldr r2,=0x1c0e
204 str r2,[r1, #pll2_cntl_reg]
205 /*;turn from int to ext. div */
206 /*; ldr r2,=0x1d0f */
207 /*; str r2,[r1, #pll2_cntl_reg]*/
208
209 /*;wait for lock */
210 pll2_lock_1:
211 ldr r2,[r1,#pll2_cntl_reg]
212 and r2,r2,#0x1
213 cmp r2,#0x1
214 bne pll2_lock_1
215 /*
216 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
217 ;gmac tx clock programming
218 ;misccntl->periph1_clken &= ~ periph_gmac;
219
220 ;misccntl->gmac_synth_clk = (synth_xmask & (0x2<<16)) | (synth_ymask & 0x3); //375*(x/y)=125
221 ;misccntl->gmac_ctr_reg = txclk_synth;
222
223 ;misccntl->periph1_clken |= periph_gmac;
224 ;misccntl->periph1_rst |= periph_gmac;
225 ;misccntl->periph1_rst &= ~periph_gmac;
226
227
228
229 ;misccntl->periph1_clken &= ~ periph_gmac;
230 */
/* periph1_clken==0x2c,此偏移對應的寄存器是perip1_clk_enb
* 該寄存器的功能是設定外設時鐘的使能
* 這裡先關閉gmac的時鐘,然後對gmac進行相關設定之後再打開gmac的時鐘
231 ldr r1, =misc_base
232 ldr r2, [r1, #periph1_clken] /*;// read the peripheral-1 clock enable register */
233 bic r2, r2, #gmac /* gmac==0x00800000,也就是将perip1_clk_enb寄存器中的gmac_clken位清零
* disable gmac ethernet的時鐘
234 str r2, [r1, #periph1_clken] /*;// set the enable value */ /* 儲存設定到寄存器 */
235
236 /*;misccntl->gmac_synth_clk = (synth_xmask & (0x2<<16)) | (synth_ymask & 0x3); //375*(x/y)=125 */
/* gmac_synth_clk==0x68,r1+gmac_synth_clk的位址對應與gmac_clk_synt寄存器。
* 對gmac_clk_synt寄存器進行設定,将其設定為0x00020003
237 ldr r2, =synth_xmask /* synth_xmask==0xffff0000 */
238 mov r3, #0x02
239 mov r3, r3, lsl #16
240 and r2, r2, r3 /* r2 = 0xffff0000 & 0x00020000 */
241 ldr r3, =synth_ymask /* synth_ymask==0x0000ffff */
242 and r3, r3, #0x03
243 orr r2, r2, r3 /* r2 = 0x00020000 | 0x00000003 */
244 str r2, [r1, #gmac_synth_clk] /* ;// read the peripheral-1 clock enable register */
245
246 /*;misccntl->gmac_ctr_reg = txclk_synth; */
/* gmac_ctr_reg==0xa8,對應于gmac_cfg_ctr寄存器 */
247 ldr r2, =0x00
248 str r2, [r1, #gmac_ctr_reg] /*;// read the peripheral-1 clock enable register */
249
250 /*;misccntl->periph1_clken |= periph_gmac; */
/* 重新将gmac對應的外設時鐘使能 */
251 ldr r2, [r1, #periph1_clken] /* ;// read the peripheral-1 clock enable register*/
252 orr r2, r2, #gmac
253 str r2, [r1, #periph1_clken] /* ;// set the enable value*/
254
255 /*;misccntl->periph1_rst |= periph_gmac; */
/* periph1_rst==0x38,對應于perip1_sof_rst寄存器
* 此處将periph1_rst寄存器中的gmac位置1是令gmac ethernet執行reset
* 使得以上對gmac的設定生效
256 ldr r2, [r1, #periph1_rst] /* ;// read the peripheral-1 clock enable register*/
257 orr r2, r2, #gmac
258 str r2, [r1, #periph1_rst] /* ;// set the enable value */
259
260 /* ;misccntl->periph1_rst &= ~periph_gmac;*/
/* 将perip1_sof_rst寄存器中的gmac複位,即reset完之後關閉reset功能 */
261 ldr r2, [r1, #periph1_rst] /*;// read the peripheral-1 clock enable register */
262 bic r2, r2, #gmac
263 str r2, [r1, #periph1_rst] /*;// set the enable value */
264
265
266
267
268
269
270
271 /* ; enable plltimeen */
/* 設定perip_clk_cfg寄存器的值為0x82,很奇怪手冊裡面沒有對該寄存器的具體說明 */
272 ldr r2,=0x82
273 str r2,[r1, #periphclk_cfg] /* periphclk_cfg==0x28 */
274
275 /* ; set pclkdiv & hclkdiv */
/* 設定core_clk_cfg寄存器的值為0x555 */
276 ldr r1, =misc_base
277 ldr r2,=0x555
278 str r2,[r1, #amba_clk_cfg] /* amba_clk_cfg==0x24 */
279
280 /* ; smi init */
281 ldr r1, =smi_base /* smi_base==0xfc000000 */
282 ldr r2, =bank_en /* bank_en==0x0000000f,低4位每一位代表一個bank
* 系統初始化的時候隻有bank0是enable的,以支援
* 從外部memory來引導系統
283 orr r2, r2, #dsel_time /* dsel_time==0x00000050 deselect time 5+1 smi_ck periods */
284 orr r2, r2, #prescalf /* prescalf==0x00000f00 ahb_ck prescaling value */
285 str r2, [r1, #smi_cr1] /* 儲存設定到寄存器smi_cr1 */
286
/* 又來一遍一樣的?不明白。。。 */
287 ldr r2, =bank_en
288 orr r2, r2, #dsel_time
289 orr r2, r2, #prescalf
290 str r2, [r1, #smi_cr1]
291
292
293 /*; setting sysctl to normal mode */
294 ldr r1, =sysctl_base /* sysctl_base==0xfca00000,是所有系統控制寄存器的基位址 */
295 ldr r3,[r1, #scctrl ] /* 讀取scctrl寄存器的值儲存在r3中 */
296 mov r2,#0x00000004
297 str r2,[r1, #scctrl] /* 将scctrl寄存器的值設定成0x00000004
* scctrl寄存器中最低3位為'b1xx(x表示任意值)時為normal模式
298
299 /* ; wait for normal mode */
300 ldr r1, =sysctl_base
/* scctrl寄存器的[6:3]位為modestatus位,當modestatus為'b0100時,
* 表示系統處于normal模式。
* 下面的代碼中,0x20=='b0(0100)000,括号中的4位即與modestatus對應。
* 當從scctrl寄存器讀出的内容經過掩碼後與0x20相同,則說明已經為normal模式了
* 否則說明還沒有切換到normal模式,繼續等待,知道成功切換到normal模式。
301 loop_normal:
302 ldr r2,[r1, #scctrl]
303 and r2,r2,#0x20
304 cmp r2,#0x20
305 bne loop_normal
306
/* 下面的代碼塊看似想要使能uart1、uart2和smi的時鐘
* 可是,感覺又好像沒有必要,因為315行這一句就使能了所有外設的時鐘了
* 有點詭異。。。沒搞明白為什麼要這麼做
307 /*; enable uart1, uart2, smi clocks */
308 ldr r1, =misc_base
309 ldr r2, [r1, #periph1_clken] /*;// read the peripheral-1 clock enable register */
310 /* ; enable uart1, uart2 & smi clocks */
311 orr r2, r2, #uart1 /* perip1_clk_enb第3位置1,使能uart1 clock */
312 orr r2, r2, #uart2 /* perip1_clk_enb第4位置1,使能uart2 clock */
313 orr r2, r2, #smi /* perip1_clk_enb第21位置1,使能serial flash clock */
314 ldr r3, =0xfffffff8
315 orr r2,r2,r3 /* ???這個比較詭異,看着好像是使能所有的外設時鐘??? */
316 str r2, [r1, #periph1_clken] /*;// set the enable value */
317
/* 這部分跟上面那部分代碼有的一拼,感覺是想要disable uart1、uart1和smi的reset,
* 可是323這一句就清除了所有外設的reset了
* 是以還是看不明白為什麼要這麼做。。。
318 /* ; remove reset of uart1, uart2, smi peripherals */
319 ldr r2, [r1, #periph1_rst]
320 bic r2, r2, #uart1
321 bic r2, r2, #uart2
322 bic r2, r2, #smi
323 mov r2,#0x00
324 str r2, [r1, #periph1_rst]
325
326 /*; remove reset on all ips */
327 /*; ldr r1,=misc_base */
328 /*@; ldr r2,=0x0 */
329 /*; str r2,[r1, #periph1_rst_reg] */
330
/* 這個标号是在watchdog引發中斷以後,用于直接傳回的,在整個sys_init的開頭就進行了判斷
* 另外,當不是由watchdog引發中斷時,程式走到這裡也将傳回,在我們上下文裡,将傳回xloader_entry
*/
331 normal_mode:
332 mov pc, r14
333