/* spi.v KISS ("keep it simple & stupid") SPI master engine. Configured for 2 slaves, occupies just 38 LCs in an Intel MAX10 or 34 in a Xilinx Spatan6. Parameter DIVPWR: SPI sck is derived from input clk divided by 2^DIVPWR. SELECTS: Number of outgoing nSS signals, i.e. number of slaves hooked to the bus. Interface Registers Write Read Address 1: ctrl nSS SPIready Address 0: data byte to xmit received byte You may have slaves at the bus that are routed to different FPGA pins. While MOSI and SCK can simply be made to drive multiple pins, MISO lines have to be combined. Since only the selected slave will drive MISO low, we can simply 'and' them togehther. SW actions (or how to use it) Write 0xFF to ctrl to DESELECT all slaves. To select a slave, write a 0 to the appropriate bit and 1s to all other bits. The data register may be written/read when SPIready is true. - Hint: your SW may spend enough time between successive SPI actions anyhow to not have to check SPIready every time. (C) 2021 binär industrie-informatik Bernd Kohler bk 25Mar21 */ module spi #(parameter DIVPWR = 3, parameter SELECTS = 1) ( // Global input reset, input clk, // uP input wr, input rd, input addr, input [7:0] din, output [7:0] dout, // SPI output reg [SELECTS-1:0] nss, output sck, input [SELECTS-1:0] miso, output mosi ); reg [DIVPWR-1:0] clks; wire spiclk; reg prev_spiclk; wire rise_spiclk = !prev_spiclk & spiclk; // Time to sample miso line(s) wire trail_spiclk = prev_spiclk & !spiclk; // Time to shift in/out reg state; reg [7:0] shift; reg [2:0] bits = 3'b0; wire [7:0] rddat = addr ? {8{!state}} : shift; reg int_miso; // 'And' of incoming miso lines. assign spiclk = clks[DIVPWR-1]; assign sck = state ? spiclk : 1'b0; assign mosi = shift[7]; assign dout = rd ? rddat : 8'hAA; // Shouldn't see the hex AA! always @(posedge clk) begin if (state) begin clks <= clks + 1; prev_spiclk <= spiclk; end end always @(posedge clk or posedge reset) begin if (reset) begin state <= 0; nss <= {SELECTS{1'b1}}; shift <= 8'b0; end // if (reset) else begin // !reset if (wr) begin if (addr) begin nss <= din[SELECTS-1:0]; end // if (address) else begin state <= 1; shift <= din; bits <= 3'b000; end // if (!address) end // if (wr) if (state) begin if (rise_spiclk) int_miso <= &miso; // Sample incoming data if (trail_spiclk) begin shift <= {shift[6:0], int_miso}; // Shift out if (bits == 3'h7) begin state <= 0; end // if (bits == 7) else bits <= bits + 1; end // if trail_spiclk end // if (state) end // !reset end // @(posedge clk or posedge reset) endmodule