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

  • Так есть ли толк в ковариационной матрице?

    Задался этим вопросом применительно к своему прибору чуть более 2 недель назад. Рыл носом землю с попеременным успехом ( раз, два, три, четыре),…

  • Big Data, чтоб их ... (4)

    Наконец-то стряхнул пыль с компьютерной модели сближения, добавил в неё код, чтобы мы могли определить интересующие нас точки, и выписать…

  • Потёмкинская деревня - 2

    В ноябре 2020 года нужно было сделать скриншот несуществующей программы рабочего места под несуществующий прибор, чтобы добавить его в документацию.…

  • Великая Октябрьская резня бензопилой

    Сегодня прокатился прочистить Абрамцевскую просеку. Как обычно, с приключениями. Выезжал на велосипеде, а вернулся на самокате. Первый раз по этим…

  • Очередная несуразность в единицах измерения

    Когда-то я написал программу PhysUnitCalc - калькулятор, умеющий работать с размерностями. Мне казалось, что я наступил уже на все грабли, которые…

  • Big Data, чтоб их... (3)

    "В предыдущих сериях": мой прибор выдаёт 6 значений: 3 координаты и 3 угла, т.е все 6 степеней свободы твёрдого тела. Причём ошибки измерения этих 6…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 4 comments