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

## Begginer question : Optimal method for serial port computations

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
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!

1 Solution

Accepted Solutions
Highlighted
Voyager
501 Views
Registered: ‎06-20-2012
I think that you don't wait the STOP bit time between the 3 operands.

10 Replies
Highlighted
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
----------------------------------------------------------------------------

Highlighted
Scholar
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.

Highlighted
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.

Highlighted
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)

Highlighted
Voyager
502 Views
Registered: ‎06-20-2012
I think that you don't wait the STOP bit time between the 3 operands.

Highlighted
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?

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

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

Highlighted
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!

Highlighted
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.

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

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.

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!