123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227 |
- //
- // 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.
- //
- // All unused spi_io are driven to 1.
- //
- //
- // XXX: add clock polarity, MSB/LSB options
- //
- module spi_master
- #(
- parameter width = 1, // Width of SPI data
- parameter n_cs = 1, // Number of CS# outputs
- parameter CPOL = 0, // Inverted SPI clock polarity
- parameter cs_delay = 1,
- parameter io_max = max(ilog2c(width)-1, 1)
- )
- (
- 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 sack, // Session started
- output eack, // Session ended
- input [n_cs-1:0] cs, // Device select (active high)
- output spi_sck, // SPI clock
- inout [min(ilog2c(width)-1, 1):0] spi_io, // SPI data
- output [n_cs-1:0] spi_cs_n // SPI CS# lines
- );
- localparam ctr_max = max(ilog2c(cs_delay)-1,2);
- reg spi_clk;
- reg spi_sck_q;
- reg spi_active;
- reg [ilog2c(width)-1:0] spi_width;
- reg [ctr_max:0] spi_ctr;
- reg [n_cs-1:0] spi_cs_q;
- reg [7:0] spi_out_q;
- reg [1:0] spi_oe_q;
- reg d_dir;
- reg [7:0] d_out;
- reg [7:0] d_in;
- reg [7:0] q_q;
- reg sack_q;
- reg eack_q;
- assign spi_cs_n = ~spi_cs_q;
- assign spi_sck = spi_sck_q ^ CPOL;
- wire spi_cs_changed = |(spi_cs_q ^ cs);
- assign spi_io[0] = spi_oe_q[0] ? spi_out_q[0] : 1'bz;
- assign spi_io[io_max:1] = spi_oe_q[1] ? spi_out_q[io_max:1] : {io_max{1'bz}};
- always @(negedge rst_n or posedge clk)
- if (~rst_n)
- begin
- spi_clk <= 1'b0;
- spi_active <= 1'b0;
- spi_width <= 4'b0001;
- spi_ctr <= 1'b0;
- spi_cs_q <= 1'b0;
- spi_out_q <= 8'hFF;
- spi_oe_q <= 2'b10;
- sack_q <= 1'b0;
- eack_q <= 1'b0;
- d_out <= 8'hFF;
- d_in <= 8'hxx;
- q_q <= 8'hxx;
- end
- else
- begin
- sack_q <= 1'b0;
- eack_q <= 1'b0;
- if (clk_en)
- begin
- spi_ctr <= spi_ctr - 1'b1;
- spi_clk <= spi_ctr[0] & spi_active;
- if (~spi_ctr[0])
- begin
- case (spi_width)
- 4'd1:
- d_in <= { d_in[6:0], spi_io[0] };
- 4'd2:
- d_in <= { d_in[5:0], spi_io[1:0] };
- 4'd4:
- d_in <= { d_in[3:0], spi_io[3:0] };
- 4'd8:
- d_in <= spi_io;
- default:
- d_in <= 8'hxx;
- endcase // case 2'd3
- end // if (~spi_ctr[0])
- else
- begin
- if (spi_active)
- begin
- spi_out_q <= 8'hFF;
- if (~|spi_ctr[ctr_max:1])
- begin
- eack_q <= 1'b1;
- q_q <= d_in;
- spi_active <= 1'b0;
- end
- case (spi_width)
- 4'd1:
- begin
- d_out <= { d_out[6:0], 1'b1 };
- spi_out_q[1] <= d_out[7];
- end
- 4'd2:
- begin
- d_out <= { d_out[5:0], 2'b11 };
- spi_out_q[1:0] <= d_out[7:6];
- end
- 4'd4:
- begin
- d_out <= { d_out[3:0], 4'b1111 };
- spi_out_q[3:0] <= d_out[7:4];
- end
- 4'd8:
- begin
- d_out <= 8'hFF;
- spi_out_q <= d_out;
- end
- default:
- begin
- d_out <= 8'hxx;
- spi_out_q <= 8'hxx;
- end
- endcase // case (spi_width)
- end // if (spi_active)
- else
- begin
- spi_cs_q <= cs;
- d_out <= d;
- spi_out_q <= 8'hFF;
- 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 (cs_delay == 0 || ~|spi_ctr[ctr_max:1])
- begin
- if (req)
- begin
- case (iowidth)
- 2'd0:
- begin
- spi_width <= 4'b0001;
- spi_ctr[ctr_max:1] <= 3'd8;
- spi_oe_q <= 2'b10;
- end
- 2'd1:
- if (width < 2)
- begin
- spi_width <= 4'b000x;
- spi_ctr[ctr_max:1] <= 1'bx;
- spi_oe_q <= 2'bxx;
- end
- else
- begin
- spi_width <= 4'b0010;
- spi_ctr[ctr_max:1] <= 3'd4;
- spi_oe_q <= {2{dir}};
- end
- 2'd2:
- if (width < 4)
- begin
- spi_width <= 4'b00xx;
- spi_ctr[ctr_max:1] <= 1'bx;
- spi_oe_q <= 2'bxx;
- end
- else
- begin
- spi_width <= 4'b0100;
- spi_ctr[ctr_max:1] <= 3'd2;
- spi_oe_q <= {2{dir}};
- end
- 2'd3:
- if (width < 8)
- begin
- spi_width <= 4'b0xxx;
- spi_ctr[ctr_max:1] <= 1'bx;
- spi_oe_q <= 2'bxx;
- end
- else
- begin
- spi_width <= 4'b1000;
- spi_ctr[ctr_max:1] <= 3'd1;
- spi_oe_q <= {2{dir}};
- end
- endcase // case (iowidth)
- spi_active <= 1'b1;
- sack_q <= 1'b1;
- end // if (req)
- end // if (cs_delay == 0 || ~|spi_ctr[ctr_max:1])
- end // else: !if(spi_active)
- end // else: !if(~spi_ctr[0])
- end // if (clk_en)
- end // else: !if(~rst_n)
- endmodule // spi_master
|