Files
QL_MiSTer/rtl/qlromext.v

350 lines
9.8 KiB
Verilog

// Logic for the QLROMEXT board of the QL-SD interface
// Copyright (C) 2011 Adrian Ives and Peter Graf
//
// This hardware description is free; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// This hardware description is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
// Version: 0.082
// Target: CPLD LC4064V
// Control Register addresses
// The position and ordering of IF_ENABLE and IF_DISABLE are
// important because of Minerva's extended memory test (which, for
// some unfathomable reason checks the ROM cartridge space for RAM!)
// This order ensures that the interface remains disabled during the
// check; preventing anything from being placed on the data bus or
// any spurious signals from being sent to the SD Card
`define IF_ENABLE 16'hFEE0 // 65248
`define IF_DISABLE 16'hFEE1 // 65249
`define IF_RESET 16'hFEE2 // 65250
`define SPI_READ 16'hFEE4 // 65252
`define SPI_XFER_FAST 16'hFEE6 // 65254
`define SPI_XFER_SLOW 16'hFEE8 // 65256
`define SPI_XFER_OFF 16'hFEEA // 65258
`define SPI_DESELECT 16'hFEF0 // 65264
`define SPI_SELECT1 16'hFEF1 // 65265
`define SPI_SELECT2 16'hFEF2 // 65266
`define SPI_SELECT3 16'hFEF3 // 65267
`define SPI_CLR_MOSI 16'hFEF4 // 65368
`define SPI_SET_MOSI 16'hFEF5 // 65369
`define SPI_CLR_SCLK 16'hFEF6 // 65370
`define SPI_SET_SCLK 16'hFEF7 // 65371
// SPI background transfer shift register write page
// If the interface is enabled and background
// transfers are switched on then any write to
// this page will load the shift register from
// the bottom eight bits of the Address Bus and
// transfer the byte over SPI in the background
`define SPI_XFER 8'hFF // $FF00,65280
// SPI Background Transfer Finite State Machine codes
`define STATE_0 3'b000 // Inactive
`define STATE_1 3'b001 // Prologue
`define STATE_2 3'b010 // Dividing
`define STATE_3 3'b011 // Shifting
`define STATE_4 3'b100 // Shifted
`define STATE_5 3'b101 // Epilogue
// Clock divider for slow speed background transfers
`define SLOW_CLK_DIVIDER 66 // This should give an approximate SPI clock of 26.25MHZ/53 = 396KHZ
module qlromext
(
input clk,
input ce_sd,
output dtack,
input [15:0] a,
output [7:0] d,
input romoel,
output io1,
input io2,
output io3,
output io4,
output sd_clk,
output sd_cs1l,
output sd_cs2l,
input sd_do,
output sd_di
);
// It's a bit of a waste of time setting initial values of registers
// to anything other than 0 because the synthesis tool doesn't appear
// to translate them into logic. By default, all CPLD registers are
// set to 0 at power up.
reg interface_enabled = 0; // Determines whether the interface is enabled
// SPI Slave Selects
reg ss1 = 1;
reg ss2 = 1;
reg ss3 = 1;
// Foreground SPI bits
reg fg_mosi = 1;
reg fg_sclk = 1;
// Background SPI bits
reg bg_mosi = 1;
reg bg_sclk = 1;
// SPI background transfer control
reg spi_bg_enabled = 0; // If true, background transfers are enabled
reg [2:0] spi_state = `STATE_0; // Background transfer current state
reg spi_xfer_running = 0; // If true, an SPI background transfer is in progress
reg spi_fast = 0; // If true, the maximum SPI clock rate (25MHZ) is used for
// background SPI transfers
reg [7:0] spi_shiftreg = 0; // SPI Shift Register
reg [3:0] spi_counter = 0; // 4 bit counter used to control SPI bit shifting
reg [6:0] spi_divider = 0; // 7 bit counter used to divide the clock for slow SPI background transfers
// Connect the SPI signals
assign sd_di = (spi_xfer_running) ? bg_mosi : fg_mosi;
assign sd_clk = (spi_xfer_running) ? bg_sclk : fg_sclk;
assign io1 = (spi_xfer_running) ? bg_mosi : fg_mosi;
assign io3 = (spi_xfer_running) ? bg_sclk : fg_sclk;
assign sd_cs1l = (interface_enabled) ? ss1 : 1'b1;
assign sd_cs2l = (interface_enabled) ? ss2 : 1'b1;
assign io4 = (interface_enabled) ? ss3 : 1'b1;
wire miso = (ss3) ? sd_do : io2 /* synthesis syn_keep=1 */ ;
//reg [5:0] dtack_delay;
//`define DTACK_DELAY 45 // Simulate timing of QL bus pheripheral (4 clock cycles at 7.5Mhz): 84Mhz / 7.5Mhz * 4 = ~45
reg [6:0] dtack_delay;
`define DTACK_DELAY 56 // Simulate timing of QL bus pheripheral (4 clock cycles at 7.5Mhz): 105Mhz / 7.5Mhz * 4 = 56
reg prev_romoel;
reg qlsd_dtack;
assign dtack = qlsd_dtack;
// --------------------------------------
// Control the Data Bus
// --------------------------------------
wire [7:0] data_out = (spi_bg_enabled) ? spi_shiftreg : { 7'b0000000, miso };
assign d = (interface_enabled && ( a == `SPI_READ ))?data_out:8'h00;
// --------------------------------------
// Process changes on the Address Bus
// --------------------------------------
always @(posedge clk) begin
qlsd_dtack <= !prev_romoel && !romoel && dtack_delay == 0;
if (dtack_delay != 0) dtack_delay <= dtack_delay - 6'd1;
prev_romoel <= romoel;
if (!romoel && prev_romoel)
begin
dtack_delay <= `DTACK_DELAY;
case (a)
`IF_ENABLE :
begin
// Enable the interface
interface_enabled <= 1;
end
`IF_DISABLE :
begin
// Disable the interface
interface_enabled <= 0;
end
`IF_RESET :
begin
// Reset the interface
fg_mosi <= 1;
fg_sclk <= 1;
ss1 <= 1;
ss2 <= 1;
ss3 <= 1;
spi_fast <= 0;
spi_bg_enabled <= 0;
end
`SPI_XFER_FAST :
begin
// Enable SPI background transfers at full speed
// SPI_READ now gets the SPI Shift Register
spi_fast <= 1;
spi_bg_enabled <= 1;
end
`SPI_XFER_SLOW :
begin
// Enable SPI background transfers at low speed
// SPI_READ now gets the SPI Shift Register
spi_fast <= 0;
spi_bg_enabled <= 1;
end
`SPI_XFER_OFF :
begin
// Disable SPI background transfers
// SPI_READ now gets foreground MISO
spi_bg_enabled <= 0;
end
`SPI_DESELECT :
begin
// Clear all slave selects
ss1 <= 1;
ss2 <= 1;
ss3 <= 1;
end
`SPI_SELECT1 :
begin
// Select SPI Slave #1
ss1 <= 0;
ss2 <= 1;
ss3 <= 1;
end
`SPI_SELECT2 :
begin
// Select SPI Slave #2
ss1 <= 1;
ss2 <= 0;
ss3 <= 1;
end
`SPI_SELECT3 :
begin
// Select SPI Slave #3
ss1 <= 1;
ss2 <= 1;
ss3 <= 0;
end
`SPI_SET_MOSI :
begin
// Bit-banged SPI; Set MOSI=1
fg_mosi <= 1;
end
`SPI_CLR_MOSI :
begin
// Bit-banged SPI; Set MOSI=0
fg_mosi <= 0;
end
`SPI_SET_SCLK :
begin
// Bit-banged SPI; Set SCLK=1
fg_sclk <= 1;
end
`SPI_CLR_SCLK :
begin
// Bit-banged SPI; Set SCLK=0
fg_sclk <= 0;
end
endcase
end
end
// --------------------------------------
// Handle SPI background transfers
// Finite State Machine using spi_state
// --------------------------------------
always @(posedge clk)
begin
if (ce_sd)
begin
case (spi_state)
`STATE_0:
// Inactive
// Stay in this state while:
// The interface is disabled
// Background transfers are disabled
// An access to the SPI_XFER address page is not detected
begin
spi_state <= (interface_enabled && spi_bg_enabled && !romoel && (a[15:8] == `SPI_XFER)) ? `STATE_1 : `STATE_0 ;
end
`STATE_1:
// Prologue
// Initialise registers for the transfer
begin
spi_shiftreg <= a[7:0]; // Load the SPI shift register from the bottom 8 bits of the Address Bus
bg_mosi <= 1;
bg_sclk <= 1; // Set background I/O lines to high
spi_counter <= 0; // Reset shift counter
spi_divider <= `SLOW_CLK_DIVIDER; // Reset clock divider
spi_xfer_running <= 1; // Signal that a background transfer is running
// This selects the background SPI output lines
spi_state <= (spi_fast) ? `STATE_3 : `STATE_2; // Select the next state according to the transfer speed
end
`STATE_2:
// Dividing
// Enter this state before transitioning SCLK when the transfer speed is set to slow
begin
spi_divider <= spi_divider - 1'd1;
spi_state <= (spi_divider == 0) ? `STATE_3 : `STATE_2 ; // Remain in this state until the clock divider count is satisfied
end
`STATE_3:
// Shifting
// Transition SCLK and shift the next bit across the SPI bus
begin
bg_sclk <= !bg_sclk;
spi_counter <= spi_counter + 1'd1;
if (bg_sclk)
// SPI clock went low to high; output the next bit
bg_mosi <= spi_shiftreg[7];
else
// SPI clock went high to low; input the next bit
spi_shiftreg <= { spi_shiftreg[6:0], miso };
spi_divider <= `SLOW_CLK_DIVIDER; // Always reset the clock divider ahead of next SCLK transition
if (spi_counter == 15)
spi_state <= `STATE_4; // If the byte has been shifted move to next state
else
spi_state <= (spi_fast) ? `STATE_3 : `STATE_2 ; // Else next state depends upon transfer speed
end
`STATE_4:
// Shifted
// Shift has completed; reset registers
begin
spi_xfer_running <= 0; // Signal transfer ended
bg_mosi <= 1; // Reset MOSI
spi_state <= `STATE_5; // Next state is Epilogue
end
`STATE_5:
// Epilogue
// Wait for the access to the SPI_XFER address page to end
begin
spi_state <= (!romoel && (a[15:8] == `SPI_XFER)) ? `STATE_5 : `STATE_0 ;
end
endcase
end
end
endmodule