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