行列地址线被选中后,数据线(data_bit)直接和电容相连接。当写入时,数据线给电容充放电;读取时,电容将数据线拉高或者置低。
7 V% k/ D9 M! C# q
SDRAM 的全称即同步动态随机存储器(Synchronous Dynamic Random Access Memory);这里的同步是指其时钟频率与对应控制器的系统时钟频率相同,并且内部命令的发送与数据传输都是以该时钟为基准;动态是指存储阵列需要不断的刷新来保证数据不丢失。
5 x# [* E; O& u' V7 d0 {( C3 j
SDR SDRAM中的SDR是指单数据速率,即每一根数据线上,每个时钟只传输一个bit的数据。SDR SDRAM的时钟频率可以达到100MHz以上,按照100MHz的速率计算,一片16位数据宽度的SDR SDRAM的读写数据带宽可以达到1.6Gbit/s。
4 J& k3 ~4 f/ B, p
SANXIN – B01的开发板上有一个容量为256Mbit(16M x 16bit)的SDR SDRAM(H57V2562GTR)。其内部存储时,分为了4个独立的区域(BANK),每个bank为4Mx16bit的存储空间;每个bank在存储时,按照二维的方式进行存储,利用行列来进行确定,有8192行(13bit地址线),有512列(9bit地址线),8192 x 512为4M的存储量。
1 ] I0 e6 |$ Z7 X: H! b& V
在进行指定某个地址时,共需要2位bank地址,13位行地址,9位列地址,合计共24位地址。但是在SDR SDRAM的指定某个地址时,行地址和列地址不是同时给出,SDR SDRAM采用行列地址线复用,所以地址线合计为2(bank 地址)+13(行、列地址复用)。
* x- m( V/ G3 w5 I6 M
SDR SDRAM需要时钟端和时钟使能端。SDR SDRAM所有的操作都依靠于此时钟;当时钟使能端无效时,SDR SDRAM自动忽略时钟上升沿。
: v) @- q% i! o0 h
SDR SDRAM拥有四个命令控制线,分别为CS、RAS、CAS、WE。组成的命令表如下:
6 R# @" ^ f' }1 S& z$ A
' p! e7 o; ~3 Y) v; B( n
& v7 S/ i i6 b& ~! m
& e& ] G; z& f# @6 Q
6 o5 }. Q$ u/ o
在写入数据时,有时会出现不想对某8bit进行写入,就可以采用DQM进行控制。
, a% S" i6 C+ U" L
4 S+ C! @$ x5 }SDR SDRAM的内部机构为:
) Z' i, |0 e7 _! M. N% S
& N+ i& z1 J# R; [8 m) B
3 F0 v2 N- d: L8 Y8 V
2 ]. A. G6 k2 y4 x/ g7 R
3 p- X: ]* E1 z/ [
由于SDR SDRAM为DRAM,内部的存储都是靠电容进行保存数据,电容的保持数据的时间为64ms,SDR SDRAM每次只能够刷新一行,为了不丢失任何数据,所以要保证64ms内,将所有的行都要刷新一遍。
0 M/ L9 G1 v' O; T$ @9 G8 }2 Z$ D G3 D; J
SDR SDRAM支持读写的长度为1、2、4、8和一行(整页)。
: j8 S' D m. v; b5 h. x7 `+ C4 J! }
具体的SDR SDRAM的介绍可以查看手册。下面只介绍几个相对重要的时序图。
) A6 n* E) P% S/ g
- ?3 s/ z- N/ D6 r9 P9 ~8 X9 m
在SDR SDRAM正常使用之前,需要进行初始化。初始化的时序图如下:
1 T4 P. T6 v1 g, Z# E
9 m5 ?2 o/ e/ n
" T5 ?0 \% M( |1 V: E5 H8 T- ^+ t3 u% W# r: I. N
( @% N) e0 [- T/ B- K" N$ b" T7 v
在PRECHARGE时,A10为高,表示选中所有的bank;A10为低,表示选中BA0、BA1所指定的bank。初始化中,A10置高。
: D; I7 d3 G/ Q* M' a% b2 ~" }( x
* j' t9 b; j N4 r3 K0 P
在LOAD MOOE REGISTER中,采用地址线进行配置模式寄存器。说明如下:
- {. G' l" U4 B) A n( n
# m0 }) X! n! Y4 i" |
4 `0 @* S* B% Z
# p* m: Q# ^- p$ O* _
/ O, w! a! {8 s. A
& l- T/ V Q( f0 X$ u
在模式配置中,利用CL(CAS Latency)表示列选通潜伏期,利用BL(Burst Length)表示突发长度。
3 m% h4 F8 x1 b0 t
SDR SDRAM中有内部的刷新控制器和刷新的行计数器,外部控制器只需要保证在64ms之内进行8192次刷新即可。
0 c% L5 I2 ~* L" N) X$ L. `" w
O; ~+ P: ]5 c在进行PRECHARGE时,A10要为高电平。
2 r d0 C4 E) X! d
" {! H0 d# ^7 d! n' f$ T5 \" O
' E* n. g" \. U1 [+ {
) }2 U, n( P8 b1 q' l3 I
3 J' t0 D. \' b X+ S: b7 b
SDR SDRAM中,我们可以在任意位置进行写入。写入的时序图如下:
& ^+ N+ \( O- h
) [1 {* n* t: ~2 K
6 n1 w v8 u' f
* E5 [) k6 c( \* Z
/ |1 Z/ D1 |( `, L) m& b
SDR SDRAM中,我们可以在任意位置进行读出。读出的时序图如下:
5 z, j" M" b. `% ^/ j: \
: `$ Y- \0 P7 _& Y# T! [- z
) M9 o8 h! Q8 K5 J
]2 h8 R/ ?* s" f! B
7 ~8 f: I/ V" e4 b8 @! e
在各个时序中的时序参数如下:
9 A( W: A/ T; g
7 s$ \ ^: L5 Q' x3 U: a$ W
* q Z6 C' J* d6 ^" N A( _
B! X Z1 i8 v4 k }% c! S, G
5 S9 ~" S. L+ \
L! G. n/ K! S9 w; ?/ A, _& j
0 ^2 ]7 G* P3 o4 t% R1 \$ c
设计一个突发长度为2,列选通潜伏期为2的SDR SDRAM的控制器。
8 X# M& D0 i6 E& m
- 设计分析
+ y5 K, e2 {- L; H# ~
该控制器共有四部分功能,初始化、刷新、写和读。四部分的执行控制采用一个模块来控制。
! `& D* \# _. |$ ]4 r' |$ ^' t" j
SDR SDRAM必须要进行初始化,初始化只用执行一次。然后启动一个计时器,等计时器达到后,进行刷新。在刷新的间隔中,根据读写的要求进行读写。
( k1 ~- g1 ]* Z
四个模块都会对SDR SDRAM的命令线和地址线进行控制,所以输出时,采用多路选择器对齐进行选择输出。
/ W# x8 c7 ?' x8 m( G, t
四个模块按照对应的时序图进行编写代码即可。
/ I1 Q) q4 A% q7 \0 i& R7 p
- 架构设计和信号说明
* ?# a" {1 l5 l0 _9 I
# `/ j: a( Z0 X: y该控制器命名为sdr_drive。
9 _% e$ |9 W$ s2 K, h- c
9 M* o5 g8 s5 a
6 c. N7 g, k) t
0 }; L v# W9 I
2 i3 ~3 R' T+ K& `1 E) e5 }
pll_sdr(锁相环模块):产生驱动所需要的100MHz的时钟(0度相位)、SDR SDRAM所需要的100MHz的时钟(270度相位)、以及PLL锁定信号当作系统复位使用。
' r7 k1 ^3 r% i) T4 l, `
timer(刷新计时器):当启动计时器后,开始计时,当计时到规定时间后,输出刷新请求,计数器直接清零计数计数。当控制器响应后,输出清除信号后,刷新请求拉低。
' t9 {5 q8 ^1 D, y0 D7 d
refresh(刷新模块)、init(初始化模块)、sdr_write(写模块)、sdr_read(读模块):当启动模块后,按照规定的时序进行输出即可,然后输出完成信号。
( H' x5 F2 c* g
sdr_ctrl(控制模块):控制各个模块协调工作。
* z6 C: Y6 T! E; z
mux4_1(四选一多路选择器模块):选择对应的bus总线作为输出。
* i& m$ x# S# Z, c! O8 h
( C3 A/ R# |+ G4 j$ V4 z" k' v
*_bus的组成为:高四位为sdr_cs_n、sdr_ras_n、sdr_cas_n、sdr_we_n。然后是bank的两位,后续为13位的sdr_addr。
$ e. \+ o: I! n9 N1 Z/ U
' y( U( i8 Y" i+ c$ S* o
! Z4 A" k$ B; x" E7 H6 P$ \$ P/ N, c
# w1 ^3 `/ | Q" R2 O
: e9 \; w2 m* Q
* y: A. E6 j6 v3 x* U- P( ^
. J9 O: l6 H& y( X7 F
- sdr_drive_head声明
1 B/ o' @/ v& d( T8 `+ C/ t: r& U
% X* s8 v0 D. P& |) h
将驱动中用到各种参数定义在该文件中。
& y, _, u3 N. _- I$ L
R$ u( _1 ?9 ^# Z; r1 G
3 l7 M% k: G7 C" F, U" L: e7 E' a! g
3 h+ A; S4 ^$ P& w! I
- pll_sdr设计实现
* T. K9 J, y& o' m9 I0 a
该模块为IP core,输出0相位的100MHz(系统时钟)和270相位的100MHz(SDR的时钟)。系统设计中,信号在上升沿输出;对于外部器件(相位调整为270),能够较好的满足建立和保持时间。
6 J/ n% a# @: T6 A" \% z
- init设计实现
) a( h' U8 t+ p! \, Y
该模块负责将SDR SDRAM进行初始化。上电延迟(PU_DELAY)设置为200us;预充电时间(Trp)设置为3个时钟周期(30ns);自刷新时间(TRFc)设置为7个时钟周期(70ns);模式寄存器应用时间(Tmrd)设置为3个时钟周期(30ns);突发长度为2;列选通潜伏期为3。
- a% l5 }4 e" U5 b) ]5 T
按照对应的初始化的时序图,做出如下设计。
6 e. J4 Z: Q# f) I3 M; Y
7 P5 F) z- f8 w$ h$ V本模块采用状态机的方式设计实现。
" q$ O" W, V8 e& B
7 H* B! E" C4 N3 y9 ^
: ~- t% p7 o4 k% `
1 z2 [4 A- O' W
& i2 i4 m" L! j5 V6 c4 c
设计代码为:
1 X& ]" X8 X$ h+ F. Y5 W7 {
% P% b/ E/ O5 n7 [( }
j( v. s% [3 J8 r+ `: K" H2 C: K; G
! o3 K9 y: }0 e( z/ W
9 a% V6 i1 B# U w6 \ G- S
- timer设计实现
. |1 ~1 ^# G6 f& }6 ^
8 V" R7 D" c- V) P
SDR SDRAM内部构造为DRAM,需要不间断的刷新,要求64ms刷新一遍。每次刷新为一行,开发板上的SDR SDRAM共有8192行,平均需要7812.5ns刷新一次,我们选择7810刷新一次。
# [3 R4 Y9 m( o0 v: y% \
# ~: I5 _7 _4 z- F( X) |) I. F" i- W
到达规定的刷新时间时,控制器有可能正在进行其他的操作。在设计时,达到时间后,发出刷新请求,当外部执行刷新后,将次请求清除。发出刷新请求的同时,计数器重新归零计数。
; r! e E# H: j( O
, [. S5 l" [: K* D0 B; v9 b+ T/ X
7 @6 n0 M' Z% J3 |
$ Q6 a5 U( l/ y7 w3 D
4 Q3 D1 N* b; W( Z- Z0 J
1 g& b0 l4 {! w0 Q J
- refresh设计实现1 s% C, X' w4 c) F7 D
该模块负责刷新,按照对应的时序图进行控制即可。
$ i: k0 |1 H+ i: J2 U" L
|! v! l2 Y( K9 t3 z
该模块利用状态机的方式实现。状态转移图如下:
; [# p( o/ T. I( C1 y. b9 z
+ }1 V2 c! s8 U7 \7 b
" \" k" T" w- N" D6 F
! n, C4 Y6 ?6 }* t; l+ t
4 M% v7 @3 w! d# m* Z
C! f" _; K: P9 z V) Y4 e c
设计代码为:
2 x e' a) `) T# \0 l% X' I
& p, }1 X/ q \; I
5 N- A- @# s# h
8 k/ h9 z8 U" E% `+ m) L
, v% N& K" k0 l3 j0 N
& B$ M- i2 P/ Z u
- sdr_write设计实现1 O' r6 D7 Y, V$ R B* U
该模块负责将外部的数据写入到规定的地址中去。在SDR SDRAM中,每操作(读写)一次,都会引起该存储位的漏电,每次结束时,可以进行预充电。SDR SDRAM提供了自动预充电的机制,在读写命令时,sdr_addr[10]=1,即可自动预充电。在设计时,应该要为自动预充电预留出足够的时间。
- O, T) ]# m& i- P/ u# r
; D; d$ ^/ x: ]* P x0 r根据对应的写入时序图,利用状态机完成此设计。
# U$ d! D5 V0 h+ X6 o; J$ l: h
* t9 {( S4 n$ R/ m
" h; D6 R* T( \# y. |
% w$ T1 N6 m( {# E5 O8 A
+ O- y# n" Z" y9 N: P. z* U8 `$ S
4 w( z) ]; E+ n/ W
设计代码如下:
! X& Q" E# m4 B, {/ p$ Q
+ w! \; |- n0 N* m. v& ]
- z' H, s9 ?7 I/ h; F: i
: B0 a" s3 X5 M; v: {, H
/ m$ ^% c9 F* G, x* |& V
- sdr_read设计实现1 Y7 f6 O, s6 G1 b8 }( H0 r
该模块负责从指定的地址中,将数据读出。
) i# j% \+ \3 O- L! r/ q
6 t+ p3 Y9 H3 B+ v按照对应的读时序图即可实现功能,本模块采用状态机方式实现,状态转移图如下:
7 v2 c: `6 a9 ]- \- f$ o
! a' A) }* ?7 f. n3 {
+ K# d z9 }$ h/ m! f! ^! `) }2 q
- p, s' [. s, I% ~
: k5 Y2 z* N! C* ^% X* ^$ f
- [/ c9 }+ a4 e. T9 j/ o j" a+ Q9 l
设计代码为:
/ V# k% ^" q7 c8 G2 X( {$ \0 i
7 n* R/ j2 Q) P- l, n
/ g4 G( }, p4 P4 N! x I, |
( q7 m8 w) L8 d% W" p
4 l: q$ E! ~- y/ j$ g9 }$ [1 [% o
" _# E- t/ C9 o& B
- mux4_1设计实现
( q; K; u" A7 c( x9 q& A; D9 a
该模块负责选择出对应的bus,然后将对应位作为输出即可。
# V8 D& F) D" D, m: P/ F
$ F: L( T; i- p# N- Y) d
设计代码为:
. v. G1 _- x4 L" v7 B
L ^, _1 R: h7 Q& ^4 q
7 J0 w) x0 Y0 ~9 g9 X
$ M' P0 }5 D& U3 K0 s! `
( L# ]8 U7 y3 W, z) H# v- B8 D
- sdr_ctrl设计实现% R5 w3 P7 j- ]) T
9 q9 T- B" X7 h; q" U该模块负责调度整个控制器,利用状态机实现。
+ [' o7 g, j# z) u- s
4 ^! _6 _' A- b$ j, [5 f9 b! V; o
$ n$ A: o7 r4 A! x7 x" \2 q9 G' V- b" d j" B
3 |7 K6 ?% r# ~ h0 l3 p# f1 C5 [
设计代码为:
! ~3 H3 A& B6 p. r; r/ ^; T8 v1 [
8 a6 G5 A( M; u5 C8 U
, f; o. B, Y% h! V) X. ^9 Z, u' ?! P) e2 d5 p5 C4 x& S2 H4 c
" ~, ] I/ h3 H" i) X1 n* ]
为了防止在进行刷新的起始部分丢失读写命令,所以在设计时,加入了缓存结构,只要有读写命令时,都会进行保存。在读写执行时,才会清除此命令。
" W' p0 f+ `; [3 P p
- RTL仿真
+ i' N4 t; L5 z% J t B
$ j- u4 |8 t8 v, q* ?- U: [5 C5 {为了能够仿真此设计,需要用到SDR SDRAM的仿真模型。仿真模型在msim的sdr_sim_module中,将其修改为行线为13bit,列为9bit,每个bank有4194304个存储空间。
; I- Q( m3 g3 X
; m+ F: e% G/ g$ ~
7 S7 ^: J3 Q' d
* t) V6 ~& Q! @6 u
! |: ^0 a# u* S2 U
在仿真时,在第二个bank,第五行,第10列,写入一个随机值。然后读取出来。
( |( D: ^: `7 q; h. [2 _
2 u$ u: t$ t% @* D: t ]仿真代码为:
. n) i8 x/ D% [ Z8 i- o! P2 @
' h% k. V, [3 |! I( T
3 d- _: C; x6 b- W
9 U9 y5 L1 v9 G5 ^, R5 |+ ]& J: `# E
9 \1 I m7 ~7 h6 C! q! R6 h/ q
这设置激励时,将tb文件和仿真模型文件同时加入添加文件中。
- @+ }% [, x8 I' \& z- E
8 ~! \, b. m7 P, B: m9 ~
* O8 S9 @, e( x# u5 P8 A) Y
/ L" i: @3 D+ |2 z: O, K- ^& v
+ _: B9 X+ c6 e
在modelsim的报告界面会显示出具体的配置信息以及读写信息。
. _! T/ z! U* |% p
$ ~1 C+ u9 P m; z
* ^3 I, j% N( A5 g9 c; B8 z R
& Z8 p) C" k' F6 H" K/ G4 q- ^
0 L4 J' }7 h1 A9 F& a7 Y2 Z5 f4 I) Z
从打印的报告中可以看出,在初始化时,列选通潜伏期为2,突发长度为2。在后续的读写时,在指定的位置,写入了13604,后续的一个位置为4629;在读出时,也正确的读出了数据。
v5 f) I( ` n) h. d# Q
5 X5 Z* `2 Y7 S# B& g报告打印出写入数据,即认为写入成功;报告打印出读出数据,只能证明控制器将数据读出,并不表示控制器能把数据接收到。
( l9 Z7 R; `2 L9 [$ V0 K
$ t( W2 F, g! Y# u
( l, K2 X3 [! N
6 C% J+ A5 k$ M8 }( M% r) D' e6 h' o2 ~+ s9 Q0 E: S- k, k
* z7 r0 g; w* @& [( Y
9 e" U" u7 {9 l6 O: w
通过控制输出的rdata以及对应的rd_valid信号,确定读出成功。在rdata中显示为16进制,16进制的1215为十进制的4629;16进制的3524的为十进制的13604。证明读数据接收正确。
) h c( u) a9 K; u
! W/ ~! N9 y4 W/ [5 j" P- 板级测试
: w* N' J! Z5 K4 H$ ?
编写控制器的上游模块(sdr_drive_test_crtl),控制写入和读出。在固定的地址中addr = {2'b01, 13'd128, 9'd20},写入一个固定的数字wdata = 32'h5a5aa5a5,然后读出,进行验证。
6 H: | g2 k: ?5 {# l
读者在进行验证时,可以采样其他的地址或者数据进行验证,且可以进行多次尝试,保证设计正确。
8 G; N! ~7 ]7 R A% w: M+ v
# _! h; Q, J4 u o3 W该模块采用状态机设计实现。
8 u9 m2 }3 `9 o0 a6 M, O5 `' `
/ v! {! l# N/ `; G
3 r3 e. b9 B) y# m$ a: ]( e+ }
0 ~) D8 _4 ]* t, l0 C
, |! a: r" c! F9 q
1 ` M/ C6 ?5 J$ q1 s, K
设计代码为:
. c4 O& K, A% M$ t/ D' _
& G/ j* z; r% X1 i, n! X) k
4 k0 F& g, c& C6 x: Q+ x! B4 U2 A' y% }" o3 u* |. K. c: p
~3 J& j. ]. w6 \" @, R" R2 X' P3 c
编写测试顶层,模块命名为sdr_drive_test,并且设置为顶层。
* e; H: [3 i1 f! }
此模块负责例化sdr_drive和sdr_drive_test_ctrl,完成连接功能,以此测试。
3 d5 H* d4 S$ H9 e. e m" @
8 h5 R2 n0 T8 L5 C8 U: v8 r
代码为:
0 F$ B! R; n% o0 N. f! _
6 X" j' h1 [8 M* V. z+ u
, H9 F8 O" Y: G: n; f" g# w6 t; O4 \! |3 l, a `7 V
+ Z; v1 B: H2 Y& m
经过综合分析后,进行分配管脚。在分配管脚后,需要将双功能管脚中的NCEO设置为普通用户IO。如果不设置,将会出现如下错误:
2 |2 w9 z) c# Y% a
& o6 y- i7 N9 z* L& @+ G; Q) \
5 c8 t& z# M4 S G _7 z4 j6 f7 } \# h
/ {# o# Z% Z1 k0 ^# K( f; C" J, i# d
右击器件名称,选择DEVICE。
4 A! L4 |& b% V! J
, D" A s6 F0 o
" Z1 Q% m, x% h' h$ \+ Y, k+ B. i1 b( q- W
0 J8 ^( q, F% u. L: M
选择device and pin option。
: F K$ S6 a4 @5 q' f
) p) S2 N7 ~" T6 W t. D& `) p
8 H R; v9 d% r4 D+ O1 `/ X1 o, K% u' e, r3 o
2 O' H8 {' w- h$ L) y4 S
% Q6 l& D9 k; }! v6 K# {
9 r- M- D ~/ }) U1 o% @$ P选择dual – purpose pins。
/ l; d1 p, f# X
2 N6 N1 o/ d. G1 n! L3 x+ {
x. h& _& @! E
% q; Z( l! }& E
8 p& n: I- d: v0 ]) ^) M% Z( x
# l+ V9 B2 e# `" t- l* q5 Y
将nceo设置为 use as regular IO。
7 X" P& x3 Z* r* `! S
! w' g3 q6 U6 v4 F. \4 C: i
4 F* ~# r! ^' b* \, a) K) V P9 p" j7 Z, S9 d4 Q6 e
# @+ W5 f. P4 f1 t. |, z
点击OK,进行编译即可。
3 [/ S) [2 T( C) R, L4 \连接上开发板,启动逻辑分析仪。
! m3 A0 C9 e7 G1 M9 h1 k- P
, C( B7 a# }7 L
将采样时钟选择为,sys_clk(PLL的c0)。采样深度选择为1K。
8 M6 {2 o% }; {" Y8 s
9 X* h6 H4 y9 P- H: m- D/ b
8 Q* s' P8 c/ f5 n0 w6 C+ b4 X1 l5 o a6 v$ F
1 c$ ] u% ?( n+ ?
添加观测信号如下,将wr_en的上升沿设置为触发条件。
{2 p) M! U3 @& D1 D }
( {9 G( V0 M- J- S& e
( G; k( J- e( r( c( E- B
M- X0 B/ y8 O' e- J- e1 I
* V r( d0 R. P2 P# e9 l$ Q" O9 G
经过保存,重新形成配置文件后,进行下板测试。# I5 R) I3 d+ u: b/ `
! `( s) J9 c; W6 x+ m7 f0 {
3 L, E! v9 y: m; i i0 [
下板后,按下复位。等待波形触发。
. O* ]$ k6 P/ a
/ P: t. R& g& U! W+ X
4 f( `6 T1 ?4 f( \0 T" p
' o; r! x5 M+ K! ~5 O5 Z& R5 v+ M" c
* z8 Z% R2 A$ M# l
通过逻辑分析仪,就可以看出可以正确的写入和读出数据。
& d2 c" Z. |. }8 Z: V( \# M" ~
0 f G# R9 Q% ~8 J( h' f; X: j3 F
1 ^! v7 G- N8 F+ G$ g1 M- U2 A4 m; i读者也可以进行尝试一次性写入多个数据,然后进行读出,进行验证设计的正确性。
& f1 n& V+ S' q g8 c: ^
3 p" ]' T! a" ]