data/full_repos/permissive/149848590/src/dev/spi/godson_sbridge_spi.v
 line:609: before: "dat_o"
1: b'%Warning-IMPLICIT: data/full_repos/permissive/149848590/src/dev/spi/godson_sbridge_spi.v:506: Signal definition not found, creating implicitly: \'param_scs\'\n                                                                                            : ... Suggested alternative: \'param_o\'\n   assign     param_scs       = param2[3];  \n              ^~~~~~~~~\n                   ... Use "/* verilator lint_off IMPLICIT */" and lint_on around source to disable this message.\n%Warning-WIDTH: data/full_repos/permissive/149848590/src/dev/spi/godson_sbridge_spi.v:612: Operator EQ expects 4 bits on the RHS, but RHS\'s CONST \'2\'h2\' generates 2 bits.\n                                                                                         : ... In instance spi_flash_ctrl.simple_spi\n  assign rfre = wb_acc & (adr_i == 2\'b10) & ack_o & ~we_i;\n                                ^~\n%Warning-WIDTH: data/full_repos/permissive/149848590/src/dev/spi/godson_sbridge_spi.v:631: Operator EQ expects 4 bits on the RHS, but RHS\'s CONST \'2\'h1\' generates 2 bits.\n                                                                                         : ... In instance spi_flash_ctrl.simple_spi\n  wire wr_spsr = wb_wr & (adr_i == 2\'b01);\n                                ^~\n%Warning-WIDTH: data/full_repos/permissive/149848590/src/dev/spi/godson_sbridge_spi.v:192: Operator COND expects 64 bits on the Conditional True, but Conditional True\'s REPLICATE generates 32 bits.\n                                                                                         : ... In instance spi_flash_ctrl\n   wire [63:0]      buf_addr_t = (buf_addr[31:20]==12\'h1fc)? \n                                                           ^\n%Warning-WIDTH: data/full_repos/permissive/149848590/src/dev/spi/godson_sbridge_spi.v:192: Operator COND expects 64 bits on the Conditional False, but Conditional False\'s REPLICATE generates 32 bits.\n                                                                                         : ... In instance spi_flash_ctrl\n   wire [63:0]      buf_addr_t = (buf_addr[31:20]==12\'h1fc)? \n                                                           ^\n%Warning-WIDTH: data/full_repos/permissive/149848590/src/dev/spi/godson_sbridge_spi.v:370: Operator SUB expects 64 bits on the LHS, but LHS\'s VARREF \'nxt_addr\' generates 24 bits.\n                                                                                         : ... In instance spi_flash_ctrl\n                                                   nxt_addr - 24\'h1f    :\n                                                            ^\n%Warning-WIDTH: data/full_repos/permissive/149848590/src/dev/spi/godson_sbridge_spi.v:370: Operator SUB expects 64 bits on the RHS, but RHS\'s CONST \'24\'h1f\' generates 24 bits.\n                                                                                         : ... In instance spi_flash_ctrl\n                                                   nxt_addr - 24\'h1f    :\n                                                            ^\n%Warning-WIDTH: data/full_repos/permissive/149848590/src/dev/spi/godson_sbridge_spi.v:371: Operator ADD expects 64 bits on the LHS, but LHS\'s VARREF \'nxt_addr\' generates 24 bits.\n                                                                                         : ... In instance spi_flash_ctrl\n                                                   nxt_addr + 24\'b1) :\n                                                            ^\n%Warning-WIDTH: data/full_repos/permissive/149848590/src/dev/spi/godson_sbridge_spi.v:371: Operator ADD expects 64 bits on the RHS, but RHS\'s CONST \'24\'h1\' generates 24 bits.\n                                                                                         : ... In instance spi_flash_ctrl\n                                                   nxt_addr + 24\'b1) :\n                                                            ^\n%Warning-WIDTH: data/full_repos/permissive/149848590/src/dev/spi/godson_sbridge_spi.v:369: Operator COND expects 64 bits on the Conditional False, but Conditional False\'s VARREF \'nxt_addr\' generates 24 bits.\n                                                                                         : ... In instance spi_flash_ctrl\n                     byte_ready & ~spi_pause? (buf_wrap&(&nxt_addr[4:0]) & ~(tot_bytes == 8\'b0)?\n                                            ^\n%Warning-WIDTH: data/full_repos/permissive/149848590/src/dev/spi/godson_sbridge_spi.v:368: Operator ASSIGNDLY expects 24 bits on the Assign RHS, but Assign RHS\'s COND generates 64 bits.\n                                                                                         : ... In instance spi_flash_ctrl\n         nxt_addr <= new_axireq & s_idle    ? buf_addr_t :\n                  ^~\n%Error: Exiting due to 11 warning(s)\n'
module spi_flash_ctrl(
    input           aclk,
    input           aresetn,
    input [15:0]    spi_addr,
    input           power_down_req,
    output          power_down_ack,
    input           fast_startup,

    input [3:0]     s_awlen,
    input [3:0]     s_awcache,
    input [3:0]     s_awid,
    input [31:0]    s_awaddr,
    input [2:0]     s_awsize,
    input [2:0]     s_awprot,
    input [1:0]     s_awburst,
    input [1:0]     s_awlock,
    input           s_awvalid,
    output          s_awready,

    input [3:0]     s_wid,
    input [31:0]    s_wdata,
    input [3:0]     s_wstrb,
    input           s_wlast,
    input           s_wvalid,
    output          s_wready,

    output [3:0]    s_bid,
    output [1:0]    s_bresp,
    output          s_bvalid,
    input           s_bready,

    input [3:0]     s_arlen,  
    input [3:0]     s_arcache,
    input [3:0]     s_arid,
    input [31:0]    s_araddr,
    input [2:0]     s_arsize,
    input [2:0]     s_arprot,
    input [1:0]     s_arburst,
    input [1:0]     s_arlock,
    input           s_arvalid,
    output          s_arready,

    output [3:0]    s_rid,
    output [31:0]   s_rdata,
    output [1:0]    s_rresp,
    output          s_rlast,
    output          s_rvalid,
    input           s_rready,

    output [3:0]    csn_o,
    output [3:0]    csn_en, 
    output          sck_o,
    input           sdo_i,
    output          sdo_o,
    output          sdo_en, 
    input           sdi_i,
    output          sdi_o,
    output          sdi_en,
    output          inta_o
);

   wire             areset = ~aresetn;

   wire             param_memory_en;
   wire             param_burst_en;
   wire             param_fast_read;
   wire             param_dual_io;
   wire [1:0]       param_tCSH;
   wire             param_tFAST;

   reg [9:0]        rd_state;
   reg [9:0]        rd_state_nxt;

   parameter        S_IDLE   = 10'b0000000001;
   parameter        S_IOREAD = 10'b0000000010;
   parameter        S_CSTURN = 10'b0000000100;
   parameter        S_ADDR   = 10'b0000001000;
   parameter        S_DATA   = 10'b0000010000;
   parameter        S_WAITBUS= 10'b0000100000;
   parameter        S_PDENTER= 10'b0001000000;
   parameter        S_PDEXIT = 10'b0010000000;
   parameter        S_STARTUP= 10'b0100000000;
   parameter        S_PWRDOWN= 10'b1000000000;

   wire             s_idle   = rd_state[0];
   wire             s_ioread = rd_state[1];
   wire             s_csturn = rd_state[2];
   wire             s_addr   = rd_state[3];
   wire             s_data   = rd_state[4];
   wire             s_waitbus= rd_state[5];
   wire             s_pdenter= rd_state[6];
   wire             s_pdexit = rd_state[7];
   wire             s_startup= rd_state[8];
   wire             s_pwrdown= rd_state[9];

   wire             ns_idle   = rd_state_nxt[0];
   wire             ns_ioread = rd_state_nxt[1];
   wire             ns_csturn = rd_state_nxt[2];
   wire             ns_addr   = rd_state_nxt[3];
   wire             ns_data   = rd_state_nxt[4];
   wire             ns_waitbus= rd_state_nxt[5];
   wire             ns_pdenter= rd_state_nxt[6];
   wire             ns_pdexit = rd_state_nxt[7];
   wire             ns_startup= rd_state_nxt[8];
   wire             ns_pwrdown= rd_state_nxt[9];

   reg              pdreq_r;

   reg [15:0]       cs_timer;
   reg              cs;
   reg [23:0]       nxt_addr;

   wire             write_valid;
   wire             reg_acc = s_ioread | write_valid;
   wire             reg_ack;
   wire [7:0]       reg_dat_i, reg_dat_o;
   wire [7:0]       param_o;

   reg [31:0]       shift_reg;
   reg [ 1:0]       sample;
   wire [31:0]      shift_reg_nxt;
   wire             sr_shift_inst;
   wire             sr_shift_one;
   wire             sr_shift_two;
   reg              sr_shift_inst_r;
   reg              sr_shift_two_r;
   wire             sample_en;
   wire             shift_en;
   wire             dual_out;
   wire             dual_in;
   wire [1:0]       serial_out;
   wire             cyc_end;          


   reg [2:0]        bit_cnt;          
   wire             spi_pause;        
   wire             spibus_busy;      

   reg [5:0]        adbit_cnt;        
   reg              spi_run;
   reg              sck;

   reg              buf_busy;
   reg [31:0]       buf_addr;
   reg [ 3:0]       buf_len;
   reg [ 2:0]       buf_size;
   reg [ 3:0]       buf_id;
   reg              buf_write;
   reg              buf_wrap;

   assign           s_arready = s_idle & ~pdreq_r & ~buf_busy & ~s_awvalid;
   assign           s_awready = s_idle & ~pdreq_r & ~buf_busy;

   reg              buf_busy_d;
   wire             new_axireq = ~buf_busy_d & buf_busy;
   wire             io_hit =(buf_addr[31:4] == {spi_addr, 12'b0}) &
                            (buf_len  == 4'b0);

   wire [63:0]      buf_addr_t = (buf_addr[31:20]==12'h1fc)? 
                                    {12'h0, buf_addr[19:0]}: 
                                    { 8'h0, buf_addr[23:0]}; 

   wire             burst_cont   = param_burst_en & cs &
                                  (buf_addr_t[23:0] == nxt_addr[23:0]);

   wire             burst_switch = param_burst_en & cs &
                                  (buf_addr_t[23:0] != nxt_addr[23:0]);

   reg [7:0]        tot_bytes;
   wire             byte_ready;

   always @(posedge aclk) begin
      if (areset) begin
         buf_busy  <= 1'b0;
         buf_write <= 1'b0;
         tot_bytes <= 8'b0;
      end else begin
         if ((s_arvalid|s_awvalid)&~buf_busy&s_idle&~pdreq_r) begin
            buf_busy <= 1'b1;
            buf_addr <= s_awvalid ? s_awaddr : s_araddr;
            buf_size <= s_awvalid ? s_awsize : s_arsize;
            buf_len  <= s_awvalid ? s_awlen  : s_arlen;
            buf_id   <= s_awvalid ? s_awid   : s_arid;
            buf_write<= s_awvalid;
            buf_wrap <= s_arvalid & (s_arburst==2'b10) & 
                        (|s_araddr[4:2]) & (|s_arlen);
            tot_bytes<= {8{s_arvalid&~s_awvalid}}&
                        (({4'b0,s_arlen} << s_arsize)|
                         ((8'b1<<s_arsize)-8'b1));
         end else begin
            if (s_bvalid & s_bready | s_rvalid & s_rready & s_rlast)
              buf_busy <= 1'b0;
            if (s_rvalid & s_rready)
              buf_len  <= buf_len - 4'b1;
            if (s_wvalid & s_wready & s_wlast)
              buf_write <= 1'b0;
            if (byte_ready & ~s_rvalid) begin
              tot_bytes <= tot_bytes - 8'b1;
            end
         end
      end
      buf_busy_d <= buf_busy;
   end

   reg    second_write; 
   always @(posedge aclk) begin
      if (areset) second_write <= 1'b0;
      else second_write <= (s_wvalid & s_wready & io_hit & (buf_size==3'b1) & (buf_addr[2:0]==3'b10));
   end

   assign s_wready = buf_busy & buf_write & s_idle;
   assign write_valid = s_wvalid & s_wready & io_hit &
                        ((buf_size==3'b0) | (buf_size==3'b1 && buf_addr[2:0]==3'b10)) |
                        second_write;

   reg  bvalid;
   always @(posedge aclk) begin
      if      (areset                       ) bvalid <= 1'b0;
      else if (s_bvalid & s_bready          ) bvalid <= 1'b0;
      else if (s_wvalid & s_wready & s_wlast) bvalid <= 1'b1;
   end
   assign s_bvalid = bvalid;
   assign s_bid    = buf_id;
   assign s_bresp  = 2'b00;
   

   reg       rvalid;
   reg [7:0] rdata[3:0];
   always @(posedge aclk) begin
      if (areset)
        rvalid <= 1'b0;
      else if (s_rvalid & s_rready) begin
         rvalid <= 1'b0;
      end else if (new_axireq & ~buf_write & io_hit & s_idle) begin
         rvalid <= 1'b1;
         rdata[buf_addr[1:0]] <= reg_dat_o;
      end else if (s_data & byte_ready & ~rvalid) begin
         rvalid               <= (&nxt_addr[1:0]) | (~|tot_bytes);
         rdata[nxt_addr[1:0]] <= shift_reg_nxt[7:0];
      end
   end
   assign s_rvalid = rvalid;
   assign s_rdata  = {rdata[ 3], rdata[ 2], rdata[ 1], rdata[ 0]};
   assign s_rlast  = ~|buf_len;
   assign s_rid    = buf_id;
   assign s_rresp  = 2'b00;

   wire [1:0] sample_in = {2{s_data}}&(param_tFAST ? {sdi_i, sdo_i} : 
                                                      sample[1:0]  ); 

   assign shift_reg_nxt = sr_shift_inst_r?{shift_reg[30:0], 1'b0          }:
                          sr_shift_two_r ?{shift_reg[29:0], sample_in[1:0]}:
                                          {shift_reg[30:0], sample_in[1]  };

   always @(posedge aclk) begin
      if (s_pwrdown & ~ns_pwrdown) begin
         shift_reg[31:24] <= 8'hab;          
      end else if (~s_pdenter & ns_pdenter) begin
         shift_reg[31:24] <= 8'hb9;          
      end else if (~s_addr & ns_addr) begin
         shift_reg[31:24] <= param_dual_io   ? 8'hbb:
                             param_fast_read ? 8'h0b:
                                               8'h03;
         shift_reg[23: 0] <= nxt_addr[23:0];
      end else if (shift_en) begin
         shift_reg[31: 0] <= shift_reg_nxt;
      end
      if (sample_en) sample[1:0] <= {sdi_i, sdo_i};
   end
   assign serial_out = param_dual_io & dual_out ? shift_reg[31:30] :
                                           {1'b0, shift_reg[31]};

   wire [3:0] espr;
   reg [11:0] clkcnt;
   wire       clkena = ~|clkcnt & ~spi_pause;
   reg  [3:0] cswcnt;
   always @(posedge aclk)
     if (areset)
       clkcnt <= 12'h0;
     else if (~spi_pause) begin
        if ((|clkcnt) & (spi_run|s_csturn))
          clkcnt <= clkcnt - 11'h1;
        else
          case (espr)
            4'b0000: clkcnt <= 12'h0;   
            4'b0001: clkcnt <= 12'h1;   
            4'b0010: clkcnt <= 12'h7;   
            4'b0011: clkcnt <= 12'hf;   
            4'b0100: clkcnt <= 12'h3;   
            4'b0101: clkcnt <= 12'h1f;  
            4'b0110: clkcnt <= 12'h3f;  
            4'b0111: clkcnt <= 12'h7f;  
            4'b1000: clkcnt <= 12'hff;  
            4'b1001: clkcnt <= 12'h1ff; 
            4'b1010: clkcnt <= 12'h3ff; 
            4'b1011: clkcnt <= 12'h7ff; 
            default: clkcnt <= 12'h7ff;
          endcase
     end 
   
   always @(posedge aclk)
     if (areset|~s_csturn) cswcnt <= 4'b0;
     else if (clkena)      cswcnt <= cswcnt + 4'b1;

   wire [3:0] cswcnt_w = cswcnt | (4'b1110 << param_tCSH);

   always @(posedge aclk) begin
      if      (areset         )  spi_run <= 1'b0;
      else if (ns_addr|ns_data)  spi_run <= 1'b1;
      else if (ns_idle|ns_csturn)spi_run <= 1'b0;
      else if (ns_pdexit |ns_pdenter)spi_run <= 1'b1;
      else if (ns_startup|ns_pwrdown)spi_run <= 1'b0;

      if      (s_idle |s_csturn) adbit_cnt <= 6'b0;
      else if (s_addr & cyc_end) adbit_cnt <= adbit_cnt + 6'b1;

      if      (areset          ) sck <= 1'b0;
      else if (spi_run & clkena) sck <= ~sck;

      if      (areset | s_idle ) bit_cnt <= 3'h0;
      else if (s_data & cyc_end) bit_cnt <= bit_cnt + 3'b1;
      else if (s_pdenter&cyc_end)bit_cnt <= bit_cnt + 3'b1;
      else if (s_pdexit &cyc_end)bit_cnt <= bit_cnt + 3'b1;

   end

   assign byte_ready = s_data & cyc_end & (&({param_dual_io,2'b00}|bit_cnt[2:0]));

   always @(posedge aclk) begin
      if (areset | ~param_memory_en) begin
         nxt_addr <= 24'b0;
         cs_timer <= 16'b0;
         cs       <= 1'b0;
      end else begin
         nxt_addr <= new_axireq & s_idle    ? buf_addr_t :
                     byte_ready & ~spi_pause? (buf_wrap&(&nxt_addr[4:0]) & ~(tot_bytes == 8'b0)?
                                                   nxt_addr - 24'h1f    :
                                                   nxt_addr + 24'b1) :
                                              nxt_addr;
         cs_timer <= buf_busy|(~cs&~s_startup)|s_pdexit ? 16'b0 :
                     ~&cs_timer   ? cs_timer+16'b1 :
                                    cs_timer ;
         cs <= ns_addr                             ? 1'b1 :
               ns_csturn | (~buf_busy & (&cs_timer)) ? 1'b0 :
               ~param_burst_en & ns_idle           ? 1'b0 :
               write_valid & (buf_addr[3:0]==4'h2) ? 1'b0 :
               ns_pdenter | ns_pdexit              ? 1'b1 :
               ns_pwrdown | ns_startup             ? 1'b0 :
                                                     cs;
      end
   end

   assign cyc_end       = spi_run & sck & clkena;

   assign shift_en      = spi_run & sck & clkena;
   assign sample_en     = spi_run &~sck & clkena & s_data;

   assign spi_pause     = rvalid;

   assign sr_shift_inst = s_addr & (adbit_cnt < 6'd8);
   assign sr_shift_two  =(s_addr & (adbit_cnt >=6'd8) | s_data) & param_dual_io;
   always @(posedge aclk) begin
      sr_shift_inst_r <= areset                    ? 1'b0 :
                         cyc_end|(~s_addr&ns_addr) ? ns_addr & (adbit_cnt < 6'd7) :
                                                     sr_shift_inst_r;
      sr_shift_two_r  <= areset                    ? 1'b0 :
                         cyc_end|(~s_data&ns_data) ? (s_addr & (adbit_cnt >=6'd7) | ns_data) & param_dual_io :
                                                     sr_shift_two_r;
   end

   assign sr_shift_one  = 1'bz; 
   wire addr_done;
   assign addr_done     = param_dual_io   ? adbit_cnt == 6'd23 :
                          param_fast_read ? adbit_cnt == 6'd39 :
                                            adbit_cnt == 6'd31 ;
   assign dual_out      = param_dual_io &
                          (adbit_cnt >= 6'd8 && adbit_cnt < 6'd22);
   reg    dual_in_r;
   assign dual_in       = param_dual_io &
                          (adbit_cnt >= 6'd22 | s_data | dual_in_r);
   always @(posedge aclk) begin
      dual_in_r <= areset            ? 1'b0 :
                   s_csturn&cswcnt[0]? 1'b0 :
                   ~cs               ? 1'b0 :
                   dual_in           ? 1'b1 : dual_in_r;
   end


   always @(posedge aclk) begin
      pdreq_r <= power_down_req;
   end
   wire   go_power_down  = pdreq_r & ~buf_busy;
   assign power_down_ack = s_pwrdown | s_pdexit | s_startup;

   always @(posedge aclk) begin
      rd_state <= areset ? S_PWRDOWN : rd_state_nxt;
   end

   always @(*) begin
      rd_state_nxt = rd_state;
      case (rd_state)
        S_IDLE  :if (new_axireq & ~buf_write) begin
                    rd_state_nxt = io_hit       ? S_IOREAD:
                                   spibus_busy  ? S_WAITBUS:
                                   burst_cont   ? S_DATA  :
                                   S_CSTURN;
                 end else if (go_power_down) begin
                    rd_state_nxt = cs ? S_CSTURN : 
                                        S_PDENTER;
                 end
        S_IOREAD: rd_state_nxt = S_IDLE;
        S_CSTURN: rd_state_nxt = clkena & (&cswcnt_w)? (go_power_down? S_PDENTER:S_ADDR):
                                                       S_CSTURN;
        S_ADDR  : rd_state_nxt = clkena & sck &
                                 addr_done ? S_DATA : S_ADDR;
        S_DATA  : rd_state_nxt = byte_ready & ~spi_pause & ~|tot_bytes     ? S_IDLE:
                                 byte_ready & ~spi_pause & buf_wrap 
                                                         & (&nxt_addr[4:0])? S_CSTURN:
                                 byte_ready & ~spi_pause & ~param_burst_en ? S_CSTURN:
                                                                             S_DATA;
        S_WAITBUS:rd_state_nxt = spibus_busy ? S_WAITBUS : S_ADDR;

        S_PWRDOWN:rd_state_nxt = go_power_down  ? S_PWRDOWN : S_PDEXIT;
        S_PDEXIT :rd_state_nxt = cyc_end & (&bit_cnt[2:0]) ? S_STARTUP : S_PDEXIT;
        S_PDENTER:rd_state_nxt = cyc_end & (&bit_cnt[2:0]) ? S_PWRDOWN : S_PDENTER;
        S_STARTUP:rd_state_nxt = &(cs_timer[10:0]|{{5{fast_startup}}, 6'b0}) ? S_IDLE :
                                                  S_STARTUP;
      endcase 
   end


   wire       ss_sck, ss_mosi, ss_miso;
   wire [7:0] param, softcs, param2;
   wire       sspi_write = buf_write | second_write;

   simple_spi_top simple_spi(
                             .clk_i  (aclk          ), 
                             .rst_i  (aresetn       ), 
                             .cyc_i  (buf_busy      ), 
                             .stb_i  (reg_acc       ), 
                             .adr_i  (buf_addr[3:0] ), 
                             .we_i   (sspi_write    ), 
                             .dat_i  (reg_dat_i     ), 
                             .dat_o  (reg_dat_o     ), 
                             .ack_o  (reg_ack       ), 
                             .inta_o (inta_o        ), 

                             .sck_o  (ss_sck        ), 
                             .mosi_o (ss_mosi       ), 
                             .miso_i (ss_miso       ), 

                             .param  (param         ),
                             .param2 (param2        ),
                             .softcs (softcs        ),
                             .busy   (spibus_busy   )
                             );
   assign     ss_miso = sdi_i;

   assign     reg_dat_i = second_write        ? s_wdata[ 31: 24] :
                          buf_addr[1:0]==2'h0 ? s_wdata[  7:  0] :
                          buf_addr[1:0]==2'h1 ? s_wdata[ 15:  8] :
                          buf_addr[1:0]==2'h2 ? s_wdata[ 23: 16] :
                                                s_wdata[ 31: 24] ;

   assign     param_memory_en = param[0];
   assign     param_burst_en  = param[1];
   assign     param_fast_read = param[2];
   assign     param_dual_io   = param[3];
   assign     espr            = param[7:4];

   assign     param_tCSH      = param2[1:0];
   assign     param_tFAST     = param2[2];
   assign     param_scs       = param2[3];  

   assign     csn_en[0] = param_memory_en? 1'b0: ~softcs[0];
   assign     csn_o [0] = param_memory_en? ~cs :  softcs[4];

   assign     csn_en[3:1] =~softcs[3:1];
   assign     csn_o [3:1] = softcs[7:5]|{3{cs|(~spibus_busy & param_scs)}}; 

   assign     sdi_en = ~spibus_busy&param_memory_en? ~dual_out     : 1'b1;
   assign     sdi_o  = ~spibus_busy&param_memory_en? serial_out[1] : 1'b0;

   assign     sdo_en = ~spibus_busy&param_memory_en? dual_in       : 1'b0;
   assign     sdo_o  = ~spibus_busy&param_memory_en? serial_out[0] | s_data
                                                                   : ss_mosi;

   assign     sck_o  = ~spibus_busy&param_memory_en? sck           : ss_sck;

endmodule
module spi_flash_ctrl(
    input           aclk,
    input           aresetn,
    input [15:0]    spi_addr,
    input           power_down_req,
    output          power_down_ack,
    input           fast_startup,

    input [3:0]     s_awlen,
    input [3:0]     s_awcache,
    input [3:0]     s_awid,
    input [31:0]    s_awaddr,
    input [2:0]     s_awsize,
    input [2:0]     s_awprot,
    input [1:0]     s_awburst,
    input [1:0]     s_awlock,
    input           s_awvalid,
    output          s_awready,

    input [3:0]     s_wid,
    input [31:0]    s_wdata,
    input [3:0]     s_wstrb,
    input           s_wlast,
    input           s_wvalid,
    output          s_wready,

    output [3:0]    s_bid,
    output [1:0]    s_bresp,
    output          s_bvalid,
    input           s_bready,

    input [3:0]     s_arlen,  
    input [3:0]     s_arcache,
    input [3:0]     s_arid,
    input [31:0]    s_araddr,
    input [2:0]     s_arsize,
    input [2:0]     s_arprot,
    input [1:0]     s_arburst,
    input [1:0]     s_arlock,
    input           s_arvalid,
    output          s_arready,

    output [3:0]    s_rid,
    output [31:0]   s_rdata,
    output [1:0]    s_rresp,
    output          s_rlast,
    output          s_rvalid,
    input           s_rready,

    output [3:0]    csn_o,
    output [3:0]    csn_en, 
    output          sck_o,
    input           sdo_i,
    output          sdo_o,
    output          sdo_en, 
    input           sdi_i,
    output          sdi_o,
    output          sdi_en,
    output          inta_o
);


   wire             areset = ~aresetn;

   wire             param_memory_en;
   wire             param_burst_en;
   wire             param_fast_read;
   wire             param_dual_io;
   wire [1:0]       param_tCSH;
   wire             param_tFAST;

   reg [9:0]        rd_state;
   reg [9:0]        rd_state_nxt;

   parameter        S_IDLE   = 10'b0000000001;
   parameter        S_IOREAD = 10'b0000000010;
   parameter        S_CSTURN = 10'b0000000100;
   parameter        S_ADDR   = 10'b0000001000;
   parameter        S_DATA   = 10'b0000010000;
   parameter        S_WAITBUS= 10'b0000100000;
   parameter        S_PDENTER= 10'b0001000000;
   parameter        S_PDEXIT = 10'b0010000000;
   parameter        S_STARTUP= 10'b0100000000;
   parameter        S_PWRDOWN= 10'b1000000000;

   wire             s_idle   = rd_state[0];
   wire             s_ioread = rd_state[1];
   wire             s_csturn = rd_state[2];
   wire             s_addr   = rd_state[3];
   wire             s_data   = rd_state[4];
   wire             s_waitbus= rd_state[5];
   wire             s_pdenter= rd_state[6];
   wire             s_pdexit = rd_state[7];
   wire             s_startup= rd_state[8];
   wire             s_pwrdown= rd_state[9];

   wire             ns_idle   = rd_state_nxt[0];
   wire             ns_ioread = rd_state_nxt[1];
   wire             ns_csturn = rd_state_nxt[2];
   wire             ns_addr   = rd_state_nxt[3];
   wire             ns_data   = rd_state_nxt[4];
   wire             ns_waitbus= rd_state_nxt[5];
   wire             ns_pdenter= rd_state_nxt[6];
   wire             ns_pdexit = rd_state_nxt[7];
   wire             ns_startup= rd_state_nxt[8];
   wire             ns_pwrdown= rd_state_nxt[9];

   reg              pdreq_r;

   reg [15:0]       cs_timer;
   reg              cs;
   reg [23:0]       nxt_addr;

   wire             write_valid;
   wire             reg_acc = s_ioread | write_valid;
   wire             reg_ack;
   wire [7:0]       reg_dat_i, reg_dat_o;
   wire [7:0]       param_o;

   reg [31:0]       shift_reg;
   reg [ 1:0]       sample;
   wire [31:0]      shift_reg_nxt;
   wire             sr_shift_inst;
   wire             sr_shift_one;
   wire             sr_shift_two;
   reg              sr_shift_inst_r;
   reg              sr_shift_two_r;
   wire             sample_en;
   wire             shift_en;
   wire             dual_out;
   wire             dual_in;
   wire [1:0]       serial_out;
   wire             cyc_end;          


   reg [2:0]        bit_cnt;          
   wire             spi_pause;        
   wire             spibus_busy;      

   reg [5:0]        adbit_cnt;        
   reg              spi_run;
   reg              sck;

   reg              buf_busy;
   reg [31:0]       buf_addr;
   reg [ 3:0]       buf_len;
   reg [ 2:0]       buf_size;
   reg [ 3:0]       buf_id;
   reg              buf_write;
   reg              buf_wrap;

   assign           s_arready = s_idle & ~pdreq_r & ~buf_busy & ~s_awvalid;
   assign           s_awready = s_idle & ~pdreq_r & ~buf_busy;

   reg              buf_busy_d;
   wire             new_axireq = ~buf_busy_d & buf_busy;
   wire             io_hit =(buf_addr[31:4] == {spi_addr, 12'b0}) &
                            (buf_len  == 4'b0);

   wire [63:0]      buf_addr_t = (buf_addr[31:20]==12'h1fc)? 
                                    {12'h0, buf_addr[19:0]}: 
                                    { 8'h0, buf_addr[23:0]}; 

   wire             burst_cont   = param_burst_en & cs &
                                  (buf_addr_t[23:0] == nxt_addr[23:0]);

   wire             burst_switch = param_burst_en & cs &
                                  (buf_addr_t[23:0] != nxt_addr[23:0]);

   reg [7:0]        tot_bytes;
   wire             byte_ready;

   always @(posedge aclk) begin
      if (areset) begin
         buf_busy  <= 1'b0;
         buf_write <= 1'b0;
         tot_bytes <= 8'b0;
      end else begin
         if ((s_arvalid|s_awvalid)&~buf_busy&s_idle&~pdreq_r) begin
            buf_busy <= 1'b1;
            buf_addr <= s_awvalid ? s_awaddr : s_araddr;
            buf_size <= s_awvalid ? s_awsize : s_arsize;
            buf_len  <= s_awvalid ? s_awlen  : s_arlen;
            buf_id   <= s_awvalid ? s_awid   : s_arid;
            buf_write<= s_awvalid;
            buf_wrap <= s_arvalid & (s_arburst==2'b10) & 
                        (|s_araddr[4:2]) & (|s_arlen);
            tot_bytes<= {8{s_arvalid&~s_awvalid}}&
                        (({4'b0,s_arlen} << s_arsize)|
                         ((8'b1<<s_arsize)-8'b1));
         end else begin
            if (s_bvalid & s_bready | s_rvalid & s_rready & s_rlast)
              buf_busy <= 1'b0;
            if (s_rvalid & s_rready)
              buf_len  <= buf_len - 4'b1;
            if (s_wvalid & s_wready & s_wlast)
              buf_write <= 1'b0;
            if (byte_ready & ~s_rvalid) begin
              tot_bytes <= tot_bytes - 8'b1;
            end
         end
      end
      buf_busy_d <= buf_busy;
   end

   reg    second_write; 
   always @(posedge aclk) begin
      if (areset) second_write <= 1'b0;
      else second_write <= (s_wvalid & s_wready & io_hit & (buf_size==3'b1) & (buf_addr[2:0]==3'b10));
   end

   assign s_wready = buf_busy & buf_write & s_idle;
   assign write_valid = s_wvalid & s_wready & io_hit &
                        ((buf_size==3'b0) | (buf_size==3'b1 && buf_addr[2:0]==3'b10)) |
                        second_write;

   reg  bvalid;
   always @(posedge aclk) begin
      if      (areset                       ) bvalid <= 1'b0;
      else if (s_bvalid & s_bready          ) bvalid <= 1'b0;
      else if (s_wvalid & s_wready & s_wlast) bvalid <= 1'b1;
   end
   assign s_bvalid = bvalid;
   assign s_bid    = buf_id;
   assign s_bresp  = 2'b00;
   

   reg       rvalid;
   reg [7:0] rdata[3:0];
   always @(posedge aclk) begin
      if (areset)
        rvalid <= 1'b0;
      else if (s_rvalid & s_rready) begin
         rvalid <= 1'b0;
      end else if (new_axireq & ~buf_write & io_hit & s_idle) begin
         rvalid <= 1'b1;
         rdata[buf_addr[1:0]] <= reg_dat_o;
      end else if (s_data & byte_ready & ~rvalid) begin
         rvalid               <= (&nxt_addr[1:0]) | (~|tot_bytes);
         rdata[nxt_addr[1:0]] <= shift_reg_nxt[7:0];
      end
   end
   assign s_rvalid = rvalid;
   assign s_rdata  = {rdata[ 3], rdata[ 2], rdata[ 1], rdata[ 0]};
   assign s_rlast  = ~|buf_len;
   assign s_rid    = buf_id;
   assign s_rresp  = 2'b00;

   wire [1:0] sample_in = {2{s_data}}&(param_tFAST ? {sdi_i, sdo_i} : 
                                                      sample[1:0]  ); 

   assign shift_reg_nxt = sr_shift_inst_r?{shift_reg[30:0], 1'b0          }:
                          sr_shift_two_r ?{shift_reg[29:0], sample_in[1:0]}:
                                          {shift_reg[30:0], sample_in[1]  };

   always @(posedge aclk) begin
      if (s_pwrdown & ~ns_pwrdown) begin
         shift_reg[31:24] <= 8'hab;          
      end else if (~s_pdenter & ns_pdenter) begin
         shift_reg[31:24] <= 8'hb9;          
      end else if (~s_addr & ns_addr) begin
         shift_reg[31:24] <= param_dual_io   ? 8'hbb:
                             param_fast_read ? 8'h0b:
                                               8'h03;
         shift_reg[23: 0] <= nxt_addr[23:0];
      end else if (shift_en) begin
         shift_reg[31: 0] <= shift_reg_nxt;
      end
      if (sample_en) sample[1:0] <= {sdi_i, sdo_i};
   end
   assign serial_out = param_dual_io & dual_out ? shift_reg[31:30] :
                                           {1'b0, shift_reg[31]};

   wire [3:0] espr;
   reg [11:0] clkcnt;
   wire       clkena = ~|clkcnt & ~spi_pause;
   reg  [3:0] cswcnt;
   always @(posedge aclk)
     if (areset)
       clkcnt <= 12'h0;
     else if (~spi_pause) begin
        if ((|clkcnt) & (spi_run|s_csturn))
          clkcnt <= clkcnt - 11'h1;
        else
          case (espr)
            4'b0000: clkcnt <= 12'h0;   
            4'b0001: clkcnt <= 12'h1;   
            4'b0010: clkcnt <= 12'h7;   
            4'b0011: clkcnt <= 12'hf;   
            4'b0100: clkcnt <= 12'h3;   
            4'b0101: clkcnt <= 12'h1f;  
            4'b0110: clkcnt <= 12'h3f;  
            4'b0111: clkcnt <= 12'h7f;  
            4'b1000: clkcnt <= 12'hff;  
            4'b1001: clkcnt <= 12'h1ff; 
            4'b1010: clkcnt <= 12'h3ff; 
            4'b1011: clkcnt <= 12'h7ff; 
            default: clkcnt <= 12'h7ff;
          endcase
     end 
   
   always @(posedge aclk)
     if (areset|~s_csturn) cswcnt <= 4'b0;
     else if (clkena)      cswcnt <= cswcnt + 4'b1;

   wire [3:0] cswcnt_w = cswcnt | (4'b1110 << param_tCSH);

   always @(posedge aclk) begin
      if      (areset         )  spi_run <= 1'b0;
      else if (ns_addr|ns_data)  spi_run <= 1'b1;
      else if (ns_idle|ns_csturn)spi_run <= 1'b0;
      else if (ns_pdexit |ns_pdenter)spi_run <= 1'b1;
      else if (ns_startup|ns_pwrdown)spi_run <= 1'b0;

      if      (s_idle |s_csturn) adbit_cnt <= 6'b0;
      else if (s_addr & cyc_end) adbit_cnt <= adbit_cnt + 6'b1;

      if      (areset          ) sck <= 1'b0;
      else if (spi_run & clkena) sck <= ~sck;

      if      (areset | s_idle ) bit_cnt <= 3'h0;
      else if (s_data & cyc_end) bit_cnt <= bit_cnt + 3'b1;
      else if (s_pdenter&cyc_end)bit_cnt <= bit_cnt + 3'b1;
      else if (s_pdexit &cyc_end)bit_cnt <= bit_cnt + 3'b1;

   end

   assign byte_ready = s_data & cyc_end & (&({param_dual_io,2'b00}|bit_cnt[2:0]));

   always @(posedge aclk) begin
      if (areset | ~param_memory_en) begin
         nxt_addr <= 24'b0;
         cs_timer <= 16'b0;
         cs       <= 1'b0;
      end else begin
         nxt_addr <= new_axireq & s_idle    ? buf_addr_t :
                     byte_ready & ~spi_pause? (buf_wrap&(&nxt_addr[4:0]) & ~(tot_bytes == 8'b0)?
                                                   nxt_addr - 24'h1f    :
                                                   nxt_addr + 24'b1) :
                                              nxt_addr;
         cs_timer <= buf_busy|(~cs&~s_startup)|s_pdexit ? 16'b0 :
                     ~&cs_timer   ? cs_timer+16'b1 :
                                    cs_timer ;
         cs <= ns_addr                             ? 1'b1 :
               ns_csturn | (~buf_busy & (&cs_timer)) ? 1'b0 :
               ~param_burst_en & ns_idle           ? 1'b0 :
               write_valid & (buf_addr[3:0]==4'h2) ? 1'b0 :
               ns_pdenter | ns_pdexit              ? 1'b1 :
               ns_pwrdown | ns_startup             ? 1'b0 :
                                                     cs;
      end
   end

   assign cyc_end       = spi_run & sck & clkena;

   assign shift_en      = spi_run & sck & clkena;
   assign sample_en     = spi_run &~sck & clkena & s_data;

   assign spi_pause     = rvalid;

   assign sr_shift_inst = s_addr & (adbit_cnt < 6'd8);
   assign sr_shift_two  =(s_addr & (adbit_cnt >=6'd8) | s_data) & param_dual_io;
   always @(posedge aclk) begin
      sr_shift_inst_r <= areset                    ? 1'b0 :
                         cyc_end|(~s_addr&ns_addr) ? ns_addr & (adbit_cnt < 6'd7) :
                                                     sr_shift_inst_r;
      sr_shift_two_r  <= areset                    ? 1'b0 :
                         cyc_end|(~s_data&ns_data) ? (s_addr & (adbit_cnt >=6'd7) | ns_data) & param_dual_io :
                                                     sr_shift_two_r;
   end

   assign sr_shift_one  = 1'bz; 
   wire addr_done;
   assign addr_done     = param_dual_io   ? adbit_cnt == 6'd23 :
                          param_fast_read ? adbit_cnt == 6'd39 :
                                            adbit_cnt == 6'd31 ;
   assign dual_out      = param_dual_io &
                          (adbit_cnt >= 6'd8 && adbit_cnt < 6'd22);
   reg    dual_in_r;
   assign dual_in       = param_dual_io &
                          (adbit_cnt >= 6'd22 | s_data | dual_in_r);
   always @(posedge aclk) begin
      dual_in_r <= areset            ? 1'b0 :
                   s_csturn&cswcnt[0]? 1'b0 :
                   ~cs               ? 1'b0 :
                   dual_in           ? 1'b1 : dual_in_r;
   end


   always @(posedge aclk) begin
      pdreq_r <= power_down_req;
   end
   wire   go_power_down  = pdreq_r & ~buf_busy;
   assign power_down_ack = s_pwrdown | s_pdexit | s_startup;

   always @(posedge aclk) begin
      rd_state <= areset ? S_PWRDOWN : rd_state_nxt;
   end

   always @(*) begin
      rd_state_nxt = rd_state;
      case (rd_state)
        S_IDLE  :if (new_axireq & ~buf_write) begin
                    rd_state_nxt = io_hit       ? S_IOREAD:
                                   spibus_busy  ? S_WAITBUS:
                                   burst_cont   ? S_DATA  :
                                   S_CSTURN;
                 end else if (go_power_down) begin
                    rd_state_nxt = cs ? S_CSTURN : 
                                        S_PDENTER;
                 end
        S_IOREAD: rd_state_nxt = S_IDLE;
        S_CSTURN: rd_state_nxt = clkena & (&cswcnt_w)? (go_power_down? S_PDENTER:S_ADDR):
                                                       S_CSTURN;
        S_ADDR  : rd_state_nxt = clkena & sck &
                                 addr_done ? S_DATA : S_ADDR;
        S_DATA  : rd_state_nxt = byte_ready & ~spi_pause & ~|tot_bytes     ? S_IDLE:
                                 byte_ready & ~spi_pause & buf_wrap 
                                                         & (&nxt_addr[4:0])? S_CSTURN:
                                 byte_ready & ~spi_pause & ~param_burst_en ? S_CSTURN:
                                                                             S_DATA;
        S_WAITBUS:rd_state_nxt = spibus_busy ? S_WAITBUS : S_ADDR;

        S_PWRDOWN:rd_state_nxt = go_power_down  ? S_PWRDOWN : S_PDEXIT;
        S_PDEXIT :rd_state_nxt = cyc_end & (&bit_cnt[2:0]) ? S_STARTUP : S_PDEXIT;
        S_PDENTER:rd_state_nxt = cyc_end & (&bit_cnt[2:0]) ? S_PWRDOWN : S_PDENTER;
        S_STARTUP:rd_state_nxt = &(cs_timer[10:0]|{{5{fast_startup}}, 6'b0}) ? S_IDLE :
                                                  S_STARTUP;
      endcase 
   end


   wire       ss_sck, ss_mosi, ss_miso;
   wire [7:0] param, softcs, param2;
   wire       sspi_write = buf_write | second_write;

   simple_spi_top simple_spi(
                             .clk_i  (aclk          ), 
                             .rst_i  (aresetn       ), 
                             .cyc_i  (buf_busy      ), 
                             .stb_i  (reg_acc       ), 
                             .adr_i  (buf_addr[3:0] ), 
                             .we_i   (sspi_write    ), 
                             .dat_i  (reg_dat_i     ), 
                             .dat_o  (reg_dat_o     ), 
                             .ack_o  (reg_ack       ), 
                             .inta_o (inta_o        ), 

                             .sck_o  (ss_sck        ), 
                             .mosi_o (ss_mosi       ), 
                             .miso_i (ss_miso       ), 

                             .param  (param         ),
                             .param2 (param2        ),
                             .softcs (softcs        ),
                             .busy   (spibus_busy   )
                             );
   assign     ss_miso = sdi_i;

   assign     reg_dat_i = second_write        ? s_wdata[ 31: 24] :
                          buf_addr[1:0]==2'h0 ? s_wdata[  7:  0] :
                          buf_addr[1:0]==2'h1 ? s_wdata[ 15:  8] :
                          buf_addr[1:0]==2'h2 ? s_wdata[ 23: 16] :
                                                s_wdata[ 31: 24] ;

   assign     param_memory_en = param[0];
   assign     param_burst_en  = param[1];
   assign     param_fast_read = param[2];
   assign     param_dual_io   = param[3];
   assign     espr            = param[7:4];

   assign     param_tCSH      = param2[1:0];
   assign     param_tFAST     = param2[2];
   assign     param_scs       = param2[3];  

   assign     csn_en[0] = param_memory_en? 1'b0: ~softcs[0];
   assign     csn_o [0] = param_memory_en? ~cs :  softcs[4];

   assign     csn_en[3:1] =~softcs[3:1];
   assign     csn_o [3:1] = softcs[7:5]|{3{cs|(~spibus_busy & param_scs)}}; 

   assign     sdi_en = ~spibus_busy&param_memory_en? ~dual_out     : 1'b1;
   assign     sdi_o  = ~spibus_busy&param_memory_en? serial_out[1] : 1'b0;

   assign     sdo_en = ~spibus_busy&param_memory_en? dual_in       : 1'b0;
   assign     sdo_o  = ~spibus_busy&param_memory_en? serial_out[0] | s_data
                                                                   : ss_mosi;

   assign     sck_o  = ~spibus_busy&param_memory_en? sck           : ss_sck;

endmodule