| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 | 
							- //
 
- // 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] = 16 LSB of 32 kHz RTC counter
 
- // Bit [15:12] = 4'b1001
 
- // Bit [11: 8] = 0 reserved
 
- // Bit [ 7: 5] = upstream interrupts pending
 
- // Bit      4  = downstream writes enabled
 
- // Bit [ 3: 1] = downstream interrupts pending
 
- // Bit      0  = underrun error
 
- //
 
- // The following CPU registers are defined:
 
- //
 
- // 0	       = status bits [3:0] (downstream)
 
- // 1	       = write-1-clear of status bits [3:0]
 
- // 2           = status bits [7:4] (upstream)
 
- // 3           = write-1-set of status bits [7:4]
 
- //
 
- 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,
 
- 		input [15:0]  rtc_ctr
 
- 		);
 
-    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_dead,		// Do nothing until CS# deasserted
 
- 	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:0] cpu_set_irq;	// CPU IRQs to set once idle
 
-    reg        cpu_send_irq;	// Ready to set CPU IRQs
 
-    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
 
-    reg	      spi_wr_en;	// SPI writes enabled by CPU
 
-    reg	      spi_mem_en;	// Read, or write enabled at start of trans
 
-    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_dead;
 
- 	  spi_cmd         <= 'b0;
 
- 	  spi_ctr         <= 4'd3;	// 8 bits needed for this state
 
- 	  cpu_irq         <= 'b0;
 
- 	  cpu_set_irq     <= 'b0;
 
- 	  cpu_send_irq    <= 1'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;
 
- 	  spi_wr_en       <= 1'b0;
 
- 	  spi_mem_en      <= 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;
 
- 	       cpu_send_irq <= |cpu_set_irq;
 
- 	    end
 
- 	  if (cpu_send_irq & ~mem_valid & ~|spi_wbe)
 
- 	    begin
 
- 	       cpu_irq      <= cpu_irq | cpu_set_irq;
 
- 	       cpu_set_irq  <= 'b0;
 
- 	       cpu_send_irq <= 1'b0;
 
- 	    end
 
- 	  if (~spi_cs_n_q && 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
 
- 		       // Load spi_shr, but account for endianness here...
 
- 		       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:28]  <= { latched_spi_irq, spi_wr_en };
 
- 			    spi_shr[27:24]  <= cpu_irq;
 
- 			    spi_shr[23:16]  <= 8'b1001_0000;
 
- 			    spi_shr[15: 8]  <= rtc_ctr[ 7: 0];
 
- 			    spi_shr[ 7: 0]  <= rtc_ctr[15: 8];
 
- 			 end // else: !if(spi_state == st_io)
 
- 		       if (mem_valid && spi_state != st_cmd)
 
- 			 begin
 
- 			    cpu_irq[0] <= 1'b1; // Overrun/underrun
 
- 			    spi_wr_en  <= 1'b0; // Block further memory writes
 
- 			    spi_mem_en <= ~mem_write;
 
- 			 end
 
- 		       case (spi_state)
 
- 			 st_dead: begin
 
- 			    // Do nothing
 
- 			 end
 
- 			 st_cmd: begin
 
- 			    spi_cmd         <= spi_indata[5:0];
 
- 			    spi_state       <= st_addr;
 
- 			    latched_spi_irq <= spi_irq;
 
- 			    spi_mem_en      <= ~mem_write | spi_wr_en;
 
- 			    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 <= spi_mem_en;
 
- 			    spi_wbe   <= 3'b000;
 
- 			 end
 
- 		       endcase
 
- 		    end // case: 4'b0000
 
- 		 default: begin
 
- 		    // Do nothing
 
- 		 end
 
- 	       endcase // case (spi_ctr)
 
- 	    end // if (spi_clk_q == 2'b01)
 
- 	  else if (~spi_cs_n_q && 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        <= spi_mem_en;
 
- 	       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] <= cpu_send_irq & cpu_set_irq[i];
 
- 	      2'b10:
 
- 		 { spi_irq, spi_wr_en } <= cpu_wdata[3:0];
 
- 	      2'b11: begin
 
- 		if (cpu_wdata[0])
 
- 		  spi_wr_en <= 1'b1;
 
- 		for (int i = 1; i < 4; i++)
 
- 		  if (cpu_wdata[i])
 
- 		    spi_irq[i] <= 1'b1;
 
- 	      end
 
- 	    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, spi_wr_en };
 
-      endcase // casez (cpu_addr[1:0])
 
- endmodule // esp
 
 
  |