aboutsummaryrefslogtreecommitdiff
path: root/design
diff options
context:
space:
mode:
Diffstat (limited to 'design')
-rw-r--r--design/spi_slave.v262
1 files changed, 262 insertions, 0 deletions
diff --git a/design/spi_slave.v b/design/spi_slave.v
new file mode 100644
index 0000000..a53d9d2
--- /dev/null
+++ b/design/spi_slave.v
@@ -0,0 +1,262 @@
+`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