From 4c6c044afbc9edbb1e1a0b4941d1698b905bf530 Mon Sep 17 00:00:00 2001 From: liangkangnan Date: Fri, 26 Jun 2020 22:40:44 +0800 Subject: [PATCH] rtl: add uart rx function Signed-off-by: liangkangnan --- rtl/perips/uart.v | 331 ++++++++++++++++++++++++++++++++++++ rtl/perips/uart_tx.v | 173 ------------------- rtl/soc/tinyriscv_soc_top.v | 8 +- 3 files changed, 336 insertions(+), 176 deletions(-) create mode 100644 rtl/perips/uart.v delete mode 100644 rtl/perips/uart_tx.v diff --git a/rtl/perips/uart.v b/rtl/perips/uart.v new file mode 100644 index 0000000..e3ada87 --- /dev/null +++ b/rtl/perips/uart.v @@ -0,0 +1,331 @@ + /* + Copyright 2020 Blue Liang, liangkangnan@163.com + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + + +// 串口模块(默认: 115200, 8 N 1) +module uart( + + input wire clk, + input wire rst, + + input wire we_i, + input wire req_i, + input wire[31:0] addr_i, + input wire[31:0] data_i, + + output reg[31:0] data_o, + output reg ack_o, + output wire tx_pin, + input wire rx_pin + + ); + + + // 50MHz时钟,波特率115200bps对应的分频系数 + localparam BAUD_115200 = 32'h1B8; + + localparam S_IDLE = 4'b0001; + localparam S_START = 4'b0010; + localparam S_SEND_BYTE = 4'b0100; + localparam S_STOP = 4'b1000; + + reg tx_data_valid; + reg tx_data_ready; + + reg[3:0] state; + reg[15:0] cycle_cnt; + reg[3:0] bit_cnt; + reg[7:0] tx_data; + reg tx_reg; + + reg rx_q0; + reg rx_q1; + wire rx_negedge; + reg rx_start; // RX使能 + reg[3:0] rx_clk_edge_cnt; // clk时钟沿的个数 + reg rx_clk_edge_level; // clk沿电平 + reg rx_done; + reg[15:0] rx_clk_cnt; + reg[15:0] rx_div_cnt; + reg[7:0] rx_data; + reg rx_over; + + localparam UART_CTRL = 8'h0; + localparam UART_STATUS = 8'h4; + localparam UART_BAUD = 8'h8; + localparam UART_TXDATA = 8'hc; + localparam UART_RXDATA = 8'h10; + + // addr: 0x00 + // rw. bit[0]: tx enable, 1 = enable, 0 = disable + // rw. bit[1]: rx enable, 1 = enable, 0 = disable + reg[31:0] uart_ctrl; + + // addr: 0x04 + // ro. bit[0]: tx busy, 1 = busy, 0 = idle + // rw. bit[1]: rx over, 1 = over, 0 = receiving + // must check this bit before tx data + reg[31:0] uart_status; + + // addr: 0x08 + // rw. clk div + reg[31:0] uart_baud; + + // addr: 0x10 + // ro. rx data + reg[31:0] uart_rx; + + assign tx_pin = tx_reg; + + + // 写寄存器 + always @ (posedge clk) begin + if (rst == 1'b0) begin + uart_ctrl <= 32'h0; + uart_status <= 32'h0; + uart_rx <= 32'h0; + uart_baud <= BAUD_115200; + tx_data_valid <= 1'b0; + end else begin + if (we_i == 1'b1) begin + case (addr_i[7:0]) + UART_CTRL: begin + uart_ctrl <= data_i; + end + UART_BAUD: begin + uart_baud <= data_i; + end + UART_STATUS: begin + uart_status[1] <= data_i[1]; + end + UART_TXDATA: begin + if (uart_ctrl[0] == 1'b1 && uart_status[0] == 1'b0) begin + tx_data <= data_i[7:0]; + uart_status[0] <= 1'b1; + tx_data_valid <= 1'b1; + end + end + endcase + end else begin + tx_data_valid <= 1'b0; + if (tx_data_ready == 1'b1) begin + uart_status[0] <= 1'b0; + end + if (uart_ctrl[1] == 1'b1) begin + if (rx_over == 1'b1) begin + uart_status[1] <= 1'b1; + uart_rx <= {24'h0, rx_data}; + end + end + end + end + end + + // 读寄存器 + always @ (*) begin + if (rst == 1'b0) begin + data_o = 32'h0; + end else begin + case (addr_i[7:0]) + UART_CTRL: begin + data_o = uart_ctrl; + end + UART_STATUS: begin + data_o = uart_status; + end + UART_BAUD: begin + data_o = uart_baud; + end + UART_RXDATA: begin + data_o = uart_rx; + end + default: begin + data_o = 32'h0; + end + endcase + end + end + + // *************************** TX发送 **************************** + + always @ (posedge clk) begin + if (rst == 1'b0) begin + state <= S_IDLE; + cycle_cnt <= 16'd0; + tx_reg <= 1'b0; + bit_cnt <= 4'd0; + tx_data_ready <= 1'b0; + end else begin + if (state == S_IDLE) begin + tx_reg <= 1'b1; + tx_data_ready <= 1'b0; + if (tx_data_valid == 1'b1) begin + state <= S_START; + cycle_cnt <= 16'd0; + bit_cnt <= 4'd0; + tx_reg <= 1'b0; + end + end else begin + cycle_cnt <= cycle_cnt + 16'd1; + if (cycle_cnt == uart_baud[15:0]) begin + cycle_cnt <= 16'd0; + case (state) + S_START: begin + tx_reg <= tx_data[bit_cnt]; + state <= S_SEND_BYTE; + bit_cnt <= bit_cnt + 4'd1; + end + S_SEND_BYTE: begin + bit_cnt <= bit_cnt + 4'd1; + if (bit_cnt == 4'd8) begin + state <= S_STOP; + tx_reg <= 1'b1; + end else begin + tx_reg <= tx_data[bit_cnt]; + end + end + S_STOP: begin + tx_reg <= 1'b1; + state <= S_IDLE; + tx_data_ready <= 1'b1; + end + endcase + end + end + end + end + + // *************************** RX接收 **************************** + + // 下降沿检测(检测起始信号) + assign rx_negedge = rx_q1 && ~rx_q0; + + + always @ (posedge clk) begin + if (rst == 1'b0) begin + rx_q0 <= 1'b0; + rx_q1 <= 1'b0; + end else begin + rx_q0 <= rx_pin; + rx_q1 <= rx_q0; + end + end + + // 开始接收数据信号,接收期间一直有效 + always @ (posedge clk) begin + if (rst == 1'b0) begin + rx_start <= 1'b0; + end else begin + if (uart_ctrl[1]) begin + if (rx_negedge) begin + rx_start <= 1'b1; + end else if (rx_clk_edge_cnt == 4'd9) begin + rx_start <= 1'b0; + end + end else begin + rx_start <= 1'b0; + end + end + end + + always @ (posedge clk) begin + if (rst == 1'b0) begin + rx_div_cnt <= 16'h0; + end else begin + // 第一个时钟沿只需波特率分频系数的一半 + if (rx_start == 1'b1 && rx_clk_edge_cnt == 4'h0) begin + rx_div_cnt <= {1'b0, uart_baud[15:1]}; + end else begin + rx_div_cnt <= uart_baud[15:0]; + end + end + end + + // 对时钟进行计数 + always @ (posedge clk) begin + if (rst == 1'b0) begin + rx_clk_cnt <= 16'h0; + end else if (rx_start == 1'b1) begin + // 计数达到分频值 + if (rx_clk_cnt == rx_div_cnt) begin + rx_clk_cnt <= 16'h0; + end else begin + rx_clk_cnt <= rx_clk_cnt + 1'b1; + end + end else begin + rx_clk_cnt <= 16'h0; + end + end + + // 每当时钟计数达到分频值时产生一个上升沿脉冲 + always @ (posedge clk) begin + if (rst == 1'b0) begin + rx_clk_edge_cnt <= 4'h0; + rx_clk_edge_level <= 1'b0; + end else if (rx_start == 1'b1) begin + // 计数达到分频值 + if (rx_clk_cnt == rx_div_cnt) begin + // 时钟沿个数达到最大值 + if (rx_clk_edge_cnt == 4'd9) begin + rx_clk_edge_cnt <= 4'h0; + rx_clk_edge_level <= 1'b0; + end else begin + // 时钟沿个数加1 + rx_clk_edge_cnt <= rx_clk_edge_cnt + 1'b1; + // 产生上升沿脉冲 + rx_clk_edge_level <= 1'b1; + end + end else begin + rx_clk_edge_level <= 1'b0; + end + end else begin + rx_clk_edge_cnt <= 4'h0; + rx_clk_edge_level <= 1'b0; + end + end + + // bit序列 + always @ (posedge clk) begin + if (rst == 1'b0) begin + rx_data <= 8'h0; + rx_over <= 1'b0; + end else begin + if (rx_start == 1'b1) begin + // 上升沿 + if (rx_clk_edge_level == 1'b1) begin + case (rx_clk_edge_cnt) + // 起始位 + 1: begin + + end + // 数据位 + 2, 3, 4, 5, 6, 7, 8, 9: begin + rx_data <= rx_data | (rx_pin << (rx_clk_edge_cnt - 2)); + // 最后一位接收完成,置位接收完成标志 + if (rx_clk_edge_cnt == 4'h9) begin + rx_over <= 1'b1; + end + end + endcase + end + end else begin + rx_data <= 8'h0; + rx_over <= 1'b0; + end + end + end + +endmodule diff --git a/rtl/perips/uart_tx.v b/rtl/perips/uart_tx.v deleted file mode 100644 index 7defca8..0000000 --- a/rtl/perips/uart_tx.v +++ /dev/null @@ -1,173 +0,0 @@ - /* - Copyright 2020 Blue Liang, liangkangnan@163.com - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - - -module uart_tx( - - input wire clk, - input wire rst, - - input wire we_i, - input wire req_i, - input wire[31:0] addr_i, - input wire[31:0] data_i, - - output reg[31:0] data_o, - output reg ack_o, - output wire tx_pin - - ); - - - localparam BAUD_115200 = 32'h1B8; - - localparam S_IDLE = 4'b0001; - localparam S_START = 4'b0010; - localparam S_SEND_BYTE = 4'b0100; - localparam S_STOP = 4'b1000; - - reg tx_data_valid; - reg tx_data_ready; - - reg[3:0] state; - reg[15:0] cycle_cnt; - reg[3:0] bit_cnt; - reg[7:0] tx_data; - reg tx_reg; - - localparam UART_CTRL = 4'h0; - localparam UART_STATUS = 4'h4; - localparam UART_BAUD = 4'h8; - localparam UART_TXDATA = 4'hc; - - // addr: 0x00 - // rw. bit[0]: tx enable, 1 = enable, 0 = disable - reg[31:0] uart_ctrl; - - // addr: 0x04 - // ro. bit[0]: tx busy, 1 = busy, 0 = idle - // must check this bit before tx data - reg[31:0] uart_status; - - // addr: 0x08 - // rw. clk div - reg[31:0] uart_baud; - - - assign tx_pin = tx_reg; - - - always @ (posedge clk) begin - if (rst == 1'b0) begin - uart_ctrl <= 32'h0; - uart_status <= 32'h0; - uart_baud <= BAUD_115200; - tx_data_valid <= 1'b0; - end else begin - if (we_i == 1'b1) begin - case (addr_i[3:0]) - UART_CTRL: begin - uart_ctrl <= data_i; - end - UART_BAUD: begin - uart_baud <= data_i; - end - UART_TXDATA: begin - if (uart_ctrl[0] == 1'b1 && uart_status[0] == 1'b0) begin - tx_data <= data_i[7:0]; - uart_status <= 32'h1; - tx_data_valid <= 1'b1; - end - end - endcase - end else begin - tx_data_valid <= 1'b0; - if (tx_data_ready == 1'b1) begin - uart_status <= 32'h0; - end - end - end - end - - always @ (*) begin - if (rst == 1'b0) begin - data_o = 32'h0; - end else begin - case (addr_i[3:0]) - UART_CTRL: begin - data_o = uart_ctrl; - end - UART_STATUS: begin - data_o = uart_status; - end - UART_BAUD: begin - data_o = uart_baud; - end - default: begin - data_o = 32'h0; - end - endcase - end - end - - always @ (posedge clk) begin - if (rst == 1'b0) begin - state <= S_IDLE; - cycle_cnt <= 16'd0; - tx_reg <= 1'b0; - bit_cnt <= 4'd0; - tx_data_ready <= 1'b0; - end else begin - if (state == S_IDLE) begin - tx_reg <= 1'b1; - tx_data_ready <= 1'b0; - if (tx_data_valid == 1'b1) begin - state <= S_START; - cycle_cnt <= 16'd0; - bit_cnt <= 4'd0; - tx_reg <= 1'b0; - end - end else begin - cycle_cnt <= cycle_cnt + 16'd1; - if (cycle_cnt == uart_baud[15:0]) begin - cycle_cnt <= 16'd0; - case (state) - S_START: begin - tx_reg <= tx_data[bit_cnt]; - state <= S_SEND_BYTE; - bit_cnt <= bit_cnt + 4'd1; - end - S_SEND_BYTE: begin - bit_cnt <= bit_cnt + 4'd1; - if (bit_cnt == 4'd8) begin - state <= S_STOP; - tx_reg <= 1'b1; - end else begin - tx_reg <= tx_data[bit_cnt]; - end - end - S_STOP: begin - tx_reg <= 1'b1; - state <= S_IDLE; - tx_data_ready <= 1'b1; - end - endcase - end - end - end - end - -endmodule diff --git a/rtl/soc/tinyriscv_soc_top.v b/rtl/soc/tinyriscv_soc_top.v index 4637409..abe427b 100644 --- a/rtl/soc/tinyriscv_soc_top.v +++ b/rtl/soc/tinyriscv_soc_top.v @@ -28,6 +28,7 @@ module tinyriscv_soc_top( output wire halted_ind, // jtag是否已经halt住CPU信号 output wire tx_pin, // UART发送引脚 + input wire rx_pin, // UART接收引脚 inout wire[1:0] gpio, // GPIO引脚 input wire jtag_TCK, // JTAG TCK引脚 @@ -218,8 +219,8 @@ module tinyriscv_soc_top( .ack_o(s2_ack_i) ); - // uart_tx模块例化 - uart_tx uart_tx_0( + // uart模块例化 + uart uart_0( .clk(clk), .rst(rst), .we_i(s3_we_o), @@ -228,7 +229,8 @@ module tinyriscv_soc_top( .data_i(s3_data_o), .data_o(s3_data_i), .ack_o(s3_ack_i), - .tx_pin(tx_pin) + .tx_pin(tx_pin), + .rx_pin(rx_pin) ); // io0