电子说
CPU通过挂载到APB总线上的UART模块,实现其与外部设备的串行通信。系统配置部分将 实现UART模块与CPU的通信,APB总线的读写和模块的功能配置,中断信号的产生 。
AMBA总线中的APB总线由于功耗小,接口简单,适用于低带宽且不需要更高性能总线的中低速外设。UART是此类常见外设,通常使用APB总线与系统连接。
REG_IF模块主要由 状态信息同步 、 APB总线读写 、FIFO读写使能和状态寄存器操作及中断产生这几部分构成。
APB总线读写:通过APB总线实现与CPU的通信,通过配置寄存器方式实现数据收发,控制UART模块功能,读取模块工作状态等。
FIFO读写使能:分别是RF_FIFO的读使能控制和TX_FIFO的写使能控制。
状态寄存器操作及中断产生:将TX、RX模块反馈的工作状态体现在状态寄存器中,供CPU查询,并产生中断信号通知CPU进行处理。
配置模块工作在系统时钟域,将收到来自接收模块和发送模块的状态指示信息,这部分信号需要在系统时钟域进行同步。
前面我们讲过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
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 uart_status