/* (c) Peter McGoron 2022 v0.4 * * This code is disjunctively dual-licensed under the MPL v2.0, or the * CERN-OHL-W v2. */ module spi_slave #( parameter ENABLE_MISO = 1, // Enable MISO and to_master port parameter ENABLE_MOSI = 1, // Enable MOSI and from_master port parameter WID = 24, // Width of bits per transaction. parameter WID_LEN = 5, // Length in bits required to store WID parameter POLARITY = 0, parameter PHASE = 0 // 0 = rising-read falling-write, 1 = rising-write falling-read. ) ( input clk, input rst_L, input sck, input ss_L, output reg [WID-1:0] from_master, input mosi, input [WID-1:0] to_master, output reg miso, output reg finished, input rdy, output reg err ); /* MOSI is almost always an external wire, so buffer it. */ reg mosi_hot = 0; reg read_mosi = 0; always @ (posedge clk) if (ENABLE_MOSI == 1) begin read_mosi <= mosi_hot; mosi_hot <= mosi; end wire ss = !ss_L; reg sck_delay = 0; reg [WID_LEN-1:0] bit_counter = 0; reg ss_delay = 0; reg ready_at_start = 0; `ifndef SPI_SLAVE_NO_WRITE reg [WID-1:0] send_buf = 0; `endif task read_data(); if (ENABLE_MOSI == 1) begin from_master <= from_master << 1; from_master[0] <= read_mosi; end endtask task write_data(); if (ENABLE_MISO == 1) begin send_buf <= send_buf << 1; miso <= send_buf[WID-1]; end endtask task setup_bits(); if (ENABLE_MISO == 1) begin /* at Mode 00, the transmission starts with * a rising edge, and at mode 11, it starts with a falling * edge. For both modes, these are READs. * * For mode 01 and mode 10, the first action is a WRITE. */ if (POLARITY == PHASE) begin miso <= to_master[WID-1]; send_buf <= to_master << 1; end else begin send_buf <= to_master; end end endtask task check_counter(); if (bit_counter == WID[WID_LEN-1:0]) begin err <= ready_at_start; end else begin bit_counter <= bit_counter + 1; end endtask always @ (posedge clk) begin if (!rst_L) begin sck_delay <= 0; bit_counter <= 0; ss_delay <= 0; ready_at_start <= 0; if (ENABLE_MOSI == 1) from_master <= 0; if (ENABLE_MISO == 1) begin miso <= 0; send_buf <= 0; end finished <= 0; err <= 0; end else begin sck_delay <= sck; ss_delay <= ss; case ({ss_delay, ss}) 2'b01: begin // rising edge of SS bit_counter <= 0; finished <= 0; err <= 0; ready_at_start <= rdy; setup_bits(); end 2'b10: begin // falling edge finished <= ready_at_start; end 2'b11: begin case ({sck_delay, sck}) 2'b01: begin // rising edge if (PHASE == 1) begin write_data(); end else begin read_data(); end if (POLARITY == 0) begin check_counter(); end end 2'b10: begin // falling edge if (PHASE == 1) begin read_data(); end else begin write_data(); end if (POLARITY == 1) begin check_counter(); end end default: ; endcase end 2'b00: if (!rdy) begin finished <= 0; err <= 0; end endcase end end endmodule