He heredado un dispositivo integrado personalizado que utiliza un MCU Renesas H8S para almacenar datos en una tarjeta SD. Hace esto directamente sin que el sistema de archivos implemente SPI a bajo nivel en el firmware escrito en ensamblador. La versión original (MMC, pre SPI) funcionó durante muchos años sin problemas en tarjetas SD de 2 GB.
Hace un tiempo, comenzamos a instalar tarjetas Kingston de 8 GB en los dispositivos, ya que nuestra opción tradicional de tarjeta de 2 GB se estaba volviendo escasa, por lo que tuvimos que realizar algunas modificaciones en las rutinas de lectura/escritura del firmware para admitir los "nuevos" modos y tiempos SPI. El desarrollador hizo que esto funcionara y pensó que todo estaba bien.
Ahora hemos descubierto que algunos de nuestros datos se sobrescriben de forma intermitente con bloques de 0x55, es decir. como 55555555555555555... (esto es 85d, o curiosamente 101010101 en binario)
Durante el funcionamiento normal, nuestro firmware almacena en búfer algunos datos entrantes en la RAM y la NVRAM hasta que llega a un bloque 512, luego escribe un solo bloque en el SDC, realizando un seguimiento de la siguiente dirección. Sigue escribiendo en los bloques 512 subsiguientes, sin retroceder nunca mientras se agrega a un registro.
Sin embargo, cuando volvemos a leer los datos, a veces los datos más antiguos se han sobrescrito con 0x55 en fragmentos de algún tamaño de página actualmente desconocido (al menos 128 bytes, por lo que probablemente 512). Parece que estos 55 se escriben 'debajo' de nuestros datos y, a veces, se superponen.
Podemos depurar el firmware (con HEW y el emulador de hardware USB) pero no podemos ver un problema en nuestro código. Estoy un poco ciego a lo que está sucediendo en el SDC, ya que la única forma que conozco de verlo es eliminarlo y crear una imagen (usándolo dd
en mi Mac), que es muy lento. (De hecho, he tenido problemas para ver algo sensato en las imágenes SD de 8 GB).
Estoy bastante seguro de que los datos se corrompieron en la tarjeta durante la escritura o poco después, y no se volvieron a leer incorrectamente. Tenemos dos rutinas de lectura bastante diferentes y ambas devuelven los mismos datos. La depuración muestra que los datos 55 salen de la tarjeta y no se rompen más adelante en el procesamiento.
Por lo tanto, solo puedo suponer que se trata de una condición de carrera, algún almacenamiento en caché interno o almacenamiento en búfer en el SDC, o posiblemente el tamaño del bloque de borrado que podría estar sobrescribiendo áreas más grandes que los 512 bloques que estamos escribiendo.
La tarjeta se usa para otros fines en otras áreas reservadas más bajas, con varios ciclos de trabajo en nuestro firmware y también se interrumpe, por lo que es posible que haya un conflicto allí, pero pensé que esto habría sucedido hace años.
Solo pasa con estas tarjetas 8BG. Todavía no hemos podido probar ninguna otra variante/marca de tarjeta, pero planeamos hacerlo.
Entonces mis preguntas son:
ACTUALIZAR
Hoy probé tantas tarjetas como pude encontrar, y encontré que todas menos una fallaban de manera similar, pero a veces diferente.
Result Card
FAIL 8GB Kingston Ultra microSDHC Class 4 (CO8G Taiwan), production.
PASS 4GB Verbatim microSD HC Class 4 - brand new
FAIL 16GB SanDisk Ultra microSDHC 80mb/s 533x - new
FAIL 8GB SanDisk microSDHC I Class 4 "BI Made in China", reused.
FAIL 8GB SanDisk ULTRA microSD HC I - UHS Class 1, production.
FAIL 4GB SanDisk microSDHC class4 - new.
FAIL* ADATA 4GB microSDHC class4 - new.
PASS Transcend 2GB microSD (SC?)
ON ORDER Transcend 8GB microSD
PASS Verbatim 8GB microSDHC Class 10
PASS Kingston 8GB microSDHC Class 10 / UHC 1
FAIL Un-branded Taiwan 8GB SDHC Class 10
De estas tarjetas, la "peor" es la Sandisk de 16 Gb que parecía impredecible e incluso movió los datos después de escribirlos, ya que las vistas posteriores de los datos son diferentes. La tarjeta ADATA también era diferente, ya que necesitaba dos lecturas para leer los datos reales. En la primera lectura, los datos solo eran parcialmente visibles, pero en la segunda lectura parecían correctos.
La mayoría de ellos se comportaron de la misma manera, como se describió anteriormente, con bloques antiguos que parecían haber sido sobrescritos cuando las escrituras posteriores se mueven a bloques nuevos (probablemente). Algunas de ellas fallaron casi inmediatamente después de una o dos escrituras de bloque 512, mucho peor que nuestras tarjetas Kingston primarias de 8 GB.
Después de ver cómo los datos se mueven de manera extraña en estos discos, actualmente siento que los discos más grandes están "haciendo cosas inteligentes" con los datos (como almacenamiento en búfer o almacenamiento en caché) que solo estamos experimentando porque estamos escribiendo/leyendo directamente en direcciones de memoria en lugar de respetando la capa del sistema de archivos (incluso si no hay un error específico en nuestro código).
ACTUALIZAR2
He aclarado y simplificado el escenario del problema, mostrando que es más simple:
A partir de esta lectura, ahora estoy pensando que la corrupción es falsa, los datos en la tarjeta están bien y los datos devueltos por nuestro lector de registro son quizás el "flujo de tokens ocupados" o DO todavía se mantiene bajo desde la escritura. proceso. Parece que el proceso de lectura de los registros A está terminando con la escritura anterior de los registros B.
Sin embargo, mirando el código, parece que se está respetando el protocolo, y la macro de escritura no sale hasta que DO sube, después de enviar los relojes en un bucle (es decir, no 8 o 32, ¡solo los que necesite!)
Esta es la asamblea.
La parte con la que he estado experimentando es el _MMCSPIWrite512ByteBlockFromer2Accept
bucle final, al que intenté agregar varias cosas:
La _MMCSPISendByter0l
macro envía 8 relojes con los bits.
Al atravesarlo, parece estar haciendo aproximadamente el proceso correcto. Sin embargo, es difícil contar las iteraciones exactas en los diversos bucles (en el emulador HEW, un poco lento), ¡y cualquier registro de depuración es demasiado difícil en asm! Y también el paso parece afectar el tiempo y, a veces, se bloquea.
Esta macro _MMCSPIWrite512ByteBlockFromer2
es la función principal para escribir un bloque 512 en la tarjeta SD, desde un búfer ram apuntado desde er2.
Revisé la especificación SPI simplificada y la parte del token de error/aceptación parece ser correcta.
Como dije antes, este algoritmo de escritura tiene muchos años de práctica exitosa en miles de dispositivos, en algunas tarjetas. Así que no espero que sea un error tan básico en el protocolo. Simplemente no puedo ver el problema que parece dejar el estado de lectura leyendo 000000, posiblemente alrededor de los primeros 512 bytes leídos (u ocasionalmente h'55s, que en realidad podrían provenir de otra parte de nuestra aplicación).
_MMCSPIWrite512ByteBlockFromer2
bset H_MMCSPIDI ; raise DI
bclr H_MMCSPInCS ; lower CS
mov.b #h'ff,r0l ;8 clock cycles Ncs
bsr _MMCSPISendByter0l
mov.b #C_MMCSPICMD24,r0l ;cmd24 is the write block command
bclr #7,r0l ;b'01 mask
bset #6,r0l
bsr _MMCSPISendByter0l
bsr _MMCSPISendArguments ;send four arguments/address
mov.b #h'ff,r0l ;h'ff is the unused dummy crc
bsr _MMCSPISendByter0l
bset H_MMCSPIDI ;send hi data with clocks till response obtained
_MMCSPIWrite512ByteBlockFromer2WaitForResponse
btst H_MMCSPIDO ;check for response, send clocks till response zero bit arrives
beq _MMCSPIWrite512ByteBlockFromer2Responded
bsr _MMCSPIClock
bra _MMCSPIWrite512ByteBlockFromer2WaitForResponse
_MMCSPIWrite512ByteBlockFromer2Responded
bsr _MMCSPIReceive7Bitsr0l ;response should be h'00 else an error
bne _MMCSPIWrite512ByteBlockFromer2Error
mov.b #h'ff,r0l ;send Nwr
bsr _MMCSPISendByter0l
mov.b #h'fe,r0l ;send start of data header
bsr _MMCSPISendByter0l
_MMCSPIWrite512ByteBlockFromer2DataStart
mov.w #d'512,e0 ;send 512 byte block of data
_MMCSPIWrite512ByteBlockFromer2DataLoop
mov.b @er2,r0l ;write data from er2 pointer, byte at a time, 512 times
bsr _MMCSPISendByter0l
inc.l #1,er2
dec.w #1,e0
bne _MMCSPIWrite512ByteBlockFromer2DataLoop
_MMCSPIWrite512ByteBlockFromer2CRC
mov.b #h'ff,r0l ;2 dummy crc bytes
bsr _MMCSPISendByter0l
mov.b #h'ff,r0l
bsr _MMCSPISendByter0l
_MMCSPIWrite512ByteBlockFromer2CheckResponse
rotl.b r0l ;get next lsb of response byte ready
bsr _MMCSPIClock
bld H_MMCSPIDO ;place mmc data output into response byte lsb
bst #0,r0l
mov.b r0l,r0h
and.b #b'00011111,r0h ;mask inappropriate bits of response and check
cmp #b'00001011,r0h ;check if a reject token?
beq _MMCSPIWrite512ByteBlockFromer2Reject
cmp #b'00000101,r0h ;check if an accept token?
beq _MMCSPIWrite512ByteBlockFromer2Accept
bra _MMCSPIWrite512ByteBlockFromer2CheckResponse ;loop till reject or accept received
_MMCSPIWrite512ByteBlockFromer2Accept
bsr _MMCSPIClock ;provide clocks for mmc writing clock requirements
btst H_MMCSPIDO
beq _MMCSPIWrite512ByteBlockFromer2Accept ;keep clocking till write complete ack'd with a high DO
bset H_MMCSPInCS
mov.b #h'00,r0l
rts
_MMCSPIWrite512ByteBlockFromer2Error
_MMCSPIWrite512ByteBlockFromer2Reject
mov.b #h'ff,r0l ;8 clock Nec
bsr _MMCSPISendByter0l
bset H_MMCSPInCS
mov.b #h'ff,r0l
rts
Actualización 3
Hoy me concentré en dos cosas: la sugerencia del tamaño del bloque y el examen de la rutina READ, pensando más en el hecho de que los datos parecen estar escritos correctamente, pero se leen de forma intermitente.
En primer lugar, he intentado enviar CMD16 para establecer el tamaño del bloque en 512, pero
a) no tuvo ningún efecto en las fallas (si funcionó) b) No estoy 100% seguro de haberlo hecho bien en ensamblador, aunque durante la depuración obtuve un bit de "error de parámetro" establecido en la respuesta, que resolví y luego obtuve una respuesta 0x00, así que creo que estoy haciendo bien la llamada de comando... c) Al leer la especificación SPI simplificada, dice claramente que el tamaño del bloque es 512 para SDHC y este comando no se usa. Muchas personas dicen que debe establecer el tamaño del bloque: ¿esto es para tarjetas MMC? o la especificación es incorrecta?
En el caso de una tarjeta de memoria SD de capacidad estándar, este comando establece la longitud del bloque (en bytes) para todos los siguientes comandos de bloque (lectura, escritura, bloqueo). La longitud de bloque predeterminada se fija en 512 bytes. La longitud establecida es válida para los comandos de acceso a la memoria solo si se permite la operación de lectura de bloque parcial en CSD. En el caso de las tarjetas SDHC y SDXC, la longitud del bloque establecida por el comando CMD16 no afecta los comandos de lectura y escritura de la memoria. Siempre se utiliza una longitud de bloque fija de 512 bytes. Este comando es efectivo para el comando LOCK_UNLOCK. En ambos casos, si la longitud del bloque se establece en más de 512 bytes, la tarjeta establece el bit BLOCK_LEN_ERROR. En el modo DDR50, los datos se muestrean en ambos bordes del reloj.
En segundo lugar, examiné la rutina de lectura y, como antes, parece generalmente correcta. Anteriormente intenté agregar más "relojes ficticios" sin ningún efecto.
La rutina que lee desde SPI es la siguiente.
_MMCSPIRead512ByteBlockToer2
bset H_MMCSPIDI
bclr H_MMCSPInCS
mov.b #h'ff,r0l ;8 clock cycles Ncs
bsr _MMCSPISendByter0l
mov.b #C_MMCSPICMD17,r0l ;cmd17 is the read block command
bclr #7,r0l ;b'01 mask
bset #6,r0l
bsr _MMCSPISendByter0l
bsr _MMCSPISendArguments ;send four arguments/address
mov.b #h'ff,r0l ;h'ff is the unused dummy crc
bsr _MMCSPISendByter0l
bset H_MMCSPIDI ;send hi data with clocks till response obtained
_MMCSPIRead512ByteBlockToer2WaitForResponse
btst H_MMCSPIDO ;check for response, send clocks till response zero bit arrives
beq _MMCSPIRead512ByteBlockToer2Responded
bsr _MMCSPIClock
bra _MMCSPIRead512ByteBlockToer2WaitForResponse
_MMCSPIRead512ByteBlockToer2Responded
bsr _MMCSPIReceive7Bitsr0l ;response should be h'00 else an error
bne _MMCSPIRead512ByteBlockToer2Error
bset H_MMCSPIDI ;send hi data with clocks till response obtained
bsr _MMCSPIClock ;clock ensures previous zero on DO is removed
_MMCSPIRead512ByteBlockToer2WaitForData
btst H_MMCSPIDO ;check for response, send clocks till response zero bit arrives
beq _MMCSPIRead512ByteBlockToer2DataStart ; because the response desired is h'fe , so last bit of this
bsr _MMCSPIClock ; response signals the start of data
bra _MMCSPIRead512ByteBlockToer2WaitForData
_MMCSPIRead512ByteBlockToer2DataStart
mov.w #d'512,e0 ;get 512 byte block of data
_MMCSPIRead512ByteBlockToer2DataLoop
bsr _MMCSPIReceiver0l ;get data and store into er2 pointer, 512 times
mov.b r0l,@er2
inc.l #1,er2
dec.w #1,e0
bne _MMCSPIRead512ByteBlockToer2DataLoop
_MMCSPIRead512ByteBlockToer2End
bsr _MMCSPIReceiver0l ;get 2 crc bytes
bsr _MMCSPIReceiver0l
bsr _MMCSPIReceiver0l ;a few more reads to endsure data is completely flushed
bsr _MMCSPIReceiver0l
bsr _MMCSPIReceiver0l
bset H_MMCSPInCS
mov.b #h'00,r0l
rts
_MMCSPIRead512ByteBlockToer2Error
mov.b #h'ff,r0l ;8 clock Nec
bsr _MMCSPISendByter0l
bset H_MMCSPInCS
mov.b #h'ff,r0l
rts
Actualización 4
Después de escribir algunas rutinas para escribir manualmente en los distintos registros, ahora puedo reproducir el problema de manera confiable con el conjunto de pasos más simple posible. También me familiaricé más con la depuración en HEW y la inspección de varias ubicaciones de memoria.
Resulta que la mayoría de los efectos que estaba viendo se debían a datos antiguos.
Después de escribir en los B-Logs, el comando SPI para leer de los A-Logs devuelve errores de la tarjeta. Sin embargo nuestro firmware no informa de este hecho, ni devuelve un resultado de error a las funciones superiores. Entonces, las funciones superiores de procesamiento de registros continúan leyendo los datos de un búfer de memoria que no ha sido modificado. El búfer tiene varios datos de registros anteriores de lectura/escritura y cuando se decodifica más (desde hexadecimal en binario hasta decimal en ascii) aparece todo tipo de datos falsos. Los 5555 en realidad provienen principalmente de 0xFF en RAM.
Entonces, aunque todavía no sé por qué el comando de lectura SPI está fallando, puedo cerrar esta pregunta, ya que se respondió en su mayoría y probablemente fue demasiado amplia.
¿Existen riesgos conocidos y problemas comunes como este escribir directamente en la tarjeta y evitar un FS? (como la alineación de bloques)
Generalmente no, usar la tarjeta como un simple dispositivo de bloqueo es perfectamente seguro. Sin embargo , sin una capa FS, usted es responsable de lo que hace. Entonces, si tiene una falla en su código, puede afectar más a la tarjeta SD.
¿Las tarjetas más grandes almacenan datos en memoria intermedia o caché internamente?
No. Escriben el bloque y una vez que ya no indican "ocupado" (R1b) los datos son persistentes. - Usted consulta el estado ocupado antes de operar más en la tarjeta, ¿no es así?
es 0x55 un valor de "relleno" conocido (a diferencia del borrado normal de 0xFF)
No. Los bloques borrados se leen como 0xFF o 0x00 (según la tarjeta).
¿Alguien puede pensar en lo que está causando las sobrescrituras intermitentes?
Me parece interesante que los datos corruptos sean consistentemente 0x55.
Pensando en ello, ¿sabe que las tarjetas SDSC (<= 2 GB) son direccionables por bytes , mientras que las tarjetas SDHC y SDXC son direccionables por bloques solamente? Por lo tanto, enviar la dirección 0x0200 a una tarjeta SDSC direccionará el comienzo del segundo bloque de 512 bytes, mientras que la misma dirección en una tarjeta SDHC/XC se referirá al comienzo del bloque 512 de la tarjeta, es decir, la dirección de byte 262144. Si si no verifica y respeta el tipo de tarjeta, es posible que se esté metiendo en problemas, o al menos no esté escribiendo datos donde cree que debería estar.
Además, asegúrese de esperar el final de cada operación realizada por la tarjeta.
Una tarjeta de 8 GB que probé no funcionó solo con los 8 relojes SPI "ficticios" estándar después de cada comando. Ahora, para estar seguro, siempre envío 32 relojes (4 bytes) y todo funciona a la perfección.
Es posible que desee verificar el tamaño de bloque de sus tarjetas o, para estar seguro, establecerlo en 512 bytes (CMD16) para todas las tarjetas durante la inicialización. De lo contrario, la tarjeta puede estar esperando más datos mientras cree que ya envió suficientes y le sucederán cosas malas a sus datos.
turbo j
scipilot
jimmyb
scipilot
tubo
JHBonarius
scipilot
scipilot
scipilot
jimmyb
jimmyb
jimmyb
jimmyb
jimmyb
scipilot
scipilot
jimmyb
jimmyb
jimmyb
scipilot
scipilot
scipilot