cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 
Highlighted
Observer
Observer
595 Views
Registered: ‎11-09-2019

Begginer question : Optimal method for serial port computations

Jump to solution

Hello. I am trying to test an implementation in an FPGA (Spartan 3E starter kit) that would allow me to send three numbers (a,b,c) from MATLAB to it and then have the FPGA return the output o = a*b+c, through a serial port. I have achieved this somewhat correctly but if I try to run this code for around 300 iterations (sometimes more, sometimes less) the counters start going of out synch and I eventually start getting gibberish data as outputs.

The code for MATLAB is just sending 3 numbers in a loop, which I'm sure is correct and I think there's no point in posting.

The code for the FPGA is:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity matlabtest2_rxleds is

--50 MHz Clock, 115200 baud UART
-- (50000000)/(115200) = 434
port 
	(
		not_data_in 			: in std_logic;
		i_Clk				: in std_logic;
		
		not_data_out 			: out std_logic;
		
		state_leds 			: out std_logic_vector(7 downto 0)
		
		
	);

end entity;

architecture rtl of matlabtest2_rxleds is




function multiplysigned(operand_a : signed((4+4)-1 downto 0);
								operand_b : signed((4+4)-1 downto 0)) return signed is
		variable multiplication_x : signed (2*(4+4)-1 downto 0);
		
begin 
	multiplication_x := operand_a * operand_b;
	return multiplication_x(2*(4+4)-(4+1) downto 4);

end function;


-----------------------------

function subtractsigned(operand_a : signed((4+4)-1 downto 0);
								operand_b : signed((4+4)-1 downto 0)) return signed is
		variable operand_a_s : signed ((4+4) downto 0);
		variable operand_b_s : signed ((4+4) downto 0);
		variable subtract_x : signed ((4+4) downto 0);		
		
		
begin 
	operand_a_s := resize(signed(operand_a), operand_a_s'length);
	operand_b_s := resize(signed(operand_b), operand_b_s'length);
	subtract_x := operand_a_s - operand_b_s;
	return subtract_x((4+4)-1 downto 0);

end function;

----------------------------

function addsigned(operand_a : signed((4+4)-1 downto 0);
								operand_b : signed((4+4)-1 downto 0)) return signed is
		variable operand_a_s : signed ((4+4) downto 0);
		variable operand_b_s : signed ((4+4) downto 0);
		variable sum_x : signed ((4+4) downto 0);		
		
		
begin 
	operand_a_s := resize(signed(operand_a), operand_a_s'length);
	operand_b_s := resize(signed(operand_b), operand_b_s'length);
	sum_x := operand_a_s + operand_b_s;
	return sum_x((4+4)-1 downto 0);

end function;

------------------------------------


type t_state is (idle,check,receiving,assigning,computing,sending,finish);

signal First_Counter 	: integer range 0 to 500-1 := 0;
signal Clk_Counter 		: integer range 0 to 500-1 := 0;
signal Reset_Counter 	: integer range 0 to 1000 := 0;
signal Index_Counter 	: integer range 0 to (4+4)-1:=0;

signal Nmbr_Counter 	: integer range 0 to 2 :=0;
signal Cycle_Counter 	: integer range 0 to 500-1 := 0;
signal data_word_rx 	: std_logic_vector ((4+4)-1 downto 0):=(others => '0');
signal data_word_tx 	: std_logic_vector ((4+4)-1 downto 0):=(others => '0');

signal s_leds_1			: std_logic_vector(3 downto 0):=(others => '0');
signal sleds_2			: std_logic_vector(3 downto 0):=(others => '0');
signal s_leds			: std_logic_vector(7 downto 0):=(others => '0');

signal i_data_1 	: signed ((4+4)-1 downto 0):=(others => '0');
signal i_data_2 	: signed ((4+4)-1 downto 0):=(others => '0');
signal i_data_3 	: signed ((4+4)-1 downto 0):=(others => '0');
signal o_data 		: signed ((4+4)-1 downto 0):=(others => '0');

signal cycle : std_logic:='0' ;
signal checkbit : std_logic:='1' ;
signal data_in : std_logic:='0';
signal data_out : std_logic:='1';
signal computed : std_logic :='0' ;

signal state : t_state := idle;


begin

p_UART : process (i_Clk)
begin
    if rising_edge(i_Clk) then
		data_in <= not not_data_in;	
        if Cycle_Counter < 500-1 then
			Cycle <= '0';
			Cycle_Counter <= Cycle_Counter+1;
		else
			Cycle <='1';
			Cycle_Counter <= 0;
		end if;
		case state is
 
			when idle =>
				sleds_2 <="0001";
				--
				Clk_Counter <= 0;
				First_Counter <= 0;
				Index_Counter <= 0;
				data_word_rx <= (others => '0');
				data_word_tx <= (others => '0');
				computed <= '0';
				o_data <= (others => '0');
				--
				data_out <= '1';
				if data_in = '0' then
					checkbit <= data_in;
					state <= check;
				end if;
			when check =>
			sleds_2 <="0010";
				if First_Counter = 500/2 then
					if checkbit = data_in then
						checkbit <= '1';
						Reset_Counter <= 0;
						state <= receiving;
					else
						checkbit <= '1';
						state <= idle;
					end if;
				else
					First_Counter <= First_Counter +1;
					Reset_Counter <= Reset_Counter +1;
					state <= check;
				end if;
			when receiving =>
			sleds_2 <="0011";
				if Clk_Counter < 500-1 then
					Clk_Counter <= Clk_Counter +1;
					state <= receiving;
				else			
					Clk_Counter <= 0;
					if Index_Counter < (4+4)-1 then
						data_word_rx(Index_Counter) <= data_in;
						Index_Counter <= Index_Counter +1;
						state <= receiving;
					else 
						data_word_rx(Index_Counter) <= data_in;
						Index_Counter <=0;
						state <= assigning;
					end if;
				end if;
			when assigning =>
			sleds_2 <="0100";
				if Nmbr_Counter = 0 then 
					i_data_1 <= signed(data_word_rx);
					Nmbr_Counter <= Nmbr_Counter +1;
					state <= idle;
				elsif Nmbr_Counter = 1 then
					i_data_2 <= signed(data_word_rx);
					Nmbr_Counter <= Nmbr_Counter +1;
					state <= idle;
				elsif Nmbr_Counter = 2 then
					i_data_3 <= signed(data_word_rx);
					Nmbr_Counter <= 0;					
					state <= computing;
				end if;
				
			when computing =>
			sleds_2 <="0101";
			
				Nmbr_Counter <= 0;	--
				if computed = '0' then
				o_data <= addsigned(multiplysigned(i_data_1,i_data_2),i_data_3);	
				computed <= '1';
				state <= computing;
				else
				data_word_tx <= std_logic_vector(o_data);
				i_data_1 <= (others => '0');
				i_data_2 <= (others => '0');
				i_data_3 <= (others => '0');
				data_out <= '0';
				state <= sending;
				
				end if;
			when sending =>
			sleds_2 <="0110";
			Nmbr_Counter <= 0;--
				if Clk_Counter < 500-1 then
					Clk_Counter <= Clk_Counter +1;
					state <= sending;
					
				else			
					Clk_Counter <= 0;
					if Index_Counter < (4+4)-1 then
						data_out <= data_word_tx(Index_Counter);
						Index_Counter <= Index_Counter +1;
						state <= sending;
					else 
						data_out <= data_word_tx(Index_Counter);
						Index_Counter <=0;
						state <= finish;
					end if;
				end if;
			when finish =>
			Nmbr_Counter <= 0;	--
			sleds_2 <= "0111";
				if Clk_Counter < 500-1 then
					Clk_Counter <= Clk_Counter +1;
					state <= finish;
				else
				data_out <= '1';
				
				state <= idle;
				end if;
		end case;		
	end if;
end process;

s_leds_1 <= std_logic_vector(to_unsigned(Nmbr_Counter+1, s_leds_1'length));
s_leds <= std_logic_vector (sleds_2 & s_leds_1);

not_data_out <= not data_out;	

state_leds <= s_leds;
	
end rtl;

I don't understand why it goes out of synch/stops working after a few apparently random interations so if anyone could point out a solution I'd greatly appreciate it. I've been trying to debug this all day but can't think of any way to correct if other than perhaps redesign the whole thing so in the case that there is a much better way to do this I'm also open to suggestions! Thanks in advance!

0 Kudos
1 Solution

Accepted Solutions
Highlighted
Voyager
Voyager
501 Views
Registered: ‎06-20-2012
I think that you don't wait the STOP bit time between the 3 operands.
 
== If this was helpful, please feel free to give Kudos, and close if it answers your question ==

View solution in original post

10 Replies
Highlighted
Moderator
Moderator
569 Views
Registered: ‎07-30-2007

A serial port needs a dc balanced characters.  I would suggest an 8B10B conversion to 8B10B characters and you could throw in a occasional comma character and alignment code to ensure the synchronization.  If you need a primer on dc balance and comma alignment see https://www.xilinx.com/publications/archives/books/serialio.pdf 




----------------------------------------------------------------------------
Don't forget to reply, kudo, and accept as solution
----------------------------------------------------------------------------


0 Kudos
Highlighted
557 Views
Registered: ‎06-21-2017

When you say serial port, do you mean something like an RS-232 UART?  It looks to me like you are sampling at the very beginning of a bit period.  The computer's clock and the FPGA clock will not be synchronized.  They will drift in relation to each other.  Sample the data bit closer to the center of the data period.  This will give you more leeway if the clocks are not exactly the same frequency.

0 Kudos
Highlighted
Observer
Observer
552 Views
Registered: ‎11-09-2019

Yes I meant UART RS-232 sorry for not mentioning it! I am already doing that, as you can see in the "check" state I wait for 500/2 (500 is the bit length here) clock ticks and start sampling in that point in every bit that follows. I thought the problem might be in this part but the error occurs at a random iteration each time so I really don't know where to look at, sadly.

0 Kudos
Highlighted
Observer
Observer
551 Views
Registered: ‎11-09-2019

Thanks for the reply and for learning material! But my problem also happens for low baud rates so is this still the solution to my problem? I say this because my approach is working for when I just send a single number and retrieve it's doubled value (the first test I performed)

0 Kudos
Highlighted
Voyager
Voyager
502 Views
Registered: ‎06-20-2012
I think that you don't wait the STOP bit time between the 3 operands.
 
== If this was helpful, please feel free to give Kudos, and close if it answers your question ==

View solution in original post

Highlighted
Observer
Observer
480 Views
Registered: ‎11-09-2019

So that means I actually have to wait 1.5 * bit time length? I'm currently not at work so I don't have the FPGA with me so I can test but that does make sense, thanks! I'll test it tomorrow morning.

 

Just one thing: Shouldn't that cause a catastrophic failure from the start? How can I run this for hundreds of times before getting an error? 

0 Kudos
Highlighted
Voyager
Voyager
473 Views
Registered: ‎06-20-2012

@hugobpontes 

It would only fail if the last bit is a '0'.

== If this was helpful, please feel free to give Kudos, and close if it answers your question ==
Highlighted
Observer
Observer
456 Views
Registered: ‎11-09-2019

Thats what I figured! I find it strange that that wouldnt happen for so many numbers, but anyway Ill try it tomorrow and say something if it doesnt work. Thanks for your help and "kudos" to you!

0 Kudos
Highlighted
Advisor
Advisor
374 Views
Registered: ‎04-26-2015

@hugobpontes Looking at your code, you've correctly calculated that you need to divide the 50MHz input clock by 434 to get 115200bps - but then your code actually divides it by 500 so you're getting 100000bps? At that sort of error (~15%) a simple UART will virtually always miss the last bit, and if the bytes are coming immediately after one another then it won't be able to re-sync.

 

Edit: you probably also need to look up input metastability. It's easy to fix, but I don't think it is fixed in this code.

0 Kudos
Highlighted
Observer
Observer
302 Views
Registered: ‎11-09-2019

@calibra 

Yes it did work! The problem was not waiting for the stop bits between the operands. I thought the problem would be something more complex but I guess I must consider even the simpler options. Thanks again, I'll accept your answer as the solution.

@u4223374 

Yes I accidentally uploaded the code for a check I did with 100.000 baud instead of 115200 but I was testing it correctly! Thanks anyway. My attempt at improving the issues with metastability was reading data_in from the port only on clock edges but maybe that's not correct, but it does work now!

0 Kudos