| // Copyright lowRISC contributors. | |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. | |
| // SPDX-License-Identifier: Apache-2.0 | |
| module uartdpi #( | |
| parameter BAUD = 'x, | |
| parameter FREQ = 'x, | |
| parameter string NAME = "uart0" | |
| )( | |
| input logic clk_i, | |
| input logic rst_ni, | |
| output logic tx_o, | |
| input logic rx_i | |
| ); | |
| // Path to a log file. Used if none is specified through the `UARTDPI_LOG_<name>` plusarg. | |
| localparam string DEFAULT_LOG_FILE = {NAME, ".log"}; | |
| // Min cycles is 2 for fast test mode | |
| localparam int CYCLES_PER_SYMBOL = FREQ / BAUD; | |
| import "DPI-C" function | |
| chandle uartdpi_create(input string name, input string log_file_path); | |
| import "DPI-C" function | |
| void uartdpi_close(input chandle ctx); | |
| import "DPI-C" function | |
| byte uartdpi_read(input chandle ctx); | |
| import "DPI-C" function | |
| int uartdpi_can_read(input chandle ctx); | |
| import "DPI-C" function | |
| void uartdpi_write(input chandle ctx, int data); | |
| chandle ctx; | |
| string log_file_path = DEFAULT_LOG_FILE; | |
| initial begin | |
| $value$plusargs({"UARTDPI_LOG_", NAME, "=%s"}, log_file_path); | |
| ctx = uartdpi_create(NAME, log_file_path); | |
| end | |
| final begin | |
| uartdpi_close(ctx); | |
| ctx = null; | |
| end | |
| // TX | |
| reg txactive; | |
| int txcount; | |
| int txcyccount; | |
| reg [9:0] txsymbol; | |
| reg seen_reset; | |
| always_ff @(negedge clk_i or negedge rst_ni) begin | |
| if (!rst_ni) begin | |
| tx_o <= 1; | |
| txactive <= 0; | |
| end else begin | |
| if (!txactive) begin | |
| tx_o <= 1; | |
| if (uartdpi_can_read(ctx)) begin | |
| automatic int c = uartdpi_read(ctx); | |
| txsymbol <= {1'b1, c[7:0], 1'b0}; | |
| txactive <= 1; | |
| txcount <= 0; | |
| txcyccount <= 0; | |
| end | |
| end else begin | |
| txcyccount <= txcyccount + 1; | |
| tx_o <= txsymbol[txcount]; | |
| if (txcyccount == CYCLES_PER_SYMBOL - 1) begin | |
| txcyccount <= 0; | |
| if (txcount == 9) | |
| txactive <= 0; | |
| else | |
| txcount <= txcount + 1; | |
| end | |
| end | |
| end | |
| end | |
| initial begin | |
| // Prevent falling edges of rx_i before reset causing spurious characters | |
| seen_reset = 0; | |
| end | |
| // RX | |
| reg rxactive; | |
| int rxcount; | |
| int rxcyccount; | |
| reg [7:0] rxsymbol; | |
| always_ff @(negedge clk_i or negedge rst_ni) begin | |
| rxcyccount <= rxcyccount + 1; | |
| if (!rst_ni) begin | |
| rxactive <= 0; | |
| seen_reset <= 1; | |
| end else begin | |
| if (!rxactive) begin | |
| if (!rx_i && seen_reset) begin | |
| rxactive <= 1; | |
| rxcount <= 0; | |
| rxcyccount <= 0; | |
| end | |
| end else begin | |
| if (rxcount == 0) begin | |
| if (rxcyccount == CYCLES_PER_SYMBOL/2 - 1) begin | |
| if (rx_i) begin | |
| rxactive <= 0; | |
| end else begin | |
| rxcount <= rxcount + 1; | |
| rxcyccount <= 0; | |
| end | |
| end | |
| end else if (rxcount <= 8) begin | |
| if (rxcyccount == CYCLES_PER_SYMBOL - 1) begin | |
| rxsymbol[rxcount-1] <= rx_i; | |
| rxcount <= rxcount + 1; | |
| rxcyccount <= 0; | |
| end | |
| end else begin | |
| if (rxcyccount == CYCLES_PER_SYMBOL - 1) begin | |
| rxactive <= 0; | |
| if (rx_i) begin | |
| uartdpi_write(ctx, rxsymbol); | |
| end | |
| end | |
| end | |
| end | |
| end | |
| end | |
| endmodule |