MMC Treiber Optimierung mittel Bit Bang Optimierung

Sklaventreiber
Günther
Developer
Beiträge: 587
Registriert: Freitag 9. September 2005, 21:48

MMC Treiber Optimierung mittel Bit Bang Optimierung

Beitrag von Günther »

Hi,
gibt es eigentlich eine Möglichkeit vom Comiler ein Assembler listing (am besten mit unterlegten C-Zeilen) erstellt zu bekommen?

Oder geht das nur über den Disassemblers? (Wenn ja, wie?)

Ich wollte mir mal anschauen, inwieweit der MMC Treiber mit Assembler noch optimiert werden kann oder ob der C-Compiler das eh schon perfekt macht.

Es geht speziell um diese (bereits von mir leicht optimierte) C-Code Passagen:

Code: Alles auswählen

    // read 512 bytes from device
	volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
	volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;
	unsigned char  t;
	cpi->iop_padat |= SD_DI;  

	for (i = 0; i < 512; i++)
	{
		data[i]=0;
		for(t = 0x80; t != 0; t >>= 1) 
		{
			cp->cp_pbdat |= SD_CLK;
			if (cpi->iop_padat & SD_DO)
				data[i] |= t;
			cp->cp_pbdat &= ~SD_CLK;
		}
	}
und

Code: Alles auswählen

	// send complete block 
	volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
	volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;
	unsigned char  t;

	for (i = 0; i < 512; i++)
	{ 
		for(t = 0x80; t != 0; t >>= 1) 
		{
			cp->cp_pbdat &= ~SD_CLK;
			if (data[i] & t)
			  cpi->iop_padat |= SD_DI;
			else
			  cpi->iop_padat &= ~SD_DI;
			cp->cp_pbdat |= SD_CLK;
		}
	}
	cp->cp_pbdat &= ~SD_CLK;
Günther
Houdini
Developer
Beiträge: 2183
Registriert: Mittwoch 10. Dezember 2003, 07:59

Beitrag von Houdini »

man gcc ist dein Freund
-S Stop after the stage of compilation proper; do not assemble. The output is in the form of an assembler code file for
each non-assembler input file specified.

By default, the assembler file name for a source file is made by replacing the suffix .c, .i, etc., with .s.

Input files that don't require compilation are ignored.
Edit: ich glaube nicht das du RISC assembler code per Hand optimieren willst oder? :-)
DBoxBaer
Senior Member
Beiträge: 255
Registriert: Donnerstag 25. August 2005, 11:34

Beitrag von DBoxBaer »

Houdini hat geschrieben:
Edit: ich glaube nicht das du RISC assembler code per Hand optimieren willst oder? :-)
Das ist gar nicht so schlimm... Für meinen IDE Treiber habe ich das
auch noch vor. Jedenfalls mal angedacht.
Es lohnt sich auf jeden Fall mal das Ergebnis des Compilers
anzuschauen, und manchmal kann man dann recht einfach
was verbessern ohne lange am C-Code zu schrauben, bis der
Compiler blickt was man eigentlich will... (vgl. "volatile")

Wenn man sich auf eine einzelne Loop konzentriert kann man
da manchmal einiges rausholen.

Da ich das im IDE Treiber auch angefangen habe:

Du legst ein routine.S File an mit etwa :-) diesem Inhalt

Code: Alles auswählen

.text
.globl dboxide_insw_loop
dboxide_insw_loop:
  nop
  nop
  /* not yet implemented */
  blr
Mit einer Deklaration wie dieser:

Code: Alles auswählen

extern int dboxide_insw_loop(int a,int b);
Kann man das aus der C-Welt aufrufen.

Ach ja: Im Makefile ist die .S Datei ganz normaler Source. Also
einfach das zu erwartende routine.o File eintragen...

Ja, richtig, da fehlt noch was: Die Parameter auf Assembler Seite:
r3 ist der erste (a), r4 der zweite (b) usw.
Return Value ist auch r3.

Wenn ich da aber so drüber nachdenke....

In Deinem Fall würde ich mir nur die innere Loop anschauen:

extern char mmc_read_byte(void * pbdat, void * padat, u32 SD_CLK, u_32 SD_DO);

for (i = 0; i < 512; i++)
{
data= mmc_read_byte(&cp->cp_pbdat, &cpi->iop_padat, SD_CLK, SD_DO );
}

Theoretisch kannst Du das "unrolled" kodieren. Dann sind
das vielleicht 4 Instruktionen pro Bit. unrolling vermeidet den
Bedingten Sprung für die Loop. (Bedingte Sprünge sind für
die Pipeline der CPU schlecht). Oh: Das kann auch in der Loop
helfen.

Probier doch mal in C diesen Code:
Vielleicht ist das ja schon ne Verbesserung:

Code: Alles auswählen

   ...

   volatile unsigned long * clk = &cp->cp_pbdat;
   volatile unsigned long * data = &cpi->iop_padat
   unsigned long clk_on  = *clk | SD_CLK;
   unsigned long clk_off = *clk & ~SD_CLK;

   for (i = 0; i < 512; i++)
   {
     register char temp = 0;
     *clk = clk_on;
     temp |= (*data & SD_DO) >> fuers erste bit
     *clk = clk_off;
     *clk = clk_on;
     temp |= (*data & SD_DO) >> fuers naechste bit
     *clk = clk_off;
     ....
     *clk = clk_on;
     temp |= (*data & SD_DO) << fuers letzte bit
     *clk = clk_off;
     data[i] = temp; 
   } 
Ich hoffe es erklärt sich von selbst, was das soll...


Ciao,

DboxBaer
just_me
Einsteiger
Einsteiger
Beiträge: 123
Registriert: Montag 28. November 2005, 11:31

Re: Assembler

Beitrag von just_me »

Günther hat geschrieben:gibt es eigentlich eine Möglichkeit vom Comiler ein Assembler listing (am besten mit unterlegten C-Zeilen) erstellt zu bekommen?

Oder geht das nur über den Disassemblers? (Wenn ja, wie?)
Eine andere Möglichkeit (neben der weiter oben von Houdini erwähnten) ist, den Compiler zusätlich mit option "-g" aufzurufen und dann mit objdump das objectfile ausgeben zu lassen. Im Verzeichnis /tuxbox/head/driver/mmc also diese Zeile ausführen:

Code: Alles auswählen

/tuxbox/cdk/powerpc-tuxbox-linux-gnu/bin/gcc -g -D__KERNEL__ -DMODULE -I../include -I../dvb/include -I/tuxbox/head/cdk/linux/include -Wall -Wstrict-prototypes -Wno-trigraphs -Werror -O2 -fno-strict-aliasing -fno-common -fomit-frame-pointer -I/tuxbox/head/cdk/linux/arch/ppc -fsigned-char -msoft-float -pipe -ffixed-r2 -Wno-uninitialized -mmultiple -mstring -mcpu=860   -DKBUILD_BASENAME=mmc2  -c -o mmc2.o mmc2.c
"objdump -S mmc2.o" gibt dann gut lesbar: :)

Code: Alles auswählen

static unsigned char mmc_spi_io(unsigned char data_out) {
  volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
  38:   3d 20 00 00     lis     r9,0
  3c:   81 69 00 00     lwz     r11,0(r9)
  40:   7c 67 1b 78     mr      r7,r3
  volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;
  44:   39 4b 09 50     addi    r10,r11,2384
  unsigned char result = 0;
  48:   38 60 00 00     li      r3,0
  4c:   39 6b 09 c0     addi    r11,r11,2496
  unsigned char  i;

  for(i = 0x80; i != 0; i >>= 1) {
  50:   39 00 00 80     li      r8,128
  54:   48 00 00 40     b       94 <mmc_spi_io+0x5c>
    if (data_out & i)
      cpi->iop_padat |= SD_DI;
  58:   a0 0a 00 06     lhz     r0,6(r10)
  5c:   60 00 00 80     ori     r0,r0,128
    else
      cpi->iop_padat &= ~SD_DI;
  60:   b0 0a 00 06     sth     r0,6(r10)

    cp->cp_pbdat |= SD_CLK;
  64:   80 0b 01 04     lwz     r0,260(r11)
  68:   60 00 40 00     ori     r0,r0,16384
  6c:   90 0b 01 04     stw     r0,260(r11)
    if (cpi->iop_padat & SD_DO) {
  70:   a1 2a 00 06     lhz     r9,6(r10)
  74:   71 20 00 40     andi.   r0,r9,64
  78:   41 82 00 08     beq-    80 <mmc_spi_io+0x48>
        result |= i;
  7c:   7c 63 43 78     or      r3,r3,r8
    }
    cp->cp_pbdat &= ~SD_CLK;
  80:   80 0b 01 04     lwz     r0,260(r11)
  84:   55 08 f8 7f     rlwinm. r8,r8,31,1,31
  88:   54 00 04 a0     rlwinm  r0,r0,0,18,16
  8c:   90 0b 01 04     stw     r0,260(r11)
  90:   41 82 00 18     beq-    a8 <mmc_spi_io+0x70>
  94:   7c e0 40 39     and.    r0,r7,r8
  98:   40 a2 ff c0     bne-    58 <mmc_spi_io+0x20>
  9c:   a0 0a 00 06     lhz     r0,6(r10)
  a0:   54 00 06 6e     rlwinm  r0,r0,0,25,23
  a4:   4b ff ff bc     b       60 <mmc_spi_io+0x28>
  }

  return result;
}
Frieder
just_me
Einsteiger
Einsteiger
Beiträge: 123
Registriert: Montag 28. November 2005, 11:31

Beitrag von just_me »

Hallo Günther,

ich habe immer noch nicht meinen Lötkolben geschwungen, daher leider ungetestet... Trotzdem möchte ich es lieber jetzt posten, um doppelte Arbeit zu vermeiden:)

Der angefügte C-code wird vom GCC erstaunlich kompakt übersetzt (mit etwa 220 Instruktionen für jeweils 32 bit).
Ausserdem kommt der erzeugte Assembler code ohne Verzweigungen (ausser für die 2 Schleifen) aus, so dass es der Pipeline ziemlich gut gehen sollte:)

Auf der Haben Seite:
  • * Integer statt Character Verarbeitung
    * loop unrolling für jeweils 4 bit (8 bit lohnt vielleicht noch (verm. 2% flotter))
    * Verwendung von (x?0x01:0x00) wird von GCC erkannt und in bit Kopier Instruktion umgesetzt:)
    * Zugriff auf Daten über inkrementierten Pointer statt Array.

Code: Alles auswählen

void mmc_read_block_low_level(unsigned int *dest)
{
   volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
   volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;

   volatile unsigned long * clk = (volatile unsigned long *)&cp->cp_pbdat;
   volatile unsigned long * data =(volatile unsigned long *)&cpi->iop_padat;
   unsigned long clk_on  = *clk | SD_CLK;
   unsigned long clk_off = *clk & ~SD_CLK;

   unsigned int i,j;
   unsigned int in; /* no need to init */

   cpi->iop_padat |= SD_DI;

   for(i = 0; i<128; i++)
   {
      for(j = 0; j<8; j++)
      {
         cp->cp_pbdat = clk_on;
         in <<= 1;
         in |= ((*data & SD_DO)?0x01:0x00);
         cp->cp_pbdat = clk_off;

         cp->cp_pbdat = clk_on;
         in <<= 1;
         in |= ((*data & SD_DO)?0x01:0x00);
         cp->cp_pbdat = clk_off;

         cp->cp_pbdat = clk_on;
         in <<= 1;
         in |= ((*data & SD_DO)?0x01:0x00);
         cp->cp_pbdat = clk_off;

         cp->cp_pbdat = clk_on;
         in <<= 1;
         in |= ((*data & SD_DO)?0x01:0x00);
         cp->cp_pbdat = clk_off;
      }
      *dest++ = in; /* here should be an endianness macro */
   }
}
Und? reichts für MP3? :D

Frieder Ferlemann
DBoxBaer
Senior Member
Beiträge: 255
Registriert: Donnerstag 25. August 2005, 11:34

Beitrag von DBoxBaer »

just_me hat geschrieben: Auf der Haben Seite:
  • * Integer statt Character Verarbeitung
    * loop unrolling für jeweils 4 bit (8 bit lohnt vielleicht noch (verm. 2% flotter))
    * Verwendung von (x?0x01:0x00) wird von GCC erkannt und in bit Kopier Instruktion umgesetzt:)
    * Zugriff auf Daten über inkrementierten Pointer statt Array.
Gute Ideen! Gefällt mir.

Ein paar Anmerkungen trotzdem:
  • * Die erste (32 Bit statt 8) kam mir später auch noch in den Sinn,
    das bringt sicher richtig viel.

    * Ich hasse zwar "?:" weil das zum Teil missbraucht wird und dann
    keiner Lesen kann, aber wenn es der Compiler blickt...
    Da die Quelle und das Ergebnis doch sowieso jeweils ein Bit sind,
    kann man das ja doch auch gleichwertig ohne ?: schreiben, und
    dann wird der Compiler das auch nie anders übersetzen können.

    * Theoretisch sollte der Compiler das Pointer incrementieren
    eigentlich erkennen, aber schaden tut es sicher nicht. (Vgl. "?:")


    * entweder statt

    Code: Alles auswählen

    cp->cp_pbdat = clk_on;
    
    dies hier

    Code: Alles auswählen

    *clk = clk_on;
    
    oder die Defintion von clk oben bringt nicht wirklich viel.

    * Bringt eigentlich -O4 als Compiler-Option vielleicht noch was?

    * Mit nem Loop-Unrolling über 32 Bits könnte man sich die Shifts
    noch sparen: Dann sinds vielleicht nochmal ein paar Promille?!

    Code: Alles auswählen

    *clk = clk_on;
    in |= ((*data & SD_DO)?0x80000000:0);
    *clk = clk_off;
    *clk = clk_on;
    in |= ((*data & SD_DO)?0x40000000:0);
    *clk = clk_off;
    ...
    *clk = clk_on;
    in |= ((*data & SD_DO)?0x00000002:0);
    *clk = clk_off;
    *clk = clk_on;
    in |= ((*data & SD_DO)?0x00000001:0);
    *clk = clk_off;
    
So oder so:
Wenn man die innere Schleife sowieso schon in Assembler
anschaut, kann man sie ja auch fast schon durch eine eigene
Kopie ersetzen, bevor man nun lange am Source rumbastelt,
bis der Compiler das gewünschte liefert. Ich bin mir da nie
sicher, wann man diesen Schritt nun wagen sollte/darf...

Nachteil davon (Assembler zu verwenden) ist allerdings, das
der Code nicht mehr portabel ist: Bei dem mmc Treiber halte
ich das für einen wichtigen Punkt, den die Technik lässt sich
recht einfach auf viele andere Systeme übertragen.
Vielleicht wird das ja mal ein Treiber im Standard-Linux
Kernel? (Der Byte-Order Hinweis geht doch auch schon
in die Richtung...)
Ich glaube für das MMC Ding wäre aus diesem Grund mein Tipp:
Bleibe besser beim C-Code, wenn der Compiler ein ähnlich
gutes Ergebnis zu Stande bringen kann.

Bei meinem IDE-CPLD/Treiber Gespann ist die Nutzung
auf anderen Systemen eher sehr unwahrscheinlich, daher
nehme ich da wahrscheinlich keine Rücksicht. Und im Gegenteil
zum MMC muss ich einige Zugriffe eher bremsen... Das
kriegt man mit dem Compiler aber nun leider gar nicht
hin.

Dann noch was ganz anderes:

Ist es eigentlich sichergestellt, das auf pbdat keine anderen
Prozesse/Threads/Tasks (was auch immer) "gleichzeitig"
rummachen? (Sorry für diese exakten Fachbegriffe...)

Wenn das nämlich so wäre, dann wäre dieser Code empfindlicher
als der alte (Inhalt von clk_on und clk_off). Ein Problem hatte
der alte Code aber dann auch schon, weil selbst ein oder-gleich (|=)
unterbrochen werden kann: Es ist leider kein atomarer
"Read-Modify-Write" Zyklus.

Ciao,

DboxBaer
just_me
Einsteiger
Einsteiger
Beiträge: 123
Registriert: Montag 28. November 2005, 11:31

Beitrag von just_me »

DBoxBaer hat geschrieben:* Ich hasse zwar "?:" weil das zum Teil missbraucht wird und dann
keiner Lesen kann, aber wenn es der Compiler blickt...
Da die Quelle und das Ergebnis doch sowieso jeweils ein Bit sind,
kann man das ja doch auch gleichwertig ohne ?: schreiben, und
dann wird der Compiler das auch nie anders übersetzen können.
ich habe inzwischen versucht, den Compiler zum bitweisen Kopieren zu überreden:

Code: Alles auswählen

register union
{
   unsigned int b;
   struct
   {
        unsigned int b0:1;
        unsigned int b1:1;
        unsigned int b2:1;
       ...
   };
} in;
und entsprechend bei der Zuweisung dann "in.b31 = (*data & SD_DO);", aber dies gibt leider bei mir keinen kompakten Code.
oder die Defintion von clk oben bringt nicht wirklich viel.
Ja.
* Bringt eigentlich -O4 als Compiler-Option vielleicht noch was?
Nein, in beiden Fällen ist (bei meinem Code) die innere Schleife 25 Instruktionen lang.
* Mit nem Loop-Unrolling über 32 Bits könnte man sich die Shifts
noch sparen: Dann sinds vielleicht nochmal ein paar Promille?!
Ja, potentiell auch noch ein Faktor 1.5 bis 2 mehr:) Siehe nächsten Abschnitt.
Wenn man die innere Schleife sowieso schon in Assembler
anschaut, kann man sie ja auch fast schon durch eine eigene
Kopie ersetzen, bevor man nun lange am Source rumbastelt,
bis der Compiler das gewünschte liefert.
Das Optimum aus meiner Sicht wäre, eine inline Assembler Funktion zu haben, die jeweils eine Zeile wie:
"in |= ((*data & SD_DO)?0x40000000:0);"
in jeweils eine einzige der "rlwinm" (o.ä.) Bitoperationen überführt.

Code: Alles auswählen

static __inline__ int __set_bit_from_bit(int dest_nr, volatile unsigned long *addr, int src_nr)
{
  __asm__ __volatile__( "<Black Magic needs to be inserted here>");
}
Nachteil davon (Assembler zu verwenden) ist allerdings, das
der Code nicht mehr portabel ist: Bei dem mmc Treiber halte
ich das für einen wichtigen Punkt, ...
Falls die oben genannte inline Funktion gefunden werden kann, ist jeweils nur die Anpassung einer einzigen Funktion notwendig (für eine noch nicht unterstützte CPU würde eine HighLevel Rückfallfuntion greifen).
Falls ein Opcode dafür existiert (schiebe von bit nach bit, oder setze bitfeld von x von bitfeld y) liesse sich die Zahl der Instruktionen für ein einzelnes Bit noch auf 3 (oder falls man zwei dieser Ops braucht auf 4) senken (mein C-code hat etwa 6) - damit wäre aber die innere Schleife endgültig ausgereizt!
Ist es eigentlich sichergestellt, das auf pbdat keine anderen
Prozesse/Threads/Tasks (was auch immer) "gleichzeitig"
rummachen? (Sorry für diese exakten Fachbegriffe...)
Hier habe ich auch ein ungutes Gefühl - falls es so ist (ich glaube aber die IRQ sind gesperrt), könnte man z.B. jeweils für 32 oder 64 übertragene Byte die IRQ sperren und dann erneut aufsetzen.

Frieder


@Moderator: damit man es wiederfinden kann wären eigentlich die letzten Postings in diesem Thread (ab 5 inklusive) besser in einem eigenen Thread "MMC Treiber Optimierung mittels Bit Bang Optimierung" (oder so) im "dbox2 drivers" Forum aufgehoben - feel free to move!
Carjay
Developer
Beiträge: 122
Registriert: Sonntag 23. April 2006, 12:37

Beitrag von Carjay »

DBoxBaer hat geschrieben: Vielleicht wird das ja mal ein Treiber im Standard-Linux
Kernel? (Der Byte-Order Hinweis geht doch auch schon
in die Richtung...)
Eher unwahrscheinlich, ich habe mich eine Weile mit dem MMC-Subsystem vom 2.6er Kernel beschäftigt und es ist wesentlich generischer als der hartkodierte Code, der beim MMC-Treiber verwendet wurde und paßt nicht mit diesem zusammen.

Es gibt dort allerdings scheinbar keinen bit-banging-Treiber sondern nur ein paar Chipsatztreiber, die das ganze Lowlevel-Protokoll in Hardware umsetzen. Und um einen generischen Bit-Banging-Treiber umzusetzen fehlten mir noch gewisse Detail-Informationen über die low level Protokolle. Und auch Zeit und Lust.

Aus diesem Grund hatte ich dann einfach den 2.4er Treiber für den 2.6er angepaßt. Ging einfacher und problemloser.
Houdini
Developer
Beiträge: 2183
Registriert: Mittwoch 10. Dezember 2003, 07:59

Beitrag von Houdini »

@just_me, DBoxBaer,

:D Respekt :D
Günther
Developer
Beiträge: 587
Registriert: Freitag 9. September 2005, 21:48

Beitrag von Günther »

Hey Leute, ich sehe Ihr seit schon gut weiter gekommen :o

Hatte heute Abend leider keine Zeit zum mitwerkeln (wos doch gerade zu spannend ist ;). Ähnliche Ideen hatte ich auch - nur konnte ich es halt leider noch nicht austesten. Ich muss mir morgen unbedint Eure 19:03-20:20 Uhr Ideen mal anschauen - sofern bis dahin nicht schon der perfekte Bit-Banging Treiber enstanden ist ;)

Bei einem muss man allerdings ev. aufpassen. Die CPU werkelt mit 15ns pro Takt und die SD-Karte kann laut spec 'nur' 40ns (also 20 ns für jedes CLK wackeln). Eventuell muss man eine Assembler Operation zwischen zwei direkt aufeinander folgenden CLK-Wacklern vorziehen.
Ansonsten bin ich bei einer meiner Optimierungen auf (theoretisch) 8 Tick/bit gekommen (ev. auf 6 reduzierbar, aber da besteht die Gefahr, das eine CLK-edge zu schnell wird).

Ich habe übrigens keine genaue Angabe gefunden, wieviele Ticks ein Assembler Befehl an Rechenzeit verbraucht. Irgendwo habe ich gelesen 1 Tick (=15ns) , stimmt das? :gruebel:

Bei 6 Ticks pro bit würden wir 8 Mhz auf der SD-Karte erreichen. Das würde für MP3 noch genug Luft geben.

Eine andere Sache wäre aber noch optimierungsbedürftig. Wenn die Leseadresse geschrieben wurde, wird aktuell in einer while-Schleife auf das Start-Bit von der SD gewartet (gleiches beim Schreiben nach dem letzten Daten-Byte). Wird hier nicht Prozessorlast verschwendet. (Kenne mich mit Linux noch nicht so gut aus: mit welcher Prio läuft den so ein Treiber, bzw. gibt es feste Zeitscheiben auch für unterschiedliche Prio-Tasks?). Eventuell könnte man den Treiber für eine bestimmte Zeit zum schlafen schicken. In den SD-Header-Daten (CSD?) steht glaube ich die zu erwartene Zeit zwischen Leseadresse und erstem Datenbyte von der SD-Karte.

Die SMC-Optimierung ist noch nicht vom Tisch, obwohl ich da ein wenig skeptisch bin, ob das Umschalten zwischen bit-banging und SMC2-512-bytes-Burst nicht die Kommunikation aus dem Tritt bring (z.B. CLKs zuviel beim umschalten), dazu aber mehr im anderen Thread ;). Das würde natürlich nochmal richtig viel bringen. Mit ca. 22 Mhz auslesen (66/3) und trotzdem 0% Prozessorlast beim Burst ;)

Habe heute endlich den SD-Leser zum Auschlachten zugschickt bekommen und natürlich gleich Sagem tauglich gelötet. Morgen werde ich dann mal in die Praxis einsteigen, juhu!

just_me hat geschrieben:"objdump -S mmc2.o" gibt dann gut lesbar:
Genau das habe ich gesucht;) Benutze in der Arbeit eine Windof-IDE, die macht das auch immer so (automatisch).

Günther
just_me
Einsteiger
Einsteiger
Beiträge: 123
Registriert: Montag 28. November 2005, 11:31

Beitrag von just_me »

Hi,
Günther hat geschrieben:Ich muss mir morgen unbedint Eure 19:03-20:20 Uhr Ideen mal anschauen - sofern bis dahin nicht schon der perfekte Bit-Banging Treiber enstanden ist ;)
Code für heute hängt dran und ich komme in den nächsten Tagen wohl nicht zu mehr. Work in progress, happy hacking!
Bei einem muss man allerdings ev. aufpassen. Die CPU werkelt mit 15ns pro Takt und die SD-Karte kann laut spec 'nur' 40ns (also 20 ns für jedes CLK wackeln). Eventuell muss man eine Assembler Operation zwischen zwei direkt aufeinander folgenden CLK-Wacklern vorziehen.
Ansonsten bin ich bei einer meiner Optimierungen auf (theoretisch) 8 Tick/bit gekommen (ev. auf 6 reduzierbar, aber da besteht die Gefahr, das eine CLK-edge zu schnell wird).
5 Instruktionen pro Bit (allerdings ohne OP zwischen CLK und Lesen) hat der folgende Code. Wenn der Code kräftig umsortiert wird ist (trotz 5 OP/bit) auch eine OP zwischen CLK und Lesen mit drin:)

#define SD_DO_NUM 6 // on SD/MMC card pin 7
#define SD_DO (1<<SD_DO_NUM)

Code: Alles auswählen

#if 1
// Achtung, hier sind garantiert noch Bugs drin und das Timing ist zu knapp
static __inline__ int set_bit_from_bit( const unsigned int dest_nr,
                                        unsigned int dest,
                                        const unsigned int src_nr,
                                        const unsigned int src)
{
        __asm__ __volatile__("\n\
        rlwinm %[src],%[src],32-%[src_nr],0,31 \n\
        rlwimi %[dest],%[src],32-(%[dest_nr]+1),%[dest_nr],(%[dest_nr]+1)-1 \n"
        : [dest] "=&r" (dest)
        : [src] "r" (src), [dest_nr] "i" (dest_nr), [src_nr] "i" (src_nr)
        : "cc");
        return dest;
}
#else
static __inline__ int set_bit_from_bit( const unsigned int dest_nr,
                                        unsigned int dest,
                                        const unsigned int src_nr,
                                        const unsigned int src)
{
  unsigned int dest_mask = 1<<dest_nr;
  unsigned int  src_mask = 1<<src_nr;
  unsigned int in = dest;

  in &= ~src_mask;
  return in | ((src & src_mask)?dest_mask:0);
}
#endif

Code: Alles auswählen

void mmc_read_block_low_level2(unsigned int *dest)
{
   volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
   volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;

   volatile unsigned long * clk = (volatile unsigned long *)&cp->cp_pbdat;
   volatile unsigned long * data =(volatile unsigned long *)&cpi->iop_padat;
   unsigned long clk_on  = *clk | SD_CLK;
   unsigned long clk_off = *clk & ~SD_CLK;

   unsigned int i;
   unsigned int in;

   cpi->iop_padat |= SD_DI;

   for(i = 0; i<128; i++)
   {

        /* xx ns delay after clock before reading SD_DO is needed -
           insert NOPs or move the clocksetting within the macro */
        #define GET_THIS_BIT(n) cp->cp_pbdat = clk_on; \
                                in = set_bit_from_bit((n), in, SD_DO_NUM, *data); \
                                cp->cp_pbdat = clk_off;

        GET_THIS_BIT(31);
        GET_THIS_BIT(30);
        GET_THIS_BIT(29);
        GET_THIS_BIT(28);
        GET_THIS_BIT(27);
        GET_THIS_BIT(26);
        GET_THIS_BIT(25);
        GET_THIS_BIT(24);
        GET_THIS_BIT(23);
        GET_THIS_BIT(22);
        GET_THIS_BIT(21);
        GET_THIS_BIT(20);
        GET_THIS_BIT(19);
        GET_THIS_BIT(18);
        GET_THIS_BIT(17);
        GET_THIS_BIT(16);
        GET_THIS_BIT(15);
        GET_THIS_BIT(14);
        GET_THIS_BIT(13);
        GET_THIS_BIT(12);
        GET_THIS_BIT(11);
        GET_THIS_BIT(10);
        GET_THIS_BIT(9);
        GET_THIS_BIT(8);
        GET_THIS_BIT(7);
        GET_THIS_BIT(6);
        GET_THIS_BIT(5);
        GET_THIS_BIT(4);
        GET_THIS_BIT(3);
        GET_THIS_BIT(2);
        GET_THIS_BIT(1);
        GET_THIS_BIT(0);

        *dest++ = in; /* endianness ok? */
   }
}
wird zu:

Code: Alles auswählen

  ...
        GET_THIS_BIT(31);
     19c:       91 0a 01 04     stw     r8,260(r10)
     1a0:       80 0b 00 00     lwz     r0,0(r11)      ; 1 weiter nach unten??
     1a4:       54 00 d0 3e     rotlwi  r0,r0,26
     1a8:       50 09 07 fe     rlwimi  r9,r0,0,31,31
     1ac:       90 ea 01 04     stw     r7,260(r10)    ; 1 weiter nach oben??
        GET_THIS_BIT(30);
     1b0:       91 0a 01 04     stw     r8,260(r10)
     1b4:       80 0b 00 00     lwz     r0,0(r11)
     1b8:       54 00 d0 3e     rotlwi  r0,r0,26
     1bc:       50 09 0f bc     rlwimi  r9,r0,1,30,30
     1c0:       90 ea 01 04     stw     r7,260(r10)
  ...
(Für das "1 weiter nach unten" müssen die bit shifts noch getauscht und angepasst werden)
Eine andere Sache wäre aber noch optimierungsbedürftig. Wenn die Leseadresse geschrieben wurde, wird aktuell in einer while-Schleife auf das Start-Bit von der SD gewartet (gleiches beim Schreiben nach dem letzten Daten-Byte). Wird hier nicht Prozessorlast verschwendet. (Kenne mich mit Linux noch nicht so gut aus: mit welcher Prio läuft den so ein Treiber, bzw. gibt es feste Zeitscheiben auch für unterschiedliche Prio-Tasks?). Eventuell könnte man den Treiber für eine bestimmte Zeit zum schlafen schicken. In den SD-Header-Daten (CSD?) steht glaube ich die zu erwartene Zeit zwischen Leseadresse und erstem Datenbyte von der SD-Karte.
Wahrscheinlich haben nicht alle Karten eine interne Clock, so dass während der Wartezeit die externe Clock laufen muss. (bin mir nicht sicher)
Die SMC-Optimierung ist noch nicht vom Tisch, obwohl ich da ein wenig skeptisch bin, ...
ja, das lässt hoffentlich eines Tages die bit-bang routinen ganz alt aussehen!
DBoxBaer
Senior Member
Beiträge: 255
Registriert: Donnerstag 25. August 2005, 11:34

Beitrag von DBoxBaer »

Moin!

Habe zwar nicht viel Zeit, aber dafür dann doch...
Günther hat geschrieben: Bei einem muss man allerdings ev. aufpassen. Die CPU werkelt mit 15ns pro Takt und die SD-Karte kann laut spec 'nur' 40ns (also 20 ns für jedes CLK wackeln). Eventuell muss man eine Assembler Operation zwischen zwei direkt aufeinander folgenden CLK-Wacklern vorziehen.
Ansonsten bin ich bei einer meiner Optimierungen auf (theoretisch) 8 Tick/bit gekommen (ev. auf 6 reduzierbar, aber da besteht die Gefahr, das eine CLK-edge zu schnell wird).
Man muss aufpassen mit den Takten:
Die CPU führt eine Instruktion pro Takt aus, bei 66MHz sind das 15ns.
Aber:
Nur wenn das aus dem Cache kommt und die Pipeline schön gefüllt
bleibt. Ein Speicherzugriff auf das RAM z.B. kostet >90ns. Wenn man
die Cachelines in Betracht zieht, tritt sowas alle 4 Worte ein, da
werden dann 4 Worte gelesen um den Cache zu füllen. Wie lange nun
ein Zugriff auf die IO-Ports dauert weiss ich aber auch nicht.
Das könnte sehr wohl länger als 15ns dauern.
Letztlich sollte man die Loop nicht nur (oder gar nicht) theoretisch
berechnen, sondern praktisch messen: Und zwar mit nem Oszi oder
Logic Analyzer :-)

Es gibt da nette Überraschungen: So hat unsere 8xx CPU z.B. ein
Problem mit dem Cache, das uns auch hier erwischt:

Ideal wäre der Copy-Ablauf beim Lesen ja so:
Man liest Wort für Wort aus und schreibt es an die Zieladresse. Der
Cache sammelt dabei vier Worte auf und schreibt diese dann per Burst
ins RAM. Leider ist dem nicht so: Beim Zugriff auf das erste Wort,
das eine Cacheline abbildet, LIEST der Cache per Burst die vier
Worte aus dem RAM. (Also die vier Worte, die gleich überschrieben
werden...)
Nun gibt es sicher ein paar schlaue Leute, die sagen, da gibt es doch
die dbcz (oder so) Instruktion, um eine Cacheline zu löschen. Aber
leider (siehe Nutzung dieser Instruktion im Kernel) funktioniert die
bei unserer CPU nicht so wie erwünscht... Daher ist bei 8xx CPUs
MemCopy auch etwa 30% langsamer als denkbar.
Mit nem Oszi wird man diese Pausen bestimmt gut sehen können.

Dann noch etwas: Die Reihenfolge von Zugriffen (siehe EIEIO
Instruktion).
Im Prinzip sollte das hier nicht stören, weil die Port-Register
bestimmt "guarded" (?! oder so) sind, ausser das es das Timing
wohl auch nicht einfacher in der Berechnung macht. Das ist dann
halt die Crux mit RISC Prozessoren. (Beim Z80 war das noch
schön einfach...)

Was ich damit sagen will: Bei dieser Art Optimierung sollte man
auch wirklich kontrollieren, ob etwas eine Verbesserung bringt.
Weniger Anweisungen KANN am Ende schlechter sein. Beim IDE
Projekt muss ich an ein paar Stellen noch ein paar NOPs
hinzufügen(!) damit der Datentransport insgesamt schneller wird...
In der Summe bringt das am Ende aber nur sehr wenig, denn
der Kernel lässt an anderen Stellen auch gerne mal relativ viel
Zeit liegen, und evtl. optimiert man gerade an der falschen
Stelle...

Ciao,

DboxBaer
... und der Rest ist dann Software (TM)
Carjay
Developer
Beiträge: 122
Registriert: Sonntag 23. April 2006, 12:37

Beitrag von Carjay »

Günther hat geschrieben:mit welcher Prio läuft den so ein Treiber
Der 2.4er Kernel ist (ungepatcht) kooperativ, solange sich der Task im Kernelmode befindet und nicht schlafenlegt läuft er ohne Unterbrechung durch.

Gut, Interrupts gibt's natürlich auch noch. Aber die lassen sich ja abschalten.
Günther
Developer
Beiträge: 587
Registriert: Freitag 9. September 2005, 21:48

Beitrag von Günther »

Carjay hat geschrieben: Der 2.4er Kernel ist (ungepatcht) kooperativ, solange sich der Task im Kernelmode befindet und nicht schlafenlegt läuft er ohne Unterbrechung durch.

Gut, Interrupts gibt's natürlich auch noch. Aber die lassen sich ja abschalten.
D.h. wenn der Treiber in einer while loop stecken bleibt, steht solange das ganze System?
Carjay
Developer
Beiträge: 122
Registriert: Sonntag 23. April 2006, 12:37

Beitrag von Carjay »

Ja. Das gilt allerdings nicht für den 2.6er mit eingeschalteter Preemption (für den 2.4er gibt es entsprechende Patche).

Bei der dbox2 muß man auch immer an den Watchdog denken (wenn man Interrupts sperrt!). Der schnellste Weg zu einem dbox2-Reset ist im Treiber folgender: 8)

Code: Alles auswählen

cli();
while(1);
Günther
Developer
Beiträge: 587
Registriert: Freitag 9. September 2005, 21:48

Beitrag von Günther »

So, ich bin eben mal zu ein paar Tests gekommen.

Mit dem orginal MMC2 Treiber habe ich folgende Datenraten gehabt.
10 MB schreiben (12 Sekunden, 830 kb/s):
10 MB lesen (65 Sekunden, 150 kb/s).

Optimierung 1: 10 MB lesen (51 Sekunden, 200 kb/s)
Optimierung 2: 10 MB lesen (38 Sekunden, 260 kb/s)
Optimierung 3: 10 MB lesen (34 Sekunden, 290 kb/s)
Optimierung 4: 10 MB lesen (35Sekunden, 290 kb/s)
Optimierung 6: 10 MB lesen (31 Sekunden, 320 kb/s)

Die inline Assembler-Routinen haben leider nicht funktioniert (Treiber startet, es ist aber kein mounten möglich). Wahrscheinlich sind die Bits nicht richtig zugeordnet?.....

Verwunderlich hier ist, daß das Schreiben deutlich schneller ist als das Lesen. Da scheinen noch deutliche Optimierungsmoglichkeiten irgendwo anders zu liegen.

Den Treiber werde ich die Tage mal als MMC3 (nur als Testversion, und nur damit die Ideen nicht verloren gehen) in das cvs einchecken.

Soweit fürs erste,
Günther

PS: MMC3.c ist in / tuxbox / driver / mmc / Attic :gruebel: :gruebel: :gruebel: sollte doch eigentlich in mmc???? Liegt das am newmake branch?
---------------
Initialisierung:

Code: Alles auswählen

insmod /var/bin/fat.o
insmod /var/bin/vfat.o
insmod /var/bin/msdos.o
insmod /var/bin/mmc3.o
mount -t vfat /dev/mmc/disc0/part1 /mnt/mp3
------
Delta zu MMC2

#define MCC_O 3

in mmc_read_block()

Code: Alles auswählen

#if (MCC_O == 0)
	for (i = 0; i < 512; i++)
	    data[i] = mmc_spi_io(0xff);
#endif
#if (MCC_O == 1)
	for (i = 0; i < 512; i++)
	    data[i] = mmc_spi_read();
#endif
#if (MCC_O == 2)
	for (i = 0; i < 512; i++)
	    data[i] = mmc_spi_read();
#endif
#if (MCC_O == 3)
	for (i = 0; i < 512; i++)
	    data[i] = mmc_spi_read();
#endif

#if (MCC_O == 6) // wie 3 nur als integer gelesen
	unsigned int * data_integer = (unsigned int * )data;
	for (i = 0; i < 128; i++)
	    *data_integer++ = mmc_spi_read();
#endif

Code: Alles auswählen

#if (MCC_O == 1)
static inline unsigned char mmc_spi_read(void) {
  volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
  volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;
  unsigned char result = 0;
  unsigned char  i;

  cpi->iop_padat |= SD_DI;
  for(i = 0x80; i != 0; i >>= 1) {
    cp->cp_pbdat |= SD_CLK;
    if (cpi->iop_padat & SD_DO) {
    	result |= i;
    }
    cp->cp_pbdat &= ~SD_CLK;
  }
  return result;
}
#endif

Code: Alles auswählen

#if (MCC_O == 2)
static inline unsigned char mmc_spi_read(void) {
  volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
  volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;
  unsigned char result = 0;

   //volatile unsigned long * clk = (volatile unsigned long *)&cp->cp_pbdat;
   //volatile unsigned long * data =(volatile unsigned long *)&cpi->iop_padat;
   //unsigned long clk_high  = *clk | SD_CLK;
   //unsigned long clk_low   = *clk & ~SD_CLK;

  unsigned long clk_high = cp->cp_pbdat | SD_CLK;
  unsigned long clk_low =  cp->cp_pbdat & ~SD_CLK;

    cpi->iop_padat |= SD_DI;
 
    // 1 bit
    cp->cp_pbdat = clk_high;
    if (cpi->iop_padat & SD_DO) result |= 0x80;
    cp->cp_pbdat = clk_low;
     // 2 bit
    cp->cp_pbdat = clk_high;
    if (cpi->iop_padat & SD_DO) result |= 0x40;
    cp->cp_pbdat = clk_low;
     // 3 bit
    cp->cp_pbdat = clk_high;
    if (cpi->iop_padat & SD_DO) result |= 0x20;
    cp->cp_pbdat = clk_low;
      // 4 bit
    cp->cp_pbdat = clk_high;
    if (cpi->iop_padat & SD_DO) result |= 0x10;
    cp->cp_pbdat = clk_low;
     // 5 bit
    cp->cp_pbdat = clk_high;
    if (cpi->iop_padat & SD_DO) result |= 0x08;
    cp->cp_pbdat = clk_low;
     // 6 bit
    cp->cp_pbdat = clk_high;
    if (cpi->iop_padat & SD_DO) result |= 0x04;
    cp->cp_pbdat = clk_low;
     // 7 bit
    cp->cp_pbdat = clk_high;
    if (cpi->iop_padat & SD_DO) result |= 0x02;
    cp->cp_pbdat = clk_low;
     // 8 bit
    cp->cp_pbdat = clk_high;
    if (cpi->iop_padat & SD_DO) result |= 0x01;
    cp->cp_pbdat = clk_low;

  return result;
}
#endif

Code: Alles auswählen

#if (MCC_O == 3)
static inline unsigned char mmc_spi_read(void)
{
   volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm;
   volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport;

  unsigned long clk_high = cp->cp_pbdat | SD_CLK;
  unsigned long clk_low =  cp->cp_pbdat & ~SD_CLK;

  unsigned char result = 0;

   cpi->iop_padat |= SD_DI;

     // 1 bit
    cp->cp_pbdat = clk_high;
         result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
    cp->cp_pbdat = clk_low;
     // 1 bit
    cp->cp_pbdat = clk_high;
         result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
    cp->cp_pbdat = clk_low;
     // 1 bit
    cp->cp_pbdat = clk_high;
         result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
    cp->cp_pbdat = clk_low;
     // 1 bit
    cp->cp_pbdat = clk_high;
         result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
    cp->cp_pbdat = clk_low;
     // 1 bit
    cp->cp_pbdat = clk_high;
         result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
    cp->cp_pbdat = clk_low;
     // 1 bit
    cp->cp_pbdat = clk_high;
         result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
    cp->cp_pbdat = clk_low;
     // 1 bit
    cp->cp_pbdat = clk_high;
         result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
    cp->cp_pbdat = clk_low;
     // 1 bit
    cp->cp_pbdat = clk_high;
         result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
    cp->cp_pbdat = clk_low;

    return result;
} 
#endif
Barf
Developer
Beiträge: 1475
Registriert: Dienstag 4. Februar 2003, 22:02

Beitrag von Barf »

Aber bitte nicht dafür den newmake-Branch missbrauchen. Der newmake-Branch ist da für ein ganz bestimmtes Zweck, und ist nicht ein Sammelbecken für diverse experimentelles Zeug. Eventuell könnte mann sogar sagen, dass der Branch in zwischen mehr produktiv als experimentell ist.

Falls gebrancht Zeug eingecheckt sein soll, benutze ein dafür geeigneter (eventuell neu erstellte) Branch/Tag. Ich habe selbst "experimentelles Zeug" (io-config.xml-relatiert) zu einem Branch io-config hier eingecheckt.

Branching in diesem Sinn ist eigentlich nur notwendig, falls eine experimentelle Version eine existierede ersetzt. Dies ist hier nicht der Fall.
PS: MMC3.c ist in / tuxbox / driver / mmc / Attic sollte doch eigentlich in mmc???? Liegt das am newmake
Dateien, die nicht in HEAD vorhanden sind, werden von CVS in Attic abgelegt.
mb405
Tuxboxer
Tuxboxer
Beiträge: 2331
Registriert: Donnerstag 24. März 2005, 21:52

Beitrag von mb405 »

ich hab den treiber mal mit kompilieren lassen.
meine karten erkennt er bei der init nicht.
nokia box ohne zusatzhardware.

beim laden
------ MMC3 Driver Version 0.1-3 -------
------ optimized bit banging version -------
mmc2: Hardware init
mmc Card init
mmc Card init
mmc: error in mmc_card_init (1)
mmc: error in mmc_init (-1)
beim entfernen kommt 90%ig ein kernel uups.
Günther
Developer
Beiträge: 587
Registriert: Freitag 9. September 2005, 21:48

Beitrag von Günther »

mb405 hat geschrieben:meine karten erkennt er bei der init nicht.
Welchen Treiber benutzt Du denn bisher (mmc/mmc2)? Der mmc3 benötigt die gleiche Verdrahtung wie der mmc2, sonst geht es nicht.

Was für eine Karte hast Du den MMC oder SD. Mit meiner Toshiba SD Karte 128MB geht es problemlos. Bei einer 'alten' MMC Karte ist ev. das CLK-Timing zu aggressiv :gruebel:

Wenn dem so ist, müßte der Treiber wohl eher zu sd.o umgenant werden. SD Karten können mit höheren CLK-Raten arbeiten.

Günther
just_me
Einsteiger
Einsteiger
Beiträge: 123
Registriert: Montag 28. November 2005, 11:31

Beitrag von just_me »

Günther hat geschrieben:10 MB schreiben (12 Sekunden, 830 kb/s):
Verwunderlich hier ist, daß das Schreiben deutlich schneller ist als das Lesen. Da scheinen noch deutliche Optimierungsmoglichkeiten irgendwo anders zu liegen.
Hoppla, 830kb/s kann leider nicht sein wenn man sich den Assembler code ansieht.
Die Übertragungsrate wird nur dann möglich, wenn ein Puffer mit im Spiel ist. Die Zeit, die ein "sync" nach Deinem Write Test brauchen würde muss man noch dazu rechnen.
müßte der Treiber wohl eher zu sd.o umgenant werden.
Bitte nicht (und eigentlich auch kein mmc3.c) :) - Ein "early fork" kommt mit hohen Kosten, da die Nutzer- und Testergemeinde fragmentiert und auf Dauer ein (nahezu gleicher) Code in mehreren Dateien nicht gut wartbar ist.

Danke für Deine Tests: mit der 60% höheren Übertragungsrate (320kByte) mit "C-Code alleine" liegt man dann ja schon mehr als 10fach höher als die benötigte Datenrate für 256kBit MP3.

Wenn das Puffern reibungslos läuft MUSS demnach eine 256kBit MP3 Wiedergabe ohne Probleme klappen, wenn die 256kBit MP3-Wiedergabe über Netzwerk unter 90% CPU Last liegt.


Vielleicht lässt sich der Report von mb405 darauf zurückführen, dass die Zeit zwischen cp->cp_pbdat = clk_low; und cp->cp_pbdat = clk_high; zu kurz für die angeschlossene Karte sein könnte?
Günther
Developer
Beiträge: 587
Registriert: Freitag 9. September 2005, 21:48

Beitrag von Günther »

just_me hat geschrieben: Hoppla, 830kb/s kann leider nicht sein wenn man sich den Assembler code ansieht.
Die Übertragungsrate wird nur dann möglich, wenn ein Puffer mit im Spiel ist. Die Zeit, die ein "sync" nach Deinem Write Test brauchen würde muss man noch dazu rechnen.
Das denke ich auch, nur nachgeprüft hatte ich es noch nicht. Es wäre ja interessant was die wirkliche write-rate ist.
just_me hat geschrieben:Bitte nicht (und eigentlich auch kein mmc3.c) :) - Ein "early fork" kommt mit hohen Kosten, da die Nutzer- und Testergemeinde fragmentiert und auf Dauer ein (nahezu gleicher) Code in mehreren Dateien nicht gut wartbar ist..
Da gebe ich Dir grundsätzlich recht, aber da die SD Karte schneller arbeiten kann als eine MMC Karte, müßte bei Bedarf darüber nachgedacht werden (oder ein Autodetect mit entsprechenden nops für MMCs, oder die SD muß langsamer arbeiten als sie könnte). Desweiteren ist der mmc3 code auch nur zu Development-Zwecken da. Ich kenne zwar das Problem, daß selbst Test-Versionen, sobald sie einmal 'draußen' sind gleich als Release angesehen werden (oder zumindest verwirren). Andererseits finde ich es genauso verwirrend, wenn Ideen und Patches nicht im cvs sind sondern über irgendwelche 'yousendme'-Server ausgetaucht werden oder ein bestehender Treiber mit #ifdefs verunstaltet wird. Wenn die Sachen funktionieren sollten sie demnach auch in den mmc2 Treiber übernommen werden. Für die SMC Optimierung (falls sie den überhaupt funktioniert) müßte allerdings wahrscheinlich ein neuer Name gefunden werden, da die Ports sich unterscheiden (-> achja, das ist natürlich der größte Nachteil: SMC ist nur auf Nokias oder gelöteten Sagem möglich, Philips weiß ich net) .
just_me hat geschrieben: Danke für Deine Tests: mit der 60% höheren Übertragungsrate (320kByte) mit "C-Code alleine" liegt man dann ja schon mehr als 10fach höher als die benötigte Datenrate für 256kBit MP3.
Und mit der Assembler Optimierung schaffen wir vielleicht auch noch die 400kByte/s :D. Und mit SMC vielleicht 1000KByte/s :P :P (theoretisches Maximum müßte ja eigentlich: 25Mhz/8bit - overhead <= 3000KByte/s sein :gruebel: )
just_me hat geschrieben:Vielleicht lässt sich der Report von mb405 darauf zurückführen, dass die Zeit zwischen cp->cp_pbdat = clk_low; und cp->cp_pbdat = clk_high; zu kurz für die angeschlossene Karte sein könnte?
Sorry, das meinte ich mit'CLK-Timing ' (mal wieder zu unverständlich ausgedrückt) :gruebel: :D
mb405
Tuxboxer
Tuxboxer
Beiträge: 2331
Registriert: Donnerstag 24. März 2005, 21:52

Beitrag von mb405 »

also ich nehm den mmc.o, also alte verdrahtung.
ok danke, dann werd ich mal die mmc2 verdrahtung probieren, obs geht.
blonder_Andi
Neugieriger
Neugieriger
Beiträge: 13
Registriert: Donnerstag 14. Juli 2005, 09:35

Beitrag von blonder_Andi »

Wenn das Puffern reibungslos läuft MUSS demnach eine 256kBit MP3 Wiedergabe ohne Probleme klappen, wenn die 256kBit MP3-Wiedergabe über Netzwerk unter 90% CPU Last liegt.
Da dieses Thema schon in 3 Threads verteilt ist bin ich mir nicht ganz sicher obs hier dazupasst...

Reden wird bei MMC/SD Implementierung von Netzwerkkarten dieses Bautyps oder gehts um Speichermedien?
(Wiedergabe über Netzwerk hat mich etwas stutzig gemacht)

Falls diese Frage bereits in einem anderen Thread beantwortet wurde, tuts mir leid..

mfg

Andi
mb405
Tuxboxer
Tuxboxer
Beiträge: 2331
Registriert: Donnerstag 24. März 2005, 21:52

Beitrag von mb405 »

nein, hier gehts um sd-mmc karten, die am modemport der box angeschlossen sind.
das war nur ein vergleich der geschwindigkeiten.
Günther
Developer
Beiträge: 587
Registriert: Freitag 9. September 2005, 21:48

Beitrag von Günther »

So, ich habe die Assembler-Optimierung zum laufen bekommen (beim casten von 16bit auf 32bit wird aus 0x0400-> 0x04000000, das DO-Bit ist damit 22 von rechts/Intel bzw. 9 von links/Motorola ;)), aber leider hat das zu keiner Geschwindigkeitsverbesserung geführt (ob ev. die Proz-last kleiner war habe ich nicht gemessen).

Gemessen habe ich das, indem ich ein 10MB über FTP geladen habe. Vielleicht liegt darin ja auch ein Fehler.

Folgender Code war sogar langsamer (10MB->37Sek, ca.260KB/s):

Code: Alles auswählen

static __inline__ int set_bit_from_bit( const unsigned int dest_nr,
                                        unsigned int dest,
                                        const unsigned int src_nr,
                                        const unsigned int src)
{
        __asm__ __volatile__("\n\
        rlwinm %[src] ,%[src],32-%[src_nr],0,31 \n\
        rlwimi %[dest],%[src],%[dest_nr],31-%[dest_nr],31-%[dest_nr] \n"
        : [dest] "=&r" (dest)
        : [src] "r" (src), [dest_nr] "i" (dest_nr), [src_nr] "i" (src_nr)
        : "cc");
         return dest;
}

void mmc_read_block_low_level(unsigned int *dest) 
{ 
   volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm; 
   volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport; 

   volatile unsigned long * data =(volatile unsigned long *)&cpi->iop_padat; 

   unsigned long clk_high = cp->cp_pbdat | SD_CLK;
   unsigned long clk_low =  cp->cp_pbdat & ~SD_CLK;

   unsigned int i; 
   unsigned int in; 

   cpi->iop_padat |= SD_DI; 

   for(i = 0; i<128; i++) 
   { 
	in = 0x00;
        /* xx ns delay after clock before reading SD_DO is needed -
           insert NOPs or move the clocksetting within the macro */
        #define GET_THIS_BIT(n) cp->cp_pbdat = clk_high; \
                                in = set_bit_from_bit((n), in, SD_DO_NUM+16, *data); \
                                cp->cp_pbdat = clk_low;

        GET_THIS_BIT(31); 
 ...
        GET_THIS_BIT(0); 

        *dest++ = in; /* endianness ok? */ 
   } 
}

und ergibt diesen Assembler-Code

Code: Alles auswählen

        GET_THIS_BIT(31); 
     32c:	91 07 01 04 	stw     r8,260(r7)
     330:	80 0b 00 00 	lwz     r0,0(r11)
     334:	54 00 50 3e 	rotlwi  r0,r0,10
     338:	50 09 f8 00 	rlwimi  r9,r0,31,0,0
     33c:	90 c7 01 04 	stw     r6,260(r7)
        GET_THIS_BIT(30); 
     340:	91 07 01 04 	stw     r8,260(r7)
     344:	80 0b 00 00 	lwz     r0,0(r11)
     348:	54 00 50 3e 	rotlwi  r0,r0,10
     34c:	50 09 f0 42 	rlwimi  r9,r0,30,1,1
     350:	90 c7 01 04 	stw     r6,260(r7)
Dann habe ich das lwz in lhz umgewandelt, und siehe es ging schon schneller (10MB->32Sek, ca.310KB/s):

Code: Alles auswählen

static __inline__ int set_bit_from_bit( const unsigned int dest_nr,
                                        unsigned int dest,
                                        const unsigned int src_nr,
                                        const unsigned int src)
{
        __asm__ __volatile__("\n\
        rlwinm %[src] ,%[src],32-%[src_nr],0,31 \n\
        rlwimi %[dest],%[src],%[dest_nr],31-%[dest_nr],31-%[dest_nr] \n"
        : [dest] "=&r" (dest)
        : [src] "r" (src), [dest_nr] "i" (dest_nr), [src_nr] "i" (src_nr)
        : "cc");
         return dest;
}

void mmc_read_block_low_level(unsigned int *dest) 
{ 
   volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm; 
   volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport; 

   //volatile unsigned long * data =(volatile unsigned long *)&cpi->iop_padat; 

   unsigned long clk_high = cp->cp_pbdat | SD_CLK;
   unsigned long clk_low =  cp->cp_pbdat & ~SD_CLK;

   unsigned int i; 
   unsigned int in; 

   cpi->iop_padat |= SD_DI; 

   for(i = 0; i<128; i++) 
   { 
	in = 0x00;
        /* xx ns delay after clock before reading SD_DO is needed -
           insert NOPs or move the clocksetting within the macro */
        #define GET_THIS_BIT(n) cp->cp_pbdat = clk_high; \
                                in = set_bit_from_bit((n), in, SD_DO_NUM, cpi->iop_padat); \
                                cp->cp_pbdat = clk_low;

        GET_THIS_BIT(31); 
  ...
        GET_THIS_BIT(0); 

        *dest++ = in; /* endianness ok? */ 
   } 
}
ergibt folgenden Assembler-Code:

Code: Alles auswählen

     51c:	91 4b 01 04 	stw     r10,260(r11)
     520:	a0 07 00 06 	lhz     r0,6(r7)
     524:	54 00 d0 3e 	rotlwi  r0,r0,26
     528:	50 09 36 72 	rlwimi  r9,r0,6,25,25
     52c:	91 0b 01 04 	stw     r8,260(r11)
Die ganze Sache als byte geht genauso schnell (32Sec)
.

Code: Alles auswählen

static __inline__ int set_bit_from_bit( const unsigned int dest_nr,
                                        unsigned int dest,
                                        const unsigned int src_nr,
                                        const unsigned int src)
{
         __asm__ __volatile__("\n\
        rlwinm %[src] ,%[src],32-%[src_nr],0,31 \n\
        rlwimi %[dest],%[src],%[dest_nr],31-%[dest_nr],31-%[dest_nr] \n"
        : [dest] "=&r" (dest)
        : [src] "r" (src), [dest_nr] "i" (dest_nr), [src_nr] "i" (src_nr)
        : "cc");
         return dest;
}

void mmc_read_block_low_level(unsigned char *dest) 
{ 
   volatile cpm8xx_t *cp  = (cpm8xx_t *) &immap->im_cpm; 
   volatile iop8xx_t *cpi = (iop8xx_t *) &immap->im_ioport; 

   //volatile unsigned long * data =(volatile unsigned long *)&cpi->iop_padat; 

   unsigned long clk_high = cp->cp_pbdat | SD_CLK;
   unsigned long clk_low =  cp->cp_pbdat & ~SD_CLK;

   unsigned int i; 
   unsigned int in; 
 
   cpi->iop_padat |= SD_DI; 

   for(i = 0; i<512; i++) 
   { 
	in = 0x00;
        /* xx ns delay after clock before reading SD_DO is needed -
           insert NOPs or move the clocksetting within the macro */
        #define GET_THIS_BIT(n) cp->cp_pbdat = clk_high; \
                                in = set_bit_from_bit((n), in, SD_DO_NUM, cpi->iop_padat); \
                                cp->cp_pbdat = clk_low;

        GET_THIS_BIT(7); 
        GET_THIS_BIT(6); 
        GET_THIS_BIT(5); 
        GET_THIS_BIT(4); 
        GET_THIS_BIT(3); 
        GET_THIS_BIT(2); 
        GET_THIS_BIT(1); 
        GET_THIS_BIT(0); 

        *dest++ = in; /* endianness ok? */ 
   } 
}
Das ist aber nur so schnell wie die bisherige Optimierung 10 (braucht sogar nur 31 Sekunden), da sieht der Assembler-Code wie folgt aus:

Code: Alles auswählen

    cp->cp_pbdat = clk_high; //4
     26c:	91 4b 01 04 	stw     r10,260(r11)
         result <<= 1;result |= ((cpi->iop_padat & SD_DO)?0x01:0x00);
     270:	a0 08 00 06 	lhz     r0,6(r8)
     274:	55 29 d7 fe 	rlwinm  r9,r9,26,31,31
     278:	54 63 08 3c 	rlwinm  r3,r3,1,0,30
    cp->cp_pbdat = clk_low;
     27c:	90 eb 01 04 	stw     r7,260(r11)
     280:	7d 23 1b 78 	or      r3,r9,r3
Das sieht doch ziemlich identisch aus und hat sogar 1 Assembler-Befehl mehr. Das rlwinm muss demnach schneller sein als rotlwi/rlwimi, oder das Nadelöhr ist noch wo anders.

Wie es aussieht werden wir so wohl nicht schneller werden. Eventuell kann man im Assembler-Code noch ein Befehl einsparen (ungetestet),

Code: Alles auswählen

 stw     r10,260(r11)
lhz     r0,6(r7)
rlwimi  r9,r0,9,0,0
stw     r8,260(r11)

stw     r10,260(r11)
lhz     r0,6(r7)
rlwimi  r9,r0,8,1,1
stw     r8,260(r11)
..
stw     r10,260(r11)
lhz     r0,6(r7)
rlwimi  r9,r0,0,9,9
stw     r8,260(r11)

stw     r10,260(r11)
lhz     r0,6(r7)
rlwimi  r9,r0,31,10,10
stw     r8,260(r11)
...
aber obs was bringt?

Sollte irgendwo anders ein Nadelöhr sein, würde auch eine SMC-Optimierung nichts bringen. Hat jemand eine Ahnung???

Günther

Ich habe das file soeben als mmc2test.c (diesmal in Head ()) eingescheckt. Ev. Könnte jemand das file mmc3.c in newmake löschen, ich war zu doof dazu (ev. habe ich auch keine Rechte). Hoffe das ist jetzt besser