VHDL : 비트 카운팅시 수신 모듈이 무작위로 실패


9

배경

이것은 개인 프로젝트입니다. FPGA를 N64에 연결하는 것과 관련하여 FPGA가 수신하는 바이트 값은 UART를 통해 내 컴퓨터로 전송됩니다. 실제로는 꽤 잘 작동합니다! 불행히도 임의의 시간에 장치가 실패한 다음 복구됩니다. 디버깅을 통해 문제를 찾을 수 있었지만 VHDL에 대해 무능하기 때문에 문제를 해결하는 방법에 어려움이 있습니다.

나는 며칠 동안 VHDL과 함께 놀았으며 이것을 해결할 수 없을 수도 있습니다.

문제

N64 신호를 FPGA로 측정하는 오실로스코프가 있고 다른 채널은 FPGA의 출력에 연결됩니다. 카운터 값을 기록하는 디지털 핀도 있습니다.

기본적으로 N64는 STOP 비트를 포함하여 9 개의 데이터 비트를 전송합니다. 카운터는 수신 된 데이터 비트를 카운트하고 9 비트에 도달하면 FPGA는 UART를 통해 전송을 시작합니다.

올바른 동작은 다음과 같습니다. 여기에 이미지 설명을 입력하십시오

FPGA는 파란색 파형이고 주황색 파형은 N64의 입력입니다. 수신 기간 동안, FPGA는 디버깅 목적으로 입력 신호를 "echos"합니다. FPGA는 9로 카운트 한 후 UART를 통해 데이터를 전송하기 시작합니다. 디지털 핀은 9로 카운트되고 N64가 완료된 직후 FPGA 출력이 LOW가됩니다.

실패의 예는 다음과 같습니다.

여기에 이미지 설명을 입력하십시오

카운터는 비트 2와 7을 건너 뜁니다. FPGA는 끝에 도달하여 N64에서 다음 시작 비트를 기다리지 만 아무것도 없습니다. 따라서 FPGA는 시간 초과 및 복구됩니다.

N64 수신 모듈의 VHDL입니다. 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;

그래서 어떤 아이디어? 디버깅 팁? 유한 상태 머신 코딩에 대한 팁?

그 사이에, 나는 그것으로 계속 놀 것이다 (나는 결국 그것을 가질 것이다)! 스택 교환을 도와주세요. 당신은 나의 유일한 희망입니다!

편집하다

디버깅에서 추가로 발견하면 상태가 waitForStart에서 waitForStop으로 다시 이동합니다. 각 상태에 waitForStart가 '5'이고 waitForStop이 '4'인 값을 지정했습니다. 아래 이미지를보십시오 : 여기에 이미지 설명을 입력하십시오


1
첫 번째 경우 블록에는 "s_bitCount <= X"0 ";" X가 오타입니까?
travisbartley

@ trav1s 아니오, "X"는 16 진수를 나타냅니다. 따라서 X "0"은 실제로 이진에서 "0000"입니다.
Nick Williams

1
linter를 통해 코드를 실행하는 동안 몇 가지 오류가 발생했습니다. 신호 N64RXD 및 tdre는 순차적 프로세스 36 행의 감도 목록에 사용해서는 안됩니다.
travisbartley

1
@ trav1s 포인터 주셔서 감사합니다, 나는 그 매개 변수를 제거했습니다; 당신 말이 맞아요, 그것들은 필요하지 않습니다. 불행히도 여전히 문제가 있습니다. 스코프를 사용하여 어떤 상태에 있는지 감지하는 신호를 추가했습니다. 어떤 이유로 FPGA는 "waitForStart"에서 "waitForStop"으로 상태가 바뀌지 않습니다. 이것이 FPGA가 비트를 카운트하는 상태에 도달하지 않기 때문에 카운트하지 않는 이유입니다. "점프 백"이 문제인 것 같습니다.
Nick Williams

1
그러나 "waitForStart"-> "waitForStop"전환이 유효하지 않습니다. 한 번의 주기로 점프 할 수있는 방법이 없습니다. 사이에 아주 짧은 상태가 없는지 확인하십시오. 그렇지 않으면 하드웨어 / 타이밍 오류가 있어야합니다.
travisbartley

답변:


9

rx 데이터 라인에 동기화 프로그램이 표시되지 않습니다.

모든 비동기 입력은 샘플링 클록과 동기화되어야합니다. 이것에는 몇 가지 이유가 있습니다 : 준 안정성과 라우팅. 이들은 다른 문제이지만 상호 관련되어 있습니다.

신호가 FPGA 패브릭을 통해 전파되는 데 시간이 걸립니다. FPGA 내부의 클럭 네트워크는 이러한 "이동"지연을 보상하도록 설계되어 FPGA 내의 모든 플립 플롭이 정확히 같은 순간에 클럭을 볼 수 있도록합니다. 일반적인 라우팅 네트워크는 이것을 가지고 있지 않으며, 대신 모든 신호는 클럭이 변경되기 전에 약간의 시간 동안 안정적이어야하고 클럭이 변경된 후 약간의 시간 동안 안정적으로 유지되어야한다는 규칙에 의존합니다. 이러한 작은 시간을 주어진 플립 플롭에 대한 셋업 및 홀드 타임이라고합니다. 툴체인의 위치 및 경로 구성 요소는 특정 디바이스의 라우팅 지연에 대해 잘 이해하고 있으며 신호가 FPGA에서 플립 플롭의 설정 및 유지 시간을 위반하지 않는다는 기본적인 가정을합니다.

샘플링 클럭과 동기화되지 않은 신호가있는 경우 새 값이 전파 될 시간이 없었기 때문에 하나의 플립 플롭에서 신호의 "이전"값을 볼 수 있습니다. 이제 동일한 신호를 보는 로직이 두 개의 다른 값을 보는 바람직하지 않은 상황에 처해 있습니다. 이로 인해 잘못된 작동, ​​충돌 상태 머신 및 혼란을 진단하기 어려운 모든 종류의 원인이 될 수 있습니다.

모든 입력 신호를 동기화해야하는 다른 이유는 준 안정성입니다. 이 주제에 관한 책이 있지만 간단히 말해서 디지털 논리 회로는 가장 기본적인 수준의 아날로그 회로입니다. 클럭 라인이 상승하면 입력 라인의 상태가 캡처되고 해당 시간에 해당 입력이 안정적으로 높거나 낮은 레벨이 아닌 경우 샘플링 플립 플롭에서 알 수없는 "중간"값을 캡처 할 수 있습니다.

아시다시피 FPGA는 디지털 짐승이며 높거나 낮지 않은 신호에 잘 반응하지 않습니다. 더구나, 그 결정적인 가치가 샘플링 플립 플롭을지나 FPGA로 들어가면 로직의 더 큰 부분이 이제는 결정되지 않은 가치를보고 그것을 이해하려고 할 때 모든 종류의 기묘함을 유발할 수 있습니다.

해결책은 신호를 동기화하는 것입니다. 가장 기본적인 수준에서 이것은 플립 플롭 체인을 사용하여 입력을 캡처한다는 것을 의미합니다. 첫 번째 플립 플롭에 의해 캡처되어이를 처리 할 수있는 준 안정 레벨은 복잡한 논리에 도달하기 전에 해결 될 또 다른 기회를 얻습니다. 두 개의 플립 플롭은 일반적으로 입력을 동기화하기에 충분합니다.

기본 동기화 프로그램은 다음과 같습니다.

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;

N64 컨트롤러 rx 데이터 라인의 물리적 핀을 동기화 기의 async_in 입력에 연결하고 sync_out 신호를 UART의 rxd 입력에 연결하십시오.

동기화되지 않은 신호는 이상한 문제를 일으킬 수 있습니다 . 신호를 읽는 프로세스의 클럭과 동기화되지 않은 FPGA 요소에 연결된 입력이 동기화되어 있는지 확인하십시오. 여기에는 푸시 버튼, UART 'rx'및 'cts'신호가 포함됩니다. FPGA가 신호를 샘플링하는 데 사용하는 클럭과 동기화되지 않은 모든 것입니다.

( 단지 : 몇 년 전 www.mixdown.ca/n64dev 에서 페이지를 썼습니다 . 방금 사이트를 마지막으로 업데이트 할 때 링크가 끊어지고 컴퓨터로 돌아 오면 아침에 문제를 해결한다는 것을 깨달았습니다. 많은 사람들이 그 페이지를 사용한 줄 몰랐어요!)


위대하고 포괄적 인 답변에 감사드립니다! 나는 이것을 시도하고 내 기계를보다 견고하게 만들 것입니다.
Nick Williams

2
실제로는 메타 스타 빌리티 (metastability)와는 거의 관련이 없지만 (비록 우려가 있지만), 비동기 입력에서 상태 변수의 비트를 보유하는 다양한 FF까지의 경로 지연과 관련된 모든 것이 있습니다.
데이브 트위드

당신이 맞습니다, @DaveTweed; 나는 둘을 함께 묶는 경향이 있으며 그것은 잘못된 생각입니다.
akohlsmith

@DaveTweed의 의견을 고려하기 위해 답변을 편집했습니다.
akohlsmith

1
트윗 담아 가기 동기화 프로그램을 추가했는데 솔루션이었습니다. 또한, 그것은 당신이 믹스 다운 페이지를 쓴 놀라운 우연의 일치입니다; N64 프로토콜에서 해당 기사를 참조하는 많은 리소스를 발견했으며 링크가 손상되어 실망했습니다. 수정 해 주셔서 감사합니다.
Nick Williams

6

문제는 동기화되지 않은 신호를 사용하여 상태 시스템에서 결정을 내린다는 것입니다. 상태 머신에서 사용하기 전에 이중 FF 동기화기를 통해 이러한 모든 외부 신호를 공급해야합니다.

상태 변수에서 두 개 이상의 비트로 변경되는 모든 상태 전이에서 발생할 수있는 상태 머신의 미묘한 문제입니다. 동기화되지 않은 입력을 사용하면 비트 중 하나가 변경되고 다른 하나는 변경되지 않을 수 있습니다. 이것은 당신을 의도 한 것과 다른 상태로 안내하며, 합법적 인 상태 일 수도 있고 아닐 수도 있습니다.

마지막 진술은 when others => ...주정부 사건 진술서에 항상 기본 사건 (VHDL에서 )을 가져야하는 이유 입니다.


그래, 이것은 내가 분리하려는 결론 이었지만, 충분한 정보를 얻기 전에 그것을 뛰어 넘고 싶지 않았다 ...
travisbartley

1
젠장, 넌 날 이겼어 나는 이것을 태블릿에 입력하는 것을 비난합니다. :-)
akohlsmith

@akohlsmith, 동쪽에서 가장 빠른 총이 응답에 중요한 것은 아닙니다. 귀하의 답변은 유용하며, 귀하가이 답변을 게시 한 직후 게시 한 이후로 부정 행위가 아니 었습니다.
travisbartley

나는 when others =>그것이 도움 이되었다고 생각 했지만 신디사이저가 "안전한"상태 머신을 원한다는 것을 이해하도록 속성을 추가 하지 않으면 ( 내가 사용한 신디사이저에서) 주장하는 것을 얻지 못한다 . 정상적인 동작은 one-hot 표현으로 최적화하고 복구 논리를 제공하지 않는 것입니다. 참조 xilinx.com/support/answers/40093.htmlsynopsys.com/Company/Publications/SynopsysInsight/Pages/... 예를 들어.
Martin Thompson

와! 그것은 훌륭한 팁이며 매력처럼 작동했습니다.
Nick Williams
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.