微波EDA网,见证研发工程师的成长!
首页 > 研发问答 > 嵌入式设计讨论 > FPGA,CPLD和ASIC > 自己写的DDR2控制器(含PHY)在FPGA上跑到1066了

自己写的DDR2控制器(含PHY)在FPGA上跑到1066了

时间:10-02 整理:3721RD 点击:

前后花了半个月完成,不过控制器代码2天就写好了,时序调了近10天才完美。手动布局、手动时钟树都用上,今天终于跑上1066~

Stratix III -3速度的片子,官方的ALTMEMPHY只支持到667。挂的是三星DDR2-800,上1066也是超频跑了~
欢迎大家对DDR2方面的问题进行讨论~

牛,代码共享不,呵呵

牛,稳定不?



   800及以下很稳定,1066的话信号质量差了很多 ,PHY只能刚好锁住。

能共享下代码和设计规范吗?



虽然猜到有人想要代码,但代码真不是关键。我搞定这个东东花了十几天 ,写代码才占两天,剩下的时间都在搞时序/PR。同样的代码,一开始时序没设全(我自以为设全了)时上板根本不动弹或者只能跑DDR2-400。
控制器代码的话,照着美光Datasheet的启动步骤来初始化,然后把读、写、刷新实现了就基本完成了(读写使用Auto Precharge可最大限度简化逻辑),效率可以慢慢优化,根本不急。我现在这个控制器逻辑就做得很简单,寄存器还没到200个。

时序的话容易遗漏的地方太多了:input/output delay要设双沿,然后要砍断很多伪路径(有些路径只分析半周期,有些路径只分析整周期);两个Clock Gating检查(DQS_i、DQS_o,其中DQS_i的Gate信号有两个);三个时钟域(命令时钟域CK、读数据时钟域DQS_i、写数据时钟域DQS_o);一个Clock Skew检查(CK和DQS_o的Skew,其实主要检查的是DQS Pad的oe信号,保证Write Postamble)。
P&R的话时钟树创建三个共同时钟路径点:整个控制器的时钟(靠近控制器逻辑)、命令输出时钟(靠近CK/CK#的Pad)、数据输出时钟(靠近DQS/DQS#的Pad)。写时钟的时候没有PHY调整,所以布局应比读电路更优先。创建共同时钟路径可以让Timequest通过CPPR优化Slack,如果直接用CLKCTRL过来的全局时钟,那时钟路径的OCV范围就高达几百ps,在我这片-3速度的Stratix III上跑上1066根本不要想。

那分享下时序约束那些吧,不然小编这么就是炫耀贴啊

时序约束我楼上已经用文字初略描述了,你有兴趣可以就其中几点细问啊,比如怎样实现Clock Gating约束(Timequest中没有set_clock_gating_check命令),或者怎样在PR时平衡CK和DQS的skew啊之类。

你也不想我把sdc贴出来刷屏吧,那个没完善的文档说明没什么意义。



   感谢小编细心的回复!



   还有一点不是很明白,也想请教一下小编。我一直不是很明白,FPGA上怎么做P&R,我一直以为这些工作时FPGA自动完成了,不能手动去控制...

[quote]回复  Timme

   还有一点不是很明白,也想请教一下小编。我一直不是很明白,FPGA上怎么做P&R,我一直 ...
ysxiliu 发表于 2012-8-13 09:07 [url=
对数据路径,我们可以指定关键的CELL/DFF摆到指定的位置;对时钟路径,我们可以指定是否走全局时钟,可以让时钟通过特定的某点(通过RTL中加keep注释实现)
这年头所有eda软件都能自动跑,asic后端工具里也可以点几下就自动跑出版图,但手动干预总是更好的。Quartus对时钟路径的优化比较弱,手动做时钟树一般可以使时序优化1~2ns...



   受教了,谢谢小编~



   我写过一个支持DDR2/3的控制器,在V6的FPGA上也只能跑300M多。不怎么会约束时钟,一般只是在综合时用synplify自动加约束,问下LZ手动的约束怎么加啊?
    我的PHY使用的XILINX和ALTERA的IP中的PHY,一般就是对PHY中的PLL输入输出的钟加周期的约束

你的控制器现在只是简单的协议解释,本身的时序就很好,不过要以后用的话加上客户接口,和请求的调度什么的,估计就跑不了这么高。那些请求调度算法还是比较复杂的,一般都是大公司的专利,不怎么好提高时序

LZ,你的ddr效率如何?

对Altera的片子,在Quartus中你可以先在Timequest里用向导添加SDC约束,以此入门。DDR的话输入输出的端口肯定要设双沿约束。

Xilinx的时序约束比较弱,想自己写PHY跑上1066估计是个梦。
时序约束只是入门第一步而已;并不是写好一个完美的约束,工具就会输出一个满意的结果。

调度算法一般损耗的是Command Rate,并不冲击接口时序。我现在在1066下跑的还是CR1,一般的商业控制器会延缓到CR2或CR3。

你可以理解为,将复杂的调度用流水线来做,就可以跑高频了
另:控制器的速度瓶颈主要在I/O,单纯片内跑上五六百兆根本谈不上难度。

现在的Latency可以到26.25ns,全球最低。AIDA64中内存潜伏最低的也有47.5ns。
现在的应用只关心延时不关心带宽,所以没对带宽作优化。



   额···你这个貌似还只是去验证你的时序命令有没有跑对而已吗?

现在验证了的有发命令、写校验码、PHY依次作5级偏移读回校验码、选择最佳PHY偏移后的长期数据读写。Latency是指控制器外部发出读请求到返回第一个数据的延时。
命令的Toggle Rate只有数据的一半,命令都跑不对就不用玩了。写数据是最难的,读数据有PHY支持感觉还好。



    PHY也是自己写的吗?

对,是搭的5级延时链,用set_net_delay约束每级延时200ps。板上证明这个PHY从400到1066都跑得很欢。

那PHY与DRAM间那些电气连接怎么处理的呢? 使用ALTERA的开发板吗?频率高了的话,PHY与DDR间的布线会对稳定性有较大影响
我记得IP中的PHY会在上电后根据频率发些读写校验操作,来自动调整读写的相位,好像是LEVEL操作吧?当时就没怎么看懂,问下LZ是怎么实现的?

PHY连在DQS上,DQS出去直接调用带OCT的差分I/O就可以了。板子是自己这边的人布的,遵循Microstrip阻抗匹配规范即可。
读写校验在上电完做,先往0地址写55AA作校验码,然后控制PHY作5级延时分别读5次,看读对了几次,选择读对那几次中间的延时固定住,然后开始响应正常的读写请求。

请问大神,如何手动布局、手动时钟树啊?我做了4年多了,一直不会啊。也找不到这方面的教程。请求大神指导!

quartus如何约束dff的位置啊,ise中很好约束

以Quartus为例(延时数据为Stratix III器件典型延时):
::手动布局::

  1. module top(input clk,din,output dout);

  2. reg din_ff,din_ff2;

  3. always@(posedge clk)
  4.    begin
  5.              din_ff  <= din;
  6.              din_ff2 <= din_ff;
  7.    end

  8. assign dout = din_ff2;

  9. endmodule

复制代码


忽略IO时序,怎样使这个电路跑到最快?在FPGA中线延时占主导,因此我们首先需要把din_ff和din_ff2两个寄存器邻近摆放。在Assignment Editor中,新增Location规则,目标选择din_ff或din_ff2,位置选择“LAB”,再选择你希望的LAB坐标(在此例中具体坐标在哪不重要)。将这两个寄存器摆到同一个LAB内,就实现了数据路径上的最优化。 这就是所谓的“手动布局”。
一个寄存器的Tsu/Th大概是50ps,Tco大概85ps,同一个LAB的互联延时大概200ps,忽略Minimum Pulse Width约束,是否意味着这个电路的最小周期是335ps即能跑3GHz呢?注意,此时数据路径上已经没有瓶颈了。
****************........我是分割线.......*****************

::基于OCV的fmax计算::
查看Timequest报告,你会发现报出的fmax远小于期望的3GHz,为什么呢?

在Quartus自动PR时,时钟通常会被拉到全局缓冲CLKCTRL(这有时是不可避免的,如PLL输出),再拉到每个DFF。虽然全局时钟到每个DFF的平均Skew较小,但绝对延时是比较大的(ns级)。而Timequest的分析是基于OCV的(简单来说,就是所有延时都有min和max),延时的绝对值越大,min和max的差值也越大。比如,假定CLKCTRL出来的延时如下:

  1. CLKCTRL -> din_ff|clk  平均延时2ns(min 1.5ns,max 2.5ns)
  2. CLKCTRL -> din_ff2|clk 平均延时2ns(min 1.5ns,max 2.5ns)

复制代码


工具先修Hold Time,din_ff到din_ff2的最快路径为(我们假定Tco(min)+线延时(min)-Th =250ps):

  1. Data(early):CLKCTRL ----1.5ns----> din_ff|clk ----250ps----> din_ff2|d

复制代码

din_ff2的最慢时钟路径为:

  1. Clock(late):CLKCTRL ----2.5ns----> din_ff2|clk

复制代码


(1.5ns+250ps<2.5ns)此时工具发现需要在din_ff后面插750ps(min)的Buffer才能使Hold Time无违规(FPGA中不插Delay Cell,一般是绕线)。插完Buffer后,我们来看看此时能达到的最小周期(计算Setup Time):
din_ff到din_ff2的最慢路径为(我们假定Tco(max)+线延时 (max)+Tsu = 350ps,插的Buffer(max)=950ps):

  1. Data(late): CLKCTRL ----2.5ns----> din_ff|clk ----350ps+950ps=1.3ns----> din_ff2|d

复制代码


din_ff2的最快时钟路径为:

  1. Clock(early): CLKCTRL ----1.5ns----> din_ff2|clk

复制代码


数据路径相对于时钟的延时为(2.5ns+1.3ns-1.5ns=2.3ns),所以最小时钟周期2.3ns,对应fmax为435MHz,与一开始预测的3GHz差别巨大。
细心的朋友会发现,这个最小周期值非常接近于CLKCTRL延时(max-min)的两倍。
****************........我是分割线.......*****************
::手动时钟树 -- 创建共同时钟路径::
OCV的计算引擎会对共同时钟路径不计算min/max,因为物理上同一段路径的延时是固定的,这个在Quartus中称为CCPP,在Cadence中称为CPPR,在Synopsys中称为CRPR。
既然CLKCTRL延时巨大,那我们可以在CLKCTRL到两个寄存器这段路径上创建共同时钟路径,减小CLKCTRL延时(max-min)对时序的冲击。我们对原代码作以下修改:

  1. module top(input clk,din,output dout);

  2. wire clk_buf/*synthesis keep*/;
  3. assign clk_buf = clk;
  4. reg din_ff,din_ff2;

  5. always@(posedge clk_buf)
  6.    begin
  7.              din_ff  <= din;
  8.              din_ff2 <= din_ff;
  9.    end

  10. assign dout = din_ff2;

  11. endmodule

复制代码


通过Location规则把clk_buf节点放在与两个寄存器同一个LAB内,取得最大的共同时钟路径和最小的分离时钟路径。此时再计算一下Hold Time(假定clk_buf到寄存器时钟端的延时min=300ps,max=400ps):

  1. Data(early):clk_buf ----300ps----> din_ff|clk ----250ps----> din_ff2|d
  2. Clock(late):clk_buf ----400ps----> din_ff2|clk

复制代码


(300ps+250ps>400ps)可见Hold Time无违规,不需要插Buffer。此时计算Setup余量:

  1. Data(late):clk_buf ----400ps----> din_ff|clk ----350ps----> din_ff2|d
  2. Clock(early):clk_buf ----300ps----> din_ff2|clk

复制代码


数据路径相对延时为(400ps+350ps-300ps=450ps),如不考虑Minimum Pulse Width约束,则最小周期450ps,fmax为2.2GHz。这就是手动时钟树 -- 创建共同时钟路径的威力。
****************........我是分割线.......*****************
::手动时钟树 -- 手动Skew调整::
Quartus的Fitter中有很多优化Clock Skew的选项,其实选了根本没有任何效果。Quartus自动PR对时钟的优化就是这么弱。

我们还能进一步对时序进行优化,那就是Clock Skew手工调整,平衡(Tco+线延时+Tsu/-Th)那段250ps~350ps的数据延时。代码继续修改如下:

  1. module top(input clk,din,output dout);

  2. wire clk_buf/*synthesis keep*/;
  3. wire clk_buf2/*synthesis keep*/;
  4. assign clk_buf = clk;
  5. assign clk_buf2 = clk_buf;
  6. reg din_ff,din_ff2;

  7. always@(posedge clk_buf)
  8.    din_ff  <= din;

  9. always@(posedge clk_buf2)
  10.    din_ff2 <= din_ff;

  11. assign dout = din_ff2;

  12. endmodule

复制代码


假定clk_buf到clk_buf2的延时为250ps~300ps(如需更大Skew,在SDC中用set_net_delay -min/max对clk_buf到clk_buf2的延时进行约束即可):

  1. Data(early):clk_buf ----300ps----> din_ff|clk ----250ps----> din_ff2|d
  2. Clock(late):clk_buf ----300ps----> clk_buf2 ----400ps----> din_ff2|clk

复制代码



(300ps+250ps<300ps+400ps)可见Hold Time违规,工具需要插150ps(min)的Buffer。假设Buffer的延时为150ps~200ps,此时计算Setup余量:

  1. Data(late):clk_buf ----400ps----> din_ff|clk ----350ps+200ps----> din_ff2|d
  2. Clock(early):clk_buf ----250ps----> clk_buf2 ----300ps----> din_ff2|clk

复制代码


(400ps+350ps+200ps-250ps-300ps=400ps), 如不考虑Minimum Pulse Width约束, 则最小周期缩短到400ps,fmax可达2.5GHz。
这就是手动时钟树 -- 手动Skew调整,这个手段在优化对外控制器模块时非常有用,用于平衡内部时钟输出到Pad的巨大延时。
****************........我是分割线.......*****************
在顶楼的DDR2-1066控制器实现过程中,大量应用了“手动布局手动时钟树 -- 创建共同时钟路径手动时钟树 -- 手动Skew调整”三项PR技术,使得用内部ALUT搭的控制器/PHY电路,最高频率得以赶超片内硬核。



    有一个问题,你的时钟clk和数据din在端口时满足建立和保持时间要求,然后数据到内部寄存器端口,时钟到达clk_buf,此时两者的相对相位关系应该发生了变化。不一定能满足时序要求。这个为何没有约束呢?

第二句话我就说了忽略IO时序。OCV加上IO时序三天三夜讲不完了。
这些都是举片内电路为例,IO的可以同理类推,手段类似。

按照你的方法在quartus stratix III里面实验了一下。最小脉宽违规!不知道你怎么通过时序的,可以跑2.5GHz?

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top