diff options
Diffstat (limited to 'design')
-rw-r--r-- | design/spi_slave.v | 262 |
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 |