| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 | //// 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_inmodule 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
 |