00 模塊簡介
CPU通過掛載到APB總線上的UART模塊,實現其與外部設備的串行通信。系統配置部分將 實現UART模塊與CPU的通信,APB總線的讀寫和模塊的功能配置,中斷信號的產生 。
AMBA總線中的APB總線由于功耗小,接口簡單,適用于低帶寬且不需要更高性能總線的中低速外設。UART是此類常見外設,通常使用APB總線與系統連接。
01 模塊接口與描述
02 實現
REG_IF模塊主要由 狀態信息同步 、 APB總線讀寫 、FIFO讀寫使能和狀態寄存器操作及中斷產生這幾部分構成。
APB總線讀寫:通過APB總線實現與CPU的通信,通過配置寄存器方式實現數據收發,控制UART模塊功能,讀取模塊工作狀態等。
FIFO讀寫使能:分別是RF_FIFO的讀使能控制和TX_FIFO的寫使能控制。
狀態寄存器操作及中斷產生:將TX、RX模塊反饋的工作狀態體現在狀態寄存器中,供CPU查詢,并產生中斷信號通知CPU進行處理。
- 狀態信息同步
配置模塊工作在系統時鐘域,將收到來自接收模塊和發送模塊的狀態指示信息,這部分信號需要在系統時鐘域進行同步。
- APB總線讀寫
前面我們講過APB讀寫時序。
APB讀:作為APB從設備,當APB總線發起讀操作時,需要在合適的時間將對應地址的寄存器值送到總線(如下圖T3),確保主機能收到數據。此時 PENABLE等于0,PSEL等于1,PWRITE等于0 。
apb讀
APB讀實現如下:
//read reg value
always@(posedge clk ornegedge rst_) begin
if(!rst_) begin
prdata_o <= 32'h0;
end
elsebegin
// APB read
if(psel_i && (!penable_i) && (!pwrite_i)) begin
case(paddr_i)
4'h0:
prdata_o <= uart_tx;
4'h1:
prdata_o <= uart_rx;
4'h2:
prdata_o <= uart_baud;
4'h3:
prdata_o <= uart_conf;
4'h4:
prdata_o <= uart_rxtrig;
4'h5:
prdata_o <= uart_txtrig;
4'h6:
prdata_o <= uart_delay;
4'h7:
prdata_o <= uart_status;
4'h8:
prdata_o <= uart_rxfifo_stat;
4'h9:
prdata_o <= uart_txfifo_stat;
endcase
end
end
end
APB寫:類似的,當APB總線上發起寫操作時,從機需要在合適的時間接收總線上的數據(如下圖T4),放到對應的寄存器地址。此時 PENABLE等于1,PSEL等于1,PWRITE等于1 。
apb寫
APB寫實現如下:
//write reg value
always@(posedge clk ornegedge rst_) begin
if(!rst_) begin
uart_tx <= 32'h0;
uart_baud <= 32'hf152;
uart_conf <= 32'h34;
uart_rxtrig <= 32'h1;
uart_txtrig <= 32'h0;
uart_delay <= 32'h2;
end
elsebegin
// APB write
if(psel_i && penable_i && pwrite_i) begin
case(paddr_i)
4'h0:
uart_tx <= pwdata_i;
4'h2:
uart_baud <= pwdata_i;
4'h3:
uart_conf <= pwdata_i;
4'h4:
uart_rxtrig <= pwdata_i;
4'h5:
uart_txtrig <= pwdata_i;
4'h6:
uart_delay <= pwdata_i;
endcase
end
end
end
- FIFO讀寫使能
RX_FIFO讀控制:
在cpu讀uart狀態寄存器(uart_status)時,如果rx中斷有效(即狀態位的第1bit位有效),且FIFO不為空,RX_FIFO讀使能一個時鐘周期。
或在cpu讀接收數據寄存器(uart_rx)時,RX_FIFO讀使能一個時鐘周期。
注意由于讀出FIFO數據需要1個時鐘周期,為了使cpu能及時讀到數據,在讀狀態寄存器時其實是一個預取動作。
// FIFO enable control
always@(posedge clk ornegedge rst_) begin
if(!rst_) begin
rx_fifo_rinc <= 1'b0;
state <= 1'b0;
end
elsebegin
case(state)
1'b0: begin
// when ARM read uart_status, judge interrupt bit ,if rx_int is
// active, or ARM read uart_rx ,rx_fifo_rinc enable 1 clk
if(psel_i && (!penable_i)&&(!pwrite_i)&&(paddr_i==4'h7)) begin
if(uart_status[1] && !rx_fifo_rempty) begin
rx_fifo_rinc <= 1'b1;
state <= 1'b1;
end
end
// when ARM read data,rx_fifo_rinc enable 1 clk
if(psel_i &&(!penable_i)&&(!pwrite_i)&&(paddr_i==4'h1)) begin
rx_fifo_rinc <= 1'b1;
state <= 1'b1;
end
end
1'b1: begin
rx_fifo_rinc <= 1'b0;
state <= 1'b0;
end
endcase
end
end
TX_FIFO寫控制:
在cpu寫uart_tx時,說明需要UART模塊發送數據,APB寫數據到寄存器需要1個時鐘周期,所以需要在1個時鐘之后再寫使能TX_FIFO。
always@(posedge clk ornegedge rst_) begin
if(!rst_) begin
tx_fifo_winc <= 1'b0;
state_en <= 2'b0;
end
elsebegin
case(state_en)
2'b0: begin
// ARM write uart_tx,tx_fifo_winc enable 1 clk after 1 clk
if(psel_i && penable_i && pwrite_i && (paddr_i==4'h0)) begin
state_en <= 2'b01;
end
end
2'b01: begin
state_en <= 2'b10;
tx_fifo_winc <= 1'b1;
end
2'b10: begin
tx_fifo_winc <= 1'b0;
state_en <= 2'b0;
end
endcase
end
end
- 狀態寄存器操作及中斷產生
狀態寄存器指示4個狀態:ST_ERROR,P_ERROR,RX_INT和TX_INT。由高到低在寄存器uart_status[3:0]。分別表示接收數據停止位出錯、接收數據校驗位出錯、接收數據中斷位和發送數據中斷位。
其中接收數據中斷位有效表示RX_FIFO中數據量增加到觸發值,數據將滿(觸發值由cpu配置),通知cpu接收數據;發送數據中斷位有效表示TX_FIFO中數據減少到觸發值,數據將空(觸發值由cpu配置),通知cpu數據將要發送完畢,需補充數據。
從接收模塊接收到的ST_ERROR,P_ERROR信號響應也在此部分產生,表示已經收到此狀態。
// uart_status register operate
always@(posedge clk ornegedge rst_) begin
if(!rst_) begin
p_error_ack <= 1'b0;
st_error_ack <= 1'b0;
uart_status <= 32'h0;
rx_state <= 1'b0;
tx_state <= 1'b0;
end
elsebegin
if(st_error_syn) begin
uart_status[3] <= 1'b1;
end
elsebegin
if(neg_uart_status3) begin
st_error_ack <= 1'b1;
end
elsebegin
if(!st_error_syn2) begin
st_error_ack <= 1'b0;
end
end
end
if(p_error_syn) begin
uart_status[2] <= 1'b1;
end
elsebegin
if(neg_uart_status2) begin
p_error_ack <= 1'b1;
end
elsebegin
if(!p_error_syn2) begin
p_error_ack <= 1'b0;
end
end
end
// when rx_fifo_cnt from less than to equal the rxtrig,
// rx_int is active
case(rx_state)
1'b0: begin
if(rx_fifo_cnt == (uart_rxtrig[3:0] - 1'b1)) begin
rx_state <= 1'b1;
end
elsebegin
rx_state <= 1'b0;
end
end
1'b1: begin
if(rx_fifo_cnt == uart_rxtrig[3:0]) begin
uart_status[1] <= 1'b1;
rx_state <= 1'b0;
end
elsebegin
rx_state <= 1'b1;
end
end
endcase
// when tx_fifo_cnt from greater than to equal the txtrig,
// tx_int is active
case(tx_state)
1'b0: begin
if(tx_fifo_cnt == (uart_txtrig[3:0] + 1'b1)) begin
tx_state <= 1'b1;
end
elsebegin
tx_state <= 1'b0;
end
end
1'b1: begin
if(tx_fifo_cnt == uart_txtrig[3:0]) begin
uart_status[0] <= 1'b1;
tx_state <= 1'b0;
end
elsebegin
tx_state <= 1'b1;
end
end
endcase
// ARM write 1 clean 0