| ////////////////////////////////////////////////////////////////////////////// |
| // SPDX-FileCopyrightText: 2021 , Dinesh Annayya |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| // SPDX-License-Identifier: Apache-2.0 |
| // SPDX-FileContributor: Created by Dinesh Annayya <dinesha@opencores.org> |
| // |
| ////////////////////////////////////////////////////////////////////// |
| //// //// |
| //// SPI WishBone Register I/F Module //// |
| //// //// |
| //// This file is part of the YIFive cores project //// |
| //// https://github.com/dineshannayya/yifive_r0.git //// |
| //// http://www.opencores.org/cores/yifive/ //// |
| //// //// |
| //// Description //// |
| //// SPI WishBone I/F module //// |
| //// This block support following functionality //// |
| //// 1. Direct SPI Read memory support for address rang //// |
| //// 0x0000 to 0x0FFF_FFFF - Use full for Instruction //// |
| //// Data Memory fetch //// |
| //// 2. SPI Local Register Access //// |
| //// 3. Indirect register way to access SPI Memory //// |
| //// //// |
| //// To Do: //// |
| //// nothing //// |
| //// //// |
| //// Author(s): //// |
| //// - Dinesh Annayya, dinesha@opencores.org //// |
| //// //// |
| //// Revision : //// |
| //// V.0 - June 8, 2021 //// |
| //// //// |
| ////////////////////////////////////////////////////////////////////// |
| //// //// |
| //// Copyright (C) 2000 Authors and OPENCORES.ORG //// |
| //// //// |
| //// This source file may be used and distributed without //// |
| //// restriction provided that this copyright statement is not //// |
| //// removed from the file and that any derivative work contains //// |
| //// the original copyright notice and the associated disclaimer. //// |
| //// //// |
| //// This source file is free software; you can redistribute it //// |
| //// and/or modify it under the terms of the GNU Lesser General //// |
| //// Public License as published by the Free Software Foundation; //// |
| //// either version 2.1 of the License, or (at your option) any //// |
| //// later version. //// |
| //// //// |
| //// This source is distributed in the hope that it will be //// |
| //// useful, but WITHOUT ANY WARRANTY; without even the implied //// |
| //// warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR //// |
| //// PURPOSE. See the GNU Lesser General Public License for more //// |
| //// details. //// |
| //// //// |
| //// You should have received a copy of the GNU Lesser General //// |
| //// Public License along with this source; if not, download it //// |
| //// from http://www.opencores.org/lgpl.shtml //// |
| //// //// |
| ////////////////////////////////////////////////////////////////////// |
| |
| |
| module spim_regs #( parameter WB_WIDTH = 32) ( |
| input logic mclk, |
| input logic rst_n, |
| |
| input logic wbd_stb_i, // strobe/request |
| input logic [WB_WIDTH-1:0] wbd_adr_i, // address |
| input logic wbd_we_i, // write |
| input logic [WB_WIDTH-1:0] wbd_dat_i, // data output |
| input logic [3:0] wbd_sel_i, // byte enable |
| output logic [WB_WIDTH-1:0] wbd_dat_o, // data input |
| output logic wbd_ack_o, // acknowlegement |
| output logic wbd_err_o, // error |
| |
| output logic [7:0] spi_clk_div, |
| input logic [8:0] spi_status, |
| |
| // Towards SPI TX/RX FSM |
| |
| |
| output logic spi_req, |
| output logic [31:0] spi_addr, |
| output logic [5:0] spi_addr_len, |
| output logic [7:0] spi_cmd, |
| output logic [5:0] spi_cmd_len, |
| output logic [7:0] spi_mode_cmd, |
| output logic spi_mode_cmd_enb, |
| output logic [3:0] spi_csreg, |
| output logic [15:0] spi_data_len, |
| output logic [15:0] spi_dummy_rd_len, |
| output logic [15:0] spi_dummy_wr_len, |
| output logic spi_swrst, |
| output logic spi_rd, |
| output logic spi_wr, |
| output logic spi_qrd, |
| output logic spi_qwr, |
| output logic [31:0] spi_wdata, |
| input logic [31:0] spi_rdata, |
| input logic spi_ack |
| |
| ); |
| |
| //---------------------------- |
| // Register Decoding |
| // --------------------------- |
| parameter REG_CTRL = 4'b0000; |
| parameter REG_CLKDIV = 4'b0001; |
| parameter REG_SPICMD = 4'b0010; |
| parameter REG_SPIADR = 4'b0011; |
| parameter REG_SPILEN = 4'b0100; |
| parameter REG_SPIDUM = 4'b0101; |
| parameter REG_SPIWDATA = 4'b0110; |
| parameter REG_SPIRDATA = 4'b0111; |
| parameter REG_STATUS = 4'b1000; |
| |
| // Init FSM |
| parameter SPI_INIT_IDLE = 3'b000; |
| parameter SPI_INIT_CMD_WAIT = 3'b001; |
| parameter SPI_INIT_WREN_CMD = 3'b010; |
| parameter SPI_INIT_WREN_WAIT = 3'b011; |
| parameter SPI_INIT_WRR_CMD = 3'b100; |
| parameter SPI_INIT_WRR_WAIT = 3'b101; |
| |
| //--------------------------------------------------------- |
| // Variable declartion |
| // ------------------------------------------------------- |
| logic spi_init_done ; |
| logic [2:0] spi_init_state ; |
| logic spim_mem_req ; |
| logic spim_reg_req ; |
| |
| |
| logic spim_wb_req ; |
| logic spim_wb_req_l ; |
| logic [WB_WIDTH-1:0] spim_wb_wdata ; |
| logic [WB_WIDTH-1:0] spim_wb_addr ; |
| logic spim_wb_ack ; |
| logic spim_wb_we ; |
| logic [3:0] spim_wb_be ; |
| logic [WB_WIDTH-1:0] spim_reg_rdata ; |
| logic [WB_WIDTH-1:0] spim_wb_rdata ; |
| logic [WB_WIDTH-1:0] reg_rdata ; |
| |
| // Control Signal Generated from Reg to SPI Access |
| logic reg2spi_req; |
| logic [31:0] reg2spi_addr; |
| logic [5:0] reg2spi_addr_len; |
| logic [31:0] reg2spi_cmd; |
| logic [5:0] reg2spi_cmd_len; |
| logic [3:0] reg2spi_csreg; |
| logic [15:0] reg2spi_data_len; |
| logic reg2spi_mode_enb; // mode enable |
| logic [7:0] reg2spi_mode; // mode |
| logic [15:0] reg2spi_dummy_rd_len; |
| logic [15:0] reg2spi_dummy_wr_len; |
| logic reg2spi_swrst; |
| logic reg2spi_rd; |
| logic reg2spi_wr; |
| logic reg2spi_qrd; |
| logic reg2spi_qwr; |
| logic [31:0] reg2spi_wdata; |
| //------------------------------------------------------------------ |
| // Priority given to mem2spi request over Reg2Spi |
| |
| assign spi_req = (spim_mem_req && !spim_wb_we) ? 1'b1 : reg2spi_req; |
| assign spi_addr = (spim_mem_req && !spim_wb_we) ? {spim_wb_addr[23:0],8'h0} : reg2spi_addr; |
| assign spi_addr_len = (spim_mem_req && !spim_wb_we) ? 24 : reg2spi_addr_len; |
| assign spi_cmd = (spim_mem_req && !spim_wb_we) ? 8'hEB : reg2spi_cmd; |
| assign spi_cmd_len = (spim_mem_req && !spim_wb_we) ? 8 : reg2spi_cmd_len; |
| assign spi_mode_cmd = (spim_mem_req && !spim_wb_we) ? 8'h00 : reg2spi_mode; |
| assign spi_mode_cmd_enb = (spim_mem_req && !spim_wb_we) ? 1 : reg2spi_mode_enb; |
| assign spi_csreg = (spim_mem_req && !spim_wb_we) ? '1 : reg2spi_csreg; |
| assign spi_data_len = (spim_mem_req && !spim_wb_we) ? 'h20 : reg2spi_data_len; |
| assign spi_dummy_rd_len = (spim_mem_req && !spim_wb_we) ? 'h20 : reg2spi_dummy_rd_len; |
| assign spi_dummy_wr_len = (spim_mem_req && !spim_wb_we) ? 0 : reg2spi_dummy_wr_len; |
| assign spi_swrst = (spim_mem_req && !spim_wb_we) ? 0 : reg2spi_swrst; |
| assign spi_rd = (spim_mem_req && !spim_wb_we) ? 0 : reg2spi_rd; |
| assign spi_wr = (spim_mem_req && !spim_wb_we) ? 0 : reg2spi_wr; |
| assign spi_qrd = (spim_mem_req && !spim_wb_we) ? 1 : reg2spi_qrd; |
| assign spi_qwr = (spim_mem_req && !spim_wb_we) ? 0 : reg2spi_qwr; |
| assign spi_wdata = (spim_mem_req && !spim_wb_we) ? 0 : reg2spi_wdata; |
| |
| |
| |
| |
| //--------------------------------------------------------------- |
| // Address Decoding |
| // 0x0000_0000 - 0x0FFF_FFFF - SPI FLASH MEMORY ACCESS - 256MB |
| // 0x1000_0000 - - SPI Register Access |
| // -------------------------------------------------------------- |
| |
| assign spim_mem_req = ((spim_wb_req) && spim_wb_addr[31:28] == 4'b0000); |
| assign spim_reg_req = ((spim_wb_req) && spim_wb_addr[31:28] == 4'b0001); |
| |
| |
| assign wbd_dat_o = spim_wb_rdata; |
| assign wbd_ack_o = spim_wb_ack; |
| assign wbd_err_o = 1'b0; |
| |
| // To reduce the load/Timing Wishbone I/F, all the variable are registered |
| always_ff @(negedge rst_n or posedge mclk) begin |
| if ( rst_n == 1'b0 ) begin |
| spim_wb_req <= '0; |
| spim_wb_req_l <= '0; |
| spim_wb_wdata <= '0; |
| spim_wb_rdata <= '0; |
| spim_wb_addr <= '0; |
| spim_wb_be <= '0; |
| spim_wb_we <= '0; |
| spim_wb_ack <= '0; |
| end else begin |
| if(spi_init_done) begin // Wait for internal SPI Init Done |
| spim_wb_req <= wbd_stb_i && (spi_ack == 0) && (spim_wb_ack==0); |
| spim_wb_req_l <= spim_wb_req; |
| spim_wb_wdata <= wbd_dat_i; |
| spim_wb_addr <= wbd_adr_i; |
| spim_wb_be <= wbd_sel_i; |
| spim_wb_we <= wbd_we_i; |
| |
| |
| // If there is Reg2Spi read Access, Register the Read Data |
| if(reg2spi_req && (reg2spi_rd || reg2spi_qrd ) && spi_ack) |
| spim_reg_rdata <= spi_rdata; |
| |
| if(!spim_wb_we && spim_wb_req && spi_ack) |
| spim_wb_rdata <= spi_rdata; |
| else if (spim_reg_req) |
| spim_wb_rdata <= reg_rdata; |
| |
| // For safer design, we have generated ack after 2 cycle latter to |
| // cross-check current request is towards SPI or not |
| spim_wb_ack <= (spi_req && spim_wb_req) ? spi_ack : |
| ((spim_wb_ack==0) && spim_wb_req && spim_wb_req_l) ; |
| end |
| end |
| end |
| |
| wire [3:0] reg_addr = spim_wb_addr[5:2]; |
| integer byte_index; |
| always_ff @(negedge rst_n or posedge mclk) begin |
| if ( rst_n == 1'b0 ) begin |
| reg2spi_swrst <= 1'b0; |
| reg2spi_rd <= 1'b0; |
| reg2spi_wr <= 1'b0; |
| reg2spi_qrd <= 1'b0; |
| reg2spi_qwr <= 1'b0; |
| reg2spi_cmd <= 'h0; |
| reg2spi_addr <= 'h0; |
| reg2spi_cmd_len <= 'h0; |
| reg2spi_addr_len <= 'h0; |
| reg2spi_data_len <= 'h0; |
| reg2spi_wdata <= 'h0; |
| reg2spi_mode_enb <= 'h0; |
| reg2spi_mode <= 'h0; |
| reg2spi_dummy_rd_len <= 'h0; |
| reg2spi_dummy_wr_len <= 'h0; |
| reg2spi_csreg <= 'h0; |
| reg2spi_req <= 'h0; |
| spi_clk_div <= 'h2; |
| spi_init_done <= 'h0; |
| spi_init_state <= SPI_INIT_IDLE; |
| end |
| else if (spi_init_done == 0) begin |
| case(spi_init_state) |
| SPI_INIT_IDLE: |
| begin |
| reg2spi_rd <= 'h0; |
| reg2spi_wr <= 'h1; // SPI Write Req |
| reg2spi_qrd <= 'h0; |
| reg2spi_qwr <= 'h0; |
| reg2spi_swrst <= 'h0; |
| reg2spi_csreg <= 'h1; |
| reg2spi_cmd[7:0] <= 'hAB; // POWER UP command |
| reg2spi_mode[7:0] <= 'h0; |
| reg2spi_cmd_len <= 'h8; |
| reg2spi_addr_len <= 'h0; |
| reg2spi_data_len <= 'h0; |
| reg2spi_wdata <= 'h0; |
| reg2spi_req <= 'h1; |
| spi_init_state <= SPI_INIT_CMD_WAIT; |
| end |
| SPI_INIT_CMD_WAIT: |
| begin |
| if(spi_ack) begin |
| reg2spi_req <= 1'b0; |
| spi_init_state <= SPI_INIT_WREN_CMD; |
| end |
| end |
| SPI_INIT_WREN_CMD: |
| begin |
| reg2spi_rd <= 'h0; |
| reg2spi_wr <= 'h1; // SPI Write Req |
| reg2spi_qrd <= 'h0; |
| reg2spi_qwr <= 'h0; |
| reg2spi_swrst <= 'h0; |
| reg2spi_csreg <= 'h1; |
| reg2spi_cmd[7:0] <= 'h6; // WREN command |
| reg2spi_mode[7:0] <= 'h0; |
| reg2spi_cmd_len <= 'h8; |
| reg2spi_addr_len <= 'h0; |
| reg2spi_data_len <= 'h0; |
| reg2spi_wdata <= 'h0; |
| reg2spi_req <= 'h1; |
| spi_init_state <= SPI_INIT_WREN_WAIT; |
| end |
| SPI_INIT_WREN_WAIT: |
| begin |
| if(spi_ack) begin |
| reg2spi_req <= 1'b0; |
| spi_init_state <= SPI_INIT_WRR_CMD; |
| end |
| end |
| SPI_INIT_WRR_CMD: |
| begin |
| reg2spi_rd <= 'h0; |
| reg2spi_wr <= 'h1; // SPI Write Req |
| reg2spi_qrd <= 'h0; |
| reg2spi_qwr <= 'h0; |
| reg2spi_swrst <= 'h0; |
| reg2spi_csreg <= 'h1; |
| reg2spi_cmd[7:0] <= 'h1; // WRR command |
| reg2spi_mode[7:0] <= 'h0; |
| reg2spi_cmd_len <= 'h8; |
| reg2spi_addr_len <= 'h0; |
| reg2spi_data_len <= 'h10; |
| reg2spi_wdata <= {8'h0,8'h2,16'h0}; // <sr1[7:0]><<cr1[7:0]><16'h0> cr1[1] = 1 indicate quad mode |
| reg2spi_req <= 'h1; |
| spi_init_state <= SPI_INIT_WRR_WAIT; |
| end |
| SPI_INIT_WRR_WAIT: |
| begin |
| if(spi_ack) begin |
| reg2spi_req <= 1'b0; |
| spi_init_done <= 'h1; |
| end |
| end |
| endcase |
| end else if (spim_reg_req & spim_wb_we ) |
| begin |
| case(reg_addr) |
| REG_CTRL: |
| begin |
| if ( spim_wb_be[0] == 1 ) |
| begin |
| reg2spi_rd <= spim_wb_wdata[0]; |
| reg2spi_wr <= spim_wb_wdata[1]; |
| reg2spi_qrd <= spim_wb_wdata[2]; |
| reg2spi_qwr <= spim_wb_wdata[3]; |
| reg2spi_swrst <= spim_wb_wdata[4]; |
| reg2spi_req <= 1'b1; |
| end |
| if ( spim_wb_be[1] == 1 ) |
| begin |
| reg2spi_csreg <= spim_wb_wdata[11:8]; |
| end |
| end |
| REG_CLKDIV: |
| if ( spim_wb_be[0] == 1 ) |
| begin |
| spi_clk_div <= spim_wb_wdata[7:0]; |
| end |
| REG_SPICMD: begin |
| if ( spim_wb_be[0] == 1 ) |
| reg2spi_cmd[7:0] <= spim_wb_wdata[7:0]; |
| if ( spim_wb_be[1] == 1 ) |
| reg2spi_mode[7:0] <= spim_wb_wdata[15:8]; |
| end |
| REG_SPIADR: |
| for (byte_index = 0; byte_index < 4; byte_index = byte_index+1 ) |
| if ( spim_wb_be[byte_index] == 1 ) |
| reg2spi_addr[byte_index*8 +: 8] <= spim_wb_wdata[(byte_index*8) +: 8]; |
| REG_SPILEN: |
| begin |
| if ( spim_wb_be[0] == 1 ) begin |
| reg2spi_mode_enb <= spim_wb_wdata[6]; |
| reg2spi_cmd_len <= spim_wb_wdata[5:0]; |
| end |
| if ( spim_wb_be[1] == 1 ) |
| reg2spi_addr_len <= spim_wb_wdata[13:8]; |
| if ( spim_wb_be[2] == 1 ) |
| reg2spi_data_len[7:0] <= spim_wb_wdata[23:16]; |
| if ( spim_wb_be[3] == 1 ) |
| reg2spi_data_len[15:8] <= spim_wb_wdata[31:24]; |
| end |
| REG_SPIDUM: |
| begin |
| if ( spim_wb_be[0] == 1 ) |
| reg2spi_dummy_rd_len[7:0] <= spim_wb_wdata[7:0]; |
| if ( spim_wb_be[1] == 1 ) |
| reg2spi_dummy_rd_len[15:8] <= spim_wb_wdata[15:8]; |
| if ( spim_wb_be[2] == 1 ) |
| reg2spi_dummy_wr_len[7:0] <= spim_wb_wdata[23:16]; |
| if ( spim_wb_be[3] == 1 ) |
| reg2spi_dummy_wr_len[15:8] <= spim_wb_wdata[31:24]; |
| end |
| REG_SPIWDATA: begin |
| reg2spi_wdata <= spim_wb_wdata; |
| end |
| endcase |
| end |
| else |
| begin |
| if(spi_ack && spim_reg_req) |
| reg2spi_req <= 1'b0; |
| end |
| end |
| |
| |
| |
| // implement slave model register read mux |
| always_comb |
| begin |
| reg_rdata = '0; |
| if(spim_reg_req) begin |
| case(reg_addr) |
| REG_CTRL: |
| reg_rdata[31:0] = { 20'h0, |
| reg2spi_csreg, |
| 3'b0, |
| reg2spi_swrst, |
| reg2spi_qwr, |
| reg2spi_qrd, |
| reg2spi_wr, |
| reg2spi_rd}; |
| |
| REG_CLKDIV: |
| reg_rdata[31:0] = {24'h0,spi_clk_div}; |
| REG_SPICMD: |
| reg_rdata[31:0] = {16'h0,reg2spi_mode,reg2spi_cmd}; |
| REG_SPIADR: |
| reg_rdata[31:0] = reg2spi_addr; |
| REG_SPILEN: |
| reg_rdata[31:0] = {reg2spi_data_len,2'b00,reg2spi_addr_len,1'b0,reg2spi_mode_enb,reg2spi_cmd_len}; |
| REG_SPIDUM: |
| reg_rdata[31:0] = {reg2spi_dummy_wr_len,reg2spi_dummy_rd_len}; |
| REG_SPIWDATA: |
| reg_rdata[31:0] = reg2spi_wdata; |
| REG_SPIRDATA: |
| reg_rdata[31:0] = spim_reg_rdata; |
| REG_STATUS: |
| reg_rdata[31:0] = {23'h0,spi_status}; |
| endcase |
| end |
| end |
| |
| |
| endmodule |