rtl: perips: rewrite uart module

Signed-off-by: liangkangnan <liangkangnan@163.com>
pull/4/head
liangkangnan 2021-08-07 14:28:46 +08:00
parent cba47c1f64
commit 58f180a92f
16 changed files with 1491 additions and 443 deletions

View File

@ -37,9 +37,14 @@
../rtl/perips/ram.sv ../rtl/perips/ram.sv
../rtl/perips/rom.sv ../rtl/perips/rom.sv
../rtl/perips/timer.sv ../rtl/perips/timer.sv
../rtl/perips/uart.sv
../rtl/perips/machine_timer.sv ../rtl/perips/machine_timer.sv
../rtl/perips/rvic.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.sv
../rtl/sys_bus/obi_interconnect_master_sel.sv ../rtl/sys_bus/obi_interconnect_master_sel.sv
@ -52,3 +57,8 @@
../rtl/utils/gen_ram.sv ../rtl/utils/gen_ram.sv
../rtl/utils/cdc_2phase.sv ../rtl/utils/cdc_2phase.sv
../rtl/utils/sync_fifo.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

View File

@ -37,9 +37,9 @@
// Timer // Timer
`define TIMER_ADDR_MASK ~32'hffff `define TIMER_ADDR_MASK ~32'hffff
`define TIMER_ADDR_BASE 32'h40000000 `define TIMER_ADDR_BASE 32'h40000000
// UART // UART0
`define UART_ADDR_MASK ~32'hffff `define UART0_ADDR_MASK ~32'hffff
`define UART_ADDR_BASE 32'h50000000 `define UART0_ADDR_BASE 32'h50000000
// Machine Timer // Machine Timer
`define MTIMER_ADDR_MASK ~32'hffff `define MTIMER_ADDR_MASK ~32'hffff
`define MTIMER_ADDR_BASE 32'hA0000000 `define MTIMER_ADDR_BASE 32'hA0000000

View File

@ -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, 8N1)
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

107
rtl/perips/uart/uart.hjson Normal file
View File

@ -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" }
]
}
]
}

View File

@ -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

View File

@ -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

View File

@ -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: <reg>_<field>_{wd|we|qs}
// or <reg>_{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

116
rtl/perips/uart/uart_rx.sv Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -57,7 +57,7 @@ module tinyriscv_soc_top #(
localparam int JtagDevice = 2; localparam int JtagDevice = 2;
localparam int Mtimer = 3; localparam int Mtimer = 3;
localparam int Gpio = 4; localparam int Gpio = 4;
localparam int Uart = 5; localparam int Uart0 = 5;
localparam int Rvic = 6; localparam int Rvic = 6;
`ifdef VERILATOR `ifdef VERILATOR
localparam int SimCtrl = 7; localparam int SimCtrl = 7;
@ -106,6 +106,7 @@ module tinyriscv_soc_top #(
wire[7:0] int_id; wire[7:0] int_id;
wire mtimer_irq; wire mtimer_irq;
wire uart0_irq;
wire[1:0] io_in; wire[1:0] io_in;
wire[31:0] gpio_ctrl; wire[31:0] gpio_ctrl;
@ -114,6 +115,7 @@ module tinyriscv_soc_top #(
always @ (*) begin always @ (*) begin
irq_src = 32'h0; irq_src = 32'h0;
irq_src[0] = mtimer_irq; irq_src[0] = mtimer_irq;
irq_src[1] = uart0_irq;
end end
`ifdef VERILATOR `ifdef VERILATOR
@ -222,19 +224,21 @@ module tinyriscv_soc_top #(
.reg_data(gpio_data) .reg_data(gpio_data)
); );
assign slave_addr_mask[Uart] = `UART_ADDR_MASK; assign slave_addr_mask[Uart0] = `UART0_ADDR_MASK;
assign slave_addr_base[Uart] = `UART_ADDR_BASE; assign slave_addr_base[Uart0] = `UART0_ADDR_BASE;
// 5.串口模块 // 5.串口0模块
uart u_uart( uart_top uart0 (
.clk (clk), .clk_i (clk),
.rst_n (ndmreset_n), .rst_ni (ndmreset_n),
.addr_i (slave_addr[Uart]), .rx_i (uart_rx_pin),
.data_i (slave_wdata[Uart]), .tx_o (uart_tx_pin),
.sel_i (slave_be[Uart]), .irq_o (uart0_irq),
.we_i (slave_we[Uart]), .req_i (slave_req[Uart0]),
.data_o (slave_rdata[Uart]), .we_i (slave_we[Uart0]),
.tx_pin (uart_tx_pin), .be_i (slave_be[Uart0]),
.rx_pin (uart_rx_pin) .addr_i (slave_addr[Uart0]),
.data_i (slave_wdata[Uart0]),
.data_o (slave_rdata[Uart0])
); );
assign slave_addr_mask[Rvic] = `RVIC_ADDR_MASK; assign slave_addr_mask[Rvic] = `RVIC_ADDR_MASK;

49
rtl/utils/clk_div.sv Normal file
View File

@ -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

51
rtl/utils/edge_detect.sv Normal file
View File

@ -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

64
rtl/utils/prim_subreg.sv Normal file
View File

@ -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

View File

@ -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

View File

@ -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