Привет, Habr! Однажды на работе мне досталась задача оценить возможность реализации хранения данных на SD-карте при подключении ее к FPGA. В качестве интерфейса взаимодействия предполагалось использование SPI, так как он проще в реализации. Полученным опытом хотелось бы поделиться.
Так как место на печатной плате всегда ограничено, рассмотрение SD-карт будет выполняться на примере карт в форм-факторе MicroSD.
Содержание
1. Чтение спецификации
1.1 Общие сведения
1.2 Инициализация
1.3 Стирание информации
1.4 Чтение информации
1.4.1 Чтение одного блока данных
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 бит.
Все команды и ответы на них подробно расписаны в спецификации Physical Layer Simplified Specification
1.2 Инициализация
Алгоритм инициализации:
- После включения питания необходимо подождать минимум 1 мс.
- Сформировать минимум 74 переключения тактового сигнала для карты. Линии CS и MOSI должны быть в состоянии логической единицы.
- Сформировать команду CMD0. Команда CMD0 выполняет сброс карты SD.
- Ожидать ответа на команду CMD0. Ответом является ответ типа R1. В случае, если ответ R1 не пришел за 16 тактов частоты инициализации, переход к шагу 3. Если ответ R1 от карты поступил, но отличается от значения 0x01 (карта не перешла в состояние инициализации), переход к шагу 3, иначе переход к шагу 5.
- Сформировать команду CMD8. Команда отправляет карте напряжения питания контроллера и тестовую последовательность для проверки линии.
- Ожидать ответа на команду CMD8. Ответом являет ответ типа R7. В случае, если в ответе содержится бит, что команда не поддерживается, переход к шагу 7, иначе, если тестовая последовательность и напряжения питания карты совпадают с отправленными контроллером, переход к шагу 17, иначе к шагу 13.
- Отправка команды CMD55. Команда CMD55 сообщает карте, что следующая команда будет нестандартная.
- Ожидание ответа на команду CMD55. Ответом является ответ типа R1. Если ответ не равен 0x01 (произошла ошибка при выполнении команды), переход к шагу 7, иначе к шагу 9.
- Отправка команды ACMD41. Команда ACMD41 сообщает карте, что поддерживает карты SDSC (параметр 0x00000000) и запускает процесс инициализации.
- Ожидание ответа на команду ACMD41. Ответом является ответ типа R1. В случае если ответе содержится бит, что команда не поддерживается, переход к шагу 11. Иначе, если ответ равен 0x00 (инициализация завершена) переход к шагу 14, в противном случае переход к шагу 7.
- Отправка команды CMD1. Команда CMD1 аналогична команде ACMD41.
- Ожидание ответа на команду CMD1. Ответом являет ответ типа R1. В случае если в ответе содержится бит, что команда не поддерживается, переход к шагу 13, иначе, если ответ равен 0x00 (инициализация завершена), переход к шагу 14, в противном случае переход к шагу 11.
- Состояние ошибки. Инициализация завершилась неудачно, дальнейшая работа невозможна. Выход из этого состояния не предусмотрен.
- Отправка команды CMD16. Команда используется для настройки в карте режима работы с блоками, размерами 512 байт.
- Ожидание ответа на команду CMD16. Ответом является ответ типа R1. В случае, если ответ равен 0x00 (команда завершена удачно) переход к шагу 16, иначе к шагу 13.
- Состояние удачного завершения инициализации. Можно начинать работать с данными на карте. Выход из алгоритма.
- Отправка команды CMD55. Команда CMD55 сообщает карте, что следующая команда будет нестандартная.
- Ожидание ответа на команду CMD55. Ответом является ответ типа R1. Если ответ не равен 0x01 (произошла ошибка при выполнении команды), переход к шагу 17, иначе к шагу 19.
- Отправка команды ACMD41. Отправка команды ACMD41. Команда ACMD41 сообщает карте, что поддерживает карты SDHC (параметр 0x40000000) и запускает процесс инициализации.
- Ожидание ответа на команду ACMD41. Ответом является ответ типа R1. В случае если ответе содержится бит, что команда не поддерживается, переход к шагу 13. Иначе, если ответ равен 0x00 (инициализация завершена) переход к шагу 21, в противном случае переход к шагу 17.
- Отправка команды CMD58. Чтение регистра конфигурации карты.
- Ожидание ответа на команду CMD58. Ответом является ответ типа R3. В случае, если в ответе установлен бит, что карта работает с адресами блоков по 512 байт, переход к шагу 16, иначе к шагу 14.
После завершения инициализации с картой можно работать блоками по 512 байт с тактовой частотой в 25 МГц. Это полная версия алгоритма инициализации, которая охватывает все типы карт. В моем случае, при использовании карты на 16 Гбайт алгоритм инициализации состоял из шагов 1-6, 17-22, 16.
1.3 Стирание информации
Карты форм-фактора Micro SD поддерживают команды стирания информации. После команды стирания информации, значение указанных адресов для стирания будет заполнено значением 0xFF или 0x00, в зависимости от карты.
Алгоритм стирания информации
- Отправка команды CMD32. Команда устанавливает адрес первого блока для стирания информации.
- Ожидание ответа на команду CMD32. Ответом является ответ типа R1. В случае если ответ не равен 0x00 (произошла какая-либо ошибка при выполнении команды), переход к шагу 3, иначе к шагу 4.
- Состояние ошибки. Стирание завершилось неудачно, выход из алгоритма стирания информации.
- Отправка команды CMD33. Команда устанавливает адрес последнего блока для стирания информации. В случае если стереть нужно только один блок, адрес должен быть равен адресу из команды CMD32.
- Ожидание ответа на команду CMD33. Ответом является ответ типа R1. В случае если ответ не равен 0x00 (произошла какая-либо ошибка при выполнении команды), переход к шагу 3, иначе к шагу 6
- Отправка команды CMD38. Команда стереть информацию с выделенных блоков. В качестве аргумента должны быть отправлены любые 4 байта, кроме значений 0x00000001, 0x00000002.
- Ожидание ответа на команду CMD38. Ответом является ответ типа R1b. Это расширенная версия ответа, когда карта формирует ответ R1, а потом притягивает линию MISO к нулю, индицируя занятость микросхемы. Необходимо дождаться появления на линии MISO значения единицы. В случае если ответ не равен 0x00 (произошла какая-либо ошибка при выполнении команды), переход к шагу 3, иначе к шагу 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 кодируют тип ошибки.
Алгоритм чтения множества блоков данных:
- Отправка команды CMD18. Команда сообщает карте, что будет выполняться чтение множества блоков.
- Ожидание ответа на команду CMD18. Ответом является ответ типа R1. В случае если ответ не равен 0x00 (произошла какая-либо ошибка при выполнении команды), переход к шагу 3, иначе к шагу 4.
- Состояние ошибки. Чтение завершилось неудачно, выход из алгоритма чтения.
- Ожидание токена от карты. В случае если от карты получен Error Token, переход к шагу 3, иначе к шагу 5.
- Прием от карты блока данных размером 512 байт.
- Прием от карты поля CRC размером 2 байта.
- Проверка количества принятых блоков данных. Если принято необходимое количество блоков, переход к шагу 8, иначе к шагу 4.
- Отправка команды CMD12. Команда сообщает карте о необходимости остановки чтения. В случае, если в этот момент выполняется передача Data Packet, она будет прервана.
- Ожидание ответа на команду CMD12. Ответом является ответ типа R1b. Это расширенная версия ответа R1, в котором карта после формирования ответа притягивает линию MISO к нулю, индицируя занятость карты. Необходимо дождаться появления на линии MISO значения единицы, что будет свидетельствовать об окончании команды. В случае если ответ не равен 0x00 (произошла какая-либо ошибка при выполнении команды), переход к шагу 3, иначе к шагу 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 nr", 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 nr");
}
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 nr", 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 nr");
}
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! nr");
}
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 nr", 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! nr");
}
break;
}
else
{
cntrDuration++;
usleep(100);
}
}
/** Duration operation */
durationErase += cntrDuration;
if (cntrDuration > MaxErase )
{
MaxErase = cntrDuration;
}
cntrDuration = 0x00;
/** Clear start */
Xil_Out32(0x43c00000, 0);
SectorAddress += 7;
}
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, кроме того случая, когда очень нужно хранить большие объемы данных без предъявления требований к скорости.
Автор: Павел