行列地址线被选中后,数据线(data_bit)直接和电容相连接。当写入时,数据线给电容充放电;读取时,电容将数据线拉高或者置低。
# O* B: i. ~8 \8 o$ K
SDRAM 的全称即同步动态随机存储器(Synchronous Dynamic Random Access Memory);这里的同步是指其时钟频率与对应控制器的系统时钟频率相同,并且内部命令的发送与数据传输都是以该时钟为基准;动态是指存储阵列需要不断的刷新来保证数据不丢失。
+ | x+ ~+ O8 R
SDR SDRAM中的SDR是指单数据速率,即每一根数据线上,每个时钟只传输一个bit的数据。SDR SDRAM的时钟频率可以达到100MHz以上,按照100MHz的速率计算,一片16位数据宽度的SDR SDRAM的读写数据带宽可以达到1.6Gbit/s。
1 ?, i5 w0 ]- z0 m" @
SANXIN – B01的开发板上有一个容量为256Mbit(16M x 16bit)的SDR SDRAM(H57V2562GTR)。其内部存储时,分为了4个独立的区域(BANK),每个bank为4Mx16bit的存储空间;每个bank在存储时,按照二维的方式进行存储,利用行列来进行确定,有8192行(13bit地址线),有512列(9bit地址线),8192 x 512为4M的存储量。
7 a' \) [3 `: R8 P
在进行指定某个地址时,共需要2位bank地址,13位行地址,9位列地址,合计共24位地址。但是在SDR SDRAM的指定某个地址时,行地址和列地址不是同时给出,SDR SDRAM采用行列地址线复用,所以地址线合计为2(bank 地址)+13(行、列地址复用)。
8 \; C: b2 g3 I7 e5 f
SDR SDRAM需要时钟端和时钟使能端。SDR SDRAM所有的操作都依靠于此时钟;当时钟使能端无效时,SDR SDRAM自动忽略时钟上升沿。
$ l* m8 f1 P, e$ s
SDR SDRAM拥有四个命令控制线,分别为CS、RAS、CAS、WE。组成的命令表如下:
/ k4 p6 e0 [7 o4 s- S$ x; w# z
/ {0 H l/ a+ q# a/ e+ J0 C
+ _; U8 V Y. F- N
+ y: ]. s) M1 I6 m. t' H' K- M4 A5 U4 X
% `/ b- f, X4 g8 L- ?
在写入数据时,有时会出现不想对某8bit进行写入,就可以采用DQM进行控制。
: O+ ?9 U C# M4 ^ N( _; A9 x3 n6 V
7 y1 e+ c; E* O$ K4 r( f% j
SDR SDRAM的内部机构为:
, v' M7 }. I/ D- M* U1 X
# v) X$ B3 V' b$ j
9 x' _' y H- C, E, S( N
2 y& ~6 @, p8 g4 I* D- J6 Q2 K3 K
- N" o: e9 P" o& s/ ?' H
由于SDR SDRAM为DRAM,内部的存储都是靠电容进行保存数据,电容的保持数据的时间为64ms,SDR SDRAM每次只能够刷新一行,为了不丢失任何数据,所以要保证64ms内,将所有的行都要刷新一遍。
* d' _5 W, w. ^ w2 L- T- u9 N2 |
SDR SDRAM支持读写的长度为1、2、4、8和一行(整页)。
7 l8 k, Q; p4 s8 @. L
具体的SDR SDRAM的介绍可以查看手册。下面只介绍几个相对重要的时序图。
3 ^( X+ x* e2 \
+ Q6 w$ q: u) A0 e- V6 S) G9 D
在SDR SDRAM正常使用之前,需要进行初始化。初始化的时序图如下:
$ F. k4 H s# u+ W. |0 q) v
8 S6 h# M( w+ H7 ~, n, ^" O% f
$ m$ v) u8 S: w4 Q# w
" l+ A: a1 X! c
- [8 S0 H7 ~' V' y5 F
在PRECHARGE时,A10为高,表示选中所有的bank;A10为低,表示选中BA0、BA1所指定的bank。初始化中,A10置高。
6 t8 t% `0 K# J$ Z6 G- ]# V
3 _/ F7 J3 z# u
在LOAD MOOE REGISTER中,采用地址线进行配置模式寄存器。说明如下:
( H4 l1 ~; l1 H; p6 N% V4 Z
" x/ P+ w0 {1 Y
( R2 J8 A2 B) h% l: q/ a- a" y+ E7 N+ b0 l* A
. m) g2 v; h. ~0 K
8 O9 D! H! \% b( [
在模式配置中,利用CL(CAS Latency)表示列选通潜伏期,利用BL(Burst Length)表示突发长度。
* _ K1 W6 j7 D% f+ h; P2 q& j( t
SDR SDRAM中有内部的刷新控制器和刷新的行计数器,外部控制器只需要保证在64ms之内进行8192次刷新即可。
9 ]4 p0 W4 n' ^2 O' i# @
; Z2 H& }+ r& V @* y! X6 g在进行PRECHARGE时,A10要为高电平。
: @% e1 V; n8 ]
. C2 t: z5 `/ B% Y' o/ H
6 d, o$ {/ J2 ]& C- s
$ J7 x" G( [$ W
: a9 W3 X6 y! m
SDR SDRAM中,我们可以在任意位置进行写入。写入的时序图如下:
$ e$ G5 d9 O) b. l$ D; W( h
4 H6 E( u: x; W+ T
0 n6 o9 O+ H. m6 J* q
6 n5 m) e0 ^% d1 R1 P% ]7 K
& L7 K9 A% ?% y+ Z( c: [4 N
SDR SDRAM中,我们可以在任意位置进行读出。读出的时序图如下:
- m, R4 B, x3 z3 Y: {. a
8 Y# l! v; W' b3 j+ B( \3 ]
m2 b, o0 I" ]) E. [# H
/ f% Y/ B. M% p' T
6 @' a# j2 `: I* ~1 ?
在各个时序中的时序参数如下:
6 d4 j9 F% D% C( g* Z. K- g3 G
* }- K( B9 h* b' W
, ^: ]) m& P8 T. \+ X( g9 w' Y- d
( ?- u" M N& P0 o
/ L" I. j4 @9 P
: Z, S" J! \9 B
/ s! J9 I; M6 r3 H, |
- 设计要求7 t( J& @$ ]+ b3 @; F0 t: `
设计一个突发长度为2,列选通潜伏期为2的SDR SDRAM的控制器。
. T5 p: H, @+ ~% ?+ R
- 设计分析2 M& i6 k% o0 m4 N( K) q4 W0 }; y: ^7 }
该控制器共有四部分功能,初始化、刷新、写和读。四部分的执行控制采用一个模块来控制。
5 b& X# a6 S# \: l
SDR SDRAM必须要进行初始化,初始化只用执行一次。然后启动一个计时器,等计时器达到后,进行刷新。在刷新的间隔中,根据读写的要求进行读写。
2 F6 p5 ~2 V2 f& U/ `
四个模块都会对SDR SDRAM的命令线和地址线进行控制,所以输出时,采用多路选择器对齐进行选择输出。
( x4 {: c. _4 X8 o& Y0 f
四个模块按照对应的时序图进行编写代码即可。
( N: _* \' b, S# C8 K
- 架构设计和信号说明( @! p, d$ ^) K3 f I, m
6 T2 h0 o4 R, o
该控制器命名为sdr_drive。
& D# Y- D) M2 y- r
# h/ `1 T& f5 f, i* }1 B6 k& ]
, j) l: y& k/ ~. V/ Q2 z& ]' a1 E' E9 M+ I0 U; ~
' L- d, u0 G1 f$ s8 Y
pll_sdr(锁相环模块):产生驱动所需要的100MHz的时钟(0度相位)、SDR SDRAM所需要的100MHz的时钟(270度相位)、以及PLL锁定信号当作系统复位使用。
0 n2 M+ u* m$ o1 g
timer(刷新计时器):当启动计时器后,开始计时,当计时到规定时间后,输出刷新请求,计数器直接清零计数计数。当控制器响应后,输出清除信号后,刷新请求拉低。
j- i6 w$ |1 [1 T! J7 o
refresh(刷新模块)、init(初始化模块)、sdr_write(写模块)、sdr_read(读模块):当启动模块后,按照规定的时序进行输出即可,然后输出完成信号。
) C F: Z8 n5 ^/ Z2 ?: \
sdr_ctrl(控制模块):控制各个模块协调工作。
$ u$ o- {" v+ y# H- g
mux4_1(四选一多路选择器模块):选择对应的bus总线作为输出。
8 Q3 F \/ F3 V! f3 A0 {. X
, M% N- r a- V t. ^; k
*_bus的组成为:高四位为sdr_cs_n、sdr_ras_n、sdr_cas_n、sdr_we_n。然后是bank的两位,后续为13位的sdr_addr。
8 C! G: ?+ ]7 R @4 ~1 V/ S9 f- N
: `% k0 Y7 R- n H: E
; j. C* n3 o( Y* _# G" Z5 W3 B# a! l H5 ?1 K
" x( \% E* O9 I! v) x
3 w/ I- T3 p5 o Q5 r# b' |- f2 n3 ?$ u
/ V9 ]* n3 z+ u
- sdr_drive_head声明
4 A4 A X: }; C& i
7 V! v W5 }' X; E, H% k' L将驱动中用到各种参数定义在该文件中。
+ G. N. `! D! r# C% O" e) y
$ l0 D0 P) T: p5 E* r/ E, E
- Z2 }6 ^" [$ ^3 |* t; z" u" G" p6 O" q2 j) `0 @3 p
1 H+ A1 e8 X l2 C3 o
- pll_sdr设计实现
, X0 _( u" a) h' P: ?# H& X& }6 ~7 M' k0 ?
该模块为IP core,输出0相位的100MHz(系统时钟)和270相位的100MHz(SDR的时钟)。系统设计中,信号在上升沿输出;对于外部器件(相位调整为270),能够较好的满足建立和保持时间。
, O& e. h9 J3 v
- init设计实现. \5 b- ~8 ?1 l- R+ }) a& D
该模块负责将SDR SDRAM进行初始化。上电延迟(PU_DELAY)设置为200us;预充电时间(Trp)设置为3个时钟周期(30ns);自刷新时间(TRFc)设置为7个时钟周期(70ns);模式寄存器应用时间(Tmrd)设置为3个时钟周期(30ns);突发长度为2;列选通潜伏期为3。
7 L4 B" X1 \* |1 b! U+ D
按照对应的初始化的时序图,做出如下设计。
+ ~! V5 z4 r7 n: v
7 u6 H& N! y8 U2 w! D( Q
本模块采用状态机的方式设计实现。
" U2 J* B5 a# d7 A0 n
' ?7 B4 O# Y" g. s5 w
( G& F& ?- R, t0 _+ [& J% M+ g( @: ^3 U) B9 {: }; q
6 I% V3 k# u- [! R5 e( J
设计代码为:
" m" D" f" e8 l2 `3 r1 t
- g6 g- M- S5 }. \
( ]8 @9 p5 u' g, Z; ~5 s% [
% C2 [9 y4 u$ \, j8 P1 z- ^
! o, v4 w/ J/ D- L, v/ m( W
- timer设计实现& v+ Y' ^" {$ H2 `+ ?: R2 N
# N1 U1 K( d7 V# D- F4 H2 x
SDR SDRAM内部构造为DRAM,需要不间断的刷新,要求64ms刷新一遍。每次刷新为一行,开发板上的SDR SDRAM共有8192行,平均需要7812.5ns刷新一次,我们选择7810刷新一次。
) e/ o, V5 M9 }3 ^/ R; J+ V
t+ h. n; r# O% ]* P2 y2 |到达规定的刷新时间时,控制器有可能正在进行其他的操作。在设计时,达到时间后,发出刷新请求,当外部执行刷新后,将次请求清除。发出刷新请求的同时,计数器重新归零计数。
4 l! ~& F X3 ?. V, m6 }$ \& x
4 ^6 W; g/ l$ I3 M) u
# b3 Y& X9 o6 @+ f3 w. e K5 }8 z
- n# }" U- M- l+ [
* w( Z) h" R9 U m* b- a% v( ^7 [0 C
/ l* ]. M' C5 Y0 Q; K0 L$ G
- refresh设计实现
1 |2 D2 y/ ^6 g7 k( W" s f' p+ G
该模块负责刷新,按照对应的时序图进行控制即可。
3 N" x0 ~. @: f' k, N4 p5 c& m# d
& V |& }( J# Y0 u( ~该模块利用状态机的方式实现。状态转移图如下:
! D4 ?* i: b1 i! i
1 y5 D% s$ _* s, S3 @' P/ a
1 l6 n S' L! P
- a7 Z8 z0 W+ H7 X4 B- g
" \, P" i7 w' U" a
! Z' l: w" Y' D6 k& a p5 I
设计代码为:
( X- C, v, w8 C" e# r9 g
0 Q" g) O$ {' w6 F* n8 g
6 S- ? @9 K2 N0 n* J' t( G4 a
, A- h1 n) m! K3 l
- V4 @0 ~3 \) [1 G* X, ]( o7 ^2 I
! v9 p5 Q0 _# `' f2 K+ c
- sdr_write设计实现5 c& O" Z7 i' K# J- A M
该模块负责将外部的数据写入到规定的地址中去。在SDR SDRAM中,每操作(读写)一次,都会引起该存储位的漏电,每次结束时,可以进行预充电。SDR SDRAM提供了自动预充电的机制,在读写命令时,sdr_addr[10]=1,即可自动预充电。在设计时,应该要为自动预充电预留出足够的时间。
. u/ W+ O& ~* R$ s! ]5 V- m) k
$ @, b+ u* B& O+ c# g# F
根据对应的写入时序图,利用状态机完成此设计。
! d: [( e8 L. v; D3 Q
- [, W! x& r7 V, v
$ R8 v: O+ B1 e. `
- i) |% J: q" g/ p, ]( ]
: n6 C, S% z: r
* t4 H8 z8 ~3 A4 O% Y
设计代码如下:
0 ~9 T$ Q8 T4 S, l
/ D9 d9 e+ d1 n9 Z
5 }" ]3 l$ h4 E0 {. K( _
" W k+ a& [ E- g% y5 d
# Q8 Y; O9 N. O" @
- sdr_read设计实现8 o9 l. t- ~/ A X; D7 T
该模块负责从指定的地址中,将数据读出。
# s y1 U- x8 g+ `
/ _* b% r5 r8 o. p/ Q按照对应的读时序图即可实现功能,本模块采用状态机方式实现,状态转移图如下:
! S- z. E) y& R
8 O3 K: ?2 p( {9 W; i- H
+ Y- f6 f/ Z, r1 Y) }; u7 j
3 e! K% Z4 Y! S
' F$ H2 V3 R: U# j4 J" d* W
) j: P N! R G# U7 f* Y9 ^
设计代码为:
2 n* @6 c: T# v8 j: A, `
4 ]8 h& c7 Z) o: l/ j
) r0 }& B* a, \# F' k6 k
| f& \" ?+ u. B1 F, j0 Q
/ _# C3 R d ^9 {2 ^4 A
6 ]8 P7 j9 u) R9 Z g
- mux4_1设计实现
+ X$ ?) F/ ^0 \+ x
该模块负责选择出对应的bus,然后将对应位作为输出即可。
+ p: f% s& v2 s5 a, P( d
3 R4 l6 m) L+ a4 G: }% u8 y
设计代码为:
4 k4 v- \$ \( V( {( D7 {1 o7 ^
{4 q* ?* p8 x
+ |: ~! d6 z: F5 \: Z) o! ?
' \$ @9 b6 ~) Z. _3 i8 V8 L
4 U4 X+ Q7 q2 e9 `) W
- sdr_ctrl设计实现+ _" S/ G3 \: L& |
$ ^/ x. @" ]' y8 O* L
该模块负责调度整个控制器,利用状态机实现。
0 f+ X9 J# D8 r) g9 @9 F, L
/ z9 h. b+ T- l0 ^& ^: X' R! B+ |
9 [+ u: K; ^. P) g# B
) G1 U4 O! C# w# R- @2 f
, u% s5 g H5 u) T: ^8 ^+ n
设计代码为:
# B( q* |: h" A) `6 H& B
: O, ?# m4 a h* f; `
, o8 v$ P; O) H3 d
' ~& l! i+ W L8 V1 D3 @1 x
: V( Z! Y/ F, R' l+ R2 K0 z* X# P
为了防止在进行刷新的起始部分丢失读写命令,所以在设计时,加入了缓存结构,只要有读写命令时,都会进行保存。在读写执行时,才会清除此命令。
9 Y( @) b8 f9 U
- RTL仿真
8 B( [0 B0 S' n7 \: W2 \' e# p) P
5 @: O$ b' u. X6 S为了能够仿真此设计,需要用到SDR SDRAM的仿真模型。仿真模型在msim的sdr_sim_module中,将其修改为行线为13bit,列为9bit,每个bank有4194304个存储空间。
5 b( I& B8 o, R: ^
) t; D% |: U% s' U5 |% j. y" e! Q2 _
5 u5 {5 G, x3 n2 ^& K( n
6 L3 j& }0 I0 m6 h# y3 [
- C2 r( r- A. z: m' J( a
在仿真时,在第二个bank,第五行,第10列,写入一个随机值。然后读取出来。
( ?6 a" w& y0 B0 V
& c1 R! `" w' s; c* Y+ a9 t仿真代码为:
3 e5 a* a) j9 L- S# J2 B# ?
: D1 E' m( U6 |9 Q; M; i/ Z5 S( p
2 F( t7 N! O6 [$ }0 g
+ _' [' S+ L' [% u }, W' I
. p$ D+ T9 J0 S0 z
这设置激励时,将tb文件和仿真模型文件同时加入添加文件中。
4 ?& i! n3 H- C
1 Y1 D4 b4 D. U8 G; J7 ~+ _& p* F
4 }+ M% W t5 M% `- h5 i, Q2 ^1 o* n: a- Y" n: z" e& Y& O
h4 Q1 O% l* }* s1 ~" e1 _ r0 c; D& a
在modelsim的报告界面会显示出具体的配置信息以及读写信息。
+ S5 y7 `' Y% D) ~& j0 `% v+ o! R
) \9 r, A* J* b& O
3 x$ J% o, O! G6 U4 i& r+ R! p4 m/ r
9 B0 s: @# M8 l
从打印的报告中可以看出,在初始化时,列选通潜伏期为2,突发长度为2。在后续的读写时,在指定的位置,写入了13604,后续的一个位置为4629;在读出时,也正确的读出了数据。
. ^' P7 `$ J4 B
" s/ T5 O8 z1 L$ a. G. }1 w Z
报告打印出写入数据,即认为写入成功;报告打印出读出数据,只能证明控制器将数据读出,并不表示控制器能把数据接收到。
1 i3 V' q1 ~- s
: |( u! ^2 n+ @
9 j: X# M% C% E4 z' e) _) P z/ X
3 G$ W8 r4 U: h* _! \1 \7 _9 w, ]. x
. x% u! d/ L0 j- h3 g9 K6 f: j! a
% c7 w, c6 X* e
5 V$ P; X/ ^$ \7 D
通过控制输出的rdata以及对应的rd_valid信号,确定读出成功。在rdata中显示为16进制,16进制的1215为十进制的4629;16进制的3524的为十进制的13604。证明读数据接收正确。
2 Z# e s- l/ N3 ]* F0 G. ?
; c3 S6 W1 r6 n7 X1 t& P
- 板级测试
4 B7 l4 W8 i5 {& D: i1 M0 L8 Q
编写控制器的上游模块(sdr_drive_test_crtl),控制写入和读出。在固定的地址中addr = {2'b01, 13'd128, 9'd20},写入一个固定的数字wdata = 32'h5a5aa5a5,然后读出,进行验证。
4 t1 L, Q5 k+ N9 _# ~5 a' M
读者在进行验证时,可以采样其他的地址或者数据进行验证,且可以进行多次尝试,保证设计正确。
0 B* n$ H- U+ j/ L$ e
0 T3 L: `6 N# C6 M* D" z该模块采用状态机设计实现。
$ [) d7 y: v! X2 E$ d% l
9 X9 r5 D1 v# L1 D# n
+ i1 f8 }# U; V- Y9 g6 y- ?& Y. ?2 f& g* x, E
2 C2 l/ _' N& h& D" C- `5 [
( p. Z5 D) b$ U% d# D* e
设计代码为:
! W: M* N4 X: ?. S
: q1 `) y$ h# S0 h4 U
. f6 I1 ^/ }3 U, b0 k+ i
% F4 U' P/ J( }; z |, l* E
3 ^6 V5 t# Y8 D, M
编写测试顶层,模块命名为sdr_drive_test,并且设置为顶层。
4 y8 w: _4 o% o3 s
此模块负责例化sdr_drive和sdr_drive_test_ctrl,完成连接功能,以此测试。
- |. w8 w' @+ Q. Q4 A9 B. `
4 S- F3 p# B* Q8 P' @代码为:
: U6 {' h. D2 S6 Y4 g1 M3 V
. S" y' c, j9 G
$ Y( a& Z b' Y4 M
9 U: D( p. g* [' t
8 M7 C2 O" [2 Q( z. |+ f
经过综合分析后,进行分配管脚。在分配管脚后,需要将双功能管脚中的NCEO设置为普通用户IO。如果不设置,将会出现如下错误:
# L k( e+ S1 z" w9 B8 w4 G
: W# @4 f3 g: A; G; \1 G
9 V3 }# ]. E* P# U8 ?* E' E# m. Z
5 s+ r4 \" R2 I/ c5 p$ n2 B: v3 H- j
. R" |( z( T+ J! p8 ?! N6 o
右击器件名称,选择DEVICE。
" A. v. t' A% d8 u
4 G1 ]$ J6 K" L" {* Z% J
- d+ i1 z* l; G- B
: P; X C0 p2 ~, n' @ f
4 e4 y2 Y! w& t& S5 `3 G7 w
选择device and pin option。
4 D0 W0 f' Q5 l* i) R' L* ]6 s: c; `
2 v+ {; [( A' D: R; e. ], V
5 Y/ G3 S& D' ~
9 y4 o$ Q( i; B; X0 @ m
' X. t( R* a$ D5 [2 Z
" w p7 ]+ n. h& Q- o
5 y1 ]: Q4 ` q1 @. G0 g- ?) b) X选择dual – purpose pins。
$ a, s ]$ s* P3 P" i9 F. u5 U" W/ `
3 J; T0 }. G' ^1 _9 }
( P/ S6 \ e1 B7 z; | E/ x1 L
" d5 U8 g' r3 W; c0 W6 P! p* f7 v) Y: L9 R8 O% l0 N
8 s/ M' A% v' h: Y% L& N
将nceo设置为 use as regular IO。
, h, n7 A5 d: v- x& G
9 E4 A4 `! ~! l7 }; t' u( P0 g1 u& n
+ h6 f. f! m; F( ]# L& |1 M0 ?4 ?( W' M
' R9 z( l' R& Y _
, V. @& C7 A+ l# z0 ~
点击OK,进行编译即可。
5 C U4 y7 O9 [; S连接上开发板,启动逻辑分析仪。
' k: H1 n' D8 O+ ^6 I! s" I s$ R
2 U% D) j a# e5 Y5 p2 q' W: c将采样时钟选择为,sys_clk(PLL的c0)。采样深度选择为1K。
3 e8 Y9 A6 N1 z9 i( l
$ C- w5 X+ O7 N- Y) M4 V
/ H k" L, k$ H. B5 q7 g
' A( x: V& K4 g9 j% L
$ y5 `$ `" t. d7 a J& P
添加观测信号如下,将wr_en的上升沿设置为触发条件。
- [) j+ M0 o) f4 w* g8 s3 v
( g* P) A0 \+ ?. i% x* \
: O: x7 v/ s, h2 t2 W
! m4 L' p3 L; u. |( I( z
( w; }8 R( e$ B! U
经过保存,重新形成配置文件后,进行下板测试。
0 C8 D: E* Q. h2 ]: {8 z2 Z
+ P- R2 @% o2 K6 B2 A4 @
) \8 y# ]) G8 y& G/ D9 Q3 w下板后,按下复位。等待波形触发。
. @4 e9 c& {* w
0 {; M+ r( k& l- M& p
2 I* e2 J3 w# l) a, ~8 h% s
6 c9 p- a4 F( @( X
; k- L# Y) f/ {! p H5 `; t% i
通过逻辑分析仪,就可以看出可以正确的写入和读出数据。
! H0 }5 W2 Z5 X: z
, J- ]) x( s1 B- G( H9 O
E+ R& q. T; p1 [读者也可以进行尝试一次性写入多个数据,然后进行读出,进行验证设计的正确性。
, c0 C \; c* G# ~( h9 |. A+ T* M4 A# T. Y. p/ s3 x, J5 K3 g; ]