nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

"Часы реального времени" для QuatCore

На мой приборчик должны каждые 100 мс прибывать сообщения "синхронизации со словом данных" по МКО (Mil-Std 1553), в которых будет передаваться число от 0 до 99, "номер текущего вычислительного такта". Просто по возрастанию, и после 99 последует 0. Тем самым, мы сможем обозначить время в интервале 10 секунд.

Получая кадр изображения, мы должны будем зафиксировать метку времени, когда он был получен (скорее даже "середину экспозиции", т.е какому моменту будут соответствовать те параметры сближения, что мы получили). По протоколу, это будет 16-битное число в формате UQ7.9, то есть беззнаковое с фиксированной запятой. Старшие 7 бит - это тот самый "номер вычислительного такта", от 0 до 99, но благодаря младшим битам можно будет отметить момент времени с точностью до ≈ 200 мкс, этого вполне достаточно, учитывая, что просто на считывания кадра с матричного фотоприёмника 1205ХВ014 будет уходить 10 мс, это при ПЛИС, "разогнанной" до 50 МГц (пока у меня 25 МГц), ну может 5 мс, если дойти до 100 МГц, и это уже предельное значение для этого фотоприёмника.

Вот думаю сделать отдельный модулёк, который будет вести счёт времени в этих 16 битах (используя обычную тактовую частоту), синхронизироваться по слову данных, а также отмечать момент получения кадра.


Вот заголовок модуля:

module QuatCoreRTC (input clk, input sync, input [15:0] D, input Mark, output reg [15:0] Q = 1'b0);



Когда sync=1, старшие 7 бит счётчика устанавливаются в значение, переданное по входу D, а младшие 9 (и ещё более младшие, "не выходящие наружу") обнуляются. Когда Mark=1, текущая метка времени "защёлкивается" на выход Q.

Такого интерфейса пока должно хватить.

И сразу можно написать самую простую часть:
	wire [15:0] Time;
	wire LowCounterCout;
	wire ce_195us;
							
	lpm_counter HighCounter (
				.clock (clk),
				.cnt_en (LowCounterCout & ce_195us),
				.data (D[6:0]),
				.sload (sync),
				.q (Time[15:9]) );
	defparam
		HighCounter.lpm_direction = "UP",
		HighCounter.lpm_port_updown = "PORT_UNUSED",
		HighCounter.lpm_type = "LPM_COUNTER",
		HighCounter.lpm_width = 7;
		
	lpm_counter LowCounter(
				.clock (clk),
				.cnt_en (ce_195us),
				.sclr(sync),
				.q (Time[8:0]),
				.cout (LowCounterCout));
						
	defparam
		LowCounter.lpm_direction = "UP",
		LowCounter.lpm_port_updown = "PORT_UNUSED",
		LowCounter.lpm_type = "LPM_COUNTER",
		LowCounter.lpm_width = 9;
		
	always @(posedge clk) if (Mark)
		Q <= Time;	


Младший счётчик по приходу sync обнуляется, старший заносит значение. Младший ведёт счёт, когда приходит ce_195us=1, а старший - когда ещё и младший досчитал до своего максимального значения 511 и по следующему ce_195us обнулится.

Ну а на выход это значение защёлкивается только по Mark=1.

Осталось за малым: сформировать сигнал ce_195us. Когда мы возились с UART, мы просто делили тактовую частоту на ближайшее целое, чтобы получить скорость передачи, похожую на заявленную. Здесь мы могли бы сделать также, поделить свои 25 МГц на 5,12 кГц, ближайшим целым выступит 4883. И в действительности вместо 5,12 кГц у нас будет 5,1198 кГц. За те 100 мс, что проходят между импульсами синхронизации, мы ошибёмся аж на 4 мкс, это меньше младшего разряда, так что пущай так и будет. как всё просто оказалось. Вообще, я уже готовился делать более хитрый алгоритм, как в делим отрезок на равные части. Впрочем, при таком коэффициенте, 4883, и при нестабильности тактовой частоты в 0,25%, особого смысла в этом нет.

Что ж, домучиваем это дело:
	lpm_counter Divider (
				.clock (clk),
				.cnt_en (1'b1),
				.sclr (sync),
				.sset (ce_195us),
				.cout (ce_195us) );
	defparam
		Divider.lpm_direction = "UP",
		Divider.lpm_port_updown = "PORT_UNUSED",
		Divider.lpm_type = "LPM_COUNTER",
		Divider.lpm_width = 13,
		Divider.lpm_svalue = 3309;


Решил попробовать прямо так, если по таймингам всё хорошо - то и ладно. И действительно, предельная частота, на которой такой модуль способен работать: 75 МГц, нормально.

Вот модуль целиком:
module QuatCoreRTC (input clk, input sync, input [15:0] D, input Mark, output reg [15:0] Q = 1'b0);

	
	wire [15:0] Time;
	wire LowCounterCout;
	wire ce_195us;
							
	lpm_counter HighCounter (
							.clock (clk),
							.cnt_en (LowCounterCout & ce_195us),
							.data (D[6:0]),
							.sload (sync),
							.q (Time[15:9]) );
	defparam
		HighCounter.lpm_direction = "UP",
		HighCounter.lpm_port_updown = "PORT_UNUSED",
		HighCounter.lpm_type = "LPM_COUNTER",
		HighCounter.lpm_width = 7;
		
	lpm_counter LowCounter(
						.clock (clk),
						.cnt_en (ce_195us),
						.sclr(sync),
						.q (Time[8:0]),
						.cout (LowCounterCout));
						
	defparam
		LowCounter.lpm_direction = "UP",
		LowCounter.lpm_port_updown = "PORT_UNUSED",
		LowCounter.lpm_type = "LPM_COUNTER",
		LowCounter.lpm_width = 9;
		
	always @(posedge clk) if (Mark)
		Q <= Time;		
		
	lpm_counter Divider (
						.clock (clk),
						.cnt_en (1'b1),
						.sclr (sync),
						.sset (ce_195us),
						.cout (ce_195us) );
	defparam
		Divider.lpm_direction = "UP",
		Divider.lpm_port_updown = "PORT_UNUSED",
		Divider.lpm_type = "LPM_COUNTER",
		Divider.lpm_width = 13,
		Divider.lpm_svalue = 3309;

endmodule


Синтезируется в 51 ЛЭ, из которых 46 содержат регистры (т.е меньше 46 сделать невозможно).

Похоже на правду:


Правда, переход с 0x17 на 0x18 должен был произойти через 4,68 мс от начала, а не 4,82 мс, т.е затянулось на лишние 132 мкс. Но я знаю почему: чтобы упростить делитель частоты, я заставляю его считать от 3309 до 8192, после чего снова устанавливается 3309, и так далее. Но при самом первом его включении он считает с нуля, тем самым добавив 3309 такта по 40 нс = 132,36 мкс. "Как в аптеке".


Как именно его "прикрутить" к остальному проекту - ближе к делу посмотрим. Не хочется из-за такой мелочи расширять мультиплексор шины данных QuatCore, он и так здоровенный. Лучше бы ближе к информационному обмену воткнуть...
Tags: ПЛИС, работа, странные девайсы
Subscribe

Recent Posts from This Journal

  • Так ли страшно 0,99969 вместо 1?

    В размышлениях о DMA, о возможных последствиях "смешивания" старых и новых значений при выдаче целевой информации, опять выполз вопрос: насколько…

  • Как продлить агонию велотрансмиссии на 1500+ км

    Последний раз о велосипедных делах отчитывался в середине мая, когда прошёл год "велопробегом по коронавирусу". Уже тогда я "жаловался", что…

  • DMA для QuatCore

    Вот фрагмент схемы нашего "процессорного ядра" QuatCore: Справа сверху "притаилась" оперативная память. На той ПЛИС, что у меня есть сейчас…

  • "МКО через UART" в железе - 2

    Продолжим проверять этот модулёк. "Для закрепления пройденного", снова запрашиваем телеметрию, сначала 1 слово данных (командное слово 0x3701, и…

  • "МКО через UART" в железе

    Долго мучали эту штуковину на симуляции, пора уже в макет это прошить и посмотреть, как оно себя поведёт. Оставлю "основную схему" (процессор, SPI,…

  • "Ошибка на единицу" при передаче CRC

    В прошлый раз обнаружили нехорошую вещь: при передаче данных от нашего устройства, CRC начинает работать одним словом раньше, чем надо, и результат…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 4 comments