blob: dff17d7d87fac2e3c51cc7b6c96d234d73cf8309 [file] [log] [blame]
// SPDX-FileCopyrightText: 2022 Piotr Wegrzyn
//
// 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
module sspi (
`ifdef USE_POWER_PINS
inout vccd1,
inout vssd1,
`endif
input i_clk,
input i_rst,
input spi_clk,
input spi_mosi,
output reg spi_miso,
output reg wb_cyc,
output reg wb_stb,
output reg [23:0] wb_adr,
input [15:0] wb_i_dat,
output reg [15:0] wb_o_dat,
output reg wb_we,
output [1:0] wb_sel,
input wb_ack,
input wb_err
);
reg [2:0] sy_clk;
wire sclk_edge = (sy_clk[2] ^ sy_clk[1]) & ~sy_clk[2];
always @(posedge i_clk) begin
if (i_rst) sy_clk <= 3'b0;
else sy_clk <= {sy_clk[1], sy_clk[0], spi_clk};
end
reg [23:0] req_addr;
reg [15:0] req_data;
reg [15:0] res_data;
reg resp_err;
reg [4:0] bit_cnt;
reg [3:0] state;
localparam STATE_IDLE = 4'b0;
localparam STATE_ADDR = 4'b1;
localparam STATE_RW = 4'b10;
localparam STATE_WRITE_DAT = 4'b11;
localparam STATE_WRITE_WB = 4'b100;
localparam STATE_WB_RESP = 4'b101;
localparam STATE_READ_WB = 4'b110;
localparam STATE_READ_DAT = 4'b111;
always @(posedge i_clk) begin
if (i_rst) begin
state <= STATE_IDLE;
bit_cnt <= 'b0;
spi_miso <= 1'b1;
wb_cyc <= 1'b0;
wb_stb <= 1'b0;
end else if (sclk_edge) begin
if (state == STATE_IDLE) begin
spi_miso <= 1'b1;
if (~spi_mosi)
state <= STATE_ADDR;
end else if (state == STATE_ADDR) begin
req_addr[bit_cnt] <= spi_mosi;
bit_cnt <= bit_cnt + 5'b1;
if (bit_cnt == 5'd23) begin
state <= STATE_RW;
bit_cnt <= 5'b0;
end
end else if (state == STATE_RW) begin
state <= (spi_mosi ? STATE_WRITE_DAT : STATE_READ_WB);
end else if (state == STATE_WRITE_DAT) begin
req_data[bit_cnt[3:0]] <= spi_mosi;
bit_cnt <= bit_cnt + 5'b1;
if (bit_cnt == 5'd15) begin
state <= STATE_WRITE_WB;
bit_cnt <= 5'b0;
end
end else if (state == STATE_WRITE_WB) begin
wb_cyc <= 1'b1;
wb_stb <= 1'b1;
wb_we <= 1'b1;
wb_adr <= req_addr;
wb_o_dat <= req_data;
if ((wb_ack | wb_err) & wb_cyc) begin
state <= STATE_WB_RESP;
bit_cnt <= 5'b0;
resp_err <= wb_err;
wb_cyc <= 1'b0;
wb_stb <= 1'b0;
spi_miso <= 1'b0; // end of wait
end
end else if (state == STATE_WB_RESP) begin
spi_miso <= resp_err;
state <= STATE_IDLE;
end else if (state == STATE_READ_WB) begin
wb_cyc <= 1'b1;
wb_stb <= 1'b1;
wb_we <= 1'b0;
wb_adr <= req_addr;
if ((wb_ack | wb_err) & wb_cyc) begin
state <= STATE_READ_DAT;
bit_cnt <= 5'b0;
res_data <= wb_i_dat;
resp_err <= wb_err;
wb_cyc <= 1'b0;
wb_stb <= 1'b0;
spi_miso <= 1'b0; // end of wait
end
end else if (state == STATE_READ_DAT) begin
spi_miso <= res_data[bit_cnt[3:0]];
bit_cnt <= bit_cnt + 5'b1;
if (bit_cnt == 5'd15) begin
state <= STATE_WB_RESP;
bit_cnt <= 5'b0;
end
end
end
end
assign wb_sel = 2'b11;
endmodule