// // spi_master.sv // // Simple byte-oriented SPI master unit with optional multiwidth support // (1, 2, 4, 8). // // The output SPI clock equals the system clock /2 unless clk_en is used // to throttle the output clock. // // spi_io[0] = DI, spi_io[1] = DO in single bit mode. // // By default the output is latched; if not, it is only valid in // the cycle that eack is asserted (useful for FIFOs etc.) // // // XXX: CHPA option (mode 1/3) // `define IO_MAX max(ilog2c(width)-1, 1) function reg [7:0] shift_in ( input [7:0] prev, input [7:0] in, input [1:0] xwidth, // log2(transaction width) input [0:0] lsb ); (* full_case *) case (xwidth) 2'd0: shift_in = lsb ? { in[0], prev[7:1] } : { prev[6:0], in[0] }; 2'd1: shift_in = lsb ? { in[1:0], prev[7:2] } : { prev[5:0], in[1:0] }; 2'd2: shift_in = lsb ? { in[3:0], prev[7:4] } : { prev[3:0], in[3:0] }; 2'd3: shift_in = in; endcase // case (width) endfunction // shift_in module spi_master #( parameter width = 1, // Max width of SPI bus (1, 2, 4, or 8) parameter n_cs = 1, // Number of CS# outputs parameter cs_delay = 1, // Time from CS# low to first data parameter latch_q = 1 // Latch output ) ( input rst_n, // Unit reset input clk, // System clock input clk_en, // SPI clock enable input [7:0] d, // System data in output [7:0] q, // System data out input req, // Session request input dir, // Session is write (for multibit) input [1:0] iowidth, // Session width (lg2) output reg sack, // Session started output reg eack, // Session ended input idle_io, // Signal level for I/Os at idle input cpol, // Clock polarity (usually constant) input lsb, // Littleendian mode (usually constant) input [n_cs-1:0] cs, // Device select (active high) output reg spi_sck, // SPI clock inout [`IO_MAX:0] spi_io, // SPI data output [n_cs-1:0] spi_cs_n // SPI CS# lines ); localparam io_max = `IO_MAX; localparam iowidth_max = ilog2c(width); localparam ctr_max = max(ilog2c(cs_delay)-1,2); reg spi_active; reg [((width > 2) ? 1 : 0):0] spi_width; reg [ctr_max:0] spi_ctr; reg [n_cs-1:0] spi_cs_q; reg [io_max:0] spi_out_q; reg [1:0] spi_oe_q; reg d_dir; reg [7:0] d_out; // Output shift register reg [7:0] d_in; // Input shift register reg [7:0] q_q; // Latched output data assign spi_cs_n = ~spi_cs_q; assign q = latch_q ? q_q : d_in; wire spi_cs_changed = |(spi_cs_q ^ cs); // Always 2'b10 for single-bit SPI wire [1:0] spi_oe = (width > 1) ? spi_oe_q : 2'b10; assign spi_io[0] = spi_oe[0] ? spi_out_q[0] : 1'bz; assign spi_io[io_max:1] = spi_oe[1] ? spi_out_q[io_max:1] : {io_max{1'bz}}; // Make it clear to the compiler that iowidth is undefined if // it is too large wire [1:0] iowidth_in = (iowidth > iowidth_max) ? 2'bxx : iowidth; // Just for convenience... wire [3:0] spi_width_n = 1'b1 << spi_width; wire [io_max:0] idle_allio = {(io_max+1){idle_io}}; always @(negedge rst_n or posedge clk) if (~rst_n) begin spi_sck <= 1'b0; spi_active <= 1'b0; spi_width <= 4'b0001; spi_ctr <= 1'b0; spi_cs_q <= 1'b0; spi_out_q <= idle_allio; spi_oe_q <= 2'b10; sack <= 1'b0; eack <= 1'b0; d_out <= idle_allio; d_in <= 8'hxx; q_q <= 8'hxx; end else begin // These are always single system clock pulses sack <= 1'b0; eack <= 1'b0; if (clk_en) begin spi_ctr <= spi_ctr - 1'b1; spi_sck <= (spi_ctr[0] & spi_active) ^ cpol; if (~spi_ctr[0]) begin d_in <= shift_in(d_in, spi_io, spi_width, lsb); end else begin spi_out_q <= idle_allio; if (spi_active) begin if (~|spi_ctr[ctr_max:1]) begin eack <= 1'b1; q_q <= d_in; spi_active <= 1'b0; end if (spi_width > iowidth_max) begin // Invalid width, let the compiler do whatever d_out <= 8'hxx; spi_out_q <= {(io_max+1){1'bx}}; end else begin d_out <= shift_in(d_out, {8{idle_io}}, spi_width, lsb); // Note: 1-byte SPI uses IO1 as output (DO) (* full_case *) casez ( {spi_width, lsb} ) 3'b00_0: spi_out_q[1] <= d_out[7]; 3'b00_1: spi_out_q[1] <= d_out[0]; 3'b01_0: spi_out_q[1:0] <= d_out[7:6]; 3'b01_1: spi_out_q[1:0] <= d_out[1:0]; 3'b10_0: spi_out_q[3:0] <= d_out[7:4]; 3'b10_1: spi_out_q[3:0] <= d_out[3:0]; 3'b11_?: spi_out_q <= d_out; endcase // casez ( {spi_width, lsb} ) end end // if (spi_active) else begin spi_cs_q <= cs; d_out <= d; if (cs_delay != 0 && (spi_cs_changed | ~|spi_ctr[ctr_max:1])) begin if (spi_cs_changed) spi_ctr[ctr_max:1] <= cs_delay; spi_oe_q <= 2'b10; // As for 1-bit mode end else if (req && (cs_delay == 0 || ~|spi_ctr[ctr_max:1])) begin if (iowidth <= iowidth_max) begin spi_width <= iowidth; spi_ctr[ctr_max:1] <= 3'd8 >> iowidth; spi_oe_q <= |iowidth ? {2{dir}} : 2'b10; spi_active <= 1'b1; sack <= 1'b1; end else begin // Invalid width, let the compiler // do whatever it wants... spi_width <= 2'bxx; spi_ctr[ctr_max:1] <= {ctr_max{1'bx}}; spi_oe_q <= 2'bxx; spi_active <= 1'bx; sack <= 1'bx; end end // if (req &&... end // else: !if(spi_active) end // else: !if(~spi_ctr[0]) end // if (clk_en) end // else: !if(~rst_n) endmodule // spi_master