Browse Source

abcbus: move abcbus to separate module; add I/O status register

Move the abcbus code, which has grown rather extensive, to a separate
module.

Add an I/O access status register: a bit is set every time a "valid"
I/O port access is performed. This register is write-1-clear, and
triggers IRQ on 0->1 transitions.

"Valid" means:

OUT#, INP#:	Selected and DMA queue empty
CS#, RST#:	Always valid
others:		Selected
H. Peter Anvin 3 năm trước cách đây
mục cha
commit
56753cd6a3

+ 467 - 0
fpga/abcbus.sv

@@ -0,0 +1,467 @@
+module abcbus (
+	       input	     rst_n,
+	       input	     sys_clk,
+	       input	     sdram_clk, // Assumed to be a multiple of sys_clk
+	       input	     stb_1mhz, // 1-2 MHz sys_clk strobe
+
+	       // CPU interface
+	       input	     abc_valid, // Control/status registers
+	       input	     map_valid, // Memory map
+	       input [31:0]  cpu_addr,
+	       input [31:0]  cpu_wdata,
+	       input [3:0]   cpu_wstrb,
+	       output [31:0] cpu_rdata, // For the ABC-bus control
+	       output [31:0] cpu_rdata_map, // For the map RAM
+	       output reg    irq,
+
+	       // ABC bus
+	       input	     abc_clk,
+	       input [15:0]  abc_a,
+	       inout [7:0]   abc_d,
+	       output reg    abc_d_oe,
+	       input	     abc_rst_n,
+	       input	     abc_cs_n,
+	       input [4:0]   abc_out_n,
+	       input [1:0]   abc_inp_n,
+	       input	     abc_xmemfl_n,
+	       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 reg    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 interface
+	       output [24:0] sdram_addr,
+	       input [7:0]   sdram_rd,
+	       output reg    sdram_rrq,
+	       input	     sdram_rack,
+	       input	     sdram_rready,
+	       output [7:0]  sdram_wd,
+	       output reg    sdram_wrq,
+	       input	     sdram_wack
+	       );
+
+   // Set if MOSFETs Q1-Q6 are installed rather than the corresponding
+   // resistors. BOTH CANNOT BE INSTALLED AT THE SAME TIME.
+   parameter [6:1] mosfet_installed = 6'b111_111;
+   parameter [0:0] exth_reversed = 1'b0;
+
+   // Synchronizer for ABC-bus input signals; also changes
+   // the sense to positive logic where applicable
+   wire	       abc_clk_s;
+   wire [15:0] abc_a_s;
+   wire [7:0]  abc_di;
+   wire        abc_rst_s;
+   wire        abc_cs_s;
+   wire [4:0]  abc_out_s;
+   wire [1:0]  abc_inp_s;
+   wire        abc_xmemfl_s;
+   wire        abc_xmemw800_s;
+   wire        abc_xmemw80_s;
+   wire        abc_xinpstb_s;
+   wire        abc_xoutpstb_s;
+
+   synchronizer #( .width(39) ) abc_synchro
+     (
+      .rst_n ( rst_n ),
+      .clk ( clk ),
+      .d ( { abc_clk, abc_a, abc_d, ~abc_rst_n, ~abc_cs_n,
+	     ~abc_out_n, ~abc_inp_n, ~abc_xmemfl_n, ~abc_xmemw800_n,
+	     ~abc_xmemw80_n, ~abc_xinpstb_n, ~abc_xoutpstb_n } ),
+      .q ( { abc_clk_s, abc_a_s, abc_di, abc_rst_s, abc_cs_s,
+	     abc_out_s, abc_inp_s, abc_xmemfl_s, abc_xmemw800_s,
+	     abc_xmemw80_s, abc_xinpstb_s, abc_xoutpstb_s } )
+      );
+
+   assign abc_master = 1'b0;	// Only device mode supported
+   assign abc_d_ce_n = 1'b0;	// Do not isolate busses
+
+   reg	     abc_clk_active;
+
+   // 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      abc80  = abc_xinpstb_s & abc_xoutpstb_s;
+   wire      abc800 = ~abc80;
+
+   // Memory read/write strobes
+   wire abc_xmemrd = abc_clk_active & abc_xmemfl_s;
+   wire abc_xmemwr = abc_clk_active &
+	(abc800 ? abc_xmemw800_s : abc_xmemw80_s);
+
+   // I/O read/write strobes for ABC-bus only
+   wire [2:0] abc_inp = abc_inp_s & {3{abc_clk_active}} & {abc800, 2'b11};
+   wire [4:0] abc_out = abc_out_s & {5{abc_clk_active}};
+   wire       abc_rst = abc_rst_s & abc_clk_active;
+   wire       abc_cs  = abc_cs_s  & abc_clk_active;
+
+   reg  [7:0] abc_do;
+   assign abc_d    = abc_d_oe ? abc_do : 8'hzz;
+
+   reg [8:0]  ioselx;
+   wire       iosel_en = ioselx[8];
+   wire       iosel = ioselx[5:0];
+
+   // ABC-bus I/O select
+   always @(negedge rst_n or posedge sdram_clk)
+     if (~rst_n)
+       ioselx <= 9'b0;
+     else if (abc_rst)
+       ioselx <= 9'b0;
+     else if (abc_cs)
+       ioselx <= { 1'b1, abc_di };
+
+   // Open drain signals with optional MOSFETs
+   wire       abc_wait  = 1'b0;
+   wire       abc_resin = ~rst_n;
+   wire       abc_int   = 1'b0;
+   wire       abc_nmi   = 1'b0;
+   wire       abc_xm    = 1'b0;
+
+   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;
+
+   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_s };
+	  case ( { abc_clk_q == 2'b10, stb_1mhz } )
+	    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 [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;
+
+      // ABC SDRAM interface
+   //
+   // Memory map for ABC-bus memory references.
+   // 512 byte granularity for memory (registers 0-127),
+   // one input and one output queue per select code for I/O (128-255).
+   //
+   // bit [24:0]  = SDRAM address.
+   // bit [25]    = write enable ( bit 30 from CPU )
+   // bit [26]    = read enable  ( bit 31 from CPU )
+   // bit [35:27] = DMA count for I/O ( separate register 384-511 from CPU )
+   //
+   // Accesses from the internal CPU supports 32-bit accesses only!
+   //
+   // If the DMA counter is exhausted, or I/O operations other than port 0,
+   // I/O is instead directed to a memory area pointed to by the iomem_base
+   // register as:
+   // bit [24:4]  = iomem_base
+   // bit [3]     = read
+   // bit [2:0]   = port
+   //
+   // However, the rd and wr enable bits in the I/O map still apply.
+   //
+   wire [24:0] abc_memaddr;
+
+   wire [7:0] abc_map_addr =
+	       abc_out_s[0] ? { 1'b1, iosel, 1'b0 } :
+	       abc_inp_s[0] ? { 1'b1, iosel, 1'b1 } :
+	      { 1'b0, abc_a_s[15:9] };
+   wire [8:0]  abc_dma_count;
+   wire [35:0] rdata_abcmemmap;
+   wire        abc_rden;
+   wire        abc_wren;
+
+   //
+   // For I/O, don't allow the read/write enables to conflict with
+   // the direction of the I/O.
+   //
+   wire [1:0] abcmap_masked_rdwr = cpu_wdata[31:30] &
+	      { ~cpu_addr[9] | ~cpu_addr[2],
+		~cpu_addr[9] |  cpu_addr[2] };
+
+   abcmapram abcmapram (
+			.aclr      ( ~rst_n ),
+
+			.clock     ( sdram_clk ),
+
+			.address_a ( abc_map_addr ),
+			.data_a    ( { abc_dma_count - 1'b1,
+				       abc_rden, abc_wren,
+				       abc_memaddr + 1'b1 } ),
+			.wren_a    ( abc_dma_update ),
+			.byteena_a ( 4'b1111 ),
+			.q_a       ( { abc_dma_count,
+				       abc_rden, abc_wren, abc_memaddr } ),
+
+			.address_b ( cpu_addr[9:2] ),
+			.data_b    ( { cpu_wdata[8:0],
+				       abcmap_masked_rdwr,
+				       cpu_wdata[24:0] } ),
+			.wren_b    ( valid_map & cpu_wstrb[0] ),
+			.byteena_b ( { cpu_addr[10],
+				       {3{~cpu_addr[10]}} } ),
+
+			.q_b       ( rdata_abcmemmap )
+			);
+
+   assign iodev_rdata_abcmemmap = cpu_addr[10] ?
+				  { 23'b0, rdata_abcmemmap[35:27] } :
+				  { rdata_abcmemmap[26:25], 5'b0,
+				    rdata_abcmemmap[24:0] };
+   reg [24:4] abc_iobase;
+   reg	      abc_memrd_en;
+   reg	      abc_memwr_en;
+   reg	      abc_dma_en;
+   reg	      abc_iowr_en;
+   reg	      abc_iord_en;
+   reg	      abc_do_memrd;
+   reg	      abc_do_memwr;
+   reg	      abc_racked;
+   reg	      abc_wacked;
+
+   wire       abc_rack;
+   wire       abc_wack;
+   wire       abc_rready;
+
+   // I/O status register: set on valid access, trigger IRQ.
+   // The RST# (INP 7) and CS# (OUT 1) signals are always valid
+   // regardless of the current select code; OUT 0/INP 0 only
+   // set the bit once the DMA counter reaches zero, if applicable.
+   reg  [7:0] abc_inpflag;
+   wire [7:0] clear_inpflag;
+   reg  [7:0] abc_outflag;
+   wire [7:0] clear_outflag;
+
+   always @(posedge sdram_clk or negedge rst_n)
+     if (~rst_n)
+       begin
+	  abc_memrd_en  <= 1'b0;
+	  abc_memwr_en  <= 1'b0;
+	  abc_dma_en    <= 1'b0;
+	  abc_iord_en   <= 1'b0;
+	  abc_iowr_en   <= 1'b0;
+	  abc_do_memrd  <= 1'b0;
+	  abc_do_memwr  <= 1'b0;
+	  sdram_rrq     <= 1'b0;
+	  sdram_wrq     <= 1'b0;
+	  abc_racked    <= 1'b0;
+	  abc_wacked    <= 1'b0;
+	  abc_inpflag   <= 8'b0;
+	  abc_outflag   <= 8'b0;
+       end
+     else
+       begin
+	  // Careful with the registering here; need to make sure
+	  // abcmapram is caught up
+	  abc_memwr_en  <= abc_xmemwr;
+	  abc_memrd_en  <= abc_xmemrd;
+	  abc_dma_en    <= iosel_en & (abc_out[0] | abc_inp[0]);
+	  abc_iowr_en   <= iosel_en & |abc_out;
+	  abc_iord_en   <= iosel_en & |abc_inp;
+
+	  abc_do_memrd  <= abc_rden & (abc_memrd_en | abc_iord_en);
+	  abc_do_memwr  <= abc_wren & (abc_memwr_en | abc_iowr_en);
+	  abc_racked    <= abc_do_memrd & (sdram_rack | abc_racked);
+	  abc_wacked    <= abc_do_memwr & (sdram_wack | abc_wacked);
+
+	  sdram_rrq     <= abc_do_memrd & ~abc_racked;
+	  sdram_wrq     <= abc_do_memwr & ~abc_wacked;
+
+	  // This will be true for one cycle only, which is what we want
+	  abc_dma_update <= abc_dma_en &
+			    ((sdram_rrq & abc_racked) |
+			     (sdram_wrq & abc_wacked));
+
+	  abc_inpflag <= ~clear_inpflag &
+			 (abc_inpflag |
+			  { abc_rst, 4'b0000,
+			    {2{abc_iord_en}} & abc_inp[2:1],
+			    abc_iord_en & ~|abc_dma_count & abc_inp[0] });
+	  abc_outflag <= ~clear_outflag &
+			 (abc_outflag |
+			  { 2'b00, {4{abc_iowr_en}} & abc_out[4:1],
+			    abc_cs,
+			    abc_iowr_en & ~|abc_dma_count & abc_out[0] });
+       end // else: !if(~rst_n)
+
+   assign sdram_addr =
+		      (abc_dma_en & |abc_dma_count) ? abc_memaddr :
+		      (abc_iord_en|abc_iowr_en) ? { abc_iobase, |abc_inp_s, abc_a_s[2:0] } :
+		      { abc_memaddr[24:9], abc_a_s[8:0] };
+
+   assign sdram_wd = abc_di;
+
+   //
+   // ABC-bus data bus handling
+   //
+   always @(posedge sdram_clk or negedge rst_n)
+     if (~rst_n)
+       begin
+	  abc_do   <= 8'hxx;
+	  abc_d_oe <= 1'b0;
+       end
+     else if (abc_racked & sdram_rready)
+       begin
+	  abc_do   <= sdram_rd;
+	  abc_d_oe <= 1'b1;
+       end
+     else
+       begin
+	  abc_do   <= 8'hxx;
+	  abc_d_oe <= 1'b0;
+       end
+
+   //
+   // ABC-bus control/status registers
+   // All these registers are 32-bit access only except the I/O status
+   // register (which is write-1-clear.)
+   //
+   assign clear_inpflag = {8{abc_valid & cpu_addr[5:2] == 5'b00011
+			     & cpu_wstrb[1]}} & cpu_wdata[15:8];
+   assign clear_outflag = {8{abc_valid & cpu_addr[5:2] == 5'b00011
+			     & cpu_wstrb[0]}} & cpu_wdata[7:0];
+
+   always @(posedge sys_clk or negedge rst_n)
+     if (~rst_n)
+       begin
+	  abc_iobase <= 20'bx;
+       end
+     else if (abc_valid & cpu_wstrb[0])
+       begin
+	  casez (cpu_addr[5:2])
+	    5'b???10:
+	      abc_iobase <= cpu_wdata[24:4];
+	    default:
+	      /* do nothing */ ;
+	  endcase
+       end
+
+   reg [31:0] abc_status[0:1];
+   always @(posedge sys_clk)
+     begin
+	abc_status[0] <= { 29'b0, abc800, abc_rst_s, abc_clk_active };
+	abc_status[1] <= abc_status[0];
+     end
+
+   wire [15:0] abc_iostatus = { abc_inpflag, abc_outflag };
+   reg [15:0]  abc_iostatus_q;
+   always @(posedge sys_clk)
+     abc_iostatus_q <= abc_iostatus;
+
+   always_comb
+     casez (cpu_addr[5:2])
+       5'b00000: cpu_rdata = abc_status[0];
+       5'b00001: cpu_rdata = ioselx[7:0];
+       5'b00010: cpu_rdata = abc_iobase;
+       5'b00011: cpu_rdata = { 16'b0, abc_iostatus };
+       default:  cpu_rdata = 32'bx;
+     endcase // casez (cpu_addr[5:2])
+
+   // irq is edge-triggered on either changes to abc_status
+   // or bits set in iostatus.
+   always @(negedge rst_n or posedge sys_clk)
+     if (~rst_n)
+       irq <= 1'b0;
+     else
+       irq <= (abc_status[1] != abc_status[0]) |
+	      (|(abc_iostatus & ~abc_iostatus_q));
+
+endmodule // abcbus

+ 2 - 1
fpga/max80.qsf

@@ -220,6 +220,7 @@ set_global_assignment -name SYSTEMVERILOG_FILE tty.sv
 set_global_assignment -name SYSTEMVERILOG_FILE sdcard.sv
 set_global_assignment -name SYSTEMVERILOG_FILE sysclock.sv
 set_global_assignment -name SYSTEMVERILOG_FILE i2c.sv
+set_global_assignment -name SYSTEMVERILOG_FILE abcbus.sv
 set_global_assignment -name VERILOG_FILE ip/abcmapram.v
 set_global_assignment -name VERILOG_FILE ip/fastmem_ip.v
 set_global_assignment -name SYSTEMVERILOG_FILE fast_mem.sv
@@ -250,4 +251,4 @@ set_global_assignment -name POWER_REPORT_POWER_DISSIPATION ON
 set_global_assignment -name POWER_USE_DEVICE_CHARACTERISTICS MAXIMUM
 set_global_assignment -name POWER_USE_TA_VALUE 35
 
-set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top
+set_instance_assignment -name PARTITION_HIERARCHY root_partition -to | -section_id Top

+ 84 - 367
fpga/max80.sv

@@ -20,7 +20,7 @@ module max80
 	      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
+	      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
@@ -116,10 +116,6 @@ module max80
 	      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
@@ -244,177 +240,6 @@ module max80
 		  .tx_outclock ( hdmi_clk )
 		  );
 
-   //
-   // ABC bus basic interface
-   //
-
-   // Synchronizer for ABC-bus input signals; also changes
-   // the sense to positive logic where applicable
-   wire	       abc_clk_s;
-   wire [15:0] abc_a_s;
-   wire [7:0]  abc_di;
-   wire        abc_rst_s;
-   wire        abc_cs_s;
-   wire [4:0]  abc_out_s;
-   wire [1:0]  abc_inp_s;
-   wire        abc_xmemfl_s;
-   wire        abc_xmemw800_s;
-   wire        abc_xmemw80_s;
-   wire        abc_xinpstb_s;
-   wire        abc_xoutpstb_s;
-
-   synchronizer #( .width(39) ) abc_synchro
-     (
-      .rst_n ( rst_n ),
-      .clk ( clk ),
-      .d ( { abc_clk, abc_a, abc_d, ~abc_rst_n, ~abc_cs_n,
-	     ~abc_out_n, ~abc_inp_n, ~abc_xmemfl_n, ~abc_xmemw800_n,
-	     ~abc_xmemw80_n, ~abc_xinpstb_n, ~abc_xoutpstb_n } ),
-      .q ( { abc_clk_s, abc_a_s, abc_di, abc_rst_s, abc_cs_s,
-	     abc_out_s, abc_inp_s, abc_xmemfl_s, abc_xmemw800_s,
-	     abc_xmemw80_s, abc_xinpstb_s, abc_xoutpstb_s } )
-      );
-
-   assign abc_master = 1'b0;	// Only device mode supported
-   assign abc_d_ce_n = 1'b0;	// Do not isolate busses
-
-   reg	     abc_clk_active;
-
-   // 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      abc80  = abc_xinpstb_s & abc_xoutpstb_s;
-   wire      abc800 = ~abc80;
-
-   // Memory read/write strobes
-   wire abc_xmemrd = abc_clk_active & abc_xmemfl_s;
-   wire abc_xmemwr = abc_clk_active &
-	(abc800 ? abc_xmemw800_s : abc_xmemw80_s);
-
-   // I/O read/write strobes
-   wire abc_iord = abc_clk_active &
-	((abc800 & abc_xinpstb_s)  | (|abc_inp_s));
-   wire abc_iowr = abc_clk_active &
-	((abc800 & abc_xoutpstb_s) | (|abc_out_s));
-
-   reg  [7:0] abc_do;
-   assign abc_d    = abc_d_oe ? abc_do : 8'hzz;
-
-   reg [8:0]  ioselx;
-   wire       iosel_en = ioselx[8];
-   wire       iosel = ioselx[5:0];
-
-   // ABC-bus I/O select
-   always @(negedge rst_n or posedge sdram_clk)
-     if (~rst_n)
-       ioselx <= 9'b0;
-     else if (abc_rst_s)
-       ioselx <= 9'b0;
-     else if (abc_cs_s)
-       ioselx <= { 1'b1, abc_di };
-
-   // 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;
-
-   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_s };
-	  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
    //
@@ -439,205 +264,38 @@ module max80
    wire        iodev_mem_valid = cpu_mem_quad[3];
 `include "iodevs.vh"
 
-   // ABC SDRAM interface
-   //
-   // Memory map for ABC-bus memory references.
-   // 512 byte granularity for memory (registers 0-127),
-   // one input and one output queue per select code for I/O (128-255).
-   //
-   // bit [24:0]  = SDRAM address.
-   // bit [25]    = write enable ( bit 30 from CPU )
-   // bit [26]    = read enable  ( bit 31 from CPU )
-   // bit [35:27] = DMA count for I/O ( separate register 384-511 from CPU )
-   //
-   // Accesses from the internal CPU supports 32-bit accesses only!
-   //
-   // If the DMA counter is exhausted, or I/O operations other than port 0,
-   // I/O is instead directed to a memory area pointed to by the iomem_base
-   // register as:
-   // bit [24:4]  = iomem_base
-   // bit [3]     = read
-   // bit [2:0]   = port
-   //
-   // However, the rd and wr enable bits in the I/O map still apply.
-   //
-   wire [24:0] abc_memaddr;
-
-   wire [7:0] abc_map_addr =
-	       abc_out_s[0] ? { 1'b1, iosel, 1'b0 } :
-	       abc_inp_s[0] ? { 1'b1, iosel, 1'b1 } :
-	      { 1'b0, abc_a_s[15:9] };
-   wire [8:0]  abc_dma_count;
-   wire [35:0] rdata_abcmemmap;
-   wire        abc_rden;
-   wire        abc_wren;
-
-   //
-   // For I/O, don't allow the read/write enables to conflict with
-   // the direction of the I/O.
    //
-   wire [1:0] abcmap_masked_rdwr = cpu_mem_wdata[31:30] &
-	      { ~cpu_mem_addr[9] | ~cpu_mem_addr[2],
-		~cpu_mem_addr[9] |  cpu_mem_addr[2] };
-
-   abcmapram abcmapram (
-			.aclr      ( ~rst_n ),
-
-			.clock     ( sdram_clk ),
-
-			.address_a ( abc_map_addr ),
-			.data_a    ( { abc_dma_count - 1'b1,
-				       abc_rden, abc_wren,
-				       abc_memaddr + 1'b1 } ),
-			.wren_a    ( abc_dma_update ),
-			.byteena_a ( 4'b1111 ),
-			.q_a       ( { abc_dma_count,
-				       abc_rden, abc_wren, abc_memaddr } ),
-
-			.address_b ( cpu_mem_addr[9:2] ),
-			.data_b    ( { cpu_mem_wdata[8:0],
-				       abcmap_masked_rdwr,
-				       cpu_mem_wdata[24:0] } ),
-			.wren_b    ( iodev_valid_abcmemmap & cpu_mem_wstrb[0] ),
-			.byteena_b ( { cpu_mem_addr[10],
-				       {3{~cpu_mem_addr[10]}} } ),
-
-			.q_b       ( rdata_abcmemmap )
-			);
-
-   assign iodev_rdata_abcmemmap = cpu_mem_addr[10] ?
-				  { 23'b0, rdata_abcmemmap[35:27] } :
-				  { rdata_abcmemmap[26:25], 5'b0,
-				    rdata_abcmemmap[24:0] };
-   reg [24:4] abc_iobase;
-   reg	      abc_mem_en;
-   reg	      abc_dma_en;
-   reg	      abc_io_en;
-   reg	      abc_rrq;
-   reg	      abc_wrq;
-   reg	      abc_do_memrd;
-   reg	      abc_do_memwr;
-   reg	      abc_racked;
-   reg	      abc_wacked;
-
-   wire       abc_rack;
-   wire       abc_wack;
-   wire       abc_rready;
-   wire [7:0] abc_sr_rd;
-
-   always @(posedge sdram_clk or negedge rst_n)
-     if (~rst_n)
-       begin
-	  abc_mem_en    <= 1'b0;
-	  abc_dma_en    <= 1'b0;
-	  abc_io_en     <= 1'b0;
-	  abc_do_memrd  <= 1'b0;
-	  abc_do_memwr  <= 1'b0;
-	  abc_rrq       <= 1'b0;
-	  abc_wrq       <= 1'b0;
-	  abc_racked    <= 1'b0;
-	  abc_wacked    <= 1'b0;
-       end
-     else
-       begin
-	  // Careful with the registering here; need to make sure
-	  // abcmapram is caught up
-	  abc_mem_en    <= abc_xmemwr | abc_xmemrd;
-	  abc_dma_en    <= iosel_en & (abc_out_s[0] | abc_inp_s[0]);
-	  abc_io_en     <= iosel_en & |{abc_out_s, abc_inp_s};
-
-	  abc_do_memrd  <= abc_rden & (abc_mem_en | abc_io_en);
-	  abc_do_memwr  <= abc_wren & (abc_mem_en | abc_io_en);
-	  abc_racked    <= abc_do_memrd & (abc_rack | abc_racked);
-	  abc_wacked    <= abc_do_memwr & (abc_wack | abc_wacked);
-
-	  abc_rrq <= abc_do_memrd & ~abc_racked;
-	  abc_wrq <= abc_do_memwr & ~abc_wacked;
-
-	  abc_dma_update <= abc_dma_en &
-			    ((abc_do_memrd & abc_rack & ~abc_racked) |
-			     (abc_do_memwr & abc_wack & ~abc_wacked));
-       end // else: !if(~rst_n)
-
-   wire [24:0] abc_sdram_addr =
-	       abc_dma_en ? abc_memaddr :
-	       abc_io_en  ? { abc_iobase, |abc_inp_s, abc_a_s[2:0] } :
-	       { abc_memaddr[24:9], abc_a_s[8:0] };
-
-   //
-   // ABC-bus data bus handling
-   //
-   always @(posedge sdram_clk or negedge rst_n)
-     if (~rst_n)
-       begin
-	  abc_do   <= 8'hxx;
-	  abc_d_oe <= 1'b0;
-       end
-     else if (abc_do_memwr & abc_racked & abc_rready)
-       begin
-	  abc_do   <= abc_sr_rd;
-	  abc_d_oe <= 1'b1;
-       end
-     else
-       begin
-	  abc_do   <= 8'hxx;
-	  abc_d_oe <= 1'b0;
-       end
-
-   //
-   // ABC-bus control/status registers
-   // All these registers are 32-bit access only...
+   // SDRAM
    //
-   always @(posedge sys_clk or negedge rst_n)
-     if (~rst_n)
-       begin
-	  abc_iobase <= 20'bx;
-       end
-     else if (iodev_valid_abc & cpu_mem_wstrb[0])
-       begin
-	  casez (cpu_mem_addr[5:2])
-	    5'b????1:
-	      abc_iobase <= cpu_mem_wdata[24:4];
-	    default:
-	      /* do nothing */ ;
-	  endcase
-       end
-
-   reg [31:0] abc_status[0:1];
-   always @(posedge sys_clk)
-     begin
-	abc_status[0] <= { 30'b0, abc800, abc_clk_active };
-	abc_status[1] <= abc_status[0];
-     end
-
-   always_comb
-     casez (cpu_mem_addr[5:2])
-       5'b00000: iodev_rdata_abc = abc_status[0];
-       5'b00001: iodev_rdata_abc = abc_iobase;
-       default:  iodev_rdata_abc = 32'bx;
-     endcase // casez (cpu_mem_addr[5:2])
 
-   assign iodev_irq_abc = (abc_status[1] != abc_status[0]);
+   // ABC interface
+   wire [24:0] abc_sr_addr;
+   wire [ 7:0] abc_sr_rd;
+   wire        abc_sr_rrq;
+   wire        abc_sr_rack;
+   wire        abc_sr_ready;
+   wire        abc_sr_wd;
+   wire        abc_sr_wrq;
+   wire        abc_sr_wack;
 
-    //
-   // SDRAM
-   //
+   // CPU interface
    wire [31:0] sdram_rd;
    wire        sdram_rack;
    wire        sdram_rready;
    wire        sdram_wack;
    reg	       sdram_acked;
+   wire        sdram_valid = cpu_mem_quad[1];
+   wire        sdram_req = sdram_valid & ~sdram_acked;
 
+   always @(posedge sdram_clk)
+     sdram_acked <= sdram_valid & (sdram_acked | sdram_rack | sdram_wack);
+
+   // Romcopy interface
    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
@@ -654,14 +312,14 @@ module max80
 		.sr_a     ( sr_a ),
 		.sr_dq    ( sr_dq ),
 
-		.a0       ( abc_sdram_addr ),
+		.a0       ( abc_sr_addr ),
 		.rd0      ( abc_sr_rd ),
-		.rrq0     ( abc_rrq ),
-		.rack0    ( abc_rack ),
-		.rready0  ( abc_rready ),
-		.wd0      ( abc_di ),
-		.wrq0     ( abc_wrq ),
-		.wack0    ( abc_wack ),
+		.rrq0     ( abc_sr_rrq ),
+		.rack0    ( abc_sr_rack ),
+		.rready0  ( abc_sr_rready ),
+		.wd0      ( abc_sr_wd ),
+		.wrq0     ( abc_sr_wrq ),
+		.wack0    ( abc_sr_wack ),
 
 		.a1       ( cpu_mem_addr[24:2] ),
 		.rd1      ( sdram_rd ),
@@ -678,6 +336,65 @@ module max80
 		.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_master ( abc_master ),
+		  .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_rrq ( abc_sr_rrq ),
+		  .sdram_rack ( abc_sr_rack ),
+		  .sdram_rready ( abc_sr_rready ),
+		  .sdram_wd ( abc_sr_wd ),
+		  .sdram_wrq ( abc_sr_wrq ),
+		  .sdram_wack ( abc_sr_wack )
+		  );
 
    // GPIO
    assign gpio    = 6'bzzzzzz;

BIN
fpga/output_files/max80.jbc


BIN
fpga/output_files/max80.jic


BIN
fpga/output_files/max80.pof


BIN
fpga/output_files/max80.sof


+ 4 - 4
fw/boot.mif

@@ -130,7 +130,7 @@ CONTENT BEGIN
 007B : 27032C06;
 007C : 0593000E;
 007D : 7E933200;
-007E : 94630027;
+007E : 94630047;
 007F : 0593000E;
 0080 : 638D0500;
 0081 : 19838513;
@@ -3273,9 +3273,9 @@ CONTENT BEGIN
 0CC2 : 74634F20;
 0CC3 : 20353120;
 0CC4 : 31323032;
-0CC5 : 3A333120;
-0CC6 : 323A3333;
-0CC7 : 000A0A34;
+0CC5 : 3A343120;
+0CC6 : 323A3334;
+0CC7 : 000A0A38;
 0CC8 : 61636473;
 0CC9 : 725F6472;
 0CCA : 5F646165;

+ 7 - 2
fw/ioregs.h

@@ -94,8 +94,13 @@
 
 #define ABC_STATUS		IODEVL(ABC,0)
 #define ABC_STATUS_LIVE		1
-#define ABC_STATUS_800		2
-#define ABC_IOBASE		IODEVL(ABC,1)
+#define ABC_STATUS_RST		2
+#define ABC_STATUS_800		4
+#define ABC_IOSEL		IODEVL(ABC,1)
+#define ABC_IOBASE		IODEVL(ABC,2)
+#define ABC_IOSTATUS		IODEVL(ABC,3)
+#define ABC_OUTSTATUS		IODEVB0(ABC,3)
+#define ABC_INPSTATUS		IODEVB1(ABC,3)
 
 #define ABCMEMMAP_PAGE(n)	IODEVL(ABCMEMMAP,n)
 #define ABCMEMMAP_WRPORT(n)	IODEVL(ABCMEMMAP,128+((n) << 1))