hw/ip/uart/rtl/uart_core.sv Cov: 100%
1: // Copyright lowRISC contributors.
2: // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3: // SPDX-License-Identifier: Apache-2.0
4: //
5: // Description: UART core module
6: //
7:
8: module uart_core (
9: input clk_i,
10: input rst_ni,
11:
12: input uart_reg_pkg::uart_reg2hw_t reg2hw,
13: output uart_reg_pkg::uart_hw2reg_t hw2reg,
14:
15: input rx,
16: output logic tx,
17:
18: output logic intr_tx_watermark_o,
19: output logic intr_rx_watermark_o,
20: output logic intr_tx_empty_o,
21: output logic intr_rx_overflow_o,
22: output logic intr_rx_frame_err_o,
23: output logic intr_rx_break_err_o,
24: output logic intr_rx_timeout_o,
25: output logic intr_rx_parity_err_o
26: );
27:
28: import uart_reg_pkg::*;
29:
30: logic [15:0] rx_val_q;
31: logic [7:0] uart_rdata;
32: logic tick_baud_x16, rx_tick_baud;
33: logic [5:0] tx_fifo_depth, rx_fifo_depth;
34: logic [5:0] rx_fifo_depth_prev_q;
35: logic [23:0] rx_timeout_count_d, rx_timeout_count_q, uart_rxto_val;
36: logic rx_fifo_depth_changed, uart_rxto_en;
37: logic tx_enable, rx_enable;
38: logic sys_loopback, line_loopback, rxnf_enable;
39: logic uart_fifo_rxrst, uart_fifo_txrst;
40: logic [2:0] uart_fifo_rxilvl;
41: logic [1:0] uart_fifo_txilvl;
42: logic ovrd_tx_en, ovrd_tx_val;
43: logic [7:0] tx_fifo_data;
44: logic tx_fifo_rready, tx_fifo_rvalid;
45: logic tx_fifo_wready, tx_uart_idle;
46: logic tx_out;
47: logic tx_out_q;
48: logic [7:0] rx_fifo_data;
49: logic rx_valid, rx_fifo_wvalid, rx_fifo_rvalid;
50: logic rx_fifo_wready, rx_uart_idle;
51: logic rx_sync;
52: logic rx_in;
53: logic break_err;
54: logic [4:0] allzero_cnt_d, allzero_cnt_q;
55: logic allzero_err, not_allzero_char;
56: logic event_tx_watermark, event_rx_watermark, event_tx_empty, event_rx_overflow;
57: logic event_rx_frame_err, event_rx_break_err, event_rx_timeout, event_rx_parity_err;
58: logic tx_watermark_d, tx_watermark_prev_q;
59: logic tx_empty_d, tx_empty_prev_q;
60:
61: assign tx_enable = reg2hw.ctrl.tx.q;
62: assign rx_enable = reg2hw.ctrl.rx.q;
63: assign rxnf_enable = reg2hw.ctrl.nf.q;
64: assign sys_loopback = reg2hw.ctrl.slpbk.q;
65: assign line_loopback = reg2hw.ctrl.llpbk.q;
66:
67: assign uart_fifo_rxrst = reg2hw.fifo_ctrl.rxrst.q & reg2hw.fifo_ctrl.rxrst.qe;
68: assign uart_fifo_txrst = reg2hw.fifo_ctrl.txrst.q & reg2hw.fifo_ctrl.txrst.qe;
69: assign uart_fifo_rxilvl = reg2hw.fifo_ctrl.rxilvl.q;
70: assign uart_fifo_txilvl = reg2hw.fifo_ctrl.txilvl.q;
71:
72: assign ovrd_tx_en = reg2hw.ovrd.txen.q;
73: assign ovrd_tx_val = reg2hw.ovrd.txval.q;
74:
75: typedef enum logic {
76: BRK_CHK,
77: BRK_WAIT
78: } break_st_e ;
79:
80: break_st_e break_st_q;
81:
82: assign not_allzero_char = rx_valid & (~event_rx_frame_err | (rx_fifo_data != 8'h0));
83: assign allzero_err = event_rx_frame_err & (rx_fifo_data == 8'h0);
84:
85:
86: assign allzero_cnt_d = (break_st_q == BRK_WAIT || not_allzero_char) ? 5'h0 :
87: //allzero_cnt_q[4] never be 1b without break_st_q as BRK_WAIT
88: //allzero_cnt_q[4] ? allzero_cnt_q :
89: allzero_err ? allzero_cnt_q + 5'd1 :
90: allzero_cnt_q;
91:
92: always_ff @(posedge clk_i or negedge rst_ni) begin
93: if (!rst_ni) allzero_cnt_q <= '0;
94: else if (rx_enable) allzero_cnt_q <= allzero_cnt_d;
95: end
96:
97: // break_err edges in same cycle as event_rx_frame_err edges ; that way the
98: // reset-on-read works the same way for break and frame error interrupts.
99:
100: always_comb begin
101: unique case (reg2hw.ctrl.rxblvl.q)
102: 2'h0: break_err = allzero_cnt_d >= 5'd2;
103: 2'h1: break_err = allzero_cnt_d >= 5'd4;
104: 2'h2: break_err = allzero_cnt_d >= 5'd8;
105: default: break_err = allzero_cnt_d >= 5'd16;
106: endcase
107: end
108:
109: always_ff @(posedge clk_i or negedge rst_ni) begin
110: if (!rst_ni) break_st_q <= BRK_CHK;
111: else begin
112: unique case (break_st_q)
113: BRK_CHK: begin
114: if (event_rx_break_err) break_st_q <= BRK_WAIT;
115: end
116:
117: BRK_WAIT: begin
118: if (rx_in) break_st_q <= BRK_CHK;
119: end
120:
121: default: begin
122: break_st_q <= BRK_CHK;
123: end
124: endcase
125: end
126: end
127:
128: assign hw2reg.val.d = rx_val_q;
129:
130: assign hw2reg.rdata.d = uart_rdata;
131:
132: assign hw2reg.status.rxempty.d = ~rx_fifo_rvalid;
133: assign hw2reg.status.rxidle.d = rx_uart_idle;
134: assign hw2reg.status.txidle.d = tx_uart_idle & ~tx_fifo_rvalid;
135: assign hw2reg.status.txempty.d = ~tx_fifo_rvalid;
136: assign hw2reg.status.rxfull.d = ~rx_fifo_wready;
137: assign hw2reg.status.txfull.d = ~tx_fifo_wready;
138:
139: assign hw2reg.fifo_status.txlvl.d = tx_fifo_depth;
140: assign hw2reg.fifo_status.rxlvl.d = rx_fifo_depth;
141:
142: // resets are self-clearing, so need to update FIFO_CTRL
143: assign hw2reg.fifo_ctrl.rxilvl.de = 1'b0;
144: assign hw2reg.fifo_ctrl.rxilvl.d = 3'h0;
145: assign hw2reg.fifo_ctrl.txilvl.de = 1'b0;
146: assign hw2reg.fifo_ctrl.txilvl.d = 2'h0;
147:
148: // NCO 16x Baud Generator
149: // output clock rate is:
150: // Fin * (NCO/2**16)
151: // So, with a 16 bit accumulator, the output clock is
152: // Fin * (NCO/65536)
153: logic [16:0] nco_sum_q; // extra bit to get the carry
154:
155: always_ff @(posedge clk_i or negedge rst_ni) begin
156: if (!rst_ni) begin
157: nco_sum_q <= 17'h0;
158: end else if (tx_enable || rx_enable) begin
159: nco_sum_q <= {1'b0,nco_sum_q[15:0]} + {1'b0,reg2hw.ctrl.nco.q};
160: end
161: end
162:
163: assign tick_baud_x16 = nco_sum_q[16];
164:
165: //////////////
166: // TX Logic //
167: //////////////
168:
169: assign tx_fifo_rready = tx_uart_idle & tx_fifo_rvalid & tx_enable;
170:
171: prim_fifo_sync #(
172: .Width(8),
173: .Pass (1'b0),
174: .Depth(32)
175: ) u_uart_txfifo (
176: .clk_i,
177: .rst_ni,
178: .clr_i (uart_fifo_txrst),
179: .wvalid (reg2hw.wdata.qe),
180: .wready (tx_fifo_wready),
181: .wdata (reg2hw.wdata.q),
182: .depth (tx_fifo_depth),
183: .rvalid (tx_fifo_rvalid),
184: .rready (tx_fifo_rready),
185: .rdata (tx_fifo_data)
186: );
187:
188: uart_tx uart_tx (
189: .clk_i,
190: .rst_ni,
191: .tx_enable,
192: .tick_baud_x16,
193: .parity_enable (reg2hw.ctrl.parity_en.q),
194: .wr (tx_fifo_rready),
195: .wr_parity ((^tx_fifo_data) ^ reg2hw.ctrl.parity_odd.q),
196: .wr_data (tx_fifo_data),
197: .idle (tx_uart_idle),
198: .tx (tx_out)
199: );
200:
201: assign tx = line_loopback ? rx : tx_out_q ;
202: always_ff @(posedge clk_i or negedge rst_ni) begin
203: if (!rst_ni) begin
204: tx_out_q <= 1'b1;
205: end else if (ovrd_tx_en) begin
206: tx_out_q <= ovrd_tx_val ;
207: end else if (sys_loopback) begin
208: tx_out_q <= 1'b1;
209: end else begin
210: tx_out_q <= tx_out;
211: end
212: end
213:
214: //////////////
215: // RX Logic //
216: //////////////
217:
218: // sync the incoming data
219: prim_flop_2sync #(
220: .Width(1),
221: .ResetValue(1)
222: ) sync_rx (
223: .clk_i,
224: .rst_ni,
225: .d(rx),
226: .q(rx_sync)
227: );
228:
229: // Based on: en.wikipedia.org/wiki/Repetition_code mentions the use of a majority filter
230: // in UART to ignore brief noise spikes
231: logic rx_sync_q1, rx_sync_q2, rx_in_mx, rx_in_maj;
232:
233: always_ff @(posedge clk_i or negedge rst_ni) begin
234: if (!rst_ni) begin
235: rx_sync_q1 <= 1'b1;
236: rx_sync_q2 <= 1'b1;
237: end else begin
238: rx_sync_q1 <= rx_sync;
239: rx_sync_q2 <= rx_sync_q1;
240: end
241: end
242:
243: assign rx_in_maj = (rx_sync & rx_sync_q1) |
244: (rx_sync & rx_sync_q2) |
245: (rx_sync_q1 & rx_sync_q2);
246: assign rx_in_mx = rxnf_enable ? rx_in_maj : rx_sync;
247:
248: assign rx_in = sys_loopback ? tx_out :
249: line_loopback ? 1'b1 :
250: rx_in_mx;
251:
252: uart_rx uart_rx (
253: .clk_i,
254: .rst_ni,
255: .rx_enable,
256: .tick_baud_x16,
257: .parity_enable (reg2hw.ctrl.parity_en.q),
258: .parity_odd (reg2hw.ctrl.parity_odd.q),
259: .tick_baud (rx_tick_baud),
260: .rx_valid,
261: .rx_data (rx_fifo_data),
262: .idle (rx_uart_idle),
263: .frame_err (event_rx_frame_err),
264: .rx (rx_in),
265: .rx_parity_err (event_rx_parity_err)
266: );
267:
268: assign rx_fifo_wvalid = rx_valid & ~event_rx_frame_err & ~event_rx_parity_err;
269:
270: prim_fifo_sync #(
271: .Width (8),
272: .Pass (1'b0),
273: .Depth (32)
274: ) u_uart_rxfifo (
275: .clk_i,
276: .rst_ni,
277: .clr_i (uart_fifo_rxrst),
278: .wvalid (rx_fifo_wvalid),
279: .wready (rx_fifo_wready),
280: .wdata (rx_fifo_data),
281: .depth (rx_fifo_depth),
282: .rvalid (rx_fifo_rvalid),
283: .rready (reg2hw.rdata.re),
284: .rdata (uart_rdata)
285: );
286:
287: always_ff @(posedge clk_i or negedge rst_ni) begin
288: if (!rst_ni) rx_val_q <= 16'h0;
289: else if (tick_baud_x16) rx_val_q <= {rx_val_q[14:0], rx_in};
290: end
291:
292: ////////////////////////
293: // Interrupt & Status //
294: ////////////////////////
295:
296: always_comb begin
297: unique case(uart_fifo_txilvl)
298: 2'h0: tx_watermark_d = (tx_fifo_depth < 6'd1);
299: 2'h1: tx_watermark_d = (tx_fifo_depth < 6'd4);
300: 2'h2: tx_watermark_d = (tx_fifo_depth < 6'd8);
301: default: tx_watermark_d = (tx_fifo_depth < 6'd16);
302: endcase
303: end
304:
305: assign event_tx_watermark = tx_watermark_d & ~tx_watermark_prev_q;
306: always_ff @(posedge clk_i or negedge rst_ni) begin
307: if (!rst_ni) begin
308: tx_watermark_prev_q <= 1'd1;
309: end else begin
310: tx_watermark_prev_q <= tx_watermark_d;
311: end
312: end
313:
314: always_comb begin
315: unique case(uart_fifo_rxilvl)
316: 3'h0: event_rx_watermark = (rx_fifo_depth >= 6'd1);
317: 3'h1: event_rx_watermark = (rx_fifo_depth >= 6'd4);
318: 3'h2: event_rx_watermark = (rx_fifo_depth >= 6'd8);
319: 3'h3: event_rx_watermark = (rx_fifo_depth >= 6'd16);
320: 3'h4: event_rx_watermark = (rx_fifo_depth >= 6'd30);
321: default: event_rx_watermark = 1'b0;
322: endcase
323: end
324:
325:
326: // rx timeout interrupt
327: assign uart_rxto_en = reg2hw.timeout_ctrl.en.q;
328: assign uart_rxto_val = reg2hw.timeout_ctrl.val.q;
329:
330: assign rx_fifo_depth_changed = (rx_fifo_depth != rx_fifo_depth_prev_q);
331:
332: assign rx_timeout_count_d =
333: // don't count if timeout feature not enabled ;
334: // will never reach timeout val + lower power
335: (uart_rxto_en == 1'b0) ? 24'd0 :
336: // reset count if timeout interrupt is set
337: event_rx_timeout ? 24'd0 :
338: // reset count upon change in fifo level: covers both read and receiving a new byte
339: rx_fifo_depth_changed ? 24'd0 :
340: // reset count if no bytes are pending
341: (rx_fifo_depth == 5'd0) ? 24'd0 :
342: // stop the count at timeout value (this will set the interrupt)
343: // Removed below line as when the timeout reaches the value,
344: // event occured, and timeout value reset to 0h.
345: //(rx_timeout_count_q == uart_rxto_val) ? rx_timeout_count_q :
346: // increment if at rx baud tick
347: rx_tick_baud ? (rx_timeout_count_q + 24'd1) :
348: rx_timeout_count_q;
349:
350: assign event_rx_timeout = (rx_timeout_count_q == uart_rxto_val) & uart_rxto_en;
351:
352: assign tx_empty_d = tx_fifo_depth == 6'h0;
353: always_ff @(posedge clk_i or negedge rst_ni) begin
354: if (!rst_ni) begin
355: rx_timeout_count_q <= 24'd0;
356: rx_fifo_depth_prev_q <= 6'd0;
357: tx_empty_prev_q <= 1'd0;
358: end else begin
359: rx_timeout_count_q <= rx_timeout_count_d;
360: rx_fifo_depth_prev_q <= rx_fifo_depth;
361: tx_empty_prev_q <= tx_empty_d;
362: end
363: end
364:
365: assign event_rx_overflow = rx_fifo_wvalid & ~rx_fifo_wready;
366: assign event_rx_break_err = break_err & (break_st_q == BRK_CHK);
367: // this event is really a duplicate of uart_fifo_txilvl=0
368: // however, it is helpful to have both a non-1 entry watermark and an empty indication
369: assign event_tx_empty = tx_empty_d & ~tx_empty_prev_q;
370:
371:
372: // instantiate interrupt hardware primitives
373:
374: prim_intr_hw #(.Width(1)) intr_hw_tx_watermark (
375: .event_intr_i (event_tx_watermark),
376: .reg2hw_intr_enable_q_i (reg2hw.intr_enable.tx_watermark.q),
377: .reg2hw_intr_test_q_i (reg2hw.intr_test.tx_watermark.q),
378: .reg2hw_intr_test_qe_i (reg2hw.intr_test.tx_watermark.qe),
379: .reg2hw_intr_state_q_i (reg2hw.intr_state.tx_watermark.q),
380: .hw2reg_intr_state_de_o (hw2reg.intr_state.tx_watermark.de),
381: .hw2reg_intr_state_d_o (hw2reg.intr_state.tx_watermark.d),
382: .intr_o (intr_tx_watermark_o)
383: );
384:
385: prim_intr_hw #(.Width(1)) intr_hw_rx_watermark (
386: .event_intr_i (event_rx_watermark),
387: .reg2hw_intr_enable_q_i (reg2hw.intr_enable.rx_watermark.q),
388: .reg2hw_intr_test_q_i (reg2hw.intr_test.rx_watermark.q),
389: .reg2hw_intr_test_qe_i (reg2hw.intr_test.rx_watermark.qe),
390: .reg2hw_intr_state_q_i (reg2hw.intr_state.rx_watermark.q),
391: .hw2reg_intr_state_de_o (hw2reg.intr_state.rx_watermark.de),
392: .hw2reg_intr_state_d_o (hw2reg.intr_state.rx_watermark.d),
393: .intr_o (intr_rx_watermark_o)
394: );
395:
396: prim_intr_hw #(.Width(1)) intr_hw_tx_empty (
397: .event_intr_i (event_tx_empty),
398: .reg2hw_intr_enable_q_i (reg2hw.intr_enable.tx_empty.q),
399: .reg2hw_intr_test_q_i (reg2hw.intr_test.tx_empty.q),
400: .reg2hw_intr_test_qe_i (reg2hw.intr_test.tx_empty.qe),
401: .reg2hw_intr_state_q_i (reg2hw.intr_state.tx_empty.q),
402: .hw2reg_intr_state_de_o (hw2reg.intr_state.tx_empty.de),
403: .hw2reg_intr_state_d_o (hw2reg.intr_state.tx_empty.d),
404: .intr_o (intr_tx_empty_o)
405: );
406:
407: prim_intr_hw #(.Width(1)) intr_hw_rx_overflow (
408: .event_intr_i (event_rx_overflow),
409: .reg2hw_intr_enable_q_i (reg2hw.intr_enable.rx_overflow.q),
410: .reg2hw_intr_test_q_i (reg2hw.intr_test.rx_overflow.q),
411: .reg2hw_intr_test_qe_i (reg2hw.intr_test.rx_overflow.qe),
412: .reg2hw_intr_state_q_i (reg2hw.intr_state.rx_overflow.q),
413: .hw2reg_intr_state_de_o (hw2reg.intr_state.rx_overflow.de),
414: .hw2reg_intr_state_d_o (hw2reg.intr_state.rx_overflow.d),
415: .intr_o (intr_rx_overflow_o)
416: );
417:
418: prim_intr_hw #(.Width(1)) intr_hw_rx_frame_err (
419: .event_intr_i (event_rx_frame_err),
420: .reg2hw_intr_enable_q_i (reg2hw.intr_enable.rx_frame_err.q),
421: .reg2hw_intr_test_q_i (reg2hw.intr_test.rx_frame_err.q),
422: .reg2hw_intr_test_qe_i (reg2hw.intr_test.rx_frame_err.qe),
423: .reg2hw_intr_state_q_i (reg2hw.intr_state.rx_frame_err.q),
424: .hw2reg_intr_state_de_o (hw2reg.intr_state.rx_frame_err.de),
425: .hw2reg_intr_state_d_o (hw2reg.intr_state.rx_frame_err.d),
426: .intr_o (intr_rx_frame_err_o)
427: );
428:
429: prim_intr_hw #(.Width(1)) intr_hw_rx_break_err (
430: .event_intr_i (event_rx_break_err),
431: .reg2hw_intr_enable_q_i (reg2hw.intr_enable.rx_break_err.q),
432: .reg2hw_intr_test_q_i (reg2hw.intr_test.rx_break_err.q),
433: .reg2hw_intr_test_qe_i (reg2hw.intr_test.rx_break_err.qe),
434: .reg2hw_intr_state_q_i (reg2hw.intr_state.rx_break_err.q),
435: .hw2reg_intr_state_de_o (hw2reg.intr_state.rx_break_err.de),
436: .hw2reg_intr_state_d_o (hw2reg.intr_state.rx_break_err.d),
437: .intr_o (intr_rx_break_err_o)
438: );
439:
440: prim_intr_hw #(.Width(1)) intr_hw_rx_timeout (
441: .event_intr_i (event_rx_timeout),
442: .reg2hw_intr_enable_q_i (reg2hw.intr_enable.rx_timeout.q),
443: .reg2hw_intr_test_q_i (reg2hw.intr_test.rx_timeout.q),
444: .reg2hw_intr_test_qe_i (reg2hw.intr_test.rx_timeout.qe),
445: .reg2hw_intr_state_q_i (reg2hw.intr_state.rx_timeout.q),
446: .hw2reg_intr_state_de_o (hw2reg.intr_state.rx_timeout.de),
447: .hw2reg_intr_state_d_o (hw2reg.intr_state.rx_timeout.d),
448: .intr_o (intr_rx_timeout_o)
449: );
450:
451: prim_intr_hw #(.Width(1)) intr_hw_rx_parity_err (
452: .event_intr_i (event_rx_parity_err),
453: .reg2hw_intr_enable_q_i (reg2hw.intr_enable.rx_parity_err.q),
454: .reg2hw_intr_test_q_i (reg2hw.intr_test.rx_parity_err.q),
455: .reg2hw_intr_test_qe_i (reg2hw.intr_test.rx_parity_err.qe),
456: .reg2hw_intr_state_q_i (reg2hw.intr_state.rx_parity_err.q),
457: .hw2reg_intr_state_de_o (hw2reg.intr_state.rx_parity_err.de),
458: .hw2reg_intr_state_d_o (hw2reg.intr_state.rx_parity_err.d),
459: .intr_o (intr_rx_parity_err_o)
460: );
461:
462: endmodule
463: