Статья посвящена опыту участия в создании одного из модулей КА «Маяк», который создавался на протяжении полугода и в итоге, по организационным вопросам, наш модуль (бортовой компьютер + радио передатчик) не полетел… Чтобы работа большой команды не прошла бесследно — хочется отразить старания и результаты каждого из ее членов (на сколько это можно) в статье  🙂  .


О проекте

О «Маяке» можно подробнее прочитать тут, а красиво рассказывают о себе и собирают деньги на проект они тут. Главным руководителем и  идеологом проекта является Александр Шаенко. Интервью с ним по поводу проекта «Маяка» можно почитать на Geektime. Cам проект является студенческим, и его, якобы, реализуют студенты и энтузиасты.

До какого-то момента проект действительно поддерживался группой энтузиастов, которые свободное от работы время проводили с пользой для ума и российской космонавтики. Проект развивался неспешно, проводились промежуточные испытания. Но в ноябре-декабре 2015 года появилась информация о возможности запуска спутника уже в мае 2016 года. На тот момент у команды разработчиков был только пластиковый макет спутника, на котором не функционировала даже система раскрытия отражателя. Без этого функционально спутник представлял собой тот самый «кирпич», с которого началась идея создания и запуска спутника.

Идея проекта появилась почти случайно. Как-то после лекции Шаенко спросили, можно ли запустить в космос кирпич. «Кирпич? Можно, конечно. Только пользы от него никакой, его даже никто не увидит. Интереснее будет запустить что-то, что заметят с Земли», — ответил инженер. После нескольких дней размышлений и набросков были готовы первые чертежи.

openrussia.org

Понимая, что качественно и в срок сделать спутник до момента запуска не получается, Александру всё-таки пришлось сотрудничать с профессионалами. Проект является некоммерческим, поэтому средствами для оплаты труда инженеров команда не располагала. В отместку, они могли предложить возможность размещения другого оборудования на КА.

Нашей компании нужно было переделать их систему раскрытия отражателя. Также, в качестве отладки и проверки работы систем, которые были разработаны нами до этого — предлагалось дополнить систему «Маяка» нашим модулем. Именно о нем и пойдет повествование статье.

 


Предисловие

На момент участия в проекте я учился на последнем курсе университета и активно совмещал учебу с работой. Работал на тот момент в довольно перспективной (насколько может быть перспективна частная российская космонавтика) компании, поставившей себе в цель решение коммуникационных проблем на планете — покрытие малонаселенной и труднодоступной местности спутниковой телефонией и интернетом.

В компании была команда программистов, в которую я и входил и был, естественно, Junior’ом. Непосредственно моей сферой обязанностей было программирование FPGA, создание DSP модулей.


Команда

%d0%bc%d0%b5%d1%80%d0%ba%d1%83%d1%88%d0%b5%d0%b2

Федор Меркушев — система раскрытия паруса. Важной и, по-сути, главной технической частью проекта «Маяка» — был светоотражающий парус, который должен был раскрыться на низкой орбите и отражать солнечный свет. Именно с ней у «энтузиастов Космоса» возникли проблемы. Федор решил эту проблему, о чем и свидетельствуют выложенные ниже видео …

 

%d1%81%d0%b8%d0%b4%d0%be%d1%80%d0%be%d0%b2

Евгений Сидоров — наладка работы КРН (комплекс радионавигации). Процессорная часть обработки данных от GPS спутников. Работал в паре с Вячеславом Применко. Областью работы Славы была FPGA реализация детектирования сигнала от спутников, разбор пакетов и отправка данных для последующей обработки на процессор.

 

 

 

%d0%b2%d0%be%d1%80%d0%be%d0%b6%d1%86%d0%be%d0%b2Михаил Ворожцов — реализация компилятора для софт процессора ACME32/ACMA32 (написаны Алексеем Чернышовым). Работал в паре с Димой Прыгуновым и Мишей Балахно, т.к. именно компилятором собирались прошивки, написанные Мишей Б.

 

 

 

%d1%87%d0%b5%d1%80%d0%bd%d1%8b%d1%88%d0%b5%d0%b2Алексей Чернышов — разработка и отладка софт-процессора. Им была создана и, в паре с Михаилом Балахно, отлажена работа софт — процессора, написанного под ПЛИС от Microsemi.

 

 

 

 


Задача команды

Работа команды началась примерно в октябре — ноябре 2015 после объявления директора о том, что мы участвуем в проекте. Основными задачами команды были :

Команда «Маяк-ПО» :

  1. Дописать и отладить софт-процессор (к октябрю он был написан, но не был отлажен),
  2. В тестовом режиме запустить и отладить КРН — как часть обнаружения, так и часть обработки данных на софт-процессоре,
  3. Сбор телеметрии с платы — данные с датчиков: температуры, тока и напряжения,
  4. Запуск и наладка блока КТС (командно-телеметрической связи) для отправки телеметрии на Землю,
  5. Полная отладка функциональных звеньев и сдача всего модуля в сборе

Команда «Маяк-Железо» :

  1. Выбор и согласование элементной базы для платы ( элементная база — industrial, т.к. спутник — низкоорбитальный),
  2. Создание Э3 (схемы электрической принципиальной) и разводка ПП,
  3. Запуск и наладка,
  4. Отдельно — система раскрытия паруса и система отстыковки от ракетоносителя

Предполагалось что две упомянутые выше команды будут работать согласованно, т.к. конечным продуктом являлась «прошитая» плата.


Работа

TimeLapse недельного отчета по проделанной работе в команде «Маяк-ПО». Собственно, на этом видео присутствуют практически все, кроме Миши Ворожцова и Алексея Чернышева. Даже на первых минутах засветился директор 🙂

На видео ниже можно увидеть модуль, над которым работала команда «Маяк-ПО» и «Маяк-Железо». Собственно это «котлетка» из двух плат — на верхней должны были располагаться датчики (на видео сверху — отладочная плата), плата снизу была с FPGA, датчиками, ЦАП, АЦП, синтезаторами частот, МШУ и усилителями выходного тракта. На видео — первые этапы отладки : включение, первоначальная прошивка FPGA для проверки основных частей. Плата — Артем Малахов.

 Тестовыми прошивками — считывание данных с термодатчиков, ЦАП, АЦП, синтезатором частот, AHB шиной для FSM КРН и децимирующим фильтром — занимался я 🙂 . На видео — Отладка ЦАП и синтезатора частот (настроен на 2.2 ГГц).

Демонстрация успехов работы Федора Меркушева.


Трудности

Во время разработки команде пришлось решить ряд сложных задач, в основном связанных с небольшим размером спутника и оставшегося свободного пространства для размещения электроники, антенн. Приоритетной, естественно, оставалась миссия с отражателем. Системы разрабатывались так, чтобы провал одной миссии не мог повлиять на другую, они были независимыми.  С одной лишь оговоркой: с раскруткой спутника по договоренности было решено повременить на одни сутки. Это связано с тем, что при вращении спутника, даже при наличии двух антенн и широкой диаграммой направленности, сеансы связи были бы слишком короткими, и не удалось бы передать на Землю приемлемое количество телеметрии.

Предполагалось сохранять и передавать на Землю следующие данные: уровень заряда аккумуляторов, скорость вращения КА, потребляемую мощность, показания температурных датчиков, координаты и скорость движения КА. Пакеты данных со штампами времени  от момента отстыковки должны были записываться во flash, затем передаться «вниз» в S диапазоне за один сеанс связи.

Единственным источником энергии спутника являются аккумуляторы, которые должны были по расчётам разрядиться в течение 2 суток. Учитывая это, а также мощность передатчика (несколько милливатт), спутник бы успешно разрядился и никому бы не помешал с точки зрения частотного диапазона.

В конечном счёте, за четыре месяца командой инженеров, было сделано всё не только для самой возможности запуска КА «Маяк», но и для того, чтобы эта миссия имела бОльший смысл и научный интерес. Ведь было бы установлено: с какой скоростью замедляется КА, с какой скоростью вращается после отделения, какие температурные воздействия испытывает.

В ходе выполнения работы пришлось отказаться от:

-приёмника на КА (изначально предполагалась полудуплексная связь),

-расчёта координат КА «на борту».  Вместо этого предлагалось собирать сырые данные с GPS спутников и отправлять их по КТС на Землю, где затем обрабатывать, и вычислять координаты (Low Energy GPS Positioning).

Основными трудностями с которыми, на мой взгляд, столкнулась наша команда ( «Маяк-ПО» ) была трудность отладки софт-процессора, так как из возможных инструментов был только Identify, который на небольшом промежутке времени потактово отображал работу софт-процессора. При этом невозможно было отследить работу процессора на большом промежутке времени.  Второй причиной стала отладка КРН в части FPGA. У Вячеслава целый месяц ушел на отладку коррелятора для поиска сигнала от спутников GPS. Организовать слежение за сигналом нескольких спутников оказалось сложнее, и решение задачи затянулось.

Также проблемы были с коммуникацией между двумя командами — Железа и ПО, с изготовлением антенны для КТС. В целом, на каждом участке workflow встречались некоторые «затыки».


О том, почему наш проект никуда не полетел

Как уже упоминалось выше — причиной стало изменение договоренностей между директором компании и руководителем проекта «Маяка». Собственно, на момент написания статьи до запуска спутника остается 58 дней — об этом гласит табличка на сайте проекта.

55days

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


Результаты работ

  1. Создана и полетит — система раскрытия паруса и система отстыковки,

  2. Создана и не полетит — отлаженная плата (имеется ввиду ее «железная часть», т.к. была проверена система работы датчиков тока и напряжения, температуры, ЦАПы и АЦП, синтезатор частот и выходной усилитель передающего тракта — имелась возможность на имеющемся оборудовании организовать сбор и передачу данных без использования софт-процессора),

  3. Создан, не до конца отлажен, не полетит — софт-процессор (хотя удачно заработал UART и FSM для КРН — последний отлаживался в симуляторе и не был отлажен в железе),

  4. Не доделан, не отлажен, не полетит — КРН (как вариант — предлагалось собирать сырые данные с GPS спутников и отправлять их по КТС на Землю, где затем обрабатывать),

  5. Не сделан — КТС (с ним повис вопрос, так как специалист, который должен был его делать, ушел из компании)


Итог

Исходя из всего написанного выше, было бы неправильно считать,  что проект КА «Маяк» является только студенческим или проектом энтузиастов.


Интересные факты

Пружины системы раскрытия – довольно сложный элемент для «кастомного» проектирования и изготовления, поэтому были использованы пружины от крепления лампы, купленные в хоз. магазине.

Из-за сжатых сроков разработки сотрудникам приходилось работать даже на больничном. Это было не обязательно, никто никого не заставлял. Общее воодушевление от причастности к такому интересному проекту, а также чувство ответственности перед другими сотрудниками затмевали личные планы.

Данная статья посвящена приемнику UART.  До этого две статьи были посвящены передатчику UART, и полной схеме, состоящей из приемника и передатчика[ F(clk) = 20 MHz, V(uart) = 115200 кБит/с ].

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

Здесь мы не будем рассматривать теоретическую часть работы UART, предполагается, что Вы с ней ознакомлены, если же нет, то теоретическая часть приводится здесь.


Как использовать модуль приемника

В модуле используются следующие порты:

clk — входной однобитный порт — подача тактового сигнала,
rx — входной однобитный вход — входные данные последовательного интерфейса,
data_o — выходные данные (8 бит),
data_rdy — выходной порт — сигнализирует о готовности и валидности данных,
have_prblm — выходной порт — сигнализирует что в блоке были найдены некоторые ошибки и возможны неточности


Код приемника … Verilog код

Здесь  сountOfStrobe — параметр, меняя который может регулировать информационную скорость нашего UART’а. В данном случае UART настроен на скорость 115200 кБит/с при тактовой частоте clk = 20 МГц.

сountOfStrobe = Fclk(Гц)/Vuart(бит/c) = 20*10^6 / 115200 = 173;

module uart_param_receiver #(
	parameter countOfStrobe = 173,  	// Параметр, который высчитывается из соотношения Fclk[Гц] / V[бит/c]
										// соответствует тому, сколько тактов приходится на прием одного 
										// бита данных при выбраной скорости UART(бит/с) и тактовой частоте CLK (Гц)
	parameter firstCheckStrobe = 99, 	// Первый check bit 
	parameter secondCheckStrobe = 120,  // Второй check bit
	parameter thirdCheckStrobe = 150    // Третий check bit 
							)(
	input clk, 							// Тактовая частота [20 МГц]
	input rx,  							// Входные данные 
	output reg [7:0] data_o, 			// Выходные данные 
	output reg data_rdy = 0, 			// Строб, сигнализирующий что на шине data_o валидные данные и их можно "забирать"			
	output reg have_prblm = 0			// Строб, сигнализирующий о проблемах в определении входного бита. Если он == 0 -> все ок, иначе -> проблемы
							); 
	
	reg [1:0] state = 2'b00; 	// Регистр статуса 
	reg [8:0] shift; 		 	// Сдвиговый регистр. В него побитово кладутся данные, приходящие с rx 
	reg [10:0] cntStrobe = 0; 	// Счетчик countOfStrobe
	reg [2:0] checker; 		 	// Регистр проверки -> На один информационный бит приходится countOfStrobe тактов. Имеет смысл "разделить"
								// данный бит и записать данные в регистр checker
	reg [4:0] cntData = 0; 		// Счетчик данных [счетчик битов]
	
	always @(posedge clk)
	begin 
	case (state)
		2'b00	:	begin 
						if (rx == 0)
						begin 
							state <= 2'b01; 
							data_rdy <= 0; 
						end 
					end 
		2'b01	:	begin 
						if (cntStrobe < countOfStrobe)
						begin 
							cntStrobe <= cntStrobe + 1; 
							if (cntStrobe == firstCheckStrobe | cntStrobe == secondCheckStrobe | cntStrobe == thirdCheckStrobe)
							begin 
								checker[0] <= rx; 
								checker[2:1] <= checker[1:0]; 
							end 
						end 
						else 
						begin 
						if (cntData < 9)
						begin 
							// Проверяем все ли у нас хорошо 
							if (checker[0] != checker[1] | checker[0] != checker[2])
								have_prblm <= 1; 
							else 
								have_prblm <= 0; 
							// Запись бита в регистр 	
							shift[8] <= checker[1]; 
							shift[7:0] <= shift[8:1]; 
							cntData <= cntData + 1; 
							cntStrobe <= 0; 
						end 
						else 
						begin 			
							have_prblm <= 0; 								
							data_o <= shift[8:1]; 
							cntData <= 0; 
							cntStrobe <= 0; 
							state <= 2'b10; 								 
						end 
						end 
					end 
		2'b10	:	begin 
						data_rdy <= 1; 
						state <= 2'b00; 
					end 
	
	endcase	
	end 

endmodule

Testbench. Симуляция работы приемника

В тестбенче подадим на входы нашего блока управляющие сигналы. Для этого воспользуемся блоком UART передатчика — о нем можно прочитать в предыдущей статье -> вот здесь

При этом передатчик заведем на частоте 100 МГц, а наш приемник на 20 МГц, т.к. такой вариант событий наиболее вероятен — это два независимых устройство со своей тактовой частотой.

Код тестбенча :


`timescale 1ns / 100ps; 
module receiver_tb(); 
	
	parameter fr = 50; 
	reg clk = 0; 
	
	always 
	begin 
		#(fr/2) clk = 1; 
		#(fr/2) clk = 0; 
	end 
	
	// Абсолютно другой клок, чтобы отвязаться от клока проекта 
	// Имеется ввиду, что наш проект частота, на которой работает UART приемник - 20 МГц, 
	// А частота передатчика - то есть нашего testbench'a = 100 МГц 
	
	parameter frTR = 10; 
	reg sclk = 0; 
	
	always 
	begin 
		#(frTR/2) sclk = 1; 
		#(frTR/2) sclk = 0; 
	end 
	
	reg [7:0] data_shift = 8'b1100_0011; 
	reg [7:0] data; 
	reg data_rdy = 0; 
	wire tx_rx; 
	wire areTransRdy; 
	reg [2:0] state = 2'b00; 
	
	always @(posedge sclk)
	begin 
		case (state)
			2'b00 : if (areTransRdy)
					begin 
						state <= 2'b01; 
						data_rdy <= 1; 
						data <= data_shift; 
					end 
			2'b01 :	begin 
						data_rdy <= 0; 
						state <= 2'b00; 
						data_shift <= data_shift + 1; 
					end 
		endcase 
	end 
	
	
	
	uart_param_receiver receiver(
							.clk(clk), 		
							.rx(tx_rx),  		
							.data_o(), 	
							.data_rdy(), 
							.have_prblm() 
						); 
	uart_param_trans transmitter(
							.clk(sclk),			   	
							.data(data),      	
							.data_rdy(data_rdy),        	
							.tx(tx_rx),      
							.transm_rdy(areTransRdy)
							);
						
endmodule; 

Код передатчика UART, настроенного на частоту 100 МГц и скорость 115200 КБит/с.

module uart_param_trans #(
	parameter countOfStrobe = 868	// Параметр, который высчитывается из соотношения Fclk[Гц] / V[бит/c]
									// соответствует тому, сколько тактов приходится на передачу одного 
									// бита данных при выбраной скорости UART(бит/с) и тактовой частоте CLK (Гц)
	)(
	input clk,			   		// Тактовая частота [20 МГц]
	input [7:0] data,      		// Данные, которые собираемся передать по TX
	input data_rdy,        		// Строб, который соответствует тому, что данные валидные и их нужно передать 
	output reg tx = 1,          // Выходной порт, передает данные по последовательному интерфейсу 
	output reg transm_rdy = 1   // Строб, который сигнализирует о том, что данные переданы и блок готов к передаче новых данных 
	); 
	
	reg [1:0] state = 2'b00; 	// Регистр, который будет менять значение в зависимости от состояния нашего модуля 
	reg [10:0] cntStrobe = 0; 	// Регистр - счетчик, который будет накапливаться до необходимого числа стробов (до countOfStrobe)
	reg [4:0] cntBit = 0; 		// Регистр - счетчик, указываюший на номер передаваемого бита из data
	reg [7:0] shiftData; 		// Сдвиговый регистр, в который мы записываем входные данные (data), а затем последовательно, побитово 
								// передаем по TX 
	
	always @(posedge clk)
	begin 
		case(state)
			2'b00 :	begin 
						if (data_rdy)
						begin 
							state <= 2'b01; 
							shiftData <= data; 
							transm_rdy <= 0; 		
							tx <= 0; 
						end 
					end 
			2'b01 :	begin 
						if (cntBit == 0)
						begin 
							if (cntStrobe < countOfStrobe)
								cntStrobe <= cntStrobe + 1; 
							else 
							begin 
								cntStrobe <= 0; 
								cntBit <= 1; 
								tx <= shiftData[0]; 
								shiftData[6:0] <= shiftData[7:1]; end end if (cntBit > 0 && cntBit < 9)
						begin 
							if (cntStrobe < countOfStrobe)
								cntStrobe <= cntStrobe + 1; 
							else 
							begin 
								cntStrobe <= 0; 
								cntBit <= cntBit + 1; 
								tx <= shiftData[0]; 
								shiftData[6:0] <= shiftData[7:1]; 
							end 
						end 
						if (cntBit == 9)
						begin 
							if (cntStrobe < countOfStrobe)
							begin
								cntStrobe <= cntStrobe + 1; 							
								tx <= 1; 
							end 
							else 
							begin 
								cntStrobe <= 0; 
								cntBit <= 0; 
								transm_rdy <= 1; 
								state <= 2'b00; 
							end 
						end 
					end 
			
		endcase 
	end 
endmodule 

Результаты моделирования в ModelSim

Ниже представлен результат моделирования (кликнув на картинку вы сможете просмотреть ее в полном масштабе — она откроется в новой вкладке).

modelsim

Как видно из результатов данные data_o (выход приемника) и data (вход передатчика) совпадают…

Это свидетельствует о правильной работе блока как передатчика, так и приемника.


Возможные ошибки

  1. При увеличении параметра countOfStrobe необходимо увеличивать разрядность cntStrobe
  2. При изменении countOfStrobe необходимо также менять firstCheckStrobe, secondCheckStrobe и thirdCheckStrobe

Вопросыыыыы??? 

cropped-img_1479-1024x683

Ну че, типа это … пиши если что 🙂

 

В данной статье речь пойдет о передатчике UART, и она немного перекликается с предыдущей статьей [в ней Вы можете ознакомиться с теоретической частью работы UART и посмотреть код для приемопередатчика с тактовой частотой 20 Мгц и инф. скоростью 115200 кБит/с], за тем исключением, что здесь мы рассмотрим один из модулей Асинхронного Универсального Приемопередатчика (UART) — а именно передатчик.

Представленный ниже код является легко параметризуемым — его можно использовать в проектах с разными частотами и на разных информационных скоростях … следует лишь изменить один параметр.


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

В модуле используются следующие порты:

clk — входной однобитный порт. По данному порту подаются тактовые импульсы, по которым тактируется весь наш блок

data — входной 8-битный порт. На этом параллельном порту мы получаем данные, которые будем передавать по последовательному порту tx

data_rdy — входной однобитный порт. Лог. единица на данном порту сигнализирует о том, что данные (data) валидны и их необходимо передать по tx

tx — выходной однобитный порт. По нему мы передаем последовательный поток данных

transm_rdy — выходной однобитный порт. Данный порт сигнализирует остальным блокам о своем состоянии. Если на нем лог. 1, то блок — готов к передаче данных и ждет на вход data и data_rdy для передачи данных, если лог. 0 — то блок находится в процессе работы (занят)


Код передатчика — Verilog код

Здесь  сountOfStrobe — параметр, меняя который может регулировать информационную скорость нашего UART’а. В данном случае UART настроен на скорость 115200 кБит/с при тактовой частоте clk = 20 МГц.

сountOfStrobe = Fclk(Гц)/Vuart(бит/c) = 20*10^6 / 115200 = 173;

module uart_param_trans #(
                            parameter countOfStrobe = 173    // Параметр, который высчитывается из соотношения Fclk[Гц] / V[бит/c]
                                                            // соответствует тому, сколько тактов приходится на передачу одного 
                                                            // бита данных при выбраной скорости UART(бит/с) и тактовой частоте CLK (Гц)
                            )(
                            input clk,                       // Тактовая частота 
                            input [7:0] data,              // Данные, которые собираемся передать по TX
                            input data_rdy,                // Строб, который соответствует тому, что данные валидные и их нужно передать 
                            output reg tx = 1,          // Выходной порт, передает данные по последовательному интерфейсу 
                            output reg transm_rdy = 1   // Строб, который сигнализирует о том, что данные переданы и блок готов к передаче новых данных 
                            ); 
    
    reg [1:0] state = 2'b00;     // Регистр, который будет менять значение в зависимости от состояния нашего модуля 
    reg [7:0] cntStrobe = 0;     // Регистр - счетчик, который будет накапливаться до необходимого числа стробов (до countOfStrobe)
    reg [4:0] cntBit = 0;         // Регистр - счетчик, указываюший на номер передаваемого бита из data
    reg [7:0] shiftData;         // Сдвиговый регистр, в который мы записываем входные данные (data), а затем последовательно, побитово 
                                // передаем по TX 
    
    always @(posedge clk)
    begin 
        case(state)
            2'b00 :    begin 
                        if (data_rdy)
                        begin 
                            state <= 2'b01; 
                            shiftData <= data; 
                            transm_rdy <= 0;         
                            tx <= 0; 
                        end 
                    end 
            2'b01 :    begin 
                        if (cntBit == 0)
                        begin 
                            if (cntStrobe < countOfStrobe)
                                cntStrobe <= cntStrobe + 1; 
                            else 
                            begin 
                                cntStrobe <= 0; 
                                cntBit <= 1; 
                                tx <= shiftData[0]; 
                                shiftData[6:0] <= shiftData[7:1]; 
                            end 
                        end 
                        if (cntBit > 0 && cntBit < 9)
                        begin 
                            if (cntStrobe < countOfStrobe)
                                cntStrobe <= cntStrobe + 1; 
                            else 
                            begin 
                                cntStrobe <= 0; 
                                cntBit <= cntBit + 1; 
                                tx <= shiftData[0]; 
                                shiftData[6:0] <= shiftData[7:1]; 
                            end 
                        end 
                        if (cntBit == 9)
                        begin 
                            if (cntStrobe < countOfStrobe)
                            begin
                                cntStrobe <= cntStrobe + 1;                             
                                tx <= 1; 
                            end 
                            else 
                            begin 
                                cntStrobe <= 0; 
                                cntBit <= 0; 
                                transm_rdy <= 1; 
                                state <= 2'b00; 
                            end 
                        end 
                    end 
            
        endcase 
    end 
endmodule 

Testbench проекта … Симуляция работы модуля в ModelSim

Для того, чтобы убедиться в том, что модуль работает — напишем тестбенч. Тестбенч — файл, который пишется для симуляции работы модуля и он симулирует работу портов, так, как это указано в техническом задании.

В нашем случае testbench подаст на наш блок — тактовый сигнал clk, данные data и строб data_rdy, при этом он будет следить за стробом transm_rdy и когда тот будет переходить в лог.1 (т.е. передатчик будет готов передавать новые данные по tx) testbench будет инкрементировать данные  (увеличивать значение на 1) и переводить data_rdy в лог. 1.

`timescale 1ns / 100 ps; 
    module uart_param_tb(); 
    parameter fr = 50; // 20 MHz 
    
    reg clk = 0; 
    reg [7:0] data = 8'b0111_1110; 
    reg data_rdy = 0; 
    reg [1:0] state = 2'b00; 
    
    wire tx, transm_rdy; 
    
    always 
    begin
        #(fr/2) clk = 1; 
        #(fr/2) clk = 0; 
    end 
    
    always @(posedge clk)
    begin 
    
    case (state)
        2'b00 :    begin 
                    if (transm_rdy)
                    begin 
                        state <= 2'b01; 
                        data <= data + 1; 
                    end 
                end 
        2'b01 : begin 
                    data_rdy <= 1; 
                    state <= 2'b10; 
                end 
        2'b10 : begin 
                    data_rdy <= 0; 
                    state <= 2'b00; 
                end 
    endcase 
    end 
    
    
    uart_param_trans tb(
                    .clk(clk),                       
                    .data(data),              
                    .data_rdy(data_rdy),                
                    .tx(tx),          
                    .transm_rdy(transm_rdy)   
                    );
    
endmodule 

Результаты моделирования в ModelSim

Ниже представлены рисунки симуляции при скорости UART 115200 и 230400 Кбит/c. Причем для изменения скорости, как говорилось выше, был поменян только один параметр.

87

Работа модуля на скорости 230400 Кбит/с

173

Работа модуля на скорости 115200 Кбит/с

Из рисунков видно, что передатчик работает правильно.


Файлы для скачивания

Вы можете скачать файлы ( uart_trans ). В архиве прикреплен файл script.do, в котором указаны все параметры для запуска симуляции в ModelSim. Для его запуска необходимо разархивировать архив, в ModelSim с помощью команды


cd вашаДиректорияГдеЛежатФайлы (Например C:/Users/Me/Decktop/thisPrj/)

do script.do

Если есть вопросы, замечания, предложения — пиши в комментариях

В данной статье рассмотрен вопрос работы ПЛИС с периферийными устройствами, например с 8-канальным 12-битным АЦП ADC128S102 ( datasheet ). Использование этого устройства в любых проектах обусловленно простотой ( АЦП опрашивается по SPI интерфейсу и по нему же АЦП выдает данные с каждого из каналов) и малыми затратами программистов на работу с данным устройством.

В чем же заключается простота работы с SPI переферией?

Дело в том, что достаточно просто подать клок (тактовую частоту), изменить чипселект (CS), тем самым дать переферийному устройству понять, что с ним сейчас работают и подать данные.

На примере нашего ADC128S102 процесс работы происходит следующим образом…

adc128s102-time

В самом начале ПЛИС выставляет выходную ножку чипселекта (CS) в логическую 1, клок (SCLK) не подается [на самом деле не имеет значения — подается тактовая частота или нет — АЦП не реагирует на нее, т.к. CS свидетельствует о том, что с устройством сейчас не работают], данные по DOUT  не идут.

В нашей программе идет циклический опрос каналов АЦП с записью данных в память и последующей отправкой полученных данных по UART на ПК [об этом — в следующей статье].

module adc(
            input fab_clk_8MHz,      // Тактовая частота 8 МГц
            output reg SENSE_CS = 1, // Два ЦАП
            input SENSE_DIN,          // Данные, приходящие с АЦП
            output reg SENSE_DOUT,      // Данные с ПЛИС на АЦП (выбор канала)
            output sclk              // Тактовая частота для управления АЦП
            ); 

assign sclk = fab_clk_8MHz; 

// Переменные 
reg [1:0] state = 2'b00;
reg [7:0] data_2adc [8:0];
reg [3:0] cnt_frame= 0;
reg [9:0] cnt = 0;
reg [11:0] data_from_adc [8:0]; 
reg [11:0] data_adc_save [8:0];

// Начальная инициализация данных для отправки данных на АЦП (по документации - важны лишь 
// 6,5,4 биты. 
initial
begin
    data_2adc[0] = 8'b00_000_000; // none
    data_2adc[1] = 8'b11_000_111; // IN0
    data_2adc[2] = 8'b11_001_111; // IN1
    data_2adc[3] = 8'b11_010_111; // IN2
    data_2adc[4] = 8'b11_011_111; // IN3
    data_2adc[5] = 8'b11_100_111; // IN4
    data_2adc[6] = 8'b11_101_111; // IN5
    data_2adc[7] = 8'b11_110_111; // IN6
    data_2adc[8] = 8'b11_111_111; // IN7
end

always @ (negedge fab_clk_8MHz)
  begin
   case (state)
    2'b00 : begin
                state <= 2'b01;
                cnt_frame <= 0;
            end
    2'b01 : begin
             if (cnt_frame < 8)
                begin
                  cnt_frame <= cnt_frame + 1;
                  state <= 2'b10;
                  cnt <= 0;
                end
             else
                state <= 2'b00;
            end
     2'b10 : begin
            if (cnt <=50)
            begin
                cnt <= cnt + 1;
             if (cnt <= 15 )
                    SENSE_CS <= 0;
             if (cnt >15 && cnt <= 50)
                    SENSE_CS <= 1;
             end
             else
                 begin
                    state <= 2'b01;
                    cnt <= 0;
                  end
              end
     endcase
  end

// Отправка данных на АЦП
always @ (negedge fab_clk_8MHz)
begin
     if (cnt > 0 && cnt <= 7)
            SENSE_DOUT <= data_2adc[cnt_frame][7-cnt];
end

// Чтение и запись данных с АЦП
always @ (posedge fab_clk_8MHz)
begin
    if (cnt >= 5 && cnt <= 16)
            data_from_adc [cnt_frame][16-cnt] <= SENSE_DIN;
else 
    begin 
        if (cnt == 18) 
                data_adc_save[cnt_frame] <= data_from_adc[cnt_frame]; 
    end 
end            
endmodule 

Промоделировав код в ModelSim получим результат:
1_%d0%ba%d0%b0%d0%bd%d0%b0%d0%bb
Как видно из рисунка данные SENSE_DOUT отправляются согласно datasheet’у… На данном фрагменте
ПЛИС запрашивает данные канала IN0 (первый канал)…
В более развернутом масштабе получим следующую картинку:
%d0%b2%d1%81%d0%b5_%d0%ba%d0%b0%d0%bd%d0%b0%d0%bb%d1%8b
Между опросами я ввел некоторую задержку, ее можно отрегулировать в коде, меняя cnt (сейчас оно равно 50)
Файл с кодом можно скачать(.rar) -> adc