pulp-platform / pulpino

An open-source microcontroller system based on RISC-V
http://www.pulp-platform.org
Other
878 stars 296 forks source link

UART issue with clock divider in sv code #330

Open caifuxi opened 4 years ago

caifuxi commented 4 years ago

I am working on compiling Pulpino in vcs and run the simple "Helloworld" test, the UART is receving garbage. After long time of debugging, I found out the reason is actually coming from the System Verilog code of apb_uart_sv.

There are two UART modules in the /ips directory: apb_uart and apb_uart_sv. The default compilation seems choose VHDL code, and works fine, but the in the System Verilog code, the UART is sending data 16x faster than the baud rate can read, which causing the reading become garbage.

As the pulpino/sw/libs/sys_lib/src/uart.c says,

/**
 * Setup UART. The UART defaults to 8 bit character mode with 1 stop bit.
 *
 * parity       Enable/disable parity mode
 * clk_counter  Clock counter value that is used to derive the UART clock.
 *              It has to be in the range of 1..2^16.
 *              There is a prescaler in place that already divides the SoC
 *              clock by 16.  Since this is a counter, a value of 1 means that
 *              the SoC clock divided by 16*2 = 32 is used. A value of 31 would mean
 *              that we use the SoC clock divided by 16*32 = 512.
 */

There is a prescaler in place that already divides the SoC clock by 16.

which doesn't seem to work in the System Verilog code. In the sv code, the uart_tx is sending at the 1/2 s_clk frequency, which is at 12.5MHz.

image

module uart_tx (
        input  logic            clk_i,
        input  logic            rstn_i,
        output logic            tx_o,
        output logic            busy_o,
        input  logic            cfg_en_i,
        input  logic [15:0]     cfg_div_i,
        input  logic            cfg_parity_en_i,
        input  logic [1:0]      cfg_bits_i,
        input  logic            cfg_stop_bits_i,
        input  logic [7:0]      tx_data_i,
        input  logic            tx_valid_i,
        output logic            tx_ready_o
        );

This is only one clk_i passing in, which is the SoC clock, without dividing.

In the VHDL code, there is a slib_clock_divider, which generate the TXCLK 8x slower (2x baudrate), combined with iTx2 in the logic, the SoC clock is divided by 16. Thus the UART baudrate is matched.

entity uart_transmitter is
    port (
        CLK         : in std_logic;                             -- Clock
        RST         : in std_logic;                             -- Reset
        TXCLK       : in std_logic;                             -- Transmitter clock (2x baudrate)
        TXSTART     : in std_logic;                             -- Start transmitter
        CLEAR       : in std_logic;                             -- Clear transmitter state
        WLS         : in std_logic_vector(1 downto 0);          -- Word length select
        STB         : in std_logic;                             -- Number of stop bits
        PEN         : in std_logic;                             -- Parity enable
        EPS         : in std_logic;                             -- Even parity select
        SP          : in std_logic;                             -- Stick parity
        BC          : in std_logic;                             -- Break control
        DIN         : in std_logic_vector(7 downto 0);          -- Input data
        TXFINISHED  : out std_logic;                            -- Transmitter operation finished
        SOUT        : out std_logic                             -- Transmitter output
    );

component slib_clock_div is
    generic (
        RATIO       : integer := 8                              -- Clock divider ratio
    );
    port (
        CLK         : in std_logic;                             -- Clock
        RST         : in std_logic;                             -- Reset
        CE          : in std_logic;                             -- Clock enable input
        Q           : out std_logic                             -- New clock enable output
    );
    end component;

Did I missed something, or it's just a bug in the UART sv code?

Thanks