Вы находитесь на странице: 1из 1

КАК СТАТЬ АВТОРОМ Зарплаты айтишников Хабровчане vs.

цифровые сервисы банков: итоги

Все потоки Разработка Администрирование Дизайн Менеджмент Маркетинг Научпоп Войти Регистрация

Fenja 17 апреля 2020 в 12:34


ЧИТАЮТ СЕЙЧАС
Работа с SD-картой по интерфейсу SPI. Реализация
на VHDL Поддельные батарейки в магазинах
Светофор
FPGA, Программирование микроконтроллеров 26,5k 118

Привет, Habr! Однажды на работе мне досталась задача оценить возможность реализации хранения Как выучить иностранный язык:
данных на SD-карте при подключении ее к FPGA. В качестве интерфейса взаимодействия
алгоритм
предполагалось использование SPI, так как он проще в реализации. Полученным опытом хотелось бы 4,7k 7

поделиться.
О спасении от удушья в своей
квартире или лонгрид о современной
вентиляции
1,4k 5

Как тебе такое, Джефф Безос?


Так как место на печатной плате всегда ограничено, рассмотрение SD-карт будет выполняться на 31,4k 290 +290
примере карт в форм-факторе MicroSD.
Фермеры в США вынуждены
Содержание взламывать тракторы, чтобы просто
починить их
96,7k 430
1. Чтение спецификации
1.1 Общие сведения
Уверенный пользователь ПК?
1.2 Инициализация
Серьёзно?
1.3 Стирание информации
49k 461
1.4 Чтение информации
1.4.1 Чтение одного блока данных
runBlocking против приложения: как
1.4.2 Чтение множества блоков данных оправить приложение в нокаут из-за
1.5 Запись информации невнимательности
1.5.1 Запись одного блока данных
Интересно
1.5.2 Запись множества блоков данных
2. Реализация алгоритма в аппаратуре
2.1 Компонент физического уровня
2.2 Компонент командного уровня Редакторский дайджест
2.3 Компонент общения с внешним миром Присылаем лучшие статьи раз в месяц

3. Проверка в железе
Электропочта
4. Результаты

1. Чтение спецификации
1.1 Общие сведения
Быстрое чтение спецификации сообщает нам следующие параметры SD-карт:

передача данных в SD-карту осуществляется по одной линии;

чтение данных с SD-карты осуществляется по одной линии;

в режиме SPI питание может быть только +3,3В;

тактовая частота в режиме инициализации в диапазоне 100-400кГц;

тактовая частота после инициализации — 25МГц.

Отсюда сразу вытекает пункт о теоретической пиковой пропускной способности: 25МГц * 1 бит = 25
Мбит/с, что несколько маловато. Второй минус использования SD-карт, питание +3,3В. На печатной
плате, на которую планировалось устанавливать SD-карту, такого напряжения нет.

Карты форм-фактора MicroSD по емкости можно разделить на 3 категории:

SDSC. Емкость карт до 2 Гбайт включительно. Адрес внутри карты байтовый.

SDHC. Емкость карт больше 2 Гбайт и до 32 Гбайт включительно. Адрес внутри карты указывает на
блок размером 512 байт.

SDXC. Емкость карт больше 32 ГБайт и до 128 Тбайт включительно. Адрес внутри карты указывает
на блок размером 512 байт.

Общий вид карты приведен на рисунке ниже.

Номер контакта Имя Тип Описание

1 RSV - Не используется

2 CS Вход Выбор микросхемы

3 DI Вход Линия данных от Master устройства (MOSI)

4 Vdd Питание Напряжение питания

5 SCLK Вход Тактовый сигнал

6 Vss Питание Земля

7 DO Выход Линия данных в Master устройство (MISO)

8 RSV - Не используется

Подключение осуществляется по схеме ниже:

Номинал резисторов должен быть 50 кОм

После включения питания, SD-карта находится в режиме работы SDIO. Для переключения в режим SPI
необходимо выполнить инициализацию. Протокол работы с картой предполагает использование схемы
контроля корректной передачи данных и команд в виде алгоритма CRC. При работе в режиме SPI
проверка CRC выключена по умолчанию. Таким образом, первая команда, отправляемая в карту для
переключения карты в режим SPI, должна содержать корректное значение CRC.

Команды, передаваемые по интерфейсу SPI, имеют размер 48 бит. Формат команд:

Бит 47 (крайний слева) всегда содержит значение 0.

Бит 46 всегда содержит значение 1.

Биты 45..40 содержат индекс команды.

Биты 39..8 содержат аргументы команды.

Биты 7..1 содержат CRC от предыдущих бит.

Бит 0 всегда содержит значение 1.

Биты 47 и 46 дают карте возможность однозначно отследить начало транзакции, так как шина MOSI в
состоянии покоя равна единице.

На команды, используемые при работе с SD-картой, будут поступать ответы типа R1, R3, R7.

Ответ типа R1, размер 8 бит.

Ответ типа R3, размер 40 бит.

Ответ типа R7, размер 40 бит.

Все команды и ответы на них подробно расписаны в спецификации Physical Layer Simplified Specification

1.2 Инициализация
Алгоритм инициализации:

1. После включения питания необходимо подождать минимум 1 мс.

2. Сформировать минимум 74 переключения тактового сигнала для карты. Линии CS и MOSI должны
быть в состоянии логической единицы.

3. Сформировать команду CMD0. Команда CMD0 выполняет сброс карты SD.

4. Ожидать ответа на команду CMD0. Ответом является ответ типа R1. В случае, если ответ R1 не
пришел за 16 тактов частоты инициализации, переход к шагу 3. Если ответ R1 от карты поступил, но
отличается от значения 0x01 (карта не перешла в состояние инициализации), переход к шагу 3,
иначе переход к шагу 5.

5. Сформировать команду CMD8. Команда отправляет карте напряжения питания контроллера и


тестовую последовательность для проверки линии.

6. Ожидать ответа на команду CMD8. Ответом являет ответ типа R7. В случае, если в ответе
содержится бит, что команда не поддерживается, переход к шагу 7, иначе, если тестовая
последовательность и напряжения питания карты совпадают с отправленными контроллером,
переход к шагу 17, иначе к шагу 13.

7. Отправка команды CMD55. Команда CMD55 сообщает карте, что следующая команда будет
нестандартная.

8. Ожидание ответа на команду CMD55. Ответом является ответ типа R1. Если ответ не равен 0x01
(произошла ошибка при выполнении команды), переход к шагу 7, иначе к шагу 9.

9. Отправка команды ACMD41. Команда ACMD41 сообщает карте, что поддерживает карты SDSC
(параметр 0x00000000) и запускает процесс инициализации.

10. Ожидание ответа на команду ACMD41. Ответом является ответ типа R1. В случае если ответе
содержится бит, что команда не поддерживается, переход к шагу 11. Иначе, если ответ равен 0x00
(инициализация завершена) переход к шагу 14, в противном случае переход к шагу 7.

11. Отправка команды CMD1. Команда CMD1 аналогична команде ACMD41.

12. Ожидание ответа на команду CMD1. Ответом являет ответ типа R1. В случае если в ответе
содержится бит, что команда не поддерживается, переход к шагу 13, иначе, если ответ равен 0x00
(инициализация завершена), переход к шагу 14, в противном случае переход к шагу 11.

13. Состояние ошибки. Инициализация завершилась неудачно, дальнейшая работа невозможна. Выход
из этого состояния не предусмотрен.

14. Отправка команды CMD16. Команда используется для настройки в карте режима работы с блоками,
размерами 512 байт.

15. Ожидание ответа на команду CMD16. Ответом является ответ типа R1. В случае, если ответ равен
0x00 (команда завершена удачно) переход к шагу 16, иначе к шагу 13.

16. Состояние удачного завершения инициализации. Можно начинать работать с данными на карте.
Выход из алгоритма.

17. Отправка команды CMD55. Команда CMD55 сообщает карте, что следующая команда будет
нестандартная.

18. Ожидание ответа на команду CMD55. Ответом является ответ типа R1. Если ответ не равен 0x01
(произошла ошибка при выполнении команды), переход к шагу 17, иначе к шагу 19.

19. Отправка команды ACMD41. Отправка команды ACMD41. Команда ACMD41 сообщает карте, что
поддерживает карты SDHC (параметр 0x40000000) и запускает процесс инициализации.

20. Ожидание ответа на команду ACMD41. Ответом является ответ типа R1. В случае если ответе
содержится бит, что команда не поддерживается, переход к шагу 13. Иначе, если ответ равен 0x00
(инициализация завершена) переход к шагу 21, в противном случае переход к шагу 17.

21. Отправка команды CMD58. Чтение регистра конфигурации карты.

22. Ожидание ответа на команду CMD58. Ответом является ответ типа R3. В случае, если в ответе
установлен бит, что карта работает с адресами блоков по 512 байт, переход к шагу 16, иначе к шагу
14.

После завершения инициализации с картой можно работать блоками по 512 байт с тактовой частотой в
25 МГц. Это полная версия алгоритма инициализации, которая охватывает все типы карт. В моем случае,
при использовании карты на 16 Гбайт алгоритм инициализации состоял из шагов 1-6, 17-22, 16.

Алгоритм в графическом виде

1.3 Стирание информации


Карты форм-фактора Micro SD поддерживают команды стирания информации. После команды стирания
информации, значение указанных адресов для стирания будет заполнено значением 0xFF или 0x00, в
зависимости от карты.

Алгоритм стирания информации

1. Отправка команды CMD32. Команда устанавливает адрес первого блока для стирания информации.

2. Ожидание ответа на команду CMD32. Ответом является ответ типа R1. В случае если ответ не равен
0x00 (произошла какая-либо ошибка при выполнении команды), переход к шагу 3, иначе к шагу 4.

3. Состояние ошибки. Стирание завершилось неудачно, выход из алгоритма стирания информации.

4. Отправка команды CMD33. Команда устанавливает адрес последнего блока для стирания
информации. В случае если стереть нужно только один блок, адрес должен быть равен адресу из
команды CMD32.

5. Ожидание ответа на команду CMD33. Ответом является ответ типа R1. В случае если ответ не равен
0x00 (произошла какая-либо ошибка при выполнении команды), переход к шагу 3, иначе к шагу 6

6. Отправка команды CMD38. Команда стереть информацию с выделенных блоков. В качестве


аргумента должны быть отправлены любые 4 байта, кроме значений 0x00000001, 0x00000002.

7. Ожидание ответа на команду CMD38. Ответом является ответ типа R1b. Это расширенная версия
ответа, когда карта формирует ответ R1, а потом притягивает линию MISO к нулю, индицируя
занятость микросхемы. Необходимо дождаться появления на линии MISO значения единицы. В
случае если ответ не равен 0x00 (произошла какая-либо ошибка при выполнении команды),
переход к шагу 3, иначе к шагу 8

8. Завершение алгоритма стирания.

Алгоритм в графическом виде

1.4 Чтение информации


Чтение информации с SD-карты по интерфейсу SPI возможно в двух вариантах.

1.4.1 Чтение одного блока данных


При чтении одного блока данных, Master устройство формирует для SD-карты команду на чтение
одного блока данных, ожидает ответа, что команда обработана и ожидает пакета с данными от карты.
После получения пакета с данными от карты, транзакция чтения заканчивается.

Общий вид транзакции чтения одного блока данных.

Первоначально был реализован именно такой вариант, но полученная скорость очень расстроила
(сравнения скоростей будут ниже).

1.4.2 Чтение множества блоков данных


При чтении множества блоков данных, Master устройство формирует для SD-карты команду на чтение
множества блоков данных, ожидает ответа, что команда обработана и ожидает пакета данными от
карты. После отправления пакета с данными, карта отправляет следующий пакет с данными. Так будет
продолжаться до тех пор, пока от Master устройства не поступит команда о завершении чтения.

Общий вид транзакции чтения множества блоков данных.

Структура Data Packet

Где:

Data Token. Для команды чтения используется значение 0xFE.

Data Block. Содержит данные, читаемые с карты.

CRC. Содержат контрольную сумму от предыдущих полей.

В случае, если при чтении произошла ошибка, карта вместо Data Token возвращает Error Token. Размер
Error Token – 1 байт. Биты 7..5 содержат значение нуля, биты 4..0 кодируют тип ошибки.

Алгоритм чтения множества блоков данных:

1. Отправка команды CMD18. Команда сообщает карте, что будет выполняться чтение множества
блоков.

2. Ожидание ответа на команду CMD18. Ответом является ответ типа R1. В случае если ответ не равен
0x00 (произошла какая-либо ошибка при выполнении команды), переход к шагу 3, иначе к шагу 4.

3. Состояние ошибки. Чтение завершилось неудачно, выход из алгоритма чтения.

4. Ожидание токена от карты. В случае если от карты получен Error Token, переход к шагу 3, иначе к
шагу 5.

5. Прием от карты блока данных размером 512 байт.

6. Прием от карты поля CRC размером 2 байта.

7. Проверка количества принятых блоков данных. Если принято необходимое количество блоков,
переход к шагу 8, иначе к шагу 4.

8. Отправка команды CMD12. Команда сообщает карте о необходимости остановки чтения. В случае,
если в этот момент выполняется передача Data Packet, она будет прервана.

9. Ожидание ответа на команду CMD12. Ответом является ответ типа R1b. Это расширенная версия
ответа R1, в котором карта после формирования ответа притягивает линию MISO к нулю,
индицируя занятость карты. Необходимо дождаться появления на линии MISO значения единицы,
что будет свидетельствовать об окончании команды. В случае если ответ не равен 0x00 (произошла
какая-либо ошибка при выполнении команды), переход к шагу 3, иначе к шагу 10.

10. Завершение алгоритма чтения.

В алгоритме чтения есть небольшой нюанс. Сигнал выбора микросхемы (CS) должен быть установлен в
состояние логического нуля перед формированием команды CMD18 и установлен в состояние
логической единицы после получения ответа на команду CMD12.

Алгоритм в графическом виде

1.5 Запись информации


Запись информации на SD-карту по интерфейсу SPI возможна в двух вариантах.

1.5.1 Запись одного блока данных


При записи одного блока данных, Master устройство формирует для SD-карты команду на запись одного
блока данных, ожидает ответ от карты, что команда обработана, передает в карту пакет с данными для
записи, ожидает от карты ответа, что данные записаны, завершает транзакцию.

Общий вид транзакции записи одного блока данных.

Как и с чтением, изначально была реализована запись одного блока данных. Результаты скорости
получились неудовлетворительные.

1.5.2 Запись множества блоков данных


При записи множества блоков данных, Master устройство формирует команду на запись множества
блоков данных, ожидает ответ от карты, что команда обработана, передает в карту пакет с данными для
записи, ожидает от карты ответа, что данные записаны. После получения ответа Master устройство
передает в карту пакет со следующими данными для записи. Так будет продолжаться до тех пор, пока
Master устройство не отправит Stop Data Token.

Общий вид транзакции записи множества блоков данных.

Структура Data Packet аналогична структуре Data Packet при чтении данных.

Формат:

Data Token. Для команды записи используется значение 0xFC.

Data Block. Содержит данные, записываемые на карту.

CRC. Содержат контрольную сумму от предыдущих полей.

Stop Tran Token, используемый для завершения команды записи, имеет размер 1 байт и равен значению
0xFD.

После того, как последний бит Data Packet был задвинут в карту, на следующий такт карта отвечает
статусом записи данных – Data Response. Data Response имеет размер 1 байт, биты 7..5 могут быть
любыми, бит 4 всегда равен нулю, бит 0 всегда равен единице, биты 3..1 кодируют статус записи данных.
После того, как карта отдала Data Packet, карта притягивает линию MISO к нулю, индицируя занятость
карты. После того, как на линии MISO будет уровень логической единицы, можно передавать в карту
следующий пакет с данными.

Алгоритм записи множества блоков данных:

Отправка команды CMD25. Команда сообщает карте, что будет выполняться запись множества
блоков.

Ожидание ответа на команду CMD25. Ответом является ответ типа R1. В случае если ответ не равен
0x00 (произошла какая-либо ошибка при выполнении команды), переход к шагу 3, иначе к шагу 4.

Состояние ошибки. Запись завершилась неудачно, выход из алгоритма.

Запись токена для данных.

Запись данных размером 512 байт в карту.

Запись в карту поля CRC размером 2 байта.

Ожидание Data Response от карты. В случае если получен ответ с ошибкой, переход к шагу 3, иначе
к шагу 8.

Проверка количества записанных блоков. Если записано необходимое количество блоков, переход
к шагу 9, иначе к шагу 4.

Отправка Stop Tran Token. Это сообщает карте, что необходимо завершить транзакцию записи.

Ожидание ответа от карты. Карта на Stop Tran Token притягивает линию MISO к нулю, индицируя
занятость карты. Необходимо дождаться на линии MISO значения единицы, что будет
свидетельствовать об окончании команды

Завершения алгоритма записи.

В алгоритме записи есть так же небольшой нюанс. Сигнал выбора микросхемы (CS) должен быть
установлен в состояние логического нуля перед формированием команды CMD25 и установлен в
состояние логической единицы после получения ответа на Stop Tran Token

Алгоритм в графическом виде

2. Реализация алгоритма в аппаратуре


В результате прочтения спецификации, получаем некоторые характеристики алгоритма, которые
необходимо реализовать в аппаратуре.

Возможные режимы работы:

Начальное формирование частоты. Линии CS и MOSI должны находиться в состоянии логической


единицы.

Передача данных в SD-карту.

Прием данных от SD-карты.

Ожидание завершения команды. Используется, когда после формирования ответа SD-карта


притягивает линию MISO к нулю, что бы дождаться появления единицы.

Чтение ответа при записи данных.

Ожидание токена при чтении данных.

Возможные режимы тактового сигнала:

Тактовый сигнал для инициализации.

Тактовый сигнал для работы.

С моей точки зрения, алгоритм оптимально реализовать с помощью трех компонентов:

Компонент физического уровня, который подключен непосредственно к SD-карте, формирует


сигналы SCLK, CS, DI, читает с DO.

Компонент командного уровня, который осуществляет подготовку всех данных для компонента
физического уровня.

Компонент общения с внешним миром, который скрывает всё внутренне устройство и


предоставляет интерфейс для команд (чтение, запись, стирание) и данных.

2.1 Компонент физического уровня

entity SDPhy is
generic ( gTCQ : time := 2 ns );
port ( -- Control bus
iPhyTxData : in std_logic_vector( 9 downto 0);
iPhyMode : in std_logic_vector( 4 downto 0);
iPhyTxWrite : in std_logic;
oPhyTxReady : out std_logic;
-- Out Data
oPhyRxData : out std_logic_vector( 7 downto 0);
oPhyRxWrite : out std_logic;
oPhyCmdEnd : out std_logic;
-- Spi
oSdCS : out std_logic;
oSdClk : out std_logic;
oSdMosi : out std_logic;
oSdMosiT : out std_logic;
iSdMiso : in std_logic;
-- system
sclk : in std_logic;
pclk : in std_logic;
rst : in std_logic );
end SDPhy;

Где:

iPhyTxData содержит данные для уровня, а iPhyMode содержит режим, как эти данные
обрабатывать.

iPhyTxWrite показывает, в какой момент iPhyTxData и iPhyMode корректны.

oPhyTxReady показывает, когда компонент готов к приему данных. Фактически представляет собой
выход FULL FIFO, используемого для синхронизации компонентов.

oPhyRxData данные и статус, прочитанные с SD-карты.

oPhyRxWrite показывает, в какой момент значение oPhyRxData корректно.

oPhyCmdEnd признак, что компонент завершил обработку команды.

oSdCS сигнал выбора микросхемы (CS) для SD-карты.

oSdClk тактовый сигнал для SD-карты.

oSdMosi линия передачи данных в SD-карту.

oSdMosiT линия управления буфером линии передачи данных в SD-карту.

iSdMiso линия приема данных от SD-карты.

sclk тактовый сигнал для работы с SD-картой (50 МГц).

pclk тактовый сигнал, на котором работает командный уровень.

rst сигнал сброса, активный уровень единица.

В ПЛИС существуют специальные блоки для работы с тактовым сигналом (PLL, MMCM), однако,
получить с их выхода тактовый сигнал меньше 5 МГц проблематично. В результате физический уровень
работает на частоте 50 МГц. Вместе с каждыми данными в сигнале iPhyMode поступает бит, который
указывает, на какой частоте эти данные должны быть переданы в SD-карту (или приняты от нее). В
зависимости от бита скорости формируются сигналы разрешения тактового сигнала (Clock enable).

В компоненте физического уровня реализованы два автомата, для передачи данных в SD-карту и для
приема данных от нее.

Код автомата для передачи данных: github.

Состояние sDummy обеспечивает начальное формирование частоты, 128 переключений.

Состояние sTxBits обеспечивает передачу данных в SD-карту.

Код автомата для приема данных: github.

Состояние sRxBits обеспечивает прием данных от SD-карты.

Состояние sBusy обеспечивает ожидание готовности SD-карты (карта отпускает линию MISO к
уровню единицы).

Состояние sResp реализует чтение ответа при записи данных.

Состояние sToken реализует ожидание токена при чтении данных.

2.2 Компонент командного уровня

entity SdCommand is
generic ( gTCQ : time := 2 ns );
port ( -- Command from host
oSdInitComp : out std_logic;
oSdInitFail : out std_logic;
iSdAddress : in std_logic_vector(31 downto 0);
iSdStartErase : in std_logic;
iSdStartRead : in std_logic;
iSdStartWrite : in std_logic;
oSdCmdFinish : out std_logic_vector( 1 downto 0);
oSdhcPresent : out std_logic;
-- Data
oSdReadData : out std_logic;
iSdDataR : in std_logic_vector(31 downto 0);
oSdWriteData : out std_logic;
oSdDataW : out std_logic_vector(32 downto 0);
-- Spi
oSdCS : out std_logic;
oSdClk : out std_logic;
oSdMosi : out std_logic;
oSdMosiT : out std_logic;
iSdMiso : in std_logic;
-- system
pclk : in std_logic;
sclk : in std_logic;
rst : in std_logic );

Где:

oSdInitComp признак завершение инициализации SD-карты.

oSdInitFail признак неудачного завершения инициализации.

iSdAddress адрес в SD-карте для выполнения команды.

iSdStartErase запуск выполнения команды стирания.

iSdStartRead запуск выполнения команды чтения.

iSdStartWrite запуск выполнения команды записи.

oSdCmdFinish статус завершения команды. Нулевой бит равен единице, команда завершена
успешно. Первый бит равен единице, команда завершена с ошибкой.

oSdhcPresent признак обнаружения карты SDHC/SDXC.

oSdReadData чтение данных для записи в SD-карту.

iSdDataR данные для записи в SD-карту.

oSdWriteData признак записи данных, прочитанных с SD-карты.

oSdDataW данные, прочитанные с SD-карты.

Остальные сигналы совпадают с сигналами физического уровня.

В компоненте реализованы 5 автоматов.

smSdInit (github) — инициализация SD-карты.

smSdErase (github) — стирание данных с SD-карты.

smSdRead (github) — чтение данных с SD-карты.

smSdWrite (github) — запись данных на SD-карту.

smSdCommand (github) — на основе сформированных признаков подготавливает данные для


физического уровня от всех предыдущих автоматов.

2.3 Компонент общения с внешним миром

entity SdHost is
generic ( gTCQ : time := 2 ns );
port ( -- Sd Host command
iSdCommand : in std_logic_vector( 2 downto 0);
iSdAddress : in std_logic_vector(31 downto 0);
iSdStart : in std_logic;
oSdStatus : out std_logic_vector( 1 downto 0);
oSdInitFail : out std_logic;
-- Write data to card
iSdTxData : in std_logic_vector(31 downto 0);
iSdTxValid : in std_logic;
iSdTxLast : in std_logic;
oSdTxReady : out std_logic;
-- Read data from card
oSdRxData : out std_logic_vector(31 downto 0);
oSdRxValid : out std_logic;
oSdRxLast : out std_logic;
iSdRxReady : in std_logic;
-- Spi
oSdCS : out std_logic;
oSdClk : out std_logic;
oSdMosi : out std_logic;
oSdMosiT : out std_logic;
iSdMiso : in std_logic;
-- system
pclk : in std_logic;
sclk : in std_logic;
rst : in std_logic );

Где:

iSdCommand код команды для выполнения.

iSdAddress адрес для выполнения команды.

iSdStart запуск выполнения команды.

oSdStatus статус завершения команды. Нулевой бит равен единице — команда завершена. Первый
бит равен единице — команда завершена с ошибкой.

oSdInitFail признак неудачного завершения инициализации.

iSdTxData. Интерфейс Axi-Stream для записи данных в SD-карту. Порт с данными.

iSdTxValid. Интерфейс Axi-Stream для записи данных в SD-карту. Порт с сигналом записи.

iSdTxLast. Интерфейс Axi-Stream для записи данных в SD-карту. Порт с признаком последнего dw в
данных.

oSdTxReady. Интерфейс Axi-Stream для записи данных в SD-карту. Порт с признаком готовности к
приему данных.

oSdRxData. Интерфейс Axi-Stream для чтения данных из SD-карты. Порт с данными.

oSdRxValid. Интерфейс Axi-Stream для чтения данных из SD-карты. Порт с сигналом записи.

oSdRxLast. Интерфейс Axi-Stream для чтения данных из SD-карты. Порт с признаком последнего dw
в данных.

iSdRxReady. Интерфейс Axi-Stream для чтения данных из SD-карты. Порт с признаком готовности к
приему данных.

Остальные сигналы совпадают с сигналами физического уровня.

В компоненте реализован один автомат smSdControl (github).

Состояние sIdle. Ожидание завершения инициализации и команды на выполнение.

Состояние sWaitCmd. Проверка типа команды.

sReadCmd. Проверка места в FIFO, что поместится пакет, который будет прочитан из SD-карты и
формирование сигнала запуска команды чтения.

sWriteCmd. Проверка, что в FIFO есть пакет для записи в SD-карту, и формирование сигнала запуска
команды записи.

sEraseCmd. Формирование сигнала запуска команды стирания.

sWaitEnd. Ожидание завершения выполнения команды от командного уровня.

sFinish. Выход из автомата, команда выполнена.

3. Проверка в железе
Алгоритм написан, в симуляторе проверен. Нужно проверить теперь в железе. Из того, что имелось в
наличии, подошла плата Zybo от фирмы Digilent

На ней имеются свободные выводы ПЛИС в банке с напряжением +3,3В, к которому можно легко
подключить внешнее устройство. Да еще и тип используемой ПЛИС — Zynq-7000, значит там есть
процессорное ядро. Можно написать тест на языке С, что несколько упросит задачу тестирования.

Итак, подключаем реализованный алгоритм к процессорному ядру через порт GP (возможна работа по
4 байта, похоже на PIO). С прерываниями заморачиваться не будем, реализуем опрос по таймеру.

При работе на процессорном модуле алгоритм записи данных будет следующим:

Задать адрес в SD-карте.

Задать команду, код 2.

Записать данные в буфер, расположенный в программируемой логике.

Запустить выполнение команды.

Дождаться завершения выполнения команды.

Сбросить статус завершения команды.

Реализованный тест:

for (SectorAddress = 0; SectorAddress < 1048576; SectorAddress ++)


{
if ((SectorAddress % 1024) == 0)
{
xil_printf("Data write to %d sector \n\r", SectorAddress);
}

/** Set address */


Xil_Out32(0x43c00008, SectorAddress);

/** Set command */


Xil_Out32(0x43c00004, 2);

/** Write data to PL */


for (int32_t i = 0; i < 1024; i++)
{
Xil_Out32(0x43c00014, cntrData);
cntrData++;
}

/** Start */
Xil_Out32(0x43c00000, 1);

/** Wait end of operation */


for (;;)
{
status = Xil_In32(0x43c0000c);
if (status == 0x01 || status == 0x03)
{
if (status == 0x03)
{
xil_printf("Error in write \n\r");
}
break;
}
else
{
cntrDuration++;
usleep(100);
}
}

/** Duration operation */


durationWrite += cntrDuration;

if (cntrDuration > MaxWrite )


{
MaxWrite = cntrDuration;
}

cntrDuration = 0x00;

/** Clear start */


Xil_Out32(0x43c00000, 0);

SectorAddress += 7;
}

На вопрос, почему в цикле используется внешняя граница 1024. В алгоритме задано количество блоков,
равное 8. Размер одного блока равен 512 байт. Общий размер 8 блоков данных составляет 8 * 512 байт
= 4096 байт. Шина между процессорным модулем и программируемой логикой имеет размер 4 байта.
Получается, чтобы переслать из процессорного модуля в программируемую логику 4096 байт по 4
байта необходимо выполнить 4096 / 4 = 1024 операции записи.

При работе на процессорном модуле алгоритм чтения данных будет следующим:

Задать адрес в SD-карте.

Задать команду, код 1.

Запустить выполнение команды.

Дождаться завершения выполнения команды.

Сбросить статус завершения команды.

Прочитать данные из буфера в программируемой логике.

Реализованный тест:

for (SectorAddress = 0; SectorAddress < 1048576; SectorAddress++)


{
if ((SectorAddress % 1024) == 0)
{
xil_printf("Data read from %d sector \n\r", SectorAddress);
}

/** Set address */


Xil_Out32(0x43c00008, SectorAddress);

/** Set command */


Xil_Out32(0x43c00004, 1);

/** Start */
Xil_Out32(0x43c00000, 1);

/** Wait end of operation */


for (;;)
{
status = Xil_In32(0x43c0000c);
if (status == 0x01 || status == 0x03)
{
if (status == 0x03)
{
xil_printf("Error in read \n\r");
}
break;
}
else
{
cntrDuration++;
usleep(100);
}
}

/** Duration operation */


durationRead += cntrDuration;

if (cntrDuration > MaxRead )


{
MaxRead = cntrDuration;
}

cntrDuration = 0x00;

/** Clear start */


Xil_Out32(0x43c00000, 0);

/** Read data from PL */


for (int32_t i = 0; i < 1024; i++)
{
DataR = Xil_In32(0x43c0001c);
if (DataR != cntrData)
{
xil_printf("Data corrupt! \n\r");
}
DataR = Xil_In32(0x43c00020);
cntrData++;
}

SectorAddress += 7;
}

При работе на процессорном модуле алгоритм стирания данных будет следующим:

Задать адрес в SD-карте.

Задать команду, код 4.

Запустить выполнение команды.

Дождаться завершения выполнения команды.

Сбросить статус завершения команды.

Реализованный тест:

for (SectorAddress = 0; SectorAddress < 1048576; SectorAddress++)


{
if ((SectorAddress % 1024) == 0)
{
xil_printf("Data erase from %d sector \n\r", SectorAddress);
}

/** Set address */


Xil_Out32(0x43c00008, SectorAddress);

/** Set command */


Xil_Out32(0x43c00004, 4);

/** Start */
Xil_Out32(0x43c00000, 1);

/** Wait end of operation */


for (;;)
{
status = Xil_In32(0x43c0000c);
if (status == 0x01 || status == 0x03)
{
if (status == 0x03)
{
xil_printf("Error in write! \n\r");
}
break;
}
else
{
cntrDuration++;
usleep(100);
}
}

/** Duration operation */


durationErase += cntrDuration;

if (cntrDuration > MaxErase )


{
MaxErase = cntrDuration;
}

cntrDuration = 0x00;

/** Clear start */


Xil_Out32(0x43c00000, 0);

SectorAddress += 7;
}

Тест полностью на github.

4. Результаты

Количество данных Чтение Запись Стирание

1 блок (512 байт) 4.7 Мбит/с 1.36 Мбит/с 0.58 Мбит/с

8 блоков (4096 байт) 15.4 Мбит/с 6.38 Мбит/с 4.66 Мбит/с

16 блоков (8192 байта) 18.82 Мбит/с 11.26 Мбит/с 9.79 Мбит/с

Использовалась карта на 16 Гбайт. При тестировании было записано 2 Гбайта данных, прочитано 2
Гбайта данных, стерто 2 Гбайта данных.

Выводы неутешительны. При использовании FPGA нет смысла использовать SD-карту в режиме SPI,
кроме того случая, когда очень нужно хранить большие объемы данных без предъявления требований
к скорости.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Оцените статью:

92,3% Понравилась 60

1,5% Не понравилась 1

20,0% Хотелось бы узнать подробнее про Zynq 13

Хотелось бы узнать подробнее про передачу данных между процессорным модулем и


23,1% 15
программируемой логикой в Zynq

Проголосовали 65 пользователей. Воздержались 9 пользователей.

Теги: SD SPI, тест скорости, алгоритм работы с SD-картой, VHDL

Хабы: FPGA, Программирование микроконтроллеров

+31 91 7,9k 16 Поделиться

56,5 0,0
Карма Рейтинг

Павел @ Fenja
Пользователь

Github

ПОХОЖИЕ ПУБЛИКАЦИИ МИНУТОЧКУ ВНИМАНИЯ

21 декабря 2016 в 12:14

Программирование SPI Flash с помощью Arduino и SD-карты


+23 39,3k 108 14

28 сентября 2016 в 17:00 Опрос


Google Photos не может удалять файлы с SD карты
+14 42,4k 29 30 Вошёл в IT – а дальше что? Отвечают
хабраюзеры
22 марта 2016 в 19:04

Развитие SD-карт: как скорость записи увеличилась с 2 до 30 мегабайт в секунду


+10 22,8k 28 13

Мегапост
КУРСЫ

Сколько получали айтишники во


Python-разработчик с нуля второй половине 2020 года?
24 февраля 2021 • 7 месяцев • 69 900 ₽
• Нетология

Python для работы с данными


24 февраля 2021 • 4 месяца • 31 500 ₽
• Нетология

Android-разработчик с нуля
26 февраля 2021 • 7 месяцев • 70 740 ₽
• Нетология

iOS-разработчик с нуля
10 марта 2021 • 8 месяцев • 70 740 ₽
• Нетология

Школа тестирования. Тест-дизайн


15 марта 2021 • 14 900 ₽
• Luxoft Training

Больше курсов на Хабр Карьере

Комментарии 16 ЧТО ОБСУЖДАЮТ

HardWrMan 17 апреля 2020 в 14:19 0 Сейчас Вчера Неделя


SPI это здорово. А может в контексте SD карт всё же попробовать реализовать полноценный SDIO?
Поддельные батарейки в магазинах
Светофор
26,5k 118
Fenja 17 апреля 2020 в 15:09 0

Пока таких планов нет. В случае необходимости реализации SDIO, я бы лучше обратил внимание на eMMC. Фермеры в США вынуждены
У eMMC есть возможность установки на плату с помощью припаивания, а не с помощью разъема. взламывать тракторы, чтобы просто
починить их
96,7k 430
HardWrMan 17 апреля 2020 в 15:16 +1

С eMMC понятно, это подходит (и уже давно используется) для набортного несъемного накопителя. Но Часы для обнаружения жестов на
часто нужна именно съемная карта. И для SD карт их родной SDIO, хотя бы на уровне физики, как это основе машинного обучения, ESP8266
сделано в старших семействах микроконтроллеров STM32F тоже интересен. Особенно, если его можно
и Arduino
будет подключить к чему-то другому. 3,9k 11

Школа или как завалить


18 апреля 2020 в 03:08 0
архитектурное планирование
ikle
6,7k 59
Эм, если вы уже используете eMMC, то в чём проблема использовать вместо 8-битной шины 4-
битную? (Или 1-битную, если кто-то экономит на выводах/соединениях.) Протокол то один и если
использовать только базовые команды, то можно работать и с SD, и с eMMC. Более того, всегда Анализируй это: как разные бренды
можно спросить у устройства, какие размеры шины оно поддерживает.
используют мобильную аналитику
Мегапост
JEDEC Standard No. 84-A441, page 15:

DAT0-DAT7: These are bidirectional data channels. The DAT signals operate in push-pull mode. Only the
card or the host is driving these signals at a time. By default, after power up or reset, only DAT0 is used
for data transfer. A wider data bus can be configured for data transfer, using either DAT0-DAT3 or
DAT0-DAT7, by the MultiMediaCard controller. ...

Там же, стр. 18:

data: data can be transferred from the card to the host or vice versa. Data is transferred via the data
lines. The number of data lines used for the data transfer can be 1(DAT0), 4(DAT0-DAT3) or 8(DAT0-
DAT7)

ну и

For each data lines, data can be transferred at the rate of one bit (single data rate) or two bits (dual
data rate) per clock cycle.

Я уже плохо помню протокол, но, если не ошибаюсь, устройство всегда стартует в SDIO с однобитной
шиной. Сбрасываем устройство, получаем паспорт, переключаемся на максимальную из
поддерживаемых контроллером и устройством. И будут работать и древние MMC (1-битная шина) и
MMCPlus (1, 4 и 8), а также современные SD (1 и 4) и eMMC (1, 4 и 8).

VBKesha 18 апреля 2020 в 16:29 0

На оперкорес было готовое ядро, которое у нас патрировали на AXI шину, с DMA, правда на с
транзакцией максимум на 8 килобайт.

Fenja 18 апреля 2020 в 18:40 0

AXI4 или AXI-Stream? Сколько скорость получилась?

VBKesha 19 апреля 2020 в 00:33 +1

AXI4, изначально делался Stream но потом сделали AXI4 в пользу простоты программирования.
Железо было на стандарт 3.3V по нему максимальный клок 50Mhz ну и 4битная шина.
Расчетная должна быть около 200 Mbit/s где так оно и получалось в пике, ну а так а вычетом
времени на посыл команды старта отсановки, поменьше выходила но точных цифр не приведу,
я это года два назад тестировал.

Brak0del 17 апреля 2020 в 23:01 0

При использовании FPGA нет смысла использовать SD-карту в режиме SPI

Тут дело не совсем в FPGA. Вроде как это замысел создателей стандарта SD-карточек: режим SPI — это
единственный открытый (бесплатный) вариант работы с SD-карточкой, а остальные все закрытые, т.е. требуют
либо покупки лицензий, либо покупки IP-корок, либо реверс-инжиниринга их проприетарного протокола. В
то же время многие производители микроконтроллеров включают подобные ядра в свои продукты, в
результате конечному пользователю не приходится заморачиваться.

Fenja 18 апреля 2020 в 08:43 +1

Создали стандарта все равно немного жадные. В бесплатной документации нет временных диаграммы
работы по шине SPI, на которые удобно ориентироваться при разработке.
Картинка

Сейчас обнаружил, что вышла новая версия спецификации. Карты размером от 2 Тб до 128 Тб
включительно вынесли из группы SDXC и создали для них новую группу SDUC (Ultra Capacity SD Memory
Card). Карты SDUC режим SPI не поддерживают. Видимо чтобы не тащить протокол, которым мало кто
будет пользоваться.

mpa4b 20 апреля 2020 в 21:14 0

не очень понял из кода: использовались команды чтения/записи по 1 сектору или по много подряд? во 2
случае достаточно задать начальный сектор и лить данные сколько надо.

Fenja 20 апреля 2020 в 21:24 0

Подряд по несколько блоков.


При чтении тут мультиплексор выбора команды. Код команды определен в SDPackage.vhd
При записи тут мультиплексор выбора команды. Код команды определен в SDPackage.vhd

mpa4b 21 апреля 2020 в 00:20 0

спасибо. правильно ли я понимаю, что ваши измерения скоростей сделаны на кусках по 8 килобайт?
возможно, если вы возьмёте например 8 мегабайт, вы приблизитесь к 25 мегабитам (исходя из скорости
SPI-интерфейса), как минимум на чтении (т.к. скорость записи может ограничиваться неидеальностью
SD-карты как микро-пародии на SSD :))

Fenja 21 апреля 2020 в 08:57 0

Да, объем данных, читаемых подряд, был равен 8кБайт. С увеличением размера читаемых данных, я
думаю скорость на чтение вырастет. Проблема только в ограниченном объеме памяти в ПЛИС. Я
хотел попробовать работу с 32 блоками (16кБайт), но это потребовало бы модификацию командного
уровня. В результате мне стало жалко 4 BRAM на прием, 4 BRAM на передачу и своего времени
(Размер одного BRAM в ПЛИС Zynq равен 4кБайт). От идеи я отказался.

mpa4b 23 апреля 2020 в 14:46 0

ну чтоб замерить скорость чтения, данные необязательно сохранять :)

VBKesha 22 января 2021 в 11:46 +1

Скорей всего здесь это уже никому не интересно, но есть в спецификации такой кусоче:

It is an obvious requirement that the clock must be running for the SD Card to output data or response
tokens. After the last SD Card bus transaction, the host is required to provide eight (8) clock cycles for
the card to complete the operation before shutting down the clock. Following is a list of various SD
Card bus transactions:
− A command with no response—eight clocks after the host command end bit.
− A command with response—eight clocks after the card response end bit.
− A read data transaction—eight clocks after the end bit of the last data block.
− A write data transaction—eight clocks after the CRC status token.

То есть после исполнения команды и получения ответа, надо выдавать ещё 8 «пустых» клоков. Обычно это не
требуется, и многие так не делают. Но вчера мне как раз попалась карта, которая без этого не проходила
инициализацию.

Fenja 23 января 2021 в 14:55 0

Мне попадалась информация, что нужно сформировать 8 «пустых» переключений тактового сигнала
перед формированием команды и я это реализовал. Думал, что отражено в статье, оказывается нет. Я
расширил код команды, добавив в старший байт 0xFF.
github.com/Finnetrib/SdController/blob/cab2ef85c89d9ed7b696d84bf141bc6eb3f89a6b/SD_Controller.srcs/sources

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

САМОЕ ЧИТАЕМОЕ

Сутки Неделя Месяц

Как тебе такое, Джефф Безос?


+66 31,4k 41 290

Поддельные батарейки в магазинах Светофор


+124 26,5k 34 118

Уверенный пользователь ПК? Серьёзно?


+52 49k 86 461

Фермеры в США вынуждены взламывать тракторы, чтобы просто починить их


+73 96,7k 61 430

Куда же плывут облака? Подбиваем итоги опроса и даём аналитику


Мегапост

Ваш аккаунт Разделы Информация Услуги

Войти Публикации Устройство сайта Реклама

Регистрация Новости Для авторов Тарифы

Хабы Для компаний Контент

Компании Документы Семинары

Пользователи Соглашение Мегапроекты

Песочница Конфиденциальность Мерч

© 2006 – 2021 «Habr» Настройка языка О сайте Служба поддержки Мобильная версия