从TimeQuest角度看create_generated_clock

最近在学SDRAM,听说SDRAM涉及到静态时序分析,那还说什么呢学吧。

在看到create_clock与create_generated_clock时我彻底疑惑了, 即然有了create_clock何必还要create_generated_clock呢,google一翻后得到一个结论,create_generated_clock是用于衍生时钟,TimeQuest作STA分析时会自动计算source clock 到generated_clock之间的skew.我又不明白了即然TimeQuest可以计算时钟偏斜,那直接对source clock进行create_clock不就行了,TimeQuest不就会自动对其衍生时钟偏斜进行分析了么。可能这个问题太低级了,大牛觉得太显然对此一略而过,也可能是我理解能力太差,大牛写了我没领会到,不管怎么样我是百思不得其解,早晨醒来就有了一个猜想。TimeQuest号称STA工具,那么他应该不会理解衍生时钟的逻辑功能,只能单纯的根据uTco、clock skew、data path等时间值对setup time、hold time、removal time、recoveral time的余量进行分析。也就是说TimeQuest只会对内部逻辑延时进行计算不进行逻辑分析,实际上TimeQuest并不知道你的衍生时钟与源时钟之间的关系。因此需要create_generated_clock对衍生时钟进行说明,下面用图来说明一下。

假设使用逻辑衍生出的理想时序如下图:

图中clock为源时钟,invert_clock为衍生时钟,与源时钟相移180度,如果仅使用create_clock来对clock时行约束,那么,TimeQuest在进行STA时对invert_clock的时钟所理解的波形应该如下图:

在上图中,clock skew仅仅是由时钟衍生逻辑产生的延时,不包括逻辑产生的相移,也就是说TimeQuest无法理解clock进行一块逻辑后出来的具体时钟与源时钟的真正关系,仅能靠一些路径延迟与DFF或LUT的参数来计算时钟延时。

而如果我们使用create_generated_clock对invert_clock进行约束后,TimeQuest在进行STA时对invert_clock的时钟所理解应该如下图:

也就是说只有对衍生时钟进行create_generated_clock约束,TimeQuest才能正确的理解波形,换句话说,一些走线延时、LUT、DFF的参数产生的问题由TimeQuest来自动搞定,如果是由于我们自己用逻辑产生的影响(如上图相位偏移),我们必须报告给TimeQuest,TimeQuest才正在静态时序分析时进行正确的计算。由于上面的TimeQuest所有过程均为我猜测得出,所以有不对的地方,请大家拍砖。

从TimeQuest角度看set_max_delay

今天开始看特权大大的《实战演练之时序收敛》,看到set_max_delay时跟着做了一下,设置了最大延时为3ns,然后report timing突然自动飘红了,很意外,于是看了看瓢红的路径的waveform,意外的发现set_max_delay中设置的值成了latch edge time,由于E文不好google了半天也没找到原因,于是再次祭法宝(从TimeQuest方向进行猜测)。由于report timing飘红让我这种初学者心里有压力,于是先将set_max_delay设置为5ns,然后果然不飘红了。开始找原因吧,先去data require path看看,果然latch edge time变成了5ns,也就是说latch edge time就是设置的最大延时。

再看一下data arrive path

发现上图中的所有的时钟延时为负的都是由PLL产生的,我理解是因为PLL自动对时钟相位进行了负的偏移来修正走线以及其他的的延时。

再看一下Technology Map Viewer中的视图:

从上面的图中我们可以看到其实在这两个节点上所谓的setup slack分析其实无非就是想说明从clk的时钟信号经过锁相环的变换到sr_clk输出共花费了3.045ns,也就是说clk+走线延时+逻辑延时-(PLL修正的延时的绝对值)就是clk到sr_clk之间的输出延时。那么为什么set_max_delay的值为什么变为latch edge time了呢,下面再看一下waveform,如图:

新建

这下都清晰了,其实TimeQuest是借用了register to register的setup slack的分析模型来检查,布局布线后的延时是否大于我们set_max_delay中设置的延时。由图可以看到,clk经过变换达到引脚sr_clk(包含PLL的相位偏移修正)共是3.045ns,这点也可以从data arrive path上看出,然后将latch edge time设置为set_max_delay的值这样就可以检测set_max_delay 的值是否大于clk到sr_clk的延时,也就是说其实这里仅仅巧妙的借用了setup slack的分析模型来检测布线延时是否满足要示。最后,一切均为猜测,没有官方依据,如有不对请大家指出哈!

关于keil编译cortex-m3纯汇编时为什么问题使用align地址问题

在编译下面一段代码时:

STACK_TOP EQU 0x20002000
AREA Reset,CODE,READONLY
DCD 0x20002000
DCD Start
ENTRY
; CODE16
Start
ldr r2,=Test
LDRD r0,r1,[r2,#4]
LDRD r0,r1,[r2]
LDRD r0,r1,[r2]
; movs r0,r0
; NOP
; align 4
Test
DCD 0x12345678
END

我发现,如果加上NOP或align4,程序就不会跑飞,否则程序就跑飞了。

经调试发现:    如果不加NOP或align 4的话产生的Test的标号地址就会产生错误,而LDRD 指令操作的地址必须是4字节对节的,如果使用的地址不是四字节对齐,那么程序就会产生异常,所以程序就跑飞了。  那么为什么不加NOP或align 4的话Test标号地址就会产生错误呢?  来看一段手册上的话:

4

 

也就是说DCD是需要标号地址按字对齐的,如果你没有对齐就可以看到如下的编译警告:test.asm(18): warning: A1581W: Added 2 bytes of padding at address 0x1a
这说明编译器会自动添加两个字节来帮你对齐,数据分布情况和下面很相似:

STACK_TOP EQU 0x20002000
AREA Reset,CODE,READONLY
DCD 0x20002000
DCD Start
ENTRY
; CODE16

Start
ldr r2,=Test
LDRD r0,r1,[r2,#4]
LDRD r0,r1,[r2]
LDRD r0,r1,[r2]
; movs r0,r0
; NOP
; align 4
Test
dcb 00 ;编译器自动添加
dcb 00 ;编译器自动添加,而movs r0,r0的机器码就是0x0000,会被
;编译器翻译成movs r0,r0,不是当作数据0x0000
DCDU 0x12345678
END

也许看来这样就完美了,但是程序依然会跑飞。原因有两点:
1.即使加了两个字节那么Test的标号地址依然不是四字节对齐。

2.这两个字节的零会被编译器当作指令来处理的,这也就是说Test标号会被编译器来当作代码标号来处理,看到了吧,我们的数据编译器一插手就变成代码了,实在无奈的很。再来看一段手册上的讲解:

5

 

也就是实际上LDR r2,=Test执行后,r2=Test+1这也解释了为什么不加NOP或align 4的话r2=0x8000017而加了NOP或Align 4就r2=0x8000018。那么来看一下,加nop或align 4后的效果:


STACK_TOP EQU 0x20002000
AREA Reset,CODE,READONLY
DCD 0x20002000
DCD Start
ENTRY
; CODE16

Start
ldr r2,=Test
LDRD r0,r1,[r2,#4]
LDRD r0,r1,[r2]
LDRD r0,r1,[r2]
; movs r0,r0
; 如果是align 4会被加两个节字的movs r0,r0(机器码为0x0000)
; 如果是nop 则会被加上nop的机器码(0xBF00)

; NOP

align 4
Test
DCDU 0x12345678
END
        需要说明的是,我总是把align 和nop 放在一块说,并不是说nop也具有对齐作用。是因为加上nop后刚好可以使Test标号地址放在4字节对齐的其他地方。在其他地方,nop也许并无此作用。

stm32的窗口看门狗的一点发现

今天看stm32的窗口看门狗,突然发现很不正常,于是做了各方面的实验得出以下结论,由于只是实验了得出的结论没有官方结论,所以如果有人有理论依据请告知我,非常感谢1
1.一旦使能窗口看门狗的时钟,SR寄存器的EWIF位就会被置1
2.一旦在定义的窗口范围外喂狗就会复位
3.在没有使能窗口看门狗定时器的情况下(即CR寄存器的WDGA寄存器置1来使能看门狗),SR不能被软件清0
结论:所以必须把窗口看门狗的清0,使能中断放在初始化的最后2句才能正常运行!

关于stm32 APB总线上的”接口时钟使能”与”外设时钟使能”

今天看<<例说stm32>>上的RTC时钟,发现他没有使能APB1的上BKP时钟,就能读出BKP寄存器里的内容.很是不解,又看了一遍发现APB总线上的时钟使能分为两种,一种是时接口时钟一种是外设时钟.
又仔细观查了一下时钟树发现在APB外设时钟使能寄存器上表现为接口时钟的都已经有了自己的时钟,再经实验证明所有这种不需要在APB上使能就有自已时钟的外设(即在APBxENR的某一位上代表的是这个外设的接口时钟的外设)在不需要使能接口时钟的情况下就能读出这种外设的寄存器的内容!

USB设备的插入检测

  又被坑了,总算解决了,要睡了长话短说。首先,着重强调 Device Class GUID, Device Interface Class GUID, Interface GUID是有区别的,另外奉上两篇MSDN文章:

  

  http://msdn.microsoft.com/en-us/library/windows/hardware/ff553412(v=vs.85).aspx

  http://msdn.microsoft.com/en-us/library/windows/hardware/ff553428(v=vs.85).aspx

  Device Class GUID 是INF文件中Verison Section中指名的设备类GUID,他标志着此设备图标,此设备出现在哪一栏,电压电流等各种信息。

    Interface GUID是指驱动程序中的GUID, 用于使用API打开此设备进行一系列读写操作,API代码如下(FINDSTR_USB_DEVICE即为我驱动程序中使用的GUID):

 1     //得到设备的句柄
 2         HDEVINFO    info=SetupDiGetClassDevs((LPGUID)&FINDSTR_USB_DEVICE,NULL,NULL,DIGCF_PRESENT|DIGCF_INTERFACEDEVICE);
 3         if(info==INVALID_HANDLE_VALUE)
 4         {
 5             AfxMessageBox("No HDEVINFO avaliable for this GUIDn");
 6             return    ;
 7         }
 8 
 9         SP_INTERFACE_DEVICE_DATA ifdata;
10         ifdata.cbSize=sizeof(ifdata);
11         DWORD instance=0;
12         if(!SetupDiEnumDeviceInterfaces(info,NULL,(LPGUID)&FINDSTR_USB_DEVICE,instance,&ifdata))
13         {
14             AfxMessageBox("No SP_INTERFACE_DEVICE_DATA available for this GUID instance");
15             SetupDiDestroyDeviceInfoList(info);
16             return    ;
17         }
18 
19         //得到符号链接名
20         DWORD ReqLen;
21         SetupDiGetDeviceInterfaceDetail(info,&ifdata,NULL,0,&ReqLen,NULL);
22         PSP_INTERFACE_DEVICE_DETAIL_DATA ifDetail=(PSP_INTERFACE_DEVICE_DETAIL_DATA)new char[ReqLen];
23         if(ifDetail==NULL)
24         {
25             AfxMessageBox("Error 1");
26             SetupDiDestroyDeviceInfoList(info);
27             return    ;
28         }
29         ifDetail->cbSize=sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
30         if(!SetupDiGetDeviceInterfaceDetail(info,&ifdata,ifDetail,ReqLen,NULL,NULL))
31         {
32             AfxMessageBox("Error 2");
33             SetupDiDestroyDeviceInfoList(info);
34             delete ifDetail;
35             return    ;
36         }
37         char buff[256]={0};
38         char * format="Symbolic link is %sn";
39         sprintf(buff,format,ifDetail->DevicePath);
40         HANDLE    rv=CreateFile(ifDetail->DevicePath,
41                         GENERIC_READ|GENERIC_WRITE,
42                         FILE_SHARE_READ|FILE_SHARE_WRITE,
43                         NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,NULL);
44         //end of 得到设备句柄
45         delete    ifDetail;
46         SetupDiDestroyDeviceInfoList(info);
47 
48         hDevice=rv;
49         DWORD ThreadId;
50         hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)UpDate,this,0,&ThreadId);
51         m_EchoLed1.EnableWindow(TRUE);
52         m_EchoLed2.EnableWindow(TRUE);
53         m_OpenDevice.SetWindowText("关闭设备");

Device Interface Class GUID是Microsoft定义用来调用RegisterDeviceNotification时所使用的一类GUID,关于Microsoft所定义的所有Devcie Interface Class GUID 可以参看上面给出的网址,下面给出这类GUID的使用方法(用于动态检测某类USB设备的插入与拔出):

 1 //注册 某个USB设备接口类 事件
 2   GUID zz = {0xA5DCBF10, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 
 3         0x51, 0xED};
 4     DEV_BROADCAST_DEVICEINTERFACE DevBroadcastDeviceInterface;
 5 
 6     DevBroadcastDeviceInterface.dbcc_size = sizeof(DevBroadcastDeviceInterface);
 7     DevBroadcastDeviceInterface.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
 8     DevBroadcastDeviceInterface.dbcc_classguid = zz;
 9     RegisterDeviceNotification(m_hWnd, &DevBroadcastDeviceInterface,
10         DEVICE_NOTIFY_WINDOW_HANDLE);

注:上述三种GUID没有任何实质上的关联,不能混为一谈。

  GUID(A5DCBF10-6530-11D2-901F-00C04FB951ED)写成代码为

  static const GUID x_guid = { 0xA5DCBF10, 0x6530, 0x11D2, 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED };

关于STM32单片机GPIO口上拉与下拉输入

      以前GPIO上接的电路都是低电平触发的那种,新画了个板子有一个按键设计的是高电平触发,结果IO口设置成上拉输入后,怎么读都不正确,按键电路如图1。无奈只能去调试一下,发现当设置为上拉输入后,其ODR(GPIO输出数据寄存器)相应的也置为1,百思不得其解。

图1 按键电路

     于是去看一下GPIO的结构图:

I/O引脚结构图

      由图可以发现其实输出寄存器与输入寄存器之间在I/O引脚处是线与状态,在GPIO设置成上位输入时,其上拉电阻闭合,这时如果输出寄存器设置为0那么在IO口内部就会自己损耗电流,而这些电流的损耗是会增加功耗的。因此当GPIO设置成上拉输入时,相应的输出寄存器也设置为1.

      将按键电路与I/O引脚结构图连起来看,就清晰多了,当GPIO设置为上位输入时,上拉电阻与按键的330欧、220K欧串联,这时IO口所读到的电平就是330欧与220K欧电阻分压的总合,由于这两个电阻阻值过大,因引读出的电压大于逻辑’1’的阀值。OK问题找到了,于是将IO设置成下拉输入,问题就解决了。因此这里得出一个结论,IO口是上拉还是下拉要根据,IO口外部接的空闲电平有关,如按键不按下时是低电平,则应该设置为下拉输入。

      在调试时还发现另一种方法可以让代码正常运行,就是当设置上拉输入时,将其输出值设置为0,这样电平就被直接拉低了,按键的接地电路基本就不起作用了,但这样做有一个坏处,前面已经说过了,这种情况会导致上拉电阻直接接地了,增加了不必要功耗,而且将3.3V电压直接加在了输出驱动器的N-MOS上,时间长了之后可能会造成芯片损坏!

关于stm32 APB总线上的”接口时钟使能”与”外设时钟使能”

今天看<<例说stm32>>上的RTC时钟,发现他没有使能APB1的上BKP时钟,就能读出BKP寄存器里的内容.很是不解,又看了一遍发现APB总线上的时钟使能分为两种,一种是时接口时钟一种是外设时钟.
又仔细观查了一下时钟树发现在APB外设时钟使能寄存器上表现为接口时钟的都已经有了自己的时钟,再经实验证明所有这种不需要在APB上使能就有自已时钟的外设(即在APBxENR的某一位上代表的是这个外设的接口时钟的外设)在不需要使能接口时钟的情况下就能读出这种外设的寄存器的内容!