| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 | //// Communication interface with ESP32-S2//// This is a DIO (2-bit, including command) SPI slave interface which// allows direct access to content in SDRAM. Additionally, each// direction has three interrupt flags (3-1); the FPGA CPU additionally// has a fourth interrupt condition (0) which indicates DRAM timing// overrun/underrun.//// The SPI command byte is:// Bit [7:5]  - reserved, must be 0// Bit    4   - read/write#// Bit [3:2]  - clear upstream (FPGA->ESP) interrupt flag if nonzero// Bit [1:0]  - set downstream (ESP->FPGA) interrupt flag if nonzero//// CPU downstream interrupts are set after the transaction completes// (CS# goes high.)//// A 32-bit address follows; for a read, the following 16 cycles// contains dummy/status data://// Bit [31:16] = adjusted memory address// Bit [15:14] = 2'b10// Bit [13: 8] = 0 reserved// Bit [ 7: 5] = upstream interrupt status// Bit      4  = 0 reserved// Bit [ 3: 1] = downstream interrupt status// Bit      0  = underrun error//module esp #(	     parameter        dram_bits = 25,	     parameter [31:0] dram_base = 32'h40000000	     ) (		input 	      rst_n,		input 	      sys_clk,		input 	      sdram_clk,		input 	      cpu_valid,		input [4:0]   cpu_addr,		input [3:0]   cpu_wstrb,		input [31:0]  cpu_wdata,		output [31:0] cpu_rdata,		output reg    irq,			      			      dram_bus.dstr dram, 		output reg    esp_int,		input 	      spi_clk,		inout [1:0]   spi_io,		input 	      spi_cs_n		);   reg  [31:0] 		      mem_addr = 'b0;   wire [31:0] 		      mem_addr_mask = (1'b1 << dram_bits) - 3'd4;   wire [31:0] 		      mem_addr_out = (mem_addr & mem_addr_mask)			      | dram_base;   reg 			      mem_valid;   reg [31:0] 		      mem_wdata;   wire 		      mem_write;   reg [ 3:0] 		      mem_wstrb;   wire 		      mem_ready;   wire [31:0] 		      mem_rdata;   dram_port #(32) mem     (      .bus   ( dram ),      .prio  ( 2'd2 ),      .addr  ( mem_addr[dram_bits-1:0] ),      .valid ( mem_valid ),      .wd    ( mem_wdata ),      .wstrb ( mem_wstrb ),      .ready ( mem_ready ),      .rd    ( mem_rdata )      );      reg [1:0]		  spi_clk_q;   reg 			  spi_cs_n_q;   reg [1:0] 		  spi_io_q;   always @(posedge sdram_clk)     begin	spi_clk_q  <= { spi_clk_q[0], spi_clk };	spi_cs_n_q <= spi_cs_n;	spi_io_q   <= spi_io;     end   typedef enum logic [1:0] {	st_cmd,			// Reading command	st_addr,		// Reading address	st_io			// I/O (including read dummy bits)   } state_t;      state_t    spi_state;   reg [ 4:0] spi_cmd;   reg [31:0] spi_shr;   reg [ 3:0] spi_ctr;   reg [ 3:0] cpu_irq;   reg [ 3:1] cpu_set_irq;	// CPU IRQs to set once idle   reg [ 3:1] spi_irq;   reg [ 3:1] latched_spi_irq;	// SPI IRQ as of transition start   reg [ 1:0] spi_out;   reg 	      spi_oe;   reg [ 2:0] spi_wbe;		// Partial word write byte enables   reg [23:0] spi_wdata;	// Partial word write data   assign spi_io = spi_oe ? spi_out : 2'bzz;   assign mem_write = ~spi_cmd[4];   wire [31:0] spi_indata = { spi_shr[29:0], spi_io_q };   reg 	       cpu_valid_q;   always @(negedge rst_n or posedge sdram_clk)     if (~rst_n)       begin	  spi_state <= st_cmd;	  spi_cmd   <= 'b0;	  spi_ctr   <= 4'd3;	// 8 bits needed for this state	  cpu_irq   <= 'b0;	  cpu_set_irq <= 'b0;	  spi_irq   <= 'b0;	  latched_spi_irq <= 'b0;	  spi_oe    <= 1'b0;	  spi_wbe   <= 3'b0;	  mem_addr  <= 'b0;	  mem_wstrb <= 4'b0;	  mem_valid <= 1'b0;       end     else       begin	  esp_int <= ~|spi_irq;	  if (spi_cs_n_q)	    begin	       spi_state  <= st_cmd;	       spi_ctr    <= 4'd3;	       spi_oe     <= 1'b0;	       if (~mem_valid)		 begin		    cpu_irq <= cpu_irq | { cpu_set_irq, 1'b0 };		    cpu_set_irq <= 'b0;		 end	    end	  else if (spi_clk_q == 2'b01)	    begin	       spi_ctr <= spi_ctr - 1'b1;	       spi_shr <= spi_indata;	       case (spi_ctr)		 4'b1100:		   if (spi_state == st_io && mem_write)		     begin			spi_wbe[0]      <= 1'b1;			spi_wdata[7:0]  <= spi_indata[7:0];		     end		 4'b1000:		   if (spi_state == st_io && mem_write)		     begin			spi_wbe[1]      <= 1'b1;			spi_wdata[15:8] <= spi_indata[7:0];		     end		 4'b0100:		   if (spi_state == st_io && mem_write)		     begin			spi_wbe[2]       <= 1'b1;			spi_wdata[23:16] <= spi_indata[7:0];		     end		 4'b0000: begin		       // Transfer data to/from memory controller, but		       // we have to shuffle endianness...		       if (spi_state == st_io)			 begin			    // Memory output			    spi_shr[31:24]  <= mem_rdata[ 7: 0];			    spi_shr[23:16]  <= mem_rdata[15: 8];			    spi_shr[15: 8]  <= mem_rdata[23:16];			    spi_shr[ 7: 0]  <= mem_rdata[31:24];			 end		       else			 begin			    // Status output			    spi_shr[31:16]  <= mem_addr_out[31:16];			    spi_shr[15: 8]  <= 8'h80;			    spi_shr[ 7: 4]  <= { latched_spi_irq, 1'b0 };			    spi_shr[ 3: 0]  <= cpu_irq;			 end // else: !if(spi_state == st_io)		       if (mem_valid && spi_state != st_cmd)			 cpu_irq[0] <= 1'b1; // Overrun/underrun		       case (spi_state)			 st_cmd: begin			    spi_cmd         <= spi_indata[5:0];			    spi_state       <= st_addr;			    latched_spi_irq <= spi_irq;			    for (int i = 1; i < 4; i++)			      begin				 if (spi_indata[3:2] == i)				   spi_irq[i] <= 1'b0;				 if (spi_indata[1:0] == i)				   cpu_set_irq[i] <= 1'b1;			      end			 end			 st_addr: begin			    mem_addr  <= spi_indata & mem_addr_mask;			    spi_state <= st_io;			    mem_valid <= ~mem_write;			    mem_wstrb <= 4'b0;			    spi_wbe   <= 3'b000;			    // If the first word is partial, skip ahead			    if (mem_write)			      spi_ctr[3:2] <= ~spi_indata[1:0];			 end			 st_io: begin			    if (mem_write)			      begin				 mem_wdata[23: 0] <= spi_wdata[23:0];				 mem_wdata[31:24] <= spi_indata[7:0];				 mem_wstrb <= { 1'b1, spi_wbe };			      end			    else			      begin				 mem_wstrb <= 4'b0000;			      end // else: !if(mem_write)			    mem_valid <= 1'b1;			    spi_wbe   <= 3'b000;			 end		       endcase		    end // case: 4'b0000		 default:		   ;		// Nothing	       endcase // case (spi_ctr)	    end // if (spi_clk_q == 2'b01)	  else if (spi_clk_q == 2'b10)	    begin	       spi_out <= spi_shr[31:30];	       spi_oe  <= (spi_state == st_io) & ~mem_write;	    end	  if (mem_valid & mem_ready)	    begin	       mem_addr  <= mem_addr + 3'd4;	       mem_valid <= 1'b0;	    end	  if (spi_state != st_io & ~mem_valid & |spi_wbe)	    begin	       // Complete a partial write terminated by CS#	       mem_valid        <= 1'b1;	       mem_wstrb        <= { 1'b0, spi_wbe };	       mem_wdata[23:0]  <= spi_wdata[23:0];	       mem_wdata[31:24] <= 8'hxx;	       spi_wbe          <= 3'b000;	    end	  cpu_valid_q <= cpu_valid;	  if (cpu_valid & ~cpu_valid_q & cpu_wstrb[0])	    case (cpu_addr[1:0])	      2'b00:		cpu_irq <= cpu_wdata[3:0];	      2'b01:		for (int i = 0; i < 4; i++)		  if (cpu_wdata[i])		    cpu_irq[i] <= 1'b0;	      2'b10:		spi_irq <= cpu_wdata[3:1];	      2'b11:		for (int i = 1; i < 4; i++)		  if (cpu_wdata[i])		    spi_irq[i] <= 1'b1;	    endcase // case (cpu_addr[1:0])       end // else: !if(~rst_n)   always @(posedge sys_clk)     irq <= |cpu_irq;      always @(*)     casez (cpu_addr[1:0])       2'b0?:	 cpu_rdata = { 28'b0, cpu_irq };       2'b1?:	 cpu_rdata = { 28'b0, spi_irq, 1'b0 };     endcase // casez (cpu_addr[1:0])endmodule // esp		      	   
 |