`default_nettype none `define ADDR_WIDTH ($clog2(MEMORY_BLOCKS + 1) + 8) module spi_slave #( parameter MEMORY_BLOCKS = 1 /* 1 block stores 256 16-bit words */ ) ( /* Interface to flash memory chip */ output reg sdo, input wire sdi, output wire sck, output reg ss_n, /* Wishbone slave interface */ output wire ACK_O, input wire [`ADDR_WIDTH - 1:0] ADR_I, input wire CLK_I, input wire [15:0] DAT_I, output wire [15:0] DAT_O, input wire RST_I, input wire STB_I, input wire WE_I, output wire STALL_O ); parameter MEMORY_IDX_WIDTH = $clog2(MEMORY_BLOCKS) + 8; parameter MEMORY_MAX_ADDR = MEMORY_BLOCKS * 256 - 1; parameter BIT_COUNT_WIDTH = MEMORY_IDX_WIDTH + 4; reg [15:0] memory [MEMORY_MAX_ADDR:0]; reg [15:0] data_read_from_memory; reg [15:0] data_read_from_regs; wire [15:0] data_to_write_to_memory; reg [`ADDR_WIDTH - 1:0] ADR_I_latched; reg [15:0] DAT_I_latched; reg [1:0] wb_read_memory; reg wb_write_memory; reg wb_read_regs; reg wb_write_regs; /* * The wires below are only relevant if regs are being accessed, i.e. * ADR_I_latched > MEMORY_MAX_ADDR. */ wire reg_bytes_to_output; wire reg_bytes_to_receive; wire reg_operating; assign reg_bytes_to_output = ADR_I_latched[1:0] == 2'b00; assign reg_bytes_to_receive = ADR_I_latched[1:0] == 2'b01; assign reg_operating = ADR_I_latched[1] == 1'b1; reg ack; reg [2:0] operating; wire [1:0] start_operating; assign start_operating = {operating[2:1] == 2'b01, operating[1:0] == 2'b01}; reg [15:0] out_data; // sdo data reg [15:0] in_data; // sdi data reg bytes_to_output_odd; reg bytes_to_receive_odd; reg [BIT_COUNT_WIDTH - 1:0] bits_to_output; reg [BIT_COUNT_WIDTH - 1:0] bits_to_receive; reg [MEMORY_IDX_WIDTH - 1:0] memory_idx; reg [1:0] spi_read_memory; reg spi_write_memory; wire spi_write_1byte; wire initial_spi_read_memory; assign spi_write_1byte = bits_to_receive[3] == 0 && bytes_to_receive_odd; assign initial_spi_read_memory = start_operating[0]; wire [MEMORY_IDX_WIDTH - 1:0] memory_read_idx; wire [MEMORY_IDX_WIDTH - 1:0] memory_write_idx; assign memory_read_idx = initial_spi_read_memory ? 0 : spi_read_memory[0] ? memory_idx : ADR_I_latched; assign memory_write_idx = spi_write_memory ? memory_idx : ADR_I_latched; assign data_to_write_to_memory = spi_write_memory ? (spi_write_1byte ? {8'hXX, in_data[7:0]} : {in_data[7:0], in_data[15:8]}) : DAT_I_latched; wire wb_reg_can_be_written; assign wb_reg_can_be_written = !operating[0]; wire wb_mread_completes; wire wb_rread_completes; wire wb_mwrite_completes; wire wb_rwrite_completes; assign wb_mread_completes = !(spi_read_memory[0] || initial_spi_read_memory) && wb_read_memory[0]; assign wb_rread_completes = wb_read_regs; /* can always read immediately */ assign wb_mwrite_completes = !spi_write_memory && wb_write_memory; assign wb_rwrite_completes = wb_reg_can_be_written && wb_write_regs; wire wb_operation_accepted; wire wb_operation_pending; wire wb_operation_completes; assign wb_operation_accepted = !STALL_O && STB_I; assign wb_operation_pending = wb_read_memory || wb_read_regs || wb_write_memory || wb_write_regs; assign wb_operation_completes = wb_mread_completes || wb_rread_completes || wb_mwrite_completes || wb_rwrite_completes; assign DAT_O = wb_read_memory[1] ? data_read_from_memory : data_read_from_regs; assign ACK_O = ack; assign STALL_O = wb_operation_pending && !wb_operation_completes; parameter state_sending = 0; parameter state_receiving = 1; reg state; assign sck = CLK_I; always @ (posedge CLK_I) begin operating[2:1] <= operating[1:0]; data_read_from_memory <= memory[memory_read_idx]; if (spi_write_memory || wb_write_memory) memory[memory_write_idx] <= data_to_write_to_memory; spi_read_memory[1] <= spi_read_memory[0] || initial_spi_read_memory; if (operating[0]) begin spi_read_memory[0] <= bits_to_output[BIT_COUNT_WIDTH - 1:4] != 0 && bits_to_output[3:0] == (bytes_to_output_odd ? 11 : 3); spi_write_memory <= bits_to_receive == 1 || bits_to_receive[3:0] == (bytes_to_receive_odd ? 9 : 1); end else begin spi_read_memory[0] <= 0; spi_write_memory <= 0; end out_data <= spi_read_memory[1] ? {data_read_from_memory[7:0], data_read_from_memory[15:8]} : {out_data[14:0], 1'bx}; in_data <= {in_data[14:0], sdi}; wb_read_memory[1] <= wb_read_memory[0]; if (reg_bytes_to_output) data_read_from_regs <= bits_to_output[BIT_COUNT_WIDTH - 1:3]; else if (reg_bytes_to_receive) data_read_from_regs <= bits_to_receive[BIT_COUNT_WIDTH - 1:3]; else if (reg_operating) data_read_from_regs <= {16{operating[0]}}; else data_read_from_regs <= 16'hXXXX; if (wb_write_regs && wb_reg_can_be_written) begin if (reg_bytes_to_output) begin bits_to_output <= DAT_I_latched << 3; bytes_to_output_odd <= DAT_I_latched[0]; end if (reg_bytes_to_receive) begin bits_to_receive <= (DAT_I_latched << 3); bytes_to_receive_odd <= DAT_I_latched[0]; end if (reg_operating) operating[0] <= 1; /* might be overwritten below */ end if (RST_I) begin wb_read_memory[0] <= 0; wb_write_memory <= 0; wb_read_regs <= 0; wb_write_regs <= 0; operating[0] <= 0; bits_to_receive <= 16'hXXXX; bits_to_output <= 16'hXXXX;//{{(BIT_COUNT_WIDTH - 4){1'bx}}, 4'h0}; bytes_to_output_odd <= 1'bx; bytes_to_receive_odd <= 1'bx; ack <= 0; end else begin // if (RST_I) if (wb_operation_accepted) begin ADR_I_latched <= ADR_I; DAT_I_latched <= DAT_I; wb_read_memory[0] <= !WE_I && ADR_I <= MEMORY_MAX_ADDR; wb_write_memory <= WE_I && ADR_I <= MEMORY_MAX_ADDR; wb_read_regs <= !WE_I && ADR_I > MEMORY_MAX_ADDR; wb_write_regs <= WE_I && ADR_I > MEMORY_MAX_ADDR; end else if (wb_operation_completes) begin wb_read_memory[0] <= 0; wb_write_memory <= 0; wb_read_regs <= 0; wb_write_regs <= 0; end ack <= wb_operation_completes; end // else: !if(RST_I) if (start_operating[1]) begin memory_idx <= 1; state <= state_sending; end if (operating == 3'b111) begin if (state == state_sending) begin bits_to_output <= bits_to_output - 1; if (spi_read_memory[0]) memory_idx <= memory_idx + 1; if (bits_to_output - 1 == 0) begin state <= state_receiving; memory_idx <= 0; if (bits_to_receive == 0) operating[0] <= 0; end end else if (state == state_receiving) begin bits_to_receive <= bits_to_receive - 1; if (spi_write_memory) memory_idx <= memory_idx + 1; if (bits_to_receive - 1 == 0) operating[0] <= 0; end end // if (operating[2]) end // always @ (posedge CLK_I) always @ (negedge CLK_I) begin sdo <= out_data[15]; ss_n <= !(operating == 3'b111); end `ifdef SIMULATION initial begin operating[0] <= 0; ss_n <= 1; end `endif endmodule // spi_slave