• Please review our updated Terms and Rules here

XT-SD controller CPLD based (ISA-8)

Tronix

Experienced Member
Joined
Dec 14, 2012
Messages
138
Location
Russia, Moscow
Hello all
and happy new year!

This is my "weekend project" - ISA XT-SD controller. I know about the existence of XT-IDE and IDE-SD adapters. Therefore, there is no problem to use SD card on XT. This project is just a hobby, no more.

So, main idea - use small CPLD for SPI bits shifting:

qs_tuefnzkfgfp_h9c904xuh8tq.jpeg


On a board you can see only two IC - 74LS138 as address decoder and EPM3032ALC44-10 CPLD. There is also 1117 linear regulator (5V to 3.3V) and some resistors for SPI pull-up and power filter capasistor 0.1uF. I planned to place here EEPROM memory for additional ROM-BIOS, but too lazy to solder a lot of wires and ordered Lo-tech ISA ROM board.

At the PC side controller have control/status and data ports. Control port used for control SPI CS signal (bit 0), LED (bit 1). Status port are merged with control port. When read control/status port bit 7 indicate INSERT signal from sd-card holder. Bit 6 - SPI BUSY status.

The OUT instruction is used to send data to the SPI
Code:
out dx,al
And IN instruction is used for get data from SPI
Code:
in al,dx

When previous write cycle not finished and new write attempt occurred, CPLD control CH_IO_RDY signal (make it low) to wait for the previous cycle to complete. When reading data from port CH_IO_RDY signal goes low immediately, MOSI set to 0xFF and shifting begin until bits count = 7. When shifting finished IO_CH_CHK signal goes hi-Z and data present on data bus until /RD signal goes high.

Some diagrams (please dont look at KB_CLK and KB_DATA labels,I forgot to remove them):

Simple write numbers 1 to 3 with large delays between OUT instructions. CH_IO_RDY (/Ready) signal don't touch
Code:
mov al,1
out dx,al
nop
nop
nop
nop
nop
nop
mov al,2
out dx,al
nop
nop
nop
nop
nop
nop
mov al,3
out dx,al
1gyb8rasc-4fa2xv6hyixvght8e.png
[/spoiler]

Writing numbers 1 to 3 without delays. CPLD controlled IO_CH_RDY (/Ready) signal:
Code:
mov al,1
out dx,al
inc al
out dx,al
inc al
out dx,al
8uooojz06c5iqhozg8sd4hrt3ue.png


Reading two bytes from SPI:
Code:
in al,dx
in al,dx
doerp2gm_6iysfxmf9bw5rcs4k8.png


As a software temp solution i used modified SDPP driver from this forum. So, it's workes:

irh1jthal23tiljuxx6usyitc6c.jpeg


Unfortunately as you will see speed is not too high. At first SDPP driver written in clean C languarge without assembler optimization. This is solved by developing my own additional ROM-BIOS. At second CPLD clocked from CLK ISA signal (8 MHz) and divided by two. So, SPI clock speed is 4 MHz. I tried clocked CPLD from ISA OSC signal (14,3 MHz) without success. I think because they are not synchronized with each other. I just started learning verilog so I do not exclude a bad design. If someone is interested, now here is a verilog code:

Code:
module MX2_CPLD(
    // System IO
    input    Clk,              // Input frequency               ISA CLK/ ISA OSC
    input    SysClk,		   // Input frequency				ISA CLK
    input    Res,              // Input reset signal, active 1  ISA RESET
    input    nRD,              // Input read port signal        ISA /IOR
    input    nWR,              // Input write signal            ISA /IOW
    input    nCS,              // Input select controller       from 74138 decoder
    input    Adr,              // Input data/control bus        ISA SA0
    inout    [7:0]Dat,         // Data bus                     	ISA SD0..SD7
    output   Ready,            // Ready                         ISA I/O CH RDY
    // SPI
    output   nSEL,             // Output card select
    output   reg SCK,          // Output SPI frequency
    input    MISO,             // Input SPI data
    output   MOSI,             // Output SPI data
    output   nRED,             // Output LED indicator
    input    nINS              // Input card inserted
);
// Internal registers
reg [7:0]Data;                 // Data register
reg [1:0]Ctrl;                 // Control register
reg [2:0]Bits;                 // Shifted bits counter
reg RMISO;                     // MISO signal latch
reg BUSY;                      // SPI busy
reg MRDY;                  	   // Read/write attempt flag when SPI busy
reg BEGWR;                     // Begin write to data port (falling nWR)
reg BEGWRC;					   // Begin write to control port (falling nWR)
reg BEGRD;                     // Begin read from data port (falling nRD)
// Combinatorics
assign nSEL = ~Ctrl[0];        // Card select
assign nRED = ~Ctrl[1];        // RED LED
assign MOSI = Data[7];         // Output MOSI SPI
assign Dat[7:0] = (~nCS & ~nRD) ?
                    (Adr) ? {~nINS,BUSY,4'h0,Ctrl[1:0]} : Data[7:0]
                    : 8'hZZ;
assign Ready = (MRDY) ? 1'b0 : 1'bZ;  // If read/write attempt when SPI busy down READY signal

// Synchronous logic
always @(posedge Clk) begin
    // Write to control port maybe asynchronous
    if (~nCS & ~nWR & Adr & ~BEGWRC & ~BEGWR & ~BEGRD)
      BEGWRC <= 1'b1;

   if (nWR & BEGWRC)
      begin
         BEGWRC <= 1'b0;
         Ctrl[1:0] <= Dat[1:0];
      end
      
    // Reset signal high priority
    if (Res)
      begin
         // Clear internal registers
         Ctrl[1:0] <= 2'h0;
         Bits[2:0] <= 3'h0;
         BUSY <= 1'b0;
         SCK <= 1'b0;
         MRDY <= 1'b0;
         BEGWR <= 1'b0;
         BEGWRC <= 1'b0;
         BEGRD <= 1'b0;
         //PRESTART <= 1'b0;
      end 
   else
      begin
         // SPI state
         if (BUSY)
            begin
               // SPI working...
               if (SCK)
                  begin
                     // SCK low
                     SCK <= 1'b0;
                     // Falling, shift bits
                     Data[7:0] <= {Data[6:0],RMISO};
                     // Count bits
                     Bits[2:0] <= Bits[2:0] + 3'h1;
                     // This is seven bit?
                     if (Bits[2] & Bits[1] & Bits[0]) 
                        begin
                           BUSY <= 1'b0;   // Clear BUSY flag
                           MRDY <= 1'b0;   // Goes IO CH RDY to hi-Z
                        end;
                  end 
               else 
                  begin
                     // SCK high
                     SCK <= 1'b1;
                     // Rising, sample data
                     RMISO <= MISO;
                  end
            end 
         else
            begin
               // Clear bits counter
               Bits[2:0] <= 3'h0;
            end
         
         // Writing to data port (preliminary)
            if (~nCS & ~nWR & ~Adr & ~BEGWR & ~BEGRD & ~BEGWRC)
            begin
               BEGWR <= 1'b1;         // Set preliminary write flag
               if (BUSY)
                  // If SPI busy set IO CH RDY low
                  MRDY <= 1'b1;
            end
            
         // Final writing to data port (nWR goes high)
         if (nWR & BEGWR)
            begin
            BEGWR <= 1'b0;            // Clear preliminary write flag
            Data[7:0] <= Dat[7:0];       // Grab some data from ISA
            BUSY <= 1'b1;         // Set prestart flag for SPI state machine
            end

         // Reading from data port (preliminary)
            if (~nCS & ~nRD & ~Adr & ~BEGRD & ~BEGWR & ~BEGWRC)
            begin
               BEGRD <= 1'b1;         // Set preliminary read flag
               if (BUSY)
                  // If SPI busy set IO CH RDY low
                  MRDY <= 1'b1;
               else
                  begin
                     MRDY <= 1'b1;             // set IO CH RDY low
                     Data[7:0] <= 8'b11111111;   // 0xFF to data register
                     BUSY <= 1'b1;         // Set prestart flag for SPI state machine
                  end
            end
            
         // Final reading from data port (nRD goes high)
         if (BEGRD & nRD)
            begin
            BEGRD <= 1'b0;            // Clear preliminary read flag
            // all other job done by combinatorics logic
            end
      end
end

// End
endmodule
 
Nice work! This seems like the logical way forward if folks are interested in moving from IDE to SD, rather than working with converters/adapters of questionable quality. I'd considered doing something similar, using a microcontroller to allow polled or DMA transfers, to lessen the load on the host CPU.

Have you looked into potential corruption issues (e.g. sudden power-off with the Raspberry Pi) if the system is locked up/reset during the middle of a SD access?
 
Nice work!
Have you looked into potential corruption issues (e.g. sudden power-off with the Raspberry Pi) if the system is locked up/reset during the middle of a SD access?

Thanks. I did not think about it. EPM3032 is very very old CPLD, which has 32 macrocells only. For example, low-end Cyclone IV FPGA has ~6000 macrocells. Thus, no way to organize any buffer or any additional logic in this small CPLD. When the power is unecpected turned off, the behavior of this controller should not be different from the XT-IDE or XT-IDE with SD-IDE adapter.
 
Back
Top