【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十一:SDRAM模块④ — 页读写 β

实验二十一:SDRAM模块④ — 页读写 β

未进入主题之前,让我们先来谈谈一些重要的体外话。《整合篇》之际,笔者曾经比拟Verilog如何模仿for循环,我们知道for循环是顺序语言的产物,如果Verilog要实现属于自己的for循环,那么它要考虑的东西除了步骤以外,还有非常关键的时钟。

for( i=0; i<4; i++ ) 操作A;
 
i = 0;
while ( i<4 ) { 操作A;i++;}
 
i = 0;
do { 操作A; i++; } while(i = 3)

代码21.1

代码2.11有三段经典的循环操作,即for循环,while循环,还有do ... while循环。如果三个循环互相交流的话,谁又是谁的朋友呢?为了解决这个问题,我们必须先建立交友标准,即先执行后判断,还是先判断后执行? 如此一来,我们可以百分百确信 for循环与while循环才是好朋友,因为两者都是先判断后执行的类型。反之do ... while循环则是先执行后判断的类型。

为了验证上述的接着,我们试着解剖一下种循环的执行过程 ...

for循环:

清零i

i为0,判断i是否小于4,如是执行操作A第0次,递增i为1;

i为1,判断i是否小于4,如是执行操作A第1次,递增i为2;

i为2,判断i是否小于4,如是执行操作A第2次,递增i为3;

i为3,判断i是否小于4,如是执行操作A第3次,递增i为4;

i为4,判断i是否小于4,不是结束循环。

while循环:

清零i;

i为0,判断i是否小于4,如是执行操作A第0次,递增i为1;

i为1,判断i是否小于4,如是执行操作A第1次,递增i为2;

i为2,判断i是否小于4,如是执行操作A第2次,递增i为3;

i为3,判断i是否小于4,如是执行操作A第3次,递增i为4;

i为4,判断i是否小于4,不是结束循环。

do ... while循环:

清零i;

i为0,执行操作A第0次,i递增为1,判断i是否小于4,如是继续;

i为1,执行操作A第1次,i递增为2,判断i是否小于4,如是继续;

i为2,执行操作A第2次,i递增为3,判断i是否小于4,如是继续;

i为3,执行操作A第3次,i递增为4,判断i是否小于4,不是则结束循环;

读者可能会很奇怪,执行与判断的次序究竟有什么好困惑?作为一只小气鬼,笔者会非常执著小细节。Verilog是并行性质的语言,执行与判断有可能同时执行,也有可能并非同时执行 ... 根据直觉,笔者相信Verilog的循环操作更加适合先执行后判断,而不是先判断后执行。

1.     0:
2.     begin
3.         if( C1 == 0 ) isEn <= 1’b1;
4.         else if( C1 == 4-2 ) isEn <= 1’b0;
5.    
6.         if( C1 == 4 -1 ) begin C1 <= 4’d0; i <= i + 1’b1; end
7.         else C1 <= C1 + 1’b1;
8.     end

代码21.1

如代码21.1所示,第6~7行表示步骤0保持4个时钟,其中 C1为0拉高isEn,C1为4-2的拉低 isEn;代码21.1告诉我们,执行与判断是同时进行,然而从解读代码的顺序来看,笔者更加倾向“先执行后判断”这种结构性

clip_image002

图21.1 代码21.1的时序图。

图21.1是代码21.1所描述的时序图,其中isEn拉高是否完全根据C1的计数。T0之前也是复位的状态,C1为0。T0之际,C1为0(过去值)拉高isEn,C1为2(过去值)拉低isEn。

1.    0: 
2.    begin isEn <= 1’b1; i <= i + 1’b1; end
3.    1: 
4.    begin isEn <= 1’b0; i <= i + 1’b1; end
5.    2:
6.    if( C1 == 4 -1 ) begin C1 <= 4’d0; i <= i + 1’b1; end
7.    else begin C1 <= C1 + 1’b1; i <= 4’d0; end

代码21.2

同样的结构性甚至可以衍生至不同的操作,如代码21.2所示。步骤0拉高isEn,步骤1拉低isEn,步骤2判断。如果步骤0~2来回重复,isEn一共拉高4次,或者说isEn产生四个高脉冲。

clip_image004

图21.2 代码21.2的理想时序图。

图21.2是代码21.2所产生的时序图。只要稍微比较图21.1与图21.2,我们会发现两者之间都有相似的“结构性”,因为两者都是倾向“先执行后判断”。

1.    0:
2.    if( Done ) begin isCall[0] <= 1’b0; i <= i + 1’b1; end
3.    else begin isCall[0] <= 1’b1; end
4.    1:
5.    if( Done ) begin isCall[1] <= 1’b0; i <= i + 1’b1; end
6.    else begin isCall[1] <= 1’b1; end
7.    2:
8.    if( C1 == 4 -1 ) begin C1 <= 4’d0; i <= i + 1’b1; end
9.    else begin C1 <= C1 + 1’b1; i <= 4’d0; end

代码21.3

再举例而言,如代码21.3所示,步骤0执行功能0,步骤1执行功能1,步骤2用来判断循环次数。步骤0~2之间会来回重复,直至功能0与1都执行四次,这种感觉好比代码21.4。

1.    int main()
2.    {
3.        for( int i = 0; i < 4; i ++ )
4.        {
5.            Function0();
6.            Function1();
7.           }
8.        return -1;
9.    }

代码21.4

不过代码21.1~21.3都有相似的结构性,即都是先执行后判断。笔者作为热爱结构的男人,笔者会尝尽一切挖掘结构的可能性。此外,这种先执行后判断的模仿对象,笔者称为伪循环(Fake Iteration)。说完题外话,接下来让我们进入本实验的主题。

页读写之所以分为α与β,那是因为过多的数据吞吐量导致读写变成非常麻烦。天真的朋友可能会认为,如果Burst Length 为4,那么数据位宽就是 4 * 16bit。再如果 Burst Length 为 512,那么数据位宽就是 512 * 16 bit。理论上,这样说是没错,不过那样做会撑爆 FPGA的嘴巴。为此,读写入口必须加入缓冲机制才行。此刻,实验十五所学过的知识(同步FIFO)就派上用场了。

clip_image006

图21.3 SDRAM基础模块的建模图。

加入FIFO储存模块的SDRAM基础模块,大致上如图21.3所示。为了简化连线,笔者稍微加大FIFO的深度,对此FIFO有没有反馈状态都没有问题。还没有进入建模之前,让笔者先来解释一下,页读写与FIFO之间,究竟有什么细节需要注意。

页写操作(请求FIFO):

clip_image008

图21.4 页写操作的理想时序图。

图21.4是也写操作的理想时序图,相较实验二十的页写时序,实验二十一的页写时序夹杂了FIFO储存模块,其中isEn[0]是SDRAM功能模块向FIFO的读请求。时序的大致过程如下:

l T1,发送ACT命令,BANK地址与行地址;

l T1半周期,SDRAM读取;

l T2,满足TRCD;

l T3,拉高isEn[0];

l T4,发送WR命令,BANK地址与列地址,FIFO发送第0数据,;

l T4半周期,SDRAM读取

l T5,FIFO发送第1~511数据,C1为510拉低isEn[0],C1为511发送BSTP命令。

图22.4基本上已经表达非常清楚,为使FIFO可以同步发送数据,因为SDRAM功能模块必须提前一个时钟拉高isEn[0],即T3拉高isEn[0]。此外,为了不使FIFO吐出过多的数据,C1为510的时候便拉低isEn[1]。C1为511的时候则发送BSTP命令。对此,Verilog则可以这样描述,结果如代码21.5所示:

1.    1: // Send Active Command with Bank and Row address
2.    begin rCMD <= _ACT; rBA <= iAddr[23:22]; rA <= iAddr[21:9]; i <= i + 1'b1; end
3.                         
4.    2: // wait TRCD 20ns
5.    if( C1 == TRCD -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
6.    else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end             
7.                    
8.    3: 
9.    begin isEn[0] <= 1'b1; i <= i + 1'b1; end
10.                         
11.    4: // Send Write command with row address,
12.    begin rCMD <= _WR; rBA <= iAddr[23:22]; rA <= { 4'b0000, iAddr[8:0] }; i <= i + 1'b1; end
13.                         
14.    5: // continue write until end and send BSTP 
15.    begin
16.        if( C1 == 512 -2 ) begin isEn[0] <= 1'b0; end
17.        if( C1 == 512 -1 ) begin rCMD <= _BSTP; C1 <= 14'd0; i <= i + 1'b1; end
18.        else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
19.    end

代码21.5

如代码21.5所示,步骤1发送Active命令,步骤2满足TRCD,步骤3提前拉高读请求,即isEn[0]。步骤4发送 Write命令,还有写入第0数据。步骤5写入第1~511数据,C1为510的时候拉低isEn[0],C1为511的时候发送 Burst Stop 命令。

页读操作(请求FIFO):

clip_image010

图21.5 页读操作的理想时序图。

图21.5还是笔者自定义的页写的时序图,相较实验二十,其中多了FIFO的写请求 isEn[1],FIFO的iData,还有C1计数。一旦 CAS Latency 得到满足,SDRAM便会开始读出数据。半个周期之后(T5)FPGA读取,并且拉高isEn[1],然后将数据转交FIFO。

T6之际,512个数据读写完毕,拉低isEn[1],然后发送BSTP命令。

时序的大致过程如下:

l T1,发送ACT命令,BANK地址与行地址;

l T1半周期,SDRAM读取;

l T2,满足TRCD;

l T3,发送RD命令,BANK地址与列地址;

l T3半周期,SDRAM读取命令。

l T4,满足 CAS Latency。

l T5,拉高isEn[1],读取第0~511数据,并且向FIFO的iData写入。

l T6,拉低isEn[1],发送BSTP命令。

l T6半周期,SDRAM读取。

对此,Verilog可以这样描述,结果如代码21.6所示:

1.    1: // Send Active command with Bank and Row address
2.    begin rCMD <= _ACT; rBA <= iAddr[23:22]; rA <= iAddr[21:9]; i <= i + 1'b1; end
3.                         
4.    2: // wait TRCD 20ns
5.    if( C1 == TRCD -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
6.    else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end 
7.                    
8.    3: // Send Read command and column address
9.    begin rCMD <= _RD; rBA <= iAddr[23:22]; rA <= { 4'b0000, iAddr[8:0]}; i <= i + 1'b1; end
10.    
11.    4: // wait CL 3 clock
12.    if( C1 == CL -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
13.    else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end 
14.                         
15.    5: // Read Data
16.    begin 
17.         D1 <= S_DQ; isEn[1] <= 1'b1; 
18.         if( C1 == 512 -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
19.         else begin C1 <= C1 + 1'b1; end
20.    end
21.                                     
22.    6:
23.    begin isEn[1] <= 1'b0; rCMD <= _BSTP; i <= i + 1'b1; end

代码21.6

如代码21.6所示,步骤4满足CL以后,步骤5便拉高isEn[1],并且读取512个数据。步骤6将isEn[1]拉低,并且发送命令 BSTP。理解完毕以后,我们可以开始建模了。

fifo_savemod.v

clip_image012

图21.6 FIFO储存模块的建模图。

图21.6是FIFO储存模块的建模图,为了简化设计,笔者不小心吃掉FIFO的输出标签,取而代之就是增加深度。虽然实验只有两只FIFO储存模块,方向也不一样,不过母体都是一样的东西。具体内容我们还是来看代码吧:

1.    module fifo_savemod
2.    (
3.         input CLOCK, RESET, 
4.         input [1:0]iEn,
5.         input [15:0]iData,
6.         output [15:0]oData,
7.         output [1:0]oTag
8.    );

以上内容为相关的出入端声明。

9.        initial begin
10.             for( C1 = 0; C1 < 1024; C1 = C1 + 1'b1 )
11.                  begin  RAM[ C1 ] <= 16'd0; end    
12.         end
13.        
14.        reg [15:0] RAM [1023:0]; 

以上内容为声明位宽为16,深度为1024的RAM,,然后将其初始化。

15.        reg [10:0] C1 = 11'd0,C2 = 11'd0; // N+1
16.                  
17.       always @ ( posedge CLOCK or negedge RESET )
18.            if( !RESET )
19.                 begin
20.                      C1 <= 11'd0;
21.                  end
22.           else if( iEn[1] ) 
23.                 begin   
24.                    RAM[ C1[9:0] ] <= iData; 
25.                     C1 <= C1 + 1'b1; 
26.                  end
27.                  
28.        always @ ( posedge CLOCK or negedge RESET )
29.            if( !RESET )
30.                 begin
31.                        C2 <= 11'd0;
32.                  end
33.             else if( iEn[0] )
34.                    begin 
35.                        //D1 <= RAM[ C2[9:0] ]; 
36.                         C2 <= C2 + 1'b1; 
37.                    end
38.        

以上内容为核心内容。第15行声明写指针C1,还有读指针C2,位宽为 RAM的深度+1。第17~26行是FIFO的写操作,第28~37行是FIFO的读操作,笔者将第35行注释掉。

39.          assign oData = RAM[ C2[9:0]];                
40.          assign oTag[1] = ( C1[10]^C2[10] & C1[9:0] == C2[9:0] ); // Full Left
41.           assign oTag[0] = ( C1 == C2 ); // Empty Right
42.    
43.    endmodule

取而代之,笔者将RAM直接驱动 oData。好奇的朋友一定觉得疑惑?其实笔者是为了偷时钟,如果用D1驱动oData,FIFO的读数据(未来值)就会慢了半拍。所以,FIFO与SDRAM功能模块之间会同步失败。反之,RAM直接驱动输出,好比组合逻辑直接驱动输出,读取数据都是即时值。第40~41行是写满状态还有读空状态的输出驱动声明。

sdram_funcmod.v
1.    module sdram_funcmod
2.    (
3.         input CLOCK,
4.         input RESET,
5.         
6.         output S_CKE, S_NCS, S_NRAS, S_NCAS, S_NWE,
7.         output [1:0]S_BA,  
8.         output [12:0]S_A,  
9.         output [1:0]S_DQM,
10.         inout [15:0]S_DQ,
11.         
12.         output [1:0]oEn, //[1]Write [0]Read
13.         input [23:0]iAddr,  // [23:22]BA,[21:9]Row,[8:0]Column
14.         input [15:0]iData,
15.         output [15:0]oData,
16.         
17.         input [3:0]iCall,
18.         output oDone
19.    );

以上内容为相关的出入端声明。

20.        parameter T100US = 14'd13300;
21.        // tRP 20ns, tRRC 63ns, tRCD 20ns, tMRD 2CLK, tWR/tDPL 2CLK, CAS Latency 3CLK
22.        parameter TRP = 14'd3, TRRC = 14'd9, TMRD = 14'd2, TRCD = 14'd3, TWR = 14'd2, CL = 14'd3;
23.        parameter _INIT = 5'b01111, _NOP = 5'b10111, _ACT = 5'b10011, _RD = 5'b10101, _WR = 5'b10100,
24.                _BSTP = 5'b10110, _PR = 5'b10010, _AR = 5'b10001, _LMR = 5'b10000;
25.                

以上内容为相关的常量声明。

26.        reg [4:0]i;
27.        reg [13:0]C1;
28.        reg [15:0]D1;
29.        reg [4:0]rCMD;
30.        reg [1:0]rBA;
31.        reg [12:0]rA;
32.        reg [1:0]rDQM;
33.        reg [1:0]isEn;
34.        reg isOut;
35.        reg isDone;
36.    
37.        always @ ( posedge CLOCK or negedge RESET )
38.            if( !RESET )
39.                begin
40.                    i <= 4'd0;
41.                    C1 <= 14'd0;
42.                    D1 <= 16'd0;
43.                    rCMD <= _NOP;
44.                    rBA <= 2'b11;
45.                    rA <= 13'h1fff;
46.                    rDQM <= 2'b00;
47.                    isEn <= 2'b00;
48.                    isOut <= 1'b1;
49.                    isDone <= 1'b0;
50.                end

以上内容为相关的寄存器声明与复位操作。

51.              else if( iCall[3] )
52.                case( i )
53.                    
54.                    0: // Set IO to output Tag
55.                    begin isOut <= 1'b1; i <= i + 1'b1; end
56.                       
57.                    1: // Send Active Command with Bank and Row address
58.                    begin rCMD <= _ACT; rBA <= iAddr[23:22]; rA <= iAddr[21:9]; i <= i + 1'b1; end
59.                         
60.                    2: // wait TRCD 20ns
61.                    if( C1 == TRCD -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
62.                    else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end             
63.                    
64.                    /*********************************************/
65.                    
66.                   3: 
67.                   begin isEn[0] <= 1'b1; i <= i + 1'b1; end
68.                         
69.                    4: // Send Write command with row address
70.                    begin rCMD <= _WR; rBA <= iAddr[23:22]; rA <= { 4'b0000, iAddr[8:0] }; i <= i + 1'b1; end
71.                         
72.                     5: // continue write until end and send BSTP 
73.                    begin
74.                        if( C1 == 512 -2 ) begin isEn[0] <= 1'b0; end
75.                        if( C1 == 512 -1 ) begin rCMD <= _BSTP; C1 <= 14'd0; i <= i + 1'b1; end
76.                        else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
77.                    end
78.    
79.                    /**********************************************/
80.                         
81.                    6: // Generate done signal
82.                    begin rCMD <= _NOP; isDone <= 1'b1; i <= i + 1'b1; end
83.                        
84.                   7:
85.                   begin isDone <= 1'b0; i <= 4'd0; end
86.                    
87.                endcase

以上内容为部分核心操作。以上内容是写操作,步骤3提前请求FIFO,步骤4写入第0数据,步骤5写入第1~511数据,然后发送BSTP命令。步骤6发送 NOP命令之余,也用来产生完成信号。

88.            else if( iCall[2] )
89.                case( i )
90.                    
91.                    0:
92.                    begin isOut <= 1'b0; D <= 16'd0; i <= i + 1'b1; end
93.    
94.                    1: // Send Active command with Bank and Row address
95.                    begin rCMD <= _ACT; rBA <= iAddr[23:22]; rA <= iAddr[21:9]; i <= i + 1'b1; end
96.                         
97.                    2: // wait TRCD 20ns
98.                    if( C1 == TRCD -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
99.                    else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end 
100.                
101.                    /********************/
102.                    
103.                    3: // Send Read command and column address
104.                    begin rCMD <= _RD; rBA <= iAddr[23:22]; rA <= { 4'b0000, iAddr[8:0]}; i <= i + 1'b1; end
105.    
106.                    4: // wait CL 3 clock
107.                    if( C1 == CL -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
108.                    else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end 
109.                                       
110.                    /********************/ 
111.                         
112.                    5: // Read Data
113.                   begin 
114.                      D1 <= S_DQ; isEn[1] <= 1'b1; 
115.                      if( C1 == 512 -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
116.                      else begin C1 <= C1 + 1'b1; end
117.                   end
118.                    
119.                    /********************/
120.                         
121.                   6:
122.                   begin isEn[1] <= 1'b0; rCMD <= _BSTP; i <= i + 1'b1; end
123.                    
124.                   /******************/
125.                         
126.                    7: // Generate done signal
127.                    begin rCMD <= _NOP; isDone <= 1'b1; i <= i + 1'b1; end
128.                        
129.                   8:
130.                   begin isDone <= 1'b0; i <= 4'd0; end
131.           
132.                endcase

以上内容为部分核心操作。以上内容是读操作。步骤4满足CL以后,步骤5读取并且发送512个数据给FIFO。步骤6,拉低isEn[1]然后发送BSTP命令。步骤7发送NOP命令,然后产生完成信号。

133.              else if( iCall[1] )
134.                case( i )
135.                    
136.                   0: // Send Precharge Command
137.                   begin rCMD <= _PR; i <= i + 1'b1; end
138.                         
139.                   1: // wait TRP 20ns
140.                   if( C1 == TRP -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
141.                    else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
142.                         
143.                    2: // Send Auto Refresh Command
144.                    begin rCMD <= _AR; i <= i + 1'b1; end
145.                   
146.                    3: // wait TRRC 63ns
147.                   if( C1 == TRRC -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
148.                    else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
149.                         
150.                    4: // Send Auto Refresh Command
151.                    begin rCMD <= _AR; i <= i + 1'b1; end
152.                   
153.                    5: // wait TRRC 63ns
154.                   if( C1 == TRRC -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
155.                    else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
156.                    
157.                    /********************/
158.                    
159.                    6: // Generate done signal
160.                    begin isDone <= 1'b1; i <= i + 1'b1; end
161.                        
162.                   7:
163.                   begin isDone <= 1'b0; i <= 4'd0; end
164.    
165.                endcase

以上内容为部分核心操作。以上内容是刷新操作。

166.              else if( iCall[0] )
167.                case( i )
168.                    
169.                   0:  // delay 100us
170.                   if( C1 == T100US -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
171.                   else begin C1 <= C1 + 1'b1; end 
172.                   
173.                   /********************/
174.                   
175.                   1: // Send Precharge Command
176.                   begin rCMD <= _PR; { rBA, rA } <= 15'h3fff; i <= i + 1'b1; end
177.                        
178.                   2: // wait TRP 20ns
179.                  if( C1 == TRP -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
180.                   else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
181.                   
182.                   3: // Send Auto Refresh Command
183.                   begin rCMD <= _AR; i <= i + 1'b1; end
184.                   
185.                   4: // wait TRRC 63ns
186.                  if( C1 == TRRC -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
187.                   else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
188.                        
189.                   5: // Send Auto Refresh Command
190.                   begin rCMD <= _AR; i <= i + 1'b1; end
191.                   
192.                   6: // wait TRRC 63ns
193.                  if( C1 == TRRC -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
194.                   else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
195.                
196.                   /********************/
197.                   
198.                   7: // Send LMR Cmd. Burst Read & Write,  3'b010 mean CAS latecy = 3, Sequential, 1 burst length
199.                   begin rCMD <= _LMR; rBA <= 2'b11; rA <= { 3'd0, 1'b0, 2'd0, 3'b011, 1'b0, 3'b111 }; i <= i + 1'b1; end
200.                        
201.                   8: // Send 2 nop CLK for tMRD
202.                   if( C1 == TMRD -1 ) begin C1 <= 14'd0; i <= i + 1'b1; end
203.                   else begin rCMD <= _NOP; C1 <= C1 + 1'b1; end
204.                   
205.                   /********************/
206.                   
207.                   9: // Generate done signal
208.                   begin isDone <= 1'b1; i <= i + 1'b1; end
209.                        
210.                  10:
211.                  begin isDone <= 1'b0; i <= 4'd0; end
212.                   
213.                endcase
214.      

以上内容为部分核心操作。以上内容是初始化,注意步骤7的设置内容,Burst Length 设置为 3’b111。

215.         assign { S_CKE, S_NCS, S_NRAS, S_NCAS, S_NWE } = rCMD;
216.         assign { S_BA, S_A } = { rBA, rA };
217.         assign S_DQM = rDQM;
218.         assign S_DQ  = isOut ? iData : 16'hzzzz;
219.         assign oEn = isEn;
220.         assign oDone = isDone;
221.         assign oData = D1;
222.    
223.    endmodule

以上内容为相关的输出驱动。注意,第218行表示FIFO直接驱动 S_DQ的输出。

sdram_ctrlmod.v

该控制模块不曾修改,所以笔者就不用重复粘贴了。

sdram_basemod.v

clip_image014

图21.7 SDRAM基础模块的建模图。

图21.7是SDRAM基础模块的建模图,其中控制模块只是负责上级调用,还有SDRAM功能模块的调用而已。SDRAM功能模块的读出操作经由FIFO储存模块缓冲,然后再由上级读写数据。Addr地址信号也是上级调用。注意,由于SDRAM基础模块的连线部署稍微复杂一点,为此图21.7稍微不遵守格式。

1.    module sdram_basemod
2.    (
3.         input CLOCK,
4.         input RESET,
5.         
6.         output S_CKE, S_NCS, S_NRAS, S_NCAS, S_NWE,
7.         output [1:0]S_BA,
8.         output [12:0]S_A, 
9.         output [1:0]S_DQM,
10.         inout [15:0]S_DQ,
11.         
12.         input [1:0]iEn,
13.         input [23:0]iAddr,
14.         input [15:0]iData,
15.         output [15:0]oData,
16.         output [1:0]oTag,
17.         
18.         input [1:0]iCall,
19.         output [1:0]oDone
20.    );
21.         wire [3:0]CallU1; // [3]Write, [2]Read, [1]A.Ref, [0]Initial
22.    
23.        sdram_ctrlmod U1
24.         (
25.              .CLOCK( CLOCK ),
26.              .RESET( RESET ),
27.              .iCall( iCall ),            // < top ,[1]Write [0]Read
28.              .oDone( oDone ),             // > top ,[1]Write [0]Read
29.              .oCall( CallU1 ),           // > U4
30.              .iDone( DoneU4 )          // < U4
31.    
32.         );
33.         
34.         wire [15:0]DataU2;
35.         
36.         fifo_savemod U2
37.         ( 
38.              .CLOCK( CLOCK ),
39.                .RESET( RESET ),
40.                .iEn( {iEn[1],EnU4[0]} ),      // < top
41.                .iData( iData ),                 // < top
42.                .oData( DataU2 ),             // > top
43.                .oTag() // 
44.         );
45.         
46.         fifo_savemod U3
47.         ( 
48.              .CLOCK( CLOCK ),
49.                .RESET( RESET ),
50.                .iEn( {EnU4[1],iEn[0]} ),     // < U4 & top
51.                .iData( DataU4 ),             // < U4
52.                .oData( oData ),             // > top
53.                .oTag()                     // 
54.         );
55.         
56.         wire DoneU4;
57.         wire [1:0]EnU4;
58.         wire [15:0]DataU4;
59.         
60.         sdram_funcmod U4
61.         (
62.             .CLOCK( CLOCK ),
63.              .RESET( RESET ),
64.              .S_CKE( S_CKE ),           // > top
65.              .S_NCS( S_NCS ),           // > top
66.              .S_NRAS( S_NRAS ),         // > top
67.              .S_NCAS( S_NCAS ),         // > top
68.              .S_NWE( S_NWE ),         // > top
69.              .S_BA( S_BA ),           // > top
70.              .S_A( S_A ),             // > top
71.              .S_DQM( S_DQM ),         // > top
72.              .S_DQ( S_DQ ),           // <> top        
73.              .oEn( EnU4 ),            // > U2 && U3
74.              .iAddr( iAddr ),           // < top
75.              .iData( DataU2 ),              // < U2
76.              .oData( DataU4 ),          // > top
77.              .iCall( CallU1 ),            // < U1
78.              .oDone( DoneU4 )          // > U1
79.         );
80.         
81.    endmodule

该组合模块的连线部署完全遵照图21.7。读者自己看着办吧。

sdram_demo.v

clip_image016

图21.8 实验二十一的建模图。

图21.8是实验二十一的建模图,内容上的改变也只有 En 多出来而已,不过核心操作的内容却有很大的改变。具体内容让我们来看代码吧。

1.    module sdram_demo
2.    (
3.        input CLOCK,
4.        input RESET,
5.        output S_CLK,
6.        output S_CKE, S_NCS, S_NRAS, S_NCAS, S_NWE,
7.        output [12:0]S_A, 
8.        output [1:0]S_BA,
9.        output [1:0]S_DQM,
10.        inout [15:0]S_DQ,
11.        output TXD
12.    ); 

以上内容为相关的出入端声明。

13.         wire CLOCK1,CLOCK2;
14.         
15.         pll_module U1
16.         (
17.                 .inclk0 ( CLOCK ), // 50Mhz
18.                .c0 ( CLOCK1 ),  // 133Mhz -210 degree phase
19.                .c1 ( CLOCK2 )   // 133Mhz 
20.         );
21.         

以上内容为PLL模块的实例化。

13.         wire CLOCK1,CLOCK2;
14.         
15.         pll_module U1
16.         (
17.                 .inclk0 ( CLOCK ), // 50Mhz
18.                .c0 ( CLOCK1 ),  // 133Mhz -210 degree phase
19.                .c1 ( CLOCK2 )   // 133Mhz 
20.         );
21.         
22.         wire [1:0]DoneU2;
23.         wire [15:0]DataU2;
24.         wire [1:0]TagU2;
25.         
26.         sdram_basemod U2
27.         (
28.             .CLOCK( CLOCK1 ),
29.             .RESET( RESET ),
30.              .S_CKE( S_CKE ),
31.              .S_NCS( S_NCS ),
32.              .S_NRAS( S_NRAS ),
33.              .S_NCAS( S_NCAS ),
34.              .S_NWE( S_NWE ),
35.              .S_A( S_A ),
36.              .S_BA( S_BA ),
37.              .S_DQM( S_DQM ),
38.              .S_DQ( S_DQ ),
39.              .iEn( isEn ), 
40.              .iAddr( {D1,9’d0} ),
41.              .iData( D2 ),
42.              .oData( DataU2 ),
43.              .oTag( TagU2 ),
44.              .iCall( isCall ),
45.              .oDone( DoneU2 )
46.         );
47.         

以上内容为SDRAM基础模块的实例化。

48.         parameter B115K2 = 11'd1157, TXFUNC = 6'd16;
49.         
50.         reg [5:0]i,Go;
51.         reg [10:0]C1,C2;
52.         reg [14:0]D1;
53.         reg [15:0]D2,D3;
54.         reg [10:0]T;
55.         reg [1:0]isCall,isEn;
56.         reg rTXD;
57.         
58.         always @ ( posedge CLOCK1 or negedge RESET )
59.             if( !RESET )
60.                 begin
61.                           i <= 6'd0;
62.                          Go <= 6'd0;
63.                          C1 <= 11'd0;
64.                          C2 <= 11'd0;
65.                           D1 <= 15'd0;
66.                          D2 <= 16'hA000;
67.                          D3 <= 16'd0;
68.                          T <= 11'd0;
69.                          isCall <= 2'b00;
70.                          isEn <= 2'b00;
71.                          rTXD <= 1'b1;
72.                 end
73.             else 

以上内容为相关的寄存器声明与复位操作。第48行是波特率为115200与伪函数入口的实例化。注意,由于本实验是页读写,所以 iAddr[8:0] 基本作废,所以D1也只有15位宽而已。然后 { D1,9’d0 } 联合驱动 iAddr。

74.                 case( i )
75.                        
76.                         0:
77.                         begin isEn[1] <= 1'b1; i <= i + 1'b1; end
78.                         
79.                         1:
80.                         begin isEn[1] <= 1'b0; i <= i + 1'b1; end
81.                         
82.                         2:
83.                         if( C2 == 511 ) begin C2 <= 11'd0; i <= i + 1'b1; end
84.                         else begin D2[11:0] <= (D2[11:0] + 1'b1); C2 <= C2 + 1'b1; i <= 6'd0; end
85.                        
86.                         3:
87.                         if( DoneU2[1] ) begin isCall[1] <= 1'b0; i <= i + 1'b1; end
88.                         else begin isCall[1] <= 1'b1; D1 <= 15'd0; end
89.                         
90.                         4:
91.                         if( DoneU2[0] ) begin  isCall[0] <= 1'b0; i <= i + 1'b1; end
92.                         else begin isCall[0] <= 1'b1; D1 <= 15'd0; end
93.                         
94.                         5:
95.                         begin isEn[0] <= 1'b1; i <= i + 1'b1; end
96.                         
97.                         6:
98.                         begin D3 <= DataU2; isEn[0] <= 1'b0; i <= i + 1'b1; end
99.                         
100.                         7:
101.                         begin T <= { 2'b11, D3[15:8], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end
102.                         
103.                         8:
104.                         begin T <= { 2'b11, D3[7:0], 1'b0 }; i <= TXFUNC; Go <= i + 1'b1; end
105.                         
106.                         9:
107.                         if( C2 == 24'd511 ) begin C2 <= 11'd0; i <= i + 1'b1; end
108.                         else begin C2 <= C2 + 1'b1; i <= 6'd5; end
109.                                              
110.                         10:
111.                         i <= i;
112.                         
113.                        /******************************/
114.

 

以上内容为部分核心操作。步骤0~2是用来写满FIFO。步骤3将FIFO的内容写入SDRAM。步骤4又将内容读至FIFO。步骤5~9从FIFO读出内容,并且发送出去,直至512个数据读完为止。步骤10发呆。

115.                          16,17,18,19,20,21,22,23,24,25,26:
116.                         if( C1 == B115K2 -1 ) begin C1 <= 11'd0; i <= i + 1'b1; end
117.                         else begin rTXD <= T[i - 16]; C1 <= C1 + 1'b1; end
118.                         
119.                         27:
120.                         i <= Go;
121.                         
122.                endcase
123.    
124.          assign S_CLK = CLOCK2;
125.          assign TXD = rTXD;
126.    
127.    endmodule

以上内容为部分核心操作。步骤16~27是发送一帧数据的伪函数。第124~125行是相关输出驱动声明。综合完毕并且下载程序,如果串口调试软件出现数据 A000~A1FF 表示实验成功。

细节一:完整的个体模块

本实验的SDRAM基础模块已经准备就绪。

posted on 2015-04-13 10:03  ALINX官方博客  阅读(1828)  评论(0编辑  收藏  举报