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: