Thông báo

Collapse
No announcement yet.

Thiết kế cổng COM (UART) bằng Verilog

Collapse
X
 
  • Lọc
  • Giờ
  • Show
Clear All
new posts

  • Thiết kế cổng COM (UART) bằng Verilog

    Spec:

    1) 8 bits data.
    2) Tốc độ - từ 300 đến 9600; độ tăng là 300.

    Hy vọng các bạn mới học verilog hưởng ứng nhiệt tình

    Thân ái
    Chúc một ngày vui vẻ
    Tony
    email : dientu_vip@yahoo.com

  • #2
    Tôi làm về thiết kế này lâu lắm rồi nên không còn nhớ mọi chi tiết. Bây giờ thì viết lại bằng trí nhớ cho nên chỉ làm căn bản trước rồi nếu có thêm chi tiết sẽ bổ sung sau:

    Theo tôi nhớ thì cổng COM có 3 khối chính:

    1) Transmitter : Lấy data từ TX register (viết sẵn từ CPU), bỏ thêm start bit (0) và stop bit (1) rồi chuyển nối tiếp ra (serialize).
    2) Sample RX data và tạo RX xung clock, nhận dạng data từ vị trí của start và stop bit rồi chuyển data qua RX register.
    3) Baud rate generator : Tạo xung clock TX và RX tùy theo tốc độ.

    Nếu các bạn có thêm chi tiết, xin bổ sung. Cám ơn.
    Chúc một ngày vui vẻ
    Tony
    email : dientu_vip@yahoo.com

    Comment


    • #3
      Hoạt động cơ bản của khối transmitter là lấy 8 data bit, thêm 1 bit 0 ở đầu và 1 bit 1 ở cuối dãy rồi tuần tự đưa ra cổng ngoài

      Hoạt động cơ bản của khối receiver là theo dõi đường vào, nhận diện data ở giữa start và stop bit, chuyển qua dạng song song rồi đưa ra ngoài. Nhiệm vụ của start and stop bit là để khối receiver nhận ra cạnh của bit. Vì ứng dụng của UART là để liên lạc giữa DTE (data terminal equipment) với DCE (data communication equipment) ở tốc độ thấp và để tránh tốn kém, khối receiver sẽ dùng xung clock 16 lần cao hơn xung clock của khối transmitter để nhận diện bit. Khối receiver sẽ có một bộ đếm 16 và khi thấy cạnh của bit (data bit đổi giá trị) thì nhẩy xuống số 0, số 8 sẽ được chọn là trung tâm của bit. Dùng cách này có thể hoạt động chính xác cho tốc độ sai khoảng 12.5%

      Baud Rate Generator cơ bản là để cung cấp nhịp cho khối tx và rx. Trong trường hợp này xung tối thiểu của thiết kế sẽ là 16 lần của tốc độ cao nhất 9600 cho ra là 16 x 9600 = 156khz. Từ 300 tới 9600 với độ nhẩy 300 sẽ cho ra 32 tốc độ khác nhau. Để thích ứng với công việc này, tiện nhất là dùng khối chia phân với mẫu số là 32 và tử số sẽ đi từ 1 tới 32. Nhịp cho rx là đường ra của khối phân này và nhịp cho tx sẽ được chia xuống 16 của lối ra này

      Vài ý kiến cơ bản, mong các bạn đóng góp thêm
      Chúc một ngày vui vẻ
      Tony
      email : dientu_vip@yahoo.com

      Comment


      • #4
        Baud Rate Generator

        BRG có lẽ là khối dễ nhất. Mình bắt đầu từ khối này trước nhe.

        Vấn đề: Có tất cả 32 tốc độ từ 300 tới 9600 với độ tăng là 300. Nếu xung clock gốc là 9600, để đạt tốc độ ra là 300 thì cần phải chia bằng 32 hay là mỗi 32 xung clock thì có một nhịp cho tốc độ ra. Nếu muốn có tốc độ 1500 thì cần 5 nhịp cho mỗi 32 xung. Nếu nhịp gần nhau quá thì khi dùng để đưa bit ra sẽ không cân bằng cho nên cần phải giãn ra dể cân bằng độ rộng của bit ra. Trong trường hợp 5 nhịp thì nên có nhịp ở 1, 7, 14, 20, 26. Hm, coi bộ hơi phúc tạp nếu phải tính giãn cho từng tốc độ. May mắn là có một công thức để dùng cho vấn đề này và công thức này đã được thảo luận trong đề tài khác ở diễn đàn: "Làm cách nào để nhân chia lẻ cho xung clock?"

        http://www.dientuvietnam.net/forums/...ad.php?t=35447

        Các bạn nào chưa đọc đề tài này thì theo link để nghiên cứu.

        Mình có thể dùng công thức này với mẫu số cố định là 32.

        Trước hết mình phải viết về module interface (kô biết tiếng Việt gọi là gì)

        1) Đường vào:
        * Xung clock
        * Reset
        * Tử số (N), mình có thể dùng parameter cho mẫu số (D)

        2) Đường ra
        * Nhịp ra cho rx
        * Nhịp ra cho tx (nhịp rx chia cho 16).

        Code:
        module baud_rate_gen (
          clk, reset, N, tx_tick, rx_tick
        );
          parameter D = 32;
          input clk;
          input reset;
          input [5:0] N;
          output tx_tick;
          output rx_tick;
        endmodule
        Theo đề tài "chia lẻ", mình có thể viết khối "always" như sau:

        Code:
          reg [6:0] R;
          reg [3:0] cnt16;
          reg fout;
          
          assign rx_tick = fout;
          assign tx_tick = ((cnt16==15)&fout);
        
          always @ (posedge clk or reset) begin
            if (reset) begin
              R = 0;
              cnt16 = 0;
              fout = 0;
            end 
            else begin
              R = R + N;
              if (R > D) begin
                R = R-D;
                fout = 1;
                cnt16 = cnt16 + 1;
              end
              else
                fout = 0;
            end
          end
        Hình của simulation:



        Các bạn chạy thử xem sao.

        Hẹn gặp tiêt mục kế
        Attached Files
        Chúc một ngày vui vẻ
        Tony
        email : dientu_vip@yahoo.com

        Comment


        • #5
          Chuẩn bị cho FSM

          2 khối kế tiếp sẽ dùng FSM để thiết kế. Nếu có thể, bạn nào chưa viết về FSM bao giờ thì hãy theo cái link dưới đây để có khái niệm trước nhe.

          http://www.dientuvietnam.net/forums/...ad.php?t=35757
          Chúc một ngày vui vẻ
          Tony
          email : dientu_vip@yahoo.com

          Comment


          • #6
            Cho khối TX, cần phải có một sự liên lạc với khối chủ. Khi khối chủ muốn gửi data ra, đưa 8 bit song song rồi hỏi khối TX đã gửi xong chưa. Nếu khối TX đã sẵn sàng, lấy data thêm bit đầu và cuối rồi gửi ra theo dạng nối dài. Sau khi gửi xong, nó sẽ thông báo với khối chủ là đã xong.
            Chúc một ngày vui vẻ
            Tony
            email : dientu_vip@yahoo.com

            Comment


            • #7
              Khối transmitter

              Dưới đây là code của khối TX. Cán bạn tham khảo rồi cho ý kiến nhe:

              Code:
              module uart_tx (
                clk, reset, tx_tick, tx_rdy, tx_reg, tx_done_o, tx_sdata_o
              );
                input clk;
                input reset;
                input tx_tick;
                input tx_rdy;
                input [7:0] tx_reg;
                output tx_done_o;
                output tx_sdata_o;
              
                parameter IDLE = 3'b001, START = 3'b010, DATA = 3'b100;
                reg [2:0] st;
                reg tx_done, tx_sdata;
                reg [2:0] cnt;
                
                assign tx_done_o = tx_done;
                assign tx_sdata_o = tx_sdata;
                
                always @ (posedge clk or reset) begin
                  if (reset) begin
                    tx_done = 1;
                    st = IDLE;
                    tx_sdata = 1;
                  end 
                  else begin
                    if (tx_tick) begin
                      case (st)
                        IDLE: begin
                          tx_sdata = 1;
                          if (tx_rdy) begin
                            st = START;
                            tx_done = 0;
                          end
                        end
                        START: begin
                          tx_sdata = 0;
                          cnt = 0;
                          st = DATA;
                        end
                        DATA: begin
                          tx_sdata = tx_reg[cnt];
                          if (cnt == 7) begin
                            st = IDLE;
                            tx_done = 1;
                          end 
                          else
                            cnt = cnt + 1;
                        end
                        default : st = IDLE;
                      endcase
                    end
                  end
                end
              endmodule
              Chúc một ngày vui vẻ
              Tony
              email : dientu_vip@yahoo.com

              Comment


              • #8
                Các bác cho em hỏi chút: bình thường toàn thấy UART nhận dữ liệu từ bàn phím máy tính, bây giờ em cần máy tính nhận số liệu từ FPGA thì phải làm thế nào ạ?

                Comment


                • #9
                  Nguyên văn bởi quachtinh83 Xem bài viết
                  Các bác cho em hỏi chút: bình thường toàn thấy UART nhận dữ liệu từ bàn phím máy tính, bây giờ em cần máy tính nhận số liệu từ FPGA thì phải làm thế nào ạ?
                  Thì FPGA cũng cần phải có UART interface. Khi máy tính gửi thì FPGA nhận và ngược lại. Thường thì máy tính sẽ làm chủ và khởi động thông tin trước. Thông tin này sẽ đi qua UART TX và RS232 TX, tới serial cable, RS232 RX, UART RX rồi tới chỗ nhận tin. Khi trả lời thì thông tin đi ngược trở lại. Máy tính sẽ làm những công việc khác. Khi thông tin tới thì sẽ ngắt công việc mà máy tính đang làm dở. Máy tính nhận thông tin rồi tiếp tục hoạt động từ chỗ bị ngắt.
                  Chúc một ngày vui vẻ
                  Tony
                  email : dientu_vip@yahoo.com

                  Comment


                  • #10
                    RX Bit tick Reciver

                    Để tiếp tục, bài này sẽ bắt đầu cho RX. Đường vào của UART thường chỉ có tín hiệu (data) thôi, không đi kèm với xung cho nên cần phải có một khối để dựa vào tín hiệu để nhận ra khoảng cách của từng bit. Những mẫu thông tin ở tầng số cao và khi nhận sẽ được tiếp tục chuyển đi tiếp thì phải cần Phase Lock Loop (PLL) để hạn chế sự rung động khi chuyển ra. Trong trường hợp của UART thì không cần đến PLL vì hoạt động ở tầng số thấp và thông tin sẽ chấm dứt ở chỗ nhận.

                    Mẫu thông tin nào cũng cần cách (encoding) để phân biệt ranh giới của bit "0" và "1". UART dùng start (0) và stop (1) bit. Dựa vào yếu tố này, cứ mỗi 10 bits (8 data, 1 start, 1 stop) thì có ít nhất 1 lần "1" chuyển qua "0" (transition). Đây là múc để có thể chuẩn đoán vị trí ở giữa của bit. Nếu dùng sampling xung 16x của tốc độ của data và nếu xóa độ đếm khi "1" chuyển qua "0" hoặc ngược lại thì vị trí ở giữa có thể chọn ở số đếm "7" hoặc "8". Khi không có transition ở data, vị trí này có thể bị xê dịch cho nên tốc độ của đường vào không được sai quá 12.5% của tốc độ chuẩn.




                    Code:
                    module rx_bit_tick (clk, reset, rx_tick, rx_sdata, bit_tick);
                    	input clk, reset, rx_tick, rx_sdata;
                    	output bit_tick;
                    
                    	reg [3:0] cnt;
                    	parameter ONE = 1'b1;
                    	parameter ZERO = 1'b0;	
                    	reg rx_tick_st;
                    	reg bit_tick_int;
                    	
                    	always @ (posedge clk or reset) begin
                    		if (reset) begin
                    			rx_tick_st = ONE;
                    			cnt = 0;
                    			bit_tick_int = 0;
                    		end else
                    		if (rx_tick) begin
                    			case (rx_tick_st)
                    				ZERO : begin
                    					if (rx_sdata) begin
                    						cnt = 0;
                    						rx_tick_st = ONE;
                    					end else
                    						cnt = cnt + 1'b1;
                    				end
                    				default : begin
                    					if (!rx_sdata) begin
                    						cnt = 0;
                    						rx_tick_st = ZERO;
                    					end else
                    						cnt = cnt + 1'b1;
                    				end
                    			endcase
                    			
                    			if (cnt == 4'b1000)
                    				bit_tick_int = 1;
                    			else
                    				bit_tick_int = 0;			
                    		end
                    	end
                    	assign #1 bit_tick = bit_tick_int;
                    endmodule
                    Attached Files
                    Chúc một ngày vui vẻ
                    Tony
                    email : dientu_vip@yahoo.com

                    Comment


                    • #11
                      Khối RX

                      Sau khi biết vị trí của RX bit, việc kế tiếp là làm cách nào để nhận dạng được data giữa start và stop bits. Khi khối TX không gửi data thì bắt buộc phải tiếp tục gửi stop bit. Dưới điều kiện này, mình có thể giả thử khối RX sẽ bắt đầu ở trạng thái nhận stop bit và khởi sự tìm kiếm bit kế là start bit. Sau start bit sẽ là data bits (8) và bit cuối cùng, ta phải kiểm tra lại phải là stop bit:

                      1) Nếu phải, data sẽ được đưa ra chỗ chứa riêng và tiếp tục kiếm start bit trở lại cho khâu data mới.
                      2) Nếu không phải thì đợi cho tới khi nhận được stop bit rồi lập lại từ đầu và coi như không nhận được gì hết. Sự kiện này có thể xẩy ra một đôi lần nhưng cuối cùng cũng sẽ nhận được data thật vì cơ hội nhận stop bit sẽ nhiều hơn vì khối TX ở đầu kia sẽ chờ coi khối RX có nhận được tin tức đã gửi hay không. Ví dụ như khi bạn đánh vào phím thì sẽ đợi coi nó có hiện ra trên màn hình hay không.

                      Code:
                      module data_extract (clk, reset, rx_tick, bit_tick, rx_sdata, rx_done, rx_reg);
                      	input clk, reset, rx_tick, bit_tick, rx_sdata;
                      	output rx_done;
                      	output [7:0] rx_reg;
                      
                      	reg [3:0] rx_cnt;
                      	reg rx_done_out;
                      	
                      	reg [7:0] rx_reg_int, rx_reg_out;
                      	parameter IDLE =   4'b0001;
                      	parameter DATA =   4'b0010;
                      	parameter STOP =   4'b0100;
                      	parameter RESYNC = 4'b1000;
                      	
                      	reg [3:0] rx_st;
                      
                      	always @ (posedge clk or reset) begin
                      		if (reset) begin
                      			rx_st = IDLE;
                      			rx_cnt = 0;
                      			rx_done_out = 0;
                      			rx_reg_int = 0;
                      		end else begin
                      			if (rx_tick & bit_tick) begin
                      				case (rx_st)
                      					IDLE : begin
                      						rx_done_out = 0;
                      						if (!rx_sdata) begin
                      							rx_cnt = 0;
                      							rx_st = DATA;
                      						end
                      					end
                      					DATA : begin
                      						rx_reg_int[7] = rx_sdata;
                      						if (rx_cnt == 7)
                      							rx_st = STOP;
                      						else begin
                      							rx_cnt = rx_cnt + 1'b1;
                      							rx_reg_int = rx_reg_int >> 1;
                      						end
                      					end
                      					STOP : begin
                      						if (rx_sdata) begin
                      							rx_reg_out = rx_reg_int;
                      							rx_st = IDLE;
                      							rx_done_out = 1;
                      						end else
                      							rx_st = RESYNC;
                      					end
                      					default : begin
                      						if (rx_sdata)
                      							rx_st = IDLE;
                      					end
                      				endcase
                      			end
                      		end
                      	end
                      	
                      	assign rx_done = rx_done_out;
                      	assign rx_reg = rx_reg_out;
                      	
                      endmodule
                      Chúc một ngày vui vẻ
                      Tony
                      email : dientu_vip@yahoo.com

                      Comment


                      • #12
                        Bác cho em hỏi tý. Em muốn mỗi lần truyền 2 byte thì làm thế nào ạ? Ví dụ cụ thể khi dùng hyper terminal, gõ một ký tự từ bàn phím muốn nhận lại trên cửa sổ hai ký tự đó thì làm thế nào? Mong bác chỉ giáo giúp! Thank

                        Comment


                        • #13
                          Nguyên văn bởi quachtinh83 Xem bài viết
                          Bác cho em hỏi tý. Em muốn mỗi lần truyền 2 byte thì làm thế nào ạ? Ví dụ cụ thể khi dùng hyper terminal, gõ một ký tự từ bàn phím muốn nhận lại trên cửa sổ hai ký tự đó thì làm thế nào? Mong bác chỉ giáo giúp! Thank
                          Bình thường ASCII chỉ dùng 8 bits thôi

                          http://en.wikipedia.org/wiki/ASCII#A...ble_characters

                          Chắc là bạn muốn dùng extended ASCII. Nếu vậy phải làm thêm "character encoding". Dưới đây là link nói về VSCII

                          http://en.wikipedia.org/wiki/Vietnam...on_Interchange

                          Chúc nghiên cứu vui vẻ

                          Tony
                          Chúc một ngày vui vẻ
                          Tony
                          email : dientu_vip@yahoo.com

                          Comment


                          • #14
                            Khi dùng hyper terminal, muốn thấy mấy kí tự mình gõ, chỉ việc chập 2 chân Rx và Tx lại (chân số 2-3 của công RS232).
                            Mỗi kí tự 1 byte thôi.

                            Comment


                            • #15
                              Một bye thì truyền khá dễ rồi. Giờ em muốn truyền số liệu từ FPGA vào máy tính và số liệu của em là 2 byte. Mong bác chỉ giáo giúp. Dưới đây là chương trình mô dun phát (TX) viết bằng VHDL

                              library ieee;
                              use ieee.std_logic_1164.all;

                              entity TxUnit is
                              port (
                              Clk : in Std_Logic; -- Clock signal
                              Reset : in Std_Logic; -- Reset input
                              Enable : in Std_Logic; -- Enable input
                              LoadA : in Std_Logic; -- Asynchronous Load
                              TxD : out Std_Logic; -- RS-232 data output
                              Busy : out Std_Logic; -- Tx Busy
                              DataI : in Std_Logic_Vector(7 downto 0)); -- Byte to transmit
                              end entity;

                              architecture Behaviour of TxUnit is

                              component synchroniser is
                              port (
                              C1 : in Std_Logic; -- Asynchronous signal
                              C : in Std_Logic; -- Clock
                              O : out Std_logic);-- Synchronised signal
                              end component;

                              signal TBuff : Std_Logic_Vector(7 downto 0); -- transmit buffer
                              signal TReg : Std_Logic_Vector(7 downto 0); -- transmit register
                              signal TBufL : Std_Logic; -- Buffer loaded
                              signal LoadS : Std_Logic; -- Synchronised load signal

                              begin
                              -- Synchronise Load on Clk
                              SyncLoad : Synchroniser port map (LoadA, Clk, LoadS);
                              Busy <= LoadS or TBufL;

                              -- Tx process
                              TxProc : process(Clk, Reset, Enable, DataI, TBuff, TReg, TBufL)
                              variable BitPos : INTEGER range 0 to 10; -- Bit position in the frame
                              begin
                              if Reset = '1' then
                              TBufL <= '0';
                              BitPos := 0;
                              TxD <= '1';
                              elsif Rising_Edge(Clk) then
                              if LoadS = '1' then
                              TBuff <= DataI;
                              TBufL <= '1';
                              end if;
                              if Enable = '1' then
                              case BitPos is
                              when 0 => -- idle or stop bit
                              TxD <= '1';
                              if TBufL = '1' then -- start transmit. next is start bit
                              TReg <= TBuff;
                              TBufL <= '0';
                              BitPos := 1;
                              end if;
                              when 1 => -- Start bit
                              TxD <= '0';
                              BitPos := 2;
                              when others =>
                              TxD <= TReg(BitPos-2); -- Serialisation of TReg
                              BitPos := BitPos + 1;
                              end case;
                              if BitPos = 10 then -- bit8. next is stop bit
                              BitPos := 0;
                              end if;
                              end if;
                              end if;
                              end process;
                              end Behaviour;

                              Comment

                              Về tác giả

                              Collapse

                              tonyvandinh A high tech engineer Tìm hiểu thêm về tonyvandinh

                              Bài viết mới nhất

                              Collapse

                              Đang tải...
                              X