本(běn)文(wén)摘自(zì)网(wǎng)絡班学員陳同(tóng)学的(de)博客:https://www.cnblogs.com/moluoqishi/p/7280191.html
明(míng)德揚FPGA网(wǎng)絡班:http://www.minyingyiyuan.com/product/670.html
明(míng)德揚FPGA就(jiù)業班:http://www.minyingyiyuan.com/product/672.html
本(běn)篇(piān)博文(wén)設計(jì)思(sī)想(xiǎng)及(jí)代(dài)碼規範均借(jiè)鉴明(míng)德揚至(zhì)簡設計(jì)法,加上(shàng)些自(zì)己的(de)理解(jiě)和(hé)靈活應(yìng)用(yòng),希望对(duì)自(zì)己和(hé)大家(jiā)都有(yǒu)所(suǒ)幫助。核心(xīn)要(yào)素依然是(shì)計(jì)數器和(hé)狀态标(biāo)志位邏輯相配合的(de)設計(jì)方(fāng)式。在(zài)最(zuì)簡單的(de)串口(kǒu)收(shōu)發(fà)一(yī)字(zì)节(jié)數據(jù)功能(néng)基礎上(shàng),实現(xiàn)字(zì)符串收(shōu)發(fà)。
上(shàng)一(yī)篇(piān)博文(wén)中(zhōng)詳细(xì)設計(jì)了(le)串口(kǒu)發(fà)送模块(kuài),串口(kǒu)接收(shōu)模块(kuài)設計(jì)思(sī)想(xiǎng)基本(běn)相同(tóng),只(zhī)不(bù)过(guò)将總(zǒng)線(xiàn)的(de)下(xià)降沿作为(wèi)數據(jù)接收(shōu)的(de)開(kāi)始条(tiáo)件(jiàn)。需要(yào)注意(yì)有(yǒu)两(liǎng)點(diǎn):其一(yī),串口(kǒu)接收(shōu)中(zhōng)读(dú)取(qǔ)每一(yī)位bit數據(jù)时(shí),最(zuì)好(hǎo)在(zài)每一(yī)位的(de)中(zhōng)間(jiān)點(diǎn)取(qǔ)值,这(zhè)樣(yàng)數據(jù)較为(wèi)準确。第(dì)二(èr),串口(kǒu)接收(shōu)的(de)比特(tè)數據(jù)屬于(yú)异(yì)步數據(jù),因(yīn)此(cǐ)需要(yào)打(dǎ)两(liǎng)拍做同(tóng)步处理,避免亞稳态的(de)出(chū)現(xiàn)。關(guān)于(yú)串口(kǒu)接收(shōu)的(de)設計(jì)细(xì)节(jié)这(zhè)里(lǐ)不(bù)再贅述,不(bù)明(míng)之处請參考串口(kǒu)發(fà)送模块(kuài)設計(jì)思(sī)路(lù)。串口(kǒu)接收(shōu)代(dài)碼如(rú)下(xià):
由(yóu)于(yú)思(sī)路(lù)代(dài)碼與(yǔ)串口(kǒu)發(fà)送非(fēi)常詳盡,这(zhè)里(lǐ)省(shěng)去(qù)仿真(zhēn),單独在(zài)線(xiàn)調試的(de)过(guò)程,将验(yàn)證工作放(fàng)在(zài)總(zǒng)體(tǐ)設計(jì)中(zhōng)。到(dào)目前(qián)为(wèi)止,串口(kǒu)的(de)一(yī)字(zì)节(jié)數據(jù)發(fà)送和(hé)接收(shōu)功能(néng)已經(jīng)实現(xiàn)。下(xià)面(miàn)我(wǒ)们(men)在(zài)此(cǐ)基礎上(shàng)做一(yī)个(gè)完整的(de)小項目。功能(néng)定(dìng)为(wèi):FPGA每隔3s向(xiàng)PC發(fà)送一(yī)个(gè)準備就(jiù)緒(等待)指令“wait”,再等待區(qū)間(jiān)內(nèi)PC端可(kě)以(yǐ)發(fà)送一(yī)个(gè)由(yóu)#号(hào)結尾且长度(dù)小于(yú)等于(yú)10个(gè)字(zì)符的(de)字(zì)符串,當FPGA在(zài)等待區(qū)間(jiān)內(nèi)收(shōu)到(dào)了(le)全(quán)部(bù)字(zì)符串,即收(shōu)到(dào)#号(hào),則等待时(shí)間(jiān)到(dào)达(dá)後(hòu)轉(zhuǎn)而(ér)發(fà)送收(shōu)到(dào)的(de)字(zì)符串实現(xiàn)环(huán)回(huí)功能(néng)。之後(hòu)如(rú)果(guǒ)沒(méi)有(yǒu)再收(shōu)到(dào)字(zì)符串再次(cì)發(fà)送“wait”字(zì)符串,循环(huán)往複。
現(xiàn)在(zài)串口(kǒu)發(fà)送接收(shōu)8位數據(jù)的(de)功能(néng)已經(jīng)实現(xiàn),而(ér)一(yī)个(gè)字(zì)符即为(wèi)8位數據(jù)(詳見(jiàn)ASCII碼表(biǎo)),那(nà)麼(me)現(xiàn)在(zài)的(de)工作重(zhòng)心(xīn)已将從發(fà)送接收(shōu)字(zì)符轉(zhuǎn)到(dào)如(rú)何实現(xiàn)字(zì)符串的(de)收(shōu)發(fà)和(hé)切(qiè)換上(shàng)。很明(míng)顯,需要(yào)一(yī)个(gè)控制模块(kuài)完成(chéng)上(shàng)述邏輯,合理調配它(tā)的(de)部(bù)下(xià):串口(kǒu)接收(shōu)模块(kuài)和(hé)串口(kǒu)發(fà)送模块(kuài)。我(wǒ)们(men)来(lái)一(yī)起分(fēn)析控制模块(kuài)的(de)实現(xiàn)细(xì)节(jié):
先(xiān)来(lái)说(shuō)發(fà)送固定(dìng)字(zì)符串的(de)功能(néng),字(zì)符串即是(shì)多(duō)个(gè)字(zì)符的(de)集合,所(suǒ)以(yǐ)这(zhè)里(lǐ)需要(yào)一(yī)个(gè)字(zì)符發(fà)送計(jì)數器,在(zài)每次(cì)串口(kǒu)發(fà)送模块(kuài)發(fà)送完一(yī)个(gè)字(zì)符後(hòu)加1,從而(ér)索引存儲在(zài)FPGA內(nèi)部(bù)的(de)字(zì)符串。说(shuō)到(dào)存儲字(zì)符串,我(wǒ)们(men)需要(yào)一(yī)个(gè)存儲結構,它(tā)能(néng)将多(duō)个(gè)比特(tè)作为(wèi)一(yī)个(gè)整體(tǐ)進(jìn)行索引,这(zhè)樣(yàng)才能(néng)通(tòng)过(guò)計(jì)數器找(zhǎo)到(dào)一(yī)整个(gè)字(zì)符,所(suǒ)以(yǐ)要(yào)用(yòng)到(dào)存儲器的(de)結構
。上(shàng)面(miàn)说(shuō)要(yào)每隔一(yī)段(duàn)时(shí)間(jiān)發(fà)送一(yī)个(gè)字(zì)符串,很明(míng)顯需要(yào)等待时(shí)間(jiān)計(jì)數器和(hé)相應(yìng)的(de)标(biāo)志位来(lái)區(qū)分(fēn)等待區(qū)間(jiān)和(hé)發(fà)送區(qū)間(jiān)。至(zhì)于(yú)字(zì)符串的(de)接收(shōu),其实是(shì)一(yī)个(gè)道(dào)理:當然也(yě)需要(yào)对(duì)接收(shōu)數據(jù)計(jì)數,这(zhè)樣(yàng)才能(néng)知道(dào)接收(shōu)到(dào)字(zì)符串的(de)长度(dù)。等待區(qū)間(jiān)內(nèi)若收(shōu)到(dào)結束(shù)符#号(hào),則在(zài)等待結束(shù)後(hòu)由(yóu)發(fà)送固定(dìng)字(zì)符轉(zhuǎn)而(ér)将接收(shōu)的(de)字(zì)符發(fà)送出(chū)去(qù)。其關(guān)鍵也(yě)是(shì)在(zài)于(yú)通(tòng)过(guò)接收(shōu)計(jì)數器对(duì)接收(shōu)緩存進(jìn)行索引。至(zhì)此(cǐ),控制模块(kuài)已設計(jì)完畢。你会(huì)發(fà)現(xiàn),上(shàng)述功能(néng)僅僅需要(yào)幾(jǐ)个(gè)計(jì)數器和(hé)一(yī)些标(biāo)志位之間(jiān)的(de)邏輯即可(kě)完成(chéng),如(rú)此(cǐ)簡單的(de)流程不(bù)需要(yào)使用(yòng)的(de)狀态機(jī)。之前(qián)的(de)按鍵檢测模块(kuài)等下(xià)也(yě)用(yòng)这(zhè)種(zhǒng)設計(jì)思(sī)想(xiǎng)加以(yǐ)化(huà)簡。廢話(huà)不(bù)多(duō)说(shuō),上(shàng)代(dài)碼:
`timescale 1ns / 1ps module uart_ctrl( input clk, input rst_n, input key_in, input [7:0] data_in, input data_in_vld, input tx_finish, output reg [2:0] baud, output reg [7:0] data_out, output reg tx_en ); parameter WAIT_TIME = 600_000_000;//3s integer i; reg [7:0] store [4:0];//發(fà)送存儲 reg [7:0] str_cnt; reg [7:0] N; reg [7:0] rx_cnt; reg [7:0] rx_cnt_tmp; reg [7:0] rx_num; reg [31:0] wait_cnt; (*mark_debug = "true"*)reg wait_flag; reg rec_flag; reg [7:0] rx_buf [9:0]; wire add_str_cnt,end_str_cnt; wire add_wait_cnt,end_wait_cnt; wire add_rx_cnt,end_rx_cnt; wire end_signal; wire din_vld; //按鍵实現(xiàn)波(bō)特(tè)率的(de)切(qiè)換 always@(posedge clk or negedge rst_n)begin if(!rst_n) baud <= 3'b000; else if(key_in)begin if(baud == 3'b100) baud <= 3'b000; else baud <= baud + 1'b1; end end always@(posedge clk or negedge rst_n)begin if(!rst_n)begin store[0] <= 0; store[1] <= 0; store[2] <= 0; store[3] <= 0; store[4] <= 0; end else begin store[0] <= "w";//8'd119;//w store[1] <= "a";//8'd97;//a store[2] <= "i";//8'd105;//i store[3] <= "t";//8'd116;//t store[4] <= " ";//8'd32;//空格 end end //發(fà)送計(jì)數器區(qū)分(fēn)發(fà)送哪一(yī)个(gè)字(zì)符 always@(posedge clk or negedge rst_n)begin if(!rst_n) str_cnt <= 0; else if(add_str_cnt)begin if(end_str_cnt) str_cnt <= 0; else str_cnt <= str_cnt + 1'b1; end end assign add_str_cnt = tx_finish; assign end_str_cnt = add_str_cnt && str_cnt == N - 1; //接收(shōu)計(jì)數器 always@(posedge clk or negedge rst_n)begin if(!rst_n) rx_cnt <= 0; else if(add_rx_cnt)begin if(end_rx_cnt) rx_cnt <= 0; else rx_cnt <= rx_cnt + 1'b1; end end assign add_rx_cnt = din_vld; assign end_rx_cnt = add_rx_cnt && ((rx_cnt == 10 - 1) || data_in == "#");//接收(shōu)到(dào)的(de)字(zì)符串最(zuì)长为(wèi)10个(gè) assign din_vld = data_in_vld && wait_flag; //計(jì)數器計(jì)时(shí)等待时(shí)間(jiān)1s always@(posedge clk or negedge rst_n)begin if(!rst_n) wait_cnt <= 0; else if(add_wait_cnt)begin if(end_wait_cnt) wait_cnt <= 0; else wait_cnt <= wait_cnt + 1'b1; end end assign add_wait_cnt = wait_flag; assign end_wait_cnt = add_wait_cnt && wait_cnt == WAIT_TIME - 1; //等待标(biāo)志位 always@(posedge clk or negedge rst_n)begin if(!rst_n) wait_flag <= 1; else if(end_wait_cnt) wait_flag <= 0; else if(end_str_cnt) wait_flag <= 1; end always@(posedge clk or negedge rst_n)begin if(!rst_n) rx_num <= 0; else if(end_signal) rx_num <= rx_cnt + 1'b1; end assign end_signal = add_rx_cnt && data_in == "#"; //接收(shōu)緩存 always@(posedge clk or negedge rst_n)begin if(!rst_n) for(i = 0;i < 10;i = i + 1)begin rx_buf[i] <= 0; end else if(din_vld && !end_signal) rx_buf[rx_cnt] <= data_in; else if(end_wait_cnt) rx_buf[rx_num - 1] <= " "; else if(end_str_cnt) for(i = 0;i < 10;i = i + 1)begin rx_buf[i] <= 0; end end //檢测有(yǒu)效數據(jù) always@(posedge clk or negedge rst_n)begin if(!rst_n) rec_flag <= 0; else if(end_signal) rec_flag <= 1; else if(end_str_cnt) rec_flag <= 0; end always@(*)begin if(rec_flag) N <= rx_num; else N <= 5; end //發(fà)送數據(jù)給(gěi)串口(kǒu)發(fà)送模块(kuài) always@(*)begin if(rec_flag) data_out <= rx_buf[str_cnt]; else data_out <= store[str_cnt]; end //等待結束(shù)後(hòu)發(fà)送使能(néng)有(yǒu)效 always@(posedge clk or negedge rst_n)begin if(!rst_n) tx_en <= 0; else if(end_wait_cnt || (add_str_cnt && str_cnt < N - 1 && !wait_flag)) tx_en <= 1; else tx_en <= 0; end endmodule
控制模块(kuài)設計(jì)結束(shù),我(wǒ)们(men)通(tòng)过(guò)仿真(zhēn)验(yàn)證預期(qī)功能(néng)是(shì)否实現(xiàn)。这(zhè)里(lǐ)僅测試最(zuì)重(zhòng)要(yào)的(de)控制模块(kuài),由(yóu)于(yú)需要(yào)用(yòng)到(dào)發(fà)送模块(kuài)的(de)tx_finish信(xìn)号(hào),在(zài)测試文(wén)件(jiàn)中(zhōng)同(tóng)时(shí)例化(huà)控制模块(kuài)和(hé)串口(kǒu)發(fà)送模块(kuài)。需要(yào)注意(yì)在(zài)仿真(zhēn)前(qián)将控制模块(kuài)設为(wèi)頂层。测試文(wén)件(jiàn):
`timescale 1ns / 1ps module uart_ctrl_tb; reg clk,rst_n; reg key_in; reg [7:0] data_in; reg data_in_vld; wire tx_finish; wire [2:0] baud; wire [7:0] data_tx; wire tx_en; uart_ctrl uart_ctrl( .clk(clk), .rst_n(rst_n), .key_in(key_in), .data_in(data_in), .data_in_vld(data_in_vld), .tx_finish(tx_finish), .baud(baud), .data_out(data_tx), .tx_en(tx_en) ); uart_tx_module uart_tx_module( .clk(clk), .rst_n(rst_n), .baud_set(baud), .send_en(tx_en), .data_in(data_tx), .data_out(), .tx_done(tx_finish) ); integer i; parameter CYC = 5, RST_TIME = 2; defparam uart_ctrl.WAIT_TIME = 2000_000; initial begin clk = 0; forever #(CYC / 2.0) clk = ~clk; end initial begin rst_n = 1; #1; rst_n = 0; #(CYC * RST_TIME); rst_n = 1; end initial begin #1; key_in = 0; data_in = 0; data_in_vld = 0; #(CYC * RST_TIME); #10_000; #5_000_000; data_in = 8'h80; repeat(4)begin data_in_vld = 1; data_in = data_in + 1; #(CYC * 1); data_in_vld = 0; end data_in_vld = 1; data_in = 8'd32; #(CYC * 1); data_in_vld = 0; #10_000; $stop; end endmodule
本(běn)次(cì)設計(jì)先(xiān)采用(yòng)VIVADO自(zì)带(dài)仿真(zhēn)工具Vivado Simulator。虽然速度(dù)有(yǒu)些慢(màn),不(bù)过(guò)对(duì)簡單的(de)設計(jì)来(lái)说(shuō)體(tǐ)验(yàn)區(qū)别不(bù)明(míng)顯,而(ér)且用(yòng)起来(lái)很方(fāng)便簡單,适合新手(shǒu)。观察行为(wèi)仿真(zhēn)波(bō)形:
可(kě)以(yǐ)看(kàn)到(dào)波(bō)形符合預期(qī)功能(néng),成(chéng)功将串口(kǒu)接收(shōu)到(dào)的(de)129 130 131 132 32五(wǔ)个(gè)數據(jù)通(tòng)过(guò)串口(kǒu)环(huán)回(huí),在(zài)沒(méi)有(yǒu)收(shōu)到(dào)有(yǒu)效字(zì)符串时(shí)發(fà)送“wait”字(zì)符串对(duì)應(yìng)的(de)ASCII碼十(shí)進(jìn)制數值。如(rú)代(dài)碼有(yǒu)問(wèn)題(tí)修改代(dài)碼并保存後(hòu)只(zhī)需按下(xià)仿真(zhēn)界面(miàn)上(shàng)方(fāng)仿真(zhēn)工具欄中(zhōng)重(zhòng)新Relaunch Simulation按鈕,開(kāi)發(fà)工具将自(zì)動(dòng)将修改後(hòu)的(de)代(dài)碼更(gèng)新到(dào)仿真(zhēn)环(huán)境中(zhōng)并重(zhòng)新開(kāi)始運行仿真(zhēn):
在(zài)上(shàng)述控制模块(kuài)中(zhōng),我(wǒ)加入(rù)了(le)根(gēn)據(jù)按鍵按下(xià)次(cì)數調整常用(yòng)波(bō)特(tè)率的(de)功能(néng),因(yīn)此(cǐ)需要(yào)例化(huà)按鍵消抖模块(kuài)。剩下(xià)的(de)工作只(zhī)需建立頂层文(wén)件(jiàn),把各(gè)个(gè)模块(kuài)之間(jiān)信(xìn)号(hào)連(lián)接起来(lái)。好(hǎo)像沒(méi)什麼(me)可(kě)说(shuō)的(de)了(le),相信(xìn)大家(jiā)都能(néng)看(kàn)懂,以(yǐ)下(xià)是(shì)頂层模块(kuài)
1 `timescale 1ns / 1ps 2 3 module send_data_top( 4 input sys_clk_p, 5 input sys_clk_n, 6 input rst_n, 7 input key, 8 9 output bit_tx, 10 output tx_finish_led, 11 12 input bit_rx, 13 output rx_finish_led 14 ); 15 16 wire tx_done,rx_done; 17 (*mark_debug = "true"*)wire data_rx_vld; 18 (*mark_debug = "true"*)wire [7:0] data_rx_byte; 19 wire key_signal; 20 wire [2:0] baud; 21 wire [7:0] data_tx; 22 (*mark_debug = "true"*)wire send_start; 23 24 // 差分(fēn)时(shí)鐘(zhōng)轉(zhuǎn)單端时(shí)鐘(zhōng) 25 // IBUFGDS是(shì)IBUFG差分(fēn)形式,當信(xìn)号(hào)從一(yī)对(duì)差分(fēn)全(quán)局(jú)时(shí)鐘(zhōng)引脚輸入(rù)时(shí),必須使用(yòng)IBUFGDS作为(wèi)全(quán)局(jú)时(shí)鐘(zhōng)輸入(rù)緩沖 26 wire sys_clk_ibufg; 27 IBUFGDS # 28 ( 29 .DIFF_TERM ("FALSE"), 30 .IBUF_LOW_PWR ("FALSE") 31 ) 32 u_ibufg_sys_clk 33 ( 34 .I (sys_clk_p), //差分(fēn)时(shí)鐘(zhōng)的(de)正(zhèng)端輸入(rù),需要(yào)和(hé)頂层模块(kuài)的(de)端口(kǒu)直(zhí)接連(lián)接 35 .IB (sys_clk_n), // 差分(fēn)时(shí)鐘(zhōng)的(de)負端輸入(rù),需要(yào)和(hé)頂层模块(kuài)的(de)端口(kǒu)直(zhí)接連(lián)接 36 .O (sys_clk_ibufg) //时(shí)鐘(zhōng)緩沖輸出(chū) 37 ); 38 39 key_jitter key_jitter 40 ( 41 .clk(sys_clk_ibufg), 42 .rst_n(rst_n), 43 44 .key_i(key), 45 .key_vld(key_signal) 46 ); 47 48 uart_ctrl uart_ctrl( 49 .clk(sys_clk_ibufg), 50 .rst_n(rst_n), 51 .key_in(key_signal), 52 53 .data_in(data_rx_byte), 54 .data_in_vld(data_rx_vld), 55 .tx_finish(tx_done), 56 .baud(baud), 57 .data_out(data_tx), 58 .tx_en(send_start) 59 ); 60 61 62 uart_tx uart_tx( 63 .clk(sys_clk_ibufg), 64 .rst_n(rst_n), 65 .baud_set(baud),//[2:0] 66 .send_en(send_start), 67 .data_in(data_tx),//[7:0] 68 69 .data_out(bit_tx), 70 .tx_done(tx_done)); 71 72 assign tx_finish_led = !tx_done; 73 74 uart_rx uart_rx( 75 .clk(sys_clk_ibufg), 76 .rst_n(rst_n), 77 .baud_set(baud), 78 .din_bit(bit_rx), 79 80 .data_byte(data_rx_byte), 81 .dout_vld(data_rx_vld) 82 ); 83 84 assign rx_finish_led = !data_rx_vld; 85 86 endmodule
看(kàn)下(xià)整體(tǐ)結構图(tú)吧,很清(qīng)晰,也(yě)确認信(xìn)号(hào)連(lián)接沒(méi)有(yǒu)犯低級錯誤
确認功能(néng)沒(méi)有(yǒu)問(wèn)題(tí)之後(hòu)添加約束(shù)文(wén)件(jiàn):
然後(hòu)步驟同(tóng)上(shàng)一(yī)篇(piān)博文(wén),添加調試IP核,綜合、布(bù)局(jú)布(bù)線(xiàn)、生(shēng)成(chéng)bit流。打(dǎ)開(kāi)硬(yìng)件(jiàn)管(guǎn)理器下(xià)载bit流,使用(yòng)調試界面(miàn)观察芯片(piàn)內(nèi)部(bù)波(bō)形數據(jù),先(xiān)来(lái)看(kàn)看(kàn)接收(shōu)有(yǒu)沒(méi)有(yǒu)問(wèn)題(tí),串口(kǒu)調試助手(shǒu)發(fà)送“good#”,观察接收(shōu)有(yǒu)效指示信(xìn)号(hào)和(hé)接收(shōu)數據(jù):
成(chéng)功接收(shōu)到(dào)了(le)good字(zì)符串,并且串口(kǒu)調試助手(shǒu)收(shōu)到(dào)了(le)發(fà)送的(de)字(zì)符,在(zài)沒(méi)有(yǒu)發(fà)送字(zì)符时(shí)每隔3s收(shōu)到(dào)一(yī)个(gè)“wait”字(zì)符串:
串口(kǒu)收(shōu)到(dào)數據(jù)的(de)工程到(dào)这(zhè)里(lǐ)告一(yī)段(duàn)落(là),以(yǐ)後(hòu)可(kě)以(yǐ)進(jìn)一(yī)步改進(jìn)和(hé)做些更(gèng)具應(yìng)用(yòng)性(xìng)的(de)工程。經(jīng)过(guò)三(sān)篇(piān)博文(wén),提(tí)高(gāo)了(le)VIVADO開(kāi)發(fà)环(huán)境的(de)基本(běn)操作熟練度(dù),对(duì)串口(kǒu)協議有(yǒu)了(le)深层次(cì)的(de)認識。最(zuì)重(zhòng)要(yào)的(de)是(shì)时(shí)序設計(jì)能(néng)力有(yǒu)了(le)一(yī)定(dìng)的(de)提(tí)升(shēng)。
















