File: uflash_controller.v

package info (click to toggle)
apycula 0.29%2Bdfsg1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 41,256 kB
  • sloc: python: 15,619; asm: 1,147; makefile: 448; sh: 72; tcl: 56; vhdl: 26
file content (283 lines) | stat: -rw-r--r-- 8,077 bytes parent folder | download | duplicates (3)
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
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
/* Copyright 2024 Grug Huhler.  License SPDX BSD-2-Clause.
 
   This module implements a controller for the user flash on the Tang
   Nano 9K FPGA development board.  The controller integrates with
   the PicoRV32 mini-SoC bus scheme.  It also instantiates the
   actual flash.  Note: the Tang Nano 20K does not contain user
   flash.

   See document UG295 "Gowin User Flash".

   The Flash is 608 Kbits, 32-bits wide, organized into 304 rows of 64
   columns each.  The erase page size is 2048 bytes, so there are
   38 pages that may be separately erased.

   This controller expects a system clock no more than 40 Mhz.  The
   actual clock frequency must be passed to the module via the
   CLK_FREQ parameter.

   Leave at least 10 millisconds between a write and an erase and do
   not write the same address twice without an erase between the writes.
   The controller does not enforce these rules.

   Reads can be 8, 16, or 32 bits wide.  Erasing is done on a page basis.
   To erase a page, do an 8 bit write to a 32-bit aligned address in the
   page. To program (write), do a 32-bit write to the address to be
   programmed.
*/

module uflash #(parameter CLK_FREQ=5400000)
(
 input wire         reset_n,
 input wire         clk,
 input wire         sel,
 input wire [3:0]   wstrb,
 input wire [14:0]  addr, // word address, 9-bits row, 6 bits col
 input wire [31:0]  data_i,
 output wire        ready,
 output wire [31:0] data_o
);

   // state machine states
   localparam IDLE = 'd0;
   localparam READ1 = 'd1;
   localparam READ2 = 'd2;
   localparam ERASE1 = 'd3;
   localparam ERASE2 = 'd4; 
   localparam ERASE3 = 'd5; 
   localparam ERASE4 = 'd6; 
   localparam ERASE5 = 'd7;
   localparam WRITE1 = 'd8;
   localparam WRITE2 = 'd9;
   localparam WRITE3 = 'd10;
   localparam WRITE4 = 'd11;
   localparam WRITE5 = 'd12;
   localparam WRITE6 = 'd13;
   localparam WRITE7 = 'd14;
   localparam DONE = 'd15;

   // clocks required in state when > 1
   localparam E2_CLKS = $rtoi(CLK_FREQ * 6.0e-6) + 1;
   localparam E3_CLKS = $rtoi(CLK_FREQ * 120.0e-3) + 1;
   localparam E4_CLKS = $rtoi(CLK_FREQ * 6.0e-6) + 1;
   localparam E5_CLKS = $rtoi(CLK_FREQ * 11.0e-6) + 1;
   localparam W2_CLKS = $rtoi(CLK_FREQ * 6.0e-6) + 1;
   localparam W3_CLKS = $rtoi(CLK_FREQ * 11.0e-6) + 1;
   localparam W4_CLKS = $rtoi(CLK_FREQ * 16.0e-6) + 1;
   localparam W6_CLKS = $rtoi(CLK_FREQ * 6.0e-6) + 1;
   localparam W7_CLKS = $rtoi(CLK_FREQ * 11.0e-6) + 1;

   reg xe = 1'b0;
   reg ye = 1'b0;
   reg se = 1'b0;
   reg erase = 1'b0;
   reg nvstr = 1'b0;
   reg prog = 1'b0;
   reg [3:0] state = IDLE;
   reg [23:0] cycle_count;

   assign ready = state == DONE;

   always @(posedge clk or negedge reset_n)
     if (!reset_n) begin
        state <= IDLE;
        se <= 1'b0;
        xe <= 1'b0;
        ye <= 1'b0;
        erase <= 1'b0;
        nvstr <= 1'b0;
        prog <= 1'b0;
        cycle_count <= 'd0;
     end
     else
       case (state)
         IDLE: begin
            if (sel) begin
               if (wstrb == 'b0) begin
                  // Read
                  state <= READ1;
                  xe <= 1'b1;
                  ye <= 1'b1;
               end
               else if (&wstrb) begin
                  // Write
                  state <= WRITE1;
                  xe <= 1'b1;
               end else if (wstrb == 'b1) begin
                  // Erase
                  ye <= 1'b0;
                  se <= 1'b0;
                  xe <= 1'b1;
                  erase <= 1'b0;
                  nvstr <= 1'b0;
                  state <= ERASE1;
               end else begin
                  // Unsupported
                  state <= DONE;
               end
            end
            else
              state <= IDLE;
         end
         READ1: begin
            se <= 1'b1;
            state <= READ2;
         end
         READ2: begin
            se <= 1'b0;
            state <= DONE;
         end
         ERASE1: begin
            state <= ERASE2;
            cycle_count <= 'd0;
            erase <= 1'b1;
         end
         ERASE2: begin
            if (cycle_count < E2_CLKS) begin
               state <= ERASE2;
               cycle_count <= cycle_count + 1;
            end
            else begin
               state <= ERASE3;
               cycle_count <= 'd0;
               nvstr <= 1'b1;
            end
         end
         ERASE3: begin
            if (cycle_count < E3_CLKS) begin
               state <= ERASE3;
               cycle_count <= cycle_count + 1;
            end
            else begin
               state <= ERASE4;
               cycle_count <= 'd0;
               erase <= 1'b0;
            end
         end
         ERASE4: begin
            if (cycle_count < E4_CLKS) begin
               state <= ERASE4;
               cycle_count <= cycle_count + 1;
            end
            else begin
               state <= ERASE5;
               cycle_count <= 'd0;
               nvstr <= 1'b0;
            end
         end
         ERASE5: begin
            if (cycle_count < E5_CLKS) begin
               state <= ERASE5;
               cycle_count <= cycle_count + 1;
            end
            else begin
               state <= DONE;
               cycle_count <= 'd0;
               xe <= 1'b0;
            end
         end
         WRITE1: begin
            state <= WRITE2;
            prog <= 1'b1;
         end
         WRITE2: begin
            if (cycle_count < W2_CLKS) begin
               state <= WRITE2;
               cycle_count <= cycle_count + 1;
            end
            else begin
               state <= WRITE3;
               cycle_count <= 'd0;
               nvstr <= 1'b1;
            end
         end
         WRITE3: begin
            if (cycle_count < W3_CLKS) begin
               state <= WRITE3;
               cycle_count <= cycle_count + 1;
            end
            else begin
               state <= WRITE4;
               cycle_count <= 'd0;
               ye <= 1'b1;
            end
         end
         WRITE4: begin
            if (cycle_count < W4_CLKS) begin
               state <= WRITE4;
               cycle_count <= cycle_count + 1;
            end
            else begin
               state <= WRITE5;
               cycle_count <= 'd0;
               ye <= 1'b0;
            end
         end
         WRITE5: begin
            state <= WRITE6;
            prog <= 1'b0;
         end
         WRITE6: begin
            if (cycle_count < W6_CLKS) begin
               state <= WRITE6;
               cycle_count <= cycle_count + 1;
            end
            else begin
               state <= WRITE7;
               cycle_count <= 'd0;
               nvstr <= 1'b0;
            end
         end
         WRITE7: begin
            if (cycle_count < W7_CLKS) begin
               state <= WRITE7;
               cycle_count <= cycle_count + 1;
            end
            else begin
               state <= DONE;
               cycle_count <= 'd0;
               xe <= 1'b0;
            end
         end
         DONE: begin
            state <= IDLE;
            xe <= 1'b0;
            ye <= 1'b0;
            se <= 1'b0;
            erase <= 1'b0;
            nvstr <= 1'b0;
            prog <= 1'b0;
         end
       endcase

`ifdef HAS_FLASH608K	   
   (* keep *)
   FLASH608K uflash_hw0 (
     .DOUT(data_o), //output [31:0] dout
     .XE(xe), //input xe
     .YE(ye), //input ye
     .SE(se), //input se
     .PROG(prog), //input prog
     .ERASE(erase), //input erase
     .NVSTR(nvstr), //input nvstr
     .XADR(addr[14:6]), //input [8:0] xadr
     .YADR(addr[5:0]), //input [5:0] yadr
     .DIN(data_i) //input [31:0] din
    );
`else
   (* keep *)
   FLASH64KZ uflash_hw0 (
     .DOUT(data_o), //output [31:0] dout
     .XE(xe), //input xe
     .YE(ye), //input ye
     .SE(se), //input se
     .PROG(prog), //input prog
     .ERASE(erase), //input erase
     .NVSTR(nvstr), //input nvstr
     .XADR(addr[10:6]), //input [4:0] xadr
     .YADR(addr[5:0]), //input [5:0] yadr
     .DIN(data_i) //input [31:0] din
    );
`endif

endmodule