VHDL: รับโมดูลสุ่มล้มเหลวเมื่อนับบิต


9

พื้นหลัง

นี่เป็นโครงการส่วนบุคคล มันเกี่ยวกับการเชื่อมต่อ FPGA กับ N64 ค่าไบต์ที่ FPGA ได้รับจะถูกส่งผ่าน UART ไปยังคอมพิวเตอร์ของฉัน มันใช้งานได้ดีจริง ๆ ! ในบางครั้งน่าเสียดายที่อุปกรณ์จะล้มเหลวจากนั้นกู้คืน ผ่านการดีบักฉันจัดการเพื่อค้นหาปัญหา แต่ฉันนิ่งงันวิธีการแก้ไขเพราะฉันไม่สามารถ VHDL

ฉันเล่นกับ VHDL มาสองสามวันแล้วและฉันอาจไม่สามารถแก้ไขปัญหานี้ได้

ปัญหา

ฉันมีออสซิลโลสโคปวัดสัญญาณ N64 ลงใน FPGA และช่องอื่น ๆ เชื่อมต่อกับเอาท์พุทของ FPGA ฉันยังมีพินดิจิตอลที่บันทึกค่าตัวนับ

โดยพื้นฐานแล้ว N64 จะส่งบิตข้อมูล 9 ชุดรวมถึงบิต STOP ตัวนับนับจำนวนข้อมูลที่ได้รับและเมื่อฉันไปถึง 9 บิต FPGA จะเริ่มส่งสัญญาณผ่าน UART

นี่คือพฤติกรรมที่ถูกต้อง: ป้อนคำอธิบายรูปภาพที่นี่

FPGA เป็นรูปคลื่นสีฟ้าและรูปคลื่นสีส้มเป็นอินพุตของ N64 ในช่วงระยะเวลาของการรับสัญญาณ FPGA ของฉัน "echos" เป็นสัญญาณของอินพุตสำหรับการดีบัก หลังจาก FPGA นับเป็น 9 จะเริ่มส่งข้อมูลผ่าน UART โปรดสังเกตว่าหมุดดิจิตอลนับเป็น 9 และเอาต์พุต FPGA จะเป็น LOW ทันทีหลังจาก N64 เสร็จสิ้น

นี่คือตัวอย่างของความล้มเหลว:

ป้อนคำอธิบายรูปภาพที่นี่

โปรดสังเกตว่าตัวนับข้ามบิต 2 และ 7! FPGA ถึงจุดสิ้นสุดรอการเริ่มต้นบิตถัดไปจาก N64 แต่ไม่มีอะไร ดังนั้น FPGA จึงหมดเวลาและกู้คืน

นี่คือ VHDL สำหรับโมดูลรับ N64 มันมีตัวนับ: 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;

ดังนั้นความคิดใด ๆ เคล็ดลับการแก้จุดบกพร่อง? เคล็ดลับในการเข้ารหัส จำกัด เครื่องจักรของรัฐ?

ในระหว่างนี้ฉันจะเล่นต่อไปเรื่อย ๆ (ในที่สุดฉันจะได้) ช่วยฉันแลกเปลี่ยน Stack คุณเป็นความหวังเดียวของฉัน!

แก้ไข

การค้นพบเพิ่มเติมในการแก้ไขจุดบกพร่องของฉันรัฐจะกระโดดจาก waitForStart กลับไปที่ waitForStop ฉันให้ค่าแต่ละรัฐด้วย waitForStart เท่ากับ '5' และ waitForStop เท่ากับ '4' ดูภาพด้านล่าง: ป้อนคำอธิบายรูปภาพที่นี่


1
ในกรณีบล็อกแรกของคุณมีบรรทัด "s_bitCount <= X" 0 ";" นั่นเป็นคำผิดหรือเปล่า?
travisbartley

@ trav1s ไม่ "X" หมายถึงเลขฐานสิบหก ดังนั้น X "0" จึงเป็น "0000" ในรูปแบบไบนารี่
Nick Williams

1
ฉันพบข้อผิดพลาดสองสามข้อในการเรียกใช้โค้ดผ่าน linter ไม่ควรใช้สัญญาณ N64RXD และ tdre ในรายการความไวของกระบวนการตามลำดับบรรทัดที่ 36
travisbartley

1
@ trav1s ขอบคุณสำหรับตัวชี้ฉันลบพารามิเตอร์เหล่านั้น คุณพูดถูกไม่จำเป็น ฉันยังมีปัญหาน่าเสียดาย ด้วยขอบเขตฉันเพิ่มสัญญาณเพื่อตรวจสอบว่าฉันอยู่ในสถานะใดด้วยเหตุผลบางอย่าง FPGA ก็กระโดดจาก "waitForStart" กลับไปที่ "waitForStop" โดยไม่มีสถานะอยู่ระหว่างนั้น! นี่คือเหตุผลว่าทำไมมันจึงไม่นับเพราะ FPGA ไม่ไปถึงสถานะที่นับบิต ดูเหมือนว่า "กระโดดกลับ" เป็นปัญหา
นิควิลเลียมส์

1
แต่การเปลี่ยน "waitForStart" -> "waitForStop" นั้นไม่ถูกต้อง ไม่มีวิธีที่จะทำให้การกระโดดในรอบเดียว ตรวจสอบอย่างใกล้ชิดเพื่อให้แน่ใจว่าไม่มีสถานะที่สั้นมากระหว่างนี้ มิฉะนั้นจะต้องมีความผิดพลาดของฮาร์ดแวร์ / การจับเวลา
travisbartley

คำตอบ:


9

ฉันไม่เห็น synchronizer ในบรรทัดข้อมูล rx

อินพุตอะซิงโครนัสทั้งหมดต้องซิงค์กับนาฬิกาตัวอย่าง มีสาเหตุสองประการคือ: ความสามารถในการแพร่กระจายและการกำหนดเส้นทาง สิ่งเหล่านี้เป็นปัญหาที่แตกต่างกัน แต่เกี่ยวเนื่องกัน

ต้องใช้เวลาสำหรับสัญญาณในการแพร่กระจายผ่านผ้า FPGA เครือข่ายนาฬิกาภายใน FPGA ได้รับการออกแบบมาเพื่อชดเชยความล่าช้า "การเดินทาง" เหล่านี้เพื่อให้ flip flops ทั้งหมดภายใน FPGA มองเห็นนาฬิกาในเวลาเดียวกัน เครือข่ายสายงานการผลิตปกติไม่มีสิ่งนี้และอาศัยกฎว่าสัญญาณทั้งหมดจะต้องมีความเสถียรสำหรับเวลาเล็กน้อยก่อนที่นาฬิกาจะเปลี่ยนและยังคงมีเสถียรภาพอยู่เล็กน้อยหลังจากนาฬิกาเปลี่ยนไป เวลาเล็กน้อยเหล่านี้รู้จักกันในชื่อการตั้งค่าและกดค้างไว้สำหรับฟล็อปฟล็อบที่กำหนด ส่วนประกอบของสถานที่และเส้นทางของ toolchain มีความเข้าใจที่ดีมากเกี่ยวกับความล่าช้าในการกำหนดเส้นทางสำหรับอุปกรณ์เฉพาะและทำให้มีการสันนิษฐานเบื้องต้นว่าสัญญาณไม่ละเมิดการตั้งค่าและเก็บเวลาของ flip flops ใน FPGA

เมื่อคุณมีสัญญาณที่ไม่ซิงโครไนซ์กับนาฬิกาสุ่มตัวอย่างคุณสามารถสิ้นสุดในสถานการณ์ที่ flip flop หนึ่งเห็นค่า "เก่า" ของสัญญาณเนื่องจากค่าใหม่ไม่มีเวลาในการเผยแพร่ ตอนนี้คุณอยู่ในสถานการณ์ที่ไม่พึงประสงค์ที่ตรรกะมองสัญญาณเดียวกันเห็นค่าที่ต่างกันสองค่า สิ่งนี้อาจทำให้เกิดการทำงานที่ผิดพลาด, เครื่องสถานะพังและยากที่จะวินิจฉัยความเสียหายทุกชนิด

อีกเหตุผลที่คุณต้องซิงโครไนซ์สัญญาณอินพุตทั้งหมดของคุณเป็นสิ่งที่เรียกว่า metastability มีปริมาณที่เขียนในเรื่องนี้ แต่สรุปวงจรตรรกะดิจิตอลอยู่ในระดับพื้นฐานที่สุดของวงจรอนาล็อก เมื่อสัญญาณนาฬิกาของคุณเพิ่มขึ้นสถานะของสายอินพุตจะถูกจับและหากอินพุตนั้นไม่อยู่ในระดับสูงหรือต่ำในเวลานั้นคุณสามารถจับค่า "in-between" ที่ไม่รู้จักได้โดยการปัดพลิกตัวอย่าง

ดังที่คุณทราบ FPGAs เป็นสัตว์ดิจิทัลและตอบสนองไม่ดีต่อสัญญาณที่ไม่สูงหรือต่ำ ยิ่งแย่ไปกว่านั้นถ้าค่าที่ไม่แน่นอนทำให้มันผ่านพ้นการฟลิปฟล็อปการสุ่มตัวอย่างและเข้าไปใน FPGA มันสามารถทำให้เกิดความแปลกประหลาดทุกประเภทเป็นส่วนใหญ่ของตรรกะในขณะนี้เห็นค่าไม่แน่นอนและพยายามทำความเข้าใจ

ทางออกคือการซิงโครไนซ์สัญญาณ ในระดับพื้นฐานที่สุดนี้หมายความว่าคุณใช้สายโซ่ของรองเท้าแตะเพื่อจับภาพอินพุต ระดับ metastable ใด ๆ ที่อาจถูกบันทึกโดย flip flop แรกและจัดการเพื่อให้ได้รับโอกาสในการแก้ไขอีกครั้งก่อนที่จะถึงตรรกะที่ซับซ้อนของคุณ สอง flip flops มักจะมากเกินพอที่จะซิงโครไนซ์อินพุต

ซิงโครไนซ์พื้นฐานดูเหมือนว่า:

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;

เชื่อมต่อฟิสิคัลพินสำหรับบรรทัดข้อมูล rx ของคอนโทรลเลอร์ N64 เข้ากับอินพุต async_in ของ synchronizer และเชื่อมต่อสัญญาณ sync_out กับอินพุต rxd ของ UART ของคุณ

สัญญาณที่ไม่ซิงโครไนซ์อาจทำให้เกิดปัญหาแปลกๆ ตรวจสอบให้แน่ใจว่าอินพุตที่เชื่อมต่อกับองค์ประกอบ FPGA ที่ไม่ได้ซิงโครไนซ์กับนาฬิกาของกระบวนการที่อ่านสัญญาณถูกซิงโครไนซ์ ซึ่งรวมถึงปุ่มกดสัญญาณ UART 'rx' และ 'cts' ... ทุกอย่างที่ไม่ได้ซิงโครไนซ์กับนาฬิกาที่ FPGA ใช้เพื่อสุ่มตัวอย่างสัญญาณ

(กัน: ฉันเขียนหน้าเว็บที่www.mixdown.ca/n64dev เมื่อหลายปีก่อนฉันเพิ่งรู้ว่าฉันทำลายลิงค์เมื่อฉันอัปเดตไซต์ครั้งล่าสุดและจะแก้ไขในตอนเช้าเมื่อฉันกลับมาที่คอมพิวเตอร์ ฉันไม่มีความคิดเลยว่ามีคนใช้เพจนี้มาก!)


ขอบคุณสำหรับคำตอบที่ยอดเยี่ยมและครอบคลุม! ฉันจะลองทำสิ่งนี้และทำให้เครื่องของฉันแข็งแกร่งขึ้น
Nick Williams

2
ที่จริงแล้วมันมีความเกี่ยวข้องกับ metastability น้อยมาก (แม้ว่ามันจะเป็นสิ่งที่น่ากังวลเช่นกัน) และทุกอย่างที่เกี่ยวข้องกับความล่าช้าของเส้นทางที่แตกต่างจากอินพุตอะซิงโครนัสไปยัง FFs ต่างๆที่เก็บบิตของตัวแปรสถานะ
Dave Tweed

คุณพูดถูก @DaveTweed; ฉันมักจะปั้นทั้งสองเข้าด้วยกันและนั่นเป็นความคิดที่ผิด
akohlsmith

ฉันได้แก้ไขคำตอบเพื่อนำความคิดเห็นของ @ DaveTweed มาพิจารณาแล้ว
akohlsmith

1
@akohlsmith น่าทึ่ง! ฉันเพิ่มตัวซิงโครไนซ์และมันก็เป็นทางออก นอกจากนี้ยังเป็นเรื่องบังเอิญที่เหลือเชื่อที่คุณเขียนหน้ามิกซ์ดาวน์ ฉันพบทรัพยากรจำนวนมากบนโปรโตคอล N64 ที่อ้างอิงบทความนั้นและฉันรู้สึกผิดหวังที่ลิงก์เสียหาย ขอบคุณสำหรับการแก้ไข
Nick Williams

6

ปัญหาของคุณคือคุณกำลังใช้สัญญาณที่ไม่ซิงโครไนซ์เพื่อตัดสินใจในเครื่องสถานะของคุณ คุณควรป้อนสัญญาณภายนอกทั้งหมดเหล่านี้ผ่านทางซิงโครไนซ์แบบ FF คู่ก่อนใช้ในเครื่องสถานะ

มันเป็นปัญหาที่ละเอียดอ่อนของเครื่องจักรสถานะที่สามารถเกิดขึ้นได้ในการเปลี่ยนสถานะใด ๆ ที่เกี่ยวข้องกับการเปลี่ยนแปลงบิตสองตัวหรือมากกว่าในตัวแปรสถานะ หากคุณใช้อินพุตที่ไม่ซิงโครไนซ์หนึ่งบิตอาจเปลี่ยนแปลงในขณะที่อีกอันหนึ่งไม่สามารถเปลี่ยนแปลงได้ สิ่งนี้จะนำคุณไปสู่สถานะที่แตกต่างจากที่ตั้งใจไว้และอาจเป็นหรือไม่เป็นสถานะทางกฎหมาย

คำสั่งสุดท้ายนั้นคือเหตุผลที่คุณควรมีตัวพิมพ์เล็ก (VHDL, when others => ...) ในคำสั่งตัวพิมพ์ใหญ่และตัวพิมพ์ใหญ่ที่นำคุณจากสถานะที่ผิดกฎหมายไปยังกฎหมาย


ใช่นี่คือข้อสรุปที่ฉันกำลังจะแยก แต่ฉันไม่ต้องการที่จะข้ามไปก่อนที่จะได้รับข้อมูลที่เพียงพอ ...
travisbartley

1
ประณามคุณเอาชนะฉันไป ฉันตำหนิการพิมพ์ของฉันทั้งหมดนี้บนแท็บเล็ต :-)
akohlsmith

@akohlsmith การเป็นปืนที่เร็วที่สุดในฝั่งตะวันออกไม่ใช่สิ่งเดียวที่สำคัญในการตอบ คำตอบของคุณมีประโยชน์และเห็นได้ชัดว่าไม่ได้โกงเนื่องจากคุณโพสต์หลังจากนั้นไม่นาน
travisbartley

ฉันเคยคิดว่าwhen others =>มันช่วย แต่มันกลับกลายเป็นว่าคุณไม่ได้รับสิ่งที่คุณอ้าง (ภายใต้ซินธิไซเซอร์ใด ๆ ที่ฉันเคยใช้) เว้นแต่คุณจะเพิ่มคุณสมบัติเพื่อให้แน่ใจว่าซินเข้าใจว่าคุณต้องการเครื่องสถานะ "ปลอดภัย" พฤติกรรมปกติคือการปรับให้เหมาะสมกับการแสดงแบบ one-hot และไม่ได้ให้ตรรกะการกู้คืน ดูxilinx.com/support/answers/40093.htmlและsynopsys.com/Company/Publications/SynopsysInsight/Pages/ ......ตัวอย่าง
Martin Thompson

ว้าว! นั่นเป็นเคล็ดลับที่ยอดเยี่ยมและใช้งานได้อย่างมีเสน่ห์
Nick Williams
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.