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

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

module max80
#(
  // Pull-up installed on RTC 32 kHz line
  parameter rtc_32khz_rework = 1'b0
) (
	      // 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 reg    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_master, // 1 = host, 0 = device
	      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
	      );

   // Set if MOSFETs Q1-Q6 are installed rather than the corresponding
   // resistors.
   parameter [6:1] mosfet_installed = 6'b000_000;

   // 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 (48 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 - generate random data to give Quartus something to do
   reg [23:0] dummydata = 30'hc8_fb87;

   always @(posedge vid_clk)
     dummydata <= { dummydata[22:0], dummydata[23] };

   wire [7:0] hdmi_data[3];
   wire [9:0] hdmi_tmds[3];
   wire [29:0] hdmi_to_tx;

   assign hdmi_data[0] = dummydata[7:0];
   assign hdmi_data[1] = dummydata[15:8];
   assign hdmi_data[2] = dummydata[23:16];

   generate
      genvar   i;
      for (i = 0; i < 3; i = i + 1)
	begin : hdmitmds
	   tmdsenc enc (
		    .rst_n ( vid_rst_n ),
		    .clk ( vid_clk ),
		    .den ( 1'b1 ),
		    .d ( hdmi_data[i] ),
		    .c ( 2'b00 ),
		    .q ( hdmi_tmds[i] )
		    );
	end
   endgenerate

   assign hdmi_scl = 1'bz;
   assign hdmi_sda = 1'bz;
   assign hdmi_hpd = 1'bz;

   //
   // The ALTLVDS_TX megafunctions is MSB-first and in time-major order.
   // However, TMDS is LSB-first, and we have three TMDS words that
   // concatenate in word(channel)-major order.
   //
   transpose #(.words(3), .bits(10), .reverse_b(1),
	       .reg_d(0), .reg_q(0)) hdmitranspose
     (
      .clk ( vid_clk ),
      .d ( { hdmi_tmds[2], hdmi_tmds[1], hdmi_tmds[0] } ),
      .q ( hdmi_to_tx )
      );

   hdmitx hdmitx (
		  .pll_areset ( ~pll_locked[0] ),
		  .tx_in ( hdmi_to_tx ),
		  .tx_inclock ( vid_clk ),
		  .tx_coreclock ( vid_hdmiclk ), // Pixel clock in HDMI domain
		  .tx_locked ( pll_locked[1] ),
		  .tx_out ( hdmi_d ),
		  .tx_outclock ( hdmi_clk )
		  );

   //
   // ABC bus basic interface
   //
   assign abc_master = 1'b0;	// Only device mode supported
   assign abc_d_ce_n = 1'b0;	// Do not isolate busses

   // On ABC800, only one of XINPSTB# or XOUTPSTB# will be active;
   // on ABC80 they will either be 00 or ZZ; in the latter case pulled
   // low by external resistors.
   wire abc800 = abc_xinpstb_n | abc_xoutpstb_n;
   wire abc80  = ~abc800;

   // Memory read/write strobes
   wire abc_xmemrd = ~abc_xmemfl_n; // For consistency
   wire abc_xmemwr = abc800 ? ~abc_xmemw800_n : ~abc_xmemw80_n;

   // I/O read/write strobes
   wire abc_iord = (abc800 & ~abc_xinpstb_n)  | ~(|abc_inp_n);
   wire abc_iowr = (abc800 & ~abc_xoutpstb_n) | ~(|abc_out_n);

   reg  [7:0] abc_do;
   reg  [7:0] abc_di;
   reg [15:0] abc_a_q;
   assign abc_d    = abc_d_oe ? abc_do : 8'hzz;

   always @(posedge sdram_clk)
     begin
	abc_di  <= abc_d;
	abc_a_q <= abc_a;
     end

   // Open drain signals with optional MOSFETs
   wire abc_wait;
   wire abc_resin;
   wire abc_int;
   wire abc_nmi;
   wire abc_xm;

   function reg opt_mosfet(input signal, input mosfet);
      if (mosfet)
	opt_mosfet = signal;
      else
	opt_mosfet = signal ? 1'b0 : 1'bz;
   endfunction // opt_mosfet

   assign abc_int80_x  = opt_mosfet(abc_int & abc80, mosfet_installed[1]);
   assign abc_rdy_x    = opt_mosfet(abc_wait, mosfet_installed[2]);
   assign abc_nmi_x    = opt_mosfet(abc_nmi, mosfet_installed[3]);
   assign abc_resin_x  = opt_mosfet(abc_resin, mosfet_installed[4]);
   assign abc_int800_x = opt_mosfet(abc_int & abc800, mosfet_installed[5]);
   assign abc_xm_x     = opt_mosfet(abc_xm, mosfet_installed[6]);

   // Detect ABC-bus clock: need a minimum frequency of 84/64 MHz
   // to be considered live...
   reg [2:0] abc_clk_ctr;
   reg [1:0] abc_clk_q;
   reg	     abc_clk_active;
   always @(negedge rst_n or posedge sys_clk)
     if (~rst_n)
       begin
	  abc_clk_q      <= 2'b0;
	  abc_clk_ctr    <= 3'b0;
	  abc_clk_active <= 1'b0;
       end
     else
       begin
	  abc_clk_q <= { abc_clk_q[0], abc_clk };
	  case ( {(abc_clk_q == 2'b10), sys_clk_stb[6]} )
	    5'b10: begin
	       if (abc_clk_ctr == 3'b111)
		 abc_clk_active <= 1'b1;
	       else
		 abc_clk_ctr <= abc_clk_ctr + 1'b1;
	    end
	    5'b01: begin
	       if (abc_clk_ctr == 3'b000)
		 abc_clk_active <= 1'b0;
	       else
		 abc_clk_ctr <= abc_clk_ctr - 1'b1;
	    end
	    default: begin
	       // nothing
	    end
	  endcase // case ( {(abc_clk_q == 2'10), sys_clk_stb[6]} )
       end // else: !if(~rst_n)

   // ABC-bus extension header (exth_c and exth_h are input only)
   // The naming of pins is kind of nonsensical:
   //
   //       +3V3 -  1  2 - +3V3
   //         HA -  3  4 - HE
   //         HB -  5  6 - HG
   //         HC -  7  8 - HH
   //         HD -  9 10 - HF
   //        GND - 11 12 - GND
   //
   // This layout allows the header to be connected on either side
   // of the board. This logic assigns the following names to the pins;
   // if the ext_reversed is set to 1 then the left and right sides
   // are flipped.
   //
   //      +3V3  -  1  2 - +3V3
   //    exth[0] -  3  4 - exth[1]
   //    exth[2] -  5  6 - exth[3]
   //    exth[6] -  7  8 - exth[7]
   //    exth[4] -  9 10 - exth[5]
   //        GND - 11 12 - GND

   wire exth_reversed = 1'b0;
   wire [7:0] exth_d;	// Input data
   wire [5:0] exth_q;	// Output data
   wire [5:0] exth_oe;	// Output enable

   assign exth_d[0]    = exth_reversed ? exth_he : exth_ha;
   assign exth_d[1]    = exth_reversed ? exth_ha : exth_he;
   assign exth_d[2]    = exth_reversed ? exth_hg : exth_hb;
   assign exth_d[3]    = exth_reversed ? exth_hb : exth_hg;
   assign exth_d[4]    = exth_reversed ? exth_hf : exth_hd;
   assign exth_d[5]    = exth_reversed ? exth_hd : exth_hf;
   assign exth_d[6]    = exth_reversed ? exth_hh : exth_hc;
   assign exth_d[7]    = exth_reversed ? exth_hc : exth_hh;

   wire [2:0] erx      = { 2'b00, exth_reversed };
   assign exth_ha      = exth_oe[3'd0 ^ erx] ? exth_q[3'd0 ^ erx] : 1'bz;
   assign exth_he      = exth_oe[3'd1 ^ erx] ? exth_q[3'd1 ^ erx] : 1'bz;
   assign exth_hb      = exth_oe[3'd2 ^ erx] ? exth_q[3'd2 ^ erx] : 1'bz;
   assign exth_hg      = exth_oe[3'd3 ^ erx] ? exth_q[3'd3 ^ erx] : 1'bz;
   assign exth_hd      = exth_oe[3'd4 ^ erx] ? exth_q[3'd4 ^ erx] : 1'bz;
   assign exth_hf      = exth_oe[3'd5 ^ erx] ? exth_q[3'd5 ^ erx] : 1'bz;

   assign exth_q  = 6'b0;
   assign exth_oe = 6'b0;

   //
   // 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;
   wire			      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"

   // ABC SDRAM interface
   reg	       abc_rrq;
   reg	       abc_wrq;
   reg	       abc_xmemrd_q;
   reg	       abc_xmemwr_q;
   reg	       abc_racked;
   reg	       abc_wacked;
   wire [15:0] abc_mempg;
   wire        abc_rden;
   wire        abc_wren;
   reg [7:0]   abc_r_q;


   wire        abc_rack;
   wire        abc_wack;
   wire        abc_rready;
   wire [7:0]  abc_sr_rd;

   //
   // Memory map for ABC-bus memory references.
   // 512 byte granularity,
   // bit [15:0] = SDRAM bits [24:9]
   // bit [16]   = write enable
   // bit [17]   = read enable
   //
   // Accesses from the internal CPU supports 32-bit accesses only!
   //
   abcmapram abcmapram (
			.aclr      ( ~rst_n ),

			.clock     ( sdram_clk ),

			.address_a ( abc_a_q[15:9] ),
			.data_a    ( 18'bx ),
			.wren_a    ( 1'b0 ),
			.q_a       ( { abc_rden, abc_wren, abc_mempg } ),

			.address_b ( cpu_mem_addr[8:2] ),
			.data_b    ( cpu_mem_wdata[17:0] ),
			.wren_b    ( iodev_valid_abcmemmap & cpu_mem_wstrb[0] ),
			.q_b       ( iodev_rdata_abcmemmap )
			);


   always @(posedge sdram_clk or negedge rst_n)
     if (~rst_n)
       begin
	  abc_d_oe      <= 1'b0;
	  abc_rrq       <= 1'b0;
	  abc_wrq       <= 1'b0;
	  abc_xmemrd_q  <= 1'b0;
	  abc_xmemwr_q  <= 1'b0;
	  abc_racked    <= 1'b0;
	  abc_wacked    <= 1'b0;
       end
     else
       begin
	  abc_d_oe      <= 1'b0;

	  abc_di        <= abc_d;
	  abc_xmemrd_q  <= abc_xmemrd & abc_rden;
	  abc_xmemwr_q  <= abc_xmemwr & abc_wren;
	  abc_racked    <= abc_xmemrd_q & (abc_rack | abc_racked);
	  abc_wacked    <= abc_xmemwr_q & (abc_wack | abc_wacked);

	  abc_rrq <= abc_xmemrd_q & ~abc_racked;
	  abc_wrq <= abc_xmemwr_q & ~abc_wacked;

	  if (abc_xmemrd_q & abc_racked & abc_rready)
	    begin
	       abc_do   <= abc_sr_rd;
	       abc_d_oe <= 1'b1;
	    end
       end // else: !if(~rst_n)

   //
   // SDRAM
   //
   wire [31:0] sdram_rd;
   wire        sdram_rack;
   wire        sdram_rready;
   wire        sdram_wack;
   reg	       sdram_acked;

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

   always @(posedge sdram_clk)
     sdram_acked <= cpu_mem_quad[1] & (sdram_acked | sdram_rack | sdram_wack);

   wire        sdram_req = cpu_mem_quad[1] & ~sdram_acked;

   sdram 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 ),

		.a0       ( { abc_mempg, abc_a_q[8:0] } ),
		.rd0      ( abc_sr_rd ),
		.rrq0     ( abc_rrq ),
		.rack0    ( abc_rack ),
		.rready0  ( abc_rready ),
		.wd0      ( abc_d_q ),
		.wrq0     ( abc_wrq ),
		.wack0    ( abc_wack ),

		.a1       ( cpu_mem_addr[24:2] ),
		.rd1      ( sdram_rd ),
		.rrq1     ( sdram_req & ~|cpu_mem_wstrb ),
		.rack1    ( sdram_rack ),
		.rready1  ( sdram_rready ),
		.wd1      ( cpu_mem_wdata ),
		.wstrb1   ( {4{sdram_req}} & cpu_mem_wstrb ),
		.wack1    ( sdram_wack ),

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


   // I2C
   assign i2c_scl = 1'bz;
   assign i2c_sda = 1'bz;

   // GPIO
   assign gpio    = 6'bzzzzzz;

   // Embedded RISC-V CPU
   parameter cpu_fast_mem_bits = 13; /* 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;

   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 ( ),

	.progaddr_reset ( 32'h0000_0000 ),
	.progaddr_irq   ( 32'h0000_0020 ),

	.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 )
	);

   // cpu_mem_ready is always true for fast memory; for SDRAM we have to
   // wait either for a write ack or a low-high transition on the
   // read ready signal.
   reg sdram_rready_q;
   reg sdram_mem_ready;
   reg [31:0] sdram_rdata;
   always @(posedge sys_clk)
     begin
	sdram_rready_q <= sdram_rready;
	if (cpu_mem_quad[1])
	  sdram_mem_ready <= sdram_mem_ready | sdram_wack |
			     (sdram_rready & ~sdram_rready_q);
	else
	  sdram_mem_ready <= 1'b0;

	sdram_rdata <= sdram_rd;
     end

   // 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 @(*)
     case ( cpu_mem_quad )
       4'b0001: cpu_mem_rdata = fast_mem_rdata;
       4'b0010: cpu_mem_rdata = sdram_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_sys_rdata = sysreg_rdata[sysreg_subreg];

   assign sysreg_rdata[0] = 32'h5058414d;
   assign sysreg_rdata[1] = { 31'b0, rtc_32khz_rework };

   // Hard system reset under program control
   assign reset_cmd = sysreg[3] & cpu_mem_wstrb[0] & cpu_mem_wdata[0];

   // ABC80/800 status
   assign iodev_rdata_abcbus = { 30'b0, abc800, abc_clk_active };
   reg [1:0] abc_status[0:1];
   always @(posedge sys_clk)
     begin
	abc_status[0] <= iodev_rdata_abcbus;
	abc_status[1] <= abc_status[0];
     end

   wire iodev_irq_abcbus = (abc_status[1] != abc_status[0]);

   // 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 ),

	       .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 ),
	       .done     ( rom_done )
	       );

   always @(posedge sys_clk)
     rom_done_q <= rom_done;

   assign sysreg_rdata[4] = { 31'b0, rom_done_q };

   //
   // 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
	    );

   reg [1:0]   tty_dtr_q;
   always @(posedge sys_clk)
     begin
	tty_dtr_q[0] <= tty_dtr;
	tty_dtr_q[1] <= gpio[4];
     end

   //
   // Route data to the two output ports
   //

   // tty_rxd because pins are DCE named
   assign tty_data_in = (tty_txd | tty_dtr_q[0]) &
			(gpio[0] | tty_dtr_q[1]);

   assign tty_rxd = tty_dtr_q[0] ? 1'bz : tty_data_out;
   assign gpio[1] = tty_dtr_q[1] ? 1'bz : tty_data_out;

   assign tty_rts_in = (tty_rts | tty_dtr_q[0]) &
		       (gpio[2] | tty_dtr_q[1]);

   assign tty_cts = tty_dtr_q[0] ? 1'bz : tty_cts_out;
   assign gpio[3] = tty_dtr_q[1] ? 1'bz : 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)
   //
   // XXX: the RTC 32 kHz signal is missing a pull-up,
   // so unless the board has been reworked, use a
   // divider down from the 84 MHz system clock. The
   // error is about 200 ppm; a proper NCO could do better.

   reg		ctr_32khz;
   reg [10:0]	ctr_64khz;
   always @(posedge sys_clk)
     begin
	if (~|ctr_64khz)
	  begin
	     ctr_32khz <= ~ctr_32khz;
	     ctr_64khz <= 11'd1280;
	  end
	else
	  ctr_64khz <= ctr_64khz - 1'b1;
     end

   // 32kHz clock synchronized with sys_clk
   wire clk_32kHz = rtc_32khz_rework ? rtc_32khz : ctr_32khz;

   sysclock #(.PERIODIC_HZ_LG2 ( 5 ))
   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

   //
   // 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
