../src/lowrisc_prim_generic_flash_0/rtl/prim_generic_flash.sv Cov: 100%

   1: // Copyright lowRISC contributors.
   2: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
   3: // SPDX-License-Identifier: Apache-2.0
   4: //
   5: // prim flash module - Emulated using memory
   6: //
   7: 
   8: module prim_generic_flash #(
   9:   parameter int PagesPerBank = 256, // pages per bank
  10:   parameter int WordsPerPage = 256, // words per page
  11:   parameter int DataWidth   = 32,   // bits per word
  12:   parameter bit SkipInit = 1,       // this is an option to reset flash to all F's at reset
  13: 
  14:   // Derived parameters
  15:   localparam int PageW = $clog2(PagesPerBank),
  16:   localparam int WordW = $clog2(WordsPerPage),
  17:   localparam int AddrW = PageW + WordW
  18: ) (
  19:   input                        clk_i,
  20:   input                        rst_ni,
  21:   input                        rd_i,
  22:   input                        prog_i,
  23:   input                        pg_erase_i,
  24:   input                        bk_erase_i,
  25:   input [AddrW-1:0]            addr_i,
  26:   input [DataWidth-1:0]        prog_data_i,
  27:   output logic                 ack_o,
  28:   output logic [DataWidth-1:0] rd_data_o,
  29:   output logic                 init_busy_o
  30: );
  31: 
  32:   // Emulated flash macro values
  33:   localparam int ReadCycles = 1;
  34:   localparam int ProgCycles = 50;
  35:   localparam int PgEraseCycles = 200;
  36:   localparam int BkEraseCycles = 2000;
  37: 
  38:   // Locally derived values
  39:   localparam int WordsPerBank  = PagesPerBank * WordsPerPage;
  40: 
  41:   typedef enum logic [2:0] {
  42:     StReset    = 'h0,
  43:     StInit     = 'h1,
  44:     StIdle     = 'h2,
  45:     StRead     = 'h3,
  46:     StProg     = 'h4,
  47:     StErase    = 'h5
  48:   } state_e;
  49: 
  50:   state_e st_q, st_d;
  51: 
  52:   logic [31:0]              time_cnt;
  53:   logic [31:0]              index_cnt;
  54:   logic                     time_cnt_inc ,time_cnt_clr, time_cnt_set1;
  55:   logic                     index_cnt_inc, index_cnt_clr;
  56:   logic [31:0]              index_limit_q, index_limit_d;
  57:   logic [31:0]              time_limit_q, time_limit_d;
  58:   logic                     prog_pend_q, prog_pend_d;
  59:   logic                     mem_req;
  60:   logic                     mem_wr;
  61:   logic [AddrW-1:0]         mem_addr;
  62:   logic [DataWidth-1:0]     held_rdata;
  63:   logic [DataWidth-1:0]     held_wdata;
  64:   logic [DataWidth-1:0]     mem_wdata;
  65:   logic                     hold_cmd;
  66:   logic [AddrW-1:0]         held_addr;
  67: 
  68:   // insert a fifo here to break the large fanout from inputs to memories on reads
  69:   logic rd_q;
  70:   logic [AddrW-1:0] addr_q;
  71: 
  72:   prim_fifo_sync #(
  73:       .Width  (AddrW),
  74:       .Pass   (0),
  75:       .Depth  (2)
  76:   ) i_slice (
  77:     .clk_i,
  78:     .rst_ni,
  79:     .clr_i  (1'b0),
  80:     .wvalid (rd_i),
  81:     .wready (),
  82:     .wdata  (addr_i),
  83:     .depth  (),
  84:     .rvalid (rd_q),
  85:     .rready (hold_cmd), //whenver command is held, pop
  86:     .rdata  (addr_q)
  87:   );
  88: 
  89: 
  90:   always_ff @(posedge clk_i or negedge rst_ni) begin
  91:     if (!rst_ni) st_q <= StReset;
  92:     else st_q <= st_d;
  93:   end
  94: 
  95:   always_ff @(posedge clk_i or negedge rst_ni) begin
  96:     if (!rst_ni) begin
  97:       held_addr <= '0;
  98:       held_wdata <= '0;
  99:     end else if (hold_cmd) begin
 100:       held_addr <= rd_q ? addr_q : addr_i;
 101:       held_wdata <= prog_data_i;
 102:     end
 103:   end
 104: 
 105:   always_ff @(posedge clk_i or negedge rst_ni) begin
 106:     if (!rst_ni) begin
 107:       time_limit_q  <= 32'h0;
 108:       index_limit_q <= 32'h0;
 109:       prog_pend_q   <= 1'h0;
 110:     end else begin
 111:       time_limit_q  <= time_limit_d;
 112:       index_limit_q <= index_limit_d;
 113:       prog_pend_q   <= prog_pend_d;
 114:     end
 115:   end
 116: 
 117:   // prog_pend_q is necessary to emulate flash behavior that a bit written to 0 cannot be written
 118:   // back to 1 without an erase
 119:   always_ff @(posedge clk_i or negedge rst_ni) begin
 120:     if (!rst_ni) begin
 121:       time_cnt <= 32'h0;
 122:       index_cnt <= 32'h0;
 123:       held_rdata <= 'h0;
 124:     end else begin
 125:       if (time_cnt_inc) time_cnt <= time_cnt + 1'b1;
 126:       else if (time_cnt_set1) time_cnt <= 32'h1;
 127:       else if (time_cnt_clr) time_cnt <= 32'h0;
 128: 
 129:       if (index_cnt_inc) index_cnt <= index_cnt + 1'b1;
 130:       else if (index_cnt_clr) index_cnt <= 32'h0;
 131: 
 132:       if (prog_pend_q) held_rdata <= rd_data_o;
 133: 
 134:     end
 135:   end
 136: 
 137: 
 138:   always_comb begin
 139:     // state
 140:     st_d             = st_q;
 141: 
 142:     // internally consumed signals
 143:     index_limit_d    = index_limit_q;
 144:     time_limit_d     = time_limit_q;
 145:     prog_pend_d      = prog_pend_q;
 146:     mem_req          = 'h0;
 147:     mem_wr           = 'h0;
 148:     mem_addr         = 'h0;
 149:     mem_wdata        = 'h0;
 150:     time_cnt_inc     = 1'h0;
 151:     time_cnt_clr     = 1'h0;
 152:     time_cnt_set1    = 1'h0;
 153:     index_cnt_inc    = 1'h0;
 154:     index_cnt_clr    = 1'h0;
 155:     hold_cmd         = 1'h0;
 156: 
 157:     // i/o
 158:     init_busy_o      = 1'h0;
 159:     ack_o            = 1'h0;
 160: 
 161:     unique case (st_q)
 162:       StReset: begin
 163:         init_busy_o = 1'h1;
 164:         st_d = StInit;
 165:       end
 166:       // Emulate flash power up to all 1's
 167:       // This implies this flash will not survive a reset
 168:       // Might need a different RESET for FPGA purposes
 169:       StInit: begin
 170:         init_busy_o = 1'h1;
 171:         if (index_cnt < WordsPerBank && !SkipInit) begin
 172:           st_d = StInit;
 173:           index_cnt_inc = 1'b1;
 174:           mem_req = 1'h0;
 175:           mem_wr  = 1'h0;
 176:           mem_addr = index_cnt[AddrW-1:0];
 177:           mem_wdata = {DataWidth{1'b1}};
 178:         end else begin
 179:           st_d = StIdle;
 180:           index_cnt_clr = 1'b1;
 181:         end
 182:       end
 183:       StIdle: begin
 184:         if (rd_q) begin
 185:           // reads begin immediately
 186:           hold_cmd = 1'b1;
 187:           mem_addr = addr_q;
 188:           mem_req = 1'b1;
 189:           time_cnt_inc = 1'b1;
 190:           st_d = StRead;
 191:         end else if (prog_i) begin
 192:           hold_cmd = 1'b1;
 193:           st_d = StRead;
 194:           prog_pend_d = 1'b1;
 195:         end else if (pg_erase_i) begin
 196:           hold_cmd = 1'b1;
 197:           st_d = StErase;
 198:           index_limit_d = WordsPerPage;
 199:           time_limit_d = PgEraseCycles;
 200:         end else if (bk_erase_i) begin
 201:           hold_cmd = 1'b1;
 202:           st_d = StErase;
 203:           index_limit_d = WordsPerBank;
 204:           time_limit_d = BkEraseCycles;
 205:         end
 206:       end
 207:       StRead: begin
 208:         mem_addr = held_addr;
 209:         if (time_cnt < ReadCycles) begin
 210:           mem_req = 1'b1;
 211:           time_cnt_inc = 1'b1;
 212:         end else if (!prog_pend_q) begin
 213:           ack_o = 1'b1; //finish up transaction
 214: 
 215:           // if another request already pending
 216:           if (rd_q) begin
 217:             hold_cmd = 1'b1;
 218:             mem_addr = addr_q;
 219:             mem_req = 1'b1;
 220:             time_cnt_set1 = 1'b1;
 221:             st_d = StRead;
 222:           end else begin
 223:             time_cnt_clr = 1'b1;
 224:             st_d = StIdle;
 225:           end
 226:         end else if (prog_pend_q) begin
 227:           // this is the read performed before a program operation
 228:           prog_pend_d = 1'b0;
 229:           time_cnt_clr = 1'b1;
 230:           st_d = StProg;
 231:         end
 232:       end
 233:       StProg: begin
 234:         mem_addr = held_addr;
 235: 
 236:         // if data is already 0, cannot program to 1 without erase
 237:         mem_wdata = held_wdata & held_rdata;
 238:         if (time_cnt < ProgCycles) begin
 239:           mem_req = 1'b1;
 240:           mem_wr = 1'b1;
 241:           time_cnt_inc = 1'b1;
 242:         end else begin
 243:           st_d = StIdle;
 244:           ack_o  = 1'b1;
 245:           time_cnt_clr = 1'b1;
 246:         end
 247:       end
 248:       StErase: begin
 249:         // Actual erasing of the page
 250:         if (index_cnt < index_limit_q || time_cnt < time_limit_q) begin
 251:           mem_req = 1'b1;
 252:           mem_wr = 1'b1;
 253:           mem_wdata = {DataWidth{1'b1}};
 254: 
 255:           mem_addr = held_addr + index_cnt[AddrW-1:0];
 256:           time_cnt_inc = (time_cnt < time_limit_q);
 257:           index_cnt_inc = (index_cnt < index_limit_q);
 258:         end else begin
 259:           st_d = StIdle;
 260:           ack_o = 1'b1;
 261:           time_cnt_clr = 1'b1;
 262:           index_cnt_clr = 1'b1;
 263:         end
 264:       end
 265:       default: begin
 266:         st_d = StIdle;
 267:       end
 268:     endcase // unique case (st_q)
 269:   end // always_comb
 270: 
 271:   prim_ram_1p #(
 272:     .Width(DataWidth),
 273:     .Depth(WordsPerBank),
 274:     .DataBitsPerMask(DataWidth)
 275:   ) u_mem (
 276:     .clk_i,
 277:     .req_i    (mem_req),
 278:     .write_i  (mem_wr),
 279:     .addr_i   (mem_addr),
 280:     .wdata_i  (mem_wdata),
 281:     .wmask_i  ({DataWidth{1'b1}}),
 282:     .rdata_o  (rd_data_o)
 283:   );
 284: 
 285: endmodule // prim_generic_flash
 286: