rtl: perips: rewrite uart module
Signed-off-by: liangkangnan <liangkangnan@163.com>pull/4/head
parent
cba47c1f64
commit
58f180a92f
12
rtl.flist
12
rtl.flist
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
|
@ -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" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue