【FPGA】Robei EDA的使用(6)—— uart

#今天也是鹹魚的一天~

UART是一個很基礎的串口通信協議,有人打趣說只要有單片機的地方,就一定有uart。這話不假,不僅很多模塊和單片機的通信有uart,單片機和上位機之間的通信,uart也是最常用的。(寒假的時候,就用C#寫了一個上位機,用的就是串口)

Uart 的原理可以參考這個博客:FPGA的串口通訊(UART)

這幾天用Robei EDA 給我的一個感想就是,在FPGA的使用設計上,如果是從原有的程序上移植,比較需要注意的就是一份代碼,需要由多少個運算模塊組成,每個模塊的IO分配,變量類型和數據位的設置,還有對於testbech的設置。

而如果是獨立的Verilog設計,設計方式是自上而下還是自下而上,每個模塊的IO分配,一個模塊內部變量的使用,parameter的設置。尤其是parameter,不適合每個模塊都對parameter設置爲常數,比較好的應該是把局部模塊的parameter設置爲一個“變量”,然後在頂層模塊把這個“變量”設置爲parameter。

回到正題……
uart 在robei EDA的庫裏是有封裝好模塊,可以直接調用。唯一的壞處就是,這個模塊已經封裝好了,不能進行任何修改。(當然,也沒什麼好修改的)
在這裏插入圖片描述代碼可以在Code裏面看到。
在這裏插入圖片描述如果不用這個模塊,自己寫一個,其實還挺麻煩的……
要分別寫一個發送uart_tx 模塊和接收uart_rx模塊,激勵文件的編寫也很麻煩……
在這裏插入圖片描述
在這裏插入圖片描述
分別是發送和接收的模塊。代碼我是參考正點原子的uart收發測試,上位機像FPGA發送一串數據,FPGA再把數據返回給上位機。

UART_TX

  1. 延時
    在這裏插入圖片描述
//對發送使能信號uart_en延遲兩個時鐘週期
always@(posedge clk or negedge rst)
begin
	if(!rst)
	begin 
		uart_en_d0 <= 1'b0;
		uart_en_d1 <= 1'b0;
	end
	else 
	begin
		uart_en_d0 <= uart_en;
		uart_en_d1 <= uart_en_d0;
	end
end
  1. 取上升沿
    在這裏插入圖片描述
assign flag = ~d0 & d1;
//捕獲uart_en上升沿,得到一個時鐘週期的脈衝信號
  1. 數據寄存
    在這裏插入圖片描述
parameter BPS_CNT=  CLK_FRE/UART_BPS;
//當脈衝信號en_flag到達時,寄存待發送的數據,並進入發送過程          
always@(posedge clk or negedge rst)
begin 
	if(!rst)
	begin
		tx_flag <= 1'b0;
		tx_data <= 8'b0;
	end
	else if(flag )
	begin
		tx_flag <= 1'b1;
		tx_data <= uart_din;
	end
	else if((tx_cnt == 4'd9)&&(clk_cnt == BPS_CNT/2))
	begin
	tx_flag <= 1'b0;
	tx_data <= 8'd0;
	end
	else 
	begin
		tx_flag <= tx_flag;
		tx_data <= tx_data;
	end
end
  1. 啓動計數器
    在這裏插入圖片描述
//進入發送過程後,啓動系統時鐘計數器與發送數據計數器
parameter BPS_CNT=  CLK_FRE/UART_BPS;
always@(posedge clk or negedge rst)
begin 
	if(!rst)
	begin 
		clk_cnt <= 16'b0;	
		tx_cnt <= 4'd0;
	end
	else if(tx_flag)
	begin
		if(clk_cnt < BPS_CNT - 1)
		begin
			clk_cnt <= clk_cnt + 1'b1;
			tx_cnt <= tx_cnt;
		end
		else
		begin 
			clk_cnt <= 16'd0;
			tx_cnt <= tx_cnt + 1'b1;
		end
	end
	else 
	begin
		clk_cnt <= 16'b0;	
		tx_cnt <= 4'd0;
	end
end
  1. 數據配置

在這裏插入圖片描述

//根據發送數據計數器來給uart發送端口賦值
always@(posedge clk or negedge rst)
begin 
	if(!rst)
		uart_txd <= 1'b1;
	else if(tx_flag)
		case(tx_cnt)
				4'd0: uart_txd <= 1'b0;
				4'd1: uart_txd <= tx_data[0];
				4'd2: uart_txd <= tx_data[1];
				4'd3: uart_txd <= tx_data[2];
				4'd4: uart_txd <= tx_data[3];
				4'd5: uart_txd <= tx_data[4];
				4'd6: uart_txd <= tx_data[5];
				4'd7: uart_txd <= tx_data[6];
				4'd8: uart_txd <= tx_data[7];
				4'd9: uart_txd <= 1'b1;
				default : ;
		endcase
		else
			uart_txd <= 1'b1;
end

UART_RX

  1. 數據延時

在這裏插入圖片描述

//對UART接收端口的數據延遲兩個時鐘週期
always @(posedge clk or negedge rst) begin 
    if (!rst) begin 
        uart_rx_d0 <= 1'b0;
        uart_rx_d1 <= 1'b0;          
    end
    else begin
        uart_rx_d0  <= uart_rx;                   
        uart_rx_d1  <= uart_rx_d0;
    end   
end
  1. 獲取下降沿
    在這裏插入圖片描述
//捕獲接收端口下降沿(起始位),得到一個時鐘週期的脈衝信號
assign  start_flag = uart_rxd_d1 & (~uart_rxd_d0); 
  1. 進入接收過程

在這裏插入圖片描述

parameter BPS_CNT  = CLK_FREQ/UART_BPS;
//當脈衝信號start_flag到達時,進入接收過程           
always @(posedge clk or negedge rst) begin         
    if (!rst)                                  
        rx_flag <= 1'b0;
    else begin
        if(flag)                          //檢測到起始位
            rx_flag <= 1'b1;                    //進入接收過程,標誌位rx_flag拉高
        else if((rx_cnt == 4'd9)&&(clk_cnt == BPS_CNT/2))
            rx_flag <= 1'b0;                    //計數到停止位中間時,停止接收過程
        else
            rx_flag <= rx_flag;
    end
end
  1. 啓動計數器
    在這裏插入圖片描述
parameter BPS_CNT  = CLK_FREQ/UART_BPS;


always @(posedge clk or negedge rst) begin         
    if (!rst) begin                             
        clk_cnt <= 16'd0;                                  
        rx_cnt  <= 4'd0;
    end                                                      
    else if ( rx_flag ) begin                   //處於接收過程
            if (clk_cnt < BPS_CNT - 1) begin
                clk_cnt <= clk_cnt + 1'b1;
                rx_cnt  <= rx_cnt;
            end
            else begin
                clk_cnt <= 16'd0;               //對系統時鐘計數達一個波特率週期後清零
                rx_cnt  <= rx_cnt + 1'b1;       //此時接收數據計數器加1
            end
        end
        else begin                              //接收過程結束,計數器清零
            clk_cnt <= 16'd0;
            rx_cnt  <= 4'd0;
        end
end
  1. 數據傳輸
    在這裏插入圖片描述
parameter BPS_CNT  = CLK_FREQ/UART_BPS;


always @(posedge clk or negedge rst) begin 
    if ( !rst)  
        rxdata <= 8'd0;                                     
    else if(rx_flag)                            //系統處於接收過程
        if (clk_cnt == BPS_CNT/2) begin         //判斷系統時鐘計數器計數到數據位中間
            case ( rx_cnt )
             4'd1 : rxdata[0] <= uart_rx_d1;   //寄存數據位最低位
             4'd2 : rxdata[1] <= uart_rx_d1;
             4'd3 : rxdata[2] <= uart_rx_d1;
             4'd4 : rxdata[3] <= uart_rx_d1;
             4'd5 : rxdata[4] <= uart_rx_d1;
             4'd6 : rxdata[5] <= uart_rx_d1;
             4'd7 : rxdata[6] <= uart_rx_d1;
             4'd8 : rxdata[7] <= uart_rx_d1;   //寄存數據位最高位
             default:;                                    
            endcase
        end
        else 
            rxdata <= rxdata;
    else
        rxdata <= 8'd0;
end
  1. 輸出數據
    在這裏插入圖片描述
always @(posedge clk or negedge rst) begin        
    if (!rst) begin
        uart_data <= 8'd0;                               
        uart_done <= 1'b0;
    end
    else if(rx_cnt == 4'd9) begin               //接收數據計數器計數到停止位時           
        uart_data <= rxdata;                    //寄存輸出接收到的數據
        uart_done <= 1'b1;                      //並將接收完成標誌位拉高
    end
    else begin
        uart_data <= 8'd0;                                   
        uart_done <= 1'b0; 
    end    
end

這次沒有建立激勵文件,因爲我仿真了一下,效果並不是很好,我感覺放出來有點奇怪,所以……激勵文件就不放了。uart更適合系統的仿真。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章