Skip to content

Commit eccb118

Browse files
committed
WIP: add new SystemVerilog UART
1 parent fb59f5b commit eccb118

File tree

11 files changed

+2085
-53
lines changed

11 files changed

+2085
-53
lines changed

Bender.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,15 @@ sources:
3333
- rtl/soc_ctrl/soc_ctrl_reg_pkg.sv
3434
- rtl/gpio/gpio_reg_pkg.sv
3535

36+
- rtl/uart/uart_pkg.sv
37+
- rtl/uart/uart_baudgeneration.sv
38+
- rtl/uart/uart_interrupts.sv
39+
- rtl/uart/uart_modem.sv
40+
- rtl/uart/uart_rx.sv
41+
- rtl/uart/uart_tx.sv
42+
- rtl/uart/uart_register.sv
43+
- rtl/uart/uart.sv
44+
3645
# RTL
3746
- target: not(netlist_yosys)
3847
files:

rtl/croc_domain.sv

Lines changed: 23 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -516,59 +516,32 @@ module croc_domain import croc_pkg::*; #(
516516
);
517517

518518
// UART
519-
reg_req_t uart_reg_req;
520-
reg_rsp_t uart_reg_rsp;
521-
522-
periph_to_reg #(
523-
.AW ( SbrObiCfg.AddrWidth ),
524-
.DW ( SbrObiCfg.DataWidth ),
525-
.BW ( 8 ),
526-
.IW ( SbrObiCfg.IdWidth ),
527-
.req_t ( reg_req_t ),
528-
.rsp_t ( reg_rsp_t )
529-
) i_uart_translate (
530-
.clk_i,
531-
.rst_ni,
532-
533-
.req_i ( uart_obi_req.req ),
534-
.add_i ( uart_obi_req.a.addr ),
535-
.wen_i ( ~uart_obi_req.a.we ),
536-
.wdata_i ( uart_obi_req.a.wdata ),
537-
.be_i ( uart_obi_req.a.be ),
538-
.id_i ( uart_obi_req.a.aid ),
539-
540-
.gnt_o ( uart_obi_rsp.gnt ),
541-
.r_rdata_o ( uart_obi_rsp.r.rdata ),
542-
.r_opc_o ( uart_obi_rsp.r.err ),
543-
.r_id_o ( uart_obi_rsp.r.rid ),
544-
.r_valid_o ( uart_obi_rsp.rvalid ),
545-
546-
.reg_req_o ( uart_reg_req ),
547-
.reg_rsp_i ( uart_reg_rsp )
548-
);
549-
550-
reg_uart_wrap #(
551-
.AddrWidth ( 32 ),
552-
.reg_req_t ( reg_req_t ),
553-
.reg_rsp_t ( reg_rsp_t )
519+
uart #(
520+
.ObiCfg ( SbrObiCfg ),
521+
.obi_req_t ( sbr_obi_req_t ),
522+
.obi_rsp_t ( sbr_obi_rsp_t )
554523
) i_uart (
555524
.clk_i,
556525
.rst_ni,
557-
.reg_req_i ( uart_reg_req ),
558-
.reg_rsp_o ( uart_reg_rsp ),
559-
.intr_o ( uart_irq ),
560-
.out2_no ( ),
561-
.out1_no ( ),
562-
.rts_no ( ),
563-
.dtr_no ( ),
564-
.cts_ni ( 1'b0 ),
565-
.dsr_ni ( 1'b0 ),
566-
.dcd_ni ( 1'b0 ),
567-
.rin_ni ( 1'b0 ),
568-
.sin_i ( uart_rx_i ),
569-
.sout_o ( uart_tx_o )
570-
);
571-
assign uart_obi_rsp.r.r_optional = '0;
526+
527+
.obi_req_i ( uart_obi_req ),
528+
.obi_rsp_o ( uart_obi_rsp ),
529+
.irq_o ( uart_irq ),
530+
.irq_no ( ),
531+
532+
.rxd_i ( uart_rx_i ),
533+
.txd_o ( uart_tx_o ),
534+
535+
// Modem control pins are optional
536+
.cts_ni ( 1'b1 ),
537+
.dsr_ni ( 1'b1 ),
538+
.ri_ni ( 1'b1 ),
539+
.cd_ni ( 1'b1 ),
540+
.rts_no ( ),
541+
.dtr_no ( ),
542+
.out1_no ( ),
543+
.out2_no ( )
544+
);
572545

573546
// GPIO
574547
gpio #(

rtl/uart/uart.sv

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
// Copyright 2024 ETH Zurich and University of Bologna.
2+
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
3+
// SPDX-License-Identifier: SHL-0.51
4+
//
5+
// Authors:
6+
// - Hannah Pochert <hpochert@ethz.ch>
7+
// - Philippe Sauter <phsauter@iis.ee.ethz.ch>
8+
9+
`include "common_cells/registers.svh"
10+
11+
module uart #(
12+
/// The OBI configuration connected to this peripheral.
13+
parameter obi_pkg::obi_cfg_t ObiCfg = obi_pkg::ObiDefaultConfig, // SbrObiCfg
14+
/// OBI request type
15+
parameter type obi_req_t = logic,
16+
/// OBI response type
17+
parameter type obi_rsp_t = logic
18+
) (
19+
input logic clk_i, // Primary input clock
20+
input logic rst_ni, // Asynchronous active-low reset
21+
22+
// OBI request interface
23+
input obi_req_t obi_req_i, // a.addr, a.we, a.be, a.wdata, a.aid, a.a_optional | rready, req
24+
// OBI response interface
25+
output obi_rsp_t obi_rsp_o, // r.rdata, r.rid, r.err, r.r_optional | gnt, rvalid
26+
27+
output logic irq_o, // Interrupt line
28+
output logic irq_no, // Negated Interrupt line
29+
30+
input logic rxd_i, // Serial Input
31+
output logic txd_o, // Serial Output
32+
33+
// Modem control pins are optional
34+
input logic cts_ni, // Modem Inp Clear To Send
35+
input logic dsr_ni, // Modem Inp Data Send Request
36+
input logic ri_ni, // Modem Inp Ring Indicator
37+
input logic cd_ni, // Modem Inp Carrier Detect
38+
output logic rts_no, // Modem Oup Ready To Send
39+
output logic dtr_no, // Modem Oup DaTa Ready
40+
output logic out1_no, // Modem Oup DaTa Ready, optional outputs
41+
output logic out2_no // Modem Oup DaTa Ready, optional outputs
42+
);
43+
// Import the UART package for definitions and parameters
44+
import uart_pkg::*;
45+
46+
//--Receiver-and-Transmitter-Interface----------------------------------------------------------
47+
logic rxd;
48+
logic txd;
49+
50+
//--Receiver-Interrupt-Interface----------------------------------------------------------------
51+
logic rx_fifo_trigger;
52+
logic rx_timeout;
53+
54+
//--Register-Interface-Signals------------------------------------------------------------------
55+
reg_read_t reg_read; // signals read from the registers
56+
reg_write_t reg_write; // new values being written to registers
57+
58+
//--Baudenable-Interface-Signals----------------------------------------------------------------
59+
logic oversample_rate_edge;
60+
logic double_rate_edge;
61+
logic baud_rate_edge;
62+
63+
////////////////////////////////////////////////////////////////////////////////////////////////
64+
// REGISTER INTERFACE //
65+
////////////////////////////////////////////////////////////////////////////////////////////////
66+
67+
uart_register #(
68+
.obi_req_t (obi_req_t),
69+
.obi_rsp_t (obi_rsp_t)
70+
) i_uart_register (
71+
.clk_i,
72+
.rst_ni,
73+
74+
.obi_req_i,
75+
.obi_rsp_o,
76+
77+
.reg_read_o (reg_read),
78+
.reg_write_i (reg_write)
79+
);
80+
81+
////////////////////////////////////////////////////////////////////////////////////////////////
82+
// MODEM CONTROL //
83+
////////////////////////////////////////////////////////////////////////////////////////////////
84+
85+
uart_modem #(
86+
) i_uart_modem (
87+
.clk_i,
88+
.rst_ni,
89+
90+
.cts_ni,
91+
.dsr_ni,
92+
.ri_ni,
93+
.cd_ni,
94+
.rts_no,
95+
.dtr_no,
96+
.out1_no,
97+
.out2_no,
98+
99+
.reg_read_i (reg_read),
100+
.reg_write_o (reg_write.modem)
101+
);
102+
103+
//--Loopback-Mode-------------------------------------------------------------------------------
104+
assign txd_o = (reg_read.mcr.loopback == 1'b1) ? 1'b1 : txd;
105+
assign rxd = (reg_read.mcr.loopback == 1'b1) ? txd : rxd_i;
106+
107+
////////////////////////////////////////////////////////////////////////////////////////////////
108+
// BAUDRATE GENERATION //
109+
////////////////////////////////////////////////////////////////////////////////////////////////
110+
111+
uart_baudgeneration #(
112+
) i_uart_baudgeneration (
113+
.clk_i,
114+
.rst_ni,
115+
116+
.oversample_rate_edge_o(oversample_rate_edge),
117+
.double_rate_edge_o (double_rate_edge),
118+
.baud_rate_edge_o (baud_rate_edge),
119+
120+
.reg_read_i (reg_read)
121+
);
122+
123+
////////////////////////////////////////////////////////////////////////////////////////////////
124+
// RECEIVE //
125+
////////////////////////////////////////////////////////////////////////////////////////////////
126+
127+
uart_rx # (
128+
) i_uart_rx (
129+
.clk_i,
130+
.rst_ni,
131+
132+
.oversample_rate_edge_i (oversample_rate_edge),
133+
.baud_rate_edge_i (baud_rate_edge),
134+
135+
.rxd_i (rxd),
136+
137+
.trigger_o (rx_fifo_trigger),
138+
.timeout_o (rx_timeout),
139+
140+
.reg_read_i (reg_read),
141+
.reg_write_o (reg_write.rx)
142+
);
143+
144+
////////////////////////////////////////////////////////////////////////////////////////////////
145+
// TRANSMIT //
146+
////////////////////////////////////////////////////////////////////////////////////////////////
147+
148+
uart_tx # (
149+
) i_uart_tx (
150+
.clk_i,
151+
.rst_ni,
152+
153+
.baud_rate_edge_i (baud_rate_edge),
154+
.double_rate_edge_i (double_rate_edge),
155+
156+
.txd_o (txd),
157+
158+
.reg_read_i (reg_read),
159+
.reg_write_o (reg_write.tx)
160+
);
161+
162+
////////////////////////////////////////////////////////////////////////////////////////////////
163+
// INTERRUPT CONTROL //
164+
////////////////////////////////////////////////////////////////////////////////////////////////
165+
166+
uart_interrupts #(
167+
) i_uart_interrupts (
168+
.clk_i,
169+
.rst_ni,
170+
171+
.rx_fifo_trigger,
172+
.rx_timeout,
173+
174+
.irq_o,
175+
.irq_no,
176+
177+
.reg_read_i (reg_read),
178+
.reg_write_i (reg_write),
179+
.reg_write_o (reg_write.intrpt)
180+
);
181+
182+
endmodule

rtl/uart/uart_baudgeneration.sv

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// Copyright 2024 ETH Zurich and University of Bologna.
2+
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
3+
// SPDX-License-Identifier: SHL-0.51
4+
//
5+
// Authors:
6+
// - Hannah Pochert <hpochert@ethz.ch>
7+
// - Philippe Sauter <phsauter@iis.ee.ethz.ch>
8+
9+
`include "common_cells/registers.svh"
10+
11+
module uart_baudgeneration import uart_pkg::*; #()
12+
(
13+
input logic clk_i,
14+
input logic rst_ni,
15+
16+
output logic oversample_rate_edge_o, // oversample clock enable signal
17+
output logic double_rate_edge_o, // doubled baud clock enable signal
18+
output logic baud_rate_edge_o, // baud clock enable signal
19+
20+
input reg_read_t reg_read_i
21+
);
22+
23+
// Import the UART package for definitions and parameters
24+
25+
//-- Configuration Signals ---------------------------------------------------------------------
26+
logic [15:0] divisor;
27+
logic divisor_valid;
28+
29+
//-- Oversample Signals ------------------------------------------------------------------------
30+
logic oversample_is_divisor;
31+
logic oversample_clear;
32+
logic [15:0] oversample_count;
33+
34+
//-- Double Baud Signals -----------------------------------------------------------------------
35+
logic clear_double_d, clear_double_q;
36+
logic count_is_double;
37+
38+
//-- Baud Signals ------------------------------------------------------------------------------
39+
logic baud_clear;
40+
logic [3:0] baud_count;
41+
logic baud_count_overflow;
42+
43+
////////////////////////////////////////////////////////////////////////////////////////////////
44+
// Clock Division //
45+
////////////////////////////////////////////////////////////////////////////////////////////////
46+
47+
//----------------------------------------------------------------------------------------------
48+
// Oversample 16x
49+
//----------------------------------------------------------------------------------------------
50+
51+
// Concatenate most significant byte and least significant byte of Divisor
52+
// -1 since we clear to zero when reaching divisor, takes one cycle
53+
assign divisor = {reg_read_i.dlm, reg_read_i.dll} - 16'd1;
54+
// divisor reset value is 0 and per the 16550A formula it has no meaning -> invalid
55+
assign divisor_valid = ~(divisor == 0);
56+
assign oversample_is_divisor = (oversample_count == divisor) & divisor_valid;
57+
// clear on reaching the divisor or when configuration changes
58+
assign oversample_clear = oversample_is_divisor | reg_read_i.obi_write_dllm;
59+
60+
counter #(
61+
.WIDTH (16),
62+
.STICKY_OVERFLOW (0)
63+
) i_oversample_counter (
64+
.clk_i,
65+
.rst_ni,
66+
.clear_i ( oversample_clear ), // Synchronous clear: Sets Counter 0 in the next cycle
67+
.en_i ( divisor_valid ), // Count only if configuration is high
68+
.load_i ( 1'b0 ),
69+
.down_i ( 1'b0 ), // Count upwards
70+
.d_i ( '0 ),
71+
.q_o ( oversample_count ),
72+
.overflow_o( ) // Set when counter overflows from '1 to '0
73+
);
74+
75+
// high for one clock cycle
76+
assign oversample_rate_edge_o = (oversample_count == '0) & divisor_valid;
77+
78+
//----------------------------------------------------------------------------------------------
79+
// Baudrate
80+
//----------------------------------------------------------------------------------------------
81+
// Counts when oversample hits target, counter has the new value in the next cycle
82+
// the same cycle when oversample_rate_edge_o goes high
83+
84+
assign baud_clear = baud_count_overflow | reg_read_i.obi_write_dllm;
85+
86+
counter #(
87+
.WIDTH (4),
88+
.STICKY_OVERFLOW(0)
89+
) i_baudrate_counter (
90+
.clk_i,
91+
.rst_ni,
92+
.clear_i ( baud_clear ), // Synchronous clear: Sets Counter 0 in the next cycle
93+
.en_i ( oversample_is_divisor ), // Count every time oversample counter hits its target
94+
.load_i ( 1'b0 ),
95+
.down_i ( 1'b0 ), // Count upwards
96+
.d_i ( '0 ),
97+
.q_o ( baud_count ),
98+
.overflow_o( baud_count_overflow )
99+
);
100+
101+
// oversample hits target, counter overflows on the next clock cycle and then clears itself
102+
// therefore baud_rate_edge_o is high in the same cycle as oversample_rate_edge_o
103+
assign baud_rate_edge_o = baud_count_overflow;
104+
105+
// Double baud rate directly generated from baudrate counter
106+
assign count_is_double = (baud_count[2:0] == '0); // last three bits zero
107+
// clear_double turns the pulse off after one clock cycle
108+
assign clear_double_d = count_is_double;
109+
assign double_rate_edge_o = count_is_double & ~clear_double_q;
110+
111+
`FF(clear_double_q, clear_double_d, '0, clk_i, rst_ni)
112+
113+
endmodule

0 commit comments

Comments
 (0)