//
// Top level module for the FPGA on the MAX80 board by
// Per Mårtensson and H. Peter Anvin
//
// This is for MAX80 as target on the ABC-bus.
//

// Sharing JTAG pins (via JTAGEN)
`undef SHARED_JTAG

module max80 (
	      // Clock oscillator
	      input	    clock_48, // 48 MHz

	      // ABC-bus
	      input	    abc_clk, // ABC-bus 3 MHz clock
	      input [15:0]  abc_a, // ABC address bus
	      inout [7:0]   abc_d, // ABC data bus
	      output	    abc_d_oe, // Data bus output enable
	      input	    abc_rst_n, // ABC bus reset strobe
	      input	    abc_cs_n, // ABC card select strobe
	      input [4:0]   abc_out_n, // OUT, C1-C4 strobe
	      input [1:0]   abc_inp_n, // INP, STATUS strobe
	      input	    abc_xmemfl_n, // Memory read strobe
	      input	    abc_xmemw800_n, // Memory write strobe (ABC800)
	      input	    abc_xmemw80_n, // Memory write strobe (ABC80)
	      input	    abc_xinpstb_n, // I/O read strobe (ABC800)
	      input	    abc_xoutpstb_n, // I/O write strobe (ABC80)
	      // The following are inverted versus the bus IF
	      // the corresponding MOSFETs are installed
	      output	    abc_rdy_x, // RDY = WAIT#
	      output	    abc_resin_x, // System reset request
	      output	    abc_int80_x, // System INT request (ABC80)
	      output	    abc_int800_x, // System INT request (ABC800)
	      output	    abc_nmi_x, // System NMI request (ABC800)
	      output	    abc_xm_x, // System memory override (ABC800)
	      // Host/device control
	      output	    abc_host, // 1 = host, 0 = target
	      output	    abc_a_oe,
	      // Bus isolation
	      output	    abc_d_ce_n,

	      // ABC-bus extension header
	      // (Note: cannot use an array here because HC and HH are
	      // input only.)
	      inout	    exth_ha,
	      inout	    exth_hb,
	      input	    exth_hc,
	      inout	    exth_hd,
	      inout	    exth_he,
	      inout	    exth_hf,
	      inout	    exth_hg,
	      input	    exth_hh,

	      // SDRAM bus
	      output	    sr_clk,
	      output	    sr_cke,
	      output [1:0]  sr_ba, // Bank address
	      output [12:0] sr_a, // Address within bank
	      inout [15:0]  sr_dq, // Also known as D or IO
	      output [1:0]  sr_dqm, // DQML and DQMH
	      output	    sr_cs_n,
	      output	    sr_we_n,
	      output	    sr_cas_n,
	      output	    sr_ras_n,

	      // SD card
	      output	    sd_clk,
	      output	    sd_cmd,
	      inout [3:0]   sd_dat,

	      // USB serial (naming is FPGA as DCE)
	      input	    tty_txd,
	      output	    tty_rxd,
	      input	    tty_rts,
	      output	    tty_cts,
	      input	    tty_dtr,

	      // SPI flash memory (also configuration)
	      output	    flash_cs_n,
	      output	    flash_sck,
	      inout [1:0]   flash_io,

	      // SPI bus (connected to ESP32 so can be bidirectional)
	      inout	    spi_clk,
	      inout	    spi_miso,
	      inout	    spi_mosi,
	      inout	    spi_cs_esp_n, // ESP32 IO10
	      inout	    spi_cs_flash_n, // ESP32 IO01

	      // Other ESP32 connections
	      inout	    esp_io0, // ESP32 IO00
	      inout	    esp_int, // ESP32 IO09

	      // I2C bus (RTC and external)
	      inout	    i2c_scl,
	      inout	    i2c_sda,
	      input	    rtc_32khz,
	      input	    rtc_int_n,

	      // LED (2 = D23/G, 1 = D22/R, 0 = D17/B)
	      output [2:0]  led,

	      // GPIO pins
	      inout [5:0]   gpio,

	      // HDMI
	      output [2:0]  hdmi_d,
	      output	    hdmi_clk,
	      inout	    hdmi_scl,
	      inout	    hdmi_sda,
	      inout	    hdmi_hpd
	      );

   // PLL and reset
   parameter reset_pow2 = 12; // Assert internal reset for 4096 cycles after PLL lock
   reg			    rst_n   = 1'b0;	// Internal reset
   wire [1:0]		    pll_locked;

   // Clocks
   wire	    sdram_clk;		// SDRAM clock
   wire	    sdram_out_clk;	// SDRAM clock, phase shifted
   wire	    sys_clk;		// System clock
   wire	    vid_clk;		// Video pixel clock
   wire	    vid_hdmiclk;	// D:o in the HDMI clock domain
   wire     flash_clk;		// Serial flash ROM clock

   reg	    reset_cmd_q = 1'b0;
   wire     reset_cmd;

   pll pll (
	    .areset ( reset_cmd_q ),
	    .inclk0 ( clock_48 ),
	    .c0 ( sdram_out_clk ),	// SDRAM external clock (168 MHz)
	    .c1 ( sys_clk ),		// System clock (84 MHz)
	    .c2 ( vid_clk ),		// Video pixel clock (56 MHz)
	    .c3 ( flash_clk ),		// Serial flash ROM clock (134 MHz)
	    .c4 ( sdram_clk ),		// SDRAM internal clock (168 MHz)
	    .locked ( pll_locked[0] ),
	    .phasestep ( 1'b0 ),
	    .phasecounterselect ( 3'b0 ),
	    .phaseupdown ( 1'b1 ),
	    .scanclk ( 1'b0 ),
	    .phasedone ( )
	    );

   wire all_plls_locked = &pll_locked;

   // sys_clk pulse generation of various powers of two
   // Also used to generate rst_n
   reg [23:1] sys_clk_ctr;
   reg [23:1] sys_clk_ctr_q;
   reg [23:1] sys_clk_stb;

   always @(negedge all_plls_locked or posedge sys_clk)
     if (~&all_plls_locked)
       begin
	  rst_n         <= 1'b0;
	  reset_cmd_q   <= 1'b0;
	  sys_clk_ctr   <= 1'b0;
	  sys_clk_ctr_q <= 1'b0;
	  sys_clk_stb   <= 1'b0;
       end
     else
       begin
	  sys_clk_ctr   <= sys_clk_ctr + 1'b1;
	  sys_clk_ctr_q <= sys_clk_ctr;
	  sys_clk_stb   <= ~sys_clk_ctr & sys_clk_ctr_q;
	  reset_cmd_q   <= rst_n & (reset_cmd_q | reset_cmd);
	  rst_n         <= rst_n | sys_clk_stb[reset_pow2];
       end

   // Unused device stubs - remove when used

   // Reset in the video clock domain
   reg vid_rst_n;
   always @(negedge all_plls_locked or posedge vid_clk)
     if (~all_plls_locked)
       vid_rst_n <= 1'b0;
     else
       vid_rst_n <= rst_n;

   // HDMI video interface
   video video (
		.rst_n      ( vid_rst_n ),
		.vid_clk    ( vid_clk ),
		.pll_locked ( pll_locked ),

		.hdmi_d   ( hdmi_d ),
		.hdmi_clk ( hdmi_clk ),
		.hdmi_scl ( hdmi_scl ),
		.hdmi_hpd ( hdmi_hpd )
		);

   //
   // Internal CPU bus
   //
   wire			      cpu_mem_valid;
   wire			      cpu_mem_instr;
   wire [ 3:0]		      cpu_mem_wstrb;
   wire [31:0]                cpu_mem_addr;
   wire [31:0]                cpu_mem_wdata;
   reg  [31:0]		      cpu_mem_rdata;
   reg			      cpu_mem_ready;

   wire                       cpu_la_read;
   wire                       cpu_la_write;
   wire [31:0]                cpu_la_addr;
   wire [31:0]		      cpu_la_wdata;
   wire [ 3:0]		      cpu_la_wstrb;

   // cpu_mem_valid by address quadrant
   wire [ 3:0] cpu_mem_quad = cpu_mem_valid << cpu_mem_addr[31:30];

   // I/O device map from iodevs.conf
   wire        iodev_mem_valid = cpu_mem_quad[3];
`include "iodevs.vh"

   //
   // SDRAM
   //
   localparam dram_port_count = 2;
   dram_bus sr_bus[1:dram_port_count] ( );

   // ABC interface
   wire [24:0] abc_sr_addr;
   wire [ 7:0] abc_sr_rd;
   wire        abc_sr_valid;
   wire        abc_sr_ready;
   wire [ 7:0] abc_sr_wd;
   wire        abc_sr_wstrb;

   dram_port #(8)
   abc_dram_port (
		  .bus   ( sr_bus[1] ),
		  .prio  ( 2'd3 ),
		  .addr  ( abc_sr_addr ),
		  .rd    ( abc_sr_rd ),
		  .valid ( abc_sr_valid ),
		  .ready ( abc_sr_ready ),
		  .wd    ( abc_sr_wd ),
		  .wstrb ( abc_sr_wstrb )
		  );

   // CPU interface
   wire [31:0] sdram_mem_rdata;
   wire        sdram_ready;
   reg	       sdram_mem_ready;

   // Retard sdram_ready by one sys_clk (multicycle path for the data,
   // see max80.sdc)
   always @(posedge sys_clk)
     sdram_mem_ready <= sdram_ready;

   dram_port #(32)
   cpu_dram_port (
		  .bus   ( sr_bus[2] ),
		  .prio  ( 2'd1 ),
		  .addr  ( cpu_mem_addr[24:0] ),
		  .rd    ( sdram_mem_rdata ),
		  .valid ( cpu_mem_quad[1] ),
		  .ready ( sdram_ready ),
		  .wd    ( cpu_mem_wdata ),
		  .wstrb ( cpu_mem_wstrb )
		  );

   // Romcopy interface
   wire [15:0] sdram_rom_wd;
   wire [24:1] sdram_rom_waddr;
   wire [ 1:0] sdram_rom_wrq;
   wire        sdram_rom_wacc;

   sdram #(.port1_count(dram_port_count))
   sdram (
	  .rst_n    ( rst_n ),
	  .clk      ( sdram_clk ), // Internal clock
	  .out_clk  ( sdram_out_clk ), // External clock (phase shifted)

	  .sr_clk   ( sr_clk ),    // Output clock buffer
	  .sr_cke   ( sr_cke ),
	  .sr_cs_n  ( sr_cs_n ),
	  .sr_ras_n ( sr_ras_n ),
	  .sr_cas_n ( sr_cas_n ),
	  .sr_we_n  ( sr_we_n ),
	  .sr_dqm   ( sr_dqm ),
	  .sr_ba    ( sr_ba ),
	  .sr_a     ( sr_a ),
	  .sr_dq    ( sr_dq ),

	  .port1    ( sr_bus ),

	  .a2       ( sdram_rom_waddr ),
	  .wd2      ( sdram_rom_wd ),
	  .wrq2     ( sdram_rom_wrq ),
	  .wacc2    ( sdram_rom_wacc )
	  );

   //
   // ABC-bus interface
   //
   abcbus abcbus (
		  .rst_n ( rst_n ),
		  .sys_clk ( sys_clk ),
		  .sdram_clk ( sdram_clk ),
		  .stb_1mhz ( sys_clk_stb[6] ),

		  .abc_valid ( iodev_valid_abc ),
		  .map_valid ( iodev_valid_abcmemmap ),
		  .cpu_addr  ( cpu_mem_addr ),
		  .cpu_wdata ( cpu_mem_wdata ),
		  .cpu_wstrb ( cpu_mem_wstrb ),
		  .cpu_rdata ( iodev_rdata_abc ),
		  .cpu_rdata_map ( iodev_rdata_abcmemmap ),
		  .irq       ( iodev_irq_abc ),

		  .abc_clk   ( abc_clk ),
		  .abc_a     ( abc_a ),
		  .abc_d     ( abc_d ),
		  .abc_d_oe  ( abc_d_oe ),
		  .abc_rst_n ( abc_rst_n ),
		  .abc_cs_n  ( abc_cs_n ),
		  .abc_out_n ( abc_out_n ),
		  .abc_inp_n ( abc_inp_n ),
		  .abc_xmemfl_n ( abc_xmemfl_n ),
		  .abc_xmemw800_n ( abc_xmemw800_n ),
		  .abc_xmemw80_n ( abc_xmemw80_n ),
		  .abc_xinpstb_n ( abc_xinpstb_n ),
		  .abc_xoutpstb_n ( abc_xoutpstb_n ),
		  .abc_rdy_x ( abc_rdy_x ),
		  .abc_resin_x ( abc_resin_x ),
		  .abc_int80_x ( abc_int80_x ),
		  .abc_int800_x ( abc_int800_x ),
		  .abc_nmi_x ( abc_nmi_x ),
		  .abc_xm_x ( abc_xm_x ),
		  .abc_host ( abc_host ),
		  .abc_a_oe ( abc_a_oe ),
		  .abc_d_ce_n ( abc_d_ce_n ),

		  .exth_ha ( exth_ha ),
		  .exth_hb ( exth_hb ),
		  .exth_hc ( exth_hc ),
		  .exth_hd ( exth_hd ),
		  .exth_he ( exth_he ),
		  .exth_hf ( exth_hf ),
		  .exth_hg ( exth_hg ),
		  .exth_hh ( exth_hh ),

		  .sdram_addr  ( abc_sr_addr ),
		  .sdram_rd    ( abc_sr_rd ),
		  .sdram_valid ( abc_sr_valid ),
		  .sdram_ready ( abc_sr_ready ),
		  .sdram_wd    ( abc_sr_wd ),
		  .sdram_wstrb ( abc_sr_wstrb )
		  );

   // GPIO
   assign gpio    = 6'bzzzzzz;

   // Embedded RISC-V CPU
   parameter cpu_fast_mem_bits = SRAM_BITS-2; /* 2^[this] * 4 bytes */

   // Edge-triggered IRQs. picorv32 latches interrupts
   // but doesn't edge detect for a slow signal, so do it
   // here instead and use level triggered signalling to the
   // CPU.
   wire [31:0] cpu_eoi;
   reg  [31:0] cpu_eoi_q;

   // sys_irq defined in iodevs.vh
   reg  [31:0] sys_irq_q;
   reg  [31:0] cpu_irq;

   // CPU permanently hung?
   wire	       cpu_trap;

   always @(negedge rst_n or posedge sys_clk)
     if (~rst_n)
       begin
	  sys_irq_q <= 32'b0;
	  cpu_eoi_q <= 32'b0;
	  cpu_irq   <= 32'b0;
       end
     else
       begin
	  sys_irq_q <= sys_irq & irq_edge_mask;
	  cpu_eoi_q <= cpu_eoi & irq_edge_mask;

	  cpu_irq <= (sys_irq & ~sys_irq_q)
	    | (cpu_irq & irq_edge_mask & ~(cpu_eoi & ~cpu_eoi_q));
       end

   picorv32 #(
	      .ENABLE_COUNTERS ( 1 ),
	      .ENABLE_COUNTERS64 ( 1 ),
	      .ENABLE_REGS_16_31 ( 1 ),
	      .ENABLE_REGS_DUALPORT ( 1 ),
	      .LATCHED_MEM_RDATA ( 1 ),
	      .BARREL_SHIFTER ( 1 ),
	      .TWO_CYCLE_COMPARE ( 0 ),
	      .TWO_CYCLE_ALU ( 0 ),
	      .COMPRESSED_ISA ( 1 ),
	      .CATCH_MISALIGN ( 1 ),
	      .CATCH_ILLINSN ( 1 ),
	      .ENABLE_FAST_MUL ( 1 ),
	      .ENABLE_DIV ( 1 ),
	      .ENABLE_IRQ ( 1 ),
	      .ENABLE_IRQ_QREGS ( 1 ),
	      .ENABLE_IRQ_TIMER ( 1 ),
	      .MASKED_IRQ ( irq_masked ),
	      .LATCHED_IRQ ( 32'h0000_0007 ),
	      .REGS_INIT_ZERO ( 1 ),
	      .STACKADDR ( 32'h4 << cpu_fast_mem_bits )
	      )

   cpu (
	.clk ( sys_clk ),
	.resetn ( rst_n ),
	.trap ( cpu_trap ),

	.progaddr_reset ( _PC_RESET ),
	.progaddr_irq   ( _PC_IRQ ),

	.mem_instr ( cpu_mem_instr ),
	.mem_ready ( cpu_mem_ready ),
	.mem_valid ( cpu_mem_valid ),
	.mem_wstrb ( cpu_mem_wstrb ),
	.mem_addr  ( cpu_mem_addr ),
	.mem_wdata ( cpu_mem_wdata ),
	.mem_rdata ( cpu_mem_rdata ),

	.mem_la_read  ( cpu_la_read ),
	.mem_la_write ( cpu_la_write ),
	.mem_la_wdata ( cpu_la_wdata ),
	.mem_la_addr  ( cpu_la_addr ),
	.mem_la_wstrb ( cpu_la_wstrb ),

	.irq ( cpu_irq ),
	.eoi ( cpu_eoi )
	);

   // Add a mandatory wait state to iodevs to reduce the size
   // of the CPU memory input MUX (it hurts timing on memory
   // accesses...)
   reg	       iodev_mem_ready;

   always @(*)
     case ( cpu_mem_quad )
       4'b0000: cpu_mem_ready = 1'b0;
       4'b0001: cpu_mem_ready = 1'b1;
       4'b0010: cpu_mem_ready = sdram_mem_ready;
       4'b0100: cpu_mem_ready = 1'b1;
       4'b1000: cpu_mem_ready = iodev_mem_ready;
       default: cpu_mem_ready = 1'bx;
     endcase // case ( mem_quad )

   //
   // Fast memory. This runs on the SDRAM clock, i.e. 2x the speed
   // of the CPU. The .bits parameter gives the number of dwords
   // as a power of 2, i.e. 11 = 2^11 * 4 = 8K.
   //
   wire [31:0] fast_mem_rdata;

   fast_mem // #(.bits(cpu_fast_mem_bits), .mif("../fw/boot"))
   fast_mem(
	    .rst_n ( rst_n ),
	    .clk   ( sys_clk ),
	    .read  ( cpu_la_read  & cpu_la_addr[31:30] == 2'b00 ),
	    .write ( cpu_la_write & cpu_la_addr[31:30] == 2'b00 ),
	    .wstrb ( cpu_la_wstrb ),
	    .addr  ( cpu_la_addr[14:2] ),
	    .wdata ( cpu_la_wdata ),
	    .rdata ( fast_mem_rdata )
	    );

   // Register I/O data to reduce the size of the read data MUX
   reg [31:0]  iodev_rdata_q;

   // Read data MUX
   always_comb
     case ( cpu_mem_quad )
       4'b0001: cpu_mem_rdata = fast_mem_rdata;
       4'b0010: cpu_mem_rdata = sdram_mem_rdata;
       4'b1000: cpu_mem_rdata = iodev_rdata_q;
       default: cpu_mem_rdata = 32'hxxxx_xxxx;
     endcase

   // Miscellaneous system control/status registers
   wire [ 4:0] sysreg_subreg = cpu_mem_addr[6:2];
   wire [31:0] sysreg = iodev_valid_sys << sysreg_subreg;
   tri1 [31:0] sysreg_rdata[0:31];
   assign iodev_rdata_sys = sysreg_rdata[sysreg_subreg];

   //
   // Board identification
   //
   // Magic number:   "MAX8"
   // Board revision: 1.0
   // Board rework flags:
   //    [0] - RTC 32 kHz pullup and serial port RxD/TxD swap
   // [15:1] - reserved
   //
   wire        rtc_32khz_rework = 1'b1;

   wire [ 7:0] max80_major = 8'd1;
   wire [ 7:0] max80_minor = 8'd0;
   wire [15:0] max80_fixes = { 14'b0, rtc_32khz_rework }; // Workarounds

   assign sysreg_rdata[0] = SYS_MAGIC_MAX80;
   assign sysreg_rdata[1] = { max80_major, max80_minor, max80_fixes };

   // Hard system reset under program control
   assign reset_cmd =
		     (sysreg[3] & cpu_mem_wstrb[0] & cpu_mem_wdata[0])
		     | cpu_trap; // CPU hung


  // LED indication from the CPU
   reg [2:0]   led_q;
   always @(negedge rst_n or posedge sys_clk)
     if (~rst_n)
       led_q <= 3'b000;
     else
       if ( sysreg[2] & cpu_mem_wstrb[0] )
	 led_q <= cpu_mem_wdata[2:0];

   assign led = led_q;
   assign sysreg_rdata[2] = { 29'b0, led_q };

   //
   // Serial ROM (also configuration ROM.) Fast hardwired data download
   // unit to SDRAM.
   //
   wire        rom_done;
   reg	       rom_done_q;

   spirom ddu (
	       .rst_n    ( rst_n ),
	       .rom_clk  ( flash_clk ),
	       .ram_clk  ( sdram_clk ),
	       .sys_clk  ( sys_clk ),

	       .spi_sck  ( flash_sck ),
	       .spi_io   ( flash_io ),
	       .spi_cs_n ( flash_cs_n ),

	       .wd       ( sdram_rom_wd ),
	       .waddr    ( sdram_rom_waddr ),
	       .wrq      ( sdram_rom_wrq ),
	       .wacc     ( sdram_rom_wacc ),

	       .cpu_rdata ( iodev_rdata_romcopy ),
	       .cpu_wdata ( cpu_mem_wdata ),
	       .cpu_valid ( iodev_valid_romcopy ),
	       .cpu_wstrb ( cpu_mem_wstrb ),
	       .cpu_addr  ( cpu_mem_addr[3:2] ),
	       .irq       ( iodev_irq_romcopy )
	       );

   //
   // Serial port. Direct to the CP2102N for reworked
   // boards or to GPIO for non-reworked boards, depending on
   // whether DTR# is asserted on either.
   //
   // The GPIO numbering matches the order of pins for FT[2]232H.
   // gpio[0] - TxD
   // gpio[1] - RxD
   // gpio[2] - RTS#
   // gpio[3] - CTS#
   // gpio[4] - DTR#
   //
   wire        tty_data_out;	// Output data
   wire        tty_data_in;	// Input data
   wire        tty_cts_out;	// Assert CTS# externally
   wire        tty_rts_in;	// RTS# received from outside

   assign tty_cts_out  = 1'b0;	// Assert CTS#
   tty console (
	    .rst_n ( rst_n ),
	    .clk   ( sys_clk ),

	    .valid ( iodev_valid_console ),
	    .wstrb ( cpu_mem_wstrb ),
	    .wdata ( cpu_mem_wdata ),
	    .rdata ( iodev_rdata_console ),
	    .addr  ( cpu_mem_addr[3:2] ),
	    .irq   ( iodev_irq_console ),

	    .tty_txd ( tty_data_out ) // DTE -> DCE
	    );

   max80_usb usb (
		  .rst_n   ( rst_n ),
		  .clock48 ( clock_48 ),
		  .tty_rxd ( ),
		  .tty_txd ( tty_data_out ),
		  .usb_dp  ( gpio[3] ),
		  .usb_dn  ( gpio[5] ),
		  .usb_pu  ( gpio[1] )
		  );

   assign tty_data_in      = tty_txd;
   assign tty_rxd          = tty_data_out;
   assign tty_rts_in       = tty_rts;
   assign tty_cts          = tty_cts_out;

   // SD card
   sdcard #(
	    .with_irq_mask ( 8'b0000_0001 )
	    )
   sdcard (
	   .rst_n   ( rst_n ),
	   .clk     ( sys_clk ),
	   .sd_cs_n ( sd_dat[3] ),
	   .sd_di   ( sd_cmd ),
	   .sd_sclk ( sd_clk ),
	   .sd_do   ( sd_dat[0] ),
	   .sd_cd_n ( 1'b0 ),
	   .sd_irq_n ( 1'b1 ),

	   .wdata   ( cpu_mem_wdata ),
	   .rdata   ( iodev_rdata_sdcard ),
	   .valid   ( iodev_valid_sdcard ),
	   .wstrb   ( cpu_mem_wstrb ),
	   .addr    ( cpu_mem_addr[6:2] ),
	   .wait_n  ( iodev_wait_n_sdcard ),
	   .irq     ( iodev_irq_sdcard )
	   );
   assign sd_dat[2:1] = 2'bzz;

   // System local clock (not an RTC, but settable from one)
   // Also provides a periodic interrupt (set to 32 Hz)
   wire clk_32kHz = ~rtc_32khz;

   sysclock #(.PERIODIC_HZ_LG2 ( TIMER_SHIFT ))
   sysclock (
		      .rst_n ( rst_n ),
		      .sys_clk ( sys_clk ),
		      .rtc_clk ( clk_32kHz ),

		      .wdata   ( cpu_mem_wdata ),
		      .rdata   ( iodev_rdata_sysclock ),
		      .valid   ( iodev_valid_sysclock ),
		      .wstrb   ( cpu_mem_wstrb ),
		      .addr    ( cpu_mem_addr[2] ),

		      .periodic ( iodev_irq_sysclock )
		      );

   // SPI bus to ESP32; using the sdcard IP as a SPI master for now at
   // least...
`ifdef REALLY_ESP32
   // ESP32
   assign spi_cs_flash_n = 1'bz;
   assign esp_io0  = 1'b1;	 // If pulled down on reset, ESP32 will enter
				 // firmware download mode

   sdcard #(
	    .with_irq_mask ( 8'b0000_0101 ),
	    .with_crc7     ( 1'b0 ),
	    .with_crc16    ( 1'b0 )
	    )
   esp (
	.rst_n    ( rst_n ),

	.clk      ( sys_clk ),
	.sd_cs_n  ( spi_cs_esp_n ),
	.sd_di    ( spi_mosi ),
	.sd_sclk  ( spi_clk ),
	.sd_do    ( spi_miso ),
	.sd_cd_n  ( 1'b0 ),
	.sd_irq_n ( esp_int ),

	.wdata  ( cpu_mem_wdata ),
	.rdata  ( iodev_rdata_esp ),
	.valid  ( iodev_valid_esp ),
	.wstrb  ( cpu_mem_wstrb ),
	.addr   ( cpu_mem_addr[6:2] ),
	.wait_n ( iodev_wait_n_esp ),
	.irq	( iodev_irq_esp )
	);
`else // !`ifdef REALLY_ESP32
   reg [5:-13] esp_ctr;		// 32768 * 2^-13 = 4 Hz

   always @(posedge clk_32kHz)
     esp_ctr <= esp_ctr + 1'b1;

   assign spi_clk        = esp_ctr[0];
   assign spi_mosi       = esp_ctr[1];
   assign spi_miso       = esp_ctr[2];
   assign spi_cs_flash_n = esp_ctr[3]; // IO01
   assign spi_cs_esp_n   = esp_ctr[4]; // IO10
   assign spi_int        = esp_ctr[5]; // IO09
   assign esp_io0        = 1'b1;

`endif

   //
   // I2C bus (RTC and to connector)
   //
   i2c i2c (
	    .rst_n ( rst_n ),
	    .clk ( sys_clk ),

	    .valid ( iodev_valid_i2c ),
	    .addr  ( cpu_mem_addr[3:2] ),
	    .wdata ( cpu_mem_wdata ),
	    .wstrb ( cpu_mem_wstrb ),
	    .rdata ( iodev_rdata_i2c ),
	    .irq ( iodev_irq_i2c ),

	    .i2c_scl ( i2c_scl ),
	    .i2c_sda ( i2c_sda )
	    );

   //
   // Registering of I/O data and handling of iodev_mem_ready
   //
   always @(posedge sys_clk)
     iodev_rdata_q <= iodev_rdata;

   always @(negedge rst_n or posedge sys_clk)
     if (~rst_n)
       iodev_mem_ready <= 1'b0;
     else
       iodev_mem_ready <= iodev_wait_n & cpu_mem_valid;

endmodule
