VHDL: nhận mô-đun thất bại ngẫu nhiên khi đếm bit


9

Lý lịch

Đây là một dự án cá nhân; liên quan đến việc kết nối một FPGA với N64, các giá trị byte mà FPGA nhận được sau đó được gửi qua UART tới máy tính của tôi. Nó thực sự hoạt động khá tốt! Không may vào thời điểm ngẫu nhiên, thiết bị sẽ bị lỗi, sau đó phục hồi. Thông qua việc gỡ lỗi, tôi đã tìm ra được vấn đề, tuy nhiên tôi vẫn chưa biết cách khắc phục vì tôi không đủ năng lực với VHDL.

Tôi đã chơi với VHDL vài ngày nay và tôi có thể không thể giải quyết vấn đề này.

Vấn đề

Tôi có một máy hiện sóng đo tín hiệu N64 vào FPGA và kênh khác kết nối với đầu ra của FPGA. Tôi cũng có chân kỹ thuật số ghi lại giá trị truy cập.

Về cơ bản, N64 gửi 9 bit dữ liệu, bao gồm một bit STOP. Bộ đếm đếm các bit dữ liệu nhận được và khi tôi đạt 9 bit, thì FPGA bắt đầu truyền qua UART.

Đây là hành vi đúng: nhập mô tả hình ảnh ở đây

FPGA là dạng sóng màu xanh và dạng sóng màu cam là đầu vào của N64. Trong thời gian nhận, FPGA của tôi "vang" tín hiệu của đầu vào cho mục đích gỡ lỗi. Sau khi FPGA đếm đến 9, nó bắt đầu truyền dữ liệu qua UART. Lưu ý rằng các chân kỹ thuật số được đếm đến 9 và đầu ra FPGA trở nên THẤP ngay sau khi N64 kết thúc.

Đây là một ví dụ về một thất bại:

nhập mô tả hình ảnh ở đây

Lưu ý rằng bộ đếm bỏ qua bit 2 và 7! Các FPGA đến cuối cùng, chờ bit bắt đầu tiếp theo từ N64 nhưng không có gì. Vì vậy, thời gian ra và phục hồi.

Đây là VHDL cho mô đun nhận N64. Nó chứa bộ đếm: s_bitCount.

library IEEE;
use IEEE.STD_LOGIC_1164.all;   
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity N64RX is
     port(
         N64RXD : in STD_LOGIC;                    --Data input
         clk25 : in STD_LOGIC;
         clr : in STD_LOGIC; 
         tdre : in STD_LOGIC;                      --detects when UART is ready
         transmit : out STD_LOGIC;                 --Signal to UART to transmit  
         sel : out STD_LOGIC; 
         echoSig : out STD_LOGIC;
         bitcount : out STD_LOGIC_VECTOR(3 downto 0);
         data : out STD_LOGIC_VECTOR(3 downto 0)   --The significant nibble
         );
end N64RX;

--}} End of automatically maintained section

architecture N64RX of N64RX is 

type state_type is (start, delay2us, sigSample, waitForStop, waitForStart, timeout, count9bits, sendToUART);

signal state: state_type;
signal s_sel, s_echoSig, s_timeoutDetect : STD_LOGIC;
signal s_baudCount : STD_LOGIC_VECTOR(6 downto 0);  --Counting variable for baud rate in delay
signal s_bitCount : STD_LOGIC_VECTOR(3 downto 0);  --Counting variable for number of bits recieved 
signal s_data : STD_LOGIC_VECTOR(8 downto 0);   --Signal for data

constant delay : STD_LOGIC_VECTOR(6 downto 0) := "0110010";  --Provided 25MHz, 50 cycles is 2us 
constant delayLong : STD_LOGIC_VECTOR(6 downto 0) := "1100100";

begin 

n64RX: process(clk25, N64RXD, clr, tdre)
begin
    if clr = '1' then
        s_timeoutDetect <= '0';
        s_echoSig <= '1';
        s_sel <= '0';
        state <= start;
        s_data <= "000000000";
        transmit <= '0'; 
        s_bitCount <= "0000";
        s_baudCount <= "0000000";  
    elsif (clk25'event and clk25 = '1') then    --on rising edge of clock input
        case state is
            when start =>   
                --s_timeoutDetect <= '0';
                s_sel <= '0';
                transmit <= '0';        --Don't request UART to transfer   
                s_data <= "000000000";
                s_bitCount <= X"0";   
                if N64RXD = '1' then
                    state <= start;
                elsif N64RXD = '0' then     --if Start bit detected
                    state <= delay2us;
                end if;    

            when delay2us =>                 --wait two microseconds to sample
                --s_timeoutDetect <= '0';
                s_sel <= '1';
                s_echoSig <= '0';
                if s_baudCount >= delay then    
                    state <= sigSample;
                else
                    s_baudCount <= s_baudCount + 1;
                    state <= delay2us;
                end if;  

            when sigSample => 
                --s_timeoutDetect <= '1';
                s_echoSig <= N64RXD;
                s_bitCount <= s_bitCount + 1;
                s_baudcount <= "0000000";
                s_data <= s_data(7 downto 0) & N64RXD;      
                state <= waitForStop;   

            when waitForStop => 
                s_echoSig <= N64RXD;
                if N64RXD = '0' then
                    state <= waitForStop;
                elsif N64RXD = '1' then
                    state <= waitForStart;
                end if;   

            when waitForStart => 
                s_echoSig <= '1';
                s_baudCount <= s_baudCount + 1; 
                if N64RXD = '0' then 
                    s_baudCount <= "0000000";
                    state <= delay2us;
                elsif N64RXD = '1' then 
                    if s_baudCount >= delayLong then
                        state <= timeout;
                    elsif s_bitCount >= X"9" then
                        state <= count9bits;
                    else
                        state <= waitForStart;
                    end if;
                end if;     

            when count9bits =>  
                s_sel <= '0';
                if tdre = '0' then
                    state <= count9bits;
                elsif tdre = '1' then
                    state <= sendToUART;
                end if;   

            when sendToUART =>
                transmit <= '1';
                if tdre = '0' then
                    state <= start;
                else
                    state <= sendToUART;
                end if;

            when timeout =>
                --s_timeoutDetect <= '1';
                state <= start;

        end case;   
    end if;
end process n64RX;  
--timeoutDetect <= s_timeoutDetect;
bitcount <= s_bitCount;
echoSig <= s_echoSig;
sel <= s_sel;
data <= s_data(4 downto 1);

end N64RX;

Vậy, có ý kiến ​​gì không? Mẹo gỡ lỗi? Lời khuyên về mã hóa máy nhà nước hữu hạn?

Trong khi đó, tôi sẽ tiếp tục chơi với nó (cuối cùng tôi sẽ có nó)! Giúp tôi trao đổi Stack, bạn là hy vọng duy nhất của tôi!

Biên tập

Một khám phá thêm trong quá trình sửa lỗi của tôi, các trạng thái sẽ chuyển từ WaitForStart trở lại WaitForStop. Tôi đã cho mỗi trạng thái một giá trị với WaitForStart bằng '5' và WaitForStop bằng '4'. Xem hình ảnh dưới đây: nhập mô tả hình ảnh ở đây


1
Trong khối trường hợp đầu tiên của bạn, có dòng "s_bitCount <= X" 0 ";" Có phải đó là một lỗi đánh máy?
travisbartley

@ trav1s Không, "X" biểu thị thập lục phân. Vì vậy, X "0" thực sự là "0000" ở dạng nhị phân.
Nick Williams

1
Tôi có một vài lỗi khi chạy mã thông qua một kẻ nói dối. Tín hiệu N64RXD và tdre không nên được sử dụng trong danh sách độ nhạy của quy trình tuần tự, dòng 36.
travisbartley

1
@ trav1s Cảm ơn con trỏ, tôi đã xóa các tham số đó; bạn nói đúng, những thứ đó không cần thiết Tôi vẫn có vấn đề không may. Với phạm vi, tôi đã thêm các tín hiệu để phát hiện trạng thái tôi đang ở. Vì một số lý do, FPGA nhảy từ "WaitForStart" trở lại "WaitForStop" không có trạng thái ở giữa! Đây là lý do tại sao nó không được tính bởi vì FPGA không đạt đến trạng thái mà nó đếm bit. "Nhảy lùi" dường như là vấn đề.
Nick Williams

1
Nhưng quá trình chuyển đổi "WaitForStart" -> "WaitForStop" không hợp lệ. Không có cách nào để thực hiện bước nhảy đó trong một chu kỳ. Kiểm tra thật kỹ để đảm bảo không có trạng thái rất ngắn ở giữa. Nếu không, phải có một lỗi phần cứng / thời gian.
travisbartley

Câu trả lời:


9

Tôi không thấy trình đồng bộ hóa trên dòng dữ liệu rx.

Tất cả các đầu vào không đồng bộ phải được đồng bộ hóa với đồng hồ lấy mẫu. Có một vài lý do cho việc này: tính di động và định tuyến. Đây là những vấn đề khác nhau nhưng có liên quan đến nhau.

Phải mất thời gian để tín hiệu truyền qua cấu trúc đồ họa. Mạng đồng hồ bên trong FPGA được thiết kế để bù cho các độ trễ "di chuyển" này để tất cả các flip-flop trong FPGA nhìn thấy đồng hồ vào cùng một thời điểm. Mạng định tuyến thông thường không có điều này, và thay vào đó dựa vào quy tắc rằng tất cả các tín hiệu phải ổn định trong một thời gian trước khi đồng hồ thay đổi và duy trì ổn định trong một chút thời gian sau khi đồng hồ thay đổi. Những bit thời gian nhỏ này được gọi là thời gian thiết lập và giữ cho một lần lật nhất định. Thành phần vị trí và tuyến đường của chuỗi công cụ có hiểu biết rất rõ về độ trễ định tuyến cho thiết bị cụ thể và đưa ra một giả định cơ bản rằng tín hiệu không vi phạm thiết lập và giữ thời gian của các lần lật trong FPGA.

Khi bạn có các tín hiệu không được đồng bộ hóa với đồng hồ lấy mẫu, bạn có thể gặp phải tình huống trong đó một lần lật sẽ thấy giá trị "cũ" của tín hiệu do giá trị mới không có thời gian để truyền đi. Bây giờ bạn đang ở trong tình huống không mong muốn khi logic nhìn vào cùng một tín hiệu sẽ thấy hai giá trị khác nhau. Điều này có thể gây ra hoạt động sai, máy bị hỏng và tất cả các loại khó chẩn đoán.

Lý do khác tại sao bạn phải đồng bộ hóa tất cả các tín hiệu đầu vào của mình là một thứ gọi là tính di động. Có các tập được viết về chủ đề này nhưng tóm lại, mạch logic kỹ thuật số ở mức cơ bản nhất là một mạch tương tự. Khi dòng đồng hồ của bạn tăng trạng thái của dòng đầu vào được ghi lại và nếu đầu vào đó không phải là mức cao hoặc thấp ổn định tại thời điểm đó, giá trị "ở giữa" không xác định có thể được ghi lại bằng cách lật mẫu.

Như bạn đã biết, các GPU là những con thú kỹ thuật số và không phản ứng tốt với tín hiệu không cao cũng không thấp. Tồi tệ hơn, nếu giá trị không xác định đó vượt qua lật lấy mẫu và vào trong FPGA, nó có thể gây ra tất cả các loại kỳ lạ khi các phần logic lớn hơn bây giờ nhìn thấy giá trị không xác định và cố gắng hiểu ý nghĩa của nó.

Giải pháp là đồng bộ hóa tín hiệu. Ở cấp độ cơ bản nhất, điều này có nghĩa là bạn sử dụng một chuỗi dép xỏ ngón để nắm bắt đầu vào. Bất kỳ mức độ siêu bền nào có thể đã bị bắt bởi lần lật đầu tiên và được quản lý để làm cho nó ra một cơ hội khác để được giải quyết trước khi nó chạm vào logic phức tạp của bạn. Hai flip flop thường là quá đủ để đồng bộ hóa đầu vào.

Một bộ đồng bộ hóa cơ bản trông như thế này:

entity sync_2ff is
port (
    async_in : in std_logic;
    clk : in std_logic;
    rst : in std_logic;
    sync_out : out std_logic
);
end;

architecture a of sync_2ff is
begin

signal ff1, ff2: std_logic;

-- It's nice to let the synthesizer know what you're doing. Altera's way of doing it as follows:
ATTRIBUTE altera_attribute : string;
ATTRIBUTE altera_attribute OF ff1 : signal is "-name SYNCHRONIZER_IDENTIFICATION ""FORCED IF ASYNCHRONOUS""";
ATTRIBUTE altera_attribute OF a : architecture is "-name SDC_STATEMENT ""set_false_path -to *|sync_2ff:*|ff1 """;

-- also set the 'preserve' attribute to ff1 and ff2 so the synthesis tool doesn't optimize them away
ATTRIBUTE preserve: boolean;
ATTRIBUTE preserve OF ff1: signal IS true;
ATTRIBUTE preserve OF ff2: signal IS true;

synchronizer: process(clk, rst)
begin
if rst = '1' then
    ff1 <= '0';
    ff2 <= '0';
else if rising_edge(clk) then
    ff1 <= async_in;
    ff2 <= ff1;
    sync_out <= ff2;
end if;
end process synchronizer;
end sync_2ff;

Kết nối chân vật lý cho dòng dữ liệu rx của bộ điều khiển N64 với đầu vào async_in của bộ đồng bộ hóa và kết nối tín hiệu syncFor với đầu vào rxd của UART của bạn.

Tín hiệu không đồng bộ có thể gây ra vấn đề kỳ lạ . Đảm bảo rằng bất kỳ đầu vào nào được kết nối với một phần tử FPGA không được đồng bộ hóa với đồng hồ của quá trình đọc tín hiệu được đồng bộ hóa. Điều này bao gồm các nút bấm, tín hiệu UART 'rx' và 'cts' ... bất cứ thứ gì không được đồng bộ hóa với đồng hồ mà FPGA đang sử dụng để lấy mẫu tín hiệu.

(Một bên: Tôi đã viết trang tại www.mixdown.ca/n64dev nhiều năm trước. Tôi chỉ nhận ra rằng tôi đã phá vỡ liên kết khi tôi cập nhật trang web lần cuối và sẽ sửa nó vào buổi sáng khi tôi quay lại máy tính. Tôi không biết nhiều người đã sử dụng trang đó!)


Cảm ơn câu trả lời tuyệt vời và toàn diện! Tôi sẽ thử cái này và làm cho máy của tôi mạnh hơn.
Nick Williams

2
Nó thực sự có rất ít liên quan đến tính di động (mặc dù cũng là một mối quan tâm) và mọi thứ phải làm với độ trễ đường dẫn khác nhau từ đầu vào không đồng bộ đến các FF khác nhau giữ các bit của biến trạng thái.
Dave Tweed

Bạn nói đúng, @DaveTweed; Tôi có xu hướng gộp hai người lại với nhau và đó là suy nghĩ sai lầm.
akohlsmith

Tôi đã chỉnh sửa câu trả lời của mình để đưa ý kiến ​​của @ DaveTweed vào tài khoản.
akohlsmith

1
@akohlsmith Tuyệt vời! Tôi đã thêm bộ đồng bộ hóa và đó là giải pháp. Ngoài ra, đó là một sự trùng hợp đáng kinh ngạc mà bạn đã viết trang mixdown; Tôi tìm thấy một loạt các tài nguyên trên giao thức N64 đã tham chiếu bài viết đó và tôi đã thất vọng vì liên kết bị hỏng. Cảm ơn đã sửa nó.
Nick Williams

6

Vấn đề của bạn là bạn đang sử dụng tín hiệu không đồng bộ để đưa ra quyết định trong máy trạng thái của mình. Bạn nên cung cấp tất cả các tín hiệu bên ngoài đó thông qua các bộ đồng bộ hóa FF kép trước khi sử dụng chúng trong máy trạng thái.

Đó là một vấn đề tinh tế với các máy trạng thái có thể phát sinh trong bất kỳ chuyển đổi trạng thái nào liên quan đến thay đổi thành hai hoặc nhiều bit trong biến trạng thái. Nếu bạn sử dụng đầu vào không đồng bộ, một trong các bit có thể thay đổi trong khi đầu vào khác không thay đổi. Điều này đưa bạn đến một trạng thái khác với trạng thái dự định và nó có thể hoặc không thể là một trạng thái hợp pháp.

Tuyên bố cuối cùng đó là lý do tại sao bạn cũng phải luôn có một trường hợp mặc định (trong VHDL, when others => ...) trong tuyên bố trường hợp máy trạng thái đưa bạn từ bất kỳ trạng thái bất hợp pháp nào sang trạng thái hợp pháp.


Vâng, đây là kết luận mà tôi sắp cô lập, nhưng tôi không muốn nhảy vào đó trước khi có đủ thông tin ...
travisbartley

1
Chết tiệt, bạn đánh tôi với nó. Tôi đổ lỗi cho việc đánh máy tất cả những thứ này trên máy tính bảng. :-)
akohlsmith

@akohlsmith, là khẩu súng nhanh nhất ở phía đông không phải là điều duy nhất quan trọng khi trả lời. Câu trả lời của bạn rất hữu ích và rõ ràng là không gian lận vì bạn đã đăng ngay sau đó.
travisbartley

Tôi đã từng nghĩ rằng điều đó when others =>có ích, nhưng hóa ra nó không mang lại cho bạn những gì bạn yêu cầu (theo bất kỳ trình tổng hợp nào tôi đã sử dụng) trừ khi bạn thêm thuộc tính để đảm bảo rằng synth hiểu bạn muốn có một máy trạng thái "an toàn". Hành vi bình thường là tối ưu hóa thành biểu diễn một nóng và không cung cấp logic khôi phục. Xem xilinx.com/support/answers/40093.htmlsynopsys.com/Company/Publications/SynopsysInsight/Pages/ trộm chẳng hạn.
Martin Thompson

Ồ Đó là một mẹo tuyệt vời và nó hoạt động như một bùa mê.
Nick Williams
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.