文买了个JLINKV9,以为神器,拿到手发现根本不是,完全没必要替换V8,想自己做个另类的调试器,当然想只是想而已。又过了一阵,在网上居然发现ARM的开源调试器CMSIS DAP代码,回想起来,KL25小板子貌似不就是这个么,以前居然没注意。代码量很小于是很有兴趣,修改引脚编译了下到片子里,发现设备,飞线到别的板子,进入MDK顺利调试。有了这么好的一个参考样本,琢磨着这次得研究下SWD,又因为各种事没动工。时间到了上周末,周六睡懒觉到10点起床,想起有这回事,于是终于开工。好了要进入正题了。
搜了一通网页,找到隔壁论坛一个好帖子:“也谈SWD接口协议分析”,感谢原作者,这个给了我起步帮助。除了SWD一点基本了解,还从中知道要玩这个要看ADIV5等等ARM原作,英文大部头有木有,英文不好有木有,读这些不如杀了我。让正规军去啃大部头吧,游击队必须另辟蹊径才能在周末搞定这事了。
年的STM32中文参考手册,CM3权威指南中文版,CM3技术参考手册中文版(这三本好歹以前读过有印象)。
理,分别有DP和AP寄存器,无非是通过多级级联后访问就像指针间接访问变量。但是参考手册只讲了寄存器地址和名字并没有详细位定义内容,又但是,望文会意,名字就很清楚告知了用途和大概使用方法,英文虽然差,好歹几个基本单词还认识。比如ADD,DAT,SELECT ID神马的。基本协议也对照“也谈SWD接口协议分析”看了一下。
  迫不及待开始敲代码试一把,先不管寄存器内容定义了。读DP_ID顺利通过,操作别的DP出错。只好回来找寄存器定义,计数参考手册派上用场鸟,一通的#DEFINE,再次上马读AP_ID,还是失败,百思不得其解啊,好歹咱悟性不至于这么愚钝吧,最后放弃治疗了,逼我回头练第二招--仿造。
先示波器抓了一把JLINK和CMSIS DAP的时序,看着波形也是醉了,一个平时CLK为高,一个平时CLK为低。我的是平时CLK为低跟JLINK一样。人眼解码了几个数,照做还是错误,总感觉能读对DP_ID应该时序不会错,一定是我软件哪里有问题,彻底较劲钻牛角尖了,闹了整整半天天黑了又一次放弃治疗,继续本招第二手,没有JLINK源代码好歹有个DAP源代码在手,做不出了只能抄。细读了底层时序,在DAP工程里临时修改测试读AP_ID没问题,于是将DAP代码照猫画虎过来。还真药到病除,读到AP_ID了。顺便将DAP通讯协议分析了下,上BUSHOUND,然后写程序将BUSHOUND记录文本解析,就不用人眼解码示波器了。第一天就这样完了。
来条USB帧数据,丢掉一大堆花里胡哨读信息帧,到读ROMTAB地址了,继续看文档,明白了,AP地址固定,由AP得到ROMTAB地址,由ROMTAB地址得到各调试组件地址,TAB顾名思义当然是一大串了,也就是有很多调试组件。结果就是不管IC公司怎么折腾,可以由ARM定的一个死地址找到一大堆IC公司定的活动地址,是不是又跟指针间接访问对上了。然后为了方便像我这样的菜鸟不把自己搞晕,每个组件都在自己那一块地址的最后放了个ID号,跟GPS一样好使,不会随便迷航,DP和AP不就有ID么。插一句,大家读过ARM类单片机手册就知道,每个模块都是占一块地址的,不像51那样乱七八糟所有模块地址都挤在一起。当然ARM是不可能未仆先知IC公司会做多少ROM,RAM,多少外设等等,所以ROMTAB并不能提供普通用户资源的地址信息,这个要看芯片手册。自我感觉到这可以上岸洗白不要逆向分析别人东西,可以“自主研发”单飞自由发挥,事实上结果也如此。遍历调试组件以及ID一次过没问题。
位摊平了就是一张4G存储卡,通过地址访问,看起来整个单片机只要给地址任你蹂躏位地址的,调试器为何能在IDE上显示值?WHY?HOW?那么问题来了,访问PC指针哪家强? 继续读文档,明白了,再级联一次就解决,通过内核调试模块访问,在这里给PC神马的做了地址映射,看到没,还是遵守真理啊地址+指针永远伟光正。再次访问,依旧失败,想了下原因果然猜对了-----要先使能调试,这些玩意灰常重要的部件,不让你随便乱碰的,使能调试后才允许访问,至此,基本上全通了,剩下就是组合使用以及调试功能了。上权威指南研究,继续测试了停止CPU,释放CPU,单步运行等都通过。
下一步准备干什么是很显然的----脱机下载!搞了个BIN提取数组,将从机烧写文件合并到自己工程文件里。分别测试了RAM下载和ROM下载都顺利。无非就是上面知识的组合应用。至于细节,下载前当然要将CPU停住,下载后重新设置PC指针后再跑。你不会希望程序下到一半PC指针跑飞了各种奇葩意外等着你吧。想到安全问题,绞尽脑汁做了个有意思的实验,加入第三片单片机监听通信并控制DIO脚通断,抓到读DP_ID后将从机断开伪造一个ID给主机然后迅速接回从机。
年以上的电工应该很少会用到单步调试,断点之类的侵入式调试了,而会更多地采用串口打印之类的非侵入调试。非侵入调试的优点是显而易见的--------不干扰正常程序的执行,效率很高。当然了要解决诡异问题时候侵入式调试也会偶尔成为杀手锏。JLINK的RTT和SCOPE就是以这个为基础的,其中RTT的实现是开环形缓冲,CPU和RTT一个拿读指针一个拿写指针,就跟串口FIFO方式一样,缺点是只能通过超级终端交互,虽然JLINK手册也说了两种方法与自己程序对接,感觉终究还是不归自己控制。SCOPE更简单了,通过MAP文件确定变量地址,定时读回指定地址数据绘图。缺点大大的,不支持复杂定义比如数组和结构体(因为MAP只给地址嘛),不支持采样率设置,更不能自己随心所欲显示和记录。正因为如此才有了自己做个特色调试器想法。
好啦,折腾到周日晚上搞定收工,脑子彻底浆糊了。最后,当然是上艳照了,所谓无图无真相。整理了个DEMO运行打印信息。

 
总结陈词。。。

上面已经给出了完整过程和详细分析,以及重要的艳照。当然我是故意没放源码,这么大篇幅文字比源码有用的太多了,自我认为这算是赤裸裸的SWD接口个人开源贴了,打破核垄断人人都能玩转SWD,不算之前对STM32的了解和对JLINK之类的使用,其实我也就两天摸入门了,比几年前从零开始读OBD好太多了。如果什么都不说丢一堆源码有意思么,这篇文章已经把肉送到嘴巴了,只要自己再嚼两口就能吃,文中我略过了一些小细节比如时序图AP传递性之类,我相信一个熟悉STM32的电工看过我文章只用不到一天就能重走一遍我的路。自己重走一遍找到这些小细节更有利于自己的理解,我相信各位不用抄能做出来。SO,不回复不讨论任何SWD细节技术问题,请自行验证自己的疑问和猜想。不然,你还真对不起我码这么长的字,大家尊重一下我哈,下面几段则热烈欢迎大家踊跃讨论,各种讨论。

相信各位电工不用一天已经搞定DEMO了,有完美癖的接下来自行啃ARM文档去了解细节问题,加入各种细节处理追求完美。然后比如位段操作,全局变量,内联函数等等提高速度到极致。比如扩展到ST其他芯片,ATMEL,NXP,FREESCALE等等。

对脱机下载,很显然想到两种,一是直接代替CPU操作,进行ROM烧写,简单直接。另一种就是先下载一个BOOTLOAD到RAM,然后用环形缓冲与BOOTLOAD交互,由BOOTLOAD来烧写FLASH,高级一些通用一些,MDK的芯片烧写算法估计就是个BOOTLOAD。又涉及保密问题了,别人可以监听时序。由此引入UID问题,BOOTLOAD也得是动态,先读UID回来,修改BOOTLOAD再下载BOOTLOAD,BOOTLOAD运行后读UID校验,校验OK才能与下载器交互,下载的ROM也是UID修改过的。具体如何修改和加密各有想法。当然别人可以抓出BOOTLOAD然后跑虚拟机研究或者反汇编研究。又当然对商品下载器可以每个客户给一个加密算法的BOOTLOAD,烧写员是老板自己心腹,屌丝电工产品没好到对方愿意不惜一切代价反汇编破解。

对调试器,你能做到JLINK的兼容性?你能做到JLINK的功能细节?你愿意花多少精力去嵌入IDE(搜了下COOCOX是实现了嵌入IDE)?你有多少时间闲折腾?你做好了能卖钱不会为别人做嫁衣?回答全是NO.但是屌丝电工们还是很容易满足的,第一咱们可以将DAP嵌入工程内,满足嵌入IDE的要求,满足单步和断点这种侵入式调试的要求。第二咱有白菜TFT+FLASH,脱机下载+调试PRINTF记录搞定。第三咱有物美价廉480M USB的高速CM4芯片,一头USB通电脑,一头通被调试芯片,读写内存刷刷刷。以内存并行读写为基础做非侵入调试功能。对SCOPE功能,支持多种类型变量。任意指定每个变量的采样间隔,这就变成了一个每个通道都可以分别设置采样率的片上示波器&逻辑仪,我们的实体示波器还只能统一采样率呢。如果上位机还支持逻辑仪解码插件呢?只给出接口DLL和范例程序,支持范例程序脚本编程,支持自己任意设计,支持分享优秀设计呢?屌丝电工们有木有高大上感觉。对于RTT功能,没啥好说的,相当于将串口打印从串口&USB转移到SWD.好处依然是非侵入,比如单片机本来就是USB与上位机通信的,不用在USB通信中插入调试交互去挤占或影响正常通信,咱另修一条高速通道做调试交互。黑白超级终端滚粗,早看你不顺眼了,调试交互软件想怎么任性就怎么设计。依然给出接口DLL给出范例程序带脚本编程分享优秀设计。当然别忘了还能脱机记录调试信息,睡个觉抽根烟再来分析现场故障哦,对啦,脱机记录还能预设与被调试系统的应答哦,而非傻乎乎只知道抄写记录。最后,AVR的ARDUINO火的一塌糊涂,CCDEBUG可以SPI连接无线芯片,USBEE虚拟示波器也有意思。我们为毛不可以结合他们做个类似的傻瓜化工具,调试器将CPU 停掉接管资源,电脑简单控制语言控制调试器对被调试芯片的访问,比如类C代码ad_get(1,3);  指挥调试器去写被调试芯片的AD模块,并返回结果到电脑。 如果我用个WHILE(1)  AD_GET(1,3);呢,那不就是USBEE一样了。  如果我用个 if(AD_GET(1,3) > 300)  led_on();  呢,那不就是ARDUINO类似了。  总之,对现有ARM板子,小朋友们可以不会单片机不装编译器也能玩,具体实现方法很多。最后的最后,我们是单纯的屌丝电工,乐于吃着地沟油分享技术,不是想到个东西就考虑钱,这个设计显然是开源共享的。

JTAG接口还没研究,等年后哪天有时间,得来个姊妹篇 JTAG接口:探索&泄密&延伸

对啦,说起JLINK,又说几句,JLINKV9出来后翻找了一些JLINK的帖子,得知APP是明码放在DLL文件里,直接提取的。BOOT是通过木马APP搞出来的,木马APP的关键是通过BOOT或上位机的验证才能下进去。已经有不少人搞定了,SO,SEGGER应该是对加密没太上心,大家可以讨论下对JLINK BOOT的分析,以及如何防范别人窃取。

 
下午用电脑上了,发现这位兄弟真的是一个人看着我的贴摸索了好几个月了,赞一个。发现写的贴能帮到几个这样的兄弟还是挺开心的。又自己回翻了下自己文章,大概理解了一下,给你讲解下,时间太久,我也不确定自己讲解的对不对了,要是讲的不对就抱歉了,大家互相学习。
看样子你前面磕磕碰碰完成了SWD接口基本时序-》读写DP接口--》读写AP接口,现在卡在读取单片机普通寄存器阶段了,过了普通寄存器阶段还有一个阶段就是内核和调试模块。至此基本就全部完了。加油,马上就要搞定了。
摘录几句 希望能帮到你 
        //AP寄存器  APREG比较多 地址包括A[3:2]以及DPREG_SELECT即A[7:4]组成 32位地址所以低两位无效
        #define apreg_dgromaddr                (0xf8>>2)//只读 存放ROM_TAB绝对地址的寄存器                    这个一个AP寄存器地址定义

//各种寄存器读写操作宏定义 START   DP/AP  wr/RD   A3:2            PAR   STOP   FREE  
       #define code_regr_rom                        (( bv(1)| bv(2)|(2<<3))>>1 )        //读取ROM_TAB地址 命令         读取这个AP寄存器的操作编码

status = swd_transfer(code_regr_rom  ,dat);//        选择存放有ROM_TAB绝对地址的AP寄存器               本次读是读不回本次结果的 每次只能读回上一次结果 所以作为单独测试的DEMO 这次读回来的值扔掉

status = swd_transfer(code_regr_rbuf ,dat);//读取ROM_TAB的绝对地址              这次读到了上一条操作的结果        注意通过AP寄存器去读其他寄存器时候有延迟一拍,下一拍读上一拍的结果 最后一次用dpreg_readbuf读取最后一拍结果

rom_addr = dat[0]&0xfffffffc;                      //获取到的ROM_TAB绝对地址编码

status = swd_transfer(code_regw_addr ,&rom_addr);          //选择读ROM_TAB绝对地址

status = swd_transfer(code_regr_dat  ,dat);//读数据         依然本次操作要下一拍读回          本次读回的数据没用

status = swd_transfer(code_regr_rbuf ,dat);//读数据  都会了ROM_TAB绝对地址里面装的内容了

总结的说,AP和DP模块用的相对地址,可以调试器直接访问的。但各家单片机的地址映射表不同,包括FLASH  RAM  REG的位置都不同,需要绝对地址访问,这些都没法摸瞎访问。有个ROM_TAB记录了各个调试模块的绝对地址位置,但是这个ROM_TAB本身也是放在FLASH里的,也要通过绝对地址访问,各家也不同。于是就在AP里有个寄存器保存了这个ROM_TAB的绝对地址, 这样就可以一步步按图索骥。 先由AP的某个寄存器找到ROM_TAB的绝对地址,再访问ROM_TAB的绝对地址找到各个调试模块的绝对地址,然后就可以去访问各个调试模块了。   另外AP有个特性,通过它去读别的东西,本次只发出指令去操作,操作结果要等一下才有返回,你得下一拍去读回结果,估计写也是一样,但是写你不用管写完,所以感受不到。如果要读一串N个,每个都顺带取回前一个的结果,倒是不碍事,总共也只多费一个操作。如果是读一个,也是多费一个操作,效率就相当于折半了    N/(N+1)  VS   1/ (1+1)

注意了这两点应该不难了,祝你成功   @tgwfcc

 
过年最后一天提早来长沙,花了一天研究了下JTAG接口,也码上来。 
书接上回,说到SWD顺便七七八八已经说完了,这下JTAG就没多少东西了,只是简单记录一下过程。 
说起JTAG,普通电工跟它的相遇基本都是大学上FPGA课发现下载那个并口线叫JTAG,或者买并口头子和牛角插头在松烟中DIY古老的并口单片机下载器。文艺电工有些可能没上大学就跟JTAG早恋了,有些毕业后才遇到JTAG。遗憾的是,认识这么多年我也从没深入了解JTAG,就知道是神马IEEE神马边界扫描标准神马测试集成电路的。好吧,现在开始了解也为时不晚。
 
不管最初设计者对JTAG的期望是神马,现实就是现在JTAG已经满大桌了,各种芯片的下载和仿真几乎清一色的JTAG,估计超出了设计者的期望。也不知道这些JTAG协议是不是已经衍生出很多不兼容的版本了,估计除了硬件接口,二进制流和基本状态定义,其他都改了。别的芯片先不管,反正我们现在只看ARM的JTAG接口。
 
依然是看STM32中文手册,已经看过SWD部分,剩下JTAG部分就那么两三页。硬件接口上,JTAG一般都不用那根JNTRST脚,庞大的20PIN只要看4根线了,TCK,TMS,TDI,TDO。I和O命名是站在从机角度说的,因为STM32是被调试者。需要注意的是调试器需要将TDO上拉,不然输出一直为零,开始就注意这个,用浮空输入折腾了2分钟一直没数据,改上拉就好了。JTAG跟SPI神似,移位结构,所以可以像595芯片一样级联串成一大串用。在STM32芯片的链路上就串了2个,一个是ARM的CM3,一个是ST公司的TMC,我们要用的就是ARM的CM3,另一个估计是ST公司做芯片质检的?发现一个名词TAP,百度了下叫测试访问接口,估计跟DP,AP差不多意思,一个芯片内有多个器件,这个TAP就是访问各器件的接口模块,每个器件一个TAP挂在JTAG串上。又有两个名词IR和DR。IR用来指定DR,通过DR对器件做读写。两个TAP的IR长度分别为5位和4位。手册提到输入IR时扫描链=5+4,不用的TAP必须输入旁路指令。输入DR时不用的TAP被旁路,需要额外增加一位。这两句话很有用。列出了ARM TAP的IR指令表(即DR地址表),五个指令分别是:旁路  IDCODE   DP访问   AP访问  中止  还给出了每个DR的数据位定义,不同的DR位数不同。再列了一张JTAG DP的寄存器表,比SW 的DP寄存器少了很多,但是地址和数据位定义是相同的。JTAG的AP与SW 的AP是共用的。 看到这里,STM32中文手册用3页纸完整的说清楚了JTAG调试接口的结构和使用:正确连线JTAG的四根线,用旁路指令屏蔽链上其他TAP,目标TAP用IR选择不同的DR寄存器,被屏蔽的TAP在链上只有一位数据,不同DR位数不同。通过DR寄存器访问AP和DP,如何使用AP和DP的介绍在SW部分。中文手册到此结束。
 
尽管ST说的很清楚,我们也看得很仔细,但是依然无从下手。因为:我们只知道在CLK节拍下将数据注入流出,不知道是上升沿还是下降沿。不知道如何区分IR链和DR链,估计TMS就是干这个事的。网上一搜,马上发现一张图叫做TAP状态机的,解了疑惑。每个圈圈都是一个状态,数字表示TMS电平,在时钟上升沿锁存,这么一想,数据估计就是下降沿移位了,因为锁存会引发状态改变,数据必须在状态跳变前送进去。每个状态跳变只有2个分支,清晰简洁,弯箭头处可以停留,也就是可能用来输入数据,直箭头是不上客的小火车站,不可能输入数据。RST和IDLE的弯箭头只是为了提供条件保持当前状态,也不会有数据。PAUSE_DR和PAUSE_IR看名字就不是输入数据地方。DR和IR数据就只能分别在SHIFT_DR和SHIFT_IR处输入了。如果当前忘了自己处于哪个圈了肿么办呢?TMS=1一路走,只要五步保证会到达并停留在RST状态。当然也可以把剪了的那根RST线用起来,直接复位简单粗暴。这下子知道怎么用了。照着这个线路图走,到达SHIFT送数据再返回就对了。 
还是有疑问,对一片或一串已知芯片,链上有多少个器件,以及各自IR长度是知道的,如果是有未知芯片呢?都不知道挂了多少个,也不知道各自IR长度多少(DR只需要知道目标器件,其他器件可以旁路掉),没法用了。想了下所有器件RST后默认状态应该是一致的,旁路?IDCODE?这样可以通过判断注入=流出终止获得设备个数,IR也可以通过注入=流出终止获得IR总长度,各自IR长度依然无法知道,对照IR指令表,除非JTAG协议规定所有器件IDCODE地址必须都是1110格式即除了低位=0其他都为1,可以找零知道每个长度。还是用示波器抓仿真器波形来验证这个想法吧。发现是先在DR发送一长串读回ID,这一步知道了器件个数以及器件ID,然后发送IR,获得的是10001000011111这样格式,这一步知道了每个IR的长度。
 
可以动手测试了。就是将上次SWD的过程照搬了一遍,一切顺利。

JTAG虽然对ARM下载已经不重要,有两线的SWD好用的很。但是在这个遍地JTAG接口的时代,不知道JTAG的电工还真不好意思,没准哪天调试别的东西遇到问题就要这个。问题来了,JTAG没法用硬件SPI,怎么让它速度快。为什么复位后直接读DP是IDCODE,读IR没有被旁路的器件是1000格式,被旁路的器件是1111格式,有相关规定么,读过JTAG正规文档的高手们,给说下呗。

 
 
 
 
 
 
 
 

SWD接口:探索&泄密&延伸的更多相关文章

  1. 也谈SWD接口协议分析

    这几日看到坛里有几个关于SWD协议相关的文章,自己也尝试了下,有点体会,也有些疑惑,写出来与大家分享和交流下.    以下我的模拟SWD接口的板子简称为Host,目标MCU(即我要连接的板子)简称为T ...

  2. stm32的swd接口的烧写协议是否公开的呢?

    stm32的swd接口的烧写协议是否公开的呢? 需要用一台好的示波器来抓才能有足够的存储深度,保证你能够过滤掉那个该死的50clock. 按照Arm的手册,每次转换发送方都需要一个TNR---但是我观 ...

  3. SWD 接口电路

  4. 浅论各种调试接口(SWD、JTAG、Jlink、Ulink、STlink)的区别

    JTAG协议 JTAG(Joint Test Action Group,联合测试行动小组)是一种国际标准测试协议(IEEE 1149.1兼容),主要用于芯片内部测试.现在多数的高级器件都支持JTAG协 ...

  5. 用SWD调试接口测量代码运行时间 ( SWO )

    用SWD调试接口测量代码运行时间 关于时间测量的种种问题 在嵌入式中,我们经常需要测量某段代码的执行时间或测量事件触发的时间,常规的思路是: 1:在测量起始点,反转电平2:在测量结束点,再次反转电平 ...

  6. 【转】keil+stm32+jlink利用swd方式进行printf输出

    出处:http://www.douban.com/note/248637026/ ----------------------------------------------------------- ...

  7. Stm32 SWD 下载 调试配置

    找到一篇比较好的 关于stm32  SWD模式 下载 调试 配置文章 整理如下:       我们比较常用的是Jlink下载器 ,这种下载器有一个缺点就是使用的Jtag 20PIN接口,太多的PIN会 ...

  8. SWD模式和JTAG模式

    一.功能 SWD模式:仿真 下载 JTAG模式:仿真 下载 二.接口 1.J-link JTAG/SWD接口 2.开发板接口电路 ①SWD模式 4根线(包片机) ②JTAG模式 20脚JTAG(网络) ...

  9. SWD通讯

    这几日看到坛里有几个关于SWD协议相关的文章,自己也尝试了下,有点体会,也有些疑惑,写出来与大家分享和交流下.    以下我的模拟SWD接口的板子简称为Host,目标MCU(即我要连接的板子)简称为T ...

随机推荐

  1. VC++编程之对话框贴图

    基于对话框的程序写好后,为对话框贴上个图片让界面更加美观(我承认做界面,MFC显得力不从心,不如QT). 其实很简单,我们以位图为例,选好我们需要的位图资源(bmp),假若自己的图片不是位图资源,可以 ...

  2. SPA examples

    http://webdesignledger.com/inspiration/40-excellent-examples-of-single-page-websites https://onepage ...

  3. Convert Sorted Array to Binary Search Tree

    Convert Sorted Array to Binary Search Tree Given an array where elements are sorted in ascending ord ...

  4. 查mysql字段中的数字记录

    select * from a where nameregexp '^[0-9]+$' ;

  5. 四则运算出题器(C++)-BUG修复

    定制题目数量这个功能测试: (1)输入题目数为负数时: 可正确处理: (2)输入题目数量为0时: 可正确处理: (3)输入题目数量为小数时: 程序运行出错: 错误分析: 因为代码中题目数量的变量Que ...

  6. strlen和mb_strlen区别

    转自:http://blog.sina.com.cn/s/blog_5f0d5bd90100mzcl.html <?php//测试时文件的编码方式要是UTF8$str='中文a字1符';echo ...

  7. Windows下配置使用MemCached

    工具: memcached-1.2.6-win32-bin.zip     MemCached服务端程序(for win) Memcached Manager             win下的Mem ...

  8. Linux - 常用命令行(一)

    今天和大家分享一些最基本常用命令行:也是作为新手最应该了解掌握的 ls 命令:用来显示指定工作目录下内容 dir命令:与ls命令一致 cd  命令:变化工作目录 pwd命令:显示用户当前的工作路径,显 ...

  9. 【BZOJ】【3522】【POI2014】Hotel

    暴力/树形DP 要求在树上找出等距三点,求方案数,那么用类似Free Tour2那样的合并方法,可以写出: f[i][j]表示以 i 为根的子树中,距离 i 为 j 的点有多少个: g[i][j]表示 ...

  10. Sea.js入门

    本文只是seajs的入门贴.要详细了解,请看GitHub主页上的相关链接,精彩不断,精选几篇: 前端模块化开发的价值 前端模块化开发的历史 ID和路径匹配原则 与RequireJS的异同 模块的加载启 ...