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"
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 [`ADDR_WIDTH*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 [`ADDR_WIDTH*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 = {`ADDR_WIDTH*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[`ADDR_WIDTH*8-3*8 +: 2] = {2{`LOW}}; // WE
ADR_ns[`ADDR_WIDTH*8-2*8 +: 2] = {2{`HIGH}}; // ~CAS
ADR_ns[`ADDR_WIDTH*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[`ADDR_WIDTH*8-3*8 +: 2] = {2{`LOW}}; // WE
ADR_ns[`ADDR_WIDTH*8-2*8 +: 2] = {2{`LOW}}; // CAS
ADR_ns[`ADDR_WIDTH*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[`ADDR_WIDTH*8-3*8 +: 2] = {2{`HIGH}}; // ~WE
ADR_ns[`ADDR_WIDTH*8-2*8 +: 2] = {2{`LOW}}; // CAS
ADR_ns[`ADDR_WIDTH*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[`ADDR_WIDTH*8-3*8 +: 2] = {2{`HIGH}}; // ~WE
ADR_ns[`ADDR_WIDTH*8-2*8 +: 2] = {2{`HIGH}}; // CAS
ADR_ns[`ADDR_WIDTH*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 = {`ADDR_WIDTH*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
|