From 58f180a92f18dd58732544e88dc0d5ce8fc5f894 Mon Sep 17 00:00:00 2001 From: liangkangnan Date: Sat, 7 Aug 2021 14:28:46 +0800 Subject: [PATCH] rtl: perips: rewrite uart module Signed-off-by: liangkangnan --- rtl.flist | 12 +- rtl/core/defines.sv | 6 +- rtl/perips/uart.sv | 425 ---------------------------- rtl/perips/uart/uart.hjson | 107 ++++++++ rtl/perips/uart/uart_core.sv | 192 +++++++++++++ rtl/perips/uart/uart_reg_pkg.sv | 154 +++++++++++ rtl/perips/uart/uart_reg_top.sv | 473 ++++++++++++++++++++++++++++++++ rtl/perips/uart/uart_rx.sv | 116 ++++++++ rtl/perips/uart/uart_top.sv | 64 +++++ rtl/perips/uart/uart_tx.sv | 82 ++++++ rtl/top/tinyriscv_soc_top.sv | 32 ++- rtl/utils/clk_div.sv | 49 ++++ rtl/utils/edge_detect.sv | 51 ++++ rtl/utils/prim_subreg.sv | 64 +++++ rtl/utils/prim_subreg_arb.sv | 79 ++++++ rtl/utils/prim_subreg_ext.sv | 28 ++ 16 files changed, 1491 insertions(+), 443 deletions(-) delete mode 100644 rtl/perips/uart.sv create mode 100644 rtl/perips/uart/uart.hjson create mode 100644 rtl/perips/uart/uart_core.sv create mode 100644 rtl/perips/uart/uart_reg_pkg.sv create mode 100644 rtl/perips/uart/uart_reg_top.sv create mode 100644 rtl/perips/uart/uart_rx.sv create mode 100644 rtl/perips/uart/uart_top.sv create mode 100644 rtl/perips/uart/uart_tx.sv create mode 100644 rtl/utils/clk_div.sv create mode 100644 rtl/utils/edge_detect.sv create mode 100644 rtl/utils/prim_subreg.sv create mode 100644 rtl/utils/prim_subreg_arb.sv create mode 100644 rtl/utils/prim_subreg_ext.sv diff --git a/rtl.flist b/rtl.flist index 01efb30..d02465d 100644 --- a/rtl.flist +++ b/rtl.flist @@ -37,9 +37,14 @@ ../rtl/perips/ram.sv ../rtl/perips/rom.sv ../rtl/perips/timer.sv -../rtl/perips/uart.sv ../rtl/perips/machine_timer.sv ../rtl/perips/rvic.sv +../rtl/perips/uart/uart_reg_pkg.sv +../rtl/perips/uart/uart_reg_top.sv +../rtl/perips/uart/uart_core.sv +../rtl/perips/uart/uart_rx.sv +../rtl/perips/uart/uart_top.sv +../rtl/perips/uart/uart_tx.sv ../rtl/sys_bus/obi_interconnect.sv ../rtl/sys_bus/obi_interconnect_master_sel.sv @@ -52,3 +57,8 @@ ../rtl/utils/gen_ram.sv ../rtl/utils/cdc_2phase.sv ../rtl/utils/sync_fifo.sv +../rtl/utils/clk_div.sv +../rtl/utils/edge_detect.sv +../rtl/utils/prim_subreg.sv +../rtl/utils/prim_subreg_arb.sv +../rtl/utils/prim_subreg_ext.sv diff --git a/rtl/core/defines.sv b/rtl/core/defines.sv index 2a69b26..412d3d6 100644 --- a/rtl/core/defines.sv +++ b/rtl/core/defines.sv @@ -37,9 +37,9 @@ // Timer `define TIMER_ADDR_MASK ~32'hffff `define TIMER_ADDR_BASE 32'h40000000 -// UART -`define UART_ADDR_MASK ~32'hffff -`define UART_ADDR_BASE 32'h50000000 +// UART0 +`define UART0_ADDR_MASK ~32'hffff +`define UART0_ADDR_BASE 32'h50000000 // Machine Timer `define MTIMER_ADDR_MASK ~32'hffff `define MTIMER_ADDR_BASE 32'hA0000000 diff --git a/rtl/perips/uart.sv b/rtl/perips/uart.sv deleted file mode 100644 index 3c8d7ec..0000000 --- a/rtl/perips/uart.sv +++ /dev/null @@ -1,425 +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. - */ - -`include "../core/defines.sv" - -// 串口收发模块(默认: 115200, 8,N,1) -module uart( - - input wire clk, - input wire rst_n, - input wire[31:0] addr_i, - input wire[31:0] data_i, - input wire[3:0] sel_i, - input wire we_i, - output wire[31:0] data_o, - - output wire tx_pin, - input wire rx_pin - - ); - - // 波特率115200bps - localparam BAUD_115200 = `CPU_CLOCK_HZ / 115200; - - localparam S_IDLE = 4'b0001; - localparam S_START = 4'b0010; - localparam S_SEND_BYTE = 4'b0100; - localparam S_STOP = 4'b1000; - - - reg[3:0] state; - reg[3:0] next_state; - reg[15:0] cycle_cnt; - reg tx_bit; - reg[3:0] bit_cnt; - - 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; - - // UART控制寄存器,可读可写 - // bit[0]: UART TX使能, 1: enable, 0: disable - // bit[1]: UART RX使能, 1: enable, 0: disable - reg[31:0] uart_ctrl; - - // UART状态寄存器 - // 只读,bit[0]: TX空闲状态标志, 1: busy, 0: idle - // 可读可写,bit[1]: RX接收完成标志, 1: over, 0: receiving - reg[31:0] uart_status; - - // UART波特率寄存器(分频系数),可读可写 - reg[31:0] uart_baud; - - // UART发送数据寄存器,可读可写 - reg[31:0] uart_tx; - - // UART接收数据寄存器,只读 - reg[31:0] uart_rx; - - wire wen = we_i; - wire ren = (~we_i); - wire write_reg_ctrl_en = wen & (addr_i[7:0] == UART_CTRL); - wire write_reg_status_en = wen & (addr_i[7:0] == UART_STATUS); - wire write_reg_baud_en = wen & (addr_i[7:0] == UART_BAUD); - wire write_reg_txdata_en = wen & (addr_i[7:0] == UART_TXDATA); - wire tx_start = write_reg_txdata_en & sel_i[0] & uart_ctrl[0] & (~uart_status[0]); - wire rx_recv_over = uart_ctrl[1] & rx_over; - - assign tx_pin = tx_bit; - - - // 写uart_rxdata - always @ (posedge clk or negedge rst_n) begin - if (!rst_n) begin - uart_rx <= 32'h0; - end else begin - // 接收完成时,保存接收到的数据 - if (rx_recv_over) begin - uart_rx[7:0] <= rx_data; - end - end - end - - // 写uart_txdata - always @ (posedge clk or negedge rst_n) begin - if (!rst_n) begin - uart_tx <= 32'h0; - end else begin - // 开始发送时,保存要发送的数据 - if (tx_start) begin - uart_tx[7:0] <= data_i[7:0]; - end - end - end - - // 写uart_status - always @ (posedge clk or negedge rst_n) begin - if (!rst_n) begin - uart_status <= 32'h0; - end else begin - if (write_reg_status_en & sel_i[0]) begin - // 写RX完成标志 - uart_status[1] <= data_i[1]; - end else begin - // 开始发送数据时,置位TX忙标志 - if (tx_start) begin - uart_status[0] <= 1'b1; - // 发送完成时,清TX忙标志 - end else if ((state == S_STOP) & (cycle_cnt == uart_baud[15:0])) begin - uart_status[0] <= 1'b0; - // 接收完成,置位接收完成标志 - end - if (rx_recv_over) begin - uart_status[1] <= 1'b1; - end - end - end - end - - // 写uart_ctrl - always @ (posedge clk or negedge rst_n) begin - if (!rst_n) begin - uart_ctrl <= 32'h0; - end else begin - if (write_reg_ctrl_en & sel_i[0]) begin - uart_ctrl[7:0] <= data_i[7:0]; - end - end - end - - // 写uart_baud - always @ (posedge clk or negedge rst_n) begin - if (!rst_n) begin - uart_baud <= BAUD_115200; - end else begin - if (write_reg_baud_en) begin - if (sel_i[0]) begin - uart_baud[7:0] <= data_i[7:0]; - end - if (sel_i[1]) begin - uart_baud[15:8] <= data_i[15:8]; - end - end - end - end - - reg[31:0] data_r; - - // 读寄存器 - always @ (posedge clk or negedge rst_n) begin - if (!rst_n) begin - data_r <= 32'h0; - end else begin - if (ren) begin - case (addr_i[7:0]) - UART_CTRL: data_r <= uart_ctrl; - UART_STATUS: data_r <= uart_status; - UART_BAUD: data_r <= uart_baud; - UART_RXDATA: data_r <= uart_rx; - default: data_r <= 32'h0; - endcase - end else begin - data_r <= 32'h0; - end - end - end - - assign data_o = data_r; - - // *************************** TX发送 **************************** - - always @ (posedge clk or negedge rst_n) begin - if (!rst_n) begin - state <= S_IDLE; - end else begin - state <= next_state; - end - end - - always @ (*) begin - case (state) - S_IDLE: begin - if (tx_start) begin - next_state = S_START; - end else begin - next_state = S_IDLE; - end - end - S_START: begin - if (cycle_cnt == uart_baud[15:0]) begin - next_state = S_SEND_BYTE; - end else begin - next_state = S_START; - end - end - S_SEND_BYTE: begin - if ((cycle_cnt == uart_baud[15:0]) & (bit_cnt == 4'd7)) begin - next_state = S_STOP; - end else begin - next_state = S_SEND_BYTE; - end - end - S_STOP: begin - if (cycle_cnt == uart_baud[15:0]) begin - next_state = S_IDLE; - end else begin - next_state = S_STOP; - end - end - default: begin - next_state = S_IDLE; - end - endcase - end - - // cycle_cnt - always @ (posedge clk or negedge rst_n) begin - if (!rst_n) begin - cycle_cnt <= 16'h0; - end else begin - if (state == S_IDLE) begin - cycle_cnt <= 16'h0; - end else begin - if (cycle_cnt == uart_baud[15:0]) begin - cycle_cnt <= 16'h0; - end else begin - cycle_cnt <= cycle_cnt + 16'h1; - end - end - end - end - - // bit_cnt - always @ (posedge clk or negedge rst_n) begin - if (!rst_n) begin - bit_cnt <= 4'h0; - end else begin - case (state) - S_IDLE: begin - bit_cnt <= 4'h0; - end - S_SEND_BYTE: begin - if (cycle_cnt == uart_baud[15:0]) begin - bit_cnt <= bit_cnt + 4'h1; - end - end - endcase - end - end - - // tx_bit - always @ (posedge clk or negedge rst_n) begin - if (!rst_n) begin - tx_bit <= 1'b0; - end else begin - case (state) - S_IDLE, S_STOP: begin - tx_bit <= 1'b1; - end - S_START: begin - tx_bit <= 1'b0; - end - S_SEND_BYTE: begin - tx_bit <= uart_tx[bit_cnt]; - end - endcase - end - end - - // *************************** RX接收 **************************** - - always @ (posedge clk or negedge rst_n) begin - if (!rst_n) begin - rx_q0 <= 1'b0; - rx_q1 <= 1'b0; - end else begin - rx_q0 <= rx_pin; - rx_q1 <= rx_q0; - end - end - - // 下降沿检测(检测起始信号) - assign rx_negedge = rx_q1 & (~rx_q0); - - // 产生开始接收数据信号,接收期间一直有效 - always @ (posedge clk or negedge rst_n) begin - if (!rst_n) 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 or negedge rst_n) begin - if (!rst_n) 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 or negedge rst_n) begin - if (!rst_n) 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 + 16'h1; - end - end else begin - rx_clk_cnt <= 16'h0; - end - end - - // 每当时钟计数达到分频值时产生一个上升沿脉冲 - always @ (posedge clk or negedge rst_n) begin - if (!rst_n) 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 + 4'h1; - // 产生上升沿脉冲 - 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 or negedge rst_n) begin - if (!rst_n) 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 - // 第1位数据位 - 2: begin - if (rx_pin) begin - rx_data <= 8'h80; - end else begin - rx_data <= 8'h0; - end - end - // 剩余数据位 - 3, 4, 5, 6, 7, 8, 9: begin - rx_data <= {rx_pin, rx_data[7:1]}; - // 最后一位接收完成,置位接收完成标志 - 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/uart.hjson b/rtl/perips/uart/uart.hjson new file mode 100644 index 0000000..d02c9a7 --- /dev/null +++ b/rtl/perips/uart/uart.hjson @@ -0,0 +1,107 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +{ name: "uart", + clocking: [{clock: "clk_i", reset: "rst_ni"}], + bus_interfaces: [ + { protocol: "tlul", direction: "device" } + ], + regwidth: "32", + registers: [ + { name: "CTRL", + desc: "UART control register", + swaccess: "rw", + hwaccess: "hro", + hwqe: "true", + fields: [ + { bits: "0", + name: "TX_EN", + desc: "TX enable" + } + { bits: "1", + name: "RX_EN", + desc: "RX enable" + } + { bits: "2", + name: "TX_FIFO_EMPTY_INT_EN", + desc: "Generate interrupt signal when tx fifo empty" + } + { bits: "3", + name: "RX_FIFO_NOT_EMPTY_INT_EN", + desc: "Generate interrupt signal when rx fifo not empty" + } + { bits: "4", + name: "TX_FIFO_RST", + desc: "Reset tx fifo", + swaccess: "r0w1c" + } + { bits: "5", + name: "RX_FIFO_RST", + desc: "Reset rx fifo", + swaccess: "r0w1c" + } + { bits: "31:16", + name: "BAUD_DIV", + desc: "Baud rate divider count", + resval: "0xD9" + } + ] + } + { name: "STATUS" + desc: "UART status register" + swaccess: "ro" + hwaccess: "hrw" + hwext: "true" + hwre: "true" + fields: [ + { bits: "0" + name: "TXFULL" + desc: "TX FIFO is full" + } + { bits: "1" + name: "RXFULL" + desc: "RX FIFO is full" + } + { bits: "2" + name: "TXEMPTY" + desc: "TX FIFO is empty" + resval: "1" + } + { bits: "3" + name: "RXEMPTY" + desc: "RX FIFO is empty" + resval: "1" + } + { bits: "4" + name: "TXIDLE" + desc: "TX FIFO is empty and all bits have been transmitted" + resval: "1" + } + { bits: "5" + name: "RXIDLE" + desc: "RX is idle" + resval: "1" + } + ] + } + { name: "TXDATA", + desc: "UART TX data register", + swaccess: "wo", + hwaccess: "hro", + hwqe: "true", + fields: [ + { bits: "7:0" } + ] + } + { name: "RXDATA", + desc: "UART RX data register", + swaccess: "ro", + hwaccess: "hrw", + hwext: "true", + hwre: "true", + fields: [ + { bits: "7:0" } + ] + } + ] +} diff --git a/rtl/perips/uart/uart_core.sv b/rtl/perips/uart/uart_core.sv new file mode 100644 index 0000000..75527ab --- /dev/null +++ b/rtl/perips/uart/uart_core.sv @@ -0,0 +1,192 @@ + /* + Copyright 2021 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_core # ( + parameter int unsigned TX_FIFO_DEPTH = 8, + parameter int unsigned RX_FIFO_DEPTH = 8 + )( + input logic clk_i, + input logic rst_ni, + + output logic tx_pin_o, + input logic rx_pin_i, + output logic irq_o, + + input logic reg_we_i, + input logic reg_re_i, + input logic [31:0] reg_wdata_i, + input logic [ 3:0] reg_be_i, + input logic [31:0] reg_addr_i, + output logic [31:0] reg_rdata_o + ); + + import uart_reg_pkg::*; + + uart_reg_pkg::uart_reg2hw_t reg2hw; + uart_reg_pkg::uart_hw2reg_t hw2reg; + + logic tx_enable; + logic rx_enable; + logic [15:0] baud_div; + logic tx_fifo_empty_int_en; + logic rx_fifo_not_empty_int_en; + logic tx_fifo_rst; + logic rx_fifo_rst; + logic tx_idle; + logic rx_idle; + logic rx_error; + logic tx_fifo_full; + logic rx_fifo_full; + logic tx_fifo_empty; + logic rx_fifo_empty; + logic tx_we; + logic [7:0] tx_wdata; + logic [7:0] rx_rdata; + logic rx_rvalid; + logic tx_fifo_pop; + logic rx_fifo_pop; + logic tx_fifo_push; + logic rx_fifo_push; + logic [7:0] tx_fifo_data_out; + logic [7:0] rx_fifo_data_out; + logic [7:0] tx_fifo_data_in; + logic [7:0] rx_fifo_data_in; + + // 波特率分频系数 + assign baud_div = reg2hw.ctrl.baud_div.q; + + // TX + assign tx_enable = reg2hw.ctrl.tx_en.q; + assign tx_fifo_empty_int_en = reg2hw.ctrl.tx_fifo_empty_int_en.q; + // 写1清TX fifo + assign tx_fifo_rst = reg2hw.ctrl.tx_fifo_rst.qe & reg2hw.ctrl.tx_fifo_rst.q; + + assign hw2reg.status.txidle.d = tx_enable ? (tx_idle & tx_fifo_empty) : 1'b1; + assign hw2reg.status.txfull.d = tx_fifo_full; + assign hw2reg.status.txempty.d = tx_fifo_empty; + + // TX开始发送数据 + assign tx_we = tx_enable & (!tx_fifo_empty) & tx_idle; + // 要发送的数据 + assign tx_wdata = tx_fifo_data_out; + // 取出fifo数据 + assign tx_fifo_pop = tx_enable & (!tx_fifo_empty) & tx_idle; + // 可以先push完数据再使能TX + assign tx_fifo_push = reg2hw.txdata.qe & (!tx_fifo_full); + // 要压入fifo的数据 + assign tx_fifo_data_in = reg2hw.txdata.q; + + // RX + assign rx_enable = reg2hw.ctrl.rx_en.q; + assign rx_fifo_not_empty_int_en = reg2hw.ctrl.rx_fifo_not_empty_int_en.q; + // 写1清RX fifo + assign rx_fifo_rst = reg2hw.ctrl.rx_fifo_rst.qe & reg2hw.ctrl.rx_fifo_rst.q; + + assign hw2reg.status.rxidle.d = rx_enable ? rx_idle : 1'b1; + assign hw2reg.status.rxfull.d = rx_fifo_full; + assign hw2reg.status.rxempty.d = rx_fifo_empty; + assign hw2reg.rxdata.d = rx_fifo_data_out; + + // 将接收到的数据压入fifo + assign rx_fifo_push = (~rx_fifo_full) & rx_rvalid; + // 要压入的数据 + assign rx_fifo_data_in = rx_rdata; + // 可以在不使能RX的情况下直接读RX fifo里面的数据 + assign rx_fifo_pop = reg2hw.rxdata.re & (~rx_fifo_empty); + + // 中断信号 + assign irq_o = (tx_enable & tx_fifo_empty_int_en & tx_fifo_empty) | + (rx_enable & rx_fifo_not_empty_int_en & (~rx_fifo_empty)); + + // TX byte + uart_tx u_uart_tx ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .enable_i (tx_enable), + .parity_en_i(1'b0), + .parity_i (1'b1), + .we_i (tx_we), + .wdata_i (tx_wdata), + .div_ratio_i(baud_div), + .idle_o (tx_idle), + .tx_bit_o (tx_pin_o) + ); + + // TX FIFO + sync_fifo #( + .DATA_WIDTH(8), + .DEPTH(TX_FIFO_DEPTH) + ) u_tx_fifo ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i (tx_fifo_rst), + .testmode_i (1'b0), + .full_o (tx_fifo_full), + .empty_o (tx_fifo_empty), + .usage_o (), + .data_i (tx_fifo_data_in), + .push_i (tx_fifo_push), + .data_o (tx_fifo_data_out), + .pop_i (tx_fifo_pop) + ); + + // RX byte + uart_rx u_uart_rx ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .enable_i (rx_enable), + .parity_en_i (1'b0), + .parity_odd_i (1'b1), + .div_ratio_i (baud_div), + .rx_i (rx_pin_i), + .idle_o (rx_idle), + .err_o (rx_error), + .rdata_o (rx_rdata), + .rvalid_o (rx_rvalid) + ); + + // RX FIFO + sync_fifo #( + .DATA_WIDTH(8), + .DEPTH(RX_FIFO_DEPTH) + ) u_rx_fifo ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .flush_i (rx_fifo_rst), + .testmode_i (1'b0), + .full_o (rx_fifo_full), + .empty_o (rx_fifo_empty), + .usage_o (), + .data_i (rx_fifo_data_in), + .push_i (rx_fifo_push), + .data_o (rx_fifo_data_out), + .pop_i (rx_fifo_pop) + ); + + uart_reg_top u_uart_reg_top ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .reg2hw (reg2hw), + .hw2reg (hw2reg), + .reg_we (reg_we_i), + .reg_re (reg_re_i), + .reg_wdata (reg_wdata_i), + .reg_be (reg_be_i), + .reg_addr (reg_addr_i), + .reg_rdata (reg_rdata_o) + ); + +endmodule diff --git a/rtl/perips/uart/uart_reg_pkg.sv b/rtl/perips/uart/uart_reg_pkg.sv new file mode 100644 index 0000000..153e7bd --- /dev/null +++ b/rtl/perips/uart/uart_reg_pkg.sv @@ -0,0 +1,154 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Package auto-generated by `reggen` containing data structure + +package uart_reg_pkg; + + // Address widths within the block + parameter int BlockAw = 4; + + //////////////////////////// + // Typedefs for registers // + //////////////////////////// + + typedef struct packed { + struct packed { + logic q; + logic qe; + } tx_en; + struct packed { + logic q; + logic qe; + } rx_en; + struct packed { + logic q; + logic qe; + } tx_fifo_empty_int_en; + struct packed { + logic q; + logic qe; + } rx_fifo_not_empty_int_en; + struct packed { + logic q; + logic qe; + } tx_fifo_rst; + struct packed { + logic q; + logic qe; + } rx_fifo_rst; + struct packed { + logic [15:0] q; + logic qe; + } baud_div; + } uart_reg2hw_ctrl_reg_t; + + typedef struct packed { + struct packed { + logic q; + logic re; + } txfull; + struct packed { + logic q; + logic re; + } rxfull; + struct packed { + logic q; + logic re; + } txempty; + struct packed { + logic q; + logic re; + } rxempty; + struct packed { + logic q; + logic re; + } txidle; + struct packed { + logic q; + logic re; + } rxidle; + } uart_reg2hw_status_reg_t; + + typedef struct packed { + logic [7:0] q; + logic qe; + } uart_reg2hw_txdata_reg_t; + + typedef struct packed { + logic [7:0] q; + logic re; + } uart_reg2hw_rxdata_reg_t; + + typedef struct packed { + struct packed { + logic d; + } txfull; + struct packed { + logic d; + } rxfull; + struct packed { + logic d; + } txempty; + struct packed { + logic d; + } rxempty; + struct packed { + logic d; + } txidle; + struct packed { + logic d; + } rxidle; + } uart_hw2reg_status_reg_t; + + typedef struct packed { + logic [7:0] d; + } uart_hw2reg_rxdata_reg_t; + + // Register -> HW type + typedef struct packed { + uart_reg2hw_ctrl_reg_t ctrl; // [58:30] + uart_reg2hw_status_reg_t status; // [29:18] + uart_reg2hw_txdata_reg_t txdata; // [17:9] + uart_reg2hw_rxdata_reg_t rxdata; // [8:0] + } uart_reg2hw_t; + + // HW -> register type + typedef struct packed { + uart_hw2reg_status_reg_t status; // [13:8] + uart_hw2reg_rxdata_reg_t rxdata; // [7:0] + } uart_hw2reg_t; + + // Register offsets + parameter logic [BlockAw-1:0] UART_CTRL_OFFSET = 4'h0; + parameter logic [BlockAw-1:0] UART_STATUS_OFFSET = 4'h4; + parameter logic [BlockAw-1:0] UART_TXDATA_OFFSET = 4'h8; + parameter logic [BlockAw-1:0] UART_RXDATA_OFFSET = 4'hc; + + // Reset values for hwext registers and their fields + parameter logic [5:0] UART_STATUS_RESVAL = 6'h3c; + parameter logic [0:0] UART_STATUS_TXEMPTY_RESVAL = 1'h1; + parameter logic [0:0] UART_STATUS_RXEMPTY_RESVAL = 1'h1; + parameter logic [0:0] UART_STATUS_TXIDLE_RESVAL = 1'h1; + parameter logic [0:0] UART_STATUS_RXIDLE_RESVAL = 1'h1; + parameter logic [7:0] UART_RXDATA_RESVAL = 8'h0; + + // Register index + typedef enum int { + UART_CTRL, + UART_STATUS, + UART_TXDATA, + UART_RXDATA + } uart_id_e; + + // Register width information to check illegal writes + parameter logic [3:0] UART_PERMIT [4] = '{ + 4'b1111, // index[0] UART_CTRL + 4'b0001, // index[1] UART_STATUS + 4'b0001, // index[2] UART_TXDATA + 4'b0001 // index[3] UART_RXDATA + }; + +endpackage + diff --git a/rtl/perips/uart/uart_reg_top.sv b/rtl/perips/uart/uart_reg_top.sv new file mode 100644 index 0000000..e05fe3b --- /dev/null +++ b/rtl/perips/uart/uart_reg_top.sv @@ -0,0 +1,473 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register Top module auto-generated by `reggen` + + +module uart_reg_top ( + input logic clk_i, + input logic rst_ni, + + // To HW + output uart_reg_pkg::uart_reg2hw_t reg2hw, // Write + input uart_reg_pkg::uart_hw2reg_t hw2reg, // Read + + input logic reg_we, + input logic reg_re, + input logic [31:0] reg_wdata, + input logic [ 3:0] reg_be, + input logic [31:0] reg_addr, + output logic [31:0] reg_rdata +); + + import uart_reg_pkg::* ; + + localparam int AW = 4; + localparam int DW = 32; + localparam int DBW = DW/8; // Byte Width + + logic reg_error; + logic addrmiss, wr_err; + + logic [DW-1:0] reg_rdata_next; + + assign reg_rdata = reg_rdata_next; + assign reg_error = wr_err; + + // Define SW related signals + // Format: __{wd|we|qs} + // or _{wd|we|qs} if field == 1 or 0 + logic ctrl_we; + logic ctrl_tx_en_qs; + logic ctrl_tx_en_wd; + logic ctrl_rx_en_qs; + logic ctrl_rx_en_wd; + logic ctrl_tx_fifo_empty_int_en_qs; + logic ctrl_tx_fifo_empty_int_en_wd; + logic ctrl_rx_fifo_not_empty_int_en_qs; + logic ctrl_rx_fifo_not_empty_int_en_wd; + logic ctrl_tx_fifo_rst_wd; + logic ctrl_rx_fifo_rst_wd; + logic [15:0] ctrl_baud_div_qs; + logic [15:0] ctrl_baud_div_wd; + logic status_re; + logic status_txfull_qs; + logic status_rxfull_qs; + logic status_txempty_qs; + logic status_rxempty_qs; + logic status_txidle_qs; + logic status_rxidle_qs; + logic txdata_we; + logic [7:0] txdata_wd; + logic rxdata_re; + logic [7:0] rxdata_qs; + + // Register instances + // R[ctrl]: V(False) + + // F[tx_en]: 0:0 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl_tx_en ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (ctrl_we), + .wd (ctrl_tx_en_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (reg2hw.ctrl.tx_en.qe), + .q (reg2hw.ctrl.tx_en.q), + + // to register interface (read) + .qs (ctrl_tx_en_qs) + ); + + + // F[rx_en]: 1:1 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl_rx_en ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (ctrl_we), + .wd (ctrl_rx_en_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (reg2hw.ctrl.rx_en.qe), + .q (reg2hw.ctrl.rx_en.q), + + // to register interface (read) + .qs (ctrl_rx_en_qs) + ); + + + // F[tx_fifo_empty_int_en]: 2:2 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl_tx_fifo_empty_int_en ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (ctrl_we), + .wd (ctrl_tx_fifo_empty_int_en_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (reg2hw.ctrl.tx_fifo_empty_int_en.qe), + .q (reg2hw.ctrl.tx_fifo_empty_int_en.q), + + // to register interface (read) + .qs (ctrl_tx_fifo_empty_int_en_qs) + ); + + + // F[rx_fifo_not_empty_int_en]: 3:3 + prim_subreg #( + .DW (1), + .SWACCESS("RW"), + .RESVAL (1'h0) + ) u_ctrl_rx_fifo_not_empty_int_en ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (ctrl_we), + .wd (ctrl_rx_fifo_not_empty_int_en_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (reg2hw.ctrl.rx_fifo_not_empty_int_en.qe), + .q (reg2hw.ctrl.rx_fifo_not_empty_int_en.q), + + // to register interface (read) + .qs (ctrl_rx_fifo_not_empty_int_en_qs) + ); + + + // F[tx_fifo_rst]: 4:4 + prim_subreg #( + .DW (1), + .SWACCESS("W1C"), + .RESVAL (1'h0) + ) u_ctrl_tx_fifo_rst ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (ctrl_we), + .wd (ctrl_tx_fifo_rst_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (reg2hw.ctrl.tx_fifo_rst.qe), + .q (reg2hw.ctrl.tx_fifo_rst.q), + + // to register interface (read) + .qs () + ); + + + // F[rx_fifo_rst]: 5:5 + prim_subreg #( + .DW (1), + .SWACCESS("W1C"), + .RESVAL (1'h0) + ) u_ctrl_rx_fifo_rst ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (ctrl_we), + .wd (ctrl_rx_fifo_rst_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (reg2hw.ctrl.rx_fifo_rst.qe), + .q (reg2hw.ctrl.rx_fifo_rst.q), + + // to register interface (read) + .qs () + ); + + + // F[baud_div]: 31:16 + prim_subreg #( + .DW (16), + .SWACCESS("RW"), + .RESVAL (16'hd9) + ) u_ctrl_baud_div ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (ctrl_we), + .wd (ctrl_baud_div_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (reg2hw.ctrl.baud_div.qe), + .q (reg2hw.ctrl.baud_div.q), + + // to register interface (read) + .qs (ctrl_baud_div_qs) + ); + + + // R[status]: V(True) + + // F[txfull]: 0:0 + prim_subreg_ext #( + .DW (1) + ) u_status_txfull ( + .re (status_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.status.txfull.d), + .qre (reg2hw.status.txfull.re), + .qe (), + .q (reg2hw.status.txfull.q), + .qs (status_txfull_qs) + ); + + + // F[rxfull]: 1:1 + prim_subreg_ext #( + .DW (1) + ) u_status_rxfull ( + .re (status_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.status.rxfull.d), + .qre (reg2hw.status.rxfull.re), + .qe (), + .q (reg2hw.status.rxfull.q), + .qs (status_rxfull_qs) + ); + + + // F[txempty]: 2:2 + prim_subreg_ext #( + .DW (1) + ) u_status_txempty ( + .re (status_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.status.txempty.d), + .qre (reg2hw.status.txempty.re), + .qe (), + .q (reg2hw.status.txempty.q), + .qs (status_txempty_qs) + ); + + + // F[rxempty]: 3:3 + prim_subreg_ext #( + .DW (1) + ) u_status_rxempty ( + .re (status_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.status.rxempty.d), + .qre (reg2hw.status.rxempty.re), + .qe (), + .q (reg2hw.status.rxempty.q), + .qs (status_rxempty_qs) + ); + + + // F[txidle]: 4:4 + prim_subreg_ext #( + .DW (1) + ) u_status_txidle ( + .re (status_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.status.txidle.d), + .qre (reg2hw.status.txidle.re), + .qe (), + .q (reg2hw.status.txidle.q), + .qs (status_txidle_qs) + ); + + + // F[rxidle]: 5:5 + prim_subreg_ext #( + .DW (1) + ) u_status_rxidle ( + .re (status_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.status.rxidle.d), + .qre (reg2hw.status.rxidle.re), + .qe (), + .q (reg2hw.status.rxidle.q), + .qs (status_rxidle_qs) + ); + + + // R[txdata]: V(False) + + prim_subreg #( + .DW (8), + .SWACCESS("WO"), + .RESVAL (8'h0) + ) u_txdata ( + .clk_i (clk_i), + .rst_ni (rst_ni), + + // from register interface + .we (txdata_we), + .wd (txdata_wd), + + // from internal hardware + .de (1'b0), + .d ('0), + + // to internal hardware + .qe (reg2hw.txdata.qe), + .q (reg2hw.txdata.q), + + // to register interface (read) + .qs () + ); + + + // R[rxdata]: V(True) + + prim_subreg_ext #( + .DW (8) + ) u_rxdata ( + .re (rxdata_re), + .we (1'b0), + .wd ('0), + .d (hw2reg.rxdata.d), + .qre (reg2hw.rxdata.re), + .qe (), + .q (reg2hw.rxdata.q), + .qs (rxdata_qs) + ); + + + logic [3:0] addr_hit; + always_comb begin + addr_hit = '0; + addr_hit[0] = (reg_addr == UART_CTRL_OFFSET); + addr_hit[1] = (reg_addr == UART_STATUS_OFFSET); + addr_hit[2] = (reg_addr == UART_TXDATA_OFFSET); + addr_hit[3] = (reg_addr == UART_RXDATA_OFFSET); + end + + assign addrmiss = (reg_re || reg_we) ? ~|addr_hit : 1'b0 ; + + // Check sub-word write is permitted + always_comb begin + wr_err = (reg_we & + ((addr_hit[0] & (|(UART_PERMIT[0] & ~reg_be))) | + (addr_hit[1] & (|(UART_PERMIT[1] & ~reg_be))) | + (addr_hit[2] & (|(UART_PERMIT[2] & ~reg_be))) | + (addr_hit[3] & (|(UART_PERMIT[3] & ~reg_be))))); + end + + assign ctrl_we = addr_hit[0] & reg_we & !reg_error; + + assign ctrl_tx_en_wd = reg_wdata[0]; + + assign ctrl_rx_en_wd = reg_wdata[1]; + + assign ctrl_tx_fifo_empty_int_en_wd = reg_wdata[2]; + + assign ctrl_rx_fifo_not_empty_int_en_wd = reg_wdata[3]; + + assign ctrl_tx_fifo_rst_wd = reg_wdata[4]; + + assign ctrl_rx_fifo_rst_wd = reg_wdata[5]; + + assign ctrl_baud_div_wd = reg_wdata[31:16]; + assign status_re = addr_hit[1] & reg_re & !reg_error; + assign txdata_we = addr_hit[2] & reg_we & !reg_error; + + assign txdata_wd = reg_wdata[7:0]; + assign rxdata_re = addr_hit[3] & reg_re & !reg_error; + + // Read data return + always_comb begin + reg_rdata_next = '0; + unique case (1'b1) + addr_hit[0]: begin + reg_rdata_next[0] = ctrl_tx_en_qs; + reg_rdata_next[1] = ctrl_rx_en_qs; + reg_rdata_next[2] = ctrl_tx_fifo_empty_int_en_qs; + reg_rdata_next[3] = ctrl_rx_fifo_not_empty_int_en_qs; + reg_rdata_next[4] = '0; + reg_rdata_next[5] = '0; + reg_rdata_next[31:16] = ctrl_baud_div_qs; + end + + addr_hit[1]: begin + reg_rdata_next[0] = status_txfull_qs; + reg_rdata_next[1] = status_rxfull_qs; + reg_rdata_next[2] = status_txempty_qs; + reg_rdata_next[3] = status_rxempty_qs; + reg_rdata_next[4] = status_txidle_qs; + reg_rdata_next[5] = status_rxidle_qs; + end + + addr_hit[2]: begin + reg_rdata_next[7:0] = '0; + end + + addr_hit[3]: begin + reg_rdata_next[7:0] = rxdata_qs; + end + + default: begin + reg_rdata_next = '1; + end + endcase + end + + // Unused signal tieoff + + // wdata / byte enable are not always fully used + // add a blanket unused statement to handle lint waivers + logic unused_wdata; + logic unused_be; + assign unused_wdata = ^reg_wdata; + assign unused_be = ^reg_be; + +endmodule diff --git a/rtl/perips/uart/uart_rx.sv b/rtl/perips/uart/uart_rx.sv new file mode 100644 index 0000000..61e8608 --- /dev/null +++ b/rtl/perips/uart/uart_rx.sv @@ -0,0 +1,116 @@ + /* + Copyright 2021 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_rx ( + input logic clk_i, // 时钟信号 + input logic rst_ni, // 异步复位信号,低电平有效 + input logic enable_i, // RX模块使能信号 + input logic parity_en_i, // 校验使能 + input logic parity_odd_i, // 奇校验 + input logic [15:0] div_ratio_i, // 波特率分频系数 + input logic rx_i, // 来自RX引脚的信号 + output logic idle_o, // RX模块空闲 + output logic err_o, // 接收出错,帧出错或者校验出错 + output logic [7:0] rdata_o, // 接收到的一个字节数据 + output logic rvalid_o // 有效接收到一个字节数据 + ); + + logic tick; + logic rx; + logic rx_start; + logic clk_div_rst_n_d, clk_div_rst_n_q; + logic idle_d, idle_q; + logic rx_valid_d, rx_valid_q; + logic [10:0] shift_reg_d, shift_reg_q; + logic [3:0] bit_cnt_d, bit_cnt_q; + logic [15:0] baud_div_d, baud_div_q; + + + always_comb begin + if (!enable_i) begin + rx_valid_d = 1'b0; + shift_reg_d = '0; + idle_d = 1'b1; + bit_cnt_d = '0; + baud_div_d = '0; + clk_div_rst_n_d = 1'b1; + end else begin + rx_valid_d = 1'b0; + shift_reg_d = shift_reg_q; + idle_d = idle_q; + bit_cnt_d = bit_cnt_q; + baud_div_d = baud_div_q; + clk_div_rst_n_d = 1'b1; + if (rx_start & idle_q) begin + bit_cnt_d = parity_en_i ? 4'd11 : 4'd10; + shift_reg_d = '0; + idle_d = 1'b0; + // 起始位,采中间值 + baud_div_d = {1'b0, div_ratio_i[15:1]}; + clk_div_rst_n_d = 1'b0; + end else if (tick && (!idle_q)) begin + shift_reg_d = {rx, shift_reg_q[10:1]}; + bit_cnt_d = bit_cnt_q - 1'b1; + idle_d = (bit_cnt_q == 4'h1); + rx_valid_d = (bit_cnt_q == 4'h1); + baud_div_d = div_ratio_i; + end + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + rx_valid_q <= 1'b0; + shift_reg_q <= '0; + idle_q <= 1'b1; + bit_cnt_q <= '0; + baud_div_q <= '0; + clk_div_rst_n_q <= 1'b1; + end else begin + rx_valid_q <= rx_valid_d; + shift_reg_q <= shift_reg_d; + idle_q <= idle_d; + bit_cnt_q <= bit_cnt_d; + baud_div_q <= baud_div_d; + clk_div_rst_n_q <= clk_div_rst_n_d; + end + end + + assign idle_o = idle_q; + assign rvalid_o = rx_valid_q; + assign rdata_o = parity_en_i ? shift_reg_q[8:1] : shift_reg_q[9:2]; + assign err_o = rx_valid_q & (~shift_reg_q[10]); + + edge_detect u_edge_detect( + .clk_i (clk_i), + .rst_ni (rst_ni), + .sig_i (rx_i), + .sig_o (rx), + .re_o (), + .fe_o (rx_start) + ); + + clk_div #( + .RATIO_WIDTH(16) + ) u_clk_div ( + .clk_i(clk_i), + .rst_ni(rst_ni || clk_div_rst_n_q), + .en_i(rx_start || (!idle_q)), + .ratio_i(baud_div_q), + .clk_o(tick) + ); + +endmodule diff --git a/rtl/perips/uart/uart_top.sv b/rtl/perips/uart/uart_top.sv new file mode 100644 index 0000000..4d68d76 --- /dev/null +++ b/rtl/perips/uart/uart_top.sv @@ -0,0 +1,64 @@ + /* + Copyright 2021 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_top ( + input logic clk_i, + input logic rst_ni, + + input logic rx_i, + output logic tx_o, + output logic irq_o, + + // OBI总线接口信号 + input logic req_i, + input logic we_i, + input logic [ 3:0] be_i, + input logic [31:0] addr_i, + input logic [31:0] data_i, + output logic [31:0] data_o + ); + + logic re; + logic we; + logic [31:0] addr; + logic [31:0] reg_rdata; + + // 读信号 + assign re = req_i & (!we_i); + // 写信号 + assign we = req_i & we_i; + // 去掉基地址 + assign addr = {16'h0, addr_i[15:0]}; + + uart_core u_uart_core ( + .clk_i (clk_i), + .rst_ni (rst_ni), + .tx_pin_o (tx_o), + .rx_pin_i (rx_i), + .irq_o (irq_o), + .reg_we_i (we), + .reg_re_i (re), + .reg_wdata_i(data_i), + .reg_be_i (be_i), + .reg_addr_i (addr), + .reg_rdata_o(reg_rdata) + ); + + always_ff @(posedge clk_i) begin + data_o <= reg_rdata; + end + +endmodule diff --git a/rtl/perips/uart/uart_tx.sv b/rtl/perips/uart/uart_tx.sv new file mode 100644 index 0000000..1c873f9 --- /dev/null +++ b/rtl/perips/uart/uart_tx.sv @@ -0,0 +1,82 @@ + /* + Copyright 2021 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 logic clk_i, // 时钟输入 + input logic rst_ni, // 异步复位信号,低电平有效 + input logic enable_i, // TX模块使能信号 + input logic parity_en_i, // 校验使能 + input logic parity_i, // 校验方式:奇校验还是偶检验 + input logic we_i, // 开始发送数据 + input logic [7:0] wdata_i, // 要发送的一个字节数据 + input logic [15:0] div_ratio_i, // 波特率分频系数 + output logic idle_o, // TX模块空闲 + output logic tx_bit_o // 要发送的1 bit数据 + ); + + logic [3:0] bit_cnt_d, bit_cnt_q; + logic [10:0] shift_reg_d, shift_reg_q; + logic tx_d, tx_q; + logic tick; + + always_comb begin + if (!enable_i) begin + bit_cnt_d = 4'h0; + shift_reg_d = 11'h7ff; + tx_d = 1'b1; + end else begin + bit_cnt_d = bit_cnt_q; + shift_reg_d = shift_reg_q; + tx_d = tx_q; + if (we_i) begin + // LSB first + shift_reg_d = {1'b1, (parity_en_i ? parity_i : 1'b1), wdata_i, 1'b0}; + bit_cnt_d = (parity_en_i ? 4'd11 : 4'd10); + end else if ((bit_cnt_q != 4'h0) && tick) begin + // 右移1位 + shift_reg_d = {1'b1, shift_reg_q[10:1]}; + tx_d = shift_reg_q[0]; + bit_cnt_d = bit_cnt_q - 4'h1; + end + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + bit_cnt_q <= 4'h0; + shift_reg_q <= 11'h7ff; + tx_q <= 1'b1; + end else begin + bit_cnt_q <= bit_cnt_d; + shift_reg_q <= shift_reg_d; + tx_q <= tx_d; + end + end + + assign idle_o = (bit_cnt_q == 4'h0); + assign tx_bit_o = tx_q; + + clk_div #( + .RATIO_WIDTH(16) + ) u_clk_div ( + .clk_i(clk_i), + .rst_ni(rst_ni || (~we_i)), + .en_i(we_i || (bit_cnt_q != 4'h0)), + .ratio_i(div_ratio_i), + .clk_o(tick) + ); + +endmodule diff --git a/rtl/top/tinyriscv_soc_top.sv b/rtl/top/tinyriscv_soc_top.sv index a32e39b..30ec84a 100644 --- a/rtl/top/tinyriscv_soc_top.sv +++ b/rtl/top/tinyriscv_soc_top.sv @@ -57,7 +57,7 @@ module tinyriscv_soc_top #( localparam int JtagDevice = 2; localparam int Mtimer = 3; localparam int Gpio = 4; - localparam int Uart = 5; + localparam int Uart0 = 5; localparam int Rvic = 6; `ifdef VERILATOR localparam int SimCtrl = 7; @@ -106,6 +106,7 @@ module tinyriscv_soc_top #( wire[7:0] int_id; wire mtimer_irq; + wire uart0_irq; wire[1:0] io_in; wire[31:0] gpio_ctrl; @@ -114,6 +115,7 @@ module tinyriscv_soc_top #( always @ (*) begin irq_src = 32'h0; irq_src[0] = mtimer_irq; + irq_src[1] = uart0_irq; end `ifdef VERILATOR @@ -222,19 +224,21 @@ module tinyriscv_soc_top #( .reg_data(gpio_data) ); - assign slave_addr_mask[Uart] = `UART_ADDR_MASK; - assign slave_addr_base[Uart] = `UART_ADDR_BASE; - // 5.串口模块 - uart u_uart( - .clk (clk), - .rst_n (ndmreset_n), - .addr_i (slave_addr[Uart]), - .data_i (slave_wdata[Uart]), - .sel_i (slave_be[Uart]), - .we_i (slave_we[Uart]), - .data_o (slave_rdata[Uart]), - .tx_pin (uart_tx_pin), - .rx_pin (uart_rx_pin) + assign slave_addr_mask[Uart0] = `UART0_ADDR_MASK; + assign slave_addr_base[Uart0] = `UART0_ADDR_BASE; + // 5.串口0模块 + uart_top uart0 ( + .clk_i (clk), + .rst_ni (ndmreset_n), + .rx_i (uart_rx_pin), + .tx_o (uart_tx_pin), + .irq_o (uart0_irq), + .req_i (slave_req[Uart0]), + .we_i (slave_we[Uart0]), + .be_i (slave_be[Uart0]), + .addr_i (slave_addr[Uart0]), + .data_i (slave_wdata[Uart0]), + .data_o (slave_rdata[Uart0]) ); assign slave_addr_mask[Rvic] = `RVIC_ADDR_MASK; diff --git a/rtl/utils/clk_div.sv b/rtl/utils/clk_div.sv new file mode 100644 index 0000000..f94d25e --- /dev/null +++ b/rtl/utils/clk_div.sv @@ -0,0 +1,49 @@ + /* + Copyright 2021 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 clk_div #( + parameter int unsigned RATIO_WIDTH = 32 + )( + input logic clk_i, // clock + input logic rst_ni, // asynchronous reset active low + input logic en_i, // enable clock divider + input logic [RATIO_WIDTH-1:0] ratio_i, // divider ratio + output logic clk_o // divided clock out + ); + + logic [RATIO_WIDTH-1:0] counter_q; + logic clk_q; + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (~rst_ni) begin + clk_q <= 1'b0; + counter_q <= '0; + end else begin + clk_q <= 1'b0; + if (en_i) begin + if (counter_q == (ratio_i - 1)) begin + clk_q <= 1'b1; + counter_q <= '0; + end else begin + counter_q <= counter_q + 1; + end + end + end + end + + assign clk_o = clk_q; + +endmodule diff --git a/rtl/utils/edge_detect.sv b/rtl/utils/edge_detect.sv new file mode 100644 index 0000000..b381a2b --- /dev/null +++ b/rtl/utils/edge_detect.sv @@ -0,0 +1,51 @@ + /* + Copyright 2021 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 edge_detect ( + input logic clk_i, + input logic rst_ni, + input logic sig_i, + output logic sig_o, + output logic re_o, + output logic fe_o + ); + + logic sig, sig_q; + + assign sig_o = sig_q; + assign fe_o = (~sig) & sig_q; + assign re_o = sig & (~sig_q); + + always_ff @(posedge clk_i, negedge rst_ni) begin + if (!rst_ni) begin + sig_q <= 1'b0; + end else begin + sig_q <= sig; + end + end + + gen_ticks_sync #( + .DP(2), + .DW(1) + ) u_sync ( + .clk(clk_i), + .rst_n(rst_ni), + .din(sig_i), + .dout(sig) + ); + +endmodule diff --git a/rtl/utils/prim_subreg.sv b/rtl/utils/prim_subreg.sv new file mode 100644 index 0000000..f02e6a9 --- /dev/null +++ b/rtl/utils/prim_subreg.sv @@ -0,0 +1,64 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register slice conforming to Comportibility guide. + +module prim_subreg #( + parameter int DW = 32 , + parameter SWACCESS = "RW", // {RW, RO, WO, W1C, W1S, W0C, RC} + parameter logic [DW-1:0] RESVAL = '0 // Reset value +) ( + input clk_i, + input rst_ni, + + // From SW: valid for RW, WO, W1C, W1S, W0C, RC + // In case of RC, Top connects Read Pulse to we + input we, + input [DW-1:0] wd, + + // From HW: valid for HRW, HWO + input de, + input [DW-1:0] d, + + // output to HW and Reg Read + output logic qe, + output logic [DW-1:0] q, + output logic [DW-1:0] qs +); + + logic wr_en; + logic [DW-1:0] wr_data; + + prim_subreg_arb #( + .DW ( DW ), + .SWACCESS ( SWACCESS ) + ) wr_en_data_arb ( + .we, + .wd, + .de, + .d, + .q, + .wr_en, + .wr_data + ); + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + qe <= 1'b0; + end else begin + qe <= we; + end + end + + always_ff @(posedge clk_i or negedge rst_ni) begin + if (!rst_ni) begin + q <= RESVAL; + end else if (wr_en) begin + q <= wr_data; + end + end + + assign qs = q; + +endmodule diff --git a/rtl/utils/prim_subreg_arb.sv b/rtl/utils/prim_subreg_arb.sv new file mode 100644 index 0000000..bf7da53 --- /dev/null +++ b/rtl/utils/prim_subreg_arb.sv @@ -0,0 +1,79 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Write enable and data arbitration logic for register slice conforming to Comportibility guide. + +module prim_subreg_arb #( + parameter int DW = 32 , + parameter SWACCESS = "RW" // {RW, RO, WO, W1C, W1S, W0C, RC} +) ( + // From SW: valid for RW, WO, W1C, W1S, W0C, RC. + // In case of RC, top connects read pulse to we. + input we, + input [DW-1:0] wd, + + // From HW: valid for HRW, HWO. + input de, + input [DW-1:0] d, + + // From register: actual reg value. + input [DW-1:0] q, + + // To register: actual write enable and write data. + output logic wr_en, + output logic [DW-1:0] wr_data +); + + if ((SWACCESS == "RW") || (SWACCESS == "WO")) begin : gen_w + assign wr_en = we | de; + assign wr_data = (we == 1'b1) ? wd : d; // SW higher priority + // Unused q - Prevent lint errors. + logic [DW-1:0] unused_q; + assign unused_q = q; + end else if (SWACCESS == "RO") begin : gen_ro + assign wr_en = de; + assign wr_data = d; + // Unused we, wd, q - Prevent lint errors. + logic unused_we; + logic [DW-1:0] unused_wd; + logic [DW-1:0] unused_q; + assign unused_we = we; + assign unused_wd = wd; + assign unused_q = q; + end else if (SWACCESS == "W1S") begin : gen_w1s + // If SWACCESS is W1S, then assume hw tries to clear. + // So, give a chance HW to clear when SW tries to set. + // If both try to set/clr at the same bit pos, SW wins. + assign wr_en = we | de; + assign wr_data = (de ? d : q) | (we ? wd : '0); + end else if (SWACCESS == "W1C") begin : gen_w1c + // If SWACCESS is W1C, then assume hw tries to set. + // So, give a chance HW to set when SW tries to clear. + // If both try to set/clr at the same bit pos, SW wins. + assign wr_en = we | de; + assign wr_data = (de ? d : q) & (we ? ~wd : '1); + end else if (SWACCESS == "W0C") begin : gen_w0c + assign wr_en = we | de; + assign wr_data = (de ? d : q) & (we ? wd : '1); + end else if (SWACCESS == "RC") begin : gen_rc + // This swtype is not recommended but exists for compatibility. + // WARN: we signal is actually read signal not write enable. + assign wr_en = we | de; + assign wr_data = (de ? d : q) & (we ? '0 : '1); + // Unused wd - Prevent lint errors. + logic [DW-1:0] unused_wd; + assign unused_wd = wd; + end else begin : gen_hw + assign wr_en = de; + assign wr_data = d; + // Unused we, wd, q - Prevent lint errors. + logic unused_we; + logic [DW-1:0] unused_wd; + logic [DW-1:0] unused_q; + assign unused_we = we; + assign unused_wd = wd; + assign unused_q = q; + end + +endmodule diff --git a/rtl/utils/prim_subreg_ext.sv b/rtl/utils/prim_subreg_ext.sv new file mode 100644 index 0000000..2401fff --- /dev/null +++ b/rtl/utils/prim_subreg_ext.sv @@ -0,0 +1,28 @@ +// Copyright lowRISC contributors. +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +// +// Register slice conforming to Comportibility guide. + +module prim_subreg_ext #( + parameter int unsigned DW = 32 +) ( + input re, + input we, + input [DW-1:0] wd, + + input [DW-1:0] d, + + // output to HW and Reg Read + output logic qe, + output logic qre, + output logic [DW-1:0] q, + output logic [DW-1:0] qs +); + + assign qs = d; + assign q = wd; + assign qe = we; + assign qre = re; + +endmodule