2
0
Эх сурвалжийг харах

Add SPI master unit IP, move generally useful functions to a common file

Add spi_master.sv -- SPI master unit with 2-, 4- or 8-bit support.

Move functions to a common file instead of replicating them.
H. Peter Anvin 3 жил өмнө
parent
commit
1355c99091
6 өөрчлөгдсөн 277 нэмэгдсэн , 20 устгасан
  1. 40 0
      functions.sv
  2. 9 7
      max80.qsf
  3. 2 2
      output_files/max80.jam
  4. BIN
      output_files/max80.sof
  5. 0 11
      sdram.sv
  6. 226 0
      spi_master.sv

+ 40 - 0
functions.sv

@@ -0,0 +1,40 @@
+//
+// Common useful functions
+//
+
+// ceil(ilog2(a))
+function int ilog2c(int a);
+  int b = 0;
+   while ((1 << b) < a)
+     b++;
+   ilog2c = b;
+endfunction // ilog2c
+
+// floor(ilog2(a))
+function int ilog2f(int a);
+  ilog2f = ilog2c(a+1)-1;
+endfunction // ilog2f
+
+function int max(int a, int b);
+  max = a > b ? a : b;
+endfunction // max
+
+function int max3(int a, int b, int c);
+  max3 = max(max(a,b),c);
+endfunction // max3
+
+function int max4(int a, int b, int c, int d);
+  max4 = max(max(a,b),max(c,d));
+endfunction // max4
+
+function int min(int a, int b);
+  min = a < b ? a : b;
+endfunction // min
+
+function int min3(int a, int b, int c);
+  min3 = min(min(a,b),c);
+endfunction // min3
+
+function int min4(int a, int b, int c, int d);
+  min4 = min(min(a,b),min(c,d));
+endfunction // min4

+ 9 - 7
max80.qsf

@@ -138,6 +138,15 @@ set_global_assignment -name POST_MODULE_SCRIPT_FILE "quartus_sh:scripts/postmodu
 
 
 
+
+set_global_assignment -name OPTIMIZATION_MODE BALANCED
+set_global_assignment -name AUTO_RAM_TO_LCELL_CONVERSION ON
+set_global_assignment -name SYNTH_GATED_CLOCK_CONVERSION ON
+set_global_assignment -name PRE_MAPPING_RESYNTHESIS ON
+set_global_assignment -name ROUTER_CLOCKING_TOPOLOGY_ANALYSIS ON
+set_global_assignment -name QII_AUTO_PACKED_REGISTERS "SPARSE AUTO"
+set_global_assignment -name SYSTEMVERILOG_FILE functions.sv
+set_global_assignment -name SYSTEMVERILOG_FILE spi_master.sv
 set_global_assignment -name SYSTEMVERILOG_FILE sdram.sv
 set_global_assignment -name VERILOG_FILE ip/ddio_out.v
 set_global_assignment -name TCL_SCRIPT_FILE scripts/post_quartus_asm.tcl
@@ -151,13 +160,6 @@ set_global_assignment -name SYSTEMVERILOG_FILE tmdsenc.sv
 set_global_assignment -name SDC_FILE max80.sdc
 set_global_assignment -name SYSTEMVERILOG_FILE max80.sv
 set_global_assignment -name SOURCE_FILE max80.pins
-set_global_assignment -name TCL_SCRIPT_FILE scripts/pins.tcl
 set_global_assignment -name SOURCE_TCL_SCRIPT_FILE scripts/pins.tcl
 
-set_global_assignment -name OPTIMIZATION_MODE BALANCED
-set_global_assignment -name AUTO_RAM_TO_LCELL_CONVERSION ON
-set_global_assignment -name SYNTH_GATED_CLOCK_CONVERSION ON
-set_global_assignment -name PRE_MAPPING_RESYNTHESIS ON
-set_global_assignment -name ROUTER_CLOCKING_TOPOLOGY_ANALYSIS ON
-set_global_assignment -name QII_AUTO_PACKED_REGISTERS "SPARSE AUTO"
 set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top

+ 2 - 2
output_files/max80.jam

@@ -13,7 +13,7 @@
 'refer to the applicable agreement for further details, at
 'https://fpgasoftware.intel.com/eula.
 
-'Device #1: EP4CE15 - /home/hpa/abc80/max80/blinktest/output_files/max80.sof Thu Sep  9 17:41:11 2021
+'Device #1: EP4CE15 - /home/hpa/abc80/max80/blinktest/output_files/max80.sof Thu Sep  9 20:45:44 2021
 
 
 NOTE "CREATOR" "QUARTUS PRIME JAM COMPOSER 20.1";
@@ -2455,4 +2455,4 @@ NEXT j;
 POP j;
 ENDPROC;
 ' END OF FILE
-CRC C692;
+CRC 9EC3;

BIN
output_files/max80.sof


+ 0 - 11
sdram.sv

@@ -120,17 +120,6 @@ module sdram
 			    mrd_interleave,	// Interleaved bursts
 			    mrd_burst };	// Burst length
 
-   // Handy functions for timing calculations
-   function int max(input int a, input int b);
-     if (a > b)
-       max = a;
-     else
-       max = b;
-   endfunction // max
-   function int max3(input int a, input int b, input int c);
-      max3 = max(max(a,b),c);
-   endfunction // max3
-
    // Where to issue a PRECHARGE when we only want to read one word
    // (terminate the burst as soon as possible, but no sooner...)
    localparam t_pre_rd_when = max(t_ras, t_rcd + 1);

+ 226 - 0
spi_master.sv

@@ -0,0 +1,226 @@
+//
+// 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 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;
+
+   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