Freitag, 11. April 2014

So einfach geht's mit CF-Karten nun auch wieder nicht

Das war nun auch zu schön um wahr zu sein. Die Demo-Schaltung mit einem Microcontroller und einer direkt dort angeschlossenen CF-Karte funktioniert leider nur deshalb, weil der Microcontroller die CF-Karte erstens mit Port-Bausteinen und zweitens auch noch langsam genug anspricht. Wenn man, wie in meinem Falle, die CF Karte direkt an den Adress- und Datenbus anschließen möchte, dann sollte man auch einen Blick in die Timing-Diagramme werfen. Und genau hier zeigen sich ein unschönes und zwei hässliche Probleme:
  1. Die CF-Karte erwartet die Adress-Informationen auf den Leitungen A0..A2 70ns vor dem eigentlichen Schreib- oder Leseimpuls (hier IORQ und RD oder WR). Der Z8S180 legt bei einem 33 MHz Takt die Adress-Informationen aber gerade einmal 5ns vor dem Zugriff auf den Bus.
  2. Die CF-Karte braucht relativ lange nach dem Abfall der Schreib- Lesesignale, bis es den Datenbus wieder freigibt (in den Tri-State geht). Immerhin bis zu 30ns, laut Datenblatt. In der Zeit ist der Z8 schon beinahe wieder am nächsten Befehl.
  3. Unschön, aber mit einigen -zig Wait-States zu regeln ist die relativ lange Wartezeit von bis zu 290ns auf die Daten aus der CF-Karte.
Ich habe spaßeshalber diesen Zugriffsmechanismus einmal für einen CPLD in Verilog geschrieben und durchsimuliert. Hier ist das Programm:


module CF_Card_Interface(
  clk,
  addr_in,
  addr_compare,
  addr_out,
  data_z8,
  data_cf,
  iorq,
  wr,
  rd,
  cfrd,
  cfwr,
  cfsel,
  z8wait);

input clk;
input [7:0] addr_in;
input [4:0] addr_compare;
output [2:0] addr_out;
reg [2:0] addr_out;
inout [7:0] data_z8;
inout [7:0] data_cf;
input iorq;
input wr;
input rd;
output cfrd;
reg cfrd;
output cfwr;
reg cfwr;
output cfsel;
reg cfsel;
output z8wait;
reg z8wait;
reg [5:0] counter;
reg [7:0] data_z8_reg;
reg [7:0] data_cf_reg;

wire cpld_sel = (iorq == 0 && addr_compare == addr_in[7:3]) ? 1 : 0;
wire z8_io_read = (rd == 0 && cpld_sel == 1) ? 1 : 0;
wire z8_io_write = (wr == 0 && cpld_sel == 1) ? 1 : 0;

assign data_z8 = (z8_io_read == 1) ? data_z8_reg : 8'bzzzzzzzz;
assign data_cf = (z8_io_write == 1) ? data_cf_reg : 8'bzzzzzzzz;

always @(negedge clk) begin
  if (!z8_io_read && !z8_io_write) begin
    counter = 0;
    cfrd = 1;
    cfwr = 1;
    cfsel = 1;
    z8wait = 1;
  end else begin
    case(counter) 
      0: begin
        addr_out <= addr_in[2:0];
        z8wait = 0;
        if (wr == 0) begin
          data_cf_reg = data_z8;
        end
      end
      3: if (rd == 0) begin
        cfrd = 0;
        cfsel = 0;
      end else begin
        cfsel = 0;
        cfwr = 0;
      end
      13: if (rd == 0) begin
        data_z8_reg = data_cf;
      end
      14: begin
        cfsel = 1;
        cfrd = 1;
        cfwr = 1;
      end
      15: begin
        z8wait = 1;
        end
      endcase
      counter = counter + 1;
    end
  end
endmodule

Soweit das Modul. Das Modul-Interface ist gleichzeitig die Pin-Beschreibung des CPLD. Ich möchte die Pins hier im einzelnen beschreiben.
Clk ist der Ausgang Phi des Z8S180 und somit ein 33 MHz Taktsignal.
Addr_In sind die Adressleitungen A0...A7 der CPU, Data_z8 ist der Datenbus der CPU.
Addr_Compare ist mit einem 5-stelligen DIP-Schalter verbunden, über den von außen der Adressbereich des Bausteins eingestellt werden kann.
IORQ, WR und RD sind die low-aktiven Schreib-/Lesesignale der CPU.
Z8WAIT ist das low-aktive WAIT Signal an die CPU.
Die restlichen Leitungen Addr_Out, Data_CF, CFWR, CFRD und CFSEL sind mit der CF-Karte verbunden.

Und so sieht das ganze dann auf dem Logic-Analyzer aus. Hier zuerst ein Schreibzugriff.


In dem Moment, in dem der Z8 Adresse und IORD anlegt, erkennt man, dass das interne Signal CPLD_SEL aktiv wird. Bei der nächsten fallenden Flanke des CLK-Signals (bei 120ns) werden sowohl Adress- und Datenleitungen für die CF-Karte beschickt, als auch ein WAIT-Signal an die Z8 gesendet.
3 CLK Zyklen später (also 90ns) werden sowohl CFSEL als auch CFWR aktiviert, also der Schreibvorgang in die CF-Karte gestartet. Laut Datenblatt der CF-Karte darf dies frühestens 70ns nach dem stabilen Anlegen der Adresse geschehen.
Gut 300ns später (die CF-Karte braucht 290ns für den Schreibvorgang), werden CFSEL und CFWR wieder deaktiviert. Damit sich nun der Datenbus der CF-Karte beruhigen kann, wird noch einmal ein CLK-Zyklus gewartet, bevor das WAIT-Signal zur Z8 wieder deaktiviert wird. Die Z8 läuft daraufhin wieder an und beendet den Schreibvorgang.

Hier dasselbe für den Lesezugriff:


Der Unterschied liegt hier am Ende des Lesezyklus. 300ns nachdem der CPLD der CF-Karte das Lesesignal gegeben hat, werden die Daten der CF-Karte auf den Datenbus des Prozessors gelegt. Bis dahin ist der Status auf dem Datenbus nicht definiert. Danach wird der Z8 wieder aus dem Wartezustand geholt und der Ablauf ist analog zum Schreibzugriff.

Leider ist mein Programm für die kleinen CPLDs schon zu kompliziert, sodass mit die Xilinx-ISE Workbench einen XC108-PC84 vorschlägt, einen Klotz, größer als die eigentliche CPU. Ich werde mal versuchen, ob ich dasselbe auch mit herkömmlichen TTL Bausteinen zusammen setzen kann. Wird zwar bestimmt genauso groß, wie der CPLD, dafür aber preiswerter und ohne großen Aufwand.

Keine Kommentare:

Kommentar veröffentlichen