أخيرا ، وصلت يدي دراسة FPGAs. ثم اتضح خطأ بطريقة ما: أكتب برامج تشغيل الأجهزة لنظام التشغيل Linux ، وأبرمج وحدات التحكم الدقيقة ، وقرأت الدوائر (والتصميم قليلاً) ، وأحتاج إلى المزيد من النمو.
نظرًا لأنه يبدو لي أنه ليس من المثير للاهتمام أن يومض LED ، فقد قررت أن أفعل شيئًا بسيطًا. أي كتابة وحدات الاستقبال والإرسال لـ UART ، ودمجها داخل FPGA (في نفس الوقت فهم كيفية استخدام IP Core) ، جيدًا ، واختبارها كلها على أجهزة حقيقية.
أقول على الفور أنه لم يكن مهمة إنشاء نواة معلمة عالمية للمهمة. هذا مجرد مشروع تجريبي ، حول موضوع "الشعور بما هو FPGA وكيفية التواصل معه".
لذا ، دعنا نبدأ بجهاز الاستقبال. تم وصف الخوارزمية
جيدًا ، لذا سأكرر هنا نقاطها الرئيسية فقط.
- تردد أخذ عينات إشارة RX أكبر أربع مرات من معدل إرسال UART المطلوب.
- يعتبر شرط بدء الاستقبال هو انتقال إشارة الإدخال RX من مستوى عالٍ إلى مستوى منخفض ، إذا لم يكن الاستقبال قيد التقدم حاليًا.
- يعتبر شرط التحديد الموثوق لبت البداية هو الاحتفاظ بإشارة RX في حالة منخفضة في الساعة الثانية من تردد أخذ العينات. في الوقت نفسه ، نصل عمليًا إلى منتصف نبضة البتات ، مما يسمح لنا بمزيد من أخذ عينات من النبضات كل 4 دورات.
- في حالة وجود خطأ في بتات البداية أو التوقف ، قم بتعيين خطأ إشارة الخطأ. بناءً على ذلك ، نقوم بتشكيل إشارة fastsync ، والتي سنستخدمها في المستقبل لمزامنة جهاز الاستقبال بسرعة.
- بعد تحديد بت البداية ، نبدأ في الاستقبال المتسلسل لبتات البيانات ، بدءًا من الأصغر. يتم كتابة البيانات المستلمة في السجل مع التحول إلى اليمين بمقدار بت واحد. سيكون شرط نهاية الاستقبال هو الكشف عن بت البدء في الموضع 0 من سجل المناوبات.
- تتمثل المزامنة السريعة للمستقبل في إعادته إلى حالته الأصلية بعد اكتشاف خطأ عندما تنتقل إشارة RX إلى مستوى عالٍ (يمكن أن يكون إما إرسال منطقي "1" أو إرسال بت توقف أو حالة خاملة لخط النقل).
- إن شرط الانتهاء بنجاح من الاستقبال (القيم الصحيحة لبتات البداية والتوقف) هي الإشارة الكاملة. من ذلك (عندما يتم تسجيلها بواسطة إشارة rdclk) ، يتم إنشاء إشارة نبض جاهزة ، مما يشير إلى وجود بيانات صالحة على ناقل rxdata.
سألاحظ على الفور أنني لم أكن أرغب في تسجيل إشارة القراءة جاهزة من إشارة ساعة clk (بشكل غير متوقع ، أليس كذلك؟) حتى لا تربط سرعة معالجة البيانات اللاحقة بسعر الصرف UART. يوجد تنفيذ مماثل في وحدة الإرسال (
انظر أدناه ). مجموعة اختبار من وحدات الاستقبال والإرسال تعتمد على Intel Core Core FIFO من Intel ، مع القدرة على محاكاة سرعات مختلفة للمستهلك ومولد البيانات. القيد الوحيد هو أن تردد الساعة للمنتج والمستهلك للبيانات يجب ألا يكون أقل من تردد الساعة clk.
وحدة الاستقبال (فيريلوج)// // UART // // rxdata , ready==1 error==0. // ready 1 rdclk. // // : // rx // 2- . , . // 8 - ( 9 ). // 2 - , // . // // . // . '0' , . '1' // idle (. '1') // start- (. '0') // stop- (. '1') module uart_rx( nreset, // (, 0) clk, // UART, .. UART rx, // UART rdclk, // (rxdata, ready) rxdata, // , ready==1 ready, // rxdata ( 1) error, // ( 1) busy, // ( , 1) idle); // ( 1) input wire nreset; // (, 0) input wire clk; // , .. UART input wire rx; // UART input wire rdclk; // output wire[7:0] rxdata; output wire ready; output error; output busy; output idle; // , rdclk reg[2:0] done = 3'b000; // , rdclk //assign ready = (done == 2'b10) ? 1'b1 : 1'b0; assign ready = (done[1] && !done[0]) ? 1'b1 : 1'b0; // reg error = 1'b0; // // error // rx, . wire fastsync = (error && rx); // reg idle = 1'b1; // : // d[9] - , .. == 1 // d[8:1] - // d[0] - , .. == 0 reg[9:0] d = 10'b1xxxxxxxx1; // . 2'b10 wire[1:0] status = { d[9], d[0] }; // . wire complete = (status == 2'b10) ? 1'b1 : 1'b0; // assign rxdata = d[8:1]; // reg busy = 0; // rx reg[1:0] cnt; always @(posedge clk, negedge nreset) begin if(!nreset) begin rxreset(); end else begin if(fastsync) begin rxreset(); end else begin if(busy == 1'b1) begin // -, rx if(cnt == 2'd0) begin // // // (.. ) d <= { rx, d[9:1] }; if(d[1] == 1'b0) begin // , busy <= 1'b0; // error <= (rx == 1'b1) ? 1'b0 : 1'b1; end else begin // if(rx && (d == 10'b1111111111)) begin // busy <= 1'b0; // error <= 1'b1; end else begin // // - - cnt <= 2'd3; end end end else begin // - cnt <= cnt - 2'd1; end end else begin // if(!error) begin // , if(rx == 1'b0) begin // - busy <= 1'b1; // . 1, .. // d[0]==0 d <= 10'b1111111111; // rx 1/2 // 1- - // 2- - (cnt 0) cnt <= 2'd0; // .. , idle <= 1'b0; end else begin // idle <= 1'b1; end end end end end end task rxreset; begin // error <= 1'b0; // (!?) idle <= 1'b1; // busy <= 0; // -, complete d <= 10'b1xxxxxxxx1; end endtask always @(negedge rdclk, negedge nreset) begin if(!nreset) begin done <= 3'b000; end else begin // complete. // ready // complete 0 1 rdclk. done <= { complete, done[2:1] }; end end endmodule
نظرًا لأن إشارة الإدخال RX غير متزامنة و (ربما) غير مستقرة ، تم توصيل
عنصر الأغلبية أمام وحدة الاستقبال في
الوحدة الرئيسية . العنصر مكتوب أيضًا في Verilog ، لكن رمزه لا معنى له هنا. بدلاً من ذلك ، صورة جميلة لعنصر مركب.
المخطط المركب لعنصر الأغلبية وحدة الإرسال أبسط ، وآمل ألا تحتاج إلى تعليقات إضافية.
وحدة الإرسال (التخصيصات غير المحظورة وغير المحظورة في الداخل دائمًا) // // UART // // : // clk - 4 , // rdclk - txdata, write, fetch. .. clk // txdata - , write/fetch // write - (1=) // fetch - (1=) // tx - UART // idle - (1=, ) // // FIFO dcfifo_component.lpm_showahead = "ON" module uart_tx( nreset, // (, 0) clk, // UART, .. UART rdclk, // txdata, // write, // ( 1) idle, // ( 1) fetch, // , rdclk tx); // UART input wire nreset; // (, 0) input wire clk; // UART input wire rdclk; input wire[7:0] txdata; input wire write; output wire idle; output fetch; output tx; // reg tx = 1'b1; reg fetch = 1'b0; // 4 reg[1:0] div4 = 2'd0; // : reg[3:0] s = 4'd10; // assign idle = (s == 4'd10); // reg[7:0] d; // reg sendstart; // reg canfetch; // , clk reg gotdata = 1'b0; // clock domains reg[2:0] sync = 3'b000; // rdclk write reg wr = 1'b0; // getdata==1 // nextdata // gotdata==1. , // . // gotdata getdata. always @(posedge rdclk, negedge nreset) begin if(!nreset) begin wr <= 1'b0; sync <= 3'b000; // fetch <= 1'b0; end else begin // write wr <= write; // sync <= { gotdata, sync[2:1] }; // gotdata // . // . fetch <= (sync[1] && !sync[0]) ? 1'b1 : 1'b0; end end always @(posedge clk, negedge nreset) begin if(!nreset) begin // div4 <= 2'd0; s <= 4'd10; gotdata <= 1'b0; end else begin // sendstart = 1'b0; // canfetch = wr; if(div4 == 2'd0) begin case(s) 4'd0: begin // sendstart = 1'b1; // , canfetch = 1'b0; end 4'd9: begin // tx <= 1'b1; end 4'd10: begin // idle, end default: begin // , tx <= d[0]; // d <= { 1'b0, d[7:1] }; // , canfetch = 1'b0; end endcase end else begin // div4 <= div4 - 2'd1; if(s < 4'd9) begin // 9 ! canfetch = 1'b0; end end if(canfetch) begin // , d <= txdata; // gotdata <= 1'b1; if(idle ) begin // idle - sendstart = 1'b1; end else begin // s <= 4'd0; end end if(gotdata) begin // , gotdata <= 1'b0; end if(sendstart) begin // tx <= 1'b0; // s <= 4'd1; // div4 <= 2'd3; end else begin if(div4 == 2'd0) begin if(s < 4'd10) begin // s <= s + 4'd1; // div4 <= 2'd3; end end end end end endmodule
تسبب تطبيق المرسل أعلاه في مناقشة ساخنة في التعليقات. على الرغم من ذلك ، يبدو أن الجميع يتفقون على أنه من الممكن القيام بذلك ، ولكن بعناية. من أجل راحة بالك ، تمت إعادة كتابة الوحدة مع مراعاة جميع الإرشادات المذكورة. في رأيي ، إنها ليست أكثر تعقيدًا بكثير من سابقتها من وجهة نظر الإدراك البشري للخوارزمية المنفذة.
وحدة الإرسال (فيريلوج ، صحيحة أيديولوجيا) // // UART // // : // clk - 4 , // rdclk - txdata, write, fetch. .. clk // txdata - , write/fetch // write - (1=) // fetch - (1=) // tx - UART // idle - (1=, ) // // FIFO dcfifo_component.lpm_showahead = "ON" module uart_tx( nreset, // (, 0) clk, // UART, .. UART rdclk, // txdata, // write, // ( 1) idle, // ( 1) fetch, // , rdclk tx); // UART input wire nreset; // (, 0) input wire clk; // UART input wire rdclk; input wire[7:0] txdata; input wire write; output wire idle; output fetch; output tx; // reg tx = 1'b1; reg fetch = 1'b0; // 4 reg[1:0] div4 = 2'd0; // : reg[3:0] s = 4'd10; // assign idle = (s == 4'd10); // reg[7:0] d; // reg sendstart; // reg canfetch; // , clk reg gotdata = 1'b0; // clock domains reg[2:0] sync = 3'b000; // rdclk write reg wr = 1'b0; // getdata==1 // nextdata // gotdata==1. , // . // gotdata getdata. always @(posedge rdclk, negedge nreset) begin if(!nreset) begin wr <= 1'b0; sync <= 3'b000; // fetch <= 1'b0; end else begin // write wr <= write; // sync <= { gotdata, sync[2:1] }; // gotdata // . // . fetch <= (sync[1] && !sync[0]) ? 1'b1 : 1'b0; end end // (?) always // sendstart canfetch always @(*) begin // sendstart = 1'b0; if(nreset) begin // canfetch = wr; if(div4 == 2'd0) begin case(s) 4'd0: begin // sendstart = 1'b1; // , canfetch = 1'b0; end 4'd9: begin // end 4'd10: begin // idle, end default: begin // // , canfetch = 1'b0; end endcase end else begin if(s < 4'd9) begin // 9 ! canfetch = 1'b0; end end if(canfetch && idle) begin // idle - sendstart = 1'b1; end end else begin // reset canfetch = 1'b0; end end always @(posedge clk, negedge nreset) begin if(!nreset) begin // div4 <= 2'd0; s <= 4'd10; gotdata <= 1'b0; end else begin if(div4 == 2'd0) begin case(s) 4'd0: begin // sendstart end 4'd9: begin // tx <= 1'b1; end 4'd10: begin // idle, end default: begin // , tx <= d[0]; // d <= { 1'b0, d[7:1] }; end endcase end else begin // div4 <= div4 - 2'd1; end if(canfetch) begin // , d <= txdata; // gotdata <= 1'b1; if(!idle /*s == 4'd10*/) begin // s <= 4'd0; end end else begin // , gotdata <= 1'b0; end if(sendstart) begin // tx <= 1'b0; // s <= 4'd1; // div4 <= 2'd3; end else begin if((div4 == 2'd0) && (s < 4'd10)) begin // s <= s + 4'd1; // div4 <= 2'd3; end end end end endmodule
لاختبار جهاز الاستقبال وجهاز الإرسال ، تم كتابة الوحدة الرئيسية على الركبة. أطلب منك ألا تقسم على ذلك ، فأنا أعرف أخطاء التصميم (nreset الخارجية غير المتزامنة للإشارة ، وعدم إعادة ضبط FIFO ، إلخ). ولكن لغرض التحقق من الوظائف ، فهي ليست مهمة.
يتم تسجيل لوحة تجريبي الخاصة بي من مصدر إشارة 50 ميجا هرتز. لذلك ، في الوحدة الرئيسية ، استخدمت PLL ، عند الإخراج C0 الذي شكلت ترددًا للعمل مع UART (1.8432Mhz ، في الواقع 1.843198Mhz) ، وللمرح ، شكلت ترددًا 300Mhz (خرج c1 PLL) على مدار الساعة لمحاكاة دائرة معالجة المعلومات.
الوحدة الرئيسية (فيريلوج) // // .. UART UART, // FPGA, // FIFO IP CORE DCFIFO. // //NB! // SDC- ! // ( if , // ). module uart( input wire clk50mhz, // 50Mhz input wire nreset, // input wire rx, // UART output wire tx, // UART output wire overflow ); // 1.8432Mhz ( 1.843198Mhz) wire clk_1843200; // 1.2288Mhz ( 1.228799Mhz) //wire clk_1228800; // 300Mhz, PLL wire clk300mhz; // UART uart_pll pll50mhz(.inclk0(clk50mhz), .c0(clk_1843200) /*, .c1(clk_1228800)*/, .c1(clk300mhz)); // UART 38400 // (1843200/38400)/4 = 12 ('b1100). // UART 57600 // (1843200/57600)/4 = 8 // UART 115200 // (1843200/115200)/4 = 4 // UART 230400 // (1843200/230400)/4 = 2 // UART 460800 // (1843200/460800)/4 = 1 (.. !) // UART wire uart_baud4; // // .data 1 . // uart_baud4 .clock/ // uart_baud4 .clock uart_osc uart_osc_1(.clock(clk_1843200), .data(5'd2/*5'd4*//*5'd12*/-5'd1), .sload(uart_baud4), .cout(uart_baud4)); //wire uart_baud4 = clk_1843200; // wire rxf; // mfilter mfilter_rx(.clk(clk50mhz /*clk_1843200*/), .in(rx), .out(rxf)); //wire rxf = rx; // wire[7:0] rxdata; wire rxready; wire error; uart_rx uart_rx_1(.nreset(nreset), .clk(uart_baud4), .rx(rxf), .rdclk(clk300mhz /*clk50mhz*/ /*clk_1843200*/), .rxdata(rxdata), .ready(rxready), .error(error)); wire[7:0] txdata; // , , wire txnone; // , wire fetch; wire full; // // uart_baud4 // clk50mhz uart_fifo_rx uart_fifo_rx_1(.data(rxdata), .rdclk(clk300mhz /*clk50mhz*/ /*clk_1843200*/ /*uart_baud4*/), .rdreq(fetch), .wrclk(clk300mhz /*clk50mhz*/ /*clk_1843200*/ /*uart_baud4*/), .wrreq(rxready), .rdempty(txnone), .q(txdata), .wrfull(full)); assign overflow = ~error; uart_tx uart_tx_1(.nreset(nreset), .clk(uart_baud4), .rdclk(clk300mhz /*clk50mhz*/ /*clk_1843200*/), .txdata(txdata), .write(~txnone), .fetch(fetch), .tx(tx)); endmodule
للاختبار ، تم استخدام مولد حركة testcom من Zelax. للأسف ، رفض مهايئ USB / UART العمل بسرعات أعلى من 230400 بت في الثانية ، لذلك تم إجراء جميع الاختبارات بهذه السرعة.
نتيجة الاختبار بتصفية إشارة الإدخال RX باستخدام عنصر الأغلبية
Signal Tap حالة الإشارة

وهنا تمت إزالة عنصر الأغلبية من المدخل.ولكن ماذا ، كيف يمكنني محاكاة الأخطاء العشوائية عند التحقق من مخطط المزامنة السريع؟

Signal Tap حالة الإشارة

ملاحظة
عذرًا ، لم ألتحق بدورات Quartus ولم يكن هناك أحد لطرح الأسئلة. ما تعثرت فيه على نفسي وما حذرت FPGAs الناشئة عنه: تأكد من إنشاء ملف SDC في المشروع ووصف ترددات الساعة فيه. نعم ، يتم إنشاء المشروع بدونه ، على الرغم من أن التحذيرات قد تظهر إذا لم يتمكن المركب من تحديد خصائص التوقيت للساعة. في البداية تجاهلتهم حتى قتلت نصف يوم لتحديد مشكلة السبب في وحدة الاستقبال الخاصة بي عند تنفيذ الكود
if(rx == 1'b0) begin busy <= 1'b1; d <= 10'b1111111111; cnt <= 2'd0; idle <= 1'b0; end else begin
تم ضبط الإشارات المشغولة والخاملة بشكل صحيح ، ولكن لم تتغير محتويات التسجيل d في بعض الأحيان.
ملحق: ملف SDC للمشروع set_time_format -unit ns -decimal_places 3 # 50Mhz, (50/50 duty cycle) create_clock -name {clk50mhz} -period 20.000 -waveform { 0.000 10.000 } ############################################################################## Now that we have created the custom clocks which will be base clocks,# derive_pll_clock is used to calculate all remaining clocks for PLLs derive_pll_clocks -create_base_clocks derive_clock_uncertainty # PLL ? # altpll_component.clk0_divide_by = 15625, # altpll_component.clk0_duty_cycle = 50, # altpll_component.clk0_multiply_by = 576, # altpll_component.clk0_phase_shift = "0", #create_generated_clock -name clk_1843200 -source [get_ports {clk50mhz}] -divide_by 15625 -multiply_by 576 -duty_cycle 50 -phase 0 -offset 0 # baudrate=38400 # 1/4 , .. duty=(1/4)*100=25% #create_generated_clock -name uart_baud4 -source [get_nets {pll50mhz|altpll_component|auto_generated|wire_pll1_clk[0]}] -divide_by 12 -duty_cycle 25 [get_nets {uart_osc_1|LPM_COUNTER_component|auto_generated|counter_reg_bit[0]}] # baudrate=230400 # 1/4 , .. duty=(1/4)*100=50% create_generated_clock -name uart_baud4 -source [get_nets {pll50mhz|altpll_component|auto_generated|wire_pll1_clk[0]}] -divide_by 2 -duty_cycle 25 [get_nets {uart_osc_1|LPM_COUNTER_component|auto_generated|counter_reg_bit[0]}] # baudrate=460800 # 1, PLL, .
شكرا جزيلا لكل من كتب تعليقات على المقال! من بين هذه ، جمعت الكثير من المعلومات المفيدة ، على الرغم من أنها متضاربة إلى حد ما في بعض الأحيان. في رأيي ، فإن قيمتها أكبر بكثير من تنفيذ الخوارزمية الموصوفة أعلاه. ولا شك أنها ستكون مفيدة لأولئك الذين يتجرأون أيضًا على الصعود إلى عالم FPGA.قائمة الروابط الخارجية
- جهاز الإرسال والاستقبال غير المتزامن العالمي (ويكيبيديا)
- عنصر الأغلبية (ويكيبيديا)