Инструменты пользователя

Инструменты сайта


prog:example_v6:otp_test_ve8

Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

Предыдущая версия справа и слева Предыдущая версия
Следующая версия
Предыдущая версия
prog:example_v6:otp_test_ve8 [2019/10/10 17:15]
vasco [Алгоритм программирования]
prog:example_v6:otp_test_ve8 [2019/12/17 14:25] (текущий)
vasco [Программирование, допрограммирование, верификация]
Строка 1: Строка 1:
 ======OTP_Test_VE8 - программирование OTP в 1986ВЕ8Т ====== ======OTP_Test_VE8 - программирование OTP в 1986ВЕ8Т ======
-Вместо привычной флеш памяти в микроконтроллере 1986ВЕ8Т реализована однократно программируемая память ОТР - (One-Time Programable). Такая память бывает двух типов - Fuse и AntiFuse. Fuse переводится как предохранитель,​ что намекает на способ программирования. В памяти типа Fuse программирование бита сводится к пережиганию перемычки,​ а в памяти AntiFuse к созданию пробоя подзатворного диэлектрика.+Вместо привычной флеш памяти в микроконтроллере 1986ВЕ8Т реализована однократно программируемая память ОТР - (One-Time Programable). Такая память бывает двух типов - Fuse и AntiFuse. Fuse переводится как предохранитель,​ что намекает на способ программирования. В памяти типа Fuse программирование бита сводится к пережиганию перемычки,​ а в памяти AntiFuse ​к созданию пробоя подзатворного диэлектрика.
  
 Красивые картинки про память и многое полезное можно найти в данной статье - [[https://​semiengineering.com/​the-benefits-of-antifuse-otp/​|The Benefits Of Antifuse OTP]]. Красивые картинки про память и многое полезное можно найти в данной статье - [[https://​semiengineering.com/​the-benefits-of-antifuse-otp/​|The Benefits Of Antifuse OTP]].
  
-В обоих случаях изначально биты имеют значения 0, а прожигаются только те биты, которые должны стать "​1"​. Согласно приведенной статье,​ прожит бита памяти Fuse можно сделать только один раз. Если после единичного прожига бит не стал читаться как "​1",​ то память бракуется. В отличие от Fuse, бит памяти типа AntiFuse можно прожигать несколько ​раз, в статье про порядка 18 раз. При прожиге происходит пробой подзатворного диэлектрика,​ если пробой будет небольшой то его можно "​прожигать"​ повторно,​ чтобы образовался надежный контакт. Если контакт оставить плохой,​ то память не сможет работать на заявленных скоростях.+В обоих случаях изначально биты имеют значения 0, а прожигаются только те биты, которые должны стать "​1"​. Согласно приведенной статье,​ прожиг бита памяти Fuse можно сделать только один раз. Если после единичного прожига бит не стал читаться как "​1",​ то память бракуется. В отличие от Fuse, бит памяти типа AntiFuse можно прожигать несколько раз. При прожиге происходит пробой подзатворного диэлектрика,​ если пробой будет небольшойто его можно и нужно "​прожигать"​ повторно,​ чтобы образовался надежный контакт. Если контакт оставить плохой,​ то память не сможет работать на заявленных скоростях.
  
-=====Программирование,​ допрограммирование,​ верификаци===== +=====Программирование,​ допрограммирование,​ верификация===== 
-Все вышесказанное объясняет алгоритм программирования памяти ОТР в микроконтроллере 1986ВЕ8Т. Согласно спецификации,​ сначала необходимо провести первичный прожиг бит равных "​1"​ - **"​фаза программирования"​**. После этого запускается **"​фаза допрограммирования"​**,​ когда считываются текущие значения в ячейках и если где-то "​1"​ не прописалась,​ то запускается процедура программирования для данного бита. После окончания допрограммирования необходимо проверить,​ на сколько качественно прожглись каналы в подзатворном диэлектрике. Для этого запускается этап **"​верификации"​**,​ который на максимально заявленной для памяти частоте сверяет значения ячеек памяти. Если каналы будут слабые,​ то при высокой скорости обращения будут возникать сбои, память будет читаться с ошибками,​ т.е. не будет совпадать со значениями которые в нее были записаны. Описанная ​сейчас ​процедура составляет один **цикл программирования** памяти. Гарантированный компанией "​Миландр"​ коэффициент программируемости составляет 0,7. Т.е. потенциально больше 30% операций записи ​может закончится неудачей. Но это гарантируемый коэффициент,​ на самом деле процент гораздо выше.+Все вышесказанное объясняет алгоритм программирования памяти ОТР в микроконтроллере 1986ВЕ8Т. Согласно спецификации,​ сначала необходимо провести первичный прожиг бит равных "​1"​ - **"​фаза программирования"​**. После этого запускается **"​фаза допрограммирования"​**,​ когда считываются текущие значения в ячейках и если где-то "​1"​ не прописалась,​ то запускается процедура программирования для данного бита. После окончания допрограммирования необходимо проверить,​ на сколько качественно прожглись каналы в подзатворном диэлектрике. Для этого запускается этап **"​верификации"​**,​ который на максимально заявленной для памяти частоте сверяет значения ячеек памяти. Если каналы будут слабые,​ то при высокой скорости обращения будут возникать сбои, память будет читаться с ошибками,​ т.е. не будет совпадать со значениямикоторые в нее были записаны. Описанная ​выше процедура составляет один **цикл программирования** памяти. Гарантированный компанией "​Миландр"​ коэффициент программируемости составляет 0,7. Т.е. при программировании 100 микросхем ​будут успешно запрограммированы только 70, а 30 микросхем запрограммировать не удастся - будут ошибки. Но это гарантируемый коэффициент,​ на самом деле процент ​программируемых микросхем ​гораздо выше. Каждый производитель оставляет себе некоторый запас.
  
 В случае,​ если в первом цикле программирования верификация выявила наличие ошибок,​ то есть возможность запустить **повторный цикл**. Но во вторичном цикле необходимо исключить операцию программирования. Это связано с тем, что вторичный прожиг уже пробитого бита не желателен. Поэтому второй цикл программирования должен состоять только из этапов **допрограммирования** и **верификации**. Производитель не рекомендует использовать более двух циклов программирования,​ предлагая в случае неудачи второго цикла браковать микроконтроллер. В случае,​ если в первом цикле программирования верификация выявила наличие ошибок,​ то есть возможность запустить **повторный цикл**. Но во вторичном цикле необходимо исключить операцию программирования. Это связано с тем, что вторичный прожиг уже пробитого бита не желателен. Поэтому второй цикл программирования должен состоять только из этапов **допрограммирования** и **верификации**. Производитель не рекомендует использовать более двух циклов программирования,​ предлагая в случае неудачи второго цикла браковать микроконтроллер.
  
-Стоит отметить,​ что если в фазе допрограммирования сразу проверять значение только что прожженого бита, то считать "​0"​ вместо положенного "​1"​ считается нормальным. Дело в том, что для памяти есть настройки,​ которые задают "​стойкость"​ значений к считыванию. Эти параметры называются **репликами** и задаются в регистре **TUNING** блока MDR_OTP. В данном регистре можно задать какое из значений "​0"​ или "​1"​ будет читаться "​легче"​. Так, например,​ если задать чтобы легче читался "​0",​ то чтение "​1"​-цы будет затруднено. К сожалению,​ я не обладаю знаниями чем эти "​легче/​тяжелее"​ обусловлены физически,​ но смысл здесь в том, что для прожига необходимо выставить наибольшее сопротивление к чтению,​ чтобы записанный бит читался "​1"​-цей только в том случае,​ когда уже диэлектрик пробит основательно. Это гарантирует,​ что при обычных репликах чтения,​ которые используются на этапе верификации,​ память будет читаться на максимальной частоте успешно. **В итоге, только этап верификации показывает было ли программирование успешно!**+Стоит отметить,​ что если в фазе допрограммирования сразу проверять значение только что про прожженного бита, то считать "​0"​ вместо положенного "​1"​ считается нормальным. Дело в том, что для памяти есть настройки,​ которые задают "​стойкость"​ значений к считыванию. Эти параметры называются **репликами** и задаются в регистре **TUNING** блока MDR_OTP. В данном регистре можно задатькакое из значений "​0"​ или "​1"​ будет читаться "​легче"​. Так, например,​ если задатьчтобы легче читался "​0",​ то чтение "​1"​-цы будет затруднено. К сожалению,​ я не обладаю знаниямичем эти "​легче/​тяжелее"​ обусловлены физически,​ но смысл здесь в том, что для прожига необходимо выставить наибольшее сопротивление к чтению,​ чтобы записанный бит читался "​1"​-цей только в том случае,​ когда уже диэлектрик пробит основательно. Это гарантирует,​ что при обычных репликах чтения,​ которые используются на этапе верификации,​ память будет читаться на максимальной частоте успешно. **В итоге, только этап верификации показываетбыло ли программирование успешно!**
  
-Кроме этого, чип бракуется если на этапе допрограммирования будет выявлено что какой-то из битов, который должен быть нулем оказался вдруг "​1"​-ей. Это может произойти,​ например,​ если данные начинают писаться в память,​ которая уже была записана раннее другими данными. Когда вместо нового "​0"​-ля,​ в памяти уже находится старая "​1"​-ца,​ то исправить такое положение нельзя. ​Повторюсь - **Нули в памяти находятся изначально,​ записываются только "​1"​-цы!**+Кроме этого, чип бракуетсяесли на этапе допрограммирования будет выявленочто какой-то из битов, который должен быть нулем оказался вдруг "​1"​-ей. Это может произойти,​ например,​ если данные начинают писаться в память,​ которая уже была записана раннее другими данными. Когда вместо нового "​0"​-ля,​ в памяти уже находится старая "​1"​-ца,​ то исправить такое положение нельзя. ​Обратите,​ пожалуйста, ​внимание,​ что **Нули в памяти находятся изначально,​ записываются только "​1"​-цы!** 
 + 
 +//​(Дополнительную информацию можно найти здесь - [[https://​startmilandr.ru/​doku.php/​doc:​mk:​1645rr#​%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5_1645%D1%80%D1%823%D1%83|Программирование 1645РТ3У]])//​
  
 =====Алгоритм программирования===== =====Алгоритм программирования=====
-Сравнивая описание этапов программирования и допрограммирования можно заметить что они мало отличаются. Собственно,​ различий всего два:+Сравнивая описание этапов программирования и допрограммированияможно заметитьчто они мало отличаются. Собственно,​ различий всего два:
   - Допрограммирование считывает текущее значение слова из памяти и затем программирует только недостающие 1-цы. Программирование же пишет 1-цы всегда,​ поскольку не читает текущее значение.   - Допрограммирование считывает текущее значение слова из памяти и затем программирует только недостающие 1-цы. Программирование же пишет 1-цы всегда,​ поскольку не читает текущее значение.
   - Программирование записывает бит только один раз, допрограммирование же пытается сделать это до 40 раз.   - Программирование записывает бит только один раз, допрограммирование же пытается сделать это до 40 раз.
  
-Если свести описания этапов на одну блок-схему то получается такая картина:​+Если свести описания этапов на одну блок-схемуто получается такая картина:​
  
 {{1986be8t:​otp_algormix.png}} {{1986be8t:​otp_algormix.png}}
  
-По схеме видно, что различия крайне незначительны и можно реализовать оба этапа одной функцией. При этом//"​Различие 2"// решается передачей в функцию количества повторов программирования бита1 цикл для программирования и 40 для допрограммирования. Остается //"​различие 1"// с присутствием чтения текущего значения при допрограммировании. Но будет ли хуже, если программирование тоже будет читать текущее значение ячейки и писать только если есть незаписанные 1-цы?+По схеме видно, что различия крайне незначительны и можно реализовать оба этапа одной функцией. При этом //"​Различие 2"// решается передачей в функцию количества повторов программирования бита ​1 цикл для программирования и 40 для допрограммирования. Остается //"​различие 1"// с присутствием чтения текущего значения при допрограммировании. Но будет ли хуже, если программирование тоже будет читать текущее значение ячейки и писать толькоесли есть незаписанные 1-цы?
  
-Изначально предполагается что чип чист и вся память 0-вая, поэтому нет чтения в программировании. В допрограммировании же важно исключить вторичный прожиг уже пробитого бита, поэтому чтение необходимо. ​Поэтому, лишнее чтение ничем этапу программирования не навредит. Репликами чтение 0-ей выставлено максимально легким,​ т.е. нет шансов что "​0"​ друг прочтется "​1"​-ей и на этапе чтения ​будет пропущено программирование какого-либо бита из-за того что было предварительное чтение. Кроме этого, если по ошибке произойдет запуск программирования для уже записанной памяти,​ то это исключит повторную запись уже пробитого бита.+Изначально предполагаетсячто чип чисти вся память 0-вая, поэтому нет чтения в программировании. В допрограммировании же важно исключить вторичный прожиг уже пробитого бита, поэтому чтение необходимо. ​Судя по всему, лишнее чтение ничем этапу программирования не навредит. Репликами чтение 0-ей выставлено максимально легким,​ т.е. нет шансовчто "​0"​ друг прочтется "​1"​-ей и будет пропущено программирование какого-либо бита из-за тогочто добавилось предварительное чтение. Кроме этого, если по ошибке произойдет запуск программирования для уже записанной памяти,​ то это исключит повторную запись уже пробитого бита.
  
-В целом я не вижу ​никакого резона делать две отдельные функции для этапов программирования и допрограммирования. Достаточно реализовать их в одной функции,​ на вход ​задав ​лишь количество допустимых повторов. В библиотеке Pack_V6 это сделано так:+В целомя не вижу ​причины делать две отдельные функции для этапов программирования и допрограммирования. Достаточно реализовать их в одной функции, ​задав ​на вход лишь количество допустимых повторов. В библиотеке Pack_V6 это сделано так:
  
 <​code>​ <​code>​
Строка 44: Строка 46:
 </​code>​ </​code>​
  
-Листинг самой функции я приводить не буду, исходники доступны на GitHub.+Листинг самой функции я приводить не буду, исходники доступны на GitHub, драйвер MDR_OTP - [[https://​github.com/​StartMilandr/​MDR_Pack_v6/​blob/​master/​PACK_Gen/​Files/​SPL/​Drivers/​MDR_OTP_VE8x.h|MDR_OTP_VE8x.h]] и [[https://​github.com/​StartMilandr/​MDR_Pack_v6/​blob/​master/​PACK_Gen/​Files/​SPL/​Drivers/​MDR_OTP_VE8x.c|MDR_OTP_VE8x.c]] . 
 + 
 +С помощью драйвера MDR_OTP запись памяти выглядит так, отрывок из проекта описанного ниже: 
 +<​code>​ 
 +void OPT_WriteTestData(void) 
 +
 +  MDR_OTP_Enable(); ​                           // - Выключение доступа к контроллеру OTP, KEY 
 +  MDR_OTP_ProgBegin_ByClockCPU_Hz(freqCPU_Hz);​ // - Расчет задержек,​ выставление минимального DUcc, 
 +                                               // ​  ​задержка HV_PE, выставление реплик в TUNING 
 +  MDR_OTP_ProgWord(OTP_TEST_PROG_ADDR,​ OTP_TEST_PROG_DATA); ​  // Программирование слова 
 +  ... 
 +  ...   
 +  ... 
 + 
 +  MDR_OTP_ProgEnd(); ​                          // - реплики и DUcc в состояние по умолчанию 
 +  MDR_OTP_Disable(); ​                          // - выключение доступа к контроллеру OTP, KEY 
 +
 +</​code>​ 
 + 
 +Внешнее напряжение программирования 7,2 В должно быть задано до исполнения данного кода! Повышенное напряжение необходимо,​ чтобы пробить диэлектрик,​ для этих же целей в функции MDR_OTP_ProgBegin() понижается питание самой микросхемы через подстройку внутренних LDO. После модификации LDO выдерживается необходимая задержка между подачей питания HV и входом в режим программирования - бит PE. 
 +=====Пример OTP_Test_VE8===== 
 +Данный пример был реализован,​ чтобы проверить работоспособность драйвера MDR_OTP. В примере используются все три кнопки на отладочной плате:​ 
 +  * Key1 - Записать слово OTP_TEST_PROG_DATA в память по адресу OTP_TEST_PROG_ADDR 
 +  * Key2 - Считать слово из адреса OTP_TEST_PROG_ADDR и сравнить со значением OTP_TEST_PROG_DATA  
 +  * Key3 - Запустить/​остановить вывод задержек на светодиод LED3. 
 + 
 + ​Для индикации используется три светодиода:​ 
 +  * LED1 (Cyclic) - мигает с произвольным периодом,​ показывая,​ что МК работает. 
 +  * LED2 (OK) - загорается по Key2, если слово в памяти совпало с ожидаемыми данными. 
 +  * LED3 (Fault)- загорается по Key2, если чтение слова из памяти НЕ совпало с ожидаемыми данными. 
 + 
 +====Тестовые адрес и данные==== 
 +Слова в проекте пишутся,​ начиная с конца памяти ОТР, чтобы оставить начальные адреса для рабочей программы. Пишется всего одно слово, на нем проверяется,​ как отрабатывает запись и чтение. Для записи следующего слова необходимо исправить в коде значение OTP_TEST_TEST_IND,​ потому что оно задает адрес:​ 
 + 
 +<​code>​ 
 +//  Увеличивать после каждой записи - сдвиг на чистую память с конца ОТР. 
 +//  Минимальное значение = 1 
 +#define OTP_TEST_TEST_IND ​         5 
 + 
 +#define OTP_TEST_ADDR_END ​         0x01020000 
 +#define OTP_TEST_PROG_ADDR ​        ​(OTP_TEST_ADDR_END - (OTP_TEST_TEST_IND * 4)) 
 +</​code>​ 
 + 
 +В тесте был опробован прожиг от трех источников тактирования,​ данные,​ записываемые при каждом прожиге,​ тоже менялись:​ 
 + 
 +<​code>​ 
 +//  Значения записываемые/​записанные в память в порядке OTP_TEST_TEST_IND 
 +//  1: OTP_TEST_TEST_IND = 1 от HSE0_PLL_Max 
 +//  #define OTP_TEST_PROG_DATA ​        ​0x13768421 
 +//  2: OTP_TEST_TEST_IND = 2 от HSE0_PLL_Max 
 +//  #define OTP_TEST_PROG_DATA ​        ​0x87245687 ​   
 +//  3: OTP_TEST_TEST_IND = 3 от HSI_Trim (задержки точные) 
 +//  #define OTP_TEST_PROG_DATA ​        ​0xABCDEFED 
 +//  4: OTP_TEST_TEST_IND = 4 от HSI без Trim (задержки не точные) 
 +//  #define OTP_TEST_PROG_DATA ​        ​0x12345678 
 +//  5: OTP_TEST_TEST_IND = 5 от HSE1 Gen 25MHz 
 +    #define OTP_TEST_PROG_DATA ​        ​0x9ABCDEF 
 +</​code>​ 
 + 
 +  * При тактировании HSE0_PLL_Max ядро тактируется от резонатора HSE0 10МГц на плате, с последующим умножением в PLL для получения максимально допустимой частоты тактирования от PLL  в 40МГц (см. Errata 0051 - большой джиттер PLL).  
 +  * При тактировании от HSI ядро работало на штатном неточном внутреннем генераторе HSI. Прошивка проводилась как при подстроенном значении HSI, так и при том значении частоты,​ какое возникает при штатном значении Trim. //(Trim - это значение подстройки частоты HSI, значение по умолчанию 32, диапазон значений от 0 до 63.)//  
 +  * Последним вариантом проверялась прошивка от внешнего генератора на 25 МГц, который заводился на HSE1. 
 + 
 +В сущности не важно, на какой частоте происходит прошивка,​ все варианты отработали с первого раза - с первого нажатия на кнопку Key1. **Важно правильно выдержать необходимые задержки в циклах программирования!** Рекомендуемая частота прошивки ~24МГц необходима лишь для проведения этапа верификации. Ведь читать память необходимо на ее максимальной рабочей частоте. В данном примере это возможно только в варианте тактирования от внешнего генератора. 
 + 
 +====Реализация задержек==== 
 +Необходимые задержки указаны в спецификации на стр. 215, таблица 36: 
 + 
 +^  Название ​ ^  Имя ​  ​^ ​ Мин значение ​ ^  Кол. тактов ​ ^^^^ 
 +^  :::  ^ :::  ^  мкс ​ ^  8 МГц ​ ^  25 МГц ^  40 МГц ^   64 МГц ​ ^ 
 +^ Режим записи - бит PE  ^^^^^^^ 
 +| Предустановка HV перед PE  | t_HV_PE |  10000 |  80000 |  250000 |  400000 |  640000 | 
 +| Предустановка PE перед DATA  | t_PE_D |  300 |  2400 |  7500 |  12000 |  19200 | 
 +| Предустановка ADR перед DATA  | t_A_D |  300 |  2400 |  7500 |  12000 |  19200 | 
 +| Удержание ADR после DATA  | t_D_A |  5 |  40 |  125  |  200 |  320 | 
 +| Время программирования ​ | t_Prog |  3000 |  24000 |  75000 |  120000 |  192000 | 
 +| Задержка между битами ​ | t_LD |  5 |  40 |  125 |  200 |  320 | 
 +| Удержание PE после DATA  | t_D_PE |  0 |  0 |  0 |  0 |  0 | 
 +^ Режим чтения - бит SE  ^^^^^^^ 
 +| Предустановка ADR перед SE  | t_A_SE |  0.005 |  0,04 |  0,125 |  0,2 |  0,32 | 
 +| Удержание ADR после SE  | t_SE_A |  0 |  0 |  0 |  0 |  0 | 
 +| Длительность HSE  | t_SE |  0.01 | 0,08 |  0,25 |  0,4 |  0,64 | 
 + 
 +По таблице видно, что для всех допустимых частот ядра достаточно лишь четырех задержек - на 10мс, 3мс, 300мкс и 5мкс. Остальные задержки составляют меньше чем один такт ядра, поэтому заботиться об их выдержке нет необходимости.  
 + 
 +Задержка 5мкс между записью нескольких бит подряд не пригодилась. Дело в том, что на блок-схемах в спецификации описан режим программирования,​ в котором каждый бит записывается и тут же читается 5 раз, чтобы убедиться,​ что он прошит. Если бит не прошит,​ то он снова прожигается,​ вплоть до 30 раз. На диаграмме же сигналов в спецификации "​Рисунок 57 – Временная диаграмма процесса программирования OTP памяти"​ представлен режим, в котором биты записываются подряд,​ а затем видимо целиком происходит чтение всего слова. По тексту спецификации подобный режим не описан,​ но реализован у Vasili с форума в его программе прошивки OTP по UART - [[http://​forum.milandr.ru/​viewtopic.php?​p=16804#​p16804|PRG_OTP_UART]]. 
 + 
 +Драйвер MDR_OTP был реализован согласно блок схем из спецификации и примера полученного от тех-поддержки. Диаграмма задержек при записи одного бита получается такая:​ 
 + 
 +{{mk8:​otp_bit_delays.png}} 
 + 
 +На диаграмме есть не упоминающиеся в спецификации задержки,​ это: 
 +  * t_Acc - время чтения памяти,​ которое по спецификации составляет 40нс. Обработка данной задержки в ПО не нужна. Потому что в коде после выставления SE идет инициализация цикла на 5 чтений памяти,​ что займет гораздо больше циклов,​ чем требуется на данную задержку. 
 +  * t_PE_SE - время между выходом из режима программирования PE и входом в режим чтения SE. В образцах кода от техподдержки и у Vasili эта задержка совпадает с задержкой t_A_D. Чтобы не получить не рабочий код, решено было оставить эту задержку такой же.  
 + 
 +Для расчета задержек в функцию MDR_OTP_ProgBegin_ByClockCPU_Hz() передается текущая частота процессора,​ которая определяется функцией MDR_CPU_GetFreqHz(). Важно лишь в MDR_ConfigVE8.h прописать правильные частоты для подключенных внешних HSE0 и HSE1.  
 + 
 +В качестве альтернативы,​ расчет задержек можно сделать отдельной функцией MDR_OTP_GetProgDelays() и получить из нее структуру с задержками,​ которая затем подается на вход MDR_OTP_ProgBegin(). Все тоже самое делает инлайн функция MDR_OTP_ProgBegin_ByClockCPU_Hz(),​ чтобы не писать это отдельно. 
 + 
 +<​code>​ 
 +  #define MDR_OTP_DELAY_US_HV_PE ​  ​10000 
 +  #define MDR_OTP_DELAY_US_A_D ​      300 
 +  #define MDR_OTP_DELAY_US_PROG ​    ​3000 
 + 
 +  typedef struct { 
 +    uint32_t delay_HV_PE;​ 
 +    uint32_t delay_A_D;​ 
 +    uint32_t delay_Prog;​ 
 +  } MDR_OTP_ProgDelays;​ 
 +   
 +  //  Вычисление задержек 
 +  MDR_OTP_ProgDelays ​  ​MDR_OTP_GetProgDelays(uint32_t freqCPU_Hz); ​  
 +   
 +  //  Инициализация программирования - снижение DUcc, выставление Tuning 
 +  void MDR_OTP_ProgBegin(const MDR_OTP_ProgDelays *progDelays);​ 
 + 
 +  //  Все описанное вместе 
 +  __STATIC_INLINE void MDR_OTP_ProgBegin_ByClockCPU_Hz(uint32_t freqCPU_Hz)  
 +  {  
 +    MDR_OTP_ProgDelays delays = MDR_OTP_GetProgDelays(freqCPU_Hz);​ 
 +    MDR_OTP_ProgBegin(&​delays);​  
 +  } 
 +</​code>​ 
 + 
 +Задержки рассчитываются под функцию MDR_Delay(),​ реализация которой была описана ранее в данной статье - [[https://​startmilandr.ru/​doku.php/​prog:​pack_v6:​delayasm|"​Функция задержки и особенности её реализации в ассемблере"​]]. 
 + 
 +Значения в таблице определяют минимальные величины задержек,​ следовательно,​ их важно не сделать короче. Самая важная задержка - это время программирования. Она единственная,​ для которой определено максимальное значение. Это необходимо для того, чтобы ограничить время воздействия на ячейку повышенного напряжения. Иначе, вероятно,​ может случиться деградация структур в топологии,​ тогда могут пострадать и соседние ячейки. Другие же задержки лишь означают,​ что следующий по диаграмме сигнал необходимо выставить не раньше,​ чем к этому будет готов контроллер памяти. 
 + 
 +Для того чтобы проверить,​ как отрабатываются задержки данной функцией в проекте,​ используется кнопка KEY3. При нажатии на данную кнопку,​ на светодиоде LED3 начинают переключатся уровни напряжений с заданными задержками. Вот, например,​ какие возникают задержки при тактировании от HSI со значением Trim по умолчанию:​ 
 + 
 +{{mk8:​otp_delays_hsi.png}} 
 + 
 +Как видно, данные задержки превышают минимальные значения,​ т.е. по-существу,​ являются допустимыми. Время программирования t_Prog по спецификации составляет от 3 до 7мс. Что тоже не нарушается при таких задержках. Поэтому при одном из запусков проекта прошивались ячейки при тактировании от HSI без подстройки. Для полного соответствия проводилась прошивка и с подстроенным HSI, задержки тут имеют более точные значения:​ 
 + 
 +{{mk8:​otp_delays_hsi_trim.png}} 
 + 
 +После вторичного нажатия на кнопку KEY3 режим вывода задержек отключается. 
 +====Запуск проекта - предварительные проверки==== 
 +Для запуска проекта к демо-плате необходимо подключить источник питания на 7,2В  - это середина из разрешенного диапазона 7,0-7,4В. В нашем случае это был источник,​ непосредственная подача питания от которого осуществляется по нажатию кнопки. Согласно порядку подачи напряжений,​ сначала необходимо запитать плату от рабочего источника,​ а уже затем, когда дело дойдет до прошивки памяти,​ подать на плату напряжение программирования. Порядок снятия напряжений обратный - сначала необходимо отключить источник на 7,2В, потом выключить плату. 
 + 
 +Для подачи напряжения программирования на плате есть специальный разъем HV: 
 + 
 +{{mk8:​otp_write_board_min.jpg}} 
 + 
 +Перед прошивкой значений в память,​ для уверенности,​ можно проверить как отрабатывают в функциях такие операции как установление минимального питания DUcc и выдержка задержек. 
 + 
 +===Проверка снижения питания (Опционально)=== 
 +Прежде чем подать на плату повышенное напряжение,​ можно убедиться,​ что функция MDR_OTP_ProgBegin_ByClockCPU_Hz() действительно понижает напряжение **DUcc**. Для этого необходимо //(см. код ниже)//:​ 
 +  - Закомментировать функцию программирования MDR_OTP_ProgWord(),​ а до и после MDR_OTP_ProgBegin_ByClockCPU_Hz() поставить breakpoint-ы. 
 +  - Проект запускается из под ОЗУ, поэтому,​ запустившись с отладчиком,​ нажимаем F5 чтобы начать исполнение. 
 +  - Нажимаем KEY1 на плате и попадаем на первый брейк-поинт - перед MDR_OTP_ProgBegin_ByClockCPU_Hz(). Замеряем значение DUcc на любом выводе DUcc на плате //(см. фото)//​. Напряжение должно быть порядка 1,8В. 
 +  - Нажимаем на F5 и попадаем на второй бейк-поинт. Снова замеряем напряжение,​ оно должно понизиться. В нашем тесте оно составило 1,67В. 
 +  - //​(Функция программирования MDR_OTP_ProgWord() закомментирована - не исполняется.)//​ 
 +  - Нажимаем F10 чтобы выполнить MDR_OTP_ProgEnd(). Проверяем,​ что напряжение вернулось в исходное значение. 
 +  - //​(Раскомментируем функцию MDR_OTP_ProgWord(),​ чтобы в дальнейшем программирование работало.)//​ 
 + 
 +<​code>​ 
 +void OPT_WriteTestData(void) 
 +
 +  MDR_OTP_Enable(); ​ // Breakpoint сюда, чтобы увидеть начальный уровень DUcc 
 +  MDR_OTP_ProgBegin_ByClockCPU_Hz(freqCPU_Hz);​ 
 +//  MDR_OTP_ProgWord(OTP_TEST_PROG_ADDR,​ OTP_TEST_PROG_DATA);​ - отключаем программирование 
 +  MDR_OTP_ProgEnd(); ​ // Breakpoint сюда, чтобы увидеть снижение DUcc 
 +  MDR_OTP_Disable();​  
 +
 +</​code>​ 
 + 
 +===Проверка выдержки задержек=== 
 +Код библиотеки может меняться,​ могут поменяться функции настройки частоты,​ или компилятор сформирует прошивку несколько по другому. Все это может привести к тому, что данный проект перестанет отрабатывать так, как он работает сейчас,​ при написании данной статьи. **Поэтому для каждого источника тактирования следует проверить,​ что задержки формируются правильно!** 
 + 
 +В проекте проверяется прошивка при тактировании от источников:​ 
 +  * HSI 8МГц,  
 +  * HSE0 резонатор 10МГц, штатный на демо-плате 
 +  * HSE1 генератор на 25МГц - подключается отдельно кабелем через разъем SMA, //(см. фото)//​. 
 + 
 +Выбор частоты для которой собирается проект делается условной компиляцией. Необходимо чтобы был выбран только один вариант из: 
 + 
 +<​code>​ 
 +#define OTP_PROG_WITH_HSE0_MAX_CLOCK ​ 1 
 +#define OTP_PROG_WITH_HSI ​            0 
 +#define OTP_PROG_WITH_HSE1_GEN_25MHZ ​ 0 
 +</​code>​ 
 + 
 +Библиотека должна знать, какие частоты подключены на HSE0 и HSE1, ведь снаружи могут быть подключены источники и с другими частотами. Значения внешних частот должны быть указаны в файле MDR_ConfigVE8.h. Это необходимо чтобы задержки из реальных величин времени пересчитывались в циклы задержки функции MDR_Delay() правильно - [[https://​startmilandr.ru/​doku.php/​prog:​pack_v6:​delayasm|MDR_Delay - Функция задержки и особенности её реализации в ассемблере]]. 
 + 
 +<​code>​ 
 +MDR_ConfigVE8.h 
 +  //​================ ​ Параметры источников частоты ================ 
 +  //  Internal Generators 
 +  #define HSI_FREQ_HZ ​      ​8000000UL 
 + 
 +  //  External Generators 
 +  #define HSE0_FREQ_HZ ​     10000000UL 
 +  ... 
 +  #define HSE1_FREQ_HZ ​     25000000UL 
 +  ... 
 +</​code>​ 
 + 
 +Чтобы измерить задержки:​ 
 +  - Запускаем отладку,​ нажимаем F5 для запуска исполнения. При этом начнет мигать LED1 (VD7).  
 +  - Нажимаем Key3 и видим, что начинает тускло гореть светодиод LED3 (VD9). Это говорит о том, что переключается сигнал на входе управляющем данным светодиодом. Уровни сигнала выдерживаются согласно трем задержкам.  
 +  - Измеряем сигнал на выводе плюс данного светодиода - должны получиться картинки,​ как те, что были представлены в предыдущем разделе. Измеряем задержки аналогично. Если задержки неправильные,​ ищем, в чем проблема - переходить к прошивке с неправильными задержками нельзя. 
 +  - Снова нажимаем на Key3, программа выходит из режима отображения задержек. 
 + 
 +Если задержки получились неправильные необходимо проверить правильно ли указана частота в MDR_ConfigVE8.h и такая ли частота подана снаружи. Если все правильно,​ то необходимо проверить пересчет задержек для MDR_Delay() и значения вроде DELAY_LOOP_CYCLES_ASM. Можно перейти на вариант задержек через DWT - см. [[https://​startmilandr.ru/​doku.php/​prog:​pack_v6:​delayasm|MDR_Delay - Функция задержки и особенности её реализации в ассемблере]]. 
 + 
 +Если задержки правильные,​ то можно переходить в программированию ячейки ОТР. 
 +====Запуск проекта - Прошивка==== 
 +Для прошивки ячейки памяти в проекте предлагается такой алгоритм действий:​ 
 +  - Включить плату. 
 +  - Войти в режим отладки,​ запускаем по F5. Начинает мигать LED1 (VD7), показывая,​ что код main исполняется. 
 +  - Нажать KEY2 (Read), при этом должен загореться светодиод LED3 (VD9) Error. Потому что ячейка памяти еще не прописана и содержит пустое значение 0х00000000. 
 +  - Теперь включить подачу напряжения программирования - 7,2В. 
 +  - Нажать KEY1 (Write) - загораются все три светодиода. Меньше чем через секунду светодиоды погаснут - программирование закончилось. 
 +  - Нажать KEY2, чтобы убедиться,​ что значение прописалось. Если значение правильное,​ то загорится LED2 (OK), если данные не совпали - LED3 (Error).  
 +  - Если данные не совпали,​ горит LED3, то повторяем запись - нажимаем KEY1. Также можно посмотреть отладчиком, ​ насколько отличаются данные,​ какие биты не прописались. 
 +  - Отключить источник 7,2В. 
 +  - Выйти из режима отладки. 
 +  - Отключить питание платы. Выжидаем некоторое время, чтобы МК полностью сбросился. 
 +  - Включить снова плату, войти в режим отладки,​ нажать KEY2 - убедиться,​ что память содержит значение в нее записанное - должен гореть LED2 (OK). 
 + 
 +При запусках проекта мы не столкнулись с ошибками при записи,​ память прописывалась с первого раза, на какой бы частоте мы ее не прописывали. Но в данном проекте нет как такового этапа верификации. Память здесь читается через регистровый доступ чтобы не было выхода в HardFault из-за ошибок ЕСС, при этом она фактически читается не при 25МГц и не через необходимые шины. Поэтому в данном примере качество "​прожига"​ оценить нельзя. 
 + 
 +Для прошивки следующего слова необходимо увеличить OTP_TEST_TEST_IND и можно поменять слово данных для разнообразия.  
 + 
 +Не стоит пытаться дописать новые биты "​1"​ в эту же ячейку памяти,​ потому что для нового значения слова изменится и ЕСС, для которого скорее всего потребуется изменить уже прописанный "​0"​ на "​1",​ что невозможно. Но с другой стороны,​ если дописываемый бит будет всего один, то это даст сбой по единичной ошибке ЕСС, которая будет аппаратно парирована в случае чтения данного слова по шине. Это можно использовать для проверки того, как отрабатывает контроллер ошибок **FT_CNTRL**,​ как генерируются события ошибки,​ прерывания и прочее. 
 + 
 +=====Выводы===== 
 +Запуск проекта показал,​ что: 
 +  - Функции работы с OTP 1986ВЕ8Т драйвера MDR_OTP отрабатывают правильно.  
 +  - Прошивать память можно на любой частоте,​ необходимо лишь выдержать правильно задержки. //(Но для верификации необходима все-таки частота 25МГц! Верификация в данном примере не рассматривалась.)//​ 
 + 
 +Следующим шагом будет написать на основе функций драйвера MDR_OTP несколько FLM и прошить какой-нибудь проект,​ тоже куда-нибудь в конец памяти - статья,​ [[prog:​example_v6:​OTP_FLM_VE8|"​OTP_FLM_VE8 - Реализация FLM для OTP в 1986ВЕ8Т"​]] 
 + 
 +Ссылка на проект - [[https://​github.com/​StartMilandr/​MDR_Pack_v6/​tree/​master/​PACK_Gen/​Files/​Examples/​All_Boards/​OTP/​OTP_Test_VE8|GitHub]]
prog/example_v6/otp_test_ve8.1570713330.txt.gz · Последние изменения: 2019/10/10 17:15 — vasco