My “Hello World!” On FPGA or the next version of UART

    My Hello World!  on FPGA or the next version of UART

    Finally I got around to learning about FPGAs. But somehow it turns out to be wrong: I’m writing drivers for Linux, I program microcontrollers, I read the circuits (and project a little), I have to grow further.

    Since it didn’t seem interesting to me to blink LEDs, I decided to do a simple thing. Namely, write receiver and transmitter modules for UART, integrate them inside the FPGA (at the same time understand how to use IP Core), and test it all on real hardware.

    Immediately I say that there was no way to make a universal parameterized core of the task. This is just a test project, on the subject of "feeling what a FPGA is and how to communicate with it."

    So let's start with the receiver. The algorithm is described quite well , so I will repeat here only its main points.

    • The sampling frequency of the RX signal is four times higher than the required UART transmission speed.
    • The condition for the start of reception will be considered as the transition of the input signal RX from a high level to a low one if it is not being received at the moment.
    • The condition for the reliable identification of the start bit will be considered to be the holding of the RX signal in the low level state at the second sampling rate. At the same time, we practically fall into the middle of a bit pulse, which will allow us to further perform sampling of pulses every 4 cycles.
    • In case of an error in the start bit or in the stop bit, set the error signal error. Based on it, we form the fastsync signal, which we will use in the future to quickly synchronize the receiver.
    • After identifying the start bit, we begin the sequential reception of data bits, starting with the youngest. The received data is written to the register with a shift to the right by one bit. The condition for the end of reception will be the detection of the start bit in the 0th position of the shift register.
    • Fast synchronization of the receiver consists in bringing it back to its original state after an error is detected when the RX signal goes to a high level (this can be either a transmission of a logical "1" or a transmission of a stop bit or the inactivity state of the transmission line).
    • The condition for successful completion of reception (correct values ​​of the start and stop bits) is the complete signal. From it (when clocked by the rdclk signal) a pulse signal is prepared, which indicates the presence of valid data on the rxdata bus.

    Immediately, I note that I did not want to clock the read signal from the clock signal clk (unexpectedly, yes?), So as not to tie up the speed of subsequent data processing to the UART exchange rate. A similar implementation in the transmitter module ( see below ). And the test bundle of receiver and transmitter modules is made on the basis of Intel's IP Core FIFO, and with the ability to simulate different speeds for the consumer and the data generator. The only limitation is that the clock frequency of the producer and the consumer of the data must not be lower than the clock frequency clk.

    Receiver Module (Verilog)
    //// Блок приемника UART//// Данные rxdata валидны, когда ready==1 и error==0.// Сигнал ready устанавливается в 1 после завершения приема на один такт rdclk.//// Реализация:// После перехода сигнала rx в низкий уровень проверяем его неизменность в течении// 2-х тактов. Если уровень остался низким, значит начат прием стартового бита.// В этом случае делаем прием 8 битов данных и одного стоп-бита (всего 9 битов).// 2 Такта - это приблизительно середина бита, так что устойчивость должна быть// хорошей.//// Биты передаются начиная от младшего к старшему.// Лог. '0' передается низким уровнем сигнала, лог. '1' передается высоким уровнем сигнала// idle передается высоким уровнем сигнала (лог. '1')// start-бит передается низким уровнем сигнала (лог. '0')// stop-бит передается высоким уровнем сигнала (лог. '1')
    module uart_rx(
    	nreset,			// сигнал сброса (асинхронный, активный уровень 0)
    	clk, 				// тактовая частота UART, д.б. в четыре раза больше скорости обмена по UART
    	rx,				// входная линия UART
    	rdclk,			// тактирование чтения результата приема (rxdata, ready)
    	rxdata,			// принятые данные, значение валидно при ready==1
    	ready,			// индикатор валидности данных rxdata (активный уровень 1)
    	error,			// индикатор ошибки приема (активный уровень 1)
    	busy,				// индикатор занятости модуля (идет прием, активный уровень 1)
    	idle);			// индикатор свободной линии приемника (активный уровень 1)
    input wire nreset;		// сигнал сброса (асинхронный, активный уровень 0)
    input wire clk; 			// тактовая частота, д.б. в четыре раза больше скорости обмена по UART
    input wire rx;				// входная линия UART
    input wire rdclk;			// тактирование чтения результата приема
    output wire[7:0] rxdata;
    output wire ready;
    output error;
    output busy;
    output idle;
    // Изменение сигнала завершения приема, тактируемое через rdclk
    reg[2:0] done = 3'b000;
    // Выходной сигнал готовности принятых данных, тактируемый rdclk//assign ready = (done == 2'b10) ? 1'b1 : 1'b0;
    assign ready = (done[1] && !done[0]) ? 1'b1 : 1'b0;
    // Признак наличия ошибки приема
    reg error = 1'b0;
    // Сигнал сброса логики приемника для быстрой синхронизации при ошибке// Если на текущем такте имеем ранее установленный сигнал error и высокий// уровень сигнала rx, возможно это пауза между передаваемыми байтами данных.
    wire fastsync = (error && rx);	
    // Признак свободной линии приемика
    reg idle = 1'b1;	
    // Принятые данные:// d[9] - стоповый бит, д.б. == 1// d[8:1] - данные// d[0] - стартовый бит, д.б. == 0
    reg[9:0] d = 10'b1xxxxxxxx1;
    // Статус приема. Завершение приема индицируется значением 2'b10
    wire[1:0] status = { d[9], d[0] };
    // Признак завершения приема.
    wire complete = (status == 2'b10) ? 1'b1 : 1'b0;
    // Принятый байт данных
    assign rxdata = d[8:1];
    // Признак занятости модуля
    reg busy = 0;
    // Счетчик тактовых импульсов до	семплирования линии rx
    reg[1:0] cnt;
    always @(posedge clk, negedge nreset)
    begin
    	if(!nreset) begin
    		rxreset();
    	end else begin
    		if(fastsync) begin
    			rxreset();
    		end else begin	
    			if(busy == 1'b1) begin
    				// Идет прием чего-то, проверяем необходимость семплинга rxif(cnt == 2'd0) begin
    					// Записываем принятый бит// в старший разряд данных со сдвигом предыдущего значения вправо// (т.к. передача идет от младшего бита к старшему)
    					d <= { rx, d[9:1] };
    					if(d[1] == 1'b0) begin
    						// На этом шаге стартовый бит попадет в последнюю позицию, прием завершен
    						busy <= 1'b0;
    						// Проверяем корректность стопового бита
    						error <= (rx == 1'b1) ? 1'b0 : 1'b1;
    					end else begin
    						// Мы находимся в процессе приемаif(rx && (d == 10'b1111111111)) begin
    							// Слишком маленькая длительность стартового бита
    							busy <= 1'b0;
    							// Индицируем наличие ошибки
    							error <= 1'b1;
    						end else begin
    							// Нормальная процедура приема// Кол-во тактов целого бита - подготовка к приему следующего бита
    							cnt <= 2'd3;
    						end
    					end			
    				end else begin
    					// Уменьшаем кол-во оставшихся до семплинга тактов
    					cnt <= cnt - 2'd1;
    				end
    			end else begin
    				// Модуль пока еще ничего не делаетif(!error) begin
    					// Нет сигнала ошибки, можно попытаться начать прием стартового битаif(rx == 1'b0) begin
    						// Линия приемника в низком уровне и до этого приема не было - начинаем работу
    						busy <= 1'b1;
    						// Инициализируем буфер приема данных. Здесь критично записать все 1, т.к. окончание// приема определяется состоянием d[0]==0
    						d <= 10'b1111111111;
    						// Проверять линию rx будем через 1/2 длительности бита// 1-й такт - это текущее сэмплирование// 2-й такт - это следующее сэмплирование (cnt будет 0)
    						cnt <= 2'd0;
    						// Т.к. мы потенциально начали прием, отмечаем линию как занятую
    						idle <= 1'b0;
    					end else begin
    						// Линия приемника свободна
    						idle <= 1'b1;
    					end
    				end
    			end
    		end
    	end
    end
    task rxreset;
    	begin
    		// Сброс признака ошибки
    		error <= 1'b0;
    		// Установка сигнала свободной линии (!?)
    		idle <= 1'b1;
    		// Сброс признака занятости модуля
    		busy <= 0;
    		// В принципе можно записать что-нибудь, лишь бы статус не попал в complete
    		d <= 10'b1xxxxxxxx1;
    	end
    endtask
    always @(negedge rdclk, negedge nreset)
    begin
    	if(!nreset) begin
    		done <= 3'b000;
    	end else begin
    		// По тактам чтения сохраняем состояние сигнала complete.// Логика сигнала формирования сигнала ready формирует один импульс при// изменение сигнала complete с 0 на 1 на один такт rdclk.
    		done <= { complete, done[2:1] };
    	end
    end
    endmodule
    


    Since the RX input signal is asynchronous and (possibly) unstable, a major element was connected before the receiver module in the main module . The element is also written in Verilog, but its code here makes no sense. Instead, a beautiful picture of the synthesized element.

    Synthesized major element scheme
    Мажоритарный элемент

    The transmitter unit is even simpler and, I hope, does not need additional comments.

    Transmitter module (Verilog, blocking and non-blocking assignments inside always)
    //// Блок передатчика UART//// Сигналы:// clk - частота должна быть в 4 раза больше скорости передачи, скважность не важна// rdclk - тактирование обмена txdata, write, fetch. Частота д.б. выше clk// txdata - данные для передачи, управляются сигналами write/fetch// write - источник имеет данные для передачи (1=да)// fetch - модуль принял данные для передачи (1=да)// tx - линия передачи UART// idle - линия передачи свободна (1=да, информационный сигнал)//// Для FIFO нужно использовать режим dcfifo_component.lpm_showahead = "ON"
    module uart_tx(
    	nreset,			// сигнал сброса (асинхронный, активный уровень 0)
    	clk,				// тактовая частота UART, д.б. в четыре раза больше скорости обмена по UART
    	rdclk,			// тактирование подтверждения приема данных от поставщика
    	txdata,			// шина данных на передачу от поставщика
    	write,			// признак наличия данных на передачу (активный уровень 1)
    	idle,				// индикатор не активного передатчика (активный уровень 1)
    	fetch,			// подтверждение загрузки данных от поставщика, тактируется rdclk
    	tx);				// выходная линия UART
    input wire nreset;		// сигнал сброса (асинхронный, активный уровень 0)
    input wire clk;			// тактирование UART
    input wire rdclk;
    input wire[7:0] txdata;
    input wire write;
    output wire idle;
    output fetch;
    output tx;
    // Состояние выходной линии
    reg tx = 1'b1;
    reg fetch = 1'b0;
    // Делитель частоты на 4
    reg[1:0] div4 = 2'd0;	
    // Состояние машины:
    reg[3:0] s = 4'd10;	
    // Передатчик полностью свободен
    assign idle = (s == 4'd10);
    // Сдвиговый регистр данных
    reg[7:0] d;	
    // Признак передачи стартового бита в данном цикле
    reg sendstart;
    // Признак возможности запроса новых данных на передачу
    reg canfetch;	
    // Признак завершения ввода новых данных, тактируется clk
    reg gotdata = 1'b0;
    // Для синхронизации clock domains
    reg[2:0] sync = 3'b000;
    // Запомненный по rdclk сигнал write
    reg wr = 1'b0;
    // При появлении запроса getdata==1 при наличии данных у внешнего источника// производится их запоминание в регистре nextdata и устанавливается признак// готовности gotdata==1. Кроме того, для внешнего источника данных формируется// сигнал подтверждени.// Сигнал gotdata снимается при снятии сигнала getdata.
    always @(posedge rdclk, negedge nreset)
    begin
    	if(!nreset) begin
    		wr <= 1'b0;
    		sync <= 3'b000;
    		// Сбрасываем сигнал подтверждения ввода данных
    		fetch <= 1'b0;
    	end else begin
    		// Запоминаем сигнал write
    		wr <= write;
    		// Проверяем появление запроса новых данных для передачи
    		sync <= { gotdata, sync[2:1] };
    		// По положительному переходу сигнала gotdata устанавливаем// сигнал подтверждения для источника данных. В остальных// случаях сбрасываем его.
    		fetch <= (sync[1] && !sync[0]) ? 1'b1 : 1'b0;
    	end
    end
    always @(posedge clk, negedge nreset)
    begin
    	if(!nreset) begin
    		// Установка передатчика в исходное состояние
    		div4 <= 2'd0;
    		s <= 4'd10;
    		gotdata <= 1'b0;
    	end else begin	
    		// Пока нет признака передачи стартового бита в этом цикле
    		sendstart = 1'b0;
    		// Начальная установка признака запроса данных на передачу
    		canfetch = wr;
    		if(div4 == 2'd0) begin
    			case(s)
    				4'd0:
    					begin
    						// Передача стартового бита будет инициирована ниже
    						sendstart = 1'b1;
    						// Передатчик занят, нельзя запрашивать новые данные
    						canfetch = 1'b0;
    					end
    				4'd9:
    					begin
    						// Передача стопового бита
    						tx <= 1'b1;
    					end
    				4'd10:
    					begin
    						// Состояние idle, ничего не делаем
    					end
    				default:
    					begin
    						// Идет передача битов данных, текущий младший является выходом
    						tx <= d[0];
    						// Выполняем сдвиг данных вправо
    						d <= { 1'b0, d[7:1] };
    						// Передатчик занят, нельзя запрашивать новые данные
    						canfetch = 1'b0;
    					end
    			endcase		
    		end else begin
    			// Выдерживаем текущее состояние
    			div4 <= div4 - 2'd1;
    			if(s < 4'd9) begin
    				// При выдерживании до состояния 9 прием новых данных невозможен!
    				canfetch = 1'b0;
    			end
    		end
    		if(canfetch) begin
    			// Входные данные готовы, передаем их на обработку
    			d <= txdata;
    			// Подтверждение взятия данных на обработку
    			gotdata <= 1'b1;
    			if(idle /*s == 4'd10*/) begin
    				// Состояние idle - немедленно начинаем передачу стартового бита
    				sendstart = 1'b1;
    			end else begin
    				// На следующем шаге переходим к передаче стартового бита
    				s <= 4'd0;
    			end
    		end
    		if(gotdata) begin
    			// Данные были приняты ранее, снимаем сигнал подтверждения
    			gotdata <= 1'b0;
    		end
    		if(sendstart) begin
    			// На данном шаге начинаем передачу стартового бита
    			tx <= 1'b0;
    			// Переходим к следующему состоянию
    			s <= 4'd1;
    			// Длительность стартового бита
    			div4 <= 2'd3;
    		end else begin
    			if(div4 == 2'd0) begin
    				if(s < 4'd10) begin
    					// Последовательное изменение состояния на следующее
    					s <= s + 4'd1;
    					// Время выдерживания состояния
    					div4 <= 2'd3;
    				end
    			end
    		end
    	end
    end
    endmodule
    


    The above implementation of the transmitter caused a heated discussion in the comments. Although as a result, it seems everyone agreed that it was possible to do this, but be careful. For your own peace of mind, the module was rewritten to reflect all the mentioned guideliness. In my opinion, it is not much more complicated than the previous one in terms of human perception of the implemented algorithm.

    Transmitter Module (Verilog, ideologically correct)
    //// Блок передатчика UART//// Сигналы:// clk - частота должна быть в 4 раза больше скорости передачи, скважность не важна// rdclk - тактирование обмена txdata, write, fetch. Частота д.б. выше clk// txdata - данные для передачи, управляются сигналами write/fetch// write - источник имеет данные для передачи (1=да)// fetch - модуль принял данные для передачи (1=да)// tx - линия передачи UART// idle - линия передачи свободна (1=да, информационный сигнал)//// Для FIFO нужно использовать режим dcfifo_component.lpm_showahead = "ON"
    module uart_tx(
    	nreset,			// сигнал сброса (асинхронный, активный уровень 0)
    	clk,				// тактовая частота UART, д.б. в четыре раза больше скорости обмена по UART
    	rdclk,			// тактирование подтверждения приема данных от поставщика
    	txdata,			// шина данных на передачу от поставщика
    	write,			// признак наличия данных на передачу (активный уровень 1)
    	idle,				// индикатор не активного передатчика (активный уровень 1)
    	fetch,			// подтверждение загрузки данных от поставщика, тактируется rdclk
    	tx);				// выходная линия UART
    input wire nreset;		// сигнал сброса (асинхронный, активный уровень 0)
    input wire clk;			// тактирование UART
    input wire rdclk;
    input wire[7:0] txdata;
    input wire write;
    output wire idle;
    output fetch;
    output tx;
    // Состояние выходной линии
    reg tx = 1'b1;
    reg fetch = 1'b0;
    // Делитель частоты на 4
    reg[1:0] div4 = 2'd0;	
    // Состояние машины:
    reg[3:0] s = 4'd10;	
    // Передатчик полностью свободен
    assign idle = (s == 4'd10);
    // Сдвиговый регистр данных
    reg[7:0] d;	
    // Признак передачи стартового бита в данном цикле
    reg sendstart;
    // Признак возможности запроса новых данных на передачу
    reg canfetch;	
    // Признак завершения ввода новых данных, тактируется clk
    reg gotdata = 1'b0;
    // Для синхронизации clock domains
    reg[2:0] sync = 3'b000;
    // Запомненный по rdclk сигнал write
    reg wr = 1'b0;
    // При появлении запроса getdata==1 при наличии данных у внешнего источника// производится их запоминание в регистре nextdata и устанавливается признак// готовности gotdata==1. Кроме того, для внешнего источника данных формируется// сигнал подтверждени.// Сигнал gotdata снимается при снятии сигнала getdata.
    always @(posedge rdclk, negedge nreset)
    begin
    	if(!nreset) begin
    		wr <= 1'b0;
    		sync <= 3'b000;
    		// Сбрасываем сигнал подтверждения ввода данных
    		fetch <= 1'b0;
    	end else begin
    		// Запоминаем сигнал write
    		wr <= write;
    		// Проверяем появление запроса новых данных для передачи
    		sync <= { gotdata, sync[2:1] };
    		// По положительному переходу сигнала gotdata устанавливаем// сигнал подтверждения для источника данных. В остальных// случаях сбрасываем его.
    		fetch <= (sync[1] && !sync[0]) ? 1'b1 : 1'b0;
    	end
    end
    // Для улучшения стиля(?) выносим комбинаторную логику в отдельный always// Здесь у нас вычисляются сигналы sendstart и canfetch
    always @(*)
    begin
    	// Пока нет признака передачи стартового бита в этом цикле
    	sendstart = 1'b0;
    	if(nreset) begin
    		// Начальная установка признака запроса данных на передачу
    		canfetch = wr;
    		if(div4 == 2'd0) begin
    			case(s)
    				4'd0:
    					begin
    						// Передача стартового бита будет инициирована ниже
    						sendstart = 1'b1;
    						// Передатчик занят, нельзя запрашивать новые данные
    						canfetch = 1'b0;
    					end
    				4'd9:
    					begin
    						// Передача стопового бита
    					end
    				4'd10:
    					begin
    						// Состояние idle, ничего не делаем
    					end
    				default:
    					begin
    						// Идет передача битов данных// Передатчик занят, нельзя запрашивать новые данные
    						canfetch = 1'b0;
    					end
    			endcase		
    		end else begin
    			if(s < 4'd9) begin
    				// При выдерживании до состояния 9 прием новых данных невозможен!
    				canfetch = 1'b0;
    			end
    		end
    		if(canfetch && idle) begin
    			// Состояние idle - немедленно начинаем передачу стартового бита
    			sendstart = 1'b1;
    		end
    	end else begin
    		// В случае активного reset мы вообще работать не будем
    		canfetch = 1'b0;
    	end
    end	
    always @(posedge clk, negedge nreset)
    begin
    	if(!nreset) begin
    		// Установка передатчика в исходное состояние
    		div4 <= 2'd0;
    		s <= 4'd10;
    		gotdata <= 1'b0;
    	end else begin
    		if(div4 == 2'd0) begin
    			case(s)
    				4'd0:
    					begin
    						// В зависимости от sendstart может быть инициирована передача стартового бита
    					end
    				4'd9:
    					begin
    						// Передача стопового бита
    						tx <= 1'b1;
    					end
    				4'd10:
    					begin
    						// Состояние idle, ничего не делаем
    					end
    				default:
    					begin
    						// Идет передача битов данных, текущий младший является выходом
    						tx <= d[0];
    						// Выполняем сдвиг данных вправо
    						d <= { 1'b0, d[7:1] };
    					end
    			endcase		
    		end else begin
    			// Выдерживаем текущее состояние
    			div4 <= div4 - 2'd1;
    		end
    		if(canfetch) begin
    			// Входные данные готовы, передаем их на обработку
    			d <= txdata;
    			// Подтверждение взятия данных на обработку
    			gotdata <= 1'b1;
    			if(!idle /*s == 4'd10*/) begin
    				// На следующем шаге переходим к передаче стартового бита
    				s <= 4'd0;
    			end
    		end else begin
    			// Если данные были приняты ранее, снимаем сигнал подтверждения
    			gotdata <= 1'b0;
    		end
    		if(sendstart) begin
    			// На данном шаге начинаем передачу стартового бита
    			tx <= 1'b0;
    			// Переходим к следующему состоянию
    			s <= 4'd1;
    			// Длительность стартового бита
    			div4 <= 2'd3;
    		end else begin
    			if((div4 == 2'd0) && (s < 4'd10)) begin
    				// Последовательное изменение состояния на следующее
    				s <= s + 4'd1;
    				// Время выдерживания состояния
    				div4 <= 2'd3;
    			end
    		end
    	end
    end
    endmodule
    



    To test the receiver and transmitter, a main module was written on the knee. I ask him not to swear, design errors (external asynchronous signal nreset, no FIFO reset, etc.) I myself know. But for the purposes of testing the functionality they are not essential.

    My demo board is clocked from a 50Mhz signal source. Therefore, in the main module I used a PLL, at the output of which C0 I formed a frequency for operation with UART (1.8432Mhz, really 1.843198Mhz) and, as a joke, I formed a frequency of 300Mhz (output c1 PLL) for clocking the simulation of the information processing circuit.

    Main module (Verilog)
    //// Т.к. прием и передача данных через UART синхронизируется тактовой частотой UART,// а обработка данных синхронизируется тактовой частотой FPGA, то нужно использовать// для каждого модуля FIFO IP CORE типа DCFIFO.////NB!// Не забываем в SDC-файле прописывать соответствующие внутренние частоты!// Иначе получаем зверские эффекты (типа часть внутри блока if выполнилась,// а часть нет).
    module uart(
    	input wire clk50mhz,	// тактовая частота 50Mhz
    	input wire nreset,	// инверсный сигнал сброса
    	input wire rx,			// входной сигнал UART
    	output wire tx,		// выходной сигнал UART
    	output wire overflow
    	);
    // Тактовая частота 1.8432Mhz (реально 1.843198Mhz)	wire clk_1843200;
    // Тактовая частота 1.2288Mhz (реально 1.228799Mhz)//wire clk_1228800;// Внутренняя тактовая частота 300Mhz, сформированная PLLwire clk300mhz;
    // Синтезируем тактовые частоты для UART
    uart_pll pll50mhz(.inclk0(clk50mhz), 
    						.c0(clk_1843200) /*, .c1(clk_1228800)*/,
    						.c1(clk300mhz));
    // Скорость UART 38400// Делитель (1843200/38400)/4 = 12 ('b1100). // Скорость UART 57600// Делитель (1843200/57600)/4 = 8// Скорость UART 115200// Делитель (1843200/115200)/4 = 4// Скорость UART 230400// Делитель (1843200/230400)/4 = 2// Скорость UART 460800// Делитель (1843200/460800)/4 = 1 (т.е. делитель вообще не нужен!)// Тактовая частота для UARTwire uart_baud4;
    // Подключаем его в схему// Значение делителя .data должно быть на 1 меньше требуемого делителя. Тогда// период сигнала uart_baud4 будет равен значению .clock/делитель// Длительность высокого уровня сигнала uart_baud4 будет равна одному такту .clock
    uart_osc uart_osc_1(.clock(clk_1843200), 
    						  .data(5'd2/*5'd4*//*5'd12*/-5'd1), 
    						  .sload(uart_baud4), 
    						  .cout(uart_baud4));
    //wire uart_baud4 = clk_1843200;// Входной сигнал после мажоритарного фильтра	wire rxf;	
    // Подключаем мажоритарный фильтр на входной сигнал
    mfilter mfilter_rx(.clk(clk50mhz /*clk_1843200*/), 
    						 .in(rx), 
    						 .out(rxf)); 
    //wire rxf = rx;// Подключаем модуль приемникаwire[7:0] rxdata;
    wire rxready;
    wireerror;
    uart_rx uart_rx_1(.nreset(nreset), 
    						.clk(uart_baud4), 
    						.rx(rxf), 
    						.rdclk(clk300mhz /*clk50mhz*//*clk_1843200*/), 
    						.rxdata(rxdata), 
    						.ready(rxready), 
    						.error(error));
    wire[7:0] txdata;
    // Сигнал, индицирующий отсутствие данных, ожидающих передачиwire txnone;
    // Сигнал, индицирующий готовность передатчика принять новые данныеwire fetch;
    wire full;
    // Буферирование принятых данных// Запись тактируется сигналом uart_baud4// Чтение тактируется сигналом clk50mhz
    uart_fifo_rx uart_fifo_rx_1(.data(rxdata), 
    									 .rdclk(clk300mhz /*clk50mhz*//*clk_1843200*//*uart_baud4*/), 
    									 .rdreq(fetch), 
    									 .wrclk(clk300mhz /*clk50mhz*//*clk_1843200*//*uart_baud4*/), 
    									 .wrreq(rxready), 
    									 .rdempty(txnone), 
    									 .q(txdata), 
    									 .wrfull(full));
    assign overflow = ~error;
    uart_tx uart_tx_1(.nreset(nreset), 
    						.clk(uart_baud4), 
    						.rdclk(clk300mhz /*clk50mhz*//*clk_1843200*/), 
    						.txdata(txdata), 
    						.write(~txnone), 
    						.fetch(fetch), 
    						.tx(tx));
    endmodule
    


    For testing, a testcom traffic generator from Zelax was used. Unfortunately, my USB / UART adapter I had refused to work at speeds above 230400BPS, so all testing was done at this speed.

    Test result with filtering the input signal RX using the majority element
    Тестирование с предварительной фильтрацией сигнала RX
    Состояние сигналов, снятые при помощи Signal Tap
    Сигналы приемника UART при отсутствии ошибок

    And here the majority element was removed from the entrance.
    А что, как мне еще было имитировать произвольные ошибки при проверке схемы быстрой синхронизации?
    Тестирование без предварительной фильтрации сигнала RX
    Состояние сигналов, снятые при помощи Signal Tap
    Сигналы при быстрой синхронизации приемника после обнаружения ошибки

    Note


    Sorry, I did not take courses on Quartus and there was no one to ask questions. I stumbled on it myself and warned about other beginner FPGAs: be sure to create an SDC file in the project and describe the clock frequencies in it. Yes, the project is going on without it, although it is possible that warnings may appear if the synthesizer could not determine the timing characteristics. I first ignored them, until I killed half a day to determine the problem, why I have a receiver module when I execute the code

    if(rx == 1'b0) begin
      busy <= 1'b1;
      d <= 10'b1111111111;
      cnt <= 2'd0;
      idle <= 1'b0;
    endelsebegin

    The busy and idle signals were set correctly, but the contents of register d sometimes did not change.

    Appendix: SDC file to project
    set_time_format -unit ns -decimal_places 3# Тактовая частота 50Mhz, (50/50 duty cycle)
    create_clock -name {clk50mhz} -period 20.000 -waveform { 0.00010.000 } 
    ##############################################################################  Now that we have created the custom clocks which will be base clocks,#  derive_pll_clock is used to calculate all remaining clocks for PLLs
    derive_pll_clocks -create_base_clocks
    derive_clock_uncertainty
    # Сигналы от PLL софтина умеет считать сама?#		altpll_component.clk0_divide_by = 15625,#		altpll_component.clk0_duty_cycle = 50,#		altpll_component.clk0_multiply_by = 576,#		altpll_component.clk0_phase_shift = "0",#create_generated_clock -name clk_1843200 -source [get_ports {clk50mhz}] -divide_by 15625 -multiply_by 576 -duty_cycle 50 -phase 0 -offset 0# Для baudrate=38400# Сигнал активен в течении 1/4 цикла, т.е. duty=(1/4)*100=25%#create_generated_clock -name uart_baud4 -source [get_nets {pll50mhz|altpll_component|auto_generated|wire_pll1_clk[0]}] -divide_by 12 -duty_cycle 25 [get_nets {uart_osc_1|LPM_COUNTER_component|auto_generated|counter_reg_bit[0]}]# Для baudrate=230400# Сигнал активен в течении 1/4 цикла, т.е. duty=(1/4)*100=50%
    create_generated_clock -name uart_baud4 -source [get_nets {pll50mhz|altpll_component|auto_generated|wire_pll1_clk[0]}] -divide_by 2 -duty_cycle 25 [get_nets {uart_osc_1|LPM_COUNTER_component|auto_generated|counter_reg_bit[0]}]
    # Для baudrate=460800# Делитель равен 1, используется непосредственно выход PLL, поэтому описание дополнительной частоты не требуется.


    Many thanks to everyone who wrote comments on the article! Of these, I drew a lot of useful, although sometimes somewhat contradictory information. In my opinion, their value is much greater than the implementation of the algorithm described above. And, undoubtedly, they will be useful to those who also dare to get into the world of FPPs.

    List of external links

    1. Universal Asynchronous Transceiver (Wikipedia)
    2. Majority Element (Wikipedia)

    Also popular now: