nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

QuatCore: торжественно отключаем Ethernet!

В июне 2019 года мы уже торжественно отключали Ethernet-контроллер, который стоит на макетной плате и используется пока что только как генератор тактовой частоты. Тогда использовался специализированный модуль, который передавал 3 сообщения, два из них по 4 байта, третье - в 3 байта. Оказывается, он вообще не использовал блок встроенной памяти (БВП, он же EAB), 15 байт кодировались с помощью логических элементов.

Тем не менее, мне хотелось попробовать сделать то же самое через QuatCore, поскольку уж инициализацию точно можно отдать процессору, он один раз её проведёт - а дальше может заниматься своими делами.

Получилась программка аж в 8 строк, но потом целый день её отлаживал. Сам до конца не понял, где был глюк, мне вообще кажется, что это в квартусе какой-то модуль не захотел повторно синтезироваться, хотя был изменён. И похоже, осциллограф ZET302 меня подвёл, хотя могу и ошибаться, нужны дополнительные исследования...




"Аппаратная часть" (та, что на verilog), уже была готова, см SPI на 3 устройства, оставалось написать программку. Вот она:

;посылаем в Ethernet-контроллер набор команд,
;чтобы максимально выключить все его устройства
;и главное, сменить тактовую частоту
%include "QuatCoreConsts.inc"
.rodata
	EthDisable	dw	2,3,0x22,0x54,0x00,0x01,3,0x22,0x66,0x00,0x18,2,0x22,0x6F,0x0F
.data

.code
	main proc
			SP		EthDisable
			SIO		ETH				
			j		[SP++]	;количество посылок (счёт с нуля)
	@@EthWord:	k		[SP++]	;количество байт в посылке (счёт с нуля)
	@@EthByte:	OUT		[SP++]
			kLOOP		@@EthByte
			;нужно, чтобы nCS сбросилось в единицу
			k		IN		;лишние регистры не "засоряем", хотя пофиг скорее всего
			jLOOP		@@EthWord
	@@endless:	JMP		@@endless			
	main endp


Опять применяем SP "не по назначению", но здесь это вообще не криминал. Настройка тактовой частоты может быть ещё более первостепенной задачей, чем настроить стек, так что сейчас настроим - и потом выставим SP куда надо :)

В "строке" EthDisable закодировано, как мы должны посылать байтики в Ethernet-контроллер:
первое число, 2 - количество сообщений, которые мы хотим отправить, отсчёт с нуля. Т.е "2" означает - ТРИ сообщения;
второе число, 3 - количество байт в очередном сообщении, также отсчёт с нуля, т.е ЧЕТЫРЕ байта.
Далее идут те самые 4 байта, а за ними - количество байт в очередном сообщении, и так далее.

Формат общения с Ethernet-контроллером таков, что ему очень важно, есть ли между отдельными байтами переход nCS из нуля в единицу и назад, или нет. Если мы не передали полное сообщение, но nCS установился в единицу, то все переданные биты игнорируются, сообщение считается неверным. А если, напротив, не установили nCS в единицу между сообщениями, то контроллер ошибочно решит, что мы продолжаем предыдущую команду, у него нет никаких других способов отличить одно от другого, окромя nCS.

Именно поэтому во внутреннем цикле мы просто передаём байт за байтом, и это получается "бесшовно" - как только мы "забросили" байт в SPI, процессор "отпускает", он идёт дальше, выходит на очередную итерацию и потом ждёт, когда же предыдущий байт отправится, чтобы тут же "забросить" туда ещё один.

А чтобы такое же поведение не повторилось во внешнем цикле, мы применяем команду IN - теперь уже процессор ждёт окончания передачи (и по совместительству, приёма), чтобы полученный байт отправить куда попросят. И если следующей же строчкой мы не запускаем OUT, то nCS установится-таки в единицу. Впрочем, так получается только если частота SPI относительно высока. Скажем, на частоте процессора 4 МГц и частоте SPI в 1 МГц всё работает именно так. А вот если снизить частоту SPI до 500 кГц, то пауза между сообщениями пропадает, nCS так и не успевает установиться в единицу. Если нам нужно работать на более низкой частоте, надо добавить ещё какую-то задержку, может даже FMA (Fused Multiply-Add), занимающий 18 тактов.

Программа успешно откомпилировалась, весь проект синтезирован и "прошит" в ПЛИС. Светодиоды показали, что мы успешно пришли в PC=8 и там остановились. Но вот тактовая частота так и не поменялась - не принял Ethernet-контроллер наши посылки...

Для начала я посмотрел, а что же там вообще передаётся:


Всё правильно передаётся - те самые 3 сообщения, первые два - по 4 байта, третье - в 3 байта. Между ними паузы в 1 такт SCK, что даёт в общей сложности 90 тактов SCK, или 90 мкс - ровно так и есть!

И байты все правильные. Первая посылка: 0x22 54 00 01 - записать в регистр MIREGADR адрес регистра PHCON1.
Вторая посылка: 0x22 66 00 18 - записать в регистр MIWR данные, которые мы хотим записать в регистр PHCON1. Как только эта команда будет получена, контроллер обратится к своей "физической части" по интерфейсу MIIM (Media Independent Interface Management). Всё, что мы хотим - чтобы физическая часть пошла поспать, а то жрёт 40 мА по чём зря :)

Наконец, третья посылка: 0x22 6F 02 (на этой осциллограмме) - записать значение 02 в старший байт регистра ECON2 - это отключит весь ethernet-контроллер, а последние 4 бита задают тактовую частоту, которую он выдаёт на CLKOUT. Можно выбрать 50 кГц, 100 кГц, 3,125 МГц, 4 МГц, 5 МГц, 6,25 МГц, 8 МГц, 8,333 МГц, 10 МГц, 12,5 МГц, 16,67 МГц, 20 МГц, 25 МГц, 33,33 МГц, или вовсе отключить выдачу тактовой частоты. В тексте программы сейчас указан байт 0F - это установит 50 кГц. Если 02, как в осциллограмме, то будет 25 МГц. Именно 25 МГц нам будут удобны для оцифровки видеосигнала.

Вроде всё как надо, а не работает...

С чем-то подобным я сталкивался и раньше, летом 2019 года. Вот тайминги SPI для Ethernet-контроллера:


В прошлый раз я разработал довольно-таки хитрючий модуль, который выдавал такие сигналы:


здесь всё происходит очень "аккуратно" - сначала nCS устанавливается в нолик, потом, через такт, запускается SCK, а по окончании передачи останавливается SCK, а потом nCS устанавливается в единичку.

Но наш новый модуль SPI куда проще - он ВСЕГДА выдаёт тактовую частоту SCK (того хотят SD-карточки, им нужно 74 такта в начале работы и 8 тактов после каждой посылки), и сигналы выглядят вот так в начале посылки:


и так в конце посылки:


как мы видим, в начале все тайминги соблюдаются: интервал между спадом nCS и фронтом SCK составляет один половину такта SCK, в нашем случае 500 нс. Контроллеру нужно свыше 50 нс - укладываемся...

Время установки данных и их удержания (по 10 нс) - тоже выдерживается, у нас данные устанавливаются опять же за полтакта SCK (500 нс) до фронта SCK, и удерживаются ещё столько же после фронта (отсюда надо ещё вычесть полтакта, а то и почти такт процессора, но это погоды не сделает). Отлично!

А вот интервал между спадом SCK и фронтом nCS (по окончании передачи) НАРУШЕН ГРУБО! По сути, эти два события происходят одновременно!

Именно в этом, скорее всего, и состояла проблема. Ethernet-контроллер очень мнительный - он не предпринимает никаких действий, прежде чем не получит всю посылку целиком и убедится, что она пришла "в целости и сохранности". Если nCS устанавливается в единицу так рано, он, видимо, считает, что посылка пришла "не полностью" - и игнорирует всё её содержимое.

Самым простым способом исправить это мне показалось сдвинуть nCS на один такт вперёд. Я это сделал в модуле SPI_Mux3:

//Device 	=	0x: ETH
//		=	10: SD
//		=	11: ADC
module SPI_Mux3 (input clk, input nCS, input [1:0] Device, output MISO_MUX, output SD_nCS, output ADC_nCS, output ETH_nCS,
					inout SD_MISO, inout ADC_MISO, inout ETH_MISO);

assign SD_nCS = nCS | ~(Device == 2'b10);
assign ADC_nCS = nCS | ~(Device == 2'b11);

reg rETH_nCS = 1'b1;
assign ETH_nCS = rETH_nCS;
always @(posedge clk)
	rETH_nCS <= nCS | Device[1];
//assign ETH_nCS = nCS | Device[1];
//Нужно обеспечить 50 нс между спадом и nCS и фронтом SCK (это у нас и так есть)
//а также 50 нс между спадом SCK и фронтом nCS (этого не было)
//этим регистром мы из 500 нс / 0 нс получаем 250 нс / 250 нс. 

assign SD_MISO = SD_nCS? 1'b1 : 1'bz;
assign ADC_MISO = ADC_nCS? 1'b1 : 1'bz;
assign ETH_MISO = ETH_nCS? 1'b1 : 1'bz;

assign MISO_MUX = 	~Device[1]?	ETH_MISO :
			Device[0]?	ADC_MISO :
					SD_MISO;
endmodule


Симулятор показал, что всё будет хорошо:



Но "в железе" всё равно ничего не заработало.

Чтобы понять, в чём же дело, я начал тыкаться осциллографом и увидел совершенно безумную вещь:


Мало того, что фронт nCS совпадает с фронтом SCK (то, чего мы пытались избежать), так ещё и спад nCS совпадает с фронтом SCK, что также способно нам создать проблемы - контроллер может уже воспринять фронт, а может не воспринять, и будет всё на один бит плавать.

После этого началась "глубокая отладка" - пристально смотрел на всё это безобразие, пробовал где-то чего-то переправлять, но особых успехов не имел.

В какой-то момент заметил странную вещь: если синхронизация осциллографа настроена "по фронту", то видим картинку как выше. Но если выставить "по спаду", то получим такое:


Но извините, это совсем другое - теперь оба перехода nCS осуществляются по СПАДУ SCK, А НЕ ПО ФРОНТУ. Причём картинка стоит очень устойчивая - не то, чтобы её трясло из стороны в сторону. И на других масштабах происходит та же фигня:



Тут, если развернуть на полный экран, можно увидеть, что переходы происходят ПО ФРОНТУ.

А если сделать синхронизацию по спаду ("Фронт: убывающ"), то вроде бы ТА ЖЕ САМАЯ КАРТИНКА:

но теперь nCS переключается ПО СПАДУ.

Это самая длинная развёртка, при которой кнопочки "Сбор данных" и "Усреднения" подсвечены серым, т.е для данного режима недоступны. На более длинных эти кнопки становятся доступными - можно выбрать "Усредн", "Пиковый" или "Выборка", а также количество точек для усреднения.

Но на более длинной развёртке разглядеть, где именно переключается nCS, уже невозможно.

У меня такое ощущение, что на малых временах развёртке этот осциллограф предпринимает какие-то ухищрения, чтобы заполучить хорошую картинку, и в итоге умудряется напрочь рассинхронизировать два канала! Или скорее каждый из них синхронизирует по отдельности, потому что я честно говоря был удивлён такому совпадению - даже когда на экран умещается один-единственный такт, фронты идеально оказываются один под другим!

Но может, дело в чём-то другом, я уже не раз прокалывался с "крышесносящими теориями", как с вольтдобавкой в выходных каскадах SD-карточки :) Возможно, всё объясняется как-то проще, не знаю.

А потом, когда я заподозрил осциллограф, то ещё разок залил ту, "исходную" схему, и ВНЕЗАПНО всё заработало как часы - тактовая частота, выдаваемая Ethernet-контроллером поменялась на 25 МГц, "как и просили".

Почему она с первого же раза не заработала - не знаю. Опять же, "завиральная идея" в том, что хитрый квартус понял, что на выходе nCS стоит обычная "защёлка" без дополнительных входов (разрешение, синхронный/асинхронный сброс/установка и кто знает, что ещё) и решил перенести её в буфер ввода-вывода (там именно такая обычная "защёлка" и стоит). И где-то поставил "галочку" куда-то дальше, возможно уже для "ассемблера" (который формирует бинарник для прошивки ПЛИС) - этот выход отметь со включённым буфером. А ассемблер решил - "ну так ничего не поменялось, выводы всё те же самые, я воспользуюсь уже откомпиленным файликом" - и в итоге эту защёлку не добавил. Помнится, в Xilinx такое было сплошь и рядом, нас ещё препод учил - если не работает, первым делом почистите все-все-все временные файлы, а то заполучите невероятный конфликт версий! Не знаю, в этом ли дело, но ещё полдня жизни потерял. Надеюсь, впрок...

Потом ещё переправил программу, чтобы переключила тактовую частоту на 50 кГц. После этого на осциллографе увидел вот что:


Ровно то, что мы и хотели, и что показывал симулятор - nCS переключается посередине интервала, где SCK=0, т.е обеспечивается хороший запас и в начале, и в конце работы. Да, на таком масштабе осциллограф показывает правильно :)

Tags: ПЛИС, Программки, странные девайсы
Subscribe

  • Хотел выпендриться

    Одно из замечаний к моему протоколу информационного обмена: ДОБАВЬ 16-битные заголовки к каждому сообщению! Нам могут прислать командное слово с…

  • Моделирование стыковки с помощью ВидеоИзмерителя

    "Нарисовал"-таки видеоролик, где мы начинаем с весьма неблагоприятных условий: вместо того, чтобы нас поставить ровно "напротив" стыковочного узла,…

  • Более тяжёлые тела падают быстрее!

    Увидел не так давно видео от Flammable Maths с таким заголовком, и подумал поначалу - он опять нас троллит. Это немецкий препод математики…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 3 comments