aboutsummaryrefslogtreecommitdiffstats
path: root/sources/hdl/verilog/dll_toggler.v
blob: 9d516792553d910644207e2cf27caefb1ddf1a4e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
`include "parameters.vh"
`define CKE_WIDTH 2
module dll_toggler #(parameter CKE_WIDTH = 1, RANK_WIDTH = 1, DQ_WIDTH = 64, DRAM_CMD_SLOTS = 4,
										DATA_BUF_ADDR_WIDTH = 5, DBUF_WIDTH = 4, DQ_BURST = 8)

(
  
  input                                             clk,
  input                                             rst,
  input                                             toggle_valid,
  output reg                                        dllt_done,  
  
  // ----------  DDR4 Signals  ----------
  output [7:0]                                      mc_ACT_n,  // DRAM ACT_n command signal for four DRAM clock cycles.
  output [17*8-1:0]                                 mc_ADR,    // DRAM address. There are 8 bits in the fabric interface for each address bit on the DRAM bus.
  output [`BANK_WIDTH*8-1:0]                        mc_BA,     // DRAM bank address. 8 bits for each DRAM bank address.
  output [`BG_WIDTH*8-1:0]                          mc_BG,     // DRAM bank group address.
  output [`CS_WIDTH*8-1:0]                          mc_CS_n,   // DRAM CS_n
  
  // NOTE: CKE is transmitted within another clock domain that is 4x faster than fabric clock.
  output [`CKE_WIDTH*8-1:0]                         mc_CKE,
  output                                            clk_sel
  
  );
    
  wire [13:0] MR1_CONF = 14'b00001100000000;  
    
  reg [17*8-1:0]       ADR_ns, ADR_r;
  reg [`BANK_WIDTH*8-1:0]       BA_ns, BA_r;
  reg [`BG_WIDTH*8-1:0]         BG_ns, BG_r;
  reg [`CS_WIDTH*8-1:0]         CS_n_ns, CS_n_r;
  reg [`ODT_WIDTH*8-1:0]        ODT_ns, ODT_r;
  reg [`CKE_WIDTH*8-1:0]        CKE_ns, CKE_r;
  
  reg                           clk_sel_ns, clk_sel_r;
  
  assign mc_ACT_n   = {8{`HIGH}};
  assign mc_ADR     = ADR_r;
  assign mc_BA      = BA_r;
  assign mc_BG      = BG_r;
  assign mc_CS_n    = CS_n_r;
  assign mc_CKE     = CKE_r;
  assign clk_sel    = clk_sel_r;
  
  
  // TODO we need to implement the following routine (TO TURN DLL OFF ONLY)
  // 1 - Precharge all banks (IDLE STATE)
  // 2 - Set MR1 A0 to 1 (DISABLE DLL), wait tMOD
  // 3 - Enter self-refresh mode, wait until tCKSRE/tCKSRE_PAR
  // 4 - Change clock frequency
  // 5 - Wait at least tCKSRX (until clock signal stabilizes)
  // 6 - Exit self-refresh mode, keep CKE high from now on
  //     if any ODT feature was enabled in self-ref. mode
  //     ODT signal must be LOW.
  // 7 - Wait tXS and set mode registers to appropriate values
  //     (UG says that CL, CWL and WR may need to be updated),
  //     wait for another tMOD 
  
  localparam IDLE_S                 = 0;
  localparam IDLE_WAIT_S            = 1;
  localparam SET_MR_1_S             = 2;
  localparam WAIT_MR_1_S            = 3;
  localparam ENTER_SELF_REF_S       = 4;
  localparam WAIT_ENTER_SELF_REF_S  = 5;
  localparam CHANGE_CLK_FREQ_S      = 6;
  localparam WAIT_CHANGE_CLK_FREQ_S = 7;
  localparam EXIT_SELF_REF_S        = 8;
  localparam WAIT_EXIT_SELF_REF_S   = 9;
  
  localparam T_PRECHARGE      = 5;    // in terms of MC cycles (which is 4x less frequent than the DDR4)
  localparam T_MOD            = 24;   // Max(24CK,15ns)
  localparam T_CKSRE          = 300;  // Max(5CK,10ns)
  localparam T_CKSRX          = 300;  // Max(5CK, 10ns) + additional room for clock to stabilize;
  localparam T_XS             = 1000;
  
  
  reg[3:0] state_r, state_ns;
  
  reg[9:0] wait_r, wait_ns;
  
  integer adr_bit_i;
  
  always @* begin
    // Set bank and bank group signals
    dllt_done  = `LOW;
    ADR_ns     = {17*8{`HIGH}};
    BG_ns      = {`BG_WIDTH*8{`LOW}};
    BA_ns      = {`BANK_WIDTH*8{`LOW}};
    CS_n_ns    = {`CS_WIDTH*8{`HIGH}}; // by default we don't issue any commands
    CKE_ns     = CKE_r;              // register this signal because it needs to be LOW during self-ref.
    wait_ns    = wait_r;
    state_ns   = state_r;
    clk_sel_ns = clk_sel_r;
    case (state_r)
      IDLE_S: begin
        if(toggle_valid) begin
          CS_n_ns[1:0] = {2*`CS_WIDTH{`LOW}};
          ADR_ns[17*8-3*8 +: 2] = {2{`LOW}};  // WE
          ADR_ns[17*8-2*8 +: 2] = {2{`HIGH}}; // ~CAS
          ADR_ns[17*8-8   +: 2] = {2{`LOW}};  // RAS
          ADR_ns[10*8 +: 2]              = {2{`HIGH}}; // Pre ALL
          wait_ns                        = T_PRECHARGE;
          state_ns                       = IDLE_WAIT_S;
        end
      end
      IDLE_WAIT_S: begin
        if(wait_r > 0)
          wait_ns = wait_r - 1'b1;
        else
          state_ns = SET_MR_1_S;
      end
      SET_MR_1_S: begin
        CS_n_ns[1:0] = {2*`CS_WIDTH{`LOW}};
        ADR_ns[17*8-3*8 +: 2] = {2{`LOW}};  // WE
        ADR_ns[17*8-2*8 +: 2] = {2{`LOW}};  // CAS
        ADR_ns[17*8-8   +: 2] = {2{`LOW}};  // RAS
        for(adr_bit_i = 0 ; adr_bit_i < 14 ; adr_bit_i = adr_bit_i + 1) begin
            ADR_ns[adr_bit_i*8 +: 2] =
                      {2{MR1_CONF[adr_bit_i]}};
        end
        // Bank + Bank group bits indicate which register this MRS is writing to.
        BA_ns[0 +: 2]  = {2{`HIGH}}; // Select MR1
        ADR_ns[0 +: 2] = {2{`LOW}};  // Set A0 to 0
        state_ns = WAIT_MR_1_S;
        wait_ns  = T_MOD;
      end
      WAIT_MR_1_S: begin
        if(wait_r > 0)
          wait_ns = wait_r - 1'b1;
        else
          state_ns = ENTER_SELF_REF_S;
      end
      ENTER_SELF_REF_S: begin
        CKE_ns = `LOW;
        CS_n_ns[1:0] = {2*`CS_WIDTH{`LOW}};
        ADR_ns[17*8-3*8 +: 2] = {2{`HIGH}};  // ~WE
        ADR_ns[17*8-2*8 +: 2] = {2{`LOW}};  // CAS
        ADR_ns[17*8-8   +: 2] = {2{`LOW}};  // RAS
        state_ns = WAIT_ENTER_SELF_REF_S;
        wait_ns  = T_CKSRE;
      end
      WAIT_ENTER_SELF_REF_S: begin
        if(wait_r > 0)
          wait_ns = wait_r - 1'b1;
        else begin
          state_ns = CHANGE_CLK_FREQ_S;            
        end
      end
      CHANGE_CLK_FREQ_S: begin
        clk_sel_ns = ~clk_sel_r;
        wait_ns    = T_CKSRX;
        state_ns   = WAIT_CHANGE_CLK_FREQ_S;
      end
      WAIT_CHANGE_CLK_FREQ_S: begin
        if(wait_r > 0)
          wait_ns = wait_r - 1'b1;
        else begin
          state_ns = EXIT_SELF_REF_S;
        end
      end
      EXIT_SELF_REF_S: begin
        CKE_ns = {`CKE_WIDTH*8{`HIGH}};
        CS_n_ns[7:0] = {8*`CS_WIDTH{`HIGH}};
        ADR_ns[17*8-3*8 +: 2] = {2{`HIGH}};  // ~WE
        ADR_ns[17*8-2*8 +: 2] = {2{`HIGH}};  // CAS
        ADR_ns[17*8-8   +: 2] = {2{`HIGH}};  // RAS
        state_ns = WAIT_EXIT_SELF_REF_S;
        wait_ns  = T_XS;
      end
      WAIT_EXIT_SELF_REF_S: begin
        CS_n_ns[7:0] = {8*`CS_WIDTH{`HIGH}};
        if(wait_r > 0)
          wait_ns = wait_r - 1'b1;
        else begin
          state_ns  = IDLE_S;
          dllt_done = `HIGH;           
        end
      end
    endcase 
  
  end
  
  always @(posedge clk) begin
    if(rst) begin
      state_r   <= IDLE_S;
      wait_r    <= `LOW;
      clk_sel_r <= `LOW;
      CKE_r     <= {`CKE_WIDTH*8{`HIGH}};
      ADR_r      = {17*8{`LOW}};
      BG_r       = {`BG_WIDTH*8{`LOW}};
      BA_r       = {`BANK_WIDTH*8{`LOW}};
      CS_n_r     = {`CS_WIDTH*8{`HIGH}};
    end
    else begin
      state_r   <= state_ns;
      wait_r    <= wait_ns;
      clk_sel_r <= clk_sel_ns;
      CKE_r <= CKE_ns;
      ADR_r <= ADR_ns;
      BA_r <= BA_ns;
      BG_r <= BG_ns;
      CS_n_r <= CS_n_ns;
    end
  end
  
endmodule